summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:24:08 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:24:08 +0000
commitf449f278dd3c70e479a035f50a9bb817a9b433ba (patch)
tree8ca2bfb785dda9bb4d573acdf9b42aea9cd51383
parentInitial commit. (diff)
downloadknot-upstream.tar.xz
knot-upstream.zip
Adding upstream version 3.2.6.upstream/3.2.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--COPYING674
-rw-r--r--Doxyfile.in2426
-rw-r--r--Makefile.am99
-rw-r--r--Makefile.in986
-rw-r--r--NEWS2873
-rw-r--r--README.md123
-rw-r--r--aclocal.m41480
-rwxr-xr-xar-lib271
-rwxr-xr-xcompile348
-rwxr-xr-xconfig.guess1480
-rwxr-xr-xconfig.sub1801
-rwxr-xr-xconfigure22159
-rw-r--r--configure.ac871
-rwxr-xr-xdepcomp791
-rw-r--r--distro/Makefile.am5
-rw-r--r--distro/Makefile.in531
-rw-r--r--distro/common/cz.nic.knotd.conf9
-rw-r--r--distro/common/knot.service30
-rw-r--r--distro/config/apkg.toml26
-rw-r--r--distro/pkg/arch/PKGBUILD66
-rw-r--r--distro/pkg/arch/knot.sysusers1
-rw-r--r--distro/pkg/arch/knot.tmpfiles.arch2
-rw-r--r--distro/pkg/deb/changelog6
-rw-r--r--distro/pkg/deb/clean2
-rw-r--r--distro/pkg/deb/compat1
-rw-r--r--distro/pkg/deb/control267
-rw-r--r--distro/pkg/deb/copyright208
-rw-r--r--distro/pkg/deb/cz.nic.knotd.conf9
-rw-r--r--distro/pkg/deb/docs1
-rw-r--r--distro/pkg/deb/knot-dnssecutils.install3
-rw-r--r--distro/pkg/deb/knot-dnssecutils.manpages3
-rw-r--r--distro/pkg/deb/knot-dnsutils.install3
-rw-r--r--distro/pkg/deb/knot-dnsutils.manpages3
-rw-r--r--distro/pkg/deb/knot-doc.install1
-rw-r--r--distro/pkg/deb/knot-doc.links5
-rw-r--r--distro/pkg/deb/knot-host.install1
-rw-r--r--distro/pkg/deb/knot-host.manpages1
-rw-r--r--distro/pkg/deb/knot-module-dnstap.install1
-rw-r--r--distro/pkg/deb/knot-module-geoip.install1
-rw-r--r--distro/pkg/deb/knot.dirs1
-rw-r--r--distro/pkg/deb/knot.init149
-rw-r--r--distro/pkg/deb/knot.install8
-rw-r--r--distro/pkg/deb/knot.manpages6
-rw-r--r--distro/pkg/deb/knot.postinst16
-rw-r--r--distro/pkg/deb/knot.postrm21
-rw-r--r--distro/pkg/deb/knot.service30
-rw-r--r--distro/pkg/deb/libdnssec9.install1
-rw-r--r--distro/pkg/deb/libdnssec9.symbols96
-rw-r--r--distro/pkg/deb/libknot-dev.install3
-rw-r--r--distro/pkg/deb/libknot13.install1
-rw-r--r--distro/pkg/deb/libknot13.symbols265
-rw-r--r--distro/pkg/deb/libzscanner4.install1
-rw-r--r--distro/pkg/deb/libzscanner4.symbols12
-rw-r--r--distro/pkg/deb/not-installed1
-rwxr-xr-xdistro/pkg/deb/prepare-environment38
-rwxr-xr-xdistro/pkg/deb/rules101
-rw-r--r--distro/pkg/deb/source/format1
-rwxr-xr-xdistro/pkg/deb/tests/authoritative-server150
-rw-r--r--distro/pkg/deb/tests/control13
-rwxr-xr-xdistro/pkg/deb/tests/kdig14
-rw-r--r--distro/pkg/deb/ufw/knot4
-rw-r--r--distro/pkg/deb/watch4
-rw-r--r--distro/pkg/el-7/01-disable-unconnected-net-test.patch26
-rw-r--r--distro/pkg/el-7/02-revert-AC_PROG_CC.patch18
-rw-r--r--distro/pkg/el-7/knot.service25
-rw-r--r--distro/pkg/el-7/knot.spec333
-rw-r--r--distro/pkg/el-7/knot.tmpfiles3
-rw-r--r--distro/pkg/nix/default.nix69
-rw-r--r--distro/pkg/nix/dont-create-run-time-dirs.patch32
-rw-r--r--distro/pkg/nix/runtime-deps.patch14
-rw-r--r--distro/pkg/nix/top-level.nix8
-rw-r--r--distro/pkg/rpm/01-test_net-disable-udp-send-on-unconnected.patch26
-rw-r--r--distro/pkg/rpm/knot.spec328
-rw-r--r--distro/tests/README.md16
-rwxr-xr-xdistro/tests/authoritative-server150
-rw-r--r--distro/tests/extra/all/control10
-rwxr-xr-xdistro/tests/kdig14
-rw-r--r--doc/Makefile.am226
-rw-r--r--doc/Makefile.in918
-rw-r--r--doc/appendices.rst126
-rw-r--r--doc/conf.py272
-rw-r--r--doc/configuration.rst905
-rw-r--r--doc/index.rst19
-rw-r--r--doc/installation.rst92
-rw-r--r--doc/introduction.rst87
-rw-r--r--doc/logo.pdfbin0 -> 3067 bytes
-rw-r--r--doc/logo.svg35
-rw-r--r--doc/man/kcatalogprint.8in80
-rw-r--r--doc/man/kdig.1in460
-rw-r--r--doc/man/keymgr.8in364
-rw-r--r--doc/man/khost.1in156
-rw-r--r--doc/man/kjournalprint.8in120
-rw-r--r--doc/man/knot.conf.5in2499
-rw-r--r--doc/man/knotc.8in425
-rw-r--r--doc/man/knotd.8in90
-rw-r--r--doc/man/knsec3hash.1in93
-rw-r--r--doc/man/knsupdate.1in211
-rw-r--r--doc/man/kxdpgun.8in241
-rw-r--r--doc/man/kzonecheck.1in88
-rw-r--r--doc/man/kzonesign.1in95
-rw-r--r--doc/man_kcatalogprint.rst54
-rw-r--r--doc/man_kdig.rst392
-rw-r--r--doc/man_keymgr.rst292
-rw-r--r--doc/man_khost.rst108
-rw-r--r--doc/man_kjournalprint.rst88
-rw-r--r--doc/man_knotc.rst377
-rw-r--r--doc/man_knotd.rst66
-rw-r--r--doc/man_knsec3hash.rst57
-rw-r--r--doc/man_knsupdate.rst181
-rw-r--r--doc/man_kxdpgun.rst183
-rw-r--r--doc/man_kzonecheck.rst62
-rw-r--r--doc/man_kzonesign.rst69
-rw-r--r--doc/migration.rst380
-rw-r--r--doc/modules.rst.in8
-rw-r--r--doc/operation.rst1200
-rw-r--r--doc/reference.rst2726
-rw-r--r--doc/requirements.rst115
-rw-r--r--doc/theme_html/static/admon_caution_48.pngbin0 -> 1553 bytes
-rw-r--r--doc/theme_html/static/admon_important_48.pngbin0 -> 1985 bytes
-rw-r--r--doc/theme_html/static/admon_note_48.pngbin0 -> 2404 bytes
-rw-r--r--doc/theme_html/static/admon_tip_48.pngbin0 -> 2176 bytes
-rw-r--r--doc/theme_html/static/admon_warning_48.pngbin0 -> 1096 bytes
-rw-r--r--doc/theme_html/static/admons.css69
-rw-r--r--doc/theme_html/static/main.css6
-rw-r--r--doc/theme_html/theme.conf4
-rw-r--r--doc/troubleshooting.rst110
-rw-r--r--doc/utilities.rst24
-rwxr-xr-xinstall-sh541
-rwxr-xr-xltmain.sh11251
-rw-r--r--m4/ax_check_compile_flag.m472
-rw-r--r--m4/ax_check_link_flag.m471
-rw-r--r--m4/code-coverage.m445
-rw-r--r--m4/knot-lib-version.m416
-rw-r--r--m4/knot-module.m436
-rw-r--r--m4/knot-version.m423
-rw-r--r--m4/libtool.m48394
-rw-r--r--m4/ltoptions.m4437
-rw-r--r--m4/ltsugar.m4124
-rw-r--r--m4/ltversion.m423
-rw-r--r--m4/lt~obsolete.m499
-rw-r--r--m4/sanitizer.m476
-rw-r--r--m4/visibility.m477
-rwxr-xr-xmissing215
-rw-r--r--python/Makefile.am16
-rw-r--r--python/Makefile.in544
-rw-r--r--python/README.md147
-rw-r--r--python/libknot/__init__.py.in95
-rw-r--r--python/libknot/control.py374
-rw-r--r--python/libknot/dname.py70
-rw-r--r--python/libknot/probe.py278
-rw-r--r--python/setup.py.in30
-rw-r--r--samples/Makefile.am34
-rw-r--r--samples/Makefile.in558
-rw-r--r--samples/example.com.zone22
-rw-r--r--samples/knot.sample.conf.in36
-rw-r--r--src/Makefile.am23
-rw-r--r--src/Makefile.in8325
-rw-r--r--src/config.h.in277
-rw-r--r--src/contrib/Makefile.inc269
-rw-r--r--src/contrib/asan.h37
-rw-r--r--src/contrib/base32hex.c353
-rw-r--r--src/contrib/base32hex.h104
-rw-r--r--src/contrib/base64.c272
-rw-r--r--src/contrib/base64.h103
-rw-r--r--src/contrib/base64url.c287
-rw-r--r--src/contrib/base64url.h103
-rw-r--r--src/contrib/color.h31
-rw-r--r--src/contrib/conn_pool.c243
-rw-r--r--src/contrib/conn_pool.h100
-rw-r--r--src/contrib/ctype.h193
-rw-r--r--src/contrib/dnstap/convert.c142
-rw-r--r--src/contrib/dnstap/convert.h60
-rw-r--r--src/contrib/dnstap/dnstap.c41
-rw-r--r--src/contrib/dnstap/dnstap.h47
-rw-r--r--src/contrib/dnstap/dnstap.proto270
-rw-r--r--src/contrib/dnstap/message.c130
-rw-r--r--src/contrib/dnstap/message.h63
-rw-r--r--src/contrib/dnstap/reader.c103
-rw-r--r--src/contrib/dnstap/reader.h73
-rw-r--r--src/contrib/dnstap/writer.c120
-rw-r--r--src/contrib/dnstap/writer.h71
-rw-r--r--src/contrib/files.c254
-rw-r--r--src/contrib/files.h77
-rw-r--r--src/contrib/getline.c60
-rw-r--r--src/contrib/getline.h39
-rw-r--r--src/contrib/json.c237
-rw-r--r--src/contrib/json.h87
-rw-r--r--src/contrib/libbpf/LICENSE1
-rw-r--r--src/contrib/libbpf/bpf/bpf.c710
-rw-r--r--src/contrib/libbpf/bpf/bpf.h184
-rw-r--r--src/contrib/libbpf/bpf/bpf_core_read.h263
-rw-r--r--src/contrib/libbpf/bpf/bpf_endian.h72
-rw-r--r--src/contrib/libbpf/bpf/bpf_helper_defs.h2759
-rw-r--r--src/contrib/libbpf/bpf/bpf_helpers.h47
-rw-r--r--src/contrib/libbpf/bpf/bpf_prog_linfo.c246
-rw-r--r--src/contrib/libbpf/bpf/bpf_tracing.h195
-rw-r--r--src/contrib/libbpf/bpf/btf.c2884
-rw-r--r--src/contrib/libbpf/bpf/btf.h311
-rw-r--r--src/contrib/libbpf/bpf/btf_dump.c1386
-rw-r--r--src/contrib/libbpf/bpf/hashmap.c229
-rw-r--r--src/contrib/libbpf/bpf/hashmap.h178
-rw-r--r--src/contrib/libbpf/bpf/libbpf.c6581
-rw-r--r--src/contrib/libbpf/bpf/libbpf.h637
-rw-r--r--src/contrib/libbpf/bpf/libbpf_errno.c63
-rw-r--r--src/contrib/libbpf/bpf/libbpf_internal.h217
-rw-r--r--src/contrib/libbpf/bpf/libbpf_probes.c323
-rw-r--r--src/contrib/libbpf/bpf/libbpf_util.h47
-rw-r--r--src/contrib/libbpf/bpf/netlink.c451
-rw-r--r--src/contrib/libbpf/bpf/nlattr.c195
-rw-r--r--src/contrib/libbpf/bpf/nlattr.h106
-rw-r--r--src/contrib/libbpf/bpf/str_error.c18
-rw-r--r--src/contrib/libbpf/bpf/str_error.h6
-rw-r--r--src/contrib/libbpf/bpf/xsk.c797
-rw-r--r--src/contrib/libbpf/bpf/xsk.h246
-rw-r--r--src/contrib/libbpf/include/asm/barrier.h7
-rw-r--r--src/contrib/libbpf/include/linux/compiler.h70
-rw-r--r--src/contrib/libbpf/include/linux/err.h38
-rw-r--r--src/contrib/libbpf/include/linux/filter.h118
-rw-r--r--src/contrib/libbpf/include/linux/kernel.h44
-rw-r--r--src/contrib/libbpf/include/linux/list.h82
-rw-r--r--src/contrib/libbpf/include/linux/overflow.h90
-rw-r--r--src/contrib/libbpf/include/linux/ring_buffer.h18
-rw-r--r--src/contrib/libbpf/include/linux/types.h31
-rw-r--r--src/contrib/libbpf/include/uapi/linux/bpf.h3692
-rw-r--r--src/contrib/libbpf/include/uapi/linux/bpf_common.h57
-rw-r--r--src/contrib/libbpf/include/uapi/linux/btf.h165
-rw-r--r--src/contrib/libbpf/include/uapi/linux/if_link.h1033
-rw-r--r--src/contrib/libbpf/include/uapi/linux/if_xdp.h108
-rw-r--r--src/contrib/libbpf/include/uapi/linux/netlink.h252
-rw-r--r--src/contrib/libngtcp2/LICENSE1
-rw-r--r--src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c644
-rw-r--r--src/contrib/libngtcp2/ngtcp2/crypto/shared.c1418
-rw-r--r--src/contrib/libngtcp2/ngtcp2/crypto/shared.h350
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c335
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h221
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c117
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h69
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c90
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h91
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c692
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h156
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c1489
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h149
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c56
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h108
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c615
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h421
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c147
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h175
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c13698
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h1115
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c291
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h208
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c895
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h148
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c154
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h34
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c167
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h98
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c79
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h89
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c819
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h345
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c822
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h123
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h58
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c336
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h136
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c113
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h72
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h136
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c40
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h140
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c46
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h65
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c77
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h49
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c2527
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h1235
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c160
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h123
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c230
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h153
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c164
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h126
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c172
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h198
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c1218
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h161
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c61
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h80
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h40
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c120
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h132
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c319
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h197
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c137
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h85
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c1676
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h467
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c233
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h94
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c698
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h310
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c71
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h46
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c243
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h120
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c39
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c99
-rw-r--r--src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h65
-rw-r--r--src/contrib/libngtcp2/ngtcp2/ngtcp2.h5906
-rw-r--r--src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h893
-rw-r--r--src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h107
-rw-r--r--src/contrib/libngtcp2/ngtcp2/version.h51
-rw-r--r--src/contrib/licenses/0BSD12
-rw-r--r--src/contrib/licenses/BSD-3-Clause21
-rw-r--r--src/contrib/licenses/LGPL-2.0339
-rw-r--r--src/contrib/licenses/LGPL-2.1503
-rw-r--r--src/contrib/licenses/MIT19
-rw-r--r--src/contrib/macros.h41
-rw-r--r--src/contrib/mempattern.c122
-rw-r--r--src/contrib/mempattern.h47
-rw-r--r--src/contrib/musl/inet_ntop.c79
-rw-r--r--src/contrib/musl/inet_ntop.h28
-rw-r--r--src/contrib/net.c747
-rw-r--r--src/contrib/net.h209
-rw-r--r--src/contrib/openbsd/LICENSE2
-rw-r--r--src/contrib/openbsd/siphash.c176
-rw-r--r--src/contrib/openbsd/siphash.h83
-rw-r--r--src/contrib/openbsd/strlcat.c48
-rw-r--r--src/contrib/openbsd/strlcat.h31
-rw-r--r--src/contrib/openbsd/strlcpy.c46
-rw-r--r--src/contrib/openbsd/strlcpy.h29
-rw-r--r--src/contrib/os.h37
-rw-r--r--src/contrib/proxyv2/proxyv2.c281
-rw-r--r--src/contrib/proxyv2/proxyv2.h29
-rw-r--r--src/contrib/qp-trie/trie.c1451
-rw-r--r--src/contrib/qp-trie/trie.h280
-rw-r--r--src/contrib/semaphore.c80
-rw-r--r--src/contrib/semaphore.h41
-rw-r--r--src/contrib/sockaddr.c368
-rw-r--r--src/contrib/sockaddr.h160
-rw-r--r--src/contrib/spinlock.h133
-rw-r--r--src/contrib/string.c247
-rw-r--r--src/contrib/string.h104
-rw-r--r--src/contrib/strtonum.h125
-rw-r--r--src/contrib/time.c441
-rw-r--r--src/contrib/time.h211
-rw-r--r--src/contrib/toeplitz.h122
-rw-r--r--src/contrib/tolower.h52
-rw-r--r--src/contrib/trim.h36
-rw-r--r--src/contrib/ucw/LICENSE1
-rw-r--r--src/contrib/ucw/array-sort.h195
-rw-r--r--src/contrib/ucw/binsearch.h50
-rw-r--r--src/contrib/ucw/heap.c153
-rw-r--r--src/contrib/ucw/heap.h46
-rw-r--r--src/contrib/ucw/lists.c264
-rw-r--r--src/contrib/ucw/lists.h74
-rw-r--r--src/contrib/ucw/mempool.c323
-rw-r--r--src/contrib/ucw/mempool.h124
-rw-r--r--src/contrib/url-parser/LICENSE1
-rw-r--r--src/contrib/url-parser/README.md14
-rw-r--r--src/contrib/url-parser/url_parser.c635
-rw-r--r--src/contrib/url-parser/url_parser.h64
-rw-r--r--src/contrib/vpool/vpool.c243
-rw-r--r--src/contrib/vpool/vpool.h60
-rw-r--r--src/contrib/wire_ctx.h355
-rw-r--r--src/knot/Makefile.inc240
-rw-r--r--src/knot/catalog/catalog_db.c347
-rw-r--r--src/knot/catalog/catalog_db.h187
-rw-r--r--src/knot/catalog/catalog_update.c407
-rw-r--r--src/knot/catalog/catalog_update.h171
-rw-r--r--src/knot/catalog/generate.c346
-rw-r--r--src/knot/catalog/generate.h56
-rw-r--r--src/knot/catalog/interpret.c257
-rw-r--r--src/knot/catalog/interpret.h52
-rw-r--r--src/knot/common/evsched.c268
-rw-r--r--src/knot/common/evsched.h154
-rw-r--r--src/knot/common/fdset.c336
-rw-r--r--src/knot/common/fdset.h382
-rw-r--r--src/knot/common/log.c491
-rw-r--r--src/knot/common/log.h187
-rw-r--r--src/knot/common/process.c194
-rw-r--r--src/knot/common/process.h60
-rw-r--r--src/knot/common/stats.c309
-rw-r--r--src/knot/common/stats.h53
-rw-r--r--src/knot/common/systemd.c168
-rw-r--r--src/knot/common/systemd.h105
-rw-r--r--src/knot/common/unreachable.c148
-rw-r--r--src/knot/common/unreachable.h87
-rw-r--r--src/knot/conf/base.c1056
-rw-r--r--src/knot/conf/base.h322
-rw-r--r--src/knot/conf/conf.c1469
-rw-r--r--src/knot/conf/conf.h939
-rw-r--r--src/knot/conf/confdb.c951
-rw-r--r--src/knot/conf/confdb.h230
-rw-r--r--src/knot/conf/confio.c1612
-rw-r--r--src/knot/conf/confio.h231
-rw-r--r--src/knot/conf/migration.c81
-rw-r--r--src/knot/conf/migration.h30
-rw-r--r--src/knot/conf/module.c509
-rw-r--r--src/knot/conf/module.h126
-rw-r--r--src/knot/conf/schema.c530
-rw-r--r--src/knot/conf/schema.h279
-rw-r--r--src/knot/conf/tools.c1069
-rw-r--r--src/knot/conf/tools.h147
-rw-r--r--src/knot/ctl/commands.c2331
-rw-r--r--src/knot/ctl/commands.h160
-rw-r--r--src/knot/ctl/process.c128
-rw-r--r--src/knot/ctl/process.h30
-rw-r--r--src/knot/dnssec/context.c351
-rw-r--r--src/knot/dnssec/context.h82
-rw-r--r--src/knot/dnssec/ds_query.c288
-rw-r--r--src/knot/dnssec/ds_query.h22
-rw-r--r--src/knot/dnssec/kasp/kasp_db.c610
-rw-r--r--src/knot/dnssec/kasp/kasp_db.h296
-rw-r--r--src/knot/dnssec/kasp/kasp_zone.c447
-rw-r--r--src/knot/dnssec/kasp/kasp_zone.h63
-rw-r--r--src/knot/dnssec/kasp/keystate.c74
-rw-r--r--src/knot/dnssec/kasp/keystate.h35
-rw-r--r--src/knot/dnssec/kasp/keystore.c89
-rw-r--r--src/knot/dnssec/kasp/keystore.h22
-rw-r--r--src/knot/dnssec/kasp/policy.h135
-rw-r--r--src/knot/dnssec/key-events.c863
-rw-r--r--src/knot/dnssec/key-events.h69
-rw-r--r--src/knot/dnssec/key_records.c300
-rw-r--r--src/knot/dnssec/key_records.h54
-rw-r--r--src/knot/dnssec/nsec-chain.c797
-rw-r--r--src/knot/dnssec/nsec-chain.h174
-rw-r--r--src/knot/dnssec/nsec3-chain.c733
-rw-r--r--src/knot/dnssec/nsec3-chain.h69
-rw-r--r--src/knot/dnssec/policy.c51
-rw-r--r--src/knot/dnssec/policy.h26
-rw-r--r--src/knot/dnssec/rrset-sign.c425
-rw-r--r--src/knot/dnssec/rrset-sign.h123
-rw-r--r--src/knot/dnssec/zone-events.c470
-rw-r--r--src/knot/dnssec/zone-events.h134
-rw-r--r--src/knot/dnssec/zone-keys.c767
-rw-r--r--src/knot/dnssec/zone-keys.h213
-rw-r--r--src/knot/dnssec/zone-nsec.c429
-rw-r--r--src/knot/dnssec/zone-nsec.h163
-rw-r--r--src/knot/dnssec/zone-sign.c1081
-rw-r--r--src/knot/dnssec/zone-sign.h162
-rw-r--r--src/knot/events/events.c564
-rw-r--r--src/knot/events/events.h214
-rw-r--r--src/knot/events/handlers.h49
-rw-r--r--src/knot/events/handlers/backup.c71
-rw-r--r--src/knot/events/handlers/dnssec.c116
-rw-r--r--src/knot/events/handlers/ds_check.c49
-rw-r--r--src/knot/events/handlers/ds_push.c277
-rw-r--r--src/knot/events/handlers/expire.c46
-rw-r--r--src/knot/events/handlers/flush.c33
-rw-r--r--src/knot/events/handlers/freeze_thaw.c46
-rw-r--r--src/knot/events/handlers/load.c406
-rw-r--r--src/knot/events/handlers/notify.c212
-rw-r--r--src/knot/events/handlers/refresh.c1391
-rw-r--r--src/knot/events/handlers/update.c433
-rw-r--r--src/knot/events/replan.c210
-rw-r--r--src/knot/events/replan.h35
-rw-r--r--src/knot/include/module.h602
-rw-r--r--src/knot/journal/journal_basic.c92
-rw-r--r--src/knot/journal/journal_basic.h118
-rw-r--r--src/knot/journal/journal_metadata.c422
-rw-r--r--src/knot/journal/journal_metadata.h187
-rw-r--r--src/knot/journal/journal_read.c436
-rw-r--r--src/knot/journal/journal_read.h158
-rw-r--r--src/knot/journal/journal_write.c333
-rw-r--r--src/knot/journal/journal_write.h121
-rw-r--r--src/knot/journal/knot_lmdb.c770
-rw-r--r--src/knot/journal/knot_lmdb.h446
-rw-r--r--src/knot/journal/serialization.c501
-rw-r--r--src/knot/journal/serialization.h169
-rw-r--r--src/knot/modules/cookies/Makefile.inc13
-rw-r--r--src/knot/modules/cookies/cookies.c308
-rw-r--r--src/knot/modules/cookies/cookies.rst110
-rw-r--r--src/knot/modules/dnsproxy/Makefile.inc13
-rw-r--r--src/knot/modules/dnsproxy/dnsproxy.c191
-rw-r--r--src/knot/modules/dnsproxy/dnsproxy.rst125
-rw-r--r--src/knot/modules/dnstap/Makefile.inc15
-rw-r--r--src/knot/modules/dnstap/dnstap.c338
-rw-r--r--src/knot/modules/dnstap/dnstap.rst113
-rw-r--r--src/knot/modules/geoip/Makefile.inc17
-rw-r--r--src/knot/modules/geoip/geodb.c216
-rw-r--r--src/knot/modules/geoip/geodb.h67
-rw-r--r--src/knot/modules/geoip/geoip.c1061
-rw-r--r--src/knot/modules/geoip/geoip.rst324
-rw-r--r--src/knot/modules/noudp/Makefile.inc12
-rw-r--r--src/knot/modules/noudp/noudp.c110
-rw-r--r--src/knot/modules/noudp/noudp.rst68
-rw-r--r--src/knot/modules/onlinesign/Makefile.inc15
-rw-r--r--src/knot/modules/onlinesign/nsec_next.c113
-rw-r--r--src/knot/modules/onlinesign/nsec_next.h29
-rw-r--r--src/knot/modules/onlinesign/onlinesign.c736
-rw-r--r--src/knot/modules/onlinesign/onlinesign.rst158
-rw-r--r--src/knot/modules/probe/Makefile.inc12
-rw-r--r--src/knot/modules/probe/probe.c190
-rw-r--r--src/knot/modules/probe/probe.rst89
-rw-r--r--src/knot/modules/queryacl/Makefile.inc12
-rw-r--r--src/knot/modules/queryacl/queryacl.c93
-rw-r--r--src/knot/modules/queryacl/queryacl.rst70
-rw-r--r--src/knot/modules/rrl/Makefile.inc15
-rw-r--r--src/knot/modules/rrl/functions.c554
-rw-r--r--src/knot/modules/rrl/functions.h111
-rw-r--r--src/knot/modules/rrl/rrl.c208
-rw-r--r--src/knot/modules/rrl/rrl.rst133
-rw-r--r--src/knot/modules/static_modules.h.in25
-rw-r--r--src/knot/modules/stats/Makefile.inc13
-rw-r--r--src/knot/modules/stats/stats.c676
-rw-r--r--src/knot/modules/stats/stats.rst274
-rw-r--r--src/knot/modules/synthrecord/Makefile.inc13
-rw-r--r--src/knot/modules/synthrecord/synthrecord.c625
-rw-r--r--src/knot/modules/synthrecord/synthrecord.rst170
-rw-r--r--src/knot/modules/whoami/Makefile.inc12
-rw-r--r--src/knot/modules/whoami/whoami.c114
-rw-r--r--src/knot/modules/whoami/whoami.rst97
-rw-r--r--src/knot/nameserver/axfr.c225
-rw-r--r--src/knot/nameserver/axfr.h27
-rw-r--r--src/knot/nameserver/chaos.c145
-rw-r--r--src/knot/nameserver/chaos.h24
-rw-r--r--src/knot/nameserver/internet.c728
-rw-r--r--src/knot/nameserver/internet.h79
-rw-r--r--src/knot/nameserver/ixfr.c332
-rw-r--r--src/knot/nameserver/ixfr.h63
-rw-r--r--src/knot/nameserver/log.h88
-rw-r--r--src/knot/nameserver/notify.c92
-rw-r--r--src/knot/nameserver/notify.h28
-rw-r--r--src/knot/nameserver/nsec_proofs.c677
-rw-r--r--src/knot/nameserver/nsec_proofs.h38
-rw-r--r--src/knot/nameserver/process_query.c978
-rw-r--r--src/knot/nameserver/process_query.h107
-rw-r--r--src/knot/nameserver/query_module.c791
-rw-r--r--src/knot/nameserver/query_module.h99
-rw-r--r--src/knot/nameserver/tsig_ctx.c189
-rw-r--r--src/knot/nameserver/tsig_ctx.h97
-rw-r--r--src/knot/nameserver/update.c107
-rw-r--r--src/knot/nameserver/update.h27
-rw-r--r--src/knot/nameserver/xfr.c96
-rw-r--r--src/knot/nameserver/xfr.h69
-rw-r--r--src/knot/query/capture.c63
-rw-r--r--src/knot/query/capture.h32
-rw-r--r--src/knot/query/layer.h136
-rw-r--r--src/knot/query/query.c85
-rw-r--r--src/knot/query/query.h66
-rw-r--r--src/knot/query/requestor.c378
-rw-r--r--src/knot/query/requestor.h119
-rw-r--r--src/knot/server/dthreads.c767
-rw-r--r--src/knot/server/dthreads.h295
-rw-r--r--src/knot/server/proxyv2.c69
-rw-r--r--src/knot/server/proxyv2.h23
-rw-r--r--src/knot/server/server.c1335
-rw-r--r--src/knot/server/server.h203
-rw-r--r--src/knot/server/tcp-handler.c380
-rw-r--r--src/knot/server/tcp-handler.h43
-rw-r--r--src/knot/server/udp-handler.c575
-rw-r--r--src/knot/server/udp-handler.h43
-rw-r--r--src/knot/server/xdp-handler.c506
-rw-r--r--src/knot/server/xdp-handler.h67
-rw-r--r--src/knot/updates/acl.c361
-rw-r--r--src/knot/updates/acl.h83
-rw-r--r--src/knot/updates/apply.c379
-rw-r--r--src/knot/updates/apply.h101
-rw-r--r--src/knot/updates/changesets.c628
-rw-r--r--src/knot/updates/changesets.h290
-rw-r--r--src/knot/updates/ddns.c701
-rw-r--r--src/knot/updates/ddns.h47
-rw-r--r--src/knot/updates/zone-update.c1098
-rw-r--r--src/knot/updates/zone-update.h299
-rw-r--r--src/knot/worker/pool.c254
-rw-r--r--src/knot/worker/pool.h93
-rw-r--r--src/knot/worker/queue.c67
-rw-r--r--src/knot/worker/queue.h65
-rw-r--r--src/knot/zone/adds_tree.c262
-rw-r--r--src/knot/zone/adds_tree.h120
-rw-r--r--src/knot/zone/adjust.c628
-rw-r--r--src/knot/zone/adjust.h123
-rw-r--r--src/knot/zone/backup.c461
-rw-r--r--src/knot/zone/backup.h74
-rw-r--r--src/knot/zone/backup_dir.c247
-rw-r--r--src/knot/zone/backup_dir.h39
-rw-r--r--src/knot/zone/contents.c609
-rw-r--r--src/knot/zone/contents.h291
-rw-r--r--src/knot/zone/digest.c305
-rw-r--r--src/knot/zone/digest.h72
-rw-r--r--src/knot/zone/measure.c133
-rw-r--r--src/knot/zone/measure.h71
-rw-r--r--src/knot/zone/node.c464
-rw-r--r--src/knot/zone/node.h419
-rw-r--r--src/knot/zone/semantic-check.c562
-rw-r--r--src/knot/zone/semantic-check.h116
-rw-r--r--src/knot/zone/serial.c78
-rw-r--r--src/knot/zone/serial.h76
-rw-r--r--src/knot/zone/timers.c228
-rw-r--r--src/knot/zone/timers.h99
-rw-r--r--src/knot/zone/zone-diff.c402
-rw-r--r--src/knot/zone/zone-diff.h31
-rw-r--r--src/knot/zone/zone-dump.c236
-rw-r--r--src/knot/zone/zone-dump.h32
-rw-r--r--src/knot/zone/zone-load.c173
-rw-r--r--src/knot/zone/zone-load.h68
-rw-r--r--src/knot/zone/zone-tree.c512
-rw-r--r--src/knot/zone/zone-tree.h337
-rw-r--r--src/knot/zone/zone.c792
-rw-r--r--src/knot/zone/zone.h290
-rw-r--r--src/knot/zone/zonedb-load.c643
-rw-r--r--src/knot/zone/zonedb-load.h40
-rw-r--r--src/knot/zone/zonedb.c188
-rw-r--r--src/knot/zone/zonedb.h135
-rw-r--r--src/knot/zone/zonefile.c371
-rw-r--r--src/knot/zone/zonefile.h104
-rw-r--r--src/knotd.pc.in9
-rw-r--r--src/libdnssec.pc.in13
-rw-r--r--src/libdnssec/Makefile.inc70
-rw-r--r--src/libdnssec/binary.c163
-rw-r--r--src/libdnssec/binary.h116
-rw-r--r--src/libdnssec/crypto.c42
-rw-r--r--src/libdnssec/crypto.h52
-rw-r--r--src/libdnssec/digest.c105
-rw-r--r--src/libdnssec/digest.h76
-rw-r--r--src/libdnssec/dnssec.h36
-rw-r--r--src/libdnssec/error.c87
-rw-r--r--src/libdnssec/error.h106
-rw-r--r--src/libdnssec/key.h309
-rw-r--r--src/libdnssec/key/algorithm.c180
-rw-r--r--src/libdnssec/key/algorithm.h30
-rw-r--r--src/libdnssec/key/convert.c375
-rw-r--r--src/libdnssec/key/convert.h44
-rw-r--r--src/libdnssec/key/dnskey.c91
-rw-r--r--src/libdnssec/key/dnskey.h46
-rw-r--r--src/libdnssec/key/ds.c128
-rw-r--r--src/libdnssec/key/internal.h35
-rw-r--r--src/libdnssec/key/key.c439
-rw-r--r--src/libdnssec/key/keytag.c88
-rw-r--r--src/libdnssec/key/privkey.c140
-rw-r--r--src/libdnssec/key/privkey.h35
-rw-r--r--src/libdnssec/key/simple.c55
-rw-r--r--src/libdnssec/keyid.c87
-rw-r--r--src/libdnssec/keyid.h64
-rw-r--r--src/libdnssec/keystore.h155
-rw-r--r--src/libdnssec/keystore/internal.h50
-rw-r--r--src/libdnssec/keystore/keystore.c186
-rw-r--r--src/libdnssec/keystore/pkcs11.c397
-rw-r--r--src/libdnssec/keystore/pkcs8.c497
-rw-r--r--src/libdnssec/keytag.h44
-rw-r--r--src/libdnssec/nsec.h155
-rw-r--r--src/libdnssec/nsec/bitmap.c142
-rw-r--r--src/libdnssec/nsec/hash.c125
-rw-r--r--src/libdnssec/nsec/nsec.c130
-rw-r--r--src/libdnssec/p11/p11.c113
-rw-r--r--src/libdnssec/p11/p11.h41
-rw-r--r--src/libdnssec/pem.c182
-rw-r--r--src/libdnssec/pem.h73
-rw-r--r--src/libdnssec/random.c53
-rw-r--r--src/libdnssec/random.h79
-rw-r--r--src/libdnssec/shared/bignum.c64
-rw-r--r--src/libdnssec/shared/bignum.h41
-rw-r--r--src/libdnssec/shared/binary_wire.h53
-rw-r--r--src/libdnssec/shared/dname.c165
-rw-r--r--src/libdnssec/shared/dname.h57
-rw-r--r--src/libdnssec/shared/keyid_gnutls.c99
-rw-r--r--src/libdnssec/shared/keyid_gnutls.h30
-rw-r--r--src/libdnssec/shared/shared.h121
-rw-r--r--src/libdnssec/sign.h114
-rw-r--r--src/libdnssec/sign/der.c229
-rw-r--r--src/libdnssec/sign/der.h56
-rw-r--r--src/libdnssec/sign/sign.c433
-rw-r--r--src/libdnssec/tsig.c242
-rw-r--r--src/libdnssec/tsig.h155
-rw-r--r--src/libdnssec/version.h25
-rw-r--r--src/libdnssec/version.h.in25
-rw-r--r--src/libknot.pc.in14
-rwxr-xr-xsrc/libknot/Makefile.inc136
-rw-r--r--src/libknot/attribute.h51
-rw-r--r--src/libknot/codes.c129
-rw-r--r--src/libknot/codes.h60
-rw-r--r--src/libknot/consts.h163
-rw-r--r--src/libknot/control/control.c568
-rw-r--r--src/libknot/control/control.h161
-rw-r--r--src/libknot/cookies.c170
-rw-r--r--src/libknot/cookies.h103
-rw-r--r--src/libknot/db/db.h92
-rw-r--r--src/libknot/db/db_lmdb.c578
-rw-r--r--src/libknot/db/db_lmdb.h73
-rw-r--r--src/libknot/db/db_trie.c176
-rw-r--r--src/libknot/db/db_trie.h40
-rw-r--r--src/libknot/descriptor.c425
-rw-r--r--src/libknot/descriptor.h307
-rw-r--r--src/libknot/dname.c801
-rw-r--r--src/libknot/dname.h354
-rw-r--r--src/libknot/dynarray.h188
-rw-r--r--src/libknot/endian.h51
-rw-r--r--src/libknot/errcode.h272
-rw-r--r--src/libknot/error.c239
-rw-r--r--src/libknot/error.h50
-rw-r--r--src/libknot/libknot.h72
-rw-r--r--src/libknot/lookup.h89
-rw-r--r--src/libknot/mm_ctx.h41
-rw-r--r--src/libknot/packet/compr.h101
-rw-r--r--src/libknot/packet/pkt.c837
-rw-r--r--src/libknot/packet/pkt.h416
-rw-r--r--src/libknot/packet/rrset-wire.c727
-rw-r--r--src/libknot/packet/rrset-wire.h69
-rw-r--r--src/libknot/packet/wire.h1045
-rw-r--r--src/libknot/probe/data.c135
-rw-r--r--src/libknot/probe/data.h132
-rw-r--r--src/libknot/probe/probe.c228
-rw-r--r--src/libknot/probe/probe.h116
-rw-r--r--src/libknot/rdata.h101
-rw-r--r--src/libknot/rdataset.c371
-rw-r--r--src/libknot/rdataset.h218
-rw-r--r--src/libknot/rrset-dump.c2292
-rw-r--r--src/libknot/rrset-dump.h118
-rw-r--r--src/libknot/rrset.c217
-rw-r--r--src/libknot/rrset.h194
-rw-r--r--src/libknot/rrtype/dnskey.h72
-rw-r--r--src/libknot/rrtype/ds.h64
-rw-r--r--src/libknot/rrtype/naptr.c47
-rw-r--r--src/libknot/rrtype/naptr.h41
-rw-r--r--src/libknot/rrtype/nsec.h50
-rw-r--r--src/libknot/rrtype/nsec3.h98
-rw-r--r--src/libknot/rrtype/nsec3param.h64
-rw-r--r--src/libknot/rrtype/opt.c687
-rw-r--r--src/libknot/rrtype/opt.h587
-rw-r--r--src/libknot/rrtype/rdname.h98
-rw-r--r--src/libknot/rrtype/rrsig.h100
-rw-r--r--src/libknot/rrtype/soa.h94
-rw-r--r--src/libknot/rrtype/svcb.h44
-rw-r--r--src/libknot/rrtype/tsig.c409
-rw-r--r--src/libknot/rrtype/tsig.h122
-rw-r--r--src/libknot/rrtype/zonemd.h71
-rw-r--r--src/libknot/tsig-op.c683
-rw-r--r--src/libknot/tsig-op.h187
-rw-r--r--src/libknot/tsig.c188
-rw-r--r--src/libknot/tsig.h89
-rw-r--r--src/libknot/version.h25
-rw-r--r--src/libknot/version.h.in25
-rw-r--r--src/libknot/wire.h154
-rw-r--r--src/libknot/xdp.h35
-rw-r--r--src/libknot/xdp/Makefile.am20
-rw-r--r--src/libknot/xdp/Makefile.in545
-rw-r--r--src/libknot/xdp/bpf-consts.h57
-rw-r--r--src/libknot/xdp/bpf-kernel-obj.c941
-rw-r--r--src/libknot/xdp/bpf-kernel-obj.h2
-rw-r--r--src/libknot/xdp/bpf-kernel.c293
-rw-r--r--src/libknot/xdp/bpf-user.c313
-rw-r--r--src/libknot/xdp/bpf-user.h139
-rw-r--r--src/libknot/xdp/eth.c312
-rw-r--r--src/libknot/xdp/eth.h111
-rw-r--r--src/libknot/xdp/msg.h62
-rw-r--r--src/libknot/xdp/msg_init.h74
-rw-r--r--src/libknot/xdp/protocols.h446
-rw-r--r--src/libknot/xdp/quic.c1028
-rw-r--r--src/libknot/xdp/quic.h134
-rw-r--r--src/libknot/xdp/quic_conn.c506
-rw-r--r--src/libknot/xdp/quic_conn.h314
-rw-r--r--src/libknot/xdp/tcp.c729
-rw-r--r--src/libknot/xdp/tcp.h227
-rw-r--r--src/libknot/xdp/tcp_iobuf.c266
-rw-r--r--src/libknot/xdp/tcp_iobuf.h120
-rw-r--r--src/libknot/xdp/xdp.c568
-rw-r--r--src/libknot/xdp/xdp.h189
-rw-r--r--src/libknot/yparser/yparser.c176
-rw-r--r--src/libknot/yparser/yparser.h149
-rw-r--r--src/libknot/yparser/ypbody.c460
-rw-r--r--src/libknot/yparser/ypformat.c121
-rw-r--r--src/libknot/yparser/ypformat.h101
-rw-r--r--src/libknot/yparser/ypschema.c598
-rw-r--r--src/libknot/yparser/ypschema.h352
-rw-r--r--src/libknot/yparser/yptrafo.c1099
-rw-r--r--src/libknot/yparser/yptrafo.h311
-rw-r--r--src/libzscanner.pc.in13
-rw-r--r--src/libzscanner/Makefile.inc40
-rw-r--r--src/libzscanner/error.c202
-rw-r--r--src/libzscanner/error.h119
-rw-r--r--src/libzscanner/functions.c980
-rw-r--r--src/libzscanner/functions.h141
-rw-r--r--src/libzscanner/scanner.c.g2102263
-rw-r--r--src/libzscanner/scanner.c.t09692
-rw-r--r--src/libzscanner/scanner.h405
-rw-r--r--src/libzscanner/scanner.rl564
-rw-r--r--src/libzscanner/scanner_body.rl2326
-rw-r--r--src/libzscanner/version.h25
-rw-r--r--src/libzscanner/version.h.in25
-rw-r--r--src/utils/Makefile.inc187
-rw-r--r--src/utils/common/cert.c61
-rw-r--r--src/utils/common/cert.h36
-rw-r--r--src/utils/common/exec.c982
-rw-r--r--src/utils/common/exec.h137
-rw-r--r--src/utils/common/hex.c82
-rw-r--r--src/utils/common/hex.h31
-rw-r--r--src/utils/common/https.c525
-rw-r--r--src/utils/common/https.h150
-rw-r--r--src/utils/common/lookup.c295
-rw-r--r--src/utils/common/lookup.h124
-rw-r--r--src/utils/common/msg.c40
-rw-r--r--src/utils/common/msg.h42
-rw-r--r--src/utils/common/netio.c896
-rw-r--r--src/utils/common/netio.h239
-rw-r--r--src/utils/common/params.c343
-rw-r--r--src/utils/common/params.h168
-rw-r--r--src/utils/common/quic.c887
-rw-r--r--src/utils/common/quic.h125
-rw-r--r--src/utils/common/resolv.c211
-rw-r--r--src/utils/common/resolv.h24
-rw-r--r--src/utils/common/sign.c109
-rw-r--r--src/utils/common/sign.h63
-rw-r--r--src/utils/common/tls.c739
-rw-r--r--src/utils/common/tls.h81
-rw-r--r--src/utils/common/token.c115
-rw-r--r--src/utils/common/token.h65
-rw-r--r--src/utils/common/util_conf.c139
-rw-r--r--src/utils/common/util_conf.h86
-rw-r--r--src/utils/kcatalogprint/main.c178
-rw-r--r--src/utils/kdig/kdig_exec.c1309
-rw-r--r--src/utils/kdig/kdig_exec.h21
-rw-r--r--src/utils/kdig/kdig_main.c45
-rw-r--r--src/utils/kdig/kdig_params.c2740
-rw-r--r--src/utils/kdig/kdig_params.h187
-rw-r--r--src/utils/keymgr/bind_privkey.c411
-rw-r--r--src/utils/keymgr/bind_privkey.h72
-rw-r--r--src/utils/keymgr/functions.c1135
-rw-r--r--src/utils/keymgr/functions.h62
-rw-r--r--src/utils/keymgr/main.c405
-rw-r--r--src/utils/keymgr/offline_ksk.c549
-rw-r--r--src/utils/keymgr/offline_ksk.h35
-rw-r--r--src/utils/khost/khost_main.c45
-rw-r--r--src/utils/khost/khost_params.c365
-rw-r--r--src/utils/khost/khost_params.h22
-rw-r--r--src/utils/kjournalprint/main.c466
-rw-r--r--src/utils/knotc/commands.c1340
-rw-r--r--src/utils/knotc/commands.h74
-rw-r--r--src/utils/knotc/interactive.c450
-rw-r--r--src/utils/knotc/interactive.h26
-rw-r--r--src/utils/knotc/main.c172
-rw-r--r--src/utils/knotc/process.c291
-rw-r--r--src/utils/knotc/process.h78
-rw-r--r--src/utils/knotd/main.c635
-rw-r--r--src/utils/knsec3hash/knsec3hash.c187
-rw-r--r--src/utils/knsupdate/knsupdate_exec.c1053
-rw-r--r--src/utils/knsupdate/knsupdate_exec.h25
-rw-r--r--src/utils/knsupdate/knsupdate_interactive.c177
-rw-r--r--src/utils/knsupdate/knsupdate_interactive.h26
-rw-r--r--src/utils/knsupdate/knsupdate_main.c46
-rw-r--r--src/utils/knsupdate/knsupdate_params.c304
-rw-r--r--src/utils/knsupdate/knsupdate_params.h72
-rw-r--r--src/utils/kxdpgun/ip_route.c358
-rw-r--r--src/utils/kxdpgun/ip_route.h46
-rw-r--r--src/utils/kxdpgun/load_queries.c159
-rw-r--r--src/utils/kxdpgun/load_queries.h32
-rw-r--r--src/utils/kxdpgun/main.c1302
-rw-r--r--src/utils/kzonecheck/main.c181
-rw-r--r--src/utils/kzonecheck/zone_check.c92
-rw-r--r--src/utils/kzonecheck/zone_check.h23
-rw-r--r--src/utils/kzonesign/main.c276
-rwxr-xr-xtest-driver150
-rw-r--r--tests-fuzz/Makefile.am130
-rw-r--r--tests-fuzz/Makefile.in1501
-rw-r--r--tests-fuzz/fuzz_dname_from_str.c39
-rw-r--r--tests-fuzz/fuzz_dname_to_str.c31
-rw-r--r--tests-fuzz/fuzz_packet.c33
-rw-r--r--tests-fuzz/fuzz_zscanner.c31
-rw-r--r--tests-fuzz/knotd_wrap/afl-loop.h21
-rw-r--r--tests-fuzz/knotd_wrap/server.c29
-rw-r--r--tests-fuzz/knotd_wrap/tcp-handler.c25
-rw-r--r--tests-fuzz/knotd_wrap/udp-handler.c116
-rw-r--r--tests-fuzz/main.c140
-rw-r--r--tests/Makefile.am257
-rw-r--r--tests/Makefile.in2813
-rw-r--r--tests/contrib/test_base32hex.c267
-rw-r--r--tests/contrib/test_base64.c237
-rw-r--r--tests/contrib/test_base64url.c252
-rw-r--r--tests/contrib/test_heap.c166
-rw-r--r--tests/contrib/test_inet_ntop.c85
-rw-r--r--tests/contrib/test_net.c718
-rw-r--r--tests/contrib/test_net_shortwrite.c151
-rw-r--r--tests/contrib/test_qp-cow.c282
-rw-r--r--tests/contrib/test_qp-trie.c284
-rw-r--r--tests/contrib/test_siphash.c135
-rw-r--r--tests/contrib/test_sockaddr.c229
-rw-r--r--tests/contrib/test_spinlock.c78
-rw-r--r--tests/contrib/test_string.c59
-rw-r--r--tests/contrib/test_strtonum.c156
-rw-r--r--tests/contrib/test_time.c203
-rw-r--r--tests/contrib/test_toeplitz.c93
-rw-r--r--tests/contrib/test_wire_ctx.c287
-rw-r--r--tests/knot/semantic_check_data/cdnskey.cds123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.both113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.delete.invalid.cds113
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.invalid.param123
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocdnskey101
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nocds110
-rw-r--r--tests/knot/semantic_check_data/cdnskey.nodnskey111
-rw-r--r--tests/knot/semantic_check_data/cdnskey.orphan.cdnskey135
-rw-r--r--tests/knot/semantic_check_data/cdnskey.orphan.cds138
-rw-r--r--tests/knot/semantic_check_data/cname_extra_01.zone18
-rw-r--r--tests/knot/semantic_check_data/cname_extra_02.signed76
-rw-r--r--tests/knot/semantic_check_data/cname_multiple.zone15
-rw-r--r--tests/knot/semantic_check_data/delegation.signed43
-rw-r--r--tests/knot/semantic_check_data/different_signer_name.signed52
-rw-r--r--tests/knot/semantic_check_data/dname_apex_nsec3.signed25
-rw-r--r--tests/knot/semantic_check_data/dname_children.zone16
-rw-r--r--tests/knot/semantic_check_data/dname_extra_ns.zone16
-rw-r--r--tests/knot/semantic_check_data/dname_multiple.zone16
-rw-r--r--tests/knot/semantic_check_data/dnskey_param_error.signed70
-rw-r--r--tests/knot/semantic_check_data/duplicate.signature19
-rw-r--r--tests/knot/semantic_check_data/glue_apex_both.missing14
-rw-r--r--tests/knot/semantic_check_data/glue_apex_one.missing16
-rw-r--r--tests/knot/semantic_check_data/glue_besides.missing17
-rw-r--r--tests/knot/semantic_check_data/glue_deleg.missing17
-rw-r--r--tests/knot/semantic_check_data/glue_in_apex.missing13
-rw-r--r--tests/knot/semantic_check_data/glue_in_deleg.valid16
-rw-r--r--tests/knot/semantic_check_data/glue_no_foreign.valid13
-rw-r--r--tests/knot/semantic_check_data/glue_wildcard.valid22
-rw-r--r--tests/knot/semantic_check_data/invalid_ds.signed106
-rw-r--r--tests/knot/semantic_check_data/missing.signed20
-rw-r--r--tests/knot/semantic_check_data/no_error_delegation_bitmap.signed61
-rw-r--r--tests/knot/semantic_check_data/no_error_nsec3_optout.signed29
-rw-r--r--tests/knot/semantic_check_data/no_rrsig.signed48
-rw-r--r--tests/knot/semantic_check_data/no_rrsig_with_delegation.signed61
-rw-r--r--tests/knot/semantic_check_data/ns_apex.missing11
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_01.signed80
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_02.signed94
-rw-r--r--tests/knot/semantic_check_data/nsec3_chain_03.signed94
-rw-r--r--tests/knot/semantic_check_data/nsec3_ds.signed57
-rw-r--r--tests/knot/semantic_check_data/nsec3_missing.signed120
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout.signed81
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout_ent.all15
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout_ent.invalid18
-rw-r--r--tests/knot/semantic_check_data/nsec3_optout_ent.valid20
-rw-r--r--tests/knot/semantic_check_data/nsec3_param_invalid.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed70
-rw-r--r--tests/knot/semantic_check_data/nsec_broken_chain_01.signed72
-rw-r--r--tests/knot/semantic_check_data/nsec_broken_chain_02.signed65
-rw-r--r--tests/knot/semantic_check_data/nsec_missing.signed67
-rw-r--r--tests/knot/semantic_check_data/nsec_multiple.signed66
-rw-r--r--tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed73
-rw-r--r--tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed73
-rw-r--r--tests/knot/semantic_check_data/rrsig_rdata_ttl.signed52
-rw-r--r--tests/knot/semantic_check_data/rrsig_signed.signed62
-rw-r--r--tests/knot/semantic_check_data/rrsig_ttl.signed52
-rw-r--r--tests/knot/test_acl.c314
-rw-r--r--tests/knot/test_changeset.c166
-rw-r--r--tests/knot/test_conf.c274
-rw-r--r--tests/knot/test_conf.h50
-rw-r--r--tests/knot/test_conf_tools.c74
-rw-r--r--tests/knot/test_confdb.c489
-rw-r--r--tests/knot/test_confio.c1100
-rw-r--r--tests/knot/test_digest.c474
-rw-r--r--tests/knot/test_dthreads.c148
-rw-r--r--tests/knot/test_fdset.c150
-rw-r--r--tests/knot/test_journal.c880
-rw-r--r--tests/knot/test_kasp_db.c233
-rw-r--r--tests/knot/test_node.c128
-rw-r--r--tests/knot/test_process_query.c201
-rw-r--r--tests/knot/test_query_module.c87
-rw-r--r--tests/knot/test_requestor.c188
-rw-r--r--tests/knot/test_semantic_check.in169
-rw-r--r--tests/knot/test_server.c72
-rw-r--r--tests/knot/test_server.h100
-rw-r--r--tests/knot/test_unreachable.c59
-rw-r--r--tests/knot/test_worker_pool.c152
-rw-r--r--tests/knot/test_worker_queue.c57
-rw-r--r--tests/knot/test_zone-tree.c135
-rw-r--r--tests/knot/test_zone-update.c337
-rw-r--r--tests/knot/test_zone_events.c99
-rw-r--r--tests/knot/test_zone_serial.c159
-rw-r--r--tests/knot/test_zone_timers.c110
-rw-r--r--tests/knot/test_zonedb.c115
-rw-r--r--tests/libdnssec/sample_keys.h498
-rw-r--r--tests/libdnssec/test_binary.c93
-rw-r--r--tests/libdnssec/test_crypto.c37
-rw-r--r--tests/libdnssec/test_key.c215
-rw-r--r--tests/libdnssec/test_key_algorithm.c90
-rw-r--r--tests/libdnssec/test_key_ds.c122
-rw-r--r--tests/libdnssec/test_keyid.c82
-rw-r--r--tests/libdnssec/test_keystore_pkcs11.c421
-rw-r--r--tests/libdnssec/test_keystore_pkcs8.c98
-rw-r--r--tests/libdnssec/test_keytag.c61
-rw-r--r--tests/libdnssec/test_nsec_bitmap.c102
-rw-r--r--tests/libdnssec/test_nsec_hash.c114
-rw-r--r--tests/libdnssec/test_random.c82
-rw-r--r--tests/libdnssec/test_shared_bignum.c128
-rw-r--r--tests/libdnssec/test_shared_dname.c79
-rw-r--r--tests/libdnssec/test_sign.c203
-rw-r--r--tests/libdnssec/test_sign_der.c203
-rw-r--r--tests/libdnssec/test_tsig.c145
-rw-r--r--tests/libknot/test_control.c221
-rw-r--r--tests/libknot/test_cookies.c174
-rw-r--r--tests/libknot/test_db.c287
-rw-r--r--tests/libknot/test_descriptor.c361
-rw-r--r--tests/libknot/test_dname.c618
-rw-r--r--tests/libknot/test_dynarray.c137
-rw-r--r--tests/libknot/test_edns.c502
-rw-r--r--tests/libknot/test_edns_ecs.c271
-rw-r--r--tests/libknot/test_endian.c70
-rw-r--r--tests/libknot/test_lookup.c66
-rw-r--r--tests/libknot/test_pkt.c199
-rw-r--r--tests/libknot/test_probe.c96
-rw-r--r--tests/libknot/test_rdata.c60
-rw-r--r--tests/libknot/test_rdataset.c227
-rw-r--r--tests/libknot/test_rrset-wire.c264
-rw-r--r--tests/libknot/test_rrset.c121
-rw-r--r--tests/libknot/test_tsig.c204
-rw-r--r--tests/libknot/test_wire.c46
-rw-r--r--tests/libknot/test_xdp_tcp.c633
-rw-r--r--tests/libknot/test_yparser.c346
-rw-r--r--tests/libknot/test_ypschema.c417
-rw-r--r--tests/libknot/test_yptrafo.c404
-rw-r--r--tests/libzscanner/TESTS86
-rw-r--r--tests/libzscanner/data/00-0_general.in27
-rw-r--r--tests/libzscanner/data/00-0_general.out70
-rw-r--r--tests/libzscanner/data/00-1_general.in7
-rw-r--r--tests/libzscanner/data/00-1_general.out2
-rw-r--r--tests/libzscanner/data/00-2_general.in1
-rw-r--r--tests/libzscanner/data/00-2_general.out2
-rw-r--r--tests/libzscanner/data/00-3_general.in4
-rw-r--r--tests/libzscanner/data/00-3_general.out6
-rw-r--r--tests/libzscanner/data/00-4_general.in0
-rw-r--r--tests/libzscanner/data/00-4_general.out0
-rw-r--r--tests/libzscanner/data/01_owner.in37
-rw-r--r--tests/libzscanner/data/01_owner.out138
-rw-r--r--tests/libzscanner/data/02_class.in10
-rw-r--r--tests/libzscanner/data/02_class.out16
-rw-r--r--tests/libzscanner/data/03_rrttl.in26
-rw-r--r--tests/libzscanner/data/03_rrttl.out100
-rw-r--r--tests/libzscanner/data/04-0_ORIGIN.in29
-rw-r--r--tests/libzscanner/data/04-0_ORIGIN.out86
-rw-r--r--tests/libzscanner/data/04-1_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-1_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-2_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-2_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-3_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-3_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-4_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-4_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-5_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-5_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-6_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-6_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-7_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-7_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-8_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-8_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/04-9_ORIGIN.in4
-rw-r--r--tests/libzscanner/data/04-9_ORIGIN.out2
-rw-r--r--tests/libzscanner/data/05-0_TTL.in36
-rw-r--r--tests/libzscanner/data/05-0_TTL.out92
-rw-r--r--tests/libzscanner/data/05-1_TTL.in4
-rw-r--r--tests/libzscanner/data/05-1_TTL.out2
-rw-r--r--tests/libzscanner/data/05-2_TTL.in4
-rw-r--r--tests/libzscanner/data/05-2_TTL.out2
-rw-r--r--tests/libzscanner/data/05-3_TTL.in4
-rw-r--r--tests/libzscanner/data/05-3_TTL.out2
-rw-r--r--tests/libzscanner/data/05-4_TTL.in4
-rw-r--r--tests/libzscanner/data/05-4_TTL.out2
-rw-r--r--tests/libzscanner/data/06-0_INCLUDE.in29
-rw-r--r--tests/libzscanner/data/06-0_INCLUDE.out138
-rw-r--r--tests/libzscanner/data/06-1_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-1_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-2_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-2_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-3_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-3_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-4_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-4_INCLUDE.out2
-rw-r--r--tests/libzscanner/data/06-5_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-5_INCLUDE.out0
-rw-r--r--tests/libzscanner/data/06-6_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-6_INCLUDE.out4
-rw-r--r--tests/libzscanner/data/06-7_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-7_INCLUDE.out4
-rw-r--r--tests/libzscanner/data/06-8_INCLUDE.in5
-rw-r--r--tests/libzscanner/data/06-8_INCLUDE.out12
-rw-r--r--tests/libzscanner/data/07-0-rdata.in1
-rw-r--r--tests/libzscanner/data/07-0-rdata.out2
-rw-r--r--tests/libzscanner/data/07-1-rdata.in1
-rw-r--r--tests/libzscanner/data/07-1-rdata.out2
-rw-r--r--tests/libzscanner/data/07-2-rdata.in1
-rw-r--r--tests/libzscanner/data/07-2-rdata.out6
-rw-r--r--tests/libzscanner/data/07-3-rdata.in1
-rw-r--r--tests/libzscanner/data/07-3-rdata.out6
-rw-r--r--tests/libzscanner/data/07-4-rdata.in1
-rw-r--r--tests/libzscanner/data/07-4-rdata.out6
-rw-r--r--tests/libzscanner/data/10_A.in19
-rw-r--r--tests/libzscanner/data/10_A.out50
-rw-r--r--tests/libzscanner/data/11_AAAA.in21
-rw-r--r--tests/libzscanner/data/11_AAAA.out62
-rw-r--r--tests/libzscanner/data/12_TXT.in37
-rw-r--r--tests/libzscanner/data/12_TXT.out138
-rw-r--r--tests/libzscanner/data/13_SPF.in14
-rw-r--r--tests/libzscanner/data/13_SPF.out32
-rw-r--r--tests/libzscanner/data/14_NS.in38
-rw-r--r--tests/libzscanner/data/14_NS.out144
-rw-r--r--tests/libzscanner/data/15_CNAME.in14
-rw-r--r--tests/libzscanner/data/15_CNAME.out32
-rw-r--r--tests/libzscanner/data/16_PTR.in14
-rw-r--r--tests/libzscanner/data/16_PTR.out32
-rw-r--r--tests/libzscanner/data/17_DNAME.in14
-rw-r--r--tests/libzscanner/data/17_DNAME.out32
-rw-r--r--tests/libzscanner/data/18_MX.in23
-rw-r--r--tests/libzscanner/data/18_MX.out62
-rw-r--r--tests/libzscanner/data/19_AFSDB.in14
-rw-r--r--tests/libzscanner/data/19_AFSDB.out32
-rw-r--r--tests/libzscanner/data/20_RT.in14
-rw-r--r--tests/libzscanner/data/20_RT.out32
-rw-r--r--tests/libzscanner/data/21_KX.in14
-rw-r--r--tests/libzscanner/data/21_KX.out32
-rw-r--r--tests/libzscanner/data/22_HINFO.in26
-rw-r--r--tests/libzscanner/data/22_HINFO.out88
-rw-r--r--tests/libzscanner/data/23_MINFO.in18
-rw-r--r--tests/libzscanner/data/23_MINFO.out48
-rw-r--r--tests/libzscanner/data/24_RP.in14
-rw-r--r--tests/libzscanner/data/24_RP.out32
-rw-r--r--tests/libzscanner/data/25_SOA.in31
-rw-r--r--tests/libzscanner/data/25_SOA.out74
-rw-r--r--tests/libzscanner/data/26_SRV.in25
-rw-r--r--tests/libzscanner/data/26_SRV.out66
-rw-r--r--tests/libzscanner/data/27_NAPTR.in20
-rw-r--r--tests/libzscanner/data/27_NAPTR.out56
-rw-r--r--tests/libzscanner/data/28_TYPE.in27
-rw-r--r--tests/libzscanner/data/28_TYPE.out64
-rw-r--r--tests/libzscanner/data/29_CERT.in58
-rw-r--r--tests/libzscanner/data/29_CERT.out244
-rw-r--r--tests/libzscanner/data/30_KEY.in31
-rw-r--r--tests/libzscanner/data/30_KEY.out86
-rw-r--r--tests/libzscanner/data/31_DNSKEY.in32
-rw-r--r--tests/libzscanner/data/31_DNSKEY.out92
-rw-r--r--tests/libzscanner/data/32_APL.in30
-rw-r--r--tests/libzscanner/data/32_APL.out104
-rw-r--r--tests/libzscanner/data/33_DS.in23
-rw-r--r--tests/libzscanner/data/33_DS.out62
-rw-r--r--tests/libzscanner/data/34_SSHFP.in21
-rw-r--r--tests/libzscanner/data/34_SSHFP.out54
-rw-r--r--tests/libzscanner/data/35_IPSECKEY.in29
-rw-r--r--tests/libzscanner/data/35_IPSECKEY.out94
-rw-r--r--tests/libzscanner/data/36_RRSIG.in46
-rw-r--r--tests/libzscanner/data/36_RRSIG.out140
-rw-r--r--tests/libzscanner/data/37_NSEC.in20
-rw-r--r--tests/libzscanner/data/37_NSEC.out64
-rw-r--r--tests/libzscanner/data/38_DHCID.in26
-rw-r--r--tests/libzscanner/data/38_DHCID.out72
-rw-r--r--tests/libzscanner/data/39_NSEC3.in46
-rw-r--r--tests/libzscanner/data/39_NSEC3.out144
-rw-r--r--tests/libzscanner/data/40_NSEC3PARAM.in23
-rw-r--r--tests/libzscanner/data/40_NSEC3PARAM.out58
-rw-r--r--tests/libzscanner/data/41_TLSA.in21
-rw-r--r--tests/libzscanner/data/41_TLSA.out54
-rw-r--r--tests/libzscanner/data/42_LOC.in64
-rw-r--r--tests/libzscanner/data/42_LOC.out248
-rw-r--r--tests/libzscanner/data/43_EUI48.in22
-rw-r--r--tests/libzscanner/data/43_EUI48.out60
-rw-r--r--tests/libzscanner/data/44_EUI64.in22
-rw-r--r--tests/libzscanner/data/44_EUI64.out60
-rw-r--r--tests/libzscanner/data/45_NID.in14
-rw-r--r--tests/libzscanner/data/45_NID.out32
-rw-r--r--tests/libzscanner/data/46_L32.in21
-rw-r--r--tests/libzscanner/data/46_L32.out54
-rw-r--r--tests/libzscanner/data/47_L64.in23
-rw-r--r--tests/libzscanner/data/47_L64.out62
-rw-r--r--tests/libzscanner/data/48_LP.in14
-rw-r--r--tests/libzscanner/data/48_LP.out32
-rw-r--r--tests/libzscanner/data/49_CDS.in23
-rw-r--r--tests/libzscanner/data/49_CDS.out62
-rw-r--r--tests/libzscanner/data/50_CDNSKEY.in32
-rw-r--r--tests/libzscanner/data/50_CDNSKEY.out92
-rw-r--r--tests/libzscanner/data/51_URI.in22
-rw-r--r--tests/libzscanner/data/51_URI.out60
-rw-r--r--tests/libzscanner/data/52_CAA.in23
-rw-r--r--tests/libzscanner/data/52_CAA.out70
-rw-r--r--tests/libzscanner/data/53_SMIMEA.in13
-rw-r--r--tests/libzscanner/data/53_SMIMEA.out26
-rw-r--r--tests/libzscanner/data/54_OPENPGPKEY.in13
-rw-r--r--tests/libzscanner/data/54_OPENPGPKEY.out26
-rw-r--r--tests/libzscanner/data/55_CSYNC.in22
-rw-r--r--tests/libzscanner/data/55_CSYNC.out68
-rw-r--r--tests/libzscanner/data/56_ZONEMD.in21
-rw-r--r--tests/libzscanner/data/56_ZONEMD.out54
-rw-r--r--tests/libzscanner/data/57_SVCB.in102
-rw-r--r--tests/libzscanner/data/57_SVCB.out306
-rw-r--r--tests/libzscanner/data/58_HTTPS.in19
-rw-r--r--tests/libzscanner/data/58_HTTPS.out46
-rw-r--r--tests/libzscanner/data/includes/include19
-rw-r--r--tests/libzscanner/data/includes/include26
-rw-r--r--tests/libzscanner/data/includes/include30
-rw-r--r--tests/libzscanner/data/includes/include41
-rw-r--r--tests/libzscanner/data/includes/include51
-rw-r--r--tests/libzscanner/data/includes/include61
-rw-r--r--tests/libzscanner/processing.c184
-rw-r--r--tests/libzscanner/processing.h31
-rw-r--r--tests/libzscanner/test_zscanner.in41
-rw-r--r--tests/libzscanner/zscanner-tool.c257
-rw-r--r--tests/modules/test_onlinesign.c203
-rw-r--r--tests/modules/test_rrl.c178
-rw-r--r--tests/tap/basic.c605
-rw-r--r--tests/tap/basic.h132
-rw-r--r--tests/tap/files.c66
-rw-r--r--tests/tap/files.h44
-rw-r--r--tests/tap/float.c67
-rw-r--r--tests/tap/float.h39
-rw-r--r--tests/tap/libtap.sh246
-rw-r--r--tests/tap/macros.h85
-rw-r--r--tests/tap/runtests.c1404
-rw-r--r--tests/utils/test_cert.c223
-rw-r--r--tests/utils/test_lookup.c136
1207 files changed, 450162 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/Doxyfile.in b/Doxyfile.in
new file mode 100644
index 0000000..da386e3
--- /dev/null
+++ b/Doxyfile.in
@@ -0,0 +1,2426 @@
+# Doxyfile 1.8.17
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See https://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Knot DNS"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+# PROJECT_LOGO = doc/logo.svg
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF = "The $name class" \
+ "The $name widget" \
+ "The $name file" \
+ is \
+ provides \
+ specifies \
+ contains \
+ represents \
+ a \
+ an \
+ the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH = src/
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = YES
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = YES
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = NO
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = NO
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 50
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE = doc/doxygen/DoxygenLayout.xml
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE = doc/html/doxygen.warn
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = doc/doxygen/Doxy.page.h \
+ src/libdnssec \
+ src/libknot \
+ src/libzscanner \
+ src/knot/include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,
+# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.
+
+FILE_PATTERNS = *.h
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse-libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = api
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = YES
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME =
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..f6cab44
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,99 @@
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src tests tests-fuzz python samples distro doc
+
+EXTRA_DIST = README.md
+
+.PHONY: singlehtml epub install-singlehtml install-epub
+singlehtml install-singlehtml epub install-epub:
+ $(MAKE) -C doc $@
+
+.PHONY: check-compile
+check-compile:
+ $(MAKE) $(AM_MAKEFLAGS) -C tests $@
+ $(MAKE) $(AM_MAKEFLAGS) -C tests-fuzz $@
+
+AM_DISTCHECK_CONFIGURE_FLAGS =
+
+CODE_COVERAGE_INFO = coverage.info
+CODE_COVERAGE_HTML = coverage.html
+CODE_COVERAGE_DIRS = \
+ src/contrib \
+ src/knot \
+ src/libdnssec \
+ src/libknot \
+ src/libzscanner
+
+code_coverage_quiet = --quiet
+
+check-code-coverage:
+if CODE_COVERAGE_ENABLED
+ $(MAKE) $(AM_MAKEFLAGS) code-coverage-initial
+ -$(MAKE) $(AM_MAKEFLAGS) -k check
+ $(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
+ $(MAKE) $(AM_MAKEFLAGS) code-coverage-html
+ $(MAKE) $(AM_MAKEFLAGS) code-coverage-summary
+else
+ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+endif
+
+code-coverage-initial:
+if CODE_COVERAGE_ENABLED
+ $(LCOV) $(code_coverage_quiet) \
+ --no-external \
+ $(foreach dir, $(CODE_COVERAGE_DIRS), --directory $(top_builddir)/$(dir)) \
+ --capture --initial \
+ --ignore-errors source \
+ --no-checksum \
+ --compat-libtool \
+ --output-file $(CODE_COVERAGE_INFO)
+else
+ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+endif
+
+code-coverage-capture:
+if CODE_COVERAGE_ENABLED
+ $(LCOV) $(code_coverage_quiet) \
+ --no-external \
+ $(foreach dir, $(CODE_COVERAGE_DIRS), --directory $(builddir)/$(dir)) \
+ --capture \
+ --ignore-errors source \
+ --no-checksum \
+ --compat-libtool \
+ --output-file $(CODE_COVERAGE_INFO)
+else
+ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+endif
+
+code-coverage-html:
+if CODE_COVERAGE_ENABLED
+ @echo "Generating code coverage HTML report (this might take a while)"
+ LANG=C $(GENHTML) $(code_coverage_quiet) \
+ --output-directory $(CODE_COVERAGE_HTML) \
+ --title "Knot DNS $(PACKAGE_VERSION) Code Coverage" \
+ --legend --show-details \
+ --ignore-errors source \
+ $(CODE_COVERAGE_INFO)
+else
+ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+endif
+
+code-coverage-summary:
+if CODE_COVERAGE_ENABLED
+ $(LCOV) \
+ --summary $(CODE_COVERAGE_INFO)
+else
+ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+endif
+
+if CODE_COVERAGE_ENABLED
+clean-local: code-coverage-clean
+ -find . -name "*.gcno" -delete
+code-coverage-clean:
+ -$(LCOV) --directory $(top_builddir) -z
+ -rm -rf $(CODE_COVERAGE_INFO) $(CODE_COVERAGE_HTML)
+ -find . -name "*.gcda" -o -name "*.gcov" -delete
+endif
+
+
+.PHONY: check-code-coverage code-coverage-initial code-coverage-capture code-coverage-html code-coverage-summary code-coverage-clean
+.NOTPARALLEL: clean
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..5810db4
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,986 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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 = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+ $(am__configure_deps) $(am__DIST_COMMON)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES = src/libknot/version.h src/libdnssec/version.h \
+ src/libzscanner/version.h Doxyfile python/libknot/__init__.py \
+ src/knot/modules/static_modules.h
+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 =
+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
+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 \
+ cscope distdir distdir-am dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# 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
+CSCOPE = cscope
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Doxyfile.in $(srcdir)/Makefile.in \
+ $(top_srcdir)/python/libknot/__init__.py.in \
+ $(top_srcdir)/src/knot/modules/static_modules.h.in \
+ $(top_srcdir)/src/libdnssec/version.h.in \
+ $(top_srcdir)/src/libknot/version.h.in \
+ $(top_srcdir)/src/libzscanner/version.h.in COPYING NEWS ar-lib \
+ compile config.guess config.sub install-sh ltmain.sh missing
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ if test -d "$(distdir)"; then \
+ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -rf "$(distdir)" \
+ || { sleep 5 && rm -rf "$(distdir)"; }; \
+ else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
+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"
+GZIP_ENV = --best
+DIST_ARCHIVES = $(distdir).tar.xz
+DIST_TARGETS = dist-xz
+# Exists only to be overridden by the user if desired.
+AM_DISTCHECK_DVI_TARGET = dvi
+distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
+distcleancheck_listfiles = find . -type f -print
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+ACLOCAL_AMFLAGS = -I m4
+SUBDIRS = src tests tests-fuzz python samples distro doc
+EXTRA_DIST = README.md
+AM_DISTCHECK_CONFIGURE_FLAGS =
+CODE_COVERAGE_INFO = coverage.info
+CODE_COVERAGE_HTML = coverage.html
+CODE_COVERAGE_DIRS = \
+ src/contrib \
+ src/knot \
+ src/libdnssec \
+ src/libknot \
+ src/libzscanner
+
+code_coverage_quiet = --quiet
+all: all-recursive
+
+.SUFFIXES:
+am--refresh: Makefile
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
+ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ $(am__cd) $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+$(am__aclocal_m4_deps):
+src/libknot/version.h: $(top_builddir)/config.status $(top_srcdir)/src/libknot/version.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+src/libdnssec/version.h: $(top_builddir)/config.status $(top_srcdir)/src/libdnssec/version.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+src/libzscanner/version.h: $(top_builddir)/config.status $(top_srcdir)/src/libzscanner/version.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+Doxyfile: $(top_builddir)/config.status $(srcdir)/Doxyfile.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+python/libknot/__init__.py: $(top_builddir)/config.status $(top_srcdir)/python/libknot/__init__.py.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+src/knot/modules/static_modules.h: $(top_builddir)/config.status $(top_srcdir)/src/knot/modules/static_modules.h.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool config.lt
+
+# 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"
+
+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"
+cscope: cscope.files
+ test ! -s cscope.files \
+ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+ -rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+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
+ -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ $(am__remove_distdir)
+ test -d "$(distdir)" || mkdir "$(distdir)"
+ @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
+ -test -n "$(am__skip_mode_fix)" \
+ || find "$(distdir)" -type d ! -perm -755 \
+ -exec chmod u+rwx,go+rx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r "$(distdir)"
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
+ $(am__post_remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+ $(am__post_remove_distdir)
+
+dist-lzip: distdir
+ tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+ $(am__post_remove_distdir)
+dist-xz: distdir
+ tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+ $(am__post_remove_distdir)
+
+dist-zstd: distdir
+ tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
+ $(am__post_remove_distdir)
+
+dist-tarZ: distdir
+ @echo WARNING: "Support for distribution archives compressed with" \
+ "legacy program 'compress' is deprecated." >&2
+ @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__post_remove_distdir)
+
+dist-shar: distdir
+ @echo WARNING: "Support for shar distribution archives is" \
+ "deprecated." >&2
+ @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+ shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
+ $(am__post_remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__post_remove_distdir)
+
+dist dist-all:
+ $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+ $(am__post_remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lz*) \
+ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
+ *.tar.xz*) \
+ xz -dc $(distdir).tar.xz | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ *.tar.zst*) \
+ zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
+ esac
+ chmod -R a-w $(distdir)
+ chmod u+w $(distdir)
+ mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
+ chmod a-w $(distdir)
+ test -d $(distdir)/_build || exit 0; \
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && am__cwd=`pwd` \
+ && $(am__cd) $(distdir)/_build/sub \
+ && ../../configure \
+ $(AM_DISTCHECK_CONFIGURE_FLAGS) \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ --srcdir=../.. --prefix="$$dc_install_base" \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
+ && cd "$$am__cwd" \
+ || exit 1
+ $(am__post_remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @test -n '$(distuninstallcheck_dir)' || { \
+ echo 'ERROR: trying to run $@ with an empty' \
+ '$$(distuninstallcheck_dir)' >&2; \
+ exit 1; \
+ }; \
+ $(am__cd) '$(distuninstallcheck_dir)' || { \
+ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+ exit 1; \
+ }; \
+ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: 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:
+
+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."
+@CODE_COVERAGE_ENABLED_FALSE@clean-local:
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-libtool \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+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 $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--refresh check check-am clean clean-cscope clean-generic \
+ clean-libtool clean-local cscope cscopelist-am ctags ctags-am \
+ dist dist-all dist-bzip2 dist-gzip dist-lzip dist-shar \
+ dist-tarZ dist-xz dist-zip dist-zstd distcheck distclean \
+ distclean-generic distclean-libtool distclean-tags \
+ distcleancheck distdir distuninstallcheck dvi dvi-am 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 installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am
+
+.PRECIOUS: Makefile
+
+
+.PHONY: singlehtml epub install-singlehtml install-epub
+singlehtml install-singlehtml epub install-epub:
+ $(MAKE) -C doc $@
+
+.PHONY: check-compile
+check-compile:
+ $(MAKE) $(AM_MAKEFLAGS) -C tests $@
+ $(MAKE) $(AM_MAKEFLAGS) -C tests-fuzz $@
+
+check-code-coverage:
+@CODE_COVERAGE_ENABLED_TRUE@ $(MAKE) $(AM_MAKEFLAGS) code-coverage-initial
+@CODE_COVERAGE_ENABLED_TRUE@ -$(MAKE) $(AM_MAKEFLAGS) -k check
+@CODE_COVERAGE_ENABLED_TRUE@ $(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
+@CODE_COVERAGE_ENABLED_TRUE@ $(MAKE) $(AM_MAKEFLAGS) code-coverage-html
+@CODE_COVERAGE_ENABLED_TRUE@ $(MAKE) $(AM_MAKEFLAGS) code-coverage-summary
+@CODE_COVERAGE_ENABLED_FALSE@ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+
+code-coverage-initial:
+@CODE_COVERAGE_ENABLED_TRUE@ $(LCOV) $(code_coverage_quiet) \
+@CODE_COVERAGE_ENABLED_TRUE@ --no-external \
+@CODE_COVERAGE_ENABLED_TRUE@ $(foreach dir, $(CODE_COVERAGE_DIRS), --directory $(top_builddir)/$(dir)) \
+@CODE_COVERAGE_ENABLED_TRUE@ --capture --initial \
+@CODE_COVERAGE_ENABLED_TRUE@ --ignore-errors source \
+@CODE_COVERAGE_ENABLED_TRUE@ --no-checksum \
+@CODE_COVERAGE_ENABLED_TRUE@ --compat-libtool \
+@CODE_COVERAGE_ENABLED_TRUE@ --output-file $(CODE_COVERAGE_INFO)
+@CODE_COVERAGE_ENABLED_FALSE@ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+
+code-coverage-capture:
+@CODE_COVERAGE_ENABLED_TRUE@ $(LCOV) $(code_coverage_quiet) \
+@CODE_COVERAGE_ENABLED_TRUE@ --no-external \
+@CODE_COVERAGE_ENABLED_TRUE@ $(foreach dir, $(CODE_COVERAGE_DIRS), --directory $(builddir)/$(dir)) \
+@CODE_COVERAGE_ENABLED_TRUE@ --capture \
+@CODE_COVERAGE_ENABLED_TRUE@ --ignore-errors source \
+@CODE_COVERAGE_ENABLED_TRUE@ --no-checksum \
+@CODE_COVERAGE_ENABLED_TRUE@ --compat-libtool \
+@CODE_COVERAGE_ENABLED_TRUE@ --output-file $(CODE_COVERAGE_INFO)
+@CODE_COVERAGE_ENABLED_FALSE@ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+
+code-coverage-html:
+@CODE_COVERAGE_ENABLED_TRUE@ @echo "Generating code coverage HTML report (this might take a while)"
+@CODE_COVERAGE_ENABLED_TRUE@ LANG=C $(GENHTML) $(code_coverage_quiet) \
+@CODE_COVERAGE_ENABLED_TRUE@ --output-directory $(CODE_COVERAGE_HTML) \
+@CODE_COVERAGE_ENABLED_TRUE@ --title "Knot DNS $(PACKAGE_VERSION) Code Coverage" \
+@CODE_COVERAGE_ENABLED_TRUE@ --legend --show-details \
+@CODE_COVERAGE_ENABLED_TRUE@ --ignore-errors source \
+@CODE_COVERAGE_ENABLED_TRUE@ $(CODE_COVERAGE_INFO)
+@CODE_COVERAGE_ENABLED_FALSE@ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+
+code-coverage-summary:
+@CODE_COVERAGE_ENABLED_TRUE@ $(LCOV) \
+@CODE_COVERAGE_ENABLED_TRUE@ --summary $(CODE_COVERAGE_INFO)
+@CODE_COVERAGE_ENABLED_FALSE@ @echo "You need to run configure with --enable-code-coverage to enable code coverage"
+
+@CODE_COVERAGE_ENABLED_TRUE@clean-local: code-coverage-clean
+@CODE_COVERAGE_ENABLED_TRUE@ -find . -name "*.gcno" -delete
+@CODE_COVERAGE_ENABLED_TRUE@code-coverage-clean:
+@CODE_COVERAGE_ENABLED_TRUE@ -$(LCOV) --directory $(top_builddir) -z
+@CODE_COVERAGE_ENABLED_TRUE@ -rm -rf $(CODE_COVERAGE_INFO) $(CODE_COVERAGE_HTML)
+@CODE_COVERAGE_ENABLED_TRUE@ -find . -name "*.gcda" -o -name "*.gcov" -delete
+
+.PHONY: check-code-coverage code-coverage-initial code-coverage-capture code-coverage-html code-coverage-summary code-coverage-clean
+.NOTPARALLEL: clean
+
+# 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/NEWS b/NEWS
new file mode 100644
index 0000000..e651e7a
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,2873 @@
+Knot DNS 3.2.6 (2023-04-04)
+===========================
+
+Improvements:
+-------------
+ - libs: upgraded embedded libngtcp2 to 0.13.1
+ - libs: added support for building on Cygwin and MSYS (Thanks to Christopher Ng)
+ - mod-dnstap: improved precision of stored time values
+ - kdig: added option for EDNS EXPIRE (see '+expire') #836
+ - kdig: extended description of SOA timers in the multiline mode
+ - kdig: reduced latency of TLS communication
+ - libknot: added EDE codes 28 and 29
+ - doc: various improvements
+
+Bugfixes:
+---------
+ - knotd: generated catalog zone not updated upon server reload #834
+ - knotd: failed to check shared module configuration
+ - knotd: missing RCU registration of the statistics thread (Thanks to Qin Longfei)
+ - knotd: server logs failed to send QUIC packets in the XDP mode
+ - libs: inconsistent transformation of IPv4-Compatible IPv6 Addresses
+ - utils: failed to load configuration if dnstap module is enabled #831
+ - libknot: missing include string.h
+
+Knot DNS 3.2.5 (2023-02-02)
+===========================
+
+Features:
+---------
+ - knotd: new configuration option for enforcing IXFR fallback (see 'zone.provide-ixfr')
+
+Improvements:
+-------------
+ - knotd: changed UNIX socket file mode to 0222 for answering and 0220 for control
+ - mod-probe: new support for communication over a UNIX socket
+ - kdig: new support for communication over a UNIX socket
+ - libs: upgraded embedded libngtcp2 to 0.13.0
+ - doc: various improvements
+
+Bugfixes:
+---------
+ - knotd: failed to get catalog member configuration if catalog template is in a template
+ - knotd: failed to respond over a UNIX socket with EDNS
+ - knotd: unexpected zone update upon restart or zone reload if ZONEMD generation is enabled
+ - knotd: redundant zone flush of unchanged zone if zone file load is 'difference-no-serial'
+ - knotd/kxdpgun: failed to receive messages over XDP with drivers tap or ena
+ - knotc: zone check doesn't report missing zone file #829
+ - kxdpgun: program crashes when remote closes QUIC connection instead of resumption
+ - mod-geoip: configuration check leaks memory in the geodb mode
+ - utils: unwanted color reset sequences in non-color output
+
+Knot DNS 3.2.4 (2022-12-12)
+===========================
+
+Improvements:
+-------------
+ - knotd: significant speed-up of catalog zone update processing
+ - knotd: new runtime check if RRSIG lifetime is lower than RRSIG refresh
+ - knotd: reworked zone re-bootstrap scheduling to be less progressive
+ - mod-synthrecord: module can work with CIDR-style reverse zones #826
+ - python: new libknot wrappers for some dname transformation functions
+ - doc: a few fixes and improvements
+
+Bugfixes:
+---------
+ - knotd: incomplete zone is received when IXFR falls back to AXFR due to
+ connection timeout if primary puts initial SOA only to the first message
+ - knotd: first zone re-bootstrap is planned after 24 hours
+ - knotd: EDNS EXPIRE option is present in outgoing transfer of a catalog zone
+ - knotd: catalog zone can expire upon EDNS EXPIRE processing
+ - knotd: DNSSEC signing doesn't fail if no offline KSK records available
+
+Knot DNS 3.2.3 (2022-11-20)
+===========================
+
+Improvements:
+-------------
+ - knotd: new per-zone DS push configuration option (see 'zone.ds-push')
+ - libs: upgraded embedded libngtcp2 to 0.11.0
+
+Bugfixes:
+---------
+ - knsupdate: program crashes when sending an update
+ - knotd: server drops more responses over UDP under higher load
+ - knotd: missing EDNS padding in responses over QUIC
+ - knotd: some memory issues when handling unusual QUIC traffic
+ - kxdpgun: broken IPv4 source subnet processing
+ - kdig: incorrect handling of unsent data over QUIC
+
+Knot DNS 3.2.2 (2022-11-01)
+===========================
+
+Features:
+---------
+ - knotd,kxdpgun: support for VLAN (802.1Q) traffic in the XDP mode
+ - knotd: added configurable delay upon D-Bus initialization (see 'server.dbus-init-delay')
+ - kdig: support for JSON (RFC 8427) output format (see '+json')
+ - kdig: support for PROXYv2 (see '+proxy') (Gift for Peter van Dijk)
+
+Improvements:
+-------------
+ - mod-geoip: module respects the server configuration of answer rotation
+ - libs: upgraded embedded libngtcp2 to 0.10.0
+ - tests: improved robustness of some unit tests
+ - doc: added description of zone bootstrap re-planning
+
+Bugfixes:
+---------
+ - knotd: catalog confusion when a member is added and immediately deleted #818
+ - knotd: defective handling of short messages with PROXYv2 header #816
+ - knotd: inconsistent processing of malformed messages with PROXYv2 header #817
+ - kxdpgun: incorrect XDP mode is logged
+ - packaging: outdated dependency check in RPM packages
+
+Knot DNS 3.2.1 (2022-09-09)
+===========================
+
+Improvements:
+-------------
+ - libknot: added compatibility with libbpf 1.0 and libxdp
+ - libknot: removed some trailing white space characters from textual RR format
+ - libs: upgraded embedded libngtcp2 to 0.8.1
+
+Bugfixes:
+---------
+ - knotd: some non-DNS packets not passed to OS if XDP mode enabled
+ - knotd: inappropriate log about QUIC port change if QUIC not enabled
+ - knotd/kxdpgun: various memory leaks related to QUIC and TCP
+ - kxdpgun: can crash at high rates in emulated XDP mode
+ - tests: broken XDP-TCP test on 32-bit platforms
+ - kdig: failed to build with enabled QUIC on OpenBSD
+ - systemd: failed to start server due to TemporaryFileSystem setting
+ - packaging: missing knot-dnssecutils package on CentOS 7
+
+Knot DNS 3.2.0 (2022-08-22)
+===========================
+
+Features:
+---------
+ - knotd: finalized TCP over XDP implementation
+ - knotd: initial implementation of DNS over QUIC in the XDP mode (see 'xdp.quic')
+ - knotd: new incremental DNSKEY management for multi-signer deployment (see 'policy.dnskey-management')
+ - knotd: support for remote grouping in configuration (see 'groups' section)
+ - knotd: implemented EDNS Expire option (RFC 7314)
+ - knotd: NSEC3 salt is changed with every ZSK rollover if lifetime is set to -1
+ - knotd: support for PROXY v2 protocol over UDP (Thanks to Robert Edmonds) #762
+ - knotd: support for key labels with PKCS #11 keystore (see 'keystore.key-label')
+ - knotd: SVCB/HTTPS treatment according to draft-ietf-dnsop-svcb-https
+ - keymgr: new JSON output format (see '-j' parameter) for listing keys or zones (Thanks to JP Mens)
+ - kxdpgun: support for DNS over QUIC with some testing modes (see '-U' parameter)
+ - kdig: new DNS over QUIC support (see '+quic')
+
+Improvements:
+-------------
+ - knotd: reduced memory consumption when processing IXFR, DNSSEC, catalog, or DDNS
+ - knotd: RRSIG refresh values don't have to match in the mode Offline KSK
+ - knotd: better decision whether AXFR fallback is needed upon a refresh error
+ - knotd: NSEC3 resalt event was merged with the DNSSEC event
+ - knotd: server logs when the connection to remote was taken from the pool
+ - knotd: server logs zone expiration time when the zone is loaded
+ - knotd: DS check verifies removal of old DS during algorithm rollover
+ - knotd: DNSSEC-related records can be updated via DDNS
+ - knotd: new 'xdp.udp' configuration option for disabling UDP over XDP
+ - knotd: outgoing NOTIFY is replanned if failed
+ - knotd: configuration checks if zone MIN interval values are lower or equal to MAX ones
+ - knotd: DNSSEC-related zone semantic checks use DNSSEC validation
+ - knotd: new configuration value 'query' for setting ACL action
+ - knotd: new check on near end of imported Offline KSK records
+ - knotd/knotc: implemented zone catalog purge, including orphaned member zones
+ - knotc: interactive mode supports catalog zone completion, value completion, and more
+ - knotc: new default brief and colorized output from zone status
+ - knotc: unified empty values in zone status output
+ - keymgr: DNSKEY TTL is taken from KSR in the Offline KSK mode
+ - kjournalprint: path to journal DB is automatically taken from the configuration,
+ which can be specified using '-c', '-C' (or '-D')
+ - kcatalogprint: path to catalog DB is automatically taken from the configuration,
+ which can be specified using '-c', '-C' (or '-D')
+ - kzonesign: added automatic configuration file detection and '-C' parameter
+ for configuration DB specificaion
+ - kzonesign: all CPU threads are used for DNSSEC validation
+ - libknot: dname pointer cannot point to another dname pointer when encoding RRsets #765
+ - libknot: QNAME case is preserved in knot_pkt_t 'wire' field (Thanks to Robert Edmonds) #780
+ - libknot: reduced memory consumption of the XDP mode
+ - libknot: XDP filter supports up to 256 NIC queues
+ - kxdpgun: new options for specifying source and remote MAC addresses
+ - utils: extended logging of LMDB-related errors
+ - utils: improved error outputs
+ - kdig: query has AD bit set by default
+ - doc: various improvements
+
+Bugfixes:
+---------
+ - knotd: zone changeset is stored to journal even if disabled
+ - knotd: journal not applied to zone file if zone file changed during reload
+ - knotd: possible out-of-order processing or postponed zone events to far future
+ - knotd: incorrect TTL is used if updated RRSet is empty over control interface
+ - knotd/libs: serial arithmetics not used for RRSIG expiration processing
+ - knsupdate: incorrect RRTYPE in the question section
+
+Compatibility:
+--------------
+ - knotd: default value for 'zone.journal-max-depth' was lowered to 20
+ - knotd: default value for 'policy.nsec3-iterations' was lowered to 0
+ - knotd: default value for 'policy.rrsig-refresh' is propagation delay + zone maximum TTL
+ - knotd: server fails to load configuration if 'policy.rrsig-refresh' is too low
+ - knotd: configuration option 'server.listen-xdp' has no effect
+ - knotd: new configuration check on deprecated DNSSEC algorithm
+ - knotc: new '-e' parameter for full zone status output
+ - keymgr: new '-e' parameter for full key list output
+ - keymgr: brief key listing mode is enabled by default
+ - keymgr: renamed parameter '-d' to '-D'
+ - knsupdate: default TTL is set to 3600
+ - knsupdate: default zone is empty
+ - kjournalprint: renamed parameter '-c' to '-H'
+ - python/libknot: removed compatibility with Python 2
+
+Packaging:
+----------
+ - systemd: removed knot.tmpfile
+ - systemd: added some hardening options
+ - distro: Debian 9 and Ubuntu 16.04 no longer supported
+ - distro: packages for CentOS 7 are built in a separate COPR repository
+ - kzonecheck/kzonesign/knsec3hash: moved to new package knot-dnssecutils
+
+Knot DNS 3.1.9 (2022-08-10)
+===========================
+
+Improvements:
+-------------
+ - knotd: new configuration checks on unsupported catalog settings
+ - knotd: semantic check issues have notice log level in the soft mode
+ - keymgr: command generate-ksr automatically sets 'from' parameter to last
+ offline KSK records' timestamp if it's not specified
+ - keymgr: command show-offline starts from the first offline KSK record set
+ if 'from' parameter isn't specified
+ - kcatalogprint: new parameters for filtering catalog or member zone
+ - mod-probe: default rate limit was increased to 100000
+ - libknot: default control timeout was increased to 30 seconds
+ - python/libknot: various exceptions are raised from class KnotCtl
+ - doc: some improvements
+
+Bugfixes:
+---------
+ - knotd: incomplete outgoing IXFR is responded if journal history is inconsistent
+ - knotd: manually triggered zone flush is suppressed if disabled zone synchronization
+ - knotd: failed to configure XDP listen interface without port specification
+ - knotd: de-cataloged member zone's file isn't deleted #805
+ - knotd: member zone leaks memory when reloading catalog during dynamic configuration change
+ - knotd: server can crash when reloading modules with DNSSEC signing (Thanks to iqinlongfei)
+ - knotd: server crashes during shutdown if PKCS #11 keystore is used
+ - keymgr: command del-all-old isn't applied to all keys in the removed state
+ - kxdpgun: user specified network interface isn't used
+ - libs: fixed compilation on illumos derivatives (Thanks to Nick Ewins)
+
+Knot DNS 3.1.8 (2022-04-28)
+===========================
+
+Features:
+---------
+ - knotd: optional automatic ACL for XFR and NOTIFY (see 'remote.automatic-acl')
+ - knotd: new soft zone semantic check mode for allowing defective zone loading
+ - knotc: added zone transfer freeze state to the zone status output
+
+Improvements:
+-------------
+ - knotd: added configuration check for serial policy of generated catalogs
+
+Bugfixes:
+---------
+ - knotd/libknot: the server can crash when validating a malformed TSIG record
+ - knotd: outgoing zone transfer freeze not preserved during server reload
+ - knotd: catalog UPDATE not processed if previous UPDATE processing not finished #790
+ - knotd: zone refresh not started if planned during server reload
+ - knotd: generated catalogs can be queried over UDP
+ - knotd/utils: failed to open LMDB database if too many stale slots occupy the lock table
+
+Knot DNS 3.1.7 (2022-03-30)
+===========================
+
+Features:
+---------
+ - knotd: new configuration items for restricting minimum and maximum zone expire
+ and retry intervals (see 'zone.expire-min-interval', 'zone.expire-max-interval',
+ 'zone.retry-min-interval', 'zone.retry-max-interval') #785
+ - knotc: added catalog information to zone status
+
+Improvements:
+-------------
+ - knotd: better warning message if SOA serial comparison failed when loading from zone file
+ - knotc: zone status shows all zone events when frozen
+ - keymgr: better error message is returned when importing SKR with insufficient permissions
+ - kdig: transfer status is also printed if failed
+
+Bugfixes:
+---------
+ - knotd: incomplete implementation of the Offline KSK mode in the IXFR and DDNS processing
+ - knotd: catalog zone accepts duplicate members via UPDATE #786
+ - knotd: server crashes if catalog database contains orphaned member zones
+ - knotd: old journal is scraped when restoring just the zone file
+ - knotd: some planned zone events can be lost during server reload
+ - knotd: frozen zone gets thawed during server reload
+ - knsupdate: missing section names in the show output
+ - knsupdate: inappropriate log message if called from a script
+
+Knot DNS 3.1.6 (2022-02-08)
+===========================
+
+Features:
+---------
+ - knotd: optional D-Bus notifications for significant server and zone events
+ (see 'server.dbus-event')
+ - knotd: new submission configuration option for delayed KSK post-activation
+ (see 'submission.parent-delay')
+ - knotc: new commands for outgoing XFR freeze (see 'zone-xfr-freeze' and 'zone-xfr-thaw')
+ - kzonesign: added multithreaded DNSSEC validation mode (see '--verify')
+
+Improvements:
+-------------
+ - kdig: trailing data in reply packet is accepted with a warning
+ - kdig: XFR responses are checked if SOA owners match
+ - knotd: failed remote operations are logged as info instead of debug
+ - knsec3hash: added alternative and more natural parameter semantics
+ - knsupdate: interactive mode is newly based on library Editline
+ - Dockerfile: added UID argument to facilitate the use of unprivileged container #783
+ - doc: various fixes and improvements
+
+Bugfixes:
+---------
+ - libknot: inaccurate KNOT_DNAME_TXT_MAXLEN constant value #781
+ - knotd: propagation delay not considered before DS push
+ - knotd: excessive refresh retry delay when a few early attemps fail
+ - knotd: duplicate KSK submission log message during a KSK rollover
+ - kdig: dname letter case not preserved in XFR and Dnstap outputs
+ - mod-cookies: missing server cookie in responses over TCP
+
+Knot DNS 3.1.5 (2021-12-20)
+===========================
+
+Features:
+---------
+ - knotd: optional outgoing TCP connection pool for faster communication with remotes
+ (see 'server.remote-pool-limit' and 'server.remote-pool-timeout')
+ - knotd: optional unreachable remote tracking to avoid zone events clogging
+ (see 'server.remote-retry-delay')
+ - knotd: new ZONEMD generation mode for the record removal from the zone apex #760
+ (see 'zone.zonemd-generate: remove')
+ - mod-dnsproxy: new source address match option (see 'mod-dnsproxy.address')
+ - scripts/probe_dump: simple mod-probe client
+
+Improvements:
+-------------
+ - knotd: DS push sets DS TTL equal to DNSKEY TTL
+ - knotd: extended zone purge error logging
+ - knotd: zone file parsing error message was extended by the file name
+ - knotd: improved debug log message when TCP timeout is reached
+ - knotd: new configuration check for using the default number of NSEC3 iterations
+ - knotd: new configuration check for insufficient RRSIG refresh time
+ - mod-geoip: configuration check newly verifies the module configuration file #778
+ - kdig: option +notimeout or +timeout=0 is interpreted as infinity
+ - kdig: option +noretry is interpreted as zero retries
+ - python/probe: more detailed default output format
+ - doc: many spelling fixes (Thanks to Josh Soref)
+ - doc: various fixes and improvements
+
+Bugfixes:
+---------
+ - knotd: imperfect TCP connection closing in the XDP mode
+ - knotd: TCP reset packets are wrongly checked for ackno in the XDP mode
+ - knotd: only first zone name is logged for multi-zone control operations #776
+ - knotd: minor memory leak when full zone update fails to write to journal
+ - knotc: configuration check doesn't check a configuration database
+ - mod-dnstap: incorrect QNAME case restore in some corner cases (Thanks to Robert Edmonds) #777
+
+Knot DNS 3.1.4 (2021-11-04)
+===========================
+
+Features:
+---------
+ - mod-dnstap: added 'responses-with-queries' configuration option (Thanks to Robert Edmonds) #764
+
+Improvements:
+-------------
+ - knotd: DNSSEC keys are logged in sorted order by timestamp
+ - mod-cookies: added statistics counter for dropped queries due to the slip limit
+ - mod-dnstap: restored the original query QNAME case #773 (Thanks to Robert Edmonds)
+ - configure: improved compatibility of some scripts on macOS and BSDs
+ - doc: updates on DNSSEC signing
+
+Bugfixes:
+---------
+ - knotd: server can crash when receiving queries with NSID EDNS flag #774 (Thanks to Romain Labolle)
+ - knotd: server crashes on reload when no interfaces configured #770
+ - knotd: ZONEMD without DNSSEC not handled correctly
+ - knotd: generated catalog zone not updated on config reload #772
+ - knotd: zone catalog not verified before its interpretation
+ - knotd: ds-push fails to update the parent zone if a CNAME exists for a non-terminal node
+
+Knot DNS 3.1.3 (2021-10-18)
+===========================
+
+Improvements:
+-------------
+ - knotd: added simple error logging to orphaned zone purge
+ - knotd: allow manual public-only keys for unused algorithm
+ - kdig: send ALPN when using DoT or XoT #769
+ - doc: various fixes and improvements #767
+
+Bugfixes:
+---------
+ - knotd: catalog backup doesn't preserve version of the catalog implementation
+ - knotd: NOTIFY is scheduled even when DNSSEC signing is up-to-date
+ - knotd: server can crash when zone difference is inconsistent upon cold start
+ - knotd: zone not bootstrapped when zone file load failed due to an error
+ - knotd: broken AXFR with knot as slave and dnsmasq as master (Thanks to Daniel Gröber)
+ - knotd: journal not able to free up space when zone-in-journal present and zonefile written
+ - mod-stats: missing protocol counters for TCP over XDP
+ - kzonesign: input zone name not lower-cased
+
+Knot DNS 3.1.2 (2021-09-08)
+===========================
+
+Features:
+---------
+ - knotd: new policy configuration for postponing complete deletion of previous keys
+ - keymgr: new optional pretty mode (-b) of listing keys
+ - kdig: added support for TCP keepopen #503
+
+Improvements:
+-------------
+ - knotd: configuration item values can contain UTF-8 characters
+ - knotd: added configuration check for database storage writability
+ - knotd: better error reporting if zone is empty
+ - knotd: smaller journal database chunks in order to mitigate LMDB fragmentation
+ - knotd/kxdpgun: CAP_SYS_RESOURCE capability no longer needed for XDP on Linux >= 5.11
+
+Bugfixes:
+---------
+ - knotd: incomplete NSEC3 proof in response to opt-outed empty non-terminal
+ - knotd: wrong SOA serial handling when enabling signing on already existing secondary zone
+ - knotd: defective ZONEMD verification error reporting when loading zone #759
+ - knotd: server can crash when reloading catalog zone #761
+ - knotd: DNSSEC validation doesn't work when only NSEC3 chain changes
+ - knotd: DNSSEC validation doesn't check if empty non-terminal over non-opt-outed
+ delegation isn't opt-outed too
+ - knotd: ZONEMD generation doesn't cause flushing zone to disk #758
+ - knotd: incorrect evaluation of ACL deny rule in combination with TSIG
+ - knotd: failed DS-check is replaned even if no key is ready
+ - kdig: abort when query times out #763
+ - libzscanner: missing output overflow check in the SVCB parsing
+
+Compatibility:
+--------------
+ - keymgr: parameter -d is marked deprecated in favor of new parameter -D
+ - kjournalprint: parameter -n is marked deprecated in favor of new parameter -x
+
+Knot DNS 3.1.1 (2021-08-10)
+===========================
+
+Improvements:
+-------------
+ - keymgr: import-bind sets publish and active timers to now if missing timers #747
+ - mod-rrl: added QNAME, which triggered an action, to log messages #757
+ - systemd: added environment variable for setting maximum configuration DB size
+
+Bugfixes:
+---------
+ - knotd: adding RRSIGs to a signed zone can lead to redundant RRSIGs for some NSEC(3)s
+ - knotd: code not compiled correctly for ARM on Fedora >= 33
+ - knotd: server can crash when opening catalog DB on startup
+ - knotd: incorrect catalog update counts in logs
+ - knotd: journal discontinuity and zone-in-journal result in incorrectly calculated journal occupation
+ - kdig: +noall does not filter out AUTHORITY comment #749
+ - tests: journal unit test not passing if memory page size is different from 4096
+
+Reverts:
+--------
+ - libzscanner: reverted "omitted TTL value is correctly set to the last explicitly stated value (RFC 1035)" #751
+
+Knot DNS 3.1.0 (2021-08-02)
+===========================
+
+Features:
+---------
+ - knotd: automatic zone catalog generation based on actual configuration
+ - knotd: zone catalog supports configuration groups
+ - knotd: support for ZONEMD validation and generation
+ - knotd: basic support for TCP over XDP processing
+ - knotd: configuration option for enabling IP route check in the XDP mode
+ - knotd: support for epoll (Linux) and kqueue (*BSD, macOS) socket polling
+ - knotd: extended EDNS error (EDE) is added to the response if appropriate
+ - knotd: DNSSEC operation with extra ready public-only KSK is newly allowed
+ - knotd: new zone backup/restore filters for more variable component specification
+ - knotd: adaptive systemd service start timeout and new zone loading status #733
+ - knotd: configuration option for enabling TCP Fast Open on outbound communication
+ - knotd: when the server starts, zone NOTIFY is send only if not sent already
+ - knotc: zone reload with the force flag triggers reload of the zone and its modules
+ - libs: support for parsing and dumping SVCB and HTTPS resource records
+ - kdig: support for TCP Fast Open along with DoT/DoH #549
+ - kxdpgun: basic support for DNS over TCP processing
+ - kxdpgun: current traffic statistics can be printed using a USR1 signal
+ - python: new libknot/probe API wrapper
+
+Improvements:
+-------------
+ - knotd: PID file is created even in the foreground mode
+ - knotd: more robust and enhanced zone data backup and restore operations
+ - knotd: maximum length of an XFR message is limited to 16 KiB for better compression
+ - knotd: maximum CNAME/DNAME chain depth per reply was decreased from 20 to 5
+ - knotd: improved performance of processing domain names with many short labels
+ - knotd: adaptive limit on the number of LMDB readers to avoid problems with many workers
+ - knotd: TTL of generated NSEC(3) records is set to min(SOA TTL, SOA minimum)
+ - knotd: TTL of generated NSEC3PARAM is equal to TTL of NSEC3 records
+ - knotd: maximum TCP segment size is restricted to 1220 octets on Linux #468
+ - knotc: various improvements in error reporting
+ - knotc: default control timeout is infinity in the blocking mode
+ - dnssec: dnskey generator tries to return a key with a unique keytag
+ - kxdpgun: RLIMIT_MEMLOCK is increased only if not high enough
+ - kxdpgun: RTNETLINK is used for getting network information instead of the ip command
+
+Bugfixes:
+---------
+ - knotd: DNAME not applied more than once to resolve the query #714
+ - knotd: root zone not correctly purged from the journal
+ - kzonecheck: incorrect check for opt-outed empty non-terminal nodes
+ - libzscanner: wrong error line number
+ - libzscanner: broken multiline rdata processing if an error occurs
+ - mod-geoip: NXDOMAIN is responded instead of NODATA #745
+ - make: build fails with undefined references if building using slibtool #722
+
+Packaging:
+----------
+ - knotd: systemd service reload uses 'kill -HUP' instead of 'knotc reload'
+ - kxdpgun: new library dependency libmnl
+ - mod-dnstap: new package separate from the knot package
+ - mod-geoip: new package separate from the knot package
+
+Compatibility:
+--------------
+ - configure: option '--enable-xdp=yes' means use an external libbpf if available
+ or use the embedded one
+ - libzscanner: omitted TTL value is correctly set to the last explicitly stated value (RFC 1035)
+ - knotc: zone restore from an old backup (3.0.x) requires forced operation
+ - knotd: configuration option 'server.listen-xdp' is replaced with 'xdp.listen'
+ - knotd: zone file loading with automatic SOA serial incrementation newly
+ requires having full zone in the journal
+ - knotd: obsolete configuration options 'zone.disable-any', 'server.tcp-handshake-timeout'
+ are silently ignored
+ - knotd: obsolete configuration options 'zone.max-zone-size', 'zone.max-journal-depth',
+ 'zone.max-journal-usage', 'zone.max-refresh-interval', 'zone.min-refresh-interval'
+ 'server.max-ipv4-udp-payload', 'server.max-ipv6-udp-payload', 'server.max-udp-payload',
+ 'server.tcp-reply-timeout', 'server.max-tcp-clients' are ignored
+ - knotd: obsolete default template options 'template.journal-db',
+ 'template.kasp-db', 'template.timer-db', 'template.max-journal-db-size',
+ 'template.journal-db-mode', 'template.max-timer-db-size',
+ 'template.max-kasp-db-size' are ignored
+
+Knot DNS 3.0.11 (2022-04-28)
+============================
+
+Improvements:
+-------------
+ - doc: various fixes and improvements
+
+Bugfixes:
+---------
+ - knotd/libknot: the server can crash when validating a malformed TSIG record
+ - knotd: public-only key makes DNSSEC signing fail
+ - knotd: frozen zone gets thawed during server reload
+ - knotd: zone refresh not started if planned during server reload
+ - knotd: some planned zone events can be lost during server reload
+ - knotd: propagation delay not considered before DS push
+ - knotd: duplicate KSK submission log message during a KSK rollover
+ - mod-cookies: missing server cookie in responses over TCP
+ - knsupdate: missing section names in the show output
+
+Knot DNS 3.0.10 (2021-11-04)
+============================
+
+Improvements:
+-------------
+ - doc: various fixes and improvements
+
+Bugfixes:
+---------
+ - knotd: server can crash when receiving queries with NSID EDNS flag #774 (Thanks to Romain Labolle)
+ - knotd: ds-push fails to update the parent zone if a CNAME exists for a non-terminal node
+ - knotd: server crashes on reload when no interfaces configured #770
+ - knotd: journal not able to free up space when zone-in-journal present and zonefile written
+ - knotd: broken AXFR with knot as slave and dnsmasq as master (Thanks to Daniel Gröber)
+ - knotd: server can crash when zone difference is inconsistent upon cold start
+ - mod-stats: missing protocol counters for TCP over XDP
+ - kzonesign: input zone name not lower-cased
+
+Knot DNS 3.0.9 (2021-09-09)
+===========================
+
+Improvements:
+-------------
+ - keymgr: import-bind sets publish and active timers to now if missing timers #747
+
+Bugfixes:
+---------
+ - knotd: incomplete NSEC3 proof in response to opt-outed empty non-terminal
+ - knotd: journal discontinuity and zone-in-journal result in incorrectly calculated journal occupation
+ - knotd: incorrect evaluation of ACL deny rule in combination with TSIG
+ - knotd: failed DS-check is replanned even if no key is ready
+ - knotd: root zone not correctly purged from the journal
+ - kdig: +noall does not filter out AUTHORITY comment #749
+
+Knot DNS 3.0.8 (2021-07-16)
+===========================
+
+Features:
+---------
+ - knotc: new command for loading DNSSEC keys without dropping all RRSIGs when re-signing
+ - knotd: new policy configuration option for disabling some DNSSEC safety features #741
+ - mod-geoip: new dnssec and policy configuration options
+
+Bugfixes:
+---------
+ - knotd: early KSK removal during a KSK rollover if automatic KSK submission check
+ is enabled and DNSKEY TTL is lower than the corresponding DS TTL
+ - knotd: failed to generate a new DNSKEY if previously generated shared key not available
+ - knotd: periodical error logging when a PKCS #11 keystore failed to initialize #742
+ - knotd: zone commit doesn't check for missing SOA record
+
+Knot DNS 3.0.7 (2021-06-16)
+===========================
+
+Features:
+---------
+ - knotd: new configuration policy option for CDS digest algorithm setting #738
+ - keymgr: new command for primary SOA serial manipulation in on-secondary signing mode
+
+Improvements:
+-------------
+ - knotd: improved algorithm rollover to shorten the last step of old RRSIG publication
+
+Bugfixes:
+---------
+ - knotd: zone is flushed upon server start, despite DNSSEC signing is up-to-date
+ - knotd: wildcard nonexistence is proved on empty-non-terminal query
+ - knotd: redundant wildcard proof for non-authoritative data in a reply
+ - knotd: missing wildcard proofs in a wildcard-cname loop reply
+ - knotd: incorrectly synthesized CNAME owner from a wildcard record #715
+ - knotd: zone-in-journal changeset ignores journal-max-usage limit #736
+ - knotd: incorrect processing of zone-in-journal changeset with SOA serial 0
+ - knotd: broken initialization of processing workers if SO_REUSEPORT(_LB) not available
+ - kjournalprint: reported journal usage is incorrect #736
+ - keymgr: cannot parse algorithm name ed448 #739
+ - keymgr: default key size not set properly
+ - kdig: failed to process huge DoH responses
+ - libknot/probe: some corner-case bugs
+
+Knot DNS 3.0.6 (2021-05-12)
+===========================
+
+Features:
+---------
+ - mod-probe: new module for simple traffic logging (Python API not yet included)
+
+Improvements:
+-------------
+ - keymgr: new mode for listing zones with at least one key stored
+ - keymgr: the pregenerate command accepts optional timestamp-from parameter
+ - kzonecheck: accept '-' as substitution for standard input #727
+ - knotd: print an error when unable to change owner of a logging file
+ - knotd: new warning log if no interface is configured
+ - knotd: new signing policy check for NSEC3 iterations higher than 20
+ - knotd: don't allow backup to/restore from the DB storage directory
+ - Various code (mostly zone backup/restore), tests, and documentation improvements
+
+Bugfixes:
+---------
+ - knotd: secondary fails to load zone file if HTTPS or SVCB record is present #725
+ - knotd: (KSK roll-over) new KSK is not signing DNSKEY long enough before DS submission
+ - knotd: (KSK roll-over) old KSK uselessly published after roll-over finished
+ - knotd: malformed address in TCP-related logs when listening on a UNIX socket
+ - knotd: server responds FORMERR instead of BADTIME if TSIG signed time is zero #730
+ - modules: incorrect local and remote addresses in the XDP mode
+ - modules: failed to read configuration from a section without identifiers
+ - mod-synthrecord: queries on synthesized empty-non-terminals not answered with NODATA
+ - keymgr: confusing error if del-all-old command fails
+
+Knot DNS 3.0.5 (2021-03-25)
+===========================
+
+Improvements:
+-------------
+ - kdig: added support for TCP Fast Open on FreeBSD
+ - keymgr: the SEP flag can be changed on already generated keys
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - knotd: journal contents can be considered malformed after changeset merge
+ - knotd: broken detection of TCP Fast Open availability
+ - knotd: zone restore can stuck in an infinite loop if zone configuration changed
+ - knotd: failed zone backup makes control socket unavailable
+ - knotd: zone not stored to journal after reload if difference-no-serial is enabled
+ - knotd: old key is being used after an algorithm rollover with a shared policy #721
+ - keymgr: keytag not recomputed upon key flag change
+ - kdig: TCP not used if +fastopen is set
+ - mod-dnstap: the local address is empty
+ - kzonecheck: missing letter lower-casing of the origin parameter
+ - XDP mode wrongly detected on NetBSD
+ - Failed to build knotd_stdio fuzzing utility
+
+Knot DNS 3.0.4 (2021-01-20)
+===========================
+
+Improvements:
+-------------
+ - Sockets to CPUs binding is no longer enabled by default but can be enabled
+ via new configuration option 'server.socket-affinity'
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - DNS queries without EDNS to the root zone apex are dropped in the XDP mode
+ - Deterministic ECDSA signing leaks memory
+ - Zone not stored to journal if zonefile-load isn't ZONEFILE_LOAD_WHOLE
+ - Server crashes if the catalog zone isn't configured for registered member zones
+ - Server crashes when loading conflicting catalog member zones
+ - CNAME and DNAME records below delegation are not ignored #713
+ - Not all udp/tcp workers are used if the number of NIC queues is lower than
+ the number of udp/tcp workers
+ - Failed to load statistics and geoip modules if built as shared
+
+Knot DNS 3.0.3 (2020-12-15)
+===========================
+
+Features:
+---------
+ - Kjournalprint can display changesets starting from specific SOA serial
+
+Improvements:
+-------------
+ - New configuration check on ambiguous 'storage' specification #706
+ - New configuration check on problematic 'zonefile-load' with 'journal-contents' combination
+ - Server logs positive ACL check in debug severity level (Thanks to Andreas Schrägle)
+ - More verbose logging of failed zone backup
+ - Extended documentation for catalog zones
+
+Bugfixes:
+---------
+ - On-slave signing produces broken NSEC(3) chain if glue node becomes (un-)orphaned #705
+ - Server responds CNAME query with NXDOMAIN for CNAME synthesized from DNAME
+ - Kdig crashes if source address and dnstap logging are specified together #702
+ - Knotc fails to display error returned from zone freeze or zone thaw
+ - Dynamically reconfigured zone isn't loaded upon configuration commit
+ - Keymgr is unable to import BIND-style private key if it contains empty lines
+ - Zone backup fails to backup keys if any of them is public-only
+ - Failed to build with XDP support on Debian testing
+
+Knot DNS 3.0.2 (2020-11-11)
+===========================
+
+Features:
+---------
+ - kdig prints Extended DNS Error (Gift for Marek Vavruša)
+ - kxdpgun allows source IP address/subnet specification
+
+Improvements:
+-------------
+ - Server doesn't start if any of listen addresses fails to bind
+ - knotc no longer stores empty and adjacent identical commands to interactive history
+ - Depth of interactive history of knotc was increased to 1000 commands
+ - keymgr prints error messages to stderr instead of stdout
+ - keymgr checks for proper offline-ksk configuration before processing KSR or SKR
+ - keymgr imports Revoked timer from BIND keys
+ - Additional XDP support detection in server
+ - Lots of spelling and grammar fixes in documentation (Thanks to Paul Dee)
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - If more masters configured, zone retransfer triggers AXFR from all masters
+ - Server can fail to bind address during restart due to missing SO_REUSEADDR
+ - KSK imported from BIND doesn't roll over automatically
+ - libdnssec respects local GnuTLS policy — affects DNSSEC operations and Knot Resolver
+ - kdig can stuck in infinite loop when solving BADCOOKIE responses
+ - Zone names received over control interface are not lower-cased
+ - Zone attributes not secured with multi-threaded changes
+ - kzonecheck ignores forced dnssec checks if zone not signed
+ - kzonecheck fails on case-sensitivity of owner names in NSEC records #699
+ - kdig fails to establish TLS connection #700
+ - Server responds NOTIMPL to queries with QDCOUNT 0 and known OPCODE
+
+Knot DNS 3.0.1 (2020-10-10)
+===========================
+
+Features:
+---------
+ - New command in keymgr for validation of RRSIGs in SKR
+ - Keymgr validates RRSIGs in SKR during import
+ - New option in kzonecheck to skip DNSSEC-related checks
+
+Improvements:
+-------------
+ - Module noudp has new configuration option for UDP truncation rate
+ - Better detection of reproducible signing availability
+ - Kxdpgun allows setting of network interface
+ - Default control timeout in knotc was increased to 60 seconds
+ - DNSSEC validation searches for invalid redundant RRSIGs
+ - Configuration source detection no longer considers empty confdb directory as active configuration
+ - Zone backup preserves original zone file if zone file synchronization is disabled
+
+Bugfixes:
+---------
+ - NSEC3 re-salt can cause server crash due to possible zone inconsistencies
+ - Zone reload logs 'invalid parameter' if zone file not changed
+ - Outgoing multi-message transfer can contain invalid compression pointers under specific conditions
+ - Improper handling of file descriptors in libdnssec
+ - Server crashes if no policy is configured with DNSSEC validation
+ - Server crashes if DNSSEC validation is enabled for unsigned zone
+ - Failed to build with libnghttp2 (Thanks to Robert Edmonds)
+ - Various bugs in zone data backup/restore
+
+Knot DNS 3.0.0 (2020-09-09)
+===========================
+
+Features:
+---------
+ - High-performance networking mode using XDP sockets (requires Linux 4.18+)
+ - Support for Catalog zones including kcatalogprint utility
+ - New DNSSEC validation mode
+ - New kzonesign utility — an interface for manual DNSSEC signing
+ - New kxdpgun utility — high-performance DNS over UDP traffic generator for Linux
+ - DoH support in kdig using GnuTLS and libnghttp2
+ - New KSK revoked state (RFC 5011) in manual DNSSEC key management mode
+ - Deterministic signing with ECDSA algorithms (requires GnuTLS 3.6.10+)
+ - Module synthrecord supports reverse pointer shortening
+ - Safe persistent zone data backup and restore
+
+Improvements:
+-------------
+ - Processing depth of CNAME and DNAME chains is limited to 20
+ - Non-FQDN is allowed as 'update-owner-name' configuration option value
+ - Kdig prints detailed algorithm identifier for PRIVATEDNS and PRIVATEOID
+ in multiline mode #334
+ - Queries with QTYPE ANY or RRSIG are always responded with at most one random RRSet
+ - The statistics module has negligible performance overhead on modern CPUs
+ - If multithreaded zone signing is enabled, some additional zone maintenance
+ steps are newly parallelized
+ - ACL can be configured by reference to a remote
+ - Better CPU cache locality for higher query processing performance
+ - Logging to non-syslog streams contains timestamps with the timezone
+ - Keeping initial DNSKEY TTL and zone maximum TTL in KASP database to ensure
+ proper rollover timing in case of TTL changes during the rollover
+ - Responding FORMERR to queries with more OPT or TSIG records
+
+Bugfixes:
+---------
+ - Module onlinesign responds NXDOMAIN instead of NOERROR (NODATA) if DNSSEC not requested
+ - Outgoing multi-message transfer can contain invalid compression pointers under specific conditions
+
+Knot DNS 2.9.9 (2021-04-01)
+===========================
+
+Improvements:
+-------------
+ - keymgr: the SEP flag can be changed on already generated keys
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - knotd: journal contents can be considered malformed after changeset merge
+ - knotd: old key is being used after an algorithm rollover with a shared policy #721
+ - keymgr: keytag not recomputed upon key flag change
+ - kzonecheck: missing letter lower-casing of the origin parameter
+
+Knot DNS 2.9.8 (2020-12-15)
+===========================
+
+Bugfixes:
+---------
+ - On-slave signing produces broken NSEC(3) chain if glue node becomes (un-)orphaned #705
+ - If more masters configured, zone retransfer triggers AXFR from all masters
+ - KSK imported from BIND doesn't roll over automatically
+ - kzonecheck fails on case-sensitivity of owner names in NSEC records #699
+ - Server responds NOTIMPL to queries with QDCOUNT 0 and known OPCODE
+ - Kdig crashes if source address and dnstap logging are specified together #702
+ - Keymgr is unable to import BIND-style private key if it contains empty lines
+ - Knotc fails to display error returned from zone freeze or zone thaw
+
+Knot DNS 2.9.7 (2020-10-09)
+===========================
+
+Bugfixes:
+---------
+ - NSEC3 re-salt can cause server crash due to possible zone inconsistencies
+ - Zone reload logs 'invalid parameter' if zone file not changed
+ - Outgoing multi-message transfer can contain invalid compression pointers under specific conditions
+ - Improper handling of file descriptors in libdnssec
+
+Improvements:
+-------------
+ - Module noudp has new configuration option for UDP truncation rate
+
+Knot DNS 2.9.6 (2020-08-31)
+===========================
+
+Features:
+---------
+ - New kdig option '+[no]opttext' to print unknown EDNS options as text if possible (Thanks to Robert Edmonds)
+
+Improvements:
+-------------
+ - Better error message if no key is ready for submission
+ - Improved logging when master is not usable
+ - Improved control logging of zone-flush errors if output directory is specified
+ - More precise system error messages when a zone transfer fails
+ - Some documentation improvements (especially Offline KSK)
+
+Bugfixes:
+---------
+ - In the case of many zones, control operations over all zones take lots of memory
+ - Misleading error message on keymgr import-bind #683
+ - DS push is triggered upon every zone change even though CDS wasn't changed
+ - Kzonecheck performance penalty with passive keys #688
+ - CSK->KSK+ZSK scheme rollover can end too early
+
+Knot DNS 2.9.5 (2020-05-25)
+===========================
+
+Bugfixes:
+---------
+ - Old ZSK can be withdrawn too early during a ZSK rollover if maximum zone TTL
+ is computed automatically
+ - Server responds SERVFAIL to ANY queries on empty non-terminal nodes
+
+Improvements:
+-------------
+ - Also module onlinesign returns minimized responses to ANY queries
+ - Linking against libcap-ng can be disabled via a configure option
+
+Knot DNS 2.9.4 (2020-05-05)
+===========================
+
+Improvements:
+-------------
+ - ANY query over UDP is always answered with one RRSet + possible RRSIG instead
+ of truncated reply
+ - Server tries to resolve CNAME record generated by geoip module (Thanks to Conrad Hoffmann)
+ - Earlier OCSP validity check in kdig certificate verification (Thanks to Alexander Schultz)
+ - Module onlinesign allows KSK + ZSK mode
+ - Server control listen backlog limit was increased to 5
+ - Zone signing event is always re-scheduled even after a signing error
+ - Extended error checks and tiny enhancements in kjournalprint
+ - kdig logs a more detailed error message when failed to acquire a remote address
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - Server can crash when zone update fails due to exceeded zone size limit
+ - keymgr 'share' command doesn't work
+ - Shared KSK doesn't work with an initial key
+ - Self-created RRSIGs are still cryptographically verified in some unnecessary cases
+ - Changed NSEC3PARAM not correctly detected during zone update
+ - NSEC(3) chain not fixed if affected by zone update
+ - knotc orphan purge doesn't work on journal
+ - Online signing configured along with DNSSEC signing can cause MDB_BAD_RSLOT
+ error during server reload
+ - Zone journal access can stuck if mismanaged zone serial
+ - Concurrently added and removed same records in a DDNS message are not properly handled
+ - Zone check logs error instead of warning after a first error occurred
+
+Knot DNS 2.9.3 (2020-03-03)
+===========================
+
+Features:
+---------
+ - New configuration option 'remote.block-notify-after-transfer' to suppress
+ sending NOTIFY messages
+ - Enabled testing support for Ed448 DNSSEC algorithm (requires GnuTLS 3.6.12+
+ and not-yet-released Nettle 3.6+)
+ - New keymgr parameter 'local-serial' for getting/setting signed zone SOA serial
+ in the KASP database
+ - keymgr can import Ed25519 and Ed448 keys in the BIND format (Thanks to Conrad Hoffmann)
+
+Improvements:
+-------------
+ - kdig returns error if the query name is invalid
+ - Increased 'server.tcp-io-timeout' default value to 500 ms
+ - Decreased 'database.journal-db-max-size' default value to 512 MiB on 32-bit systems
+ - Server no longer falls back to AXFR if master is outdated during zone refresh
+ - Some documentation improvements (including new EPUB format and compatibility
+ with Ultra Electronics CIS Keyper Plus HSM)
+ - Some packaging improvements (including new python3-libknot deb package)
+
+Bugfixes:
+---------
+ - Outgoing IXFR can be malformed if the message size has specific size
+ - Server can crash if the zone contains solo NSEC3 record
+ - Improved compatibility with older journal format
+ - Incorrect SOA TTL in negative answers — SOA minimum not considered
+ - Cannot unset uppercase nodes via control interface #668
+ - Module RRL doesn't set AA flag and NOERROR rcode in slipped responses
+ - Server returns FORMERR instead of NOTIMP if empty QUESTION and unknown OPCODE
+
+Knot DNS 2.9.2 (2019-12-12)
+===========================
+
+Improvements:
+-------------
+ - Tiny ds-check log message rewording
+ - Some unnecessary code cleanup
+
+Bugfixes:
+---------
+ - ds-push doesn't replace the DS RRset on the parent #661
+ - Server gets stuck in a never-ending logging loop when changing SOA TTL
+ - Server can crash when the journal database size limit is reached
+ - Server can create a bogus changeset with equal serials from and to
+ - Unreasonable re-signing of the NSEC3PARAM record when reloading the zone
+ and 'zonefile-load: difference-no-serial' is configured
+ - SOA RRSIG not updated if the only changed record is SOA
+ - Failed to remove NSEC3 records through the control interface #666
+ - Failed to stop the server if a zone transaction is active
+
+Knot DNS 2.9.1 (2019-11-11)
+===========================
+
+Features:
+---------
+ - New option for OCSP stapling '+[no]tls-ocsp-stapling[=H]' in kdig (Thanks to Alexander Schultz)
+
+Improvements:
+-------------
+ - Kdig always randomizes source TCP port on recent Linux #575
+ - Server no longer warns about disabled zone file synchronization during shutdown
+ - Zone loading stops if failed to load zone from the journal
+ - Speed-up of insertion to big RRSets
+ - Various code and documentation improvements
+
+Bugfixes:
+---------
+ - Failed to apply journal changes after upgrade #659
+ - Failed to finish zone loading if journal changeset serials from and to are equal
+ - Incorrect handling of 0 value for 'tcp-io-timeout' and 'tcp-remote-io-timeout' configuration
+ - Server can crash if zone transaction is open during zone update
+ - NSEC3 chain not fully updated if NSEC3 salt changes during zone update
+ - Server can crash when flushing zone to a specified directory
+ - Server can respond incorrect NSEC3 records after NSEC3 salt change
+ - Delegation glue records not updated after specific zone change
+
+Knot DNS 2.9.0 (2019-10-10)
+===========================
+
+Features:
+---------
+ - Full support for different master/slave serial arithmetics when on-slave signing
+ - Module geoip newly supports wildcard records #650
+ - New DNSSEC policy configuration option 'rrsig-pre-refresh' for reducing
+ frequency of the zone signing event
+ - New server configuration option 'tcp-reuseport' for setting SO_REUSEPORT(_LB)
+ mode on TCP sockets
+ - New server configuration option 'tcp-io-timeout' [ms] for restricting inbound
+ IO operations over TCP #474
+
+Improvements:
+-------------
+ - Significant speed-up of zone contents modifications
+ - Avoided double zone signing during CSK rollovers
+ - Self-created RRSIGs are not cryptographically verified if not necessary
+ - Zone journal can store two changesets if zone file difference computing
+ and DNSSEC signing are enabled. The first one containing the difference of
+ zone history needed by slave servers, the second one containing the difference
+ between zone file and zone needed for server restart
+ - Universal and more robust memory clearing
+ - More precise socket timeout handling
+ - New notice log message for configuration changes requiring server restart
+ - Module RRL logs both trigger source address and affected subnet
+ - Various code (especially zone and TCP processing) and documentation improvements
+
+Bugfixes:
+---------
+ - RRSIGs are wrongly checked for inconsistent RRSet TTLs during zone update
+ - DS check/push warnings after disabled DNSSEC signing
+ - NSEC3 records not accessible through control interface
+ - Module geoip doesn't accept underscore character in dname specification #655
+
+Compatibility:
+--------------
+ - Removed runtime reconfiguration of network workers and interfaces since
+ it was imperfect and also couldn't work after dropped process privileges
+ - Removed inaccurate and misleading knotc command 'zone-memstats' because
+ memory consumption varies during zone modifications or transfers
+ - Removed useless 'zone.request-edns-option' configuration option
+ - Reimplemented DNS Cookies to be interoperable (based on draft-ietf-dnsop-server-cookies
+ and work by Witold Kręcicki)
+ - Default limit on TCP clients is auto-configured to one half of the file
+ descriptor limit for the server process
+ - Number of open files limit is set to 1048576 in upstream packages
+ - Default number of TCP workers is equal to the number of online CPUs or at least 10
+ - Default EDNS buffer size is 1232 for both IPv4 and IPv6
+ - Removed 'tcp-handshake-timeout' server configuration option
+ - Some configuration options were renamed and possibly moved. Old names will
+ be supported at least until next major release:
+ - 'server.tcp-reply-timeout' [s] to 'server.tcp-remote-io-timeout' [ms]
+ - 'server.max-tcp-clients' to 'server.tcp-max-clients'
+ - 'server.max-udp-payload' to 'server.udp-max-payload'
+ - 'server.max-ipv4-udp-payload' to 'server.udp-max-payload-ipv4'
+ - 'server.max-ipv6-udp-payload' to 'server.udp-max-payload-ipv6'
+ - 'template.journal-db' to 'database.journal-db'
+ - 'template.journal-db-mode' to 'database.journal-db-mode'
+ - 'template.max-journal-db-size' to 'database.journal-db-max-size'
+ - 'template.kasp-db' to 'database.kasp-db'
+ - 'template.max-kasp-db-size' to 'database.kasp-db-max-size'
+ - 'template.timer-db' to 'database.timer-db'
+ - 'template.max-timer-db-size' to 'database.timer-db-max-size'
+ - 'zone.max-journal-usage' to 'zone.journal-max-usage'
+ - 'zone.max-journal-depth' to 'zone.journal-max-depth'
+ - 'zone.max-zone-size' to 'zone.zone-max-size'
+ - 'zone.max-refresh-interval' to 'zone.refresh-max-interval'
+ - 'zone.min-refresh-interval' to 'zone.refresh-min-interval'
+
+Knot DNS 2.8.5 (2020-01-01)
+===========================
+
+Improvements:
+-------------
+ - Tiny ds-check log message rewording
+ - Various code and documentation improvements
+
+Bugfixes:
+---------
+ - RRSIGs are wrongly checked for inconsistent RRSet TTLs during zone update
+ - Server can crash when flushing zone to a specified directory
+ - ds-push doesn't replace the DS RRset on the parent #661
+ - Server gets stuck in a never-ending logging loop when changing SOA TTL
+ - Server can crash when the journal database size limit is reached
+ - Server can create a bogus changeset with equal serials from and to
+ - Server returns FORMERR instead of NOTIMP if empty QUESTION and unknown OPCODE
+
+Knot DNS 2.8.4 (2019-09-24)
+===========================
+
+Features:
+---------
+ - Automatic uploading of DS records to parent zone using DDNS,
+ see 'policy.ds-push' configuration option
+
+Improvements:
+-------------
+ - Incoming IXFR no longer falls back to AXFR if connection error #642
+ - More accurate semantic checks for missing glue records
+ - Various code and documentation improvements
+
+Bugfixes:
+---------
+ - Failed to read/export configuration if 'acl.update-type' is set #651
+ - Failed to generate initial zero-length salt
+ - Missing error log for invalid rrtype input to dynamic configuration #652
+ - Missing error log when AXFR processing fails to store zone data
+ - Redundant notice log about unavailable persistent configuration DB
+ - Zone not flushed after retransfer if SOA serial not changed
+ - Zone contents not properly fixed during zone transfers
+ - No changeset created for updated rrset's TTL if changed by RR addition
+
+Knot DNS 2.8.3 (2019-07-16)
+===========================
+
+Features:
+---------
+ - Added cert/key file configuration for TLS in kdig (Thanks to Alexander Schultz)
+
+Improvements:
+-------------
+ - More verbose log message for offline-KSK signing
+ - Module RRL logs affected source address subnet instead of only one source address
+ - Extended DNSSEC policy configuration checks
+ - Various improvements in the documentation
+
+Bugfixes:
+---------
+ - Excessive server load when maximum TCP clients limit is reached
+ - Incorrect reply after zone update with a node changed from non-authoritative to delegation
+ - Wrong error line number in a config file if it contains leading tab character
+ - Config file error message contains unrelated parsing context
+ - NSEC3 salt not updated when reconfigured to zero length
+ - Kjournalprint sometimes prints a random value for per-zone occupation
+ - Missing debug log for failed zone refresh triggered by zone notification
+ - DS check not scheduled when reconfigured
+ - Broken unit test on NetBSD 8.x
+
+Knot DNS 2.8.2 (2019-06-05)
+===========================
+
+Features:
+---------
+ - New blocking mode for zone event triggers in knotc
+ - New weighted records mode in the module geoip (Thanks to Conrad Hoffmann)
+ - Module noudp allows UDP allow rate configuration
+
+Improvements:
+-------------
+ - NSEC3 salt lifetime can be set to infinity
+ - New 'running' zone event status in the knotc output
+ - Knotc in the forced mode returns failure also if zone check emits any warning
+ - Ignoring PMTU information for IPv4/UDP via IP_PMTUDISC_OMIT (Thanks to Daisuke Higashi)
+ - Various improvements in the documentation
+
+Bugfixes:
+---------
+ - Broken setting of CPU affinity for UDP workers
+ - Unexpected results with the geoip subnet mode
+ - Sometimes insufficient zone adjusting
+ - Incoherent DNSKEY RRSIG lifetimes in SKR
+ - Confusing output from keymgr if an error occurs during KSR generation
+ - Non-functional changeset history depth limitation in kjournalprint
+ - Wrong processing of multiple $INCLUDE directives #646
+
+Knot DNS 2.8.1 (2019-04-09)
+===========================
+
+Improvements:
+-------------
+ - Possible zone transaction is aborted by zone events to avoid inconsistency
+ - Added log message if no persistent config DB is available during 'conf-begin'
+ - New environment setting 'KNOT_VERSION_FORMAT=release' for extended version suppression
+ - Various improvements in the documentation
+
+Bugfixes:
+---------
+ - Broken NSEC3-wildcard-nonexistence proof after NSEC3 re-salt
+ - Glue records under delegation are sometimes signed
+ - RRL doesn't work correctly on big-endian architectures
+ - NSEC3 not re-salted during AXFR refresh
+ - Failed to sign new zone contents if added dynamically #641
+ - NSEC3 opt-out signing doesn't work in some cases
+ - Broken NSEC3 chain after adding new sub-delegations
+ - Redundant SOA RRSIG on slave if RRSIG TTL changed on master
+ - Sometimes confusing log error message for NOTIFY event
+ - Improper include for LMDB #638
+
+Knot DNS 2.8.0 (2019-03-05)
+===========================
+
+Features:
+---------
+ - New offline-KSK mode of operation
+ - Configurable multithreaded DNSSEC signing for large zones
+ - Extended ACL configuration for dynamic updates
+ - New knotc trigger 'zone-key-rollover' for immediate DNSKEY rollover
+ - Added support for OPENPGPKEY, CSYNC, SMIMEA, and ZONEMD RR types
+ - New 'double-ds' option for CDS/CDNSKEY publication
+
+Improvements:
+-------------
+ - Significant speed-up of zone updates
+ - Knotc supports force option in the interactive mode
+ - Copy-on-write support for QP-trie (Thanks to Tony Finch)
+ - Unified and more efficient LMDB layer for journal, timer, and KASP databases
+ - DS check event is re-planned according to KASP even when purged timers
+ - Module DNS Cookies supports explicit Server Secret configuration
+ - Zone mtime is verified against full-precision timestamp (Thanks to Daniel Kahn Gillmor)
+ - Extended logging (loaded SOA serials, refresh duration, tiny cleanup)
+ - Relaxed fixed-length condition for DNSSEC key ID
+ - Extended semantic checks for DNAME and NS RR types
+ - Added support for FreeBSD's SO_REUSEPORT_LB
+ - Improved performance of geoip module
+ - Various improvements in the documentation
+
+Compatibility:
+--------------
+ - Changed configuration default for 'cds-cdnskey-publish' to 'rollover'
+ - Journal DB format changes are not downgrade-compatible
+ - Keymgr no longer prints DS for algorithm SHA-1
+
+Knot DNS 2.7.8 (2019-07-16)
+===========================
+
+Improvements:
+-------------
+ - Various improvements in the documentation
+
+Bugfixes:
+---------
+ - Excessive server load when maximum TCP clients limit is reached
+ - Incorrect reply after zone update with a node changed from non-authoritative to delegation
+ - Missing debug log for failed zone refresh triggered by zone notification
+ - Wrong processing of multiple $INCLUDE directives #646
+ - Broken unit test on NetBSD 8.x
+
+Knot DNS 2.7.7 (2019-04-15)
+===========================
+
+Improvements:
+-------------
+ - Possible zone transaction is aborted by zone events to avoid inconsistency
+ - Added log message if no persistent config DB is available during 'conf-begin'
+ - Tiny building improvements
+
+Bugfixes:
+---------
+ - Glue records under delegation are sometimes signed
+ - NSEC3 not re-salted during AXFR refresh
+ - Broken NSEC3 chain after adding new sub-delegations
+ - Failed to sign new zone contents if added dynamically #641
+ - NSEC3 opt-out signing doesn't work in some cases
+ - Redundant SOA RRSIG on slave if RRSIG TTL changed on master
+ - Sometimes confusing log error message for NOTIFY event
+ - Failed to explicit set value 0 for submission timeout
+
+Knot DNS 2.7.6 (2019-01-23)
+===========================
+
+Improvements:
+-------------
+ - Zone status also shows when the zone load is scheduled
+ - Server workers status also shows background workers utilization
+ - Default control timeout for knotc was increased to 10 seconds
+ - Pkg-config files contain auxiliary variable with library filename
+
+Bugfixes:
+---------
+ - Configuration commit or server reload can drop some pending zone events
+ - Nonempty zone journal is created even though it's disabled #635
+ - Zone is completely re-signed during empty dynamic update processing
+ - Server can crash when storing a big zone difference to the journal
+ - Failed to link on FreeBSD 12 with Clang
+
+Knot DNS 2.7.5 (2019-01-07)
+===========================
+
+Features:
+---------
+ - Keymgr supports NSEC3 salt handling
+
+Improvements:
+-------------
+ - Zone history in journal is dropped apon AXFR-like zone update
+ - Libdnssec is no longer linked against libm #628
+ - Libdnssec is explicitly linked against libpthread if PKCS #11 enabled #629
+ - Better support for libknot packaging in Python
+ - Manually generated KSK is 'ready' by default
+ - Kdig supports '+timeout' as an alias for '+time'
+ - Kdig supports '+nocomments' option
+ - Kdig no longer prints empty lines between retries
+ - Kdig returns failure if operations not successfully resolved #632
+ - Fixed repeating of the 'KSK submission, waiting for confirmation' log
+ - Various improvements in documentation, Dockerfile, and tests
+
+Bugfixes:
+---------
+ - Knotc fails to unset huge configuration section
+ - Kjournalprint sometimes fails to display zone journal content
+ - Improper timing of ZSK removal during ZSK rollover
+ - Missing UTC time zone indication in the 'iso' keymgr list output
+ - A race condition in the online signing module
+
+Knot DNS 2.7.4 (2018-11-13)
+===========================
+
+Features:
+---------
+ - Added SNI configuration for TLS in kdig (Thanks to Alexander Schultz)
+
+Improvements:
+-------------
+ - Added warning log when DNSSEC events not successfully scheduled
+ - New semantic check on timer values in keymgr
+ - DS query no longer asks other addresses if got a negative answer
+ - Reintroduced 'rollover' configuration option for CDS/CDNSKEY publication
+ - Extended logging for zone loading
+ - Various documentation improvements
+
+Bugfixes:
+---------
+ - Failed to import module configuration #613
+ - Improper Cflags value in libknot.pc if built with embedded LMDB #615
+ - IXFR doesn't fall back to AXFR if malformed reply
+ - DNSSEC events not correctly scheduled for empty zone updates
+ - During algorithm rollover old keys get removed before DS TTL expires #617
+ - Maximum zone's RRSIG TTL not considered during algorithm rollover #620
+
+Knot DNS 2.7.3 (2018-10-11)
+===========================
+
+Features:
+---------
+ - New queryacl module for query access control
+ - Configurable answer rrset rotation #612
+ - Configurable NSEC bitmap in online signing
+
+Improvements:
+-------------
+ - Better error logging for KASP DB operations #601
+ - Some documentation improvements
+
+Bugfixes:
+---------
+ - Keymgr "list" output doesn't show key size for ECDSA algorithms #602
+ - Failed to link statically with embedded LMDB
+ - Configuration commit causes zone reload for all zones
+ - The statistics module overlooks TSIG record in a request
+ - Improper processing of an AXFR-style-IXFR response consisting of one-record messages
+ - Race condition in online signing during key rollover #600
+ - Server can crash if geoip module is enabled in the geo mode
+
+Knot DNS 2.7.2 (2018-08-29)
+===========================
+
+Improvements:
+-------------
+ - Keymgr list command displays also key size
+ - Kjournalprint displays total occupied size in the debug mode
+ - Server doesn't stop if failed to load a shared module from the module directory
+ - Libraries libcap-ng, pthread, and dl are linked selectively if needed
+
+Bugfixes:
+---------
+ - Sometimes incorrect result from dnssec_nsec_bitmap_contains (libdnssec)
+ - Server can crash when loading zone file difference and zone-in-journal is set
+ - Incorrect treatment of specific queries in the module RRL
+ - Failed to link module Cookies as a shared library
+
+Knot DNS 2.7.1 (2018-08-14)
+===========================
+
+Improvements:
+-------------
+ - Added zone wire size information to zone loading log message
+ - Added debug log message for each unsuccessful remote address operation
+ - Various improvements for packaging
+
+Bugfixes:
+---------
+ - Incompatible handling of RRSIG TTL value when creating a DNS message
+ - Incorrect RRSIG TTL value in zone differences and knotc zone operation outputs
+ - Default configure prefix is ignored
+
+Knot DNS 2.7.0 (2018-08-03)
+===========================
+
+Features:
+---------
+ - New DNS Cookies module and related '+cookie' kdig option
+ - New module for response tailoring according to client's subnet or geographic location
+ - General EDNS Client Subnet support in the server
+ - OSS-Fuzz integration (Thanks to Jonathan Foote)
+ - New '+ednsopt' kdig option (Thanks to Jan Včelák)
+ - Online Signing support for automatic key rollover
+ - Non-normal file (e.g. pipe) loading support in zscanner #542
+ - Automatic SOA serial incrementation if non-empty zone difference
+ - New zone file load option for ignoring zone file's SOA serial
+ - New build-time option for alternative malloc specification
+ - Structured logging for DNSSEC key submission event
+ - Empty QNAME support in kdig
+
+Improvements:
+-------------
+ - Various library and server optimizations
+ - Reduced memory consumption of outgoing IXFR processing
+ - Linux capabilities use overhaul #546 (Thanks to Robert Edmonds)
+ - Online Signing properly signs delegations and CNAME records
+ - CDS/CDNSKEY rrset is signed with KSK instead of ZSK
+ - DNSSEC-related records are ignored when loading zone difference with signing enabled
+ - Minimum allowed RSA key length was increased to 1024
+ - Removed explicit dependency on Nettle
+
+Bugfixes:
+---------
+ - Possible uninitialized address buffer use in zscanner
+ - Possible index overflow during multiline record parsing in zscanner
+ - kdig +tls sometimes consumes 100 % CPU #561
+ - Single-Type Signing doesn't work with single ZSK key #566
+ - Zone not flushed after re-signing during zone load #594
+ - Server crashes when committing empty zone transaction
+ - Incoming IXFR with on-slave signing sometimes leads to memory corruption #595
+
+Compatibility:
+--------------
+ - Removed obsolete RRL configuration
+ - Removed obsolete module names 'mod-online-sign' and 'mod-synth-record'
+ - Removed obsolete 'ixfr-from-differences' configuration option
+ - Removed old journal migration
+ - Removed module rosedb
+
+Knot DNS 2.6.9 (2018-08-14)
+===========================
+
+Improvements:
+-------------
+ - Added zone wire size to zone loading log message
+ - Added debug log message for each unsuccessful remote address operation
+
+Bugfixes:
+---------
+ - Zone not flushed after re-signing during zone load #594
+ - Server crashes when committing empty zone transaction
+ - Incoming IXFR with on-slave signing sometimes leads to memory corruption #595
+
+Knot DNS 2.6.8 (2018-07-10)
+===========================
+
+Features:
+---------
+ - New 'import-pkcs11' command in keymgr
+
+Improvements:
+-------------
+ - Unixtime serial policy mimics Bind – increment if lower #593
+
+Bugfixes:
+---------
+ - Creeping memory consumption upon server reload #584
+ - Kdig incorrectly detects QNAME if 'notify' is a prefix
+ - Server crashes when zone sign fails #587
+ - CSK->KZSK rollover retires CSK early #588
+ - Server crashes when zone expires during outgoing multi-message transfer
+ - Kjournalprint doesn't convert zone name argument to lower-case
+ - Cannot switch to a previously used ksk-shared dnssec policy #589
+
+Knot DNS 2.6.7 (2018-05-17)
+===========================
+
+Features:
+---------
+ - Added 'dateserial' (YYYYMMDDnn) serial policy configuration (Thanks to Wolfgang Jung)
+
+Improvements:
+-------------
+ - Trailing data indication from the packet parser (libknot)
+ - Better configuration check for a problematical option combination
+
+Bugfixes:
+---------
+ - Incomplete configuration option item name check
+ - Possible buffer overflow in 'knot_dname_to_str' (libknot)
+ - Module dnsproxy doesn't preserve letter case of QNAME
+ - Module dnsproxy duplicates OPT and TSIG in the non-fallback mode
+
+Knot DNS 2.6.6 (2018-04-11)
+===========================
+
+Features:
+---------
+ - New EDNS option counters in the statistics module
+ - New '+orphan' filter for the 'zone-purge' operation
+
+Improvements:
+-------------
+ - Reduced memory consumption of disabled statistics metrics
+ - Some spelling fixes (Thanks to Daniel Kahn Gillmor)
+ - Server no longer fails to start if MODULE_DIR doesn't exist
+ - Configuration include doesn't fail if empty wildcard match
+ - Added a configuration check for a problematical option combination
+
+Bugfixes:
+---------
+ - NSEC3 chain not re-created when SOA minimum TTL changed
+ - Failed to start server if no template is configured
+ - Possibly incorrect SOA serial upon changed zone reload with DNSSEC signing
+ - Inaccurate outgoing zone transfer size in the log message
+ - Invalid dname compression if empty question section
+ - Missing EDNS in EMALF responses
+
+Knot DNS 2.6.5 (2018-02-12)
+===========================
+
+Features:
+---------
+ - New 'zone-notify' command in knotc
+ - Kdig uses '@server' as a hostname for TLS authentication if '+tls-ca' is set
+
+Improvements:
+-------------
+ - Better heap memory trimming for zone operations
+ - Added proper polling for TLS operations in kdig
+ - Configuration export uses stdout as a default output
+ - Simplified detection of atomic operations
+ - Added '--disable-modules' configure option
+ - Small documentation updates
+
+Bugfixes:
+---------
+ - Zone retransfer doesn't work well if more masters configured
+ - Kdig can leak or double free memory in corner cases
+ - Inconsistent error outputs from dynamic configuration operations
+ - Failed to generate documentation on OpenBSD
+
+Knot DNS 2.6.4 (2018-01-02)
+===========================
+
+Features:
+---------
+ - Module synthrecord allows multiple 'network' specification
+ - New CSK handling support in keymgr
+
+Improvements:
+-------------
+ - Allowed configuration for infinite zsk lifetime
+ - Increased performance and security of the module synthrecord
+ - Signing changeset is stored into journal even if 'zonefile-load' is whole
+
+Bugfixes:
+---------
+ - Unintentional zone re-sign during reload if empty NSEC3 salt
+ - Inconsistent zone names in journald structured logs
+ - Malformed outgoing transfer for big zone with TSIG
+ - Some minor DNSSEC-related issues
+
+Knot DNS 2.6.3 (2017-11-24)
+===========================
+
+Bugfixes:
+---------
+ - Wrong detection of signing scheme rollover
+
+Knot DNS 2.6.2 (2017-11-23)
+===========================
+
+Features:
+---------
+ - CSK algorithm rollover and (KSK, ZSK) <-> CSK rollover support
+
+Improvements:
+-------------
+ - Allowed explicit configuration for infinite ksk lifetime
+ - Proper error messages instead of unclear error codes in server log
+ - Better support for old compilers
+
+Bugfixes:
+---------
+ - Unexpected reply for DS query with an owner below a delegation point
+ - Old dependencies in the pkg-config file
+
+Knot DNS 2.6.1 (2017-11-02)
+===========================
+
+Features:
+---------
+ - NSEC3 Opt-Out support in the DNSSEC signing
+ - New CDS/CDNSKEY publish configuration option
+
+Improvements:
+-------------
+ - Simplified DNSSEC log message with DNSKEY details
+ - +tls-hostname in kdig implies +tls-ca if neither +tls-ca nor +tls-pin is given
+ - New documentation sections for DNSSEC key rollovers and shared keys
+ - Keymgr no longer prints useless algorithm number for generated key
+ - Kdig prints unknown RCODE in a numeric format
+ - Better support for LLVM libFuzzer
+
+Bugfixes:
+---------
+ - Faulty DNAME semantic check if present in the zone apex and NSEC3 is used
+ - Immediate zone flush not scheduled during the zone load event
+ - Server crashes upon dynamic zone addition if a query module is loaded
+ - Kdig fails to connect over TLS due to SNI is set to server IP address
+ - Possible out-of-bounds memory access at the end of the input
+ - TCP Fast Open enabled by default in kdig breaks TLS connection
+
+Knot DNS 2.6.0 (2017-09-29)
+===========================
+
+Features:
+---------
+ - On-slave (inline) signing support
+ - Automatic DNSSEC key algorithm rollover
+ - Ed25519 algorithm support in DNSSEC (requires GnuTLS 3.6.0)
+ - New 'journal-content' and 'zonefile-load' configuration options
+ - keymgr tries to run as user/group set in the configuration
+ - Public-only DNSSEC key import into KASP DB via keymgr
+ - NSEC3 resalt and parent DS query events are persistent in timer DB
+ - New processing state for a response suppression within a query module
+ - Enabled server side TCP Fast Open if supported
+ - TCP Fast Open support in kdig
+
+Improvements:
+-------------
+ - Better record owner compression if related to the previous rdata dname
+ - NSEC(3) chain is no longer recomputed whole on every update
+ - Remove inconsistent and unnecessary quoting in log files
+ - Avoiding of overlapping key rollovers at a time
+ - More DNSSEC-related semantic checks
+ - Extended timestamp format in keymgr
+
+Bugfixes:
+---------
+ - Incorrect journal free space computation causing inefficient space handling
+ - Interface-automatic broken on Linux in the presence of asymmetric routing
+
+Knot DNS 2.5.7 (2018-01-02)
+===========================
+
+Bugfixes:
+---------
+ - Unintentional zone re-sign during reload if empty NSEC3 salt
+ - Inconsistent zone names in journald structured logs
+ - Malformed outgoing transfer for big zone with TSIG
+ - Unexpected reply for DS query with an owner below a delegation point
+ - Old dependencies in the pkg-config file
+
+Knot DNS 2.5.6 (2017-11-02)
+===========================
+
+Improvements:
+-------------
+ - Keymgr no longer prints useless algorithm number for generated key
+
+Bugfixes:
+---------
+ - Faulty DNAME semantic check if present in the zone apex and NSEC3 is used
+ - Immediate zone flush not scheduled during the zone load event
+ - Server crashes upon dynamic zone addition if a query module is loaded
+ - Kdig fails to connect over TLS due to SNI is set to server IP address
+
+Knot DNS 2.5.5 (2017-09-29)
+===========================
+
+Improvements:
+-------------
+ - Constant time memory comparison in the TSIG processing
+ - Proper use of the ctype functions
+ - Generated RRSIG records have inception time 90 minutes in the past
+
+Bugfixes:
+---------
+ - Incorrect online signature for NSEC in the case of a CNAME record
+ - Incorrect timestamps in dnstap records
+ - EDNS Subnet Client validation rejects valid payloads
+ - Module configuration semantic checks are not executed
+ - Kzonecheck segfaults with unusual inputs
+
+Knot DNS 2.5.4 (2017-08-31)
+===========================
+
+Improvements:
+-------------
+ - New minimum and maximum refresh interval config options (Thanks to Manabu Sonoda)
+ - New warning when unforced flush with disabled zone file synchronization
+ - New 'dnskey' keymgr command
+ - Linking with libatomic on architectures that require it (Thanks to Pierre-Olivier Mercier)
+ - Removed 'OK' from listing keymgr command outputs
+ - Extended journal and keymgr documentation and logging
+
+Bugfixes:
+---------
+ - Incorrect handling of specific corner-cases with zone-in-journal
+ - The 'share' keymgr command doesn't work
+ - Server crashes if configured with query-size and reply-size statistics options
+ - Malformed big integer configuration values on some 32-bit platforms
+ - Keymgr uses local time when parsing date inputs
+ - Memory leak in kdig upon IXFR query
+
+Knot DNS 2.5.3 (2017-07-14)
+===========================
+
+Features:
+---------
+ - CSK rollover support for Single-Type Signing Scheme
+
+Improvements:
+-------------
+ - Allowed binding to non-local addresses for TCP (Thanks to Julian Brost!)
+ - New documentation section for manual DNSSEC key algorithm rollover
+ - Initial KSK also generated in the submission state
+ - The 'ds' keymgr command with no parameter uses all KSK keys
+ - New debug mode in kjournalprint
+ - Updated keymgr documentation
+
+Bugfixes:
+---------
+ - Sometimes missing RRSIG by KSK in submission state.
+ - Minor DNSSEC-related issues
+
+Knot DNS 2.5.2 (2017-06-23)
+===========================
+
+Security:
+---------
+ - CVE-2017-11104: Improper TSIG validity period check can allow TSIG forgery (Thanks to Synacktiv!)
+
+Improvements:
+-------------
+ - Extended debug logging for TSIG errors
+ - Better error message for unknown module section in the configuration
+ - Module documentation compilation no longer depends on module configuration
+ - Extended policy section configuration semantic checks
+ - Improved python version compatibility in pykeymgr
+ - Extended migration section in the documentation
+ - Improved DNSSEC event timing on 32-bit systems
+ - New KSK rollover start log info message
+ - NULL qtype support in kdig
+
+Bugfixes:
+---------
+ - Failed to process included configuration
+ - dnskey_ttl policy option in the configuration has no effect on DNSKEY TTL
+ - Corner case journal fixes (huge changesets, OpenWRT operation)
+ - Confusing event timestamps in knotc zone-status output
+ - NSEC/NSEC3 bitmap not updated for CDS/CDNSKEY
+ - CDS/CDNSKEY RRSIG not updated
+
+Knot DNS 2.5.1 (2017-06-07)
+===========================
+
+Bugfixes:
+---------
+ - pykeymgr no longer crash on empty json files in the KASP DB directory
+ - pykeymgr no longer imports keys in the "removed" state
+ - Imported keys in the "removed" state no longer makes knotd to crash
+ - Including an empty configuration directory no longer makes knotd to crash
+ - pykeymgr is distributed and installed to the distribution tarball
+
+Knot DNS 2.5.0 (2017-06-05)
+===========================
+
+Features:
+---------
+ - KASP database switched from JSON files to LMDB database
+ - KSK rollover support using CDNSKEY and CDS in the automatic DNSSEC signing
+ - Dynamic module loading support with proper module API
+ - Journal can store full zone contents (not only differences)
+ - Zone freeze/thaw support
+ - Updated knotc zone-status output with optional column filters
+ - New '[no]crypto' option in kdig
+ - New keymgr implementation reflecting KASP database changes
+ - New pykeymgr for JSON-based KASP database migration
+ - Removed obsolete knot1to2 utility
+
+Improvements:
+-------------
+ - Added libidn2 support to kdig (with libidn fallback)
+ - Maximum timer database switched from configure to the server configuration
+
+Knot DNS 2.4.4 (2017-06-05)
+===========================
+
+Improvements:
+-------------
+ - Improved error handling in kjournalprint
+
+Bugfixes:
+---------
+ - Zone flush not replanned upon unsuccessful flush
+ - Journal inconsistency after deleting deleted zone
+ - Zone events not rescheduled upon server reload (Thanks to Mark Warren)
+ - Unreliable LMDB mapsize detection in kjournalprint
+ - Some minor issues found by AddressSanitizer
+
+Knot DNS 2.4.3 (2017-04-11)
+===========================
+
+Improvements:
+-------------
+ - New 'journal-db-mode' optimization configuration option
+ - The default TSIG algorithm for utilities input is HMAC-SHA256
+ - Implemented sensible default EDNS(0) padding policy (Thanks to D. K. Gillmor)
+ - Added some more semantic checks on the knotc configuration operations
+
+Bugfixes:
+---------
+ - Missing 'zone' keyword in the YAML output
+ - Missing trailing dot in the keymgr DS owner output
+ - Journal logs 'invalid parameter' in several cases
+ - Some minor journal-related problems
+
+Knot DNS 2.4.2 (2017-03-23)
+===========================
+
+Features:
+---------
+ - Zscanner can store record comments placed on the same line
+ - Knotc status extension with version, configure, and workers parameters
+
+Improvements:
+-------------
+ - Significant incoming XFR speed-up in the case of many zones
+
+Bugfixes:
+---------
+ - Double OPT RR insertion when a global module returns KNOT_STATE_FAIL
+ - User-driven zscanner parsing logic inconsistency
+ - Lower serial at master doesn't trigger any errors
+ - Queries with too long DNAME substitution do not return YXDOMAIN response
+ - Incorrect elapsed time in the DDNS log
+ - Failed to process forwarded DDNS request with TSIG
+
+Knot DNS 2.4.1 (2017-02-10)
+===========================
+
+Improvements:
+-------------
+ - Speed-up of rdata addition into a huge rrset
+ - Introduce check of minimum timeout for next refresh
+ - Dnsproxy module can forward all queries without local resolving
+
+Bugfixes:
+--------
+ - Transfer of a huge rrset goes into an infinite loop
+ - Huge response over TCP contains useless TC bit instead of SERVFAIL
+ - Failed to build utilities with disabled daemon
+ - Memory leaks during keys removal
+ - Rough TSIG packet reservation causes early truncation
+ - Minor out-of-bounds string termination write in rrset dump
+ - Server crash during stop if failed to open timers DB
+ - Failed to compile on OS X older than Sierra
+ - Poor minimum UDP-max-size configuration check
+ - Failed to receive one-record-per-message IXFR-style AXFR
+ - Kdig timeouts when receiving RCODE != NOERROR on subsequent transfer message
+
+Knot DNS 2.4.0 (2017-01-18)
+===========================
+
+Bugfixes:
+--------
+ - False positive semantic-check warning about invalid bitmap in NSEC
+ - Unnecessary SOA queries upon notify with up to date serial
+ - Timers for expired zones are reset on reload
+ - Zone doesn't expire when the server is down
+ - Failed to handle keys with duplicate keytags
+ - Per zone module and global module inconsistency
+ - Obsolete online signing module configuration
+ - Malformed output from kjournalprint
+ - Redundant SO_REUSEPORT activation on the TCP socket
+ - Failed to use higher number of background workers
+
+Improvements:
+-------------
+ - Lower memory consumption with qp-trie
+ - Zone events and zone timers improvements
+ - Print all zone names in the FQDN format
+ - Simplified query module interface
+ - Shared TCP connection between SOA query and transfer
+ - Response Rate Limiting as a module with statistics support
+ - Key filters in keymgr
+
+Features:
+---------
+ - New unified LMDB-based zone journal
+ - Server statistics support
+ - New statistics module for traffic measuring
+ - Automatic deletion of retired DNSSEC keys
+ - New control logging category
+
+Knot DNS 2.3.4 (2017-11-20)
+===========================
+
+Security:
+---------
+ - CVE-2017-11104: Improper TSIG validity period check can allow TSIG forgery (Thanks to Synacktiv!)
+
+Bugfixes:
+---------
+ - Unexpected response for DS query below delegation poing
+ - Zone events not rescheduled upon server reload (Thanks to Mark Warren)
+ - Missing trailing dot in the keymgr DS owner output
+ - Malformed output from kjournalprint
+ - Redundant SO_REUSEPORT activation on the TCP socket
+
+Knot DNS 2.3.3 (2016-12-08)
+===========================
+
+Bugfixes:
+---------
+ - Double free when failed to apply zone journal
+ - Zone bootstrap retry interval not preserved upon zone reload
+ - DNSSEC related records not flushed if not signed
+ - False semantic checks warning about incorrect type in NSEC bitmap
+ - Memory leak in kzonecheck
+
+Improvements:
+-------------
+ - All zone names are fully-qualified in log
+
+Features:
+---------
+ - New kjournalprint utility
+
+Knot DNS 2.3.2 (2016-11-04)
+===========================
+
+Bugfixes:
+---------
+ - Incorrect %s expansion for the root zone
+ - Failed to refresh not existing slave zone after restart
+ - Immediate zone refresh upon restart if refresh already scheduled
+ - Early zone transfer after restart if transfer already scheduled
+ - Not ignoring empty non-terminal parents during delegation lookup
+ - CD bit preservation in responses
+ - Compilation error on GNU/kFreeBSD
+ - Server crash after double zone-commit if journal error
+
+Improvements:
+-------------
+ - Speed-up of knotc if control operation and known socket
+ - Zone purge operation purges also zone timers
+
+Features:
+---------
+ - Simple modules don't require empty configuration section
+ - New zone journal path configuration option
+ - New timeout configuration option for module dnsproxy
+
+Knot DNS 2.3.1 (2016-10-07)
+===========================
+
+Bugfixes:
+---------
+ - Missing glue records in some responses
+ - Knsupdate prompt printing on non-terminal
+ - Mismatch between configuration policy item names and documentation
+ - Segfault on OS X (Sierra)
+
+Improvements:
+-------------
+ - Significant speed-up of conf-commit and conf-diff operations (in most cases)
+ - New EDNS Client Subnet libknot API
+ - Better semantic-checks error messages
+
+Features:
+---------
+ - Print TLS certificate hierarchy in kdig verbose mode
+ - New +subnet alias for +client
+ - New mod-whoami and mod-noudp modules
+ - New zone-purge control command
+ - New log-queries and log-responses options for mod-dnstap
+
+Knot DNS 2.3.0 (2016-08-09)
+===========================
+
+Bugfixes:
+---------
+ - No wildcard expansion below empty non-terminal for NSEC signed zone
+ - Avoid multiple loads of the same PKCS #11 module
+ - Fix kdig IXFR response processing if the transfer content is empty
+ - Don't ignore non-existing records to be removed in IXFR
+
+Improvements:
+-------------
+ - Refactored semantic checks and improved error messages
+ - Set TC flag in delegation only if mandatory glue doesn't fit the response
+ - Separate EDNS(0) payload size configuration for IPv4 and IPv6
+
+Features:
+---------
+ - DNSSEC policy can be defined in server configuration
+ - Automatic NSEC3 resalt according to DNSSEC policy
+ - Zone content editing using control interface
+ - Zone size limit restriction for DDNS, AXFR, and IXFR (CVE-2016-6171)
+ - DNS-over-TLS support in kdig (RFC 7858)
+ - EDNS(0) padding and alignment support in kdig (RFC 7830)
+
+Knot DNS 2.2.1 (2016-05-24)
+===========================
+
+Bugfixes:
+---------
+ - Fix separate logging of server and zone events
+ - Fix concurrent zone file flushing with many zones
+ - Fix possible server crash with empty hostname on OpenWRT
+ - Fix control timeout parsing in knotc
+ - Fix "Environment maxreaders limit reached" error in knotc
+ - Don't apply journal changes on modified zone file
+ - Remove broken LTO option from configure script
+ - Enable multiple zone names completion in interactive knotc
+ - Set the TC flag in a response if a glue doesn't fit the response
+ - Disallow server reload when there is an active configuration transaction
+
+Improvements:
+-------------
+ - Distinguish unavailable zones from zones with zero serial in log messages
+ - Log warning and error messages to standard error output in all utilities
+ - Document tested PKCS #11 devices
+ - Extended Python configuration interface
+
+Knot DNS 2.2.0 (2016-04-26)
+===========================
+
+Bugfixes:
+---------
+ - Fix build dependencies on FreeBSD
+ - Fix query/response message type setting in dnstap module
+ - Fix remote address retrieval from dnstap capture in kdig
+ - Fix global modules execution for queries hitting existing zones
+ - Fix execution of semantic checks after an IXFR transfer
+ - Fix PKCS#11 support detection at build time
+ - Fix kdig failure when the first AXFR message contains just the SOA record
+ - Exclude non-authoritative types from NSEC/NSEC3 bitmap at a delegation
+ - Mark PKCS#11 generated keys as sensitive (required by Luna SA)
+ - Fix error when removing the only zone from the server
+ - Don't abort knotc transaction when some check fails
+
+Features:
+---------
+ - URI and CAA resource record types support
+ - RRL client address based white list
+ - knotc interactive mode
+
+Improvements:
+-------------
+ - Consistent IXFR error messages
+ - Various fixes for better compatibility with PKCS#11 devices
+ - Various keymgr user interface improvements
+ - Better zone event scheduler performance with many zones
+ - New server control interface
+ - kdig uses local resolver if resolv.conf is empty
+
+Knot DNS 2.1.1 (2016-02-10)
+===========================
+
+Bugfixes:
+---------
+ - DNSSEC: Allow import of duplicate private key into the KASP
+ - DNSSEC: Avoid duplicate NSEC for Wildcard No Data answer
+ - Fix server crash when an incoming transfer is in progress and reload is issued
+ - Fix socket polling when configured with many interfaces and threads
+ - Fix compilation against Nettle 3.2
+
+Improvements:
+-------------
+ - Select correct source address for UDP messages received on ANY address
+ - Extend documentation of knotc commands
+
+Knot DNS 2.1.0 (2016-01-14)
+===========================
+
+Features:
+---------
+ - Per-thread UDP socket binding using SO_REUSEPORT on Linux
+ - Support for dynamic configuration database
+ - DNSSEC: Support for cryptographic tokens via PKCS #11 interface
+ - DNSSEC: Experimental support for online signing
+
+Improvements:
+-------------
+ - Support for zone file name patterns
+ - Configurable location of zone timer database
+ - Non-blocking network operations and better timeout handling
+ - Caching of Critical configuration values for better performance
+ - Logging of ACL failures
+ - RRL: Add rate-limit-slip zero support to drop all responses
+ - RRL: Document behavior for different rate-limit-slip options
+ - kdig: Warning instead of error on TSIG validation failure
+ - Cleanup of support libraries interfaces (libknot, libzscanner, libdnssec)
+ - Remove possibly insecure server control over a network socket
+ - Remove implementation limit for the number of network interfaces
+
+Bugfixes:
+---------
+ - synth-record module: Fix application of default configuration options
+ - TSIG: Allow compressed TSIG name when forwarding DDNS updates
+ - Schedule zone bootstrap after slave zone fails to load from disk
+
+Knot DNS 2.0.2 (2015-11-24)
+===========================
+
+Bugfixes:
+---------
+ - Out-of-bound read in packet parser for malformed NAPTR records (LibFuzzer)
+
+Knot DNS 2.0.1 (2015-09-02)
+===========================
+
+Bugfixes:
+---------
+ - Do not reload expired zones on 'knotc reload' and server startup
+ - Fix rare race-condition in event scheduling causing delayed event execution
+ - Fix skipping of non-authoritative nodes in NSEC proofs
+ - Fix TC flag setting in RRL slipped answers
+ - Disable domain name compression for root label
+ - Log via journald only when running under systemd
+ - Fix CNAME following when querying for NSEC RR type
+ - Fix refreshing of DNSSEC signatures for zone keys
+ - Fix binding an unavailable IPv6 address on Linux (IP_FREEBIND)
+ - Fix infinite loop in knotc zonestatus and memstats
+ - Fix memory leak in configuration on server shutdown
+ - Fix broken dnsproxy module
+ - Fix DNSSEC KASP timestamps parsing in strict POSIX environment
+ - Fix multi value parsing on big-endian
+ - Adapt to Nettle 3 API break causing base64 decoding failures on big-endian
+
+Features:
+---------
+ - Add 'keymgr zone key ds' to show key's DS record
+ - Add 'keymgr tsig generate' to generate TSIG keys
+ - Add query module scoping to process either all queries or zone queries only
+ - Add support for file name globbing in config file includes
+ - Add 'request-edns-option' config option to add custom EDNS0 option into
+ server initiated queries
+
+Improvements:
+-------------
+ - Send minimal responses (remove NS from Authority section for NOERROR)
+ - Update persistent timers only on shutdown for better performance
+ - Allow change of RR TTL over DDNS
+ - Documentation fixes, updates, and improvements in formatting
+ - Install yparser and zscanner header files
+ - Improve lookup of libsystemd build dependencies
+ - Fix compilation warnings in endian conversion functions on OpenBSD
+
+Knot DNS 2.0.0 (2015-06-26)
+===========================
+
+Bugfixes:
+---------
+ - Fix lost NOTIFY message if received during zone transfer
+ - Disable fast zone parser when compiled in Clang (workaround for Clang bug)
+ - kdig: Record correct dnstap SocketProtocol when retrying over TCP
+ - kdig: Hide TSIG section with +noall
+ - Do not set AA flag for AXFR/IXFR queries
+
+Features:
+---------
+ - DNSSEC: separate library, switch to GnuTLS, new utilities
+ - DNSSEC: basic KASP support (generate initial keys, ZSK rollover)
+ - Configuration: New text format in YAML, binary store in LMDB
+ - Zone parser: Split long TXT/SPF strings into multiple strings
+ - kdig: Add generic dump style option (+generic)
+ - Try all master servers in multi-master environment
+ - Improved remotes and ACLs (multiple addresses, multiple keys)
+ - Basic support for zone file patterns (%s to substitute zone name)
+ - Disable zone file synchronization by setting 'zonefile_sync' to '-1'
+ - knsupdate: Add input prompt in interactive mode and 'quit' command
+ - knsupdate: Allow TSIG algorithm specification in interactive prompt
+
+Improvements:
+-------------
+ - Zone dump: Do not write class for SOA record (unified with other RR types)
+ - Zone dump: Do not write master server address into the zone file
+ - Documentation: Manual pages are included in HTML and PDF
+
+Knot DNS 1.6.3 (2015-04-08)
+===========================
+
+Bugfixes:
+---------
+ - Performance drop for NSEC-signed zones
+ - Proper handling of TCP short-writes
+ - Out-of-bound read in zone parser for long domain names in origin (AFL fuzzer)
+ - Out-of-bound read in packet parser for TSIG RR without RDATA (AFL fuzzer)
+ - Out-of-bound read in packet parser for malformed NAPTR RR (AFL fuzzer)
+
+Features:
+--------
+ - CDS and CDNSKEY support in zone parser
+
+Improvements:
+-------------
+ - Add defaults for TCP config options into documentation
+ - Detailed error message if zone reload fails
+
+Knot DNS 1.6.2 (2015-02-19)
+===========================
+
+Features:
+---------
+ - Limiting number of parallel TCP clients (max-tcp-clients config option)
+
+Bugfixes:
+---------
+ - Ignore refresh and transfer events on non-slave zones
+ - Compilation with Dnstap support on FreeBSD
+ - Possible file descriptor leak when terminating inactive TCP clients
+
+Knot DNS 1.6.1 (2014-12-13)
+===========================
+
+Bugfixes:
+---------
+ - Journal file would sometimes outgrow its limit (ixfr-fslimit in configuration)
+ - Fixed incompatibility with OpenSSL 0.9.8
+ - Proper handling when hostname cannot be retrieved (for NSID and CH)
+
+Features:
+---------
+ - DNSSEC Single Type Signing Scheme is now supported
+
+Knot DNS 1.6.0 (2014-10-23)
+===========================
+
+Bugfixes:
+---------
+ - Fix zone expiration when AXFR/IXFR is being refused by master
+ - Fix forced zone refresh on slave (knotc refresh -f)
+
+Knot DNS 1.6.0-rc2 (2014-10-17)
+===============================
+
+Improvements:
+-------------
+ - Maximal size of persistent timers database increased from 10 MB to 100 MB
+ - Added logging of persistent timers database errors
+
+Bugfixes:
+---------
+ - Persistent timers database opening after privileges has been dropped
+
+Knot DNS 1.6.0-rc1 (2014-10-13)
+===============================
+
+Features:
+---------
+ - Persistent timers for slave zones (expire, refresh, and flush)
+
+Bugfixes:
+---------
+ - DNSSEC: RFC compliant processing of letter case in RDATA domain names
+ - EDNS: Return minimal error response for queries with unsupported version
+ - EDNS: Fix interpretation of Extended RCODE
+
+Knot DNS 1.5.3 (2014-09-15)
+===========================
+
+Bugfixes:
+---------
+ - Some specific incoming IXFRs were causing server to crash
+ - Rare synchronization error during reload caused read-after-free
+ - Response synthetization module did not work properly with DNSSEC-enabled zones
+ - When Knot sent AXFR when IXFR was requested, message ID and opcode were wrong
+ - Knot failed to send large messages to remote control (present since 1.5.1)
+
+Knot DNS 1.5.2 (2014-09-08)
+===========================
+
+Bugfixes:
+---------
+ - Some RR parsing corner cases were not handled properly
+ - AXFR-style IXFR was refused and had to be retransferred
+ - Hash character (#) was not properly escaped when storing text zone file
+
+Knot DNS 1.5.1 (2014-08-19)
+===========================
+
+Features:
+---------
+ - Basic support for logging using systemd journal
+ - DDNS: Ability to process updates in bulk
+
+Improvements:
+-------------
+ - Unified logging messages structure
+ - DNSSEC: More strict controls for signing keys
+
+Bugfixes:
+---------
+ - DNSSEC: DNAMEs in RDATA were not lowercased before signing
+ - EDNS: OPT RR were not put into responsing for some errors
+ - TSIG: DDNS responses were not signed with TSIG
+ - DDNS: Prerequisite checks failed for some inputs
+ - knsupdate: Zone origin was not used for deletions
+
+Knot DNS 1.5.0 (2014-07-08)
+===========================
+
+Features:
+---------
+ - DDNS forwarding reimplemented
+
+Improvements:
+-------------
+ - Transfer sizes logged in bytes if needed
+ - Logging outgoing NOTIFY messages
+ - Logging unauthorized incoming NOTIFYs
+
+Bugfixes:
+---------
+ - Zone flush planning after bootstrap
+ - Incorrect incoming AXFR message sizes
+ - DDNS signing changes were freed too soon, possibility of stale data
+ - knotc remote control key handling
+
+Knot DNS 1.5.0-rc2 (2014-06-18)
+===============================
+
+Features:
+---------
+ - edns-client-subnet support in kdig
+ - Optional asynchronous startup (config "asynchronous-start")
+
+Improvements:
+-------------
+ - Preempt task queue for faster reload
+ - Lazy zone file write after zone transfer (governed by
+ "zonefile-sync")
+
+Bugfixes:
+---------
+ - Close zone transfer after SERVFAIL response
+ - Incremental to full zone transfer fallback, wrong log message
+ - Zone events corner cases, reload replanning
+
+Knot DNS 1.5.0-rc1 (2014-06-03)
+===============================
+
+Features:
+---------
+ - Pluggable query processing modules
+ - Synthetic IPv4/IPv6 reverse/forward records (optional module)
+ - dnstap support in both utilities & server (optional module)
+ - NOTIFY message support and new TSIG section in kdig
+ - Zone transfer master failover
+
+Improvements:
+-------------
+ - Query processing and core functionality overhaul
+ - Performance and reduced memory footprint
+ - Faster zone events scheduling
+ - RFC compliant queries/responses in some corner cases
+ - Log messages
+ - New documentation (Sphinx)
+
+Knot DNS 1.4.2 (2014-01-27)
+===========================
+
+Bugfixes:
+---------
+ - AXFR/IXFR compatibility issues with tinydns/axfrdns
+ - Journal file is created only when needed
+ - Zone-related log messages are logged into correct category
+ - DNSSEC: Refresh signatures earlier (3 days before their expiration
+ with the default signature lifetime)
+ - Fixed RCU synchronization causing deadlock on 'knotc signzone'
+ - RRSIG not fitting in the additional records doesn't cause
+ truncation
+
+Knot DNS 1.4.1 (2014-01-13)
+===========================
+
+Bugfixes:
+---------
+ - Empty APL record support
+ - 'zonestatus' when using immediate zone syncing
+ - Immediate zone syncing after reload
+ - Race condition writing time values to zone file
+
+Knot DNS 1.4.0 (2014-01-06)
+===========================
+
+Features:
+---------
+ - Zone SERIAL policies (INCREMENT, UNIXTIME)
+ - IDN support in Knot utilities
+ - DNSSEC: support for GOST algorithm
+ - Better logging of automatic DNSSEC events
+ - Support for DNSSEC key pre-publication
+ - Experimental automatic DNSSEC signing
+ - Reduced memory usage
+
+Improvements:
+-------------
+ - ./configure prints build configuration summary
+ - Pretty zone file output (DNSSEC-related data separately)
+ - Lower memory consumption
+ - config: option 'dnssec-keydir' can be set per zone
+ - config: option 'storage' can be set per zone
+
+Bugfixes:
+---------
+ - AXFR crash with specific packet
+ - QNAME case-sensitive since 1.4.0-rc0
+ - DNSSEC records over DDNS
+ - Semantic check fail in AXFR is only soft-error
+ - Journal race condition
+ - Notifies are sent immediately
+ - Crash in particular additionals processing
+ - Race condition in event cancellation
+ - Journal corruption after failed transactions
+ - DNSSEC: fixed detection of ECDSA support
+ - Refactored zone loading
+ - Improved journal locking and fixed some race conditions
+ - Various fixes in client utilities
+ - Fixed memory errors in automatic DNSSEC signing
+ - 'dnssec-keydir' doesn't auto-enable signing
+ - Fixed rescheduling of zone resigns
+
+Knot DNS 1.3.3 (2013-10-28)
+===========================
+
+Bugfixes:
+---------
+ - Improved zone loading error messages
+ - Correct control socket permissions
+ - Improved log syntax documentation
+ - Fixed wrong assertions in DDNS prerequisites checking
+ - Fixed processing of some malformed DNS packets
+ - Fixed notify messages being ignored in some cases
+
+Knot DNS 1.3.2 (2013-09-30)
+===========================
+
+Bugfixes:
+---------
+ - Configuration option for EDNS0 max UDP payload.
+ - Max UDP payload from EDNS0 affected TCP responses.
+ - Fixed build on SLE 10.
+ - knotc reload did not close files included from config.
+
+Knot DNS 1.3.1 (2013-08-26)
+===========================
+
+Bugfixes:
+---------
+ - Response with NSID contained extra bytes after reload
+ - List of remotes is scanned for longest prefix match
+ - Multipacket TSIG signatures for transfers
+ - Wrongly parsed TSIG key secret without quotes
+ - Removed autoconf checks for extended instruction sets
+
+Knot DNS 1.3.0 (2013-08-05)
+===========================
+
+Features:
+---------
+ - Defaults for CH TXT id.server,version.server (see doc)
+ - Much faster bootstrap of many zones
+ - --with-configdir option for default config path
+ - Reintroduced 'pidfile' config option
+ - Utility to estimate memory consumption (see 'knotc memstats')
+ - PID file is not created when running on foreground
+ - UNIX sockets support for knotc
+ - Configurable 'rundir' and 'storage'
+ - Faster zone parser
+ - Full support for EUI and ILNP resource records
+ - Lower memory footprint for large zones
+ - No compilation of zones
+ - Improved scheduling of zone transfers
+ - Logging of serials and timing information for zone transfers
+ - Config: 'groups' keyword allowing to create groups of remotes
+ - Config: 'include' keyword allowing other file includes
+ - Client utilities: kdig, khost, knsupdate
+ - Server identification using TXT/CH queries (RFC 4892)
+ - Improved build scripts
+ - Improved dname compression and performance
+
+Bugfixes:
+---------
+ - Progressive interval for bootstrap retry
+ - Transfers randomly cancelled
+ - Disabling RRL on reload
+ - Secondary groups not initialized when dropping privileges
+ - Responding to DS queries for names at or below delegation points
+ - Removed deprecated 'knotc -w' option
+ - Slave ignores out-of-zone records in zone
+ - Support for obsolete types in zone transfers
+ - Slave zone file names fixes
+ - Long transfers being randomly dropped
+ - AXFR/IXFR subsystem performance improvements
+ - Rescheduling of AXFR in some cases
+ - RRSIGs not in the same section for DS records
+ - Log messages leaking to syslog
+ - 'knotc restart' option removed due to several limitations
+ - IXFR with an arbitrary number of diffs
+ - Processing of knotc TSIG keyfile
+ - Atomic PID file writing, removed deprecated 'knotc start'
+ - Performance regression when RRSIGs came before covered RRs in AXFR
+ - Label compression related bug
+ - Proper resolution of some CNAME chains
+ - Unstable response rate in rare cases
+ - Several log messages
+ - Fixed creating of PID file when dropping privileges
+
+Knot DNS 1.2.0 (2013-03-29)
+===========================
+
+Features:
+---------
+ - knotc 'zonestatus' command
+ - Response rate limiting (see documentation)
+ - Dynamic updates, including forwarding (limited on signed zones)
+ - Updated remote control utility
+ - Configurable TCP timeouts
+ - LOC RR support
+
+Bugfixes:
+---------
+ - Memory leaks
+ - Check for broken recvmmsg() implementation
+ - Changing logfile ownership before dropping privileges
+ - knotc respects 'control' section from configuration
+ - RRL: resolved bucket collisions
+ - RRL: updated bucket mapping to conform RRL technical memo
+ - Fixed OpenBSD build
+ - Responses to ANY should contain RRSIGs
+ - Fixed processing of some non-standard dnames.
+ - Correct checking of label length bounds in some cases.
+ - More compliant rcodes in case of DDNS/TSIG failures.
+ - Correct processing of malformed DDNS prereq section.
+
+Knot DNS 1.1.3 (2012-12-19)
+===========================
+
+Bugfixes:
+---------
+ - Updated manpage.
+ - Fixed answering DS queries (RRSIGs not together with DS, AA bit
+ missing).
+ - Fixed setting ARCOUNT in some error responses with EDNS enabled.
+ - Fixed crash when compiling zone zone with NSEC3PARAM but no NSEC3
+ and semantic checks enabled.
+
+Knot DNS 1.1.2 (2012-11-21)
+===========================
+
+Bugfixes:
+---------
+ - Fixed debug message.
+ - Fixed crash on reload when config contained duplicate zones.
+ - Fixed scheduling of transfers.
+
+Knot DNS 1.1.1 (2012-10-31)
+===========================
+
+Features:
+---------
+ - Improved compression of packets. Out-of-zone dnames present in
+ RDATA were not compressed.
+ - Slave zones are now automatically refreshed after startup.
+ - Proper response to IXFR/UDP query (returns SOA in Authority
+ section).
+
+Bugfixes:
+---------
+ - Fixed assertion failing when asking directly for a wildcard name.
+ - Crash after IXFR in certain cases when adding RRSIG in an IXFR.
+ - Fixed behaviour when incoming IXFR removes a zone cut. Previously
+ occluded names now become properly visible. Previously lead to a
+ crash when the server was asked for the previously occluded name.
+ - Fixed handling of zero-length strings in text zone dump. Caused the
+ compilation to fail.
+ - Fixed TSIG algorithm name comparison - the names should be in
+ canonical form.
+ - Fixed handling unknown RR types with type less than 251.
+
+Knot DNS 1.1.0 (2012-08-31)
+===========================
+
+Features:
+---------
+ - Signing SOA with TSIG queries when checking zone version with
+ master.
+ - Optionally disable ANY queries for authoritative answers.
+ - Dropping identical records in zone and incoming transfers.
+ - Support for '/' in zone names.
+ - Generating journal from reloaded zone (EXPERIMENTAL).
+ - Outgoing-only interfaces in configuration file.
+ - Following DNAME if the synthesized name is in the same zone.
+
+Bugfixes:
+---------
+ - Syncing journal to zone was not updating the compiled zone
+ database.
+ - Fixed ixfr-from-differences journal generation in case of IPSECKEY
+ and APL records.
+ - Fixed possible leak on server shutdown with a pending transfer.
+ - Crash when zone contained RRSIG signing a CNAME, but did not
+ contain the CNAME.
+ - Malformed packets parsing.
+ - Failed IXFR caused memory leaks.
+ - Failed IXFR might have resulted in inconsistent zone structures.
+ - Fixed answering to +dnssec queries when NSEC3 chain is corrupted.
+ - Fixed answering when transitioning from NSEC3 to NSEC.
+ - Fixed answering when zone contains multiple NSEC3 chains.
+ - Handling RRSets with different TTLs - TTL from the first RR is
+ used.
+ - Synchronization of zone reload and zone transfers.
+ - Fixed build on NetBSD 5 and FreeBSD.
+ - Fixed binding to both IPv4 and IPv6 at the same time on special
+ interfaces.
+ - Fixed access rights of created files.
+ - Semantic checks corrupted RDATA domain names which are covered by
+ wildcard in the same zone.
+
+Improvements:
+-------------
+ - Improved user manual.
+ - Better checks of corrupted zone database.
+ - IXFR-in optimized.
+ - Many zones loading optimized.
+ - More detailed log messages (mostly transfer-related).
+ - Copying Question section to error responses.
+ - Using zone name from config file as default origin in zone file.
+ - Additional records are now added to response also from
+ wildcard-covered names.
+
+Knot DNS 1.0.6 (2012-06-13)
+===========================
+
+Bugfixes:
+---------
+ - Fixed potential problems with RCU synchronization.
+ - Adding NSEC/NSEC3 for all wildcard CNAMEs in the response.
+
+Knot DNS 1.0.5 (2012-05-17)
+===========================
+
+Bugfixes:
+---------
+ - Fixed bug with creating journal files.
+
+Knot DNS 1.0.4 (2012-05-16)
+===========================
+
+Features:
+---------
+ - Parallel loading of zones to the server.
+ - RFC3339-complaint format of log time.
+ - Support for TLSA (RR type 52).
+ - knotc checkzone (as a dry-run of zone compile).
+ - knotc refresh for forcing Knot to update all zones from master
+ servers.
+ - Reopening log files upon start (used to truncate them).
+
+Improvements:
+-------------
+ - Significantly sped up IXFR-in and reduced its memory requirements.
+
+Bugfixes:
+---------
+ - Copying OPCODE and RD bit from query to NOTIMPL responses.
+ - Corrected response to CNAME queries if the canonical name was also
+ an alias (was adding the whole CNAME chain to the response).
+ - Fixed crash when NS or MX points to an alias.
+ - Fixed problem with early closing of filedescriptors (lead to crash
+ when compiling and loading or bootstrapping and restarting the
+ server with a lot of zones).
+
+Knot DNS 1.0.3 (2012-04-17)
+===========================
+
+Bugfixes:
+---------
+ - Corrected handling of EDNS0 when TCP is used (was applying the UDP
+ size limit).
+ - Fixed slow compilation of zones.
+ - Fixed potential crash with many concurrent transfers.
+ - Fixed missing include for FreeBSD.
+
+Knot DNS 1.0.2 (2012-04-13)
+===========================
+
+Features:
+---------
+ - Configuration checker (invoked via knotc).
+ - Specifying source interface for transfers and NOTIFY requests
+ directly.
+
+Bugfixes:
+---------
+ - Fixed leak when querying non-existing name and zone SOA TTL >
+ minimal.
+ - Fixed some minor bugs in transfers.
+
+Improvements:
+-------------
+ - Improved log messages (added date and time, better specification of
+ XFR remote).
+ - Improved saving incoming IXFR to journal (memory optimized).
+ - Now using system scheduler (better for Linux).
+ - Decreased thread stack size.
+
+Knot DNS 1.0.1 (2012-05-09)
+===========================
+
+Features:
+---------
+ - Implemented jitter to REFRESH/RETRY timers.
+ - Implemented magic bytes for journal.
+ - Improved error messages.
+
+Bugfixes:
+---------
+ - Problem with creating IXFR journal for bootstrapped zone.
+ - Race condition in processing NOTIFY/SOA queries.
+ - Leak when reloading zone with NSEC3.
+ - Processing of APL RR.
+ - TSIG improper assignment of algorithm type.
+
+Knot DNS 1.0.0 (2012-02-29)
+===========================
+
+Features:
+---------
+ - Support for subnets in ACL.
+ - Debug messages enabling in configure.
+ - Optimized memory consumption of zone structures.
+ - NSID support (RFC5001).
+ - Root zone support.
+ - Automatic zone compiling on server start.
+ - Setting user to run Knot under in config file.
+ - Dropping privileges after binding to port 53.
+ + Support for Linux capabilities(7).
+ - Setting source address of outgoing transfers in config file.
+ - Custom PID file.
+ - CNAME loop detection.
+ - Timeout on TCP connections.
+ - Basic defense against DoS attacks.
+
+Bugfixes:
+---------
+ - Memory errors and leaks.
+ - Fixed improper handling of failed IXFR/IN.
+ - Several other minor bugfixes.
+ - Fixed IXFR processing.
+ - Patched URCU so that it compiles on architectures without TLS in
+ compiler (NetBSD, OpenBSD).
+ - Fixed response to DS query at parent zone.
+ - A lot of other bugfixes.
+
+Knot DNS 0.9.1 (2012-01-20)
+===========================
+
+Features:
+---------
+ - RRSet rotation
+
+Improvements:
+-------------
+ - Replaced pseudo-random number generator by one with MIT/BSD
+ license.
+
+Bugfixes:
+---------
+ - Fixed build on BSD.
+ - Fixes in parsing and dumping of zone RR types IPSECKEY, WKS, DLV,
+ APL, NSAP
+
+Knot DNS 0.9.0 (2012-01-13)
+===========================
+
+Features:
+---------
+ - TSIG support in both client and server.
+ - Use of sendmmsg() on Linux 3.0+ (improves performance).
+
+Bugfixes:
+---------
+ - Knot was not accepting AXFR-style IXFR with first SOA in a separate
+ packet (i.e. from Power DNS).
+ - Wrong SOA TTL in negative answers.
+ - Wrong max packet size for outgoing transfers (was causing the
+ packets to be malformed).
+ - Wrong handling of WKS record in zone compiler.
+ - Problems with zone bootstrapping.
+
+Knot DNS 0.8.1 (2011-12-01)
+===========================
+
+Bugfixes:
+---------
+ - Handling SPF record.
+ - Wrong text dump of unknown records.
+
+Knot DNS 0.8.0 (2011-11-03)
+===========================
+
+Features:
+---------
+ - First Public Release
+ - AXFR-in/-out
+ - IXFR-in/-out
+ - EDNS0
+ - DNSSEC
+ - NSEC3
+ - IPv6
+ - Runtime reconfiguration
+
+Known issues:
+-------------
+ - Missing support for TSIG
+ - Root zone support
+ - NSID support
+ - Other DNS classes than IN
+ - RRSet rotation not implemented
+ - Dynamic update support
+ - IXFR code might be flaky sometimes
+ - IXFR may be slow when too much (10 000+) RRSets are transferred at
+ once
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e8ef95d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,123 @@
+[![Coverity Status](https://img.shields.io/coverity/scan/knot-dns.svg)](https://scan.coverity.com/projects/knot-dns)
+[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/knot-dns.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:knot-dns)
+[![Documentation Status](https://readthedocs.org/projects/knot/badge/?version=master)](https://knot.readthedocs.io/en/master/)
+
+# Requirements
+
+[doc/requirements.rst](doc/requirements.rst)
+
+# Installation
+
+[doc/installation.rst](doc/installation.rst)
+
+## 1. Install prerequisites
+
+### Debian based distributions
+
+#### Update the system:
+```bash
+sudo apt-get update
+sudo apt-get upgrade
+```
+
+#### Install prerequisites:
+```bash
+sudo apt-get install \
+ libtool autoconf automake make pkg-config liburcu-dev libgnutls28-dev libedit-dev liblmdb-dev
+```
+
+#### Install optional packages:
+```bash
+sudo apt-get install \
+ libcap-ng-dev libsystemd-dev libidn2-0-dev libprotobuf-c-dev protobuf-c-compiler libfstrm-dev libmaxminddb-dev libnghttp2-dev libmnl-dev
+```
+
+### Fedora like distributions
+
+#### Update the system:
+```bash
+dnf upgrade
+```
+
+#### Install basic development tools:
+```bash
+dnf install @buildsys-build
+```
+
+#### Install prerequisites:
+```bash
+dnf install \
+ libtool autoconf automake pkgconfig userspace-rcu-devel gnutls-devel libedit-devel lmdb-devel
+```
+
+#### Install optional packages:
+```bash
+dnf install \
+ libcap-ng-devel systemd-devel libidn2-devel protobuf-c-devel fstrm-devel libmaxminddb-devel libnghttp2-devel libmnl-devel
+```
+
+When compiling on RHEL based system, the Fedora EPEL repository has to be
+enabled. Also for RHEL 6, forward compatibility package gnutls30-devel
+with newer GnuTLS is required instead of gnutls-devel.
+
+## 2. Install Knot DNS
+
+Get the source code:
+```bash
+git clone https://gitlab.nic.cz/knot/knot-dns.git
+```
+Or extract source package to knot-dns directory.
+
+Compile the source code:
+```bash
+cd knot-dns
+autoreconf -if
+./configure
+make
+```
+
+Install Knot DNS into system:
+```bash
+sudo make install
+sudo ldconfig
+```
+
+# Running
+
+### 1. Ensure some configuration
+
+[doc/configuration.rst](doc/configuration.rst)
+
+Please see [samples/knot.sample.conf](samples/knot.sample.conf),
+[project documentation](https://www.knot-dns.cz/documentation/),
+or `man 5 knot.conf` for more details. Basically the configuration should specify:
+- network interfaces
+- served zones
+
+E.g. use the default configuration file:
+```bash
+cd /etc/knot
+mv knot.sample.conf knot.conf
+```
+Modify the configuration file:
+```bash
+editor knot.conf
+```
+
+### 2. Prepare working directory
+
+```bash
+mv example.com.zone /var/lib/knot/
+```
+
+### 3. Start the server
+
+[doc/operation.rst](doc/operation.rst)
+
+This can be done by running the `knotd` command. Alternatively, your distribution
+should have an init script available, if you installed Knot DNS from a binary package.
+
+Start the server in foreground to see if it runs:
+```bash
+knotd -c /etc/knot/knot.conf
+```
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..f5f82db
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,1480 @@
+# generated automatically by aclocal 1.16.3 -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+
+# This file 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.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+# serial 12 (pkg-config-0.29.2)
+
+dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+dnl 02111-1307, USA.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29.2])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+ [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
+dnl ----------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+fi[]dnl
+])dnl PKG_PROG_PKG_CONFIG
+
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_default([$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes ],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])dnl _PKG_CONFIG
+
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
+
+
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $2])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ AC_MSG_RESULT([no])
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+ ])
+elif test $pkg_failed = untried; then
+ AC_MSG_RESULT([no])
+ m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+ ])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ $3
+fi[]dnl
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+ [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+ [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+ [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+ [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
+
+# Copyright (C) 2002-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.16'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.16.3], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.16.3])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# Copyright (C) 2011-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_PROG_AR([ACT-IF-FAIL])
+# -------------------------
+# Try to determine the archiver interface, and trigger the ar-lib wrapper
+# if it is needed. If the detection of archiver interface fails, run
+# ACT-IF-FAIL (default is to abort configure with a proper error message).
+AC_DEFUN([AM_PROG_AR],
+[AC_BEFORE([$0], [LT_INIT])dnl
+AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl
+AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([ar-lib])dnl
+AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false])
+: ${AR=ar}
+
+AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface],
+ [AC_LANG_PUSH([C])
+ am_cv_ar_interface=ar
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])],
+ [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
+ AC_TRY_EVAL([am_ar_try])
+ if test "$ac_status" -eq 0; then
+ am_cv_ar_interface=ar
+ else
+ am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
+ AC_TRY_EVAL([am_ar_try])
+ if test "$ac_status" -eq 0; then
+ am_cv_ar_interface=lib
+ else
+ am_cv_ar_interface=unknown
+ fi
+ fi
+ rm -f conftest.lib libconftest.a
+ ])
+ AC_LANG_POP([C])])
+
+case $am_cv_ar_interface in
+ar)
+ ;;
+lib)
+ # Microsoft lib, so override with the ar-lib wrapper script.
+ # FIXME: It is wrong to rewrite AR.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__AR in this case,
+ # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
+ # similar.
+ AR="$am_aux_dir/ar-lib $AR"
+ ;;
+unknown)
+ m4_default([$1],
+ [AC_MSG_ERROR([could not determine $AR interface])])
+ ;;
+esac
+AC_SUBST([AR])dnl
+])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
+ [$1], [CXX], [depcc="$CXX" am_compiler_list=],
+ [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+ [$1], [UPC], [depcc="$UPC" am_compiler_list=],
+ [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+ [--enable-dependency-tracking],
+ [do not reject slow dependency extractors])
+AS_HELP_STRING(
+ [--disable-dependency-tracking],
+ [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+ am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Older Autoconf quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ AS_CASE([$CONFIG_FILES],
+ [*\'*], [eval set x "$CONFIG_FILES"],
+ [*], [set x $CONFIG_FILES])
+ shift
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
+ do
+ # Strip MF so we end up with the name of the file.
+ am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`AS_DIRNAME(["$am_mf"])`
+ am_filepart=`AS_BASENAME(["$am_mf"])`
+ AM_RUN_LOG([cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles]) || am_rc=$?
+ done
+ if test $am_rc -ne 0; then
+ AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. If GNU make was not used, consider
+ re-running the configure script with MAKE="gmake" (or whatever is
+ necessary). You can also try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).])
+ fi
+ AS_UNSET([am_dirpart])
+ AS_UNSET([am_filepart])
+ AS_UNSET([am_mf])
+ AS_UNSET([am_rc])
+ rm -f conftest-deps.mk
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+#
+# This file 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 macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+ [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+ m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+ [ok:ok],,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES([CC])],
+ [m4_define([AC_PROG_CC],
+ m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES([CXX])],
+ [m4_define([AC_PROG_CXX],
+ m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES([OBJC])],
+ [m4_define([AC_PROG_OBJC],
+ m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+ [_AM_DEPENDENCIES([OBJCXX])],
+ [m4_define([AC_PROG_OBJCXX],
+ m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+ fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
+am__doit:
+ @echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+ AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+ ['0:this is the am__doit target'],
+ [AS_CASE([$s],
+ [BSD], [am__include='.include' am__quote='"'],
+ [am__include='include' am__quote=''])])
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
+else
+ am_missing_run=
+ AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+ [whether $CC understands -c and -o together],
+ [am_cv_prog_cc_c_o],
+ [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+ alias in your environment])
+ fi
+ if test "$[2]" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+ [AC_MSG_CHECKING([that generated files are newer than configure])
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+ [--enable-silent-rules],
+ [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+ [--disable-silent-rules],
+ [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+ [am_cv_make_support_nested_variables],
+ [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+ dnl Using '$V' instead of '$(V)' breaks IRIX make.
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004-2020 Free Software Foundation, Inc.
+#
+# This file 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.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+ [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+ [m4_case([$1],
+ [ustar],
+ [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+ # There is notably a 21 bits limit for the UID and the GID. In fact,
+ # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+ # and bug#13588).
+ am_max_uid=2097151 # 2^21 - 1
+ am_max_gid=$am_max_uid
+ # The $UID and $GID variables are not portable, so we need to resort
+ # to the POSIX-mandated id(1) utility. Errors in the 'id' calls
+ # below are definitely unexpected, so allow the users to see them
+ # (that is, avoid stderr redirection).
+ am_uid=`id -u || echo unknown`
+ am_gid=`id -g || echo unknown`
+ AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+ if test $am_uid -le $am_max_uid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi
+ AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+ if test $am_gid -le $am_max_gid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi],
+
+ [pax],
+ [],
+
+ [m4_fatal([Unknown tar format])])
+
+ AC_MSG_CHECKING([how to create a $1 tar archive])
+
+ # Go ahead even if we have the value already cached. We do so because we
+ # need to set the values for the 'am__tar' and 'am__untar' variables.
+ _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+ for _am_tool in $_am_tools; do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar; do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works.
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ AM_RUN_LOG([cat conftest.dir/file])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+ done
+ rm -rf conftest.dir
+
+ AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+ AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
+m4_include([m4/ax_check_compile_flag.m4])
+m4_include([m4/ax_check_link_flag.m4])
+m4_include([m4/code-coverage.m4])
+m4_include([m4/knot-lib-version.m4])
+m4_include([m4/knot-module.m4])
+m4_include([m4/libtool.m4])
+m4_include([m4/ltoptions.m4])
+m4_include([m4/ltsugar.m4])
+m4_include([m4/ltversion.m4])
+m4_include([m4/lt~obsolete.m4])
+m4_include([m4/sanitizer.m4])
+m4_include([m4/visibility.m4])
diff --git a/ar-lib b/ar-lib
new file mode 100755
index 0000000..1e9388e
--- /dev/null
+++ b/ar-lib
@@ -0,0 +1,271 @@
+#! /bin/sh
+# Wrapper for Microsoft lib.exe
+
+me=ar-lib
+scriptversion=2019-07-04.01; # UTC
+
+# Copyright (C) 2010-2020 Free Software Foundation, Inc.
+# Written by Peter Rosin <peda@lysator.liu.se>.
+#
+# 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 2, 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/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+
+# func_error message
+func_error ()
+{
+ echo "$me: $1" 1>&2
+ exit 1
+}
+
+file_conv=
+
+# func_file_conv build_file
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts.
+func_file_conv ()
+{
+ file=$1
+ case $file in
+ / | /[!/]*) # absolute file, and not a UNC file
+ if test -z "$file_conv"; then
+ # lazily determine how to convert abs files
+ case `uname -s` in
+ MINGW*)
+ file_conv=mingw
+ ;;
+ CYGWIN* | MSYS*)
+ file_conv=cygwin
+ ;;
+ *)
+ file_conv=wine
+ ;;
+ esac
+ fi
+ case $file_conv in
+ mingw)
+ file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+ ;;
+ cygwin | msys)
+ file=`cygpath -m "$file" || echo "$file"`
+ ;;
+ wine)
+ file=`winepath -w "$file" || echo "$file"`
+ ;;
+ esac
+ ;;
+ esac
+}
+
+# func_at_file at_file operation archive
+# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
+# for each of them.
+# When interpreting the content of the @FILE, do NOT use func_file_conv,
+# since the user would need to supply preconverted file names to
+# binutils ar, at least for MinGW.
+func_at_file ()
+{
+ operation=$2
+ archive=$3
+ at_file_contents=`cat "$1"`
+ eval set x "$at_file_contents"
+ shift
+
+ for member
+ do
+ $AR -NOLOGO $operation:"$member" "$archive" || exit $?
+ done
+}
+
+case $1 in
+ '')
+ func_error "no command. Try '$0 --help' for more information."
+ ;;
+ -h | --h*)
+ cat <<EOF
+Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
+
+Members may be specified in a file named with @FILE.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "$me, version $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test $# -lt 3; then
+ func_error "you must specify a program, an action and an archive"
+fi
+
+AR=$1
+shift
+while :
+do
+ if test $# -lt 2; then
+ func_error "you must specify a program, an action and an archive"
+ fi
+ case $1 in
+ -lib | -LIB \
+ | -ltcg | -LTCG \
+ | -machine* | -MACHINE* \
+ | -subsystem* | -SUBSYSTEM* \
+ | -verbose | -VERBOSE \
+ | -wx* | -WX* )
+ AR="$AR $1"
+ shift
+ ;;
+ *)
+ action=$1
+ shift
+ break
+ ;;
+ esac
+done
+orig_archive=$1
+shift
+func_file_conv "$orig_archive"
+archive=$file
+
+# strip leading dash in $action
+action=${action#-}
+
+delete=
+extract=
+list=
+quick=
+replace=
+index=
+create=
+
+while test -n "$action"
+do
+ case $action in
+ d*) delete=yes ;;
+ x*) extract=yes ;;
+ t*) list=yes ;;
+ q*) quick=yes ;;
+ r*) replace=yes ;;
+ s*) index=yes ;;
+ S*) ;; # the index is always updated implicitly
+ c*) create=yes ;;
+ u*) ;; # TODO: don't ignore the update modifier
+ v*) ;; # TODO: don't ignore the verbose modifier
+ *)
+ func_error "unknown action specified"
+ ;;
+ esac
+ action=${action#?}
+done
+
+case $delete$extract$list$quick$replace,$index in
+ yes,* | ,yes)
+ ;;
+ yesyes*)
+ func_error "more than one action specified"
+ ;;
+ *)
+ func_error "no action specified"
+ ;;
+esac
+
+if test -n "$delete"; then
+ if test ! -f "$orig_archive"; then
+ func_error "archive not found"
+ fi
+ for member
+ do
+ case $1 in
+ @*)
+ func_at_file "${1#@}" -REMOVE "$archive"
+ ;;
+ *)
+ func_file_conv "$1"
+ $AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
+ ;;
+ esac
+ done
+
+elif test -n "$extract"; then
+ if test ! -f "$orig_archive"; then
+ func_error "archive not found"
+ fi
+ if test $# -gt 0; then
+ for member
+ do
+ case $1 in
+ @*)
+ func_at_file "${1#@}" -EXTRACT "$archive"
+ ;;
+ *)
+ func_file_conv "$1"
+ $AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
+ ;;
+ esac
+ done
+ else
+ $AR -NOLOGO -LIST "$archive" | tr -d '\r' | sed -e 's/\\/\\\\/g' \
+ | while read member
+ do
+ $AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
+ done
+ fi
+
+elif test -n "$quick$replace"; then
+ if test ! -f "$orig_archive"; then
+ if test -z "$create"; then
+ echo "$me: creating $orig_archive"
+ fi
+ orig_archive=
+ else
+ orig_archive=$archive
+ fi
+
+ for member
+ do
+ case $1 in
+ @*)
+ func_file_conv "${1#@}"
+ set x "$@" "@$file"
+ ;;
+ *)
+ func_file_conv "$1"
+ set x "$@" "$file"
+ ;;
+ esac
+ shift
+ shift
+ done
+
+ if test -n "$orig_archive"; then
+ $AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
+ else
+ $AR -NOLOGO -OUT:"$archive" "$@" || exit $?
+ fi
+
+elif test -n "$list"; then
+ if test ! -f "$orig_archive"; then
+ func_error "archive not found"
+ fi
+ $AR -NOLOGO -LIST "$archive" || exit $?
+fi
diff --git a/compile b/compile
new file mode 100755
index 0000000..23fcba0
--- /dev/null
+++ b/compile
@@ -0,0 +1,348 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+# Written by Tom Tromey <tromey@cygnus.com>.
+#
+# 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 2, 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/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+nl='
+'
+
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent tools from complaining about whitespace usage.
+IFS=" "" $nl"
+
+file_conv=
+
+# func_file_conv build_file lazy
+# Convert a $build file to $host form and store it in $file
+# Currently only supports Windows hosts. If the determined conversion
+# type is listed in (the comma separated) LAZY, no conversion will
+# take place.
+func_file_conv ()
+{
+ file=$1
+ case $file in
+ / | /[!/]*) # absolute file, and not a UNC file
+ if test -z "$file_conv"; then
+ # lazily determine how to convert abs files
+ case `uname -s` in
+ MINGW*)
+ file_conv=mingw
+ ;;
+ CYGWIN* | MSYS*)
+ file_conv=cygwin
+ ;;
+ *)
+ file_conv=wine
+ ;;
+ esac
+ fi
+ case $file_conv/,$2, in
+ *,$file_conv,*)
+ ;;
+ mingw/*)
+ file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
+ ;;
+ cygwin/* | msys/*)
+ file=`cygpath -m "$file" || echo "$file"`
+ ;;
+ wine/*)
+ file=`winepath -w "$file" || echo "$file"`
+ ;;
+ esac
+ ;;
+ esac
+}
+
+# func_cl_dashL linkdir
+# Make cl look for libraries in LINKDIR
+func_cl_dashL ()
+{
+ func_file_conv "$1"
+ if test -z "$lib_path"; then
+ lib_path=$file
+ else
+ lib_path="$lib_path;$file"
+ fi
+ linker_opts="$linker_opts -LIBPATH:$file"
+}
+
+# func_cl_dashl library
+# Do a library search-path lookup for cl
+func_cl_dashl ()
+{
+ lib=$1
+ found=no
+ save_IFS=$IFS
+ IFS=';'
+ for dir in $lib_path $LIB
+ do
+ IFS=$save_IFS
+ if $shared && test -f "$dir/$lib.dll.lib"; then
+ found=yes
+ lib=$dir/$lib.dll.lib
+ break
+ fi
+ if test -f "$dir/$lib.lib"; then
+ found=yes
+ lib=$dir/$lib.lib
+ break
+ fi
+ if test -f "$dir/lib$lib.a"; then
+ found=yes
+ lib=$dir/lib$lib.a
+ break
+ fi
+ done
+ IFS=$save_IFS
+
+ if test "$found" != yes; then
+ lib=$lib.lib
+ fi
+}
+
+# func_cl_wrapper cl arg...
+# Adjust compile command to suit cl
+func_cl_wrapper ()
+{
+ # Assume a capable shell
+ lib_path=
+ shared=:
+ linker_opts=
+ for arg
+ do
+ if test -n "$eat"; then
+ eat=
+ else
+ case $1 in
+ -o)
+ # configure might choose to run compile as 'compile cc -o foo foo.c'.
+ eat=1
+ case $2 in
+ *.o | *.[oO][bB][jJ])
+ func_file_conv "$2"
+ set x "$@" -Fo"$file"
+ shift
+ ;;
+ *)
+ func_file_conv "$2"
+ set x "$@" -Fe"$file"
+ shift
+ ;;
+ esac
+ ;;
+ -I)
+ eat=1
+ func_file_conv "$2" mingw
+ set x "$@" -I"$file"
+ shift
+ ;;
+ -I*)
+ func_file_conv "${1#-I}" mingw
+ set x "$@" -I"$file"
+ shift
+ ;;
+ -l)
+ eat=1
+ func_cl_dashl "$2"
+ set x "$@" "$lib"
+ shift
+ ;;
+ -l*)
+ func_cl_dashl "${1#-l}"
+ set x "$@" "$lib"
+ shift
+ ;;
+ -L)
+ eat=1
+ func_cl_dashL "$2"
+ ;;
+ -L*)
+ func_cl_dashL "${1#-L}"
+ ;;
+ -static)
+ shared=false
+ ;;
+ -Wl,*)
+ arg=${1#-Wl,}
+ save_ifs="$IFS"; IFS=','
+ for flag in $arg; do
+ IFS="$save_ifs"
+ linker_opts="$linker_opts $flag"
+ done
+ IFS="$save_ifs"
+ ;;
+ -Xlinker)
+ eat=1
+ linker_opts="$linker_opts $2"
+ ;;
+ -*)
+ set x "$@" "$1"
+ shift
+ ;;
+ *.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
+ func_file_conv "$1"
+ set x "$@" -Tp"$file"
+ shift
+ ;;
+ *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
+ func_file_conv "$1" mingw
+ set x "$@" "$file"
+ shift
+ ;;
+ *)
+ set x "$@" "$1"
+ shift
+ ;;
+ esac
+ fi
+ shift
+ done
+ if test -n "$linker_opts"; then
+ linker_opts="-link$linker_opts"
+ fi
+ exec "$@" $linker_opts
+ exit 1
+}
+
+eat=
+
+case $1 in
+ '')
+ echo "$0: No command. Try '$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: compile [--help] [--version] PROGRAM [ARGS]
+
+Wrapper for compilers which do not understand '-c -o'.
+Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
+arguments, and rename the output as expected.
+
+If you are trying to build a whole package this is not the
+right script to run: please start by reading the file 'INSTALL'.
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "compile $scriptversion"
+ exit $?
+ ;;
+ cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
+ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
+ func_cl_wrapper "$@" # Doesn't return...
+ ;;
+esac
+
+ofile=
+cfile=
+
+for arg
+do
+ if test -n "$eat"; then
+ eat=
+ else
+ case $1 in
+ -o)
+ # configure might choose to run compile as 'compile cc -o foo foo.c'.
+ # So we strip '-o arg' only if arg is an object.
+ eat=1
+ case $2 in
+ *.o | *.obj)
+ ofile=$2
+ ;;
+ *)
+ set x "$@" -o "$2"
+ shift
+ ;;
+ esac
+ ;;
+ *.c)
+ cfile=$1
+ set x "$@" "$1"
+ shift
+ ;;
+ *)
+ set x "$@" "$1"
+ shift
+ ;;
+ esac
+ fi
+ shift
+done
+
+if test -z "$ofile" || test -z "$cfile"; then
+ # If no '-o' option was seen then we might have been invoked from a
+ # pattern rule where we don't need one. That is ok -- this is a
+ # normal compilation that the losing compiler can handle. If no
+ # '.c' file was seen then we are probably linking. That is also
+ # ok.
+ exec "$@"
+fi
+
+# Name of file we expect compiler to create.
+cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
+
+# Create the lock directory.
+# Note: use '[/\\:.-]' here to ensure that we don't use the same name
+# that we are using for the .o file. Also, base the name on the expected
+# object file name, since that is what matters with a parallel build.
+lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
+while true; do
+ if mkdir "$lockdir" >/dev/null 2>&1; then
+ break
+ fi
+ sleep 1
+done
+# FIXME: race condition here if user kills between mkdir and trap.
+trap "rmdir '$lockdir'; exit 1" 1 2 15
+
+# Run the compile.
+"$@"
+ret=$?
+
+if test -f "$cofile"; then
+ test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
+elif test -f "${cofile}bj"; then
+ test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
+fi
+
+rmdir "$lockdir"
+exit $ret
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/config.guess b/config.guess
new file mode 100755
index 0000000..f50dcdb
--- /dev/null
+++ b/config.guess
@@ -0,0 +1,1480 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2018 Free Software Foundation, Inc.
+
+timestamp='2018-02-24'
+
+# This file 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/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2018 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,) echo "int x;" > "$dummy.c" ;
+ for c in cc gcc c89 c99 ; do
+ if ($c -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$c"; break ;
+ fi ;
+ done ;
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found ;
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+esac ; set_cc_for_build= ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ eval "$set_cc_for_build"
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #else
+ LIBC=gnu
+ #endif
+ EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+ # If ldd exists, use it to detect musl libc.
+ if command -v ldd >/dev/null && \
+ ldd --version 2>&1 | grep -q ^musl
+ then
+ LIBC=musl
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown)`
+ case "$UNAME_MACHINE_ARCH" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ eval "$set_cc_for_build"
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "$UNAME_VERSION" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "$machine-${os}${release}${abi}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+ exit ;;
+ *:SolidBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:MirBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
+ exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix"$UNAME_RELEASE"
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ eval "$set_cc_for_build"
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ ;;
+ sun4)
+ echo sparc-sun-sunos"$UNAME_RELEASE"
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ eval "$set_cc_for_build"
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos"$UNAME_RELEASE"
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+ then
+ if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+ [ "$TARGET_BINARY_INTERFACE"x = x ]
+ then
+ echo m88k-dg-dgux"$UNAME_RELEASE"
+ else
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+ fi
+ else
+ echo i586-dg-dgux"$UNAME_RELEASE"
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ eval "$set_cc_for_build"
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "$HP_ARCH" = "" ]; then
+ eval "$set_cc_for_build"
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ "$HP_ARCH" = hppa2.0w ]
+ then
+ eval "$set_cc_for_build"
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux"$HPUX_REV"
+ exit ;;
+ 3050*:HI-UX:*:*)
+ eval "$set_cc_for_build"
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
+ else
+ echo "$UNAME_MACHINE"-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:BSD/OS:*:*)
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case "$UNAME_PROCESSOR" in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ i*:CYGWIN*:*)
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo "$UNAME_MACHINE"-pc-mingw32
+ exit ;;
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
+ exit ;;
+ i*:PW*:*)
+ echo "$UNAME_MACHINE"-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
+ x86)
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ esac ;;
+ i*:UWIN*:*)
+ echo "$UNAME_MACHINE"-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-unknown-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ exit ;;
+ i*86:Minix:*:*)
+ echo "$UNAME_MACHINE"-pc-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arm*:Linux:*:*)
+ eval "$set_cc_for_build"
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ cris:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ crisv32:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ frv:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ ia64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m32r*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m68*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ eval "$set_cc_for_build"
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef ${UNAME_MACHINE}
+ #undef ${UNAME_MACHINE}el
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ CPU=${UNAME_MACHINE}el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ CPU=${UNAME_MACHINE}
+ #else
+ CPU=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU'`"
+ test "x$CPU" != x && { echo "$CPU-unknown-linux-$LIBC"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+ exit ;;
+ sh64*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sh*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ vax:Linux:*:*)
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+ exit ;;
+ x86_64:Linux:*:*)
+ if objdump -f /bin/sh | grep -q elf32-x86-64; then
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"x32
+ else
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ fi
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo "$UNAME_MACHINE"-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo "$UNAME_MACHINE"-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo "$UNAME_MACHINE"-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo "$UNAME_MACHINE"-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ i*86:*DOS:*:*)
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}{$UNAME_VERSION}"
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv"$UNAME_RELEASE"
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo "$UNAME_MACHINE"-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo "$UNAME_MACHINE"-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux"$UNAME_RELEASE"
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
+ else
+ echo mips-unknown-sysv"$UNAME_RELEASE"
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Rhapsody:*:*)
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+ eval "$set_cc_for_build"
+ if test "$UNAME_PROCESSOR" = unknown ; then
+ UNAME_PROCESSOR=powerpc
+ fi
+ if test "`echo "$UNAME_RELEASE" | sed -e 's/\..*//'`" -le 10 ; then
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # Avoid executing cc on OS X 10.9, as it ships with a stub
+ # that puts up a graphical alert prompting to install
+ # developer tools. Any system running Mac OS X 10.7 or
+ # later (Darwin 11 and later) is required to have a 64-bit
+ # processor. This is not true of the ARM version of Darwin
+ # that Apple uses in portable devices.
+ UNAME_PROCESSOR=x86_64
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "$cputype" = 386; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo "$UNAME_MACHINE"-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux"$UNAME_RELEASE"
+ exit ;;
+ *:DragonFly:*:*)
+ echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "$UNAME_MACHINE" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+ exit ;;
+ i*86:rdos:*:*)
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+esac
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+and
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-functions 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.sub b/config.sub
new file mode 100755
index 0000000..1d8e98b
--- /dev/null
+++ b/config.sub
@@ -0,0 +1,1801 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2018 Free Software Foundation, Inc.
+
+timestamp='2018-02-22'
+
+# This file 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/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2018 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help"
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
+ kopensolaris*-gnu* | cloudabi*-eabi* | \
+ storm-chaos* | os2-emx* | rtmk-nova*)
+ os=-$maybe_os
+ basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+ ;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo "$1" | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
+ *)
+ basic_machine=`echo "$1" | sed 's/-[^-]*$//'`
+ if [ "$basic_machine" != "$1" ]
+ then os=`echo "$1" | sed 's/.*-/-/'`
+ else os=; fi
+ ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work. We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+ -sun*os*)
+ # Prevent following clause from handling this invalid input.
+ ;;
+ -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+ -apple | -axis | -knuth | -cray | -microblaze*)
+ os=
+ basic_machine=$1
+ ;;
+ -bluegene*)
+ os=-cnk
+ ;;
+ -sim | -cisco | -oki | -wec | -winbond)
+ os=
+ basic_machine=$1
+ ;;
+ -scout)
+ ;;
+ -wrs)
+ os=-vxworks
+ basic_machine=$1
+ ;;
+ -chorusos*)
+ os=-chorusos
+ basic_machine=$1
+ ;;
+ -chorusrdb)
+ os=-chorusrdb
+ basic_machine=$1
+ ;;
+ -hiux*)
+ os=-hiuxwe2
+ ;;
+ -sco6)
+ os=-sco5v6
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5)
+ os=-sco3.2v5
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco4)
+ os=-sco3.2v4
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco3.2v[4-9]*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -sco*)
+ os=-sco3.2v2
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -udk*)
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -isc)
+ os=-isc2.2
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -clix*)
+ basic_machine=clipper-intergraph
+ ;;
+ -isc*)
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-pc/'`
+ ;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
+ -lynx*)
+ os=-lynxos
+ ;;
+ -ptx*)
+ basic_machine=`echo "$1" | sed -e 's/86-.*/86-sequent/'`
+ ;;
+ -psos*)
+ os=-psos
+ ;;
+ -mint | -mint[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+ # Recognize the basic CPU types without company name.
+ # Some are omitted here because they have special meanings below.
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+ | am33_2.0 \
+ | arc | arceb \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | ba \
+ | be32 | be64 \
+ | bfin \
+ | c4x | c8051 | clipper \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | epiphany \
+ | fido | fr30 | frv | ft32 \
+ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle | m68000 | m68k | m88k \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
+ | ns16k | ns32k \
+ | open8 | or1k | or1knd | or32 \
+ | pdp10 | pj | pjl \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
+ | pru \
+ | pyramid \
+ | riscv32 | riscv64 \
+ | rl78 | rx \
+ | score \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
+ | ubicom32 \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | visium \
+ | wasm32 \
+ | x86 | xc16x | xstormy16 | xtensa \
+ | z8k | z80)
+ basic_machine=$basic_machine-unknown
+ ;;
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ leon|leon[3-9])
+ basic_machine=sparc-$basic_machine
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65)
+ ;;
+ ms1)
+ basic_machine=mt-unknown
+ ;;
+
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-unknown
+ ;;
+
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ basic_machine=$basic_machine-pc
+ ;;
+ # Object if more than one company name word.
+ *-*-*)
+ echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
+ exit 1
+ ;;
+ # Recognize the basic CPU types with company name.
+ 580-* \
+ | a29k-* \
+ | aarch64-* | aarch64_be-* \
+ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
+ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
+ | avr-* | avr32-* \
+ | ba-* \
+ | be32-* | be64-* \
+ | bfin-* | bs2000-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
+ | c8051-* | clipper-* | craynv-* | cydra-* \
+ | d10v-* | d30v-* | dlx-* \
+ | e2k-* | elxsi-* \
+ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
+ | h8300-* | h8500-* \
+ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+ | hexagon-* \
+ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
+ | ip2k-* | iq2000-* \
+ | k1om-* \
+ | le32-* | le64-* \
+ | lm32-* \
+ | m32c-* | m32r-* | m32rle-* \
+ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
+ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+ | mips16-* \
+ | mips64-* | mips64el-* \
+ | mips64octeon-* | mips64octeonel-* \
+ | mips64orion-* | mips64orionel-* \
+ | mips64r5900-* | mips64r5900el-* \
+ | mips64vr-* | mips64vrel-* \
+ | mips64vr4100-* | mips64vr4100el-* \
+ | mips64vr4300-* | mips64vr4300el-* \
+ | mips64vr5000-* | mips64vr5000el-* \
+ | mips64vr5900-* | mips64vr5900el-* \
+ | mipsisa32-* | mipsisa32el-* \
+ | mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa32r6-* | mipsisa32r6el-* \
+ | mipsisa64-* | mipsisa64el-* \
+ | mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64r6-* | mipsisa64r6el-* \
+ | mipsisa64sb1-* | mipsisa64sb1el-* \
+ | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
+ | mipstx39-* | mipstx39el-* \
+ | mmix-* \
+ | mt-* \
+ | msp430-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
+ | none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
+ | or1k*-* \
+ | orion-* \
+ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ | pru-* \
+ | pyramid-* \
+ | riscv32-* | riscv64-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
+ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
+ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
+ | sparclite-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
+ | tahoe-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
+ | tron-* \
+ | ubicom32-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
+ | visium-* \
+ | wasm32-* \
+ | we32k-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
+ | xstormy16-* | xtensa*-* \
+ | ymp-* \
+ | z8k-* | z80-*)
+ ;;
+ # Recognize the basic CPU types without company name, with glob match.
+ xtensa*)
+ basic_machine=$basic_machine-unknown
+ ;;
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 386bsd)
+ basic_machine=i386-pc
+ os=-bsd
+ ;;
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ basic_machine=m68000-att
+ ;;
+ 3b*)
+ basic_machine=we32k-att
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ abacus)
+ basic_machine=abacus-unknown
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=-scout
+ ;;
+ alliant | fx80)
+ basic_machine=fx80-alliant
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=-bsd
+ ;;
+ amd64)
+ basic_machine=x86_64-pc
+ ;;
+ amd64-*)
+ basic_machine=x86_64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=-sysv
+ ;;
+ amiga | amiga-*)
+ basic_machine=m68k-unknown
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=-amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=-sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=-sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=-bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=-aros
+ ;;
+ asmjs)
+ basic_machine=asmjs-unknown
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=-aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=-dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=-linux
+ ;;
+ blackfin-*)
+ basic_machine=bfin-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ bluegene*)
+ basic_machine=powerpc-ibm
+ os=-cnk
+ ;;
+ c54x-*)
+ basic_machine=tic54x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ c90)
+ basic_machine=c90-cray
+ os=-unicos
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=-cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=-bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=-bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=-bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=-bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=-bsd
+ ;;
+ cray | j90)
+ basic_machine=j90-cray
+ os=-unicos
+ ;;
+ craynv)
+ basic_machine=craynv-cray
+ os=-unicosmp
+ ;;
+ cr16 | cr16-*)
+ basic_machine=cr16-unknown
+ os=-elf
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ ;;
+ crisv32 | crisv32-* | etraxfs*)
+ basic_machine=crisv32-axis
+ ;;
+ cris | cris-* | etrax*)
+ basic_machine=cris-axis
+ ;;
+ crx)
+ basic_machine=crx-unknown
+ os=-elf
+ ;;
+ da30 | da30-*)
+ basic_machine=m68k-da30
+ ;;
+ decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ ;;
+ decsystem10* | dec10*)
+ basic_machine=pdp10-dec
+ os=-tops10
+ ;;
+ decsystem20* | dec20*)
+ basic_machine=pdp10-dec
+ os=-tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ basic_machine=m68k-motorola
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=-sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=-dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=-msdosdjgpp
+ ;;
+ dpx20 | dpx20-*)
+ basic_machine=rs6000-bull
+ os=-bosx
+ ;;
+ dpx2*)
+ basic_machine=m68k-bull
+ os=-sysv3
+ ;;
+ e500v[12])
+ basic_machine=powerpc-unknown
+ os=$os"spe"
+ ;;
+ e500v[12]-*)
+ basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ os=$os"spe"
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=-ebmon
+ ;;
+ elxsi)
+ basic_machine=elxsi-elxsi
+ os=-bsd
+ ;;
+ encore | umax | mmax)
+ basic_machine=ns32k-encore
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=-ose
+ ;;
+ fx2800)
+ basic_machine=i860-alliant
+ ;;
+ genix)
+ basic_machine=ns32k-ns
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=-sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=-go32
+ ;;
+ h3050r* | hiux*)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=-hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=-xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=-hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=-sysv3
+ ;;
+ hp300-*)
+ basic_machine=m68k-hp
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=-bsd
+ ;;
+ hp300hpux)
+ basic_machine=m68k-hp
+ os=-hpux
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ basic_machine=m68000-hp
+ ;;
+ hp9k3[2-9][0-9])
+ basic_machine=m68k-hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ basic_machine=hppa1.1-hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ basic_machine=hppa1.0-hp
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=-osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=-proelf
+ ;;
+ i370-ibm* | ibm*)
+ basic_machine=i370-ibm
+ ;;
+ i*86v32)
+ basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+ os=-sysv32
+ ;;
+ i*86v4*)
+ basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+ os=-sysv4
+ ;;
+ i*86v)
+ basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+ os=-sysv
+ ;;
+ i*86sol2)
+ basic_machine=`echo "$1" | sed -e 's/86.*/86-pc/'`
+ os=-solaris2
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=-mach
+ ;;
+ vsta)
+ basic_machine=i386-unknown
+ os=-vsta
+ ;;
+ iris | iris4d)
+ basic_machine=mips-sgi
+ case $os in
+ -irix*)
+ ;;
+ *)
+ os=-irix4
+ ;;
+ esac
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=-sysv
+ ;;
+ leon-*|leon[3-9]-*)
+ basic_machine=sparc-`echo "$basic_machine" | sed 's/-.*//'`
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=-linux
+ ;;
+ m68knommu-*)
+ basic_machine=m68k-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=-sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=-sysv
+ ;;
+ microblaze*)
+ basic_machine=microblaze-xilinx
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=-mingw32ce
+ ;;
+ miniframe)
+ basic_machine=m68000-convergent
+ ;;
+ *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ basic_machine=m68k-atari
+ os=-mint
+ ;;
+ mips3*-*)
+ basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`
+ ;;
+ mips3*)
+ basic_machine=`echo "$basic_machine" | sed -e 's/mips3/mips64/'`-unknown
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=-morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=-moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=-msdos
+ ;;
+ ms1-*)
+ basic_machine=`echo "$basic_machine" | sed -e 's/ms1-/mt-/'`
+ ;;
+ msys)
+ basic_machine=i686-pc
+ os=-msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=-mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=-sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-unknown
+ os=-netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=-linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=-newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=-newsos
+ ;;
+ news-3600 | risc-news)
+ basic_machine=mips-sony
+ os=-newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=-sysv
+ ;;
+ next | m*-next)
+ basic_machine=m68k-next
+ case $os in
+ -nextstep* )
+ ;;
+ -ns2*)
+ os=-nextstep2
+ ;;
+ *)
+ os=-nextstep3
+ ;;
+ esac
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=-cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=-cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=-nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=-mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=-nonstopux
+ ;;
+ np1)
+ basic_machine=np1-gould
+ ;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
+ nsr-tandem)
+ basic_machine=nsr-tandem
+ ;;
+ nsv-tandem)
+ basic_machine=nsv-tandem
+ ;;
+ nsx-tandem)
+ basic_machine=nsx-tandem
+ ;;
+ op50n-* | op60c-*)
+ basic_machine=hppa1.1-oki
+ os=-proelf
+ ;;
+ openrisc | openrisc-*)
+ basic_machine=or32-unknown
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=-os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=-ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=-os68k
+ ;;
+ pa-hitachi)
+ basic_machine=hppa1.1-hitachi
+ os=-hiuxwe2
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=-osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=-linux
+ ;;
+ parisc-*)
+ basic_machine=hppa-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ os=-linux
+ ;;
+ pbd)
+ basic_machine=sparc-tti
+ ;;
+ pbb)
+ basic_machine=m68k-tti
+ ;;
+ pc532 | pc532-*)
+ basic_machine=ns32k-pc532
+ ;;
+ pc98)
+ basic_machine=i386-pc
+ ;;
+ pc98-*)
+ basic_machine=i386-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ pentium | p5 | k5 | k6 | nexgen | viac3)
+ basic_machine=i586-pc
+ ;;
+ pentiumpro | p6 | 6x86 | athlon | athlon_*)
+ basic_machine=i686-pc
+ ;;
+ pentiumii | pentium2 | pentiumiii | pentium3)
+ basic_machine=i686-pc
+ ;;
+ pentium4)
+ basic_machine=i786-pc
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ basic_machine=i586-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-*)
+ basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ basic_machine=i686-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ pentium4-*)
+ basic_machine=i786-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ pn)
+ basic_machine=pn-gould
+ ;;
+ power) basic_machine=power-ibm
+ ;;
+ ppc | ppcbe) basic_machine=powerpc-unknown
+ ;;
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ ppcle | powerpclittle)
+ basic_machine=powerpcle-unknown
+ ;;
+ ppcle-* | powerpclittle-*)
+ basic_machine=powerpcle-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ ppc64) basic_machine=powerpc64-unknown
+ ;;
+ ppc64-*) basic_machine=powerpc64-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ ppc64le | powerpc64little)
+ basic_machine=powerpc64le-unknown
+ ;;
+ ppc64le-* | powerpc64little-*)
+ basic_machine=powerpc64le-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ ps2)
+ basic_machine=i386-ibm
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=-pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=-rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=-coff
+ ;;
+ rm[46]00)
+ basic_machine=mips-siemens
+ ;;
+ rtpc | rtpc-*)
+ basic_machine=romp-ibm
+ ;;
+ s390 | s390-*)
+ basic_machine=s390-ibm
+ ;;
+ s390x | s390x-*)
+ basic_machine=s390x-ibm
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ sb1)
+ basic_machine=mipsisa64sb1-unknown
+ ;;
+ sb1el)
+ basic_machine=mipsisa64sb1el-unknown
+ ;;
+ sde)
+ basic_machine=mipsisa32-sde
+ os=-elf
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=-seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ ;;
+ sh5el)
+ basic_machine=sh5le-unknown
+ ;;
+ simso-wrs)
+ basic_machine=sparclite-wrs
+ os=-vxworks
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=-sysv2
+ ;;
+ spur)
+ basic_machine=spur-unknown
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=-sysv4
+ ;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo "$basic_machine" | sed 's/^[^-]*-//'`
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=-sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=-sunos4
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=-sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=-sunos4
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=-sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=-sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=-solaris2
+ ;;
+ sun3 | sun3-*)
+ basic_machine=m68k-sun
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=-unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=-dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=-unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=-unicos
+ ;;
+ tile*)
+ basic_machine=$basic_machine-unknown
+ os=-linux-gnu
+ ;;
+ tx39)
+ basic_machine=mipstx39-unknown
+ ;;
+ tx39el)
+ basic_machine=mipstx39el-unknown
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=-tops20
+ ;;
+ tower | tower-32)
+ basic_machine=m68k-ncr
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=-tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=-udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=-sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=-none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=-sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=-vms
+ ;;
+ vpp*|vx|vx-*)
+ basic_machine=f301-fujitsu
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=-vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=-vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=-vxworks
+ ;;
+ w65*)
+ basic_machine=w65-wdc
+ os=-none
+ ;;
+ w89k-*)
+ basic_machine=hppa1.1-winbond
+ os=-proelf
+ ;;
+ x64)
+ basic_machine=x86_64-pc
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=-mingw32
+ ;;
+ xps | xps100)
+ basic_machine=xps100-honeywell
+ ;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo "$basic_machine" | sed 's/^xscale/arm/'`
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=-unicos
+ ;;
+ none)
+ basic_machine=none-none
+ os=-none
+ ;;
+
+# Here we handle the default manufacturer of certain CPU types. It is in
+# some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ basic_machine=hppa1.1-winbond
+ ;;
+ op50n)
+ basic_machine=hppa1.1-oki
+ ;;
+ op60c)
+ basic_machine=hppa1.1-oki
+ ;;
+ romp)
+ basic_machine=romp-ibm
+ ;;
+ mmix)
+ basic_machine=mmix-knuth
+ ;;
+ rs6000)
+ basic_machine=rs6000-ibm
+ ;;
+ vax)
+ basic_machine=vax-dec
+ ;;
+ pdp11)
+ basic_machine=pdp11-dec
+ ;;
+ we32k)
+ basic_machine=we32k-att
+ ;;
+ sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
+ basic_machine=sh-unknown
+ ;;
+ cydra)
+ basic_machine=cydra-cydrome
+ ;;
+ orion)
+ basic_machine=orion-highlevel
+ ;;
+ orion105)
+ basic_machine=clipper-highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ basic_machine=m68k-apple
+ ;;
+ pmac | pmac-mpw)
+ basic_machine=powerpc-apple
+ ;;
+ *-unknown)
+ # Make sure to match an already-canonicalized machine name.
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$basic_machine"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+ *-digital*)
+ basic_machine=`echo "$basic_machine" | sed 's/digital.*/dec/'`
+ ;;
+ *-commodore*)
+ basic_machine=`echo "$basic_machine" | sed 's/commodore.*/cbm/'`
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # -solaris* is a basic system type, with this one exception.
+ -auroraux)
+ os=-auroraux
+ ;;
+ -solaris1 | -solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ -solaris)
+ os=-solaris2
+ ;;
+ -unixware*)
+ os=-sysv4.2uw
+ ;;
+ -gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ -es1800*)
+ os=-ose
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ # -sysv* is not here because it comes later, after sysvr4.
+ -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
+ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
+ | -sym* | -kopensolaris* | -plan9* \
+ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+ | -aos* | -aros* | -cloudabi* | -sortix* \
+ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+ | -hiux* | -knetbsd* | -mirbsd* | -netbsd* \
+ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
+ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
+ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* \
+ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+ | -morphos* | -superux* | -rtmk* | -windiss* \
+ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
+ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox* | -bme* \
+ | -midnightbsd*)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ -qnx*)
+ case $basic_machine in
+ x86-* | i*86-*)
+ ;;
+ *)
+ os=-nto$os
+ ;;
+ esac
+ ;;
+ -nto-qnx*)
+ ;;
+ -nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ -sim | -xray | -os68k* | -v88r* \
+ | -windows* | -osx | -abug | -netware* | -os9* \
+ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ ;;
+ -mac*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
+ ;;
+ -linux-dietlibc)
+ os=-linux-dietlibc
+ ;;
+ -linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ -sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ ;;
+ -sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ ;;
+ -opened*)
+ os=-openedition
+ ;;
+ -os400*)
+ os=-os400
+ ;;
+ -wince*)
+ os=-wince
+ ;;
+ -utek*)
+ os=-bsd
+ ;;
+ -dynix*)
+ os=-bsd
+ ;;
+ -acis*)
+ os=-aos
+ ;;
+ -atheos*)
+ os=-atheos
+ ;;
+ -syllable*)
+ os=-syllable
+ ;;
+ -386bsd)
+ os=-bsd
+ ;;
+ -ctix* | -uts*)
+ os=-sysv
+ ;;
+ -nova*)
+ os=-rtmk-nova
+ ;;
+ -ns2)
+ os=-nextstep2
+ ;;
+ -nsk*)
+ os=-nsk
+ ;;
+ # Preserve the version number of sinix5.
+ -sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ -sinix*)
+ os=-sysv4
+ ;;
+ -tpf*)
+ os=-tpf
+ ;;
+ -triton*)
+ os=-sysv3
+ ;;
+ -oss*)
+ os=-sysv3
+ ;;
+ -svr4*)
+ os=-sysv4
+ ;;
+ -svr3)
+ os=-sysv3
+ ;;
+ -sysvr4)
+ os=-sysv4
+ ;;
+ # This must come after -sysvr4.
+ -sysv*)
+ ;;
+ -ose*)
+ os=-ose
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ os=-mint
+ ;;
+ -zvmoe)
+ os=-zvmoe
+ ;;
+ -dicos*)
+ os=-dicos
+ ;;
+ -pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $basic_machine in
+ arm*)
+ os=-eabi
+ ;;
+ *)
+ os=-elf
+ ;;
+ esac
+ ;;
+ -nacl*)
+ ;;
+ -ios)
+ ;;
+ -none)
+ ;;
+ *)
+ # Get rid of the `-' at the beginning of $os.
+ os=`echo $os | sed 's/[^-]*-//'`
+ echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+ score-*)
+ os=-elf
+ ;;
+ spu-*)
+ os=-elf
+ ;;
+ *-acorn)
+ os=-riscix1.2
+ ;;
+ arm*-rebel)
+ os=-linux
+ ;;
+ arm*-semi)
+ os=-aout
+ ;;
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ c8051-*)
+ os=-elf
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=-tops20
+ ;;
+ pdp11-*)
+ os=-none
+ ;;
+ *-dec | vax-*)
+ os=-ultrix4.2
+ ;;
+ m68*-apollo)
+ os=-domain
+ ;;
+ i386-sun)
+ os=-sunos4.0.2
+ ;;
+ m68000-sun)
+ os=-sunos3
+ ;;
+ m68*-cisco)
+ os=-aout
+ ;;
+ mep-*)
+ os=-elf
+ ;;
+ mips*-cisco)
+ os=-elf
+ ;;
+ mips*-*)
+ os=-elf
+ ;;
+ or32-*)
+ os=-coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=-sysv3
+ ;;
+ sparc-* | *-sun)
+ os=-sunos4.1.1
+ ;;
+ pru-*)
+ os=-elf
+ ;;
+ *-be)
+ os=-beos
+ ;;
+ *-ibm)
+ os=-aix
+ ;;
+ *-knuth)
+ os=-mmixware
+ ;;
+ *-wec)
+ os=-proelf
+ ;;
+ *-winbond)
+ os=-proelf
+ ;;
+ *-oki)
+ os=-proelf
+ ;;
+ *-hp)
+ os=-hpux
+ ;;
+ *-hitachi)
+ os=-hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=-sysv
+ ;;
+ *-cbm)
+ os=-amigaos
+ ;;
+ *-dg)
+ os=-dgux
+ ;;
+ *-dolphin)
+ os=-sysv3
+ ;;
+ m68k-ccur)
+ os=-rtu
+ ;;
+ m88k-omron*)
+ os=-luna
+ ;;
+ *-next)
+ os=-nextstep
+ ;;
+ *-sequent)
+ os=-ptx
+ ;;
+ *-crds)
+ os=-unos
+ ;;
+ *-ns)
+ os=-genix
+ ;;
+ i370-*)
+ os=-mvs
+ ;;
+ *-gould)
+ os=-sysv
+ ;;
+ *-highlevel)
+ os=-bsd
+ ;;
+ *-encore)
+ os=-bsd
+ ;;
+ *-sgi)
+ os=-irix
+ ;;
+ *-siemens)
+ os=-sysv4
+ ;;
+ *-masscomp)
+ os=-rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=-uxpv
+ ;;
+ *-rom68k)
+ os=-coff
+ ;;
+ *-*bug)
+ os=-coff
+ ;;
+ *-apple)
+ os=-macos
+ ;;
+ *-atari*)
+ os=-mint
+ ;;
+ *)
+ os=-none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+ *-unknown)
+ case $os in
+ -riscix*)
+ vendor=acorn
+ ;;
+ -sunos*)
+ vendor=sun
+ ;;
+ -cnk*|-aix*)
+ vendor=ibm
+ ;;
+ -beos*)
+ vendor=be
+ ;;
+ -hpux*)
+ vendor=hp
+ ;;
+ -mpeix*)
+ vendor=hp
+ ;;
+ -hiux*)
+ vendor=hitachi
+ ;;
+ -unos*)
+ vendor=crds
+ ;;
+ -dgux*)
+ vendor=dg
+ ;;
+ -luna*)
+ vendor=omron
+ ;;
+ -genix*)
+ vendor=ns
+ ;;
+ -mvs* | -opened*)
+ vendor=ibm
+ ;;
+ -os400*)
+ vendor=ibm
+ ;;
+ -ptx*)
+ vendor=sequent
+ ;;
+ -tpf*)
+ vendor=ibm
+ ;;
+ -vxsim* | -vxworks* | -windiss*)
+ vendor=wrs
+ ;;
+ -aux*)
+ vendor=apple
+ ;;
+ -hms*)
+ vendor=hitachi
+ ;;
+ -mpw* | -macos*)
+ vendor=apple
+ ;;
+ -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ vendor=atari
+ ;;
+ -vos*)
+ vendor=stratus
+ ;;
+ esac
+ basic_machine=`echo "$basic_machine" | sed "s/unknown/$vendor/"`
+ ;;
+esac
+
+echo "$basic_machine$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'write-file-functions 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755
index 0000000..cda0340
--- /dev/null
+++ b/configure
@@ -0,0 +1,22159 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for knot 3.2.6.
+#
+# Report bugs to <knot-dns@labs.nic.cz>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1
+
+ test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: knot-dns@labs.nic.cz about your system, including any
+$0: error possibly output before this message. Then install
+$0: a modern shell, or manually run the script under such a
+$0: shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='knot'
+PACKAGE_TARNAME='knot'
+PACKAGE_VERSION='3.2.6'
+PACKAGE_STRING='knot 3.2.6'
+PACKAGE_BUGREPORT='knot-dns@labs.nic.cz'
+PACKAGE_URL=''
+
+ac_unique_file="src/knot"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_list=
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+HAVE_MAKEINFO_FALSE
+HAVE_MAKEINFO_TRUE
+HAVE_PDFLATEX_FALSE
+HAVE_PDFLATEX_TRUE
+HAVE_SPHINXBUILD_FALSE
+HAVE_SPHINXBUILD_TRUE
+PDFLATEX
+SPHINXBUILD
+OSS_FUZZ_FALSE
+OSS_FUZZ_TRUE
+FUZZER_FALSE
+FUZZER_TRUE
+fuzzer_LDFLAGS
+fuzzer_CFLAGS
+GENHTML
+LCOV
+CODE_COVERAGE_ENABLED
+CODE_COVERAGE_ENABLED_FALSE
+CODE_COVERAGE_ENABLED_TRUE
+HAVE_VISIBILITY
+CFLAG_VISIBILITY
+USE_GNUTLS_MEMSET_FALSE
+USE_GNUTLS_MEMSET_TRUE
+math_LIBS
+dlopen_LIBS
+pthread_LIBS
+cap_ng_LIBS
+cap_ng_CFLAGS
+libmnl_LIBS
+libmnl_CFLAGS
+libnghttp2_LIBS
+libnghttp2_CFLAGS
+libidn_LIBS
+libidn_CFLAGS
+libidn2_LIBS
+libidn2_CFLAGS
+embedded_libngtcp2_LIBS
+embedded_libngtcp2_CFLAGS
+ENABLE_QUIC_FALSE
+ENABLE_QUIC_TRUE
+EMBEDDED_LIBNGTCP2_FALSE
+EMBEDDED_LIBNGTCP2_TRUE
+libngtcp2_LIBS
+libngtcp2_CFLAGS
+libedit_LIBS
+libedit_CFLAGS
+conf_mapsize
+lmdb_LIBS
+lmdb_CFLAGS
+HAVE_MAXMINDDB_FALSE
+HAVE_MAXMINDDB_TRUE
+libmaxminddb_LIBS
+libmaxminddb_CFLAGS
+HAVE_LIBDNSTAP_FALSE
+HAVE_LIBDNSTAP_TRUE
+HAVE_DNSTAP_FALSE
+HAVE_DNSTAP_TRUE
+DNSTAP_LIBS
+DNSTAP_CFLAGS
+libprotobuf_c_LIBS
+libprotobuf_c_CFLAGS
+libfstrm_LIBS
+libfstrm_CFLAGS
+PROTOC_C
+DOC_MODULES
+STATIC_MODULES_INIT
+STATIC_MODULES_DECLARS
+SHARED_MODULE_whoami_FALSE
+SHARED_MODULE_whoami_TRUE
+STATIC_MODULE_whoami_FALSE
+STATIC_MODULE_whoami_TRUE
+SHARED_MODULE_synthrecord_FALSE
+SHARED_MODULE_synthrecord_TRUE
+STATIC_MODULE_synthrecord_FALSE
+STATIC_MODULE_synthrecord_TRUE
+SHARED_MODULE_stats_FALSE
+SHARED_MODULE_stats_TRUE
+STATIC_MODULE_stats_FALSE
+STATIC_MODULE_stats_TRUE
+SHARED_MODULE_rrl_FALSE
+SHARED_MODULE_rrl_TRUE
+STATIC_MODULE_rrl_FALSE
+STATIC_MODULE_rrl_TRUE
+SHARED_MODULE_queryacl_FALSE
+SHARED_MODULE_queryacl_TRUE
+STATIC_MODULE_queryacl_FALSE
+STATIC_MODULE_queryacl_TRUE
+SHARED_MODULE_probe_FALSE
+SHARED_MODULE_probe_TRUE
+STATIC_MODULE_probe_FALSE
+STATIC_MODULE_probe_TRUE
+SHARED_MODULE_onlinesign_FALSE
+SHARED_MODULE_onlinesign_TRUE
+STATIC_MODULE_onlinesign_FALSE
+STATIC_MODULE_onlinesign_TRUE
+SHARED_MODULE_noudp_FALSE
+SHARED_MODULE_noudp_TRUE
+STATIC_MODULE_noudp_FALSE
+STATIC_MODULE_noudp_TRUE
+SHARED_MODULE_geoip_FALSE
+SHARED_MODULE_geoip_TRUE
+STATIC_MODULE_geoip_FALSE
+STATIC_MODULE_geoip_TRUE
+SHARED_MODULE_dnstap_FALSE
+SHARED_MODULE_dnstap_TRUE
+STATIC_MODULE_dnstap_FALSE
+STATIC_MODULE_dnstap_TRUE
+SHARED_MODULE_dnsproxy_FALSE
+SHARED_MODULE_dnsproxy_TRUE
+STATIC_MODULE_dnsproxy_FALSE
+STATIC_MODULE_dnsproxy_TRUE
+SHARED_MODULE_cookies_FALSE
+SHARED_MODULE_cookies_TRUE
+STATIC_MODULE_cookies_FALSE
+STATIC_MODULE_cookies_TRUE
+liburcu_PKGCONFIG
+liburcu_LIBS
+liburcu_CFLAGS
+malloc_LIBS
+libkqueue_LIBS
+libkqueue_CFLAGS
+systemd_LIBS
+systemd_CFLAGS
+libxdp_LIBS
+libxdp_CFLAGS
+embedded_libbpf_LIBS
+embedded_libbpf_CFLAGS
+ENABLE_XDP_FALSE
+ENABLE_XDP_TRUE
+EMBEDDED_LIBBPF_FALSE
+EMBEDDED_LIBBPF_TRUE
+libelf_LIBS
+libelf_CFLAGS
+libbpf_LIBS
+libbpf_CFLAGS
+ENABLE_PKCS11_FALSE
+ENABLE_PKCS11_TRUE
+gnutls_LIBS
+gnutls_CFLAGS
+FAST_PARSER_FALSE
+FAST_PARSER_TRUE
+HAVE_LIBUTILS_FALSE
+HAVE_LIBUTILS_TRUE
+HAVE_DOCS_FALSE
+HAVE_DOCS_TRUE
+HAVE_UTILS_FALSE
+HAVE_UTILS_TRUE
+HAVE_DAEMON_FALSE
+HAVE_DAEMON_TRUE
+module_dir
+module_instdir
+config_dir
+storage_dir
+run_dir
+pkgconfigdir
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
+LT_NO_UNDEFINED
+LT_SYS_LIBRARY_PATH
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+RANLIB
+DLLTOOL
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+LIBTOOL
+ac_ct_AR
+AR
+LDFLAG_EXCLUDE_LIBS
+RELEASE_DATE
+SED
+KNOT_VERSION_PATCH
+KNOT_VERSION_MINOR
+KNOT_VERSION_MAJOR
+libzscanner_SONAME
+libzscanner_SOVERSION
+libzscanner_VERSION_INFO
+libdnssec_SONAME
+libdnssec_SOVERSION
+libdnssec_VERSION_INFO
+libknot_SONAME
+libknot_SOVERSION
+libknot_VERSION_INFO
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+EGREP
+GREP
+CPP
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL
+am__quote'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_aix_soname
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+with_pkgconfigdir
+with_rundir
+with_storage
+with_configdir
+with_moduledir
+enable_daemon
+enable_modules
+enable_utilities
+enable_documentation
+enable_fastparser
+enable_recvmmsg
+enable_xdp
+enable_reuseport
+enable_systemd
+with_socket_polling
+with_memory_allocator
+with_urcu
+with_module_cookies
+with_module_dnsproxy
+with_module_dnstap
+with_module_geoip
+with_module_noudp
+with_module_onlinesign
+with_module_probe
+with_module_queryacl
+with_module_rrl
+with_module_stats
+with_module_synthrecord
+with_module_whoami
+enable_dnstap
+enable_maxminddb
+with_lmdb
+with_conf_mapsize
+enable_quic
+with_libidn
+with_libnghttp2
+enable_cap_ng
+enable_code_coverage
+with_sanitizer
+with_fuzzer
+with_oss_fuzz
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+LT_SYS_LIBRARY_PATH
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+gnutls_CFLAGS
+gnutls_LIBS
+libbpf_CFLAGS
+libbpf_LIBS
+libelf_CFLAGS
+libelf_LIBS
+libxdp_CFLAGS
+libxdp_LIBS
+systemd_CFLAGS
+systemd_LIBS
+libkqueue_CFLAGS
+libkqueue_LIBS
+liburcu_CFLAGS
+liburcu_LIBS
+libfstrm_CFLAGS
+libfstrm_LIBS
+libprotobuf_c_CFLAGS
+libprotobuf_c_LIBS
+libmaxminddb_CFLAGS
+libmaxminddb_LIBS
+lmdb_CFLAGS
+lmdb_LIBS
+libedit_CFLAGS
+libedit_LIBS
+libngtcp2_CFLAGS
+libngtcp2_LIBS
+libidn2_CFLAGS
+libidn2_LIBS
+libidn_CFLAGS
+libidn_LIBS
+libnghttp2_CFLAGS
+libnghttp2_LIBS
+libmnl_CFLAGS
+libmnl_LIBS
+cap_ng_CFLAGS
+cap_ng_LIBS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir runstatedir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures knot 3.2.6 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/knot]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of knot 3.2.6:";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-silent-rules less verbose build output (undo: "make V=1")
+ --disable-silent-rules verbose build output (undo: "make V=0")
+ --enable-dependency-tracking
+ do not reject slow dependency extractors
+ --disable-dependency-tracking
+ speeds up one-time build
+ --enable-shared[=PKGS] build shared libraries [default=yes]
+ --enable-static[=PKGS] build static libraries [default=yes]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --disable-daemon Don't build Knot DNS main daemon
+ --disable-modules Don't build Knot DNS modules
+ --disable-utilities Don't build Knot DNS utilities
+ --disable-documentation Don't build Knot DNS documentation
+ --disable-fastparser Disable use of fast zone parser
+ --enable-recvmmsg=auto|yes|no
+ enable recvmmsg() network API [default=auto]
+ --enable-xdp=auto|yes|no
+ enable eXpress Data Path [default=auto]
+ --enable-reuseport=auto|yes|no
+ enable SO_REUSEPORT(_LB) support [default=auto]
+ --enable-systemd=auto|yes|no
+ enable systemd integration [default=auto]
+ --enable-dnstap Enable dnstap support for kdig (requires fstrm,
+ protobuf-c)
+ --enable-maxminddb=auto|yes|no
+ enable MaxMind DB [default=auto]
+ --enable-quic=auto|yes|no
+ Support DoQ (needs libngtcp2 >= 0.13.0, gnutls >=
+ 3.7.2) [default=auto]
+ --enable-cap-ng=auto|no enable POSIX capabilities [default=auto]
+ --enable-code-coverage enable code coverage testing with gcov
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-aix-soname=aix|svr4|both
+ shared library versioning (aka "SONAME") variant to
+ provide on AIX, [default=aix].
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the
+ compiler's sysroot if not specified).
+ --with-pkgconfigdir pkg-config installation directory
+ ['${libdir}/pkgconfig']
+ --with-rundir=path Path to run-time variable data (pid, sockets...).
+ [default=LOCALSTATEDIR/run/knot]
+ --with-storage=path Default storage directory (slave zones, persistent
+ data). [default=LOCALSTATEDIR/lib/knot]
+ --with-configdir=path Default directory for configuration.
+ [default=SYSCONFDIR/knot]
+ --with-moduledir=path Path to auto-loaded dynamic modules. [default not
+ set]
+ --with-socket-polling=auto|poll|epoll|kqueue|libkqueue
+ Use specific socket polling method [default=auto]
+ --with-memory-allocator=auto|LIBRARY
+ Use specific memory allocator for the server (e.g.
+ jemalloc) [default=auto]
+ --with-urcu=DIR where to find userspace-rcu library
+ --with-module-cookies=yes|shared|no
+ Build 'cookies' module [default="yes"]
+ --with-module-dnsproxy=yes|shared|no
+ Build 'dnsproxy' module [default="yes"]
+ --with-module-dnstap=yes|shared|no
+ Build 'dnstap' module [default="no"]
+ --with-module-geoip=yes|shared|no
+ Build 'geoip' module [default="yes"]
+ --with-module-noudp=yes|shared|no
+ Build 'noudp' module [default="yes"]
+ --with-module-onlinesign=yes|shared|no
+ Build 'onlinesign' module [default="yes"]
+ --with-module-probe=yes|shared|no
+ Build 'probe' module [default="yes"]
+ --with-module-queryacl=yes|shared|no
+ Build 'queryacl' module [default="yes"]
+ --with-module-rrl=yes|shared|no
+ Build 'rrl' module [default="yes"]
+ --with-module-stats=yes|shared|no
+ Build 'stats' module [default="yes"]
+ --with-module-synthrecord=yes|shared|no
+ Build 'synthrecord' module [default="yes"]
+ --with-module-whoami=yes|shared|no
+ Build 'whoami' module [default="yes"]
+ --with-lmdb=DIR explicit location where to find LMDB
+
+ --with-conf-mapsize=NUM Configuration DB mapsize in MiB
+ [default=$conf_mapsize_default]
+ --with-libidn=DIR Support IDN (needs GNU libidn2 or libidn)
+ --with-libnghttp2=DIR Support DoH (needs libnghttp2)
+ --with-sanitizer Compile with sanitizer [default=no]
+ --with-fuzzer Compile with libfuzzer [default=no]
+ --with-oss-fuzz Link for oss-fuzz environment [default=no]
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CPP C preprocessor
+ LT_SYS_LIBRARY_PATH
+ User-defined run-time library search path.
+ PKG_CONFIG path to pkg-config utility
+ PKG_CONFIG_PATH
+ directories to add to pkg-config's search path
+ PKG_CONFIG_LIBDIR
+ path overriding pkg-config's built-in search path
+ gnutls_CFLAGS
+ C compiler flags for gnutls, overriding pkg-config
+ gnutls_LIBS linker flags for gnutls, overriding pkg-config
+ libbpf_CFLAGS
+ C compiler flags for libbpf, overriding pkg-config
+ libbpf_LIBS linker flags for libbpf, overriding pkg-config
+ libelf_CFLAGS
+ C compiler flags for libelf, overriding pkg-config
+ libelf_LIBS linker flags for libelf, overriding pkg-config
+ libxdp_CFLAGS
+ C compiler flags for libxdp, overriding pkg-config
+ libxdp_LIBS linker flags for libxdp, overriding pkg-config
+ systemd_CFLAGS
+ C compiler flags for systemd, overriding pkg-config
+ systemd_LIBS
+ linker flags for systemd, overriding pkg-config
+ libkqueue_CFLAGS
+ C compiler flags for libkqueue, overriding pkg-config
+ libkqueue_LIBS
+ linker flags for libkqueue, overriding pkg-config
+ liburcu_CFLAGS
+ C compiler flags for liburcu, overriding pkg-config
+ liburcu_LIBS
+ linker flags for liburcu, overriding pkg-config
+ libfstrm_CFLAGS
+ C compiler flags for libfstrm, overriding pkg-config
+ libfstrm_LIBS
+ linker flags for libfstrm, overriding pkg-config
+ libprotobuf_c_CFLAGS
+ C compiler flags for libprotobuf_c, overriding pkg-config
+ libprotobuf_c_LIBS
+ linker flags for libprotobuf_c, overriding pkg-config
+ libmaxminddb_CFLAGS
+ C compiler flags for libmaxminddb, overriding pkg-config
+ libmaxminddb_LIBS
+ linker flags for libmaxminddb, overriding pkg-config
+ lmdb_CFLAGS C compiler flags for lmdb, overriding pkg-config
+ lmdb_LIBS linker flags for lmdb, overriding pkg-config
+ libedit_CFLAGS
+ C compiler flags for libedit, overriding pkg-config
+ libedit_LIBS
+ linker flags for libedit, overriding pkg-config
+ libngtcp2_CFLAGS
+ C compiler flags for libngtcp2, overriding pkg-config
+ libngtcp2_LIBS
+ linker flags for libngtcp2, overriding pkg-config
+ libidn2_CFLAGS
+ C compiler flags for libidn2, overriding pkg-config
+ libidn2_LIBS
+ linker flags for libidn2, overriding pkg-config
+ libidn_CFLAGS
+ C compiler flags for libidn, overriding pkg-config
+ libidn_LIBS linker flags for libidn, overriding pkg-config
+ libnghttp2_CFLAGS
+ C compiler flags for libnghttp2, overriding pkg-config
+ libnghttp2_LIBS
+ linker flags for libnghttp2, overriding pkg-config
+ libmnl_CFLAGS
+ C compiler flags for libmnl, overriding pkg-config
+ libmnl_LIBS linker flags for libmnl, overriding pkg-config
+ cap_ng_CFLAGS
+ C compiler flags for cap_ng, overriding pkg-config
+ cap_ng_LIBS linker flags for cap_ng, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <knot-dns@labs.nic.cz>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+knot configure 3.2.6
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## ----------------------------------- ##
+## Report this to knot-dns@labs.nic.cz ##
+## ----------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
+# ---------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly.
+ac_fn_c_check_decl ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ as_decl_name=`echo $2|sed 's/ *(.*//'`
+ as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+$as_echo_n "checking whether $as_decl_name is declared... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+ (void) $as_decl_use;
+#else
+ (void) $as_decl_name;
+#endif
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_decl
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by knot $as_me 3.2.6, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+as_fn_append ac_header_list " pthread_np.h"
+as_fn_append ac_header_list " stdatomic.h"
+as_fn_append ac_header_list " sys/uio.h"
+as_fn_append ac_header_list " bsd/string.h"
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+am__api_version='1.16'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+ alias in your environment" "$LINENO" 5
+ fi
+ if test "$2" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ MISSING="\${SHELL} '$am_aux_dir/missing'"
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+ enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='knot'
+ VERSION='3.2.6'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+ fi
+fi
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+ enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=0;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+
+ac_config_headers="$ac_config_headers src/config.h"
+
+
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
+am__doit:
+ @echo this is the am__doit target >confinc.out
+.PHONY: am__doit
+END
+am__include="#"
+am__quote=
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+ (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+ case $?:`cat confinc.out 2>/dev/null` in #(
+ '0:this is the am__doit target') :
+ case $s in #(
+ BSD) :
+ am__include='.include' am__quote='"' ;; #(
+ *) :
+ am__include='include' am__quote='' ;;
+esac ;; #(
+ *) :
+ ;;
+esac
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+$as_echo "${_am_result}" >&6; }
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+ am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+ ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+ ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
+if test "x$ac_cv_header_minix_config_h" = xyes; then :
+ MINIX=yes
+else
+ MINIX=
+fi
+
+
+ if test "$MINIX" = yes; then
+
+$as_echo "#define _POSIX_SOURCE 1" >>confdefs.h
+
+
+$as_echo "#define _POSIX_1_SOURCE 2" >>confdefs.h
+
+
+$as_echo "#define _MINIX 1" >>confdefs.h
+
+ fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if ${ac_cv_safe_to_define___extensions__+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# define __EXTENSIONS__ 1
+ $ac_includes_default
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_safe_to_define___extensions__=yes
+else
+ ac_cv_safe_to_define___extensions__=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+$as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
+ test $ac_cv_safe_to_define___extensions__ = yes &&
+ $as_echo "#define __EXTENSIONS__ 1" >>confdefs.h
+
+ $as_echo "#define _ALL_SOURCE 1" >>confdefs.h
+
+ $as_echo "#define _GNU_SOURCE 1" >>confdefs.h
+
+ $as_echo "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+ $as_echo "#define _TANDEM_SOURCE 1" >>confdefs.h
+
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+# Update library versions
+# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+
+ libknot_VERSION_INFO="-version-info 13:0:0"
+
+ libknot_SOVERSION="13"
+
+ case $host_os in #(
+ darwin*) :
+ libknot_SONAME="libknot.13.dylib"
+ ;; #(
+ cygwin*) :
+ libknot_SONAME="cygknot-13.dll"
+ ;; #(
+ msys*) :
+ libknot_SONAME="msys-knot-13.dll"
+ ;; #(
+ *) :
+ libknot_SONAME="libknot.so.13"
+
+ ;; #(
+ *) :
+ ;;
+esac
+
+
+ libdnssec_VERSION_INFO="-version-info 9:0:0"
+
+ libdnssec_SOVERSION="9"
+
+ case $host_os in #(
+ darwin*) :
+ libdnssec_SONAME="libdnssec.9.dylib"
+ ;; #(
+ cygwin*) :
+ libdnssec_SONAME="cygdnssec-9.dll"
+ ;; #(
+ msys*) :
+ libdnssec_SONAME="msys-dnssec-9.dll"
+ ;; #(
+ *) :
+ libdnssec_SONAME="libdnssec.so.9"
+
+ ;; #(
+ *) :
+ ;;
+esac
+
+
+ libzscanner_VERSION_INFO="-version-info 4:0:0"
+
+ libzscanner_SOVERSION="4"
+
+ case $host_os in #(
+ darwin*) :
+ libzscanner_SONAME="libzscanner.4.dylib"
+ ;; #(
+ cygwin*) :
+ libzscanner_SONAME="cygzscanner-4.dll"
+ ;; #(
+ msys*) :
+ libzscanner_SONAME="msys-zscanner-4.dll"
+ ;; #(
+ *) :
+ libzscanner_SONAME="libzscanner.so.4"
+
+ ;; #(
+ *) :
+ ;;
+esac
+
+
+KNOT_VERSION_MAJOR=3
+
+KNOT_VERSION_MINOR=2
+
+KNOT_VERSION_PATCH=6
+
+
+# Store ./configure parameters and CFLAGS
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGURE_PARAMS "$*"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGURE_CFLAGS "$CFLAGS"
+_ACEOF
+
+
+ac_config_files="$ac_config_files src/libknot/version.h src/libdnssec/version.h src/libzscanner/version.h"
+
+
+# Automatically update release date based on NEWS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+release_date=$($SED -n 's/^Knot DNS .* (\(.*\))/\1/p;q;' ${srcdir}/NEWS)
+RELEASE_DATE=$release_date
+
+
+# Set compiler compatibility flags
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+ ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+ac_c_preproc_warn_flag=yes
+
+# Set default CFLAGS
+CFLAGS="$CFLAGS -Wall -Wshadow -Werror=format-security -Werror=implicit -Werror=attributes -Wstrict-prototypes"
+
+as_CACHEVAR=`$as_echo "ax_cv_check_cflags_"-Werror"_"-fpredictive-commoning"" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts \"-fpredictive-commoning\"" >&5
+$as_echo_n "checking whether C compiler accepts \"-fpredictive-commoning\"... " >&6; }
+if eval \${$as_CACHEVAR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ax_check_save_flags=$CFLAGS
+ CFLAGS="$CFLAGS "-Werror" "-fpredictive-commoning""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_CACHEVAR=yes"
+else
+ eval "$as_CACHEVAR=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS=$ax_check_save_flags
+fi
+eval ac_res=\$$as_CACHEVAR
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if test x"`eval 'as_val=${'$as_CACHEVAR'};$as_echo "$as_val"'`" = xyes; then :
+ CFLAGS="$CFLAGS -fpredictive-commoning"
+else
+ :
+fi
+
+as_CACHEVAR=`$as_echo "ax_cv_check_ldflags_""_"-Wl,--exclude-libs,ALL"" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the linker accepts \"-Wl,--exclude-libs,ALL\"" >&5
+$as_echo_n "checking whether the linker accepts \"-Wl,--exclude-libs,ALL\"... " >&6; }
+if eval \${$as_CACHEVAR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ax_check_save_flags=$LDFLAGS
+ LDFLAGS="$LDFLAGS "" "-Wl,--exclude-libs,ALL""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$as_CACHEVAR=yes"
+else
+ eval "$as_CACHEVAR=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$ax_check_save_flags
+fi
+eval ac_res=\$$as_CACHEVAR
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if test x"`eval 'as_val=${'$as_CACHEVAR'};$as_echo "$as_val"'`" = xyes; then :
+ ldflag_exclude_libs="-Wl,--exclude-libs,ALL"
+else
+ ldflag_exclude_libs=""
+fi
+
+LDFLAG_EXCLUDE_LIBS=$ldflag_exclude_libs
+
+
+# Get processor byte ordering
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_bigendian=unknown
+ # See if we're dealing with a universal compiler.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __APPLE_CC__
+ not a universal capable compiler
+ #endif
+ typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+ # Check for potential -arch flags. It is not universal unless
+ # there are at least two -arch flags with different values.
+ ac_arch=
+ ac_prev=
+ for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+ if test -n "$ac_prev"; then
+ case $ac_word in
+ i?86 | x86_64 | ppc | ppc64)
+ if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+ ac_arch=$ac_word
+ else
+ ac_cv_c_bigendian=universal
+ break
+ fi
+ ;;
+ esac
+ ac_prev=
+ elif test "x$ac_word" = "x-arch"; then
+ ac_prev=arch
+ fi
+ done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if sys/param.h defines the BYTE_ORDER macro.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+ && LITTLE_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_bigendian=yes
+else
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ # It does; now see whether it defined to _BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_bigendian=yes
+else
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # Compile a test program.
+ if test "$cross_compiling" = yes; then :
+ # Try to guess by grepping values from an object file.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+short int ascii_mm[] =
+ { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+ short int ascii_ii[] =
+ { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+ int use_ascii (int i) {
+ return ascii_mm[i] + ascii_ii[i];
+ }
+ short int ebcdic_ii[] =
+ { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+ short int ebcdic_mm[] =
+ { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+ int use_ebcdic (int i) {
+ return ebcdic_mm[i] + ebcdic_ii[i];
+ }
+ extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+ ac_cv_c_bigendian=yes
+ fi
+ if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+ u.l = 1;
+ return u.c[sizeof (long int) - 1] == 1;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_c_bigendian=no
+else
+ ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+ yes)
+ endianity=big-endian;; #(
+ no)
+ endianity=little-endian ;; #(
+ universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+ ;; #(
+ *)
+ as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+if test "$endianity" = "little-endian"; then :
+
+
+$as_echo "#define ENDIANITY_LITTLE 1" >>confdefs.h
+
+fi
+
+# Check if an archiver is available
+if test -n "$ac_tool_prefix"; then
+ for ac_prog in ar lib "link -lib"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AR" && break
+ done
+fi
+if test -z "$AR"; then
+ ac_ct_AR=$AR
+ for ac_prog in ar lib "link -lib"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_AR" && break
+done
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+fi
+
+: ${AR=ar}
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5
+$as_echo_n "checking the archiver ($AR) interface... " >&6; }
+if ${am_cv_ar_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ am_cv_ar_interface=ar
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int some_variable = 0;
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5
+ (eval $am_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test "$ac_status" -eq 0; then
+ am_cv_ar_interface=ar
+ else
+ am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5
+ (eval $am_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test "$ac_status" -eq 0; then
+ am_cv_ar_interface=lib
+ else
+ am_cv_ar_interface=unknown
+ fi
+ fi
+ rm -f conftest.lib libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5
+$as_echo "$am_cv_ar_interface" >&6; }
+
+case $am_cv_ar_interface in
+ar)
+ ;;
+lib)
+ # Microsoft lib, so override with the ar-lib wrapper script.
+ # FIXME: It is wrong to rewrite AR.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__AR in this case,
+ # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
+ # similar.
+ AR="$am_aux_dir/ar-lib $AR"
+ ;;
+unknown)
+ as_fn_error $? "could not determine $AR interface" "$LINENO" 5
+ ;;
+esac
+
+
+
+
+# Initialize libtool
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.6'
+macro_revision='2.4.6'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO ""
+}
+
+case $ECHO in
+ printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+ print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+ *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi
+fi
+
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in dumpbin "link -dump"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in dumpbin "link -dump"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+ ac_ct_DLLTOOL=$DLLTOOL
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DLLTOOL"; then
+ ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DLLTOOL" = x; then
+ DLLTOOL="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DLLTOOL=$ac_ct_DLLTOOL
+ fi
+else
+ DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ for ac_prog in ar
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AR" && break
+ done
+fi
+if test -z "$AR"; then
+ ac_ct_AR=$AR
+ for ac_prog in ar
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_AR" && break
+done
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cr}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ar_at_file=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&5
+ if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&5 && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+ withval=$with_sysroot;
+else
+ with_sysroot=no
+fi
+
+
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+$as_echo "$with_sysroot" >&6; }
+ as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+ ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+$as_echo_n "checking for a working dd... " >&6; }
+if ${ac_cv_path_lt_DD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+ ac_path_lt_DD_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in dd; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+ $ac_path_lt_DD_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_lt_DD"; then
+ :
+ fi
+else
+ ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+$as_echo "$ac_cv_path_lt_DD" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+$as_echo_n "checking how to truncate binary pipes... " >&6; }
+if ${lt_cv_truncate_bin+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+$as_echo "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$MANIFEST_TOOL"; then
+ ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+ ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+ # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_MANIFEST_TOOL"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_MANIFEST_TOOL" = x; then
+ MANIFEST_TOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+ fi
+else
+ MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&5
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&5
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+ echo "$AR cr libconftest.a conftest.o" >&5
+ $AR cr libconftest.a conftest.o 2>&5
+ echo "$RANLIB libconftest.a" >&5
+ $RANLIB libconftest.a 2>&5
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&5
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[912]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[012][,.]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*|11.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ # Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ pic_mode=default
+fi
+
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+ shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[5-9]*,yes)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
+$as_echo_n "checking which variant of shared library versioning to provide... " >&6; }
+
+# Check whether --with-aix-soname was given.
+if test "${with_aix_soname+set}" = set; then :
+ withval=$with_aix_soname; case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname
+else
+ if ${lt_cv_with_aix_soname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_with_aix_soname=aix
+fi
+
+ with_aix_soname=$lt_cv_with_aix_soname
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
+$as_echo "$with_aix_soname" >&6; }
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/${ac_tool_prefix}file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC=$CC
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+ *)
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+ esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+ if test yes = "$GCC"; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ lt_prog_compiler_static=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ lt_prog_compiler_wl='-Xlinker '
+ if test -n "$lt_prog_compiler_pic"; then
+ lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # flang / f18. f95 an alias for gfortran or flang on Debian
+ flang* | f18* | f95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Intel*\ [CF]*Compiler*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ *Portland\ Group*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_static_works"; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ link_all_deplibs=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+ *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ export_dynamic_flag_spec='$wl--export-all-symbols'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+ exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ haiku*)
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ link_all_deplibs=yes
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ export_dynamic_flag_spec='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test no = "$ld_shlibs"; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ export_dynamic_flag_spec='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' $wl-bernotok'
+ allow_undefined_flag=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ fi
+ archive_cmds_need_lc=yes
+ archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ file_list_spec='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+ enable_shared_with_static_runtimes=yes
+ exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ old_postinstall_cmds='chmod 644 $oldlib'
+ postlink_cmds='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ enable_shared_with_static_runtimes=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+ else
+ whole_archive_flag_spec=''
+ fi
+ link_all_deplibs=yes
+ allow_undefined_flag=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler__b=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -b"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler__b=yes
+ fi
+ else
+ lt_cv_prog_compiler__b=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test yes = "$lt_cv_prog_compiler__b"; then
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_irix_exported_symbol=yes
+else
+ lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ link_all_deplibs=no
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ ld_shlibs=yes
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ else
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='$wl-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='$wl-z,text'
+ allow_undefined_flag='$wl-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test no = "$ld_shlibs" && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ lt_cv_archive_cmds_need_lc=no
+ else
+ lt_cv_archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+ archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a(lib.so.V)'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[23].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[3-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+fi
+
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsdelf*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='NetBSD ld.elf_so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test yes = "$hardcode_automatic"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$hardcode_direct" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+ test no != "$hardcode_minus_L"; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test relink = "$hardcode_action" ||
+ test yes = "$inherit_rpath"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report what library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC=$lt_save_CC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+# Build DLLs on Cygwin/MSYS2
+case $host_os in #(
+ cygwin* | msys*) :
+ LT_NO_UNDEFINED="-no-undefined"
+ ;; #(
+ *) :
+ ;;
+esac
+
+
+# Use pkg-config
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+fi
+
+
+
+# Check whether --with-pkgconfigdir was given.
+if test "${with_pkgconfigdir+set}" = set; then :
+ withval=$with_pkgconfigdir;
+else
+ with_pkgconfigdir='${libdir}/pkgconfig'
+fi
+
+pkgconfigdir=$with_pkgconfigdir
+
+
+
+
+ac_config_files="$ac_config_files src/knotd.pc src/libknot.pc src/libdnssec.pc src/libzscanner.pc"
+
+
+# Default directories
+knot_prefix=$ac_default_prefix
+if test "$prefix" != NONE; then :
+ knot_prefix=$prefix
+fi
+
+run_dir="${localstatedir}/run/knot"
+
+# Check whether --with-rundir was given.
+if test "${with_rundir+set}" = set; then :
+ withval=$with_rundir; run_dir=$withval
+fi
+
+
+
+storage_dir="${localstatedir}/lib/knot"
+
+# Check whether --with-storage was given.
+if test "${with_storage+set}" = set; then :
+ withval=$with_storage; storage_dir=$withval
+fi
+
+
+
+config_dir="${sysconfdir}/knot"
+
+# Check whether --with-configdir was given.
+if test "${with_configdir+set}" = set; then :
+ withval=$with_configdir; config_dir=$withval
+fi
+
+
+
+module_dir=
+module_instdir="${libdir}/knot/modules-${KNOT_VERSION_MAJOR}.${KNOT_VERSION_MINOR}"
+
+# Check whether --with-moduledir was given.
+if test "${with_moduledir+set}" = set; then :
+ withval=$with_moduledir; module_dir=$withval module_instdir=$module_dir
+fi
+
+
+
+
+# Build Knot DNS daemon
+# Check whether --enable-daemon was given.
+if test "${enable_daemon+set}" = set; then :
+ enableval=$enable_daemon;
+else
+ enable_daemon=yes
+fi
+
+ if test "$enable_daemon" = "yes"; then
+ HAVE_DAEMON_TRUE=
+ HAVE_DAEMON_FALSE='#'
+else
+ HAVE_DAEMON_TRUE='#'
+ HAVE_DAEMON_FALSE=
+fi
+
+
+# Build Knot DNS modules
+# Check whether --enable-modules was given.
+if test "${enable_modules+set}" = set; then :
+ enableval=$enable_modules;
+else
+ enable_modules=yes
+fi
+
+
+# Build Knot DNS utilities
+# Check whether --enable-utilities was given.
+if test "${enable_utilities+set}" = set; then :
+ enableval=$enable_utilities;
+else
+ enable_utilities=yes
+fi
+
+ if test "$enable_utilities" = "yes"; then
+ HAVE_UTILS_TRUE=
+ HAVE_UTILS_FALSE='#'
+else
+ HAVE_UTILS_TRUE='#'
+ HAVE_UTILS_FALSE=
+fi
+
+
+# Build Knot DNS documentation
+# Check whether --enable-documentation was given.
+if test "${enable_documentation+set}" = set; then :
+ enableval=$enable_documentation;
+else
+ enable_documentation=yes
+fi
+
+ if test "$enable_documentation" = "yes"; then
+ HAVE_DOCS_TRUE=
+ HAVE_DOCS_FALSE='#'
+else
+ HAVE_DOCS_TRUE='#'
+ HAVE_DOCS_FALSE=
+fi
+
+
+ if test "$enable_utilities" != "no" -o \
+ "$enable_daemon" != "no"; then
+ HAVE_LIBUTILS_TRUE=
+ HAVE_LIBUTILS_FALSE='#'
+else
+ HAVE_LIBUTILS_TRUE='#'
+ HAVE_LIBUTILS_FALSE=
+fi
+
+######################
+# Generic dependencies
+######################
+
+# Check whether --enable-fastparser was given.
+if test "${enable_fastparser+set}" = set; then :
+ enableval=$enable_fastparser;
+else
+ if test -d ".git"; then :
+ enable_fastparser=no
+else
+ enable_fastparser=yes
+fi
+
+fi
+
+ if test "$enable_fastparser" = "yes"; then
+ FAST_PARSER_TRUE=
+ FAST_PARSER_FALSE='#'
+else
+ FAST_PARSER_TRUE='#'
+ FAST_PARSER_FALSE=
+fi
+
+
+# GnuTLS crypto backend
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls >= 3.3" >&5
+$as_echo_n "checking for gnutls >= 3.3... " >&6; }
+
+if test -n "$gnutls_CFLAGS"; then
+ pkg_cv_gnutls_CFLAGS="$gnutls_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= 3.3\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "gnutls >= 3.3") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_gnutls_CFLAGS=`$PKG_CONFIG --cflags "gnutls >= 3.3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$gnutls_LIBS"; then
+ pkg_cv_gnutls_LIBS="$gnutls_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gnutls >= 3.3\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "gnutls >= 3.3") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_gnutls_LIBS=`$PKG_CONFIG --libs "gnutls >= 3.3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ gnutls_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gnutls >= 3.3" 2>&1`
+ else
+ gnutls_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gnutls >= 3.3" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$gnutls_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (gnutls >= 3.3) were not met:
+
+$gnutls_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables gnutls_CFLAGS
+and gnutls_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables gnutls_CFLAGS
+and gnutls_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ gnutls_CFLAGS=$pkg_cv_gnutls_CFLAGS
+ gnutls_LIBS=$pkg_cv_gnutls_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ save_CFLAGS=$CFLAGS
+ save_LIBS=$LIBS
+ CFLAGS="$CFLAGS $gnutls_CFLAGS"
+ LIBS="$LIBS $gnutls_LIBS"
+
+ ac_fn_c_check_func "$LINENO" "gnutls_pkcs11_copy_pubkey" "ac_cv_func_gnutls_pkcs11_copy_pubkey"
+if test "x$ac_cv_func_gnutls_pkcs11_copy_pubkey" = xyes; then :
+ enable_pkcs11=yes
+else
+ enable_pkcs11=no
+fi
+
+ if test "$enable_pkcs11" = yes; then :
+
+$as_echo "#define ENABLE_PKCS11 1" >>confdefs.h
+
+fi
+
+ ac_fn_c_check_decl "$LINENO" "GNUTLS_PK_EDDSA_ED25519" "ac_cv_have_decl_GNUTLS_PK_EDDSA_ED25519" "#include <gnutls/gnutls.h>
+"
+if test "x$ac_cv_have_decl_GNUTLS_PK_EDDSA_ED25519" = xyes; then :
+
+$as_echo "#define HAVE_ED25519 1" >>confdefs.h
+
+ enable_ed25519=yes
+else
+ enable_ed25519=no
+fi
+
+
+ ac_fn_c_check_decl "$LINENO" "GNUTLS_SIGN_EDDSA_ED448" "ac_cv_have_decl_GNUTLS_SIGN_EDDSA_ED448" "#include <gnutls/gnutls.h>
+"
+if test "x$ac_cv_have_decl_GNUTLS_SIGN_EDDSA_ED448" = xyes; then :
+
+$as_echo "#define HAVE_ED448 1" >>confdefs.h
+
+ enable_ed448=yes
+else
+ enable_ed448=no
+fi
+
+
+ ac_fn_c_check_decl "$LINENO" "GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE" "ac_cv_have_decl_GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE" "#include <gnutls/abstract.h>
+"
+if test "x$ac_cv_have_decl_GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE" = xyes; then :
+
+$as_echo "#define HAVE_GNUTLS_REPRODUCIBLE 1" >>confdefs.h
+
+ # Ensure the version is at least 3.6.10
+ ac_fn_c_check_func "$LINENO" "gnutls_aead_cipher_encryptv2" "ac_cv_func_gnutls_aead_cipher_encryptv2"
+if test "x$ac_cv_func_gnutls_aead_cipher_encryptv2" = xyes; then :
+ enable_repro_signing=yes
+else
+ enable_repro_signing=no
+fi
+
+else
+ enable_repro_signing=no
+fi
+
+
+ ac_fn_c_check_func "$LINENO" "gnutls_privkey_sign_data2" "ac_cv_func_gnutls_privkey_sign_data2"
+if test "x$ac_cv_func_gnutls_privkey_sign_data2" = xyes; then :
+
+$as_echo "#define HAVE_SIGN_DATA2 1" >>confdefs.h
+
+fi
+
+
+ ac_fn_c_check_func "$LINENO" "gnutls_privkey_export_x509" "ac_cv_func_gnutls_privkey_export_x509"
+if test "x$ac_cv_func_gnutls_privkey_export_x509" = xyes; then :
+
+$as_echo "#define HAVE_EXPORT_X509 1" >>confdefs.h
+
+fi
+
+
+ ac_fn_c_check_func "$LINENO" "gnutls_memset" "ac_cv_func_gnutls_memset"
+if test "x$ac_cv_func_gnutls_memset" = xyes; then :
+
+$as_echo "#define HAVE_GNUTLS_MEMSET 1" >>confdefs.h
+
+ gnutls_memset=yes
+else
+ gnutls_memset=no
+fi
+
+
+ ac_fn_c_check_func "$LINENO" "gnutls_early_cipher_get" "ac_cv_func_gnutls_early_cipher_get"
+if test "x$ac_cv_func_gnutls_early_cipher_get" = xyes; then :
+
+$as_echo "#define HAVE_GNUTLS_QUIC 1" >>confdefs.h
+
+ gnutls_quic=yes
+else
+ gnutls_quic=no
+fi
+
+
+ CFLAGS=$save_CFLAGS
+ LIBS=$save_LIBS
+
+fi
+ if test "$enable_pkcs11" = "yes"; then
+ ENABLE_PKCS11_TRUE=
+ ENABLE_PKCS11_FALSE='#'
+else
+ ENABLE_PKCS11_TRUE='#'
+ ENABLE_PKCS11_FALSE=
+fi
+
+
+# Check whether --enable-recvmmsg was given.
+if test "${enable_recvmmsg+set}" = set; then :
+ enableval=$enable_recvmmsg;
+else
+ enable_recvmmsg=auto
+fi
+
+
+case $enable_recvmmsg in #(
+ auto|yes) :
+
+ ac_fn_c_check_func "$LINENO" "recvmmsg" "ac_cv_func_recvmmsg"
+if test "x$ac_cv_func_recvmmsg" = xyes; then :
+ ac_fn_c_check_func "$LINENO" "sendmmsg" "ac_cv_func_sendmmsg"
+if test "x$ac_cv_func_sendmmsg" = xyes; then :
+ enable_recvmmsg=yes
+else
+ enable_recvmmsg=no
+fi
+
+else
+ enable_recvmmsg=no
+fi
+ ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --enable-recvmmsg.
+ " "$LINENO" 5 ;; #(
+ *) :
+ ;;
+esac
+
+if test "$enable_recvmmsg" = yes; then :
+
+
+$as_echo "#define ENABLE_RECVMMSG 1" >>confdefs.h
+
+fi
+
+# XDP support
+# Check whether --enable-xdp was given.
+if test "${enable_xdp+set}" = set; then :
+ enableval=$enable_xdp;
+else
+ enable_xdp=auto
+fi
+
+
+case $enable_xdp in #(
+ auto) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libbpf >= 0.0.6" >&5
+$as_echo_n "checking for libbpf >= 0.0.6... " >&6; }
+
+if test -n "$libbpf_CFLAGS"; then
+ pkg_cv_libbpf_CFLAGS="$libbpf_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.0.6\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libbpf >= 0.0.6") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libbpf_CFLAGS=`$PKG_CONFIG --cflags "libbpf >= 0.0.6" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libbpf_LIBS"; then
+ pkg_cv_libbpf_LIBS="$libbpf_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.0.6\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libbpf >= 0.0.6") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libbpf_LIBS=`$PKG_CONFIG --libs "libbpf >= 0.0.6" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libbpf_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libbpf >= 0.0.6" 2>&1`
+ else
+ libbpf_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libbpf >= 0.0.6" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libbpf_PKG_ERRORS" >&5
+
+ enable_xdp=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_xdp=no
+else
+ libbpf_CFLAGS=$pkg_cv_libbpf_CFLAGS
+ libbpf_LIBS=$pkg_cv_libbpf_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_xdp=yes
+fi ;; #(
+ yes) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libbpf >= 0.0.6" >&5
+$as_echo_n "checking for libbpf >= 0.0.6... " >&6; }
+
+if test -n "$libbpf_CFLAGS"; then
+ pkg_cv_libbpf_CFLAGS="$libbpf_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.0.6\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libbpf >= 0.0.6") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libbpf_CFLAGS=`$PKG_CONFIG --cflags "libbpf >= 0.0.6" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libbpf_LIBS"; then
+ pkg_cv_libbpf_LIBS="$libbpf_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libbpf >= 0.0.6\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libbpf >= 0.0.6") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libbpf_LIBS=`$PKG_CONFIG --libs "libbpf >= 0.0.6" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libbpf_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libbpf >= 0.0.6" 2>&1`
+ else
+ libbpf_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libbpf >= 0.0.6" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libbpf_PKG_ERRORS" >&5
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libelf" >&5
+$as_echo_n "checking for libelf... " >&6; }
+
+if test -n "$libelf_CFLAGS"; then
+ pkg_cv_libelf_CFLAGS="$libelf_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libelf\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libelf") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libelf_CFLAGS=`$PKG_CONFIG --cflags "libelf" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libelf_LIBS"; then
+ pkg_cv_libelf_LIBS="$libelf_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libelf\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libelf") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libelf_LIBS=`$PKG_CONFIG --libs "libelf" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libelf_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libelf" 2>&1`
+ else
+ libelf_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libelf" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libelf_PKG_ERRORS" >&5
+
+ as_fn_error $? "libelf is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "libelf is required" "$LINENO" 5
+else
+ libelf_CFLAGS=$pkg_cv_libelf_CFLAGS
+ libelf_LIBS=$pkg_cv_libelf_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray"
+if test "x$ac_cv_func_reallocarray" = xyes; then :
+ enable_xdp=embedded
+ embedded_libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf/include -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"
+ embedded_libbpf_LIBS=$libelf_LIBS
+ libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"
+else
+ enable_xdp=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: reallocarray not available" >&5
+$as_echo "$as_me: WARNING: reallocarray not available" >&2;}
+fi
+
+fi
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libelf" >&5
+$as_echo_n "checking for libelf... " >&6; }
+
+if test -n "$libelf_CFLAGS"; then
+ pkg_cv_libelf_CFLAGS="$libelf_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libelf\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libelf") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libelf_CFLAGS=`$PKG_CONFIG --cflags "libelf" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libelf_LIBS"; then
+ pkg_cv_libelf_LIBS="$libelf_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libelf\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libelf") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libelf_LIBS=`$PKG_CONFIG --libs "libelf" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libelf_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libelf" 2>&1`
+ else
+ libelf_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libelf" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libelf_PKG_ERRORS" >&5
+
+ as_fn_error $? "libelf is required" "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "libelf is required" "$LINENO" 5
+else
+ libelf_CFLAGS=$pkg_cv_libelf_CFLAGS
+ libelf_LIBS=$pkg_cv_libelf_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ ac_fn_c_check_func "$LINENO" "reallocarray" "ac_cv_func_reallocarray"
+if test "x$ac_cv_func_reallocarray" = xyes; then :
+ enable_xdp=embedded
+ embedded_libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf/include -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"
+ embedded_libbpf_LIBS=$libelf_LIBS
+ libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"
+else
+ enable_xdp=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: reallocarray not available" >&5
+$as_echo "$as_me: WARNING: reallocarray not available" >&2;}
+fi
+
+fi
+else
+ libbpf_CFLAGS=$pkg_cv_libbpf_CFLAGS
+ libbpf_LIBS=$pkg_cv_libbpf_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_xdp=yes
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --enable-xdp." "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$enable_xdp" = "embedded"; then
+ EMBEDDED_LIBBPF_TRUE=
+ EMBEDDED_LIBBPF_FALSE='#'
+else
+ EMBEDDED_LIBBPF_TRUE='#'
+ EMBEDDED_LIBBPF_FALSE=
+fi
+
+ if test "$enable_xdp" != "no"; then
+ ENABLE_XDP_TRUE=
+ ENABLE_XDP_FALSE='#'
+else
+ ENABLE_XDP_TRUE='#'
+ ENABLE_XDP_FALSE=
+fi
+
+
+
+
+if test "$enable_xdp" == "yes"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libxdp" >&5
+$as_echo_n "checking for libxdp... " >&6; }
+
+if test -n "$libxdp_CFLAGS"; then
+ pkg_cv_libxdp_CFLAGS="$libxdp_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxdp\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libxdp") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libxdp_CFLAGS=`$PKG_CONFIG --cflags "libxdp" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libxdp_LIBS"; then
+ pkg_cv_libxdp_LIBS="$libxdp_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libxdp\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libxdp") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libxdp_LIBS=`$PKG_CONFIG --libs "libxdp" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libxdp_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libxdp" 2>&1`
+ else
+ libxdp_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libxdp" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libxdp_PKG_ERRORS" >&5
+
+ enable_xdp=yes
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_xdp=yes
+else
+ libxdp_CFLAGS=$pkg_cv_libxdp_CFLAGS
+ libxdp_LIBS=$pkg_cv_libxdp_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_xdp=libxdp
+fi
+ if test "$enable_xdp" == "libxdp"; then :
+
+
+$as_echo "#define USE_LIBXDP 1" >>confdefs.h
+
+ libbpf_CFLAGS="$libbpf_CFLAGS $libxdp_CFLAGS"
+ libbpf_LIBS="$libbpf_LIBS $libxdp_LIBS"
+
+fi
+
+fi
+
+
+
+if test "$enable_xdp" != "no"; then :
+
+
+$as_echo "#define ENABLE_XDP 1" >>confdefs.h
+
+fi
+
+# Reuseport support
+case $host_os in #(
+ freebsd*) :
+ reuseport_opt=SO_REUSEPORT_LB ;; #(
+ *) :
+ reuseport_opt=SO_REUSEPORT ;; #(
+ *) :
+ ;;
+esac
+
+# Check whether --enable-reuseport was given.
+if test "${enable_reuseport+set}" = set; then :
+ enableval=$enable_reuseport;
+else
+ enable_reuseport=auto
+
+fi
+
+
+case $enable_reuseport in #(
+ auto) :
+
+ case $host_os in #(
+ freebsd*|linux*) :
+ as_ac_Symbol=`$as_echo "ac_cv_have_decl_$reuseport_opt" | $as_tr_sh`
+ac_fn_c_check_decl "$LINENO" "$reuseport_opt" "$as_ac_Symbol" "#include <sys/socket.h>
+
+"
+if eval test \"x\$"$as_ac_Symbol"\" = x"yes"; then :
+ enable_reuseport=yes
+else
+ enable_reuseport=no
+fi
+ ;; #(
+ *) :
+ enable_reuseport=no
+ ;; #(
+ *) :
+ ;;
+esac ;; #(
+ yes) :
+ as_ac_Symbol=`$as_echo "ac_cv_have_decl_$reuseport_opt" | $as_tr_sh`
+ac_fn_c_check_decl "$LINENO" "$reuseport_opt" "$as_ac_Symbol" "#include <sys/socket.h>
+
+"
+if eval test \"x\$"$as_ac_Symbol"\" = x"yes"; then :
+
+else
+ as_fn_error $? "SO_REUSEPORT(_LB) not supported." "$LINENO" 5
+fi
+ ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --enable-reuseport." "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+
+if test "$enable_reuseport" = yes; then :
+
+
+$as_echo "#define ENABLE_REUSEPORT 1" >>confdefs.h
+
+fi
+
+#########################################
+# Dependencies needed for Knot DNS daemon
+#########################################
+
+# Systemd integration
+# Check whether --enable-systemd was given.
+if test "${enable_systemd+set}" = set; then :
+ enableval=$enable_systemd; enable_systemd="$enableval"
+else
+ enable_systemd=auto
+fi
+
+
+if test "$enable_daemon" = "yes"; then :
+
+
+if test "$enable_systemd" != "no"; then :
+
+ case $enable_systemd in #(
+ auto) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd" >&5
+$as_echo_n "checking for libsystemd... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd-daemon libsystemd-journal" >&5
+$as_echo_n "checking for libsystemd-daemon libsystemd-journal... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+ enable_systemd=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_systemd=no
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_systemd=yes
+fi
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd-daemon libsystemd-journal" >&5
+$as_echo_n "checking for libsystemd-daemon libsystemd-journal... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+ enable_systemd=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_systemd=no
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_systemd=yes
+fi
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_systemd=yes
+fi ;; #(
+ yes) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd" >&5
+$as_echo_n "checking for libsystemd... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd-daemon libsystemd-journal" >&5
+$as_echo_n "checking for libsystemd-daemon libsystemd-journal... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libsystemd-daemon libsystemd-journal) were not met:
+
+$systemd_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables systemd_CFLAGS
+and systemd_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables systemd_CFLAGS
+and systemd_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libsystemd-daemon libsystemd-journal" >&5
+$as_echo_n "checking for libsystemd-daemon libsystemd-journal... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+ pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+ pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libsystemd-daemon libsystemd-journal\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libsystemd-daemon libsystemd-journal") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "libsystemd-daemon libsystemd-journal" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ else
+ systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libsystemd-daemon libsystemd-journal" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$systemd_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libsystemd-daemon libsystemd-journal) were not met:
+
+$systemd_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables systemd_CFLAGS
+and systemd_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables systemd_CFLAGS
+and systemd_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+else
+ systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+ systemd_LIBS=$pkg_cv_systemd_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --enable-systemd." "$LINENO" 5 ;; #(
+ *) :
+ ;;
+esac
+
+fi
+
+if test "$enable_systemd" = "yes"; then :
+
+
+$as_echo "#define ENABLE_SYSTEMD 1" >>confdefs.h
+
+ ac_fn_c_check_header_mongrel "$LINENO" "systemd/sd-bus.h" "ac_cv_header_systemd_sd_bus_h" "$ac_includes_default"
+if test "x$ac_cv_header_systemd_sd_bus_h" = xyes; then :
+
+$as_echo "#define ENABLE_DBUS 1" >>confdefs.h
+
+else
+ enable_systemd="yes (without D-Bus)"
+fi
+
+
+
+fi
+
+
+fi
+# Socket polling method
+socket_polling=
+
+# Check whether --with-socket-polling was given.
+if test "${with_socket_polling+set}" = set; then :
+ withval=$with_socket_polling; socket_polling=$withval
+else
+ socket_polling=auto
+
+fi
+
+
+case $socket_polling in #(
+ auto) :
+ for ac_func in kqueue
+do :
+ ac_fn_c_check_func "$LINENO" "kqueue" "ac_cv_func_kqueue"
+if test "x$ac_cv_func_kqueue" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_KQUEUE 1
+_ACEOF
+
+$as_echo "#define HAVE_KQUEUE 1" >>confdefs.h
+
+ socket_polling=kqueue
+else
+ for ac_func in epoll_create
+do :
+ ac_fn_c_check_func "$LINENO" "epoll_create" "ac_cv_func_epoll_create"
+if test "x$ac_cv_func_epoll_create" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_EPOLL_CREATE 1
+_ACEOF
+
+$as_echo "#define HAVE_EPOLL 1" >>confdefs.h
+
+ socket_polling=epoll
+else
+ socket_polling=poll
+fi
+done
+
+fi
+done
+ ;; #(
+ poll) :
+ socket_polling=poll ;; #(
+ epoll) :
+ for ac_func in epoll_create
+do :
+ ac_fn_c_check_func "$LINENO" "epoll_create" "ac_cv_func_epoll_create"
+if test "x$ac_cv_func_epoll_create" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_EPOLL_CREATE 1
+_ACEOF
+
+$as_echo "#define HAVE_EPOLL 1" >>confdefs.h
+
+ socket_polling=epoll
+else
+ as_fn_error $? "epoll not available." "$LINENO" 5
+fi
+done
+ ;; #(
+ kqueue) :
+ for ac_func in kqueue
+do :
+ ac_fn_c_check_func "$LINENO" "kqueue" "ac_cv_func_kqueue"
+if test "x$ac_cv_func_kqueue" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_KQUEUE 1
+_ACEOF
+
+$as_echo "#define HAVE_KQUEUE 1" >>confdefs.h
+
+ socket_polling=kqueue
+else
+ as_fn_error $? "kqueue not available." "$LINENO" 5
+fi
+done
+ ;; #(
+ libkqueue) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libkqueue" >&5
+$as_echo_n "checking for libkqueue... " >&6; }
+
+if test -n "$libkqueue_CFLAGS"; then
+ pkg_cv_libkqueue_CFLAGS="$libkqueue_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libkqueue\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libkqueue") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libkqueue_CFLAGS=`$PKG_CONFIG --cflags "libkqueue" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libkqueue_LIBS"; then
+ pkg_cv_libkqueue_LIBS="$libkqueue_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libkqueue\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libkqueue") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libkqueue_LIBS=`$PKG_CONFIG --libs "libkqueue" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libkqueue_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libkqueue" 2>&1`
+ else
+ libkqueue_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libkqueue" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libkqueue_PKG_ERRORS" >&5
+
+ as_fn_error $? "libkqueue not available." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "libkqueue not available." "$LINENO" 5
+else
+ libkqueue_CFLAGS=$pkg_cv_libkqueue_CFLAGS
+ libkqueue_LIBS=$pkg_cv_libkqueue_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_KQUEUE 1" >>confdefs.h
+
+ socket_polling=libkqueue
+fi ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --socket-polling." "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+
+# Alternative memory allocator
+malloc_LIBS=
+
+# Check whether --with-memory-allocator was given.
+if test "${with_memory_allocator+set}" = set; then :
+ withval=$with_memory_allocator; case $withval in #(
+ auto) :
+ ;; #(
+ *) :
+ malloc_LIBS="-l$withval"
+ ;; #(
+ *) :
+ ;;
+esac
+ with_memory_allocator=$withval
+
+fi
+
+if test "$with_memory_allocator" = ""; then :
+ with_memory_allocator="auto"
+fi
+
+
+
+# Check whether --with-urcu was given.
+if test "${with_urcu+set}" = set; then :
+ withval=$with_urcu;
+fi
+
+
+if test "$enable_daemon" = "yes"; then :
+
+
+if test "$with_urcu" != "no"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liburcu" >&5
+$as_echo_n "checking for liburcu... " >&6; }
+
+if test -n "$liburcu_CFLAGS"; then
+ pkg_cv_liburcu_CFLAGS="$liburcu_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"liburcu\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "liburcu") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_liburcu_CFLAGS=`$PKG_CONFIG --cflags "liburcu" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$liburcu_LIBS"; then
+ pkg_cv_liburcu_LIBS="$liburcu_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"liburcu\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "liburcu") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_liburcu_LIBS=`$PKG_CONFIG --libs "liburcu" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ liburcu_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "liburcu" 2>&1`
+ else
+ liburcu_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "liburcu" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$liburcu_PKG_ERRORS" >&5
+
+
+ for try_urcu in "$with_urcu" "" "/usr/local"; do
+ save_LIBS="$LIBS"
+ save_CPPFLAGS="$CPPFLAGS"
+
+ if test -d "$try_urcu"; then :
+
+ liburcu_CFLAGS="-I$try_urcu/include"
+ liburcu_LIBS="-L$try_urcu/lib"
+
+else
+
+ liburcu_CFLAGS=""
+ liburcu_LIBS=""
+
+fi
+
+ CPPFLAGS="$CPPFLAGS $liburcu_CFLAGS"
+ LIBS="$LIBS $liburcu_LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing rcu_set_pointer_sym" >&5
+$as_echo_n "checking for library containing rcu_set_pointer_sym... " >&6; }
+if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char rcu_set_pointer_sym ();
+int
+main ()
+{
+return rcu_set_pointer_sym ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' urcu; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_rcu_set_pointer_sym=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+
+else
+ ac_cv_search_rcu_set_pointer_sym=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_rcu_set_pointer_sym" >&5
+$as_echo "$ac_cv_search_rcu_set_pointer_sym" >&6; }
+ac_res=$ac_cv_search_rcu_set_pointer_sym
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ with_urcu=yes
+ liburcu_LIBS="$liburcu_LIBS -lurcu"
+
+
+ break
+
+else
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ with_urcu=no
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_rcu_set_pointer_sym
+
+fi
+
+ done
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ if test "$with_urcu" = "no"; then :
+
+ as_fn_error $? "liburcu is required" "$LINENO" 5
+
+fi
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ for try_urcu in "$with_urcu" "" "/usr/local"; do
+ save_LIBS="$LIBS"
+ save_CPPFLAGS="$CPPFLAGS"
+
+ if test -d "$try_urcu"; then :
+
+ liburcu_CFLAGS="-I$try_urcu/include"
+ liburcu_LIBS="-L$try_urcu/lib"
+
+else
+
+ liburcu_CFLAGS=""
+ liburcu_LIBS=""
+
+fi
+
+ CPPFLAGS="$CPPFLAGS $liburcu_CFLAGS"
+ LIBS="$LIBS $liburcu_LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing rcu_set_pointer_sym" >&5
+$as_echo_n "checking for library containing rcu_set_pointer_sym... " >&6; }
+if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char rcu_set_pointer_sym ();
+int
+main ()
+{
+return rcu_set_pointer_sym ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' urcu; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_rcu_set_pointer_sym=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_rcu_set_pointer_sym+:} false; then :
+
+else
+ ac_cv_search_rcu_set_pointer_sym=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_rcu_set_pointer_sym" >&5
+$as_echo "$ac_cv_search_rcu_set_pointer_sym" >&6; }
+ac_res=$ac_cv_search_rcu_set_pointer_sym
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ with_urcu=yes
+ liburcu_LIBS="$liburcu_LIBS -lurcu"
+
+
+ break
+
+else
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ with_urcu=no
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_rcu_set_pointer_sym
+
+fi
+
+ done
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ if test "$with_urcu" = "no"; then :
+
+ as_fn_error $? "liburcu is required" "$LINENO" 5
+
+fi
+
+else
+ liburcu_CFLAGS=$pkg_cv_liburcu_CFLAGS
+ liburcu_LIBS=$pkg_cv_liburcu_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ liburcu_PKGCONFIG="liburcu >= 0.4.0"
+
+ with_urcu=yes
+
+fi
+
+fi
+
+
+fi
+
+static_modules=""
+shared_modules=""
+static_modules_declars=""
+static_modules_init=""
+doc_modules=""
+
+
+
+# Check whether --with-module-cookies was given.
+if test "${with_module_cookies+set}" = set; then :
+ withval=$with_module_cookies; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/cookies/cookies.rst\n"
+
+ STATIC_MODULE_cookies=no
+ SHARED_MODULE_cookies=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_cookies=yes
+ static_modules="${static_modules}cookies "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_cookies;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_cookies }," ;; #(
+ shared) :
+ SHARED_MODULE_cookies=yes
+ shared_modules="${shared_modules}cookies "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module cookies cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module cookies requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-cookies" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_cookies" = "yes"; then
+ STATIC_MODULE_cookies_TRUE=
+ STATIC_MODULE_cookies_FALSE='#'
+else
+ STATIC_MODULE_cookies_TRUE='#'
+ STATIC_MODULE_cookies_FALSE=
+fi
+
+ if test "$SHARED_MODULE_cookies" = "yes"; then
+ SHARED_MODULE_cookies_TRUE=
+ SHARED_MODULE_cookies_FALSE='#'
+else
+ SHARED_MODULE_cookies_TRUE='#'
+ SHARED_MODULE_cookies_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-dnsproxy was given.
+if test "${with_module_dnsproxy+set}" = set; then :
+ withval=$with_module_dnsproxy; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/dnsproxy/dnsproxy.rst\n"
+
+ STATIC_MODULE_dnsproxy=no
+ SHARED_MODULE_dnsproxy=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_dnsproxy=yes
+ static_modules="${static_modules}dnsproxy "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_dnsproxy;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_dnsproxy }," ;; #(
+ shared) :
+ SHARED_MODULE_dnsproxy=yes
+ shared_modules="${shared_modules}dnsproxy "
+ if test ""non-shareable"" = "non-shareable"; then :
+ as_fn_error $? "Module dnsproxy cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module dnsproxy requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-dnsproxy" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_dnsproxy" = "yes"; then
+ STATIC_MODULE_dnsproxy_TRUE=
+ STATIC_MODULE_dnsproxy_FALSE='#'
+else
+ STATIC_MODULE_dnsproxy_TRUE='#'
+ STATIC_MODULE_dnsproxy_FALSE=
+fi
+
+ if test "$SHARED_MODULE_dnsproxy" = "yes"; then
+ SHARED_MODULE_dnsproxy_TRUE=
+ SHARED_MODULE_dnsproxy_FALSE='#'
+else
+ SHARED_MODULE_dnsproxy_TRUE='#'
+ SHARED_MODULE_dnsproxy_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-dnstap was given.
+if test "${with_module_dnstap+set}" = set; then :
+ withval=$with_module_dnstap; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="no"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/dnstap/dnstap.rst\n"
+
+ STATIC_MODULE_dnstap=no
+ SHARED_MODULE_dnstap=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_dnstap=yes
+ static_modules="${static_modules}dnstap "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_dnstap;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_dnstap }," ;; #(
+ shared) :
+ SHARED_MODULE_dnstap=yes
+ shared_modules="${shared_modules}dnstap "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module dnstap cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module dnstap requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-dnstap" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_dnstap" = "yes"; then
+ STATIC_MODULE_dnstap_TRUE=
+ STATIC_MODULE_dnstap_FALSE='#'
+else
+ STATIC_MODULE_dnstap_TRUE='#'
+ STATIC_MODULE_dnstap_FALSE=
+fi
+
+ if test "$SHARED_MODULE_dnstap" = "yes"; then
+ SHARED_MODULE_dnstap_TRUE=
+ SHARED_MODULE_dnstap_FALSE='#'
+else
+ SHARED_MODULE_dnstap_TRUE='#'
+ SHARED_MODULE_dnstap_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-geoip was given.
+if test "${with_module_geoip+set}" = set; then :
+ withval=$with_module_geoip; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/geoip/geoip.rst\n"
+
+ STATIC_MODULE_geoip=no
+ SHARED_MODULE_geoip=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_geoip=yes
+ static_modules="${static_modules}geoip "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_geoip;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_geoip }," ;; #(
+ shared) :
+ SHARED_MODULE_geoip=yes
+ shared_modules="${shared_modules}geoip "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module geoip cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module geoip requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-geoip" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_geoip" = "yes"; then
+ STATIC_MODULE_geoip_TRUE=
+ STATIC_MODULE_geoip_FALSE='#'
+else
+ STATIC_MODULE_geoip_TRUE='#'
+ STATIC_MODULE_geoip_FALSE=
+fi
+
+ if test "$SHARED_MODULE_geoip" = "yes"; then
+ SHARED_MODULE_geoip_TRUE=
+ SHARED_MODULE_geoip_FALSE='#'
+else
+ SHARED_MODULE_geoip_TRUE='#'
+ SHARED_MODULE_geoip_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-noudp was given.
+if test "${with_module_noudp+set}" = set; then :
+ withval=$with_module_noudp; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/noudp/noudp.rst\n"
+
+ STATIC_MODULE_noudp=no
+ SHARED_MODULE_noudp=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_noudp=yes
+ static_modules="${static_modules}noudp "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_noudp;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_noudp }," ;; #(
+ shared) :
+ SHARED_MODULE_noudp=yes
+ shared_modules="${shared_modules}noudp "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module noudp cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module noudp requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-noudp" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_noudp" = "yes"; then
+ STATIC_MODULE_noudp_TRUE=
+ STATIC_MODULE_noudp_FALSE='#'
+else
+ STATIC_MODULE_noudp_TRUE='#'
+ STATIC_MODULE_noudp_FALSE=
+fi
+
+ if test "$SHARED_MODULE_noudp" = "yes"; then
+ SHARED_MODULE_noudp_TRUE=
+ SHARED_MODULE_noudp_FALSE='#'
+else
+ SHARED_MODULE_noudp_TRUE='#'
+ SHARED_MODULE_noudp_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-onlinesign was given.
+if test "${with_module_onlinesign+set}" = set; then :
+ withval=$with_module_onlinesign; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/onlinesign/onlinesign.rst\n"
+
+ STATIC_MODULE_onlinesign=no
+ SHARED_MODULE_onlinesign=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_onlinesign=yes
+ static_modules="${static_modules}onlinesign "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_onlinesign;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_onlinesign }," ;; #(
+ shared) :
+ SHARED_MODULE_onlinesign=yes
+ shared_modules="${shared_modules}onlinesign "
+ if test ""non-shareable"" = "non-shareable"; then :
+ as_fn_error $? "Module onlinesign cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module onlinesign requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-onlinesign" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_onlinesign" = "yes"; then
+ STATIC_MODULE_onlinesign_TRUE=
+ STATIC_MODULE_onlinesign_FALSE='#'
+else
+ STATIC_MODULE_onlinesign_TRUE='#'
+ STATIC_MODULE_onlinesign_FALSE=
+fi
+
+ if test "$SHARED_MODULE_onlinesign" = "yes"; then
+ SHARED_MODULE_onlinesign_TRUE=
+ SHARED_MODULE_onlinesign_FALSE='#'
+else
+ SHARED_MODULE_onlinesign_TRUE='#'
+ SHARED_MODULE_onlinesign_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-probe was given.
+if test "${with_module_probe+set}" = set; then :
+ withval=$with_module_probe; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/probe/probe.rst\n"
+
+ STATIC_MODULE_probe=no
+ SHARED_MODULE_probe=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_probe=yes
+ static_modules="${static_modules}probe "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_probe;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_probe }," ;; #(
+ shared) :
+ SHARED_MODULE_probe=yes
+ shared_modules="${shared_modules}probe "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module probe cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module probe requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-probe" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_probe" = "yes"; then
+ STATIC_MODULE_probe_TRUE=
+ STATIC_MODULE_probe_FALSE='#'
+else
+ STATIC_MODULE_probe_TRUE='#'
+ STATIC_MODULE_probe_FALSE=
+fi
+
+ if test "$SHARED_MODULE_probe" = "yes"; then
+ SHARED_MODULE_probe_TRUE=
+ SHARED_MODULE_probe_FALSE='#'
+else
+ SHARED_MODULE_probe_TRUE='#'
+ SHARED_MODULE_probe_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-queryacl was given.
+if test "${with_module_queryacl+set}" = set; then :
+ withval=$with_module_queryacl; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/queryacl/queryacl.rst\n"
+
+ STATIC_MODULE_queryacl=no
+ SHARED_MODULE_queryacl=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_queryacl=yes
+ static_modules="${static_modules}queryacl "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_queryacl;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_queryacl }," ;; #(
+ shared) :
+ SHARED_MODULE_queryacl=yes
+ shared_modules="${shared_modules}queryacl "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module queryacl cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module queryacl requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-queryacl" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_queryacl" = "yes"; then
+ STATIC_MODULE_queryacl_TRUE=
+ STATIC_MODULE_queryacl_FALSE='#'
+else
+ STATIC_MODULE_queryacl_TRUE='#'
+ STATIC_MODULE_queryacl_FALSE=
+fi
+
+ if test "$SHARED_MODULE_queryacl" = "yes"; then
+ SHARED_MODULE_queryacl_TRUE=
+ SHARED_MODULE_queryacl_FALSE='#'
+else
+ SHARED_MODULE_queryacl_TRUE='#'
+ SHARED_MODULE_queryacl_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-rrl was given.
+if test "${with_module_rrl+set}" = set; then :
+ withval=$with_module_rrl; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/rrl/rrl.rst\n"
+
+ STATIC_MODULE_rrl=no
+ SHARED_MODULE_rrl=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_rrl=yes
+ static_modules="${static_modules}rrl "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_rrl;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_rrl }," ;; #(
+ shared) :
+ SHARED_MODULE_rrl=yes
+ shared_modules="${shared_modules}rrl "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module rrl cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module rrl requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-rrl" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_rrl" = "yes"; then
+ STATIC_MODULE_rrl_TRUE=
+ STATIC_MODULE_rrl_FALSE='#'
+else
+ STATIC_MODULE_rrl_TRUE='#'
+ STATIC_MODULE_rrl_FALSE=
+fi
+
+ if test "$SHARED_MODULE_rrl" = "yes"; then
+ SHARED_MODULE_rrl_TRUE=
+ SHARED_MODULE_rrl_FALSE='#'
+else
+ SHARED_MODULE_rrl_TRUE='#'
+ SHARED_MODULE_rrl_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-stats was given.
+if test "${with_module_stats+set}" = set; then :
+ withval=$with_module_stats; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/stats/stats.rst\n"
+
+ STATIC_MODULE_stats=no
+ SHARED_MODULE_stats=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_stats=yes
+ static_modules="${static_modules}stats "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_stats;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_stats }," ;; #(
+ shared) :
+ SHARED_MODULE_stats=yes
+ shared_modules="${shared_modules}stats "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module stats cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module stats requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-stats" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_stats" = "yes"; then
+ STATIC_MODULE_stats_TRUE=
+ STATIC_MODULE_stats_FALSE='#'
+else
+ STATIC_MODULE_stats_TRUE='#'
+ STATIC_MODULE_stats_FALSE=
+fi
+
+ if test "$SHARED_MODULE_stats" = "yes"; then
+ SHARED_MODULE_stats_TRUE=
+ SHARED_MODULE_stats_FALSE='#'
+else
+ SHARED_MODULE_stats_TRUE='#'
+ SHARED_MODULE_stats_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-synthrecord was given.
+if test "${with_module_synthrecord+set}" = set; then :
+ withval=$with_module_synthrecord; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/synthrecord/synthrecord.rst\n"
+
+ STATIC_MODULE_synthrecord=no
+ SHARED_MODULE_synthrecord=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_synthrecord=yes
+ static_modules="${static_modules}synthrecord "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_synthrecord;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_synthrecord }," ;; #(
+ shared) :
+ SHARED_MODULE_synthrecord=yes
+ shared_modules="${shared_modules}synthrecord "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module synthrecord cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module synthrecord requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-synthrecord" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_synthrecord" = "yes"; then
+ STATIC_MODULE_synthrecord_TRUE=
+ STATIC_MODULE_synthrecord_FALSE='#'
+else
+ STATIC_MODULE_synthrecord_TRUE='#'
+ STATIC_MODULE_synthrecord_FALSE=
+fi
+
+ if test "$SHARED_MODULE_synthrecord" = "yes"; then
+ SHARED_MODULE_synthrecord_TRUE=
+ SHARED_MODULE_synthrecord_FALSE='#'
+else
+ SHARED_MODULE_synthrecord_TRUE='#'
+ SHARED_MODULE_synthrecord_FALSE=
+fi
+
+
+
+
+# Check whether --with-module-whoami was given.
+if test "${with_module_whoami+set}" = set; then :
+ withval=$with_module_whoami; module=$withval
+else
+ if test "$enable_modules" = "no"; then :
+ module=no
+else
+ module="yes"
+
+fi
+
+fi
+
+
+ doc_modules="${doc_modules}.. include:: modules/whoami/whoami.rst\n"
+
+ STATIC_MODULE_whoami=no
+ SHARED_MODULE_whoami=no
+ case $module in #(
+ yes) :
+ STATIC_MODULE_whoami=yes
+ static_modules="${static_modules}whoami "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_whoami;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_whoami }," ;; #(
+ shared) :
+ SHARED_MODULE_whoami=yes
+ shared_modules="${shared_modules}whoami "
+ if test "" = "non-shareable"; then :
+ as_fn_error $? "Module whoami cannot be shared" "$LINENO" 5
+fi
+ if test "$enable_shared" != "yes"; then :
+ as_fn_error $? "Shared module whoami requires shared libraries" "$LINENO" 5
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value '$module' for --with-module-whoami" "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$STATIC_MODULE_whoami" = "yes"; then
+ STATIC_MODULE_whoami_TRUE=
+ STATIC_MODULE_whoami_FALSE='#'
+else
+ STATIC_MODULE_whoami_TRUE='#'
+ STATIC_MODULE_whoami_FALSE=
+fi
+
+ if test "$SHARED_MODULE_whoami" = "yes"; then
+ SHARED_MODULE_whoami_TRUE=
+ SHARED_MODULE_whoami_FALSE='#'
+else
+ SHARED_MODULE_whoami_TRUE='#'
+ SHARED_MODULE_whoami_FALSE=
+fi
+
+
+
+STATIC_MODULES_DECLARS=$(printf "$static_modules_declars")
+
+
+STATIC_MODULES_INIT=$(printf "$static_modules_init")
+
+
+DOC_MODULES=$(printf "$doc_modules")
+
+
+
+# Check for Dnstap
+# Check whether --enable-dnstap was given.
+if test "${enable_dnstap+set}" = set; then :
+ enableval=$enable_dnstap;
+else
+ enable_dnstap=no
+fi
+
+
+if test "$enable_dnstap" != "no" -o "$STATIC_MODULE_dnstap" != "no" -o "$SHARED_MODULE_dnstap" != "no"; then :
+
+ # Extract the first word of "protoc-c", so it can be a program name with args.
+set dummy protoc-c; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PROTOC_C+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PROTOC_C in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PROTOC_C="$PROTOC_C" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PROTOC_C="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PROTOC_C=$ac_cv_path_PROTOC_C
+if test -n "$PROTOC_C"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PROTOC_C" >&5
+$as_echo "$PROTOC_C" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test -z "$PROTOC_C"; then :
+
+ as_fn_error $? "The protoc-c program was not found. Please install protobuf-c!" "$LINENO" 5
+
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libfstrm" >&5
+$as_echo_n "checking for libfstrm... " >&6; }
+
+if test -n "$libfstrm_CFLAGS"; then
+ pkg_cv_libfstrm_CFLAGS="$libfstrm_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libfstrm\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libfstrm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libfstrm_CFLAGS=`$PKG_CONFIG --cflags "libfstrm" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libfstrm_LIBS"; then
+ pkg_cv_libfstrm_LIBS="$libfstrm_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libfstrm\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libfstrm") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libfstrm_LIBS=`$PKG_CONFIG --libs "libfstrm" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libfstrm_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libfstrm" 2>&1`
+ else
+ libfstrm_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libfstrm" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libfstrm_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libfstrm) were not met:
+
+$libfstrm_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables libfstrm_CFLAGS
+and libfstrm_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables libfstrm_CFLAGS
+and libfstrm_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ libfstrm_CFLAGS=$pkg_cv_libfstrm_CFLAGS
+ libfstrm_LIBS=$pkg_cv_libfstrm_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libprotobuf-c >= 1.0.0" >&5
+$as_echo_n "checking for libprotobuf-c >= 1.0.0... " >&6; }
+
+if test -n "$libprotobuf_c_CFLAGS"; then
+ pkg_cv_libprotobuf_c_CFLAGS="$libprotobuf_c_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libprotobuf-c >= 1.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libprotobuf-c >= 1.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libprotobuf_c_CFLAGS=`$PKG_CONFIG --cflags "libprotobuf-c >= 1.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libprotobuf_c_LIBS"; then
+ pkg_cv_libprotobuf_c_LIBS="$libprotobuf_c_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libprotobuf-c >= 1.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libprotobuf-c >= 1.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libprotobuf_c_LIBS=`$PKG_CONFIG --libs "libprotobuf-c >= 1.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libprotobuf_c_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libprotobuf-c >= 1.0.0" 2>&1`
+ else
+ libprotobuf_c_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libprotobuf-c >= 1.0.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libprotobuf_c_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libprotobuf-c >= 1.0.0) were not met:
+
+$libprotobuf_c_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables libprotobuf_c_CFLAGS
+and libprotobuf_c_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables libprotobuf_c_CFLAGS
+and libprotobuf_c_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ libprotobuf_c_CFLAGS=$pkg_cv_libprotobuf_c_CFLAGS
+ libprotobuf_c_LIBS=$pkg_cv_libprotobuf_c_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+ DNSTAP_CFLAGS="$libfstrm_CFLAGS $libprotobuf_c_CFLAGS"
+
+ DNSTAP_LIBS="$libfstrm_LIBS $libprotobuf_c_LIBS"
+
+
+fi
+
+if test "$enable_dnstap" != "no"; then :
+
+
+$as_echo "#define USE_DNSTAP 1" >>confdefs.h
+
+
+fi
+ if test "$enable_dnstap" != "no"; then
+ HAVE_DNSTAP_TRUE=
+ HAVE_DNSTAP_FALSE='#'
+else
+ HAVE_DNSTAP_TRUE='#'
+ HAVE_DNSTAP_FALSE=
+fi
+
+
+ if test "$enable_dnstap" != "no" -o \
+ "$STATIC_MODULE_dnstap" != "no" -o \
+ "$SHARED_MODULE_dnstap" != "no"; then
+ HAVE_LIBDNSTAP_TRUE=
+ HAVE_LIBDNSTAP_FALSE='#'
+else
+ HAVE_LIBDNSTAP_TRUE='#'
+ HAVE_LIBDNSTAP_FALSE=
+fi
+
+# MaxMind DB for the GeoIP module
+# Check whether --enable-maxminddb was given.
+if test "${enable_maxminddb+set}" = set; then :
+ enableval=$enable_maxminddb; enable_maxminddb="$enableval"
+else
+ enable_maxminddb=auto
+fi
+
+
+if test "$enable_daemon" = "no"; then :
+ enable_maxminddb=no
+fi
+case $enable_maxminddb in #(
+ no) :
+ ;; #(
+ auto) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmaxminddb" >&5
+$as_echo_n "checking for libmaxminddb... " >&6; }
+
+if test -n "$libmaxminddb_CFLAGS"; then
+ pkg_cv_libmaxminddb_CFLAGS="$libmaxminddb_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmaxminddb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmaxminddb_CFLAGS=`$PKG_CONFIG --cflags "libmaxminddb" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libmaxminddb_LIBS"; then
+ pkg_cv_libmaxminddb_LIBS="$libmaxminddb_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmaxminddb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmaxminddb_LIBS=`$PKG_CONFIG --libs "libmaxminddb" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libmaxminddb_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmaxminddb" 2>&1`
+ else
+ libmaxminddb_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmaxminddb" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libmaxminddb_PKG_ERRORS" >&5
+
+ enable_maxminddb=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_maxminddb=no
+else
+ libmaxminddb_CFLAGS=$pkg_cv_libmaxminddb_CFLAGS
+ libmaxminddb_LIBS=$pkg_cv_libmaxminddb_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_maxminddb=yes
+fi ;; #(
+ yes) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmaxminddb" >&5
+$as_echo_n "checking for libmaxminddb... " >&6; }
+
+if test -n "$libmaxminddb_CFLAGS"; then
+ pkg_cv_libmaxminddb_CFLAGS="$libmaxminddb_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmaxminddb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmaxminddb_CFLAGS=`$PKG_CONFIG --cflags "libmaxminddb" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libmaxminddb_LIBS"; then
+ pkg_cv_libmaxminddb_LIBS="$libmaxminddb_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmaxminddb\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmaxminddb") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmaxminddb_LIBS=`$PKG_CONFIG --libs "libmaxminddb" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libmaxminddb_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmaxminddb" 2>&1`
+ else
+ libmaxminddb_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmaxminddb" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libmaxminddb_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (libmaxminddb) were not met:
+
+$libmaxminddb_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables libmaxminddb_CFLAGS
+and libmaxminddb_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables libmaxminddb_CFLAGS
+and libmaxminddb_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ libmaxminddb_CFLAGS=$pkg_cv_libmaxminddb_CFLAGS
+ libmaxminddb_LIBS=$pkg_cv_libmaxminddb_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi ;; #(
+ *) :
+
+ save_CFLAGS="$CFLAGS"
+ save_LIBS="$LIBS"
+ if test "$enable_maxminddb" != ""; then :
+
+ LIBS="$LIBS -L$enable_maxminddb"
+ CFLAGS="$CFLAGS -I$enable_maxminddb/include"
+
+fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing MMDB_open" >&5
+$as_echo_n "checking for library containing MMDB_open... " >&6; }
+if ${ac_cv_search_MMDB_open+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char MMDB_open ();
+int
+main ()
+{
+return MMDB_open ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' maxminddb; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_MMDB_open=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_MMDB_open+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_MMDB_open+:} false; then :
+
+else
+ ac_cv_search_MMDB_open=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_MMDB_open" >&5
+$as_echo "$ac_cv_search_MMDB_open" >&6; }
+ac_res=$ac_cv_search_MMDB_open
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$enable_maxminddb" != ""; then :
+
+ libmaxminddb_CFLAGS="-I$enable_maxminddb/include"
+ libmaxminddb_LIBS="-L$enable_maxminddb -lmaxminddb"
+
+else
+
+ libmaxminddb_CFLAGS=""
+ libmaxminddb_LIBS="$ac_cv_search_MMDB_open"
+
+fi
+
+else
+ as_fn_error $? "\"not found in \`$enable_maxminddb'\"" "$LINENO" 5
+fi
+
+ CFLAGS="$save_CFLAGS"
+ LIBS="$save_LIBS"
+
+
+ enable_maxminddb=yes
+ ;; #(
+ *) :
+ ;;
+esac
+
+if test "$enable_maxminddb" = yes; then :
+
+$as_echo "#define HAVE_MAXMINDDB 1" >>confdefs.h
+
+fi
+ if test "$enable_maxminddb" = yes; then
+ HAVE_MAXMINDDB_TRUE=
+ HAVE_MAXMINDDB_FALSE='#'
+else
+ HAVE_MAXMINDDB_TRUE='#'
+ HAVE_MAXMINDDB_FALSE=
+fi
+
+
+
+# Check whether --with-lmdb was given.
+if test "${with_lmdb+set}" = set; then :
+ withval=$with_lmdb;
+fi
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for lmdb >= 0.9.15" >&5
+$as_echo_n "checking for lmdb >= 0.9.15... " >&6; }
+
+if test -n "$lmdb_CFLAGS"; then
+ pkg_cv_lmdb_CFLAGS="$lmdb_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lmdb >= 0.9.15\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "lmdb >= 0.9.15") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_lmdb_CFLAGS=`$PKG_CONFIG --cflags "lmdb >= 0.9.15" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$lmdb_LIBS"; then
+ pkg_cv_lmdb_LIBS="$lmdb_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"lmdb >= 0.9.15\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "lmdb >= 0.9.15") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_lmdb_LIBS=`$PKG_CONFIG --libs "lmdb >= 0.9.15" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ lmdb_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "lmdb >= 0.9.15" 2>&1`
+ else
+ lmdb_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "lmdb >= 0.9.15" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$lmdb_PKG_ERRORS" >&5
+
+
+ save_CPPFLAGS=$CPPFLAGS
+ save_LIBS=$LIBS
+
+ have_lmdb=no
+
+ for try_lmdb in "$with_lmdb" "" "/usr/local" "/usr/pkg"; do
+ if test -d "$try_lmdb"; then :
+
+ lmdb_CFLAGS="-I$try_lmdb/include"
+ lmdb_LIBS="-L$try_lmdb/lib"
+
+else
+
+ lmdb_CFLAGS=""
+ lmdb_LIBS=""
+
+fi
+
+ CPPFLAGS="$save_CPPFLAGS $lmdb_CFLAGS"
+ LIBS="$save_LIBS $lmdb_LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing mdb_txn_id" >&5
+$as_echo_n "checking for library containing mdb_txn_id... " >&6; }
+if ${ac_cv_search_mdb_txn_id+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mdb_txn_id ();
+int
+main ()
+{
+return mdb_txn_id ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' lmdb; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_mdb_txn_id=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_mdb_txn_id+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_mdb_txn_id+:} false; then :
+
+else
+ ac_cv_search_mdb_txn_id=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_mdb_txn_id" >&5
+$as_echo "$ac_cv_search_mdb_txn_id" >&6; }
+ac_res=$ac_cv_search_mdb_txn_id
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ have_lmdb=yes
+ lmdb_LIBS="$lmdb_LIBS -llmdb"
+
+
+ break
+
+fi
+
+
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_mdb_txn_id
+ done
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ if test "$have_lmdb" = "no"; then :
+
+ as_fn_error $? "lmdb library not found" "$LINENO" 5
+
+fi
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ save_CPPFLAGS=$CPPFLAGS
+ save_LIBS=$LIBS
+
+ have_lmdb=no
+
+ for try_lmdb in "$with_lmdb" "" "/usr/local" "/usr/pkg"; do
+ if test -d "$try_lmdb"; then :
+
+ lmdb_CFLAGS="-I$try_lmdb/include"
+ lmdb_LIBS="-L$try_lmdb/lib"
+
+else
+
+ lmdb_CFLAGS=""
+ lmdb_LIBS=""
+
+fi
+
+ CPPFLAGS="$save_CPPFLAGS $lmdb_CFLAGS"
+ LIBS="$save_LIBS $lmdb_LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing mdb_txn_id" >&5
+$as_echo_n "checking for library containing mdb_txn_id... " >&6; }
+if ${ac_cv_search_mdb_txn_id+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char mdb_txn_id ();
+int
+main ()
+{
+return mdb_txn_id ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' lmdb; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_mdb_txn_id=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_mdb_txn_id+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_mdb_txn_id+:} false; then :
+
+else
+ ac_cv_search_mdb_txn_id=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_mdb_txn_id" >&5
+$as_echo "$ac_cv_search_mdb_txn_id" >&6; }
+ac_res=$ac_cv_search_mdb_txn_id
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ have_lmdb=yes
+ lmdb_LIBS="$lmdb_LIBS -llmdb"
+
+
+ break
+
+fi
+
+
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_mdb_txn_id
+ done
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ if test "$have_lmdb" = "no"; then :
+
+ as_fn_error $? "lmdb library not found" "$LINENO" 5
+
+fi
+
+else
+ lmdb_CFLAGS=$pkg_cv_lmdb_CFLAGS
+ lmdb_LIBS=$pkg_cv_lmdb_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+
+# LMDB mapping sizes
+conf_mapsize_default=500
+
+# Check whether --with-conf_mapsize was given.
+if test "${with_conf_mapsize+set}" = set; then :
+ withval=$with_conf_mapsize; conf_mapsize=$withval
+else
+ conf_mapsize=$conf_mapsize_default
+fi
+
+
+case $conf_mapsize in #(
+ yes) :
+ conf_mapsize=$conf_mapsize_default ;; #(
+ no) :
+ as_fn_error $? "conf_mapsize must be a number" "$LINENO" 5 ;; #(
+ *) :
+ if test $conf_mapsize != $(( $conf_mapsize + 0 )); then :
+ as_fn_error $? "conf_mapsize must be an integer number" "$LINENO" 5
+fi ;; #(
+ *) :
+ ;;
+esac
+
+cat >>confdefs.h <<_ACEOF
+#define CONF_MAPSIZE $conf_mapsize
+_ACEOF
+
+
+
+# libedit
+if test "$enable_daemon" = "yes" -o "$enable_utilities" = "yes"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libedit" >&5
+$as_echo_n "checking for libedit... " >&6; }
+
+if test -n "$libedit_CFLAGS"; then
+ pkg_cv_libedit_CFLAGS="$libedit_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libedit_CFLAGS=`$PKG_CONFIG --cflags "libedit" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libedit_LIBS"; then
+ pkg_cv_libedit_LIBS="$libedit_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libedit") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libedit_LIBS=`$PKG_CONFIG --libs "libedit" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libedit_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1`
+ else
+ libedit_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libedit_PKG_ERRORS" >&5
+
+
+ with_libedit=no
+ ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default"
+if test "x$ac_cv_header_histedit_h" = xyes; then :
+
+ # workaround for OpenBSD
+ case $host_os in #(
+ openbsd*) :
+ libedit_deps=-lcurses ;; #(
+ *) :
+ libedit_deps=
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for el_init in -ledit" >&5
+$as_echo_n "checking for el_init in -ledit... " >&6; }
+if ${ac_cv_lib_edit_el_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ledit $libedit_deps
+ $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char el_init ();
+int
+main ()
+{
+return el_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_edit_el_init=yes
+else
+ ac_cv_lib_edit_el_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_el_init" >&5
+$as_echo "$ac_cv_lib_edit_el_init" >&6; }
+if test "x$ac_cv_lib_edit_el_init" = xyes; then :
+
+ with_libedit=yes
+ libedit_CFLAGS=
+ libedit_LIBS="-ledit $libedit_deps"
+
+fi
+
+
+fi
+
+
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ with_libedit=no
+ ac_fn_c_check_header_mongrel "$LINENO" "histedit.h" "ac_cv_header_histedit_h" "$ac_includes_default"
+if test "x$ac_cv_header_histedit_h" = xyes; then :
+
+ # workaround for OpenBSD
+ case $host_os in #(
+ openbsd*) :
+ libedit_deps=-lcurses ;; #(
+ *) :
+ libedit_deps=
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for el_init in -ledit" >&5
+$as_echo_n "checking for el_init in -ledit... " >&6; }
+if ${ac_cv_lib_edit_el_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ledit $libedit_deps
+ $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char el_init ();
+int
+main ()
+{
+return el_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_edit_el_init=yes
+else
+ ac_cv_lib_edit_el_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_edit_el_init" >&5
+$as_echo "$ac_cv_lib_edit_el_init" >&6; }
+if test "x$ac_cv_lib_edit_el_init" = xyes; then :
+
+ with_libedit=yes
+ libedit_CFLAGS=
+ libedit_LIBS="-ledit $libedit_deps"
+
+fi
+
+
+fi
+
+
+
+else
+ libedit_CFLAGS=$pkg_cv_libedit_CFLAGS
+ libedit_LIBS=$pkg_cv_libedit_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ with_libedit=yes
+fi
+ if test "$with_libedit" != "yes"; then :
+
+ as_fn_error $? "libedit not found" "$LINENO" 5
+
+fi
+
+else
+
+ with_libedit=no
+ libedit_CFLAGS=
+ libedit_LIBS=
+
+fi
+
+# QUIC support
+# Check whether --enable-quic was given.
+if test "${enable_quic+set}" = set; then :
+ enableval=$enable_quic;
+else
+ enable_quic=auto
+fi
+
+
+case $enable_quic in #(
+ auto) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" >&5
+$as_echo_n "checking for libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls... " >&6; }
+
+if test -n "$libngtcp2_CFLAGS"; then
+ pkg_cv_libngtcp2_CFLAGS="$libngtcp2_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libngtcp2_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libngtcp2_LIBS"; then
+ pkg_cv_libngtcp2_LIBS="$libngtcp2_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libngtcp2_LIBS=`$PKG_CONFIG --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libngtcp2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>&1`
+ else
+ libngtcp2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libngtcp2_PKG_ERRORS" >&5
+
+ enable_quic=no
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ enable_quic=no
+else
+ libngtcp2_CFLAGS=$pkg_cv_libngtcp2_CFLAGS
+ libngtcp2_LIBS=$pkg_cv_libngtcp2_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_quic=yes
+fi ;; #(
+ yes) :
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" >&5
+$as_echo_n "checking for libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls... " >&6; }
+
+if test -n "$libngtcp2_CFLAGS"; then
+ pkg_cv_libngtcp2_CFLAGS="$libngtcp2_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libngtcp2_CFLAGS=`$PKG_CONFIG --cflags "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libngtcp2_LIBS"; then
+ pkg_cv_libngtcp2_LIBS="$libngtcp2_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libngtcp2_LIBS=`$PKG_CONFIG --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libngtcp2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>&1`
+ else
+ libngtcp2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libngtcp2_PKG_ERRORS" >&5
+
+ if test "$gnutls_quic" = "yes"; then :
+ enable_quic=embedded
+ embedded_libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2 -I\$(top_srcdir)/src/contrib/libngtcp2/ngtcp2/lib"
+ embedded_libngtcp2_LIBS=$libelf_LIBS
+ libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2"
+else
+ enable_quic=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: gnutls >= 3.7.2 is required for QUIC" >&5
+$as_echo "$as_me: WARNING: gnutls >= 3.7.2 is required for QUIC" >&2;}
+fi
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ if test "$gnutls_quic" = "yes"; then :
+ enable_quic=embedded
+ embedded_libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2 -I\$(top_srcdir)/src/contrib/libngtcp2/ngtcp2/lib"
+ embedded_libngtcp2_LIBS=$libelf_LIBS
+ libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2"
+else
+ enable_quic=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: gnutls >= 3.7.2 is required for QUIC" >&5
+$as_echo "$as_me: WARNING: gnutls >= 3.7.2 is required for QUIC" >&2;}
+fi
+else
+ libngtcp2_CFLAGS=$pkg_cv_libngtcp2_CFLAGS
+ libngtcp2_LIBS=$pkg_cv_libngtcp2_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_quic=yes
+fi ;; #(
+ no) :
+ ;; #(
+ *) :
+ as_fn_error $? "Invalid value of --enable-quic." "$LINENO" 5
+ ;; #(
+ *) :
+ ;;
+esac
+ if test "$enable_quic" = "embedded"; then
+ EMBEDDED_LIBNGTCP2_TRUE=
+ EMBEDDED_LIBNGTCP2_FALSE='#'
+else
+ EMBEDDED_LIBNGTCP2_TRUE='#'
+ EMBEDDED_LIBNGTCP2_FALSE=
+fi
+
+ if test "$enable_quic" != "no"; then
+ ENABLE_QUIC_TRUE=
+ ENABLE_QUIC_FALSE='#'
+else
+ ENABLE_QUIC_TRUE='#'
+ ENABLE_QUIC_FALSE=
+fi
+
+
+
+
+
+
+if test "$enable_quic" != "no"; then :
+
+
+$as_echo "#define ENABLE_QUIC 1" >>confdefs.h
+
+fi
+
+############################################
+# Dependencies needed for Knot DNS utilities
+############################################
+
+
+# Check whether --with-libidn was given.
+if test "${with_libidn+set}" = set; then :
+ withval=$with_libidn; with_libidn=$withval
+else
+ with_libidn=yes
+
+fi
+
+
+
+# Check whether --with-libnghttp2 was given.
+if test "${with_libnghttp2+set}" = set; then :
+ withval=$with_libnghttp2; with_libnghttp2=$withval
+else
+ with_libnghttp2=yes
+
+fi
+
+
+if test "$enable_utilities" = "yes"; then :
+
+ if test "$with_libidn" != "no"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libidn2 >= 2.0.0" >&5
+$as_echo_n "checking for libidn2 >= 2.0.0... " >&6; }
+
+if test -n "$libidn2_CFLAGS"; then
+ pkg_cv_libidn2_CFLAGS="$libidn2_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn2 >= 2.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn2 >= 2.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn2_CFLAGS=`$PKG_CONFIG --cflags "libidn2 >= 2.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libidn2_LIBS"; then
+ pkg_cv_libidn2_LIBS="$libidn2_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn2 >= 2.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn2 >= 2.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn2_LIBS=`$PKG_CONFIG --libs "libidn2 >= 2.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libidn2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libidn2 >= 2.0.0" 2>&1`
+ else
+ libidn2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libidn2 >= 2.0.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libidn2_PKG_ERRORS" >&5
+
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libidn >= 0.0.0" >&5
+$as_echo_n "checking for libidn >= 0.0.0... " >&6; }
+
+if test -n "$libidn_CFLAGS"; then
+ pkg_cv_libidn_CFLAGS="$libidn_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn >= 0.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn >= 0.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn_CFLAGS=`$PKG_CONFIG --cflags "libidn >= 0.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libidn_LIBS"; then
+ pkg_cv_libidn_LIBS="$libidn_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn >= 0.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn >= 0.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn_LIBS=`$PKG_CONFIG --libs "libidn >= 0.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libidn_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libidn >= 0.0.0" 2>&1`
+ else
+ libidn_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libidn >= 0.0.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libidn_PKG_ERRORS" >&5
+
+
+ with_libidn=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libidn2 or libidn not found" >&5
+$as_echo "$as_me: WARNING: libidn2 or libidn not found" >&2;}
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ with_libidn=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libidn2 or libidn not found" >&5
+$as_echo "$as_me: WARNING: libidn2 or libidn not found" >&2;}
+
+else
+ libidn_CFLAGS=$pkg_cv_libidn_CFLAGS
+ libidn_LIBS=$pkg_cv_libidn_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ with_libidn=libidn
+
+$as_echo "#define LIBIDN 1" >>confdefs.h
+
+
+$as_echo "#define LIBIDN_HEADER <idna.h>" >>confdefs.h
+
+
+fi
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libidn >= 0.0.0" >&5
+$as_echo_n "checking for libidn >= 0.0.0... " >&6; }
+
+if test -n "$libidn_CFLAGS"; then
+ pkg_cv_libidn_CFLAGS="$libidn_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn >= 0.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn >= 0.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn_CFLAGS=`$PKG_CONFIG --cflags "libidn >= 0.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libidn_LIBS"; then
+ pkg_cv_libidn_LIBS="$libidn_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libidn >= 0.0.0\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libidn >= 0.0.0") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libidn_LIBS=`$PKG_CONFIG --libs "libidn >= 0.0.0" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libidn_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libidn >= 0.0.0" 2>&1`
+ else
+ libidn_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libidn >= 0.0.0" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libidn_PKG_ERRORS" >&5
+
+
+ with_libidn=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libidn2 or libidn not found" >&5
+$as_echo "$as_me: WARNING: libidn2 or libidn not found" >&2;}
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ with_libidn=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libidn2 or libidn not found" >&5
+$as_echo "$as_me: WARNING: libidn2 or libidn not found" >&2;}
+
+else
+ libidn_CFLAGS=$pkg_cv_libidn_CFLAGS
+ libidn_LIBS=$pkg_cv_libidn_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ with_libidn=libidn
+
+$as_echo "#define LIBIDN 1" >>confdefs.h
+
+
+$as_echo "#define LIBIDN_HEADER <idna.h>" >>confdefs.h
+
+
+fi
+
+else
+ libidn2_CFLAGS=$pkg_cv_libidn2_CFLAGS
+ libidn2_LIBS=$pkg_cv_libidn2_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ with_libidn=libidn2
+
+$as_echo "#define LIBIDN 1" >>confdefs.h
+
+
+$as_echo "#define LIBIDN_HEADER <idn2.h>" >>confdefs.h
+
+
+fi
+
+fi
+
+ if test "$with_libnghttp2" != "no"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libnghttp2" >&5
+$as_echo_n "checking for libnghttp2... " >&6; }
+
+if test -n "$libnghttp2_CFLAGS"; then
+ pkg_cv_libnghttp2_CFLAGS="$libnghttp2_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnghttp2\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libnghttp2") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libnghttp2_CFLAGS=`$PKG_CONFIG --cflags "libnghttp2" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libnghttp2_LIBS"; then
+ pkg_cv_libnghttp2_LIBS="$libnghttp2_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libnghttp2\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libnghttp2") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libnghttp2_LIBS=`$PKG_CONFIG --libs "libnghttp2" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libnghttp2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libnghttp2" 2>&1`
+ else
+ libnghttp2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libnghttp2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libnghttp2_PKG_ERRORS" >&5
+
+
+ with_libnghttp2=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libnghttp2 not found" >&5
+$as_echo "$as_me: WARNING: libnghttp2 not found" >&2;}
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ with_libnghttp2=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libnghttp2 not found" >&5
+$as_echo "$as_me: WARNING: libnghttp2 not found" >&2;}
+
+else
+ libnghttp2_CFLAGS=$pkg_cv_libnghttp2_CFLAGS
+ libnghttp2_LIBS=$pkg_cv_libnghttp2_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+ with_libnghttp2=libnghttp2
+
+$as_echo "#define LIBNGHTTP2 1" >>confdefs.h
+
+
+fi
+
+fi
+
+ if test "$enable_xdp" != "no"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmnl" >&5
+$as_echo_n "checking for libmnl... " >&6; }
+
+if test -n "$libmnl_CFLAGS"; then
+ pkg_cv_libmnl_CFLAGS="$libmnl_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmnl\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmnl") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmnl_CFLAGS=`$PKG_CONFIG --cflags "libmnl" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$libmnl_LIBS"; then
+ pkg_cv_libmnl_LIBS="$libmnl_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libmnl\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "libmnl") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_libmnl_LIBS=`$PKG_CONFIG --libs "libmnl" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ libmnl_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libmnl" 2>&1`
+ else
+ libmnl_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libmnl" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$libmnl_PKG_ERRORS" >&5
+
+
+ as_fn_error $? "libmnl not found" "$LINENO" 5
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ as_fn_error $? "libmnl not found" "$LINENO" 5
+
+else
+ libmnl_CFLAGS=$pkg_cv_libmnl_CFLAGS
+ libmnl_LIBS=$pkg_cv_libmnl_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+
+fi
+
+fi # Knot DNS utilities dependencies
+
+# Check whether --enable-cap-ng was given.
+if test "${enable_cap_ng+set}" = set; then :
+ enableval=$enable_cap_ng; enable_cap_ng="$enableval"
+else
+ enable_cap_ng=auto
+fi
+
+
+if test "$enable_daemon" = "yes"; then :
+
+
+if test "$enable_cap_ng" != "no"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cap-ng" >&5
+$as_echo_n "checking for cap-ng... " >&6; }
+
+if test -n "$cap_ng_CFLAGS"; then
+ pkg_cv_cap_ng_CFLAGS="$cap_ng_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cap-ng\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "cap-ng") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_cap_ng_CFLAGS=`$PKG_CONFIG --cflags "cap-ng" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$cap_ng_LIBS"; then
+ pkg_cv_cap_ng_LIBS="$cap_ng_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"cap-ng\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "cap-ng") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_cap_ng_LIBS=`$PKG_CONFIG --libs "cap-ng" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ cap_ng_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "cap-ng" 2>&1`
+ else
+ cap_ng_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "cap-ng" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$cap_ng_PKG_ERRORS" >&5
+
+
+ enable_cap_ng=no
+ ac_fn_c_check_header_mongrel "$LINENO" "cap-ng.h" "ac_cv_header_cap_ng_h" "$ac_includes_default"
+if test "x$ac_cv_header_cap_ng_h" = xyes; then :
+
+ save_LIBS="$LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing capng_apply" >&5
+$as_echo_n "checking for library containing capng_apply... " >&6; }
+if ${ac_cv_search_capng_apply+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char capng_apply ();
+int
+main ()
+{
+return capng_apply ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' cap-ng; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_capng_apply=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_capng_apply+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_capng_apply+:} false; then :
+
+else
+ ac_cv_search_capng_apply=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_capng_apply" >&5
+$as_echo "$ac_cv_search_capng_apply" >&6; }
+ac_res=$ac_cv_search_capng_apply
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$ac_cv_search_capng_apply" != "none required"; then :
+ cap_ng_LIBS="$ac_cv_search_capng_apply"
+else
+ cap_ng_LIBS=
+fi
+
+ enable_cap_ng=yes
+
+fi
+
+ LIBS="$save_LIBS"
+
+fi
+
+
+
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+ enable_cap_ng=no
+ ac_fn_c_check_header_mongrel "$LINENO" "cap-ng.h" "ac_cv_header_cap_ng_h" "$ac_includes_default"
+if test "x$ac_cv_header_cap_ng_h" = xyes; then :
+
+ save_LIBS="$LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing capng_apply" >&5
+$as_echo_n "checking for library containing capng_apply... " >&6; }
+if ${ac_cv_search_capng_apply+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char capng_apply ();
+int
+main ()
+{
+return capng_apply ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' cap-ng; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_capng_apply=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_capng_apply+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_capng_apply+:} false; then :
+
+else
+ ac_cv_search_capng_apply=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_capng_apply" >&5
+$as_echo "$ac_cv_search_capng_apply" >&6; }
+ac_res=$ac_cv_search_capng_apply
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$ac_cv_search_capng_apply" != "none required"; then :
+ cap_ng_LIBS="$ac_cv_search_capng_apply"
+else
+ cap_ng_LIBS=
+fi
+
+ enable_cap_ng=yes
+
+fi
+
+ LIBS="$save_LIBS"
+
+fi
+
+
+
+else
+ cap_ng_CFLAGS=$pkg_cv_cap_ng_CFLAGS
+ cap_ng_LIBS=$pkg_cv_cap_ng_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ enable_cap_ng=yes
+fi
+
+else
+
+ enable_cap_ng=no
+ cap_ng_LIBS=
+
+fi
+fi
+
+if test "$enable_cap_ng" = yes; then :
+
+$as_echo "#define ENABLE_CAP_NG 1" >>confdefs.h
+
+
+fi
+
+save_LIBS="$LIBS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5
+$as_echo_n "checking for library containing pthread_create... " >&6; }
+if ${ac_cv_search_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_create ();
+int
+main ()
+{
+return pthread_create ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_pthread_create=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pthread_create+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pthread_create+:} false; then :
+
+else
+ ac_cv_search_pthread_create=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5
+$as_echo "$ac_cv_search_pthread_create" >&6; }
+ac_res=$ac_cv_search_pthread_create
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$ac_cv_search_pthread_create" != "none required"; then :
+ pthread_LIBS="$ac_cv_search_pthread_create"
+else
+ pthread_LIBS=
+fi
+
+
+else
+
+ as_fn_error $? "pthreads not found" "$LINENO" 5
+
+fi
+
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5
+$as_echo_n "checking for library containing dlopen... " >&6; }
+if ${ac_cv_search_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dl; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_dlopen=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_dlopen+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_dlopen+:} false; then :
+
+else
+ ac_cv_search_dlopen=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
+$as_echo "$ac_cv_search_dlopen" >&6; }
+ac_res=$ac_cv_search_dlopen
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$ac_cv_search_dlopen" != "none required"; then :
+ dlopen_LIBS="$ac_cv_search_dlopen"
+else
+ dlopen_LIBS=
+fi
+
+
+else
+
+ as_fn_error $? "dlopen not found" "$LINENO" 5
+
+fi
+
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5
+$as_echo_n "checking for library containing pow... " >&6; }
+if ${ac_cv_search_pow+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pow ();
+int
+main ()
+{
+return pow ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' m; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_pow=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pow+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pow+:} false; then :
+
+else
+ ac_cv_search_pow=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5
+$as_echo "$ac_cv_search_pow" >&6; }
+ac_res=$ac_cv_search_pow
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+ if test "$ac_cv_search_pow" != "none required"; then :
+ math_LIBS="$ac_cv_search_pow"
+else
+ math_LIBS=
+fi
+
+
+else
+
+ as_fn_error $? "math not found" "$LINENO" 5
+
+fi
+
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_setaffinity_np" >&5
+$as_echo_n "checking for library containing pthread_setaffinity_np... " >&6; }
+if ${ac_cv_search_pthread_setaffinity_np+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_setaffinity_np ();
+int
+main ()
+{
+return pthread_setaffinity_np ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' pthread; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_pthread_setaffinity_np=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_pthread_setaffinity_np+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_pthread_setaffinity_np+:} false; then :
+
+else
+ ac_cv_search_pthread_setaffinity_np=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_setaffinity_np" >&5
+$as_echo "$ac_cv_search_pthread_setaffinity_np" >&6; }
+ac_res=$ac_cv_search_pthread_setaffinity_np
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+
+$as_echo "#define HAVE_PTHREAD_SETAFFINITY_NP 1" >>confdefs.h
+
+
+fi
+
+LIBS="$save_LIBS"
+
+# Checks for header files.
+for ac_header in sys/types.h netinet/in.h arpa/nameser.h netdb.h resolv.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h> /* inet_ functions / structs */
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h> /* DNS HEADER struct */
+#endif
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+ for ac_header in $ac_header_list
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+
+
+
+
+
+
+# Checks for optional library functions.
+for ac_func in accept4 clock_gettime fgetln getline initgroups malloc_trim \
+ setgroups strlcat strlcpy sysctlbyname
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+# Check for robust memory cleanup implementations.
+ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
+if test "x$ac_cv_func_explicit_bzero" = xyes; then :
+
+
+$as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
+
+ explicit_bzero=yes
+else
+ explicit_bzero=no
+
+fi
+
+ac_fn_c_check_func "$LINENO" "explicit_memset" "ac_cv_func_explicit_memset"
+if test "x$ac_cv_func_explicit_memset" = xyes; then :
+
+
+$as_echo "#define HAVE_EXPLICIT_MEMSET 1" >>confdefs.h
+
+ explicit_memset=yes
+else
+ explicit_memset=no
+
+fi
+
+ if test "$explicit_bzero" = "no" -a "$explicit_memset" = "no" -a "$gnutls_memset" = "yes"; then
+ USE_GNUTLS_MEMSET_TRUE=
+ USE_GNUTLS_MEMSET_FALSE='#'
+else
+ USE_GNUTLS_MEMSET_TRUE='#'
+ USE_GNUTLS_MEMSET_FALSE=
+fi
+
+
+# Check for mandatory library functions.
+ac_fn_c_check_func "$LINENO" "vasprintf" "ac_cv_func_vasprintf"
+if test "x$ac_cv_func_vasprintf" = xyes; then :
+
+else
+
+ as_fn_error $? "vasprintf support in the libc is required" "$LINENO" 5
+fi
+
+
+# Check for cpu_set_t/cpuset_t compatibility
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+int
+main ()
+{
+cpu_set_t set; CPU_ZERO(&set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_LINUX 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread_np.h>
+int
+main ()
+{
+cpuset_t set; CPU_ZERO(&set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_BSD 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sched.h>
+int
+main ()
+{
+cpuset_t* set = cpuset_create(); cpuset_destroy(set);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_CPUSET_NETBSD 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+# Check for '__atomic' compiler builtin atomic functions.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdint.h>
+int
+main ()
+{
+uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_ATOMIC 1" >>confdefs.h
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+# Check for '__sync' compiler builtin atomic functions.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdint.h>
+int
+main ()
+{
+int val = 0; __sync_add_and_fetch(&val, 1);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+$as_echo "#define HAVE_SYNC_ATOMIC 1" >>confdefs.h
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+# Prepare CFLAG_VISIBILITY to be used where needed
+
+
+ CFLAG_VISIBILITY=
+ HAVE_VISIBILITY=0
+ if test -n "$GCC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the -Werror option is usable" >&5
+$as_echo_n "checking whether the -Werror option is usable... " >&6; }
+ if ${gl_cv_cc_vis_werror+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Werror"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ gl_cv_cc_vis_werror=yes
+else
+ gl_cv_cc_vis_werror=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS="$gl_save_CFLAGS"
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_cc_vis_werror" >&5
+$as_echo "$gl_cv_cc_vis_werror" >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for simple visibility declarations" >&5
+$as_echo_n "checking for simple visibility declarations... " >&6; }
+ if ${gl_cv_cc_visibility+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fvisibility=hidden"
+ if test $gl_cv_cc_vis_werror = yes; then
+ CFLAGS="$CFLAGS -Werror"
+ fi
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+extern __attribute__((__visibility__("hidden"))) int hiddenvar;
+ extern __attribute__((__visibility__("default"))) int exportedvar;
+ extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void);
+ extern __attribute__((__visibility__("default"))) int exportedfunc (void);
+ void dummyfunc (void) {}
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ gl_cv_cc_visibility=yes
+else
+ gl_cv_cc_visibility=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS="$gl_save_CFLAGS"
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gl_cv_cc_visibility" >&5
+$as_echo "$gl_cv_cc_visibility" >&6; }
+ if test $gl_cv_cc_visibility = yes; then
+ CFLAG_VISIBILITY="-fvisibility=hidden"
+ HAVE_VISIBILITY=1
+ fi
+ fi
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_VISIBILITY $HAVE_VISIBILITY
+_ACEOF
+
+
+
+# Add code coverage macro
+
+ # Check whether --enable-code-coverage was given.
+if test "${enable_code_coverage+set}" = set; then :
+ enableval=$enable_code_coverage; enable_code_coverage=$enableval
+else
+ enable_code_coverage=no
+
+fi
+
+ if test "$enable_code_coverage" = "yes"; then
+ CODE_COVERAGE_ENABLED_TRUE=
+ CODE_COVERAGE_ENABLED_FALSE='#'
+else
+ CODE_COVERAGE_ENABLED_TRUE='#'
+ CODE_COVERAGE_ENABLED_FALSE=
+fi
+
+ CODE_COVERAGE_ENABLED=$enable_code_coverage
+
+
+ if test "$enable_code_coverage" = "yes"; then :
+
+ # Extract the first word of "lcov", so it can be a program name with args.
+set dummy lcov; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LCOV+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LCOV"; then
+ ac_cv_prog_LCOV="$LCOV" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LCOV="lcov"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LCOV=$ac_cv_prog_LCOV
+if test -n "$LCOV"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LCOV" >&5
+$as_echo "$LCOV" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ # Extract the first word of "genhtml", so it can be a program name with args.
+set dummy genhtml; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_GENHTML+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$GENHTML"; then
+ ac_cv_prog_GENHTML="$GENHTML" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_GENHTML="genhtml"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+GENHTML=$ac_cv_prog_GENHTML
+if test -n "$GENHTML"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GENHTML" >&5
+$as_echo "$GENHTML" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ if test -z "$LCOV"; then :
+
+ as_fn_error $? "Could not find lcov" "$LINENO" 5
+
+fi
+ if test -z "$GENHTML"; then :
+
+ as_fn_error $? "Could not find genhtml from the lcov package" "$LINENO" 5
+
+fi
+
+
+ CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
+
+
+ CFLAGS="$CFLAGS --coverage"
+ LDFLAGS="$LDFLAGS --coverage"
+
+fi
+
+
+
+
+ # Configure options
+
+# Check whether --with-sanitizer was given.
+if test "${with_sanitizer+set}" = set; then :
+ withval=$with_sanitizer;
+else
+ with_sanitizer=no
+
+fi
+
+
+# Check whether --with-fuzzer was given.
+if test "${with_fuzzer+set}" = set; then :
+ withval=$with_fuzzer;
+else
+ with_fuzzer=no
+
+fi
+
+
+# Check whether --with-oss-fuzz was given.
+if test "${with_oss_fuzz+set}" = set; then :
+ withval=$with_oss_fuzz;
+else
+ with_oss_fuzz=no
+
+fi
+
+
+ # Default values
+ if test "$with_sanitizer" = "yes"; then :
+ with_sanitizer=address,undefined
+fi
+ if test "$with_fuzzer" = "yes"; then :
+ with_fuzzer=fuzzer
+fi
+
+ # Construct output variables
+ sanitizer_CFLAGS=
+ fuzzer_CFLAGS=
+ fuzzer_LDFLAGS=
+ if test "$with_sanitizer" != "no"; then :
+
+ sanitizer_CFLAGS="-fsanitize=${with_sanitizer}"
+
+fi
+ if test "$with_fuzzer" != "no"; then :
+
+ fuzzer_CFLAGS="-fsanitize=${with_fuzzer}"
+ fuzzer_LDFLAGS="-fsanitize=${with_fuzzer}"
+
+fi
+
+
+
+ # Test compiler support
+ if test -n "$sanitizer_CFLAGS" -o -n "$fuzzer_CFLAGS"; then :
+
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $sanitizer_CFLAGS $fuzzer_CFLAGS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler accepts '${sanitizer_CFLAGS} ${fuzzer_CFLAGS}'" >&5
+$as_echo_n "checking whether compiler accepts '${sanitizer_CFLAGS} ${fuzzer_CFLAGS}'... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ as_fn_error $? "Options are not supported." "$LINENO" 5
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ CFLAGS="$save_CFLAGS"
+
+fi
+
+
+if test -n "$sanitizer_CFLAGS"; then :
+ CFLAGS="$CFLAGS $sanitizer_CFLAGS"
+fi
+ if test "$with_fuzzer" != "no"; then
+ FUZZER_TRUE=
+ FUZZER_FALSE='#'
+else
+ FUZZER_TRUE='#'
+ FUZZER_FALSE=
+fi
+
+ if test "$with_oss_fuzz" != "no"; then
+ OSS_FUZZ_TRUE=
+ OSS_FUZZ_FALSE='#'
+else
+ OSS_FUZZ_TRUE='#'
+ OSS_FUZZ_FALSE=
+fi
+
+
+if test "$enable_documentation" = "yes"; then :
+
+
+# Extract the first word of "sphinx-build", so it can be a program name with args.
+set dummy sphinx-build; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_SPHINXBUILD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $SPHINXBUILD in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_SPHINXBUILD="$SPHINXBUILD" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_SPHINXBUILD="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_SPHINXBUILD" && ac_cv_path_SPHINXBUILD="false"
+ ;;
+esac
+fi
+SPHINXBUILD=$ac_cv_path_SPHINXBUILD
+if test -n "$SPHINXBUILD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SPHINXBUILD" >&5
+$as_echo "$SPHINXBUILD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if test "$SPHINXBUILD" = "false"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: missing 'sphinx-build' executable for documentation generation" >&5
+$as_echo "$as_me: WARNING: missing 'sphinx-build' executable for documentation generation" >&2;}
+
+fi
+# Extract the first word of "pdflatex", so it can be a program name with args.
+set dummy pdflatex; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PDFLATEX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PDFLATEX in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PDFLATEX="$PDFLATEX" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PDFLATEX="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PDFLATEX" && ac_cv_path_PDFLATEX="false"
+ ;;
+esac
+fi
+PDFLATEX=$ac_cv_path_PDFLATEX
+if test -n "$PDFLATEX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PDFLATEX" >&5
+$as_echo "$PDFLATEX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+# Extract the first word of "makeinfo", so it can be a program name with args.
+set dummy makeinfo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MAKEINFO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAKEINFO in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_MAKEINFO="$MAKEINFO" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_MAKEINFO="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_MAKEINFO" && ac_cv_path_MAKEINFO="false"
+ ;;
+esac
+fi
+MAKEINFO=$ac_cv_path_MAKEINFO
+if test -n "$MAKEINFO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAKEINFO" >&5
+$as_echo "$MAKEINFO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+fi
+
+ if test "$SPHINXBUILD" != "false"; then
+ HAVE_SPHINXBUILD_TRUE=
+ HAVE_SPHINXBUILD_FALSE='#'
+else
+ HAVE_SPHINXBUILD_TRUE='#'
+ HAVE_SPHINXBUILD_FALSE=
+fi
+
+ if test "$PDFLATEX" != "false"; then
+ HAVE_PDFLATEX_TRUE=
+ HAVE_PDFLATEX_FALSE='#'
+else
+ HAVE_PDFLATEX_TRUE='#'
+ HAVE_PDFLATEX_FALSE=
+fi
+
+ if test "$MAKEINFO" != "false"; then
+ HAVE_MAKEINFO_TRUE=
+ HAVE_MAKEINFO_FALSE='#'
+else
+ HAVE_MAKEINFO_TRUE='#'
+ HAVE_MAKEINFO_FALSE=
+fi
+
+
+# Strip -fdebug-prefix-map= parameters from flags for better reproducibility of binaries.
+filtered_cflags=$(echo -n "$CFLAGS" | \
+ sed 's/[^[:alnum:]]-f[^[:space:]]*-prefix-map=[^[:space:]]*//g')
+filtered_cppflags=$(echo -n "$CPPFLAGS" | \
+ sed 's/[^[:alnum:]]-f[^[:space:]]*-prefix-map=[^[:space:]]*//g')
+
+result_msg_base=" Knot DNS $VERSION
+
+ Target: $host_os $host_cpu $endianity
+ Compiler: ${CC}
+ CFLAGS: ${filtered_cflags} ${filtered_cppflags}
+ LIBS: ${LIBS} ${LDFLAGS}
+ LibURCU: ${liburcu_LIBS} ${liburcu_CFLAGS}
+ GnuTLS: ${gnutls_LIBS} ${gnutls_CFLAGS}
+ Libedit: ${libedit_LIBS} ${libedit_CFLAGS}
+ LMDB: ${lmdb_LIBS} ${lmdb_CFLAGS}
+ Config: ${conf_mapsize} MiB default mapsize
+
+ Prefix: ${knot_prefix}
+ Run dir: ${run_dir}
+ Storage dir: ${storage_dir}
+ Config dir: ${config_dir}
+ Module dir: ${module_dir}
+
+ Static modules: ${static_modules}
+ Shared modules: ${shared_modules}
+
+ Knot DNS libraries: yes
+ Knot DNS daemon: ${enable_daemon}
+ Knot DNS utilities: ${enable_utilities}
+ Knot DNS documentation: ${enable_documentation}
+
+ Use recvmmsg: ${enable_recvmmsg}
+ Use SO_REUSEPORT(_LB): ${enable_reuseport}
+ XDP support: ${enable_xdp}
+ DoQ support: ${enable_quic}
+ Socket polling: ${socket_polling}
+ Memory allocator: ${with_memory_allocator}
+ Fast zone parser: ${enable_fastparser}
+ Utilities with IDN: ${with_libidn}
+ Utilities with DoH: ${with_libnghttp2}
+ Utilities with Dnstap: ${enable_dnstap}
+ MaxMind DB support: ${enable_maxminddb}
+ Systemd integration: ${enable_systemd}
+ POSIX capabilities: ${enable_cap_ng}
+ PKCS #11 support: ${enable_pkcs11}
+ Ed25519 support: ${enable_ed25519}
+ Ed448 support: ${enable_ed448}
+ Reproducible signing: ${enable_repro_signing}
+ Code coverage: ${enable_code_coverage}
+ Sanitizer: ${with_sanitizer}
+ LibFuzzer: ${with_fuzzer}
+ OSS-Fuzz: ${with_oss_fuzz}"
+
+result_msg_esc=$(echo -n "$result_msg_base" | sed '$!s/$/\\n/' | tr -d '\n')
+
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIGURE_SUMMARY "$result_msg_esc"
+_ACEOF
+
+
+ac_config_files="$ac_config_files Makefile Doxyfile doc/Makefile tests/Makefile tests-fuzz/Makefile samples/Makefile distro/Makefile python/Makefile python/setup.py python/libknot/__init__.py src/Makefile src/libknot/xdp/Makefile src/knot/modules/static_modules.h"
+
+
+ac_config_files="$ac_config_files doc/modules.rst"
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+if test -z "${HAVE_DAEMON_TRUE}" && test -z "${HAVE_DAEMON_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_DAEMON\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_UTILS_TRUE}" && test -z "${HAVE_UTILS_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_UTILS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_DOCS_TRUE}" && test -z "${HAVE_DOCS_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_DOCS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_LIBUTILS_TRUE}" && test -z "${HAVE_LIBUTILS_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_LIBUTILS\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${FAST_PARSER_TRUE}" && test -z "${FAST_PARSER_FALSE}"; then
+ as_fn_error $? "conditional \"FAST_PARSER\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ENABLE_PKCS11_TRUE}" && test -z "${ENABLE_PKCS11_FALSE}"; then
+ as_fn_error $? "conditional \"ENABLE_PKCS11\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${EMBEDDED_LIBBPF_TRUE}" && test -z "${EMBEDDED_LIBBPF_FALSE}"; then
+ as_fn_error $? "conditional \"EMBEDDED_LIBBPF\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ENABLE_XDP_TRUE}" && test -z "${ENABLE_XDP_FALSE}"; then
+ as_fn_error $? "conditional \"ENABLE_XDP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_cookies_TRUE}" && test -z "${STATIC_MODULE_cookies_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_cookies\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_cookies_TRUE}" && test -z "${SHARED_MODULE_cookies_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_cookies\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_dnsproxy_TRUE}" && test -z "${STATIC_MODULE_dnsproxy_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_dnsproxy\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_dnsproxy_TRUE}" && test -z "${SHARED_MODULE_dnsproxy_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_dnsproxy\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_dnstap_TRUE}" && test -z "${STATIC_MODULE_dnstap_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_dnstap\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_dnstap_TRUE}" && test -z "${SHARED_MODULE_dnstap_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_dnstap\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_geoip_TRUE}" && test -z "${STATIC_MODULE_geoip_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_geoip\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_geoip_TRUE}" && test -z "${SHARED_MODULE_geoip_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_geoip\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_noudp_TRUE}" && test -z "${STATIC_MODULE_noudp_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_noudp\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_noudp_TRUE}" && test -z "${SHARED_MODULE_noudp_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_noudp\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_onlinesign_TRUE}" && test -z "${STATIC_MODULE_onlinesign_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_onlinesign\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_onlinesign_TRUE}" && test -z "${SHARED_MODULE_onlinesign_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_onlinesign\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_probe_TRUE}" && test -z "${STATIC_MODULE_probe_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_probe\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_probe_TRUE}" && test -z "${SHARED_MODULE_probe_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_probe\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_queryacl_TRUE}" && test -z "${STATIC_MODULE_queryacl_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_queryacl\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_queryacl_TRUE}" && test -z "${SHARED_MODULE_queryacl_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_queryacl\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_rrl_TRUE}" && test -z "${STATIC_MODULE_rrl_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_rrl\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_rrl_TRUE}" && test -z "${SHARED_MODULE_rrl_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_rrl\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_stats_TRUE}" && test -z "${STATIC_MODULE_stats_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_stats\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_stats_TRUE}" && test -z "${SHARED_MODULE_stats_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_stats\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_synthrecord_TRUE}" && test -z "${STATIC_MODULE_synthrecord_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_synthrecord\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_synthrecord_TRUE}" && test -z "${SHARED_MODULE_synthrecord_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_synthrecord\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${STATIC_MODULE_whoami_TRUE}" && test -z "${STATIC_MODULE_whoami_FALSE}"; then
+ as_fn_error $? "conditional \"STATIC_MODULE_whoami\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${SHARED_MODULE_whoami_TRUE}" && test -z "${SHARED_MODULE_whoami_FALSE}"; then
+ as_fn_error $? "conditional \"SHARED_MODULE_whoami\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_DNSTAP_TRUE}" && test -z "${HAVE_DNSTAP_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_DNSTAP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_LIBDNSTAP_TRUE}" && test -z "${HAVE_LIBDNSTAP_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_LIBDNSTAP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_MAXMINDDB_TRUE}" && test -z "${HAVE_MAXMINDDB_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_MAXMINDDB\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${EMBEDDED_LIBNGTCP2_TRUE}" && test -z "${EMBEDDED_LIBNGTCP2_FALSE}"; then
+ as_fn_error $? "conditional \"EMBEDDED_LIBNGTCP2\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${ENABLE_QUIC_TRUE}" && test -z "${ENABLE_QUIC_FALSE}"; then
+ as_fn_error $? "conditional \"ENABLE_QUIC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${USE_GNUTLS_MEMSET_TRUE}" && test -z "${USE_GNUTLS_MEMSET_FALSE}"; then
+ as_fn_error $? "conditional \"USE_GNUTLS_MEMSET\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${CODE_COVERAGE_ENABLED_TRUE}" && test -z "${CODE_COVERAGE_ENABLED_FALSE}"; then
+ as_fn_error $? "conditional \"CODE_COVERAGE_ENABLED\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${FUZZER_TRUE}" && test -z "${FUZZER_FALSE}"; then
+ as_fn_error $? "conditional \"FUZZER\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${OSS_FUZZ_TRUE}" && test -z "${OSS_FUZZ_FALSE}"; then
+ as_fn_error $? "conditional \"OSS_FUZZ\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_SPHINXBUILD_TRUE}" && test -z "${HAVE_SPHINXBUILD_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_SPHINXBUILD\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_PDFLATEX_TRUE}" && test -z "${HAVE_PDFLATEX_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_PDFLATEX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${HAVE_MAKEINFO_TRUE}" && test -z "${HAVE_MAKEINFO_FALSE}"; then
+ as_fn_error $? "conditional \"HAVE_MAKEINFO\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by knot $as_me 3.2.6, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <knot-dns@labs.nic.cz>."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+knot config.status 3.2.6
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
+configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+DLLTOOL \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
+nm_file_list_spec \
+lt_cv_truncate_bin \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+configure_time_dlsearch_path \
+configure_time_lt_sys_library_path; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "src/libknot/version.h") CONFIG_FILES="$CONFIG_FILES src/libknot/version.h" ;;
+ "src/libdnssec/version.h") CONFIG_FILES="$CONFIG_FILES src/libdnssec/version.h" ;;
+ "src/libzscanner/version.h") CONFIG_FILES="$CONFIG_FILES src/libzscanner/version.h" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "src/knotd.pc") CONFIG_FILES="$CONFIG_FILES src/knotd.pc" ;;
+ "src/libknot.pc") CONFIG_FILES="$CONFIG_FILES src/libknot.pc" ;;
+ "src/libdnssec.pc") CONFIG_FILES="$CONFIG_FILES src/libdnssec.pc" ;;
+ "src/libzscanner.pc") CONFIG_FILES="$CONFIG_FILES src/libzscanner.pc" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "Doxyfile") CONFIG_FILES="$CONFIG_FILES Doxyfile" ;;
+ "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
+ "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile" ;;
+ "tests-fuzz/Makefile") CONFIG_FILES="$CONFIG_FILES tests-fuzz/Makefile" ;;
+ "samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;;
+ "distro/Makefile") CONFIG_FILES="$CONFIG_FILES distro/Makefile" ;;
+ "python/Makefile") CONFIG_FILES="$CONFIG_FILES python/Makefile" ;;
+ "python/setup.py") CONFIG_FILES="$CONFIG_FILES python/setup.py" ;;
+ "python/libknot/__init__.py") CONFIG_FILES="$CONFIG_FILES python/libknot/__init__.py" ;;
+ "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+ "src/libknot/xdp/Makefile") CONFIG_FILES="$CONFIG_FILES src/libknot/xdp/Makefile" ;;
+ "src/knot/modules/static_modules.h") CONFIG_FILES="$CONFIG_FILES src/knot/modules/static_modules.h" ;;
+ "doc/modules.rst") CONFIG_FILES="$CONFIG_FILES doc/modules.rst" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Older Autoconf quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ case $CONFIG_FILES in #(
+ *\'*) :
+ eval set x "$CONFIG_FILES" ;; #(
+ *) :
+ set x $CONFIG_FILES ;; #(
+ *) :
+ ;;
+esac
+ shift
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
+ do
+ # Strip MF so we end up with the name of the file.
+ am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$am_mf" : 'X\(//\)[^/]' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$am_mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$am_mf" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ { echo "$as_me:$LINENO: cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles" >&5
+ (cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } || am_rc=$?
+ done
+ if test $am_rc -ne 0; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. If GNU make was not used, consider
+ re-running the configure script with MAKE=\"gmake\" (or whatever is
+ necessary). You can also try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ { am_dirpart=; unset am_dirpart;}
+ { am_filepart=; unset am_filepart;}
+ { am_mf=; unset am_mf;}
+ { am_rc=; unset am_rc;}
+ rm -f conftest-deps.mk
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 names of the tagged configurations supported by this script.
+available_tags=''
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shared archive member basename,for filename based shared library versioning on AIX.
+shared_archive_member_spec=$shared_archive_member_spec
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and where our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Detected run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
+
+# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
+configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "doc/modules.rst":F) cp doc/modules.rst "${srcdir}"/doc/modules.rst 2>/dev/null
+ abs_srcdir=$(cd "${srcdir}" && pwd)
+ ln -s -f "${abs_srcdir}"/src/knot/modules "${srcdir}"/doc 2>/dev/null ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
+$result_msg_base
+" >&5
+$as_echo "
+$result_msg_base
+" >&6; }
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..09c8e47
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,871 @@
+AC_PREREQ([2.69])
+
+m4_define([knot_VERSION_MAJOR], 3)dnl
+m4_define([knot_VERSION_MINOR], 2)dnl
+m4_define([knot_VERSION_PATCH], 6)dnl Leave empty if the master branch!
+m4_include([m4/knot-version.m4])
+
+AC_INIT([knot], [knot_PKG_VERSION], [knot-dns@labs.nic.cz])
+AM_INIT_AUTOMAKE([foreign std-options subdir-objects no-dist-gzip dist-xz -Wall -Werror])
+AM_SILENT_RULES([yes])
+AC_CONFIG_SRCDIR([src/knot])
+AC_CONFIG_HEADERS([src/config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_USE_SYSTEM_EXTENSIONS
+AC_CANONICAL_HOST
+
+# Update library versions
+# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+KNOT_LIB_VERSION([libknot], 13, 0, 0)
+KNOT_LIB_VERSION([libdnssec], 9, 0, 0)
+KNOT_LIB_VERSION([libzscanner], 4, 0, 0)
+
+AC_SUBST([KNOT_VERSION_MAJOR], knot_VERSION_MAJOR)
+AC_SUBST([KNOT_VERSION_MINOR], knot_VERSION_MINOR)
+AC_SUBST([KNOT_VERSION_PATCH], knot_VERSION_PATCH)
+
+# Store ./configure parameters and CFLAGS
+AC_DEFINE_UNQUOTED([CONFIGURE_PARAMS],["$*"],[Params passed to configure])
+AC_DEFINE_UNQUOTED([CONFIGURE_CFLAGS],["$CFLAGS"],[Passed CFLAGS from environment])
+
+AC_CONFIG_FILES([src/libknot/version.h
+ src/libdnssec/version.h
+ src/libzscanner/version.h])
+
+# Automatically update release date based on NEWS
+AC_PROG_SED
+release_date=$($SED -n 's/^Knot DNS .* (\(.*\))/\1/p;q;' ${srcdir}/NEWS)
+AC_SUBST([RELEASE_DATE], $release_date)
+
+# Set compiler compatibility flags
+AC_PROG_CC
+AC_PROG_CPP_WERROR
+
+# Set default CFLAGS
+CFLAGS="$CFLAGS -Wall -Wshadow -Werror=format-security -Werror=implicit -Werror=attributes -Wstrict-prototypes"
+
+AX_CHECK_COMPILE_FLAG("-fpredictive-commoning", [CFLAGS="$CFLAGS -fpredictive-commoning"], [], "-Werror")
+AX_CHECK_LINK_FLAG(["-Wl,--exclude-libs,ALL"], [ldflag_exclude_libs="-Wl,--exclude-libs,ALL"], [ldflag_exclude_libs=""], "")
+AC_SUBST([LDFLAG_EXCLUDE_LIBS], $ldflag_exclude_libs)
+
+# Get processor byte ordering
+AC_C_BIGENDIAN([endianity=big-endian], [endianity=little-endian])
+AS_IF([test "$endianity" = "little-endian"],[
+ AC_DEFINE([ENDIANITY_LITTLE], [1], [System is little-endian.])])
+
+# Check if an archiver is available
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+
+AC_PROG_INSTALL
+
+# Initialize libtool
+LT_INIT
+
+# Build DLLs on Cygwin/MSYS2
+AS_CASE([$host_os],
+ [cygwin* | msys*], [LT_NO_UNDEFINED="-no-undefined"]
+)
+AC_SUBST(LT_NO_UNDEFINED)
+
+# Use pkg-config
+PKG_PROG_PKG_CONFIG
+m4_ifdef([PKG_INSTALLDIR], [PKG_INSTALLDIR], [AC_SUBST([pkgconfigdir], ['${libdir}/pkgconfig'])])
+AC_CONFIG_FILES([src/knotd.pc
+ src/libknot.pc
+ src/libdnssec.pc
+ src/libzscanner.pc
+ ])
+
+# Default directories
+knot_prefix=$ac_default_prefix
+AS_IF([test "$prefix" != NONE], [knot_prefix=$prefix])
+
+run_dir="${localstatedir}/run/knot"
+AC_ARG_WITH([rundir],
+ AS_HELP_STRING([--with-rundir=path], [Path to run-time variable data (pid, sockets...). [default=LOCALSTATEDIR/run/knot]]),
+ [run_dir=$withval])
+AC_SUBST(run_dir)
+
+storage_dir="${localstatedir}/lib/knot"
+AC_ARG_WITH([storage],
+ AS_HELP_STRING([--with-storage=path], [Default storage directory (slave zones, persistent data). [default=LOCALSTATEDIR/lib/knot]]),
+ [storage_dir=$withval])
+AC_SUBST(storage_dir)
+
+config_dir="${sysconfdir}/knot"
+AC_ARG_WITH([configdir],
+ AS_HELP_STRING([--with-configdir=path], [Default directory for configuration. [default=SYSCONFDIR/knot]]),
+ [config_dir=$withval])
+AC_SUBST(config_dir)
+
+module_dir=
+module_instdir="${libdir}/knot/modules-${KNOT_VERSION_MAJOR}.${KNOT_VERSION_MINOR}"
+AC_ARG_WITH([moduledir],
+ AS_HELP_STRING([--with-moduledir=path], [Path to auto-loaded dynamic modules. [default not set]]),
+ [module_dir=$withval module_instdir=$module_dir])
+AC_SUBST(module_instdir)
+AC_SUBST(module_dir)
+
+# Build Knot DNS daemon
+AC_ARG_ENABLE([daemon],
+ AS_HELP_STRING([--disable-daemon], [Don't build Knot DNS main daemon]), [], [enable_daemon=yes])
+AM_CONDITIONAL([HAVE_DAEMON], [test "$enable_daemon" = "yes"])
+
+# Build Knot DNS modules
+AC_ARG_ENABLE([modules],
+ AS_HELP_STRING([--disable-modules], [Don't build Knot DNS modules]), [], [enable_modules=yes])
+
+# Build Knot DNS utilities
+AC_ARG_ENABLE([utilities],
+ AS_HELP_STRING([--disable-utilities], [Don't build Knot DNS utilities]), [], [enable_utilities=yes])
+AM_CONDITIONAL([HAVE_UTILS], [test "$enable_utilities" = "yes"])
+
+# Build Knot DNS documentation
+AC_ARG_ENABLE([documentation],
+ AS_HELP_STRING([--disable-documentation], [Don't build Knot DNS documentation]), [], [enable_documentation=yes])
+AM_CONDITIONAL([HAVE_DOCS], [test "$enable_documentation" = "yes"])
+
+AM_CONDITIONAL([HAVE_LIBUTILS], test "$enable_utilities" != "no" -o \
+ "$enable_daemon" != "no")
+######################
+# Generic dependencies
+######################
+
+AC_ARG_ENABLE([fastparser],
+ AS_HELP_STRING([--disable-fastparser], [Disable use of fast zone parser]),
+ [], [AS_IF([test -d ".git"], [enable_fastparser=no], [enable_fastparser=yes])]
+)
+AM_CONDITIONAL([FAST_PARSER], [test "$enable_fastparser" = "yes"])
+
+# GnuTLS crypto backend
+PKG_CHECK_MODULES([gnutls], [gnutls >= 3.3], [
+ save_CFLAGS=$CFLAGS
+ save_LIBS=$LIBS
+ CFLAGS="$CFLAGS $gnutls_CFLAGS"
+ LIBS="$LIBS $gnutls_LIBS"
+
+ AC_CHECK_FUNC([gnutls_pkcs11_copy_pubkey], [enable_pkcs11=yes], [enable_pkcs11=no])
+ AS_IF([test "$enable_pkcs11" = yes],
+ [AC_DEFINE([ENABLE_PKCS11], [1], [PKCS #11 support available])])
+
+ AC_CHECK_DECL([GNUTLS_PK_EDDSA_ED25519],
+ [AC_DEFINE([HAVE_ED25519], [1], [GnuTLS ED25519 support available])
+ enable_ed25519=yes],
+ [enable_ed25519=no],
+ [#include <gnutls/gnutls.h>])
+
+ AC_CHECK_DECL([GNUTLS_SIGN_EDDSA_ED448],
+ [AC_DEFINE([HAVE_ED448], [1], [GnuTLS ED448 support available])
+ enable_ed448=yes],
+ [enable_ed448=no],
+ [#include <gnutls/gnutls.h>])
+
+ AC_CHECK_DECL([GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE],
+ [AC_DEFINE([HAVE_GNUTLS_REPRODUCIBLE], [1], [GnuTLS reproducible signing available])
+ # Ensure the version is at least 3.6.10
+ AC_CHECK_FUNC([gnutls_aead_cipher_encryptv2], [enable_repro_signing=yes], [enable_repro_signing=no])],
+ [enable_repro_signing=no],
+ [#include <gnutls/abstract.h>])
+
+ AC_CHECK_FUNC([gnutls_privkey_sign_data2],
+ [AC_DEFINE([HAVE_SIGN_DATA2], [1], [gnutls_privkey_sign_data2 available])])
+
+ AC_CHECK_FUNC([gnutls_privkey_export_x509],
+ [AC_DEFINE([HAVE_EXPORT_X509], [1], [gnutls_privkey_export_x509 available])])
+
+ AC_CHECK_FUNC([gnutls_memset],
+ [AC_DEFINE([HAVE_GNUTLS_MEMSET], [1], [gnutls_memset available])
+ gnutls_memset=yes], [gnutls_memset=no])
+
+ AC_CHECK_FUNC([gnutls_early_cipher_get],
+ [AC_DEFINE([HAVE_GNUTLS_QUIC], [1], [gnutls_early_cipher_get available])
+ gnutls_quic=yes], [gnutls_quic=no])
+
+ CFLAGS=$save_CFLAGS
+ LIBS=$save_LIBS
+])
+AM_CONDITIONAL([ENABLE_PKCS11], [test "$enable_pkcs11" = "yes"])
+
+AC_ARG_ENABLE([recvmmsg],
+ AS_HELP_STRING([--enable-recvmmsg=auto|yes|no], [enable recvmmsg() network API [default=auto]]),
+ [], [enable_recvmmsg=auto])
+
+AS_CASE([$enable_recvmmsg],
+ [auto|yes],[
+ AC_CHECK_FUNC([recvmmsg],
+ [AC_CHECK_FUNC([sendmmsg],[enable_recvmmsg=yes],[enable_recvmmsg=no])],
+ [enable_recvmmsg=no])],
+ [no],[],
+ [*], [AC_MSG_ERROR([Invalid value of --enable-recvmmsg.]
+ )])
+
+AS_IF([test "$enable_recvmmsg" = yes],[
+ AC_DEFINE([ENABLE_RECVMMSG], [1], [Use recvmmsg().])])
+
+# XDP support
+AC_ARG_ENABLE([xdp],
+ AS_HELP_STRING([--enable-xdp=auto|yes|no], [enable eXpress Data Path [default=auto]]),
+ [], [enable_xdp=auto])
+
+AS_CASE([$enable_xdp],
+ [auto], [PKG_CHECK_MODULES([libbpf], [libbpf >= 0.0.6], [enable_xdp=yes], [enable_xdp=no])],
+ [yes], [PKG_CHECK_MODULES([libbpf], [libbpf >= 0.0.6], [enable_xdp=yes],
+ [PKG_CHECK_MODULES([libelf], [libelf],
+ [AC_CHECK_FUNC([reallocarray],
+ [enable_xdp=embedded
+ embedded_libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf/include -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"
+ embedded_libbpf_LIBS=$libelf_LIBS
+ libbpf_CFLAGS="-I\$(top_srcdir)/src/contrib/libbpf -I\$(top_srcdir)/src/contrib/libbpf/include/uapi"],
+ [enable_xdp=no
+ AC_MSG_WARN([reallocarray not available])])],
+ [AC_MSG_ERROR([libelf is required])])])],
+ [no], [],
+ [*], [AC_MSG_ERROR([Invalid value of --enable-xdp.])]
+)
+AM_CONDITIONAL([EMBEDDED_LIBBPF], [test "$enable_xdp" = "embedded"])
+AM_CONDITIONAL([ENABLE_XDP], [test "$enable_xdp" != "no"])
+AC_SUBST([embedded_libbpf_CFLAGS])
+AC_SUBST([embedded_libbpf_LIBS])
+
+AS_IF([test "$enable_xdp" == "yes"], [
+ PKG_CHECK_MODULES([libxdp], [libxdp], [enable_xdp=libxdp], [enable_xdp=yes])
+ AS_IF([test "$enable_xdp" == "libxdp"], [
+ AC_DEFINE([USE_LIBXDP], [1], [Use external libxdp and libbpf.])
+ libbpf_CFLAGS="$libbpf_CFLAGS $libxdp_CFLAGS"
+ libbpf_LIBS="$libbpf_LIBS $libxdp_LIBS"
+ ])]
+)
+AC_SUBST([libbpf_CFLAGS])
+AC_SUBST([libbpf_LIBS])
+
+AS_IF([test "$enable_xdp" != "no"],[
+ AC_DEFINE([ENABLE_XDP], [1], [Use eXpress Data Path.])])
+
+# Reuseport support
+AS_CASE([$host_os],
+ [freebsd*], [reuseport_opt=SO_REUSEPORT_LB],
+ [*], [reuseport_opt=SO_REUSEPORT],
+)
+
+AC_ARG_ENABLE([reuseport],
+ AS_HELP_STRING([--enable-reuseport=auto|yes|no],
+ [enable SO_REUSEPORT(_LB) support [default=auto]]),
+ [], [enable_reuseport=auto]
+)
+
+AS_CASE([$enable_reuseport],
+ [auto], [
+ AS_CASE([$host_os],
+ [freebsd*|linux*], [AC_CHECK_DECL([$reuseport_opt],
+ [enable_reuseport=yes],
+ [enable_reuseport=no],
+ [#include <sys/socket.h>
+ ])],
+ [*], [enable_reuseport=no]
+ )],
+ [yes], [AC_CHECK_DECL([$reuseport_opt], [],
+ [AC_MSG_ERROR([SO_REUSEPORT(_LB) not supported.])],
+ [#include <sys/socket.h>
+ ])],
+ [no], [],
+ [*], [AC_MSG_ERROR([Invalid value of --enable-reuseport.])]
+)
+
+AS_IF([test "$enable_reuseport" = yes],[
+ AC_DEFINE([ENABLE_REUSEPORT], [1], [Use SO_REUSEPORT(_LB).])])
+
+#########################################
+# Dependencies needed for Knot DNS daemon
+#########################################
+
+# Systemd integration
+AC_ARG_ENABLE([systemd],
+ AS_HELP_STRING([--enable-systemd=auto|yes|no], [enable systemd integration [default=auto]]),
+ [enable_systemd="$enableval"], [enable_systemd=auto])
+
+AS_IF([test "$enable_daemon" = "yes"],[
+
+AS_IF([test "$enable_systemd" != "no"],[
+ AS_CASE([$enable_systemd],
+ [auto],[PKG_CHECK_MODULES([systemd], [libsystemd], [enable_systemd=yes], [
+ PKG_CHECK_MODULES([systemd], [libsystemd-daemon libsystemd-journal], [enable_systemd=yes], [enable_systemd=no])])],
+ [yes],[PKG_CHECK_MODULES([systemd], [libsystemd], [], [
+ PKG_CHECK_MODULES([systemd], [libsystemd-daemon libsystemd-journal])])],
+ [*],[AC_MSG_ERROR([Invalid value of --enable-systemd.])])
+ ])
+
+AS_IF([test "$enable_systemd" = "yes"],[
+ AC_DEFINE([ENABLE_SYSTEMD], [1], [Use systemd integration.])
+ AC_CHECK_HEADER([systemd/sd-bus.h],
+ [AC_DEFINE([ENABLE_DBUS], [1], [systemd D-Bus available])],
+ [enable_systemd="yes (without D-Bus)"])
+])
+
+]) dnl enable_daemon
+
+# Socket polling method
+socket_polling=
+AC_ARG_WITH([socket-polling],
+ AS_HELP_STRING([--with-socket-polling=auto|poll|epoll|kqueue|libkqueue],
+ [Use specific socket polling method [default=auto]]),
+ [socket_polling=$withval], [socket_polling=auto]
+)
+
+AS_CASE([$socket_polling],
+ [auto], [AC_CHECK_FUNCS([kqueue],
+ [AC_DEFINE([HAVE_KQUEUE], [1], [kqueue available])
+ socket_polling=kqueue],
+ [AC_CHECK_FUNCS([epoll_create],
+ [AC_DEFINE([HAVE_EPOLL], [1], [epoll available])
+ socket_polling=epoll],
+ [socket_polling=poll])])],
+ [poll], [socket_polling=poll],
+ [epoll], [AC_CHECK_FUNCS([epoll_create],
+ [AC_DEFINE([HAVE_EPOLL], [1], [epoll available])
+ socket_polling=epoll],
+ [AC_MSG_ERROR([epoll not available.])])],
+ [kqueue], [AC_CHECK_FUNCS([kqueue],
+ [AC_DEFINE([HAVE_KQUEUE], [1], [kqueue available])
+ socket_polling=kqueue],
+ [AC_MSG_ERROR([kqueue not available.])])],
+ [libkqueue], [PKG_CHECK_MODULES([libkqueue], [libkqueue],
+ [AC_DEFINE([HAVE_KQUEUE], [1], [libkqueue available])
+ socket_polling=libkqueue],
+ [AC_MSG_ERROR([libkqueue not available.])])],
+ [*], [AC_MSG_ERROR([Invalid value of --socket-polling.])]
+)
+
+# Alternative memory allocator
+malloc_LIBS=
+AC_ARG_WITH([memory-allocator],
+ AS_HELP_STRING([--with-memory-allocator=auto|LIBRARY],
+ [Use specific memory allocator for the server (e.g. jemalloc) [default=auto]]),
+ AS_CASE([$withval],
+ [auto], [],
+ [*], [malloc_LIBS="-l$withval"]
+ )
+ with_memory_allocator=[$withval]
+)
+AS_IF([test "$with_memory_allocator" = ""], [with_memory_allocator="auto"])
+AC_SUBST([malloc_LIBS])
+
+dnl Check for userspace-rcu library
+AC_ARG_WITH(urcu,
+[ --with-urcu=DIR where to find userspace-rcu library])
+
+AS_IF([test "$enable_daemon" = "yes"],[
+
+AS_IF([test "$with_urcu" != "no"], [
+ PKG_CHECK_MODULES([liburcu], liburcu, [
+ liburcu_PKGCONFIG="liburcu >= 0.4.0"
+ AC_SUBST([liburcu_PKGCONFIG])
+ with_urcu=yes
+ ],[
+ for try_urcu in "$with_urcu" "" "/usr/local"; do
+ save_LIBS="$LIBS"
+ save_CPPFLAGS="$CPPFLAGS"
+
+ AS_IF([test -d "$try_urcu"], [
+ liburcu_CFLAGS="-I$try_urcu/include"
+ liburcu_LIBS="-L$try_urcu/lib"
+ ],[
+ liburcu_CFLAGS=""
+ liburcu_LIBS=""
+ ])
+
+ CPPFLAGS="$CPPFLAGS $liburcu_CFLAGS"
+ LIBS="$LIBS $liburcu_LIBS"
+
+ AC_SEARCH_LIBS([rcu_set_pointer_sym], [urcu], [
+ with_urcu=yes
+ liburcu_LIBS="$liburcu_LIBS -lurcu"
+ AC_SUBST([liburcu_CFLAGS])
+ AC_SUBST([liburcu_LIBS])
+ break
+ ],[
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ with_urcu=no
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_rcu_set_pointer_sym
+ ])
+ done
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ AS_IF([test "$with_urcu" = "no"],[
+ AC_MSG_ERROR([liburcu is required])
+ ])
+ ])
+])
+
+])
+
+static_modules=""
+shared_modules=""
+static_modules_declars=""
+static_modules_init=""
+doc_modules=""
+
+KNOT_MODULE([cookies], "yes")
+KNOT_MODULE([dnsproxy], "yes", "non-shareable")
+KNOT_MODULE([dnstap], "no")
+KNOT_MODULE([geoip], "yes")
+KNOT_MODULE([noudp], "yes")
+KNOT_MODULE([onlinesign], "yes", "non-shareable")
+KNOT_MODULE([probe], "yes")
+KNOT_MODULE([queryacl], "yes")
+KNOT_MODULE([rrl], "yes")
+KNOT_MODULE([stats], "yes")
+KNOT_MODULE([synthrecord], "yes")
+KNOT_MODULE([whoami], "yes")
+
+AC_SUBST([STATIC_MODULES_DECLARS], [$(printf "$static_modules_declars")])
+AM_SUBST_NOTMAKE([STATIC_MODULES_DECLARS])
+AC_SUBST([STATIC_MODULES_INIT], [$(printf "$static_modules_init")])
+AM_SUBST_NOTMAKE([STATIC_MODULES_INIT])
+AC_SUBST([DOC_MODULES], [$(printf "$doc_modules")])
+AM_SUBST_NOTMAKE([DOC_MODULES])
+
+# Check for Dnstap
+AC_ARG_ENABLE([dnstap],
+ AS_HELP_STRING([--enable-dnstap], [Enable dnstap support for kdig (requires fstrm, protobuf-c)]),
+ [], [enable_dnstap=no])
+
+AS_IF([test "$enable_dnstap" != "no" -o "$STATIC_MODULE_dnstap" != "no" -o "$SHARED_MODULE_dnstap" != "no"],[
+ AC_PATH_PROG([PROTOC_C], [protoc-c])
+ AS_IF([test -z "$PROTOC_C"],[
+ AC_MSG_ERROR([The protoc-c program was not found. Please install protobuf-c!])
+ ])
+ PKG_CHECK_MODULES([libfstrm], [libfstrm])
+ PKG_CHECK_MODULES([libprotobuf_c], [libprotobuf-c >= 1.0.0])
+ AC_SUBST([DNSTAP_CFLAGS], ["$libfstrm_CFLAGS $libprotobuf_c_CFLAGS"])
+ AC_SUBST([DNSTAP_LIBS], ["$libfstrm_LIBS $libprotobuf_c_LIBS"])
+])
+
+AS_IF([test "$enable_dnstap" != "no"],[
+ AC_DEFINE([USE_DNSTAP], [1], [Define to 1 to enable dnstap support for kdig])
+])
+AM_CONDITIONAL([HAVE_DNSTAP], test "$enable_dnstap" != "no")
+
+AM_CONDITIONAL([HAVE_LIBDNSTAP], test "$enable_dnstap" != "no" -o \
+ "$STATIC_MODULE_dnstap" != "no" -o \
+ "$SHARED_MODULE_dnstap" != "no")
+# MaxMind DB for the GeoIP module
+AC_ARG_ENABLE([maxminddb],
+ AS_HELP_STRING([--enable-maxminddb=auto|yes|no], [enable MaxMind DB [default=auto]]),
+ [enable_maxminddb="$enableval"], [enable_maxminddb=auto])
+
+AS_IF([test "$enable_daemon" = "no"],[enable_maxminddb=no])
+AS_CASE([$enable_maxminddb],
+ [no],[],
+ [auto],[PKG_CHECK_MODULES([libmaxminddb], [libmaxminddb], [enable_maxminddb=yes], [enable_maxminddb=no])],
+ [yes], [PKG_CHECK_MODULES([libmaxminddb], [libmaxminddb])],
+ [*],[
+ save_CFLAGS="$CFLAGS"
+ save_LIBS="$LIBS"
+ AS_IF([test "$enable_maxminddb" != ""],[
+ LIBS="$LIBS -L$enable_maxminddb"
+ CFLAGS="$CFLAGS -I$enable_maxminddb/include"
+ ])
+ AC_SEARCH_LIBS([MMDB_open], [maxminddb], [
+ AS_IF([test "$enable_maxminddb" != ""], [
+ libmaxminddb_CFLAGS="-I$enable_maxminddb/include"
+ libmaxminddb_LIBS="-L$enable_maxminddb -lmaxminddb"
+ ],[
+ libmaxminddb_CFLAGS=""
+ libmaxminddb_LIBS="$ac_cv_search_MMDB_open"
+ ])
+ ],[AC_MSG_ERROR("not found in `$enable_maxminddb'")])
+ CFLAGS="$save_CFLAGS"
+ LIBS="$save_LIBS"
+ AC_SUBST([libmaxminddb_CFLAGS])
+ AC_SUBST([libmaxminddb_LIBS])
+ enable_maxminddb=yes
+ ])
+
+AS_IF([test "$enable_maxminddb" = yes], [AC_DEFINE([HAVE_MAXMINDDB], [1], [Define to 1 to enable MaxMind DB.])])
+AM_CONDITIONAL([HAVE_MAXMINDDB], [test "$enable_maxminddb" = yes])
+
+AC_ARG_WITH([lmdb],
+ [AS_HELP_STRING([--with-lmdb=DIR], [explicit location where to find LMDB])]
+)
+PKG_CHECK_MODULES([lmdb], [lmdb >= 0.9.15], [], [
+ save_CPPFLAGS=$CPPFLAGS
+ save_LIBS=$LIBS
+
+ have_lmdb=no
+
+ for try_lmdb in "$with_lmdb" "" "/usr/local" "/usr/pkg"; do
+ AS_IF([test -d "$try_lmdb"], [
+ lmdb_CFLAGS="-I$try_lmdb/include"
+ lmdb_LIBS="-L$try_lmdb/lib"
+ ],[
+ lmdb_CFLAGS=""
+ lmdb_LIBS=""
+ ])
+
+ CPPFLAGS="$save_CPPFLAGS $lmdb_CFLAGS"
+ LIBS="$save_LIBS $lmdb_LIBS"
+
+ AC_SEARCH_LIBS([mdb_txn_id], [lmdb], [
+ have_lmdb=yes
+ lmdb_LIBS="$lmdb_LIBS -llmdb"
+ AC_SUBST([lmdb_CFLAGS])
+ AC_SUBST([lmdb_LIBS])
+ break
+ ])
+
+ # do not cache result of AC_SEARCH_LIBS test
+ unset ac_cv_search_mdb_txn_id
+ done
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+
+ AS_IF([test "$have_lmdb" = "no"], [
+ AC_MSG_ERROR([lmdb library not found])
+ ])
+])
+
+# LMDB mapping sizes
+conf_mapsize_default=500
+AC_ARG_WITH([conf_mapsize],
+ AS_HELP_STRING([--with-conf-mapsize=NUM], [Configuration DB mapsize in MiB [default=$conf_mapsize_default]]),
+ [conf_mapsize=$withval],[conf_mapsize=$conf_mapsize_default])
+
+AS_CASE([$conf_mapsize],
+ [yes],[conf_mapsize=$conf_mapsize_default],
+ [no], [AC_MSG_ERROR([conf_mapsize must be a number])],
+ [*], [AS_IF([test $conf_mapsize != $(( $conf_mapsize + 0 ))],
+ [AC_MSG_ERROR(conf_mapsize must be an integer number)])])
+AC_DEFINE_UNQUOTED([CONF_MAPSIZE], [$conf_mapsize], [Configuration DB mapsize.])
+AC_SUBST(conf_mapsize)
+
+# libedit
+AS_IF([test "$enable_daemon" = "yes" -o "$enable_utilities" = "yes"], [
+ PKG_CHECK_MODULES([libedit], [libedit], [with_libedit=yes], [
+ with_libedit=no
+ AC_CHECK_HEADER([histedit.h], [
+ # workaround for OpenBSD
+ AS_CASE([$host_os],
+ [openbsd*], [libedit_deps=-lcurses],
+ [libedit_deps=]
+ )
+ AC_CHECK_LIB([edit], [el_init], [
+ with_libedit=yes
+ libedit_CFLAGS=
+ libedit_LIBS="-ledit $libedit_deps"
+ ], [], [$libedit_deps]
+ )
+ ])
+ ])
+ AS_IF([test "$with_libedit" != "yes"], [
+ AC_MSG_ERROR([libedit not found])
+ ])
+], [
+ with_libedit=no
+ libedit_CFLAGS=
+ libedit_LIBS=
+])
+
+# QUIC support
+AC_ARG_ENABLE([quic],
+ AS_HELP_STRING([--enable-quic=auto|yes|no], [Support DoQ (needs libngtcp2 >= 0.13.0, gnutls >= 3.7.2) [default=auto]]),
+ [], [enable_quic=auto])
+
+AS_CASE([$enable_quic],
+ [auto], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls], [enable_quic=yes], [enable_quic=no])],
+ [yes], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 >= 0.13.0 libngtcp2_crypto_gnutls], [enable_quic=yes],
+ AS_IF([test "$gnutls_quic" = "yes"],
+ [enable_quic=embedded
+ embedded_libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2 -I\$(top_srcdir)/src/contrib/libngtcp2/ngtcp2/lib"
+ embedded_libngtcp2_LIBS=$libelf_LIBS
+ libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2"],
+ [enable_quic=no
+ AC_MSG_WARN([gnutls >= 3.7.2 is required for QUIC])]))],
+ [no], [],
+ [*], [AC_MSG_ERROR([Invalid value of --enable-quic.])]
+)
+AM_CONDITIONAL([EMBEDDED_LIBNGTCP2], [test "$enable_quic" = "embedded"])
+AM_CONDITIONAL([ENABLE_QUIC], [test "$enable_quic" != "no"])
+AC_SUBST([embedded_libngtcp2_CFLAGS])
+AC_SUBST([embedded_libngtcp2_LIBS])
+AC_SUBST([libngtcp2_CFLAGS])
+AC_SUBST([libngtcp2_LIBS])
+
+AS_IF([test "$enable_quic" != "no"], [
+ AC_DEFINE([ENABLE_QUIC], [1], [Define to 1 to enable DoQ support using libngtcp2 and GnuTLS])])
+
+############################################
+# Dependencies needed for Knot DNS utilities
+############################################
+
+dnl Check for libidn.
+AC_ARG_WITH(libidn,
+ AS_HELP_STRING([--with-libidn=[DIR]], [Support IDN (needs GNU libidn2 or libidn)]),
+ with_libidn=$withval,
+ with_libidn=yes
+)
+
+dnl Check for libnghttp2.
+AC_ARG_WITH(libnghttp2,
+ AS_HELP_STRING([--with-libnghttp2=[DIR]], [Support DoH (needs libnghttp2)]),
+ with_libnghttp2=$withval,
+ with_libnghttp2=yes
+)
+
+AS_IF([test "$enable_utilities" = "yes"], [
+ AS_IF([test "$with_libidn" != "no"], [
+ PKG_CHECK_MODULES([libidn2], [libidn2 >= 2.0.0], [
+ with_libidn=libidn2
+ AC_DEFINE([LIBIDN], [1], [Define to 1 to enable IDN support])
+ AC_DEFINE([LIBIDN_HEADER], [<idn2.h>], [Define to proper libidn header])
+ ], [
+ PKG_CHECK_MODULES([libidn], [libidn >= 0.0.0], [
+ with_libidn=libidn
+ AC_DEFINE([LIBIDN], [1], [Define to 1 to enable IDN support])
+ AC_DEFINE([LIBIDN_HEADER], [<idna.h>], [Define to proper libidn header])
+ ], [
+ with_libidn=no
+ AC_MSG_WARN([libidn2 or libidn not found])
+ ])
+ ])
+ ])
+
+ AS_IF([test "$with_libnghttp2" != "no"], [
+ PKG_CHECK_MODULES([libnghttp2], [libnghttp2], [
+ with_libnghttp2=libnghttp2
+ AC_DEFINE([LIBNGHTTP2], [1], [Define to 1 to enable DoH support])
+ ], [
+ with_libnghttp2=no
+ AC_MSG_WARN([libnghttp2 not found])
+ ])
+ ])
+
+ AS_IF([test "$enable_xdp" != "no"], [
+ PKG_CHECK_MODULES([libmnl], [libmnl], [], [
+ AC_MSG_ERROR([libmnl not found])
+ ])
+ ])
+]) # Knot DNS utilities dependencies
+
+AC_ARG_ENABLE([cap-ng],
+ AS_HELP_STRING([--enable-cap-ng=auto|no], [enable POSIX capabilities [default=auto]]),
+ [enable_cap_ng="$enableval"], [enable_cap_ng=auto])
+
+AS_IF([test "$enable_daemon" = "yes"], [
+
+AS_IF([test "$enable_cap_ng" != "no"],[
+ PKG_CHECK_MODULES([cap_ng], [cap-ng], [enable_cap_ng=yes], [
+ enable_cap_ng=no
+ AC_CHECK_HEADER([cap-ng.h], [
+ save_LIBS="$LIBS"
+ AC_SEARCH_LIBS([capng_apply], [cap-ng], [
+ AS_IF([test "$ac_cv_search_capng_apply" != "none required"],
+ [cap_ng_LIBS="$ac_cv_search_capng_apply"], [cap_ng_LIBS=])
+ AC_SUBST([cap_ng_LIBS])
+ enable_cap_ng=yes
+ ])
+ LIBS="$save_LIBS"
+ ])
+ ])
+], [
+ enable_cap_ng=no
+ cap_ng_LIBS=
+])])
+
+AS_IF([test "$enable_cap_ng" = yes],
+ [AC_DEFINE([ENABLE_CAP_NG], [1], [POSIX capabilities available])]
+)
+
+save_LIBS="$LIBS"
+AC_SEARCH_LIBS([pthread_create], [pthread], [
+ AS_IF([test "$ac_cv_search_pthread_create" != "none required"],
+ [pthread_LIBS="$ac_cv_search_pthread_create"], [pthread_LIBS=])
+ AC_SUBST([pthread_LIBS])
+],[
+ AC_MSG_ERROR([pthreads not found])
+])
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+AC_SEARCH_LIBS([dlopen], [dl], [
+ AS_IF([test "$ac_cv_search_dlopen" != "none required"],
+ [dlopen_LIBS="$ac_cv_search_dlopen"], [dlopen_LIBS=])
+ AC_SUBST([dlopen_LIBS])
+],[
+ AC_MSG_ERROR([dlopen not found])
+])
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+AC_SEARCH_LIBS([pow], [m], [
+ AS_IF([test "$ac_cv_search_pow" != "none required"],
+ [math_LIBS="$ac_cv_search_pow"], [math_LIBS=])
+ AC_SUBST([math_LIBS])
+],[
+ AC_MSG_ERROR([math not found])
+])
+LIBS="$save_LIBS"
+
+save_LIBS="$LIBS"
+AC_SEARCH_LIBS([pthread_setaffinity_np], [pthread], [
+ AC_DEFINE([HAVE_PTHREAD_SETAFFINITY_NP], [1],
+ [Define to 1 if you have the pthread_setaffinity_np function.])
+])
+LIBS="$save_LIBS"
+
+# Checks for header files.
+AC_HEADER_RESOLV
+AC_CHECK_HEADERS_ONCE([pthread_np.h stdatomic.h sys/uio.h bsd/string.h])
+
+# Checks for optional library functions.
+AC_CHECK_FUNCS([accept4 clock_gettime fgetln getline initgroups malloc_trim \
+ setgroups strlcat strlcpy sysctlbyname])
+
+# Check for robust memory cleanup implementations.
+AC_CHECK_FUNC([explicit_bzero], [
+ AC_DEFINE([HAVE_EXPLICIT_BZERO], [1], [explicit_bzero available])
+ explicit_bzero=yes], [explicit_bzero=no]
+)
+AC_CHECK_FUNC([explicit_memset], [
+ AC_DEFINE([HAVE_EXPLICIT_MEMSET], [1], [explicit_memset available])
+ explicit_memset=yes], [explicit_memset=no]
+)
+AM_CONDITIONAL([USE_GNUTLS_MEMSET], [test "$explicit_bzero" = "no" -a "$explicit_memset" = "no" -a "$gnutls_memset" = "yes"])
+
+# Check for mandatory library functions.
+AC_CHECK_FUNC([vasprintf], [], [
+ AC_MSG_ERROR([vasprintf support in the libc is required])])
+
+# Check for cpu_set_t/cpuset_t compatibility
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[cpu_set_t set; CPU_ZERO(&set);]])],
+[AC_DEFINE(HAVE_CPUSET_LINUX, 1, [Define if Linux-like cpu_set_t exists.])])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread_np.h>]], [[cpuset_t set; CPU_ZERO(&set);]])],
+[AC_DEFINE(HAVE_CPUSET_BSD, 1, [Define if FreeBSD-like cpuset_t exists.])])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sched.h>]], [[cpuset_t* set = cpuset_create(); cpuset_destroy(set);]])],
+[AC_DEFINE(HAVE_CPUSET_NETBSD, 1, [Define if cpuset_t and cpuset(3) exists.])])
+
+# Check for '__atomic' compiler builtin atomic functions.
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#include <stdint.h>]],
+ [[uint64_t val = 0; __atomic_add_fetch(&val, 1, __ATOMIC_RELAXED);]])],
+ [AC_DEFINE(HAVE_ATOMIC, 1, [Define to 1 if you have '__atomic' functions.])]
+)
+
+# Check for '__sync' compiler builtin atomic functions.
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#include <stdint.h>]],
+ [[int val = 0; __sync_add_and_fetch(&val, 1);]])],
+ [AC_DEFINE(HAVE_SYNC_ATOMIC, 1, [Define to 1 if you have '__sync' functions.])]
+)
+
+# Prepare CFLAG_VISIBILITY to be used where needed
+gl_VISIBILITY()
+
+# Add code coverage macro
+AX_CODE_COVERAGE
+
+AX_SANITIZER
+AS_IF([test -n "$sanitizer_CFLAGS"], [CFLAGS="$CFLAGS $sanitizer_CFLAGS"])
+AM_CONDITIONAL([FUZZER], [test "$with_fuzzer" != "no"])
+AM_CONDITIONAL([OSS_FUZZ], [test "$with_oss_fuzz" != "no"])
+
+AS_IF([test "$enable_documentation" = "yes"],[
+
+AC_PATH_PROG([SPHINXBUILD], [sphinx-build], [false])
+AS_IF([test "$SPHINXBUILD" = "false"],
+ [AC_MSG_WARN([missing 'sphinx-build' executable for documentation generation])]
+)
+AC_PATH_PROG([PDFLATEX], [pdflatex], [false])
+AC_PATH_PROG([MAKEINFO], [makeinfo], [false])
+
+])
+
+AM_CONDITIONAL([HAVE_SPHINXBUILD], test "$SPHINXBUILD" != "false")
+AM_CONDITIONAL([HAVE_PDFLATEX], test "$PDFLATEX" != "false")
+AM_CONDITIONAL([HAVE_MAKEINFO], test "$MAKEINFO" != "false")
+
+# Strip -fdebug-prefix-map= parameters from flags for better reproducibility of binaries.
+filtered_cflags=$(echo -n "$CFLAGS" | \
+ sed 's/[[^[:alnum:]]]-f[[^[:space:]]]*-prefix-map=[[^[:space:]]]*//g')
+filtered_cppflags=$(echo -n "$CPPFLAGS" | \
+ sed 's/[[^[:alnum:]]]-f[[^[:space:]]]*-prefix-map=[[^[:space:]]]*//g')
+
+result_msg_base=" Knot DNS $VERSION
+
+ Target: $host_os $host_cpu $endianity
+ Compiler: ${CC}
+ CFLAGS: ${filtered_cflags} ${filtered_cppflags}
+ LIBS: ${LIBS} ${LDFLAGS}
+ LibURCU: ${liburcu_LIBS} ${liburcu_CFLAGS}
+ GnuTLS: ${gnutls_LIBS} ${gnutls_CFLAGS}
+ Libedit: ${libedit_LIBS} ${libedit_CFLAGS}
+ LMDB: ${lmdb_LIBS} ${lmdb_CFLAGS}
+ Config: ${conf_mapsize} MiB default mapsize
+
+ Prefix: ${knot_prefix}
+ Run dir: ${run_dir}
+ Storage dir: ${storage_dir}
+ Config dir: ${config_dir}
+ Module dir: ${module_dir}
+
+ Static modules: ${static_modules}
+ Shared modules: ${shared_modules}
+
+ Knot DNS libraries: yes
+ Knot DNS daemon: ${enable_daemon}
+ Knot DNS utilities: ${enable_utilities}
+ Knot DNS documentation: ${enable_documentation}
+
+ Use recvmmsg: ${enable_recvmmsg}
+ Use SO_REUSEPORT(_LB): ${enable_reuseport}
+ XDP support: ${enable_xdp}
+ DoQ support: ${enable_quic}
+ Socket polling: ${socket_polling}
+ Memory allocator: ${with_memory_allocator}
+ Fast zone parser: ${enable_fastparser}
+ Utilities with IDN: ${with_libidn}
+ Utilities with DoH: ${with_libnghttp2}
+ Utilities with Dnstap: ${enable_dnstap}
+ MaxMind DB support: ${enable_maxminddb}
+ Systemd integration: ${enable_systemd}
+ POSIX capabilities: ${enable_cap_ng}
+ PKCS #11 support: ${enable_pkcs11}
+ Ed25519 support: ${enable_ed25519}
+ Ed448 support: ${enable_ed448}
+ Reproducible signing: ${enable_repro_signing}
+ Code coverage: ${enable_code_coverage}
+ Sanitizer: ${with_sanitizer}
+ LibFuzzer: ${with_fuzzer}
+ OSS-Fuzz: ${with_oss_fuzz}"
+
+result_msg_esc=$(echo -n "$result_msg_base" | sed '$!s/$/\\n/' | tr -d '\n')
+
+AC_DEFINE_UNQUOTED([CONFIGURE_SUMMARY],["$result_msg_esc"],[Configure summary])
+
+AC_CONFIG_FILES([Makefile
+ Doxyfile
+ doc/Makefile
+ tests/Makefile
+ tests-fuzz/Makefile
+ samples/Makefile
+ distro/Makefile
+ python/Makefile
+ python/setup.py
+ python/libknot/__init__.py
+ src/Makefile
+ src/libknot/xdp/Makefile
+ src/knot/modules/static_modules.h
+ ])
+
+AC_CONFIG_FILES([doc/modules.rst],
+ [cp doc/modules.rst "${srcdir}"/doc/modules.rst 2>/dev/null
+ abs_srcdir=$(cd "${srcdir}" && pwd)
+ ln -s -f "${abs_srcdir}"/src/knot/modules "${srcdir}"/doc 2>/dev/null])
+
+AC_OUTPUT
+AC_MSG_RESULT([
+$result_msg_base
+])
diff --git a/depcomp b/depcomp
new file mode 100755
index 0000000..6b39162
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,791 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1999-2020 Free Software Foundation, Inc.
+
+# 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 2, 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/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try '$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by 'PROGRAMS ARGS'.
+ object Object file output by 'PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputting dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'. Note that this directory component will
+# be either empty or ending with a '/' character. This is deliberate.
+set_dir_from ()
+{
+ case $1 in
+ */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+ *) dir=;;
+ esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+ base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+ echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+ # If the compiler actually managed to produce a dependency file,
+ # post-process it.
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form 'foo.o: dependency.h'.
+ # Do two passes, one to just change these to
+ # $object: dependency.h
+ # and one to simply output
+ # dependency.h:
+ # which is needed to avoid the deleted-header problem.
+ { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+ sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+ } > "$depfile"
+ rm -f "$tmpdepfile"
+ else
+ make_dummy_depfile
+ fi
+}
+
+# A tabulation character.
+tab=' '
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+cygpath_u="cygpath -u -f -"
+if test "$depmode" = msvcmsys; then
+ # This is just like msvisualcpp but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u='sed s,\\\\,/,g'
+ depmode=msvisualcpp
+fi
+
+if test "$depmode" = msvc7msys; then
+ # This is just like msvc7 but w/o cygpath translation.
+ # Just convert the backslash-escaped backslashes to single forward
+ # slashes to satisfy depend.m4
+ cygpath_u='sed s,\\\\,/,g'
+ depmode=msvc7
+fi
+
+if test "$depmode" = xlc; then
+ # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
+ gccflag=-qmakedep=gcc,-MF
+ depmode=gcc
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+## Unfortunately, FreeBSD c89 acceptance of flags depends upon
+## the command line argument order; so add the flags where they
+## appear in depend2.am. Note that the slowdown incurred here
+## affects only configure: in makefiles, %FASTDEP% shortcuts this.
+ for arg
+ do
+ case $arg in
+ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
+ *) set fnord "$@" "$arg" ;;
+ esac
+ shift # fnord
+ shift # $arg
+ done
+ "$@"
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say). Also, it might not be
+## supported by the other compilers which use the 'gcc' depmode.
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ # The second -e expression handles DOS-style file names with drive
+ # letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the "deleted header file" problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+## Some versions of gcc put a space before the ':'. On the theory
+## that the space means something, we add a space to the output as
+## well. hp depmode also adds that space, but also prefixes the VPATH
+## to the object. Take care to not repeat it in the output.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like '#:fec' to the end of the
+ # dependency line.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
+ | tr "$nl" ' ' >> "$depfile"
+ echo >> "$depfile"
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> "$depfile"
+ else
+ make_dummy_depfile
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+xlc)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts '$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ set_dir_from "$object"
+ set_base_from "$object"
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$base.u
+ tmpdepfile3=$dir.libs/$base.u
+ "$@" -Wc,-M
+ else
+ tmpdepfile1=$dir$base.u
+ tmpdepfile2=$dir$base.u
+ tmpdepfile3=$dir$base.u
+ "$@" -M
+ fi
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ aix_post_process_depfile
+ ;;
+
+tcc)
+ # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+ # FIXME: That version still under development at the moment of writing.
+ # Make that this statement remains true also for stable, released
+ # versions.
+ # It will wrap lines (doesn't matter whether long or short) with a
+ # trailing '\', as in:
+ #
+ # foo.o : \
+ # foo.c \
+ # foo.h \
+ #
+ # It will put a trailing '\' even on the last line, and will use leading
+ # spaces rather than leading tabs (at least since its commit 0394caf7
+ # "Emit spaces for -MD").
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+ # We have to change lines of the first kind to '$object: \'.
+ sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+ # And for each line of the second kind, we have to emit a 'dep.h:'
+ # dummy dependency, to avoid the deleted-header problem.
+ sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file. A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+ # Portland's C compiler understands '-MD'.
+ # Will always output deps to 'file.d' where file is the root name of the
+ # source file under compilation, even if file resides in a subdirectory.
+ # The object file name does not affect the name of the '.d' file.
+ # pgcc 10.2 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using '\' :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+ set_dir_from "$object"
+ # Use the source, not the object, to determine the base name, since
+ # that's sadly what pgcc will do too.
+ set_base_from "$source"
+ tmpdepfile=$base.d
+
+ # For projects that build the same source file twice into different object
+ # files, the pgcc approach of using the *source* file root name can cause
+ # problems in parallel builds. Use a locking strategy to avoid stomping on
+ # the same $tmpdepfile.
+ lockdir=$base.d-lock
+ trap "
+ echo '$0: caught signal, cleaning up...' >&2
+ rmdir '$lockdir'
+ exit 1
+ " 1 2 13 15
+ numtries=100
+ i=$numtries
+ while test $i -gt 0; do
+ # mkdir is a portable test-and-set.
+ if mkdir "$lockdir" 2>/dev/null; then
+ # This process acquired the lock.
+ "$@" -MD
+ stat=$?
+ # Release the lock.
+ rmdir "$lockdir"
+ break
+ else
+ # If the lock is being held by a different process, wait
+ # until the winning process is done or we timeout.
+ while test -d "$lockdir" && test $i -gt 0; do
+ sleep 1
+ i=`expr $i - 1`
+ done
+ fi
+ i=`expr $i - 1`
+ done
+ trap - 1 2 13 15
+ if test $i -le 0; then
+ echo "$0: failed to acquire lock after $numtries attempts" >&2
+ echo "$0: check lockdir '$lockdir'" >&2
+ exit 1
+ fi
+
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+ | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp2)
+ # The "hp" stanza above does not work with aCC (C++) and HP's ia64
+ # compilers, which have integrated preprocessors. The correct option
+ # to use with these is +Maked; it writes dependencies to a file named
+ # 'foo.d', which lands next to the object file, wherever that
+ # happens to be.
+ # Much of this is similar to the tru64 case; see comments there.
+ set_dir_from "$object"
+ set_base_from "$object"
+ if test "$libtool" = yes; then
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir.libs/$base.d
+ "$@" -Wc,+Maked
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ "$@" +Maked
+ fi
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile1" "$tmpdepfile2"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add 'dependent.h:' lines.
+ sed -ne '2,${
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
+ else
+ make_dummy_depfile
+ fi
+ rm -f "$tmpdepfile" "$tmpdepfile2"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in 'foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ set_dir_from "$object"
+ set_base_from "$object"
+
+ if test "$libtool" = yes; then
+ # Libtool generates 2 separate objects for the 2 libraries. These
+ # two compilations output dependencies in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir$base.o.d # libtool 1.5
+ tmpdepfile2=$dir.libs/$base.o.d # Likewise.
+ tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ # Same post-processing that is required for AIX mode.
+ aix_post_process_depfile
+ ;;
+
+msvc7)
+ if test "$libtool" = yes; then
+ showIncludes=-Wc,-showIncludes
+ else
+ showIncludes=-showIncludes
+ fi
+ "$@" $showIncludes > "$tmpdepfile"
+ stat=$?
+ grep -v '^Note: including file: ' "$tmpdepfile"
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ # The first sed program below extracts the file names and escapes
+ # backslashes for cygpath. The second sed program outputs the file
+ # name when reading, but also accumulates all include files in the
+ # hold buffer in order to output them again at the end. This only
+ # works with sed implementations that can handle large buffers.
+ sed < "$tmpdepfile" -n '
+/^Note: including file: *\(.*\)/ {
+ s//\1/
+ s/\\/\\\\/g
+ p
+}' | $cygpath_u | sort -u | sed -n '
+s/ /\\ /g
+s/\(.*\)/'"$tab"'\1 \\/p
+s/.\(.*\) \\/\1:/
+H
+$ {
+ s/.*/'"$tab"'/
+ G
+ p
+}' >> "$depfile"
+ echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+ rm -f "$tmpdepfile"
+ ;;
+
+msvc7msys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove '-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for ':'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
+ "$@" $dashmflag |
+ sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this sed invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no eat=no
+ for arg
+ do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ if test $eat = yes; then
+ eat=no
+ continue
+ fi
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -arch)
+ eat=yes ;;
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix=`echo "$object" | sed 's/^.*\././'`
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ # makedepend may prepend the VPATH from the source file name to the object.
+ # No need to regex-escape $object, excess matching of '.' is harmless.
+ sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process the last invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed '1,2d' "$tmpdepfile" \
+ | tr ' ' "$nl" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove '-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E \
+ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ | sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test "X$1" != 'X--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E 2>/dev/null |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
+ echo "$tab" >> "$depfile"
+ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvcmsys)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/distro/Makefile.am b/distro/Makefile.am
new file mode 100644
index 0000000..7e55ad7
--- /dev/null
+++ b/distro/Makefile.am
@@ -0,0 +1,5 @@
+EXTRA_DIST = \
+ common \
+ config \
+ pkg \
+ tests
diff --git a/distro/Makefile.in b/distro/Makefile.in
new file mode 100644
index 0000000..12139fe
--- /dev/null
+++ b/distro/Makefile.in
@@ -0,0 +1,531 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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 = distro
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = \
+ common \
+ config \
+ pkg \
+ tests
+
+all: all-am
+
+.SUFFIXES:
+$(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 distro/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign distro/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
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+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:
+
+clean-generic:
+
+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."
+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:
+
+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: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am 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 \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# 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/distro/common/cz.nic.knotd.conf b/distro/common/cz.nic.knotd.conf
new file mode 100644
index 0000000..50af87a
--- /dev/null
+++ b/distro/common/cz.nic.knotd.conf
@@ -0,0 +1,9 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="knot">
+ <allow own="cz.nic.knotd" />
+ </policy>
+ <policy context="default">
+ <allow receive_sender="cz.nic.knotd" />
+ </policy>
+</busconfig>
diff --git a/distro/common/knot.service b/distro/common/knot.service
new file mode 100644
index 0000000..e6c13ed
--- /dev/null
+++ b/distro/common/knot.service
@@ -0,0 +1,30 @@
+[Unit]
+Description=Knot DNS server
+Wants=network-online.target
+After=network-online.target
+Documentation=man:knotd(8) man:knot.conf(5) man:knotc(8)
+
+[Service]
+Type=notify
+User=knot
+Group=knot
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETPCAP
+AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_SETPCAP
+ExecStartPre=/usr/sbin/knotc conf-check
+ExecStart=/usr/sbin/knotd -m "$KNOT_CONF_MAX_SIZE"
+ExecReload=/bin/kill -HUP $MAINPID
+Restart=on-abort
+LimitNOFILE=1048576
+TimeoutStopSec=300
+# Extend the systemd startup timeout by this value (seconds) for each zone
+Environment="KNOT_ZONE_LOAD_TIMEOUT_SEC=180"
+# Maximum size (MiB) of a configuration database
+Environment="KNOT_CONF_MAX_SIZE=512"
+
+# Expected systemd >= v239
+RuntimeDirectory=knot
+StateDirectory=knot
+NoNewPrivileges=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/distro/config/apkg.toml b/distro/config/apkg.toml
new file mode 100644
index 0000000..0858753
--- /dev/null
+++ b/distro/config/apkg.toml
@@ -0,0 +1,26 @@
+[project]
+name = "knot-dns"
+# needed for make-archive
+make_archive_script = "scripts/make-dev-archive.sh"
+
+[upstream]
+# needed for get-archive
+archive_url = "https://secure.nic.cz/files/knot-dns/knot-{{ version }}.tar.xz"
+signature_url = "https://secure.nic.cz/files/knot-dns/knot-{{ version }}.tar.xz.asc"
+
+[apkg]
+compat = 3
+
+[[distro.aliases]]
+name = "el-7"
+distro = ["centos == 7", "rhel == 7"]
+
+# pkstyle aliases bellow are expected to be built-in in apkg >= 0.5.0
+# https://gitlab.nic.cz/packaging/apkg/-/issues/79
+[[distro.aliases]]
+name = "deb"
+distro = ['ubuntu', 'debian']
+
+[[distro.aliases]]
+name = "rpm"
+distro = ['almalinux', 'centos', 'fedora', 'opensuse', 'oracle', 'rhel', 'rocky', 'scientific']
diff --git a/distro/pkg/arch/PKGBUILD b/distro/pkg/arch/PKGBUILD
new file mode 100644
index 0000000..16f1259
--- /dev/null
+++ b/distro/pkg/arch/PKGBUILD
@@ -0,0 +1,66 @@
+# Maintainer: Tomas Krizek <tomas.krizek@nic.cz>
+# Maintainer: Bruno Pagani <archange@archlinux.org>
+# Contributor: Ondřej Surý <ondrej@sury.org>
+# Contributor: Julian Brost <julian@0x4a42.net>
+# Contributor: Oleander Reis <oleander@oleander.cc>
+# Contributor: Otto Sabart <seberm[at]gmail[dot]com>
+
+pkgname=knot
+pkgver={{ version }}
+pkgrel=1
+pkgdesc="High-performance authoritative-only DNS server"
+arch=('x86_64')
+url="https://www.knot-dns.cz/"
+license=('GPL3')
+depends=('fstrm'
+ 'gnutls'
+ 'libcap-ng'
+ 'libedit'
+ 'libidn2'
+ 'libmaxminddb'
+ 'liburcu'
+ 'lmdb'
+ 'protobuf-c'
+ 'systemd')
+backup=('etc/knot/knot.conf')
+source=("${pkgname}-${pkgver}.tar.xz")
+sha256sums=('SKIP')
+validpgpkeys=('742FA4E95829B6C5EAC6B85710BB7AF6FEBBD6AB') # Daniel Salzman <daniel.salzman@nic.cz>
+
+build() {
+ cd ${pkgname}-${pkgver}
+
+ ./configure \
+ --prefix=/usr \
+ --sbindir=/usr/bin \
+ --sysconfdir=/etc \
+ --localstatedir=/var/lib \
+ --libexecdir=/usr/lib/knot \
+ --with-rundir=/run/knot \
+ --with-storage=/var/lib/knot \
+ --enable-recvmmsg \
+ --enable-dnstap \
+ --enable-systemd \
+ --enable-reuseport \
+ --disable-silent-rules \
+ --disable-static
+
+ make
+}
+
+check() {
+ cd ${pkgname}-${pkgver}
+ make check
+}
+
+package() {
+ cd ${pkgname}-${pkgver}
+
+ make DESTDIR="${pkgdir}" install
+
+ rm "${pkgdir}"/etc/knot/example.com.zone
+ mv "${pkgdir}"/etc/knot/{knot.sample.conf,knot.conf}
+
+ install -Dm644 distro/common/${pkgname}.service -t "${pkgdir}"/usr/lib/systemd/system/
+ install -Dm644 distro/pkg/arch/${pkgname}.sysusers "${pkgdir}"/usr/lib/sysusers.d/${pkgname}.conf
+}
diff --git a/distro/pkg/arch/knot.sysusers b/distro/pkg/arch/knot.sysusers
new file mode 100644
index 0000000..735db76
--- /dev/null
+++ b/distro/pkg/arch/knot.sysusers
@@ -0,0 +1 @@
+u knot - "Knot DNS Daemon User"
diff --git a/distro/pkg/arch/knot.tmpfiles.arch b/distro/pkg/arch/knot.tmpfiles.arch
new file mode 100644
index 0000000..b20df6a
--- /dev/null
+++ b/distro/pkg/arch/knot.tmpfiles.arch
@@ -0,0 +1,2 @@
+d /run/knot 0755 knot knot - -
+d /var/lib/knot 0700 knot knot - -
diff --git a/distro/pkg/deb/changelog b/distro/pkg/deb/changelog
new file mode 100644
index 0000000..123f92b
--- /dev/null
+++ b/distro/pkg/deb/changelog
@@ -0,0 +1,6 @@
+knot ({{ version }}-cznic.{{ release }}) unstable; urgency=medium
+
+ * upstream package
+ * see https://www.knot-dns.cz
+
+ -- Knot DNS <knot-dns@labs.nic.cz> {{ now }}
diff --git a/distro/pkg/deb/clean b/distro/pkg/deb/clean
new file mode 100644
index 0000000..b2a9f3f
--- /dev/null
+++ b/distro/pkg/deb/clean
@@ -0,0 +1,2 @@
+doc/modules
+.pybuild/
diff --git a/distro/pkg/deb/compat b/distro/pkg/deb/compat
new file mode 100644
index 0000000..b4de394
--- /dev/null
+++ b/distro/pkg/deb/compat
@@ -0,0 +1 @@
+11
diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control
new file mode 100644
index 0000000..9cc4f54
--- /dev/null
+++ b/distro/pkg/deb/control
@@ -0,0 +1,267 @@
+Source: knot
+Section: net
+Priority: optional
+Maintainer: Knot DNS <knot-dns@labs.nic.cz>
+Uploaders:
+ Jakub Ružička <jakub.ruzicka@nic.cz>,
+ Daniel Salzman <daniel.salzman@nic.cz>,
+Build-Depends-Indep:
+ python3-setuptools,
+ python3-sphinx,
+Build-Depends:
+ autoconf,
+ automake,
+ debhelper (>= 11),
+ dh-python,
+ libcap-ng-dev,
+ libedit-dev,
+ libelf-dev,
+ libfstrm-dev,
+ libgnutls28-dev,
+ libidn2-dev,
+ liblmdb-dev,
+ libmaxminddb-dev,
+ libmnl-dev,
+ libnghttp2-dev,
+ libprotobuf-c-dev,
+ libsofthsm2 <!nocheck>,
+ libsystemd-dev [linux-any] | libsystemd-daemon-dev [linux-any],
+ libsystemd-dev [linux-any] | libsystemd-journal-dev [linux-any],
+ libtool,
+ liburcu-dev,
+ pkg-config,
+ protobuf-c-compiler,
+ python3-all,
+Standards-Version: 4.5.0
+Homepage: https://www.knot-dns.cz/
+Vcs-Browser: https://gitlab.nic.cz/knot/knot-dns
+Vcs-Git: https://gitlab.nic.cz/knot/knot-dns.git
+Rules-Requires-Root: no
+
+Package: knot
+Architecture: any
+Depends:
+ adduser,
+ libdnssec9 (= ${binary:Version}),
+ libknot13 (= ${binary:Version}),
+ libzscanner4 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Pre-Depends:
+ ${misc:Pre-Depends},
+Suggests:
+ systemd,
+Description: Authoritative domain name server
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+
+Package: libknot13
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+Section: libs
+Description: DNS shared library from Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides a DNS shared library used by Knot DNS and
+ Knot Resolver.
+
+Package: libzscanner4
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+Section: libs
+Description: DNS zone-parsing shared library from Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides a fast zone parser shared library used by Knot
+ DNS and Knot Resolver.
+
+Package: libdnssec9
+Architecture: any
+Depends:
+ ${misc:Depends},
+ ${shlibs:Depends},
+Section: libs
+Description: DNSSEC shared library from Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides common DNSSEC shared library used by Knot DNS
+ and Knot Resolver.
+
+Package: libknot-dev
+Architecture: any
+Depends:
+ libdnssec9 (= ${binary:Version}),
+ libgnutls28-dev,
+ libknot13 (= ${binary:Version}),
+ libzscanner4 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Section: libdevel
+Description: Knot DNS shared library development files
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides development files for shared libraries from Knot DNS.
+
+Package: knot-dnsutils
+Architecture: any
+Depends:
+ libdnssec9 (= ${binary:Version}),
+ libknot13 (= ${binary:Version}),
+ libzscanner4 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: DNS clients provided with Knot DNS (kdig, knsupdate)
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package delivers various DNS client programs from Knot DNS.
+ .
+ - kdig - query a DNS server in various ways
+ - knsupdate - perform dynamic updates (See RFC2136)
+ - kxdpgun - send a DNS query stream over UDP to a DNS server
+ .
+ Those clients were designed to be almost 1:1 compatible with BIND dnsutils,
+ but they provide some enhancements, which are documented.
+ .
+ WARNING: knslookup is not provided as it is considered obsolete.
+
+Package: knot-dnssecutils
+Architecture: any
+Depends:
+ libdnssec9 (= ${binary:Version}),
+ libknot13 (= ${binary:Version}),
+ libzscanner4 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: DNSSEC tools provided with Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package delivers various DNSSEC tools from Knot DNS.
+ .
+ - kzonecheck
+ - kzonesign
+ - knsec3hash
+
+Package: knot-host
+Architecture: any
+Depends:
+ libdnssec9 (= ${binary:Version}),
+ libknot13 (= ${binary:Version}),
+ libzscanner4 (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: Version of 'host' bundled with Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides the 'host' program from Knot DNS. This program is
+ designed to be almost 1:1 compatible with BIND 9.x 'host' program.
+
+Package: knot-module-dnstap
+Architecture: any
+Depends:
+ knot (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: dnstap module for Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package contains dnstap module for logging DNS traffic.
+
+Package: knot-module-geoip
+Architecture: any
+Depends:
+ knot (= ${binary:Version}),
+ ${misc:Depends},
+ ${shlibs:Depends},
+Description: geoip module for Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package contains geoip module for geography-based responses.
+
+Package: knot-doc
+Architecture: all
+Multi-Arch: foreign
+Depends:
+ libjs-jquery,
+ libjs-sphinxdoc,
+ libjs-underscore,
+ ${misc:Depends},
+Section: doc
+Description: Documentation for Knot DNS
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides various documents that are useful for
+ maintaining a working Knot DNS installation.
+
+Package: python3-libknot
+Architecture: all
+Depends:
+ ${misc:Depends},
+ ${python3:Depends},
+Section: python
+Description: Python bindings for libknot
+ Knot DNS is a fast, authoritative only, high performance, feature
+ full and open source name server.
+ .
+ Knot DNS is developed by CZ.NIC Labs, the R&D department of .CZ
+ registry and hence is well suited to run anything from the root
+ zone, the top-level domain, to many smaller standard domain names.
+ .
+ This package provides Python bindings for the libknot shared library.
diff --git a/distro/pkg/deb/copyright b/distro/pkg/deb/copyright
new file mode 100644
index 0000000..3565d25
--- /dev/null
+++ b/distro/pkg/deb/copyright
@@ -0,0 +1,208 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Knot DNS
+Upstream-Contact: knot-dns@labs.nic.cz
+Source: https://secure.nic.cz/files/knot-dns/
+
+Files: *
+Copyright: 2011-2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+License: GPL-3+
+
+Files: m4/*
+Copyright: 2011-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 1996-2001, 2003-2015 Free Software Foundation, Inc.
+License: GPL-3+
+
+Files: install-sh
+Copyright: 1994 X Consortium
+License: MIT
+
+Files: debian/* distro/pkg/deb/*
+Copyright: 2011-2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 2011 Ondřej Surý <ondrej@debian.org>
+License: GPL-3+
+
+Files: tests/tap/*
+Copyright: 2000-2001, 2004, 2006-2012 Russ Allbery <rra@stanford.edu>
+ 2006, 2007, 2008, 2013 The Board of Trustees of the Leland Stanford Junior University
+License: MIT
+
+Files: tests/tap/files.*
+Copyright: 2011-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+License: GPL-3+
+
+Files: src/contrib/dnstap/*
+Copyright: 2014, Farsight Security, Inc. <software@farsightsecurity.com>
+ 2011-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+License: GPL-3+
+
+Files: src/contrib/libbpf/*
+Copyright: 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ 2015 Wang Nan <wangnan0@huawei.com>
+ 2015 Huawei Inc.
+ 2017 Nicira, Inc.
+ 2019 Isovalent, Inc.
+ 2019 Netronome Systems, Inc.
+ 2003-2013 Thomas Graf <tgraf@suug.ch>
+ 2018-2019 Intel Corporation.
+ 2018-2019 Facebook
+License: LGPL-2.1
+
+Files: src/contrib/libngtcp2/*
+Copyright: 2016-2023 ngtcp2 contributors
+ 2012-2017 nghttp2 contributors
+License: MIT
+
+Files: src/contrib/musl/*
+Copyright: 2005-2020 Rich Felker, et al.
+License: MIT
+
+Files: src/contrib/openbsd/siphash.*
+Copyright: 2013 Andre Oppermann <andre@FreeBSD.org>
+License: BSD-3-Clause
+
+Files: src/contrib/openbsd/strl*
+Copyright: 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+License: 0BSD
+
+Files: src/contrib/proxyv2/*
+Copyright: 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 2021 Fastly, Inc.
+License: GPL-3+
+
+Files: src/contrib/qp-trie/*
+Copyright: 2011-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 2018 Tony Finch <dot@dotat.at>
+License: GPL-3+
+
+Files: src/contrib/ucw/*
+Copyright: 2011-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 1997-2017 Martin Mares <mj@ucw.cz>
+ 2007 Pavel Charvat <pchar@ucw.cz>
+ 2012 Ondrej Filip <feela@network.cz>
+License: LGPL-2.0
+
+Files: src/contrib/ucw/heap.h
+Copyright: 2011-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+License: GPL-3+
+
+Files: src/contrib/url-parser/*
+Copyright: 2020 Igor Sysoev
+ 2020 Nginx, Inc.
+ 2020 Joyent, Inc.
+License: MIT
+
+Files: src/contrib/vpool/*
+Copyright: 2006, 2008 Alexey Vatchenko <av@bsdua.org>
+License: 0BSD
+
+Files: tests-fuzz/main.c
+Copyright: 2011-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ 2017 Tim Ruehsen
+License: MIT
+
+License: GPL-3+
+ 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/>.
+ .
+ On Debian systems, the full text of the GNU General Public License
+ version 3 can be found in the file `/usr/share/common-licenses/GPL-3'.
+
+License: LGPL-2.0
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ .
+ This library 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
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+License: LGPL-2.1
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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
+ Lesser General Public License for more details.
+ .
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program; If not, see <http://www.gnu.org/licenses/>.
+ .
+ On Debian systems, the complete text of the GNU Lesser General Public
+ License version 2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'.
+
+License: 0BSD
+ Permission to use, copy, modify, and distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+License: BSD-3-Clause
+ Redistribution and use in source and binary forms, with or without modification,
+ are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+ .
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
diff --git a/distro/pkg/deb/cz.nic.knotd.conf b/distro/pkg/deb/cz.nic.knotd.conf
new file mode 100644
index 0000000..50af87a
--- /dev/null
+++ b/distro/pkg/deb/cz.nic.knotd.conf
@@ -0,0 +1,9 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="knot">
+ <allow own="cz.nic.knotd" />
+ </policy>
+ <policy context="default">
+ <allow receive_sender="cz.nic.knotd" />
+ </policy>
+</busconfig>
diff --git a/distro/pkg/deb/docs b/distro/pkg/deb/docs
new file mode 100644
index 0000000..b43bf86
--- /dev/null
+++ b/distro/pkg/deb/docs
@@ -0,0 +1 @@
+README.md
diff --git a/distro/pkg/deb/knot-dnssecutils.install b/distro/pkg/deb/knot-dnssecutils.install
new file mode 100644
index 0000000..20009e8
--- /dev/null
+++ b/distro/pkg/deb/knot-dnssecutils.install
@@ -0,0 +1,3 @@
+usr/bin/knsec3hash
+usr/bin/kzonecheck
+usr/bin/kzonesign
diff --git a/distro/pkg/deb/knot-dnssecutils.manpages b/distro/pkg/deb/knot-dnssecutils.manpages
new file mode 100644
index 0000000..913c4cb
--- /dev/null
+++ b/distro/pkg/deb/knot-dnssecutils.manpages
@@ -0,0 +1,3 @@
+usr/share/man/man1/knsec3hash.1
+usr/share/man/man1/kzonecheck.1
+usr/share/man/man1/kzonesign.1
diff --git a/distro/pkg/deb/knot-dnsutils.install b/distro/pkg/deb/knot-dnsutils.install
new file mode 100644
index 0000000..e2f2a8a
--- /dev/null
+++ b/distro/pkg/deb/knot-dnsutils.install
@@ -0,0 +1,3 @@
+usr/bin/kdig
+usr/bin/knsupdate
+usr/sbin/kxdpgun
diff --git a/distro/pkg/deb/knot-dnsutils.manpages b/distro/pkg/deb/knot-dnsutils.manpages
new file mode 100644
index 0000000..67254d9
--- /dev/null
+++ b/distro/pkg/deb/knot-dnsutils.manpages
@@ -0,0 +1,3 @@
+usr/share/man/man1/kdig.1
+usr/share/man/man1/knsupdate.1
+usr/share/man/man8/kxdpgun.8
diff --git a/distro/pkg/deb/knot-doc.install b/distro/pkg/deb/knot-doc.install
new file mode 100644
index 0000000..c2a345d
--- /dev/null
+++ b/distro/pkg/deb/knot-doc.install
@@ -0,0 +1 @@
+usr/share/doc/knot/* /usr/share/doc/knot-doc/
diff --git a/distro/pkg/deb/knot-doc.links b/distro/pkg/deb/knot-doc.links
new file mode 100644
index 0000000..1376b3a
--- /dev/null
+++ b/distro/pkg/deb/knot-doc.links
@@ -0,0 +1,5 @@
+usr/share/javascript/jquery/jquery.min.js usr/share/doc/knot-doc/_static/jquery.js
+usr/share/javascript/sphinxdoc/1.0/doctools.js usr/share/doc/knot-doc/_static/doctools.js
+usr/share/javascript/sphinxdoc/1.0/language_data.js usr/share/doc/knot-doc/_static/language_data.js
+usr/share/javascript/sphinxdoc/1.0/searchtools.js usr/share/doc/knot-doc/_static/searchtools.js
+usr/share/javascript/underscore/underscore.min.js usr/share/doc/knot-doc/_static/underscore.js
diff --git a/distro/pkg/deb/knot-host.install b/distro/pkg/deb/knot-host.install
new file mode 100644
index 0000000..51bacf0
--- /dev/null
+++ b/distro/pkg/deb/knot-host.install
@@ -0,0 +1 @@
+usr/bin/khost
diff --git a/distro/pkg/deb/knot-host.manpages b/distro/pkg/deb/knot-host.manpages
new file mode 100644
index 0000000..4891e2c
--- /dev/null
+++ b/distro/pkg/deb/knot-host.manpages
@@ -0,0 +1 @@
+usr/share/man/man1/khost.1
diff --git a/distro/pkg/deb/knot-module-dnstap.install b/distro/pkg/deb/knot-module-dnstap.install
new file mode 100644
index 0000000..983455e
--- /dev/null
+++ b/distro/pkg/deb/knot-module-dnstap.install
@@ -0,0 +1 @@
+usr/lib/*/knot/modules-*/dnstap.so
diff --git a/distro/pkg/deb/knot-module-geoip.install b/distro/pkg/deb/knot-module-geoip.install
new file mode 100644
index 0000000..16d87c3
--- /dev/null
+++ b/distro/pkg/deb/knot-module-geoip.install
@@ -0,0 +1 @@
+usr/lib/*/knot/modules-*/geoip.so
diff --git a/distro/pkg/deb/knot.dirs b/distro/pkg/deb/knot.dirs
new file mode 100644
index 0000000..6e937aa
--- /dev/null
+++ b/distro/pkg/deb/knot.dirs
@@ -0,0 +1 @@
+var/lib/knot
diff --git a/distro/pkg/deb/knot.init b/distro/pkg/deb/knot.init
new file mode 100644
index 0000000..3f8fcae
--- /dev/null
+++ b/distro/pkg/deb/knot.init
@@ -0,0 +1,149 @@
+#!/bin/sh
+### BEGIN INIT INFO
+# Provides: knot
+# Required-Start: $network $local_fs $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: authoritative domain name server
+# Description: Knot DNS is a authoritative-only domain name server
+### END INIT INFO
+
+# Author: Ondřej Surý <ondrej@debian.org>
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="Knot DNS server" # Introduce a short description here
+NAME=knotd # Introduce the short server's name here
+DAEMON=/usr/sbin/$NAME # Introduce the server's location here
+PIDFILE=/run/knot/knot.pid
+SCRIPTNAME=/etc/init.d/knot
+KNOTC=/usr/sbin/knotc
+RUNDIR=/run/knot
+
+# Exit if the package is not installed
+[ -x $DAEMON ] || exit 0
+
+KNOTD_ARGS=""
+
+# Read configuration variable file if it is present
+[ -r /etc/default/knot ] && . /etc/default/knot
+
+DAEMON_ARGS="-d $KNOTD_ARGS"
+
+# Define LSB log_* functions.
+# Depend on sysvinit-utils (>= 2.96) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+
+ $KNOTC status >/dev/null 2>/dev/null \
+ && return 1
+
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+
+ $KNOTC status >/dev/null 2>/dev/null \
+ || return 1
+
+ $KNOTC stop >/dev/null
+ RETVAL="$?"
+ [ $? = 1 ] && return 2
+
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return 0
+}
+
+do_reload() {
+ $KNOTC reload >/dev/null
+ return $?
+}
+
+do_mkrundir() {
+ mkdir -p $RUNDIR
+ chmod 0755 $RUNDIR
+ chown knot:knot $RUNDIR
+}
+
+case "$1" in
+ start)
+ do_mkrundir
+ log_daemon_msg "Starting $DESC " "$NAME"
+ do_start
+ case "$?" in
+ 0|1) log_end_msg 0 ;;
+ 2) log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) log_end_msg 0 ;;
+ 2) log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ STATUS=$($KNOTC status 2>&1 >/dev/null)
+ RETVAL=$?
+ if [ $RETVAL = 0 ]; then
+ log_success_msg "$NAME is running"
+ else
+ log_failure_msg "$NAME is not running ($STATUS)"
+ fi
+ exit $RETVAL
+ ;;
+ reload|force-reload)
+ log_daemon_msg "Reloading $DESC" "$NAME"
+ do_reload
+ log_end_msg $?
+ ;;
+ restart)
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
diff --git a/distro/pkg/deb/knot.install b/distro/pkg/deb/knot.install
new file mode 100644
index 0000000..94c3999
--- /dev/null
+++ b/distro/pkg/deb/knot.install
@@ -0,0 +1,8 @@
+debian/cz.nic.knotd.conf etc/dbus-1/system.d/
+debian/ufw/knot etc/ufw/applications.d/
+etc/knot/knot.conf
+usr/sbin/kcatalogprint
+usr/sbin/keymgr
+usr/sbin/kjournalprint
+usr/sbin/knotc
+usr/sbin/knotd
diff --git a/distro/pkg/deb/knot.manpages b/distro/pkg/deb/knot.manpages
new file mode 100644
index 0000000..5d23e9f
--- /dev/null
+++ b/distro/pkg/deb/knot.manpages
@@ -0,0 +1,6 @@
+usr/share/man/man5/knot.conf.5
+usr/share/man/man8/kcatalogprint.8
+usr/share/man/man8/keymgr.8
+usr/share/man/man8/kjournalprint.8
+usr/share/man/man8/knotc.8
+usr/share/man/man8/knotd.8
diff --git a/distro/pkg/deb/knot.postinst b/distro/pkg/deb/knot.postinst
new file mode 100644
index 0000000..da747c8
--- /dev/null
+++ b/distro/pkg/deb/knot.postinst
@@ -0,0 +1,16 @@
+#!/bin/sh
+set -e
+
+if [ "$1" = "configure" ]; then
+ if ! getent passwd knot > /dev/null; then
+ adduser --quiet --system --group --no-create-home --home /var/lib/knot knot
+ fi
+
+ dpkg-statoverride --list /var/lib/knot >/dev/null 2>&1 || dpkg-statoverride --update --add root knot 0770 /var/lib/knot
+ dpkg-statoverride --list /etc/knot/knot.conf >/dev/null 2>&1 || dpkg-statoverride --update --add root knot 0640 /etc/knot/knot.conf
+ dpkg-statoverride --list /etc/knot >/dev/null 2>&1 || dpkg-statoverride --update --add root knot 0750 /etc/knot
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distro/pkg/deb/knot.postrm b/distro/pkg/deb/knot.postrm
new file mode 100644
index 0000000..14b3d69
--- /dev/null
+++ b/distro/pkg/deb/knot.postrm
@@ -0,0 +1,21 @@
+#!/bin/sh
+set -e
+
+if test "$1" = "purge"; then
+ state_dir=/var/lib/knot
+ for db_name in "catalog" "confdb" "journal" "keys" "timers"; do
+ rm -rf $state_dir/$db_name >/dev/null 2>&1 || true
+ done
+ rmdir $state_dir >/dev/null 2>&1 || true
+ [ -e $state_dir/* ] && echo "Notice: there are still data in ${state_dir}, please check."
+
+ dpkg-statoverride --remove /var/lib/knot >/dev/null 2>&1 || true
+ dpkg-statoverride --remove /etc/knot/knot.conf >/dev/null 2>&1 || true
+ dpkg-statoverride --remove /etc/knot >/dev/null 2>&1 || true
+
+ deluser --quiet knot >/dev/null 2>&1 || true
+fi
+
+#DEBHELPER#
+
+exit 0
diff --git a/distro/pkg/deb/knot.service b/distro/pkg/deb/knot.service
new file mode 100644
index 0000000..e6c13ed
--- /dev/null
+++ b/distro/pkg/deb/knot.service
@@ -0,0 +1,30 @@
+[Unit]
+Description=Knot DNS server
+Wants=network-online.target
+After=network-online.target
+Documentation=man:knotd(8) man:knot.conf(5) man:knotc(8)
+
+[Service]
+Type=notify
+User=knot
+Group=knot
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETPCAP
+AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_SETPCAP
+ExecStartPre=/usr/sbin/knotc conf-check
+ExecStart=/usr/sbin/knotd -m "$KNOT_CONF_MAX_SIZE"
+ExecReload=/bin/kill -HUP $MAINPID
+Restart=on-abort
+LimitNOFILE=1048576
+TimeoutStopSec=300
+# Extend the systemd startup timeout by this value (seconds) for each zone
+Environment="KNOT_ZONE_LOAD_TIMEOUT_SEC=180"
+# Maximum size (MiB) of a configuration database
+Environment="KNOT_CONF_MAX_SIZE=512"
+
+# Expected systemd >= v239
+RuntimeDirectory=knot
+StateDirectory=knot
+NoNewPrivileges=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/distro/pkg/deb/libdnssec9.install b/distro/pkg/deb/libdnssec9.install
new file mode 100644
index 0000000..17a9fe6
--- /dev/null
+++ b/distro/pkg/deb/libdnssec9.install
@@ -0,0 +1 @@
+usr/lib/*/libdnssec.so.*
diff --git a/distro/pkg/deb/libdnssec9.symbols b/distro/pkg/deb/libdnssec9.symbols
new file mode 100644
index 0000000..c3ab2ed
--- /dev/null
+++ b/distro/pkg/deb/libdnssec9.symbols
@@ -0,0 +1,96 @@
+libdnssec.so.9 libdnssec9 #MINVER#
+* Build-Depends-Package: libknot-dev
+ dnssec_algorithm_digest_support@Base 3.2.0
+ dnssec_algorithm_key_size_check@Base 3.2.0
+ dnssec_algorithm_key_size_default@Base 3.2.0
+ dnssec_algorithm_key_size_range@Base 3.2.0
+ dnssec_algorithm_key_support@Base 3.2.0
+ dnssec_algorithm_reproducible@Base 3.2.0
+ dnssec_binary_alloc@Base 3.2.0
+ dnssec_binary_cmp@Base 3.2.0
+ dnssec_binary_dup@Base 3.2.0
+ dnssec_binary_free@Base 3.2.0
+ dnssec_binary_from_base64@Base 3.2.0
+ dnssec_binary_resize@Base 3.2.0
+ dnssec_binary_to_base64@Base 3.2.0
+ dnssec_crypto_cleanup@Base 3.2.0
+ dnssec_crypto_init@Base 3.2.0
+ dnssec_crypto_reinit@Base 3.2.0
+ dnssec_digest@Base 3.2.0
+ dnssec_digest_finish@Base 3.2.0
+ dnssec_digest_init@Base 3.2.0
+ dnssec_key_can_sign@Base 3.2.0
+ dnssec_key_can_verify@Base 3.2.0
+ dnssec_key_clear@Base 3.2.0
+ dnssec_key_create_ds@Base 3.2.0
+ dnssec_key_dup@Base 3.2.0
+ dnssec_key_free@Base 3.2.0
+ dnssec_key_get_algorithm@Base 3.2.0
+ dnssec_key_get_dname@Base 3.2.0
+ dnssec_key_get_flags@Base 3.2.0
+ dnssec_key_get_keyid@Base 3.2.0
+ dnssec_key_get_keytag@Base 3.2.0
+ dnssec_key_get_protocol@Base 3.2.0
+ dnssec_key_get_pubkey@Base 3.2.0
+ dnssec_key_get_rdata@Base 3.2.0
+ dnssec_key_get_size@Base 3.2.0
+ dnssec_key_load_pkcs8@Base 3.2.0
+ dnssec_key_new@Base 3.2.0
+ dnssec_key_set_algorithm@Base 3.2.0
+ dnssec_key_set_dname@Base 3.2.0
+ dnssec_key_set_flags@Base 3.2.0
+ dnssec_key_set_protocol@Base 3.2.0
+ dnssec_key_set_pubkey@Base 3.2.0
+ dnssec_key_set_rdata@Base 3.2.0
+ dnssec_keyid_copy@Base 3.2.0
+ dnssec_keyid_equal@Base 3.2.0
+ dnssec_keyid_is_valid@Base 3.2.0
+ dnssec_keyid_normalize@Base 3.2.0
+ dnssec_keystore_close@Base 3.2.0
+ dnssec_keystore_deinit@Base 3.2.0
+ dnssec_keystore_generate@Base 3.2.0
+ dnssec_keystore_get_private@Base 3.2.0
+ dnssec_keystore_import@Base 3.2.0
+ dnssec_keystore_init@Base 3.2.0
+ dnssec_keystore_init_pkcs11@Base 3.2.0
+ dnssec_keystore_init_pkcs8@Base 3.2.0
+ dnssec_keystore_open@Base 3.2.0
+ dnssec_keystore_remove@Base 3.2.0
+ dnssec_keystore_set_private@Base 3.2.0
+ dnssec_keytag@Base 3.2.0
+ dnssec_nsec3_hash@Base 3.2.0
+ dnssec_nsec3_hash_length@Base 3.2.0
+ dnssec_nsec3_params_free@Base 3.2.0
+ dnssec_nsec3_params_from_rdata@Base 3.2.0
+ dnssec_nsec3_params_match@Base 3.2.0
+ dnssec_nsec_bitmap_add@Base 3.2.0
+ dnssec_nsec_bitmap_clear@Base 3.2.0
+ dnssec_nsec_bitmap_contains@Base 3.2.0
+ dnssec_nsec_bitmap_free@Base 3.2.0
+ dnssec_nsec_bitmap_new@Base 3.2.0
+ dnssec_nsec_bitmap_size@Base 3.2.0
+ dnssec_nsec_bitmap_write@Base 3.2.0
+ dnssec_pem_from_privkey@Base 3.2.0
+ dnssec_pem_from_x509@Base 3.2.0
+ dnssec_pem_to_privkey@Base 3.2.0
+ dnssec_pem_to_x509@Base 3.2.0
+ dnssec_random_binary@Base 3.2.0
+ dnssec_random_buffer@Base 3.2.0
+ dnssec_sign_add@Base 3.2.0
+ dnssec_sign_free@Base 3.2.0
+ dnssec_sign_init@Base 3.2.0
+ dnssec_sign_new@Base 3.2.0
+ dnssec_sign_verify@Base 3.2.0
+ dnssec_sign_write@Base 3.2.0
+ dnssec_strerror@Base 3.2.0
+ dnssec_tsig_add@Base 3.2.0
+ dnssec_tsig_algorithm_from_dname@Base 3.2.0
+ dnssec_tsig_algorithm_from_name@Base 3.2.0
+ dnssec_tsig_algorithm_size@Base 3.2.0
+ dnssec_tsig_algorithm_to_dname@Base 3.2.0
+ dnssec_tsig_algorithm_to_name@Base 3.2.0
+ dnssec_tsig_free@Base 3.2.0
+ dnssec_tsig_new@Base 3.2.0
+ dnssec_tsig_optimal_key_size@Base 3.2.0
+ dnssec_tsig_size@Base 3.2.0
+ dnssec_tsig_write@Base 3.2.0
diff --git a/distro/pkg/deb/libknot-dev.install b/distro/pkg/deb/libknot-dev.install
new file mode 100644
index 0000000..cb60d88
--- /dev/null
+++ b/distro/pkg/deb/libknot-dev.install
@@ -0,0 +1,3 @@
+usr/include/
+usr/lib/*/*.so
+usr/lib/*/pkgconfig/*
diff --git a/distro/pkg/deb/libknot13.install b/distro/pkg/deb/libknot13.install
new file mode 100644
index 0000000..f9b9f93
--- /dev/null
+++ b/distro/pkg/deb/libknot13.install
@@ -0,0 +1 @@
+usr/lib/*/libknot.so.*
diff --git a/distro/pkg/deb/libknot13.symbols b/distro/pkg/deb/libknot13.symbols
new file mode 100644
index 0000000..d4b2b45
--- /dev/null
+++ b/distro/pkg/deb/libknot13.symbols
@@ -0,0 +1,265 @@
+libknot.so.13 libknot13 #MINVER#
+* Build-Depends-Package: libknot-dev
+ KNOT_DB_LMDB_DUPSORT@Base 3.2.0
+ KNOT_DB_LMDB_INTEGERKEY@Base 3.2.0
+ KNOT_DB_LMDB_MAPASYNC@Base 3.2.0
+ KNOT_DB_LMDB_NOSYNC@Base 3.2.0
+ KNOT_DB_LMDB_NOTLS@Base 3.2.0
+ KNOT_DB_LMDB_RDONLY@Base 3.2.0
+ KNOT_DB_LMDB_WRITEMAP@Base 3.2.0
+ KNOT_DUMP_STYLE_DEFAULT@Base 3.2.0
+ knot_ctl_accept@Base 3.2.0
+ knot_ctl_alloc@Base 3.2.0
+ knot_ctl_bind@Base 3.2.0
+ knot_ctl_close@Base 3.2.0
+ knot_ctl_connect@Base 3.2.0
+ knot_ctl_free@Base 3.2.0
+ knot_ctl_receive@Base 3.2.0
+ knot_ctl_send@Base 3.2.0
+ knot_ctl_set_timeout@Base 3.2.0
+ knot_ctl_unbind@Base 3.2.0
+ knot_db_lmdb_api@Base 3.2.0
+ knot_db_lmdb_del_exact@Base 3.2.0
+ knot_db_lmdb_get_mapsize@Base 3.2.0
+ knot_db_lmdb_get_path@Base 3.2.0
+ knot_db_lmdb_get_usage@Base 3.2.0
+ knot_db_lmdb_iter_del@Base 3.2.0
+ knot_db_lmdb_txn_begin@Base 3.2.0
+ knot_db_trie_api@Base 3.2.0
+ knot_dname_cmp@Base 3.2.0
+ knot_dname_copy@Base 3.2.0
+ knot_dname_copy_lower@Base 3.2.0
+ knot_dname_free@Base 3.2.0
+ knot_dname_from_str@Base 3.2.0
+ knot_dname_in_bailiwick@Base 3.2.0
+ knot_dname_is_case_equal@Base 3.2.0
+ knot_dname_is_equal@Base 3.2.0
+ knot_dname_labels@Base 3.2.0
+ knot_dname_lf@Base 3.2.0
+ knot_dname_matched_labels@Base 3.2.0
+ knot_dname_prefixlen@Base 3.2.0
+ knot_dname_realsize@Base 3.2.0
+ knot_dname_replace_suffix@Base 3.2.0
+ knot_dname_size@Base 3.2.0
+ knot_dname_store@Base 3.2.0
+ knot_dname_to_lower@Base 3.2.0
+ knot_dname_to_str@Base 3.2.0
+ knot_dname_to_wire@Base 3.2.0
+ knot_dname_unpack@Base 3.2.0
+ knot_dname_wire_check@Base 3.2.0
+ knot_dnssec_alg_names@Base 3.2.0
+ knot_edns_add_option@Base 3.2.0
+ knot_edns_alignment_size@Base 3.2.0
+ knot_edns_chain_parse@Base 3.2.0
+ knot_edns_chain_size@Base 3.2.0
+ knot_edns_chain_write@Base 3.2.0
+ knot_edns_client_subnet_get_addr@Base 3.2.0
+ knot_edns_client_subnet_parse@Base 3.2.0
+ knot_edns_client_subnet_set_addr@Base 3.2.0
+ knot_edns_client_subnet_size@Base 3.2.0
+ knot_edns_client_subnet_write@Base 3.2.0
+ knot_edns_cookie_client_check@Base 3.2.0
+ knot_edns_cookie_client_generate@Base 3.2.0
+ knot_edns_cookie_parse@Base 3.2.0
+ knot_edns_cookie_server_check@Base 3.2.0
+ knot_edns_cookie_server_generate@Base 3.2.0
+ knot_edns_cookie_size@Base 3.2.0
+ knot_edns_cookie_write@Base 3.2.0
+ knot_edns_ede_names@Base 3.2.0
+ knot_edns_get_ext_rcode@Base 3.2.0
+ knot_edns_get_option@Base 3.2.0
+ knot_edns_get_options@Base 3.2.0
+ knot_edns_get_version@Base 3.2.0
+ knot_edns_init@Base 3.2.0
+ knot_edns_keepalive_parse@Base 3.2.0
+ knot_edns_keepalive_size@Base 3.2.0
+ knot_edns_keepalive_write@Base 3.2.0
+ knot_edns_reserve_option@Base 3.2.0
+ knot_edns_set_ext_rcode@Base 3.2.0
+ knot_edns_set_version@Base 3.2.0
+ knot_error_from_libdnssec@Base 3.2.0
+ knot_eth_mtu@Base 3.2.0
+ knot_eth_name_from_addr@Base 3.2.0
+ knot_eth_queues@Base 3.2.0
+ knot_eth_rss@Base 3.2.0
+ knot_eth_vlans@Base 3.2.2
+ knot_eth_xdp_mode@Base 3.2.0
+ knot_get_obsolete_rdata_descriptor@Base 3.2.0
+ knot_get_rdata_descriptor@Base 3.2.0
+ knot_naptr_header_size@Base 3.2.0
+ knot_opcode_names@Base 3.2.0
+ knot_opt_code_to_string@Base 3.2.0
+ knot_pkt_begin@Base 3.2.0
+ knot_pkt_clear@Base 3.2.0
+ knot_pkt_copy@Base 3.2.0
+ knot_pkt_ext_rcode@Base 3.2.0
+ knot_pkt_ext_rcode_name@Base 3.2.0
+ knot_pkt_free@Base 3.2.0
+ knot_pkt_init_response@Base 3.2.0
+ knot_pkt_new@Base 3.2.0
+ knot_pkt_parse@Base 3.2.0
+ knot_pkt_parse_question@Base 3.2.0
+ knot_pkt_put_question@Base 3.2.0
+ knot_pkt_put_rotate@Base 3.2.0
+ knot_pkt_reclaim@Base 3.2.0
+ knot_pkt_reserve@Base 3.2.0
+ knot_probe_alloc@Base 3.2.0
+ knot_probe_consume@Base 3.2.0
+ knot_probe_data_set@Base 3.2.0
+ knot_probe_fd@Base 3.2.0
+ knot_probe_free@Base 3.2.0
+ knot_probe_produce@Base 3.2.0
+ knot_probe_set_consumer@Base 3.2.0
+ knot_probe_set_producer@Base 3.2.0
+ knot_probe_tcp_rtt@Base 3.2.0
+ knot_rcode_names@Base 3.2.0
+ knot_rdataset_add@Base 3.2.0
+ knot_rdataset_at@Base 3.2.0
+ knot_rdataset_clear@Base 3.2.0
+ knot_rdataset_copy@Base 3.2.0
+ knot_rdataset_eq@Base 3.2.0
+ knot_rdataset_intersect@Base 3.2.0
+ knot_rdataset_intersect2@Base 3.2.0
+ knot_rdataset_member@Base 3.2.0
+ knot_rdataset_merge@Base 3.2.0
+ knot_rdataset_subset@Base 3.2.0
+ knot_rdataset_subtract@Base 3.2.0
+ knot_rrclass_from_string@Base 3.2.0
+ knot_rrclass_to_string@Base 3.2.0
+ knot_rrset_add_rdata@Base 3.2.0
+ knot_rrset_clear@Base 3.2.0
+ knot_rrset_copy@Base 3.2.0
+ knot_rrset_equal@Base 3.2.0
+ knot_rrset_free@Base 3.2.0
+ knot_rrset_is_nsec3rel@Base 3.2.0
+ knot_rrset_new@Base 3.2.0
+ knot_rrset_rr_from_wire@Base 3.2.0
+ knot_rrset_rr_to_canonical@Base 3.2.0
+ knot_rrset_size@Base 3.2.0
+ knot_rrset_to_wire_extra@Base 3.2.0
+ knot_rrset_txt_dump@Base 3.2.0
+ knot_rrset_txt_dump_data@Base 3.2.0
+ knot_rrset_txt_dump_header@Base 3.2.0
+ knot_rrtype_additional_needed@Base 3.2.0
+ knot_rrtype_from_string@Base 3.2.0
+ knot_rrtype_is_dnssec@Base 3.2.0
+ knot_rrtype_is_metatype@Base 3.2.0
+ knot_rrtype_should_be_lowercased@Base 3.2.0
+ knot_rrtype_to_string@Base 3.2.0
+ knot_strerror@Base 3.2.0
+ knot_svcb_param_names@Base 3.2.0
+ knot_tcp_cleanup@Base 3.2.0
+ knot_tcp_inbuf_update@Base 3.2.0
+ knot_tcp_outbufs_ack@Base 3.2.0
+ knot_tcp_outbufs_add@Base 3.2.0
+ knot_tcp_outbufs_can_send@Base 3.2.0
+ knot_tcp_outbufs_usage@Base 3.2.0
+ knot_tcp_recv@Base 3.2.0
+ knot_tcp_reply_data@Base 3.2.0
+ knot_tcp_send@Base 3.2.0
+ knot_tcp_sweep@Base 3.2.0
+ knot_tcp_table_free@Base 3.2.0
+ knot_tcp_table_new@Base 3.2.0
+ knot_tsig_add@Base 3.2.0
+ knot_tsig_append@Base 3.2.0
+ knot_tsig_client_check@Base 3.2.0
+ knot_tsig_client_check_next@Base 3.2.0
+ knot_tsig_create_rdata@Base 3.2.0
+ knot_tsig_key_copy@Base 3.2.0
+ knot_tsig_key_deinit@Base 3.2.0
+ knot_tsig_key_init@Base 3.2.0
+ knot_tsig_key_init_file@Base 3.2.0
+ knot_tsig_key_init_str@Base 3.2.0
+ knot_tsig_rcode_names@Base 3.2.0
+ knot_tsig_rdata_alg@Base 3.2.0
+ knot_tsig_rdata_alg_name@Base 3.2.0
+ knot_tsig_rdata_error@Base 3.2.0
+ knot_tsig_rdata_fudge@Base 3.2.0
+ knot_tsig_rdata_is_ok@Base 3.2.0
+ knot_tsig_rdata_mac@Base 3.2.0
+ knot_tsig_rdata_mac_length@Base 3.2.0
+ knot_tsig_rdata_orig_id@Base 3.2.0
+ knot_tsig_rdata_other_data@Base 3.2.0
+ knot_tsig_rdata_other_data_length@Base 3.2.0
+ knot_tsig_rdata_set_fudge@Base 3.2.0
+ knot_tsig_rdata_set_mac@Base 3.2.0
+ knot_tsig_rdata_set_orig_id@Base 3.2.0
+ knot_tsig_rdata_set_other_data@Base 3.2.0
+ knot_tsig_rdata_set_time_signed@Base 3.2.0
+ knot_tsig_rdata_time_signed@Base 3.2.0
+ knot_tsig_rdata_tsig_timers_length@Base 3.2.0
+ knot_tsig_rdata_tsig_variables_length@Base 3.2.0
+ knot_tsig_server_check@Base 3.2.0
+ knot_tsig_sign@Base 3.2.0
+ knot_tsig_sign_next@Base 3.2.0
+ knot_tsig_wire_maxsize@Base 3.2.0
+ knot_tsig_wire_size@Base 3.2.0
+ knot_xdp_deinit@Base 3.2.0
+ knot_xdp_info@Base 3.2.0
+ knot_xdp_init@Base 3.2.0
+ knot_xdp_recv@Base 3.2.0
+ knot_xdp_recv_finish@Base 3.2.0
+ knot_xdp_reply_alloc@Base 3.2.0
+ knot_xdp_send@Base 3.2.0
+ knot_xdp_send_alloc@Base 3.2.0
+ knot_xdp_send_finish@Base 3.2.0
+ knot_xdp_send_free@Base 3.2.0
+ knot_xdp_send_prepare@Base 3.2.0
+ knot_xdp_socket_fd@Base 3.2.0
+ knot_xquic_cleanup@Base 3.2.1
+ knot_xquic_client@Base 3.2.0
+ knot_xquic_conn_get_stream@Base 3.2.0
+ knot_xquic_conn_rtt@Base 3.2.0
+ knot_xquic_free_creds@Base 3.2.0
+ knot_xquic_handle@Base 3.2.0
+ knot_xquic_init_creds@Base 3.2.0
+ knot_xquic_send@Base 3.2.0
+ knot_xquic_session_load@Base 3.2.0
+ knot_xquic_session_save@Base 3.2.0
+ knot_xquic_stream_add_data@Base 3.2.0
+ knot_xquic_stream_get_process@Base 3.2.0
+ knot_xquic_table_free@Base 3.2.0
+ knot_xquic_table_new@Base 3.2.0
+ knot_xquic_table_rem@Base 3.2.0
+ knot_xquic_table_sweep@Base 3.2.0
+ yp_addr@Base 3.2.0
+ yp_addr_noport@Base 3.2.0
+ yp_addr_noport_to_bin@Base 3.2.0
+ yp_addr_noport_to_txt@Base 3.2.0
+ yp_addr_range_to_bin@Base 3.2.0
+ yp_addr_range_to_txt@Base 3.2.0
+ yp_addr_to_bin@Base 3.2.0
+ yp_addr_to_txt@Base 3.2.0
+ yp_base64_to_bin@Base 3.2.0
+ yp_base64_to_txt@Base 3.2.0
+ yp_bool_to_bin@Base 3.2.0
+ yp_bool_to_txt@Base 3.2.0
+ yp_deinit@Base 3.2.0
+ yp_dname_to_bin@Base 3.2.0
+ yp_dname_to_txt@Base 3.2.0
+ yp_format_id@Base 3.2.0
+ yp_format_key0@Base 3.2.0
+ yp_format_key1@Base 3.2.0
+ yp_hex_to_bin@Base 3.2.0
+ yp_hex_to_txt@Base 3.2.0
+ yp_init@Base 3.2.0
+ yp_int_to_bin@Base 3.2.0
+ yp_int_to_txt@Base 3.2.0
+ yp_item_to_bin@Base 3.2.0
+ yp_item_to_txt@Base 3.2.0
+ yp_option_to_bin@Base 3.2.0
+ yp_option_to_txt@Base 3.2.0
+ yp_parse@Base 3.2.0
+ yp_schema_check_deinit@Base 3.2.0
+ yp_schema_check_init@Base 3.2.0
+ yp_schema_check_parser@Base 3.2.0
+ yp_schema_check_str@Base 3.2.0
+ yp_schema_copy@Base 3.2.0
+ yp_schema_find@Base 3.2.0
+ yp_schema_free@Base 3.2.0
+ yp_schema_merge@Base 3.2.0
+ yp_schema_purge_dynamic@Base 3.2.0
+ yp_set_input_file@Base 3.2.0
+ yp_set_input_string@Base 3.2.0
+ yp_str_to_bin@Base 3.2.0
+ yp_str_to_txt@Base 3.2.0
diff --git a/distro/pkg/deb/libzscanner4.install b/distro/pkg/deb/libzscanner4.install
new file mode 100644
index 0000000..a8dc226
--- /dev/null
+++ b/distro/pkg/deb/libzscanner4.install
@@ -0,0 +1 @@
+usr/lib/*/libzscanner.so.*
diff --git a/distro/pkg/deb/libzscanner4.symbols b/distro/pkg/deb/libzscanner4.symbols
new file mode 100644
index 0000000..99ac3b7
--- /dev/null
+++ b/distro/pkg/deb/libzscanner4.symbols
@@ -0,0 +1,12 @@
+libzscanner.so.4 libzscanner4 #MINVER#
+* Build-Depends-Package: libknot-dev
+ zs_deinit@Base 3.1.0
+ zs_errorname@Base 3.1.0
+ zs_init@Base 3.1.0
+ zs_parse_all@Base 3.1.0
+ zs_parse_record@Base 3.1.0
+ zs_set_input_file@Base 3.1.0
+ zs_set_input_string@Base 3.1.0
+ zs_set_processing@Base 3.1.0
+ zs_set_processing_comment@Base 3.1.0
+ zs_strerror@Base 3.1.0
diff --git a/distro/pkg/deb/not-installed b/distro/pkg/deb/not-installed
new file mode 100644
index 0000000..c928be1
--- /dev/null
+++ b/distro/pkg/deb/not-installed
@@ -0,0 +1 @@
+etc/knot/example.com.zone
diff --git a/distro/pkg/deb/prepare-environment b/distro/pkg/deb/prepare-environment
new file mode 100755
index 0000000..7176f5e
--- /dev/null
+++ b/distro/pkg/deb/prepare-environment
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+set -eu
+
+CONFFILE=${1:-/etc/knot/knot.conf}
+
+if [ ! -r $CONFFILE ]; then
+ echo "$CONFFILE doesn't exist or has wrong permissions."
+ exit 1;
+fi
+
+KNOT_RUNDIR=$(sed -ne "s/#.*$//;s/.*rundir: \"*\([^\";]*\\).*/\\1/p;" $CONFFILE)
+[ -z "$KNOT_RUNDIR" ] && KNOT_RUNDIR=/run/knot
+
+mkdir --parents "$KNOT_RUNDIR";
+
+KNOT_USER=$(sed -ne "s/#.*$//;s/.*user:[ \"]*\\([^\\:\"]*\\)[ \"]*/\\1/p;" $CONFFILE)
+
+if [ -n "$KNOT_USER" ]; then
+ if ! getent passwd $KNOT_USER >/dev/null; then
+ echo "Configured user '$KNOT_USER' doesn't exist."
+ exit 1
+ fi
+
+ KNOT_GROUP=$(sed -ne "s/#.*$//;s/.*user:[ \"]*[^\\:\"]*\\:\\([^\"]*\\)[ \"]*/\\1/p;" $CONFFILE)
+ if [ -z "$KNOT_GROUP" ]; then
+ KNOT_GROUP=$(getent group $(getent passwd "$KNOT_USER" | cut -f 4 -d :) | cut -f 1 -d :)
+ fi
+
+ if ! getent group $KNOT_GROUP >/dev/null; then
+ echo "Configured group '$KNOT_GROUP' doesn't exist."
+ exit 1
+ fi
+ chown --silent "$KNOT_USER:$KNOT_GROUP" "$KNOT_RUNDIR"
+ chmod 775 "$KNOT_RUNDIR"
+fi
+
+:
diff --git a/distro/pkg/deb/rules b/distro/pkg/deb/rules
new file mode 100755
index 0000000..bfa0bba
--- /dev/null
+++ b/distro/pkg/deb/rules
@@ -0,0 +1,101 @@
+#!/usr/bin/make -f
+
+export DEB_BUILD_MAINT_OPTIONS = hardening=+all
+export DEB_CFLAGS_MAINT_APPEND = -Wall -DNDEBUG
+export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
+
+export DPKG_GENSYMBOLS_CHECK_LEVEL := 4
+export KNOT_SOFTHSM2_DSO = /usr/lib/softhsm/libsofthsm2.so
+
+include /usr/share/dpkg/default.mk
+
+ifeq (maint,$(filter $(DEB_BUILD_OPTIONS),maint))
+ FASTPARSER := --disable-fastparser
+else
+ FASTPARSER := --enable-fastparser
+endif
+
+ifeq ($(DEB_HOST_ARCH),$(filter $(DEB_HOST_ARCH),hurd-i386))
+ RECVMMSG:=--enable-recvmmsg=no
+else
+ RECVMMSG:=--enable-recvmmsg=yes
+endif
+
+ifeq ($(DEB_HOST_ARCH),$(filter $(DEB_HOST_ARCH),amd64 i386))
+ RUN_TEST :=
+else
+ RUN_TEST := -timeout --kill-after=5s 5m
+endif
+
+LIBKNOT_SYMBOLS := $(wildcard $(CURDIR)/debian/libknot*.symbols)
+
+# MAJOR.MINOR version part
+BASE_VERSION := $(shell echo $(DEB_VERSION) | sed 's/^\([^.]\+\.[^.]\+\).*/\1/')
+
+PYBUILD = pybuild --dir python --dest-dir debian/python3-libknot
+
+%:
+ dh $@ \
+ --exclude=.la --exclude=example.com.zone \
+ --with python3
+
+override_dh_auto_configure:
+ dh_auto_configure -- \
+ --sysconfdir=/etc \
+ --localstatedir=/var/lib \
+ --libexecdir=/usr/lib/knot \
+ --with-rundir=/run/knot \
+ --with-moduledir=/usr/lib/$(DEB_HOST_MULTIARCH)/knot/modules-$(BASE_VERSION) \
+ --with-storage=/var/lib/knot \
+ --enable-systemd=auto \
+ --enable-dnstap \
+ --with-module-dnstap=shared \
+ --with-module-geoip=shared \
+ $(RECVMMSG) \
+ $(FASTPARSER) \
+ --disable-silent-rules \
+ --enable-xdp=yes \
+ --enable-quic=yes \
+ --disable-static
+
+override_dh_auto_configure-indep:
+ $(PYBUILD) --configure
+
+override_dh_auto_build-indep:
+ dh_auto_build -- html
+ $(PYBUILD) --build
+
+override_dh_auto_install-arch:
+ dh_auto_install -- install
+ # rename knot.sample.conf to knot.conf
+ mv $(CURDIR)/debian/tmp/etc/knot/knot.sample.conf $(CURDIR)/debian/tmp/etc/knot/knot.conf
+ # Some workarounds where XDP is unavailable
+ @if [ -f "$(CURDIR)/debian/tmp/usr/sbin/kxdpgun" ]; then \
+ echo "XDP enabled"; \
+ if grep -E -q "DoQ support: +no" "$(CURDIR)/debian/tmp/usr/sbin/knotd"; then \
+ echo "Stripping the QUIC symbols"; \
+ sed -i '/knot_xquic_/d' $(LIBKNOT_SYMBOLS); \
+ fi \
+ else \
+ echo "XDP disabled"; \
+ touch $(CURDIR)/debian/tmp/usr/share/man/man8/kxdpgun.8; \
+ printf '#!/bin/sh\n\necho "kxdpgun not available"\n' > $(CURDIR)/debian/tmp/usr/sbin/kxdpgun; \
+ fi
+
+override_dh_auto_install-indep:
+ dh_auto_install -- install-html
+ # rename knot.sample.conf to knot.conf
+ mv $(CURDIR)/debian/tmp/etc/knot/knot.sample.conf $(CURDIR)/debian/tmp/etc/knot/knot.conf
+ $(PYBUILD) --install
+
+override_dh_auto_test-indep:
+override_dh_auto_test-arch:
+ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
+ $(RUN_TEST) dh_auto_test
+endif
+
+override_dh_missing:
+ dh_missing --fail-missing
+
+override_dh_installchangelogs:
+ dh_installchangelogs NEWS
diff --git a/distro/pkg/deb/source/format b/distro/pkg/deb/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/distro/pkg/deb/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/distro/pkg/deb/tests/authoritative-server b/distro/pkg/deb/tests/authoritative-server
new file mode 100755
index 0000000..028dfbf
--- /dev/null
+++ b/distro/pkg/deb/tests/authoritative-server
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+# 2018-11-02
+# License: GPLv3+
+
+# error on exit
+set -e
+# for handling jobspecs:
+set -m
+
+if [ -z "$AUTOPKGTEST_ARTIFACTS" ]; then
+ d="$(mktemp -d)"
+ remove="$d"
+else
+ d="$AUTOPKGTEST_ARTIFACTS"
+fi
+ip="${TESTIP:-127.$(( $RANDOM % 256 )).$(( $RANDOM % 256 )).$(( $RANDOM % 256 ))}"
+port="${PORT:-8123}"
+knotc="${KNOTC:-/usr/sbin/knotc}"
+knotd="${KNOTD:-/usr/sbin/knotd}"
+keymgr="${KEYMGR:-/usr/sbin/keymgr}"
+kdig="${KDIG:-$(command -v kdig)}"
+kzonecheck="${KZONECHECK:-$(command -v kzonecheck)}"
+test_address="${TEST_ADDRESS:-192.0.2.199}"
+
+declare -a knot_conf="--config=$d/knot.conf"
+declare -a knot_args=("$knot_conf" --verbose)
+
+printf "%s + %s roundtrip tests\n------------\n workdir: %s\n IP addr: %s\n knot args: %s\n" "$knotd" "$kdig" "$d" "$ip" "${knot_args[*]}"
+
+section() {
+ printf "\n%s\n" "$1"
+ sed 's/./-/g' <<<"$1"
+}
+
+cleanup () {
+ section "cleaning up"
+ find "$d" -ls
+ "${knotc}" "${knot_args[@]}" stop
+ wait %1
+ tail -n +1 -v "$d"/*.err
+ if [ "$remove" ]; then
+ printf "\ncleaning up working directory %s\n" "$remove"
+ rm -rf "$remove"
+ fi
+}
+trap cleanup EXIT
+
+section "set up config file and zonefile"
+
+user=$(id -nu)
+group=$(id -ng)
+cat > "$d/knot.conf" <<EOF
+server:
+ rundir: "$d"
+ listen: $ip@$port
+ user: $user:$group
+database:
+ storage: "$d"
+template:
+ - id: default
+ storage: "$d"
+ file: "%s.zone"
+zone:
+ - domain: example.net
+ dnssec-signing: on
+EOF
+
+cat > "$d/example.net.zone" <<EOF
+@ 1D IN SOA a.ns hostmaster 2018103100 3h 15m 1w 1d
+@ 1D IN NS a.ns.example.net.
+@ 1D IN NS b.ns.example.net.
+a.ns 1D IN A 192.0.2.1
+b.ns 1D IN A 192.0.2.2
+test 1D IN A $test_address
+EOF
+
+find "$d" -maxdepth 1 -type f -print0 | xargs -0 tail -n +1 -v
+
+mkdir -p "${d}"
+
+section "kzonecheck'ing zonefile"
+"${kzonecheck}" -v "$d/example.net.zone"
+
+section "launching knot"
+"${knotd}" "${knot_args[@]}" 2> "$d/knotd.err" &
+
+# FIXME: this is an annoying poll -- would be better if we could be
+# alerted when the daemon is done setting up the socket, but i don't
+# want to "--daemonize" if i can avoid it because i want the shell to
+# remain in direct supervision of all its processes
+tried=0
+while [ $tried -lt 10 ] ; do
+ if "${knotc}" "${knot_args[@]}" status 2>&1; then
+ break;
+ fi
+ sleep 0.5
+ tried=$(( $tried + 1 ))
+done
+if [ $tried -ge 10 ]; then
+ printf "failed to use %s\n" "${knotc}" >&2
+ exit 1
+fi
+
+section "querying knot"
+"${kdig}" -p "${port}" @"${ip}" -t A test.example.net test2.example.net
+answer="$("${kdig}" +short -p "${port}" @"${ip}" -t A test.example.net)"
+if ! [ "$answer" = "$test_address" ]; then
+ printf "test.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer" >&2
+ exit 1
+fi
+answer2="$("${kdig}" +short -p "${port}" @"${ip}" -t A test2.example.net)"
+if ! [ "$answer2" = "" ]; then
+ printf "test2.example.net gave unexpected answer!\n got: %s\n" "$answer2" >&2
+ exit 1
+fi
+
+section "modifying zone"
+printf "test2 1D IN A $test_address\n" >>"$d/example.net.zone"
+sed -i 's/^@ 1D IN SOA.*/@ 1D IN SOA a.ns hostmaster 2018110100 3h 15m 1w 1d/' "$d/example.net.zone"
+"${knotc}" "${knot_args[@]}" reload
+sleep 1
+
+section "querying again"
+"${kdig}" -p "${port}" @"${ip}" -t A test.example.net test2.example.net
+answer="$("${kdig}" +short -p "${port}" @"${ip}" -t A test.example.net)"
+if ! [ "$answer" = "$test_address" ]; then
+ printf "test.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer" >&2
+ exit 1
+fi
+answer2="$("${kdig}" +short -p "${port}" @"${ip}" -t A test2.example.net)"
+if ! [ "$answer2" = "$test_address" ]; then
+ printf "test2.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer2" >&2
+ exit 1
+fi
+
+section "querying DNSSEC"
+"${kdig}" -p "${port}" @"${ip}" -t DNSKEY example.net. +dnssec
+if ! "${kdig}" -p "${port}" @"${ip}" -t DNSKEY example.net. +dnssec 2>&1 | grep -q "RRSIG[[:space:]]*DNSKEY"; then
+ printf "DNSSEC query not successful" >&2
+ exit 1
+fi
+
+section "listing keys with keymgr"
+"${keymgr}" "$knot_conf" -e example.net. list
+if ! "${keymgr}" "$knot_conf" -e example.net. list 2>&1 | grep -q "ksk=yes"; then
+ printf "keymgr did not list KSK as expected" >&2
+ exit 1
+fi
diff --git a/distro/pkg/deb/tests/control b/distro/pkg/deb/tests/control
new file mode 100644
index 0000000..e8b3dcb
--- /dev/null
+++ b/distro/pkg/deb/tests/control
@@ -0,0 +1,13 @@
+Tests: kdig
+Restrictions: skippable
+Depends:
+ ca-certificates,
+ iputils-ping,
+ knot-dnsutils,
+
+Tests: authoritative-server
+Depends:
+ findutils,
+ knot,
+ knot-dnsutils,
+ knot-dnssecutils,
diff --git a/distro/pkg/deb/tests/kdig b/distro/pkg/deb/tests/kdig
new file mode 100755
index 0000000..f1dbe5a
--- /dev/null
+++ b/distro/pkg/deb/tests/kdig
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+# Skip the test if no internet access
+ping -c1 1.1.1.1 2>&1 || exit 77
+
+expected=198.41.0.4
+answer=$(kdig +short +tls-ca @1.1.1.1 -q a.root-servers.net. -t A 2>&1 || true)
+
+if [ "$answer" != "$expected" ]; then
+ printf "expected: %s\ngot: %s\n" "$expected" "$answer" >&2
+ kdig -d +tls-ca @1.1.1.1 -q a.root-servers.net. -t A
+fi
diff --git a/distro/pkg/deb/ufw/knot b/distro/pkg/deb/ufw/knot
new file mode 100644
index 0000000..ee36916
--- /dev/null
+++ b/distro/pkg/deb/ufw/knot
@@ -0,0 +1,4 @@
+[Knot]
+title=Internet Domain Name Server
+description=The Knot DNS implements an Internet domain name server.
+ports=53
diff --git a/distro/pkg/deb/watch b/distro/pkg/deb/watch
new file mode 100644
index 0000000..7cf9ea1
--- /dev/null
+++ b/distro/pkg/deb/watch
@@ -0,0 +1,4 @@
+version=4
+opts=uversionmangle=s/-((alpha|beta|rc)\d*)$/~$1/,pgpsigurlmangle=s/$/.asc/,dversionmangle=s/\+hotfix// \
+https://secure.nic.cz/files/knot-dns/ \
+(?:|.*/)knot(?:[_\-]v?|)(\d\S*)\.(?:tar\.xz|txz|tar\.bz2|tbz2|tar\.gz|tgz)
diff --git a/distro/pkg/el-7/01-disable-unconnected-net-test.patch b/distro/pkg/el-7/01-disable-unconnected-net-test.patch
new file mode 100644
index 0000000..f2b5b11
--- /dev/null
+++ b/distro/pkg/el-7/01-disable-unconnected-net-test.patch
@@ -0,0 +1,26 @@
+Author: Tomas Krizek <tomas.krizek@nic.cz>
+Date: 2019-02-28 14:26:56 +0100
+
+ test_net: disable UDP send on unconnected
+
+ This test started to fail with 5.0 kernel in F30+ on aarch64 and s390x.
+ The functionality isn't used by Knot DNS, so disabling this test until
+ the issue is resolved should be safe.
+
+ Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1675235
+
+diff --git a/tests/contrib/test_net.c b/tests/contrib/test_net.c
+index 0a22abbbc..6ec4bda31 100644
+--- a/tests/contrib/test_net.c
++++ b/tests/contrib/test_net.c
+@@ -310,10 +310,6 @@ static void test_unconnected(void)
+ r = net_dgram_recv(sock, buffer, buffer_len, TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "UDP, receive timeout on unconnected socket");
+
+- struct sockaddr_storage server_addr = addr_from_socket(server);
+- r = net_dgram_send(sock, buffer, buffer_len, &server_addr);
+- is_int(buffer_len, r, "UDP, send on defined address");
+-
+ close(sock);
+
+ // TCP
diff --git a/distro/pkg/el-7/02-revert-AC_PROG_CC.patch b/distro/pkg/el-7/02-revert-AC_PROG_CC.patch
new file mode 100644
index 0000000..fb49c00
--- /dev/null
+++ b/distro/pkg/el-7/02-revert-AC_PROG_CC.patch
@@ -0,0 +1,18 @@
+From: Daniel Salzman <daniel.salzman@nic.cz>
+Date: Sun, 20 Feb 2022 20:38:35 +0100
+Subject: [PATCH] Revert "configure: upgrade from AC_PROG_CC_C99 to AC_PROG_CC"
+
+diff --git a/configure.ac b/configure.ac
+index 6506197ed..c7df7f815 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -38,7 +38,8 @@ release_date=$($SED -n 's/^Knot DNS .* (\(.*\))/\1/p;q;' ${srcdir}/NEWS)
+ AC_SUBST([RELEASE_DATE], $release_date)
+
+ # Set compiler compatibility flags
+-AC_PROG_CC
++AC_PROG_CC_C99 # AC_PROG_CC not supported by CentOS 7
++AM_PROG_CC_C_O # Needed by CentOS 7
+ AC_PROG_CPP_WERROR
+
+ # Set default CFLAGS
diff --git a/distro/pkg/el-7/knot.service b/distro/pkg/el-7/knot.service
new file mode 100644
index 0000000..a872929
--- /dev/null
+++ b/distro/pkg/el-7/knot.service
@@ -0,0 +1,25 @@
+[Unit]
+Description=Knot DNS server
+Wants=network-online.target
+After=network-online.target
+Documentation=man:knotd(8) man:knot.conf(5) man:knotc(8)
+
+[Service]
+Type=notify
+User=knot
+Group=knot
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETPCAP
+AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_SETPCAP
+ExecStartPre=/usr/sbin/knotc conf-check
+ExecStart=/usr/sbin/knotd -m "$KNOT_CONF_MAX_SIZE"
+ExecReload=/bin/kill -HUP $MAINPID
+Restart=on-abort
+LimitNOFILE=1048576
+TimeoutStopSec=300
+# Extend the systemd startup timeout by this value (seconds) for each zone
+Environment="KNOT_ZONE_LOAD_TIMEOUT_SEC=180"
+# Maximum size (MiB) of a configuration database
+Environment="KNOT_CONF_MAX_SIZE=512"
+
+[Install]
+WantedBy=multi-user.target
diff --git a/distro/pkg/el-7/knot.spec b/distro/pkg/el-7/knot.spec
new file mode 100644
index 0000000..b3953e2
--- /dev/null
+++ b/distro/pkg/el-7/knot.spec
@@ -0,0 +1,333 @@
+%global _hardened_build 1
+%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}}
+
+%define GPG_CHECK 0
+%define BASE_VERSION %(echo "%{version}" | sed 's/^\\([^.]\\+\\.[^.]\\+\\).*/\\1/')
+%define repodir %{_builddir}/%{name}-%{version}
+
+Summary: High-performance authoritative DNS server
+Name: knot
+Version: {{ version }}
+Release: cznic.{{ release }}%{?dist}
+License: GPL-3.0-or-later
+URL: https://www.knot-dns.cz
+Source0: %{name}-%{version}.tar.xz
+
+%if 0%{?GPG_CHECK}
+Source1: https://secure.nic.cz/files/knot-dns/%{name}-%{version}.tar.xz.asc
+# PGP keys used to sign upstream releases
+# Export with --armor using command from https://fedoraproject.org/wiki/PackagingDrafts:GPGSignatures
+# Don't forget to update %%prep section when adding/removing keys
+Source100: gpgkey-742FA4E95829B6C5EAC6B85710BB7AF6FEBBD6AB.gpg.asc
+BuildRequires: gnupg2
+%endif
+
+# Test fails on F30+ aarch/s390x for unknown reason, but it is not neccassary for Knot DNS
+Patch1: 01-disable-unconnected-net-test.patch
+Patch2: 02-revert-AC_PROG_CC.patch
+
+# Required dependencies
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: libtool
+BuildRequires: devtoolset-11-make
+BuildRequires: devtoolset-11-gcc
+BuildRequires: pkgconfig(liburcu)
+BuildRequires: pkgconfig(gnutls) >= 3.3
+BuildRequires: pkgconfig(libedit)
+
+# Optional dependencies
+BuildRequires: pkgconfig(libcap-ng)
+BuildRequires: pkgconfig(libidn2)
+BuildRequires: pkgconfig(libmnl)
+BuildRequires: pkgconfig(libnghttp2)
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(systemd)
+# dnstap dependencies
+BuildRequires: pkgconfig(libfstrm)
+BuildRequires: pkgconfig(libprotobuf-c)
+# geoip dependencies
+BuildRequires: pkgconfig(libmaxminddb)
+
+# Distro-dependent dependencies
+%if 0%{?suse_version}
+BuildRequires: python3-Sphinx
+BuildRequires: lmdb-devel
+BuildRequires: protobuf-c
+Requires(pre): pwdutils
+%endif
+%if 0%{?rhel} && 0%{?rhel} <= 7
+BuildRequires: python-sphinx
+BuildRequires: lmdb-devel
+%endif
+%if 0%{?fedora} || 0%{?rhel} > 7
+BuildRequires: python3-sphinx
+BuildRequires: pkgconfig(lmdb)
+%endif
+
+# disable XDP on old EL
+%define configure_xdp --enable-xdp=no
+
+Requires(post): systemd %{_sbindir}/runuser
+Requires(preun): systemd
+Requires(postun): systemd
+
+# Knot DNS 2.7+ isn't compatible with earlier knot-resolver
+Conflicts: knot-resolver < 3.0.0
+
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description
+Knot DNS is a high-performance authoritative DNS server implementation.
+
+%package libs
+Summary: Libraries used by the Knot DNS server and client applications
+
+%description libs
+The package contains shared libraries used by the Knot DNS server and
+utilities.
+
+%package devel
+Summary: Development header files for the Knot DNS libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description devel
+The package contains development header files for the Knot DNS libraries
+included in knot-libs package.
+
+%package utils
+Summary: DNS client utilities shipped with the Knot DNS server
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+# Debian package compat
+Provides: %{name}-dnsutils = %{version}-%{release}
+
+%description utils
+The package contains DNS client utilities shipped with the Knot DNS server.
+
+%package dnssecutils
+Summary: DNSSEC tools shipped with the Knot DNS server
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description dnssecutils
+The package contains DNSSEC tools shipped with the Knot DNS server.
+
+%package module-dnstap
+Summary: dnstap module for Knot DNS
+Requires: %{name} = %{version}-%{release}
+
+%description module-dnstap
+The package contains dnstap Knot DNS module for logging DNS traffic.
+
+%package module-geoip
+Summary: geoip module for Knot DNS
+Requires: %{name} = %{version}-%{release}
+
+%description module-geoip
+The package contains geoip Knot DNS module for geography-based responses.
+
+%package doc
+Summary: Documentation for the Knot DNS server
+BuildArch: noarch
+Provides: bundled(jquery)
+
+%description doc
+The package contains documentation for the Knot DNS server.
+On-line version is available on https://www.knot-dns.cz/documentation/
+
+%prep
+%if 0%{?GPG_CHECK}
+export GNUPGHOME=./gpg-keyring
+[ -d ${GNUPGHOME} ] && rm -r ${GNUPGHOME}
+mkdir --mode=700 ${GNUPGHOME}
+gpg2 --import %{SOURCE100}
+gpg2 --verify %{SOURCE1} %{SOURCE0}
+%endif
+%autosetup -p1
+
+%build
+# disable debug code (causes unused warnings)
+CFLAGS="%{optflags} -DNDEBUG -Wno-unused"
+
+%ifarch armv7hl i686
+# 32-bit architectures sometimes do not have sufficient amount of
+# contiguous address space to handle default values
+%define configure_db_sizes --with-conf-mapsize=64
+%endif
+
+autoreconf -if
+
+export CC="/opt/rh/devtoolset-11/root/usr/bin/gcc"
+%configure \
+ --sysconfdir=/etc \
+ --localstatedir=/var/lib \
+ --libexecdir=/usr/lib/knot \
+ --with-rundir=/run/knot \
+ --with-moduledir=%{_libdir}/knot/modules-%{BASE_VERSION} \
+ --with-storage=/var/lib/knot \
+ %{?configure_db_sizes} \
+ %{?configure_xdp} \
+ --disable-static \
+ --enable-dnstap=yes \
+ --with-module-dnstap=shared \
+ --with-module-geoip=shared
+make %{?_smp_mflags}
+make html
+
+%install
+make install DESTDIR=%{buildroot}
+
+# install documentation
+install -d -m 0755 %{buildroot}%{_pkgdocdir}/samples
+install -p -m 0644 -t %{buildroot}%{_pkgdocdir}/samples samples/*.zone*
+install -p -m 0644 NEWS README.md %{buildroot}%{_pkgdocdir}
+cp -av doc/_build/html %{buildroot}%{_pkgdocdir}
+[ -r %{buildroot}%{_pkgdocdir}/html/index.html ] || exit 1
+rm -f %{buildroot}%{_pkgdocdir}/html/.buildinfo
+
+# install daemon and dbus configuration files
+rm %{buildroot}%{_sysconfdir}/%{name}/*
+install -p -m 0644 -D %{repodir}/samples/%{name}.sample.conf %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf
+%if 0%{?fedora} || 0%{?rhel} > 7
+install -p -m 0644 -D %{repodir}/distro/common/cz.nic.knotd.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/cz.nic.knotd.conf
+%endif
+
+# install systemd files
+install -p -m 0644 -D %{repodir}/distro/pkg/el-7/%{name}.service %{buildroot}%{_unitdir}/%{name}.service
+install -p -m 0644 -D %{repodir}/distro/pkg/el-7/%{name}.tmpfiles %{buildroot}%{_tmpfilesdir}/%{name}.conf
+%if 0%{?suse_version}
+ln -s service %{buildroot}/%{_sbindir}/rcknot
+%endif
+
+# create storage dir
+install -d %{buildroot}%{_sharedstatedir}
+install -d -m 0770 -D %{buildroot}%{_sharedstatedir}/knot
+
+# remove libarchive files
+find %{buildroot} -type f -name "*.la" -delete -print
+
+%check
+V=1 make check
+
+%pre
+getent group knot >/dev/null || groupadd -r knot
+getent passwd knot >/dev/null || \
+ useradd -r -g knot -d %{_sharedstatedir}/knot -s /sbin/nologin \
+ -c "Knot DNS server" knot
+%if 0%{?suse_version}
+%service_add_pre knot.service
+%endif
+
+%post
+systemd-tmpfiles --create %{_tmpfilesdir}/knot.conf &>/dev/null || :
+%if 0%{?suse_version}
+%service_add_post knot.service
+%else
+%systemd_post knot.service
+%endif
+
+%preun
+%if 0%{?suse_version}
+%service_del_preun knot.service
+%else
+%systemd_preun knot.service
+%endif
+
+%postun
+%if 0%{?suse_version}
+%service_del_postun knot.service
+%else
+%systemd_postun_with_restart knot.service
+%endif
+
+%if 0%{?fedora} || 0%{?rhel} > 7
+# https://fedoraproject.org/wiki/Changes/Removing_ldconfig_scriptlets
+%else
+%post libs -p /sbin/ldconfig
+%postun libs -p /sbin/ldconfig
+%endif
+
+%files
+%license COPYING
+%doc %{_pkgdocdir}
+%exclude %{_pkgdocdir}/html
+%attr(770,root,knot) %dir %{_sysconfdir}/knot
+%config(noreplace) %attr(640,root,knot) %{_sysconfdir}/knot/knot.conf
+%if 0%{?fedora} || 0%{?rhel} > 7
+%config(noreplace) %attr(644,root,root) %{_sysconfdir}/dbus-1/system.d/cz.nic.knotd.conf
+%endif
+%attr(770,root,knot) %dir %{_sharedstatedir}/knot
+%dir %{_libdir}/knot
+%dir %{_libdir}/knot/modules-*
+%{_unitdir}/knot.service
+%{_tmpfilesdir}/knot.conf
+%{_sbindir}/kcatalogprint
+%{_sbindir}/kjournalprint
+%{_sbindir}/keymgr
+%{_sbindir}/knotc
+%{_sbindir}/knotd
+%if 0%{?suse_version}
+%{_sbindir}/rcknot
+%endif
+%{_mandir}/man5/knot.conf.*
+%{_mandir}/man8/kcatalogprint.*
+%{_mandir}/man8/kjournalprint.*
+%{_mandir}/man8/keymgr.*
+%{_mandir}/man8/knotc.*
+%{_mandir}/man8/knotd.*
+%ghost %attr(770,root,knot) %dir %{_rundir}/knot
+
+%files utils
+%{_bindir}/kdig
+%{_bindir}/khost
+%{_bindir}/knsupdate
+%if 0%{?use_xdp}
+%{_sbindir}/kxdpgun
+%{_mandir}/man8/kxdpgun.*
+%endif
+%{_mandir}/man1/kdig.*
+%{_mandir}/man1/khost.*
+%{_mandir}/man1/knsupdate.*
+
+%files dnssecutils
+%{_bindir}/knsec3hash
+%{_bindir}/kzonecheck
+%{_bindir}/kzonesign
+%{_mandir}/man1/knsec3hash.*
+%{_mandir}/man1/kzonecheck.*
+%{_mandir}/man1/kzonesign.*
+
+%files module-dnstap
+%{_libdir}/knot/modules-*/dnstap.so
+
+%files module-geoip
+%{_libdir}/knot/modules-*/geoip.so
+
+%files libs
+%license COPYING
+%doc NEWS
+%doc README.md
+%{_libdir}/libdnssec.so.*
+%{_libdir}/libknot.so.*
+%{_libdir}/libzscanner.so.*
+
+%files devel
+%{_includedir}/libdnssec
+%{_includedir}/knot
+%{_includedir}/libknot
+%{_includedir}/libzscanner
+%{_libdir}/libdnssec.so
+%{_libdir}/libknot.so
+%{_libdir}/libzscanner.so
+%{_libdir}/pkgconfig/knotd.pc
+%{_libdir}/pkgconfig/libdnssec.pc
+%{_libdir}/pkgconfig/libknot.pc
+%{_libdir}/pkgconfig/libzscanner.pc
+
+%files doc
+%dir %{_pkgdocdir}
+%doc %{_pkgdocdir}/html
+
+%changelog
+* {{ now }} Jakub Ružička <jakub.ruzicka@nic.cz> - {{ version }}-{{ release }}
+- upstream package
+- see https://www.knot-dns.cz
diff --git a/distro/pkg/el-7/knot.tmpfiles b/distro/pkg/el-7/knot.tmpfiles
new file mode 100644
index 0000000..edec729
--- /dev/null
+++ b/distro/pkg/el-7/knot.tmpfiles
@@ -0,0 +1,3 @@
+# tmpfiles.d(5) runtime directory for knot
+#Type Path Mode UID GID Age Argument
+ d /run/knot 0755 knot knot - -
diff --git a/distro/pkg/nix/default.nix b/distro/pkg/nix/default.nix
new file mode 100644
index 0000000..35cc3e4
--- /dev/null
+++ b/distro/pkg/nix/default.nix
@@ -0,0 +1,69 @@
+{ lib, stdenv, fetchurl, pkg-config, gnutls, liburcu, lmdb, libcap_ng, libidn2, libunistring
+, systemd, nettle, libedit, zlib, libiconv, libintl, libmaxminddb, libbpf, nghttp2, libmnl
+, autoreconfHook, nixosTests, knot-resolver
+}:
+
+stdenv.mkDerivation rec {
+ pname = "knot-dns";
+ version = "{{ version }}";
+
+ src = fetchurl {
+ url = "https://secure.nic.cz/files/knot-dns/knot-${version}.tar.xz";
+ sha256 = "{{ src_hash }}";
+ };
+
+ outputs = [ "bin" "out" "dev" ];
+
+ configureFlags = [
+ "--with-configdir=/etc/knot"
+ "--with-rundir=/run/knot"
+ "--with-storage=/var/lib/knot"
+ ];
+
+ patches = [
+ # Don't try to create directories like /var/lib/knot at build time.
+ # They are later created from NixOS itself.
+ ./dont-create-run-time-dirs.patch
+ ./runtime-deps.patch
+ ];
+
+ nativeBuildInputs = [ pkg-config autoreconfHook ];
+ buildInputs = [
+ gnutls liburcu libidn2 libunistring
+ nettle libedit
+ libiconv lmdb libintl
+ nghttp2 # DoH support in kdig
+ libmaxminddb # optional for geoip module (it's tiny)
+ # without sphinx &al. for developer documentation
+ # TODO: add dnstap support?
+ ] ++ lib.optionals stdenv.isLinux [
+ libcap_ng systemd
+ libbpf libmnl # XDP support (it's Linux kernel API)
+ ] ++ lib.optional stdenv.isDarwin zlib; # perhaps due to gnutls
+
+ enableParallelBuilding = true;
+
+ CFLAGS = [ "-O2" "-DNDEBUG" ];
+
+ doCheck = true;
+ checkFlags = "V=1"; # verbose output in case some test fails
+ doInstallCheck = true;
+
+ postInstall = ''
+ rm -r "$out"/lib/*.la
+ '';
+
+ passthru.tests = {
+ inherit knot-resolver;
+ } // lib.optionalAttrs stdenv.isLinux {
+ inherit (nixosTests) knot;
+ };
+
+ meta = with lib; {
+ description = "Authoritative-only DNS server from .cz domain registry";
+ homepage = "https://knot-dns.cz";
+ license = licenses.gpl3Plus;
+ platforms = platforms.unix;
+ maintainers = [ maintainers.vcunat ];
+ };
+}
diff --git a/distro/pkg/nix/dont-create-run-time-dirs.patch b/distro/pkg/nix/dont-create-run-time-dirs.patch
new file mode 100644
index 0000000..9fe165e
--- /dev/null
+++ b/distro/pkg/nix/dont-create-run-time-dirs.patch
@@ -0,0 +1,32 @@
+diff --git a/samples/Makefile.am b/samples/Makefile.am
+index c253c91..107401d 100644
+--- a/samples/Makefile.am
++++ b/samples/Makefile.am
+@@ -19,11 +19,6 @@ EXTRA_DIST = knot.sample.conf.in example.com.zone
+
+ if HAVE_DAEMON
+
+-install-data-local: knot.sample.conf
+- if [ \! -f $(DESTDIR)/$(config_dir)/knot.sample.conf ]; then \
+- $(INSTALL) -d $(DESTDIR)/$(config_dir); \
+- $(INSTALL_DATA) knot.sample.conf $(srcdir)/example.com.zone $(DESTDIR)/$(config_dir); \
+- fi
+ uninstall-local:
+ -rm -rf $(DESTDIR)/$(config_dir)/knot.sample.conf \
+ $(DESTDIR)/$(config_dir)/example.com.zone
+diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc
+index e6765d9..d859d23 100644
+--- a/src/utils/Makefile.inc
++++ b/src/utils/Makefile.inc
+@@ -79,11 +79,6 @@ endif HAVE_DNSTAP
+ endif HAVE_UTILS
+
+ if HAVE_DAEMON
+-# Create storage and run-time directories
+-install-data-hook:
+- $(INSTALL) -d $(DESTDIR)/@config_dir@
+- $(INSTALL) -d $(DESTDIR)/@run_dir@
+- $(INSTALL) -d $(DESTDIR)/@storage_dir@
+
+ sbin_PROGRAMS = knotc knotd
+
diff --git a/distro/pkg/nix/runtime-deps.patch b/distro/pkg/nix/runtime-deps.patch
new file mode 100644
index 0000000..19fc9cd
--- /dev/null
+++ b/distro/pkg/nix/runtime-deps.patch
@@ -0,0 +1,14 @@
+Remove unnecessary runtime dependencies.
+
+`knotc status configure` shows summary from the configure script,
+but that contains also references like include paths.
+Filter these at least in a crude way (whole lines).
+--- a/configure.ac
++++ b/configure.ac
+@@ -766,5 +766,5 @@ result_msg_base=" Knot DNS $VERSION
+
+-result_msg_esc=$(echo -n "$result_msg_base" | sed '$!s/$/\\n/' | tr -d '\n')
++result_msg_esc=$(echo -n "$result_msg_base" | grep -Fv "$NIX_STORE" | sed '$!s/$/\\n/' | tr -d '\n')
+
+ AC_DEFINE_UNQUOTED([CONFIGURE_SUMMARY],["$result_msg_esc"],[Configure summary])
+
diff --git a/distro/pkg/nix/top-level.nix b/distro/pkg/nix/top-level.nix
new file mode 100644
index 0000000..303923c
--- /dev/null
+++ b/distro/pkg/nix/top-level.nix
@@ -0,0 +1,8 @@
+
+with import <nixpkgs> {};
+
+(callPackage ./. {
+}).overrideAttrs (attrs: {
+ src = ./knot-{{ version }}.tar.xz;
+})
+
diff --git a/distro/pkg/rpm/01-test_net-disable-udp-send-on-unconnected.patch b/distro/pkg/rpm/01-test_net-disable-udp-send-on-unconnected.patch
new file mode 100644
index 0000000..f2b5b11
--- /dev/null
+++ b/distro/pkg/rpm/01-test_net-disable-udp-send-on-unconnected.patch
@@ -0,0 +1,26 @@
+Author: Tomas Krizek <tomas.krizek@nic.cz>
+Date: 2019-02-28 14:26:56 +0100
+
+ test_net: disable UDP send on unconnected
+
+ This test started to fail with 5.0 kernel in F30+ on aarch64 and s390x.
+ The functionality isn't used by Knot DNS, so disabling this test until
+ the issue is resolved should be safe.
+
+ Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1675235
+
+diff --git a/tests/contrib/test_net.c b/tests/contrib/test_net.c
+index 0a22abbbc..6ec4bda31 100644
+--- a/tests/contrib/test_net.c
++++ b/tests/contrib/test_net.c
+@@ -310,10 +310,6 @@ static void test_unconnected(void)
+ r = net_dgram_recv(sock, buffer, buffer_len, TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "UDP, receive timeout on unconnected socket");
+
+- struct sockaddr_storage server_addr = addr_from_socket(server);
+- r = net_dgram_send(sock, buffer, buffer_len, &server_addr);
+- is_int(buffer_len, r, "UDP, send on defined address");
+-
+ close(sock);
+
+ // TCP
diff --git a/distro/pkg/rpm/knot.spec b/distro/pkg/rpm/knot.spec
new file mode 100644
index 0000000..c245945
--- /dev/null
+++ b/distro/pkg/rpm/knot.spec
@@ -0,0 +1,328 @@
+%global _hardened_build 1
+%{!?_pkgdocdir: %global _pkgdocdir %{_docdir}/%{name}}
+
+%define GPG_CHECK 0
+%define BASE_VERSION %(echo "%{version}" | sed 's/^\\([^.]\\+\\.[^.]\\+\\).*/\\1/')
+%define repodir %{_builddir}/%{name}-%{version}
+
+Summary: High-performance authoritative DNS server
+Name: knot
+Version: {{ version }}
+Release: cznic.{{ release }}%{?dist}
+License: GPL-3.0-or-later
+URL: https://www.knot-dns.cz
+Source0: %{name}-%{version}.tar.xz
+
+%if 0%{?GPG_CHECK}
+Source1: https://secure.nic.cz/files/knot-dns/%{name}-%{version}.tar.xz.asc
+# PGP keys used to sign upstream releases
+# Export with --armor using command from https://fedoraproject.org/wiki/PackagingDrafts:GPGSignatures
+# Don't forget to update %%prep section when adding/removing keys
+Source100: gpgkey-742FA4E95829B6C5EAC6B85710BB7AF6FEBBD6AB.gpg.asc
+BuildRequires: gnupg2
+%endif
+
+# Test fails on F30+ aarch/s390x for unknown reason, but it is not neccassary for Knot DNS
+Patch1: 01-test_net-disable-udp-send-on-unconnected.patch
+
+# Required dependencies
+BuildRequires: autoconf
+BuildRequires: automake
+BuildRequires: libtool
+BuildRequires: make
+BuildRequires: gcc
+BuildRequires: pkgconfig(liburcu)
+BuildRequires: pkgconfig(gnutls) >= 3.3
+BuildRequires: pkgconfig(libedit)
+
+# Optional dependencies
+BuildRequires: pkgconfig(libcap-ng)
+BuildRequires: pkgconfig(libidn2)
+BuildRequires: pkgconfig(libmnl)
+BuildRequires: pkgconfig(libnghttp2)
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(systemd)
+# dnstap dependencies
+BuildRequires: pkgconfig(libfstrm)
+BuildRequires: pkgconfig(libprotobuf-c)
+# geoip dependencies
+BuildRequires: pkgconfig(libmaxminddb)
+# XDP dependencies
+BuildRequires: pkgconfig(libbpf)
+
+# Distro-dependent dependencies
+%if 0%{?suse_version}
+BuildRequires: python3-Sphinx
+BuildRequires: lmdb-devel
+BuildRequires: protobuf-c
+Requires(pre): pwdutils
+%if 0%{?sle_version} != 150400
+BuildRequires: pkgconfig(libxdp)
+%endif
+%endif
+%if 0%{?fedora} || 0%{?rhel}
+BuildRequires: python3-sphinx
+BuildRequires: pkgconfig(lmdb)
+%if 0%{?fedora}
+BuildRequires: pkgconfig(libxdp)
+%endif
+%endif
+
+%if 0%{?rhel} >= 9 || 0%{?suse_version} || 0%{?fedora}
+%define configure_quic --enable-quic=yes
+%endif
+
+Requires(post): systemd %{_sbindir}/runuser
+Requires(preun): systemd
+Requires(postun): systemd
+
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description
+Knot DNS is a high-performance authoritative DNS server implementation.
+
+%package libs
+Summary: Libraries used by the Knot DNS server and client applications
+# Knot DNS 3.2+ isn't compatible with earlier knot-resolver
+Conflicts: knot-resolver < 5.5.2
+
+%description libs
+The package contains shared libraries used by the Knot DNS server and
+utilities.
+
+%package devel
+Summary: Development header files for the Knot DNS libraries
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description devel
+The package contains development header files for the Knot DNS libraries
+included in knot-libs package.
+
+%package utils
+Summary: DNS client utilities shipped with the Knot DNS server
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+# Debian package compat
+Provides: %{name}-dnsutils = %{version}-%{release}
+
+%description utils
+The package contains DNS client utilities shipped with the Knot DNS server.
+
+%package dnssecutils
+Summary: DNSSEC tools shipped with the Knot DNS server
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description dnssecutils
+The package contains DNSSEC tools shipped with the Knot DNS server.
+
+%package module-dnstap
+Summary: dnstap module for Knot DNS
+Requires: %{name} = %{version}-%{release}
+
+%description module-dnstap
+The package contains dnstap Knot DNS module for logging DNS traffic.
+
+%package module-geoip
+Summary: geoip module for Knot DNS
+Requires: %{name} = %{version}-%{release}
+
+%description module-geoip
+The package contains geoip Knot DNS module for geography-based responses.
+
+%package doc
+Summary: Documentation for the Knot DNS server
+BuildArch: noarch
+Provides: bundled(jquery)
+
+%description doc
+The package contains documentation for the Knot DNS server.
+On-line version is available on https://www.knot-dns.cz/documentation/
+
+%prep
+%if 0%{?GPG_CHECK}
+export GNUPGHOME=./gpg-keyring
+[ -d ${GNUPGHOME} ] && rm -r ${GNUPGHOME}
+mkdir --mode=700 ${GNUPGHOME}
+gpg2 --import %{SOURCE100}
+gpg2 --verify %{SOURCE1} %{SOURCE0}
+%endif
+%autosetup -p1
+
+%build
+# disable debug code (causes unused warnings)
+CFLAGS="%{optflags} -DNDEBUG -Wno-unused"
+
+%ifarch armv7hl i686
+# 32-bit architectures sometimes do not have sufficient amount of
+# contiguous address space to handle default values
+%define configure_db_sizes --with-conf-mapsize=64
+%endif
+
+%configure \
+ --sysconfdir=/etc \
+ --localstatedir=/var/lib \
+ --libexecdir=/usr/lib/knot \
+ --with-rundir=/run/knot \
+ --with-moduledir=%{_libdir}/knot/modules-%{BASE_VERSION} \
+ --with-storage=/var/lib/knot \
+ %{?configure_db_sizes} \
+ %{?configure_quic} \
+ --disable-static \
+ --enable-dnstap=yes \
+ --with-module-dnstap=shared \
+ --with-module-geoip=shared
+make %{?_smp_mflags}
+make html
+
+%install
+make install DESTDIR=%{buildroot}
+
+# install documentation
+install -d -m 0755 %{buildroot}%{_pkgdocdir}/samples
+install -p -m 0644 -t %{buildroot}%{_pkgdocdir}/samples samples/*.zone*
+install -p -m 0644 NEWS README.md %{buildroot}%{_pkgdocdir}
+cp -av doc/_build/html %{buildroot}%{_pkgdocdir}
+[ -r %{buildroot}%{_pkgdocdir}/html/index.html ] || exit 1
+rm -f %{buildroot}%{_pkgdocdir}/html/.buildinfo
+
+# install daemon and dbus configuration files
+rm %{buildroot}%{_sysconfdir}/%{name}/*
+install -p -m 0644 -D %{repodir}/samples/%{name}.sample.conf %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf
+%if 0%{?fedora} || 0%{?rhel} > 7
+install -p -m 0644 -D %{repodir}/distro/common/cz.nic.knotd.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/cz.nic.knotd.conf
+%endif
+
+# install systemd files
+install -p -m 0644 -D %{repodir}/distro/common/%{name}.service %{buildroot}%{_unitdir}/%{name}.service
+%if 0%{?suse_version}
+ln -s service %{buildroot}/%{_sbindir}/rcknot
+%endif
+
+# create storage dir
+install -d %{buildroot}%{_sharedstatedir}
+install -d -m 0770 -D %{buildroot}%{_sharedstatedir}/knot
+
+# remove libarchive files
+find %{buildroot} -type f -name "*.la" -delete -print
+
+%check
+V=1 make check
+
+%pre
+getent group knot >/dev/null || groupadd -r knot
+getent passwd knot >/dev/null || \
+ useradd -r -g knot -d %{_sharedstatedir}/knot -s /sbin/nologin \
+ -c "Knot DNS server" knot
+%if 0%{?suse_version}
+%service_add_pre knot.service
+%endif
+
+%post
+%if 0%{?suse_version}
+%service_add_post knot.service
+%else
+%systemd_post knot.service
+%endif
+
+%preun
+%if 0%{?suse_version}
+%service_del_preun knot.service
+%else
+%systemd_preun knot.service
+%endif
+
+%postun
+%if 0%{?suse_version}
+%service_del_postun knot.service
+%else
+%systemd_postun_with_restart knot.service
+%endif
+
+%if 0%{?fedora} || 0%{?rhel} > 7
+# https://fedoraproject.org/wiki/Changes/Removing_ldconfig_scriptlets
+%else
+%post libs -p /sbin/ldconfig
+%postun libs -p /sbin/ldconfig
+%endif
+
+%files
+%license COPYING
+%doc %{_pkgdocdir}
+%exclude %{_pkgdocdir}/html
+%attr(750,root,knot) %dir %{_sysconfdir}/knot
+%config(noreplace) %attr(640,root,knot) %{_sysconfdir}/knot/knot.conf
+%if 0%{?fedora} || 0%{?rhel} > 7
+%config(noreplace) %attr(644,root,root) %{_sysconfdir}/dbus-1/system.d/cz.nic.knotd.conf
+%endif
+%attr(770,root,knot) %dir %{_sharedstatedir}/knot
+%dir %{_libdir}/knot
+%dir %{_libdir}/knot/modules-*
+%{_unitdir}/knot.service
+%{_sbindir}/kcatalogprint
+%{_sbindir}/kjournalprint
+%{_sbindir}/keymgr
+%{_sbindir}/knotc
+%{_sbindir}/knotd
+%if 0%{?suse_version}
+%{_sbindir}/rcknot
+%endif
+%{_mandir}/man5/knot.conf.*
+%{_mandir}/man8/kcatalogprint.*
+%{_mandir}/man8/kjournalprint.*
+%{_mandir}/man8/keymgr.*
+%{_mandir}/man8/knotc.*
+%{_mandir}/man8/knotd.*
+%ghost %attr(770,root,knot) %dir %{_rundir}/knot
+
+%files utils
+%{_bindir}/kdig
+%{_bindir}/khost
+%{_bindir}/knsupdate
+%{_sbindir}/kxdpgun
+%{_mandir}/man8/kxdpgun.*
+%{_mandir}/man1/kdig.*
+%{_mandir}/man1/khost.*
+%{_mandir}/man1/knsupdate.*
+
+%files dnssecutils
+%{_bindir}/knsec3hash
+%{_bindir}/kzonecheck
+%{_bindir}/kzonesign
+%{_mandir}/man1/knsec3hash.*
+%{_mandir}/man1/kzonecheck.*
+%{_mandir}/man1/kzonesign.*
+
+%files module-dnstap
+%{_libdir}/knot/modules-*/dnstap.so
+
+%files module-geoip
+%{_libdir}/knot/modules-*/geoip.so
+
+%files libs
+%license COPYING
+%doc NEWS
+%doc README.md
+%{_libdir}/libdnssec.so.*
+%{_libdir}/libknot.so.*
+%{_libdir}/libzscanner.so.*
+
+%files devel
+%{_includedir}/libdnssec
+%{_includedir}/knot
+%{_includedir}/libknot
+%{_includedir}/libzscanner
+%{_libdir}/libdnssec.so
+%{_libdir}/libknot.so
+%{_libdir}/libzscanner.so
+%{_libdir}/pkgconfig/knotd.pc
+%{_libdir}/pkgconfig/libdnssec.pc
+%{_libdir}/pkgconfig/libknot.pc
+%{_libdir}/pkgconfig/libzscanner.pc
+
+%files doc
+%dir %{_pkgdocdir}
+%doc %{_pkgdocdir}/html
+
+%changelog
+* {{ now }} Knot DNS <knot-dns@labs.nic.cz> - {{ version }}-{{ release }}
+- upstream package
+- see https://www.knot-dns.cz
diff --git a/distro/tests/README.md b/distro/tests/README.md
new file mode 100644
index 0000000..d356db5
--- /dev/null
+++ b/distro/tests/README.md
@@ -0,0 +1,16 @@
+# packaging tests
+
+Debian autopkgtests from `distro/pkg/deb/tests` are reused here
+using `apkg test` and symlinks.
+
+To run tests (from project root):
+
+ apkg test
+
+See templated tests control: distro/tests/extra/all
+
+To see rendered control (from project root):
+
+ apkg test --show-control [--distro debian-11]
+
+See [apkg test docs](https://pkg.labs.nic.cz/pages/apkg/test/).
diff --git a/distro/tests/authoritative-server b/distro/tests/authoritative-server
new file mode 100755
index 0000000..028dfbf
--- /dev/null
+++ b/distro/tests/authoritative-server
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+# 2018-11-02
+# License: GPLv3+
+
+# error on exit
+set -e
+# for handling jobspecs:
+set -m
+
+if [ -z "$AUTOPKGTEST_ARTIFACTS" ]; then
+ d="$(mktemp -d)"
+ remove="$d"
+else
+ d="$AUTOPKGTEST_ARTIFACTS"
+fi
+ip="${TESTIP:-127.$(( $RANDOM % 256 )).$(( $RANDOM % 256 )).$(( $RANDOM % 256 ))}"
+port="${PORT:-8123}"
+knotc="${KNOTC:-/usr/sbin/knotc}"
+knotd="${KNOTD:-/usr/sbin/knotd}"
+keymgr="${KEYMGR:-/usr/sbin/keymgr}"
+kdig="${KDIG:-$(command -v kdig)}"
+kzonecheck="${KZONECHECK:-$(command -v kzonecheck)}"
+test_address="${TEST_ADDRESS:-192.0.2.199}"
+
+declare -a knot_conf="--config=$d/knot.conf"
+declare -a knot_args=("$knot_conf" --verbose)
+
+printf "%s + %s roundtrip tests\n------------\n workdir: %s\n IP addr: %s\n knot args: %s\n" "$knotd" "$kdig" "$d" "$ip" "${knot_args[*]}"
+
+section() {
+ printf "\n%s\n" "$1"
+ sed 's/./-/g' <<<"$1"
+}
+
+cleanup () {
+ section "cleaning up"
+ find "$d" -ls
+ "${knotc}" "${knot_args[@]}" stop
+ wait %1
+ tail -n +1 -v "$d"/*.err
+ if [ "$remove" ]; then
+ printf "\ncleaning up working directory %s\n" "$remove"
+ rm -rf "$remove"
+ fi
+}
+trap cleanup EXIT
+
+section "set up config file and zonefile"
+
+user=$(id -nu)
+group=$(id -ng)
+cat > "$d/knot.conf" <<EOF
+server:
+ rundir: "$d"
+ listen: $ip@$port
+ user: $user:$group
+database:
+ storage: "$d"
+template:
+ - id: default
+ storage: "$d"
+ file: "%s.zone"
+zone:
+ - domain: example.net
+ dnssec-signing: on
+EOF
+
+cat > "$d/example.net.zone" <<EOF
+@ 1D IN SOA a.ns hostmaster 2018103100 3h 15m 1w 1d
+@ 1D IN NS a.ns.example.net.
+@ 1D IN NS b.ns.example.net.
+a.ns 1D IN A 192.0.2.1
+b.ns 1D IN A 192.0.2.2
+test 1D IN A $test_address
+EOF
+
+find "$d" -maxdepth 1 -type f -print0 | xargs -0 tail -n +1 -v
+
+mkdir -p "${d}"
+
+section "kzonecheck'ing zonefile"
+"${kzonecheck}" -v "$d/example.net.zone"
+
+section "launching knot"
+"${knotd}" "${knot_args[@]}" 2> "$d/knotd.err" &
+
+# FIXME: this is an annoying poll -- would be better if we could be
+# alerted when the daemon is done setting up the socket, but i don't
+# want to "--daemonize" if i can avoid it because i want the shell to
+# remain in direct supervision of all its processes
+tried=0
+while [ $tried -lt 10 ] ; do
+ if "${knotc}" "${knot_args[@]}" status 2>&1; then
+ break;
+ fi
+ sleep 0.5
+ tried=$(( $tried + 1 ))
+done
+if [ $tried -ge 10 ]; then
+ printf "failed to use %s\n" "${knotc}" >&2
+ exit 1
+fi
+
+section "querying knot"
+"${kdig}" -p "${port}" @"${ip}" -t A test.example.net test2.example.net
+answer="$("${kdig}" +short -p "${port}" @"${ip}" -t A test.example.net)"
+if ! [ "$answer" = "$test_address" ]; then
+ printf "test.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer" >&2
+ exit 1
+fi
+answer2="$("${kdig}" +short -p "${port}" @"${ip}" -t A test2.example.net)"
+if ! [ "$answer2" = "" ]; then
+ printf "test2.example.net gave unexpected answer!\n got: %s\n" "$answer2" >&2
+ exit 1
+fi
+
+section "modifying zone"
+printf "test2 1D IN A $test_address\n" >>"$d/example.net.zone"
+sed -i 's/^@ 1D IN SOA.*/@ 1D IN SOA a.ns hostmaster 2018110100 3h 15m 1w 1d/' "$d/example.net.zone"
+"${knotc}" "${knot_args[@]}" reload
+sleep 1
+
+section "querying again"
+"${kdig}" -p "${port}" @"${ip}" -t A test.example.net test2.example.net
+answer="$("${kdig}" +short -p "${port}" @"${ip}" -t A test.example.net)"
+if ! [ "$answer" = "$test_address" ]; then
+ printf "test.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer" >&2
+ exit 1
+fi
+answer2="$("${kdig}" +short -p "${port}" @"${ip}" -t A test2.example.net)"
+if ! [ "$answer2" = "$test_address" ]; then
+ printf "test2.example.net mismatch!\nexpected: %s\n got: %s\n" "$test_address" "$answer2" >&2
+ exit 1
+fi
+
+section "querying DNSSEC"
+"${kdig}" -p "${port}" @"${ip}" -t DNSKEY example.net. +dnssec
+if ! "${kdig}" -p "${port}" @"${ip}" -t DNSKEY example.net. +dnssec 2>&1 | grep -q "RRSIG[[:space:]]*DNSKEY"; then
+ printf "DNSSEC query not successful" >&2
+ exit 1
+fi
+
+section "listing keys with keymgr"
+"${keymgr}" "$knot_conf" -e example.net. list
+if ! "${keymgr}" "$knot_conf" -e example.net. list 2>&1 | grep -q "ksk=yes"; then
+ printf "keymgr did not list KSK as expected" >&2
+ exit 1
+fi
diff --git a/distro/tests/extra/all/control b/distro/tests/extra/all/control
new file mode 100644
index 0000000..6f9da6b
--- /dev/null
+++ b/distro/tests/extra/all/control
@@ -0,0 +1,10 @@
+Tests: kdig
+Restrictions: skippable
+{%- if distro.match('deb') %}
+Depends: iputils-ping, ca-certificates
+{%- elif distro.match('rpm') %}
+Depends: iputils
+{%- endif %}
+
+Tests: authoritative-server
+Depends: findutils
diff --git a/distro/tests/kdig b/distro/tests/kdig
new file mode 100755
index 0000000..f1dbe5a
--- /dev/null
+++ b/distro/tests/kdig
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+set -e
+
+# Skip the test if no internet access
+ping -c1 1.1.1.1 2>&1 || exit 77
+
+expected=198.41.0.4
+answer=$(kdig +short +tls-ca @1.1.1.1 -q a.root-servers.net. -t A 2>&1 || true)
+
+if [ "$answer" != "$expected" ]; then
+ printf "expected: %s\ngot: %s\n" "$expected" "$answer" >&2
+ kdig -d +tls-ca @1.1.1.1 -q a.root-servers.net. -t A
+fi
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 0000000..47fba85
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,226 @@
+MANPAGES_IN = \
+ man/knot.conf.5in \
+ man/knotc.8in \
+ man/knotd.8in \
+ man/kcatalogprint.8in \
+ man/keymgr.8in \
+ man/kjournalprint.8in \
+ man/kdig.1in \
+ man/khost.1in \
+ man/knsupdate.1in \
+ man/knsec3hash.1in \
+ man/kzonecheck.1in \
+ man/kzonesign.1in \
+ man/kxdpgun.8in
+
+MANPAGES_RST = \
+ reference.rst \
+ man_knotc.rst \
+ man_knotd.rst \
+ man_kcatalogprint.rst \
+ man_keymgr.rst \
+ man_kjournalprint.rst \
+ man_kdig.rst \
+ man_khost.rst \
+ man_knsupdate.rst \
+ man_knsec3hash.rst \
+ man_kzonecheck.rst \
+ man_kzonesign.rst \
+ man_kxdpgun.rst
+
+EXTRA_DIST = \
+ conf.py \
+ \
+ appendices.rst \
+ configuration.rst \
+ index.rst \
+ installation.rst \
+ introduction.rst \
+ migration.rst \
+ modules.rst.in \
+ operation.rst \
+ reference.rst \
+ requirements.rst \
+ troubleshooting.rst \
+ utilities.rst \
+ \
+ $(MANPAGES_IN) \
+ $(MANPAGES_RST) \
+ \
+ logo.pdf \
+ logo.svg \
+ \
+ theme_html
+
+SPHINX_V = $(SPHINX_V_@AM_V@)
+SPHINX_V_ = $(SPHINX_V_@AM_DEFAULT_V@)
+SPHINX_V_0 = -q
+SPHINX_V_1 = -n
+
+AM_V_SPHINX = $(AM_V_SPHINX_@AM_V@)
+AM_V_SPHINX_ = $(AM_V_SPHINX_@AM_DEFAULT_V@)
+AM_V_SPHINX_0 = @echo " SPHINX $@";
+
+SPHINXBUILDDIR = $(builddir)/_build
+
+_SPHINXOPTS = -c $(srcdir) \
+ -a \
+ $(SPHINX_V)
+
+ALLSPHINXOPTS = $(_SPHINXOPTS) \
+ -D version="$(VERSION)" \
+ -D today="$(RELEASE_DATE)" \
+ -D release="$(VERSION)" \
+ $(SPHINXOPTS) \
+ $(srcdir)
+
+man_SPHINXOPTS = $(_SPHINXOPTS) \
+ -D version="@""VERSION@" \
+ -D today="@""RELEASE_DATE@" \
+ -D release="@""VERSION@" \
+ $(SPHINXOPTS) \
+ $(srcdir)
+
+.PHONY: html-local singlehtml pdf-local info-local epub man install-html-local install-singlehtml install-pdf-local install-info-local install-epub
+
+man_MANS =
+
+if HAVE_DAEMON
+man_MANS += \
+ man/knot.conf.5 \
+ man/knotc.8 \
+ man/knotd.8
+endif # HAVE_DAEMON
+
+if HAVE_UTILS
+if HAVE_DAEMON
+man_MANS += \
+ man/kcatalogprint.8 \
+ man/keymgr.8 \
+ man/kjournalprint.8 \
+ man/kzonecheck.1 \
+ man/kzonesign.1
+endif # HAVE_DAEMON
+
+man_MANS += \
+ man/kdig.1 \
+ man/khost.1 \
+ man/knsupdate.1 \
+ man/knsec3hash.1
+
+if ENABLE_XDP
+man_MANS += man/kxdpgun.8
+endif # ENABLE_XDP
+endif # HAVE_UTILS
+
+man/knot.conf.5: man/knot.conf.5in
+man/knotc.8: man/knotc.8in
+man/knotd.8: man/knotd.8in
+man/kcatalogprint.8: man/kcatalogprint.8in
+man/keymgr.8: man/keymgr.8in
+man/kjournalprint.8: man/kjournalprint.8in
+man/kdig.1: man/kdig.1in
+man/khost.1: man/khost.1in
+man/knsupdate.1: man/knsupdate.1in
+man/knsec3hash.1: man/knsec3hash.1in
+man/kzonecheck.1: man/kzonecheck.1in
+man/kzonesign.1: man/kzonesign.1in
+man/kxdpgun.8: man/kxdpgun.8in
+
+man_SUBST = $(AM_V_GEN)mkdir -p man; \
+ sed -e 's,[@]VERSION@,$(VERSION),' \
+ -e 's,[@]RELEASE_DATE@,$(RELEASE_DATE),' \
+ -e 's,[@]config_dir@,$(config_dir),' \
+ -e 's,[@]storage_dir@,$(storage_dir),' \
+ -e 's,[@]run_dir@,$(run_dir),' \
+ -e 's,[@]conf_mapsize@,$(conf_mapsize),' \
+ $< > $@
+
+.1in.1:
+ $(man_SUBST)
+
+.5in.5:
+ $(man_SUBST)
+
+.8in.8:
+ $(man_SUBST)
+
+if HAVE_DOCS
+
+if HAVE_SPHINXBUILD
+html-local:
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b html -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/html
+ @echo "The HTML documentation has been built in $(SPHINXBUILDDIR)/html/"
+
+install-html-local:
+ $(INSTALL) -d $(DESTDIR)/$(docdir) $(DESTDIR)/$(docdir)/_static $(DESTDIR)/$(docdir)/_sources
+ $(INSTALL) -D $(SPHINXBUILDDIR)/html/*.html $(DESTDIR)/$(docdir)/
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/html/_sources/* $(DESTDIR)/$(docdir)/_sources/
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/html/_static/* $(DESTDIR)/$(docdir)/_static/
+
+singlehtml:
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b singlehtml -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/singlehtml
+ @echo "The single HTML documentation has been built in $(SPHINXBUILDDIR)/singlehtml/"
+
+install-singlehtml: singlehtml
+ $(INSTALL) -d $(DESTDIR)/$(docdir) $(DESTDIR)/$(docdir)/_static
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/singlehtml/*.html $(DESTDIR)/$(docdir)/
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/singlehtml/_static/* $(DESTDIR)/$(docdir)/_static/
+
+epub:
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b epub -A today=$(RELEASE_DATE) -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/epub
+ @echo "The EPUB documentation has been built in $(SPHINXBUILDDIR)/epub/"
+
+install-epub:
+ $(INSTALL) -d $(DESTDIR)/$(docdir)
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/epub/KnotDNS.epub $(DESTDIR)/$(docdir)/
+
+if HAVE_PDFLATEX
+pdf-local:
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b latex -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/latex
+ $(MAKE) -C $(SPHINXBUILDDIR)/latex all-pdf
+ @echo "The PDF documentation has been built in $(SPHINXBUILDDIR)/latex/"
+
+install-pdf-local:
+ $(INSTALL) -d $(DESTDIR)/$(docdir)
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/latex/KnotDNS.pdf $(DESTDIR)/$(docdir)/
+
+else
+pdf-local install-pdf-local:
+ @echo "Install 'pdflatex' and re-run configure to be able to generate PDF documentation!"
+endif # HAVE_PDFLATEX
+
+if HAVE_MAKEINFO
+info-local:
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b texinfo -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/texinfo
+ $(MAKE) -C $(SPHINXBUILDDIR)/texinfo info
+ @echo "The Info pages have been built in $(SPHINXBUILDDIR)/texinfo/"
+
+install-info-local:
+ $(INSTALL) -d $(DESTDIR)/$(infodir)
+ $(INSTALL_DATA) $(SPHINXBUILDDIR)/texinfo/knot.info $(DESTDIR)/$(infodir)/
+
+else
+info-local install-info-local:
+ @echo "Install 'texinfo' and re-run configure to be able to generate Info pages!"
+endif # HAVE_MAKEINFO
+
+.NOTPARALLEL: man
+man: $(man_MANS)
+$(MANPAGES_IN): $(MANPAGES_RST)
+ $(AM_V_SPHINX)$(SPHINXBUILD) -b man -d $(SPHINXBUILDDIR)/doctrees $(man_SPHINXOPTS) $(SPHINXBUILDDIR)/man
+ @mkdir -p $(srcdir)/man
+ @for f in $(SPHINXBUILDDIR)/man/*; do \
+ sed -e '/^\.TP$$/ {' -e 'n' -e 's/^\.B //' -e '}' "$$f" > "$(srcdir)/man/$$(basename $$f)in"; \
+ done
+
+else
+html-local singlehtml pdf-local info-local epub man install-html-local install-singlehtml install-pdf-local install-info-local install-epub:
+ @echo "Install 'sphinx-build' and re-run configure to be able to generate documentation!"
+endif # HAVE_SPHINXBUILD
+
+endif # HAVE_DOCS
+
+clean-local:
+ -rm -rf $(SPHINXBUILDDIR)
+ -rm -f man/*.1 man/*.5 man/*.8
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644
index 0000000..f3f55ad
--- /dev/null
+++ b/doc/Makefile.in
@@ -0,0 +1,918 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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@
+@HAVE_DAEMON_TRUE@am__append_1 = \
+@HAVE_DAEMON_TRUE@ man/knot.conf.5 \
+@HAVE_DAEMON_TRUE@ man/knotc.8 \
+@HAVE_DAEMON_TRUE@ man/knotd.8
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_2 = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ man/kcatalogprint.8 \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ man/keymgr.8 \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ man/kjournalprint.8 \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ man/kzonecheck.1 \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ man/kzonesign.1
+
+@HAVE_UTILS_TRUE@am__append_3 = \
+@HAVE_UTILS_TRUE@ man/kdig.1 \
+@HAVE_UTILS_TRUE@ man/khost.1 \
+@HAVE_UTILS_TRUE@ man/knsupdate.1 \
+@HAVE_UTILS_TRUE@ man/knsec3hash.1
+
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_4 = man/kxdpgun.8
+subdir = doc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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 = modules.rst
+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__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
+am__installdirs = "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man8dir)"
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/modules.rst.in
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+MANPAGES_IN = \
+ man/knot.conf.5in \
+ man/knotc.8in \
+ man/knotd.8in \
+ man/kcatalogprint.8in \
+ man/keymgr.8in \
+ man/kjournalprint.8in \
+ man/kdig.1in \
+ man/khost.1in \
+ man/knsupdate.1in \
+ man/knsec3hash.1in \
+ man/kzonecheck.1in \
+ man/kzonesign.1in \
+ man/kxdpgun.8in
+
+MANPAGES_RST = \
+ reference.rst \
+ man_knotc.rst \
+ man_knotd.rst \
+ man_kcatalogprint.rst \
+ man_keymgr.rst \
+ man_kjournalprint.rst \
+ man_kdig.rst \
+ man_khost.rst \
+ man_knsupdate.rst \
+ man_knsec3hash.rst \
+ man_kzonecheck.rst \
+ man_kzonesign.rst \
+ man_kxdpgun.rst
+
+EXTRA_DIST = \
+ conf.py \
+ \
+ appendices.rst \
+ configuration.rst \
+ index.rst \
+ installation.rst \
+ introduction.rst \
+ migration.rst \
+ modules.rst.in \
+ operation.rst \
+ reference.rst \
+ requirements.rst \
+ troubleshooting.rst \
+ utilities.rst \
+ \
+ $(MANPAGES_IN) \
+ $(MANPAGES_RST) \
+ \
+ logo.pdf \
+ logo.svg \
+ \
+ theme_html
+
+SPHINX_V = $(SPHINX_V_@AM_V@)
+SPHINX_V_ = $(SPHINX_V_@AM_DEFAULT_V@)
+SPHINX_V_0 = -q
+SPHINX_V_1 = -n
+AM_V_SPHINX = $(AM_V_SPHINX_@AM_V@)
+AM_V_SPHINX_ = $(AM_V_SPHINX_@AM_DEFAULT_V@)
+AM_V_SPHINX_0 = @echo " SPHINX $@";
+SPHINXBUILDDIR = $(builddir)/_build
+_SPHINXOPTS = -c $(srcdir) \
+ -a \
+ $(SPHINX_V)
+
+ALLSPHINXOPTS = $(_SPHINXOPTS) \
+ -D version="$(VERSION)" \
+ -D today="$(RELEASE_DATE)" \
+ -D release="$(VERSION)" \
+ $(SPHINXOPTS) \
+ $(srcdir)
+
+man_SPHINXOPTS = $(_SPHINXOPTS) \
+ -D version="@""VERSION@" \
+ -D today="@""RELEASE_DATE@" \
+ -D release="@""VERSION@" \
+ $(SPHINXOPTS) \
+ $(srcdir)
+
+man_MANS = $(am__append_1) $(am__append_2) $(am__append_3) \
+ $(am__append_4)
+man_SUBST = $(AM_V_GEN)mkdir -p man; \
+ sed -e 's,[@]VERSION@,$(VERSION),' \
+ -e 's,[@]RELEASE_DATE@,$(RELEASE_DATE),' \
+ -e 's,[@]config_dir@,$(config_dir),' \
+ -e 's,[@]storage_dir@,$(storage_dir),' \
+ -e 's,[@]run_dir@,$(run_dir),' \
+ -e 's,[@]conf_mapsize@,$(conf_mapsize),' \
+ $< > $@
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .1 .1in .5 .5in .8 .8in
+$(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 doc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign doc/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):
+modules.rst: $(top_builddir)/config.status $(srcdir)/modules.rst.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man1: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ 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=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | 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-man5: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || 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 '/\.5[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,^[^5][0-9a-z]*$$,5,;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)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$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)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.5[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || 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 '/\.8[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,^[^8][0-9a-z]*$$,8,;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)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$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)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.8[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+check: check-am
+all-am: Makefile $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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:
+
+clean-generic:
+
+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."
+@HAVE_DOCS_FALSE@install-pdf-local:
+@HAVE_PDFLATEX_FALSE@install-pdf-local:
+@HAVE_SPHINXBUILD_FALSE@install-pdf-local:
+@HAVE_DOCS_FALSE@install-html-local:
+@HAVE_SPHINXBUILD_FALSE@install-html-local:
+@HAVE_DOCS_FALSE@info-local:
+@HAVE_MAKEINFO_FALSE@info-local:
+@HAVE_SPHINXBUILD_FALSE@info-local:
+@HAVE_DOCS_FALSE@html-local:
+@HAVE_SPHINXBUILD_FALSE@html-local:
+@HAVE_DOCS_FALSE@install-info-local:
+@HAVE_MAKEINFO_FALSE@install-info-local:
+@HAVE_SPHINXBUILD_FALSE@install-info-local:
+@HAVE_DOCS_FALSE@pdf-local:
+@HAVE_PDFLATEX_FALSE@pdf-local:
+@HAVE_SPHINXBUILD_FALSE@pdf-local:
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am: html-local
+
+info: info-am
+
+info-am: info-local
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am: install-html-local
+
+install-info: install-info-am
+
+install-info-am: install-info-local
+
+install-man: install-man1 install-man5 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am: install-pdf-local
+
+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: pdf-local
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man
+
+uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am html-local \
+ info info-am info-local install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am \
+ install-html-local install-info install-info-am \
+ install-info-local install-man install-man1 install-man5 \
+ install-man8 install-pdf install-pdf-am install-pdf-local \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am pdf-local ps ps-am tags-am \
+ uninstall uninstall-am uninstall-man uninstall-man1 \
+ uninstall-man5 uninstall-man8
+
+.PRECIOUS: Makefile
+
+
+.PHONY: html-local singlehtml pdf-local info-local epub man install-html-local install-singlehtml install-pdf-local install-info-local install-epub
+
+man/knot.conf.5: man/knot.conf.5in
+man/knotc.8: man/knotc.8in
+man/knotd.8: man/knotd.8in
+man/kcatalogprint.8: man/kcatalogprint.8in
+man/keymgr.8: man/keymgr.8in
+man/kjournalprint.8: man/kjournalprint.8in
+man/kdig.1: man/kdig.1in
+man/khost.1: man/khost.1in
+man/knsupdate.1: man/knsupdate.1in
+man/knsec3hash.1: man/knsec3hash.1in
+man/kzonecheck.1: man/kzonecheck.1in
+man/kzonesign.1: man/kzonesign.1in
+man/kxdpgun.8: man/kxdpgun.8in
+
+.1in.1:
+ $(man_SUBST)
+
+.5in.5:
+ $(man_SUBST)
+
+.8in.8:
+ $(man_SUBST)
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@html-local:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b html -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/html
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ @echo "The HTML documentation has been built in $(SPHINXBUILDDIR)/html/"
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@install-html-local:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -d $(DESTDIR)/$(docdir) $(DESTDIR)/$(docdir)/_static $(DESTDIR)/$(docdir)/_sources
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -D $(SPHINXBUILDDIR)/html/*.html $(DESTDIR)/$(docdir)/
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/html/_sources/* $(DESTDIR)/$(docdir)/_sources/
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/html/_static/* $(DESTDIR)/$(docdir)/_static/
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@singlehtml:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b singlehtml -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/singlehtml
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ @echo "The single HTML documentation has been built in $(SPHINXBUILDDIR)/singlehtml/"
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@install-singlehtml: singlehtml
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -d $(DESTDIR)/$(docdir) $(DESTDIR)/$(docdir)/_static
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/singlehtml/*.html $(DESTDIR)/$(docdir)/
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/singlehtml/_static/* $(DESTDIR)/$(docdir)/_static/
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@epub:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b epub -A today=$(RELEASE_DATE) -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/epub
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ @echo "The EPUB documentation has been built in $(SPHINXBUILDDIR)/epub/"
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@install-epub:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -d $(DESTDIR)/$(docdir)
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/epub/KnotDNS.epub $(DESTDIR)/$(docdir)/
+
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@pdf-local:
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b latex -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/latex
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(MAKE) -C $(SPHINXBUILDDIR)/latex all-pdf
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@ @echo "The PDF documentation has been built in $(SPHINXBUILDDIR)/latex/"
+
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@install-pdf-local:
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -d $(DESTDIR)/$(docdir)
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/latex/KnotDNS.pdf $(DESTDIR)/$(docdir)/
+
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_FALSE@@HAVE_SPHINXBUILD_TRUE@pdf-local install-pdf-local:
+@HAVE_DOCS_TRUE@@HAVE_PDFLATEX_FALSE@@HAVE_SPHINXBUILD_TRUE@ @echo "Install 'pdflatex' and re-run configure to be able to generate PDF documentation!"
+
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@info-local:
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b texinfo -d $(SPHINXBUILDDIR)/doctrees $(ALLSPHINXOPTS) $(SPHINXBUILDDIR)/texinfo
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(MAKE) -C $(SPHINXBUILDDIR)/texinfo info
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@ @echo "The Info pages have been built in $(SPHINXBUILDDIR)/texinfo/"
+
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@install-info-local:
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL) -d $(DESTDIR)/$(infodir)
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(INSTALL_DATA) $(SPHINXBUILDDIR)/texinfo/knot.info $(DESTDIR)/$(infodir)/
+
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_FALSE@@HAVE_SPHINXBUILD_TRUE@info-local install-info-local:
+@HAVE_DOCS_TRUE@@HAVE_MAKEINFO_FALSE@@HAVE_SPHINXBUILD_TRUE@ @echo "Install 'texinfo' and re-run configure to be able to generate Info pages!"
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@.NOTPARALLEL: man
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@man: $(man_MANS)
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@$(MANPAGES_IN): $(MANPAGES_RST)
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ $(AM_V_SPHINX)$(SPHINXBUILD) -b man -d $(SPHINXBUILDDIR)/doctrees $(man_SPHINXOPTS) $(SPHINXBUILDDIR)/man
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ @mkdir -p $(srcdir)/man
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ @for f in $(SPHINXBUILDDIR)/man/*; do \
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ sed -e '/^\.TP$$/ {' -e 'n' -e 's/^\.B //' -e '}' "$$f" > "$(srcdir)/man/$$(basename $$f)in"; \
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_TRUE@ done
+
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_FALSE@html-local singlehtml pdf-local info-local epub man install-html-local install-singlehtml install-pdf-local install-info-local install-epub:
+@HAVE_DOCS_TRUE@@HAVE_SPHINXBUILD_FALSE@ @echo "Install 'sphinx-build' and re-run configure to be able to generate documentation!"
+
+clean-local:
+ -rm -rf $(SPHINXBUILDDIR)
+ -rm -f man/*.1 man/*.5 man/*.8
+
+# 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/doc/appendices.rst b/doc/appendices.rst
new file mode 100644
index 0000000..309bb20
--- /dev/null
+++ b/doc/appendices.rst
@@ -0,0 +1,126 @@
+.. highlight:: none
+.. _Appendices:
+
+**********
+Appendices
+**********
+
+.. _compatible_pkcs11_devices:
+
+Compatible PKCS #11 Devices
+===========================
+
+This section has informative character. Knot DNS has been tested with several
+devices which claim to support PKCS #11 interface. The following table
+indicates which algorithms and operations have been observed to work. Please
+notice minimal GnuTLS library version required for particular algorithm
+support.
+
+.. |yes| replace:: **yes**
+.. |no| replace:: no
+.. |unknown| replace:: ?
+
+.. list-table::
+ :header-rows: 1
+ :stub-columns: 1
+
+ * -
+ - Key generate
+ - Key import
+ - ED25519 256-bit
+ - ECDSA 256-bit
+ - ECDSA 384-bit
+ - RSA 1024-bit
+ - RSA 2048-bit
+ - RSA 4096-bit
+ * - `Feitian ePass 2003 <https://www.ftsafe.com/Products/PKI/Standard>`_
+ - |yes|
+ - |no|
+ - |no|
+ - |no|
+ - |no|
+ - |yes|
+ - |yes|
+ - |no|
+ * - `SafeNet Network HSM (Luna SA 4) <https://safenet.gemalto.com/data-encryption/hardware-security-modules-hsms/luna-hsms-key-management/luna-sa-network-hsm/>`_
+ - |yes|
+ - |no|
+ - |no|
+ - |no|
+ - |no|
+ - |yes|
+ - |yes|
+ - |yes|
+ * - `SoftHSM 2.0 <https://www.opendnssec.org/softhsm/>`_ [#fn-softhsm]_
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ * - `Trustway Proteccio NetHSM <https://atos.net/en/solutions/cyber-security/data-protection-and-governance/hardware-security-module-trustway-proteccio-nethsm>`_
+ - |yes|
+ - ECDSA only
+ - |no|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ * - `Ultra Electronics CIS Keyper Plus (Model 9860-2) <https://www.ultra.group/our-business-units/intelligence-communications/cyber/key-management/#acc-keyperplus>`_
+ - |yes|
+ - RSA only
+ - |no|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ * - `Utimaco SecurityServer (V4) <https://hsm.utimaco.com/products-hardware-security-modules/general-purpose-hsm/securityserver-cse/>`_ [#fn-utimaco]_
+ - |yes|
+ - |yes|
+ - |no|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+ - |yes|
+
+.. in progress: key ID checks have to be disabled in code
+ * - `Yubikey NEO <https://www.yubico.com/products/yubikey-hardware/yubikey-neo/>`_
+ - |no|
+ - |no|
+ - |no|
+ - |yes|
+ - |no|
+ - |yes|
+ - |yes|
+ - |no|
+
+.. [#fn-softhsm] Algorithms supported depend on support in OpenSSL on which SoftHSM relies.
+ A command similar to the following may be used to verify what algorithms are supported:
+ ``$ pkcs11-tool --modul /usr/lib64/pkcs11/libsofthsm2.so -M``.
+.. [#fn-utimaco] Requires setting the number of background workers to 1!
+
+The following table summarizes supported DNSSEC algorithm numbers and minimal
+GnuTLS library version required. Any algorithm may work with older library,
+however the supported operations may be limited (e.g. private key import).
+
+.. list-table::
+ :header-rows: 1
+ :stub-columns: 1
+
+ * -
+ - `Numbers <https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml#dns-sec-alg-numbers-1>`_
+ - GnuTLS version
+ * - ED25519
+ - 15
+ - 3.6.0 or newer
+ * - ECDSA
+ - 13, 14
+ - 3.4.8 or newer
+ * - RSA
+ - 5, 7, 8, 10
+ - 3.4.6 or newer
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..d814073
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+#
+# Knot DNS documentation build configuration file, created by
+# sphinx-quickstart on Tue Apr 15 13:48:28 2014.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os, time, logging
+
+sys.setrecursionlimit(1500)
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = []
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = 'Knot DNS'
+copyright_year = 2023
+current_year = time.localtime().tm_year
+if current_year > copyright_year:
+ logging.warning('Copyright year is %d, but current year is %d.'%(copyright_year, current_year))
+ logging.warning('Maybe you should update copyright_year in doc/conf.py?')
+copyright = u'Copyright 2010–%d, CZ.NIC, z.s.p.o.' % copyright_year
+author = 'CZ.NIC Labs <https://www.knot-dns.cz>'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+#version = ''
+# The full version, including alpha/beta/rc tags.
+#release = ''
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = False
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build', 'modules']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'theme_html'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['.']
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+html_logo = 'logo.svg'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, smartquotes will be used to convert quotes and dashes to
+# typographically correct entities.
+smartquotes = False
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+html_domain_indices = False
+
+# If false, no index is generated.
+html_use_index = False
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'KnotDNSdoc'
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+'papersize': 'a4paper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+
+# No empty pages between chapters
+'classoptions': ',openany,oneside',
+
+# Language preferences
+'babel': '\\usepackage[english]{babel}',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'KnotDNS.tex', 'Knot DNS Documentation', copyright, 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+latex_logo = 'logo.pdf'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+latex_domain_indices = False
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('reference', 'knot.conf', 'Knot DNS configuration file', author, 5),
+ ('man_knotc', 'knotc', 'Knot DNS control utility', author, 8),
+ ('man_knotd', 'knotd', 'Knot DNS server daemon', author, 8),
+ ('man_kcatalogprint', 'kcatalogprint', 'Knot DNS catalog print utility', author, 8),
+ ('man_keymgr', 'keymgr', 'Knot DNS key management utility', author, 8),
+ ('man_kjournalprint', 'kjournalprint', 'Knot DNS journal print utility', author, 8),
+ ('man_kdig', 'kdig', 'Advanced DNS lookup utility', author, 1),
+ ('man_khost', 'khost', 'Simple DNS lookup utility', author, 1),
+ ('man_knsec3hash', 'knsec3hash', 'Simple utility to compute NSEC3 hash', author, 1),
+ ('man_knsupdate', 'knsupdate', 'Dynamic DNS update utility', author, 1),
+ ('man_kzonecheck', 'kzonecheck', 'Knot DNS zone check tool', author, 1),
+ ('man_kzonesign', 'kzonesign', 'DNSSEC signing utility', author, 1),
+ ('man_kxdpgun', 'kxdpgun', 'XDP-powered DNS benchmarking tool', author, 8),
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'knot', 'Knot DNS Documentation', author,
+ 'KnotDNS', 'Knot Authoritative DNS Server', 'Miscellaneous')
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# -- Options for Epub output ----------------------------------------------
+
+epub_title = project
+epub_author = author
+epub_publisher = author
+epub_copyright = copyright
+
+epub_theme = 'theme_epub'
+epub_cover = ('_static/logo.svg', 'epub-cover.html')
+epub_exclude_files = ['epub-cover.xhtml']
diff --git a/doc/configuration.rst b/doc/configuration.rst
new file mode 100644
index 0000000..d91a117
--- /dev/null
+++ b/doc/configuration.rst
@@ -0,0 +1,905 @@
+.. highlight:: none
+.. _Configuration:
+
+*************
+Configuration
+*************
+
+Simple configuration
+====================
+
+The following example presents a simple configuration file
+which can be used as a base for your Knot DNS setup::
+
+ # Example of a very simple Knot DNS configuration.
+
+ server:
+ listen: 0.0.0.0@53
+ listen: ::@53
+
+ zone:
+ - domain: example.com
+ storage: /var/lib/knot/zones/
+ file: example.com.zone
+
+ log:
+ - target: syslog
+ any: info
+
+Now let's walk through this configuration step by step:
+
+- The :ref:`server_listen` statement in the :ref:`server section<Server section>`
+ defines where the server will listen for incoming connections.
+ We have defined the server to listen on all available IPv4 and IPv6 addresses,
+ all on port 53.
+- The :ref:`zone section` defines the zones that the server will
+ serve. In this case, we defined one zone named *example.com* which is stored
+ in the zone file :file:`/var/lib/knot/zones/example.com.zone`.
+- The :ref:`log section` defines the log facilities for
+ the server. In this example, we told Knot DNS to send its log messages with
+ the severity ``info`` or more serious to the syslog (or systemd journal).
+
+For detailed description of all configuration items see
+:ref:`Configuration Reference`.
+
+Zone templates
+==============
+
+A zone template allows a single zone configuration to be shared among several
+zones. There is no inheritance between templates; they are exclusive. The
+``default`` template identifier is reserved for the default template::
+
+ template:
+ - id: default
+ storage: /var/lib/knot/master
+ semantic-checks: on
+
+ - id: signed
+ storage: /var/lib/knot/signed
+ dnssec-signing: on
+ semantic-checks: on
+ master: [master1, master2]
+
+ - id: slave
+ storage: /var/lib/knot/slave
+
+ zone:
+ - domain: example1.com # Uses default template
+
+ - domain: example2.com # Uses default template
+ semantic-checks: off # Override default settings
+
+ - domain: example.cz
+ template: signed
+ master: master3 # Override masters to just master3
+
+ - domain: example1.eu
+ template: slave
+ master: master1
+
+ - domain: example2.eu
+ template: slave
+ master: master2
+
+.. NOTE::
+ Each template option can be explicitly overridden in zone-specific configuration.
+
+.. _ACL:
+
+Access control list (ACL)
+=========================
+
+Some types of incoming DNS requests must be authorized before they can be
+processed by the server. A zone can have configured :ref:`zone_acl` which is
+a sequence of :ref:`rules <ACL section>` describing what requests are authorized.
+By default if :ref:`automatic ACL <server_automatic-acl>` is not enabled, all requests,
+which require authorization, are denied.
+
+Every ACL rule can allow or deny one or more request types based on the
+source IP address, network subnet, or address range and/or if the request is
+secured by a given TSIG key. See :doc:`keymgr -t<man_keymgr>` on how
+to generate a TSIG key.
+
+If there are multiple ACL rules assigned to a zone, they are applied in the
+specified order of the :ref:`zone_acl` configuration. The first rule that matches
+the given request is applied and the remaining rules are ignored. Some examples::
+
+ acl:
+ - id: address_rule
+ address: [2001:db8::1, 192.168.2.0/24]
+ action: transfer
+
+ - id: deny_rule
+ address: 192.168.2.100
+ action: transfer
+ deny: on
+
+ zone:
+ - domain: acl1.example.com
+ acl: [deny_rule, address_rule] # Allow some addresses with an exception
+
+::
+
+ key:
+ - id: key1 # The real TSIG key name
+ algorithm: hmac-sha256
+ secret: 4Tc0K1QkcMCs7cOW2LuSWnxQY0qysdvsZlSb4yTN9pA=
+
+ acl:
+ - id: deny_all
+ address: 192.168.3.0/24
+ deny: on # No action specified and deny on implies denial of all actions
+
+ - id: key_rule
+ key: key1 # Access based just on TSIG key
+ action: [transfer, notify]
+
+ zone:
+ - domain: acl2.example.com
+ acl: [deny_all, key_rule] # Allow with the TSIG except for the subnet
+
+In the case of dynamic DNS updates, some additional conditions may be specified
+for more granular filtering. See more in the section :ref:`Restricting dynamic updates`.
+
+.. NOTE::
+ If more conditions (address ranges and/or a key)
+ are given in a single ACL rule, all of them have to be satisfied for the rule to match.
+
+.. TIP::
+ In order to restrict regular DNS queries, use module :ref:`queryacl<mod-queryacl>`.
+
+Secondary (slave) zone
+======================
+
+Knot DNS doesn't strictly differ between primary (formerly known as master)
+and secondary (formerly known as slave) zones. The only requirement for a secondary
+zone is to have a :ref:`zone_master` statement set. For effective zone synchronization,
+incoming zone change notifications (NOTIFY), which require authorization, can be
+enabled using :ref:`automatic ACL <server_automatic-acl>` or :ref:`explicit ACL <zone_acl>`
+configuration. Optional transaction authentication (TSIG) is supported for both
+zone transfers and zone notifications::
+
+ server:
+ automatic-acl: on # Enabled automatic ACL
+
+ key:
+ - id: xfr_notify_key # Common TSIG key for XFR an NOTIFY
+ algorithm: hmac-sha256
+ secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
+
+ remote:
+ - id: primary
+ address: [2001:DB8:1::1, 192.168.1.1] # Primary server IP addresses
+ # via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses (optional)
+ key: xfr_notify_key # TSIG key (optional)
+
+ zone:
+ - domain: example.com
+ master: primary # Primary remote(s)
+
+An example of explicit ACL with different TSIG keys for zone transfers
+and notifications::
+
+ key:
+ - id: notify_key # TSIG key for NOTIFY
+ algorithm: hmac-sha256
+ secret: uBbhV4aeSS4fPd+wF2ZIn5pxOMF35xEtdq2ibi2hHEQ=
+
+ - id: xfr_key # TSIG key for XFR
+ algorithm: hmac-sha256
+ secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
+
+ remote:
+ - id: primary
+ address: [2001:DB8:1::1, 192.168.1.1] # Primary server IP addresses
+ # via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses if needed
+ key: xfr_key # Optional TSIG key
+
+ acl:
+ - id: notify_from_primary # ACL rule for NOTIFY from primary
+ address: [2001:DB8:1::1, 192.168.1.1] # Primary addresses (optional)
+ key: notify_key # TSIG key (optional)
+ action: notify
+
+ zone:
+ - domain: example.com
+ master: primary # Primary remote(s)
+ acl: notify_from_primary # Explicit ACL(s)
+
+Note that the :ref:`zone_master` option accepts a list of remotes, which are
+queried for a zone refresh sequentially in the specified order. When the server
+receives a zone change notification from a listed remote, only that remote is
+used for a subsequent zone transfer.
+
+.. NOTE::
+ When transferring a lot of zones, the server may easily get into a state
+ where all available ports are in the TIME_WAIT state, thus transfers
+ cease until the operating system closes the ports for good. There are
+ several ways to work around this:
+
+ * Allow reusing of ports in TIME_WAIT (sysctl -w net.ipv4.tcp_tw_reuse=1)
+ * Shorten TIME_WAIT timeout (tcp_fin_timeout)
+ * Increase available local port count
+
+Primary (master) zone
+=====================
+
+A zone is considered primary if it doesn't have :ref:`zone_master` set. As
+outgoing zone transfers (XFR) require authorization, it must be enabled
+using :ref:`automatic ACL <server_automatic-acl>` or :ref:`explicit ACL <zone_acl>`
+configuration. Outgoing zone change notifications (NOTIFY) to remotes can be
+set by configuring :ref:`zone_notify`. Transaction authentication
+(TSIG) is supported for both zone transfers and zone notifications::
+
+ server:
+ automatic-acl: on # Enabled automatic ACL
+
+ key:
+ - id: xfr_notify_key # Common TSIG key for XFR an NOTIFY
+ algorithm: hmac-sha256
+ secret: VFRejzw8h4M7mb0xZKRFiZAfhhd1eDGybjqHr2FV3vc=
+
+ remote:
+ - id: secondary
+ address: [2001:DB8:1::1, 192.168.1.1] # Secondary server IP addresses
+ # via: [2001:DB8:2::1, 10.0.0.1] # Local source addresses (optional)
+ key: xfr_notify_key # TSIG key (optional)
+
+ acl:
+ - id: local_xfr # Allow XFR to localhost without TSIG
+ address: [::1, 127.0.0.1]
+ action: transfer
+
+ zone:
+ - domain: example.com
+ notify: secondary # Secondary remote(s)
+ acl: local_xfr # Explicit ACL for local XFR
+
+Note that the :ref:`zone_notify` option accepts a list of remotes, which are
+all notified sequentially in the specified order.
+
+A secondary zone may serve as a primary zone for a different set of remotes
+at the same time.
+
+Dynamic updates
+===============
+
+Dynamic updates for the zone are allowed via proper ACL rule with the
+``update`` action. If the zone is configured as a secondary and a DNS update
+message is accepted, the server forwards the message to its primary master.
+The primary master's response is then forwarded back to the originator.
+
+However, if the zone is configured as a primary, the update is accepted and
+processed::
+
+ acl:
+ - id: update_acl
+ address: 192.168.3.0/24
+ action: update
+
+ zone:
+ - domain: example.com
+ acl: update_acl
+
+.. _Restricting dynamic updates:
+
+Restricting dynamic updates
+---------------------------
+
+There are several additional ACL options for dynamic DNS updates which affect
+the request classification based on the update contents.
+
+Updates can be restricted to specific resource record types::
+
+ acl:
+ - id: type_rule
+ action: update
+ update-type: [A, AAAA, MX] # Updated records must match one of the specified types
+
+Another possibility is restriction on the owner name of updated records. The option
+:ref:`acl_update-owner` is used to select the source of domain
+names which are used for the comparison. And the option :ref:`acl_update-owner-match`
+specifies the required relation between the record owner and the reference domain
+names. Example::
+
+ acl:
+ - id: owner_rule1
+ action: update
+ update-owner: name # Updated record owners are restricted by the next conditions
+ update-owner-match: equal # The record owner must exactly match one name from the next list
+ update-owner-name: [foo, bar.] # Reference domain names
+
+.. NOTE::
+ If the specified owner name is non-FQDN (e.g. ``foo``), it's considered relatively
+ to the effective zone name. So it can apply to more zones
+ (e.g. ``foo.example.com.`` or ``foo.example.net.``). Alternatively, if the
+ name is FQDN (e.g. ``bar.``), the rule only applies to this name.
+
+If the reference domain name is the zone name, the following variant can be used::
+
+ acl:
+ - id: owner_rule2
+ action: update
+ update-owner: zone # The reference name is the zone name
+ update-owner-match: sub # Any record owner matches except for the zone name itself
+
+ template:
+ - id: default
+ acl: owner_rule2
+
+ zone:
+ - domain: example.com.
+ - domain: example.net.
+
+The last variant is for the cases where the reference domain name is a TSIG key name,
+which must be used for the transaction security::
+
+ key:
+ - id: example.com # Key names are always considered FQDN
+ ...
+ - id: steve.example.net
+ ...
+ - id: jane.example.net
+ ...
+
+ acl:
+ - id: owner_rule3_com
+ action: update
+ update-owner: key # The reference name is the TSIG key name
+ update-owner-match: sub # The record owner must be a subdomain of the key name
+ key: [example.com] # One common key for updating all non-apex records
+
+ - id: owner_rule3_net
+ action: update
+ update-owner: key # The reference name is the TSIG key name
+ update-owner-match: equal # The record owner must exactly match the used key name
+ key: [steve.example.net, jane.example.net] # Keys for updating specific zone nodes
+
+ zone:
+ - domain: example.com.
+ acl: owner_rule3_com
+ - domain: example.net.
+ acl: owner_rule3_net
+
+.. _dnssec:
+
+Automatic DNSSEC signing
+========================
+
+Knot DNS supports automatic DNSSEC signing of zones. The signing
+can operate in two modes:
+
+1. :ref:`Automatic key management <dnssec-automatic-zsk-management>`.
+ In this mode, the server maintains signing keys. New keys are generated
+ according to assigned policy and are rolled automatically in a safe manner.
+ No zone operator intervention is necessary.
+
+2. :ref:`Manual key management <dnssec-manual-key-management>`.
+ In this mode, the server maintains zone signatures only. The signatures
+ are kept up-to-date and signing keys are rolled according to timing
+ parameters assigned to the keys. The keys must be generated and timing
+ parameters must be assigned by the zone operator.
+
+The DNSSEC signing process maintains some metadata which is stored in the
+:abbr:`KASP (Key And Signature Policy)` database. This database is backed
+by LMDB.
+
+.. WARNING::
+ Make sure to set the KASP database permissions correctly. For manual key
+ management, the database must be *readable* by the server process. For
+ automatic key management, it must be *writeable*. If no HSM is used,
+ the database also contains private key material – don't set the permissions
+ too weak.
+
+.. _dnssec-automatic-zsk-management:
+
+Automatic ZSK management
+------------------------
+
+For automatic ZSK management a signing :ref:`policy<Policy section>` has to
+be configured and assigned to the zone. The policy specifies how the zone
+is signed (i.e. signing algorithm, key size, key lifetime, signature lifetime,
+etc.). If no policy is specified or the ``default`` one is assigned, the
+default signing parameters are used.
+
+A minimal zone configuration may look as follows::
+
+ zone:
+ - domain: myzone.test
+ dnssec-signing: on
+
+With a custom signing policy, the policy section will be added::
+
+ policy:
+ - id: custom_policy
+ signing-threads: 4
+ algorithm: ECDSAP256SHA256
+ zsk-lifetime: 60d
+
+ zone:
+ - domain: myzone.test
+ dnssec-signing: on
+ dnssec-policy: custom_policy
+
+After configuring the server, reload the changes:
+
+.. code-block:: console
+
+ $ knotc reload
+
+The server will generate initial signing keys and sign the zone properly. Check
+the server logs to see whether everything went well.
+
+.. _dnssec-automatic-ksk-management:
+
+Automatic KSK management
+------------------------
+
+For automatic KSK management, first configure ZSK management like above, and use
+additional options in :ref:`policy section <Policy section>`, mostly specifying
+desired (finite) lifetime for KSK: ::
+
+ remote:
+ - id: parent_zone_server
+ address: 192.168.12.1@53
+
+ submission:
+ - id: parent_zone_sbm
+ parent: [parent_zone_server]
+
+ policy:
+ - id: custom_policy
+ signing-threads: 4
+ algorithm: ECDSAP256SHA256
+ zsk-lifetime: 60d
+ ksk-lifetime: 365d
+ ksk-submission: parent_zone_sbm
+
+ zone:
+ - domain: myzone.test
+ dnssec-signing: on
+ dnssec-policy: custom_policy
+
+After the initially-generated KSK reaches its lifetime, new KSK is published and after
+convenience delay the submission is started. The server publishes CDS and CDNSKEY records
+and the user shall propagate them to the parent. The server periodically checks for
+DS at the parent zone and when positive, finishes the rollover.
+
+.. _dnssec-manual-key-management:
+
+Manual key management
+---------------------
+
+For automatic DNSSEC signing with manual key management, a signing policy
+with manual key management flag has to be set::
+
+ policy:
+ - id: manual
+ manual: on
+
+ zone:
+ - domain: myzone.test
+ dnssec-signing: on
+ dnssec-policy: manual
+
+To generate signing keys, use the :doc:`keymgr<man_keymgr>` utility.
+For example, we can use Single-Type Signing:
+
+.. code-block:: console
+
+ $ keymgr myzone.test. generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes
+
+And reload the server. The zone will be signed.
+
+To perform a manual rollover of a key, the timing parameters of the key need
+to be set. Let's roll the key. Generate a new key, but do not activate
+it yet:
+
+.. code-block:: console
+
+ $ keymgr myzone.test. generate algorithm=ECDSAP256SHA256 ksk=yes zsk=yes active=+1d
+
+Take the key ID (or key tag) of the old key and disable it the same time
+the new key gets activated:
+
+.. code-block:: console
+
+ $ keymgr myzone.test. set <old_key_id> retire=+2d remove=+3d
+
+Reload the server again. The new key will be published (i.e. the DNSKEY record
+will be added into the zone). Remember to update the DS record in the
+parent zone to include a reference to the new key. This must happen within one
+day (in this case) including a delay required to propagate the new DS to
+caches.
+
+.. WARNING::
+ If you ever decide to switch from manual key management to automatic key management,
+ note that the automatic key management uses
+ :ref:`policy_zsk-lifetime` and :ref:`policy_ksk-lifetime` policy configuration
+ options to schedule key rollovers and it internally uses timestamps of keys differently
+ than in the manual case. As a consequence it might break if the ``retire`` or ``remove`` timestamps
+ are set for the manually generated keys currently in use. Make sure to set these timestamps
+ to zero using :doc:`keymgr<man_keymgr>`:
+
+ .. code-block:: console
+
+ $ keymgr myzone.test. set <key_id> retire=0 remove=0
+
+ and configure your policy suitably according to :ref:`dnssec-automatic-zsk-management`
+ and :ref:`dnssec-automatic-ksk-management`.
+
+.. _dnssec-signing:
+
+Zone signing
+------------
+
+The signing process consists of the following steps:
+
+#. Processing KASP database events. (e.g. performing a step of a rollover).
+#. Updating the DNSKEY records. The whole DNSKEY set in zone apex is replaced
+ by the keys from the KASP database. Note that keys added into the zone file
+ manually will be removed. To add an extra DNSKEY record into the set, the
+ key must be imported into the KASP database (possibly deactivated).
+#. Fixing the NSEC or NSEC3 chain.
+#. Removing expired signatures, invalid signatures, signatures expiring
+ in a short time, and signatures issued by an unknown key.
+#. Creating missing signatures. Unless the Single-Type Signing Scheme
+ is used, DNSKEY records in a zone apex are signed by KSK keys and
+ all other records are signed by ZSK keys.
+#. Updating and re-signing SOA record.
+
+The signing is initiated on the following occasions:
+
+- Start of the server
+- Zone reload
+- Reaching the signature refresh period
+- Key set changed due to rollover event
+- Received DDNS update
+- Forced zone re-sign via server control interface
+
+On a forced zone re-sign, all signatures in the zone are dropped and recreated.
+
+The ``knotc zone-status`` command can be used to see when the next scheduled
+DNSSEC re-sign will happen.
+
+.. _dnssec-on-slave-signing:
+
+On-secondary (on-slave) signing
+-------------------------------
+
+It is possible to enable automatic DNSSEC zone signing even on a secondary
+server. If enabled, the zone is signed after every AXFR/IXFR transfer
+from primary, so that the secondary always serves a signed up-to-date version
+of the zone.
+
+It is strongly recommended to block any outside access to the primary
+server, so that only the secondary server's signed version of the zone is served.
+
+Enabled on-secondary signing introduces events when the secondary zone changes
+while the primary zone remains unchanged, such as a key rollover or
+refreshing of RRSIG records, which cause inequality of zone SOA serial
+between primary and secondary. The secondary server handles this by saving the
+primary's SOA serial in a special variable inside KASP DB and appropriately
+modifying AXFR/IXFR queries/answers to keep the communication with
+primary server consistent while applying the changes with a different serial.
+
+.. _catalog-zones:
+
+Catalog zones
+=============
+
+Catalog zones are a concept whereby a list of zones to be configured is maintained
+as contents of a separate, special zone. This approach has the benefit of simple
+propagation of a zone list to secondary servers, especially when the list is
+frequently updated. Currently, catalog zones are described in this `Internet Draft
+<https://tools.ietf.org/html/draft-ietf-dnsop-dns-catalog-zones>`_.
+
+Terminology first. *Catalog zone* is a meta-zone which shall not be a part
+of the DNS tree, but it contains information about the set of member zones and
+is transferable to secondary servers using common AXFR/IXFR techniques.
+A *catalog-member zone* (or just *member zone*) is a zone based on
+information from the catalog zone and not from configuration file/database.
+*Member properties* are some additional information related to each member zone,
+also distributed with the catalog zone.
+
+A catalog zone is handled almost in the same way as a regular zone:
+It can be configured using all the standard options (but for example
+DNSSEC signing is useless as the zone won't be queried by clients), including primary/secondary configuration
+and ACLs. A catalog zone is indicated by setting the option
+:ref:`zone_catalog-role`. The difference is that standard DNS
+queries to a catalog zone are answered with REFUSED as though the zone
+doesn't exist, unless querying over TCP from an address with transfers enabled
+by ACL. The name of the catalog zone is arbitrary. It's possible to configure
+multiple catalog zones.
+
+.. WARNING::
+ Don't choose a name for a catalog zone below a name of any other
+ existing zones configured on the server as it would effectively "shadow"
+ part of your DNS subtree.
+
+Upon catalog zone (re)load or change, all the PTR records in the format
+``unique-id.zones.catalog. 0 IN PTR member.com.`` (but not ``too.deep.zones.catalog.``!)
+are processed and member zones created, with zone names taken from the
+PTR records' RData, and zone settings taken from the configuration
+templates specified by :ref:`zone_catalog-template`.
+
+The owner names of the PTR records shall follow this scheme:
+
+.. code-block:: console
+
+ <unique-id>.zones.<catalog-zone>.
+
+where the mentioned labels shall match:
+
+- *<unique-id>* — Single label that is recommended to be unique among member zones.
+- ``zones`` — Required label.
+- *<catalog-zone>* — Name of the catalog zone.
+
+Additionally, records in the format
+``group.unique-id.zones.catalog. 0 IN TXT "conf-template"``
+are processed as a definition of the member's *group* property. The
+``unique-id`` must match the one of the PTR record defining the member.
+
+All other records and other member properties are ignored. They remain in the catalog
+zone, however, and might be for example transferred to a secondary server,
+which may interpret catalog zones differently. SOA still needs to be present in
+the catalog zone and its serial handled appropriately. An apex NS record must be
+present as for any other zone. The version record ``version 0 IN TXT "2"``
+is required at the catalog zone apex.
+
+A catalog zone may be modified using any standard means (e.g. AXFR/IXFR, DDNS,
+zone file reload). In the case of incremental change, only affected
+member zones are reloaded.
+
+The catalog zone must have at least one :ref:`zone_catalog-template`
+configured. The configuration for any defined member zone is taken from its
+*group* property value, which should match some catalog-template name.
+If the *group* property is not defined for a member, is empty, or doesn't match
+any of defined catalog-template names, the first catalog-template
+(in the order from configuration) is used. Nesting of catalog zones isn't
+supported.
+
+Any de-cataloged member zone is purged immediately, including its
+zone file, journal, timers, and DNSSEC keys. The zone file is not
+deleted if :ref:`zone_zonefile-sync` is set to *-1* for member zones.
+Any member zone, whose PTR record's owner has been changed, is purged
+immediately if and only if the *<unique-id>* has been changed.
+
+When setting up catalog zones, it might be useful to set
+:ref:`database_catalog-db` and :ref:`database_catalog-db-max-size`
+to non-default values.
+
+.. NOTE::
+
+ Whenever a catalog zone is updated, the server reloads itself with
+ all configured zones, including possibly existing other catalog zones.
+ It's similar to calling `knotc zone-reload` (for all zones).
+ The consequence is that new zone files might be discovered and reloaded,
+ even for zones that do not relate to updated catalog zone.
+
+ Catalog zones never expire automatically, regardless of what is declared
+ in the catalog zone SOA. However, a catalog zone can be expired manually
+ at any time using `knotc -f zone-purge +expire`.
+
+ Currently, expiration of a catalog zone doesn't have any effect on its
+ member zones. This will likely change in the future depending on the
+ Internet Draft.
+
+.. WARNING::
+
+ The server does not work well if one member zone appears in two catalog zones
+ concurrently. The user is encouraged to avoid this situation whatsoever.
+ Thus, there is no way a member zone can be migrated from one catalog
+ to another while preserving its metadata. Following steps may be used
+ as a workaround:
+
+ * :ref:`Back up<Data and metadata backup>` the member zone's metadata
+ (on each server separately).
+ * Remove the member zone from the catalog it's a member of.
+ * Wait for the catalog zone to be propagated to all servers.
+ * Add the member zone to the other catalog.
+ * Restore the backed up metadata (on each server separately).
+
+Catalog zones configuration examples
+------------------------------------
+
+Below are configuration snippets (e.g. `server` and `log` sections missing)
+of very simple catalog zone setups, in order to illustrate the relations
+between catalog-related configuration options.
+
+First setup represents a very simple scenario where the primary is
+the catalog zone generator and the secondary is the catalog zone consumer.
+
+Primary configuration::
+
+ acl:
+ - id: slave_xfr
+ address: ...
+ action: transfer
+
+ template:
+ - id: mmemb
+ catalog-role: member
+ catalog-zone: catz.
+ acl: slave_xfr
+
+ zone:
+ - domain: catz.
+ catalog-role: generate
+ acl: slave_xfr
+
+ - domain: foo.com.
+ template: mmemb
+
+ - domain: bar.com.
+ template: mmemb
+
+Secondary configuration::
+
+ acl:
+ - id: master_notify
+ address: ...
+ action: notify
+
+ template:
+ - id: smemb
+ master: master
+ acl: master_notify
+
+ zone:
+ - domain: catz.
+ master: master
+ acl: master_notify
+ catalog-role: interpret
+ catalog-template: smemb
+
+When new zones are added (or removed) to the primary configuration with assigned
+`mmemb` template, they will automatically propagate to the secondary
+and have the `smemb` template assigned there.
+
+Second example is with a hand-written (or script-generated) catalog zone,
+while employing configuration groups::
+
+ catz. 0 SOA invalid. invalid. 1625079950 3600 600 2147483646 0
+ catz. 0 NS invalid.
+ version.catz. 0 TXT "2"
+ nj2xg5bnmz2w4ltd.zones.catz. 0 PTR just-fun.com.
+ group.nj2xg5bnmz2w4ltd.zones.catz. 0 TXT unsigned
+ nvxxezjnmz2w4ltd.zones.catz. 0 PTR more-fun.com.
+ group.nvxxezjnmz2w4ltd.zones.catz. 0 TXT unsigned
+ nfwxa33sorqw45bo.zones.catz. 0 PTR important.com.
+ group.nfwxa33sorqw45bo.zones.catz. 0 TXT signed
+ mjqw42zomnxw2lq0.zones.catz. 0 PTR bank.com.
+ group.mjqw42zomnxw2lq0.zones.catz. 0 TXT signed
+
+And the server in this case is configured to distinguish the groups by applying
+different templates::
+
+ template:
+ - id: unsigned
+ ...
+
+ - id: signed
+ dnssec-signing: on
+ dnssec-policy: ...
+ ...
+
+ zone:
+ - domain: catz.
+ file: ...
+ catalog-role: interpret
+ catalog-template: [ unsigned, signed ]
+
+.. _query-modules:
+
+Query modules
+=============
+
+Knot DNS supports configurable query modules that can alter the way
+queries are processed. Each query requires a finite number of steps to
+be resolved. We call this set of steps a *query plan*, an abstraction
+that groups these steps into several stages.
+
+* Before-query processing
+* Answer, Authority, Additional records packet sections processing
+* After-query processing
+
+For example, processing an Internet-class query needs to find an
+answer. Then based on the previous state, it may also append an
+authority SOA or provide additional records. Each of these actions
+represents a 'processing step'. Now, if a query module is loaded for a
+zone, it is provided with an implicit query plan which can be extended
+by the module or even changed altogether.
+
+A module is active if its name, which includes the ``mod-`` prefix, is assigned
+to the zone/template :ref:`zone_module` option or to the ``default`` template
+:ref:`template_global-module` option if activating for all queries.
+If the module is configurable, a corresponding module section with
+an identifier must be created and then referenced in the form of
+``module_name/module_id``. See :ref:`Modules` for the list of available modules.
+
+.. NOTE::
+ Query modules are processed in the order they are specified in the
+ zone/template configuration. In most cases, the recommended order is::
+
+ mod-synthrecord, mod-onlinesign, mod-cookies, mod-rrl, mod-dnstap, mod-stats
+
+Performance Tuning
+==================
+
+Numbers of Workers
+------------------
+
+There are three types of workers ready for parallel execution of performance-oriented tasks:
+UDP workers, TCP workers, and Background workers. The first two types handle all network requests
+via the UDP and TCP protocol (respectively) and do the response jobs for common
+queries. Background workers process changes to the zone.
+
+By default, Knot determines a well-fitting number of workers based on the number of CPU cores.
+The user can specify the number of workers for each type with configuration/server section:
+:ref:`server_udp-workers`, :ref:`server_tcp-workers`, :ref:`server_background-workers`.
+
+An indication of when to increase the number of workers is when the server is lagging behind
+expected performance, while CPU usage remains low. This is usually due to waiting for network
+or I/O response during the operation. It may be caused by Knot design not fitting the use-case well.
+The user should try increasing the number of workers (of the related type) slightly above 100 and if
+the performance improves, decide a further, exact setting.
+
+Number of available file descriptors
+------------------------------------
+
+A name server configured for a large number of zones (hundreds or more) needs enough file descriptors
+available for zone transfers and zone file updates, which default OS settings often don't provide.
+It's necessary to check with the OS configuration and documentation and ensure the number of file
+descriptors (sometimes called a number of concurrently open files) effective for the knotd process
+is set suitably high. The number of concurrently open incoming TCP connections must be taken into
+account too. In other words, the required setting is affected by the :ref:`server_tcp-max-clients`
+setting.
+
+Sysctl and NIC optimizations
+----------------------------
+
+There are several recommendations based on Knot developers' experience with their specific HW and SW
+(mainstream Intel-based servers, Debian-based GNU/Linux distribution). They may improve or impact
+performance in common use cases.
+
+If your NIC driver allows it (see /proc/interrupts for hint), set CPU affinity (/proc/irq/$IRQ/smp_affinity)
+manually so that each NIC channel is served by unique CPU core(s). You must turn off irqbalance service
+in advance to avoid configuration override.
+
+Configure sysctl as follows: ::
+
+ socket_bufsize=1048576
+ busy_latency=0
+ backlog=40000
+ optmem_max=20480
+
+ net.core.wmem_max = $socket_bufsize
+ net.core.wmem_default = $socket_bufsize
+ net.core.rmem_max = $socket_bufsize
+ net.core.rmem_default = $socket_bufsize
+ net.core.busy_read = $busy_latency
+ net.core.busy_poll = $busy_latency
+ net.core.netdev_max_backlog = $backlog
+ net.core.optmem_max = $optmem_max
+
+Disable huge pages.
+
+Configure your CPU to "performance" mode. This can be achieved depending on architecture, e.g. in BIOS,
+or e.g. configuring /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor to "performance".
+
+Tune your NIC device with ethtool: ::
+
+ ethtool -A $dev autoneg off rx off tx off
+ ethtool -K $dev tso off gro off ufo off
+ ethtool -G $dev rx 4096 tx 4096
+ ethtool -C $dev rx-usecs 75
+ ethtool -C $dev tx-usecs 75
+ ethtool -N $dev rx-flow-hash udp4 sdfn
+ ethtool -N $dev rx-flow-hash udp6 sdfn
+
+On FreeBSD you can just: ::
+
+ ifconfig ${dev} -rxcsum -txcsum -lro -tso
+
+Knot developers are open to hear about users' further suggestions about network devices tuning/optimization.
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..c2bd3dd
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,19 @@
+.. highlight:: none
+
+Welcome to Knot DNS's documentation!
+====================================
+
+.. toctree::
+ :maxdepth: 2
+
+ introduction
+ requirements
+ installation
+ configuration
+ operation
+ troubleshooting
+ reference
+ modules
+ utilities
+ migration
+ appendices
diff --git a/doc/installation.rst b/doc/installation.rst
new file mode 100644
index 0000000..1ff71a9
--- /dev/null
+++ b/doc/installation.rst
@@ -0,0 +1,92 @@
+.. highlight:: console
+.. _Installation:
+
+************
+Installation
+************
+
+.. _Installation from a package_:
+
+Installation from a package
+===========================
+
+Knot DNS may already be included in your operating system distribution and
+therefore can be installed from packages (Linux), ports (BSD), or via
+Homebrew (macOS). This is always preferred unless you want to test the latest
+features, contribute to Knot development, or you know what you are doing.
+
+See the project `download <https://www.knot-dns.cz/download>`_ page for
+the latest information.
+
+.. _Installation from source code:
+
+Installation from source code
+=============================
+
+Required build environment
+--------------------------
+
+The build process relies on these standard tools:
+
+* make
+* libtool
+* pkg-config
+* autoconf >= 2.65
+* python-sphinx (optional, for documentation building)
+
+GCC >= 4.1 is mandatory for atomic built-ins, but the latest
+available version is recommended. Another requirement is ``_GNU_SOURCE``
+and C99 support, otherwise it adapts to the available compiler features.
+LLVM clang compiler since version 2.9 can be used as well.
+
+Getting the source code
+-----------------------
+
+You can find the source code for the latest release on `www.knot-dns.cz <https://www.knot-dns.cz>`_.
+Alternatively, you can fetch the whole project from the git repository
+`https://gitlab.nic.cz/knot/knot-dns.git <https://gitlab.nic.cz/knot/knot-dns>`_.
+
+After obtaining the source code, compilation and installation is quite a
+straightforward process using autotools.
+
+.. _Configuring and generating Makefiles:
+
+Configuring and generating Makefiles
+------------------------------------
+
+If compiling from git source, you need to bootstrap the ``./configure`` file first::
+
+ $ autoreconf -i -f
+
+In most cases, you can just run configure without any options::
+
+ $ ./configure
+
+For all available configure options run::
+
+ $ ./configure --help
+
+Compilation
+-----------
+
+After running ``./configure`` you can compile Knot DNS by running
+``make`` command, which will produce binaries and other related
+files::
+
+ $ make
+
+.. NOTE::
+ The compilation with enabled optimizations may take a long time. In such
+ a case the ``--disable-fastparser`` configure option can help.
+
+Installation
+------------
+
+When you have finished building Knot DNS, it's time to install the
+binaries and configuration files into the operation system hierarchy.
+You can do so by executing::
+
+ $ make install
+
+When installing as a non-root user, you might have to gain elevated privileges by
+switching to root user, e.g. ``sudo make install`` or ``su -c 'make install'``.
diff --git a/doc/introduction.rst b/doc/introduction.rst
new file mode 100644
index 0000000..8a54f6e
--- /dev/null
+++ b/doc/introduction.rst
@@ -0,0 +1,87 @@
+.. highlight:: none
+.. _Introduction:
+
+************
+Introduction
+************
+
+What is Knot DNS
+================
+
+Knot DNS is a high-performance open-source DNS server. It
+implements only the authoritative domain name service. Knot DNS
+can reliably serve TLD domains as well as any other zones.
+
+Knot DNS benefits from its multi-threaded and mostly lock-free
+implementation which allows it to scale well on SMP systems and
+operate non-stop even when adding or removing zones.
+
+The server itself is accompanied by several utilities for general DNS
+operations or for maintaining the server.
+
+For more info and downloads see `www.knot-dns.cz <https://www.knot-dns.cz>`_.
+
+Knot DNS features
+=================
+
+DNS features:
+
+* Primary and secondary server operation
+* Internet class (IN)
+* DNS extension (EDNS0, EDE)
+* TCP and UDP protocols
+* Zone catalog generation and interpretation
+* Minimal responses
+* Dynamic zone updates
+* DNSSEC with NSEC and NSEC3
+* ZONEMD generation and validation
+* Transaction signature using TSIG
+* Full and incremental zone transfers (AXFR, IXFR)
+* Name server identification using NSID or Chaos TXT records
+* Resource record types A, NS, CNAME, SOA, PTR, HINFO, MINFO, MX,
+ TXT, RP, AFSDB, RT, KEY, AAAA, LOC, SRV, NAPTR, KX, CERT, DNAME, APL, DS,
+ SSHFP, IPSECKEY, RRSIG, NSEC, DNSKEY, DHCID, NSEC3, NSEC3PARAM, TLSA, SMIMEA,
+ CDS, CDNSKEY, OPENPGPKEY, CSYNC, ZONEMD, SVCB, HTTPS, SPF, NID, L32, L64, LP,
+ EUI48, EUI64, URI, CAA, and Unknown
+
+Server features:
+
+* IPv4 and IPv6 support
+* Semantic zone checks
+* Server control interface
+* Zone journal storage
+* Persistent zone event timers
+* YAML-based or database-based configuration
+* Query processing modules with dynamic loading
+* On-the-fly zone management and server reconfiguration
+* Multithreaded DNSSEC zone signing and zone validation
+* Automatic DNSSEC key management
+* Zone data backup and restore
+* Offline KSK operation
+* PKCS #11 interface
+
+Remarkable module extensions:
+
+* Response rate limiting
+* Forward and reverse records synthesis
+* DNS request traffic statistics
+* Efficient DNS traffic logging interface
+* Dnstap traffic logging
+* Online DNSSEC signing
+* GeoIP response tailoring supporting ECS and DNSSEC
+
+Remarkable supported networking features:
+
+* TCP Fast Open (client and server)
+* High-performance UDP and TCP through AF_XDP processing (on Linux 4.18+)
+* Inbound DNS-over-QUIC processing (on Linux)
+* SO_REUSEPORT (on Linux) or SO_REUSEPORT_LB (on FreeBSD 12.0+) on UDP and by choice on TCP
+* Binding to non-local addresses (IP_FREEBIND on Linux, IP_BINDANY/IPV6_BINDANY on FreeBSD)
+* Ignoring PMTU information for IPv4/UDP via IP_PMTUDISC_OMIT
+
+License
+=======
+
+Knot DNS is licensed under the `GNU General Public License <https://www.gnu.org/copyleft/gpl.html>`_
+version 3 or (at your option) any later version. The full text of the license
+is available in the ``COPYING`` file distributed with source code.
diff --git a/doc/logo.pdf b/doc/logo.pdf
new file mode 100644
index 0000000..113566b
--- /dev/null
+++ b/doc/logo.pdf
Binary files differ
diff --git a/doc/logo.svg b/doc/logo.svg
new file mode 100644
index 0000000..e0085ca
--- /dev/null
+++ b/doc/logo.svg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 23.0.6, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Vrstva_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 600 225" style="enable-background:new 0 0 600 225;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#003893;}
+</style>
+<path class="st0" d="M319.2,106.8l-30.5-37.9h-0.2v37.9h-19.5V24.6h19.5V58h0.4l29.4-33.4H343l-35.4,37.9l37.5,44.4H319.2
+ M407.2,106.8L374.1,53h-0.3l0.5,53.8h-19.3V24.6h22.6l33,53.6h0.4l-0.5-53.6h19.3v82.2H407.2z M486.1,109
+ c-25.7,0-44.7-17.7-44.7-43.7c0-26.4,19-43,44.7-43c25.8,0,44.8,16.6,44.8,43C530.9,91.3,511.9,109,486.1,109z M486.1,40.3
+ c-13.9,0-23.5,10.7-23.5,25.1c0,14.9,9.6,25.5,23.5,25.5c13.8,0,23.6-10.7,23.6-25.5C509.7,50.9,500,40.3,486.1,40.3z M576.8,41.5
+ v65.3h-19.9V41.5h-23.2v-17H600v17H576.8z M299.6,200.2h-30.7V118h29.7c23.3,0,47,9.8,47,40.9C345.7,187.8,322.2,200.2,299.6,200.2z
+ M298.2,134.9h-9.9V183h9.4c14.2,0,27.3-5.8,27.3-24.2C325,140.4,311.9,134.9,298.2,134.9z M407.2,200.2l-33.1-53.8h-0.3l0.5,53.8
+ h-19.3V118h22.6l33,53.6h0.4l-0.5-53.6h19.3v82.2H407.2z M488.6,139.3c-3.4-4.3-9.5-7.1-14.6-7.1c-5.1,0-11.5,1.7-11.5,8.2
+ c0,5.5,4.9,7.2,12.7,9.6c11.1,3.6,25.5,8.4,25.5,24.7c0,18.9-15.2,27.4-31.5,27.4c-11.7,0-23.6-4.3-30.8-11.8l13-13.2
+ c3.9,5,11.3,8.7,17.8,8.7c6,0,11.4-2.3,11.4-8.9c0-6.3-6.3-8.2-17.1-11.7c-10.5-3.4-21-8.7-21-23.6c0-18.2,16.5-25.8,31.8-25.8
+ c9.3,0,19.7,3.5,26.9,10.1L488.6,139.3z M184.9,27.1c1.3,6.9,2.1,14,2.1,21.3c0,22.2-6.6,43-17.9,60.4c-0.4,0.6-0.8,1.2-1.2,1.8
+ c-0.4,0.6-0.8,1.2-1.2,1.7c-4.3,6.2-9.3,11.9-14.8,17.1c-0.5,0.5-1,0.9-1.5,1.4c-0.5,0.5-1,0.9-1.5,1.4c-10.9,9.6-23.6,17-37.6,21.8
+ c-3.5-6.3-6.2-13.1-8.1-20.3c12-3.9,23-10.3,32.2-18.6c0.5-0.4,1-0.9,1.4-1.3c0.5-0.4,1-0.9,1.4-1.4c5.2-5.1,9.8-10.9,13.6-17.1
+ c0.4-0.6,0.7-1.2,1-1.7c0.4-0.6,0.7-1.2,1-1.8c7.1-12.9,11.2-27.6,11.2-43.3c0-6.4-0.7-12.6-2-18.6c-0.1-0.7-0.3-1.3-0.4-1.9
+ c-0.2-0.6-0.3-1.3-0.5-1.9c-0.6,0.2-1.3,0.3-1.9,0.5c-0.6,0.2-1.3,0.4-1.9,0.6c-20.6,6.7-37.9,20.7-49,38.9
+ c-7.2-2.2-14.7-3.8-22.4-4.6c13.2-25.5,35.8-45.3,63.3-54.6c0,0,0,0,0,0c-12.1-4.5-25.3-6.9-38.9-6.9C55.7-0.1,9.4,40.5,0.6,93.8
+ c0.5-0.4,1-0.9,1.5-1.3c19.6-17.2,45.3-27.7,73.4-27.7c1.9,0,3.7,0,5.5,0.1c0.7,0,1.4,0.1,2.1,0.1c0.7,0.1,1.4,0.1,2.1,0.2
+ c7.7,0.7,15.1,2.1,22.2,4.3c0.6,0.2,1.3,0.4,1.9,0.6c0.6,0.2,1.3,0.4,1.9,0.6c14,4.8,26.7,12.2,37.6,21.8c-3.8,6.3-8.3,12-13.5,17.1
+ c-9.2-8.3-20.1-14.7-32.1-18.6c-0.6-0.2-1.2-0.4-1.9-0.6c-0.6-0.2-1.3-0.4-1.9-0.6c-6.9-1.9-14.2-3-21.6-3.2c-0.7,0-1.4,0-2.1,0
+ h-0.3c-0.6,0-1.2,0-1.8,0c-22.3,0.4-42.7,9-58.1,23c-0.5,0.4-1,0.9-1.4,1.3c-0.5,0.4-1,0.9-1.4,1.4c0.4,0.4,0.9,0.9,1.4,1.4
+ c0.5,0.5,1,0.9,1.5,1.3c15.5,13.9,35.8,22.5,58.1,23c1.7,7.5,4.2,14.8,7.3,21.7c-1.8,0.1-3.7,0.1-5.5,0.1
+ c-28.1,0-53.8-10.5-73.4-27.7c-0.5-0.4-1-0.9-1.5-1.3c8.8,53.3,55.1,94,110.9,94c13.7,0,26.8-2.4,38.9-6.9
+ c-27.5-9.4-50.1-29.2-63.3-54.7c-0.3-0.6-0.6-1.3-0.9-1.9c-0.3-0.6-0.6-1.3-0.9-1.9c-3.2-6.8-5.7-13.9-7.5-21.4
+ c-0.2-0.6-0.3-1.3-0.4-2c-0.2-0.6-0.3-1.3-0.4-2c-1.4-7-2.1-14.3-2.1-21.7c0-7.4,0.7-14.7,2.1-21.7c7.5,0.1,14.7,1.2,21.6,3.1
+ c-1.3,6-1.9,12.2-1.9,18.6s0.7,12.6,1.9,18.6c0.1,0.6,0.3,1.3,0.4,1.9c0.2,0.6,0.3,1.3,0.5,1.9c1.9,7.2,4.6,14,8,20.4
+ c0.3,0.6,0.6,1.2,1,1.8c0.3,0.6,0.7,1.2,1,1.8c11.1,18.2,28.4,32.2,49,38.9c0.6,0.2,1.3,0.4,1.9,0.6c0.6,0.2,1.3,0.4,1.9,0.5
+ c0.2-0.6,0.3-1.3,0.5-1.9c0.2-0.6,0.3-1.3,0.4-2c1.3-6,2-12.2,2-18.6c0-15.7-4.1-30.5-11.2-43.3c5.6-5.2,10.7-10.9,15.1-17.1
+ c11.3,17.4,17.9,38.2,17.9,60.5c0,7.3-0.7,14.4-2.1,21.2c23.9-20.6,39-51.1,39-85.2C223.9,78.3,208.8,47.8,184.9,27.1z"/>
+</svg>
diff --git a/doc/man/kcatalogprint.8in b/doc/man/kcatalogprint.8in
new file mode 100644
index 0000000..afab092
--- /dev/null
+++ b/doc/man/kcatalogprint.8in
@@ -0,0 +1,80 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KCATALOGPRINT" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kcatalogprint \- Knot DNS catalog print utility
+.SH SYNOPSIS
+.sp
+\fBkcatalogprint\fP [\fIconfig_option\fP \fIconfig_argument\fP] [\fIoption\fP]
+.SH DESCRIPTION
+.sp
+The program prints zone catalog stored in a catalog database.
+.SS Config options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.TP
+\fB\-D\fP, \fB\-\-dir\fP \fIpath\fP
+Use specified catalog database path and default configuration.
+.UNINDENT
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-a\fP, \fB\-\-catalog\fP
+Filter the output by catalog zone name.
+.TP
+\fB\-m\fP, \fB\-\-member\fP
+Filter the output by member zone name.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH SEE ALSO
+.sp
+\fBknotd(8)\fP, \fBknot.conf(5)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/kdig.1in b/doc/man/kdig.1in
new file mode 100644
index 0000000..714eaf9
--- /dev/null
+++ b/doc/man/kdig.1in
@@ -0,0 +1,460 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KDIG" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kdig \- Advanced DNS lookup utility
+.SH SYNOPSIS
+.sp
+\fBkdig\fP [\fIcommon\-settings\fP] [\fIquery\fP [\fIsettings\fP]]...
+.sp
+\fBkdig\fP \fB\-h\fP
+.SH DESCRIPTION
+.sp
+This utility sends one or more DNS queries to a nameserver. Each query can have
+individual \fIsettings\fP, or it can be specified globally via \fIcommon\-settings\fP,
+which must precede \fIquery\fP specification.
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIquery\fP
+\fIname\fP | \fB\-q\fP \fIname\fP | \fB\-x\fP \fIaddress\fP | \fB\-G\fP \fItapfile\fP
+.TP
+\fIcommon\-settings\fP, \fIsettings\fP
+[\fIquery_class\fP] [\fIquery_type\fP] [\fB@\fP\fIserver\fP]... [\fIoptions\fP]
+.TP
+\fIname\fP
+Is a domain name that is to be looked up.
+.TP
+\fIserver\fP
+Is a domain name or an IPv4 or IPv6 address of the nameserver to send a query
+to. An additional port can be specified using address:port ([address]:port
+for IPv6 address), address@port, or address#port notation. A value which begins
+with \(aq/\(aq character is considered an absolute UNIX socket path. If no server is
+specified, the servers from \fB/etc/resolv.conf\fP are used.
+.UNINDENT
+.sp
+If no arguments are provided, \fBkdig\fP sends NS query for the root
+zone.
+.SS Query classes
+.sp
+A \fIquery_class\fP can be either a DNS class name (IN, CH) or generic class
+specification \fBCLASS\fP\fIXXXXX\fP where \fIXXXXX\fP is a corresponding decimal
+class number. The default query class is IN.
+.SS Query types
+.sp
+A \fIquery_type\fP can be either a DNS resource record type
+(A, AAAA, NS, SOA, DNSKEY, ANY, etc.) or one of the following:
+.INDENT 0.0
+.TP
+\fBTYPE\fP\fIXXXXX\fP
+Generic query type specification where \fIXXXXX\fP is a corresponding decimal
+type number.
+.TP
+\fBAXFR\fP
+Full zone transfer request.
+.TP
+\fBIXFR=\fP\fIserial\fP
+Incremental zone transfer request for specified SOA serial number
+(i.e. all zone updates since the specified zone version are to be returned).
+.TP
+\fBNOTIFY=\fP\fIserial\fP
+Notify message with a SOA serial hint specified.
+.TP
+\fBNOTIFY\fP
+Notify message with a SOA serial hint unspecified.
+.UNINDENT
+.sp
+The default query type is A.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-4\fP
+Use the IPv4 protocol only.
+.TP
+\fB\-6\fP
+Use the IPv6 protocol only.
+.TP
+\fB\-b\fP \fIaddress\fP
+Set the source IP address of the query to \fIaddress\fP\&. The address must be a
+valid address for local interface or :: or 0.0.0.0. An optional port
+can be specified in the same format as the \fIserver\fP value.
+.TP
+\fB\-c\fP \fIclass\fP
+An explicit \fIquery_class\fP specification. See possible values above.
+.TP
+\fB\-d\fP
+Enable debug messages.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-k\fP \fIkeyfile\fP
+Use the TSIG key stored in a file \fIkeyfile\fP to authenticate the request. The
+file must contain the key in the same format as accepted by the
+\fB\-y\fP option.
+.TP
+\fB\-p\fP \fIport\fP
+Set the nameserver port number or service name to send a query to. The default
+port is 53.
+.TP
+\fB\-q\fP \fIname\fP
+Set the query name. An explicit variant of \fIname\fP specification. If no \fIname\fP
+is provided, empty question section is set.
+.TP
+\fB\-t\fP \fItype\fP
+An explicit \fIquery_type\fP specification. See possible values above.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.TP
+\fB\-x\fP \fIaddress\fP
+Send a reverse (PTR) query for IPv4 or IPv6 \fIaddress\fP\&. The correct name, class
+and type is set automatically.
+.TP
+\fB\-y\fP [\fIalg\fP:]\fIname\fP:\fIkey\fP
+Use the TSIG key named \fIname\fP to authenticate the request. The \fIalg\fP
+part specifies the algorithm (the default is hmac\-sha256) and \fIkey\fP specifies
+the shared secret encoded in Base64.
+.TP
+\fB\-E\fP \fItapfile\fP
+Export a dnstap trace of the query and response messages received to the
+file \fItapfile\fP\&.
+.TP
+\fB\-G\fP \fItapfile\fP
+Generate message output from a previously saved dnstap file \fItapfile\fP\&.
+.TP
+\fB+\fP[\fBno\fP]\fBmultiline\fP
+Wrap long records to more lines and improve human readability.
+.TP
+\fB+\fP[\fBno\fP]\fBshort\fP
+Show record data only.
+.TP
+\fB+\fP[\fBno\fP]\fBgeneric\fP
+Use the generic representation format when printing resource record types
+and data.
+.TP
+\fB+\fP[\fBno\fP]\fBcrypto\fP
+Display the DNSSEC keys and signatures values in base64, instead of omitting them.
+.TP
+\fB+\fP[\fBno\fP]\fBaaflag\fP
+Set the AA flag.
+.TP
+\fB+\fP[\fBno\fP]\fBtcflag\fP
+Set the TC flag.
+.TP
+\fB+\fP[\fBno\fP]\fBrdflag\fP
+Set the RD flag.
+.TP
+\fB+\fP[\fBno\fP]\fBrecurse\fP
+Same as \fB+\fP[\fBno\fP]\fBrdflag\fP
+.TP
+\fB+\fP[\fBno\fP]\fBraflag\fP
+Set the RA flag.
+.TP
+\fB+\fP[\fBno\fP]\fBzflag\fP
+Set the zero flag bit.
+.TP
+\fB+\fP[\fBno\fP]\fBadflag\fP
+Set the AD flag.
+.TP
+\fB+\fP[\fBno\fP]\fBcdflag\fP
+Set the CD flag.
+.TP
+\fB+\fP[\fBno\fP]\fBdnssec\fP
+Set the DO flag.
+.TP
+\fB+\fP[\fBno\fP]\fBall\fP
+Show all packet sections.
+.TP
+\fB+\fP[\fBno\fP]\fBqr\fP
+Show the query packet.
+.TP
+\fB+\fP[\fBno\fP]\fBheader\fP
+Show the packet header.
+.TP
+\fB+\fP[\fBno\fP]\fBcomments\fP
+Show commented section names.
+.TP
+\fB+\fP[\fBno\fP]\fBopt\fP
+Show the EDNS pseudosection.
+.TP
+\fB+\fP[\fBno\fP]\fBopttext\fP
+Try to show unknown EDNS options as text.
+.TP
+\fB+\fP[\fBno\fP]\fBquestion\fP
+Show the question section.
+.TP
+\fB+\fP[\fBno\fP]\fBanswer\fP
+Show the answer section.
+.TP
+\fB+\fP[\fBno\fP]\fBauthority\fP
+Show the authority section.
+.TP
+\fB+\fP[\fBno\fP]\fBadditional\fP
+Show the additional section.
+.TP
+\fB+\fP[\fBno\fP]\fBtsig\fP
+Show the TSIG pseudosection.
+.TP
+\fB+\fP[\fBno\fP]\fBstats\fP
+Show trailing packet statistics.
+.TP
+\fB+\fP[\fBno\fP]\fBclass\fP
+Show the DNS class.
+.TP
+\fB+\fP[\fBno\fP]\fBttl\fP
+Show the TTL value.
+.TP
+\fB+\fP[\fBno\fP]\fBtcp\fP
+Use the TCP protocol (default is UDP for standard query and TCP for AXFR/IXFR).
+.TP
+\fB+\fP[\fBno\fP]\fBfastopen\fP
+Use TCP Fast Open.
+.TP
+\fB+\fP[\fBno\fP]\fBignore\fP
+Don\(aqt use TCP automatically if a truncated reply is received.
+.TP
+\fB+\fP[\fBno\fP]\fBkeepopen\fP
+Keep TCP connection open for the following query if it has the same connection
+configuration. This applies to +tcp, +tls, and +https operations. The connection
+is considered in the context of a single kdig call only.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\fP
+Use TLS with the Opportunistic privacy profile (\fI\%RFC 7858#section\-4.1\fP).
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-ca\fP[=\fIFILE\fP]
+Use TLS with a certificate validation. Certification authority certificates
+are loaded from the specified PEM file (default is system certificate storage
+if no argument is provided).
+Can be specified multiple times. If the +tls\-hostname option is not provided,
+the name of the target server (if specified) is used for strict authentication.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-pin\fP=\fIBASE64\fP
+Use TLS with the Out\-of\-Band key\-pinned privacy profile (\fI\%RFC 7858#section\-4.2\fP).
+The PIN must be a Base64 encoded SHA\-256 hash of the X.509 SubjectPublicKeyInfo.
+Can be specified multiple times.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-hostname\fP=\fISTR\fP
+Use TLS with a remote server hostname check.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-sni\fP=\fISTR\fP
+Use TLS with a Server Name Indication.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-keyfile\fP=\fIFILE\fP
+Use TLS with a client keyfile.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-certfile\fP=\fIFILE\fP
+Use TLS with a client certfile.
+.TP
+\fB+\fP[\fBno\fP]\fBtls\-ocsp\-stapling\fP[=\fIH\fP]
+Use TLS with a valid stapled OCSP response for the server certificate
+(%u or specify hours). OCSP responses older than the specified period are
+considered invalid.
+.TP
+\fB+\fP[\fBno\fP]\fBhttps\fP[=\fIURL\fP]
+Use HTTPS (DNS\-over\-HTTPS) in wire format (\fI\%RFC 1035#section\-4.2.1\fP).
+It is also possible to specify URL=[authority][/path] where request
+will be sent to. Any leading scheme and authority indicator (i.e. //) are ignored.
+Authority might also be specified as the \fIserver\fP (using the parameter \fI@\fP).
+If \fIpath\fP is specified and \fIauthority\fP is missing, then the \fIserver\fP
+is used as authority together with the specified \fIpath\fP\&.
+Library \fIlibnghttp2\fP is required.
+.TP
+\fB+\fP[\fBno\fP]\fBhttps\-get\fP
+Use HTTPS with HTTP/GET method instead of the default HTTP/POST method.
+Library \fIlibnghttp2\fP is required.
+.TP
+\fB+\fP[\fBno\fP]\fBquic\fP
+Use QUIC (DNS\-over\-QUIC).
+.TP
+\fB+\fP[\fBno\fP]\fBnsid\fP
+Request the nameserver identifier (NSID).
+.TP
+\fB+\fP[\fBno\fP]\fBbufsize\fP=\fIB\fP
+Set EDNS buffer size in bytes (default is 4096 bytes).
+.TP
+\fB+\fP[\fBno\fP]\fBpadding\fP[=\fIB\fP]
+Use EDNS(0) padding option to pad queries, optionally to a specific
+size. The default is to pad queries with a sensible amount when using
++tls, and not to pad at all when queries are sent without TLS. With
+no argument (i.e., just +padding) pad every query with a sensible
+amount regardless of the use of TLS. With +nopadding, never pad.
+.TP
+\fB+\fP[\fBno\fP]\fBalignment\fP[=\fIB\fP]
+Align the query to B\-byte\-block message using the EDNS(0) padding option
+(default is no or 128 if no argument is specified).
+.TP
+\fB+\fP[\fBno\fP]\fBsubnet\fP=\fISUBN\fP
+Set EDNS(0) client subnet SUBN=addr/prefix.
+.TP
+\fB+\fP[\fBno\fP]\fBedns\fP[=\fIN\fP]
+Use EDNS version (default is 0).
+.TP
+\fB+\fP[\fBno\fP]\fBtimeout\fP=\fIT\fP
+Set the wait\-for\-reply interval in seconds (default is 5 seconds). This timeout
+applies to each query attempt. Zero value or \fInotimeout\fP is interpreted as
+infinity.
+.TP
+\fB+\fP[\fBno\fP]\fBretry\fP=\fIN\fP
+Set the number (>=0) of UDP retries (default is 2). This doesn\(aqt apply to
+AXFR/IXFR.
+.TP
+\fB+\fP[\fBno\fP]\fBexpire\fP
+Sets the EXPIRE EDNS option.
+.TP
+\fB+\fP[\fBno\fP]\fBcookie\fP[=\fIHEX\fP]
+Attach EDNS(0) cookie to the query.
+.TP
+\fB+\fP[\fBno\fP]\fBbadcookie\fP
+Repeat a query with the correct cookie.
+.TP
+\fB+\fP[\fBno\fP]\fBednsopt\fP[=\fICODE\fP[:\fIHEX\fP]]
+Send custom EDNS option. The \fICODE\fP is EDNS option code in decimal, \fIHEX\fP
+is an optional hex encoded string to use as EDNS option value. This argument
+can be used multiple times. +noednsopt clears all EDNS options specified by
++ednsopt.
+.TP
+\fB+\fP[\fBno\fP]\fBproxy\fP=\fISRC_ADDR\fP[#\fISRC_PORT\fP]\-\fIDST_ADDR\fP[#\fIDST_PORT\fP]
+Add PROXYv2 header with the specified source and destination addresses to the query.
+The default source port is 0 and destination port 53.
+.TP
+\fB+\fP[\fBno\fP]\fBjson\fP
+Use JSON for output encoding (RFC 8427).
+.TP
+\fB+noidn\fP
+Disable the IDN transformation to ASCII and vice versa. IDN support depends
+on libidn availability during project building! If used in \fIcommon\-settings\fP,
+all IDN transformations are disabled. If used in the individual query \fIsettings\fP,
+transformation from ASCII is disabled on output for the particular query. Note
+that IDN transformation does not preserve domain name letter case.
+.UNINDENT
+.SH NOTES
+.sp
+Options \fB\-k\fP and \fB\-y\fP can not be used simultaneously.
+.sp
+Dnssec\-keygen keyfile format is not supported. Use \fBkeymgr(8)\fP instead.
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.INDENT 0.0
+.IP 1. 3
+Get A records for example.com:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig example.com A
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 2. 3
+Perform AXFR for zone example.com from the server 192.0.2.1:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig example.com \-t AXFR @192.0.2.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 3. 3
+Get A records for example.com from 192.0.2.1 and reverse lookup for address
+2001:DB8::1 from 192.0.2.2. Both using the TCP protocol:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig +tcp example.com \-t A @192.0.2.1 \-x 2001:DB8::1 @192.0.2.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 4. 3
+Get SOA record for example.com, use TLS, use system certificates, check
+for specified hostname, check for certificate pin, and print additional
+debug info:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig \-d @185.49.141.38 +tls\-ca +tls\-host=getdnsapi.net \e
+ +tls\-pin=foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9S= soa example.com
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 5. 3
+DNS over HTTPS examples (various DoH implementations):
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig @1.1.1.1 +https example.com.
+$ kdig @193.17.47.1 +https=/doh example.com.
+$ kdig @8.8.4.4 +https +https\-get example.com.
+$ kdig @8.8.8.8 +https +tls\-hostname=dns.google +fastopen example.com.
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 6. 3
+More queries share one DoT connection:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kdig @1.1.1.1 +tls +keepopen abc.example.com A mail.example.com AAAA
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH FILES
+.sp
+\fB/etc/resolv.conf\fP
+.SH SEE ALSO
+.sp
+\fBkhost(1)\fP, \fBknsupdate(1)\fP, \fBkeymgr(8)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/keymgr.8in b/doc/man/keymgr.8in
new file mode 100644
index 0000000..399ece8
--- /dev/null
+++ b/doc/man/keymgr.8in
@@ -0,0 +1,364 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KEYMGR" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+keymgr \- Knot DNS key management utility
+.SH SYNOPSIS
+.sp
+\fBkeymgr\fP [\fIconfig_option\fP \fIconfig_argument\fP] [\fIoption\fP\&...] \fIzone_name\fP \fIcommand\fP \fIargument\fP\&...
+.sp
+\fBkeymgr\fP [\fIconfig_option\fP \fIconfig_argument\fP] \fB\-l\fP
+.sp
+\fBkeymgr\fP \fB\-t\fP \fIparameter\fP\&...
+.SH DESCRIPTION
+.sp
+The \fBkeymgr\fP utility serves for manual key management in Knot DNS server.
+.sp
+Functions for DNSSEC keys and KASP (Key And Signature Policy)
+management are provided.
+.sp
+The DNSSEC and KASP configuration is stored in a so called KASP database.
+The database is backed by LMDB.
+.SS Config options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.TP
+\fB\-D\fP, \fB\-\-dir\fP \fIpath\fP
+Use specified KASP database path and default configuration.
+.UNINDENT
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-t\fP, \fB\-\-tsig\fP \fItsig_name\fP [\fItsig_algorithm\fP [\fItsig_bits\fP]]
+Generates a TSIG key. TSIG algorithm can be specified by string (default: hmac\-sha256),
+bit length of the key by number (default: optimal length given by algorithm). The generated
+TSIG key is only displayed on \fIstdout\fP: the command does not create a file, nor include the
+key in a keystore.
+.TP
+\fB\-e\fP, \fB\-\-extended\fP
+Extended output (listing of keys with full description).
+.TP
+\fB\-j\fP, \fB\-\-json\fP
+Print the zones or keys in JSON format.
+.TP
+\fB\-l\fP, \fB\-\-list\fP
+Print the list of zones that have at least one key stored in the configured KASP
+database.
+.TP
+\fB\-x\fP, \fB\-\-mono\fP
+Don\(aqt generate colorized output.
+.TP
+\fB\-X\fP, \fB\-\-color\fP
+Force colorized output in the normal mode.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Keymgr runs with the same user privileges as configured for knotd\&.
+For example, if keymgr is run as \fBroot\fP, but the configured user
+is \fBknot\fP, it won\(aqt be able to read files (PEM files, KASP database, ...) readable
+only by \fBroot\fP\&.
+.UNINDENT
+.UNINDENT
+.SS Commands
+.INDENT 0.0
+.TP
+\fBlist\fP [\fItimestamp_format\fP]
+Prints the list of key IDs and parameters of keys belonging to the zone.
+.TP
+\fBgenerate\fP [\fIarguments\fP\&...]
+Generates new DNSSEC key and stores it in KASP database. Prints the key ID.
+This action takes some number of arguments (see below). Values for unspecified arguments are taken
+from corresponding policy (if \fI\-c\fP or \fI\-C\fP options used) or from Knot policy defaults.
+.TP
+\fBimport\-bind\fP \fIBIND_key_file\fP
+Imports a BIND\-style key into KASP database (converting it to PEM format).
+Takes one argument: path to BIND key file (private or public, but both MUST exist).
+.TP
+\fBimport\-pub\fP \fIBIND_pubkey_file\fP
+Imports a public key into KASP database. This key won\(aqt be rolled over nor used for signing.
+Takes one argument: path to BIND public key file.
+.TP
+\fBimport\-pem\fP \fIPEM_file\fP [\fIarguments\fP\&...]
+Imports a DNSSEC key from PEM file. The key parameters (same as for the generate action) need to be
+specified (mainly algorithm, timers...) because they are not contained in the PEM format.
+.TP
+\fBimport\-pkcs11\fP \fIkey_id\fP [\fIarguments\fP\&...]
+Imports a DNSSEC key from PKCS #11 storage. The key parameters (same as for the generate action) need to be
+specified (mainly algorithm, timers...) because they are not available. In fact, no key
+data is imported, only KASP database metadata is created.
+.TP
+\fBnsec3\-salt\fP [\fInew_salt\fP]
+Prints the current NSEC3 salt used for signing. If \fInew_salt\fP is specified, the salt is overwritten.
+The salt is printed and expected in hexadecimal, or dash if empty.
+.TP
+\fBlocal\-serial\fP [\fInew_serial\fP]
+Print SOA serial stored in KASP database when using on\-secondary DNSSEC signing.
+If \fInew_serial\fP is specified, the serial is overwritten. After updating the serial, expire the zone
+(\fBzone\-purge +expire +zonefile +journal\fP) if the server is running, or remove corresponding zone file
+and journal contents if the server is stopped.
+.TP
+\fBmaster\-serial\fP [\fInew_serial\fP]
+Print SOA serial of the remote master stored in KASP database when using on\-secondary DNSSEC signing.
+If \fInew_serial\fP is specified, the serial is overwritten (not recommended).
+.TP
+\fBset\fP \fIkey_spec\fP [\fIarguments\fP\&...]
+Changes a timing argument (or ksk/zsk) of an existing key to a new value. \fIKey_spec\fP is either the
+key tag or a prefix of the key ID, with an optional \fI[id=|keytag=]\fP prefix; \fIarguments\fP
+are like for \fBgenerate\fP, but just the related ones.
+.TP
+\fBds\fP [\fIkey_spec\fP]
+Generate DS record (all digest algorithms together) for specified key. \fIKey_spec\fP
+is like for \fBset\fP, if unspecified, all KSKs are used.
+.TP
+\fBdnskey\fP [\fIkey_spec\fP]
+Generate DNSKEY record for specified key. \fIKey_spec\fP
+is like for \fBds\fP, if unspecified, all KSKs are used.
+.TP
+\fBdelete\fP \fIkey_spec\fP
+Remove the specified key from zone. If the key was not shared, it is also deleted from keystore.
+.TP
+\fBshare\fP \fIkey_ID\fP \fIzone_from\fP
+Import a key (specified by full key ID) from another zone as shared. After this, the key is
+owned by both zones equally.
+.UNINDENT
+.SS Commands related to Offline KSK feature
+.INDENT 0.0
+.TP
+\fBpregenerate\fP [\fItimestamp\-from\fP] \fItimestamp\-to\fP
+Pre\-generate ZSKs for use with offline KSK, for the specified period starting from now or specified time.
+This function also applies to non\-offline KSK keys.
+.TP
+\fBshow\-offline\fP [\fItimestamp\-from\fP] [\fItimestamp\-to\fP]
+Print pre\-generated offline key\-related records for specified time interval. If \fItimestamp_to\fP
+is omitted, it will be to infinity. If \fItimestamp\-from\fP is omitted, it will start from the
+beginning.
+.TP
+\fBdel\-offline\fP \fItimestamp\-from\fP \fItimestamp\-to\fP
+Delete pre\-generated offline key\-related records in specified time interval.
+.TP
+\fBdel\-all\-old\fP
+Delete old keys that are in state \(aqremoved\(aq. This function also applies to
+non\-offline KSK keys.
+.TP
+\fBgenerate\-ksr\fP [\fItimestamp\-from\fP] \fItimestamp\-to\fP
+Print to stdout KeySigningRequest based on pre\-generated ZSKs for specified time period.
+If \fItimestamp\-from\fP is omitted, timestamp of the last offline records set is used
+or now if no records available.
+.TP
+\fBsign\-ksr\fP \fIksr_file\fP
+Read KeySigningRequest from a text file, sign it using local keyset and print SignedKeyResponse to stdout.
+.TP
+\fBvalidate\-skr\fP \fIskr_file\fP
+Read SignedKeyResponse from a text file and validate the RRSIGs in it if not corrupt.
+.TP
+\fBimport\-skr\fP \fIskr_file\fP
+Read SignedKeyResponse from a text file and import the signatures for later use in zone. If some
+signatures have already been imported, they will be deleted for the period from beginning of the SKR
+to infinity.
+.UNINDENT
+.SS Generate arguments
+.sp
+Arguments are separated by space, each of them is in format \(aqname=value\(aq.
+.INDENT 0.0
+.TP
+\fBalgorithm\fP
+Either an algorithm number (e.g. 14), or text name without dashes (e.g. ECDSAP384SHA384).
+.TP
+\fBsize\fP
+Key length in bits.
+.TP
+\fBksk\fP
+If set to \fByes\fP, the key will be used for signing DNSKEY rrset. The generated key will also
+have the Secure Entry Point flag set to 1.
+.TP
+\fBzsk\fP
+If set to \fByes\fP, the key will be used for signing zone (except DNSKEY rrset). This flag can
+be set concurrently with the \fBksk\fP flag.
+.TP
+\fBsep\fP
+Overrides the standard setting of the Secure Entry Point flag.
+.UNINDENT
+.sp
+The following arguments are timestamps of key lifetime (see DNSSEC Key states):
+.INDENT 0.0
+.TP
+\fBpre_active\fP
+Key started to be used for signing, not published (only for algorithm rollover).
+.TP
+\fBpublish\fP
+Key published.
+.TP
+\fBready\fP
+Key is waiting for submission (only for KSK).
+.TP
+\fBactive\fP
+Key used for signing.
+.TP
+\fBretire_active\fP
+Key still used for signing, but another key is active (only for KSK or algorithm rollover).
+.TP
+\fBretire\fP
+Key still published, but no longer used for signing.
+.TP
+\fBpost_active\fP
+Key no longer published, but still used for signing (only for algorithm rollover).
+.TP
+\fBrevoke\fP
+Key revoked according to \fI\%RFC 5011\fP trust anchor roll\-over.
+.TP
+\fBremove\fP
+Key deleted.
+.UNINDENT
+.SS Timestamps
+.INDENT 0.0
+.TP
+0
+Zero timestamp means infinite future.
+.TP
+\fIUNIX_time\fP
+Positive number of seconds since 1970 UTC.
+.TP
+\fIYYYYMMDDHHMMSS\fP
+Date and time in this format without any punctuation.
+.TP
+\fIrelative_timestamp\fP
+A sign character (\fB+\fP, \fB\-\fP), a number, and an optional time unit
+(\fBy\fP, \fBmo\fP, \fBd\fP, \fBh\fP, \fBmi\fP, \fBs\fP). The default unit is one second.
+E.g. +1mi, \-2mo.
+.UNINDENT
+.SS Output timestamp formats
+.INDENT 0.0
+.TP
+(none)
+The timestamps are printed as UNIX timestamp.
+.TP
+\fBhuman\fP
+The timestamps are printed relatively to now using time units (e.g. \-2y5mo, +1h13s).
+.TP
+\fBiso\fP
+The timestamps are printed in the ISO8601 format (e.g. 2016\-12\-31T23:59:00).
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.INDENT 0.0
+.IP 1. 3
+Generate new TSIG key:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ keymgr \-t my_name hmac\-sha384
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 2. 3
+Generate new DNSSEC key:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \e
+ ksk=true created=1488034625 publish=20170223205611 retire=+10mo remove=+1y
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 3. 3
+Import a DNSSEC key from BIND:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ keymgr example.com. import\-bind ~/bind/Kharbinge4d5.+007+63089.key
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 4. 3
+Configure key timing:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ keymgr example.com. set 4208 active=+2mi retire=+4mi remove=+5mi
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 5. 3
+Share a KSK from another zone:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ keymgr example.com. share e687cf927029e9db7184d2ece6d663f5d1e5b0e9 another\-zone.com.
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fI\%RFC 6781\fP \- DNSSEC Operational Practices.
+\fI\%RFC 7583\fP \- DNSSEC Key Rollover Timing Considerations.
+.sp
+\fBknot.conf(5)\fP,
+\fBknotc(8)\fP,
+\fBknotd(8)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/khost.1in b/doc/man/khost.1in
new file mode 100644
index 0000000..d0f0e7c
--- /dev/null
+++ b/doc/man/khost.1in
@@ -0,0 +1,156 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KHOST" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+khost \- Simple DNS lookup utility
+.SH SYNOPSIS
+.sp
+\fBkhost\fP [\fIoptions\fP] \fIname\fP [\fIserver\fP]
+.SH DESCRIPTION
+.sp
+This utility sends a DNS query for the \fIname\fP to the \fIserver\fP and prints a reply
+in more user\-readable form. For more advanced DNS queries use kdig
+instead.
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIname\fP
+Is a domain name that is to be looked up. If the \fIname\fP is IPv4 or IPv6
+address the PTR query type is used.
+.TP
+\fIserver\fP
+Is a name or an address of the nameserver to send a query to. The address
+can be specified using [address]:port notation. If no server is specified,
+the servers from \fB/etc/resolv.conf\fP are used.
+.UNINDENT
+.sp
+If no arguments are provided, \fBkhost\fP prints a short help.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-4\fP
+Use the IPv4 protocol only.
+.TP
+\fB\-6\fP
+Use the IPv6 protocol only.
+.TP
+\fB\-a\fP
+Send ANY query with verbose mode.
+.TP
+\fB\-d\fP
+Enable debug messages.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-r\fP
+Disable recursion.
+.TP
+\fB\-T\fP
+Use the TCP protocol.
+.TP
+\fB\-v\fP
+Enable verbose output.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.TP
+\fB\-w\fP
+Wait forever for the reply.
+.TP
+\fB\-c\fP \fIclass\fP
+Set the query class (e.g. CH, CLASS4). The default class is IN.
+.TP
+\fB\-t\fP \fItype\fP
+Set the query type (e.g. NS, IXFR=12345, TYPE65535). The default is to send 3
+queries (A, AAAA and MX).
+.TP
+\fB\-R\fP \fIretries\fP
+The number (>=0) of UDP retries to query a nameserver. The default is 1.
+.TP
+\fB\-W\fP \fIwait\fP
+The time to wait for a reply in seconds. This timeout applies to each query
+try. The default is 2 seconds.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.INDENT 0.0
+.IP 1. 3
+Get the A, AAAA and MX records for example.com:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ khost example.com
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 2. 3
+Get the reverse record for address 192.0.2.1:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ khost 192.0.2.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.IP 3. 3
+Perform a verbose zone transfer for zone example.com:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ khost \-t AXFR \-v example.com
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH FILES
+.sp
+\fB/etc/resolv.conf\fP
+.SH SEE ALSO
+.sp
+\fBkdig(1)\fP, \fBknsupdate(1)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/kjournalprint.8in b/doc/man/kjournalprint.8in
new file mode 100644
index 0000000..2ff78e8
--- /dev/null
+++ b/doc/man/kjournalprint.8in
@@ -0,0 +1,120 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KJOURNALPRINT" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kjournalprint \- Knot DNS journal print utility
+.SH SYNOPSIS
+.sp
+\fBkjournalprint\fP [\fIconfig_option\fP \fIconfig_argument\fP] [\fIoption\fP\&...] \fIzone_name\fP
+.sp
+\fBkjournalprint\fP [\fIconfig_option\fP \fIconfig_argument\fP] \fB\-z\fP
+.SH DESCRIPTION
+.sp
+The program prints zone history stored in a journal database. As default,
+changes are colored for terminal.
+.SS Config options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.TP
+\fB\-D\fP, \fB\-\-dir\fP \fIpath\fP
+Use specified journal database path and default configuration.
+.UNINDENT
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-z\fP, \fB\-\-zone\-list\fP
+Instead of reading the journal, display the list of zones in the DB.
+.TP
+\fB\-l\fP, \fB\-\-limit\fP \fIlimit\fP
+Limits the number of displayed changes.
+.TP
+\fB\-s\fP, \fB\-\-serial\fP \fIsoa\fP
+Start at a specific SOA serial.
+.TP
+\fB\-H\fP, \fB\-\-check\fP
+Enable additional journal semantic checks during printing.
+.TP
+\fB\-d\fP, \fB\-\-debug\fP
+Debug mode brief output.
+.TP
+\fB\-x\fP, \fB\-\-mono\fP
+Don\(aqt generate colorized output.
+.TP
+\fB\-n\fP, \fB\-\-no\-color\fP
+An alias for \fB\-x\fP\&. Use of this option is deprecated, it will be removed in the future.
+.TP
+\fB\-X\fP, \fB\-\-color\fP
+Force colorized output.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIzone_name\fP
+A name of the zone to print the history for.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.sp
+Last (most recent) 5 changes without colors:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ kjournalprint \-nl 5 /var/lib/knot/journal example.com.
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fBknotd(8)\fP, \fBknot.conf(5)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/knot.conf.5in b/doc/man/knot.conf.5in
new file mode 100644
index 0000000..4ed939d
--- /dev/null
+++ b/doc/man/knot.conf.5in
@@ -0,0 +1,2499 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KNOT.CONF" "5" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+knot.conf \- Knot DNS configuration file
+.SH DESCRIPTION
+.sp
+Configuration files for Knot DNS use simplified YAML format. Simplified means
+that not all of the features are supported.
+.sp
+For the description of configuration items, we have to declare a meaning of
+the following symbols:
+.INDENT 0.0
+.IP \(bu 2
+\fBINT\fP – Integer
+.IP \(bu 2
+\fBSTR\fP – Textual string
+.IP \(bu 2
+\fBHEXSTR\fP – Hexadecimal string (with \fB0x\fP prefix)
+.IP \(bu 2
+\fBBOOL\fP – Boolean value (\fBon\fP/\fBoff\fP or \fBtrue\fP/\fBfalse\fP)
+.IP \(bu 2
+\fBTIME\fP – Number of seconds, an integer with possible time multiplier suffix
+(\fBs\fP ~ 1, \fBm\fP ~ 60, \fBh\fP ~ 3600 or \fBd\fP ~ 24 * 3600)
+.IP \(bu 2
+\fBSIZE\fP – Number of bytes, an integer with possible size multiplier suffix
+(\fBB\fP ~ 1, \fBK\fP ~ 1024, \fBM\fP ~ 1024^2 or \fBG\fP ~ 1024^3)
+.IP \(bu 2
+\fBBASE64\fP – Base64 encoded string
+.IP \(bu 2
+\fBADDR\fP – IPv4 or IPv6 address
+.IP \(bu 2
+\fBDNAME\fP – Domain name
+.IP \(bu 2
+\fB\&...\fP – Multi\-valued item, order of the values is preserved
+.IP \(bu 2
+\fB[\fP \fB]\fP – Optional value
+.IP \(bu 2
+\fB|\fP – Choice
+.UNINDENT
+.sp
+The configuration consists of several fixed sections and optional module
+sections. There are 16 fixed sections (\fBmodule\fP, \fBserver\fP, \fBxdp\fP, \fBcontrol\fP,
+\fBlog\fP, \fBstatistics\fP, \fBdatabase\fP, \fBkeystore\fP, \fBkey\fP, \fBremote\fP,
+\fBremotes\fP, \fBacl\fP, \fBsubmission\fP, \fBpolicy\fP, \fBtemplate\fP, \fBzone\fP).
+Module sections are prefixed with the \fBmod\-\fP prefix (e.g. \fBmod\-stats\fP).
+.sp
+Most of the sections (e.g. \fBzone\fP) are sequences of settings blocks. Each
+settings block begins with a unique identifier, which can be used as a reference
+from other sections (such an identifier must be defined in advance).
+.sp
+A multi\-valued item can be specified either as a YAML sequence:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+address: [10.0.0.1, 10.0.0.2]
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+or as more single\-valued items each on an extra line:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+address: 10.0.0.1
+address: 10.0.0.2
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+If an item value contains spaces or other special characters, it is necessary
+to enclose such a value within double quotes \fB"\fP \fB"\fP\&.
+.SH COMMENTS
+.sp
+A comment begins with a \fB#\fP character and is ignored during processing.
+Also each configuration section or sequence block allows a permanent
+comment using the \fBcomment\fP item which is stored in the server beside the
+configuration.
+.SH INCLUDING CONFIGURATION
+.sp
+Another configuration file or files, matching a pattern, can be included at
+the top level in the current file.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+include: STR
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS include
+.sp
+A path or a matching pattern specifying one or more files that are included
+at the place of the include option position in the configuration.
+If the path is not absolute, then it is considered to be relative to the
+current file. The pattern can be an arbitrary string meeting POSIX \fIglob\fP
+requirements, e.g. dir/*.conf. Matching files are processed in sorted order.
+.sp
+\fIDefault:\fP not set
+.SH MODULE SECTION
+.sp
+Dynamic modules loading configuration.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If configured with non\-empty \fB\(ga\-\-with\-moduledir=path\(ga\fP parameter, all
+shared modules in this directory will be automatically loaded.
+.UNINDENT
+.UNINDENT
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+module:
+ \- id: STR
+ file: STR
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A module identifier in the form of the \fBmod\-\fP prefix and module name suffix.
+.SS file
+.sp
+A path to a shared library file with the module implementation.
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+If the path is not absolute, the library is searched in the set of
+system directories. See \fBman dlopen\fP for more details.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB${libdir}/knot/modules\-${version}\fP/module_name.so
+(or \fB${path}\fP/module_name.so if configured with \fB\-\-with\-moduledir=path\fP)
+.SH SERVER SECTION
+.sp
+General options related to the server.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+server:
+ identity: [STR]
+ version: [STR]
+ nsid: [STR|HEXSTR]
+ rundir: STR
+ user: STR[:STR]
+ pidfile: STR
+ udp\-workers: INT
+ tcp\-workers: INT
+ background\-workers: INT
+ async\-start: BOOL
+ tcp\-idle\-timeout: TIME
+ tcp\-io\-timeout: INT
+ tcp\-remote\-io\-timeout: INT
+ tcp\-max\-clients: INT
+ tcp\-reuseport: BOOL
+ tcp\-fastopen: BOOL
+ quic\-max\-clients: INT
+ quic\-outbuf\-max\-size: SIZE
+ quic\-idle\-close\-timeout: TIME
+ remote\-pool\-limit: INT
+ remote\-pool\-timeout: TIME
+ remote\-retry\-delay: TIME
+ socket\-affinity: BOOL
+ udp\-max\-payload: SIZE
+ udp\-max\-payload\-ipv4: SIZE
+ udp\-max\-payload\-ipv6: SIZE
+ key\-file: STR
+ cert\-file: STR
+ edns\-client\-subnet: BOOL
+ answer\-rotation: BOOL
+ automatic\-acl: BOOL
+ proxy\-allowlist: ADDR[/INT] | ADDR\-ADDR ...
+ dbus\-event: none | running | zone\-updated | ksk\-submission | dnssec\-invalid ...
+ dbus\-init\-delay: TIME
+ listen: ADDR[@INT] ...
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBCAUTION:\fP
+.INDENT 0.0
+.INDENT 3.5
+When you change configuration parameters dynamically or via configuration file
+reload, some parameters in the Server section require restarting the Knot server
+so that the changes take effect. See below for the details.
+.UNINDENT
+.UNINDENT
+.SS identity
+.sp
+An identity of the server returned in the response to the query for TXT
+record \fBid.server.\fP or \fBhostname.bind.\fP in the CHAOS class (\fI\%RFC 4892\fP).
+Set to an empty value to disable.
+.sp
+\fIDefault:\fP FQDN hostname
+.SS version
+.sp
+A version of the server software returned in the response to the query
+for TXT record \fBversion.server.\fP or \fBversion.bind.\fP in the CHAOS
+class (\fI\%RFC 4892\fP). Set to an empty value to disable.
+.sp
+\fIDefault:\fP server version
+.SS nsid
+.sp
+A DNS name server identifier (\fI\%RFC 5001\fP). Set to an empty value to disable.
+.sp
+\fIDefault:\fP FQDN hostname at the moment of the daemon start
+.SS rundir
+.sp
+A path for storing run\-time data (PID file, unix sockets, etc.).
+.sp
+Depending on the usage of this parameter, its change may require restart of the Knot
+server to take effect.
+.sp
+\fIDefault:\fP \fB${localstatedir}/run/knot\fP (configured with \fB\-\-with\-rundir=path\fP)
+.SS user
+.sp
+A system user with an optional system group (\fBuser:group\fP) under which the
+server is run after starting and binding to interfaces. Linux capabilities
+are employed if supported.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBroot:root\fP
+.SS pidfile
+.sp
+A PID file location.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fI\%rundir\fP\fB/knot.pid\fP
+.SS udp\-workers
+.sp
+A number of UDP workers (threads) used to process incoming queries
+over UDP.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP equal to the number of online CPUs
+.SS tcp\-workers
+.sp
+A number of TCP workers (threads) used to process incoming queries
+over TCP.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP equal to the number of online CPUs, default value is at least 10
+.SS background\-workers
+.sp
+A number of workers (threads) used to execute background operations (zone
+loading, zone updates, etc.).
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP equal to the number of online CPUs, default value is at most 10
+.SS async\-start
+.sp
+If enabled, server doesn\(aqt wait for the zones to be loaded and starts
+responding immediately with SERVFAIL answers until the zone loads.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS tcp\-idle\-timeout
+.sp
+Maximum idle time (in seconds) between requests on an inbound TCP connection.
+It means if there is no activity on an inbound TCP connection during this limit,
+the connection is closed by the server.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB10\fP
+.SS tcp\-io\-timeout
+.sp
+Maximum time (in milliseconds) to receive or send one DNS message over an inbound
+TCP connection. It means this limit applies to normal DNS queries and replies,
+incoming DDNS, and \fBoutgoing zone transfers\fP\&. The timeout is measured since some
+data is already available for processing.
+Set to 0 for infinity.
+.sp
+\fIDefault:\fP \fB500\fP (milliseconds)
+.sp
+\fBCAUTION:\fP
+.INDENT 0.0
+.INDENT 3.5
+In order to reduce the risk of Slow Loris attacks, it\(aqs recommended setting
+this limit as low as possible on public servers.
+.UNINDENT
+.UNINDENT
+.SS tcp\-remote\-io\-timeout
+.sp
+Maximum time (in milliseconds) to receive or send one DNS message over an outbound
+TCP connection which has already been established to a configured remote server.
+It means this limit applies to incoming zone transfers, sending NOTIFY,
+DDNS forwarding, and DS check or push. This timeout includes the time needed
+for a network round\-trip and for a query processing by the remote.
+Set to 0 for infinity.
+.sp
+\fIDefault:\fP \fB5000\fP (milliseconds)
+.SS tcp\-reuseport
+.sp
+If enabled, each TCP worker listens on its own socket and the OS kernel
+socket load balancing is employed using SO_REUSEPORT (or SO_REUSEPORT_LB
+on FreeBSD). Due to the lack of one shared socket, the server can offer
+higher response rate processing over TCP. However, in the case of
+time\-consuming requests (e.g. zone transfers of a TLD zone), enabled reuseport
+may result in delayed or not being responded client requests. So it is
+advisable to use this option on secondary servers.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS tcp\-fastopen
+.sp
+If enabled, use TCP Fast Open for outbound TCP communication (client side):
+incoming zone transfers, sending NOTIFY, and DDNS forwarding. This mode simplifies
+TCP handshake and can result in better networking performance. TCP Fast Open
+for inbound TCP communication (server side) isn\(aqt affected by this
+configuration as it\(aqs enabled automatically if supported by OS.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+The TCP Fast Open support must also be enabled on the OS level:
+.INDENT 0.0
+.IP \(bu 2
+Linux/macOS: ensure kernel parameter \fBnet.ipv4.tcp_fastopen\fP is \fB2\fP or
+\fB3\fP for server side, and \fB1\fP or \fB3\fP for client side.
+.IP \(bu 2
+FreeBSD: ensure kernel parameter \fBnet.inet.tcp.fastopen.server_enable\fP
+is \fB1\fP for server side, and \fBnet.inet.tcp.fastopen.client_enable\fP is
+\fB1\fP for client side.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS quic\-max\-clients
+.sp
+A maximum number of QUIC clients connected in parallel.
+.sp
+See also \fI\%quic\fP\&.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIMinimum:\fP \fB128\fP
+.sp
+\fIDefault:\fP \fB10000\fP (ten thousand)
+.SS quic\-outbuf\-max\-size
+.sp
+Maximum cumulative size of memory used for buffers of unACKed
+sent messages.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Set low if little memory is available (together with \fI\%quic\-max\-clients\fP
+since QUIC connections are memory\-heavy). Set to high value if outgoing zone
+transfers of big zone over QUIC are expected.
+.UNINDENT
+.UNINDENT
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIMinimum:\fP \fB1M\fP (1 MiB)
+.sp
+\fIDefault:\fP \fB100M\fP (100 MiB)
+.SS quic\-idle\-close\-timeout
+.sp
+Time in seconds, after which any idle QUIC connection is gracefully closed.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB4\fP
+.SS remote\-pool\-limit
+.sp
+If nonzero, the server will keep up to this number of outgoing TCP connections
+open for later use. This is an optimization to avoid frequent opening of
+TCP connections to the same remote.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fB0\fP
+.SS remote\-pool\-timeout
+.sp
+The timeout in seconds after which the unused kept\-open outgoing TCP connections
+to remote servers are closed.
+.sp
+\fIDefault:\fP \fB5\fP
+.SS remote\-retry\-delay
+.sp
+When a connection attempt times out to some remote address, this information will be
+kept for this specified time (in milliseconds) and other connections to the same address won\(aqt
+be attempted. This prevents repetitive waiting for timeout on an unreachable remote.
+.sp
+\fIDefault:\fP \fB0\fP
+.SS socket\-affinity
+.sp
+If enabled and if SO_REUSEPORT is available on Linux, all configured network
+sockets are bound to UDP and TCP workers in order to increase the networking performance.
+This mode isn\(aqt recommended for setups where the number of network card queues
+is lower than the number of UDP or TCP workers.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS tcp\-max\-clients
+.sp
+A maximum number of TCP clients connected in parallel, set this below the file
+descriptor limit to avoid resource exhaustion.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+It is advisable to adjust the maximum number of open files per process in your
+operating system configuration.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP one half of the file descriptor limit for the server process
+.SS udp\-max\-payload
+.sp
+Maximum EDNS0 UDP payload size default for both IPv4 and IPv6.
+.sp
+\fIDefault:\fP \fB1232\fP
+.SS udp\-max\-payload\-ipv4
+.sp
+Maximum EDNS0 UDP payload size for IPv4.
+.sp
+\fIDefault:\fP \fB1232\fP
+.SS udp\-max\-payload\-ipv6
+.sp
+Maximum EDNS0 UDP payload size for IPv6.
+.sp
+\fIDefault:\fP \fB1232\fP
+.SS key\-file
+.sp
+Path to a server key PEM file which is used for DNS over QUIC communication.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP one\-time in\-memory key
+.SS cert\-file
+.sp
+Path to a server certificate PEM file which is used for DNS over QUIC communication.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP one\-time in\-memory certificate
+.SS edns\-client\-subnet
+.sp
+Enable or disable EDNS Client Subnet support. If enabled, responses to queries
+containing the EDNS Client Subnet option
+always contain a valid EDNS Client Subnet option according to \fI\%RFC 7871\fP\&.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS answer\-rotation
+.sp
+Enable or disable sorted\-rrset rotation in the answer section of normal replies.
+The rotation shift is simply determined by a query ID.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS automatic\-acl
+.sp
+If enabled, \fI\%automatic ACL\fP setting of
+configured remotes is considered when evaluating authorized operations.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS proxy\-allowlist
+.sp
+An ordered list of IP addresses, network subnets, or network ranges
+which are allowed as a source address of proxied DNS traffic over UDP.
+The supported proxy protocol is
+\fI\%haproxy PROXY v2\fP\&.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+TCP is not supported.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS dbus\-event
+.sp
+Specification of server or zone states which emit a D\-Bus signal on the system
+bus. The bus name is \fBcz.nic.knotd\fP, the object path is \fB/cz/nic/knotd\fP, and
+the interface name is \fBcz.nic.knotd.events\fP\&.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – No signal is emitted.
+.IP \(bu 2
+\fBrunning\fP – The signal \fBstarted\fP is emitted when the server is fully operational
+and the signal \fBstopped\fP is emitted at the beginning of the server shutdown.
+.IP \(bu 2
+\fBzone\-updated\fP – The signal \fBzone_updated\fP is emitted when a zone has been updated;
+the signal parameters are \fIzone name\fP and \fIzone SOA serial\fP\&.
+.IP \(bu 2
+\fBksk\-submission\fP – The signal \fBzone_ksk_submission\fP is emitted if there is
+a ready KSK present when the zone is signed; the signal parameters are
+\fIzone name\fP, \fIKSK keytag\fP, and \fIKSK KASP id\fP\&.
+.IP \(bu 2
+\fBdnssec\-invalid\fP – The signal \fBzone_dnssec_invalid\fP is emitted when DNSSEC
+validation fails; the signal parameter is \fIzone name\fP\&.
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This function requires systemd version at least 221.
+.UNINDENT
+.UNINDENT
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBnone\fP
+.SS dbus\-init\-delay
+.sp
+Time in seconds which the server waits upon D\-Bus initialization to ensure
+the D\-Bus client is ready to receive signals.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIMinimum:\fP \fB0\fP
+.sp
+\fIDefault:\fP \fB1\fP
+.SS listen
+.sp
+One or more IP addresses where the server listens for incoming queries.
+Optional port specification (default is 53) can be appended to each address
+using \fB@\fP separator. Use \fB0.0.0.0\fP for all configured IPv4 addresses or
+\fB::\fP for all configured IPv6 addresses. Filesystem path can be specified
+for listening on local unix SOCK_STREAM socket. Non\-local address binding
+is automatically enabled if supported by the operating system.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP not set
+.SH XDP SECTION
+.sp
+Various options related to XDP listening, especially TCP.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+xdp:
+ listen: STR[@INT] | ADDR[@INT] ...
+ udp: BOOL
+ tcp: BOOL
+ quic: BOOL
+ quic\-port: INT
+ quic\-log: BOOL
+ tcp\-max\-clients: INT
+ tcp\-inbuf\-max\-size: SIZE
+ tcp\-outbuf\-max\-size: SIZE
+ tcp\-idle\-close\-timeout: TIME
+ tcp\-idle\-reset\-timeout: TIME
+ tcp\-resend\-timeout: TIME
+ route\-check: BOOL
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBCAUTION:\fP
+.INDENT 0.0
+.INDENT 3.5
+When you change configuration parameters dynamically or via configuration file
+reload, some parameters in the XDP section require restarting the Knot server
+so that the changes take effect.
+.UNINDENT
+.UNINDENT
+.SS listen
+.sp
+One or more network device names (e.g. \fBens786f0\fP) on which the Mode XDP
+is enabled. Alternatively, an IP address can be used instead of a device name,
+but the server will still listen on all addresses belonging to the same interface!
+Optional port specification (default is 53) can be appended to each device name
+or address using \fB@\fP separator.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fBCAUTION:\fP
+.INDENT 0.0
+.INDENT 3.5
+If XDP workers only process regular DNS traffic over UDP, it is strongly
+recommended to also \fI\%listen\fP on the addresses which are
+intended to offer the DNS service, at least to fulfil the DNS requirement for
+working TCP.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS udp
+.sp
+If enabled, DNS over UDP is processed with XDP workers.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBon\fP
+.SS tcp
+.sp
+If enabled, DNS over TCP traffic is processed with XDP workers.
+.sp
+The TCP stack limitations:
+.INDENT 0.0
+.INDENT 3.5
+.INDENT 0.0
+.IP \(bu 2
+Congestion control is not implemented.
+.IP \(bu 2
+Lost packets that do not contain TCP payload may not be resend.
+.IP \(bu 2
+Not optimized for transfers of non\-trivial zones.
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS quic
+.sp
+If enabled, DNS over QUIC is processed with XDP workers.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS quic\-port
+.sp
+DNS over QUIC will listen on the interfaces configured by \fI\%listen\fP,
+but on different port, configured by this option.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fB853\fP
+.SS quic\-log
+.sp
+Triggers extensive logging of all QUIC protocol internals for every connection.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS tcp\-max\-clients
+.sp
+A maximum number of TCP clients connected in parallel.
+.sp
+\fIMinimum:\fP \fB1024\fP
+.sp
+\fIDefault:\fP \fB1000000\fP (one million)
+.SS tcp\-inbuf\-max\-size
+.sp
+Maximum cumulative size of memory used for buffers of incompletely
+received messages.
+.sp
+\fIMinimum:\fP \fB1M\fP (1 MiB)
+.sp
+\fIDefault:\fP \fB100M\fP (100 MiB)
+.SS tcp\-outbuf\-max\-size
+.sp
+Maximum cumulative size of memory used for buffers of unACKed
+sent messages.
+.sp
+\fIMinimum:\fP \fB1M\fP (1 MiB)
+.sp
+\fIDefault:\fP \fB100M\fP (100 MiB)
+.SS tcp\-idle\-close\-timeout
+.sp
+Time in seconds, after which any idle connection is gracefully closed.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB10\fP
+.SS tcp\-idle\-reset\-timeout
+.sp
+Time in seconds, after which any idle connection is forcibly closed.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB20\fP
+.SS tcp\-resend\-timeout
+.sp
+Resend outgoing data packets (with DNS response payload) if not ACKed
+before this timeout.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB5\fP
+.SS route\-check
+.sp
+If enabled, routing information from the operating system is considered
+when processing every incoming DNS packet received over the XDP interface:
+.INDENT 0.0
+.IP \(bu 2
+If the outgoing interface of the corresponding DNS response differs from
+the incoming one, the packet is processed normally by UDP/TCP workers
+(XDP isn\(aqt used).
+.IP \(bu 2
+If the destination address is blackholed, unreachable, or prohibited,
+the DNS packet is dropped without any response.
+.IP \(bu 2
+The destination MAC address and possible VLAN tag for the response are taken
+from the routing system.
+.UNINDENT
+.sp
+If disabled, symmetrical routing is applied. It means that the query source
+MAC address is used as a response destination MAC address. Possible VLAN tag
+is preserved.
+.sp
+Change of this parameter requires restart of the Knot server to take effect.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This mode requires forwarding enabled on the loopback interface
+(\fBsysctl \-w net.ipv4.conf.lo.forwarding=1\fP and \fBsysctl \-w net.ipv6.conf.lo.forwarding=1\fP).
+If forwarding is disabled, all incoming DNS packets are dropped!
+.sp
+Only VLAN 802.1Q is supported.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SH CONTROL SECTION
+.sp
+Configuration of the server control interface.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+control:
+ listen: STR
+ timeout: TIME
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS listen
+.sp
+A UNIX socket path where the server listens for control commands.
+.sp
+\fIDefault:\fP \fI\%rundir\fP\fB/knot.sock\fP
+.SS timeout
+.sp
+Maximum time (in seconds) the control socket operations can take.
+Set to 0 for infinity.
+.sp
+\fIDefault:\fP \fB5\fP
+.SH LOG SECTION
+.sp
+Server can be configured to log to the standard output, standard error
+output, syslog (or systemd journal if systemd is enabled) or into an arbitrary
+file.
+.sp
+There are 6 logging severity levels:
+.INDENT 0.0
+.IP \(bu 2
+\fBcritical\fP – Non\-recoverable error resulting in server shutdown.
+.IP \(bu 2
+\fBerror\fP – Recoverable error, action should be taken.
+.IP \(bu 2
+\fBwarning\fP – Warning that might require user action.
+.IP \(bu 2
+\fBnotice\fP – Server notice or hint.
+.IP \(bu 2
+\fBinfo\fP – Informational message.
+.IP \(bu 2
+\fBdebug\fP – Debug or detailed message.
+.UNINDENT
+.sp
+In the case of a missing log section, \fBwarning\fP or more serious messages
+will be logged to both standard error output and syslog. The \fBinfo\fP and
+\fBnotice\fP messages will be logged to standard output.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+log:
+ \- target: stdout | stderr | syslog | STR
+ server: critical | error | warning | notice | info | debug
+ control: critical | error | warning | notice | info | debug
+ zone: critical | error | warning | notice | info | debug
+ any: critical | error | warning | notice | info | debug
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS target
+.sp
+A logging output.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBstdout\fP – Standard output.
+.IP \(bu 2
+\fBstderr\fP – Standard error output.
+.IP \(bu 2
+\fBsyslog\fP – Syslog or systemd journal.
+.IP \(bu 2
+\fIfile_name\fP – A specific file.
+.UNINDENT
+.sp
+With \fBsyslog\fP target, syslog service is used. However, if Knot DNS has been compiled
+with systemd support and operating system has been booted with systemd, systemd journal
+is used for logging instead of syslog.
+.SS server
+.sp
+Minimum severity level for messages related to general operation of the server to be
+logged.
+.sp
+\fIDefault:\fP not set
+.SS control
+.sp
+Minimum severity level for messages related to server control to be logged.
+.sp
+\fIDefault:\fP not set
+.SS zone
+.sp
+Minimum severity level for messages related to zones to be logged.
+.sp
+\fIDefault:\fP not set
+.SS any
+.sp
+Minimum severity level for all message types to be logged.
+.sp
+\fIDefault:\fP not set
+.SH STATS SECTION
+.sp
+Periodic server statistics dumping.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+statistics:
+ timer: TIME
+ file: STR
+ append: BOOL
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS timer
+.sp
+A period after which all available statistics metrics will by written to the
+\fI\%file\fP\&.
+.sp
+\fIDefault:\fP not set
+.SS file
+.sp
+A file path of statistics output in the YAML format.
+.sp
+\fIDefault:\fP \fI\%rundir\fP\fB/stats.yaml\fP
+.SS append
+.sp
+If enabled, the output will be appended to the \fI\%file\fP
+instead of file replacement.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SH DATABASE SECTION
+.sp
+Configuration of databases for zone contents, DNSSEC metadata, or event timers.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+database:
+ storage: STR
+ journal\-db: STR
+ journal\-db\-mode: robust | asynchronous
+ journal\-db\-max\-size: SIZE
+ kasp\-db: STR
+ kasp\-db\-max\-size: SIZE
+ timer\-db: STR
+ timer\-db\-max\-size: SIZE
+ catalog\-db: str
+ catalog\-db\-max\-size: SIZE
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS storage
+.sp
+A data directory for storing journal, KASP, and timer databases.
+.sp
+\fIDefault:\fP \fB${localstatedir}/lib/knot\fP (configured with \fB\-\-with\-storage=path\fP)
+.SS journal\-db
+.sp
+An explicit specification of the persistent journal database directory.
+Non\-absolute path (i.e. not starting with \fB/\fP) is relative to
+\fI\%storage\fP\&.
+.sp
+\fIDefault:\fP \fI\%storage\fP\fB/journal\fP
+.SS journal\-db\-mode
+.sp
+Specifies journal LMDB backend configuration, which influences performance
+and durability.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBrobust\fP – The journal database disk synchronization ensures database
+durability but is generally slower.
+.IP \(bu 2
+\fBasynchronous\fP – The journal database disk synchronization is optimized for
+better performance at the expense of lower database durability in the case of
+a crash. This mode is recommended on secondary servers with many zones.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBrobust\fP
+.SS journal\-db\-max\-size
+.sp
+The hard limit for the journal database maximum size. There is no cleanup logic
+in journal to recover from reaching this limit. Journal simply starts refusing
+changes across all zones. Decreasing this value has no effect if it is lower
+than the actual database file size.
+.sp
+It is recommended to limit \fI\%journal\-max\-usage\fP
+per\-zone instead of \fI\%journal\-db\-max\-size\fP
+in most cases. Please keep this value larger than the sum of all zones\(aq
+journal usage limits. See more details regarding
+journal behaviour\&.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This value also influences server\(aqs usage of virtual memory.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB20G\fP (20 GiB), or \fB512M\fP (512 MiB) for 32\-bit
+.SS kasp\-db
+.sp
+An explicit specification of the KASP database directory.
+Non\-absolute path (i.e. not starting with \fB/\fP) is relative to
+\fI\%storage\fP\&.
+.sp
+\fIDefault:\fP \fI\%storage\fP\fB/keys\fP
+.SS kasp\-db\-max\-size
+.sp
+The hard limit for the KASP database maximum size.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This value also influences server\(aqs usage of virtual memory.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB500M\fP (500 MiB)
+.SS timer\-db
+.sp
+An explicit specification of the persistent timer database directory.
+Non\-absolute path (i.e. not starting with \fB/\fP) is relative to
+\fI\%storage\fP\&.
+.sp
+\fIDefault:\fP \fI\%storage\fP\fB/timers\fP
+.SS timer\-db\-max\-size
+.sp
+The hard limit for the timer database maximum size.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This value also influences server\(aqs usage of virtual memory.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB100M\fP (100 MiB)
+.SS catalog\-db
+.sp
+An explicit specification of the zone catalog database directory.
+Only useful if catalog\-zones are enabled.
+Non\-absolute path (i.e. not starting with \fB/\fP) is relative to
+\fI\%storage\fP\&.
+.sp
+\fIDefault:\fP \fI\%storage\fP\fB/catalog\fP
+.SS catalog\-db\-max\-size
+.sp
+The hard limit for the catalog database maximum size.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This value also influences server\(aqs usage of virtual memory.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB20G\fP (20 GiB), or \fB512M\fP (512 MiB) for 32\-bit
+.SH KEYSTORE SECTION
+.sp
+DNSSEC keystore configuration.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+keystore:
+ \- id: STR
+ backend: pem | pkcs11
+ config: STR
+ key\-label: BOOL
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A keystore identifier.
+.SS backend
+.sp
+A key storage backend type.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBpem\fP – PEM files.
+.IP \(bu 2
+\fBpkcs11\fP – PKCS #11 storage.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBpem\fP
+.SS config
+.sp
+A backend specific configuration. A directory with PEM files (the path can
+be specified as a relative path to \fI\%kasp\-db\fP) or
+a configuration string for PKCS #11 storage (\fI<pkcs11\-url> <module\-path>\fP).
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Example configuration string for PKCS #11:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+"pkcs11:token=knot;pin\-value=1234 /usr/lib64/pkcs11/libsofthsm2.so"
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fI\%kasp\-db\fP\fB/keys\fP
+.SS key\-label
+.sp
+If enabled in combination with the PKCS #11 \fI\%backend\fP, generated keys
+are labeled in the form \fB<zone_name> KSK|ZSK\fP\&.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SH KEY SECTION
+.sp
+Shared TSIG keys used to authenticate communication with the server.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+key:
+ \- id: DNAME
+ algorithm: hmac\-md5 | hmac\-sha1 | hmac\-sha224 | hmac\-sha256 | hmac\-sha384 | hmac\-sha512
+ secret: BASE64
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A key name identifier.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This value MUST be exactly the same as the name of the TSIG key on the
+opposite primary/secondary server(s).
+.UNINDENT
+.UNINDENT
+.SS algorithm
+.sp
+A TSIG key algorithm. See
+\fI\%TSIG Algorithm Numbers\fP\&.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBhmac\-md5\fP
+.IP \(bu 2
+\fBhmac\-sha1\fP
+.IP \(bu 2
+\fBhmac\-sha224\fP
+.IP \(bu 2
+\fBhmac\-sha256\fP
+.IP \(bu 2
+\fBhmac\-sha384\fP
+.IP \(bu 2
+\fBhmac\-sha512\fP
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS secret
+.sp
+Shared key secret.
+.sp
+\fIDefault:\fP not set
+.SH REMOTE SECTION
+.sp
+Definitions of remote servers for outgoing connections (source of a zone
+transfer, target for a notification, etc.).
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remote:
+ \- id: STR
+ address: ADDR[@INT] ...
+ via: ADDR[@INT] ...
+ key: key_id
+ block\-notify\-after\-transfer: BOOL
+ no\-edns: BOOL
+ automatic\-acl: BOOL
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A remote identifier.
+.SS address
+.sp
+An ordered list of destination IP addresses which are used for communication
+with the remote server. The addresses are tried in sequence until the
+remote is reached. Optional destination port (default is 53)
+can be appended to the address using \fB@\fP separator.
+.sp
+\fIDefault:\fP not set
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If the remote is contacted and it refuses to perform requested action,
+no more addresses will be tried for this remote.
+.UNINDENT
+.UNINDENT
+.SS via
+.sp
+An ordered list of source IP addresses. The first address with the same family
+as the destination address is used as a source address for communication with
+the remote. This option can help if the server listens on more addresses.
+Optional source port (default is random) can be appended
+to the address using \fB@\fP separator.
+.sp
+\fIDefault:\fP not set
+.SS key
+.sp
+A \fI\%reference\fP to the TSIG key which is used to authenticate
+the communication with the remote server.
+.sp
+\fIDefault:\fP not set
+.SS block\-notify\-after\-transfer
+.sp
+When incoming AXFR/IXFR from this remote (as a primary server), suppress
+sending NOTIFY messages to all configured secondary servers.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS no\-edns
+.sp
+If enabled, no OPT record (EDNS) is inserted to outgoing requests to this
+remote server. This mode is necessary for communication with some broken
+implementations (e.g. Windows Server 2016).
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option effectively disables zone expire timer
+updates via EDNS EXPIRE option specified in \fI\%RFC 7314\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS automatic\-acl
+.sp
+If enabled, some authorized operations for the remote are automatically allowed
+based on the context:
+.INDENT 0.0
+.IP \(bu 2
+Incoming NOTIFY is allowed from the remote if it\(aqs configured as a
+\fI\%primary server\fP for the zone.
+.IP \(bu 2
+Outgoing zone transfer is allowed to the remote if it\(aqs configured as a
+\fI\%NOTIFY target\fP for the zone.
+.UNINDENT
+.sp
+Automatic ACL rules are evaluated before explicit \fI\%zone ACL\fP configuration.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This functionality requires global activation via
+\fI\%automatic\-acl\fP in the server section.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBon\fP
+.SH REMOTES SECTION
+.sp
+Definitions of groups of remote servers. Remote grouping can simplify the
+configuration.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+remotes:
+ \- id: STR
+ remote: remote_id ...
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A remote group identifier.
+.SS remote
+.sp
+An ordered list of \fI\%references\fP to remote server definitions.
+.sp
+\fIDefault:\fP not set
+.SH ACL SECTION
+.sp
+Access control list rule definitions. An ACL rule is a description of one
+or more authorized operations (zone transfer request, zone change notification,
+and dynamic DNS update) which are allowed to be processed or denied.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+acl:
+ \- id: STR
+ address: ADDR[/INT] | ADDR\-ADDR ...
+ key: key_id ...
+ remote: remote_id | remotes_id ...
+ action: query | notify | transfer | update ...
+ deny: BOOL
+ update\-type: STR ...
+ update\-owner: key | zone | name
+ update\-owner\-match: sub\-or\-equal | equal | sub
+ update\-owner\-name: STR ...
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+An ACL rule identifier.
+.SS address
+.sp
+An ordered list of IP addresses, network subnets, or network ranges. The query\(aqs
+source address must match one of them. If this item is not set, address match is not
+required.
+.sp
+\fIDefault:\fP not set
+.SS key
+.sp
+An ordered list of \fI\%reference\fPs to TSIG keys. The query must
+match one of them. If this item is not set, transaction authentication is not used.
+.sp
+\fIDefault:\fP not set
+.SS remote
+.sp
+An ordered list of references \fI\%remote\fP and
+\fI\%remotes\fP\&. The query must
+match one of the remotes. Specifically, one of the remote\(aqs addresses and remote\(aqs
+TSIG key if configured must match.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option cannot be specified along with the \fI\%address\fP or
+\fI\%key\fP option at one ACL item.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS action
+.sp
+An ordered list of allowed (or denied) actions.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBquery\fP – Allow regular DNS query. As normal queries are always allowed,
+this action is only useful in combination with \fI\%TSIG key\fP\&.
+.IP \(bu 2
+\fBnotify\fP – Allow incoming notify (NOTIFY).
+.IP \(bu 2
+\fBtransfer\fP – Allow zone transfer (AXFR, IXFR).
+.IP \(bu 2
+\fBupdate\fP – Allow zone updates (DDNS).
+.UNINDENT
+.sp
+\fIDefault:\fP \fBquery\fP
+.SS deny
+.sp
+If enabled, instead of allowing, deny the specified \fI\%action\fP,
+\fI\%address\fP, \fI\%key\fP, or combination if these
+items. If no action is specified, deny all actions.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS update\-type
+.sp
+A list of allowed types of Resource Records in a zone update. Every record in an update
+must match one of the specified types.
+.sp
+\fIDefault:\fP not set
+.SS update\-owner
+.sp
+This option restricts possible owners of Resource Records in a zone update by comparing
+them to either the \fI\%TSIG key\fP identity, the current zone name, or to a list of
+domain names given by the \fI\%update\-owner\-name\fP option.
+The comparison method is given by the \fI\%update\-owner\-match\fP option.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBkey\fP — The owner of each updated RR must match the identity of the TSIG key if used.
+.IP \(bu 2
+\fBname\fP — The owner of each updated RR must match at least one name in the
+\fI\%update\-owner\-name\fP list.
+.IP \(bu 2
+\fBzone\fP — The owner of each updated RR must match the current zone name.
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS update\-owner\-match
+.sp
+This option defines how the owners of Resource Records in an update are matched to the domain name(s)
+set by the \fI\%update\-owner\fP option.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBsub\-or\-equal\fP — The owner of each RR in an update must either be equal to
+or be a subdomain of at least one domain name set by \fI\%update\-owner\fP\&.
+.IP \(bu 2
+\fBequal\fP — The owner of each updated RR must be equal to at least one domain
+name set by \fI\%update\-owner\fP\&.
+.IP \(bu 2
+\fBsub\fP — The owner of each updated RR must be a subdomain of, but MUST NOT
+be equal to at least one domain name set by \fI\%update\-owner\fP\&.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBsub\-or\-equal\fP
+.SS update\-owner\-name
+.sp
+A list of allowed owners of RRs in a zone update used with \fI\%update\-owner\fP
+set to \fBname\fP\&. Every listed owner name which is not FQDN (i.e. it doesn\(aqt end
+in a dot) is considered as if it was appended with the target zone name.
+Such a relative owner name specification allows better ACL rule reusability across
+multiple zones.
+.sp
+\fIDefault:\fP not set
+.SH SUBMISSION SECTION
+.sp
+Parameters of KSK submission checks.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+submission:
+ \- id: STR
+ parent: remote_id | remotes_id ...
+ check\-interval: TIME
+ timeout: TIME
+ parent\-delay: TIME
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A submission identifier.
+.SS parent
+.sp
+A list of references \fI\%remote\fP and \fI\%remotes\fP
+to parent\(aqs DNS servers to be checked for
+presence of corresponding DS records in the case of KSK submission. All of them must
+have a corresponding DS for the rollover to continue. If none is specified, the
+rollover must be pushed forward manually.
+.sp
+\fIDefault:\fP not set
+.sp
+\fBTIP:\fP
+.INDENT 0.0
+.INDENT 3.5
+A DNSSEC\-validating resolver can be set as a parent.
+.UNINDENT
+.UNINDENT
+.SS check\-interval
+.sp
+Interval for periodic checks of DS presence on parent\(aqs DNS servers, in the
+case of the KSK submission.
+.sp
+\fIDefault:\fP \fB1h\fP (1 hour)
+.SS timeout
+.sp
+After this time period (in seconds) the KSK submission is automatically considered
+successful, even if all the checks were negative or no parents are configured.
+Set to 0 for infinity.
+.sp
+\fIDefault:\fP \fB0\fP
+.SS parent\-delay
+.sp
+After successful parent DS check, wait for this period before continuing the next
+key roll\-over step. This delay shall cover the propagation delay of update in the
+parent zone.
+.sp
+\fIDefault:\fP \fB0\fP
+.SH POLICY SECTION
+.sp
+DNSSEC policy configuration.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+policy:
+ \- id: STR
+ keystore: keystore_id
+ manual: BOOL
+ single\-type\-signing: BOOL
+ algorithm: rsasha1 | rsasha1\-nsec3\-sha1 | rsasha256 | rsasha512 | ecdsap256sha256 | ecdsap384sha384 | ed25519 | ed448
+ ksk\-size: SIZE
+ zsk\-size: SIZE
+ ksk\-shared: BOOL
+ dnskey\-ttl: TIME
+ zone\-max\-ttl: TIME
+ ksk\-lifetime: TIME
+ zsk\-lifetime: TIME
+ delete\-delay: TIME
+ propagation\-delay: TIME
+ rrsig\-lifetime: TIME
+ rrsig\-refresh: TIME
+ rrsig\-pre\-refresh: TIME
+ reproducible\-signing: BOOL
+ nsec3: BOOL
+ nsec3\-iterations: INT
+ nsec3\-opt\-out: BOOL
+ nsec3\-salt\-length: INT
+ nsec3\-salt\-lifetime: TIME
+ signing\-threads: INT
+ ksk\-submission: submission_id
+ ds\-push: remote_id | remotes_id ...
+ cds\-cdnskey\-publish: none | delete\-dnssec | rollover | always | double\-ds
+ cds\-digest\-type: sha256 | sha384
+ dnskey\-management: full | incremental
+ offline\-ksk: BOOL
+ unsafe\-operation: none | no\-check\-keyset | no\-update\-dnskey | no\-update\-nsec | no\-update\-expired ...
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A policy identifier.
+.SS keystore
+.sp
+A \fI\%reference\fP to a keystore holding private key material
+for zones.
+.sp
+\fIDefault:\fP an imaginary keystore with all default values
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+A configured keystore called "default" won\(aqt be used unless explicitly referenced.
+.UNINDENT
+.UNINDENT
+.SS manual
+.sp
+If enabled, automatic key management is not used.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS single\-type\-signing
+.sp
+If enabled, Single\-Type Signing Scheme is used in the automatic key management
+mode.
+.sp
+\fIDefault:\fP \fBoff\fP (module onlinesign has default \fBon\fP)
+.SS algorithm
+.sp
+An algorithm of signing keys and issued signatures. See
+\fI\%DNSSEC Algorithm Numbers\fP\&.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBrsasha1\fP
+.IP \(bu 2
+\fBrsasha1\-nsec3\-sha1\fP
+.IP \(bu 2
+\fBrsasha256\fP
+.IP \(bu 2
+\fBrsasha512\fP
+.IP \(bu 2
+\fBecdsap256sha256\fP
+.IP \(bu 2
+\fBecdsap384sha384\fP
+.IP \(bu 2
+\fBed25519\fP
+.IP \(bu 2
+\fBed448\fP
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Ed25519 algorithm is only available if compiled with GnuTLS 3.6.0+.
+.sp
+Ed448 algorithm is only available if compiled with GnuTLS 3.6.12+ and Nettle 3.6+.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBecdsap256sha256\fP
+.SS ksk\-size
+.sp
+A length of newly generated KSK or
+CSK keys.
+.sp
+\fIDefault:\fP \fB2048\fP (rsa*), \fB256\fP (ecdsap256), \fB384\fP (ecdsap384), \fB256\fP (ed25519),
+\fB456\fP (ed448)
+.SS zsk\-size
+.sp
+A length of newly generated ZSK keys.
+.sp
+\fIDefault:\fP see default for \fI\%ksk\-size\fP
+.SS ksk\-shared
+.sp
+If enabled, all zones with this policy assigned will share one or more KSKs.
+More KSKs can be shared during a KSK rollover.
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+As the shared KSK set is bound to the policy \fI\%id\fP, renaming the
+policy breaks this connection and new shared KSK set is initiated when
+a new KSK is needed.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS dnskey\-ttl
+.sp
+A TTL value for DNSKEY records added into zone apex.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Has influence over ZSK key lifetime.
+.UNINDENT
+.UNINDENT
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+Ensure all DNSKEYs with updated TTL are propagated before any subsequent
+DNSKEY rollover starts.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP zone SOA TTL
+.SS zone\-max\-ttl
+.sp
+Declare (override) maximal TTL value among all the records in zone.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+It\(aqs generally recommended to override the maximal TTL computation by setting this
+explicitly whenever possible. It\(aqs required for DNSSEC Offline KSK and
+really reasonable when records are generated dynamically
+(e.g. by a module).
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP computed after zone is loaded
+.SS ksk\-lifetime
+.sp
+A period between KSK activation and the next rollover initiation.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+KSK key lifetime is also influenced by propagation\-delay, dnskey\-ttl,
+and KSK submission delay.
+.sp
+Zero (aka infinity) value causes no KSK rollover as a result.
+.sp
+This applies for CSK lifetime if single\-type\-signing is enabled.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB0\fP
+.SS zsk\-lifetime
+.sp
+A period between ZSK activation and the next rollover initiation.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+More exactly, this period is measured since a ZSK is activated,
+and after this, a new ZSK is generated to replace it within
+following roll\-over.
+.sp
+ZSK key lifetime is also influenced by propagation\-delay and dnskey\-ttl
+.sp
+Zero (aka infinity) value causes no ZSK rollover as a result.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB30d\fP (30 days)
+.SS delete\-delay
+.sp
+Once a key (KSK or ZSK) is rolled\-over and removed from the zone,
+keep it in the KASP database for at least this period before deleting it completely.
+This might be useful in some troubleshooting cases when resurrection
+is needed.
+.sp
+\fIDefault:\fP \fB0\fP
+.SS propagation\-delay
+.sp
+An extra delay added for each key rollover step. This value should be high
+enough to cover propagation of data from the primary server to all
+secondary servers, as well as the duration of signing routine itself and
+possible outages in signing and propagation infrastructure. In other words,
+this delay should ensure that within this period of time after planned
+change of the key set, all public\-facing secondaries will already serve
+new DNSKEY RRSet for sure.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Has influence over ZSK key lifetime.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB1h\fP (1 hour)
+.SS rrsig\-lifetime
+.sp
+A validity period of newly issued signatures.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+The RRSIG\(aqs signature inception time is set to 90 minutes in the past. This
+time period is not counted to the signature lifetime.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB14d\fP (14 days)
+.SS rrsig\-refresh
+.sp
+A period how long at least before a signature expiration the signature will be refreshed,
+in order to prevent expired RRSIGs on secondary servers or resolvers\(aq caches.
+.sp
+\fIDefault:\fP \fI\%propagation\-delay\fP + \fI\%zone\-max\-ttl\fP
+.SS rrsig\-pre\-refresh
+.sp
+A period how long at most before a signature refresh time the signature might be refreshed,
+in order to refresh RRSIGs in bigger batches on a frequently updated zone
+(avoid re\-sign event too often).
+.sp
+\fIDefault:\fP \fB1h\fP (1 hour)
+.SS reproducible\-signing
+.sp
+For ECDSA algorithms, generate RRSIG signatures deterministically (\fI\%RFC 6979\fP).
+Besides better theoretical cryptographic security, this mode allows significant
+speed\-up of loading signed (by the same method) zones. However, the zone signing
+is a bit slower.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS nsec3
+.sp
+Specifies if NSEC3 will be used instead of NSEC.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS nsec3\-iterations
+.sp
+A number of additional times the hashing is performed.
+.sp
+\fIDefault:\fP \fB0\fP
+.SS nsec3\-opt\-out
+.sp
+If set, NSEC3 records won\(aqt be created for insecure delegations.
+This speeds up the zone signing and reduces overall zone size.
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+NSEC3 with the Opt\-Out bit set no longer works as a proof of non\-existence
+in this zone.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS nsec3\-salt\-length
+.sp
+A length of a salt field in octets, which is appended to the original owner
+name before hashing.
+.sp
+\fIDefault:\fP \fB8\fP
+.SS nsec3\-salt\-lifetime
+.sp
+A validity period of newly issued salt field.
+.sp
+Zero value means infinity.
+.sp
+Special value \fI\-1\fP triggers re\-salt every time when active ZSK changes.
+This optimizes the number of big changes to the zone.
+.sp
+\fIDefault:\fP \fB30d\fP (30 days)
+.SS signing\-threads
+.sp
+When signing zone or update, use this number of threads for parallel signing.
+.sp
+Those are extra threads independent of \fI\%Background workers\fP\&.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Some steps of the DNSSEC signing operation are not parallelized.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB1\fP (no extra threads)
+.SS ksk\-submission
+.sp
+A reference to \fI\%submission\fP section holding parameters of
+KSK submission checks.
+.sp
+\fIDefault:\fP not set
+.SS ds\-push
+.sp
+Optional references \fI\%remote\fP and \fI\%remotes\fP
+to authoritative DNS server of the
+parent\(aqs zone. The remote server must be configured to accept DS record
+updates via DDNS. Whenever a CDS record in the local zone is changed, the
+corresponding DS record is sent as a dynamic update (DDNS) to the parent
+DNS server. All previous DS records are deleted within the DDNS message.
+It\(aqs possible to manage both child and parent zones by the same Knot DNS server.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This feature requires \fI\%cds\-cdnskey\-publish\fP
+not to be set to \fBnone\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+The mentioned change to CDS record usually means that a KSK roll\-over is running
+and the new key being rolled\-in is in "ready" state already for the period of
+\fI\%propagation\-delay\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Module Onlinesign doesn\(aqt support DS push.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS cds\-cdnskey\-publish
+.sp
+Controls if and how shall the CDS and CDNSKEY be published in the zone.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – Never publish any CDS or CDNSKEY records in the zone.
+.IP \(bu 2
+\fBdelete\-dnssec\fP – Publish special CDS and CDNSKEY records indicating turning off DNSSEC.
+.IP \(bu 2
+\fBrollover\fP – Publish CDS and CDNSKEY records for ready and not yet active KSK (submission phase of KSK rollover).
+.IP \(bu 2
+\fBalways\fP – Always publish one CDS and one CDNSKEY records for the current KSK.
+.IP \(bu 2
+\fBdouble\-ds\fP – Always publish up to two CDS and two CDNSKEY records for ready and/or active KSKs.
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If the zone keys are managed manually, the CDS and CDNSKEY rrsets may contain
+more records depending on the keys available.
+.UNINDENT
+.UNINDENT
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+The \fBdouble\-ds\fP value does not trigger double\-DS roll\-over method. That method is
+only supported when performed manually, with unset \fI\%ksk\-submission\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBrollover\fP
+.SS cds\-digest\-type
+.sp
+Specify digest type for published CDS records.
+.sp
+\fIDefault:\fP \fBsha256\fP
+.SS dnskey\-management
+.sp
+Specify how the DNSKEY, CDNSKEY, and CDS RRSets at the zone apex are handled
+when (re\-)signing the zone.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBfull\fP – Upon every zone (re\-)sign, delete all unknown DNSKEY, CDNSKEY, and CDS
+records and keep just those that are related to the zone keys stored in the KASP database.
+.IP \(bu 2
+\fBincremental\fP – Keep unknown DNSKEY, CDNSKEY, and CDS records in the zone, and
+modify server\-managed records incrementally by employing changes in the KASP database.
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Prerequisites for \fIincremental\fP:
+.INDENT 0.0
+.IP \(bu 2
+The Offline KSK isn\(aqt supported.
+.IP \(bu 2
+The \fI\%delete\-delay\fP is long enough to cover possible daemon
+shutdown (e.g. due to server maintenance).
+.IP \(bu 2
+Avoided manual deletion of keys with keymgr\&.
+.UNINDENT
+.sp
+Otherwise there might remain some DNSKEY records in the zone, belonging to
+deleted keys.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBfull\fP
+.SS offline\-ksk
+.sp
+Specifies if Offline KSK feature is enabled.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS unsafe\-operation
+.sp
+Turn off some DNSSEC safety features.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – Nothing disabled.
+.IP \(bu 2
+\fBno\-check\-keyset\fP – Don\(aqt check active keys in present algorithms. This may
+lead to violation of \fI\%RFC 4035#section\-2.2\fP\&.
+.IP \(bu 2
+\fBno\-update\-dnskey\fP – Don\(aqt maintain/update DNSKEY, CDNSKEY, and CDS records
+in the zone apex according to KASP database. Juste leave them as they are in the zone.
+.IP \(bu 2
+\fBno\-update\-nsec\fP – Don\(aqt maintain/update NSEC/NSEC3 chain. Leave all the records
+as they are in the zone.
+.IP \(bu 2
+\fBno\-update\-expired\fP – Don\(aqt update expired RRSIGs.
+.UNINDENT
+.sp
+Multiple values may be specified.
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+This mode is intended for DNSSEC experts who understand the corresponding consequences.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBnone\fP
+.SH TEMPLATE SECTION
+.sp
+A template is shareable zone settings, which can simplify configuration by
+reducing duplicates. A special default template (with the \fIdefault\fP identifier)
+can be used for global zone configuration or as an implicit configuration
+if a zone doesn\(aqt have another template specified.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+template:
+ \- id: STR
+ global\-module: STR/STR ...
+ # All zone options (excluding \(aqtemplate\(aq item)
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If an item is explicitly specified both in the referenced template and
+the zone, the template item value is overridden by the zone item value.
+.UNINDENT
+.UNINDENT
+.SS id
+.sp
+A template identifier.
+.SS global\-module
+.sp
+An ordered list of references to query modules in the form of \fImodule_name\fP or
+\fImodule_name/module_id\fP\&. These modules apply to all queries.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option is only available in the \fIdefault\fP template.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SH ZONE SECTION
+.sp
+Definition of zones served by the server.
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+zone:
+ \- domain: DNAME
+ template: template_id
+ storage: STR
+ file: STR
+ master: remote_id | remotes_id ...
+ ddns\-master: remote_id
+ notify: remote_id | remotes_id ...
+ acl: acl_id ...
+ provide\-ixfr: BOOL
+ semantic\-checks: BOOL | soft
+ zonefile\-sync: TIME
+ zonefile\-load: none | difference | difference\-no\-serial | whole
+ journal\-content: none | changes | all
+ journal\-max\-usage: SIZE
+ journal\-max\-depth: INT
+ zone\-max\-size : SIZE
+ adjust\-threads: INT
+ dnssec\-signing: BOOL
+ dnssec\-validation: BOOL
+ dnssec\-policy: policy_id
+ ds\-push: remote_id | remotes_id ...
+ zonemd\-verify: BOOL
+ zonemd\-generate: none | zonemd\-sha384 | zonemd\-sha512 | remove
+ serial\-policy: increment | unixtime | dateserial
+ refresh\-min\-interval: TIME
+ refresh\-max\-interval: TIME
+ retry\-min\-interval: TIME
+ retry\-max\-interval: TIME
+ expire\-min\-interval: TIME
+ expire\-max\-interval: TIME
+ catalog\-role: none | interpret | generate | member
+ catalog\-template: template_id ...
+ catalog\-zone: DNAME
+ catalog\-group: STR
+ module: STR/STR ...
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS domain
+.sp
+A zone name identifier.
+.SS template
+.sp
+A \fI\%reference\fP to a configuration template.
+.sp
+\fIDefault:\fP not set or \fBdefault\fP (if the template exists)
+.SS storage
+.sp
+A data directory for storing zone files.
+.sp
+\fIDefault:\fP \fB${localstatedir}/lib/knot\fP (configured with \fB\-\-with\-storage=path\fP)
+.SS file
+.sp
+A path to the zone file. Non\-absolute path (i.e. not starting with \fB/\fP) is
+relative to \fI\%storage\fP\&.
+It is also possible to use the following formatters:
+.INDENT 0.0
+.IP \(bu 2
+\fB%c[\fP\fIN\fP\fB]\fP or \fB%c[\fP\fIN\fP\fB\-\fP\fIM\fP\fB]\fP – Means the \fIN\fPth
+character or a sequence of characters beginning from the \fIN\fPth and ending
+with the \fIM\fPth character of the textual zone name (see \fB%s\fP). The
+indexes are counted from 0 from the left. All dots (including the terminal
+one) are considered. If the character is not available, the formatter has no effect.
+.IP \(bu 2
+\fB%l[\fP\fIN\fP\fB]\fP – Means the \fIN\fPth label of the textual zone name
+(see \fB%s\fP). The index is counted from 0 from the right (0 ~ TLD).
+If the label is not available, the formatter has no effect.
+.IP \(bu 2
+\fB%s\fP – Means the current zone name in the textual representation.
+The zone name doesn\(aqt include the terminating dot (the result for the root
+zone is the empty string!).
+.IP \(bu 2
+\fB%%\fP – Means the \fB%\fP character.
+.UNINDENT
+.sp
+\fBWARNING:\fP
+.INDENT 0.0
+.INDENT 3.5
+Beware of special characters which are escaped or encoded in the \eDDD form
+where DDD is corresponding decimal ASCII code.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fI\%storage\fP\fB/%s.zone\fP
+.SS master
+.sp
+An ordered list of references \fI\%remote\fP and
+\fI\%remotes\fP to zone primary servers
+(formerly known as master servers).
+.sp
+\fIDefault:\fP not set
+.SS ddns\-master
+.sp
+A \fI\%reference\fP to zone primary master. If not specified,
+the first \fI\%master\fP server is used.
+.sp
+\fIDefault:\fP not set
+.SS notify
+.sp
+An ordered list of references \fI\%remote\fP and
+\fI\%remotes\fP to secondary servers to which notify
+message is sent if the zone changes.
+.sp
+\fIDefault:\fP not set
+.SS acl
+.sp
+An ordered list of \fI\%references\fP to ACL rules which can allow
+or disallow zone transfers, updates or incoming notifies.
+.sp
+\fIDefault:\fP not set
+.SS provide\-ixfr
+.sp
+If disabled, the server is forced to respond with AXFR to IXFR queries.
+If enabled, IXFR requests are responded normally.
+.sp
+\fIDefault:\fP \fBon\fP
+.SS semantic\-checks
+.sp
+Selects if extra zone semantic checks are used or impacts of the mandatory checks.
+.sp
+There are several mandatory checks which are always enabled and cannot be turned
+off. An error in a mandatory check causes the zone not to be loaded. Most of
+the mandatory checks can be weakened by setting \fBsoft\fP, which allows the zone to
+be loaded even if the check fails.
+.sp
+If enabled, extra checks are used. These checks don\(aqt prevent the zone from loading.
+.sp
+The mandatory checks are applied to zone files, zone transfers, and updates via
+control interface. The extra checks are applied to zone files only!
+.sp
+Mandatory checks:
+.INDENT 0.0
+.IP \(bu 2
+Missing SOA record at the zone apex (\fI\%RFC 1034\fP) (*)
+.IP \(bu 2
+An extra record exists together with a CNAME record except for RRSIG and NSEC (\fI\%RFC 1034\fP)
+.IP \(bu 2
+Multiple CNAME records with the same owner exist (\fI\%RFC 1034\fP)
+.IP \(bu 2
+DNAME record having a record under it (\fI\%RFC 6672\fP)
+.IP \(bu 2
+Multiple DNAME records with the same owner exist (\fI\%RFC 6672\fP)
+.IP \(bu 2
+NS record exists together with a DNAME record (\fI\%RFC 6672\fP)
+.UNINDENT
+.sp
+(*) The marked check can\(aqt be weakened by the soft mode. All other mandatory checks
+are subject to the optional soft mode.
+.sp
+Extra checks:
+.INDENT 0.0
+.IP \(bu 2
+Missing NS record at the zone apex
+.IP \(bu 2
+Missing glue A or AAAA record
+.IP \(bu 2
+Invalid DS or NSEC3PARAM record
+.IP \(bu 2
+CDS or CDNSKEY inconsistency
+.IP \(bu 2
+All other DNSSEC checks executed during \fI\%dnssec\-validation\fP
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+The soft mode allows the refresh event to ignore a CNAME response to a SOA
+query (malformed message) and triggers a zone bootstrap instead.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS zonefile\-sync
+.sp
+The time after which the current zone in memory will be synced with a zone file
+on the disk (see \fI\%file\fP). The server will serve the latest
+zone even after a restart using zone journal, but the zone file on the disk will
+only be synced after \fBzonefile\-sync\fP time has expired (or after manual zone
+flush). This is applicable when the zone is updated via IXFR, DDNS or automatic
+DNSSEC signing. In order to completely disable automatic zone file synchronization,
+set the value to \-1. In that case, it is still possible to force a manual zone flush
+using the \fB\-f\fP option.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If you are serving large zones with frequent updates where
+the immediate sync with a zone file is not desirable, increase the value.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB0\fP (immediate)
+.SS zonefile\-load
+.sp
+Selects how the zone file contents are applied during zone load.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – The zone file is not used at all.
+.IP \(bu 2
+\fBdifference\fP – If the zone contents are already available during server start or reload,
+the difference is computed between them and the contents of the zone file. This difference
+is then checked for semantic errors and applied to the current zone contents.
+.IP \(bu 2
+\fBdifference\-no\-serial\fP – Same as \fBdifference\fP, but the SOA serial in the zone file is
+ignored, the server takes care of incrementing the serial automatically.
+.IP \(bu 2
+\fBwhole\fP – Zone contents are loaded from the zone file.
+.UNINDENT
+.sp
+When \fBdifference\fP is configured and there are no zone contents yet (cold start
+and no zone contents in the journal), it behaves the same way as \fBwhole\fP\&.
+.sp
+\fIDefault:\fP \fBwhole\fP
+.SS journal\-content
+.sp
+Selects how the journal shall be used to store zone and its changes.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – The journal is not used at all.
+.IP \(bu 2
+\fBchanges\fP – Zone changes history is stored in journal.
+.IP \(bu 2
+\fBall\fP – Zone contents and history is stored in journal.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBchanges\fP
+.SS journal\-max\-usage
+.sp
+Policy how much space in journal DB will the zone\(aqs journal occupy.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Journal DB may grow far above the sum of journal\-max\-usage across
+all zones, because of DB free space fragmentation.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fB100M\fP (100 MiB)
+.SS journal\-max\-depth
+.sp
+Maximum history length of the journal.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Zone\-in\-journal changeset isn\(aqt counted to the limit.
+.UNINDENT
+.UNINDENT
+.sp
+\fIMinimum:\fP \fB2\fP
+.sp
+\fIDefault:\fP \fB20\fP
+.SS zone\-max\-size
+.sp
+Maximum size of the zone. The size is measured as size of the zone records
+in wire format without compression. The limit is enforced for incoming zone
+transfers and dynamic updates.
+.sp
+For incremental transfers (IXFR), the effective limit for the total size of
+the records in the transfer is twice the configured value. However the final
+size of the zone must satisfy the configured value.
+.sp
+\fIDefault:\fP unlimited
+.SS adjust\-threads
+.sp
+Parallelize internal zone adjusting procedures by using specified number of
+threads. This is useful with huge zones with NSEC3. Speedup observable at
+server startup and while processing NSEC3 re\-salt.
+.sp
+\fIDefault:\fP \fB1\fP (no extra threads)
+.SS dnssec\-signing
+.sp
+If enabled, automatic DNSSEC signing for the zone is turned on.
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS dnssec\-validation
+.sp
+If enabled, the zone contents are validated for being correctly signed
+(including NSEC/NSEC3 chain) with DNSSEC signatures every time the zone
+is loaded or changed (including AXFR/IXFR).
+.sp
+When the validation fails, the zone being loaded or update being applied
+is cancelled with an error, and either none or previous zone state is published.
+.sp
+List of DNSSEC checks:
+.INDENT 0.0
+.IP \(bu 2
+Every zone RRSet is correctly signed by at least one present DNSKEY.
+.IP \(bu 2
+DNSKEY RRSet is signed by KSK.
+.IP \(bu 2
+NSEC(3) RR exists for each name (unless opt\-out) with correct bitmap.
+.IP \(bu 2
+Every NSEC(3) RR is linked to the lexicographically next one.
+.UNINDENT
+.sp
+The validation is not affected by \fI\%dnssec\-policy\fP configuration,
+except for \fI\%signing\-threads\fP option, which specifies the number
+of threads for parallel validation.
+.sp
+\fIDefault:\fP not set
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Redundant or garbage NSEC3 records are ignored.
+.sp
+This mode is not compatible with \fI\%dnssec\-signing\fP\&.
+.UNINDENT
+.UNINDENT
+.SS dnssec\-policy
+.sp
+A \fI\%reference\fP to DNSSEC signing policy.
+.sp
+\fIDefault:\fP an imaginary policy with all default values
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+A configured policy called "default" won\(aqt be used unless explicitly referenced.
+.UNINDENT
+.UNINDENT
+.SS ds\-push
+.sp
+Per zone configuration of \fI\%ds\-push\fP\&. This option overrides possible
+per policy option.
+.sp
+\fIDefault:\fP not set
+.SS zonemd\-verify
+.sp
+On each zone load/update, verify that ZONEMD is present in the zone and valid.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+Zone digest calculation may take much time and CPU on large zones.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBoff\fP
+.SS zonemd\-generate
+.sp
+On each zone update, calculate ZONEMD and put it into the zone.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – No action regarding ZONEMD.
+.IP \(bu 2
+\fBzonemd\-sha384\fP – Generate ZONEMD using SHA384 algorithm.
+.IP \(bu 2
+\fBzonemd\-sha512\fP – Generate ZONEMD using SHA512 algorithm.
+.IP \(bu 2
+\fBremove\fP – Remove any ZONEMD from the zone apex.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBnone\fP
+.SS serial\-policy
+.sp
+Specifies how the zone serial is updated after a dynamic update or
+automatic DNSSEC signing. If the serial is changed by the dynamic update,
+no change is made.
+.sp
+Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBincrement\fP – The serial is incremented according to serial number arithmetic.
+.IP \(bu 2
+\fBunixtime\fP – The serial is set to the current unix time.
+.IP \(bu 2
+\fBdateserial\fP – The 10\-digit serial (YYYYMMDDnn) is incremented, the first
+8 digits match the current iso\-date.
+.UNINDENT
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+If the resulting serial for \fBunixtime\fP or \fBdateserial\fP is lower than or
+equal to the current serial (this happens e.g. when migrating from other policy or
+frequent updates), the serial is incremented instead.
+.sp
+To avoid user confusion, use \fBdateserial\fP only if you expect at most
+100 updates per day per zone and \fBunixtime\fP only if you expect at most
+one update per second per zone.
+.sp
+Generated catalog zones use \fBunixtime\fP only.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP \fBincrement\fP (\fBunixtime\fP for generated catalog zones)
+.SS refresh\-min\-interval
+.sp
+Forced minimum zone refresh interval (in seconds) to avoid flooding primary server.
+.sp
+\fIMinimum:\fP \fB2\fP
+.sp
+\fIDefault:\fP \fB2\fP
+.SS refresh\-max\-interval
+.sp
+Forced maximum zone refresh interval (in seconds).
+.sp
+\fIDefault:\fP not set
+.SS retry\-min\-interval
+.sp
+Forced minimum zone retry interval (in seconds) to avoid flooding primary server.
+.sp
+\fIMinimum:\fP \fB1\fP
+.sp
+\fIDefault:\fP \fB1\fP
+.SS retry\-max\-interval
+.sp
+Forced maximum zone retry interval (in seconds).
+.sp
+\fIDefault:\fP not set
+.SS expire\-min\-interval
+.sp
+Forced minimum zone expire interval (in seconds) to avoid flooding primary server.
+.sp
+\fIMinimum:\fP \fB3\fP
+.sp
+\fIDefault:\fP \fB3\fP
+.SS expire\-max\-interval
+.sp
+Forced maximum zone expire interval (in seconds).
+.sp
+\fIDefault:\fP not set
+.SS catalog\-role
+.sp
+Trigger zone catalog feature. Possible values:
+.INDENT 0.0
+.IP \(bu 2
+\fBnone\fP – Not a catalog zone.
+.IP \(bu 2
+\fBinterpret\fP – A catalog zone which is loaded from a zone file or XFR,
+and member zones shall be configured based on its contents.
+.IP \(bu 2
+\fBgenerate\fP – A catalog zone whose contents are generated according to
+assigned member zones.
+.IP \(bu 2
+\fBmember\fP – A member zone that is assigned to one generated catalog zone.
+.UNINDENT
+.sp
+\fIDefault:\fP \fBnone\fP
+.SS catalog\-template
+.sp
+For the catalog member zones, the specified configuration template will be applied.
+.sp
+Multiple catalog templates may be defined. The first one is used unless the member zone
+has the \fIgroup\fP property defined, matching another catalog template.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option must be set if and only if \fI\%catalog\-role\fP is \fIinterpret\fP\&.
+.sp
+Nested catalog zones aren\(aqt supported. Therefore catalog templates can\(aqt use
+\fI\%catalog\-template\fP, \fI\%catalog\-role\fP, \fI\%catalog\-zone\fP,
+and \fI\%catalog\-group\fP options.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS catalog\-zone
+.sp
+Assign this member zone to specified generated catalog zone.
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option must be set if and only if \fI\%catalog\-role\fP is \fImember\fP\&.
+.sp
+The referenced catalog zone must exist and have \fI\%catalog\-role\fP set to \fIgenerate\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS catalog\-group
+.sp
+Assign this member zone to specified catalog group (configuration template).
+.sp
+\fBNOTE:\fP
+.INDENT 0.0
+.INDENT 3.5
+This option has effect if and only if \fI\%catalog\-role\fP is \fImember\fP\&.
+.UNINDENT
+.UNINDENT
+.sp
+\fIDefault:\fP not set
+.SS module
+.sp
+An ordered list of references to query modules in the form of \fImodule_name\fP or
+\fImodule_name/module_id\fP\&. These modules apply only to the current zone queries.
+.sp
+\fIDefault:\fP not set
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/knotc.8in b/doc/man/knotc.8in
new file mode 100644
index 0000000..9a88274
--- /dev/null
+++ b/doc/man/knotc.8in
@@ -0,0 +1,425 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KNOTC" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+knotc \- Knot DNS control utility
+.SH SYNOPSIS
+.sp
+\fBknotc\fP [\fIparameters\fP] \fIaction\fP [\fIaction_args\fP]
+.SH DESCRIPTION
+.sp
+This program controls a running \fIknotd\fP process using a socket.
+.sp
+If an \fIaction\fP is specified, it is performed and \fIknotc\fP exits, otherwise the program
+is executed in the interactive mode.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.TP
+\fB\-m\fP, \fB\-\-max\-conf\-size\fP \fIMiB\fP
+Set maximum size of the configuration database
+(default is @conf_mapsize@ MiB, maximum 10000 MiB).
+.TP
+\fB\-s\fP, \fB\-\-socket\fP \fIpath\fP
+Use a control UNIX socket path (default is \fB@run_dir@/knot.sock\fP).
+.TP
+\fB\-t\fP, \fB\-\-timeout\fP \fIseconds\fP
+Use a control timeout in seconds. Set to 0 for infinity (default is 60).
+The control socket operations are also subject to the timeout
+parameter set on the server side in server\(aqs Control configuration section.
+.TP
+\fB\-b\fP, \fB\-\-blocking\fP
+Zone event trigger commands wait until the event is finished. Control timeout
+is set to infinity if not forced by explicit timeout specification.
+.TP
+\fB\-e\fP, \fB\-\-extended\fP
+Show extended output (even empty items in zone status).
+.TP
+\fB\-f\fP, \fB\-\-force\fP
+Forced operation. Overrides some checks.
+.TP
+\fB\-x\fP, \fB\-\-mono\fP
+Don\(aqt generate colorized output.
+.TP
+\fB\-X\fP, \fB\-\-color\fP
+Force colorized output in extended output or to a pipe.
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Enable debug output.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SS Actions
+.INDENT 0.0
+.TP
+\fBstatus\fP [\fIdetail\fP]
+Check if the server is running. Details are \fBversion\fP for the running
+server version, \fBworkers\fP for the numbers of worker threads,
+or \fBconfigure\fP for the configure summary.
+.TP
+\fBstop\fP
+Stop the server if running.
+.TP
+\fBreload\fP
+Reload the server configuration and modified zone files. All open zone
+transactions will be aborted!
+.TP
+\fBstats\fP [\fImodule\fP[\fB\&.\fP\fIcounter\fP]]
+Show global statistics counter(s). To print also counters with value 0, use
+force option.
+.TP
+\fBzone\-check\fP [\fIzone\fP\&...]
+Test if the server can load the zone. Semantic checks are executed if enabled
+in the configuration. If invoked with the force option, an error is returned
+when semantic check warning appears. (*)
+.TP
+\fBzone\-status\fP [\fIzone\fP\&...] [\fIfilter\fP]
+Show the zone status. Filters are \fB+role\fP, \fB+serial\fP, \fB+transaction\fP,
+\fB+events\fP, \fB+freeze\fP, and \fB+catalog\fP\&. Empty zone parameters are omitted,
+unless the \fB\-\-extended\fP option is used. A single dash in the output represents
+an unset value. Automatic colorization can be overruled using the \fB\-\-mono\fP and
+\fB\-\-color\fP options.
+.sp
+The color code is:
+\fIgreen\fP \- zone acts as a master / \fIred\fP \- zone acts as a slave,
+\fIbold font (highlited)\fP \- zone is active / \fInormal\fP \- zone is empty,
+\fIunderscored\fP \- zone is an interpreted catalog member.
+.TP
+\fBzone\-reload\fP [\fIzone\fP\&...]
+Trigger a zone reload from a disk without checking its modification time. For
+secondary zone, the refresh event from primary server(s) is scheduled;
+for primary zone, the notify event to secondary server(s) is scheduled. An open
+zone transaction will be aborted! If invoked with the force option, also zone
+modules will be re\-loaded, but blocking mode might not work reliably. (#)
+.TP
+\fBzone\-refresh\fP [\fIzone\fP\&...]
+Trigger a check for the zone serial on the zone\(aqs primary server. If
+the primary server has a newer zone, a transfer is scheduled. This command is
+valid for secondary zones. (#)
+.TP
+\fBzone\-retransfer\fP [\fIzone\fP\&...]
+Trigger a zone transfer from the zone\(aqs primary server. The server
+doesn\(aqt check the serial of the primary server\(aqs zone. This command is valid
+for secondary zones. (#)
+.TP
+\fBzone\-notify\fP [\fIzone\fP\&...]
+Trigger a NOTIFY message to all configured remotes. This can help in cases
+when previous NOTIFY had been lost or the secondary servers have been
+offline. (#)
+.TP
+\fBzone\-flush\fP [\fIzone\fP\&...] [\fB+outdir\fP \fIdirectory\fP]
+Trigger a zone journal flush to the configured zone file. If an output
+directory is specified, the current zone is immediately dumped (in the
+blocking mode) to a zone file in the specified directory. See
+\fI\%Notes\fP below about the directory permissions. (#)
+.TP
+\fBzone\-backup\fP [\fIzone\fP\&...] \fB+backupdir\fP \fIdirectory\fP [\fIfilter\fP\&...]
+Trigger a zone data and metadata backup to a specified directory.
+Available filters are \fB+zonefile\fP, \fB+journal\fP, \fB+timers\fP, \fB+kaspdb\fP,
+\fB+catalog\fP, and their negative counterparts \fB+nozonefile\fP, \fB+nojournal\fP,
+\fB+notimers\fP, \fB+nokaspdb\fP, and \fB+nocatalog\fP\&. With these filters set,
+zone contents, zone\(aqs journal, zone related timers, zone related data in the
+KASP database together with keys, and zone\(aqs catalog, respectively, are backed up,
+or omitted from the backup. By default, filters \fB+zonefile\fP, \fB+timers\fP,
+\fB+kaspdb\fP, \fB+catalog\fP, and \fB+nojournal\fP are set. Setting a filter
+for an item doesn\(aqt change default settings for other items. If zone flushing
+is disabled, original zone file is backed up instead of writing out zone
+contents to a file. See \fI\%Notes\fP below about the directory
+permissions. (#)
+.TP
+\fBzone\-restore\fP [\fIzone\fP\&...] \fB+backupdir\fP \fIdirectory\fP [\fIfilter\fP\&...]
+Trigger a zone data and metadata restore from a specified backup directory.
+Optional filters are equivalent to the same filters of \fBzone\-backup\fP\&.
+Restore from backups created by Knot DNS releases prior to 3.1 is possible
+with the force option. See \fI\%Notes\fP below about the directory
+permissions. (#)
+.TP
+\fBzone\-sign\fP [\fIzone\fP\&...]
+Trigger a DNSSEC re\-sign of the zone. Existing signatures will be dropped.
+This command is valid for zones with DNSSEC signing enabled. (#)
+.TP
+\fBzone\-keys\-load\fP [\fIzone\fP\&...]
+Trigger a load of DNSSEC keys and other signing material from KASP database
+(which might have been altered manually). If suitable, re\-sign the zone
+afterwards (keeping valid signatures intact). (#)
+.TP
+\fBzone\-key\-rollover\fP \fIzone\fP \fIkey_type\fP
+Trigger immediate key rollover. Publish new key and start a key rollover,
+even when the key has a lifetime to go. Key type can be \fBksk\fP (also for CSK)
+or \fBzsk\fP\&. This command is valid for zones with DNSSEC signing and automatic
+key management enabled. Note that complete key rollover consists of several steps
+and the blocking mode relates to the initial one only! (#)
+.TP
+\fBzone\-ksk\-submitted\fP \fIzone\fP\&...
+Use when the zone\(aqs KSK rollover is in submission phase. By calling this command
+the user confirms manually that the parent zone contains DS record for the new
+KSK in submission phase and the old KSK can be retired. (#)
+.TP
+\fBzone\-freeze\fP [\fIzone\fP\&...]
+Trigger a zone freeze. All running events will be finished and all new and pending
+(planned) zone\-changing events (load, refresh, update, flush, and DNSSEC signing)
+will be held up until the zone is thawed. (#)
+.TP
+\fBzone\-thaw\fP [\fIzone\fP\&...]
+Trigger dismissal of zone freeze. (#)
+.TP
+\fBzone\-xfr\-freeze\fP [\fIzone\fP\&...]
+Temporarily disable outgoing AXFR/IXFR for the zone(s). (#)
+.TP
+\fBzone\-xfr\-thaw\fP [\fIzone\fP\&...]
+Dismiss outgoing XFR freeze. (#)
+.TP
+\fBzone\-read\fP \fIzone\fP [\fIowner\fP [\fItype\fP]]
+Get zone data that are currently being presented.
+.TP
+\fBzone\-begin\fP \fIzone\fP\&...
+Begin a zone transaction.
+.TP
+\fBzone\-commit\fP \fIzone\fP\&...
+Commit the zone transaction. All changes are applied to the zone.
+.TP
+\fBzone\-abort\fP \fIzone\fP\&...
+Abort the zone transaction. All changes are discarded.
+.TP
+\fBzone\-diff\fP \fIzone\fP
+Get zone changes within the transaction.
+.TP
+\fBzone\-get\fP \fIzone\fP [\fIowner\fP [\fItype\fP]]
+Get zone data within the transaction.
+.TP
+\fBzone\-set\fP \fIzone\fP \fIowner\fP [\fIttl\fP] \fItype\fP \fIrdata\fP
+Add zone record within the transaction. The first record in a rrset
+requires a ttl value specified.
+.TP
+\fBzone\-unset\fP \fIzone\fP \fIowner\fP [\fItype\fP [\fIrdata\fP]]
+Remove zone data within the transaction.
+.TP
+\fBzone\-purge\fP \fIzone\fP\&... [\fB+orphan\fP] [\fIfilter\fP\&...]
+Purge zone data, zone file, journal, timers, and/or KASP data of specified zones.
+Available filters are \fB+expire\fP, \fB+zonefile\fP, \fB+journal\fP, \fB+timers\fP,
+\fB+kaspdb\fP, and \fB+catalog\fP\&. If no filter is specified, all filters are enabled.
+If the zone is no longer configured, add \fB+orphan\fP parameter (zone file cannot
+be purged in this case). When purging orphans, always check the server log for
+possible errors. This command always requires the force option. (#)
+.TP
+\fBzone\-stats\fP \fIzone\fP [\fImodule\fP[\fB\&.\fP\fIcounter\fP]]
+Show zone statistics counter(s). To print also counters with value 0, use
+force option.
+.TP
+\fBconf\-init\fP
+Initialize the configuration database. If the database doesn\(aqt exist yet,
+execute this command as an intended user to ensure the server is permitted
+to access the database (e.g. \fIsudo \-u knot knotc conf\-init\fP). (*)
+.TP
+\fBconf\-check\fP
+Check the server configuration. (*)
+.TP
+\fBconf\-import\fP \fIfilename\fP
+Import a configuration file into the configuration database. If the database
+doesn\(aqt exist yet, execute this command as an intended user to ensure the server
+is permitted to access the database (e.g. \fIsudo \-u knot knotc conf\-import ...\fP).
+Also ensure the server is not using the configuration database at the same time! (*)
+.TP
+\fBconf\-export\fP [\fIfilename\fP]
+Export the configuration database into a config file or stdout. (*)
+.TP
+\fBconf\-list\fP [\fIitem\fP]
+List the configuration database sections or section items.
+.TP
+\fBconf\-read\fP [\fIitem\fP]
+Read the item from the active configuration database.
+.TP
+\fBconf\-begin\fP
+Begin a writing configuration database transaction. Only one transaction
+can be opened at a time.
+.TP
+\fBconf\-commit\fP
+Commit the configuration database transaction.
+.TP
+\fBconf\-abort\fP
+Rollback the configuration database transaction.
+.TP
+\fBconf\-diff\fP [\fIitem\fP]
+Get the item difference in the transaction.
+.TP
+\fBconf\-get\fP [\fIitem\fP]
+Get the item data from the transaction.
+.TP
+\fBconf\-set\fP \fIitem\fP [\fIdata\fP\&...]
+Set the item data in the transaction.
+.TP
+\fBconf\-unset\fP [\fIitem\fP] [\fIdata\fP\&...]
+Unset the item data in the transaction.
+.UNINDENT
+.SS Notes
+.sp
+Empty or \fB\-\-\fP \fIzone\fP parameter means all zones or all zones with a transaction.
+.sp
+Use \fB@\fP \fIowner\fP to denote the zone name.
+.sp
+Type \fIitem\fP parameter in the form of \fIsection\fP[\fB[\fP\fIid\fP\fB]\fP][\fB\&.\fP\fIname\fP].
+.sp
+(*) indicates a local operation which requires a configuration.
+.sp
+(#) indicates an optionally blocking operation.
+.sp
+The \fB\-b\fP and \fB\-f\fP options can be placed right after the command name.
+.sp
+Responses returned by \fIknotc\fP commands depend on the mode:
+.INDENT 0.0
+.IP \(bu 2
+In the blocking mode, \fIknotc\fP reports if an error occurred during processing
+of the command by the server. If an error is reported, a more detailed information
+about the failure can usually be found in the server log.
+.IP \(bu 2
+In the non\-blocking (default) mode, \fIknotc\fP doesn\(aqt report processing errors.
+The \fIOK\fP response to triggering commands means that the command has been successfully
+sent to the server. To verify if the operation succeeded, it\(aqs necessary to
+check the server log.
+.UNINDENT
+.sp
+Actions \fBzone\-flush\fP, \fBzone\-backup\fP, and \fBzone\-restore\fP are carried out by
+the \fIknotd\fP process. The directory specified must be accessible to the user account
+that \fIknotd\fP runs under and if the directory already exists, its permissions must be
+appropriate for that user account.
+.SS Interactive mode
+.sp
+The utility provides interactive mode with basic line editing functionality,
+command completion, and command history.
+.sp
+Interactive mode behavior can be customized in \fI~/.editrc\fP\&. Refer to
+\fBeditrc(5)\fP for details.
+.sp
+Command history is saved in \fI~/.knotc_history\fP\&.
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.SS Reload the whole server configuration
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc reload
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Flush the example.com and example.org zones
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc zone\-flush example.com example.org
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Get the current server configuration
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc conf\-read server
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Get the list of the current zones
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc conf\-read zone.domain
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Get the primary servers for the example.com zone
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc conf\-read \(aqzone[example.com].master\(aq
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Add example.org zone with a zonefile location
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc conf\-begin
+$ knotc conf\-set \(aqzone[example.org]\(aq
+$ knotc conf\-set \(aqzone[example.org].file\(aq \(aq/var/zones/example.org.zone\(aq
+$ knotc conf\-commit
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SS Get the SOA record for each configured zone
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knotc zone\-read \-\- @ SOA
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fBknotd(8)\fP, \fBknot.conf(5)\fP, \fBeditrc(5)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/knotd.8in b/doc/man/knotd.8in
new file mode 100644
index 0000000..d4d18a3
--- /dev/null
+++ b/doc/man/knotd.8in
@@ -0,0 +1,90 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KNOTD" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+knotd \- Knot DNS server daemon
+.SH SYNOPSIS
+.sp
+\fBknotd\fP [\fIparameters\fP]
+.SH DESCRIPTION
+.sp
+Knot DNS is a high\-performance authoritative DNS server. The \fIknotd\fP program is
+the DNS server daemon.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.TP
+\fB\-m\fP, \fB\-\-max\-conf\-size\fP \fIMiB\fP
+Set maximum size of the configuration database
+(default is @conf_mapsize@ MiB, maximum 10000 MiB).
+.TP
+\fB\-s\fP, \fB\-\-socket\fP \fIpath\fP
+Use a remote control UNIX socket path (default is \fB@run_dir@/knot.sock\fP).
+.TP
+\fB\-d\fP, \fB\-\-daemonize\fP [\fIdirectory\fP]
+Run the server as a daemon. New root directory may be specified
+(default is \fB/\fP).
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Enable debug output.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SS Signals
+.sp
+If the \fIknotd\fP process receives a SIGHUP signal, it reloads its configuration and
+reopens the log files, if they are configured. When \fIknotd\fP receives a SIGUSR1
+signal, it reloads all configured zones. Upon receiving a SIGINT signal, \fIknotd\fP
+exits.
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH SEE ALSO
+.sp
+\fBknot.conf(5)\fP, \fBknotc(8)\fP, \fBkeymgr(8)\fP,
+\fBkjournalprint(8)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/knsec3hash.1in b/doc/man/knsec3hash.1in
new file mode 100644
index 0000000..624f99d
--- /dev/null
+++ b/doc/man/knsec3hash.1in
@@ -0,0 +1,93 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KNSEC3HASH" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+knsec3hash \- Simple utility to compute NSEC3 hash
+.SH SYNOPSIS
+.sp
+\fBknsec3hash\fP \fIsalt\fP \fIalgorithm\fP \fIiterations\fP \fIname\fP
+.sp
+\fBknsec3hash\fP \fIalgorithm\fP \fIflags\fP \fIiterations\fP \fIsalt\fP \fIname\fP
+.SH DESCRIPTION
+.sp
+This utility generates a NSEC3 hash for a given domain name and parameters of NSEC3 hash.
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIsalt\fP
+Specifies a binary salt encoded as a hexadecimal string.
+.TP
+\fIalgorithm\fP
+Specifies a hashing algorithm by number. Currently, the only supported algorithm is SHA\-1 (number 1).
+.TP
+\fIiterations\fP
+Specifies the number of additional iterations of the hashing algorithm.
+.TP
+\fIname\fP
+Specifies the domain name to be hashed.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knsec3hash 1 0 10 c01dcafe knot\-dns.cz
+7PTVGE7QV67EM61ROS9238P5RAKR2DM7 (salt=c01dcafe, hash=1, iterations=10)
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knsec3hash \- 1 0 net
+A1RT98BS5QGC9NFI51S9HCI47ULJG6JH (salt=\-, hash=1, iterations=0)
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fI\%RFC 5155\fP – DNS Security (DNSSEC) Hashed Authenticated Denial of Existence.
+.sp
+\fBknotc(8)\fP, \fBknotd(8)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/knsupdate.1in b/doc/man/knsupdate.1in
new file mode 100644
index 0000000..49438ad
--- /dev/null
+++ b/doc/man/knsupdate.1in
@@ -0,0 +1,211 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KNSUPDATE" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+knsupdate \- Dynamic DNS update utility
+.SH SYNOPSIS
+.sp
+\fBknsupdate\fP [\fIoptions\fP] [\fIfilename\fP]
+.SH DESCRIPTION
+.sp
+This utility sends Dynamic DNS update messages to a DNS server. Update content
+is read from a file (if the parameter \fIfilename\fP is given) or from the standard
+input.
+.sp
+The format of updates is textual and is made up of commands. Every command is
+placed on the separate line of the input. Lines starting with a semicolon are
+comments and are not processed.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-d\fP
+Enable debug messages.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-k\fP \fIkeyfile\fP
+Use the TSIG key stored in a file \fIkeyfile\fP to authenticate the request. The
+file should contain the key in the same format, which is accepted by the
+\fB\-y\fP option.
+.TP
+\fB\-p\fP \fIport\fP
+Set the port to use for connections to the server (if not explicitly specified
+in the update). The default is 53.
+.TP
+\fB\-r\fP \fIretries\fP
+The number of retries for UDP requests. The default is 3.
+.TP
+\fB\-t\fP \fItimeout\fP
+The total timeout (for all UDP update tries) of the update request in seconds.
+The default is 12. If set to zero, the timeout is infinite.
+.TP
+\fB\-v\fP
+Use a TCP connection.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.TP
+\fB\-y\fP [\fIalg\fP:]\fIname\fP:\fIkey\fP
+Use the TSIG key with a name \fIname\fP to authenticate the request. The \fIalg\fP
+part specifies the algorithm (the default is hmac\-sha256) and \fIkey\fP specifies
+the shared secret encoded in Base64.
+.UNINDENT
+.SS Commands
+.INDENT 0.0
+.TP
+\fBserver\fP \fIname\fP [\fIport\fP]
+Specifies a receiving server of the dynamic update message. The \fIname\fP parameter
+can be either a host name or an IP address. If the \fIport\fP is not specified,
+the default port is used. The default port value can be controlled using
+the \fB\-p\fP program option.
+.TP
+\fBlocal\fP \fIaddress\fP [\fIport\fP]
+Specifies outgoing \fIaddress\fP and \fIport\fP\&. If no local is specified, the
+address and port are set by the system automatically. The default port number
+is 0.
+.TP
+\fBzone\fP \fIname\fP
+Specifies that all updates are done within a zone \fIname\fP\&. The zone name doesn\(aqt
+have a default and must be set explicitly.
+.TP
+\fBorigin\fP \fIname\fP
+Specifies fully qualified domain name suffix which is appended to non\-fqd
+owners in update commands. The default is the terminal label (\fB\&.\fP).
+.TP
+\fBclass\fP \fIname\fP
+Sets \fIname\fP as the default class for all updates. If not used, the default
+class is IN.
+.TP
+\fBttl\fP \fIvalue\fP
+Sets \fIvalue\fP as the default TTL (in seconds). If not used, the default value
+is 3600.
+.TP
+\fBkey\fP [\fIalg\fP:]\fIname\fP \fIkey\fP
+Specifies the TSIG \fIkey\fP named \fIname\fP to authenticate the request. An optional
+\fIalg\fP algorithm can be specified. This command has the same effect as
+the program option \fB\-y\fP\&.
+.TP
+[\fBprereq\fP] \fBnxdomain\fP \fIname\fP
+Adds a prerequisite for a non\-existing record owned by \fIname\fP\&.
+.TP
+[\fBprereq\fP] \fByxdomain\fP \fIname\fP
+Adds a prerequisite for an existing record owned by \fIname\fP\&.
+.TP
+[\fBprereq\fP] \fBnxrrset\fP \fIname\fP [\fIclass\fP] \fItype\fP
+Adds a prerequisite for a non\-existing record of the \fItype\fP owned by \fIname\fP\&.
+Internet \fIclass\fP is expected.
+.TP
+[\fBprereq\fP] \fByxrrset\fP \fIname\fP [\fIclass\fP] \fItype\fP [\fIdata\fP]
+Adds a prerequisite for an existing record of the \fItype\fP owned by \fIname\fP
+with optional \fIdata\fP\&. Internet \fIclass\fP is expected.
+.TP
+[\fBupdate\fP] \fBadd\fP \fIname\fP [\fIttl\fP] [\fIclass\fP] \fItype\fP \fIdata\fP
+Adds a request to add a new resource record into the zone.
+Please note that if the \fIname\fP is not fully qualified domain name, the
+current origin name is appended to it.
+.TP
+[\fBupdate\fP] \fBdel\fP[\fBete\fP] \fIname\fP [\fIttl\fP] [\fIclass\fP] [\fItype\fP] [\fIdata\fP]
+Adds a request to remove all (or matching \fIclass\fP, \fItype\fP or \fIdata\fP)
+resource records from the zone. There is the same requirement for the \fIname\fP
+parameter as in \fBupdate add\fP command. The \fIttl\fP item is ignored.
+.TP
+\fBshow\fP
+Displays current content of the update message.
+.TP
+\fBsend\fP
+Sends the current update message and cleans the list of updates.
+.TP
+\fBanswer\fP
+Displays the last answer from the server.
+.TP
+\fBdebug\fP
+Enable debugging. This command has the same meaning as the \fB\-d\fP program option.
+.TP
+\fBexit\fP
+End the program.
+.UNINDENT
+.SH NOTES
+.sp
+Options \fB\-k\fP and \fB\-y\fP can not be used simultaneously.
+.sp
+Dnssec\-keygen keyfile format is not supported. Use \fBkeymgr(8)\fP instead.
+.sp
+Zone name/server guessing is not supported if the zone name/server is not specified.
+.sp
+Empty line doesn\(aqt send the update.
+.SS Interactive mode
+.sp
+The utility provides interactive mode with basic line editing functionality,
+command completion, and command history.
+.sp
+Interactive mode behavior can be customized in \fI~/.editrc\fP\&. Refer to
+\fBeditrc(5)\fP for details.
+.sp
+Command history is saved in \fI~/.knsupdate_history\fP\&.
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.INDENT 0.0
+.IP 1. 3
+Send one update of the zone example.com to the server 192.168.1.1. The update
+contains two new records:
+.INDENT 3.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+$ knsupdate
+knsupdate> server 192.168.1.1
+knsupdate> zone example.com.
+knsupdate> origin example.com.
+knsupdate> ttl 3600
+knsupdate> add test1.example.com. 7200 A 192.168.2.2
+knsupdate> add test2 TXT "hello"
+knsupdate> show
+knsupdate> send
+knsupdate> answer
+knsupdate> exit
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fBkdig(1)\fP, \fBkhost(1)\fP, \fBkeymgr(8)\fP, \fBeditrc(5)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/kxdpgun.8in b/doc/man/kxdpgun.8in
new file mode 100644
index 0000000..3304838
--- /dev/null
+++ b/doc/man/kxdpgun.8in
@@ -0,0 +1,241 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KXDPGUN" "8" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kxdpgun \- XDP-powered DNS benchmarking tool
+.SH SYNOPSIS
+.sp
+\fBkxdpgun\fP [\fIoptions\fP] \fB\-i\fP \fIfilename\fP \fItargetIP\fP
+.SH DESCRIPTION
+.sp
+Powerful generator of DNS traffic, sending and receiving packets through XDP.
+.sp
+Queries are generated according to a textual file which is read sequentially
+in a loop until a configured duration elapses. The order of queries is not
+guaranteed. Responses are received (unless disabled) and counted, but not
+checked against queries.
+.sp
+The number of parallel threads is autodetected according to the number of queues
+configured for the network interface.
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-t\fP, \fB\-\-duration\fP \fIseconds\fP
+Duration of traffic generation, specified as a decimal number in seconds
+(default is 5.0).
+.TP
+\fB\-T\fP, \fB\-\-tcp\fP[\fB=\fP\fIdebug_mode\fP]
+Send queries over TCP. See the list of optional debug modes below.
+.TP
+\fB\-U\fP, \fB\-\-quic\fP[\fB=\fP\fIdebug_mode\fP]
+Send queries over QUIC. See the list of optional debug modes below.
+.TP
+\fB\-Q\fP, \fB\-\-qps\fP \fIqueries\fP
+Number of queries\-per\-second (approximately) to be sent (default is 1000).
+The program is not optimized for low speeds at which it may lose
+communication packets. The recommended minimum speed is 2 packets per thread
+(Rx/Tx queue).
+.TP
+\fB\-b\fP, \fB\-\-batch\fP \fIsize\fP
+Send more queries in a batch. Improves QPS but may affect the counterpart\(aqs
+packet loss (default is 10 for UDP and 1 for TCP/QUIC).
+.TP
+\fB\-r\fP, \fB\-\-drop\fP
+Drop incoming responses. Improves QPS, but disables response statistics.
+.TP
+\fB\-p\fP, \fB\-\-port\fP \fInumber\fP
+Remote destination port (default is 53 for UDP/TCP, 853 for QUIC).
+.TP
+\fB\-F\fP, \fB\-\-affinity\fP \fIcpu_spec\fP
+CPU affinity for all threads specified in the format [<cpu_start>][s<cpu_step>],
+where <cpu_start> is the CPU ID for the first thread and <cpu_step> is the
+CPU ID increment for next thread (default is 0s1).
+.TP
+\fB\-i\fP, \fB\-\-infile\fP \fIfilename\fP
+Path to a file with query templates.
+.TP
+\fB\-I\fP, \fB\-\-interface\fP \fIinterface\fP
+Network interface for outgoing communication. This can be useful in situations
+when the interfaces are in a bond for example.
+.TP
+\fB\-l\fP, \fB\-\-local\fP \fIlocalIP\fP[\fB/\fP\fIprefix\fP]
+Override the auto\-detected source IP address. If an address range is specified
+instead, various IPs from the range will be used for different queries uniformly
+(address range not supported in the QUIC mode).
+.TP
+\fItargetIP\fP
+The IPv4 or IPv6 address of remote destination.
+.TP
+\fB\-L\fP, \fB\-\-mac\-local\fP
+Override auto\-detected local MAC address.
+.TP
+\fB\-R\fP, \fB\-\-mac\-remote\fP
+Override auto\-detected remote MAC address.
+.TP
+\fB\-v\fP, \fB\-\-vlan\fP \fIid\fP
+Add VLAN 802.1Q header with the given id. VLAN offloading should be disabled.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SS Queries file format
+.sp
+Each line describes a query in the form:
+.sp
+\fIquery_name\fP \fIquery_type\fP [\fIflags\fP]
+.sp
+Where \fIquery_name\fP is a domain name to be queried, \fIquery_type\fP is a record type
+name, and \fIflags\fP is a single character:
+.sp
+\fBE\fP Send query with EDNS.
+.sp
+\fBD\fP Request DNSSEC (EDNS + DO flag).
+.SS TCP/QUIC debug modes
+.INDENT 0.0
+.TP
+\fB0\fP
+Perform full handshake for all connections (QUIC only).
+.TP
+\fB1\fP
+Just send SYN (Initial) and receive SYN\-ACK (Handshake).
+.TP
+\fB2\fP
+Perform TCP/QUIC handshake and don\(aqt send anything, allow close initiated by counterpart.
+.TP
+\fB3\fP
+Perform TCP/QUIC handshake and don\(aqt react further.
+.TP
+\fB5\fP
+Send incomplete query (N\-1 bytes) and don\(aqt react further.
+.TP
+\fB7\fP
+Send query and don\(aqt ACK the response or anything further.
+.TP
+\fB8\fP
+Don\(aqt close the connection and ignore close by counterpart (TCP only).
+.TP
+\fB9\fP
+Operate normally except for not ACKing the final FIN+ACK (TCP only).
+.UNINDENT
+.SS Signals
+.sp
+Sending USR1 signal to a running process triggers current statistics dump
+to the standard output.
+.SH NOTES
+.sp
+Linux kernel 4.18+ is required.
+.sp
+The utility has to be executed under root or with these capabilities:
+CAP_NET_RAW, CAP_NET_ADMIN, CAP_SYS_ADMIN, CAP_IPC_LOCK, and CAP_SYS_RESOURCE
+(Linux < 5.11).
+.sp
+The utility allocates source UDP/TCP ports from the range 2000\-65535.
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH EXAMPLES
+.sp
+Manually created queries file:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+abc6.example.com. AAAA
+nxdomain.example.com. A
+notzone. A
+a.example.com. NS E
+ab.example.com. A D
+abcd.example.com. DS D
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Queries file generated from a zone file (Knot DNS format):
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+cat ZONE_FILE | awk "{print \e$1,\e$3}" | grep \-E "(NS|DS|A|AAAA|PTR|MX|SOA)$" | sort \-u \-R > queries.txt
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+Basic usage:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+# kxdpgun \-i ~/queries.txt 2001:DB8::1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fIUsing UDP with increased batch size\fP:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+# kxdpgun \-t 20 \-Q 1000000 \-i ~/queries.txt \-b 20 \-p 8853 192.0.2.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.sp
+\fIUsing TCP\fP:
+.INDENT 0.0
+.INDENT 3.5
+.sp
+.nf
+.ft C
+# kxdpgun \-t 20 \-Q 100000 \-i ~/queries.txt \-T \-p 8853 192.0.2.1
+.ft P
+.fi
+.UNINDENT
+.UNINDENT
+.SH SEE ALSO
+.sp
+\fBkdig(1)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/kzonecheck.1in b/doc/man/kzonecheck.1in
new file mode 100644
index 0000000..6f8a834
--- /dev/null
+++ b/doc/man/kzonecheck.1in
@@ -0,0 +1,88 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KZONECHECK" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kzonecheck \- Knot DNS zone check tool
+.SH SYNOPSIS
+.sp
+\fBkzonecheck\fP [\fIoptions\fP] \fIfilename\fP
+.SH DESCRIPTION
+.sp
+The utility checks zone file syntax and runs semantic checks on the zone
+content. The executed checks are the same as the checks run by the Knot
+DNS server.
+.sp
+Please, refer to the \fBsemantic\-checks\fP configuration option in
+\fBknot.conf(5)\fP for the full list of available semantic checks.
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIfilename\fP
+Path to the zone file to be checked. For reading from \fBstdin\fP use \fB/dev/stdin\fP
+or just \fB\-\fP\&.
+.UNINDENT
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-o\fP, \fB\-\-origin\fP \fIorigin\fP
+Zone origin. If not specified, the origin is determined from the file name
+(possibly removing the \fB\&.zone\fP suffix).
+.TP
+\fB\-d\fP, \fB\-\-dnssec\fP \fBon\fP|\fBoff\fP
+Also check DNSSEC\-related records. The default is to decide based on the
+existence of a RRSIG for SOA.
+.TP
+\fB\-t\fP, \fB\-\-time\fP \fItime\fP
+Current time specification. Use UNIX timestamp, YYYYMMDDHHmmSS
+format, or [+/\-]\fItime\fP[unit] format, where unit can be \fBY\fP, \fBM\fP,
+\fBD\fP, \fBh\fP, \fBm\fP, or \fBs\fP\&. Default is current UNIX timestamp.
+.TP
+\fB\-v\fP, \fB\-\-verbose\fP
+Enable debug output.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH SEE ALSO
+.sp
+\fBknotd(8)\fP, \fBknot.conf(5)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man/kzonesign.1in b/doc/man/kzonesign.1in
new file mode 100644
index 0000000..32a2095
--- /dev/null
+++ b/doc/man/kzonesign.1in
@@ -0,0 +1,95 @@
+.\" Man page generated from reStructuredText.
+.
+.
+.nr rst2man-indent-level 0
+.
+.de1 rstReportMargin
+\\$1 \\n[an-margin]
+level \\n[rst2man-indent-level]
+level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
+-
+\\n[rst2man-indent0]
+\\n[rst2man-indent1]
+\\n[rst2man-indent2]
+..
+.de1 INDENT
+.\" .rstReportMargin pre:
+. RS \\$1
+. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
+. nr rst2man-indent-level +1
+.\" .rstReportMargin post:
+..
+.de UNINDENT
+. RE
+.\" indent \\n[an-margin]
+.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.nr rst2man-indent-level -1
+.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
+.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
+..
+.TH "KZONESIGN" "1" "@RELEASE_DATE@" "@VERSION@" "Knot DNS"
+.SH NAME
+kzonesign \- DNSSEC signing utility
+.SH SYNOPSIS
+.sp
+\fBkzonesign\fP [\fIconfig_option\fP \fIconfig_argument\fP] [\fIoptions\fP] \fIzone_name\fP
+.SH DESCRIPTION
+.sp
+This utility reads the zone\(aqs zone file, signs the zone according to given
+configuration, and writes the signed zone file back. An alternative mode
+is DNSSEC validation of the given zone. The signing or validation
+can run in parallel if enabled in the configuration (see policy.signing\-threads
+and zone.adjust\-threads).
+.SS Config options
+.INDENT 0.0
+.TP
+\fB\-c\fP, \fB\-\-config\fP \fIfile\fP
+Use a textual configuration file (default is \fB@config_dir@/knot.conf\fP).
+.TP
+\fB\-C\fP, \fB\-\-confdb\fP \fIdirectory\fP
+Use a binary configuration database directory (default is \fB@storage_dir@/confdb\fP).
+The default configuration database, if exists, has a preference to the default
+configuration file.
+.UNINDENT
+.SS Options
+.INDENT 0.0
+.TP
+\fB\-o\fP, \fB\-\-outdir\fP \fIdir_name\fP
+Write the output zone file to the specified directory instead of the configured one.
+.TP
+\fB\-r\fP, \fB\-\-rollover\fP
+Allow key roll\-overs and NSEC3 re\-salt. In order to finish possible KSK submission,
+set the KSK\(aqs \fBactive\fP timestamp to now (\fB+0\fP) using keymgr\&.
+.TP
+\fB\-v\fP, \fB\-\-verify\fP
+Instead of (re\-)signing the zone, just verify that the zone is correctly signed.
+.TP
+\fB\-t\fP, \fB\-\-time\fP \fItimestamp\fP
+Sign/verify the zone (and roll the keys if necessary) as if it was at the time
+specified by timestamp.
+.TP
+\fB\-h\fP, \fB\-\-help\fP
+Print the program help.
+.TP
+\fB\-V\fP, \fB\-\-version\fP
+Print the program version.
+.UNINDENT
+.SS Parameters
+.INDENT 0.0
+.TP
+\fIzone_name\fP
+A name of the zone to be signed.
+.UNINDENT
+.SH EXIT VALUES
+.sp
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+.SH SEE ALSO
+.sp
+\fBknot.conf(5)\fP, \fBkeymgr(8)\fP\&.
+.SH AUTHOR
+CZ.NIC Labs <https://www.knot-dns.cz>
+.SH COPYRIGHT
+Copyright 2010–2023, CZ.NIC, z.s.p.o.
+.\" Generated by docutils manpage writer.
+.
diff --git a/doc/man_kcatalogprint.rst b/doc/man_kcatalogprint.rst
new file mode 100644
index 0000000..0fb5088
--- /dev/null
+++ b/doc/man_kcatalogprint.rst
@@ -0,0 +1,54 @@
+.. highlight:: console
+
+kcatalogprint – Knot DNS catalog print utility
+==============================================
+
+Synopsis
+--------
+
+:program:`kcatalogprint` [*config_option* *config_argument*] [*option*]
+
+Description
+-----------
+
+The program prints zone catalog stored in a catalog database.
+
+Config options
+..............
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+**-D**, **--dir** *path*
+ Use specified catalog database path and default configuration.
+
+Options
+.......
+
+**-a**, **--catalog**
+ Filter the output by catalog zone name.
+
+**-m**, **--member**
+ Filter the output by member zone name.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+See Also
+--------
+
+:manpage:`knotd(8)`, :manpage:`knot.conf(5)`.
diff --git a/doc/man_kdig.rst b/doc/man_kdig.rst
new file mode 100644
index 0000000..122fefa
--- /dev/null
+++ b/doc/man_kdig.rst
@@ -0,0 +1,392 @@
+.. highlight:: console
+
+``kdig`` – Advanced DNS lookup utility
+======================================
+
+Synopsis
+--------
+
+:program:`kdig` [*common-settings*] [*query* [*settings*]]...
+
+:program:`kdig` **-h**
+
+Description
+-----------
+
+This utility sends one or more DNS queries to a nameserver. Each query can have
+individual *settings*, or it can be specified globally via *common-settings*,
+which must precede *query* specification.
+
+Parameters
+..........
+
+*query*
+ *name* | **-q** *name* | **-x** *address* | **-G** *tapfile*
+
+*common-settings*, *settings*
+ [*query_class*] [*query_type*] [**@**\ *server*]... [*options*]
+
+*name*
+ Is a domain name that is to be looked up.
+
+*server*
+ Is a domain name or an IPv4 or IPv6 address of the nameserver to send a query
+ to. An additional port can be specified using address:port ([address]:port
+ for IPv6 address), address@port, or address#port notation. A value which begins
+ with '/' character is considered an absolute UNIX socket path. If no server is
+ specified, the servers from :file:`/etc/resolv.conf` are used.
+
+If no arguments are provided, :program:`kdig` sends NS query for the root
+zone.
+
+Query classes
+.............
+
+A *query_class* can be either a DNS class name (IN, CH) or generic class
+specification **CLASS**\ *XXXXX* where *XXXXX* is a corresponding decimal
+class number. The default query class is IN.
+
+Query types
+...........
+
+A *query_type* can be either a DNS resource record type
+(A, AAAA, NS, SOA, DNSKEY, ANY, etc.) or one of the following:
+
+**TYPE**\ *XXXXX*
+ Generic query type specification where *XXXXX* is a corresponding decimal
+ type number.
+
+**AXFR**
+ Full zone transfer request.
+
+**IXFR=**\ *serial*
+ Incremental zone transfer request for specified SOA serial number
+ (i.e. all zone updates since the specified zone version are to be returned).
+
+**NOTIFY=**\ *serial*
+ Notify message with a SOA serial hint specified.
+
+**NOTIFY**
+ Notify message with a SOA serial hint unspecified.
+
+The default query type is A.
+
+Options
+.......
+
+**-4**
+ Use the IPv4 protocol only.
+
+**-6**
+ Use the IPv6 protocol only.
+
+**-b** *address*
+ Set the source IP address of the query to *address*. The address must be a
+ valid address for local interface or :: or 0.0.0.0. An optional port
+ can be specified in the same format as the *server* value.
+
+**-c** *class*
+ An explicit *query_class* specification. See possible values above.
+
+**-d**
+ Enable debug messages.
+
+**-h**, **--help**
+ Print the program help.
+
+**-k** *keyfile*
+ Use the TSIG key stored in a file *keyfile* to authenticate the request. The
+ file must contain the key in the same format as accepted by the
+ **-y** option.
+
+**-p** *port*
+ Set the nameserver port number or service name to send a query to. The default
+ port is 53.
+
+**-q** *name*
+ Set the query name. An explicit variant of *name* specification. If no *name*
+ is provided, empty question section is set.
+
+**-t** *type*
+ An explicit *query_type* specification. See possible values above.
+
+**-V**, **--version**
+ Print the program version.
+
+**-x** *address*
+ Send a reverse (PTR) query for IPv4 or IPv6 *address*. The correct name, class
+ and type is set automatically.
+
+**-y** [*alg*:]\ *name*:*key*
+ Use the TSIG key named *name* to authenticate the request. The *alg*
+ part specifies the algorithm (the default is hmac-sha256) and *key* specifies
+ the shared secret encoded in Base64.
+
+**-E** *tapfile*
+ Export a dnstap trace of the query and response messages received to the
+ file *tapfile*.
+
+**-G** *tapfile*
+ Generate message output from a previously saved dnstap file *tapfile*.
+
+**+**\ [\ **no**\ ]\ **multiline**
+ Wrap long records to more lines and improve human readability.
+
+**+**\ [\ **no**\ ]\ **short**
+ Show record data only.
+
+**+**\ [\ **no**\ ]\ **generic**
+ Use the generic representation format when printing resource record types
+ and data.
+
+**+**\ [\ **no**\ ]\ **crypto**
+ Display the DNSSEC keys and signatures values in base64, instead of omitting them.
+
+**+**\ [\ **no**\ ]\ **aaflag**
+ Set the AA flag.
+
+**+**\ [\ **no**\ ]\ **tcflag**
+ Set the TC flag.
+
+**+**\ [\ **no**\ ]\ **rdflag**
+ Set the RD flag.
+
+**+**\ [\ **no**\ ]\ **recurse**
+ Same as **+**\ [\ **no**\ ]\ **rdflag**
+
+**+**\ [\ **no**\ ]\ **raflag**
+ Set the RA flag.
+
+**+**\ [\ **no**\ ]\ **zflag**
+ Set the zero flag bit.
+
+**+**\ [\ **no**\ ]\ **adflag**
+ Set the AD flag.
+
+**+**\ [\ **no**\ ]\ **cdflag**
+ Set the CD flag.
+
+**+**\ [\ **no**\ ]\ **dnssec**
+ Set the DO flag.
+
+**+**\ [\ **no**\ ]\ **all**
+ Show all packet sections.
+
+**+**\ [\ **no**\ ]\ **qr**
+ Show the query packet.
+
+**+**\ [\ **no**\ ]\ **header**
+ Show the packet header.
+
+**+**\ [\ **no**\ ]\ **comments**
+ Show commented section names.
+
+**+**\ [\ **no**\ ]\ **opt**
+ Show the EDNS pseudosection.
+
+**+**\ [\ **no**\ ]\ **opttext**
+ Try to show unknown EDNS options as text.
+
+**+**\ [\ **no**\ ]\ **question**
+ Show the question section.
+
+**+**\ [\ **no**\ ]\ **answer**
+ Show the answer section.
+
+**+**\ [\ **no**\ ]\ **authority**
+ Show the authority section.
+
+**+**\ [\ **no**\ ]\ **additional**
+ Show the additional section.
+
+**+**\ [\ **no**\ ]\ **tsig**
+ Show the TSIG pseudosection.
+
+**+**\ [\ **no**\ ]\ **stats**
+ Show trailing packet statistics.
+
+**+**\ [\ **no**\ ]\ **class**
+ Show the DNS class.
+
+**+**\ [\ **no**\ ]\ **ttl**
+ Show the TTL value.
+
+**+**\ [\ **no**\ ]\ **tcp**
+ Use the TCP protocol (default is UDP for standard query and TCP for AXFR/IXFR).
+
+**+**\ [\ **no**\ ]\ **fastopen**
+ Use TCP Fast Open.
+
+**+**\ [\ **no**\ ]\ **ignore**
+ Don't use TCP automatically if a truncated reply is received.
+
+**+**\ [\ **no**\ ]\ **keepopen**
+ Keep TCP connection open for the following query if it has the same connection
+ configuration. This applies to +tcp, +tls, and +https operations. The connection
+ is considered in the context of a single kdig call only.
+
+**+**\ [\ **no**\ ]\ **tls**
+ Use TLS with the Opportunistic privacy profile (:rfc:`7858#section-4.1`).
+
+**+**\ [\ **no**\ ]\ **tls-ca**\[\ =\ *FILE*\]
+ Use TLS with a certificate validation. Certification authority certificates
+ are loaded from the specified PEM file (default is system certificate storage
+ if no argument is provided).
+ Can be specified multiple times. If the +tls-hostname option is not provided,
+ the name of the target server (if specified) is used for strict authentication.
+
+**+**\ [\ **no**\ ]\ **tls-pin**\ =\ *BASE64*
+ Use TLS with the Out-of-Band key-pinned privacy profile (:rfc:`7858#section-4.2`).
+ The PIN must be a Base64 encoded SHA-256 hash of the X.509 SubjectPublicKeyInfo.
+ Can be specified multiple times.
+
+**+**\ [\ **no**\ ]\ **tls-hostname**\ =\ *STR*
+ Use TLS with a remote server hostname check.
+
+**+**\ [\ **no**\ ]\ **tls-sni**\ =\ *STR*
+ Use TLS with a Server Name Indication.
+
+**+**\ [\ **no**\ ]\ **tls-keyfile**\ =\ *FILE*
+ Use TLS with a client keyfile.
+
+**+**\ [\ **no**\ ]\ **tls-certfile**\ =\ *FILE*
+ Use TLS with a client certfile.
+
+**+**\ [\ **no**\ ]\ **tls-ocsp-stapling**\[\ =\ *H*\]
+ Use TLS with a valid stapled OCSP response for the server certificate
+ (%u or specify hours). OCSP responses older than the specified period are
+ considered invalid.
+
+**+**\ [\ **no**\ ]\ **https**\[\ =\ *URL*\]
+ Use HTTPS (DNS-over-HTTPS) in wire format (:rfc:`1035#section-4.2.1`).
+ It is also possible to specify URL=\[authority\]\[/path\] where request
+ will be sent to. Any leading scheme and authority indicator (i.e. //) are ignored.
+ Authority might also be specified as the *server* (using the parameter `@`).
+ If *path* is specified and *authority* is missing, then the *server*
+ is used as authority together with the specified *path*.
+ Library *libnghttp2* is required.
+
+**+**\ [\ **no**\ ]\ **https-get**
+ Use HTTPS with HTTP/GET method instead of the default HTTP/POST method.
+ Library *libnghttp2* is required.
+
+**+**\ [\ **no**\ ]\ **quic**
+ Use QUIC (DNS-over-QUIC).
+
+**+**\ [\ **no**\ ]\ **nsid**
+ Request the nameserver identifier (NSID).
+
+**+**\ [\ **no**\ ]\ **bufsize**\ =\ *B*
+ Set EDNS buffer size in bytes (default is 4096 bytes).
+
+**+**\ [\ **no**\ ]\ **padding**\[\ =\ *B*\]
+ Use EDNS(0) padding option to pad queries, optionally to a specific
+ size. The default is to pad queries with a sensible amount when using
+ +tls, and not to pad at all when queries are sent without TLS. With
+ no argument (i.e., just +padding) pad every query with a sensible
+ amount regardless of the use of TLS. With +nopadding, never pad.
+
+**+**\ [\ **no**\ ]\ **alignment**\[\ =\ *B*\]
+ Align the query to B\-byte-block message using the EDNS(0) padding option
+ (default is no or 128 if no argument is specified).
+
+**+**\ [\ **no**\ ]\ **subnet**\ =\ *SUBN*
+ Set EDNS(0) client subnet SUBN=addr/prefix.
+
+**+**\ [\ **no**\ ]\ **edns**\[\ =\ *N*\]
+ Use EDNS version (default is 0).
+
+**+**\ [\ **no**\ ]\ **timeout**\ =\ *T*
+ Set the wait-for-reply interval in seconds (default is 5 seconds). This timeout
+ applies to each query attempt. Zero value or *notimeout* is interpreted as
+ infinity.
+
+**+**\ [\ **no**\ ]\ **retry**\ =\ *N*
+ Set the number (>=0) of UDP retries (default is 2). This doesn't apply to
+ AXFR/IXFR.
+
+**+**\ [\ **no**\ ]\ **expire**
+ Sets the EXPIRE EDNS option.
+
+**+**\ [\ **no**\ ]\ **cookie**\[\ =\ *HEX*\]
+ Attach EDNS(0) cookie to the query.
+
+**+**\ [\ **no**\ ]\ **badcookie**
+ Repeat a query with the correct cookie.
+
+**+**\ [\ **no**\ ]\ **ednsopt**\[\ =\ *CODE*\[:*HEX*\]\]
+ Send custom EDNS option. The *CODE* is EDNS option code in decimal, *HEX*
+ is an optional hex encoded string to use as EDNS option value. This argument
+ can be used multiple times. +noednsopt clears all EDNS options specified by
+ +ednsopt.
+
+**+**\ [\ **no**\ ]\ **proxy**\ =\ *SRC_ADDR*\[#\ *SRC_PORT*\]-*DST_ADDR*\[#\ *DST_PORT*\]
+ Add PROXYv2 header with the specified source and destination addresses to the query.
+ The default source port is 0 and destination port 53.
+
+**+**\ [\ **no**\ ]\ **json**
+ Use JSON for output encoding (RFC 8427).
+
+**+noidn**
+ Disable the IDN transformation to ASCII and vice versa. IDN support depends
+ on libidn availability during project building! If used in *common-settings*,
+ all IDN transformations are disabled. If used in the individual query *settings*,
+ transformation from ASCII is disabled on output for the particular query. Note
+ that IDN transformation does not preserve domain name letter case.
+
+Notes
+-----
+
+Options **-k** and **-y** can not be used simultaneously.
+
+Dnssec-keygen keyfile format is not supported. Use :manpage:`keymgr(8)` instead.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+1. Get A records for example.com::
+
+ $ kdig example.com A
+
+2. Perform AXFR for zone example.com from the server 192.0.2.1::
+
+ $ kdig example.com -t AXFR @192.0.2.1
+
+3. Get A records for example.com from 192.0.2.1 and reverse lookup for address
+ 2001:DB8::1 from 192.0.2.2. Both using the TCP protocol::
+
+ $ kdig +tcp example.com -t A @192.0.2.1 -x 2001:DB8::1 @192.0.2.2
+
+4. Get SOA record for example.com, use TLS, use system certificates, check
+ for specified hostname, check for certificate pin, and print additional
+ debug info::
+
+ $ kdig -d @185.49.141.38 +tls-ca +tls-host=getdnsapi.net \
+ +tls-pin=foxZRnIh9gZpWnl+zEiKa0EJ2rdCGroMWm02gaxSc9S= soa example.com
+
+5. DNS over HTTPS examples (various DoH implementations)::
+
+ $ kdig @1.1.1.1 +https example.com.
+ $ kdig @193.17.47.1 +https=/doh example.com.
+ $ kdig @8.8.4.4 +https +https-get example.com.
+ $ kdig @8.8.8.8 +https +tls-hostname=dns.google +fastopen example.com.
+
+6. More queries share one DoT connection::
+
+ $ kdig @1.1.1.1 +tls +keepopen abc.example.com A mail.example.com AAAA
+
+Files
+-----
+
+:file:`/etc/resolv.conf`
+
+See Also
+--------
+
+:manpage:`khost(1)`, :manpage:`knsupdate(1)`, :manpage:`keymgr(8)`.
diff --git a/doc/man_keymgr.rst b/doc/man_keymgr.rst
new file mode 100644
index 0000000..77f8e1a
--- /dev/null
+++ b/doc/man_keymgr.rst
@@ -0,0 +1,292 @@
+.. highlight:: console
+
+``keymgr`` – Key management utility
+===================================
+
+Synopsis
+--------
+
+:program:`keymgr` [*config_option* *config_argument*] [*option*...] *zone_name* *command* *argument*...
+
+:program:`keymgr` [*config_option* *config_argument*] **-l**
+
+:program:`keymgr` **-t** *parameter*...
+
+Description
+-----------
+
+The :program:`keymgr` utility serves for manual key management in Knot DNS server.
+
+Functions for DNSSEC keys and KASP (Key And Signature Policy)
+management are provided.
+
+The DNSSEC and KASP configuration is stored in a so called KASP database.
+The database is backed by LMDB.
+
+Config options
+..............
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+**-D**, **--dir** *path*
+ Use specified KASP database path and default configuration.
+
+Options
+.......
+
+**-t**, **--tsig** *tsig_name* [*tsig_algorithm* [*tsig_bits*]]
+ Generates a TSIG key. TSIG algorithm can be specified by string (default: hmac-sha256),
+ bit length of the key by number (default: optimal length given by algorithm). The generated
+ TSIG key is only displayed on `stdout`: the command does not create a file, nor include the
+ key in a keystore.
+
+**-e**, **--extended**
+ Extended output (listing of keys with full description).
+
+**-j**, **--json**
+ Print the zones or keys in JSON format.
+
+**-l**, **--list**
+ Print the list of zones that have at least one key stored in the configured KASP
+ database.
+
+**-x**, **--mono**
+ Don't generate colorized output.
+
+**-X**, **--color**
+ Force colorized output in the normal mode.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+.. NOTE::
+ Keymgr runs with the same user privileges as configured for :doc:`knotd<man_knotd>`.
+ For example, if keymgr is run as ``root``, but the configured :ref:`user<server_user>`
+ is ``knot``, it won't be able to read files (PEM files, KASP database, ...) readable
+ only by ``root``.
+
+Commands
+........
+
+**list** [*timestamp_format*]
+ Prints the list of key IDs and parameters of keys belonging to the zone.
+
+**generate** [*arguments*...]
+ Generates new DNSSEC key and stores it in KASP database. Prints the key ID.
+ This action takes some number of arguments (see below). Values for unspecified arguments are taken
+ from corresponding policy (if *-c* or *-C* options used) or from Knot policy defaults.
+
+**import-bind** *BIND_key_file*
+ Imports a BIND-style key into KASP database (converting it to PEM format).
+ Takes one argument: path to BIND key file (private or public, but both MUST exist).
+
+**import-pub** *BIND_pubkey_file*
+ Imports a public key into KASP database. This key won't be rolled over nor used for signing.
+ Takes one argument: path to BIND public key file.
+
+**import-pem** *PEM_file* [*arguments*...]
+ Imports a DNSSEC key from PEM file. The key parameters (same as for the generate action) need to be
+ specified (mainly algorithm, timers...) because they are not contained in the PEM format.
+
+**import-pkcs11** *key_id* [*arguments*...]
+ Imports a DNSSEC key from PKCS #11 storage. The key parameters (same as for the generate action) need to be
+ specified (mainly algorithm, timers...) because they are not available. In fact, no key
+ data is imported, only KASP database metadata is created.
+
+**nsec3-salt** [*new_salt*]
+ Prints the current NSEC3 salt used for signing. If *new_salt* is specified, the salt is overwritten.
+ The salt is printed and expected in hexadecimal, or dash if empty.
+
+**local-serial** [*new_serial*]
+ Print SOA serial stored in KASP database when using on-secondary DNSSEC signing.
+ If *new_serial* is specified, the serial is overwritten. After updating the serial, expire the zone
+ (**zone-purge +expire +zonefile +journal**) if the server is running, or remove corresponding zone file
+ and journal contents if the server is stopped.
+
+**master-serial** [*new_serial*]
+ Print SOA serial of the remote master stored in KASP database when using on-secondary DNSSEC signing.
+ If *new_serial* is specified, the serial is overwritten (not recommended).
+
+**set** *key_spec* [*arguments*...]
+ Changes a timing argument (or ksk/zsk) of an existing key to a new value. *Key_spec* is either the
+ key tag or a prefix of the key ID, with an optional *[id=|keytag=]* prefix; *arguments*
+ are like for **generate**, but just the related ones.
+
+**ds** [*key_spec*]
+ Generate DS record (all digest algorithms together) for specified key. *Key_spec*
+ is like for **set**, if unspecified, all KSKs are used.
+
+**dnskey** [*key_spec*]
+ Generate DNSKEY record for specified key. *Key_spec*
+ is like for **ds**, if unspecified, all KSKs are used.
+
+**delete** *key_spec*
+ Remove the specified key from zone. If the key was not shared, it is also deleted from keystore.
+
+**share** *key_ID* *zone_from*
+ Import a key (specified by full key ID) from another zone as shared. After this, the key is
+ owned by both zones equally.
+
+Commands related to Offline KSK feature
+.......................................
+
+**pregenerate** [*timestamp-from*] *timestamp-to*
+ Pre-generate ZSKs for use with offline KSK, for the specified period starting from now or specified time.
+ This function also applies to non-offline KSK keys.
+
+**show-offline** [*timestamp-from*] [*timestamp-to*]
+ Print pre-generated offline key-related records for specified time interval. If *timestamp_to*
+ is omitted, it will be to infinity. If *timestamp-from* is omitted, it will start from the
+ beginning.
+
+**del-offline** *timestamp-from* *timestamp-to*
+ Delete pre-generated offline key-related records in specified time interval.
+
+**del-all-old**
+ Delete old keys that are in state 'removed'. This function also applies to
+ non-offline KSK keys.
+
+**generate-ksr** [*timestamp-from*] *timestamp-to*
+ Print to stdout KeySigningRequest based on pre-generated ZSKs for specified time period.
+ If *timestamp-from* is omitted, timestamp of the last offline records set is used
+ or now if no records available.
+
+**sign-ksr** *ksr_file*
+ Read KeySigningRequest from a text file, sign it using local keyset and print SignedKeyResponse to stdout.
+
+**validate-skr** *skr_file*
+ Read SignedKeyResponse from a text file and validate the RRSIGs in it if not corrupt.
+
+**import-skr** *skr_file*
+ Read SignedKeyResponse from a text file and import the signatures for later use in zone. If some
+ signatures have already been imported, they will be deleted for the period from beginning of the SKR
+ to infinity.
+
+Generate arguments
+..................
+
+Arguments are separated by space, each of them is in format 'name=value'.
+
+**algorithm**
+ Either an algorithm number (e.g. 14), or text name without dashes (e.g. ECDSAP384SHA384).
+
+**size**
+ Key length in bits.
+
+**ksk**
+ If set to **yes**, the key will be used for signing DNSKEY rrset. The generated key will also
+ have the Secure Entry Point flag set to 1.
+
+**zsk**
+ If set to **yes**, the key will be used for signing zone (except DNSKEY rrset). This flag can
+ be set concurrently with the **ksk** flag.
+
+**sep**
+ Overrides the standard setting of the Secure Entry Point flag.
+
+The following arguments are timestamps of key lifetime (see :ref:`DNSSEC Key states`):
+
+**pre_active**
+ Key started to be used for signing, not published (only for algorithm rollover).
+
+**publish**
+ Key published.
+
+**ready**
+ Key is waiting for submission (only for KSK).
+
+**active**
+ Key used for signing.
+
+**retire_active**
+ Key still used for signing, but another key is active (only for KSK or algorithm rollover).
+
+**retire**
+ Key still published, but no longer used for signing.
+
+**post_active**
+ Key no longer published, but still used for signing (only for algorithm rollover).
+
+**revoke**
+ Key revoked according to :rfc:`5011` trust anchor roll-over.
+
+**remove**
+ Key deleted.
+
+Timestamps
+..........
+
+0
+ Zero timestamp means infinite future.
+
+*UNIX_time*
+ Positive number of seconds since 1970 UTC.
+
+*YYYYMMDDHHMMSS*
+ Date and time in this format without any punctuation.
+
+*relative_timestamp*
+ A sign character (**+**, **-**), a number, and an optional time unit
+ (**y**, **mo**, **d**, **h**, **mi**, **s**). The default unit is one second.
+ E.g. +1mi, -2mo.
+
+Output timestamp formats
+........................
+
+(none)
+ The timestamps are printed as UNIX timestamp.
+
+**human**
+ The timestamps are printed relatively to now using time units (e.g. -2y5mo, +1h13s).
+
+**iso**
+ The timestamps are printed in the ISO8601 format (e.g. 2016-12-31T23:59:00).
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+1. Generate new TSIG key::
+
+ $ keymgr -t my_name hmac-sha384
+
+2. Generate new DNSSEC key::
+
+ $ keymgr example.com. generate algorithm=ECDSAP256SHA256 size=256 \
+ ksk=true created=1488034625 publish=20170223205611 retire=+10mo remove=+1y
+
+3. Import a DNSSEC key from BIND::
+
+ $ keymgr example.com. import-bind ~/bind/Kharbinge4d5.+007+63089.key
+
+4. Configure key timing::
+
+ $ keymgr example.com. set 4208 active=+2mi retire=+4mi remove=+5mi
+
+5. Share a KSK from another zone::
+
+ $ keymgr example.com. share e687cf927029e9db7184d2ece6d663f5d1e5b0e9 another-zone.com.
+
+See Also
+--------
+
+:rfc:`6781` - DNSSEC Operational Practices.
+:rfc:`7583` - DNSSEC Key Rollover Timing Considerations.
+
+:manpage:`knot.conf(5)`,
+:manpage:`knotc(8)`,
+:manpage:`knotd(8)`.
diff --git a/doc/man_khost.rst b/doc/man_khost.rst
new file mode 100644
index 0000000..76fa9c8
--- /dev/null
+++ b/doc/man_khost.rst
@@ -0,0 +1,108 @@
+.. highlight:: console
+
+``khost`` – Simple DNS lookup utility
+=====================================
+
+Synopsis
+--------
+
+:program:`khost` [*options*] *name* [*server*]
+
+Description
+-----------
+
+This utility sends a DNS query for the *name* to the *server* and prints a reply
+in more user-readable form. For more advanced DNS queries use :doc:`kdig<man_kdig>`
+instead.
+
+Parameters
+..........
+
+*name*
+ Is a domain name that is to be looked up. If the *name* is IPv4 or IPv6
+ address the PTR query type is used.
+
+*server*
+ Is a name or an address of the nameserver to send a query to. The address
+ can be specified using [address]:port notation. If no server is specified,
+ the servers from :file:`/etc/resolv.conf` are used.
+
+If no arguments are provided, :program:`khost` prints a short help.
+
+Options
+.......
+
+**-4**
+ Use the IPv4 protocol only.
+
+**-6**
+ Use the IPv6 protocol only.
+
+**-a**
+ Send ANY query with verbose mode.
+
+**-d**
+ Enable debug messages.
+
+**-h**, **--help**
+ Print the program help.
+
+**-r**
+ Disable recursion.
+
+**-T**
+ Use the TCP protocol.
+
+**-v**
+ Enable verbose output.
+
+**-V**, **--version**
+ Print the program version.
+
+**-w**
+ Wait forever for the reply.
+
+**-c** *class*
+ Set the query class (e.g. CH, CLASS4). The default class is IN.
+
+**-t** *type*
+ Set the query type (e.g. NS, IXFR=12345, TYPE65535). The default is to send 3
+ queries (A, AAAA and MX).
+
+**-R** *retries*
+ The number (>=0) of UDP retries to query a nameserver. The default is 1.
+
+**-W** *wait*
+ The time to wait for a reply in seconds. This timeout applies to each query
+ try. The default is 2 seconds.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+1. Get the A, AAAA and MX records for example.com::
+
+ $ khost example.com
+
+2. Get the reverse record for address 192.0.2.1::
+
+ $ khost 192.0.2.1
+
+3. Perform a verbose zone transfer for zone example.com::
+
+ $ khost -t AXFR -v example.com
+
+Files
+-----
+
+:file:`/etc/resolv.conf`
+
+See Also
+--------
+
+:manpage:`kdig(1)`, :manpage:`knsupdate(1)`.
diff --git a/doc/man_kjournalprint.rst b/doc/man_kjournalprint.rst
new file mode 100644
index 0000000..0753ae3
--- /dev/null
+++ b/doc/man_kjournalprint.rst
@@ -0,0 +1,88 @@
+.. highlight:: console
+
+``kjournalprint`` – Knot DNS journal print utility
+==================================================
+
+Synopsis
+--------
+
+:program:`kjournalprint` [*config_option* *config_argument*] [*option*...] *zone_name*
+
+:program:`kjournalprint` [*config_option* *config_argument*] **-z**
+
+Description
+-----------
+
+The program prints zone history stored in a journal database. As default,
+changes are colored for terminal.
+
+Config options
+..............
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+**-D**, **--dir** *path*
+ Use specified journal database path and default configuration.
+
+Options
+.......
+
+**-z**, **--zone-list**
+ Instead of reading the journal, display the list of zones in the DB.
+
+**-l**, **--limit** *limit*
+ Limits the number of displayed changes.
+
+**-s**, **--serial** *soa*
+ Start at a specific SOA serial.
+
+**-H**, **--check**
+ Enable additional journal semantic checks during printing.
+
+**-d**, **--debug**
+ Debug mode brief output.
+
+**-x**, **--mono**
+ Don't generate colorized output.
+
+**-n**, **--no-color**
+ An alias for **-x**. Use of this option is deprecated, it will be removed in the future.
+
+**-X**, **--color**
+ Force colorized output.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Parameters
+..........
+
+*zone_name*
+ A name of the zone to print the history for.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+Last (most recent) 5 changes without colors::
+
+ $ kjournalprint -nl 5 /var/lib/knot/journal example.com.
+
+See Also
+--------
+
+:manpage:`knotd(8)`, :manpage:`knot.conf(5)`.
diff --git a/doc/man_knotc.rst b/doc/man_knotc.rst
new file mode 100644
index 0000000..2b7d95e
--- /dev/null
+++ b/doc/man_knotc.rst
@@ -0,0 +1,377 @@
+.. highlight:: console
+
+``knotc`` – Knot DNS control utility
+====================================
+
+Synopsis
+--------
+
+:program:`knotc` [*parameters*] *action* [*action_args*]
+
+Description
+-----------
+
+This program controls a running `knotd` process using a socket.
+
+If an *action* is specified, it is performed and `knotc` exits, otherwise the program
+is executed in the interactive mode.
+
+Options
+.......
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+**-m**, **--max-conf-size** *MiB*
+ Set maximum size of the configuration database
+ (default is @conf_mapsize@ MiB, maximum 10000 MiB).
+
+**-s**, **--socket** *path*
+ Use a control UNIX socket path (default is :file:`@run_dir@/knot.sock`).
+
+**-t**, **--timeout** *seconds*
+ Use a control timeout in seconds. Set to 0 for infinity (default is 60).
+ The control socket operations are also subject to the :ref:`timeout<control_timeout>`
+ parameter set on the server side in server's Control configuration section.
+
+**-b**, **--blocking**
+ Zone event trigger commands wait until the event is finished. Control timeout
+ is set to infinity if not forced by explicit timeout specification.
+
+**-e**, **--extended**
+ Show extended output (even empty items in zone status).
+
+**-f**, **--force**
+ Forced operation. Overrides some checks.
+
+**-x**, **--mono**
+ Don't generate colorized output.
+
+**-X**, **--color**
+ Force colorized output in extended output or to a pipe.
+
+**-v**, **--verbose**
+ Enable debug output.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Actions
+.......
+
+**status** [*detail*]
+ Check if the server is running. Details are **version** for the running
+ server version, **workers** for the numbers of worker threads,
+ or **configure** for the configure summary.
+
+**stop**
+ Stop the server if running.
+
+**reload**
+ Reload the server configuration and modified zone files. All open zone
+ transactions will be aborted!
+
+**stats** [*module*\ [\ **.**\ *counter*\ ]]
+ Show global statistics counter(s). To print also counters with value 0, use
+ force option.
+
+**zone-check** [*zone*...]
+ Test if the server can load the zone. Semantic checks are executed if enabled
+ in the configuration. If invoked with the force option, an error is returned
+ when semantic check warning appears. (*)
+
+**zone-status** [*zone*...] [*filter*]
+ Show the zone status. Filters are **+role**, **+serial**, **+transaction**,
+ **+events**, **+freeze**, and **+catalog**. Empty zone parameters are omitted,
+ unless the **--extended** option is used. A single dash in the output represents
+ an unset value. Automatic colorization can be overruled using the **--mono** and
+ **--color** options.
+
+ The color code is:
+ *green* - zone acts as a master / *red* - zone acts as a slave,
+ *bold font (highlited)* - zone is active / *normal* - zone is empty,
+ *underscored* - zone is an interpreted catalog member.
+
+**zone-reload** [*zone*...]
+ Trigger a zone reload from a disk without checking its modification time. For
+ secondary zone, the refresh event from primary server(s) is scheduled;
+ for primary zone, the notify event to secondary server(s) is scheduled. An open
+ zone transaction will be aborted! If invoked with the force option, also zone
+ modules will be re-loaded, but blocking mode might not work reliably. (#)
+
+**zone-refresh** [*zone*...]
+ Trigger a check for the zone serial on the zone's primary server. If
+ the primary server has a newer zone, a transfer is scheduled. This command is
+ valid for secondary zones. (#)
+
+**zone-retransfer** [*zone*...]
+ Trigger a zone transfer from the zone's primary server. The server
+ doesn't check the serial of the primary server's zone. This command is valid
+ for secondary zones. (#)
+
+**zone-notify** [*zone*...]
+ Trigger a NOTIFY message to all configured remotes. This can help in cases
+ when previous NOTIFY had been lost or the secondary servers have been
+ offline. (#)
+
+**zone-flush** [*zone*...] [**+outdir** *directory*]
+ Trigger a zone journal flush to the configured zone file. If an output
+ directory is specified, the current zone is immediately dumped (in the
+ blocking mode) to a zone file in the specified directory. See
+ :ref:`Notes<notes>` below about the directory permissions. (#)
+
+**zone-backup** [*zone*...] **+backupdir** *directory* [*filter*...]
+ Trigger a zone data and metadata backup to a specified directory.
+ Available filters are **+zonefile**, **+journal**, **+timers**, **+kaspdb**,
+ **+catalog**, and their negative counterparts **+nozonefile**, **+nojournal**,
+ **+notimers**, **+nokaspdb**, and **+nocatalog**. With these filters set,
+ zone contents, zone's journal, zone related timers, zone related data in the
+ KASP database together with keys, and zone's catalog, respectively, are backed up,
+ or omitted from the backup. By default, filters **+zonefile**, **+timers**,
+ **+kaspdb**, **+catalog**, and **+nojournal** are set. Setting a filter
+ for an item doesn't change default settings for other items. If zone flushing
+ is disabled, original zone file is backed up instead of writing out zone
+ contents to a file. See :ref:`Notes<notes>` below about the directory
+ permissions. (#)
+
+**zone-restore** [*zone*...] **+backupdir** *directory* [*filter*...]
+ Trigger a zone data and metadata restore from a specified backup directory.
+ Optional filters are equivalent to the same filters of **zone-backup**.
+ Restore from backups created by Knot DNS releases prior to 3.1 is possible
+ with the force option. See :ref:`Notes<notes>` below about the directory
+ permissions. (#)
+
+**zone-sign** [*zone*...]
+ Trigger a DNSSEC re-sign of the zone. Existing signatures will be dropped.
+ This command is valid for zones with DNSSEC signing enabled. (#)
+
+**zone-keys-load** [*zone*...]
+ Trigger a load of DNSSEC keys and other signing material from KASP database
+ (which might have been altered manually). If suitable, re-sign the zone
+ afterwards (keeping valid signatures intact). (#)
+
+**zone-key-rollover** *zone* *key_type*
+ Trigger immediate key rollover. Publish new key and start a key rollover,
+ even when the key has a lifetime to go. Key type can be **ksk** (also for CSK)
+ or **zsk**. This command is valid for zones with DNSSEC signing and automatic
+ key management enabled. Note that complete key rollover consists of several steps
+ and the blocking mode relates to the initial one only! (#)
+
+**zone-ksk-submitted** *zone*...
+ Use when the zone's KSK rollover is in submission phase. By calling this command
+ the user confirms manually that the parent zone contains DS record for the new
+ KSK in submission phase and the old KSK can be retired. (#)
+
+**zone-freeze** [*zone*...]
+ Trigger a zone freeze. All running events will be finished and all new and pending
+ (planned) zone-changing events (load, refresh, update, flush, and DNSSEC signing)
+ will be held up until the zone is thawed. (#)
+
+**zone-thaw** [*zone*...]
+ Trigger dismissal of zone freeze. (#)
+
+**zone-xfr-freeze** [*zone*...]
+ Temporarily disable outgoing AXFR/IXFR for the zone(s). (#)
+
+**zone-xfr-thaw** [*zone*...]
+ Dismiss outgoing XFR freeze. (#)
+
+**zone-read** *zone* [*owner* [*type*]]
+ Get zone data that are currently being presented.
+
+**zone-begin** *zone*...
+ Begin a zone transaction.
+
+**zone-commit** *zone*...
+ Commit the zone transaction. All changes are applied to the zone.
+
+**zone-abort** *zone*...
+ Abort the zone transaction. All changes are discarded.
+
+**zone-diff** *zone*
+ Get zone changes within the transaction.
+
+**zone-get** *zone* [*owner* [*type*]]
+ Get zone data within the transaction.
+
+**zone-set** *zone* *owner* [*ttl*] *type* *rdata*
+ Add zone record within the transaction. The first record in a rrset
+ requires a ttl value specified.
+
+**zone-unset** *zone* *owner* [*type* [*rdata*]]
+ Remove zone data within the transaction.
+
+**zone-purge** *zone*... [**+orphan**] [*filter*...]
+ Purge zone data, zone file, journal, timers, and/or KASP data of specified zones.
+ Available filters are **+expire**, **+zonefile**, **+journal**, **+timers**,
+ **+kaspdb**, and **+catalog**. If no filter is specified, all filters are enabled.
+ If the zone is no longer configured, add **+orphan** parameter (zone file cannot
+ be purged in this case). When purging orphans, always check the server log for
+ possible errors. This command always requires the force option. (#)
+
+**zone-stats** *zone* [*module*\ [\ **.**\ *counter*\ ]]
+ Show zone statistics counter(s). To print also counters with value 0, use
+ force option.
+
+**conf-init**
+ Initialize the configuration database. If the database doesn't exist yet,
+ execute this command as an intended user to ensure the server is permitted
+ to access the database (e.g. *sudo -u knot knotc conf-init*). (*)
+
+**conf-check**
+ Check the server configuration. (*)
+
+**conf-import** *filename*
+ Import a configuration file into the configuration database. If the database
+ doesn't exist yet, execute this command as an intended user to ensure the server
+ is permitted to access the database (e.g. *sudo -u knot knotc conf-import ...*).
+ Also ensure the server is not using the configuration database at the same time! (*)
+
+**conf-export** [*filename*]
+ Export the configuration database into a config file or stdout. (*)
+
+**conf-list** [*item*]
+ List the configuration database sections or section items.
+
+**conf-read** [*item*]
+ Read the item from the active configuration database.
+
+**conf-begin**
+ Begin a writing configuration database transaction. Only one transaction
+ can be opened at a time.
+
+**conf-commit**
+ Commit the configuration database transaction.
+
+**conf-abort**
+ Rollback the configuration database transaction.
+
+**conf-diff** [*item*]
+ Get the item difference in the transaction.
+
+**conf-get** [*item*]
+ Get the item data from the transaction.
+
+**conf-set** *item* [*data*...]
+ Set the item data in the transaction.
+
+**conf-unset** [*item*] [*data*...]
+ Unset the item data in the transaction.
+
+.. _notes:
+
+Notes
+.....
+
+Empty or **--** *zone* parameter means all zones or all zones with a transaction.
+
+Use **@** *owner* to denote the zone name.
+
+Type *item* parameter in the form of *section*\ [**[**\ *id*\ **]**\ ][**.**\ *name*].
+
+(*) indicates a local operation which requires a configuration.
+
+(\#) indicates an optionally blocking operation.
+
+The **-b** and **-f** options can be placed right after the command name.
+
+Responses returned by `knotc` commands depend on the mode:
+
+- In the blocking mode, `knotc` reports if an error occurred during processing
+ of the command by the server. If an error is reported, a more detailed information
+ about the failure can usually be found in the server log.
+
+- In the non-blocking (default) mode, `knotc` doesn't report processing errors.
+ The `OK` response to triggering commands means that the command has been successfully
+ sent to the server. To verify if the operation succeeded, it's necessary to
+ check the server log.
+
+Actions **zone-flush**, **zone-backup**, and **zone-restore** are carried out by
+the `knotd` process. The directory specified must be accessible to the user account
+that `knotd` runs under and if the directory already exists, its permissions must be
+appropriate for that user account.
+
+Interactive mode
+................
+
+The utility provides interactive mode with basic line editing functionality,
+command completion, and command history.
+
+Interactive mode behavior can be customized in `~/.editrc`. Refer to
+:manpage:`editrc(5)` for details.
+
+Command history is saved in `~/.knotc_history`.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+Reload the whole server configuration
+.....................................
+
+::
+
+ $ knotc reload
+
+Flush the example.com and example.org zones
+...........................................
+
+::
+
+ $ knotc zone-flush example.com example.org
+
+Get the current server configuration
+....................................
+
+::
+
+ $ knotc conf-read server
+
+Get the list of the current zones
+.................................
+
+::
+
+ $ knotc conf-read zone.domain
+
+Get the primary servers for the example.com zone
+................................................
+
+::
+
+ $ knotc conf-read 'zone[example.com].master'
+
+Add example.org zone with a zonefile location
+.............................................
+
+::
+
+ $ knotc conf-begin
+ $ knotc conf-set 'zone[example.org]'
+ $ knotc conf-set 'zone[example.org].file' '/var/zones/example.org.zone'
+ $ knotc conf-commit
+
+Get the SOA record for each configured zone
+...........................................
+
+::
+
+ $ knotc zone-read -- @ SOA
+
+See Also
+--------
+
+:manpage:`knotd(8)`, :manpage:`knot.conf(5)`, :manpage:`editrc(5)`.
diff --git a/doc/man_knotd.rst b/doc/man_knotd.rst
new file mode 100644
index 0000000..0dd5a66
--- /dev/null
+++ b/doc/man_knotd.rst
@@ -0,0 +1,66 @@
+.. highlight:: console
+
+``knotd`` – Knot DNS server daemon
+==================================
+
+Synopsis
+--------
+
+:program:`knotd` [*parameters*]
+
+Description
+-----------
+
+Knot DNS is a high-performance authoritative DNS server. The `knotd` program is
+the DNS server daemon.
+
+Options
+.......
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+**-m**, **--max-conf-size** *MiB*
+ Set maximum size of the configuration database
+ (default is @conf_mapsize@ MiB, maximum 10000 MiB).
+
+**-s**, **--socket** *path*
+ Use a remote control UNIX socket path (default is :file:`@run_dir@/knot.sock`).
+
+**-d**, **--daemonize** [*directory*]
+ Run the server as a daemon. New root directory may be specified
+ (default is :file:`/`).
+
+**-v**, **--verbose**
+ Enable debug output.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Signals
+.......
+
+If the `knotd` process receives a SIGHUP signal, it reloads its configuration and
+reopens the log files, if they are configured. When `knotd` receives a SIGUSR1
+signal, it reloads all configured zones. Upon receiving a SIGINT signal, `knotd`
+exits.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+See Also
+--------
+
+:manpage:`knot.conf(5)`, :manpage:`knotc(8)`, :manpage:`keymgr(8)`,
+:manpage:`kjournalprint(8)`.
diff --git a/doc/man_knsec3hash.rst b/doc/man_knsec3hash.rst
new file mode 100644
index 0000000..7684e90
--- /dev/null
+++ b/doc/man_knsec3hash.rst
@@ -0,0 +1,57 @@
+.. highlight:: console
+
+``knsec3hash`` – NSEC hash computation utility
+==============================================
+
+Synopsis
+--------
+
+:program:`knsec3hash` *salt* *algorithm* *iterations* *name*
+
+:program:`knsec3hash` *algorithm* *flags* *iterations* *salt* *name*
+
+Description
+-----------
+
+This utility generates a NSEC3 hash for a given domain name and parameters of NSEC3 hash.
+
+Parameters
+..........
+
+*salt*
+ Specifies a binary salt encoded as a hexadecimal string.
+
+*algorithm*
+ Specifies a hashing algorithm by number. Currently, the only supported algorithm is SHA-1 (number 1).
+
+*iterations*
+ Specifies the number of additional iterations of the hashing algorithm.
+
+*name*
+ Specifies the domain name to be hashed.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+::
+
+ $ knsec3hash 1 0 10 c01dcafe knot-dns.cz
+ 7PTVGE7QV67EM61ROS9238P5RAKR2DM7 (salt=c01dcafe, hash=1, iterations=10)
+
+::
+
+ $ knsec3hash - 1 0 net
+ A1RT98BS5QGC9NFI51S9HCI47ULJG6JH (salt=-, hash=1, iterations=0)
+
+See Also
+--------
+
+:rfc:`5155` – DNS Security (DNSSEC) Hashed Authenticated Denial of Existence.
+
+:manpage:`knotc(8)`, :manpage:`knotd(8)`.
diff --git a/doc/man_knsupdate.rst b/doc/man_knsupdate.rst
new file mode 100644
index 0000000..552a00c
--- /dev/null
+++ b/doc/man_knsupdate.rst
@@ -0,0 +1,181 @@
+.. highlight:: console
+
+``knsupdate`` – Dynamic DNS update utility
+==========================================
+
+Synopsis
+--------
+
+:program:`knsupdate` [*options*] [*filename*]
+
+Description
+-----------
+
+This utility sends Dynamic DNS update messages to a DNS server. Update content
+is read from a file (if the parameter *filename* is given) or from the standard
+input.
+
+The format of updates is textual and is made up of commands. Every command is
+placed on the separate line of the input. Lines starting with a semicolon are
+comments and are not processed.
+
+Options
+.......
+
+**-d**
+ Enable debug messages.
+
+**-h**, **--help**
+ Print the program help.
+
+**-k** *keyfile*
+ Use the TSIG key stored in a file *keyfile* to authenticate the request. The
+ file should contain the key in the same format, which is accepted by the
+ **-y** option.
+
+**-p** *port*
+ Set the port to use for connections to the server (if not explicitly specified
+ in the update). The default is 53.
+
+**-r** *retries*
+ The number of retries for UDP requests. The default is 3.
+
+**-t** *timeout*
+ The total timeout (for all UDP update tries) of the update request in seconds.
+ The default is 12. If set to zero, the timeout is infinite.
+
+**-v**
+ Use a TCP connection.
+
+**-V**, **--version**
+ Print the program version.
+
+**-y** [*alg*:]\ *name*:*key*
+ Use the TSIG key with a name *name* to authenticate the request. The *alg*
+ part specifies the algorithm (the default is hmac-sha256) and *key* specifies
+ the shared secret encoded in Base64.
+
+Commands
+........
+
+**server** *name* [*port*]
+ Specifies a receiving server of the dynamic update message. The *name* parameter
+ can be either a host name or an IP address. If the *port* is not specified,
+ the default port is used. The default port value can be controlled using
+ the **-p** program option.
+
+**local** *address* [*port*]
+ Specifies outgoing *address* and *port*. If no local is specified, the
+ address and port are set by the system automatically. The default port number
+ is 0.
+
+**zone** *name*
+ Specifies that all updates are done within a zone *name*. The zone name doesn't
+ have a default and must be set explicitly.
+
+**origin** *name*
+ Specifies fully qualified domain name suffix which is appended to non-fqd
+ owners in update commands. The default is the terminal label (**.**).
+
+**class** *name*
+ Sets *name* as the default class for all updates. If not used, the default
+ class is IN.
+
+**ttl** *value*
+ Sets *value* as the default TTL (in seconds). If not used, the default value
+ is 3600.
+
+**key** [*alg*:]\ *name* *key*
+ Specifies the TSIG *key* named *name* to authenticate the request. An optional
+ *alg* algorithm can be specified. This command has the same effect as
+ the program option **-y**.
+
+[**prereq**] **nxdomain** *name*
+ Adds a prerequisite for a non-existing record owned by *name*.
+
+[**prereq**] **yxdomain** *name*
+ Adds a prerequisite for an existing record owned by *name*.
+
+[**prereq**] **nxrrset** *name* [*class*] *type*
+ Adds a prerequisite for a non-existing record of the *type* owned by *name*.
+ Internet *class* is expected.
+
+[**prereq**] **yxrrset** *name* [*class*] *type* [*data*]
+ Adds a prerequisite for an existing record of the *type* owned by *name*
+ with optional *data*. Internet *class* is expected.
+
+[**update**] **add** *name* [*ttl*] [*class*] *type* *data*
+ Adds a request to add a new resource record into the zone.
+ Please note that if the *name* is not fully qualified domain name, the
+ current origin name is appended to it.
+
+[**update**] **del**\[**ete**] *name* [*ttl*] [*class*] [*type*] [*data*]
+ Adds a request to remove all (or matching *class*, *type* or *data*)
+ resource records from the zone. There is the same requirement for the *name*
+ parameter as in **update add** command. The *ttl* item is ignored.
+
+**show**
+ Displays current content of the update message.
+
+**send**
+ Sends the current update message and cleans the list of updates.
+
+**answer**
+ Displays the last answer from the server.
+
+**debug**
+ Enable debugging. This command has the same meaning as the **-d** program option.
+
+**exit**
+ End the program.
+
+Notes
+-----
+
+Options **-k** and **-y** can not be used simultaneously.
+
+Dnssec-keygen keyfile format is not supported. Use :manpage:`keymgr(8)` instead.
+
+Zone name/server guessing is not supported if the zone name/server is not specified.
+
+Empty line doesn't send the update.
+
+Interactive mode
+................
+
+The utility provides interactive mode with basic line editing functionality,
+command completion, and command history.
+
+Interactive mode behavior can be customized in `~/.editrc`. Refer to
+:manpage:`editrc(5)` for details.
+
+Command history is saved in `~/.knsupdate_history`.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+1. Send one update of the zone example.com to the server 192.168.1.1. The update
+ contains two new records::
+
+ $ knsupdate
+ knsupdate> server 192.168.1.1
+ knsupdate> zone example.com.
+ knsupdate> origin example.com.
+ knsupdate> ttl 3600
+ knsupdate> add test1.example.com. 7200 A 192.168.2.2
+ knsupdate> add test2 TXT "hello"
+ knsupdate> show
+ knsupdate> send
+ knsupdate> answer
+ knsupdate> exit
+
+See Also
+--------
+
+:manpage:`kdig(1)`, :manpage:`khost(1)`, :manpage:`keymgr(8)`, :manpage:`editrc(5)`.
diff --git a/doc/man_kxdpgun.rst b/doc/man_kxdpgun.rst
new file mode 100644
index 0000000..bb6c917
--- /dev/null
+++ b/doc/man_kxdpgun.rst
@@ -0,0 +1,183 @@
+.. highlight:: console
+
+``kxdpgun`` – DNS benchmarking tool
+===================================
+
+Synopsis
+--------
+
+:program:`kxdpgun` [*options*] **-i** *filename* *targetIP*
+
+Description
+-----------
+
+Powerful generator of DNS traffic, sending and receiving packets through XDP.
+
+Queries are generated according to a textual file which is read sequentially
+in a loop until a configured duration elapses. The order of queries is not
+guaranteed. Responses are received (unless disabled) and counted, but not
+checked against queries.
+
+The number of parallel threads is autodetected according to the number of queues
+configured for the network interface.
+
+Options
+.......
+
+**-t**, **--duration** *seconds*
+ Duration of traffic generation, specified as a decimal number in seconds
+ (default is 5.0).
+
+**-T**, **--tcp**\[\ **=**\ *debug_mode*\]
+ Send queries over TCP. See the list of optional debug modes below.
+
+**-U**, **--quic**\[\ **=**\ *debug_mode*\]
+ Send queries over QUIC. See the list of optional debug modes below.
+
+**-Q**, **--qps** *queries*
+ Number of queries-per-second (approximately) to be sent (default is 1000).
+ The program is not optimized for low speeds at which it may lose
+ communication packets. The recommended minimum speed is 2 packets per thread
+ (Rx/Tx queue).
+
+**-b**, **--batch** *size*
+ Send more queries in a batch. Improves QPS but may affect the counterpart's
+ packet loss (default is 10 for UDP and 1 for TCP/QUIC).
+
+**-r**, **--drop**
+ Drop incoming responses. Improves QPS, but disables response statistics.
+
+**-p**, **--port** *number*
+ Remote destination port (default is 53 for UDP/TCP, 853 for QUIC).
+
+**-F**, **--affinity** *cpu_spec*
+ CPU affinity for all threads specified in the format [<cpu_start>][s<cpu_step>],
+ where <cpu_start> is the CPU ID for the first thread and <cpu_step> is the
+ CPU ID increment for next thread (default is 0s1).
+
+**-i**, **--infile** *filename*
+ Path to a file with query templates.
+
+**-I**, **--interface** *interface*
+ Network interface for outgoing communication. This can be useful in situations
+ when the interfaces are in a bond for example.
+
+**-l**, **--local** *localIP*\ [**/**\ *prefix*]
+ Override the auto-detected source IP address. If an address range is specified
+ instead, various IPs from the range will be used for different queries uniformly
+ (address range not supported in the QUIC mode).
+
+*targetIP*
+ The IPv4 or IPv6 address of remote destination.
+
+**-L**, **--mac-local**
+ Override auto-detected local MAC address.
+
+**-R**, **--mac-remote**
+ Override auto-detected remote MAC address.
+
+**-v**, **--vlan** *id*
+ Add VLAN 802.1Q header with the given id. VLAN offloading should be disabled.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Queries file format
+...................
+
+Each line describes a query in the form:
+
+*query_name* *query_type* [*flags*]
+
+Where *query_name* is a domain name to be queried, *query_type* is a record type
+name, and *flags* is a single character:
+
+**E** Send query with EDNS.
+
+**D** Request DNSSEC (EDNS + DO flag).
+
+TCP/QUIC debug modes
+....................
+
+**0**
+ Perform full handshake for all connections (QUIC only).
+
+**1**
+ Just send SYN (Initial) and receive SYN-ACK (Handshake).
+
+**2**
+ Perform TCP/QUIC handshake and don't send anything, allow close initiated by counterpart.
+
+**3**
+ Perform TCP/QUIC handshake and don't react further.
+
+**5**
+ Send incomplete query (N-1 bytes) and don't react further.
+
+**7**
+ Send query and don't ACK the response or anything further.
+
+**8**
+ Don't close the connection and ignore close by counterpart (TCP only).
+
+**9**
+ Operate normally except for not ACKing the final FIN+ACK (TCP only).
+
+Signals
+.......
+
+Sending USR1 signal to a running process triggers current statistics dump
+to the standard output.
+
+Notes
+-----
+
+Linux kernel 4.18+ is required.
+
+The utility has to be executed under root or with these capabilities:
+CAP_NET_RAW, CAP_NET_ADMIN, CAP_SYS_ADMIN, CAP_IPC_LOCK, and CAP_SYS_RESOURCE
+(Linux < 5.11).
+
+The utility allocates source UDP/TCP ports from the range 2000-65535.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+Examples
+--------
+
+Manually created queries file::
+
+ abc6.example.com. AAAA
+ nxdomain.example.com. A
+ notzone. A
+ a.example.com. NS E
+ ab.example.com. A D
+ abcd.example.com. DS D
+
+Queries file generated from a zone file (Knot DNS format)::
+
+ cat ZONE_FILE | awk "{print \$1,\$3}" | grep -E "(NS|DS|A|AAAA|PTR|MX|SOA)$" | sort -u -R > queries.txt
+
+Basic usage::
+
+ # kxdpgun -i ~/queries.txt 2001:DB8::1
+
+*Using UDP with increased batch size*::
+
+ # kxdpgun -t 20 -Q 1000000 -i ~/queries.txt -b 20 -p 8853 192.0.2.1
+
+*Using TCP*::
+
+ # kxdpgun -t 20 -Q 100000 -i ~/queries.txt -T -p 8853 192.0.2.1
+
+See Also
+--------
+
+:manpage:`kdig(1)`.
diff --git a/doc/man_kzonecheck.rst b/doc/man_kzonecheck.rst
new file mode 100644
index 0000000..b81bcd5
--- /dev/null
+++ b/doc/man_kzonecheck.rst
@@ -0,0 +1,62 @@
+.. highlight:: console
+
+``kzonecheck`` – Knot DNS zone file checking tool
+=================================================
+
+Synopsis
+--------
+
+:program:`kzonecheck` [*options*] *filename*
+
+Description
+-----------
+
+The utility checks zone file syntax and runs semantic checks on the zone
+content. The executed checks are the same as the checks run by the Knot
+DNS server.
+
+Please, refer to the ``semantic-checks`` configuration option in
+:manpage:`knot.conf(5)` for the full list of available semantic checks.
+
+Parameters
+..........
+
+*filename*
+ Path to the zone file to be checked. For reading from **stdin** use **/dev/stdin**
+ or just **-**.
+
+Options
+.......
+
+**-o**, **--origin** *origin*
+ Zone origin. If not specified, the origin is determined from the file name
+ (possibly removing the ``.zone`` suffix).
+
+**-d**, **--dnssec** **on**\|\ **off**
+ Also check DNSSEC-related records. The default is to decide based on the
+ existence of a RRSIG for SOA.
+
+**-t**, **--time** *time*
+ Current time specification. Use UNIX timestamp, YYYYMMDDHHmmSS
+ format, or [+/-]\ *time*\ [unit] format, where unit can be **Y**, **M**,
+ **D**, **h**, **m**, or **s**. Default is current UNIX timestamp.
+
+**-v**, **--verbose**
+ Enable debug output.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+See Also
+--------
+
+:manpage:`knotd(8)`, :manpage:`knot.conf(5)`.
diff --git a/doc/man_kzonesign.rst b/doc/man_kzonesign.rst
new file mode 100644
index 0000000..c9143f0
--- /dev/null
+++ b/doc/man_kzonesign.rst
@@ -0,0 +1,69 @@
+.. highlight:: console
+
+``kzonesign`` – DNSSEC signing utility
+======================================
+
+Synopsis
+--------
+
+:program:`kzonesign` [*config_option* *config_argument*] [*options*] *zone_name*
+
+Description
+-----------
+
+This utility reads the zone's zone file, signs the zone according to given
+configuration, and writes the signed zone file back. An alternative mode
+is DNSSEC validation of the given zone. The signing or validation
+can run in parallel if enabled in the configuration (see policy.signing-threads
+and zone.adjust-threads).
+
+Config options
+..............
+
+**-c**, **--config** *file*
+ Use a textual configuration file (default is :file:`@config_dir@/knot.conf`).
+
+**-C**, **--confdb** *directory*
+ Use a binary configuration database directory (default is :file:`@storage_dir@/confdb`).
+ The default configuration database, if exists, has a preference to the default
+ configuration file.
+
+Options
+.......
+
+**-o**, **--outdir** *dir_name*
+ Write the output zone file to the specified directory instead of the configured one.
+
+**-r**, **--rollover**
+ Allow key roll-overs and NSEC3 re-salt. In order to finish possible KSK submission,
+ set the KSK's **active** timestamp to now (**+0**) using :doc:`keymgr<man_keymgr>`.
+
+**-v**, **--verify**
+ Instead of (re-)signing the zone, just verify that the zone is correctly signed.
+
+**-t**, **--time** *timestamp*
+ Sign/verify the zone (and roll the keys if necessary) as if it was at the time
+ specified by timestamp.
+
+**-h**, **--help**
+ Print the program help.
+
+**-V**, **--version**
+ Print the program version.
+
+Parameters
+..........
+
+*zone_name*
+ A name of the zone to be signed.
+
+Exit values
+-----------
+
+Exit status of 0 means successful operation. Any other exit status indicates
+an error.
+
+See Also
+--------
+
+:manpage:`knot.conf(5)`, :manpage:`keymgr(8)`.
diff --git a/doc/migration.rst b/doc/migration.rst
new file mode 100644
index 0000000..e9ec1d2
--- /dev/null
+++ b/doc/migration.rst
@@ -0,0 +1,380 @@
+.. highlight:: none
+.. _Migration:
+
+*********
+Migration
+*********
+
+.. _Upgrade 2.4.x to 2.5.x:
+
+Upgrade 2.4.x to 2.5.x
+======================
+
+This chapter describes some steps necessary after upgrading Knot DNS from
+version 2.4.x to 2.5.x.
+
+.. _Building changes:
+
+Building changes
+----------------
+
+The ``--enable-dnstap`` configure option now enables the dnstap support in
+:doc:`kdig<man_kdig>` only! To build the dnstap query module, ``--with-module-dnstap``
+have to be used.
+
+Since Knot DNS version 2.5.0 each query module can be configured to be:
+
+- disabled: ``--with-module-``\ MODULE_NAME\ ``=no``
+- embedded: ``--with-module-``\ MODULE_NAME\ ``=yes``
+- external: ``--with-module-``\ MODULE_NAME\ ``=shared`` (excluding
+ ``dnsproxy`` and ``onlinesign``)
+
+The ``--with-timer-mapsize`` configure option was replaced with the runtime
+``template.max-timer-db-size`` configuration option.
+
+.. _KASP DB migration:
+
+KASP DB migration
+-----------------
+
+Knot DNS version 2.4.x and earlier uses JSON files to store DNSSEC keys metadata,
+one for each zone. 2.5.x versions store those in binary format in a LMDB, all zones
+together. The migration is possible with the
+`pykeymgr <https://gitlab.nic.cz/knot/knot-dns/blob/2.6/src/utils/pykeymgr/pykeymgr.in>`_
+script::
+
+ $ pykeymgr -i path/to/keydir
+
+The path to KASP DB directory is configuration-dependent, usually it is the ``keys``
+subdirectory in the zone storage.
+
+In rare installations, the JSON files might be spread across more directories. In such
+case, it is necessary to put them together into one directory and migrate at once.
+
+.. _Configuration changes 2.5:
+
+Configuration changes
+---------------------
+
+It is no longer possible to configure KASP DB per zone or in a non-default
+template. Ensure just one common KASP DB configuration in the default
+template.
+
+As Knot DNS version 2.5.0 brings dynamically loaded modules, some modules
+were renamed for technical reasons. So it is necessary to rename all
+occurrences (module section names and references from zones or templates)
+of the following module names in the configuration::
+
+ mod-online-sign -> mod-onlinesign
+
+ mod-synth-record -> mod-synthrecord
+
+.. _Upgrade 2.5.x to 2.6.x:
+
+Upgrade 2.5.x to 2.6.x
+======================
+
+Upgrading from Knot DNS version 2.5.x to 2.6.x is almost seamless.
+
+.. _Configuration changes 2.6:
+
+Configuration changes
+---------------------
+
+The ``dsa`` and ``dsa-nsec3-sha1`` algorithm values are no longer supported
+by the :ref:`policy_algorithm` option.
+
+The ``ixfr-from-differences`` zone/template option was deprecated in favor of
+the :ref:`zone_zonefile-load` option.
+
+.. _Upgrade 2.6.x to 2.7.x:
+
+Upgrade 2.6.x to 2.7.x
+======================
+
+Upgrading from Knot DNS version 2.6.x to 2.7.x is seamless if no obsolete
+configuration or module rosedb is used.
+
+.. _Upgrade 2.7.x to 2.8.x:
+
+Upgrade 2.7.x to 2.8.x
+======================
+
+Upgrading from Knot DNS version 2.7.x to 2.8.x is seamless.
+
+However, if the previous version was migrated (possibly indirectly)
+from version 2.5.x, the format of the keys stored in
+Keys And Signature Policy Database
+is no longer compatible and needs to be updated.
+
+The easiest ways to update how keys are stored in KASP DB is to modify
+with Keymgr version 2.7.x
+some of each key's parameters in an undamaging way, e.g.::
+
+ $ keymgr example.com. list
+ $ keymgr example.com. set <keyTag> created=1
+ $ keymgr example.com. set <keyTag2> created=1
+ ...
+
+.. _Upgrade 2.8.x to 2.9.x:
+
+Upgrade 2.8.x to 2.9.x
+======================
+
+Upgrading from Knot DNS version 2.8.x to 2.9.x is almost seamless but check
+the following changes first.
+
+Configuration changes
+---------------------
+
+- Imperfect runtime reconfiguration of :ref:`server_udp-workers`,
+ :ref:`server_tcp-workers`, and :ref:`server_listen`
+ is no longer supported.
+
+- Replaced options (with backward compatibility):
+
+ .. csv-table::
+ :header: Old section, Old item name, New section, New item name
+ :widths: 35, 60, 35, 60
+
+ :ref:`server<Server section>` , ``tcp-reply-timeout`` [s] , :ref:`server<Server section>` , :ref:`server_tcp-remote-io-timeout` [ms]
+ :ref:`server<Server section>` , ``max-tcp-clients`` , :ref:`server<Server section>` , :ref:`server_tcp-max-clients`
+ :ref:`server<Server section>` , ``max-udp-payload`` , :ref:`server<Server section>` , :ref:`server_udp-max-payload`
+ :ref:`server<Server section>` , ``max-ipv4-udp-payload`` , :ref:`server<Server section>` , :ref:`server_udp-max-payload-ipv4`
+ :ref:`server<Server section>` , ``max-ipv6-udp-payload`` , :ref:`server<Server section>` , :ref:`server_udp-max-payload-ipv6`
+ :ref:`template<Template section>` , ``journal-db`` , :ref:`database<Database section>` , :ref:`database_journal-db`
+ :ref:`template<Template section>` , ``journal-db-mode`` , :ref:`database<Database section>` , :ref:`database_journal-db-mode`
+ :ref:`template<Template section>` , ``max-journal-db-size`` , :ref:`database<Database section>` , :ref:`database_journal-db-max-size`
+ :ref:`template<Template section>` , ``kasp-db`` , :ref:`database<Database section>` , :ref:`database_kasp-db`
+ :ref:`template<Template section>` , ``max-kasp-db-size`` , :ref:`database<Database section>` , :ref:`database_kasp-db-max-size`
+ :ref:`template<Template section>` , ``timer-db`` , :ref:`database<Database section>` , :ref:`database_timer-db`
+ :ref:`template<Template section>` , ``max-timer-db-size`` , :ref:`database<Database section>` , :ref:`database_timer-db-max-size`
+ :ref:`zone<Zone section>` , ``max-journal-usage`` , :ref:`zone<Zone section>` , :ref:`zone_journal-max-usage`
+ :ref:`zone<Zone section>` , ``max-journal-depth`` , :ref:`zone<Zone section>` , :ref:`zone_journal-max-depth`
+ :ref:`zone<Zone section>` , ``max-zone-size`` , :ref:`zone<Zone section>` , :ref:`zone_zone-max-size`
+ :ref:`zone<Zone section>` , ``max-refresh-interval`` , :ref:`zone<Zone section>` , :ref:`zone_refresh-max-interval`
+ :ref:`zone<Zone section>` , ``min-refresh-interval`` , :ref:`zone<Zone section>` , :ref:`zone_refresh-min-interval`
+
+- Removed options (no backward compatibility):
+
+ - ``server.tcp-handshake-timeout``
+ - ``zone.request-edns-option``
+
+- New default values for:
+
+ - :ref:`server_tcp-workers`
+ - :ref:`server_tcp-max-clients`
+ - :ref:`server_udp-max-payload`
+ - :ref:`server_udp-max-payload-ipv4`
+ - :ref:`server_udp-max-payload-ipv6`
+
+- New DNSSEC policy option :ref:`policy_rrsig-pre-refresh` may affect
+ configuration validity, which is ``rrsig-refresh + rrsig-pre-refresh < rrsig-lifetime``
+
+Miscellaneous changes
+---------------------
+
+- Memory use estimation via ``knotc zone-memstats`` was removed
+- Based on `<https://tools.ietf.org/html/draft-ietf-dnsop-server-cookies>`_
+ the module :ref:`DNS Cookies<mod-cookies>` was updated to be interoperable
+- Number of open files limit is set to 1048576 in upstream packages
+
+.. _Upgrade 2.9.x to 3.0.x:
+
+Upgrade 2.9.x to 3.0.x
+======================
+
+Knot DNS version 3.0.x is functionally compatible with 2.9.x with the following
+exceptions.
+
+ACL
+---
+
+Configuration option :ref:`acl_update-owner-name` is newly FQDN-sensitive.
+It means that values ``a.example.com`` and ``a.example.com.`` are not equivalent.
+
+Module synthrecord
+------------------
+
+:ref:`Reverse IPv6 address shortening<mod-synthrecord_reverse-short>` is enabled by default.
+For example, the module generates::
+
+ dynamic-2620-0-b61-100--1.test. 400 IN AAAA 2620:0:b61:100::1
+
+instead of::
+
+ dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. 400 IN AAAA 2620:0:b61:100::1
+
+Query module API change
+-----------------------
+
+The following functions require additional parameter (thread id – ``qdata->params->thread_id``)
+on the second position::
+
+ knotd_mod_stats_incr()
+ knotd_mod_stats_decr()
+ knotd_mod_stats_store()
+
+Building notes
+--------------
+
+- The embedded library *LMDB* is no longer part of the source code. Almost every
+ modern operating system has a sufficient version of this library.
+- DoH support in kdig requires optional library *libnghttp2*.
+- XDP support on Linux requires optional library *libbpf >= 0.0.6*. If not available,
+ an embedded library can be used via ``--enable-xdp=yes`` configure option.
+
+.. _Upgrade 3.0.x to 3.1.x:
+
+Upgrade 3.0.x to 3.1.x
+======================
+
+Knot DNS version 3.1.x is functionally compatible with 3.0.x with the following
+exceptions.
+
+Configuration changes
+---------------------
+
+- Automatic SOA serial incrementation (``zonefile-load: difference-no-serial``)
+ requires having full zone stored in the journal (``journal-content: all``).
+ This change is necessary for reliable operation.
+
+- Replaced options (with backward compatibility):
+
+ .. csv-table::
+ :header: Old section, Old item name, New section, New item name
+ :widths: 40, 60, 40, 60
+
+ :ref:`server<Server section>`, ``listen-xdp``, :ref:`xdp<XDP section>`, :ref:`xdp_listen`
+
+- Ignored obsolete options (with a notice log):
+
+ - ``server.max-zone-size``
+ - ``server.max-journal-depth``
+ - ``server.max-journal-usage``
+ - ``server.max-refresh-interval``
+ - ``server.min-refresh-interval``
+ - ``server.max-ipv4-udp-payload``
+ - ``server.max-ipv6-udp-payload``
+ - ``server.max-udp-payload``
+ - ``server.max-tcp-clients``
+ - ``server.tcp-reply-timeout``
+ - ``template.journal-db``
+ - ``template.kasp-db``
+ - ``template.timer-db``
+ - ``template.max-journal-db-size``
+ - ``template.max-timer-db-size``
+ - ``template.max-kasp-db-size``
+ - ``template.journal-db-mode``
+
+- Silently ignored obsolete options:
+
+ - ``server.tcp-handshake-timeout``
+ - ``zone.disable-any``
+
+Zone backup and restore
+-----------------------
+
+The online backup format has changed slightly since 3.0 version. For zone-restore
+from backups in the previous format, it's necessary to set the *-f* option.
+Offline restore procedure of zone files from online backups is different than
+what it was before. The details are described in :ref:`Data and metadata backup`.
+
+Building notes
+--------------
+
+- The configure option ``--enable-xdp=yes`` has slightly changed its semantics.
+ It first tries to find an external library *libbpf*. If it's not detected,
+ the embedded one is used instead.
+- The kxdpgun tool also depends on library *libmnl*.
+
+Packaging
+---------
+
+Users who use module :ref:`geoip<mod-geoip>` or :ref:`dnstap<mod-dnstap>` might
+need installing an additional package with the module.
+
+.. _Upgrade 3.1.x to 3.2.x:
+
+Upgrade 3.1.x to 3.2.x
+======================
+
+Knot DNS version 3.2.x is functionally compatible with 3.1.x with the following
+exceptions.
+
+Configuration changes
+---------------------
+
+- Ignored obsolete option (with a notice log):
+
+ - ``server.listen-xdp``
+
+Utilities:
+----------
+
+- :doc:`knotc<man_knotc>` prints simplified zones status by default. Use ``-e``
+ for full output.
+- :doc:`keymgr<man_keymgr>` uses the brief key listing mode by default. Use ``-e``
+ for full output.
+- :doc:`keymgr<man_keymgr>` parameter ``-d`` was renamed to ``-D``.
+- :doc:`kjournalprint<man_kjournalprint>` parameter ``-c`` was renamed to ``-H``.
+
+Packaging
+---------
+
+- Linux distributions Debian 9 and Ubuntu 16.04 are no longer supported.
+
+- Packages for CentOS 7 are stored in a separate COPR repository
+ ``cznic/knot-dns-latest-centos7``.
+
+- Utilities :doc:`kzonecheck<man_kzonecheck>`, :doc:`kzonesign<man_kzonesign>`,
+ and :doc:`knsec3hash<man_knsec3hash>` are located in a new ``knot-dnssecutils``
+ package.
+
+Python
+------
+
+- Compatibility with Python 2 was removed.
+
+.. _Knot DNS for BIND users:
+
+Knot DNS for BIND users
+=======================
+
+.. _Automatic DNSSEC signing:
+
+Automatic DNSSEC signing
+------------------------
+
+Migrating automatically signed zones from BIND to Knot DNS requires copying
+up-to-date zone files from BIND, importing existing private keys, and updating
+server configuration:
+
+1. To obtain current content of the zone which is being migrated,
+ request BIND to flush the zone into the zone file: ``rndc sync
+ example.com``.
+
+ .. NOTE::
+ If dynamic updates (DDNS) are enabled for the given zone, you
+ might need to freeze the zone before flushing it. That can be done
+ similarly::
+
+ $ rndc freeze example.com
+
+2. Copy the fresh zone file into the zones :ref:`storage<zone_storage>`
+ directory of Knot DNS.
+
+3. Import all existing zone keys into the KASP database. Make sure that all
+ the keys were imported correctly::
+
+ $ keymgr example.com. import-bind path/to/Kexample.com.+013+11111
+ $ keymgr example.com. import-bind path/to/Kexample.com.+013+22222
+ $ ...
+ $ keymgr example.com. list
+
+ .. NOTE::
+ If the server configuration file or database is not at the default location,
+ add a configuration parameter (-c or -C). See :doc:`keymgr<man_keymgr>`
+ for more info about required access rights to the key files.
+
+4. Follow :ref:`Automatic DNSSEC signing` steps to configure DNSSEC signing.
diff --git a/doc/modules.rst.in b/doc/modules.rst.in
new file mode 100644
index 0000000..727e9e0
--- /dev/null
+++ b/doc/modules.rst.in
@@ -0,0 +1,8 @@
+.. highlight:: none
+.. _Modules:
+
+*******
+Modules
+*******
+
+@DOC_MODULES@
diff --git a/doc/operation.rst b/doc/operation.rst
new file mode 100644
index 0000000..d27642d
--- /dev/null
+++ b/doc/operation.rst
@@ -0,0 +1,1200 @@
+.. highlight:: console
+.. _Operation:
+
+*********
+Operation
+*********
+
+The Knot DNS server part :doc:`knotd<man_knotd>` can run either in the foreground,
+or in the background using the ``-d`` option. When run in the foreground, it
+doesn't create a PID file. Other than that, there are no differences and you
+can control both the same way.
+
+The tool :doc:`knotc<man_knotc>` is designed as a user front-end, making it easier
+to control a running server daemon. If you want to control the daemon directly,
+use ``SIGINT`` to quit the process or ``SIGHUP`` to reload the configuration.
+
+If you pass neither configuration file (``-c`` parameter) nor configuration
+database (``-C`` parameter), the server will first attempt to use the default
+configuration database stored in ``/var/lib/knot/confdb`` or the
+default configuration file stored in ``/etc/knot/knot.conf``. Both the
+default paths can be reconfigured with ``--with-storage=path`` or
+``--with-configdir=path`` respectively.
+
+Example of server start as a daemon::
+
+ $ knotd -d -c knot.conf
+
+Example of server shutdown::
+
+ $ knotc -c knot.conf stop
+
+For a complete list of actions refer to the program help (``-h`` parameter)
+or to the corresponding manual page.
+
+Also, the server needs to create :ref:`server_rundir` and :ref:`zone_storage`
+directories in order to run properly.
+
+.. _Configuration database:
+
+Configuration database
+======================
+
+In the case of a huge configuration file, the configuration can be stored
+in a binary database. Such a database can be simply initialized::
+
+ $ knotc conf-init
+
+or preloaded from a file::
+
+ $ knotc conf-import input.conf
+
+Also the configuration database can be exported into a textual file::
+
+ $ knotc conf-export output.conf
+
+.. WARNING::
+ The import and export commands access the configuration database
+ directly, without any interaction with the server. Therefore, any data
+ not yet committed to the database won't be exported. And the server won't
+ reflect imported configuration correctly. So it is strictly recommended to
+ import new configuration when the server is not running.
+
+.. _Dynamic configuration:
+
+Dynamic configuration
+=====================
+
+The configuration database can be accessed using the server control interface
+while the server is running. To get the full power of the dynamic configuration,
+the server must be started with a specified configuration database location
+or with the default database initialized. Otherwise all the changes to the
+configuration will be temporary (until the server is stopped).
+
+.. NOTE::
+ The database can be :ref:`imported<Configuration database>` in advance.
+
+Most of the commands get an item name and value parameters. The item name is
+in the form of ``section[identifier].name``. If the item is multivalued,
+more values can be specified as individual (command line) arguments.
+
+.. CAUTION::
+ Beware of the possibility of pathname expansion by the shell. For this reason,
+ it is advisable to escape (with backslash) square brackets or to quote command parameters if
+ not executed in the interactive mode.
+
+To get the list of configuration sections or to get the list of section items::
+
+ $ knotc conf-list
+ $ knotc conf-list 'server'
+
+To get the whole configuration or to get the whole configuration section or
+to get all section identifiers or to get a specific configuration item::
+
+ $ knotc conf-read
+ $ knotc conf-read 'remote'
+ $ knotc conf-read 'zone.domain'
+ $ knotc conf-read 'zone[example.com].master'
+
+.. WARNING::
+ The following operations don't work on OpenBSD!
+
+Modifying operations require an active configuration database transaction.
+Just one transaction can be active at a time. Such a transaction then can
+be aborted or committed. A semantic check is executed automatically before
+every commit::
+
+ $ knotc conf-begin
+ $ knotc conf-abort
+ $ knotc conf-commit
+
+To set a configuration item value or to add more values or to add a new
+section identifier or to add a value to all identified sections::
+
+ $ knotc conf-set 'server.identity' 'Knot DNS'
+ $ knotc conf-set 'server.listen' '0.0.0.0@53' '::@53'
+ $ knotc conf-set 'zone[example.com]'
+ $ knotc conf-set 'zone.slave' 'slave2'
+
+.. NOTE::
+ Also the include operation can be performed. A non-absolute file
+ location is relative to the server binary path, not to the control binary
+ path!
+
+ ::
+
+ $ knotc conf-set 'include' '/tmp/new_zones.conf'
+
+To unset the whole configuration or to unset the whole configuration section
+or to unset an identified section or to unset an item or to unset a specific
+item value::
+
+ $ knotc conf-unset
+ $ knotc conf-unset 'zone'
+ $ knotc conf-unset 'zone[example.com]'
+ $ knotc conf-unset 'zone[example.com].master'
+ $ knotc conf-unset 'zone[example.com].master' 'remote2' 'remote5'
+
+To get the change between the current configuration and the active transaction
+for the whole configuration or for a specific section or for a specific
+identified section or for a specific item::
+
+ $ knotc conf-diff
+ $ knotc conf-diff 'zone'
+ $ knotc conf-diff 'zone[example.com]'
+ $ knotc conf-diff 'zone[example.com].master'
+
+.. CAUTION::
+ While it is possible to change most of the configuration parameters
+ dynamically or via configuration file reload, a few of the parameters
+ in the section ``server`` require restarting the server, such that the changes
+ take effect. These parameters are:
+ :ref:`rundir<server_rundir>`,
+ :ref:`user<server_user>`,
+ :ref:`pidfile<server_pidfile>`,
+ :ref:`tcp-reuseport<server_tcp-reuseport>`,
+ :ref:`udp-workers<server_udp-workers>`,
+ :ref:`tcp-workers<server_tcp-workers>`,
+ :ref:`background-workers<server_background-workers>`, and
+ :ref:`listen<server_listen>`.
+
+An example of possible configuration initialization::
+
+ $ knotc conf-begin
+ $ knotc conf-set 'server.listen' '0.0.0.0@53' '::@53'
+ $ knotc conf-set 'remote[master_server]'
+ $ knotc conf-set 'remote[master_server].address' '192.168.1.1'
+ $ knotc conf-set 'template[default]'
+ $ knotc conf-set 'template[default].storage' '/var/lib/knot/zones/'
+ $ knotc conf-set 'template[default].master' 'master_server'
+ $ knotc conf-set 'zone[example.com]'
+ $ knotc conf-diff
+ $ knotc conf-commit
+
+.. _Secondary mode:
+
+Secondary (slave) mode
+======================
+
+Running the server as a secondary is very straightforward as the zone
+is transfered automatically from a remote server. The received zone is
+usually stored in a zone file after the :ref:`zone_zonefile-sync` period
+elapses. Zone differences are stored in the :ref:`zone journal <Journal behaviour>`.
+
+.. _Primary mode:
+
+Primary (master) mode
+=====================
+
+If you just want to check the zone files before starting, you can use::
+
+ $ knotc zone-check example.com
+
+.. _Editing zones:
+
+Reading and editing zones
+=========================
+
+Knot DNS allows you to read or change zone contents online using the server
+control interface.
+
+.. WARNING::
+ Avoid concurrent zone access when a zone event (zone file load, refresh,
+ DNSSEC signing, dynamic update) is in progress or pending. In such a case
+ zone events must be frozen before. For more information on how to freeze the
+ zone read :ref:`Editing zone file`.
+
+To get contents of all configured zones, or a specific zone contents, or zone
+records with a specific owner, or even with a specific record type::
+
+ $ knotc zone-read --
+ $ knotc zone-read example.com
+ $ knotc zone-read example.com ns1
+ $ knotc zone-read example.com ns1 NS
+
+.. NOTE::
+ If the record owner is not a fully qualified domain name, then it is
+ considered as a relative name to the zone name.
+
+To start a writing transaction on all zones or on specific zones::
+
+ $ knotc zone-begin --
+ $ knotc zone-begin example.com example.net
+
+Now you can list all nodes within the transaction using the ```zone-get```
+command, which always returns current data with all changes included. The
+command has the same syntax as ```zone-read```.
+
+Within the transaction, you can add a record to a specific zone or to all
+zones with an open transaction::
+
+ $ knotc zone-set example.com ns1 3600 A 192.168.0.1
+ $ knotc zone-set -- ns1 3600 A 192.168.0.1
+
+To remove all records with a specific owner, or a specific rrset, or
+specific record data::
+
+ $ knotc zone-unset example.com ns1
+ $ knotc zone-unset example.com ns1 A
+ $ knotc zone-unset example.com ns1 A 192.168.0.2
+
+To see the difference between the original zone and the current version::
+
+ $ knotc zone-diff example.com
+
+Finally, either commit or abort your transaction::
+
+ $ knotc zone-commit example.com
+ $ knotc zone-abort example.com
+
+A full example of setting up a completely new zone from scratch::
+
+ $ knotc conf-begin
+ $ knotc conf-set zone.domain example.com
+ $ knotc conf-commit
+ $ knotc zone-begin example.com
+ $ knotc zone-set example.com @ 7200 SOA ns hostmaster 1 86400 900 691200 3600
+ $ knotc zone-set example.com ns 3600 A 192.168.0.1
+ $ knotc zone-set example.com www 3600 A 192.168.0.100
+ $ knotc zone-commit example.com
+
+.. NOTE::
+ If quotes are necessary for record data specification, remember to escape them::
+
+ $ knotc zone-set example.com @ 3600 TXT \"v=spf1 a:mail.example.com -all\"
+
+.. _Editing zone file:
+
+Reading and editing the zone file safely
+========================================
+
+It's always possible to read and edit zone contents via zone file manipulation.
+It may lead to confusion, however, if the zone contents are continuously being
+changed by DDNS, DNSSEC signing and the like. In such a case, the safe way to
+modify the zone file is to freeze zone events first::
+
+ $ knotc -b zone-freeze example.com.
+ $ knotc -b zone-flush example.com.
+
+After calling freeze on the zone, there still may be running zone operations (e.g. signing),
+causing freeze pending. Because of this, the blocking mode is used to ensure
+the operation was finished. Then the zone can be flushed to a file.
+
+Now the zone file can be safely modified (e.g. using a text editor).
+If :ref:`zone_zonefile-load` is not set to `difference-no-serial`, it's also necessary to
+**increase SOA serial** in this step to keep consistency. Finally, we can load the
+modified zone file and if successful, thaw the zone::
+
+ $ knotc -b zone-reload example.com.
+ $ knotc zone-thaw example.com.
+
+.. _Zone loading:
+
+Zone loading
+============
+
+The process of how the server loads a zone is influenced by the configuration of the
+:ref:`zonefile-load <zone_zonefile-load>` and :ref:`journal-content <zone_journal-content>`
+parameters (also DNSSEC signing applies), the existence of a zone file and journal
+(and their relative out-of-dateness), and whether it is a cold start of the server
+or a zone reload (e.g. invoked by the :doc:`knotc<man_knotc>` interface). Please note
+that zone transfers are not taken into account here – they are planned after the zone
+is loaded (including :ref:`zone bootstrap<Zone bootstrap>`).
+
+If the zone file exists and is not excluded by the configuration, it is first loaded
+and according to its SOA serial number, relevant journal changesets are applied.
+If this is a zone reload and we have :ref:`zone_zonefile-load` set to `difference`, the difference
+between old and new contents is computed and stored in the journal like an update.
+The zone file should be either unchanged since last load or changed with incremented
+SOA serial. In the case of a decreased SOA serial, the load is interrupted with
+an error; if unchanged, it is increased by the server.
+
+If the procedure described above succeeds without errors, the resulting zone contents are (after potential DNSSEC signing)
+used as the new zone.
+
+The option :ref:`zone_journal-content` set to `all` lets the server, beside better performance, keep
+track of the zone contents also across server restarts. It makes the cold start
+effectively work like a zone reload with the old contents loaded from the journal
+(unless this is the very first start with the zone not yet saved into the journal).
+
+.. _Journal behaviour:
+
+Journal behaviour
+=================
+
+The zone journal keeps some history of changes made to the zone. It is useful for
+responding to IXFR queries. Also if :ref:`zone file flush <zone_zonefile-sync>` is disabled, the
+journal keeps the difference between the zone file and the current zone in case of server shutdown.
+The history is stored in changesets – differences of zone contents between two
+(usually subsequent) zone versions (specified by SOA serials).
+
+Journals of all zones are stored in a common LMDB database. Huge changesets are
+split into 70 KiB [#fn-hc]_ blocks to prevent fragmentation of the DB. The
+journal does each operation in one transaction to keep consistency of the DB and performance.
+
+Each zone journal has its own occupation limits :ref:`maximum usage <zone_journal-max-usage>`
+and :ref:`maximum depth <zone_journal-max-depth>`. Changesets are stored in the journal
+one by one. When hitting any of the limits, the zone is flushed into the zone file
+if there are no redundant changesets to delete, and the oldest changesets are deleted.
+In the case of the size limit, twice [#fn-hc]_ the needed amount of space is purged
+to prevent overly frequent deletes.
+
+If :ref:`zone file flush <zone_zonefile-sync>` is disabled, then instead of flushing
+the zone, the journal tries to save space by merging the changesets into a special one.
+This approach is effective if the changes rewrite each other, e.g. periodically
+changing the same zone records, re-signing the whole zone etc. Thus the difference between the zone
+file and the zone is still preserved even if the journal deletes some older changesets.
+
+If the journal is used to store both zone history and contents, a special changeset
+is present with zone contents. When the journal gets full, the changes are merged into this
+special changeset.
+
+There is also a :ref:`safety hard limit <database_journal-db-max-size>` for overall
+journal database size, but it's strongly recommended to set the per-zone limits in
+a way to prevent hitting this one. For LMDB, it's hard to recover from the
+database-full state. For wiping one zone's journal, see *knotc zone-purge +journal*
+command.
+
+.. [#fn-hc] This constant is hardcoded.
+
+.. _Handling, zone file, journal, changes, serials:
+
+Handling zone file, journal, changes, serials
+=============================================
+
+Some configuration options regarding the zone file and journal, together with operation
+procedures, might lead to unexpected results. This chapter points out
+potential interference and both recommends and warns before some combinations thereof.
+Unfortunately, there is no optimal combination of configuration options,
+every approach has some disadvantages.
+
+Example 1
+---------
+
+Keep the zone file updated::
+
+ zonefile-sync: 0
+ zonefile-load: whole
+ journal-content: changes
+
+These are default values. The user can always check the current zone
+contents in the zone file, and also modify it (recommended with server turned-off or
+taking the :ref:`safe way<Editing zone file>`). The journal serves here just as a source of
+history for secondary servers' IXFR. Some users dislike that the server overwrites their
+prettily prepared zone file.
+
+Example 2
+---------
+
+Zonefileless setup::
+
+ zonefile-sync: -1
+ zonefile-load: none
+ journal-content: all
+
+Zone contents are stored only in the journal. The zone is updated by DDNS,
+zone transfer, or via the control interface. The user might have filled the
+zone contents initially from a zone file by setting :ref:`zone_zonefile-load` to
+`whole` temporarily.
+It's also a good setup for secondary servers. Anyway, it's recommended to carefully tune
+the journal-size-related options to avoid surprises like the journal getting full.
+
+Example 3
+---------
+
+Input-only zone file::
+
+ zonefile-sync: -1
+ zonefile-load: difference
+ journal-content: changes
+
+The user can make changes to the zone by editing the zone file, and his pretty zone file
+is never overwritten or filled with DNSSEC-related autogenerated records – they are
+only stored in the journal.
+
+The zone file's SOA serial must be properly set to a number which is higher than the
+current SOA serial in the zone (not in the zone file) if manually updated!
+
+Example 4
+---------
+
+Auto-increment SOA serial::
+
+ zonefile-sync: -1
+ zonefile-load: difference-no-serial
+ journal-content: all
+
+This is similar to the previous setup, but the SOA serial is handled by the server
+automatically. So the user no longer needs to care about it in the zone file.
+
+However, this requires setting :ref:`zone_journal-content` to `all` so that
+the information about the last real SOA serial is preserved in case of server re-start.
+
+.. _Zone bootstrap:
+
+Zone bootstrapping on secondary
+===============================
+
+When zone refresh from the primary fails, the ``retry`` value from SOA is used
+as the interval between refresh attempts. In a case that SOA isn't known to the
+secondary (either because the zone hasn't been retrieved from the primary yet,
+or the zone has expired), a backoff is used for repeated retry attempts.
+
+With every retry, the delay rises as a quadratic polynomial (5 * n^2, where n
+represents the sequence number of the retry attempt) up to two hours, each time
+with a random delay of 0 to 30 seconds added to spread the load on the primary.
+In each attempt, the retry interval is subject to :ref:`zone_retry-min-interval`
+and :ref:`zone_retry-max-interval`.
+
+Until the refresh has been successfully completed, the backoff is restarted from
+the beginning by every ```zone-refresh``` or ```zone-retransfer``` of the zone
+triggered manually via :doc:`knotc<man_knotc>`, by ```zone-purge``` or
+```zone-restore``` of the zone's timers, or by a restart of :doc:`knotd<man_knotd>`.
+
+.. _Zone expiration:
+
+Zone expiration
+===============
+
+On a primary, zone normally never expires. On a secondary, zone expiration results
+in removal of the current zone contents and a trigger of immediate zone refresh.
+The zone file and zone's journal are kept, but not used for answering requests
+until the refresh is successfully completed.
+
+The zone expire timer is set according to the zone's SOA expire field. In addition
+to it, Knot DNS also supports EDNS EXPIRE extension of the expire timer in both
+primary and secondary roles as described in :rfc:`7314`.
+
+When Knot DNS is configured as a secondary, EDNS EXPIRE option present in a SOA,
+IXFR, or AFXR response from the primary is processed and used to update the zone
+timer when necessary. This functionality (together with requests of any other EDNS
+options) for a specified primary may be disabled using the :ref:`remote_no-edns`
+configuration parameter.
+
+If it's necessary, any zone may be expired manually using the ``zone-purge``
+command of the :doc:`knotc<man_knotc>` utility. Manual expiration is applicable
+to any zone, including a catalog zone or a zone on a primary. Beware, a manually
+expired zone on a primary or a manually expired catalog zone becomes valid again
+after a server configuration is reloaded or the :doc:`knotd<man_knotd>` process
+is restarted, provided that the zone data hasn't been removed.
+
+.. _DNSSEC Key states:
+
+DNSSEC key states
+=================
+
+During its lifetime, a DNSSEC key finds itself in different states. Most of the time it
+is used for signing the zone and published in the zone. In order to exchange
+the key, one type of a key rollover is necessary, and during this rollover,
+the key goes through various states with respect to the rollover type and also the
+state of the other key being rolled-over.
+
+First, let's list the states of the key being rolled-in.
+
+Standard states:
+
+- ``active`` — The key is used for signing.
+- ``published`` — The key is published in the zone, but not used for signing. If the key is
+ a KSK or CSK, it is used for signing the DNSKEY RRSet.
+- ``ready`` (only for KSK) — The key is published in the zone and used for signing. The
+ old key is still active, since we are waiting for the DS records in the parent zone to be
+ updated (i.e. "KSK submission").
+
+Special states for algorithm rollover:
+
+- ``pre-active`` — The key is not yet published in the zone, but it's used for signing the zone.
+- ``published`` — The key is published in the zone, and it's still used for signing since the
+ pre-active state.
+
+Second, we list the states of the key being rolled-out.
+
+Standard states:
+
+- ``retire-active`` — The key is still used for signing, and is published in the zone, waiting for
+ the updated DS records in parent zone to be acked by resolvers (KSK case) or synchronizing
+ with KSK during algorithm rollover (ZSK case).
+- ``retired`` — The key is no longer used for signing. If ZSK, the key is still published in the zone.
+- ``removed`` — The key is not used in any way (in most cases such keys are deleted immediately).
+
+Special states for algorithm rollover:
+
+- ``post-active`` — The key is no longer published in the zone, but still used for signing.
+
+Special states for :rfc:`5011` trust anchor roll-over
+
+- ``revoke`` (only for KSK) — The key is published and used for signing, and the Revoked flag is set.
+
+.. NOTE::
+ Trust anchor roll-over is not implemented with automatic key management.
+
+ The ``revoke`` state can only be established using :doc:`keymgr<man_keymgr>` when using
+ :ref:`dnssec-manual-key-management`.
+
+The states listed above are relevant for :doc:`keymgr<man_keymgr>` operations like generating
+a key, setting its timers and listing KASP database.
+
+Note that the key "states" displayed in the server log lines while zone signing
+are not according to those listed above, but just a hint as to what the key is currently used for
+(e.g. "public, active" = key is published in the zone and used for signing).
+
+.. _DNSSEC Key rollovers:
+
+DNSSEC key rollovers
+====================
+
+This section describes the process of DNSSEC key rollover and its implementation
+in Knot DNS, and how the operator might watch and check that it's working correctly.
+The prerequisite is automatic zone signing with enabled
+:ref:`automatic key management<dnssec-automatic-ksk-management>`.
+
+The KSK and ZSK rollovers are triggered by the respective zone key getting old according
+to the settings (see :ref:`KSK<policy_ksk-lifetime>` and :ref:`ZSK<policy_zsk-lifetime>` lifetimes).
+
+The algorithm rollover starts when the policy :ref:`algorithm<policy_algorithm>`
+field is updated to a different value.
+
+The signing scheme rollover happens when the policy :ref:`signing scheme<policy_single-type-signing>`
+field is changed.
+
+It's also possible to change the algorithm and signing scheme in one rollover.
+
+The operator may check the next rollover phase time by watching the next zone signing time,
+either in the log or via ``knotc zone-status``. There is no special log for finishing a rollover.
+
+.. NOTE::
+ There are never two key rollovers running in parallel for one zone. If
+ a rollover is triggered while another is in progress, it waits until the
+ first one is finished.
+
+The ZSK rollover is performed with Pre-publish method, KSK rollover uses Double-Signature
+scheme, as described in :rfc:`6781`.
+
+.. _Automatic KSK and ZSK rollovers example:
+
+Automatic KSK and ZSK rollovers example
+---------------------------------------
+
+Let's start with the following set of keys::
+
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, key, tag 50613, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+
+The last fields hint the key state: ``public`` denotes a key that will be presented
+as the DNSKEY record, ``ready`` means that CDS/CDNSKEY records were created,
+``active`` tells us that the key is used for signing, while ``active+`` is an
+active key undergoing a roll-over or roll-in.
+
+For demonstration purposes, the following configuration is used::
+
+ submission:
+ - id: test_submission
+ check-interval: 2s
+ parent: dnssec_validating_resolver
+
+ policy:
+ - id: test_policy
+ ksk-lifetime: 5m
+ zsk-lifetime: 2m
+ propagation-delay: 2s
+ dnskey-ttl: 10s
+ zone-max-ttl: 15s
+ ksk-submission: test_submission
+
+Upon the zone's KSK lifetime expiration, a new KSK is generated and the rollover
+continues along the lines of :rfc:`6781#section-4.1.2`::
+
+ # KSK Rollover (50613 -> 9081)
+
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, KSK rollover started
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, key, tag 50613, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active+
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:50:00+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:50:12+0200
+
+ ... (propagation-delay + dnskey-ttl) ...
+
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:50:12+0200 notice: [example.com.] DNSSEC, KSK submission, waiting for confirmation
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, key, tag 50613, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, ready, active+
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:50:12+0200 info: [example.com.] DNSSEC, next signing at 2021-05-17T20:49:56+0200
+
+At this point the new KSK has to be submitted to the parent zone. Knot detects the updated parent's DS
+record automatically (and waits for additional period of the DS's TTL before retiring the old key)
+if :ref:`parent DS check<Submission section>` is configured, otherwise the
+operator must confirm it manually (using ``knotc zone-ksk-submitted``)::
+
+ 2021-05-10T20:50:12+0200 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300, KSK submission check: negative
+ 2021-05-10T20:50:14+0200 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300, KSK submission check: negative
+ 2021-05-10T20:50:16+0200 info: [example.com.] DS check, outgoing, remote 127.0.0.1@5300, KSK submission check: positive
+ 2021-05-10T20:50:16+0200 notice: [example.com.] DNSSEC, KSK submission, confirmed
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, key, tag 50613, algorithm ECDSAP256SHA256, KSK, public, active+
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:50:16+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:50:23+0200
+
+ ... (parent's DS TTL is 7 seconds) ...
+
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:50:23+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:51:56+0200
+
+Upon the zone's ZSK lifetime expiration, a new ZSK is generated and the rollover
+continues along the lines of :rfc:`6781#section-4.1.1`::
+
+ # ZSK Rollover (62932 -> 33255)
+
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, ZSK rollover started
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, key, tag 33255, algorithm ECDSAP256SHA256, public
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:51:56+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:52:08+0200
+
+ ... (propagation-delay + dnskey-ttl) ...
+
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, key, tag 62932, algorithm ECDSAP256SHA256, public
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, key, tag 33255, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:52:08+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:52:25+0200
+
+ ... (propagation-delay + zone-max-ttl) ...
+
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, key, tag 33255, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:52:25+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:54:08+0200
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, signing zone
+
+Further rollovers::
+
+ ... (zsk-lifetime - propagation-delay - zone-max-ttl) ...
+
+ # Another ZSK Rollover (33255 -> 49526)
+
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, ZSK rollover started
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, key, tag 33255, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, key, tag 49526, algorithm ECDSAP256SHA256, public
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:54:08+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:54:20+0200
+
+ ...
+
+ # Another KSK Rollover (9081 -> 9179)
+
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, signing zone
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, KSK rollover started
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, key, tag 9081, algorithm ECDSAP256SHA256, KSK, public, active
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, key, tag 49526, algorithm ECDSAP256SHA256, public, active
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, key, tag 9179, algorithm ECDSAP256SHA256, KSK, public, active+
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, signing started
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, successfully signed
+ 2021-05-10T20:55:00+0200 info: [example.com.] DNSSEC, next signing at 2021-05-10T20:55:12+0200
+
+ ...
+
+.. TIP::
+ If systemd is available, the KSK submission event is logged into journald
+ in a structured way. The intended use case is to trigger a user-created script.
+ Example::
+
+ journalctl -f -t knotd -o json | python3 -c '
+ import json, sys
+ for line in sys.stdin:
+ k = json.loads(line);
+ if "KEY_SUBMISSION" in k:
+ print("%s, zone=%s, keytag=%s" % (k["__REALTIME_TIMESTAMP"], k["ZONE"], k["KEY_SUBMISSION"]))
+ '
+
+.. _DNSSEC Shared KSK:
+
+DNSSEC shared KSK
+=================
+
+Knot DNS allows, with automatic DNSSEC key management, to configure a shared KSK for multiple zones.
+By enabling :ref:`policy_ksk-shared`, we tell Knot to share all newly-created KSKs
+among all the zones with the same :ref:`DNSSEC signing policy<Policy section>` assigned.
+
+The feature works as follows. Each zone still manages its keys separately. If a new KSK shall be
+generated for the zone, it first checks if it can grab another zone's shared KSK instead -
+that is the last generated KSK in any of the zones with the same policy assigned.
+Anyway, only the cryptographic material is shared, the key may have different timers
+in each zone.
+
+.. rubric:: Consequences:
+
+If we have an initial setting with brand new zones without any DNSSEC keys,
+the initial keys for all zones are generated. With shared KSK, they will all have the same KSK,
+but different ZSKs. The KSK rollovers may take place at slightly different times for each of the zones,
+but the resulting new KSK will be shared again among all of them.
+
+If we have zones which already have their keys, turning on the shared KSK feature triggers no action.
+But when a KSK rollover takes place, they will use the same new key afterwards.
+
+.. WARNING::
+ Changing the policy :ref:`id<policy_id>` must be done carefully if shared
+ KSK is in use.
+
+.. _DNSSEC Delete algorithm:
+
+DNSSEC delete algorithm
+=======================
+
+This is how to "disconnect" a signed zone from a DNSSEC-aware parent zone.
+More precisely, we tell the parent zone to remove our zone's DS record by
+publishing a special formatted CDNSKEY and CDS record. This is mostly useful
+if we want to turn off DNSSEC on our zone so it becomes insecure, but not bogus.
+
+With automatic DNSSEC signing and key management by Knot, this is as easy as
+configuring :ref:`policy_cds-cdnskey-publish` option and reloading the configuration.
+We check if the special CDNSKEY and CDS records with the rdata "0 3 0 AA==" and "0 0 0 00",
+respectively, appeared in the zone.
+
+After the parent zone notices and reflects the change, we wait for TTL expire
+(so all resolvers' caches get updated), and finally we may do anything with the
+zone, e.g. turning off DNSSEC, removing all the keys and signatures as desired.
+
+.. _DNSSEC Offline KSK:
+
+DNSSEC Offline KSK
+==================
+
+Knot DNS allows a special mode of operation where the private part of the Key Signing Key is
+not available to the daemon, but it is rather stored securely in an offline storage. This requires
+that the KSK/ZSK signing scheme is used (i.e. :ref:`policy_single-type-signing` is off).
+The Zone Signing Key is always fully available to the daemon in order to sign common changes to the zone contents.
+
+The server (or the "ZSK side") only uses ZSK to sign zone contents and its changes. Before
+performing a ZSK rollover, the DNSKEY records will be pre-generated and signed by the
+signer (the "KSK side"). Both sides exchange keys in the form of human-readable messages with the help
+of the :doc:`keymgr<man_keymgr>` utility.
+
+Prerequisites
+-------------
+
+For the ZSK side (i.e. the operator of the DNS server), the zone has to be configured with:
+
+- Enabled :ref:`DNSSEC signing <zone_dnssec-signing>`
+- Properly configured and assigned :ref:`DNSSEC policy <Policy section>`:
+
+ - Enabled :ref:`policy_manual`
+ - Enabled :ref:`policy_offline-ksk`
+ - Explicit :ref:`policy_dnskey-ttl`
+ - Explicit :ref:`policy_zone-max-ttl`
+ - Other options are optional
+- KASP DB may contain a ZSK (the present or some previous one(s))
+
+For the KSK side (i.e. the operator of the KSK signer), the zone has to be configured with:
+
+- Properly configured and assigned :ref:`DNSSEC policy <Policy section>`:
+
+ - Enabled :ref:`policy_manual`
+ - Enabled :ref:`policy_offline-ksk`
+ - Optional :ref:`policy_rrsig-lifetime`, :ref:`policy_rrsig-refresh`,
+ :ref:`policy_algorithm`, :ref:`policy_reproducible-signing`,
+ and :ref:`policy_cds-cdnskey-publish`
+ - Other options are ignored
+- KASP DB contains a KSK (the present or a newly generated one)
+
+Generating and signing future ZSKs
+----------------------------------
+
+1. Use the ``keymgr pregenerate`` command on the ZSK side to prepare the ZSKs for a specified period of time in the future. The following example
+ generates ZSKs for the *example.com* zone for 6 months ahead starting from now::
+
+ $ keymgr -c /path/to/ZSK/side.conf example.com. pregenerate +6mo
+
+ If the time period is selected as e.g. *2 x* :ref:`policy_zsk-lifetime` *+ 4 x* :ref:`policy_propagation-delay`, it will
+ prepare roughly two complete future key rollovers. The newly-generated
+ ZSKs remain in non-published state until their rollover starts, i.e. the time
+ they would be generated in case of automatic key management.
+
+2. Use the ``keymgr generate-ksr`` command on the ZSK side to export the public parts of the future ZSKs in a form
+ similar to DNSKEY records. You might use the same time period as in the first step::
+
+ $ keymgr -c /path/to/ZSK/side.conf example.com. generate-ksr +0 +6mo > /path/to/ksr/file
+
+ Save the output of the command (called the Key Signing Request or KSR) to a file and transfer it to the KSK side e.g. via e-mail.
+
+3. Use the ``keymgr sign-ksr`` command on the KSK side with the KSR file from the previous step as a parameter::
+
+ $ keymgr -c /path/to/KSK/side.conf example.com. sign-ksr /path/to/ksr/file > /path/to/skr/file
+
+ This creates all the future forms of the DNSKEY, CDNSKEY and CSK records and all the respective RRSIGs and prints them on output. Save
+ the output of the command (called the Signed Key Response or SKR) to a file and transfer it back to the ZSK side.
+
+4. Use the ``keymgr import-skr`` command to import the records and signatures from the SKR file generated in the last step
+ into the KASP DB on the ZSK side::
+
+ $ keymgr -c /path/to/ZSK/side.conf example.com. import-skr /path/to/skr/file
+
+5. Use the ``knotc zone-keys-load`` command to trigger a zone re-sign on the ZSK-side and set up the future re-signing events correctly.::
+
+ $ knotc -c /path/to/ZSK/side.conf zone-keys-load example.com.
+
+6. Now the future ZSKs and DNSKEY records with signatures are ready in KASP DB for later usage.
+ Knot automatically uses them at the correct time intervals.
+ The entire procedure must be repeated before the time period selected at the beginning passes,
+ or whenever a configuration is changed significantly. Importing new SKR over some previously-imported
+ one leads to deleting the old offline records.
+
+Offline KSK and manual ZSK management
+-------------------------------------
+
+If the automatically preplanned ZSK roll-overs (first step) are not desired, just set the :ref:`policy_zsk-lifetime`
+to zero, and manually pregenerate ZSK keys and set their timers. Then follow the steps
+``generate-ksr — sign-ksr — import-skr — zone-keys-load`` and repeat the ceremony when necessary.
+
+Offline KSK roll-over
+---------------------
+
+The KSKs (on the KSK side) must be managed manually, but manual KSK roll-over is possible. Just plan the steps
+of the KSK roll-over in advance, and whenever the KSK set or timers are changed, re-perform the relevant rest of the ceremony
+``sign-ksr — import-skr — zone-keys-load``.
+
+Emergency SKR
+-------------
+
+A general recommendation for large deployments is to have some backup pre-published keys, so that if the current ones are
+compromised, they can be rolled-over to the backup ones without any delay. But in the case of Offline KSK, according to
+the procedures above, both ZSK and KSK immediate rollovers require the KSR-SKR ceremony.
+
+However, a trick can be done to achieve really immediate key substitution. This is no longer about Knot DNS functionality,
+just a hint for the operator.
+
+The idea is to perform every KSR-SKR ceremony twice: once with normal state of the keys (the backup key is only published),
+and once with the keys already exchanged (the backup key is temporarily marked as active and the standard key temporarily
+as public only). The second (backup) SKR should be saved for emergency key replacement.
+
+Summary of the steps:
+
+* Prepare KSK and ZSK side as usual, including public-only emergency key
+* Perform normal Offline KSK ceremony:
+
+ * Pre-generate ZSKs (only in the case of automatic ZSK management)
+ * Generate KSR
+ * Sign KSR on the KSK side
+ * Import SKR
+ * Re-sign the zone
+
+* Freeze the zone on the ZSK side
+* Temporarily set the backup key as active and the normal key as publish-only
+* Perform backup Offline KSK ceremony:
+
+ * Generate KSR (only if the backup key is a replacement for ZSK)
+ * Sign the KSR on the KSK side
+ * Save the SKR to a backup storage, don't import it yet
+
+* Return the keys to the previous state
+* Thaw the zone on the ZSK side
+
+Emergency key replacement:
+
+* Import the backup SKR
+* Align the keys with the new states (backup key as active, compromised key as public)
+* Re-sign the zone
+
+.. _DNSSEC Import of keys to HSM:
+
+Import of keys to HSM
+=====================
+
+Knot DNS stores DNSSEC keys in textual PEM format (:rfc:`7468`),
+while many HSM management software require the keys for import to be in binary
+DER format (`Rec. ITU-T X.690 <https://www.itu.int/ITU-T/recommendations/rec.aspx?rec=x.690>`_).
+Keys can be converted from one format to another by software tools such as
+``certtool`` from `GnuTLS <https://www.gnutls.org/>`_ suite or
+``openssl`` from `OpenSSL <https://www.openssl.org/>`_ suite.
+
+In the examples below, ``c4eae5dea3ee8c15395680085c515f2ad41941b6`` is used as the key ID,
+``c4eae5dea3ee8c15395680085c515f2ad41941b6.pem`` represents the filename of the key in PEM format
+as copied from the Knot DNS zone's :ref:`KASP database directory <database_kasp-db>`,
+``c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der`` represents the file containing the private
+key in DER format as generated by the conversion tool, and
+``c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der`` represents the file containing the public
+key in DER format as generated by the conversion tool.
+
+.. code-block:: console
+
+ $ certtool -V -k --outder --infile c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
+ --outfile c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der
+
+ $ certtool -V --pubkey-info --outder --load-privkey c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
+ --outfile c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der
+
+As an alternative, ``openssl`` can be used instead. It is necessary to specify either ``rsa`` or ``ec``
+command according to the algorithm used by the key.
+
+.. code-block:: console
+
+ $ openssl rsa -outform DER -in c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
+ -out c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der
+
+ $ openssl rsa -outform DER -in c4eae5dea3ee8c15395680085c515f2ad41941b6.pem \
+ -out c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der -pubout
+
+Actual import of keys (both public and private keys from the same key pair) to an HSM can be done
+via PKCS #11 interface, by ``pkcs11-tool`` from `OpenSC <https://github.com/OpenSC/OpenSC/wiki>`_ toolkit
+for example. In the example below, ``/usr/local/lib/pkcs11.so`` is used as a name of the PKCS #11 library
+or module used for communication with the HSM.
+
+.. code-block:: console
+
+ $ pkcs11-tool --module /usr/local/lib/pkcs11.so --login \
+ --write-object c4eae5dea3ee8c15395680085c515f2ad41941b6.priv.der --type privkey \
+ --usage-sign --id c4eae5dea3ee8c15395680085c515f2ad41941b6
+
+ $ pkcs11-tool --module /usr/local/lib/pkcs11.so -login \
+ --write-object c4eae5dea3ee8c15395680085c515f2ad41941b6.pub.der --type pubkey \
+ --usage-sign --id c4eae5dea3ee8c15395680085c515f2ad41941b6
+
+.. _Controlling a running daemon:
+
+Daemon controls
+===============
+
+Knot DNS was designed to allow server reconfiguration on-the-fly
+without interrupting its operation. Thus it is possible to change
+both configuration and zone files and also add or remove zones without
+restarting the server. This can be done with::
+
+ $ knotc reload
+
+If you want to refresh the secondary zones, you can do this with::
+
+ $ knotc zone-refresh
+
+.. _Data and metadata backup:
+
+Data and metadata backup
+========================
+
+Some of the zone-related data, such as zone contents or DNSSEC signing keys,
+and metadata, like zone timers, might be worth backing up. For the sake of
+consistency, it's usually necessary to shut down the server, or at least freeze all
+the zones, before copying the data like zone files, KASP database, etc, to
+a backup location. To avoid this necessity, Knot DNS provides a feature to
+back up some or all of the zones seamlessly.
+
+Online backup
+-------------
+
+While the server is running and the zones normally loaded (even when they are
+constantly/frequently being updated), the user can manually trigger the
+backup by calling::
+
+ $ knotc zone-backup +backupdir /path/of/backup
+
+To back up just some of the zones (instead of all), the user might provide
+their list::
+
+ $ knotc zone-backup +backupdir /path/to/backup zone1.com. zone2.com. ...
+
+The backup directory should be empty or non-existing and it must be accessible
+and writable for the :ref:`server_user` account under which knotd is running.
+The backup procedure will begin soon and will happen zone-by-zone
+(partially in parallel if more :ref:`server_background-workers` are configured).
+**The user shall check the logs for the outcome of each zone's backup attempt.**
+The knotc's ``-b`` parameter might be used if the user desires to wait until
+the backup work is done and a simple result status is printed out.
+
+.. TIP::
+ There is a plain ASCII text file in the backup directory,
+ ``knot_backup.label``, that contains some useful information about the
+ backup, such as the backup creation date & time, the server identity, etc.
+ Care must always be taken **not to remove this file** from the backup nor to
+ damage it.
+
+Offline restore
+---------------
+
+If the Online backup was performed for all zones, it's possible to
+restore the backed up data by simply copying them to their normal locations,
+since they're simply copies. For example, the user can copy (overwrite)
+the backed up KASP database files to their configured location.
+
+This restore of course must be done when the server is stopped. After starting up
+the server, it should run in the same state as at the time of backup.
+
+This method is recommended in the case of complete data loss, for example
+physical server failure.
+
+.. NOTE::
+ The online backup procedure stores all zone files in a single directory
+ using their default file names. If the original directory layout was
+ different, then the required directory structure must be created manually
+ for offline restore and zone files must be placed individually to their
+ respective directories. If the zone file names don't follow the default
+ pattern, they must be renamed manually to match the configuration. These
+ limitations don't apply to the online restore procedure.
+
+Online restore
+--------------
+
+This procedure is symmetrical to Online backup. By calling::
+
+ $ knotc zone-restore +backupdir /path/of/backup
+
+the user triggers a one-by-one zone restore from the backup on a running
+server. Again, a subset of zones might be specified. It must be specified
+if the backup was created for only a subset of zones.
+
+.. NOTE::
+ For restore of backups that have been created by Knot DNS releases prior
+ to 3.1, it's necessary to use the ``-f`` option. Since this option also
+ turns off some verification checks, it shouldn't be used in other cases.
+
+Limitations
+-----------
+
+Neither configuration file nor :ref:`Configuration database` is backed up
+by zone backup. The configuration has to be synchronized before zone restore
+is performed!
+
+If the private keys are stored in a HSM (anything using a PKCS#11 interface),
+they are not backed up. This includes internal metadata of the PKCS#11 provider
+software, such as key mappings, authentication information, and the configuration
+of the provider. Details are vendor-specific.
+
+The restore procedure does not care for keys deleted after taking the snapshot.
+Thus, after restore, there might remain some redundant ``.pem`` files
+of obsolete signing keys.
+
+.. TIP::
+ In order to seamlessly deploy a restored backup of KASP DB with respect to
+ a possibly ongoing DNSSEC key rollover, it's recommended to set
+ :ref:`propagation-delay <policy_propagation-delay>` to the sum of:
+
+ - The maximum delay between beginning of the zone signing and publishing
+ re-signed zone on all public secondary servers.
+ - How long it takes for the backup server to start up with the restored data.
+ - The period between taking backup snapshots of the live environment.
+
+.. _Statistics:
+
+Statistics
+==========
+
+The server provides some general statistics and optional query module statistics
+(see :ref:`mod-stats<mod-stats>`).
+
+Server statistics or global module statistics can be shown by::
+
+ $ knotc stats
+ $ knotc stats server # Show all server counters
+ $ knotc stats mod-stats # Show all mod-stats counters
+ $ knotc stats server.zone-count # Show specific server counter
+
+Per zone statistics can be shown by::
+
+ $ knotc zone-stats example.com mod-stats
+
+To show all supported counters even with 0 value, use the force option.
+
+A simple periodic statistic dump to a YAML file can also be enabled. See
+:ref:`stats section` for the configuration details.
+
+As the statistics data can be accessed over the server control socket,
+it is possible to create an arbitrary script (Python is supported at the moment)
+which could, for example, publish the data in JSON format via HTTP(S)
+or upload the data to a more efficient time series database. Take a look into
+the python folder of the project for these scripts.
+
+.. _Mode XDP:
+
+Mode XDP
+========
+
+Thanks to recent Linux kernel capabilities, namely eXpress Data Path and AF_XDP
+address family, Knot DNS offers a high-performance DNS over UDP packet processing
+mode. The basic idea is to filter DNS messages close to the network device and
+effectively forward them to the nameserver without touching the network stack
+of the operating system. Other messages (including DNS over TCP) are processed
+as usual.
+
+If :ref:`xdp_listen` is configured, the server creates
+additional XDP workers, listening on specified interface(s) and port(s) for DNS
+over UDP queries. Each XDP worker handles one RX and TX network queue pair.
+
+.. _Mode XDP_pre-requisites:
+
+Pre-requisites
+--------------
+
+* Linux kernel 4.18+ (5.x+ is recommended for optimal performance) compiled with
+ the `CONFIG_XDP_SOCKETS=y` option. The XDP mode isn't supported in other operating systems.
+* A multiqueue network card, which offers enough Combined RX/TX channels, with
+ native XDP support is highly recommended. Successfully tested cards:
+
+ * Intel series 700 (driver `i40e`), maximum number of channels per interface is 64.
+ * Intel series 500 (driver `ixgbe`), maximum number of channels per interface is 64.
+ The number of CPUs available has to be at most 64!
+
+* If the `knotd` service is not directly executed in the privileged mode, some
+ additional Linux capabilities have to be set:
+
+ Execute command::
+
+ systemctl edit knot
+
+ And insert these lines::
+
+ [Service]
+ CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_IPC_LOCK CAP_SYS_RESOURCE
+ AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_ADMIN CAP_IPC_LOCK CAP_SYS_RESOURCE
+
+ The `CAP_SYS_RESOURCE` is needed on Linux < 5.11.
+
+ All the capabilities are dropped upon the service is started.
+
+* For proper processing of VLAN traffic, VLAN offloading should be disabled. E.g.::
+
+ ethtool -K <interface> tx-vlan-offload off rx-vlan-offload off
+
+
+Optimizations
+-------------
+
+Some helpful commands::
+
+ ethtool -N <interface> rx-flow-hash udp4 sdfn
+ ethtool -N <interface> rx-flow-hash udp6 sdfn
+ ethtool -L <interface> combined <?>
+ ethtool -G <interface> rx <?> tx <?>
+ renice -n 19 -p $(pgrep '^ksoftirqd/[0-9]*$')
+
+Limitations
+-----------
+
+* Request and its response must go through the same physical network device.
+* Dynamic DNS over XDP is not supported.
+* MTU higher than 1790 bytes is not supported.
+* Multiple BPF filters per one network device are not supported.
+* Systems with big-endian byte ordering require special recompilation of the nameserver.
+* IPv4 header and UDP checksums are not verified on received DNS messages.
+* DNS over XDP traffic is not visible to common system tools (e.g. firewall, tcpdump etc.).
+* BPF filter is not automatically unloaded from the network device. Manual filter unload::
+
+ ip link set dev <interface> xdp off
diff --git a/doc/reference.rst b/doc/reference.rst
new file mode 100644
index 0000000..737c4e5
--- /dev/null
+++ b/doc/reference.rst
@@ -0,0 +1,2726 @@
+.. highlight:: none
+.. _Configuration Reference:
+
+***********************
+Configuration Reference
+***********************
+
+.. _Description:
+
+Description
+===========
+
+Configuration files for Knot DNS use simplified YAML format. Simplified means
+that not all of the features are supported.
+
+For the description of configuration items, we have to declare a meaning of
+the following symbols:
+
+- ``INT`` – Integer
+- ``STR`` – Textual string
+- ``HEXSTR`` – Hexadecimal string (with ``0x`` prefix)
+- ``BOOL`` – Boolean value (``on``/``off`` or ``true``/``false``)
+- ``TIME`` – Number of seconds, an integer with possible time multiplier suffix
+ (``s`` ~ 1, ``m`` ~ 60, ``h`` ~ 3600 or ``d`` ~ 24 * 3600)
+- ``SIZE`` – Number of bytes, an integer with possible size multiplier suffix
+ (``B`` ~ 1, ``K`` ~ 1024, ``M`` ~ 1024^2 or ``G`` ~ 1024^3)
+- ``BASE64`` – Base64 encoded string
+- ``ADDR`` – IPv4 or IPv6 address
+- ``DNAME`` – Domain name
+- ``...`` – Multi-valued item, order of the values is preserved
+- ``[`` ``]`` – Optional value
+- ``|`` – Choice
+
+The configuration consists of several fixed sections and optional module
+sections. There are 16 fixed sections (``module``, ``server``, ``xdp``, ``control``,
+``log``, ``statistics``, ``database``, ``keystore``, ``key``, ``remote``,
+``remotes``, ``acl``, ``submission``, ``policy``, ``template``, ``zone``).
+Module sections are prefixed with the ``mod-`` prefix (e.g. ``mod-stats``).
+
+Most of the sections (e.g. ``zone``) are sequences of settings blocks. Each
+settings block begins with a unique identifier, which can be used as a reference
+from other sections (such an identifier must be defined in advance).
+
+A multi-valued item can be specified either as a YAML sequence::
+
+ address: [10.0.0.1, 10.0.0.2]
+
+or as more single-valued items each on an extra line::
+
+ address: 10.0.0.1
+ address: 10.0.0.2
+
+If an item value contains spaces or other special characters, it is necessary
+to enclose such a value within double quotes ``"`` ``"``.
+
+.. _Comments:
+
+Comments
+========
+
+A comment begins with a ``#`` character and is ignored during processing.
+Also each configuration section or sequence block allows a permanent
+comment using the ``comment`` item which is stored in the server beside the
+configuration.
+
+.. _including configuration:
+
+Including configuration
+=======================
+
+Another configuration file or files, matching a pattern, can be included at
+the top level in the current file.
+
+::
+
+ include: STR
+
+.. _include:
+
+include
+-------
+
+A path or a matching pattern specifying one or more files that are included
+at the place of the include option position in the configuration.
+If the path is not absolute, then it is considered to be relative to the
+current file. The pattern can be an arbitrary string meeting POSIX *glob*
+requirements, e.g. dir/\*.conf. Matching files are processed in sorted order.
+
+*Default:* not set
+
+.. _module section:
+
+``module`` section
+==================
+
+Dynamic modules loading configuration.
+
+.. NOTE::
+ If configured with non-empty ```--with-moduledir=path``` parameter, all
+ shared modules in this directory will be automatically loaded.
+
+::
+
+ module:
+ - id: STR
+ file: STR
+
+.. _module_id:
+
+id
+--
+
+A module identifier in the form of the ``mod-`` prefix and module name suffix.
+
+.. _module_file:
+
+file
+----
+
+A path to a shared library file with the module implementation.
+
+.. WARNING::
+ If the path is not absolute, the library is searched in the set of
+ system directories. See ``man dlopen`` for more details.
+
+*Default:* ``${libdir}/knot/modules-${version}``/module_name.so
+(or ``${path}``/module_name.so if configured with ``--with-moduledir=path``)
+
+.. _server section:
+
+``server`` section
+==================
+
+General options related to the server.
+
+::
+
+ server:
+ identity: [STR]
+ version: [STR]
+ nsid: [STR|HEXSTR]
+ rundir: STR
+ user: STR[:STR]
+ pidfile: STR
+ udp-workers: INT
+ tcp-workers: INT
+ background-workers: INT
+ async-start: BOOL
+ tcp-idle-timeout: TIME
+ tcp-io-timeout: INT
+ tcp-remote-io-timeout: INT
+ tcp-max-clients: INT
+ tcp-reuseport: BOOL
+ tcp-fastopen: BOOL
+ quic-max-clients: INT
+ quic-outbuf-max-size: SIZE
+ quic-idle-close-timeout: TIME
+ remote-pool-limit: INT
+ remote-pool-timeout: TIME
+ remote-retry-delay: TIME
+ socket-affinity: BOOL
+ udp-max-payload: SIZE
+ udp-max-payload-ipv4: SIZE
+ udp-max-payload-ipv6: SIZE
+ key-file: STR
+ cert-file: STR
+ edns-client-subnet: BOOL
+ answer-rotation: BOOL
+ automatic-acl: BOOL
+ proxy-allowlist: ADDR[/INT] | ADDR-ADDR ...
+ dbus-event: none | running | zone-updated | ksk-submission | dnssec-invalid ...
+ dbus-init-delay: TIME
+ listen: ADDR[@INT] ...
+
+.. CAUTION::
+ When you change configuration parameters dynamically or via configuration file
+ reload, some parameters in the Server section require restarting the Knot server
+ so that the changes take effect. See below for the details.
+
+.. _server_identity:
+
+identity
+--------
+
+An identity of the server returned in the response to the query for TXT
+record ``id.server.`` or ``hostname.bind.`` in the CHAOS class (:rfc:`4892`).
+Set to an empty value to disable.
+
+*Default:* FQDN hostname
+
+.. _server_version:
+
+version
+-------
+
+A version of the server software returned in the response to the query
+for TXT record ``version.server.`` or ``version.bind.`` in the CHAOS
+class (:rfc:`4892`). Set to an empty value to disable.
+
+*Default:* server version
+
+.. _server_nsid:
+
+nsid
+----
+
+A DNS name server identifier (:rfc:`5001`). Set to an empty value to disable.
+
+*Default:* FQDN hostname at the moment of the daemon start
+
+.. _server_rundir:
+
+rundir
+------
+
+A path for storing run-time data (PID file, unix sockets, etc.).
+
+Depending on the usage of this parameter, its change may require restart of the Knot
+server to take effect.
+
+*Default:* ``${localstatedir}/run/knot`` (configured with ``--with-rundir=path``)
+
+.. _server_user:
+
+user
+----
+
+A system user with an optional system group (``user:group``) under which the
+server is run after starting and binding to interfaces. Linux capabilities
+are employed if supported.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``root:root``
+
+.. _server_pidfile:
+
+pidfile
+-------
+
+A PID file location.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* :ref:`rundir<server_rundir>`\ ``/knot.pid``
+
+.. _server_udp-workers:
+
+udp-workers
+-----------
+
+A number of UDP workers (threads) used to process incoming queries
+over UDP.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* equal to the number of online CPUs
+
+.. _server_tcp-workers:
+
+tcp-workers
+-----------
+
+A number of TCP workers (threads) used to process incoming queries
+over TCP.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* equal to the number of online CPUs, default value is at least 10
+
+.. _server_background-workers:
+
+background-workers
+------------------
+
+A number of workers (threads) used to execute background operations (zone
+loading, zone updates, etc.).
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* equal to the number of online CPUs, default value is at most 10
+
+.. _server_async-start:
+
+async-start
+-----------
+
+If enabled, server doesn't wait for the zones to be loaded and starts
+responding immediately with SERVFAIL answers until the zone loads.
+
+*Default:* ``off``
+
+.. _server_tcp-idle-timeout:
+
+tcp-idle-timeout
+----------------
+
+Maximum idle time (in seconds) between requests on an inbound TCP connection.
+It means if there is no activity on an inbound TCP connection during this limit,
+the connection is closed by the server.
+
+*Minimum:* ``1``
+
+*Default:* ``10``
+
+.. _server_tcp-io-timeout:
+
+tcp-io-timeout
+--------------
+
+Maximum time (in milliseconds) to receive or send one DNS message over an inbound
+TCP connection. It means this limit applies to normal DNS queries and replies,
+incoming DDNS, and **outgoing zone transfers**. The timeout is measured since some
+data is already available for processing.
+Set to 0 for infinity.
+
+*Default:* ``500`` (milliseconds)
+
+.. CAUTION::
+ In order to reduce the risk of Slow Loris attacks, it's recommended setting
+ this limit as low as possible on public servers.
+
+.. _server_tcp-remote-io-timeout:
+
+tcp-remote-io-timeout
+---------------------
+
+Maximum time (in milliseconds) to receive or send one DNS message over an outbound
+TCP connection which has already been established to a configured remote server.
+It means this limit applies to incoming zone transfers, sending NOTIFY,
+DDNS forwarding, and DS check or push. This timeout includes the time needed
+for a network round-trip and for a query processing by the remote.
+Set to 0 for infinity.
+
+*Default:* ``5000`` (milliseconds)
+
+.. _server_tcp-reuseport:
+
+tcp-reuseport
+-------------
+
+If enabled, each TCP worker listens on its own socket and the OS kernel
+socket load balancing is employed using SO_REUSEPORT (or SO_REUSEPORT_LB
+on FreeBSD). Due to the lack of one shared socket, the server can offer
+higher response rate processing over TCP. However, in the case of
+time-consuming requests (e.g. zone transfers of a TLD zone), enabled reuseport
+may result in delayed or not being responded client requests. So it is
+advisable to use this option on secondary servers.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``off``
+
+.. _server_tcp-fastopen:
+
+tcp-fastopen
+------------
+
+If enabled, use TCP Fast Open for outbound TCP communication (client side):
+incoming zone transfers, sending NOTIFY, and DDNS forwarding. This mode simplifies
+TCP handshake and can result in better networking performance. TCP Fast Open
+for inbound TCP communication (server side) isn't affected by this
+configuration as it's enabled automatically if supported by OS.
+
+.. NOTE::
+ The TCP Fast Open support must also be enabled on the OS level:
+
+ * Linux/macOS: ensure kernel parameter ``net.ipv4.tcp_fastopen`` is ``2`` or
+ ``3`` for server side, and ``1`` or ``3`` for client side.
+ * FreeBSD: ensure kernel parameter ``net.inet.tcp.fastopen.server_enable``
+ is ``1`` for server side, and ``net.inet.tcp.fastopen.client_enable`` is
+ ``1`` for client side.
+
+*Default:* ``off``
+
+.. _server_quic-max-clients:
+
+quic-max-clients
+----------------
+
+A maximum number of QUIC clients connected in parallel.
+
+See also :ref:`xdp_quic`.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Minimum:* ``128``
+
+*Default:* ``10000`` (ten thousand)
+
+.. _server_quic-outbuf-max-size:
+
+quic-outbuf-max-size
+--------------------
+
+Maximum cumulative size of memory used for buffers of unACKed
+sent messages.
+
+.. NOTE::
+ Set low if little memory is available (together with :ref:`server_quic-max-clients`
+ since QUIC connections are memory-heavy). Set to high value if outgoing zone
+ transfers of big zone over QUIC are expected.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Minimum:* ``1M`` (1 MiB)
+
+*Default:* ``100M`` (100 MiB)
+
+.. _server_quic-idle-close-timeout:
+
+quic-idle-close-timeout
+-----------------------
+
+Time in seconds, after which any idle QUIC connection is gracefully closed.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Minimum:* ``1``
+
+*Default:* ``4``
+
+.. _server_remote-pool-limit:
+
+remote-pool-limit
+-----------------
+
+If nonzero, the server will keep up to this number of outgoing TCP connections
+open for later use. This is an optimization to avoid frequent opening of
+TCP connections to the same remote.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``0``
+
+.. _server_remote-pool-timeout:
+
+remote-pool-timeout
+-------------------
+
+The timeout in seconds after which the unused kept-open outgoing TCP connections
+to remote servers are closed.
+
+*Default:* ``5``
+
+.. _server_remote-retry-delay:
+
+remote-retry-delay
+------------------
+
+When a connection attempt times out to some remote address, this information will be
+kept for this specified time (in milliseconds) and other connections to the same address won't
+be attempted. This prevents repetitive waiting for timeout on an unreachable remote.
+
+*Default:* ``0``
+
+.. _server_socket-affinity:
+
+socket-affinity
+---------------
+
+If enabled and if SO_REUSEPORT is available on Linux, all configured network
+sockets are bound to UDP and TCP workers in order to increase the networking performance.
+This mode isn't recommended for setups where the number of network card queues
+is lower than the number of UDP or TCP workers.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``off``
+
+.. _server_tcp-max-clients:
+
+tcp-max-clients
+---------------
+
+A maximum number of TCP clients connected in parallel, set this below the file
+descriptor limit to avoid resource exhaustion.
+
+.. NOTE::
+ It is advisable to adjust the maximum number of open files per process in your
+ operating system configuration.
+
+*Default:* one half of the file descriptor limit for the server process
+
+.. _server_udp-max-payload:
+
+udp-max-payload
+---------------
+
+Maximum EDNS0 UDP payload size default for both IPv4 and IPv6.
+
+*Default:* ``1232``
+
+.. _server_udp-max-payload-ipv4:
+
+udp-max-payload-ipv4
+--------------------
+
+Maximum EDNS0 UDP payload size for IPv4.
+
+*Default:* ``1232``
+
+.. _server_udp-max-payload-ipv6:
+
+udp-max-payload-ipv6
+--------------------
+
+Maximum EDNS0 UDP payload size for IPv6.
+
+*Default:* ``1232``
+
+.. _server_key-file:
+
+key-file
+--------
+
+Path to a server key PEM file which is used for DNS over QUIC communication.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* one-time in-memory key
+
+.. _server_cert-file:
+
+cert-file
+---------
+
+Path to a server certificate PEM file which is used for DNS over QUIC communication.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* one-time in-memory certificate
+
+.. _server_edns-client-subnet:
+
+edns-client-subnet
+------------------
+
+Enable or disable EDNS Client Subnet support. If enabled, responses to queries
+containing the EDNS Client Subnet option
+always contain a valid EDNS Client Subnet option according to :rfc:`7871`.
+
+*Default:* ``off``
+
+.. _server_answer-rotation:
+
+answer-rotation
+---------------
+
+Enable or disable sorted-rrset rotation in the answer section of normal replies.
+The rotation shift is simply determined by a query ID.
+
+*Default:* ``off``
+
+.. _server_automatic-acl:
+
+automatic-acl
+-------------
+
+If enabled, :ref:`automatic ACL<remote_automatic-acl>` setting of
+configured remotes is considered when evaluating authorized operations.
+
+*Default:* ``off``
+
+.. _server_proxy-allowlist:
+
+proxy-allowlist
+---------------
+
+An ordered list of IP addresses, network subnets, or network ranges
+which are allowed as a source address of proxied DNS traffic over UDP.
+The supported proxy protocol is
+`haproxy PROXY v2 <https://www.haproxy.org/download/2.5/doc/proxy-protocol.txt>`_.
+
+.. NOTE::
+ TCP is not supported.
+
+*Default:* not set
+
+.. _server_dbus-event:
+
+dbus-event
+----------
+
+Specification of server or zone states which emit a D-Bus signal on the system
+bus. The bus name is ``cz.nic.knotd``, the object path is ``/cz/nic/knotd``, and
+the interface name is ``cz.nic.knotd.events``.
+
+Possible values:
+
+- ``none`` – No signal is emitted.
+- ``running`` – The signal ``started`` is emitted when the server is fully operational
+ and the signal ``stopped`` is emitted at the beginning of the server shutdown.
+- ``zone-updated`` – The signal ``zone_updated`` is emitted when a zone has been updated;
+ the signal parameters are `zone name` and `zone SOA serial`.
+- ``ksk-submission`` – The signal ``zone_ksk_submission`` is emitted if there is
+ a ready KSK present when the zone is signed; the signal parameters are
+ `zone name`, `KSK keytag`, and `KSK KASP id`.
+- ``dnssec-invalid`` – The signal ``zone_dnssec_invalid`` is emitted when DNSSEC
+ validation fails; the signal parameter is `zone name`.
+
+.. NOTE::
+ This function requires systemd version at least 221.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``none``
+
+.. _server_dbus-init-delay:
+
+dbus-init-delay
+---------------
+
+Time in seconds which the server waits upon D-Bus initialization to ensure
+the D-Bus client is ready to receive signals.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Minimum:* ``0``
+
+*Default:* ``1``
+
+.. _server_listen:
+
+listen
+------
+
+One or more IP addresses where the server listens for incoming queries.
+Optional port specification (default is 53) can be appended to each address
+using ``@`` separator. Use ``0.0.0.0`` for all configured IPv4 addresses or
+``::`` for all configured IPv6 addresses. Filesystem path can be specified
+for listening on local unix SOCK_STREAM socket. Non-local address binding
+is automatically enabled if supported by the operating system.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* not set
+
+.. _xdp section:
+
+``xdp`` section
+===============
+
+Various options related to XDP listening, especially TCP.
+
+::
+
+ xdp:
+ listen: STR[@INT] | ADDR[@INT] ...
+ udp: BOOL
+ tcp: BOOL
+ quic: BOOL
+ quic-port: INT
+ quic-log: BOOL
+ tcp-max-clients: INT
+ tcp-inbuf-max-size: SIZE
+ tcp-outbuf-max-size: SIZE
+ tcp-idle-close-timeout: TIME
+ tcp-idle-reset-timeout: TIME
+ tcp-resend-timeout: TIME
+ route-check: BOOL
+
+.. CAUTION::
+ When you change configuration parameters dynamically or via configuration file
+ reload, some parameters in the XDP section require restarting the Knot server
+ so that the changes take effect.
+
+.. _xdp_listen:
+
+listen
+------
+
+One or more network device names (e.g. ``ens786f0``) on which the :ref:`Mode XDP`
+is enabled. Alternatively, an IP address can be used instead of a device name,
+but the server will still listen on all addresses belonging to the same interface!
+Optional port specification (default is 53) can be appended to each device name
+or address using ``@`` separator.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+.. CAUTION::
+ If XDP workers only process regular DNS traffic over UDP, it is strongly
+ recommended to also :ref:`listen <server_listen>` on the addresses which are
+ intended to offer the DNS service, at least to fulfil the DNS requirement for
+ working TCP.
+
+*Default:* not set
+
+.. _xdp_udp:
+
+udp
+---
+
+If enabled, DNS over UDP is processed with XDP workers.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``on``
+
+.. _xdp_tcp:
+
+tcp
+---
+
+If enabled, DNS over TCP traffic is processed with XDP workers.
+
+The TCP stack limitations:
+
+ - Congestion control is not implemented.
+ - Lost packets that do not contain TCP payload may not be resend.
+ - Not optimized for transfers of non-trivial zones.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``off``
+
+.. _xdp_quic:
+
+quic
+----
+
+If enabled, DNS over QUIC is processed with XDP workers.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``off``
+
+.. _xdp_quic-port:
+
+quic-port
+---------
+
+DNS over QUIC will listen on the interfaces configured by :ref:`xdp_listen`,
+but on different port, configured by this option.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``853``
+
+.. _xdp_quic-log:
+
+quic-log
+--------
+
+Triggers extensive logging of all QUIC protocol internals for every connection.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+*Default:* ``off``
+
+.. _xdp_tcp-max-clients:
+
+tcp-max-clients
+---------------
+
+A maximum number of TCP clients connected in parallel.
+
+*Minimum:* ``1024``
+
+*Default:* ``1000000`` (one million)
+
+.. _xdp_tcp-inbuf-max-size:
+
+tcp-inbuf-max-size
+------------------
+
+Maximum cumulative size of memory used for buffers of incompletely
+received messages.
+
+*Minimum:* ``1M`` (1 MiB)
+
+*Default:* ``100M`` (100 MiB)
+
+.. _xdp_tcp-outbuf-max-size:
+
+tcp-outbuf-max-size
+-------------------
+
+Maximum cumulative size of memory used for buffers of unACKed
+sent messages.
+
+*Minimum:* ``1M`` (1 MiB)
+
+*Default:* ``100M`` (100 MiB)
+
+.. _xdp_tcp-idle-close-timeout:
+
+tcp-idle-close-timeout
+----------------------
+
+Time in seconds, after which any idle connection is gracefully closed.
+
+*Minimum:* ``1``
+
+*Default:* ``10``
+
+.. _xdp_tcp-idle-reset-timeout:
+
+tcp-idle-reset-timeout
+----------------------
+
+Time in seconds, after which any idle connection is forcibly closed.
+
+*Minimum:* ``1``
+
+*Default:* ``20``
+
+.. _xdp_tcp-resend-timeout:
+
+tcp-resend-timeout
+------------------
+
+Resend outgoing data packets (with DNS response payload) if not ACKed
+before this timeout.
+
+*Minimum:* ``1``
+
+*Default:* ``5``
+
+.. _xdp_route-check:
+
+route-check
+-----------
+
+If enabled, routing information from the operating system is considered
+when processing every incoming DNS packet received over the XDP interface:
+
+- If the outgoing interface of the corresponding DNS response differs from
+ the incoming one, the packet is processed normally by UDP/TCP workers
+ (XDP isn't used).
+- If the destination address is blackholed, unreachable, or prohibited,
+ the DNS packet is dropped without any response.
+- The destination MAC address and possible VLAN tag for the response are taken
+ from the routing system.
+
+If disabled, symmetrical routing is applied. It means that the query source
+MAC address is used as a response destination MAC address. Possible VLAN tag
+is preserved.
+
+Change of this parameter requires restart of the Knot server to take effect.
+
+.. NOTE::
+ This mode requires forwarding enabled on the loopback interface
+ (``sysctl -w net.ipv4.conf.lo.forwarding=1`` and ``sysctl -w net.ipv6.conf.lo.forwarding=1``).
+ If forwarding is disabled, all incoming DNS packets are dropped!
+
+ Only VLAN 802.1Q is supported.
+
+*Default:* ``off``
+
+.. _control section:
+
+``control`` section
+===================
+
+Configuration of the server control interface.
+
+::
+
+ control:
+ listen: STR
+ timeout: TIME
+
+.. _control_listen:
+
+listen
+------
+
+A UNIX socket path where the server listens for control commands.
+
+*Default:* :ref:`rundir<server_rundir>`\ ``/knot.sock``
+
+.. _control_timeout:
+
+timeout
+-------
+
+Maximum time (in seconds) the control socket operations can take.
+Set to 0 for infinity.
+
+*Default:* ``5``
+
+.. _log section:
+
+``log`` section
+===============
+
+Server can be configured to log to the standard output, standard error
+output, syslog (or systemd journal if systemd is enabled) or into an arbitrary
+file.
+
+There are 6 logging severity levels:
+
+- ``critical`` – Non-recoverable error resulting in server shutdown.
+- ``error`` – Recoverable error, action should be taken.
+- ``warning`` – Warning that might require user action.
+- ``notice`` – Server notice or hint.
+- ``info`` – Informational message.
+- ``debug`` – Debug or detailed message.
+
+In the case of a missing log section, ``warning`` or more serious messages
+will be logged to both standard error output and syslog. The ``info`` and
+``notice`` messages will be logged to standard output.
+
+::
+
+ log:
+ - target: stdout | stderr | syslog | STR
+ server: critical | error | warning | notice | info | debug
+ control: critical | error | warning | notice | info | debug
+ zone: critical | error | warning | notice | info | debug
+ any: critical | error | warning | notice | info | debug
+
+.. _log_target:
+
+target
+------
+
+A logging output.
+
+Possible values:
+
+- ``stdout`` – Standard output.
+- ``stderr`` – Standard error output.
+- ``syslog`` – Syslog or systemd journal.
+- *file\_name* – A specific file.
+
+With ``syslog`` target, syslog service is used. However, if Knot DNS has been compiled
+with systemd support and operating system has been booted with systemd, systemd journal
+is used for logging instead of syslog.
+
+.. _log_server:
+
+server
+------
+
+Minimum severity level for messages related to general operation of the server to be
+logged.
+
+*Default:* not set
+
+.. _log_control:
+
+control
+-------
+
+Minimum severity level for messages related to server control to be logged.
+
+*Default:* not set
+
+.. _log_zone:
+
+zone
+----
+
+Minimum severity level for messages related to zones to be logged.
+
+*Default:* not set
+
+.. _log_any:
+
+any
+---
+
+Minimum severity level for all message types to be logged.
+
+*Default:* not set
+
+.. _stats section:
+
+``stats`` section
+=================
+
+Periodic server statistics dumping.
+
+::
+
+ statistics:
+ timer: TIME
+ file: STR
+ append: BOOL
+
+.. _statistics_timer:
+
+timer
+-----
+
+A period after which all available statistics metrics will by written to the
+:ref:`file<statistics_file>`.
+
+*Default:* not set
+
+.. _statistics_file:
+
+file
+----
+
+A file path of statistics output in the YAML format.
+
+*Default:* :ref:`rundir<server_rundir>`\ ``/stats.yaml``
+
+.. _statistics_append:
+
+append
+------
+
+If enabled, the output will be appended to the :ref:`file<statistics_file>`
+instead of file replacement.
+
+*Default:* ``off``
+
+.. _database section:
+
+``database`` section
+====================
+
+Configuration of databases for zone contents, DNSSEC metadata, or event timers.
+
+::
+
+ database:
+ storage: STR
+ journal-db: STR
+ journal-db-mode: robust | asynchronous
+ journal-db-max-size: SIZE
+ kasp-db: STR
+ kasp-db-max-size: SIZE
+ timer-db: STR
+ timer-db-max-size: SIZE
+ catalog-db: str
+ catalog-db-max-size: SIZE
+
+.. _database_storage:
+
+storage
+-------
+
+A data directory for storing journal, KASP, and timer databases.
+
+*Default:* ``${localstatedir}/lib/knot`` (configured with ``--with-storage=path``)
+
+.. _database_journal-db:
+
+journal-db
+----------
+
+An explicit specification of the persistent journal database directory.
+Non-absolute path (i.e. not starting with ``/``) is relative to
+:ref:`storage<database_storage>`.
+
+*Default:* :ref:`storage<database_storage>`\ ``/journal``
+
+.. _database_journal-db-mode:
+
+journal-db-mode
+---------------
+
+Specifies journal LMDB backend configuration, which influences performance
+and durability.
+
+Possible values:
+
+- ``robust`` – The journal database disk synchronization ensures database
+ durability but is generally slower.
+- ``asynchronous`` – The journal database disk synchronization is optimized for
+ better performance at the expense of lower database durability in the case of
+ a crash. This mode is recommended on secondary servers with many zones.
+
+*Default:* ``robust``
+
+.. _database_journal-db-max-size:
+
+journal-db-max-size
+-------------------
+
+The hard limit for the journal database maximum size. There is no cleanup logic
+in journal to recover from reaching this limit. Journal simply starts refusing
+changes across all zones. Decreasing this value has no effect if it is lower
+than the actual database file size.
+
+It is recommended to limit :ref:`journal-max-usage<zone_journal-max-usage>`
+per-zone instead of :ref:`journal-db-max-size<database_journal-db-max-size>`
+in most cases. Please keep this value larger than the sum of all zones'
+journal usage limits. See more details regarding
+:ref:`journal behaviour<Journal behaviour>`.
+
+.. NOTE::
+ This value also influences server's usage of virtual memory.
+
+*Default:* ``20G`` (20 GiB), or ``512M`` (512 MiB) for 32-bit
+
+.. _database_kasp-db:
+
+kasp-db
+-------
+
+An explicit specification of the KASP database directory.
+Non-absolute path (i.e. not starting with ``/``) is relative to
+:ref:`storage<database_storage>`.
+
+*Default:* :ref:`storage<database_storage>`\ ``/keys``
+
+.. _database_kasp-db-max-size:
+
+kasp-db-max-size
+----------------
+
+The hard limit for the KASP database maximum size.
+
+.. NOTE::
+ This value also influences server's usage of virtual memory.
+
+*Default:* ``500M`` (500 MiB)
+
+.. _database_timer-db:
+
+timer-db
+--------
+
+An explicit specification of the persistent timer database directory.
+Non-absolute path (i.e. not starting with ``/``) is relative to
+:ref:`storage<database_storage>`.
+
+*Default:* :ref:`storage<database_storage>`\ ``/timers``
+
+.. _database_timer-db-max-size:
+
+timer-db-max-size
+-----------------
+
+The hard limit for the timer database maximum size.
+
+.. NOTE::
+ This value also influences server's usage of virtual memory.
+
+*Default:* ``100M`` (100 MiB)
+
+.. _database_catalog-db:
+
+catalog-db
+----------
+
+An explicit specification of the zone catalog database directory.
+Only useful if :ref:`catalog-zones` are enabled.
+Non-absolute path (i.e. not starting with ``/``) is relative to
+:ref:`storage<database_storage>`.
+
+*Default:* :ref:`storage<database_storage>`\ ``/catalog``
+
+.. _database_catalog-db-max-size:
+
+catalog-db-max-size
+-------------------
+
+The hard limit for the catalog database maximum size.
+
+.. NOTE::
+ This value also influences server's usage of virtual memory.
+
+*Default:* ``20G`` (20 GiB), or ``512M`` (512 MiB) for 32-bit
+
+.. _keystore section:
+
+``keystore`` section
+====================
+
+DNSSEC keystore configuration.
+
+::
+
+ keystore:
+ - id: STR
+ backend: pem | pkcs11
+ config: STR
+ key-label: BOOL
+
+.. _keystore_id:
+
+id
+--
+
+A keystore identifier.
+
+
+.. _keystore_backend:
+
+backend
+-------
+
+A key storage backend type.
+
+Possible values:
+
+- ``pem`` – PEM files.
+- ``pkcs11`` – PKCS #11 storage.
+
+*Default:* ``pem``
+
+.. _keystore_config:
+
+config
+------
+
+A backend specific configuration. A directory with PEM files (the path can
+be specified as a relative path to :ref:`kasp-db<database_kasp-db>`) or
+a configuration string for PKCS #11 storage (`<pkcs11-url> <module-path>`).
+
+.. NOTE::
+ Example configuration string for PKCS #11::
+
+ "pkcs11:token=knot;pin-value=1234 /usr/lib64/pkcs11/libsofthsm2.so"
+
+*Default:* :ref:`kasp-db<database_kasp-db>`\ ``/keys``
+
+.. _keystore_key-label:
+
+key-label
+---------
+
+If enabled in combination with the PKCS #11 :ref:`keystore_backend`, generated keys
+are labeled in the form ``<zone_name> KSK|ZSK``.
+
+*Default:* ``off``
+
+.. _key section:
+
+``key`` section
+===============
+
+Shared TSIG keys used to authenticate communication with the server.
+
+::
+
+ key:
+ - id: DNAME
+ algorithm: hmac-md5 | hmac-sha1 | hmac-sha224 | hmac-sha256 | hmac-sha384 | hmac-sha512
+ secret: BASE64
+
+.. _key_id:
+
+id
+--
+
+A key name identifier.
+
+.. NOTE::
+ This value MUST be exactly the same as the name of the TSIG key on the
+ opposite primary/secondary server(s).
+
+.. _key_algorithm:
+
+algorithm
+---------
+
+A TSIG key algorithm. See
+`TSIG Algorithm Numbers <https://www.iana.org/assignments/tsig-algorithm-names/tsig-algorithm-names.xhtml>`_.
+
+Possible values:
+
+- ``hmac-md5``
+- ``hmac-sha1``
+- ``hmac-sha224``
+- ``hmac-sha256``
+- ``hmac-sha384``
+- ``hmac-sha512``
+
+*Default:* not set
+
+.. _key_secret:
+
+secret
+------
+
+Shared key secret.
+
+*Default:* not set
+
+.. _remote section:
+
+``remote`` section
+==================
+
+Definitions of remote servers for outgoing connections (source of a zone
+transfer, target for a notification, etc.).
+
+::
+
+ remote:
+ - id: STR
+ address: ADDR[@INT] ...
+ via: ADDR[@INT] ...
+ key: key_id
+ block-notify-after-transfer: BOOL
+ no-edns: BOOL
+ automatic-acl: BOOL
+
+.. _remote_id:
+
+id
+--
+
+A remote identifier.
+
+.. _remote_address:
+
+address
+-------
+
+An ordered list of destination IP addresses which are used for communication
+with the remote server. The addresses are tried in sequence until the
+remote is reached. Optional destination port (default is 53)
+can be appended to the address using ``@`` separator.
+
+*Default:* not set
+
+.. NOTE::
+ If the remote is contacted and it refuses to perform requested action,
+ no more addresses will be tried for this remote.
+
+.. _remote_via:
+
+via
+---
+
+An ordered list of source IP addresses. The first address with the same family
+as the destination address is used as a source address for communication with
+the remote. This option can help if the server listens on more addresses.
+Optional source port (default is random) can be appended
+to the address using ``@`` separator.
+
+*Default:* not set
+
+.. _remote_key:
+
+key
+---
+
+A :ref:`reference<key_id>` to the TSIG key which is used to authenticate
+the communication with the remote server.
+
+*Default:* not set
+
+.. _remote_block-notify-after-transfer:
+
+block-notify-after-transfer
+---------------------------
+
+When incoming AXFR/IXFR from this remote (as a primary server), suppress
+sending NOTIFY messages to all configured secondary servers.
+
+*Default:* ``off``
+
+.. _remote_no-edns:
+
+no-edns
+-------
+
+If enabled, no OPT record (EDNS) is inserted to outgoing requests to this
+remote server. This mode is necessary for communication with some broken
+implementations (e.g. Windows Server 2016).
+
+.. NOTE::
+ This option effectively disables :ref:`zone expire<Zone expiration>` timer
+ updates via EDNS EXPIRE option specified in :rfc:`7314`.
+
+*Default:* ``off``
+
+.. _remote_automatic-acl:
+
+automatic-acl
+-------------
+
+If enabled, some authorized operations for the remote are automatically allowed
+based on the context:
+
+- Incoming NOTIFY is allowed from the remote if it's configured as a
+ :ref:`primary server <zone_master>` for the zone.
+- Outgoing zone transfer is allowed to the remote if it's configured as a
+ :ref:`NOTIFY target <zone_notify>` for the zone.
+
+Automatic ACL rules are evaluated before explicit :ref:`zone ACL <zone_acl>` configuration.
+
+.. NOTE::
+ This functionality requires global activation via
+ :ref:`server_automatic-acl` in the server section.
+
+*Default:* ``on``
+
+.. _remotes section:
+
+``remotes`` section
+===================
+
+Definitions of groups of remote servers. Remote grouping can simplify the
+configuration.
+
+::
+
+ remotes:
+ - id: STR
+ remote: remote_id ...
+
+.. _remotes_id:
+
+id
+--
+
+A remote group identifier.
+
+.. _remotes_remote:
+
+remote
+------
+
+An ordered list of :ref:`references<remote_id>` to remote server definitions.
+
+*Default:* not set
+
+.. _acl section:
+
+``acl`` section
+===============
+
+Access control list rule definitions. An ACL rule is a description of one
+or more authorized operations (zone transfer request, zone change notification,
+and dynamic DNS update) which are allowed to be processed or denied.
+
+::
+
+ acl:
+ - id: STR
+ address: ADDR[/INT] | ADDR-ADDR ...
+ key: key_id ...
+ remote: remote_id | remotes_id ...
+ action: query | notify | transfer | update ...
+ deny: BOOL
+ update-type: STR ...
+ update-owner: key | zone | name
+ update-owner-match: sub-or-equal | equal | sub
+ update-owner-name: STR ...
+
+.. _acl_id:
+
+id
+--
+
+An ACL rule identifier.
+
+.. _acl_address:
+
+address
+-------
+
+An ordered list of IP addresses, network subnets, or network ranges. The query's
+source address must match one of them. If this item is not set, address match is not
+required.
+
+*Default:* not set
+
+.. _acl_key:
+
+key
+---
+
+An ordered list of :ref:`reference<key_id>`\ s to TSIG keys. The query must
+match one of them. If this item is not set, transaction authentication is not used.
+
+*Default:* not set
+
+.. _acl_remote:
+
+remote
+------
+
+An ordered list of references :ref:`remote<remote_id>` and
+:ref:`remotes<remotes_id>`. The query must
+match one of the remotes. Specifically, one of the remote's addresses and remote's
+TSIG key if configured must match.
+
+.. NOTE::
+ This option cannot be specified along with the :ref:`acl_address` or
+ :ref:`acl_key` option at one ACL item.
+
+*Default:* not set
+
+.. _acl_action:
+
+action
+------
+
+An ordered list of allowed (or denied) actions.
+
+Possible values:
+
+- ``query`` – Allow regular DNS query. As normal queries are always allowed,
+ this action is only useful in combination with :ref:`TSIG key<acl_key>`.
+- ``notify`` – Allow incoming notify (NOTIFY).
+- ``transfer`` – Allow zone transfer (AXFR, IXFR).
+- ``update`` – Allow zone updates (DDNS).
+
+*Default:* ``query``
+
+.. _acl_deny:
+
+deny
+----
+
+If enabled, instead of allowing, deny the specified :ref:`action<acl_action>`,
+:ref:`address<acl_address>`, :ref:`key<acl_key>`, or combination if these
+items. If no action is specified, deny all actions.
+
+*Default:* ``off``
+
+.. _acl_update-type:
+
+update-type
+-----------
+
+A list of allowed types of Resource Records in a zone update. Every record in an update
+must match one of the specified types.
+
+*Default:* not set
+
+.. _acl_update-owner:
+
+update-owner
+------------
+
+This option restricts possible owners of Resource Records in a zone update by comparing
+them to either the :ref:`TSIG key<acl_key>` identity, the current zone name, or to a list of
+domain names given by the :ref:`acl_update-owner-name` option.
+The comparison method is given by the :ref:`acl_update-owner-match` option.
+
+Possible values:
+
+- ``key`` — The owner of each updated RR must match the identity of the TSIG key if used.
+- ``name`` — The owner of each updated RR must match at least one name in the
+ :ref:`acl_update-owner-name` list.
+- ``zone`` — The owner of each updated RR must match the current zone name.
+
+*Default:* not set
+
+.. _acl_update-owner-match:
+
+update-owner-match
+------------------
+
+This option defines how the owners of Resource Records in an update are matched to the domain name(s)
+set by the :ref:`acl_update-owner` option.
+
+Possible values:
+
+- ``sub-or-equal`` — The owner of each RR in an update must either be equal to
+ or be a subdomain of at least one domain name set by :ref:`acl_update-owner`.
+- ``equal`` — The owner of each updated RR must be equal to at least one domain
+ name set by :ref:`acl_update-owner`.
+- ``sub`` — The owner of each updated RR must be a subdomain of, but MUST NOT
+ be equal to at least one domain name set by :ref:`acl_update-owner`.
+
+*Default:* ``sub-or-equal``
+
+.. _acl_update-owner-name:
+
+update-owner-name
+-----------------
+
+A list of allowed owners of RRs in a zone update used with :ref:`acl_update-owner`
+set to ``name``. Every listed owner name which is not FQDN (i.e. it doesn't end
+in a dot) is considered as if it was appended with the target zone name.
+Such a relative owner name specification allows better ACL rule reusability across
+multiple zones.
+
+*Default:* not set
+
+.. _submission section:
+
+``submission`` section
+======================
+
+Parameters of KSK submission checks.
+
+::
+
+ submission:
+ - id: STR
+ parent: remote_id | remotes_id ...
+ check-interval: TIME
+ timeout: TIME
+ parent-delay: TIME
+
+.. _submission_id:
+
+id
+--
+
+A submission identifier.
+
+.. _submission_parent:
+
+parent
+------
+
+A list of references :ref:`remote<remote_id>` and :ref:`remotes<remotes_id>`
+to parent's DNS servers to be checked for
+presence of corresponding DS records in the case of KSK submission. All of them must
+have a corresponding DS for the rollover to continue. If none is specified, the
+rollover must be pushed forward manually.
+
+*Default:* not set
+
+.. TIP::
+ A DNSSEC-validating resolver can be set as a parent.
+
+.. _submission_check-interval:
+
+check-interval
+--------------
+
+Interval for periodic checks of DS presence on parent's DNS servers, in the
+case of the KSK submission.
+
+*Default:* ``1h`` (1 hour)
+
+.. _submission_timeout:
+
+timeout
+-------
+
+After this time period (in seconds) the KSK submission is automatically considered
+successful, even if all the checks were negative or no parents are configured.
+Set to 0 for infinity.
+
+*Default:* ``0``
+
+.. _submission_parent-delay:
+
+parent-delay
+------------
+
+After successful parent DS check, wait for this period before continuing the next
+key roll-over step. This delay shall cover the propagation delay of update in the
+parent zone.
+
+*Default:* ``0``
+
+.. _policy section:
+
+``policy`` section
+==================
+
+DNSSEC policy configuration.
+
+::
+
+ policy:
+ - id: STR
+ keystore: keystore_id
+ manual: BOOL
+ single-type-signing: BOOL
+ algorithm: rsasha1 | rsasha1-nsec3-sha1 | rsasha256 | rsasha512 | ecdsap256sha256 | ecdsap384sha384 | ed25519 | ed448
+ ksk-size: SIZE
+ zsk-size: SIZE
+ ksk-shared: BOOL
+ dnskey-ttl: TIME
+ zone-max-ttl: TIME
+ ksk-lifetime: TIME
+ zsk-lifetime: TIME
+ delete-delay: TIME
+ propagation-delay: TIME
+ rrsig-lifetime: TIME
+ rrsig-refresh: TIME
+ rrsig-pre-refresh: TIME
+ reproducible-signing: BOOL
+ nsec3: BOOL
+ nsec3-iterations: INT
+ nsec3-opt-out: BOOL
+ nsec3-salt-length: INT
+ nsec3-salt-lifetime: TIME
+ signing-threads: INT
+ ksk-submission: submission_id
+ ds-push: remote_id | remotes_id ...
+ cds-cdnskey-publish: none | delete-dnssec | rollover | always | double-ds
+ cds-digest-type: sha256 | sha384
+ dnskey-management: full | incremental
+ offline-ksk: BOOL
+ unsafe-operation: none | no-check-keyset | no-update-dnskey | no-update-nsec | no-update-expired ...
+
+.. _policy_id:
+
+id
+--
+
+A policy identifier.
+
+.. _policy_keystore:
+
+keystore
+--------
+
+A :ref:`reference<keystore_id>` to a keystore holding private key material
+for zones.
+
+*Default:* an imaginary keystore with all default values
+
+.. NOTE::
+ A configured keystore called "default" won't be used unless explicitly referenced.
+
+.. _policy_manual:
+
+manual
+------
+
+If enabled, automatic key management is not used.
+
+*Default:* ``off``
+
+.. _policy_single-type-signing:
+
+single-type-signing
+-------------------
+
+If enabled, Single-Type Signing Scheme is used in the automatic key management
+mode.
+
+*Default:* ``off`` (:ref:`module onlinesign<mod-onlinesign>` has default ``on``)
+
+.. _policy_algorithm:
+
+algorithm
+---------
+
+An algorithm of signing keys and issued signatures. See
+`DNSSEC Algorithm Numbers <https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml#dns-sec-alg-numbers-1>`_.
+
+Possible values:
+
+- ``rsasha1``
+- ``rsasha1-nsec3-sha1``
+- ``rsasha256``
+- ``rsasha512``
+- ``ecdsap256sha256``
+- ``ecdsap384sha384``
+- ``ed25519``
+- ``ed448``
+
+.. NOTE::
+ Ed25519 algorithm is only available if compiled with GnuTLS 3.6.0+.
+
+ Ed448 algorithm is only available if compiled with GnuTLS 3.6.12+ and Nettle 3.6+.
+
+*Default:* ``ecdsap256sha256``
+
+.. _policy_ksk-size:
+
+ksk-size
+--------
+
+A length of newly generated :abbr:`KSK (Key Signing Key)` or
+:abbr:`CSK (Combined Signing Key)` keys.
+
+*Default:* ``2048`` (rsa*), ``256`` (ecdsap256), ``384`` (ecdsap384), ``256`` (ed25519),
+``456`` (ed448)
+
+.. _policy_zsk-size:
+
+zsk-size
+--------
+
+A length of newly generated :abbr:`ZSK (Zone Signing Key)` keys.
+
+*Default:* see default for :ref:`ksk-size<policy_ksk-size>`
+
+.. _policy_ksk-shared:
+
+ksk-shared
+----------
+
+If enabled, all zones with this policy assigned will share one or more KSKs.
+More KSKs can be shared during a KSK rollover.
+
+.. WARNING::
+ As the shared KSK set is bound to the policy :ref:`id<policy_id>`, renaming the
+ policy breaks this connection and new shared KSK set is initiated when
+ a new KSK is needed.
+
+*Default:* ``off``
+
+.. _policy_dnskey-ttl:
+
+dnskey-ttl
+----------
+
+A TTL value for DNSKEY records added into zone apex.
+
+.. NOTE::
+ Has influence over ZSK key lifetime.
+
+.. WARNING::
+ Ensure all DNSKEYs with updated TTL are propagated before any subsequent
+ DNSKEY rollover starts.
+
+*Default:* zone SOA TTL
+
+.. _policy_zone-max-ttl:
+
+zone-max-ttl
+------------
+
+Declare (override) maximal TTL value among all the records in zone.
+
+.. NOTE::
+ It's generally recommended to override the maximal TTL computation by setting this
+ explicitly whenever possible. It's required for :ref:`DNSSEC Offline KSK` and
+ really reasonable when records are generated dynamically
+ (e.g. by a :ref:`module<mod-synthrecord>`).
+
+*Default:* computed after zone is loaded
+
+.. _policy_ksk-lifetime:
+
+ksk-lifetime
+------------
+
+A period between KSK activation and the next rollover initiation.
+
+.. NOTE::
+ KSK key lifetime is also influenced by propagation-delay, dnskey-ttl,
+ and KSK submission delay.
+
+ Zero (aka infinity) value causes no KSK rollover as a result.
+
+ This applies for CSK lifetime if single-type-signing is enabled.
+
+*Default:* ``0``
+
+.. _policy_zsk-lifetime:
+
+zsk-lifetime
+------------
+
+A period between ZSK activation and the next rollover initiation.
+
+.. NOTE::
+ More exactly, this period is measured since a ZSK is activated,
+ and after this, a new ZSK is generated to replace it within
+ following roll-over.
+
+ ZSK key lifetime is also influenced by propagation-delay and dnskey-ttl
+
+ Zero (aka infinity) value causes no ZSK rollover as a result.
+
+*Default:* ``30d`` (30 days)
+
+.. _policy_delete-delay:
+
+delete-delay
+------------
+
+Once a key (KSK or ZSK) is rolled-over and removed from the zone,
+keep it in the KASP database for at least this period before deleting it completely.
+This might be useful in some troubleshooting cases when resurrection
+is needed.
+
+*Default:* ``0``
+
+.. _policy_propagation-delay:
+
+propagation-delay
+-----------------
+
+An extra delay added for each key rollover step. This value should be high
+enough to cover propagation of data from the primary server to all
+secondary servers, as well as the duration of signing routine itself and
+possible outages in signing and propagation infrastructure. In other words,
+this delay should ensure that within this period of time after planned
+change of the key set, all public-facing secondaries will already serve
+new DNSKEY RRSet for sure.
+
+.. NOTE::
+ Has influence over ZSK key lifetime.
+
+*Default:* ``1h`` (1 hour)
+
+.. _policy_rrsig-lifetime:
+
+rrsig-lifetime
+--------------
+
+A validity period of newly issued signatures.
+
+.. NOTE::
+ The RRSIG's signature inception time is set to 90 minutes in the past. This
+ time period is not counted to the signature lifetime.
+
+*Default:* ``14d`` (14 days)
+
+.. _policy_rrsig-refresh:
+
+rrsig-refresh
+-------------
+
+A period how long at least before a signature expiration the signature will be refreshed,
+in order to prevent expired RRSIGs on secondary servers or resolvers' caches.
+
+*Default:* :ref:`policy_propagation-delay` + :ref:`policy_zone-max-ttl`
+
+.. _policy_rrsig-pre-refresh:
+
+rrsig-pre-refresh
+-----------------
+
+A period how long at most before a signature refresh time the signature might be refreshed,
+in order to refresh RRSIGs in bigger batches on a frequently updated zone
+(avoid re-sign event too often).
+
+*Default:* ``1h`` (1 hour)
+
+.. _policy_reproducible-signing:
+
+reproducible-signing
+--------------------
+
+For ECDSA algorithms, generate RRSIG signatures deterministically (:rfc:`6979`).
+Besides better theoretical cryptographic security, this mode allows significant
+speed-up of loading signed (by the same method) zones. However, the zone signing
+is a bit slower.
+
+*Default:* ``off``
+
+.. _policy_nsec3:
+
+nsec3
+-----
+
+Specifies if NSEC3 will be used instead of NSEC.
+
+*Default:* ``off``
+
+.. _policy_nsec3-iterations:
+
+nsec3-iterations
+----------------
+
+A number of additional times the hashing is performed.
+
+*Default:* ``0``
+
+.. _policy_nsec3-opt-out:
+
+nsec3-opt-out
+-------------
+
+If set, NSEC3 records won't be created for insecure delegations.
+This speeds up the zone signing and reduces overall zone size.
+
+.. WARNING::
+ NSEC3 with the Opt-Out bit set no longer works as a proof of non-existence
+ in this zone.
+
+*Default:* ``off``
+
+.. _policy_nsec3-salt-length:
+
+nsec3-salt-length
+-----------------
+
+A length of a salt field in octets, which is appended to the original owner
+name before hashing.
+
+*Default:* ``8``
+
+.. _policy_nsec3-salt-lifetime:
+
+nsec3-salt-lifetime
+-------------------
+
+A validity period of newly issued salt field.
+
+Zero value means infinity.
+
+Special value *-1* triggers re-salt every time when active ZSK changes.
+This optimizes the number of big changes to the zone.
+
+*Default:* ``30d`` (30 days)
+
+.. _policy_signing-threads:
+
+signing-threads
+---------------
+
+When signing zone or update, use this number of threads for parallel signing.
+
+Those are extra threads independent of :ref:`Background workers<server_background-workers>`.
+
+.. NOTE::
+ Some steps of the DNSSEC signing operation are not parallelized.
+
+*Default:* ``1`` (no extra threads)
+
+.. _policy_ksk-submission-check:
+
+ksk-submission
+--------------
+
+A reference to :ref:`submission<submission_id>` section holding parameters of
+KSK submission checks.
+
+*Default:* not set
+
+.. _policy_ds-push:
+
+ds-push
+-------
+
+Optional references :ref:`remote<remote_id>` and :ref:`remotes<remotes_id>`
+to authoritative DNS server of the
+parent's zone. The remote server must be configured to accept DS record
+updates via DDNS. Whenever a CDS record in the local zone is changed, the
+corresponding DS record is sent as a dynamic update (DDNS) to the parent
+DNS server. All previous DS records are deleted within the DDNS message.
+It's possible to manage both child and parent zones by the same Knot DNS server.
+
+.. NOTE::
+ This feature requires :ref:`cds-cdnskey-publish<policy_cds-cdnskey-publish>`
+ not to be set to ``none``.
+
+.. NOTE::
+ The mentioned change to CDS record usually means that a KSK roll-over is running
+ and the new key being rolled-in is in "ready" state already for the period of
+ :ref:`propagation-delay<policy_propagation-delay>`.
+
+.. NOTE::
+ Module :ref:`Onlinesign<mod-onlinesign>` doesn't support DS push.
+
+*Default:* not set
+
+.. _policy_cds-cdnskey-publish:
+
+cds-cdnskey-publish
+-------------------
+
+Controls if and how shall the CDS and CDNSKEY be published in the zone.
+
+Possible values:
+
+- ``none`` – Never publish any CDS or CDNSKEY records in the zone.
+- ``delete-dnssec`` – Publish special CDS and CDNSKEY records indicating turning off DNSSEC.
+- ``rollover`` – Publish CDS and CDNSKEY records for ready and not yet active KSK (submission phase of KSK rollover).
+- ``always`` – Always publish one CDS and one CDNSKEY records for the current KSK.
+- ``double-ds`` – Always publish up to two CDS and two CDNSKEY records for ready and/or active KSKs.
+
+.. NOTE::
+ If the zone keys are managed manually, the CDS and CDNSKEY rrsets may contain
+ more records depending on the keys available.
+
+.. WARNING::
+ The ``double-ds`` value does not trigger double-DS roll-over method. That method is
+ only supported when performed manually, with unset :ref:`policy_ksk-submission-check`.
+
+*Default:* ``rollover``
+
+.. _policy_cds-digest-type:
+
+cds-digest-type
+---------------
+
+Specify digest type for published CDS records.
+
+*Default:* ``sha256``
+
+.. _policy_dnskey-management:
+
+dnskey-management
+-----------------
+
+Specify how the DNSKEY, CDNSKEY, and CDS RRSets at the zone apex are handled
+when (re-)signing the zone.
+
+Possible values:
+
+- ``full`` – Upon every zone (re-)sign, delete all unknown DNSKEY, CDNSKEY, and CDS
+ records and keep just those that are related to the zone keys stored in the KASP database.
+- ``incremental`` – Keep unknown DNSKEY, CDNSKEY, and CDS records in the zone, and
+ modify server-managed records incrementally by employing changes in the KASP database.
+
+.. NOTE::
+ Prerequisites for *incremental*:
+
+ - The :ref:`Offline KSK <DNSSEC Offline KSK>` isn't supported.
+ - The :ref:`policy_delete-delay` is long enough to cover possible daemon
+ shutdown (e.g. due to server maintenance).
+ - Avoided manual deletion of keys with :doc:`keymgr<man_keymgr>`.
+
+ Otherwise there might remain some DNSKEY records in the zone, belonging to
+ deleted keys.
+
+*Default:* ``full``
+
+.. _policy_offline-ksk:
+
+offline-ksk
+-----------
+
+Specifies if :ref:`Offline KSK <DNSSEC Offline KSK>` feature is enabled.
+
+*Default:* ``off``
+
+.. _policy_unsafe-operation:
+
+unsafe-operation
+----------------
+
+Turn off some DNSSEC safety features.
+
+Possible values:
+
+- ``none`` – Nothing disabled.
+- ``no-check-keyset`` – Don't check active keys in present algorithms. This may
+ lead to violation of :rfc:`4035#section-2.2`.
+- ``no-update-dnskey`` – Don't maintain/update DNSKEY, CDNSKEY, and CDS records
+ in the zone apex according to KASP database. Juste leave them as they are in the zone.
+- ``no-update-nsec`` – Don't maintain/update NSEC/NSEC3 chain. Leave all the records
+ as they are in the zone.
+- ``no-update-expired`` – Don't update expired RRSIGs.
+
+Multiple values may be specified.
+
+.. WARNING::
+ This mode is intended for DNSSEC experts who understand the corresponding consequences.
+
+*Default:* ``none``
+
+.. _template section:
+
+``template`` section
+====================
+
+A template is shareable zone settings, which can simplify configuration by
+reducing duplicates. A special default template (with the *default* identifier)
+can be used for global zone configuration or as an implicit configuration
+if a zone doesn't have another template specified.
+
+::
+
+ template:
+ - id: STR
+ global-module: STR/STR ...
+ # All zone options (excluding 'template' item)
+
+.. NOTE::
+ If an item is explicitly specified both in the referenced template and
+ the zone, the template item value is overridden by the zone item value.
+
+.. _template_id:
+
+id
+--
+
+A template identifier.
+
+.. _template_global-module:
+
+global-module
+-------------
+
+An ordered list of references to query modules in the form of *module_name* or
+*module_name/module_id*. These modules apply to all queries.
+
+.. NOTE::
+ This option is only available in the *default* template.
+
+*Default:* not set
+
+.. _zone section:
+
+``zone`` section
+================
+
+Definition of zones served by the server.
+
+::
+
+ zone:
+ - domain: DNAME
+ template: template_id
+ storage: STR
+ file: STR
+ master: remote_id | remotes_id ...
+ ddns-master: remote_id
+ notify: remote_id | remotes_id ...
+ acl: acl_id ...
+ provide-ixfr: BOOL
+ semantic-checks: BOOL | soft
+ zonefile-sync: TIME
+ zonefile-load: none | difference | difference-no-serial | whole
+ journal-content: none | changes | all
+ journal-max-usage: SIZE
+ journal-max-depth: INT
+ zone-max-size : SIZE
+ adjust-threads: INT
+ dnssec-signing: BOOL
+ dnssec-validation: BOOL
+ dnssec-policy: policy_id
+ ds-push: remote_id | remotes_id ...
+ zonemd-verify: BOOL
+ zonemd-generate: none | zonemd-sha384 | zonemd-sha512 | remove
+ serial-policy: increment | unixtime | dateserial
+ refresh-min-interval: TIME
+ refresh-max-interval: TIME
+ retry-min-interval: TIME
+ retry-max-interval: TIME
+ expire-min-interval: TIME
+ expire-max-interval: TIME
+ catalog-role: none | interpret | generate | member
+ catalog-template: template_id ...
+ catalog-zone: DNAME
+ catalog-group: STR
+ module: STR/STR ...
+
+.. _zone_domain:
+
+domain
+------
+
+A zone name identifier.
+
+.. _zone_template:
+
+template
+--------
+
+A :ref:`reference<template_id>` to a configuration template.
+
+*Default:* not set or ``default`` (if the template exists)
+
+.. _zone_storage:
+
+storage
+-------
+
+A data directory for storing zone files.
+
+*Default:* ``${localstatedir}/lib/knot`` (configured with ``--with-storage=path``)
+
+.. _zone_file:
+
+file
+----
+
+A path to the zone file. Non-absolute path (i.e. not starting with ``/``) is
+relative to :ref:`storage<zone_storage>`.
+It is also possible to use the following formatters:
+
+- ``%c[``\ *N*\ ``]`` or ``%c[``\ *N*\ ``-``\ *M*\ ``]`` – Means the *N*\ th
+ character or a sequence of characters beginning from the *N*\ th and ending
+ with the *M*\ th character of the textual zone name (see ``%s``). The
+ indexes are counted from 0 from the left. All dots (including the terminal
+ one) are considered. If the character is not available, the formatter has no effect.
+- ``%l[``\ *N*\ ``]`` – Means the *N*\ th label of the textual zone name
+ (see ``%s``). The index is counted from 0 from the right (0 ~ TLD).
+ If the label is not available, the formatter has no effect.
+- ``%s`` – Means the current zone name in the textual representation.
+ The zone name doesn't include the terminating dot (the result for the root
+ zone is the empty string!).
+- ``%%`` – Means the ``%`` character.
+
+.. WARNING::
+ Beware of special characters which are escaped or encoded in the \\DDD form
+ where DDD is corresponding decimal ASCII code.
+
+*Default:* :ref:`storage<zone_storage>`\ ``/%s.zone``
+
+.. _zone_master:
+
+master
+------
+
+An ordered list of references :ref:`remote<remote_id>` and
+:ref:`remotes<remotes_id>` to zone primary servers
+(formerly known as master servers).
+
+*Default:* not set
+
+.. _zone_ddns-master:
+
+ddns-master
+-----------
+
+A :ref:`reference<remote_id>` to zone primary master. If not specified,
+the first :ref:`master<zone_master>` server is used.
+
+*Default:* not set
+
+.. _zone_notify:
+
+notify
+------
+
+An ordered list of references :ref:`remote<remote_id>` and
+:ref:`remotes<remotes_id>` to secondary servers to which notify
+message is sent if the zone changes.
+
+*Default:* not set
+
+.. _zone_acl:
+
+acl
+---
+
+An ordered list of :ref:`references<acl_id>` to ACL rules which can allow
+or disallow zone transfers, updates or incoming notifies.
+
+*Default:* not set
+
+.. _zone_provide-ixfr:
+
+provide-ixfr
+------------
+
+If disabled, the server is forced to respond with AXFR to IXFR queries.
+If enabled, IXFR requests are responded normally.
+
+*Default:* ``on``
+
+.. _zone_semantic-checks:
+
+semantic-checks
+---------------
+
+Selects if extra zone semantic checks are used or impacts of the mandatory checks.
+
+There are several mandatory checks which are always enabled and cannot be turned
+off. An error in a mandatory check causes the zone not to be loaded. Most of
+the mandatory checks can be weakened by setting ``soft``, which allows the zone to
+be loaded even if the check fails.
+
+If enabled, extra checks are used. These checks don't prevent the zone from loading.
+
+The mandatory checks are applied to zone files, zone transfers, and updates via
+control interface. The extra checks are applied to zone files only!
+
+Mandatory checks:
+
+- Missing SOA record at the zone apex (:rfc:`1034`) (*)
+- An extra record exists together with a CNAME record except for RRSIG and NSEC (:rfc:`1034`)
+- Multiple CNAME records with the same owner exist (:rfc:`1034`)
+- DNAME record having a record under it (:rfc:`6672`)
+- Multiple DNAME records with the same owner exist (:rfc:`6672`)
+- NS record exists together with a DNAME record (:rfc:`6672`)
+
+(*) The marked check can't be weakened by the soft mode. All other mandatory checks
+are subject to the optional soft mode.
+
+Extra checks:
+
+- Missing NS record at the zone apex
+- Missing glue A or AAAA record
+- Invalid DS or NSEC3PARAM record
+- CDS or CDNSKEY inconsistency
+- All other DNSSEC checks executed during :ref:`zone_dnssec-validation`
+
+.. NOTE::
+ The soft mode allows the refresh event to ignore a CNAME response to a SOA
+ query (malformed message) and triggers a zone bootstrap instead.
+
+*Default:* ``off``
+
+.. _zone_zonefile-sync:
+
+zonefile-sync
+-------------
+
+The time after which the current zone in memory will be synced with a zone file
+on the disk (see :ref:`file<zone_file>`). The server will serve the latest
+zone even after a restart using zone journal, but the zone file on the disk will
+only be synced after ``zonefile-sync`` time has expired (or after manual zone
+flush). This is applicable when the zone is updated via IXFR, DDNS or automatic
+DNSSEC signing. In order to completely disable automatic zone file synchronization,
+set the value to -1. In that case, it is still possible to force a manual zone flush
+using the ``-f`` option.
+
+.. NOTE::
+ If you are serving large zones with frequent updates where
+ the immediate sync with a zone file is not desirable, increase the value.
+
+*Default:* ``0`` (immediate)
+
+.. _zone_zonefile-load:
+
+zonefile-load
+-------------
+
+Selects how the zone file contents are applied during zone load.
+
+Possible values:
+
+- ``none`` – The zone file is not used at all.
+- ``difference`` – If the zone contents are already available during server start or reload,
+ the difference is computed between them and the contents of the zone file. This difference
+ is then checked for semantic errors and applied to the current zone contents.
+- ``difference-no-serial`` – Same as ``difference``, but the SOA serial in the zone file is
+ ignored, the server takes care of incrementing the serial automatically.
+- ``whole`` – Zone contents are loaded from the zone file.
+
+When ``difference`` is configured and there are no zone contents yet (cold start
+and no zone contents in the journal), it behaves the same way as ``whole``.
+
+*Default:* ``whole``
+
+.. _zone_journal-content:
+
+journal-content
+---------------
+
+Selects how the journal shall be used to store zone and its changes.
+
+Possible values:
+
+- ``none`` – The journal is not used at all.
+- ``changes`` – Zone changes history is stored in journal.
+- ``all`` – Zone contents and history is stored in journal.
+
+*Default:* ``changes``
+
+.. _zone_journal-max-usage:
+
+journal-max-usage
+-----------------
+
+Policy how much space in journal DB will the zone's journal occupy.
+
+.. NOTE::
+ Journal DB may grow far above the sum of journal-max-usage across
+ all zones, because of DB free space fragmentation.
+
+*Default:* ``100M`` (100 MiB)
+
+.. _zone_journal-max-depth:
+
+journal-max-depth
+-----------------
+
+Maximum history length of the journal.
+
+.. NOTE::
+ Zone-in-journal changeset isn't counted to the limit.
+
+*Minimum:* ``2``
+
+*Default:* ``20``
+
+.. _zone_zone-max-size:
+
+zone-max-size
+-------------
+
+Maximum size of the zone. The size is measured as size of the zone records
+in wire format without compression. The limit is enforced for incoming zone
+transfers and dynamic updates.
+
+For incremental transfers (IXFR), the effective limit for the total size of
+the records in the transfer is twice the configured value. However the final
+size of the zone must satisfy the configured value.
+
+*Default:* unlimited
+
+.. _zone_adjust-threads:
+
+adjust-threads
+--------------
+
+Parallelize internal zone adjusting procedures by using specified number of
+threads. This is useful with huge zones with NSEC3. Speedup observable at
+server startup and while processing NSEC3 re-salt.
+
+*Default:* ``1`` (no extra threads)
+
+.. _zone_dnssec-signing:
+
+dnssec-signing
+--------------
+
+If enabled, automatic DNSSEC signing for the zone is turned on.
+
+*Default:* ``off``
+
+.. _zone_dnssec-validation:
+
+dnssec-validation
+-----------------
+
+If enabled, the zone contents are validated for being correctly signed
+(including NSEC/NSEC3 chain) with DNSSEC signatures every time the zone
+is loaded or changed (including AXFR/IXFR).
+
+When the validation fails, the zone being loaded or update being applied
+is cancelled with an error, and either none or previous zone state is published.
+
+List of DNSSEC checks:
+
+- Every zone RRSet is correctly signed by at least one present DNSKEY.
+- DNSKEY RRSet is signed by KSK.
+- NSEC(3) RR exists for each name (unless opt-out) with correct bitmap.
+- Every NSEC(3) RR is linked to the lexicographically next one.
+
+The validation is not affected by :ref:`zone_dnssec-policy` configuration,
+except for :ref:`policy_signing-threads` option, which specifies the number
+of threads for parallel validation.
+
+*Default:* not set
+
+.. NOTE::
+
+ Redundant or garbage NSEC3 records are ignored.
+
+ This mode is not compatible with :ref:`zone_dnssec-signing`.
+
+.. _zone_dnssec-policy:
+
+dnssec-policy
+-------------
+
+A :ref:`reference<policy_id>` to DNSSEC signing policy.
+
+*Default:* an imaginary policy with all default values
+
+.. NOTE::
+ A configured policy called "default" won't be used unless explicitly referenced.
+
+.. _zone_ds-push:
+
+ds-push
+-------
+
+Per zone configuration of :ref:`policy_ds-push`. This option overrides possible
+per policy option.
+
+*Default:* not set
+
+.. _zone_zonemd-verify:
+
+zonemd-verify
+-------------
+
+On each zone load/update, verify that ZONEMD is present in the zone and valid.
+
+.. NOTE::
+ Zone digest calculation may take much time and CPU on large zones.
+
+*Default:* ``off``
+
+.. _zone_zonemd-generate:
+
+zonemd-generate
+---------------
+
+On each zone update, calculate ZONEMD and put it into the zone.
+
+Possible values:
+
+- ``none`` – No action regarding ZONEMD.
+- ``zonemd-sha384`` – Generate ZONEMD using SHA384 algorithm.
+- ``zonemd-sha512`` – Generate ZONEMD using SHA512 algorithm.
+- ``remove`` – Remove any ZONEMD from the zone apex.
+
+*Default:* ``none``
+
+.. _zone_serial-policy:
+
+serial-policy
+-------------
+
+Specifies how the zone serial is updated after a dynamic update or
+automatic DNSSEC signing. If the serial is changed by the dynamic update,
+no change is made.
+
+Possible values:
+
+- ``increment`` – The serial is incremented according to serial number arithmetic.
+- ``unixtime`` – The serial is set to the current unix time.
+- ``dateserial`` – The 10-digit serial (YYYYMMDDnn) is incremented, the first
+ 8 digits match the current iso-date.
+
+.. NOTE::
+ If the resulting serial for ``unixtime`` or ``dateserial`` is lower than or
+ equal to the current serial (this happens e.g. when migrating from other policy or
+ frequent updates), the serial is incremented instead.
+
+ To avoid user confusion, use ``dateserial`` only if you expect at most
+ 100 updates per day per zone and ``unixtime`` only if you expect at most
+ one update per second per zone.
+
+ Generated catalog zones use ``unixtime`` only.
+
+*Default:* ``increment`` (``unixtime`` for generated catalog zones)
+
+.. _zone_refresh-min-interval:
+
+refresh-min-interval
+--------------------
+
+Forced minimum zone refresh interval (in seconds) to avoid flooding primary server.
+
+*Minimum:* ``2``
+
+*Default:* ``2``
+
+.. _zone_refresh-max-interval:
+
+refresh-max-interval
+--------------------
+
+Forced maximum zone refresh interval (in seconds).
+
+*Default:* not set
+
+.. _zone_retry-min-interval:
+
+retry-min-interval
+------------------
+
+Forced minimum zone retry interval (in seconds) to avoid flooding primary server.
+
+*Minimum:* ``1``
+
+*Default:* ``1``
+
+.. _zone_retry-max-interval:
+
+retry-max-interval
+------------------
+
+Forced maximum zone retry interval (in seconds).
+
+*Default:* not set
+
+.. _zone_expire-min-interval:
+
+expire-min-interval
+-------------------
+
+Forced minimum zone expire interval (in seconds) to avoid flooding primary server.
+
+*Minimum:* ``3``
+
+*Default:* ``3``
+
+.. _zone_expire-max-interval:
+
+expire-max-interval
+-------------------
+
+Forced maximum zone expire interval (in seconds).
+
+*Default:* not set
+
+.. _zone_catalog-role:
+
+catalog-role
+------------
+
+Trigger zone catalog feature. Possible values:
+
+- ``none`` – Not a catalog zone.
+- ``interpret`` – A catalog zone which is loaded from a zone file or XFR,
+ and member zones shall be configured based on its contents.
+- ``generate`` – A catalog zone whose contents are generated according to
+ assigned member zones.
+- ``member`` – A member zone that is assigned to one generated catalog zone.
+
+*Default:* ``none``
+
+.. _zone_catalog-template:
+
+catalog-template
+----------------
+
+For the catalog member zones, the specified configuration template will be applied.
+
+Multiple catalog templates may be defined. The first one is used unless the member zone
+has the *group* property defined, matching another catalog template.
+
+.. NOTE::
+ This option must be set if and only if :ref:`zone_catalog-role` is *interpret*.
+
+ Nested catalog zones aren't supported. Therefore catalog templates can't use
+ :ref:`zone_catalog-template`, :ref:`zone_catalog-role`, :ref:`zone_catalog-zone`,
+ and :ref:`zone_catalog-group` options.
+
+*Default:* not set
+
+.. _zone_catalog-zone:
+
+catalog-zone
+------------
+
+Assign this member zone to specified generated catalog zone.
+
+.. NOTE::
+ This option must be set if and only if :ref:`zone_catalog-role` is *member*.
+
+ The referenced catalog zone must exist and have :ref:`zone_catalog-role` set to *generate*.
+
+*Default:* not set
+
+.. _zone_catalog-group:
+
+catalog-group
+-------------
+
+Assign this member zone to specified catalog group (configuration template).
+
+.. NOTE::
+ This option has effect if and only if :ref:`zone_catalog-role` is *member*.
+
+*Default:* not set
+
+.. _zone_module:
+
+module
+------
+
+An ordered list of references to query modules in the form of *module_name* or
+*module_name/module_id*. These modules apply only to the current zone queries.
+
+*Default:* not set
diff --git a/doc/requirements.rst b/doc/requirements.rst
new file mode 100644
index 0000000..ed114b8
--- /dev/null
+++ b/doc/requirements.rst
@@ -0,0 +1,115 @@
+.. highlight:: none
+.. _Requirements:
+
+************
+Requirements
+************
+
+Hardware
+========
+
+Knot DNS requirements are not very demanding for typical
+installations, and a commodity server or a virtual solution will be
+sufficient in most cases.
+
+However, please note that there are some scenarios that will require
+administrator's attention and some testing of exact requirements before
+deploying Knot DNS to a production environment. These cases include
+deployment for a large number of zones (DNS hosting), large number
+of records in one or more zones (TLD), or large number of requests.
+
+CPU requirements
+----------------
+
+The server scales with processing power and also with the number of
+available cores/CPUs. Enabling Hyper-threading is convenient if supported.
+
+There is no lower bound on the CPU requirements, but it should support
+memory barriers and atomic instructions (i586 and newer).
+
+Network card
+------------
+
+The best results have been achieved with multi-queue network cards. The
+number of multi-queues should equal the total number of CPU cores (with
+Hyper-threading enabled).
+
+Memory requirements
+-------------------
+
+The server implementation focuses on performance and thus can be quite
+memory demanding. The rough estimate for memory requirements is
+3 times the size of the zone in the plain-text format. Again this is only
+an estimate and you are advised to do your own measurements before
+deploying Knot DNS to production.
+
+.. NOTE::
+ To ensure uninterrupted serving of the zone, Knot DNS
+ employs the Read-Copy-Update mechanism instead of locking and thus
+ requires twice the amount of memory for the duration of incoming
+ transfers.
+
+Operating system
+================
+
+Knot DNS itself is written in a portable way and can be compiled
+and run on most UNIX-like systems, such as Linux, \*BSD, and macOS.
+
+Required libraries
+==================
+
+Knot DNS requires a few libraries to be available:
+
+* libedit
+* gnutls >= 3.3
+* liburcu >= 0.5.4
+* lmdb >= 0.9.15
+
+.. NOTE::
+ The LMDB library is included with Knot DNS source code. However, linking
+ with the system library is preferred.
+
+Optional libraries
+==================
+
+International Domain Names support (IDNA2008 or IDNA2003) in :doc:`kdig<man_kdig>`:
+
+* libidn2 (or libidn)
+
+Systemd's startup notification mechanism and journald logging:
+
+* libsystemd
+
+Dnstap support in :doc:`kdig<man_kdig>` or module :ref:`dnstap<mod-dnstap>`:
+
+* fstrm (and protobuf-c if building from source code)
+
+Linux :manpage:`capabilities(7)` support, which allows the server to be started
+as a non-root user/group, binding to privileged ports (53), and giving up all
+its capabilities, resulting in a completely unprivileged process:
+
+* libcap-ng >= 0.6.4
+
+MaxMind database for **geodb** support in module :ref:`geoip<mod-geoip>`:
+
+* libmaxminddb0
+
+DNS-over-HTTPS (DoH) support in :doc:`kdig<man_kdig>`:
+
+* libnghttp2
+
+The :ref:`XDP functionality <Mode XDP>` and :doc:`kxdpgun <man_kxdpgun>`
+tool. These are only supported on Linux operating systems. See the chapter
+:ref:`Mode XDP <Mode XDP_pre-requisites>` for software and hardware
+recommendations.
+
+* libbpf >= 0.0.6 (or embedded one via `--enable-xdp`)
+* libxdp (if libbpf >= 1.0)
+* libmnl (for kxdpgun)
+
+DNS-over-QUIC (DoQ) support in :doc:`knotd<man_knotd>`, :doc:`kxdpgun<man_kxdpgun>`,
+and :doc:`kdig<man_kdig>`:
+
+* libngtcp2 >= 0.13.0 (or embedded one via `--enable-quic`)
+* gnutls >= 3.7.2
+* :ref:`Mode XDP` (for knotd and kxdpgun)
diff --git a/doc/theme_html/static/admon_caution_48.png b/doc/theme_html/static/admon_caution_48.png
new file mode 100644
index 0000000..9016ec0
--- /dev/null
+++ b/doc/theme_html/static/admon_caution_48.png
Binary files differ
diff --git a/doc/theme_html/static/admon_important_48.png b/doc/theme_html/static/admon_important_48.png
new file mode 100644
index 0000000..7021f4c
--- /dev/null
+++ b/doc/theme_html/static/admon_important_48.png
Binary files differ
diff --git a/doc/theme_html/static/admon_note_48.png b/doc/theme_html/static/admon_note_48.png
new file mode 100644
index 0000000..e72e336
--- /dev/null
+++ b/doc/theme_html/static/admon_note_48.png
Binary files differ
diff --git a/doc/theme_html/static/admon_tip_48.png b/doc/theme_html/static/admon_tip_48.png
new file mode 100644
index 0000000..f679193
--- /dev/null
+++ b/doc/theme_html/static/admon_tip_48.png
Binary files differ
diff --git a/doc/theme_html/static/admon_warning_48.png b/doc/theme_html/static/admon_warning_48.png
new file mode 100644
index 0000000..2c338d5
--- /dev/null
+++ b/doc/theme_html/static/admon_warning_48.png
Binary files differ
diff --git a/doc/theme_html/static/admons.css b/doc/theme_html/static/admons.css
new file mode 100644
index 0000000..d9f3406
--- /dev/null
+++ b/doc/theme_html/static/admons.css
@@ -0,0 +1,69 @@
+/* === Style for admonitions === */
+
+/* Settings from 'basic' theme (modified only) */
+
+div.admonition {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ padding: 7px;
+ padding-left: 54px;/* based on icon size */
+ padding-right: 24px;/* based on icon size */
+}
+
+/* Settings from 'classic' theme (modified only) */
+
+div.admonition p {
+ display: block;
+}
+
+p.admonition-title {
+ padding-bottom: 11px;/* based on icon size */
+ padding-top: 11px;/* based on icon size */
+}
+
+p.admonition-title:after {
+ content: none; /* do not add colon */
+}
+
+/* Icon settings for all admonitions */
+div.admonition {
+ /* background-image: url('abstract_admon_48.png'); */
+ background-repeat: no-repeat;
+ background-position: 2px 2px;
+}
+
+/* Specific admonitions */
+div.admonition-todo {
+ background-color: #CAE2B6;
+ border: solid 1px #439427;
+}
+
+div.warning {
+ background-image: url('admon_warning_48.png');
+ background-color: #FFE4E4;
+ border: solid 3px #990D1C;
+}
+
+div.caution {
+ background-image: url('admon_caution_48.png');
+ background-color: #FFE4E4;
+ border: solid 1px #990D1C;
+}
+
+div.important {
+ background-image: url('admon_important_48.png');
+ background-color: #FBECC8;
+ border: solid 1px #D68612;
+}
+
+div.note {
+ background-image: url('admon_note_48.png');
+ background-color: white;
+ border: solid 1px #D1D3D4;
+}
+
+div.tip {
+ background-image: url('admon_tip_48.png');
+ background-color: #F2E4FD;
+ border: solid 1px #D1C2E6;
+}
diff --git a/doc/theme_html/static/main.css b/doc/theme_html/static/main.css
new file mode 100644
index 0000000..d7eb5d1
--- /dev/null
+++ b/doc/theme_html/static/main.css
@@ -0,0 +1,6 @@
+@import url("nature.css");
+
+/* Addendum for admonitions */
+@import url("admons.css");
+
+/* Other overrides here */
diff --git a/doc/theme_html/theme.conf b/doc/theme_html/theme.conf
new file mode 100644
index 0000000..eb8c607
--- /dev/null
+++ b/doc/theme_html/theme.conf
@@ -0,0 +1,4 @@
+[theme]
+inherit = nature
+stylesheet = main.css
+pygments_style = sphinx
diff --git a/doc/troubleshooting.rst b/doc/troubleshooting.rst
new file mode 100644
index 0000000..a07dd12
--- /dev/null
+++ b/doc/troubleshooting.rst
@@ -0,0 +1,110 @@
+.. highlight:: console
+.. _Troubleshooting:
+
+***************
+Troubleshooting
+***************
+
+First of all, check the logs. Enabling at least the ``warning`` message
+severity may help you to identify some problems. See the :ref:`log section`
+for details.
+
+.. _Submitting a bugreport:
+
+Reporting bugs
+==============
+
+If you are unable to solve the problem by yourself, you can submit a
+bugreport to the Knot DNS developers. For security or sensitive issues
+contact the developers directly on
+`knot-dns@labs.nic.cz <mailto:knot-dns@labs.nic.cz>`_.
+All other bugs and questions may be directed to the public Knot DNS users
+mailing list
+(`knot-dns-users@lists.nic.cz <mailto:knot-dns-users@lists.nic.cz>`_) or
+may be entered into the
+`issue tracking system <https://gitlab.nic.cz/knot/knot-dns/issues>`_.
+
+Before anything else, please try to answer the following questions:
+
+* Has it been working?
+* What has changed? System configuration, software updates, network
+ configuration, firewall rules modification, hardware replacement, etc.
+
+The bugreport should contain the answers for the previous questions and in
+addition at least the following information:
+
+* Knot DNS version and type of installation (distribution package, from source,
+ etc.)
+* Operating system, platform, kernel version
+* Relevant basic hardware information (processor, amount of memory, available
+ network devices, etc.)
+* Description of the bug
+* Log output with the highest verbosity (category ``any``, severity ``debug``)
+* Steps to reproduce the bug (if known)
+* Backtrace (if the bug caused a crash or a hang; see the next section)
+
+If possible, please provide a minimal configuration file and zone files which
+can be used to reproduce the bug.
+
+.. _Generating backtrace:
+
+Generating backtrace
+====================
+
+Backtrace carries basic information about the state of the program and how
+the program got where it is. It helps determining the location of the bug in
+the source code.
+
+If you run Knot DNS from distribution packages, make sure the debugging
+symbols for the package are installed. The symbols are usually distributed
+in a separate package.
+
+There are several ways to get the backtrace. One possible way is to extract
+the backtrace from a core dump file. Core dump is a memory snapshot generated
+by the operating system when a process crashes. The generating of core dumps
+must be usually enabled::
+
+ $ ulimit -c unlimited # Enable unlimited core dump size
+ $ knotd ... # Reproduce the crash
+ ...
+ $ gdb knotd <core-dump-file> # Start gdb on the core dump
+ (gdb) info threads # Get a summary of all threads
+ (gdb) thread apply all bt full # Extract backtrace from all threads
+ (gdb) quit
+
+To save the backtrace into a file, the following GDB commands can be used::
+
+ (gdb) set pagination off
+ (gdb) set logging file backtrace.txt
+ (gdb) set logging on
+ (gdb) info threads
+ (gdb) thread apply all bt full
+ (gdb) set logging off
+
+To generate a core dump of a running process, the `gcore` utility can be used::
+
+ $ gcore -o <output-file> $(pidof knotd)
+
+Please note that core dumps can be intercepted by an error-collecting system
+service (systemd-coredump, ABRT, Apport, etc.). If you are using such a service,
+consult its documentation about core dump retrieval.
+
+If the error is reproducible, it is also possible to start and inspect the
+server directly in the debugger::
+
+ $ gdb --args knotd -c /etc/knot.conf
+ (gdb) run
+ ...
+
+Alternatively, the debugger can be attached to a running server
+process. This is generally useful when troubleshooting a stuck process::
+
+ $ knotd ...
+ $ gdb --pid $(pidof knotd)
+ (gdb) continue
+ ...
+
+If you fail to get a backtrace of a running process using the previous method,
+you may try the single-purpose ``pstack`` utility::
+
+ $ pstack $(pidof knotd) > backtrace.txt
diff --git a/doc/utilities.rst b/doc/utilities.rst
new file mode 100644
index 0000000..392115d
--- /dev/null
+++ b/doc/utilities.rst
@@ -0,0 +1,24 @@
+.. highlight:: none
+.. _Utilities:
+
+Utilities
+=========
+
+Knot DNS comes with a few DNS client utilities and a few utilities to control
+the server. This section collects manual pages for all provided binaries:
+
+.. toctree::
+ :titlesonly:
+
+ man_knotd
+ man_knotc
+ man_keymgr
+ man_kjournalprint
+ man_kcatalogprint
+ man_kzonecheck
+ man_kzonesign
+ man_kdig
+ man_khost
+ man_knsec3hash
+ man_knsupdate
+ man_kxdpgun
diff --git a/install-sh b/install-sh
new file mode 100755
index 0000000..ec298b5
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,541 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab=' '
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -p pass -p to $cpprog.
+ -s $stripprog installed files.
+ -S SUFFIX attempt to back up existing files, with suffix SUFFIX.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -p) cpprog="$cpprog -p";;
+
+ -s) stripcmd=$stripprog;;
+
+ -S) backupsuffix="$2"
+ shift;;
+
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
+
+ -T) is_target_a_directory=never;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call 'install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
+if test -z "$dir_arg"; then
+ do_exit='(exit $ret); exit $ret'
+ trap "ret=129; $do_exit" 1
+ trap "ret=130; $do_exit" 2
+ trap "ret=141; $do_exit" 13
+ trap "ret=143; $do_exit" 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names problematic for 'test' and other utilities.
+ case $src in
+ -* | [=\(\)!]) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ # Don't chown directories that already exist.
+ if test $dstdir_status = 0; then
+ chowncmd=""
+ fi
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+ dst=$dst_arg
+
+ # If destination is a directory, append the input filename.
+ if test -d "$dst"; then
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dstbase=`basename "$src"`
+ case $dst in
+ */) dst=$dst$dstbase;;
+ *) dst=$dst/$dstbase;;
+ esac
+ dstdir_status=0
+ else
+ dstdir=`dirname "$dst"`
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ case $dstdir in
+ */) dstdirslash=$dstdir;;
+ *) dstdirslash=$dstdir/;;
+ esac
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ # The $RANDOM variable is not portable (e.g., dash). Use it
+ # here however when possible just to lower collision chance.
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+ trap '
+ ret=$?
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+ exit $ret
+ ' 0
+
+ # Because "mkdir -p" follows existing symlinks and we likely work
+ # directly in world-writeable /tmp, make sure that the '$tmpdir'
+ # directory is successfully created first before we actually test
+ # 'mkdir -p'.
+ if (umask $mkdir_umask &&
+ $mkdirprog $mkdir_mode "$tmpdir" &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ test_tmpdir="$tmpdir/a"
+ ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+ fi
+ trap '' 0;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ oIFS=$IFS
+ IFS=/
+ set -f
+ set fnord $dstdir
+ shift
+ set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=${dstdirslash}_inst.$$_
+ rmtmp=${dstdirslash}_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask &&
+ { test -z "$stripcmd" || {
+ # Create $dsttmp read-write so that cp doesn't create it read-only,
+ # which would cause strip to fail.
+ if test -z "$doit"; then
+ : >"$dsttmp" # No need to fork-exec 'touch'.
+ else
+ $doit touch "$dsttmp"
+ fi
+ }
+ } &&
+ $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ set +f &&
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # If $backupsuffix is set, and the file being installed
+ # already exists, attempt a backup. Don't worry if it fails,
+ # e.g., if mv doesn't support -f.
+ if test -n "$backupsuffix" && test -f "$dst"; then
+ $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+ fi
+
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/ltmain.sh b/ltmain.sh
new file mode 100755
index 0000000..21e5e07
--- /dev/null
+++ b/ltmain.sh
@@ -0,0 +1,11251 @@
+#! /bin/sh
+## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
+## by inline-source v2014-01-03.01
+
+# libtool (GNU libtool) 2.4.6
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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/>.
+
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION="2.4.6 Debian-2.4.6-15"
+package_revision=2.4.6
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Run './libtool --help' for help with using this script from the
+# command line.
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# After configure completes, it has a better idea of some of the
+# shell tools we need than the defaults used by the functions shared
+# with bootstrap, so set those here where they can still be over-
+# ridden by the user, but otherwise take precedence.
+
+: ${AUTOCONF="autoconf"}
+: ${AUTOMAKE="automake"}
+
+
+## -------------------------- ##
+## Source external libraries. ##
+## -------------------------- ##
+
+# Much of our low-level functionality needs to be sourced from external
+# libraries, which are installed to $pkgauxdir.
+
+# Set a version string for this script.
+scriptversion=2015-01-20.17; # UTC
+
+# General shell script boiler plate, and helper functions.
+# Written by Gary V. Vaughan, 2004
+
+# Copyright (C) 2004-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# 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.
+
+# As a special exception to the GNU General Public License, if you distribute
+# this file as part of a program or library that is built using GNU Libtool,
+# you may include this file under the same distribution terms that you use
+# for the rest of that program.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNES 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/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Evaluate this file near the top of your script to gain access to
+# the functions and variables defined here:
+#
+# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
+#
+# If you need to override any of the default environment variable
+# settings, do that before evaluating this file.
+
+
+## -------------------- ##
+## Shell normalisation. ##
+## -------------------- ##
+
+# Some shells need a little help to be as Bourne compatible as possible.
+# Before doing anything else, make sure all that help has been provided!
+
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
+fi
+
+# NLS nuisances: We save the old values in case they are required later.
+_G_user_locale=
+_G_safe_locale=
+for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test set = \"\${$_G_var+set}\"; then
+ save_$_G_var=\$$_G_var
+ $_G_var=C
+ export $_G_var
+ _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
+ _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
+ fi"
+done
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Make sure IFS has a sensible default
+sp=' '
+nl='
+'
+IFS="$sp $nl"
+
+# There are apparently some retarded systems that use ';' as a PATH separator!
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+
+## ------------------------- ##
+## Locate command utilities. ##
+## ------------------------- ##
+
+
+# func_executable_p FILE
+# ----------------------
+# Check that FILE is an executable regular file.
+func_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+}
+
+
+# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
+# --------------------------------------------
+# Search for either a program that responds to --version with output
+# containing "GNU", or else returned by CHECK_FUNC otherwise, by
+# trying all the directories in PATH with each of the elements of
+# PROGS_LIST.
+#
+# CHECK_FUNC should accept the path to a candidate program, and
+# set $func_check_prog_result if it truncates its output less than
+# $_G_path_prog_max characters.
+func_path_progs ()
+{
+ _G_progs_list=$1
+ _G_check_func=$2
+ _G_PATH=${3-"$PATH"}
+
+ _G_path_prog_max=0
+ _G_path_prog_found=false
+ _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
+ for _G_dir in $_G_PATH; do
+ IFS=$_G_save_IFS
+ test -z "$_G_dir" && _G_dir=.
+ for _G_prog_name in $_G_progs_list; do
+ for _exeext in '' .EXE; do
+ _G_path_prog=$_G_dir/$_G_prog_name$_exeext
+ func_executable_p "$_G_path_prog" || continue
+ case `"$_G_path_prog" --version 2>&1` in
+ *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
+ *) $_G_check_func $_G_path_prog
+ func_path_progs_result=$func_check_prog_result
+ ;;
+ esac
+ $_G_path_prog_found && break 3
+ done
+ done
+ done
+ IFS=$_G_save_IFS
+ test -z "$func_path_progs_result" && {
+ echo "no acceptable sed could be found in \$PATH" >&2
+ exit 1
+ }
+}
+
+
+# We want to be able to use the functions in this file before configure
+# has figured out where the best binaries are kept, which means we have
+# to search for them ourselves - except when the results are already set
+# where we skip the searches.
+
+# Unless the user overrides by setting SED, search the path for either GNU
+# sed, or the sed that truncates its output the least.
+test -z "$SED" && {
+ _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for _G_i in 1 2 3 4 5 6 7; do
+ _G_sed_script=$_G_sed_script$nl$_G_sed_script
+ done
+ echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
+ _G_sed_script=
+
+ func_check_prog_sed ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo '' >> conftest.nl
+ "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
+ rm -f conftest.sed
+ SED=$func_path_progs_result
+}
+
+
+# Unless the user overrides by setting GREP, search the path for either GNU
+# grep, or the grep that truncates its output the least.
+test -z "$GREP" && {
+ func_check_prog_grep ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ _G_path_prog_max=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo 'GREP' >> conftest.nl
+ "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
+ GREP=$func_path_progs_result
+}
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# All uppercase variable names are used for environment variables. These
+# variables can be overridden by the user before calling a script that
+# uses them if a suitable command of that name is not already available
+# in the command search PATH.
+
+: ${CP="cp -f"}
+: ${ECHO="printf %s\n"}
+: ${EGREP="$GREP -E"}
+: ${FGREP="$GREP -F"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+
+
+## -------------------- ##
+## Useful sed snippets. ##
+## -------------------- ##
+
+sed_dirname='s|/[^/]*$||'
+sed_basename='s|^.*/||'
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Same as above, but do not quote variable references.
+sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
+
+# Sed substitution that converts a w32 file name or path
+# that contains forward slashes, into one that contains
+# (escaped) backslashes. A very naive implementation.
+sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-'\' parameter expansions in output of sed_double_quote_subst that
+# were '\'-ed in input to the same. If an odd number of '\' preceded a
+# '$' in input to sed_double_quote_subst, that '$' was protected from
+# expansion. Since each input '\' is now two '\'s, look for any number
+# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'.
+_G_bs='\\'
+_G_bs2='\\\\'
+_G_bs4='\\\\\\\\'
+_G_dollar='\$'
+sed_double_backslash="\
+ s/$_G_bs4/&\\
+/g
+ s/^$_G_bs2$_G_dollar/$_G_bs&/
+ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
+ s/\n//g"
+
+
+## ----------------- ##
+## Global variables. ##
+## ----------------- ##
+
+# Except for the global variables explicitly listed below, the following
+# functions in the '^func_' namespace, and the '^require_' namespace
+# variables initialised in the 'Resource management' section, sourcing
+# this file will not pollute your global namespace with anything
+# else. There's no portable way to scope variables in Bourne shell
+# though, so actually running these functions will sometimes place
+# results into a variable named after the function, and often use
+# temporary variables in the '^_G_' namespace. If you are careful to
+# avoid using those namespaces casually in your sourcing script, things
+# should continue to work as you expect. And, of course, you can freely
+# overwrite any of the functions or variables defined here before
+# calling anything to customize them.
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+# Allow overriding, eg assuming that you follow the convention of
+# putting '$debug_cmd' at the start of all your functions, you can get
+# bash to show function call trace with:
+#
+# debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+debug_cmd=${debug_cmd-":"}
+exit_cmd=:
+
+# By convention, finish your script with:
+#
+# exit $exit_status
+#
+# so that you can set exit_status to non-zero if you want to indicate
+# something went wrong during execution without actually bailing out at
+# the point of failure.
+exit_status=$EXIT_SUCCESS
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath=$0
+
+# The name of this program.
+progname=`$ECHO "$progpath" |$SED "$sed_basename"`
+
+# Make sure we have an absolute progpath for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
+ progdir=`cd "$progdir" && pwd`
+ progpath=$progdir/$progname
+ ;;
+ *)
+ _G_IFS=$IFS
+ IFS=${PATH_SEPARATOR-:}
+ for progdir in $PATH; do
+ IFS=$_G_IFS
+ test -x "$progdir/$progname" && break
+ done
+ IFS=$_G_IFS
+ test -n "$progdir" || progdir=`pwd`
+ progpath=$progdir/$progname
+ ;;
+esac
+
+
+## ----------------- ##
+## Standard options. ##
+## ----------------- ##
+
+# The following options affect the operation of the functions defined
+# below, and should be set appropriately depending on run-time para-
+# meters passed on the command line.
+
+opt_dry_run=false
+opt_quiet=false
+opt_verbose=false
+
+# Categories 'all' and 'none' are always available. Append any others
+# you will pass as the first argument to func_warning from your own
+# code.
+warning_categories=
+
+# By default, display warnings according to 'opt_warning_types'. Set
+# 'warning_func' to ':' to elide all warnings, or func_fatal_error to
+# treat the next displayed warning as a fatal error.
+warning_func=func_warn_and_continue
+
+# Set to 'all' to display all warnings, 'none' to suppress all
+# warnings, or a space delimited list of some subset of
+# 'warning_categories' to display only the listed warnings.
+opt_warning_types=all
+
+
+## -------------------- ##
+## Resource management. ##
+## -------------------- ##
+
+# This section contains definitions for functions that each ensure a
+# particular resource (a file, or a non-empty configuration variable for
+# example) is available, and if appropriate to extract default values
+# from pertinent package files. Call them using their associated
+# 'require_*' variable to ensure that they are executed, at most, once.
+#
+# It's entirely deliberate that calling these functions can set
+# variables that don't obey the namespace limitations obeyed by the rest
+# of this file, in order that that they be as useful as possible to
+# callers.
+
+
+# require_term_colors
+# -------------------
+# Allow display of bold text on terminals that support it.
+require_term_colors=func_require_term_colors
+func_require_term_colors ()
+{
+ $debug_cmd
+
+ test -t 1 && {
+ # COLORTERM and USE_ANSI_COLORS environment variables take
+ # precedence, because most terminfo databases neglect to describe
+ # whether color sequences are supported.
+ test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
+
+ if test 1 = "$USE_ANSI_COLORS"; then
+ # Standard ANSI escape sequences
+ tc_reset=''
+ tc_bold=''; tc_standout=''
+ tc_red=''; tc_green=''
+ tc_blue=''; tc_cyan=''
+ else
+ # Otherwise trust the terminfo database after all.
+ test -n "`tput sgr0 2>/dev/null`" && {
+ tc_reset=`tput sgr0`
+ test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
+ tc_standout=$tc_bold
+ test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
+ test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
+ test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
+ test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
+ test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
+ }
+ fi
+ }
+
+ require_term_colors=:
+}
+
+
+## ----------------- ##
+## Function library. ##
+## ----------------- ##
+
+# This section contains a variety of useful functions to call in your
+# scripts. Take note of the portable wrappers for features provided by
+# some modern shells, which will fall back to slower equivalents on
+# less featureful shells.
+
+
+# func_append VAR VALUE
+# ---------------------
+# Append VALUE onto the existing contents of VAR.
+
+ # We should try to minimise forks, especially on Windows where they are
+ # unreasonably slow, so skip the feature probes when bash or zsh are
+ # being used:
+ if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
+ : ${_G_HAVE_ARITH_OP="yes"}
+ : ${_G_HAVE_XSI_OPS="yes"}
+ # The += operator was introduced in bash 3.1
+ case $BASH_VERSION in
+ [12].* | 3.0 | 3.0*) ;;
+ *)
+ : ${_G_HAVE_PLUSEQ_OP="yes"}
+ ;;
+ esac
+ fi
+
+ # _G_HAVE_PLUSEQ_OP
+ # Can be empty, in which case the shell is probed, "yes" if += is
+ # useable or anything else if it does not work.
+ test -z "$_G_HAVE_PLUSEQ_OP" \
+ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
+ && _G_HAVE_PLUSEQ_OP=yes
+
+if test yes = "$_G_HAVE_PLUSEQ_OP"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_append ()
+ {
+ $debug_cmd
+
+ eval "$1+=\$2"
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_append ()
+ {
+ $debug_cmd
+
+ eval "$1=\$$1\$2"
+ }
+fi
+
+
+# func_append_quoted VAR VALUE
+# ----------------------------
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+if test yes = "$_G_HAVE_PLUSEQ_OP"; then
+ eval 'func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1+=\\ \$func_quote_for_eval_result"
+ }'
+else
+ func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1=\$$1\\ \$func_quote_for_eval_result"
+ }
+fi
+
+
+# func_append_uniq VAR VALUE
+# --------------------------
+# Append unique VALUE onto the existing contents of VAR, assuming
+# entries are delimited by the first character of VALUE. For example:
+#
+# func_append_uniq options " --another-option option-argument"
+#
+# will only append to $options if " --another-option option-argument "
+# is not already present somewhere in $options already (note spaces at
+# each end implied by leading space in second argument).
+func_append_uniq ()
+{
+ $debug_cmd
+
+ eval _G_current_value='`$ECHO $'$1'`'
+ _G_delim=`expr "$2" : '\(.\)'`
+
+ case $_G_delim$_G_current_value$_G_delim in
+ *"$2$_G_delim"*) ;;
+ *) func_append "$@" ;;
+ esac
+}
+
+
+# func_arith TERM...
+# ------------------
+# Set func_arith_result to the result of evaluating TERMs.
+ test -z "$_G_HAVE_ARITH_OP" \
+ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
+ && _G_HAVE_ARITH_OP=yes
+
+if test yes = "$_G_HAVE_ARITH_OP"; then
+ eval 'func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=$(( $* ))
+ }'
+else
+ func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=`expr "$@"`
+ }
+fi
+
+
+# func_basename FILE
+# ------------------
+# Set func_basename_result to FILE with everything up to and including
+# the last / stripped.
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ # If this shell supports suffix pattern removal, then use it to avoid
+ # forking. Hide the definitions single quotes in case the shell chokes
+ # on unsupported syntax...
+ _b='func_basename_result=${1##*/}'
+ _d='case $1 in
+ */*) func_dirname_result=${1%/*}$2 ;;
+ * ) func_dirname_result=$3 ;;
+ esac'
+
+else
+ # ...otherwise fall back to using sed.
+ _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
+ _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"`
+ if test "X$func_dirname_result" = "X$1"; then
+ func_dirname_result=$3
+ else
+ func_append func_dirname_result "$2"
+ fi'
+fi
+
+eval 'func_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+}'
+
+
+# func_dirname FILE APPEND NONDIR_REPLACEMENT
+# -------------------------------------------
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+eval 'func_dirname ()
+{
+ $debug_cmd
+
+ '"$_d"'
+}'
+
+
+# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
+# --------------------------------------------------------
+# Perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# For efficiency, we do not delegate to the functions above but instead
+# duplicate the functionality here.
+eval 'func_dirname_and_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+ '"$_d"'
+}'
+
+
+# func_echo ARG...
+# ----------------
+# Echo program name prefixed message.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_echo_all ARG...
+# --------------------
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+
+# func_echo_infix_1 INFIX ARG...
+# ------------------------------
+# Echo program name, followed by INFIX on the first line, with any
+# additional lines not showing INFIX.
+func_echo_infix_1 ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ _G_infix=$1; shift
+ _G_indent=$_G_infix
+ _G_prefix="$progname: $_G_infix: "
+ _G_message=$*
+
+ # Strip color escape sequences before counting printable length
+ for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
+ do
+ test -n "$_G_tc" && {
+ _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
+ _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
+ }
+ done
+ _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes
+
+ func_echo_infix_1_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_infix_1_IFS
+ $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
+ _G_prefix=$_G_indent
+ done
+ IFS=$func_echo_infix_1_IFS
+}
+
+
+# func_error ARG...
+# -----------------
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2
+}
+
+
+# func_fatal_error ARG...
+# -----------------------
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ $debug_cmd
+
+ func_error "$*"
+ exit $EXIT_FAILURE
+}
+
+
+# func_grep EXPRESSION FILENAME
+# -----------------------------
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $debug_cmd
+
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_len STRING
+# ---------------
+# Set func_len_result to the length of STRING. STRING may not
+# start with a hyphen.
+ test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=${#1}
+ }'
+else
+ func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+ }
+fi
+
+
+# func_mkdir_p DIRECTORY-PATH
+# ---------------------------
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ $debug_cmd
+
+ _G_directory_path=$1
+ _G_dir_list=
+
+ if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
+
+ # Protect directory names starting with '-'
+ case $_G_directory_path in
+ -*) _G_directory_path=./$_G_directory_path ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$_G_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ _G_dir_list=$_G_directory_path:$_G_dir_list
+
+ # If the last portion added has no slash in it, the list is done
+ case $_G_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
+ done
+ _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
+
+ func_mkdir_p_IFS=$IFS; IFS=:
+ for _G_dir in $_G_dir_list; do
+ IFS=$func_mkdir_p_IFS
+ # mkdir can fail with a 'File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$_G_dir" 2>/dev/null || :
+ done
+ IFS=$func_mkdir_p_IFS
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$_G_directory_path" || \
+ func_fatal_error "Failed to create '$1'"
+ fi
+}
+
+
+# func_mktempdir [BASENAME]
+# -------------------------
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, BASENAME is the basename for that directory.
+func_mktempdir ()
+{
+ $debug_cmd
+
+ _G_template=${TMPDIR-/tmp}/${1-$progname}
+
+ if test : = "$opt_dry_run"; then
+ # Return a directory name, but don't create it in dry-run mode
+ _G_tmpdir=$_G_template-$$
+ else
+
+ # If mktemp works, use that first and foremost
+ _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$_G_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ _G_tmpdir=$_G_template-${RANDOM-0}$$
+
+ func_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$_G_tmpdir"
+ umask $func_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$_G_tmpdir" || \
+ func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
+ fi
+
+ $ECHO "$_G_tmpdir"
+}
+
+
+# func_normal_abspath PATH
+# ------------------------
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+func_normal_abspath ()
+{
+ $debug_cmd
+
+ # These SED scripts presuppose an absolute path with a trailing slash.
+ _G_pathcar='s|^/\([^/]*\).*$|\1|'
+ _G_pathcdr='s|^/[^/]*||'
+ _G_removedotparts=':dotsl
+ s|/\./|/|g
+ t dotsl
+ s|/\.$|/|'
+ _G_collapseslashes='s|/\{1,\}|/|g'
+ _G_finalslash='s|/*$|/|'
+
+ # Start from root dir and reassemble the path.
+ func_normal_abspath_result=
+ func_normal_abspath_tpath=$1
+ func_normal_abspath_altnamespace=
+ case $func_normal_abspath_tpath in
+ "")
+ # Empty path, that just means $cwd.
+ func_stripname '' '/' "`pwd`"
+ func_normal_abspath_result=$func_stripname_result
+ return
+ ;;
+ # The next three entries are used to spot a run of precisely
+ # two leading slashes without using negated character classes;
+ # we take advantage of case's first-match behaviour.
+ ///*)
+ # Unusual form of absolute path, do nothing.
+ ;;
+ //*)
+ # Not necessarily an ordinary path; POSIX reserves leading '//'
+ # and for example Cygwin uses it to access remote file shares
+ # over CIFS/SMB, so we conserve a leading double slash if found.
+ func_normal_abspath_altnamespace=/
+ ;;
+ /*)
+ # Absolute path, do nothing.
+ ;;
+ *)
+ # Relative path, prepend $cwd.
+ func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+ ;;
+ esac
+
+ # Cancel out all the simple stuff to save iterations. We also want
+ # the path to end with a slash for ease of parsing, so make sure
+ # there is one (and only one) here.
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
+ while :; do
+ # Processed it all yet?
+ if test / = "$func_normal_abspath_tpath"; then
+ # If we ascended to the root using ".." the result may be empty now.
+ if test -z "$func_normal_abspath_result"; then
+ func_normal_abspath_result=/
+ fi
+ break
+ fi
+ func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcar"`
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcdr"`
+ # Figure out what to do with it
+ case $func_normal_abspath_tcomponent in
+ "")
+ # Trailing empty path component, ignore it.
+ ;;
+ ..)
+ # Parent dir; strip last assembled component from result.
+ func_dirname "$func_normal_abspath_result"
+ func_normal_abspath_result=$func_dirname_result
+ ;;
+ *)
+ # Actual path component, append it.
+ func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
+ ;;
+ esac
+ done
+ # Restore leading double-slash if one was found on entry.
+ func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+
+# func_notquiet ARG...
+# --------------------
+# Echo program name prefixed message only when not in quiet mode.
+func_notquiet ()
+{
+ $debug_cmd
+
+ $opt_quiet || func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+
+# func_relative_path SRCDIR DSTDIR
+# --------------------------------
+# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
+func_relative_path ()
+{
+ $debug_cmd
+
+ func_relative_path_result=
+ func_normal_abspath "$1"
+ func_relative_path_tlibdir=$func_normal_abspath_result
+ func_normal_abspath "$2"
+ func_relative_path_tbindir=$func_normal_abspath_result
+
+ # Ascend the tree starting from libdir
+ while :; do
+ # check if we have found a prefix of bindir
+ case $func_relative_path_tbindir in
+ $func_relative_path_tlibdir)
+ # found an exact match
+ func_relative_path_tcancelled=
+ break
+ ;;
+ $func_relative_path_tlibdir*)
+ # found a matching prefix
+ func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+ func_relative_path_tcancelled=$func_stripname_result
+ if test -z "$func_relative_path_result"; then
+ func_relative_path_result=.
+ fi
+ break
+ ;;
+ *)
+ func_dirname $func_relative_path_tlibdir
+ func_relative_path_tlibdir=$func_dirname_result
+ if test -z "$func_relative_path_tlibdir"; then
+ # Have to descend all the way to the root!
+ func_relative_path_result=../$func_relative_path_result
+ func_relative_path_tcancelled=$func_relative_path_tbindir
+ break
+ fi
+ func_relative_path_result=../$func_relative_path_result
+ ;;
+ esac
+ done
+
+ # Now calculate path; take care to avoid doubling-up slashes.
+ func_stripname '' '/' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ func_stripname '/' '/' "$func_relative_path_tcancelled"
+ if test -n "$func_stripname_result"; then
+ func_append func_relative_path_result "/$func_stripname_result"
+ fi
+
+ # Normalisation. If bindir is libdir, return '.' else relative path.
+ if test -n "$func_relative_path_result"; then
+ func_stripname './' '' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ fi
+
+ test -n "$func_relative_path_result" || func_relative_path_result=.
+
+ :
+}
+
+
+# func_quote_for_eval ARG...
+# --------------------------
+# Aesthetically quote ARGs to be evaled later.
+# This function returns two values:
+# i) func_quote_for_eval_result
+# double-quoted, suitable for a subsequent eval
+# ii) func_quote_for_eval_unquoted_result
+# has all characters that are still active within double
+# quotes backslashified.
+func_quote_for_eval ()
+{
+ $debug_cmd
+
+ func_quote_for_eval_unquoted_result=
+ func_quote_for_eval_result=
+ while test 0 -lt $#; do
+ case $1 in
+ *[\\\`\"\$]*)
+ _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+ *)
+ _G_unquoted_arg=$1 ;;
+ esac
+ if test -n "$func_quote_for_eval_unquoted_result"; then
+ func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
+ else
+ func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+ fi
+
+ case $_G_unquoted_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and variable expansion
+ # for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_quoted_arg=\"$_G_unquoted_arg\"
+ ;;
+ *)
+ _G_quoted_arg=$_G_unquoted_arg
+ ;;
+ esac
+
+ if test -n "$func_quote_for_eval_result"; then
+ func_append func_quote_for_eval_result " $_G_quoted_arg"
+ else
+ func_append func_quote_for_eval_result "$_G_quoted_arg"
+ fi
+ shift
+ done
+}
+
+
+# func_quote_for_expand ARG
+# -------------------------
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ $debug_cmd
+
+ case $1 in
+ *[\\\`\"]*)
+ _G_arg=`$ECHO "$1" | $SED \
+ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ _G_arg=$1 ;;
+ esac
+
+ case $_G_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_arg=\"$_G_arg\"
+ ;;
+ esac
+
+ func_quote_for_expand_result=$_G_arg
+}
+
+
+# func_stripname PREFIX SUFFIX NAME
+# ---------------------------------
+# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_stripname ()
+ {
+ $debug_cmd
+
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary variable first.
+ func_stripname_result=$3
+ func_stripname_result=${func_stripname_result#"$1"}
+ func_stripname_result=${func_stripname_result%"$2"}
+ }'
+else
+ func_stripname ()
+ {
+ $debug_cmd
+
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
+ esac
+ }
+fi
+
+
+# func_show_eval CMD [FAIL_EXP]
+# -----------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ func_quote_for_expand "$_G_cmd"
+ eval "func_notquiet $func_quote_for_expand_result"
+
+ $opt_dry_run || {
+ eval "$_G_cmd"
+ _G_status=$?
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_show_eval_locale CMD [FAIL_EXP]
+# ------------------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ $opt_quiet || {
+ func_quote_for_expand "$_G_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ $opt_dry_run || {
+ eval "$_G_user_locale
+ $_G_cmd"
+ _G_status=$?
+ eval "$_G_safe_locale"
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_tr_sh
+# ----------
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result. All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+ $debug_cmd
+
+ case $1 in
+ [0-9]* | *[!a-zA-Z0-9_]*)
+ func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
+ ;;
+ * )
+ func_tr_sh_result=$1
+ ;;
+ esac
+}
+
+
+# func_verbose ARG...
+# -------------------
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $debug_cmd
+
+ $opt_verbose && func_echo "$*"
+
+ :
+}
+
+
+# func_warn_and_continue ARG...
+# -----------------------------
+# Echo program name prefixed warning message to standard error.
+func_warn_and_continue ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
+}
+
+
+# func_warning CATEGORY ARG...
+# ----------------------------
+# Echo program name prefixed warning message to standard error. Warning
+# messages can be filtered according to CATEGORY, where this function
+# elides messages where CATEGORY is not listed in the global variable
+# 'opt_warning_types'.
+func_warning ()
+{
+ $debug_cmd
+
+ # CATEGORY must be in the warning_categories list!
+ case " $warning_categories " in
+ *" $1 "*) ;;
+ *) func_internal_error "invalid warning category '$1'" ;;
+ esac
+
+ _G_category=$1
+ shift
+
+ case " $opt_warning_types " in
+ *" $_G_category "*) $warning_func ${1+"$@"} ;;
+ esac
+}
+
+
+# func_sort_ver VER1 VER2
+# -----------------------
+# 'sort -V' is not generally available.
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+func_sort_ver ()
+{
+ $debug_cmd
+
+ printf '%s\n%s\n' "$1" "$2" \
+ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
+}
+
+# func_lt_ver PREV CURR
+# ---------------------
+# Return true if PREV and CURR are in the correct order according to
+# func_sort_ver, otherwise false. Use it like this:
+#
+# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
+func_lt_ver ()
+{
+ $debug_cmd
+
+ test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+#! /bin/sh
+
+# Set a version string for this script.
+scriptversion=2015-10-07.11; # UTC
+
+# A portable, pluggable option parser for Bourne shell.
+# Written by Gary V. Vaughan, 2010
+
+# Copyright (C) 2010-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# 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/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# This file is a library for parsing options in your shell scripts along
+# with assorted other useful supporting features that you can make use
+# of too.
+#
+# For the simplest scripts you might need only:
+#
+# #!/bin/sh
+# . relative/path/to/funclib.sh
+# . relative/path/to/options-parser
+# scriptversion=1.0
+# func_options ${1+"$@"}
+# eval set dummy "$func_options_result"; shift
+# ...rest of your script...
+#
+# In order for the '--version' option to work, you will need to have a
+# suitably formatted comment like the one at the top of this file
+# starting with '# Written by ' and ending with '# warranty; '.
+#
+# For '-h' and '--help' to work, you will also need a one line
+# description of your script's purpose in a comment directly above the
+# '# Written by ' line, like the one at the top of this file.
+#
+# The default options also support '--debug', which will turn on shell
+# execution tracing (see the comment above debug_cmd below for another
+# use), and '--verbose' and the func_verbose function to allow your script
+# to display verbose messages only when your user has specified
+# '--verbose'.
+#
+# After sourcing this file, you can plug processing for additional
+# options by amending the variables from the 'Configuration' section
+# below, and following the instructions in the 'Option parsing'
+# section further down.
+
+## -------------- ##
+## Configuration. ##
+## -------------- ##
+
+# You should override these variables in your script after sourcing this
+# file so that they reflect the customisations you have added to the
+# option parser.
+
+# The usage line for option parsing errors and the start of '-h' and
+# '--help' output messages. You can embed shell variables for delayed
+# expansion at the time the message is displayed, but you will need to
+# quote other shell meta-characters carefully to prevent them being
+# expanded when the contents are evaled.
+usage='$progpath [OPTION]...'
+
+# Short help message in response to '-h' and '--help'. Add to this or
+# override it after sourcing this library to reflect the full set of
+# options your script accepts.
+usage_message="\
+ --debug enable verbose shell tracing
+ -W, --warnings=CATEGORY
+ report the warnings falling in CATEGORY [all]
+ -v, --verbose verbosely report processing
+ --version print version information and exit
+ -h, --help print short or long help message and exit
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+long_help_message="
+Warning categories include:
+ 'all' show all warnings
+ 'none' turn off all the warnings
+ 'error' warnings are treated as fatal errors"
+
+# Help message printed before fatal option parsing errors.
+fatal_help="Try '\$progname --help' for more information."
+
+
+
+## ------------------------- ##
+## Hook function management. ##
+## ------------------------- ##
+
+# This section contains functions for adding, removing, and running hooks
+# to the main code. A hook is just a named list of of function, that can
+# be run in order later on.
+
+# func_hookable FUNC_NAME
+# -----------------------
+# Declare that FUNC_NAME will run hooks added with
+# 'func_add_hook FUNC_NAME ...'.
+func_hookable ()
+{
+ $debug_cmd
+
+ func_append hookable_fns " $1"
+}
+
+
+# func_add_hook FUNC_NAME HOOK_FUNC
+# ---------------------------------
+# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must
+# first have been declared "hookable" by a call to 'func_hookable'.
+func_add_hook ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not accept hook functions." ;;
+ esac
+
+ eval func_append ${1}_hooks '" $2"'
+}
+
+
+# func_remove_hook FUNC_NAME HOOK_FUNC
+# ------------------------------------
+# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
+func_remove_hook ()
+{
+ $debug_cmd
+
+ eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
+}
+
+
+# func_run_hooks FUNC_NAME [ARG]...
+# ---------------------------------
+# Run all hook functions registered to FUNC_NAME.
+# It is assumed that the list of hook functions contains nothing more
+# than a whitespace-delimited list of legal shell function names, and
+# no effort is wasted trying to catch shell meta-characters or preserve
+# whitespace.
+func_run_hooks ()
+{
+ $debug_cmd
+
+ _G_rc_run_hooks=false
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not support hook funcions.n" ;;
+ esac
+
+ eval _G_hook_fns=\$$1_hooks; shift
+
+ for _G_hook in $_G_hook_fns; do
+ if eval $_G_hook '"$@"'; then
+ # store returned options list back into positional
+ # parameters for next 'cmd' execution.
+ eval _G_hook_result=\$${_G_hook}_result
+ eval set dummy "$_G_hook_result"; shift
+ _G_rc_run_hooks=:
+ fi
+ done
+
+ $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result
+}
+
+
+
+## --------------- ##
+## Option parsing. ##
+## --------------- ##
+
+# In order to add your own option parsing hooks, you must accept the
+# full positional parameter list in your hook function, you may remove/edit
+# any options that you action, and then pass back the remaining unprocessed
+# options in '<hooked_function_name>_result', escaped suitably for
+# 'eval'. In this case you also must return $EXIT_SUCCESS to let the
+# hook's caller know that it should pay attention to
+# '<hooked_function_name>_result'. Returning $EXIT_FAILURE signalizes that
+# arguments are left untouched by the hook and therefore caller will ignore the
+# result variable.
+#
+# Like this:
+#
+# my_options_prep ()
+# {
+# $debug_cmd
+#
+# # Extend the existing usage message.
+# usage_message=$usage_message'
+# -s, --silent don'\''t print informational messages
+# '
+# # No change in '$@' (ignored completely by this hook). There is
+# # no need to do the equivalent (but slower) action:
+# # func_quote_for_eval ${1+"$@"}
+# # my_options_prep_result=$func_quote_for_eval_result
+# false
+# }
+# func_add_hook func_options_prep my_options_prep
+#
+#
+# my_silent_option ()
+# {
+# $debug_cmd
+#
+# args_changed=false
+#
+# # Note that for efficiency, we parse as many options as we can
+# # recognise in a loop before passing the remainder back to the
+# # caller on the first unrecognised argument we encounter.
+# while test $# -gt 0; do
+# opt=$1; shift
+# case $opt in
+# --silent|-s) opt_silent=:
+# args_changed=:
+# ;;
+# # Separate non-argument short options:
+# -s*) func_split_short_opt "$_G_opt"
+# set dummy "$func_split_short_opt_name" \
+# "-$func_split_short_opt_arg" ${1+"$@"}
+# shift
+# args_changed=:
+# ;;
+# *) # Make sure the first unrecognised option "$_G_opt"
+# # is added back to "$@", we could need that later
+# # if $args_changed is true.
+# set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+# esac
+# done
+#
+# if $args_changed; then
+# func_quote_for_eval ${1+"$@"}
+# my_silent_option_result=$func_quote_for_eval_result
+# fi
+#
+# $args_changed
+# }
+# func_add_hook func_parse_options my_silent_option
+#
+#
+# my_option_validation ()
+# {
+# $debug_cmd
+#
+# $opt_silent && $opt_verbose && func_fatal_help "\
+# '--silent' and '--verbose' options are mutually exclusive."
+#
+# false
+# }
+# func_add_hook func_validate_options my_option_validation
+#
+# You'll also need to manually amend $usage_message to reflect the extra
+# options you parse. It's preferable to append if you can, so that
+# multiple option parsing hooks can be added safely.
+
+
+# func_options_finish [ARG]...
+# ----------------------------
+# Finishing the option parse loop (call 'func_options' hooks ATM).
+func_options_finish ()
+{
+ $debug_cmd
+
+ _G_func_options_finish_exit=false
+ if func_run_hooks func_options ${1+"$@"}; then
+ func_options_finish_result=$func_run_hooks_result
+ _G_func_options_finish_exit=:
+ fi
+
+ $_G_func_options_finish_exit
+}
+
+
+# func_options [ARG]...
+# ---------------------
+# All the functions called inside func_options are hookable. See the
+# individual implementations for details.
+func_hookable func_options
+func_options ()
+{
+ $debug_cmd
+
+ _G_rc_options=false
+
+ for my_func in options_prep parse_options validate_options options_finish
+ do
+ if eval func_$my_func '${1+"$@"}'; then
+ eval _G_res_var='$'"func_${my_func}_result"
+ eval set dummy "$_G_res_var" ; shift
+ _G_rc_options=:
+ fi
+ done
+
+ # Save modified positional parameters for caller. As a top-level
+ # options-parser function we always need to set the 'func_options_result'
+ # variable (regardless the $_G_rc_options value).
+ if $_G_rc_options; then
+ func_options_result=$_G_res_var
+ else
+ func_quote_for_eval ${1+"$@"}
+ func_options_result=$func_quote_for_eval_result
+ fi
+
+ $_G_rc_options
+}
+
+
+# func_options_prep [ARG]...
+# --------------------------
+# All initialisations required before starting the option parse loop.
+# Note that when calling hook functions, we pass through the list of
+# positional parameters. If a hook function modifies that list, and
+# needs to propagate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before
+# returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned).
+func_hookable func_options_prep
+func_options_prep ()
+{
+ $debug_cmd
+
+ # Option defaults:
+ opt_verbose=false
+ opt_warning_types=
+
+ _G_rc_options_prep=false
+ if func_run_hooks func_options_prep ${1+"$@"}; then
+ _G_rc_options_prep=:
+ # save modified positional parameters for caller
+ func_options_prep_result=$func_run_hooks_result
+ fi
+
+ $_G_rc_options_prep
+}
+
+
+# func_parse_options [ARG]...
+# ---------------------------
+# The main option parsing loop.
+func_hookable func_parse_options
+func_parse_options ()
+{
+ $debug_cmd
+
+ func_parse_options_result=
+
+ _G_rc_parse_options=false
+ # this just eases exit handling
+ while test $# -gt 0; do
+ # Defer to hook functions for initial option parsing, so they
+ # get priority in the event of reusing an option name.
+ if func_run_hooks func_parse_options ${1+"$@"}; then
+ eval set dummy "$func_run_hooks_result"; shift
+ _G_rc_parse_options=:
+ fi
+
+ # Break out of the loop if we already parsed every option.
+ test $# -gt 0 || break
+
+ _G_match_parse_options=:
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --debug|-x) debug_cmd='set -x'
+ func_echo "enabling shell trace mode"
+ $debug_cmd
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ set dummy --warnings none ${1+"$@"}
+ shift
+ ;;
+
+ --warnings|--warning|-W)
+ if test $# = 0 && func_missing_arg $_G_opt; then
+ _G_rc_parse_options=:
+ break
+ fi
+ case " $warning_categories $1" in
+ *" $1 "*)
+ # trailing space prevents matching last $1 above
+ func_append_uniq opt_warning_types " $1"
+ ;;
+ *all)
+ opt_warning_types=$warning_categories
+ ;;
+ *none)
+ opt_warning_types=none
+ warning_func=:
+ ;;
+ *error)
+ opt_warning_types=$warning_categories
+ warning_func=func_fatal_error
+ ;;
+ *)
+ func_fatal_error \
+ "unsupported warning category: '$1'"
+ ;;
+ esac
+ shift
+ ;;
+
+ --verbose|-v) opt_verbose=: ;;
+ --version) func_version ;;
+ -\?|-h) func_usage ;;
+ --help) func_help ;;
+
+ # Separate optargs to long options (plugins may need this):
+ --*=*) func_split_equals "$_G_opt"
+ set dummy "$func_split_equals_lhs" \
+ "$func_split_equals_rhs" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate optargs to short options:
+ -W*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate non-argument short options:
+ -\?*|-h*|-v*|-x*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "-$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ --) _G_rc_parse_options=: ; break ;;
+ -*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
+ *) set dummy "$_G_opt" ${1+"$@"}; shift
+ _G_match_parse_options=false
+ break
+ ;;
+ esac
+
+ $_G_match_parse_options && _G_rc_parse_options=:
+ done
+
+
+ if $_G_rc_parse_options; then
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ func_parse_options_result=$func_quote_for_eval_result
+ fi
+
+ $_G_rc_parse_options
+}
+
+
+# func_validate_options [ARG]...
+# ------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+func_hookable func_validate_options
+func_validate_options ()
+{
+ $debug_cmd
+
+ _G_rc_validate_options=false
+
+ # Display all warnings if -W was not given.
+ test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
+
+ if func_run_hooks func_validate_options ${1+"$@"}; then
+ # save modified positional parameters for caller
+ func_validate_options_result=$func_run_hooks_result
+ _G_rc_validate_options=:
+ fi
+
+ # Bail if the options were screwed!
+ $exit_cmd $EXIT_FAILURE
+
+ $_G_rc_validate_options
+}
+
+
+
+## ----------------- ##
+## Helper functions. ##
+## ----------------- ##
+
+# This section contains the helper functions used by the rest of the
+# hookable option parser framework in ascii-betical order.
+
+
+# func_fatal_help ARG...
+# ----------------------
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ eval \$ECHO \""$fatal_help"\"
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+
+# func_help
+# ---------
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message"
+ exit 0
+}
+
+
+# func_missing_arg ARGNAME
+# ------------------------
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ $debug_cmd
+
+ func_error "Missing argument for '$1'."
+ exit_cmd=exit
+}
+
+
+# func_split_equals STRING
+# ------------------------
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
+# splitting STRING at the '=' sign.
+test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=${1%%=*}
+ func_split_equals_rhs=${1#*=}
+ test "x$func_split_equals_lhs" = "x$1" \
+ && func_split_equals_rhs=
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
+ func_split_equals_rhs=
+ test "x$func_split_equals_lhs" = "x$1" \
+ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
+ }
+fi #func_split_equals
+
+
+# func_split_short_opt SHORTOPT
+# -----------------------------
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_arg=${1#??}
+ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
+ func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
+ }
+fi #func_split_short_opt
+
+
+# func_usage
+# ----------
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
+ exit 0
+}
+
+
+# func_usage_message
+# ------------------
+# Echo short help message to standard output.
+func_usage_message ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ echo
+ $SED -n 's|^# ||
+ /^Written by/{
+ x;p;x
+ }
+ h
+ /^Written by/q' < "$progpath"
+ echo
+ eval \$ECHO \""$usage_message"\"
+}
+
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $debug_cmd
+
+ printf '%s\n' "$progname $scriptversion"
+ $SED -n '
+ /(C)/!b go
+ :more
+ /\./!{
+ N
+ s|\n# | |
+ b more
+ }
+ :go
+ /^# Written by /,/# warranty; / {
+ s|^# ||
+ s|^# *$||
+ s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+ p
+ }
+ /^# Written by / {
+ s|^# ||
+ p
+ }
+ /^warranty; /q' < "$progpath"
+
+ exit $?
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+
+# Set a version string.
+scriptversion='(GNU libtool) 2.4.6'
+
+
+# func_echo ARG...
+# ----------------
+# Libtool also displays the current mode in messages, so override
+# funclib.sh func_echo with this custom definition.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+ $debug_cmd
+
+ $warning_func ${1+"$@"}
+}
+
+
+## ---------------- ##
+## Options parsing. ##
+## ---------------- ##
+
+# Hook in the functions to make sure our own options are parsed during
+# the option parsing loop.
+
+usage='$progpath [OPTION]... [MODE-ARG]...'
+
+# Short help message in response to '-h'.
+usage_message="Options:
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+ -n, --dry-run display commands without modifying any files
+ --features display basic configuration information and exit
+ --mode=MODE use operation mode MODE
+ --no-warnings equivalent to '-Wnone'
+ --preserve-dup-deps don't remove duplicate dependency libraries
+ --quiet, --silent don't print informational messages
+ --tag=TAG use configuration variables from tag TAG
+ -v, --verbose print more informational messages than default
+ --version print version information
+ -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
+ -h, --help, --help-all print short, long, or detailed help message
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message
+
+MODE must be one of the following:
+
+ clean remove files from the build directory
+ compile compile a source file into a libtool object
+ execute automatically set library path, then run a program
+ finish complete the installation of libtool libraries
+ install install libraries or executables
+ link create a library or an executable
+ uninstall remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE. When passed as first option,
+'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
+Try '$progname --help --mode=MODE' for a more detailed description of MODE.
+
+When reporting a bug, please describe a test case to reproduce it and
+include the following information:
+
+ host-triplet: $host
+ shell: $SHELL
+ compiler: $LTCC
+ compiler flags: $LTCFLAGS
+ linker: $LD (gnu? $with_gnu_ld)
+ version: $progname $scriptversion Debian-2.4.6-15
+ automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
+ autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
+
+Report bugs to <bug-libtool@gnu.org>.
+GNU libtool home page: <http://www.gnu.org/s/libtool/>.
+General help using GNU software: <http://www.gnu.org/gethelp/>."
+ exit 0
+}
+
+
+# func_lo2o OBJECT-NAME
+# ---------------------
+# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
+# object suffix.
+
+lo2o=s/\\.lo\$/.$objext/
+o2lo=s/\\.$objext\$/.lo/
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_lo2o ()
+ {
+ case $1 in
+ *.lo) func_lo2o_result=${1%.lo}.$objext ;;
+ * ) func_lo2o_result=$1 ;;
+ esac
+ }'
+
+ # func_xform LIBOBJ-OR-SOURCE
+ # ---------------------------
+ # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
+ # suffix to a '.lo' libtool-object suffix.
+ eval 'func_xform ()
+ {
+ func_xform_result=${1%.*}.lo
+ }'
+else
+ # ...otherwise fall back to using sed.
+ func_lo2o ()
+ {
+ func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
+ }
+
+ func_xform ()
+ {
+ func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
+ }
+fi
+
+
+# func_fatal_configuration ARG...
+# -------------------------------
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func__fatal_error ${1+"$@"} \
+ "See the $PACKAGE documentation for more information." \
+ "Fatal configuration error."
+}
+
+
+# func_config
+# -----------
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+
+# func_features
+# -------------
+# Display the features supported by this script.
+func_features ()
+{
+ echo "host: $host"
+ if test yes = "$build_libtool_libs"; then
+ echo "enable shared libraries"
+ else
+ echo "disable shared libraries"
+ fi
+ if test yes = "$build_old_libs"; then
+ echo "enable static libraries"
+ else
+ echo "disable static libraries"
+ fi
+
+ exit $?
+}
+
+
+# func_enable_tag TAGNAME
+# -----------------------
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname=$1
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf=/$re_begincf/,/$re_endcf/p
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+
+# func_check_version_match
+# ------------------------
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+# libtool_options_prep [ARG]...
+# -----------------------------
+# Preparation for options parsed by libtool.
+libtool_options_prep ()
+{
+ $debug_mode
+
+ # Option defaults:
+ opt_config=false
+ opt_dlopen=
+ opt_dry_run=false
+ opt_help=false
+ opt_mode=
+ opt_preserve_dup_deps=false
+ opt_quiet=false
+
+ nonopt=
+ preserve_args=
+
+ _G_rc_lt_options_prep=:
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ *)
+ _G_rc_lt_options_prep=false
+ ;;
+ esac
+
+ if $_G_rc_lt_options_prep; then
+ # Pass back the list of options.
+ func_quote_for_eval ${1+"$@"}
+ libtool_options_prep_result=$func_quote_for_eval_result
+ fi
+
+ $_G_rc_lt_options_prep
+}
+func_add_hook func_options_prep libtool_options_prep
+
+
+# libtool_parse_options [ARG]...
+# ---------------------------------
+# Provide handling for libtool specific options.
+libtool_parse_options ()
+{
+ $debug_cmd
+
+ _G_rc_lt_parse_options=false
+
+ # Perform our own loop to consume as many options as possible in
+ # each iteration.
+ while test $# -gt 0; do
+ _G_match_lt_parse_options=:
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --dry-run|--dryrun|-n)
+ opt_dry_run=:
+ ;;
+
+ --config) func_config ;;
+
+ --dlopen|-dlopen)
+ opt_dlopen="${opt_dlopen+$opt_dlopen
+}$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_preserve_dup_deps=: ;;
+
+ --features) func_features ;;
+
+ --finish) set dummy --mode finish ${1+"$@"}; shift ;;
+
+ --help) opt_help=: ;;
+
+ --help-all) opt_help=': help-all' ;;
+
+ --mode) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_mode=$1
+ case $1 in
+ # Valid mode arguments:
+ clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $_G_opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+ shift
+ ;;
+
+ --no-silent|--no-quiet)
+ opt_quiet=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ opt_warning=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-verbose)
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --silent|--quiet)
+ opt_quiet=:
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --tag) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_tag=$1
+ func_append preserve_args " $_G_opt $1"
+ func_enable_tag "$1"
+ shift
+ ;;
+
+ --verbose|-v) opt_quiet=false
+ opt_verbose=:
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ # An option not handled by this hook function:
+ *) set dummy "$_G_opt" ${1+"$@"} ; shift
+ _G_match_lt_parse_options=false
+ break
+ ;;
+ esac
+ $_G_match_lt_parse_options && _G_rc_lt_parse_options=:
+ done
+
+ if $_G_rc_lt_parse_options; then
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ libtool_parse_options_result=$func_quote_for_eval_result
+ fi
+
+ $_G_rc_lt_parse_options
+}
+func_add_hook func_parse_options libtool_parse_options
+
+
+
+# libtool_validate_options [ARG]...
+# ---------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+libtool_validate_options ()
+{
+ # save first non-option argument
+ if test 0 -lt $#; then
+ nonopt=$1
+ shift
+ fi
+
+ # preserve --debug
+ test : = "$debug_cmd" || func_append preserve_args " --debug"
+
+ case $host in
+ # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
+ # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
+ *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+ ;;
+ esac
+
+ $opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ test yes != "$build_libtool_libs" \
+ && test yes != "$build_old_libs" \
+ && func_fatal_configuration "not configured to build any kind of library"
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
+ func_error "unrecognized option '-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help=$help
+ help="Try '$progname --help --mode=$opt_mode' for more information."
+ }
+
+ # Pass back the unparsed argument list
+ func_quote_for_eval ${1+"$@"}
+ libtool_validate_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_validate_options libtool_validate_options
+
+
+# Process options as early as possible so that --help and --version
+# can return quickly.
+func_options ${1+"$@"}
+eval set dummy "$func_options_result"; shift
+
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+magic='%%%MAGIC variable%%%'
+magic_exe='%%%MAGIC EXE variable%%%'
+
+# Global variables.
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# func_generated_by_libtool
+# True iff stdin has been generated by Libtool. This function is only
+# a basic sanity check; it will hardly flush out determined imposters.
+func_generated_by_libtool_p ()
+{
+ $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ test -f "$1" &&
+ $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if 'file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case $lalib_p_line in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test yes = "$lalib_p"
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ test -f "$1" &&
+ $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $debug_cmd
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# 'FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $debug_cmd
+
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot. Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+ func_resolve_sysroot_result=$1
+ case $func_resolve_sysroot_result in
+ =*)
+ func_stripname '=' '' "$func_resolve_sysroot_result"
+ func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+ ;;
+ esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+ case $lt_sysroot:$1 in
+ ?*:"$lt_sysroot"*)
+ func_stripname "$lt_sysroot" '' "$1"
+ func_replace_sysroot_result='='$func_stripname_result
+ ;;
+ *)
+ # Including no sysroot.
+ func_replace_sysroot_result=$1
+ ;;
+ esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $debug_cmd
+
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case "$@ " in
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with '--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=$1
+ if test yes = "$build_libtool_libs"; then
+ write_lobj=\'$2\'
+ else
+ write_lobj=none
+ fi
+
+ if test yes = "$build_old_libs"; then
+ write_oldobj=\'$3\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "$write_libobj"
+ }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+ $debug_cmd
+
+ func_convert_core_file_wine_to_w32_result=$1
+ if test -n "$1"; then
+ # Unfortunately, winepath does not exit with a non-zero error code, so we
+ # are forced to check the contents of stdout. On the other hand, if the
+ # command is not found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both error code of
+ # zero AND non-empty stdout, which explains the odd construction:
+ func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
+ func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+ $SED -e "$sed_naive_backslashify"`
+ else
+ func_convert_core_file_wine_to_w32_result=
+ fi
+ fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+ $debug_cmd
+
+ # unfortunately, winepath doesn't convert paths, only file names
+ func_convert_core_path_wine_to_w32_result=
+ if test -n "$1"; then
+ oldIFS=$IFS
+ IFS=:
+ for func_convert_core_path_wine_to_w32_f in $1; do
+ IFS=$oldIFS
+ func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+ if test -n "$func_convert_core_file_wine_to_w32_result"; then
+ if test -z "$func_convert_core_path_wine_to_w32_result"; then
+ func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
+ else
+ func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+ fi
+ fi
+ done
+ IFS=$oldIFS
+ fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+ $debug_cmd
+
+ if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+ func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+ if test "$?" -ne 0; then
+ # on failure, ensure result is empty
+ func_cygpath_result=
+ fi
+ else
+ func_cygpath_result=
+ func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
+ fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format. Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+ $debug_cmd
+
+ # awkward: cmd appends spaces to result
+ func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+ $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+ $debug_cmd
+
+ if test -z "$2" && test -n "$1"; then
+ func_error "Could not determine host file name corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_file_result=$1
+ fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+ $debug_cmd
+
+ if test -z "$4" && test -n "$3"; then
+ func_error "Could not determine the host path corresponding to"
+ func_error " '$3'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This is a deliberately simplistic "conversion" and
+ # should not be "improved". See libtool.info.
+ if test "x$1" != "x$2"; then
+ lt_replace_pathsep_chars="s|$1|$2|g"
+ func_to_host_path_result=`echo "$3" |
+ $SED -e "$lt_replace_pathsep_chars"`
+ else
+ func_to_host_path_result=$3
+ fi
+ fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+ $debug_cmd
+
+ case $4 in
+ $1 ) func_to_host_path_result=$3$func_to_host_path_result
+ ;;
+ esac
+ case $4 in
+ $2 ) func_append func_to_host_path_result "$3"
+ ;;
+ esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via '$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+ $debug_cmd
+
+ $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result. If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+ $debug_cmd
+
+ case ,$2, in
+ *,"$to_tool_file_cmd",*)
+ func_to_tool_file_result=$1
+ ;;
+ *)
+ $to_tool_file_cmd "$1"
+ func_to_tool_file_result=$func_to_host_file_result
+ ;;
+ esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+ func_to_host_file_result=$1
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_msys_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+ # LT_CYGPATH in this case.
+ func_to_host_file_result=`cygpath -m "$1"`
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format. Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_file_wine_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_msys_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set. Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+ func_convert_core_file_wine_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via '$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format. If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+# file name conversion function : func_convert_file_X_to_Y ()
+# path conversion function : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same. If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+ $debug_cmd
+
+ if test -z "$to_host_path_cmd"; then
+ func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+ to_host_path_cmd=func_convert_path_$func_stripname_result
+ fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+ $debug_cmd
+
+ func_init_to_host_path_cmd
+ $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+ func_to_host_path_result=$1
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from ARG. MSYS
+ # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+ # and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_msys_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format. Requires a wine environment and
+# a working winepath. Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set. Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_dll_def_p FILE
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with _LT_DLL_DEF_P in libtool.m4
+func_dll_def_p ()
+{
+ $debug_cmd
+
+ func_dll_def_p_tmp=`$SED -n \
+ -e 's/^[ ]*//' \
+ -e '/^\(;.*\)*$/d' \
+ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \
+ -e q \
+ "$1"`
+ test DEF = "$func_dll_def_p_tmp"
+}
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $debug_cmd
+
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile=$nonopt # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg=$arg
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj=$arg
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify '-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ func_append pie_flag " $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ func_append later " $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs=$IFS; IFS=,
+ for arg in $args; do
+ IFS=$save_ifs
+ func_append_quoted lastarg "$arg"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ func_append base_compile " $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg=$srcfile
+ srcfile=$arg
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_append_quoted base_compile "$lastarg"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with '-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj=$func_basename_result
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from '$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test yes = "$build_libtool_libs" \
+ || func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name '$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname=$func_basename_result
+ xdir=$func_dirname_result
+ lobj=$xdir$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test yes = "$build_old_libs"; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+ if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test no = "$compiler_c_o"; then
+ output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
+ lockfile=$output_obj.lock
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test yes = "$need_locks"; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test warn = "$need_locks"; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ func_append removelist " $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ func_append removelist " $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+ srcfile=$func_to_tool_file_result
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test yes = "$build_libtool_libs"; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test no != "$pic_mode"; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ func_append command " -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test yes = "$suppress_opt"; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test yes = "$build_old_libs"; then
+ if test yes != "$pic_mode"; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test yes = "$compiler_c_o"; then
+ func_append command " -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ func_append command "$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test no != "$need_locks"; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+ test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $opt_mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to build PIC objects only
+ -prefer-non-pic try to build non-PIC objects only
+ -shared do not build a '.o' file suitable for static linking
+ -static only build a '.o' file suitable for static linking
+ -Wc,FLAG pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a 'standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix '.c' with the
+library object suffix, '.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to '-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the '--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the 'install' or 'cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -bindir BINDIR specify path to binaries directory (for systems where
+ libraries must be found in the PATH setting at runtime)
+ -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE use a list of object files found in FILE to specify objects
+ -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes)
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+ -Wc,FLAG
+ -Xcompiler FLAG pass linker-specific FLAG directly to the compiler
+ -Wl,FLAG
+ -Xlinker FLAG pass linker-specific FLAG directly to the linker
+ -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with '-') are ignored.
+
+Every other argument is treated as a filename. Files ending in '.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in '.la', then a libtool library is created,
+only library objects ('.lo' files) may be specified, and '-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
+using 'ar' and 'ranlib', or on Windows using 'lib'.
+
+If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode '$opt_mode'"
+ ;;
+ esac
+
+ echo
+ $ECHO "Try '$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+ if test : = "$opt_help"; then
+ func_mode_help
+ else
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ func_mode_help
+ done
+ } | $SED -n '1p; 2,$s/^Usage:/ or: /p'
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ echo
+ func_mode_help
+ done
+ } |
+ $SED '1d
+ /^When reporting/,/^Report/{
+ H
+ d
+ }
+ $x
+ /information about other modes/d
+ /more detailed .*MODE/d
+ s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+ fi
+ exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $debug_cmd
+
+ # The first argument is the command name.
+ cmd=$nonopt
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $opt_dlopen; do
+ test -f "$file" \
+ || func_fatal_help "'$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "'$file' was not linked with '-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+
+ if test -f "$dir/$objdir/$dlname"; then
+ func_append dir "/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ ;;
+
+ *)
+ func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir=$absdir
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic=$magic
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -* | *.la | *.lo ) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_append_quoted args "$file"
+ done
+
+ if $opt_dry_run; then
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ echo "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ else
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd=\$cmd$args
+ fi
+}
+
+test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $debug_cmd
+
+ libs=
+ libdirs=
+ admincmds=
+
+ for opt in "$nonopt" ${1+"$@"}
+ do
+ if test -d "$opt"; then
+ func_append libdirs " $opt"
+
+ elif test -f "$opt"; then
+ if func_lalib_unsafe_p "$opt"; then
+ func_append libs " $opt"
+ else
+ func_warning "'$opt' is not a valid libtool archive"
+ fi
+
+ else
+ func_fatal_error "invalid argument '$opt'"
+ fi
+ done
+
+ if test -n "$libs"; then
+ if test -n "$lt_sysroot"; then
+ sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+ sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+ else
+ sysroot_cmd=
+ fi
+
+ # Remove sysroot references
+ if $opt_dry_run; then
+ for lib in $libs; do
+ echo "removing references to $lt_sysroot and '=' prefixes from $lib"
+ done
+ else
+ tmpdir=`func_mktempdir`
+ for lib in $libs; do
+ $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+ > $tmpdir/tmp-la
+ mv -f $tmpdir/tmp-la $lib
+ done
+ ${RM}r "$tmpdir"
+ fi
+ fi
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || func_append admincmds "
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_quiet && exit $EXIT_SUCCESS
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ echo "----------------------------------------------------------------------"
+ echo "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ echo
+ echo "If you ever happen to want to link against installed libraries"
+ echo "in a given directory, LIBDIR, you must either use libtool, and"
+ echo "specify the full pathname of the library, or use the '-LLIBDIR'"
+ echo "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ echo " - add LIBDIR to the '$shlibpath_var' environment variable"
+ echo " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ echo " - add LIBDIR to the '$runpath_var' environment variable"
+ echo " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the '$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
+ fi
+ echo
+
+ echo "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ echo "pages."
+ ;;
+ *)
+ echo "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ echo "----------------------------------------------------------------------"
+ fi
+ exit $EXIT_SUCCESS
+}
+
+test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $debug_cmd
+
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
+ # Allow the use of GNU shtool's install command.
+ case $nonopt in *shtool*) :;; *) false;; esac
+ then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ func_append install_prog "$func_quote_for_eval_result"
+ install_shared_prog=$install_prog
+ case " $install_prog " in
+ *[\\\ /]cp\ *) install_cp=: ;;
+ *) install_cp=false ;;
+ esac
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=false
+ stripme=
+ no_mode=:
+ for arg
+ do
+ arg2=
+ if test -n "$dest"; then
+ func_append files " $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=: ;;
+ -f)
+ if $install_cp; then :; else
+ prev=$arg
+ fi
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ if test X-m = "X$prev" && test -n "$install_override_mode"; then
+ arg2=$install_override_mode
+ no_mode=false
+ fi
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ func_append install_prog " $func_quote_for_eval_result"
+ if test -n "$arg2"; then
+ func_quote_for_eval "$arg2"
+ fi
+ func_append install_shared_prog " $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prev' option requires an argument"
+
+ if test -n "$install_override_mode" && $no_mode; then
+ if $install_cp; then :; else
+ func_quote_for_eval "$install_override_mode"
+ func_append install_shared_prog " -m $func_quote_for_eval_result"
+ fi
+ fi
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=:
+ if $isdir; then
+ destdir=$dest
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir=$func_dirname_result
+ destname=$func_basename_result
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "'$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "'$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ func_append staticlibs " $file"
+ ;;
+
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append current_libdirs " $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append future_libdirs " $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir=$func_dirname_result
+ func_append dir "$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking '$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname=$1
+ shift
+
+ srcname=$realname
+ test -n "$relink_command" && srcname=${realname}T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme=$stripme
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ os2*)
+ case $realname in
+ *_dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try 'ln -sf' first, because the 'ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib=$destdir/$realname
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name=$func_basename_result
+ instname=$dir/${name}i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest=$destfile
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to '$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test yes = "$build_old_libs"; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=.exe
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script '$wrapper'"
+
+ finalize=:
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "'$lib' has not been installed in '$libdir'"
+ finalize=false
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test no = "$fast_install" && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if $finalize; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file=$func_basename_result
+ outputname=$tmpdir/$file
+ # Replace the output file specification.
+ relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_quiet || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink '$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file=$outputname
+ else
+ func_warning "cannot relink '$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name=$func_basename_result
+
+ # Set up the ranlib parameters.
+ oldlib=$destdir/$name
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run '$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test install = "$opt_mode" && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $debug_cmd
+
+ my_outputname=$1
+ my_originator=$2
+ my_pic_p=${3-false}
+ my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms=${my_outputname}S.c
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist=$output_objdir/$my_outputname.nm
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test yes = "$dlself"; then
+ func_verbose "generating symbol list for '$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+ func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
+ $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols=$output_objdir/$outputname.exp
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from '$dlprefile'"
+ func_basename "$dlprefile"
+ name=$func_basename_result
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ # if an import library, we need to obtain dlname
+ if func_win32_import_lib_p "$dlprefile"; then
+ func_tr_sh "$dlprefile"
+ eval "curr_lafile=\$libfile_$func_tr_sh_result"
+ dlprefile_dlbasename=
+ if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+ # Use subshell, to avoid clobbering current variable values
+ dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+ if test -n "$dlprefile_dlname"; then
+ func_basename "$dlprefile_dlname"
+ dlprefile_dlbasename=$func_basename_result
+ else
+ # no lafile. user explicitly requested -dlpreopen <import library>.
+ $sharedlib_from_linklib_cmd "$dlprefile"
+ dlprefile_dlbasename=$sharedlib_from_linklib_result
+ fi
+ fi
+ $opt_dry_run || {
+ if test -n "$dlprefile_dlbasename"; then
+ eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+ else
+ func_warning "Could not compute DLL name from $name"
+ eval '$ECHO ": $name " >> "$nlist"'
+ fi
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ }
+ else # not an import lib
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ fi
+ ;;
+ *)
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ ;;
+ esac
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ func_show_eval '$RM "${nlist}I"'
+ if test -n "$global_symbol_to_import"; then
+ eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
+ fi
+
+ echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];\
+"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+static void lt_syminit(void)
+{
+ LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
+ for (; symbol->name; ++symbol)
+ {"
+ $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
+ echo >> "$output_objdir/$my_dlsyms" "\
+ }
+}"
+ fi
+ echo >> "$output_objdir/$my_dlsyms" "\
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{ {\"$my_originator\", (void *) 0},"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {\"@INIT@\", (void *) &lt_syminit},"
+ fi
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ $my_pic_p && pic_flag_for_symtable=" $pic_flag"
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) func_append symtab_cflags " $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj=$output_objdir/${my_outputname}S.$objext
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for '$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+ test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+ test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+ $debug_cmd
+
+ win32_libid_type=unknown
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+ case $nm_interface in
+ "MS dumpbin")
+ if func_cygming_ms_implib_p "$1" ||
+ func_cygming_gnu_implib_p "$1"
+ then
+ win32_nmres=import
+ else
+ win32_nmres=
+ fi
+ ;;
+ *)
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s|.*|import|
+ p
+ q
+ }
+ }'`
+ ;;
+ esac
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+ $debug_cmd
+
+ sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+ $debug_cmd
+
+ match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+ $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+ $SED '/^Contents of section '"$match_literal"':/{
+ # Place marker at beginning of archive member dllname section
+ s/.*/====MARK====/
+ p
+ d
+ }
+ # These lines can sometimes be longer than 43 characters, but
+ # are always uninteresting
+ /:[ ]*file format pe[i]\{,1\}-/d
+ /^In archive [^:]*:/d
+ # Ensure marker is printed
+ /^====MARK====/p
+ # Remove all lines with less than 43 characters
+ /^.\{43\}/!d
+ # From remaining lines, remove first 43 characters
+ s/^.\{43\}//' |
+ $SED -n '
+ # Join marker and all lines until next marker into a single line
+ /^====MARK====/ b para
+ H
+ $ b para
+ b
+ :para
+ x
+ s/\n//g
+ # Remove the marker
+ s/^====MARK====//
+ # Remove trailing dots and whitespace
+ s/[\. \t]*$//
+ # Print
+ /./p' |
+ # we now have a list, one entry per line, of the stringified
+ # contents of the appropriate section of all members of the
+ # archive that possess that section. Heuristic: eliminate
+ # all those that have a first or second character that is
+ # a '.' (that is, objdump's representation of an unprintable
+ # character.) This should work for all archives with less than
+ # 0x302f exports -- but will fail for DLLs whose name actually
+ # begins with a literal '.' or a single character followed by
+ # a '.'.
+ #
+ # Of those that remain, print the first one.
+ $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+ $debug_cmd
+
+ if func_cygming_gnu_implib_p "$1"; then
+ # binutils import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+ elif func_cygming_ms_implib_p "$1"; then
+ # ms-generated import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+ else
+ # unknown
+ sharedlib_from_linklib_result=
+ fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $debug_cmd
+
+ f_ex_an_ar_dir=$1; shift
+ f_ex_an_ar_oldlib=$1
+ if test yes = "$lock_old_archive_extraction"; then
+ lockfile=$f_ex_an_ar_oldlib.lock
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ fi
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+ 'stat=$?; rm -f "$lockfile"; exit $stat'
+ if test yes = "$lock_old_archive_extraction"; then
+ $opt_dry_run || rm -f "$lockfile"
+ fi
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $debug_cmd
+
+ my_gentop=$1; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=
+ my_xlib=
+ my_xabs=
+ my_xdir=
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib=$func_basename_result
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir=$my_gentop/$my_xlib_u
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ func_basename "$darwin_archive"
+ darwin_base_archive=$func_basename_result
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches; do
+ func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
+ $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
+ cd "unfat-$$/$darwin_base_archive-$darwin_arch"
+ func_extract_an_archive "`pwd`" "$darwin_base_archive"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+ done
+
+ func_extract_archives_result=$my_oldobjs
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory where it is stored is
+# the $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=${1-no}
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ file=\"\$0\""
+
+ qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+ $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+ ECHO=\"$qECHO\"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=\$0
+ shift
+ for lt_opt
+ do
+ case \"\$lt_opt\" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+ test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+ lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+ cat \"\$lt_dump_D/\$lt_dump_F\"
+ exit 0
+ ;;
+ --lt-*)
+ \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n \"\$lt_option_debug\"; then
+ echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
+ lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case \" \$* \" in
+ *\\ --lt-*)
+ for lt_wr_arg
+ do
+ case \$lt_wr_arg in
+ --lt-*) ;;
+ *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core \${1+\"\$@\"}
+}
+
+ # Parse options
+ func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test yes = "$fast_install"; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ \$ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # fixup the dll searchpath if we need to.
+ #
+ # Fix the DLL searchpath if we need to. Do this before prepending
+ # to shlibpath, because on Windows, both are PATH and uninstalled
+ # libraries must come first.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ # Export our shlibpath_var if we have one.
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+ func_exec_program \${1+\"\$@\"}
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+*/
+EOF
+ cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* declarations of non-ANSI functions */
+#if defined __MINGW32__
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined __CYGWIN__
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined other_platform || defined ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined _MSC_VER
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+# define S_IXUSR _S_IEXEC
+#elif defined __MINGW32__
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+#elif defined __CYGWIN__
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined other platforms ... */
+#endif
+
+#if defined PATH_MAX
+# define LT_PATHMAX PATH_MAX
+#elif defined MAXPATHLEN
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
+ defined __OS2__
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free (stale); stale = 0; } \
+} while (0)
+
+#if defined LT_DEBUGWRAPPER
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+ cat <<EOF
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
+# define externally_visible volatile
+#else
+# define externally_visible __attribute__((externally_visible)) volatile
+#endif
+externally_visible const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_path "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_path "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test yes = "$fast_install"; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ int rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ newargz = XMALLOC (char *, (size_t) argc + 1);
+
+ /* very simple arg parsing; don't want to rely on getopt
+ * also, copy all non cwrapper options to newargz, except
+ * argz[0], which is handled differently
+ */
+ newargc=0;
+ for (i = 1; i < argc; i++)
+ {
+ if (STREQ (argv[i], dumpscript_opt))
+ {
+EOF
+ case $host in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<"EOF"
+ lt_dump_script (stdout);
+ return 0;
+ }
+ if (STREQ (argv[i], debug_opt))
+ {
+ lt_debug = 1;
+ continue;
+ }
+ if (STREQ (argv[i], ltwrapper_option_prefix))
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal (__FILE__, __LINE__,
+ "unrecognized %s option: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+EOF
+ cat <<EOF
+ /* The GNU banner must be the first non-error debug message */
+ lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
+EOF
+ cat <<"EOF"
+ lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (before symlink chase) at: %s\n",
+ tmp_pathspec);
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (after symlink chase) at: %s\n",
+ actual_cwrapper_path);
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) libtool target name: %s\n",
+ target_name);
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must
+ be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+ because on Windows, both *_VARNAMEs are PATH but uninstalled
+ libraries must come first. */
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+ lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+ nonnull (lt_argv_zero));
+ for (i = 0; i < newargc; i++)
+ {
+ lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+ i, nonnull (newargz[i]));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ newargz = prepare_spawn (newargz);
+ rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) failed to launch target \"%s\": %s\n",
+ lt_argv_zero, nonnull (strerror (errno)));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, newargz);
+ return rval; /* =127, but avoids unused variable warning */
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ size_t tmp_len;
+ char *concat_name;
+
+ lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+ nonempty (wrapper));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = (size_t) (q - p);
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ lt_debugprintf (__FILE__, __LINE__,
+ "checking path component for symlinks: %s\n",
+ tmp_pathspec);
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "error accessing file \"%s\": %s",
+ tmp_pathspec, nonnull (strerror (errno)));
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (STREQ (str, pat))
+ *str = '\0';
+ }
+ return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ if (lt_debug)
+ {
+ (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+ }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+ int line, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+ va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+ return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+ return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_setenv) setting '%s' to '%s'\n",
+ nonnull (name), nonnull (value));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ size_t len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ size_t orig_value_len = strlen (orig_value);
+ size_t add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ size_t len = strlen (new_value);
+ while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[--len] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+EOF
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+ Note that spawn() does not by itself call the command interpreter
+ (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+ ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&v);
+ v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+ }) ? "cmd.exe" : "command.com").
+ Instead it simply concatenates the arguments, separated by ' ', and calls
+ CreateProcess(). We must quote the arguments since Win32 CreateProcess()
+ interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+ special way:
+ - Space and tab are interpreted as delimiters. They are not treated as
+ delimiters if they are surrounded by double quotes: "...".
+ - Unescaped double quotes are removed from the input. Their only effect is
+ that within double quotes, space and tab are treated like normal
+ characters.
+ - Backslashes not followed by double quotes are not special.
+ - But 2*n+1 backslashes followed by a double quote become
+ n backslashes followed by a double quote (n >= 0):
+ \" -> "
+ \\\" -> \"
+ \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+ size_t argc;
+ char **new_argv;
+ size_t i;
+
+ /* Count number of arguments. */
+ for (argc = 0; argv[argc] != NULL; argc++)
+ ;
+
+ /* Allocate new argument vector. */
+ new_argv = XMALLOC (char *, argc + 1);
+
+ /* Put quoted arguments into the new argument vector. */
+ for (i = 0; i < argc; i++)
+ {
+ const char *string = argv[i];
+
+ if (string[0] == '\0')
+ new_argv[i] = xstrdup ("\"\"");
+ else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+ {
+ int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+ size_t length;
+ unsigned int backslashes;
+ const char *s;
+ char *quoted_string;
+ char *p;
+
+ length = 0;
+ backslashes = 0;
+ if (quote_around)
+ length++;
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ length += backslashes + 1;
+ length++;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ length += backslashes + 1;
+
+ quoted_string = XMALLOC (char, length + 1);
+
+ p = quoted_string;
+ backslashes = 0;
+ if (quote_around)
+ *p++ = '"';
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ {
+ unsigned int j;
+ for (j = backslashes + 1; j > 0; j--)
+ *p++ = '\\';
+ }
+ *p++ = c;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ {
+ unsigned int j;
+ for (j = backslashes; j > 0; j--)
+ *p++ = '\\';
+ *p++ = '"';
+ }
+ *p = '\0';
+
+ new_argv[i] = quoted_string;
+ }
+ else
+ new_argv[i] = (char *) string;
+ }
+ new_argv[argc] = NULL;
+
+ return new_argv;
+}
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+ func_emit_wrapper yes |
+ $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/ fputs ("\1", f);/p
+g
+D'
+ cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+ $debug_cmd
+
+ case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+ *import*) : ;;
+ *) false ;;
+ esac
+}
+
+# func_suncc_cstd_abi
+# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
+# Several compiler flags select an ABI that is incompatible with the
+# Cstd library. Avoid specifying it if any are in CXXFLAGS.
+func_suncc_cstd_abi ()
+{
+ $debug_cmd
+
+ case " $compile_command " in
+ *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
+ suncc_use_cstd_abi=no
+ ;;
+ *)
+ suncc_use_cstd_abi=yes
+ ;;
+ esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $debug_cmd
+
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # what system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll that has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ bindir=
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ os2dllname=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=false
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module=$wl-single_module
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test yes != "$build_libtool_libs" \
+ && func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg=$1
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ bindir)
+ bindir=$arg
+ prev=
+ continue
+ ;;
+ dlfiles|dlprefiles)
+ $preload || {
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=:
+ }
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test no = "$dlself"; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test dlprefiles = "$prev"; then
+ dlself=yes
+ elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test dlfiles = "$prev"; then
+ func_append dlfiles " $arg"
+ else
+ func_append dlprefiles " $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols=$arg
+ test -f "$arg" \
+ || func_fatal_error "symbol file '$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex=$arg
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) func_append deplibs " $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir=$arg
+ prev=
+ continue
+ ;;
+ mllvm)
+ # Clang does not use LLVM to link, so we can simply discard any
+ # '-mllvm $arg' options when doing the link step.
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# func_append moreargs " $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ if test none != "$pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ fi
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file '$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ os2dllname)
+ os2dllname=$arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex=$arg
+ prev=
+ continue
+ ;;
+ release)
+ release=-$arg
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test rpath = "$prev"; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) func_append rpath " $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) func_append xrpath " $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds=$arg
+ prev=
+ continue
+ ;;
+ weak)
+ func_append weak_libs " $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg=$arg
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "'-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -bindir)
+ prev=bindir
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test X-export-symbols = "X$arg"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname "-L" '' "$arg"
+ if test -z "$func_stripname_result"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between '-L' and '$1'"
+ else
+ func_fatal_error "need path for '-L' option"
+ fi
+ fi
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of '$dir'"
+ dir=$absdir
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "* | *" $arg "*)
+ # Will only happen for absolute or sysroot arguments
+ ;;
+ *)
+ # Preserve sysroot, but never include relative directories
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+ *) func_append deplibs " -L$dir" ;;
+ esac
+ func_append lib_search_path " $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) func_append dllsearchpath ":$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test X-lc = "X$arg" || test X-lm = "X$arg"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc due to us having libc/libc_r.
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ func_append deplibs " System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test X-lc = "X$arg" && continue
+ ;;
+ esac
+ elif test X-lc_r = "X$arg"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ func_append deplibs " $arg"
+ continue
+ ;;
+
+ -mllvm)
+ prev=mllvm
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot|--sysroot)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) func_append new_inherited_linker_flags " $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module=$wl-multi_module
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "'-no-install' is ignored for $host"
+ func_warning "assuming '-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -os2dllname)
+ prev=os2dllname
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ =*)
+ func_stripname '=' '' "$dir"
+ dir=$lt_sysroot$func_stripname_result
+ ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $func_quote_for_eval_result"
+ func_append compiler_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $wl$func_quote_for_eval_result"
+ func_append compiler_flags " $wl$func_quote_for_eval_result"
+ func_append linker_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ # Flags to be passed through unchanged, with rationale:
+ # -64, -mips[0-9] enable 64-bit mode for the SGI compiler
+ # -r[0-9][0-9]* specify processor for the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+ # +DA*, +DD* enable 64-bit mode for the HP compiler
+ # -q* compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+ # -F/path path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* profiling flags for GCC
+ # -fstack-protector* stack protector flags for GCC
+ # @file GCC response files
+ # -tp=* Portland pgcc target processor selection
+ # --sysroot=* for sysroot support
+ # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -specs=* GCC specs files
+ # -stdlib=* select c++ std lib with clang
+ # -fsanitize=* Clang/GCC memory and address sanitizer
+ # -fuse-ld=* Linker select flags for GCC
+ # -static-* direct GCC to link specific libraries statically
+ # -fcilkplus Cilk Plus language extension features for C/C++
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
+ -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ func_append compiler_flags " $arg"
+ continue
+ ;;
+
+ -Z*)
+ if test os2 = "`expr $host : '.*\(os2\)'`"; then
+ # OS/2 uses -Zxxx to specify OS/2-specific options
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case $arg in
+ -Zlinker | -Zstack)
+ prev=xcompiler
+ ;;
+ esac
+ continue
+ else
+ # Otherwise treat like 'Some other compiler flag' below
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ fi
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ *.$objext)
+ # A standard object.
+ func_append objs " $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ test none = "$pic_object" || {
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ }
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ func_append deplibs " $arg"
+ func_append old_deplibs " $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ func_resolve_sysroot "$arg"
+ if test dlfiles = "$prev"; then
+ # This library was specified with -dlopen.
+ func_append dlfiles " $func_resolve_sysroot_result"
+ prev=
+ elif test dlprefiles = "$prev"; then
+ # The library was specified with -dlpreopen.
+ func_append dlprefiles " $func_resolve_sysroot_result"
+ prev=
+ else
+ func_append deplibs " $func_resolve_sysroot_result"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prevarg' option requires an argument"
+
+ if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname=$func_basename_result
+ libobjs_save=$libobjs
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ # Definition is injected by LT_CONFIG during libtool generation.
+ func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
+
+ func_dirname "$output" "/" ""
+ output_objdir=$func_dirname_result$objdir
+ func_to_tool_file "$output_objdir/"
+ tool_output_objdir=$func_to_tool_file_result
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_preserve_dup_deps; then
+ case "$libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append libs " $deplib"
+ done
+
+ if test lib = "$linkmode"; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+ esac
+ func_append pre_post_deps " $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=false
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test lib,link = "$linkmode,$pass"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs=$tmp_deplibs
+ fi
+
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass"; then
+ libs=$deplibs
+ deplibs=
+ fi
+ if test prog = "$linkmode"; then
+ case $pass in
+ dlopen) libs=$dlfiles ;;
+ dlpreopen) libs=$dlprefiles ;;
+ link)
+ libs="$deplibs %DEPLIBS%"
+ test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs"
+ ;;
+ esac
+ fi
+ if test lib,dlpreopen = "$linkmode,$pass"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ func_resolve_sysroot "$lib"
+ case $lib in
+ *.la) func_source "$func_resolve_sysroot_result" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ func_basename "$deplib"
+ deplib_base=$func_basename_result
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) func_append deplibs " $deplib" ;;
+ esac
+ done
+ done
+ libs=$dlprefiles
+ fi
+ if test dlopen = "$pass"; then
+ # Collect dlpreopened libraries
+ save_deplibs=$deplibs
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=false
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append compiler_flags " $deplib"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test lib != "$linkmode" && test prog != "$linkmode"; then
+ func_warning "'-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test lib = "$linkmode"; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib=$searchdir/lib$name$search_ext
+ if test -f "$lib"; then
+ if test .la = "$search_ext"; then
+ found=:
+ else
+ found=false
+ fi
+ break 2
+ fi
+ done
+ done
+ if $found; then
+ # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll=$l
+ done
+ if test "X$ll" = "X$old_library"; then # only static version available
+ found=false
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+ lib=$ladir/$old_library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ else
+ # deplib doesn't seem to be a libtool library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ ;; # -l
+ *.ltframework)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test conv = "$pass" && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ prog)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test scan = "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ *)
+ func_warning "'-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test link = "$pass"; then
+ func_stripname '-R' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ lib=$func_resolve_sysroot_result
+ ;;
+ *.$libext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=false
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=:
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=:
+ ;;
+ esac
+ if $valid_a_lib; then
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ else
+ echo
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because the file extensions .$libext of this argument makes me believe"
+ echo "*** that it is just a static archive that I should not use here."
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test link != "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ elif test prog = "$linkmode"; then
+ if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ func_append newdlprefiles " $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append newdlfiles " $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=:
+ continue
+ ;;
+ esac # case $deplib
+
+ $found || test -f "$lib" \
+ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "'$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass" ||
+ { test prog != "$linkmode" && test lib != "$linkmode"; }; then
+ test -n "$dlopen" && func_append dlfiles " $dlopen"
+ test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+ fi
+
+ if test conv = "$pass"; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ func_append convenience " $ladir/$objdir/$old_library"
+ func_append old_convenience " $ladir/$objdir/$old_library"
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done
+ elif test prog != "$linkmode" && test lib != "$linkmode"; then
+ func_fatal_error "'$lib' is not a convenience library"
+ fi
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ if test -n "$old_library" &&
+ { test yes = "$prefer_static_libs" ||
+ test built,no = "$prefer_static_libs,$installed"; }; then
+ linklib=$old_library
+ else
+ for l in $old_library $library_names; do
+ linklib=$l
+ done
+ fi
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test dlopen = "$pass"; then
+ test -z "$libdir" \
+ && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
+ if test -z "$dlname" ||
+ test yes != "$dlopen_support" ||
+ test no = "$build_libtool_libs"
+ then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ func_append dlprefiles " $lib $dependency_libs"
+ else
+ func_append newdlfiles " $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of '$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir=$ladir
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname=$func_basename_result
+
+ # Find the relevant object directory and library name.
+ if test yes = "$installed"; then
+ if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library '$lib' was moved."
+ dir=$ladir
+ absdir=$abs_ladir
+ libdir=$abs_ladir
+ else
+ dir=$lt_sysroot$libdir
+ absdir=$lt_sysroot$libdir
+ fi
+ test yes = "$hardcode_automatic" && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir=$ladir
+ absdir=$abs_ladir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ else
+ dir=$ladir/$objdir
+ absdir=$abs_ladir/$objdir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test dlpreopen = "$pass"; then
+ if test -z "$libdir" && test prog = "$linkmode"; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
+ fi
+ case $host in
+ # special handling for platforms with PE-DLLs.
+ *cygwin* | *mingw* | *cegcc* )
+ # Linker will automatically link against shared library if both
+ # static and shared are present. Therefore, ensure we extract
+ # symbols from the import library if a shared library is present
+ # (otherwise, the dlopen module name will be incorrect). We do
+ # this by putting the import library name into $newdlprefiles.
+ # We recover the dlopen module name by 'saving' the la file
+ # name in a special purpose variable, and (later) extracting the
+ # dlname from the la file.
+ if test -n "$dlname"; then
+ func_tr_sh "$dir/$linklib"
+ eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+ func_append newdlprefiles " $dir/$linklib"
+ else
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ fi
+ ;;
+ * )
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ func_append newdlprefiles " $dir/$dlname"
+ else
+ func_append newdlprefiles " $dir/$linklib"
+ fi
+ ;;
+ esac
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test lib = "$linkmode"; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test prog = "$linkmode" && test link != "$pass"; then
+ func_append newlib_search_path " $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=false
+ if test no != "$link_all_deplibs" || test -z "$library_names" ||
+ test no = "$build_libtool_libs"; then
+ linkalldeplibs=:
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if $linkalldeplibs; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test prog,link = "$linkmode,$pass"; then
+ if test -n "$library_names" &&
+ { { test no = "$prefer_static_libs" ||
+ test built,yes = "$prefer_static_libs,$installed"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
+ # Make sure the rpath contains only unique directories.
+ case $temp_rpath: in
+ *"$absdir:"*) ;;
+ *) func_append temp_rpath "$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if $alldeplibs &&
+ { test pass_all = "$deplibs_check_method" ||
+ { test yes = "$build_libtool_libs" &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test built = "$use_static_libs" && test yes = "$installed"; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test no = "$use_static_libs" || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw* | *cegcc* | *os2*)
+ # No point in relinking DLLs because paths are not encoded
+ func_append notinst_deplibs " $lib"
+ need_relink=no
+ ;;
+ *)
+ if test no = "$installed"; then
+ func_append notinst_deplibs " $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule=$dlpremoduletest
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
+ echo
+ if test prog = "$linkmode"; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test lib = "$linkmode" &&
+ test yes = "$hardcode_into_libs"; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname=$dlname
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw* | *cegcc* | *os2*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot=$soname
+ func_basename "$soroot"
+ soname=$func_basename_result
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from '$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for '$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test prog = "$linkmode" || test relink != "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test no = "$hardcode_direct"; then
+ add=$dir/$linklib
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
+ *-*-sysv4*uw2*) add_dir=-L$dir ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir=-L$dir ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we cannot
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library"; then
+ echo
+ echo "*** And there doesn't seem to be a static archive available"
+ echo "*** The link will probably fail, sorry"
+ else
+ add=$dir/$old_library
+ fi
+ elif test -n "$old_library"; then
+ add=$dir/$old_library
+ fi
+ fi
+ esac
+ elif test no = "$hardcode_minus_L"; then
+ case $host in
+ *-*-sunos*) add_shlibpath=$dir ;;
+ esac
+ add_dir=-L$dir
+ add=-l$name
+ elif test no = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$dir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$absdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test yes != "$lib_linked"; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) func_append compile_shlibpath "$add_shlibpath:" ;;
+ esac
+ fi
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test yes != "$hardcode_direct" &&
+ test yes != "$hardcode_minus_L" &&
+ test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test prog = "$linkmode" || test relink = "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$libdir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$libdir
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ add=-l$name
+ elif test yes = "$hardcode_automatic"; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib"; then
+ add=$inst_prefix_dir$libdir/$linklib
+ else
+ add=$libdir/$linklib
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir=-L$libdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ fi
+
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test prog = "$linkmode"; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test unsupported != "$hardcode_direct"; then
+ test -n "$old_library" && linklib=$old_library
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test yes = "$build_libtool_libs"; then
+ # Not a shared library
+ if test pass_all != "$deplibs_check_method"; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ echo
+ $ECHO "*** Warning: This system cannot link to static lib archive $lib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ if test yes = "$module"; then
+ echo "*** But as you try to build a module library, libtool will still create "
+ echo "*** a static module, that should work as long as the dlopening application"
+ echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test lib = "$linkmode"; then
+ if test -n "$dependency_libs" &&
+ { test yes != "$hardcode_into_libs" ||
+ test yes = "$build_old_libs" ||
+ test yes = "$link_static"; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) func_append xrpath " $temp_xrpath";;
+ esac;;
+ *) func_append temp_deplibs " $libdir";;
+ esac
+ done
+ dependency_libs=$temp_deplibs
+ fi
+
+ func_append newlib_search_path " $absdir"
+ # Link against this library
+ test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result";;
+ *) func_resolve_sysroot "$deplib" ;;
+ esac
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $func_resolve_sysroot_result "*)
+ func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+ esac
+ fi
+ func_append tmp_libs " $func_resolve_sysroot_result"
+ done
+
+ if test no != "$link_all_deplibs"; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ path=
+ case $deplib in
+ -L*) path=$deplib ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ deplib=$func_resolve_sysroot_result
+ func_dirname "$deplib" "" "."
+ dir=$func_dirname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of '$dir'"
+ absdir=$dir
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names"; then
+ for tmp in $deplibrary_names; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl"; then
+ depdepl=$absdir/$objdir/$depdepl
+ darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
+ func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path=-L$absdir/$objdir
+ ;;
+ esac
+ else
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "'$deplib' seems to be moved"
+
+ path=-L$absdir
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test link = "$pass"; then
+ if test prog = "$linkmode"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs=$newdependency_libs
+ if test dlpreopen = "$pass"; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test dlopen != "$pass"; then
+ test conv = "$pass" || {
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) func_append lib_search_path " $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ }
+
+ if test prog,link = "$linkmode,$pass"; then
+ vars="compile_deplibs finalize_deplibs"
+ else
+ vars=deplibs
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+
+ # Add Sun CC postdeps if required:
+ test CXX = "$tagname" && {
+ case $host_os in
+ linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C++ 5.9
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ func_cc_basename "$CC"
+ case $func_cc_basename_result in
+ CC* | sunCC*)
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ }
+
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=
+ ;;
+ esac
+ if test -n "$i"; then
+ func_append tmp_libs " $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test prog = "$linkmode"; then
+ dlfiles=$newdlfiles
+ fi
+ if test prog = "$linkmode" || test lib = "$linkmode"; then
+ dlprefiles=$newdlprefiles
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "'-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs=$output
+ func_append objs "$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form 'libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test no = "$module" \
+ && func_fatal_help "libtool library '$output' must begin with 'lib'"
+
+ if test no != "$need_lib_prefix"; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test pass_all != "$deplibs_check_method"; then
+ func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
+ else
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ func_append libobjs " $objs"
+ fi
+ fi
+
+ test no = "$dlself" \
+ || func_warning "'-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test 1 -lt "$#" \
+ && func_warning "ignoring multiple '-rpath's for a libtool library"
+
+ install_libdir=$1
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test yes = "$build_libtool_libs"; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a '.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs=$IFS; IFS=:
+ set dummy $vinfo 0 0 0
+ shift
+ IFS=$save_ifs
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to '-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major=$1
+ number_minor=$2
+ number_revision=$3
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # that has an extra 1 added just for fun
+ #
+ case $version_type in
+ # correct linux to gnu/linux during the next big refactor
+ darwin|freebsd-elf|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_revision
+ ;;
+ freebsd-aout|qnx|sunos)
+ current=$number_major
+ revision=$number_minor
+ age=0
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_minor
+ lt_irix_increment=no
+ ;;
+ *)
+ func_fatal_configuration "$modename: unknown library version type '$version_type'"
+ ;;
+ esac
+ ;;
+ no)
+ current=$1
+ revision=$2
+ age=$3
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT '$current' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION '$revision' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE '$age' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE '$age' is greater than the current interface number '$current'"
+ func_fatal_error "'$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ # On Darwin other compilers
+ case $CC in
+ nagfor*)
+ verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ ;;
+ *)
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+ esac
+ ;;
+
+ freebsd-aout)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ freebsd-elf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ irix | nonstopux)
+ if test no = "$lt_irix_increment"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring=$verstring_prefix$major.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test 0 -ne "$loop"; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring_prefix$major.$iface:$verstring
+ done
+
+ # Before this point, $major must not contain '.'.
+ major=.$major
+ versuffix=$major.$revision
+ ;;
+
+ linux) # correct to gnu/linux during the next big refactor
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=.$current.$age.$revision
+ verstring=$current.$age.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test 0 -ne "$loop"; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring:$iface.0
+ done
+
+ # Make executables depend on our current version.
+ func_append verstring ":$current.0"
+ ;;
+
+ qnx)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sco)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sunos)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 file systems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type '$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring=0.0
+ ;;
+ esac
+ if test no = "$need_version"; then
+ versuffix=
+ else
+ versuffix=.0.0
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test yes,no = "$avoid_version,$need_version"; then
+ major=
+ versuffix=
+ verstring=
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test yes = "$allow_undefined"; then
+ if test unsupported = "$allow_undefined_flag"; then
+ if test yes = "$build_old_libs"; then
+ func_warning "undefined symbols not allowed in $host shared libraries; building static only"
+ build_libtool_libs=no
+ else
+ func_fatal_error "can't build $host shared library unless -no-undefined is specified"
+ fi
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag=$no_undefined_flag
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" :
+ func_append libobjs " $symfileobj"
+ test " " = "$libobjs" && libobjs=
+
+ if test relink != "$opt_mode"; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext | *.gcno)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
+ if test -n "$precious_files_regex"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ func_append removelist " $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
+ func_append oldlibs " $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+ # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ func_replace_sysroot "$libdir"
+ func_append temp_xrpath " -R$func_replace_sysroot_result"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles=$dlfiles
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) func_append dlfiles " $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles=$dlprefiles
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) func_append dlprefiles " $lib" ;;
+ esac
+ done
+
+ if test yes = "$build_libtool_libs"; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ func_append deplibs " System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test yes = "$build_libtool_need_lc"; then
+ func_append deplibs " -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=
+ versuffix=
+ major=
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which I believe you do not have"
+ echo "*** because a test_compile did reveal that the linker did not use it for"
+ echo "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because a test_compile did reveal that the linker did not use this one"
+ echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ echo "*** make it link in! You will probably need to install it or some"
+ echo "*** library that it depends on before this library will be fully"
+ echo "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ if test -n "$file_magic_glob"; then
+ libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+ else
+ libnameglob=$libname
+ fi
+ test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ if test yes = "$want_nocaseglob"; then
+ shopt -s nocaseglob
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ $nocaseglob
+ else
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ fi
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib=$potent_lib
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
+ *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib=$potent_lib # see symlink-check above in file_magic test
+ if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=
+ tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ for i in $predeps $postdeps; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
+ done
+ fi
+ case $tmp_deplibs in
+ *[!\ \ ]*)
+ echo
+ if test none = "$deplibs_check_method"; then
+ echo "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ echo "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ echo "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ ;;
+ esac
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test yes = "$droppeddeps"; then
+ if test yes = "$module"; then
+ echo
+ echo "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ echo "*** a static module, that should work as long as the dlopening"
+ echo "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ echo "*** The inter-library dependencies that have been dropped here will be"
+ echo "*** automatically added whenever a program is linked with this library"
+ echo "*** or is declared to -dlopen it."
+
+ if test no = "$allow_undefined"; then
+ echo
+ echo "*** Since this library must not contain undefined symbols,"
+ echo "*** because either the platform does not support them or"
+ echo "*** it was explicitly requested with -no-undefined,"
+ echo "*** libtool will only create a static version of it."
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ deplibs=$new_libs
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test yes = "$build_libtool_libs"; then
+ # Remove $wl instances when linking with ld.
+ # FIXME: should test the right _cmds variable.
+ case $archive_cmds in
+ *\$LD\ *) wl= ;;
+ esac
+ if test yes = "$hardcode_into_libs"; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath=$finalize_rpath
+ test relink = "$opt_mode" || rpath=$compile_rpath$rpath
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ func_replace_sysroot "$libdir"
+ libdir=$func_replace_sysroot_result
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append dep_rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath=$finalize_shlibpath
+ test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib=$output_objdir/$realname
+ linknames=
+ for link
+ do
+ func_append linknames " $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols=$output_objdir/$libname.uexp
+ func_append delfiles " $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ func_dll_def_p "$export_symbols" || {
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols=$export_symbols
+ export_symbols=
+ always_export_symbols=yes
+ }
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs=$IFS; IFS='~'
+ for cmd1 in $cmds; do
+ IFS=$save_ifs
+ # Take the normal branch if the nm_file_list_spec branch
+ # doesn't work or if tool conversion is not needed.
+ case $nm_file_list_spec~$to_tool_file_cmd in
+ *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+ try_normal_branch=yes
+ eval cmd=\"$cmd1\"
+ func_len " $cmd"
+ len=$func_len_result
+ ;;
+ *)
+ try_normal_branch=no
+ ;;
+ esac
+ if test yes = "$try_normal_branch" \
+ && { test "$len" -lt "$max_cmd_len" \
+ || test "$max_cmd_len" -le -1; }
+ then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ elif test -n "$nm_file_list_spec"; then
+ func_basename "$output"
+ output_la=$func_basename_result
+ save_libobjs=$libobjs
+ save_output=$output
+ output=$output_objdir/$output_la.nm
+ func_to_tool_file "$output"
+ libobjs=$nm_file_list_spec$func_to_tool_file_result
+ func_append delfiles " $output"
+ func_verbose "creating $NM input file list: $output"
+ for obj in $save_libobjs; do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > "$output"
+ eval cmd=\"$cmd1\"
+ func_show_eval "$cmd" 'exit $?'
+ output=$save_output
+ libobjs=$save_libobjs
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS=$save_ifs
+ if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ func_append tmp_deplibs " $test_deplib"
+ ;;
+ esac
+ done
+ deplibs=$tmp_deplibs
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test yes = "$compiler_needs_object" &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ func_append linker_flags " $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test : != "$skipped_export" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ func_basename "$output"
+ output_la=$func_basename_result
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+ output=$output_objdir/$output_la.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ echo 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ echo ')' >> $output
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$func_to_tool_file_result
+ elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ output=$output_objdir/$output_la.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test yes = "$compiler_needs_object"; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-$k.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test -z "$objlist" ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test 1 -eq "$k"; then
+ # The first file doesn't have a previous command to add.
+ reload_objs=$objlist
+ eval concat_cmds=\"$reload_cmds\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-$k.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-$k.$objext
+ objlist=" $obj"
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds$reload_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ func_append delfiles " $output"
+
+ else
+ output=
+ fi
+
+ ${skipped_export-false} && {
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ }
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs=$IFS; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ ${skipped_export-false} && {
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ }
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $cmds; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test yes = "$module" || test yes = "$export_dynamic"; then
+ # On all known operating systems, these are identical.
+ dlname=$soname
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object '$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj=$output
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # if reload_cmds runs $LD directly, get rid of -Wl from
+ # whole_archive_flag_spec and hope we can get by with turning comma
+ # into space.
+ case $reload_cmds in
+ *\$LD[\ \$]*) wl= ;;
+ esac
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
+ else
+ gentop=$output_objdir/${obj}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # If we're not building shared, we need to use non_pic_objs
+ test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
+
+ # Create the old-style object.
+ reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
+
+ output=$obj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ test yes = "$build_libtool_libs" || {
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ }
+
+ if test -n "$pic_flag" || test default != "$pic_mode"; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output=$libobj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for programs"
+
+ $preload \
+ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
+ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test CXX = "$tagname"; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ func_append compile_command " $wl-bind_at_load"
+ func_append finalize_command " $wl-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ compile_deplibs=$new_libs
+
+
+ func_append compile_command " $compile_deplibs"
+ func_append finalize_command " $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) func_append dllsearchpath ":$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath=$rpath
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath=$rpath
+
+ if test -n "$libobjs" && test yes = "$build_old_libs"; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" false
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=:
+ case $host in
+ *cegcc* | *mingw32ce*)
+ # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+ wrappers_required=false
+ ;;
+ *cygwin* | *mingw* )
+ test yes = "$build_libtool_libs" || wrappers_required=false
+ ;;
+ *)
+ if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
+ wrappers_required=false
+ fi
+ ;;
+ esac
+ $wrappers_required || {
+ # Replace the output file specification.
+ compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ link_command=$compile_command$compile_rpath
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.$objext"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
+ fi
+
+ exit $exit_status
+ }
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test yes = "$no_install"; then
+ # We don't need to create a wrapper script.
+ link_command=$compile_var$compile_command$compile_rpath
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ case $hardcode_action,$fast_install in
+ relink,*)
+ # Fast installation is not supported
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "'$output' will be relinked during installation"
+ ;;
+ *,yes)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+ ;;
+ *,no)
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+ ;;
+ *,needless)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=
+ ;;
+ esac
+
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output_objdir/$outputname"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource=$output_path/$objdir/lt-$output_name.c
+ cwrapper=$output_path/$output_name.exe
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host"; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ case $build_libtool_libs in
+ convenience)
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs=$convenience
+ build_libtool_libs=no
+ ;;
+ module)
+ oldobjs=$libobjs_save
+ addlibs=$old_convenience
+ build_libtool_libs=no
+ ;;
+ *)
+ oldobjs="$old_deplibs $non_pic_objects"
+ $preload && test -f "$symfileobj" \
+ && func_append oldobjs " $symfileobj"
+ addlibs=$old_convenience
+ ;;
+ esac
+
+ if test -n "$addlibs"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $addlibs
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ echo "copying selected object files to avoid basename conflicts..."
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase=$func_basename_result
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ func_append oldobjs " $gentop/$newobj"
+ ;;
+ *) func_append oldobjs " $obj" ;;
+ esac
+ done
+ fi
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ elif test -n "$archiver_list_spec"; then
+ func_verbose "using command file archive linking..."
+ for obj in $oldobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > $output_objdir/$libname.libcmd
+ func_to_tool_file "$output_objdir/$libname.libcmd"
+ oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj"; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test -z "$oldobjs"; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test yes = "$build_old_libs" && old_library=$libname.$libext
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ if test yes = "$hardcode_automatic"; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test yes = "$installed"; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output=$output_objdir/${outputname}i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name=$func_basename_result
+ func_resolve_sysroot "$deplib"
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ -L*)
+ func_stripname -L '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -L$func_replace_sysroot_result"
+ ;;
+ -R*)
+ func_stripname -R '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -R$func_replace_sysroot_result"
+ ;;
+ *) func_append newdependency_libs " $deplib" ;;
+ esac
+ done
+ dependency_libs=$newdependency_libs
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ *) func_append newdlfiles " $lib" ;;
+ esac
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles=$newdlprefiles
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlfiles " $abs"
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlprefiles " $abs"
+ done
+ dlprefiles=$newdlprefiles
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ # In fact, it would be nice if we could use this code for all target
+ # systems that can't hard-code library paths into their executables
+ # and that have no shared library path variable independent of PATH,
+ # but it turns out we can't easily determine that from inspecting
+ # libtool variables, so we have to hard-code the OSs to which it
+ # applies here; at the moment, that means platforms that use the PE
+ # object format with DLL files. See the long comment at the top of
+ # tests/bindir.at for full details.
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ # If a -bindir argument was supplied, place the dll there.
+ if test -n "$bindir"; then
+ func_relative_path "$install_libdir" "$bindir"
+ tdlname=$func_relative_path_result/$dlname
+ else
+ # Otherwise fall back on heuristic.
+ tdlname=../bin/$dlname
+ fi
+ ;;
+ esac
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that cannot go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test no,yes = "$installed,$need_relink"; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+if test link = "$opt_mode" || test relink = "$opt_mode"; then
+ func_mode_link ${1+"$@"}
+fi
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $debug_cmd
+
+ RM=$nonopt
+ files=
+ rmforce=false
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ for arg
+ do
+ case $arg in
+ -f) func_append RM " $arg"; rmforce=: ;;
+ -*) func_append RM " $arg" ;;
+ *) func_append files " $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ if test . = "$dir"; then
+ odir=$objdir
+ else
+ odir=$dir/$objdir
+ fi
+ func_basename "$file"
+ name=$func_basename_result
+ test uninstall = "$opt_mode" && odir=$dir
+
+ # Remember odir for removal later, being careful to avoid duplicates
+ if test clean = "$opt_mode"; then
+ case " $rmdirs " in
+ *" $odir "*) ;;
+ *) func_append rmdirs " $odir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif $rmforce; then
+ continue
+ fi
+
+ rmfiles=$file
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ func_append rmfiles " $odir/$n"
+ done
+ test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+ case $opt_mode in
+ clean)
+ case " $library_names " in
+ *" $dlname "*) ;;
+ *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+ esac
+ test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" && test none != "$pic_object"; then
+ func_append rmfiles " $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" && test none != "$non_pic_object"; then
+ func_append rmfiles " $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test clean = "$opt_mode"; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ func_append rmfiles " $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ func_append rmfiles " $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ func_append rmfiles " $odir/$name $odir/${name}S.$objext"
+ if test yes = "$fast_install" && test -n "$relink_command"; then
+ func_append rmfiles " $odir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name"; then
+ func_append rmfiles " $odir/lt-$noexename.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+
+ # Try to remove the $objdir's in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
+ func_mode_uninstall ${1+"$@"}
+fi
+
+test -z "$opt_mode" && {
+ help=$generic_help
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode '$opt_mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# where we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644
index 0000000..5bebe4e
--- /dev/null
+++ b/m4/ax_check_compile_flag.m4
@@ -0,0 +1,72 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# 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/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
new file mode 100644
index 0000000..7e1de72
--- /dev/null
+++ b/m4/ax_check_link_flag.m4
@@ -0,0 +1,71 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the linker or gives an error.
+# (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# 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/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 2
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+ ax_check_save_flags=$LDFLAGS
+ LDFLAGS="$LDFLAGS $4 $1"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM()],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ LDFLAGS=$ax_check_save_flags])
+AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/code-coverage.m4 b/m4/code-coverage.m4
new file mode 100644
index 0000000..ae4f416
--- /dev/null
+++ b/m4/code-coverage.m4
@@ -0,0 +1,45 @@
+# Copyright 2014 CZ.NIC, z.s.p.o.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 3, as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranties of
+# MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
+
+AC_DEFUN([AX_CODE_COVERAGE], [
+ dnl Check for --enable-code-coverage
+ AC_ARG_ENABLE([code-coverage],
+ AS_HELP_STRING([--enable-code-coverage], [enable code coverage testing with gcov]),
+ [enable_code_coverage=$enableval],
+ [enable_code_coverage=no]
+ )
+ AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test "$enable_code_coverage" = "yes"])
+ AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
+
+ AS_IF([test "$enable_code_coverage" = "yes"], [
+ AC_CHECK_PROG([LCOV], [lcov], [lcov])
+ AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
+
+ AS_IF([test -z "$LCOV"], [
+ AC_MSG_ERROR([Could not find lcov])
+ ])
+ AS_IF([test -z "$GENHTML"], [
+ AC_MSG_ERROR([Could not find genhtml from the lcov package])
+ ])
+
+ dnl Remove all optimization flags from CFLAGS
+ changequote({,})
+ CFLAGS=`echo "$CFLAGS" | $SED -e 's/-O[0-9]*//g'`
+ changequote([,])
+
+ dnl Add the coverage flags (clang, gcc)
+ CFLAGS="$CFLAGS --coverage"
+ LDFLAGS="$LDFLAGS --coverage"
+ ])
+]) # AC_CODE_COVERAGE
diff --git a/m4/knot-lib-version.m4 b/m4/knot-lib-version.m4
new file mode 100644
index 0000000..1627957
--- /dev/null
+++ b/m4/knot-lib-version.m4
@@ -0,0 +1,16 @@
+# KNOT_LIB_VERSION([library-name], [current], [revision], [age])
+# --------------------------------------------------------------
+# See https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
+
+AC_DEFUN([KNOT_LIB_VERSION],
+[
+ m4_define([LIBNAME], patsubst($1, [^lib], []))dnl
+ AC_SUBST([$1_VERSION_INFO], ["-version-info $2:$3:$4"])
+ AC_SUBST([$1_SOVERSION], ["$2"])
+ AS_CASE([$host_os],
+ [darwin*], [AC_SUBST([$1_SONAME], ["$1.$2.dylib"])],
+ [cygwin*], [AC_SUBST([$1_SONAME], [["cyg]LIBNAME[-$2.dll"]])],
+ [msys*], [AC_SUBST([$1_SONAME], [["msys-]LIBNAME[-$2.dll"]])],
+ [*], [AC_SUBST([$1_SONAME], ["$1.so.$2"])]
+ )
+])
diff --git a/m4/knot-module.m4 b/m4/knot-module.m4
new file mode 100644
index 0000000..5893830
--- /dev/null
+++ b/m4/knot-module.m4
@@ -0,0 +1,36 @@
+# KNOT_MODULE([module-name], [default], [non-shareable])
+# ------------------------------------------------------
+# Set Knot DNS module building
+
+AC_DEFUN([KNOT_MODULE],
+[
+ AC_ARG_WITH([module-$1],
+ AS_HELP_STRING([--with-module-$1=yes|shared|no], [Build '$1' module [default=$2]]),
+ [module=$withval],
+ AS_IF([test "$enable_modules" = "no"],
+ [module=no],
+ [module=$2]
+ )
+ )
+
+ doc_modules="${doc_modules}.. include:: modules/$1/$1.rst\n"
+
+ STATIC_MODULE_$1=no
+ SHARED_MODULE_$1=no
+ AS_CASE([$module],
+ [yes], [STATIC_MODULE_$1=yes
+ static_modules="${static_modules}$1 "
+ static_modules_declars="${static_modules_declars}extern const knotd_mod_api_t knotd_mod_api_$1;\n"
+ static_modules_init="${static_modules_init}\\\\\n\t{ &knotd_mod_api_$1 },"],
+ [shared], [SHARED_MODULE_$1=yes
+ shared_modules="${shared_modules}$1 "
+ AS_IF([test "$3" = "non-shareable"],
+ [AC_MSG_ERROR([Module $1 cannot be shared])])
+ AS_IF([test "$enable_shared" != "yes"],
+ [AC_MSG_ERROR([Shared module $1 requires shared libraries])])],
+ [no], [],
+ [*], [AC_MSG_ERROR([Invalid value '$module' for --with-module-$1])]
+ )
+ AM_CONDITIONAL([STATIC_MODULE_$1], [test "$STATIC_MODULE_$1" = "yes"])
+ AM_CONDITIONAL([SHARED_MODULE_$1], [test "$SHARED_MODULE_$1" = "yes"])
+])
diff --git a/m4/knot-version.m4 b/m4/knot-version.m4
new file mode 100644
index 0000000..f39486f
--- /dev/null
+++ b/m4/knot-version.m4
@@ -0,0 +1,23 @@
+################################################################################
+# Knot DNS versions are as follows
+#
+# <MAJOR>.<MINOR>.dev[.<TIMESTAMP>.<HASH>] Build from the master branch
+# <MAJOR>.<MINOR>.<PATCH>[.<TIMESTAMP>.<HASH>] Build from a feature branch
+#
+# To force release version format set env variable KNOT_VERSION_FORMAT=release
+#
+# If the repository is not available or if HEAD is tagged,
+# the optional part is missing!
+#
+# Example: 2.7.dev.1521027664.5e69ccc
+################################################################################
+
+m4_define([knot_PATCH], m4_ifblank(knot_VERSION_PATCH, [dev], knot_VERSION_PATCH))dnl
+
+m4_define([knot_GIT_HASH], m4_esyscmd_s(git rev-parse --short HEAD 2>/dev/null))dnl
+m4_define([knot_GIT_TAG], m4_esyscmd_s(git describe --exact-match 2>/dev/null))dnl
+m4_define([knot_TIMESTAMP], m4_esyscmd_s(date -u +'%s' 2>/dev/null))dnl
+m4_define([knot_GIT_OK], m4_case(m4_esyscmd_s(echo $KNOT_VERSION_FORMAT 2>/dev/null), release, [], knot_GIT_HASH))dnl
+m4_define([knot_GIT_INFO], m4_ifblank(knot_GIT_TAG, m4_ifnblank(knot_GIT_OK, .knot_TIMESTAMP.knot_GIT_HASH, []), []))dnl
+
+m4_define([knot_PKG_VERSION], [knot_VERSION_MAJOR.knot_VERSION_MINOR.knot_PATCH]knot_GIT_INFO)dnl
diff --git a/m4/libtool.m4 b/m4/libtool.m4
new file mode 100644
index 0000000..c4c0294
--- /dev/null
+++ b/m4/libtool.m4
@@ -0,0 +1,8394 @@
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file 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.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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/>.
+])
+
+# serial 58 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_PREPARE_CC_BASENAME
+# -----------------------
+m4_defun([_LT_PREPARE_CC_BASENAME], [
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in @S|@*""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+])# _LT_PREPARE_CC_BASENAME
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME,
+# but that macro is also expanded into generated libtool script, which
+# arranges for $SED and $ECHO to be set by different means.
+m4_defun([_LT_CC_BASENAME],
+[m4_require([_LT_PREPARE_CC_BASENAME])dnl
+AC_REQUIRE([_LT_DECL_SED])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+func_cc_basename $1
+cc_basename=$func_cc_basename_result
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+m4_require([_LT_CMD_TRUNCATE])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from 'configure', and 'config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# 'config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain=$ac_aux_dir/ltmain.sh
+])# _LT_PROG_LTMAIN
+
+
+## ------------------------------------- ##
+## Accumulate code for creating libtool. ##
+## ------------------------------------- ##
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the 'libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+## ------------------------ ##
+## FIXME: Eliminate VARNAME ##
+## ------------------------ ##
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to 'config.status' so that its
+# declaration there will have the same value as in 'configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags='_LT_TAGS'dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into 'config.status', and then the shell code to quote escape them in
+# for loops in 'config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable. If COMMENT is supplied, it is inserted after the
+# '#!' sequence but before initialization text begins. After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script. The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test 0 = "$lt_write_fail" && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+'$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test 0 != $[#]
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try '$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try '$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test yes = "$silent" &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_PREPARE_MUNGE_PATH_LIST
+_LT_PREPARE_CC_BASENAME
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Go], [_LT_LANG(GO)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_GO. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+############################################################
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC], [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+ if test -n "$ac_tool_prefix"; then
+ AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+ fi
+fi
+if test -z "$GOC"; then
+ AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+ [LT_LANG(GO)],
+ [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS=$save_LDFLAGS
+ ])
+
+ AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+ [lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+ echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+ $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+ echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+ $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[912]]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]][[,.]]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*|11.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+ [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+ m4_if([$1], [CXX],
+[ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+ lt_aix_libpath_sed='[
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }]'
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi],[])
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib
+ fi
+ ])
+ aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script that will find a shell with a builtin
+# printf (that we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+case $ECHO in
+ printf*) AC_MSG_RESULT([printf]) ;;
+ print*) AC_MSG_RESULT([print -r]) ;;
+ *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test "X`printf %s $ECHO`" = "X$ECHO" \
+ || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@],
+ [Search for dependent libraries within DIR (or the compiler's sysroot
+ if not specified).])],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted. We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ AC_MSG_RESULT([$with_sysroot])
+ AC_MSG_ERROR([The sysroot must be an absolute path.])
+ ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and where our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cr}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+ [lt_cv_ar_at_file=no
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+ [echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+ ])
+ ])
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+ [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+ [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes = "$cross_compiling"; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen=shl_load],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen=dlopen],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links=nottested
+if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test no = "$hard_links"; then
+ AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/",
+ [Define to the sub-directory where libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$_LT_TAGVAR(hardcode_direct, $1)" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" &&
+ test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test relink = "$_LT_TAGVAR(hardcode_action, $1)" ||
+ test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_PREPARE_MUNGE_PATH_LIST
+# ---------------------------
+# Make sure func_munge_path_list() is defined correctly.
+m4_defun([_LT_PREPARE_MUNGE_PATH_LIST],
+[[# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x@S|@2 in
+ x)
+ ;;
+ *:)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\"
+ ;;
+ x:*)
+ eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\"
+ ;;
+ *)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+]])# _LT_PREPARE_PATH_LIST
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+AC_ARG_VAR([LT_SYS_LIBRARY_PATH],
+[User-defined run-time library search path.])
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a[(]lib.so.V[)]'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[23]].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+ [lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [lt_cv_shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+ ])
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsdelf*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='NetBSD ld.elf_so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+ [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2],
+ [Detected run-time system search path for libraries])
+_LT_DECL([], [configure_time_lt_sys_library_path], [2],
+ [Explicit LT_SYS_LIBRARY_PATH set during ./configure time])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program that can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$1"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac])
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program that can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test no = "$withval" || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi])
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_PATH_DD
+# -----------
+# find a working dd
+m4_defun([_LT_PATH_DD],
+[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd],
+[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi])
+rm -f conftest.i conftest2.i conftest.out])
+])# _LT_PATH_DD
+
+
+# _LT_CMD_TRUNCATE
+# ----------------
+# find command to truncate a binary pipe
+m4_defun([_LT_CMD_TRUNCATE],
+[m4_require([_LT_PATH_DD])
+AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"])
+_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1],
+ [Command to truncate a binary pipe])
+])# _LT_CMD_TRUNCATE
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+ [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+ [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+ AC_SUBST([DUMPBIN])
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+ [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+ [lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*])
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# _LT_DLL_DEF_P([FILE])
+# ---------------------
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with func_dll_def_p in the libtool script
+AC_DEFUN([_LT_DLL_DEF_P],
+[dnl
+ test DEF = "`$SED -n dnl
+ -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace
+ -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments
+ -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl
+ -e q dnl Only consider the first "real" line
+ $1`" dnl
+])# _LT_DLL_DEF_P
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw)
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM=-lm)
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+ esac
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD
+ if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT@&t@_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT@&t@_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1],
+ [Transform the output of nm into a list of symbols to manually relocate])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([nm_interface], [lt_cv_nm_interface], [1],
+ [The name lister interface])
+_LT_DECL([], [nm_file_list_spec], [1],
+ [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64, which still supported -KPIC.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+ # IBM XL 8.0, 9.0 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd* | netbsdelf*-gnu)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+ if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # flang / f18. f95 an alias for gfortran or flang on Debian
+ flang* | f18* | f95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Intel*\ [[CF]]*Compiler*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ *Portland\ Group*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
+ ;;
+ cygwin* | mingw* | cegcc*)
+ case $cc_basename in
+ cl*)
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+ *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ m4_if($1, [], [
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ _LT_LINKER_OPTION([if $CC understands -b],
+ _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+ [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+ [lt_cv_irix_exported_symbol],
+ [save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE(
+ [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+ [C++], [[int foo (void) { return 0; }]],
+ [Fortran 77], [[
+ subroutine foo
+ end]],
+ [Fortran], [[
+ subroutine foo
+ end]])])],
+ [lt_cv_irix_exported_symbol=yes],
+ [lt_cv_irix_exported_symbol=no])
+ LDFLAGS=$save_LDFLAGS])
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+ [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+ [$RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ ])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting $shlibpath_var if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+ [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC=$CC
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report what library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC=$lt_save_CC
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_caught_CXX_error"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_CFLAGS=$CFLAGS
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ CFLAGS=$CXXFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test yes = "$GXX"; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test yes = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='$wl'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GXX"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ # The "-G" linker flag allows undefined symbols.
+ _LT_TAGVAR(no_undefined_flag, $1)='-bernotok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared
+ # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $GXX,$cc_basename in
+ ,cl* | no,cl*)
+ # Native MSVC
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # g++
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd2.*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ ;;
+ xl* | mpixl* | bgxl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=func_echo_all
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
+ else
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+ '"$_LT_TAGVAR(old_archive_cmds, $1)"
+ _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+ '"$_LT_TAGVAR(reload_cmds, $1)"
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)=$GXX
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+ case @S|@2 in
+ .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;;
+ *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;;
+ esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $prev$p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
+ prev=$p
+ continue
+ fi
+
+ # Expand the sysroot to ease extracting the directories later.
+ if test -z "$prev"; then
+ case $p in
+ -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+ -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+ -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+ esac
+ fi
+ case $p in
+ =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+ esac
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
+ -L | -R)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)=$prev$p
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p"
+ fi
+ fi
+ prev=
+ ;;
+
+ *.lto.$objext) ;; # Ignore GCC LTO objects
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test no = "$pre_test_object_deps_done"; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)=$p
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)=$p
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test no = "$F77"; then
+ _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_F77"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${F77-"f77"}
+ CFLAGS=$FFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$G77
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_F77"
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test no = "$FC"; then
+ _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_FC"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${FC-"f95"}
+ CFLAGS=$FCFLAGS
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_FC"
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code=$lt_simple_compile_test_code
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+############################################################
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f "$lt_ac_sed" && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test 10 -lt "$lt_ac_count" && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test "$lt_ac_count" -gt "$lt_ac_max"; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine what file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path). These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+ [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+ [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4
new file mode 100644
index 0000000..94b0829
--- /dev/null
+++ b/m4/ltoptions.m4
@@ -0,0 +1,437 @@
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file 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.
+
+# serial 8 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option '$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
+ [_LT_WITH_AIX_SONAME([aix])])
+ ])
+])# _LT_SET_OPTIONS
+
+
+## --------------------------------- ##
+## Macros to handle LT_INIT options. ##
+## --------------------------------- ##
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the 'shared' and
+# 'disable-shared' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the 'static' and
+# 'disable-static' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the 'fast-install'
+# and 'disable-fast-install' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_AIX_SONAME([DEFAULT])
+# ----------------------------------
+# implement the --with-aix-soname flag, and support the `aix-soname=aix'
+# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
+# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
+m4_define([_LT_WITH_AIX_SONAME],
+[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
+shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[[5-9]]*,yes)
+ AC_MSG_CHECKING([which variant of shared library versioning to provide])
+ AC_ARG_WITH([aix-soname],
+ [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
+ [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
+ [case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ AC_MSG_ERROR([Unknown argument to --with-aix-soname])
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname],
+ [AC_CACHE_VAL([lt_cv_with_aix_soname],
+ [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
+ with_aix_soname=$lt_cv_with_aix_soname])
+ AC_MSG_RESULT([$with_aix_soname])
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+_LT_DECL([], [shared_archive_member_spec], [0],
+ [Shared archive member basename, for filename based shared library versioning on AIX])dnl
+])# _LT_WITH_AIX_SONAME
+
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
+# LT_INIT options.
+# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [pic_mode=m4_default([$1], [default])])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+## ----------------- ##
+## LTDL_INIT Options ##
+## ----------------- ##
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4
new file mode 100644
index 0000000..48bc934
--- /dev/null
+++ b/m4/ltsugar.m4
@@ -0,0 +1,124 @@
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file 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.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59, which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
diff --git a/m4/ltversion.m4 b/m4/ltversion.m4
new file mode 100644
index 0000000..fa04b52
--- /dev/null
+++ b/m4/ltversion.m4
@@ -0,0 +1,23 @@
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file 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.
+
+# @configure_input@
+
+# serial 4179 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.6])
+m4_define([LT_PACKAGE_REVISION], [2.4.6])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.6'
+macro_revision='2.4.6'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4
new file mode 100644
index 0000000..c6b26f8
--- /dev/null
+++ b/m4/lt~obsolete.m4
@@ -0,0 +1,99 @@
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file 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.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
diff --git a/m4/sanitizer.m4 b/m4/sanitizer.m4
new file mode 100644
index 0000000..439d441
--- /dev/null
+++ b/m4/sanitizer.m4
@@ -0,0 +1,76 @@
+# Copyright (C) 2018 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 version 3, as published
+# by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranties of
+# MERCHANTABILITY, SATISFACTORY QUALITY, 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/>.
+
+#
+# Processes --with-sanitizer, --with-fuzzer, and --with-oss-fuzz flags, checks
+# if the options are supported by the compiler, and sets the following
+# variables accordingly:
+#
+# - sanitizer_CFLAGS -fsanitize=...
+# - fuzzer_CLAGS -fsanitize=...
+# - fuzzer_LDLAGS -fsanitize=...
+#
+
+AC_DEFUN([AX_SANITIZER], [
+
+ # Configure options
+ AC_ARG_WITH([sanitizer],
+ [AS_HELP_STRING([--with-sanitizer], [Compile with sanitizer [default=no]])],
+ [],
+ [with_sanitizer=no]
+ )
+ AC_ARG_WITH([fuzzer],
+ [AS_HELP_STRING([--with-fuzzer], [Compile with libfuzzer [default=no]])],
+ [],
+ [with_fuzzer=no]
+ )
+ AC_ARG_WITH([oss-fuzz],
+ [AS_HELP_STRING([--with-oss-fuzz], [Link for oss-fuzz environment [default=no]])],
+ [],
+ [with_oss_fuzz=no]
+ )
+
+ # Default values
+ AS_IF([test "$with_sanitizer" = "yes"], [ with_sanitizer=address,undefined ])
+ AS_IF([test "$with_fuzzer" = "yes"], [ with_fuzzer=fuzzer ])
+
+ # Construct output variables
+ sanitizer_CFLAGS=
+ fuzzer_CFLAGS=
+ fuzzer_LDFLAGS=
+ AS_IF([test "$with_sanitizer" != "no"], [
+ sanitizer_CFLAGS="-fsanitize=${with_sanitizer}"
+ ])
+ AS_IF([test "$with_fuzzer" != "no"], [
+ fuzzer_CFLAGS="-fsanitize=${with_fuzzer}"
+ fuzzer_LDFLAGS="-fsanitize=${with_fuzzer}"
+ ])
+ AC_SUBST(fuzzer_CFLAGS)
+ AC_SUBST(fuzzer_LDFLAGS)
+
+ # Test compiler support
+ AS_IF([test -n "$sanitizer_CFLAGS" -o -n "$fuzzer_CFLAGS"], [
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $sanitizer_CFLAGS $fuzzer_CFLAGS"
+ AC_MSG_CHECKING([whether compiler accepts '${sanitizer_CFLAGS} ${fuzzer_CFLAGS}'])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Options are not supported.])
+ ])
+ CFLAGS="$save_CFLAGS"
+ ])
+
+]) # AX_SANITIZER
diff --git a/m4/visibility.m4 b/m4/visibility.m4
new file mode 100644
index 0000000..e99e3fb
--- /dev/null
+++ b/m4/visibility.m4
@@ -0,0 +1,77 @@
+# visibility.m4 serial 5 (gettext-0.18.2)
+dnl Copyright (C) 2005, 2008, 2010-2016 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+dnl Tests whether the compiler supports the command-line option
+dnl -fvisibility=hidden and the function and variable attributes
+dnl __attribute__((__visibility__("hidden"))) and
+dnl __attribute__((__visibility__("default"))).
+dnl Does *not* test for __visibility__("protected") - which has tricky
+dnl semantics (see the 'vismain' test in glibc) and does not exist e.g. on
+dnl Mac OS X.
+dnl Does *not* test for __visibility__("internal") - which has processor
+dnl dependent semantics.
+dnl Does *not* test for #pragma GCC visibility push(hidden) - which is
+dnl "really only recommended for legacy code".
+dnl Set the variable CFLAG_VISIBILITY.
+dnl Defines and sets the variable HAVE_VISIBILITY.
+
+AC_DEFUN([gl_VISIBILITY],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ CFLAG_VISIBILITY=
+ HAVE_VISIBILITY=0
+ if test -n "$GCC"; then
+ dnl First, check whether -Werror can be added to the command line, or
+ dnl whether it leads to an error because of some other option that the
+ dnl user has put into $CC $CFLAGS $CPPFLAGS.
+ AC_MSG_CHECKING([whether the -Werror option is usable])
+ AC_CACHE_VAL([gl_cv_cc_vis_werror], [
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Werror"
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[]], [[]])],
+ [gl_cv_cc_vis_werror=yes],
+ [gl_cv_cc_vis_werror=no])
+ CFLAGS="$gl_save_CFLAGS"])
+ AC_MSG_RESULT([$gl_cv_cc_vis_werror])
+ dnl Now check whether visibility declarations are supported.
+ AC_MSG_CHECKING([for simple visibility declarations])
+ AC_CACHE_VAL([gl_cv_cc_visibility], [
+ gl_save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -fvisibility=hidden"
+ dnl We use the option -Werror and a function dummyfunc, because on some
+ dnl platforms (Cygwin 1.7) the use of -fvisibility triggers a warning
+ dnl "visibility attribute not supported in this configuration; ignored"
+ dnl at the first function definition in every compilation unit, and we
+ dnl don't want to use the option in this case.
+ if test $gl_cv_cc_vis_werror = yes; then
+ CFLAGS="$CFLAGS -Werror"
+ fi
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[extern __attribute__((__visibility__("hidden"))) int hiddenvar;
+ extern __attribute__((__visibility__("default"))) int exportedvar;
+ extern __attribute__((__visibility__("hidden"))) int hiddenfunc (void);
+ extern __attribute__((__visibility__("default"))) int exportedfunc (void);
+ void dummyfunc (void) {}
+ ]],
+ [[]])],
+ [gl_cv_cc_visibility=yes],
+ [gl_cv_cc_visibility=no])
+ CFLAGS="$gl_save_CFLAGS"])
+ AC_MSG_RESULT([$gl_cv_cc_visibility])
+ if test $gl_cv_cc_visibility = yes; then
+ CFLAG_VISIBILITY="-fvisibility=hidden"
+ HAVE_VISIBILITY=1
+ fi
+ fi
+ AC_SUBST([CFLAG_VISIBILITY])
+ AC_SUBST([HAVE_VISIBILITY])
+ AC_DEFINE_UNQUOTED([HAVE_VISIBILITY], [$HAVE_VISIBILITY],
+ [Define to 1 or 0, depending whether the compiler supports simple visibility declarations.])
+])
diff --git a/missing b/missing
new file mode 100755
index 0000000..8d0eaad
--- /dev/null
+++ b/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 1996-2020 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# 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 2, 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/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+fi
+
+case $1 in
+
+ --is-lightweight)
+ # Used by our autoconf macros to check whether the available missing
+ # script is modern enough.
+ exit 0
+ ;;
+
+ --run)
+ # Back-compat with the calling convention used by older automake.
+ shift
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+
+Supported PROGRAM values:
+ aclocal autoconf autoheader autom4te automake makeinfo
+ bison yacc flex lex help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: unknown '$1' option"
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch. This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+ msg="probably too old"
+elif test $st -eq 127; then
+ # Program was missing.
+ msg="missing on your system"
+else
+ # Program was found and executed, but failed. Give up.
+ exit $st
+fi
+
+perl_URL=https://www.perl.org/
+flex_URL=https://github.com/westes/flex
+gnu_software_URL=https://www.gnu.org/software
+
+program_details ()
+{
+ case $1 in
+ aclocal|automake)
+ echo "The '$1' program is part of the GNU Automake package:"
+ echo "<$gnu_software_URL/automake>"
+ echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/autoconf>"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ autoconf|autom4te|autoheader)
+ echo "The '$1' program is part of the GNU Autoconf package:"
+ echo "<$gnu_software_URL/autoconf/>"
+ echo "It also requires GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ esac
+}
+
+give_advice ()
+{
+ # Normalize program name to check for.
+ normalized_program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+ printf '%s\n' "'$1' is $msg."
+
+ configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+ case $normalized_program in
+ autoconf*)
+ echo "You should only need it if you modified 'configure.ac',"
+ echo "or m4 files included by it."
+ program_details 'autoconf'
+ ;;
+ autoheader*)
+ echo "You should only need it if you modified 'acconfig.h' or"
+ echo "$configure_deps."
+ program_details 'autoheader'
+ ;;
+ automake*)
+ echo "You should only need it if you modified 'Makefile.am' or"
+ echo "$configure_deps."
+ program_details 'automake'
+ ;;
+ aclocal*)
+ echo "You should only need it if you modified 'acinclude.m4' or"
+ echo "$configure_deps."
+ program_details 'aclocal'
+ ;;
+ autom4te*)
+ echo "You might have modified some maintainer files that require"
+ echo "the 'autom4te' program to be rebuilt."
+ program_details 'autom4te'
+ ;;
+ bison*|yacc*)
+ echo "You should only need it if you modified a '.y' file."
+ echo "You may want to install the GNU Bison package:"
+ echo "<$gnu_software_URL/bison/>"
+ ;;
+ lex*|flex*)
+ echo "You should only need it if you modified a '.l' file."
+ echo "You may want to install the Fast Lexical Analyzer package:"
+ echo "<$flex_URL>"
+ ;;
+ help2man*)
+ echo "You should only need it if you modified a dependency" \
+ "of a man page."
+ echo "You may want to install the GNU Help2man package:"
+ echo "<$gnu_software_URL/help2man/>"
+ ;;
+ makeinfo*)
+ echo "You should only need it if you modified a '.texi' file, or"
+ echo "any other file indirectly affecting the aspect of the manual."
+ echo "You might want to install the Texinfo package:"
+ echo "<$gnu_software_URL/texinfo/>"
+ echo "The spurious makeinfo call might also be the consequence of"
+ echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+ echo "want to install GNU make:"
+ echo "<$gnu_software_URL/make/>"
+ ;;
+ *)
+ echo "You might have modified some files without having the proper"
+ echo "tools for further handling them. Check the 'README' file, it"
+ echo "often tells you about the needed prerequisites for installing"
+ echo "this package. You may also peek at any GNU archive site, in"
+ echo "case some other package contains this missing '$1' program."
+ ;;
+ esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+ -e '2,$s/^/ /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/python/Makefile.am b/python/Makefile.am
new file mode 100644
index 0000000..5903219
--- /dev/null
+++ b/python/Makefile.am
@@ -0,0 +1,16 @@
+EXTRA_DIST = \
+ libknot/__init__.py.in \
+ libknot/control.py \
+ libknot/dname.py \
+ libknot/probe.py \
+ README.md \
+ setup.py.in
+
+clean-local:
+ -rm -rf dist *.egg-info
+
+dist: clean-local
+ python3 setup.py sdist
+
+upload:
+ twine upload dist/*
diff --git a/python/Makefile.in b/python/Makefile.in
new file mode 100644
index 0000000..e8e011f
--- /dev/null
+++ b/python/Makefile.in
@@ -0,0 +1,544 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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 = python
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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 = setup.py
+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__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/setup.py.in
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = \
+ libknot/__init__.py.in \
+ libknot/control.py \
+ libknot/dname.py \
+ libknot/probe.py \
+ README.md \
+ setup.py.in
+
+all: all-am
+
+.SUFFIXES:
+$(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 python/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign python/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):
+setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+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:
+
+clean-generic:
+
+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."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+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: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am 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 \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+clean-local:
+ -rm -rf dist *.egg-info
+
+dist: clean-local
+ python3 setup.py sdist
+
+upload:
+ twine upload dist/*
+
+# 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/python/README.md b/python/README.md
new file mode 100644
index 0000000..77fb54c
--- /dev/null
+++ b/python/README.md
@@ -0,0 +1,147 @@
+# Libknot API in Python
+
+A Python interface for managing the Knot DNS daemon.
+
+# Table of contents
+
+* [Introduction](#introduction)
+* [Control module](#control-module)
+ + [Usage](#using-the-control-module)
+ + [Example](#control-module-example)
+* [Probe module](#probe-module)
+ + [Usage](#using-the-probe-module)
+ + [Example](#probe-module-example)
+* [Dname module](#dname-module)
+ + [Usage](#using-the-dname-module)
+ + [Example](#dname-module-example)
+
+## Introduction
+
+If the shared `libknot.so` library isn't available in the library search path, it's
+necessary to load the library first, e.g.:
+
+```python3
+import libknot
+libknot.Knot("/usr/lib/libknot.so")
+```
+
+## Control module
+
+Using this module it's possible to create scripts for efficient tasks that
+would require complex shell scripts with multiple calls of `knotc`. For
+communication with the daemon it uses the same mechanism as the `knotc` utility,
+i.e. communication via a Unix socket.
+
+The module API is stored in `libknot.control`.
+
+### Using the Control module
+
+The module usage consists of several steps:
+
+* Initialization and connection to the daemon control socket.
+* One or more control operations. An operation is called by sending a command
+ with optional data to the daemon. The operation result has to be received
+ afterwards.
+* Closing the connection and deinitialization.
+
+### Control module example
+
+```python3
+import json
+import libknot.control
+
+# Initialization
+ctl = libknot.control.KnotCtl()
+ctl.connect("/var/run/knot/knot.sock")
+ctl.set_timeout(60)
+
+try:
+ # Operation without parameters
+ ctl.send_block(cmd="conf-begin")
+ resp = ctl.receive_block()
+
+ # Operation with parameters
+ ctl.send_block(cmd="conf-set", section="zone", item="domain", data="test")
+ resp = ctl.receive_block()
+
+ ctl.send_block(cmd="conf-commit")
+ resp = ctl.receive_block()
+
+ # Operation with a result displayed in JSON format
+ ctl.send_block(cmd="conf-read", section="zone", item="domain")
+ resp = ctl.receive_block()
+ print(json.dumps(resp, indent=4))
+except libknot.control.KnotCtlError as exc:
+ # Print libknot error
+ print(exc)
+finally:
+ # Deinitialization
+ ctl.send(libknot.control.KnotCtlType.END)
+ ctl.close()
+```
+
+```python3
+ # List configured zones (including catalog member ones)
+ ctl.send_block(cmd="conf-list", flags="z")
+ resp = ctl.receive_block()
+ for zone in resp['zone']:
+ print(zone)
+```
+
+## Probe module
+
+Using this module it's possible to receive traffic data from a running daemon with
+active probe module.
+
+The module API is stored in `libknot.probe`.
+
+### Using the Probe module
+
+The module usage consists of several steps:
+
+* Initialization of one or more probe channels
+* Periodical receiving of data units from the channels and data processing
+
+### Probe module example
+
+```python3
+import libknot.probe
+
+# Initialization of the first probe channel stored in `/run/knot`
+probe = libknot.probe.KnotProbe("/run/knot", 1)
+
+# Array for storing up to 8 data units
+data = libknot.probe.KnotProbeDataArray(8)
+while (True):
+ # Receiving data units with timeout of 1000 ms
+ if probe.consume(data, 1000) > 0:
+ # Printing received data units in the default format
+ for item in data:
+ print(item)
+```
+
+## Dname module
+
+This module provides a few dname-related operations.
+
+### Using the Dname module
+
+The dname object is initialized from a string with textual dname.
+Then the dname can be reformatted to wire format or back to textual format.
+
+### Dname module example
+
+```python3
+import libknot.dname
+
+dname = libknot.dname.KnotDname("e\\120ample.c\om.")
+print(dname.size()
+print(dname.str())
+print(dname.wire())
+```
+
+```bash
+13
+example.com.
+b'\x07example\x03com\x00'
+```
diff --git a/python/libknot/__init__.py.in b/python/libknot/__init__.py.in
new file mode 100644
index 0000000..554cbe6
--- /dev/null
+++ b/python/libknot/__init__.py.in
@@ -0,0 +1,95 @@
+"""Python libknot interface."""
+
+import ctypes
+import sys
+
+
+class KnotLookup(ctypes.Structure):
+ """Libknot lookup return structure."""
+
+ _fields_ = [('id', ctypes.c_int), ('name', ctypes.c_char_p)]
+
+
+class KnotRdataDescriptor(ctypes.Structure):
+ """Rdata descriptor structure."""
+
+ _fields_ = [('block_types', ctypes.c_int * 8), ('name', ctypes.c_char_p)]
+
+
+class Knot(object):
+ """Basic libknot interface."""
+
+ LIBKNOT = None
+ LIBKNOT_VERSION = "@libknot_SOVERSION@"
+
+ RCODE_NAMES = None
+
+ STRERROR = None
+ RDATA_DESC = None
+
+ @classmethod
+ def __init__(cls, path: str = None) -> None:
+ """Loads shared libknot library.
+ An explicit library path can be specified.
+ """
+
+ if cls.LIBKNOT:
+ return
+
+ if path is None:
+ version = ""
+ try:
+ version = ".%u" % int(cls.LIBKNOT_VERSION)
+ except Exception:
+ pass
+
+ if sys.platform == "darwin":
+ path = "libknot%s.dylib" % version
+ else:
+ path = "libknot.so%s" % version
+
+ cls.LIBKNOT = ctypes.cdll.LoadLibrary(path)
+
+ cls.RCODE_NAMES = (KnotLookup * 32).in_dll(cls.LIBKNOT, "knot_rcode_names")
+
+ cls.STRERROR = cls.LIBKNOT.knot_strerror
+ cls.STRERROR.restype = ctypes.c_char_p
+ cls.STRERROR.argtypes = [ctypes.c_int]
+
+ cls.RDATA_DESC = cls.LIBKNOT.knot_get_rdata_descriptor
+ cls.RDATA_DESC.restype = ctypes.POINTER(KnotRdataDescriptor)
+ cls.RDATA_DESC.argtypes = [ctypes.c_ushort]
+
+ @classmethod
+ def rclass_str(cls, rclass: int) -> str:
+ """Returns RRCLASS in text form."""
+
+ if (rclass == 1):
+ return "IN"
+ elif (rclass == 3):
+ return "CH"
+ elif (rclass == 254):
+ return "NONE"
+ elif (rclass == 255):
+ return "ANY"
+ else:
+ return "CLASS%i" % rclass
+
+ @classmethod
+ def rtype_str(cls, rtype: int) -> str:
+ """Returns RRTYPE in text form."""
+
+ descr = cls.RDATA_DESC(rtype).contents.name
+ if descr:
+ return descr.decode()
+ else:
+ return "TYPE%i" % rtype
+
+ @classmethod
+ def rcode_str(cls, rcode: int) -> str:
+ """Returns RCODE in text form."""
+
+ for item in cls.RCODE_NAMES:
+ if item.name and item.id == rcode:
+ return item.name.decode()
+ return "RCODE%i" % rcode
diff --git a/python/libknot/control.py b/python/libknot/control.py
new file mode 100644
index 0000000..44aa516
--- /dev/null
+++ b/python/libknot/control.py
@@ -0,0 +1,374 @@
+"""Libknot server control interface wrapper."""
+
+import ctypes
+import enum
+import warnings
+import libknot
+
+
+def load_lib(path: str = None) -> None:
+ """Compatibility wrapper."""
+
+ libknot.Knot(path)
+ warnings.warn("libknot.control.load_lib() is deprecated, use libknot.Knot() instead", \
+ category=Warning, stacklevel=2)
+
+
+class KnotCtlType(enum.IntEnum):
+ """Libknot server control data unit types."""
+
+ END = 0
+ DATA = 1
+ EXTRA = 2
+ BLOCK = 3
+
+
+class KnotCtlDataIdx(enum.IntEnum):
+ """Libknot server control data unit indices."""
+
+ COMMAND = 0
+ FLAGS = 1
+ ERROR = 2
+ SECTION = 3
+ ITEM = 4
+ ID = 5
+ ZONE = 6
+ OWNER = 7
+ TTL = 8
+ TYPE = 9
+ DATA = 10
+ FILTER = 11
+
+
+class KnotCtlData(object):
+ """Libknot server control data unit."""
+
+ DataArray = ctypes.c_char_p * len(KnotCtlDataIdx)
+
+ def __init__(self) -> None:
+ self.data = self.DataArray()
+
+ def __str__(self) -> str:
+ """Returns data unit in text form."""
+
+ string = str()
+
+ for idx in KnotCtlDataIdx:
+ if self.data[idx]:
+ if string:
+ string += ", "
+ string += "%s = '%s'" % (idx.name, self.data[idx].decode())
+
+ return string
+
+ def __getitem__(self, index: KnotCtlDataIdx) -> str:
+ """Data unit item getter."""
+
+ value = self.data[index]
+ return value.decode() if value else str()
+
+ def __setitem__(self, index: KnotCtlDataIdx, value: str) -> None:
+ """Data unit item setter."""
+
+ self.data[index] = ctypes.c_char_p(value.encode()) if value else ctypes.c_char_p()
+
+
+class KnotCtlError(Exception):
+ """Libknot server control error."""
+
+ def __init__(self, message: str, data: KnotCtlData = None) -> None:
+ super().__init__()
+ self.message = message
+ self.data = data
+
+ def __str__(self) -> str:
+ out = "%s" % self.message
+ if self.data:
+ out += " (%s)" % self.data
+ return out
+
+
+class KnotCtlErrorConnect(KnotCtlError):
+ """Control connection error."""
+
+
+class KnotCtlErrorSend(KnotCtlError):
+ """Control data send error."""
+
+
+class KnotCtlErrorReceive(KnotCtlError):
+ """Control data receive error."""
+
+
+class KnotCtlErrorRemote(KnotCtlError):
+ """Control error on the remote (server) side."""
+
+
+class KnotCtl(object):
+ """Libknot server control interface."""
+
+ ALLOC = None
+ FREE = None
+ SET_TIMEOUT = None
+ CONNECT = None
+ CLOSE = None
+ SEND = None
+ RECEIVE = None
+
+ def __init__(self) -> None:
+ """Initializes a control interface instance."""
+
+ if not KnotCtl.ALLOC:
+ libknot.Knot()
+
+ KnotCtl.ALLOC = libknot.Knot.LIBKNOT.knot_ctl_alloc
+ KnotCtl.ALLOC.restype = ctypes.c_void_p
+
+ KnotCtl.FREE = libknot.Knot.LIBKNOT.knot_ctl_free
+ KnotCtl.FREE.argtypes = [ctypes.c_void_p]
+
+ KnotCtl.SET_TIMEOUT = libknot.Knot.LIBKNOT.knot_ctl_set_timeout
+ KnotCtl.SET_TIMEOUT.argtypes = [ctypes.c_void_p, ctypes.c_int]
+
+ KnotCtl.CONNECT = libknot.Knot.LIBKNOT.knot_ctl_connect
+ KnotCtl.CONNECT.restype = ctypes.c_int
+ KnotCtl.CONNECT.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
+
+ KnotCtl.CLOSE = libknot.Knot.LIBKNOT.knot_ctl_close
+ KnotCtl.CLOSE.argtypes = [ctypes.c_void_p]
+
+ KnotCtl.SEND = libknot.Knot.LIBKNOT.knot_ctl_send
+ KnotCtl.SEND.restype = ctypes.c_int
+ KnotCtl.SEND.argtypes = [ctypes.c_void_p, ctypes.c_uint, ctypes.c_void_p]
+
+ KnotCtl.RECEIVE = libknot.Knot.LIBKNOT.knot_ctl_receive
+ KnotCtl.RECEIVE.restype = ctypes.c_int
+ KnotCtl.RECEIVE.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
+
+ self.obj = KnotCtl.ALLOC()
+
+ def __del__(self) -> None:
+ """Deallocates control interface instance."""
+
+ KnotCtl.FREE(self.obj)
+
+ def set_timeout(self, timeout: int) -> None:
+ """Sets control socket operations timeout in seconds."""
+
+ KnotCtl.SET_TIMEOUT(self.obj, timeout * 1000)
+
+ def connect(self, path: str) -> None:
+ """Connect to a specified control UNIX socket."""
+
+ ret = KnotCtl.CONNECT(self.obj, path.encode())
+ if ret != 0:
+ err = libknot.Knot.STRERROR(ret)
+ raise KnotCtlErrorConnect(err.decode())
+
+ def close(self) -> None:
+ """Disconnects from the current control socket."""
+
+ KnotCtl.CLOSE(self.obj)
+
+ def send(self, data_type: KnotCtlType, data: KnotCtlData = None) -> None:
+ """Sends a data unit to the connected control socket."""
+
+ ret = KnotCtl.SEND(self.obj, data_type,
+ data.data if data else ctypes.c_char_p())
+ if ret != 0:
+ err = libknot.Knot.STRERROR(ret)
+ raise KnotCtlErrorSend(err.decode())
+
+ def receive(self, data: KnotCtlData = None) -> KnotCtlType:
+ """Receives a data unit from the connected control socket."""
+
+ data_type = ctypes.c_uint()
+ ret = KnotCtl.RECEIVE(self.obj, ctypes.byref(data_type),
+ data.data if data else ctypes.c_char_p())
+ if ret != 0:
+ err = libknot.Knot.STRERROR(ret)
+ raise KnotCtlErrorReceive(err.decode())
+ return KnotCtlType(data_type.value)
+
+ def send_block(self, cmd: str, section: str = None, item: str = None,
+ identifier: str = None, zone: str = None, owner: str = None,
+ ttl: str = None, rtype: str = None, data: str = None,
+ flags: str = None, filters: str = None) -> None:
+ """Sends a control query block."""
+
+ query = KnotCtlData()
+ query[KnotCtlDataIdx.COMMAND] = cmd
+ query[KnotCtlDataIdx.SECTION] = section
+ query[KnotCtlDataIdx.ITEM] = item
+ query[KnotCtlDataIdx.ID] = identifier
+ query[KnotCtlDataIdx.ZONE] = zone
+ query[KnotCtlDataIdx.OWNER] = owner
+ query[KnotCtlDataIdx.TTL] = ttl
+ query[KnotCtlDataIdx.TYPE] = rtype
+ query[KnotCtlDataIdx.DATA] = data
+ query[KnotCtlDataIdx.FLAGS] = flags
+ query[KnotCtlDataIdx.FILTER] = filters
+
+ self.send(KnotCtlType.DATA, query)
+ self.send(KnotCtlType.BLOCK)
+
+ def _receive_conf(self, out, reply):
+
+ section = reply[KnotCtlDataIdx.SECTION]
+ ident = reply[KnotCtlDataIdx.ID]
+ item = reply[KnotCtlDataIdx.ITEM]
+ data = reply[KnotCtlDataIdx.DATA]
+
+ # Add the section if not exists.
+ if section not in out:
+ out[section] = dict()
+
+ # Add the identifier if not exists.
+ if ident and ident not in out[section]:
+ out[section][ident] = dict()
+
+ # Return if no item/value.
+ if not item:
+ return
+
+ item_level = out[section][ident] if ident else out[section]
+
+ # Treat alone identifier item differently.
+ if item in ["id", "domain", "target"]:
+ if data not in out[section]:
+ out[section][data] = dict()
+ else:
+ if item not in item_level:
+ item_level[item] = list()
+
+ if data:
+ item_level[item].append(data)
+
+ def _receive_zone_status(self, out, reply):
+
+ zone = reply[KnotCtlDataIdx.ZONE]
+ rtype = reply[KnotCtlDataIdx.TYPE]
+ data = reply[KnotCtlDataIdx.DATA]
+
+ # Add the zone if not exists.
+ if zone not in out:
+ out[zone] = dict()
+
+ out[zone][rtype] = data
+
+ def _receive_zone(self, out, reply):
+
+ zone = reply[KnotCtlDataIdx.ZONE]
+ owner = reply[KnotCtlDataIdx.OWNER]
+ ttl = reply[KnotCtlDataIdx.TTL]
+ rtype = reply[KnotCtlDataIdx.TYPE]
+ data = reply[KnotCtlDataIdx.DATA]
+
+ # Add the zone if not exists.
+ if zone not in out:
+ out[zone] = dict()
+
+ if owner not in out[zone]:
+ out[zone][owner] = dict()
+
+ if rtype not in out[zone][owner]:
+ out[zone][owner][rtype] = dict()
+
+ # Add the key/value.
+ out[zone][owner][rtype]["ttl"] = ttl
+
+ if not "data" in out[zone][owner][rtype]:
+ out[zone][owner][rtype]["data"] = [data]
+ else:
+ out[zone][owner][rtype]["data"].append(data)
+
+ def _receive_stats(self, out, reply):
+
+ zone = reply[KnotCtlDataIdx.ZONE]
+ section = reply[KnotCtlDataIdx.SECTION]
+ item = reply[KnotCtlDataIdx.ITEM]
+ idx = reply[KnotCtlDataIdx.ID]
+ data = int(reply[KnotCtlDataIdx.DATA])
+
+ # Add the zone if not exists.
+ if zone:
+ if "zone" not in out:
+ out["zone"] = dict()
+
+ if zone not in out["zone"]:
+ out["zone"][zone] = dict()
+
+ section_level = out["zone"][zone] if zone else out
+
+ if section not in section_level:
+ section_level[section] = dict()
+
+ if idx:
+ if item not in section_level[section]:
+ section_level[section][item] = dict()
+
+ section_level[section][item][idx] = data
+ else:
+ section_level[section][item] = data
+
+ def receive_stats(self) -> dict:
+ """Receives statistics answer and returns it as a structured dictionary."""
+
+ out = dict()
+ err_reply = None
+
+ while True:
+ reply = KnotCtlData()
+ reply_type = self.receive(reply)
+
+ # Stop if not data type.
+ if reply_type not in [KnotCtlType.DATA, KnotCtlType.EXTRA]:
+ break
+
+ # Check for an error.
+ if reply[KnotCtlDataIdx.ERROR]:
+ err_reply = reply
+ continue
+
+ self._receive_stats(out, reply)
+
+ if err_reply:
+ raise KnotCtlErrorRemote(err_reply[KnotCtlDataIdx.ERROR], err_reply)
+
+ return out
+
+ def receive_block(self) -> dict:
+ """Receives a control answer and returns it as a structured dictionary."""
+
+ out = dict()
+ err_reply = None
+
+ while True:
+ reply = KnotCtlData()
+ reply_type = self.receive(reply)
+
+ # Stop if not data type.
+ if reply_type not in [KnotCtlType.DATA, KnotCtlType.EXTRA]:
+ break
+
+ # Check for an error.
+ if reply[KnotCtlDataIdx.ERROR]:
+ err_reply = reply
+ continue
+
+ # Check for config data.
+ if reply[KnotCtlDataIdx.SECTION]:
+ self._receive_conf(out, reply)
+ # Check for zone data.
+ elif reply[KnotCtlDataIdx.ZONE]:
+ if reply[KnotCtlDataIdx.OWNER]:
+ self._receive_zone(out, reply)
+ else:
+ self._receive_zone_status(out, reply)
+ else:
+ continue
+
+ if err_reply:
+ raise KnotCtlErrorRemote(err_reply[KnotCtlDataIdx.ERROR], err_reply)
+
+ return out
diff --git a/python/libknot/dname.py b/python/libknot/dname.py
new file mode 100644
index 0000000..4f585fe
--- /dev/null
+++ b/python/libknot/dname.py
@@ -0,0 +1,70 @@
+"""Libknot dname interface wrapper."""
+
+import ctypes
+import libknot
+
+
+class KnotDname(object):
+ """Libknot dname."""
+
+ CAPACITY = 255
+ CAPACITY_TXT = 1004
+
+ DnameStorage = ctypes.c_char * CAPACITY
+ DnameTxtStorage = ctypes.c_char * CAPACITY_TXT
+
+ SIZE = None
+ TO_STR = None
+ FROM_STR = None
+
+ data = None
+
+ def __init__(self, dname: str = None) -> None:
+ """Initializes a dname storage. Optionally initializes from a string."""
+
+ if not KnotDname.SIZE:
+ libknot.Knot()
+
+ KnotDname.SIZE = libknot.Knot.LIBKNOT.knot_dname_size
+ KnotDname.SIZE.restype = ctypes.c_size_t
+ KnotDname.SIZE.argtypes = [KnotDname.DnameStorage]
+
+ KnotDname.TO_STR = libknot.Knot.LIBKNOT.knot_dname_to_str
+ KnotDname.TO_STR.restype = ctypes.c_char_p
+ KnotDname.TO_STR.argtypes = [KnotDname.DnameTxtStorage, KnotDname.DnameStorage, ctypes.c_size_t]
+
+ KnotDname.FROM_STR = libknot.Knot.LIBKNOT.knot_dname_from_str
+ KnotDname.FROM_STR.restype = ctypes.c_char_p
+ KnotDname.FROM_STR.argtypes = [KnotDname.DnameStorage, ctypes.c_char_p, ctypes.c_size_t]
+
+ if dname:
+ self.data = KnotDname.DnameStorage()
+ if not KnotDname.FROM_STR(self.data, dname.encode('utf-8'), KnotDname.CAPACITY):
+ raise ValueError
+
+ def size(self):
+ """Returns size of the stored dname."""
+
+ if self.data:
+ return KnotDname.SIZE(self.data)
+ else:
+ return 0
+
+ def str(self) -> str:
+ """Prints the stored dname in textual format."""
+
+ if self.data:
+ data_txt = KnotDname.DnameTxtStorage()
+ if not KnotDname.TO_STR(data_txt, self.data, KnotDname.CAPACITY_TXT):
+ raise ValueError
+ return data_txt.value.decode("utf-8")
+ else:
+ return ""
+
+ def wire(self) -> bytes:
+ """Returns the dname in wire format."""
+
+ if self.data:
+ return self.data.value + b'\x00'
+ else:
+ return bytes()
diff --git a/python/libknot/probe.py b/python/libknot/probe.py
new file mode 100644
index 0000000..e6f09db
--- /dev/null
+++ b/python/libknot/probe.py
@@ -0,0 +1,278 @@
+"""Libknot probe interface wrapper."""
+
+import ctypes
+import datetime
+import enum
+import socket
+import libknot
+
+
+class KnotProbeDataProto(enum.IntEnum):
+ """Libknot probe transport protocol types."""
+
+ UDP = 0
+ TCP = 1
+ QUIC = 3
+ TLS = 4
+ HTTPS = 5
+
+
+class KnotProbeDataDNSHdr(ctypes.BigEndianStructure):
+ """DNS message header."""
+
+ _fields_ = [('id', ctypes.c_ushort),
+ ('flag_qr', ctypes.c_ubyte, 1),
+ ('opcode', ctypes.c_ubyte, 4),
+ ('flag_aa', ctypes.c_ubyte, 1),
+ ('flag_tc', ctypes.c_ubyte, 1),
+ ('flag_rd', ctypes.c_ubyte, 1),
+ ('flag_ra', ctypes.c_ubyte, 1),
+ ('flag_z', ctypes.c_ubyte, 1),
+ ('flag_ad', ctypes.c_ubyte, 1),
+ ('flag_cd', ctypes.c_ubyte, 1),
+ ('rcode', ctypes.c_ubyte, 4),
+ ('questions', ctypes.c_ushort),
+ ('answers', ctypes.c_ushort),
+ ('authorities', ctypes.c_ushort),
+ ('additionals', ctypes.c_ushort)]
+
+
+class KnotProbeData(ctypes.Structure):
+ """Libknot probe data unit."""
+
+ ADDR_MAX_SIZE = 16
+ QNAME_MAX_SIZE = 255
+
+ EDE_NONE = 65535
+
+ _fields_ = [('ip', ctypes.c_ubyte),
+ ('proto', ctypes.c_ubyte),
+ ('local_addr', ctypes.c_ubyte * ADDR_MAX_SIZE),
+ ('local_port', ctypes.c_ushort),
+ ('remote_addr', ctypes.c_ubyte * ADDR_MAX_SIZE),
+ ('remote_port', ctypes.c_ushort),
+ ('reply_hdr', KnotProbeDataDNSHdr),
+ ('reply_size', ctypes.c_ushort),
+ ('reply_rcode', ctypes.c_ushort),
+ ('reply_ede', ctypes.c_ushort),
+ ('tcp_rtt', ctypes.c_uint),
+ ('edns_options', ctypes.c_uint),
+ ('edns_payload', ctypes.c_ushort),
+ ('edns_version', ctypes.c_ubyte),
+ ('edns_present', ctypes.c_ubyte, 1),
+ ('edns_flag_do', ctypes.c_ubyte, 1),
+ ('_reserved_', ctypes.c_ubyte, 6),
+ ('query_hdr', KnotProbeDataDNSHdr),
+ ('query_size', ctypes.c_ushort),
+ ('query_class', ctypes.c_ushort),
+ ('query_type', ctypes.c_ushort),
+ ('query_name_len', ctypes.c_ubyte),
+ ('query_name', ctypes.c_ubyte * (QNAME_MAX_SIZE))]
+
+ def addr_str(self, addr: ctypes.c_ubyte * ADDR_MAX_SIZE) -> str:
+ """Converts IPv4 or IPv6 address from binary to text form."""
+
+ if self.ip == 4:
+ buffer = ctypes.create_string_buffer(4)
+ ctypes.memmove(buffer, ctypes.addressof(addr), 4)
+ return socket.inet_ntop(socket.AF_INET, buffer)
+ else:
+ return socket.inet_ntop(socket.AF_INET6, addr)
+
+ def qname_str(self) -> str:
+ """Returns QNAME in text form."""
+
+ string = str()
+ pos = 0
+ while pos < self.query_name_len:
+ label_len = self.query_name[pos]
+ if label_len == 0:
+ if self.query_name_len == 1:
+ string += "."
+ break
+ pos += 1
+ label_end = pos + label_len
+ while pos < label_end:
+ string += chr(self.query_name[pos])
+ pos += 1
+ string += "."
+ return string
+
+ def __str__(self) -> str:
+ """Returns the data unit in a pre-formatted text form."""
+
+ return self.str()
+
+ def str(self, timestamp: bool = True, color: bool = True) -> str:
+ """Returns the data unit in a pre-formatted text form with customization."""
+
+ RST = "\x1B[0m"
+ BOLD = "\x1B[1m"
+ UNDR = "\x1B[4m"
+ RED = "\x1B[31m"
+ GRN = "\x1B[32m"
+ ORG = "\x1B[33m"
+ YELW = "\x1B[93m"
+ MGNT = "\x1B[35m"
+ CYAN = "\x1B[36m"
+
+ def COL(string, color_str, active=color):
+ return str(string) if not active else color_str + str(string) + RST
+
+ string = str()
+ if timestamp:
+ string += "%s " % COL(datetime.datetime.now().time(), YELW)
+ if self.ip != 0:
+ string += "%s -> %s, " % (COL(self.addr_str(self.remote_addr), UNDR),
+ COL(self.addr_str(self.local_addr), UNDR))
+ string += "port %u -> %u " % (self.remote_port, self.local_port)
+ else:
+ string += "%s, " % COL("UNIX", UNDR)
+ if self.proto == KnotProbeDataProto.UDP:
+ string += COL("UDP", GRN)
+ elif self.proto == KnotProbeDataProto.TCP:
+ string += COL("TCP", RED)
+ else:
+ string += COL("QUIC", ORG)
+ if self.tcp_rtt > 0:
+ string += ", RTT %.2f ms" % (self.tcp_rtt / 1000)
+ string += "\n ID %u, " % self.query_hdr.id
+ if self.query_hdr.opcode == 0:
+ string += "QUERY"
+ elif self.query_hdr.opcode == 4:
+ string += COL("NOTIFY", MGNT)
+ elif self.query_hdr.opcode == 5:
+ string += COL("UPDATE", MGNT)
+ else:
+ string += COL("OPCODE%i" % self.query_hdr.opcode, MGNT)
+ string += ", "
+ string += COL("%s %s %s" % (self.qname_str(),
+ libknot.Knot.rclass_str(self.query_class),
+ libknot.Knot.rtype_str(self.query_type)), BOLD)
+ if self.edns_present == 1:
+ string += ", EDNS %i B" % self.edns_payload
+ if self.edns_flag_do == 1:
+ string += ", " + COL("DO", BOLD)
+ if (self.edns_options & (1 << 3)) != 0:
+ string += ", NSID"
+ if (self.edns_options & (1 << 8)) != 0:
+ string += ", ECS"
+ if (self.edns_options & (1 << 10)) != 0:
+ string += ", COOKIE"
+ string += ", " + COL("%u B" % self.query_size, CYAN)
+ if self.reply_size == 0:
+ string += " -> %s" % COL("DROPPED", RED)
+ return string
+ string += " -> %s" % COL(libknot.Knot.rcode_str(self.reply_rcode), BOLD)
+ if (self.reply_ede != libknot.probe.KnotProbeData.EDE_NONE):
+ string += ", EDE %u" % self.reply_ede
+ if self.reply_hdr.flag_aa != 0:
+ string += ", " + COL("AA", BOLD)
+ if self.reply_hdr.flag_tc != 0:
+ string += ", " + COL("TC", BOLD)
+ if self.reply_hdr.answers > 0:
+ string += ", %u ANS" % self.reply_hdr.answers
+ if self.reply_hdr.authorities > 0:
+ string += ", %u AUT" % self.reply_hdr.authorities
+ if self.reply_hdr.additionals > 0:
+ string += ", %u ADD" % self.reply_hdr.additionals
+ string += ", " + COL("%u B" % self.reply_size, CYAN)
+ return string
+
+
+class KnotProbeDataArray(object):
+ """Libknot probe data unit array."""
+
+ def __init__(self, size: int = 1) -> None:
+ """Creates a data array of a given size."""
+
+ if size < 1 or size > 255:
+ raise ValueError
+ data_array = KnotProbeData * size
+ self.data = data_array()
+ self.capacity = size
+ self.used = 0
+ self.pos = 0
+
+ def __getitem__(self, i: int) -> KnotProbeData:
+ """Returns a data unit at a specified position."""
+
+ if i < 0 or i >= self.capacity:
+ raise ValueError
+ return self.data[i]
+
+ def __len__(self) -> int:
+ """Returns currently used size of the array."""
+
+ return self.used
+
+ def __iter__(self):
+ """Initializes the array iterator."""
+
+ self.pos = 0
+ return self
+
+ def __next__(self) -> KnotProbeData:
+ """Increments the array iterator."""
+
+ if self.used == 0 or self.pos == self.used:
+ raise StopIteration
+ else:
+ data = self.data[self.pos]
+ self.pos += 1
+ return data
+
+
+class KnotProbe(object):
+ """Libknot probe consumer interface."""
+
+ ALLOC = None
+ FREE = None
+ CONSUME = None
+ SET_CONSUMER = None
+
+ def __init__(self, path: str = "/run/knot", idx: int = 1) -> None:
+ """Initializes a probe channel at a specified path with a channel index."""
+
+ if not KnotProbe.ALLOC:
+ libknot.Knot()
+
+ KnotProbe.ALLOC = libknot.Knot.LIBKNOT.knot_probe_alloc
+ KnotProbe.ALLOC.restype = ctypes.c_void_p
+
+ KnotProbe.FREE = libknot.Knot.LIBKNOT.knot_probe_free
+ KnotProbe.FREE.argtypes = [ctypes.c_void_p]
+
+ KnotProbe.CONSUME = libknot.Knot.LIBKNOT.knot_probe_consume
+ KnotProbe.CONSUME.restype = ctypes.c_int
+ KnotProbe.CONSUME.argtypes = [ctypes.c_void_p, ctypes.c_void_p, \
+ ctypes.c_ubyte, ctypes.c_int]
+
+ KnotProbe.SET_CONSUMER = libknot.Knot.LIBKNOT.knot_probe_set_consumer
+ KnotProbe.SET_CONSUMER.restype = ctypes.c_int
+ KnotProbe.SET_CONSUMER.argtypes = [ctypes.c_void_p, ctypes.c_char_p, \
+ ctypes.c_ushort]
+
+ self.obj = KnotProbe.ALLOC()
+
+ ret = KnotProbe.SET_CONSUMER(self.obj, path.encode(), idx)
+ if ret != 0:
+ err = libknot.Knot.STRERROR(ret)
+ raise RuntimeError(err.decode())
+
+ def __del__(self) -> None:
+ """Deinitializes a probe channel."""
+
+ KnotProbe.FREE(self.obj)
+
+ def consume(self, data: KnotProbeDataArray, timeout: int = 1000) -> int:
+ '''Consumes data units from a channel and stores them in data array.
+ Returns the number of consumed data units.
+ '''
+
+ ret = KnotProbe.CONSUME(self.obj, data.data, data.capacity, timeout)
+ if ret < 0:
+ err = libknot.Knot.STRERROR(ret)
+ raise RuntimeError(err.decode())
+ data.used = ret
+ return ret
diff --git a/python/setup.py.in b/python/setup.py.in
new file mode 100644
index 0000000..bd340ba
--- /dev/null
+++ b/python/setup.py.in
@@ -0,0 +1,30 @@
+import pathlib
+import setuptools
+
+p = pathlib.Path("README.md")
+if p.exists():
+ long_description = p.read_text()
+
+setuptools.setup(
+ name='libknot',
+ version='@PACKAGE_VERSION@',
+ description='Python bindings for libknot',
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ author='Daniel Salzman',
+ author_email='daniel.salzman@nic.cz',
+ url='https://gitlab.nic.cz/knot/knot-dns',
+ license='GPL-3.0',
+ packages=['libknot'],
+ classifiers=[ # See https://pypi.org/classifiers
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Internet :: Name Service (DNS)',
+ 'Topic :: Software Development :: Libraries',
+ 'Topic :: System :: Systems Administration',
+ ],
+ python_requires='>=3.5',
+)
diff --git a/samples/Makefile.am b/samples/Makefile.am
new file mode 100644
index 0000000..c253c91
--- /dev/null
+++ b/samples/Makefile.am
@@ -0,0 +1,34 @@
+edit = sed \
+ -e 's|@version[@]|$(PACKAGE_VERSION)|g' \
+ -e 's|@package[@]|$(PACKAGE_NAME)|g' \
+ -e 's|@localstatedir[@]|$(localstatedir)|g' \
+ -e 's|@prefix[@]|$(prefix)|g' \
+ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \
+ -e 's|@config_dir[@]|$(config_dir)|g' \
+ -e 's|@storage_dir[@]|$(storage_dir)|g' \
+ -e 's|@run_dir[@]|$(run_dir)|g'
+
+knot.sample.conf: knot.sample.conf.in
+ rm -f $@ $@.tmp
+ srcdir=''; \
+ test -f ./$@.in || srcdir=$(srcdir)/; \
+ $(edit) $${srcdir}$@.in >$@.tmp
+ mv $@.tmp $@
+
+EXTRA_DIST = knot.sample.conf.in example.com.zone
+
+if HAVE_DAEMON
+
+install-data-local: knot.sample.conf
+ if [ \! -f $(DESTDIR)/$(config_dir)/knot.sample.conf ]; then \
+ $(INSTALL) -d $(DESTDIR)/$(config_dir); \
+ $(INSTALL_DATA) knot.sample.conf $(srcdir)/example.com.zone $(DESTDIR)/$(config_dir); \
+ fi
+uninstall-local:
+ -rm -rf $(DESTDIR)/$(config_dir)/knot.sample.conf \
+ $(DESTDIR)/$(config_dir)/example.com.zone
+
+endif # HAVE_DAEMON
+
+clean-local:
+ rm -f knot.sample.conf
diff --git a/samples/Makefile.in b/samples/Makefile.in
new file mode 100644
index 0000000..c9b625c
--- /dev/null
+++ b/samples/Makefile.in
@@ -0,0 +1,558 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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 = samples
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+edit = sed \
+ -e 's|@version[@]|$(PACKAGE_VERSION)|g' \
+ -e 's|@package[@]|$(PACKAGE_NAME)|g' \
+ -e 's|@localstatedir[@]|$(localstatedir)|g' \
+ -e 's|@prefix[@]|$(prefix)|g' \
+ -e 's|@sysconfdir[@]|$(sysconfdir)|g' \
+ -e 's|@config_dir[@]|$(config_dir)|g' \
+ -e 's|@storage_dir[@]|$(storage_dir)|g' \
+ -e 's|@run_dir[@]|$(run_dir)|g'
+
+EXTRA_DIST = knot.sample.conf.in example.com.zone
+all: all-am
+
+.SUFFIXES:
+$(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 samples/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign samples/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
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+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:
+
+clean-generic:
+
+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."
+@HAVE_DAEMON_FALSE@uninstall-local:
+@HAVE_DAEMON_FALSE@install-data-local:
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+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: uninstall-local
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-local 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 tags-am uninstall \
+ uninstall-am uninstall-local
+
+.PRECIOUS: Makefile
+
+
+knot.sample.conf: knot.sample.conf.in
+ rm -f $@ $@.tmp
+ srcdir=''; \
+ test -f ./$@.in || srcdir=$(srcdir)/; \
+ $(edit) $${srcdir}$@.in >$@.tmp
+ mv $@.tmp $@
+
+@HAVE_DAEMON_TRUE@install-data-local: knot.sample.conf
+@HAVE_DAEMON_TRUE@ if [ \! -f $(DESTDIR)/$(config_dir)/knot.sample.conf ]; then \
+@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/$(config_dir); \
+@HAVE_DAEMON_TRUE@ $(INSTALL_DATA) knot.sample.conf $(srcdir)/example.com.zone $(DESTDIR)/$(config_dir); \
+@HAVE_DAEMON_TRUE@ fi
+@HAVE_DAEMON_TRUE@uninstall-local:
+@HAVE_DAEMON_TRUE@ -rm -rf $(DESTDIR)/$(config_dir)/knot.sample.conf \
+@HAVE_DAEMON_TRUE@ $(DESTDIR)/$(config_dir)/example.com.zone
+
+clean-local:
+ rm -f knot.sample.conf
+
+# 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/samples/example.com.zone b/samples/example.com.zone
new file mode 100644
index 0000000..ee51895
--- /dev/null
+++ b/samples/example.com.zone
@@ -0,0 +1,22 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ SOA dns1.example.com. hostmaster.example.com. (
+ 2010111213 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ NS dns2
+ MX 10 mail
+
+dns1 A 192.0.2.1
+ AAAA 2001:DB8::1
+
+dns2 A 192.0.2.2
+ AAAA 2001:DB8::2
+
+mail A 192.0.2.3
+ AAAA 2001:DB8::3
diff --git a/samples/knot.sample.conf.in b/samples/knot.sample.conf.in
new file mode 100644
index 0000000..be3f2ec
--- /dev/null
+++ b/samples/knot.sample.conf.in
@@ -0,0 +1,36 @@
+# This is a sample of a minimal configuration file for Knot DNS.
+# See knot.conf(5) or refer to the server documentation.
+
+server:
+ rundir: "@run_dir@"
+ user: knot:knot
+ automatic-acl: on
+# listen: [ 127.0.0.1@53, ::1@53 ]
+
+log:
+ - target: syslog
+ any: info
+
+database:
+ storage: "@storage_dir@"
+
+remote:
+# - id: secondary
+# address: 192.168.1.1@53
+#
+# - id: primary
+# address: 192.168.2.1@53
+
+template:
+ - id: default
+ storage: "@storage_dir@"
+ file: "%s.zone"
+
+zone:
+# # Primary zone
+# - domain: example.com
+# notify: secondary
+
+# # Secondary zone
+# - domain: example.net
+# master: primary
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..d6c00a1
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,23 @@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -DCONFIG_DIR='"${config_dir}"' \
+ -DSTORAGE_DIR='"${storage_dir}"' \
+ -DRUN_DIR='"${run_dir}"' \
+ -DMODULE_DIR='"${module_dir}"' \
+ -DMODULE_INSTDIR='"${module_instdir}"'
+
+AM_LDFLAGS = $(LT_NO_UNDEFINED)
+
+EXTRA_DIST =
+CLEANFILES =
+BUILT_SOURCES =
+lib_LTLIBRARIES =
+noinst_LTLIBRARIES =
+pkgconfig_DATA =
+
+include $(srcdir)/contrib/Makefile.inc
+include $(srcdir)/libdnssec/Makefile.inc
+include $(srcdir)/libknot/Makefile.inc
+include $(srcdir)/libzscanner/Makefile.inc
+include $(srcdir)/knot/Makefile.inc
+include $(srcdir)/utils/Makefile.inc
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..8039ba0
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,8325 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+
+
+
+
+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@
+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@
+@USE_GNUTLS_MEMSET_TRUE@am__append_1 = $(gnutls_CFLAGS)
+@USE_GNUTLS_MEMSET_TRUE@am__append_2 = $(gnutls_LIBS)
+@EMBEDDED_LIBBPF_TRUE@am__append_3 = libembbpf.la
+@HAVE_LIBDNSTAP_TRUE@am__append_4 = libdnstap.la
+@HAVE_LIBDNSTAP_TRUE@am__append_5 = $(nodist_libdnstap_la_SOURCES)
+@HAVE_LIBDNSTAP_TRUE@am__append_6 = $(nodist_libdnstap_la_SOURCES)
+@EMBEDDED_LIBNGTCP2_TRUE@am__append_7 = libembngtcp2.la
+@ENABLE_PKCS11_TRUE@am__append_8 = $(pthread_LIBS)
+@EMBEDDED_LIBNGTCP2_TRUE@am__append_9 = $(libembngtcp2_LIBS)
+@EMBEDDED_LIBBPF_TRUE@am__append_10 = $(libembbpf_LIBS)
+@ENABLE_XDP_TRUE@am__append_11 = $(libbpf_CFLAGS)
+@ENABLE_XDP_TRUE@am__append_12 = $(libbpf_LIBS)
+@ENABLE_XDP_TRUE@am__append_13 = \
+@ENABLE_XDP_TRUE@ libknot/xdp/bpf-consts.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/eth.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/msg.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/tcp.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/xdp.h
+
+@ENABLE_XDP_TRUE@am__append_14 = \
+@ENABLE_XDP_TRUE@ libknot/xdp/bpf-kernel-obj.c \
+@ENABLE_XDP_TRUE@ libknot/xdp/bpf-kernel-obj.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/bpf-user.c \
+@ENABLE_XDP_TRUE@ libknot/xdp/bpf-user.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/eth.c \
+@ENABLE_XDP_TRUE@ libknot/xdp/msg_init.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/protocols.h \
+@ENABLE_XDP_TRUE@ libknot/xdp/tcp.c \
+@ENABLE_XDP_TRUE@ libknot/xdp/xdp.c
+
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__append_15 = $(libngtcp2_CFLAGS) $(gnutls_CFLAGS)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__append_16 = $(libngtcp2_LIBS) $(gnutls_LIBS)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__append_17 = \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/quic.h \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/quic_conn.h
+
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__append_18 = \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/quic.c \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/quic_conn.c
+
+@EMBEDDED_LIBNGTCP2_TRUE@am__append_19 = $(libembngtcp2_LIBS)
+@HAVE_DAEMON_TRUE@am__append_20 = libknotd.la
+@HAVE_DAEMON_TRUE@am__append_21 = knotd.pc
+@STATIC_MODULE_cookies_TRUE@am__append_22 = $(knot_modules_cookies_la_SOURCES)
+@SHARED_MODULE_cookies_TRUE@am__append_23 = knot/modules/cookies.la
+@STATIC_MODULE_dnsproxy_TRUE@am__append_24 = $(knot_modules_dnsproxy_la_SOURCES)
+@SHARED_MODULE_dnsproxy_TRUE@am__append_25 = knot/modules/dnsproxy.la
+@STATIC_MODULE_dnstap_TRUE@am__append_26 = $(knot_modules_dnstap_la_SOURCES)
+@STATIC_MODULE_dnstap_TRUE@am__append_27 = $(DNSTAP_CFLAGS)
+@STATIC_MODULE_dnstap_TRUE@am__append_28 = $(libdnstap_LIBS)
+@SHARED_MODULE_dnstap_TRUE@am__append_29 = knot/modules/dnstap.la
+@STATIC_MODULE_geoip_TRUE@am__append_30 = $(knot_modules_geoip_la_SOURCES)
+@STATIC_MODULE_geoip_TRUE@am__append_31 = $(libmaxminddb_CFLAGS)
+@STATIC_MODULE_geoip_TRUE@am__append_32 = $(libmaxminddb_LIBS)
+@SHARED_MODULE_geoip_TRUE@am__append_33 = knot/modules/geoip.la
+@STATIC_MODULE_noudp_TRUE@am__append_34 = $(knot_modules_noudp_la_SOURCES)
+@SHARED_MODULE_noudp_TRUE@am__append_35 = knot/modules/noudp.la
+@STATIC_MODULE_onlinesign_TRUE@am__append_36 = $(knot_modules_onlinesign_la_SOURCES)
+@SHARED_MODULE_onlinesign_TRUE@am__append_37 = knot/modules/onlinesign.la
+@STATIC_MODULE_probe_TRUE@am__append_38 = $(knot_modules_probe_la_SOURCES)
+@SHARED_MODULE_probe_TRUE@am__append_39 = knot/modules/probe.la
+@STATIC_MODULE_queryacl_TRUE@am__append_40 = $(knot_modules_queryacl_la_SOURCES)
+@SHARED_MODULE_queryacl_TRUE@am__append_41 = knot/modules/queryacl.la
+@STATIC_MODULE_rrl_TRUE@am__append_42 = $(knot_modules_rrl_la_SOURCES)
+@SHARED_MODULE_rrl_TRUE@am__append_43 = knot/modules/rrl.la
+@STATIC_MODULE_stats_TRUE@am__append_44 = $(knot_modules_stats_la_SOURCES)
+@SHARED_MODULE_stats_TRUE@am__append_45 = knot/modules/stats.la
+@STATIC_MODULE_synthrecord_TRUE@am__append_46 = $(knot_modules_synthrecord_la_SOURCES)
+@SHARED_MODULE_synthrecord_TRUE@am__append_47 = knot/modules/synthrecord.la
+@STATIC_MODULE_whoami_TRUE@am__append_48 = $(knot_modules_whoami_la_SOURCES)
+@SHARED_MODULE_whoami_TRUE@am__append_49 = knot/modules/whoami.la
+bin_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2)
+sbin_PROGRAMS = $(am__EXEEXT_3) $(am__EXEEXT_4) $(am__EXEEXT_5)
+@HAVE_LIBUTILS_TRUE@am__append_50 = libknotus.la
+@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__append_51 = $(libembngtcp2_LIBS)
+@HAVE_UTILS_TRUE@am__append_52 = kdig khost knsec3hash knsupdate
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_53 = $(DNSTAP_CFLAGS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_54 = $(libdnstap_LIBS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_55 = $(DNSTAP_CFLAGS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_56 = $(libdnstap_LIBS)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_57 = kxdpgun
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_58 = $(gnutls_CFLAGS)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__append_59 = $(gnutls_LIBS)
+@HAVE_DAEMON_TRUE@am__append_60 = knotc knotd
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_61 = kzonecheck kzonesign
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_62 = keymgr kjournalprint kcatalogprint
+subdir = src
+SUBDIRS =
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_libdnssec_HEADERS) \
+ $(include_libknotd_HEADERS) $(include_libzscanner_HEADERS) \
+ $(am__nobase_include_libknot_HEADERS_DIST) $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES = knotd.pc libknot.pc libdnssec.pc libzscanner.pc
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_UTILS_TRUE@am__EXEEXT_1 = kdig$(EXEEXT) khost$(EXEEXT) \
+@HAVE_UTILS_TRUE@ knsec3hash$(EXEEXT) knsupdate$(EXEEXT)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__EXEEXT_2 = kzonecheck$(EXEEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ kzonesign$(EXEEXT)
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \
+ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" \
+ "$(DESTDIR)$(pkgconfigdir)" \
+ "$(DESTDIR)$(include_libdnssecdir)" \
+ "$(DESTDIR)$(include_libknotddir)" \
+ "$(DESTDIR)$(include_libzscannerdir)" \
+ "$(DESTDIR)$(include_libknotdir)"
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__EXEEXT_3 = kxdpgun$(EXEEXT)
+@HAVE_DAEMON_TRUE@am__EXEEXT_4 = knotc$(EXEEXT) knotd$(EXEEXT)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__EXEEXT_5 = keymgr$(EXEEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ kjournalprint$(EXEEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ kcatalogprint$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS)
+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; }; \
+ }
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \
+ $(pkglib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+@USE_GNUTLS_MEMSET_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = libcontrib.la $(am__DEPENDENCIES_2)
+@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_DEPENDENCIES = \
+@SHARED_MODULE_cookies_TRUE@ $(am__DEPENDENCIES_3)
+am__dirstamp = $(am__leading_dot)dirstamp
+am_knot_modules_cookies_la_OBJECTS = \
+ knot/modules/cookies/la-cookies.lo
+knot_modules_cookies_la_OBJECTS = \
+ $(am_knot_modules_cookies_la_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 =
+knot_modules_cookies_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_cookies_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_cookies_TRUE@am_knot_modules_cookies_la_rpath = -rpath \
+@SHARED_MODULE_cookies_TRUE@ $(pkglibdir)
+@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_DEPENDENCIES = \
+@SHARED_MODULE_dnsproxy_TRUE@ $(am__DEPENDENCIES_3)
+am_knot_modules_dnsproxy_la_OBJECTS = \
+ knot/modules/dnsproxy/la-dnsproxy.lo
+knot_modules_dnsproxy_la_OBJECTS = \
+ $(am_knot_modules_dnsproxy_la_OBJECTS)
+knot_modules_dnsproxy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_dnsproxy_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_dnsproxy_TRUE@am_knot_modules_dnsproxy_la_rpath = \
+@SHARED_MODULE_dnsproxy_TRUE@ -rpath $(pkglibdir)
+@HAVE_LIBDNSTAP_TRUE@am__DEPENDENCIES_4 = libdnstap.la \
+@HAVE_LIBDNSTAP_TRUE@ $(am__DEPENDENCIES_1)
+@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_DEPENDENCIES = \
+@SHARED_MODULE_dnstap_TRUE@ $(am__DEPENDENCIES_4)
+am_knot_modules_dnstap_la_OBJECTS = knot/modules/dnstap/la-dnstap.lo
+knot_modules_dnstap_la_OBJECTS = $(am_knot_modules_dnstap_la_OBJECTS)
+knot_modules_dnstap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_dnstap_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_dnstap_TRUE@am_knot_modules_dnstap_la_rpath = -rpath \
+@SHARED_MODULE_dnstap_TRUE@ $(pkglibdir)
+@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_DEPENDENCIES = \
+@SHARED_MODULE_geoip_TRUE@ $(am__DEPENDENCIES_3) \
+@SHARED_MODULE_geoip_TRUE@ $(am__DEPENDENCIES_1)
+am_knot_modules_geoip_la_OBJECTS = knot/modules/geoip/la-geoip.lo \
+ knot/modules/geoip/la-geodb.lo
+knot_modules_geoip_la_OBJECTS = $(am_knot_modules_geoip_la_OBJECTS)
+knot_modules_geoip_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_geoip_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_geoip_TRUE@am_knot_modules_geoip_la_rpath = -rpath \
+@SHARED_MODULE_geoip_TRUE@ $(pkglibdir)
+knot_modules_noudp_la_LIBADD =
+am_knot_modules_noudp_la_OBJECTS = knot/modules/noudp/la-noudp.lo
+knot_modules_noudp_la_OBJECTS = $(am_knot_modules_noudp_la_OBJECTS)
+knot_modules_noudp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_noudp_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_noudp_TRUE@am_knot_modules_noudp_la_rpath = -rpath \
+@SHARED_MODULE_noudp_TRUE@ $(pkglibdir)
+@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_DEPENDENCIES = \
+@SHARED_MODULE_onlinesign_TRUE@ $(am__DEPENDENCIES_3)
+am_knot_modules_onlinesign_la_OBJECTS = \
+ knot/modules/onlinesign/la-onlinesign.lo \
+ knot/modules/onlinesign/la-nsec_next.lo
+knot_modules_onlinesign_la_OBJECTS = \
+ $(am_knot_modules_onlinesign_la_OBJECTS)
+knot_modules_onlinesign_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_onlinesign_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_onlinesign_TRUE@am_knot_modules_onlinesign_la_rpath = \
+@SHARED_MODULE_onlinesign_TRUE@ -rpath $(pkglibdir)
+knot_modules_probe_la_LIBADD =
+am_knot_modules_probe_la_OBJECTS = knot/modules/probe/la-probe.lo
+knot_modules_probe_la_OBJECTS = $(am_knot_modules_probe_la_OBJECTS)
+knot_modules_probe_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_probe_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_probe_TRUE@am_knot_modules_probe_la_rpath = -rpath \
+@SHARED_MODULE_probe_TRUE@ $(pkglibdir)
+knot_modules_queryacl_la_LIBADD =
+am_knot_modules_queryacl_la_OBJECTS = \
+ knot/modules/queryacl/la-queryacl.lo
+knot_modules_queryacl_la_OBJECTS = \
+ $(am_knot_modules_queryacl_la_OBJECTS)
+knot_modules_queryacl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_queryacl_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_queryacl_TRUE@am_knot_modules_queryacl_la_rpath = \
+@SHARED_MODULE_queryacl_TRUE@ -rpath $(pkglibdir)
+@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_DEPENDENCIES = \
+@SHARED_MODULE_rrl_TRUE@ $(am__DEPENDENCIES_3)
+am_knot_modules_rrl_la_OBJECTS = knot/modules/rrl/la-rrl.lo \
+ knot/modules/rrl/la-functions.lo
+knot_modules_rrl_la_OBJECTS = $(am_knot_modules_rrl_la_OBJECTS)
+knot_modules_rrl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_rrl_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_rrl_TRUE@am_knot_modules_rrl_la_rpath = -rpath \
+@SHARED_MODULE_rrl_TRUE@ $(pkglibdir)
+@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_DEPENDENCIES = \
+@SHARED_MODULE_stats_TRUE@ $(am__DEPENDENCIES_3)
+am_knot_modules_stats_la_OBJECTS = knot/modules/stats/la-stats.lo
+knot_modules_stats_la_OBJECTS = $(am_knot_modules_stats_la_OBJECTS)
+knot_modules_stats_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_stats_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_stats_TRUE@am_knot_modules_stats_la_rpath = -rpath \
+@SHARED_MODULE_stats_TRUE@ $(pkglibdir)
+@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_DEPENDENCIES = \
+@SHARED_MODULE_synthrecord_TRUE@ $(am__DEPENDENCIES_3)
+am_knot_modules_synthrecord_la_OBJECTS = \
+ knot/modules/synthrecord/la-synthrecord.lo
+knot_modules_synthrecord_la_OBJECTS = \
+ $(am_knot_modules_synthrecord_la_OBJECTS)
+knot_modules_synthrecord_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_synthrecord_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_synthrecord_TRUE@am_knot_modules_synthrecord_la_rpath = \
+@SHARED_MODULE_synthrecord_TRUE@ -rpath $(pkglibdir)
+knot_modules_whoami_la_LIBADD =
+am_knot_modules_whoami_la_OBJECTS = knot/modules/whoami/la-whoami.lo
+knot_modules_whoami_la_OBJECTS = $(am_knot_modules_whoami_la_OBJECTS)
+knot_modules_whoami_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(knot_modules_whoami_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@SHARED_MODULE_whoami_TRUE@am_knot_modules_whoami_la_rpath = -rpath \
+@SHARED_MODULE_whoami_TRUE@ $(pkglibdir)
+libcontrib_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_libcontrib_la_OBJECTS = contrib/libcontrib_la-base32hex.lo \
+ contrib/libcontrib_la-base64.lo \
+ contrib/libcontrib_la-base64url.lo \
+ contrib/libcontrib_la-conn_pool.lo \
+ contrib/libcontrib_la-files.lo \
+ contrib/libcontrib_la-getline.lo contrib/libcontrib_la-json.lo \
+ contrib/libcontrib_la-mempattern.lo \
+ contrib/musl/libcontrib_la-inet_ntop.lo \
+ contrib/libcontrib_la-net.lo \
+ contrib/qp-trie/libcontrib_la-trie.lo \
+ contrib/libcontrib_la-semaphore.lo \
+ contrib/libcontrib_la-sockaddr.lo \
+ contrib/libcontrib_la-string.lo contrib/libcontrib_la-time.lo \
+ contrib/openbsd/libcontrib_la-siphash.lo \
+ contrib/openbsd/libcontrib_la-strlcat.lo \
+ contrib/openbsd/libcontrib_la-strlcpy.lo \
+ contrib/proxyv2/libcontrib_la-proxyv2.lo \
+ contrib/ucw/libcontrib_la-heap.lo \
+ contrib/ucw/libcontrib_la-lists.lo \
+ contrib/ucw/libcontrib_la-mempool.lo \
+ contrib/url-parser/libcontrib_la-url_parser.lo \
+ contrib/vpool/libcontrib_la-vpool.lo
+libcontrib_la_OBJECTS = $(am_libcontrib_la_OBJECTS)
+libcontrib_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libcontrib_la_LDFLAGS) $(LDFLAGS) -o $@
+@ENABLE_PKCS11_TRUE@am__DEPENDENCIES_5 = $(am__DEPENDENCIES_1)
+libdnssec_la_DEPENDENCIES = $(am__DEPENDENCIES_3) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_5)
+am_libdnssec_la_OBJECTS = libdnssec/la-binary.lo \
+ libdnssec/la-crypto.lo libdnssec/la-digest.lo \
+ libdnssec/la-error.lo libdnssec/key/la-algorithm.lo \
+ libdnssec/key/la-convert.lo libdnssec/key/la-dnskey.lo \
+ libdnssec/key/la-ds.lo libdnssec/key/la-key.lo \
+ libdnssec/key/la-keytag.lo libdnssec/key/la-privkey.lo \
+ libdnssec/key/la-simple.lo libdnssec/la-keyid.lo \
+ libdnssec/keystore/la-keystore.lo \
+ libdnssec/keystore/la-pkcs11.lo libdnssec/keystore/la-pkcs8.lo \
+ libdnssec/nsec/la-bitmap.lo libdnssec/nsec/la-hash.lo \
+ libdnssec/nsec/la-nsec.lo libdnssec/p11/la-p11.lo \
+ libdnssec/la-pem.lo libdnssec/la-random.lo \
+ libdnssec/shared/la-bignum.lo libdnssec/shared/la-dname.lo \
+ libdnssec/shared/la-keyid_gnutls.lo libdnssec/sign/la-der.lo \
+ libdnssec/sign/la-sign.lo libdnssec/la-tsig.lo
+libdnssec_la_OBJECTS = $(am_libdnssec_la_OBJECTS)
+libdnssec_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdnssec_la_LDFLAGS) $(LDFLAGS) -o $@
+libdnstap_la_LIBADD =
+am__libdnstap_la_SOURCES_DIST = contrib/dnstap/convert.c \
+ contrib/dnstap/convert.h contrib/dnstap/dnstap.c \
+ contrib/dnstap/dnstap.h contrib/dnstap/message.c \
+ contrib/dnstap/message.h contrib/dnstap/reader.c \
+ contrib/dnstap/reader.h contrib/dnstap/writer.c \
+ contrib/dnstap/writer.h
+@HAVE_LIBDNSTAP_TRUE@am_libdnstap_la_OBJECTS = \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-convert.lo \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-dnstap.lo \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-message.lo \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-reader.lo \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-writer.lo
+@HAVE_LIBDNSTAP_TRUE@nodist_libdnstap_la_OBJECTS = contrib/dnstap/libdnstap_la-dnstap.pb-c.lo
+libdnstap_la_OBJECTS = $(am_libdnstap_la_OBJECTS) \
+ $(nodist_libdnstap_la_OBJECTS)
+libdnstap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libdnstap_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_LIBDNSTAP_TRUE@am_libdnstap_la_rpath =
+libembbpf_la_LIBADD =
+am__libembbpf_la_SOURCES_DIST = contrib/libbpf/include/asm/barrier.h \
+ contrib/libbpf/include/linux/compiler.h \
+ contrib/libbpf/include/linux/err.h \
+ contrib/libbpf/include/linux/filter.h \
+ contrib/libbpf/include/linux/kernel.h \
+ contrib/libbpf/include/linux/list.h \
+ contrib/libbpf/include/linux/overflow.h \
+ contrib/libbpf/include/linux/ring_buffer.h \
+ contrib/libbpf/include/linux/types.h \
+ contrib/libbpf/include/uapi/linux/bpf_common.h \
+ contrib/libbpf/include/uapi/linux/bpf.h \
+ contrib/libbpf/include/uapi/linux/btf.h \
+ contrib/libbpf/include/uapi/linux/if_link.h \
+ contrib/libbpf/include/uapi/linux/if_xdp.h \
+ contrib/libbpf/include/uapi/linux/netlink.h \
+ contrib/libbpf/bpf/bpf.c contrib/libbpf/bpf/bpf.h \
+ contrib/libbpf/bpf/bpf_core_read.h \
+ contrib/libbpf/bpf/bpf_endian.h \
+ contrib/libbpf/bpf/bpf_helper_defs.h \
+ contrib/libbpf/bpf/bpf_helpers.h \
+ contrib/libbpf/bpf/bpf_prog_linfo.c \
+ contrib/libbpf/bpf/bpf_tracing.h contrib/libbpf/bpf/btf.c \
+ contrib/libbpf/bpf/btf.h contrib/libbpf/bpf/btf_dump.c \
+ contrib/libbpf/bpf/hashmap.c contrib/libbpf/bpf/hashmap.h \
+ contrib/libbpf/bpf/libbpf.c contrib/libbpf/bpf/libbpf.h \
+ contrib/libbpf/bpf/libbpf_errno.c \
+ contrib/libbpf/bpf/libbpf_internal.h \
+ contrib/libbpf/bpf/libbpf_probes.c \
+ contrib/libbpf/bpf/libbpf_util.h contrib/libbpf/bpf/netlink.c \
+ contrib/libbpf/bpf/nlattr.c contrib/libbpf/bpf/nlattr.h \
+ contrib/libbpf/bpf/str_error.c contrib/libbpf/bpf/str_error.h \
+ contrib/libbpf/bpf/xsk.c contrib/libbpf/bpf/xsk.h
+@EMBEDDED_LIBBPF_TRUE@am_libembbpf_la_OBJECTS = \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-bpf.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-btf.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-btf_dump.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-hashmap.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-libbpf.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-netlink.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-nlattr.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-str_error.lo \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libembbpf_la-xsk.lo
+libembbpf_la_OBJECTS = $(am_libembbpf_la_OBJECTS)
+libembbpf_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libembbpf_la_LDFLAGS) $(LDFLAGS) -o $@
+@EMBEDDED_LIBBPF_TRUE@am_libembbpf_la_rpath =
+libembngtcp2_la_LIBADD =
+am__libembngtcp2_la_SOURCES_DIST = \
+ contrib/libngtcp2/ngtcp2/crypto/gnutls.c \
+ contrib/libngtcp2/ngtcp2/crypto/shared.c \
+ contrib/libngtcp2/ngtcp2/crypto/shared.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h \
+ contrib/libngtcp2/ngtcp2/version.h
+@EMBEDDED_LIBNGTCP2_TRUE@am_libembngtcp2_la_OBJECTS = contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo
+libembngtcp2_la_OBJECTS = $(am_libembngtcp2_la_OBJECTS)
+libembngtcp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libembngtcp2_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+@EMBEDDED_LIBNGTCP2_TRUE@am_libembngtcp2_la_rpath =
+@EMBEDDED_LIBNGTCP2_TRUE@am__DEPENDENCIES_6 = libembngtcp2.la \
+@EMBEDDED_LIBNGTCP2_TRUE@ $(am__DEPENDENCIES_1) \
+@EMBEDDED_LIBNGTCP2_TRUE@ $(am__DEPENDENCIES_1)
+@EMBEDDED_LIBNGTCP2_TRUE@am__DEPENDENCIES_7 = $(am__DEPENDENCIES_6)
+@EMBEDDED_LIBBPF_TRUE@am__DEPENDENCIES_8 = libembbpf.la \
+@EMBEDDED_LIBBPF_TRUE@ $(am__DEPENDENCIES_1)
+@EMBEDDED_LIBBPF_TRUE@am__DEPENDENCIES_9 = $(am__DEPENDENCIES_8)
+@ENABLE_XDP_TRUE@am__DEPENDENCIES_10 = $(am__DEPENDENCIES_1)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__DEPENDENCIES_11 = \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ $(am__DEPENDENCIES_1) \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ $(am__DEPENDENCIES_1)
+libknot_la_DEPENDENCIES = libdnssec.la $(am__DEPENDENCIES_3) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_7) $(am__DEPENDENCIES_9) \
+ $(am__DEPENDENCIES_10) $(am__DEPENDENCIES_11)
+am__libknot_la_SOURCES_DIST = libknot/codes.c \
+ libknot/control/control.c libknot/cookies.c \
+ libknot/descriptor.c libknot/dname.c libknot/error.c \
+ libknot/db/db_lmdb.c libknot/db/db_trie.c libknot/packet/pkt.c \
+ libknot/packet/rrset-wire.c libknot/probe/data.c \
+ libknot/probe/probe.c libknot/rdataset.c libknot/rrset-dump.c \
+ libknot/rrset.c libknot/rrtype/naptr.c libknot/rrtype/opt.c \
+ libknot/rrtype/tsig.c libknot/tsig-op.c libknot/tsig.c \
+ libknot/yparser/yparser.c libknot/yparser/ypbody.c \
+ libknot/yparser/ypformat.c libknot/yparser/ypschema.c \
+ libknot/yparser/yptrafo.c libknot/xdp/tcp_iobuf.c \
+ libknot/xdp/bpf-kernel-obj.c libknot/xdp/bpf-kernel-obj.h \
+ libknot/xdp/bpf-user.c libknot/xdp/bpf-user.h \
+ libknot/xdp/eth.c libknot/xdp/msg_init.h \
+ libknot/xdp/protocols.h libknot/xdp/tcp.c libknot/xdp/xdp.c \
+ libknot/xdp/quic.c libknot/xdp/quic_conn.c
+@ENABLE_XDP_TRUE@am__objects_1 = libknot/xdp/la-bpf-kernel-obj.lo \
+@ENABLE_XDP_TRUE@ libknot/xdp/la-bpf-user.lo \
+@ENABLE_XDP_TRUE@ libknot/xdp/la-eth.lo libknot/xdp/la-tcp.lo \
+@ENABLE_XDP_TRUE@ libknot/xdp/la-xdp.lo
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@am__objects_2 = \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/la-quic.lo \
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@ libknot/xdp/la-quic_conn.lo
+am_libknot_la_OBJECTS = libknot/la-codes.lo \
+ libknot/control/la-control.lo libknot/la-cookies.lo \
+ libknot/la-descriptor.lo libknot/la-dname.lo \
+ libknot/la-error.lo libknot/db/la-db_lmdb.lo \
+ libknot/db/la-db_trie.lo libknot/packet/la-pkt.lo \
+ libknot/packet/la-rrset-wire.lo libknot/probe/la-data.lo \
+ libknot/probe/la-probe.lo libknot/la-rdataset.lo \
+ libknot/la-rrset-dump.lo libknot/la-rrset.lo \
+ libknot/rrtype/la-naptr.lo libknot/rrtype/la-opt.lo \
+ libknot/rrtype/la-tsig.lo libknot/la-tsig-op.lo \
+ libknot/la-tsig.lo libknot/yparser/la-yparser.lo \
+ libknot/yparser/la-ypbody.lo libknot/yparser/la-ypformat.lo \
+ libknot/yparser/la-ypschema.lo libknot/yparser/la-yptrafo.lo \
+ libknot/xdp/la-tcp_iobuf.lo $(am__objects_1) $(am__objects_2)
+libknot_la_OBJECTS = $(am_libknot_la_OBJECTS)
+libknot_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libknot_la_LDFLAGS) $(LDFLAGS) -o $@
+@STATIC_MODULE_dnstap_TRUE@am__DEPENDENCIES_12 = \
+@STATIC_MODULE_dnstap_TRUE@ $(am__DEPENDENCIES_4)
+@STATIC_MODULE_geoip_TRUE@am__DEPENDENCIES_13 = $(am__DEPENDENCIES_1)
+libknotd_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_7) $(am__DEPENDENCIES_12) \
+ $(am__DEPENDENCIES_13)
+am__libknotd_la_SOURCES_DIST = knot/catalog/catalog_db.c \
+ knot/catalog/catalog_db.h knot/catalog/catalog_update.c \
+ knot/catalog/catalog_update.h knot/catalog/generate.c \
+ knot/catalog/generate.h knot/catalog/interpret.c \
+ knot/catalog/interpret.h knot/conf/base.c knot/conf/base.h \
+ knot/conf/conf.c knot/conf/conf.h knot/conf/confdb.c \
+ knot/conf/confdb.h knot/conf/confio.c knot/conf/confio.h \
+ knot/conf/migration.c knot/conf/migration.h knot/conf/module.h \
+ knot/conf/module.c knot/conf/schema.c knot/conf/schema.h \
+ knot/conf/tools.c knot/conf/tools.h knot/ctl/commands.c \
+ knot/ctl/commands.h knot/ctl/process.c knot/ctl/process.h \
+ knot/dnssec/context.c knot/dnssec/context.h \
+ knot/dnssec/ds_query.c knot/dnssec/ds_query.h \
+ knot/dnssec/kasp/kasp_db.c knot/dnssec/kasp/kasp_db.h \
+ knot/dnssec/kasp/kasp_zone.c knot/dnssec/kasp/kasp_zone.h \
+ knot/dnssec/kasp/keystate.c knot/dnssec/kasp/keystate.h \
+ knot/dnssec/kasp/keystore.c knot/dnssec/kasp/keystore.h \
+ knot/dnssec/kasp/policy.h knot/dnssec/key-events.c \
+ knot/dnssec/key-events.h knot/dnssec/key_records.c \
+ knot/dnssec/key_records.h knot/dnssec/nsec-chain.c \
+ knot/dnssec/nsec-chain.h knot/dnssec/nsec3-chain.c \
+ knot/dnssec/nsec3-chain.h knot/dnssec/policy.c \
+ knot/dnssec/policy.h knot/dnssec/rrset-sign.c \
+ knot/dnssec/rrset-sign.h knot/dnssec/zone-events.c \
+ knot/dnssec/zone-events.h knot/dnssec/zone-keys.c \
+ knot/dnssec/zone-keys.h knot/dnssec/zone-nsec.c \
+ knot/dnssec/zone-nsec.h knot/dnssec/zone-sign.c \
+ knot/dnssec/zone-sign.h knot/events/events.c \
+ knot/events/events.h knot/events/handlers.h \
+ knot/events/handlers/backup.c knot/events/handlers/dnssec.c \
+ knot/events/handlers/ds_check.c knot/events/handlers/ds_push.c \
+ knot/events/handlers/expire.c knot/events/handlers/flush.c \
+ knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \
+ knot/events/handlers/notify.c knot/events/handlers/refresh.c \
+ knot/events/handlers/update.c knot/events/replan.c \
+ knot/events/replan.h knot/nameserver/axfr.c \
+ knot/nameserver/axfr.h knot/nameserver/chaos.c \
+ knot/nameserver/chaos.h knot/nameserver/internet.c \
+ knot/nameserver/internet.h knot/nameserver/ixfr.c \
+ knot/nameserver/ixfr.h knot/nameserver/log.h \
+ knot/nameserver/notify.c knot/nameserver/notify.h \
+ knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \
+ knot/nameserver/process_query.c \
+ knot/nameserver/process_query.h knot/nameserver/query_module.c \
+ knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \
+ knot/nameserver/tsig_ctx.h knot/nameserver/update.c \
+ knot/nameserver/update.h knot/nameserver/xfr.c \
+ knot/nameserver/xfr.h knot/query/capture.c \
+ knot/query/capture.h knot/query/layer.h knot/query/query.c \
+ knot/query/query.h knot/query/requestor.c \
+ knot/query/requestor.h knot/common/evsched.c \
+ knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \
+ knot/common/log.c knot/common/log.h knot/common/process.c \
+ knot/common/process.h knot/common/stats.c knot/common/stats.h \
+ knot/common/systemd.c knot/common/systemd.h \
+ knot/common/unreachable.c knot/common/unreachable.h \
+ knot/journal/journal_basic.c knot/journal/journal_basic.h \
+ knot/journal/journal_metadata.c \
+ knot/journal/journal_metadata.h knot/journal/journal_read.c \
+ knot/journal/journal_read.h knot/journal/journal_write.c \
+ knot/journal/journal_write.h knot/journal/knot_lmdb.c \
+ knot/journal/knot_lmdb.h knot/journal/serialization.c \
+ knot/journal/serialization.h knot/server/dthreads.c \
+ knot/server/dthreads.h knot/server/proxyv2.c \
+ knot/server/proxyv2.h knot/server/server.c \
+ knot/server/server.h knot/server/tcp-handler.c \
+ knot/server/tcp-handler.h knot/server/udp-handler.c \
+ knot/server/udp-handler.h knot/server/xdp-handler.c \
+ knot/server/xdp-handler.h knot/updates/acl.c \
+ knot/updates/acl.h knot/updates/apply.c knot/updates/apply.h \
+ knot/updates/changesets.c knot/updates/changesets.h \
+ knot/updates/ddns.c knot/updates/ddns.h \
+ knot/updates/zone-update.c knot/updates/zone-update.h \
+ knot/worker/pool.c knot/worker/pool.h knot/worker/queue.c \
+ knot/worker/queue.h knot/zone/adds_tree.c \
+ knot/zone/adds_tree.h knot/zone/adjust.c knot/zone/adjust.h \
+ knot/zone/backup.c knot/zone/backup.h knot/zone/backup_dir.c \
+ knot/zone/backup_dir.h knot/zone/contents.c \
+ knot/zone/contents.h knot/zone/digest.c knot/zone/digest.h \
+ knot/zone/measure.h knot/zone/measure.c knot/zone/node.c \
+ knot/zone/node.h knot/zone/semantic-check.c \
+ knot/zone/semantic-check.h knot/zone/serial.c \
+ knot/zone/serial.h knot/zone/timers.c knot/zone/timers.h \
+ knot/zone/zone-diff.c knot/zone/zone-diff.h \
+ knot/zone/zone-dump.c knot/zone/zone-dump.h \
+ knot/zone/zone-load.c knot/zone/zone-load.h \
+ knot/zone/zone-tree.c knot/zone/zone-tree.h knot/zone/zone.c \
+ knot/zone/zone.h knot/zone/zonedb-load.c \
+ knot/zone/zonedb-load.h knot/zone/zonedb.c knot/zone/zonedb.h \
+ knot/zone/zonefile.c knot/zone/zonefile.h \
+ knot/modules/cookies/cookies.c \
+ knot/modules/dnsproxy/dnsproxy.c knot/modules/dnstap/dnstap.c \
+ knot/modules/geoip/geoip.c knot/modules/geoip/geodb.c \
+ knot/modules/geoip/geodb.h knot/modules/noudp/noudp.c \
+ knot/modules/onlinesign/onlinesign.c \
+ knot/modules/onlinesign/nsec_next.c \
+ knot/modules/onlinesign/nsec_next.h knot/modules/probe/probe.c \
+ knot/modules/queryacl/queryacl.c knot/modules/rrl/rrl.c \
+ knot/modules/rrl/functions.c knot/modules/rrl/functions.h \
+ knot/modules/stats/stats.c \
+ knot/modules/synthrecord/synthrecord.c \
+ knot/modules/whoami/whoami.c
+am__objects_3 = knot/modules/cookies/libknotd_la-cookies.lo
+@STATIC_MODULE_cookies_TRUE@am__objects_4 = $(am__objects_3)
+am__objects_5 = knot/modules/dnsproxy/libknotd_la-dnsproxy.lo
+@STATIC_MODULE_dnsproxy_TRUE@am__objects_6 = $(am__objects_5)
+am__objects_7 = knot/modules/dnstap/libknotd_la-dnstap.lo
+@STATIC_MODULE_dnstap_TRUE@am__objects_8 = $(am__objects_7)
+am__objects_9 = knot/modules/geoip/libknotd_la-geoip.lo \
+ knot/modules/geoip/libknotd_la-geodb.lo
+@STATIC_MODULE_geoip_TRUE@am__objects_10 = $(am__objects_9)
+am__objects_11 = knot/modules/noudp/libknotd_la-noudp.lo
+@STATIC_MODULE_noudp_TRUE@am__objects_12 = $(am__objects_11)
+am__objects_13 = knot/modules/onlinesign/libknotd_la-onlinesign.lo \
+ knot/modules/onlinesign/libknotd_la-nsec_next.lo
+@STATIC_MODULE_onlinesign_TRUE@am__objects_14 = $(am__objects_13)
+am__objects_15 = knot/modules/probe/libknotd_la-probe.lo
+@STATIC_MODULE_probe_TRUE@am__objects_16 = $(am__objects_15)
+am__objects_17 = knot/modules/queryacl/libknotd_la-queryacl.lo
+@STATIC_MODULE_queryacl_TRUE@am__objects_18 = $(am__objects_17)
+am__objects_19 = knot/modules/rrl/libknotd_la-rrl.lo \
+ knot/modules/rrl/libknotd_la-functions.lo
+@STATIC_MODULE_rrl_TRUE@am__objects_20 = $(am__objects_19)
+am__objects_21 = knot/modules/stats/libknotd_la-stats.lo
+@STATIC_MODULE_stats_TRUE@am__objects_22 = $(am__objects_21)
+am__objects_23 = knot/modules/synthrecord/libknotd_la-synthrecord.lo
+@STATIC_MODULE_synthrecord_TRUE@am__objects_24 = $(am__objects_23)
+am__objects_25 = knot/modules/whoami/libknotd_la-whoami.lo
+@STATIC_MODULE_whoami_TRUE@am__objects_26 = $(am__objects_25)
+am_libknotd_la_OBJECTS = knot/catalog/libknotd_la-catalog_db.lo \
+ knot/catalog/libknotd_la-catalog_update.lo \
+ knot/catalog/libknotd_la-generate.lo \
+ knot/catalog/libknotd_la-interpret.lo \
+ knot/conf/libknotd_la-base.lo knot/conf/libknotd_la-conf.lo \
+ knot/conf/libknotd_la-confdb.lo \
+ knot/conf/libknotd_la-confio.lo \
+ knot/conf/libknotd_la-migration.lo \
+ knot/conf/libknotd_la-module.lo \
+ knot/conf/libknotd_la-schema.lo knot/conf/libknotd_la-tools.lo \
+ knot/ctl/libknotd_la-commands.lo \
+ knot/ctl/libknotd_la-process.lo \
+ knot/dnssec/libknotd_la-context.lo \
+ knot/dnssec/libknotd_la-ds_query.lo \
+ knot/dnssec/kasp/libknotd_la-kasp_db.lo \
+ knot/dnssec/kasp/libknotd_la-kasp_zone.lo \
+ knot/dnssec/kasp/libknotd_la-keystate.lo \
+ knot/dnssec/kasp/libknotd_la-keystore.lo \
+ knot/dnssec/libknotd_la-key-events.lo \
+ knot/dnssec/libknotd_la-key_records.lo \
+ knot/dnssec/libknotd_la-nsec-chain.lo \
+ knot/dnssec/libknotd_la-nsec3-chain.lo \
+ knot/dnssec/libknotd_la-policy.lo \
+ knot/dnssec/libknotd_la-rrset-sign.lo \
+ knot/dnssec/libknotd_la-zone-events.lo \
+ knot/dnssec/libknotd_la-zone-keys.lo \
+ knot/dnssec/libknotd_la-zone-nsec.lo \
+ knot/dnssec/libknotd_la-zone-sign.lo \
+ knot/events/libknotd_la-events.lo \
+ knot/events/handlers/libknotd_la-backup.lo \
+ knot/events/handlers/libknotd_la-dnssec.lo \
+ knot/events/handlers/libknotd_la-ds_check.lo \
+ knot/events/handlers/libknotd_la-ds_push.lo \
+ knot/events/handlers/libknotd_la-expire.lo \
+ knot/events/handlers/libknotd_la-flush.lo \
+ knot/events/handlers/libknotd_la-freeze_thaw.lo \
+ knot/events/handlers/libknotd_la-load.lo \
+ knot/events/handlers/libknotd_la-notify.lo \
+ knot/events/handlers/libknotd_la-refresh.lo \
+ knot/events/handlers/libknotd_la-update.lo \
+ knot/events/libknotd_la-replan.lo \
+ knot/nameserver/libknotd_la-axfr.lo \
+ knot/nameserver/libknotd_la-chaos.lo \
+ knot/nameserver/libknotd_la-internet.lo \
+ knot/nameserver/libknotd_la-ixfr.lo \
+ knot/nameserver/libknotd_la-notify.lo \
+ knot/nameserver/libknotd_la-nsec_proofs.lo \
+ knot/nameserver/libknotd_la-process_query.lo \
+ knot/nameserver/libknotd_la-query_module.lo \
+ knot/nameserver/libknotd_la-tsig_ctx.lo \
+ knot/nameserver/libknotd_la-update.lo \
+ knot/nameserver/libknotd_la-xfr.lo \
+ knot/query/libknotd_la-capture.lo \
+ knot/query/libknotd_la-query.lo \
+ knot/query/libknotd_la-requestor.lo \
+ knot/common/libknotd_la-evsched.lo \
+ knot/common/libknotd_la-fdset.lo \
+ knot/common/libknotd_la-log.lo \
+ knot/common/libknotd_la-process.lo \
+ knot/common/libknotd_la-stats.lo \
+ knot/common/libknotd_la-systemd.lo \
+ knot/common/libknotd_la-unreachable.lo \
+ knot/journal/libknotd_la-journal_basic.lo \
+ knot/journal/libknotd_la-journal_metadata.lo \
+ knot/journal/libknotd_la-journal_read.lo \
+ knot/journal/libknotd_la-journal_write.lo \
+ knot/journal/libknotd_la-knot_lmdb.lo \
+ knot/journal/libknotd_la-serialization.lo \
+ knot/server/libknotd_la-dthreads.lo \
+ knot/server/libknotd_la-proxyv2.lo \
+ knot/server/libknotd_la-server.lo \
+ knot/server/libknotd_la-tcp-handler.lo \
+ knot/server/libknotd_la-udp-handler.lo \
+ knot/server/libknotd_la-xdp-handler.lo \
+ knot/updates/libknotd_la-acl.lo \
+ knot/updates/libknotd_la-apply.lo \
+ knot/updates/libknotd_la-changesets.lo \
+ knot/updates/libknotd_la-ddns.lo \
+ knot/updates/libknotd_la-zone-update.lo \
+ knot/worker/libknotd_la-pool.lo \
+ knot/worker/libknotd_la-queue.lo \
+ knot/zone/libknotd_la-adds_tree.lo \
+ knot/zone/libknotd_la-adjust.lo \
+ knot/zone/libknotd_la-backup.lo \
+ knot/zone/libknotd_la-backup_dir.lo \
+ knot/zone/libknotd_la-contents.lo \
+ knot/zone/libknotd_la-digest.lo \
+ knot/zone/libknotd_la-measure.lo knot/zone/libknotd_la-node.lo \
+ knot/zone/libknotd_la-semantic-check.lo \
+ knot/zone/libknotd_la-serial.lo \
+ knot/zone/libknotd_la-timers.lo \
+ knot/zone/libknotd_la-zone-diff.lo \
+ knot/zone/libknotd_la-zone-dump.lo \
+ knot/zone/libknotd_la-zone-load.lo \
+ knot/zone/libknotd_la-zone-tree.lo \
+ knot/zone/libknotd_la-zone.lo \
+ knot/zone/libknotd_la-zonedb-load.lo \
+ knot/zone/libknotd_la-zonedb.lo \
+ knot/zone/libknotd_la-zonefile.lo $(am__objects_4) \
+ $(am__objects_6) $(am__objects_8) $(am__objects_10) \
+ $(am__objects_12) $(am__objects_14) $(am__objects_16) \
+ $(am__objects_18) $(am__objects_20) $(am__objects_22) \
+ $(am__objects_24) $(am__objects_26)
+libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS)
+libknotd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libknotd_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_DAEMON_TRUE@am_libknotd_la_rpath =
+@EMBEDDED_LIBNGTCP2_TRUE@@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_14 = $(am__DEPENDENCIES_6)
+@HAVE_LIBUTILS_TRUE@libknotus_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_14)
+am__libknotus_la_SOURCES_DIST = utils/common/cert.c \
+ utils/common/cert.h utils/common/exec.c utils/common/exec.h \
+ utils/common/hex.c utils/common/hex.h utils/common/https.c \
+ utils/common/https.h utils/common/lookup.c \
+ utils/common/lookup.h utils/common/msg.c utils/common/msg.h \
+ utils/common/netio.c utils/common/netio.h \
+ utils/common/params.c utils/common/params.h \
+ utils/common/quic.c utils/common/quic.h utils/common/resolv.c \
+ utils/common/resolv.h utils/common/sign.c utils/common/sign.h \
+ utils/common/tls.c utils/common/tls.h utils/common/token.c \
+ utils/common/token.h utils/common/util_conf.c \
+ utils/common/util_conf.h
+@HAVE_LIBUTILS_TRUE@am_libknotus_la_OBJECTS = \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-cert.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-exec.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-hex.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-https.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-lookup.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-msg.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-netio.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-params.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-quic.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-resolv.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-sign.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-tls.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-token.lo \
+@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-util_conf.lo
+libknotus_la_OBJECTS = $(am_libknotus_la_OBJECTS)
+libknotus_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libknotus_la_LDFLAGS) $(LDFLAGS) -o $@
+@HAVE_LIBUTILS_TRUE@am_libknotus_la_rpath =
+libzscanner_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__objects_27 =
+am_libzscanner_la_OBJECTS = libzscanner/la-error.lo \
+ libzscanner/la-functions.lo $(am__objects_27)
+nodist_libzscanner_la_OBJECTS = libzscanner/la-scanner.lo
+libzscanner_la_OBJECTS = $(am_libzscanner_la_OBJECTS) \
+ $(nodist_libzscanner_la_OBJECTS)
+libzscanner_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libzscanner_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+am__kcatalogprint_SOURCES_DIST = utils/kcatalogprint/main.c
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kcatalogprint_OBJECTS = utils/kcatalogprint/kcatalogprint-main.$(OBJEXT)
+kcatalogprint_OBJECTS = $(am_kcatalogprint_OBJECTS)
+am__DEPENDENCIES_15 = libknotd.la libknot.la libdnssec.la \
+ libzscanner.la $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+@HAVE_LIBUTILS_TRUE@am__DEPENDENCIES_16 = libknotus.la libknot.la \
+@HAVE_LIBUTILS_TRUE@ libdnssec.la $(am__DEPENDENCIES_3) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
+kcatalogprint_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(kcatalogprint_LDFLAGS) $(LDFLAGS) -o $@
+am__kdig_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \
+ utils/kdig/kdig_main.c utils/kdig/kdig_params.c \
+ utils/kdig/kdig_params.h
+@HAVE_UTILS_TRUE@am_kdig_OBJECTS = \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_exec.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_main.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_params.$(OBJEXT)
+kdig_OBJECTS = $(am_kdig_OBJECTS)
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_17 = \
+@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_4)
+@HAVE_UTILS_TRUE@kdig_DEPENDENCIES = $(am__DEPENDENCIES_16) \
+@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_17)
+am__keymgr_SOURCES_DIST = utils/keymgr/bind_privkey.c \
+ utils/keymgr/bind_privkey.h utils/keymgr/functions.c \
+ utils/keymgr/functions.h utils/keymgr/offline_ksk.c \
+ utils/keymgr/offline_ksk.h utils/keymgr/main.c
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_keymgr_OBJECTS = utils/keymgr/keymgr-bind_privkey.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-functions.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-offline_ksk.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-main.$(OBJEXT)
+keymgr_OBJECTS = $(am_keymgr_OBJECTS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
+keymgr_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(keymgr_LDFLAGS) $(LDFLAGS) -o $@
+am__khost_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \
+ utils/kdig/kdig_params.c utils/kdig/kdig_params.h \
+ utils/khost/khost_main.c utils/khost/khost_params.c \
+ utils/khost/khost_params.h
+@HAVE_UTILS_TRUE@am_khost_OBJECTS = \
+@HAVE_UTILS_TRUE@ utils/kdig/khost-kdig_exec.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/kdig/khost-kdig_params.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/khost/khost-khost_main.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/khost/khost-khost_params.$(OBJEXT)
+khost_OBJECTS = $(am_khost_OBJECTS)
+@HAVE_UTILS_TRUE@khost_DEPENDENCIES = $(am__DEPENDENCIES_16) \
+@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_17)
+am__kjournalprint_SOURCES_DIST = utils/kjournalprint/main.c
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kjournalprint_OBJECTS = utils/kjournalprint/kjournalprint-main.$(OBJEXT)
+kjournalprint_OBJECTS = $(am_kjournalprint_OBJECTS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
+kjournalprint_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(kjournalprint_LDFLAGS) $(LDFLAGS) -o $@
+am__knotc_SOURCES_DIST = utils/knotc/commands.c utils/knotc/commands.h \
+ utils/knotc/interactive.c utils/knotc/interactive.h \
+ utils/knotc/process.c utils/knotc/process.h utils/knotc/main.c
+@HAVE_DAEMON_TRUE@am_knotc_OBJECTS = \
+@HAVE_DAEMON_TRUE@ utils/knotc/knotc-commands.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@ utils/knotc/knotc-interactive.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@ utils/knotc/knotc-process.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@ utils/knotc/knotc-main.$(OBJEXT)
+knotc_OBJECTS = $(am_knotc_OBJECTS)
+@HAVE_DAEMON_TRUE@knotc_DEPENDENCIES = $(am__DEPENDENCIES_15) \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_16)
+knotc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(knotc_LDFLAGS) $(LDFLAGS) -o $@
+am__knotd_SOURCES_DIST = utils/knotd/main.c
+@HAVE_DAEMON_TRUE@am_knotd_OBJECTS = utils/knotd/knotd-main.$(OBJEXT)
+knotd_OBJECTS = $(am_knotd_OBJECTS)
+@HAVE_DAEMON_TRUE@knotd_DEPENDENCIES = $(am__DEPENDENCIES_1) \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_15) $(am__DEPENDENCIES_1)
+knotd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(knotd_LDFLAGS) $(LDFLAGS) -o $@
+am__knsec3hash_SOURCES_DIST = utils/knsec3hash/knsec3hash.c
+@HAVE_UTILS_TRUE@am_knsec3hash_OBJECTS = utils/knsec3hash/knsec3hash-knsec3hash.$(OBJEXT)
+knsec3hash_OBJECTS = $(am_knsec3hash_OBJECTS)
+@HAVE_UTILS_TRUE@knsec3hash_DEPENDENCIES = libknot.la libdnssec.la \
+@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_3)
+am__knsupdate_SOURCES_DIST = utils/knsupdate/knsupdate_exec.c \
+ utils/knsupdate/knsupdate_exec.h \
+ utils/knsupdate/knsupdate_interactive.c \
+ utils/knsupdate/knsupdate_interactive.h \
+ utils/knsupdate/knsupdate_main.c \
+ utils/knsupdate/knsupdate_params.c \
+ utils/knsupdate/knsupdate_params.h
+@HAVE_UTILS_TRUE@am_knsupdate_OBJECTS = utils/knsupdate/knsupdate-knsupdate_exec.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_interactive.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_main.$(OBJEXT) \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_params.$(OBJEXT)
+knsupdate_OBJECTS = $(am_knsupdate_OBJECTS)
+@HAVE_UTILS_TRUE@knsupdate_DEPENDENCIES = $(am__DEPENDENCIES_16) \
+@HAVE_UTILS_TRUE@ libzscanner.la
+am__kxdpgun_SOURCES_DIST = utils/kxdpgun/ip_route.c \
+ utils/kxdpgun/ip_route.h utils/kxdpgun/load_queries.c \
+ utils/kxdpgun/load_queries.h utils/kxdpgun/main.c
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am_kxdpgun_OBJECTS = utils/kxdpgun/kxdpgun-ip_route.$(OBJEXT) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-load_queries.$(OBJEXT) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/kxdpgun-main.$(OBJEXT)
+kxdpgun_OBJECTS = $(am_kxdpgun_OBJECTS)
+@ENABLE_QUIC_TRUE@@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_18 = $(am__DEPENDENCIES_1)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_DEPENDENCIES = libknot.la \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_3) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_18)
+am__kzonecheck_SOURCES_DIST = utils/kzonecheck/main.c \
+ utils/kzonecheck/zone_check.c utils/kzonecheck/zone_check.h
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kzonecheck_OBJECTS = utils/kzonecheck/kzonecheck-main.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/kzonecheck-zone_check.$(OBJEXT)
+kzonecheck_OBJECTS = $(am_kzonecheck_OBJECTS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15)
+kzonecheck_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(kzonecheck_LDFLAGS) $(LDFLAGS) -o $@
+am__kzonesign_SOURCES_DIST = utils/kzonesign/main.c
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kzonesign_OBJECTS = utils/kzonesign/kzonesign-main.$(OBJEXT)
+kzonesign_OBJECTS = $(am_kzonesign_OBJECTS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_15) \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_16)
+kzonesign_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(kzonesign_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 = contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-base64.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-base64url.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-conn_pool.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-files.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-getline.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-json.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-net.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-semaphore.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-string.Plo \
+ contrib/$(DEPDIR)/libcontrib_la-time.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo \
+ contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Plo \
+ contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Plo \
+ contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Plo \
+ contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Plo \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Plo \
+ contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Plo \
+ contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo \
+ contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo \
+ contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo \
+ contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Plo \
+ contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo \
+ contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo \
+ contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo \
+ contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo \
+ contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Plo \
+ contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Plo \
+ knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Plo \
+ knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo \
+ knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo \
+ knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-evsched.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-fdset.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-log.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-process.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-stats.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-systemd.Plo \
+ knot/common/$(DEPDIR)/libknotd_la-unreachable.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-base.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-conf.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-confio.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-migration.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-module.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-schema.Plo \
+ knot/conf/$(DEPDIR)/libknotd_la-tools.Plo \
+ knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo \
+ knot/ctl/$(DEPDIR)/libknotd_la-process.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo \
+ knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo \
+ knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo \
+ knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo \
+ knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo \
+ knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo \
+ knot/events/$(DEPDIR)/libknotd_la-events.Plo \
+ knot/events/$(DEPDIR)/libknotd_la-replan.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo \
+ knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-journal_write.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Plo \
+ knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo \
+ knot/modules/cookies/$(DEPDIR)/la-cookies.Plo \
+ knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo \
+ knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Plo \
+ knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo \
+ knot/modules/dnstap/$(DEPDIR)/la-dnstap.Plo \
+ knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo \
+ knot/modules/geoip/$(DEPDIR)/la-geodb.Plo \
+ knot/modules/geoip/$(DEPDIR)/la-geoip.Plo \
+ knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo \
+ knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo \
+ knot/modules/noudp/$(DEPDIR)/la-noudp.Plo \
+ knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo \
+ knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Plo \
+ knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Plo \
+ knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo \
+ knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo \
+ knot/modules/probe/$(DEPDIR)/la-probe.Plo \
+ knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Plo \
+ knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo \
+ knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo \
+ knot/modules/rrl/$(DEPDIR)/la-functions.Plo \
+ knot/modules/rrl/$(DEPDIR)/la-rrl.Plo \
+ knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo \
+ knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo \
+ knot/modules/stats/$(DEPDIR)/la-stats.Plo \
+ knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo \
+ knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Plo \
+ knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo \
+ knot/modules/whoami/$(DEPDIR)/la-whoami.Plo \
+ knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo \
+ knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo \
+ knot/query/$(DEPDIR)/libknotd_la-capture.Plo \
+ knot/query/$(DEPDIR)/libknotd_la-query.Plo \
+ knot/query/$(DEPDIR)/libknotd_la-requestor.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-server.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo \
+ knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Plo \
+ knot/updates/$(DEPDIR)/libknotd_la-acl.Plo \
+ knot/updates/$(DEPDIR)/libknotd_la-apply.Plo \
+ knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo \
+ knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo \
+ knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo \
+ knot/worker/$(DEPDIR)/libknotd_la-pool.Plo \
+ knot/worker/$(DEPDIR)/libknotd_la-queue.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-adjust.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-backup.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-contents.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-digest.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-measure.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-node.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-serial.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-timers.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zone.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo \
+ knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo \
+ libdnssec/$(DEPDIR)/la-binary.Plo \
+ libdnssec/$(DEPDIR)/la-crypto.Plo \
+ libdnssec/$(DEPDIR)/la-digest.Plo \
+ libdnssec/$(DEPDIR)/la-error.Plo \
+ libdnssec/$(DEPDIR)/la-keyid.Plo \
+ libdnssec/$(DEPDIR)/la-pem.Plo \
+ libdnssec/$(DEPDIR)/la-random.Plo \
+ libdnssec/$(DEPDIR)/la-tsig.Plo \
+ libdnssec/key/$(DEPDIR)/la-algorithm.Plo \
+ libdnssec/key/$(DEPDIR)/la-convert.Plo \
+ libdnssec/key/$(DEPDIR)/la-dnskey.Plo \
+ libdnssec/key/$(DEPDIR)/la-ds.Plo \
+ libdnssec/key/$(DEPDIR)/la-key.Plo \
+ libdnssec/key/$(DEPDIR)/la-keytag.Plo \
+ libdnssec/key/$(DEPDIR)/la-privkey.Plo \
+ libdnssec/key/$(DEPDIR)/la-simple.Plo \
+ libdnssec/keystore/$(DEPDIR)/la-keystore.Plo \
+ libdnssec/keystore/$(DEPDIR)/la-pkcs11.Plo \
+ libdnssec/keystore/$(DEPDIR)/la-pkcs8.Plo \
+ libdnssec/nsec/$(DEPDIR)/la-bitmap.Plo \
+ libdnssec/nsec/$(DEPDIR)/la-hash.Plo \
+ libdnssec/nsec/$(DEPDIR)/la-nsec.Plo \
+ libdnssec/p11/$(DEPDIR)/la-p11.Plo \
+ libdnssec/shared/$(DEPDIR)/la-bignum.Plo \
+ libdnssec/shared/$(DEPDIR)/la-dname.Plo \
+ libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Plo \
+ libdnssec/sign/$(DEPDIR)/la-der.Plo \
+ libdnssec/sign/$(DEPDIR)/la-sign.Plo \
+ libknot/$(DEPDIR)/la-codes.Plo \
+ libknot/$(DEPDIR)/la-cookies.Plo \
+ libknot/$(DEPDIR)/la-descriptor.Plo \
+ libknot/$(DEPDIR)/la-dname.Plo libknot/$(DEPDIR)/la-error.Plo \
+ libknot/$(DEPDIR)/la-rdataset.Plo \
+ libknot/$(DEPDIR)/la-rrset-dump.Plo \
+ libknot/$(DEPDIR)/la-rrset.Plo \
+ libknot/$(DEPDIR)/la-tsig-op.Plo libknot/$(DEPDIR)/la-tsig.Plo \
+ libknot/control/$(DEPDIR)/la-control.Plo \
+ libknot/db/$(DEPDIR)/la-db_lmdb.Plo \
+ libknot/db/$(DEPDIR)/la-db_trie.Plo \
+ libknot/packet/$(DEPDIR)/la-pkt.Plo \
+ libknot/packet/$(DEPDIR)/la-rrset-wire.Plo \
+ libknot/probe/$(DEPDIR)/la-data.Plo \
+ libknot/probe/$(DEPDIR)/la-probe.Plo \
+ libknot/rrtype/$(DEPDIR)/la-naptr.Plo \
+ libknot/rrtype/$(DEPDIR)/la-opt.Plo \
+ libknot/rrtype/$(DEPDIR)/la-tsig.Plo \
+ libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Plo \
+ libknot/xdp/$(DEPDIR)/la-bpf-user.Plo \
+ libknot/xdp/$(DEPDIR)/la-eth.Plo \
+ libknot/xdp/$(DEPDIR)/la-quic.Plo \
+ libknot/xdp/$(DEPDIR)/la-quic_conn.Plo \
+ libknot/xdp/$(DEPDIR)/la-tcp.Plo \
+ libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Plo \
+ libknot/xdp/$(DEPDIR)/la-xdp.Plo \
+ libknot/yparser/$(DEPDIR)/la-yparser.Plo \
+ libknot/yparser/$(DEPDIR)/la-ypbody.Plo \
+ libknot/yparser/$(DEPDIR)/la-ypformat.Plo \
+ libknot/yparser/$(DEPDIR)/la-ypschema.Plo \
+ libknot/yparser/$(DEPDIR)/la-yptrafo.Plo \
+ libzscanner/$(DEPDIR)/la-error.Plo \
+ libzscanner/$(DEPDIR)/la-functions.Plo \
+ libzscanner/$(DEPDIR)/la-scanner.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-cert.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-exec.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-hex.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-https.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-lookup.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-msg.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-netio.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-params.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-quic.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-resolv.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-sign.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-tls.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-token.Plo \
+ utils/common/$(DEPDIR)/libknotus_la-util_conf.Plo \
+ utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po \
+ utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po \
+ utils/kdig/$(DEPDIR)/kdig-kdig_main.Po \
+ utils/kdig/$(DEPDIR)/kdig-kdig_params.Po \
+ utils/kdig/$(DEPDIR)/khost-kdig_exec.Po \
+ utils/kdig/$(DEPDIR)/khost-kdig_params.Po \
+ utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po \
+ utils/keymgr/$(DEPDIR)/keymgr-functions.Po \
+ utils/keymgr/$(DEPDIR)/keymgr-main.Po \
+ utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po \
+ utils/khost/$(DEPDIR)/khost-khost_main.Po \
+ utils/khost/$(DEPDIR)/khost-khost_params.Po \
+ utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po \
+ utils/knotc/$(DEPDIR)/knotc-commands.Po \
+ utils/knotc/$(DEPDIR)/knotc-interactive.Po \
+ utils/knotc/$(DEPDIR)/knotc-main.Po \
+ utils/knotc/$(DEPDIR)/knotc-process.Po \
+ utils/knotd/$(DEPDIR)/knotd-main.Po \
+ utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po \
+ utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po \
+ utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po \
+ utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po \
+ utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po \
+ utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po \
+ utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po \
+ utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po \
+ utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po \
+ utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po \
+ utils/kzonesign/$(DEPDIR)/kzonesign-main.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 = $(knot_modules_cookies_la_SOURCES) \
+ $(knot_modules_dnsproxy_la_SOURCES) \
+ $(knot_modules_dnstap_la_SOURCES) \
+ $(knot_modules_geoip_la_SOURCES) \
+ $(knot_modules_noudp_la_SOURCES) \
+ $(knot_modules_onlinesign_la_SOURCES) \
+ $(knot_modules_probe_la_SOURCES) \
+ $(knot_modules_queryacl_la_SOURCES) \
+ $(knot_modules_rrl_la_SOURCES) \
+ $(knot_modules_stats_la_SOURCES) \
+ $(knot_modules_synthrecord_la_SOURCES) \
+ $(knot_modules_whoami_la_SOURCES) $(libcontrib_la_SOURCES) \
+ $(libdnssec_la_SOURCES) $(libdnstap_la_SOURCES) \
+ $(nodist_libdnstap_la_SOURCES) $(libembbpf_la_SOURCES) \
+ $(libembngtcp2_la_SOURCES) $(libknot_la_SOURCES) \
+ $(libknotd_la_SOURCES) $(libknotus_la_SOURCES) \
+ $(libzscanner_la_SOURCES) $(nodist_libzscanner_la_SOURCES) \
+ $(kcatalogprint_SOURCES) $(kdig_SOURCES) $(keymgr_SOURCES) \
+ $(khost_SOURCES) $(kjournalprint_SOURCES) $(knotc_SOURCES) \
+ $(knotd_SOURCES) $(knsec3hash_SOURCES) $(knsupdate_SOURCES) \
+ $(kxdpgun_SOURCES) $(kzonecheck_SOURCES) $(kzonesign_SOURCES)
+DIST_SOURCES = $(knot_modules_cookies_la_SOURCES) \
+ $(knot_modules_dnsproxy_la_SOURCES) \
+ $(knot_modules_dnstap_la_SOURCES) \
+ $(knot_modules_geoip_la_SOURCES) \
+ $(knot_modules_noudp_la_SOURCES) \
+ $(knot_modules_onlinesign_la_SOURCES) \
+ $(knot_modules_probe_la_SOURCES) \
+ $(knot_modules_queryacl_la_SOURCES) \
+ $(knot_modules_rrl_la_SOURCES) \
+ $(knot_modules_stats_la_SOURCES) \
+ $(knot_modules_synthrecord_la_SOURCES) \
+ $(knot_modules_whoami_la_SOURCES) $(libcontrib_la_SOURCES) \
+ $(libdnssec_la_SOURCES) $(am__libdnstap_la_SOURCES_DIST) \
+ $(am__libembbpf_la_SOURCES_DIST) \
+ $(am__libembngtcp2_la_SOURCES_DIST) \
+ $(am__libknot_la_SOURCES_DIST) $(am__libknotd_la_SOURCES_DIST) \
+ $(am__libknotus_la_SOURCES_DIST) $(libzscanner_la_SOURCES) \
+ $(am__kcatalogprint_SOURCES_DIST) $(am__kdig_SOURCES_DIST) \
+ $(am__keymgr_SOURCES_DIST) $(am__khost_SOURCES_DIST) \
+ $(am__kjournalprint_SOURCES_DIST) $(am__knotc_SOURCES_DIST) \
+ $(am__knotd_SOURCES_DIST) $(am__knsec3hash_SOURCES_DIST) \
+ $(am__knsupdate_SOURCES_DIST) $(am__kxdpgun_SOURCES_DIST) \
+ $(am__kzonecheck_SOURCES_DIST) $(am__kzonesign_SOURCES_DIST)
+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
+DATA = $(pkgconfig_DATA)
+am__nobase_include_libknot_HEADERS_DIST = libknot/attribute.h \
+ libknot/codes.h libknot/consts.h libknot/control/control.h \
+ libknot/cookies.h libknot/descriptor.h libknot/dname.h \
+ libknot/dynarray.h libknot/endian.h libknot/errcode.h \
+ libknot/error.h libknot/libknot.h libknot/lookup.h \
+ libknot/mm_ctx.h libknot/db/db.h libknot/db/db_lmdb.h \
+ libknot/db/db_trie.h libknot/packet/compr.h \
+ libknot/packet/pkt.h libknot/packet/rrset-wire.h \
+ libknot/packet/wire.h libknot/probe/data.h \
+ libknot/probe/probe.h libknot/rdata.h libknot/rdataset.h \
+ libknot/rrset-dump.h libknot/rrset.h libknot/rrtype/dnskey.h \
+ libknot/rrtype/ds.h libknot/rrtype/naptr.h \
+ libknot/rrtype/nsec.h libknot/rrtype/nsec3.h \
+ libknot/rrtype/nsec3param.h libknot/rrtype/opt.h \
+ libknot/rrtype/rdname.h libknot/rrtype/rrsig.h \
+ libknot/rrtype/soa.h libknot/rrtype/svcb.h \
+ libknot/rrtype/tsig.h libknot/rrtype/zonemd.h \
+ libknot/tsig-op.h libknot/tsig.h libknot/wire.h \
+ libknot/yparser/yparser.h libknot/yparser/ypformat.h \
+ libknot/yparser/ypschema.h libknot/yparser/yptrafo.h \
+ libknot/version.h libknot/xdp/tcp_iobuf.h libknot/xdp.h \
+ libknot/xdp/bpf-consts.h libknot/xdp/eth.h libknot/xdp/msg.h \
+ libknot/xdp/tcp.h libknot/xdp/xdp.h libknot/xdp/quic.h \
+ libknot/xdp/quic_conn.h
+HEADERS = $(include_libdnssec_HEADERS) $(include_libknotd_HEADERS) \
+ $(include_libzscanner_HEADERS) \
+ $(nobase_include_libknot_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__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
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+ $(srcdir)/contrib/Makefile.inc $(srcdir)/knot/Makefile.inc \
+ $(srcdir)/knot/modules/cookies/Makefile.inc \
+ $(srcdir)/knot/modules/dnsproxy/Makefile.inc \
+ $(srcdir)/knot/modules/dnstap/Makefile.inc \
+ $(srcdir)/knot/modules/geoip/Makefile.inc \
+ $(srcdir)/knot/modules/noudp/Makefile.inc \
+ $(srcdir)/knot/modules/onlinesign/Makefile.inc \
+ $(srcdir)/knot/modules/probe/Makefile.inc \
+ $(srcdir)/knot/modules/queryacl/Makefile.inc \
+ $(srcdir)/knot/modules/rrl/Makefile.inc \
+ $(srcdir)/knot/modules/stats/Makefile.inc \
+ $(srcdir)/knot/modules/synthrecord/Makefile.inc \
+ $(srcdir)/knot/modules/whoami/Makefile.inc \
+ $(srcdir)/knotd.pc.in $(srcdir)/libdnssec.pc.in \
+ $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot.pc.in \
+ $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner.pc.in \
+ $(srcdir)/libzscanner/Makefile.inc \
+ $(srcdir)/utils/Makefile.inc $(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"
+pkglibdir = $(module_instdir)
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -DCONFIG_DIR='"${config_dir}"' \
+ -DSTORAGE_DIR='"${storage_dir}"' \
+ -DRUN_DIR='"${run_dir}"' \
+ -DMODULE_DIR='"${module_dir}"' \
+ -DMODULE_INSTDIR='"${module_instdir}"'
+
+AM_LDFLAGS = $(LT_NO_UNDEFINED)
+EXTRA_DIST = contrib/licenses/0BSD contrib/licenses/BSD-3-Clause \
+ contrib/licenses/LGPL-2.0 contrib/licenses/LGPL-2.1 \
+ contrib/licenses/MIT contrib/libbpf/LICENSE \
+ contrib/libngtcp2/LICENSE contrib/openbsd/LICENSE \
+ contrib/ucw/LICENSE contrib/url-parser/LICENSE \
+ contrib/url-parser/README.md contrib/dnstap/dnstap.proto \
+ libzscanner/scanner.rl libzscanner/scanner_body.rl \
+ libzscanner/scanner.c.g2 libzscanner/scanner.c.t0 \
+ knot/modules/cookies/cookies.rst \
+ knot/modules/dnsproxy/dnsproxy.rst \
+ knot/modules/dnstap/dnstap.rst knot/modules/geoip/geoip.rst \
+ knot/modules/noudp/noudp.rst \
+ knot/modules/onlinesign/onlinesign.rst \
+ knot/modules/probe/probe.rst \
+ knot/modules/queryacl/queryacl.rst knot/modules/rrl/rrl.rst \
+ knot/modules/stats/stats.rst \
+ knot/modules/synthrecord/synthrecord.rst \
+ knot/modules/whoami/whoami.rst
+CLEANFILES = $(am__append_6) libzscanner/scanner.c
+BUILT_SOURCES = $(am__append_5) libzscanner/scanner.c
+lib_LTLIBRARIES = libdnssec.la libknot.la libzscanner.la
+noinst_LTLIBRARIES = libcontrib.la $(am__append_3) $(am__append_4) \
+ $(am__append_7) $(am__append_20) $(am__append_50)
+pkgconfig_DATA = libdnssec.pc libknot.pc libzscanner.pc \
+ $(am__append_21)
+libcontrib_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
+ $(am__append_1)
+libcontrib_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libcontrib_la_LIBADD = $(pthread_LIBS)
+libcontrib_LIBS = libcontrib.la $(am__append_2)
+libcontrib_la_SOURCES = \
+ contrib/asan.h \
+ contrib/base32hex.c \
+ contrib/base32hex.h \
+ contrib/base64.c \
+ contrib/base64.h \
+ contrib/base64url.c \
+ contrib/base64url.h \
+ contrib/conn_pool.c \
+ contrib/conn_pool.h \
+ contrib/color.h \
+ contrib/ctype.h \
+ contrib/files.c \
+ contrib/files.h \
+ contrib/getline.c \
+ contrib/getline.h \
+ contrib/json.c \
+ contrib/json.h \
+ contrib/macros.h \
+ contrib/mempattern.c \
+ contrib/mempattern.h \
+ contrib/musl/inet_ntop.c \
+ contrib/musl/inet_ntop.h \
+ contrib/net.c \
+ contrib/net.h \
+ contrib/os.h \
+ contrib/qp-trie/trie.c \
+ contrib/qp-trie/trie.h \
+ contrib/semaphore.c \
+ contrib/semaphore.h \
+ contrib/sockaddr.c \
+ contrib/sockaddr.h \
+ contrib/spinlock.h \
+ contrib/string.c \
+ contrib/string.h \
+ contrib/strtonum.h \
+ contrib/time.c \
+ contrib/time.h \
+ contrib/toeplitz.h \
+ contrib/tolower.h \
+ contrib/trim.h \
+ contrib/wire_ctx.h \
+ contrib/openbsd/siphash.c \
+ contrib/openbsd/siphash.h \
+ contrib/openbsd/strlcat.c \
+ contrib/openbsd/strlcat.h \
+ contrib/openbsd/strlcpy.c \
+ contrib/openbsd/strlcpy.h \
+ contrib/proxyv2/proxyv2.c \
+ contrib/proxyv2/proxyv2.h \
+ contrib/ucw/array-sort.h \
+ contrib/ucw/binsearch.h \
+ contrib/ucw/heap.c \
+ contrib/ucw/heap.h \
+ contrib/ucw/lists.c \
+ contrib/ucw/lists.h \
+ contrib/ucw/mempool.c \
+ contrib/ucw/mempool.h \
+ contrib/url-parser/url_parser.c \
+ contrib/url-parser/url_parser.h \
+ contrib/vpool/vpool.c \
+ contrib/vpool/vpool.h
+
+@EMBEDDED_LIBBPF_TRUE@libembbpf_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(embedded_libbpf_CFLAGS)
+@EMBEDDED_LIBBPF_TRUE@libembbpf_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+@EMBEDDED_LIBBPF_TRUE@libembbpf_LIBS = libembbpf.la $(embedded_libbpf_LIBS)
+@EMBEDDED_LIBBPF_TRUE@libembbpf_la_SOURCES = \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/asm/barrier.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/compiler.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/err.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/filter.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/kernel.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/list.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/overflow.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/ring_buffer.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/linux/types.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/bpf_common.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/bpf.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/btf.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/if_link.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/if_xdp.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/include/uapi/linux/netlink.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_core_read.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_endian.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_helper_defs.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_helpers.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_prog_linfo.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/bpf_tracing.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/btf.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/btf.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/btf_dump.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/hashmap.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/hashmap.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf_errno.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf_internal.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf_probes.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/libbpf_util.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/netlink.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/nlattr.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/nlattr.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/str_error.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/str_error.h \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/xsk.c \
+@EMBEDDED_LIBBPF_TRUE@ contrib/libbpf/bpf/xsk.h
+
+@HAVE_LIBDNSTAP_TRUE@libdnstap_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(DNSTAP_CFLAGS)
+@HAVE_LIBDNSTAP_TRUE@libdnstap_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+@HAVE_LIBDNSTAP_TRUE@libdnstap_LIBS = libdnstap.la $(DNSTAP_LIBS)
+@HAVE_LIBDNSTAP_TRUE@SUFFIXES = .proto .pb-c.c .pb-c.h
+@HAVE_LIBDNSTAP_TRUE@libdnstap_la_SOURCES = \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/convert.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/convert.h \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.h \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/message.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/message.h \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/reader.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/reader.h \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/writer.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/writer.h
+
+@HAVE_LIBDNSTAP_TRUE@nodist_libdnstap_la_SOURCES = \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.pb-c.c \
+@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.pb-c.h
+
+@EMBEDDED_LIBNGTCP2_TRUE@libembngtcp2_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
+@EMBEDDED_LIBNGTCP2_TRUE@ $(embedded_libngtcp2_CFLAGS) $(gnutls_CFLAGS)
+
+@EMBEDDED_LIBNGTCP2_TRUE@libembngtcp2_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+@EMBEDDED_LIBNGTCP2_TRUE@libembngtcp2_LIBS = libembngtcp2.la $(embedded_libngtcp2_LIBS) $(gnutls_LIBS)
+@EMBEDDED_LIBNGTCP2_TRUE@libembngtcp2_la_SOURCES = \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/crypto/gnutls.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/crypto/shared.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/crypto/shared.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/ngtcp2.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h \
+@EMBEDDED_LIBNGTCP2_TRUE@ contrib/libngtcp2/ngtcp2/version.h
+
+libdnssec_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS)
+libdnssec_la_LDFLAGS = $(AM_LDFLAGS) $(libdnssec_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS)
+libdnssec_la_LIBADD = $(libcontrib_LIBS) $(gnutls_LIBS) \
+ $(am__append_8)
+include_libdnssecdir = $(includedir)/libdnssec
+include_libdnssec_HEADERS = \
+ libdnssec/binary.h \
+ libdnssec/crypto.h \
+ libdnssec/digest.h \
+ libdnssec/dnssec.h \
+ libdnssec/error.h \
+ libdnssec/key.h \
+ libdnssec/keyid.h \
+ libdnssec/keystore.h \
+ libdnssec/keytag.h \
+ libdnssec/nsec.h \
+ libdnssec/pem.h \
+ libdnssec/random.h \
+ libdnssec/sign.h \
+ libdnssec/tsig.h \
+ libdnssec/version.h
+
+libdnssec_la_SOURCES = \
+ libdnssec/binary.c \
+ libdnssec/crypto.c \
+ libdnssec/digest.c \
+ libdnssec/error.c \
+ libdnssec/key/algorithm.c \
+ libdnssec/key/algorithm.h \
+ libdnssec/key/convert.c \
+ libdnssec/key/convert.h \
+ libdnssec/key/dnskey.c \
+ libdnssec/key/dnskey.h \
+ libdnssec/key/ds.c \
+ libdnssec/key/internal.h \
+ libdnssec/key/key.c \
+ libdnssec/key/keytag.c \
+ libdnssec/key/privkey.c \
+ libdnssec/key/privkey.h \
+ libdnssec/key/simple.c \
+ libdnssec/keyid.c \
+ libdnssec/keystore/internal.h \
+ libdnssec/keystore/keystore.c \
+ libdnssec/keystore/pkcs11.c \
+ libdnssec/keystore/pkcs8.c \
+ libdnssec/nsec/bitmap.c \
+ libdnssec/nsec/hash.c \
+ libdnssec/nsec/nsec.c \
+ libdnssec/p11/p11.c \
+ libdnssec/p11/p11.h \
+ libdnssec/pem.c \
+ libdnssec/random.c \
+ libdnssec/shared/bignum.c \
+ libdnssec/shared/bignum.h \
+ libdnssec/shared/binary_wire.h \
+ libdnssec/shared/dname.c \
+ libdnssec/shared/dname.h \
+ libdnssec/shared/keyid_gnutls.c \
+ libdnssec/shared/keyid_gnutls.h \
+ libdnssec/shared/shared.h \
+ libdnssec/sign/der.c \
+ libdnssec/sign/der.h \
+ libdnssec/sign/sign.c \
+ libdnssec/tsig.c
+
+libknot_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
+ $(lmdb_CFLAGS) ${fuzzer_CFLAGS} $(am__append_11) \
+ $(am__append_15)
+libknot_la_LDFLAGS = $(AM_LDFLAGS) $(libknot_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS) \
+ ${fuzzer_LDFLAGS}
+
+libknot_la_LIBADD = libdnssec.la $(libcontrib_LIBS) $(lmdb_LIBS) \
+ $(math_LIBS) $(am__append_9) $(am__append_10) $(am__append_12) \
+ $(am__append_16)
+include_libknotdir = $(includedir)
+nobase_include_libknot_HEADERS = libknot/attribute.h libknot/codes.h \
+ libknot/consts.h libknot/control/control.h libknot/cookies.h \
+ libknot/descriptor.h libknot/dname.h libknot/dynarray.h \
+ libknot/endian.h libknot/errcode.h libknot/error.h \
+ libknot/libknot.h libknot/lookup.h libknot/mm_ctx.h \
+ libknot/db/db.h libknot/db/db_lmdb.h libknot/db/db_trie.h \
+ libknot/packet/compr.h libknot/packet/pkt.h \
+ libknot/packet/rrset-wire.h libknot/packet/wire.h \
+ libknot/probe/data.h libknot/probe/probe.h libknot/rdata.h \
+ libknot/rdataset.h libknot/rrset-dump.h libknot/rrset.h \
+ libknot/rrtype/dnskey.h libknot/rrtype/ds.h \
+ libknot/rrtype/naptr.h libknot/rrtype/nsec.h \
+ libknot/rrtype/nsec3.h libknot/rrtype/nsec3param.h \
+ libknot/rrtype/opt.h libknot/rrtype/rdname.h \
+ libknot/rrtype/rrsig.h libknot/rrtype/soa.h \
+ libknot/rrtype/svcb.h libknot/rrtype/tsig.h \
+ libknot/rrtype/zonemd.h libknot/tsig-op.h libknot/tsig.h \
+ libknot/wire.h libknot/yparser/yparser.h \
+ libknot/yparser/ypformat.h libknot/yparser/ypschema.h \
+ libknot/yparser/yptrafo.h libknot/version.h \
+ libknot/xdp/tcp_iobuf.h libknot/xdp.h $(am__append_13) \
+ $(am__append_17)
+libknot_la_SOURCES = libknot/codes.c libknot/control/control.c \
+ libknot/cookies.c libknot/descriptor.c libknot/dname.c \
+ libknot/error.c libknot/db/db_lmdb.c libknot/db/db_trie.c \
+ libknot/packet/pkt.c libknot/packet/rrset-wire.c \
+ libknot/probe/data.c libknot/probe/probe.c libknot/rdataset.c \
+ libknot/rrset-dump.c libknot/rrset.c libknot/rrtype/naptr.c \
+ libknot/rrtype/opt.c libknot/rrtype/tsig.c libknot/tsig-op.c \
+ libknot/tsig.c libknot/yparser/yparser.c \
+ libknot/yparser/ypbody.c libknot/yparser/ypformat.c \
+ libknot/yparser/ypschema.c libknot/yparser/yptrafo.c \
+ libknot/xdp/tcp_iobuf.c $(am__append_14) $(am__append_18)
+DIST_SUBDIRS = libknot/xdp
+libzscanner_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) ${fuzzer_CFLAGS}
+libzscanner_la_LDFLAGS = $(AM_LDFLAGS) $(libzscanner_VERSION_INFO) \
+ $(LDFLAG_EXCLUDE_LIBS) ${fuzzer_LDFLAGS}
+
+libzscanner_la_LIBADD = $(math_LIBS)
+include_libzscannerdir = $(includedir)/libzscanner
+include_libzscanner_HEADERS = \
+ libzscanner/error.h \
+ libzscanner/scanner.h \
+ libzscanner/version.h
+
+libzscanner_la_SOURCES = \
+ libzscanner/error.c \
+ libzscanner/functions.h \
+ libzscanner/functions.c \
+ $(include_libzscanner_HEADERS)
+
+nodist_libzscanner_la_SOURCES = \
+ libzscanner/scanner.c
+
+libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
+ $(libkqueue_CFLAGS) $(liburcu_CFLAGS) $(lmdb_CFLAGS) \
+ $(systemd_CFLAGS) $(gnutls_CFLAGS) $(libngtcp2_CFLAGS) \
+ -DKNOTD_MOD_STATIC $(am__append_27) $(am__append_31)
+libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_'
+libknotd_la_LIBADD = $(dlopen_LIBS) $(libkqueue_LIBS) $(pthread_LIBS) \
+ $(libngtcp2_LIBS) $(am__append_19) $(am__append_28) \
+ $(am__append_32)
+libknotd_LIBS = libknotd.la libknot.la libdnssec.la libzscanner.la \
+ $(libcontrib_LIBS) $(liburcu_LIBS) $(lmdb_LIBS) \
+ $(systemd_LIBS) $(gnutls_LIBS)
+
+include_libknotddir = $(includedir)/knot
+include_libknotd_HEADERS = \
+ knot/include/module.h
+
+libknotd_la_SOURCES = knot/catalog/catalog_db.c \
+ knot/catalog/catalog_db.h knot/catalog/catalog_update.c \
+ knot/catalog/catalog_update.h knot/catalog/generate.c \
+ knot/catalog/generate.h knot/catalog/interpret.c \
+ knot/catalog/interpret.h knot/conf/base.c knot/conf/base.h \
+ knot/conf/conf.c knot/conf/conf.h knot/conf/confdb.c \
+ knot/conf/confdb.h knot/conf/confio.c knot/conf/confio.h \
+ knot/conf/migration.c knot/conf/migration.h knot/conf/module.h \
+ knot/conf/module.c knot/conf/schema.c knot/conf/schema.h \
+ knot/conf/tools.c knot/conf/tools.h knot/ctl/commands.c \
+ knot/ctl/commands.h knot/ctl/process.c knot/ctl/process.h \
+ knot/dnssec/context.c knot/dnssec/context.h \
+ knot/dnssec/ds_query.c knot/dnssec/ds_query.h \
+ knot/dnssec/kasp/kasp_db.c knot/dnssec/kasp/kasp_db.h \
+ knot/dnssec/kasp/kasp_zone.c knot/dnssec/kasp/kasp_zone.h \
+ knot/dnssec/kasp/keystate.c knot/dnssec/kasp/keystate.h \
+ knot/dnssec/kasp/keystore.c knot/dnssec/kasp/keystore.h \
+ knot/dnssec/kasp/policy.h knot/dnssec/key-events.c \
+ knot/dnssec/key-events.h knot/dnssec/key_records.c \
+ knot/dnssec/key_records.h knot/dnssec/nsec-chain.c \
+ knot/dnssec/nsec-chain.h knot/dnssec/nsec3-chain.c \
+ knot/dnssec/nsec3-chain.h knot/dnssec/policy.c \
+ knot/dnssec/policy.h knot/dnssec/rrset-sign.c \
+ knot/dnssec/rrset-sign.h knot/dnssec/zone-events.c \
+ knot/dnssec/zone-events.h knot/dnssec/zone-keys.c \
+ knot/dnssec/zone-keys.h knot/dnssec/zone-nsec.c \
+ knot/dnssec/zone-nsec.h knot/dnssec/zone-sign.c \
+ knot/dnssec/zone-sign.h knot/events/events.c \
+ knot/events/events.h knot/events/handlers.h \
+ knot/events/handlers/backup.c knot/events/handlers/dnssec.c \
+ knot/events/handlers/ds_check.c knot/events/handlers/ds_push.c \
+ knot/events/handlers/expire.c knot/events/handlers/flush.c \
+ knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \
+ knot/events/handlers/notify.c knot/events/handlers/refresh.c \
+ knot/events/handlers/update.c knot/events/replan.c \
+ knot/events/replan.h knot/nameserver/axfr.c \
+ knot/nameserver/axfr.h knot/nameserver/chaos.c \
+ knot/nameserver/chaos.h knot/nameserver/internet.c \
+ knot/nameserver/internet.h knot/nameserver/ixfr.c \
+ knot/nameserver/ixfr.h knot/nameserver/log.h \
+ knot/nameserver/notify.c knot/nameserver/notify.h \
+ knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \
+ knot/nameserver/process_query.c \
+ knot/nameserver/process_query.h knot/nameserver/query_module.c \
+ knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \
+ knot/nameserver/tsig_ctx.h knot/nameserver/update.c \
+ knot/nameserver/update.h knot/nameserver/xfr.c \
+ knot/nameserver/xfr.h knot/query/capture.c \
+ knot/query/capture.h knot/query/layer.h knot/query/query.c \
+ knot/query/query.h knot/query/requestor.c \
+ knot/query/requestor.h knot/common/evsched.c \
+ knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \
+ knot/common/log.c knot/common/log.h knot/common/process.c \
+ knot/common/process.h knot/common/stats.c knot/common/stats.h \
+ knot/common/systemd.c knot/common/systemd.h \
+ knot/common/unreachable.c knot/common/unreachable.h \
+ knot/journal/journal_basic.c knot/journal/journal_basic.h \
+ knot/journal/journal_metadata.c \
+ knot/journal/journal_metadata.h knot/journal/journal_read.c \
+ knot/journal/journal_read.h knot/journal/journal_write.c \
+ knot/journal/journal_write.h knot/journal/knot_lmdb.c \
+ knot/journal/knot_lmdb.h knot/journal/serialization.c \
+ knot/journal/serialization.h knot/server/dthreads.c \
+ knot/server/dthreads.h knot/server/proxyv2.c \
+ knot/server/proxyv2.h knot/server/server.c \
+ knot/server/server.h knot/server/tcp-handler.c \
+ knot/server/tcp-handler.h knot/server/udp-handler.c \
+ knot/server/udp-handler.h knot/server/xdp-handler.c \
+ knot/server/xdp-handler.h knot/updates/acl.c \
+ knot/updates/acl.h knot/updates/apply.c knot/updates/apply.h \
+ knot/updates/changesets.c knot/updates/changesets.h \
+ knot/updates/ddns.c knot/updates/ddns.h \
+ knot/updates/zone-update.c knot/updates/zone-update.h \
+ knot/worker/pool.c knot/worker/pool.h knot/worker/queue.c \
+ knot/worker/queue.h knot/zone/adds_tree.c \
+ knot/zone/adds_tree.h knot/zone/adjust.c knot/zone/adjust.h \
+ knot/zone/backup.c knot/zone/backup.h knot/zone/backup_dir.c \
+ knot/zone/backup_dir.h knot/zone/contents.c \
+ knot/zone/contents.h knot/zone/digest.c knot/zone/digest.h \
+ knot/zone/measure.h knot/zone/measure.c knot/zone/node.c \
+ knot/zone/node.h knot/zone/semantic-check.c \
+ knot/zone/semantic-check.h knot/zone/serial.c \
+ knot/zone/serial.h knot/zone/timers.c knot/zone/timers.h \
+ knot/zone/zone-diff.c knot/zone/zone-diff.h \
+ knot/zone/zone-dump.c knot/zone/zone-dump.h \
+ knot/zone/zone-load.c knot/zone/zone-load.h \
+ knot/zone/zone-tree.c knot/zone/zone-tree.h knot/zone/zone.c \
+ knot/zone/zone.h knot/zone/zonedb-load.c \
+ knot/zone/zonedb-load.h knot/zone/zonedb.c knot/zone/zonedb.h \
+ knot/zone/zonefile.c knot/zone/zonefile.h $(am__append_22) \
+ $(am__append_24) $(am__append_26) $(am__append_30) \
+ $(am__append_34) $(am__append_36) $(am__append_38) \
+ $(am__append_40) $(am__append_42) $(am__append_44) \
+ $(am__append_46) $(am__append_48)
+KNOTD_MOD_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY)
+KNOTD_MOD_LDFLAGS = $(AM_LDFLAGS) -module -shared -avoid-version
+pkglib_LTLIBRARIES = $(am__append_23) $(am__append_25) \
+ $(am__append_29) $(am__append_33) $(am__append_35) \
+ $(am__append_37) $(am__append_39) $(am__append_41) \
+ $(am__append_43) $(am__append_45) $(am__append_47) \
+ $(am__append_49)
+knot_modules_cookies_la_SOURCES = knot/modules/cookies/cookies.c
+@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_dnsproxy_la_SOURCES = knot/modules/dnsproxy/dnsproxy.c
+@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_dnstap_la_SOURCES = knot/modules/dnstap/dnstap.c
+@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(DNSTAP_CFLAGS)
+@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_LIBADD = $(libdnstap_LIBS)
+knot_modules_geoip_la_SOURCES = knot/modules/geoip/geoip.c \
+ knot/modules/geoip/geodb.c \
+ knot/modules/geoip/geodb.h
+
+@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(libmaxminddb_CFLAGS)
+@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_LIBADD = $(libcontrib_LIBS) $(libmaxminddb_LIBS)
+knot_modules_noudp_la_SOURCES = knot/modules/noudp/noudp.c
+@SHARED_MODULE_noudp_TRUE@knot_modules_noudp_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_noudp_TRUE@knot_modules_noudp_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_onlinesign_la_SOURCES = knot/modules/onlinesign/onlinesign.c \
+ knot/modules/onlinesign/nsec_next.c \
+ knot/modules/onlinesign/nsec_next.h
+
+@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_probe_la_SOURCES = knot/modules/probe/probe.c
+@SHARED_MODULE_probe_TRUE@knot_modules_probe_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_probe_TRUE@knot_modules_probe_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_queryacl_la_SOURCES = knot/modules/queryacl/queryacl.c
+@SHARED_MODULE_queryacl_TRUE@knot_modules_queryacl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_queryacl_TRUE@knot_modules_queryacl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \
+ knot/modules/rrl/functions.c \
+ knot/modules/rrl/functions.h
+
+@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_stats_la_SOURCES = knot/modules/stats/stats.c
+@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_synthrecord_la_SOURCES = knot/modules/synthrecord/synthrecord.c
+@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_LIBADD = $(libcontrib_LIBS)
+knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c
+@SHARED_MODULE_whoami_TRUE@knot_modules_whoami_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+@SHARED_MODULE_whoami_TRUE@knot_modules_whoami_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+@HAVE_LIBUTILS_TRUE@libknotus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libedit_CFLAGS) $(libidn2_CFLAGS) $(libidn_CFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libkqueue_CFLAGS) $(libnghttp2_CFLAGS) $(libngtcp2_CFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(lmdb_CFLAGS)
+
+@HAVE_LIBUTILS_TRUE@libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+@HAVE_LIBUTILS_TRUE@libknotus_la_LIBADD = $(libidn2_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(libidn_LIBS) $(libnghttp2_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(libngtcp2_LIBS) $(am__append_51)
+@HAVE_LIBUTILS_TRUE@libknotus_LIBS = libknotus.la libknot.la libdnssec.la $(libcontrib_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(gnutls_LIBS) $(libedit_LIBS)
+
+@HAVE_LIBUTILS_TRUE@libknotus_la_SOURCES = \
+@HAVE_LIBUTILS_TRUE@ utils/common/cert.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/cert.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/exec.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/exec.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/hex.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/hex.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/https.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/https.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/lookup.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/lookup.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/msg.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/msg.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/netio.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/netio.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/params.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/params.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/quic.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/quic.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/resolv.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/resolv.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/sign.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/sign.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/tls.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/tls.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/token.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/token.h \
+@HAVE_LIBUTILS_TRUE@ utils/common/util_conf.c \
+@HAVE_LIBUTILS_TRUE@ utils/common/util_conf.h
+
+@HAVE_UTILS_TRUE@kdig_SOURCES = \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.c \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.h \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_main.c \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.c \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.h
+
+@HAVE_UTILS_TRUE@khost_SOURCES = \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.c \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.h \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.c \
+@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.h \
+@HAVE_UTILS_TRUE@ utils/khost/khost_main.c \
+@HAVE_UTILS_TRUE@ utils/khost/khost_params.c \
+@HAVE_UTILS_TRUE@ utils/khost/khost_params.h
+
+@HAVE_UTILS_TRUE@knsec3hash_SOURCES = \
+@HAVE_UTILS_TRUE@ utils/knsec3hash/knsec3hash.c
+
+@HAVE_UTILS_TRUE@knsupdate_SOURCES = \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_exec.c \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_exec.h \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_interactive.c \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_interactive.h \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_main.c \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_params.c \
+@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_params.h
+
+@HAVE_UTILS_TRUE@kdig_CPPFLAGS = $(libknotus_la_CPPFLAGS) \
+@HAVE_UTILS_TRUE@ $(am__append_53)
+@HAVE_UTILS_TRUE@kdig_LDADD = $(libknotus_LIBS) $(am__append_54)
+@HAVE_UTILS_TRUE@khost_CPPFLAGS = $(libknotus_la_CPPFLAGS) \
+@HAVE_UTILS_TRUE@ $(am__append_55)
+@HAVE_UTILS_TRUE@khost_LDADD = $(libknotus_LIBS) $(am__append_56)
+@HAVE_UTILS_TRUE@knsec3hash_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_UTILS_TRUE@knsec3hash_LDADD = libknot.la libdnssec.la $(libcontrib_LIBS)
+@HAVE_UTILS_TRUE@knsupdate_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_UTILS_TRUE@knsupdate_LDADD = $(libknotus_LIBS) libzscanner.la
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_SOURCES = \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/ip_route.c \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/ip_route.h \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/load_queries.c \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/load_queries.h \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ utils/kxdpgun/main.c
+
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_CPPFLAGS = \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libknotus_la_CPPFLAGS) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libmnl_CFLAGS) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_58)
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@kxdpgun_LDADD = libknot.la \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libcontrib_LIBS) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(libmnl_LIBS) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(pthread_LIBS) \
+@ENABLE_XDP_TRUE@@HAVE_UTILS_TRUE@ $(am__append_59)
+@HAVE_DAEMON_TRUE@knotc_SOURCES = \
+@HAVE_DAEMON_TRUE@ utils/knotc/commands.c \
+@HAVE_DAEMON_TRUE@ utils/knotc/commands.h \
+@HAVE_DAEMON_TRUE@ utils/knotc/interactive.c \
+@HAVE_DAEMON_TRUE@ utils/knotc/interactive.h \
+@HAVE_DAEMON_TRUE@ utils/knotc/process.c \
+@HAVE_DAEMON_TRUE@ utils/knotc/process.h \
+@HAVE_DAEMON_TRUE@ utils/knotc/main.c
+
+@HAVE_DAEMON_TRUE@knotd_SOURCES = \
+@HAVE_DAEMON_TRUE@ utils/knotd/main.c
+
+@HAVE_DAEMON_TRUE@knotc_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@knotc_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+@HAVE_DAEMON_TRUE@knotc_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@knotd_CPPFLAGS = $(libknotus_la_CPPFLAGS) $(liburcu_CFLAGS) $(systemd_CFLAGS)
+@HAVE_DAEMON_TRUE@knotd_LDADD = $(malloc_LIBS) $(libknotd_LIBS) $(cap_ng_LIBS)
+@HAVE_DAEMON_TRUE@knotd_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_SOURCES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/main.c \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/zone_check.c \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/zone_check.h
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_SOURCES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonesign/main.c
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_SOURCES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/bind_privkey.c \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/bind_privkey.h \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/functions.c \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/functions.h \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/offline_ksk.c \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/offline_ksk.h \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/main.c
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_SOURCES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kjournalprint/main.c
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_SOURCES = \
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kcatalogprint/main.c
+
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_LDADD = $(libknotd_LIBS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonesign_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kcatalogprint_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+all: $(BUILT_SOURCES) config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .proto .pb-c.c .pb-c.h .c .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/contrib/Makefile.inc $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner/Makefile.inc $(srcdir)/knot/Makefile.inc $(srcdir)/knot/modules/cookies/Makefile.inc $(srcdir)/knot/modules/dnsproxy/Makefile.inc $(srcdir)/knot/modules/dnstap/Makefile.inc $(srcdir)/knot/modules/geoip/Makefile.inc $(srcdir)/knot/modules/noudp/Makefile.inc $(srcdir)/knot/modules/onlinesign/Makefile.inc $(srcdir)/knot/modules/probe/Makefile.inc $(srcdir)/knot/modules/queryacl/Makefile.inc $(srcdir)/knot/modules/rrl/Makefile.inc $(srcdir)/knot/modules/stats/Makefile.inc $(srcdir)/knot/modules/synthrecord/Makefile.inc $(srcdir)/knot/modules/whoami/Makefile.inc $(srcdir)/utils/Makefile.inc $(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;
+$(srcdir)/contrib/Makefile.inc $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner/Makefile.inc $(srcdir)/knot/Makefile.inc $(srcdir)/knot/modules/cookies/Makefile.inc $(srcdir)/knot/modules/dnsproxy/Makefile.inc $(srcdir)/knot/modules/dnstap/Makefile.inc $(srcdir)/knot/modules/geoip/Makefile.inc $(srcdir)/knot/modules/noudp/Makefile.inc $(srcdir)/knot/modules/onlinesign/Makefile.inc $(srcdir)/knot/modules/probe/Makefile.inc $(srcdir)/knot/modules/queryacl/Makefile.inc $(srcdir)/knot/modules/rrl/Makefile.inc $(srcdir)/knot/modules/stats/Makefile.inc $(srcdir)/knot/modules/synthrecord/Makefile.inc $(srcdir)/knot/modules/whoami/Makefile.inc $(srcdir)/utils/Makefile.inc $(am__empty):
+
+$(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
+knotd.pc: $(top_builddir)/config.status $(srcdir)/knotd.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+libknot.pc: $(top_builddir)/config.status $(srcdir)/libknot.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+libdnssec.pc: $(top_builddir)/config.status $(srcdir)/libdnssec.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+libzscanner.pc: $(top_builddir)/config.status $(srcdir)/libzscanner.pc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+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
+
+installcheck-binPROGRAMS: $(bin_PROGRAMS)
+ bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
+ case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+ *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+ esac; \
+ f=`echo "$$p" | \
+ sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ for opt in --help --version; do \
+ if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \
+ 2>c$${pid}_.err </dev/null \
+ && test -n "`cat c$${pid}_.out`" \
+ && test -z "`cat c$${pid}_.err`"; then :; \
+ else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+ done; \
+ done; rm -f c$${pid}_.???; exit $$bad
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || 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)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || 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)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_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
+
+installcheck-sbinPROGRAMS: $(sbin_PROGRAMS)
+ bad=0; pid=$$$$; list="$(sbin_PROGRAMS)"; for p in $$list; do \
+ case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+ *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+ esac; \
+ f=`echo "$$p" | \
+ sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ for opt in --help --version; do \
+ if "$(DESTDIR)$(sbindir)/$$f" $$opt >c$${pid}_.out \
+ 2>c$${pid}_.err </dev/null \
+ && test -n "`cat c$${pid}_.out`" \
+ && test -z "`cat c$${pid}_.err`"; then :; \
+ else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+ done; \
+ done; rm -f c$${pid}_.???; exit $$bad
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \
+ }
+
+uninstall-pkglibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
+ done
+
+clean-pkglibLTLIBRARIES:
+ -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES)
+ @list='$(pkglib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+knot/modules/cookies/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/cookies
+ @: > knot/modules/cookies/$(am__dirstamp)
+knot/modules/cookies/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/cookies/$(DEPDIR)
+ @: > knot/modules/cookies/$(DEPDIR)/$(am__dirstamp)
+knot/modules/cookies/la-cookies.lo: \
+ knot/modules/cookies/$(am__dirstamp) \
+ knot/modules/cookies/$(DEPDIR)/$(am__dirstamp)
+knot/modules/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules
+ @: > knot/modules/$(am__dirstamp)
+
+knot/modules/cookies.la: $(knot_modules_cookies_la_OBJECTS) $(knot_modules_cookies_la_DEPENDENCIES) $(EXTRA_knot_modules_cookies_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_cookies_la_LINK) $(am_knot_modules_cookies_la_rpath) $(knot_modules_cookies_la_OBJECTS) $(knot_modules_cookies_la_LIBADD) $(LIBS)
+knot/modules/dnsproxy/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/dnsproxy
+ @: > knot/modules/dnsproxy/$(am__dirstamp)
+knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/dnsproxy/$(DEPDIR)
+ @: > knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp)
+knot/modules/dnsproxy/la-dnsproxy.lo: \
+ knot/modules/dnsproxy/$(am__dirstamp) \
+ knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/dnsproxy.la: $(knot_modules_dnsproxy_la_OBJECTS) $(knot_modules_dnsproxy_la_DEPENDENCIES) $(EXTRA_knot_modules_dnsproxy_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_dnsproxy_la_LINK) $(am_knot_modules_dnsproxy_la_rpath) $(knot_modules_dnsproxy_la_OBJECTS) $(knot_modules_dnsproxy_la_LIBADD) $(LIBS)
+knot/modules/dnstap/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/dnstap
+ @: > knot/modules/dnstap/$(am__dirstamp)
+knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/dnstap/$(DEPDIR)
+ @: > knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp)
+knot/modules/dnstap/la-dnstap.lo: knot/modules/dnstap/$(am__dirstamp) \
+ knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/dnstap.la: $(knot_modules_dnstap_la_OBJECTS) $(knot_modules_dnstap_la_DEPENDENCIES) $(EXTRA_knot_modules_dnstap_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_dnstap_la_LINK) $(am_knot_modules_dnstap_la_rpath) $(knot_modules_dnstap_la_OBJECTS) $(knot_modules_dnstap_la_LIBADD) $(LIBS)
+knot/modules/geoip/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/geoip
+ @: > knot/modules/geoip/$(am__dirstamp)
+knot/modules/geoip/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/geoip/$(DEPDIR)
+ @: > knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+knot/modules/geoip/la-geoip.lo: knot/modules/geoip/$(am__dirstamp) \
+ knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+knot/modules/geoip/la-geodb.lo: knot/modules/geoip/$(am__dirstamp) \
+ knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/geoip.la: $(knot_modules_geoip_la_OBJECTS) $(knot_modules_geoip_la_DEPENDENCIES) $(EXTRA_knot_modules_geoip_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_geoip_la_LINK) $(am_knot_modules_geoip_la_rpath) $(knot_modules_geoip_la_OBJECTS) $(knot_modules_geoip_la_LIBADD) $(LIBS)
+knot/modules/noudp/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/noudp
+ @: > knot/modules/noudp/$(am__dirstamp)
+knot/modules/noudp/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/noudp/$(DEPDIR)
+ @: > knot/modules/noudp/$(DEPDIR)/$(am__dirstamp)
+knot/modules/noudp/la-noudp.lo: knot/modules/noudp/$(am__dirstamp) \
+ knot/modules/noudp/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/noudp.la: $(knot_modules_noudp_la_OBJECTS) $(knot_modules_noudp_la_DEPENDENCIES) $(EXTRA_knot_modules_noudp_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_noudp_la_LINK) $(am_knot_modules_noudp_la_rpath) $(knot_modules_noudp_la_OBJECTS) $(knot_modules_noudp_la_LIBADD) $(LIBS)
+knot/modules/onlinesign/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/onlinesign
+ @: > knot/modules/onlinesign/$(am__dirstamp)
+knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/onlinesign/$(DEPDIR)
+ @: > knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+knot/modules/onlinesign/la-onlinesign.lo: \
+ knot/modules/onlinesign/$(am__dirstamp) \
+ knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+knot/modules/onlinesign/la-nsec_next.lo: \
+ knot/modules/onlinesign/$(am__dirstamp) \
+ knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/onlinesign.la: $(knot_modules_onlinesign_la_OBJECTS) $(knot_modules_onlinesign_la_DEPENDENCIES) $(EXTRA_knot_modules_onlinesign_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_onlinesign_la_LINK) $(am_knot_modules_onlinesign_la_rpath) $(knot_modules_onlinesign_la_OBJECTS) $(knot_modules_onlinesign_la_LIBADD) $(LIBS)
+knot/modules/probe/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/probe
+ @: > knot/modules/probe/$(am__dirstamp)
+knot/modules/probe/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/probe/$(DEPDIR)
+ @: > knot/modules/probe/$(DEPDIR)/$(am__dirstamp)
+knot/modules/probe/la-probe.lo: knot/modules/probe/$(am__dirstamp) \
+ knot/modules/probe/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/probe.la: $(knot_modules_probe_la_OBJECTS) $(knot_modules_probe_la_DEPENDENCIES) $(EXTRA_knot_modules_probe_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_probe_la_LINK) $(am_knot_modules_probe_la_rpath) $(knot_modules_probe_la_OBJECTS) $(knot_modules_probe_la_LIBADD) $(LIBS)
+knot/modules/queryacl/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/queryacl
+ @: > knot/modules/queryacl/$(am__dirstamp)
+knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/queryacl/$(DEPDIR)
+ @: > knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/queryacl/la-queryacl.lo: \
+ knot/modules/queryacl/$(am__dirstamp) \
+ knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/queryacl.la: $(knot_modules_queryacl_la_OBJECTS) $(knot_modules_queryacl_la_DEPENDENCIES) $(EXTRA_knot_modules_queryacl_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_queryacl_la_LINK) $(am_knot_modules_queryacl_la_rpath) $(knot_modules_queryacl_la_OBJECTS) $(knot_modules_queryacl_la_LIBADD) $(LIBS)
+knot/modules/rrl/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/rrl
+ @: > knot/modules/rrl/$(am__dirstamp)
+knot/modules/rrl/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/rrl/$(DEPDIR)
+ @: > knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/la-rrl.lo: knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/la-functions.lo: knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/rrl.la: $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_DEPENDENCIES) $(EXTRA_knot_modules_rrl_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_rrl_la_LINK) $(am_knot_modules_rrl_la_rpath) $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_LIBADD) $(LIBS)
+knot/modules/stats/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/stats
+ @: > knot/modules/stats/$(am__dirstamp)
+knot/modules/stats/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/stats/$(DEPDIR)
+ @: > knot/modules/stats/$(DEPDIR)/$(am__dirstamp)
+knot/modules/stats/la-stats.lo: knot/modules/stats/$(am__dirstamp) \
+ knot/modules/stats/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/stats.la: $(knot_modules_stats_la_OBJECTS) $(knot_modules_stats_la_DEPENDENCIES) $(EXTRA_knot_modules_stats_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_stats_la_LINK) $(am_knot_modules_stats_la_rpath) $(knot_modules_stats_la_OBJECTS) $(knot_modules_stats_la_LIBADD) $(LIBS)
+knot/modules/synthrecord/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/synthrecord
+ @: > knot/modules/synthrecord/$(am__dirstamp)
+knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/synthrecord/$(DEPDIR)
+ @: > knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp)
+knot/modules/synthrecord/la-synthrecord.lo: \
+ knot/modules/synthrecord/$(am__dirstamp) \
+ knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/synthrecord.la: $(knot_modules_synthrecord_la_OBJECTS) $(knot_modules_synthrecord_la_DEPENDENCIES) $(EXTRA_knot_modules_synthrecord_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_synthrecord_la_LINK) $(am_knot_modules_synthrecord_la_rpath) $(knot_modules_synthrecord_la_OBJECTS) $(knot_modules_synthrecord_la_LIBADD) $(LIBS)
+knot/modules/whoami/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/whoami
+ @: > knot/modules/whoami/$(am__dirstamp)
+knot/modules/whoami/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/modules/whoami/$(DEPDIR)
+ @: > knot/modules/whoami/$(DEPDIR)/$(am__dirstamp)
+knot/modules/whoami/la-whoami.lo: knot/modules/whoami/$(am__dirstamp) \
+ knot/modules/whoami/$(DEPDIR)/$(am__dirstamp)
+
+knot/modules/whoami.la: $(knot_modules_whoami_la_OBJECTS) $(knot_modules_whoami_la_DEPENDENCIES) $(EXTRA_knot_modules_whoami_la_DEPENDENCIES) knot/modules/$(am__dirstamp)
+ $(AM_V_CCLD)$(knot_modules_whoami_la_LINK) $(am_knot_modules_whoami_la_rpath) $(knot_modules_whoami_la_OBJECTS) $(knot_modules_whoami_la_LIBADD) $(LIBS)
+contrib/$(am__dirstamp):
+ @$(MKDIR_P) contrib
+ @: > contrib/$(am__dirstamp)
+contrib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/$(DEPDIR)
+ @: > contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-base32hex.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-base64.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-base64url.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-conn_pool.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-files.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-getline.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-json.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-mempattern.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/musl/$(am__dirstamp):
+ @$(MKDIR_P) contrib/musl
+ @: > contrib/musl/$(am__dirstamp)
+contrib/musl/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/musl/$(DEPDIR)
+ @: > contrib/musl/$(DEPDIR)/$(am__dirstamp)
+contrib/musl/libcontrib_la-inet_ntop.lo: contrib/musl/$(am__dirstamp) \
+ contrib/musl/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-net.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/qp-trie/$(am__dirstamp):
+ @$(MKDIR_P) contrib/qp-trie
+ @: > contrib/qp-trie/$(am__dirstamp)
+contrib/qp-trie/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/qp-trie/$(DEPDIR)
+ @: > contrib/qp-trie/$(DEPDIR)/$(am__dirstamp)
+contrib/qp-trie/libcontrib_la-trie.lo: \
+ contrib/qp-trie/$(am__dirstamp) \
+ contrib/qp-trie/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-semaphore.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-sockaddr.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-string.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/libcontrib_la-time.lo: contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/openbsd/$(am__dirstamp):
+ @$(MKDIR_P) contrib/openbsd
+ @: > contrib/openbsd/$(am__dirstamp)
+contrib/openbsd/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/openbsd/$(DEPDIR)
+ @: > contrib/openbsd/$(DEPDIR)/$(am__dirstamp)
+contrib/openbsd/libcontrib_la-siphash.lo: \
+ contrib/openbsd/$(am__dirstamp) \
+ contrib/openbsd/$(DEPDIR)/$(am__dirstamp)
+contrib/openbsd/libcontrib_la-strlcat.lo: \
+ contrib/openbsd/$(am__dirstamp) \
+ contrib/openbsd/$(DEPDIR)/$(am__dirstamp)
+contrib/openbsd/libcontrib_la-strlcpy.lo: \
+ contrib/openbsd/$(am__dirstamp) \
+ contrib/openbsd/$(DEPDIR)/$(am__dirstamp)
+contrib/proxyv2/$(am__dirstamp):
+ @$(MKDIR_P) contrib/proxyv2
+ @: > contrib/proxyv2/$(am__dirstamp)
+contrib/proxyv2/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/proxyv2/$(DEPDIR)
+ @: > contrib/proxyv2/$(DEPDIR)/$(am__dirstamp)
+contrib/proxyv2/libcontrib_la-proxyv2.lo: \
+ contrib/proxyv2/$(am__dirstamp) \
+ contrib/proxyv2/$(DEPDIR)/$(am__dirstamp)
+contrib/ucw/$(am__dirstamp):
+ @$(MKDIR_P) contrib/ucw
+ @: > contrib/ucw/$(am__dirstamp)
+contrib/ucw/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/ucw/$(DEPDIR)
+ @: > contrib/ucw/$(DEPDIR)/$(am__dirstamp)
+contrib/ucw/libcontrib_la-heap.lo: contrib/ucw/$(am__dirstamp) \
+ contrib/ucw/$(DEPDIR)/$(am__dirstamp)
+contrib/ucw/libcontrib_la-lists.lo: contrib/ucw/$(am__dirstamp) \
+ contrib/ucw/$(DEPDIR)/$(am__dirstamp)
+contrib/ucw/libcontrib_la-mempool.lo: contrib/ucw/$(am__dirstamp) \
+ contrib/ucw/$(DEPDIR)/$(am__dirstamp)
+contrib/url-parser/$(am__dirstamp):
+ @$(MKDIR_P) contrib/url-parser
+ @: > contrib/url-parser/$(am__dirstamp)
+contrib/url-parser/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/url-parser/$(DEPDIR)
+ @: > contrib/url-parser/$(DEPDIR)/$(am__dirstamp)
+contrib/url-parser/libcontrib_la-url_parser.lo: \
+ contrib/url-parser/$(am__dirstamp) \
+ contrib/url-parser/$(DEPDIR)/$(am__dirstamp)
+contrib/vpool/$(am__dirstamp):
+ @$(MKDIR_P) contrib/vpool
+ @: > contrib/vpool/$(am__dirstamp)
+contrib/vpool/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/vpool/$(DEPDIR)
+ @: > contrib/vpool/$(DEPDIR)/$(am__dirstamp)
+contrib/vpool/libcontrib_la-vpool.lo: contrib/vpool/$(am__dirstamp) \
+ contrib/vpool/$(DEPDIR)/$(am__dirstamp)
+
+libcontrib.la: $(libcontrib_la_OBJECTS) $(libcontrib_la_DEPENDENCIES) $(EXTRA_libcontrib_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcontrib_la_LINK) $(libcontrib_la_OBJECTS) $(libcontrib_la_LIBADD) $(LIBS)
+libdnssec/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec
+ @: > libdnssec/$(am__dirstamp)
+libdnssec/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/$(DEPDIR)
+ @: > libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-binary.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-crypto.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-digest.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-error.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/key
+ @: > libdnssec/key/$(am__dirstamp)
+libdnssec/key/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/key/$(DEPDIR)
+ @: > libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-algorithm.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-convert.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-dnskey.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-ds.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-key.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-keytag.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-privkey.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/key/la-simple.lo: libdnssec/key/$(am__dirstamp) \
+ libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-keyid.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/keystore/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/keystore
+ @: > libdnssec/keystore/$(am__dirstamp)
+libdnssec/keystore/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/keystore/$(DEPDIR)
+ @: > libdnssec/keystore/$(DEPDIR)/$(am__dirstamp)
+libdnssec/keystore/la-keystore.lo: libdnssec/keystore/$(am__dirstamp) \
+ libdnssec/keystore/$(DEPDIR)/$(am__dirstamp)
+libdnssec/keystore/la-pkcs11.lo: libdnssec/keystore/$(am__dirstamp) \
+ libdnssec/keystore/$(DEPDIR)/$(am__dirstamp)
+libdnssec/keystore/la-pkcs8.lo: libdnssec/keystore/$(am__dirstamp) \
+ libdnssec/keystore/$(DEPDIR)/$(am__dirstamp)
+libdnssec/nsec/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/nsec
+ @: > libdnssec/nsec/$(am__dirstamp)
+libdnssec/nsec/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/nsec/$(DEPDIR)
+ @: > libdnssec/nsec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/nsec/la-bitmap.lo: libdnssec/nsec/$(am__dirstamp) \
+ libdnssec/nsec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/nsec/la-hash.lo: libdnssec/nsec/$(am__dirstamp) \
+ libdnssec/nsec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/nsec/la-nsec.lo: libdnssec/nsec/$(am__dirstamp) \
+ libdnssec/nsec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/p11/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/p11
+ @: > libdnssec/p11/$(am__dirstamp)
+libdnssec/p11/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/p11/$(DEPDIR)
+ @: > libdnssec/p11/$(DEPDIR)/$(am__dirstamp)
+libdnssec/p11/la-p11.lo: libdnssec/p11/$(am__dirstamp) \
+ libdnssec/p11/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-pem.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-random.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/shared/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/shared
+ @: > libdnssec/shared/$(am__dirstamp)
+libdnssec/shared/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/shared/$(DEPDIR)
+ @: > libdnssec/shared/$(DEPDIR)/$(am__dirstamp)
+libdnssec/shared/la-bignum.lo: libdnssec/shared/$(am__dirstamp) \
+ libdnssec/shared/$(DEPDIR)/$(am__dirstamp)
+libdnssec/shared/la-dname.lo: libdnssec/shared/$(am__dirstamp) \
+ libdnssec/shared/$(DEPDIR)/$(am__dirstamp)
+libdnssec/shared/la-keyid_gnutls.lo: libdnssec/shared/$(am__dirstamp) \
+ libdnssec/shared/$(DEPDIR)/$(am__dirstamp)
+libdnssec/sign/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/sign
+ @: > libdnssec/sign/$(am__dirstamp)
+libdnssec/sign/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/sign/$(DEPDIR)
+ @: > libdnssec/sign/$(DEPDIR)/$(am__dirstamp)
+libdnssec/sign/la-der.lo: libdnssec/sign/$(am__dirstamp) \
+ libdnssec/sign/$(DEPDIR)/$(am__dirstamp)
+libdnssec/sign/la-sign.lo: libdnssec/sign/$(am__dirstamp) \
+ libdnssec/sign/$(DEPDIR)/$(am__dirstamp)
+libdnssec/la-tsig.lo: libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec.la: $(libdnssec_la_OBJECTS) $(libdnssec_la_DEPENDENCIES) $(EXTRA_libdnssec_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdnssec_la_LINK) -rpath $(libdir) $(libdnssec_la_OBJECTS) $(libdnssec_la_LIBADD) $(LIBS)
+contrib/dnstap/$(am__dirstamp):
+ @$(MKDIR_P) contrib/dnstap
+ @: > contrib/dnstap/$(am__dirstamp)
+contrib/dnstap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/dnstap/$(DEPDIR)
+ @: > contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-convert.lo: \
+ contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-dnstap.lo: contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-message.lo: \
+ contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-reader.lo: contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-writer.lo: contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+contrib/dnstap/libdnstap_la-dnstap.pb-c.lo: \
+ contrib/dnstap/$(am__dirstamp) \
+ contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+
+libdnstap.la: $(libdnstap_la_OBJECTS) $(libdnstap_la_DEPENDENCIES) $(EXTRA_libdnstap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdnstap_la_LINK) $(am_libdnstap_la_rpath) $(libdnstap_la_OBJECTS) $(libdnstap_la_LIBADD) $(LIBS)
+contrib/libbpf/bpf/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libbpf/bpf
+ @: > contrib/libbpf/bpf/$(am__dirstamp)
+contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libbpf/bpf/$(DEPDIR)
+ @: > contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-bpf.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-btf.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-btf_dump.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-hashmap.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-libbpf.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-netlink.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-nlattr.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-str_error.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+contrib/libbpf/bpf/libembbpf_la-xsk.lo: \
+ contrib/libbpf/bpf/$(am__dirstamp) \
+ contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+
+libembbpf.la: $(libembbpf_la_OBJECTS) $(libembbpf_la_DEPENDENCIES) $(EXTRA_libembbpf_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libembbpf_la_LINK) $(am_libembbpf_la_rpath) $(libembbpf_la_OBJECTS) $(libembbpf_la_LIBADD) $(LIBS)
+contrib/libngtcp2/ngtcp2/crypto/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libngtcp2/ngtcp2/crypto
+ @: > contrib/libngtcp2/ngtcp2/crypto/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)
+ @: > contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo: \
+ contrib/libngtcp2/ngtcp2/crypto/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo: \
+ contrib/libngtcp2/ngtcp2/crypto/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libngtcp2/ngtcp2/lib
+ @: > contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)
+ @: > contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo: \
+ contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp) \
+ contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+
+libembngtcp2.la: $(libembngtcp2_la_OBJECTS) $(libembngtcp2_la_DEPENDENCIES) $(EXTRA_libembngtcp2_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libembngtcp2_la_LINK) $(am_libembngtcp2_la_rpath) $(libembngtcp2_la_OBJECTS) $(libembngtcp2_la_LIBADD) $(LIBS)
+libknot/$(am__dirstamp):
+ @$(MKDIR_P) libknot
+ @: > libknot/$(am__dirstamp)
+libknot/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/$(DEPDIR)
+ @: > libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-codes.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/control/$(am__dirstamp):
+ @$(MKDIR_P) libknot/control
+ @: > libknot/control/$(am__dirstamp)
+libknot/control/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/control/$(DEPDIR)
+ @: > libknot/control/$(DEPDIR)/$(am__dirstamp)
+libknot/control/la-control.lo: libknot/control/$(am__dirstamp) \
+ libknot/control/$(DEPDIR)/$(am__dirstamp)
+libknot/la-cookies.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-descriptor.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-dname.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-error.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/db/$(am__dirstamp):
+ @$(MKDIR_P) libknot/db
+ @: > libknot/db/$(am__dirstamp)
+libknot/db/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/db/$(DEPDIR)
+ @: > libknot/db/$(DEPDIR)/$(am__dirstamp)
+libknot/db/la-db_lmdb.lo: libknot/db/$(am__dirstamp) \
+ libknot/db/$(DEPDIR)/$(am__dirstamp)
+libknot/db/la-db_trie.lo: libknot/db/$(am__dirstamp) \
+ libknot/db/$(DEPDIR)/$(am__dirstamp)
+libknot/packet/$(am__dirstamp):
+ @$(MKDIR_P) libknot/packet
+ @: > libknot/packet/$(am__dirstamp)
+libknot/packet/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/packet/$(DEPDIR)
+ @: > libknot/packet/$(DEPDIR)/$(am__dirstamp)
+libknot/packet/la-pkt.lo: libknot/packet/$(am__dirstamp) \
+ libknot/packet/$(DEPDIR)/$(am__dirstamp)
+libknot/packet/la-rrset-wire.lo: libknot/packet/$(am__dirstamp) \
+ libknot/packet/$(DEPDIR)/$(am__dirstamp)
+libknot/probe/$(am__dirstamp):
+ @$(MKDIR_P) libknot/probe
+ @: > libknot/probe/$(am__dirstamp)
+libknot/probe/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/probe/$(DEPDIR)
+ @: > libknot/probe/$(DEPDIR)/$(am__dirstamp)
+libknot/probe/la-data.lo: libknot/probe/$(am__dirstamp) \
+ libknot/probe/$(DEPDIR)/$(am__dirstamp)
+libknot/probe/la-probe.lo: libknot/probe/$(am__dirstamp) \
+ libknot/probe/$(DEPDIR)/$(am__dirstamp)
+libknot/la-rdataset.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-rrset-dump.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-rrset.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/rrtype/$(am__dirstamp):
+ @$(MKDIR_P) libknot/rrtype
+ @: > libknot/rrtype/$(am__dirstamp)
+libknot/rrtype/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/rrtype/$(DEPDIR)
+ @: > libknot/rrtype/$(DEPDIR)/$(am__dirstamp)
+libknot/rrtype/la-naptr.lo: libknot/rrtype/$(am__dirstamp) \
+ libknot/rrtype/$(DEPDIR)/$(am__dirstamp)
+libknot/rrtype/la-opt.lo: libknot/rrtype/$(am__dirstamp) \
+ libknot/rrtype/$(DEPDIR)/$(am__dirstamp)
+libknot/rrtype/la-tsig.lo: libknot/rrtype/$(am__dirstamp) \
+ libknot/rrtype/$(DEPDIR)/$(am__dirstamp)
+libknot/la-tsig-op.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/la-tsig.lo: libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/$(am__dirstamp):
+ @$(MKDIR_P) libknot/yparser
+ @: > libknot/yparser/$(am__dirstamp)
+libknot/yparser/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/yparser/$(DEPDIR)
+ @: > libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/la-yparser.lo: libknot/yparser/$(am__dirstamp) \
+ libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/la-ypbody.lo: libknot/yparser/$(am__dirstamp) \
+ libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/la-ypformat.lo: libknot/yparser/$(am__dirstamp) \
+ libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/la-ypschema.lo: libknot/yparser/$(am__dirstamp) \
+ libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/yparser/la-yptrafo.lo: libknot/yparser/$(am__dirstamp) \
+ libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/$(am__dirstamp):
+ @$(MKDIR_P) libknot/xdp
+ @: > libknot/xdp/$(am__dirstamp)
+libknot/xdp/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/xdp/$(DEPDIR)
+ @: > libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-tcp_iobuf.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-bpf-kernel-obj.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-bpf-user.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-eth.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-tcp.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-xdp.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-quic.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+libknot/xdp/la-quic_conn.lo: libknot/xdp/$(am__dirstamp) \
+ libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+
+libknot.la: $(libknot_la_OBJECTS) $(libknot_la_DEPENDENCIES) $(EXTRA_libknot_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libknot_la_LINK) -rpath $(libdir) $(libknot_la_OBJECTS) $(libknot_la_LIBADD) $(LIBS)
+knot/catalog/$(am__dirstamp):
+ @$(MKDIR_P) knot/catalog
+ @: > knot/catalog/$(am__dirstamp)
+knot/catalog/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/catalog/$(DEPDIR)
+ @: > knot/catalog/$(DEPDIR)/$(am__dirstamp)
+knot/catalog/libknotd_la-catalog_db.lo: knot/catalog/$(am__dirstamp) \
+ knot/catalog/$(DEPDIR)/$(am__dirstamp)
+knot/catalog/libknotd_la-catalog_update.lo: \
+ knot/catalog/$(am__dirstamp) \
+ knot/catalog/$(DEPDIR)/$(am__dirstamp)
+knot/catalog/libknotd_la-generate.lo: knot/catalog/$(am__dirstamp) \
+ knot/catalog/$(DEPDIR)/$(am__dirstamp)
+knot/catalog/libknotd_la-interpret.lo: knot/catalog/$(am__dirstamp) \
+ knot/catalog/$(DEPDIR)/$(am__dirstamp)
+knot/conf/$(am__dirstamp):
+ @$(MKDIR_P) knot/conf
+ @: > knot/conf/$(am__dirstamp)
+knot/conf/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/conf/$(DEPDIR)
+ @: > knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-base.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-conf.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-confdb.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-confio.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-migration.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-module.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-schema.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/conf/libknotd_la-tools.lo: knot/conf/$(am__dirstamp) \
+ knot/conf/$(DEPDIR)/$(am__dirstamp)
+knot/ctl/$(am__dirstamp):
+ @$(MKDIR_P) knot/ctl
+ @: > knot/ctl/$(am__dirstamp)
+knot/ctl/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/ctl/$(DEPDIR)
+ @: > knot/ctl/$(DEPDIR)/$(am__dirstamp)
+knot/ctl/libknotd_la-commands.lo: knot/ctl/$(am__dirstamp) \
+ knot/ctl/$(DEPDIR)/$(am__dirstamp)
+knot/ctl/libknotd_la-process.lo: knot/ctl/$(am__dirstamp) \
+ knot/ctl/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/$(am__dirstamp):
+ @$(MKDIR_P) knot/dnssec
+ @: > knot/dnssec/$(am__dirstamp)
+knot/dnssec/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/dnssec/$(DEPDIR)
+ @: > knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-context.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-ds_query.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/kasp/$(am__dirstamp):
+ @$(MKDIR_P) knot/dnssec/kasp
+ @: > knot/dnssec/kasp/$(am__dirstamp)
+knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/dnssec/kasp/$(DEPDIR)
+ @: > knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/kasp/libknotd_la-kasp_db.lo: \
+ knot/dnssec/kasp/$(am__dirstamp) \
+ knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/kasp/libknotd_la-kasp_zone.lo: \
+ knot/dnssec/kasp/$(am__dirstamp) \
+ knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/kasp/libknotd_la-keystate.lo: \
+ knot/dnssec/kasp/$(am__dirstamp) \
+ knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/kasp/libknotd_la-keystore.lo: \
+ knot/dnssec/kasp/$(am__dirstamp) \
+ knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-key-events.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-key_records.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-nsec-chain.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-nsec3-chain.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-policy.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-rrset-sign.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-zone-events.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-zone-keys.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-zone-nsec.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/dnssec/libknotd_la-zone-sign.lo: knot/dnssec/$(am__dirstamp) \
+ knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+knot/events/$(am__dirstamp):
+ @$(MKDIR_P) knot/events
+ @: > knot/events/$(am__dirstamp)
+knot/events/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/events/$(DEPDIR)
+ @: > knot/events/$(DEPDIR)/$(am__dirstamp)
+knot/events/libknotd_la-events.lo: knot/events/$(am__dirstamp) \
+ knot/events/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/$(am__dirstamp):
+ @$(MKDIR_P) knot/events/handlers
+ @: > knot/events/handlers/$(am__dirstamp)
+knot/events/handlers/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/events/handlers/$(DEPDIR)
+ @: > knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-backup.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-dnssec.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-ds_check.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-ds_push.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-expire.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-flush.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-freeze_thaw.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-load.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-notify.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-refresh.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/handlers/libknotd_la-update.lo: \
+ knot/events/handlers/$(am__dirstamp) \
+ knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+knot/events/libknotd_la-replan.lo: knot/events/$(am__dirstamp) \
+ knot/events/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/$(am__dirstamp):
+ @$(MKDIR_P) knot/nameserver
+ @: > knot/nameserver/$(am__dirstamp)
+knot/nameserver/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/nameserver/$(DEPDIR)
+ @: > knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-axfr.lo: knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-chaos.lo: knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-internet.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-ixfr.lo: knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-notify.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-nsec_proofs.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-process_query.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-query_module.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-tsig_ctx.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-update.lo: \
+ knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/nameserver/libknotd_la-xfr.lo: knot/nameserver/$(am__dirstamp) \
+ knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+knot/query/$(am__dirstamp):
+ @$(MKDIR_P) knot/query
+ @: > knot/query/$(am__dirstamp)
+knot/query/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/query/$(DEPDIR)
+ @: > knot/query/$(DEPDIR)/$(am__dirstamp)
+knot/query/libknotd_la-capture.lo: knot/query/$(am__dirstamp) \
+ knot/query/$(DEPDIR)/$(am__dirstamp)
+knot/query/libknotd_la-query.lo: knot/query/$(am__dirstamp) \
+ knot/query/$(DEPDIR)/$(am__dirstamp)
+knot/query/libknotd_la-requestor.lo: knot/query/$(am__dirstamp) \
+ knot/query/$(DEPDIR)/$(am__dirstamp)
+knot/common/$(am__dirstamp):
+ @$(MKDIR_P) knot/common
+ @: > knot/common/$(am__dirstamp)
+knot/common/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/common/$(DEPDIR)
+ @: > knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-evsched.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-fdset.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-log.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-process.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-stats.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-systemd.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/common/libknotd_la-unreachable.lo: knot/common/$(am__dirstamp) \
+ knot/common/$(DEPDIR)/$(am__dirstamp)
+knot/journal/$(am__dirstamp):
+ @$(MKDIR_P) knot/journal
+ @: > knot/journal/$(am__dirstamp)
+knot/journal/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/journal/$(DEPDIR)
+ @: > knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-journal_basic.lo: \
+ knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-journal_metadata.lo: \
+ knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-journal_read.lo: \
+ knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-journal_write.lo: \
+ knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-knot_lmdb.lo: knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/journal/libknotd_la-serialization.lo: \
+ knot/journal/$(am__dirstamp) \
+ knot/journal/$(DEPDIR)/$(am__dirstamp)
+knot/server/$(am__dirstamp):
+ @$(MKDIR_P) knot/server
+ @: > knot/server/$(am__dirstamp)
+knot/server/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/server/$(DEPDIR)
+ @: > knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-dthreads.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-proxyv2.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-server.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-tcp-handler.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-udp-handler.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/server/libknotd_la-xdp-handler.lo: knot/server/$(am__dirstamp) \
+ knot/server/$(DEPDIR)/$(am__dirstamp)
+knot/updates/$(am__dirstamp):
+ @$(MKDIR_P) knot/updates
+ @: > knot/updates/$(am__dirstamp)
+knot/updates/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/updates/$(DEPDIR)
+ @: > knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/updates/libknotd_la-acl.lo: knot/updates/$(am__dirstamp) \
+ knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/updates/libknotd_la-apply.lo: knot/updates/$(am__dirstamp) \
+ knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/updates/libknotd_la-changesets.lo: knot/updates/$(am__dirstamp) \
+ knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/updates/libknotd_la-ddns.lo: knot/updates/$(am__dirstamp) \
+ knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/updates/libknotd_la-zone-update.lo: knot/updates/$(am__dirstamp) \
+ knot/updates/$(DEPDIR)/$(am__dirstamp)
+knot/worker/$(am__dirstamp):
+ @$(MKDIR_P) knot/worker
+ @: > knot/worker/$(am__dirstamp)
+knot/worker/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/worker/$(DEPDIR)
+ @: > knot/worker/$(DEPDIR)/$(am__dirstamp)
+knot/worker/libknotd_la-pool.lo: knot/worker/$(am__dirstamp) \
+ knot/worker/$(DEPDIR)/$(am__dirstamp)
+knot/worker/libknotd_la-queue.lo: knot/worker/$(am__dirstamp) \
+ knot/worker/$(DEPDIR)/$(am__dirstamp)
+knot/zone/$(am__dirstamp):
+ @$(MKDIR_P) knot/zone
+ @: > knot/zone/$(am__dirstamp)
+knot/zone/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/zone/$(DEPDIR)
+ @: > knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-adds_tree.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-adjust.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-backup.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-backup_dir.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-contents.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-digest.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-measure.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-node.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-semantic-check.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-serial.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-timers.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zone-diff.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zone-dump.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zone-load.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zone-tree.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zone.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zonedb-load.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zonedb.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/zone/libknotd_la-zonefile.lo: knot/zone/$(am__dirstamp) \
+ knot/zone/$(DEPDIR)/$(am__dirstamp)
+knot/modules/cookies/libknotd_la-cookies.lo: \
+ knot/modules/cookies/$(am__dirstamp) \
+ knot/modules/cookies/$(DEPDIR)/$(am__dirstamp)
+knot/modules/dnsproxy/libknotd_la-dnsproxy.lo: \
+ knot/modules/dnsproxy/$(am__dirstamp) \
+ knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp)
+knot/modules/dnstap/libknotd_la-dnstap.lo: \
+ knot/modules/dnstap/$(am__dirstamp) \
+ knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp)
+knot/modules/geoip/libknotd_la-geoip.lo: \
+ knot/modules/geoip/$(am__dirstamp) \
+ knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+knot/modules/geoip/libknotd_la-geodb.lo: \
+ knot/modules/geoip/$(am__dirstamp) \
+ knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+knot/modules/noudp/libknotd_la-noudp.lo: \
+ knot/modules/noudp/$(am__dirstamp) \
+ knot/modules/noudp/$(DEPDIR)/$(am__dirstamp)
+knot/modules/onlinesign/libknotd_la-onlinesign.lo: \
+ knot/modules/onlinesign/$(am__dirstamp) \
+ knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+knot/modules/onlinesign/libknotd_la-nsec_next.lo: \
+ knot/modules/onlinesign/$(am__dirstamp) \
+ knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+knot/modules/probe/libknotd_la-probe.lo: \
+ knot/modules/probe/$(am__dirstamp) \
+ knot/modules/probe/$(DEPDIR)/$(am__dirstamp)
+knot/modules/queryacl/libknotd_la-queryacl.lo: \
+ knot/modules/queryacl/$(am__dirstamp) \
+ knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/libknotd_la-rrl.lo: knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/rrl/libknotd_la-functions.lo: \
+ knot/modules/rrl/$(am__dirstamp) \
+ knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+knot/modules/stats/libknotd_la-stats.lo: \
+ knot/modules/stats/$(am__dirstamp) \
+ knot/modules/stats/$(DEPDIR)/$(am__dirstamp)
+knot/modules/synthrecord/libknotd_la-synthrecord.lo: \
+ knot/modules/synthrecord/$(am__dirstamp) \
+ knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp)
+knot/modules/whoami/libknotd_la-whoami.lo: \
+ knot/modules/whoami/$(am__dirstamp) \
+ knot/modules/whoami/$(DEPDIR)/$(am__dirstamp)
+
+libknotd.la: $(libknotd_la_OBJECTS) $(libknotd_la_DEPENDENCIES) $(EXTRA_libknotd_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libknotd_la_LINK) $(am_libknotd_la_rpath) $(libknotd_la_OBJECTS) $(libknotd_la_LIBADD) $(LIBS)
+utils/common/$(am__dirstamp):
+ @$(MKDIR_P) utils/common
+ @: > utils/common/$(am__dirstamp)
+utils/common/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/common/$(DEPDIR)
+ @: > utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-cert.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-exec.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-hex.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-https.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-lookup.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-msg.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-netio.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-params.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-quic.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-resolv.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-sign.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-tls.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-token.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+utils/common/libknotus_la-util_conf.lo: utils/common/$(am__dirstamp) \
+ utils/common/$(DEPDIR)/$(am__dirstamp)
+
+libknotus.la: $(libknotus_la_OBJECTS) $(libknotus_la_DEPENDENCIES) $(EXTRA_libknotus_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libknotus_la_LINK) $(am_libknotus_la_rpath) $(libknotus_la_OBJECTS) $(libknotus_la_LIBADD) $(LIBS)
+libzscanner/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner
+ @: > libzscanner/$(am__dirstamp)
+libzscanner/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner/$(DEPDIR)
+ @: > libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/la-error.lo: libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/la-functions.lo: libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/la-scanner.lo: libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+
+libzscanner.la: $(libzscanner_la_OBJECTS) $(libzscanner_la_DEPENDENCIES) $(EXTRA_libzscanner_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libzscanner_la_LINK) -rpath $(libdir) $(libzscanner_la_OBJECTS) $(libzscanner_la_LIBADD) $(LIBS)
+utils/kcatalogprint/$(am__dirstamp):
+ @$(MKDIR_P) utils/kcatalogprint
+ @: > utils/kcatalogprint/$(am__dirstamp)
+utils/kcatalogprint/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kcatalogprint/$(DEPDIR)
+ @: > utils/kcatalogprint/$(DEPDIR)/$(am__dirstamp)
+utils/kcatalogprint/kcatalogprint-main.$(OBJEXT): \
+ utils/kcatalogprint/$(am__dirstamp) \
+ utils/kcatalogprint/$(DEPDIR)/$(am__dirstamp)
+
+kcatalogprint$(EXEEXT): $(kcatalogprint_OBJECTS) $(kcatalogprint_DEPENDENCIES) $(EXTRA_kcatalogprint_DEPENDENCIES)
+ @rm -f kcatalogprint$(EXEEXT)
+ $(AM_V_CCLD)$(kcatalogprint_LINK) $(kcatalogprint_OBJECTS) $(kcatalogprint_LDADD) $(LIBS)
+utils/kdig/$(am__dirstamp):
+ @$(MKDIR_P) utils/kdig
+ @: > utils/kdig/$(am__dirstamp)
+utils/kdig/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kdig/$(DEPDIR)
+ @: > utils/kdig/$(DEPDIR)/$(am__dirstamp)
+utils/kdig/kdig-kdig_exec.$(OBJEXT): utils/kdig/$(am__dirstamp) \
+ utils/kdig/$(DEPDIR)/$(am__dirstamp)
+utils/kdig/kdig-kdig_main.$(OBJEXT): utils/kdig/$(am__dirstamp) \
+ utils/kdig/$(DEPDIR)/$(am__dirstamp)
+utils/kdig/kdig-kdig_params.$(OBJEXT): utils/kdig/$(am__dirstamp) \
+ utils/kdig/$(DEPDIR)/$(am__dirstamp)
+
+kdig$(EXEEXT): $(kdig_OBJECTS) $(kdig_DEPENDENCIES) $(EXTRA_kdig_DEPENDENCIES)
+ @rm -f kdig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(kdig_OBJECTS) $(kdig_LDADD) $(LIBS)
+utils/keymgr/$(am__dirstamp):
+ @$(MKDIR_P) utils/keymgr
+ @: > utils/keymgr/$(am__dirstamp)
+utils/keymgr/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/keymgr/$(DEPDIR)
+ @: > utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+utils/keymgr/keymgr-bind_privkey.$(OBJEXT): \
+ utils/keymgr/$(am__dirstamp) \
+ utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+utils/keymgr/keymgr-functions.$(OBJEXT): utils/keymgr/$(am__dirstamp) \
+ utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+utils/keymgr/keymgr-offline_ksk.$(OBJEXT): \
+ utils/keymgr/$(am__dirstamp) \
+ utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+utils/keymgr/keymgr-main.$(OBJEXT): utils/keymgr/$(am__dirstamp) \
+ utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+
+keymgr$(EXEEXT): $(keymgr_OBJECTS) $(keymgr_DEPENDENCIES) $(EXTRA_keymgr_DEPENDENCIES)
+ @rm -f keymgr$(EXEEXT)
+ $(AM_V_CCLD)$(keymgr_LINK) $(keymgr_OBJECTS) $(keymgr_LDADD) $(LIBS)
+utils/kdig/khost-kdig_exec.$(OBJEXT): utils/kdig/$(am__dirstamp) \
+ utils/kdig/$(DEPDIR)/$(am__dirstamp)
+utils/kdig/khost-kdig_params.$(OBJEXT): utils/kdig/$(am__dirstamp) \
+ utils/kdig/$(DEPDIR)/$(am__dirstamp)
+utils/khost/$(am__dirstamp):
+ @$(MKDIR_P) utils/khost
+ @: > utils/khost/$(am__dirstamp)
+utils/khost/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/khost/$(DEPDIR)
+ @: > utils/khost/$(DEPDIR)/$(am__dirstamp)
+utils/khost/khost-khost_main.$(OBJEXT): utils/khost/$(am__dirstamp) \
+ utils/khost/$(DEPDIR)/$(am__dirstamp)
+utils/khost/khost-khost_params.$(OBJEXT): utils/khost/$(am__dirstamp) \
+ utils/khost/$(DEPDIR)/$(am__dirstamp)
+
+khost$(EXEEXT): $(khost_OBJECTS) $(khost_DEPENDENCIES) $(EXTRA_khost_DEPENDENCIES)
+ @rm -f khost$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(khost_OBJECTS) $(khost_LDADD) $(LIBS)
+utils/kjournalprint/$(am__dirstamp):
+ @$(MKDIR_P) utils/kjournalprint
+ @: > utils/kjournalprint/$(am__dirstamp)
+utils/kjournalprint/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kjournalprint/$(DEPDIR)
+ @: > utils/kjournalprint/$(DEPDIR)/$(am__dirstamp)
+utils/kjournalprint/kjournalprint-main.$(OBJEXT): \
+ utils/kjournalprint/$(am__dirstamp) \
+ utils/kjournalprint/$(DEPDIR)/$(am__dirstamp)
+
+kjournalprint$(EXEEXT): $(kjournalprint_OBJECTS) $(kjournalprint_DEPENDENCIES) $(EXTRA_kjournalprint_DEPENDENCIES)
+ @rm -f kjournalprint$(EXEEXT)
+ $(AM_V_CCLD)$(kjournalprint_LINK) $(kjournalprint_OBJECTS) $(kjournalprint_LDADD) $(LIBS)
+utils/knotc/$(am__dirstamp):
+ @$(MKDIR_P) utils/knotc
+ @: > utils/knotc/$(am__dirstamp)
+utils/knotc/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/knotc/$(DEPDIR)
+ @: > utils/knotc/$(DEPDIR)/$(am__dirstamp)
+utils/knotc/knotc-commands.$(OBJEXT): utils/knotc/$(am__dirstamp) \
+ utils/knotc/$(DEPDIR)/$(am__dirstamp)
+utils/knotc/knotc-interactive.$(OBJEXT): utils/knotc/$(am__dirstamp) \
+ utils/knotc/$(DEPDIR)/$(am__dirstamp)
+utils/knotc/knotc-process.$(OBJEXT): utils/knotc/$(am__dirstamp) \
+ utils/knotc/$(DEPDIR)/$(am__dirstamp)
+utils/knotc/knotc-main.$(OBJEXT): utils/knotc/$(am__dirstamp) \
+ utils/knotc/$(DEPDIR)/$(am__dirstamp)
+
+knotc$(EXEEXT): $(knotc_OBJECTS) $(knotc_DEPENDENCIES) $(EXTRA_knotc_DEPENDENCIES)
+ @rm -f knotc$(EXEEXT)
+ $(AM_V_CCLD)$(knotc_LINK) $(knotc_OBJECTS) $(knotc_LDADD) $(LIBS)
+utils/knotd/$(am__dirstamp):
+ @$(MKDIR_P) utils/knotd
+ @: > utils/knotd/$(am__dirstamp)
+utils/knotd/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/knotd/$(DEPDIR)
+ @: > utils/knotd/$(DEPDIR)/$(am__dirstamp)
+utils/knotd/knotd-main.$(OBJEXT): utils/knotd/$(am__dirstamp) \
+ utils/knotd/$(DEPDIR)/$(am__dirstamp)
+
+knotd$(EXEEXT): $(knotd_OBJECTS) $(knotd_DEPENDENCIES) $(EXTRA_knotd_DEPENDENCIES)
+ @rm -f knotd$(EXEEXT)
+ $(AM_V_CCLD)$(knotd_LINK) $(knotd_OBJECTS) $(knotd_LDADD) $(LIBS)
+utils/knsec3hash/$(am__dirstamp):
+ @$(MKDIR_P) utils/knsec3hash
+ @: > utils/knsec3hash/$(am__dirstamp)
+utils/knsec3hash/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/knsec3hash/$(DEPDIR)
+ @: > utils/knsec3hash/$(DEPDIR)/$(am__dirstamp)
+utils/knsec3hash/knsec3hash-knsec3hash.$(OBJEXT): \
+ utils/knsec3hash/$(am__dirstamp) \
+ utils/knsec3hash/$(DEPDIR)/$(am__dirstamp)
+
+knsec3hash$(EXEEXT): $(knsec3hash_OBJECTS) $(knsec3hash_DEPENDENCIES) $(EXTRA_knsec3hash_DEPENDENCIES)
+ @rm -f knsec3hash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knsec3hash_OBJECTS) $(knsec3hash_LDADD) $(LIBS)
+utils/knsupdate/$(am__dirstamp):
+ @$(MKDIR_P) utils/knsupdate
+ @: > utils/knsupdate/$(am__dirstamp)
+utils/knsupdate/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/knsupdate/$(DEPDIR)
+ @: > utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+utils/knsupdate/knsupdate-knsupdate_exec.$(OBJEXT): \
+ utils/knsupdate/$(am__dirstamp) \
+ utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+utils/knsupdate/knsupdate-knsupdate_interactive.$(OBJEXT): \
+ utils/knsupdate/$(am__dirstamp) \
+ utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+utils/knsupdate/knsupdate-knsupdate_main.$(OBJEXT): \
+ utils/knsupdate/$(am__dirstamp) \
+ utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+utils/knsupdate/knsupdate-knsupdate_params.$(OBJEXT): \
+ utils/knsupdate/$(am__dirstamp) \
+ utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+
+knsupdate$(EXEEXT): $(knsupdate_OBJECTS) $(knsupdate_DEPENDENCIES) $(EXTRA_knsupdate_DEPENDENCIES)
+ @rm -f knsupdate$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knsupdate_OBJECTS) $(knsupdate_LDADD) $(LIBS)
+utils/kxdpgun/$(am__dirstamp):
+ @$(MKDIR_P) utils/kxdpgun
+ @: > utils/kxdpgun/$(am__dirstamp)
+utils/kxdpgun/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kxdpgun/$(DEPDIR)
+ @: > utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+utils/kxdpgun/kxdpgun-ip_route.$(OBJEXT): \
+ utils/kxdpgun/$(am__dirstamp) \
+ utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+utils/kxdpgun/kxdpgun-load_queries.$(OBJEXT): \
+ utils/kxdpgun/$(am__dirstamp) \
+ utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+utils/kxdpgun/kxdpgun-main.$(OBJEXT): utils/kxdpgun/$(am__dirstamp) \
+ utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+
+kxdpgun$(EXEEXT): $(kxdpgun_OBJECTS) $(kxdpgun_DEPENDENCIES) $(EXTRA_kxdpgun_DEPENDENCIES)
+ @rm -f kxdpgun$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(kxdpgun_OBJECTS) $(kxdpgun_LDADD) $(LIBS)
+utils/kzonecheck/$(am__dirstamp):
+ @$(MKDIR_P) utils/kzonecheck
+ @: > utils/kzonecheck/$(am__dirstamp)
+utils/kzonecheck/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kzonecheck/$(DEPDIR)
+ @: > utils/kzonecheck/$(DEPDIR)/$(am__dirstamp)
+utils/kzonecheck/kzonecheck-main.$(OBJEXT): \
+ utils/kzonecheck/$(am__dirstamp) \
+ utils/kzonecheck/$(DEPDIR)/$(am__dirstamp)
+utils/kzonecheck/kzonecheck-zone_check.$(OBJEXT): \
+ utils/kzonecheck/$(am__dirstamp) \
+ utils/kzonecheck/$(DEPDIR)/$(am__dirstamp)
+
+kzonecheck$(EXEEXT): $(kzonecheck_OBJECTS) $(kzonecheck_DEPENDENCIES) $(EXTRA_kzonecheck_DEPENDENCIES)
+ @rm -f kzonecheck$(EXEEXT)
+ $(AM_V_CCLD)$(kzonecheck_LINK) $(kzonecheck_OBJECTS) $(kzonecheck_LDADD) $(LIBS)
+utils/kzonesign/$(am__dirstamp):
+ @$(MKDIR_P) utils/kzonesign
+ @: > utils/kzonesign/$(am__dirstamp)
+utils/kzonesign/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/kzonesign/$(DEPDIR)
+ @: > utils/kzonesign/$(DEPDIR)/$(am__dirstamp)
+utils/kzonesign/kzonesign-main.$(OBJEXT): \
+ utils/kzonesign/$(am__dirstamp) \
+ utils/kzonesign/$(DEPDIR)/$(am__dirstamp)
+
+kzonesign$(EXEEXT): $(kzonesign_OBJECTS) $(kzonesign_DEPENDENCIES) $(EXTRA_kzonesign_DEPENDENCIES)
+ @rm -f kzonesign$(EXEEXT)
+ $(AM_V_CCLD)$(kzonesign_LINK) $(kzonesign_OBJECTS) $(kzonesign_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f contrib/*.$(OBJEXT)
+ -rm -f contrib/*.lo
+ -rm -f contrib/dnstap/*.$(OBJEXT)
+ -rm -f contrib/dnstap/*.lo
+ -rm -f contrib/libbpf/bpf/*.$(OBJEXT)
+ -rm -f contrib/libbpf/bpf/*.lo
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/*.$(OBJEXT)
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/*.lo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/*.$(OBJEXT)
+ -rm -f contrib/libngtcp2/ngtcp2/lib/*.lo
+ -rm -f contrib/musl/*.$(OBJEXT)
+ -rm -f contrib/musl/*.lo
+ -rm -f contrib/openbsd/*.$(OBJEXT)
+ -rm -f contrib/openbsd/*.lo
+ -rm -f contrib/proxyv2/*.$(OBJEXT)
+ -rm -f contrib/proxyv2/*.lo
+ -rm -f contrib/qp-trie/*.$(OBJEXT)
+ -rm -f contrib/qp-trie/*.lo
+ -rm -f contrib/ucw/*.$(OBJEXT)
+ -rm -f contrib/ucw/*.lo
+ -rm -f contrib/url-parser/*.$(OBJEXT)
+ -rm -f contrib/url-parser/*.lo
+ -rm -f contrib/vpool/*.$(OBJEXT)
+ -rm -f contrib/vpool/*.lo
+ -rm -f knot/catalog/*.$(OBJEXT)
+ -rm -f knot/catalog/*.lo
+ -rm -f knot/common/*.$(OBJEXT)
+ -rm -f knot/common/*.lo
+ -rm -f knot/conf/*.$(OBJEXT)
+ -rm -f knot/conf/*.lo
+ -rm -f knot/ctl/*.$(OBJEXT)
+ -rm -f knot/ctl/*.lo
+ -rm -f knot/dnssec/*.$(OBJEXT)
+ -rm -f knot/dnssec/*.lo
+ -rm -f knot/dnssec/kasp/*.$(OBJEXT)
+ -rm -f knot/dnssec/kasp/*.lo
+ -rm -f knot/events/*.$(OBJEXT)
+ -rm -f knot/events/*.lo
+ -rm -f knot/events/handlers/*.$(OBJEXT)
+ -rm -f knot/events/handlers/*.lo
+ -rm -f knot/journal/*.$(OBJEXT)
+ -rm -f knot/journal/*.lo
+ -rm -f knot/modules/cookies/*.$(OBJEXT)
+ -rm -f knot/modules/cookies/*.lo
+ -rm -f knot/modules/dnsproxy/*.$(OBJEXT)
+ -rm -f knot/modules/dnsproxy/*.lo
+ -rm -f knot/modules/dnstap/*.$(OBJEXT)
+ -rm -f knot/modules/dnstap/*.lo
+ -rm -f knot/modules/geoip/*.$(OBJEXT)
+ -rm -f knot/modules/geoip/*.lo
+ -rm -f knot/modules/noudp/*.$(OBJEXT)
+ -rm -f knot/modules/noudp/*.lo
+ -rm -f knot/modules/onlinesign/*.$(OBJEXT)
+ -rm -f knot/modules/onlinesign/*.lo
+ -rm -f knot/modules/probe/*.$(OBJEXT)
+ -rm -f knot/modules/probe/*.lo
+ -rm -f knot/modules/queryacl/*.$(OBJEXT)
+ -rm -f knot/modules/queryacl/*.lo
+ -rm -f knot/modules/rrl/*.$(OBJEXT)
+ -rm -f knot/modules/rrl/*.lo
+ -rm -f knot/modules/stats/*.$(OBJEXT)
+ -rm -f knot/modules/stats/*.lo
+ -rm -f knot/modules/synthrecord/*.$(OBJEXT)
+ -rm -f knot/modules/synthrecord/*.lo
+ -rm -f knot/modules/whoami/*.$(OBJEXT)
+ -rm -f knot/modules/whoami/*.lo
+ -rm -f knot/nameserver/*.$(OBJEXT)
+ -rm -f knot/nameserver/*.lo
+ -rm -f knot/query/*.$(OBJEXT)
+ -rm -f knot/query/*.lo
+ -rm -f knot/server/*.$(OBJEXT)
+ -rm -f knot/server/*.lo
+ -rm -f knot/updates/*.$(OBJEXT)
+ -rm -f knot/updates/*.lo
+ -rm -f knot/worker/*.$(OBJEXT)
+ -rm -f knot/worker/*.lo
+ -rm -f knot/zone/*.$(OBJEXT)
+ -rm -f knot/zone/*.lo
+ -rm -f libdnssec/*.$(OBJEXT)
+ -rm -f libdnssec/*.lo
+ -rm -f libdnssec/key/*.$(OBJEXT)
+ -rm -f libdnssec/key/*.lo
+ -rm -f libdnssec/keystore/*.$(OBJEXT)
+ -rm -f libdnssec/keystore/*.lo
+ -rm -f libdnssec/nsec/*.$(OBJEXT)
+ -rm -f libdnssec/nsec/*.lo
+ -rm -f libdnssec/p11/*.$(OBJEXT)
+ -rm -f libdnssec/p11/*.lo
+ -rm -f libdnssec/shared/*.$(OBJEXT)
+ -rm -f libdnssec/shared/*.lo
+ -rm -f libdnssec/sign/*.$(OBJEXT)
+ -rm -f libdnssec/sign/*.lo
+ -rm -f libknot/*.$(OBJEXT)
+ -rm -f libknot/*.lo
+ -rm -f libknot/control/*.$(OBJEXT)
+ -rm -f libknot/control/*.lo
+ -rm -f libknot/db/*.$(OBJEXT)
+ -rm -f libknot/db/*.lo
+ -rm -f libknot/packet/*.$(OBJEXT)
+ -rm -f libknot/packet/*.lo
+ -rm -f libknot/probe/*.$(OBJEXT)
+ -rm -f libknot/probe/*.lo
+ -rm -f libknot/rrtype/*.$(OBJEXT)
+ -rm -f libknot/rrtype/*.lo
+ -rm -f libknot/xdp/*.$(OBJEXT)
+ -rm -f libknot/xdp/*.lo
+ -rm -f libknot/yparser/*.$(OBJEXT)
+ -rm -f libknot/yparser/*.lo
+ -rm -f libzscanner/*.$(OBJEXT)
+ -rm -f libzscanner/*.lo
+ -rm -f utils/common/*.$(OBJEXT)
+ -rm -f utils/common/*.lo
+ -rm -f utils/kcatalogprint/*.$(OBJEXT)
+ -rm -f utils/kdig/*.$(OBJEXT)
+ -rm -f utils/keymgr/*.$(OBJEXT)
+ -rm -f utils/khost/*.$(OBJEXT)
+ -rm -f utils/kjournalprint/*.$(OBJEXT)
+ -rm -f utils/knotc/*.$(OBJEXT)
+ -rm -f utils/knotd/*.$(OBJEXT)
+ -rm -f utils/knsec3hash/*.$(OBJEXT)
+ -rm -f utils/knsupdate/*.$(OBJEXT)
+ -rm -f utils/kxdpgun/*.$(OBJEXT)
+ -rm -f utils/kzonecheck/*.$(OBJEXT)
+ -rm -f utils/kzonesign/*.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-base64url.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-conn_pool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-files.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-getline.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-json.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-net.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-semaphore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-string.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-time.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-evsched.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-fdset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-process.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-stats.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-systemd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-unreachable.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-base.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-conf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-confio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-migration.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-module.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-schema.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-tools.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/ctl/$(DEPDIR)/libknotd_la-process.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/$(DEPDIR)/libknotd_la-events.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/$(DEPDIR)/libknotd_la-replan.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal_write.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/cookies/$(DEPDIR)/la-cookies.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnstap/$(DEPDIR)/la-dnstap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/la-geodb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/la-geoip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/noudp/$(DEPDIR)/la-noudp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/probe/$(DEPDIR)/la-probe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-functions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/la-rrl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/la-stats.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/whoami/$(DEPDIR)/la-whoami.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-capture.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-query.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-requestor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-server.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-acl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-apply.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/worker/$(DEPDIR)/libknotd_la-pool.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/worker/$(DEPDIR)/libknotd_la-queue.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-adjust.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-backup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-contents.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-measure.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-node.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-serial.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-timers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-binary.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-crypto.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-digest.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-keyid.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-pem.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-random.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/la-tsig.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-algorithm.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-convert.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-dnskey.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-ds.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-key.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-keytag.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-privkey.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/la-simple.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/la-keystore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/la-pkcs11.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/la-pkcs8.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/la-bitmap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/la-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/la-nsec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/p11/$(DEPDIR)/la-p11.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/la-bignum.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/la-dname.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/sign/$(DEPDIR)/la-der.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/sign/$(DEPDIR)/la-sign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-codes.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-cookies.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-descriptor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-dname.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-rdataset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-rrset-dump.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-rrset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-tsig-op.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/la-tsig.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/control/$(DEPDIR)/la-control.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/db/$(DEPDIR)/la-db_lmdb.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/db/$(DEPDIR)/la-db_trie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/packet/$(DEPDIR)/la-pkt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/packet/$(DEPDIR)/la-rrset-wire.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/probe/$(DEPDIR)/la-data.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/probe/$(DEPDIR)/la-probe.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-naptr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-opt.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/la-tsig.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-bpf-user.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-eth.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-quic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-quic_conn.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-tcp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/xdp/$(DEPDIR)/la-xdp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/la-yparser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/la-ypbody.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/la-ypformat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/la-ypschema.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/la-yptrafo.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/la-error.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/la-functions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/la-scanner.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-cert.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-exec.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-hex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-https.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-lookup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-msg.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-netio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-params.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-quic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-resolv.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-sign.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-tls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-token.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-util_conf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_params.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/khost-kdig_exec.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/khost-kdig_params.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-functions.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/khost/$(DEPDIR)/khost-khost_main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/khost/$(DEPDIR)/khost-khost_params.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-commands.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-interactive.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-process.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knotd/$(DEPDIR)/knotd-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonesign/$(DEPDIR)/kzonesign-main.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 $@ $<
+
+knot/modules/cookies/la-cookies.lo: knot/modules/cookies/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_cookies_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/cookies/la-cookies.lo -MD -MP -MF knot/modules/cookies/$(DEPDIR)/la-cookies.Tpo -c -o knot/modules/cookies/la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/cookies/$(DEPDIR)/la-cookies.Tpo knot/modules/cookies/$(DEPDIR)/la-cookies.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/cookies/cookies.c' object='knot/modules/cookies/la-cookies.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_cookies_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/cookies/la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c
+
+knot/modules/dnsproxy/la-dnsproxy.lo: knot/modules/dnsproxy/dnsproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnsproxy_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnsproxy/la-dnsproxy.lo -MD -MP -MF knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Tpo -c -o knot/modules/dnsproxy/la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Tpo knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnsproxy/dnsproxy.c' object='knot/modules/dnsproxy/la-dnsproxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnsproxy_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnsproxy/la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c
+
+knot/modules/dnstap/la-dnstap.lo: knot/modules/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnstap/la-dnstap.lo -MD -MP -MF knot/modules/dnstap/$(DEPDIR)/la-dnstap.Tpo -c -o knot/modules/dnstap/la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnstap/$(DEPDIR)/la-dnstap.Tpo knot/modules/dnstap/$(DEPDIR)/la-dnstap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnstap/dnstap.c' object='knot/modules/dnstap/la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnstap/la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c
+
+knot/modules/geoip/la-geoip.lo: knot/modules/geoip/geoip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/la-geoip.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/la-geoip.Tpo -c -o knot/modules/geoip/la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/la-geoip.Tpo knot/modules/geoip/$(DEPDIR)/la-geoip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geoip.c' object='knot/modules/geoip/la-geoip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c
+
+knot/modules/geoip/la-geodb.lo: knot/modules/geoip/geodb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/la-geodb.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/la-geodb.Tpo -c -o knot/modules/geoip/la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/la-geodb.Tpo knot/modules/geoip/$(DEPDIR)/la-geodb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geodb.c' object='knot/modules/geoip/la-geodb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c
+
+knot/modules/noudp/la-noudp.lo: knot/modules/noudp/noudp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_noudp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/noudp/la-noudp.lo -MD -MP -MF knot/modules/noudp/$(DEPDIR)/la-noudp.Tpo -c -o knot/modules/noudp/la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/noudp/$(DEPDIR)/la-noudp.Tpo knot/modules/noudp/$(DEPDIR)/la-noudp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/noudp/noudp.c' object='knot/modules/noudp/la-noudp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_noudp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/noudp/la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c
+
+knot/modules/onlinesign/la-onlinesign.lo: knot/modules/onlinesign/onlinesign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/la-onlinesign.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Tpo -c -o knot/modules/onlinesign/la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Tpo knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/onlinesign.c' object='knot/modules/onlinesign/la-onlinesign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c
+
+knot/modules/onlinesign/la-nsec_next.lo: knot/modules/onlinesign/nsec_next.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/la-nsec_next.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Tpo -c -o knot/modules/onlinesign/la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Tpo knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/nsec_next.c' object='knot/modules/onlinesign/la-nsec_next.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c
+
+knot/modules/probe/la-probe.lo: knot/modules/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_probe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/probe/la-probe.lo -MD -MP -MF knot/modules/probe/$(DEPDIR)/la-probe.Tpo -c -o knot/modules/probe/la-probe.lo `test -f 'knot/modules/probe/probe.c' || echo '$(srcdir)/'`knot/modules/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/probe/$(DEPDIR)/la-probe.Tpo knot/modules/probe/$(DEPDIR)/la-probe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/probe/probe.c' object='knot/modules/probe/la-probe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_probe_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/probe/la-probe.lo `test -f 'knot/modules/probe/probe.c' || echo '$(srcdir)/'`knot/modules/probe/probe.c
+
+knot/modules/queryacl/la-queryacl.lo: knot/modules/queryacl/queryacl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_queryacl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/queryacl/la-queryacl.lo -MD -MP -MF knot/modules/queryacl/$(DEPDIR)/la-queryacl.Tpo -c -o knot/modules/queryacl/la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/queryacl/$(DEPDIR)/la-queryacl.Tpo knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/queryacl/queryacl.c' object='knot/modules/queryacl/la-queryacl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_queryacl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/queryacl/la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c
+
+knot/modules/rrl/la-rrl.lo: knot/modules/rrl/rrl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/la-rrl.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/la-rrl.Tpo -c -o knot/modules/rrl/la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/la-rrl.Tpo knot/modules/rrl/$(DEPDIR)/la-rrl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/rrl.c' object='knot/modules/rrl/la-rrl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c
+
+knot/modules/rrl/la-functions.lo: knot/modules/rrl/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/la-functions.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/la-functions.Tpo -c -o knot/modules/rrl/la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/la-functions.Tpo knot/modules/rrl/$(DEPDIR)/la-functions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/functions.c' object='knot/modules/rrl/la-functions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+
+knot/modules/stats/la-stats.lo: knot/modules/stats/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_stats_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/la-stats.Tpo -c -o knot/modules/stats/la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/la-stats.Tpo knot/modules/stats/$(DEPDIR)/la-stats.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/stats/stats.c' object='knot/modules/stats/la-stats.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_stats_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/stats/la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
+
+knot/modules/synthrecord/la-synthrecord.lo: knot/modules/synthrecord/synthrecord.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_synthrecord_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/synthrecord/la-synthrecord.lo -MD -MP -MF knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Tpo -c -o knot/modules/synthrecord/la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Tpo knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/synthrecord/synthrecord.c' object='knot/modules/synthrecord/la-synthrecord.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_synthrecord_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/synthrecord/la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c
+
+knot/modules/whoami/la-whoami.lo: knot/modules/whoami/whoami.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_whoami_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/whoami/la-whoami.lo -MD -MP -MF knot/modules/whoami/$(DEPDIR)/la-whoami.Tpo -c -o knot/modules/whoami/la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/whoami/$(DEPDIR)/la-whoami.Tpo knot/modules/whoami/$(DEPDIR)/la-whoami.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/whoami/whoami.c' object='knot/modules/whoami/la-whoami.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_whoami_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/whoami/la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c
+
+contrib/libcontrib_la-base32hex.lo: contrib/base32hex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-base32hex.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-base32hex.Tpo -c -o contrib/libcontrib_la-base32hex.lo `test -f 'contrib/base32hex.c' || echo '$(srcdir)/'`contrib/base32hex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-base32hex.Tpo contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/base32hex.c' object='contrib/libcontrib_la-base32hex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-base32hex.lo `test -f 'contrib/base32hex.c' || echo '$(srcdir)/'`contrib/base32hex.c
+
+contrib/libcontrib_la-base64.lo: contrib/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-base64.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-base64.Tpo -c -o contrib/libcontrib_la-base64.lo `test -f 'contrib/base64.c' || echo '$(srcdir)/'`contrib/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-base64.Tpo contrib/$(DEPDIR)/libcontrib_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/base64.c' object='contrib/libcontrib_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-base64.lo `test -f 'contrib/base64.c' || echo '$(srcdir)/'`contrib/base64.c
+
+contrib/libcontrib_la-base64url.lo: contrib/base64url.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-base64url.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-base64url.Tpo -c -o contrib/libcontrib_la-base64url.lo `test -f 'contrib/base64url.c' || echo '$(srcdir)/'`contrib/base64url.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-base64url.Tpo contrib/$(DEPDIR)/libcontrib_la-base64url.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/base64url.c' object='contrib/libcontrib_la-base64url.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-base64url.lo `test -f 'contrib/base64url.c' || echo '$(srcdir)/'`contrib/base64url.c
+
+contrib/libcontrib_la-conn_pool.lo: contrib/conn_pool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-conn_pool.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-conn_pool.Tpo -c -o contrib/libcontrib_la-conn_pool.lo `test -f 'contrib/conn_pool.c' || echo '$(srcdir)/'`contrib/conn_pool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-conn_pool.Tpo contrib/$(DEPDIR)/libcontrib_la-conn_pool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/conn_pool.c' object='contrib/libcontrib_la-conn_pool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-conn_pool.lo `test -f 'contrib/conn_pool.c' || echo '$(srcdir)/'`contrib/conn_pool.c
+
+contrib/libcontrib_la-files.lo: contrib/files.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-files.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-files.Tpo -c -o contrib/libcontrib_la-files.lo `test -f 'contrib/files.c' || echo '$(srcdir)/'`contrib/files.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-files.Tpo contrib/$(DEPDIR)/libcontrib_la-files.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/files.c' object='contrib/libcontrib_la-files.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-files.lo `test -f 'contrib/files.c' || echo '$(srcdir)/'`contrib/files.c
+
+contrib/libcontrib_la-getline.lo: contrib/getline.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-getline.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-getline.Tpo -c -o contrib/libcontrib_la-getline.lo `test -f 'contrib/getline.c' || echo '$(srcdir)/'`contrib/getline.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-getline.Tpo contrib/$(DEPDIR)/libcontrib_la-getline.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/getline.c' object='contrib/libcontrib_la-getline.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-getline.lo `test -f 'contrib/getline.c' || echo '$(srcdir)/'`contrib/getline.c
+
+contrib/libcontrib_la-json.lo: contrib/json.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-json.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-json.Tpo -c -o contrib/libcontrib_la-json.lo `test -f 'contrib/json.c' || echo '$(srcdir)/'`contrib/json.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-json.Tpo contrib/$(DEPDIR)/libcontrib_la-json.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/json.c' object='contrib/libcontrib_la-json.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-json.lo `test -f 'contrib/json.c' || echo '$(srcdir)/'`contrib/json.c
+
+contrib/libcontrib_la-mempattern.lo: contrib/mempattern.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-mempattern.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-mempattern.Tpo -c -o contrib/libcontrib_la-mempattern.lo `test -f 'contrib/mempattern.c' || echo '$(srcdir)/'`contrib/mempattern.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-mempattern.Tpo contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/mempattern.c' object='contrib/libcontrib_la-mempattern.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-mempattern.lo `test -f 'contrib/mempattern.c' || echo '$(srcdir)/'`contrib/mempattern.c
+
+contrib/musl/libcontrib_la-inet_ntop.lo: contrib/musl/inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/musl/libcontrib_la-inet_ntop.lo -MD -MP -MF contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Tpo -c -o contrib/musl/libcontrib_la-inet_ntop.lo `test -f 'contrib/musl/inet_ntop.c' || echo '$(srcdir)/'`contrib/musl/inet_ntop.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Tpo contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/musl/inet_ntop.c' object='contrib/musl/libcontrib_la-inet_ntop.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/musl/libcontrib_la-inet_ntop.lo `test -f 'contrib/musl/inet_ntop.c' || echo '$(srcdir)/'`contrib/musl/inet_ntop.c
+
+contrib/libcontrib_la-net.lo: contrib/net.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-net.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-net.Tpo -c -o contrib/libcontrib_la-net.lo `test -f 'contrib/net.c' || echo '$(srcdir)/'`contrib/net.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-net.Tpo contrib/$(DEPDIR)/libcontrib_la-net.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/net.c' object='contrib/libcontrib_la-net.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-net.lo `test -f 'contrib/net.c' || echo '$(srcdir)/'`contrib/net.c
+
+contrib/qp-trie/libcontrib_la-trie.lo: contrib/qp-trie/trie.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/qp-trie/libcontrib_la-trie.lo -MD -MP -MF contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Tpo -c -o contrib/qp-trie/libcontrib_la-trie.lo `test -f 'contrib/qp-trie/trie.c' || echo '$(srcdir)/'`contrib/qp-trie/trie.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Tpo contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/qp-trie/trie.c' object='contrib/qp-trie/libcontrib_la-trie.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/qp-trie/libcontrib_la-trie.lo `test -f 'contrib/qp-trie/trie.c' || echo '$(srcdir)/'`contrib/qp-trie/trie.c
+
+contrib/libcontrib_la-semaphore.lo: contrib/semaphore.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-semaphore.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-semaphore.Tpo -c -o contrib/libcontrib_la-semaphore.lo `test -f 'contrib/semaphore.c' || echo '$(srcdir)/'`contrib/semaphore.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-semaphore.Tpo contrib/$(DEPDIR)/libcontrib_la-semaphore.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/semaphore.c' object='contrib/libcontrib_la-semaphore.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-semaphore.lo `test -f 'contrib/semaphore.c' || echo '$(srcdir)/'`contrib/semaphore.c
+
+contrib/libcontrib_la-sockaddr.lo: contrib/sockaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-sockaddr.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-sockaddr.Tpo -c -o contrib/libcontrib_la-sockaddr.lo `test -f 'contrib/sockaddr.c' || echo '$(srcdir)/'`contrib/sockaddr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-sockaddr.Tpo contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/sockaddr.c' object='contrib/libcontrib_la-sockaddr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-sockaddr.lo `test -f 'contrib/sockaddr.c' || echo '$(srcdir)/'`contrib/sockaddr.c
+
+contrib/libcontrib_la-string.lo: contrib/string.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-string.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-string.Tpo -c -o contrib/libcontrib_la-string.lo `test -f 'contrib/string.c' || echo '$(srcdir)/'`contrib/string.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-string.Tpo contrib/$(DEPDIR)/libcontrib_la-string.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/string.c' object='contrib/libcontrib_la-string.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-string.lo `test -f 'contrib/string.c' || echo '$(srcdir)/'`contrib/string.c
+
+contrib/libcontrib_la-time.lo: contrib/time.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-time.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-time.Tpo -c -o contrib/libcontrib_la-time.lo `test -f 'contrib/time.c' || echo '$(srcdir)/'`contrib/time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-time.Tpo contrib/$(DEPDIR)/libcontrib_la-time.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/time.c' object='contrib/libcontrib_la-time.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-time.lo `test -f 'contrib/time.c' || echo '$(srcdir)/'`contrib/time.c
+
+contrib/openbsd/libcontrib_la-siphash.lo: contrib/openbsd/siphash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-siphash.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Tpo -c -o contrib/openbsd/libcontrib_la-siphash.lo `test -f 'contrib/openbsd/siphash.c' || echo '$(srcdir)/'`contrib/openbsd/siphash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/siphash.c' object='contrib/openbsd/libcontrib_la-siphash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-siphash.lo `test -f 'contrib/openbsd/siphash.c' || echo '$(srcdir)/'`contrib/openbsd/siphash.c
+
+contrib/openbsd/libcontrib_la-strlcat.lo: contrib/openbsd/strlcat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-strlcat.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Tpo -c -o contrib/openbsd/libcontrib_la-strlcat.lo `test -f 'contrib/openbsd/strlcat.c' || echo '$(srcdir)/'`contrib/openbsd/strlcat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/strlcat.c' object='contrib/openbsd/libcontrib_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-strlcat.lo `test -f 'contrib/openbsd/strlcat.c' || echo '$(srcdir)/'`contrib/openbsd/strlcat.c
+
+contrib/openbsd/libcontrib_la-strlcpy.lo: contrib/openbsd/strlcpy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-strlcpy.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Tpo -c -o contrib/openbsd/libcontrib_la-strlcpy.lo `test -f 'contrib/openbsd/strlcpy.c' || echo '$(srcdir)/'`contrib/openbsd/strlcpy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/strlcpy.c' object='contrib/openbsd/libcontrib_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-strlcpy.lo `test -f 'contrib/openbsd/strlcpy.c' || echo '$(srcdir)/'`contrib/openbsd/strlcpy.c
+
+contrib/proxyv2/libcontrib_la-proxyv2.lo: contrib/proxyv2/proxyv2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/proxyv2/libcontrib_la-proxyv2.lo -MD -MP -MF contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Tpo -c -o contrib/proxyv2/libcontrib_la-proxyv2.lo `test -f 'contrib/proxyv2/proxyv2.c' || echo '$(srcdir)/'`contrib/proxyv2/proxyv2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Tpo contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/proxyv2/proxyv2.c' object='contrib/proxyv2/libcontrib_la-proxyv2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/proxyv2/libcontrib_la-proxyv2.lo `test -f 'contrib/proxyv2/proxyv2.c' || echo '$(srcdir)/'`contrib/proxyv2/proxyv2.c
+
+contrib/ucw/libcontrib_la-heap.lo: contrib/ucw/heap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-heap.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Tpo -c -o contrib/ucw/libcontrib_la-heap.lo `test -f 'contrib/ucw/heap.c' || echo '$(srcdir)/'`contrib/ucw/heap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/heap.c' object='contrib/ucw/libcontrib_la-heap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-heap.lo `test -f 'contrib/ucw/heap.c' || echo '$(srcdir)/'`contrib/ucw/heap.c
+
+contrib/ucw/libcontrib_la-lists.lo: contrib/ucw/lists.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-lists.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Tpo -c -o contrib/ucw/libcontrib_la-lists.lo `test -f 'contrib/ucw/lists.c' || echo '$(srcdir)/'`contrib/ucw/lists.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/lists.c' object='contrib/ucw/libcontrib_la-lists.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-lists.lo `test -f 'contrib/ucw/lists.c' || echo '$(srcdir)/'`contrib/ucw/lists.c
+
+contrib/ucw/libcontrib_la-mempool.lo: contrib/ucw/mempool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-mempool.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Tpo -c -o contrib/ucw/libcontrib_la-mempool.lo `test -f 'contrib/ucw/mempool.c' || echo '$(srcdir)/'`contrib/ucw/mempool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/mempool.c' object='contrib/ucw/libcontrib_la-mempool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-mempool.lo `test -f 'contrib/ucw/mempool.c' || echo '$(srcdir)/'`contrib/ucw/mempool.c
+
+contrib/url-parser/libcontrib_la-url_parser.lo: contrib/url-parser/url_parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/url-parser/libcontrib_la-url_parser.lo -MD -MP -MF contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Tpo -c -o contrib/url-parser/libcontrib_la-url_parser.lo `test -f 'contrib/url-parser/url_parser.c' || echo '$(srcdir)/'`contrib/url-parser/url_parser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Tpo contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/url-parser/url_parser.c' object='contrib/url-parser/libcontrib_la-url_parser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/url-parser/libcontrib_la-url_parser.lo `test -f 'contrib/url-parser/url_parser.c' || echo '$(srcdir)/'`contrib/url-parser/url_parser.c
+
+contrib/vpool/libcontrib_la-vpool.lo: contrib/vpool/vpool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/vpool/libcontrib_la-vpool.lo -MD -MP -MF contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Tpo -c -o contrib/vpool/libcontrib_la-vpool.lo `test -f 'contrib/vpool/vpool.c' || echo '$(srcdir)/'`contrib/vpool/vpool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Tpo contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/vpool/vpool.c' object='contrib/vpool/libcontrib_la-vpool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/vpool/libcontrib_la-vpool.lo `test -f 'contrib/vpool/vpool.c' || echo '$(srcdir)/'`contrib/vpool/vpool.c
+
+libdnssec/la-binary.lo: libdnssec/binary.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-binary.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-binary.Tpo -c -o libdnssec/la-binary.lo `test -f 'libdnssec/binary.c' || echo '$(srcdir)/'`libdnssec/binary.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-binary.Tpo libdnssec/$(DEPDIR)/la-binary.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/binary.c' object='libdnssec/la-binary.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-binary.lo `test -f 'libdnssec/binary.c' || echo '$(srcdir)/'`libdnssec/binary.c
+
+libdnssec/la-crypto.lo: libdnssec/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-crypto.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-crypto.Tpo -c -o libdnssec/la-crypto.lo `test -f 'libdnssec/crypto.c' || echo '$(srcdir)/'`libdnssec/crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-crypto.Tpo libdnssec/$(DEPDIR)/la-crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/crypto.c' object='libdnssec/la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-crypto.lo `test -f 'libdnssec/crypto.c' || echo '$(srcdir)/'`libdnssec/crypto.c
+
+libdnssec/la-digest.lo: libdnssec/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-digest.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-digest.Tpo -c -o libdnssec/la-digest.lo `test -f 'libdnssec/digest.c' || echo '$(srcdir)/'`libdnssec/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-digest.Tpo libdnssec/$(DEPDIR)/la-digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/digest.c' object='libdnssec/la-digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-digest.lo `test -f 'libdnssec/digest.c' || echo '$(srcdir)/'`libdnssec/digest.c
+
+libdnssec/la-error.lo: libdnssec/error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-error.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-error.Tpo -c -o libdnssec/la-error.lo `test -f 'libdnssec/error.c' || echo '$(srcdir)/'`libdnssec/error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-error.Tpo libdnssec/$(DEPDIR)/la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/error.c' object='libdnssec/la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-error.lo `test -f 'libdnssec/error.c' || echo '$(srcdir)/'`libdnssec/error.c
+
+libdnssec/key/la-algorithm.lo: libdnssec/key/algorithm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-algorithm.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-algorithm.Tpo -c -o libdnssec/key/la-algorithm.lo `test -f 'libdnssec/key/algorithm.c' || echo '$(srcdir)/'`libdnssec/key/algorithm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-algorithm.Tpo libdnssec/key/$(DEPDIR)/la-algorithm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/algorithm.c' object='libdnssec/key/la-algorithm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-algorithm.lo `test -f 'libdnssec/key/algorithm.c' || echo '$(srcdir)/'`libdnssec/key/algorithm.c
+
+libdnssec/key/la-convert.lo: libdnssec/key/convert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-convert.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-convert.Tpo -c -o libdnssec/key/la-convert.lo `test -f 'libdnssec/key/convert.c' || echo '$(srcdir)/'`libdnssec/key/convert.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-convert.Tpo libdnssec/key/$(DEPDIR)/la-convert.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/convert.c' object='libdnssec/key/la-convert.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-convert.lo `test -f 'libdnssec/key/convert.c' || echo '$(srcdir)/'`libdnssec/key/convert.c
+
+libdnssec/key/la-dnskey.lo: libdnssec/key/dnskey.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-dnskey.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-dnskey.Tpo -c -o libdnssec/key/la-dnskey.lo `test -f 'libdnssec/key/dnskey.c' || echo '$(srcdir)/'`libdnssec/key/dnskey.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-dnskey.Tpo libdnssec/key/$(DEPDIR)/la-dnskey.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/dnskey.c' object='libdnssec/key/la-dnskey.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-dnskey.lo `test -f 'libdnssec/key/dnskey.c' || echo '$(srcdir)/'`libdnssec/key/dnskey.c
+
+libdnssec/key/la-ds.lo: libdnssec/key/ds.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-ds.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-ds.Tpo -c -o libdnssec/key/la-ds.lo `test -f 'libdnssec/key/ds.c' || echo '$(srcdir)/'`libdnssec/key/ds.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-ds.Tpo libdnssec/key/$(DEPDIR)/la-ds.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/ds.c' object='libdnssec/key/la-ds.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-ds.lo `test -f 'libdnssec/key/ds.c' || echo '$(srcdir)/'`libdnssec/key/ds.c
+
+libdnssec/key/la-key.lo: libdnssec/key/key.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-key.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-key.Tpo -c -o libdnssec/key/la-key.lo `test -f 'libdnssec/key/key.c' || echo '$(srcdir)/'`libdnssec/key/key.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-key.Tpo libdnssec/key/$(DEPDIR)/la-key.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/key.c' object='libdnssec/key/la-key.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-key.lo `test -f 'libdnssec/key/key.c' || echo '$(srcdir)/'`libdnssec/key/key.c
+
+libdnssec/key/la-keytag.lo: libdnssec/key/keytag.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-keytag.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-keytag.Tpo -c -o libdnssec/key/la-keytag.lo `test -f 'libdnssec/key/keytag.c' || echo '$(srcdir)/'`libdnssec/key/keytag.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-keytag.Tpo libdnssec/key/$(DEPDIR)/la-keytag.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/keytag.c' object='libdnssec/key/la-keytag.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-keytag.lo `test -f 'libdnssec/key/keytag.c' || echo '$(srcdir)/'`libdnssec/key/keytag.c
+
+libdnssec/key/la-privkey.lo: libdnssec/key/privkey.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-privkey.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-privkey.Tpo -c -o libdnssec/key/la-privkey.lo `test -f 'libdnssec/key/privkey.c' || echo '$(srcdir)/'`libdnssec/key/privkey.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-privkey.Tpo libdnssec/key/$(DEPDIR)/la-privkey.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/privkey.c' object='libdnssec/key/la-privkey.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-privkey.lo `test -f 'libdnssec/key/privkey.c' || echo '$(srcdir)/'`libdnssec/key/privkey.c
+
+libdnssec/key/la-simple.lo: libdnssec/key/simple.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/la-simple.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/la-simple.Tpo -c -o libdnssec/key/la-simple.lo `test -f 'libdnssec/key/simple.c' || echo '$(srcdir)/'`libdnssec/key/simple.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/la-simple.Tpo libdnssec/key/$(DEPDIR)/la-simple.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/simple.c' object='libdnssec/key/la-simple.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/la-simple.lo `test -f 'libdnssec/key/simple.c' || echo '$(srcdir)/'`libdnssec/key/simple.c
+
+libdnssec/la-keyid.lo: libdnssec/keyid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-keyid.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-keyid.Tpo -c -o libdnssec/la-keyid.lo `test -f 'libdnssec/keyid.c' || echo '$(srcdir)/'`libdnssec/keyid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-keyid.Tpo libdnssec/$(DEPDIR)/la-keyid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keyid.c' object='libdnssec/la-keyid.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-keyid.lo `test -f 'libdnssec/keyid.c' || echo '$(srcdir)/'`libdnssec/keyid.c
+
+libdnssec/keystore/la-keystore.lo: libdnssec/keystore/keystore.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/la-keystore.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/la-keystore.Tpo -c -o libdnssec/keystore/la-keystore.lo `test -f 'libdnssec/keystore/keystore.c' || echo '$(srcdir)/'`libdnssec/keystore/keystore.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/la-keystore.Tpo libdnssec/keystore/$(DEPDIR)/la-keystore.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/keystore.c' object='libdnssec/keystore/la-keystore.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/la-keystore.lo `test -f 'libdnssec/keystore/keystore.c' || echo '$(srcdir)/'`libdnssec/keystore/keystore.c
+
+libdnssec/keystore/la-pkcs11.lo: libdnssec/keystore/pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/la-pkcs11.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/la-pkcs11.Tpo -c -o libdnssec/keystore/la-pkcs11.lo `test -f 'libdnssec/keystore/pkcs11.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/la-pkcs11.Tpo libdnssec/keystore/$(DEPDIR)/la-pkcs11.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/pkcs11.c' object='libdnssec/keystore/la-pkcs11.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/la-pkcs11.lo `test -f 'libdnssec/keystore/pkcs11.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs11.c
+
+libdnssec/keystore/la-pkcs8.lo: libdnssec/keystore/pkcs8.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/la-pkcs8.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/la-pkcs8.Tpo -c -o libdnssec/keystore/la-pkcs8.lo `test -f 'libdnssec/keystore/pkcs8.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/la-pkcs8.Tpo libdnssec/keystore/$(DEPDIR)/la-pkcs8.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/pkcs8.c' object='libdnssec/keystore/la-pkcs8.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/la-pkcs8.lo `test -f 'libdnssec/keystore/pkcs8.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8.c
+
+libdnssec/nsec/la-bitmap.lo: libdnssec/nsec/bitmap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/la-bitmap.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/la-bitmap.Tpo -c -o libdnssec/nsec/la-bitmap.lo `test -f 'libdnssec/nsec/bitmap.c' || echo '$(srcdir)/'`libdnssec/nsec/bitmap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/la-bitmap.Tpo libdnssec/nsec/$(DEPDIR)/la-bitmap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/bitmap.c' object='libdnssec/nsec/la-bitmap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/la-bitmap.lo `test -f 'libdnssec/nsec/bitmap.c' || echo '$(srcdir)/'`libdnssec/nsec/bitmap.c
+
+libdnssec/nsec/la-hash.lo: libdnssec/nsec/hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/la-hash.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/la-hash.Tpo -c -o libdnssec/nsec/la-hash.lo `test -f 'libdnssec/nsec/hash.c' || echo '$(srcdir)/'`libdnssec/nsec/hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/la-hash.Tpo libdnssec/nsec/$(DEPDIR)/la-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/hash.c' object='libdnssec/nsec/la-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/la-hash.lo `test -f 'libdnssec/nsec/hash.c' || echo '$(srcdir)/'`libdnssec/nsec/hash.c
+
+libdnssec/nsec/la-nsec.lo: libdnssec/nsec/nsec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/la-nsec.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/la-nsec.Tpo -c -o libdnssec/nsec/la-nsec.lo `test -f 'libdnssec/nsec/nsec.c' || echo '$(srcdir)/'`libdnssec/nsec/nsec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/la-nsec.Tpo libdnssec/nsec/$(DEPDIR)/la-nsec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/nsec.c' object='libdnssec/nsec/la-nsec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/la-nsec.lo `test -f 'libdnssec/nsec/nsec.c' || echo '$(srcdir)/'`libdnssec/nsec/nsec.c
+
+libdnssec/p11/la-p11.lo: libdnssec/p11/p11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/p11/la-p11.lo -MD -MP -MF libdnssec/p11/$(DEPDIR)/la-p11.Tpo -c -o libdnssec/p11/la-p11.lo `test -f 'libdnssec/p11/p11.c' || echo '$(srcdir)/'`libdnssec/p11/p11.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/p11/$(DEPDIR)/la-p11.Tpo libdnssec/p11/$(DEPDIR)/la-p11.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/p11/p11.c' object='libdnssec/p11/la-p11.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/p11/la-p11.lo `test -f 'libdnssec/p11/p11.c' || echo '$(srcdir)/'`libdnssec/p11/p11.c
+
+libdnssec/la-pem.lo: libdnssec/pem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-pem.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-pem.Tpo -c -o libdnssec/la-pem.lo `test -f 'libdnssec/pem.c' || echo '$(srcdir)/'`libdnssec/pem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-pem.Tpo libdnssec/$(DEPDIR)/la-pem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/pem.c' object='libdnssec/la-pem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-pem.lo `test -f 'libdnssec/pem.c' || echo '$(srcdir)/'`libdnssec/pem.c
+
+libdnssec/la-random.lo: libdnssec/random.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-random.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-random.Tpo -c -o libdnssec/la-random.lo `test -f 'libdnssec/random.c' || echo '$(srcdir)/'`libdnssec/random.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-random.Tpo libdnssec/$(DEPDIR)/la-random.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/random.c' object='libdnssec/la-random.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-random.lo `test -f 'libdnssec/random.c' || echo '$(srcdir)/'`libdnssec/random.c
+
+libdnssec/shared/la-bignum.lo: libdnssec/shared/bignum.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/la-bignum.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/la-bignum.Tpo -c -o libdnssec/shared/la-bignum.lo `test -f 'libdnssec/shared/bignum.c' || echo '$(srcdir)/'`libdnssec/shared/bignum.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/la-bignum.Tpo libdnssec/shared/$(DEPDIR)/la-bignum.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/bignum.c' object='libdnssec/shared/la-bignum.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/la-bignum.lo `test -f 'libdnssec/shared/bignum.c' || echo '$(srcdir)/'`libdnssec/shared/bignum.c
+
+libdnssec/shared/la-dname.lo: libdnssec/shared/dname.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/la-dname.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/la-dname.Tpo -c -o libdnssec/shared/la-dname.lo `test -f 'libdnssec/shared/dname.c' || echo '$(srcdir)/'`libdnssec/shared/dname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/la-dname.Tpo libdnssec/shared/$(DEPDIR)/la-dname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/dname.c' object='libdnssec/shared/la-dname.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/la-dname.lo `test -f 'libdnssec/shared/dname.c' || echo '$(srcdir)/'`libdnssec/shared/dname.c
+
+libdnssec/shared/la-keyid_gnutls.lo: libdnssec/shared/keyid_gnutls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/la-keyid_gnutls.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Tpo -c -o libdnssec/shared/la-keyid_gnutls.lo `test -f 'libdnssec/shared/keyid_gnutls.c' || echo '$(srcdir)/'`libdnssec/shared/keyid_gnutls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Tpo libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/keyid_gnutls.c' object='libdnssec/shared/la-keyid_gnutls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/la-keyid_gnutls.lo `test -f 'libdnssec/shared/keyid_gnutls.c' || echo '$(srcdir)/'`libdnssec/shared/keyid_gnutls.c
+
+libdnssec/sign/la-der.lo: libdnssec/sign/der.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/sign/la-der.lo -MD -MP -MF libdnssec/sign/$(DEPDIR)/la-der.Tpo -c -o libdnssec/sign/la-der.lo `test -f 'libdnssec/sign/der.c' || echo '$(srcdir)/'`libdnssec/sign/der.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/sign/$(DEPDIR)/la-der.Tpo libdnssec/sign/$(DEPDIR)/la-der.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/sign/der.c' object='libdnssec/sign/la-der.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/sign/la-der.lo `test -f 'libdnssec/sign/der.c' || echo '$(srcdir)/'`libdnssec/sign/der.c
+
+libdnssec/sign/la-sign.lo: libdnssec/sign/sign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/sign/la-sign.lo -MD -MP -MF libdnssec/sign/$(DEPDIR)/la-sign.Tpo -c -o libdnssec/sign/la-sign.lo `test -f 'libdnssec/sign/sign.c' || echo '$(srcdir)/'`libdnssec/sign/sign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/sign/$(DEPDIR)/la-sign.Tpo libdnssec/sign/$(DEPDIR)/la-sign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/sign/sign.c' object='libdnssec/sign/la-sign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/sign/la-sign.lo `test -f 'libdnssec/sign/sign.c' || echo '$(srcdir)/'`libdnssec/sign/sign.c
+
+libdnssec/la-tsig.lo: libdnssec/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/la-tsig.lo -MD -MP -MF libdnssec/$(DEPDIR)/la-tsig.Tpo -c -o libdnssec/la-tsig.lo `test -f 'libdnssec/tsig.c' || echo '$(srcdir)/'`libdnssec/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/la-tsig.Tpo libdnssec/$(DEPDIR)/la-tsig.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/tsig.c' object='libdnssec/la-tsig.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/la-tsig.lo `test -f 'libdnssec/tsig.c' || echo '$(srcdir)/'`libdnssec/tsig.c
+
+contrib/dnstap/libdnstap_la-convert.lo: contrib/dnstap/convert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-convert.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Tpo -c -o contrib/dnstap/libdnstap_la-convert.lo `test -f 'contrib/dnstap/convert.c' || echo '$(srcdir)/'`contrib/dnstap/convert.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/convert.c' object='contrib/dnstap/libdnstap_la-convert.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-convert.lo `test -f 'contrib/dnstap/convert.c' || echo '$(srcdir)/'`contrib/dnstap/convert.c
+
+contrib/dnstap/libdnstap_la-dnstap.lo: contrib/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-dnstap.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Tpo -c -o contrib/dnstap/libdnstap_la-dnstap.lo `test -f 'contrib/dnstap/dnstap.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/dnstap.c' object='contrib/dnstap/libdnstap_la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-dnstap.lo `test -f 'contrib/dnstap/dnstap.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.c
+
+contrib/dnstap/libdnstap_la-message.lo: contrib/dnstap/message.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-message.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Tpo -c -o contrib/dnstap/libdnstap_la-message.lo `test -f 'contrib/dnstap/message.c' || echo '$(srcdir)/'`contrib/dnstap/message.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/message.c' object='contrib/dnstap/libdnstap_la-message.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-message.lo `test -f 'contrib/dnstap/message.c' || echo '$(srcdir)/'`contrib/dnstap/message.c
+
+contrib/dnstap/libdnstap_la-reader.lo: contrib/dnstap/reader.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-reader.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Tpo -c -o contrib/dnstap/libdnstap_la-reader.lo `test -f 'contrib/dnstap/reader.c' || echo '$(srcdir)/'`contrib/dnstap/reader.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/reader.c' object='contrib/dnstap/libdnstap_la-reader.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-reader.lo `test -f 'contrib/dnstap/reader.c' || echo '$(srcdir)/'`contrib/dnstap/reader.c
+
+contrib/dnstap/libdnstap_la-writer.lo: contrib/dnstap/writer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-writer.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Tpo -c -o contrib/dnstap/libdnstap_la-writer.lo `test -f 'contrib/dnstap/writer.c' || echo '$(srcdir)/'`contrib/dnstap/writer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/writer.c' object='contrib/dnstap/libdnstap_la-writer.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-writer.lo `test -f 'contrib/dnstap/writer.c' || echo '$(srcdir)/'`contrib/dnstap/writer.c
+
+contrib/dnstap/libdnstap_la-dnstap.pb-c.lo: contrib/dnstap/dnstap.pb-c.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-dnstap.pb-c.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Tpo -c -o contrib/dnstap/libdnstap_la-dnstap.pb-c.lo `test -f 'contrib/dnstap/dnstap.pb-c.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.pb-c.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/dnstap.pb-c.c' object='contrib/dnstap/libdnstap_la-dnstap.pb-c.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-dnstap.pb-c.lo `test -f 'contrib/dnstap/dnstap.pb-c.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.pb-c.c
+
+contrib/libbpf/bpf/libembbpf_la-bpf.lo: contrib/libbpf/bpf/bpf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-bpf.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-bpf.lo `test -f 'contrib/libbpf/bpf/bpf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/bpf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/bpf.c' object='contrib/libbpf/bpf/libembbpf_la-bpf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-bpf.lo `test -f 'contrib/libbpf/bpf/bpf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/bpf.c
+
+contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo: contrib/libbpf/bpf/bpf_prog_linfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo `test -f 'contrib/libbpf/bpf/bpf_prog_linfo.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/bpf_prog_linfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/bpf_prog_linfo.c' object='contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-bpf_prog_linfo.lo `test -f 'contrib/libbpf/bpf/bpf_prog_linfo.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/bpf_prog_linfo.c
+
+contrib/libbpf/bpf/libembbpf_la-btf.lo: contrib/libbpf/bpf/btf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-btf.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-btf.lo `test -f 'contrib/libbpf/bpf/btf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/btf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/btf.c' object='contrib/libbpf/bpf/libembbpf_la-btf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-btf.lo `test -f 'contrib/libbpf/bpf/btf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/btf.c
+
+contrib/libbpf/bpf/libembbpf_la-btf_dump.lo: contrib/libbpf/bpf/btf_dump.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-btf_dump.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-btf_dump.lo `test -f 'contrib/libbpf/bpf/btf_dump.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/btf_dump.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/btf_dump.c' object='contrib/libbpf/bpf/libembbpf_la-btf_dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-btf_dump.lo `test -f 'contrib/libbpf/bpf/btf_dump.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/btf_dump.c
+
+contrib/libbpf/bpf/libembbpf_la-hashmap.lo: contrib/libbpf/bpf/hashmap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-hashmap.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-hashmap.lo `test -f 'contrib/libbpf/bpf/hashmap.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/hashmap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/hashmap.c' object='contrib/libbpf/bpf/libembbpf_la-hashmap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-hashmap.lo `test -f 'contrib/libbpf/bpf/hashmap.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/hashmap.c
+
+contrib/libbpf/bpf/libembbpf_la-libbpf.lo: contrib/libbpf/bpf/libbpf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-libbpf.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-libbpf.lo `test -f 'contrib/libbpf/bpf/libbpf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/libbpf.c' object='contrib/libbpf/bpf/libembbpf_la-libbpf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-libbpf.lo `test -f 'contrib/libbpf/bpf/libbpf.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf.c
+
+contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo: contrib/libbpf/bpf/libbpf_errno.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo `test -f 'contrib/libbpf/bpf/libbpf_errno.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf_errno.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/libbpf_errno.c' object='contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-libbpf_errno.lo `test -f 'contrib/libbpf/bpf/libbpf_errno.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf_errno.c
+
+contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo: contrib/libbpf/bpf/libbpf_probes.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo `test -f 'contrib/libbpf/bpf/libbpf_probes.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf_probes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/libbpf_probes.c' object='contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-libbpf_probes.lo `test -f 'contrib/libbpf/bpf/libbpf_probes.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/libbpf_probes.c
+
+contrib/libbpf/bpf/libembbpf_la-netlink.lo: contrib/libbpf/bpf/netlink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-netlink.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-netlink.lo `test -f 'contrib/libbpf/bpf/netlink.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/netlink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/netlink.c' object='contrib/libbpf/bpf/libembbpf_la-netlink.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-netlink.lo `test -f 'contrib/libbpf/bpf/netlink.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/netlink.c
+
+contrib/libbpf/bpf/libembbpf_la-nlattr.lo: contrib/libbpf/bpf/nlattr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-nlattr.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-nlattr.lo `test -f 'contrib/libbpf/bpf/nlattr.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/nlattr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/nlattr.c' object='contrib/libbpf/bpf/libembbpf_la-nlattr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-nlattr.lo `test -f 'contrib/libbpf/bpf/nlattr.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/nlattr.c
+
+contrib/libbpf/bpf/libembbpf_la-str_error.lo: contrib/libbpf/bpf/str_error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-str_error.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-str_error.lo `test -f 'contrib/libbpf/bpf/str_error.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/str_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/str_error.c' object='contrib/libbpf/bpf/libembbpf_la-str_error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-str_error.lo `test -f 'contrib/libbpf/bpf/str_error.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/str_error.c
+
+contrib/libbpf/bpf/libembbpf_la-xsk.lo: contrib/libbpf/bpf/xsk.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libbpf/bpf/libembbpf_la-xsk.lo -MD -MP -MF contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Tpo -c -o contrib/libbpf/bpf/libembbpf_la-xsk.lo `test -f 'contrib/libbpf/bpf/xsk.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/xsk.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Tpo contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libbpf/bpf/xsk.c' object='contrib/libbpf/bpf/libembbpf_la-xsk.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembbpf_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libbpf/bpf/libembbpf_la-xsk.lo `test -f 'contrib/libbpf/bpf/xsk.c' || echo '$(srcdir)/'`contrib/libbpf/bpf/xsk.c
+
+contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo: contrib/libngtcp2/ngtcp2/crypto/gnutls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Tpo -c -o contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo `test -f 'contrib/libngtcp2/ngtcp2/crypto/gnutls.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/crypto/gnutls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Tpo contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/crypto/gnutls.c' object='contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-gnutls.lo `test -f 'contrib/libngtcp2/ngtcp2/crypto/gnutls.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/crypto/gnutls.c
+
+contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo: contrib/libngtcp2/ngtcp2/crypto/shared.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Tpo -c -o contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo `test -f 'contrib/libngtcp2/ngtcp2/crypto/shared.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/crypto/shared.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Tpo contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/crypto/shared.c' object='contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/crypto/libembngtcp2_la-shared.lo `test -f 'contrib/libngtcp2/ngtcp2/crypto/shared.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/crypto/shared.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_acktr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_addr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_balloc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_bbr2.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_buf.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_cid.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conn.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_conv.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_crypto.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_err.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_gaptr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_idtr.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ksl.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_log.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_map.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_mem.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_objalloc.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_opl.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_path.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pkt.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pmtud.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ppe.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pq.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_pv.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_qlog.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_range.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_ringbuf.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rob.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rst.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_rtb.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_str.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_strm.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_unreachable.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_vec.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_version.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
+
+contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo: contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo -MD -MP -MF contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Tpo -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Tpo contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c' object='contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libembngtcp2_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libngtcp2/ngtcp2/lib/libembngtcp2_la-ngtcp2_window_filter.lo `test -f 'contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c' || echo '$(srcdir)/'`contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
+
+libknot/la-codes.lo: libknot/codes.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-codes.lo -MD -MP -MF libknot/$(DEPDIR)/la-codes.Tpo -c -o libknot/la-codes.lo `test -f 'libknot/codes.c' || echo '$(srcdir)/'`libknot/codes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-codes.Tpo libknot/$(DEPDIR)/la-codes.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/codes.c' object='libknot/la-codes.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-codes.lo `test -f 'libknot/codes.c' || echo '$(srcdir)/'`libknot/codes.c
+
+libknot/control/la-control.lo: libknot/control/control.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/control/la-control.lo -MD -MP -MF libknot/control/$(DEPDIR)/la-control.Tpo -c -o libknot/control/la-control.lo `test -f 'libknot/control/control.c' || echo '$(srcdir)/'`libknot/control/control.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/control/$(DEPDIR)/la-control.Tpo libknot/control/$(DEPDIR)/la-control.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/control/control.c' object='libknot/control/la-control.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/control/la-control.lo `test -f 'libknot/control/control.c' || echo '$(srcdir)/'`libknot/control/control.c
+
+libknot/la-cookies.lo: libknot/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-cookies.lo -MD -MP -MF libknot/$(DEPDIR)/la-cookies.Tpo -c -o libknot/la-cookies.lo `test -f 'libknot/cookies.c' || echo '$(srcdir)/'`libknot/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-cookies.Tpo libknot/$(DEPDIR)/la-cookies.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/cookies.c' object='libknot/la-cookies.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-cookies.lo `test -f 'libknot/cookies.c' || echo '$(srcdir)/'`libknot/cookies.c
+
+libknot/la-descriptor.lo: libknot/descriptor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-descriptor.lo -MD -MP -MF libknot/$(DEPDIR)/la-descriptor.Tpo -c -o libknot/la-descriptor.lo `test -f 'libknot/descriptor.c' || echo '$(srcdir)/'`libknot/descriptor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-descriptor.Tpo libknot/$(DEPDIR)/la-descriptor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/descriptor.c' object='libknot/la-descriptor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-descriptor.lo `test -f 'libknot/descriptor.c' || echo '$(srcdir)/'`libknot/descriptor.c
+
+libknot/la-dname.lo: libknot/dname.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-dname.lo -MD -MP -MF libknot/$(DEPDIR)/la-dname.Tpo -c -o libknot/la-dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-dname.Tpo libknot/$(DEPDIR)/la-dname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/dname.c' object='libknot/la-dname.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c
+
+libknot/la-error.lo: libknot/error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-error.lo -MD -MP -MF libknot/$(DEPDIR)/la-error.Tpo -c -o libknot/la-error.lo `test -f 'libknot/error.c' || echo '$(srcdir)/'`libknot/error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-error.Tpo libknot/$(DEPDIR)/la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/error.c' object='libknot/la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-error.lo `test -f 'libknot/error.c' || echo '$(srcdir)/'`libknot/error.c
+
+libknot/db/la-db_lmdb.lo: libknot/db/db_lmdb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/db/la-db_lmdb.lo -MD -MP -MF libknot/db/$(DEPDIR)/la-db_lmdb.Tpo -c -o libknot/db/la-db_lmdb.lo `test -f 'libknot/db/db_lmdb.c' || echo '$(srcdir)/'`libknot/db/db_lmdb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/db/$(DEPDIR)/la-db_lmdb.Tpo libknot/db/$(DEPDIR)/la-db_lmdb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/db/db_lmdb.c' object='libknot/db/la-db_lmdb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/db/la-db_lmdb.lo `test -f 'libknot/db/db_lmdb.c' || echo '$(srcdir)/'`libknot/db/db_lmdb.c
+
+libknot/db/la-db_trie.lo: libknot/db/db_trie.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/db/la-db_trie.lo -MD -MP -MF libknot/db/$(DEPDIR)/la-db_trie.Tpo -c -o libknot/db/la-db_trie.lo `test -f 'libknot/db/db_trie.c' || echo '$(srcdir)/'`libknot/db/db_trie.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/db/$(DEPDIR)/la-db_trie.Tpo libknot/db/$(DEPDIR)/la-db_trie.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/db/db_trie.c' object='libknot/db/la-db_trie.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/db/la-db_trie.lo `test -f 'libknot/db/db_trie.c' || echo '$(srcdir)/'`libknot/db/db_trie.c
+
+libknot/packet/la-pkt.lo: libknot/packet/pkt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/packet/la-pkt.lo -MD -MP -MF libknot/packet/$(DEPDIR)/la-pkt.Tpo -c -o libknot/packet/la-pkt.lo `test -f 'libknot/packet/pkt.c' || echo '$(srcdir)/'`libknot/packet/pkt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/packet/$(DEPDIR)/la-pkt.Tpo libknot/packet/$(DEPDIR)/la-pkt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/packet/pkt.c' object='libknot/packet/la-pkt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/packet/la-pkt.lo `test -f 'libknot/packet/pkt.c' || echo '$(srcdir)/'`libknot/packet/pkt.c
+
+libknot/packet/la-rrset-wire.lo: libknot/packet/rrset-wire.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/packet/la-rrset-wire.lo -MD -MP -MF libknot/packet/$(DEPDIR)/la-rrset-wire.Tpo -c -o libknot/packet/la-rrset-wire.lo `test -f 'libknot/packet/rrset-wire.c' || echo '$(srcdir)/'`libknot/packet/rrset-wire.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/packet/$(DEPDIR)/la-rrset-wire.Tpo libknot/packet/$(DEPDIR)/la-rrset-wire.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/packet/rrset-wire.c' object='libknot/packet/la-rrset-wire.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/packet/la-rrset-wire.lo `test -f 'libknot/packet/rrset-wire.c' || echo '$(srcdir)/'`libknot/packet/rrset-wire.c
+
+libknot/probe/la-data.lo: libknot/probe/data.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/probe/la-data.lo -MD -MP -MF libknot/probe/$(DEPDIR)/la-data.Tpo -c -o libknot/probe/la-data.lo `test -f 'libknot/probe/data.c' || echo '$(srcdir)/'`libknot/probe/data.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/probe/$(DEPDIR)/la-data.Tpo libknot/probe/$(DEPDIR)/la-data.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/probe/data.c' object='libknot/probe/la-data.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/probe/la-data.lo `test -f 'libknot/probe/data.c' || echo '$(srcdir)/'`libknot/probe/data.c
+
+libknot/probe/la-probe.lo: libknot/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/probe/la-probe.lo -MD -MP -MF libknot/probe/$(DEPDIR)/la-probe.Tpo -c -o libknot/probe/la-probe.lo `test -f 'libknot/probe/probe.c' || echo '$(srcdir)/'`libknot/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/probe/$(DEPDIR)/la-probe.Tpo libknot/probe/$(DEPDIR)/la-probe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/probe/probe.c' object='libknot/probe/la-probe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/probe/la-probe.lo `test -f 'libknot/probe/probe.c' || echo '$(srcdir)/'`libknot/probe/probe.c
+
+libknot/la-rdataset.lo: libknot/rdataset.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-rdataset.lo -MD -MP -MF libknot/$(DEPDIR)/la-rdataset.Tpo -c -o libknot/la-rdataset.lo `test -f 'libknot/rdataset.c' || echo '$(srcdir)/'`libknot/rdataset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-rdataset.Tpo libknot/$(DEPDIR)/la-rdataset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rdataset.c' object='libknot/la-rdataset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-rdataset.lo `test -f 'libknot/rdataset.c' || echo '$(srcdir)/'`libknot/rdataset.c
+
+libknot/la-rrset-dump.lo: libknot/rrset-dump.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-rrset-dump.lo -MD -MP -MF libknot/$(DEPDIR)/la-rrset-dump.Tpo -c -o libknot/la-rrset-dump.lo `test -f 'libknot/rrset-dump.c' || echo '$(srcdir)/'`libknot/rrset-dump.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-rrset-dump.Tpo libknot/$(DEPDIR)/la-rrset-dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrset-dump.c' object='libknot/la-rrset-dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-rrset-dump.lo `test -f 'libknot/rrset-dump.c' || echo '$(srcdir)/'`libknot/rrset-dump.c
+
+libknot/la-rrset.lo: libknot/rrset.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-rrset.lo -MD -MP -MF libknot/$(DEPDIR)/la-rrset.Tpo -c -o libknot/la-rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-rrset.Tpo libknot/$(DEPDIR)/la-rrset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrset.c' object='libknot/la-rrset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c
+
+libknot/rrtype/la-naptr.lo: libknot/rrtype/naptr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/la-naptr.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/la-naptr.Tpo -c -o libknot/rrtype/la-naptr.lo `test -f 'libknot/rrtype/naptr.c' || echo '$(srcdir)/'`libknot/rrtype/naptr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/la-naptr.Tpo libknot/rrtype/$(DEPDIR)/la-naptr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/naptr.c' object='libknot/rrtype/la-naptr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/la-naptr.lo `test -f 'libknot/rrtype/naptr.c' || echo '$(srcdir)/'`libknot/rrtype/naptr.c
+
+libknot/rrtype/la-opt.lo: libknot/rrtype/opt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/la-opt.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/la-opt.Tpo -c -o libknot/rrtype/la-opt.lo `test -f 'libknot/rrtype/opt.c' || echo '$(srcdir)/'`libknot/rrtype/opt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/la-opt.Tpo libknot/rrtype/$(DEPDIR)/la-opt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/opt.c' object='libknot/rrtype/la-opt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/la-opt.lo `test -f 'libknot/rrtype/opt.c' || echo '$(srcdir)/'`libknot/rrtype/opt.c
+
+libknot/rrtype/la-tsig.lo: libknot/rrtype/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/la-tsig.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/la-tsig.Tpo -c -o libknot/rrtype/la-tsig.lo `test -f 'libknot/rrtype/tsig.c' || echo '$(srcdir)/'`libknot/rrtype/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/la-tsig.Tpo libknot/rrtype/$(DEPDIR)/la-tsig.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/tsig.c' object='libknot/rrtype/la-tsig.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/la-tsig.lo `test -f 'libknot/rrtype/tsig.c' || echo '$(srcdir)/'`libknot/rrtype/tsig.c
+
+libknot/la-tsig-op.lo: libknot/tsig-op.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-tsig-op.lo -MD -MP -MF libknot/$(DEPDIR)/la-tsig-op.Tpo -c -o libknot/la-tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-tsig-op.Tpo libknot/$(DEPDIR)/la-tsig-op.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/tsig-op.c' object='libknot/la-tsig-op.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c
+
+libknot/la-tsig.lo: libknot/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/la-tsig.lo -MD -MP -MF libknot/$(DEPDIR)/la-tsig.Tpo -c -o libknot/la-tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/la-tsig.Tpo libknot/$(DEPDIR)/la-tsig.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/tsig.c' object='libknot/la-tsig.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/la-tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c
+
+libknot/yparser/la-yparser.lo: libknot/yparser/yparser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/la-yparser.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/la-yparser.Tpo -c -o libknot/yparser/la-yparser.lo `test -f 'libknot/yparser/yparser.c' || echo '$(srcdir)/'`libknot/yparser/yparser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/la-yparser.Tpo libknot/yparser/$(DEPDIR)/la-yparser.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/yparser.c' object='libknot/yparser/la-yparser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/la-yparser.lo `test -f 'libknot/yparser/yparser.c' || echo '$(srcdir)/'`libknot/yparser/yparser.c
+
+libknot/yparser/la-ypbody.lo: libknot/yparser/ypbody.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/la-ypbody.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/la-ypbody.Tpo -c -o libknot/yparser/la-ypbody.lo `test -f 'libknot/yparser/ypbody.c' || echo '$(srcdir)/'`libknot/yparser/ypbody.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/la-ypbody.Tpo libknot/yparser/$(DEPDIR)/la-ypbody.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypbody.c' object='libknot/yparser/la-ypbody.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/la-ypbody.lo `test -f 'libknot/yparser/ypbody.c' || echo '$(srcdir)/'`libknot/yparser/ypbody.c
+
+libknot/yparser/la-ypformat.lo: libknot/yparser/ypformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/la-ypformat.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/la-ypformat.Tpo -c -o libknot/yparser/la-ypformat.lo `test -f 'libknot/yparser/ypformat.c' || echo '$(srcdir)/'`libknot/yparser/ypformat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/la-ypformat.Tpo libknot/yparser/$(DEPDIR)/la-ypformat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypformat.c' object='libknot/yparser/la-ypformat.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/la-ypformat.lo `test -f 'libknot/yparser/ypformat.c' || echo '$(srcdir)/'`libknot/yparser/ypformat.c
+
+libknot/yparser/la-ypschema.lo: libknot/yparser/ypschema.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/la-ypschema.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/la-ypschema.Tpo -c -o libknot/yparser/la-ypschema.lo `test -f 'libknot/yparser/ypschema.c' || echo '$(srcdir)/'`libknot/yparser/ypschema.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/la-ypschema.Tpo libknot/yparser/$(DEPDIR)/la-ypschema.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypschema.c' object='libknot/yparser/la-ypschema.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/la-ypschema.lo `test -f 'libknot/yparser/ypschema.c' || echo '$(srcdir)/'`libknot/yparser/ypschema.c
+
+libknot/yparser/la-yptrafo.lo: libknot/yparser/yptrafo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/la-yptrafo.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/la-yptrafo.Tpo -c -o libknot/yparser/la-yptrafo.lo `test -f 'libknot/yparser/yptrafo.c' || echo '$(srcdir)/'`libknot/yparser/yptrafo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/la-yptrafo.Tpo libknot/yparser/$(DEPDIR)/la-yptrafo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/yptrafo.c' object='libknot/yparser/la-yptrafo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/la-yptrafo.lo `test -f 'libknot/yparser/yptrafo.c' || echo '$(srcdir)/'`libknot/yparser/yptrafo.c
+
+libknot/xdp/la-tcp_iobuf.lo: libknot/xdp/tcp_iobuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-tcp_iobuf.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Tpo -c -o libknot/xdp/la-tcp_iobuf.lo `test -f 'libknot/xdp/tcp_iobuf.c' || echo '$(srcdir)/'`libknot/xdp/tcp_iobuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Tpo libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/tcp_iobuf.c' object='libknot/xdp/la-tcp_iobuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-tcp_iobuf.lo `test -f 'libknot/xdp/tcp_iobuf.c' || echo '$(srcdir)/'`libknot/xdp/tcp_iobuf.c
+
+libknot/xdp/la-bpf-kernel-obj.lo: libknot/xdp/bpf-kernel-obj.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-bpf-kernel-obj.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Tpo -c -o libknot/xdp/la-bpf-kernel-obj.lo `test -f 'libknot/xdp/bpf-kernel-obj.c' || echo '$(srcdir)/'`libknot/xdp/bpf-kernel-obj.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Tpo libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/bpf-kernel-obj.c' object='libknot/xdp/la-bpf-kernel-obj.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-bpf-kernel-obj.lo `test -f 'libknot/xdp/bpf-kernel-obj.c' || echo '$(srcdir)/'`libknot/xdp/bpf-kernel-obj.c
+
+libknot/xdp/la-bpf-user.lo: libknot/xdp/bpf-user.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-bpf-user.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-bpf-user.Tpo -c -o libknot/xdp/la-bpf-user.lo `test -f 'libknot/xdp/bpf-user.c' || echo '$(srcdir)/'`libknot/xdp/bpf-user.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-bpf-user.Tpo libknot/xdp/$(DEPDIR)/la-bpf-user.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/bpf-user.c' object='libknot/xdp/la-bpf-user.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-bpf-user.lo `test -f 'libknot/xdp/bpf-user.c' || echo '$(srcdir)/'`libknot/xdp/bpf-user.c
+
+libknot/xdp/la-eth.lo: libknot/xdp/eth.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-eth.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-eth.Tpo -c -o libknot/xdp/la-eth.lo `test -f 'libknot/xdp/eth.c' || echo '$(srcdir)/'`libknot/xdp/eth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-eth.Tpo libknot/xdp/$(DEPDIR)/la-eth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/eth.c' object='libknot/xdp/la-eth.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-eth.lo `test -f 'libknot/xdp/eth.c' || echo '$(srcdir)/'`libknot/xdp/eth.c
+
+libknot/xdp/la-tcp.lo: libknot/xdp/tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-tcp.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-tcp.Tpo -c -o libknot/xdp/la-tcp.lo `test -f 'libknot/xdp/tcp.c' || echo '$(srcdir)/'`libknot/xdp/tcp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-tcp.Tpo libknot/xdp/$(DEPDIR)/la-tcp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/tcp.c' object='libknot/xdp/la-tcp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-tcp.lo `test -f 'libknot/xdp/tcp.c' || echo '$(srcdir)/'`libknot/xdp/tcp.c
+
+libknot/xdp/la-xdp.lo: libknot/xdp/xdp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-xdp.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-xdp.Tpo -c -o libknot/xdp/la-xdp.lo `test -f 'libknot/xdp/xdp.c' || echo '$(srcdir)/'`libknot/xdp/xdp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-xdp.Tpo libknot/xdp/$(DEPDIR)/la-xdp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/xdp.c' object='libknot/xdp/la-xdp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-xdp.lo `test -f 'libknot/xdp/xdp.c' || echo '$(srcdir)/'`libknot/xdp/xdp.c
+
+libknot/xdp/la-quic.lo: libknot/xdp/quic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-quic.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-quic.Tpo -c -o libknot/xdp/la-quic.lo `test -f 'libknot/xdp/quic.c' || echo '$(srcdir)/'`libknot/xdp/quic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-quic.Tpo libknot/xdp/$(DEPDIR)/la-quic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/quic.c' object='libknot/xdp/la-quic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-quic.lo `test -f 'libknot/xdp/quic.c' || echo '$(srcdir)/'`libknot/xdp/quic.c
+
+libknot/xdp/la-quic_conn.lo: libknot/xdp/quic_conn.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/xdp/la-quic_conn.lo -MD -MP -MF libknot/xdp/$(DEPDIR)/la-quic_conn.Tpo -c -o libknot/xdp/la-quic_conn.lo `test -f 'libknot/xdp/quic_conn.c' || echo '$(srcdir)/'`libknot/xdp/quic_conn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/xdp/$(DEPDIR)/la-quic_conn.Tpo libknot/xdp/$(DEPDIR)/la-quic_conn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/xdp/quic_conn.c' object='libknot/xdp/la-quic_conn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/xdp/la-quic_conn.lo `test -f 'libknot/xdp/quic_conn.c' || echo '$(srcdir)/'`libknot/xdp/quic_conn.c
+
+knot/catalog/libknotd_la-catalog_db.lo: knot/catalog/catalog_db.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/catalog/libknotd_la-catalog_db.lo -MD -MP -MF knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Tpo -c -o knot/catalog/libknotd_la-catalog_db.lo `test -f 'knot/catalog/catalog_db.c' || echo '$(srcdir)/'`knot/catalog/catalog_db.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Tpo knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/catalog/catalog_db.c' object='knot/catalog/libknotd_la-catalog_db.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/catalog/libknotd_la-catalog_db.lo `test -f 'knot/catalog/catalog_db.c' || echo '$(srcdir)/'`knot/catalog/catalog_db.c
+
+knot/catalog/libknotd_la-catalog_update.lo: knot/catalog/catalog_update.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/catalog/libknotd_la-catalog_update.lo -MD -MP -MF knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Tpo -c -o knot/catalog/libknotd_la-catalog_update.lo `test -f 'knot/catalog/catalog_update.c' || echo '$(srcdir)/'`knot/catalog/catalog_update.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Tpo knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/catalog/catalog_update.c' object='knot/catalog/libknotd_la-catalog_update.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/catalog/libknotd_la-catalog_update.lo `test -f 'knot/catalog/catalog_update.c' || echo '$(srcdir)/'`knot/catalog/catalog_update.c
+
+knot/catalog/libknotd_la-generate.lo: knot/catalog/generate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/catalog/libknotd_la-generate.lo -MD -MP -MF knot/catalog/$(DEPDIR)/libknotd_la-generate.Tpo -c -o knot/catalog/libknotd_la-generate.lo `test -f 'knot/catalog/generate.c' || echo '$(srcdir)/'`knot/catalog/generate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/catalog/$(DEPDIR)/libknotd_la-generate.Tpo knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/catalog/generate.c' object='knot/catalog/libknotd_la-generate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/catalog/libknotd_la-generate.lo `test -f 'knot/catalog/generate.c' || echo '$(srcdir)/'`knot/catalog/generate.c
+
+knot/catalog/libknotd_la-interpret.lo: knot/catalog/interpret.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/catalog/libknotd_la-interpret.lo -MD -MP -MF knot/catalog/$(DEPDIR)/libknotd_la-interpret.Tpo -c -o knot/catalog/libknotd_la-interpret.lo `test -f 'knot/catalog/interpret.c' || echo '$(srcdir)/'`knot/catalog/interpret.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/catalog/$(DEPDIR)/libknotd_la-interpret.Tpo knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/catalog/interpret.c' object='knot/catalog/libknotd_la-interpret.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/catalog/libknotd_la-interpret.lo `test -f 'knot/catalog/interpret.c' || echo '$(srcdir)/'`knot/catalog/interpret.c
+
+knot/conf/libknotd_la-base.lo: knot/conf/base.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-base.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-base.Tpo -c -o knot/conf/libknotd_la-base.lo `test -f 'knot/conf/base.c' || echo '$(srcdir)/'`knot/conf/base.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-base.Tpo knot/conf/$(DEPDIR)/libknotd_la-base.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/base.c' object='knot/conf/libknotd_la-base.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-base.lo `test -f 'knot/conf/base.c' || echo '$(srcdir)/'`knot/conf/base.c
+
+knot/conf/libknotd_la-conf.lo: knot/conf/conf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-conf.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-conf.Tpo -c -o knot/conf/libknotd_la-conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-conf.Tpo knot/conf/$(DEPDIR)/libknotd_la-conf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/conf.c' object='knot/conf/libknotd_la-conf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c
+
+knot/conf/libknotd_la-confdb.lo: knot/conf/confdb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-confdb.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-confdb.Tpo -c -o knot/conf/libknotd_la-confdb.lo `test -f 'knot/conf/confdb.c' || echo '$(srcdir)/'`knot/conf/confdb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-confdb.Tpo knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/confdb.c' object='knot/conf/libknotd_la-confdb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-confdb.lo `test -f 'knot/conf/confdb.c' || echo '$(srcdir)/'`knot/conf/confdb.c
+
+knot/conf/libknotd_la-confio.lo: knot/conf/confio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-confio.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-confio.Tpo -c -o knot/conf/libknotd_la-confio.lo `test -f 'knot/conf/confio.c' || echo '$(srcdir)/'`knot/conf/confio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-confio.Tpo knot/conf/$(DEPDIR)/libknotd_la-confio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/confio.c' object='knot/conf/libknotd_la-confio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-confio.lo `test -f 'knot/conf/confio.c' || echo '$(srcdir)/'`knot/conf/confio.c
+
+knot/conf/libknotd_la-migration.lo: knot/conf/migration.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-migration.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-migration.Tpo -c -o knot/conf/libknotd_la-migration.lo `test -f 'knot/conf/migration.c' || echo '$(srcdir)/'`knot/conf/migration.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-migration.Tpo knot/conf/$(DEPDIR)/libknotd_la-migration.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/migration.c' object='knot/conf/libknotd_la-migration.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-migration.lo `test -f 'knot/conf/migration.c' || echo '$(srcdir)/'`knot/conf/migration.c
+
+knot/conf/libknotd_la-module.lo: knot/conf/module.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-module.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-module.Tpo -c -o knot/conf/libknotd_la-module.lo `test -f 'knot/conf/module.c' || echo '$(srcdir)/'`knot/conf/module.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-module.Tpo knot/conf/$(DEPDIR)/libknotd_la-module.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/module.c' object='knot/conf/libknotd_la-module.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-module.lo `test -f 'knot/conf/module.c' || echo '$(srcdir)/'`knot/conf/module.c
+
+knot/conf/libknotd_la-schema.lo: knot/conf/schema.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-schema.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-schema.Tpo -c -o knot/conf/libknotd_la-schema.lo `test -f 'knot/conf/schema.c' || echo '$(srcdir)/'`knot/conf/schema.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-schema.Tpo knot/conf/$(DEPDIR)/libknotd_la-schema.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/schema.c' object='knot/conf/libknotd_la-schema.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-schema.lo `test -f 'knot/conf/schema.c' || echo '$(srcdir)/'`knot/conf/schema.c
+
+knot/conf/libknotd_la-tools.lo: knot/conf/tools.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-tools.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-tools.Tpo -c -o knot/conf/libknotd_la-tools.lo `test -f 'knot/conf/tools.c' || echo '$(srcdir)/'`knot/conf/tools.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-tools.Tpo knot/conf/$(DEPDIR)/libknotd_la-tools.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/tools.c' object='knot/conf/libknotd_la-tools.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-tools.lo `test -f 'knot/conf/tools.c' || echo '$(srcdir)/'`knot/conf/tools.c
+
+knot/ctl/libknotd_la-commands.lo: knot/ctl/commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/ctl/libknotd_la-commands.lo -MD -MP -MF knot/ctl/$(DEPDIR)/libknotd_la-commands.Tpo -c -o knot/ctl/libknotd_la-commands.lo `test -f 'knot/ctl/commands.c' || echo '$(srcdir)/'`knot/ctl/commands.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/ctl/$(DEPDIR)/libknotd_la-commands.Tpo knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/ctl/commands.c' object='knot/ctl/libknotd_la-commands.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/ctl/libknotd_la-commands.lo `test -f 'knot/ctl/commands.c' || echo '$(srcdir)/'`knot/ctl/commands.c
+
+knot/ctl/libknotd_la-process.lo: knot/ctl/process.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/ctl/libknotd_la-process.lo -MD -MP -MF knot/ctl/$(DEPDIR)/libknotd_la-process.Tpo -c -o knot/ctl/libknotd_la-process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/ctl/$(DEPDIR)/libknotd_la-process.Tpo knot/ctl/$(DEPDIR)/libknotd_la-process.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/ctl/process.c' object='knot/ctl/libknotd_la-process.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/ctl/libknotd_la-process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c
+
+knot/dnssec/libknotd_la-context.lo: knot/dnssec/context.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-context.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-context.Tpo -c -o knot/dnssec/libknotd_la-context.lo `test -f 'knot/dnssec/context.c' || echo '$(srcdir)/'`knot/dnssec/context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-context.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/context.c' object='knot/dnssec/libknotd_la-context.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-context.lo `test -f 'knot/dnssec/context.c' || echo '$(srcdir)/'`knot/dnssec/context.c
+
+knot/dnssec/libknotd_la-ds_query.lo: knot/dnssec/ds_query.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-ds_query.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Tpo -c -o knot/dnssec/libknotd_la-ds_query.lo `test -f 'knot/dnssec/ds_query.c' || echo '$(srcdir)/'`knot/dnssec/ds_query.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/ds_query.c' object='knot/dnssec/libknotd_la-ds_query.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-ds_query.lo `test -f 'knot/dnssec/ds_query.c' || echo '$(srcdir)/'`knot/dnssec/ds_query.c
+
+knot/dnssec/kasp/libknotd_la-kasp_db.lo: knot/dnssec/kasp/kasp_db.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-kasp_db.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Tpo -c -o knot/dnssec/kasp/libknotd_la-kasp_db.lo `test -f 'knot/dnssec/kasp/kasp_db.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_db.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/kasp_db.c' object='knot/dnssec/kasp/libknotd_la-kasp_db.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-kasp_db.lo `test -f 'knot/dnssec/kasp/kasp_db.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_db.c
+
+knot/dnssec/kasp/libknotd_la-kasp_zone.lo: knot/dnssec/kasp/kasp_zone.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-kasp_zone.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Tpo -c -o knot/dnssec/kasp/libknotd_la-kasp_zone.lo `test -f 'knot/dnssec/kasp/kasp_zone.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_zone.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/kasp_zone.c' object='knot/dnssec/kasp/libknotd_la-kasp_zone.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-kasp_zone.lo `test -f 'knot/dnssec/kasp/kasp_zone.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_zone.c
+
+knot/dnssec/kasp/libknotd_la-keystate.lo: knot/dnssec/kasp/keystate.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-keystate.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Tpo -c -o knot/dnssec/kasp/libknotd_la-keystate.lo `test -f 'knot/dnssec/kasp/keystate.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/keystate.c' object='knot/dnssec/kasp/libknotd_la-keystate.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-keystate.lo `test -f 'knot/dnssec/kasp/keystate.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystate.c
+
+knot/dnssec/kasp/libknotd_la-keystore.lo: knot/dnssec/kasp/keystore.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-keystore.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Tpo -c -o knot/dnssec/kasp/libknotd_la-keystore.lo `test -f 'knot/dnssec/kasp/keystore.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystore.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/keystore.c' object='knot/dnssec/kasp/libknotd_la-keystore.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-keystore.lo `test -f 'knot/dnssec/kasp/keystore.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystore.c
+
+knot/dnssec/libknotd_la-key-events.lo: knot/dnssec/key-events.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-key-events.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Tpo -c -o knot/dnssec/libknotd_la-key-events.lo `test -f 'knot/dnssec/key-events.c' || echo '$(srcdir)/'`knot/dnssec/key-events.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/key-events.c' object='knot/dnssec/libknotd_la-key-events.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-key-events.lo `test -f 'knot/dnssec/key-events.c' || echo '$(srcdir)/'`knot/dnssec/key-events.c
+
+knot/dnssec/libknotd_la-key_records.lo: knot/dnssec/key_records.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-key_records.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Tpo -c -o knot/dnssec/libknotd_la-key_records.lo `test -f 'knot/dnssec/key_records.c' || echo '$(srcdir)/'`knot/dnssec/key_records.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/key_records.c' object='knot/dnssec/libknotd_la-key_records.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-key_records.lo `test -f 'knot/dnssec/key_records.c' || echo '$(srcdir)/'`knot/dnssec/key_records.c
+
+knot/dnssec/libknotd_la-nsec-chain.lo: knot/dnssec/nsec-chain.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-nsec-chain.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Tpo -c -o knot/dnssec/libknotd_la-nsec-chain.lo `test -f 'knot/dnssec/nsec-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec-chain.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/nsec-chain.c' object='knot/dnssec/libknotd_la-nsec-chain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-nsec-chain.lo `test -f 'knot/dnssec/nsec-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec-chain.c
+
+knot/dnssec/libknotd_la-nsec3-chain.lo: knot/dnssec/nsec3-chain.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-nsec3-chain.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Tpo -c -o knot/dnssec/libknotd_la-nsec3-chain.lo `test -f 'knot/dnssec/nsec3-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec3-chain.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/nsec3-chain.c' object='knot/dnssec/libknotd_la-nsec3-chain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-nsec3-chain.lo `test -f 'knot/dnssec/nsec3-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec3-chain.c
+
+knot/dnssec/libknotd_la-policy.lo: knot/dnssec/policy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-policy.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-policy.Tpo -c -o knot/dnssec/libknotd_la-policy.lo `test -f 'knot/dnssec/policy.c' || echo '$(srcdir)/'`knot/dnssec/policy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-policy.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/policy.c' object='knot/dnssec/libknotd_la-policy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-policy.lo `test -f 'knot/dnssec/policy.c' || echo '$(srcdir)/'`knot/dnssec/policy.c
+
+knot/dnssec/libknotd_la-rrset-sign.lo: knot/dnssec/rrset-sign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-rrset-sign.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Tpo -c -o knot/dnssec/libknotd_la-rrset-sign.lo `test -f 'knot/dnssec/rrset-sign.c' || echo '$(srcdir)/'`knot/dnssec/rrset-sign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/rrset-sign.c' object='knot/dnssec/libknotd_la-rrset-sign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-rrset-sign.lo `test -f 'knot/dnssec/rrset-sign.c' || echo '$(srcdir)/'`knot/dnssec/rrset-sign.c
+
+knot/dnssec/libknotd_la-zone-events.lo: knot/dnssec/zone-events.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-events.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Tpo -c -o knot/dnssec/libknotd_la-zone-events.lo `test -f 'knot/dnssec/zone-events.c' || echo '$(srcdir)/'`knot/dnssec/zone-events.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-events.c' object='knot/dnssec/libknotd_la-zone-events.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-events.lo `test -f 'knot/dnssec/zone-events.c' || echo '$(srcdir)/'`knot/dnssec/zone-events.c
+
+knot/dnssec/libknotd_la-zone-keys.lo: knot/dnssec/zone-keys.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-keys.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Tpo -c -o knot/dnssec/libknotd_la-zone-keys.lo `test -f 'knot/dnssec/zone-keys.c' || echo '$(srcdir)/'`knot/dnssec/zone-keys.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-keys.c' object='knot/dnssec/libknotd_la-zone-keys.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-keys.lo `test -f 'knot/dnssec/zone-keys.c' || echo '$(srcdir)/'`knot/dnssec/zone-keys.c
+
+knot/dnssec/libknotd_la-zone-nsec.lo: knot/dnssec/zone-nsec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-nsec.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Tpo -c -o knot/dnssec/libknotd_la-zone-nsec.lo `test -f 'knot/dnssec/zone-nsec.c' || echo '$(srcdir)/'`knot/dnssec/zone-nsec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-nsec.c' object='knot/dnssec/libknotd_la-zone-nsec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-nsec.lo `test -f 'knot/dnssec/zone-nsec.c' || echo '$(srcdir)/'`knot/dnssec/zone-nsec.c
+
+knot/dnssec/libknotd_la-zone-sign.lo: knot/dnssec/zone-sign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-sign.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Tpo -c -o knot/dnssec/libknotd_la-zone-sign.lo `test -f 'knot/dnssec/zone-sign.c' || echo '$(srcdir)/'`knot/dnssec/zone-sign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-sign.c' object='knot/dnssec/libknotd_la-zone-sign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-sign.lo `test -f 'knot/dnssec/zone-sign.c' || echo '$(srcdir)/'`knot/dnssec/zone-sign.c
+
+knot/events/libknotd_la-events.lo: knot/events/events.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/libknotd_la-events.lo -MD -MP -MF knot/events/$(DEPDIR)/libknotd_la-events.Tpo -c -o knot/events/libknotd_la-events.lo `test -f 'knot/events/events.c' || echo '$(srcdir)/'`knot/events/events.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/$(DEPDIR)/libknotd_la-events.Tpo knot/events/$(DEPDIR)/libknotd_la-events.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/events.c' object='knot/events/libknotd_la-events.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/libknotd_la-events.lo `test -f 'knot/events/events.c' || echo '$(srcdir)/'`knot/events/events.c
+
+knot/events/handlers/libknotd_la-backup.lo: knot/events/handlers/backup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-backup.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Tpo -c -o knot/events/handlers/libknotd_la-backup.lo `test -f 'knot/events/handlers/backup.c' || echo '$(srcdir)/'`knot/events/handlers/backup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/backup.c' object='knot/events/handlers/libknotd_la-backup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-backup.lo `test -f 'knot/events/handlers/backup.c' || echo '$(srcdir)/'`knot/events/handlers/backup.c
+
+knot/events/handlers/libknotd_la-dnssec.lo: knot/events/handlers/dnssec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-dnssec.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Tpo -c -o knot/events/handlers/libknotd_la-dnssec.lo `test -f 'knot/events/handlers/dnssec.c' || echo '$(srcdir)/'`knot/events/handlers/dnssec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/dnssec.c' object='knot/events/handlers/libknotd_la-dnssec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-dnssec.lo `test -f 'knot/events/handlers/dnssec.c' || echo '$(srcdir)/'`knot/events/handlers/dnssec.c
+
+knot/events/handlers/libknotd_la-ds_check.lo: knot/events/handlers/ds_check.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-ds_check.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Tpo -c -o knot/events/handlers/libknotd_la-ds_check.lo `test -f 'knot/events/handlers/ds_check.c' || echo '$(srcdir)/'`knot/events/handlers/ds_check.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/ds_check.c' object='knot/events/handlers/libknotd_la-ds_check.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-ds_check.lo `test -f 'knot/events/handlers/ds_check.c' || echo '$(srcdir)/'`knot/events/handlers/ds_check.c
+
+knot/events/handlers/libknotd_la-ds_push.lo: knot/events/handlers/ds_push.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-ds_push.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Tpo -c -o knot/events/handlers/libknotd_la-ds_push.lo `test -f 'knot/events/handlers/ds_push.c' || echo '$(srcdir)/'`knot/events/handlers/ds_push.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/ds_push.c' object='knot/events/handlers/libknotd_la-ds_push.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-ds_push.lo `test -f 'knot/events/handlers/ds_push.c' || echo '$(srcdir)/'`knot/events/handlers/ds_push.c
+
+knot/events/handlers/libknotd_la-expire.lo: knot/events/handlers/expire.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-expire.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Tpo -c -o knot/events/handlers/libknotd_la-expire.lo `test -f 'knot/events/handlers/expire.c' || echo '$(srcdir)/'`knot/events/handlers/expire.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/expire.c' object='knot/events/handlers/libknotd_la-expire.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-expire.lo `test -f 'knot/events/handlers/expire.c' || echo '$(srcdir)/'`knot/events/handlers/expire.c
+
+knot/events/handlers/libknotd_la-flush.lo: knot/events/handlers/flush.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-flush.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Tpo -c -o knot/events/handlers/libknotd_la-flush.lo `test -f 'knot/events/handlers/flush.c' || echo '$(srcdir)/'`knot/events/handlers/flush.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/flush.c' object='knot/events/handlers/libknotd_la-flush.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-flush.lo `test -f 'knot/events/handlers/flush.c' || echo '$(srcdir)/'`knot/events/handlers/flush.c
+
+knot/events/handlers/libknotd_la-freeze_thaw.lo: knot/events/handlers/freeze_thaw.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-freeze_thaw.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Tpo -c -o knot/events/handlers/libknotd_la-freeze_thaw.lo `test -f 'knot/events/handlers/freeze_thaw.c' || echo '$(srcdir)/'`knot/events/handlers/freeze_thaw.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/freeze_thaw.c' object='knot/events/handlers/libknotd_la-freeze_thaw.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-freeze_thaw.lo `test -f 'knot/events/handlers/freeze_thaw.c' || echo '$(srcdir)/'`knot/events/handlers/freeze_thaw.c
+
+knot/events/handlers/libknotd_la-load.lo: knot/events/handlers/load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-load.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-load.Tpo -c -o knot/events/handlers/libknotd_la-load.lo `test -f 'knot/events/handlers/load.c' || echo '$(srcdir)/'`knot/events/handlers/load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-load.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/load.c' object='knot/events/handlers/libknotd_la-load.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-load.lo `test -f 'knot/events/handlers/load.c' || echo '$(srcdir)/'`knot/events/handlers/load.c
+
+knot/events/handlers/libknotd_la-notify.lo: knot/events/handlers/notify.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-notify.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Tpo -c -o knot/events/handlers/libknotd_la-notify.lo `test -f 'knot/events/handlers/notify.c' || echo '$(srcdir)/'`knot/events/handlers/notify.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/notify.c' object='knot/events/handlers/libknotd_la-notify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-notify.lo `test -f 'knot/events/handlers/notify.c' || echo '$(srcdir)/'`knot/events/handlers/notify.c
+
+knot/events/handlers/libknotd_la-refresh.lo: knot/events/handlers/refresh.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-refresh.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Tpo -c -o knot/events/handlers/libknotd_la-refresh.lo `test -f 'knot/events/handlers/refresh.c' || echo '$(srcdir)/'`knot/events/handlers/refresh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/refresh.c' object='knot/events/handlers/libknotd_la-refresh.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-refresh.lo `test -f 'knot/events/handlers/refresh.c' || echo '$(srcdir)/'`knot/events/handlers/refresh.c
+
+knot/events/handlers/libknotd_la-update.lo: knot/events/handlers/update.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-update.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-update.Tpo -c -o knot/events/handlers/libknotd_la-update.lo `test -f 'knot/events/handlers/update.c' || echo '$(srcdir)/'`knot/events/handlers/update.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-update.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/update.c' object='knot/events/handlers/libknotd_la-update.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-update.lo `test -f 'knot/events/handlers/update.c' || echo '$(srcdir)/'`knot/events/handlers/update.c
+
+knot/events/libknotd_la-replan.lo: knot/events/replan.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/libknotd_la-replan.lo -MD -MP -MF knot/events/$(DEPDIR)/libknotd_la-replan.Tpo -c -o knot/events/libknotd_la-replan.lo `test -f 'knot/events/replan.c' || echo '$(srcdir)/'`knot/events/replan.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/$(DEPDIR)/libknotd_la-replan.Tpo knot/events/$(DEPDIR)/libknotd_la-replan.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/replan.c' object='knot/events/libknotd_la-replan.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/libknotd_la-replan.lo `test -f 'knot/events/replan.c' || echo '$(srcdir)/'`knot/events/replan.c
+
+knot/nameserver/libknotd_la-axfr.lo: knot/nameserver/axfr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-axfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Tpo -c -o knot/nameserver/libknotd_la-axfr.lo `test -f 'knot/nameserver/axfr.c' || echo '$(srcdir)/'`knot/nameserver/axfr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/axfr.c' object='knot/nameserver/libknotd_la-axfr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-axfr.lo `test -f 'knot/nameserver/axfr.c' || echo '$(srcdir)/'`knot/nameserver/axfr.c
+
+knot/nameserver/libknotd_la-chaos.lo: knot/nameserver/chaos.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-chaos.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Tpo -c -o knot/nameserver/libknotd_la-chaos.lo `test -f 'knot/nameserver/chaos.c' || echo '$(srcdir)/'`knot/nameserver/chaos.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/chaos.c' object='knot/nameserver/libknotd_la-chaos.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-chaos.lo `test -f 'knot/nameserver/chaos.c' || echo '$(srcdir)/'`knot/nameserver/chaos.c
+
+knot/nameserver/libknotd_la-internet.lo: knot/nameserver/internet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-internet.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-internet.Tpo -c -o knot/nameserver/libknotd_la-internet.lo `test -f 'knot/nameserver/internet.c' || echo '$(srcdir)/'`knot/nameserver/internet.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-internet.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/internet.c' object='knot/nameserver/libknotd_la-internet.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-internet.lo `test -f 'knot/nameserver/internet.c' || echo '$(srcdir)/'`knot/nameserver/internet.c
+
+knot/nameserver/libknotd_la-ixfr.lo: knot/nameserver/ixfr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-ixfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Tpo -c -o knot/nameserver/libknotd_la-ixfr.lo `test -f 'knot/nameserver/ixfr.c' || echo '$(srcdir)/'`knot/nameserver/ixfr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/ixfr.c' object='knot/nameserver/libknotd_la-ixfr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-ixfr.lo `test -f 'knot/nameserver/ixfr.c' || echo '$(srcdir)/'`knot/nameserver/ixfr.c
+
+knot/nameserver/libknotd_la-notify.lo: knot/nameserver/notify.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-notify.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-notify.Tpo -c -o knot/nameserver/libknotd_la-notify.lo `test -f 'knot/nameserver/notify.c' || echo '$(srcdir)/'`knot/nameserver/notify.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-notify.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/notify.c' object='knot/nameserver/libknotd_la-notify.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-notify.lo `test -f 'knot/nameserver/notify.c' || echo '$(srcdir)/'`knot/nameserver/notify.c
+
+knot/nameserver/libknotd_la-nsec_proofs.lo: knot/nameserver/nsec_proofs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-nsec_proofs.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Tpo -c -o knot/nameserver/libknotd_la-nsec_proofs.lo `test -f 'knot/nameserver/nsec_proofs.c' || echo '$(srcdir)/'`knot/nameserver/nsec_proofs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/nsec_proofs.c' object='knot/nameserver/libknotd_la-nsec_proofs.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-nsec_proofs.lo `test -f 'knot/nameserver/nsec_proofs.c' || echo '$(srcdir)/'`knot/nameserver/nsec_proofs.c
+
+knot/nameserver/libknotd_la-process_query.lo: knot/nameserver/process_query.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-process_query.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Tpo -c -o knot/nameserver/libknotd_la-process_query.lo `test -f 'knot/nameserver/process_query.c' || echo '$(srcdir)/'`knot/nameserver/process_query.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/process_query.c' object='knot/nameserver/libknotd_la-process_query.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-process_query.lo `test -f 'knot/nameserver/process_query.c' || echo '$(srcdir)/'`knot/nameserver/process_query.c
+
+knot/nameserver/libknotd_la-query_module.lo: knot/nameserver/query_module.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-query_module.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Tpo -c -o knot/nameserver/libknotd_la-query_module.lo `test -f 'knot/nameserver/query_module.c' || echo '$(srcdir)/'`knot/nameserver/query_module.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/query_module.c' object='knot/nameserver/libknotd_la-query_module.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-query_module.lo `test -f 'knot/nameserver/query_module.c' || echo '$(srcdir)/'`knot/nameserver/query_module.c
+
+knot/nameserver/libknotd_la-tsig_ctx.lo: knot/nameserver/tsig_ctx.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-tsig_ctx.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Tpo -c -o knot/nameserver/libknotd_la-tsig_ctx.lo `test -f 'knot/nameserver/tsig_ctx.c' || echo '$(srcdir)/'`knot/nameserver/tsig_ctx.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/tsig_ctx.c' object='knot/nameserver/libknotd_la-tsig_ctx.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-tsig_ctx.lo `test -f 'knot/nameserver/tsig_ctx.c' || echo '$(srcdir)/'`knot/nameserver/tsig_ctx.c
+
+knot/nameserver/libknotd_la-update.lo: knot/nameserver/update.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-update.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-update.Tpo -c -o knot/nameserver/libknotd_la-update.lo `test -f 'knot/nameserver/update.c' || echo '$(srcdir)/'`knot/nameserver/update.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-update.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/update.c' object='knot/nameserver/libknotd_la-update.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-update.lo `test -f 'knot/nameserver/update.c' || echo '$(srcdir)/'`knot/nameserver/update.c
+
+knot/nameserver/libknotd_la-xfr.lo: knot/nameserver/xfr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-xfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Tpo -c -o knot/nameserver/libknotd_la-xfr.lo `test -f 'knot/nameserver/xfr.c' || echo '$(srcdir)/'`knot/nameserver/xfr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/xfr.c' object='knot/nameserver/libknotd_la-xfr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-xfr.lo `test -f 'knot/nameserver/xfr.c' || echo '$(srcdir)/'`knot/nameserver/xfr.c
+
+knot/query/libknotd_la-capture.lo: knot/query/capture.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-capture.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-capture.Tpo -c -o knot/query/libknotd_la-capture.lo `test -f 'knot/query/capture.c' || echo '$(srcdir)/'`knot/query/capture.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-capture.Tpo knot/query/$(DEPDIR)/libknotd_la-capture.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/capture.c' object='knot/query/libknotd_la-capture.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-capture.lo `test -f 'knot/query/capture.c' || echo '$(srcdir)/'`knot/query/capture.c
+
+knot/query/libknotd_la-query.lo: knot/query/query.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-query.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-query.Tpo -c -o knot/query/libknotd_la-query.lo `test -f 'knot/query/query.c' || echo '$(srcdir)/'`knot/query/query.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-query.Tpo knot/query/$(DEPDIR)/libknotd_la-query.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/query.c' object='knot/query/libknotd_la-query.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-query.lo `test -f 'knot/query/query.c' || echo '$(srcdir)/'`knot/query/query.c
+
+knot/query/libknotd_la-requestor.lo: knot/query/requestor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-requestor.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-requestor.Tpo -c -o knot/query/libknotd_la-requestor.lo `test -f 'knot/query/requestor.c' || echo '$(srcdir)/'`knot/query/requestor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-requestor.Tpo knot/query/$(DEPDIR)/libknotd_la-requestor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/requestor.c' object='knot/query/libknotd_la-requestor.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-requestor.lo `test -f 'knot/query/requestor.c' || echo '$(srcdir)/'`knot/query/requestor.c
+
+knot/common/libknotd_la-evsched.lo: knot/common/evsched.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-evsched.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo -c -o knot/common/libknotd_la-evsched.lo `test -f 'knot/common/evsched.c' || echo '$(srcdir)/'`knot/common/evsched.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/evsched.c' object='knot/common/libknotd_la-evsched.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-evsched.lo `test -f 'knot/common/evsched.c' || echo '$(srcdir)/'`knot/common/evsched.c
+
+knot/common/libknotd_la-fdset.lo: knot/common/fdset.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-fdset.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-fdset.Tpo -c -o knot/common/libknotd_la-fdset.lo `test -f 'knot/common/fdset.c' || echo '$(srcdir)/'`knot/common/fdset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-fdset.Tpo knot/common/$(DEPDIR)/libknotd_la-fdset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/fdset.c' object='knot/common/libknotd_la-fdset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-fdset.lo `test -f 'knot/common/fdset.c' || echo '$(srcdir)/'`knot/common/fdset.c
+
+knot/common/libknotd_la-log.lo: knot/common/log.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-log.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-log.Tpo -c -o knot/common/libknotd_la-log.lo `test -f 'knot/common/log.c' || echo '$(srcdir)/'`knot/common/log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-log.Tpo knot/common/$(DEPDIR)/libknotd_la-log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/log.c' object='knot/common/libknotd_la-log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-log.lo `test -f 'knot/common/log.c' || echo '$(srcdir)/'`knot/common/log.c
+
+knot/common/libknotd_la-process.lo: knot/common/process.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-process.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-process.Tpo -c -o knot/common/libknotd_la-process.lo `test -f 'knot/common/process.c' || echo '$(srcdir)/'`knot/common/process.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-process.Tpo knot/common/$(DEPDIR)/libknotd_la-process.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/process.c' object='knot/common/libknotd_la-process.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-process.lo `test -f 'knot/common/process.c' || echo '$(srcdir)/'`knot/common/process.c
+
+knot/common/libknotd_la-stats.lo: knot/common/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-stats.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-stats.Tpo -c -o knot/common/libknotd_la-stats.lo `test -f 'knot/common/stats.c' || echo '$(srcdir)/'`knot/common/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-stats.Tpo knot/common/$(DEPDIR)/libknotd_la-stats.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/stats.c' object='knot/common/libknotd_la-stats.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-stats.lo `test -f 'knot/common/stats.c' || echo '$(srcdir)/'`knot/common/stats.c
+
+knot/common/libknotd_la-systemd.lo: knot/common/systemd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-systemd.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-systemd.Tpo -c -o knot/common/libknotd_la-systemd.lo `test -f 'knot/common/systemd.c' || echo '$(srcdir)/'`knot/common/systemd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-systemd.Tpo knot/common/$(DEPDIR)/libknotd_la-systemd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/systemd.c' object='knot/common/libknotd_la-systemd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-systemd.lo `test -f 'knot/common/systemd.c' || echo '$(srcdir)/'`knot/common/systemd.c
+
+knot/common/libknotd_la-unreachable.lo: knot/common/unreachable.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-unreachable.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-unreachable.Tpo -c -o knot/common/libknotd_la-unreachable.lo `test -f 'knot/common/unreachable.c' || echo '$(srcdir)/'`knot/common/unreachable.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-unreachable.Tpo knot/common/$(DEPDIR)/libknotd_la-unreachable.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/unreachable.c' object='knot/common/libknotd_la-unreachable.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-unreachable.lo `test -f 'knot/common/unreachable.c' || echo '$(srcdir)/'`knot/common/unreachable.c
+
+knot/journal/libknotd_la-journal_basic.lo: knot/journal/journal_basic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-journal_basic.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Tpo -c -o knot/journal/libknotd_la-journal_basic.lo `test -f 'knot/journal/journal_basic.c' || echo '$(srcdir)/'`knot/journal/journal_basic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Tpo knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/journal_basic.c' object='knot/journal/libknotd_la-journal_basic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-journal_basic.lo `test -f 'knot/journal/journal_basic.c' || echo '$(srcdir)/'`knot/journal/journal_basic.c
+
+knot/journal/libknotd_la-journal_metadata.lo: knot/journal/journal_metadata.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-journal_metadata.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Tpo -c -o knot/journal/libknotd_la-journal_metadata.lo `test -f 'knot/journal/journal_metadata.c' || echo '$(srcdir)/'`knot/journal/journal_metadata.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Tpo knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/journal_metadata.c' object='knot/journal/libknotd_la-journal_metadata.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-journal_metadata.lo `test -f 'knot/journal/journal_metadata.c' || echo '$(srcdir)/'`knot/journal/journal_metadata.c
+
+knot/journal/libknotd_la-journal_read.lo: knot/journal/journal_read.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-journal_read.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-journal_read.Tpo -c -o knot/journal/libknotd_la-journal_read.lo `test -f 'knot/journal/journal_read.c' || echo '$(srcdir)/'`knot/journal/journal_read.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-journal_read.Tpo knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/journal_read.c' object='knot/journal/libknotd_la-journal_read.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-journal_read.lo `test -f 'knot/journal/journal_read.c' || echo '$(srcdir)/'`knot/journal/journal_read.c
+
+knot/journal/libknotd_la-journal_write.lo: knot/journal/journal_write.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-journal_write.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-journal_write.Tpo -c -o knot/journal/libknotd_la-journal_write.lo `test -f 'knot/journal/journal_write.c' || echo '$(srcdir)/'`knot/journal/journal_write.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-journal_write.Tpo knot/journal/$(DEPDIR)/libknotd_la-journal_write.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/journal_write.c' object='knot/journal/libknotd_la-journal_write.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-journal_write.lo `test -f 'knot/journal/journal_write.c' || echo '$(srcdir)/'`knot/journal/journal_write.c
+
+knot/journal/libknotd_la-knot_lmdb.lo: knot/journal/knot_lmdb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-knot_lmdb.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Tpo -c -o knot/journal/libknotd_la-knot_lmdb.lo `test -f 'knot/journal/knot_lmdb.c' || echo '$(srcdir)/'`knot/journal/knot_lmdb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Tpo knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/knot_lmdb.c' object='knot/journal/libknotd_la-knot_lmdb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-knot_lmdb.lo `test -f 'knot/journal/knot_lmdb.c' || echo '$(srcdir)/'`knot/journal/knot_lmdb.c
+
+knot/journal/libknotd_la-serialization.lo: knot/journal/serialization.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-serialization.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-serialization.Tpo -c -o knot/journal/libknotd_la-serialization.lo `test -f 'knot/journal/serialization.c' || echo '$(srcdir)/'`knot/journal/serialization.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-serialization.Tpo knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/serialization.c' object='knot/journal/libknotd_la-serialization.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-serialization.lo `test -f 'knot/journal/serialization.c' || echo '$(srcdir)/'`knot/journal/serialization.c
+
+knot/server/libknotd_la-dthreads.lo: knot/server/dthreads.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-dthreads.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-dthreads.Tpo -c -o knot/server/libknotd_la-dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-dthreads.Tpo knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/dthreads.c' object='knot/server/libknotd_la-dthreads.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c
+
+knot/server/libknotd_la-proxyv2.lo: knot/server/proxyv2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-proxyv2.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-proxyv2.Tpo -c -o knot/server/libknotd_la-proxyv2.lo `test -f 'knot/server/proxyv2.c' || echo '$(srcdir)/'`knot/server/proxyv2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-proxyv2.Tpo knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/proxyv2.c' object='knot/server/libknotd_la-proxyv2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-proxyv2.lo `test -f 'knot/server/proxyv2.c' || echo '$(srcdir)/'`knot/server/proxyv2.c
+
+knot/server/libknotd_la-server.lo: knot/server/server.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-server.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-server.Tpo -c -o knot/server/libknotd_la-server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-server.Tpo knot/server/$(DEPDIR)/libknotd_la-server.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/server.c' object='knot/server/libknotd_la-server.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c
+
+knot/server/libknotd_la-tcp-handler.lo: knot/server/tcp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-tcp-handler.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Tpo -c -o knot/server/libknotd_la-tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Tpo knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/tcp-handler.c' object='knot/server/libknotd_la-tcp-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c
+
+knot/server/libknotd_la-udp-handler.lo: knot/server/udp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-udp-handler.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-udp-handler.Tpo -c -o knot/server/libknotd_la-udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-udp-handler.Tpo knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/udp-handler.c' object='knot/server/libknotd_la-udp-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c
+
+knot/server/libknotd_la-xdp-handler.lo: knot/server/xdp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-xdp-handler.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Tpo -c -o knot/server/libknotd_la-xdp-handler.lo `test -f 'knot/server/xdp-handler.c' || echo '$(srcdir)/'`knot/server/xdp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Tpo knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/xdp-handler.c' object='knot/server/libknotd_la-xdp-handler.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-xdp-handler.lo `test -f 'knot/server/xdp-handler.c' || echo '$(srcdir)/'`knot/server/xdp-handler.c
+
+knot/updates/libknotd_la-acl.lo: knot/updates/acl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-acl.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-acl.Tpo -c -o knot/updates/libknotd_la-acl.lo `test -f 'knot/updates/acl.c' || echo '$(srcdir)/'`knot/updates/acl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-acl.Tpo knot/updates/$(DEPDIR)/libknotd_la-acl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/acl.c' object='knot/updates/libknotd_la-acl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-acl.lo `test -f 'knot/updates/acl.c' || echo '$(srcdir)/'`knot/updates/acl.c
+
+knot/updates/libknotd_la-apply.lo: knot/updates/apply.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-apply.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-apply.Tpo -c -o knot/updates/libknotd_la-apply.lo `test -f 'knot/updates/apply.c' || echo '$(srcdir)/'`knot/updates/apply.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-apply.Tpo knot/updates/$(DEPDIR)/libknotd_la-apply.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/apply.c' object='knot/updates/libknotd_la-apply.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-apply.lo `test -f 'knot/updates/apply.c' || echo '$(srcdir)/'`knot/updates/apply.c
+
+knot/updates/libknotd_la-changesets.lo: knot/updates/changesets.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-changesets.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-changesets.Tpo -c -o knot/updates/libknotd_la-changesets.lo `test -f 'knot/updates/changesets.c' || echo '$(srcdir)/'`knot/updates/changesets.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-changesets.Tpo knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/changesets.c' object='knot/updates/libknotd_la-changesets.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-changesets.lo `test -f 'knot/updates/changesets.c' || echo '$(srcdir)/'`knot/updates/changesets.c
+
+knot/updates/libknotd_la-ddns.lo: knot/updates/ddns.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-ddns.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-ddns.Tpo -c -o knot/updates/libknotd_la-ddns.lo `test -f 'knot/updates/ddns.c' || echo '$(srcdir)/'`knot/updates/ddns.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-ddns.Tpo knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/ddns.c' object='knot/updates/libknotd_la-ddns.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-ddns.lo `test -f 'knot/updates/ddns.c' || echo '$(srcdir)/'`knot/updates/ddns.c
+
+knot/updates/libknotd_la-zone-update.lo: knot/updates/zone-update.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-zone-update.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-zone-update.Tpo -c -o knot/updates/libknotd_la-zone-update.lo `test -f 'knot/updates/zone-update.c' || echo '$(srcdir)/'`knot/updates/zone-update.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-zone-update.Tpo knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/zone-update.c' object='knot/updates/libknotd_la-zone-update.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-zone-update.lo `test -f 'knot/updates/zone-update.c' || echo '$(srcdir)/'`knot/updates/zone-update.c
+
+knot/worker/libknotd_la-pool.lo: knot/worker/pool.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/worker/libknotd_la-pool.lo -MD -MP -MF knot/worker/$(DEPDIR)/libknotd_la-pool.Tpo -c -o knot/worker/libknotd_la-pool.lo `test -f 'knot/worker/pool.c' || echo '$(srcdir)/'`knot/worker/pool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/worker/$(DEPDIR)/libknotd_la-pool.Tpo knot/worker/$(DEPDIR)/libknotd_la-pool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/worker/pool.c' object='knot/worker/libknotd_la-pool.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/worker/libknotd_la-pool.lo `test -f 'knot/worker/pool.c' || echo '$(srcdir)/'`knot/worker/pool.c
+
+knot/worker/libknotd_la-queue.lo: knot/worker/queue.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/worker/libknotd_la-queue.lo -MD -MP -MF knot/worker/$(DEPDIR)/libknotd_la-queue.Tpo -c -o knot/worker/libknotd_la-queue.lo `test -f 'knot/worker/queue.c' || echo '$(srcdir)/'`knot/worker/queue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/worker/$(DEPDIR)/libknotd_la-queue.Tpo knot/worker/$(DEPDIR)/libknotd_la-queue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/worker/queue.c' object='knot/worker/libknotd_la-queue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/worker/libknotd_la-queue.lo `test -f 'knot/worker/queue.c' || echo '$(srcdir)/'`knot/worker/queue.c
+
+knot/zone/libknotd_la-adds_tree.lo: knot/zone/adds_tree.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-adds_tree.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Tpo -c -o knot/zone/libknotd_la-adds_tree.lo `test -f 'knot/zone/adds_tree.c' || echo '$(srcdir)/'`knot/zone/adds_tree.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Tpo knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/adds_tree.c' object='knot/zone/libknotd_la-adds_tree.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-adds_tree.lo `test -f 'knot/zone/adds_tree.c' || echo '$(srcdir)/'`knot/zone/adds_tree.c
+
+knot/zone/libknotd_la-adjust.lo: knot/zone/adjust.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-adjust.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-adjust.Tpo -c -o knot/zone/libknotd_la-adjust.lo `test -f 'knot/zone/adjust.c' || echo '$(srcdir)/'`knot/zone/adjust.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-adjust.Tpo knot/zone/$(DEPDIR)/libknotd_la-adjust.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/adjust.c' object='knot/zone/libknotd_la-adjust.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-adjust.lo `test -f 'knot/zone/adjust.c' || echo '$(srcdir)/'`knot/zone/adjust.c
+
+knot/zone/libknotd_la-backup.lo: knot/zone/backup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-backup.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-backup.Tpo -c -o knot/zone/libknotd_la-backup.lo `test -f 'knot/zone/backup.c' || echo '$(srcdir)/'`knot/zone/backup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-backup.Tpo knot/zone/$(DEPDIR)/libknotd_la-backup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/backup.c' object='knot/zone/libknotd_la-backup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-backup.lo `test -f 'knot/zone/backup.c' || echo '$(srcdir)/'`knot/zone/backup.c
+
+knot/zone/libknotd_la-backup_dir.lo: knot/zone/backup_dir.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-backup_dir.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Tpo -c -o knot/zone/libknotd_la-backup_dir.lo `test -f 'knot/zone/backup_dir.c' || echo '$(srcdir)/'`knot/zone/backup_dir.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Tpo knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/backup_dir.c' object='knot/zone/libknotd_la-backup_dir.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-backup_dir.lo `test -f 'knot/zone/backup_dir.c' || echo '$(srcdir)/'`knot/zone/backup_dir.c
+
+knot/zone/libknotd_la-contents.lo: knot/zone/contents.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-contents.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-contents.Tpo -c -o knot/zone/libknotd_la-contents.lo `test -f 'knot/zone/contents.c' || echo '$(srcdir)/'`knot/zone/contents.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-contents.Tpo knot/zone/$(DEPDIR)/libknotd_la-contents.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/contents.c' object='knot/zone/libknotd_la-contents.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-contents.lo `test -f 'knot/zone/contents.c' || echo '$(srcdir)/'`knot/zone/contents.c
+
+knot/zone/libknotd_la-digest.lo: knot/zone/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-digest.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-digest.Tpo -c -o knot/zone/libknotd_la-digest.lo `test -f 'knot/zone/digest.c' || echo '$(srcdir)/'`knot/zone/digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-digest.Tpo knot/zone/$(DEPDIR)/libknotd_la-digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/digest.c' object='knot/zone/libknotd_la-digest.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-digest.lo `test -f 'knot/zone/digest.c' || echo '$(srcdir)/'`knot/zone/digest.c
+
+knot/zone/libknotd_la-measure.lo: knot/zone/measure.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-measure.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-measure.Tpo -c -o knot/zone/libknotd_la-measure.lo `test -f 'knot/zone/measure.c' || echo '$(srcdir)/'`knot/zone/measure.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-measure.Tpo knot/zone/$(DEPDIR)/libknotd_la-measure.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/measure.c' object='knot/zone/libknotd_la-measure.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-measure.lo `test -f 'knot/zone/measure.c' || echo '$(srcdir)/'`knot/zone/measure.c
+
+knot/zone/libknotd_la-node.lo: knot/zone/node.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-node.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-node.Tpo -c -o knot/zone/libknotd_la-node.lo `test -f 'knot/zone/node.c' || echo '$(srcdir)/'`knot/zone/node.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-node.Tpo knot/zone/$(DEPDIR)/libknotd_la-node.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/node.c' object='knot/zone/libknotd_la-node.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-node.lo `test -f 'knot/zone/node.c' || echo '$(srcdir)/'`knot/zone/node.c
+
+knot/zone/libknotd_la-semantic-check.lo: knot/zone/semantic-check.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-semantic-check.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Tpo -c -o knot/zone/libknotd_la-semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Tpo knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/semantic-check.c' object='knot/zone/libknotd_la-semantic-check.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c
+
+knot/zone/libknotd_la-serial.lo: knot/zone/serial.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-serial.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-serial.Tpo -c -o knot/zone/libknotd_la-serial.lo `test -f 'knot/zone/serial.c' || echo '$(srcdir)/'`knot/zone/serial.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-serial.Tpo knot/zone/$(DEPDIR)/libknotd_la-serial.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/serial.c' object='knot/zone/libknotd_la-serial.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-serial.lo `test -f 'knot/zone/serial.c' || echo '$(srcdir)/'`knot/zone/serial.c
+
+knot/zone/libknotd_la-timers.lo: knot/zone/timers.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-timers.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-timers.Tpo -c -o knot/zone/libknotd_la-timers.lo `test -f 'knot/zone/timers.c' || echo '$(srcdir)/'`knot/zone/timers.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-timers.Tpo knot/zone/$(DEPDIR)/libknotd_la-timers.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/timers.c' object='knot/zone/libknotd_la-timers.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-timers.lo `test -f 'knot/zone/timers.c' || echo '$(srcdir)/'`knot/zone/timers.c
+
+knot/zone/libknotd_la-zone-diff.lo: knot/zone/zone-diff.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-diff.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Tpo -c -o knot/zone/libknotd_la-zone-diff.lo `test -f 'knot/zone/zone-diff.c' || echo '$(srcdir)/'`knot/zone/zone-diff.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-diff.c' object='knot/zone/libknotd_la-zone-diff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-diff.lo `test -f 'knot/zone/zone-diff.c' || echo '$(srcdir)/'`knot/zone/zone-diff.c
+
+knot/zone/libknotd_la-zone-dump.lo: knot/zone/zone-dump.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-dump.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Tpo -c -o knot/zone/libknotd_la-zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-dump.c' object='knot/zone/libknotd_la-zone-dump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c
+
+knot/zone/libknotd_la-zone-load.lo: knot/zone/zone-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-load.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-load.Tpo -c -o knot/zone/libknotd_la-zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-load.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-load.c' object='knot/zone/libknotd_la-zone-load.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c
+
+knot/zone/libknotd_la-zone-tree.lo: knot/zone/zone-tree.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-tree.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Tpo -c -o knot/zone/libknotd_la-zone-tree.lo `test -f 'knot/zone/zone-tree.c' || echo '$(srcdir)/'`knot/zone/zone-tree.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-tree.c' object='knot/zone/libknotd_la-zone-tree.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-tree.lo `test -f 'knot/zone/zone-tree.c' || echo '$(srcdir)/'`knot/zone/zone-tree.c
+
+knot/zone/libknotd_la-zone.lo: knot/zone/zone.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone.Tpo -c -o knot/zone/libknotd_la-zone.lo `test -f 'knot/zone/zone.c' || echo '$(srcdir)/'`knot/zone/zone.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone.c' object='knot/zone/libknotd_la-zone.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone.lo `test -f 'knot/zone/zone.c' || echo '$(srcdir)/'`knot/zone/zone.c
+
+knot/zone/libknotd_la-zonedb-load.lo: knot/zone/zonedb-load.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonedb-load.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Tpo -c -o knot/zone/libknotd_la-zonedb-load.lo `test -f 'knot/zone/zonedb-load.c' || echo '$(srcdir)/'`knot/zone/zonedb-load.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonedb-load.c' object='knot/zone/libknotd_la-zonedb-load.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonedb-load.lo `test -f 'knot/zone/zonedb-load.c' || echo '$(srcdir)/'`knot/zone/zonedb-load.c
+
+knot/zone/libknotd_la-zonedb.lo: knot/zone/zonedb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonedb.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonedb.Tpo -c -o knot/zone/libknotd_la-zonedb.lo `test -f 'knot/zone/zonedb.c' || echo '$(srcdir)/'`knot/zone/zonedb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonedb.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonedb.c' object='knot/zone/libknotd_la-zonedb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonedb.lo `test -f 'knot/zone/zonedb.c' || echo '$(srcdir)/'`knot/zone/zonedb.c
+
+knot/zone/libknotd_la-zonefile.lo: knot/zone/zonefile.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonefile.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonefile.Tpo -c -o knot/zone/libknotd_la-zonefile.lo `test -f 'knot/zone/zonefile.c' || echo '$(srcdir)/'`knot/zone/zonefile.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonefile.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonefile.c' object='knot/zone/libknotd_la-zonefile.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonefile.lo `test -f 'knot/zone/zonefile.c' || echo '$(srcdir)/'`knot/zone/zonefile.c
+
+knot/modules/cookies/libknotd_la-cookies.lo: knot/modules/cookies/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/cookies/libknotd_la-cookies.lo -MD -MP -MF knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Tpo -c -o knot/modules/cookies/libknotd_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Tpo knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/cookies/cookies.c' object='knot/modules/cookies/libknotd_la-cookies.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/cookies/libknotd_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c
+
+knot/modules/dnsproxy/libknotd_la-dnsproxy.lo: knot/modules/dnsproxy/dnsproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnsproxy/libknotd_la-dnsproxy.lo -MD -MP -MF knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Tpo -c -o knot/modules/dnsproxy/libknotd_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Tpo knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnsproxy/dnsproxy.c' object='knot/modules/dnsproxy/libknotd_la-dnsproxy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnsproxy/libknotd_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c
+
+knot/modules/dnstap/libknotd_la-dnstap.lo: knot/modules/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnstap/libknotd_la-dnstap.lo -MD -MP -MF knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Tpo -c -o knot/modules/dnstap/libknotd_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Tpo knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnstap/dnstap.c' object='knot/modules/dnstap/libknotd_la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnstap/libknotd_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c
+
+knot/modules/geoip/libknotd_la-geoip.lo: knot/modules/geoip/geoip.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/libknotd_la-geoip.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Tpo -c -o knot/modules/geoip/libknotd_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Tpo knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geoip.c' object='knot/modules/geoip/libknotd_la-geoip.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/libknotd_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c
+
+knot/modules/geoip/libknotd_la-geodb.lo: knot/modules/geoip/geodb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/libknotd_la-geodb.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Tpo -c -o knot/modules/geoip/libknotd_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Tpo knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geodb.c' object='knot/modules/geoip/libknotd_la-geodb.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/libknotd_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c
+
+knot/modules/noudp/libknotd_la-noudp.lo: knot/modules/noudp/noudp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/noudp/libknotd_la-noudp.lo -MD -MP -MF knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Tpo -c -o knot/modules/noudp/libknotd_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Tpo knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/noudp/noudp.c' object='knot/modules/noudp/libknotd_la-noudp.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/noudp/libknotd_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c
+
+knot/modules/onlinesign/libknotd_la-onlinesign.lo: knot/modules/onlinesign/onlinesign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/libknotd_la-onlinesign.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Tpo -c -o knot/modules/onlinesign/libknotd_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Tpo knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/onlinesign.c' object='knot/modules/onlinesign/libknotd_la-onlinesign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/libknotd_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c
+
+knot/modules/onlinesign/libknotd_la-nsec_next.lo: knot/modules/onlinesign/nsec_next.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/libknotd_la-nsec_next.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Tpo -c -o knot/modules/onlinesign/libknotd_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Tpo knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/nsec_next.c' object='knot/modules/onlinesign/libknotd_la-nsec_next.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/libknotd_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c
+
+knot/modules/probe/libknotd_la-probe.lo: knot/modules/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/probe/libknotd_la-probe.lo -MD -MP -MF knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Tpo -c -o knot/modules/probe/libknotd_la-probe.lo `test -f 'knot/modules/probe/probe.c' || echo '$(srcdir)/'`knot/modules/probe/probe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Tpo knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/probe/probe.c' object='knot/modules/probe/libknotd_la-probe.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/probe/libknotd_la-probe.lo `test -f 'knot/modules/probe/probe.c' || echo '$(srcdir)/'`knot/modules/probe/probe.c
+
+knot/modules/queryacl/libknotd_la-queryacl.lo: knot/modules/queryacl/queryacl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/queryacl/libknotd_la-queryacl.lo -MD -MP -MF knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Tpo -c -o knot/modules/queryacl/libknotd_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Tpo knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/queryacl/queryacl.c' object='knot/modules/queryacl/libknotd_la-queryacl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/queryacl/libknotd_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c
+
+knot/modules/rrl/libknotd_la-rrl.lo: knot/modules/rrl/rrl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-rrl.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Tpo -c -o knot/modules/rrl/libknotd_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/rrl.c' object='knot/modules/rrl/libknotd_la-rrl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c
+
+knot/modules/rrl/libknotd_la-functions.lo: knot/modules/rrl/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-functions.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Tpo -c -o knot/modules/rrl/libknotd_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/functions.c' object='knot/modules/rrl/libknotd_la-functions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c
+
+knot/modules/stats/libknotd_la-stats.lo: knot/modules/stats/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/libknotd_la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo -c -o knot/modules/stats/libknotd_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/stats/stats.c' object='knot/modules/stats/libknotd_la-stats.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/stats/libknotd_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c
+
+knot/modules/synthrecord/libknotd_la-synthrecord.lo: knot/modules/synthrecord/synthrecord.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/synthrecord/libknotd_la-synthrecord.lo -MD -MP -MF knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Tpo -c -o knot/modules/synthrecord/libknotd_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Tpo knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/synthrecord/synthrecord.c' object='knot/modules/synthrecord/libknotd_la-synthrecord.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/synthrecord/libknotd_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c
+
+knot/modules/whoami/libknotd_la-whoami.lo: knot/modules/whoami/whoami.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/whoami/libknotd_la-whoami.lo -MD -MP -MF knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Tpo -c -o knot/modules/whoami/libknotd_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Tpo knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/whoami/whoami.c' object='knot/modules/whoami/libknotd_la-whoami.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/whoami/libknotd_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c
+
+utils/common/libknotus_la-cert.lo: utils/common/cert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-cert.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-cert.Tpo -c -o utils/common/libknotus_la-cert.lo `test -f 'utils/common/cert.c' || echo '$(srcdir)/'`utils/common/cert.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-cert.Tpo utils/common/$(DEPDIR)/libknotus_la-cert.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/cert.c' object='utils/common/libknotus_la-cert.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-cert.lo `test -f 'utils/common/cert.c' || echo '$(srcdir)/'`utils/common/cert.c
+
+utils/common/libknotus_la-exec.lo: utils/common/exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-exec.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-exec.Tpo -c -o utils/common/libknotus_la-exec.lo `test -f 'utils/common/exec.c' || echo '$(srcdir)/'`utils/common/exec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-exec.Tpo utils/common/$(DEPDIR)/libknotus_la-exec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/exec.c' object='utils/common/libknotus_la-exec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-exec.lo `test -f 'utils/common/exec.c' || echo '$(srcdir)/'`utils/common/exec.c
+
+utils/common/libknotus_la-hex.lo: utils/common/hex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-hex.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-hex.Tpo -c -o utils/common/libknotus_la-hex.lo `test -f 'utils/common/hex.c' || echo '$(srcdir)/'`utils/common/hex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-hex.Tpo utils/common/$(DEPDIR)/libknotus_la-hex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/hex.c' object='utils/common/libknotus_la-hex.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-hex.lo `test -f 'utils/common/hex.c' || echo '$(srcdir)/'`utils/common/hex.c
+
+utils/common/libknotus_la-https.lo: utils/common/https.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-https.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-https.Tpo -c -o utils/common/libknotus_la-https.lo `test -f 'utils/common/https.c' || echo '$(srcdir)/'`utils/common/https.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-https.Tpo utils/common/$(DEPDIR)/libknotus_la-https.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/https.c' object='utils/common/libknotus_la-https.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-https.lo `test -f 'utils/common/https.c' || echo '$(srcdir)/'`utils/common/https.c
+
+utils/common/libknotus_la-lookup.lo: utils/common/lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-lookup.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-lookup.Tpo -c -o utils/common/libknotus_la-lookup.lo `test -f 'utils/common/lookup.c' || echo '$(srcdir)/'`utils/common/lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-lookup.Tpo utils/common/$(DEPDIR)/libknotus_la-lookup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/lookup.c' object='utils/common/libknotus_la-lookup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-lookup.lo `test -f 'utils/common/lookup.c' || echo '$(srcdir)/'`utils/common/lookup.c
+
+utils/common/libknotus_la-msg.lo: utils/common/msg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-msg.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-msg.Tpo -c -o utils/common/libknotus_la-msg.lo `test -f 'utils/common/msg.c' || echo '$(srcdir)/'`utils/common/msg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-msg.Tpo utils/common/$(DEPDIR)/libknotus_la-msg.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/msg.c' object='utils/common/libknotus_la-msg.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-msg.lo `test -f 'utils/common/msg.c' || echo '$(srcdir)/'`utils/common/msg.c
+
+utils/common/libknotus_la-netio.lo: utils/common/netio.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-netio.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-netio.Tpo -c -o utils/common/libknotus_la-netio.lo `test -f 'utils/common/netio.c' || echo '$(srcdir)/'`utils/common/netio.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-netio.Tpo utils/common/$(DEPDIR)/libknotus_la-netio.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/netio.c' object='utils/common/libknotus_la-netio.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-netio.lo `test -f 'utils/common/netio.c' || echo '$(srcdir)/'`utils/common/netio.c
+
+utils/common/libknotus_la-params.lo: utils/common/params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-params.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-params.Tpo -c -o utils/common/libknotus_la-params.lo `test -f 'utils/common/params.c' || echo '$(srcdir)/'`utils/common/params.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-params.Tpo utils/common/$(DEPDIR)/libknotus_la-params.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/params.c' object='utils/common/libknotus_la-params.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-params.lo `test -f 'utils/common/params.c' || echo '$(srcdir)/'`utils/common/params.c
+
+utils/common/libknotus_la-quic.lo: utils/common/quic.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-quic.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-quic.Tpo -c -o utils/common/libknotus_la-quic.lo `test -f 'utils/common/quic.c' || echo '$(srcdir)/'`utils/common/quic.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-quic.Tpo utils/common/$(DEPDIR)/libknotus_la-quic.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/quic.c' object='utils/common/libknotus_la-quic.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-quic.lo `test -f 'utils/common/quic.c' || echo '$(srcdir)/'`utils/common/quic.c
+
+utils/common/libknotus_la-resolv.lo: utils/common/resolv.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-resolv.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-resolv.Tpo -c -o utils/common/libknotus_la-resolv.lo `test -f 'utils/common/resolv.c' || echo '$(srcdir)/'`utils/common/resolv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-resolv.Tpo utils/common/$(DEPDIR)/libknotus_la-resolv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/resolv.c' object='utils/common/libknotus_la-resolv.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-resolv.lo `test -f 'utils/common/resolv.c' || echo '$(srcdir)/'`utils/common/resolv.c
+
+utils/common/libknotus_la-sign.lo: utils/common/sign.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-sign.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-sign.Tpo -c -o utils/common/libknotus_la-sign.lo `test -f 'utils/common/sign.c' || echo '$(srcdir)/'`utils/common/sign.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-sign.Tpo utils/common/$(DEPDIR)/libknotus_la-sign.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/sign.c' object='utils/common/libknotus_la-sign.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-sign.lo `test -f 'utils/common/sign.c' || echo '$(srcdir)/'`utils/common/sign.c
+
+utils/common/libknotus_la-tls.lo: utils/common/tls.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-tls.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-tls.Tpo -c -o utils/common/libknotus_la-tls.lo `test -f 'utils/common/tls.c' || echo '$(srcdir)/'`utils/common/tls.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-tls.Tpo utils/common/$(DEPDIR)/libknotus_la-tls.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/tls.c' object='utils/common/libknotus_la-tls.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-tls.lo `test -f 'utils/common/tls.c' || echo '$(srcdir)/'`utils/common/tls.c
+
+utils/common/libknotus_la-token.lo: utils/common/token.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-token.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-token.Tpo -c -o utils/common/libknotus_la-token.lo `test -f 'utils/common/token.c' || echo '$(srcdir)/'`utils/common/token.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-token.Tpo utils/common/$(DEPDIR)/libknotus_la-token.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/token.c' object='utils/common/libknotus_la-token.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-token.lo `test -f 'utils/common/token.c' || echo '$(srcdir)/'`utils/common/token.c
+
+utils/common/libknotus_la-util_conf.lo: utils/common/util_conf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-util_conf.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-util_conf.Tpo -c -o utils/common/libknotus_la-util_conf.lo `test -f 'utils/common/util_conf.c' || echo '$(srcdir)/'`utils/common/util_conf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-util_conf.Tpo utils/common/$(DEPDIR)/libknotus_la-util_conf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/util_conf.c' object='utils/common/libknotus_la-util_conf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-util_conf.lo `test -f 'utils/common/util_conf.c' || echo '$(srcdir)/'`utils/common/util_conf.c
+
+libzscanner/la-error.lo: libzscanner/error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/la-error.lo -MD -MP -MF libzscanner/$(DEPDIR)/la-error.Tpo -c -o libzscanner/la-error.lo `test -f 'libzscanner/error.c' || echo '$(srcdir)/'`libzscanner/error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/la-error.Tpo libzscanner/$(DEPDIR)/la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/error.c' object='libzscanner/la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/la-error.lo `test -f 'libzscanner/error.c' || echo '$(srcdir)/'`libzscanner/error.c
+
+libzscanner/la-functions.lo: libzscanner/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/la-functions.lo -MD -MP -MF libzscanner/$(DEPDIR)/la-functions.Tpo -c -o libzscanner/la-functions.lo `test -f 'libzscanner/functions.c' || echo '$(srcdir)/'`libzscanner/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/la-functions.Tpo libzscanner/$(DEPDIR)/la-functions.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/functions.c' object='libzscanner/la-functions.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/la-functions.lo `test -f 'libzscanner/functions.c' || echo '$(srcdir)/'`libzscanner/functions.c
+
+libzscanner/la-scanner.lo: libzscanner/scanner.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/la-scanner.lo -MD -MP -MF libzscanner/$(DEPDIR)/la-scanner.Tpo -c -o libzscanner/la-scanner.lo `test -f 'libzscanner/scanner.c' || echo '$(srcdir)/'`libzscanner/scanner.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/la-scanner.Tpo libzscanner/$(DEPDIR)/la-scanner.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/scanner.c' object='libzscanner/la-scanner.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/la-scanner.lo `test -f 'libzscanner/scanner.c' || echo '$(srcdir)/'`libzscanner/scanner.c
+
+utils/kcatalogprint/kcatalogprint-main.o: utils/kcatalogprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kcatalogprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kcatalogprint/kcatalogprint-main.o -MD -MP -MF utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Tpo -c -o utils/kcatalogprint/kcatalogprint-main.o `test -f 'utils/kcatalogprint/main.c' || echo '$(srcdir)/'`utils/kcatalogprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Tpo utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kcatalogprint/main.c' object='utils/kcatalogprint/kcatalogprint-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kcatalogprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kcatalogprint/kcatalogprint-main.o `test -f 'utils/kcatalogprint/main.c' || echo '$(srcdir)/'`utils/kcatalogprint/main.c
+
+utils/kcatalogprint/kcatalogprint-main.obj: utils/kcatalogprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kcatalogprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kcatalogprint/kcatalogprint-main.obj -MD -MP -MF utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Tpo -c -o utils/kcatalogprint/kcatalogprint-main.obj `if test -f 'utils/kcatalogprint/main.c'; then $(CYGPATH_W) 'utils/kcatalogprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kcatalogprint/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Tpo utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kcatalogprint/main.c' object='utils/kcatalogprint/kcatalogprint-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kcatalogprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kcatalogprint/kcatalogprint-main.obj `if test -f 'utils/kcatalogprint/main.c'; then $(CYGPATH_W) 'utils/kcatalogprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kcatalogprint/main.c'; fi`
+
+utils/kdig/kdig-kdig_exec.o: utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_exec.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo -c -o utils/kdig/kdig-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/kdig-kdig_exec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c
+
+utils/kdig/kdig-kdig_exec.obj: utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_exec.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo -c -o utils/kdig/kdig-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/kdig-kdig_exec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi`
+
+utils/kdig/kdig-kdig_main.o: utils/kdig/kdig_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_main.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo -c -o utils/kdig/kdig-kdig_main.o `test -f 'utils/kdig/kdig_main.c' || echo '$(srcdir)/'`utils/kdig/kdig_main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_main.c' object='utils/kdig/kdig-kdig_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_main.o `test -f 'utils/kdig/kdig_main.c' || echo '$(srcdir)/'`utils/kdig/kdig_main.c
+
+utils/kdig/kdig-kdig_main.obj: utils/kdig/kdig_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_main.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo -c -o utils/kdig/kdig-kdig_main.obj `if test -f 'utils/kdig/kdig_main.c'; then $(CYGPATH_W) 'utils/kdig/kdig_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_main.c' object='utils/kdig/kdig-kdig_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_main.obj `if test -f 'utils/kdig/kdig_main.c'; then $(CYGPATH_W) 'utils/kdig/kdig_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_main.c'; fi`
+
+utils/kdig/kdig-kdig_params.o: utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_params.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo -c -o utils/kdig/kdig-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/kdig-kdig_params.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c
+
+utils/kdig/kdig-kdig_params.obj: utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_params.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo -c -o utils/kdig/kdig-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/kdig-kdig_params.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi`
+
+utils/keymgr/keymgr-bind_privkey.o: utils/keymgr/bind_privkey.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-bind_privkey.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo -c -o utils/keymgr/keymgr-bind_privkey.o `test -f 'utils/keymgr/bind_privkey.c' || echo '$(srcdir)/'`utils/keymgr/bind_privkey.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/bind_privkey.c' object='utils/keymgr/keymgr-bind_privkey.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-bind_privkey.o `test -f 'utils/keymgr/bind_privkey.c' || echo '$(srcdir)/'`utils/keymgr/bind_privkey.c
+
+utils/keymgr/keymgr-bind_privkey.obj: utils/keymgr/bind_privkey.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-bind_privkey.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo -c -o utils/keymgr/keymgr-bind_privkey.obj `if test -f 'utils/keymgr/bind_privkey.c'; then $(CYGPATH_W) 'utils/keymgr/bind_privkey.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/bind_privkey.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/bind_privkey.c' object='utils/keymgr/keymgr-bind_privkey.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-bind_privkey.obj `if test -f 'utils/keymgr/bind_privkey.c'; then $(CYGPATH_W) 'utils/keymgr/bind_privkey.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/bind_privkey.c'; fi`
+
+utils/keymgr/keymgr-functions.o: utils/keymgr/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-functions.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo -c -o utils/keymgr/keymgr-functions.o `test -f 'utils/keymgr/functions.c' || echo '$(srcdir)/'`utils/keymgr/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo utils/keymgr/$(DEPDIR)/keymgr-functions.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/functions.c' object='utils/keymgr/keymgr-functions.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-functions.o `test -f 'utils/keymgr/functions.c' || echo '$(srcdir)/'`utils/keymgr/functions.c
+
+utils/keymgr/keymgr-functions.obj: utils/keymgr/functions.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-functions.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo -c -o utils/keymgr/keymgr-functions.obj `if test -f 'utils/keymgr/functions.c'; then $(CYGPATH_W) 'utils/keymgr/functions.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/functions.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo utils/keymgr/$(DEPDIR)/keymgr-functions.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/functions.c' object='utils/keymgr/keymgr-functions.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-functions.obj `if test -f 'utils/keymgr/functions.c'; then $(CYGPATH_W) 'utils/keymgr/functions.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/functions.c'; fi`
+
+utils/keymgr/keymgr-offline_ksk.o: utils/keymgr/offline_ksk.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-offline_ksk.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Tpo -c -o utils/keymgr/keymgr-offline_ksk.o `test -f 'utils/keymgr/offline_ksk.c' || echo '$(srcdir)/'`utils/keymgr/offline_ksk.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Tpo utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/offline_ksk.c' object='utils/keymgr/keymgr-offline_ksk.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-offline_ksk.o `test -f 'utils/keymgr/offline_ksk.c' || echo '$(srcdir)/'`utils/keymgr/offline_ksk.c
+
+utils/keymgr/keymgr-offline_ksk.obj: utils/keymgr/offline_ksk.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-offline_ksk.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Tpo -c -o utils/keymgr/keymgr-offline_ksk.obj `if test -f 'utils/keymgr/offline_ksk.c'; then $(CYGPATH_W) 'utils/keymgr/offline_ksk.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/offline_ksk.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Tpo utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/offline_ksk.c' object='utils/keymgr/keymgr-offline_ksk.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-offline_ksk.obj `if test -f 'utils/keymgr/offline_ksk.c'; then $(CYGPATH_W) 'utils/keymgr/offline_ksk.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/offline_ksk.c'; fi`
+
+utils/keymgr/keymgr-main.o: utils/keymgr/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-main.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-main.Tpo -c -o utils/keymgr/keymgr-main.o `test -f 'utils/keymgr/main.c' || echo '$(srcdir)/'`utils/keymgr/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-main.Tpo utils/keymgr/$(DEPDIR)/keymgr-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/main.c' object='utils/keymgr/keymgr-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-main.o `test -f 'utils/keymgr/main.c' || echo '$(srcdir)/'`utils/keymgr/main.c
+
+utils/keymgr/keymgr-main.obj: utils/keymgr/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-main.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-main.Tpo -c -o utils/keymgr/keymgr-main.obj `if test -f 'utils/keymgr/main.c'; then $(CYGPATH_W) 'utils/keymgr/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-main.Tpo utils/keymgr/$(DEPDIR)/keymgr-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/main.c' object='utils/keymgr/keymgr-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-main.obj `if test -f 'utils/keymgr/main.c'; then $(CYGPATH_W) 'utils/keymgr/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/main.c'; fi`
+
+utils/kdig/khost-kdig_exec.o: utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_exec.o -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo -c -o utils/kdig/khost-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo utils/kdig/$(DEPDIR)/khost-kdig_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/khost-kdig_exec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c
+
+utils/kdig/khost-kdig_exec.obj: utils/kdig/kdig_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_exec.obj -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo -c -o utils/kdig/khost-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo utils/kdig/$(DEPDIR)/khost-kdig_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/khost-kdig_exec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi`
+
+utils/kdig/khost-kdig_params.o: utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_params.o -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo -c -o utils/kdig/khost-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo utils/kdig/$(DEPDIR)/khost-kdig_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/khost-kdig_params.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c
+
+utils/kdig/khost-kdig_params.obj: utils/kdig/kdig_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_params.obj -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo -c -o utils/kdig/khost-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo utils/kdig/$(DEPDIR)/khost-kdig_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/khost-kdig_params.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi`
+
+utils/khost/khost-khost_main.o: utils/khost/khost_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_main.o -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_main.Tpo -c -o utils/khost/khost-khost_main.o `test -f 'utils/khost/khost_main.c' || echo '$(srcdir)/'`utils/khost/khost_main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_main.Tpo utils/khost/$(DEPDIR)/khost-khost_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_main.c' object='utils/khost/khost-khost_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_main.o `test -f 'utils/khost/khost_main.c' || echo '$(srcdir)/'`utils/khost/khost_main.c
+
+utils/khost/khost-khost_main.obj: utils/khost/khost_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_main.obj -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_main.Tpo -c -o utils/khost/khost-khost_main.obj `if test -f 'utils/khost/khost_main.c'; then $(CYGPATH_W) 'utils/khost/khost_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_main.Tpo utils/khost/$(DEPDIR)/khost-khost_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_main.c' object='utils/khost/khost-khost_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_main.obj `if test -f 'utils/khost/khost_main.c'; then $(CYGPATH_W) 'utils/khost/khost_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_main.c'; fi`
+
+utils/khost/khost-khost_params.o: utils/khost/khost_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_params.o -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_params.Tpo -c -o utils/khost/khost-khost_params.o `test -f 'utils/khost/khost_params.c' || echo '$(srcdir)/'`utils/khost/khost_params.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_params.Tpo utils/khost/$(DEPDIR)/khost-khost_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_params.c' object='utils/khost/khost-khost_params.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_params.o `test -f 'utils/khost/khost_params.c' || echo '$(srcdir)/'`utils/khost/khost_params.c
+
+utils/khost/khost-khost_params.obj: utils/khost/khost_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_params.obj -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_params.Tpo -c -o utils/khost/khost-khost_params.obj `if test -f 'utils/khost/khost_params.c'; then $(CYGPATH_W) 'utils/khost/khost_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_params.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_params.Tpo utils/khost/$(DEPDIR)/khost-khost_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_params.c' object='utils/khost/khost-khost_params.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_params.obj `if test -f 'utils/khost/khost_params.c'; then $(CYGPATH_W) 'utils/khost/khost_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_params.c'; fi`
+
+utils/kjournalprint/kjournalprint-main.o: utils/kjournalprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kjournalprint/kjournalprint-main.o -MD -MP -MF utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo -c -o utils/kjournalprint/kjournalprint-main.o `test -f 'utils/kjournalprint/main.c' || echo '$(srcdir)/'`utils/kjournalprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kjournalprint/main.c' object='utils/kjournalprint/kjournalprint-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kjournalprint/kjournalprint-main.o `test -f 'utils/kjournalprint/main.c' || echo '$(srcdir)/'`utils/kjournalprint/main.c
+
+utils/kjournalprint/kjournalprint-main.obj: utils/kjournalprint/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kjournalprint/kjournalprint-main.obj -MD -MP -MF utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo -c -o utils/kjournalprint/kjournalprint-main.obj `if test -f 'utils/kjournalprint/main.c'; then $(CYGPATH_W) 'utils/kjournalprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kjournalprint/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kjournalprint/main.c' object='utils/kjournalprint/kjournalprint-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kjournalprint/kjournalprint-main.obj `if test -f 'utils/kjournalprint/main.c'; then $(CYGPATH_W) 'utils/kjournalprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kjournalprint/main.c'; fi`
+
+utils/knotc/knotc-commands.o: utils/knotc/commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-commands.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-commands.Tpo -c -o utils/knotc/knotc-commands.o `test -f 'utils/knotc/commands.c' || echo '$(srcdir)/'`utils/knotc/commands.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-commands.Tpo utils/knotc/$(DEPDIR)/knotc-commands.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/commands.c' object='utils/knotc/knotc-commands.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-commands.o `test -f 'utils/knotc/commands.c' || echo '$(srcdir)/'`utils/knotc/commands.c
+
+utils/knotc/knotc-commands.obj: utils/knotc/commands.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-commands.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-commands.Tpo -c -o utils/knotc/knotc-commands.obj `if test -f 'utils/knotc/commands.c'; then $(CYGPATH_W) 'utils/knotc/commands.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/commands.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-commands.Tpo utils/knotc/$(DEPDIR)/knotc-commands.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/commands.c' object='utils/knotc/knotc-commands.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-commands.obj `if test -f 'utils/knotc/commands.c'; then $(CYGPATH_W) 'utils/knotc/commands.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/commands.c'; fi`
+
+utils/knotc/knotc-interactive.o: utils/knotc/interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-interactive.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-interactive.Tpo -c -o utils/knotc/knotc-interactive.o `test -f 'utils/knotc/interactive.c' || echo '$(srcdir)/'`utils/knotc/interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-interactive.Tpo utils/knotc/$(DEPDIR)/knotc-interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/interactive.c' object='utils/knotc/knotc-interactive.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-interactive.o `test -f 'utils/knotc/interactive.c' || echo '$(srcdir)/'`utils/knotc/interactive.c
+
+utils/knotc/knotc-interactive.obj: utils/knotc/interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-interactive.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-interactive.Tpo -c -o utils/knotc/knotc-interactive.obj `if test -f 'utils/knotc/interactive.c'; then $(CYGPATH_W) 'utils/knotc/interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/interactive.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-interactive.Tpo utils/knotc/$(DEPDIR)/knotc-interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/interactive.c' object='utils/knotc/knotc-interactive.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-interactive.obj `if test -f 'utils/knotc/interactive.c'; then $(CYGPATH_W) 'utils/knotc/interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/interactive.c'; fi`
+
+utils/knotc/knotc-process.o: utils/knotc/process.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-process.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-process.Tpo -c -o utils/knotc/knotc-process.o `test -f 'utils/knotc/process.c' || echo '$(srcdir)/'`utils/knotc/process.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-process.Tpo utils/knotc/$(DEPDIR)/knotc-process.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/process.c' object='utils/knotc/knotc-process.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-process.o `test -f 'utils/knotc/process.c' || echo '$(srcdir)/'`utils/knotc/process.c
+
+utils/knotc/knotc-process.obj: utils/knotc/process.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-process.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-process.Tpo -c -o utils/knotc/knotc-process.obj `if test -f 'utils/knotc/process.c'; then $(CYGPATH_W) 'utils/knotc/process.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/process.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-process.Tpo utils/knotc/$(DEPDIR)/knotc-process.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/process.c' object='utils/knotc/knotc-process.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-process.obj `if test -f 'utils/knotc/process.c'; then $(CYGPATH_W) 'utils/knotc/process.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/process.c'; fi`
+
+utils/knotc/knotc-main.o: utils/knotc/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-main.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-main.Tpo -c -o utils/knotc/knotc-main.o `test -f 'utils/knotc/main.c' || echo '$(srcdir)/'`utils/knotc/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-main.Tpo utils/knotc/$(DEPDIR)/knotc-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/main.c' object='utils/knotc/knotc-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-main.o `test -f 'utils/knotc/main.c' || echo '$(srcdir)/'`utils/knotc/main.c
+
+utils/knotc/knotc-main.obj: utils/knotc/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-main.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-main.Tpo -c -o utils/knotc/knotc-main.obj `if test -f 'utils/knotc/main.c'; then $(CYGPATH_W) 'utils/knotc/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-main.Tpo utils/knotc/$(DEPDIR)/knotc-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/main.c' object='utils/knotc/knotc-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-main.obj `if test -f 'utils/knotc/main.c'; then $(CYGPATH_W) 'utils/knotc/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/main.c'; fi`
+
+utils/knotd/knotd-main.o: utils/knotd/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotd/knotd-main.o -MD -MP -MF utils/knotd/$(DEPDIR)/knotd-main.Tpo -c -o utils/knotd/knotd-main.o `test -f 'utils/knotd/main.c' || echo '$(srcdir)/'`utils/knotd/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotd/$(DEPDIR)/knotd-main.Tpo utils/knotd/$(DEPDIR)/knotd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotd/main.c' object='utils/knotd/knotd-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotd/knotd-main.o `test -f 'utils/knotd/main.c' || echo '$(srcdir)/'`utils/knotd/main.c
+
+utils/knotd/knotd-main.obj: utils/knotd/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotd/knotd-main.obj -MD -MP -MF utils/knotd/$(DEPDIR)/knotd-main.Tpo -c -o utils/knotd/knotd-main.obj `if test -f 'utils/knotd/main.c'; then $(CYGPATH_W) 'utils/knotd/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotd/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotd/$(DEPDIR)/knotd-main.Tpo utils/knotd/$(DEPDIR)/knotd-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotd/main.c' object='utils/knotd/knotd-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotd/knotd-main.obj `if test -f 'utils/knotd/main.c'; then $(CYGPATH_W) 'utils/knotd/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotd/main.c'; fi`
+
+utils/knsec3hash/knsec3hash-knsec3hash.o: utils/knsec3hash/knsec3hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsec3hash/knsec3hash-knsec3hash.o -MD -MP -MF utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo -c -o utils/knsec3hash/knsec3hash-knsec3hash.o `test -f 'utils/knsec3hash/knsec3hash.c' || echo '$(srcdir)/'`utils/knsec3hash/knsec3hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsec3hash/knsec3hash.c' object='utils/knsec3hash/knsec3hash-knsec3hash.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsec3hash/knsec3hash-knsec3hash.o `test -f 'utils/knsec3hash/knsec3hash.c' || echo '$(srcdir)/'`utils/knsec3hash/knsec3hash.c
+
+utils/knsec3hash/knsec3hash-knsec3hash.obj: utils/knsec3hash/knsec3hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsec3hash/knsec3hash-knsec3hash.obj -MD -MP -MF utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo -c -o utils/knsec3hash/knsec3hash-knsec3hash.obj `if test -f 'utils/knsec3hash/knsec3hash.c'; then $(CYGPATH_W) 'utils/knsec3hash/knsec3hash.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsec3hash/knsec3hash.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsec3hash/knsec3hash.c' object='utils/knsec3hash/knsec3hash-knsec3hash.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsec3hash/knsec3hash-knsec3hash.obj `if test -f 'utils/knsec3hash/knsec3hash.c'; then $(CYGPATH_W) 'utils/knsec3hash/knsec3hash.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsec3hash/knsec3hash.c'; fi`
+
+utils/knsupdate/knsupdate-knsupdate_exec.o: utils/knsupdate/knsupdate_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_exec.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_exec.o `test -f 'utils/knsupdate/knsupdate_exec.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_exec.c' object='utils/knsupdate/knsupdate-knsupdate_exec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_exec.o `test -f 'utils/knsupdate/knsupdate_exec.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_exec.c
+
+utils/knsupdate/knsupdate-knsupdate_exec.obj: utils/knsupdate/knsupdate_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_exec.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_exec.obj `if test -f 'utils/knsupdate/knsupdate_exec.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_exec.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_exec.c' object='utils/knsupdate/knsupdate-knsupdate_exec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_exec.obj `if test -f 'utils/knsupdate/knsupdate_exec.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_exec.c'; fi`
+
+utils/knsupdate/knsupdate-knsupdate_interactive.o: utils/knsupdate/knsupdate_interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_interactive.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_interactive.o `test -f 'utils/knsupdate/knsupdate_interactive.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_interactive.c' object='utils/knsupdate/knsupdate-knsupdate_interactive.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_interactive.o `test -f 'utils/knsupdate/knsupdate_interactive.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_interactive.c
+
+utils/knsupdate/knsupdate-knsupdate_interactive.obj: utils/knsupdate/knsupdate_interactive.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_interactive.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_interactive.obj `if test -f 'utils/knsupdate/knsupdate_interactive.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_interactive.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_interactive.c' object='utils/knsupdate/knsupdate-knsupdate_interactive.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_interactive.obj `if test -f 'utils/knsupdate/knsupdate_interactive.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_interactive.c'; fi`
+
+utils/knsupdate/knsupdate-knsupdate_main.o: utils/knsupdate/knsupdate_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_main.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_main.o `test -f 'utils/knsupdate/knsupdate_main.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_main.c' object='utils/knsupdate/knsupdate-knsupdate_main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_main.o `test -f 'utils/knsupdate/knsupdate_main.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_main.c
+
+utils/knsupdate/knsupdate-knsupdate_main.obj: utils/knsupdate/knsupdate_main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_main.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_main.obj `if test -f 'utils/knsupdate/knsupdate_main.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_main.c' object='utils/knsupdate/knsupdate-knsupdate_main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_main.obj `if test -f 'utils/knsupdate/knsupdate_main.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_main.c'; fi`
+
+utils/knsupdate/knsupdate-knsupdate_params.o: utils/knsupdate/knsupdate_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_params.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_params.o `test -f 'utils/knsupdate/knsupdate_params.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_params.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_params.c' object='utils/knsupdate/knsupdate-knsupdate_params.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_params.o `test -f 'utils/knsupdate/knsupdate_params.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_params.c
+
+utils/knsupdate/knsupdate-knsupdate_params.obj: utils/knsupdate/knsupdate_params.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_params.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_params.obj `if test -f 'utils/knsupdate/knsupdate_params.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_params.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_params.c' object='utils/knsupdate/knsupdate-knsupdate_params.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_params.obj `if test -f 'utils/knsupdate/knsupdate_params.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_params.c'; fi`
+
+utils/kxdpgun/kxdpgun-ip_route.o: utils/kxdpgun/ip_route.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-ip_route.o -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Tpo -c -o utils/kxdpgun/kxdpgun-ip_route.o `test -f 'utils/kxdpgun/ip_route.c' || echo '$(srcdir)/'`utils/kxdpgun/ip_route.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/ip_route.c' object='utils/kxdpgun/kxdpgun-ip_route.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-ip_route.o `test -f 'utils/kxdpgun/ip_route.c' || echo '$(srcdir)/'`utils/kxdpgun/ip_route.c
+
+utils/kxdpgun/kxdpgun-ip_route.obj: utils/kxdpgun/ip_route.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-ip_route.obj -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Tpo -c -o utils/kxdpgun/kxdpgun-ip_route.obj `if test -f 'utils/kxdpgun/ip_route.c'; then $(CYGPATH_W) 'utils/kxdpgun/ip_route.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/ip_route.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/ip_route.c' object='utils/kxdpgun/kxdpgun-ip_route.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-ip_route.obj `if test -f 'utils/kxdpgun/ip_route.c'; then $(CYGPATH_W) 'utils/kxdpgun/ip_route.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/ip_route.c'; fi`
+
+utils/kxdpgun/kxdpgun-load_queries.o: utils/kxdpgun/load_queries.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-load_queries.o -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Tpo -c -o utils/kxdpgun/kxdpgun-load_queries.o `test -f 'utils/kxdpgun/load_queries.c' || echo '$(srcdir)/'`utils/kxdpgun/load_queries.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/load_queries.c' object='utils/kxdpgun/kxdpgun-load_queries.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-load_queries.o `test -f 'utils/kxdpgun/load_queries.c' || echo '$(srcdir)/'`utils/kxdpgun/load_queries.c
+
+utils/kxdpgun/kxdpgun-load_queries.obj: utils/kxdpgun/load_queries.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-load_queries.obj -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Tpo -c -o utils/kxdpgun/kxdpgun-load_queries.obj `if test -f 'utils/kxdpgun/load_queries.c'; then $(CYGPATH_W) 'utils/kxdpgun/load_queries.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/load_queries.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/load_queries.c' object='utils/kxdpgun/kxdpgun-load_queries.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-load_queries.obj `if test -f 'utils/kxdpgun/load_queries.c'; then $(CYGPATH_W) 'utils/kxdpgun/load_queries.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/load_queries.c'; fi`
+
+utils/kxdpgun/kxdpgun-main.o: utils/kxdpgun/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-main.o -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Tpo -c -o utils/kxdpgun/kxdpgun-main.o `test -f 'utils/kxdpgun/main.c' || echo '$(srcdir)/'`utils/kxdpgun/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/main.c' object='utils/kxdpgun/kxdpgun-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-main.o `test -f 'utils/kxdpgun/main.c' || echo '$(srcdir)/'`utils/kxdpgun/main.c
+
+utils/kxdpgun/kxdpgun-main.obj: utils/kxdpgun/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kxdpgun/kxdpgun-main.obj -MD -MP -MF utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Tpo -c -o utils/kxdpgun/kxdpgun-main.obj `if test -f 'utils/kxdpgun/main.c'; then $(CYGPATH_W) 'utils/kxdpgun/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Tpo utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kxdpgun/main.c' object='utils/kxdpgun/kxdpgun-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kxdpgun_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kxdpgun/kxdpgun-main.obj `if test -f 'utils/kxdpgun/main.c'; then $(CYGPATH_W) 'utils/kxdpgun/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kxdpgun/main.c'; fi`
+
+utils/kzonecheck/kzonecheck-main.o: utils/kzonecheck/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-main.o -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo -c -o utils/kzonecheck/kzonecheck-main.o `test -f 'utils/kzonecheck/main.c' || echo '$(srcdir)/'`utils/kzonecheck/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/main.c' object='utils/kzonecheck/kzonecheck-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-main.o `test -f 'utils/kzonecheck/main.c' || echo '$(srcdir)/'`utils/kzonecheck/main.c
+
+utils/kzonecheck/kzonecheck-main.obj: utils/kzonecheck/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-main.obj -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo -c -o utils/kzonecheck/kzonecheck-main.obj `if test -f 'utils/kzonecheck/main.c'; then $(CYGPATH_W) 'utils/kzonecheck/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/main.c' object='utils/kzonecheck/kzonecheck-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-main.obj `if test -f 'utils/kzonecheck/main.c'; then $(CYGPATH_W) 'utils/kzonecheck/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/main.c'; fi`
+
+utils/kzonecheck/kzonecheck-zone_check.o: utils/kzonecheck/zone_check.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-zone_check.o -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo -c -o utils/kzonecheck/kzonecheck-zone_check.o `test -f 'utils/kzonecheck/zone_check.c' || echo '$(srcdir)/'`utils/kzonecheck/zone_check.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/zone_check.c' object='utils/kzonecheck/kzonecheck-zone_check.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-zone_check.o `test -f 'utils/kzonecheck/zone_check.c' || echo '$(srcdir)/'`utils/kzonecheck/zone_check.c
+
+utils/kzonecheck/kzonecheck-zone_check.obj: utils/kzonecheck/zone_check.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-zone_check.obj -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo -c -o utils/kzonecheck/kzonecheck-zone_check.obj `if test -f 'utils/kzonecheck/zone_check.c'; then $(CYGPATH_W) 'utils/kzonecheck/zone_check.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/zone_check.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/zone_check.c' object='utils/kzonecheck/kzonecheck-zone_check.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-zone_check.obj `if test -f 'utils/kzonecheck/zone_check.c'; then $(CYGPATH_W) 'utils/kzonecheck/zone_check.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/zone_check.c'; fi`
+
+utils/kzonesign/kzonesign-main.o: utils/kzonesign/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonesign_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonesign/kzonesign-main.o -MD -MP -MF utils/kzonesign/$(DEPDIR)/kzonesign-main.Tpo -c -o utils/kzonesign/kzonesign-main.o `test -f 'utils/kzonesign/main.c' || echo '$(srcdir)/'`utils/kzonesign/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonesign/$(DEPDIR)/kzonesign-main.Tpo utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonesign/main.c' object='utils/kzonesign/kzonesign-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonesign_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonesign/kzonesign-main.o `test -f 'utils/kzonesign/main.c' || echo '$(srcdir)/'`utils/kzonesign/main.c
+
+utils/kzonesign/kzonesign-main.obj: utils/kzonesign/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonesign_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonesign/kzonesign-main.obj -MD -MP -MF utils/kzonesign/$(DEPDIR)/kzonesign-main.Tpo -c -o utils/kzonesign/kzonesign-main.obj `if test -f 'utils/kzonesign/main.c'; then $(CYGPATH_W) 'utils/kzonesign/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonesign/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonesign/$(DEPDIR)/kzonesign-main.Tpo utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonesign/main.c' object='utils/kzonesign/kzonesign-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonesign_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonesign/kzonesign-main.obj `if test -f 'utils/kzonesign/main.c'; then $(CYGPATH_W) 'utils/kzonesign/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonesign/main.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf contrib/.libs contrib/_libs
+ -rm -rf contrib/dnstap/.libs contrib/dnstap/_libs
+ -rm -rf contrib/libbpf/bpf/.libs contrib/libbpf/bpf/_libs
+ -rm -rf contrib/libngtcp2/ngtcp2/crypto/.libs contrib/libngtcp2/ngtcp2/crypto/_libs
+ -rm -rf contrib/libngtcp2/ngtcp2/lib/.libs contrib/libngtcp2/ngtcp2/lib/_libs
+ -rm -rf contrib/musl/.libs contrib/musl/_libs
+ -rm -rf contrib/openbsd/.libs contrib/openbsd/_libs
+ -rm -rf contrib/proxyv2/.libs contrib/proxyv2/_libs
+ -rm -rf contrib/qp-trie/.libs contrib/qp-trie/_libs
+ -rm -rf contrib/ucw/.libs contrib/ucw/_libs
+ -rm -rf contrib/url-parser/.libs contrib/url-parser/_libs
+ -rm -rf contrib/vpool/.libs contrib/vpool/_libs
+ -rm -rf knot/catalog/.libs knot/catalog/_libs
+ -rm -rf knot/common/.libs knot/common/_libs
+ -rm -rf knot/conf/.libs knot/conf/_libs
+ -rm -rf knot/ctl/.libs knot/ctl/_libs
+ -rm -rf knot/dnssec/.libs knot/dnssec/_libs
+ -rm -rf knot/dnssec/kasp/.libs knot/dnssec/kasp/_libs
+ -rm -rf knot/events/.libs knot/events/_libs
+ -rm -rf knot/events/handlers/.libs knot/events/handlers/_libs
+ -rm -rf knot/journal/.libs knot/journal/_libs
+ -rm -rf knot/modules/.libs knot/modules/_libs
+ -rm -rf knot/modules/cookies/.libs knot/modules/cookies/_libs
+ -rm -rf knot/modules/dnsproxy/.libs knot/modules/dnsproxy/_libs
+ -rm -rf knot/modules/dnstap/.libs knot/modules/dnstap/_libs
+ -rm -rf knot/modules/geoip/.libs knot/modules/geoip/_libs
+ -rm -rf knot/modules/noudp/.libs knot/modules/noudp/_libs
+ -rm -rf knot/modules/onlinesign/.libs knot/modules/onlinesign/_libs
+ -rm -rf knot/modules/probe/.libs knot/modules/probe/_libs
+ -rm -rf knot/modules/queryacl/.libs knot/modules/queryacl/_libs
+ -rm -rf knot/modules/rrl/.libs knot/modules/rrl/_libs
+ -rm -rf knot/modules/stats/.libs knot/modules/stats/_libs
+ -rm -rf knot/modules/synthrecord/.libs knot/modules/synthrecord/_libs
+ -rm -rf knot/modules/whoami/.libs knot/modules/whoami/_libs
+ -rm -rf knot/nameserver/.libs knot/nameserver/_libs
+ -rm -rf knot/query/.libs knot/query/_libs
+ -rm -rf knot/server/.libs knot/server/_libs
+ -rm -rf knot/updates/.libs knot/updates/_libs
+ -rm -rf knot/worker/.libs knot/worker/_libs
+ -rm -rf knot/zone/.libs knot/zone/_libs
+ -rm -rf libdnssec/.libs libdnssec/_libs
+ -rm -rf libdnssec/key/.libs libdnssec/key/_libs
+ -rm -rf libdnssec/keystore/.libs libdnssec/keystore/_libs
+ -rm -rf libdnssec/nsec/.libs libdnssec/nsec/_libs
+ -rm -rf libdnssec/p11/.libs libdnssec/p11/_libs
+ -rm -rf libdnssec/shared/.libs libdnssec/shared/_libs
+ -rm -rf libdnssec/sign/.libs libdnssec/sign/_libs
+ -rm -rf libknot/.libs libknot/_libs
+ -rm -rf libknot/control/.libs libknot/control/_libs
+ -rm -rf libknot/db/.libs libknot/db/_libs
+ -rm -rf libknot/packet/.libs libknot/packet/_libs
+ -rm -rf libknot/probe/.libs libknot/probe/_libs
+ -rm -rf libknot/rrtype/.libs libknot/rrtype/_libs
+ -rm -rf libknot/xdp/.libs libknot/xdp/_libs
+ -rm -rf libknot/yparser/.libs libknot/yparser/_libs
+ -rm -rf libzscanner/.libs libzscanner/_libs
+ -rm -rf utils/common/.libs utils/common/_libs
+install-pkgconfigDATA: $(pkgconfig_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \
+ done
+
+uninstall-pkgconfigDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir)
+install-include_libdnssecHEADERS: $(include_libdnssec_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_libdnssec_HEADERS)'; test -n "$(include_libdnssecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(include_libdnssecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(include_libdnssecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(include_libdnssecdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libdnssecdir)" || exit $$?; \
+ done
+
+uninstall-include_libdnssecHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_libdnssec_HEADERS)'; test -n "$(include_libdnssecdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(include_libdnssecdir)'; $(am__uninstall_files_from_dir)
+install-include_libknotdHEADERS: $(include_libknotd_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_libknotd_HEADERS)'; test -n "$(include_libknotddir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(include_libknotddir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(include_libknotddir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(include_libknotddir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libknotddir)" || exit $$?; \
+ done
+
+uninstall-include_libknotdHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_libknotd_HEADERS)'; test -n "$(include_libknotddir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(include_libknotddir)'; $(am__uninstall_files_from_dir)
+install-include_libzscannerHEADERS: $(include_libzscanner_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_libzscanner_HEADERS)'; test -n "$(include_libzscannerdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(include_libzscannerdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(include_libzscannerdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(include_libzscannerdir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libzscannerdir)" || exit $$?; \
+ done
+
+uninstall-include_libzscannerHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_libzscanner_HEADERS)'; test -n "$(include_libzscannerdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(include_libzscannerdir)'; $(am__uninstall_files_from_dir)
+install-nobase_include_libknotHEADERS: $(nobase_include_libknot_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(nobase_include_libknot_HEADERS)'; test -n "$(include_libknotdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(include_libknotdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(include_libknotdir)" || 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)$(include_libknotdir)/$$dir'"; \
+ $(MKDIR_P) "$(DESTDIR)$(include_libknotdir)/$$dir"; }; \
+ echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(include_libknotdir)/$$dir'"; \
+ $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(include_libknotdir)/$$dir" || exit $$?; }; \
+ done
+
+uninstall-nobase_include_libknotHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(nobase_include_libknot_HEADERS)'; test -n "$(include_libknotdir)" || list=; \
+ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
+ dir='$(DESTDIR)$(include_libknotdir)'; $(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"
+
+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) $(LTLIBRARIES) $(DATA) $(HEADERS) \
+ config.h
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(include_libdnssecdir)" "$(DESTDIR)$(include_libknotddir)" "$(DESTDIR)$(include_libzscannerdir)" "$(DESTDIR)$(include_libknotdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-recursive
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) 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 contrib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/$(am__dirstamp)
+ -rm -f contrib/dnstap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/dnstap/$(am__dirstamp)
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/libbpf/bpf/$(am__dirstamp)
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(am__dirstamp)
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(am__dirstamp)
+ -rm -f contrib/musl/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/musl/$(am__dirstamp)
+ -rm -f contrib/openbsd/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/openbsd/$(am__dirstamp)
+ -rm -f contrib/proxyv2/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/proxyv2/$(am__dirstamp)
+ -rm -f contrib/qp-trie/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/qp-trie/$(am__dirstamp)
+ -rm -f contrib/ucw/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/ucw/$(am__dirstamp)
+ -rm -f contrib/url-parser/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/url-parser/$(am__dirstamp)
+ -rm -f contrib/vpool/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/vpool/$(am__dirstamp)
+ -rm -f knot/catalog/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/catalog/$(am__dirstamp)
+ -rm -f knot/common/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/common/$(am__dirstamp)
+ -rm -f knot/conf/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/conf/$(am__dirstamp)
+ -rm -f knot/ctl/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/ctl/$(am__dirstamp)
+ -rm -f knot/dnssec/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/dnssec/$(am__dirstamp)
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/dnssec/kasp/$(am__dirstamp)
+ -rm -f knot/events/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/events/$(am__dirstamp)
+ -rm -f knot/events/handlers/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/events/handlers/$(am__dirstamp)
+ -rm -f knot/journal/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/journal/$(am__dirstamp)
+ -rm -f knot/modules/$(am__dirstamp)
+ -rm -f knot/modules/cookies/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/cookies/$(am__dirstamp)
+ -rm -f knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/dnsproxy/$(am__dirstamp)
+ -rm -f knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/dnstap/$(am__dirstamp)
+ -rm -f knot/modules/geoip/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/geoip/$(am__dirstamp)
+ -rm -f knot/modules/noudp/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/noudp/$(am__dirstamp)
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/onlinesign/$(am__dirstamp)
+ -rm -f knot/modules/probe/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/probe/$(am__dirstamp)
+ -rm -f knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/queryacl/$(am__dirstamp)
+ -rm -f knot/modules/rrl/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/rrl/$(am__dirstamp)
+ -rm -f knot/modules/stats/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/stats/$(am__dirstamp)
+ -rm -f knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/synthrecord/$(am__dirstamp)
+ -rm -f knot/modules/whoami/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/modules/whoami/$(am__dirstamp)
+ -rm -f knot/nameserver/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/nameserver/$(am__dirstamp)
+ -rm -f knot/query/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/query/$(am__dirstamp)
+ -rm -f knot/server/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/server/$(am__dirstamp)
+ -rm -f knot/updates/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/updates/$(am__dirstamp)
+ -rm -f knot/worker/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/worker/$(am__dirstamp)
+ -rm -f knot/zone/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/zone/$(am__dirstamp)
+ -rm -f libdnssec/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/$(am__dirstamp)
+ -rm -f libdnssec/key/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/key/$(am__dirstamp)
+ -rm -f libdnssec/keystore/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/keystore/$(am__dirstamp)
+ -rm -f libdnssec/nsec/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/nsec/$(am__dirstamp)
+ -rm -f libdnssec/p11/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/p11/$(am__dirstamp)
+ -rm -f libdnssec/shared/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/shared/$(am__dirstamp)
+ -rm -f libdnssec/sign/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/sign/$(am__dirstamp)
+ -rm -f libknot/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/$(am__dirstamp)
+ -rm -f libknot/control/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/control/$(am__dirstamp)
+ -rm -f libknot/db/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/db/$(am__dirstamp)
+ -rm -f libknot/packet/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/packet/$(am__dirstamp)
+ -rm -f libknot/probe/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/probe/$(am__dirstamp)
+ -rm -f libknot/rrtype/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/rrtype/$(am__dirstamp)
+ -rm -f libknot/xdp/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/xdp/$(am__dirstamp)
+ -rm -f libknot/yparser/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/yparser/$(am__dirstamp)
+ -rm -f libzscanner/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libzscanner/$(am__dirstamp)
+ -rm -f utils/common/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/common/$(am__dirstamp)
+ -rm -f utils/kcatalogprint/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kcatalogprint/$(am__dirstamp)
+ -rm -f utils/kdig/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kdig/$(am__dirstamp)
+ -rm -f utils/keymgr/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/keymgr/$(am__dirstamp)
+ -rm -f utils/khost/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/khost/$(am__dirstamp)
+ -rm -f utils/kjournalprint/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kjournalprint/$(am__dirstamp)
+ -rm -f utils/knotc/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/knotc/$(am__dirstamp)
+ -rm -f utils/knotd/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/knotd/$(am__dirstamp)
+ -rm -f utils/knsec3hash/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/knsec3hash/$(am__dirstamp)
+ -rm -f utils/knsupdate/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/knsupdate/$(am__dirstamp)
+ -rm -f utils/kxdpgun/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kxdpgun/$(am__dirstamp)
+ -rm -f utils/kzonecheck/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kzonecheck/$(am__dirstamp)
+ -rm -f utils/kzonesign/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/kzonesign/$(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)
+@HAVE_DAEMON_FALSE@install-data-hook:
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base64.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base64url.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-conn_pool.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-files.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-getline.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-json.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-net.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-semaphore.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-string.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-time.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Plo
+ -rm -f contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo
+ -rm -f contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Plo
+ -rm -f contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo
+ -rm -f contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Plo
+ -rm -f contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-fdset.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-log.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-process.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-stats.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-systemd.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-unreachable.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-base.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-conf.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-confio.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-migration.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-module.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-schema.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-tools.Plo
+ -rm -f knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo
+ -rm -f knot/ctl/$(DEPDIR)/libknotd_la-process.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo
+ -rm -f knot/events/$(DEPDIR)/libknotd_la-events.Plo
+ -rm -f knot/events/$(DEPDIR)/libknotd_la-replan.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_write.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo
+ -rm -f knot/modules/cookies/$(DEPDIR)/la-cookies.Plo
+ -rm -f knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo
+ -rm -f knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Plo
+ -rm -f knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo
+ -rm -f knot/modules/dnstap/$(DEPDIR)/la-dnstap.Plo
+ -rm -f knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/la-geodb.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/la-geoip.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo
+ -rm -f knot/modules/noudp/$(DEPDIR)/la-noudp.Plo
+ -rm -f knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo
+ -rm -f knot/modules/probe/$(DEPDIR)/la-probe.Plo
+ -rm -f knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Plo
+ -rm -f knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo
+ -rm -f knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-rrl.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo
+ -rm -f knot/modules/stats/$(DEPDIR)/la-stats.Plo
+ -rm -f knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
+ -rm -f knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Plo
+ -rm -f knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo
+ -rm -f knot/modules/whoami/$(DEPDIR)/la-whoami.Plo
+ -rm -f knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-capture.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-query.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-requestor.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-server.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-acl.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-apply.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo
+ -rm -f knot/worker/$(DEPDIR)/libknotd_la-pool.Plo
+ -rm -f knot/worker/$(DEPDIR)/libknotd_la-queue.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-adjust.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-backup.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-contents.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-digest.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-measure.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-node.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-serial.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-timers.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-binary.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-crypto.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-digest.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-error.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-keyid.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-pem.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-random.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-tsig.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-algorithm.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-convert.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-dnskey.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-ds.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-key.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-keytag.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-privkey.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-simple.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-keystore.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-pkcs11.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-pkcs8.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-bitmap.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-hash.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-nsec.Plo
+ -rm -f libdnssec/p11/$(DEPDIR)/la-p11.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-bignum.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-dname.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Plo
+ -rm -f libdnssec/sign/$(DEPDIR)/la-der.Plo
+ -rm -f libdnssec/sign/$(DEPDIR)/la-sign.Plo
+ -rm -f libknot/$(DEPDIR)/la-codes.Plo
+ -rm -f libknot/$(DEPDIR)/la-cookies.Plo
+ -rm -f libknot/$(DEPDIR)/la-descriptor.Plo
+ -rm -f libknot/$(DEPDIR)/la-dname.Plo
+ -rm -f libknot/$(DEPDIR)/la-error.Plo
+ -rm -f libknot/$(DEPDIR)/la-rdataset.Plo
+ -rm -f libknot/$(DEPDIR)/la-rrset-dump.Plo
+ -rm -f libknot/$(DEPDIR)/la-rrset.Plo
+ -rm -f libknot/$(DEPDIR)/la-tsig-op.Plo
+ -rm -f libknot/$(DEPDIR)/la-tsig.Plo
+ -rm -f libknot/control/$(DEPDIR)/la-control.Plo
+ -rm -f libknot/db/$(DEPDIR)/la-db_lmdb.Plo
+ -rm -f libknot/db/$(DEPDIR)/la-db_trie.Plo
+ -rm -f libknot/packet/$(DEPDIR)/la-pkt.Plo
+ -rm -f libknot/packet/$(DEPDIR)/la-rrset-wire.Plo
+ -rm -f libknot/probe/$(DEPDIR)/la-data.Plo
+ -rm -f libknot/probe/$(DEPDIR)/la-probe.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-naptr.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-opt.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-tsig.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-bpf-user.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-eth.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-quic.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-quic_conn.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-tcp.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-xdp.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-yparser.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypbody.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypformat.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypschema.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-yptrafo.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-error.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-functions.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-scanner.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-cert.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-exec.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-hex.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-https.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-lookup.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-msg.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-netio.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-params.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-quic.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-resolv.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-sign.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-tls.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-token.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-util_conf.Plo
+ -rm -f utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_main.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_params.Po
+ -rm -f utils/kdig/$(DEPDIR)/khost-kdig_exec.Po
+ -rm -f utils/kdig/$(DEPDIR)/khost-kdig_params.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-functions.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-main.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po
+ -rm -f utils/khost/$(DEPDIR)/khost-khost_main.Po
+ -rm -f utils/khost/$(DEPDIR)/khost-khost_params.Po
+ -rm -f utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-commands.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-interactive.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-main.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-process.Po
+ -rm -f utils/knotd/$(DEPDIR)/knotd-main.Po
+ -rm -f utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+ -rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
+ -rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
+ -rm -f utils/kzonesign/$(DEPDIR)/kzonesign-main.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-include_libdnssecHEADERS \
+ install-include_libknotdHEADERS \
+ install-include_libzscannerHEADERS \
+ install-nobase_include_libknotHEADERS install-pkgconfigDATA
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \
+ install-pkglibLTLIBRARIES install-sbinPROGRAMS
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am: installcheck-binPROGRAMS installcheck-sbinPROGRAMS
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base64.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-base64url.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-conn_pool.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-files.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-getline.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-json.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-net.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-semaphore.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-string.Plo
+ -rm -f contrib/$(DEPDIR)/libcontrib_la-time.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo
+ -rm -f contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-bpf_prog_linfo.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-btf_dump.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-hashmap.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_errno.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-libbpf_probes.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-netlink.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-nlattr.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-str_error.Plo
+ -rm -f contrib/libbpf/bpf/$(DEPDIR)/libembbpf_la-xsk.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-gnutls.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/crypto/$(DEPDIR)/libembngtcp2_la-shared.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_acktr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_addr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_balloc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_bbr2.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_buf.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_cid.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conn.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_conv.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_crypto.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_err.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_gaptr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_idtr.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ksl.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_log.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_map.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_mem.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_objalloc.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_opl.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_path.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pkt.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pmtud.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ppe.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pq.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_pv.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_qlog.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_range.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_ringbuf.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rob.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rst.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_rtb.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_str.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_strm.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_unreachable.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_vec.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_version.Plo
+ -rm -f contrib/libngtcp2/ngtcp2/lib/$(DEPDIR)/libembngtcp2_la-ngtcp2_window_filter.Plo
+ -rm -f contrib/musl/$(DEPDIR)/libcontrib_la-inet_ntop.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo
+ -rm -f contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo
+ -rm -f contrib/proxyv2/$(DEPDIR)/libcontrib_la-proxyv2.Plo
+ -rm -f contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo
+ -rm -f contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo
+ -rm -f contrib/url-parser/$(DEPDIR)/libcontrib_la-url_parser.Plo
+ -rm -f contrib/vpool/$(DEPDIR)/libcontrib_la-vpool.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_db.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-catalog_update.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-generate.Plo
+ -rm -f knot/catalog/$(DEPDIR)/libknotd_la-interpret.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-evsched.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-fdset.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-log.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-process.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-stats.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-systemd.Plo
+ -rm -f knot/common/$(DEPDIR)/libknotd_la-unreachable.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-base.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-conf.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-confio.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-migration.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-module.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-schema.Plo
+ -rm -f knot/conf/$(DEPDIR)/libknotd_la-tools.Plo
+ -rm -f knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo
+ -rm -f knot/ctl/$(DEPDIR)/libknotd_la-process.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-key_records.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo
+ -rm -f knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo
+ -rm -f knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo
+ -rm -f knot/events/$(DEPDIR)/libknotd_la-events.Plo
+ -rm -f knot/events/$(DEPDIR)/libknotd_la-replan.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-backup.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-ds_check.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-ds_push.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo
+ -rm -f knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_basic.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_metadata.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_read.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-journal_write.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-knot_lmdb.Plo
+ -rm -f knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo
+ -rm -f knot/modules/cookies/$(DEPDIR)/la-cookies.Plo
+ -rm -f knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo
+ -rm -f knot/modules/dnsproxy/$(DEPDIR)/la-dnsproxy.Plo
+ -rm -f knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo
+ -rm -f knot/modules/dnstap/$(DEPDIR)/la-dnstap.Plo
+ -rm -f knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/la-geodb.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/la-geoip.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo
+ -rm -f knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo
+ -rm -f knot/modules/noudp/$(DEPDIR)/la-noudp.Plo
+ -rm -f knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/la-nsec_next.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/la-onlinesign.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo
+ -rm -f knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo
+ -rm -f knot/modules/probe/$(DEPDIR)/la-probe.Plo
+ -rm -f knot/modules/probe/$(DEPDIR)/libknotd_la-probe.Plo
+ -rm -f knot/modules/queryacl/$(DEPDIR)/la-queryacl.Plo
+ -rm -f knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/la-rrl.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo
+ -rm -f knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo
+ -rm -f knot/modules/stats/$(DEPDIR)/la-stats.Plo
+ -rm -f knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo
+ -rm -f knot/modules/synthrecord/$(DEPDIR)/la-synthrecord.Plo
+ -rm -f knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo
+ -rm -f knot/modules/whoami/$(DEPDIR)/la-whoami.Plo
+ -rm -f knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo
+ -rm -f knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-capture.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-query.Plo
+ -rm -f knot/query/$(DEPDIR)/libknotd_la-requestor.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-proxyv2.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-server.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo
+ -rm -f knot/server/$(DEPDIR)/libknotd_la-xdp-handler.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-acl.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-apply.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo
+ -rm -f knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo
+ -rm -f knot/worker/$(DEPDIR)/libknotd_la-pool.Plo
+ -rm -f knot/worker/$(DEPDIR)/libknotd_la-queue.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-adds_tree.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-adjust.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-backup.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-backup_dir.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-contents.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-digest.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-measure.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-node.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-serial.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-timers.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zone.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo
+ -rm -f knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-binary.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-crypto.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-digest.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-error.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-keyid.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-pem.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-random.Plo
+ -rm -f libdnssec/$(DEPDIR)/la-tsig.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-algorithm.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-convert.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-dnskey.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-ds.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-key.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-keytag.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-privkey.Plo
+ -rm -f libdnssec/key/$(DEPDIR)/la-simple.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-keystore.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-pkcs11.Plo
+ -rm -f libdnssec/keystore/$(DEPDIR)/la-pkcs8.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-bitmap.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-hash.Plo
+ -rm -f libdnssec/nsec/$(DEPDIR)/la-nsec.Plo
+ -rm -f libdnssec/p11/$(DEPDIR)/la-p11.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-bignum.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-dname.Plo
+ -rm -f libdnssec/shared/$(DEPDIR)/la-keyid_gnutls.Plo
+ -rm -f libdnssec/sign/$(DEPDIR)/la-der.Plo
+ -rm -f libdnssec/sign/$(DEPDIR)/la-sign.Plo
+ -rm -f libknot/$(DEPDIR)/la-codes.Plo
+ -rm -f libknot/$(DEPDIR)/la-cookies.Plo
+ -rm -f libknot/$(DEPDIR)/la-descriptor.Plo
+ -rm -f libknot/$(DEPDIR)/la-dname.Plo
+ -rm -f libknot/$(DEPDIR)/la-error.Plo
+ -rm -f libknot/$(DEPDIR)/la-rdataset.Plo
+ -rm -f libknot/$(DEPDIR)/la-rrset-dump.Plo
+ -rm -f libknot/$(DEPDIR)/la-rrset.Plo
+ -rm -f libknot/$(DEPDIR)/la-tsig-op.Plo
+ -rm -f libknot/$(DEPDIR)/la-tsig.Plo
+ -rm -f libknot/control/$(DEPDIR)/la-control.Plo
+ -rm -f libknot/db/$(DEPDIR)/la-db_lmdb.Plo
+ -rm -f libknot/db/$(DEPDIR)/la-db_trie.Plo
+ -rm -f libknot/packet/$(DEPDIR)/la-pkt.Plo
+ -rm -f libknot/packet/$(DEPDIR)/la-rrset-wire.Plo
+ -rm -f libknot/probe/$(DEPDIR)/la-data.Plo
+ -rm -f libknot/probe/$(DEPDIR)/la-probe.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-naptr.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-opt.Plo
+ -rm -f libknot/rrtype/$(DEPDIR)/la-tsig.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-bpf-kernel-obj.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-bpf-user.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-eth.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-quic.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-quic_conn.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-tcp.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-tcp_iobuf.Plo
+ -rm -f libknot/xdp/$(DEPDIR)/la-xdp.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-yparser.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypbody.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypformat.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-ypschema.Plo
+ -rm -f libknot/yparser/$(DEPDIR)/la-yptrafo.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-error.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-functions.Plo
+ -rm -f libzscanner/$(DEPDIR)/la-scanner.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-cert.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-exec.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-hex.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-https.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-lookup.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-msg.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-netio.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-params.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-quic.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-resolv.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-sign.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-tls.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-token.Plo
+ -rm -f utils/common/$(DEPDIR)/libknotus_la-util_conf.Plo
+ -rm -f utils/kcatalogprint/$(DEPDIR)/kcatalogprint-main.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_main.Po
+ -rm -f utils/kdig/$(DEPDIR)/kdig-kdig_params.Po
+ -rm -f utils/kdig/$(DEPDIR)/khost-kdig_exec.Po
+ -rm -f utils/kdig/$(DEPDIR)/khost-kdig_params.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-functions.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-main.Po
+ -rm -f utils/keymgr/$(DEPDIR)/keymgr-offline_ksk.Po
+ -rm -f utils/khost/$(DEPDIR)/khost-khost_main.Po
+ -rm -f utils/khost/$(DEPDIR)/khost-khost_params.Po
+ -rm -f utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-commands.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-interactive.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-main.Po
+ -rm -f utils/knotc/$(DEPDIR)/knotc-process.Po
+ -rm -f utils/knotd/$(DEPDIR)/knotd-main.Po
+ -rm -f utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_interactive.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po
+ -rm -f utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-ip_route.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-load_queries.Po
+ -rm -f utils/kxdpgun/$(DEPDIR)/kxdpgun-main.Po
+ -rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po
+ -rm -f utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po
+ -rm -f utils/kzonesign/$(DEPDIR)/kzonesign-main.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-include_libdnssecHEADERS \
+ uninstall-include_libknotdHEADERS \
+ uninstall-include_libzscannerHEADERS uninstall-libLTLIBRARIES \
+ uninstall-nobase_include_libknotHEADERS \
+ uninstall-pkgconfigDATA uninstall-pkglibLTLIBRARIES \
+ uninstall-sbinPROGRAMS
+
+.MAKE: $(am__recursive_targets) all check install install-am \
+ install-data-am install-exec install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-binPROGRAMS \
+ clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \
+ clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-include_libdnssecHEADERS \
+ install-include_libknotdHEADERS \
+ install-include_libzscannerHEADERS install-info \
+ install-info-am install-libLTLIBRARIES install-man \
+ install-nobase_include_libknotHEADERS install-pdf \
+ install-pdf-am install-pkgconfigDATA install-pkglibLTLIBRARIES \
+ install-ps install-ps-am install-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installcheck-binPROGRAMS \
+ installcheck-sbinPROGRAMS 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-include_libdnssecHEADERS \
+ uninstall-include_libknotdHEADERS \
+ uninstall-include_libzscannerHEADERS uninstall-libLTLIBRARIES \
+ uninstall-nobase_include_libknotHEADERS \
+ uninstall-pkgconfigDATA uninstall-pkglibLTLIBRARIES \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+@HAVE_LIBDNSTAP_TRUE@.proto.pb-c.c:
+@HAVE_LIBDNSTAP_TRUE@ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
+
+@HAVE_LIBDNSTAP_TRUE@.proto.pb-c.h:
+@HAVE_LIBDNSTAP_TRUE@ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
+
+@FAST_PARSER_TRUE@libzscanner/scanner.c: libzscanner/scanner.c.g2
+@FAST_PARSER_TRUE@ @cp $(srcdir)/$@.g2 $@
+@FAST_PARSER_TRUE@ @echo "NOTE: Compilation of scanner.c can take several minutes!"
+@FAST_PARSER_FALSE@libzscanner/scanner.c: libzscanner/scanner.c.t0
+@FAST_PARSER_FALSE@ @cp $(srcdir)/$@.t0 $@
+
+# Create storage and run-time directories
+@HAVE_DAEMON_TRUE@install-data-hook:
+@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@config_dir@
+@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@run_dir@
+@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@storage_dir@
+
+# 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/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..be9d1d1
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,277 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Passed CFLAGS from environment */
+#undef CONFIGURE_CFLAGS
+
+/* Params passed to configure */
+#undef CONFIGURE_PARAMS
+
+/* Configure summary */
+#undef CONFIGURE_SUMMARY
+
+/* Configuration DB mapsize. */
+#undef CONF_MAPSIZE
+
+/* POSIX capabilities available */
+#undef ENABLE_CAP_NG
+
+/* systemd D-Bus available */
+#undef ENABLE_DBUS
+
+/* PKCS #11 support available */
+#undef ENABLE_PKCS11
+
+/* Define to 1 to enable DoQ support using libngtcp2 and GnuTLS */
+#undef ENABLE_QUIC
+
+/* Use recvmmsg(). */
+#undef ENABLE_RECVMMSG
+
+/* Use SO_REUSEPORT(_LB). */
+#undef ENABLE_REUSEPORT
+
+/* Use systemd integration. */
+#undef ENABLE_SYSTEMD
+
+/* Use eXpress Data Path. */
+#undef ENABLE_XDP
+
+/* System is little-endian. */
+#undef ENDIANITY_LITTLE
+
+/* Define to 1 if you have the `accept4' function. */
+#undef HAVE_ACCEPT4
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#undef HAVE_ARPA_NAMESER_H
+
+/* Define to 1 if you have '__atomic' functions. */
+#undef HAVE_ATOMIC
+
+/* Define to 1 if you have the <bsd/string.h> header file. */
+#undef HAVE_BSD_STRING_H
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#undef HAVE_CLOCK_GETTIME
+
+/* Define if FreeBSD-like cpuset_t exists. */
+#undef HAVE_CPUSET_BSD
+
+/* Define if Linux-like cpu_set_t exists. */
+#undef HAVE_CPUSET_LINUX
+
+/* Define if cpuset_t and cpuset(3) exists. */
+#undef HAVE_CPUSET_NETBSD
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* GnuTLS ED25519 support available */
+#undef HAVE_ED25519
+
+/* GnuTLS ED448 support available */
+#undef HAVE_ED448
+
+/* epoll available */
+#undef HAVE_EPOLL
+
+/* Define to 1 if you have the `epoll_create' function. */
+#undef HAVE_EPOLL_CREATE
+
+/* explicit_bzero available */
+#undef HAVE_EXPLICIT_BZERO
+
+/* explicit_memset available */
+#undef HAVE_EXPLICIT_MEMSET
+
+/* gnutls_privkey_export_x509 available */
+#undef HAVE_EXPORT_X509
+
+/* Define to 1 if you have the `fgetln' function. */
+#undef HAVE_FGETLN
+
+/* Define to 1 if you have the `getline' function. */
+#undef HAVE_GETLINE
+
+/* gnutls_memset available */
+#undef HAVE_GNUTLS_MEMSET
+
+/* gnutls_early_cipher_get available */
+#undef HAVE_GNUTLS_QUIC
+
+/* GnuTLS reproducible signing available */
+#undef HAVE_GNUTLS_REPRODUCIBLE
+
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* libkqueue available */
+#undef HAVE_KQUEUE
+
+/* Define to 1 if you have the `malloc_trim' function. */
+#undef HAVE_MALLOC_TRIM
+
+/* Define to 1 to enable MaxMind DB. */
+#undef HAVE_MAXMINDDB
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#undef HAVE_NETINET_IN_H
+
+/* Define to 1 if you have the <pthread_np.h> header file. */
+#undef HAVE_PTHREAD_NP_H
+
+/* Define to 1 if you have the pthread_setaffinity_np function. */
+#undef HAVE_PTHREAD_SETAFFINITY_NP
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#undef HAVE_RESOLV_H
+
+/* Define to 1 if you have the `setgroups' function. */
+#undef HAVE_SETGROUPS
+
+/* gnutls_privkey_sign_data2 available */
+#undef HAVE_SIGN_DATA2
+
+/* Define to 1 if you have the <stdatomic.h> header file. */
+#undef HAVE_STDATOMIC_H
+
+/* 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 `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have '__sync' functions. */
+#undef HAVE_SYNC_ATOMIC
+
+/* Define to 1 if you have the `sysctlbyname' function. */
+#undef HAVE_SYSCTLBYNAME
+
+/* 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/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 or 0, depending whether the compiler supports simple visibility
+ declarations. */
+#undef HAVE_VISIBILITY
+
+/* Define to 1 to enable IDN support */
+#undef LIBIDN
+
+/* Define to proper libidn header */
+#undef LIBIDN_HEADER
+
+/* Define to 1 to enable DoH support */
+#undef LIBNGHTTP2
+
+/* 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 full name of this package. */
+#undef PACKAGE_NAME
+
+/* 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 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 to enable dnstap support for kdig */
+#undef USE_DNSTAP
+
+/* Use external libxdp and libbpf. */
+#undef USE_LIBXDP
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+
+
+/* Version number of package */
+#undef VERSION
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc
new file mode 100644
index 0000000..64a0279
--- /dev/null
+++ b/src/contrib/Makefile.inc
@@ -0,0 +1,269 @@
+noinst_LTLIBRARIES += libcontrib.la
+
+libcontrib_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY)
+libcontrib_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libcontrib_la_LIBADD = $(pthread_LIBS)
+libcontrib_LIBS = libcontrib.la
+if USE_GNUTLS_MEMSET
+libcontrib_la_CPPFLAGS += $(gnutls_CFLAGS)
+libcontrib_LIBS += $(gnutls_LIBS)
+endif USE_GNUTLS_MEMSET
+
+EXTRA_DIST += \
+ contrib/licenses/0BSD \
+ contrib/licenses/BSD-3-Clause \
+ contrib/licenses/LGPL-2.0 \
+ contrib/licenses/LGPL-2.1 \
+ contrib/licenses/MIT \
+ contrib/libbpf/LICENSE \
+ contrib/libngtcp2/LICENSE \
+ contrib/openbsd/LICENSE \
+ contrib/ucw/LICENSE \
+ contrib/url-parser/LICENSE \
+ contrib/url-parser/README.md \
+ contrib/dnstap/dnstap.proto
+
+libcontrib_la_SOURCES = \
+ contrib/asan.h \
+ contrib/base32hex.c \
+ contrib/base32hex.h \
+ contrib/base64.c \
+ contrib/base64.h \
+ contrib/base64url.c \
+ contrib/base64url.h \
+ contrib/conn_pool.c \
+ contrib/conn_pool.h \
+ contrib/color.h \
+ contrib/ctype.h \
+ contrib/files.c \
+ contrib/files.h \
+ contrib/getline.c \
+ contrib/getline.h \
+ contrib/json.c \
+ contrib/json.h \
+ contrib/macros.h \
+ contrib/mempattern.c \
+ contrib/mempattern.h \
+ contrib/musl/inet_ntop.c \
+ contrib/musl/inet_ntop.h \
+ contrib/net.c \
+ contrib/net.h \
+ contrib/os.h \
+ contrib/qp-trie/trie.c \
+ contrib/qp-trie/trie.h \
+ contrib/semaphore.c \
+ contrib/semaphore.h \
+ contrib/sockaddr.c \
+ contrib/sockaddr.h \
+ contrib/spinlock.h \
+ contrib/string.c \
+ contrib/string.h \
+ contrib/strtonum.h \
+ contrib/time.c \
+ contrib/time.h \
+ contrib/toeplitz.h \
+ contrib/tolower.h \
+ contrib/trim.h \
+ contrib/wire_ctx.h \
+ contrib/openbsd/siphash.c \
+ contrib/openbsd/siphash.h \
+ contrib/openbsd/strlcat.c \
+ contrib/openbsd/strlcat.h \
+ contrib/openbsd/strlcpy.c \
+ contrib/openbsd/strlcpy.h \
+ contrib/proxyv2/proxyv2.c \
+ contrib/proxyv2/proxyv2.h \
+ contrib/ucw/array-sort.h \
+ contrib/ucw/binsearch.h \
+ contrib/ucw/heap.c \
+ contrib/ucw/heap.h \
+ contrib/ucw/lists.c \
+ contrib/ucw/lists.h \
+ contrib/ucw/mempool.c \
+ contrib/ucw/mempool.h \
+ contrib/url-parser/url_parser.c \
+ contrib/url-parser/url_parser.h \
+ contrib/vpool/vpool.c \
+ contrib/vpool/vpool.h
+
+if EMBEDDED_LIBBPF
+noinst_LTLIBRARIES += libembbpf.la
+
+libembbpf_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(embedded_libbpf_CFLAGS)
+libembbpf_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libembbpf_LIBS = libembbpf.la $(embedded_libbpf_LIBS)
+
+libembbpf_la_SOURCES = \
+ contrib/libbpf/include/asm/barrier.h \
+ contrib/libbpf/include/linux/compiler.h \
+ contrib/libbpf/include/linux/err.h \
+ contrib/libbpf/include/linux/filter.h \
+ contrib/libbpf/include/linux/kernel.h \
+ contrib/libbpf/include/linux/list.h \
+ contrib/libbpf/include/linux/overflow.h \
+ contrib/libbpf/include/linux/ring_buffer.h \
+ contrib/libbpf/include/linux/types.h \
+ contrib/libbpf/include/uapi/linux/bpf_common.h \
+ contrib/libbpf/include/uapi/linux/bpf.h \
+ contrib/libbpf/include/uapi/linux/btf.h \
+ contrib/libbpf/include/uapi/linux/if_link.h \
+ contrib/libbpf/include/uapi/linux/if_xdp.h \
+ contrib/libbpf/include/uapi/linux/netlink.h \
+ contrib/libbpf/bpf/bpf.c \
+ contrib/libbpf/bpf/bpf.h \
+ contrib/libbpf/bpf/bpf_core_read.h \
+ contrib/libbpf/bpf/bpf_endian.h \
+ contrib/libbpf/bpf/bpf_helper_defs.h \
+ contrib/libbpf/bpf/bpf_helpers.h \
+ contrib/libbpf/bpf/bpf_prog_linfo.c \
+ contrib/libbpf/bpf/bpf_tracing.h \
+ contrib/libbpf/bpf/btf.c \
+ contrib/libbpf/bpf/btf.h \
+ contrib/libbpf/bpf/btf_dump.c \
+ contrib/libbpf/bpf/hashmap.c \
+ contrib/libbpf/bpf/hashmap.h \
+ contrib/libbpf/bpf/libbpf.c \
+ contrib/libbpf/bpf/libbpf.h \
+ contrib/libbpf/bpf/libbpf_errno.c \
+ contrib/libbpf/bpf/libbpf_internal.h \
+ contrib/libbpf/bpf/libbpf_probes.c \
+ contrib/libbpf/bpf/libbpf_util.h \
+ contrib/libbpf/bpf/netlink.c \
+ contrib/libbpf/bpf/nlattr.c \
+ contrib/libbpf/bpf/nlattr.h \
+ contrib/libbpf/bpf/str_error.c \
+ contrib/libbpf/bpf/str_error.h \
+ contrib/libbpf/bpf/xsk.c \
+ contrib/libbpf/bpf/xsk.h
+endif EMBEDDED_LIBBPF
+
+if HAVE_LIBDNSTAP
+noinst_LTLIBRARIES += libdnstap.la
+
+libdnstap_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(DNSTAP_CFLAGS)
+libdnstap_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libdnstap_LIBS = libdnstap.la $(DNSTAP_LIBS)
+
+SUFFIXES = .proto .pb-c.c .pb-c.h
+
+.proto.pb-c.c:
+ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
+
+.proto.pb-c.h:
+ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $<
+
+libdnstap_la_SOURCES = \
+ contrib/dnstap/convert.c \
+ contrib/dnstap/convert.h \
+ contrib/dnstap/dnstap.c \
+ contrib/dnstap/dnstap.h \
+ contrib/dnstap/message.c \
+ contrib/dnstap/message.h \
+ contrib/dnstap/reader.c \
+ contrib/dnstap/reader.h \
+ contrib/dnstap/writer.c \
+ contrib/dnstap/writer.h
+
+nodist_libdnstap_la_SOURCES = \
+ contrib/dnstap/dnstap.pb-c.c \
+ contrib/dnstap/dnstap.pb-c.h
+
+BUILT_SOURCES += $(nodist_libdnstap_la_SOURCES)
+CLEANFILES += $(nodist_libdnstap_la_SOURCES)
+endif HAVE_LIBDNSTAP
+
+if EMBEDDED_LIBNGTCP2
+noinst_LTLIBRARIES += libembngtcp2.la
+
+libembngtcp2_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \
+ $(embedded_libngtcp2_CFLAGS) $(gnutls_CFLAGS)
+libembngtcp2_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libembngtcp2_LIBS = libembngtcp2.la $(embedded_libngtcp2_LIBS) $(gnutls_LIBS)
+
+libembngtcp2_la_SOURCES = \
+ contrib/libngtcp2/ngtcp2/crypto/gnutls.c \
+ contrib/libngtcp2/ngtcp2/crypto/shared.c \
+ contrib/libngtcp2/ngtcp2/crypto/shared.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h \
+ contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h \
+ contrib/libngtcp2/ngtcp2/version.h
+endif EMBEDDED_LIBNGTCP2
diff --git a/src/contrib/asan.h b/src/contrib/asan.h
new file mode 100644
index 0000000..5feb2c1
--- /dev/null
+++ b/src/contrib/asan.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2015 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/>.
+ */
+
+#pragma once
+
+/*
+ * see sanitizer/asan_interface.h in compiler-rt (LLVM)
+ */
+#ifndef __has_feature
+ #define __has_feature(feature) 0
+#endif
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+ void __asan_poison_memory_region(void const volatile *addr, size_t size);
+ void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+ #define ASAN_POISON_MEMORY_REGION(addr, size) \
+ __asan_poison_memory_region((addr), (size))
+ #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
+ __asan_unpoison_memory_region((addr), (size))
+#else
+ #define ASAN_POISON_MEMORY_REGION(addr, size) \
+ ((void)(addr), (void)(size))
+ #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
+ ((void)(addr), (void)(size))
+#endif
diff --git a/src/contrib/base32hex.c b/src/contrib/base32hex.c
new file mode 100644
index 0000000..ad216d2
--- /dev/null
+++ b/src/contrib/base32hex.c
@@ -0,0 +1,353 @@
+/* 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 "contrib/base32hex.h"
+#include "libknot/errcode.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/*! \brief Maximal length of binary input to Base32hex encoding. */
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 8) * 5)
+
+/*! \brief Base32hex padding character. */
+static const uint8_t base32hex_pad = '=';
+/*! \brief Base32hex alphabet. */
+static const uint8_t base32hex_enc[] = "0123456789abcdefghijklmnopqrstuv";
+
+/*! \brief Indicates bad Base32hex character. */
+#define KO 255
+/*! \brief Indicates Base32hex padding character. */
+#define PD 32
+
+/*! \brief Transformation and validation table for decoding Base32hex. */
+static const uint8_t base32hex_dec[256] = {
+ [ 0] = KO, [ 43] = KO, ['V'] = 31, [129] = KO, [172] = KO, [215] = KO,
+ [ 1] = KO, [ 44] = KO, ['W'] = KO, [130] = KO, [173] = KO, [216] = KO,
+ [ 2] = KO, [ 45] = KO, ['X'] = KO, [131] = KO, [174] = KO, [217] = KO,
+ [ 3] = KO, [ 46] = KO, ['Y'] = KO, [132] = KO, [175] = KO, [218] = KO,
+ [ 4] = KO, [ 47] = KO, ['Z'] = KO, [133] = KO, [176] = KO, [219] = KO,
+ [ 5] = KO, ['0'] = 0, [ 91] = KO, [134] = KO, [177] = KO, [220] = KO,
+ [ 6] = KO, ['1'] = 1, [ 92] = KO, [135] = KO, [178] = KO, [221] = KO,
+ [ 7] = KO, ['2'] = 2, [ 93] = KO, [136] = KO, [179] = KO, [222] = KO,
+ [ 8] = KO, ['3'] = 3, [ 94] = KO, [137] = KO, [180] = KO, [223] = KO,
+ [ 9] = KO, ['4'] = 4, [ 95] = KO, [138] = KO, [181] = KO, [224] = KO,
+ [ 10] = KO, ['5'] = 5, [ 96] = KO, [139] = KO, [182] = KO, [225] = KO,
+ [ 11] = KO, ['6'] = 6, ['a'] = 10, [140] = KO, [183] = KO, [226] = KO,
+ [ 12] = KO, ['7'] = 7, ['b'] = 11, [141] = KO, [184] = KO, [227] = KO,
+ [ 13] = KO, ['8'] = 8, ['c'] = 12, [142] = KO, [185] = KO, [228] = KO,
+ [ 14] = KO, ['9'] = 9, ['d'] = 13, [143] = KO, [186] = KO, [229] = KO,
+ [ 15] = KO, [ 58] = KO, ['e'] = 14, [144] = KO, [187] = KO, [230] = KO,
+ [ 16] = KO, [ 59] = KO, ['f'] = 15, [145] = KO, [188] = KO, [231] = KO,
+ [ 17] = KO, [ 60] = KO, ['g'] = 16, [146] = KO, [189] = KO, [232] = KO,
+ [ 18] = KO, ['='] = PD, ['h'] = 17, [147] = KO, [190] = KO, [233] = KO,
+ [ 19] = KO, [ 62] = KO, ['i'] = 18, [148] = KO, [191] = KO, [234] = KO,
+ [ 20] = KO, [ 63] = KO, ['j'] = 19, [149] = KO, [192] = KO, [235] = KO,
+ [ 21] = KO, [ 64] = KO, ['k'] = 20, [150] = KO, [193] = KO, [236] = KO,
+ [ 22] = KO, ['A'] = 10, ['l'] = 21, [151] = KO, [194] = KO, [237] = KO,
+ [ 23] = KO, ['B'] = 11, ['m'] = 22, [152] = KO, [195] = KO, [238] = KO,
+ [ 24] = KO, ['C'] = 12, ['n'] = 23, [153] = KO, [196] = KO, [239] = KO,
+ [ 25] = KO, ['D'] = 13, ['o'] = 24, [154] = KO, [197] = KO, [240] = KO,
+ [ 26] = KO, ['E'] = 14, ['p'] = 25, [155] = KO, [198] = KO, [241] = KO,
+ [ 27] = KO, ['F'] = 15, ['q'] = 26, [156] = KO, [199] = KO, [242] = KO,
+ [ 28] = KO, ['G'] = 16, ['r'] = 27, [157] = KO, [200] = KO, [243] = KO,
+ [ 29] = KO, ['H'] = 17, ['s'] = 28, [158] = KO, [201] = KO, [244] = KO,
+ [ 30] = KO, ['I'] = 18, ['t'] = 29, [159] = KO, [202] = KO, [245] = KO,
+ [ 31] = KO, ['J'] = 19, ['u'] = 30, [160] = KO, [203] = KO, [246] = KO,
+ [ 32] = KO, ['K'] = 20, ['v'] = 31, [161] = KO, [204] = KO, [247] = KO,
+ [ 33] = KO, ['L'] = 21, ['w'] = KO, [162] = KO, [205] = KO, [248] = KO,
+ [ 34] = KO, ['M'] = 22, ['x'] = KO, [163] = KO, [206] = KO, [249] = KO,
+ [ 35] = KO, ['N'] = 23, ['y'] = KO, [164] = KO, [207] = KO, [250] = KO,
+ [ 36] = KO, ['O'] = 24, ['z'] = KO, [165] = KO, [208] = KO, [251] = KO,
+ [ 37] = KO, ['P'] = 25, [123] = KO, [166] = KO, [209] = KO, [252] = KO,
+ [ 38] = KO, ['Q'] = 26, [124] = KO, [167] = KO, [210] = KO, [253] = KO,
+ [ 39] = KO, ['R'] = 27, [125] = KO, [168] = KO, [211] = KO, [254] = KO,
+ [ 40] = KO, ['S'] = 28, [126] = KO, [169] = KO, [212] = KO, [255] = KO,
+ [ 41] = KO, ['T'] = 29, [127] = KO, [170] = KO, [213] = KO,
+ [ 42] = KO, ['U'] = 30, [128] = KO, [171] = KO, [214] = KO,
+};
+
+int32_t knot_base32hex_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 KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 4) / 5) * 8) {
+ return KNOT_ERANGE;
+ }
+
+ uint8_t rest_len = in_len % 5;
+ const uint8_t *stop = in + in_len - rest_len;
+ uint8_t *text = out;
+
+ // Encoding loop takes 5 bytes and creates 8 characters.
+ while (in < stop) {
+ text[0] = base32hex_enc[in[0] >> 3];
+ text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6];
+ text[2] = base32hex_enc[(in[1] & 0x3E) >> 1];
+ text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4];
+ text[4] = base32hex_enc[(in[2] & 0x0F) << 1 | in[3] >> 7];
+ text[5] = base32hex_enc[(in[3] & 0x7C) >> 2];
+ text[6] = base32hex_enc[(in[3] & 0x03) << 3 | in[4] >> 5];
+ text[7] = base32hex_enc[in[4] & 0x1F];
+ text += 8;
+ in += 5;
+ }
+
+ // Processing of padding, if any.
+ switch (rest_len) {
+ case 4:
+ text[0] = base32hex_enc[in[0] >> 3];
+ text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6];
+ text[2] = base32hex_enc[(in[1] & 0x3E) >> 1];
+ text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4];
+ text[4] = base32hex_enc[(in[2] & 0x0F) << 1 | in[3] >> 7];
+ text[5] = base32hex_enc[(in[3] & 0x7C) >> 2];
+ text[6] = base32hex_enc[(in[3] & 0x03) << 3];
+ text[7] = base32hex_pad;
+ text += 8;
+ break;
+ case 3:
+ text[0] = base32hex_enc[in[0] >> 3];
+ text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6];
+ text[2] = base32hex_enc[(in[1] & 0x3E) >> 1];
+ text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4];
+ text[4] = base32hex_enc[(in[2] & 0x0F) << 1];
+ text[5] = base32hex_pad;
+ text[6] = base32hex_pad;
+ text[7] = base32hex_pad;
+ text += 8;
+ break;
+ case 2:
+ text[0] = base32hex_enc[in[0] >> 3];
+ text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6];
+ text[2] = base32hex_enc[(in[1] & 0x3E) >> 1];
+ text[3] = base32hex_enc[(in[1] & 0x01) << 4];
+ text[4] = base32hex_pad;
+ text[5] = base32hex_pad;
+ text[6] = base32hex_pad;
+ text[7] = base32hex_pad;
+ text += 8;
+ break;
+ case 1:
+ text[0] = base32hex_enc[in[0] >> 3];
+ text[1] = base32hex_enc[(in[0] & 0x07) << 2];
+ text[2] = base32hex_pad;
+ text[3] = base32hex_pad;
+ text[4] = base32hex_pad;
+ text[5] = base32hex_pad;
+ text[6] = base32hex_pad;
+ text[7] = base32hex_pad;
+ text += 8;
+ break;
+ }
+
+ return (text - out);
+}
+
+int32_t knot_base32hex_encode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN) {
+ return KNOT_ERANGE;
+ }
+
+ // Compute output buffer length.
+ uint32_t out_len = ((in_len + 4) / 5) * 8;
+
+ // Allocate output buffer.
+ *out = malloc(out_len);
+ if (*out == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Encode data.
+ int32_t ret = knot_base32hex_encode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
+
+int32_t knot_base32hex_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 KNOT_EINVAL;
+ }
+ if (in_len > INT32_MAX || out_len < ((in_len + 7) / 8) * 5) {
+ return KNOT_ERANGE;
+ }
+ if ((in_len % 8) != 0) {
+ return KNOT_BASE32HEX_ESIZE;
+ }
+
+ const uint8_t *stop = in + in_len;
+ uint8_t *bin = out;
+ uint8_t pad_len = 0;
+ uint8_t c1, c2, c3, c4, c5, c6, c7, c8;
+
+ // Decoding loop takes 8 characters and creates 5 bytes.
+ while (in < stop) {
+ // Filling and transforming 8 Base32hex chars.
+ c1 = base32hex_dec[in[0]];
+ c2 = base32hex_dec[in[1]];
+ c3 = base32hex_dec[in[2]];
+ c4 = base32hex_dec[in[3]];
+ c5 = base32hex_dec[in[4]];
+ c6 = base32hex_dec[in[5]];
+ c7 = base32hex_dec[in[6]];
+ c8 = base32hex_dec[in[7]];
+
+ // Check 8. char if is bad or padding.
+ if (c8 >= PD) {
+ if (c8 == PD && pad_len == 0) {
+ pad_len = 1;
+ } else {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // Check 7. char if is bad or padding (if so, 6. must be too).
+ if (c7 >= PD) {
+ if (c7 == PD && c6 == PD && pad_len == 1) {
+ pad_len = 3;
+ } else {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // Check 6. char if is bad or padding.
+ if (c6 >= PD) {
+ if (!(c6 == PD && pad_len == 3)) {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // Check 5. char if is bad or padding.
+ if (c5 >= PD) {
+ if (c5 == PD && pad_len == 3) {
+ pad_len = 4;
+ } else {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // Check 4. char if is bad or padding (if so, 3. must be too).
+ if (c4 >= PD) {
+ if (c4 == PD && c3 == PD && pad_len == 4) {
+ pad_len = 6;
+ } else {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // Check 3. char if is bad or padding.
+ if (c3 >= PD) {
+ if (!(c3 == PD && pad_len == 6)) {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+ }
+
+ // 1. and 2. chars must not be padding.
+ if (c2 >= PD || c1 >= PD) {
+ return KNOT_BASE32HEX_ECHAR;
+ }
+
+ // Computing of output data based on padding length.
+ switch (pad_len) {
+ case 0:
+ bin[4] = (c7 << 5) + c8;
+ // FALLTHROUGH
+ case 1:
+ bin[3] = (c5 << 7) + (c6 << 2) + (c7 >> 3);
+ // FALLTHROUGH
+ case 3:
+ bin[2] = (c4 << 4) + (c5 >> 1);
+ // FALLTHROUGH
+ case 4:
+ bin[1] = (c2 << 6) + (c3 << 1) + (c4 >> 4);
+ // FALLTHROUGH
+ case 6:
+ bin[0] = (c1 << 3) + (c2 >> 2);
+ }
+
+ // Update output end.
+ switch (pad_len) {
+ case 0:
+ bin += 5;
+ break;
+ case 1:
+ bin += 4;
+ break;
+ case 3:
+ bin += 3;
+ break;
+ case 4:
+ bin += 2;
+ break;
+ case 6:
+ bin += 1;
+ break;
+ }
+
+ in += 8;
+ }
+
+ return (bin - out);
+}
+
+int32_t knot_base32hex_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Compute output buffer length.
+ uint32_t out_len = ((in_len + 7) / 8) * 5;
+
+ // Allocate output buffer.
+ *out = malloc(out_len);
+ if (*out == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Decode data.
+ int32_t ret = knot_base32hex_decode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
diff --git a/src/contrib/base32hex.h b/src/contrib/base32hex.h
new file mode 100644
index 0000000..4a6fd8f
--- /dev/null
+++ b/src/contrib/base32hex.h
@@ -0,0 +1,104 @@
+/* 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 Base32hex implementation (RFC 4648).
+ *
+ * \note Input Base32hex string can contain a-v characters. These characters
+ * are considered as A-V equivalent. Lower-case variant is used for encoding!
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Encodes binary data using Base32hex.
+ *
+ * \note Output data buffer contains Base32hex 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 knot_base32hex_encode(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t *out,
+ const uint32_t out_len);
+
+/*!
+ * \brief Encodes binary data using Base32hex and output stores to own buffer.
+ *
+ * \note Output data buffer contains Base32hex 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 knot_base32hex_encode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out);
+
+/*!
+ * \brief Decodes text data using Base32hex.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base32hex 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 knot_base32hex_decode(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t *out,
+ const uint32_t out_len);
+
+/*!
+ * \brief Decodes text data using Base32hex and output stores to own buffer.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base32hex 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 knot_base32hex_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out);
diff --git a/src/contrib/base64.c b/src/contrib/base64.c
new file mode 100644
index 0000000..ab4a560
--- /dev/null
+++ b/src/contrib/base64.c
@@ -0,0 +1,272 @@
+/* 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 "contrib/base64.h"
+#include "libknot/errcode.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/*! \brief Maximal length of binary input to Base64 encoding. */
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3)
+
+/*! \brief Base64 padding character. */
+static const uint8_t base64_pad = '=';
+/*! \brief Base64 alphabet. */
+static const uint8_t base64_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 base64_dec[256] = {
+ [ 0] = KO, ['+'] = 62, ['V'] = 21, [129] = KO, [172] = KO, [215] = KO,
+ [ 1] = KO, [ 44] = KO, ['W'] = 22, [130] = KO, [173] = KO, [216] = KO,
+ [ 2] = KO, [ 45] = KO, ['X'] = 23, [131] = KO, [174] = KO, [217] = KO,
+ [ 3] = KO, [ 46] = KO, ['Y'] = 24, [132] = KO, [175] = KO, [218] = KO,
+ [ 4] = KO, ['/'] = 63, ['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, [ 95] = KO, [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, ['='] = PD, ['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 knot_base64_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 KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+ return KNOT_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] = base64_enc[in[0] >> 2];
+ text[1] = base64_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+ text[2] = base64_enc[(in[1] & 0x0F) << 2 | in[2] >> 6];
+ text[3] = base64_enc[in[2] & 0x3F];
+ text += 4;
+ in += 3;
+ }
+
+ // Processing of padding, if any.
+ switch (rest_len) {
+ case 2:
+ text[0] = base64_enc[in[0] >> 2];
+ text[1] = base64_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+ text[2] = base64_enc[(in[1] & 0x0F) << 2];
+ text[3] = base64_pad;
+ text += 4;
+ break;
+ case 1:
+ text[0] = base64_enc[in[0] >> 2];
+ text[1] = base64_enc[(in[0] & 0x03) << 4];
+ text[2] = base64_pad;
+ text[3] = base64_pad;
+ text += 4;
+ break;
+ }
+
+ return (text - out);
+}
+
+int32_t knot_base64_encode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN) {
+ return KNOT_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 KNOT_ENOMEM;
+ }
+
+ // Encode data.
+ int32_t ret = knot_base64_encode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
+
+int32_t knot_base64_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 KNOT_EINVAL;
+ }
+ if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+ return KNOT_ERANGE;
+ }
+ if ((in_len % 4) != 0) {
+ return KNOT_BASE64_ESIZE;
+ }
+
+ 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 = base64_dec[in[0]];
+ c2 = base64_dec[in[1]];
+ c3 = base64_dec[in[2]];
+ c4 = base64_dec[in[3]];
+
+ // Check 4. char if is bad or padding.
+ if (c4 >= PD) {
+ if (c4 == PD && pad_len == 0) {
+ pad_len = 1;
+ } else {
+ return KNOT_BASE64_ECHAR;
+ }
+ }
+
+ // Check 3. char if is bad or padding.
+ if (c3 >= PD) {
+ if (c3 == PD && pad_len == 1) {
+ pad_len = 2;
+ } else {
+ return KNOT_BASE64_ECHAR;
+ }
+ }
+
+ // Check 1. and 2. chars if are not padding.
+ if (c2 >= PD || c1 >= PD) {
+ return KNOT_BASE64_ECHAR;
+ }
+
+ // 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 knot_base64_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_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 KNOT_ENOMEM;
+ }
+
+ // Decode data.
+ int32_t ret = knot_base64_decode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
diff --git a/src/contrib/base64.h b/src/contrib/base64.h
new file mode 100644
index 0000000..0a98733
--- /dev/null
+++ b/src/contrib/base64.h
@@ -0,0 +1,103 @@
+/* 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 Base64 implementation (RFC 4648).
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \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 knot_base64_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 knot_base64_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 knot_base64_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 knot_base64_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out);
+
+/*! @} */
diff --git a/src/contrib/base64url.c b/src/contrib/base64url.c
new file mode 100644
index 0000000..34e2bbe
--- /dev/null
+++ b/src/contrib/base64url.c
@@ -0,0 +1,287 @@
+/* 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 "contrib/base64url.h"
+#include "libknot/errcode.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <ctype.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,
+ ['%'] = 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 knot_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 KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+ return KNOT_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 knot_base64url_encode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (in_len > MAX_BIN_DATA_LEN) {
+ return KNOT_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 KNOT_ENOMEM;
+ }
+
+ // Encode data.
+ int32_t ret = knot_base64url_encode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
+
+int32_t knot_base64url_decode(const uint8_t *in,
+ uint32_t in_len,
+ uint8_t *out,
+ const uint32_t out_len)
+{
+ // Checking inputs.
+ if (in == NULL || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // cut up to two "%3d" from the end of input
+ int pad3d = 0;
+ const uint8_t *end = in + in_len;
+ char *perc3d = "d3%d3%", *stop3d = perc3d + 6;
+ while (end != in && perc3d != stop3d && tolower(*--end) == *perc3d) {
+ if (*perc3d++ == '%') {
+ in_len -= 3;
+ pad3d++;
+ }
+ }
+
+ if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+ return KNOT_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 = base64url_dec[in[1]] ;
+ c3 = (in + 2 < stop) ? base64url_dec[in[2]] : PD;
+ c4 = (in + 3 < stop) ? base64url_dec[in[3]] : PD;
+
+ // Check 1. and 2. chars if are not padding
+ if (c1 >= PD || c2 >= PD) {
+ return KNOT_BASE64_ECHAR;
+ }
+ // Check 3. char if is bad or padding.
+ else if (c3 >= PD) {
+ if (c3 == PD) {
+ pad_len = 2;
+ } else {
+ return KNOT_BASE64_ECHAR;
+ }
+ }
+ // Check 3. char if is bad or padding.
+ else if (c4 >= PD) {
+ if (c4 == PD) {
+ pad_len = 1;
+ } else {
+ return KNOT_BASE64_ECHAR;
+ }
+ }
+
+ if (pad_len > 0 && in <= stop - 4) {
+ return KNOT_BASE64_ECHAR;
+ }
+
+ // 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;
+ goto end;
+ case 2:
+ bin += 1;
+ goto end;
+ }
+
+ in += 4;
+ }
+
+end:
+ if (pad3d > pad_len) {
+ return KNOT_BASE64_ECHAR;
+ }
+ return (bin - out);
+}
+
+int32_t knot_base64url_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ // Checking inputs.
+ if (out == NULL) {
+ return KNOT_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 KNOT_ENOMEM;
+ }
+
+ // Decode data.
+ int32_t ret = knot_base64url_decode(in, in_len, *out, out_len);
+ if (ret < 0) {
+ free(*out);
+ *out = NULL;
+ }
+
+ return ret;
+}
diff --git a/src/contrib/base64url.h b/src/contrib/base64url.h
new file mode 100644
index 0000000..ba4bb43
--- /dev/null
+++ b/src/contrib/base64url.h
@@ -0,0 +1,103 @@
+/* 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).
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \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 knot_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 knot_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 knot_base64url_decode(const uint8_t *in,
+ 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 knot_base64url_decode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out);
+
+/*! @} */
diff --git a/src/contrib/color.h b/src/contrib/color.h
new file mode 100644
index 0000000..6a7ff6a
--- /dev/null
+++ b/src/contrib/color.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#define COL_RST(active) ((active) ? "\x1B[0m" : "")
+
+#define COL_BOLD(active) ((active) ? "\x1B[1m" : "")
+#define COL_DIM(active) ((active) ? "\x1B[2m" : "")
+#define COL_UNDR(active) ((active) ? "\x1B[4m" : "")
+
+#define COL_RED(active) ((active) ? "\x1B[31m" : "")
+#define COL_GRN(active) ((active) ? "\x1B[32m" : "")
+#define COL_YELW(active) ((active) ? "\x1B[93m" : "")
+#define COL_BLUE(active) ((active) ? "\x1B[34m" : "")
+#define COL_MGNT(active) ((active) ? "\x1B[35m" : "")
+#define COL_CYAN(active) ((active) ? "\x1B[36m" : "")
+#define COL_WHIT(active) ((active) ? "\x1B[97m" : "")
diff --git a/src/contrib/conn_pool.c b/src/contrib/conn_pool.c
new file mode 100644
index 0000000..8367555
--- /dev/null
+++ b/src/contrib/conn_pool.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "contrib/conn_pool.h"
+
+#include "contrib/sockaddr.h"
+
+conn_pool_t *global_conn_pool = NULL;
+
+static int pool_pop(conn_pool_t *pool, size_t i);
+
+/*!
+ * \brief Try to get an open connection older than specified timestamp.
+ *
+ * \param pool Pool to search in.
+ * \param older_than Timestamp that the connection must be older than.
+ * \param next_oldest Out: the timestamp of the oldest connection (other than the returned).
+ *
+ * \return -1 if error (no such connection), >= 0 connection file descriptor.
+ *
+ * \warning The returned connection is not necessarily the oldest one.
+ */
+static int get_old(conn_pool_t *pool,
+ knot_time_t older_than,
+ knot_time_t *next_oldest)
+{
+ assert(pool);
+
+ *next_oldest = 0;
+
+ int fd = -1;
+ pthread_mutex_lock(&pool->mutex);
+
+ for (size_t i = 0; i < pool->capacity; i++) {
+ knot_time_t la = pool->conns[i].last_active;
+ if (fd == -1 && knot_time_cmp(la, older_than) < 0) {
+ fd = pool_pop(pool, i);
+ } else if (knot_time_cmp(la, *next_oldest) < 0) {
+ *next_oldest = la;
+ }
+ }
+
+ pthread_mutex_unlock(&pool->mutex);
+ return fd;
+}
+
+static void *closing_thread(void *_arg)
+{
+ conn_pool_t *pool = _arg;
+
+ while (true) {
+ knot_time_t now = knot_time(), next = 0;
+ knot_timediff_t timeout = conn_pool_timeout(pool, 0);
+ assert(timeout != 0);
+
+ while (true) {
+ int old_fd = get_old(pool, now - timeout + 1, &next);
+ if (old_fd >= 0) {
+ close(old_fd);
+ } else {
+ break;
+ }
+ }
+
+ if (next == 0) {
+ sleep(timeout);
+ } else {
+ sleep(next + timeout - now);
+ }
+ }
+
+ return NULL; // we never get here since the thread will be cancelled instead
+}
+
+conn_pool_t *conn_pool_init(size_t capacity, knot_timediff_t timeout)
+{
+ if (capacity == 0 || timeout == 0) {
+ return NULL;
+ }
+
+ conn_pool_t *pool = calloc(1, sizeof(*pool) + capacity * sizeof(pool->conns[0]));
+ if (pool != NULL) {
+ pool->capacity = capacity;
+ pool->timeout = timeout;
+ if (pthread_mutex_init(&pool->mutex, 0) != 0) {
+ free(pool);
+ return NULL;
+ }
+ if (pthread_create(&pool->closing_thread, NULL, closing_thread, pool) != 0) {
+ pthread_mutex_destroy(&pool->mutex);
+ free(pool);
+ return NULL;
+ }
+ }
+ return pool;
+}
+
+void conn_pool_deinit(conn_pool_t *pool)
+{
+ if (pool != NULL) {
+ pthread_cancel(pool->closing_thread);
+ pthread_join(pool->closing_thread, NULL);
+
+ int fd;
+ knot_time_t unused;
+ while ((fd = get_old(pool, 0, &unused)) >= 0) {
+ close(fd);
+ }
+
+ pthread_mutex_destroy(&pool->mutex);
+ free(pool);
+ }
+}
+
+knot_timediff_t conn_pool_timeout(conn_pool_t *pool,
+ knot_timediff_t new_timeout)
+{
+ if (pool == NULL) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&pool->mutex);
+
+ knot_timediff_t prev = pool->timeout;
+ if (new_timeout != 0) {
+ pool->timeout = new_timeout;
+ }
+
+ pthread_mutex_unlock(&pool->mutex);
+ return prev;
+}
+
+static int pool_pop(conn_pool_t *pool, size_t i)
+{
+ conn_pool_memb_t *conn = &pool->conns[i];
+ assert(conn->last_active != 0);
+ assert(pool->usage > 0);
+ int fd = conn->fd;
+ memset(conn, 0, sizeof(*conn));
+ pool->usage--;
+ return fd;
+}
+
+int conn_pool_get(conn_pool_t *pool,
+ struct sockaddr_storage *src,
+ struct sockaddr_storage *dst)
+{
+ if (pool == NULL) {
+ return -1;
+ }
+
+ int fd = -1;
+ pthread_mutex_lock(&pool->mutex);
+
+ for (size_t i = 0; i < pool->capacity; i++) {
+ if (pool->conns[i].last_active != 0 &&
+ sockaddr_cmp(&pool->conns[i].dst, dst, false) == 0 &&
+ sockaddr_cmp(&pool->conns[i].src, src, true) == 0) {
+ fd = pool_pop(pool, i);
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&pool->mutex);
+
+ if (fd >= 0) {
+ uint8_t unused;
+ int peek = recv(fd, &unused, 1, MSG_PEEK | MSG_DONTWAIT);
+ if (peek >= 0) { // closed or pending data
+ close(fd);
+ fd = -1;
+ }
+ }
+
+ return fd;
+}
+
+static void pool_push(conn_pool_t *pool, size_t i,
+ struct sockaddr_storage *src,
+ struct sockaddr_storage *dst,
+ int fd)
+{
+ conn_pool_memb_t *conn = &pool->conns[i];
+ assert(conn->last_active == 0);
+ assert(pool->usage < pool->capacity);
+ conn->last_active = knot_time();
+ conn->fd = fd;
+ memcpy(&conn->src, src, sizeof(conn->src));
+ memcpy(&conn->dst, dst, sizeof(conn->dst));
+ pool->usage++;
+}
+
+int conn_pool_put(conn_pool_t *pool,
+ struct sockaddr_storage *src,
+ struct sockaddr_storage *dst,
+ int fd)
+{
+ if (pool == NULL || pool->capacity == 0) {
+ return fd;
+ }
+
+ knot_time_t oldest_time = 0;
+ size_t oldest_i = pool->capacity;
+
+ pthread_mutex_lock(&pool->mutex);
+
+ for (size_t i = 0; i < pool->capacity; i++) {
+ knot_time_t la = pool->conns[i].last_active;
+ if (la == 0) {
+ pool_push(pool, i, src, dst, fd);
+ pthread_mutex_unlock(&pool->mutex);
+ return -1;
+ } else if (knot_time_cmp(la, oldest_time) < 0) {
+ oldest_time = la;
+ oldest_i = i;
+ }
+ }
+
+ assert(oldest_i < pool->capacity);
+ int oldest_fd = pool_pop(pool, oldest_i);
+ pool_push(pool, oldest_i, src, dst, fd);
+ pthread_mutex_unlock(&pool->mutex);
+ return oldest_fd;
+}
diff --git a/src/contrib/conn_pool.h b/src/contrib/conn_pool.h
new file mode 100644
index 0000000..f1f2c6c
--- /dev/null
+++ b/src/contrib/conn_pool.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "contrib/time.h"
+
+typedef struct {
+ struct sockaddr_storage src;
+ struct sockaddr_storage dst;
+ int fd;
+ knot_time_t last_active;
+} conn_pool_memb_t;
+
+typedef struct {
+ size_t capacity;
+ size_t usage;
+ knot_timediff_t timeout;
+ pthread_mutex_t mutex;
+ pthread_t closing_thread;
+ conn_pool_memb_t conns[];
+} conn_pool_t;
+
+extern conn_pool_t *global_conn_pool;
+
+/*!
+ * \brief Allocate connection pool.
+ *
+ * \param capacity Connection pool capacity (must be positive number).
+ * \param timeout Connection timeout (must be positive number).
+ *
+ * \return Connection pool or NULL if error.
+ */
+conn_pool_t *conn_pool_init(size_t capacity, knot_timediff_t timeout);
+
+/*!
+ * \brief Deallocate the pool, close all connections, terminate closing thread.
+ *
+ * \param pool Connection pool.
+ */
+void conn_pool_deinit(conn_pool_t *pool);
+
+/*!
+ * \brief Get and/or set connection timeout.
+ *
+ * \param pool Connection pool.
+ * \param new_timeout Optional: set new timeout (if positive number).
+ *
+ * \return Previous value of timeout.
+ */
+knot_timediff_t conn_pool_timeout(conn_pool_t *pool,
+ knot_timediff_t new_timeout);
+
+/*!
+ * \brief Try to get an open connection if present, check if alive.
+ *
+ * \param pool Pool to search in.
+ * \param src Connection source address.
+ * \param dst Connection destination address.
+ *
+ * \retval -1 If error (no such connection).
+ * \return >= 0 File descriptor of the connection.
+ */
+int conn_pool_get(conn_pool_t *pool,
+ struct sockaddr_storage *src,
+ struct sockaddr_storage *dst);
+
+/*!
+ * \brief Put an open connection to the pool, possibly displacing the oldest one there.
+ *
+ * \param pool Pool to insert into.
+ * \param src Connestion source address.
+ * \param dst Connection destination adress.
+ * \param fd Connection file descriptor.
+ *
+ * \retval -1 If connection stored to free slot.
+ * \retval fd If not able to store connection.
+ * \return >= 0 File descriptor of the displaced old connection.
+ */
+int conn_pool_put(conn_pool_t *pool,
+ struct sockaddr_storage *src,
+ struct sockaddr_storage *dst,
+ int fd);
diff --git a/src/contrib/ctype.h b/src/contrib/ctype.h
new file mode 100644
index 0000000..d1e9d27
--- /dev/null
+++ b/src/contrib/ctype.h
@@ -0,0 +1,193 @@
+/* Copyright (C) 2017 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 Locale-independent ctype functions.
+ */
+
+#pragma once
+
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+enum {
+ CT_DIGIT = 1 << 0,
+ CT_UPPER = 1 << 1,
+ CT_LOWER = 1 << 2,
+ CT_XDIGT = 1 << 3,
+ CT_PUNCT = 1 << 4,
+ CT_PRINT = 1 << 5,
+ CT_SPACE = 1 << 6,
+};
+
+static const uint8_t char_mask[256] = {
+ // 0 - 8
+ ['\t'] = CT_SPACE,
+ ['\n'] = CT_SPACE,
+ ['\v'] = CT_SPACE,
+ ['\f'] = CT_SPACE,
+ ['\r'] = CT_SPACE,
+ // 14 - 31
+ [' '] = CT_PRINT | CT_SPACE,
+
+ ['!'] = CT_PRINT | CT_PUNCT,
+ ['"'] = CT_PRINT | CT_PUNCT,
+ ['#'] = CT_PRINT | CT_PUNCT,
+ ['$'] = CT_PRINT | CT_PUNCT,
+ ['%'] = CT_PRINT | CT_PUNCT,
+ ['&'] = CT_PRINT | CT_PUNCT,
+ ['\''] = CT_PRINT | CT_PUNCT,
+ ['('] = CT_PRINT | CT_PUNCT,
+ [')'] = CT_PRINT | CT_PUNCT,
+ ['*'] = CT_PRINT | CT_PUNCT,
+ ['+'] = CT_PRINT | CT_PUNCT,
+ [','] = CT_PRINT | CT_PUNCT,
+ ['-'] = CT_PRINT | CT_PUNCT,
+ ['.'] = CT_PRINT | CT_PUNCT,
+ ['/'] = CT_PRINT | CT_PUNCT,
+
+ ['0'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['1'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['2'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['3'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['4'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['5'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['6'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['7'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['8'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+ ['9'] = CT_PRINT | CT_DIGIT | CT_XDIGT,
+
+ [':'] = CT_PRINT | CT_PUNCT,
+ [';'] = CT_PRINT | CT_PUNCT,
+ ['<'] = CT_PRINT | CT_PUNCT,
+ ['='] = CT_PRINT | CT_PUNCT,
+ ['>'] = CT_PRINT | CT_PUNCT,
+ ['?'] = CT_PRINT | CT_PUNCT,
+ ['@'] = CT_PRINT | CT_PUNCT,
+
+ ['A'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['B'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['C'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['D'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['E'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['F'] = CT_PRINT | CT_UPPER | CT_XDIGT,
+ ['G'] = CT_PRINT | CT_UPPER,
+ ['H'] = CT_PRINT | CT_UPPER,
+ ['I'] = CT_PRINT | CT_UPPER,
+ ['J'] = CT_PRINT | CT_UPPER,
+ ['K'] = CT_PRINT | CT_UPPER,
+ ['L'] = CT_PRINT | CT_UPPER,
+ ['M'] = CT_PRINT | CT_UPPER,
+ ['N'] = CT_PRINT | CT_UPPER,
+ ['O'] = CT_PRINT | CT_UPPER,
+ ['P'] = CT_PRINT | CT_UPPER,
+ ['Q'] = CT_PRINT | CT_UPPER,
+ ['R'] = CT_PRINT | CT_UPPER,
+ ['S'] = CT_PRINT | CT_UPPER,
+ ['T'] = CT_PRINT | CT_UPPER,
+ ['U'] = CT_PRINT | CT_UPPER,
+ ['V'] = CT_PRINT | CT_UPPER,
+ ['W'] = CT_PRINT | CT_UPPER,
+ ['X'] = CT_PRINT | CT_UPPER,
+ ['Y'] = CT_PRINT | CT_UPPER,
+ ['Z'] = CT_PRINT | CT_UPPER,
+
+ ['['] = CT_PRINT | CT_PUNCT,
+ ['\\'] = CT_PRINT | CT_PUNCT,
+ [']'] = CT_PRINT | CT_PUNCT,
+ ['^'] = CT_PRINT | CT_PUNCT,
+ ['_'] = CT_PRINT | CT_PUNCT,
+ ['`'] = CT_PRINT | CT_PUNCT,
+
+ ['a'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['b'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['c'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['d'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['e'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['f'] = CT_PRINT | CT_LOWER | CT_XDIGT,
+ ['g'] = CT_PRINT | CT_LOWER,
+ ['h'] = CT_PRINT | CT_LOWER,
+ ['i'] = CT_PRINT | CT_LOWER,
+ ['j'] = CT_PRINT | CT_LOWER,
+ ['k'] = CT_PRINT | CT_LOWER,
+ ['l'] = CT_PRINT | CT_LOWER,
+ ['m'] = CT_PRINT | CT_LOWER,
+ ['n'] = CT_PRINT | CT_LOWER,
+ ['o'] = CT_PRINT | CT_LOWER,
+ ['p'] = CT_PRINT | CT_LOWER,
+ ['q'] = CT_PRINT | CT_LOWER,
+ ['r'] = CT_PRINT | CT_LOWER,
+ ['s'] = CT_PRINT | CT_LOWER,
+ ['t'] = CT_PRINT | CT_LOWER,
+ ['u'] = CT_PRINT | CT_LOWER,
+ ['v'] = CT_PRINT | CT_LOWER,
+ ['w'] = CT_PRINT | CT_LOWER,
+ ['x'] = CT_PRINT | CT_LOWER,
+ ['y'] = CT_PRINT | CT_LOWER,
+ ['z'] = CT_PRINT | CT_LOWER,
+
+ ['{'] = CT_PRINT | CT_PUNCT,
+ ['|'] = CT_PRINT | CT_PUNCT,
+ ['}'] = CT_PRINT | CT_PUNCT,
+ ['~'] = CT_PRINT | CT_PUNCT,
+ // 127 - 255
+};
+
+static inline bool is_alnum(uint8_t c)
+{
+ return char_mask[c] & (CT_DIGIT | CT_UPPER | CT_LOWER);
+}
+
+static inline bool is_alpha(uint8_t c)
+{
+ return char_mask[c] & (CT_UPPER | CT_LOWER);
+}
+
+static inline bool is_digit(uint8_t c)
+{
+ return char_mask[c] & CT_DIGIT;
+}
+
+static inline bool is_xdigit(uint8_t c)
+{
+ return char_mask[c] & CT_XDIGT;
+}
+
+static inline bool is_lower(uint8_t c)
+{
+ return char_mask[c] & CT_LOWER;
+}
+
+static inline bool is_upper(uint8_t c)
+{
+ return char_mask[c] & CT_UPPER;
+}
+
+static inline bool is_print(uint8_t c)
+{
+ return char_mask[c] & CT_PRINT;
+}
+
+static inline bool is_punct(uint8_t c)
+{
+ return char_mask[c] & CT_PUNCT;
+}
+
+static inline bool is_space(uint8_t c)
+{
+ return char_mask[c] & CT_SPACE;
+}
diff --git a/src/contrib/dnstap/convert.c b/src/contrib/dnstap/convert.c
new file mode 100644
index 0000000..93ee8e9
--- /dev/null
+++ b/src/contrib/dnstap/convert.c
@@ -0,0 +1,142 @@
+/* Copyright (C) 2016 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 <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include "contrib/dnstap/convert.h"
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+/*!
+ * \brief Translation between real and Dnstap value.
+ */
+typedef struct mapping {
+ int real;
+ int dnstap;
+} mapping_t;
+
+/*!
+ * \brief Mapping for network family.
+ */
+static const mapping_t SOCKET_FAMILY_MAPPING[] = {
+ { AF_INET, DNSTAP__SOCKET_FAMILY__INET },
+ { AF_INET6, DNSTAP__SOCKET_FAMILY__INET6 },
+ { 0 }
+};
+
+/*!
+ * \brief Mapping from network protocol.
+ */
+static const mapping_t SOCKET_PROTOCOL_MAPPING[] = {
+ { IPPROTO_UDP, DNSTAP__SOCKET_PROTOCOL__UDP },
+ { IPPROTO_TCP, DNSTAP__SOCKET_PROTOCOL__TCP },
+ { 0 }
+};
+
+/*!
+ * \brief Get Dnstap value for a given real value.
+ */
+static int encode(const mapping_t *mapping, int real)
+{
+ for (const mapping_t *m = mapping; m->real != 0; m += 1) {
+ if (m->real == real) {
+ return m->dnstap;
+ }
+ }
+
+ return 0;
+}
+
+/*!
+ * \brief Get real value for a given Dnstap value.
+ */
+static int decode(const mapping_t *mapping, int dnstap)
+{
+ for (const mapping_t *m = mapping; m->real != 0; m += 1) {
+ if (m->dnstap == dnstap) {
+ return m->real;
+ }
+ }
+
+ return 0;
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+Dnstap__SocketFamily dt_family_encode(int family)
+{
+ return encode(SOCKET_FAMILY_MAPPING, family);
+}
+
+int dt_family_decode(Dnstap__SocketFamily dnstap_family)
+{
+ return decode(SOCKET_FAMILY_MAPPING, dnstap_family);
+}
+
+Dnstap__SocketProtocol dt_protocol_encode(int protocol)
+{
+ return encode(SOCKET_PROTOCOL_MAPPING, protocol);
+}
+
+int dt_protocol_decode(Dnstap__SocketProtocol dnstap_protocol)
+{
+ return decode(SOCKET_PROTOCOL_MAPPING, dnstap_protocol);
+}
+
+bool dt_message_type_is_query(Dnstap__Message__Type type)
+{
+ switch (type) {
+ case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
+ case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
+ case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY:
+ case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY:
+ case DNSTAP__MESSAGE__TYPE__STUB_QUERY:
+ case DNSTAP__MESSAGE__TYPE__TOOL_QUERY:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool dt_message_type_is_response(Dnstap__Message__Type type)
+{
+ switch (type) {
+ case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool dt_message_role_is_initiator(Dnstap__Message__Type type)
+{
+ switch (type) {
+ case DNSTAP__MESSAGE__TYPE__AUTH_QUERY:
+ case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE:
+ case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY:
+ case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE:
+ return false;
+ default:
+ return true;
+ }
+}
diff --git a/src/contrib/dnstap/convert.h b/src/contrib/dnstap/convert.h
new file mode 100644
index 0000000..0e2a86a
--- /dev/null
+++ b/src/contrib/dnstap/convert.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2018 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 Dnstap identifiers conversions.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+/*!
+ * \brief Get Dnstap socket family from the real one.
+ */
+Dnstap__SocketFamily dt_family_encode(int family);
+
+/*!
+ * \brief Get real socket family from the Dnstap one.
+ */
+int dt_family_decode(Dnstap__SocketFamily dnstap_family);
+
+/*!
+ * \brief Get Dnstap protocol from a real one.
+ */
+Dnstap__SocketProtocol dt_protocol_encode(int protocol);
+
+/*!
+ * \brief Get real protocol from the Dnstap one.
+ */
+int dt_protocol_decode(Dnstap__SocketProtocol dnstap_protocol);
+
+/*!
+ * Check if a message type is any type of a query.
+ */
+bool dt_message_type_is_query(Dnstap__Message__Type type);
+
+/*!
+ * Check if a message type is any type of a response.
+ */
+bool dt_message_type_is_response(Dnstap__Message__Type type);
+
+/*!
+ * Check if a message role is any type of an initiator.
+ */
+bool dt_message_role_is_initiator(Dnstap__Message__Type type);
diff --git a/src/contrib/dnstap/dnstap.c b/src/contrib/dnstap/dnstap.c
new file mode 100644
index 0000000..1516b4d
--- /dev/null
+++ b/src/contrib/dnstap/dnstap.c
@@ -0,0 +1,41 @@
+/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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 <stdint.h>
+#include <stdlib.h>
+
+#include "contrib/dnstap/dnstap.h"
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+#define DNSTAP_INITIAL_BUF_SIZE 256
+
+uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz)
+{
+ ProtobufCBufferSimple sbuf = { { NULL } };
+
+ sbuf.base.append = protobuf_c_buffer_simple_append;
+ sbuf.len = 0;
+ sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE;
+ sbuf.data = malloc(sbuf.alloced);
+ if (sbuf.data == NULL) {
+ return NULL;
+ }
+ sbuf.must_free_data = 1;
+
+ *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf);
+ *buf = sbuf.data;
+ return *buf;
+}
diff --git a/src/contrib/dnstap/dnstap.h b/src/contrib/dnstap/dnstap.h
new file mode 100644
index 0000000..f7aecbc
--- /dev/null
+++ b/src/contrib/dnstap/dnstap.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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/>.
+ */
+
+/*!
+ * \author Robert Edmonds <edmonds@fsi.io>
+ *
+ * \brief Public interface for dnstap.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+/*! \brief Frame Streams "Content Type" value for dnstap. */
+#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap"
+
+/*!
+ * \brief Serializes a filled out dnstap protobuf struct. Dynamically allocates
+ * storage for the serialized frame.
+ *
+ * \note This function returns a copy of its parameter return value 'buf' to
+ * make error checking slightly easier.
+ *
+ * \param d dnstap protobuf struct.
+ * \param[out] buf Serialized frame.
+ * \param[out] sz Size in bytes of the serialized frame.
+ *
+ * \return Serialized frame.
+ * \retval NULL if error.
+ */
+uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz);
diff --git a/src/contrib/dnstap/dnstap.proto b/src/contrib/dnstap/dnstap.proto
new file mode 100644
index 0000000..ea5c77a
--- /dev/null
+++ b/src/contrib/dnstap/dnstap.proto
@@ -0,0 +1,270 @@
+// dnstap: flexible, structured event replication format for DNS software
+//
+// This file contains the protobuf schemas for the "dnstap" structured event
+// replication format for DNS software.
+
+// Written in 2013-2014 by Farsight Security, Inc.
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this file to the public
+// domain worldwide. This file is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along
+// with this file. If not, see:
+//
+// <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+syntax = "proto2";
+
+package dnstap;
+
+// "Dnstap": this is the top-level dnstap type, which is a "union" type that
+// contains other kinds of dnstap payloads, although currently only one type
+// of dnstap payload is defined.
+// See: https://developers.google.com/protocol-buffers/docs/techniques#union
+message Dnstap {
+ // DNS server identity.
+ // If enabled, this is the identity string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by an
+ // "NSID" (RFC 5001) query.
+ optional bytes identity = 1;
+
+ // DNS server version.
+ // If enabled, this is the version string of the DNS server which generated
+ // this message. Typically this would be the same string as returned by a
+ // "version.bind" query.
+ optional bytes version = 2;
+
+ // Extra data for this payload.
+ // This field can be used for adding an arbitrary byte-string annotation to
+ // the payload. No encoding or interpretation is applied or enforced.
+ optional bytes extra = 3;
+
+ // Identifies which field below is filled in.
+ enum Type {
+ MESSAGE = 1;
+ }
+ required Type type = 15;
+
+ // One of the following will be filled in.
+ optional Message message = 14;
+}
+
+// SocketFamily: the network protocol family of a socket. This specifies how
+// to interpret "network address" fields.
+enum SocketFamily {
+ INET = 1; // IPv4 (RFC 791)
+ INET6 = 2; // IPv6 (RFC 2460)
+}
+
+// SocketProtocol: the transport protocol of a socket. This specifies how to
+// interpret "transport port" fields.
+enum SocketProtocol {
+ UDP = 1; // User Datagram Protocol (RFC 768)
+ TCP = 2; // Transmission Control Protocol (RFC 793)
+}
+
+// Message: a wire-format (RFC 1035 section 4) DNS message and associated
+// metadata. Applications generating "Message" payloads should follow
+// certain requirements based on the MessageType, see below.
+message Message {
+
+ // There are eight types of "Message" defined that correspond to the
+ // four arrows in the following diagram, slightly modified from RFC 1035
+ // section 2:
+
+ // +---------+ +----------+ +--------+
+ // | | query | | query | |
+ // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. |
+ // | Resolver| | Server | | Name |
+ // | |<-SR--------CR-| |<-RR----AR-| Server |
+ // +---------+ response | | response | |
+ // +----------+ +--------+
+
+ // Each arrow has two Type values each, one for each "end" of each arrow,
+ // because these are considered to be distinct events. Each end of each
+ // arrow on the diagram above has been marked with a two-letter Type
+ // mnemonic. Clockwise from upper left, these mnemonic values are:
+ //
+ // SQ: STUB_QUERY
+ // CQ: CLIENT_QUERY
+ // RQ: RESOLVER_QUERY
+ // AQ: AUTH_QUERY
+ // AR: AUTH_RESPONSE
+ // RR: RESOLVER_RESPONSE
+ // CR: CLIENT_RESPONSE
+ // SR: STUB_RESPONSE
+
+ // Two additional types of "Message" have been defined for the
+ // "forwarding" case where an upstream DNS server is responsible for
+ // further recursion. These are not shown on the diagram above, but have
+ // the following mnemonic values:
+
+ // FQ: FORWARDER_QUERY
+ // FR: FORWARDER_RESPONSE
+
+ // The "Message" Type values are defined below.
+
+ enum Type {
+ // AUTH_QUERY is a DNS query message received from a resolver by an
+ // authoritative name server, from the perspective of the authoritative
+ // name server.
+ AUTH_QUERY = 1;
+
+ // AUTH_RESPONSE is a DNS response message sent from an authoritative
+ // name server to a resolver, from the perspective of the authoritative
+ // name server.
+ AUTH_RESPONSE = 2;
+
+ // RESOLVER_QUERY is a DNS query message sent from a resolver to an
+ // authoritative name server, from the perspective of the resolver.
+ // Resolvers typically clear the RD (recursion desired) bit when
+ // sending queries.
+ RESOLVER_QUERY = 3;
+
+ // RESOLVER_RESPONSE is a DNS response message received from an
+ // authoritative name server by a resolver, from the perspective of
+ // the resolver.
+ RESOLVER_RESPONSE = 4;
+
+ // CLIENT_QUERY is a DNS query message sent from a client to a DNS
+ // server which is expected to perform further recursion, from the
+ // perspective of the DNS server. The client may be a stub resolver or
+ // forwarder or some other type of software which typically sets the RD
+ // (recursion desired) bit when querying the DNS server. The DNS server
+ // may be a simple forwarding proxy or it may be a full recursive
+ // resolver.
+ CLIENT_QUERY = 5;
+
+ // CLIENT_RESPONSE is a DNS response message sent from a DNS server to
+ // a client, from the perspective of the DNS server. The DNS server
+ // typically sets the RA (recursion available) bit when responding.
+ CLIENT_RESPONSE = 6;
+
+ // FORWARDER_QUERY is a DNS query message sent from a downstream DNS
+ // server to an upstream DNS server which is expected to perform
+ // further recursion, from the perspective of the downstream DNS
+ // server.
+ FORWARDER_QUERY = 7;
+
+ // FORWARDER_RESPONSE is a DNS response message sent from an upstream
+ // DNS server performing recursion to a downstream DNS server, from the
+ // perspective of the downstream DNS server.
+ FORWARDER_RESPONSE = 8;
+
+ // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS
+ // server, from the perspective of the stub resolver.
+ STUB_QUERY = 9;
+
+ // STUB_RESPONSE is a DNS response message sent from a DNS server to a
+ // stub resolver, from the perspective of the stub resolver.
+ STUB_RESPONSE = 10;
+
+ // TOOL_QUERY is a DNS query message sent from a DNS software tool to a
+ // DNS server, from the perspective of the tool.
+ TOOL_QUERY = 11;
+
+ // TOOL_RESPONSE is a DNS response message received by a DNS software
+ // tool from a DNS server, from the perspective of the tool.
+ TOOL_RESPONSE = 12;
+ }
+
+ // One of the Type values described above.
+ required Type type = 1;
+
+ // One of the SocketFamily values described above.
+ optional SocketFamily socket_family = 2;
+
+ // One of the SocketProtocol values described above.
+ optional SocketProtocol socket_protocol = 3;
+
+ // The network address of the message initiator.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes query_address = 4;
+
+ // The network address of the message responder.
+ // For SocketFamily INET, this field is 4 octets (IPv4 address).
+ // For SocketFamily INET6, this field is 16 octets (IPv6 address).
+ optional bytes response_address = 5;
+
+ // The transport port of the message initiator.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 query_port = 6;
+
+ // The transport port of the message responder.
+ // This is a 16-bit UDP or TCP port number, depending on SocketProtocol.
+ optional uint32 response_port = 7;
+
+ // The time at which the DNS query message was sent or received, depending
+ // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 query_time_sec = 8;
+
+ // The time at which the DNS query message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 query_time_nsec = 9;
+
+ // The initiator's original wire-format DNS query message, verbatim.
+ optional bytes query_message = 10;
+
+ // The "zone" or "bailiwick" pertaining to the DNS query message.
+ // This is a wire-format DNS domain name.
+ optional bytes query_zone = 11;
+
+ // The time at which the DNS response message was sent or received,
+ // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or
+ // CLIENT_RESPONSE.
+ // This is the number of seconds since the UNIX epoch.
+ optional uint64 response_time_sec = 12;
+
+ // The time at which the DNS response message was sent or received.
+ // This is the seconds fraction, expressed as a count of nanoseconds.
+ optional fixed32 response_time_nsec = 13;
+
+ // The responder's original wire-format DNS response message, verbatim.
+ optional bytes response_message = 14;
+}
+
+// All fields except for 'type' in the Message schema are optional.
+// It is recommended that at least the following fields be filled in for
+// particular types of Messages.
+
+// AUTH_QUERY:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_message
+// query_time_sec, query_time_nsec
+
+// AUTH_RESPONSE:
+// socket_family, socket_protocol
+// query_address, query_port
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
+
+// RESOLVER_QUERY:
+// socket_family, socket_protocol
+// query_message
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+
+// RESOLVER_RESPONSE:
+// socket_family, socket_protocol
+// query_time_sec, query_time_nsec
+// query_zone
+// response_address, response_port
+// response_message
+// response_time_sec, response_time_nsec
+
+// CLIENT_QUERY:
+// socket_family, socket_protocol
+// query_message
+// query_time_sec, query_time_nsec
+
+// CLIENT_RESPONSE:
+// socket_family, socket_protocol
+// query_time_sec, query_time_nsec
+// response_message
+// response_time_sec, response_time_nsec
diff --git a/src/contrib/dnstap/message.c b/src/contrib/dnstap/message.c
new file mode 100644
index 0000000..a5f798e
--- /dev/null
+++ b/src/contrib/dnstap/message.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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 <netinet/in.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/errcode.h"
+
+#include "contrib/dnstap/convert.h"
+#include "contrib/dnstap/message.h"
+
+static void set_address(const struct sockaddr *sockaddr,
+ ProtobufCBinaryData *addr,
+ protobuf_c_boolean *has_addr,
+ uint32_t *port,
+ protobuf_c_boolean *has_port)
+{
+ if (sockaddr == NULL) {
+ *has_addr = 0;
+ *has_port = 0;
+ return;
+ }
+
+ *has_addr = 1;
+ *has_port = 1;
+
+ if (sockaddr->sa_family == AF_INET) {
+ const struct sockaddr_in *sai;
+ sai = (const struct sockaddr_in *)sockaddr;
+ addr->len = sizeof(sai->sin_addr);
+ addr->data = (uint8_t *)&sai->sin_addr.s_addr;
+ *port = ntohs(sai->sin_port);
+ } else if (sockaddr->sa_family == AF_INET6) {
+ const struct sockaddr_in6 *sai6;
+ sai6 = (const struct sockaddr_in6 *)sockaddr;
+ addr->len = sizeof(sai6->sin6_addr);
+ addr->data = (uint8_t *)&sai6->sin6_addr.s6_addr;
+ *port = ntohs(sai6->sin6_port);
+ }
+}
+
+static int get_family(const struct sockaddr *query_sa,
+ const struct sockaddr *response_sa)
+{
+ const struct sockaddr *source = query_sa ? query_sa : response_sa;
+ if (source == NULL) {
+ return 0;
+ }
+
+ return dt_family_encode(source->sa_family);
+}
+
+int dt_message_fill(Dnstap__Message *m,
+ const Dnstap__Message__Type type,
+ const struct sockaddr *query_sa,
+ const struct sockaddr *response_sa,
+ const int protocol,
+ const void *wire,
+ const size_t len_wire,
+ const struct timespec *mtime)
+{
+ if (m == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(m, 0, sizeof(*m));
+
+ m->base.descriptor = &dnstap__message__descriptor;
+
+ // Message.type
+ m->type = type;
+
+ // Message.socket_family
+ m->socket_family = get_family(query_sa, response_sa);
+ m->has_socket_family = m->socket_family != 0;
+
+ // Message.socket_protocol
+ m->socket_protocol = dt_protocol_encode(protocol);
+ m->has_socket_protocol = m->socket_protocol != 0;
+
+ // Message addresses
+ set_address(query_sa, &m->query_address, &m->has_query_address,
+ &m->query_port, &m->has_query_port);
+ set_address(response_sa, &m->response_address, &m->has_response_address,
+ &m->response_port, &m->has_response_port);
+
+ if (dt_message_type_is_query(type)) {
+ // Message.query_message
+ m->query_message.len = len_wire;
+ m->query_message.data = (uint8_t *)wire;
+ m->has_query_message = 1;
+ // Message.query_time_sec, Message.query_time_nsec
+ if (mtime != NULL) {
+ m->query_time_sec = mtime->tv_sec;
+ m->query_time_nsec = mtime->tv_nsec;
+ m->has_query_time_sec = 1;
+ m->has_query_time_nsec = 1;
+ }
+ } else if (dt_message_type_is_response(type)) {
+ // Message.response_message
+ m->response_message.len = len_wire;
+ m->response_message.data = (uint8_t *)wire;
+ m->has_response_message = 1;
+ // Message.response_time_sec, Message.response_time_nsec
+ if (mtime != NULL) {
+ m->response_time_sec = mtime->tv_sec;
+ m->response_time_nsec = mtime->tv_nsec;
+ m->has_response_time_sec = 1;
+ m->has_response_time_nsec = 1;
+ }
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/contrib/dnstap/message.h b/src/contrib/dnstap/message.h
new file mode 100644
index 0000000..b9e3aff
--- /dev/null
+++ b/src/contrib/dnstap/message.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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/>.
+ */
+
+/*!
+ * \author Robert Edmonds <edmonds@fsi.io>
+ *
+ * \brief Dnstap message interface.
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <stddef.h>
+
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+/*!
+ * \brief Fill a Dnstap__Message structure with the given parameters.
+ *
+ * \param[out] m
+ * Dnstap__Message structure to fill. Will be zeroed first.
+ * \param type
+ * One of the DNSTAP__MESSAGE__TYPE__* values.
+ * \param query_sa
+ * sockaddr_in or sockaddr_in6 to use when filling the 'socket_family',
+ * 'query_address', 'query_port' fields.
+ * \param response_sa
+ * sockaddr_in or sockaddr_in6 to use when filling the 'socket_family',
+ * 'response_address', 'response_port' fields.
+ * \param protocol
+ * \c IPPROTO_UDP or \c IPPROTO_TCP.
+ * \param wire
+ * Wire-format query message or response message (depending on 'type').
+ * \param len_wire
+ * Length in bytes of 'wire'.
+ * \param mtime
+ * Message time. May be NULL.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int dt_message_fill(Dnstap__Message *m,
+ const Dnstap__Message__Type type,
+ const struct sockaddr *query_sa,
+ const struct sockaddr *response_sa,
+ const int protocol,
+ const void *wire,
+ const size_t len_wire,
+ const struct timespec *mtime);
diff --git a/src/contrib/dnstap/reader.c b/src/contrib/dnstap/reader.c
new file mode 100644
index 0000000..dc1c053
--- /dev/null
+++ b/src/contrib/dnstap/reader.c
@@ -0,0 +1,103 @@
+/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+#include "contrib/dnstap/dnstap.h"
+#include "contrib/dnstap/reader.h"
+
+dt_reader_t* dt_reader_create(const char *file_path)
+{
+ struct fstrm_file_options *fopt = NULL;
+ struct fstrm_reader_options *ropt = NULL;
+ dt_reader_t *reader = NULL;
+ fstrm_res res;
+
+ reader = calloc(1, sizeof(dt_reader_t));
+ if (reader == NULL) {
+ goto fail;
+ }
+
+ // Open reader.
+ fopt = fstrm_file_options_init();
+ fstrm_file_options_set_file_path(fopt, file_path);
+ ropt = fstrm_reader_options_init();
+ fstrm_reader_options_add_content_type(ropt,
+ (const uint8_t *) DNSTAP_CONTENT_TYPE,
+ strlen(DNSTAP_CONTENT_TYPE));
+ reader->fr = fstrm_file_reader_init(fopt, ropt);
+ fstrm_file_options_destroy(&fopt);
+ fstrm_reader_options_destroy(&ropt);
+ if (reader->fr == NULL) {
+ goto fail;
+ }
+ res = fstrm_reader_open(reader->fr);
+ if (res != fstrm_res_success) {
+ goto fail;
+ }
+
+ return reader;
+fail:
+ dt_reader_free(reader);
+ return NULL;
+}
+
+void dt_reader_free(dt_reader_t *reader)
+{
+ if (reader == NULL) {
+ return;
+ }
+
+ fstrm_reader_destroy(&reader->fr);
+ free(reader);
+}
+
+int dt_reader_read(dt_reader_t *reader, Dnstap__Dnstap **d)
+{
+ fstrm_res res;
+ const uint8_t *data = NULL;
+ size_t len = 0;
+
+ res = fstrm_reader_read(reader->fr, &data, &len);
+ if (res == fstrm_res_success) {
+ *d = dnstap__dnstap__unpack(NULL, len, data);
+ if (*d == NULL) {
+ return KNOT_ENOMEM;
+ }
+ } else if (res == fstrm_res_failure) {
+ return KNOT_ERROR;
+ } else if (res == fstrm_res_stop) {
+ return KNOT_EOF;
+ }
+
+ return KNOT_EOK;
+}
+
+void dt_reader_free_frame(_unused_ dt_reader_t *reader, Dnstap__Dnstap **frame_ptr)
+{
+ if (!*frame_ptr) {
+ return;
+ }
+
+ dnstap__dnstap__free_unpacked(*frame_ptr, NULL);
+ *frame_ptr = NULL;
+}
diff --git a/src/contrib/dnstap/reader.h b/src/contrib/dnstap/reader.h
new file mode 100644
index 0000000..cc6385f
--- /dev/null
+++ b/src/contrib/dnstap/reader.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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 Dnstap file reader.
+ */
+
+#pragma once
+
+#include <fstrm.h>
+#include <protobuf-c/protobuf-c.h>
+
+#include "contrib/dnstap/dnstap.pb-c.h"
+
+/*! \brief Structure for dnstap file reader. */
+typedef struct {
+ /*!< Input reader. */
+ struct fstrm_reader *fr;
+} dt_reader_t;
+
+/*!
+ * \brief Creates dnstap file reader structure.
+ *
+ * \param file_path Name of file to read input from.
+ *
+ * \retval reader if success.
+ * \retval NULL if error.
+ */
+dt_reader_t* dt_reader_create(const char *file_path);
+
+/*!
+ * \brief Close dnstap file reader.
+ *
+ * \param reader dnstap file reader structure.
+ */
+void dt_reader_free(dt_reader_t *reader);
+
+/*!
+ * \brief Read a dnstap protobuf from a dnstap file reader.
+ *
+ * Caller must deallocate the returned protobuf with the
+ * dnstap__dnstap__free_unpacked() function.
+ *
+ * \param[in] reader dnstap file reader structure.
+ * \param[out] d Unpacked dnstap protobuf.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ERROR
+ * \retval KNOT_EOF
+ * \retval KNOT_ENOMEM
+ */
+int dt_reader_read(dt_reader_t *reader, Dnstap__Dnstap **d);
+
+/*!
+ * \brief free the frame allocated by dt_read_data.
+ *
+ * \param reader Dnstap reader context.
+ * \param d The frame to be freed.
+ */
+void dt_reader_free_frame(dt_reader_t *reader, Dnstap__Dnstap **d);
diff --git a/src/contrib/dnstap/writer.c b/src/contrib/dnstap/writer.c
new file mode 100644
index 0000000..03961c1
--- /dev/null
+++ b/src/contrib/dnstap/writer.c
@@ -0,0 +1,120 @@
+/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/errcode.h"
+
+#include "contrib/dnstap/dnstap.h"
+#include "contrib/dnstap/writer.h"
+
+dt_writer_t* dt_writer_create(const char *file_path, const char *version)
+{
+ struct fstrm_file_options *fopt = NULL;
+ struct fstrm_writer_options *wopt = NULL;
+ dt_writer_t *writer = NULL;
+ fstrm_res res;
+
+ writer = calloc(1, sizeof(dt_writer_t));
+ if (writer == NULL) {
+ goto fail;
+ }
+
+ // Set "version".
+ if (version != NULL) {
+ writer->len_version = strlen(version);
+ writer->version = strdup(version);
+ if (!writer->version) {
+ goto fail;
+ }
+ }
+
+ // Open writer.
+ fopt = fstrm_file_options_init();
+ fstrm_file_options_set_file_path(fopt, file_path);
+ wopt = fstrm_writer_options_init();
+ fstrm_writer_options_add_content_type(wopt,
+ (const uint8_t *) DNSTAP_CONTENT_TYPE,
+ strlen(DNSTAP_CONTENT_TYPE));
+ writer->fw = fstrm_file_writer_init(fopt, wopt);
+ fstrm_file_options_destroy(&fopt);
+ fstrm_writer_options_destroy(&wopt);
+ if (writer->fw == NULL) {
+ goto fail;
+ }
+
+ res = fstrm_writer_open(writer->fw);
+ if (res != fstrm_res_success) {
+ goto fail;
+ }
+
+ return writer;
+fail:
+ dt_writer_free(writer);
+ return NULL;
+}
+
+void dt_writer_free(dt_writer_t *writer)
+{
+ if (writer == NULL) {
+ return;
+ }
+
+ fstrm_writer_destroy(&writer->fw);
+ free(writer->version);
+ free(writer);
+}
+
+int dt_writer_write(dt_writer_t *writer, const ProtobufCMessage *msg)
+{
+ Dnstap__Dnstap dnstap = DNSTAP__DNSTAP__INIT;
+ size_t len;
+ uint8_t *data;
+
+ if (writer->fw == NULL) {
+ return KNOT_EOK;
+ }
+
+ // Only handle dnstap/Message.
+ assert(msg->descriptor == &dnstap__message__descriptor);
+
+ // Fill out 'dnstap'.
+ if (writer->version) {
+ dnstap.version.data = writer->version;
+ dnstap.version.len = writer->len_version;
+ dnstap.has_version = 1;
+ }
+ dnstap.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
+ dnstap.message = (Dnstap__Message *)msg;
+
+ // Serialize the dnstap frame.
+ if (!dt_pack(&dnstap, &data, &len)) {
+ return KNOT_ENOMEM;
+ }
+
+ // Write the dnstap frame to the output stream.
+ if (fstrm_writer_write(writer->fw, data, len) != fstrm_res_success) {
+ return KNOT_ERROR;
+ }
+
+ // Cleanup.
+ free(data);
+
+ return KNOT_EOK;
+}
diff --git a/src/contrib/dnstap/writer.h b/src/contrib/dnstap/writer.h
new file mode 100644
index 0000000..e6928c5
--- /dev/null
+++ b/src/contrib/dnstap/writer.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com>
+
+ 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/>.
+ */
+
+/*!
+ * \author Robert Edmonds <edmonds@fsi.io>
+ *
+ * \brief Dnstap file writer.
+ */
+
+#pragma once
+
+#include <fstrm.h>
+#include <protobuf-c/protobuf-c.h>
+
+/*! \brief Structure for dnstap file writer. */
+typedef struct {
+ /*!< Output writer. */
+ struct fstrm_writer *fw;
+
+ /*!< dnstap "version" field. */
+ void *version;
+
+ /*!< length of dnstap "version" field. */
+ size_t len_version;
+} dt_writer_t;
+
+/*!
+ * \brief Creates dnstap file writer structure.
+ *
+ * \param file_path Name of file to write output to.
+ * \param version Version string of software. May be NULL.
+ *
+ * \retval writer if success.
+ * \retval NULL if error.
+ */
+dt_writer_t* dt_writer_create(const char *file_path, const char *version);
+
+/*!
+ * \brief Finish writing dnstap file writer and free resources.
+ *
+ * \param writer dnstap file writer structure.
+ */
+void dt_writer_free(dt_writer_t *writer);
+
+/*!
+ * \brief Write a protobuf to the dnstap file writer.
+ *
+ * Supported protobuf types for the 'msg' parameter:
+ * \c Dnstap__Message
+ *
+ * \param writer dnstap file writer structure.
+ * \param msg dnstap protobuf. Must be a supported type.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ */
+int dt_writer_write(dt_writer_t *writer, const ProtobufCMessage *msg);
diff --git a/src/contrib/files.c b/src/contrib/files.c
new file mode 100644
index 0000000..c753ca8
--- /dev/null
+++ b/src/contrib/files.c
@@ -0,0 +1,254 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "contrib/files.h"
+#include "contrib/string.h"
+#include "libknot/errcode.h"
+
+#if defined(MAXBSIZE)
+ #define BUFSIZE MAXBSIZE
+#else
+ #define BUFSIZE (64 * 1024)
+#endif
+
+char* abs_path(const char *path, const char *base_dir)
+{
+ if (path == NULL) {
+ return NULL;
+ } else if (path[0] == '/') {
+ return strdup(path);
+ } else {
+ char *full_path;
+ if (base_dir == NULL) {
+ char *cwd = realpath("./", NULL);
+ full_path = sprintf_alloc("%s/%s", cwd, path);
+ free(cwd);
+ } else {
+ full_path = sprintf_alloc("%s/%s", base_dir, path);
+ }
+ return full_path;
+ }
+}
+
+bool same_path(const char *path1, const char *path2)
+{
+ bool equal = false;
+ int err = 0;
+
+ struct stat sb1;
+ if (stat(path1, &sb1) == 0) {
+ struct stat sb2;
+ if (stat(path2, &sb2) == 0) {
+ if (sb1.st_dev == sb2.st_dev &&
+ sb1.st_ino == sb2.st_ino) {
+ equal = true;
+ }
+ } else {
+ err = errno;
+ }
+ } else {
+ err = errno;
+ }
+
+ if (err != 0) {
+ // Can't compare real absolute paths, as stat() failed already. Try the best.
+ char *full_path1 = abs_path(path1, NULL);
+ char *full_path2 = abs_path(path2, NULL);
+
+ if (strcmp(full_path1, full_path2) == 0) {
+ equal = true;
+ }
+
+ free(full_path1);
+ free(full_path2);
+ }
+
+ return equal;
+}
+
+static int remove_file(const char *path, const struct stat *stat, int type, struct FTW *ftw)
+{
+ (void)stat;
+ (void)ftw;
+ if (type == FTW_DP) {
+ return rmdir(path);
+ } else {
+ return unlink(path);
+ }
+}
+
+bool remove_path(const char *path)
+{
+ return (0 == nftw(path, remove_file, 1, FTW_DEPTH | FTW_PHYS));
+}
+
+int make_dir(const char *path, mode_t mode, bool ignore_existing)
+{
+ if (mkdir(path, mode) == 0) {
+ return KNOT_EOK;
+ }
+
+ if (!ignore_existing || errno != EEXIST) {
+ return knot_map_errno();
+ }
+
+ assert(errno == EEXIST);
+
+ struct stat st = { 0 };
+ if (stat(path, &st) != 0) {
+ return knot_map_errno();
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ return KNOT_EEXIST;
+ }
+
+ return KNOT_EOK;
+}
+
+int make_path(const char *path, mode_t mode)
+{
+ if (path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ char *dir = strdup(path);
+ if (dir == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ for (char *p = strchr(dir + 1, '/'); p != NULL; p = strchr(p + 1, '/')) {
+ *p = '\0';
+ if (mkdir(dir, mode) == -1 && errno != EEXIST) {
+ free(dir);
+ return knot_map_errno();
+ }
+ *p = '/';
+ }
+
+ free(dir);
+
+ return KNOT_EOK;
+}
+
+int open_tmp_file(const char *path, char **tmp_name, FILE **file, mode_t mode)
+{
+ int ret;
+
+ *tmp_name = sprintf_alloc("%s.XXXXXX", path);
+ if (*tmp_name == NULL) {
+ ret = KNOT_ENOMEM;
+ goto open_tmp_failed;
+ }
+
+ int fd = mkstemp(*tmp_name);
+ if (fd < 0) {
+ ret = knot_map_errno();
+ goto open_tmp_failed;
+ }
+
+ if (fchmod(fd, mode) != 0) {
+ ret = knot_map_errno();
+ close(fd);
+ unlink(*tmp_name);
+ goto open_tmp_failed;
+ }
+
+ *file = fdopen(fd, "w");
+ if (*file == NULL) {
+ ret = knot_map_errno();
+ close(fd);
+ unlink(*tmp_name);
+ goto open_tmp_failed;
+ }
+
+ return KNOT_EOK;
+open_tmp_failed:
+ free(*tmp_name);
+ *tmp_name = NULL;
+ *file = NULL;
+
+ assert(ret != KNOT_EOK);
+ return ret;
+}
+
+int copy_file(const char *dest, const char *src)
+{
+ if (dest == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = 0;
+ char *buf = NULL, *tmp_name = NULL;
+ FILE *file = NULL;
+
+ FILE *from = fopen(src, "r");
+ if (from == NULL) {
+ ret = errno == ENOENT ? KNOT_EFILE : knot_map_errno();
+ goto done;
+ }
+
+ buf = malloc(sizeof(*buf) * BUFSIZE);
+ if (buf == NULL) {
+ ret = KNOT_ENOMEM;
+ goto done;
+ }
+
+ ret = open_tmp_file(dest, &tmp_name, &file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
+ ssize_t cnt;
+ while ((cnt = fread(buf, sizeof(*buf), BUFSIZE, from)) != 0 &&
+ (ret = (fwrite(buf, sizeof(*buf), cnt, file) == cnt))) {
+ }
+
+ ret = !ret || ferror(from);
+ if (ret != 0) {
+ ret = knot_map_errno();
+ unlink(tmp_name);
+ goto done;
+ }
+
+ ret = rename(tmp_name, dest);
+ if (ret != 0) {
+ ret = knot_map_errno();
+ unlink(tmp_name);
+ goto done;
+ }
+ ret = KNOT_EOK;
+
+done:
+ free(tmp_name);
+ if (file != NULL) {
+ fclose(file);
+ }
+ free(buf);
+ if (from != NULL) {
+ fclose(from);
+ }
+ return ret;
+}
diff --git a/src/contrib/files.h b/src/contrib/files.h
new file mode 100644
index 0000000..16a0f44
--- /dev/null
+++ b/src/contrib/files.h
@@ -0,0 +1,77 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+/*!
+ * Gets the absolute path.
+ *
+ * \note The result must be explicitly deallocated.
+ *
+ * \param[in] path Absolute path or a relative path suffix; a string.
+ * \param[in] base_dir Path prefix for a relative string.
+ *
+ * \return Absolute path string pointer.
+ */
+char* abs_path(const char *path, const char *base_dir);
+
+/*!
+ * Try to compare two paths whether they are identical.
+ *
+ * \note If any of the two paths doesn't physically exist, their identity can't
+ * be detected in some special corner cases.
+ *
+ * \param[in] path1 Absolute or a relative path (a file, a directory, etc.)
+ * \param[in] path2 Absolute or a relative path (a file, a directory, etc.)
+ *
+ * \return True if both paths are identical (if they point to the same inode),
+ * false otherwise.
+ */
+bool same_path(const char *path1, const char *path2);
+
+/*!
+ * \brief Delete file or directory (recursive).
+ *
+ * \return true on success, false when one or more files failed to be removed.
+ */
+bool remove_path(const char *path);
+
+/*!
+ * Equivalent to mkdir(2), can succeed if the directory already exists.
+ */
+int make_dir(const char *path, mode_t mode, bool ignore_existing);
+
+/*!
+ * Makes a directory part of the path with all parent directories if not exist.
+ */
+int make_path(const char *path, mode_t mode);
+
+/*!
+ * Creates and opens for writing a temporary file based on given path.
+ */
+int open_tmp_file(const char *path, char **tmp_name, FILE **file, mode_t mode);
+
+/*!
+ * Copies a file, possibly overwriting existing one, as an atomic operation.
+ *
+ * \return KNOT_EOK on success, KNOT_EFILE if the source file doesn't exist,
+ * \or other KNOT_E* values in case of other errors.
+ */
+int copy_file(const char *dest, const char *src);
diff --git a/src/contrib/getline.c b/src/contrib/getline.c
new file mode 100644
index 0000000..46cdff9
--- /dev/null
+++ b/src/contrib/getline.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 2011 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/>.
+ */
+
+// FreeBSD POSIX2008 getline
+#ifndef _WITH_GETLINE
+#define _WITH_GETLINE
+#endif
+
+#include "contrib/getline.h"
+
+#include <stdio.h> // getline or fgetln
+#include <stdlib.h> // free
+#include <string.h> // memcpy
+
+ssize_t knot_getline(char **lineptr, size_t *n, FILE *stream)
+{
+#ifdef HAVE_GETLINE
+ return getline(lineptr, n, stream);
+#else
+#ifdef HAVE_FGETLN
+ size_t length = 0;
+ char *buffer = fgetln(stream, &length);
+ if (buffer == NULL) {
+ return -1;
+ }
+
+ /* NOTE: Function fgetln doesn't return terminated string!
+ * Output buffer from the fgetln can't be freed.
+ */
+
+ // If the output buffer is not specified or is small, extend it.
+ if (*lineptr == NULL || *n <= length) {
+ char *tmp = realloc(*lineptr, length + 1);
+ if (tmp == NULL) {
+ return -1;
+ }
+ *lineptr = tmp;
+ *n = length + 1;
+ }
+
+ memcpy(*lineptr, buffer, length);
+ (*lineptr)[length] = '\0';
+
+ return length;
+#endif
+#endif
+}
diff --git a/src/contrib/getline.h b/src/contrib/getline.h
new file mode 100644
index 0000000..80f2db1
--- /dev/null
+++ b/src/contrib/getline.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2018 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 Multiplatform getline wrapper.
+ */
+
+#pragma once
+
+#include <stdio.h>
+#include <sys/types.h>
+
+/*!
+ * \brief Reads a line from a stream.
+ *
+ * This function has the same semantics as POSIX.1-2008 getline().
+ * If necessary, the output buffer will be allocated/reallocated.
+ *
+ * \param lineptr Output buffer.
+ * \param n Output buffer size.
+ * \param stream Input stream.
+ *
+ * \retval Number of characters read, including new line delimiter,
+ * not including terminating. -1 on error or EOF.
+ */
+ssize_t knot_getline(char **lineptr, size_t *n, FILE *stream);
diff --git a/src/contrib/json.c b/src/contrib/json.c
new file mode 100644
index 0000000..8b8bda4
--- /dev/null
+++ b/src/contrib/json.c
@@ -0,0 +1,237 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/json.h"
+
+#include "contrib/string.h"
+
+#define MAX_DEPTH 8
+
+enum {
+ BLOCK_INVALID = 0,
+ BLOCK_OBJECT,
+ BLOCK_LIST,
+};
+
+/*! One indented block of JSON. */
+struct block {
+ /*! Block type. */
+ int type;
+ /*! Number of elements written. */
+ int count;
+};
+
+struct jsonw {
+ /*! Output file stream. */
+ FILE *out;
+ /*! Indentaiton string. */
+ const char *indent;
+ /*! List to be used as a stack of blocks in progress. */
+ struct block stack[MAX_DEPTH];
+ /*! Index pointing to the top of the stack. */
+ int top;
+ /*! Newline needed indication. */
+ bool wrap;
+};
+
+static const char *DEFAULT_INDENT = "\t";
+
+static void start_block(jsonw_t *w, int type)
+{
+ assert(w->top > 0);
+
+ struct block b = {
+ .type = type,
+ .count = 0,
+ };
+
+ w->top -= 1;
+ w->stack[w->top] = b;
+}
+
+static struct block *cur_block(jsonw_t *w)
+{
+ if (w && w->top < MAX_DEPTH) {
+ return &w->stack[w->top];
+ }
+ return NULL;
+}
+
+/*! Insert new line and indent for the next write. */
+static void wrap(jsonw_t *w)
+{
+ if (!w->wrap) {
+ w->wrap = true;
+ return;
+ }
+
+ fputc('\n', w->out);
+ int level = MAX_DEPTH - w->top;
+ for (int i = 0; i < level; i++) {
+ fprintf(w->out, "%s", w->indent);
+ }
+}
+
+static void end_block(jsonw_t *w)
+{
+ assert(w->top < MAX_DEPTH);
+
+ w->top += 1;
+}
+
+static void escaped_print(jsonw_t *w, const char *str)
+{
+ fputc('"', w->out);
+ for (const char *pos = str; *pos != '\0'; pos++) {
+ char c = *pos;
+ if (c == '\\' || c == '\"') {
+ fputc('\\', w->out);
+ }
+ fputc(c, w->out);
+ }
+ fputc('"', w->out);
+}
+
+static void align_key(jsonw_t *w, const char *key)
+{
+ struct block *top = cur_block(w);
+ if (top && top->count++) {
+ fputc(',', w->out);
+ }
+
+ wrap(w);
+
+ if (key && key[0]) {
+ escaped_print(w, key);
+ fprintf(w->out, ": ");
+ }
+}
+
+jsonw_t *jsonw_new(FILE *out, const char *indent)
+{
+ assert(out);
+
+ jsonw_t *w = calloc(1, sizeof(*w));
+ if (w == NULL) {
+ return w;
+ }
+
+ w->out = out;
+ w->indent = indent ? indent : DEFAULT_INDENT;
+ w->top = MAX_DEPTH;
+
+ return w;
+}
+
+void jsonw_free(jsonw_t **w)
+{
+ if (w == NULL) {
+ return;
+ }
+
+ wrap(*w);
+
+ free(*w);
+ *w = NULL;
+}
+
+void jsonw_object(jsonw_t *w, const char *key)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "{");
+ start_block(w, BLOCK_OBJECT);
+}
+
+void jsonw_list(jsonw_t *w, const char *key)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "[");
+ start_block(w, BLOCK_LIST);
+}
+
+void jsonw_str(jsonw_t *w, const char *key, const char *value)
+{
+ assert(w);
+
+ align_key(w, key);
+ escaped_print(w, value);
+}
+
+void jsonw_ulong(jsonw_t *w, const char *key, unsigned long value)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "%lu", value);
+}
+
+void jsonw_int(jsonw_t *w, const char *key, int value)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "%d", value);
+}
+
+
+void jsonw_bool(jsonw_t *w, const char *key, bool value)
+{
+ assert(w);
+
+ align_key(w, key);
+ fprintf(w->out, "%s", value ? "true" : "false");
+}
+
+void jsonw_hex(jsonw_t *w, const char *key, const uint8_t *data, size_t len)
+{
+ assert(w);
+
+ char *hex = bin_to_hex(data, len, true);
+ if (hex != NULL) {
+ jsonw_str(w, key, hex);
+ }
+ free(hex);
+}
+
+void jsonw_end(jsonw_t *w)
+{
+ assert(w);
+
+ struct block *top = cur_block(w);
+ if (top == NULL) {
+ return;
+ }
+
+ end_block(w);
+ wrap(w);
+
+ switch (top->type) {
+ case BLOCK_OBJECT:
+ fprintf(w->out, "}");
+ break;
+ case BLOCK_LIST:
+ fprintf(w->out, "]");
+ break;
+ }
+}
diff --git a/src/contrib/json.h b/src/contrib/json.h
new file mode 100644
index 0000000..983b329
--- /dev/null
+++ b/src/contrib/json.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2022 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/>.
+*/
+
+#pragma once
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/*!
+ * Simple pretty JSON writer.
+ */
+struct jsonw;
+typedef struct jsonw jsonw_t;
+
+/*!
+ * Create new JSON writer.
+ *
+ * @param out Output file stream.
+ * @param indent Indentation string.
+ *
+ * @return JSON writer or NULL for allocation error.
+ */
+jsonw_t *jsonw_new(FILE *out, const char *indent);
+
+/*!
+ * Free JSON writer created with jsonw_new.
+ */
+void jsonw_free(jsonw_t **w);
+
+/*!
+ * Start writing a new object.
+ *
+ * The following writes will represent key and value pairs respectively until
+ * jsonw_end is called.
+ */
+void jsonw_object(jsonw_t *w, const char *key);
+
+/*!
+ * Start writing a new list.
+ *
+ * The following writes will represent values until jsonw_end is called.
+ */
+void jsonw_list(jsonw_t *w, const char *key);
+
+/*!
+ * Write string as JSON. The string will be escaped properly.
+ */
+void jsonw_str(jsonw_t *w, const char *key, const char *value);
+
+/*!
+ * Write unsigned long value as JSON.
+ */
+void jsonw_ulong(jsonw_t *w, const char *key, unsigned long value);
+
+/*!
+ * Write integer as JSON.
+ */
+void jsonw_int(jsonw_t *w, const char *key, int value);
+
+/*!
+ * Write boolean value as JSON.
+ */
+void jsonw_bool(jsonw_t *w, const char *key, bool value);
+
+/*!
+ * Write binary data encoded to HEX as JSON.
+ */
+void jsonw_hex(jsonw_t *w, const char *key, const uint8_t *data, size_t len);
+
+/*!
+ * Terminate in-progress object or list.
+ */
+void jsonw_end(jsonw_t *w);
diff --git a/src/contrib/libbpf/LICENSE b/src/contrib/libbpf/LICENSE
new file mode 100644
index 0000000..149c7b0
--- /dev/null
+++ b/src/contrib/libbpf/LICENSE
@@ -0,0 +1 @@
+../licenses/LGPL-2.1 \ No newline at end of file
diff --git a/src/contrib/libbpf/bpf/bpf.c b/src/contrib/libbpf/bpf/bpf.c
new file mode 100644
index 0000000..98596e1
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <errno.h>
+#include <linux/bpf.h>
+#include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+
+/*
+ * When building perf, unistd.h is overridden. __NR_bpf is
+ * required to be defined explicitly.
+ */
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# elif defined(__sparc__)
+# define __NR_bpf 349
+# elif defined(__s390__)
+# define __NR_bpf 351
+# elif defined(__arc__)
+# define __NR_bpf 280
+# else
+# error __NR_bpf not defined. libbpf does not support your arch.
+# endif
+#endif
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
+static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ return syscall(__NR_bpf, cmd, attr, size);
+}
+
+static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size)
+{
+ int fd;
+
+ do {
+ fd = sys_bpf(BPF_PROG_LOAD, attr, size);
+ } while (fd < 0 && errno == EAGAIN);
+
+ return fd;
+}
+
+int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
+{
+ union bpf_attr attr;
+
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = create_attr->map_type;
+ attr.key_size = create_attr->key_size;
+ attr.value_size = create_attr->value_size;
+ attr.max_entries = create_attr->max_entries;
+ attr.map_flags = create_attr->map_flags;
+ if (create_attr->name)
+ memcpy(attr.map_name, create_attr->name,
+ min(strlen(create_attr->name), BPF_OBJ_NAME_LEN - 1));
+ attr.numa_node = create_attr->numa_node;
+ attr.btf_fd = create_attr->btf_fd;
+ attr.btf_key_type_id = create_attr->btf_key_type_id;
+ attr.btf_value_type_id = create_attr->btf_value_type_id;
+ attr.map_ifindex = create_attr->map_ifindex;
+ attr.inner_map_fd = create_attr->inner_map_fd;
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags, int node)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.name = name;
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+ if (node >= 0) {
+ map_attr.numa_node = node;
+ map_attr.map_flags |= BPF_F_NUMA_NODE;
+ }
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size,
+ int value_size, int max_entries, __u32 map_flags)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.name = name;
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags, int node)
+{
+ union bpf_attr attr;
+
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = 4;
+ attr.inner_map_fd = inner_map_fd;
+ attr.max_entries = max_entries;
+ attr.map_flags = map_flags;
+ if (name)
+ memcpy(attr.map_name, name,
+ min(strlen(name), BPF_OBJ_NAME_LEN - 1));
+
+ if (node >= 0) {
+ attr.map_flags |= BPF_F_NUMA_NODE;
+ attr.numa_node = node;
+ }
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags)
+{
+ return bpf_create_map_in_map_node(map_type, name, key_size,
+ inner_map_fd, max_entries, map_flags,
+ -1);
+}
+
+static void *
+alloc_zero_tailing_info(const void *orecord, __u32 cnt,
+ __u32 actual_rec_size, __u32 expected_rec_size)
+{
+ __u64 info_len = (__u64)actual_rec_size * cnt;
+ void *info, *nrecord;
+ int i;
+
+ info = malloc(info_len);
+ if (!info)
+ return NULL;
+
+ /* zero out bytes kernel does not understand */
+ nrecord = info;
+ for (i = 0; i < cnt; i++) {
+ memcpy(nrecord, orecord, expected_rec_size);
+ memset(nrecord + expected_rec_size, 0,
+ actual_rec_size - expected_rec_size);
+ orecord += actual_rec_size;
+ nrecord += actual_rec_size;
+ }
+
+ return info;
+}
+
+int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
+ char *log_buf, size_t log_buf_sz)
+{
+ void *finfo = NULL, *linfo = NULL;
+ union bpf_attr attr;
+ __u32 log_level;
+ int fd;
+
+ if (!load_attr || !log_buf != !log_buf_sz)
+ return -EINVAL;
+
+ log_level = load_attr->log_level;
+ if (log_level > (4 | 2 | 1) || (log_level && !log_buf))
+ return -EINVAL;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = load_attr->prog_type;
+ attr.expected_attach_type = load_attr->expected_attach_type;
+ if (attr.prog_type == BPF_PROG_TYPE_TRACING) {
+ attr.attach_btf_id = load_attr->attach_btf_id;
+ attr.attach_prog_fd = load_attr->attach_prog_fd;
+ } else {
+ attr.prog_ifindex = load_attr->prog_ifindex;
+ attr.kern_version = load_attr->kern_version;
+ }
+ attr.insn_cnt = (__u32)load_attr->insns_cnt;
+ attr.insns = ptr_to_u64(load_attr->insns);
+ attr.license = ptr_to_u64(load_attr->license);
+
+ attr.log_level = log_level;
+ if (log_level) {
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ } else {
+ attr.log_buf = ptr_to_u64(NULL);
+ attr.log_size = 0;
+ }
+
+ attr.prog_btf_fd = load_attr->prog_btf_fd;
+ attr.func_info_rec_size = load_attr->func_info_rec_size;
+ attr.func_info_cnt = load_attr->func_info_cnt;
+ attr.func_info = ptr_to_u64(load_attr->func_info);
+ attr.line_info_rec_size = load_attr->line_info_rec_size;
+ attr.line_info_cnt = load_attr->line_info_cnt;
+ attr.line_info = ptr_to_u64(load_attr->line_info);
+ if (load_attr->name)
+ memcpy(attr.prog_name, load_attr->name,
+ min(strlen(load_attr->name), BPF_OBJ_NAME_LEN - 1));
+ attr.prog_flags = load_attr->prog_flags;
+
+ fd = sys_bpf_prog_load(&attr, sizeof(attr));
+ if (fd >= 0)
+ return fd;
+
+ /* After bpf_prog_load, the kernel may modify certain attributes
+ * to give user space a hint how to deal with loading failure.
+ * Check to see whether we can make some changes and load again.
+ */
+ while (errno == E2BIG && (!finfo || !linfo)) {
+ if (!finfo && attr.func_info_cnt &&
+ attr.func_info_rec_size < load_attr->func_info_rec_size) {
+ /* try with corrected func info records */
+ finfo = alloc_zero_tailing_info(load_attr->func_info,
+ load_attr->func_info_cnt,
+ load_attr->func_info_rec_size,
+ attr.func_info_rec_size);
+ if (!finfo)
+ goto done;
+
+ attr.func_info = ptr_to_u64(finfo);
+ attr.func_info_rec_size = load_attr->func_info_rec_size;
+ } else if (!linfo && attr.line_info_cnt &&
+ attr.line_info_rec_size <
+ load_attr->line_info_rec_size) {
+ linfo = alloc_zero_tailing_info(load_attr->line_info,
+ load_attr->line_info_cnt,
+ load_attr->line_info_rec_size,
+ attr.line_info_rec_size);
+ if (!linfo)
+ goto done;
+
+ attr.line_info = ptr_to_u64(linfo);
+ attr.line_info_rec_size = load_attr->line_info_rec_size;
+ } else {
+ break;
+ }
+
+ fd = sys_bpf_prog_load(&attr, sizeof(attr));
+
+ if (fd >= 0)
+ goto done;
+ }
+
+ if (log_level || !log_buf)
+ goto done;
+
+ /* Try again with log */
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = 1;
+ log_buf[0] = 0;
+ fd = sys_bpf_prog_load(&attr, sizeof(attr));
+done:
+ free(finfo);
+ free(linfo);
+ return fd;
+}
+
+int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, const char *license,
+ __u32 kern_version, char *log_buf,
+ size_t log_buf_sz)
+{
+ struct bpf_load_program_attr load_attr;
+
+ memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+ load_attr.prog_type = type;
+ load_attr.expected_attach_type = 0;
+ load_attr.name = NULL;
+ load_attr.insns = insns;
+ load_attr.insns_cnt = insns_cnt;
+ load_attr.license = license;
+ load_attr.kern_version = kern_version;
+
+ return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
+}
+
+int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, __u32 prog_flags, const char *license,
+ __u32 kern_version, char *log_buf, size_t log_buf_sz,
+ int log_level)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = type;
+ attr.insn_cnt = (__u32)insns_cnt;
+ attr.insns = ptr_to_u64(insns);
+ attr.license = ptr_to_u64(license);
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = log_level;
+ log_buf[0] = 0;
+ attr.kern_version = kern_version;
+ attr.prog_flags = prog_flags;
+
+ return sys_bpf_prog_load(&attr, sizeof(attr));
+}
+
+int bpf_map_update_elem(int fd, const void *key, const void *value,
+ __u64 flags)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_lookup_elem(int fd, const void *key, void *value)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+
+ return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+
+ return sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_delete_elem(int fd, const void *key)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+
+ return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_get_next_key(int fd, const void *key, void *next_key)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.next_key = ptr_to_u64(next_key);
+
+ return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
+}
+
+int bpf_map_freeze(int fd)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_fd = fd;
+
+ return sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
+}
+
+int bpf_obj_pin(int fd, const char *pathname)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+ attr.bpf_fd = fd;
+
+ return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
+}
+
+int bpf_obj_get(const char *pathname)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+
+ return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
+int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
+ unsigned int flags)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_bpf_fd = prog_fd;
+ attr.attach_type = type;
+ attr.attach_flags = flags;
+
+ return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_type = type;
+
+ return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_bpf_fd = prog_fd;
+ attr.attach_type = type;
+
+ return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
+ __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
+{
+ union bpf_attr attr;
+ int ret;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.query.target_fd = target_fd;
+ attr.query.attach_type = type;
+ attr.query.query_flags = query_flags;
+ attr.query.prog_cnt = *prog_cnt;
+ attr.query.prog_ids = ptr_to_u64(prog_ids);
+
+ ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
+ if (attach_flags)
+ *attach_flags = attr.query.attach_flags;
+ *prog_cnt = attr.query.prog_cnt;
+ return ret;
+}
+
+int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
+ void *data_out, __u32 *size_out, __u32 *retval,
+ __u32 *duration)
+{
+ union bpf_attr attr;
+ int ret;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.test.prog_fd = prog_fd;
+ attr.test.data_in = ptr_to_u64(data);
+ attr.test.data_out = ptr_to_u64(data_out);
+ attr.test.data_size_in = size;
+ attr.test.repeat = repeat;
+
+ ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
+ if (size_out)
+ *size_out = attr.test.data_size_out;
+ if (retval)
+ *retval = attr.test.retval;
+ if (duration)
+ *duration = attr.test.duration;
+ return ret;
+}
+
+int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
+{
+ union bpf_attr attr;
+ int ret;
+
+ if (!test_attr->data_out && test_attr->data_size_out > 0)
+ return -EINVAL;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.test.prog_fd = test_attr->prog_fd;
+ attr.test.data_in = ptr_to_u64(test_attr->data_in);
+ attr.test.data_out = ptr_to_u64(test_attr->data_out);
+ attr.test.data_size_in = test_attr->data_size_in;
+ attr.test.data_size_out = test_attr->data_size_out;
+ attr.test.ctx_in = ptr_to_u64(test_attr->ctx_in);
+ attr.test.ctx_out = ptr_to_u64(test_attr->ctx_out);
+ attr.test.ctx_size_in = test_attr->ctx_size_in;
+ attr.test.ctx_size_out = test_attr->ctx_size_out;
+ attr.test.repeat = test_attr->repeat;
+
+ ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
+ test_attr->data_size_out = attr.test.data_size_out;
+ test_attr->ctx_size_out = attr.test.ctx_size_out;
+ test_attr->retval = attr.test.retval;
+ test_attr->duration = attr.test.duration;
+ return ret;
+}
+
+static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
+{
+ union bpf_attr attr;
+ int err;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.start_id = start_id;
+
+ err = sys_bpf(cmd, &attr, sizeof(attr));
+ if (!err)
+ *next_id = attr.next_id;
+
+ return err;
+}
+
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ return bpf_obj_get_next_id(start_id, next_id, BPF_PROG_GET_NEXT_ID);
+}
+
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ return bpf_obj_get_next_id(start_id, next_id, BPF_MAP_GET_NEXT_ID);
+}
+
+int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
+}
+
+int bpf_prog_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_id = id;
+
+ return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_map_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.map_id = id;
+
+ return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_btf_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.btf_id = id;
+
+ return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
+{
+ union bpf_attr attr;
+ int err;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.info.bpf_fd = prog_fd;
+ attr.info.info_len = *info_len;
+ attr.info.info = ptr_to_u64(info);
+
+ err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
+ if (!err)
+ *info_len = attr.info.info_len;
+
+ return err;
+}
+
+int bpf_raw_tracepoint_open(const char *name, int prog_fd)
+{
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.raw_tracepoint.name = ptr_to_u64(name);
+ attr.raw_tracepoint.prog_fd = prog_fd;
+
+ return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
+}
+
+int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
+ bool do_log)
+{
+ union bpf_attr attr = {};
+ int fd;
+
+ attr.btf = ptr_to_u64(btf);
+ attr.btf_size = btf_size;
+
+retry:
+ if (do_log && log_buf && log_buf_size) {
+ attr.btf_log_level = 1;
+ attr.btf_log_size = log_buf_size;
+ attr.btf_log_buf = ptr_to_u64(log_buf);
+ }
+
+ fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
+ if (fd == -1 && !do_log && log_buf && log_buf_size) {
+ do_log = true;
+ goto retry;
+ }
+
+ return fd;
+}
+
+int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
+ __u32 *prog_id, __u32 *fd_type, __u64 *probe_offset,
+ __u64 *probe_addr)
+{
+ union bpf_attr attr = {};
+ int err;
+
+ attr.task_fd_query.pid = pid;
+ attr.task_fd_query.fd = fd;
+ attr.task_fd_query.flags = flags;
+ attr.task_fd_query.buf = ptr_to_u64(buf);
+ attr.task_fd_query.buf_len = *buf_len;
+
+ err = sys_bpf(BPF_TASK_FD_QUERY, &attr, sizeof(attr));
+ *buf_len = attr.task_fd_query.buf_len;
+ *prog_id = attr.task_fd_query.prog_id;
+ *fd_type = attr.task_fd_query.fd_type;
+ *probe_offset = attr.task_fd_query.probe_offset;
+ *probe_addr = attr.task_fd_query.probe_addr;
+
+ return err;
+}
diff --git a/src/contrib/libbpf/bpf/bpf.h b/src/contrib/libbpf/bpf/bpf.h
new file mode 100644
index 0000000..3c791fa
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses>
+ */
+#ifndef __LIBBPF_BPF_H
+#define __LIBBPF_BPF_H
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LIBBPF_API
+#define LIBBPF_API __attribute__((visibility("default")))
+#endif
+
+struct bpf_create_map_attr {
+ const char *name;
+ enum bpf_map_type map_type;
+ __u32 map_flags;
+ __u32 key_size;
+ __u32 value_size;
+ __u32 max_entries;
+ __u32 numa_node;
+ __u32 btf_fd;
+ __u32 btf_key_type_id;
+ __u32 btf_value_type_id;
+ __u32 map_ifindex;
+ __u32 inner_map_fd;
+};
+
+LIBBPF_API int
+bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
+LIBBPF_API int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size,
+ int max_entries, __u32 map_flags, int node);
+LIBBPF_API int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size,
+ int max_entries, __u32 map_flags);
+LIBBPF_API int bpf_create_map(enum bpf_map_type map_type, int key_size,
+ int value_size, int max_entries, __u32 map_flags);
+LIBBPF_API int bpf_create_map_in_map_node(enum bpf_map_type map_type,
+ const char *name, int key_size,
+ int inner_map_fd, int max_entries,
+ __u32 map_flags, int node);
+LIBBPF_API int bpf_create_map_in_map(enum bpf_map_type map_type,
+ const char *name, int key_size,
+ int inner_map_fd, int max_entries,
+ __u32 map_flags);
+
+struct bpf_load_program_attr {
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type expected_attach_type;
+ const char *name;
+ const struct bpf_insn *insns;
+ size_t insns_cnt;
+ const char *license;
+ union {
+ __u32 kern_version;
+ __u32 attach_prog_fd;
+ };
+ union {
+ __u32 prog_ifindex;
+ __u32 attach_btf_id;
+ };
+ __u32 prog_btf_fd;
+ __u32 func_info_rec_size;
+ const void *func_info;
+ __u32 func_info_cnt;
+ __u32 line_info_rec_size;
+ const void *line_info;
+ __u32 line_info_cnt;
+ __u32 log_level;
+ __u32 prog_flags;
+};
+
+/* Flags to direct loading requirements */
+#define MAPS_RELAX_COMPAT 0x01
+
+/* Recommend log buffer size */
+#define BPF_LOG_BUF_SIZE (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */
+LIBBPF_API int
+bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
+ char *log_buf, size_t log_buf_sz);
+LIBBPF_API int bpf_load_program(enum bpf_prog_type type,
+ const struct bpf_insn *insns, size_t insns_cnt,
+ const char *license, __u32 kern_version,
+ char *log_buf, size_t log_buf_sz);
+LIBBPF_API int bpf_verify_program(enum bpf_prog_type type,
+ const struct bpf_insn *insns,
+ size_t insns_cnt, __u32 prog_flags,
+ const char *license, __u32 kern_version,
+ char *log_buf, size_t log_buf_sz,
+ int log_level);
+
+LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value,
+ __u64 flags);
+
+LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value);
+LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value,
+ __u64 flags);
+LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key,
+ void *value);
+LIBBPF_API int bpf_map_delete_elem(int fd, const void *key);
+LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key);
+LIBBPF_API int bpf_map_freeze(int fd);
+LIBBPF_API int bpf_obj_pin(int fd, const char *pathname);
+LIBBPF_API int bpf_obj_get(const char *pathname);
+LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd,
+ enum bpf_attach_type type, unsigned int flags);
+LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
+LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd,
+ enum bpf_attach_type type);
+
+struct bpf_prog_test_run_attr {
+ int prog_fd;
+ int repeat;
+ const void *data_in;
+ __u32 data_size_in;
+ void *data_out; /* optional */
+ __u32 data_size_out; /* in: max length of data_out
+ * out: length of data_out */
+ __u32 retval; /* out: return code of the BPF program */
+ __u32 duration; /* out: average per repetition in ns */
+ const void *ctx_in; /* optional */
+ __u32 ctx_size_in;
+ void *ctx_out; /* optional */
+ __u32 ctx_size_out; /* in: max length of ctx_out
+ * out: length of cxt_out */
+};
+
+LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
+
+/*
+ * bpf_prog_test_run does not check that data_out is large enough. Consider
+ * using bpf_prog_test_run_xattr instead.
+ */
+LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
+ __u32 size, void *data_out, __u32 *size_out,
+ __u32 *retval, __u32 *duration);
+LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
+LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
+LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
+LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
+LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
+LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
+LIBBPF_API int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
+LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type,
+ __u32 query_flags, __u32 *attach_flags,
+ __u32 *prog_ids, __u32 *prog_cnt);
+LIBBPF_API int bpf_raw_tracepoint_open(const char *name, int prog_fd);
+LIBBPF_API int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf,
+ __u32 log_buf_size, bool do_log);
+LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
+ __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
+ __u64 *probe_offset, __u64 *probe_addr);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __LIBBPF_BPF_H */
diff --git a/src/contrib/libbpf/bpf/bpf_core_read.h b/src/contrib/libbpf/bpf/bpf_core_read.h
new file mode 100644
index 0000000..7009dc9
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_core_read.h
@@ -0,0 +1,263 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_CORE_READ_H__
+#define __BPF_CORE_READ_H__
+
+/*
+ * enum bpf_field_info_kind is passed as a second argument into
+ * __builtin_preserve_field_info() built-in to get a specific aspect of
+ * a field, captured as a first argument. __builtin_preserve_field_info(field,
+ * info_kind) returns __u32 integer and produces BTF field relocation, which
+ * is understood and processed by libbpf during BPF object loading. See
+ * selftests/bpf for examples.
+ */
+enum bpf_field_info_kind {
+ BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
+ BPF_FIELD_BYTE_SIZE = 1,
+ BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
+ BPF_FIELD_SIGNED = 3,
+ BPF_FIELD_LSHIFT_U64 = 4,
+ BPF_FIELD_RSHIFT_U64 = 5,
+};
+
+#define __CORE_RELO(src, field, info) \
+ __builtin_preserve_field_info((src)->field, BPF_FIELD_##info)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \
+ bpf_probe_read((void *)dst, \
+ __CORE_RELO(src, fld, BYTE_SIZE), \
+ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
+#else
+/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so
+ * for big-endian we need to adjust destination pointer accordingly, based on
+ * field byte size
+ */
+#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \
+ bpf_probe_read((void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \
+ __CORE_RELO(src, fld, BYTE_SIZE), \
+ (const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
+#endif
+
+/*
+ * Extract bitfield, identified by s->field, and return its value as u64.
+ * All this is done in relocatable manner, so bitfield changes such as
+ * signedness, bit size, offset changes, this will be handled automatically.
+ * This version of macro is using bpf_probe_read() to read underlying integer
+ * storage. Macro functions as an expression and its return type is
+ * bpf_probe_read()'s return value: 0, on success, <0 on error.
+ */
+#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \
+ unsigned long long val = 0; \
+ \
+ __CORE_BITFIELD_PROBE_READ(&val, s, field); \
+ val <<= __CORE_RELO(s, field, LSHIFT_U64); \
+ if (__CORE_RELO(s, field, SIGNED)) \
+ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \
+ else \
+ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \
+ val; \
+})
+
+/*
+ * Extract bitfield, identified by s->field, and return its value as u64.
+ * This version of macro is using direct memory reads and should be used from
+ * BPF program types that support such functionality (e.g., typed raw
+ * tracepoints).
+ */
+#define BPF_CORE_READ_BITFIELD(s, field) ({ \
+ const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \
+ unsigned long long val; \
+ \
+ switch (__CORE_RELO(s, field, BYTE_SIZE)) { \
+ case 1: val = *(const unsigned char *)p; \
+ case 2: val = *(const unsigned short *)p; \
+ case 4: val = *(const unsigned int *)p; \
+ case 8: val = *(const unsigned long long *)p; \
+ } \
+ val <<= __CORE_RELO(s, field, LSHIFT_U64); \
+ if (__CORE_RELO(s, field, SIGNED)) \
+ val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \
+ else \
+ val = val >> __CORE_RELO(s, field, RSHIFT_U64); \
+ val; \
+})
+
+/*
+ * Convenience macro to check that field actually exists in target kernel's.
+ * Returns:
+ * 1, if matching field is present in target kernel;
+ * 0, if no matching field found.
+ */
+#define bpf_core_field_exists(field) \
+ __builtin_preserve_field_info(field, BPF_FIELD_EXISTS)
+
+/*
+ * Convenience macro to get byte size of a field. Works for integers,
+ * struct/unions, pointers, arrays, and enums.
+ */
+#define bpf_core_field_size(field) \
+ __builtin_preserve_field_info(field, BPF_FIELD_BYTE_SIZE)
+
+/*
+ * bpf_core_read() abstracts away bpf_probe_read() call and captures offset
+ * relocation for source address using __builtin_preserve_access_index()
+ * built-in, provided by Clang.
+ *
+ * __builtin_preserve_access_index() takes as an argument an expression of
+ * taking an address of a field within struct/union. It makes compiler emit
+ * a relocation, which records BTF type ID describing root struct/union and an
+ * accessor string which describes exact embedded field that was used to take
+ * an address. See detailed description of this relocation format and
+ * semantics in comments to struct bpf_field_reloc in libbpf_internal.h.
+ *
+ * This relocation allows libbpf to adjust BPF instruction to use correct
+ * actual field offset, based on target kernel BTF type that matches original
+ * (local) BTF, used to record relocation.
+ */
+#define bpf_core_read(dst, sz, src) \
+ bpf_probe_read(dst, sz, \
+ (const void *)__builtin_preserve_access_index(src))
+
+/*
+ * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str()
+ * additionally emitting BPF CO-RE field relocation for specified source
+ * argument.
+ */
+#define bpf_core_read_str(dst, sz, src) \
+ bpf_probe_read_str(dst, sz, \
+ (const void *)__builtin_preserve_access_index(src))
+
+#define ___concat(a, b) a ## b
+#define ___apply(fn, n) ___concat(fn, n)
+#define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N
+
+/*
+ * return number of provided arguments; used for switch-based variadic macro
+ * definitions (see ___last, ___arrow, etc below)
+ */
+#define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+/*
+ * return 0 if no arguments are passed, N - otherwise; used for
+ * recursively-defined macros to specify termination (0) case, and generic
+ * (N) case (e.g., ___read_ptrs, ___core_read)
+ */
+#define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
+
+#define ___last1(x) x
+#define ___last2(a, x) x
+#define ___last3(a, b, x) x
+#define ___last4(a, b, c, x) x
+#define ___last5(a, b, c, d, x) x
+#define ___last6(a, b, c, d, e, x) x
+#define ___last7(a, b, c, d, e, f, x) x
+#define ___last8(a, b, c, d, e, f, g, x) x
+#define ___last9(a, b, c, d, e, f, g, h, x) x
+#define ___last10(a, b, c, d, e, f, g, h, i, x) x
+#define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__)
+
+#define ___nolast2(a, _) a
+#define ___nolast3(a, b, _) a, b
+#define ___nolast4(a, b, c, _) a, b, c
+#define ___nolast5(a, b, c, d, _) a, b, c, d
+#define ___nolast6(a, b, c, d, e, _) a, b, c, d, e
+#define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f
+#define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g
+#define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h
+#define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i
+#define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__)
+
+#define ___arrow1(a) a
+#define ___arrow2(a, b) a->b
+#define ___arrow3(a, b, c) a->b->c
+#define ___arrow4(a, b, c, d) a->b->c->d
+#define ___arrow5(a, b, c, d, e) a->b->c->d->e
+#define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f
+#define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g
+#define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h
+#define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i
+#define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j
+#define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__)
+
+#define ___type(...) typeof(___arrow(__VA_ARGS__))
+
+#define ___read(read_fn, dst, src_type, src, accessor) \
+ read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor)
+
+/* "recursively" read a sequence of inner pointers using local __t var */
+#define ___rd_first(src, a) ___read(bpf_core_read, &__t, ___type(src), src, a);
+#define ___rd_last(...) \
+ ___read(bpf_core_read, &__t, \
+ ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__));
+#define ___rd_p1(...) const void *__t; ___rd_first(__VA_ARGS__)
+#define ___rd_p2(...) ___rd_p1(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p3(...) ___rd_p2(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p4(...) ___rd_p3(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p5(...) ___rd_p4(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p6(...) ___rd_p5(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p7(...) ___rd_p6(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p8(...) ___rd_p7(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___rd_p9(...) ___rd_p8(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
+#define ___read_ptrs(src, ...) \
+ ___apply(___rd_p, ___narg(__VA_ARGS__))(src, __VA_ARGS__)
+
+#define ___core_read0(fn, dst, src, a) \
+ ___read(fn, dst, ___type(src), src, a);
+#define ___core_readN(fn, dst, src, ...) \
+ ___read_ptrs(src, ___nolast(__VA_ARGS__)) \
+ ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \
+ ___last(__VA_ARGS__));
+#define ___core_read(fn, dst, src, a, ...) \
+ ___apply(___core_read, ___empty(__VA_ARGS__))(fn, dst, \
+ src, a, ##__VA_ARGS__)
+
+/*
+ * BPF_CORE_READ_INTO() is a more performance-conscious variant of
+ * BPF_CORE_READ(), in which final field is read into user-provided storage.
+ * See BPF_CORE_READ() below for more details on general usage.
+ */
+#define BPF_CORE_READ_INTO(dst, src, a, ...) \
+ ({ \
+ ___core_read(bpf_core_read, dst, src, a, ##__VA_ARGS__) \
+ })
+
+/*
+ * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as
+ * BPF_CORE_READ() for intermediate pointers, but then executes (and returns
+ * corresponding error code) bpf_core_read_str() for final string read.
+ */
+#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) \
+ ({ \
+ ___core_read(bpf_core_read_str, dst, src, a, ##__VA_ARGS__) \
+ })
+
+/*
+ * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially
+ * when there are few pointer chasing steps.
+ * E.g., what in non-BPF world (or in BPF w/ BCC) would be something like:
+ * int x = s->a.b.c->d.e->f->g;
+ * can be succinctly achieved using BPF_CORE_READ as:
+ * int x = BPF_CORE_READ(s, a.b.c, d.e, f, g);
+ *
+ * BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF
+ * CO-RE relocatable bpf_probe_read() wrapper) calls, logically equivalent to:
+ * 1. const void *__t = s->a.b.c;
+ * 2. __t = __t->d.e;
+ * 3. __t = __t->f;
+ * 4. return __t->g;
+ *
+ * Equivalence is logical, because there is a heavy type casting/preservation
+ * involved, as well as all the reads are happening through bpf_probe_read()
+ * calls using __builtin_preserve_access_index() to emit CO-RE relocations.
+ *
+ * N.B. Only up to 9 "field accessors" are supported, which should be more
+ * than enough for any practical purpose.
+ */
+#define BPF_CORE_READ(src, a, ...) \
+ ({ \
+ ___type(src, a, ##__VA_ARGS__) __r; \
+ BPF_CORE_READ_INTO(&__r, src, a, ##__VA_ARGS__); \
+ __r; \
+ })
+
+#endif
+
diff --git a/src/contrib/libbpf/bpf/bpf_endian.h b/src/contrib/libbpf/bpf/bpf_endian.h
new file mode 100644
index 0000000..fbe2800
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_endian.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_ENDIAN__
+#define __BPF_ENDIAN__
+
+#include <linux/stddef.h>
+#include <linux/swab.h>
+
+/* LLVM's BPF target selects the endianness of the CPU
+ * it compiles on, or the user specifies (bpfel/bpfeb),
+ * respectively. The used __BYTE_ORDER__ is defined by
+ * the compiler, we cannot rely on __BYTE_ORDER from
+ * libc headers, since it doesn't reflect the actual
+ * requested byte order.
+ *
+ * Note, LLVM's BPF target has different __builtin_bswapX()
+ * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE
+ * in bpfel and bpfeb case, which means below, that we map
+ * to cpu_to_be16(). We could use it unconditionally in BPF
+ * case, but better not rely on it, so that this header here
+ * can be used from application and BPF program side, which
+ * use different targets.
+ */
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define __bpf_ntohs(x) __builtin_bswap16(x)
+# define __bpf_htons(x) __builtin_bswap16(x)
+# define __bpf_constant_ntohs(x) ___constant_swab16(x)
+# define __bpf_constant_htons(x) ___constant_swab16(x)
+# define __bpf_ntohl(x) __builtin_bswap32(x)
+# define __bpf_htonl(x) __builtin_bswap32(x)
+# define __bpf_constant_ntohl(x) ___constant_swab32(x)
+# define __bpf_constant_htonl(x) ___constant_swab32(x)
+# define __bpf_be64_to_cpu(x) __builtin_bswap64(x)
+# define __bpf_cpu_to_be64(x) __builtin_bswap64(x)
+# define __bpf_constant_be64_to_cpu(x) ___constant_swab64(x)
+# define __bpf_constant_cpu_to_be64(x) ___constant_swab64(x)
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define __bpf_ntohs(x) (x)
+# define __bpf_htons(x) (x)
+# define __bpf_constant_ntohs(x) (x)
+# define __bpf_constant_htons(x) (x)
+# define __bpf_ntohl(x) (x)
+# define __bpf_htonl(x) (x)
+# define __bpf_constant_ntohl(x) (x)
+# define __bpf_constant_htonl(x) (x)
+# define __bpf_be64_to_cpu(x) (x)
+# define __bpf_cpu_to_be64(x) (x)
+# define __bpf_constant_be64_to_cpu(x) (x)
+# define __bpf_constant_cpu_to_be64(x) (x)
+#else
+# error "Fix your compiler's __BYTE_ORDER__?!"
+#endif
+
+#define bpf_htons(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_htons(x) : __bpf_htons(x))
+#define bpf_ntohs(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_ntohs(x) : __bpf_ntohs(x))
+#define bpf_htonl(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_htonl(x) : __bpf_htonl(x))
+#define bpf_ntohl(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_ntohl(x) : __bpf_ntohl(x))
+#define bpf_cpu_to_be64(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x))
+#define bpf_be64_to_cpu(x) \
+ (__builtin_constant_p(x) ? \
+ __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x))
+
+#endif /* __BPF_ENDIAN__ */
diff --git a/src/contrib/libbpf/bpf/bpf_helper_defs.h b/src/contrib/libbpf/bpf/bpf_helper_defs.h
new file mode 100644
index 0000000..1f357f6
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_helper_defs.h
@@ -0,0 +1,2759 @@
+/* This is auto-generated file. See bpf_helpers_doc.py for details. */
+
+/* Forward declarations of BPF structs */
+struct bpf_fib_lookup;
+struct bpf_perf_event_data;
+struct bpf_perf_event_value;
+struct bpf_sock;
+struct bpf_sock_addr;
+struct bpf_sock_ops;
+struct bpf_sock_tuple;
+struct bpf_spin_lock;
+struct bpf_sysctl;
+struct bpf_tcp_sock;
+struct bpf_tunnel_key;
+struct bpf_xfrm_state;
+struct pt_regs;
+struct sk_reuseport_md;
+struct sockaddr;
+struct tcphdr;
+struct __sk_buff;
+struct sk_msg_md;
+struct xdp_md;
+
+/*
+ * bpf_map_lookup_elem
+ *
+ * Perform a lookup in *map* for an entry associated to *key*.
+ *
+ * Returns
+ * Map value associated to *key*, or **NULL** if no entry was
+ * found.
+ */
+static void *(*bpf_map_lookup_elem)(void *map, const void *key) = (void *) 1;
+
+/*
+ * bpf_map_update_elem
+ *
+ * Add or update the value of the entry associated to *key* in
+ * *map* with *value*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * Flag value **BPF_NOEXIST** cannot be used for maps of types
+ * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all
+ * elements always exist), the helper would return an error.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_map_update_elem)(void *map, const void *key, const void *value, __u64 flags) = (void *) 2;
+
+/*
+ * bpf_map_delete_elem
+ *
+ * Delete entry with *key* from *map*.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_map_delete_elem)(void *map, const void *key) = (void *) 3;
+
+/*
+ * bpf_probe_read
+ *
+ * For tracing programs, safely attempt to read *size* bytes from
+ * kernel space address *unsafe_ptr* and store the data in *dst*.
+ *
+ * Generally, use bpf_probe_read_user() or bpf_probe_read_kernel()
+ * instead.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_probe_read)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 4;
+
+/*
+ * bpf_ktime_get_ns
+ *
+ * Return the time elapsed since system boot, in nanoseconds.
+ *
+ * Returns
+ * Current *ktime*.
+ */
+static __u64 (*bpf_ktime_get_ns)(void) = (void *) 5;
+
+/*
+ * bpf_trace_printk
+ *
+ * This helper is a "printk()-like" facility for debugging. It
+ * prints a message defined by format *fmt* (of size *fmt_size*)
+ * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if
+ * available. It can take up to three additional **u64**
+ * arguments (as an eBPF helpers, the total number of arguments is
+ * limited to five).
+ *
+ * Each time the helper is called, it appends a line to the trace.
+ * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
+ * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
+ * The format of the trace is customizable, and the exact output
+ * one will get depends on the options set in
+ * *\/sys/kernel/debug/tracing/trace_options* (see also the
+ * *README* file under the same directory). However, it usually
+ * defaults to something like:
+ *
+ * ::
+ *
+ * telnet-470 [001] .N.. 419421.045894: 0x00000001: <formatted msg>
+ *
+ * In the above:
+ *
+ * * ``telnet`` is the name of the current task.
+ * * ``470`` is the PID of the current task.
+ * * ``001`` is the CPU number on which the task is
+ * running.
+ * * In ``.N..``, each character refers to a set of
+ * options (whether irqs are enabled, scheduling
+ * options, whether hard/softirqs are running, level of
+ * preempt_disabled respectively). **N** means that
+ * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED**
+ * are set.
+ * * ``419421.045894`` is a timestamp.
+ * * ``0x00000001`` is a fake value used by BPF for the
+ * instruction pointer register.
+ * * ``<formatted msg>`` is the message formatted with
+ * *fmt*.
+ *
+ * The conversion specifiers supported by *fmt* are similar, but
+ * more limited than for printk(). They are **%d**, **%i**,
+ * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**,
+ * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size
+ * of field, padding with zeroes, etc.) is available, and the
+ * helper will return **-EINVAL** (but print nothing) if it
+ * encounters an unknown specifier.
+ *
+ * Also, note that **bpf_trace_printk**\ () is slow, and should
+ * only be used for debugging purposes. For this reason, a notice
+ * bloc (spanning several lines) is printed to kernel logs and
+ * states that the helper should not be used "for production use"
+ * the first time this helper is used (or more precisely, when
+ * **trace_printk**\ () buffers are allocated). For passing values
+ * to user space, perf events should be preferred.
+ *
+ * Returns
+ * The number of bytes written to the buffer, or a negative error
+ * in case of failure.
+ */
+static int (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) 6;
+
+/*
+ * bpf_get_prandom_u32
+ *
+ * Get a pseudo-random number.
+ *
+ * From a security point of view, this helper uses its own
+ * pseudo-random internal state, and cannot be used to infer the
+ * seed of other random functions in the kernel. However, it is
+ * essential to note that the generator used by the helper is not
+ * cryptographically secure.
+ *
+ * Returns
+ * A random 32-bit unsigned value.
+ */
+static __u32 (*bpf_get_prandom_u32)(void) = (void *) 7;
+
+/*
+ * bpf_get_smp_processor_id
+ *
+ * Get the SMP (symmetric multiprocessing) processor id. Note that
+ * all programs run with preemption disabled, which means that the
+ * SMP processor id is stable during all the execution of the
+ * program.
+ *
+ * Returns
+ * The SMP id of the processor running the program.
+ */
+static __u32 (*bpf_get_smp_processor_id)(void) = (void *) 8;
+
+/*
+ * bpf_skb_store_bytes
+ *
+ * Store *len* bytes from address *from* into the packet
+ * associated to *skb*, at *offset*. *flags* are a combination of
+ * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the
+ * checksum for the packet after storing the bytes) and
+ * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\
+ * **->swhash** and *skb*\ **->l4hash** to 0).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len, __u64 flags) = (void *) 9;
+
+/*
+ * bpf_l3_csum_replace
+ *
+ * Recompute the layer 3 (e.g. IP) checksum for the packet
+ * associated to *skb*. Computation is incremental, so the helper
+ * must know the former value of the header field that was
+ * modified (*from*), the new value of this field (*to*), and the
+ * number of bytes (2 or 4) for this field, stored in *size*.
+ * Alternatively, it is possible to store the difference between
+ * the previous and the new values of the header field in *to*, by
+ * setting *from* and *size* to 0. For both methods, *offset*
+ * indicates the location of the IP checksum within the packet.
+ *
+ * This helper works in combination with **bpf_csum_diff**\ (),
+ * which does not update the checksum in-place, but offers more
+ * flexibility and can handle sizes larger than 2 or 4 for the
+ * checksum to update.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_l3_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 size) = (void *) 10;
+
+/*
+ * bpf_l4_csum_replace
+ *
+ * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
+ * packet associated to *skb*. Computation is incremental, so the
+ * helper must know the former value of the header field that was
+ * modified (*from*), the new value of this field (*to*), and the
+ * number of bytes (2 or 4) for this field, stored on the lowest
+ * four bits of *flags*. Alternatively, it is possible to store
+ * the difference between the previous and the new values of the
+ * header field in *to*, by setting *from* and the four lowest
+ * bits of *flags* to 0. For both methods, *offset* indicates the
+ * location of the IP checksum within the packet. In addition to
+ * the size of the field, *flags* can be added (bitwise OR) actual
+ * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
+ * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
+ * for updates resulting in a null checksum the value is set to
+ * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
+ * the checksum is to be computed against a pseudo-header.
+ *
+ * This helper works in combination with **bpf_csum_diff**\ (),
+ * which does not update the checksum in-place, but offers more
+ * flexibility and can handle sizes larger than 2 or 4 for the
+ * checksum to update.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_l4_csum_replace)(struct __sk_buff *skb, __u32 offset, __u64 from, __u64 to, __u64 flags) = (void *) 11;
+
+/*
+ * bpf_tail_call
+ *
+ * This special helper is used to trigger a "tail call", or in
+ * other words, to jump into another eBPF program. The same stack
+ * frame is used (but values on stack and in registers for the
+ * caller are not accessible to the callee). This mechanism allows
+ * for program chaining, either for raising the maximum number of
+ * available eBPF instructions, or to execute given programs in
+ * conditional blocks. For security reasons, there is an upper
+ * limit to the number of successive tail calls that can be
+ * performed.
+ *
+ * Upon call of this helper, the program attempts to jump into a
+ * program referenced at index *index* in *prog_array_map*, a
+ * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes
+ * *ctx*, a pointer to the context.
+ *
+ * If the call succeeds, the kernel immediately runs the first
+ * instruction of the new program. This is not a function call,
+ * and it never returns to the previous program. If the call
+ * fails, then the helper has no effect, and the caller continues
+ * to run its subsequent instructions. A call can fail if the
+ * destination program for the jump does not exist (i.e. *index*
+ * is superior to the number of entries in *prog_array_map*), or
+ * if the maximum number of tail calls has been reached for this
+ * chain of programs. This limit is defined in the kernel by the
+ * macro **MAX_TAIL_CALL_CNT** (not accessible to user space),
+ * which is currently set to 32.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_tail_call)(void *ctx, void *prog_array_map, __u32 index) = (void *) 12;
+
+/*
+ * bpf_clone_redirect
+ *
+ * Clone and redirect the packet associated to *skb* to another
+ * net device of index *ifindex*. Both ingress and egress
+ * interfaces can be used for redirection. The **BPF_F_INGRESS**
+ * value in *flags* is used to make the distinction (ingress path
+ * is selected if the flag is present, egress path otherwise).
+ * This is the only flag supported for now.
+ *
+ * In comparison with **bpf_redirect**\ () helper,
+ * **bpf_clone_redirect**\ () has the associated cost of
+ * duplicating the packet buffer, but this can be executed out of
+ * the eBPF program. Conversely, **bpf_redirect**\ () is more
+ * efficient, but it is handled through an action code where the
+ * redirection happens only after the eBPF program has returned.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_clone_redirect)(struct __sk_buff *skb, __u32 ifindex, __u64 flags) = (void *) 13;
+
+/*
+ * bpf_get_current_pid_tgid
+ *
+ *
+ * Returns
+ * A 64-bit integer containing the current tgid and pid, and
+ * created as such:
+ * *current_task*\ **->tgid << 32 \|**
+ * *current_task*\ **->pid**.
+ */
+static __u64 (*bpf_get_current_pid_tgid)(void) = (void *) 14;
+
+/*
+ * bpf_get_current_uid_gid
+ *
+ *
+ * Returns
+ * A 64-bit integer containing the current GID and UID, and
+ * created as such: *current_gid* **<< 32 \|** *current_uid*.
+ */
+static __u64 (*bpf_get_current_uid_gid)(void) = (void *) 15;
+
+/*
+ * bpf_get_current_comm
+ *
+ * Copy the **comm** attribute of the current task into *buf* of
+ * *size_of_buf*. The **comm** attribute contains the name of
+ * the executable (excluding the path) for the current task. The
+ * *size_of_buf* must be strictly positive. On success, the
+ * helper makes sure that the *buf* is NUL-terminated. On failure,
+ * it is filled with zeroes.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_get_current_comm)(void *buf, __u32 size_of_buf) = (void *) 16;
+
+/*
+ * bpf_get_cgroup_classid
+ *
+ * Retrieve the classid for the current task, i.e. for the net_cls
+ * cgroup to which *skb* belongs.
+ *
+ * This helper can be used on TC egress path, but not on ingress.
+ *
+ * The net_cls cgroup provides an interface to tag network packets
+ * based on a user-provided identifier for all traffic coming from
+ * the tasks belonging to the related cgroup. See also the related
+ * kernel documentation, available from the Linux sources in file
+ * *Documentation/admin-guide/cgroup-v1/net_cls.rst*.
+ *
+ * The Linux kernel has two versions for cgroups: there are
+ * cgroups v1 and cgroups v2. Both are available to users, who can
+ * use a mixture of them, but note that the net_cls cgroup is for
+ * cgroup v1 only. This makes it incompatible with BPF programs
+ * run on cgroups, which is a cgroup-v2-only feature (a socket can
+ * only hold data for one version of cgroups at a time).
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to
+ * "**y**" or to "**m**".
+ *
+ * Returns
+ * The classid, or 0 for the default unconfigured classid.
+ */
+static __u32 (*bpf_get_cgroup_classid)(struct __sk_buff *skb) = (void *) 17;
+
+/*
+ * bpf_skb_vlan_push
+ *
+ * Push a *vlan_tci* (VLAN tag control information) of protocol
+ * *vlan_proto* to the packet associated to *skb*, then update
+ * the checksum. Note that if *vlan_proto* is different from
+ * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to
+ * be **ETH_P_8021Q**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_vlan_push)(struct __sk_buff *skb, __be16 vlan_proto, __u16 vlan_tci) = (void *) 18;
+
+/*
+ * bpf_skb_vlan_pop
+ *
+ * Pop a VLAN header from the packet associated to *skb*.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_vlan_pop)(struct __sk_buff *skb) = (void *) 19;
+
+/*
+ * bpf_skb_get_tunnel_key
+ *
+ * Get tunnel metadata. This helper takes a pointer *key* to an
+ * empty **struct bpf_tunnel_key** of **size**, that will be
+ * filled with tunnel metadata for the packet associated to *skb*.
+ * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which
+ * indicates that the tunnel is based on IPv6 protocol instead of
+ * IPv4.
+ *
+ * The **struct bpf_tunnel_key** is an object that generalizes the
+ * principal parameters used by various tunneling protocols into a
+ * single struct. This way, it can be used to easily make a
+ * decision based on the contents of the encapsulation header,
+ * "summarized" in this struct. In particular, it holds the IP
+ * address of the remote end (IPv4 or IPv6, depending on the case)
+ * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also,
+ * this struct exposes the *key*\ **->tunnel_id**, which is
+ * generally mapped to a VNI (Virtual Network Identifier), making
+ * it programmable together with the **bpf_skb_set_tunnel_key**\
+ * () helper.
+ *
+ * Let's imagine that the following code is part of a program
+ * attached to the TC ingress interface, on one end of a GRE
+ * tunnel, and is supposed to filter out all messages coming from
+ * remote ends with IPv4 address other than 10.0.0.1:
+ *
+ * ::
+ *
+ * int ret;
+ * struct bpf_tunnel_key key = {};
+ *
+ * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+ * if (ret < 0)
+ * return TC_ACT_SHOT; // drop packet
+ *
+ * if (key.remote_ipv4 != 0x0a000001)
+ * return TC_ACT_SHOT; // drop packet
+ *
+ * return TC_ACT_OK; // accept packet
+ *
+ * This interface can also be used with all encapsulation devices
+ * that can operate in "collect metadata" mode: instead of having
+ * one network device per specific configuration, the "collect
+ * metadata" mode only requires a single device where the
+ * configuration can be extracted from this helper.
+ *
+ * This can be used together with various tunnels such as VXLan,
+ * Geneve, GRE or IP in IP (IPIP).
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_get_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 20;
+
+/*
+ * bpf_skb_set_tunnel_key
+ *
+ * Populate tunnel metadata for packet associated to *skb.* The
+ * tunnel metadata is set to the contents of *key*, of *size*. The
+ * *flags* can be set to a combination of the following values:
+ *
+ * **BPF_F_TUNINFO_IPV6**
+ * Indicate that the tunnel is based on IPv6 protocol
+ * instead of IPv4.
+ * **BPF_F_ZERO_CSUM_TX**
+ * For IPv4 packets, add a flag to tunnel metadata
+ * indicating that checksum computation should be skipped
+ * and checksum set to zeroes.
+ * **BPF_F_DONT_FRAGMENT**
+ * Add a flag to tunnel metadata indicating that the
+ * packet should not be fragmented.
+ * **BPF_F_SEQ_NUMBER**
+ * Add a flag to tunnel metadata indicating that a
+ * sequence number should be added to tunnel header before
+ * sending the packet. This flag was added for GRE
+ * encapsulation, but might be used with other protocols
+ * as well in the future.
+ *
+ * Here is a typical usage on the transmit path:
+ *
+ * ::
+ *
+ * struct bpf_tunnel_key key;
+ * populate key ...
+ * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
+ * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0);
+ *
+ * See also the description of the **bpf_skb_get_tunnel_key**\ ()
+ * helper for additional information.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_set_tunnel_key)(struct __sk_buff *skb, struct bpf_tunnel_key *key, __u32 size, __u64 flags) = (void *) 21;
+
+/*
+ * bpf_perf_event_read
+ *
+ * Read the value of a perf event counter. This helper relies on a
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of
+ * the perf event counter is selected when *map* is updated with
+ * perf event file descriptors. The *map* is an array whose size
+ * is the number of available CPUs, and each cell contains a value
+ * relative to one CPU. The value to retrieve is indicated by
+ * *flags*, that contains the index of the CPU to look up, masked
+ * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
+ * **BPF_F_CURRENT_CPU** to indicate that the value for the
+ * current CPU should be retrieved.
+ *
+ * Note that before Linux 4.13, only hardware perf event can be
+ * retrieved.
+ *
+ * Also, be aware that the newer helper
+ * **bpf_perf_event_read_value**\ () is recommended over
+ * **bpf_perf_event_read**\ () in general. The latter has some ABI
+ * quirks where error and counter value are used as a return code
+ * (which is wrong to do since ranges may overlap). This issue is
+ * fixed with **bpf_perf_event_read_value**\ (), which at the same
+ * time provides more features over the **bpf_perf_event_read**\
+ * () interface. Please refer to the description of
+ * **bpf_perf_event_read_value**\ () for details.
+ *
+ * Returns
+ * The value of the perf event counter read from the map, or a
+ * negative error code in case of failure.
+ */
+static __u64 (*bpf_perf_event_read)(void *map, __u64 flags) = (void *) 22;
+
+/*
+ * bpf_redirect
+ *
+ * Redirect the packet to another net device of index *ifindex*.
+ * This helper is somewhat similar to **bpf_clone_redirect**\
+ * (), except that the packet is not cloned, which provides
+ * increased performance.
+ *
+ * Except for XDP, both ingress and egress interfaces can be used
+ * for redirection. The **BPF_F_INGRESS** value in *flags* is used
+ * to make the distinction (ingress path is selected if the flag
+ * is present, egress path otherwise). Currently, XDP only
+ * supports redirection to the egress interface, and accepts no
+ * flag at all.
+ *
+ * The same effect can be attained with the more generic
+ * **bpf_redirect_map**\ (), which requires specific maps to be
+ * used but offers better performance.
+ *
+ * Returns
+ * For XDP, the helper returns **XDP_REDIRECT** on success or
+ * **XDP_ABORTED** on error. For other program types, the values
+ * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on
+ * error.
+ */
+static int (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void *) 23;
+
+/*
+ * bpf_get_route_realm
+ *
+ * Retrieve the realm or the route, that is to say the
+ * **tclassid** field of the destination for the *skb*. The
+ * indentifier retrieved is a user-provided tag, similar to the
+ * one used with the net_cls cgroup (see description for
+ * **bpf_get_cgroup_classid**\ () helper), but here this tag is
+ * held by a route (a destination entry), not by a task.
+ *
+ * Retrieving this identifier works with the clsact TC egress hook
+ * (see also **tc-bpf(8)**), or alternatively on conventional
+ * classful egress qdiscs, but not on TC ingress path. In case of
+ * clsact TC egress hook, this has the advantage that, internally,
+ * the destination entry has not been dropped yet in the transmit
+ * path. Therefore, the destination entry does not need to be
+ * artificially held via **netif_keep_dst**\ () for a classful
+ * qdisc until the *skb* is freed.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_IP_ROUTE_CLASSID** configuration option.
+ *
+ * Returns
+ * The realm of the route for the packet associated to *skb*, or 0
+ * if none was found.
+ */
+static __u32 (*bpf_get_route_realm)(struct __sk_buff *skb) = (void *) 24;
+
+/*
+ * bpf_perf_event_output
+ *
+ * Write raw *data* blob into a special BPF perf event held by
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
+ * event must have the following attributes: **PERF_SAMPLE_RAW**
+ * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
+ * **PERF_COUNT_SW_BPF_OUTPUT** as **config**.
+ *
+ * The *flags* are used to indicate the index in *map* for which
+ * the value must be put, masked with **BPF_F_INDEX_MASK**.
+ * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
+ * to indicate that the index of the current CPU core should be
+ * used.
+ *
+ * The value to write, of *size*, is passed through eBPF stack and
+ * pointed by *data*.
+ *
+ * The context of the program *ctx* needs also be passed to the
+ * helper.
+ *
+ * On user space, a program willing to read the values needs to
+ * call **perf_event_open**\ () on the perf event (either for
+ * one or for all CPUs) and to store the file descriptor into the
+ * *map*. This must be done before the eBPF program can send data
+ * into it. An example is available in file
+ * *samples/bpf/trace_output_user.c* in the Linux kernel source
+ * tree (the eBPF program counterpart is in
+ * *samples/bpf/trace_output_kern.c*).
+ *
+ * **bpf_perf_event_output**\ () achieves better performance
+ * than **bpf_trace_printk**\ () for sharing data with user
+ * space, and is much better suitable for streaming data from eBPF
+ * programs.
+ *
+ * Note that this helper is not restricted to tracing use cases
+ * and can be used with programs attached to TC or XDP as well,
+ * where it allows for passing data to user space listeners. Data
+ * can be:
+ *
+ * * Only custom structs,
+ * * Only the packet payload, or
+ * * A combination of both.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_perf_event_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 25;
+
+/*
+ * bpf_skb_load_bytes
+ *
+ * This helper was provided as an easy way to load data from a
+ * packet. It can be used to load *len* bytes from *offset* from
+ * the packet associated to *skb*, into the buffer pointed by
+ * *to*.
+ *
+ * Since Linux 4.7, usage of this helper has mostly been replaced
+ * by "direct packet access", enabling packet data to be
+ * manipulated with *skb*\ **->data** and *skb*\ **->data_end**
+ * pointing respectively to the first byte of packet data and to
+ * the byte after the last byte of packet data. However, it
+ * remains useful if one wishes to read large quantities of data
+ * at once from a packet into the eBPF stack.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_load_bytes)(const void *skb, __u32 offset, void *to, __u32 len) = (void *) 26;
+
+/*
+ * bpf_get_stackid
+ *
+ * Walk a user or a kernel stack and return its id. To achieve
+ * this, the helper needs *ctx*, which is a pointer to the context
+ * on which the tracing program is executed, and a pointer to a
+ * *map* of type **BPF_MAP_TYPE_STACK_TRACE**.
+ *
+ * The last argument, *flags*, holds the number of stack frames to
+ * skip (from 0 to 255), masked with
+ * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
+ * a combination of the following flags:
+ *
+ * **BPF_F_USER_STACK**
+ * Collect a user space stack instead of a kernel stack.
+ * **BPF_F_FAST_STACK_CMP**
+ * Compare stacks by hash only.
+ * **BPF_F_REUSE_STACKID**
+ * If two different stacks hash into the same *stackid*,
+ * discard the old one.
+ *
+ * The stack id retrieved is a 32 bit long integer handle which
+ * can be further combined with other data (including other stack
+ * ids) and used as a key into maps. This can be useful for
+ * generating a variety of graphs (such as flame graphs or off-cpu
+ * graphs).
+ *
+ * For walking a stack, this helper is an improvement over
+ * **bpf_probe_read**\ (), which can be used with unrolled loops
+ * but is not efficient and consumes a lot of eBPF instructions.
+ * Instead, **bpf_get_stackid**\ () can collect up to
+ * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that
+ * this limit can be controlled with the **sysctl** program, and
+ * that it should be manually increased in order to profile long
+ * user stacks (such as stacks for Java programs). To do so, use:
+ *
+ * ::
+ *
+ * # sysctl kernel.perf_event_max_stack=<new value>
+ *
+ * Returns
+ * The positive or null stack id on success, or a negative error
+ * in case of failure.
+ */
+static int (*bpf_get_stackid)(void *ctx, void *map, __u64 flags) = (void *) 27;
+
+/*
+ * bpf_csum_diff
+ *
+ * Compute a checksum difference, from the raw buffer pointed by
+ * *from*, of length *from_size* (that must be a multiple of 4),
+ * towards the raw buffer pointed by *to*, of size *to_size*
+ * (same remark). An optional *seed* can be added to the value
+ * (this can be cascaded, the seed may come from a previous call
+ * to the helper).
+ *
+ * This is flexible enough to be used in several ways:
+ *
+ * * With *from_size* == 0, *to_size* > 0 and *seed* set to
+ * checksum, it can be used when pushing new data.
+ * * With *from_size* > 0, *to_size* == 0 and *seed* set to
+ * checksum, it can be used when removing data from a packet.
+ * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it
+ * can be used to compute a diff. Note that *from_size* and
+ * *to_size* do not need to be equal.
+ *
+ * This helper can be used in combination with
+ * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to
+ * which one can feed in the difference computed with
+ * **bpf_csum_diff**\ ().
+ *
+ * Returns
+ * The checksum result, or a negative error code in case of
+ * failure.
+ */
+static __s64 (*bpf_csum_diff)(__be32 *from, __u32 from_size, __be32 *to, __u32 to_size, __wsum seed) = (void *) 28;
+
+/*
+ * bpf_skb_get_tunnel_opt
+ *
+ * Retrieve tunnel options metadata for the packet associated to
+ * *skb*, and store the raw tunnel option data to the buffer *opt*
+ * of *size*.
+ *
+ * This helper can be used with encapsulation devices that can
+ * operate in "collect metadata" mode (please refer to the related
+ * note in the description of **bpf_skb_get_tunnel_key**\ () for
+ * more details). A particular example where this can be used is
+ * in combination with the Geneve encapsulation protocol, where it
+ * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper)
+ * and retrieving arbitrary TLVs (Type-Length-Value headers) from
+ * the eBPF program. This allows for full customization of these
+ * headers.
+ *
+ * Returns
+ * The size of the option data retrieved.
+ */
+static int (*bpf_skb_get_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 29;
+
+/*
+ * bpf_skb_set_tunnel_opt
+ *
+ * Set tunnel options metadata for the packet associated to *skb*
+ * to the option data contained in the raw buffer *opt* of *size*.
+ *
+ * See also the description of the **bpf_skb_get_tunnel_opt**\ ()
+ * helper for additional information.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_set_tunnel_opt)(struct __sk_buff *skb, void *opt, __u32 size) = (void *) 30;
+
+/*
+ * bpf_skb_change_proto
+ *
+ * Change the protocol of the *skb* to *proto*. Currently
+ * supported are transition from IPv4 to IPv6, and from IPv6 to
+ * IPv4. The helper takes care of the groundwork for the
+ * transition, including resizing the socket buffer. The eBPF
+ * program is expected to fill the new headers, if any, via
+ * **skb_store_bytes**\ () and to recompute the checksums with
+ * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\
+ * (). The main case for this helper is to perform NAT64
+ * operations out of an eBPF program.
+ *
+ * Internally, the GSO type is marked as dodgy so that headers are
+ * checked and segments are recalculated by the GSO/GRO engine.
+ * The size for GSO target is adapted as well.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_change_proto)(struct __sk_buff *skb, __be16 proto, __u64 flags) = (void *) 31;
+
+/*
+ * bpf_skb_change_type
+ *
+ * Change the packet type for the packet associated to *skb*. This
+ * comes down to setting *skb*\ **->pkt_type** to *type*, except
+ * the eBPF program does not have a write access to *skb*\
+ * **->pkt_type** beside this helper. Using a helper here allows
+ * for graceful handling of errors.
+ *
+ * The major use case is to change incoming *skb*s to
+ * **PACKET_HOST** in a programmatic way instead of having to
+ * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for
+ * example.
+ *
+ * Note that *type* only allows certain values. At this time, they
+ * are:
+ *
+ * **PACKET_HOST**
+ * Packet is for us.
+ * **PACKET_BROADCAST**
+ * Send packet to all.
+ * **PACKET_MULTICAST**
+ * Send packet to group.
+ * **PACKET_OTHERHOST**
+ * Send packet to someone else.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_change_type)(struct __sk_buff *skb, __u32 type) = (void *) 32;
+
+/*
+ * bpf_skb_under_cgroup
+ *
+ * Check whether *skb* is a descendant of the cgroup2 held by
+ * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
+ *
+ * Returns
+ * The return value depends on the result of the test, and can be:
+ *
+ * * 0, if the *skb* failed the cgroup2 descendant test.
+ * * 1, if the *skb* succeeded the cgroup2 descendant test.
+ * * A negative error code, if an error occurred.
+ */
+static int (*bpf_skb_under_cgroup)(struct __sk_buff *skb, void *map, __u32 index) = (void *) 33;
+
+/*
+ * bpf_get_hash_recalc
+ *
+ * Retrieve the hash of the packet, *skb*\ **->hash**. If it is
+ * not set, in particular if the hash was cleared due to mangling,
+ * recompute this hash. Later accesses to the hash can be done
+ * directly with *skb*\ **->hash**.
+ *
+ * Calling **bpf_set_hash_invalid**\ (), changing a packet
+ * prototype with **bpf_skb_change_proto**\ (), or calling
+ * **bpf_skb_store_bytes**\ () with the
+ * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear
+ * the hash and to trigger a new computation for the next call to
+ * **bpf_get_hash_recalc**\ ().
+ *
+ * Returns
+ * The 32-bit hash.
+ */
+static __u32 (*bpf_get_hash_recalc)(struct __sk_buff *skb) = (void *) 34;
+
+/*
+ * bpf_get_current_task
+ *
+ *
+ * Returns
+ * A pointer to the current task struct.
+ */
+static __u64 (*bpf_get_current_task)(void) = (void *) 35;
+
+/*
+ * bpf_probe_write_user
+ *
+ * Attempt in a safe way to write *len* bytes from the buffer
+ * *src* to *dst* in memory. It only works for threads that are in
+ * user context, and *dst* must be a valid user space address.
+ *
+ * This helper should not be used to implement any kind of
+ * security mechanism because of TOC-TOU attacks, but rather to
+ * debug, divert, and manipulate execution of semi-cooperative
+ * processes.
+ *
+ * Keep in mind that this feature is meant for experiments, and it
+ * has a risk of crashing the system and running programs.
+ * Therefore, when an eBPF program using this helper is attached,
+ * a warning including PID and process name is printed to kernel
+ * logs.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_probe_write_user)(void *dst, const void *src, __u32 len) = (void *) 36;
+
+/*
+ * bpf_current_task_under_cgroup
+ *
+ * Check whether the probe is being run is the context of a given
+ * subset of the cgroup2 hierarchy. The cgroup2 to test is held by
+ * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
+ *
+ * Returns
+ * The return value depends on the result of the test, and can be:
+ *
+ * * 0, if the *skb* task belongs to the cgroup2.
+ * * 1, if the *skb* task does not belong to the cgroup2.
+ * * A negative error code, if an error occurred.
+ */
+static int (*bpf_current_task_under_cgroup)(void *map, __u32 index) = (void *) 37;
+
+/*
+ * bpf_skb_change_tail
+ *
+ * Resize (trim or grow) the packet associated to *skb* to the
+ * new *len*. The *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * The basic idea is that the helper performs the needed work to
+ * change the size of the packet, then the eBPF program rewrites
+ * the rest via helpers like **bpf_skb_store_bytes**\ (),
+ * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ ()
+ * and others. This helper is a slow path utility intended for
+ * replies with control messages. And because it is targeted for
+ * slow path, the helper itself can afford to be slow: it
+ * implicitly linearizes, unclones and drops offloads from the
+ * *skb*.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_change_tail)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 38;
+
+/*
+ * bpf_skb_pull_data
+ *
+ * Pull in non-linear data in case the *skb* is non-linear and not
+ * all of *len* are part of the linear section. Make *len* bytes
+ * from *skb* readable and writable. If a zero value is passed for
+ * *len*, then the whole length of the *skb* is pulled.
+ *
+ * This helper is only needed for reading and writing with direct
+ * packet access.
+ *
+ * For direct packet access, testing that offsets to access
+ * are within packet boundaries (test on *skb*\ **->data_end**) is
+ * susceptible to fail if offsets are invalid, or if the requested
+ * data is in non-linear parts of the *skb*. On failure the
+ * program can just bail out, or in the case of a non-linear
+ * buffer, use a helper to make the data available. The
+ * **bpf_skb_load_bytes**\ () helper is a first solution to access
+ * the data. Another one consists in using **bpf_skb_pull_data**
+ * to pull in once the non-linear parts, then retesting and
+ * eventually access the data.
+ *
+ * At the same time, this also makes sure the *skb* is uncloned,
+ * which is a necessary condition for direct write. As this needs
+ * to be an invariant for the write part only, the verifier
+ * detects writes and adds a prologue that is calling
+ * **bpf_skb_pull_data()** to effectively unclone the *skb* from
+ * the very beginning in case it is indeed cloned.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_pull_data)(struct __sk_buff *skb, __u32 len) = (void *) 39;
+
+/*
+ * bpf_csum_update
+ *
+ * Add the checksum *csum* into *skb*\ **->csum** in case the
+ * driver has supplied a checksum for the entire packet into that
+ * field. Return an error otherwise. This helper is intended to be
+ * used in combination with **bpf_csum_diff**\ (), in particular
+ * when the checksum needs to be updated after data has been
+ * written into the packet through direct packet access.
+ *
+ * Returns
+ * The checksum on success, or a negative error code in case of
+ * failure.
+ */
+static __s64 (*bpf_csum_update)(struct __sk_buff *skb, __wsum csum) = (void *) 40;
+
+/*
+ * bpf_set_hash_invalid
+ *
+ * Invalidate the current *skb*\ **->hash**. It can be used after
+ * mangling on headers through direct packet access, in order to
+ * indicate that the hash is outdated and to trigger a
+ * recalculation the next time the kernel tries to access this
+ * hash or when the **bpf_get_hash_recalc**\ () helper is called.
+ *
+ */
+static void (*bpf_set_hash_invalid)(struct __sk_buff *skb) = (void *) 41;
+
+/*
+ * bpf_get_numa_node_id
+ *
+ * Return the id of the current NUMA node. The primary use case
+ * for this helper is the selection of sockets for the local NUMA
+ * node, when the program is attached to sockets using the
+ * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**),
+ * but the helper is also available to other eBPF program types,
+ * similarly to **bpf_get_smp_processor_id**\ ().
+ *
+ * Returns
+ * The id of current NUMA node.
+ */
+static int (*bpf_get_numa_node_id)(void) = (void *) 42;
+
+/*
+ * bpf_skb_change_head
+ *
+ * Grows headroom of packet associated to *skb* and adjusts the
+ * offset of the MAC header accordingly, adding *len* bytes of
+ * space. It automatically extends and reallocates memory as
+ * required.
+ *
+ * This helper can be used on a layer 3 *skb* to push a MAC header
+ * for redirection into a layer 2 device.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_change_head)(struct __sk_buff *skb, __u32 len, __u64 flags) = (void *) 43;
+
+/*
+ * bpf_xdp_adjust_head
+ *
+ * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
+ * it is possible to use a negative value for *delta*. This helper
+ * can be used to prepare the packet for pushing or popping
+ * headers.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_xdp_adjust_head)(struct xdp_md *xdp_md, int delta) = (void *) 44;
+
+/*
+ * bpf_probe_read_str
+ *
+ * Copy a NUL terminated string from an unsafe kernel address
+ * *unsafe_ptr* to *dst*. See bpf_probe_read_kernel_str() for
+ * more details.
+ *
+ * Generally, use bpf_probe_read_user_str() or bpf_probe_read_kernel_str()
+ * instead.
+ *
+ * Returns
+ * On success, the strictly positive length of the string,
+ * including the trailing NUL character. On error, a negative
+ * value.
+ */
+static int (*bpf_probe_read_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 45;
+
+/*
+ * bpf_get_socket_cookie
+ *
+ * If the **struct sk_buff** pointed by *skb* has a known socket,
+ * retrieve the cookie (generated by the kernel) of this socket.
+ * If no cookie has been set yet, generate a new cookie. Once
+ * generated, the socket cookie remains stable for the life of the
+ * socket. This helper can be useful for monitoring per socket
+ * networking traffic statistics as it provides a global socket
+ * identifier that can be assumed unique.
+ *
+ * Returns
+ * A 8-byte long non-decreasing number on success, or 0 if the
+ * socket field is missing inside *skb*.
+ */
+static __u64 (*bpf_get_socket_cookie)(void *ctx) = (void *) 46;
+
+/*
+ * bpf_get_socket_uid
+ *
+ *
+ * Returns
+ * The owner UID of the socket associated to *skb*. If the socket
+ * is **NULL**, or if it is not a full socket (i.e. if it is a
+ * time-wait or a request socket instead), **overflowuid** value
+ * is returned (note that **overflowuid** might also be the actual
+ * UID value for the socket).
+ */
+static __u32 (*bpf_get_socket_uid)(struct __sk_buff *skb) = (void *) 47;
+
+/*
+ * bpf_set_hash
+ *
+ * Set the full hash for *skb* (set the field *skb*\ **->hash**)
+ * to value *hash*.
+ *
+ * Returns
+ * 0
+ */
+static __u32 (*bpf_set_hash)(struct __sk_buff *skb, __u32 hash) = (void *) 48;
+
+/*
+ * bpf_setsockopt
+ *
+ * Emulate a call to **setsockopt()** on the socket associated to
+ * *bpf_socket*, which must be a full socket. The *level* at
+ * which the option resides and the name *optname* of the option
+ * must be specified, see **setsockopt(2)** for more information.
+ * The option value of length *optlen* is pointed by *optval*.
+ *
+ * This helper actually implements a subset of **setsockopt()**.
+ * It supports the following *level*\ s:
+ *
+ * * **SOL_SOCKET**, which supports the following *optname*\ s:
+ * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**,
+ * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**.
+ * * **IPPROTO_TCP**, which supports the following *optname*\ s:
+ * **TCP_CONGESTION**, **TCP_BPF_IW**,
+ * **TCP_BPF_SNDCWND_CLAMP**.
+ * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
+ * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_setsockopt)(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 49;
+
+/*
+ * bpf_skb_adjust_room
+ *
+ * Grow or shrink the room for data in the packet associated to
+ * *skb* by *len_diff*, and according to the selected *mode*.
+ *
+ * There are two supported modes at this time:
+ *
+ * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer
+ * (room space is added or removed below the layer 2 header).
+ *
+ * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
+ * (room space is added or removed below the layer 3 header).
+ *
+ * The following flags are supported at this time:
+ *
+ * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size.
+ * Adjusting mss in this way is not allowed for datagrams.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**,
+ * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**:
+ * Any new space is reserved to hold a tunnel header.
+ * Configure skb offsets and other fields accordingly.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**,
+ * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**:
+ * Use with ENCAP_L3 flags to further specify the tunnel type.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*):
+ * Use with ENCAP_L3/L4 flags to further specify the tunnel
+ * type; *len* is the length of the inner MAC header.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_adjust_room)(struct __sk_buff *skb, __s32 len_diff, __u32 mode, __u64 flags) = (void *) 50;
+
+/*
+ * bpf_redirect_map
+ *
+ * Redirect the packet to the endpoint referenced by *map* at
+ * index *key*. Depending on its type, this *map* can contain
+ * references to net devices (for forwarding packets through other
+ * ports), or to CPUs (for redirecting XDP frames to another CPU;
+ * but this is only implemented for native XDP (with driver
+ * support) as of this writing).
+ *
+ * The lower two bits of *flags* are used as the return code if
+ * the map lookup fails. This is so that the return value can be
+ * one of the XDP program return codes up to XDP_TX, as chosen by
+ * the caller. Any higher bits in the *flags* argument must be
+ * unset.
+ *
+ * When used to redirect packets to net devices, this helper
+ * provides a high performance increase over **bpf_redirect**\ ().
+ * This is due to various implementation details of the underlying
+ * mechanisms, one of which is the fact that **bpf_redirect_map**\
+ * () tries to send packet as a "bulk" to the device.
+ *
+ * Returns
+ * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error.
+ */
+static int (*bpf_redirect_map)(void *map, __u32 key, __u64 flags) = (void *) 51;
+
+/*
+ * bpf_sk_redirect_map
+ *
+ * Redirect the packet to the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ *
+ * Returns
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ */
+static int (*bpf_sk_redirect_map)(struct __sk_buff *skb, void *map, __u32 key, __u64 flags) = (void *) 52;
+
+/*
+ * bpf_sock_map_update
+ *
+ * Add an entry to, or update a *map* referencing sockets. The
+ * *skops* is used as a new value for the entry associated to
+ * *key*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * If the *map* has eBPF programs (parser and verdict), those will
+ * be inherited by the socket being added. If the socket is
+ * already attached to eBPF programs, this results in an error.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_sock_map_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 53;
+
+/*
+ * bpf_xdp_adjust_meta
+ *
+ * Adjust the address pointed by *xdp_md*\ **->data_meta** by
+ * *delta* (which can be positive or negative). Note that this
+ * operation modifies the address stored in *xdp_md*\ **->data**,
+ * so the latter must be loaded only after the helper has been
+ * called.
+ *
+ * The use of *xdp_md*\ **->data_meta** is optional and programs
+ * are not required to use it. The rationale is that when the
+ * packet is processed with XDP (e.g. as DoS filter), it is
+ * possible to push further meta data along with it before passing
+ * to the stack, and to give the guarantee that an ingress eBPF
+ * program attached as a TC classifier on the same device can pick
+ * this up for further post-processing. Since TC works with socket
+ * buffers, it remains possible to set from XDP the **mark** or
+ * **priority** pointers, or other pointers for the socket buffer.
+ * Having this scratch space generic and programmable allows for
+ * more flexibility as the user is free to store whatever meta
+ * data they need.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_xdp_adjust_meta)(struct xdp_md *xdp_md, int delta) = (void *) 54;
+
+/*
+ * bpf_perf_event_read_value
+ *
+ * Read the value of a perf event counter, and store it into *buf*
+ * of size *buf_size*. This helper relies on a *map* of type
+ * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event
+ * counter is selected when *map* is updated with perf event file
+ * descriptors. The *map* is an array whose size is the number of
+ * available CPUs, and each cell contains a value relative to one
+ * CPU. The value to retrieve is indicated by *flags*, that
+ * contains the index of the CPU to look up, masked with
+ * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
+ * **BPF_F_CURRENT_CPU** to indicate that the value for the
+ * current CPU should be retrieved.
+ *
+ * This helper behaves in a way close to
+ * **bpf_perf_event_read**\ () helper, save that instead of
+ * just returning the value observed, it fills the *buf*
+ * structure. This allows for additional data to be retrieved: in
+ * particular, the enabled and running times (in *buf*\
+ * **->enabled** and *buf*\ **->running**, respectively) are
+ * copied. In general, **bpf_perf_event_read_value**\ () is
+ * recommended over **bpf_perf_event_read**\ (), which has some
+ * ABI issues and provides fewer functionalities.
+ *
+ * These values are interesting, because hardware PMU (Performance
+ * Monitoring Unit) counters are limited resources. When there are
+ * more PMU based perf events opened than available counters,
+ * kernel will multiplex these events so each event gets certain
+ * percentage (but not all) of the PMU time. In case that
+ * multiplexing happens, the number of samples or counter value
+ * will not reflect the case compared to when no multiplexing
+ * occurs. This makes comparison between different runs difficult.
+ * Typically, the counter value should be normalized before
+ * comparing to other experiments. The usual normalization is done
+ * as follows.
+ *
+ * ::
+ *
+ * normalized_counter = counter * t_enabled / t_running
+ *
+ * Where t_enabled is the time enabled for event and t_running is
+ * the time running for event since last normalization. The
+ * enabled and running times are accumulated since the perf event
+ * open. To achieve scaling factor between two invocations of an
+ * eBPF program, users can can use CPU id as the key (which is
+ * typical for perf array usage model) to remember the previous
+ * value and do the calculation inside the eBPF program.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_perf_event_read_value)(void *map, __u64 flags, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 55;
+
+/*
+ * bpf_perf_prog_read_value
+ *
+ * For en eBPF program attached to a perf event, retrieve the
+ * value of the event counter associated to *ctx* and store it in
+ * the structure pointed by *buf* and of size *buf_size*. Enabled
+ * and running times are also stored in the structure (see
+ * description of helper **bpf_perf_event_read_value**\ () for
+ * more details).
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_perf_prog_read_value)(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, __u32 buf_size) = (void *) 56;
+
+/*
+ * bpf_getsockopt
+ *
+ * Emulate a call to **getsockopt()** on the socket associated to
+ * *bpf_socket*, which must be a full socket. The *level* at
+ * which the option resides and the name *optname* of the option
+ * must be specified, see **getsockopt(2)** for more information.
+ * The retrieved value is stored in the structure pointed by
+ * *opval* and of length *optlen*.
+ *
+ * This helper actually implements a subset of **getsockopt()**.
+ * It supports the following *level*\ s:
+ *
+ * * **IPPROTO_TCP**, which supports *optname*
+ * **TCP_CONGESTION**.
+ * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
+ * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_getsockopt)(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen) = (void *) 57;
+
+/*
+ * bpf_override_return
+ *
+ * Used for error injection, this helper uses kprobes to override
+ * the return value of the probed function, and to set it to *rc*.
+ * The first argument is the context *regs* on which the kprobe
+ * works.
+ *
+ * This helper works by setting setting the PC (program counter)
+ * to an override function which is run in place of the original
+ * probed function. This means the probed function is not run at
+ * all. The replacement function just returns with the required
+ * value.
+ *
+ * This helper has security implications, and thus is subject to
+ * restrictions. It is only available if the kernel was compiled
+ * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration
+ * option, and in this case it only works on functions tagged with
+ * **ALLOW_ERROR_INJECTION** in the kernel code.
+ *
+ * Also, the helper is only available for the architectures having
+ * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing,
+ * x86 architecture is the only one to support this feature.
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_override_return)(struct pt_regs *regs, __u64 rc) = (void *) 58;
+
+/*
+ * bpf_sock_ops_cb_flags_set
+ *
+ * Attempt to set the value of the **bpf_sock_ops_cb_flags** field
+ * for the full TCP socket associated to *bpf_sock_ops* to
+ * *argval*.
+ *
+ * The primary use of this field is to determine if there should
+ * be calls to eBPF programs of type
+ * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP
+ * code. A program of the same type can change its value, per
+ * connection and as necessary, when the connection is
+ * established. This field is directly accessible for reading, but
+ * this helper must be used for updates in order to return an
+ * error if an eBPF program tries to set a callback that is not
+ * supported in the current kernel.
+ *
+ * *argval* is a flag array which can combine these flags:
+ *
+ * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)
+ * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)
+ * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)
+ * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT)
+ *
+ * Therefore, this function can be used to clear a callback flag by
+ * setting the appropriate bit to zero. e.g. to disable the RTO
+ * callback:
+ *
+ * **bpf_sock_ops_cb_flags_set(bpf_sock,**
+ * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)**
+ *
+ * Here are some examples of where one could call such eBPF
+ * program:
+ *
+ * * When RTO fires.
+ * * When a packet is retransmitted.
+ * * When the connection terminates.
+ * * When a packet is sent.
+ * * When a packet is received.
+ *
+ * Returns
+ * Code **-EINVAL** if the socket is not a full TCP socket;
+ * otherwise, a positive number containing the bits that could not
+ * be set is returned (which comes down to 0 if all bits were set
+ * as required).
+ */
+static int (*bpf_sock_ops_cb_flags_set)(struct bpf_sock_ops *bpf_sock, int argval) = (void *) 59;
+
+/*
+ * bpf_msg_redirect_map
+ *
+ * This helper is used in programs implementing policies at the
+ * socket level. If the message *msg* is allowed to pass (i.e. if
+ * the verdict eBPF program returns **SK_PASS**), redirect it to
+ * the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ *
+ * Returns
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ */
+static int (*bpf_msg_redirect_map)(struct sk_msg_md *msg, void *map, __u32 key, __u64 flags) = (void *) 60;
+
+/*
+ * bpf_msg_apply_bytes
+ *
+ * For socket policies, apply the verdict of the eBPF program to
+ * the next *bytes* (number of bytes) of message *msg*.
+ *
+ * For example, this helper can be used in the following cases:
+ *
+ * * A single **sendmsg**\ () or **sendfile**\ () system call
+ * contains multiple logical messages that the eBPF program is
+ * supposed to read and for which it should apply a verdict.
+ * * An eBPF program only cares to read the first *bytes* of a
+ * *msg*. If the message has a large payload, then setting up
+ * and calling the eBPF program repeatedly for all bytes, even
+ * though the verdict is already known, would create unnecessary
+ * overhead.
+ *
+ * When called from within an eBPF program, the helper sets a
+ * counter internal to the BPF infrastructure, that is used to
+ * apply the last verdict to the next *bytes*. If *bytes* is
+ * smaller than the current data being processed from a
+ * **sendmsg**\ () or **sendfile**\ () system call, the first
+ * *bytes* will be sent and the eBPF program will be re-run with
+ * the pointer for start of data pointing to byte number *bytes*
+ * **+ 1**. If *bytes* is larger than the current data being
+ * processed, then the eBPF verdict will be applied to multiple
+ * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are
+ * consumed.
+ *
+ * Note that if a socket closes with the internal counter holding
+ * a non-zero value, this is not a problem because data is not
+ * being buffered for *bytes* and is sent as it is received.
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_msg_apply_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 61;
+
+/*
+ * bpf_msg_cork_bytes
+ *
+ * For socket policies, prevent the execution of the verdict eBPF
+ * program for message *msg* until *bytes* (byte number) have been
+ * accumulated.
+ *
+ * This can be used when one needs a specific number of bytes
+ * before a verdict can be assigned, even if the data spans
+ * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme
+ * case would be a user calling **sendmsg**\ () repeatedly with
+ * 1-byte long message segments. Obviously, this is bad for
+ * performance, but it is still valid. If the eBPF program needs
+ * *bytes* bytes to validate a header, this helper can be used to
+ * prevent the eBPF program to be called again until *bytes* have
+ * been accumulated.
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_msg_cork_bytes)(struct sk_msg_md *msg, __u32 bytes) = (void *) 62;
+
+/*
+ * bpf_msg_pull_data
+ *
+ * For socket policies, pull in non-linear data from user space
+ * for *msg* and set pointers *msg*\ **->data** and *msg*\
+ * **->data_end** to *start* and *end* bytes offsets into *msg*,
+ * respectively.
+ *
+ * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
+ * *msg* it can only parse data that the (**data**, **data_end**)
+ * pointers have already consumed. For **sendmsg**\ () hooks this
+ * is likely the first scatterlist element. But for calls relying
+ * on the **sendpage** handler (e.g. **sendfile**\ ()) this will
+ * be the range (**0**, **0**) because the data is shared with
+ * user space and by default the objective is to avoid allowing
+ * user space to modify data while (or after) eBPF verdict is
+ * being decided. This helper can be used to pull in data and to
+ * set the start and end pointer to given values. Data will be
+ * copied if necessary (i.e. if data was not linear and if start
+ * and end pointers do not point to the same chunk).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_msg_pull_data)(struct sk_msg_md *msg, __u32 start, __u32 end, __u64 flags) = (void *) 63;
+
+/*
+ * bpf_bind
+ *
+ * Bind the socket associated to *ctx* to the address pointed by
+ * *addr*, of length *addr_len*. This allows for making outgoing
+ * connection from the desired IP address, which can be useful for
+ * example when all processes inside a cgroup should use one
+ * single IP address on a host that has multiple IP configured.
+ *
+ * This helper works for IPv4 and IPv6, TCP and UDP sockets. The
+ * domain (*addr*\ **->sa_family**) must be **AF_INET** (or
+ * **AF_INET6**). Looking for a free port to bind to can be
+ * expensive, therefore binding to port is not permitted by the
+ * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively)
+ * must be set to zero.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_bind)(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len) = (void *) 64;
+
+/*
+ * bpf_xdp_adjust_tail
+ *
+ * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is
+ * only possible to shrink the packet as of this writing,
+ * therefore *delta* must be a negative integer.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_xdp_adjust_tail)(struct xdp_md *xdp_md, int delta) = (void *) 65;
+
+/*
+ * bpf_skb_get_xfrm_state
+ *
+ * Retrieve the XFRM state (IP transform framework, see also
+ * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*.
+ *
+ * The retrieved value is stored in the **struct bpf_xfrm_state**
+ * pointed by *xfrm_state* and of length *size*.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_XFRM** configuration option.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_get_xfrm_state)(struct __sk_buff *skb, __u32 index, struct bpf_xfrm_state *xfrm_state, __u32 size, __u64 flags) = (void *) 66;
+
+/*
+ * bpf_get_stack
+ *
+ * Return a user or a kernel stack in bpf program provided buffer.
+ * To achieve this, the helper needs *ctx*, which is a pointer
+ * to the context on which the tracing program is executed.
+ * To store the stacktrace, the bpf program provides *buf* with
+ * a nonnegative *size*.
+ *
+ * The last argument, *flags*, holds the number of stack frames to
+ * skip (from 0 to 255), masked with
+ * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
+ * the following flags:
+ *
+ * **BPF_F_USER_STACK**
+ * Collect a user space stack instead of a kernel stack.
+ * **BPF_F_USER_BUILD_ID**
+ * Collect buildid+offset instead of ips for user stack,
+ * only valid if **BPF_F_USER_STACK** is also specified.
+ *
+ * **bpf_get_stack**\ () can collect up to
+ * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject
+ * to sufficient large buffer size. Note that
+ * this limit can be controlled with the **sysctl** program, and
+ * that it should be manually increased in order to profile long
+ * user stacks (such as stacks for Java programs). To do so, use:
+ *
+ * ::
+ *
+ * # sysctl kernel.perf_event_max_stack=<new value>
+ *
+ * Returns
+ * A non-negative value equal to or less than *size* on success,
+ * or a negative error in case of failure.
+ */
+static int (*bpf_get_stack)(void *ctx, void *buf, __u32 size, __u64 flags) = (void *) 67;
+
+/*
+ * bpf_skb_load_bytes_relative
+ *
+ * This helper is similar to **bpf_skb_load_bytes**\ () in that
+ * it provides an easy way to load *len* bytes from *offset*
+ * from the packet associated to *skb*, into the buffer pointed
+ * by *to*. The difference to **bpf_skb_load_bytes**\ () is that
+ * a fifth argument *start_header* exists in order to select a
+ * base offset to start from. *start_header* can be one of:
+ *
+ * **BPF_HDR_START_MAC**
+ * Base offset to load data from is *skb*'s mac header.
+ * **BPF_HDR_START_NET**
+ * Base offset to load data from is *skb*'s network header.
+ *
+ * In general, "direct packet access" is the preferred method to
+ * access packet data, however, this helper is in particular useful
+ * in socket filters where *skb*\ **->data** does not always point
+ * to the start of the mac header and where "direct packet access"
+ * is not available.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *to, __u32 len, __u32 start_header) = (void *) 68;
+
+/*
+ * bpf_fib_lookup
+ *
+ * Do FIB lookup in kernel tables using parameters in *params*.
+ * If lookup is successful and result shows packet is to be
+ * forwarded, the neighbor tables are searched for the nexthop.
+ * If successful (ie., FIB lookup shows forwarding and nexthop
+ * is resolved), the nexthop address is returned in ipv4_dst
+ * or ipv6_dst based on family, smac is set to mac address of
+ * egress device, dmac is set to nexthop mac address, rt_metric
+ * is set to metric from route (IPv4/IPv6 only), and ifindex
+ * is set to the device index of the nexthop from the FIB lookup.
+ *
+ * *plen* argument is the size of the passed in struct.
+ * *flags* argument can be a combination of one or more of the
+ * following values:
+ *
+ * **BPF_FIB_LOOKUP_DIRECT**
+ * Do a direct table lookup vs full lookup using FIB
+ * rules.
+ * **BPF_FIB_LOOKUP_OUTPUT**
+ * Perform lookup from an egress perspective (default is
+ * ingress).
+ *
+ * *ctx* is either **struct xdp_md** for XDP programs or
+ * **struct sk_buff** tc cls_act programs.
+ *
+ * Returns
+ * * < 0 if any input argument is invalid
+ * * 0 on success (packet is forwarded, nexthop neighbor exists)
+ * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
+ * packet is not forwarded or needs assist from full stack
+ */
+static int (*bpf_fib_lookup)(void *ctx, struct bpf_fib_lookup *params, int plen, __u32 flags) = (void *) 69;
+
+/*
+ * bpf_sock_hash_update
+ *
+ * Add an entry to, or update a sockhash *map* referencing sockets.
+ * The *skops* is used as a new value for the entry associated to
+ * *key*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * If the *map* has eBPF programs (parser and verdict), those will
+ * be inherited by the socket being added. If the socket is
+ * already attached to eBPF programs, this results in an error.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_sock_hash_update)(struct bpf_sock_ops *skops, void *map, void *key, __u64 flags) = (void *) 70;
+
+/*
+ * bpf_msg_redirect_hash
+ *
+ * This helper is used in programs implementing policies at the
+ * socket level. If the message *msg* is allowed to pass (i.e. if
+ * the verdict eBPF program returns **SK_PASS**), redirect it to
+ * the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ *
+ * Returns
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ */
+static int (*bpf_msg_redirect_hash)(struct sk_msg_md *msg, void *map, void *key, __u64 flags) = (void *) 71;
+
+/*
+ * bpf_sk_redirect_hash
+ *
+ * This helper is used in programs implementing policies at the
+ * skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
+ * if the verdeict eBPF program returns **SK_PASS**), redirect it
+ * to the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress otherwise). This is the only flag supported for now.
+ *
+ * Returns
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ */
+static int (*bpf_sk_redirect_hash)(struct __sk_buff *skb, void *map, void *key, __u64 flags) = (void *) 72;
+
+/*
+ * bpf_lwt_push_encap
+ *
+ * Encapsulate the packet associated to *skb* within a Layer 3
+ * protocol header. This header is provided in the buffer at
+ * address *hdr*, with *len* its size in bytes. *type* indicates
+ * the protocol of the header and can be one of:
+ *
+ * **BPF_LWT_ENCAP_SEG6**
+ * IPv6 encapsulation with Segment Routing Header
+ * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
+ * the IPv6 header is computed by the kernel.
+ * **BPF_LWT_ENCAP_SEG6_INLINE**
+ * Only works if *skb* contains an IPv6 packet. Insert a
+ * Segment Routing Header (**struct ipv6_sr_hdr**) inside
+ * the IPv6 header.
+ * **BPF_LWT_ENCAP_IP**
+ * IP encapsulation (GRE/GUE/IPIP/etc). The outer header
+ * must be IPv4 or IPv6, followed by zero or more
+ * additional headers, up to **LWT_BPF_MAX_HEADROOM**
+ * total bytes in all prepended headers. Please note that
+ * if **skb_is_gso**\ (*skb*) is true, no more than two
+ * headers can be prepended, and the inner header, if
+ * present, should be either GRE or UDP/GUE.
+ *
+ * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs
+ * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can
+ * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and
+ * **BPF_PROG_TYPE_LWT_XMIT**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_lwt_push_encap)(struct __sk_buff *skb, __u32 type, void *hdr, __u32 len) = (void *) 73;
+
+/*
+ * bpf_lwt_seg6_store_bytes
+ *
+ * Store *len* bytes from address *from* into the packet
+ * associated to *skb*, at *offset*. Only the flags, tag and TLVs
+ * inside the outermost IPv6 Segment Routing Header can be
+ * modified through this helper.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_lwt_seg6_store_bytes)(struct __sk_buff *skb, __u32 offset, const void *from, __u32 len) = (void *) 74;
+
+/*
+ * bpf_lwt_seg6_adjust_srh
+ *
+ * Adjust the size allocated to TLVs in the outermost IPv6
+ * Segment Routing Header contained in the packet associated to
+ * *skb*, at position *offset* by *delta* bytes. Only offsets
+ * after the segments are accepted. *delta* can be as well
+ * positive (growing) as negative (shrinking).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_lwt_seg6_adjust_srh)(struct __sk_buff *skb, __u32 offset, __s32 delta) = (void *) 75;
+
+/*
+ * bpf_lwt_seg6_action
+ *
+ * Apply an IPv6 Segment Routing action of type *action* to the
+ * packet associated to *skb*. Each action takes a parameter
+ * contained at address *param*, and of length *param_len* bytes.
+ * *action* can be one of:
+ *
+ * **SEG6_LOCAL_ACTION_END_X**
+ * End.X action: Endpoint with Layer-3 cross-connect.
+ * Type of *param*: **struct in6_addr**.
+ * **SEG6_LOCAL_ACTION_END_T**
+ * End.T action: Endpoint with specific IPv6 table lookup.
+ * Type of *param*: **int**.
+ * **SEG6_LOCAL_ACTION_END_B6**
+ * End.B6 action: Endpoint bound to an SRv6 policy.
+ * Type of *param*: **struct ipv6_sr_hdr**.
+ * **SEG6_LOCAL_ACTION_END_B6_ENCAP**
+ * End.B6.Encap action: Endpoint bound to an SRv6
+ * encapsulation policy.
+ * Type of *param*: **struct ipv6_sr_hdr**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_lwt_seg6_action)(struct __sk_buff *skb, __u32 action, void *param, __u32 param_len) = (void *) 76;
+
+/*
+ * bpf_rc_repeat
+ *
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded repeat key message. This delays
+ * the generation of a key up event for previously generated
+ * key down event.
+ *
+ * Some IR protocols like NEC have a special IR message for
+ * repeating last button, for when a button is held down.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_rc_repeat)(void *ctx) = (void *) 77;
+
+/*
+ * bpf_rc_keydown
+ *
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded key press with *scancode*,
+ * *toggle* value in the given *protocol*. The scancode will be
+ * translated to a keycode using the rc keymap, and reported as
+ * an input key down event. After a period a key up event is
+ * generated. This period can be extended by calling either
+ * **bpf_rc_keydown**\ () again with the same values, or calling
+ * **bpf_rc_repeat**\ ().
+ *
+ * Some protocols include a toggle bit, in case the button was
+ * released and pressed again between consecutive scancodes.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * The *protocol* is the decoded protocol number (see
+ * **enum rc_proto** for some predefined values).
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_rc_keydown)(void *ctx, __u32 protocol, __u64 scancode, __u32 toggle) = (void *) 78;
+
+/*
+ * bpf_skb_cgroup_id
+ *
+ * Return the cgroup v2 id of the socket associated with the *skb*.
+ * This is roughly similar to the **bpf_get_cgroup_classid**\ ()
+ * helper for cgroup v1 by providing a tag resp. identifier that
+ * can be matched on or used for map lookups e.g. to implement
+ * policy. The cgroup v2 id of a given path in the hierarchy is
+ * exposed in user space through the f_handle API in order to get
+ * to the same 64-bit id.
+ *
+ * This helper can be used on TC egress path, but not on ingress,
+ * and is available only if the kernel was compiled with the
+ * **CONFIG_SOCK_CGROUP_DATA** configuration option.
+ *
+ * Returns
+ * The id is returned or 0 in case the id could not be retrieved.
+ */
+static __u64 (*bpf_skb_cgroup_id)(struct __sk_buff *skb) = (void *) 79;
+
+/*
+ * bpf_get_current_cgroup_id
+ *
+ *
+ * Returns
+ * A 64-bit integer containing the current cgroup id based
+ * on the cgroup within which the current task is running.
+ */
+static __u64 (*bpf_get_current_cgroup_id)(void) = (void *) 80;
+
+/*
+ * bpf_get_local_storage
+ *
+ * Get the pointer to the local storage area.
+ * The type and the size of the local storage is defined
+ * by the *map* argument.
+ * The *flags* meaning is specific for each map type,
+ * and has to be 0 for cgroup local storage.
+ *
+ * Depending on the BPF program type, a local storage area
+ * can be shared between multiple instances of the BPF program,
+ * running simultaneously.
+ *
+ * A user should care about the synchronization by himself.
+ * For example, by using the **BPF_STX_XADD** instruction to alter
+ * the shared data.
+ *
+ * Returns
+ * A pointer to the local storage area.
+ */
+static void *(*bpf_get_local_storage)(void *map, __u64 flags) = (void *) 81;
+
+/*
+ * bpf_sk_select_reuseport
+ *
+ * Select a **SO_REUSEPORT** socket from a
+ * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
+ * It checks the selected socket is matching the incoming
+ * request in the socket buffer.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_sk_select_reuseport)(struct sk_reuseport_md *reuse, void *map, void *key, __u64 flags) = (void *) 82;
+
+/*
+ * bpf_skb_ancestor_cgroup_id
+ *
+ * Return id of cgroup v2 that is ancestor of cgroup associated
+ * with the *skb* at the *ancestor_level*. The root cgroup is at
+ * *ancestor_level* zero and each step down the hierarchy
+ * increments the level. If *ancestor_level* == level of cgroup
+ * associated with *skb*, then return value will be same as that
+ * of **bpf_skb_cgroup_id**\ ().
+ *
+ * The helper is useful to implement policies based on cgroups
+ * that are upper in hierarchy than immediate cgroup associated
+ * with *skb*.
+ *
+ * The format of returned id and helper limitations are same as in
+ * **bpf_skb_cgroup_id**\ ().
+ *
+ * Returns
+ * The id is returned or 0 in case the id could not be retrieved.
+ */
+static __u64 (*bpf_skb_ancestor_cgroup_id)(struct __sk_buff *skb, int ancestor_level) = (void *) 83;
+
+/*
+ * bpf_sk_lookup_tcp
+ *
+ * Look for TCP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * The *ctx* should point to the context of the program, such as
+ * the skb or socket (depending on the hook in use). This is used
+ * to determine the base network namespace for the lookup.
+ *
+ * *tuple_size* must be one of:
+ *
+ * **sizeof**\ (*tuple*\ **->ipv4**)
+ * Look for an IPv4 socket.
+ * **sizeof**\ (*tuple*\ **->ipv6**)
+ * Look for an IPv6 socket.
+ *
+ * If the *netns* is a negative signed 32-bit integer, then the
+ * socket lookup table in the netns associated with the *ctx* will
+ * will be used. For the TC hooks, this is the netns of the device
+ * in the skb. For socket hooks, this is the netns of the socket.
+ * If *netns* is any other signed 32-bit value greater than or
+ * equal to zero then it specifies the ID of the netns relative to
+ * the netns associated with the *ctx*. *netns* values beyond the
+ * range of 32-bit integers are reserved for future use.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ *
+ * Returns
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ */
+static struct bpf_sock *(*bpf_sk_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 84;
+
+/*
+ * bpf_sk_lookup_udp
+ *
+ * Look for UDP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * The *ctx* should point to the context of the program, such as
+ * the skb or socket (depending on the hook in use). This is used
+ * to determine the base network namespace for the lookup.
+ *
+ * *tuple_size* must be one of:
+ *
+ * **sizeof**\ (*tuple*\ **->ipv4**)
+ * Look for an IPv4 socket.
+ * **sizeof**\ (*tuple*\ **->ipv6**)
+ * Look for an IPv6 socket.
+ *
+ * If the *netns* is a negative signed 32-bit integer, then the
+ * socket lookup table in the netns associated with the *ctx* will
+ * will be used. For the TC hooks, this is the netns of the device
+ * in the skb. For socket hooks, this is the netns of the socket.
+ * If *netns* is any other signed 32-bit value greater than or
+ * equal to zero then it specifies the ID of the netns relative to
+ * the netns associated with the *ctx*. *netns* values beyond the
+ * range of 32-bit integers are reserved for future use.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ *
+ * Returns
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ */
+static struct bpf_sock *(*bpf_sk_lookup_udp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 85;
+
+/*
+ * bpf_sk_release
+ *
+ * Release the reference held by *sock*. *sock* must be a
+ * non-**NULL** pointer that was returned from
+ * **bpf_sk_lookup_xxx**\ ().
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_sk_release)(struct bpf_sock *sock) = (void *) 86;
+
+/*
+ * bpf_map_push_elem
+ *
+ * Push an element *value* in *map*. *flags* is one of:
+ *
+ * **BPF_EXIST**
+ * If the queue/stack is full, the oldest element is
+ * removed to make room for this.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_map_push_elem)(void *map, const void *value, __u64 flags) = (void *) 87;
+
+/*
+ * bpf_map_pop_elem
+ *
+ * Pop an element from *map*.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_map_pop_elem)(void *map, void *value) = (void *) 88;
+
+/*
+ * bpf_map_peek_elem
+ *
+ * Get an element from *map* without removing it.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_map_peek_elem)(void *map, void *value) = (void *) 89;
+
+/*
+ * bpf_msg_push_data
+ *
+ * For socket policies, insert *len* bytes into *msg* at offset
+ * *start*.
+ *
+ * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
+ * *msg* it may want to insert metadata or options into the *msg*.
+ * This can later be read and used by any of the lower layer BPF
+ * hooks.
+ *
+ * This helper may fail if under memory pressure (a malloc
+ * fails) in these cases BPF programs will get an appropriate
+ * error and BPF programs will need to handle them.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_msg_push_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 90;
+
+/*
+ * bpf_msg_pop_data
+ *
+ * Will remove *len* bytes from a *msg* starting at byte *start*.
+ * This may result in **ENOMEM** errors under certain situations if
+ * an allocation and copy are required due to a full ring buffer.
+ * However, the helper will try to avoid doing the allocation
+ * if possible. Other errors can occur if input parameters are
+ * invalid either due to *start* byte not being valid part of *msg*
+ * payload and/or *pop* value being to large.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_msg_pop_data)(struct sk_msg_md *msg, __u32 start, __u32 len, __u64 flags) = (void *) 91;
+
+/*
+ * bpf_rc_pointer_rel
+ *
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded pointer movement.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_rc_pointer_rel)(void *ctx, __s32 rel_x, __s32 rel_y) = (void *) 92;
+
+/*
+ * bpf_spin_lock
+ *
+ * Acquire a spinlock represented by the pointer *lock*, which is
+ * stored as part of a value of a map. Taking the lock allows to
+ * safely update the rest of the fields in that value. The
+ * spinlock can (and must) later be released with a call to
+ * **bpf_spin_unlock**\ (\ *lock*\ ).
+ *
+ * Spinlocks in BPF programs come with a number of restrictions
+ * and constraints:
+ *
+ * * **bpf_spin_lock** objects are only allowed inside maps of
+ * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this
+ * list could be extended in the future).
+ * * BTF description of the map is mandatory.
+ * * The BPF program can take ONE lock at a time, since taking two
+ * or more could cause dead locks.
+ * * Only one **struct bpf_spin_lock** is allowed per map element.
+ * * When the lock is taken, calls (either BPF to BPF or helpers)
+ * are not allowed.
+ * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not
+ * allowed inside a spinlock-ed region.
+ * * The BPF program MUST call **bpf_spin_unlock**\ () to release
+ * the lock, on all execution paths, before it returns.
+ * * The BPF program can access **struct bpf_spin_lock** only via
+ * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ ()
+ * helpers. Loading or storing data into the **struct
+ * bpf_spin_lock** *lock*\ **;** field of a map is not allowed.
+ * * To use the **bpf_spin_lock**\ () helper, the BTF description
+ * of the map value must be a struct and have **struct
+ * bpf_spin_lock** *anyname*\ **;** field at the top level.
+ * Nested lock inside another struct is not allowed.
+ * * The **struct bpf_spin_lock** *lock* field in a map value must
+ * be aligned on a multiple of 4 bytes in that value.
+ * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy
+ * the **bpf_spin_lock** field to user space.
+ * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from
+ * a BPF program, do not update the **bpf_spin_lock** field.
+ * * **bpf_spin_lock** cannot be on the stack or inside a
+ * networking packet (it can only be inside of a map values).
+ * * **bpf_spin_lock** is available to root only.
+ * * Tracing programs and socket filter programs cannot use
+ * **bpf_spin_lock**\ () due to insufficient preemption checks
+ * (but this may change in the future).
+ * * **bpf_spin_lock** is not allowed in inner maps of map-in-map.
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_spin_lock)(struct bpf_spin_lock *lock) = (void *) 93;
+
+/*
+ * bpf_spin_unlock
+ *
+ * Release the *lock* previously locked by a call to
+ * **bpf_spin_lock**\ (\ *lock*\ ).
+ *
+ * Returns
+ * 0
+ */
+static int (*bpf_spin_unlock)(struct bpf_spin_lock *lock) = (void *) 94;
+
+/*
+ * bpf_sk_fullsock
+ *
+ * This helper gets a **struct bpf_sock** pointer such
+ * that all the fields in this **bpf_sock** can be accessed.
+ *
+ * Returns
+ * A **struct bpf_sock** pointer on success, or **NULL** in
+ * case of failure.
+ */
+static struct bpf_sock *(*bpf_sk_fullsock)(struct bpf_sock *sk) = (void *) 95;
+
+/*
+ * bpf_tcp_sock
+ *
+ * This helper gets a **struct bpf_tcp_sock** pointer from a
+ * **struct bpf_sock** pointer.
+ *
+ * Returns
+ * A **struct bpf_tcp_sock** pointer on success, or **NULL** in
+ * case of failure.
+ */
+static struct bpf_tcp_sock *(*bpf_tcp_sock)(struct bpf_sock *sk) = (void *) 96;
+
+/*
+ * bpf_skb_ecn_set_ce
+ *
+ * Set ECN (Explicit Congestion Notification) field of IP header
+ * to **CE** (Congestion Encountered) if current value is **ECT**
+ * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6
+ * and IPv4.
+ *
+ * Returns
+ * 1 if the **CE** flag is set (either by the current helper call
+ * or because it was already present), 0 if it is not set.
+ */
+static int (*bpf_skb_ecn_set_ce)(struct __sk_buff *skb) = (void *) 97;
+
+/*
+ * bpf_get_listener_sock
+ *
+ * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state.
+ * **bpf_sk_release**\ () is unnecessary and not allowed.
+ *
+ * Returns
+ * A **struct bpf_sock** pointer on success, or **NULL** in
+ * case of failure.
+ */
+static struct bpf_sock *(*bpf_get_listener_sock)(struct bpf_sock *sk) = (void *) 98;
+
+/*
+ * bpf_skc_lookup_tcp
+ *
+ * Look for TCP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * This function is identical to **bpf_sk_lookup_tcp**\ (), except
+ * that it also returns timewait or request sockets. Use
+ * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the
+ * full structure.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ *
+ * Returns
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ */
+static struct bpf_sock *(*bpf_skc_lookup_tcp)(void *ctx, struct bpf_sock_tuple *tuple, __u32 tuple_size, __u64 netns, __u64 flags) = (void *) 99;
+
+/*
+ * bpf_tcp_check_syncookie
+ *
+ * Check whether *iph* and *th* contain a valid SYN cookie ACK for
+ * the listening socket in *sk*.
+ *
+ * *iph* points to the start of the IPv4 or IPv6 header, while
+ * *iph_len* contains **sizeof**\ (**struct iphdr**) or
+ * **sizeof**\ (**struct ip6hdr**).
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains **sizeof**\ (**struct tcphdr**).
+ *
+ *
+ * Returns
+ * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative
+ * error otherwise.
+ */
+static int (*bpf_tcp_check_syncookie)(struct bpf_sock *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 100;
+
+/*
+ * bpf_sysctl_get_name
+ *
+ * Get name of sysctl in /proc/sys/ and copy it into provided by
+ * program buffer *buf* of size *buf_len*.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ *
+ * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is
+ * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name
+ * only (e.g. "tcp_mem").
+ *
+ * Returns
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ */
+static int (*bpf_sysctl_get_name)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len, __u64 flags) = (void *) 101;
+
+/*
+ * bpf_sysctl_get_current_value
+ *
+ * Get current value of sysctl as it is presented in /proc/sys
+ * (incl. newline, etc), and copy it as a string into provided
+ * by program buffer *buf* of size *buf_len*.
+ *
+ * The whole value is copied, no matter what file position user
+ * space issued e.g. sys_read at.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ *
+ * Returns
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ *
+ * **-EINVAL** if current value was unavailable, e.g. because
+ * sysctl is uninitialized and read returns -EIO for it.
+ */
+static int (*bpf_sysctl_get_current_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 102;
+
+/*
+ * bpf_sysctl_get_new_value
+ *
+ * Get new value being written by user space to sysctl (before
+ * the actual write happens) and copy it as a string into
+ * provided by program buffer *buf* of size *buf_len*.
+ *
+ * User space may write new value at file position > 0.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ *
+ * Returns
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ *
+ * **-EINVAL** if sysctl is being read.
+ */
+static int (*bpf_sysctl_get_new_value)(struct bpf_sysctl *ctx, char *buf, unsigned long buf_len) = (void *) 103;
+
+/*
+ * bpf_sysctl_set_new_value
+ *
+ * Override new value being written by user space to sysctl with
+ * value provided by program in buffer *buf* of size *buf_len*.
+ *
+ * *buf* should contain a string in same form as provided by user
+ * space on sysctl write.
+ *
+ * User space may write new value at file position > 0. To override
+ * the whole sysctl value file position should be set to zero.
+ *
+ * Returns
+ * 0 on success.
+ *
+ * **-E2BIG** if the *buf_len* is too big.
+ *
+ * **-EINVAL** if sysctl is being read.
+ */
+static int (*bpf_sysctl_set_new_value)(struct bpf_sysctl *ctx, const char *buf, unsigned long buf_len) = (void *) 104;
+
+/*
+ * bpf_strtol
+ *
+ * Convert the initial part of the string from buffer *buf* of
+ * size *buf_len* to a long integer according to the given base
+ * and save the result in *res*.
+ *
+ * The string may begin with an arbitrary amount of white space
+ * (as determined by **isspace**\ (3)) followed by a single
+ * optional '**-**' sign.
+ *
+ * Five least significant bits of *flags* encode base, other bits
+ * are currently unused.
+ *
+ * Base must be either 8, 10, 16 or 0 to detect it automatically
+ * similar to user space **strtol**\ (3).
+ *
+ * Returns
+ * Number of characters consumed on success. Must be positive but
+ * no more than *buf_len*.
+ *
+ * **-EINVAL** if no valid digits were found or unsupported base
+ * was provided.
+ *
+ * **-ERANGE** if resulting value was out of range.
+ */
+static int (*bpf_strtol)(const char *buf, unsigned long buf_len, __u64 flags, long *res) = (void *) 105;
+
+/*
+ * bpf_strtoul
+ *
+ * Convert the initial part of the string from buffer *buf* of
+ * size *buf_len* to an unsigned long integer according to the
+ * given base and save the result in *res*.
+ *
+ * The string may begin with an arbitrary amount of white space
+ * (as determined by **isspace**\ (3)).
+ *
+ * Five least significant bits of *flags* encode base, other bits
+ * are currently unused.
+ *
+ * Base must be either 8, 10, 16 or 0 to detect it automatically
+ * similar to user space **strtoul**\ (3).
+ *
+ * Returns
+ * Number of characters consumed on success. Must be positive but
+ * no more than *buf_len*.
+ *
+ * **-EINVAL** if no valid digits were found or unsupported base
+ * was provided.
+ *
+ * **-ERANGE** if resulting value was out of range.
+ */
+static int (*bpf_strtoul)(const char *buf, unsigned long buf_len, __u64 flags, unsigned long *res) = (void *) 106;
+
+/*
+ * bpf_sk_storage_get
+ *
+ * Get a bpf-local-storage from a *sk*.
+ *
+ * Logically, it could be thought of getting the value from
+ * a *map* with *sk* as the **key**. From this
+ * perspective, the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this
+ * helper enforces the key must be a full socket and the map must
+ * be a **BPF_MAP_TYPE_SK_STORAGE** also.
+ *
+ * Underneath, the value is stored locally at *sk* instead of
+ * the *map*. The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf-local-storages residing at *sk*.
+ *
+ * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf-local-storage will be
+ * created if one does not exist. *value* can be used
+ * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf-local-storage. If *value* is
+ * **NULL**, the new bpf-local-storage will be zero initialized.
+ *
+ * Returns
+ * A bpf-local-storage pointer is returned on success.
+ *
+ * **NULL** if not found or there was an error in adding
+ * a new bpf-local-storage.
+ */
+static void *(*bpf_sk_storage_get)(void *map, struct bpf_sock *sk, void *value, __u64 flags) = (void *) 107;
+
+/*
+ * bpf_sk_storage_delete
+ *
+ * Delete a bpf-local-storage from a *sk*.
+ *
+ * Returns
+ * 0 on success.
+ *
+ * **-ENOENT** if the bpf-local-storage cannot be found.
+ */
+static int (*bpf_sk_storage_delete)(void *map, struct bpf_sock *sk) = (void *) 108;
+
+/*
+ * bpf_send_signal
+ *
+ * Send signal *sig* to the current task.
+ *
+ * Returns
+ * 0 on success or successfully queued.
+ *
+ * **-EBUSY** if work queue under nmi is full.
+ *
+ * **-EINVAL** if *sig* is invalid.
+ *
+ * **-EPERM** if no permission to send the *sig*.
+ *
+ * **-EAGAIN** if bpf program can try again.
+ */
+static int (*bpf_send_signal)(__u32 sig) = (void *) 109;
+
+/*
+ * bpf_tcp_gen_syncookie
+ *
+ * Try to issue a SYN cookie for the packet with corresponding
+ * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*.
+ *
+ * *iph* points to the start of the IPv4 or IPv6 header, while
+ * *iph_len* contains **sizeof**\ (**struct iphdr**) or
+ * **sizeof**\ (**struct ip6hdr**).
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains the length of the TCP header.
+ *
+ *
+ * Returns
+ * On success, lower 32 bits hold the generated SYN cookie in
+ * followed by 16 bits which hold the MSS value for that cookie,
+ * and the top 16 bits are unused.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** SYN cookie cannot be issued due to error
+ *
+ * **-ENOENT** SYN cookie should not be issued (no SYN flood)
+ *
+ * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies
+ *
+ * **-EPROTONOSUPPORT** IP packet version is not 4 or 6
+ */
+static __s64 (*bpf_tcp_gen_syncookie)(struct bpf_sock *sk, void *iph, __u32 iph_len, struct tcphdr *th, __u32 th_len) = (void *) 110;
+
+/*
+ * bpf_skb_output
+ *
+ * Write raw *data* blob into a special BPF perf event held by
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
+ * event must have the following attributes: **PERF_SAMPLE_RAW**
+ * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
+ * **PERF_COUNT_SW_BPF_OUTPUT** as **config**.
+ *
+ * The *flags* are used to indicate the index in *map* for which
+ * the value must be put, masked with **BPF_F_INDEX_MASK**.
+ * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
+ * to indicate that the index of the current CPU core should be
+ * used.
+ *
+ * The value to write, of *size*, is passed through eBPF stack and
+ * pointed by *data*.
+ *
+ * *ctx* is a pointer to in-kernel struct sk_buff.
+ *
+ * This helper is similar to **bpf_perf_event_output**\ () but
+ * restricted to raw_tracepoint bpf programs.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_skb_output)(void *ctx, void *map, __u64 flags, void *data, __u64 size) = (void *) 111;
+
+/*
+ * bpf_probe_read_user
+ *
+ * Safely attempt to read *size* bytes from user space address
+ * *unsafe_ptr* and store the data in *dst*.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_probe_read_user)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 112;
+
+/*
+ * bpf_probe_read_kernel
+ *
+ * Safely attempt to read *size* bytes from kernel space address
+ * *unsafe_ptr* and store the data in *dst*.
+ *
+ * Returns
+ * 0 on success, or a negative error in case of failure.
+ */
+static int (*bpf_probe_read_kernel)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 113;
+
+/*
+ * bpf_probe_read_user_str
+ *
+ * Copy a NUL terminated string from an unsafe user address
+ * *unsafe_ptr* to *dst*. The *size* should include the
+ * terminating NUL byte. In case the string length is smaller than
+ * *size*, the target is not padded with further NUL bytes. If the
+ * string length is larger than *size*, just *size*-1 bytes are
+ * copied and the last byte is set to NUL.
+ *
+ * On success, the length of the copied string is returned. This
+ * makes this helper useful in tracing programs for reading
+ * strings, and more importantly to get its length at runtime. See
+ * the following snippet:
+ *
+ * ::
+ *
+ * SEC("kprobe/sys_open")
+ * void bpf_sys_open(struct pt_regs *ctx)
+ * {
+ * char buf[PATHLEN]; // PATHLEN is defined to 256
+ * int res = bpf_probe_read_user_str(buf, sizeof(buf),
+ * ctx->di);
+ *
+ * // Consume buf, for example push it to
+ * // userspace via bpf_perf_event_output(); we
+ * // can use res (the string length) as event
+ * // size, after checking its boundaries.
+ * }
+ *
+ * In comparison, using **bpf_probe_read_user()** helper here
+ * instead to read the string would require to estimate the length
+ * at compile time, and would often result in copying more memory
+ * than necessary.
+ *
+ * Another useful use case is when parsing individual process
+ * arguments or individual environment variables navigating
+ * *current*\ **->mm->arg_start** and *current*\
+ * **->mm->env_start**: using this helper and the return value,
+ * one can quickly iterate at the right offset of the memory area.
+ *
+ * Returns
+ * On success, the strictly positive length of the string,
+ * including the trailing NUL character. On error, a negative
+ * value.
+ */
+static int (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114;
+
+/*
+ * bpf_probe_read_kernel_str
+ *
+ * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr*
+ * to *dst*. Same semantics as with bpf_probe_read_user_str() apply.
+ *
+ * Returns
+ * On success, the strictly positive length of the string, including
+ * the trailing NUL character. On error, a negative value.
+ */
+static int (*bpf_probe_read_kernel_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 115;
+
+
diff --git a/src/contrib/libbpf/bpf/bpf_helpers.h b/src/contrib/libbpf/bpf/bpf_helpers.h
new file mode 100644
index 0000000..0c7d282
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_helpers.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_HELPERS__
+#define __BPF_HELPERS__
+
+#include "bpf_helper_defs.h"
+
+#define __uint(name, val) int (*name)[val]
+#define __type(name, val) typeof(val) *name
+
+/* Helper macro to print out debug messages */
+#define bpf_printk(fmt, ...) \
+({ \
+ char ____fmt[] = fmt; \
+ bpf_trace_printk(____fmt, sizeof(____fmt), \
+ ##__VA_ARGS__); \
+})
+
+/*
+ * Helper macro to place programs, maps, license in
+ * different sections in elf_bpf file. Section names
+ * are interpreted by elf_bpf loader
+ */
+#define SEC(NAME) __attribute__((section(NAME), used))
+
+#ifndef __always_inline
+#define __always_inline __attribute__((always_inline))
+#endif
+
+/*
+ * Helper structure used by eBPF C program
+ * to describe BPF map attributes to libbpf loader
+ */
+struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+ unsigned int map_flags;
+};
+
+enum libbpf_pin_type {
+ LIBBPF_PIN_NONE,
+ /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
+ LIBBPF_PIN_BY_NAME,
+};
+
+#endif
diff --git a/src/contrib/libbpf/bpf/bpf_prog_linfo.c b/src/contrib/libbpf/bpf/bpf_prog_linfo.c
new file mode 100644
index 0000000..3ed1a27
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_prog_linfo.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2018 Facebook */
+
+#include <string.h>
+#include <stdlib.h>
+#include <linux/err.h>
+#include <linux/bpf.h>
+#include "libbpf.h"
+#include "libbpf_internal.h"
+
+struct bpf_prog_linfo {
+ void *raw_linfo;
+ void *raw_jited_linfo;
+ __u32 *nr_jited_linfo_per_func;
+ __u32 *jited_linfo_func_idx;
+ __u32 nr_linfo;
+ __u32 nr_jited_func;
+ __u32 rec_size;
+ __u32 jited_rec_size;
+};
+
+static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
+ const __u64 *ksym_func, const __u32 *ksym_len)
+{
+ __u32 nr_jited_func, nr_linfo;
+ const void *raw_jited_linfo;
+ const __u64 *jited_linfo;
+ __u64 last_jited_linfo;
+ /*
+ * Index to raw_jited_linfo:
+ * i: Index for searching the next ksym_func
+ * prev_i: Index to the last found ksym_func
+ */
+ __u32 i, prev_i;
+ __u32 f; /* Index to ksym_func */
+
+ raw_jited_linfo = prog_linfo->raw_jited_linfo;
+ jited_linfo = raw_jited_linfo;
+ if (ksym_func[0] != *jited_linfo)
+ goto errout;
+
+ prog_linfo->jited_linfo_func_idx[0] = 0;
+ nr_jited_func = prog_linfo->nr_jited_func;
+ nr_linfo = prog_linfo->nr_linfo;
+
+ for (prev_i = 0, i = 1, f = 1;
+ i < nr_linfo && f < nr_jited_func;
+ i++) {
+ raw_jited_linfo += prog_linfo->jited_rec_size;
+ last_jited_linfo = *jited_linfo;
+ jited_linfo = raw_jited_linfo;
+
+ if (ksym_func[f] == *jited_linfo) {
+ prog_linfo->jited_linfo_func_idx[f] = i;
+
+ /* Sanity check */
+ if (last_jited_linfo - ksym_func[f - 1] + 1 >
+ ksym_len[f - 1])
+ goto errout;
+
+ prog_linfo->nr_jited_linfo_per_func[f - 1] =
+ i - prev_i;
+ prev_i = i;
+
+ /*
+ * The ksym_func[f] is found in jited_linfo.
+ * Look for the next one.
+ */
+ f++;
+ } else if (*jited_linfo <= last_jited_linfo) {
+ /* Ensure the addr is increasing _within_ a func */
+ goto errout;
+ }
+ }
+
+ if (f != nr_jited_func)
+ goto errout;
+
+ prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
+ nr_linfo - prev_i;
+
+ return 0;
+
+errout:
+ return -EINVAL;
+}
+
+void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
+{
+ if (!prog_linfo)
+ return;
+
+ free(prog_linfo->raw_linfo);
+ free(prog_linfo->raw_jited_linfo);
+ free(prog_linfo->nr_jited_linfo_per_func);
+ free(prog_linfo->jited_linfo_func_idx);
+ free(prog_linfo);
+}
+
+struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
+{
+ struct bpf_prog_linfo *prog_linfo;
+ __u32 nr_linfo, nr_jited_func;
+ __u64 data_sz;
+
+ nr_linfo = info->nr_line_info;
+
+ if (!nr_linfo)
+ return NULL;
+
+ /*
+ * The min size that bpf_prog_linfo has to access for
+ * searching purpose.
+ */
+ if (info->line_info_rec_size <
+ offsetof(struct bpf_line_info, file_name_off))
+ return NULL;
+
+ prog_linfo = calloc(1, sizeof(*prog_linfo));
+ if (!prog_linfo)
+ return NULL;
+
+ /* Copy xlated line_info */
+ prog_linfo->nr_linfo = nr_linfo;
+ prog_linfo->rec_size = info->line_info_rec_size;
+ data_sz = (__u64)nr_linfo * prog_linfo->rec_size;
+ prog_linfo->raw_linfo = malloc(data_sz);
+ if (!prog_linfo->raw_linfo)
+ goto err_free;
+ memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info, data_sz);
+
+ nr_jited_func = info->nr_jited_ksyms;
+ if (!nr_jited_func ||
+ !info->jited_line_info ||
+ info->nr_jited_line_info != nr_linfo ||
+ info->jited_line_info_rec_size < sizeof(__u64) ||
+ info->nr_jited_func_lens != nr_jited_func ||
+ !info->jited_ksyms ||
+ !info->jited_func_lens)
+ /* Not enough info to provide jited_line_info */
+ return prog_linfo;
+
+ /* Copy jited_line_info */
+ prog_linfo->nr_jited_func = nr_jited_func;
+ prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
+ data_sz = (__u64)nr_linfo * prog_linfo->jited_rec_size;
+ prog_linfo->raw_jited_linfo = malloc(data_sz);
+ if (!prog_linfo->raw_jited_linfo)
+ goto err_free;
+ memcpy(prog_linfo->raw_jited_linfo,
+ (void *)(long)info->jited_line_info, data_sz);
+
+ /* Number of jited_line_info per jited func */
+ prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
+ sizeof(__u32));
+ if (!prog_linfo->nr_jited_linfo_per_func)
+ goto err_free;
+
+ /*
+ * For each jited func,
+ * the start idx to the "linfo" and "jited_linfo" array,
+ */
+ prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
+ sizeof(__u32));
+ if (!prog_linfo->jited_linfo_func_idx)
+ goto err_free;
+
+ if (dissect_jited_func(prog_linfo,
+ (__u64 *)(long)info->jited_ksyms,
+ (__u32 *)(long)info->jited_func_lens))
+ goto err_free;
+
+ return prog_linfo;
+
+err_free:
+ bpf_prog_linfo__free(prog_linfo);
+ return NULL;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+ __u64 addr, __u32 func_idx, __u32 nr_skip)
+{
+ __u32 jited_rec_size, rec_size, nr_linfo, start, i;
+ const void *raw_jited_linfo, *raw_linfo;
+ const __u64 *jited_linfo;
+
+ if (func_idx >= prog_linfo->nr_jited_func)
+ return NULL;
+
+ nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
+ if (nr_skip >= nr_linfo)
+ return NULL;
+
+ start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
+ jited_rec_size = prog_linfo->jited_rec_size;
+ raw_jited_linfo = prog_linfo->raw_jited_linfo +
+ (start * jited_rec_size);
+ jited_linfo = raw_jited_linfo;
+ if (addr < *jited_linfo)
+ return NULL;
+
+ nr_linfo -= nr_skip;
+ rec_size = prog_linfo->rec_size;
+ raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
+ for (i = 0; i < nr_linfo; i++) {
+ if (addr < *jited_linfo)
+ break;
+
+ raw_linfo += rec_size;
+ raw_jited_linfo += jited_rec_size;
+ jited_linfo = raw_jited_linfo;
+ }
+
+ return raw_linfo - rec_size;
+}
+
+const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+ __u32 insn_off, __u32 nr_skip)
+{
+ const struct bpf_line_info *linfo;
+ __u32 rec_size, nr_linfo, i;
+ const void *raw_linfo;
+
+ nr_linfo = prog_linfo->nr_linfo;
+ if (nr_skip >= nr_linfo)
+ return NULL;
+
+ rec_size = prog_linfo->rec_size;
+ raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
+ linfo = raw_linfo;
+ if (insn_off < linfo->insn_off)
+ return NULL;
+
+ nr_linfo -= nr_skip;
+ for (i = 0; i < nr_linfo; i++) {
+ if (insn_off < linfo->insn_off)
+ break;
+
+ raw_linfo += rec_size;
+ linfo = raw_linfo;
+ }
+
+ return raw_linfo - rec_size;
+}
diff --git a/src/contrib/libbpf/bpf/bpf_tracing.h b/src/contrib/libbpf/bpf/bpf_tracing.h
new file mode 100644
index 0000000..b0dafe8
--- /dev/null
+++ b/src/contrib/libbpf/bpf/bpf_tracing.h
@@ -0,0 +1,195 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __BPF_TRACING_H__
+#define __BPF_TRACING_H__
+
+/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
+#if defined(__TARGET_ARCH_x86)
+ #define bpf_target_x86
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_s390)
+ #define bpf_target_s390
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_arm)
+ #define bpf_target_arm
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_arm64)
+ #define bpf_target_arm64
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_mips)
+ #define bpf_target_mips
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_powerpc)
+ #define bpf_target_powerpc
+ #define bpf_target_defined
+#elif defined(__TARGET_ARCH_sparc)
+ #define bpf_target_sparc
+ #define bpf_target_defined
+#else
+ #undef bpf_target_defined
+#endif
+
+/* Fall back to what the compiler says */
+#ifndef bpf_target_defined
+#if defined(__x86_64__)
+ #define bpf_target_x86
+#elif defined(__s390__)
+ #define bpf_target_s390
+#elif defined(__arm__)
+ #define bpf_target_arm
+#elif defined(__aarch64__)
+ #define bpf_target_arm64
+#elif defined(__mips__)
+ #define bpf_target_mips
+#elif defined(__powerpc__)
+ #define bpf_target_powerpc
+#elif defined(__sparc__)
+ #define bpf_target_sparc
+#endif
+#endif
+
+#if defined(bpf_target_x86)
+
+#ifdef __KERNEL__
+#define PT_REGS_PARM1(x) ((x)->di)
+#define PT_REGS_PARM2(x) ((x)->si)
+#define PT_REGS_PARM3(x) ((x)->dx)
+#define PT_REGS_PARM4(x) ((x)->cx)
+#define PT_REGS_PARM5(x) ((x)->r8)
+#define PT_REGS_RET(x) ((x)->sp)
+#define PT_REGS_FP(x) ((x)->bp)
+#define PT_REGS_RC(x) ((x)->ax)
+#define PT_REGS_SP(x) ((x)->sp)
+#define PT_REGS_IP(x) ((x)->ip)
+#else
+#ifdef __i386__
+/* i386 kernel is built with -mregparm=3 */
+#define PT_REGS_PARM1(x) ((x)->eax)
+#define PT_REGS_PARM2(x) ((x)->edx)
+#define PT_REGS_PARM3(x) ((x)->ecx)
+#define PT_REGS_PARM4(x) 0
+#define PT_REGS_PARM5(x) 0
+#define PT_REGS_RET(x) ((x)->esp)
+#define PT_REGS_FP(x) ((x)->ebp)
+#define PT_REGS_RC(x) ((x)->eax)
+#define PT_REGS_SP(x) ((x)->esp)
+#define PT_REGS_IP(x) ((x)->eip)
+#else
+#define PT_REGS_PARM1(x) ((x)->rdi)
+#define PT_REGS_PARM2(x) ((x)->rsi)
+#define PT_REGS_PARM3(x) ((x)->rdx)
+#define PT_REGS_PARM4(x) ((x)->rcx)
+#define PT_REGS_PARM5(x) ((x)->r8)
+#define PT_REGS_RET(x) ((x)->rsp)
+#define PT_REGS_FP(x) ((x)->rbp)
+#define PT_REGS_RC(x) ((x)->rax)
+#define PT_REGS_SP(x) ((x)->rsp)
+#define PT_REGS_IP(x) ((x)->rip)
+#endif
+#endif
+
+#elif defined(bpf_target_s390)
+
+/* s390 provides user_pt_regs instead of struct pt_regs to userspace */
+struct pt_regs;
+#define PT_REGS_S390 const volatile user_pt_regs
+#define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2])
+#define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3])
+#define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4])
+#define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5])
+#define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6])
+#define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14])
+/* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11])
+#define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2])
+#define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15])
+#define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr)
+
+#elif defined(bpf_target_arm)
+
+#define PT_REGS_PARM1(x) ((x)->uregs[0])
+#define PT_REGS_PARM2(x) ((x)->uregs[1])
+#define PT_REGS_PARM3(x) ((x)->uregs[2])
+#define PT_REGS_PARM4(x) ((x)->uregs[3])
+#define PT_REGS_PARM5(x) ((x)->uregs[4])
+#define PT_REGS_RET(x) ((x)->uregs[14])
+#define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_RC(x) ((x)->uregs[0])
+#define PT_REGS_SP(x) ((x)->uregs[13])
+#define PT_REGS_IP(x) ((x)->uregs[12])
+
+#elif defined(bpf_target_arm64)
+
+/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
+struct pt_regs;
+#define PT_REGS_ARM64 const volatile struct user_pt_regs
+#define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
+#define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
+#define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
+#define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])
+#define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])
+#define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30])
+/* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])
+#define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])
+#define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
+#define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)
+
+#elif defined(bpf_target_mips)
+
+#define PT_REGS_PARM1(x) ((x)->regs[4])
+#define PT_REGS_PARM2(x) ((x)->regs[5])
+#define PT_REGS_PARM3(x) ((x)->regs[6])
+#define PT_REGS_PARM4(x) ((x)->regs[7])
+#define PT_REGS_PARM5(x) ((x)->regs[8])
+#define PT_REGS_RET(x) ((x)->regs[31])
+#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */
+#define PT_REGS_RC(x) ((x)->regs[1])
+#define PT_REGS_SP(x) ((x)->regs[29])
+#define PT_REGS_IP(x) ((x)->cp0_epc)
+
+#elif defined(bpf_target_powerpc)
+
+#define PT_REGS_PARM1(x) ((x)->gpr[3])
+#define PT_REGS_PARM2(x) ((x)->gpr[4])
+#define PT_REGS_PARM3(x) ((x)->gpr[5])
+#define PT_REGS_PARM4(x) ((x)->gpr[6])
+#define PT_REGS_PARM5(x) ((x)->gpr[7])
+#define PT_REGS_RC(x) ((x)->gpr[3])
+#define PT_REGS_SP(x) ((x)->sp)
+#define PT_REGS_IP(x) ((x)->nip)
+
+#elif defined(bpf_target_sparc)
+
+#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])
+#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])
+#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2])
+#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3])
+#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4])
+#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])
+#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])
+#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])
+
+/* Should this also be a bpf_target check for the sparc case? */
+#if defined(__arch64__)
+#define PT_REGS_IP(x) ((x)->tpc)
+#else
+#define PT_REGS_IP(x) ((x)->pc)
+#endif
+
+#endif
+
+#if defined(bpf_target_powerpc)
+#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
+#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
+#elif defined(bpf_target_sparc)
+#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
+#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
+#else
+#define BPF_KPROBE_READ_RET_IP(ip, ctx) \
+ ({ bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
+#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \
+ ({ bpf_probe_read(&(ip), sizeof(ip), \
+ (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
+#endif
+
+#endif
diff --git a/src/contrib/libbpf/bpf/btf.c b/src/contrib/libbpf/bpf/btf.c
new file mode 100644
index 0000000..88efa2b
--- /dev/null
+++ b/src/contrib/libbpf/bpf/btf.c
@@ -0,0 +1,2884 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2018 Facebook */
+
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <linux/btf.h>
+#include <gelf.h>
+#include "btf.h"
+#include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+#include "hashmap.h"
+
+#define BTF_MAX_NR_TYPES 0x7fffffff
+#define BTF_MAX_STR_OFFSET 0x7fffffff
+
+static struct btf_type btf_void;
+
+struct btf {
+ union {
+ struct btf_header *hdr;
+ void *data;
+ };
+ struct btf_type **types;
+ const char *strings;
+ void *nohdr_data;
+ __u32 nr_types;
+ __u32 types_size;
+ __u32 data_size;
+ int fd;
+};
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
+static int btf_add_type(struct btf *btf, struct btf_type *t)
+{
+ if (btf->types_size - btf->nr_types < 2) {
+ struct btf_type **new_types;
+ __u32 expand_by, new_size;
+
+ if (btf->types_size == BTF_MAX_NR_TYPES)
+ return -E2BIG;
+
+ expand_by = max(btf->types_size >> 2, 16);
+ new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by);
+
+ new_types = realloc(btf->types, sizeof(*new_types) * new_size);
+ if (!new_types)
+ return -ENOMEM;
+
+ if (btf->nr_types == 0)
+ new_types[0] = &btf_void;
+
+ btf->types = new_types;
+ btf->types_size = new_size;
+ }
+
+ btf->types[++(btf->nr_types)] = t;
+
+ return 0;
+}
+
+static int btf_parse_hdr(struct btf *btf)
+{
+ const struct btf_header *hdr = btf->hdr;
+ __u32 meta_left;
+
+ if (btf->data_size < sizeof(struct btf_header)) {
+ pr_debug("BTF header not found\n");
+ return -EINVAL;
+ }
+
+ if (hdr->magic != BTF_MAGIC) {
+ pr_debug("Invalid BTF magic:%x\n", hdr->magic);
+ return -EINVAL;
+ }
+
+ if (hdr->version != BTF_VERSION) {
+ pr_debug("Unsupported BTF version:%u\n", hdr->version);
+ return -ENOTSUP;
+ }
+
+ if (hdr->flags) {
+ pr_debug("Unsupported BTF flags:%x\n", hdr->flags);
+ return -ENOTSUP;
+ }
+
+ meta_left = btf->data_size - sizeof(*hdr);
+ if (!meta_left) {
+ pr_debug("BTF has no data\n");
+ return -EINVAL;
+ }
+
+ if (meta_left < hdr->type_off) {
+ pr_debug("Invalid BTF type section offset:%u\n", hdr->type_off);
+ return -EINVAL;
+ }
+
+ if (meta_left < hdr->str_off) {
+ pr_debug("Invalid BTF string section offset:%u\n", hdr->str_off);
+ return -EINVAL;
+ }
+
+ if (hdr->type_off >= hdr->str_off) {
+ pr_debug("BTF type section offset >= string section offset. No type?\n");
+ return -EINVAL;
+ }
+
+ if (hdr->type_off & 0x02) {
+ pr_debug("BTF type section is not aligned to 4 bytes\n");
+ return -EINVAL;
+ }
+
+ btf->nohdr_data = btf->hdr + 1;
+
+ return 0;
+}
+
+static int btf_parse_str_sec(struct btf *btf)
+{
+ const struct btf_header *hdr = btf->hdr;
+ const char *start = btf->nohdr_data + hdr->str_off;
+ const char *end = start + btf->hdr->str_len;
+
+ if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_STR_OFFSET ||
+ start[0] || end[-1]) {
+ pr_debug("Invalid BTF string section\n");
+ return -EINVAL;
+ }
+
+ btf->strings = start;
+
+ return 0;
+}
+
+static int btf_type_size(struct btf_type *t)
+{
+ int base_size = sizeof(struct btf_type);
+ __u16 vlen = btf_vlen(t);
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_FWD:
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ return base_size;
+ case BTF_KIND_INT:
+ return base_size + sizeof(__u32);
+ case BTF_KIND_ENUM:
+ return base_size + vlen * sizeof(struct btf_enum);
+ case BTF_KIND_ARRAY:
+ return base_size + sizeof(struct btf_array);
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ return base_size + vlen * sizeof(struct btf_member);
+ case BTF_KIND_FUNC_PROTO:
+ return base_size + vlen * sizeof(struct btf_param);
+ case BTF_KIND_VAR:
+ return base_size + sizeof(struct btf_var);
+ case BTF_KIND_DATASEC:
+ return base_size + vlen * sizeof(struct btf_var_secinfo);
+ default:
+ pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
+ return -EINVAL;
+ }
+}
+
+static int btf_parse_type_sec(struct btf *btf)
+{
+ struct btf_header *hdr = btf->hdr;
+ void *nohdr_data = btf->nohdr_data;
+ void *next_type = nohdr_data + hdr->type_off;
+ void *end_type = nohdr_data + hdr->str_off;
+
+ while (next_type < end_type) {
+ struct btf_type *t = next_type;
+ int type_size;
+ int err;
+
+ type_size = btf_type_size(t);
+ if (type_size < 0)
+ return type_size;
+ next_type += type_size;
+ err = btf_add_type(btf, t);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+__u32 btf__get_nr_types(const struct btf *btf)
+{
+ return btf->nr_types;
+}
+
+const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
+{
+ if (type_id > btf->nr_types)
+ return NULL;
+
+ return btf->types[type_id];
+}
+
+static bool btf_type_is_void(const struct btf_type *t)
+{
+ return t == &btf_void || btf_is_fwd(t);
+}
+
+static bool btf_type_is_void_or_null(const struct btf_type *t)
+{
+ return !t || btf_type_is_void(t);
+}
+
+#define MAX_RESOLVE_DEPTH 32
+
+__s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
+{
+ const struct btf_array *array;
+ const struct btf_type *t;
+ __u32 nelems = 1;
+ __s64 size = -1;
+ int i;
+
+ t = btf__type_by_id(btf, type_id);
+ for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
+ i++) {
+ switch (btf_kind(t)) {
+ case BTF_KIND_INT:
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ case BTF_KIND_ENUM:
+ case BTF_KIND_DATASEC:
+ size = t->size;
+ goto done;
+ case BTF_KIND_PTR:
+ size = sizeof(void *);
+ goto done;
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_VAR:
+ type_id = t->type;
+ break;
+ case BTF_KIND_ARRAY:
+ array = btf_array(t);
+ if (nelems && array->nelems > UINT32_MAX / nelems)
+ return -E2BIG;
+ nelems *= array->nelems;
+ type_id = array->type;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ t = btf__type_by_id(btf, type_id);
+ }
+
+done:
+ if (size < 0)
+ return -EINVAL;
+ if (nelems && size > UINT32_MAX / nelems)
+ return -E2BIG;
+
+ return nelems * size;
+}
+
+int btf__resolve_type(const struct btf *btf, __u32 type_id)
+{
+ const struct btf_type *t;
+ int depth = 0;
+
+ t = btf__type_by_id(btf, type_id);
+ while (depth < MAX_RESOLVE_DEPTH &&
+ !btf_type_is_void_or_null(t) &&
+ (btf_is_mod(t) || btf_is_typedef(t) || btf_is_var(t))) {
+ type_id = t->type;
+ t = btf__type_by_id(btf, type_id);
+ depth++;
+ }
+
+ if (depth == MAX_RESOLVE_DEPTH || btf_type_is_void_or_null(t))
+ return -EINVAL;
+
+ return type_id;
+}
+
+__s32 btf__find_by_name(const struct btf *btf, const char *type_name)
+{
+ __u32 i;
+
+ if (!strcmp(type_name, "void"))
+ return 0;
+
+ for (i = 1; i <= btf->nr_types; i++) {
+ const struct btf_type *t = btf->types[i];
+ const char *name = btf__name_by_offset(btf, t->name_off);
+
+ if (name && !strcmp(type_name, name))
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+__s32 btf__find_by_name_kind(const struct btf *btf, const char *type_name,
+ __u32 kind)
+{
+ __u32 i;
+
+ if (kind == BTF_KIND_UNKN || !strcmp(type_name, "void"))
+ return 0;
+
+ for (i = 1; i <= btf->nr_types; i++) {
+ const struct btf_type *t = btf->types[i];
+ const char *name;
+
+ if (btf_kind(t) != kind)
+ continue;
+ name = btf__name_by_offset(btf, t->name_off);
+ if (name && !strcmp(type_name, name))
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+void btf__free(struct btf *btf)
+{
+ if (!btf)
+ return;
+
+ if (btf->fd != -1)
+ close(btf->fd);
+
+ free(btf->data);
+ free(btf->types);
+ free(btf);
+}
+
+struct btf *btf__new(__u8 *data, __u32 size)
+{
+ struct btf *btf;
+ int err;
+
+ btf = calloc(1, sizeof(struct btf));
+ if (!btf)
+ return ERR_PTR(-ENOMEM);
+
+ btf->fd = -1;
+
+ btf->data = malloc(size);
+ if (!btf->data) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ memcpy(btf->data, data, size);
+ btf->data_size = size;
+
+ err = btf_parse_hdr(btf);
+ if (err)
+ goto done;
+
+ err = btf_parse_str_sec(btf);
+ if (err)
+ goto done;
+
+ err = btf_parse_type_sec(btf);
+
+done:
+ if (err) {
+ btf__free(btf);
+ return ERR_PTR(err);
+ }
+
+ return btf;
+}
+
+static bool btf_check_endianness(const GElf_Ehdr *ehdr)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return ehdr->e_ident[EI_DATA] == ELFDATA2LSB;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ return ehdr->e_ident[EI_DATA] == ELFDATA2MSB;
+#else
+# error "Unrecognized __BYTE_ORDER__"
+#endif
+}
+
+struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext)
+{
+ Elf_Data *btf_data = NULL, *btf_ext_data = NULL;
+ int err = 0, fd = -1, idx = 0;
+ struct btf *btf = NULL;
+ Elf_Scn *scn = NULL;
+ Elf *elf = NULL;
+ GElf_Ehdr ehdr;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_warn("failed to init libelf for %s\n", path);
+ return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
+ }
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ err = -errno;
+ pr_warn("failed to open %s: %s\n", path, strerror(errno));
+ return ERR_PTR(err);
+ }
+
+ err = -LIBBPF_ERRNO__FORMAT;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+ if (!elf) {
+ pr_warn("failed to open %s as ELF file\n", path);
+ goto done;
+ }
+ if (!gelf_getehdr(elf, &ehdr)) {
+ pr_warn("failed to get EHDR from %s\n", path);
+ goto done;
+ }
+ if (!btf_check_endianness(&ehdr)) {
+ pr_warn("non-native ELF endianness is not supported\n");
+ goto done;
+ }
+ if (!elf_rawdata(elf_getscn(elf, ehdr.e_shstrndx), NULL)) {
+ pr_warn("failed to get e_shstrndx from %s\n", path);
+ goto done;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ GElf_Shdr sh;
+ char *name;
+
+ idx++;
+ if (gelf_getshdr(scn, &sh) != &sh) {
+ pr_warn("failed to get section(%d) header from %s\n",
+ idx, path);
+ goto done;
+ }
+ name = elf_strptr(elf, ehdr.e_shstrndx, sh.sh_name);
+ if (!name) {
+ pr_warn("failed to get section(%d) name from %s\n",
+ idx, path);
+ goto done;
+ }
+ if (strcmp(name, BTF_ELF_SEC) == 0) {
+ btf_data = elf_getdata(scn, 0);
+ if (!btf_data) {
+ pr_warn("failed to get section(%d, %s) data from %s\n",
+ idx, name, path);
+ goto done;
+ }
+ continue;
+ } else if (btf_ext && strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+ btf_ext_data = elf_getdata(scn, 0);
+ if (!btf_ext_data) {
+ pr_warn("failed to get section(%d, %s) data from %s\n",
+ idx, name, path);
+ goto done;
+ }
+ continue;
+ }
+ }
+
+ err = 0;
+
+ if (!btf_data) {
+ err = -ENOENT;
+ goto done;
+ }
+ btf = btf__new(btf_data->d_buf, btf_data->d_size);
+ if (IS_ERR(btf))
+ goto done;
+
+ if (btf_ext && btf_ext_data) {
+ *btf_ext = btf_ext__new(btf_ext_data->d_buf,
+ btf_ext_data->d_size);
+ if (IS_ERR(*btf_ext))
+ goto done;
+ } else if (btf_ext) {
+ *btf_ext = NULL;
+ }
+done:
+ if (elf)
+ elf_end(elf);
+ close(fd);
+
+ if (err)
+ return ERR_PTR(err);
+ /*
+ * btf is always parsed before btf_ext, so no need to clean up
+ * btf_ext, if btf loading failed
+ */
+ if (IS_ERR(btf))
+ return btf;
+ if (btf_ext && IS_ERR(*btf_ext)) {
+ btf__free(btf);
+ err = PTR_ERR(*btf_ext);
+ return ERR_PTR(err);
+ }
+ return btf;
+}
+
+static int compare_vsi_off(const void *_a, const void *_b)
+{
+ const struct btf_var_secinfo *a = _a;
+ const struct btf_var_secinfo *b = _b;
+
+ return a->offset - b->offset;
+}
+
+static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
+ struct btf_type *t)
+{
+ __u32 size = 0, off = 0, i, vars = btf_vlen(t);
+ const char *name = btf__name_by_offset(btf, t->name_off);
+ const struct btf_type *t_var;
+ struct btf_var_secinfo *vsi;
+ const struct btf_var *var;
+ int ret;
+
+ if (!name) {
+ pr_debug("No name found in string section for DATASEC kind.\n");
+ return -ENOENT;
+ }
+
+ ret = bpf_object__section_size(obj, name, &size);
+ if (ret || !size || (t->size && t->size != size)) {
+ pr_debug("Invalid size for section %s: %u bytes\n", name, size);
+ return -ENOENT;
+ }
+
+ t->size = size;
+
+ for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
+ t_var = btf__type_by_id(btf, vsi->type);
+ var = btf_var(t_var);
+
+ if (!btf_is_var(t_var)) {
+ pr_debug("Non-VAR type seen in section %s\n", name);
+ return -EINVAL;
+ }
+
+ if (var->linkage == BTF_VAR_STATIC)
+ continue;
+
+ name = btf__name_by_offset(btf, t_var->name_off);
+ if (!name) {
+ pr_debug("No name found in string section for VAR kind\n");
+ return -ENOENT;
+ }
+
+ ret = bpf_object__variable_offset(obj, name, &off);
+ if (ret) {
+ pr_debug("No offset found in symbol table for VAR %s\n",
+ name);
+ return -ENOENT;
+ }
+
+ vsi->offset = off;
+ }
+
+ qsort(t + 1, vars, sizeof(*vsi), compare_vsi_off);
+ return 0;
+}
+
+int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
+{
+ int err = 0;
+ __u32 i;
+
+ for (i = 1; i <= btf->nr_types; i++) {
+ struct btf_type *t = btf->types[i];
+
+ /* Loader needs to fix up some of the things compiler
+ * couldn't get its hands on while emitting BTF. This
+ * is section size and global variable offset. We use
+ * the info from the ELF itself for this purpose.
+ */
+ if (btf_is_datasec(t)) {
+ err = btf_fixup_datasec(obj, btf, t);
+ if (err)
+ break;
+ }
+ }
+
+ return err;
+}
+
+int btf__load(struct btf *btf)
+{
+ __u32 log_buf_size = BPF_LOG_BUF_SIZE;
+ char *log_buf = NULL;
+ int err = 0;
+
+ if (btf->fd >= 0)
+ return -EEXIST;
+
+ log_buf = malloc(log_buf_size);
+ if (!log_buf)
+ return -ENOMEM;
+
+ *log_buf = 0;
+
+ btf->fd = bpf_load_btf(btf->data, btf->data_size,
+ log_buf, log_buf_size, false);
+ if (btf->fd < 0) {
+ err = -errno;
+ pr_warn("Error loading BTF: %s(%d)\n", strerror(errno), errno);
+ if (*log_buf)
+ pr_warn("%s\n", log_buf);
+ goto done;
+ }
+
+done:
+ free(log_buf);
+ return err;
+}
+
+int btf__fd(const struct btf *btf)
+{
+ return btf->fd;
+}
+
+const void *btf__get_raw_data(const struct btf *btf, __u32 *size)
+{
+ *size = btf->data_size;
+ return btf->data;
+}
+
+const char *btf__name_by_offset(const struct btf *btf, __u32 offset)
+{
+ if (offset < btf->hdr->str_len)
+ return &btf->strings[offset];
+ else
+ return NULL;
+}
+
+int btf__get_from_id(__u32 id, struct btf **btf)
+{
+ struct bpf_btf_info btf_info = { 0 };
+ __u32 len = sizeof(btf_info);
+ __u32 last_size;
+ int btf_fd;
+ void *ptr;
+ int err;
+
+ err = 0;
+ *btf = NULL;
+ btf_fd = bpf_btf_get_fd_by_id(id);
+ if (btf_fd < 0)
+ return 0;
+
+ /* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
+ * let's start with a sane default - 4KiB here - and resize it only if
+ * bpf_obj_get_info_by_fd() needs a bigger buffer.
+ */
+ btf_info.btf_size = 4096;
+ last_size = btf_info.btf_size;
+ ptr = malloc(last_size);
+ if (!ptr) {
+ err = -ENOMEM;
+ goto exit_free;
+ }
+
+ memset(ptr, 0, last_size);
+ btf_info.btf = ptr_to_u64(ptr);
+ err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+
+ if (!err && btf_info.btf_size > last_size) {
+ void *temp_ptr;
+
+ last_size = btf_info.btf_size;
+ temp_ptr = realloc(ptr, last_size);
+ if (!temp_ptr) {
+ err = -ENOMEM;
+ goto exit_free;
+ }
+ ptr = temp_ptr;
+ memset(ptr, 0, last_size);
+ btf_info.btf = ptr_to_u64(ptr);
+ err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
+ }
+
+ if (err || btf_info.btf_size > last_size) {
+ err = errno;
+ goto exit_free;
+ }
+
+ *btf = btf__new((__u8 *)(long)btf_info.btf, btf_info.btf_size);
+ if (IS_ERR(*btf)) {
+ err = PTR_ERR(*btf);
+ *btf = NULL;
+ }
+
+exit_free:
+ close(btf_fd);
+ free(ptr);
+
+ return err;
+}
+
+int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
+ __u32 expected_key_size, __u32 expected_value_size,
+ __u32 *key_type_id, __u32 *value_type_id)
+{
+ const struct btf_type *container_type;
+ const struct btf_member *key, *value;
+ const size_t max_name = 256;
+ char container_name[max_name];
+ __s64 key_size, value_size;
+ __s32 container_id;
+
+ if (snprintf(container_name, max_name, "____btf_map_%s", map_name) ==
+ max_name) {
+ pr_warn("map:%s length of '____btf_map_%s' is too long\n",
+ map_name, map_name);
+ return -EINVAL;
+ }
+
+ container_id = btf__find_by_name(btf, container_name);
+ if (container_id < 0) {
+ pr_debug("map:%s container_name:%s cannot be found in BTF. Missing BPF_ANNOTATE_KV_PAIR?\n",
+ map_name, container_name);
+ return container_id;
+ }
+
+ container_type = btf__type_by_id(btf, container_id);
+ if (!container_type) {
+ pr_warn("map:%s cannot find BTF type for container_id:%u\n",
+ map_name, container_id);
+ return -EINVAL;
+ }
+
+ if (!btf_is_struct(container_type) || btf_vlen(container_type) < 2) {
+ pr_warn("map:%s container_name:%s is an invalid container struct\n",
+ map_name, container_name);
+ return -EINVAL;
+ }
+
+ key = btf_members(container_type);
+ value = key + 1;
+
+ key_size = btf__resolve_size(btf, key->type);
+ if (key_size < 0) {
+ pr_warn("map:%s invalid BTF key_type_size\n", map_name);
+ return key_size;
+ }
+
+ if (expected_key_size != key_size) {
+ pr_warn("map:%s btf_key_type_size:%u != map_def_key_size:%u\n",
+ map_name, (__u32)key_size, expected_key_size);
+ return -EINVAL;
+ }
+
+ value_size = btf__resolve_size(btf, value->type);
+ if (value_size < 0) {
+ pr_warn("map:%s invalid BTF value_type_size\n", map_name);
+ return value_size;
+ }
+
+ if (expected_value_size != value_size) {
+ pr_warn("map:%s btf_value_type_size:%u != map_def_value_size:%u\n",
+ map_name, (__u32)value_size, expected_value_size);
+ return -EINVAL;
+ }
+
+ *key_type_id = key->type;
+ *value_type_id = value->type;
+
+ return 0;
+}
+
+struct btf_ext_sec_setup_param {
+ __u32 off;
+ __u32 len;
+ __u32 min_rec_size;
+ struct btf_ext_info *ext_info;
+ const char *desc;
+};
+
+static int btf_ext_setup_info(struct btf_ext *btf_ext,
+ struct btf_ext_sec_setup_param *ext_sec)
+{
+ const struct btf_ext_info_sec *sinfo;
+ struct btf_ext_info *ext_info;
+ __u32 info_left, record_size;
+ /* The start of the info sec (including the __u32 record_size). */
+ void *info;
+
+ if (ext_sec->len == 0)
+ return 0;
+
+ if (ext_sec->off & 0x03) {
+ pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n",
+ ext_sec->desc);
+ return -EINVAL;
+ }
+
+ info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off;
+ info_left = ext_sec->len;
+
+ if (btf_ext->data + btf_ext->data_size < info + ext_sec->len) {
+ pr_debug("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n",
+ ext_sec->desc, ext_sec->off, ext_sec->len);
+ return -EINVAL;
+ }
+
+ /* At least a record size */
+ if (info_left < sizeof(__u32)) {
+ pr_debug(".BTF.ext %s record size not found\n", ext_sec->desc);
+ return -EINVAL;
+ }
+
+ /* The record size needs to meet the minimum standard */
+ record_size = *(__u32 *)info;
+ if (record_size < ext_sec->min_rec_size ||
+ record_size & 0x03) {
+ pr_debug("%s section in .BTF.ext has invalid record size %u\n",
+ ext_sec->desc, record_size);
+ return -EINVAL;
+ }
+
+ sinfo = info + sizeof(__u32);
+ info_left -= sizeof(__u32);
+
+ /* If no records, return failure now so .BTF.ext won't be used. */
+ if (!info_left) {
+ pr_debug("%s section in .BTF.ext has no records", ext_sec->desc);
+ return -EINVAL;
+ }
+
+ while (info_left) {
+ unsigned int sec_hdrlen = sizeof(struct btf_ext_info_sec);
+ __u64 total_record_size;
+ __u32 num_records;
+
+ if (info_left < sec_hdrlen) {
+ pr_debug("%s section header is not found in .BTF.ext\n",
+ ext_sec->desc);
+ return -EINVAL;
+ }
+
+ num_records = sinfo->num_info;
+ if (num_records == 0) {
+ pr_debug("%s section has incorrect num_records in .BTF.ext\n",
+ ext_sec->desc);
+ return -EINVAL;
+ }
+
+ total_record_size = sec_hdrlen +
+ (__u64)num_records * record_size;
+ if (info_left < total_record_size) {
+ pr_debug("%s section has incorrect num_records in .BTF.ext\n",
+ ext_sec->desc);
+ return -EINVAL;
+ }
+
+ info_left -= total_record_size;
+ sinfo = (void *)sinfo + total_record_size;
+ }
+
+ ext_info = ext_sec->ext_info;
+ ext_info->len = ext_sec->len - sizeof(__u32);
+ ext_info->rec_size = record_size;
+ ext_info->info = info + sizeof(__u32);
+
+ return 0;
+}
+
+static int btf_ext_setup_func_info(struct btf_ext *btf_ext)
+{
+ struct btf_ext_sec_setup_param param = {
+ .off = btf_ext->hdr->func_info_off,
+ .len = btf_ext->hdr->func_info_len,
+ .min_rec_size = sizeof(struct bpf_func_info_min),
+ .ext_info = &btf_ext->func_info,
+ .desc = "func_info"
+ };
+
+ return btf_ext_setup_info(btf_ext, &param);
+}
+
+static int btf_ext_setup_line_info(struct btf_ext *btf_ext)
+{
+ struct btf_ext_sec_setup_param param = {
+ .off = btf_ext->hdr->line_info_off,
+ .len = btf_ext->hdr->line_info_len,
+ .min_rec_size = sizeof(struct bpf_line_info_min),
+ .ext_info = &btf_ext->line_info,
+ .desc = "line_info",
+ };
+
+ return btf_ext_setup_info(btf_ext, &param);
+}
+
+static int btf_ext_setup_field_reloc(struct btf_ext *btf_ext)
+{
+ struct btf_ext_sec_setup_param param = {
+ .off = btf_ext->hdr->field_reloc_off,
+ .len = btf_ext->hdr->field_reloc_len,
+ .min_rec_size = sizeof(struct bpf_field_reloc),
+ .ext_info = &btf_ext->field_reloc_info,
+ .desc = "field_reloc",
+ };
+
+ return btf_ext_setup_info(btf_ext, &param);
+}
+
+static int btf_ext_parse_hdr(__u8 *data, __u32 data_size)
+{
+ const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
+
+ if (data_size < offsetofend(struct btf_ext_header, hdr_len) ||
+ data_size < hdr->hdr_len) {
+ pr_debug("BTF.ext header not found");
+ return -EINVAL;
+ }
+
+ if (hdr->magic != BTF_MAGIC) {
+ pr_debug("Invalid BTF.ext magic:%x\n", hdr->magic);
+ return -EINVAL;
+ }
+
+ if (hdr->version != BTF_VERSION) {
+ pr_debug("Unsupported BTF.ext version:%u\n", hdr->version);
+ return -ENOTSUP;
+ }
+
+ if (hdr->flags) {
+ pr_debug("Unsupported BTF.ext flags:%x\n", hdr->flags);
+ return -ENOTSUP;
+ }
+
+ if (data_size == hdr->hdr_len) {
+ pr_debug("BTF.ext has no data\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void btf_ext__free(struct btf_ext *btf_ext)
+{
+ if (!btf_ext)
+ return;
+ free(btf_ext->data);
+ free(btf_ext);
+}
+
+struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
+{
+ struct btf_ext *btf_ext;
+ int err;
+
+ err = btf_ext_parse_hdr(data, size);
+ if (err)
+ return ERR_PTR(err);
+
+ btf_ext = calloc(1, sizeof(struct btf_ext));
+ if (!btf_ext)
+ return ERR_PTR(-ENOMEM);
+
+ btf_ext->data_size = size;
+ btf_ext->data = malloc(size);
+ if (!btf_ext->data) {
+ err = -ENOMEM;
+ goto done;
+ }
+ memcpy(btf_ext->data, data, size);
+
+ if (btf_ext->hdr->hdr_len <
+ offsetofend(struct btf_ext_header, line_info_len))
+ goto done;
+ err = btf_ext_setup_func_info(btf_ext);
+ if (err)
+ goto done;
+
+ err = btf_ext_setup_line_info(btf_ext);
+ if (err)
+ goto done;
+
+ if (btf_ext->hdr->hdr_len <
+ offsetofend(struct btf_ext_header, field_reloc_len))
+ goto done;
+ err = btf_ext_setup_field_reloc(btf_ext);
+ if (err)
+ goto done;
+
+done:
+ if (err) {
+ btf_ext__free(btf_ext);
+ return ERR_PTR(err);
+ }
+
+ return btf_ext;
+}
+
+const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext, __u32 *size)
+{
+ *size = btf_ext->data_size;
+ return btf_ext->data;
+}
+
+static int btf_ext_reloc_info(const struct btf *btf,
+ const struct btf_ext_info *ext_info,
+ const char *sec_name, __u32 insns_cnt,
+ void **info, __u32 *cnt)
+{
+ __u32 sec_hdrlen = sizeof(struct btf_ext_info_sec);
+ __u32 i, record_size, existing_len, records_len;
+ struct btf_ext_info_sec *sinfo;
+ const char *info_sec_name;
+ __u64 remain_len;
+ void *data;
+
+ record_size = ext_info->rec_size;
+ sinfo = ext_info->info;
+ remain_len = ext_info->len;
+ while (remain_len > 0) {
+ records_len = sinfo->num_info * record_size;
+ info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off);
+ if (strcmp(info_sec_name, sec_name)) {
+ remain_len -= sec_hdrlen + records_len;
+ sinfo = (void *)sinfo + sec_hdrlen + records_len;
+ continue;
+ }
+
+ existing_len = (*cnt) * record_size;
+ data = realloc(*info, existing_len + records_len);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(data + existing_len, sinfo->data, records_len);
+ /* adjust insn_off only, the rest data will be passed
+ * to the kernel.
+ */
+ for (i = 0; i < sinfo->num_info; i++) {
+ __u32 *insn_off;
+
+ insn_off = data + existing_len + (i * record_size);
+ *insn_off = *insn_off / sizeof(struct bpf_insn) +
+ insns_cnt;
+ }
+ *info = data;
+ *cnt += sinfo->num_info;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int btf_ext__reloc_func_info(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const char *sec_name, __u32 insns_cnt,
+ void **func_info, __u32 *cnt)
+{
+ return btf_ext_reloc_info(btf, &btf_ext->func_info, sec_name,
+ insns_cnt, func_info, cnt);
+}
+
+int btf_ext__reloc_line_info(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const char *sec_name, __u32 insns_cnt,
+ void **line_info, __u32 *cnt)
+{
+ return btf_ext_reloc_info(btf, &btf_ext->line_info, sec_name,
+ insns_cnt, line_info, cnt);
+}
+
+__u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext)
+{
+ return btf_ext->func_info.rec_size;
+}
+
+__u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext)
+{
+ return btf_ext->line_info.rec_size;
+}
+
+struct btf_dedup;
+
+static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
+ const struct btf_dedup_opts *opts);
+static void btf_dedup_free(struct btf_dedup *d);
+static int btf_dedup_strings(struct btf_dedup *d);
+static int btf_dedup_prim_types(struct btf_dedup *d);
+static int btf_dedup_struct_types(struct btf_dedup *d);
+static int btf_dedup_ref_types(struct btf_dedup *d);
+static int btf_dedup_compact_types(struct btf_dedup *d);
+static int btf_dedup_remap_types(struct btf_dedup *d);
+
+/*
+ * Deduplicate BTF types and strings.
+ *
+ * BTF dedup algorithm takes as an input `struct btf` representing `.BTF` ELF
+ * section with all BTF type descriptors and string data. It overwrites that
+ * memory in-place with deduplicated types and strings without any loss of
+ * information. If optional `struct btf_ext` representing '.BTF.ext' ELF section
+ * is provided, all the strings referenced from .BTF.ext section are honored
+ * and updated to point to the right offsets after deduplication.
+ *
+ * If function returns with error, type/string data might be garbled and should
+ * be discarded.
+ *
+ * More verbose and detailed description of both problem btf_dedup is solving,
+ * as well as solution could be found at:
+ * https://facebookmicrosites.github.io/bpf/blog/2018/11/14/btf-enhancement.html
+ *
+ * Problem description and justification
+ * =====================================
+ *
+ * BTF type information is typically emitted either as a result of conversion
+ * from DWARF to BTF or directly by compiler. In both cases, each compilation
+ * unit contains information about a subset of all the types that are used
+ * in an application. These subsets are frequently overlapping and contain a lot
+ * of duplicated information when later concatenated together into a single
+ * binary. This algorithm ensures that each unique type is represented by single
+ * BTF type descriptor, greatly reducing resulting size of BTF data.
+ *
+ * Compilation unit isolation and subsequent duplication of data is not the only
+ * problem. The same type hierarchy (e.g., struct and all the type that struct
+ * references) in different compilation units can be represented in BTF to
+ * various degrees of completeness (or, rather, incompleteness) due to
+ * struct/union forward declarations.
+ *
+ * Let's take a look at an example, that we'll use to better understand the
+ * problem (and solution). Suppose we have two compilation units, each using
+ * same `struct S`, but each of them having incomplete type information about
+ * struct's fields:
+ *
+ * // CU #1:
+ * struct S;
+ * struct A {
+ * int a;
+ * struct A* self;
+ * struct S* parent;
+ * };
+ * struct B;
+ * struct S {
+ * struct A* a_ptr;
+ * struct B* b_ptr;
+ * };
+ *
+ * // CU #2:
+ * struct S;
+ * struct A;
+ * struct B {
+ * int b;
+ * struct B* self;
+ * struct S* parent;
+ * };
+ * struct S {
+ * struct A* a_ptr;
+ * struct B* b_ptr;
+ * };
+ *
+ * In case of CU #1, BTF data will know only that `struct B` exist (but no
+ * more), but will know the complete type information about `struct A`. While
+ * for CU #2, it will know full type information about `struct B`, but will
+ * only know about forward declaration of `struct A` (in BTF terms, it will
+ * have `BTF_KIND_FWD` type descriptor with name `B`).
+ *
+ * This compilation unit isolation means that it's possible that there is no
+ * single CU with complete type information describing structs `S`, `A`, and
+ * `B`. Also, we might get tons of duplicated and redundant type information.
+ *
+ * Additional complication we need to keep in mind comes from the fact that
+ * types, in general, can form graphs containing cycles, not just DAGs.
+ *
+ * While algorithm does deduplication, it also merges and resolves type
+ * information (unless disabled throught `struct btf_opts`), whenever possible.
+ * E.g., in the example above with two compilation units having partial type
+ * information for structs `A` and `B`, the output of algorithm will emit
+ * a single copy of each BTF type that describes structs `A`, `B`, and `S`
+ * (as well as type information for `int` and pointers), as if they were defined
+ * in a single compilation unit as:
+ *
+ * struct A {
+ * int a;
+ * struct A* self;
+ * struct S* parent;
+ * };
+ * struct B {
+ * int b;
+ * struct B* self;
+ * struct S* parent;
+ * };
+ * struct S {
+ * struct A* a_ptr;
+ * struct B* b_ptr;
+ * };
+ *
+ * Algorithm summary
+ * =================
+ *
+ * Algorithm completes its work in 6 separate passes:
+ *
+ * 1. Strings deduplication.
+ * 2. Primitive types deduplication (int, enum, fwd).
+ * 3. Struct/union types deduplication.
+ * 4. Reference types deduplication (pointers, typedefs, arrays, funcs, func
+ * protos, and const/volatile/restrict modifiers).
+ * 5. Types compaction.
+ * 6. Types remapping.
+ *
+ * Algorithm determines canonical type descriptor, which is a single
+ * representative type for each truly unique type. This canonical type is the
+ * one that will go into final deduplicated BTF type information. For
+ * struct/unions, it is also the type that algorithm will merge additional type
+ * information into (while resolving FWDs), as it discovers it from data in
+ * other CUs. Each input BTF type eventually gets either mapped to itself, if
+ * that type is canonical, or to some other type, if that type is equivalent
+ * and was chosen as canonical representative. This mapping is stored in
+ * `btf_dedup->map` array. This map is also used to record STRUCT/UNION that
+ * FWD type got resolved to.
+ *
+ * To facilitate fast discovery of canonical types, we also maintain canonical
+ * index (`btf_dedup->dedup_table`), which maps type descriptor's signature hash
+ * (i.e., hashed kind, name, size, fields, etc) into a list of canonical types
+ * that match that signature. With sufficiently good choice of type signature
+ * hashing function, we can limit number of canonical types for each unique type
+ * signature to a very small number, allowing to find canonical type for any
+ * duplicated type very quickly.
+ *
+ * Struct/union deduplication is the most critical part and algorithm for
+ * deduplicating structs/unions is described in greater details in comments for
+ * `btf_dedup_is_equiv` function.
+ */
+int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
+ const struct btf_dedup_opts *opts)
+{
+ struct btf_dedup *d = btf_dedup_new(btf, btf_ext, opts);
+ int err;
+
+ if (IS_ERR(d)) {
+ pr_debug("btf_dedup_new failed: %ld", PTR_ERR(d));
+ return -EINVAL;
+ }
+
+ err = btf_dedup_strings(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_strings failed:%d\n", err);
+ goto done;
+ }
+ err = btf_dedup_prim_types(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_prim_types failed:%d\n", err);
+ goto done;
+ }
+ err = btf_dedup_struct_types(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_struct_types failed:%d\n", err);
+ goto done;
+ }
+ err = btf_dedup_ref_types(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_ref_types failed:%d\n", err);
+ goto done;
+ }
+ err = btf_dedup_compact_types(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_compact_types failed:%d\n", err);
+ goto done;
+ }
+ err = btf_dedup_remap_types(d);
+ if (err < 0) {
+ pr_debug("btf_dedup_remap_types failed:%d\n", err);
+ goto done;
+ }
+
+done:
+ btf_dedup_free(d);
+ return err;
+}
+
+#define BTF_UNPROCESSED_ID ((__u32)-1)
+#define BTF_IN_PROGRESS_ID ((__u32)-2)
+
+struct btf_dedup {
+ /* .BTF section to be deduped in-place */
+ struct btf *btf;
+ /*
+ * Optional .BTF.ext section. When provided, any strings referenced
+ * from it will be taken into account when deduping strings
+ */
+ struct btf_ext *btf_ext;
+ /*
+ * This is a map from any type's signature hash to a list of possible
+ * canonical representative type candidates. Hash collisions are
+ * ignored, so even types of various kinds can share same list of
+ * candidates, which is fine because we rely on subsequent
+ * btf_xxx_equal() checks to authoritatively verify type equality.
+ */
+ struct hashmap *dedup_table;
+ /* Canonical types map */
+ __u32 *map;
+ /* Hypothetical mapping, used during type graph equivalence checks */
+ __u32 *hypot_map;
+ __u32 *hypot_list;
+ size_t hypot_cnt;
+ size_t hypot_cap;
+ /* Various option modifying behavior of algorithm */
+ struct btf_dedup_opts opts;
+};
+
+struct btf_str_ptr {
+ const char *str;
+ __u32 new_off;
+ bool used;
+};
+
+struct btf_str_ptrs {
+ struct btf_str_ptr *ptrs;
+ const char *data;
+ __u32 cnt;
+ __u32 cap;
+};
+
+static long hash_combine(long h, long value)
+{
+ return h * 31 + value;
+}
+
+#define for_each_dedup_cand(d, node, hash) \
+ hashmap__for_each_key_entry(d->dedup_table, node, (void *)hash)
+
+static int btf_dedup_table_add(struct btf_dedup *d, long hash, __u32 type_id)
+{
+ return hashmap__append(d->dedup_table,
+ (void *)hash, (void *)(long)type_id);
+}
+
+static int btf_dedup_hypot_map_add(struct btf_dedup *d,
+ __u32 from_id, __u32 to_id)
+{
+ if (d->hypot_cnt == d->hypot_cap) {
+ __u32 *new_list;
+
+ d->hypot_cap += max(16, d->hypot_cap / 2);
+ new_list = realloc(d->hypot_list, sizeof(__u32) * d->hypot_cap);
+ if (!new_list)
+ return -ENOMEM;
+ d->hypot_list = new_list;
+ }
+ d->hypot_list[d->hypot_cnt++] = from_id;
+ d->hypot_map[from_id] = to_id;
+ return 0;
+}
+
+static void btf_dedup_clear_hypot_map(struct btf_dedup *d)
+{
+ int i;
+
+ for (i = 0; i < d->hypot_cnt; i++)
+ d->hypot_map[d->hypot_list[i]] = BTF_UNPROCESSED_ID;
+ d->hypot_cnt = 0;
+}
+
+static void btf_dedup_free(struct btf_dedup *d)
+{
+ hashmap__free(d->dedup_table);
+ d->dedup_table = NULL;
+
+ free(d->map);
+ d->map = NULL;
+
+ free(d->hypot_map);
+ d->hypot_map = NULL;
+
+ free(d->hypot_list);
+ d->hypot_list = NULL;
+
+ free(d);
+}
+
+static size_t btf_dedup_identity_hash_fn(const void *key, void *ctx)
+{
+ return (size_t)key;
+}
+
+static size_t btf_dedup_collision_hash_fn(const void *key, void *ctx)
+{
+ return 0;
+}
+
+static bool btf_dedup_equal_fn(const void *k1, const void *k2, void *ctx)
+{
+ return k1 == k2;
+}
+
+static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
+ const struct btf_dedup_opts *opts)
+{
+ struct btf_dedup *d = calloc(1, sizeof(struct btf_dedup));
+ hashmap_hash_fn hash_fn = btf_dedup_identity_hash_fn;
+ int i, err = 0;
+
+ if (!d)
+ return ERR_PTR(-ENOMEM);
+
+ d->opts.dont_resolve_fwds = opts && opts->dont_resolve_fwds;
+ /* dedup_table_size is now used only to force collisions in tests */
+ if (opts && opts->dedup_table_size == 1)
+ hash_fn = btf_dedup_collision_hash_fn;
+
+ d->btf = btf;
+ d->btf_ext = btf_ext;
+
+ d->dedup_table = hashmap__new(hash_fn, btf_dedup_equal_fn, NULL);
+ if (IS_ERR(d->dedup_table)) {
+ err = PTR_ERR(d->dedup_table);
+ d->dedup_table = NULL;
+ goto done;
+ }
+
+ d->map = malloc(sizeof(__u32) * (1 + btf->nr_types));
+ if (!d->map) {
+ err = -ENOMEM;
+ goto done;
+ }
+ /* special BTF "void" type is made canonical immediately */
+ d->map[0] = 0;
+ for (i = 1; i <= btf->nr_types; i++) {
+ struct btf_type *t = d->btf->types[i];
+
+ /* VAR and DATASEC are never deduped and are self-canonical */
+ if (btf_is_var(t) || btf_is_datasec(t))
+ d->map[i] = i;
+ else
+ d->map[i] = BTF_UNPROCESSED_ID;
+ }
+
+ d->hypot_map = malloc(sizeof(__u32) * (1 + btf->nr_types));
+ if (!d->hypot_map) {
+ err = -ENOMEM;
+ goto done;
+ }
+ for (i = 0; i <= btf->nr_types; i++)
+ d->hypot_map[i] = BTF_UNPROCESSED_ID;
+
+done:
+ if (err) {
+ btf_dedup_free(d);
+ return ERR_PTR(err);
+ }
+
+ return d;
+}
+
+typedef int (*str_off_fn_t)(__u32 *str_off_ptr, void *ctx);
+
+/*
+ * Iterate over all possible places in .BTF and .BTF.ext that can reference
+ * string and pass pointer to it to a provided callback `fn`.
+ */
+static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
+{
+ void *line_data_cur, *line_data_end;
+ int i, j, r, rec_size;
+ struct btf_type *t;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ t = d->btf->types[i];
+ r = fn(&t->name_off, ctx);
+ if (r)
+ return r;
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+
+ for (j = 0; j < vlen; j++) {
+ r = fn(&m->name_off, ctx);
+ if (r)
+ return r;
+ m++;
+ }
+ break;
+ }
+ case BTF_KIND_ENUM: {
+ struct btf_enum *m = btf_enum(t);
+ __u16 vlen = btf_vlen(t);
+
+ for (j = 0; j < vlen; j++) {
+ r = fn(&m->name_off, ctx);
+ if (r)
+ return r;
+ m++;
+ }
+ break;
+ }
+ case BTF_KIND_FUNC_PROTO: {
+ struct btf_param *m = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+
+ for (j = 0; j < vlen; j++) {
+ r = fn(&m->name_off, ctx);
+ if (r)
+ return r;
+ m++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (!d->btf_ext)
+ return 0;
+
+ line_data_cur = d->btf_ext->line_info.info;
+ line_data_end = d->btf_ext->line_info.info + d->btf_ext->line_info.len;
+ rec_size = d->btf_ext->line_info.rec_size;
+
+ while (line_data_cur < line_data_end) {
+ struct btf_ext_info_sec *sec = line_data_cur;
+ struct bpf_line_info_min *line_info;
+ __u32 num_info = sec->num_info;
+
+ r = fn(&sec->sec_name_off, ctx);
+ if (r)
+ return r;
+
+ line_data_cur += sizeof(struct btf_ext_info_sec);
+ for (i = 0; i < num_info; i++) {
+ line_info = line_data_cur;
+ r = fn(&line_info->file_name_off, ctx);
+ if (r)
+ return r;
+ r = fn(&line_info->line_off, ctx);
+ if (r)
+ return r;
+ line_data_cur += rec_size;
+ }
+ }
+
+ return 0;
+}
+
+static int str_sort_by_content(const void *a1, const void *a2)
+{
+ const struct btf_str_ptr *p1 = a1;
+ const struct btf_str_ptr *p2 = a2;
+
+ return strcmp(p1->str, p2->str);
+}
+
+static int str_sort_by_offset(const void *a1, const void *a2)
+{
+ const struct btf_str_ptr *p1 = a1;
+ const struct btf_str_ptr *p2 = a2;
+
+ if (p1->str != p2->str)
+ return p1->str < p2->str ? -1 : 1;
+ return 0;
+}
+
+static int btf_dedup_str_ptr_cmp(const void *str_ptr, const void *pelem)
+{
+ const struct btf_str_ptr *p = pelem;
+
+ if (str_ptr != p->str)
+ return (const char *)str_ptr < p->str ? -1 : 1;
+ return 0;
+}
+
+static int btf_str_mark_as_used(__u32 *str_off_ptr, void *ctx)
+{
+ struct btf_str_ptrs *strs;
+ struct btf_str_ptr *s;
+
+ if (*str_off_ptr == 0)
+ return 0;
+
+ strs = ctx;
+ s = bsearch(strs->data + *str_off_ptr, strs->ptrs, strs->cnt,
+ sizeof(struct btf_str_ptr), btf_dedup_str_ptr_cmp);
+ if (!s)
+ return -EINVAL;
+ s->used = true;
+ return 0;
+}
+
+static int btf_str_remap_offset(__u32 *str_off_ptr, void *ctx)
+{
+ struct btf_str_ptrs *strs;
+ struct btf_str_ptr *s;
+
+ if (*str_off_ptr == 0)
+ return 0;
+
+ strs = ctx;
+ s = bsearch(strs->data + *str_off_ptr, strs->ptrs, strs->cnt,
+ sizeof(struct btf_str_ptr), btf_dedup_str_ptr_cmp);
+ if (!s)
+ return -EINVAL;
+ *str_off_ptr = s->new_off;
+ return 0;
+}
+
+/*
+ * Dedup string and filter out those that are not referenced from either .BTF
+ * or .BTF.ext (if provided) sections.
+ *
+ * This is done by building index of all strings in BTF's string section,
+ * then iterating over all entities that can reference strings (e.g., type
+ * names, struct field names, .BTF.ext line info, etc) and marking corresponding
+ * strings as used. After that all used strings are deduped and compacted into
+ * sequential blob of memory and new offsets are calculated. Then all the string
+ * references are iterated again and rewritten using new offsets.
+ */
+static int btf_dedup_strings(struct btf_dedup *d)
+{
+ const struct btf_header *hdr = d->btf->hdr;
+ char *start = (char *)d->btf->nohdr_data + hdr->str_off;
+ char *end = start + d->btf->hdr->str_len;
+ char *p = start, *tmp_strs = NULL;
+ struct btf_str_ptrs strs = {
+ .cnt = 0,
+ .cap = 0,
+ .ptrs = NULL,
+ .data = start,
+ };
+ int i, j, err = 0, grp_idx;
+ bool grp_used;
+
+ /* build index of all strings */
+ while (p < end) {
+ if (strs.cnt + 1 > strs.cap) {
+ struct btf_str_ptr *new_ptrs;
+
+ strs.cap += max(strs.cnt / 2, 16);
+ new_ptrs = realloc(strs.ptrs,
+ sizeof(strs.ptrs[0]) * strs.cap);
+ if (!new_ptrs) {
+ err = -ENOMEM;
+ goto done;
+ }
+ strs.ptrs = new_ptrs;
+ }
+
+ strs.ptrs[strs.cnt].str = p;
+ strs.ptrs[strs.cnt].used = false;
+
+ p += strlen(p) + 1;
+ strs.cnt++;
+ }
+
+ /* temporary storage for deduplicated strings */
+ tmp_strs = malloc(d->btf->hdr->str_len);
+ if (!tmp_strs) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ /* mark all used strings */
+ strs.ptrs[0].used = true;
+ err = btf_for_each_str_off(d, btf_str_mark_as_used, &strs);
+ if (err)
+ goto done;
+
+ /* sort strings by context, so that we can identify duplicates */
+ qsort(strs.ptrs, strs.cnt, sizeof(strs.ptrs[0]), str_sort_by_content);
+
+ /*
+ * iterate groups of equal strings and if any instance in a group was
+ * referenced, emit single instance and remember new offset
+ */
+ p = tmp_strs;
+ grp_idx = 0;
+ grp_used = strs.ptrs[0].used;
+ /* iterate past end to avoid code duplication after loop */
+ for (i = 1; i <= strs.cnt; i++) {
+ /*
+ * when i == strs.cnt, we want to skip string comparison and go
+ * straight to handling last group of strings (otherwise we'd
+ * need to handle last group after the loop w/ duplicated code)
+ */
+ if (i < strs.cnt &&
+ !strcmp(strs.ptrs[i].str, strs.ptrs[grp_idx].str)) {
+ grp_used = grp_used || strs.ptrs[i].used;
+ continue;
+ }
+
+ /*
+ * this check would have been required after the loop to handle
+ * last group of strings, but due to <= condition in a loop
+ * we avoid that duplication
+ */
+ if (grp_used) {
+ int new_off = p - tmp_strs;
+ __u32 len = strlen(strs.ptrs[grp_idx].str);
+
+ memmove(p, strs.ptrs[grp_idx].str, len + 1);
+ for (j = grp_idx; j < i; j++)
+ strs.ptrs[j].new_off = new_off;
+ p += len + 1;
+ }
+
+ if (i < strs.cnt) {
+ grp_idx = i;
+ grp_used = strs.ptrs[i].used;
+ }
+ }
+
+ /* replace original strings with deduped ones */
+ d->btf->hdr->str_len = p - tmp_strs;
+ memmove(start, tmp_strs, d->btf->hdr->str_len);
+ end = start + d->btf->hdr->str_len;
+
+ /* restore original order for further binary search lookups */
+ qsort(strs.ptrs, strs.cnt, sizeof(strs.ptrs[0]), str_sort_by_offset);
+
+ /* remap string offsets */
+ err = btf_for_each_str_off(d, btf_str_remap_offset, &strs);
+ if (err)
+ goto done;
+
+ d->btf->hdr->str_len = end - start;
+
+done:
+ free(tmp_strs);
+ free(strs.ptrs);
+ return err;
+}
+
+static long btf_hash_common(struct btf_type *t)
+{
+ long h;
+
+ h = hash_combine(0, t->name_off);
+ h = hash_combine(h, t->info);
+ h = hash_combine(h, t->size);
+ return h;
+}
+
+static bool btf_equal_common(struct btf_type *t1, struct btf_type *t2)
+{
+ return t1->name_off == t2->name_off &&
+ t1->info == t2->info &&
+ t1->size == t2->size;
+}
+
+/* Calculate type signature hash of INT. */
+static long btf_hash_int(struct btf_type *t)
+{
+ __u32 info = *(__u32 *)(t + 1);
+ long h;
+
+ h = btf_hash_common(t);
+ h = hash_combine(h, info);
+ return h;
+}
+
+/* Check structural equality of two INTs. */
+static bool btf_equal_int(struct btf_type *t1, struct btf_type *t2)
+{
+ __u32 info1, info2;
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+ info1 = *(__u32 *)(t1 + 1);
+ info2 = *(__u32 *)(t2 + 1);
+ return info1 == info2;
+}
+
+/* Calculate type signature hash of ENUM. */
+static long btf_hash_enum(struct btf_type *t)
+{
+ long h;
+
+ /* don't hash vlen and enum members to support enum fwd resolving */
+ h = hash_combine(0, t->name_off);
+ h = hash_combine(h, t->info & ~0xffff);
+ h = hash_combine(h, t->size);
+ return h;
+}
+
+/* Check structural equality of two ENUMs. */
+static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
+{
+ const struct btf_enum *m1, *m2;
+ __u16 vlen;
+ int i;
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+
+ vlen = btf_vlen(t1);
+ m1 = btf_enum(t1);
+ m2 = btf_enum(t2);
+ for (i = 0; i < vlen; i++) {
+ if (m1->name_off != m2->name_off || m1->val != m2->val)
+ return false;
+ m1++;
+ m2++;
+ }
+ return true;
+}
+
+static inline bool btf_is_enum_fwd(struct btf_type *t)
+{
+ return btf_is_enum(t) && btf_vlen(t) == 0;
+}
+
+static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
+{
+ if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2))
+ return btf_equal_enum(t1, t2);
+ /* ignore vlen when comparing */
+ return t1->name_off == t2->name_off &&
+ (t1->info & ~0xffff) == (t2->info & ~0xffff) &&
+ t1->size == t2->size;
+}
+
+/*
+ * Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
+ * as referenced type IDs equivalence is established separately during type
+ * graph equivalence check algorithm.
+ */
+static long btf_hash_struct(struct btf_type *t)
+{
+ const struct btf_member *member = btf_members(t);
+ __u32 vlen = btf_vlen(t);
+ long h = btf_hash_common(t);
+ int i;
+
+ for (i = 0; i < vlen; i++) {
+ h = hash_combine(h, member->name_off);
+ h = hash_combine(h, member->offset);
+ /* no hashing of referenced type ID, it can be unresolved yet */
+ member++;
+ }
+ return h;
+}
+
+/*
+ * Check structural compatibility of two FUNC_PROTOs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2)
+{
+ const struct btf_member *m1, *m2;
+ __u16 vlen;
+ int i;
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+
+ vlen = btf_vlen(t1);
+ m1 = btf_members(t1);
+ m2 = btf_members(t2);
+ for (i = 0; i < vlen; i++) {
+ if (m1->name_off != m2->name_off || m1->offset != m2->offset)
+ return false;
+ m1++;
+ m2++;
+ }
+ return true;
+}
+
+/*
+ * Calculate type signature hash of ARRAY, including referenced type IDs,
+ * under assumption that they were already resolved to canonical type IDs and
+ * are not going to change.
+ */
+static long btf_hash_array(struct btf_type *t)
+{
+ const struct btf_array *info = btf_array(t);
+ long h = btf_hash_common(t);
+
+ h = hash_combine(h, info->type);
+ h = hash_combine(h, info->index_type);
+ h = hash_combine(h, info->nelems);
+ return h;
+}
+
+/*
+ * Check exact equality of two ARRAYs, taking into account referenced
+ * type IDs, under assumption that they were already resolved to canonical
+ * type IDs and are not going to change.
+ * This function is called during reference types deduplication to compare
+ * ARRAY to potential canonical representative.
+ */
+static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
+{
+ const struct btf_array *info1, *info2;
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+
+ info1 = btf_array(t1);
+ info2 = btf_array(t2);
+ return info1->type == info2->type &&
+ info1->index_type == info2->index_type &&
+ info1->nelems == info2->nelems;
+}
+
+/*
+ * Check structural compatibility of two ARRAYs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
+{
+ if (!btf_equal_common(t1, t2))
+ return false;
+
+ return btf_array(t1)->nelems == btf_array(t2)->nelems;
+}
+
+/*
+ * Calculate type signature hash of FUNC_PROTO, including referenced type IDs,
+ * under assumption that they were already resolved to canonical type IDs and
+ * are not going to change.
+ */
+static long btf_hash_fnproto(struct btf_type *t)
+{
+ const struct btf_param *member = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ long h = btf_hash_common(t);
+ int i;
+
+ for (i = 0; i < vlen; i++) {
+ h = hash_combine(h, member->name_off);
+ h = hash_combine(h, member->type);
+ member++;
+ }
+ return h;
+}
+
+/*
+ * Check exact equality of two FUNC_PROTOs, taking into account referenced
+ * type IDs, under assumption that they were already resolved to canonical
+ * type IDs and are not going to change.
+ * This function is called during reference types deduplication to compare
+ * FUNC_PROTO to potential canonical representative.
+ */
+static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
+{
+ const struct btf_param *m1, *m2;
+ __u16 vlen;
+ int i;
+
+ if (!btf_equal_common(t1, t2))
+ return false;
+
+ vlen = btf_vlen(t1);
+ m1 = btf_params(t1);
+ m2 = btf_params(t2);
+ for (i = 0; i < vlen; i++) {
+ if (m1->name_off != m2->name_off || m1->type != m2->type)
+ return false;
+ m1++;
+ m2++;
+ }
+ return true;
+}
+
+/*
+ * Check structural compatibility of two FUNC_PROTOs, ignoring referenced type
+ * IDs. This check is performed during type graph equivalence check and
+ * referenced types equivalence is checked separately.
+ */
+static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
+{
+ const struct btf_param *m1, *m2;
+ __u16 vlen;
+ int i;
+
+ /* skip return type ID */
+ if (t1->name_off != t2->name_off || t1->info != t2->info)
+ return false;
+
+ vlen = btf_vlen(t1);
+ m1 = btf_params(t1);
+ m2 = btf_params(t2);
+ for (i = 0; i < vlen; i++) {
+ if (m1->name_off != m2->name_off)
+ return false;
+ m1++;
+ m2++;
+ }
+ return true;
+}
+
+/*
+ * Deduplicate primitive types, that can't reference other types, by calculating
+ * their type signature hash and comparing them with any possible canonical
+ * candidate. If no canonical candidate matches, type itself is marked as
+ * canonical and is added into `btf_dedup->dedup_table` as another candidate.
+ */
+static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
+{
+ struct btf_type *t = d->btf->types[type_id];
+ struct hashmap_entry *hash_entry;
+ struct btf_type *cand;
+ /* if we don't find equivalent type, then we are canonical */
+ __u32 new_id = type_id;
+ __u32 cand_id;
+ long h;
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_ARRAY:
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ case BTF_KIND_FUNC:
+ case BTF_KIND_FUNC_PROTO:
+ case BTF_KIND_VAR:
+ case BTF_KIND_DATASEC:
+ return 0;
+
+ case BTF_KIND_INT:
+ h = btf_hash_int(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_int(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
+ case BTF_KIND_ENUM:
+ h = btf_hash_enum(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_enum(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ if (d->opts.dont_resolve_fwds)
+ continue;
+ if (btf_compat_enum(t, cand)) {
+ if (btf_is_enum_fwd(t)) {
+ /* resolve fwd to full enum */
+ new_id = cand_id;
+ break;
+ }
+ /* resolve canonical enum fwd to full enum */
+ d->map[cand_id] = type_id;
+ }
+ }
+ break;
+
+ case BTF_KIND_FWD:
+ h = btf_hash_common(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_common(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ d->map[type_id] = new_id;
+ if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int btf_dedup_prim_types(struct btf_dedup *d)
+{
+ int i, err;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ err = btf_dedup_prim_type(d, i);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * Check whether type is already mapped into canonical one (could be to itself).
+ */
+static inline bool is_type_mapped(struct btf_dedup *d, uint32_t type_id)
+{
+ return d->map[type_id] <= BTF_MAX_NR_TYPES;
+}
+
+/*
+ * Resolve type ID into its canonical type ID, if any; otherwise return original
+ * type ID. If type is FWD and is resolved into STRUCT/UNION already, follow
+ * STRUCT/UNION link and resolve it into canonical type ID as well.
+ */
+static inline __u32 resolve_type_id(struct btf_dedup *d, __u32 type_id)
+{
+ while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
+ type_id = d->map[type_id];
+ return type_id;
+}
+
+/*
+ * Resolve FWD to underlying STRUCT/UNION, if any; otherwise return original
+ * type ID.
+ */
+static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
+{
+ __u32 orig_type_id = type_id;
+
+ if (!btf_is_fwd(d->btf->types[type_id]))
+ return type_id;
+
+ while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
+ type_id = d->map[type_id];
+
+ if (!btf_is_fwd(d->btf->types[type_id]))
+ return type_id;
+
+ return orig_type_id;
+}
+
+
+static inline __u16 btf_fwd_kind(struct btf_type *t)
+{
+ return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
+}
+
+/*
+ * Check equivalence of BTF type graph formed by candidate struct/union (we'll
+ * call it "candidate graph" in this description for brevity) to a type graph
+ * formed by (potential) canonical struct/union ("canonical graph" for brevity
+ * here, though keep in mind that not all types in canonical graph are
+ * necessarily canonical representatives themselves, some of them might be
+ * duplicates or its uniqueness might not have been established yet).
+ * Returns:
+ * - >0, if type graphs are equivalent;
+ * - 0, if not equivalent;
+ * - <0, on error.
+ *
+ * Algorithm performs side-by-side DFS traversal of both type graphs and checks
+ * equivalence of BTF types at each step. If at any point BTF types in candidate
+ * and canonical graphs are not compatible structurally, whole graphs are
+ * incompatible. If types are structurally equivalent (i.e., all information
+ * except referenced type IDs is exactly the same), a mapping from `canon_id` to
+ * a `cand_id` is recored in hypothetical mapping (`btf_dedup->hypot_map`).
+ * If a type references other types, then those referenced types are checked
+ * for equivalence recursively.
+ *
+ * During DFS traversal, if we find that for current `canon_id` type we
+ * already have some mapping in hypothetical map, we check for two possible
+ * situations:
+ * - `canon_id` is mapped to exactly the same type as `cand_id`. This will
+ * happen when type graphs have cycles. In this case we assume those two
+ * types are equivalent.
+ * - `canon_id` is mapped to different type. This is contradiction in our
+ * hypothetical mapping, because same graph in canonical graph corresponds
+ * to two different types in candidate graph, which for equivalent type
+ * graphs shouldn't happen. This condition terminates equivalence check
+ * with negative result.
+ *
+ * If type graphs traversal exhausts types to check and find no contradiction,
+ * then type graphs are equivalent.
+ *
+ * When checking types for equivalence, there is one special case: FWD types.
+ * If FWD type resolution is allowed and one of the types (either from canonical
+ * or candidate graph) is FWD and other is STRUCT/UNION (depending on FWD's kind
+ * flag) and their names match, hypothetical mapping is updated to point from
+ * FWD to STRUCT/UNION. If graphs will be determined as equivalent successfully,
+ * this mapping will be used to record FWD -> STRUCT/UNION mapping permanently.
+ *
+ * Technically, this could lead to incorrect FWD to STRUCT/UNION resolution,
+ * if there are two exactly named (or anonymous) structs/unions that are
+ * compatible structurally, one of which has FWD field, while other is concrete
+ * STRUCT/UNION, but according to C sources they are different structs/unions
+ * that are referencing different types with the same name. This is extremely
+ * unlikely to happen, but btf_dedup API allows to disable FWD resolution if
+ * this logic is causing problems.
+ *
+ * Doing FWD resolution means that both candidate and/or canonical graphs can
+ * consists of portions of the graph that come from multiple compilation units.
+ * This is due to the fact that types within single compilation unit are always
+ * deduplicated and FWDs are already resolved, if referenced struct/union
+ * definiton is available. So, if we had unresolved FWD and found corresponding
+ * STRUCT/UNION, they will be from different compilation units. This
+ * consequently means that when we "link" FWD to corresponding STRUCT/UNION,
+ * type graph will likely have at least two different BTF types that describe
+ * same type (e.g., most probably there will be two different BTF types for the
+ * same 'int' primitive type) and could even have "overlapping" parts of type
+ * graph that describe same subset of types.
+ *
+ * This in turn means that our assumption that each type in canonical graph
+ * must correspond to exactly one type in candidate graph might not hold
+ * anymore and will make it harder to detect contradictions using hypothetical
+ * map. To handle this problem, we allow to follow FWD -> STRUCT/UNION
+ * resolution only in canonical graph. FWDs in candidate graphs are never
+ * resolved. To see why it's OK, let's check all possible situations w.r.t. FWDs
+ * that can occur:
+ * - Both types in canonical and candidate graphs are FWDs. If they are
+ * structurally equivalent, then they can either be both resolved to the
+ * same STRUCT/UNION or not resolved at all. In both cases they are
+ * equivalent and there is no need to resolve FWD on candidate side.
+ * - Both types in canonical and candidate graphs are concrete STRUCT/UNION,
+ * so nothing to resolve as well, algorithm will check equivalence anyway.
+ * - Type in canonical graph is FWD, while type in candidate is concrete
+ * STRUCT/UNION. In this case candidate graph comes from single compilation
+ * unit, so there is exactly one BTF type for each unique C type. After
+ * resolving FWD into STRUCT/UNION, there might be more than one BTF type
+ * in canonical graph mapping to single BTF type in candidate graph, but
+ * because hypothetical mapping maps from canonical to candidate types, it's
+ * alright, and we still maintain the property of having single `canon_id`
+ * mapping to single `cand_id` (there could be two different `canon_id`
+ * mapped to the same `cand_id`, but it's not contradictory).
+ * - Type in canonical graph is concrete STRUCT/UNION, while type in candidate
+ * graph is FWD. In this case we are just going to check compatibility of
+ * STRUCT/UNION and corresponding FWD, and if they are compatible, we'll
+ * assume that whatever STRUCT/UNION FWD resolves to must be equivalent to
+ * a concrete STRUCT/UNION from canonical graph. If the rest of type graphs
+ * turn out equivalent, we'll re-resolve FWD to concrete STRUCT/UNION from
+ * canonical graph.
+ */
+static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
+ __u32 canon_id)
+{
+ struct btf_type *cand_type;
+ struct btf_type *canon_type;
+ __u32 hypot_type_id;
+ __u16 cand_kind;
+ __u16 canon_kind;
+ int i, eq;
+
+ /* if both resolve to the same canonical, they must be equivalent */
+ if (resolve_type_id(d, cand_id) == resolve_type_id(d, canon_id))
+ return 1;
+
+ canon_id = resolve_fwd_id(d, canon_id);
+
+ hypot_type_id = d->hypot_map[canon_id];
+ if (hypot_type_id <= BTF_MAX_NR_TYPES)
+ return hypot_type_id == cand_id;
+
+ if (btf_dedup_hypot_map_add(d, canon_id, cand_id))
+ return -ENOMEM;
+
+ cand_type = d->btf->types[cand_id];
+ canon_type = d->btf->types[canon_id];
+ cand_kind = btf_kind(cand_type);
+ canon_kind = btf_kind(canon_type);
+
+ if (cand_type->name_off != canon_type->name_off)
+ return 0;
+
+ /* FWD <--> STRUCT/UNION equivalence check, if enabled */
+ if (!d->opts.dont_resolve_fwds
+ && (cand_kind == BTF_KIND_FWD || canon_kind == BTF_KIND_FWD)
+ && cand_kind != canon_kind) {
+ __u16 real_kind;
+ __u16 fwd_kind;
+
+ if (cand_kind == BTF_KIND_FWD) {
+ real_kind = canon_kind;
+ fwd_kind = btf_fwd_kind(cand_type);
+ } else {
+ real_kind = cand_kind;
+ fwd_kind = btf_fwd_kind(canon_type);
+ }
+ return fwd_kind == real_kind;
+ }
+
+ if (cand_kind != canon_kind)
+ return 0;
+
+ switch (cand_kind) {
+ case BTF_KIND_INT:
+ return btf_equal_int(cand_type, canon_type);
+
+ case BTF_KIND_ENUM:
+ if (d->opts.dont_resolve_fwds)
+ return btf_equal_enum(cand_type, canon_type);
+ else
+ return btf_compat_enum(cand_type, canon_type);
+
+ case BTF_KIND_FWD:
+ return btf_equal_common(cand_type, canon_type);
+
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ if (cand_type->info != canon_type->info)
+ return 0;
+ return btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
+
+ case BTF_KIND_ARRAY: {
+ const struct btf_array *cand_arr, *canon_arr;
+
+ if (!btf_compat_array(cand_type, canon_type))
+ return 0;
+ cand_arr = btf_array(cand_type);
+ canon_arr = btf_array(canon_type);
+ eq = btf_dedup_is_equiv(d,
+ cand_arr->index_type, canon_arr->index_type);
+ if (eq <= 0)
+ return eq;
+ return btf_dedup_is_equiv(d, cand_arr->type, canon_arr->type);
+ }
+
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *cand_m, *canon_m;
+ __u16 vlen;
+
+ if (!btf_shallow_equal_struct(cand_type, canon_type))
+ return 0;
+ vlen = btf_vlen(cand_type);
+ cand_m = btf_members(cand_type);
+ canon_m = btf_members(canon_type);
+ for (i = 0; i < vlen; i++) {
+ eq = btf_dedup_is_equiv(d, cand_m->type, canon_m->type);
+ if (eq <= 0)
+ return eq;
+ cand_m++;
+ canon_m++;
+ }
+
+ return 1;
+ }
+
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *cand_p, *canon_p;
+ __u16 vlen;
+
+ if (!btf_compat_fnproto(cand_type, canon_type))
+ return 0;
+ eq = btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
+ if (eq <= 0)
+ return eq;
+ vlen = btf_vlen(cand_type);
+ cand_p = btf_params(cand_type);
+ canon_p = btf_params(canon_type);
+ for (i = 0; i < vlen; i++) {
+ eq = btf_dedup_is_equiv(d, cand_p->type, canon_p->type);
+ if (eq <= 0)
+ return eq;
+ cand_p++;
+ canon_p++;
+ }
+ return 1;
+ }
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Use hypothetical mapping, produced by successful type graph equivalence
+ * check, to augment existing struct/union canonical mapping, where possible.
+ *
+ * If BTF_KIND_FWD resolution is allowed, this mapping is also used to record
+ * FWD -> STRUCT/UNION correspondence as well. FWD resolution is bidirectional:
+ * it doesn't matter if FWD type was part of canonical graph or candidate one,
+ * we are recording the mapping anyway. As opposed to carefulness required
+ * for struct/union correspondence mapping (described below), for FWD resolution
+ * it's not important, as by the time that FWD type (reference type) will be
+ * deduplicated all structs/unions will be deduped already anyway.
+ *
+ * Recording STRUCT/UNION mapping is purely a performance optimization and is
+ * not required for correctness. It needs to be done carefully to ensure that
+ * struct/union from candidate's type graph is not mapped into corresponding
+ * struct/union from canonical type graph that itself hasn't been resolved into
+ * canonical representative. The only guarantee we have is that canonical
+ * struct/union was determined as canonical and that won't change. But any
+ * types referenced through that struct/union fields could have been not yet
+ * resolved, so in case like that it's too early to establish any kind of
+ * correspondence between structs/unions.
+ *
+ * No canonical correspondence is derived for primitive types (they are already
+ * deduplicated completely already anyway) or reference types (they rely on
+ * stability of struct/union canonical relationship for equivalence checks).
+ */
+static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
+{
+ __u32 cand_type_id, targ_type_id;
+ __u16 t_kind, c_kind;
+ __u32 t_id, c_id;
+ int i;
+
+ for (i = 0; i < d->hypot_cnt; i++) {
+ cand_type_id = d->hypot_list[i];
+ targ_type_id = d->hypot_map[cand_type_id];
+ t_id = resolve_type_id(d, targ_type_id);
+ c_id = resolve_type_id(d, cand_type_id);
+ t_kind = btf_kind(d->btf->types[t_id]);
+ c_kind = btf_kind(d->btf->types[c_id]);
+ /*
+ * Resolve FWD into STRUCT/UNION.
+ * It's ok to resolve FWD into STRUCT/UNION that's not yet
+ * mapped to canonical representative (as opposed to
+ * STRUCT/UNION <--> STRUCT/UNION mapping logic below), because
+ * eventually that struct is going to be mapped and all resolved
+ * FWDs will automatically resolve to correct canonical
+ * representative. This will happen before ref type deduping,
+ * which critically depends on stability of these mapping. This
+ * stability is not a requirement for STRUCT/UNION equivalence
+ * checks, though.
+ */
+ if (t_kind != BTF_KIND_FWD && c_kind == BTF_KIND_FWD)
+ d->map[c_id] = t_id;
+ else if (t_kind == BTF_KIND_FWD && c_kind != BTF_KIND_FWD)
+ d->map[t_id] = c_id;
+
+ if ((t_kind == BTF_KIND_STRUCT || t_kind == BTF_KIND_UNION) &&
+ c_kind != BTF_KIND_FWD &&
+ is_type_mapped(d, c_id) &&
+ !is_type_mapped(d, t_id)) {
+ /*
+ * as a perf optimization, we can map struct/union
+ * that's part of type graph we just verified for
+ * equivalence. We can do that for struct/union that has
+ * canonical representative only, though.
+ */
+ d->map[t_id] = c_id;
+ }
+ }
+}
+
+/*
+ * Deduplicate struct/union types.
+ *
+ * For each struct/union type its type signature hash is calculated, taking
+ * into account type's name, size, number, order and names of fields, but
+ * ignoring type ID's referenced from fields, because they might not be deduped
+ * completely until after reference types deduplication phase. This type hash
+ * is used to iterate over all potential canonical types, sharing same hash.
+ * For each canonical candidate we check whether type graphs that they form
+ * (through referenced types in fields and so on) are equivalent using algorithm
+ * implemented in `btf_dedup_is_equiv`. If such equivalence is found and
+ * BTF_KIND_FWD resolution is allowed, then hypothetical mapping
+ * (btf_dedup->hypot_map) produced by aforementioned type graph equivalence
+ * algorithm is used to record FWD -> STRUCT/UNION mapping. It's also used to
+ * potentially map other structs/unions to their canonical representatives,
+ * if such relationship hasn't yet been established. This speeds up algorithm
+ * by eliminating some of the duplicate work.
+ *
+ * If no matching canonical representative was found, struct/union is marked
+ * as canonical for itself and is added into btf_dedup->dedup_table hash map
+ * for further look ups.
+ */
+static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
+{
+ struct btf_type *cand_type, *t;
+ struct hashmap_entry *hash_entry;
+ /* if we don't find equivalent type, then we are canonical */
+ __u32 new_id = type_id;
+ __u16 kind;
+ long h;
+
+ /* already deduped or is in process of deduping (loop detected) */
+ if (d->map[type_id] <= BTF_MAX_NR_TYPES)
+ return 0;
+
+ t = d->btf->types[type_id];
+ kind = btf_kind(t);
+
+ if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
+ return 0;
+
+ h = btf_hash_struct(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ __u32 cand_id = (__u32)(long)hash_entry->value;
+ int eq;
+
+ /*
+ * Even though btf_dedup_is_equiv() checks for
+ * btf_shallow_equal_struct() internally when checking two
+ * structs (unions) for equivalence, we need to guard here
+ * from picking matching FWD type as a dedup candidate.
+ * This can happen due to hash collision. In such case just
+ * relying on btf_dedup_is_equiv() would lead to potentially
+ * creating a loop (FWD -> STRUCT and STRUCT -> FWD), because
+ * FWD and compatible STRUCT/UNION are considered equivalent.
+ */
+ cand_type = d->btf->types[cand_id];
+ if (!btf_shallow_equal_struct(t, cand_type))
+ continue;
+
+ btf_dedup_clear_hypot_map(d);
+ eq = btf_dedup_is_equiv(d, type_id, cand_id);
+ if (eq < 0)
+ return eq;
+ if (!eq)
+ continue;
+ new_id = cand_id;
+ btf_dedup_merge_hypot_map(d);
+ break;
+ }
+
+ d->map[type_id] = new_id;
+ if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int btf_dedup_struct_types(struct btf_dedup *d)
+{
+ int i, err;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ err = btf_dedup_struct_type(d, i);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * Deduplicate reference type.
+ *
+ * Once all primitive and struct/union types got deduplicated, we can easily
+ * deduplicate all other (reference) BTF types. This is done in two steps:
+ *
+ * 1. Resolve all referenced type IDs into their canonical type IDs. This
+ * resolution can be done either immediately for primitive or struct/union types
+ * (because they were deduped in previous two phases) or recursively for
+ * reference types. Recursion will always terminate at either primitive or
+ * struct/union type, at which point we can "unwind" chain of reference types
+ * one by one. There is no danger of encountering cycles because in C type
+ * system the only way to form type cycle is through struct/union, so any chain
+ * of reference types, even those taking part in a type cycle, will inevitably
+ * reach struct/union at some point.
+ *
+ * 2. Once all referenced type IDs are resolved into canonical ones, BTF type
+ * becomes "stable", in the sense that no further deduplication will cause
+ * any changes to it. With that, it's now possible to calculate type's signature
+ * hash (this time taking into account referenced type IDs) and loop over all
+ * potential canonical representatives. If no match was found, current type
+ * will become canonical representative of itself and will be added into
+ * btf_dedup->dedup_table as another possible canonical representative.
+ */
+static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
+{
+ struct hashmap_entry *hash_entry;
+ __u32 new_id = type_id, cand_id;
+ struct btf_type *t, *cand;
+ /* if we don't find equivalent type, then we are representative type */
+ int ref_type_id;
+ long h;
+
+ if (d->map[type_id] == BTF_IN_PROGRESS_ID)
+ return -ELOOP;
+ if (d->map[type_id] <= BTF_MAX_NR_TYPES)
+ return resolve_type_id(d, type_id);
+
+ t = d->btf->types[type_id];
+ d->map[type_id] = BTF_IN_PROGRESS_ID;
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ ref_type_id = btf_dedup_ref_type(d, t->type);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ t->type = ref_type_id;
+
+ h = btf_hash_common(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_common(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+
+ case BTF_KIND_ARRAY: {
+ struct btf_array *info = btf_array(t);
+
+ ref_type_id = btf_dedup_ref_type(d, info->type);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ info->type = ref_type_id;
+
+ ref_type_id = btf_dedup_ref_type(d, info->index_type);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ info->index_type = ref_type_id;
+
+ h = btf_hash_array(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_array(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+ }
+
+ case BTF_KIND_FUNC_PROTO: {
+ struct btf_param *param;
+ __u16 vlen;
+ int i;
+
+ ref_type_id = btf_dedup_ref_type(d, t->type);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ t->type = ref_type_id;
+
+ vlen = btf_vlen(t);
+ param = btf_params(t);
+ for (i = 0; i < vlen; i++) {
+ ref_type_id = btf_dedup_ref_type(d, param->type);
+ if (ref_type_id < 0)
+ return ref_type_id;
+ param->type = ref_type_id;
+ param++;
+ }
+
+ h = btf_hash_fnproto(t);
+ for_each_dedup_cand(d, hash_entry, h) {
+ cand_id = (__u32)(long)hash_entry->value;
+ cand = d->btf->types[cand_id];
+ if (btf_equal_fnproto(t, cand)) {
+ new_id = cand_id;
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ d->map[type_id] = new_id;
+ if (type_id == new_id && btf_dedup_table_add(d, h, type_id))
+ return -ENOMEM;
+
+ return new_id;
+}
+
+static int btf_dedup_ref_types(struct btf_dedup *d)
+{
+ int i, err;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ err = btf_dedup_ref_type(d, i);
+ if (err < 0)
+ return err;
+ }
+ /* we won't need d->dedup_table anymore */
+ hashmap__free(d->dedup_table);
+ d->dedup_table = NULL;
+ return 0;
+}
+
+/*
+ * Compact types.
+ *
+ * After we established for each type its corresponding canonical representative
+ * type, we now can eliminate types that are not canonical and leave only
+ * canonical ones layed out sequentially in memory by copying them over
+ * duplicates. During compaction btf_dedup->hypot_map array is reused to store
+ * a map from original type ID to a new compacted type ID, which will be used
+ * during next phase to "fix up" type IDs, referenced from struct/union and
+ * reference types.
+ */
+static int btf_dedup_compact_types(struct btf_dedup *d)
+{
+ struct btf_type **new_types;
+ __u32 next_type_id = 1;
+ char *types_start, *p;
+ int i, len;
+
+ /* we are going to reuse hypot_map to store compaction remapping */
+ d->hypot_map[0] = 0;
+ for (i = 1; i <= d->btf->nr_types; i++)
+ d->hypot_map[i] = BTF_UNPROCESSED_ID;
+
+ types_start = d->btf->nohdr_data + d->btf->hdr->type_off;
+ p = types_start;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ if (d->map[i] != i)
+ continue;
+
+ len = btf_type_size(d->btf->types[i]);
+ if (len < 0)
+ return len;
+
+ memmove(p, d->btf->types[i], len);
+ d->hypot_map[i] = next_type_id;
+ d->btf->types[next_type_id] = (struct btf_type *)p;
+ p += len;
+ next_type_id++;
+ }
+
+ /* shrink struct btf's internal types index and update btf_header */
+ d->btf->nr_types = next_type_id - 1;
+ d->btf->types_size = d->btf->nr_types;
+ d->btf->hdr->type_len = p - types_start;
+ new_types = realloc(d->btf->types,
+ (1 + d->btf->nr_types) * sizeof(struct btf_type *));
+ if (!new_types)
+ return -ENOMEM;
+ d->btf->types = new_types;
+
+ /* make sure string section follows type information without gaps */
+ d->btf->hdr->str_off = p - (char *)d->btf->nohdr_data;
+ memmove(p, d->btf->strings, d->btf->hdr->str_len);
+ d->btf->strings = p;
+ p += d->btf->hdr->str_len;
+
+ d->btf->data_size = p - (char *)d->btf->data;
+ return 0;
+}
+
+/*
+ * Figure out final (deduplicated and compacted) type ID for provided original
+ * `type_id` by first resolving it into corresponding canonical type ID and
+ * then mapping it to a deduplicated type ID, stored in btf_dedup->hypot_map,
+ * which is populated during compaction phase.
+ */
+static int btf_dedup_remap_type_id(struct btf_dedup *d, __u32 type_id)
+{
+ __u32 resolved_type_id, new_type_id;
+
+ resolved_type_id = resolve_type_id(d, type_id);
+ new_type_id = d->hypot_map[resolved_type_id];
+ if (new_type_id > BTF_MAX_NR_TYPES)
+ return -EINVAL;
+ return new_type_id;
+}
+
+/*
+ * Remap referenced type IDs into deduped type IDs.
+ *
+ * After BTF types are deduplicated and compacted, their final type IDs may
+ * differ from original ones. The map from original to a corresponding
+ * deduped type ID is stored in btf_dedup->hypot_map and is populated during
+ * compaction phase. During remapping phase we are rewriting all type IDs
+ * referenced from any BTF type (e.g., struct fields, func proto args, etc) to
+ * their final deduped type IDs.
+ */
+static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
+{
+ struct btf_type *t = d->btf->types[type_id];
+ int i, r;
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ break;
+
+ case BTF_KIND_FWD:
+ case BTF_KIND_CONST:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ case BTF_KIND_VAR:
+ r = btf_dedup_remap_type_id(d, t->type);
+ if (r < 0)
+ return r;
+ t->type = r;
+ break;
+
+ case BTF_KIND_ARRAY: {
+ struct btf_array *arr_info = btf_array(t);
+
+ r = btf_dedup_remap_type_id(d, arr_info->type);
+ if (r < 0)
+ return r;
+ arr_info->type = r;
+ r = btf_dedup_remap_type_id(d, arr_info->index_type);
+ if (r < 0)
+ return r;
+ arr_info->index_type = r;
+ break;
+ }
+
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ struct btf_member *member = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++) {
+ r = btf_dedup_remap_type_id(d, member->type);
+ if (r < 0)
+ return r;
+ member->type = r;
+ member++;
+ }
+ break;
+ }
+
+ case BTF_KIND_FUNC_PROTO: {
+ struct btf_param *param = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+
+ r = btf_dedup_remap_type_id(d, t->type);
+ if (r < 0)
+ return r;
+ t->type = r;
+
+ for (i = 0; i < vlen; i++) {
+ r = btf_dedup_remap_type_id(d, param->type);
+ if (r < 0)
+ return r;
+ param->type = r;
+ param++;
+ }
+ break;
+ }
+
+ case BTF_KIND_DATASEC: {
+ struct btf_var_secinfo *var = btf_var_secinfos(t);
+ __u16 vlen = btf_vlen(t);
+
+ for (i = 0; i < vlen; i++) {
+ r = btf_dedup_remap_type_id(d, var->type);
+ if (r < 0)
+ return r;
+ var->type = r;
+ var++;
+ }
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int btf_dedup_remap_types(struct btf_dedup *d)
+{
+ int i, r;
+
+ for (i = 1; i <= d->btf->nr_types; i++) {
+ r = btf_dedup_remap_type(d, i);
+ if (r < 0)
+ return r;
+ }
+ return 0;
+}
diff --git a/src/contrib/libbpf/bpf/btf.h b/src/contrib/libbpf/bpf/btf.h
new file mode 100644
index 0000000..d9ac73a
--- /dev/null
+++ b/src/contrib/libbpf/bpf/btf.h
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2018 Facebook */
+
+#ifndef __LIBBPF_BTF_H
+#define __LIBBPF_BTF_H
+
+#include <stdarg.h>
+#include <linux/btf.h>
+#include <linux/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LIBBPF_API
+#define LIBBPF_API __attribute__((visibility("default")))
+#endif
+
+#define BTF_ELF_SEC ".BTF"
+#define BTF_EXT_ELF_SEC ".BTF.ext"
+#define MAPS_ELF_SEC ".maps"
+
+struct btf;
+struct btf_ext;
+struct btf_type;
+
+struct bpf_object;
+
+/*
+ * The .BTF.ext ELF section layout defined as
+ * struct btf_ext_header
+ * func_info subsection
+ *
+ * The func_info subsection layout:
+ * record size for struct bpf_func_info in the func_info subsection
+ * struct btf_sec_func_info for section #1
+ * a list of bpf_func_info records for section #1
+ * where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
+ * but may not be identical
+ * struct btf_sec_func_info for section #2
+ * a list of bpf_func_info records for section #2
+ * ......
+ *
+ * Note that the bpf_func_info record size in .BTF.ext may not
+ * be the same as the one defined in include/uapi/linux/bpf.h.
+ * The loader should ensure that record_size meets minimum
+ * requirement and pass the record as is to the kernel. The
+ * kernel will handle the func_info properly based on its contents.
+ */
+struct btf_ext_header {
+ __u16 magic;
+ __u8 version;
+ __u8 flags;
+ __u32 hdr_len;
+
+ /* All offsets are in bytes relative to the end of this header */
+ __u32 func_info_off;
+ __u32 func_info_len;
+ __u32 line_info_off;
+ __u32 line_info_len;
+
+ /* optional part of .BTF.ext header */
+ __u32 field_reloc_off;
+ __u32 field_reloc_len;
+};
+
+LIBBPF_API void btf__free(struct btf *btf);
+LIBBPF_API struct btf *btf__new(__u8 *data, __u32 size);
+LIBBPF_API struct btf *btf__parse_elf(const char *path,
+ struct btf_ext **btf_ext);
+LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
+LIBBPF_API int btf__load(struct btf *btf);
+LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
+ const char *type_name);
+LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
+ const char *type_name, __u32 kind);
+LIBBPF_API __u32 btf__get_nr_types(const struct btf *btf);
+LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
+ __u32 id);
+LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
+LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
+LIBBPF_API int btf__fd(const struct btf *btf);
+LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size);
+LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
+LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
+LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
+ __u32 expected_key_size,
+ __u32 expected_value_size,
+ __u32 *key_type_id, __u32 *value_type_id);
+
+LIBBPF_API struct btf_ext *btf_ext__new(__u8 *data, __u32 size);
+LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext);
+LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext,
+ __u32 *size);
+LIBBPF_API int btf_ext__reloc_func_info(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const char *sec_name, __u32 insns_cnt,
+ void **func_info, __u32 *cnt);
+LIBBPF_API int btf_ext__reloc_line_info(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const char *sec_name, __u32 insns_cnt,
+ void **line_info, __u32 *cnt);
+LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
+LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
+
+struct btf_dedup_opts {
+ unsigned int dedup_table_size;
+ bool dont_resolve_fwds;
+};
+
+LIBBPF_API int btf__dedup(struct btf *btf, struct btf_ext *btf_ext,
+ const struct btf_dedup_opts *opts);
+
+struct btf_dump;
+
+struct btf_dump_opts {
+ void *ctx;
+};
+
+typedef void (*btf_dump_printf_fn_t)(void *ctx, const char *fmt, va_list args);
+
+LIBBPF_API struct btf_dump *btf_dump__new(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const struct btf_dump_opts *opts,
+ btf_dump_printf_fn_t printf_fn);
+LIBBPF_API void btf_dump__free(struct btf_dump *d);
+
+LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id);
+
+/*
+ * A set of helpers for easier BTF types handling
+ */
+static inline __u16 btf_kind(const struct btf_type *t)
+{
+ return BTF_INFO_KIND(t->info);
+}
+
+static inline __u16 btf_vlen(const struct btf_type *t)
+{
+ return BTF_INFO_VLEN(t->info);
+}
+
+static inline bool btf_kflag(const struct btf_type *t)
+{
+ return BTF_INFO_KFLAG(t->info);
+}
+
+static inline bool btf_is_int(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_INT;
+}
+
+static inline bool btf_is_ptr(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_PTR;
+}
+
+static inline bool btf_is_array(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_ARRAY;
+}
+
+static inline bool btf_is_struct(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_STRUCT;
+}
+
+static inline bool btf_is_union(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_UNION;
+}
+
+static inline bool btf_is_composite(const struct btf_type *t)
+{
+ __u16 kind = btf_kind(t);
+
+ return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION;
+}
+
+static inline bool btf_is_enum(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_ENUM;
+}
+
+static inline bool btf_is_fwd(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FWD;
+}
+
+static inline bool btf_is_typedef(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_TYPEDEF;
+}
+
+static inline bool btf_is_volatile(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_VOLATILE;
+}
+
+static inline bool btf_is_const(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_CONST;
+}
+
+static inline bool btf_is_restrict(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_RESTRICT;
+}
+
+static inline bool btf_is_mod(const struct btf_type *t)
+{
+ __u16 kind = btf_kind(t);
+
+ return kind == BTF_KIND_VOLATILE ||
+ kind == BTF_KIND_CONST ||
+ kind == BTF_KIND_RESTRICT;
+}
+
+static inline bool btf_is_func(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FUNC;
+}
+
+static inline bool btf_is_func_proto(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_FUNC_PROTO;
+}
+
+static inline bool btf_is_var(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_VAR;
+}
+
+static inline bool btf_is_datasec(const struct btf_type *t)
+{
+ return btf_kind(t) == BTF_KIND_DATASEC;
+}
+
+static inline __u8 btf_int_encoding(const struct btf_type *t)
+{
+ return BTF_INT_ENCODING(*(__u32 *)(t + 1));
+}
+
+static inline __u8 btf_int_offset(const struct btf_type *t)
+{
+ return BTF_INT_OFFSET(*(__u32 *)(t + 1));
+}
+
+static inline __u8 btf_int_bits(const struct btf_type *t)
+{
+ return BTF_INT_BITS(*(__u32 *)(t + 1));
+}
+
+static inline struct btf_array *btf_array(const struct btf_type *t)
+{
+ return (struct btf_array *)(t + 1);
+}
+
+static inline struct btf_enum *btf_enum(const struct btf_type *t)
+{
+ return (struct btf_enum *)(t + 1);
+}
+
+static inline struct btf_member *btf_members(const struct btf_type *t)
+{
+ return (struct btf_member *)(t + 1);
+}
+
+/* Get bit offset of a member with specified index. */
+static inline __u32 btf_member_bit_offset(const struct btf_type *t,
+ __u32 member_idx)
+{
+ const struct btf_member *m = btf_members(t) + member_idx;
+ bool kflag = btf_kflag(t);
+
+ return kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset;
+}
+/*
+ * Get bitfield size of a member, assuming t is BTF_KIND_STRUCT or
+ * BTF_KIND_UNION. If member is not a bitfield, zero is returned.
+ */
+static inline __u32 btf_member_bitfield_size(const struct btf_type *t,
+ __u32 member_idx)
+{
+ const struct btf_member *m = btf_members(t) + member_idx;
+ bool kflag = btf_kflag(t);
+
+ return kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
+}
+
+static inline struct btf_param *btf_params(const struct btf_type *t)
+{
+ return (struct btf_param *)(t + 1);
+}
+
+static inline struct btf_var *btf_var(const struct btf_type *t)
+{
+ return (struct btf_var *)(t + 1);
+}
+
+static inline struct btf_var_secinfo *
+btf_var_secinfos(const struct btf_type *t)
+{
+ return (struct btf_var_secinfo *)(t + 1);
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __LIBBPF_BTF_H */
diff --git a/src/contrib/libbpf/bpf/btf_dump.c b/src/contrib/libbpf/bpf/btf_dump.c
new file mode 100644
index 0000000..cb126d8
--- /dev/null
+++ b/src/contrib/libbpf/bpf/btf_dump.c
@@ -0,0 +1,1386 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * BTF-to-C type converter.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/err.h>
+#include <linux/btf.h>
+#include "btf.h"
+#include "hashmap.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+
+static const char PREFIXES[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t";
+static const size_t PREFIX_CNT = sizeof(PREFIXES) - 1;
+
+static const char *pfx(int lvl)
+{
+ return lvl >= PREFIX_CNT ? PREFIXES : &PREFIXES[PREFIX_CNT - lvl];
+}
+
+enum btf_dump_type_order_state {
+ NOT_ORDERED,
+ ORDERING,
+ ORDERED,
+};
+
+enum btf_dump_type_emit_state {
+ NOT_EMITTED,
+ EMITTING,
+ EMITTED,
+};
+
+/* per-type auxiliary state */
+struct btf_dump_type_aux_state {
+ /* topological sorting state */
+ enum btf_dump_type_order_state order_state: 2;
+ /* emitting state used to determine the need for forward declaration */
+ enum btf_dump_type_emit_state emit_state: 2;
+ /* whether forward declaration was already emitted */
+ __u8 fwd_emitted: 1;
+ /* whether unique non-duplicate name was already assigned */
+ __u8 name_resolved: 1;
+ /* whether type is referenced from any other type */
+ __u8 referenced: 1;
+};
+
+struct btf_dump {
+ const struct btf *btf;
+ const struct btf_ext *btf_ext;
+ btf_dump_printf_fn_t printf_fn;
+ struct btf_dump_opts opts;
+
+ /* per-type auxiliary state */
+ struct btf_dump_type_aux_state *type_states;
+ /* per-type optional cached unique name, must be freed, if present */
+ const char **cached_names;
+
+ /* topo-sorted list of dependent type definitions */
+ __u32 *emit_queue;
+ int emit_queue_cap;
+ int emit_queue_cnt;
+
+ /*
+ * stack of type declarations (e.g., chain of modifiers, arrays,
+ * funcs, etc)
+ */
+ __u32 *decl_stack;
+ int decl_stack_cap;
+ int decl_stack_cnt;
+
+ /* maps struct/union/enum name to a number of name occurrences */
+ struct hashmap *type_names;
+ /*
+ * maps typedef identifiers and enum value names to a number of such
+ * name occurrences
+ */
+ struct hashmap *ident_names;
+};
+
+static size_t str_hash_fn(const void *key, void *ctx)
+{
+ const char *s = key;
+ size_t h = 0;
+
+ while (*s) {
+ h = h * 31 + *s;
+ s++;
+ }
+ return h;
+}
+
+static bool str_equal_fn(const void *a, const void *b, void *ctx)
+{
+ return strcmp(a, b) == 0;
+}
+
+static const char *btf_name_of(const struct btf_dump *d, __u32 name_off)
+{
+ return btf__name_by_offset(d->btf, name_off);
+}
+
+static void btf_dump_printf(const struct btf_dump *d, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ d->printf_fn(d->opts.ctx, fmt, args);
+ va_end(args);
+}
+
+struct btf_dump *btf_dump__new(const struct btf *btf,
+ const struct btf_ext *btf_ext,
+ const struct btf_dump_opts *opts,
+ btf_dump_printf_fn_t printf_fn)
+{
+ struct btf_dump *d;
+ int err;
+
+ d = calloc(1, sizeof(struct btf_dump));
+ if (!d)
+ return ERR_PTR(-ENOMEM);
+
+ d->btf = btf;
+ d->btf_ext = btf_ext;
+ d->printf_fn = printf_fn;
+ d->opts.ctx = opts ? opts->ctx : NULL;
+
+ d->type_names = hashmap__new(str_hash_fn, str_equal_fn, NULL);
+ if (IS_ERR(d->type_names)) {
+ err = PTR_ERR(d->type_names);
+ d->type_names = NULL;
+ btf_dump__free(d);
+ return ERR_PTR(err);
+ }
+ d->ident_names = hashmap__new(str_hash_fn, str_equal_fn, NULL);
+ if (IS_ERR(d->ident_names)) {
+ err = PTR_ERR(d->ident_names);
+ d->ident_names = NULL;
+ btf_dump__free(d);
+ return ERR_PTR(err);
+ }
+
+ return d;
+}
+
+void btf_dump__free(struct btf_dump *d)
+{
+ int i, cnt;
+
+ if (!d)
+ return;
+
+ free(d->type_states);
+ if (d->cached_names) {
+ /* any set cached name is owned by us and should be freed */
+ for (i = 0, cnt = btf__get_nr_types(d->btf); i <= cnt; i++) {
+ if (d->cached_names[i])
+ free((void *)d->cached_names[i]);
+ }
+ }
+ free(d->cached_names);
+ free(d->emit_queue);
+ free(d->decl_stack);
+ hashmap__free(d->type_names);
+ hashmap__free(d->ident_names);
+
+ free(d);
+}
+
+static int btf_dump_mark_referenced(struct btf_dump *d);
+static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr);
+static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id);
+
+/*
+ * Dump BTF type in a compilable C syntax, including all the necessary
+ * dependent types, necessary for compilation. If some of the dependent types
+ * were already emitted as part of previous btf_dump__dump_type() invocation
+ * for another type, they won't be emitted again. This API allows callers to
+ * filter out BTF types according to user-defined criterias and emitted only
+ * minimal subset of types, necessary to compile everything. Full struct/union
+ * definitions will still be emitted, even if the only usage is through
+ * pointer and could be satisfied with just a forward declaration.
+ *
+ * Dumping is done in two high-level passes:
+ * 1. Topologically sort type definitions to satisfy C rules of compilation.
+ * 2. Emit type definitions in C syntax.
+ *
+ * Returns 0 on success; <0, otherwise.
+ */
+int btf_dump__dump_type(struct btf_dump *d, __u32 id)
+{
+ int err, i;
+
+ if (id > btf__get_nr_types(d->btf))
+ return -EINVAL;
+
+ /* type states are lazily allocated, as they might not be needed */
+ if (!d->type_states) {
+ d->type_states = calloc(1 + btf__get_nr_types(d->btf),
+ sizeof(d->type_states[0]));
+ if (!d->type_states)
+ return -ENOMEM;
+ d->cached_names = calloc(1 + btf__get_nr_types(d->btf),
+ sizeof(d->cached_names[0]));
+ if (!d->cached_names)
+ return -ENOMEM;
+
+ /* VOID is special */
+ d->type_states[0].order_state = ORDERED;
+ d->type_states[0].emit_state = EMITTED;
+
+ /* eagerly determine referenced types for anon enums */
+ err = btf_dump_mark_referenced(d);
+ if (err)
+ return err;
+ }
+
+ d->emit_queue_cnt = 0;
+ err = btf_dump_order_type(d, id, false);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < d->emit_queue_cnt; i++)
+ btf_dump_emit_type(d, d->emit_queue[i], 0 /*top-level*/);
+
+ return 0;
+}
+
+/*
+ * Mark all types that are referenced from any other type. This is used to
+ * determine top-level anonymous enums that need to be emitted as an
+ * independent type declarations.
+ * Anonymous enums come in two flavors: either embedded in a struct's field
+ * definition, in which case they have to be declared inline as part of field
+ * type declaration; or as a top-level anonymous enum, typically used for
+ * declaring global constants. It's impossible to distinguish between two
+ * without knowning whether given enum type was referenced from other type:
+ * top-level anonymous enum won't be referenced by anything, while embedded
+ * one will.
+ */
+static int btf_dump_mark_referenced(struct btf_dump *d)
+{
+ int i, j, n = btf__get_nr_types(d->btf);
+ const struct btf_type *t;
+ __u16 vlen;
+
+ for (i = 1; i <= n; i++) {
+ t = btf__type_by_id(d->btf, i);
+ vlen = btf_vlen(t);
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ case BTF_KIND_FWD:
+ break;
+
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_PTR:
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_FUNC:
+ case BTF_KIND_VAR:
+ d->type_states[t->type].referenced = 1;
+ break;
+
+ case BTF_KIND_ARRAY: {
+ const struct btf_array *a = btf_array(t);
+
+ d->type_states[a->index_type].referenced = 1;
+ d->type_states[a->type].referenced = 1;
+ break;
+ }
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *m = btf_members(t);
+
+ for (j = 0; j < vlen; j++, m++)
+ d->type_states[m->type].referenced = 1;
+ break;
+ }
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+
+ for (j = 0; j < vlen; j++, p++)
+ d->type_states[p->type].referenced = 1;
+ break;
+ }
+ case BTF_KIND_DATASEC: {
+ const struct btf_var_secinfo *v = btf_var_secinfos(t);
+
+ for (j = 0; j < vlen; j++, v++)
+ d->type_states[v->type].referenced = 1;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+static int btf_dump_add_emit_queue_id(struct btf_dump *d, __u32 id)
+{
+ __u32 *new_queue;
+ size_t new_cap;
+
+ if (d->emit_queue_cnt >= d->emit_queue_cap) {
+ new_cap = max(16, d->emit_queue_cap * 3 / 2);
+ new_queue = realloc(d->emit_queue,
+ new_cap * sizeof(new_queue[0]));
+ if (!new_queue)
+ return -ENOMEM;
+ d->emit_queue = new_queue;
+ d->emit_queue_cap = new_cap;
+ }
+
+ d->emit_queue[d->emit_queue_cnt++] = id;
+ return 0;
+}
+
+/*
+ * Determine order of emitting dependent types and specified type to satisfy
+ * C compilation rules. This is done through topological sorting with an
+ * additional complication which comes from C rules. The main idea for C is
+ * that if some type is "embedded" into a struct/union, it's size needs to be
+ * known at the time of definition of containing type. E.g., for:
+ *
+ * struct A {};
+ * struct B { struct A x; }
+ *
+ * struct A *HAS* to be defined before struct B, because it's "embedded",
+ * i.e., it is part of struct B layout. But in the following case:
+ *
+ * struct A;
+ * struct B { struct A *x; }
+ * struct A {};
+ *
+ * it's enough to just have a forward declaration of struct A at the time of
+ * struct B definition, as struct B has a pointer to struct A, so the size of
+ * field x is known without knowing struct A size: it's sizeof(void *).
+ *
+ * Unfortunately, there are some trickier cases we need to handle, e.g.:
+ *
+ * struct A {}; // if this was forward-declaration: compilation error
+ * struct B {
+ * struct { // anonymous struct
+ * struct A y;
+ * } *x;
+ * };
+ *
+ * In this case, struct B's field x is a pointer, so it's size is known
+ * regardless of the size of (anonymous) struct it points to. But because this
+ * struct is anonymous and thus defined inline inside struct B, *and* it
+ * embeds struct A, compiler requires full definition of struct A to be known
+ * before struct B can be defined. This creates a transitive dependency
+ * between struct A and struct B. If struct A was forward-declared before
+ * struct B definition and fully defined after struct B definition, that would
+ * trigger compilation error.
+ *
+ * All this means that while we are doing topological sorting on BTF type
+ * graph, we need to determine relationships between different types (graph
+ * nodes):
+ * - weak link (relationship) between X and Y, if Y *CAN* be
+ * forward-declared at the point of X definition;
+ * - strong link, if Y *HAS* to be fully-defined before X can be defined.
+ *
+ * The rule is as follows. Given a chain of BTF types from X to Y, if there is
+ * BTF_KIND_PTR type in the chain and at least one non-anonymous type
+ * Z (excluding X, including Y), then link is weak. Otherwise, it's strong.
+ * Weak/strong relationship is determined recursively during DFS traversal and
+ * is returned as a result from btf_dump_order_type().
+ *
+ * btf_dump_order_type() is trying to avoid unnecessary forward declarations,
+ * but it is not guaranteeing that no extraneous forward declarations will be
+ * emitted.
+ *
+ * To avoid extra work, algorithm marks some of BTF types as ORDERED, when
+ * it's done with them, but not for all (e.g., VOLATILE, CONST, RESTRICT,
+ * ARRAY, FUNC_PROTO), as weak/strong semantics for those depends on the
+ * entire graph path, so depending where from one came to that BTF type, it
+ * might cause weak or strong ordering. For types like STRUCT/UNION/INT/ENUM,
+ * once they are processed, there is no need to do it again, so they are
+ * marked as ORDERED. We can mark PTR as ORDERED as well, as it semi-forces
+ * weak link, unless subsequent referenced STRUCT/UNION/ENUM is anonymous. But
+ * in any case, once those are processed, no need to do it again, as the
+ * result won't change.
+ *
+ * Returns:
+ * - 1, if type is part of strong link (so there is strong topological
+ * ordering requirements);
+ * - 0, if type is part of weak link (so can be satisfied through forward
+ * declaration);
+ * - <0, on error (e.g., unsatisfiable type loop detected).
+ */
+static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
+{
+ /*
+ * Order state is used to detect strong link cycles, but only for BTF
+ * kinds that are or could be an independent definition (i.e.,
+ * stand-alone fwd decl, enum, typedef, struct, union). Ptrs, arrays,
+ * func_protos, modifiers are just means to get to these definitions.
+ * Int/void don't need definitions, they are assumed to be always
+ * properly defined. We also ignore datasec, var, and funcs for now.
+ * So for all non-defining kinds, we never even set ordering state,
+ * for defining kinds we set ORDERING and subsequently ORDERED if it
+ * forms a strong link.
+ */
+ struct btf_dump_type_aux_state *tstate = &d->type_states[id];
+ const struct btf_type *t;
+ __u16 vlen;
+ int err, i;
+
+ /* return true, letting typedefs know that it's ok to be emitted */
+ if (tstate->order_state == ORDERED)
+ return 1;
+
+ t = btf__type_by_id(d->btf, id);
+
+ if (tstate->order_state == ORDERING) {
+ /* type loop, but resolvable through fwd declaration */
+ if (btf_is_composite(t) && through_ptr && t->name_off != 0)
+ return 0;
+ pr_warn("unsatisfiable type cycle, id:[%u]\n", id);
+ return -ELOOP;
+ }
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_INT:
+ tstate->order_state = ORDERED;
+ return 0;
+
+ case BTF_KIND_PTR:
+ err = btf_dump_order_type(d, t->type, true);
+ tstate->order_state = ORDERED;
+ return err;
+
+ case BTF_KIND_ARRAY:
+ return btf_dump_order_type(d, btf_array(t)->type, through_ptr);
+
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *m = btf_members(t);
+ /*
+ * struct/union is part of strong link, only if it's embedded
+ * (so no ptr in a path) or it's anonymous (so has to be
+ * defined inline, even if declared through ptr)
+ */
+ if (through_ptr && t->name_off != 0)
+ return 0;
+
+ tstate->order_state = ORDERING;
+
+ vlen = btf_vlen(t);
+ for (i = 0; i < vlen; i++, m++) {
+ err = btf_dump_order_type(d, m->type, false);
+ if (err < 0)
+ return err;
+ }
+
+ if (t->name_off != 0) {
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err < 0)
+ return err;
+ }
+
+ tstate->order_state = ORDERED;
+ return 1;
+ }
+ case BTF_KIND_ENUM:
+ case BTF_KIND_FWD:
+ /*
+ * non-anonymous or non-referenced enums are top-level
+ * declarations and should be emitted. Same logic can be
+ * applied to FWDs, it won't hurt anyways.
+ */
+ if (t->name_off != 0 || !tstate->referenced) {
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err)
+ return err;
+ }
+ tstate->order_state = ORDERED;
+ return 1;
+
+ case BTF_KIND_TYPEDEF: {
+ int is_strong;
+
+ is_strong = btf_dump_order_type(d, t->type, through_ptr);
+ if (is_strong < 0)
+ return is_strong;
+
+ /* typedef is similar to struct/union w.r.t. fwd-decls */
+ if (through_ptr && !is_strong)
+ return 0;
+
+ /* typedef is always a named definition */
+ err = btf_dump_add_emit_queue_id(d, id);
+ if (err)
+ return err;
+
+ d->type_states[id].order_state = ORDERED;
+ return 1;
+ }
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ return btf_dump_order_type(d, t->type, through_ptr);
+
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ bool is_strong;
+
+ err = btf_dump_order_type(d, t->type, through_ptr);
+ if (err < 0)
+ return err;
+ is_strong = err > 0;
+
+ vlen = btf_vlen(t);
+ for (i = 0; i < vlen; i++, p++) {
+ err = btf_dump_order_type(d, p->type, through_ptr);
+ if (err < 0)
+ return err;
+ if (err > 0)
+ is_strong = true;
+ }
+ return is_strong;
+ }
+ case BTF_KIND_FUNC:
+ case BTF_KIND_VAR:
+ case BTF_KIND_DATASEC:
+ d->type_states[id].order_state = ORDERED;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+static void btf_dump_emit_struct_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t);
+
+static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl);
+
+/* a local view into a shared stack */
+struct id_stack {
+ const __u32 *ids;
+ int cnt;
+};
+
+static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
+ const char *fname, int lvl);
+static void btf_dump_emit_type_chain(struct btf_dump *d,
+ struct id_stack *decl_stack,
+ const char *fname, int lvl);
+
+static const char *btf_dump_type_name(struct btf_dump *d, __u32 id);
+static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id);
+static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map,
+ const char *orig_name);
+
+static bool btf_dump_is_blacklisted(struct btf_dump *d, __u32 id)
+{
+ const struct btf_type *t = btf__type_by_id(d->btf, id);
+
+ /* __builtin_va_list is a compiler built-in, which causes compilation
+ * errors, when compiling w/ different compiler, then used to compile
+ * original code (e.g., GCC to compile kernel, Clang to use generated
+ * C header from BTF). As it is built-in, it should be already defined
+ * properly internally in compiler.
+ */
+ if (t->name_off == 0)
+ return false;
+ return strcmp(btf_name_of(d, t->name_off), "__builtin_va_list") == 0;
+}
+
+/*
+ * Emit C-syntax definitions of types from chains of BTF types.
+ *
+ * High-level handling of determining necessary forward declarations are handled
+ * by btf_dump_emit_type() itself, but all nitty-gritty details of emitting type
+ * declarations/definitions in C syntax are handled by a combo of
+ * btf_dump_emit_type_decl()/btf_dump_emit_type_chain() w/ delegation to
+ * corresponding btf_dump_emit_*_{def,fwd}() functions.
+ *
+ * We also keep track of "containing struct/union type ID" to determine when
+ * we reference it from inside and thus can avoid emitting unnecessary forward
+ * declaration.
+ *
+ * This algorithm is designed in such a way, that even if some error occurs
+ * (either technical, e.g., out of memory, or logical, i.e., malformed BTF
+ * that doesn't comply to C rules completely), algorithm will try to proceed
+ * and produce as much meaningful output as possible.
+ */
+static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
+{
+ struct btf_dump_type_aux_state *tstate = &d->type_states[id];
+ bool top_level_def = cont_id == 0;
+ const struct btf_type *t;
+ __u16 kind;
+
+ if (tstate->emit_state == EMITTED)
+ return;
+
+ t = btf__type_by_id(d->btf, id);
+ kind = btf_kind(t);
+
+ if (tstate->emit_state == EMITTING) {
+ if (tstate->fwd_emitted)
+ return;
+
+ switch (kind) {
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ /*
+ * if we are referencing a struct/union that we are
+ * part of - then no need for fwd declaration
+ */
+ if (id == cont_id)
+ return;
+ if (t->name_off == 0) {
+ pr_warn("anonymous struct/union loop, id:[%u]\n",
+ id);
+ return;
+ }
+ btf_dump_emit_struct_fwd(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->fwd_emitted = 1;
+ break;
+ case BTF_KIND_TYPEDEF:
+ /*
+ * for typedef fwd_emitted means typedef definition
+ * was emitted, but it can be used only for "weak"
+ * references through pointer only, not for embedding
+ */
+ if (!btf_dump_is_blacklisted(d, id)) {
+ btf_dump_emit_typedef_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ };
+ tstate->fwd_emitted = 1;
+ break;
+ default:
+ break;
+ }
+
+ return;
+ }
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_ENUM:
+ if (top_level_def) {
+ btf_dump_emit_enum_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ }
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_PTR:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ btf_dump_emit_type(d, t->type, cont_id);
+ break;
+ case BTF_KIND_ARRAY:
+ btf_dump_emit_type(d, btf_array(t)->type, cont_id);
+ break;
+ case BTF_KIND_FWD:
+ btf_dump_emit_fwd_def(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_TYPEDEF:
+ tstate->emit_state = EMITTING;
+ btf_dump_emit_type(d, t->type, id);
+ /*
+ * typedef can server as both definition and forward
+ * declaration; at this stage someone depends on
+ * typedef as a forward declaration (refers to it
+ * through pointer), so unless we already did it,
+ * emit typedef as a forward declaration
+ */
+ if (!tstate->fwd_emitted && !btf_dump_is_blacklisted(d, id)) {
+ btf_dump_emit_typedef_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ }
+ tstate->emit_state = EMITTED;
+ break;
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ tstate->emit_state = EMITTING;
+ /* if it's a top-level struct/union definition or struct/union
+ * is anonymous, then in C we'll be emitting all fields and
+ * their types (as opposed to just `struct X`), so we need to
+ * make sure that all types, referenced from struct/union
+ * members have necessary forward-declarations, where
+ * applicable
+ */
+ if (top_level_def || t->name_off == 0) {
+ const struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+ int i, new_cont_id;
+
+ new_cont_id = t->name_off == 0 ? cont_id : id;
+ for (i = 0; i < vlen; i++, m++)
+ btf_dump_emit_type(d, m->type, new_cont_id);
+ } else if (!tstate->fwd_emitted && id != cont_id) {
+ btf_dump_emit_struct_fwd(d, id, t);
+ btf_dump_printf(d, ";\n\n");
+ tstate->fwd_emitted = 1;
+ }
+
+ if (top_level_def) {
+ btf_dump_emit_struct_def(d, id, t, 0);
+ btf_dump_printf(d, ";\n\n");
+ tstate->emit_state = EMITTED;
+ } else {
+ tstate->emit_state = NOT_EMITTED;
+ }
+ break;
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ int i;
+
+ btf_dump_emit_type(d, t->type, cont_id);
+ for (i = 0; i < vlen; i++, p++)
+ btf_dump_emit_type(d, p->type, cont_id);
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static int btf_align_of(const struct btf *btf, __u32 id)
+{
+ const struct btf_type *t = btf__type_by_id(btf, id);
+ __u16 kind = btf_kind(t);
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ return min(sizeof(void *), t->size);
+ case BTF_KIND_PTR:
+ return sizeof(void *);
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ return btf_align_of(btf, t->type);
+ case BTF_KIND_ARRAY:
+ return btf_align_of(btf, btf_array(t)->type);
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION: {
+ const struct btf_member *m = btf_members(t);
+ __u16 vlen = btf_vlen(t);
+ int i, align = 1;
+
+ for (i = 0; i < vlen; i++, m++)
+ align = max(align, btf_align_of(btf, m->type));
+
+ return align;
+ }
+ default:
+ pr_warn("unsupported BTF_KIND:%u\n", btf_kind(t));
+ return 1;
+ }
+}
+
+static bool btf_is_struct_packed(const struct btf *btf, __u32 id,
+ const struct btf_type *t)
+{
+ const struct btf_member *m;
+ int align, i, bit_sz;
+ __u16 vlen;
+
+ align = btf_align_of(btf, id);
+ /* size of a non-packed struct has to be a multiple of its alignment*/
+ if (t->size % align)
+ return true;
+
+ m = btf_members(t);
+ vlen = btf_vlen(t);
+ /* all non-bitfield fields have to be naturally aligned */
+ for (i = 0; i < vlen; i++, m++) {
+ align = btf_align_of(btf, m->type);
+ bit_sz = btf_member_bitfield_size(t, i);
+ if (bit_sz == 0 && m->offset % (8 * align) != 0)
+ return true;
+ }
+
+ /*
+ * if original struct was marked as packed, but its layout is
+ * naturally aligned, we'll detect that it's not packed
+ */
+ return false;
+}
+
+static int chip_away_bits(int total, int at_most)
+{
+ return total % at_most ? : at_most;
+}
+
+static void btf_dump_emit_bit_padding(const struct btf_dump *d,
+ int cur_off, int m_off, int m_bit_sz,
+ int align, int lvl)
+{
+ int off_diff = m_off - cur_off;
+ int ptr_bits = sizeof(void *) * 8;
+
+ if (off_diff <= 0)
+ /* no gap */
+ return;
+ if (m_bit_sz == 0 && off_diff < align * 8)
+ /* natural padding will take care of a gap */
+ return;
+
+ while (off_diff > 0) {
+ const char *pad_type;
+ int pad_bits;
+
+ if (ptr_bits > 32 && off_diff > 32) {
+ pad_type = "long";
+ pad_bits = chip_away_bits(off_diff, ptr_bits);
+ } else if (off_diff > 16) {
+ pad_type = "int";
+ pad_bits = chip_away_bits(off_diff, 32);
+ } else if (off_diff > 8) {
+ pad_type = "short";
+ pad_bits = chip_away_bits(off_diff, 16);
+ } else {
+ pad_type = "char";
+ pad_bits = chip_away_bits(off_diff, 8);
+ }
+ btf_dump_printf(d, "\n%s%s: %d;", pfx(lvl), pad_type, pad_bits);
+ off_diff -= pad_bits;
+ }
+}
+
+static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ btf_dump_printf(d, "%s %s",
+ btf_is_struct(t) ? "struct" : "union",
+ btf_dump_type_name(d, id));
+}
+
+static void btf_dump_emit_struct_def(struct btf_dump *d,
+ __u32 id,
+ const struct btf_type *t,
+ int lvl)
+{
+ const struct btf_member *m = btf_members(t);
+ bool is_struct = btf_is_struct(t);
+ int align, i, packed, off = 0;
+ __u16 vlen = btf_vlen(t);
+
+ packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0;
+
+ btf_dump_printf(d, "%s%s%s {",
+ is_struct ? "struct" : "union",
+ t->name_off ? " " : "",
+ btf_dump_type_name(d, id));
+
+ for (i = 0; i < vlen; i++, m++) {
+ const char *fname;
+ int m_off, m_sz;
+
+ fname = btf_name_of(d, m->name_off);
+ m_sz = btf_member_bitfield_size(t, i);
+ m_off = btf_member_bit_offset(t, i);
+ align = packed ? 1 : btf_align_of(d->btf, m->type);
+
+ btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1);
+ btf_dump_printf(d, "\n%s", pfx(lvl + 1));
+ btf_dump_emit_type_decl(d, m->type, fname, lvl + 1);
+
+ if (m_sz) {
+ btf_dump_printf(d, ": %d", m_sz);
+ off = m_off + m_sz;
+ } else {
+ m_sz = max(0, btf__resolve_size(d->btf, m->type));
+ off = m_off + m_sz * 8;
+ }
+ btf_dump_printf(d, ";");
+ }
+
+ /* pad at the end, if necessary */
+ if (is_struct) {
+ align = packed ? 1 : btf_align_of(d->btf, id);
+ btf_dump_emit_bit_padding(d, off, t->size * 8, 0, align,
+ lvl + 1);
+ }
+
+ if (vlen)
+ btf_dump_printf(d, "\n");
+ btf_dump_printf(d, "%s}", pfx(lvl));
+ if (packed)
+ btf_dump_printf(d, " __attribute__((packed))");
+}
+
+static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ btf_dump_printf(d, "enum %s", btf_dump_type_name(d, id));
+}
+
+static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t,
+ int lvl)
+{
+ const struct btf_enum *v = btf_enum(t);
+ __u16 vlen = btf_vlen(t);
+ const char *name;
+ size_t dup_cnt;
+ int i;
+
+ btf_dump_printf(d, "enum%s%s",
+ t->name_off ? " " : "",
+ btf_dump_type_name(d, id));
+
+ if (vlen) {
+ btf_dump_printf(d, " {");
+ for (i = 0; i < vlen; i++, v++) {
+ name = btf_name_of(d, v->name_off);
+ /* enumerators share namespace with typedef idents */
+ dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
+ if (dup_cnt > 1) {
+ btf_dump_printf(d, "\n%s%s___%zu = %d,",
+ pfx(lvl + 1), name, dup_cnt,
+ (__s32)v->val);
+ } else {
+ btf_dump_printf(d, "\n%s%s = %d,",
+ pfx(lvl + 1), name,
+ (__s32)v->val);
+ }
+ }
+ btf_dump_printf(d, "\n%s}", pfx(lvl));
+ }
+}
+
+static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t)
+{
+ const char *name = btf_dump_type_name(d, id);
+
+ if (btf_kflag(t))
+ btf_dump_printf(d, "union %s", name);
+ else
+ btf_dump_printf(d, "struct %s", name);
+}
+
+static void btf_dump_emit_typedef_def(struct btf_dump *d, __u32 id,
+ const struct btf_type *t, int lvl)
+{
+ const char *name = btf_dump_ident_name(d, id);
+
+ /*
+ * Old GCC versions are emitting invalid typedef for __gnuc_va_list
+ * pointing to VOID. This generates warnings from btf_dump() and
+ * results in uncompilable header file, so we are fixing it up here
+ * with valid typedef into __builtin_va_list.
+ */
+ if (t->type == 0 && strcmp(name, "__gnuc_va_list") == 0) {
+ btf_dump_printf(d, "typedef __builtin_va_list __gnuc_va_list");
+ return;
+ }
+
+ btf_dump_printf(d, "typedef ");
+ btf_dump_emit_type_decl(d, t->type, name, lvl);
+}
+
+static int btf_dump_push_decl_stack_id(struct btf_dump *d, __u32 id)
+{
+ __u32 *new_stack;
+ size_t new_cap;
+
+ if (d->decl_stack_cnt >= d->decl_stack_cap) {
+ new_cap = max(16, d->decl_stack_cap * 3 / 2);
+ new_stack = realloc(d->decl_stack,
+ new_cap * sizeof(new_stack[0]));
+ if (!new_stack)
+ return -ENOMEM;
+ d->decl_stack = new_stack;
+ d->decl_stack_cap = new_cap;
+ }
+
+ d->decl_stack[d->decl_stack_cnt++] = id;
+
+ return 0;
+}
+
+/*
+ * Emit type declaration (e.g., field type declaration in a struct or argument
+ * declaration in function prototype) in correct C syntax.
+ *
+ * For most types it's trivial, but there are few quirky type declaration
+ * cases worth mentioning:
+ * - function prototypes (especially nesting of function prototypes);
+ * - arrays;
+ * - const/volatile/restrict for pointers vs other types.
+ *
+ * For a good discussion of *PARSING* C syntax (as a human), see
+ * Peter van der Linden's "Expert C Programming: Deep C Secrets",
+ * Ch.3 "Unscrambling Declarations in C".
+ *
+ * It won't help with BTF to C conversion much, though, as it's an opposite
+ * problem. So we came up with this algorithm in reverse to van der Linden's
+ * parsing algorithm. It goes from structured BTF representation of type
+ * declaration to a valid compilable C syntax.
+ *
+ * For instance, consider this C typedef:
+ * typedef const int * const * arr[10] arr_t;
+ * It will be represented in BTF with this chain of BTF types:
+ * [typedef] -> [array] -> [ptr] -> [const] -> [ptr] -> [const] -> [int]
+ *
+ * Notice how [const] modifier always goes before type it modifies in BTF type
+ * graph, but in C syntax, const/volatile/restrict modifiers are written to
+ * the right of pointers, but to the left of other types. There are also other
+ * quirks, like function pointers, arrays of them, functions returning other
+ * functions, etc.
+ *
+ * We handle that by pushing all the types to a stack, until we hit "terminal"
+ * type (int/enum/struct/union/fwd). Then depending on the kind of a type on
+ * top of a stack, modifiers are handled differently. Array/function pointers
+ * have also wildly different syntax and how nesting of them are done. See
+ * code for authoritative definition.
+ *
+ * To avoid allocating new stack for each independent chain of BTF types, we
+ * share one bigger stack, with each chain working only on its own local view
+ * of a stack frame. Some care is required to "pop" stack frames after
+ * processing type declaration chain.
+ */
+static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
+ const char *fname, int lvl)
+{
+ struct id_stack decl_stack;
+ const struct btf_type *t;
+ int err, stack_start;
+
+ stack_start = d->decl_stack_cnt;
+ for (;;) {
+ err = btf_dump_push_decl_stack_id(d, id);
+ if (err < 0) {
+ /*
+ * if we don't have enough memory for entire type decl
+ * chain, restore stack, emit warning, and try to
+ * proceed nevertheless
+ */
+ pr_warn("not enough memory for decl stack:%d", err);
+ d->decl_stack_cnt = stack_start;
+ return;
+ }
+
+ /* VOID */
+ if (id == 0)
+ break;
+
+ t = btf__type_by_id(d->btf, id);
+ switch (btf_kind(t)) {
+ case BTF_KIND_PTR:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ case BTF_KIND_FUNC_PROTO:
+ id = t->type;
+ break;
+ case BTF_KIND_ARRAY:
+ id = btf_array(t)->type;
+ break;
+ case BTF_KIND_INT:
+ case BTF_KIND_ENUM:
+ case BTF_KIND_FWD:
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ case BTF_KIND_TYPEDEF:
+ goto done;
+ default:
+ pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n",
+ btf_kind(t), id);
+ goto done;
+ }
+ }
+done:
+ /*
+ * We might be inside a chain of declarations (e.g., array of function
+ * pointers returning anonymous (so inlined) structs, having another
+ * array field). Each of those needs its own "stack frame" to handle
+ * emitting of declarations. Those stack frames are non-overlapping
+ * portions of shared btf_dump->decl_stack. To make it a bit nicer to
+ * handle this set of nested stacks, we create a view corresponding to
+ * our own "stack frame" and work with it as an independent stack.
+ * We'll need to clean up after emit_type_chain() returns, though.
+ */
+ decl_stack.ids = d->decl_stack + stack_start;
+ decl_stack.cnt = d->decl_stack_cnt - stack_start;
+ btf_dump_emit_type_chain(d, &decl_stack, fname, lvl);
+ /*
+ * emit_type_chain() guarantees that it will pop its entire decl_stack
+ * frame before returning. But it works with a read-only view into
+ * decl_stack, so it doesn't actually pop anything from the
+ * perspective of shared btf_dump->decl_stack, per se. We need to
+ * reset decl_stack state to how it was before us to avoid it growing
+ * all the time.
+ */
+ d->decl_stack_cnt = stack_start;
+}
+
+static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack)
+{
+ const struct btf_type *t;
+ __u32 id;
+
+ while (decl_stack->cnt) {
+ id = decl_stack->ids[decl_stack->cnt - 1];
+ t = btf__type_by_id(d->btf, id);
+
+ switch (btf_kind(t)) {
+ case BTF_KIND_VOLATILE:
+ btf_dump_printf(d, "volatile ");
+ break;
+ case BTF_KIND_CONST:
+ btf_dump_printf(d, "const ");
+ break;
+ case BTF_KIND_RESTRICT:
+ btf_dump_printf(d, "restrict ");
+ break;
+ default:
+ return;
+ }
+ decl_stack->cnt--;
+ }
+}
+
+static void btf_dump_emit_name(const struct btf_dump *d,
+ const char *name, bool last_was_ptr)
+{
+ bool separate = name[0] && !last_was_ptr;
+
+ btf_dump_printf(d, "%s%s", separate ? " " : "", name);
+}
+
+static void btf_dump_emit_type_chain(struct btf_dump *d,
+ struct id_stack *decls,
+ const char *fname, int lvl)
+{
+ /*
+ * last_was_ptr is used to determine if we need to separate pointer
+ * asterisk (*) from previous part of type signature with space, so
+ * that we get `int ***`, instead of `int * * *`. We default to true
+ * for cases where we have single pointer in a chain. E.g., in ptr ->
+ * func_proto case. func_proto will start a new emit_type_chain call
+ * with just ptr, which should be emitted as (*) or (*<fname>), so we
+ * don't want to prepend space for that last pointer.
+ */
+ bool last_was_ptr = true;
+ const struct btf_type *t;
+ const char *name;
+ __u16 kind;
+ __u32 id;
+
+ while (decls->cnt) {
+ id = decls->ids[--decls->cnt];
+ if (id == 0) {
+ /* VOID is a special snowflake */
+ btf_dump_emit_mods(d, decls);
+ btf_dump_printf(d, "void");
+ last_was_ptr = false;
+ continue;
+ }
+
+ t = btf__type_by_id(d->btf, id);
+ kind = btf_kind(t);
+
+ switch (kind) {
+ case BTF_KIND_INT:
+ btf_dump_emit_mods(d, decls);
+ name = btf_name_of(d, t->name_off);
+ btf_dump_printf(d, "%s", name);
+ break;
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ btf_dump_emit_mods(d, decls);
+ /* inline anonymous struct/union */
+ if (t->name_off == 0)
+ btf_dump_emit_struct_def(d, id, t, lvl);
+ else
+ btf_dump_emit_struct_fwd(d, id, t);
+ break;
+ case BTF_KIND_ENUM:
+ btf_dump_emit_mods(d, decls);
+ /* inline anonymous enum */
+ if (t->name_off == 0)
+ btf_dump_emit_enum_def(d, id, t, lvl);
+ else
+ btf_dump_emit_enum_fwd(d, id, t);
+ break;
+ case BTF_KIND_FWD:
+ btf_dump_emit_mods(d, decls);
+ btf_dump_emit_fwd_def(d, id, t);
+ break;
+ case BTF_KIND_TYPEDEF:
+ btf_dump_emit_mods(d, decls);
+ btf_dump_printf(d, "%s", btf_dump_ident_name(d, id));
+ break;
+ case BTF_KIND_PTR:
+ btf_dump_printf(d, "%s", last_was_ptr ? "*" : " *");
+ break;
+ case BTF_KIND_VOLATILE:
+ btf_dump_printf(d, " volatile");
+ break;
+ case BTF_KIND_CONST:
+ btf_dump_printf(d, " const");
+ break;
+ case BTF_KIND_RESTRICT:
+ btf_dump_printf(d, " restrict");
+ break;
+ case BTF_KIND_ARRAY: {
+ const struct btf_array *a = btf_array(t);
+ const struct btf_type *next_t;
+ __u32 next_id;
+ bool multidim;
+ /*
+ * GCC has a bug
+ * (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=8354)
+ * which causes it to emit extra const/volatile
+ * modifiers for an array, if array's element type has
+ * const/volatile modifiers. Clang doesn't do that.
+ * In general, it doesn't seem very meaningful to have
+ * a const/volatile modifier for array, so we are
+ * going to silently skip them here.
+ */
+ while (decls->cnt) {
+ next_id = decls->ids[decls->cnt - 1];
+ next_t = btf__type_by_id(d->btf, next_id);
+ if (btf_is_mod(next_t))
+ decls->cnt--;
+ else
+ break;
+ }
+
+ if (decls->cnt == 0) {
+ btf_dump_emit_name(d, fname, last_was_ptr);
+ btf_dump_printf(d, "[%u]", a->nelems);
+ return;
+ }
+
+ next_id = decls->ids[decls->cnt - 1];
+ next_t = btf__type_by_id(d->btf, next_id);
+ multidim = btf_is_array(next_t);
+ /* we need space if we have named non-pointer */
+ if (fname[0] && !last_was_ptr)
+ btf_dump_printf(d, " ");
+ /* no parentheses for multi-dimensional array */
+ if (!multidim)
+ btf_dump_printf(d, "(");
+ btf_dump_emit_type_chain(d, decls, fname, lvl);
+ if (!multidim)
+ btf_dump_printf(d, ")");
+ btf_dump_printf(d, "[%u]", a->nelems);
+ return;
+ }
+ case BTF_KIND_FUNC_PROTO: {
+ const struct btf_param *p = btf_params(t);
+ __u16 vlen = btf_vlen(t);
+ int i;
+
+ btf_dump_emit_mods(d, decls);
+ if (decls->cnt) {
+ btf_dump_printf(d, " (");
+ btf_dump_emit_type_chain(d, decls, fname, lvl);
+ btf_dump_printf(d, ")");
+ } else {
+ btf_dump_emit_name(d, fname, last_was_ptr);
+ }
+ btf_dump_printf(d, "(");
+ /*
+ * Clang for BPF target generates func_proto with no
+ * args as a func_proto with a single void arg (e.g.,
+ * `int (*f)(void)` vs just `int (*f)()`). We are
+ * going to pretend there are no args for such case.
+ */
+ if (vlen == 1 && p->type == 0) {
+ btf_dump_printf(d, ")");
+ return;
+ }
+
+ for (i = 0; i < vlen; i++, p++) {
+ if (i > 0)
+ btf_dump_printf(d, ", ");
+
+ /* last arg of type void is vararg */
+ if (i == vlen - 1 && p->type == 0) {
+ btf_dump_printf(d, "...");
+ break;
+ }
+
+ name = btf_name_of(d, p->name_off);
+ btf_dump_emit_type_decl(d, p->type, name, lvl);
+ }
+
+ btf_dump_printf(d, ")");
+ return;
+ }
+ default:
+ pr_warn("unexpected type in decl chain, kind:%u, id:[%u]\n",
+ kind, id);
+ return;
+ }
+
+ last_was_ptr = kind == BTF_KIND_PTR;
+ }
+
+ btf_dump_emit_name(d, fname, last_was_ptr);
+}
+
+/* return number of duplicates (occurrences) of a given name */
+static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map,
+ const char *orig_name)
+{
+ size_t dup_cnt = 0;
+
+ hashmap__find(name_map, orig_name, (void **)&dup_cnt);
+ dup_cnt++;
+ hashmap__set(name_map, orig_name, (void *)dup_cnt, NULL, NULL);
+
+ return dup_cnt;
+}
+
+static const char *btf_dump_resolve_name(struct btf_dump *d, __u32 id,
+ struct hashmap *name_map)
+{
+ struct btf_dump_type_aux_state *s = &d->type_states[id];
+ const struct btf_type *t = btf__type_by_id(d->btf, id);
+ const char *orig_name = btf_name_of(d, t->name_off);
+ const char **cached_name = &d->cached_names[id];
+ size_t dup_cnt;
+
+ if (t->name_off == 0)
+ return "";
+
+ if (s->name_resolved)
+ return *cached_name ? *cached_name : orig_name;
+
+ dup_cnt = btf_dump_name_dups(d, name_map, orig_name);
+ if (dup_cnt > 1) {
+ const size_t max_len = 256;
+ char new_name[max_len];
+
+ snprintf(new_name, max_len, "%s___%zu", orig_name, dup_cnt);
+ *cached_name = strdup(new_name);
+ }
+
+ s->name_resolved = 1;
+ return *cached_name ? *cached_name : orig_name;
+}
+
+static const char *btf_dump_type_name(struct btf_dump *d, __u32 id)
+{
+ return btf_dump_resolve_name(d, id, d->type_names);
+}
+
+static const char *btf_dump_ident_name(struct btf_dump *d, __u32 id)
+{
+ return btf_dump_resolve_name(d, id, d->ident_names);
+}
diff --git a/src/contrib/libbpf/bpf/hashmap.c b/src/contrib/libbpf/bpf/hashmap.c
new file mode 100644
index 0000000..6122272
--- /dev/null
+++ b/src/contrib/libbpf/bpf/hashmap.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Generic non-thread safe hash map implementation.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <linux/err.h>
+#include "hashmap.h"
+
+/* start with 4 buckets */
+#define HASHMAP_MIN_CAP_BITS 2
+
+static void hashmap_add_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
+{
+ entry->next = *pprev;
+ *pprev = entry;
+}
+
+static void hashmap_del_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
+{
+ *pprev = entry->next;
+ entry->next = NULL;
+}
+
+void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx)
+{
+ map->hash_fn = hash_fn;
+ map->equal_fn = equal_fn;
+ map->ctx = ctx;
+
+ map->buckets = NULL;
+ map->cap = 0;
+ map->cap_bits = 0;
+ map->sz = 0;
+}
+
+struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
+{
+ struct hashmap *map = malloc(sizeof(struct hashmap));
+
+ if (!map)
+ return ERR_PTR(-ENOMEM);
+ hashmap__init(map, hash_fn, equal_fn, ctx);
+ return map;
+}
+
+void hashmap__clear(struct hashmap *map)
+{
+ free(map->buckets);
+ map->cap = map->cap_bits = map->sz = 0;
+}
+
+void hashmap__free(struct hashmap *map)
+{
+ if (!map)
+ return;
+
+ hashmap__clear(map);
+ free(map);
+}
+
+size_t hashmap__size(const struct hashmap *map)
+{
+ return map->sz;
+}
+
+size_t hashmap__capacity(const struct hashmap *map)
+{
+ return map->cap;
+}
+
+static bool hashmap_needs_to_grow(struct hashmap *map)
+{
+ /* grow if empty or more than 75% filled */
+ return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
+}
+
+static int hashmap_grow(struct hashmap *map)
+{
+ struct hashmap_entry **new_buckets;
+ struct hashmap_entry *cur, *tmp;
+ size_t new_cap_bits, new_cap;
+ size_t h;
+ int bkt;
+
+ new_cap_bits = map->cap_bits + 1;
+ if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
+ new_cap_bits = HASHMAP_MIN_CAP_BITS;
+
+ new_cap = 1UL << new_cap_bits;
+ new_buckets = calloc(new_cap, sizeof(new_buckets[0]));
+ if (!new_buckets)
+ return -ENOMEM;
+
+ hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
+ h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
+ hashmap_add_entry(&new_buckets[h], cur);
+ }
+
+ map->cap = new_cap;
+ map->cap_bits = new_cap_bits;
+ free(map->buckets);
+ map->buckets = new_buckets;
+
+ return 0;
+}
+
+static bool hashmap_find_entry(const struct hashmap *map,
+ const void *key, size_t hash,
+ struct hashmap_entry ***pprev,
+ struct hashmap_entry **entry)
+{
+ struct hashmap_entry *cur, **prev_ptr;
+
+ if (!map->buckets)
+ return false;
+
+ for (prev_ptr = &map->buckets[hash], cur = *prev_ptr;
+ cur;
+ prev_ptr = &cur->next, cur = cur->next) {
+ if (map->equal_fn(cur->key, key, map->ctx)) {
+ if (pprev)
+ *pprev = prev_ptr;
+ *entry = cur;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int hashmap__insert(struct hashmap *map, const void *key, void *value,
+ enum hashmap_insert_strategy strategy,
+ const void **old_key, void **old_value)
+{
+ struct hashmap_entry *entry;
+ size_t h;
+ int err;
+
+ if (old_key)
+ *old_key = NULL;
+ if (old_value)
+ *old_value = NULL;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (strategy != HASHMAP_APPEND &&
+ hashmap_find_entry(map, key, h, NULL, &entry)) {
+ if (old_key)
+ *old_key = entry->key;
+ if (old_value)
+ *old_value = entry->value;
+
+ if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
+ entry->key = key;
+ entry->value = value;
+ return 0;
+ } else if (strategy == HASHMAP_ADD) {
+ return -EEXIST;
+ }
+ }
+
+ if (strategy == HASHMAP_UPDATE)
+ return -ENOENT;
+
+ if (hashmap_needs_to_grow(map)) {
+ err = hashmap_grow(map);
+ if (err)
+ return err;
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ }
+
+ entry = malloc(sizeof(struct hashmap_entry));
+ if (!entry)
+ return -ENOMEM;
+
+ entry->key = key;
+ entry->value = value;
+ hashmap_add_entry(&map->buckets[h], entry);
+ map->sz++;
+
+ return 0;
+}
+
+bool hashmap__find(const struct hashmap *map, const void *key, void **value)
+{
+ struct hashmap_entry *entry;
+ size_t h;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (!hashmap_find_entry(map, key, h, NULL, &entry))
+ return false;
+
+ if (value)
+ *value = entry->value;
+ return true;
+}
+
+bool hashmap__delete(struct hashmap *map, const void *key,
+ const void **old_key, void **old_value)
+{
+ struct hashmap_entry **pprev, *entry;
+ size_t h;
+
+ h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+ if (!hashmap_find_entry(map, key, h, &pprev, &entry))
+ return false;
+
+ if (old_key)
+ *old_key = entry->key;
+ if (old_value)
+ *old_value = entry->value;
+
+ hashmap_del_entry(pprev, entry);
+ free(entry);
+ map->sz--;
+
+ return true;
+}
+
diff --git a/src/contrib/libbpf/bpf/hashmap.h b/src/contrib/libbpf/bpf/hashmap.h
new file mode 100644
index 0000000..bae8879
--- /dev/null
+++ b/src/contrib/libbpf/bpf/hashmap.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * Generic non-thread safe hash map implementation.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+#ifndef __LIBBPF_HASHMAP_H
+#define __LIBBPF_HASHMAP_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#ifdef __GLIBC__
+#include <bits/wordsize.h>
+#else
+#include <bits/reg.h>
+#endif
+#include "libbpf_internal.h"
+
+static inline size_t hash_bits(size_t h, int bits)
+{
+ /* shuffle bits and return requested number of upper bits */
+ return (h * 11400714819323198485llu) >> (__WORDSIZE - bits);
+}
+
+typedef size_t (*hashmap_hash_fn)(const void *key, void *ctx);
+typedef bool (*hashmap_equal_fn)(const void *key1, const void *key2, void *ctx);
+
+struct hashmap_entry {
+ const void *key;
+ void *value;
+ struct hashmap_entry *next;
+};
+
+struct hashmap {
+ hashmap_hash_fn hash_fn;
+ hashmap_equal_fn equal_fn;
+ void *ctx;
+
+ struct hashmap_entry **buckets;
+ size_t cap;
+ size_t cap_bits;
+ size_t sz;
+};
+
+#define HASHMAP_INIT(hash_fn, equal_fn, ctx) { \
+ .hash_fn = (hash_fn), \
+ .equal_fn = (equal_fn), \
+ .ctx = (ctx), \
+ .buckets = NULL, \
+ .cap = 0, \
+ .cap_bits = 0, \
+ .sz = 0, \
+}
+
+void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx);
+struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx);
+void hashmap__clear(struct hashmap *map);
+void hashmap__free(struct hashmap *map);
+
+size_t hashmap__size(const struct hashmap *map);
+size_t hashmap__capacity(const struct hashmap *map);
+
+/*
+ * Hashmap insertion strategy:
+ * - HASHMAP_ADD - only add key/value if key doesn't exist yet;
+ * - HASHMAP_SET - add key/value pair if key doesn't exist yet; otherwise,
+ * update value;
+ * - HASHMAP_UPDATE - update value, if key already exists; otherwise, do
+ * nothing and return -ENOENT;
+ * - HASHMAP_APPEND - always add key/value pair, even if key already exists.
+ * This turns hashmap into a multimap by allowing multiple values to be
+ * associated with the same key. Most useful read API for such hashmap is
+ * hashmap__for_each_key_entry() iteration. If hashmap__find() is still
+ * used, it will return last inserted key/value entry (first in a bucket
+ * chain).
+ */
+enum hashmap_insert_strategy {
+ HASHMAP_ADD,
+ HASHMAP_SET,
+ HASHMAP_UPDATE,
+ HASHMAP_APPEND,
+};
+
+/*
+ * hashmap__insert() adds key/value entry w/ various semantics, depending on
+ * provided strategy value. If a given key/value pair replaced already
+ * existing key/value pair, both old key and old value will be returned
+ * through old_key and old_value to allow calling code do proper memory
+ * management.
+ */
+int hashmap__insert(struct hashmap *map, const void *key, void *value,
+ enum hashmap_insert_strategy strategy,
+ const void **old_key, void **old_value);
+
+static inline int hashmap__add(struct hashmap *map,
+ const void *key, void *value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_ADD, NULL, NULL);
+}
+
+static inline int hashmap__set(struct hashmap *map,
+ const void *key, void *value,
+ const void **old_key, void **old_value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_SET,
+ old_key, old_value);
+}
+
+static inline int hashmap__update(struct hashmap *map,
+ const void *key, void *value,
+ const void **old_key, void **old_value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_UPDATE,
+ old_key, old_value);
+}
+
+static inline int hashmap__append(struct hashmap *map,
+ const void *key, void *value)
+{
+ return hashmap__insert(map, key, value, HASHMAP_APPEND, NULL, NULL);
+}
+
+bool hashmap__delete(struct hashmap *map, const void *key,
+ const void **old_key, void **old_value);
+
+bool hashmap__find(const struct hashmap *map, const void *key, void **value);
+
+/*
+ * hashmap__for_each_entry - iterate over all entries in hashmap
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @bkt: integer used as a bucket loop cursor
+ */
+#define hashmap__for_each_entry(map, cur, bkt) \
+ for (bkt = 0; bkt < map->cap; bkt++) \
+ for (cur = map->buckets[bkt]; cur; cur = cur->next)
+
+/*
+ * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe
+ * against removals
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @tmp: struct hashmap_entry * used as a temporary next cursor storage
+ * @bkt: integer used as a bucket loop cursor
+ */
+#define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \
+ for (bkt = 0; bkt < map->cap; bkt++) \
+ for (cur = map->buckets[bkt]; \
+ cur && ({tmp = cur->next; true; }); \
+ cur = tmp)
+
+/*
+ * hashmap__for_each_key_entry - iterate over entries associated with given key
+ * @map: hashmap to iterate
+ * @cur: struct hashmap_entry * used as a loop cursor
+ * @key: key to iterate entries for
+ */
+#define hashmap__for_each_key_entry(map, cur, _key) \
+ for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
+ map->cap_bits); \
+ map->buckets ? map->buckets[bkt] : NULL; }); \
+ cur; \
+ cur = cur->next) \
+ if (map->equal_fn(cur->key, (_key), map->ctx))
+
+#define hashmap__for_each_key_entry_safe(map, cur, tmp, _key) \
+ for (cur = ({ size_t bkt = hash_bits(map->hash_fn((_key), map->ctx),\
+ map->cap_bits); \
+ cur = map->buckets ? map->buckets[bkt] : NULL; }); \
+ cur && ({ tmp = cur->next; true; }); \
+ cur = tmp) \
+ if (map->equal_fn(cur->key, (_key), map->ctx))
+
+#endif /* __LIBBPF_HASHMAP_H */
diff --git a/src/contrib/libbpf/bpf/libbpf.c b/src/contrib/libbpf/bpf/libbpf.c
new file mode 100644
index 0000000..29d8d03
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf.c
@@ -0,0 +1,6581 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ * Copyright (C) 2017 Nicira, Inc.
+ * Copyright (C) 2019 Isovalent, Inc.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <libgen.h>
+#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <asm/unistd.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/filter.h>
+#include <linux/list.h>
+#include <linux/limits.h>
+#include <linux/perf_event.h>
+#include <linux/ring_buffer.h>
+#include <linux/version.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <sys/utsname.h>
+#include <libelf.h>
+#include <gelf.h>
+
+#include "libbpf.h"
+#include "bpf.h"
+#include "btf.h"
+#include "str_error.h"
+#include "libbpf_internal.h"
+#include "hashmap.h"
+
+#ifndef EM_BPF
+#define EM_BPF 247
+#endif
+
+#ifndef BPF_FS_MAGIC
+#define BPF_FS_MAGIC 0xcafe4a11
+#endif
+
+/* vsprintf() in __base_pr() uses nonliteral format string. It may break
+ * compilation if user enables corresponding warning. Disable it explicitly.
+ */
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+#define __printf(a, b) __attribute__((format(printf, a, b)))
+
+static int __base_pr(enum libbpf_print_level level, const char *format,
+ va_list args)
+{
+ if (level == LIBBPF_DEBUG)
+ return 0;
+
+ return vfprintf(stderr, format, args);
+}
+
+static libbpf_print_fn_t __libbpf_pr = __base_pr;
+
+libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn)
+{
+ libbpf_print_fn_t old_print_fn = __libbpf_pr;
+
+ __libbpf_pr = fn;
+ return old_print_fn;
+}
+
+__printf(2, 3)
+void libbpf_print(enum libbpf_print_level level, const char *format, ...)
+{
+ va_list args;
+
+ if (!__libbpf_pr)
+ return;
+
+ va_start(args, format);
+ __libbpf_pr(level, format, args);
+ va_end(args);
+}
+
+#define STRERR_BUFSIZE 128
+
+#define CHECK_ERR(action, err, out) do { \
+ err = action; \
+ if (err) \
+ goto out; \
+} while (0)
+
+
+/* Copied from tools/perf/util/util.h */
+#ifndef zfree
+# define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
+#endif
+
+#ifndef zclose
+# define zclose(fd) ({ \
+ int ___err = 0; \
+ if ((fd) >= 0) \
+ ___err = close((fd)); \
+ fd = -1; \
+ ___err; })
+#endif
+
+#ifdef HAVE_LIBELF_MMAP_SUPPORT
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP
+#else
+# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
+#endif
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
+struct bpf_capabilities {
+ /* v4.14: kernel support for program & map names. */
+ __u32 name:1;
+ /* v5.2: kernel support for global data sections. */
+ __u32 global_data:1;
+ /* BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO support */
+ __u32 btf_func:1;
+ /* BTF_KIND_VAR and BTF_KIND_DATASEC support */
+ __u32 btf_datasec:1;
+ /* BPF_F_MMAPABLE is supported for arrays */
+ __u32 array_mmap:1;
+};
+
+/*
+ * bpf_prog should be a better name but it has been used in
+ * linux/filter.h.
+ */
+struct bpf_program {
+ /* Index in elf obj file, for relocation use. */
+ int idx;
+ char *name;
+ int prog_ifindex;
+ char *section_name;
+ /* section_name with / replaced by _; makes recursive pinning
+ * in bpf_object__pin_programs easier
+ */
+ char *pin_name;
+ struct bpf_insn *insns;
+ size_t insns_cnt, main_prog_cnt;
+ enum bpf_prog_type type;
+
+ struct reloc_desc {
+ enum {
+ RELO_LD64,
+ RELO_CALL,
+ RELO_DATA,
+ } type;
+ int insn_idx;
+ int map_idx;
+ int sym_off;
+ } *reloc_desc;
+ int nr_reloc;
+ int log_level;
+
+ struct {
+ int nr;
+ int *fds;
+ } instances;
+ bpf_program_prep_t preprocessor;
+
+ struct bpf_object *obj;
+ void *priv;
+ bpf_program_clear_priv_t clear_priv;
+
+ enum bpf_attach_type expected_attach_type;
+ __u32 attach_btf_id;
+ __u32 attach_prog_fd;
+ void *func_info;
+ __u32 func_info_rec_size;
+ __u32 func_info_cnt;
+
+ struct bpf_capabilities *caps;
+
+ void *line_info;
+ __u32 line_info_rec_size;
+ __u32 line_info_cnt;
+ __u32 prog_flags;
+};
+
+enum libbpf_map_type {
+ LIBBPF_MAP_UNSPEC,
+ LIBBPF_MAP_DATA,
+ LIBBPF_MAP_BSS,
+ LIBBPF_MAP_RODATA,
+};
+
+static const char * const libbpf_type_to_btf_name[] = {
+ [LIBBPF_MAP_DATA] = ".data",
+ [LIBBPF_MAP_BSS] = ".bss",
+ [LIBBPF_MAP_RODATA] = ".rodata",
+};
+
+struct bpf_map {
+ int fd;
+ char *name;
+ int sec_idx;
+ size_t sec_offset;
+ int map_ifindex;
+ int inner_map_fd;
+ struct bpf_map_def def;
+ __u32 btf_key_type_id;
+ __u32 btf_value_type_id;
+ void *priv;
+ bpf_map_clear_priv_t clear_priv;
+ enum libbpf_map_type libbpf_type;
+ char *pin_path;
+ bool pinned;
+ bool reused;
+};
+
+struct bpf_secdata {
+ void *rodata;
+ void *data;
+};
+
+static LIST_HEAD(bpf_objects_list);
+
+struct bpf_object {
+ char name[BPF_OBJ_NAME_LEN];
+ char license[64];
+ __u32 kern_version;
+
+ struct bpf_program *programs;
+ size_t nr_programs;
+ struct bpf_map *maps;
+ size_t nr_maps;
+ size_t maps_cap;
+ struct bpf_secdata sections;
+
+ bool loaded;
+ bool has_pseudo_calls;
+ bool relaxed_core_relocs;
+
+ /*
+ * Information when doing elf related work. Only valid if fd
+ * is valid.
+ */
+ struct {
+ int fd;
+ const void *obj_buf;
+ size_t obj_buf_sz;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ Elf_Data *symbols;
+ Elf_Data *data;
+ Elf_Data *rodata;
+ Elf_Data *bss;
+ size_t strtabidx;
+ struct {
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ } *reloc_sects;
+ int nr_reloc_sects;
+ int maps_shndx;
+ int btf_maps_shndx;
+ int text_shndx;
+ int data_shndx;
+ int rodata_shndx;
+ int bss_shndx;
+ } efile;
+ /*
+ * All loaded bpf_object is linked in a list, which is
+ * hidden to caller. bpf_objects__<func> handlers deal with
+ * all objects.
+ */
+ struct list_head list;
+
+ struct btf *btf;
+ struct btf_ext *btf_ext;
+
+ void *priv;
+ bpf_object_clear_priv_t clear_priv;
+
+ struct bpf_capabilities caps;
+
+ char path[];
+};
+#define obj_elf_valid(o) ((o)->efile.elf)
+
+void bpf_program__unload(struct bpf_program *prog)
+{
+ int i;
+
+ if (!prog)
+ return;
+
+ /*
+ * If the object is opened but the program was never loaded,
+ * it is possible that prog->instances.nr == -1.
+ */
+ if (prog->instances.nr > 0) {
+ for (i = 0; i < prog->instances.nr; i++)
+ zclose(prog->instances.fds[i]);
+ } else if (prog->instances.nr != -1) {
+ pr_warn("Internal error: instances.nr is %d\n",
+ prog->instances.nr);
+ }
+
+ prog->instances.nr = -1;
+ zfree(&prog->instances.fds);
+
+ zfree(&prog->func_info);
+ zfree(&prog->line_info);
+}
+
+static void bpf_program__exit(struct bpf_program *prog)
+{
+ if (!prog)
+ return;
+
+ if (prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = NULL;
+ prog->clear_priv = NULL;
+
+ bpf_program__unload(prog);
+ zfree(&prog->name);
+ zfree(&prog->section_name);
+ zfree(&prog->pin_name);
+ zfree(&prog->insns);
+ zfree(&prog->reloc_desc);
+
+ prog->nr_reloc = 0;
+ prog->insns_cnt = 0;
+ prog->idx = -1;
+}
+
+static char *__bpf_program__pin_name(struct bpf_program *prog)
+{
+ char *name, *p;
+
+ name = p = strdup(prog->section_name);
+ while ((p = strchr(p, '/')))
+ *p = '_';
+
+ return name;
+}
+
+static int
+bpf_program__init(void *data, size_t size, char *section_name, int idx,
+ struct bpf_program *prog)
+{
+ const size_t bpf_insn_sz = sizeof(struct bpf_insn);
+
+ if (size == 0 || size % bpf_insn_sz) {
+ pr_warn("corrupted section '%s', size: %zu\n",
+ section_name, size);
+ return -EINVAL;
+ }
+
+ memset(prog, 0, sizeof(*prog));
+
+ prog->section_name = strdup(section_name);
+ if (!prog->section_name) {
+ pr_warn("failed to alloc name for prog under section(%d) %s\n",
+ idx, section_name);
+ goto errout;
+ }
+
+ prog->pin_name = __bpf_program__pin_name(prog);
+ if (!prog->pin_name) {
+ pr_warn("failed to alloc pin name for prog under section(%d) %s\n",
+ idx, section_name);
+ goto errout;
+ }
+
+ prog->insns = malloc(size);
+ if (!prog->insns) {
+ pr_warn("failed to alloc insns for prog under section %s\n",
+ section_name);
+ goto errout;
+ }
+ prog->insns_cnt = size / bpf_insn_sz;
+ memcpy(prog->insns, data, size);
+ prog->idx = idx;
+ prog->instances.fds = NULL;
+ prog->instances.nr = -1;
+ prog->type = BPF_PROG_TYPE_UNSPEC;
+
+ return 0;
+errout:
+ bpf_program__exit(prog);
+ return -ENOMEM;
+}
+
+static int
+bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
+ char *section_name, int idx)
+{
+ struct bpf_program prog, *progs;
+ int nr_progs, err;
+
+ err = bpf_program__init(data, size, section_name, idx, &prog);
+ if (err)
+ return err;
+
+ prog.caps = &obj->caps;
+ progs = obj->programs;
+ nr_progs = obj->nr_programs;
+
+ progs = reallocarray(progs, nr_progs + 1, sizeof(progs[0]));
+ if (!progs) {
+ /*
+ * In this case the original obj->programs
+ * is still valid, so don't need special treat for
+ * bpf_close_object().
+ */
+ pr_warn("failed to alloc a new program under section '%s'\n",
+ section_name);
+ bpf_program__exit(&prog);
+ return -ENOMEM;
+ }
+
+ pr_debug("found program %s\n", prog.section_name);
+ obj->programs = progs;
+ obj->nr_programs = nr_progs + 1;
+ prog.obj = obj;
+ progs[nr_progs] = prog;
+ return 0;
+}
+
+static int
+bpf_object__init_prog_names(struct bpf_object *obj)
+{
+ Elf_Data *symbols = obj->efile.symbols;
+ struct bpf_program *prog;
+ size_t pi, si;
+
+ for (pi = 0; pi < obj->nr_programs; pi++) {
+ const char *name = NULL;
+
+ prog = &obj->programs[pi];
+
+ for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name;
+ si++) {
+ GElf_Sym sym;
+
+ if (!gelf_getsym(symbols, si, &sym))
+ continue;
+ if (sym.st_shndx != prog->idx)
+ continue;
+ if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL)
+ continue;
+
+ name = elf_strptr(obj->efile.elf,
+ obj->efile.strtabidx,
+ sym.st_name);
+ if (!name) {
+ pr_warn("failed to get sym name string for prog %s\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__LIBELF;
+ }
+ }
+
+ if (!name && prog->idx == obj->efile.text_shndx)
+ name = ".text";
+
+ if (!name) {
+ pr_warn("failed to find sym for prog %s\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+
+ prog->name = strdup(name);
+ if (!prog->name) {
+ pr_warn("failed to allocate memory for prog sym %s\n",
+ name);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static __u32 get_kernel_version(void)
+{
+ __u32 major, minor, patch;
+ struct utsname info;
+
+ uname(&info);
+ if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
+ return 0;
+ return KERNEL_VERSION(major, minor, patch);
+}
+
+static struct bpf_object *bpf_object__new(const char *path,
+ const void *obj_buf,
+ size_t obj_buf_sz,
+ const char *obj_name)
+{
+ struct bpf_object *obj;
+ char *end;
+
+ obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
+ if (!obj) {
+ pr_warn("alloc memory failed for %s\n", path);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ strcpy(obj->path, path);
+ if (obj_name) {
+ strncpy(obj->name, obj_name, sizeof(obj->name) - 1);
+ obj->name[sizeof(obj->name) - 1] = 0;
+ } else {
+ /* Using basename() GNU version which doesn't modify arg. */
+ strncpy(obj->name, basename((void *)path),
+ sizeof(obj->name) - 1);
+ end = strchr(obj->name, '.');
+ if (end)
+ *end = 0;
+ }
+
+ obj->efile.fd = -1;
+ /*
+ * Caller of this function should also call
+ * bpf_object__elf_finish() after data collection to return
+ * obj_buf to user. If not, we should duplicate the buffer to
+ * avoid user freeing them before elf finish.
+ */
+ obj->efile.obj_buf = obj_buf;
+ obj->efile.obj_buf_sz = obj_buf_sz;
+ obj->efile.maps_shndx = -1;
+ obj->efile.btf_maps_shndx = -1;
+ obj->efile.data_shndx = -1;
+ obj->efile.rodata_shndx = -1;
+ obj->efile.bss_shndx = -1;
+
+ obj->kern_version = get_kernel_version();
+ obj->loaded = false;
+
+ INIT_LIST_HEAD(&obj->list);
+ list_add(&obj->list, &bpf_objects_list);
+ return obj;
+}
+
+static void bpf_object__elf_finish(struct bpf_object *obj)
+{
+ if (!obj_elf_valid(obj))
+ return;
+
+ if (obj->efile.elf) {
+ elf_end(obj->efile.elf);
+ obj->efile.elf = NULL;
+ }
+ obj->efile.symbols = NULL;
+ obj->efile.data = NULL;
+ obj->efile.rodata = NULL;
+ obj->efile.bss = NULL;
+
+ zfree(&obj->efile.reloc_sects);
+ obj->efile.nr_reloc_sects = 0;
+ zclose(obj->efile.fd);
+ obj->efile.obj_buf = NULL;
+ obj->efile.obj_buf_sz = 0;
+}
+
+static int bpf_object__elf_init(struct bpf_object *obj)
+{
+ int err = 0;
+ GElf_Ehdr *ep;
+
+ if (obj_elf_valid(obj)) {
+ pr_warn("elf init: internal error\n");
+ return -LIBBPF_ERRNO__LIBELF;
+ }
+
+ if (obj->efile.obj_buf_sz > 0) {
+ /*
+ * obj_buf should have been validated by
+ * bpf_object__open_buffer().
+ */
+ obj->efile.elf = elf_memory((char *)obj->efile.obj_buf,
+ obj->efile.obj_buf_sz);
+ } else {
+ obj->efile.fd = open(obj->path, O_RDONLY);
+ if (obj->efile.fd < 0) {
+ char errmsg[STRERR_BUFSIZE], *cp;
+
+ err = -errno;
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+ pr_warn("failed to open %s: %s\n", obj->path, cp);
+ return err;
+ }
+
+ obj->efile.elf = elf_begin(obj->efile.fd,
+ LIBBPF_ELF_C_READ_MMAP, NULL);
+ }
+
+ if (!obj->efile.elf) {
+ pr_warn("failed to open %s as ELF file\n", obj->path);
+ err = -LIBBPF_ERRNO__LIBELF;
+ goto errout;
+ }
+
+ if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
+ pr_warn("failed to get EHDR from %s\n", obj->path);
+ err = -LIBBPF_ERRNO__FORMAT;
+ goto errout;
+ }
+ ep = &obj->efile.ehdr;
+
+ /* Old LLVM set e_machine to EM_NONE */
+ if (ep->e_type != ET_REL ||
+ (ep->e_machine && ep->e_machine != EM_BPF)) {
+ pr_warn("%s is not an eBPF object file\n", obj->path);
+ err = -LIBBPF_ERRNO__FORMAT;
+ goto errout;
+ }
+
+ return 0;
+errout:
+ bpf_object__elf_finish(obj);
+ return err;
+}
+
+static int bpf_object__check_endianness(struct bpf_object *obj)
+{
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2LSB)
+ return 0;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ if (obj->efile.ehdr.e_ident[EI_DATA] == ELFDATA2MSB)
+ return 0;
+#else
+# error "Unrecognized __BYTE_ORDER__"
+#endif
+ pr_warn("endianness mismatch.\n");
+ return -LIBBPF_ERRNO__ENDIAN;
+}
+
+static int
+bpf_object__init_license(struct bpf_object *obj, void *data, size_t size)
+{
+ memcpy(obj->license, data, min(size, sizeof(obj->license) - 1));
+ pr_debug("license of %s is %s\n", obj->path, obj->license);
+ return 0;
+}
+
+static int
+bpf_object__init_kversion(struct bpf_object *obj, void *data, size_t size)
+{
+ __u32 kver;
+
+ if (size != sizeof(kver)) {
+ pr_warn("invalid kver section in %s\n", obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ memcpy(&kver, data, sizeof(kver));
+ obj->kern_version = kver;
+ pr_debug("kernel version of %s is %x\n", obj->path, obj->kern_version);
+ return 0;
+}
+
+static int compare_bpf_map(const void *_a, const void *_b)
+{
+ const struct bpf_map *a = _a;
+ const struct bpf_map *b = _b;
+
+ if (a->sec_idx != b->sec_idx)
+ return a->sec_idx - b->sec_idx;
+ return a->sec_offset - b->sec_offset;
+}
+
+static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
+{
+ if (type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+ type == BPF_MAP_TYPE_HASH_OF_MAPS)
+ return true;
+ return false;
+}
+
+static int bpf_object_search_section_size(const struct bpf_object *obj,
+ const char *name, size_t *d_size)
+{
+ const GElf_Ehdr *ep = &obj->efile.ehdr;
+ Elf *elf = obj->efile.elf;
+ Elf_Scn *scn = NULL;
+ int idx = 0;
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ const char *sec_name;
+ Elf_Data *data;
+ GElf_Shdr sh;
+
+ idx++;
+ if (gelf_getshdr(scn, &sh) != &sh) {
+ pr_warn("failed to get section(%d) header from %s\n",
+ idx, obj->path);
+ return -EIO;
+ }
+
+ sec_name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
+ if (!sec_name) {
+ pr_warn("failed to get section(%d) name from %s\n",
+ idx, obj->path);
+ return -EIO;
+ }
+
+ if (strcmp(name, sec_name))
+ continue;
+
+ data = elf_getdata(scn, 0);
+ if (!data) {
+ pr_warn("failed to get section(%d) data from %s(%s)\n",
+ idx, name, obj->path);
+ return -EIO;
+ }
+
+ *d_size = data->d_size;
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int bpf_object__section_size(const struct bpf_object *obj, const char *name,
+ __u32 *size)
+{
+ int ret = -ENOENT;
+ size_t d_size;
+
+ *size = 0;
+ if (!name) {
+ return -EINVAL;
+ } else if (!strcmp(name, ".data")) {
+ if (obj->efile.data)
+ *size = obj->efile.data->d_size;
+ } else if (!strcmp(name, ".bss")) {
+ if (obj->efile.bss)
+ *size = obj->efile.bss->d_size;
+ } else if (!strcmp(name, ".rodata")) {
+ if (obj->efile.rodata)
+ *size = obj->efile.rodata->d_size;
+ } else {
+ ret = bpf_object_search_section_size(obj, name, &d_size);
+ if (!ret)
+ *size = d_size;
+ }
+
+ return *size ? 0 : ret;
+}
+
+int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
+ __u32 *off)
+{
+ Elf_Data *symbols = obj->efile.symbols;
+ const char *sname;
+ size_t si;
+
+ if (!name || !off)
+ return -EINVAL;
+
+ for (si = 0; si < symbols->d_size / sizeof(GElf_Sym); si++) {
+ GElf_Sym sym;
+
+ if (!gelf_getsym(symbols, si, &sym))
+ continue;
+ if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL ||
+ GELF_ST_TYPE(sym.st_info) != STT_OBJECT)
+ continue;
+
+ sname = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
+ sym.st_name);
+ if (!sname) {
+ pr_warn("failed to get sym name string for var %s\n",
+ name);
+ return -EIO;
+ }
+ if (strcmp(name, sname) == 0) {
+ *off = sym.st_value;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+
+static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
+{
+ struct bpf_map *new_maps;
+ size_t new_cap;
+ int i;
+
+ if (obj->nr_maps < obj->maps_cap)
+ return &obj->maps[obj->nr_maps++];
+
+ new_cap = max((size_t)4, obj->maps_cap * 3 / 2);
+ new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
+ if (!new_maps) {
+ pr_warn("alloc maps for object failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ obj->maps_cap = new_cap;
+ obj->maps = new_maps;
+
+ /* zero out new maps */
+ memset(obj->maps + obj->nr_maps, 0,
+ (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps));
+ /*
+ * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin)
+ * when failure (zclose won't close negative fd)).
+ */
+ for (i = obj->nr_maps; i < obj->maps_cap; i++) {
+ obj->maps[i].fd = -1;
+ obj->maps[i].inner_map_fd = -1;
+ }
+
+ return &obj->maps[obj->nr_maps++];
+}
+
+static int
+bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
+ int sec_idx, Elf_Data *data, void **data_buff)
+{
+ char map_name[BPF_OBJ_NAME_LEN];
+ struct bpf_map_def *def;
+ struct bpf_map *map;
+
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ map->libbpf_type = type;
+ map->sec_idx = sec_idx;
+ map->sec_offset = 0;
+ snprintf(map_name, sizeof(map_name), "%.8s%.7s", obj->name,
+ libbpf_type_to_btf_name[type]);
+ map->name = strdup(map_name);
+ if (!map->name) {
+ pr_warn("failed to alloc map name\n");
+ return -ENOMEM;
+ }
+
+ def = &map->def;
+ def->type = BPF_MAP_TYPE_ARRAY;
+ def->key_size = sizeof(int);
+ def->value_size = data->d_size;
+ def->max_entries = 1;
+ def->map_flags = type == LIBBPF_MAP_RODATA ? BPF_F_RDONLY_PROG : 0;
+ if (obj->caps.array_mmap)
+ def->map_flags |= BPF_F_MMAPABLE;
+
+ pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
+ map_name, map->sec_idx, map->sec_offset, def->map_flags);
+
+ if (data_buff) {
+ *data_buff = malloc(data->d_size);
+ if (!*data_buff) {
+ zfree(&map->name);
+ pr_warn("failed to alloc map content buffer\n");
+ return -ENOMEM;
+ }
+ memcpy(*data_buff, data->d_buf, data->d_size);
+ }
+
+ pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name);
+ return 0;
+}
+
+static int bpf_object__init_global_data_maps(struct bpf_object *obj)
+{
+ int err;
+
+ if (!obj->caps.global_data)
+ return 0;
+ /*
+ * Populate obj->maps with libbpf internal maps.
+ */
+ if (obj->efile.data_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
+ obj->efile.data_shndx,
+ obj->efile.data,
+ &obj->sections.data);
+ if (err)
+ return err;
+ }
+ if (obj->efile.rodata_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
+ obj->efile.rodata_shndx,
+ obj->efile.rodata,
+ &obj->sections.rodata);
+ if (err)
+ return err;
+ }
+ if (obj->efile.bss_shndx >= 0) {
+ err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
+ obj->efile.bss_shndx,
+ obj->efile.bss, NULL);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
+{
+ Elf_Data *symbols = obj->efile.symbols;
+ int i, map_def_sz = 0, nr_maps = 0, nr_syms;
+ Elf_Data *data = NULL;
+ Elf_Scn *scn;
+
+ if (obj->efile.maps_shndx < 0)
+ return 0;
+
+ if (!symbols)
+ return -EINVAL;
+
+ scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
+ if (scn)
+ data = elf_getdata(scn, NULL);
+ if (!scn || !data) {
+ pr_warn("failed to get Elf_Data from map section %d\n",
+ obj->efile.maps_shndx);
+ return -EINVAL;
+ }
+
+ /*
+ * Count number of maps. Each map has a name.
+ * Array of maps is not supported: only the first element is
+ * considered.
+ *
+ * TODO: Detect array of map and report error.
+ */
+ nr_syms = symbols->d_size / sizeof(GElf_Sym);
+ for (i = 0; i < nr_syms; i++) {
+ GElf_Sym sym;
+
+ if (!gelf_getsym(symbols, i, &sym))
+ continue;
+ if (sym.st_shndx != obj->efile.maps_shndx)
+ continue;
+ nr_maps++;
+ }
+ /* Assume equally sized map definitions */
+ pr_debug("maps in %s: %d maps in %zd bytes\n",
+ obj->path, nr_maps, data->d_size);
+
+ if (!data->d_size || nr_maps == 0 || (data->d_size % nr_maps) != 0) {
+ pr_warn("unable to determine map definition size section %s, %d maps in %zd bytes\n",
+ obj->path, nr_maps, data->d_size);
+ return -EINVAL;
+ }
+ map_def_sz = data->d_size / nr_maps;
+
+ /* Fill obj->maps using data in "maps" section. */
+ for (i = 0; i < nr_syms; i++) {
+ GElf_Sym sym;
+ const char *map_name;
+ struct bpf_map_def *def;
+ struct bpf_map *map;
+
+ if (!gelf_getsym(symbols, i, &sym))
+ continue;
+ if (sym.st_shndx != obj->efile.maps_shndx)
+ continue;
+
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+
+ map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
+ sym.st_name);
+ if (!map_name) {
+ pr_warn("failed to get map #%d name sym string for obj %s\n",
+ i, obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+
+ map->libbpf_type = LIBBPF_MAP_UNSPEC;
+ map->sec_idx = sym.st_shndx;
+ map->sec_offset = sym.st_value;
+ pr_debug("map '%s' (legacy): at sec_idx %d, offset %zu.\n",
+ map_name, map->sec_idx, map->sec_offset);
+ if (sym.st_value + map_def_sz > data->d_size) {
+ pr_warn("corrupted maps section in %s: last map \"%s\" too small\n",
+ obj->path, map_name);
+ return -EINVAL;
+ }
+
+ map->name = strdup(map_name);
+ if (!map->name) {
+ pr_warn("failed to alloc map name\n");
+ return -ENOMEM;
+ }
+ pr_debug("map %d is \"%s\"\n", i, map->name);
+ def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
+ /*
+ * If the definition of the map in the object file fits in
+ * bpf_map_def, copy it. Any extra fields in our version
+ * of bpf_map_def will default to zero as a result of the
+ * calloc above.
+ */
+ if (map_def_sz <= sizeof(struct bpf_map_def)) {
+ memcpy(&map->def, def, map_def_sz);
+ } else {
+ /*
+ * Here the map structure being read is bigger than what
+ * we expect, truncate if the excess bits are all zero.
+ * If they are not zero, reject this map as
+ * incompatible.
+ */
+ char *b;
+
+ for (b = ((char *)def) + sizeof(struct bpf_map_def);
+ b < ((char *)def) + map_def_sz; b++) {
+ if (*b != 0) {
+ pr_warn("maps section in %s: \"%s\" has unrecognized, non-zero options\n",
+ obj->path, map_name);
+ if (strict)
+ return -EINVAL;
+ }
+ }
+ memcpy(&map->def, def, sizeof(struct bpf_map_def));
+ }
+ }
+ return 0;
+}
+
+static const struct btf_type *
+skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id)
+{
+ const struct btf_type *t = btf__type_by_id(btf, id);
+
+ if (res_id)
+ *res_id = id;
+
+ while (btf_is_mod(t) || btf_is_typedef(t)) {
+ if (res_id)
+ *res_id = t->type;
+ t = btf__type_by_id(btf, t->type);
+ }
+
+ return t;
+}
+
+/*
+ * Fetch integer attribute of BTF map definition. Such attributes are
+ * represented using a pointer to an array, in which dimensionality of array
+ * encodes specified integer value. E.g., int (*type)[BPF_MAP_TYPE_ARRAY];
+ * encodes `type => BPF_MAP_TYPE_ARRAY` key/value pair completely using BTF
+ * type definition, while using only sizeof(void *) space in ELF data section.
+ */
+static bool get_map_field_int(const char *map_name, const struct btf *btf,
+ const struct btf_type *def,
+ const struct btf_member *m, __u32 *res)
+{
+ const struct btf_type *t = skip_mods_and_typedefs(btf, m->type, NULL);
+ const char *name = btf__name_by_offset(btf, m->name_off);
+ const struct btf_array *arr_info;
+ const struct btf_type *arr_t;
+
+ if (!btf_is_ptr(t)) {
+ pr_warn("map '%s': attr '%s': expected PTR, got %u.\n",
+ map_name, name, btf_kind(t));
+ return false;
+ }
+
+ arr_t = btf__type_by_id(btf, t->type);
+ if (!arr_t) {
+ pr_warn("map '%s': attr '%s': type [%u] not found.\n",
+ map_name, name, t->type);
+ return false;
+ }
+ if (!btf_is_array(arr_t)) {
+ pr_warn("map '%s': attr '%s': expected ARRAY, got %u.\n",
+ map_name, name, btf_kind(arr_t));
+ return false;
+ }
+ arr_info = btf_array(arr_t);
+ *res = arr_info->nelems;
+ return true;
+}
+
+static int build_map_pin_path(struct bpf_map *map, const char *path)
+{
+ char buf[PATH_MAX];
+ int err, len;
+
+ if (!path)
+ path = "/sys/fs/bpf";
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map));
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_map__set_pin_path(map, buf);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int bpf_object__init_user_btf_map(struct bpf_object *obj,
+ const struct btf_type *sec,
+ int var_idx, int sec_idx,
+ const Elf_Data *data, bool strict,
+ const char *pin_root_path)
+{
+ const struct btf_type *var, *def, *t;
+ const struct btf_var_secinfo *vi;
+ const struct btf_var *var_extra;
+ const struct btf_member *m;
+ const char *map_name;
+ struct bpf_map *map;
+ int vlen, i;
+
+ vi = btf_var_secinfos(sec) + var_idx;
+ var = btf__type_by_id(obj->btf, vi->type);
+ var_extra = btf_var(var);
+ map_name = btf__name_by_offset(obj->btf, var->name_off);
+ vlen = btf_vlen(var);
+
+ if (map_name == NULL || map_name[0] == '\0') {
+ pr_warn("map #%d: empty name.\n", var_idx);
+ return -EINVAL;
+ }
+ if ((__u64)vi->offset + vi->size > data->d_size) {
+ pr_warn("map '%s' BTF data is corrupted.\n", map_name);
+ return -EINVAL;
+ }
+ if (!btf_is_var(var)) {
+ pr_warn("map '%s': unexpected var kind %u.\n",
+ map_name, btf_kind(var));
+ return -EINVAL;
+ }
+ if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED &&
+ var_extra->linkage != BTF_VAR_STATIC) {
+ pr_warn("map '%s': unsupported var linkage %u.\n",
+ map_name, var_extra->linkage);
+ return -EOPNOTSUPP;
+ }
+
+ def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
+ if (!btf_is_struct(def)) {
+ pr_warn("map '%s': unexpected def kind %u.\n",
+ map_name, btf_kind(var));
+ return -EINVAL;
+ }
+ if (def->size > vi->size) {
+ pr_warn("map '%s': invalid def size.\n", map_name);
+ return -EINVAL;
+ }
+
+ map = bpf_object__add_map(obj);
+ if (IS_ERR(map))
+ return PTR_ERR(map);
+ map->name = strdup(map_name);
+ if (!map->name) {
+ pr_warn("map '%s': failed to alloc map name.\n", map_name);
+ return -ENOMEM;
+ }
+ map->libbpf_type = LIBBPF_MAP_UNSPEC;
+ map->def.type = BPF_MAP_TYPE_UNSPEC;
+ map->sec_idx = sec_idx;
+ map->sec_offset = vi->offset;
+ pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
+ map_name, map->sec_idx, map->sec_offset);
+
+ vlen = btf_vlen(def);
+ m = btf_members(def);
+ for (i = 0; i < vlen; i++, m++) {
+ const char *name = btf__name_by_offset(obj->btf, m->name_off);
+
+ if (!name) {
+ pr_warn("map '%s': invalid field #%d.\n", map_name, i);
+ return -EINVAL;
+ }
+ if (strcmp(name, "type") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.type))
+ return -EINVAL;
+ pr_debug("map '%s': found type = %u.\n",
+ map_name, map->def.type);
+ } else if (strcmp(name, "max_entries") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.max_entries))
+ return -EINVAL;
+ pr_debug("map '%s': found max_entries = %u.\n",
+ map_name, map->def.max_entries);
+ } else if (strcmp(name, "map_flags") == 0) {
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &map->def.map_flags))
+ return -EINVAL;
+ pr_debug("map '%s': found map_flags = %u.\n",
+ map_name, map->def.map_flags);
+ } else if (strcmp(name, "key_size") == 0) {
+ __u32 sz;
+
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &sz))
+ return -EINVAL;
+ pr_debug("map '%s': found key_size = %u.\n",
+ map_name, sz);
+ if (map->def.key_size && map->def.key_size != sz) {
+ pr_warn("map '%s': conflicting key size %u != %u.\n",
+ map_name, map->def.key_size, sz);
+ return -EINVAL;
+ }
+ map->def.key_size = sz;
+ } else if (strcmp(name, "key") == 0) {
+ __s64 sz;
+
+ t = btf__type_by_id(obj->btf, m->type);
+ if (!t) {
+ pr_warn("map '%s': key type [%d] not found.\n",
+ map_name, m->type);
+ return -EINVAL;
+ }
+ if (!btf_is_ptr(t)) {
+ pr_warn("map '%s': key spec is not PTR: %u.\n",
+ map_name, btf_kind(t));
+ return -EINVAL;
+ }
+ sz = btf__resolve_size(obj->btf, t->type);
+ if (sz < 0) {
+ pr_warn("map '%s': can't determine key size for type [%u]: %lld.\n",
+ map_name, t->type, sz);
+ return sz;
+ }
+ pr_debug("map '%s': found key [%u], sz = %lld.\n",
+ map_name, t->type, sz);
+ if (map->def.key_size && map->def.key_size != sz) {
+ pr_warn("map '%s': conflicting key size %u != %lld.\n",
+ map_name, map->def.key_size, sz);
+ return -EINVAL;
+ }
+ map->def.key_size = sz;
+ map->btf_key_type_id = t->type;
+ } else if (strcmp(name, "value_size") == 0) {
+ __u32 sz;
+
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &sz))
+ return -EINVAL;
+ pr_debug("map '%s': found value_size = %u.\n",
+ map_name, sz);
+ if (map->def.value_size && map->def.value_size != sz) {
+ pr_warn("map '%s': conflicting value size %u != %u.\n",
+ map_name, map->def.value_size, sz);
+ return -EINVAL;
+ }
+ map->def.value_size = sz;
+ } else if (strcmp(name, "value") == 0) {
+ __s64 sz;
+
+ t = btf__type_by_id(obj->btf, m->type);
+ if (!t) {
+ pr_warn("map '%s': value type [%d] not found.\n",
+ map_name, m->type);
+ return -EINVAL;
+ }
+ if (!btf_is_ptr(t)) {
+ pr_warn("map '%s': value spec is not PTR: %u.\n",
+ map_name, btf_kind(t));
+ return -EINVAL;
+ }
+ sz = btf__resolve_size(obj->btf, t->type);
+ if (sz < 0) {
+ pr_warn("map '%s': can't determine value size for type [%u]: %lld.\n",
+ map_name, t->type, sz);
+ return sz;
+ }
+ pr_debug("map '%s': found value [%u], sz = %lld.\n",
+ map_name, t->type, sz);
+ if (map->def.value_size && map->def.value_size != sz) {
+ pr_warn("map '%s': conflicting value size %u != %lld.\n",
+ map_name, map->def.value_size, sz);
+ return -EINVAL;
+ }
+ map->def.value_size = sz;
+ map->btf_value_type_id = t->type;
+ } else if (strcmp(name, "pinning") == 0) {
+ __u32 val;
+ int err;
+
+ if (!get_map_field_int(map_name, obj->btf, def, m,
+ &val))
+ return -EINVAL;
+ pr_debug("map '%s': found pinning = %u.\n",
+ map_name, val);
+
+ if (val != LIBBPF_PIN_NONE &&
+ val != LIBBPF_PIN_BY_NAME) {
+ pr_warn("map '%s': invalid pinning value %u.\n",
+ map_name, val);
+ return -EINVAL;
+ }
+ if (val == LIBBPF_PIN_BY_NAME) {
+ err = build_map_pin_path(map, pin_root_path);
+ if (err) {
+ pr_warn("map '%s': couldn't build pin path.\n",
+ map_name);
+ return err;
+ }
+ }
+ } else {
+ if (strict) {
+ pr_warn("map '%s': unknown field '%s'.\n",
+ map_name, name);
+ return -ENOTSUP;
+ }
+ pr_debug("map '%s': ignoring unknown field '%s'.\n",
+ map_name, name);
+ }
+ }
+
+ if (map->def.type == BPF_MAP_TYPE_UNSPEC) {
+ pr_warn("map '%s': map type isn't specified.\n", map_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
+ const char *pin_root_path)
+{
+ const struct btf_type *sec = NULL;
+ int nr_types, i, vlen, err;
+ const struct btf_type *t;
+ const char *name;
+ Elf_Data *data;
+ Elf_Scn *scn;
+
+ if (obj->efile.btf_maps_shndx < 0)
+ return 0;
+
+ scn = elf_getscn(obj->efile.elf, obj->efile.btf_maps_shndx);
+ if (scn)
+ data = elf_getdata(scn, NULL);
+ if (!scn || !data) {
+ pr_warn("failed to get Elf_Data from map section %d (%s)\n",
+ obj->efile.maps_shndx, MAPS_ELF_SEC);
+ return -EINVAL;
+ }
+
+ nr_types = btf__get_nr_types(obj->btf);
+ for (i = 1; i <= nr_types; i++) {
+ t = btf__type_by_id(obj->btf, i);
+ if (!btf_is_datasec(t))
+ continue;
+ name = btf__name_by_offset(obj->btf, t->name_off);
+ if (strcmp(name, MAPS_ELF_SEC) == 0) {
+ sec = t;
+ break;
+ }
+ }
+
+ if (!sec) {
+ pr_warn("DATASEC '%s' not found.\n", MAPS_ELF_SEC);
+ return -ENOENT;
+ }
+
+ vlen = btf_vlen(sec);
+ for (i = 0; i < vlen; i++) {
+ err = bpf_object__init_user_btf_map(obj, sec, i,
+ obj->efile.btf_maps_shndx,
+ data, strict,
+ pin_root_path);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int bpf_object__init_maps(struct bpf_object *obj, bool relaxed_maps,
+ const char *pin_root_path)
+{
+ bool strict = !relaxed_maps;
+ int err;
+
+ err = bpf_object__init_user_maps(obj, strict);
+ if (err)
+ return err;
+
+ err = bpf_object__init_user_btf_maps(obj, strict, pin_root_path);
+ if (err)
+ return err;
+
+ err = bpf_object__init_global_data_maps(obj);
+ if (err)
+ return err;
+
+ if (obj->nr_maps) {
+ qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]),
+ compare_bpf_map);
+ }
+ return 0;
+}
+
+static bool section_have_execinstr(struct bpf_object *obj, int idx)
+{
+ Elf_Scn *scn;
+ GElf_Shdr sh;
+
+ scn = elf_getscn(obj->efile.elf, idx);
+ if (!scn)
+ return false;
+
+ if (gelf_getshdr(scn, &sh) != &sh)
+ return false;
+
+ if (sh.sh_flags & SHF_EXECINSTR)
+ return true;
+
+ return false;
+}
+
+static void bpf_object__sanitize_btf(struct bpf_object *obj)
+{
+ bool has_datasec = obj->caps.btf_datasec;
+ bool has_func = obj->caps.btf_func;
+ struct btf *btf = obj->btf;
+ struct btf_type *t;
+ int i, j, vlen;
+
+ if (!obj->btf || (has_func && has_datasec))
+ return;
+
+ for (i = 1; i <= btf__get_nr_types(btf); i++) {
+ t = (struct btf_type *)btf__type_by_id(btf, i);
+
+ if (!has_datasec && btf_is_var(t)) {
+ /* replace VAR with INT */
+ t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
+ /*
+ * using size = 1 is the safest choice, 4 will be too
+ * big and cause kernel BTF validation failure if
+ * original variable took less than 4 bytes
+ */
+ t->size = 1;
+ *(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
+ } else if (!has_datasec && btf_is_datasec(t)) {
+ /* replace DATASEC with STRUCT */
+ const struct btf_var_secinfo *v = btf_var_secinfos(t);
+ struct btf_member *m = btf_members(t);
+ struct btf_type *vt;
+ char *name;
+
+ name = (char *)btf__name_by_offset(btf, t->name_off);
+ while (*name) {
+ if (*name == '.')
+ *name = '_';
+ name++;
+ }
+
+ vlen = btf_vlen(t);
+ t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen);
+ for (j = 0; j < vlen; j++, v++, m++) {
+ /* order of field assignments is important */
+ m->offset = v->offset * 8;
+ m->type = v->type;
+ /* preserve variable name as member name */
+ vt = (void *)btf__type_by_id(btf, v->type);
+ m->name_off = vt->name_off;
+ }
+ } else if (!has_func && btf_is_func_proto(t)) {
+ /* replace FUNC_PROTO with ENUM */
+ vlen = btf_vlen(t);
+ t->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen);
+ t->size = sizeof(__u32); /* kernel enforced */
+ } else if (!has_func && btf_is_func(t)) {
+ /* replace FUNC with TYPEDEF */
+ t->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0);
+ }
+ }
+}
+
+static void bpf_object__sanitize_btf_ext(struct bpf_object *obj)
+{
+ if (!obj->btf_ext)
+ return;
+
+ if (!obj->caps.btf_func) {
+ btf_ext__free(obj->btf_ext);
+ obj->btf_ext = NULL;
+ }
+}
+
+static bool bpf_object__is_btf_mandatory(const struct bpf_object *obj)
+{
+ return obj->efile.btf_maps_shndx >= 0;
+}
+
+static int bpf_object__init_btf(struct bpf_object *obj,
+ Elf_Data *btf_data,
+ Elf_Data *btf_ext_data)
+{
+ bool btf_required = bpf_object__is_btf_mandatory(obj);
+ int err = 0;
+
+ if (btf_data) {
+ obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
+ if (IS_ERR(obj->btf)) {
+ pr_warn("Error loading ELF section %s: %d.\n",
+ BTF_ELF_SEC, err);
+ goto out;
+ }
+ err = btf__finalize_data(obj, obj->btf);
+ if (err) {
+ pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err);
+ goto out;
+ }
+ }
+ if (btf_ext_data) {
+ if (!obj->btf) {
+ pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
+ BTF_EXT_ELF_SEC, BTF_ELF_SEC);
+ goto out;
+ }
+ obj->btf_ext = btf_ext__new(btf_ext_data->d_buf,
+ btf_ext_data->d_size);
+ if (IS_ERR(obj->btf_ext)) {
+ pr_warn("Error loading ELF section %s: %ld. Ignored and continue.\n",
+ BTF_EXT_ELF_SEC, PTR_ERR(obj->btf_ext));
+ obj->btf_ext = NULL;
+ goto out;
+ }
+ }
+out:
+ if (err || IS_ERR(obj->btf)) {
+ if (btf_required)
+ err = err ? : PTR_ERR(obj->btf);
+ else
+ err = 0;
+ if (!IS_ERR_OR_NULL(obj->btf))
+ btf__free(obj->btf);
+ obj->btf = NULL;
+ }
+ if (btf_required && !obj->btf) {
+ pr_warn("BTF is required, but is missing or corrupted.\n");
+ return err == 0 ? -ENOENT : err;
+ }
+ return 0;
+}
+
+static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
+{
+ int err = 0;
+
+ if (!obj->btf)
+ return 0;
+
+ bpf_object__sanitize_btf(obj);
+ bpf_object__sanitize_btf_ext(obj);
+
+ err = btf__load(obj->btf);
+ if (err) {
+ pr_warn("Error loading %s into kernel: %d.\n",
+ BTF_ELF_SEC, err);
+ btf__free(obj->btf);
+ obj->btf = NULL;
+ /* btf_ext can't exist without btf, so free it as well */
+ if (obj->btf_ext) {
+ btf_ext__free(obj->btf_ext);
+ obj->btf_ext = NULL;
+ }
+
+ if (bpf_object__is_btf_mandatory(obj))
+ return err;
+ }
+ return 0;
+}
+
+static int bpf_object__elf_collect(struct bpf_object *obj, bool relaxed_maps,
+ const char *pin_root_path)
+{
+ Elf *elf = obj->efile.elf;
+ GElf_Ehdr *ep = &obj->efile.ehdr;
+ Elf_Data *btf_ext_data = NULL;
+ Elf_Data *btf_data = NULL;
+ Elf_Scn *scn = NULL;
+ int idx = 0, err = 0;
+
+ /* Elf is corrupted/truncated, avoid calling elf_strptr. */
+ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
+ pr_warn("failed to get e_shstrndx from %s\n", obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ char *name;
+ GElf_Shdr sh;
+ Elf_Data *data;
+
+ idx++;
+ if (gelf_getshdr(scn, &sh) != &sh) {
+ pr_warn("failed to get section(%d) header from %s\n",
+ idx, obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+
+ name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
+ if (!name) {
+ pr_warn("failed to get section(%d) name from %s\n",
+ idx, obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+
+ data = elf_getdata(scn, 0);
+ if (!data) {
+ pr_warn("failed to get section(%d) data from %s(%s)\n",
+ idx, name, obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
+ idx, name, (unsigned long)data->d_size,
+ (int)sh.sh_link, (unsigned long)sh.sh_flags,
+ (int)sh.sh_type);
+
+ if (strcmp(name, "license") == 0) {
+ err = bpf_object__init_license(obj,
+ data->d_buf,
+ data->d_size);
+ if (err)
+ return err;
+ } else if (strcmp(name, "version") == 0) {
+ err = bpf_object__init_kversion(obj,
+ data->d_buf,
+ data->d_size);
+ if (err)
+ return err;
+ } else if (strcmp(name, "maps") == 0) {
+ obj->efile.maps_shndx = idx;
+ } else if (strcmp(name, MAPS_ELF_SEC) == 0) {
+ obj->efile.btf_maps_shndx = idx;
+ } else if (strcmp(name, BTF_ELF_SEC) == 0) {
+ btf_data = data;
+ } else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
+ btf_ext_data = data;
+ } else if (sh.sh_type == SHT_SYMTAB) {
+ if (obj->efile.symbols) {
+ pr_warn("bpf: multiple SYMTAB in %s\n",
+ obj->path);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ obj->efile.symbols = data;
+ obj->efile.strtabidx = sh.sh_link;
+ } else if (sh.sh_type == SHT_PROGBITS && data->d_size > 0) {
+ if (sh.sh_flags & SHF_EXECINSTR) {
+ if (strcmp(name, ".text") == 0)
+ obj->efile.text_shndx = idx;
+ err = bpf_object__add_program(obj, data->d_buf,
+ data->d_size,
+ name, idx);
+ if (err) {
+ char errmsg[STRERR_BUFSIZE];
+ char *cp;
+
+ cp = libbpf_strerror_r(-err, errmsg,
+ sizeof(errmsg));
+ pr_warn("failed to alloc program %s (%s): %s",
+ name, obj->path, cp);
+ return err;
+ }
+ } else if (strcmp(name, ".data") == 0) {
+ obj->efile.data = data;
+ obj->efile.data_shndx = idx;
+ } else if (strcmp(name, ".rodata") == 0) {
+ obj->efile.rodata = data;
+ obj->efile.rodata_shndx = idx;
+ } else {
+ pr_debug("skip section(%d) %s\n", idx, name);
+ }
+ } else if (sh.sh_type == SHT_REL) {
+ int nr_sects = obj->efile.nr_reloc_sects;
+ void *sects = obj->efile.reloc_sects;
+ int sec = sh.sh_info; /* points to other section */
+
+ /* Only do relo for section with exec instructions */
+ if (!section_have_execinstr(obj, sec)) {
+ pr_debug("skip relo %s(%d) for section(%d)\n",
+ name, idx, sec);
+ continue;
+ }
+
+ sects = reallocarray(sects, nr_sects + 1,
+ sizeof(*obj->efile.reloc_sects));
+ if (!sects) {
+ pr_warn("reloc_sects realloc failed\n");
+ return -ENOMEM;
+ }
+
+ obj->efile.reloc_sects = sects;
+ obj->efile.nr_reloc_sects++;
+
+ obj->efile.reloc_sects[nr_sects].shdr = sh;
+ obj->efile.reloc_sects[nr_sects].data = data;
+ } else if (sh.sh_type == SHT_NOBITS && strcmp(name, ".bss") == 0) {
+ obj->efile.bss = data;
+ obj->efile.bss_shndx = idx;
+ } else {
+ pr_debug("skip section(%d) %s\n", idx, name);
+ }
+ }
+
+ if (!obj->efile.strtabidx || obj->efile.strtabidx > idx) {
+ pr_warn("Corrupted ELF file: index of strtab invalid\n");
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ err = bpf_object__init_btf(obj, btf_data, btf_ext_data);
+ if (!err)
+ err = bpf_object__init_maps(obj, relaxed_maps, pin_root_path);
+ if (!err)
+ err = bpf_object__sanitize_and_load_btf(obj);
+ if (!err)
+ err = bpf_object__init_prog_names(obj);
+ return err;
+}
+
+static struct bpf_program *
+bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
+{
+ struct bpf_program *prog;
+ size_t i;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+ if (prog->idx == idx)
+ return prog;
+ }
+ return NULL;
+}
+
+struct bpf_program *
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title)
+{
+ struct bpf_program *pos;
+
+ bpf_object__for_each_program(pos, obj) {
+ if (pos->section_name && !strcmp(pos->section_name, title))
+ return pos;
+ }
+ return NULL;
+}
+
+static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
+ int shndx)
+{
+ return shndx == obj->efile.data_shndx ||
+ shndx == obj->efile.bss_shndx ||
+ shndx == obj->efile.rodata_shndx;
+}
+
+static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
+ int shndx)
+{
+ return shndx == obj->efile.maps_shndx ||
+ shndx == obj->efile.btf_maps_shndx;
+}
+
+static enum libbpf_map_type
+bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
+{
+ if (shndx == obj->efile.data_shndx)
+ return LIBBPF_MAP_DATA;
+ else if (shndx == obj->efile.bss_shndx)
+ return LIBBPF_MAP_BSS;
+ else if (shndx == obj->efile.rodata_shndx)
+ return LIBBPF_MAP_RODATA;
+ else
+ return LIBBPF_MAP_UNSPEC;
+}
+
+static int bpf_program__record_reloc(struct bpf_program *prog,
+ struct reloc_desc *reloc_desc,
+ __u32 insn_idx, const char *name,
+ const GElf_Sym *sym, const GElf_Rel *rel)
+{
+ struct bpf_insn *insn = &prog->insns[insn_idx];
+ size_t map_idx, nr_maps = prog->obj->nr_maps;
+ struct bpf_object *obj = prog->obj;
+ __u32 shdr_idx = sym->st_shndx;
+ enum libbpf_map_type type;
+ struct bpf_map *map;
+
+ /* sub-program call relocation */
+ if (insn->code == (BPF_JMP | BPF_CALL)) {
+ if (insn->src_reg != BPF_PSEUDO_CALL) {
+ pr_warn("incorrect bpf_call opcode\n");
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ /* text_shndx can be 0, if no default "main" program exists */
+ if (!shdr_idx || shdr_idx != obj->efile.text_shndx) {
+ pr_warn("bad call relo against section %u\n", shdr_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ if (sym->st_value % 8) {
+ pr_warn("bad call relo offset: %llu\n", (__u64)sym->st_value);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ reloc_desc->type = RELO_CALL;
+ reloc_desc->insn_idx = insn_idx;
+ reloc_desc->sym_off = sym->st_value;
+ obj->has_pseudo_calls = true;
+ return 0;
+ }
+
+ if (insn->code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ pr_warn("invalid relo for insns[%d].code 0x%x\n",
+ insn_idx, insn->code);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ if (!shdr_idx || shdr_idx >= SHN_LORESERVE) {
+ pr_warn("invalid relo for \'%s\' in special section 0x%x; forgot to initialize global var?..\n",
+ name, shdr_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+
+ type = bpf_object__section_to_libbpf_map_type(obj, shdr_idx);
+
+ /* generic map reference relocation */
+ if (type == LIBBPF_MAP_UNSPEC) {
+ if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
+ pr_warn("bad map relo against section %u\n",
+ shdr_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ for (map_idx = 0; map_idx < nr_maps; map_idx++) {
+ map = &obj->maps[map_idx];
+ if (map->libbpf_type != type ||
+ map->sec_idx != sym->st_shndx ||
+ map->sec_offset != sym->st_value)
+ continue;
+ pr_debug("found map %zd (%s, sec %d, off %zu) for insn %u\n",
+ map_idx, map->name, map->sec_idx,
+ map->sec_offset, insn_idx);
+ break;
+ }
+ if (map_idx >= nr_maps) {
+ pr_warn("map relo failed to find map for sec %u, off %llu\n",
+ shdr_idx, (__u64)sym->st_value);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ reloc_desc->type = RELO_LD64;
+ reloc_desc->insn_idx = insn_idx;
+ reloc_desc->map_idx = map_idx;
+ reloc_desc->sym_off = 0; /* sym->st_value determines map_idx */
+ return 0;
+ }
+
+ /* global data map relocation */
+ if (!bpf_object__shndx_is_data(obj, shdr_idx)) {
+ pr_warn("bad data relo against section %u\n", shdr_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ if (!obj->caps.global_data) {
+ pr_warn("relocation: kernel does not support global \'%s\' variable access in insns[%d]\n",
+ name, insn_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ for (map_idx = 0; map_idx < nr_maps; map_idx++) {
+ map = &obj->maps[map_idx];
+ if (map->libbpf_type != type)
+ continue;
+ pr_debug("found data map %zd (%s, sec %d, off %zu) for insn %u\n",
+ map_idx, map->name, map->sec_idx, map->sec_offset,
+ insn_idx);
+ break;
+ }
+ if (map_idx >= nr_maps) {
+ pr_warn("data relo failed to find map for sec %u\n",
+ shdr_idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+
+ reloc_desc->type = RELO_DATA;
+ reloc_desc->insn_idx = insn_idx;
+ reloc_desc->map_idx = map_idx;
+ reloc_desc->sym_off = sym->st_value;
+ return 0;
+}
+
+static int
+bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
+ Elf_Data *data, struct bpf_object *obj)
+{
+ Elf_Data *symbols = obj->efile.symbols;
+ int err, i, nrels;
+
+ pr_debug("collecting relocating info for: '%s'\n", prog->section_name);
+ nrels = shdr->sh_size / shdr->sh_entsize;
+
+ prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
+ if (!prog->reloc_desc) {
+ pr_warn("failed to alloc memory in relocation\n");
+ return -ENOMEM;
+ }
+ prog->nr_reloc = nrels;
+
+ for (i = 0; i < nrels; i++) {
+ const char *name;
+ __u32 insn_idx;
+ GElf_Sym sym;
+ GElf_Rel rel;
+
+ if (!gelf_getrel(data, i, &rel)) {
+ pr_warn("relocation: failed to get %d reloc\n", i);
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ if (!gelf_getsym(symbols, GELF_R_SYM(rel.r_info), &sym)) {
+ pr_warn("relocation: symbol %"PRIx64" not found\n",
+ GELF_R_SYM(rel.r_info));
+ return -LIBBPF_ERRNO__FORMAT;
+ }
+ if (rel.r_offset % sizeof(struct bpf_insn))
+ return -LIBBPF_ERRNO__FORMAT;
+
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+ name = elf_strptr(obj->efile.elf, obj->efile.strtabidx,
+ sym.st_name) ? : "<?>";
+
+ pr_debug("relo for shdr %u, symb %llu, value %llu, type %d, bind %d, name %d (\'%s\'), insn %u\n",
+ (__u32)sym.st_shndx, (__u64)GELF_R_SYM(rel.r_info),
+ (__u64)sym.st_value, GELF_ST_TYPE(sym.st_info),
+ GELF_ST_BIND(sym.st_info), sym.st_name, name,
+ insn_idx);
+
+ err = bpf_program__record_reloc(prog, &prog->reloc_desc[i],
+ insn_idx, name, &sym, &rel);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
+{
+ struct bpf_map_def *def = &map->def;
+ __u32 key_type_id = 0, value_type_id = 0;
+ int ret;
+
+ /* if it's BTF-defined map, we don't need to search for type IDs */
+ if (map->sec_idx == obj->efile.btf_maps_shndx)
+ return 0;
+
+ if (!bpf_map__is_internal(map)) {
+ ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size,
+ def->value_size, &key_type_id,
+ &value_type_id);
+ } else {
+ /*
+ * LLVM annotates global data differently in BTF, that is,
+ * only as '.data', '.bss' or '.rodata'.
+ */
+ ret = btf__find_by_name(obj->btf,
+ libbpf_type_to_btf_name[map->libbpf_type]);
+ }
+ if (ret < 0)
+ return ret;
+
+ map->btf_key_type_id = key_type_id;
+ map->btf_value_type_id = bpf_map__is_internal(map) ?
+ ret : value_type_id;
+ return 0;
+}
+
+int bpf_map__reuse_fd(struct bpf_map *map, int fd)
+{
+ struct bpf_map_info info = {};
+ __u32 len = sizeof(info);
+ int new_fd, err;
+ char *new_name;
+
+ err = bpf_obj_get_info_by_fd(fd, &info, &len);
+ if (err)
+ return err;
+
+ new_name = strdup(info.name);
+ if (!new_name)
+ return -errno;
+
+ new_fd = open("/", O_RDONLY | O_CLOEXEC);
+ if (new_fd < 0) {
+ err = -errno;
+ goto err_free_new_name;
+ }
+
+ new_fd = dup3(fd, new_fd, O_CLOEXEC);
+ if (new_fd < 0) {
+ err = -errno;
+ goto err_close_new_fd;
+ }
+
+ err = zclose(map->fd);
+ if (err) {
+ err = -errno;
+ goto err_close_new_fd;
+ }
+ free(map->name);
+
+ map->fd = new_fd;
+ map->name = new_name;
+ map->def.type = info.type;
+ map->def.key_size = info.key_size;
+ map->def.value_size = info.value_size;
+ map->def.max_entries = info.max_entries;
+ map->def.map_flags = info.map_flags;
+ map->btf_key_type_id = info.btf_key_type_id;
+ map->btf_value_type_id = info.btf_value_type_id;
+ map->reused = true;
+
+ return 0;
+
+err_close_new_fd:
+ close(new_fd);
+err_free_new_name:
+ free(new_name);
+ return err;
+}
+
+int bpf_map__resize(struct bpf_map *map, __u32 max_entries)
+{
+ if (!map || !max_entries)
+ return -EINVAL;
+
+ /* If map already created, its attributes can't be changed. */
+ if (map->fd >= 0)
+ return -EBUSY;
+
+ map->def.max_entries = max_entries;
+
+ return 0;
+}
+
+static int
+bpf_object__probe_name(struct bpf_object *obj)
+{
+ struct bpf_load_program_attr attr;
+ char *cp, errmsg[STRERR_BUFSIZE];
+ struct bpf_insn insns[] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ int ret;
+
+ /* make sure basic loading works */
+
+ memset(&attr, 0, sizeof(attr));
+ attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ attr.insns = insns;
+ attr.insns_cnt = ARRAY_SIZE(insns);
+ attr.license = "GPL";
+
+ ret = bpf_load_program_xattr(&attr, NULL, 0);
+ if (ret < 0) {
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("Error in %s():%s(%d). Couldn't load basic 'r0 = 0' BPF program.\n",
+ __func__, cp, errno);
+ return -errno;
+ }
+ close(ret);
+
+ /* now try the same program, but with the name */
+
+ attr.name = "test";
+ ret = bpf_load_program_xattr(&attr, NULL, 0);
+ if (ret >= 0) {
+ obj->caps.name = 1;
+ close(ret);
+ }
+
+ return 0;
+}
+
+static int
+bpf_object__probe_global_data(struct bpf_object *obj)
+{
+ struct bpf_load_program_attr prg_attr;
+ struct bpf_create_map_attr map_attr;
+ char *cp, errmsg[STRERR_BUFSIZE];
+ struct bpf_insn insns[] = {
+ BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16),
+ BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ int ret, map;
+
+ memset(&map_attr, 0, sizeof(map_attr));
+ map_attr.map_type = BPF_MAP_TYPE_ARRAY;
+ map_attr.key_size = sizeof(int);
+ map_attr.value_size = 32;
+ map_attr.max_entries = 1;
+
+ map = bpf_create_map_xattr(&map_attr);
+ if (map < 0) {
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n",
+ __func__, cp, errno);
+ return -errno;
+ }
+
+ insns[0].imm = map;
+
+ memset(&prg_attr, 0, sizeof(prg_attr));
+ prg_attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
+ prg_attr.insns = insns;
+ prg_attr.insns_cnt = ARRAY_SIZE(insns);
+ prg_attr.license = "GPL";
+
+ ret = bpf_load_program_xattr(&prg_attr, NULL, 0);
+ if (ret >= 0) {
+ obj->caps.global_data = 1;
+ close(ret);
+ }
+
+ close(map);
+ return 0;
+}
+
+static int bpf_object__probe_btf_func(struct bpf_object *obj)
+{
+ static const char strs[] = "\0int\0x\0a";
+ /* void x(int a) {} */
+ __u32 types[] = {
+ /* int */
+ BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
+ /* FUNC_PROTO */ /* [2] */
+ BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
+ BTF_PARAM_ENC(7, 1),
+ /* FUNC x */ /* [3] */
+ BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), 2),
+ };
+ int btf_fd;
+
+ btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types),
+ strs, sizeof(strs));
+ if (btf_fd >= 0) {
+ obj->caps.btf_func = 1;
+ close(btf_fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bpf_object__probe_btf_datasec(struct bpf_object *obj)
+{
+ static const char strs[] = "\0x\0.data";
+ /* static int a; */
+ __u32 types[] = {
+ /* int */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
+ /* VAR x */ /* [2] */
+ BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
+ BTF_VAR_STATIC,
+ /* DATASEC val */ /* [3] */
+ BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
+ BTF_VAR_SECINFO_ENC(2, 0, 4),
+ };
+ int btf_fd;
+
+ btf_fd = libbpf__load_raw_btf((char *)types, sizeof(types),
+ strs, sizeof(strs));
+ if (btf_fd >= 0) {
+ obj->caps.btf_datasec = 1;
+ close(btf_fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bpf_object__probe_array_mmap(struct bpf_object *obj)
+{
+ struct bpf_create_map_attr attr = {
+ .map_type = BPF_MAP_TYPE_ARRAY,
+ .map_flags = BPF_F_MMAPABLE,
+ .key_size = sizeof(int),
+ .value_size = sizeof(int),
+ .max_entries = 1,
+ };
+ int fd;
+
+ fd = bpf_create_map_xattr(&attr);
+ if (fd >= 0) {
+ obj->caps.array_mmap = 1;
+ close(fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+bpf_object__probe_caps(struct bpf_object *obj)
+{
+ int (*probe_fn[])(struct bpf_object *obj) = {
+ bpf_object__probe_name,
+ bpf_object__probe_global_data,
+ bpf_object__probe_btf_func,
+ bpf_object__probe_btf_datasec,
+ bpf_object__probe_array_mmap,
+ };
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(probe_fn); i++) {
+ ret = probe_fn[i](obj);
+ if (ret < 0)
+ pr_debug("Probe #%d failed with %d.\n", i, ret);
+ }
+
+ return 0;
+}
+
+static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd)
+{
+ struct bpf_map_info map_info = {};
+ char msg[STRERR_BUFSIZE];
+ __u32 map_info_len;
+
+ map_info_len = sizeof(map_info);
+
+ if (bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len)) {
+ pr_warn("failed to get map info for map FD %d: %s\n",
+ map_fd, libbpf_strerror_r(errno, msg, sizeof(msg)));
+ return false;
+ }
+
+ return (map_info.type == map->def.type &&
+ map_info.key_size == map->def.key_size &&
+ map_info.value_size == map->def.value_size &&
+ map_info.max_entries == map->def.max_entries &&
+ map_info.map_flags == map->def.map_flags);
+}
+
+static int
+bpf_object__reuse_map(struct bpf_map *map)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int err, pin_fd;
+
+ pin_fd = bpf_obj_get(map->pin_path);
+ if (pin_fd < 0) {
+ err = -errno;
+ if (err == -ENOENT) {
+ pr_debug("found no pinned map to reuse at '%s'\n",
+ map->pin_path);
+ return 0;
+ }
+
+ cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
+ pr_warn("couldn't retrieve pinned map '%s': %s\n",
+ map->pin_path, cp);
+ return err;
+ }
+
+ if (!map_is_reuse_compat(map, pin_fd)) {
+ pr_warn("couldn't reuse pinned map at '%s': parameter mismatch\n",
+ map->pin_path);
+ close(pin_fd);
+ return -EINVAL;
+ }
+
+ err = bpf_map__reuse_fd(map, pin_fd);
+ if (err) {
+ close(pin_fd);
+ return err;
+ }
+ map->pinned = true;
+ pr_debug("reused pinned map at '%s'\n", map->pin_path);
+
+ return 0;
+}
+
+static int
+bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int err, zero = 0;
+ __u8 *data;
+
+ /* Nothing to do here since kernel already zero-initializes .bss map. */
+ if (map->libbpf_type == LIBBPF_MAP_BSS)
+ return 0;
+
+ data = map->libbpf_type == LIBBPF_MAP_DATA ?
+ obj->sections.data : obj->sections.rodata;
+
+ err = bpf_map_update_elem(map->fd, &zero, data, 0);
+ /* Freeze .rodata map as read-only from syscall side. */
+ if (!err && map->libbpf_type == LIBBPF_MAP_RODATA) {
+ err = bpf_map_freeze(map->fd);
+ if (err) {
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("Error freezing map(%s) as read-only: %s\n",
+ map->name, cp);
+ err = 0;
+ }
+ }
+ return err;
+}
+
+static int
+bpf_object__create_maps(struct bpf_object *obj)
+{
+ struct bpf_create_map_attr create_attr = {};
+ int nr_cpus = 0;
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < obj->nr_maps; i++) {
+ struct bpf_map *map = &obj->maps[i];
+ struct bpf_map_def *def = &map->def;
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int *pfd = &map->fd;
+
+ if (map->pin_path) {
+ err = bpf_object__reuse_map(map);
+ if (err) {
+ pr_warn("error reusing pinned map %s\n",
+ map->name);
+ return err;
+ }
+ }
+
+ if (map->fd >= 0) {
+ pr_debug("skip map create (preset) %s: fd=%d\n",
+ map->name, map->fd);
+ continue;
+ }
+
+ if (obj->caps.name)
+ create_attr.name = map->name;
+ create_attr.map_ifindex = map->map_ifindex;
+ create_attr.map_type = def->type;
+ create_attr.map_flags = def->map_flags;
+ create_attr.key_size = def->key_size;
+ create_attr.value_size = def->value_size;
+ if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
+ !def->max_entries) {
+ if (!nr_cpus)
+ nr_cpus = libbpf_num_possible_cpus();
+ if (nr_cpus < 0) {
+ pr_warn("failed to determine number of system CPUs: %d\n",
+ nr_cpus);
+ err = nr_cpus;
+ goto err_out;
+ }
+ pr_debug("map '%s': setting size to %d\n",
+ map->name, nr_cpus);
+ create_attr.max_entries = nr_cpus;
+ } else {
+ create_attr.max_entries = def->max_entries;
+ }
+ create_attr.btf_fd = 0;
+ create_attr.btf_key_type_id = 0;
+ create_attr.btf_value_type_id = 0;
+ if (bpf_map_type__is_map_in_map(def->type) &&
+ map->inner_map_fd >= 0)
+ create_attr.inner_map_fd = map->inner_map_fd;
+
+ if (obj->btf && !bpf_map_find_btf_info(obj, map)) {
+ create_attr.btf_fd = btf__fd(obj->btf);
+ create_attr.btf_key_type_id = map->btf_key_type_id;
+ create_attr.btf_value_type_id = map->btf_value_type_id;
+ }
+
+ *pfd = bpf_create_map_xattr(&create_attr);
+ if (*pfd < 0 && (create_attr.btf_key_type_id ||
+ create_attr.btf_value_type_id)) {
+ err = -errno;
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+ pr_warn("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
+ map->name, cp, err);
+ create_attr.btf_fd = 0;
+ create_attr.btf_key_type_id = 0;
+ create_attr.btf_value_type_id = 0;
+ map->btf_key_type_id = 0;
+ map->btf_value_type_id = 0;
+ *pfd = bpf_create_map_xattr(&create_attr);
+ }
+
+ if (*pfd < 0) {
+ size_t j;
+
+ err = -errno;
+err_out:
+ cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
+ pr_warn("failed to create map (name: '%s'): %s(%d)\n",
+ map->name, cp, err);
+ for (j = 0; j < i; j++)
+ zclose(obj->maps[j].fd);
+ return err;
+ }
+
+ if (bpf_map__is_internal(map)) {
+ err = bpf_object__populate_internal_map(obj, map);
+ if (err < 0) {
+ zclose(*pfd);
+ goto err_out;
+ }
+ }
+
+ if (map->pin_path && !map->pinned) {
+ err = bpf_map__pin(map, NULL);
+ if (err) {
+ pr_warn("failed to auto-pin map name '%s' at '%s'\n",
+ map->name, map->pin_path);
+ return err;
+ }
+ }
+
+ pr_debug("created map %s: fd=%d\n", map->name, *pfd);
+ }
+
+ return 0;
+}
+
+static int
+check_btf_ext_reloc_err(struct bpf_program *prog, int err,
+ void *btf_prog_info, const char *info_name)
+{
+ if (err != -ENOENT) {
+ pr_warn("Error in loading %s for sec %s.\n",
+ info_name, prog->section_name);
+ return err;
+ }
+
+ /* err == -ENOENT (i.e. prog->section_name not found in btf_ext) */
+
+ if (btf_prog_info) {
+ /*
+ * Some info has already been found but has problem
+ * in the last btf_ext reloc. Must have to error out.
+ */
+ pr_warn("Error in relocating %s for sec %s.\n",
+ info_name, prog->section_name);
+ return err;
+ }
+
+ /* Have problem loading the very first info. Ignore the rest. */
+ pr_warn("Cannot find %s for main program sec %s. Ignore all %s.\n",
+ info_name, prog->section_name, info_name);
+ return 0;
+}
+
+static int
+bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj,
+ const char *section_name, __u32 insn_offset)
+{
+ int err;
+
+ if (!insn_offset || prog->func_info) {
+ /*
+ * !insn_offset => main program
+ *
+ * For sub prog, the main program's func_info has to
+ * be loaded first (i.e. prog->func_info != NULL)
+ */
+ err = btf_ext__reloc_func_info(obj->btf, obj->btf_ext,
+ section_name, insn_offset,
+ &prog->func_info,
+ &prog->func_info_cnt);
+ if (err)
+ return check_btf_ext_reloc_err(prog, err,
+ prog->func_info,
+ "bpf_func_info");
+
+ prog->func_info_rec_size = btf_ext__func_info_rec_size(obj->btf_ext);
+ }
+
+ if (!insn_offset || prog->line_info) {
+ err = btf_ext__reloc_line_info(obj->btf, obj->btf_ext,
+ section_name, insn_offset,
+ &prog->line_info,
+ &prog->line_info_cnt);
+ if (err)
+ return check_btf_ext_reloc_err(prog, err,
+ prog->line_info,
+ "bpf_line_info");
+
+ prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext);
+ }
+
+ return 0;
+}
+
+#define BPF_CORE_SPEC_MAX_LEN 64
+
+/* represents BPF CO-RE field or array element accessor */
+struct bpf_core_accessor {
+ __u32 type_id; /* struct/union type or array element type */
+ __u32 idx; /* field index or array index */
+ const char *name; /* field name or NULL for array accessor */
+};
+
+struct bpf_core_spec {
+ const struct btf *btf;
+ /* high-level spec: named fields and array indices only */
+ struct bpf_core_accessor spec[BPF_CORE_SPEC_MAX_LEN];
+ /* high-level spec length */
+ int len;
+ /* raw, low-level spec: 1-to-1 with accessor spec string */
+ int raw_spec[BPF_CORE_SPEC_MAX_LEN];
+ /* raw spec length */
+ int raw_len;
+ /* field bit offset represented by spec */
+ __u32 bit_offset;
+};
+
+static bool str_is_empty(const char *s)
+{
+ return !s || !s[0];
+}
+
+/*
+ * Turn bpf_field_reloc into a low- and high-level spec representation,
+ * validating correctness along the way, as well as calculating resulting
+ * field bit offset, specified by accessor string. Low-level spec captures
+ * every single level of nestedness, including traversing anonymous
+ * struct/union members. High-level one only captures semantically meaningful
+ * "turning points": named fields and array indicies.
+ * E.g., for this case:
+ *
+ * struct sample {
+ * int __unimportant;
+ * struct {
+ * int __1;
+ * int __2;
+ * int a[7];
+ * };
+ * };
+ *
+ * struct sample *s = ...;
+ *
+ * int x = &s->a[3]; // access string = '0:1:2:3'
+ *
+ * Low-level spec has 1:1 mapping with each element of access string (it's
+ * just a parsed access string representation): [0, 1, 2, 3].
+ *
+ * High-level spec will capture only 3 points:
+ * - intial zero-index access by pointer (&s->... is the same as &s[0]...);
+ * - field 'a' access (corresponds to '2' in low-level spec);
+ * - array element #3 access (corresponds to '3' in low-level spec).
+ *
+ */
+static int bpf_core_spec_parse(const struct btf *btf,
+ __u32 type_id,
+ const char *spec_str,
+ struct bpf_core_spec *spec)
+{
+ int access_idx, parsed_len, i;
+ const struct btf_type *t;
+ const char *name;
+ __u32 id;
+ __s64 sz;
+
+ if (str_is_empty(spec_str) || *spec_str == ':')
+ return -EINVAL;
+
+ memset(spec, 0, sizeof(*spec));
+ spec->btf = btf;
+
+ /* parse spec_str="0:1:2:3:4" into array raw_spec=[0, 1, 2, 3, 4] */
+ while (*spec_str) {
+ if (*spec_str == ':')
+ ++spec_str;
+ if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1)
+ return -EINVAL;
+ if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+ spec_str += parsed_len;
+ spec->raw_spec[spec->raw_len++] = access_idx;
+ }
+
+ if (spec->raw_len == 0)
+ return -EINVAL;
+
+ /* first spec value is always reloc type array index */
+ t = skip_mods_and_typedefs(btf, type_id, &id);
+ if (!t)
+ return -EINVAL;
+
+ access_idx = spec->raw_spec[0];
+ spec->spec[0].type_id = id;
+ spec->spec[0].idx = access_idx;
+ spec->len++;
+
+ sz = btf__resolve_size(btf, id);
+ if (sz < 0)
+ return sz;
+ spec->bit_offset = access_idx * sz * 8;
+
+ for (i = 1; i < spec->raw_len; i++) {
+ t = skip_mods_and_typedefs(btf, id, &id);
+ if (!t)
+ return -EINVAL;
+
+ access_idx = spec->raw_spec[i];
+
+ if (btf_is_composite(t)) {
+ const struct btf_member *m;
+ __u32 bit_offset;
+
+ if (access_idx >= btf_vlen(t))
+ return -EINVAL;
+
+ bit_offset = btf_member_bit_offset(t, access_idx);
+ spec->bit_offset += bit_offset;
+
+ m = btf_members(t) + access_idx;
+ if (m->name_off) {
+ name = btf__name_by_offset(btf, m->name_off);
+ if (str_is_empty(name))
+ return -EINVAL;
+
+ spec->spec[spec->len].type_id = id;
+ spec->spec[spec->len].idx = access_idx;
+ spec->spec[spec->len].name = name;
+ spec->len++;
+ }
+
+ id = m->type;
+ } else if (btf_is_array(t)) {
+ const struct btf_array *a = btf_array(t);
+
+ t = skip_mods_and_typedefs(btf, a->type, &id);
+ if (!t || access_idx >= a->nelems)
+ return -EINVAL;
+
+ spec->spec[spec->len].type_id = id;
+ spec->spec[spec->len].idx = access_idx;
+ spec->len++;
+
+ sz = btf__resolve_size(btf, id);
+ if (sz < 0)
+ return sz;
+ spec->bit_offset += access_idx * sz * 8;
+ } else {
+ pr_warn("relo for [%u] %s (at idx %d) captures type [%d] of unexpected kind %d\n",
+ type_id, spec_str, i, id, btf_kind(t));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static bool bpf_core_is_flavor_sep(const char *s)
+{
+ /* check X___Y name pattern, where X and Y are not underscores */
+ return s[0] != '_' && /* X */
+ s[1] == '_' && s[2] == '_' && s[3] == '_' && /* ___ */
+ s[4] != '_'; /* Y */
+}
+
+/* Given 'some_struct_name___with_flavor' return the length of a name prefix
+ * before last triple underscore. Struct name part after last triple
+ * underscore is ignored by BPF CO-RE relocation during relocation matching.
+ */
+static size_t bpf_core_essential_name_len(const char *name)
+{
+ size_t n = strlen(name);
+ int i;
+
+ for (i = n - 5; i >= 0; i--) {
+ if (bpf_core_is_flavor_sep(name + i))
+ return i + 1;
+ }
+ return n;
+}
+
+/* dynamically sized list of type IDs */
+struct ids_vec {
+ __u32 *data;
+ int len;
+};
+
+static void bpf_core_free_cands(struct ids_vec *cand_ids)
+{
+ free(cand_ids->data);
+ free(cand_ids);
+}
+
+static struct ids_vec *bpf_core_find_cands(const struct btf *local_btf,
+ __u32 local_type_id,
+ const struct btf *targ_btf)
+{
+ size_t local_essent_len, targ_essent_len;
+ const char *local_name, *targ_name;
+ const struct btf_type *t;
+ struct ids_vec *cand_ids;
+ __u32 *new_ids;
+ int i, err, n;
+
+ t = btf__type_by_id(local_btf, local_type_id);
+ if (!t)
+ return ERR_PTR(-EINVAL);
+
+ local_name = btf__name_by_offset(local_btf, t->name_off);
+ if (str_is_empty(local_name))
+ return ERR_PTR(-EINVAL);
+ local_essent_len = bpf_core_essential_name_len(local_name);
+
+ cand_ids = calloc(1, sizeof(*cand_ids));
+ if (!cand_ids)
+ return ERR_PTR(-ENOMEM);
+
+ n = btf__get_nr_types(targ_btf);
+ for (i = 1; i <= n; i++) {
+ t = btf__type_by_id(targ_btf, i);
+ targ_name = btf__name_by_offset(targ_btf, t->name_off);
+ if (str_is_empty(targ_name))
+ continue;
+
+ targ_essent_len = bpf_core_essential_name_len(targ_name);
+ if (targ_essent_len != local_essent_len)
+ continue;
+
+ if (strncmp(local_name, targ_name, local_essent_len) == 0) {
+ pr_debug("[%d] %s: found candidate [%d] %s\n",
+ local_type_id, local_name, i, targ_name);
+ new_ids = realloc(cand_ids->data, cand_ids->len + 1);
+ if (!new_ids) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ cand_ids->data = new_ids;
+ cand_ids->data[cand_ids->len++] = i;
+ }
+ }
+ return cand_ids;
+err_out:
+ bpf_core_free_cands(cand_ids);
+ return ERR_PTR(err);
+}
+
+/* Check two types for compatibility, skipping const/volatile/restrict and
+ * typedefs, to ensure we are relocating compatible entities:
+ * - any two STRUCTs/UNIONs are compatible and can be mixed;
+ * - any two FWDs are compatible, if their names match (modulo flavor suffix);
+ * - any two PTRs are always compatible;
+ * - for ENUMs, names should be the same (ignoring flavor suffix) or at
+ * least one of enums should be anonymous;
+ * - for ENUMs, check sizes, names are ignored;
+ * - for INT, size and signedness are ignored;
+ * - for ARRAY, dimensionality is ignored, element types are checked for
+ * compatibility recursively;
+ * - everything else shouldn't be ever a target of relocation.
+ * These rules are not set in stone and probably will be adjusted as we get
+ * more experience with using BPF CO-RE relocations.
+ */
+static int bpf_core_fields_are_compat(const struct btf *local_btf,
+ __u32 local_id,
+ const struct btf *targ_btf,
+ __u32 targ_id)
+{
+ const struct btf_type *local_type, *targ_type;
+
+recur:
+ local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id);
+ targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
+ if (!local_type || !targ_type)
+ return -EINVAL;
+
+ if (btf_is_composite(local_type) && btf_is_composite(targ_type))
+ return 1;
+ if (btf_kind(local_type) != btf_kind(targ_type))
+ return 0;
+
+ switch (btf_kind(local_type)) {
+ case BTF_KIND_PTR:
+ return 1;
+ case BTF_KIND_FWD:
+ case BTF_KIND_ENUM: {
+ const char *local_name, *targ_name;
+ size_t local_len, targ_len;
+
+ local_name = btf__name_by_offset(local_btf,
+ local_type->name_off);
+ targ_name = btf__name_by_offset(targ_btf, targ_type->name_off);
+ local_len = bpf_core_essential_name_len(local_name);
+ targ_len = bpf_core_essential_name_len(targ_name);
+ /* one of them is anonymous or both w/ same flavor-less names */
+ return local_len == 0 || targ_len == 0 ||
+ (local_len == targ_len &&
+ strncmp(local_name, targ_name, local_len) == 0);
+ }
+ case BTF_KIND_INT:
+ /* just reject deprecated bitfield-like integers; all other
+ * integers are by default compatible between each other
+ */
+ return btf_int_offset(local_type) == 0 &&
+ btf_int_offset(targ_type) == 0;
+ case BTF_KIND_ARRAY:
+ local_id = btf_array(local_type)->type;
+ targ_id = btf_array(targ_type)->type;
+ goto recur;
+ default:
+ pr_warn("unexpected kind %d relocated, local [%d], target [%d]\n",
+ btf_kind(local_type), local_id, targ_id);
+ return 0;
+ }
+}
+
+/*
+ * Given single high-level named field accessor in local type, find
+ * corresponding high-level accessor for a target type. Along the way,
+ * maintain low-level spec for target as well. Also keep updating target
+ * bit offset.
+ *
+ * Searching is performed through recursive exhaustive enumeration of all
+ * fields of a struct/union. If there are any anonymous (embedded)
+ * structs/unions, they are recursively searched as well. If field with
+ * desired name is found, check compatibility between local and target types,
+ * before returning result.
+ *
+ * 1 is returned, if field is found.
+ * 0 is returned if no compatible field is found.
+ * <0 is returned on error.
+ */
+static int bpf_core_match_member(const struct btf *local_btf,
+ const struct bpf_core_accessor *local_acc,
+ const struct btf *targ_btf,
+ __u32 targ_id,
+ struct bpf_core_spec *spec,
+ __u32 *next_targ_id)
+{
+ const struct btf_type *local_type, *targ_type;
+ const struct btf_member *local_member, *m;
+ const char *local_name, *targ_name;
+ __u32 local_id;
+ int i, n, found;
+
+ targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
+ if (!targ_type)
+ return -EINVAL;
+ if (!btf_is_composite(targ_type))
+ return 0;
+
+ local_id = local_acc->type_id;
+ local_type = btf__type_by_id(local_btf, local_id);
+ local_member = btf_members(local_type) + local_acc->idx;
+ local_name = btf__name_by_offset(local_btf, local_member->name_off);
+
+ n = btf_vlen(targ_type);
+ m = btf_members(targ_type);
+ for (i = 0; i < n; i++, m++) {
+ __u32 bit_offset;
+
+ bit_offset = btf_member_bit_offset(targ_type, i);
+
+ /* too deep struct/union/array nesting */
+ if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+
+ /* speculate this member will be the good one */
+ spec->bit_offset += bit_offset;
+ spec->raw_spec[spec->raw_len++] = i;
+
+ targ_name = btf__name_by_offset(targ_btf, m->name_off);
+ if (str_is_empty(targ_name)) {
+ /* embedded struct/union, we need to go deeper */
+ found = bpf_core_match_member(local_btf, local_acc,
+ targ_btf, m->type,
+ spec, next_targ_id);
+ if (found) /* either found or error */
+ return found;
+ } else if (strcmp(local_name, targ_name) == 0) {
+ /* matching named field */
+ struct bpf_core_accessor *targ_acc;
+
+ targ_acc = &spec->spec[spec->len++];
+ targ_acc->type_id = targ_id;
+ targ_acc->idx = i;
+ targ_acc->name = targ_name;
+
+ *next_targ_id = m->type;
+ found = bpf_core_fields_are_compat(local_btf,
+ local_member->type,
+ targ_btf, m->type);
+ if (!found)
+ spec->len--; /* pop accessor */
+ return found;
+ }
+ /* member turned out not to be what we looked for */
+ spec->bit_offset -= bit_offset;
+ spec->raw_len--;
+ }
+
+ return 0;
+}
+
+/*
+ * Try to match local spec to a target type and, if successful, produce full
+ * target spec (high-level, low-level + bit offset).
+ */
+static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
+ const struct btf *targ_btf, __u32 targ_id,
+ struct bpf_core_spec *targ_spec)
+{
+ const struct btf_type *targ_type;
+ const struct bpf_core_accessor *local_acc;
+ struct bpf_core_accessor *targ_acc;
+ int i, sz, matched;
+
+ memset(targ_spec, 0, sizeof(*targ_spec));
+ targ_spec->btf = targ_btf;
+
+ local_acc = &local_spec->spec[0];
+ targ_acc = &targ_spec->spec[0];
+
+ for (i = 0; i < local_spec->len; i++, local_acc++, targ_acc++) {
+ targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id,
+ &targ_id);
+ if (!targ_type)
+ return -EINVAL;
+
+ if (local_acc->name) {
+ matched = bpf_core_match_member(local_spec->btf,
+ local_acc,
+ targ_btf, targ_id,
+ targ_spec, &targ_id);
+ if (matched <= 0)
+ return matched;
+ } else {
+ /* for i=0, targ_id is already treated as array element
+ * type (because it's the original struct), for others
+ * we should find array element type first
+ */
+ if (i > 0) {
+ const struct btf_array *a;
+
+ if (!btf_is_array(targ_type))
+ return 0;
+
+ a = btf_array(targ_type);
+ if (local_acc->idx >= a->nelems)
+ return 0;
+ if (!skip_mods_and_typedefs(targ_btf, a->type,
+ &targ_id))
+ return -EINVAL;
+ }
+
+ /* too deep struct/union/array nesting */
+ if (targ_spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
+ return -E2BIG;
+
+ targ_acc->type_id = targ_id;
+ targ_acc->idx = local_acc->idx;
+ targ_acc->name = NULL;
+ targ_spec->len++;
+ targ_spec->raw_spec[targ_spec->raw_len] = targ_acc->idx;
+ targ_spec->raw_len++;
+
+ sz = btf__resolve_size(targ_btf, targ_id);
+ if (sz < 0)
+ return sz;
+ targ_spec->bit_offset += local_acc->idx * sz * 8;
+ }
+ }
+
+ return 1;
+}
+
+static int bpf_core_calc_field_relo(const struct bpf_program *prog,
+ const struct bpf_field_reloc *relo,
+ const struct bpf_core_spec *spec,
+ __u32 *val, bool *validate)
+{
+ const struct bpf_core_accessor *acc = &spec->spec[spec->len - 1];
+ const struct btf_type *t = btf__type_by_id(spec->btf, acc->type_id);
+ __u32 byte_off, byte_sz, bit_off, bit_sz;
+ const struct btf_member *m;
+ const struct btf_type *mt;
+ bool bitfield;
+ __s64 sz;
+
+ /* a[n] accessor needs special handling */
+ if (!acc->name) {
+ if (relo->kind == BPF_FIELD_BYTE_OFFSET) {
+ *val = spec->bit_offset / 8;
+ } else if (relo->kind == BPF_FIELD_BYTE_SIZE) {
+ sz = btf__resolve_size(spec->btf, acc->type_id);
+ if (sz < 0)
+ return -EINVAL;
+ *val = sz;
+ } else {
+ pr_warn("prog '%s': relo %d at insn #%d can't be applied to array access\n",
+ bpf_program__title(prog, false),
+ relo->kind, relo->insn_off / 8);
+ return -EINVAL;
+ }
+ if (validate)
+ *validate = true;
+ return 0;
+ }
+
+ m = btf_members(t) + acc->idx;
+ mt = skip_mods_and_typedefs(spec->btf, m->type, NULL);
+ bit_off = spec->bit_offset;
+ bit_sz = btf_member_bitfield_size(t, acc->idx);
+
+ bitfield = bit_sz > 0;
+ if (bitfield) {
+ byte_sz = mt->size;
+ byte_off = bit_off / 8 / byte_sz * byte_sz;
+ /* figure out smallest int size necessary for bitfield load */
+ while (bit_off + bit_sz - byte_off * 8 > byte_sz * 8) {
+ if (byte_sz >= 8) {
+ /* bitfield can't be read with 64-bit read */
+ pr_warn("prog '%s': relo %d at insn #%d can't be satisfied for bitfield\n",
+ bpf_program__title(prog, false),
+ relo->kind, relo->insn_off / 8);
+ return -E2BIG;
+ }
+ byte_sz *= 2;
+ byte_off = bit_off / 8 / byte_sz * byte_sz;
+ }
+ } else {
+ sz = btf__resolve_size(spec->btf, m->type);
+ if (sz < 0)
+ return -EINVAL;
+ byte_sz = sz;
+ byte_off = spec->bit_offset / 8;
+ bit_sz = byte_sz * 8;
+ }
+
+ /* for bitfields, all the relocatable aspects are ambiguous and we
+ * might disagree with compiler, so turn off validation of expected
+ * value, except for signedness
+ */
+ if (validate)
+ *validate = !bitfield;
+
+ switch (relo->kind) {
+ case BPF_FIELD_BYTE_OFFSET:
+ *val = byte_off;
+ break;
+ case BPF_FIELD_BYTE_SIZE:
+ *val = byte_sz;
+ break;
+ case BPF_FIELD_SIGNED:
+ /* enums will be assumed unsigned */
+ *val = btf_is_enum(mt) ||
+ (btf_int_encoding(mt) & BTF_INT_SIGNED);
+ if (validate)
+ *validate = true; /* signedness is never ambiguous */
+ break;
+ case BPF_FIELD_LSHIFT_U64:
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ *val = 64 - (bit_off + bit_sz - byte_off * 8);
+#else
+ *val = (8 - byte_sz) * 8 + (bit_off - byte_off * 8);
+#endif
+ break;
+ case BPF_FIELD_RSHIFT_U64:
+ *val = 64 - bit_sz;
+ if (validate)
+ *validate = true; /* right shift is never ambiguous */
+ break;
+ case BPF_FIELD_EXISTS:
+ default:
+ pr_warn("prog '%s': unknown relo %d at insn #%d\n",
+ bpf_program__title(prog, false),
+ relo->kind, relo->insn_off / 8);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Patch relocatable BPF instruction.
+ *
+ * Patched value is determined by relocation kind and target specification.
+ * For field existence relocation target spec will be NULL if field is not
+ * found.
+ * Expected insn->imm value is determined using relocation kind and local
+ * spec, and is checked before patching instruction. If actual insn->imm value
+ * is wrong, bail out with error.
+ *
+ * Currently three kinds of BPF instructions are supported:
+ * 1. rX = <imm> (assignment with immediate operand);
+ * 2. rX += <imm> (arithmetic operations with immediate operand);
+ */
+static int bpf_core_reloc_insn(struct bpf_program *prog,
+ const struct bpf_field_reloc *relo,
+ const struct bpf_core_spec *local_spec,
+ const struct bpf_core_spec *targ_spec)
+{
+ bool failed = false, validate = true;
+ __u32 orig_val, new_val;
+ struct bpf_insn *insn;
+ int insn_idx, err;
+ __u8 class;
+
+ if (relo->insn_off % sizeof(struct bpf_insn))
+ return -EINVAL;
+ insn_idx = relo->insn_off / sizeof(struct bpf_insn);
+
+ if (relo->kind == BPF_FIELD_EXISTS) {
+ orig_val = 1; /* can't generate EXISTS relo w/o local field */
+ new_val = targ_spec ? 1 : 0;
+ } else if (!targ_spec) {
+ failed = true;
+ new_val = (__u32)-1;
+ } else {
+ err = bpf_core_calc_field_relo(prog, relo, local_spec,
+ &orig_val, &validate);
+ if (err)
+ return err;
+ err = bpf_core_calc_field_relo(prog, relo, targ_spec,
+ &new_val, NULL);
+ if (err)
+ return err;
+ }
+
+ insn = &prog->insns[insn_idx];
+ class = BPF_CLASS(insn->code);
+
+ if (class == BPF_ALU || class == BPF_ALU64) {
+ if (BPF_SRC(insn->code) != BPF_K)
+ return -EINVAL;
+ if (!failed && validate && insn->imm != orig_val) {
+ pr_warn("prog '%s': unexpected insn #%d value: got %u, exp %u -> %u\n",
+ bpf_program__title(prog, false), insn_idx,
+ insn->imm, orig_val, new_val);
+ return -EINVAL;
+ }
+ orig_val = insn->imm;
+ insn->imm = new_val;
+ pr_debug("prog '%s': patched insn #%d (ALU/ALU64)%s imm %u -> %u\n",
+ bpf_program__title(prog, false), insn_idx,
+ failed ? " w/ failed reloc" : "", orig_val, new_val);
+ } else {
+ pr_warn("prog '%s': trying to relocate unrecognized insn #%d, code:%x, src:%x, dst:%x, off:%x, imm:%x\n",
+ bpf_program__title(prog, false),
+ insn_idx, insn->code, insn->src_reg, insn->dst_reg,
+ insn->off, insn->imm);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct btf *btf_load_raw(const char *path)
+{
+ struct btf *btf;
+ size_t read_cnt;
+ struct stat st;
+ void *data;
+ FILE *f;
+
+ if (stat(path, &st))
+ return ERR_PTR(-errno);
+
+ data = malloc(st.st_size);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ f = fopen(path, "rb");
+ if (!f) {
+ btf = ERR_PTR(-errno);
+ goto cleanup;
+ }
+
+ read_cnt = fread(data, 1, st.st_size, f);
+ fclose(f);
+ if (read_cnt < st.st_size) {
+ btf = ERR_PTR(-EBADF);
+ goto cleanup;
+ }
+
+ btf = btf__new(data, read_cnt);
+
+cleanup:
+ free(data);
+ return btf;
+}
+
+/*
+ * Probe few well-known locations for vmlinux kernel image and try to load BTF
+ * data out of it to use for target BTF.
+ */
+static struct btf *bpf_core_find_kernel_btf(void)
+{
+ struct {
+ const char *path_fmt;
+ bool raw_btf;
+ } locations[] = {
+ /* try canonical vmlinux BTF through sysfs first */
+ { "/sys/kernel/btf/vmlinux", true /* raw BTF */ },
+ /* fall back to trying to find vmlinux ELF on disk otherwise */
+ { "/boot/vmlinux-%1$s" },
+ { "/lib/modules/%1$s/vmlinux-%1$s" },
+ { "/lib/modules/%1$s/build/vmlinux" },
+ { "/usr/lib/modules/%1$s/kernel/vmlinux" },
+ { "/usr/lib/debug/boot/vmlinux-%1$s" },
+ { "/usr/lib/debug/boot/vmlinux-%1$s.debug" },
+ { "/usr/lib/debug/lib/modules/%1$s/vmlinux" },
+ };
+ char path[PATH_MAX + 1];
+ struct utsname buf;
+ struct btf *btf;
+ int i;
+
+ uname(&buf);
+
+ for (i = 0; i < ARRAY_SIZE(locations); i++) {
+ snprintf(path, PATH_MAX, locations[i].path_fmt, buf.release);
+
+ if (access(path, R_OK))
+ continue;
+
+ if (locations[i].raw_btf)
+ btf = btf_load_raw(path);
+ else
+ btf = btf__parse_elf(path, NULL);
+
+ pr_debug("loading kernel BTF '%s': %ld\n",
+ path, IS_ERR(btf) ? PTR_ERR(btf) : 0);
+ if (IS_ERR(btf))
+ continue;
+
+ return btf;
+ }
+
+ pr_warn("failed to find valid kernel BTF\n");
+ return ERR_PTR(-ESRCH);
+}
+
+/* Output spec definition in the format:
+ * [<type-id>] (<type-name>) + <raw-spec> => <offset>@<spec>,
+ * where <spec> is a C-syntax view of recorded field access, e.g.: x.a[3].b
+ */
+static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec)
+{
+ const struct btf_type *t;
+ const char *s;
+ __u32 type_id;
+ int i;
+
+ type_id = spec->spec[0].type_id;
+ t = btf__type_by_id(spec->btf, type_id);
+ s = btf__name_by_offset(spec->btf, t->name_off);
+ libbpf_print(level, "[%u] %s + ", type_id, s);
+
+ for (i = 0; i < spec->raw_len; i++)
+ libbpf_print(level, "%d%s", spec->raw_spec[i],
+ i == spec->raw_len - 1 ? " => " : ":");
+
+ libbpf_print(level, "%u.%u @ &x",
+ spec->bit_offset / 8, spec->bit_offset % 8);
+
+ for (i = 0; i < spec->len; i++) {
+ if (spec->spec[i].name)
+ libbpf_print(level, ".%s", spec->spec[i].name);
+ else
+ libbpf_print(level, "[%u]", spec->spec[i].idx);
+ }
+
+}
+
+static size_t bpf_core_hash_fn(const void *key, void *ctx)
+{
+ return (size_t)key;
+}
+
+static bool bpf_core_equal_fn(const void *k1, const void *k2, void *ctx)
+{
+ return k1 == k2;
+}
+
+static void *u32_as_hash_key(__u32 x)
+{
+ return (void *)(uintptr_t)x;
+}
+
+/*
+ * CO-RE relocate single instruction.
+ *
+ * The outline and important points of the algorithm:
+ * 1. For given local type, find corresponding candidate target types.
+ * Candidate type is a type with the same "essential" name, ignoring
+ * everything after last triple underscore (___). E.g., `sample`,
+ * `sample___flavor_one`, `sample___flavor_another_one`, are all candidates
+ * for each other. Names with triple underscore are referred to as
+ * "flavors" and are useful, among other things, to allow to
+ * specify/support incompatible variations of the same kernel struct, which
+ * might differ between different kernel versions and/or build
+ * configurations.
+ *
+ * N.B. Struct "flavors" could be generated by bpftool's BTF-to-C
+ * converter, when deduplicated BTF of a kernel still contains more than
+ * one different types with the same name. In that case, ___2, ___3, etc
+ * are appended starting from second name conflict. But start flavors are
+ * also useful to be defined "locally", in BPF program, to extract same
+ * data from incompatible changes between different kernel
+ * versions/configurations. For instance, to handle field renames between
+ * kernel versions, one can use two flavors of the struct name with the
+ * same common name and use conditional relocations to extract that field,
+ * depending on target kernel version.
+ * 2. For each candidate type, try to match local specification to this
+ * candidate target type. Matching involves finding corresponding
+ * high-level spec accessors, meaning that all named fields should match,
+ * as well as all array accesses should be within the actual bounds. Also,
+ * types should be compatible (see bpf_core_fields_are_compat for details).
+ * 3. It is supported and expected that there might be multiple flavors
+ * matching the spec. As long as all the specs resolve to the same set of
+ * offsets across all candidates, there is no error. If there is any
+ * ambiguity, CO-RE relocation will fail. This is necessary to accomodate
+ * imprefection of BTF deduplication, which can cause slight duplication of
+ * the same BTF type, if some directly or indirectly referenced (by
+ * pointer) type gets resolved to different actual types in different
+ * object files. If such situation occurs, deduplicated BTF will end up
+ * with two (or more) structurally identical types, which differ only in
+ * types they refer to through pointer. This should be OK in most cases and
+ * is not an error.
+ * 4. Candidate types search is performed by linearly scanning through all
+ * types in target BTF. It is anticipated that this is overall more
+ * efficient memory-wise and not significantly worse (if not better)
+ * CPU-wise compared to prebuilding a map from all local type names to
+ * a list of candidate type names. It's also sped up by caching resolved
+ * list of matching candidates per each local "root" type ID, that has at
+ * least one bpf_field_reloc associated with it. This list is shared
+ * between multiple relocations for the same type ID and is updated as some
+ * of the candidates are pruned due to structural incompatibility.
+ */
+static int bpf_core_reloc_field(struct bpf_program *prog,
+ const struct bpf_field_reloc *relo,
+ int relo_idx,
+ const struct btf *local_btf,
+ const struct btf *targ_btf,
+ struct hashmap *cand_cache)
+{
+ const char *prog_name = bpf_program__title(prog, false);
+ struct bpf_core_spec local_spec, cand_spec, targ_spec;
+ const void *type_key = u32_as_hash_key(relo->type_id);
+ const struct btf_type *local_type, *cand_type;
+ const char *local_name, *cand_name;
+ struct ids_vec *cand_ids;
+ __u32 local_id, cand_id;
+ const char *spec_str;
+ int i, j, err;
+
+ local_id = relo->type_id;
+ local_type = btf__type_by_id(local_btf, local_id);
+ if (!local_type)
+ return -EINVAL;
+
+ local_name = btf__name_by_offset(local_btf, local_type->name_off);
+ if (str_is_empty(local_name))
+ return -EINVAL;
+
+ spec_str = btf__name_by_offset(local_btf, relo->access_str_off);
+ if (str_is_empty(spec_str))
+ return -EINVAL;
+
+ err = bpf_core_spec_parse(local_btf, local_id, spec_str, &local_spec);
+ if (err) {
+ pr_warn("prog '%s': relo #%d: parsing [%d] %s + %s failed: %d\n",
+ prog_name, relo_idx, local_id, local_name, spec_str,
+ err);
+ return -EINVAL;
+ }
+
+ pr_debug("prog '%s': relo #%d: kind %d, spec is ", prog_name, relo_idx,
+ relo->kind);
+ bpf_core_dump_spec(LIBBPF_DEBUG, &local_spec);
+ libbpf_print(LIBBPF_DEBUG, "\n");
+
+ if (!hashmap__find(cand_cache, type_key, (void **)&cand_ids)) {
+ cand_ids = bpf_core_find_cands(local_btf, local_id, targ_btf);
+ if (IS_ERR(cand_ids)) {
+ pr_warn("prog '%s': relo #%d: target candidate search failed for [%d] %s: %ld",
+ prog_name, relo_idx, local_id, local_name,
+ PTR_ERR(cand_ids));
+ return PTR_ERR(cand_ids);
+ }
+ err = hashmap__set(cand_cache, type_key, cand_ids, NULL, NULL);
+ if (err) {
+ bpf_core_free_cands(cand_ids);
+ return err;
+ }
+ }
+
+ for (i = 0, j = 0; i < cand_ids->len; i++) {
+ cand_id = cand_ids->data[i];
+ cand_type = btf__type_by_id(targ_btf, cand_id);
+ cand_name = btf__name_by_offset(targ_btf, cand_type->name_off);
+
+ err = bpf_core_spec_match(&local_spec, targ_btf,
+ cand_id, &cand_spec);
+ pr_debug("prog '%s': relo #%d: matching candidate #%d %s against spec ",
+ prog_name, relo_idx, i, cand_name);
+ bpf_core_dump_spec(LIBBPF_DEBUG, &cand_spec);
+ libbpf_print(LIBBPF_DEBUG, ": %d\n", err);
+ if (err < 0) {
+ pr_warn("prog '%s': relo #%d: matching error: %d\n",
+ prog_name, relo_idx, err);
+ return err;
+ }
+ if (err == 0)
+ continue;
+
+ if (j == 0) {
+ targ_spec = cand_spec;
+ } else if (cand_spec.bit_offset != targ_spec.bit_offset) {
+ /* if there are many candidates, they should all
+ * resolve to the same bit offset
+ */
+ pr_warn("prog '%s': relo #%d: offset ambiguity: %u != %u\n",
+ prog_name, relo_idx, cand_spec.bit_offset,
+ targ_spec.bit_offset);
+ return -EINVAL;
+ }
+
+ cand_ids->data[j++] = cand_spec.spec[0].type_id;
+ }
+
+ /*
+ * For BPF_FIELD_EXISTS relo or when relaxed CO-RE reloc mode is
+ * requested, it's expected that we might not find any candidates.
+ * In this case, if field wasn't found in any candidate, the list of
+ * candidates shouldn't change at all, we'll just handle relocating
+ * appropriately, depending on relo's kind.
+ */
+ if (j > 0)
+ cand_ids->len = j;
+
+ if (j == 0 && !prog->obj->relaxed_core_relocs &&
+ relo->kind != BPF_FIELD_EXISTS) {
+ pr_warn("prog '%s': relo #%d: no matching targets found for [%d] %s + %s\n",
+ prog_name, relo_idx, local_id, local_name, spec_str);
+ return -ESRCH;
+ }
+
+ /* bpf_core_reloc_insn should know how to handle missing targ_spec */
+ err = bpf_core_reloc_insn(prog, relo, &local_spec,
+ j ? &targ_spec : NULL);
+ if (err) {
+ pr_warn("prog '%s': relo #%d: failed to patch insn at offset %d: %d\n",
+ prog_name, relo_idx, relo->insn_off, err);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+bpf_core_reloc_fields(struct bpf_object *obj, const char *targ_btf_path)
+{
+ const struct btf_ext_info_sec *sec;
+ const struct bpf_field_reloc *rec;
+ const struct btf_ext_info *seg;
+ struct hashmap_entry *entry;
+ struct hashmap *cand_cache = NULL;
+ struct bpf_program *prog;
+ struct btf *targ_btf;
+ const char *sec_name;
+ int i, err = 0;
+
+ if (targ_btf_path)
+ targ_btf = btf__parse_elf(targ_btf_path, NULL);
+ else
+ targ_btf = bpf_core_find_kernel_btf();
+ if (IS_ERR(targ_btf)) {
+ pr_warn("failed to get target BTF: %ld\n", PTR_ERR(targ_btf));
+ return PTR_ERR(targ_btf);
+ }
+
+ cand_cache = hashmap__new(bpf_core_hash_fn, bpf_core_equal_fn, NULL);
+ if (IS_ERR(cand_cache)) {
+ err = PTR_ERR(cand_cache);
+ goto out;
+ }
+
+ seg = &obj->btf_ext->field_reloc_info;
+ for_each_btf_ext_sec(seg, sec) {
+ sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off);
+ if (str_is_empty(sec_name)) {
+ err = -EINVAL;
+ goto out;
+ }
+ prog = bpf_object__find_program_by_title(obj, sec_name);
+ if (!prog) {
+ pr_warn("failed to find program '%s' for CO-RE offset relocation\n",
+ sec_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ pr_debug("prog '%s': performing %d CO-RE offset relocs\n",
+ sec_name, sec->num_info);
+
+ for_each_btf_ext_rec(seg, sec, i, rec) {
+ err = bpf_core_reloc_field(prog, rec, i, obj->btf,
+ targ_btf, cand_cache);
+ if (err) {
+ pr_warn("prog '%s': relo #%d: failed to relocate: %d\n",
+ sec_name, i, err);
+ goto out;
+ }
+ }
+ }
+
+out:
+ btf__free(targ_btf);
+ if (!IS_ERR_OR_NULL(cand_cache)) {
+ hashmap__for_each_entry(cand_cache, entry, i) {
+ bpf_core_free_cands(entry->value);
+ }
+ hashmap__free(cand_cache);
+ }
+ return err;
+}
+
+static int
+bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
+{
+ int err = 0;
+
+ if (obj->btf_ext->field_reloc_info.len)
+ err = bpf_core_reloc_fields(obj, targ_btf_path);
+
+ return err;
+}
+
+static int
+bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
+ struct reloc_desc *relo)
+{
+ struct bpf_insn *insn, *new_insn;
+ struct bpf_program *text;
+ size_t new_cnt;
+ int err;
+
+ if (relo->type != RELO_CALL)
+ return -LIBBPF_ERRNO__RELOC;
+
+ if (prog->idx == obj->efile.text_shndx) {
+ pr_warn("relo in .text insn %d into off %d (insn #%d)\n",
+ relo->insn_idx, relo->sym_off, relo->sym_off / 8);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+
+ if (prog->main_prog_cnt == 0) {
+ text = bpf_object__find_prog_by_idx(obj, obj->efile.text_shndx);
+ if (!text) {
+ pr_warn("no .text section found yet relo into text exist\n");
+ return -LIBBPF_ERRNO__RELOC;
+ }
+ new_cnt = prog->insns_cnt + text->insns_cnt;
+ new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn));
+ if (!new_insn) {
+ pr_warn("oom in prog realloc\n");
+ return -ENOMEM;
+ }
+ prog->insns = new_insn;
+
+ if (obj->btf_ext) {
+ err = bpf_program_reloc_btf_ext(prog, obj,
+ text->section_name,
+ prog->insns_cnt);
+ if (err)
+ return err;
+ }
+
+ memcpy(new_insn + prog->insns_cnt, text->insns,
+ text->insns_cnt * sizeof(*insn));
+ prog->main_prog_cnt = prog->insns_cnt;
+ prog->insns_cnt = new_cnt;
+ pr_debug("added %zd insn from %s to prog %s\n",
+ text->insns_cnt, text->section_name,
+ prog->section_name);
+ }
+ insn = &prog->insns[relo->insn_idx];
+ insn->imm += relo->sym_off / 8 + prog->main_prog_cnt - relo->insn_idx;
+ return 0;
+}
+
+static int
+bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
+{
+ int i, err;
+
+ if (!prog)
+ return 0;
+
+ if (obj->btf_ext) {
+ err = bpf_program_reloc_btf_ext(prog, obj,
+ prog->section_name, 0);
+ if (err)
+ return err;
+ }
+
+ if (!prog->reloc_desc)
+ return 0;
+
+ for (i = 0; i < prog->nr_reloc; i++) {
+ struct reloc_desc *relo = &prog->reloc_desc[i];
+
+ if (relo->type == RELO_LD64 || relo->type == RELO_DATA) {
+ struct bpf_insn *insn = &prog->insns[relo->insn_idx];
+
+ if (relo->insn_idx + 1 >= (int)prog->insns_cnt) {
+ pr_warn("relocation out of range: '%s'\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+
+ if (relo->type != RELO_DATA) {
+ insn[0].src_reg = BPF_PSEUDO_MAP_FD;
+ } else {
+ insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
+ insn[1].imm = insn[0].imm + relo->sym_off;
+ }
+ insn[0].imm = obj->maps[relo->map_idx].fd;
+ } else if (relo->type == RELO_CALL) {
+ err = bpf_program__reloc_text(prog, obj, relo);
+ if (err)
+ return err;
+ }
+ }
+
+ zfree(&prog->reloc_desc);
+ prog->nr_reloc = 0;
+ return 0;
+}
+
+static int
+bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path)
+{
+ struct bpf_program *prog;
+ size_t i;
+ int err;
+
+ if (obj->btf_ext) {
+ err = bpf_object__relocate_core(obj, targ_btf_path);
+ if (err) {
+ pr_warn("failed to perform CO-RE relocations: %d\n",
+ err);
+ return err;
+ }
+ }
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+
+ err = bpf_program__relocate(prog, obj);
+ if (err) {
+ pr_warn("failed to relocate '%s'\n", prog->section_name);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int bpf_object__collect_reloc(struct bpf_object *obj)
+{
+ int i, err;
+
+ if (!obj_elf_valid(obj)) {
+ pr_warn("Internal error: elf object is closed\n");
+ return -LIBBPF_ERRNO__INTERNAL;
+ }
+
+ for (i = 0; i < obj->efile.nr_reloc_sects; i++) {
+ GElf_Shdr *shdr = &obj->efile.reloc_sects[i].shdr;
+ Elf_Data *data = obj->efile.reloc_sects[i].data;
+ int idx = shdr->sh_info;
+ struct bpf_program *prog;
+
+ if (shdr->sh_type != SHT_REL) {
+ pr_warn("internal error at %d\n", __LINE__);
+ return -LIBBPF_ERRNO__INTERNAL;
+ }
+
+ prog = bpf_object__find_prog_by_idx(obj, idx);
+ if (!prog) {
+ pr_warn("relocation failed: no section(%d)\n", idx);
+ return -LIBBPF_ERRNO__RELOC;
+ }
+
+ err = bpf_program__collect_reloc(prog, shdr, data, obj);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int
+load_program(struct bpf_program *prog, struct bpf_insn *insns, int insns_cnt,
+ char *license, __u32 kern_version, int *pfd)
+{
+ struct bpf_load_program_attr load_attr;
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int log_buf_size = BPF_LOG_BUF_SIZE;
+ char *log_buf;
+ int btf_fd, ret;
+
+ if (!insns || !insns_cnt)
+ return -EINVAL;
+
+ memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+ load_attr.prog_type = prog->type;
+ load_attr.expected_attach_type = prog->expected_attach_type;
+ if (prog->caps->name)
+ load_attr.name = prog->name;
+ load_attr.insns = insns;
+ load_attr.insns_cnt = insns_cnt;
+ load_attr.license = license;
+ if (prog->type == BPF_PROG_TYPE_TRACING) {
+ load_attr.attach_prog_fd = prog->attach_prog_fd;
+ load_attr.attach_btf_id = prog->attach_btf_id;
+ } else {
+ load_attr.kern_version = kern_version;
+ load_attr.prog_ifindex = prog->prog_ifindex;
+ }
+ /* if .BTF.ext was loaded, kernel supports associated BTF for prog */
+ if (prog->obj->btf_ext)
+ btf_fd = bpf_object__btf_fd(prog->obj);
+ else
+ btf_fd = -1;
+ load_attr.prog_btf_fd = btf_fd >= 0 ? btf_fd : 0;
+ load_attr.func_info = prog->func_info;
+ load_attr.func_info_rec_size = prog->func_info_rec_size;
+ load_attr.func_info_cnt = prog->func_info_cnt;
+ load_attr.line_info = prog->line_info;
+ load_attr.line_info_rec_size = prog->line_info_rec_size;
+ load_attr.line_info_cnt = prog->line_info_cnt;
+ load_attr.log_level = prog->log_level;
+ load_attr.prog_flags = prog->prog_flags;
+
+retry_load:
+ log_buf = malloc(log_buf_size);
+ if (!log_buf)
+ pr_warn("Alloc log buffer for bpf loader error, continue without log\n");
+
+ ret = bpf_load_program_xattr(&load_attr, log_buf, log_buf_size);
+
+ if (ret >= 0) {
+ if (load_attr.log_level)
+ pr_debug("verifier log:\n%s", log_buf);
+ *pfd = ret;
+ ret = 0;
+ goto out;
+ }
+
+ if (errno == ENOSPC) {
+ log_buf_size <<= 1;
+ free(log_buf);
+ goto retry_load;
+ }
+ ret = -errno;
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("load bpf program failed: %s\n", cp);
+
+ if (log_buf && log_buf[0] != '\0') {
+ ret = -LIBBPF_ERRNO__VERIFY;
+ pr_warn("-- BEGIN DUMP LOG ---\n");
+ pr_warn("\n%s\n", log_buf);
+ pr_warn("-- END LOG --\n");
+ } else if (load_attr.insns_cnt >= BPF_MAXINSNS) {
+ pr_warn("Program too large (%zu insns), at most %d insns\n",
+ load_attr.insns_cnt, BPF_MAXINSNS);
+ ret = -LIBBPF_ERRNO__PROG2BIG;
+ } else if (load_attr.prog_type != BPF_PROG_TYPE_KPROBE) {
+ /* Wrong program type? */
+ int fd;
+
+ load_attr.prog_type = BPF_PROG_TYPE_KPROBE;
+ load_attr.expected_attach_type = 0;
+ fd = bpf_load_program_xattr(&load_attr, NULL, 0);
+ if (fd >= 0) {
+ close(fd);
+ ret = -LIBBPF_ERRNO__PROGTYPE;
+ goto out;
+ }
+ }
+
+out:
+ free(log_buf);
+ return ret;
+}
+
+int
+bpf_program__load(struct bpf_program *prog,
+ char *license, __u32 kern_version)
+{
+ int err = 0, fd, i;
+
+ if (prog->instances.nr < 0 || !prog->instances.fds) {
+ if (prog->preprocessor) {
+ pr_warn("Internal error: can't load program '%s'\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__INTERNAL;
+ }
+
+ prog->instances.fds = malloc(sizeof(int));
+ if (!prog->instances.fds) {
+ pr_warn("Not enough memory for BPF fds\n");
+ return -ENOMEM;
+ }
+ prog->instances.nr = 1;
+ prog->instances.fds[0] = -1;
+ }
+
+ if (!prog->preprocessor) {
+ if (prog->instances.nr != 1) {
+ pr_warn("Program '%s' is inconsistent: nr(%d) != 1\n",
+ prog->section_name, prog->instances.nr);
+ }
+ err = load_program(prog, prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->instances.fds[0] = fd;
+ goto out;
+ }
+
+ for (i = 0; i < prog->instances.nr; i++) {
+ struct bpf_prog_prep_result result;
+ bpf_program_prep_t preprocessor = prog->preprocessor;
+
+ memset(&result, 0, sizeof(result));
+ err = preprocessor(prog, i, prog->insns,
+ prog->insns_cnt, &result);
+ if (err) {
+ pr_warn("Preprocessing the %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+
+ if (!result.new_insn_ptr || !result.new_insn_cnt) {
+ pr_debug("Skip loading the %dth instance of program '%s'\n",
+ i, prog->section_name);
+ prog->instances.fds[i] = -1;
+ if (result.pfd)
+ *result.pfd = -1;
+ continue;
+ }
+
+ err = load_program(prog, result.new_insn_ptr,
+ result.new_insn_cnt,
+ license, kern_version, &fd);
+
+ if (err) {
+ pr_warn("Loading the %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+
+ if (result.pfd)
+ *result.pfd = fd;
+ prog->instances.fds[i] = fd;
+ }
+out:
+ if (err)
+ pr_warn("failed to load program '%s'\n", prog->section_name);
+ zfree(&prog->insns);
+ prog->insns_cnt = 0;
+ return err;
+}
+
+static bool bpf_program__is_function_storage(const struct bpf_program *prog,
+ const struct bpf_object *obj)
+{
+ return prog->idx == obj->efile.text_shndx && obj->has_pseudo_calls;
+}
+
+static int
+bpf_object__load_progs(struct bpf_object *obj, int log_level)
+{
+ size_t i;
+ int err;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ if (bpf_program__is_function_storage(&obj->programs[i], obj))
+ continue;
+ obj->programs[i].log_level |= log_level;
+ err = bpf_program__load(&obj->programs[i],
+ obj->license,
+ obj->kern_version);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int libbpf_find_attach_btf_id(const char *name,
+ enum bpf_attach_type attach_type,
+ __u32 attach_prog_fd);
+static struct bpf_object *
+__bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz,
+ struct bpf_object_open_opts *opts)
+{
+ const char *pin_root_path;
+ struct bpf_program *prog;
+ struct bpf_object *obj;
+ const char *obj_name;
+ char tmp_name[64];
+ bool relaxed_maps;
+ __u32 attach_prog_fd;
+ int err;
+
+ if (elf_version(EV_CURRENT) == EV_NONE) {
+ pr_warn("failed to init libelf for %s\n",
+ path ? : "(mem buf)");
+ return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
+ }
+
+ if (!OPTS_VALID(opts, bpf_object_open_opts))
+ return ERR_PTR(-EINVAL);
+
+ obj_name = OPTS_GET(opts, object_name, NULL);
+ if (obj_buf) {
+ if (!obj_name) {
+ snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
+ (unsigned long)obj_buf,
+ (unsigned long)obj_buf_sz);
+ obj_name = tmp_name;
+ }
+ path = obj_name;
+ pr_debug("loading object '%s' from buffer\n", obj_name);
+ }
+
+ obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name);
+ if (IS_ERR(obj))
+ return obj;
+
+ obj->relaxed_core_relocs = OPTS_GET(opts, relaxed_core_relocs, false);
+ relaxed_maps = OPTS_GET(opts, relaxed_maps, false);
+ pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
+ attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
+
+ CHECK_ERR(bpf_object__elf_init(obj), err, out);
+ CHECK_ERR(bpf_object__check_endianness(obj), err, out);
+ CHECK_ERR(bpf_object__probe_caps(obj), err, out);
+ CHECK_ERR(bpf_object__elf_collect(obj, relaxed_maps, pin_root_path),
+ err, out);
+ CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
+ bpf_object__elf_finish(obj);
+
+ bpf_object__for_each_program(prog, obj) {
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type attach_type;
+
+ err = libbpf_prog_type_by_name(prog->section_name, &prog_type,
+ &attach_type);
+ if (err == -ESRCH)
+ /* couldn't guess, but user might manually specify */
+ continue;
+ if (err)
+ goto out;
+
+ bpf_program__set_type(prog, prog_type);
+ bpf_program__set_expected_attach_type(prog, attach_type);
+ if (prog_type == BPF_PROG_TYPE_TRACING) {
+ err = libbpf_find_attach_btf_id(prog->section_name,
+ attach_type,
+ attach_prog_fd);
+ if (err <= 0)
+ goto out;
+ prog->attach_btf_id = err;
+ prog->attach_prog_fd = attach_prog_fd;
+ }
+ }
+
+ return obj;
+out:
+ bpf_object__close(obj);
+ return ERR_PTR(err);
+}
+
+static struct bpf_object *
+__bpf_object__open_xattr(struct bpf_object_open_attr *attr, int flags)
+{
+ DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .relaxed_maps = flags & MAPS_RELAX_COMPAT,
+ );
+
+ /* param validation */
+ if (!attr->file)
+ return NULL;
+
+ pr_debug("loading %s\n", attr->file);
+ return __bpf_object__open(attr->file, NULL, 0, &opts);
+}
+
+struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr)
+{
+ return __bpf_object__open_xattr(attr, 0);
+}
+
+struct bpf_object *bpf_object__open(const char *path)
+{
+ struct bpf_object_open_attr attr = {
+ .file = path,
+ .prog_type = BPF_PROG_TYPE_UNSPEC,
+ };
+
+ return bpf_object__open_xattr(&attr);
+}
+
+struct bpf_object *
+bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts)
+{
+ if (!path)
+ return ERR_PTR(-EINVAL);
+
+ pr_debug("loading %s\n", path);
+
+ return __bpf_object__open(path, NULL, 0, opts);
+}
+
+struct bpf_object *
+bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
+ struct bpf_object_open_opts *opts)
+{
+ if (!obj_buf || obj_buf_sz == 0)
+ return ERR_PTR(-EINVAL);
+
+ return __bpf_object__open(NULL, obj_buf, obj_buf_sz, opts);
+}
+
+struct bpf_object *
+bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
+ const char *name)
+{
+ DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .object_name = name,
+ /* wrong default, but backwards-compatible */
+ .relaxed_maps = true,
+ );
+
+ /* returning NULL is wrong, but backwards-compatible */
+ if (!obj_buf || obj_buf_sz == 0)
+ return NULL;
+
+ return bpf_object__open_mem(obj_buf, obj_buf_sz, &opts);
+}
+
+int bpf_object__unload(struct bpf_object *obj)
+{
+ size_t i;
+
+ if (!obj)
+ return -EINVAL;
+
+ for (i = 0; i < obj->nr_maps; i++)
+ zclose(obj->maps[i].fd);
+
+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__unload(&obj->programs[i]);
+
+ return 0;
+}
+
+int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
+{
+ struct bpf_object *obj;
+ int err, i;
+
+ if (!attr)
+ return -EINVAL;
+ obj = attr->obj;
+ if (!obj)
+ return -EINVAL;
+
+ if (obj->loaded) {
+ pr_warn("object should not be loaded twice\n");
+ return -EINVAL;
+ }
+
+ obj->loaded = true;
+
+ CHECK_ERR(bpf_object__create_maps(obj), err, out);
+ CHECK_ERR(bpf_object__relocate(obj, attr->target_btf_path), err, out);
+ CHECK_ERR(bpf_object__load_progs(obj, attr->log_level), err, out);
+
+ return 0;
+out:
+ /* unpin any maps that were auto-pinned during load */
+ for (i = 0; i < obj->nr_maps; i++)
+ if (obj->maps[i].pinned && !obj->maps[i].reused)
+ bpf_map__unpin(&obj->maps[i], NULL);
+
+ bpf_object__unload(obj);
+ pr_warn("failed to load object '%s'\n", obj->path);
+ return err;
+}
+
+int bpf_object__load(struct bpf_object *obj)
+{
+ struct bpf_object_load_attr attr = {
+ .obj = obj,
+ };
+
+ return bpf_object__load_xattr(&attr);
+}
+
+static int make_parent_dir(const char *path)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ char *dname, *dir;
+ int err = 0;
+
+ dname = strdup(path);
+ if (dname == NULL)
+ return -ENOMEM;
+
+ dir = dirname(dname);
+ if (mkdir(dir, 0700) && errno != EEXIST)
+ err = -errno;
+
+ free(dname);
+ if (err) {
+ cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
+ pr_warn("failed to mkdir %s: %s\n", path, cp);
+ }
+ return err;
+}
+
+static int check_path(const char *path)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ struct statfs st_fs;
+ char *dname, *dir;
+ int err = 0;
+
+ if (path == NULL)
+ return -EINVAL;
+
+ dname = strdup(path);
+ if (dname == NULL)
+ return -ENOMEM;
+
+ dir = dirname(dname);
+ if (statfs(dir, &st_fs)) {
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("failed to statfs %s: %s\n", dir, cp);
+ err = -errno;
+ }
+ free(dname);
+
+ if (!err && st_fs.f_type != BPF_FS_MAGIC) {
+ pr_warn("specified path %s is not on BPF FS\n", path);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
+ int instance)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int err;
+
+ err = make_parent_dir(path);
+ if (err)
+ return err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warn("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (instance < 0 || instance >= prog->instances.nr) {
+ pr_warn("invalid prog instance %d of prog %s (max %d)\n",
+ instance, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ if (bpf_obj_pin(prog->instances.fds[instance], path)) {
+ cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
+ pr_warn("failed to pin program: %s\n", cp);
+ return -errno;
+ }
+ pr_debug("pinned program '%s'\n", path);
+
+ return 0;
+}
+
+int bpf_program__unpin_instance(struct bpf_program *prog, const char *path,
+ int instance)
+{
+ int err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warn("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (instance < 0 || instance >= prog->instances.nr) {
+ pr_warn("invalid prog instance %d of prog %s (max %d)\n",
+ instance, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ err = unlink(path);
+ if (err != 0)
+ return -errno;
+ pr_debug("unpinned program '%s'\n", path);
+
+ return 0;
+}
+
+int bpf_program__pin(struct bpf_program *prog, const char *path)
+{
+ int i, err;
+
+ err = make_parent_dir(path);
+ if (err)
+ return err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warn("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (prog->instances.nr <= 0) {
+ pr_warn("no instances of prog %s to pin\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+
+ if (prog->instances.nr == 1) {
+ /* don't create subdirs when pinning single instance */
+ return bpf_program__pin_instance(prog, path, 0);
+ }
+
+ for (i = 0; i < prog->instances.nr; i++) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+ if (len < 0) {
+ err = -EINVAL;
+ goto err_unpin;
+ } else if (len >= PATH_MAX) {
+ err = -ENAMETOOLONG;
+ goto err_unpin;
+ }
+
+ err = bpf_program__pin_instance(prog, buf, i);
+ if (err)
+ goto err_unpin;
+ }
+
+ return 0;
+
+err_unpin:
+ for (i = i - 1; i >= 0; i--) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+ if (len < 0)
+ continue;
+ else if (len >= PATH_MAX)
+ continue;
+
+ bpf_program__unpin_instance(prog, buf, i);
+ }
+
+ rmdir(path);
+
+ return err;
+}
+
+int bpf_program__unpin(struct bpf_program *prog, const char *path)
+{
+ int i, err;
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ if (prog == NULL) {
+ pr_warn("invalid program pointer\n");
+ return -EINVAL;
+ }
+
+ if (prog->instances.nr <= 0) {
+ pr_warn("no instances of prog %s to pin\n",
+ prog->section_name);
+ return -EINVAL;
+ }
+
+ if (prog->instances.nr == 1) {
+ /* don't create subdirs when pinning single instance */
+ return bpf_program__unpin_instance(prog, path, 0);
+ }
+
+ for (i = 0; i < prog->instances.nr; i++) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_program__unpin_instance(prog, buf, i);
+ if (err)
+ return err;
+ }
+
+ err = rmdir(path);
+ if (err)
+ return -errno;
+
+ return 0;
+}
+
+int bpf_map__pin(struct bpf_map *map, const char *path)
+{
+ char *cp, errmsg[STRERR_BUFSIZE];
+ int err;
+
+ if (map == NULL) {
+ pr_warn("invalid map pointer\n");
+ return -EINVAL;
+ }
+
+ if (map->pin_path) {
+ if (path && strcmp(path, map->pin_path)) {
+ pr_warn("map '%s' already has pin path '%s' different from '%s'\n",
+ bpf_map__name(map), map->pin_path, path);
+ return -EINVAL;
+ } else if (map->pinned) {
+ pr_debug("map '%s' already pinned at '%s'; not re-pinning\n",
+ bpf_map__name(map), map->pin_path);
+ return 0;
+ }
+ } else {
+ if (!path) {
+ pr_warn("missing a path to pin map '%s' at\n",
+ bpf_map__name(map));
+ return -EINVAL;
+ } else if (map->pinned) {
+ pr_warn("map '%s' already pinned\n", bpf_map__name(map));
+ return -EEXIST;
+ }
+
+ map->pin_path = strdup(path);
+ if (!map->pin_path) {
+ err = -errno;
+ goto out_err;
+ }
+ }
+
+ err = make_parent_dir(map->pin_path);
+ if (err)
+ return err;
+
+ err = check_path(map->pin_path);
+ if (err)
+ return err;
+
+ if (bpf_obj_pin(map->fd, map->pin_path)) {
+ err = -errno;
+ goto out_err;
+ }
+
+ map->pinned = true;
+ pr_debug("pinned map '%s'\n", map->pin_path);
+
+ return 0;
+
+out_err:
+ cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
+ pr_warn("failed to pin map: %s\n", cp);
+ return err;
+}
+
+int bpf_map__unpin(struct bpf_map *map, const char *path)
+{
+ int err;
+
+ if (map == NULL) {
+ pr_warn("invalid map pointer\n");
+ return -EINVAL;
+ }
+
+ if (map->pin_path) {
+ if (path && strcmp(path, map->pin_path)) {
+ pr_warn("map '%s' already has pin path '%s' different from '%s'\n",
+ bpf_map__name(map), map->pin_path, path);
+ return -EINVAL;
+ }
+ path = map->pin_path;
+ } else if (!path) {
+ pr_warn("no path to unpin map '%s' from\n",
+ bpf_map__name(map));
+ return -EINVAL;
+ }
+
+ err = check_path(path);
+ if (err)
+ return err;
+
+ err = unlink(path);
+ if (err != 0)
+ return -errno;
+
+ map->pinned = false;
+ pr_debug("unpinned map '%s' from '%s'\n", bpf_map__name(map), path);
+
+ return 0;
+}
+
+int bpf_map__set_pin_path(struct bpf_map *map, const char *path)
+{
+ char *new = NULL;
+
+ if (path) {
+ new = strdup(path);
+ if (!new)
+ return -errno;
+ }
+
+ free(map->pin_path);
+ map->pin_path = new;
+ return 0;
+}
+
+const char *bpf_map__get_pin_path(const struct bpf_map *map)
+{
+ return map->pin_path;
+}
+
+bool bpf_map__is_pinned(const struct bpf_map *map)
+{
+ return map->pinned;
+}
+
+int bpf_object__pin_maps(struct bpf_object *obj, const char *path)
+{
+ struct bpf_map *map;
+ int err;
+
+ if (!obj)
+ return -ENOENT;
+
+ if (!obj->loaded) {
+ pr_warn("object not yet loaded; load it first\n");
+ return -ENOENT;
+ }
+
+ bpf_object__for_each_map(map, obj) {
+ char *pin_path = NULL;
+ char buf[PATH_MAX];
+
+ if (path) {
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ bpf_map__name(map));
+ if (len < 0) {
+ err = -EINVAL;
+ goto err_unpin_maps;
+ } else if (len >= PATH_MAX) {
+ err = -ENAMETOOLONG;
+ goto err_unpin_maps;
+ }
+ pin_path = buf;
+ } else if (!map->pin_path) {
+ continue;
+ }
+
+ err = bpf_map__pin(map, pin_path);
+ if (err)
+ goto err_unpin_maps;
+ }
+
+ return 0;
+
+err_unpin_maps:
+ while ((map = bpf_map__prev(map, obj))) {
+ if (!map->pin_path)
+ continue;
+
+ bpf_map__unpin(map, NULL);
+ }
+
+ return err;
+}
+
+int bpf_object__unpin_maps(struct bpf_object *obj, const char *path)
+{
+ struct bpf_map *map;
+ int err;
+
+ if (!obj)
+ return -ENOENT;
+
+ bpf_object__for_each_map(map, obj) {
+ char *pin_path = NULL;
+ char buf[PATH_MAX];
+
+ if (path) {
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ bpf_map__name(map));
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+ pin_path = buf;
+ } else if (!map->pin_path) {
+ continue;
+ }
+
+ err = bpf_map__unpin(map, pin_path);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int bpf_object__pin_programs(struct bpf_object *obj, const char *path)
+{
+ struct bpf_program *prog;
+ int err;
+
+ if (!obj)
+ return -ENOENT;
+
+ if (!obj->loaded) {
+ pr_warn("object not yet loaded; load it first\n");
+ return -ENOENT;
+ }
+
+ bpf_object__for_each_program(prog, obj) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ prog->pin_name);
+ if (len < 0) {
+ err = -EINVAL;
+ goto err_unpin_programs;
+ } else if (len >= PATH_MAX) {
+ err = -ENAMETOOLONG;
+ goto err_unpin_programs;
+ }
+
+ err = bpf_program__pin(prog, buf);
+ if (err)
+ goto err_unpin_programs;
+ }
+
+ return 0;
+
+err_unpin_programs:
+ while ((prog = bpf_program__prev(prog, obj))) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ prog->pin_name);
+ if (len < 0)
+ continue;
+ else if (len >= PATH_MAX)
+ continue;
+
+ bpf_program__unpin(prog, buf);
+ }
+
+ return err;
+}
+
+int bpf_object__unpin_programs(struct bpf_object *obj, const char *path)
+{
+ struct bpf_program *prog;
+ int err;
+
+ if (!obj)
+ return -ENOENT;
+
+ bpf_object__for_each_program(prog, obj) {
+ char buf[PATH_MAX];
+ int len;
+
+ len = snprintf(buf, PATH_MAX, "%s/%s", path,
+ prog->pin_name);
+ if (len < 0)
+ return -EINVAL;
+ else if (len >= PATH_MAX)
+ return -ENAMETOOLONG;
+
+ err = bpf_program__unpin(prog, buf);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+int bpf_object__pin(struct bpf_object *obj, const char *path)
+{
+ int err;
+
+ err = bpf_object__pin_maps(obj, path);
+ if (err)
+ return err;
+
+ err = bpf_object__pin_programs(obj, path);
+ if (err) {
+ bpf_object__unpin_maps(obj, path);
+ return err;
+ }
+
+ return 0;
+}
+
+void bpf_object__close(struct bpf_object *obj)
+{
+ size_t i;
+
+ if (!obj)
+ return;
+
+ if (obj->clear_priv)
+ obj->clear_priv(obj, obj->priv);
+
+ bpf_object__elf_finish(obj);
+ bpf_object__unload(obj);
+ btf__free(obj->btf);
+ btf_ext__free(obj->btf_ext);
+
+ for (i = 0; i < obj->nr_maps; i++) {
+ zfree(&obj->maps[i].name);
+ zfree(&obj->maps[i].pin_path);
+ if (obj->maps[i].clear_priv)
+ obj->maps[i].clear_priv(&obj->maps[i],
+ obj->maps[i].priv);
+ obj->maps[i].priv = NULL;
+ obj->maps[i].clear_priv = NULL;
+ }
+
+ zfree(&obj->sections.rodata);
+ zfree(&obj->sections.data);
+ zfree(&obj->maps);
+ obj->nr_maps = 0;
+
+ if (obj->programs && obj->nr_programs) {
+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__exit(&obj->programs[i]);
+ }
+ zfree(&obj->programs);
+
+ list_del(&obj->list);
+ free(obj);
+}
+
+struct bpf_object *
+bpf_object__next(struct bpf_object *prev)
+{
+ struct bpf_object *next;
+
+ if (!prev)
+ next = list_first_entry(&bpf_objects_list,
+ struct bpf_object,
+ list);
+ else
+ next = list_next_entry(prev, list);
+
+ /* Empty list is noticed here so don't need checking on entry. */
+ if (&next->list == &bpf_objects_list)
+ return NULL;
+
+ return next;
+}
+
+const char *bpf_object__name(const struct bpf_object *obj)
+{
+ return obj ? obj->name : ERR_PTR(-EINVAL);
+}
+
+unsigned int bpf_object__kversion(const struct bpf_object *obj)
+{
+ return obj ? obj->kern_version : 0;
+}
+
+struct btf *bpf_object__btf(const struct bpf_object *obj)
+{
+ return obj ? obj->btf : NULL;
+}
+
+int bpf_object__btf_fd(const struct bpf_object *obj)
+{
+ return obj->btf ? btf__fd(obj->btf) : -1;
+}
+
+int bpf_object__set_priv(struct bpf_object *obj, void *priv,
+ bpf_object_clear_priv_t clear_priv)
+{
+ if (obj->priv && obj->clear_priv)
+ obj->clear_priv(obj, obj->priv);
+
+ obj->priv = priv;
+ obj->clear_priv = clear_priv;
+ return 0;
+}
+
+void *bpf_object__priv(const struct bpf_object *obj)
+{
+ return obj ? obj->priv : ERR_PTR(-EINVAL);
+}
+
+static struct bpf_program *
+__bpf_program__iter(const struct bpf_program *p, const struct bpf_object *obj,
+ bool forward)
+{
+ size_t nr_programs = obj->nr_programs;
+ ssize_t idx;
+
+ if (!nr_programs)
+ return NULL;
+
+ if (!p)
+ /* Iter from the beginning */
+ return forward ? &obj->programs[0] :
+ &obj->programs[nr_programs - 1];
+
+ if (p->obj != obj) {
+ pr_warn("error: program handler doesn't match object\n");
+ return NULL;
+ }
+
+ idx = (p - obj->programs) + (forward ? 1 : -1);
+ if (idx >= obj->nr_programs || idx < 0)
+ return NULL;
+ return &obj->programs[idx];
+}
+
+struct bpf_program *
+bpf_program__next(struct bpf_program *prev, const struct bpf_object *obj)
+{
+ struct bpf_program *prog = prev;
+
+ do {
+ prog = __bpf_program__iter(prog, obj, true);
+ } while (prog && bpf_program__is_function_storage(prog, obj));
+
+ return prog;
+}
+
+struct bpf_program *
+bpf_program__prev(struct bpf_program *next, const struct bpf_object *obj)
+{
+ struct bpf_program *prog = next;
+
+ do {
+ prog = __bpf_program__iter(prog, obj, false);
+ } while (prog && bpf_program__is_function_storage(prog, obj));
+
+ return prog;
+}
+
+int bpf_program__set_priv(struct bpf_program *prog, void *priv,
+ bpf_program_clear_priv_t clear_priv)
+{
+ if (prog->priv && prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = priv;
+ prog->clear_priv = clear_priv;
+ return 0;
+}
+
+void *bpf_program__priv(const struct bpf_program *prog)
+{
+ return prog ? prog->priv : ERR_PTR(-EINVAL);
+}
+
+void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
+{
+ prog->prog_ifindex = ifindex;
+}
+
+const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
+{
+ const char *title;
+
+ title = prog->section_name;
+ if (needs_copy) {
+ title = strdup(title);
+ if (!title) {
+ pr_warn("failed to strdup program title\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+
+ return title;
+}
+
+int bpf_program__fd(const struct bpf_program *prog)
+{
+ return bpf_program__nth_fd(prog, 0);
+}
+
+size_t bpf_program__size(const struct bpf_program *prog)
+{
+ return prog->insns_cnt * sizeof(struct bpf_insn);
+}
+
+int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
+ bpf_program_prep_t prep)
+{
+ int *instances_fds;
+
+ if (nr_instances <= 0 || !prep)
+ return -EINVAL;
+
+ if (prog->instances.nr > 0 || prog->instances.fds) {
+ pr_warn("Can't set pre-processor after loading\n");
+ return -EINVAL;
+ }
+
+ instances_fds = malloc(sizeof(int) * nr_instances);
+ if (!instances_fds) {
+ pr_warn("alloc memory failed for fds\n");
+ return -ENOMEM;
+ }
+
+ /* fill all fd with -1 */
+ memset(instances_fds, -1, sizeof(int) * nr_instances);
+
+ prog->instances.nr = nr_instances;
+ prog->instances.fds = instances_fds;
+ prog->preprocessor = prep;
+ return 0;
+}
+
+int bpf_program__nth_fd(const struct bpf_program *prog, int n)
+{
+ int fd;
+
+ if (!prog)
+ return -EINVAL;
+
+ if (n >= prog->instances.nr || n < 0) {
+ pr_warn("Can't get the %dth fd from program %s: only %d instances\n",
+ n, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ fd = prog->instances.fds[n];
+ if (fd < 0) {
+ pr_warn("%dth instance of program '%s' is invalid\n",
+ n, prog->section_name);
+ return -ENOENT;
+ }
+
+ return fd;
+}
+
+enum bpf_prog_type bpf_program__get_type(struct bpf_program *prog)
+{
+ return prog->type;
+}
+
+void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
+{
+ prog->type = type;
+}
+
+static bool bpf_program__is_type(const struct bpf_program *prog,
+ enum bpf_prog_type type)
+{
+ return prog ? (prog->type == type) : false;
+}
+
+#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
+int bpf_program__set_##NAME(struct bpf_program *prog) \
+{ \
+ if (!prog) \
+ return -EINVAL; \
+ bpf_program__set_type(prog, TYPE); \
+ return 0; \
+} \
+ \
+bool bpf_program__is_##NAME(const struct bpf_program *prog) \
+{ \
+ return bpf_program__is_type(prog, TYPE); \
+} \
+
+BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
+BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
+BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
+BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
+BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
+BPF_PROG_TYPE_FNS(raw_tracepoint, BPF_PROG_TYPE_RAW_TRACEPOINT);
+BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
+BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
+BPF_PROG_TYPE_FNS(tracing, BPF_PROG_TYPE_TRACING);
+
+enum bpf_attach_type
+bpf_program__get_expected_attach_type(struct bpf_program *prog)
+{
+ return prog->expected_attach_type;
+}
+
+void bpf_program__set_expected_attach_type(struct bpf_program *prog,
+ enum bpf_attach_type type)
+{
+ prog->expected_attach_type = type;
+}
+
+#define BPF_PROG_SEC_IMPL(string, ptype, eatype, is_attachable, btf, atype) \
+ { string, sizeof(string) - 1, ptype, eatype, is_attachable, btf, atype }
+
+/* Programs that can NOT be attached. */
+#define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_IMPL(string, ptype, 0, 0, 0, 0)
+
+/* Programs that can be attached. */
+#define BPF_APROG_SEC(string, ptype, atype) \
+ BPF_PROG_SEC_IMPL(string, ptype, 0, 1, 0, atype)
+
+/* Programs that must specify expected attach type at load time. */
+#define BPF_EAPROG_SEC(string, ptype, eatype) \
+ BPF_PROG_SEC_IMPL(string, ptype, eatype, 1, 0, eatype)
+
+/* Programs that use BTF to identify attach point */
+#define BPF_PROG_BTF(string, ptype, eatype) \
+ BPF_PROG_SEC_IMPL(string, ptype, eatype, 0, 1, 0)
+
+/* Programs that can be attached but attach type can't be identified by section
+ * name. Kept for backward compatibility.
+ */
+#define BPF_APROG_COMPAT(string, ptype) BPF_PROG_SEC(string, ptype)
+
+static const struct {
+ const char *sec;
+ size_t len;
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type expected_attach_type;
+ bool is_attachable;
+ bool is_attach_btf;
+ enum bpf_attach_type attach_type;
+} section_names[] = {
+ BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER),
+ BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE),
+ BPF_PROG_SEC("uprobe/", BPF_PROG_TYPE_KPROBE),
+ BPF_PROG_SEC("kretprobe/", BPF_PROG_TYPE_KPROBE),
+ BPF_PROG_SEC("uretprobe/", BPF_PROG_TYPE_KPROBE),
+ BPF_PROG_SEC("classifier", BPF_PROG_TYPE_SCHED_CLS),
+ BPF_PROG_SEC("action", BPF_PROG_TYPE_SCHED_ACT),
+ BPF_PROG_SEC("tracepoint/", BPF_PROG_TYPE_TRACEPOINT),
+ BPF_PROG_SEC("tp/", BPF_PROG_TYPE_TRACEPOINT),
+ BPF_PROG_SEC("raw_tracepoint/", BPF_PROG_TYPE_RAW_TRACEPOINT),
+ BPF_PROG_SEC("raw_tp/", BPF_PROG_TYPE_RAW_TRACEPOINT),
+ BPF_PROG_BTF("tp_btf/", BPF_PROG_TYPE_TRACING,
+ BPF_TRACE_RAW_TP),
+ BPF_PROG_BTF("fentry/", BPF_PROG_TYPE_TRACING,
+ BPF_TRACE_FENTRY),
+ BPF_PROG_BTF("fexit/", BPF_PROG_TYPE_TRACING,
+ BPF_TRACE_FEXIT),
+ BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
+ BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
+ BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
+ BPF_PROG_SEC("lwt_out", BPF_PROG_TYPE_LWT_OUT),
+ BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT),
+ BPF_PROG_SEC("lwt_seg6local", BPF_PROG_TYPE_LWT_SEG6LOCAL),
+ BPF_APROG_SEC("cgroup_skb/ingress", BPF_PROG_TYPE_CGROUP_SKB,
+ BPF_CGROUP_INET_INGRESS),
+ BPF_APROG_SEC("cgroup_skb/egress", BPF_PROG_TYPE_CGROUP_SKB,
+ BPF_CGROUP_INET_EGRESS),
+ BPF_APROG_COMPAT("cgroup/skb", BPF_PROG_TYPE_CGROUP_SKB),
+ BPF_APROG_SEC("cgroup/sock", BPF_PROG_TYPE_CGROUP_SOCK,
+ BPF_CGROUP_INET_SOCK_CREATE),
+ BPF_EAPROG_SEC("cgroup/post_bind4", BPF_PROG_TYPE_CGROUP_SOCK,
+ BPF_CGROUP_INET4_POST_BIND),
+ BPF_EAPROG_SEC("cgroup/post_bind6", BPF_PROG_TYPE_CGROUP_SOCK,
+ BPF_CGROUP_INET6_POST_BIND),
+ BPF_APROG_SEC("cgroup/dev", BPF_PROG_TYPE_CGROUP_DEVICE,
+ BPF_CGROUP_DEVICE),
+ BPF_APROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS,
+ BPF_CGROUP_SOCK_OPS),
+ BPF_APROG_SEC("sk_skb/stream_parser", BPF_PROG_TYPE_SK_SKB,
+ BPF_SK_SKB_STREAM_PARSER),
+ BPF_APROG_SEC("sk_skb/stream_verdict", BPF_PROG_TYPE_SK_SKB,
+ BPF_SK_SKB_STREAM_VERDICT),
+ BPF_APROG_COMPAT("sk_skb", BPF_PROG_TYPE_SK_SKB),
+ BPF_APROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG,
+ BPF_SK_MSG_VERDICT),
+ BPF_APROG_SEC("lirc_mode2", BPF_PROG_TYPE_LIRC_MODE2,
+ BPF_LIRC_MODE2),
+ BPF_APROG_SEC("flow_dissector", BPF_PROG_TYPE_FLOW_DISSECTOR,
+ BPF_FLOW_DISSECTOR),
+ BPF_EAPROG_SEC("cgroup/bind4", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_INET4_BIND),
+ BPF_EAPROG_SEC("cgroup/bind6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_INET6_BIND),
+ BPF_EAPROG_SEC("cgroup/connect4", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_INET4_CONNECT),
+ BPF_EAPROG_SEC("cgroup/connect6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_INET6_CONNECT),
+ BPF_EAPROG_SEC("cgroup/sendmsg4", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP4_SENDMSG),
+ BPF_EAPROG_SEC("cgroup/sendmsg6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP6_SENDMSG),
+ BPF_EAPROG_SEC("cgroup/recvmsg4", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP4_RECVMSG),
+ BPF_EAPROG_SEC("cgroup/recvmsg6", BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_CGROUP_UDP6_RECVMSG),
+ BPF_EAPROG_SEC("cgroup/sysctl", BPF_PROG_TYPE_CGROUP_SYSCTL,
+ BPF_CGROUP_SYSCTL),
+ BPF_EAPROG_SEC("cgroup/getsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ BPF_CGROUP_GETSOCKOPT),
+ BPF_EAPROG_SEC("cgroup/setsockopt", BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ BPF_CGROUP_SETSOCKOPT),
+};
+
+#undef BPF_PROG_SEC_IMPL
+#undef BPF_PROG_SEC
+#undef BPF_APROG_SEC
+#undef BPF_EAPROG_SEC
+#undef BPF_APROG_COMPAT
+
+#define MAX_TYPE_NAME_SIZE 32
+
+static char *libbpf_get_type_names(bool attach_type)
+{
+ int i, len = ARRAY_SIZE(section_names) * MAX_TYPE_NAME_SIZE;
+ char *buf;
+
+ buf = malloc(len);
+ if (!buf)
+ return NULL;
+
+ buf[0] = '\0';
+ /* Forge string buf with all available names */
+ for (i = 0; i < ARRAY_SIZE(section_names); i++) {
+ if (attach_type && !section_names[i].is_attachable)
+ continue;
+
+ if (strlen(buf) + strlen(section_names[i].sec) + 2 > len) {
+ free(buf);
+ return NULL;
+ }
+ strcat(buf, " ");
+ strcat(buf, section_names[i].sec);
+ }
+
+ return buf;
+}
+
+int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
+ enum bpf_attach_type *expected_attach_type)
+{
+ char *type_names;
+ int i;
+
+ if (!name)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(section_names); i++) {
+ if (strncmp(name, section_names[i].sec, section_names[i].len))
+ continue;
+ *prog_type = section_names[i].prog_type;
+ *expected_attach_type = section_names[i].expected_attach_type;
+ return 0;
+ }
+ pr_warn("failed to guess program type from ELF section '%s'\n", name);
+ type_names = libbpf_get_type_names(false);
+ if (type_names != NULL) {
+ pr_info("supported section(type) names are:%s\n", type_names);
+ free(type_names);
+ }
+
+ return -ESRCH;
+}
+
+#define BTF_PREFIX "btf_trace_"
+int libbpf_find_vmlinux_btf_id(const char *name,
+ enum bpf_attach_type attach_type)
+{
+ struct btf *btf = bpf_core_find_kernel_btf();
+ char raw_tp_btf[128] = BTF_PREFIX;
+ char *dst = raw_tp_btf + sizeof(BTF_PREFIX) - 1;
+ const char *btf_name;
+ int err = -EINVAL;
+ __u32 kind;
+
+ if (IS_ERR(btf)) {
+ pr_warn("vmlinux BTF is not found\n");
+ return -EINVAL;
+ }
+
+ if (attach_type == BPF_TRACE_RAW_TP) {
+ /* prepend "btf_trace_" prefix per kernel convention */
+ strncat(dst, name, sizeof(raw_tp_btf) - sizeof(BTF_PREFIX));
+ btf_name = raw_tp_btf;
+ kind = BTF_KIND_TYPEDEF;
+ } else {
+ btf_name = name;
+ kind = BTF_KIND_FUNC;
+ }
+ err = btf__find_by_name_kind(btf, btf_name, kind);
+ btf__free(btf);
+ return err;
+}
+
+static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd)
+{
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info *info;
+ struct btf *btf = NULL;
+ int err = -EINVAL;
+
+ info_linear = bpf_program__get_prog_info_linear(attach_prog_fd, 0);
+ if (IS_ERR_OR_NULL(info_linear)) {
+ pr_warn("failed get_prog_info_linear for FD %d\n",
+ attach_prog_fd);
+ return -EINVAL;
+ }
+ info = &info_linear->info;
+ if (!info->btf_id) {
+ pr_warn("The target program doesn't have BTF\n");
+ goto out;
+ }
+ if (btf__get_from_id(info->btf_id, &btf)) {
+ pr_warn("Failed to get BTF of the program\n");
+ goto out;
+ }
+ err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
+ btf__free(btf);
+ if (err <= 0) {
+ pr_warn("%s is not found in prog's BTF\n", name);
+ goto out;
+ }
+out:
+ free(info_linear);
+ return err;
+}
+
+static int libbpf_find_attach_btf_id(const char *name,
+ enum bpf_attach_type attach_type,
+ __u32 attach_prog_fd)
+{
+ int i, err;
+
+ if (!name)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(section_names); i++) {
+ if (!section_names[i].is_attach_btf)
+ continue;
+ if (strncmp(name, section_names[i].sec, section_names[i].len))
+ continue;
+ if (attach_prog_fd)
+ err = libbpf_find_prog_btf_id(name + section_names[i].len,
+ attach_prog_fd);
+ else
+ err = libbpf_find_vmlinux_btf_id(name + section_names[i].len,
+ attach_type);
+ if (err <= 0)
+ pr_warn("%s is not found in vmlinux BTF\n", name);
+ return err;
+ }
+ pr_warn("failed to identify btf_id based on ELF section name '%s'\n", name);
+ return -ESRCH;
+}
+
+int libbpf_attach_type_by_name(const char *name,
+ enum bpf_attach_type *attach_type)
+{
+ char *type_names;
+ int i;
+
+ if (!name)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(section_names); i++) {
+ if (strncmp(name, section_names[i].sec, section_names[i].len))
+ continue;
+ if (!section_names[i].is_attachable)
+ return -EINVAL;
+ *attach_type = section_names[i].attach_type;
+ return 0;
+ }
+ pr_warn("failed to guess attach type based on ELF section name '%s'\n", name);
+ type_names = libbpf_get_type_names(true);
+ if (type_names != NULL) {
+ pr_info("attachable section(type) names are:%s\n", type_names);
+ free(type_names);
+ }
+
+ return -EINVAL;
+}
+
+int bpf_map__fd(const struct bpf_map *map)
+{
+ return map ? map->fd : -EINVAL;
+}
+
+const struct bpf_map_def *bpf_map__def(const struct bpf_map *map)
+{
+ return map ? &map->def : ERR_PTR(-EINVAL);
+}
+
+const char *bpf_map__name(const struct bpf_map *map)
+{
+ return map ? map->name : NULL;
+}
+
+__u32 bpf_map__btf_key_type_id(const struct bpf_map *map)
+{
+ return map ? map->btf_key_type_id : 0;
+}
+
+__u32 bpf_map__btf_value_type_id(const struct bpf_map *map)
+{
+ return map ? map->btf_value_type_id : 0;
+}
+
+int bpf_map__set_priv(struct bpf_map *map, void *priv,
+ bpf_map_clear_priv_t clear_priv)
+{
+ if (!map)
+ return -EINVAL;
+
+ if (map->priv) {
+ if (map->clear_priv)
+ map->clear_priv(map, map->priv);
+ }
+
+ map->priv = priv;
+ map->clear_priv = clear_priv;
+ return 0;
+}
+
+void *bpf_map__priv(const struct bpf_map *map)
+{
+ return map ? map->priv : ERR_PTR(-EINVAL);
+}
+
+bool bpf_map__is_offload_neutral(const struct bpf_map *map)
+{
+ return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
+}
+
+bool bpf_map__is_internal(const struct bpf_map *map)
+{
+ return map->libbpf_type != LIBBPF_MAP_UNSPEC;
+}
+
+void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
+{
+ map->map_ifindex = ifindex;
+}
+
+int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
+{
+ if (!bpf_map_type__is_map_in_map(map->def.type)) {
+ pr_warn("error: unsupported map type\n");
+ return -EINVAL;
+ }
+ if (map->inner_map_fd != -1) {
+ pr_warn("error: inner_map_fd already specified\n");
+ return -EINVAL;
+ }
+ map->inner_map_fd = fd;
+ return 0;
+}
+
+static struct bpf_map *
+__bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
+{
+ ssize_t idx;
+ struct bpf_map *s, *e;
+
+ if (!obj || !obj->maps)
+ return NULL;
+
+ s = obj->maps;
+ e = obj->maps + obj->nr_maps;
+
+ if ((m < s) || (m >= e)) {
+ pr_warn("error in %s: map handler doesn't belong to object\n",
+ __func__);
+ return NULL;
+ }
+
+ idx = (m - obj->maps) + i;
+ if (idx >= obj->nr_maps || idx < 0)
+ return NULL;
+ return &obj->maps[idx];
+}
+
+struct bpf_map *
+bpf_map__next(const struct bpf_map *prev, const struct bpf_object *obj)
+{
+ if (prev == NULL)
+ return obj->maps;
+
+ return __bpf_map__iter(prev, obj, 1);
+}
+
+struct bpf_map *
+bpf_map__prev(const struct bpf_map *next, const struct bpf_object *obj)
+{
+ if (next == NULL) {
+ if (!obj->nr_maps)
+ return NULL;
+ return obj->maps + obj->nr_maps - 1;
+ }
+
+ return __bpf_map__iter(next, obj, -1);
+}
+
+struct bpf_map *
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name)
+{
+ struct bpf_map *pos;
+
+ bpf_object__for_each_map(pos, obj) {
+ if (pos->name && !strcmp(pos->name, name))
+ return pos;
+ }
+ return NULL;
+}
+
+int
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name)
+{
+ return bpf_map__fd(bpf_object__find_map_by_name(obj, name));
+}
+
+struct bpf_map *
+bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
+{
+ return ERR_PTR(-ENOTSUP);
+}
+
+long libbpf_get_error(const void *ptr)
+{
+ return PTR_ERR_OR_ZERO(ptr);
+}
+
+int bpf_prog_load(const char *file, enum bpf_prog_type type,
+ struct bpf_object **pobj, int *prog_fd)
+{
+ struct bpf_prog_load_attr attr;
+
+ memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
+ attr.file = file;
+ attr.prog_type = type;
+ attr.expected_attach_type = 0;
+
+ return bpf_prog_load_xattr(&attr, pobj, prog_fd);
+}
+
+int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
+ struct bpf_object **pobj, int *prog_fd)
+{
+ struct bpf_object_open_attr open_attr = {};
+ struct bpf_program *prog, *first_prog = NULL;
+ struct bpf_object *obj;
+ struct bpf_map *map;
+ int err;
+
+ if (!attr)
+ return -EINVAL;
+ if (!attr->file)
+ return -EINVAL;
+
+ open_attr.file = attr->file;
+ open_attr.prog_type = attr->prog_type;
+
+ obj = bpf_object__open_xattr(&open_attr);
+ if (IS_ERR_OR_NULL(obj))
+ return -ENOENT;
+
+ bpf_object__for_each_program(prog, obj) {
+ enum bpf_attach_type attach_type = attr->expected_attach_type;
+ /*
+ * to preserve backwards compatibility, bpf_prog_load treats
+ * attr->prog_type, if specified, as an override to whatever
+ * bpf_object__open guessed
+ */
+ if (attr->prog_type != BPF_PROG_TYPE_UNSPEC) {
+ bpf_program__set_type(prog, attr->prog_type);
+ bpf_program__set_expected_attach_type(prog,
+ attach_type);
+ }
+ if (bpf_program__get_type(prog) == BPF_PROG_TYPE_UNSPEC) {
+ /*
+ * we haven't guessed from section name and user
+ * didn't provide a fallback type, too bad...
+ */
+ bpf_object__close(obj);
+ return -EINVAL;
+ }
+
+ prog->prog_ifindex = attr->ifindex;
+ prog->log_level = attr->log_level;
+ prog->prog_flags = attr->prog_flags;
+ if (!first_prog)
+ first_prog = prog;
+ }
+
+ bpf_object__for_each_map(map, obj) {
+ if (!bpf_map__is_offload_neutral(map))
+ map->map_ifindex = attr->ifindex;
+ }
+
+ if (!first_prog) {
+ pr_warn("object file doesn't contain bpf program\n");
+ bpf_object__close(obj);
+ return -ENOENT;
+ }
+
+ err = bpf_object__load(obj);
+ if (err) {
+ bpf_object__close(obj);
+ return -EINVAL;
+ }
+
+ *pobj = obj;
+ *prog_fd = bpf_program__fd(first_prog);
+ return 0;
+}
+
+struct bpf_link {
+ int (*destroy)(struct bpf_link *link);
+};
+
+int bpf_link__destroy(struct bpf_link *link)
+{
+ int err;
+
+ if (!link)
+ return 0;
+
+ err = link->destroy(link);
+ free(link);
+
+ return err;
+}
+
+struct bpf_link_fd {
+ struct bpf_link link; /* has to be at the top of struct */
+ int fd; /* hook FD */
+};
+
+static int bpf_link__destroy_perf_event(struct bpf_link *link)
+{
+ struct bpf_link_fd *l = (void *)link;
+ int err;
+
+ err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0);
+ if (err)
+ err = -errno;
+
+ close(l->fd);
+ return err;
+}
+
+struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
+ int pfd)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link_fd *link;
+ int prog_fd, err;
+
+ if (pfd < 0) {
+ pr_warn("program '%s': invalid perf event FD %d\n",
+ bpf_program__title(prog, false), pfd);
+ return ERR_PTR(-EINVAL);
+ }
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ pr_warn("program '%s': can't attach BPF program w/o FD (did you load it?)\n",
+ bpf_program__title(prog, false));
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ link->link.destroy = &bpf_link__destroy_perf_event;
+ link->fd = pfd;
+
+ if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
+ err = -errno;
+ free(link);
+ pr_warn("program '%s': failed to attach to pfd %d: %s\n",
+ bpf_program__title(prog, false), pfd,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return ERR_PTR(err);
+ }
+ if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+ err = -errno;
+ free(link);
+ pr_warn("program '%s': failed to enable pfd %d: %s\n",
+ bpf_program__title(prog, false), pfd,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return ERR_PTR(err);
+ }
+ return (struct bpf_link *)link;
+}
+
+/*
+ * this function is expected to parse integer in the range of [0, 2^31-1] from
+ * given file using scanf format string fmt. If actual parsed value is
+ * negative, the result might be indistinguishable from error
+ */
+static int parse_uint_from_file(const char *file, const char *fmt)
+{
+ char buf[STRERR_BUFSIZE];
+ int err, ret;
+ FILE *f;
+
+ f = fopen(file, "r");
+ if (!f) {
+ err = -errno;
+ pr_debug("failed to open '%s': %s\n", file,
+ libbpf_strerror_r(err, buf, sizeof(buf)));
+ return err;
+ }
+ err = fscanf(f, fmt, &ret);
+ if (err != 1) {
+ err = err == EOF ? -EIO : -errno;
+ pr_debug("failed to parse '%s': %s\n", file,
+ libbpf_strerror_r(err, buf, sizeof(buf)));
+ fclose(f);
+ return err;
+ }
+ fclose(f);
+ return ret;
+}
+
+static int determine_kprobe_perf_type(void)
+{
+ const char *file = "/sys/bus/event_source/devices/kprobe/type";
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_uprobe_perf_type(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/type";
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_kprobe_retprobe_bit(void)
+{
+ const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";
+
+ return parse_uint_from_file(file, "config:%d\n");
+}
+
+static int determine_uprobe_retprobe_bit(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
+
+ return parse_uint_from_file(file, "config:%d\n");
+}
+
+static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
+ uint64_t offset, int pid)
+{
+ struct perf_event_attr attr = {};
+ char errmsg[STRERR_BUFSIZE];
+ int type, pfd, err;
+
+ type = uprobe ? determine_uprobe_perf_type()
+ : determine_kprobe_perf_type();
+ if (type < 0) {
+ pr_warn("failed to determine %s perf type: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
+ return type;
+ }
+ if (retprobe) {
+ int bit = uprobe ? determine_uprobe_retprobe_bit()
+ : determine_kprobe_retprobe_bit();
+
+ if (bit < 0) {
+ pr_warn("failed to determine %s retprobe bit: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(bit, errmsg, sizeof(errmsg)));
+ return bit;
+ }
+ attr.config |= 1 << bit;
+ }
+ attr.size = sizeof(attr);
+ attr.type = type;
+ attr.config1 = ptr_to_u64(name); /* kprobe_func or uprobe_path */
+ attr.config2 = offset; /* kprobe_addr or probe_offset */
+
+ /* pid filter is meaningful only for uprobes */
+ pfd = syscall(__NR_perf_event_open, &attr,
+ pid < 0 ? -1 : pid /* pid */,
+ pid == -1 ? 0 : -1 /* cpu */,
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (pfd < 0) {
+ err = -errno;
+ pr_warn("%s perf_event_open() failed: %s\n",
+ uprobe ? "uprobe" : "kprobe",
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ return pfd;
+}
+
+struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
+ bool retprobe,
+ const char *func_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
+ 0 /* offset */, -1 /* pid */);
+ if (pfd < 0) {
+ pr_warn("program '%s': failed to create %s '%s' perf event: %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "kretprobe" : "kprobe", func_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warn("program '%s': failed to attach to %s '%s': %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "kretprobe" : "kprobe", func_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
+ bool retprobe, pid_t pid,
+ const char *binary_path,
+ size_t func_offset)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_probe(true /* uprobe */, retprobe,
+ binary_path, func_offset, pid);
+ if (pfd < 0) {
+ pr_warn("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "uretprobe" : "uprobe",
+ binary_path, func_offset,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warn("program '%s': failed to attach to %s '%s:0x%zx': %s\n",
+ bpf_program__title(prog, false),
+ retprobe ? "uretprobe" : "uprobe",
+ binary_path, func_offset,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+static int determine_tracepoint_id(const char *tp_category,
+ const char *tp_name)
+{
+ char file[PATH_MAX];
+ int ret;
+
+ ret = snprintf(file, sizeof(file),
+ "/sys/kernel/debug/tracing/events/%s/%s/id",
+ tp_category, tp_name);
+ if (ret < 0)
+ return -errno;
+ if (ret >= sizeof(file)) {
+ pr_debug("tracepoint %s/%s path is too long\n",
+ tp_category, tp_name);
+ return -E2BIG;
+ }
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int perf_event_open_tracepoint(const char *tp_category,
+ const char *tp_name)
+{
+ struct perf_event_attr attr = {};
+ char errmsg[STRERR_BUFSIZE];
+ int tp_id, pfd, err;
+
+ tp_id = determine_tracepoint_id(tp_category, tp_name);
+ if (tp_id < 0) {
+ pr_warn("failed to determine tracepoint '%s/%s' perf event ID: %s\n",
+ tp_category, tp_name,
+ libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg)));
+ return tp_id;
+ }
+
+ attr.type = PERF_TYPE_TRACEPOINT;
+ attr.size = sizeof(attr);
+ attr.config = tp_id;
+
+ pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */,
+ -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
+ if (pfd < 0) {
+ err = -errno;
+ pr_warn("tracepoint '%s/%s' perf_event_open() failed: %s\n",
+ tp_category, tp_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return err;
+ }
+ return pfd;
+}
+
+struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
+ const char *tp_category,
+ const char *tp_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link *link;
+ int pfd, err;
+
+ pfd = perf_event_open_tracepoint(tp_category, tp_name);
+ if (pfd < 0) {
+ pr_warn("program '%s': failed to create tracepoint '%s/%s' perf event: %s\n",
+ bpf_program__title(prog, false),
+ tp_category, tp_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link = bpf_program__attach_perf_event(prog, pfd);
+ if (IS_ERR(link)) {
+ close(pfd);
+ err = PTR_ERR(link);
+ pr_warn("program '%s': failed to attach to tracepoint '%s/%s': %s\n",
+ bpf_program__title(prog, false),
+ tp_category, tp_name,
+ libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
+ return link;
+ }
+ return link;
+}
+
+static int bpf_link__destroy_fd(struct bpf_link *link)
+{
+ struct bpf_link_fd *l = (void *)link;
+
+ return close(l->fd);
+}
+
+struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
+ const char *tp_name)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link_fd *link;
+ int prog_fd, pfd;
+
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ pr_warn("program '%s': can't attach before loaded\n",
+ bpf_program__title(prog, false));
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ link->link.destroy = &bpf_link__destroy_fd;
+
+ pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
+ if (pfd < 0) {
+ pfd = -errno;
+ free(link);
+ pr_warn("program '%s': failed to attach to raw tracepoint '%s': %s\n",
+ bpf_program__title(prog, false), tp_name,
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link->fd = pfd;
+ return (struct bpf_link *)link;
+}
+
+struct bpf_link *bpf_program__attach_trace(struct bpf_program *prog)
+{
+ char errmsg[STRERR_BUFSIZE];
+ struct bpf_link_fd *link;
+ int prog_fd, pfd;
+
+ prog_fd = bpf_program__fd(prog);
+ if (prog_fd < 0) {
+ pr_warn("program '%s': can't attach before loaded\n",
+ bpf_program__title(prog, false));
+ return ERR_PTR(-EINVAL);
+ }
+
+ link = malloc(sizeof(*link));
+ if (!link)
+ return ERR_PTR(-ENOMEM);
+ link->link.destroy = &bpf_link__destroy_fd;
+
+ pfd = bpf_raw_tracepoint_open(NULL, prog_fd);
+ if (pfd < 0) {
+ pfd = -errno;
+ free(link);
+ pr_warn("program '%s': failed to attach to trace: %s\n",
+ bpf_program__title(prog, false),
+ libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
+ return ERR_PTR(pfd);
+ }
+ link->fd = pfd;
+ return (struct bpf_link *)link;
+}
+
+enum bpf_perf_event_ret
+bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
+ void **copy_mem, size_t *copy_size,
+ bpf_perf_event_print_t fn, void *private_data)
+{
+ struct perf_event_mmap_page *header = mmap_mem;
+ __u64 data_head = ring_buffer_read_head(header);
+ __u64 data_tail = header->data_tail;
+ void *base = ((__u8 *)header) + page_size;
+ int ret = LIBBPF_PERF_EVENT_CONT;
+ struct perf_event_header *ehdr;
+ size_t ehdr_size;
+
+ while (data_head != data_tail) {
+ ehdr = base + (data_tail & (mmap_size - 1));
+ ehdr_size = ehdr->size;
+
+ if (((void *)ehdr) + ehdr_size > base + mmap_size) {
+ void *copy_start = ehdr;
+ size_t len_first = base + mmap_size - copy_start;
+ size_t len_secnd = ehdr_size - len_first;
+
+ if (*copy_size < ehdr_size) {
+ free(*copy_mem);
+ *copy_mem = malloc(ehdr_size);
+ if (!*copy_mem) {
+ *copy_size = 0;
+ ret = LIBBPF_PERF_EVENT_ERROR;
+ break;
+ }
+ *copy_size = ehdr_size;
+ }
+
+ memcpy(*copy_mem, copy_start, len_first);
+ memcpy(*copy_mem + len_first, base, len_secnd);
+ ehdr = *copy_mem;
+ }
+
+ ret = fn(ehdr, private_data);
+ data_tail += ehdr_size;
+ if (ret != LIBBPF_PERF_EVENT_CONT)
+ break;
+ }
+
+ ring_buffer_write_tail(header, data_tail);
+ return ret;
+}
+
+struct perf_buffer;
+
+struct perf_buffer_params {
+ struct perf_event_attr *attr;
+ /* if event_cb is specified, it takes precendence */
+ perf_buffer_event_fn event_cb;
+ /* sample_cb and lost_cb are higher-level common-case callbacks */
+ perf_buffer_sample_fn sample_cb;
+ perf_buffer_lost_fn lost_cb;
+ void *ctx;
+ int cpu_cnt;
+ int *cpus;
+ int *map_keys;
+};
+
+struct perf_cpu_buf {
+ struct perf_buffer *pb;
+ void *base; /* mmap()'ed memory */
+ void *buf; /* for reconstructing segmented data */
+ size_t buf_size;
+ int fd;
+ int cpu;
+ int map_key;
+};
+
+struct perf_buffer {
+ perf_buffer_event_fn event_cb;
+ perf_buffer_sample_fn sample_cb;
+ perf_buffer_lost_fn lost_cb;
+ void *ctx; /* passed into callbacks */
+
+ size_t page_size;
+ size_t mmap_size;
+ struct perf_cpu_buf **cpu_bufs;
+ struct epoll_event *events;
+ int cpu_cnt;
+ int epoll_fd; /* perf event FD */
+ int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
+};
+
+static void perf_buffer__free_cpu_buf(struct perf_buffer *pb,
+ struct perf_cpu_buf *cpu_buf)
+{
+ if (!cpu_buf)
+ return;
+ if (cpu_buf->base &&
+ munmap(cpu_buf->base, pb->mmap_size + pb->page_size))
+ pr_warn("failed to munmap cpu_buf #%d\n", cpu_buf->cpu);
+ if (cpu_buf->fd >= 0) {
+ ioctl(cpu_buf->fd, PERF_EVENT_IOC_DISABLE, 0);
+ close(cpu_buf->fd);
+ }
+ free(cpu_buf->buf);
+ free(cpu_buf);
+}
+
+void perf_buffer__free(struct perf_buffer *pb)
+{
+ int i;
+
+ if (!pb)
+ return;
+ if (pb->cpu_bufs) {
+ for (i = 0; i < pb->cpu_cnt && pb->cpu_bufs[i]; i++) {
+ struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i];
+
+ bpf_map_delete_elem(pb->map_fd, &cpu_buf->map_key);
+ perf_buffer__free_cpu_buf(pb, cpu_buf);
+ }
+ free(pb->cpu_bufs);
+ }
+ if (pb->epoll_fd >= 0)
+ close(pb->epoll_fd);
+ free(pb->events);
+ free(pb);
+}
+
+static struct perf_cpu_buf *
+perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr,
+ int cpu, int map_key)
+{
+ struct perf_cpu_buf *cpu_buf;
+ char msg[STRERR_BUFSIZE];
+ int err;
+
+ cpu_buf = calloc(1, sizeof(*cpu_buf));
+ if (!cpu_buf)
+ return ERR_PTR(-ENOMEM);
+
+ cpu_buf->pb = pb;
+ cpu_buf->cpu = cpu;
+ cpu_buf->map_key = map_key;
+
+ cpu_buf->fd = syscall(__NR_perf_event_open, attr, -1 /* pid */, cpu,
+ -1, PERF_FLAG_FD_CLOEXEC);
+ if (cpu_buf->fd < 0) {
+ err = -errno;
+ pr_warn("failed to open perf buffer event on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ cpu_buf->base = mmap(NULL, pb->mmap_size + pb->page_size,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ cpu_buf->fd, 0);
+ if (cpu_buf->base == MAP_FAILED) {
+ cpu_buf->base = NULL;
+ err = -errno;
+ pr_warn("failed to mmap perf buffer on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+ err = -errno;
+ pr_warn("failed to enable perf buffer event on cpu #%d: %s\n",
+ cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ return cpu_buf;
+
+error:
+ perf_buffer__free_cpu_buf(pb, cpu_buf);
+ return (struct perf_cpu_buf *)ERR_PTR(err);
+}
+
+static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
+ struct perf_buffer_params *p);
+
+struct perf_buffer *perf_buffer__new(int map_fd, size_t page_cnt,
+ const struct perf_buffer_opts *opts)
+{
+ struct perf_buffer_params p = {};
+ struct perf_event_attr attr = { 0, };
+
+ attr.config = PERF_COUNT_SW_BPF_OUTPUT,
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.sample_type = PERF_SAMPLE_RAW;
+ attr.sample_period = 1;
+ attr.wakeup_events = 1;
+
+ p.attr = &attr;
+ p.sample_cb = opts ? opts->sample_cb : NULL;
+ p.lost_cb = opts ? opts->lost_cb : NULL;
+ p.ctx = opts ? opts->ctx : NULL;
+
+ return __perf_buffer__new(map_fd, page_cnt, &p);
+}
+
+struct perf_buffer *
+perf_buffer__new_raw(int map_fd, size_t page_cnt,
+ const struct perf_buffer_raw_opts *opts)
+{
+ struct perf_buffer_params p = {};
+
+ p.attr = opts->attr;
+ p.event_cb = opts->event_cb;
+ p.ctx = opts->ctx;
+ p.cpu_cnt = opts->cpu_cnt;
+ p.cpus = opts->cpus;
+ p.map_keys = opts->map_keys;
+
+ return __perf_buffer__new(map_fd, page_cnt, &p);
+}
+
+static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
+ struct perf_buffer_params *p)
+{
+ struct bpf_map_info map = {};
+ char msg[STRERR_BUFSIZE];
+ struct perf_buffer *pb;
+ __u32 map_info_len;
+ int err, i;
+
+ if (page_cnt & (page_cnt - 1)) {
+ pr_warn("page count should be power of two, but is %zu\n",
+ page_cnt);
+ return ERR_PTR(-EINVAL);
+ }
+
+ map_info_len = sizeof(map);
+ err = bpf_obj_get_info_by_fd(map_fd, &map, &map_info_len);
+ if (err) {
+ err = -errno;
+ pr_warn("failed to get map info for map FD %d: %s\n",
+ map_fd, libbpf_strerror_r(err, msg, sizeof(msg)));
+ return ERR_PTR(err);
+ }
+
+ if (map.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
+ pr_warn("map '%s' should be BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
+ map.name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ pb = calloc(1, sizeof(*pb));
+ if (!pb)
+ return ERR_PTR(-ENOMEM);
+
+ pb->event_cb = p->event_cb;
+ pb->sample_cb = p->sample_cb;
+ pb->lost_cb = p->lost_cb;
+ pb->ctx = p->ctx;
+
+ pb->page_size = getpagesize();
+ pb->mmap_size = pb->page_size * page_cnt;
+ pb->map_fd = map_fd;
+
+ pb->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (pb->epoll_fd < 0) {
+ err = -errno;
+ pr_warn("failed to create epoll instance: %s\n",
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ if (p->cpu_cnt > 0) {
+ pb->cpu_cnt = p->cpu_cnt;
+ } else {
+ pb->cpu_cnt = libbpf_num_possible_cpus();
+ if (pb->cpu_cnt < 0) {
+ err = pb->cpu_cnt;
+ goto error;
+ }
+ if (map.max_entries < pb->cpu_cnt)
+ pb->cpu_cnt = map.max_entries;
+ }
+
+ pb->events = calloc(pb->cpu_cnt, sizeof(*pb->events));
+ if (!pb->events) {
+ err = -ENOMEM;
+ pr_warn("failed to allocate events: out of memory\n");
+ goto error;
+ }
+ pb->cpu_bufs = calloc(pb->cpu_cnt, sizeof(*pb->cpu_bufs));
+ if (!pb->cpu_bufs) {
+ err = -ENOMEM;
+ pr_warn("failed to allocate buffers: out of memory\n");
+ goto error;
+ }
+
+ for (i = 0; i < pb->cpu_cnt; i++) {
+ struct perf_cpu_buf *cpu_buf;
+ int cpu, map_key;
+
+ cpu = p->cpu_cnt > 0 ? p->cpus[i] : i;
+ map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i;
+
+ cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key);
+ if (IS_ERR(cpu_buf)) {
+ err = PTR_ERR(cpu_buf);
+ goto error;
+ }
+
+ pb->cpu_bufs[i] = cpu_buf;
+
+ err = bpf_map_update_elem(pb->map_fd, &map_key,
+ &cpu_buf->fd, 0);
+ if (err) {
+ err = -errno;
+ pr_warn("failed to set cpu #%d, key %d -> perf FD %d: %s\n",
+ cpu, map_key, cpu_buf->fd,
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+
+ pb->events[i].events = EPOLLIN;
+ pb->events[i].data.ptr = cpu_buf;
+ if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd,
+ &pb->events[i]) < 0) {
+ err = -errno;
+ pr_warn("failed to epoll_ctl cpu #%d perf FD %d: %s\n",
+ cpu, cpu_buf->fd,
+ libbpf_strerror_r(err, msg, sizeof(msg)));
+ goto error;
+ }
+ }
+
+ return pb;
+
+error:
+ if (pb)
+ perf_buffer__free(pb);
+ return ERR_PTR(err);
+}
+
+struct perf_sample_raw {
+ struct perf_event_header header;
+ uint32_t size;
+ char data[0];
+};
+
+struct perf_sample_lost {
+ struct perf_event_header header;
+ uint64_t id;
+ uint64_t lost;
+ uint64_t sample_id;
+};
+
+static enum bpf_perf_event_ret
+perf_buffer__process_record(struct perf_event_header *e, void *ctx)
+{
+ struct perf_cpu_buf *cpu_buf = ctx;
+ struct perf_buffer *pb = cpu_buf->pb;
+ void *data = e;
+
+ /* user wants full control over parsing perf event */
+ if (pb->event_cb)
+ return pb->event_cb(pb->ctx, cpu_buf->cpu, e);
+
+ switch (e->type) {
+ case PERF_RECORD_SAMPLE: {
+ struct perf_sample_raw *s = data;
+
+ if (pb->sample_cb)
+ pb->sample_cb(pb->ctx, cpu_buf->cpu, s->data, s->size);
+ break;
+ }
+ case PERF_RECORD_LOST: {
+ struct perf_sample_lost *s = data;
+
+ if (pb->lost_cb)
+ pb->lost_cb(pb->ctx, cpu_buf->cpu, s->lost);
+ break;
+ }
+ default:
+ pr_warn("unknown perf sample type %d\n", e->type);
+ return LIBBPF_PERF_EVENT_ERROR;
+ }
+ return LIBBPF_PERF_EVENT_CONT;
+}
+
+static int perf_buffer__process_records(struct perf_buffer *pb,
+ struct perf_cpu_buf *cpu_buf)
+{
+ enum bpf_perf_event_ret ret;
+
+ ret = bpf_perf_event_read_simple(cpu_buf->base, pb->mmap_size,
+ pb->page_size, &cpu_buf->buf,
+ &cpu_buf->buf_size,
+ perf_buffer__process_record, cpu_buf);
+ if (ret != LIBBPF_PERF_EVENT_CONT)
+ return ret;
+ return 0;
+}
+
+int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms)
+{
+ int i, cnt, err;
+
+ cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, timeout_ms);
+ for (i = 0; i < cnt; i++) {
+ struct perf_cpu_buf *cpu_buf = pb->events[i].data.ptr;
+
+ err = perf_buffer__process_records(pb, cpu_buf);
+ if (err) {
+ pr_warn("error while processing records: %d\n", err);
+ return err;
+ }
+ }
+ return cnt < 0 ? -errno : cnt;
+}
+
+struct bpf_prog_info_array_desc {
+ int array_offset; /* e.g. offset of jited_prog_insns */
+ int count_offset; /* e.g. offset of jited_prog_len */
+ int size_offset; /* > 0: offset of rec size,
+ * < 0: fix size of -size_offset
+ */
+};
+
+static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = {
+ [BPF_PROG_INFO_JITED_INSNS] = {
+ offsetof(struct bpf_prog_info, jited_prog_insns),
+ offsetof(struct bpf_prog_info, jited_prog_len),
+ -1,
+ },
+ [BPF_PROG_INFO_XLATED_INSNS] = {
+ offsetof(struct bpf_prog_info, xlated_prog_insns),
+ offsetof(struct bpf_prog_info, xlated_prog_len),
+ -1,
+ },
+ [BPF_PROG_INFO_MAP_IDS] = {
+ offsetof(struct bpf_prog_info, map_ids),
+ offsetof(struct bpf_prog_info, nr_map_ids),
+ -(int)sizeof(__u32),
+ },
+ [BPF_PROG_INFO_JITED_KSYMS] = {
+ offsetof(struct bpf_prog_info, jited_ksyms),
+ offsetof(struct bpf_prog_info, nr_jited_ksyms),
+ -(int)sizeof(__u64),
+ },
+ [BPF_PROG_INFO_JITED_FUNC_LENS] = {
+ offsetof(struct bpf_prog_info, jited_func_lens),
+ offsetof(struct bpf_prog_info, nr_jited_func_lens),
+ -(int)sizeof(__u32),
+ },
+ [BPF_PROG_INFO_FUNC_INFO] = {
+ offsetof(struct bpf_prog_info, func_info),
+ offsetof(struct bpf_prog_info, nr_func_info),
+ offsetof(struct bpf_prog_info, func_info_rec_size),
+ },
+ [BPF_PROG_INFO_LINE_INFO] = {
+ offsetof(struct bpf_prog_info, line_info),
+ offsetof(struct bpf_prog_info, nr_line_info),
+ offsetof(struct bpf_prog_info, line_info_rec_size),
+ },
+ [BPF_PROG_INFO_JITED_LINE_INFO] = {
+ offsetof(struct bpf_prog_info, jited_line_info),
+ offsetof(struct bpf_prog_info, nr_jited_line_info),
+ offsetof(struct bpf_prog_info, jited_line_info_rec_size),
+ },
+ [BPF_PROG_INFO_PROG_TAGS] = {
+ offsetof(struct bpf_prog_info, prog_tags),
+ offsetof(struct bpf_prog_info, nr_prog_tags),
+ -(int)sizeof(__u8) * BPF_TAG_SIZE,
+ },
+
+};
+
+static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info,
+ int offset)
+{
+ __u32 *array = (__u32 *)info;
+
+ if (offset >= 0)
+ return array[offset / sizeof(__u32)];
+ return -(int)offset;
+}
+
+static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info,
+ int offset)
+{
+ __u64 *array = (__u64 *)info;
+
+ if (offset >= 0)
+ return array[offset / sizeof(__u64)];
+ return -(int)offset;
+}
+
+static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
+ __u32 val)
+{
+ __u32 *array = (__u32 *)info;
+
+ if (offset >= 0)
+ array[offset / sizeof(__u32)] = val;
+}
+
+static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
+ __u64 val)
+{
+ __u64 *array = (__u64 *)info;
+
+ if (offset >= 0)
+ array[offset / sizeof(__u64)] = val;
+}
+
+struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays)
+{
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info info = {};
+ __u32 info_len = sizeof(info);
+ __u32 data_len = 0;
+ int i, err;
+ void *ptr;
+
+ if (arrays >> BPF_PROG_INFO_LAST_ARRAY)
+ return ERR_PTR(-EINVAL);
+
+ /* step 1: get array dimensions */
+ err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
+ if (err) {
+ pr_debug("can't get prog info: %s", strerror(errno));
+ return ERR_PTR(-EFAULT);
+ }
+
+ /* step 2: calculate total size of all arrays */
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ bool include_array = (arrays & (1UL << i)) > 0;
+ struct bpf_prog_info_array_desc *desc;
+ __u32 count, size;
+
+ desc = bpf_prog_info_array_desc + i;
+
+ /* kernel is too old to support this field */
+ if (info_len < desc->array_offset + sizeof(__u32) ||
+ info_len < desc->count_offset + sizeof(__u32) ||
+ (desc->size_offset > 0 && info_len < desc->size_offset))
+ include_array = false;
+
+ if (!include_array) {
+ arrays &= ~(1UL << i); /* clear the bit */
+ continue;
+ }
+
+ count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+
+ data_len += count * size;
+ }
+
+ /* step 3: allocate continuous memory */
+ data_len = roundup(data_len, sizeof(__u64));
+ info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len);
+ if (!info_linear)
+ return ERR_PTR(-ENOMEM);
+
+ /* step 4: fill data to info_linear->info */
+ info_linear->arrays = arrays;
+ memset(&info_linear->info, 0, sizeof(info));
+ ptr = info_linear->data;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u32 count, size;
+
+ if ((arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+ bpf_prog_info_set_offset_u32(&info_linear->info,
+ desc->count_offset, count);
+ bpf_prog_info_set_offset_u32(&info_linear->info,
+ desc->size_offset, size);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset,
+ ptr_to_u64(ptr));
+ ptr += count * size;
+ }
+
+ /* step 5: call syscall again to get required arrays */
+ err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
+ if (err) {
+ pr_debug("can't get prog info: %s", strerror(errno));
+ free(info_linear);
+ return ERR_PTR(-EFAULT);
+ }
+
+ /* step 6: verify the data */
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u32 v1, v2;
+
+ if ((arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+ desc->count_offset);
+ if (v1 != v2)
+ pr_warn("%s: mismatch in element count\n", __func__);
+
+ v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+ v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+ desc->size_offset);
+ if (v1 != v2)
+ pr_warn("%s: mismatch in rec size\n", __func__);
+ }
+
+ /* step 7: update info_len and data_len */
+ info_linear->info_len = sizeof(struct bpf_prog_info);
+ info_linear->data_len = data_len;
+
+ return info_linear;
+}
+
+void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear)
+{
+ int i;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u64 addr, offs;
+
+ if ((info_linear->arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ addr = bpf_prog_info_read_offset_u64(&info_linear->info,
+ desc->array_offset);
+ offs = addr - ptr_to_u64(info_linear->data);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset, offs);
+ }
+}
+
+void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
+{
+ int i;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u64 addr, offs;
+
+ if ((info_linear->arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ offs = bpf_prog_info_read_offset_u64(&info_linear->info,
+ desc->array_offset);
+ addr = offs + ptr_to_u64(info_linear->data);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset, addr);
+ }
+}
+
+int libbpf_num_possible_cpus(void)
+{
+ static const char *fcpu = "/sys/devices/system/cpu/possible";
+ int len = 0, n = 0, il = 0, ir = 0;
+ unsigned int start = 0, end = 0;
+ int tmp_cpus = 0;
+ static int cpus;
+ char buf[128];
+ int error = 0;
+ int fd = -1;
+
+ tmp_cpus = READ_ONCE(cpus);
+ if (tmp_cpus > 0)
+ return tmp_cpus;
+
+ fd = open(fcpu, O_RDONLY);
+ if (fd < 0) {
+ error = errno;
+ pr_warn("Failed to open file %s: %s\n", fcpu, strerror(error));
+ return -error;
+ }
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (len <= 0) {
+ error = len ? errno : EINVAL;
+ pr_warn("Failed to read # of possible cpus from %s: %s\n",
+ fcpu, strerror(error));
+ return -error;
+ }
+ if (len == sizeof(buf)) {
+ pr_warn("File %s size overflow\n", fcpu);
+ return -EOVERFLOW;
+ }
+ buf[len] = '\0';
+
+ for (ir = 0, tmp_cpus = 0; ir <= len; ir++) {
+ /* Each sub string separated by ',' has format \d+-\d+ or \d+ */
+ if (buf[ir] == ',' || buf[ir] == '\0') {
+ buf[ir] = '\0';
+ n = sscanf(&buf[il], "%u-%u", &start, &end);
+ if (n <= 0) {
+ pr_warn("Failed to get # CPUs from %s\n",
+ &buf[il]);
+ return -EINVAL;
+ } else if (n == 1) {
+ end = start;
+ }
+ tmp_cpus += end - start + 1;
+ il = ir + 1;
+ }
+ }
+ if (tmp_cpus <= 0) {
+ pr_warn("Invalid #CPUs %d from %s\n", tmp_cpus, fcpu);
+ return -EINVAL;
+ }
+
+ WRITE_ONCE(cpus, tmp_cpus);
+ return tmp_cpus;
+}
diff --git a/src/contrib/libbpf/bpf/libbpf.h b/src/contrib/libbpf/bpf/libbpf.h
new file mode 100644
index 0000000..0dbf4bf
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf.h
@@ -0,0 +1,637 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * Common eBPF ELF object loading operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __LIBBPF_LIBBPF_H
+#define __LIBBPF_LIBBPF_H
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h> // for size_t
+#include <linux/bpf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LIBBPF_API
+#define LIBBPF_API __attribute__((visibility("default")))
+#endif
+
+enum libbpf_errno {
+ __LIBBPF_ERRNO__START = 4000,
+
+ /* Something wrong in libelf */
+ LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
+ LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */
+ LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */
+ LIBBPF_ERRNO__ENDIAN, /* Endian mismatch */
+ LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */
+ LIBBPF_ERRNO__RELOC, /* Relocation failed */
+ LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */
+ LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
+ LIBBPF_ERRNO__PROG2BIG, /* Program too big */
+ LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
+ LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */
+ LIBBPF_ERRNO__WRNGPID, /* Wrong pid in netlink message */
+ LIBBPF_ERRNO__INVSEQ, /* Invalid netlink sequence */
+ LIBBPF_ERRNO__NLPARSE, /* netlink parsing error */
+ __LIBBPF_ERRNO__END,
+};
+
+LIBBPF_API int libbpf_strerror(int err, char *buf, size_t size);
+
+enum libbpf_print_level {
+ LIBBPF_WARN,
+ LIBBPF_INFO,
+ LIBBPF_DEBUG,
+};
+
+typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level,
+ const char *, va_list ap);
+
+LIBBPF_API libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn);
+
+/* Hide internal to user */
+struct bpf_object;
+
+struct bpf_object_open_attr {
+ const char *file;
+ enum bpf_prog_type prog_type;
+};
+
+/* Helper macro to declare and initialize libbpf options struct
+ *
+ * This dance with uninitialized declaration, followed by memset to zero,
+ * followed by assignment using compound literal syntax is done to preserve
+ * ability to use a nice struct field initialization syntax and **hopefully**
+ * have all the padding bytes initialized to zero. It's not guaranteed though,
+ * when copying literal, that compiler won't copy garbage in literal's padding
+ * bytes, but that's the best way I've found and it seems to work in practice.
+ *
+ * Macro declares opts struct of given type and name, zero-initializes,
+ * including any extra padding, it with memset() and then assigns initial
+ * values provided by users in struct initializer-syntax as varargs.
+ */
+#define DECLARE_LIBBPF_OPTS(TYPE, NAME, ...) \
+ struct TYPE NAME = ({ \
+ memset(&NAME, 0, sizeof(struct TYPE)); \
+ (struct TYPE) { \
+ .sz = sizeof(struct TYPE), \
+ __VA_ARGS__ \
+ }; \
+ })
+
+struct bpf_object_open_opts {
+ /* size of this struct, for forward/backward compatiblity */
+ size_t sz;
+ /* object name override, if provided:
+ * - for object open from file, this will override setting object
+ * name from file path's base name;
+ * - for object open from memory buffer, this will specify an object
+ * name and will override default "<addr>-<buf-size>" name;
+ */
+ const char *object_name;
+ /* parse map definitions non-strictly, allowing extra attributes/data */
+ bool relaxed_maps;
+ /* process CO-RE relocations non-strictly, allowing them to fail */
+ bool relaxed_core_relocs;
+ /* maps that set the 'pinning' attribute in their definition will have
+ * their pin_path attribute set to a file in this directory, and be
+ * auto-pinned to that path on load; defaults to "/sys/fs/bpf".
+ */
+ const char *pin_root_path;
+ __u32 attach_prog_fd;
+};
+#define bpf_object_open_opts__last_field attach_prog_fd
+
+LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
+LIBBPF_API struct bpf_object *
+bpf_object__open_file(const char *path, struct bpf_object_open_opts *opts);
+LIBBPF_API struct bpf_object *
+bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
+ struct bpf_object_open_opts *opts);
+
+/* deprecated bpf_object__open variants */
+LIBBPF_API struct bpf_object *
+bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
+ const char *name);
+LIBBPF_API struct bpf_object *
+bpf_object__open_xattr(struct bpf_object_open_attr *attr);
+
+int bpf_object__section_size(const struct bpf_object *obj, const char *name,
+ __u32 *size);
+int bpf_object__variable_offset(const struct bpf_object *obj, const char *name,
+ __u32 *off);
+
+enum libbpf_pin_type {
+ LIBBPF_PIN_NONE,
+ /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
+ LIBBPF_PIN_BY_NAME,
+};
+
+/* pin_maps and unpin_maps can both be called with a NULL path, in which case
+ * they will use the pin_path attribute of each map (and ignore all maps that
+ * don't have a pin_path set).
+ */
+LIBBPF_API int bpf_object__pin_maps(struct bpf_object *obj, const char *path);
+LIBBPF_API int bpf_object__unpin_maps(struct bpf_object *obj,
+ const char *path);
+LIBBPF_API int bpf_object__pin_programs(struct bpf_object *obj,
+ const char *path);
+LIBBPF_API int bpf_object__unpin_programs(struct bpf_object *obj,
+ const char *path);
+LIBBPF_API int bpf_object__pin(struct bpf_object *object, const char *path);
+LIBBPF_API void bpf_object__close(struct bpf_object *object);
+
+struct bpf_object_load_attr {
+ struct bpf_object *obj;
+ int log_level;
+ const char *target_btf_path;
+};
+
+/* Load/unload object into/from kernel */
+LIBBPF_API int bpf_object__load(struct bpf_object *obj);
+LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
+LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
+LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
+LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
+
+struct btf;
+LIBBPF_API struct btf *bpf_object__btf(const struct bpf_object *obj);
+LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
+
+LIBBPF_API struct bpf_program *
+bpf_object__find_program_by_title(const struct bpf_object *obj,
+ const char *title);
+
+LIBBPF_API struct bpf_object *bpf_object__next(struct bpf_object *prev);
+#define bpf_object__for_each_safe(pos, tmp) \
+ for ((pos) = bpf_object__next(NULL), \
+ (tmp) = bpf_object__next(pos); \
+ (pos) != NULL; \
+ (pos) = (tmp), (tmp) = bpf_object__next(tmp))
+
+typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
+LIBBPF_API int bpf_object__set_priv(struct bpf_object *obj, void *priv,
+ bpf_object_clear_priv_t clear_priv);
+LIBBPF_API void *bpf_object__priv(const struct bpf_object *prog);
+
+LIBBPF_API int
+libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
+ enum bpf_attach_type *expected_attach_type);
+LIBBPF_API int libbpf_attach_type_by_name(const char *name,
+ enum bpf_attach_type *attach_type);
+LIBBPF_API int libbpf_find_vmlinux_btf_id(const char *name,
+ enum bpf_attach_type attach_type);
+
+/* Accessors of bpf_program */
+struct bpf_program;
+LIBBPF_API struct bpf_program *bpf_program__next(struct bpf_program *prog,
+ const struct bpf_object *obj);
+
+#define bpf_object__for_each_program(pos, obj) \
+ for ((pos) = bpf_program__next(NULL, (obj)); \
+ (pos) != NULL; \
+ (pos) = bpf_program__next((pos), (obj)))
+
+LIBBPF_API struct bpf_program *bpf_program__prev(struct bpf_program *prog,
+ const struct bpf_object *obj);
+
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, void *);
+
+LIBBPF_API int bpf_program__set_priv(struct bpf_program *prog, void *priv,
+ bpf_program_clear_priv_t clear_priv);
+
+LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
+LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
+ __u32 ifindex);
+
+LIBBPF_API const char *bpf_program__title(const struct bpf_program *prog,
+ bool needs_copy);
+
+/* returns program size in bytes */
+LIBBPF_API size_t bpf_program__size(const struct bpf_program *prog);
+
+LIBBPF_API int bpf_program__load(struct bpf_program *prog, char *license,
+ __u32 kern_version);
+LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
+LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
+ const char *path,
+ int instance);
+LIBBPF_API int bpf_program__unpin_instance(struct bpf_program *prog,
+ const char *path,
+ int instance);
+LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
+LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
+LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
+
+struct bpf_link;
+
+LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
+
+LIBBPF_API struct bpf_link *
+bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
+ const char *func_name);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
+ pid_t pid, const char *binary_path,
+ size_t func_offset);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_tracepoint(struct bpf_program *prog,
+ const char *tp_category,
+ const char *tp_name);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
+ const char *tp_name);
+
+LIBBPF_API struct bpf_link *
+bpf_program__attach_trace(struct bpf_program *prog);
+struct bpf_insn;
+
+/*
+ * Libbpf allows callers to adjust BPF programs before being loaded
+ * into kernel. One program in an object file can be transformed into
+ * multiple variants to be attached to different hooks.
+ *
+ * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
+ * form an API for this purpose.
+ *
+ * - bpf_program_prep_t:
+ * Defines a 'preprocessor', which is a caller defined function
+ * passed to libbpf through bpf_program__set_prep(), and will be
+ * called before program is loaded. The processor should adjust
+ * the program one time for each instance according to the instance id
+ * passed to it.
+ *
+ * - bpf_program__set_prep:
+ * Attaches a preprocessor to a BPF program. The number of instances
+ * that should be created is also passed through this function.
+ *
+ * - bpf_program__nth_fd:
+ * After the program is loaded, get resulting FD of a given instance
+ * of the BPF program.
+ *
+ * If bpf_program__set_prep() is not used, the program would be loaded
+ * without adjustment during bpf_object__load(). The program has only
+ * one instance. In this case bpf_program__fd(prog) is equal to
+ * bpf_program__nth_fd(prog, 0).
+ */
+
+struct bpf_prog_prep_result {
+ /*
+ * If not NULL, load new instruction array.
+ * If set to NULL, don't load this instance.
+ */
+ struct bpf_insn *new_insn_ptr;
+ int new_insn_cnt;
+
+ /* If not NULL, result FD is written to it. */
+ int *pfd;
+};
+
+/*
+ * Parameters of bpf_program_prep_t:
+ * - prog: The bpf_program being loaded.
+ * - n: Index of instance being generated.
+ * - insns: BPF instructions array.
+ * - insns_cnt:Number of instructions in insns.
+ * - res: Output parameter, result of transformation.
+ *
+ * Return value:
+ * - Zero: pre-processing success.
+ * - Non-zero: pre-processing error, stop loading.
+ */
+typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
+ struct bpf_insn *insns, int insns_cnt,
+ struct bpf_prog_prep_result *res);
+
+LIBBPF_API int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
+ bpf_program_prep_t prep);
+
+LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n);
+
+/*
+ * Adjust type of BPF program. Default is kprobe.
+ */
+LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog);
+LIBBPF_API int bpf_program__set_tracing(struct bpf_program *prog);
+
+LIBBPF_API enum bpf_prog_type bpf_program__get_type(struct bpf_program *prog);
+LIBBPF_API void bpf_program__set_type(struct bpf_program *prog,
+ enum bpf_prog_type type);
+
+LIBBPF_API enum bpf_attach_type
+bpf_program__get_expected_attach_type(struct bpf_program *prog);
+LIBBPF_API void
+bpf_program__set_expected_attach_type(struct bpf_program *prog,
+ enum bpf_attach_type type);
+
+LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
+LIBBPF_API bool bpf_program__is_tracing(const struct bpf_program *prog);
+
+/*
+ * No need for __attribute__((packed)), all members of 'bpf_map_def'
+ * are all aligned. In addition, using __attribute__((packed))
+ * would trigger a -Wpacked warning message, and lead to an error
+ * if -Werror is set.
+ */
+struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+ unsigned int map_flags;
+};
+
+/*
+ * The 'struct bpf_map' in include/linux/bpf.h is internal to the kernel,
+ * so no need to worry about a name clash.
+ */
+struct bpf_map;
+LIBBPF_API struct bpf_map *
+bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name);
+
+LIBBPF_API int
+bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name);
+
+/*
+ * Get bpf_map through the offset of corresponding struct bpf_map_def
+ * in the BPF object file.
+ */
+LIBBPF_API struct bpf_map *
+bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
+
+LIBBPF_API struct bpf_map *
+bpf_map__next(const struct bpf_map *map, const struct bpf_object *obj);
+#define bpf_object__for_each_map(pos, obj) \
+ for ((pos) = bpf_map__next(NULL, (obj)); \
+ (pos) != NULL; \
+ (pos) = bpf_map__next((pos), (obj)))
+#define bpf_map__for_each bpf_object__for_each_map
+
+LIBBPF_API struct bpf_map *
+bpf_map__prev(const struct bpf_map *map, const struct bpf_object *obj);
+
+LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
+LIBBPF_API const struct bpf_map_def *bpf_map__def(const struct bpf_map *map);
+LIBBPF_API const char *bpf_map__name(const struct bpf_map *map);
+LIBBPF_API __u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
+LIBBPF_API __u32 bpf_map__btf_value_type_id(const struct bpf_map *map);
+
+typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
+LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
+ bpf_map_clear_priv_t clear_priv);
+LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
+LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
+LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries);
+LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
+LIBBPF_API void bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
+LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path);
+LIBBPF_API const char *bpf_map__get_pin_path(const struct bpf_map *map);
+LIBBPF_API bool bpf_map__is_pinned(const struct bpf_map *map);
+LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
+LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
+
+LIBBPF_API int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd);
+
+LIBBPF_API long libbpf_get_error(const void *ptr);
+
+struct bpf_prog_load_attr {
+ const char *file;
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type expected_attach_type;
+ int ifindex;
+ int log_level;
+ int prog_flags;
+};
+
+LIBBPF_API int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
+ struct bpf_object **pobj, int *prog_fd);
+LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type,
+ struct bpf_object **pobj, int *prog_fd);
+
+struct xdp_link_info {
+ __u32 prog_id;
+ __u32 drv_prog_id;
+ __u32 hw_prog_id;
+ __u32 skb_prog_id;
+ __u8 attach_mode;
+};
+
+LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
+LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
+LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
+ size_t info_size, __u32 flags);
+
+struct perf_buffer;
+
+typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu,
+ void *data, __u32 size);
+typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);
+
+/* common use perf buffer options */
+struct perf_buffer_opts {
+ /* if specified, sample_cb is called for each sample */
+ perf_buffer_sample_fn sample_cb;
+ /* if specified, lost_cb is called for each batch of lost samples */
+ perf_buffer_lost_fn lost_cb;
+ /* ctx is provided to sample_cb and lost_cb */
+ void *ctx;
+};
+
+LIBBPF_API struct perf_buffer *
+perf_buffer__new(int map_fd, size_t page_cnt,
+ const struct perf_buffer_opts *opts);
+
+enum bpf_perf_event_ret {
+ LIBBPF_PERF_EVENT_DONE = 0,
+ LIBBPF_PERF_EVENT_ERROR = -1,
+ LIBBPF_PERF_EVENT_CONT = -2,
+};
+
+struct perf_event_header;
+
+typedef enum bpf_perf_event_ret
+(*perf_buffer_event_fn)(void *ctx, int cpu, struct perf_event_header *event);
+
+/* raw perf buffer options, giving most power and control */
+struct perf_buffer_raw_opts {
+ /* perf event attrs passed directly into perf_event_open() */
+ struct perf_event_attr *attr;
+ /* raw event callback */
+ perf_buffer_event_fn event_cb;
+ /* ctx is provided to event_cb */
+ void *ctx;
+ /* if cpu_cnt == 0, open all on all possible CPUs (up to the number of
+ * max_entries of given PERF_EVENT_ARRAY map)
+ */
+ int cpu_cnt;
+ /* if cpu_cnt > 0, cpus is an array of CPUs to open ring buffers on */
+ int *cpus;
+ /* if cpu_cnt > 0, map_keys specify map keys to set per-CPU FDs for */
+ int *map_keys;
+};
+
+LIBBPF_API struct perf_buffer *
+perf_buffer__new_raw(int map_fd, size_t page_cnt,
+ const struct perf_buffer_raw_opts *opts);
+
+LIBBPF_API void perf_buffer__free(struct perf_buffer *pb);
+LIBBPF_API int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms);
+
+typedef enum bpf_perf_event_ret
+ (*bpf_perf_event_print_t)(struct perf_event_header *hdr,
+ void *private_data);
+LIBBPF_API enum bpf_perf_event_ret
+bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
+ void **copy_mem, size_t *copy_size,
+ bpf_perf_event_print_t fn, void *private_data);
+
+struct nlattr;
+typedef int (*libbpf_dump_nlmsg_t)(void *cookie, void *msg, struct nlattr **tb);
+int libbpf_netlink_open(unsigned int *nl_pid);
+int libbpf_nl_get_link(int sock, unsigned int nl_pid,
+ libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie);
+int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
+ libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie);
+int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
+ libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie);
+int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
+ libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie);
+
+struct bpf_prog_linfo;
+struct bpf_prog_info;
+
+LIBBPF_API void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo);
+LIBBPF_API struct bpf_prog_linfo *
+bpf_prog_linfo__new(const struct bpf_prog_info *info);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
+ __u64 addr, __u32 func_idx, __u32 nr_skip);
+LIBBPF_API const struct bpf_line_info *
+bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
+ __u32 insn_off, __u32 nr_skip);
+
+/*
+ * Probe for supported system features
+ *
+ * Note that running many of these probes in a short amount of time can cause
+ * the kernel to reach the maximal size of lockable memory allowed for the
+ * user, causing subsequent probes to fail. In this case, the caller may want
+ * to adjust that limit with setrlimit().
+ */
+LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type,
+ __u32 ifindex);
+LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex);
+LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id,
+ enum bpf_prog_type prog_type, __u32 ifindex);
+
+/*
+ * Get bpf_prog_info in continuous memory
+ *
+ * struct bpf_prog_info has multiple arrays. The user has option to choose
+ * arrays to fetch from kernel. The following APIs provide an uniform way to
+ * fetch these data. All arrays in bpf_prog_info are stored in a single
+ * continuous memory region. This makes it easy to store the info in a
+ * file.
+ *
+ * Before writing bpf_prog_info_linear to files, it is necessary to
+ * translate pointers in bpf_prog_info to offsets. Helper functions
+ * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr()
+ * are introduced to switch between pointers and offsets.
+ *
+ * Examples:
+ * # To fetch map_ids and prog_tags:
+ * __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) |
+ * (1UL << BPF_PROG_INFO_PROG_TAGS);
+ * struct bpf_prog_info_linear *info_linear =
+ * bpf_program__get_prog_info_linear(fd, arrays);
+ *
+ * # To save data in file
+ * bpf_program__bpil_addr_to_offs(info_linear);
+ * write(f, info_linear, sizeof(*info_linear) + info_linear->data_len);
+ *
+ * # To read data from file
+ * read(f, info_linear, <proper_size>);
+ * bpf_program__bpil_offs_to_addr(info_linear);
+ */
+enum bpf_prog_info_array {
+ BPF_PROG_INFO_FIRST_ARRAY = 0,
+ BPF_PROG_INFO_JITED_INSNS = 0,
+ BPF_PROG_INFO_XLATED_INSNS,
+ BPF_PROG_INFO_MAP_IDS,
+ BPF_PROG_INFO_JITED_KSYMS,
+ BPF_PROG_INFO_JITED_FUNC_LENS,
+ BPF_PROG_INFO_FUNC_INFO,
+ BPF_PROG_INFO_LINE_INFO,
+ BPF_PROG_INFO_JITED_LINE_INFO,
+ BPF_PROG_INFO_PROG_TAGS,
+ BPF_PROG_INFO_LAST_ARRAY,
+};
+
+struct bpf_prog_info_linear {
+ /* size of struct bpf_prog_info, when the tool is compiled */
+ __u32 info_len;
+ /* total bytes allocated for data, round up to 8 bytes */
+ __u32 data_len;
+ /* which arrays are included in data */
+ __u64 arrays;
+ struct bpf_prog_info info;
+ __u8 data[];
+};
+
+LIBBPF_API struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays);
+
+LIBBPF_API void
+bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
+
+LIBBPF_API void
+bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
+
+/*
+ * A helper function to get the number of possible CPUs before looking up
+ * per-CPU maps. Negative errno is returned on failure.
+ *
+ * Example usage:
+ *
+ * int ncpus = libbpf_num_possible_cpus();
+ * if (ncpus < 0) {
+ * // error handling
+ * }
+ * long values[ncpus];
+ * bpf_map_lookup_elem(per_cpu_map_fd, key, values);
+ *
+ */
+LIBBPF_API int libbpf_num_possible_cpus(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __LIBBPF_LIBBPF_H */
diff --git a/src/contrib/libbpf/bpf/libbpf_errno.c b/src/contrib/libbpf/bpf/libbpf_errno.c
new file mode 100644
index 0000000..4343e40
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf_errno.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ * Copyright (C) 2017 Nicira, Inc.
+ */
+
+#undef _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+
+#include "libbpf.h"
+
+#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
+#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
+#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
+
+static const char *libbpf_strerror_table[NR_ERRNO] = {
+ [ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
+ [ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
+ [ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
+ [ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
+ [ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
+ [ERRCODE_OFFSET(RELOC)] = "Relocation failed",
+ [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
+ [ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
+ [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
+ [ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
+ [ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message",
+ [ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence",
+ [ERRCODE_OFFSET(NLPARSE)] = "Incorrect netlink message parsing",
+};
+
+int libbpf_strerror(int err, char *buf, size_t size)
+{
+ if (!buf || !size)
+ return -1;
+
+ err = err > 0 ? err : -err;
+
+ if (err < __LIBBPF_ERRNO__START) {
+ int ret;
+
+ ret = strerror_r(err, buf, size);
+ buf[size - 1] = '\0';
+ return ret;
+ }
+
+ if (err < __LIBBPF_ERRNO__END) {
+ const char *msg;
+
+ msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
+ snprintf(buf, size, "%s", msg);
+ buf[size - 1] = '\0';
+ return 0;
+ }
+
+ snprintf(buf, size, "Unknown libbpf error %d", err);
+ buf[size - 1] = '\0';
+ return -1;
+}
diff --git a/src/contrib/libbpf/bpf/libbpf_internal.h b/src/contrib/libbpf/bpf/libbpf_internal.h
new file mode 100644
index 0000000..97ac17a
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf_internal.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * Internal libbpf helpers.
+ *
+ * Copyright (c) 2019 Facebook
+ */
+
+#ifndef __LIBBPF_LIBBPF_INTERNAL_H
+#define __LIBBPF_LIBBPF_INTERNAL_H
+
+#include "libbpf.h"
+
+#define BTF_INFO_ENC(kind, kind_flag, vlen) \
+ ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
+#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
+#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
+ ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
+#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
+ BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
+ BTF_INT_ENC(encoding, bits_offset, bits)
+#define BTF_MEMBER_ENC(name, type, bits_offset) (name), (type), (bits_offset)
+#define BTF_PARAM_ENC(name, type) (name), (type)
+#define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
+
+#ifndef min
+# define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#ifndef max
+# define max(x, y) ((x) < (y) ? (y) : (x))
+#endif
+#ifndef offsetofend
+# define offsetofend(TYPE, FIELD) \
+ (offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD))
+#endif
+
+/* Symbol versioning is different between static and shared library.
+ * Properly versioned symbols are needed for shared library, but
+ * only the symbol of the new version is needed for static library.
+ */
+#ifdef SHARED
+# define COMPAT_VERSION(internal_name, api_name, version) \
+ asm(".symver " #internal_name "," #api_name "@" #version);
+# define DEFAULT_VERSION(internal_name, api_name, version) \
+ asm(".symver " #internal_name "," #api_name "@@" #version);
+#else
+# define COMPAT_VERSION(internal_name, api_name, version)
+# define DEFAULT_VERSION(internal_name, api_name, version) \
+ extern typeof(internal_name) api_name \
+ __attribute__((alias(#internal_name)));
+#endif
+
+extern void libbpf_print(enum libbpf_print_level level,
+ const char *format, ...)
+ __attribute__((format(printf, 2, 3)));
+
+#define __pr(level, fmt, ...) \
+do { \
+ libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \
+} while (0)
+
+#define pr_warn(fmt, ...) __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
+#define pr_info(fmt, ...) __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
+#define pr_debug(fmt, ...) __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
+
+static inline bool libbpf_validate_opts(const char *opts,
+ size_t opts_sz, size_t user_sz,
+ const char *type_name)
+{
+ if (user_sz < sizeof(size_t)) {
+ pr_warn("%s size (%zu) is too small\n", type_name, user_sz);
+ return false;
+ }
+ if (user_sz > opts_sz) {
+ size_t i;
+
+ for (i = opts_sz; i < user_sz; i++) {
+ if (opts[i]) {
+ pr_warn("%s has non-zero extra bytes",
+ type_name);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+#define OPTS_VALID(opts, type) \
+ (!(opts) || libbpf_validate_opts((const char *)opts, \
+ offsetofend(struct type, \
+ type##__last_field), \
+ (opts)->sz, #type))
+#define OPTS_HAS(opts, field) \
+ ((opts) && opts->sz >= offsetofend(typeof(*(opts)), field))
+#define OPTS_GET(opts, field, fallback_value) \
+ (OPTS_HAS(opts, field) ? (opts)->field : fallback_value)
+
+int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
+ const char *str_sec, size_t str_len);
+
+struct btf_ext_info {
+ /*
+ * info points to the individual info section (e.g. func_info and
+ * line_info) from the .BTF.ext. It does not include the __u32 rec_size.
+ */
+ void *info;
+ __u32 rec_size;
+ __u32 len;
+};
+
+#define for_each_btf_ext_sec(seg, sec) \
+ for (sec = (seg)->info; \
+ (void *)sec < (seg)->info + (seg)->len; \
+ sec = (void *)sec + sizeof(struct btf_ext_info_sec) + \
+ (seg)->rec_size * sec->num_info)
+
+#define for_each_btf_ext_rec(seg, sec, i, rec) \
+ for (i = 0, rec = (void *)&(sec)->data; \
+ i < (sec)->num_info; \
+ i++, rec = (void *)rec + (seg)->rec_size)
+
+struct btf_ext {
+ union {
+ struct btf_ext_header *hdr;
+ void *data;
+ };
+ struct btf_ext_info func_info;
+ struct btf_ext_info line_info;
+ struct btf_ext_info field_reloc_info;
+ __u32 data_size;
+};
+
+struct btf_ext_info_sec {
+ __u32 sec_name_off;
+ __u32 num_info;
+ /* Followed by num_info * record_size number of bytes */
+ __u8 data[0];
+};
+
+/* The minimum bpf_func_info checked by the loader */
+struct bpf_func_info_min {
+ __u32 insn_off;
+ __u32 type_id;
+};
+
+/* The minimum bpf_line_info checked by the loader */
+struct bpf_line_info_min {
+ __u32 insn_off;
+ __u32 file_name_off;
+ __u32 line_off;
+ __u32 line_col;
+};
+
+/* bpf_field_info_kind encodes which aspect of captured field has to be
+ * adjusted by relocations. Currently supported values are:
+ * - BPF_FIELD_BYTE_OFFSET: field offset (in bytes);
+ * - BPF_FIELD_EXISTS: field existence (1, if field exists; 0, otherwise);
+ */
+enum bpf_field_info_kind {
+ BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
+ BPF_FIELD_BYTE_SIZE = 1,
+ BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
+ BPF_FIELD_SIGNED = 3,
+ BPF_FIELD_LSHIFT_U64 = 4,
+ BPF_FIELD_RSHIFT_U64 = 5,
+};
+
+/* The minimum bpf_field_reloc checked by the loader
+ *
+ * Field relocation captures the following data:
+ * - insn_off - instruction offset (in bytes) within a BPF program that needs
+ * its insn->imm field to be relocated with actual field info;
+ * - type_id - BTF type ID of the "root" (containing) entity of a relocatable
+ * field;
+ * - access_str_off - offset into corresponding .BTF string section. String
+ * itself encodes an accessed field using a sequence of field and array
+ * indicies, separated by colon (:). It's conceptually very close to LLVM's
+ * getelementptr ([0]) instruction's arguments for identifying offset to
+ * a field.
+ *
+ * Example to provide a better feel.
+ *
+ * struct sample {
+ * int a;
+ * struct {
+ * int b[10];
+ * };
+ * };
+ *
+ * struct sample *s = ...;
+ * int x = &s->a; // encoded as "0:0" (a is field #0)
+ * int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1,
+ * // b is field #0 inside anon struct, accessing elem #5)
+ * int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
+ *
+ * type_id for all relocs in this example will capture BTF type id of
+ * `struct sample`.
+ *
+ * Such relocation is emitted when using __builtin_preserve_access_index()
+ * Clang built-in, passing expression that captures field address, e.g.:
+ *
+ * bpf_probe_read(&dst, sizeof(dst),
+ * __builtin_preserve_access_index(&src->a.b.c));
+ *
+ * In this case Clang will emit field relocation recording necessary data to
+ * be able to find offset of embedded `a.b.c` field within `src` struct.
+ *
+ * [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
+ */
+struct bpf_field_reloc {
+ __u32 insn_off;
+ __u32 type_id;
+ __u32 access_str_off;
+ enum bpf_field_info_kind kind;
+};
+
+#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
diff --git a/src/contrib/libbpf/bpf/libbpf_probes.c b/src/contrib/libbpf/bpf/libbpf_probes.c
new file mode 100644
index 0000000..a9eb8b3
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf_probes.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2019 Netronome Systems, Inc. */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <sys/utsname.h>
+
+#include <linux/btf.h>
+#include <linux/filter.h>
+#include <linux/kernel.h>
+
+#include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+
+static bool grep(const char *buffer, const char *pattern)
+{
+ return !!strstr(buffer, pattern);
+}
+
+static int get_vendor_id(int ifindex)
+{
+ char ifname[IF_NAMESIZE], path[64], buf[8];
+ ssize_t len;
+ int fd;
+
+ if (!if_indextoname(ifindex, ifname))
+ return -1;
+
+ snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ len = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (len < 0)
+ return -1;
+ if (len >= (ssize_t)sizeof(buf))
+ return -1;
+ buf[len] = '\0';
+
+ return strtol(buf, NULL, 0);
+}
+
+static int get_kernel_version(void)
+{
+ int version, subversion, patchlevel;
+ struct utsname utsn;
+
+ /* Return 0 on failure, and attempt to probe with empty kversion */
+ if (uname(&utsn))
+ return 0;
+
+ if (sscanf(utsn.release, "%d.%d.%d",
+ &version, &subversion, &patchlevel) != 3)
+ return 0;
+
+ return (version << 16) + (subversion << 8) + patchlevel;
+}
+
+static void
+probe_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns,
+ size_t insns_cnt, char *buf, size_t buf_len, __u32 ifindex)
+{
+ struct bpf_load_program_attr xattr = {};
+ int fd;
+
+ switch (prog_type) {
+ case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
+ xattr.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
+ break;
+ case BPF_PROG_TYPE_KPROBE:
+ xattr.kern_version = get_kernel_version();
+ break;
+ case BPF_PROG_TYPE_UNSPEC:
+ case BPF_PROG_TYPE_SOCKET_FILTER:
+ case BPF_PROG_TYPE_SCHED_CLS:
+ case BPF_PROG_TYPE_SCHED_ACT:
+ case BPF_PROG_TYPE_TRACEPOINT:
+ case BPF_PROG_TYPE_XDP:
+ case BPF_PROG_TYPE_PERF_EVENT:
+ case BPF_PROG_TYPE_CGROUP_SKB:
+ case BPF_PROG_TYPE_CGROUP_SOCK:
+ case BPF_PROG_TYPE_LWT_IN:
+ case BPF_PROG_TYPE_LWT_OUT:
+ case BPF_PROG_TYPE_LWT_XMIT:
+ case BPF_PROG_TYPE_SOCK_OPS:
+ case BPF_PROG_TYPE_SK_SKB:
+ case BPF_PROG_TYPE_CGROUP_DEVICE:
+ case BPF_PROG_TYPE_SK_MSG:
+ case BPF_PROG_TYPE_RAW_TRACEPOINT:
+ case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
+ case BPF_PROG_TYPE_LWT_SEG6LOCAL:
+ case BPF_PROG_TYPE_LIRC_MODE2:
+ case BPF_PROG_TYPE_SK_REUSEPORT:
+ case BPF_PROG_TYPE_FLOW_DISSECTOR:
+ case BPF_PROG_TYPE_CGROUP_SYSCTL:
+ case BPF_PROG_TYPE_CGROUP_SOCKOPT:
+ case BPF_PROG_TYPE_TRACING:
+ default:
+ break;
+ }
+
+ xattr.prog_type = prog_type;
+ xattr.insns = insns;
+ xattr.insns_cnt = insns_cnt;
+ xattr.license = "GPL";
+ xattr.prog_ifindex = ifindex;
+
+ fd = bpf_load_program_xattr(&xattr, buf, buf_len);
+ if (fd >= 0)
+ close(fd);
+}
+
+bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex)
+{
+ struct bpf_insn insns[2] = {
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN()
+ };
+
+ if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS)
+ /* nfp returns -EINVAL on exit(0) with TC offload */
+ insns[0].imm = 2;
+
+ errno = 0;
+ probe_load(prog_type, insns, ARRAY_SIZE(insns), NULL, 0, ifindex);
+
+ return errno != EINVAL && errno != EOPNOTSUPP;
+}
+
+int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
+ const char *str_sec, size_t str_len)
+{
+ struct btf_header hdr = {
+ .magic = BTF_MAGIC,
+ .version = BTF_VERSION,
+ .hdr_len = sizeof(struct btf_header),
+ .type_len = types_len,
+ .str_off = types_len,
+ .str_len = str_len,
+ };
+ int btf_fd, btf_len;
+ __u8 *raw_btf;
+
+ btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len;
+ raw_btf = malloc(btf_len);
+ if (!raw_btf)
+ return -ENOMEM;
+
+ memcpy(raw_btf, &hdr, sizeof(hdr));
+ memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len);
+ memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
+
+ btf_fd = bpf_load_btf(raw_btf, btf_len, NULL, 0, false);
+
+ free(raw_btf);
+ return btf_fd;
+}
+
+static int load_sk_storage_btf(void)
+{
+ const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l";
+ /* struct bpf_spin_lock {
+ * int val;
+ * };
+ * struct val {
+ * int cnt;
+ * struct bpf_spin_lock l;
+ * };
+ */
+ __u32 types[] = {
+ /* int */
+ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
+ /* struct bpf_spin_lock */ /* [2] */
+ BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),
+ BTF_MEMBER_ENC(15, 1, 0), /* int val; */
+ /* struct val */ /* [3] */
+ BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8),
+ BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */
+ BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */
+ };
+
+ return libbpf__load_raw_btf((char *)types, sizeof(types),
+ strs, sizeof(strs));
+}
+
+bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
+{
+ int key_size, value_size, max_entries, map_flags;
+ __u32 btf_key_type_id = 0, btf_value_type_id = 0;
+ struct bpf_create_map_attr attr = {};
+ int fd = -1, btf_fd = -1, fd_inner;
+
+ key_size = sizeof(__u32);
+ value_size = sizeof(__u32);
+ max_entries = 1;
+ map_flags = 0;
+
+ switch (map_type) {
+ case BPF_MAP_TYPE_STACK_TRACE:
+ value_size = sizeof(__u64);
+ break;
+ case BPF_MAP_TYPE_LPM_TRIE:
+ key_size = sizeof(__u64);
+ value_size = sizeof(__u64);
+ map_flags = BPF_F_NO_PREALLOC;
+ break;
+ case BPF_MAP_TYPE_CGROUP_STORAGE:
+ case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
+ key_size = sizeof(struct bpf_cgroup_storage_key);
+ value_size = sizeof(__u64);
+ max_entries = 0;
+ break;
+ case BPF_MAP_TYPE_QUEUE:
+ case BPF_MAP_TYPE_STACK:
+ key_size = 0;
+ break;
+ case BPF_MAP_TYPE_SK_STORAGE:
+ btf_key_type_id = 1;
+ btf_value_type_id = 3;
+ value_size = 8;
+ max_entries = 0;
+ map_flags = BPF_F_NO_PREALLOC;
+ btf_fd = load_sk_storage_btf();
+ if (btf_fd < 0)
+ return false;
+ break;
+ case BPF_MAP_TYPE_UNSPEC:
+ case BPF_MAP_TYPE_HASH:
+ case BPF_MAP_TYPE_ARRAY:
+ case BPF_MAP_TYPE_PROG_ARRAY:
+ case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
+ case BPF_MAP_TYPE_PERCPU_HASH:
+ case BPF_MAP_TYPE_PERCPU_ARRAY:
+ case BPF_MAP_TYPE_CGROUP_ARRAY:
+ case BPF_MAP_TYPE_LRU_HASH:
+ case BPF_MAP_TYPE_LRU_PERCPU_HASH:
+ case BPF_MAP_TYPE_ARRAY_OF_MAPS:
+ case BPF_MAP_TYPE_HASH_OF_MAPS:
+ case BPF_MAP_TYPE_DEVMAP:
+ case BPF_MAP_TYPE_DEVMAP_HASH:
+ case BPF_MAP_TYPE_SOCKMAP:
+ case BPF_MAP_TYPE_CPUMAP:
+ case BPF_MAP_TYPE_XSKMAP:
+ case BPF_MAP_TYPE_SOCKHASH:
+ case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
+ default:
+ break;
+ }
+
+ if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+ map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
+ /* TODO: probe for device, once libbpf has a function to create
+ * map-in-map for offload
+ */
+ if (ifindex)
+ return false;
+
+ fd_inner = bpf_create_map(BPF_MAP_TYPE_HASH,
+ sizeof(__u32), sizeof(__u32), 1, 0);
+ if (fd_inner < 0)
+ return false;
+ fd = bpf_create_map_in_map(map_type, NULL, sizeof(__u32),
+ fd_inner, 1, 0);
+ close(fd_inner);
+ } else {
+ /* Note: No other restriction on map type probes for offload */
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = value_size;
+ attr.max_entries = max_entries;
+ attr.map_flags = map_flags;
+ attr.map_ifindex = ifindex;
+ if (btf_fd >= 0) {
+ attr.btf_fd = btf_fd;
+ attr.btf_key_type_id = btf_key_type_id;
+ attr.btf_value_type_id = btf_value_type_id;
+ }
+
+ fd = bpf_create_map_xattr(&attr);
+ }
+ if (fd >= 0)
+ close(fd);
+ if (btf_fd >= 0)
+ close(btf_fd);
+
+ return fd >= 0;
+}
+
+bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type,
+ __u32 ifindex)
+{
+ struct bpf_insn insns[2] = {
+ BPF_EMIT_CALL(id),
+ BPF_EXIT_INSN()
+ };
+ char buf[4096] = {};
+ bool res;
+
+ probe_load(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf),
+ ifindex);
+ res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ");
+
+ if (ifindex) {
+ switch (get_vendor_id(ifindex)) {
+ case 0x19ee: /* Netronome specific */
+ res = res && !grep(buf, "not supported by FW") &&
+ !grep(buf, "unsupported function id");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return res;
+}
diff --git a/src/contrib/libbpf/bpf/libbpf_util.h b/src/contrib/libbpf/bpf/libbpf_util.h
new file mode 100644
index 0000000..59c779c
--- /dev/null
+++ b/src/contrib/libbpf/bpf/libbpf_util.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2019 Facebook */
+
+#ifndef __LIBBPF_LIBBPF_UTIL_H
+#define __LIBBPF_LIBBPF_UTIL_H
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Use these barrier functions instead of smp_[rw]mb() when they are
+ * used in a libbpf header file. That way they can be built into the
+ * application that uses libbpf.
+ */
+#if defined(__i386__) || defined(__x86_64__)
+# define libbpf_smp_rmb() asm volatile("" : : : "memory")
+# define libbpf_smp_wmb() asm volatile("" : : : "memory")
+# define libbpf_smp_mb() \
+ asm volatile("lock; addl $0,-4(%%rsp)" : : : "memory", "cc")
+/* Hinders stores to be observed before older loads. */
+# define libbpf_smp_rwmb() asm volatile("" : : : "memory")
+#elif defined(__aarch64__)
+# define libbpf_smp_rmb() asm volatile("dmb ishld" : : : "memory")
+# define libbpf_smp_wmb() asm volatile("dmb ishst" : : : "memory")
+# define libbpf_smp_mb() asm volatile("dmb ish" : : : "memory")
+# define libbpf_smp_rwmb() libbpf_smp_mb()
+#elif defined(__arm__)
+/* These are only valid for armv7 and above */
+# define libbpf_smp_rmb() asm volatile("dmb ish" : : : "memory")
+# define libbpf_smp_wmb() asm volatile("dmb ishst" : : : "memory")
+# define libbpf_smp_mb() asm volatile("dmb ish" : : : "memory")
+# define libbpf_smp_rwmb() libbpf_smp_mb()
+#else
+/* Architecture missing native barrier functions. */
+# define libbpf_smp_rmb() __sync_synchronize()
+# define libbpf_smp_wmb() __sync_synchronize()
+# define libbpf_smp_mb() __sync_synchronize()
+# define libbpf_smp_rwmb() __sync_synchronize()
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
diff --git a/src/contrib/libbpf/bpf/netlink.c b/src/contrib/libbpf/bpf/netlink.c
new file mode 100644
index 0000000..5065c1a
--- /dev/null
+++ b/src/contrib/libbpf/bpf/netlink.c
@@ -0,0 +1,451 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+/* Copyright (c) 2018 Facebook */
+
+#include <stdlib.h>
+#include <memory.h>
+#include <unistd.h>
+#include <linux/bpf.h>
+#include <linux/rtnetlink.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <time.h>
+
+#include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+#include "nlattr.h"
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+typedef int (*__dump_nlmsg_t)(struct nlmsghdr *nlmsg, libbpf_dump_nlmsg_t,
+ void *cookie);
+
+struct xdp_id_md {
+ int ifindex;
+ __u32 flags;
+ struct xdp_link_info info;
+};
+
+int libbpf_netlink_open(__u32 *nl_pid)
+{
+ struct sockaddr_nl sa;
+ socklen_t addrlen;
+ int one = 1, ret;
+ int sock;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nl_family = AF_NETLINK;
+
+ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0)
+ return -errno;
+
+ if (setsockopt(sock, SOL_NETLINK, NETLINK_EXT_ACK,
+ &one, sizeof(one)) < 0) {
+ pr_warn("Netlink error reporting not supported\n");
+ }
+
+ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ addrlen = sizeof(sa);
+ if (getsockname(sock, (struct sockaddr *)&sa, &addrlen) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+
+ if (addrlen != sizeof(sa)) {
+ ret = -LIBBPF_ERRNO__INTERNAL;
+ goto cleanup;
+ }
+
+ *nl_pid = sa.nl_pid;
+ return sock;
+
+cleanup:
+ close(sock);
+ return ret;
+}
+
+static int bpf_netlink_recv(int sock, __u32 nl_pid, int seq,
+ __dump_nlmsg_t _fn, libbpf_dump_nlmsg_t fn,
+ void *cookie)
+{
+ bool multipart = true;
+ struct nlmsgerr *err;
+ struct nlmsghdr *nh;
+ char buf[4096];
+ int len, ret;
+
+ while (multipart) {
+ multipart = false;
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ ret = -errno;
+ goto done;
+ }
+
+ if (len == 0)
+ break;
+
+ for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len);
+ nh = NLMSG_NEXT(nh, len)) {
+ if (nh->nlmsg_pid != nl_pid) {
+ ret = -LIBBPF_ERRNO__WRNGPID;
+ goto done;
+ }
+ if (nh->nlmsg_seq != seq) {
+ ret = -LIBBPF_ERRNO__INVSEQ;
+ goto done;
+ }
+ if (nh->nlmsg_flags & NLM_F_MULTI)
+ multipart = true;
+ switch (nh->nlmsg_type) {
+ case NLMSG_ERROR:
+ err = (struct nlmsgerr *)NLMSG_DATA(nh);
+ if (!err->error)
+ continue;
+ ret = err->error;
+ libbpf_nla_dump_errormsg(nh);
+ goto done;
+ case NLMSG_DONE:
+ return 0;
+ default:
+ break;
+ }
+ if (_fn) {
+ ret = _fn(nh, fn, cookie);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ ret = 0;
+done:
+ return ret;
+}
+
+int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
+{
+ int sock, seq = 0, ret;
+ struct nlattr *nla, *nla_xdp;
+ struct {
+ struct nlmsghdr nh;
+ struct ifinfomsg ifinfo;
+ char attrbuf[64];
+ } req;
+ __u32 nl_pid;
+
+ sock = libbpf_netlink_open(&nl_pid);
+ if (sock < 0)
+ return sock;
+
+ memset(&req, 0, sizeof(req));
+ req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nh.nlmsg_type = RTM_SETLINK;
+ req.nh.nlmsg_pid = 0;
+ req.nh.nlmsg_seq = ++seq;
+ req.ifinfo.ifi_family = AF_UNSPEC;
+ req.ifinfo.ifi_index = ifindex;
+
+ /* started nested attribute for XDP */
+ nla = (struct nlattr *)(((char *)&req)
+ + NLMSG_ALIGN(req.nh.nlmsg_len));
+ nla->nla_type = NLA_F_NESTED | IFLA_XDP;
+ nla->nla_len = NLA_HDRLEN;
+
+ /* add XDP fd */
+ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
+ nla_xdp->nla_type = IFLA_XDP_FD;
+ nla_xdp->nla_len = NLA_HDRLEN + sizeof(int);
+ memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd));
+ nla->nla_len += nla_xdp->nla_len;
+
+ /* if user passed in any flags, add those too */
+ if (flags) {
+ nla_xdp = (struct nlattr *)((char *)nla + nla->nla_len);
+ nla_xdp->nla_type = IFLA_XDP_FLAGS;
+ nla_xdp->nla_len = NLA_HDRLEN + sizeof(flags);
+ memcpy((char *)nla_xdp + NLA_HDRLEN, &flags, sizeof(flags));
+ nla->nla_len += nla_xdp->nla_len;
+ }
+
+ req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len);
+
+ if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) {
+ ret = -errno;
+ goto cleanup;
+ }
+ ret = bpf_netlink_recv(sock, nl_pid, seq, NULL, NULL, NULL);
+
+cleanup:
+ close(sock);
+ return ret;
+}
+
+static int __dump_link_nlmsg(struct nlmsghdr *nlh,
+ libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
+{
+ struct nlattr *tb[IFLA_MAX + 1], *attr;
+ struct ifinfomsg *ifi = NLMSG_DATA(nlh);
+ int len;
+
+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi));
+ attr = (struct nlattr *) ((void *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
+ if (libbpf_nla_parse(tb, IFLA_MAX, attr, len, NULL) != 0)
+ return -LIBBPF_ERRNO__NLPARSE;
+
+ return dump_link_nlmsg(cookie, ifi, tb);
+}
+
+static int get_xdp_info(void *cookie, void *msg, struct nlattr **tb)
+{
+ struct nlattr *xdp_tb[IFLA_XDP_MAX + 1];
+ struct xdp_id_md *xdp_id = cookie;
+ struct ifinfomsg *ifinfo = msg;
+ int ret;
+
+ if (xdp_id->ifindex && xdp_id->ifindex != ifinfo->ifi_index)
+ return 0;
+
+ if (!tb[IFLA_XDP])
+ return 0;
+
+ ret = libbpf_nla_parse_nested(xdp_tb, IFLA_XDP_MAX, tb[IFLA_XDP], NULL);
+ if (ret)
+ return ret;
+
+ if (!xdp_tb[IFLA_XDP_ATTACHED])
+ return 0;
+
+ xdp_id->info.attach_mode = libbpf_nla_getattr_u8(
+ xdp_tb[IFLA_XDP_ATTACHED]);
+
+ if (xdp_id->info.attach_mode == XDP_ATTACHED_NONE)
+ return 0;
+
+ if (xdp_tb[IFLA_XDP_PROG_ID])
+ xdp_id->info.prog_id = libbpf_nla_getattr_u32(
+ xdp_tb[IFLA_XDP_PROG_ID]);
+
+ if (xdp_tb[IFLA_XDP_SKB_PROG_ID])
+ xdp_id->info.skb_prog_id = libbpf_nla_getattr_u32(
+ xdp_tb[IFLA_XDP_SKB_PROG_ID]);
+
+ if (xdp_tb[IFLA_XDP_DRV_PROG_ID])
+ xdp_id->info.drv_prog_id = libbpf_nla_getattr_u32(
+ xdp_tb[IFLA_XDP_DRV_PROG_ID]);
+
+ if (xdp_tb[IFLA_XDP_HW_PROG_ID])
+ xdp_id->info.hw_prog_id = libbpf_nla_getattr_u32(
+ xdp_tb[IFLA_XDP_HW_PROG_ID]);
+
+ return 0;
+}
+
+int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
+ size_t info_size, __u32 flags)
+{
+ struct xdp_id_md xdp_id = {};
+ int sock, ret;
+ __u32 nl_pid;
+ __u32 mask;
+
+ if (flags & ~XDP_FLAGS_MASK || !info_size)
+ return -EINVAL;
+
+ /* Check whether the single {HW,DRV,SKB} mode is set */
+ flags &= (XDP_FLAGS_SKB_MODE | XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE);
+ mask = flags - 1;
+ if (flags && flags & mask)
+ return -EINVAL;
+
+ sock = libbpf_netlink_open(&nl_pid);
+ if (sock < 0)
+ return sock;
+
+ xdp_id.ifindex = ifindex;
+ xdp_id.flags = flags;
+
+ ret = libbpf_nl_get_link(sock, nl_pid, get_xdp_info, &xdp_id);
+ if (!ret) {
+ size_t sz = min(info_size, sizeof(xdp_id.info));
+
+ memcpy(info, &xdp_id.info, sz);
+ memset((void *) info + sz, 0, info_size - sz);
+ }
+
+ close(sock);
+ return ret;
+}
+
+static __u32 get_xdp_id(struct xdp_link_info *info, __u32 flags)
+{
+ if (info->attach_mode != XDP_ATTACHED_MULTI)
+ return info->prog_id;
+ if (flags & XDP_FLAGS_DRV_MODE)
+ return info->drv_prog_id;
+ if (flags & XDP_FLAGS_HW_MODE)
+ return info->hw_prog_id;
+ if (flags & XDP_FLAGS_SKB_MODE)
+ return info->skb_prog_id;
+
+ return 0;
+}
+
+int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags)
+{
+ struct xdp_link_info info;
+ int ret;
+
+ ret = bpf_get_link_xdp_info(ifindex, &info, sizeof(info), flags);
+ if (!ret)
+ *prog_id = get_xdp_id(&info, flags);
+
+ return ret;
+}
+
+int libbpf_nl_get_link(int sock, unsigned int nl_pid,
+ libbpf_dump_nlmsg_t dump_link_nlmsg, void *cookie)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct ifinfomsg ifm;
+ } req = {
+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+ .nlh.nlmsg_type = RTM_GETLINK,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .ifm.ifi_family = AF_PACKET,
+ };
+ int seq = time(NULL);
+
+ req.nlh.nlmsg_seq = seq;
+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
+ return -errno;
+
+ return bpf_netlink_recv(sock, nl_pid, seq, __dump_link_nlmsg,
+ dump_link_nlmsg, cookie);
+}
+
+static int __dump_class_nlmsg(struct nlmsghdr *nlh,
+ libbpf_dump_nlmsg_t dump_class_nlmsg,
+ void *cookie)
+{
+ struct nlattr *tb[TCA_MAX + 1], *attr;
+ struct tcmsg *t = NLMSG_DATA(nlh);
+ int len;
+
+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
+ attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
+ if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
+ return -LIBBPF_ERRNO__NLPARSE;
+
+ return dump_class_nlmsg(cookie, t, tb);
+}
+
+int libbpf_nl_get_class(int sock, unsigned int nl_pid, int ifindex,
+ libbpf_dump_nlmsg_t dump_class_nlmsg, void *cookie)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct tcmsg t;
+ } req = {
+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
+ .nlh.nlmsg_type = RTM_GETTCLASS,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .t.tcm_family = AF_UNSPEC,
+ .t.tcm_ifindex = ifindex,
+ };
+ int seq = time(NULL);
+
+ req.nlh.nlmsg_seq = seq;
+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
+ return -errno;
+
+ return bpf_netlink_recv(sock, nl_pid, seq, __dump_class_nlmsg,
+ dump_class_nlmsg, cookie);
+}
+
+static int __dump_qdisc_nlmsg(struct nlmsghdr *nlh,
+ libbpf_dump_nlmsg_t dump_qdisc_nlmsg,
+ void *cookie)
+{
+ struct nlattr *tb[TCA_MAX + 1], *attr;
+ struct tcmsg *t = NLMSG_DATA(nlh);
+ int len;
+
+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
+ attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
+ if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
+ return -LIBBPF_ERRNO__NLPARSE;
+
+ return dump_qdisc_nlmsg(cookie, t, tb);
+}
+
+int libbpf_nl_get_qdisc(int sock, unsigned int nl_pid, int ifindex,
+ libbpf_dump_nlmsg_t dump_qdisc_nlmsg, void *cookie)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct tcmsg t;
+ } req = {
+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
+ .nlh.nlmsg_type = RTM_GETQDISC,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .t.tcm_family = AF_UNSPEC,
+ .t.tcm_ifindex = ifindex,
+ };
+ int seq = time(NULL);
+
+ req.nlh.nlmsg_seq = seq;
+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
+ return -errno;
+
+ return bpf_netlink_recv(sock, nl_pid, seq, __dump_qdisc_nlmsg,
+ dump_qdisc_nlmsg, cookie);
+}
+
+static int __dump_filter_nlmsg(struct nlmsghdr *nlh,
+ libbpf_dump_nlmsg_t dump_filter_nlmsg,
+ void *cookie)
+{
+ struct nlattr *tb[TCA_MAX + 1], *attr;
+ struct tcmsg *t = NLMSG_DATA(nlh);
+ int len;
+
+ len = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*t));
+ attr = (struct nlattr *) ((void *) t + NLMSG_ALIGN(sizeof(*t)));
+ if (libbpf_nla_parse(tb, TCA_MAX, attr, len, NULL) != 0)
+ return -LIBBPF_ERRNO__NLPARSE;
+
+ return dump_filter_nlmsg(cookie, t, tb);
+}
+
+int libbpf_nl_get_filter(int sock, unsigned int nl_pid, int ifindex, int handle,
+ libbpf_dump_nlmsg_t dump_filter_nlmsg, void *cookie)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct tcmsg t;
+ } req = {
+ .nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg)),
+ .nlh.nlmsg_type = RTM_GETTFILTER,
+ .nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+ .t.tcm_family = AF_UNSPEC,
+ .t.tcm_ifindex = ifindex,
+ .t.tcm_parent = handle,
+ };
+ int seq = time(NULL);
+
+ req.nlh.nlmsg_seq = seq;
+ if (send(sock, &req, req.nlh.nlmsg_len, 0) < 0)
+ return -errno;
+
+ return bpf_netlink_recv(sock, nl_pid, seq, __dump_filter_nlmsg,
+ dump_filter_nlmsg, cookie);
+}
diff --git a/src/contrib/libbpf/bpf/nlattr.c b/src/contrib/libbpf/bpf/nlattr.c
new file mode 100644
index 0000000..8db44bb
--- /dev/null
+++ b/src/contrib/libbpf/bpf/nlattr.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * NETLINK Netlink attributes
+ *
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <errno.h>
+#include "nlattr.h"
+#include "libbpf_internal.h"
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <stdio.h>
+
+static uint16_t nla_attr_minlen[LIBBPF_NLA_TYPE_MAX+1] = {
+ [LIBBPF_NLA_U8] = sizeof(uint8_t),
+ [LIBBPF_NLA_U16] = sizeof(uint16_t),
+ [LIBBPF_NLA_U32] = sizeof(uint32_t),
+ [LIBBPF_NLA_U64] = sizeof(uint64_t),
+ [LIBBPF_NLA_STRING] = 1,
+ [LIBBPF_NLA_FLAG] = 0,
+};
+
+static struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
+{
+ int totlen = NLA_ALIGN(nla->nla_len);
+
+ *remaining -= totlen;
+ return (struct nlattr *) ((char *) nla + totlen);
+}
+
+static int nla_ok(const struct nlattr *nla, int remaining)
+{
+ return remaining >= sizeof(*nla) &&
+ nla->nla_len >= sizeof(*nla) &&
+ nla->nla_len <= remaining;
+}
+
+static int nla_type(const struct nlattr *nla)
+{
+ return nla->nla_type & NLA_TYPE_MASK;
+}
+
+static int validate_nla(struct nlattr *nla, int maxtype,
+ struct libbpf_nla_policy *policy)
+{
+ struct libbpf_nla_policy *pt;
+ unsigned int minlen = 0;
+ int type = nla_type(nla);
+
+ if (type < 0 || type > maxtype)
+ return 0;
+
+ pt = &policy[type];
+
+ if (pt->type > LIBBPF_NLA_TYPE_MAX)
+ return 0;
+
+ if (pt->minlen)
+ minlen = pt->minlen;
+ else if (pt->type != LIBBPF_NLA_UNSPEC)
+ minlen = nla_attr_minlen[pt->type];
+
+ if (libbpf_nla_len(nla) < minlen)
+ return -1;
+
+ if (pt->maxlen && libbpf_nla_len(nla) > pt->maxlen)
+ return -1;
+
+ if (pt->type == LIBBPF_NLA_STRING) {
+ char *data = libbpf_nla_data(nla);
+
+ if (data[libbpf_nla_len(nla) - 1] != '\0')
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline int nlmsg_len(const struct nlmsghdr *nlh)
+{
+ return nlh->nlmsg_len - NLMSG_HDRLEN;
+}
+
+/**
+ * Create attribute index based on a stream of attributes.
+ * @arg tb Index array to be filled (maxtype+1 elements).
+ * @arg maxtype Maximum attribute type expected and accepted.
+ * @arg head Head of attribute stream.
+ * @arg len Length of attribute stream.
+ * @arg policy Attribute validation policy.
+ *
+ * Iterates over the stream of attributes and stores a pointer to each
+ * attribute in the index array using the attribute type as index to
+ * the array. Attribute with a type greater than the maximum type
+ * specified will be silently ignored in order to maintain backwards
+ * compatibility. If \a policy is not NULL, the attribute will be
+ * validated using the specified policy.
+ *
+ * @see nla_validate
+ * @return 0 on success or a negative error code.
+ */
+int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
+ int len, struct libbpf_nla_policy *policy)
+{
+ struct nlattr *nla;
+ int rem, err;
+
+ memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
+ libbpf_nla_for_each_attr(nla, head, len, rem) {
+ int type = nla_type(nla);
+
+ if (type > maxtype)
+ continue;
+
+ if (policy) {
+ err = validate_nla(nla, maxtype, policy);
+ if (err < 0)
+ goto errout;
+ }
+
+ if (tb[type])
+ pr_warn("Attribute of type %#x found multiple times in message, "
+ "previous attribute is being ignored.\n", type);
+
+ tb[type] = nla;
+ }
+
+ err = 0;
+errout:
+ return err;
+}
+
+/**
+ * Create attribute index based on nested attribute
+ * @arg tb Index array to be filled (maxtype+1 elements).
+ * @arg maxtype Maximum attribute type expected and accepted.
+ * @arg nla Nested Attribute.
+ * @arg policy Attribute validation policy.
+ *
+ * Feeds the stream of attributes nested into the specified attribute
+ * to libbpf_nla_parse().
+ *
+ * @see libbpf_nla_parse
+ * @return 0 on success or a negative error code.
+ */
+int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
+ struct nlattr *nla,
+ struct libbpf_nla_policy *policy)
+{
+ return libbpf_nla_parse(tb, maxtype, libbpf_nla_data(nla),
+ libbpf_nla_len(nla), policy);
+}
+
+/* dump netlink extended ack error message */
+int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh)
+{
+ struct libbpf_nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = {
+ [NLMSGERR_ATTR_MSG] = { .type = LIBBPF_NLA_STRING },
+ [NLMSGERR_ATTR_OFFS] = { .type = LIBBPF_NLA_U32 },
+ };
+ struct nlattr *tb[NLMSGERR_ATTR_MAX + 1], *attr;
+ struct nlmsgerr *err;
+ char *errmsg = NULL;
+ int hlen, alen;
+
+ /* no TLVs, nothing to do here */
+ if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
+ return 0;
+
+ err = (struct nlmsgerr *)NLMSG_DATA(nlh);
+ hlen = sizeof(*err);
+
+ /* if NLM_F_CAPPED is set then the inner err msg was capped */
+ if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
+ hlen += nlmsg_len(&err->msg);
+
+ attr = (struct nlattr *) ((void *) err + hlen);
+ alen = nlh->nlmsg_len - hlen;
+
+ if (libbpf_nla_parse(tb, NLMSGERR_ATTR_MAX, attr, alen,
+ extack_policy) != 0) {
+ pr_warn("Failed to parse extended error attributes\n");
+ return 0;
+ }
+
+ if (tb[NLMSGERR_ATTR_MSG])
+ errmsg = (char *) libbpf_nla_data(tb[NLMSGERR_ATTR_MSG]);
+
+ pr_warn("Kernel error message: %s\n", errmsg);
+
+ return 0;
+}
diff --git a/src/contrib/libbpf/bpf/nlattr.h b/src/contrib/libbpf/bpf/nlattr.h
new file mode 100644
index 0000000..6cc3ac9
--- /dev/null
+++ b/src/contrib/libbpf/bpf/nlattr.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * NETLINK Netlink attributes
+ *
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __LIBBPF_NLATTR_H
+#define __LIBBPF_NLATTR_H
+
+#include <stdint.h>
+#include <linux/netlink.h>
+/* avoid multiple definition of netlink features */
+#define __LINUX_NETLINK_H
+
+/**
+ * Standard attribute types to specify validation policy
+ */
+enum {
+ LIBBPF_NLA_UNSPEC, /**< Unspecified type, binary data chunk */
+ LIBBPF_NLA_U8, /**< 8 bit integer */
+ LIBBPF_NLA_U16, /**< 16 bit integer */
+ LIBBPF_NLA_U32, /**< 32 bit integer */
+ LIBBPF_NLA_U64, /**< 64 bit integer */
+ LIBBPF_NLA_STRING, /**< NUL terminated character string */
+ LIBBPF_NLA_FLAG, /**< Flag */
+ LIBBPF_NLA_MSECS, /**< Micro seconds (64bit) */
+ LIBBPF_NLA_NESTED, /**< Nested attributes */
+ __LIBBPF_NLA_TYPE_MAX,
+};
+
+#define LIBBPF_NLA_TYPE_MAX (__LIBBPF_NLA_TYPE_MAX - 1)
+
+/**
+ * @ingroup attr
+ * Attribute validation policy.
+ *
+ * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
+ */
+struct libbpf_nla_policy {
+ /** Type of attribute or LIBBPF_NLA_UNSPEC */
+ uint16_t type;
+
+ /** Minimal length of payload required */
+ uint16_t minlen;
+
+ /** Maximal length of payload allowed */
+ uint16_t maxlen;
+};
+
+/**
+ * @ingroup attr
+ * Iterate over a stream of attributes
+ * @arg pos loop counter, set to current attribute
+ * @arg head head of attribute stream
+ * @arg len length of attribute stream
+ * @arg rem initialized to len, holds bytes currently remaining in stream
+ */
+#define libbpf_nla_for_each_attr(pos, head, len, rem) \
+ for (pos = head, rem = len; \
+ nla_ok(pos, rem); \
+ pos = nla_next(pos, &(rem)))
+
+/**
+ * libbpf_nla_data - head of payload
+ * @nla: netlink attribute
+ */
+static inline void *libbpf_nla_data(const struct nlattr *nla)
+{
+ return (char *) nla + NLA_HDRLEN;
+}
+
+static inline uint8_t libbpf_nla_getattr_u8(const struct nlattr *nla)
+{
+ return *(uint8_t *)libbpf_nla_data(nla);
+}
+
+static inline uint32_t libbpf_nla_getattr_u32(const struct nlattr *nla)
+{
+ return *(uint32_t *)libbpf_nla_data(nla);
+}
+
+static inline const char *libbpf_nla_getattr_str(const struct nlattr *nla)
+{
+ return (const char *)libbpf_nla_data(nla);
+}
+
+/**
+ * libbpf_nla_len - length of payload
+ * @nla: netlink attribute
+ */
+static inline int libbpf_nla_len(const struct nlattr *nla)
+{
+ return nla->nla_len - NLA_HDRLEN;
+}
+
+int libbpf_nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
+ int len, struct libbpf_nla_policy *policy);
+int libbpf_nla_parse_nested(struct nlattr *tb[], int maxtype,
+ struct nlattr *nla,
+ struct libbpf_nla_policy *policy);
+
+int libbpf_nla_dump_errormsg(struct nlmsghdr *nlh);
+
+#endif /* __LIBBPF_NLATTR_H */
diff --git a/src/contrib/libbpf/bpf/str_error.c b/src/contrib/libbpf/bpf/str_error.c
new file mode 100644
index 0000000..b8064ee
--- /dev/null
+++ b/src/contrib/libbpf/bpf/str_error.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+#undef _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include "str_error.h"
+
+/*
+ * Wrapper to allow for building in non-GNU systems such as Alpine Linux's musl
+ * libc, while checking strerror_r() return to avoid having to check this in
+ * all places calling it.
+ */
+char *libbpf_strerror_r(int err, char *dst, int len)
+{
+ int ret = strerror_r(err < 0 ? -err : err, dst, len);
+ if (ret)
+ snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
+ return dst;
+}
diff --git a/src/contrib/libbpf/bpf/str_error.h b/src/contrib/libbpf/bpf/str_error.h
new file mode 100644
index 0000000..a139334
--- /dev/null
+++ b/src/contrib/libbpf/bpf/str_error.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __LIBBPF_STR_ERROR_H
+#define __LIBBPF_STR_ERROR_H
+
+char *libbpf_strerror_r(int err, char *dst, int len);
+#endif /* __LIBBPF_STR_ERROR_H */
diff --git a/src/contrib/libbpf/bpf/xsk.c b/src/contrib/libbpf/bpf/xsk.c
new file mode 100644
index 0000000..8e0ffa8
--- /dev/null
+++ b/src/contrib/libbpf/bpf/xsk.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
+
+/*
+ * AF_XDP user-space access library.
+ *
+ * Copyright(c) 2018 - 2019 Intel Corporation.
+ *
+ * Author(s): Magnus Karlsson <magnus.karlsson@intel.com>
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <asm/barrier.h>
+#include <linux/compiler.h>
+#include <linux/ethtool.h>
+#include <linux/filter.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/if_xdp.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "bpf.h"
+#include "libbpf.h"
+#include "libbpf_internal.h"
+#include "xsk.h"
+
+#ifndef SOL_XDP
+ #define SOL_XDP 283
+#endif
+
+#ifndef AF_XDP
+ #define AF_XDP 44
+#endif
+
+#ifndef PF_XDP
+ #define PF_XDP AF_XDP
+#endif
+
+struct xsk_umem {
+ struct xsk_ring_prod *fill;
+ struct xsk_ring_cons *comp;
+ char *umem_area;
+ struct xsk_umem_config config;
+ int fd;
+ int refcount;
+};
+
+struct xsk_socket {
+ struct xsk_ring_cons *rx;
+ struct xsk_ring_prod *tx;
+ __u64 outstanding_tx;
+ struct xsk_umem *umem;
+ struct xsk_socket_config config;
+ int fd;
+ int ifindex;
+ int prog_fd;
+ int xsks_map_fd;
+ __u32 queue_id;
+ char ifname[IFNAMSIZ];
+};
+
+struct xsk_nl_info {
+ bool xdp_prog_attached;
+ int ifindex;
+ int fd;
+};
+
+/* Up until and including Linux 5.3 */
+struct xdp_ring_offset_v1 {
+ __u64 producer;
+ __u64 consumer;
+ __u64 desc;
+};
+
+/* Up until and including Linux 5.3 */
+struct xdp_mmap_offsets_v1 {
+ struct xdp_ring_offset_v1 rx;
+ struct xdp_ring_offset_v1 tx;
+ struct xdp_ring_offset_v1 fr;
+ struct xdp_ring_offset_v1 cr;
+};
+
+int xsk_umem__fd(const struct xsk_umem *umem)
+{
+ return umem ? umem->fd : -EINVAL;
+}
+
+int xsk_socket__fd(const struct xsk_socket *xsk)
+{
+ return xsk ? xsk->fd : -EINVAL;
+}
+
+static bool xsk_page_aligned(void *buffer)
+{
+ unsigned long addr = (unsigned long)buffer;
+
+ return !(addr & (getpagesize() - 1));
+}
+
+static void xsk_set_umem_config(struct xsk_umem_config *cfg,
+ const struct xsk_umem_config *usr_cfg)
+{
+ if (!usr_cfg) {
+ cfg->fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
+ cfg->comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
+ cfg->frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE;
+ cfg->frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM;
+ cfg->flags = XSK_UMEM__DEFAULT_FLAGS;
+ return;
+ }
+
+ cfg->fill_size = usr_cfg->fill_size;
+ cfg->comp_size = usr_cfg->comp_size;
+ cfg->frame_size = usr_cfg->frame_size;
+ cfg->frame_headroom = usr_cfg->frame_headroom;
+ cfg->flags = usr_cfg->flags;
+}
+
+static int xsk_set_xdp_socket_config(struct xsk_socket_config *cfg,
+ const struct xsk_socket_config *usr_cfg)
+{
+ if (!usr_cfg) {
+ cfg->rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS;
+ cfg->tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS;
+ cfg->libbpf_flags = 0;
+ cfg->xdp_flags = 0;
+ cfg->bind_flags = 0;
+ return 0;
+ }
+
+ if (usr_cfg->libbpf_flags & ~XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)
+ return -EINVAL;
+
+ cfg->rx_size = usr_cfg->rx_size;
+ cfg->tx_size = usr_cfg->tx_size;
+ cfg->libbpf_flags = usr_cfg->libbpf_flags;
+ cfg->xdp_flags = usr_cfg->xdp_flags;
+ cfg->bind_flags = usr_cfg->bind_flags;
+
+ return 0;
+}
+
+static void xsk_mmap_offsets_v1(struct xdp_mmap_offsets *off)
+{
+ struct xdp_mmap_offsets_v1 off_v1;
+
+ /* getsockopt on a kernel <= 5.3 has no flags fields.
+ * Copy over the offsets to the correct places in the >=5.4 format
+ * and put the flags where they would have been on that kernel.
+ */
+ memcpy(&off_v1, off, sizeof(off_v1));
+
+ off->rx.producer = off_v1.rx.producer;
+ off->rx.consumer = off_v1.rx.consumer;
+ off->rx.desc = off_v1.rx.desc;
+ off->rx.flags = off_v1.rx.consumer + sizeof(__u32);
+
+ off->tx.producer = off_v1.tx.producer;
+ off->tx.consumer = off_v1.tx.consumer;
+ off->tx.desc = off_v1.tx.desc;
+ off->tx.flags = off_v1.tx.consumer + sizeof(__u32);
+
+ off->fr.producer = off_v1.fr.producer;
+ off->fr.consumer = off_v1.fr.consumer;
+ off->fr.desc = off_v1.fr.desc;
+ off->fr.flags = off_v1.fr.consumer + sizeof(__u32);
+
+ off->cr.producer = off_v1.cr.producer;
+ off->cr.consumer = off_v1.cr.consumer;
+ off->cr.desc = off_v1.cr.desc;
+ off->cr.flags = off_v1.cr.consumer + sizeof(__u32);
+}
+
+static int xsk_get_mmap_offsets(int fd, struct xdp_mmap_offsets *off)
+{
+ socklen_t optlen;
+ int err;
+
+ optlen = sizeof(*off);
+ err = getsockopt(fd, SOL_XDP, XDP_MMAP_OFFSETS, off, &optlen);
+ if (err)
+ return err;
+
+ if (optlen == sizeof(*off))
+ return 0;
+
+ if (optlen == sizeof(struct xdp_mmap_offsets_v1)) {
+ xsk_mmap_offsets_v1(off);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+int xsk_umem__create_v0_0_4(struct xsk_umem **umem_ptr, void *umem_area,
+ __u64 size, struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *usr_config)
+{
+ struct xdp_mmap_offsets off;
+ struct xdp_umem_reg mr;
+ struct xsk_umem *umem;
+ void *map;
+ int err;
+
+ if (!umem_area || !umem_ptr || !fill || !comp)
+ return -EFAULT;
+ if (!size && !xsk_page_aligned(umem_area))
+ return -EINVAL;
+
+ umem = calloc(1, sizeof(*umem));
+ if (!umem)
+ return -ENOMEM;
+
+ umem->fd = socket(AF_XDP, SOCK_RAW, 0);
+ if (umem->fd < 0) {
+ err = -errno;
+ goto out_umem_alloc;
+ }
+
+ umem->umem_area = umem_area;
+ xsk_set_umem_config(&umem->config, usr_config);
+
+ memset(&mr, 0, sizeof(mr));
+ mr.addr = (uintptr_t)umem_area;
+ mr.len = size;
+ mr.chunk_size = umem->config.frame_size;
+ mr.headroom = umem->config.frame_headroom;
+ mr.flags = umem->config.flags;
+
+ err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr));
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+ err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_FILL_RING,
+ &umem->config.fill_size,
+ sizeof(umem->config.fill_size));
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+ err = setsockopt(umem->fd, SOL_XDP, XDP_UMEM_COMPLETION_RING,
+ &umem->config.comp_size,
+ sizeof(umem->config.comp_size));
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+
+ err = xsk_get_mmap_offsets(umem->fd, &off);
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+
+ map = mmap(NULL, off.fr.desc + umem->config.fill_size * sizeof(__u64),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, umem->fd,
+ XDP_UMEM_PGOFF_FILL_RING);
+ if (map == MAP_FAILED) {
+ err = -errno;
+ goto out_socket;
+ }
+
+ umem->fill = fill;
+ fill->mask = umem->config.fill_size - 1;
+ fill->size = umem->config.fill_size;
+ fill->producer = map + off.fr.producer;
+ fill->consumer = map + off.fr.consumer;
+ fill->flags = map + off.fr.flags;
+ fill->ring = map + off.fr.desc;
+ fill->cached_cons = umem->config.fill_size;
+
+ map = mmap(NULL, off.cr.desc + umem->config.comp_size * sizeof(__u64),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, umem->fd,
+ XDP_UMEM_PGOFF_COMPLETION_RING);
+ if (map == MAP_FAILED) {
+ err = -errno;
+ goto out_mmap;
+ }
+
+ umem->comp = comp;
+ comp->mask = umem->config.comp_size - 1;
+ comp->size = umem->config.comp_size;
+ comp->producer = map + off.cr.producer;
+ comp->consumer = map + off.cr.consumer;
+ comp->flags = map + off.cr.flags;
+ comp->ring = map + off.cr.desc;
+
+ *umem_ptr = umem;
+ return 0;
+
+out_mmap:
+ munmap(map, off.fr.desc + umem->config.fill_size * sizeof(__u64));
+out_socket:
+ close(umem->fd);
+out_umem_alloc:
+ free(umem);
+ return err;
+}
+
+struct xsk_umem_config_v1 {
+ __u32 fill_size;
+ __u32 comp_size;
+ __u32 frame_size;
+ __u32 frame_headroom;
+};
+
+int xsk_umem__create_v0_0_2(struct xsk_umem **umem_ptr, void *umem_area,
+ __u64 size, struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *usr_config)
+{
+ struct xsk_umem_config config;
+
+ memcpy(&config, usr_config, sizeof(struct xsk_umem_config_v1));
+ config.flags = 0;
+
+ return xsk_umem__create_v0_0_4(umem_ptr, umem_area, size, fill, comp,
+ &config);
+}
+COMPAT_VERSION(xsk_umem__create_v0_0_2, xsk_umem__create, LIBBPF_0.0.2)
+DEFAULT_VERSION(xsk_umem__create_v0_0_4, xsk_umem__create, LIBBPF_0.0.4)
+
+static int xsk_load_xdp_prog(struct xsk_socket *xsk)
+{
+ static const int log_buf_size = 16 * 1024;
+ char log_buf[log_buf_size];
+ int err, prog_fd;
+
+ /* This is the C-program:
+ * SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
+ * {
+ * int ret, index = ctx->rx_queue_index;
+ *
+ * // A set entry here means that the correspnding queue_id
+ * // has an active AF_XDP socket bound to it.
+ * ret = bpf_redirect_map(&xsks_map, index, XDP_PASS);
+ * if (ret > 0)
+ * return ret;
+ *
+ * // Fallback for pre-5.3 kernels, not supporting default
+ * // action in the flags parameter.
+ * if (bpf_map_lookup_elem(&xsks_map, &index))
+ * return bpf_redirect_map(&xsks_map, index, 0);
+ * return XDP_PASS;
+ * }
+ */
+ struct bpf_insn prog[] = {
+ /* r2 = *(u32 *)(r1 + 16) */
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 16),
+ /* *(u32 *)(r10 - 4) = r2 */
+ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_2, -4),
+ /* r1 = xskmap[] */
+ BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
+ /* r3 = XDP_PASS */
+ BPF_MOV64_IMM(BPF_REG_3, 2),
+ /* call bpf_redirect_map */
+ BPF_EMIT_CALL(BPF_FUNC_redirect_map),
+ /* if w0 != 0 goto pc+13 */
+ BPF_JMP32_IMM(BPF_JSGT, BPF_REG_0, 0, 13),
+ /* r2 = r10 */
+ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+ /* r2 += -4 */
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
+ /* r1 = xskmap[] */
+ BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
+ /* call bpf_map_lookup_elem */
+ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
+ /* r1 = r0 */
+ BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+ /* r0 = XDP_PASS */
+ BPF_MOV64_IMM(BPF_REG_0, 2),
+ /* if r1 == 0 goto pc+5 */
+ BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 5),
+ /* r2 = *(u32 *)(r10 - 4) */
+ BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10, -4),
+ /* r1 = xskmap[] */
+ BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd),
+ /* r3 = 0 */
+ BPF_MOV64_IMM(BPF_REG_3, 0),
+ /* call bpf_redirect_map */
+ BPF_EMIT_CALL(BPF_FUNC_redirect_map),
+ /* The jumps are to this instruction */
+ BPF_EXIT_INSN(),
+ };
+ size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
+
+ prog_fd = bpf_load_program(BPF_PROG_TYPE_XDP, prog, insns_cnt,
+ "LGPL-2.1 or BSD-2-Clause", 0, log_buf,
+ log_buf_size);
+ if (prog_fd < 0) {
+ pr_warn("BPF log buffer:\n%s", log_buf);
+ return prog_fd;
+ }
+
+ err = bpf_set_link_xdp_fd(xsk->ifindex, prog_fd, xsk->config.xdp_flags);
+ if (err) {
+ close(prog_fd);
+ return err;
+ }
+
+ xsk->prog_fd = prog_fd;
+ return 0;
+}
+
+static int xsk_get_max_queues(struct xsk_socket *xsk)
+{
+ struct ethtool_channels channels = { .cmd = ETHTOOL_GCHANNELS };
+ struct ifreq ifr = {};
+ int fd, err, ret;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -errno;
+
+ ifr.ifr_data = (void *)&channels;
+ memcpy(ifr.ifr_name, xsk->ifname, IFNAMSIZ - 1);
+ ifr.ifr_name[IFNAMSIZ - 1] = '\0';
+ err = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (err && errno != EOPNOTSUPP) {
+ ret = -errno;
+ goto out;
+ }
+
+ if (err) {
+ /* If the device says it has no channels, then all traffic
+ * is sent to a single stream, so max queues = 1.
+ */
+ ret = 1;
+ } else {
+ /* Take the max of rx, tx, combined. Drivers return
+ * the number of channels in different ways.
+ */
+ ret = max(channels.max_rx, channels.max_tx);
+ ret = max(ret, (int)channels.max_combined);
+ }
+
+out:
+ close(fd);
+ return ret;
+}
+
+static int xsk_create_bpf_maps(struct xsk_socket *xsk)
+{
+ int max_queues;
+ int fd;
+
+ max_queues = xsk_get_max_queues(xsk);
+ if (max_queues < 0)
+ return max_queues;
+
+ fd = bpf_create_map_name(BPF_MAP_TYPE_XSKMAP, "xsks_map",
+ sizeof(int), sizeof(int), max_queues, 0);
+ if (fd < 0)
+ return fd;
+
+ xsk->xsks_map_fd = fd;
+
+ return 0;
+}
+
+static void xsk_delete_bpf_maps(struct xsk_socket *xsk)
+{
+ bpf_map_delete_elem(xsk->xsks_map_fd, &xsk->queue_id);
+ close(xsk->xsks_map_fd);
+}
+
+static int xsk_lookup_bpf_maps(struct xsk_socket *xsk)
+{
+ __u32 i, *map_ids, num_maps, prog_len = sizeof(struct bpf_prog_info);
+ __u32 map_len = sizeof(struct bpf_map_info);
+ struct bpf_prog_info prog_info = {};
+ struct bpf_map_info map_info;
+ int fd, err;
+
+ err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len);
+ if (err)
+ return err;
+
+ num_maps = prog_info.nr_map_ids;
+
+ map_ids = calloc(prog_info.nr_map_ids, sizeof(*map_ids));
+ if (!map_ids)
+ return -ENOMEM;
+
+ memset(&prog_info, 0, prog_len);
+ prog_info.nr_map_ids = num_maps;
+ prog_info.map_ids = (__u64)(unsigned long)map_ids;
+
+ err = bpf_obj_get_info_by_fd(xsk->prog_fd, &prog_info, &prog_len);
+ if (err)
+ goto out_map_ids;
+
+ xsk->xsks_map_fd = -1;
+
+ for (i = 0; i < prog_info.nr_map_ids; i++) {
+ fd = bpf_map_get_fd_by_id(map_ids[i]);
+ if (fd < 0)
+ continue;
+
+ err = bpf_obj_get_info_by_fd(fd, &map_info, &map_len);
+ if (err) {
+ close(fd);
+ continue;
+ }
+
+ if (!strcmp(map_info.name, "xsks_map")) {
+ xsk->xsks_map_fd = fd;
+ continue;
+ }
+
+ close(fd);
+ }
+
+ err = 0;
+ if (xsk->xsks_map_fd == -1)
+ err = -ENOENT;
+
+out_map_ids:
+ free(map_ids);
+ return err;
+}
+
+static int xsk_set_bpf_maps(struct xsk_socket *xsk)
+{
+ return bpf_map_update_elem(xsk->xsks_map_fd, &xsk->queue_id,
+ &xsk->fd, 0);
+}
+
+static int xsk_setup_xdp_prog(struct xsk_socket *xsk)
+{
+ __u32 prog_id = 0;
+ int err;
+
+ err = bpf_get_link_xdp_id(xsk->ifindex, &prog_id,
+ xsk->config.xdp_flags);
+ if (err)
+ return err;
+
+ if (!prog_id) {
+ err = xsk_create_bpf_maps(xsk);
+ if (err)
+ return err;
+
+ err = xsk_load_xdp_prog(xsk);
+ if (err) {
+ xsk_delete_bpf_maps(xsk);
+ return err;
+ }
+ } else {
+ xsk->prog_fd = bpf_prog_get_fd_by_id(prog_id);
+ if (xsk->prog_fd < 0)
+ return -errno;
+ err = xsk_lookup_bpf_maps(xsk);
+ if (err) {
+ close(xsk->prog_fd);
+ return err;
+ }
+ }
+
+ if (xsk->rx)
+ err = xsk_set_bpf_maps(xsk);
+ if (err) {
+ xsk_delete_bpf_maps(xsk);
+ close(xsk->prog_fd);
+ return err;
+ }
+
+ return 0;
+}
+
+int xsk_socket__create(struct xsk_socket **xsk_ptr, const char *ifname,
+ __u32 queue_id, struct xsk_umem *umem,
+ struct xsk_ring_cons *rx, struct xsk_ring_prod *tx,
+ const struct xsk_socket_config *usr_config)
+{
+ void *rx_map = NULL, *tx_map = NULL;
+ struct sockaddr_xdp sxdp = {};
+ struct xdp_mmap_offsets off;
+ struct xsk_socket *xsk;
+ int err;
+
+ if (!umem || !xsk_ptr || !(rx || tx))
+ return -EFAULT;
+
+ xsk = calloc(1, sizeof(*xsk));
+ if (!xsk)
+ return -ENOMEM;
+
+ err = xsk_set_xdp_socket_config(&xsk->config, usr_config);
+ if (err)
+ goto out_xsk_alloc;
+
+ if (umem->refcount &&
+ !(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) {
+ pr_warn("Error: shared umems not supported by libbpf supplied XDP program.\n");
+ err = -EBUSY;
+ goto out_xsk_alloc;
+ }
+
+ if (umem->refcount++ > 0) {
+ xsk->fd = socket(AF_XDP, SOCK_RAW, 0);
+ if (xsk->fd < 0) {
+ err = -errno;
+ goto out_xsk_alloc;
+ }
+ } else {
+ xsk->fd = umem->fd;
+ }
+
+ xsk->outstanding_tx = 0;
+ xsk->queue_id = queue_id;
+ xsk->umem = umem;
+ xsk->ifindex = if_nametoindex(ifname);
+ if (!xsk->ifindex) {
+ err = -errno;
+ goto out_socket;
+ }
+ memcpy(xsk->ifname, ifname, IFNAMSIZ - 1);
+ xsk->ifname[IFNAMSIZ - 1] = '\0';
+
+ if (rx) {
+ err = setsockopt(xsk->fd, SOL_XDP, XDP_RX_RING,
+ &xsk->config.rx_size,
+ sizeof(xsk->config.rx_size));
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+ }
+ if (tx) {
+ err = setsockopt(xsk->fd, SOL_XDP, XDP_TX_RING,
+ &xsk->config.tx_size,
+ sizeof(xsk->config.tx_size));
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+ }
+
+ err = xsk_get_mmap_offsets(xsk->fd, &off);
+ if (err) {
+ err = -errno;
+ goto out_socket;
+ }
+
+ if (rx) {
+ rx_map = mmap(NULL, off.rx.desc +
+ xsk->config.rx_size * sizeof(struct xdp_desc),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
+ xsk->fd, XDP_PGOFF_RX_RING);
+ if (rx_map == MAP_FAILED) {
+ err = -errno;
+ goto out_socket;
+ }
+
+ rx->mask = xsk->config.rx_size - 1;
+ rx->size = xsk->config.rx_size;
+ rx->producer = rx_map + off.rx.producer;
+ rx->consumer = rx_map + off.rx.consumer;
+ rx->flags = rx_map + off.rx.flags;
+ rx->ring = rx_map + off.rx.desc;
+ }
+ xsk->rx = rx;
+
+ if (tx) {
+ tx_map = mmap(NULL, off.tx.desc +
+ xsk->config.tx_size * sizeof(struct xdp_desc),
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
+ xsk->fd, XDP_PGOFF_TX_RING);
+ if (tx_map == MAP_FAILED) {
+ err = -errno;
+ goto out_mmap_rx;
+ }
+
+ tx->mask = xsk->config.tx_size - 1;
+ tx->size = xsk->config.tx_size;
+ tx->producer = tx_map + off.tx.producer;
+ tx->consumer = tx_map + off.tx.consumer;
+ tx->flags = tx_map + off.tx.flags;
+ tx->ring = tx_map + off.tx.desc;
+ tx->cached_cons = xsk->config.tx_size;
+ }
+ xsk->tx = tx;
+
+ sxdp.sxdp_family = PF_XDP;
+ sxdp.sxdp_ifindex = xsk->ifindex;
+ sxdp.sxdp_queue_id = xsk->queue_id;
+ if (umem->refcount > 1) {
+ sxdp.sxdp_flags = XDP_SHARED_UMEM;
+ sxdp.sxdp_shared_umem_fd = umem->fd;
+ } else {
+ sxdp.sxdp_flags = xsk->config.bind_flags;
+ }
+
+ err = bind(xsk->fd, (struct sockaddr *)&sxdp, sizeof(sxdp));
+ if (err) {
+ err = -errno;
+ goto out_mmap_tx;
+ }
+
+ xsk->prog_fd = -1;
+
+ if (!(xsk->config.libbpf_flags & XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD)) {
+ err = xsk_setup_xdp_prog(xsk);
+ if (err)
+ goto out_mmap_tx;
+ }
+
+ *xsk_ptr = xsk;
+ return 0;
+
+out_mmap_tx:
+ if (tx)
+ munmap(tx_map, off.tx.desc +
+ xsk->config.tx_size * sizeof(struct xdp_desc));
+out_mmap_rx:
+ if (rx)
+ munmap(rx_map, off.rx.desc +
+ xsk->config.rx_size * sizeof(struct xdp_desc));
+out_socket:
+ if (--umem->refcount)
+ close(xsk->fd);
+out_xsk_alloc:
+ free(xsk);
+ return err;
+}
+
+int xsk_umem__delete(struct xsk_umem *umem)
+{
+ struct xdp_mmap_offsets off;
+ int err;
+
+ if (!umem)
+ return 0;
+
+ if (umem->refcount)
+ return -EBUSY;
+
+ err = xsk_get_mmap_offsets(umem->fd, &off);
+ if (!err) {
+ munmap(umem->fill->ring - off.fr.desc,
+ off.fr.desc + umem->config.fill_size * sizeof(__u64));
+ munmap(umem->comp->ring - off.cr.desc,
+ off.cr.desc + umem->config.comp_size * sizeof(__u64));
+ }
+
+ close(umem->fd);
+ free(umem);
+
+ return 0;
+}
+
+void xsk_socket__delete(struct xsk_socket *xsk)
+{
+ size_t desc_sz = sizeof(struct xdp_desc);
+ struct xdp_mmap_offsets off;
+ int err;
+
+ if (!xsk)
+ return;
+
+ if (xsk->prog_fd != -1) {
+ xsk_delete_bpf_maps(xsk);
+ close(xsk->prog_fd);
+ }
+
+ err = xsk_get_mmap_offsets(xsk->fd, &off);
+ if (!err) {
+ if (xsk->rx) {
+ munmap(xsk->rx->ring - off.rx.desc,
+ off.rx.desc + xsk->config.rx_size * desc_sz);
+ }
+ if (xsk->tx) {
+ munmap(xsk->tx->ring - off.tx.desc,
+ off.tx.desc + xsk->config.tx_size * desc_sz);
+ }
+
+ }
+
+ xsk->umem->refcount--;
+ /* Do not close an fd that also has an associated umem connected
+ * to it.
+ */
+ if (xsk->fd != xsk->umem->fd)
+ close(xsk->fd);
+ free(xsk);
+}
diff --git a/src/contrib/libbpf/bpf/xsk.h b/src/contrib/libbpf/bpf/xsk.h
new file mode 100644
index 0000000..584f682
--- /dev/null
+++ b/src/contrib/libbpf/bpf/xsk.h
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+/*
+ * AF_XDP user-space access library.
+ *
+ * Copyright(c) 2018 - 2019 Intel Corporation.
+ *
+ * Author(s): Magnus Karlsson <magnus.karlsson@intel.com>
+ */
+
+#ifndef __LIBBPF_XSK_H
+#define __LIBBPF_XSK_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <linux/if_xdp.h>
+
+#include "libbpf.h"
+#include "libbpf_util.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Do not access these members directly. Use the functions below. */
+#define DEFINE_XSK_RING(name) \
+struct name { \
+ __u32 cached_prod; \
+ __u32 cached_cons; \
+ __u32 mask; \
+ __u32 size; \
+ __u32 *producer; \
+ __u32 *consumer; \
+ void *ring; \
+ __u32 *flags; \
+}
+
+DEFINE_XSK_RING(xsk_ring_prod);
+DEFINE_XSK_RING(xsk_ring_cons);
+
+/* For a detailed explanation on the memory barriers associated with the
+ * ring, please take a look at net/xdp/xsk_queue.h.
+ */
+
+struct xsk_umem;
+struct xsk_socket;
+
+static inline __u64 *xsk_ring_prod__fill_addr(struct xsk_ring_prod *fill,
+ __u32 idx)
+{
+ __u64 *addrs = (__u64 *)fill->ring;
+
+ return &addrs[idx & fill->mask];
+}
+
+static inline const __u64 *
+xsk_ring_cons__comp_addr(const struct xsk_ring_cons *comp, __u32 idx)
+{
+ const __u64 *addrs = (const __u64 *)comp->ring;
+
+ return &addrs[idx & comp->mask];
+}
+
+static inline struct xdp_desc *xsk_ring_prod__tx_desc(struct xsk_ring_prod *tx,
+ __u32 idx)
+{
+ struct xdp_desc *descs = (struct xdp_desc *)tx->ring;
+
+ return &descs[idx & tx->mask];
+}
+
+static inline const struct xdp_desc *
+xsk_ring_cons__rx_desc(const struct xsk_ring_cons *rx, __u32 idx)
+{
+ const struct xdp_desc *descs = (const struct xdp_desc *)rx->ring;
+
+ return &descs[idx & rx->mask];
+}
+
+static inline int xsk_ring_prod__needs_wakeup(const struct xsk_ring_prod *r)
+{
+ return *r->flags & XDP_RING_NEED_WAKEUP;
+}
+
+static inline __u32 xsk_prod_nb_free(struct xsk_ring_prod *r, __u32 nb)
+{
+ __u32 free_entries = r->cached_cons - r->cached_prod;
+
+ if (free_entries >= nb)
+ return free_entries;
+
+ /* Refresh the local tail pointer.
+ * cached_cons is r->size bigger than the real consumer pointer so
+ * that this addition can be avoided in the more frequently
+ * executed code that computs free_entries in the beginning of
+ * this function. Without this optimization it whould have been
+ * free_entries = r->cached_prod - r->cached_cons + r->size.
+ */
+ r->cached_cons = *r->consumer + r->size;
+
+ return r->cached_cons - r->cached_prod;
+}
+
+static inline __u32 xsk_cons_nb_avail(struct xsk_ring_cons *r, __u32 nb)
+{
+ __u32 entries = r->cached_prod - r->cached_cons;
+
+ if (entries == 0) {
+ r->cached_prod = *r->producer;
+ entries = r->cached_prod - r->cached_cons;
+ }
+
+ return (entries > nb) ? nb : entries;
+}
+
+static inline size_t xsk_ring_prod__reserve(struct xsk_ring_prod *prod,
+ size_t nb, __u32 *idx)
+{
+ if (xsk_prod_nb_free(prod, nb) < nb)
+ return 0;
+
+ *idx = prod->cached_prod;
+ prod->cached_prod += nb;
+
+ return nb;
+}
+
+static inline void xsk_ring_prod__submit(struct xsk_ring_prod *prod, size_t nb)
+{
+ /* Make sure everything has been written to the ring before indicating
+ * this to the kernel by writing the producer pointer.
+ */
+ libbpf_smp_wmb();
+
+ *prod->producer += nb;
+}
+
+static inline size_t xsk_ring_cons__peek(struct xsk_ring_cons *cons,
+ size_t nb, __u32 *idx)
+{
+ size_t entries = xsk_cons_nb_avail(cons, nb);
+
+ if (entries > 0) {
+ /* Make sure we do not speculatively read the data before
+ * we have received the packet buffers from the ring.
+ */
+ libbpf_smp_rmb();
+
+ *idx = cons->cached_cons;
+ cons->cached_cons += entries;
+ }
+
+ return entries;
+}
+
+static inline void xsk_ring_cons__release(struct xsk_ring_cons *cons, size_t nb)
+{
+ /* Make sure data has been read before indicating we are done
+ * with the entries by updating the consumer pointer.
+ */
+ libbpf_smp_rwmb();
+
+ *cons->consumer += nb;
+}
+
+static inline void *xsk_umem__get_data(void *umem_area, __u64 addr)
+{
+ return &((char *)umem_area)[addr];
+}
+
+static inline __u64 xsk_umem__extract_addr(__u64 addr)
+{
+ return addr & XSK_UNALIGNED_BUF_ADDR_MASK;
+}
+
+static inline __u64 xsk_umem__extract_offset(__u64 addr)
+{
+ return addr >> XSK_UNALIGNED_BUF_OFFSET_SHIFT;
+}
+
+static inline __u64 xsk_umem__add_offset_to_addr(__u64 addr)
+{
+ return xsk_umem__extract_addr(addr) + xsk_umem__extract_offset(addr);
+}
+
+LIBBPF_API int xsk_umem__fd(const struct xsk_umem *umem);
+LIBBPF_API int xsk_socket__fd(const struct xsk_socket *xsk);
+
+#define XSK_RING_CONS__DEFAULT_NUM_DESCS 2048
+#define XSK_RING_PROD__DEFAULT_NUM_DESCS 2048
+#define XSK_UMEM__DEFAULT_FRAME_SHIFT 12 /* 4096 bytes */
+#define XSK_UMEM__DEFAULT_FRAME_SIZE (1 << XSK_UMEM__DEFAULT_FRAME_SHIFT)
+#define XSK_UMEM__DEFAULT_FRAME_HEADROOM 0
+#define XSK_UMEM__DEFAULT_FLAGS 0
+
+struct xsk_umem_config {
+ __u32 fill_size;
+ __u32 comp_size;
+ __u32 frame_size;
+ __u32 frame_headroom;
+ __u32 flags;
+};
+
+/* Flags for the libbpf_flags field. */
+#define XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD (1 << 0)
+
+struct xsk_socket_config {
+ __u32 rx_size;
+ __u32 tx_size;
+ __u32 libbpf_flags;
+ __u32 xdp_flags;
+ __u16 bind_flags;
+};
+
+/* Set config to NULL to get the default configuration. */
+LIBBPF_API int xsk_umem__create(struct xsk_umem **umem,
+ void *umem_area, __u64 size,
+ struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *config);
+LIBBPF_API int xsk_umem__create_v0_0_2(struct xsk_umem **umem,
+ void *umem_area, __u64 size,
+ struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *config);
+LIBBPF_API int xsk_umem__create_v0_0_4(struct xsk_umem **umem,
+ void *umem_area, __u64 size,
+ struct xsk_ring_prod *fill,
+ struct xsk_ring_cons *comp,
+ const struct xsk_umem_config *config);
+LIBBPF_API int xsk_socket__create(struct xsk_socket **xsk,
+ const char *ifname, __u32 queue_id,
+ struct xsk_umem *umem,
+ struct xsk_ring_cons *rx,
+ struct xsk_ring_prod *tx,
+ const struct xsk_socket_config *config);
+
+/* Returns 0 for success and -EBUSY if the umem is still in use. */
+LIBBPF_API int xsk_umem__delete(struct xsk_umem *umem);
+LIBBPF_API void xsk_socket__delete(struct xsk_socket *xsk);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __LIBBPF_XSK_H */
diff --git a/src/contrib/libbpf/include/asm/barrier.h b/src/contrib/libbpf/include/asm/barrier.h
new file mode 100644
index 0000000..1fc6aee
--- /dev/null
+++ b/src/contrib/libbpf/include/asm/barrier.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef __ASM_BARRIER_H
+#define __ASM_BARRIER_H
+
+#include <linux/compiler.h>
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/compiler.h b/src/contrib/libbpf/include/linux/compiler.h
new file mode 100644
index 0000000..26336dc
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/compiler.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_COMPILER_H
+#define __LINUX_COMPILER_H
+
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+
+#define READ_ONCE(x) (*(volatile typeof(x) *)&x)
+#define WRITE_ONCE(x, v) (*(volatile typeof(x) *)&x) = (v)
+
+#define barrier() asm volatile("" ::: "memory")
+
+#if defined(__x86_64__)
+
+# define smp_rmb() barrier()
+# define smp_wmb() barrier()
+# define smp_mb() asm volatile("lock; addl $0,-132(%%rsp)" ::: "memory", "cc")
+
+# define smp_store_release(p, v) \
+do { \
+ barrier(); \
+ WRITE_ONCE(*p, v); \
+} while (0)
+
+# define smp_load_acquire(p) \
+({ \
+ typeof(*p) ___p = READ_ONCE(*p); \
+ barrier(); \
+ ___p; \
+})
+
+#elif defined(__aarch64__)
+
+# define smp_rmb() asm volatile("dmb ishld" ::: "memory")
+# define smp_wmb() asm volatile("dmb ishst" ::: "memory")
+# define smp_mb() asm volatile("dmb ish" ::: "memory")
+
+#endif
+
+#ifndef smp_mb
+# define smp_mb() __sync_synchronize()
+#endif
+
+#ifndef smp_rmb
+# define smp_rmb() smp_mb()
+#endif
+
+#ifndef smp_wmb
+# define smp_wmb() smp_mb()
+#endif
+
+#ifndef smp_store_release
+# define smp_store_release(p, v) \
+do { \
+ smp_mb(); \
+ WRITE_ONCE(*p, v); \
+} while (0)
+#endif
+
+#ifndef smp_load_acquire
+# define smp_load_acquire(p) \
+({ \
+ typeof(*p) ___p = READ_ONCE(*p); \
+ smp_mb(); \
+ ___p; \
+})
+#endif
+
+#endif /* __LINUX_COMPILER_H */
diff --git a/src/contrib/libbpf/include/linux/err.h b/src/contrib/libbpf/include/linux/err.h
new file mode 100644
index 0000000..1b1dafb
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/err.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_ERR_H
+#define __LINUX_ERR_H
+
+#include <linux/types.h>
+#include <asm/errno.h>
+
+#define MAX_ERRNO 4095
+
+#define IS_ERR_VALUE(x) ((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void * ERR_PTR(long error_)
+{
+ return (void *) error_;
+}
+
+static inline long PTR_ERR(const void *ptr)
+{
+ return (long) ptr;
+}
+
+static inline bool IS_ERR(const void *ptr)
+{
+ return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+ return (!ptr) || IS_ERR_VALUE((unsigned long)ptr);
+}
+
+static inline long PTR_ERR_OR_ZERO(const void *ptr)
+{
+ return IS_ERR(ptr) ? PTR_ERR(ptr) : 0;
+}
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/filter.h b/src/contrib/libbpf/include/linux/filter.h
new file mode 100644
index 0000000..b0700e2
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/filter.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_FILTER_H
+#define __LINUX_FILTER_H
+
+#include <linux/bpf.h>
+
+#define BPF_ALU64_IMM(OP, DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_MOV64_IMM(DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_EXIT_INSN() \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_EXIT, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = 0 })
+
+#define BPF_EMIT_CALL(FUNC) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_CALL, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = ((FUNC) - BPF_FUNC_unspec) })
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+#define BPF_MOV64_REG(DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+#define BPF_MOV32_IMM(DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_MOV | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_LD_IMM64_RAW_FULL(DST, SRC, OFF1, OFF2, IMM1, IMM2) \
+ ((struct bpf_insn) { \
+ .code = BPF_LD | BPF_DW | BPF_IMM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF1, \
+ .imm = IMM1 }), \
+ ((struct bpf_insn) { \
+ .code = 0, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = OFF2, \
+ .imm = IMM2 })
+
+#define BPF_LD_MAP_FD(DST, MAP_FD) \
+ BPF_LD_IMM64_RAW_FULL(DST, BPF_PSEUDO_MAP_FD, 0, 0, \
+ MAP_FD, 0)
+
+#define BPF_LD_MAP_VALUE(DST, MAP_FD, VALUE_OFF) \
+ BPF_LD_IMM64_RAW_FULL(DST, BPF_PSEUDO_MAP_VALUE, 0, 0, \
+ MAP_FD, VALUE_OFF)
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/kernel.h b/src/contrib/libbpf/include/linux/kernel.h
new file mode 100644
index 0000000..a4a7a9d
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/kernel.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_KERNEL_H
+#define __LINUX_KERNEL_H
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#ifndef max
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef roundup
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+#endif
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/list.h b/src/contrib/libbpf/include/linux/list.h
new file mode 100644
index 0000000..e3814f7
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/list.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_LIST_H
+#define __LINUX_LIST_H
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define POISON_POINTER_DELTA 0
+#define LIST_POISON1 ((void *) 0x100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x200 + POISON_POINTER_DELTA)
+
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+ list->next = list;
+ list->prev = list;
+}
+
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void __list_del_entry(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+#define list_first_entry(ptr, type, member) \
+ list_entry((ptr)->next, type, member)
+#define list_next_entry(pos, member) \
+ list_entry((pos)->member.next, typeof(*(pos)), member)
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/overflow.h b/src/contrib/libbpf/include/linux/overflow.h
new file mode 100644
index 0000000..53d7580
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/overflow.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_OVERFLOW_H
+#define __LINUX_OVERFLOW_H
+
+#define is_signed_type(type) (((type)(-1)) < (type)1)
+#define __type_half_max(type) ((type)1 << (8*sizeof(type) - 1 - is_signed_type(type)))
+#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
+#define type_min(T) ((T)((T)-type_max(T)-(T)1))
+
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+#if GCC_VERSION >= 50100
+#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1
+#endif
+#endif
+
+#ifdef COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW
+
+#define check_mul_overflow(a, b, d) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ typeof(d) __d = (d); \
+ (void) (&__a == &__b); \
+ (void) (&__a == __d); \
+ __builtin_mul_overflow(__a, __b, __d); \
+})
+
+#else
+
+/*
+ * If one of a or b is a compile-time constant, this avoids a division.
+ */
+#define __unsigned_mul_overflow(a, b, d) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ typeof(d) __d = (d); \
+ (void) (&__a == &__b); \
+ (void) (&__a == __d); \
+ *__d = __a * __b; \
+ __builtin_constant_p(__b) ? \
+ __b > 0 && __a > type_max(typeof(__a)) / __b : \
+ __a > 0 && __b > type_max(typeof(__b)) / __a; \
+})
+
+/*
+ * Signed multiplication is rather hard. gcc always follows C99, so
+ * division is truncated towards 0. This means that we can write the
+ * overflow check like this:
+ *
+ * (a > 0 && (b > MAX/a || b < MIN/a)) ||
+ * (a < -1 && (b > MIN/a || b < MAX/a) ||
+ * (a == -1 && b == MIN)
+ *
+ * The redundant casts of -1 are to silence an annoying -Wtype-limits
+ * (included in -Wextra) warning: When the type is u8 or u16, the
+ * __b_c_e in check_mul_overflow obviously selects
+ * __unsigned_mul_overflow, but unfortunately gcc still parses this
+ * code and warns about the limited range of __b.
+ */
+
+#define __signed_mul_overflow(a, b, d) ({ \
+ typeof(a) __a = (a); \
+ typeof(b) __b = (b); \
+ typeof(d) __d = (d); \
+ typeof(a) __tmax = type_max(typeof(a)); \
+ typeof(a) __tmin = type_min(typeof(a)); \
+ (void) (&__a == &__b); \
+ (void) (&__a == __d); \
+ *__d = (__u64)__a * (__u64)__b; \
+ (__b > 0 && (__a > __tmax/__b || __a < __tmin/__b)) || \
+ (__b < (typeof(__b))-1 && (__a > __tmin/__b || __a < __tmax/__b)) || \
+ (__b == (typeof(__b))-1 && __a == __tmin); \
+})
+
+#define check_mul_overflow(a, b, d) \
+ __builtin_choose_expr(is_signed_type(typeof(a)), \
+ __signed_mul_overflow(a, b, d), \
+ __unsigned_mul_overflow(a, b, d))
+
+
+#endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */
+
+#endif
diff --git a/src/contrib/libbpf/include/linux/ring_buffer.h b/src/contrib/libbpf/include/linux/ring_buffer.h
new file mode 100644
index 0000000..fc4677b
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/ring_buffer.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+#ifndef _TOOLS_LINUX_RING_BUFFER_H_
+#define _TOOLS_LINUX_RING_BUFFER_H_
+
+#include <linux/compiler.h>
+
+static inline __u64 ring_buffer_read_head(struct perf_event_mmap_page *base)
+{
+ return smp_load_acquire(&base->data_head);
+}
+
+static inline void ring_buffer_write_tail(struct perf_event_mmap_page *base,
+ __u64 tail)
+{
+ smp_store_release(&base->data_tail, tail);
+}
+
+#endif /* _TOOLS_LINUX_RING_BUFFER_H_ */
diff --git a/src/contrib/libbpf/include/linux/types.h b/src/contrib/libbpf/include/linux/types.h
new file mode 100644
index 0000000..bae1ed8
--- /dev/null
+++ b/src/contrib/libbpf/include/linux/types.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+
+#ifndef __LINUX_TYPES_H
+#define __LINUX_TYPES_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <asm/types.h>
+#include <asm/posix_types.h>
+
+#define __bitwise__
+#define __bitwise __bitwise__
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+#ifndef __aligned_u64
+# define __aligned_u64 __u64 __attribute__((aligned(8)))
+#endif
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#endif
diff --git a/src/contrib/libbpf/include/uapi/linux/bpf.h b/src/contrib/libbpf/include/uapi/linux/bpf.h
new file mode 100644
index 0000000..dbbcf0b
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/bpf.h
@@ -0,0 +1,3692 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#ifndef _UAPI__LINUX_BPF_H__
+#define _UAPI__LINUX_BPF_H__
+
+#include <linux/types.h>
+#include <linux/bpf_common.h>
+
+/* Extended instruction set based on top of classic BPF */
+
+/* instruction classes */
+#define BPF_JMP32 0x06 /* jmp mode in word width */
+#define BPF_ALU64 0x07 /* alu mode in double word width */
+
+/* ld/ldx fields */
+#define BPF_DW 0x18 /* double word (64-bit) */
+#define BPF_XADD 0xc0 /* exclusive add */
+
+/* alu/jmp fields */
+#define BPF_MOV 0xb0 /* mov reg to reg */
+#define BPF_ARSH 0xc0 /* sign extending arithmetic shift right */
+
+/* change endianness of a register */
+#define BPF_END 0xd0 /* flags for endianness conversion: */
+#define BPF_TO_LE 0x00 /* convert to little-endian */
+#define BPF_TO_BE 0x08 /* convert to big-endian */
+#define BPF_FROM_LE BPF_TO_LE
+#define BPF_FROM_BE BPF_TO_BE
+
+/* jmp encodings */
+#define BPF_JNE 0x50 /* jump != */
+#define BPF_JLT 0xa0 /* LT is unsigned, '<' */
+#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */
+#define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */
+#define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */
+#define BPF_JSLT 0xc0 /* SLT is signed, '<' */
+#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */
+#define BPF_CALL 0x80 /* function call */
+#define BPF_EXIT 0x90 /* function return */
+
+/* Register numbers */
+enum {
+ BPF_REG_0 = 0,
+ BPF_REG_1,
+ BPF_REG_2,
+ BPF_REG_3,
+ BPF_REG_4,
+ BPF_REG_5,
+ BPF_REG_6,
+ BPF_REG_7,
+ BPF_REG_8,
+ BPF_REG_9,
+ BPF_REG_10,
+ __MAX_BPF_REG,
+};
+
+/* BPF has 10 general purpose 64-bit registers and stack frame. */
+#define MAX_BPF_REG __MAX_BPF_REG
+
+struct bpf_insn {
+ __u8 code; /* opcode */
+ __u8 dst_reg:4; /* dest register */
+ __u8 src_reg:4; /* source register */
+ __s16 off; /* signed offset */
+ __s32 imm; /* signed immediate constant */
+};
+
+/* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */
+struct bpf_lpm_trie_key {
+ __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */
+ __u8 data[0]; /* Arbitrary size */
+};
+
+struct bpf_cgroup_storage_key {
+ __u64 cgroup_inode_id; /* cgroup inode id */
+ __u32 attach_type; /* program attach type */
+};
+
+/* BPF syscall commands, see bpf(2) man-page for details. */
+enum bpf_cmd {
+ BPF_MAP_CREATE,
+ BPF_MAP_LOOKUP_ELEM,
+ BPF_MAP_UPDATE_ELEM,
+ BPF_MAP_DELETE_ELEM,
+ BPF_MAP_GET_NEXT_KEY,
+ BPF_PROG_LOAD,
+ BPF_OBJ_PIN,
+ BPF_OBJ_GET,
+ BPF_PROG_ATTACH,
+ BPF_PROG_DETACH,
+ BPF_PROG_TEST_RUN,
+ BPF_PROG_GET_NEXT_ID,
+ BPF_MAP_GET_NEXT_ID,
+ BPF_PROG_GET_FD_BY_ID,
+ BPF_MAP_GET_FD_BY_ID,
+ BPF_OBJ_GET_INFO_BY_FD,
+ BPF_PROG_QUERY,
+ BPF_RAW_TRACEPOINT_OPEN,
+ BPF_BTF_LOAD,
+ BPF_BTF_GET_FD_BY_ID,
+ BPF_TASK_FD_QUERY,
+ BPF_MAP_LOOKUP_AND_DELETE_ELEM,
+ BPF_MAP_FREEZE,
+ BPF_BTF_GET_NEXT_ID,
+};
+
+enum bpf_map_type {
+ BPF_MAP_TYPE_UNSPEC,
+ BPF_MAP_TYPE_HASH,
+ BPF_MAP_TYPE_ARRAY,
+ BPF_MAP_TYPE_PROG_ARRAY,
+ BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+ BPF_MAP_TYPE_PERCPU_HASH,
+ BPF_MAP_TYPE_PERCPU_ARRAY,
+ BPF_MAP_TYPE_STACK_TRACE,
+ BPF_MAP_TYPE_CGROUP_ARRAY,
+ BPF_MAP_TYPE_LRU_HASH,
+ BPF_MAP_TYPE_LRU_PERCPU_HASH,
+ BPF_MAP_TYPE_LPM_TRIE,
+ BPF_MAP_TYPE_ARRAY_OF_MAPS,
+ BPF_MAP_TYPE_HASH_OF_MAPS,
+ BPF_MAP_TYPE_DEVMAP,
+ BPF_MAP_TYPE_SOCKMAP,
+ BPF_MAP_TYPE_CPUMAP,
+ BPF_MAP_TYPE_XSKMAP,
+ BPF_MAP_TYPE_SOCKHASH,
+ BPF_MAP_TYPE_CGROUP_STORAGE,
+ BPF_MAP_TYPE_REUSEPORT_SOCKARRAY,
+ BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
+ BPF_MAP_TYPE_QUEUE,
+ BPF_MAP_TYPE_STACK,
+ BPF_MAP_TYPE_SK_STORAGE,
+ BPF_MAP_TYPE_DEVMAP_HASH,
+};
+
+/* Note that tracing related programs such as
+ * BPF_PROG_TYPE_{KPROBE,TRACEPOINT,PERF_EVENT,RAW_TRACEPOINT}
+ * are not subject to a stable API since kernel internal data
+ * structures can change from release to release and may
+ * therefore break existing tracing BPF programs. Tracing BPF
+ * programs correspond to /a/ specific kernel which is to be
+ * analyzed, and not /a/ specific kernel /and/ all future ones.
+ */
+enum bpf_prog_type {
+ BPF_PROG_TYPE_UNSPEC,
+ BPF_PROG_TYPE_SOCKET_FILTER,
+ BPF_PROG_TYPE_KPROBE,
+ BPF_PROG_TYPE_SCHED_CLS,
+ BPF_PROG_TYPE_SCHED_ACT,
+ BPF_PROG_TYPE_TRACEPOINT,
+ BPF_PROG_TYPE_XDP,
+ BPF_PROG_TYPE_PERF_EVENT,
+ BPF_PROG_TYPE_CGROUP_SKB,
+ BPF_PROG_TYPE_CGROUP_SOCK,
+ BPF_PROG_TYPE_LWT_IN,
+ BPF_PROG_TYPE_LWT_OUT,
+ BPF_PROG_TYPE_LWT_XMIT,
+ BPF_PROG_TYPE_SOCK_OPS,
+ BPF_PROG_TYPE_SK_SKB,
+ BPF_PROG_TYPE_CGROUP_DEVICE,
+ BPF_PROG_TYPE_SK_MSG,
+ BPF_PROG_TYPE_RAW_TRACEPOINT,
+ BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_PROG_TYPE_LWT_SEG6LOCAL,
+ BPF_PROG_TYPE_LIRC_MODE2,
+ BPF_PROG_TYPE_SK_REUSEPORT,
+ BPF_PROG_TYPE_FLOW_DISSECTOR,
+ BPF_PROG_TYPE_CGROUP_SYSCTL,
+ BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE,
+ BPF_PROG_TYPE_CGROUP_SOCKOPT,
+ BPF_PROG_TYPE_TRACING,
+};
+
+enum bpf_attach_type {
+ BPF_CGROUP_INET_INGRESS,
+ BPF_CGROUP_INET_EGRESS,
+ BPF_CGROUP_INET_SOCK_CREATE,
+ BPF_CGROUP_SOCK_OPS,
+ BPF_SK_SKB_STREAM_PARSER,
+ BPF_SK_SKB_STREAM_VERDICT,
+ BPF_CGROUP_DEVICE,
+ BPF_SK_MSG_VERDICT,
+ BPF_CGROUP_INET4_BIND,
+ BPF_CGROUP_INET6_BIND,
+ BPF_CGROUP_INET4_CONNECT,
+ BPF_CGROUP_INET6_CONNECT,
+ BPF_CGROUP_INET4_POST_BIND,
+ BPF_CGROUP_INET6_POST_BIND,
+ BPF_CGROUP_UDP4_SENDMSG,
+ BPF_CGROUP_UDP6_SENDMSG,
+ BPF_LIRC_MODE2,
+ BPF_FLOW_DISSECTOR,
+ BPF_CGROUP_SYSCTL,
+ BPF_CGROUP_UDP4_RECVMSG,
+ BPF_CGROUP_UDP6_RECVMSG,
+ BPF_CGROUP_GETSOCKOPT,
+ BPF_CGROUP_SETSOCKOPT,
+ BPF_TRACE_RAW_TP,
+ BPF_TRACE_FENTRY,
+ BPF_TRACE_FEXIT,
+ __MAX_BPF_ATTACH_TYPE
+};
+
+#define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE
+
+/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command
+ *
+ * NONE(default): No further bpf programs allowed in the subtree.
+ *
+ * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program,
+ * the program in this cgroup yields to sub-cgroup program.
+ *
+ * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program,
+ * that cgroup program gets run in addition to the program in this cgroup.
+ *
+ * Only one program is allowed to be attached to a cgroup with
+ * NONE or BPF_F_ALLOW_OVERRIDE flag.
+ * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will
+ * release old program and attach the new one. Attach flags has to match.
+ *
+ * Multiple programs are allowed to be attached to a cgroup with
+ * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order
+ * (those that were attached first, run first)
+ * The programs of sub-cgroup are executed first, then programs of
+ * this cgroup and then programs of parent cgroup.
+ * When children program makes decision (like picking TCP CA or sock bind)
+ * parent program has a chance to override it.
+ *
+ * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups.
+ * A cgroup with NONE doesn't allow any programs in sub-cgroups.
+ * Ex1:
+ * cgrp1 (MULTI progs A, B) ->
+ * cgrp2 (OVERRIDE prog C) ->
+ * cgrp3 (MULTI prog D) ->
+ * cgrp4 (OVERRIDE prog E) ->
+ * cgrp5 (NONE prog F)
+ * the event in cgrp5 triggers execution of F,D,A,B in that order.
+ * if prog F is detached, the execution is E,D,A,B
+ * if prog F and D are detached, the execution is E,A,B
+ * if prog F, E and D are detached, the execution is C,A,B
+ *
+ * All eligible programs are executed regardless of return code from
+ * earlier programs.
+ */
+#define BPF_F_ALLOW_OVERRIDE (1U << 0)
+#define BPF_F_ALLOW_MULTI (1U << 1)
+
+/* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the
+ * verifier will perform strict alignment checking as if the kernel
+ * has been built with CONFIG_EFFICIENT_UNALIGNED_ACCESS not set,
+ * and NET_IP_ALIGN defined to 2.
+ */
+#define BPF_F_STRICT_ALIGNMENT (1U << 0)
+
+/* If BPF_F_ANY_ALIGNMENT is used in BPF_PROF_LOAD command, the
+ * verifier will allow any alignment whatsoever. On platforms
+ * with strict alignment requirements for loads ands stores (such
+ * as sparc and mips) the verifier validates that all loads and
+ * stores provably follow this requirement. This flag turns that
+ * checking and enforcement off.
+ *
+ * It is mostly used for testing when we want to validate the
+ * context and memory access aspects of the verifier, but because
+ * of an unaligned access the alignment check would trigger before
+ * the one we are interested in.
+ */
+#define BPF_F_ANY_ALIGNMENT (1U << 1)
+
+/* BPF_F_TEST_RND_HI32 is used in BPF_PROG_LOAD command for testing purpose.
+ * Verifier does sub-register def/use analysis and identifies instructions whose
+ * def only matters for low 32-bit, high 32-bit is never referenced later
+ * through implicit zero extension. Therefore verifier notifies JIT back-ends
+ * that it is safe to ignore clearing high 32-bit for these instructions. This
+ * saves some back-ends a lot of code-gen. However such optimization is not
+ * necessary on some arches, for example x86_64, arm64 etc, whose JIT back-ends
+ * hence hasn't used verifier's analysis result. But, we really want to have a
+ * way to be able to verify the correctness of the described optimization on
+ * x86_64 on which testsuites are frequently exercised.
+ *
+ * So, this flag is introduced. Once it is set, verifier will randomize high
+ * 32-bit for those instructions who has been identified as safe to ignore them.
+ * Then, if verifier is not doing correct analysis, such randomization will
+ * regress tests to expose bugs.
+ */
+#define BPF_F_TEST_RND_HI32 (1U << 2)
+
+/* The verifier internal test flag. Behavior is undefined */
+#define BPF_F_TEST_STATE_FREQ (1U << 3)
+
+/* When BPF ldimm64's insn[0].src_reg != 0 then this can have
+ * two extensions:
+ *
+ * insn[0].src_reg: BPF_PSEUDO_MAP_FD BPF_PSEUDO_MAP_VALUE
+ * insn[0].imm: map fd map fd
+ * insn[1].imm: 0 offset into value
+ * insn[0].off: 0 0
+ * insn[1].off: 0 0
+ * ldimm64 rewrite: address of map address of map[0]+offset
+ * verifier type: CONST_PTR_TO_MAP PTR_TO_MAP_VALUE
+ */
+#define BPF_PSEUDO_MAP_FD 1
+#define BPF_PSEUDO_MAP_VALUE 2
+
+/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
+ * offset to another bpf function
+ */
+#define BPF_PSEUDO_CALL 1
+
+/* flags for BPF_MAP_UPDATE_ELEM command */
+#define BPF_ANY 0 /* create new element or update existing */
+#define BPF_NOEXIST 1 /* create new element if it didn't exist */
+#define BPF_EXIST 2 /* update existing element */
+#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */
+
+/* flags for BPF_MAP_CREATE command */
+#define BPF_F_NO_PREALLOC (1U << 0)
+/* Instead of having one common LRU list in the
+ * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list
+ * which can scale and perform better.
+ * Note, the LRU nodes (including free nodes) cannot be moved
+ * across different LRU lists.
+ */
+#define BPF_F_NO_COMMON_LRU (1U << 1)
+/* Specify numa node during map creation */
+#define BPF_F_NUMA_NODE (1U << 2)
+
+#define BPF_OBJ_NAME_LEN 16U
+
+/* Flags for accessing BPF object from syscall side. */
+#define BPF_F_RDONLY (1U << 3)
+#define BPF_F_WRONLY (1U << 4)
+
+/* Flag for stack_map, store build_id+offset instead of pointer */
+#define BPF_F_STACK_BUILD_ID (1U << 5)
+
+/* Zero-initialize hash function seed. This should only be used for testing. */
+#define BPF_F_ZERO_SEED (1U << 6)
+
+/* Flags for accessing BPF object from program side. */
+#define BPF_F_RDONLY_PROG (1U << 7)
+#define BPF_F_WRONLY_PROG (1U << 8)
+
+/* Clone map from listener for newly accepted socket */
+#define BPF_F_CLONE (1U << 9)
+
+/* Enable memory-mapping BPF map */
+#define BPF_F_MMAPABLE (1U << 10)
+
+/* flags for BPF_PROG_QUERY */
+#define BPF_F_QUERY_EFFECTIVE (1U << 0)
+
+enum bpf_stack_build_id_status {
+ /* user space need an empty entry to identify end of a trace */
+ BPF_STACK_BUILD_ID_EMPTY = 0,
+ /* with valid build_id and offset */
+ BPF_STACK_BUILD_ID_VALID = 1,
+ /* couldn't get build_id, fallback to ip */
+ BPF_STACK_BUILD_ID_IP = 2,
+};
+
+#define BPF_BUILD_ID_SIZE 20
+struct bpf_stack_build_id {
+ __s32 status;
+ unsigned char build_id[BPF_BUILD_ID_SIZE];
+ union {
+ __u64 offset;
+ __u64 ip;
+ };
+};
+
+union bpf_attr {
+ struct { /* anonymous struct used by BPF_MAP_CREATE command */
+ __u32 map_type; /* one of enum bpf_map_type */
+ __u32 key_size; /* size of key in bytes */
+ __u32 value_size; /* size of value in bytes */
+ __u32 max_entries; /* max number of entries in a map */
+ __u32 map_flags; /* BPF_MAP_CREATE related
+ * flags defined above.
+ */
+ __u32 inner_map_fd; /* fd pointing to the inner map */
+ __u32 numa_node; /* numa node (effective only if
+ * BPF_F_NUMA_NODE is set).
+ */
+ char map_name[BPF_OBJ_NAME_LEN];
+ __u32 map_ifindex; /* ifindex of netdev to create on */
+ __u32 btf_fd; /* fd pointing to a BTF type data */
+ __u32 btf_key_type_id; /* BTF type_id of the key */
+ __u32 btf_value_type_id; /* BTF type_id of the value */
+ };
+
+ struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
+ __u32 map_fd;
+ __aligned_u64 key;
+ union {
+ __aligned_u64 value;
+ __aligned_u64 next_key;
+ };
+ __u64 flags;
+ };
+
+ struct { /* anonymous struct used by BPF_PROG_LOAD command */
+ __u32 prog_type; /* one of enum bpf_prog_type */
+ __u32 insn_cnt;
+ __aligned_u64 insns;
+ __aligned_u64 license;
+ __u32 log_level; /* verbosity level of verifier */
+ __u32 log_size; /* size of user buffer */
+ __aligned_u64 log_buf; /* user supplied buffer */
+ __u32 kern_version; /* not used */
+ __u32 prog_flags;
+ char prog_name[BPF_OBJ_NAME_LEN];
+ __u32 prog_ifindex; /* ifindex of netdev to prep for */
+ /* For some prog types expected attach type must be known at
+ * load time to verify attach type specific parts of prog
+ * (context accesses, allowed helpers, etc).
+ */
+ __u32 expected_attach_type;
+ __u32 prog_btf_fd; /* fd pointing to BTF type data */
+ __u32 func_info_rec_size; /* userspace bpf_func_info size */
+ __aligned_u64 func_info; /* func info */
+ __u32 func_info_cnt; /* number of bpf_func_info records */
+ __u32 line_info_rec_size; /* userspace bpf_line_info size */
+ __aligned_u64 line_info; /* line info */
+ __u32 line_info_cnt; /* number of bpf_line_info records */
+ __u32 attach_btf_id; /* in-kernel BTF type id to attach to */
+ __u32 attach_prog_fd; /* 0 to attach to vmlinux */
+ };
+
+ struct { /* anonymous struct used by BPF_OBJ_* commands */
+ __aligned_u64 pathname;
+ __u32 bpf_fd;
+ __u32 file_flags;
+ };
+
+ struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
+ __u32 target_fd; /* container object to attach to */
+ __u32 attach_bpf_fd; /* eBPF program to attach */
+ __u32 attach_type;
+ __u32 attach_flags;
+ };
+
+ struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */
+ __u32 prog_fd;
+ __u32 retval;
+ __u32 data_size_in; /* input: len of data_in */
+ __u32 data_size_out; /* input/output: len of data_out
+ * returns ENOSPC if data_out
+ * is too small.
+ */
+ __aligned_u64 data_in;
+ __aligned_u64 data_out;
+ __u32 repeat;
+ __u32 duration;
+ __u32 ctx_size_in; /* input: len of ctx_in */
+ __u32 ctx_size_out; /* input/output: len of ctx_out
+ * returns ENOSPC if ctx_out
+ * is too small.
+ */
+ __aligned_u64 ctx_in;
+ __aligned_u64 ctx_out;
+ } test;
+
+ struct { /* anonymous struct used by BPF_*_GET_*_ID */
+ union {
+ __u32 start_id;
+ __u32 prog_id;
+ __u32 map_id;
+ __u32 btf_id;
+ };
+ __u32 next_id;
+ __u32 open_flags;
+ };
+
+ struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+ __u32 bpf_fd;
+ __u32 info_len;
+ __aligned_u64 info;
+ } info;
+
+ struct { /* anonymous struct used by BPF_PROG_QUERY command */
+ __u32 target_fd; /* container object to query */
+ __u32 attach_type;
+ __u32 query_flags;
+ __u32 attach_flags;
+ __aligned_u64 prog_ids;
+ __u32 prog_cnt;
+ } query;
+
+ struct {
+ __u64 name;
+ __u32 prog_fd;
+ } raw_tracepoint;
+
+ struct { /* anonymous struct for BPF_BTF_LOAD */
+ __aligned_u64 btf;
+ __aligned_u64 btf_log_buf;
+ __u32 btf_size;
+ __u32 btf_log_size;
+ __u32 btf_log_level;
+ };
+
+ struct {
+ __u32 pid; /* input: pid */
+ __u32 fd; /* input: fd */
+ __u32 flags; /* input: flags */
+ __u32 buf_len; /* input/output: buf len */
+ __aligned_u64 buf; /* input/output:
+ * tp_name for tracepoint
+ * symbol for kprobe
+ * filename for uprobe
+ */
+ __u32 prog_id; /* output: prod_id */
+ __u32 fd_type; /* output: BPF_FD_TYPE_* */
+ __u64 probe_offset; /* output: probe_offset */
+ __u64 probe_addr; /* output: probe_addr */
+ } task_fd_query;
+} __attribute__((aligned(8)));
+
+/* The description below is an attempt at providing documentation to eBPF
+ * developers about the multiple available eBPF helper functions. It can be
+ * parsed and used to produce a manual page. The workflow is the following,
+ * and requires the rst2man utility:
+ *
+ * $ ./scripts/bpf_helpers_doc.py \
+ * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
+ * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
+ * $ man /tmp/bpf-helpers.7
+ *
+ * Note that in order to produce this external documentation, some RST
+ * formatting is used in the descriptions to get "bold" and "italics" in
+ * manual pages. Also note that the few trailing white spaces are
+ * intentional, removing them would break paragraphs for rst2man.
+ *
+ * Start of BPF helper function descriptions:
+ *
+ * void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)
+ * Description
+ * Perform a lookup in *map* for an entry associated to *key*.
+ * Return
+ * Map value associated to *key*, or **NULL** if no entry was
+ * found.
+ *
+ * int bpf_map_update_elem(struct bpf_map *map, const void *key, const void *value, u64 flags)
+ * Description
+ * Add or update the value of the entry associated to *key* in
+ * *map* with *value*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * Flag value **BPF_NOEXIST** cannot be used for maps of types
+ * **BPF_MAP_TYPE_ARRAY** or **BPF_MAP_TYPE_PERCPU_ARRAY** (all
+ * elements always exist), the helper would return an error.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_delete_elem(struct bpf_map *map, const void *key)
+ * Description
+ * Delete entry with *key* from *map*.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_probe_read(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * For tracing programs, safely attempt to read *size* bytes from
+ * kernel space address *unsafe_ptr* and store the data in *dst*.
+ *
+ * Generally, use bpf_probe_read_user() or bpf_probe_read_kernel()
+ * instead.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * u64 bpf_ktime_get_ns(void)
+ * Description
+ * Return the time elapsed since system boot, in nanoseconds.
+ * Return
+ * Current *ktime*.
+ *
+ * int bpf_trace_printk(const char *fmt, u32 fmt_size, ...)
+ * Description
+ * This helper is a "printk()-like" facility for debugging. It
+ * prints a message defined by format *fmt* (of size *fmt_size*)
+ * to file *\/sys/kernel/debug/tracing/trace* from DebugFS, if
+ * available. It can take up to three additional **u64**
+ * arguments (as an eBPF helpers, the total number of arguments is
+ * limited to five).
+ *
+ * Each time the helper is called, it appends a line to the trace.
+ * Lines are discarded while *\/sys/kernel/debug/tracing/trace* is
+ * open, use *\/sys/kernel/debug/tracing/trace_pipe* to avoid this.
+ * The format of the trace is customizable, and the exact output
+ * one will get depends on the options set in
+ * *\/sys/kernel/debug/tracing/trace_options* (see also the
+ * *README* file under the same directory). However, it usually
+ * defaults to something like:
+ *
+ * ::
+ *
+ * telnet-470 [001] .N.. 419421.045894: 0x00000001: <formatted msg>
+ *
+ * In the above:
+ *
+ * * ``telnet`` is the name of the current task.
+ * * ``470`` is the PID of the current task.
+ * * ``001`` is the CPU number on which the task is
+ * running.
+ * * In ``.N..``, each character refers to a set of
+ * options (whether irqs are enabled, scheduling
+ * options, whether hard/softirqs are running, level of
+ * preempt_disabled respectively). **N** means that
+ * **TIF_NEED_RESCHED** and **PREEMPT_NEED_RESCHED**
+ * are set.
+ * * ``419421.045894`` is a timestamp.
+ * * ``0x00000001`` is a fake value used by BPF for the
+ * instruction pointer register.
+ * * ``<formatted msg>`` is the message formatted with
+ * *fmt*.
+ *
+ * The conversion specifiers supported by *fmt* are similar, but
+ * more limited than for printk(). They are **%d**, **%i**,
+ * **%u**, **%x**, **%ld**, **%li**, **%lu**, **%lx**, **%lld**,
+ * **%lli**, **%llu**, **%llx**, **%p**, **%s**. No modifier (size
+ * of field, padding with zeroes, etc.) is available, and the
+ * helper will return **-EINVAL** (but print nothing) if it
+ * encounters an unknown specifier.
+ *
+ * Also, note that **bpf_trace_printk**\ () is slow, and should
+ * only be used for debugging purposes. For this reason, a notice
+ * bloc (spanning several lines) is printed to kernel logs and
+ * states that the helper should not be used "for production use"
+ * the first time this helper is used (or more precisely, when
+ * **trace_printk**\ () buffers are allocated). For passing values
+ * to user space, perf events should be preferred.
+ * Return
+ * The number of bytes written to the buffer, or a negative error
+ * in case of failure.
+ *
+ * u32 bpf_get_prandom_u32(void)
+ * Description
+ * Get a pseudo-random number.
+ *
+ * From a security point of view, this helper uses its own
+ * pseudo-random internal state, and cannot be used to infer the
+ * seed of other random functions in the kernel. However, it is
+ * essential to note that the generator used by the helper is not
+ * cryptographically secure.
+ * Return
+ * A random 32-bit unsigned value.
+ *
+ * u32 bpf_get_smp_processor_id(void)
+ * Description
+ * Get the SMP (symmetric multiprocessing) processor id. Note that
+ * all programs run with preemption disabled, which means that the
+ * SMP processor id is stable during all the execution of the
+ * program.
+ * Return
+ * The SMP id of the processor running the program.
+ *
+ * int bpf_skb_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len, u64 flags)
+ * Description
+ * Store *len* bytes from address *from* into the packet
+ * associated to *skb*, at *offset*. *flags* are a combination of
+ * **BPF_F_RECOMPUTE_CSUM** (automatically recompute the
+ * checksum for the packet after storing the bytes) and
+ * **BPF_F_INVALIDATE_HASH** (set *skb*\ **->hash**, *skb*\
+ * **->swhash** and *skb*\ **->l4hash** to 0).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_l3_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 size)
+ * Description
+ * Recompute the layer 3 (e.g. IP) checksum for the packet
+ * associated to *skb*. Computation is incremental, so the helper
+ * must know the former value of the header field that was
+ * modified (*from*), the new value of this field (*to*), and the
+ * number of bytes (2 or 4) for this field, stored in *size*.
+ * Alternatively, it is possible to store the difference between
+ * the previous and the new values of the header field in *to*, by
+ * setting *from* and *size* to 0. For both methods, *offset*
+ * indicates the location of the IP checksum within the packet.
+ *
+ * This helper works in combination with **bpf_csum_diff**\ (),
+ * which does not update the checksum in-place, but offers more
+ * flexibility and can handle sizes larger than 2 or 4 for the
+ * checksum to update.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags)
+ * Description
+ * Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
+ * packet associated to *skb*. Computation is incremental, so the
+ * helper must know the former value of the header field that was
+ * modified (*from*), the new value of this field (*to*), and the
+ * number of bytes (2 or 4) for this field, stored on the lowest
+ * four bits of *flags*. Alternatively, it is possible to store
+ * the difference between the previous and the new values of the
+ * header field in *to*, by setting *from* and the four lowest
+ * bits of *flags* to 0. For both methods, *offset* indicates the
+ * location of the IP checksum within the packet. In addition to
+ * the size of the field, *flags* can be added (bitwise OR) actual
+ * flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
+ * untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
+ * for updates resulting in a null checksum the value is set to
+ * **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
+ * the checksum is to be computed against a pseudo-header.
+ *
+ * This helper works in combination with **bpf_csum_diff**\ (),
+ * which does not update the checksum in-place, but offers more
+ * flexibility and can handle sizes larger than 2 or 4 for the
+ * checksum to update.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_tail_call(void *ctx, struct bpf_map *prog_array_map, u32 index)
+ * Description
+ * This special helper is used to trigger a "tail call", or in
+ * other words, to jump into another eBPF program. The same stack
+ * frame is used (but values on stack and in registers for the
+ * caller are not accessible to the callee). This mechanism allows
+ * for program chaining, either for raising the maximum number of
+ * available eBPF instructions, or to execute given programs in
+ * conditional blocks. For security reasons, there is an upper
+ * limit to the number of successive tail calls that can be
+ * performed.
+ *
+ * Upon call of this helper, the program attempts to jump into a
+ * program referenced at index *index* in *prog_array_map*, a
+ * special map of type **BPF_MAP_TYPE_PROG_ARRAY**, and passes
+ * *ctx*, a pointer to the context.
+ *
+ * If the call succeeds, the kernel immediately runs the first
+ * instruction of the new program. This is not a function call,
+ * and it never returns to the previous program. If the call
+ * fails, then the helper has no effect, and the caller continues
+ * to run its subsequent instructions. A call can fail if the
+ * destination program for the jump does not exist (i.e. *index*
+ * is superior to the number of entries in *prog_array_map*), or
+ * if the maximum number of tail calls has been reached for this
+ * chain of programs. This limit is defined in the kernel by the
+ * macro **MAX_TAIL_CALL_CNT** (not accessible to user space),
+ * which is currently set to 32.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags)
+ * Description
+ * Clone and redirect the packet associated to *skb* to another
+ * net device of index *ifindex*. Both ingress and egress
+ * interfaces can be used for redirection. The **BPF_F_INGRESS**
+ * value in *flags* is used to make the distinction (ingress path
+ * is selected if the flag is present, egress path otherwise).
+ * This is the only flag supported for now.
+ *
+ * In comparison with **bpf_redirect**\ () helper,
+ * **bpf_clone_redirect**\ () has the associated cost of
+ * duplicating the packet buffer, but this can be executed out of
+ * the eBPF program. Conversely, **bpf_redirect**\ () is more
+ * efficient, but it is handled through an action code where the
+ * redirection happens only after the eBPF program has returned.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * u64 bpf_get_current_pid_tgid(void)
+ * Return
+ * A 64-bit integer containing the current tgid and pid, and
+ * created as such:
+ * *current_task*\ **->tgid << 32 \|**
+ * *current_task*\ **->pid**.
+ *
+ * u64 bpf_get_current_uid_gid(void)
+ * Return
+ * A 64-bit integer containing the current GID and UID, and
+ * created as such: *current_gid* **<< 32 \|** *current_uid*.
+ *
+ * int bpf_get_current_comm(void *buf, u32 size_of_buf)
+ * Description
+ * Copy the **comm** attribute of the current task into *buf* of
+ * *size_of_buf*. The **comm** attribute contains the name of
+ * the executable (excluding the path) for the current task. The
+ * *size_of_buf* must be strictly positive. On success, the
+ * helper makes sure that the *buf* is NUL-terminated. On failure,
+ * it is filled with zeroes.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * u32 bpf_get_cgroup_classid(struct sk_buff *skb)
+ * Description
+ * Retrieve the classid for the current task, i.e. for the net_cls
+ * cgroup to which *skb* belongs.
+ *
+ * This helper can be used on TC egress path, but not on ingress.
+ *
+ * The net_cls cgroup provides an interface to tag network packets
+ * based on a user-provided identifier for all traffic coming from
+ * the tasks belonging to the related cgroup. See also the related
+ * kernel documentation, available from the Linux sources in file
+ * *Documentation/admin-guide/cgroup-v1/net_cls.rst*.
+ *
+ * The Linux kernel has two versions for cgroups: there are
+ * cgroups v1 and cgroups v2. Both are available to users, who can
+ * use a mixture of them, but note that the net_cls cgroup is for
+ * cgroup v1 only. This makes it incompatible with BPF programs
+ * run on cgroups, which is a cgroup-v2-only feature (a socket can
+ * only hold data for one version of cgroups at a time).
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_CGROUP_NET_CLASSID** configuration option set to
+ * "**y**" or to "**m**".
+ * Return
+ * The classid, or 0 for the default unconfigured classid.
+ *
+ * int bpf_skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
+ * Description
+ * Push a *vlan_tci* (VLAN tag control information) of protocol
+ * *vlan_proto* to the packet associated to *skb*, then update
+ * the checksum. Note that if *vlan_proto* is different from
+ * **ETH_P_8021Q** and **ETH_P_8021AD**, it is considered to
+ * be **ETH_P_8021Q**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_vlan_pop(struct sk_buff *skb)
+ * Description
+ * Pop a VLAN header from the packet associated to *skb*.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_get_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
+ * Description
+ * Get tunnel metadata. This helper takes a pointer *key* to an
+ * empty **struct bpf_tunnel_key** of **size**, that will be
+ * filled with tunnel metadata for the packet associated to *skb*.
+ * The *flags* can be set to **BPF_F_TUNINFO_IPV6**, which
+ * indicates that the tunnel is based on IPv6 protocol instead of
+ * IPv4.
+ *
+ * The **struct bpf_tunnel_key** is an object that generalizes the
+ * principal parameters used by various tunneling protocols into a
+ * single struct. This way, it can be used to easily make a
+ * decision based on the contents of the encapsulation header,
+ * "summarized" in this struct. In particular, it holds the IP
+ * address of the remote end (IPv4 or IPv6, depending on the case)
+ * in *key*\ **->remote_ipv4** or *key*\ **->remote_ipv6**. Also,
+ * this struct exposes the *key*\ **->tunnel_id**, which is
+ * generally mapped to a VNI (Virtual Network Identifier), making
+ * it programmable together with the **bpf_skb_set_tunnel_key**\
+ * () helper.
+ *
+ * Let's imagine that the following code is part of a program
+ * attached to the TC ingress interface, on one end of a GRE
+ * tunnel, and is supposed to filter out all messages coming from
+ * remote ends with IPv4 address other than 10.0.0.1:
+ *
+ * ::
+ *
+ * int ret;
+ * struct bpf_tunnel_key key = {};
+ *
+ * ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+ * if (ret < 0)
+ * return TC_ACT_SHOT; // drop packet
+ *
+ * if (key.remote_ipv4 != 0x0a000001)
+ * return TC_ACT_SHOT; // drop packet
+ *
+ * return TC_ACT_OK; // accept packet
+ *
+ * This interface can also be used with all encapsulation devices
+ * that can operate in "collect metadata" mode: instead of having
+ * one network device per specific configuration, the "collect
+ * metadata" mode only requires a single device where the
+ * configuration can be extracted from this helper.
+ *
+ * This can be used together with various tunnels such as VXLan,
+ * Geneve, GRE or IP in IP (IPIP).
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_set_tunnel_key(struct sk_buff *skb, struct bpf_tunnel_key *key, u32 size, u64 flags)
+ * Description
+ * Populate tunnel metadata for packet associated to *skb.* The
+ * tunnel metadata is set to the contents of *key*, of *size*. The
+ * *flags* can be set to a combination of the following values:
+ *
+ * **BPF_F_TUNINFO_IPV6**
+ * Indicate that the tunnel is based on IPv6 protocol
+ * instead of IPv4.
+ * **BPF_F_ZERO_CSUM_TX**
+ * For IPv4 packets, add a flag to tunnel metadata
+ * indicating that checksum computation should be skipped
+ * and checksum set to zeroes.
+ * **BPF_F_DONT_FRAGMENT**
+ * Add a flag to tunnel metadata indicating that the
+ * packet should not be fragmented.
+ * **BPF_F_SEQ_NUMBER**
+ * Add a flag to tunnel metadata indicating that a
+ * sequence number should be added to tunnel header before
+ * sending the packet. This flag was added for GRE
+ * encapsulation, but might be used with other protocols
+ * as well in the future.
+ *
+ * Here is a typical usage on the transmit path:
+ *
+ * ::
+ *
+ * struct bpf_tunnel_key key;
+ * populate key ...
+ * bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
+ * bpf_clone_redirect(skb, vxlan_dev_ifindex, 0);
+ *
+ * See also the description of the **bpf_skb_get_tunnel_key**\ ()
+ * helper for additional information.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * u64 bpf_perf_event_read(struct bpf_map *map, u64 flags)
+ * Description
+ * Read the value of a perf event counter. This helper relies on a
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of
+ * the perf event counter is selected when *map* is updated with
+ * perf event file descriptors. The *map* is an array whose size
+ * is the number of available CPUs, and each cell contains a value
+ * relative to one CPU. The value to retrieve is indicated by
+ * *flags*, that contains the index of the CPU to look up, masked
+ * with **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
+ * **BPF_F_CURRENT_CPU** to indicate that the value for the
+ * current CPU should be retrieved.
+ *
+ * Note that before Linux 4.13, only hardware perf event can be
+ * retrieved.
+ *
+ * Also, be aware that the newer helper
+ * **bpf_perf_event_read_value**\ () is recommended over
+ * **bpf_perf_event_read**\ () in general. The latter has some ABI
+ * quirks where error and counter value are used as a return code
+ * (which is wrong to do since ranges may overlap). This issue is
+ * fixed with **bpf_perf_event_read_value**\ (), which at the same
+ * time provides more features over the **bpf_perf_event_read**\
+ * () interface. Please refer to the description of
+ * **bpf_perf_event_read_value**\ () for details.
+ * Return
+ * The value of the perf event counter read from the map, or a
+ * negative error code in case of failure.
+ *
+ * int bpf_redirect(u32 ifindex, u64 flags)
+ * Description
+ * Redirect the packet to another net device of index *ifindex*.
+ * This helper is somewhat similar to **bpf_clone_redirect**\
+ * (), except that the packet is not cloned, which provides
+ * increased performance.
+ *
+ * Except for XDP, both ingress and egress interfaces can be used
+ * for redirection. The **BPF_F_INGRESS** value in *flags* is used
+ * to make the distinction (ingress path is selected if the flag
+ * is present, egress path otherwise). Currently, XDP only
+ * supports redirection to the egress interface, and accepts no
+ * flag at all.
+ *
+ * The same effect can be attained with the more generic
+ * **bpf_redirect_map**\ (), which requires specific maps to be
+ * used but offers better performance.
+ * Return
+ * For XDP, the helper returns **XDP_REDIRECT** on success or
+ * **XDP_ABORTED** on error. For other program types, the values
+ * are **TC_ACT_REDIRECT** on success or **TC_ACT_SHOT** on
+ * error.
+ *
+ * u32 bpf_get_route_realm(struct sk_buff *skb)
+ * Description
+ * Retrieve the realm or the route, that is to say the
+ * **tclassid** field of the destination for the *skb*. The
+ * indentifier retrieved is a user-provided tag, similar to the
+ * one used with the net_cls cgroup (see description for
+ * **bpf_get_cgroup_classid**\ () helper), but here this tag is
+ * held by a route (a destination entry), not by a task.
+ *
+ * Retrieving this identifier works with the clsact TC egress hook
+ * (see also **tc-bpf(8)**), or alternatively on conventional
+ * classful egress qdiscs, but not on TC ingress path. In case of
+ * clsact TC egress hook, this has the advantage that, internally,
+ * the destination entry has not been dropped yet in the transmit
+ * path. Therefore, the destination entry does not need to be
+ * artificially held via **netif_keep_dst**\ () for a classful
+ * qdisc until the *skb* is freed.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_IP_ROUTE_CLASSID** configuration option.
+ * Return
+ * The realm of the route for the packet associated to *skb*, or 0
+ * if none was found.
+ *
+ * int bpf_perf_event_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
+ * Description
+ * Write raw *data* blob into a special BPF perf event held by
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
+ * event must have the following attributes: **PERF_SAMPLE_RAW**
+ * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
+ * **PERF_COUNT_SW_BPF_OUTPUT** as **config**.
+ *
+ * The *flags* are used to indicate the index in *map* for which
+ * the value must be put, masked with **BPF_F_INDEX_MASK**.
+ * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
+ * to indicate that the index of the current CPU core should be
+ * used.
+ *
+ * The value to write, of *size*, is passed through eBPF stack and
+ * pointed by *data*.
+ *
+ * The context of the program *ctx* needs also be passed to the
+ * helper.
+ *
+ * On user space, a program willing to read the values needs to
+ * call **perf_event_open**\ () on the perf event (either for
+ * one or for all CPUs) and to store the file descriptor into the
+ * *map*. This must be done before the eBPF program can send data
+ * into it. An example is available in file
+ * *samples/bpf/trace_output_user.c* in the Linux kernel source
+ * tree (the eBPF program counterpart is in
+ * *samples/bpf/trace_output_kern.c*).
+ *
+ * **bpf_perf_event_output**\ () achieves better performance
+ * than **bpf_trace_printk**\ () for sharing data with user
+ * space, and is much better suitable for streaming data from eBPF
+ * programs.
+ *
+ * Note that this helper is not restricted to tracing use cases
+ * and can be used with programs attached to TC or XDP as well,
+ * where it allows for passing data to user space listeners. Data
+ * can be:
+ *
+ * * Only custom structs,
+ * * Only the packet payload, or
+ * * A combination of both.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_load_bytes(const void *skb, u32 offset, void *to, u32 len)
+ * Description
+ * This helper was provided as an easy way to load data from a
+ * packet. It can be used to load *len* bytes from *offset* from
+ * the packet associated to *skb*, into the buffer pointed by
+ * *to*.
+ *
+ * Since Linux 4.7, usage of this helper has mostly been replaced
+ * by "direct packet access", enabling packet data to be
+ * manipulated with *skb*\ **->data** and *skb*\ **->data_end**
+ * pointing respectively to the first byte of packet data and to
+ * the byte after the last byte of packet data. However, it
+ * remains useful if one wishes to read large quantities of data
+ * at once from a packet into the eBPF stack.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_get_stackid(void *ctx, struct bpf_map *map, u64 flags)
+ * Description
+ * Walk a user or a kernel stack and return its id. To achieve
+ * this, the helper needs *ctx*, which is a pointer to the context
+ * on which the tracing program is executed, and a pointer to a
+ * *map* of type **BPF_MAP_TYPE_STACK_TRACE**.
+ *
+ * The last argument, *flags*, holds the number of stack frames to
+ * skip (from 0 to 255), masked with
+ * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
+ * a combination of the following flags:
+ *
+ * **BPF_F_USER_STACK**
+ * Collect a user space stack instead of a kernel stack.
+ * **BPF_F_FAST_STACK_CMP**
+ * Compare stacks by hash only.
+ * **BPF_F_REUSE_STACKID**
+ * If two different stacks hash into the same *stackid*,
+ * discard the old one.
+ *
+ * The stack id retrieved is a 32 bit long integer handle which
+ * can be further combined with other data (including other stack
+ * ids) and used as a key into maps. This can be useful for
+ * generating a variety of graphs (such as flame graphs or off-cpu
+ * graphs).
+ *
+ * For walking a stack, this helper is an improvement over
+ * **bpf_probe_read**\ (), which can be used with unrolled loops
+ * but is not efficient and consumes a lot of eBPF instructions.
+ * Instead, **bpf_get_stackid**\ () can collect up to
+ * **PERF_MAX_STACK_DEPTH** both kernel and user frames. Note that
+ * this limit can be controlled with the **sysctl** program, and
+ * that it should be manually increased in order to profile long
+ * user stacks (such as stacks for Java programs). To do so, use:
+ *
+ * ::
+ *
+ * # sysctl kernel.perf_event_max_stack=<new value>
+ * Return
+ * The positive or null stack id on success, or a negative error
+ * in case of failure.
+ *
+ * s64 bpf_csum_diff(__be32 *from, u32 from_size, __be32 *to, u32 to_size, __wsum seed)
+ * Description
+ * Compute a checksum difference, from the raw buffer pointed by
+ * *from*, of length *from_size* (that must be a multiple of 4),
+ * towards the raw buffer pointed by *to*, of size *to_size*
+ * (same remark). An optional *seed* can be added to the value
+ * (this can be cascaded, the seed may come from a previous call
+ * to the helper).
+ *
+ * This is flexible enough to be used in several ways:
+ *
+ * * With *from_size* == 0, *to_size* > 0 and *seed* set to
+ * checksum, it can be used when pushing new data.
+ * * With *from_size* > 0, *to_size* == 0 and *seed* set to
+ * checksum, it can be used when removing data from a packet.
+ * * With *from_size* > 0, *to_size* > 0 and *seed* set to 0, it
+ * can be used to compute a diff. Note that *from_size* and
+ * *to_size* do not need to be equal.
+ *
+ * This helper can be used in combination with
+ * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\ (), to
+ * which one can feed in the difference computed with
+ * **bpf_csum_diff**\ ().
+ * Return
+ * The checksum result, or a negative error code in case of
+ * failure.
+ *
+ * int bpf_skb_get_tunnel_opt(struct sk_buff *skb, void *opt, u32 size)
+ * Description
+ * Retrieve tunnel options metadata for the packet associated to
+ * *skb*, and store the raw tunnel option data to the buffer *opt*
+ * of *size*.
+ *
+ * This helper can be used with encapsulation devices that can
+ * operate in "collect metadata" mode (please refer to the related
+ * note in the description of **bpf_skb_get_tunnel_key**\ () for
+ * more details). A particular example where this can be used is
+ * in combination with the Geneve encapsulation protocol, where it
+ * allows for pushing (with **bpf_skb_get_tunnel_opt**\ () helper)
+ * and retrieving arbitrary TLVs (Type-Length-Value headers) from
+ * the eBPF program. This allows for full customization of these
+ * headers.
+ * Return
+ * The size of the option data retrieved.
+ *
+ * int bpf_skb_set_tunnel_opt(struct sk_buff *skb, void *opt, u32 size)
+ * Description
+ * Set tunnel options metadata for the packet associated to *skb*
+ * to the option data contained in the raw buffer *opt* of *size*.
+ *
+ * See also the description of the **bpf_skb_get_tunnel_opt**\ ()
+ * helper for additional information.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_change_proto(struct sk_buff *skb, __be16 proto, u64 flags)
+ * Description
+ * Change the protocol of the *skb* to *proto*. Currently
+ * supported are transition from IPv4 to IPv6, and from IPv6 to
+ * IPv4. The helper takes care of the groundwork for the
+ * transition, including resizing the socket buffer. The eBPF
+ * program is expected to fill the new headers, if any, via
+ * **skb_store_bytes**\ () and to recompute the checksums with
+ * **bpf_l3_csum_replace**\ () and **bpf_l4_csum_replace**\
+ * (). The main case for this helper is to perform NAT64
+ * operations out of an eBPF program.
+ *
+ * Internally, the GSO type is marked as dodgy so that headers are
+ * checked and segments are recalculated by the GSO/GRO engine.
+ * The size for GSO target is adapted as well.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_change_type(struct sk_buff *skb, u32 type)
+ * Description
+ * Change the packet type for the packet associated to *skb*. This
+ * comes down to setting *skb*\ **->pkt_type** to *type*, except
+ * the eBPF program does not have a write access to *skb*\
+ * **->pkt_type** beside this helper. Using a helper here allows
+ * for graceful handling of errors.
+ *
+ * The major use case is to change incoming *skb*s to
+ * **PACKET_HOST** in a programmatic way instead of having to
+ * recirculate via **redirect**\ (..., **BPF_F_INGRESS**), for
+ * example.
+ *
+ * Note that *type* only allows certain values. At this time, they
+ * are:
+ *
+ * **PACKET_HOST**
+ * Packet is for us.
+ * **PACKET_BROADCAST**
+ * Send packet to all.
+ * **PACKET_MULTICAST**
+ * Send packet to group.
+ * **PACKET_OTHERHOST**
+ * Send packet to someone else.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_under_cgroup(struct sk_buff *skb, struct bpf_map *map, u32 index)
+ * Description
+ * Check whether *skb* is a descendant of the cgroup2 held by
+ * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
+ * Return
+ * The return value depends on the result of the test, and can be:
+ *
+ * * 0, if the *skb* failed the cgroup2 descendant test.
+ * * 1, if the *skb* succeeded the cgroup2 descendant test.
+ * * A negative error code, if an error occurred.
+ *
+ * u32 bpf_get_hash_recalc(struct sk_buff *skb)
+ * Description
+ * Retrieve the hash of the packet, *skb*\ **->hash**. If it is
+ * not set, in particular if the hash was cleared due to mangling,
+ * recompute this hash. Later accesses to the hash can be done
+ * directly with *skb*\ **->hash**.
+ *
+ * Calling **bpf_set_hash_invalid**\ (), changing a packet
+ * prototype with **bpf_skb_change_proto**\ (), or calling
+ * **bpf_skb_store_bytes**\ () with the
+ * **BPF_F_INVALIDATE_HASH** are actions susceptible to clear
+ * the hash and to trigger a new computation for the next call to
+ * **bpf_get_hash_recalc**\ ().
+ * Return
+ * The 32-bit hash.
+ *
+ * u64 bpf_get_current_task(void)
+ * Return
+ * A pointer to the current task struct.
+ *
+ * int bpf_probe_write_user(void *dst, const void *src, u32 len)
+ * Description
+ * Attempt in a safe way to write *len* bytes from the buffer
+ * *src* to *dst* in memory. It only works for threads that are in
+ * user context, and *dst* must be a valid user space address.
+ *
+ * This helper should not be used to implement any kind of
+ * security mechanism because of TOC-TOU attacks, but rather to
+ * debug, divert, and manipulate execution of semi-cooperative
+ * processes.
+ *
+ * Keep in mind that this feature is meant for experiments, and it
+ * has a risk of crashing the system and running programs.
+ * Therefore, when an eBPF program using this helper is attached,
+ * a warning including PID and process name is printed to kernel
+ * logs.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_current_task_under_cgroup(struct bpf_map *map, u32 index)
+ * Description
+ * Check whether the probe is being run is the context of a given
+ * subset of the cgroup2 hierarchy. The cgroup2 to test is held by
+ * *map* of type **BPF_MAP_TYPE_CGROUP_ARRAY**, at *index*.
+ * Return
+ * The return value depends on the result of the test, and can be:
+ *
+ * * 0, if the *skb* task belongs to the cgroup2.
+ * * 1, if the *skb* task does not belong to the cgroup2.
+ * * A negative error code, if an error occurred.
+ *
+ * int bpf_skb_change_tail(struct sk_buff *skb, u32 len, u64 flags)
+ * Description
+ * Resize (trim or grow) the packet associated to *skb* to the
+ * new *len*. The *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * The basic idea is that the helper performs the needed work to
+ * change the size of the packet, then the eBPF program rewrites
+ * the rest via helpers like **bpf_skb_store_bytes**\ (),
+ * **bpf_l3_csum_replace**\ (), **bpf_l3_csum_replace**\ ()
+ * and others. This helper is a slow path utility intended for
+ * replies with control messages. And because it is targeted for
+ * slow path, the helper itself can afford to be slow: it
+ * implicitly linearizes, unclones and drops offloads from the
+ * *skb*.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_pull_data(struct sk_buff *skb, u32 len)
+ * Description
+ * Pull in non-linear data in case the *skb* is non-linear and not
+ * all of *len* are part of the linear section. Make *len* bytes
+ * from *skb* readable and writable. If a zero value is passed for
+ * *len*, then the whole length of the *skb* is pulled.
+ *
+ * This helper is only needed for reading and writing with direct
+ * packet access.
+ *
+ * For direct packet access, testing that offsets to access
+ * are within packet boundaries (test on *skb*\ **->data_end**) is
+ * susceptible to fail if offsets are invalid, or if the requested
+ * data is in non-linear parts of the *skb*. On failure the
+ * program can just bail out, or in the case of a non-linear
+ * buffer, use a helper to make the data available. The
+ * **bpf_skb_load_bytes**\ () helper is a first solution to access
+ * the data. Another one consists in using **bpf_skb_pull_data**
+ * to pull in once the non-linear parts, then retesting and
+ * eventually access the data.
+ *
+ * At the same time, this also makes sure the *skb* is uncloned,
+ * which is a necessary condition for direct write. As this needs
+ * to be an invariant for the write part only, the verifier
+ * detects writes and adds a prologue that is calling
+ * **bpf_skb_pull_data()** to effectively unclone the *skb* from
+ * the very beginning in case it is indeed cloned.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * s64 bpf_csum_update(struct sk_buff *skb, __wsum csum)
+ * Description
+ * Add the checksum *csum* into *skb*\ **->csum** in case the
+ * driver has supplied a checksum for the entire packet into that
+ * field. Return an error otherwise. This helper is intended to be
+ * used in combination with **bpf_csum_diff**\ (), in particular
+ * when the checksum needs to be updated after data has been
+ * written into the packet through direct packet access.
+ * Return
+ * The checksum on success, or a negative error code in case of
+ * failure.
+ *
+ * void bpf_set_hash_invalid(struct sk_buff *skb)
+ * Description
+ * Invalidate the current *skb*\ **->hash**. It can be used after
+ * mangling on headers through direct packet access, in order to
+ * indicate that the hash is outdated and to trigger a
+ * recalculation the next time the kernel tries to access this
+ * hash or when the **bpf_get_hash_recalc**\ () helper is called.
+ *
+ * int bpf_get_numa_node_id(void)
+ * Description
+ * Return the id of the current NUMA node. The primary use case
+ * for this helper is the selection of sockets for the local NUMA
+ * node, when the program is attached to sockets using the
+ * **SO_ATTACH_REUSEPORT_EBPF** option (see also **socket(7)**),
+ * but the helper is also available to other eBPF program types,
+ * similarly to **bpf_get_smp_processor_id**\ ().
+ * Return
+ * The id of current NUMA node.
+ *
+ * int bpf_skb_change_head(struct sk_buff *skb, u32 len, u64 flags)
+ * Description
+ * Grows headroom of packet associated to *skb* and adjusts the
+ * offset of the MAC header accordingly, adding *len* bytes of
+ * space. It automatically extends and reallocates memory as
+ * required.
+ *
+ * This helper can be used on a layer 3 *skb* to push a MAC header
+ * for redirection into a layer 2 device.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_xdp_adjust_head(struct xdp_buff *xdp_md, int delta)
+ * Description
+ * Adjust (move) *xdp_md*\ **->data** by *delta* bytes. Note that
+ * it is possible to use a negative value for *delta*. This helper
+ * can be used to prepare the packet for pushing or popping
+ * headers.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_probe_read_str(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * Copy a NUL terminated string from an unsafe kernel address
+ * *unsafe_ptr* to *dst*. See bpf_probe_read_kernel_str() for
+ * more details.
+ *
+ * Generally, use bpf_probe_read_user_str() or bpf_probe_read_kernel_str()
+ * instead.
+ * Return
+ * On success, the strictly positive length of the string,
+ * including the trailing NUL character. On error, a negative
+ * value.
+ *
+ * u64 bpf_get_socket_cookie(struct sk_buff *skb)
+ * Description
+ * If the **struct sk_buff** pointed by *skb* has a known socket,
+ * retrieve the cookie (generated by the kernel) of this socket.
+ * If no cookie has been set yet, generate a new cookie. Once
+ * generated, the socket cookie remains stable for the life of the
+ * socket. This helper can be useful for monitoring per socket
+ * networking traffic statistics as it provides a global socket
+ * identifier that can be assumed unique.
+ * Return
+ * A 8-byte long non-decreasing number on success, or 0 if the
+ * socket field is missing inside *skb*.
+ *
+ * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx)
+ * Description
+ * Equivalent to bpf_get_socket_cookie() helper that accepts
+ * *skb*, but gets socket from **struct bpf_sock_addr** context.
+ * Return
+ * A 8-byte long non-decreasing number.
+ *
+ * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx)
+ * Description
+ * Equivalent to bpf_get_socket_cookie() helper that accepts
+ * *skb*, but gets socket from **struct bpf_sock_ops** context.
+ * Return
+ * A 8-byte long non-decreasing number.
+ *
+ * u32 bpf_get_socket_uid(struct sk_buff *skb)
+ * Return
+ * The owner UID of the socket associated to *skb*. If the socket
+ * is **NULL**, or if it is not a full socket (i.e. if it is a
+ * time-wait or a request socket instead), **overflowuid** value
+ * is returned (note that **overflowuid** might also be the actual
+ * UID value for the socket).
+ *
+ * u32 bpf_set_hash(struct sk_buff *skb, u32 hash)
+ * Description
+ * Set the full hash for *skb* (set the field *skb*\ **->hash**)
+ * to value *hash*.
+ * Return
+ * 0
+ *
+ * int bpf_setsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * Description
+ * Emulate a call to **setsockopt()** on the socket associated to
+ * *bpf_socket*, which must be a full socket. The *level* at
+ * which the option resides and the name *optname* of the option
+ * must be specified, see **setsockopt(2)** for more information.
+ * The option value of length *optlen* is pointed by *optval*.
+ *
+ * This helper actually implements a subset of **setsockopt()**.
+ * It supports the following *level*\ s:
+ *
+ * * **SOL_SOCKET**, which supports the following *optname*\ s:
+ * **SO_RCVBUF**, **SO_SNDBUF**, **SO_MAX_PACING_RATE**,
+ * **SO_PRIORITY**, **SO_RCVLOWAT**, **SO_MARK**.
+ * * **IPPROTO_TCP**, which supports the following *optname*\ s:
+ * **TCP_CONGESTION**, **TCP_BPF_IW**,
+ * **TCP_BPF_SNDCWND_CLAMP**.
+ * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
+ * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_adjust_room(struct sk_buff *skb, s32 len_diff, u32 mode, u64 flags)
+ * Description
+ * Grow or shrink the room for data in the packet associated to
+ * *skb* by *len_diff*, and according to the selected *mode*.
+ *
+ * There are two supported modes at this time:
+ *
+ * * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer
+ * (room space is added or removed below the layer 2 header).
+ *
+ * * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
+ * (room space is added or removed below the layer 3 header).
+ *
+ * The following flags are supported at this time:
+ *
+ * * **BPF_F_ADJ_ROOM_FIXED_GSO**: Do not adjust gso_size.
+ * Adjusting mss in this way is not allowed for datagrams.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV4**,
+ * **BPF_F_ADJ_ROOM_ENCAP_L3_IPV6**:
+ * Any new space is reserved to hold a tunnel header.
+ * Configure skb offsets and other fields accordingly.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L4_GRE**,
+ * **BPF_F_ADJ_ROOM_ENCAP_L4_UDP**:
+ * Use with ENCAP_L3 flags to further specify the tunnel type.
+ *
+ * * **BPF_F_ADJ_ROOM_ENCAP_L2**\ (*len*):
+ * Use with ENCAP_L3/L4 flags to further specify the tunnel
+ * type; *len* is the length of the inner MAC header.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_redirect_map(struct bpf_map *map, u32 key, u64 flags)
+ * Description
+ * Redirect the packet to the endpoint referenced by *map* at
+ * index *key*. Depending on its type, this *map* can contain
+ * references to net devices (for forwarding packets through other
+ * ports), or to CPUs (for redirecting XDP frames to another CPU;
+ * but this is only implemented for native XDP (with driver
+ * support) as of this writing).
+ *
+ * The lower two bits of *flags* are used as the return code if
+ * the map lookup fails. This is so that the return value can be
+ * one of the XDP program return codes up to XDP_TX, as chosen by
+ * the caller. Any higher bits in the *flags* argument must be
+ * unset.
+ *
+ * When used to redirect packets to net devices, this helper
+ * provides a high performance increase over **bpf_redirect**\ ().
+ * This is due to various implementation details of the underlying
+ * mechanisms, one of which is the fact that **bpf_redirect_map**\
+ * () tries to send packet as a "bulk" to the device.
+ * Return
+ * **XDP_REDIRECT** on success, or **XDP_ABORTED** on error.
+ *
+ * int bpf_sk_redirect_map(struct sk_buff *skb, struct bpf_map *map, u32 key, u64 flags)
+ * Description
+ * Redirect the packet to the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ * Return
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_sock_map_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
+ * Description
+ * Add an entry to, or update a *map* referencing sockets. The
+ * *skops* is used as a new value for the entry associated to
+ * *key*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * If the *map* has eBPF programs (parser and verdict), those will
+ * be inherited by the socket being added. If the socket is
+ * already attached to eBPF programs, this results in an error.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_xdp_adjust_meta(struct xdp_buff *xdp_md, int delta)
+ * Description
+ * Adjust the address pointed by *xdp_md*\ **->data_meta** by
+ * *delta* (which can be positive or negative). Note that this
+ * operation modifies the address stored in *xdp_md*\ **->data**,
+ * so the latter must be loaded only after the helper has been
+ * called.
+ *
+ * The use of *xdp_md*\ **->data_meta** is optional and programs
+ * are not required to use it. The rationale is that when the
+ * packet is processed with XDP (e.g. as DoS filter), it is
+ * possible to push further meta data along with it before passing
+ * to the stack, and to give the guarantee that an ingress eBPF
+ * program attached as a TC classifier on the same device can pick
+ * this up for further post-processing. Since TC works with socket
+ * buffers, it remains possible to set from XDP the **mark** or
+ * **priority** pointers, or other pointers for the socket buffer.
+ * Having this scratch space generic and programmable allows for
+ * more flexibility as the user is free to store whatever meta
+ * data they need.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_perf_event_read_value(struct bpf_map *map, u64 flags, struct bpf_perf_event_value *buf, u32 buf_size)
+ * Description
+ * Read the value of a perf event counter, and store it into *buf*
+ * of size *buf_size*. This helper relies on a *map* of type
+ * **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. The nature of the perf event
+ * counter is selected when *map* is updated with perf event file
+ * descriptors. The *map* is an array whose size is the number of
+ * available CPUs, and each cell contains a value relative to one
+ * CPU. The value to retrieve is indicated by *flags*, that
+ * contains the index of the CPU to look up, masked with
+ * **BPF_F_INDEX_MASK**. Alternatively, *flags* can be set to
+ * **BPF_F_CURRENT_CPU** to indicate that the value for the
+ * current CPU should be retrieved.
+ *
+ * This helper behaves in a way close to
+ * **bpf_perf_event_read**\ () helper, save that instead of
+ * just returning the value observed, it fills the *buf*
+ * structure. This allows for additional data to be retrieved: in
+ * particular, the enabled and running times (in *buf*\
+ * **->enabled** and *buf*\ **->running**, respectively) are
+ * copied. In general, **bpf_perf_event_read_value**\ () is
+ * recommended over **bpf_perf_event_read**\ (), which has some
+ * ABI issues and provides fewer functionalities.
+ *
+ * These values are interesting, because hardware PMU (Performance
+ * Monitoring Unit) counters are limited resources. When there are
+ * more PMU based perf events opened than available counters,
+ * kernel will multiplex these events so each event gets certain
+ * percentage (but not all) of the PMU time. In case that
+ * multiplexing happens, the number of samples or counter value
+ * will not reflect the case compared to when no multiplexing
+ * occurs. This makes comparison between different runs difficult.
+ * Typically, the counter value should be normalized before
+ * comparing to other experiments. The usual normalization is done
+ * as follows.
+ *
+ * ::
+ *
+ * normalized_counter = counter * t_enabled / t_running
+ *
+ * Where t_enabled is the time enabled for event and t_running is
+ * the time running for event since last normalization. The
+ * enabled and running times are accumulated since the perf event
+ * open. To achieve scaling factor between two invocations of an
+ * eBPF program, users can can use CPU id as the key (which is
+ * typical for perf array usage model) to remember the previous
+ * value and do the calculation inside the eBPF program.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct bpf_perf_event_value *buf, u32 buf_size)
+ * Description
+ * For en eBPF program attached to a perf event, retrieve the
+ * value of the event counter associated to *ctx* and store it in
+ * the structure pointed by *buf* and of size *buf_size*. Enabled
+ * and running times are also stored in the structure (see
+ * description of helper **bpf_perf_event_read_value**\ () for
+ * more details).
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_getsockopt(struct bpf_sock_ops *bpf_socket, int level, int optname, void *optval, int optlen)
+ * Description
+ * Emulate a call to **getsockopt()** on the socket associated to
+ * *bpf_socket*, which must be a full socket. The *level* at
+ * which the option resides and the name *optname* of the option
+ * must be specified, see **getsockopt(2)** for more information.
+ * The retrieved value is stored in the structure pointed by
+ * *opval* and of length *optlen*.
+ *
+ * This helper actually implements a subset of **getsockopt()**.
+ * It supports the following *level*\ s:
+ *
+ * * **IPPROTO_TCP**, which supports *optname*
+ * **TCP_CONGESTION**.
+ * * **IPPROTO_IP**, which supports *optname* **IP_TOS**.
+ * * **IPPROTO_IPV6**, which supports *optname* **IPV6_TCLASS**.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_override_return(struct pt_regs *regs, u64 rc)
+ * Description
+ * Used for error injection, this helper uses kprobes to override
+ * the return value of the probed function, and to set it to *rc*.
+ * The first argument is the context *regs* on which the kprobe
+ * works.
+ *
+ * This helper works by setting setting the PC (program counter)
+ * to an override function which is run in place of the original
+ * probed function. This means the probed function is not run at
+ * all. The replacement function just returns with the required
+ * value.
+ *
+ * This helper has security implications, and thus is subject to
+ * restrictions. It is only available if the kernel was compiled
+ * with the **CONFIG_BPF_KPROBE_OVERRIDE** configuration
+ * option, and in this case it only works on functions tagged with
+ * **ALLOW_ERROR_INJECTION** in the kernel code.
+ *
+ * Also, the helper is only available for the architectures having
+ * the CONFIG_FUNCTION_ERROR_INJECTION option. As of this writing,
+ * x86 architecture is the only one to support this feature.
+ * Return
+ * 0
+ *
+ * int bpf_sock_ops_cb_flags_set(struct bpf_sock_ops *bpf_sock, int argval)
+ * Description
+ * Attempt to set the value of the **bpf_sock_ops_cb_flags** field
+ * for the full TCP socket associated to *bpf_sock_ops* to
+ * *argval*.
+ *
+ * The primary use of this field is to determine if there should
+ * be calls to eBPF programs of type
+ * **BPF_PROG_TYPE_SOCK_OPS** at various points in the TCP
+ * code. A program of the same type can change its value, per
+ * connection and as necessary, when the connection is
+ * established. This field is directly accessible for reading, but
+ * this helper must be used for updates in order to return an
+ * error if an eBPF program tries to set a callback that is not
+ * supported in the current kernel.
+ *
+ * *argval* is a flag array which can combine these flags:
+ *
+ * * **BPF_SOCK_OPS_RTO_CB_FLAG** (retransmission time out)
+ * * **BPF_SOCK_OPS_RETRANS_CB_FLAG** (retransmission)
+ * * **BPF_SOCK_OPS_STATE_CB_FLAG** (TCP state change)
+ * * **BPF_SOCK_OPS_RTT_CB_FLAG** (every RTT)
+ *
+ * Therefore, this function can be used to clear a callback flag by
+ * setting the appropriate bit to zero. e.g. to disable the RTO
+ * callback:
+ *
+ * **bpf_sock_ops_cb_flags_set(bpf_sock,**
+ * **bpf_sock->bpf_sock_ops_cb_flags & ~BPF_SOCK_OPS_RTO_CB_FLAG)**
+ *
+ * Here are some examples of where one could call such eBPF
+ * program:
+ *
+ * * When RTO fires.
+ * * When a packet is retransmitted.
+ * * When the connection terminates.
+ * * When a packet is sent.
+ * * When a packet is received.
+ * Return
+ * Code **-EINVAL** if the socket is not a full TCP socket;
+ * otherwise, a positive number containing the bits that could not
+ * be set is returned (which comes down to 0 if all bits were set
+ * as required).
+ *
+ * int bpf_msg_redirect_map(struct sk_msg_buff *msg, struct bpf_map *map, u32 key, u64 flags)
+ * Description
+ * This helper is used in programs implementing policies at the
+ * socket level. If the message *msg* is allowed to pass (i.e. if
+ * the verdict eBPF program returns **SK_PASS**), redirect it to
+ * the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKMAP**) at index *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ * Return
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_msg_apply_bytes(struct sk_msg_buff *msg, u32 bytes)
+ * Description
+ * For socket policies, apply the verdict of the eBPF program to
+ * the next *bytes* (number of bytes) of message *msg*.
+ *
+ * For example, this helper can be used in the following cases:
+ *
+ * * A single **sendmsg**\ () or **sendfile**\ () system call
+ * contains multiple logical messages that the eBPF program is
+ * supposed to read and for which it should apply a verdict.
+ * * An eBPF program only cares to read the first *bytes* of a
+ * *msg*. If the message has a large payload, then setting up
+ * and calling the eBPF program repeatedly for all bytes, even
+ * though the verdict is already known, would create unnecessary
+ * overhead.
+ *
+ * When called from within an eBPF program, the helper sets a
+ * counter internal to the BPF infrastructure, that is used to
+ * apply the last verdict to the next *bytes*. If *bytes* is
+ * smaller than the current data being processed from a
+ * **sendmsg**\ () or **sendfile**\ () system call, the first
+ * *bytes* will be sent and the eBPF program will be re-run with
+ * the pointer for start of data pointing to byte number *bytes*
+ * **+ 1**. If *bytes* is larger than the current data being
+ * processed, then the eBPF verdict will be applied to multiple
+ * **sendmsg**\ () or **sendfile**\ () calls until *bytes* are
+ * consumed.
+ *
+ * Note that if a socket closes with the internal counter holding
+ * a non-zero value, this is not a problem because data is not
+ * being buffered for *bytes* and is sent as it is received.
+ * Return
+ * 0
+ *
+ * int bpf_msg_cork_bytes(struct sk_msg_buff *msg, u32 bytes)
+ * Description
+ * For socket policies, prevent the execution of the verdict eBPF
+ * program for message *msg* until *bytes* (byte number) have been
+ * accumulated.
+ *
+ * This can be used when one needs a specific number of bytes
+ * before a verdict can be assigned, even if the data spans
+ * multiple **sendmsg**\ () or **sendfile**\ () calls. The extreme
+ * case would be a user calling **sendmsg**\ () repeatedly with
+ * 1-byte long message segments. Obviously, this is bad for
+ * performance, but it is still valid. If the eBPF program needs
+ * *bytes* bytes to validate a header, this helper can be used to
+ * prevent the eBPF program to be called again until *bytes* have
+ * been accumulated.
+ * Return
+ * 0
+ *
+ * int bpf_msg_pull_data(struct sk_msg_buff *msg, u32 start, u32 end, u64 flags)
+ * Description
+ * For socket policies, pull in non-linear data from user space
+ * for *msg* and set pointers *msg*\ **->data** and *msg*\
+ * **->data_end** to *start* and *end* bytes offsets into *msg*,
+ * respectively.
+ *
+ * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
+ * *msg* it can only parse data that the (**data**, **data_end**)
+ * pointers have already consumed. For **sendmsg**\ () hooks this
+ * is likely the first scatterlist element. But for calls relying
+ * on the **sendpage** handler (e.g. **sendfile**\ ()) this will
+ * be the range (**0**, **0**) because the data is shared with
+ * user space and by default the objective is to avoid allowing
+ * user space to modify data while (or after) eBPF verdict is
+ * being decided. This helper can be used to pull in data and to
+ * set the start and end pointer to given values. Data will be
+ * copied if necessary (i.e. if data was not linear and if start
+ * and end pointers do not point to the same chunk).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_bind(struct bpf_sock_addr *ctx, struct sockaddr *addr, int addr_len)
+ * Description
+ * Bind the socket associated to *ctx* to the address pointed by
+ * *addr*, of length *addr_len*. This allows for making outgoing
+ * connection from the desired IP address, which can be useful for
+ * example when all processes inside a cgroup should use one
+ * single IP address on a host that has multiple IP configured.
+ *
+ * This helper works for IPv4 and IPv6, TCP and UDP sockets. The
+ * domain (*addr*\ **->sa_family**) must be **AF_INET** (or
+ * **AF_INET6**). Looking for a free port to bind to can be
+ * expensive, therefore binding to port is not permitted by the
+ * helper: *addr*\ **->sin_port** (or **sin6_port**, respectively)
+ * must be set to zero.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_xdp_adjust_tail(struct xdp_buff *xdp_md, int delta)
+ * Description
+ * Adjust (move) *xdp_md*\ **->data_end** by *delta* bytes. It is
+ * only possible to shrink the packet as of this writing,
+ * therefore *delta* must be a negative integer.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_skb_get_xfrm_state(struct sk_buff *skb, u32 index, struct bpf_xfrm_state *xfrm_state, u32 size, u64 flags)
+ * Description
+ * Retrieve the XFRM state (IP transform framework, see also
+ * **ip-xfrm(8)**) at *index* in XFRM "security path" for *skb*.
+ *
+ * The retrieved value is stored in the **struct bpf_xfrm_state**
+ * pointed by *xfrm_state* and of length *size*.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_XFRM** configuration option.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_get_stack(void *ctx, void *buf, u32 size, u64 flags)
+ * Description
+ * Return a user or a kernel stack in bpf program provided buffer.
+ * To achieve this, the helper needs *ctx*, which is a pointer
+ * to the context on which the tracing program is executed.
+ * To store the stacktrace, the bpf program provides *buf* with
+ * a nonnegative *size*.
+ *
+ * The last argument, *flags*, holds the number of stack frames to
+ * skip (from 0 to 255), masked with
+ * **BPF_F_SKIP_FIELD_MASK**. The next bits can be used to set
+ * the following flags:
+ *
+ * **BPF_F_USER_STACK**
+ * Collect a user space stack instead of a kernel stack.
+ * **BPF_F_USER_BUILD_ID**
+ * Collect buildid+offset instead of ips for user stack,
+ * only valid if **BPF_F_USER_STACK** is also specified.
+ *
+ * **bpf_get_stack**\ () can collect up to
+ * **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject
+ * to sufficient large buffer size. Note that
+ * this limit can be controlled with the **sysctl** program, and
+ * that it should be manually increased in order to profile long
+ * user stacks (such as stacks for Java programs). To do so, use:
+ *
+ * ::
+ *
+ * # sysctl kernel.perf_event_max_stack=<new value>
+ * Return
+ * A non-negative value equal to or less than *size* on success,
+ * or a negative error in case of failure.
+ *
+ * int bpf_skb_load_bytes_relative(const void *skb, u32 offset, void *to, u32 len, u32 start_header)
+ * Description
+ * This helper is similar to **bpf_skb_load_bytes**\ () in that
+ * it provides an easy way to load *len* bytes from *offset*
+ * from the packet associated to *skb*, into the buffer pointed
+ * by *to*. The difference to **bpf_skb_load_bytes**\ () is that
+ * a fifth argument *start_header* exists in order to select a
+ * base offset to start from. *start_header* can be one of:
+ *
+ * **BPF_HDR_START_MAC**
+ * Base offset to load data from is *skb*'s mac header.
+ * **BPF_HDR_START_NET**
+ * Base offset to load data from is *skb*'s network header.
+ *
+ * In general, "direct packet access" is the preferred method to
+ * access packet data, however, this helper is in particular useful
+ * in socket filters where *skb*\ **->data** does not always point
+ * to the start of the mac header and where "direct packet access"
+ * is not available.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_fib_lookup(void *ctx, struct bpf_fib_lookup *params, int plen, u32 flags)
+ * Description
+ * Do FIB lookup in kernel tables using parameters in *params*.
+ * If lookup is successful and result shows packet is to be
+ * forwarded, the neighbor tables are searched for the nexthop.
+ * If successful (ie., FIB lookup shows forwarding and nexthop
+ * is resolved), the nexthop address is returned in ipv4_dst
+ * or ipv6_dst based on family, smac is set to mac address of
+ * egress device, dmac is set to nexthop mac address, rt_metric
+ * is set to metric from route (IPv4/IPv6 only), and ifindex
+ * is set to the device index of the nexthop from the FIB lookup.
+ *
+ * *plen* argument is the size of the passed in struct.
+ * *flags* argument can be a combination of one or more of the
+ * following values:
+ *
+ * **BPF_FIB_LOOKUP_DIRECT**
+ * Do a direct table lookup vs full lookup using FIB
+ * rules.
+ * **BPF_FIB_LOOKUP_OUTPUT**
+ * Perform lookup from an egress perspective (default is
+ * ingress).
+ *
+ * *ctx* is either **struct xdp_md** for XDP programs or
+ * **struct sk_buff** tc cls_act programs.
+ * Return
+ * * < 0 if any input argument is invalid
+ * * 0 on success (packet is forwarded, nexthop neighbor exists)
+ * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the
+ * packet is not forwarded or needs assist from full stack
+ *
+ * int bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags)
+ * Description
+ * Add an entry to, or update a sockhash *map* referencing sockets.
+ * The *skops* is used as a new value for the entry associated to
+ * *key*. *flags* is one of:
+ *
+ * **BPF_NOEXIST**
+ * The entry for *key* must not exist in the map.
+ * **BPF_EXIST**
+ * The entry for *key* must already exist in the map.
+ * **BPF_ANY**
+ * No condition on the existence of the entry for *key*.
+ *
+ * If the *map* has eBPF programs (parser and verdict), those will
+ * be inherited by the socket being added. If the socket is
+ * already attached to eBPF programs, this results in an error.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_redirect_hash(struct sk_msg_buff *msg, struct bpf_map *map, void *key, u64 flags)
+ * Description
+ * This helper is used in programs implementing policies at the
+ * socket level. If the message *msg* is allowed to pass (i.e. if
+ * the verdict eBPF program returns **SK_PASS**), redirect it to
+ * the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress path otherwise). This is the only flag supported for now.
+ * Return
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_sk_redirect_hash(struct sk_buff *skb, struct bpf_map *map, void *key, u64 flags)
+ * Description
+ * This helper is used in programs implementing policies at the
+ * skb socket level. If the sk_buff *skb* is allowed to pass (i.e.
+ * if the verdeict eBPF program returns **SK_PASS**), redirect it
+ * to the socket referenced by *map* (of type
+ * **BPF_MAP_TYPE_SOCKHASH**) using hash *key*. Both ingress and
+ * egress interfaces can be used for redirection. The
+ * **BPF_F_INGRESS** value in *flags* is used to make the
+ * distinction (ingress path is selected if the flag is present,
+ * egress otherwise). This is the only flag supported for now.
+ * Return
+ * **SK_PASS** on success, or **SK_DROP** on error.
+ *
+ * int bpf_lwt_push_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len)
+ * Description
+ * Encapsulate the packet associated to *skb* within a Layer 3
+ * protocol header. This header is provided in the buffer at
+ * address *hdr*, with *len* its size in bytes. *type* indicates
+ * the protocol of the header and can be one of:
+ *
+ * **BPF_LWT_ENCAP_SEG6**
+ * IPv6 encapsulation with Segment Routing Header
+ * (**struct ipv6_sr_hdr**). *hdr* only contains the SRH,
+ * the IPv6 header is computed by the kernel.
+ * **BPF_LWT_ENCAP_SEG6_INLINE**
+ * Only works if *skb* contains an IPv6 packet. Insert a
+ * Segment Routing Header (**struct ipv6_sr_hdr**) inside
+ * the IPv6 header.
+ * **BPF_LWT_ENCAP_IP**
+ * IP encapsulation (GRE/GUE/IPIP/etc). The outer header
+ * must be IPv4 or IPv6, followed by zero or more
+ * additional headers, up to **LWT_BPF_MAX_HEADROOM**
+ * total bytes in all prepended headers. Please note that
+ * if **skb_is_gso**\ (*skb*) is true, no more than two
+ * headers can be prepended, and the inner header, if
+ * present, should be either GRE or UDP/GUE.
+ *
+ * **BPF_LWT_ENCAP_SEG6**\ \* types can be called by BPF programs
+ * of type **BPF_PROG_TYPE_LWT_IN**; **BPF_LWT_ENCAP_IP** type can
+ * be called by bpf programs of types **BPF_PROG_TYPE_LWT_IN** and
+ * **BPF_PROG_TYPE_LWT_XMIT**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_lwt_seg6_store_bytes(struct sk_buff *skb, u32 offset, const void *from, u32 len)
+ * Description
+ * Store *len* bytes from address *from* into the packet
+ * associated to *skb*, at *offset*. Only the flags, tag and TLVs
+ * inside the outermost IPv6 Segment Routing Header can be
+ * modified through this helper.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_lwt_seg6_adjust_srh(struct sk_buff *skb, u32 offset, s32 delta)
+ * Description
+ * Adjust the size allocated to TLVs in the outermost IPv6
+ * Segment Routing Header contained in the packet associated to
+ * *skb*, at position *offset* by *delta* bytes. Only offsets
+ * after the segments are accepted. *delta* can be as well
+ * positive (growing) as negative (shrinking).
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_lwt_seg6_action(struct sk_buff *skb, u32 action, void *param, u32 param_len)
+ * Description
+ * Apply an IPv6 Segment Routing action of type *action* to the
+ * packet associated to *skb*. Each action takes a parameter
+ * contained at address *param*, and of length *param_len* bytes.
+ * *action* can be one of:
+ *
+ * **SEG6_LOCAL_ACTION_END_X**
+ * End.X action: Endpoint with Layer-3 cross-connect.
+ * Type of *param*: **struct in6_addr**.
+ * **SEG6_LOCAL_ACTION_END_T**
+ * End.T action: Endpoint with specific IPv6 table lookup.
+ * Type of *param*: **int**.
+ * **SEG6_LOCAL_ACTION_END_B6**
+ * End.B6 action: Endpoint bound to an SRv6 policy.
+ * Type of *param*: **struct ipv6_sr_hdr**.
+ * **SEG6_LOCAL_ACTION_END_B6_ENCAP**
+ * End.B6.Encap action: Endpoint bound to an SRv6
+ * encapsulation policy.
+ * Type of *param*: **struct ipv6_sr_hdr**.
+ *
+ * A call to this helper is susceptible to change the underlying
+ * packet buffer. Therefore, at load time, all checks on pointers
+ * previously done by the verifier are invalidated and must be
+ * performed again, if the helper is used in combination with
+ * direct packet access.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_rc_repeat(void *ctx)
+ * Description
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded repeat key message. This delays
+ * the generation of a key up event for previously generated
+ * key down event.
+ *
+ * Some IR protocols like NEC have a special IR message for
+ * repeating last button, for when a button is held down.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ * Return
+ * 0
+ *
+ * int bpf_rc_keydown(void *ctx, u32 protocol, u64 scancode, u32 toggle)
+ * Description
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded key press with *scancode*,
+ * *toggle* value in the given *protocol*. The scancode will be
+ * translated to a keycode using the rc keymap, and reported as
+ * an input key down event. After a period a key up event is
+ * generated. This period can be extended by calling either
+ * **bpf_rc_keydown**\ () again with the same values, or calling
+ * **bpf_rc_repeat**\ ().
+ *
+ * Some protocols include a toggle bit, in case the button was
+ * released and pressed again between consecutive scancodes.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * The *protocol* is the decoded protocol number (see
+ * **enum rc_proto** for some predefined values).
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ * Return
+ * 0
+ *
+ * u64 bpf_skb_cgroup_id(struct sk_buff *skb)
+ * Description
+ * Return the cgroup v2 id of the socket associated with the *skb*.
+ * This is roughly similar to the **bpf_get_cgroup_classid**\ ()
+ * helper for cgroup v1 by providing a tag resp. identifier that
+ * can be matched on or used for map lookups e.g. to implement
+ * policy. The cgroup v2 id of a given path in the hierarchy is
+ * exposed in user space through the f_handle API in order to get
+ * to the same 64-bit id.
+ *
+ * This helper can be used on TC egress path, but not on ingress,
+ * and is available only if the kernel was compiled with the
+ * **CONFIG_SOCK_CGROUP_DATA** configuration option.
+ * Return
+ * The id is returned or 0 in case the id could not be retrieved.
+ *
+ * u64 bpf_get_current_cgroup_id(void)
+ * Return
+ * A 64-bit integer containing the current cgroup id based
+ * on the cgroup within which the current task is running.
+ *
+ * void *bpf_get_local_storage(void *map, u64 flags)
+ * Description
+ * Get the pointer to the local storage area.
+ * The type and the size of the local storage is defined
+ * by the *map* argument.
+ * The *flags* meaning is specific for each map type,
+ * and has to be 0 for cgroup local storage.
+ *
+ * Depending on the BPF program type, a local storage area
+ * can be shared between multiple instances of the BPF program,
+ * running simultaneously.
+ *
+ * A user should care about the synchronization by himself.
+ * For example, by using the **BPF_STX_XADD** instruction to alter
+ * the shared data.
+ * Return
+ * A pointer to the local storage area.
+ *
+ * int bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags)
+ * Description
+ * Select a **SO_REUSEPORT** socket from a
+ * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*.
+ * It checks the selected socket is matching the incoming
+ * request in the socket buffer.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * u64 bpf_skb_ancestor_cgroup_id(struct sk_buff *skb, int ancestor_level)
+ * Description
+ * Return id of cgroup v2 that is ancestor of cgroup associated
+ * with the *skb* at the *ancestor_level*. The root cgroup is at
+ * *ancestor_level* zero and each step down the hierarchy
+ * increments the level. If *ancestor_level* == level of cgroup
+ * associated with *skb*, then return value will be same as that
+ * of **bpf_skb_cgroup_id**\ ().
+ *
+ * The helper is useful to implement policies based on cgroups
+ * that are upper in hierarchy than immediate cgroup associated
+ * with *skb*.
+ *
+ * The format of returned id and helper limitations are same as in
+ * **bpf_skb_cgroup_id**\ ().
+ * Return
+ * The id is returned or 0 in case the id could not be retrieved.
+ *
+ * struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
+ * Description
+ * Look for TCP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * The *ctx* should point to the context of the program, such as
+ * the skb or socket (depending on the hook in use). This is used
+ * to determine the base network namespace for the lookup.
+ *
+ * *tuple_size* must be one of:
+ *
+ * **sizeof**\ (*tuple*\ **->ipv4**)
+ * Look for an IPv4 socket.
+ * **sizeof**\ (*tuple*\ **->ipv6**)
+ * Look for an IPv6 socket.
+ *
+ * If the *netns* is a negative signed 32-bit integer, then the
+ * socket lookup table in the netns associated with the *ctx* will
+ * will be used. For the TC hooks, this is the netns of the device
+ * in the skb. For socket hooks, this is the netns of the socket.
+ * If *netns* is any other signed 32-bit value greater than or
+ * equal to zero then it specifies the ID of the netns relative to
+ * the netns associated with the *ctx*. *netns* values beyond the
+ * range of 32-bit integers are reserved for future use.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ * Return
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ *
+ * struct bpf_sock *bpf_sk_lookup_udp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
+ * Description
+ * Look for UDP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * The *ctx* should point to the context of the program, such as
+ * the skb or socket (depending on the hook in use). This is used
+ * to determine the base network namespace for the lookup.
+ *
+ * *tuple_size* must be one of:
+ *
+ * **sizeof**\ (*tuple*\ **->ipv4**)
+ * Look for an IPv4 socket.
+ * **sizeof**\ (*tuple*\ **->ipv6**)
+ * Look for an IPv6 socket.
+ *
+ * If the *netns* is a negative signed 32-bit integer, then the
+ * socket lookup table in the netns associated with the *ctx* will
+ * will be used. For the TC hooks, this is the netns of the device
+ * in the skb. For socket hooks, this is the netns of the socket.
+ * If *netns* is any other signed 32-bit value greater than or
+ * equal to zero then it specifies the ID of the netns relative to
+ * the netns associated with the *ctx*. *netns* values beyond the
+ * range of 32-bit integers are reserved for future use.
+ *
+ * All values for *flags* are reserved for future usage, and must
+ * be left at zero.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ * Return
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ *
+ * int bpf_sk_release(struct bpf_sock *sock)
+ * Description
+ * Release the reference held by *sock*. *sock* must be a
+ * non-**NULL** pointer that was returned from
+ * **bpf_sk_lookup_xxx**\ ().
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_push_elem(struct bpf_map *map, const void *value, u64 flags)
+ * Description
+ * Push an element *value* in *map*. *flags* is one of:
+ *
+ * **BPF_EXIST**
+ * If the queue/stack is full, the oldest element is
+ * removed to make room for this.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_pop_elem(struct bpf_map *map, void *value)
+ * Description
+ * Pop an element from *map*.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_map_peek_elem(struct bpf_map *map, void *value)
+ * Description
+ * Get an element from *map* without removing it.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_push_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags)
+ * Description
+ * For socket policies, insert *len* bytes into *msg* at offset
+ * *start*.
+ *
+ * If a program of type **BPF_PROG_TYPE_SK_MSG** is run on a
+ * *msg* it may want to insert metadata or options into the *msg*.
+ * This can later be read and used by any of the lower layer BPF
+ * hooks.
+ *
+ * This helper may fail if under memory pressure (a malloc
+ * fails) in these cases BPF programs will get an appropriate
+ * error and BPF programs will need to handle them.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_msg_pop_data(struct sk_msg_buff *msg, u32 start, u32 len, u64 flags)
+ * Description
+ * Will remove *len* bytes from a *msg* starting at byte *start*.
+ * This may result in **ENOMEM** errors under certain situations if
+ * an allocation and copy are required due to a full ring buffer.
+ * However, the helper will try to avoid doing the allocation
+ * if possible. Other errors can occur if input parameters are
+ * invalid either due to *start* byte not being valid part of *msg*
+ * payload and/or *pop* value being to large.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_rc_pointer_rel(void *ctx, s32 rel_x, s32 rel_y)
+ * Description
+ * This helper is used in programs implementing IR decoding, to
+ * report a successfully decoded pointer movement.
+ *
+ * The *ctx* should point to the lirc sample as passed into
+ * the program.
+ *
+ * This helper is only available is the kernel was compiled with
+ * the **CONFIG_BPF_LIRC_MODE2** configuration option set to
+ * "**y**".
+ * Return
+ * 0
+ *
+ * int bpf_spin_lock(struct bpf_spin_lock *lock)
+ * Description
+ * Acquire a spinlock represented by the pointer *lock*, which is
+ * stored as part of a value of a map. Taking the lock allows to
+ * safely update the rest of the fields in that value. The
+ * spinlock can (and must) later be released with a call to
+ * **bpf_spin_unlock**\ (\ *lock*\ ).
+ *
+ * Spinlocks in BPF programs come with a number of restrictions
+ * and constraints:
+ *
+ * * **bpf_spin_lock** objects are only allowed inside maps of
+ * types **BPF_MAP_TYPE_HASH** and **BPF_MAP_TYPE_ARRAY** (this
+ * list could be extended in the future).
+ * * BTF description of the map is mandatory.
+ * * The BPF program can take ONE lock at a time, since taking two
+ * or more could cause dead locks.
+ * * Only one **struct bpf_spin_lock** is allowed per map element.
+ * * When the lock is taken, calls (either BPF to BPF or helpers)
+ * are not allowed.
+ * * The **BPF_LD_ABS** and **BPF_LD_IND** instructions are not
+ * allowed inside a spinlock-ed region.
+ * * The BPF program MUST call **bpf_spin_unlock**\ () to release
+ * the lock, on all execution paths, before it returns.
+ * * The BPF program can access **struct bpf_spin_lock** only via
+ * the **bpf_spin_lock**\ () and **bpf_spin_unlock**\ ()
+ * helpers. Loading or storing data into the **struct
+ * bpf_spin_lock** *lock*\ **;** field of a map is not allowed.
+ * * To use the **bpf_spin_lock**\ () helper, the BTF description
+ * of the map value must be a struct and have **struct
+ * bpf_spin_lock** *anyname*\ **;** field at the top level.
+ * Nested lock inside another struct is not allowed.
+ * * The **struct bpf_spin_lock** *lock* field in a map value must
+ * be aligned on a multiple of 4 bytes in that value.
+ * * Syscall with command **BPF_MAP_LOOKUP_ELEM** does not copy
+ * the **bpf_spin_lock** field to user space.
+ * * Syscall with command **BPF_MAP_UPDATE_ELEM**, or update from
+ * a BPF program, do not update the **bpf_spin_lock** field.
+ * * **bpf_spin_lock** cannot be on the stack or inside a
+ * networking packet (it can only be inside of a map values).
+ * * **bpf_spin_lock** is available to root only.
+ * * Tracing programs and socket filter programs cannot use
+ * **bpf_spin_lock**\ () due to insufficient preemption checks
+ * (but this may change in the future).
+ * * **bpf_spin_lock** is not allowed in inner maps of map-in-map.
+ * Return
+ * 0
+ *
+ * int bpf_spin_unlock(struct bpf_spin_lock *lock)
+ * Description
+ * Release the *lock* previously locked by a call to
+ * **bpf_spin_lock**\ (\ *lock*\ ).
+ * Return
+ * 0
+ *
+ * struct bpf_sock *bpf_sk_fullsock(struct bpf_sock *sk)
+ * Description
+ * This helper gets a **struct bpf_sock** pointer such
+ * that all the fields in this **bpf_sock** can be accessed.
+ * Return
+ * A **struct bpf_sock** pointer on success, or **NULL** in
+ * case of failure.
+ *
+ * struct bpf_tcp_sock *bpf_tcp_sock(struct bpf_sock *sk)
+ * Description
+ * This helper gets a **struct bpf_tcp_sock** pointer from a
+ * **struct bpf_sock** pointer.
+ * Return
+ * A **struct bpf_tcp_sock** pointer on success, or **NULL** in
+ * case of failure.
+ *
+ * int bpf_skb_ecn_set_ce(struct sk_buff *skb)
+ * Description
+ * Set ECN (Explicit Congestion Notification) field of IP header
+ * to **CE** (Congestion Encountered) if current value is **ECT**
+ * (ECN Capable Transport). Otherwise, do nothing. Works with IPv6
+ * and IPv4.
+ * Return
+ * 1 if the **CE** flag is set (either by the current helper call
+ * or because it was already present), 0 if it is not set.
+ *
+ * struct bpf_sock *bpf_get_listener_sock(struct bpf_sock *sk)
+ * Description
+ * Return a **struct bpf_sock** pointer in **TCP_LISTEN** state.
+ * **bpf_sk_release**\ () is unnecessary and not allowed.
+ * Return
+ * A **struct bpf_sock** pointer on success, or **NULL** in
+ * case of failure.
+ *
+ * struct bpf_sock *bpf_skc_lookup_tcp(void *ctx, struct bpf_sock_tuple *tuple, u32 tuple_size, u64 netns, u64 flags)
+ * Description
+ * Look for TCP socket matching *tuple*, optionally in a child
+ * network namespace *netns*. The return value must be checked,
+ * and if non-**NULL**, released via **bpf_sk_release**\ ().
+ *
+ * This function is identical to **bpf_sk_lookup_tcp**\ (), except
+ * that it also returns timewait or request sockets. Use
+ * **bpf_sk_fullsock**\ () or **bpf_tcp_sock**\ () to access the
+ * full structure.
+ *
+ * This helper is available only if the kernel was compiled with
+ * **CONFIG_NET** configuration option.
+ * Return
+ * Pointer to **struct bpf_sock**, or **NULL** in case of failure.
+ * For sockets with reuseport option, the **struct bpf_sock**
+ * result is from *reuse*\ **->socks**\ [] using the hash of the
+ * tuple.
+ *
+ * int bpf_tcp_check_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
+ * Description
+ * Check whether *iph* and *th* contain a valid SYN cookie ACK for
+ * the listening socket in *sk*.
+ *
+ * *iph* points to the start of the IPv4 or IPv6 header, while
+ * *iph_len* contains **sizeof**\ (**struct iphdr**) or
+ * **sizeof**\ (**struct ip6hdr**).
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains **sizeof**\ (**struct tcphdr**).
+ *
+ * Return
+ * 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative
+ * error otherwise.
+ *
+ * int bpf_sysctl_get_name(struct bpf_sysctl *ctx, char *buf, size_t buf_len, u64 flags)
+ * Description
+ * Get name of sysctl in /proc/sys/ and copy it into provided by
+ * program buffer *buf* of size *buf_len*.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ *
+ * If *flags* is zero, full name (e.g. "net/ipv4/tcp_mem") is
+ * copied. Use **BPF_F_SYSCTL_BASE_NAME** flag to copy base name
+ * only (e.g. "tcp_mem").
+ * Return
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ *
+ * int bpf_sysctl_get_current_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
+ * Description
+ * Get current value of sysctl as it is presented in /proc/sys
+ * (incl. newline, etc), and copy it as a string into provided
+ * by program buffer *buf* of size *buf_len*.
+ *
+ * The whole value is copied, no matter what file position user
+ * space issued e.g. sys_read at.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ * Return
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ *
+ * **-EINVAL** if current value was unavailable, e.g. because
+ * sysctl is uninitialized and read returns -EIO for it.
+ *
+ * int bpf_sysctl_get_new_value(struct bpf_sysctl *ctx, char *buf, size_t buf_len)
+ * Description
+ * Get new value being written by user space to sysctl (before
+ * the actual write happens) and copy it as a string into
+ * provided by program buffer *buf* of size *buf_len*.
+ *
+ * User space may write new value at file position > 0.
+ *
+ * The buffer is always NUL terminated, unless it's zero-sized.
+ * Return
+ * Number of character copied (not including the trailing NUL).
+ *
+ * **-E2BIG** if the buffer wasn't big enough (*buf* will contain
+ * truncated name in this case).
+ *
+ * **-EINVAL** if sysctl is being read.
+ *
+ * int bpf_sysctl_set_new_value(struct bpf_sysctl *ctx, const char *buf, size_t buf_len)
+ * Description
+ * Override new value being written by user space to sysctl with
+ * value provided by program in buffer *buf* of size *buf_len*.
+ *
+ * *buf* should contain a string in same form as provided by user
+ * space on sysctl write.
+ *
+ * User space may write new value at file position > 0. To override
+ * the whole sysctl value file position should be set to zero.
+ * Return
+ * 0 on success.
+ *
+ * **-E2BIG** if the *buf_len* is too big.
+ *
+ * **-EINVAL** if sysctl is being read.
+ *
+ * int bpf_strtol(const char *buf, size_t buf_len, u64 flags, long *res)
+ * Description
+ * Convert the initial part of the string from buffer *buf* of
+ * size *buf_len* to a long integer according to the given base
+ * and save the result in *res*.
+ *
+ * The string may begin with an arbitrary amount of white space
+ * (as determined by **isspace**\ (3)) followed by a single
+ * optional '**-**' sign.
+ *
+ * Five least significant bits of *flags* encode base, other bits
+ * are currently unused.
+ *
+ * Base must be either 8, 10, 16 or 0 to detect it automatically
+ * similar to user space **strtol**\ (3).
+ * Return
+ * Number of characters consumed on success. Must be positive but
+ * no more than *buf_len*.
+ *
+ * **-EINVAL** if no valid digits were found or unsupported base
+ * was provided.
+ *
+ * **-ERANGE** if resulting value was out of range.
+ *
+ * int bpf_strtoul(const char *buf, size_t buf_len, u64 flags, unsigned long *res)
+ * Description
+ * Convert the initial part of the string from buffer *buf* of
+ * size *buf_len* to an unsigned long integer according to the
+ * given base and save the result in *res*.
+ *
+ * The string may begin with an arbitrary amount of white space
+ * (as determined by **isspace**\ (3)).
+ *
+ * Five least significant bits of *flags* encode base, other bits
+ * are currently unused.
+ *
+ * Base must be either 8, 10, 16 or 0 to detect it automatically
+ * similar to user space **strtoul**\ (3).
+ * Return
+ * Number of characters consumed on success. Must be positive but
+ * no more than *buf_len*.
+ *
+ * **-EINVAL** if no valid digits were found or unsupported base
+ * was provided.
+ *
+ * **-ERANGE** if resulting value was out of range.
+ *
+ * void *bpf_sk_storage_get(struct bpf_map *map, struct bpf_sock *sk, void *value, u64 flags)
+ * Description
+ * Get a bpf-local-storage from a *sk*.
+ *
+ * Logically, it could be thought of getting the value from
+ * a *map* with *sk* as the **key**. From this
+ * perspective, the usage is not much different from
+ * **bpf_map_lookup_elem**\ (*map*, **&**\ *sk*) except this
+ * helper enforces the key must be a full socket and the map must
+ * be a **BPF_MAP_TYPE_SK_STORAGE** also.
+ *
+ * Underneath, the value is stored locally at *sk* instead of
+ * the *map*. The *map* is used as the bpf-local-storage
+ * "type". The bpf-local-storage "type" (i.e. the *map*) is
+ * searched against all bpf-local-storages residing at *sk*.
+ *
+ * An optional *flags* (**BPF_SK_STORAGE_GET_F_CREATE**) can be
+ * used such that a new bpf-local-storage will be
+ * created if one does not exist. *value* can be used
+ * together with **BPF_SK_STORAGE_GET_F_CREATE** to specify
+ * the initial value of a bpf-local-storage. If *value* is
+ * **NULL**, the new bpf-local-storage will be zero initialized.
+ * Return
+ * A bpf-local-storage pointer is returned on success.
+ *
+ * **NULL** if not found or there was an error in adding
+ * a new bpf-local-storage.
+ *
+ * int bpf_sk_storage_delete(struct bpf_map *map, struct bpf_sock *sk)
+ * Description
+ * Delete a bpf-local-storage from a *sk*.
+ * Return
+ * 0 on success.
+ *
+ * **-ENOENT** if the bpf-local-storage cannot be found.
+ *
+ * int bpf_send_signal(u32 sig)
+ * Description
+ * Send signal *sig* to the current task.
+ * Return
+ * 0 on success or successfully queued.
+ *
+ * **-EBUSY** if work queue under nmi is full.
+ *
+ * **-EINVAL** if *sig* is invalid.
+ *
+ * **-EPERM** if no permission to send the *sig*.
+ *
+ * **-EAGAIN** if bpf program can try again.
+ *
+ * s64 bpf_tcp_gen_syncookie(struct bpf_sock *sk, void *iph, u32 iph_len, struct tcphdr *th, u32 th_len)
+ * Description
+ * Try to issue a SYN cookie for the packet with corresponding
+ * IP/TCP headers, *iph* and *th*, on the listening socket in *sk*.
+ *
+ * *iph* points to the start of the IPv4 or IPv6 header, while
+ * *iph_len* contains **sizeof**\ (**struct iphdr**) or
+ * **sizeof**\ (**struct ip6hdr**).
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains the length of the TCP header.
+ *
+ * Return
+ * On success, lower 32 bits hold the generated SYN cookie in
+ * followed by 16 bits which hold the MSS value for that cookie,
+ * and the top 16 bits are unused.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** SYN cookie cannot be issued due to error
+ *
+ * **-ENOENT** SYN cookie should not be issued (no SYN flood)
+ *
+ * **-EOPNOTSUPP** kernel configuration does not enable SYN cookies
+ *
+ * **-EPROTONOSUPPORT** IP packet version is not 4 or 6
+ *
+ * int bpf_skb_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size)
+ * Description
+ * Write raw *data* blob into a special BPF perf event held by
+ * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf
+ * event must have the following attributes: **PERF_SAMPLE_RAW**
+ * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and
+ * **PERF_COUNT_SW_BPF_OUTPUT** as **config**.
+ *
+ * The *flags* are used to indicate the index in *map* for which
+ * the value must be put, masked with **BPF_F_INDEX_MASK**.
+ * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU**
+ * to indicate that the index of the current CPU core should be
+ * used.
+ *
+ * The value to write, of *size*, is passed through eBPF stack and
+ * pointed by *data*.
+ *
+ * *ctx* is a pointer to in-kernel struct sk_buff.
+ *
+ * This helper is similar to **bpf_perf_event_output**\ () but
+ * restricted to raw_tracepoint bpf programs.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_probe_read_user(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * Safely attempt to read *size* bytes from user space address
+ * *unsafe_ptr* and store the data in *dst*.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * Safely attempt to read *size* bytes from kernel space address
+ * *unsafe_ptr* and store the data in *dst*.
+ * Return
+ * 0 on success, or a negative error in case of failure.
+ *
+ * int bpf_probe_read_user_str(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * Copy a NUL terminated string from an unsafe user address
+ * *unsafe_ptr* to *dst*. The *size* should include the
+ * terminating NUL byte. In case the string length is smaller than
+ * *size*, the target is not padded with further NUL bytes. If the
+ * string length is larger than *size*, just *size*-1 bytes are
+ * copied and the last byte is set to NUL.
+ *
+ * On success, the length of the copied string is returned. This
+ * makes this helper useful in tracing programs for reading
+ * strings, and more importantly to get its length at runtime. See
+ * the following snippet:
+ *
+ * ::
+ *
+ * SEC("kprobe/sys_open")
+ * void bpf_sys_open(struct pt_regs *ctx)
+ * {
+ * char buf[PATHLEN]; // PATHLEN is defined to 256
+ * int res = bpf_probe_read_user_str(buf, sizeof(buf),
+ * ctx->di);
+ *
+ * // Consume buf, for example push it to
+ * // userspace via bpf_perf_event_output(); we
+ * // can use res (the string length) as event
+ * // size, after checking its boundaries.
+ * }
+ *
+ * In comparison, using **bpf_probe_read_user()** helper here
+ * instead to read the string would require to estimate the length
+ * at compile time, and would often result in copying more memory
+ * than necessary.
+ *
+ * Another useful use case is when parsing individual process
+ * arguments or individual environment variables navigating
+ * *current*\ **->mm->arg_start** and *current*\
+ * **->mm->env_start**: using this helper and the return value,
+ * one can quickly iterate at the right offset of the memory area.
+ * Return
+ * On success, the strictly positive length of the string,
+ * including the trailing NUL character. On error, a negative
+ * value.
+ *
+ * int bpf_probe_read_kernel_str(void *dst, u32 size, const void *unsafe_ptr)
+ * Description
+ * Copy a NUL terminated string from an unsafe kernel address *unsafe_ptr*
+ * to *dst*. Same semantics as with bpf_probe_read_user_str() apply.
+ * Return
+ * On success, the strictly positive length of the string, including
+ * the trailing NUL character. On error, a negative value.
+ */
+#define __BPF_FUNC_MAPPER(FN) \
+ FN(unspec), \
+ FN(map_lookup_elem), \
+ FN(map_update_elem), \
+ FN(map_delete_elem), \
+ FN(probe_read), \
+ FN(ktime_get_ns), \
+ FN(trace_printk), \
+ FN(get_prandom_u32), \
+ FN(get_smp_processor_id), \
+ FN(skb_store_bytes), \
+ FN(l3_csum_replace), \
+ FN(l4_csum_replace), \
+ FN(tail_call), \
+ FN(clone_redirect), \
+ FN(get_current_pid_tgid), \
+ FN(get_current_uid_gid), \
+ FN(get_current_comm), \
+ FN(get_cgroup_classid), \
+ FN(skb_vlan_push), \
+ FN(skb_vlan_pop), \
+ FN(skb_get_tunnel_key), \
+ FN(skb_set_tunnel_key), \
+ FN(perf_event_read), \
+ FN(redirect), \
+ FN(get_route_realm), \
+ FN(perf_event_output), \
+ FN(skb_load_bytes), \
+ FN(get_stackid), \
+ FN(csum_diff), \
+ FN(skb_get_tunnel_opt), \
+ FN(skb_set_tunnel_opt), \
+ FN(skb_change_proto), \
+ FN(skb_change_type), \
+ FN(skb_under_cgroup), \
+ FN(get_hash_recalc), \
+ FN(get_current_task), \
+ FN(probe_write_user), \
+ FN(current_task_under_cgroup), \
+ FN(skb_change_tail), \
+ FN(skb_pull_data), \
+ FN(csum_update), \
+ FN(set_hash_invalid), \
+ FN(get_numa_node_id), \
+ FN(skb_change_head), \
+ FN(xdp_adjust_head), \
+ FN(probe_read_str), \
+ FN(get_socket_cookie), \
+ FN(get_socket_uid), \
+ FN(set_hash), \
+ FN(setsockopt), \
+ FN(skb_adjust_room), \
+ FN(redirect_map), \
+ FN(sk_redirect_map), \
+ FN(sock_map_update), \
+ FN(xdp_adjust_meta), \
+ FN(perf_event_read_value), \
+ FN(perf_prog_read_value), \
+ FN(getsockopt), \
+ FN(override_return), \
+ FN(sock_ops_cb_flags_set), \
+ FN(msg_redirect_map), \
+ FN(msg_apply_bytes), \
+ FN(msg_cork_bytes), \
+ FN(msg_pull_data), \
+ FN(bind), \
+ FN(xdp_adjust_tail), \
+ FN(skb_get_xfrm_state), \
+ FN(get_stack), \
+ FN(skb_load_bytes_relative), \
+ FN(fib_lookup), \
+ FN(sock_hash_update), \
+ FN(msg_redirect_hash), \
+ FN(sk_redirect_hash), \
+ FN(lwt_push_encap), \
+ FN(lwt_seg6_store_bytes), \
+ FN(lwt_seg6_adjust_srh), \
+ FN(lwt_seg6_action), \
+ FN(rc_repeat), \
+ FN(rc_keydown), \
+ FN(skb_cgroup_id), \
+ FN(get_current_cgroup_id), \
+ FN(get_local_storage), \
+ FN(sk_select_reuseport), \
+ FN(skb_ancestor_cgroup_id), \
+ FN(sk_lookup_tcp), \
+ FN(sk_lookup_udp), \
+ FN(sk_release), \
+ FN(map_push_elem), \
+ FN(map_pop_elem), \
+ FN(map_peek_elem), \
+ FN(msg_push_data), \
+ FN(msg_pop_data), \
+ FN(rc_pointer_rel), \
+ FN(spin_lock), \
+ FN(spin_unlock), \
+ FN(sk_fullsock), \
+ FN(tcp_sock), \
+ FN(skb_ecn_set_ce), \
+ FN(get_listener_sock), \
+ FN(skc_lookup_tcp), \
+ FN(tcp_check_syncookie), \
+ FN(sysctl_get_name), \
+ FN(sysctl_get_current_value), \
+ FN(sysctl_get_new_value), \
+ FN(sysctl_set_new_value), \
+ FN(strtol), \
+ FN(strtoul), \
+ FN(sk_storage_get), \
+ FN(sk_storage_delete), \
+ FN(send_signal), \
+ FN(tcp_gen_syncookie), \
+ FN(skb_output), \
+ FN(probe_read_user), \
+ FN(probe_read_kernel), \
+ FN(probe_read_user_str), \
+ FN(probe_read_kernel_str),
+
+/* integer value in 'imm' field of BPF_CALL instruction selects which helper
+ * function eBPF program intends to call
+ */
+#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x
+enum bpf_func_id {
+ __BPF_FUNC_MAPPER(__BPF_ENUM_FN)
+ __BPF_FUNC_MAX_ID,
+};
+#undef __BPF_ENUM_FN
+
+/* All flags used by eBPF helper functions, placed here. */
+
+/* BPF_FUNC_skb_store_bytes flags. */
+#define BPF_F_RECOMPUTE_CSUM (1ULL << 0)
+#define BPF_F_INVALIDATE_HASH (1ULL << 1)
+
+/* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags.
+ * First 4 bits are for passing the header field size.
+ */
+#define BPF_F_HDR_FIELD_MASK 0xfULL
+
+/* BPF_FUNC_l4_csum_replace flags. */
+#define BPF_F_PSEUDO_HDR (1ULL << 4)
+#define BPF_F_MARK_MANGLED_0 (1ULL << 5)
+#define BPF_F_MARK_ENFORCE (1ULL << 6)
+
+/* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */
+#define BPF_F_INGRESS (1ULL << 0)
+
+/* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */
+#define BPF_F_TUNINFO_IPV6 (1ULL << 0)
+
+/* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */
+#define BPF_F_SKIP_FIELD_MASK 0xffULL
+#define BPF_F_USER_STACK (1ULL << 8)
+/* flags used by BPF_FUNC_get_stackid only. */
+#define BPF_F_FAST_STACK_CMP (1ULL << 9)
+#define BPF_F_REUSE_STACKID (1ULL << 10)
+/* flags used by BPF_FUNC_get_stack only. */
+#define BPF_F_USER_BUILD_ID (1ULL << 11)
+
+/* BPF_FUNC_skb_set_tunnel_key flags. */
+#define BPF_F_ZERO_CSUM_TX (1ULL << 1)
+#define BPF_F_DONT_FRAGMENT (1ULL << 2)
+#define BPF_F_SEQ_NUMBER (1ULL << 3)
+
+/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
+ * BPF_FUNC_perf_event_read_value flags.
+ */
+#define BPF_F_INDEX_MASK 0xffffffffULL
+#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
+/* BPF_FUNC_perf_event_output for sk_buff input context. */
+#define BPF_F_CTXLEN_MASK (0xfffffULL << 32)
+
+/* Current network namespace */
+#define BPF_F_CURRENT_NETNS (-1L)
+
+/* BPF_FUNC_skb_adjust_room flags. */
+#define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0)
+
+#define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff
+#define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56
+
+#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1)
+#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2)
+#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3)
+#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4)
+#define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \
+ BPF_ADJ_ROOM_ENCAP_L2_MASK) \
+ << BPF_ADJ_ROOM_ENCAP_L2_SHIFT)
+
+/* BPF_FUNC_sysctl_get_name flags. */
+#define BPF_F_SYSCTL_BASE_NAME (1ULL << 0)
+
+/* BPF_FUNC_sk_storage_get flags */
+#define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0)
+
+/* Mode for BPF_FUNC_skb_adjust_room helper. */
+enum bpf_adj_room_mode {
+ BPF_ADJ_ROOM_NET,
+ BPF_ADJ_ROOM_MAC,
+};
+
+/* Mode for BPF_FUNC_skb_load_bytes_relative helper. */
+enum bpf_hdr_start_off {
+ BPF_HDR_START_MAC,
+ BPF_HDR_START_NET,
+};
+
+/* Encapsulation type for BPF_FUNC_lwt_push_encap helper. */
+enum bpf_lwt_encap_mode {
+ BPF_LWT_ENCAP_SEG6,
+ BPF_LWT_ENCAP_SEG6_INLINE,
+ BPF_LWT_ENCAP_IP,
+};
+
+#define __bpf_md_ptr(type, name) \
+union { \
+ type name; \
+ __u64 :64; \
+} __attribute__((aligned(8)))
+
+/* user accessible mirror of in-kernel sk_buff.
+ * new fields can only be added to the end of this structure
+ */
+struct __sk_buff {
+ __u32 len;
+ __u32 pkt_type;
+ __u32 mark;
+ __u32 queue_mapping;
+ __u32 protocol;
+ __u32 vlan_present;
+ __u32 vlan_tci;
+ __u32 vlan_proto;
+ __u32 priority;
+ __u32 ingress_ifindex;
+ __u32 ifindex;
+ __u32 tc_index;
+ __u32 cb[5];
+ __u32 hash;
+ __u32 tc_classid;
+ __u32 data;
+ __u32 data_end;
+ __u32 napi_id;
+
+ /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */
+ __u32 family;
+ __u32 remote_ip4; /* Stored in network byte order */
+ __u32 local_ip4; /* Stored in network byte order */
+ __u32 remote_ip6[4]; /* Stored in network byte order */
+ __u32 local_ip6[4]; /* Stored in network byte order */
+ __u32 remote_port; /* Stored in network byte order */
+ __u32 local_port; /* stored in host byte order */
+ /* ... here. */
+
+ __u32 data_meta;
+ __bpf_md_ptr(struct bpf_flow_keys *, flow_keys);
+ __u64 tstamp;
+ __u32 wire_len;
+ __u32 gso_segs;
+ __bpf_md_ptr(struct bpf_sock *, sk);
+};
+
+struct bpf_tunnel_key {
+ __u32 tunnel_id;
+ union {
+ __u32 remote_ipv4;
+ __u32 remote_ipv6[4];
+ };
+ __u8 tunnel_tos;
+ __u8 tunnel_ttl;
+ __u16 tunnel_ext; /* Padding, future use. */
+ __u32 tunnel_label;
+};
+
+/* user accessible mirror of in-kernel xfrm_state.
+ * new fields can only be added to the end of this structure
+ */
+struct bpf_xfrm_state {
+ __u32 reqid;
+ __u32 spi; /* Stored in network byte order */
+ __u16 family;
+ __u16 ext; /* Padding, future use. */
+ union {
+ __u32 remote_ipv4; /* Stored in network byte order */
+ __u32 remote_ipv6[4]; /* Stored in network byte order */
+ };
+};
+
+/* Generic BPF return codes which all BPF program types may support.
+ * The values are binary compatible with their TC_ACT_* counter-part to
+ * provide backwards compatibility with existing SCHED_CLS and SCHED_ACT
+ * programs.
+ *
+ * XDP is handled seprately, see XDP_*.
+ */
+enum bpf_ret_code {
+ BPF_OK = 0,
+ /* 1 reserved */
+ BPF_DROP = 2,
+ /* 3-6 reserved */
+ BPF_REDIRECT = 7,
+ /* >127 are reserved for prog type specific return codes.
+ *
+ * BPF_LWT_REROUTE: used by BPF_PROG_TYPE_LWT_IN and
+ * BPF_PROG_TYPE_LWT_XMIT to indicate that skb had been
+ * changed and should be routed based on its new L3 header.
+ * (This is an L3 redirect, as opposed to L2 redirect
+ * represented by BPF_REDIRECT above).
+ */
+ BPF_LWT_REROUTE = 128,
+};
+
+struct bpf_sock {
+ __u32 bound_dev_if;
+ __u32 family;
+ __u32 type;
+ __u32 protocol;
+ __u32 mark;
+ __u32 priority;
+ /* IP address also allows 1 and 2 bytes access */
+ __u32 src_ip4;
+ __u32 src_ip6[4];
+ __u32 src_port; /* host byte order */
+ __u32 dst_port; /* network byte order */
+ __u32 dst_ip4;
+ __u32 dst_ip6[4];
+ __u32 state;
+};
+
+struct bpf_tcp_sock {
+ __u32 snd_cwnd; /* Sending congestion window */
+ __u32 srtt_us; /* smoothed round trip time << 3 in usecs */
+ __u32 rtt_min;
+ __u32 snd_ssthresh; /* Slow start size threshold */
+ __u32 rcv_nxt; /* What we want to receive next */
+ __u32 snd_nxt; /* Next sequence we send */
+ __u32 snd_una; /* First byte we want an ack for */
+ __u32 mss_cache; /* Cached effective mss, not including SACKS */
+ __u32 ecn_flags; /* ECN status bits. */
+ __u32 rate_delivered; /* saved rate sample: packets delivered */
+ __u32 rate_interval_us; /* saved rate sample: time elapsed */
+ __u32 packets_out; /* Packets which are "in flight" */
+ __u32 retrans_out; /* Retransmitted packets out */
+ __u32 total_retrans; /* Total retransmits for entire connection */
+ __u32 segs_in; /* RFC4898 tcpEStatsPerfSegsIn
+ * total number of segments in.
+ */
+ __u32 data_segs_in; /* RFC4898 tcpEStatsPerfDataSegsIn
+ * total number of data segments in.
+ */
+ __u32 segs_out; /* RFC4898 tcpEStatsPerfSegsOut
+ * The total number of segments sent.
+ */
+ __u32 data_segs_out; /* RFC4898 tcpEStatsPerfDataSegsOut
+ * total number of data segments sent.
+ */
+ __u32 lost_out; /* Lost packets */
+ __u32 sacked_out; /* SACK'd packets */
+ __u64 bytes_received; /* RFC4898 tcpEStatsAppHCThruOctetsReceived
+ * sum(delta(rcv_nxt)), or how many bytes
+ * were acked.
+ */
+ __u64 bytes_acked; /* RFC4898 tcpEStatsAppHCThruOctetsAcked
+ * sum(delta(snd_una)), or how many bytes
+ * were acked.
+ */
+ __u32 dsack_dups; /* RFC4898 tcpEStatsStackDSACKDups
+ * total number of DSACK blocks received
+ */
+ __u32 delivered; /* Total data packets delivered incl. rexmits */
+ __u32 delivered_ce; /* Like the above but only ECE marked packets */
+ __u32 icsk_retransmits; /* Number of unrecovered [RTO] timeouts */
+};
+
+struct bpf_sock_tuple {
+ union {
+ struct {
+ __be32 saddr;
+ __be32 daddr;
+ __be16 sport;
+ __be16 dport;
+ } ipv4;
+ struct {
+ __be32 saddr[4];
+ __be32 daddr[4];
+ __be16 sport;
+ __be16 dport;
+ } ipv6;
+ };
+};
+
+struct bpf_xdp_sock {
+ __u32 queue_id;
+};
+
+#define XDP_PACKET_HEADROOM 256
+
+/* User return codes for XDP prog type.
+ * A valid XDP program must return one of these defined values. All other
+ * return codes are reserved for future use. Unknown return codes will
+ * result in packet drops and a warning via bpf_warn_invalid_xdp_action().
+ */
+enum xdp_action {
+ XDP_ABORTED = 0,
+ XDP_DROP,
+ XDP_PASS,
+ XDP_TX,
+ XDP_REDIRECT,
+};
+
+/* user accessible metadata for XDP packet hook
+ * new fields must be added to the end of this structure
+ */
+struct xdp_md {
+ __u32 data;
+ __u32 data_end;
+ __u32 data_meta;
+ /* Below access go through struct xdp_rxq_info */
+ __u32 ingress_ifindex; /* rxq->dev->ifindex */
+ __u32 rx_queue_index; /* rxq->queue_index */
+};
+
+enum sk_action {
+ SK_DROP = 0,
+ SK_PASS,
+};
+
+/* user accessible metadata for SK_MSG packet hook, new fields must
+ * be added to the end of this structure
+ */
+struct sk_msg_md {
+ __bpf_md_ptr(void *, data);
+ __bpf_md_ptr(void *, data_end);
+
+ __u32 family;
+ __u32 remote_ip4; /* Stored in network byte order */
+ __u32 local_ip4; /* Stored in network byte order */
+ __u32 remote_ip6[4]; /* Stored in network byte order */
+ __u32 local_ip6[4]; /* Stored in network byte order */
+ __u32 remote_port; /* Stored in network byte order */
+ __u32 local_port; /* stored in host byte order */
+ __u32 size; /* Total size of sk_msg */
+};
+
+struct sk_reuseport_md {
+ /*
+ * Start of directly accessible data. It begins from
+ * the tcp/udp header.
+ */
+ __bpf_md_ptr(void *, data);
+ /* End of directly accessible data */
+ __bpf_md_ptr(void *, data_end);
+ /*
+ * Total length of packet (starting from the tcp/udp header).
+ * Note that the directly accessible bytes (data_end - data)
+ * could be less than this "len". Those bytes could be
+ * indirectly read by a helper "bpf_skb_load_bytes()".
+ */
+ __u32 len;
+ /*
+ * Eth protocol in the mac header (network byte order). e.g.
+ * ETH_P_IP(0x0800) and ETH_P_IPV6(0x86DD)
+ */
+ __u32 eth_protocol;
+ __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */
+ __u32 bind_inany; /* Is sock bound to an INANY address? */
+ __u32 hash; /* A hash of the packet 4 tuples */
+};
+
+#define BPF_TAG_SIZE 8
+
+struct bpf_prog_info {
+ __u32 type;
+ __u32 id;
+ __u8 tag[BPF_TAG_SIZE];
+ __u32 jited_prog_len;
+ __u32 xlated_prog_len;
+ __aligned_u64 jited_prog_insns;
+ __aligned_u64 xlated_prog_insns;
+ __u64 load_time; /* ns since boottime */
+ __u32 created_by_uid;
+ __u32 nr_map_ids;
+ __aligned_u64 map_ids;
+ char name[BPF_OBJ_NAME_LEN];
+ __u32 ifindex;
+ __u32 gpl_compatible:1;
+ __u32 :31; /* alignment pad */
+ __u64 netns_dev;
+ __u64 netns_ino;
+ __u32 nr_jited_ksyms;
+ __u32 nr_jited_func_lens;
+ __aligned_u64 jited_ksyms;
+ __aligned_u64 jited_func_lens;
+ __u32 btf_id;
+ __u32 func_info_rec_size;
+ __aligned_u64 func_info;
+ __u32 nr_func_info;
+ __u32 nr_line_info;
+ __aligned_u64 line_info;
+ __aligned_u64 jited_line_info;
+ __u32 nr_jited_line_info;
+ __u32 line_info_rec_size;
+ __u32 jited_line_info_rec_size;
+ __u32 nr_prog_tags;
+ __aligned_u64 prog_tags;
+ __u64 run_time_ns;
+ __u64 run_cnt;
+} __attribute__((aligned(8)));
+
+struct bpf_map_info {
+ __u32 type;
+ __u32 id;
+ __u32 key_size;
+ __u32 value_size;
+ __u32 max_entries;
+ __u32 map_flags;
+ char name[BPF_OBJ_NAME_LEN];
+ __u32 ifindex;
+ __u32 :32;
+ __u64 netns_dev;
+ __u64 netns_ino;
+ __u32 btf_id;
+ __u32 btf_key_type_id;
+ __u32 btf_value_type_id;
+} __attribute__((aligned(8)));
+
+struct bpf_btf_info {
+ __aligned_u64 btf;
+ __u32 btf_size;
+ __u32 id;
+} __attribute__((aligned(8)));
+
+/* User bpf_sock_addr struct to access socket fields and sockaddr struct passed
+ * by user and intended to be used by socket (e.g. to bind to, depends on
+ * attach attach type).
+ */
+struct bpf_sock_addr {
+ __u32 user_family; /* Allows 4-byte read, but no write. */
+ __u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
+ * Stored in network byte order.
+ */
+ __u32 user_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
+ * Stored in network byte order.
+ */
+ __u32 user_port; /* Allows 4-byte read and write.
+ * Stored in network byte order
+ */
+ __u32 family; /* Allows 4-byte read, but no write */
+ __u32 type; /* Allows 4-byte read, but no write */
+ __u32 protocol; /* Allows 4-byte read, but no write */
+ __u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write.
+ * Stored in network byte order.
+ */
+ __u32 msg_src_ip6[4]; /* Allows 1,2,4,8-byte read and 4,8-byte write.
+ * Stored in network byte order.
+ */
+ __bpf_md_ptr(struct bpf_sock *, sk);
+};
+
+/* User bpf_sock_ops struct to access socket values and specify request ops
+ * and their replies.
+ * Some of this fields are in network (bigendian) byte order and may need
+ * to be converted before use (bpf_ntohl() defined in samples/bpf/bpf_endian.h).
+ * New fields can only be added at the end of this structure
+ */
+struct bpf_sock_ops {
+ __u32 op;
+ union {
+ __u32 args[4]; /* Optionally passed to bpf program */
+ __u32 reply; /* Returned by bpf program */
+ __u32 replylong[4]; /* Optionally returned by bpf prog */
+ };
+ __u32 family;
+ __u32 remote_ip4; /* Stored in network byte order */
+ __u32 local_ip4; /* Stored in network byte order */
+ __u32 remote_ip6[4]; /* Stored in network byte order */
+ __u32 local_ip6[4]; /* Stored in network byte order */
+ __u32 remote_port; /* Stored in network byte order */
+ __u32 local_port; /* stored in host byte order */
+ __u32 is_fullsock; /* Some TCP fields are only valid if
+ * there is a full socket. If not, the
+ * fields read as zero.
+ */
+ __u32 snd_cwnd;
+ __u32 srtt_us; /* Averaged RTT << 3 in usecs */
+ __u32 bpf_sock_ops_cb_flags; /* flags defined in uapi/linux/tcp.h */
+ __u32 state;
+ __u32 rtt_min;
+ __u32 snd_ssthresh;
+ __u32 rcv_nxt;
+ __u32 snd_nxt;
+ __u32 snd_una;
+ __u32 mss_cache;
+ __u32 ecn_flags;
+ __u32 rate_delivered;
+ __u32 rate_interval_us;
+ __u32 packets_out;
+ __u32 retrans_out;
+ __u32 total_retrans;
+ __u32 segs_in;
+ __u32 data_segs_in;
+ __u32 segs_out;
+ __u32 data_segs_out;
+ __u32 lost_out;
+ __u32 sacked_out;
+ __u32 sk_txhash;
+ __u64 bytes_received;
+ __u64 bytes_acked;
+ __bpf_md_ptr(struct bpf_sock *, sk);
+};
+
+/* Definitions for bpf_sock_ops_cb_flags */
+#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0)
+#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1)
+#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2)
+#define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3)
+#define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently
+ * supported cb flags
+ */
+
+/* List of known BPF sock_ops operators.
+ * New entries can only be added at the end
+ */
+enum {
+ BPF_SOCK_OPS_VOID,
+ BPF_SOCK_OPS_TIMEOUT_INIT, /* Should return SYN-RTO value to use or
+ * -1 if default value should be used
+ */
+ BPF_SOCK_OPS_RWND_INIT, /* Should return initial advertized
+ * window (in packets) or -1 if default
+ * value should be used
+ */
+ BPF_SOCK_OPS_TCP_CONNECT_CB, /* Calls BPF program right before an
+ * active connection is initialized
+ */
+ BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB, /* Calls BPF program when an
+ * active connection is
+ * established
+ */
+ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB, /* Calls BPF program when a
+ * passive connection is
+ * established
+ */
+ BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control
+ * needs ECN
+ */
+ BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is
+ * based on the path and may be
+ * dependent on the congestion control
+ * algorithm. In general it indicates
+ * a congestion threshold. RTTs above
+ * this indicate congestion
+ */
+ BPF_SOCK_OPS_RTO_CB, /* Called when an RTO has triggered.
+ * Arg1: value of icsk_retransmits
+ * Arg2: value of icsk_rto
+ * Arg3: whether RTO has expired
+ */
+ BPF_SOCK_OPS_RETRANS_CB, /* Called when skb is retransmitted.
+ * Arg1: sequence number of 1st byte
+ * Arg2: # segments
+ * Arg3: return value of
+ * tcp_transmit_skb (0 => success)
+ */
+ BPF_SOCK_OPS_STATE_CB, /* Called when TCP changes state.
+ * Arg1: old_state
+ * Arg2: new_state
+ */
+ BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
+ * socket transition to LISTEN state.
+ */
+ BPF_SOCK_OPS_RTT_CB, /* Called on every RTT.
+ */
+};
+
+/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
+ * changes between the TCP and BPF versions. Ideally this should never happen.
+ * If it does, we need to add code to convert them before calling
+ * the BPF sock_ops function.
+ */
+enum {
+ BPF_TCP_ESTABLISHED = 1,
+ BPF_TCP_SYN_SENT,
+ BPF_TCP_SYN_RECV,
+ BPF_TCP_FIN_WAIT1,
+ BPF_TCP_FIN_WAIT2,
+ BPF_TCP_TIME_WAIT,
+ BPF_TCP_CLOSE,
+ BPF_TCP_CLOSE_WAIT,
+ BPF_TCP_LAST_ACK,
+ BPF_TCP_LISTEN,
+ BPF_TCP_CLOSING, /* Now a valid state */
+ BPF_TCP_NEW_SYN_RECV,
+
+ BPF_TCP_MAX_STATES /* Leave at the end! */
+};
+
+#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */
+#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */
+
+struct bpf_perf_event_value {
+ __u64 counter;
+ __u64 enabled;
+ __u64 running;
+};
+
+#define BPF_DEVCG_ACC_MKNOD (1ULL << 0)
+#define BPF_DEVCG_ACC_READ (1ULL << 1)
+#define BPF_DEVCG_ACC_WRITE (1ULL << 2)
+
+#define BPF_DEVCG_DEV_BLOCK (1ULL << 0)
+#define BPF_DEVCG_DEV_CHAR (1ULL << 1)
+
+struct bpf_cgroup_dev_ctx {
+ /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */
+ __u32 access_type;
+ __u32 major;
+ __u32 minor;
+};
+
+struct bpf_raw_tracepoint_args {
+ __u64 args[0];
+};
+
+/* DIRECT: Skip the FIB rules and go to FIB table associated with device
+ * OUTPUT: Do lookup from egress perspective; default is ingress
+ */
+#define BPF_FIB_LOOKUP_DIRECT (1U << 0)
+#define BPF_FIB_LOOKUP_OUTPUT (1U << 1)
+
+enum {
+ BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */
+ BPF_FIB_LKUP_RET_BLACKHOLE, /* dest is blackholed; can be dropped */
+ BPF_FIB_LKUP_RET_UNREACHABLE, /* dest is unreachable; can be dropped */
+ BPF_FIB_LKUP_RET_PROHIBIT, /* dest not allowed; can be dropped */
+ BPF_FIB_LKUP_RET_NOT_FWDED, /* packet is not forwarded */
+ BPF_FIB_LKUP_RET_FWD_DISABLED, /* fwding is not enabled on ingress */
+ BPF_FIB_LKUP_RET_UNSUPP_LWT, /* fwd requires encapsulation */
+ BPF_FIB_LKUP_RET_NO_NEIGH, /* no neighbor entry for nh */
+ BPF_FIB_LKUP_RET_FRAG_NEEDED, /* fragmentation required to fwd */
+};
+
+struct bpf_fib_lookup {
+ /* input: network family for lookup (AF_INET, AF_INET6)
+ * output: network family of egress nexthop
+ */
+ __u8 family;
+
+ /* set if lookup is to consider L4 data - e.g., FIB rules */
+ __u8 l4_protocol;
+ __be16 sport;
+ __be16 dport;
+
+ /* total length of packet from network header - used for MTU check */
+ __u16 tot_len;
+
+ /* input: L3 device index for lookup
+ * output: device index from FIB lookup
+ */
+ __u32 ifindex;
+
+ union {
+ /* inputs to lookup */
+ __u8 tos; /* AF_INET */
+ __be32 flowinfo; /* AF_INET6, flow_label + priority */
+
+ /* output: metric of fib result (IPv4/IPv6 only) */
+ __u32 rt_metric;
+ };
+
+ union {
+ __be32 ipv4_src;
+ __u32 ipv6_src[4]; /* in6_addr; network order */
+ };
+
+ /* input to bpf_fib_lookup, ipv{4,6}_dst is destination address in
+ * network header. output: bpf_fib_lookup sets to gateway address
+ * if FIB lookup returns gateway route
+ */
+ union {
+ __be32 ipv4_dst;
+ __u32 ipv6_dst[4]; /* in6_addr; network order */
+ };
+
+ /* output */
+ __be16 h_vlan_proto;
+ __be16 h_vlan_TCI;
+ __u8 smac[6]; /* ETH_ALEN */
+ __u8 dmac[6]; /* ETH_ALEN */
+};
+
+enum bpf_task_fd_type {
+ BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */
+ BPF_FD_TYPE_TRACEPOINT, /* tp name */
+ BPF_FD_TYPE_KPROBE, /* (symbol + offset) or addr */
+ BPF_FD_TYPE_KRETPROBE, /* (symbol + offset) or addr */
+ BPF_FD_TYPE_UPROBE, /* filename + offset */
+ BPF_FD_TYPE_URETPROBE, /* filename + offset */
+};
+
+#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0)
+#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1)
+#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2)
+
+struct bpf_flow_keys {
+ __u16 nhoff;
+ __u16 thoff;
+ __u16 addr_proto; /* ETH_P_* of valid addrs */
+ __u8 is_frag;
+ __u8 is_first_frag;
+ __u8 is_encap;
+ __u8 ip_proto;
+ __be16 n_proto;
+ __be16 sport;
+ __be16 dport;
+ union {
+ struct {
+ __be32 ipv4_src;
+ __be32 ipv4_dst;
+ };
+ struct {
+ __u32 ipv6_src[4]; /* in6_addr; network order */
+ __u32 ipv6_dst[4]; /* in6_addr; network order */
+ };
+ };
+ __u32 flags;
+ __be32 flow_label;
+};
+
+struct bpf_func_info {
+ __u32 insn_off;
+ __u32 type_id;
+};
+
+#define BPF_LINE_INFO_LINE_NUM(line_col) ((line_col) >> 10)
+#define BPF_LINE_INFO_LINE_COL(line_col) ((line_col) & 0x3ff)
+
+struct bpf_line_info {
+ __u32 insn_off;
+ __u32 file_name_off;
+ __u32 line_off;
+ __u32 line_col;
+};
+
+struct bpf_spin_lock {
+ __u32 val;
+};
+
+struct bpf_sysctl {
+ __u32 write; /* Sysctl is being read (= 0) or written (= 1).
+ * Allows 1,2,4-byte read, but no write.
+ */
+ __u32 file_pos; /* Sysctl file position to read from, write to.
+ * Allows 1,2,4-byte read an 4-byte write.
+ */
+};
+
+struct bpf_sockopt {
+ __bpf_md_ptr(struct bpf_sock *, sk);
+ __bpf_md_ptr(void *, optval);
+ __bpf_md_ptr(void *, optval_end);
+
+ __s32 level;
+ __s32 optname;
+ __s32 optlen;
+ __s32 retval;
+};
+
+#endif /* _UAPI__LINUX_BPF_H__ */
diff --git a/src/contrib/libbpf/include/uapi/linux/bpf_common.h b/src/contrib/libbpf/include/uapi/linux/bpf_common.h
new file mode 100644
index 0000000..ee97668
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/bpf_common.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__LINUX_BPF_COMMON_H__
+#define _UAPI__LINUX_BPF_COMMON_H__
+
+/* Instruction classes */
+#define BPF_CLASS(code) ((code) & 0x07)
+#define BPF_LD 0x00
+#define BPF_LDX 0x01
+#define BPF_ST 0x02
+#define BPF_STX 0x03
+#define BPF_ALU 0x04
+#define BPF_JMP 0x05
+#define BPF_RET 0x06
+#define BPF_MISC 0x07
+
+/* ld/ldx fields */
+#define BPF_SIZE(code) ((code) & 0x18)
+#define BPF_W 0x00 /* 32-bit */
+#define BPF_H 0x08 /* 16-bit */
+#define BPF_B 0x10 /* 8-bit */
+/* eBPF BPF_DW 0x18 64-bit */
+#define BPF_MODE(code) ((code) & 0xe0)
+#define BPF_IMM 0x00
+#define BPF_ABS 0x20
+#define BPF_IND 0x40
+#define BPF_MEM 0x60
+#define BPF_LEN 0x80
+#define BPF_MSH 0xa0
+
+/* alu/jmp fields */
+#define BPF_OP(code) ((code) & 0xf0)
+#define BPF_ADD 0x00
+#define BPF_SUB 0x10
+#define BPF_MUL 0x20
+#define BPF_DIV 0x30
+#define BPF_OR 0x40
+#define BPF_AND 0x50
+#define BPF_LSH 0x60
+#define BPF_RSH 0x70
+#define BPF_NEG 0x80
+#define BPF_MOD 0x90
+#define BPF_XOR 0xa0
+
+#define BPF_JA 0x00
+#define BPF_JEQ 0x10
+#define BPF_JGT 0x20
+#define BPF_JGE 0x30
+#define BPF_JSET 0x40
+#define BPF_SRC(code) ((code) & 0x08)
+#define BPF_K 0x00
+#define BPF_X 0x08
+
+#ifndef BPF_MAXINSNS
+#define BPF_MAXINSNS 4096
+#endif
+
+#endif /* _UAPI__LINUX_BPF_COMMON_H__ */
diff --git a/src/contrib/libbpf/include/uapi/linux/btf.h b/src/contrib/libbpf/include/uapi/linux/btf.h
new file mode 100644
index 0000000..63ae4a3
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/btf.h
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2018 Facebook */
+#ifndef _UAPI__LINUX_BTF_H__
+#define _UAPI__LINUX_BTF_H__
+
+#include <linux/types.h>
+
+#define BTF_MAGIC 0xeB9F
+#define BTF_VERSION 1
+
+struct btf_header {
+ __u16 magic;
+ __u8 version;
+ __u8 flags;
+ __u32 hdr_len;
+
+ /* All offsets are in bytes relative to the end of this header */
+ __u32 type_off; /* offset of type section */
+ __u32 type_len; /* length of type section */
+ __u32 str_off; /* offset of string section */
+ __u32 str_len; /* length of string section */
+};
+
+/* Max # of type identifier */
+#define BTF_MAX_TYPE 0x0000ffff
+/* Max offset into the string section */
+#define BTF_MAX_NAME_OFFSET 0x0000ffff
+/* Max # of struct/union/enum members or func args */
+#define BTF_MAX_VLEN 0xffff
+
+struct btf_type {
+ __u32 name_off;
+ /* "info" bits arrangement
+ * bits 0-15: vlen (e.g. # of struct's members)
+ * bits 16-23: unused
+ * bits 24-27: kind (e.g. int, ptr, array...etc)
+ * bits 28-30: unused
+ * bit 31: kind_flag, currently used by
+ * struct, union and fwd
+ */
+ __u32 info;
+ /* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
+ * "size" tells the size of the type it is describing.
+ *
+ * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
+ * FUNC, FUNC_PROTO and VAR.
+ * "type" is a type_id referring to another type.
+ */
+ union {
+ __u32 size;
+ __u32 type;
+ };
+};
+
+#define BTF_INFO_KIND(info) (((info) >> 24) & 0x0f)
+#define BTF_INFO_VLEN(info) ((info) & 0xffff)
+#define BTF_INFO_KFLAG(info) ((info) >> 31)
+
+#define BTF_KIND_UNKN 0 /* Unknown */
+#define BTF_KIND_INT 1 /* Integer */
+#define BTF_KIND_PTR 2 /* Pointer */
+#define BTF_KIND_ARRAY 3 /* Array */
+#define BTF_KIND_STRUCT 4 /* Struct */
+#define BTF_KIND_UNION 5 /* Union */
+#define BTF_KIND_ENUM 6 /* Enumeration */
+#define BTF_KIND_FWD 7 /* Forward */
+#define BTF_KIND_TYPEDEF 8 /* Typedef */
+#define BTF_KIND_VOLATILE 9 /* Volatile */
+#define BTF_KIND_CONST 10 /* Const */
+#define BTF_KIND_RESTRICT 11 /* Restrict */
+#define BTF_KIND_FUNC 12 /* Function */
+#define BTF_KIND_FUNC_PROTO 13 /* Function Proto */
+#define BTF_KIND_VAR 14 /* Variable */
+#define BTF_KIND_DATASEC 15 /* Section */
+#define BTF_KIND_MAX BTF_KIND_DATASEC
+#define NR_BTF_KINDS (BTF_KIND_MAX + 1)
+
+/* For some specific BTF_KIND, "struct btf_type" is immediately
+ * followed by extra data.
+ */
+
+/* BTF_KIND_INT is followed by a u32 and the following
+ * is the 32 bits arrangement:
+ */
+#define BTF_INT_ENCODING(VAL) (((VAL) & 0x0f000000) >> 24)
+#define BTF_INT_OFFSET(VAL) (((VAL) & 0x00ff0000) >> 16)
+#define BTF_INT_BITS(VAL) ((VAL) & 0x000000ff)
+
+/* Attributes stored in the BTF_INT_ENCODING */
+#define BTF_INT_SIGNED (1 << 0)
+#define BTF_INT_CHAR (1 << 1)
+#define BTF_INT_BOOL (1 << 2)
+
+/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
+ * The exact number of btf_enum is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_enum {
+ __u32 name_off;
+ __s32 val;
+};
+
+/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
+struct btf_array {
+ __u32 type;
+ __u32 index_type;
+ __u32 nelems;
+};
+
+/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
+ * by multiple "struct btf_member". The exact number
+ * of btf_member is stored in the vlen (of the info in
+ * "struct btf_type").
+ */
+struct btf_member {
+ __u32 name_off;
+ __u32 type;
+ /* If the type info kind_flag is set, the btf_member offset
+ * contains both member bitfield size and bit offset. The
+ * bitfield size is set for bitfield members. If the type
+ * info kind_flag is not set, the offset contains only bit
+ * offset.
+ */
+ __u32 offset;
+};
+
+/* If the struct/union type info kind_flag is set, the
+ * following two macros are used to access bitfield_size
+ * and bit_offset from btf_member.offset.
+ */
+#define BTF_MEMBER_BITFIELD_SIZE(val) ((val) >> 24)
+#define BTF_MEMBER_BIT_OFFSET(val) ((val) & 0xffffff)
+
+/* BTF_KIND_FUNC_PROTO is followed by multiple "struct btf_param".
+ * The exact number of btf_param is stored in the vlen (of the
+ * info in "struct btf_type").
+ */
+struct btf_param {
+ __u32 name_off;
+ __u32 type;
+};
+
+enum {
+ BTF_VAR_STATIC = 0,
+ BTF_VAR_GLOBAL_ALLOCATED,
+};
+
+/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
+ * additional information related to the variable such as its linkage.
+ */
+struct btf_var {
+ __u32 linkage;
+};
+
+/* BTF_KIND_DATASEC is followed by multiple "struct btf_var_secinfo"
+ * to describe all BTF_KIND_VAR types it contains along with it's
+ * in-section offset as well as size.
+ */
+struct btf_var_secinfo {
+ __u32 type;
+ __u32 offset;
+ __u32 size;
+};
+
+#endif /* _UAPI__LINUX_BTF_H__ */
diff --git a/src/contrib/libbpf/include/uapi/linux/if_link.h b/src/contrib/libbpf/include/uapi/linux/if_link.h
new file mode 100644
index 0000000..8aec876
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/if_link.h
@@ -0,0 +1,1033 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_IF_LINK_H
+#define _UAPI_LINUX_IF_LINK_H
+
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+/* This struct should be in sync with struct rtnl_link_stats64 */
+struct rtnl_link_stats {
+ __u32 rx_packets; /* total packets received */
+ __u32 tx_packets; /* total packets transmitted */
+ __u32 rx_bytes; /* total bytes received */
+ __u32 tx_bytes; /* total bytes transmitted */
+ __u32 rx_errors; /* bad packets received */
+ __u32 tx_errors; /* packet transmit problems */
+ __u32 rx_dropped; /* no space in linux buffers */
+ __u32 tx_dropped; /* no space available in linux */
+ __u32 multicast; /* multicast packets received */
+ __u32 collisions;
+
+ /* detailed rx_errors: */
+ __u32 rx_length_errors;
+ __u32 rx_over_errors; /* receiver ring buff overflow */
+ __u32 rx_crc_errors; /* recved pkt with crc error */
+ __u32 rx_frame_errors; /* recv'd frame alignment error */
+ __u32 rx_fifo_errors; /* recv'r fifo overrun */
+ __u32 rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ __u32 tx_aborted_errors;
+ __u32 tx_carrier_errors;
+ __u32 tx_fifo_errors;
+ __u32 tx_heartbeat_errors;
+ __u32 tx_window_errors;
+
+ /* for cslip etc */
+ __u32 rx_compressed;
+ __u32 tx_compressed;
+
+ __u32 rx_nohandler; /* dropped, no handler found */
+};
+
+/* The main device statistics structure */
+struct rtnl_link_stats64 {
+ __u64 rx_packets; /* total packets received */
+ __u64 tx_packets; /* total packets transmitted */
+ __u64 rx_bytes; /* total bytes received */
+ __u64 tx_bytes; /* total bytes transmitted */
+ __u64 rx_errors; /* bad packets received */
+ __u64 tx_errors; /* packet transmit problems */
+ __u64 rx_dropped; /* no space in linux buffers */
+ __u64 tx_dropped; /* no space available in linux */
+ __u64 multicast; /* multicast packets received */
+ __u64 collisions;
+
+ /* detailed rx_errors: */
+ __u64 rx_length_errors;
+ __u64 rx_over_errors; /* receiver ring buff overflow */
+ __u64 rx_crc_errors; /* recved pkt with crc error */
+ __u64 rx_frame_errors; /* recv'd frame alignment error */
+ __u64 rx_fifo_errors; /* recv'r fifo overrun */
+ __u64 rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ __u64 tx_aborted_errors;
+ __u64 tx_carrier_errors;
+ __u64 tx_fifo_errors;
+ __u64 tx_heartbeat_errors;
+ __u64 tx_window_errors;
+
+ /* for cslip etc */
+ __u64 rx_compressed;
+ __u64 tx_compressed;
+
+ __u64 rx_nohandler; /* dropped, no handler found */
+};
+
+/* The struct should be in sync with struct ifmap */
+struct rtnl_link_ifmap {
+ __u64 mem_start;
+ __u64 mem_end;
+ __u64 base_addr;
+ __u16 irq;
+ __u8 dma;
+ __u8 port;
+};
+
+/*
+ * IFLA_AF_SPEC
+ * Contains nested attributes for address family specific attributes.
+ * Each address family may create a attribute with the address family
+ * number as type and create its own attribute structure in it.
+ *
+ * Example:
+ * [IFLA_AF_SPEC] = {
+ * [AF_INET] = {
+ * [IFLA_INET_CONF] = ...,
+ * },
+ * [AF_INET6] = {
+ * [IFLA_INET6_FLAGS] = ...,
+ * [IFLA_INET6_CONF] = ...,
+ * }
+ * }
+ */
+
+enum {
+ IFLA_UNSPEC,
+ IFLA_ADDRESS,
+ IFLA_BROADCAST,
+ IFLA_IFNAME,
+ IFLA_MTU,
+ IFLA_LINK,
+ IFLA_QDISC,
+ IFLA_STATS,
+ IFLA_COST,
+#define IFLA_COST IFLA_COST
+ IFLA_PRIORITY,
+#define IFLA_PRIORITY IFLA_PRIORITY
+ IFLA_MASTER,
+#define IFLA_MASTER IFLA_MASTER
+ IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */
+#define IFLA_WIRELESS IFLA_WIRELESS
+ IFLA_PROTINFO, /* Protocol specific information for a link */
+#define IFLA_PROTINFO IFLA_PROTINFO
+ IFLA_TXQLEN,
+#define IFLA_TXQLEN IFLA_TXQLEN
+ IFLA_MAP,
+#define IFLA_MAP IFLA_MAP
+ IFLA_WEIGHT,
+#define IFLA_WEIGHT IFLA_WEIGHT
+ IFLA_OPERSTATE,
+ IFLA_LINKMODE,
+ IFLA_LINKINFO,
+#define IFLA_LINKINFO IFLA_LINKINFO
+ IFLA_NET_NS_PID,
+ IFLA_IFALIAS,
+ IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */
+ IFLA_VFINFO_LIST,
+ IFLA_STATS64,
+ IFLA_VF_PORTS,
+ IFLA_PORT_SELF,
+ IFLA_AF_SPEC,
+ IFLA_GROUP, /* Group the device belongs to */
+ IFLA_NET_NS_FD,
+ IFLA_EXT_MASK, /* Extended info mask, VFs, etc */
+ IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */
+#define IFLA_PROMISCUITY IFLA_PROMISCUITY
+ IFLA_NUM_TX_QUEUES,
+ IFLA_NUM_RX_QUEUES,
+ IFLA_CARRIER,
+ IFLA_PHYS_PORT_ID,
+ IFLA_CARRIER_CHANGES,
+ IFLA_PHYS_SWITCH_ID,
+ IFLA_LINK_NETNSID,
+ IFLA_PHYS_PORT_NAME,
+ IFLA_PROTO_DOWN,
+ IFLA_GSO_MAX_SEGS,
+ IFLA_GSO_MAX_SIZE,
+ IFLA_PAD,
+ IFLA_XDP,
+ IFLA_EVENT,
+ IFLA_NEW_NETNSID,
+ IFLA_IF_NETNSID,
+ IFLA_TARGET_NETNSID = IFLA_IF_NETNSID, /* new alias */
+ IFLA_CARRIER_UP_COUNT,
+ IFLA_CARRIER_DOWN_COUNT,
+ IFLA_NEW_IFINDEX,
+ IFLA_MIN_MTU,
+ IFLA_MAX_MTU,
+ IFLA_PROP_LIST,
+ IFLA_ALT_IFNAME, /* Alternative ifname */
+ __IFLA_MAX
+};
+
+
+#define IFLA_MAX (__IFLA_MAX - 1)
+
+/* backwards compatibility for userspace */
+#ifndef __KERNEL__
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+enum {
+ IFLA_INET_UNSPEC,
+ IFLA_INET_CONF,
+ __IFLA_INET_MAX,
+};
+
+#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
+
+/* ifi_flags.
+
+ IFF_* flags.
+
+ The only change is:
+ IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are
+ more not changeable by user. They describe link media
+ characteristics and set by device driver.
+
+ Comments:
+ - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid
+ - If neither of these three flags are set;
+ the interface is NBMA.
+
+ - IFF_MULTICAST does not mean anything special:
+ multicasts can be used on all not-NBMA links.
+ IFF_MULTICAST means that this media uses special encapsulation
+ for multicast frames. Apparently, all IFF_POINTOPOINT and
+ IFF_BROADCAST devices are able to use multicasts too.
+ */
+
+/* IFLA_LINK.
+ For usual devices it is equal ifi_index.
+ If it is a "virtual interface" (f.e. tunnel), ifi_link
+ can point to real physical interface (f.e. for bandwidth calculations),
+ or maybe 0, what means, that real media is unknown (usual
+ for IPIP tunnels, when route to endpoint is allowed to change)
+ */
+
+/* Subtype attributes for IFLA_PROTINFO */
+enum {
+ IFLA_INET6_UNSPEC,
+ IFLA_INET6_FLAGS, /* link flags */
+ IFLA_INET6_CONF, /* sysctl parameters */
+ IFLA_INET6_STATS, /* statistics */
+ IFLA_INET6_MCAST, /* MC things. What of them? */
+ IFLA_INET6_CACHEINFO, /* time values and max reasm size */
+ IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */
+ IFLA_INET6_TOKEN, /* device token */
+ IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */
+ __IFLA_INET6_MAX
+};
+
+#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
+
+enum in6_addr_gen_mode {
+ IN6_ADDR_GEN_MODE_EUI64,
+ IN6_ADDR_GEN_MODE_NONE,
+ IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+ IN6_ADDR_GEN_MODE_RANDOM,
+};
+
+/* Bridge section */
+
+enum {
+ IFLA_BR_UNSPEC,
+ IFLA_BR_FORWARD_DELAY,
+ IFLA_BR_HELLO_TIME,
+ IFLA_BR_MAX_AGE,
+ IFLA_BR_AGEING_TIME,
+ IFLA_BR_STP_STATE,
+ IFLA_BR_PRIORITY,
+ IFLA_BR_VLAN_FILTERING,
+ IFLA_BR_VLAN_PROTOCOL,
+ IFLA_BR_GROUP_FWD_MASK,
+ IFLA_BR_ROOT_ID,
+ IFLA_BR_BRIDGE_ID,
+ IFLA_BR_ROOT_PORT,
+ IFLA_BR_ROOT_PATH_COST,
+ IFLA_BR_TOPOLOGY_CHANGE,
+ IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+ IFLA_BR_HELLO_TIMER,
+ IFLA_BR_TCN_TIMER,
+ IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+ IFLA_BR_GC_TIMER,
+ IFLA_BR_GROUP_ADDR,
+ IFLA_BR_FDB_FLUSH,
+ IFLA_BR_MCAST_ROUTER,
+ IFLA_BR_MCAST_SNOOPING,
+ IFLA_BR_MCAST_QUERY_USE_IFADDR,
+ IFLA_BR_MCAST_QUERIER,
+ IFLA_BR_MCAST_HASH_ELASTICITY,
+ IFLA_BR_MCAST_HASH_MAX,
+ IFLA_BR_MCAST_LAST_MEMBER_CNT,
+ IFLA_BR_MCAST_STARTUP_QUERY_CNT,
+ IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+ IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+ IFLA_BR_MCAST_QUERIER_INTVL,
+ IFLA_BR_MCAST_QUERY_INTVL,
+ IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+ IFLA_BR_MCAST_STARTUP_QUERY_INTVL,
+ IFLA_BR_NF_CALL_IPTABLES,
+ IFLA_BR_NF_CALL_IP6TABLES,
+ IFLA_BR_NF_CALL_ARPTABLES,
+ IFLA_BR_VLAN_DEFAULT_PVID,
+ IFLA_BR_PAD,
+ IFLA_BR_VLAN_STATS_ENABLED,
+ IFLA_BR_MCAST_STATS_ENABLED,
+ IFLA_BR_MCAST_IGMP_VERSION,
+ IFLA_BR_MCAST_MLD_VERSION,
+ IFLA_BR_VLAN_STATS_PER_PORT,
+ IFLA_BR_MULTI_BOOLOPT,
+ __IFLA_BR_MAX,
+};
+
+#define IFLA_BR_MAX (__IFLA_BR_MAX - 1)
+
+struct ifla_bridge_id {
+ __u8 prio[2];
+ __u8 addr[6]; /* ETH_ALEN */
+};
+
+enum {
+ BRIDGE_MODE_UNSPEC,
+ BRIDGE_MODE_HAIRPIN,
+};
+
+enum {
+ IFLA_BRPORT_UNSPEC,
+ IFLA_BRPORT_STATE, /* Spanning tree state */
+ IFLA_BRPORT_PRIORITY, /* " priority */
+ IFLA_BRPORT_COST, /* " cost */
+ IFLA_BRPORT_MODE, /* mode (hairpin) */
+ IFLA_BRPORT_GUARD, /* bpdu guard */
+ IFLA_BRPORT_PROTECT, /* root port protection */
+ IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */
+ IFLA_BRPORT_LEARNING, /* mac learning */
+ IFLA_BRPORT_UNICAST_FLOOD, /* flood unicast traffic */
+ IFLA_BRPORT_PROXYARP, /* proxy ARP */
+ IFLA_BRPORT_LEARNING_SYNC, /* mac learning sync from device */
+ IFLA_BRPORT_PROXYARP_WIFI, /* proxy ARP for Wi-Fi */
+ IFLA_BRPORT_ROOT_ID, /* designated root */
+ IFLA_BRPORT_BRIDGE_ID, /* designated bridge */
+ IFLA_BRPORT_DESIGNATED_PORT,
+ IFLA_BRPORT_DESIGNATED_COST,
+ IFLA_BRPORT_ID,
+ IFLA_BRPORT_NO,
+ IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+ IFLA_BRPORT_CONFIG_PENDING,
+ IFLA_BRPORT_MESSAGE_AGE_TIMER,
+ IFLA_BRPORT_FORWARD_DELAY_TIMER,
+ IFLA_BRPORT_HOLD_TIMER,
+ IFLA_BRPORT_FLUSH,
+ IFLA_BRPORT_MULTICAST_ROUTER,
+ IFLA_BRPORT_PAD,
+ IFLA_BRPORT_MCAST_FLOOD,
+ IFLA_BRPORT_MCAST_TO_UCAST,
+ IFLA_BRPORT_VLAN_TUNNEL,
+ IFLA_BRPORT_BCAST_FLOOD,
+ IFLA_BRPORT_GROUP_FWD_MASK,
+ IFLA_BRPORT_NEIGH_SUPPRESS,
+ IFLA_BRPORT_ISOLATED,
+ IFLA_BRPORT_BACKUP_PORT,
+ __IFLA_BRPORT_MAX
+};
+#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+
+struct ifla_cacheinfo {
+ __u32 max_reasm_len;
+ __u32 tstamp; /* ipv6InterfaceTable updated timestamp */
+ __u32 reachable_time;
+ __u32 retrans_time;
+};
+
+enum {
+ IFLA_INFO_UNSPEC,
+ IFLA_INFO_KIND,
+ IFLA_INFO_DATA,
+ IFLA_INFO_XSTATS,
+ IFLA_INFO_SLAVE_KIND,
+ IFLA_INFO_SLAVE_DATA,
+ __IFLA_INFO_MAX,
+};
+
+#define IFLA_INFO_MAX (__IFLA_INFO_MAX - 1)
+
+/* VLAN section */
+
+enum {
+ IFLA_VLAN_UNSPEC,
+ IFLA_VLAN_ID,
+ IFLA_VLAN_FLAGS,
+ IFLA_VLAN_EGRESS_QOS,
+ IFLA_VLAN_INGRESS_QOS,
+ IFLA_VLAN_PROTOCOL,
+ __IFLA_VLAN_MAX,
+};
+
+#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
+
+struct ifla_vlan_flags {
+ __u32 flags;
+ __u32 mask;
+};
+
+enum {
+ IFLA_VLAN_QOS_UNSPEC,
+ IFLA_VLAN_QOS_MAPPING,
+ __IFLA_VLAN_QOS_MAX
+};
+
+#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1)
+
+struct ifla_vlan_qos_mapping {
+ __u32 from;
+ __u32 to;
+};
+
+/* MACVLAN section */
+enum {
+ IFLA_MACVLAN_UNSPEC,
+ IFLA_MACVLAN_MODE,
+ IFLA_MACVLAN_FLAGS,
+ IFLA_MACVLAN_MACADDR_MODE,
+ IFLA_MACVLAN_MACADDR,
+ IFLA_MACVLAN_MACADDR_DATA,
+ IFLA_MACVLAN_MACADDR_COUNT,
+ __IFLA_MACVLAN_MAX,
+};
+
+#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
+
+enum macvlan_mode {
+ MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
+ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */
+ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */
+ MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
+ MACVLAN_MODE_SOURCE = 16,/* use source MAC address list to assign */
+};
+
+enum macvlan_macaddr_mode {
+ MACVLAN_MACADDR_ADD,
+ MACVLAN_MACADDR_DEL,
+ MACVLAN_MACADDR_FLUSH,
+ MACVLAN_MACADDR_SET,
+};
+
+#define MACVLAN_FLAG_NOPROMISC 1
+
+/* VRF section */
+enum {
+ IFLA_VRF_UNSPEC,
+ IFLA_VRF_TABLE,
+ __IFLA_VRF_MAX
+};
+
+#define IFLA_VRF_MAX (__IFLA_VRF_MAX - 1)
+
+enum {
+ IFLA_VRF_PORT_UNSPEC,
+ IFLA_VRF_PORT_TABLE,
+ __IFLA_VRF_PORT_MAX
+};
+
+#define IFLA_VRF_PORT_MAX (__IFLA_VRF_PORT_MAX - 1)
+
+/* MACSEC section */
+enum {
+ IFLA_MACSEC_UNSPEC,
+ IFLA_MACSEC_SCI,
+ IFLA_MACSEC_PORT,
+ IFLA_MACSEC_ICV_LEN,
+ IFLA_MACSEC_CIPHER_SUITE,
+ IFLA_MACSEC_WINDOW,
+ IFLA_MACSEC_ENCODING_SA,
+ IFLA_MACSEC_ENCRYPT,
+ IFLA_MACSEC_PROTECT,
+ IFLA_MACSEC_INC_SCI,
+ IFLA_MACSEC_ES,
+ IFLA_MACSEC_SCB,
+ IFLA_MACSEC_REPLAY_PROTECT,
+ IFLA_MACSEC_VALIDATION,
+ IFLA_MACSEC_PAD,
+ __IFLA_MACSEC_MAX,
+};
+
+#define IFLA_MACSEC_MAX (__IFLA_MACSEC_MAX - 1)
+
+/* XFRM section */
+enum {
+ IFLA_XFRM_UNSPEC,
+ IFLA_XFRM_LINK,
+ IFLA_XFRM_IF_ID,
+ __IFLA_XFRM_MAX
+};
+
+#define IFLA_XFRM_MAX (__IFLA_XFRM_MAX - 1)
+
+enum macsec_validation_type {
+ MACSEC_VALIDATE_DISABLED = 0,
+ MACSEC_VALIDATE_CHECK = 1,
+ MACSEC_VALIDATE_STRICT = 2,
+ __MACSEC_VALIDATE_END,
+ MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
+};
+
+/* IPVLAN section */
+enum {
+ IFLA_IPVLAN_UNSPEC,
+ IFLA_IPVLAN_MODE,
+ IFLA_IPVLAN_FLAGS,
+ __IFLA_IPVLAN_MAX
+};
+
+#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
+
+enum ipvlan_mode {
+ IPVLAN_MODE_L2 = 0,
+ IPVLAN_MODE_L3,
+ IPVLAN_MODE_L3S,
+ IPVLAN_MODE_MAX
+};
+
+#define IPVLAN_F_PRIVATE 0x01
+#define IPVLAN_F_VEPA 0x02
+
+/* VXLAN section */
+enum {
+ IFLA_VXLAN_UNSPEC,
+ IFLA_VXLAN_ID,
+ IFLA_VXLAN_GROUP, /* group or remote address */
+ IFLA_VXLAN_LINK,
+ IFLA_VXLAN_LOCAL,
+ IFLA_VXLAN_TTL,
+ IFLA_VXLAN_TOS,
+ IFLA_VXLAN_LEARNING,
+ IFLA_VXLAN_AGEING,
+ IFLA_VXLAN_LIMIT,
+ IFLA_VXLAN_PORT_RANGE, /* source port */
+ IFLA_VXLAN_PROXY,
+ IFLA_VXLAN_RSC,
+ IFLA_VXLAN_L2MISS,
+ IFLA_VXLAN_L3MISS,
+ IFLA_VXLAN_PORT, /* destination port */
+ IFLA_VXLAN_GROUP6,
+ IFLA_VXLAN_LOCAL6,
+ IFLA_VXLAN_UDP_CSUM,
+ IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+ IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+ IFLA_VXLAN_REMCSUM_TX,
+ IFLA_VXLAN_REMCSUM_RX,
+ IFLA_VXLAN_GBP,
+ IFLA_VXLAN_REMCSUM_NOPARTIAL,
+ IFLA_VXLAN_COLLECT_METADATA,
+ IFLA_VXLAN_LABEL,
+ IFLA_VXLAN_GPE,
+ IFLA_VXLAN_TTL_INHERIT,
+ IFLA_VXLAN_DF,
+ __IFLA_VXLAN_MAX
+};
+#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
+
+struct ifla_vxlan_port_range {
+ __be16 low;
+ __be16 high;
+};
+
+enum ifla_vxlan_df {
+ VXLAN_DF_UNSET = 0,
+ VXLAN_DF_SET,
+ VXLAN_DF_INHERIT,
+ __VXLAN_DF_END,
+ VXLAN_DF_MAX = __VXLAN_DF_END - 1,
+};
+
+/* GENEVE section */
+enum {
+ IFLA_GENEVE_UNSPEC,
+ IFLA_GENEVE_ID,
+ IFLA_GENEVE_REMOTE,
+ IFLA_GENEVE_TTL,
+ IFLA_GENEVE_TOS,
+ IFLA_GENEVE_PORT, /* destination port */
+ IFLA_GENEVE_COLLECT_METADATA,
+ IFLA_GENEVE_REMOTE6,
+ IFLA_GENEVE_UDP_CSUM,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+ IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+ IFLA_GENEVE_LABEL,
+ IFLA_GENEVE_TTL_INHERIT,
+ IFLA_GENEVE_DF,
+ __IFLA_GENEVE_MAX
+};
+#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)
+
+enum ifla_geneve_df {
+ GENEVE_DF_UNSET = 0,
+ GENEVE_DF_SET,
+ GENEVE_DF_INHERIT,
+ __GENEVE_DF_END,
+ GENEVE_DF_MAX = __GENEVE_DF_END - 1,
+};
+
+/* PPP section */
+enum {
+ IFLA_PPP_UNSPEC,
+ IFLA_PPP_DEV_FD,
+ __IFLA_PPP_MAX
+};
+#define IFLA_PPP_MAX (__IFLA_PPP_MAX - 1)
+
+/* GTP section */
+
+enum ifla_gtp_role {
+ GTP_ROLE_GGSN = 0,
+ GTP_ROLE_SGSN,
+};
+
+enum {
+ IFLA_GTP_UNSPEC,
+ IFLA_GTP_FD0,
+ IFLA_GTP_FD1,
+ IFLA_GTP_PDP_HASHSIZE,
+ IFLA_GTP_ROLE,
+ __IFLA_GTP_MAX,
+};
+#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
+
+/* Bonding section */
+
+enum {
+ IFLA_BOND_UNSPEC,
+ IFLA_BOND_MODE,
+ IFLA_BOND_ACTIVE_SLAVE,
+ IFLA_BOND_MIIMON,
+ IFLA_BOND_UPDELAY,
+ IFLA_BOND_DOWNDELAY,
+ IFLA_BOND_USE_CARRIER,
+ IFLA_BOND_ARP_INTERVAL,
+ IFLA_BOND_ARP_IP_TARGET,
+ IFLA_BOND_ARP_VALIDATE,
+ IFLA_BOND_ARP_ALL_TARGETS,
+ IFLA_BOND_PRIMARY,
+ IFLA_BOND_PRIMARY_RESELECT,
+ IFLA_BOND_FAIL_OVER_MAC,
+ IFLA_BOND_XMIT_HASH_POLICY,
+ IFLA_BOND_RESEND_IGMP,
+ IFLA_BOND_NUM_PEER_NOTIF,
+ IFLA_BOND_ALL_SLAVES_ACTIVE,
+ IFLA_BOND_MIN_LINKS,
+ IFLA_BOND_LP_INTERVAL,
+ IFLA_BOND_PACKETS_PER_SLAVE,
+ IFLA_BOND_AD_LACP_RATE,
+ IFLA_BOND_AD_SELECT,
+ IFLA_BOND_AD_INFO,
+ IFLA_BOND_AD_ACTOR_SYS_PRIO,
+ IFLA_BOND_AD_USER_PORT_KEY,
+ IFLA_BOND_AD_ACTOR_SYSTEM,
+ IFLA_BOND_TLB_DYNAMIC_LB,
+ IFLA_BOND_PEER_NOTIF_DELAY,
+ __IFLA_BOND_MAX,
+};
+
+#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1)
+
+enum {
+ IFLA_BOND_AD_INFO_UNSPEC,
+ IFLA_BOND_AD_INFO_AGGREGATOR,
+ IFLA_BOND_AD_INFO_NUM_PORTS,
+ IFLA_BOND_AD_INFO_ACTOR_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_KEY,
+ IFLA_BOND_AD_INFO_PARTNER_MAC,
+ __IFLA_BOND_AD_INFO_MAX,
+};
+
+#define IFLA_BOND_AD_INFO_MAX (__IFLA_BOND_AD_INFO_MAX - 1)
+
+enum {
+ IFLA_BOND_SLAVE_UNSPEC,
+ IFLA_BOND_SLAVE_STATE,
+ IFLA_BOND_SLAVE_MII_STATUS,
+ IFLA_BOND_SLAVE_LINK_FAILURE_COUNT,
+ IFLA_BOND_SLAVE_PERM_HWADDR,
+ IFLA_BOND_SLAVE_QUEUE_ID,
+ IFLA_BOND_SLAVE_AD_AGGREGATOR_ID,
+ IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
+ IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE,
+ __IFLA_BOND_SLAVE_MAX,
+};
+
+#define IFLA_BOND_SLAVE_MAX (__IFLA_BOND_SLAVE_MAX - 1)
+
+/* SR-IOV virtual function management section */
+
+enum {
+ IFLA_VF_INFO_UNSPEC,
+ IFLA_VF_INFO,
+ __IFLA_VF_INFO_MAX,
+};
+
+#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
+
+enum {
+ IFLA_VF_UNSPEC,
+ IFLA_VF_MAC, /* Hardware queue specific attributes */
+ IFLA_VF_VLAN, /* VLAN ID and QoS */
+ IFLA_VF_TX_RATE, /* Max TX Bandwidth Allocation */
+ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */
+ IFLA_VF_LINK_STATE, /* link state enable/disable/auto switch */
+ IFLA_VF_RATE, /* Min and Max TX Bandwidth Allocation */
+ IFLA_VF_RSS_QUERY_EN, /* RSS Redirection Table and Hash Key query
+ * on/off switch
+ */
+ IFLA_VF_STATS, /* network device statistics */
+ IFLA_VF_TRUST, /* Trust VF */
+ IFLA_VF_IB_NODE_GUID, /* VF Infiniband node GUID */
+ IFLA_VF_IB_PORT_GUID, /* VF Infiniband port GUID */
+ IFLA_VF_VLAN_LIST, /* nested list of vlans, option for QinQ */
+ IFLA_VF_BROADCAST, /* VF broadcast */
+ __IFLA_VF_MAX,
+};
+
+#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
+
+struct ifla_vf_mac {
+ __u32 vf;
+ __u8 mac[32]; /* MAX_ADDR_LEN */
+};
+
+struct ifla_vf_broadcast {
+ __u8 broadcast[32];
+};
+
+struct ifla_vf_vlan {
+ __u32 vf;
+ __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ __u32 qos;
+};
+
+enum {
+ IFLA_VF_VLAN_INFO_UNSPEC,
+ IFLA_VF_VLAN_INFO, /* VLAN ID, QoS and VLAN protocol */
+ __IFLA_VF_VLAN_INFO_MAX,
+};
+
+#define IFLA_VF_VLAN_INFO_MAX (__IFLA_VF_VLAN_INFO_MAX - 1)
+#define MAX_VLAN_LIST_LEN 1
+
+struct ifla_vf_vlan_info {
+ __u32 vf;
+ __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ __u32 qos;
+ __be16 vlan_proto; /* VLAN protocol either 802.1Q or 802.1ad */
+};
+
+struct ifla_vf_tx_rate {
+ __u32 vf;
+ __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
+};
+
+struct ifla_vf_rate {
+ __u32 vf;
+ __u32 min_tx_rate; /* Min Bandwidth in Mbps */
+ __u32 max_tx_rate; /* Max Bandwidth in Mbps */
+};
+
+struct ifla_vf_spoofchk {
+ __u32 vf;
+ __u32 setting;
+};
+
+struct ifla_vf_guid {
+ __u32 vf;
+ __u64 guid;
+};
+
+enum {
+ IFLA_VF_LINK_STATE_AUTO, /* link state of the uplink */
+ IFLA_VF_LINK_STATE_ENABLE, /* link always up */
+ IFLA_VF_LINK_STATE_DISABLE, /* link always down */
+ __IFLA_VF_LINK_STATE_MAX,
+};
+
+struct ifla_vf_link_state {
+ __u32 vf;
+ __u32 link_state;
+};
+
+struct ifla_vf_rss_query_en {
+ __u32 vf;
+ __u32 setting;
+};
+
+enum {
+ IFLA_VF_STATS_RX_PACKETS,
+ IFLA_VF_STATS_TX_PACKETS,
+ IFLA_VF_STATS_RX_BYTES,
+ IFLA_VF_STATS_TX_BYTES,
+ IFLA_VF_STATS_BROADCAST,
+ IFLA_VF_STATS_MULTICAST,
+ IFLA_VF_STATS_PAD,
+ IFLA_VF_STATS_RX_DROPPED,
+ IFLA_VF_STATS_TX_DROPPED,
+ __IFLA_VF_STATS_MAX,
+};
+
+#define IFLA_VF_STATS_MAX (__IFLA_VF_STATS_MAX - 1)
+
+struct ifla_vf_trust {
+ __u32 vf;
+ __u32 setting;
+};
+
+/* VF ports management section
+ *
+ * Nested layout of set/get msg is:
+ *
+ * [IFLA_NUM_VF]
+ * [IFLA_VF_PORTS]
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * ...
+ * [IFLA_PORT_SELF]
+ * [IFLA_PORT_*], ...
+ */
+
+enum {
+ IFLA_VF_PORT_UNSPEC,
+ IFLA_VF_PORT, /* nest */
+ __IFLA_VF_PORT_MAX,
+};
+
+#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
+
+enum {
+ IFLA_PORT_UNSPEC,
+ IFLA_PORT_VF, /* __u32 */
+ IFLA_PORT_PROFILE, /* string */
+ IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */
+ IFLA_PORT_INSTANCE_UUID, /* binary UUID */
+ IFLA_PORT_HOST_UUID, /* binary UUID */
+ IFLA_PORT_REQUEST, /* __u8 */
+ IFLA_PORT_RESPONSE, /* __u16, output only */
+ __IFLA_PORT_MAX,
+};
+
+#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
+
+#define PORT_PROFILE_MAX 40
+#define PORT_UUID_MAX 16
+#define PORT_SELF_VF -1
+
+enum {
+ PORT_REQUEST_PREASSOCIATE = 0,
+ PORT_REQUEST_PREASSOCIATE_RR,
+ PORT_REQUEST_ASSOCIATE,
+ PORT_REQUEST_DISASSOCIATE,
+};
+
+enum {
+ PORT_VDP_RESPONSE_SUCCESS = 0,
+ PORT_VDP_RESPONSE_INVALID_FORMAT,
+ PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_VDP_RESPONSE_UNUSED_VTID,
+ PORT_VDP_RESPONSE_VTID_VIOLATION,
+ PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
+ PORT_VDP_RESPONSE_OUT_OF_SYNC,
+ /* 0x08-0xFF reserved for future VDP use */
+ PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
+ PORT_PROFILE_RESPONSE_INPROGRESS,
+ PORT_PROFILE_RESPONSE_INVALID,
+ PORT_PROFILE_RESPONSE_BADSTATE,
+ PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_PROFILE_RESPONSE_ERROR,
+};
+
+struct ifla_port_vsi {
+ __u8 vsi_mgr_id;
+ __u8 vsi_type_id[3];
+ __u8 vsi_type_version;
+ __u8 pad[3];
+};
+
+
+/* IPoIB section */
+
+enum {
+ IFLA_IPOIB_UNSPEC,
+ IFLA_IPOIB_PKEY,
+ IFLA_IPOIB_MODE,
+ IFLA_IPOIB_UMCAST,
+ __IFLA_IPOIB_MAX
+};
+
+enum {
+ IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */
+ IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
+};
+
+#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
+
+/* HSR section */
+
+enum {
+ IFLA_HSR_UNSPEC,
+ IFLA_HSR_SLAVE1,
+ IFLA_HSR_SLAVE2,
+ IFLA_HSR_MULTICAST_SPEC, /* Last byte of supervision addr */
+ IFLA_HSR_SUPERVISION_ADDR, /* Supervision frame multicast addr */
+ IFLA_HSR_SEQ_NR,
+ IFLA_HSR_VERSION, /* HSR version */
+ __IFLA_HSR_MAX,
+};
+
+#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1)
+
+/* STATS section */
+
+struct if_stats_msg {
+ __u8 family;
+ __u8 pad1;
+ __u16 pad2;
+ __u32 ifindex;
+ __u32 filter_mask;
+};
+
+/* A stats attribute can be netdev specific or a global stat.
+ * For netdev stats, lets use the prefix IFLA_STATS_LINK_*
+ */
+enum {
+ IFLA_STATS_UNSPEC, /* also used as 64bit pad attribute */
+ IFLA_STATS_LINK_64,
+ IFLA_STATS_LINK_XSTATS,
+ IFLA_STATS_LINK_XSTATS_SLAVE,
+ IFLA_STATS_LINK_OFFLOAD_XSTATS,
+ IFLA_STATS_AF_SPEC,
+ __IFLA_STATS_MAX,
+};
+
+#define IFLA_STATS_MAX (__IFLA_STATS_MAX - 1)
+
+#define IFLA_STATS_FILTER_BIT(ATTR) (1 << (ATTR - 1))
+
+/* These are embedded into IFLA_STATS_LINK_XSTATS:
+ * [IFLA_STATS_LINK_XSTATS]
+ * -> [LINK_XSTATS_TYPE_xxx]
+ * -> [rtnl link type specific attributes]
+ */
+enum {
+ LINK_XSTATS_TYPE_UNSPEC,
+ LINK_XSTATS_TYPE_BRIDGE,
+ LINK_XSTATS_TYPE_BOND,
+ __LINK_XSTATS_TYPE_MAX
+};
+#define LINK_XSTATS_TYPE_MAX (__LINK_XSTATS_TYPE_MAX - 1)
+
+/* These are stats embedded into IFLA_STATS_LINK_OFFLOAD_XSTATS */
+enum {
+ IFLA_OFFLOAD_XSTATS_UNSPEC,
+ IFLA_OFFLOAD_XSTATS_CPU_HIT, /* struct rtnl_link_stats64 */
+ __IFLA_OFFLOAD_XSTATS_MAX
+};
+#define IFLA_OFFLOAD_XSTATS_MAX (__IFLA_OFFLOAD_XSTATS_MAX - 1)
+
+/* XDP section */
+
+#define XDP_FLAGS_UPDATE_IF_NOEXIST (1U << 0)
+#define XDP_FLAGS_SKB_MODE (1U << 1)
+#define XDP_FLAGS_DRV_MODE (1U << 2)
+#define XDP_FLAGS_HW_MODE (1U << 3)
+#define XDP_FLAGS_MODES (XDP_FLAGS_SKB_MODE | \
+ XDP_FLAGS_DRV_MODE | \
+ XDP_FLAGS_HW_MODE)
+#define XDP_FLAGS_MASK (XDP_FLAGS_UPDATE_IF_NOEXIST | \
+ XDP_FLAGS_MODES)
+
+/* These are stored into IFLA_XDP_ATTACHED on dump. */
+enum {
+ XDP_ATTACHED_NONE = 0,
+ XDP_ATTACHED_DRV,
+ XDP_ATTACHED_SKB,
+ XDP_ATTACHED_HW,
+ XDP_ATTACHED_MULTI,
+};
+
+enum {
+ IFLA_XDP_UNSPEC,
+ IFLA_XDP_FD,
+ IFLA_XDP_ATTACHED,
+ IFLA_XDP_FLAGS,
+ IFLA_XDP_PROG_ID,
+ IFLA_XDP_DRV_PROG_ID,
+ IFLA_XDP_SKB_PROG_ID,
+ IFLA_XDP_HW_PROG_ID,
+ __IFLA_XDP_MAX,
+};
+
+#define IFLA_XDP_MAX (__IFLA_XDP_MAX - 1)
+
+enum {
+ IFLA_EVENT_NONE,
+ IFLA_EVENT_REBOOT, /* internal reset / reboot */
+ IFLA_EVENT_FEATURES, /* change in offload features */
+ IFLA_EVENT_BONDING_FAILOVER, /* change in active slave */
+ IFLA_EVENT_NOTIFY_PEERS, /* re-sent grat. arp/ndisc */
+ IFLA_EVENT_IGMP_RESEND, /* re-sent IGMP JOIN */
+ IFLA_EVENT_BONDING_OPTIONS, /* change in bonding options */
+};
+
+/* tun section */
+
+enum {
+ IFLA_TUN_UNSPEC,
+ IFLA_TUN_OWNER,
+ IFLA_TUN_GROUP,
+ IFLA_TUN_TYPE,
+ IFLA_TUN_PI,
+ IFLA_TUN_VNET_HDR,
+ IFLA_TUN_PERSIST,
+ IFLA_TUN_MULTI_QUEUE,
+ IFLA_TUN_NUM_QUEUES,
+ IFLA_TUN_NUM_DISABLED_QUEUES,
+ __IFLA_TUN_MAX,
+};
+
+#define IFLA_TUN_MAX (__IFLA_TUN_MAX - 1)
+
+/* rmnet section */
+
+#define RMNET_FLAGS_INGRESS_DEAGGREGATION (1U << 0)
+#define RMNET_FLAGS_INGRESS_MAP_COMMANDS (1U << 1)
+#define RMNET_FLAGS_INGRESS_MAP_CKSUMV4 (1U << 2)
+#define RMNET_FLAGS_EGRESS_MAP_CKSUMV4 (1U << 3)
+
+enum {
+ IFLA_RMNET_UNSPEC,
+ IFLA_RMNET_MUX_ID,
+ IFLA_RMNET_FLAGS,
+ __IFLA_RMNET_MAX,
+};
+
+#define IFLA_RMNET_MAX (__IFLA_RMNET_MAX - 1)
+
+struct ifla_rmnet_flags {
+ __u32 flags;
+ __u32 mask;
+};
+
+#endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/src/contrib/libbpf/include/uapi/linux/if_xdp.h b/src/contrib/libbpf/include/uapi/linux/if_xdp.h
new file mode 100644
index 0000000..be328c5
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/if_xdp.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * if_xdp: XDP socket user-space interface
+ * Copyright(c) 2018 Intel Corporation.
+ *
+ * Author(s): Björn Töpel <bjorn.topel@intel.com>
+ * Magnus Karlsson <magnus.karlsson@intel.com>
+ */
+
+#ifndef _LINUX_IF_XDP_H
+#define _LINUX_IF_XDP_H
+
+#include <linux/types.h>
+
+/* Options for the sxdp_flags field */
+#define XDP_SHARED_UMEM (1 << 0)
+#define XDP_COPY (1 << 1) /* Force copy-mode */
+#define XDP_ZEROCOPY (1 << 2) /* Force zero-copy mode */
+/* If this option is set, the driver might go sleep and in that case
+ * the XDP_RING_NEED_WAKEUP flag in the fill and/or Tx rings will be
+ * set. If it is set, the application need to explicitly wake up the
+ * driver with a poll() (Rx and Tx) or sendto() (Tx only). If you are
+ * running the driver and the application on the same core, you should
+ * use this option so that the kernel will yield to the user space
+ * application.
+ */
+#define XDP_USE_NEED_WAKEUP (1 << 3)
+
+/* Flags for xsk_umem_config flags */
+#define XDP_UMEM_UNALIGNED_CHUNK_FLAG (1 << 0)
+
+struct sockaddr_xdp {
+ __u16 sxdp_family;
+ __u16 sxdp_flags;
+ __u32 sxdp_ifindex;
+ __u32 sxdp_queue_id;
+ __u32 sxdp_shared_umem_fd;
+};
+
+/* XDP_RING flags */
+#define XDP_RING_NEED_WAKEUP (1 << 0)
+
+struct xdp_ring_offset {
+ __u64 producer;
+ __u64 consumer;
+ __u64 desc;
+ __u64 flags;
+};
+
+struct xdp_mmap_offsets {
+ struct xdp_ring_offset rx;
+ struct xdp_ring_offset tx;
+ struct xdp_ring_offset fr; /* Fill */
+ struct xdp_ring_offset cr; /* Completion */
+};
+
+/* XDP socket options */
+#define XDP_MMAP_OFFSETS 1
+#define XDP_RX_RING 2
+#define XDP_TX_RING 3
+#define XDP_UMEM_REG 4
+#define XDP_UMEM_FILL_RING 5
+#define XDP_UMEM_COMPLETION_RING 6
+#define XDP_STATISTICS 7
+#define XDP_OPTIONS 8
+
+struct xdp_umem_reg {
+ __u64 addr; /* Start of packet data area */
+ __u64 len; /* Length of packet data area */
+ __u32 chunk_size;
+ __u32 headroom;
+ __u32 flags;
+};
+
+struct xdp_statistics {
+ __u64 rx_dropped; /* Dropped for reasons other than invalid desc */
+ __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
+ __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
+};
+
+struct xdp_options {
+ __u32 flags;
+};
+
+/* Flags for the flags field of struct xdp_options */
+#define XDP_OPTIONS_ZEROCOPY (1 << 0)
+
+/* Pgoff for mmaping the rings */
+#define XDP_PGOFF_RX_RING 0
+#define XDP_PGOFF_TX_RING 0x80000000
+#define XDP_UMEM_PGOFF_FILL_RING 0x100000000ULL
+#define XDP_UMEM_PGOFF_COMPLETION_RING 0x180000000ULL
+
+/* Masks for unaligned chunks mode */
+#define XSK_UNALIGNED_BUF_OFFSET_SHIFT 48
+#define XSK_UNALIGNED_BUF_ADDR_MASK \
+ ((1ULL << XSK_UNALIGNED_BUF_OFFSET_SHIFT) - 1)
+
+/* Rx/Tx descriptor */
+struct xdp_desc {
+ __u64 addr;
+ __u32 len;
+ __u32 options;
+};
+
+/* UMEM descriptor is __u64 */
+
+#endif /* _LINUX_IF_XDP_H */
diff --git a/src/contrib/libbpf/include/uapi/linux/netlink.h b/src/contrib/libbpf/include/uapi/linux/netlink.h
new file mode 100644
index 0000000..0a4d733
--- /dev/null
+++ b/src/contrib/libbpf/include/uapi/linux/netlink.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI__LINUX_NETLINK_H
+#define _UAPI__LINUX_NETLINK_H
+
+#include <linux/kernel.h>
+#include <linux/socket.h> /* for __kernel_sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_UNUSED 1 /* Unused number */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Unused number, formerly ip_queue */
+#define NETLINK_SOCK_DIAG 4 /* socket monitoring */
+#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
+#define NETLINK_XFRM 6 /* ipsec */
+#define NETLINK_SELINUX 7 /* SELinux event notifications */
+#define NETLINK_ISCSI 8 /* Open-iSCSI */
+#define NETLINK_AUDIT 9 /* auditing */
+#define NETLINK_FIB_LOOKUP 10
+#define NETLINK_CONNECTOR 11
+#define NETLINK_NETFILTER 12 /* netfilter subsystem */
+#define NETLINK_IP6_FW 13
+#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
+#define NETLINK_GENERIC 16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
+#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
+#define NETLINK_CRYPTO 21 /* Crypto layer */
+#define NETLINK_SMC 22 /* SMC monitoring */
+
+#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
+
+#define MAX_LINKS 32
+
+struct sockaddr_nl {
+ __kernel_sa_family_t nl_family; /* AF_NETLINK */
+ unsigned short nl_pad; /* zero */
+ __u32 nl_pid; /* port ID */
+ __u32 nl_groups; /* multicast groups mask */
+};
+
+struct nlmsghdr {
+ __u32 nlmsg_len; /* Length of message including header */
+ __u16 nlmsg_type; /* Message content */
+ __u16 nlmsg_flags; /* Additional flags */
+ __u32 nlmsg_seq; /* Sequence number */
+ __u32 nlmsg_pid; /* Sending process port ID */
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST 0x01 /* It is request message. */
+#define NLM_F_MULTI 0x02 /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK 0x04 /* Reply with ack, with zero or error code */
+#define NLM_F_ECHO 0x08 /* Echo this request */
+#define NLM_F_DUMP_INTR 0x10 /* Dump was inconsistent due to sequence change */
+#define NLM_F_DUMP_FILTERED 0x20 /* Dump was filtered as requested */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT 0x100 /* specify tree root */
+#define NLM_F_MATCH 0x200 /* return all matching */
+#define NLM_F_ATOMIC 0x400 /* atomic GET */
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE 0x100 /* Override existing */
+#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
+#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
+#define NLM_F_APPEND 0x800 /* Add to end of list */
+
+/* Modifiers to DELETE request */
+#define NLM_F_NONREC 0x100 /* Do not delete recursively */
+
+/* Flags for ACK message */
+#define NLM_F_CAPPED 0x100 /* request was capped */
+#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */
+
+/*
+ 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
+ 4.4BSD CHANGE NLM_F_REPLACE
+
+ True CHANGE NLM_F_CREATE|NLM_F_REPLACE
+ Append NLM_F_CREATE
+ Check NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO 4U
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+ (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP 0x1 /* Nothing. */
+#define NLMSG_ERROR 0x2 /* Error */
+#define NLMSG_DONE 0x3 /* End of a dump */
+#define NLMSG_OVERRUN 0x4 /* Data lost */
+
+#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
+
+struct nlmsgerr {
+ int error;
+ struct nlmsghdr msg;
+ /*
+ * followed by the message contents unless NETLINK_CAP_ACK was set
+ * or the ACK indicates success (error == 0)
+ * message length is aligned with NLMSG_ALIGN()
+ */
+ /*
+ * followed by TLVs defined in enum nlmsgerr_attrs
+ * if NETLINK_EXT_ACK was set
+ */
+};
+
+/**
+ * enum nlmsgerr_attrs - nlmsgerr attributes
+ * @NLMSGERR_ATTR_UNUSED: unused
+ * @NLMSGERR_ATTR_MSG: error message string (string)
+ * @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
+ * message, counting from the beginning of the header (u32)
+ * @NLMSGERR_ATTR_COOKIE: arbitrary subsystem specific cookie to
+ * be used - in the success case - to identify a created
+ * object or operation or similar (binary)
+ * @__NLMSGERR_ATTR_MAX: number of attributes
+ * @NLMSGERR_ATTR_MAX: highest attribute number
+ */
+enum nlmsgerr_attrs {
+ NLMSGERR_ATTR_UNUSED,
+ NLMSGERR_ATTR_MSG,
+ NLMSGERR_ATTR_OFFS,
+ NLMSGERR_ATTR_COOKIE,
+
+ __NLMSGERR_ATTR_MAX,
+ NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
+};
+
+#define NETLINK_ADD_MEMBERSHIP 1
+#define NETLINK_DROP_MEMBERSHIP 2
+#define NETLINK_PKTINFO 3
+#define NETLINK_BROADCAST_ERROR 4
+#define NETLINK_NO_ENOBUFS 5
+#ifndef __KERNEL__
+#define NETLINK_RX_RING 6
+#define NETLINK_TX_RING 7
+#endif
+#define NETLINK_LISTEN_ALL_NSID 8
+#define NETLINK_LIST_MEMBERSHIPS 9
+#define NETLINK_CAP_ACK 10
+#define NETLINK_EXT_ACK 11
+#define NETLINK_GET_STRICT_CHK 12
+
+struct nl_pktinfo {
+ __u32 group;
+};
+
+struct nl_mmap_req {
+ unsigned int nm_block_size;
+ unsigned int nm_block_nr;
+ unsigned int nm_frame_size;
+ unsigned int nm_frame_nr;
+};
+
+struct nl_mmap_hdr {
+ unsigned int nm_status;
+ unsigned int nm_len;
+ __u32 nm_group;
+ /* credentials */
+ __u32 nm_pid;
+ __u32 nm_uid;
+ __u32 nm_gid;
+};
+
+#ifndef __KERNEL__
+enum nl_mmap_status {
+ NL_MMAP_STATUS_UNUSED,
+ NL_MMAP_STATUS_RESERVED,
+ NL_MMAP_STATUS_VALID,
+ NL_MMAP_STATUS_COPY,
+ NL_MMAP_STATUS_SKIP,
+};
+
+#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
+#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
+#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
+#endif
+
+#define NET_MAJOR 36 /* Major 36 is reserved for networking */
+
+enum {
+ NETLINK_UNCONNECTED = 0,
+ NETLINK_CONNECTED,
+};
+
+/*
+ * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct nlattr) | ing | | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr {
+ __u16 nla_len;
+ __u16 nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED (1 << 15)
+#define NLA_F_NET_BYTEORDER (1 << 14)
+#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+/* Generic 32 bitflags attribute content sent to the kernel.
+ *
+ * The value is a bitmap that defines the values being set
+ * The selector is a bitmask that defines which value is legit
+ *
+ * Examples:
+ * value = 0x0, and selector = 0x1
+ * implies we are selecting bit 1 and we want to set its value to 0.
+ *
+ * value = 0x2, and selector = 0x2
+ * implies we are selecting bit 2 and we want to set its value to 1.
+ *
+ */
+struct nla_bitfield32 {
+ __u32 value;
+ __u32 selector;
+};
+
+#endif /* _UAPI__LINUX_NETLINK_H */
diff --git a/src/contrib/libngtcp2/LICENSE b/src/contrib/libngtcp2/LICENSE
new file mode 100644
index 0000000..0161703
--- /dev/null
+++ b/src/contrib/libngtcp2/LICENSE
@@ -0,0 +1 @@
+../licenses/MIT \ No newline at end of file
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c
new file mode 100644
index 0000000..73ea0c1
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c
@@ -0,0 +1,644 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <string.h>
+
+#include "shared.h"
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)GNUTLS_DIG_SHA256;
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+ ctx->md.native_handle = (void *)GNUTLS_DIG_SHA256;
+ ctx->hp.native_handle = (void *)GNUTLS_CIPHER_AES_128_CBC;
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = gnutls_cipher_get_tag_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead_native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+}
+
+static gnutls_cipher_algorithm_t
+crypto_get_hp(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_128_CCM:
+ return GNUTLS_CIPHER_AES_128_CBC;
+ case GNUTLS_CIPHER_AES_256_GCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return GNUTLS_CIPHER_AES_256_CBC;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return GNUTLS_CIPHER_CHACHA20_32;
+ default:
+ return GNUTLS_CIPHER_UNKNOWN;
+ }
+}
+
+static uint64_t
+crypto_get_aead_max_encryption(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_256_GCM:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ case GNUTLS_CIPHER_AES_128_CCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t
+crypto_get_aead_max_decryption_failure(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_256_GCM:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ case GNUTLS_CIPHER_AES_128_CCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ gnutls_session_t session = tls_native_handle;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_digest_algorithm_t hash;
+ gnutls_cipher_algorithm_t hp_cipher;
+
+ cipher = gnutls_cipher_get(session);
+ if (cipher != GNUTLS_CIPHER_UNKNOWN && cipher != GNUTLS_CIPHER_NULL) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)cipher);
+ }
+
+ hash = gnutls_prf_hash_get(session);
+ if (hash != GNUTLS_DIG_UNKNOWN && hash != GNUTLS_DIG_NULL) {
+ ctx->md.native_handle = (void *)hash;
+ }
+
+ hp_cipher = crypto_get_hp(cipher);
+ if (hp_cipher != GNUTLS_CIPHER_UNKNOWN) {
+ ctx->hp.native_handle = (void *)hp_cipher;
+ }
+
+ ctx->max_encryption = crypto_get_aead_max_encryption(cipher);
+ ctx->max_decryption_failure = crypto_get_aead_max_decryption_failure(cipher);
+
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ gnutls_session_t session = tls_native_handle;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_digest_algorithm_t hash;
+ gnutls_cipher_algorithm_t hp_cipher;
+
+ cipher = gnutls_early_cipher_get(session);
+ if (cipher != GNUTLS_CIPHER_UNKNOWN && cipher != GNUTLS_CIPHER_NULL) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)cipher);
+ }
+
+ hash = gnutls_early_prf_hash_get(session);
+ if (hash != GNUTLS_DIG_UNKNOWN && hash != GNUTLS_DIG_NULL) {
+ ctx->md.native_handle = (void *)hash;
+ }
+
+ hp_cipher = crypto_get_hp(cipher);
+ if (hp_cipher != GNUTLS_CIPHER_UNKNOWN) {
+ ctx->hp.native_handle = (void *)hp_cipher;
+ }
+
+ ctx->max_encryption = crypto_get_aead_max_encryption(cipher);
+ ctx->max_decryption_failure = crypto_get_aead_max_decryption_failure(cipher);
+
+ return ctx;
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return gnutls_hash_get_len(
+ (gnutls_digest_algorithm_t)(intptr_t)md->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return gnutls_cipher_get_key_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return gnutls_cipher_get_iv_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ (void)noncelen;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
+
+ if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = hd;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ (void)noncelen;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
+
+ if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = hd;
+
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ gnutls_aead_cipher_deinit(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ gnutls_cipher_algorithm_t _cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)cipher->native_handle;
+ gnutls_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)gnutls_cipher_get_key_size(_cipher);
+
+ if (gnutls_cipher_init(&hd, _cipher, &_key, NULL) != 0) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = hd;
+
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ gnutls_cipher_deinit(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
+
+ if (gnutls_hkdf_extract(prf, &_secret, &_salt, dest) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
+
+ if (gnutls_hkdf_expand(prf, &_secret, &_info, dest, destlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ size_t keylen = ngtcp2_crypto_md_hashlen(md);
+ uint8_t key[64];
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _key = {(void *)key, (unsigned int)keylen};
+ gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
+ gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
+
+ assert(keylen <= sizeof(key));
+
+ if (gnutls_hkdf_extract(prf, &_secret, &_salt, key) != 0) {
+ return -1;
+ }
+
+ if (gnutls_hkdf_expand(prf, &_key, &_info, dest, destlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd = aead_ctx->native_handle;
+ size_t taglen = gnutls_cipher_get_tag_size(cipher);
+ size_t ciphertextlen = plaintextlen + taglen;
+
+ if (gnutls_aead_cipher_encrypt(hd, nonce, noncelen, aad, aadlen, taglen,
+ plaintext, plaintextlen, dest,
+ &ciphertextlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd = aead_ctx->native_handle;
+ size_t taglen = gnutls_cipher_get_tag_size(cipher);
+ size_t plaintextlen;
+
+ if (taglen > ciphertextlen) {
+ return -1;
+ }
+
+ plaintextlen = ciphertextlen - taglen;
+
+ if (gnutls_aead_cipher_decrypt(hd, nonce, noncelen, aad, aadlen, taglen,
+ ciphertext, ciphertextlen, dest,
+ &plaintextlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)hp->native_handle;
+ gnutls_cipher_hd_t hd = hp_ctx->native_handle;
+
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_CBC:
+ case GNUTLS_CIPHER_AES_256_CBC: {
+ uint8_t iv[16];
+ uint8_t buf[16];
+
+ /* Emulate one block AES-ECB by invalidating the effect of IV */
+ memset(iv, 0, sizeof(iv));
+
+ gnutls_cipher_set_iv(hd, iv, sizeof(iv));
+
+ if (gnutls_cipher_encrypt2(hd, sample, 16, buf, sizeof(buf)) != 0) {
+ return -1;
+ }
+
+ memcpy(dest, buf, 5);
+ } break;
+
+ case GNUTLS_CIPHER_CHACHA20_32: {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ uint8_t buf[5 + 16];
+ size_t buflen = sizeof(buf);
+
+ gnutls_cipher_set_iv(hd, (void *)sample, 16);
+
+ if (gnutls_cipher_encrypt2(hd, PLAINTEXT, sizeof(PLAINTEXT) - 1, buf,
+ buflen) != 0) {
+ return -1;
+ }
+
+ memcpy(dest, buf, 5);
+ } break;
+ default:
+ assert(0);
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(
+ gnutls_record_encryption_level_t gtls_level) {
+ switch (gtls_level) {
+ case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ case GNUTLS_ENCRYPTION_LEVEL_EARLY:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+gnutls_record_encryption_level_t
+ngtcp2_crypto_gnutls_from_ngtcp2_level(ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return GNUTLS_ENCRYPTION_LEVEL_INITIAL;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return GNUTLS_ENCRYPTION_LEVEL_APPLICATION;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return GNUTLS_ENCRYPTION_LEVEL_EARLY;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ gnutls_session_t session = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+
+ if (datalen > 0) {
+ rv = gnutls_handshake_write(
+ session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data,
+ datalen);
+ if (rv != 0) {
+ if (!gnutls_error_is_fatal(rv)) {
+ return 0;
+ }
+ gnutls_alert_send_appropriate(session, rv);
+ return -1;
+ }
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = gnutls_handshake(session);
+ if (rv < 0) {
+ if (!gnutls_error_is_fatal(rv)) {
+ return 0;
+ }
+ gnutls_alert_send_appropriate(session, rv);
+ return -1;
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ (void)conn;
+ (void)tls;
+ /* Nothing to do; GnuTLS applications are supposed to register the
+ quic_transport_parameters extension with
+ gnutls_session_ext_register. */
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ (void)tls;
+ (void)buf;
+ (void)len;
+ /* Nothing to do; GnuTLS applications are supposed to register the
+ quic_transport_parameters extension with
+ gnutls_session_ext_register. */
+ return 0;
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data, NGTCP2_PATH_CHALLENGE_DATALEN) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data, datalen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int secret_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ const void *rx_secret, const void *tx_secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
+
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ gnutls_handshake_description_t htype, const void *data,
+ size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
+ int rv;
+
+ if (htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC) {
+ return 0;
+ }
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int alert_read_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ gnutls_alert_level_t alert_level,
+ gnutls_alert_description_t alert_desc) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)gtls_level;
+ (void)alert_level;
+
+ ngtcp2_conn_set_tls_alert(conn, (uint8_t)alert_desc);
+
+ return 0;
+}
+
+static int tp_recv_func(gnutls_session_t session, const uint8_t *data,
+ size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ int rv;
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tp_send_func(gnutls_session_t session, gnutls_buffer_t extdata) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ uint8_t buf[256];
+ ngtcp2_ssize nwrite;
+ int rv;
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
+ if (nwrite < 0) {
+ return -1;
+ }
+
+ rv = gnutls_buffer_append_data(extdata, buf, (size_t)nwrite);
+ if (rv != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_gnutls_configure_session(gnutls_session_t session) {
+ int rv;
+
+ gnutls_handshake_set_secret_function(session, secret_func);
+ gnutls_handshake_set_read_function(session, read_func);
+ gnutls_alert_set_read_function(session, alert_read_func);
+
+ rv = gnutls_session_ext_register(
+ session, "QUIC Transport Parameters",
+ NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS, tp_recv_func,
+ tp_send_func, NULL, NULL, NULL,
+ GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE);
+ if (rv != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_gnutls_configure_server_session(gnutls_session_t session) {
+ return crypto_gnutls_configure_session(session);
+}
+
+int ngtcp2_crypto_gnutls_configure_client_session(gnutls_session_t session) {
+ return crypto_gnutls_configure_session(session);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.c b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c
new file mode 100644
index 0000000..f38e0b1
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c
@@ -0,0 +1,1418 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "shared.h"
+
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <netinet/in.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_net.h"
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle) {
+ md->native_handle = md_native_handle;
+ return md;
+}
+
+int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *label, size_t labellen) {
+ static const uint8_t LABEL[] = "tls13 ";
+ uint8_t info[256];
+ uint8_t *p = info;
+
+ *p++ = (uint8_t)(destlen / 256);
+ *p++ = (uint8_t)(destlen % 256);
+ *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen);
+ memcpy(p, LABEL, sizeof(LABEL) - 1);
+ p += sizeof(LABEL) - 1;
+ memcpy(p, label, labellen);
+ p += labellen;
+ *p++ = 0;
+
+ return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info,
+ (size_t)(p - info));
+}
+
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side) {
+ static const uint8_t CLABEL[] = "client in";
+ static const uint8_t SLABEL[] = "server in";
+ uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t *client_secret;
+ uint8_t *server_secret;
+ ngtcp2_crypto_ctx ctx;
+ const uint8_t *salt;
+ size_t saltlen;
+
+ if (!initial_secret) {
+ initial_secret = initial_secret_buf;
+ }
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V2) - 1;
+ break;
+ default:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data,
+ client_dcid->datalen, salt, saltlen) != 0) {
+ return -1;
+ }
+
+ if (side == NGTCP2_CRYPTO_SIDE_SERVER) {
+ client_secret = rx_secret;
+ server_secret = tx_secret;
+ } else {
+ client_secret = tx_secret;
+ server_secret = rx_secret;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(
+ client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL,
+ sizeof(CLABEL) - 1) != 0 ||
+ ngtcp2_crypto_hkdf_expand_label(
+ server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL,
+ sizeof(SLABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
+ size_t noncelen = ngtcp2_crypto_aead_noncelen(aead);
+ return ngtcp2_max(8, noncelen);
+}
+
+int ngtcp2_crypto_derive_packet_protection_key(
+ uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version,
+ const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen) {
+ static const uint8_t KEY_LABEL_V1[] = "quic key";
+ static const uint8_t IV_LABEL_V1[] = "quic iv";
+ static const uint8_t HP_KEY_LABEL_V1[] = "quic hp";
+ static const uint8_t KEY_LABEL_V2[] = "quicv2 key";
+ static const uint8_t IV_LABEL_V2[] = "quicv2 iv";
+ static const uint8_t HP_KEY_LABEL_V2[] = "quicv2 hp";
+ size_t keylen = ngtcp2_crypto_aead_keylen(aead);
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ const uint8_t *key_label;
+ size_t key_labellen;
+ const uint8_t *iv_label;
+ size_t iv_labellen;
+ const uint8_t *hp_key_label;
+ size_t hp_key_labellen;
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2:
+ key_label = KEY_LABEL_V2;
+ key_labellen = sizeof(KEY_LABEL_V2) - 1;
+ iv_label = IV_LABEL_V2;
+ iv_labellen = sizeof(IV_LABEL_V2) - 1;
+ hp_key_label = HP_KEY_LABEL_V2;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V2) - 1;
+ break;
+ default:
+ key_label = KEY_LABEL_V1;
+ key_labellen = sizeof(KEY_LABEL_V1) - 1;
+ iv_label = IV_LABEL_V1;
+ iv_labellen = sizeof(IV_LABEL_V1) - 1;
+ hp_key_label = HP_KEY_LABEL_V1;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
+ key_label, key_labellen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
+ iv_label, iv_labellen) != 0) {
+ return -1;
+ }
+
+ if (hp_key != NULL &&
+ ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen,
+ hp_key_label, hp_key_labellen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen) {
+ static const uint8_t LABEL[] = "quic ku";
+
+ if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen,
+ LABEL, sizeof(LABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+ uint32_t version;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ ngtcp2_crypto_ctx_tls_early(&cctx, tls);
+ ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ hp = &ctx->hp;
+ ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ if (!ngtcp2_conn_is_server(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+/*
+ * crypto_set_local_transport_params gets local QUIC transport
+ * parameters from |conn| and sets it to |tls|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
+ ngtcp2_ssize nwrite;
+ uint8_t buf[256];
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
+ if (nwrite < 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+ uint32_t version;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ ngtcp2_crypto_ctx_tls_early(&cctx, tls);
+ ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ hp = &ctx->hp;
+ ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_conn_is_server(conn) &&
+ crypto_set_local_transport_params(conn, tls) != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_derive_and_install_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+ uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+ uint32_t version, const ngtcp2_cid *client_dcid) {
+ uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_crypto_aead retry_aead;
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx retry_aead_ctx = {0};
+ int rv;
+ int server = ngtcp2_conn_is_server(conn);
+ const uint8_t *retry_key;
+ size_t retry_noncelen;
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (!rx_secret) {
+ rx_secret = rx_secretbuf;
+ }
+ if (!tx_secret) {
+ tx_secret = tx_secretbuf;
+ }
+ if (!initial_secret) {
+ initial_secret = initial_secretbuf;
+ }
+
+ if (!rx_key) {
+ rx_key = rx_keybuf;
+ }
+ if (!rx_iv) {
+ rx_iv = rx_ivbuf;
+ }
+ if (!rx_hp_key) {
+ rx_hp_key = rx_hp_keybuf;
+ }
+ if (!tx_key) {
+ tx_key = tx_keybuf;
+ }
+ if (!tx_iv) {
+ tx_iv = tx_ivbuf;
+ }
+ if (!tx_hp_key) {
+ tx_hp_key = tx_hp_keybuf;
+ }
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx);
+
+ if (ngtcp2_crypto_derive_initial_secrets(
+ version, rx_secret, tx_secret, initial_secret, client_dcid,
+ server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx.aead, rx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx.hp, rx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx.aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx.hp, tx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (!server && !ngtcp2_conn_after_retry(conn)) {
+ ngtcp2_crypto_aead_retry(&retry_aead);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ break;
+ default:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&retry_aead_ctx, &retry_aead,
+ retry_key, retry_noncelen) != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_initial_key(conn, &rx_aead_ctx, rx_iv, &rx_hp_ctx,
+ &tx_aead_ctx, tx_iv, &tx_hp_ctx,
+ NGTCP2_CRYPTO_INITIAL_IVLEN);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (retry_aead_ctx.native_handle) {
+ ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx);
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+ uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+ uint32_t version, const ngtcp2_cid *client_dcid) {
+ uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn);
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+ int rv;
+ int server = ngtcp2_conn_is_server(conn);
+
+ if (!rx_secret) {
+ rx_secret = rx_secretbuf;
+ }
+ if (!tx_secret) {
+ tx_secret = tx_secretbuf;
+ }
+ if (!initial_secret) {
+ initial_secret = initial_secretbuf;
+ }
+
+ if (!rx_key) {
+ rx_key = rx_keybuf;
+ }
+ if (!rx_iv) {
+ rx_iv = rx_ivbuf;
+ }
+ if (!rx_hp_key) {
+ rx_hp_key = rx_hp_keybuf;
+ }
+ if (!tx_key) {
+ tx_key = tx_keybuf;
+ }
+ if (!tx_iv) {
+ tx_iv = tx_ivbuf;
+ }
+ if (!tx_hp_key) {
+ tx_hp_key = tx_hp_keybuf;
+ }
+
+ if (ngtcp2_crypto_derive_initial_secrets(
+ version, rx_secret, tx_secret, initial_secret, client_dcid,
+ server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ rv = ngtcp2_conn_install_vneg_initial_key(
+ conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv,
+ &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_update_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ const ngtcp2_crypto_aead *aead = &ctx->aead;
+ const ngtcp2_crypto_md *md = &ctx->md;
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret,
+ secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret,
+ secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) !=
+ 0) {
+ ngtcp2_crypto_aead_ctx_free(rx_aead_ctx);
+ rx_aead_ctx->native_handle = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen,
+ nonce, noncelen, aad, aadlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen,
+ nonce, noncelen, aad, aadlen) != 0) {
+ return NGTCP2_ERR_DECRYPT;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data) {
+ uint8_t rx_key[64];
+ uint8_t tx_key[64];
+ (void)conn;
+ (void)user_data;
+
+ if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_aead_ctx, rx_key,
+ rx_iv, tx_aead_ctx, tx_key, tx_iv,
+ current_rx_secret, current_tx_secret,
+ secretlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token,
+ const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_cid *cid) {
+ static const uint8_t info[] = "stateless_reset";
+ ngtcp2_crypto_md md;
+
+ if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN,
+ ngtcp2_crypto_md_sha256(&md), secret, secretlen,
+ cid->data, cid->datalen, info,
+ sizeof(info) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
+ size_t ivlen, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info_prefix,
+ size_t info_prefixlen) {
+ static const uint8_t key_info_suffix[] = " key";
+ static const uint8_t iv_info_suffix[] = " iv";
+ uint8_t intsecret[32];
+ uint8_t info[32];
+ uint8_t *p;
+
+ assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret));
+ assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info));
+ assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info));
+
+ if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt,
+ saltlen) != 0) {
+ return -1;
+ }
+
+ memcpy(info, info_prefix, info_prefixlen);
+ p = info + info_prefixlen;
+
+ memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1);
+ p += sizeof(key_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ p = info + info_prefixlen;
+
+ memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1);
+ p += sizeof(iv_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version,
+ const ngtcp2_sockaddr *sa,
+ ngtcp2_socklen salen,
+ const ngtcp2_cid *retry_scid) {
+ uint8_t *p = dest;
+
+ version = ngtcp2_htonl(version);
+ memcpy(p, &version, sizeof(version));
+ memcpy(p, sa, (size_t)salen);
+ p += salen;
+ memcpy(p, retry_scid->data, retry_scid->datalen);
+ p += retry_scid->datalen;
+
+ return (size_t)(p - dest);
+}
+
+static const uint8_t retry_token_info_prefix[] = "retry_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
+ uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
+ int rv;
+
+ memset(plaintext, 0, sizeof(plaintext));
+
+ *p++ = (uint8_t)odcid->datalen;
+ memcpy(p, odcid->data, odcid->datalen);
+ p += NGTCP2_MAX_CIDLEN;
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, retry_scid);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
+ uint8_t
+ plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ size_t cil;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, dcid);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ cil = plaintext[0];
+
+ assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN));
+
+ memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN,
+ sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil);
+
+ return 0;
+}
+
+static size_t crypto_generate_regular_token_aad(uint8_t *dest,
+ const ngtcp2_sockaddr *sa) {
+ const uint8_t *addr;
+ size_t addrlen;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr);
+ break;
+ case AF_INET6:
+ addr =
+ (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ memcpy(dest, addr, addrlen);
+
+ return addrlen;
+}
+
+static const uint8_t regular_token_info_prefix[] = "regular_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
+ int rv;
+ (void)remote_addrlen;
+
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+ (void)remote_addrlen;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ memcpy(&gen_ts, plaintext, sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen) {
+ uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (ngtcp2_crypto_derive_initial_secrets(version, rx_secret, tx_secret,
+ initial_secret, scid,
+ NGTCP2_CRYPTO_SIDE_SERVER) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ spktlen = ngtcp2_pkt_write_connection_close(
+ dest, destlen, version, dcid, scid, error_code, reason, reasonlen,
+ ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv,
+ ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+end:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return spktlen;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
+ uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid,
+ const ngtcp2_cid *odcid,
+ const uint8_t *token, size_t tokenlen) {
+ ngtcp2_crypto_aead aead;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ const uint8_t *key;
+ size_t noncelen;
+
+ ngtcp2_crypto_aead_retry(&aead);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V2;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ break;
+ default:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, noncelen) !=
+ 0) {
+ return -1;
+ }
+
+ spktlen = ngtcp2_pkt_write_retry(dest, destlen, version, dcid, scid, odcid,
+ token, tokenlen, ngtcp2_crypto_encrypt_cb,
+ &aead, &aead_ctx);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return spktlen;
+}
+
+int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
+ const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (crypto_set_local_transport_params(conn, tls) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL,
+ NULL, 0) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version,
+ client_dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_aead_ctx_free(aead_ctx);
+}
+
+void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_cipher_ctx_free(cipher_ctx);
+}
+
+int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ int rv;
+ (void)offset;
+ (void)user_data;
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) !=
+ 0) {
+ rv = ngtcp2_conn_get_tls_error(conn);
+ if (rv) {
+ return rv;
+ }
+ return NGTCP2_ERR_CRYPTO;
+ }
+
+ return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.h b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h
new file mode 100644
index 0000000..c7690c1
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h
@@ -0,0 +1,350 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_SHARED_H
+#define NGTCP2_SHARED_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2_crypto.h>
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_DRAFT` is a salt value which is used to
+ * derive initial secret. It is used for QUIC draft versions.
+ */
+#define NGTCP2_INITIAL_SALT_DRAFT \
+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90" \
+ "\xa8\x99"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v1.
+ */
+#define NGTCP2_INITIAL_SALT_V1 \
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \
+ "\x7f\x0a"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V2` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v2.
+ */
+#define NGTCP2_INITIAL_SALT_V2 \
+ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd" \
+ "\x2e\xd9"
+
+/* Maximum key usage (encryption) limits */
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL)
+
+/* Maximum authentication failure (decryption) limits during the
+ lifetime of a connection. */
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 52)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL)
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
+ * encryption and decryption.
+ */
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
+ * |aead_native_handle| which is an underlying AEAD object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
+ * a pointer to EVP_CIPHER.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
+ * gnutls_cipher_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
+ * be a pointer to EVP_AEAD.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
+ * AEAD_AES_128_GCM for Retry packet integrity protection.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_initial_secrets` derives initial secrets.
+ * |rx_secret| and |tx_secret| must point to the buffer of at least 32
+ * bytes capacity. rx for read and tx for write. This function
+ * writes rx and tx secrets into |rx_secret| and |tx_secret|
+ * respectively. The length of secret is 32 bytes long.
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client. If |initial_secret| is not NULL, the initial
+ * secret is written to it. It must point to the buffer which has at
+ * least 32 bytes capacity. The initial secret is 32 bytes long.
+ * |side| specifies the side of application.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_packet_protection_key` derives packet
+ * protection key. This function writes packet protection key into
+ * the buffer pointed by |key|. The length of derived key is
+ * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
+ * bytes. |key| must have enough capacity to store the key. This
+ * function writes packet protection IV into |iv|. The length of
+ * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have
+ * enough capacity to store the IV.
+ *
+ * If |hp| is not NULL, this function also derives packet header
+ * protection key and writes the key into the buffer pointed by |hp|.
+ * The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
+ * <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have
+ * enough capacity to store the key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv,
+ uint8_t *hp, uint32_t version,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_traffic_secret` derives the next generation
+ * of the traffic secret. |secret| specifies the current secret and
+ * its length is given in |secretlen|. The length of new key is the
+ * same as the current key. This function writes new key into the
+ * buffer pointed by |dest|. |dest| must have the enough capacity to
+ * store the new key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport
+ * parameter, which is encoded in wire format and stored in the buffer
+ * pointed by |buf| of length |len|, to the native handle |tls|.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC
+ * transport parameters from |tls| and sets it to |conn| using
+ * `ngtcp2_conn_set_remote_transport_params`.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_initial_key` derives initial
+ * keying materials and installs keys to |conn|.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|. The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|. The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|. The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|. If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|. If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long. The length of packet protection IV is 12 bytes long.
+ *
+ * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set
+ * initial AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_initial_crypto_ctx` to get the object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial
+ * keying materials and installs keys to |conn|. This function is
+ * dedicated to install keys for |version| which is negotiated, or
+ * being negotiated.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|. The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|. The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|. The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|. If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|. If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long. The length of packet protection IV is 12 bytes long.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx|
+ * with new cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |cipher| specifies cipher to use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by
+ * |cipher_ctx|. This function does not free the memory pointed by
+ * |cipher_ctx| itself.
+ */
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx);
+
+/*
+ * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message
+ * digest algorithm and returns |md|.
+ */
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md);
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead);
+
+/*
+ * `ngtcp2_crypto_random` writes cryptographically-secure random
+ * |datalen| bytes into the buffer pointed by |data|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen);
+
+#endif /* NGTCP2_SHARED_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
new file mode 100644
index 0000000..3f1f9b3
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
@@ -0,0 +1,335 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_acktr.h"
+
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp) {
+ ent->pkt_num = pkt_num;
+ ent->len = 1;
+ ent->tstamp = tstamp;
+}
+
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp,
+ ngtcp2_objalloc *objalloc) {
+ *ent = ngtcp2_objalloc_acktr_entry_get(objalloc);
+ if (*ent == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ acktr_entry_init(*ent, pkt_num, tstamp);
+
+ return 0;
+}
+
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+ ngtcp2_objalloc *objalloc) {
+ ngtcp2_objalloc_acktr_entry_release(objalloc, ent);
+}
+
+static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs > *(int64_t *)rhs;
+}
+
+int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ int rv;
+
+ ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem);
+
+ rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry),
+ mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
+
+ acktr->log = log;
+ acktr->mem = mem;
+ acktr->flags = NGTCP2_ACKTR_FLAG_NONE;
+ acktr->first_unacked_ts = UINT64_MAX;
+ acktr->rx_npkt = 0;
+
+ return 0;
+}
+
+void ngtcp2_acktr_free(ngtcp2_acktr *acktr) {
+#ifdef NOMEMPOOL
+ ngtcp2_ksl_it it;
+#endif /* NOMEMPOOL */
+
+ if (acktr == NULL) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc);
+ }
+#endif /* NOMEMPOOL */
+
+ ngtcp2_ksl_free(&acktr->ents);
+
+ ngtcp2_ringbuf_free(&acktr->acks);
+
+ ngtcp2_objalloc_free(&acktr->objalloc);
+}
+
+int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it, prev_it;
+ ngtcp2_acktr_entry *ent, *prev_ent, *delent;
+ int rv;
+ int added = 0;
+
+ if (ngtcp2_ksl_len(&acktr->ents)) {
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &pkt_num);
+ if (ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->pkt_num >= pkt_num + (int64_t)ent->len);
+
+ if (ent->pkt_num == pkt_num + (int64_t)ent->len) {
+ ++ent->len;
+ added = 1;
+ }
+ } else {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->pkt_num != pkt_num);
+
+ if (ngtcp2_ksl_it_begin(&it)) {
+ if (ent->pkt_num + 1 == pkt_num) {
+ ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
+ ent->pkt_num = pkt_num;
+ ent->tstamp = ts;
+ ++ent->len;
+ added = 1;
+ }
+ } else {
+ prev_it = it;
+ ngtcp2_ksl_it_prev(&prev_it);
+ prev_ent = ngtcp2_ksl_it_get(&prev_it);
+
+ assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len);
+
+ if (ent->pkt_num + 1 == pkt_num) {
+ if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
+ prev_ent->len += ent->len + 1;
+ ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
+ added = 1;
+ } else {
+ ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
+ ent->pkt_num = pkt_num;
+ ent->tstamp = ts;
+ ++ent->len;
+ added = 1;
+ }
+ } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
+ ++prev_ent->len;
+ added = 1;
+ }
+ }
+ }
+ }
+
+ if (!added) {
+ rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent);
+ if (rv != 0) {
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
+ return rv;
+ }
+ }
+
+ if (active_ack) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_ACTIVE_ACK;
+ if (acktr->first_unacked_ts == UINT64_MAX) {
+ acktr->first_unacked_ts = ts;
+ }
+ }
+
+ if (ngtcp2_ksl_len(&acktr->ents) > NGTCP2_ACKTR_MAX_ENT) {
+ it = ngtcp2_ksl_end(&acktr->ents);
+ ngtcp2_ksl_it_prev(&it);
+ delent = ngtcp2_ksl_it_get(&it);
+ ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc);
+ }
+
+ return 0;
+}
+
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &ent->pkt_num);
+ assert(*(int64_t *)ngtcp2_ksl_it_key(&it) == (int64_t)ent->pkt_num);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+ ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
+ }
+}
+
+ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) {
+ return ngtcp2_ksl_begin(&acktr->ents);
+}
+
+int ngtcp2_acktr_empty(ngtcp2_acktr *acktr) {
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&acktr->ents);
+ return ngtcp2_ksl_it_end(&it);
+}
+
+ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
+ int64_t pkt_num,
+ int64_t largest_ack) {
+ ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks);
+
+ ent->largest_ack = largest_ack;
+ ent->pkt_num = pkt_num;
+
+ return ent;
+}
+
+/*
+ * acktr_remove removes |ent| from |acktr|. |it| must point to the
+ * node whose key identifies |ent|. The iterator which points to the
+ * entry next to |ent| is assigned to |it|.
+ */
+static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
+ ngtcp2_acktr_entry *ent) {
+ ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num);
+ ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
+}
+
+static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
+ size_t ack_ent_offset) {
+ ngtcp2_acktr_ack_entry *ack_ent;
+ ngtcp2_acktr_entry *ent;
+ ngtcp2_ksl_it it;
+
+ assert(ngtcp2_ringbuf_len(rb));
+
+ ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset);
+
+ /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
+ it = ngtcp2_ksl_lower_bound(&acktr->ents, &ack_ent->largest_ack);
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+ acktr_remove(acktr, &it, ent);
+ }
+
+ if (ngtcp2_ksl_len(&acktr->ents)) {
+ assert(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+ if (ent->pkt_num > ack_ent->largest_ack &&
+ ack_ent->largest_ack >= ent->pkt_num - (int64_t)(ent->len - 1)) {
+ ent->len = (size_t)(ent->pkt_num - ack_ent->largest_ack);
+ }
+ }
+
+ ngtcp2_ringbuf_resize(rb, ack_ent_offset);
+}
+
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) {
+ ngtcp2_acktr_ack_entry *ent;
+ int64_t largest_ack = fr->largest_ack, min_ack;
+ size_t i, j;
+ ngtcp2_ringbuf *rb = &acktr->acks;
+ size_t nacks = ngtcp2_ringbuf_len(rb);
+
+ /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
+ for (j = 0; j < nacks; ++j) {
+ ent = ngtcp2_ringbuf_get(rb, j);
+ if (largest_ack >= ent->pkt_num) {
+ break;
+ }
+ }
+ if (j == nacks) {
+ return;
+ }
+
+ min_ack = largest_ack - (int64_t)fr->first_ack_range;
+
+ if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) {
+ acktr_on_ack(acktr, rb, j);
+ return;
+ }
+
+ for (i = 0; i < fr->rangecnt && j < nacks; ++i) {
+ largest_ack = min_ack - (int64_t)fr->ranges[i].gap - 2;
+ min_ack = largest_ack - (int64_t)fr->ranges[i].len;
+
+ for (;;) {
+ if (ent->pkt_num > largest_ack) {
+ ++j;
+ if (j == nacks) {
+ return;
+ }
+ ent = ngtcp2_ringbuf_get(rb, j);
+ continue;
+ }
+ if (ent->pkt_num < min_ack) {
+ break;
+ }
+ acktr_on_ack(acktr, rb, j);
+ return;
+ }
+ }
+}
+
+void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) {
+ acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK |
+ NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK |
+ NGTCP2_ACKTR_FLAG_CANCEL_TIMER);
+ acktr->first_unacked_ts = UINT64_MAX;
+ acktr->rx_npkt = 0;
+}
+
+int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr,
+ ngtcp2_duration max_ack_delay,
+ ngtcp2_tstamp ts) {
+ return acktr->first_unacked_ts != UINT64_MAX &&
+ acktr->first_unacked_ts + max_ack_delay <= ts;
+}
+
+void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h
new file mode 100644
index 0000000..70a3c71
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h
@@ -0,0 +1,221 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ACKTR_H
+#define NGTCP2_ACKTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_ringbuf.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_objalloc.h"
+
+/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
+ which ngtcp2_acktr stores. */
+#define NGTCP2_ACKTR_MAX_ENT 1024
+
+typedef struct ngtcp2_log ngtcp2_log;
+
+/*
+ * ngtcp2_acktr_entry is a range of packets which need to be acked.
+ */
+typedef struct ngtcp2_acktr_entry {
+ union {
+ struct {
+ /* pkt_num is the largest packet number to acknowledge in this
+ range. */
+ int64_t pkt_num;
+ /* len is the consecutive packets started from pkt_num which
+ includes pkt_num itself counting in decreasing order. So pkt_num
+ = 987 and len = 2, this entry includes packet 987 and 986. */
+ size_t len;
+ /* tstamp is the timestamp when a packet denoted by pkt_num is
+ received. */
+ ngtcp2_tstamp tstamp;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
+} ngtcp2_acktr_entry;
+
+ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent);
+
+/*
+ * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and
+ * initializes it with the given parameters. The pointer to the
+ * allocated object is stored to |*ent|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+ ngtcp2_tstamp tstamp,
+ ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for
+ * |ent|.
+ */
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+ ngtcp2_objalloc *objalloc);
+
+typedef struct ngtcp2_acktr_ack_entry {
+ /* largest_ack is the largest packet number in outgoing ACK frame */
+ int64_t largest_ack;
+ /* pkt_num is the packet number that ACK frame is included. */
+ int64_t pkt_num;
+} ngtcp2_acktr_ack_entry;
+
+/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */
+#define NGTCP2_ACKTR_FLAG_NONE 0x00u
+/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate
+ acknowledgement is required. */
+#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01u
+/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending
+ protected packet to be acknowledged. */
+#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02u
+/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is
+ expired and canceled. */
+#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100u
+
+/*
+ * ngtcp2_acktr tracks received packets which we have to send ack.
+ */
+typedef struct ngtcp2_acktr {
+ ngtcp2_objalloc objalloc;
+ ngtcp2_ringbuf acks;
+ /* ents includes ngtcp2_acktr_entry sorted by decreasing order of
+ packet number. */
+ ngtcp2_ksl ents;
+ ngtcp2_log *log;
+ const ngtcp2_mem *mem;
+ /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */
+ uint16_t flags;
+ /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added
+ first time after the last outgoing ACK frame. */
+ ngtcp2_tstamp first_unacked_ts;
+ /* rx_npkt is the number of ACK eliciting packets received without
+ sending ACK. */
+ size_t rx_npkt;
+} ngtcp2_acktr;
+
+/*
+ * ngtcp2_acktr_init initializes |acktr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_acktr_free frees resources allocated for |acktr|. It frees
+ * any ngtcp2_acktr_entry added to |acktr|.
+ */
+void ngtcp2_acktr_free(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|.
+ * |active_ack| is nonzero if |pkt_num| is retransmittable packet.
+ *
+ * This function assumes that |acktr| does not contain |pkt_num|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * OUt of memory.
+ */
+int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_acktr_forget removes all entries which have the packet
+ * number that is equal to or less than ent->pkt_num. This function
+ * assumes that |acktr| includes |ent|.
+ */
+void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent);
+
+/*
+ * ngtcp2_acktr_get returns the pointer to pointer to the entry which
+ * has the largest packet number to be acked. If there is no entry,
+ * returned value satisfies ngtcp2_ksl_it_end(&it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_empty returns nonzero if it has no packet to
+ * acknowledge.
+ */
+int ngtcp2_acktr_empty(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest
+ * acknowledged packet number is |largest_ack|. |pkt_num| is the
+ * packet number of a packet in which ACK frame is included. This
+ * function returns a pointer to the object it adds.
+ */
+ngtcp2_acktr_ack_entry *
+ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack);
+
+/*
+ * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|.
+ * |pkt_num| is a packet number which includes |fr|. If we receive
+ * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack,
+ * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed.
+ */
+void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated.
+ */
+void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr);
+
+/*
+ * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should
+ * be generated actively.
+ */
+int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr,
+ ngtcp2_duration max_ack_delay,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_acktr_immediate_ack tells |acktr| that immediate
+ * acknowledgement is required.
+ */
+void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr);
+
+#endif /* NGTCP2_ACKTR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
new file mode 100644
index 0000000..f389abe
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
@@ -0,0 +1,117 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_addr.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_unreachable.h"
+
+ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen) {
+ dest->addrlen = addrlen;
+ dest->addr = (ngtcp2_sockaddr *)addr;
+ return dest;
+}
+
+void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) {
+ dest->addrlen = src->addrlen;
+ if (src->addrlen) {
+ memcpy(dest->addr, src->addr, (size_t)src->addrlen);
+ }
+}
+
+void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen) {
+ dest->addrlen = addrlen;
+ if (addrlen) {
+ memcpy(dest->addr, addr, (size_t)addrlen);
+ }
+}
+
+static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
+ assert(a->sa_family == b->sa_family);
+
+ switch (a->sa_family) {
+ case NGTCP2_AF_INET: {
+ const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+ return ai->sin_port == bi->sin_port &&
+ memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
+ }
+ case NGTCP2_AF_INET6: {
+ const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+ return ai->sin6_port == bi->sin6_port &&
+ memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
+ }
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) {
+ return a->addr->sa_family == b->addr->sa_family &&
+ sockaddr_eq(a->addr, b->addr);
+}
+
+uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
+ uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE;
+ const ngtcp2_sockaddr *a = aa->addr;
+ const ngtcp2_sockaddr *b = bb->addr;
+
+ if (a->sa_family != b->sa_family) {
+ return NGTCP2_ADDR_COMPARE_FLAG_FAMILY;
+ }
+
+ switch (a->sa_family) {
+ case NGTCP2_AF_INET: {
+ const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+ if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
+ }
+ if (ai->sin_port != bi->sin_port) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT;
+ }
+ return flags;
+ }
+ case NGTCP2_AF_INET6: {
+ const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+ *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+ if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
+ }
+ if (ai->sin6_port != bi->sin6_port) {
+ flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT;
+ }
+ return flags;
+ }
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h
new file mode 100644
index 0000000..f1d7f7b
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h
@@ -0,0 +1,69 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ADDR_H
+#define NGTCP2_ADDR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_addr_copy copies |src| to |dest|. This function assumes
+ * that dest->addr points to a buffer which have sufficient size to
+ * store the copy.
+ */
+void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src);
+
+/*
+ * ngtcp2_addr_eq returns nonzero if |a| equals |b|.
+ */
+int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b);
+
+/* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */
+#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u
+/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not
+ match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u
+/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u
+/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not
+ match. */
+#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u
+
+/*
+ * ngtcp2_addr_compare compares address and port between |a| and |b|,
+ * and returns zero or more of NGTCP2_ADDR_COMPARE_FLAG_*.
+ */
+uint32_t ngtcp2_addr_compare(const ngtcp2_addr *a, const ngtcp2_addr *b);
+
+/*
+ * ngtcp2_addr_empty returns nonzero if |addr| has zero length
+ * address.
+ */
+int ngtcp2_addr_empty(const ngtcp2_addr *addr);
+
+#endif /* NGTCP2_ADDR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
new file mode 100644
index 0000000..5cc39ee
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
@@ -0,0 +1,90 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_balloc.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+ const ngtcp2_mem *mem) {
+ assert((blklen & 0xfu) == 0);
+
+ balloc->mem = mem;
+ balloc->blklen = blklen;
+ balloc->head = NULL;
+ ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc) {
+ if (balloc == NULL) {
+ return;
+ }
+
+ ngtcp2_balloc_clear(balloc);
+}
+
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) {
+ ngtcp2_memblock_hd *p, *next;
+
+ for (p = balloc->head; p; p = next) {
+ next = p->next;
+ ngtcp2_mem_free(balloc->mem, p);
+ }
+
+ balloc->head = NULL;
+ ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
+ uint8_t *p;
+ ngtcp2_memblock_hd *hd;
+
+ assert(n <= balloc->blklen);
+
+ if (ngtcp2_buf_left(&balloc->buf) < n) {
+ p = ngtcp2_mem_malloc(balloc->mem,
+ sizeof(ngtcp2_memblock_hd) + 0x10u + balloc->blklen);
+ if (p == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ hd = (ngtcp2_memblock_hd *)(void *)p;
+ hd->next = balloc->head;
+ balloc->head = hd;
+ ngtcp2_buf_init(
+ &balloc->buf,
+ (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfu) &
+ ~(uintptr_t)0xfu),
+ balloc->blklen);
+ }
+
+ assert(((uintptr_t)balloc->buf.last & 0xfu) == 0);
+
+ *pbuf = balloc->buf.last;
+ balloc->buf.last += (n + 0xfu) & ~(uintptr_t)0xfu;
+
+ return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h
new file mode 100644
index 0000000..1fb1632
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h
@@ -0,0 +1,91 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BALLOC_H
+#define NGTCP2_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_buf.h"
+
+typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd;
+
+/*
+ * ngtcp2_memblock_hd is the header of memory block.
+ */
+struct ngtcp2_memblock_hd {
+ ngtcp2_memblock_hd *next;
+};
+
+/*
+ * ngtcp2_balloc is a custom memory allocator. It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct ngtcp2_balloc {
+ /* mem is the underlying memory allocator. */
+ const ngtcp2_mem *mem;
+ /* blklen is the size of memory block. */
+ size_t blklen;
+ /* head points to the list of memory block allocated so far. */
+ ngtcp2_memblock_hd *head;
+ /* buf wraps the current memory block for allocation requests. */
+ ngtcp2_buf buf;
+} ngtcp2_balloc;
+
+/*
+ * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_balloc_free releases all allocated memory blocks.
+ */
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc);
+
+/*
+ * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * ngtcp2_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc);
+
+#endif /* NGTCP2_BALLOC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
new file mode 100644
index 0000000..ed26d3e
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
@@ -0,0 +1,692 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_bbr.h"
+
+#include <assert.h>
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+
+static const double pacing_gain_cycle[] = {1.25, 0.75, 1, 1, 1, 1, 1, 1};
+
+#define NGTCP2_BBR_GAIN_CYCLELEN ngtcp2_arraylen(pacing_gain_cycle)
+
+#define NGTCP2_BBR_HIGH_GAIN 2.89
+#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
+#define NGTCP2_RTPROP_FILTERLEN (10 * NGTCP2_SECONDS)
+#define NGTCP2_BBR_BTL_BW_FILTERLEN 10
+
+static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_init_round_counting(ngtcp2_bbr_cc *cc);
+static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack);
+static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain);
+static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat);
+static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat);
+static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts);
+static void bbr_enter_startup(ngtcp2_bbr_cc *cc);
+static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc);
+static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc);
+static void bbr_enter_drain(ngtcp2_bbr_cc *cc);
+static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat);
+static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc);
+static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts);
+
+void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log) {
+ cc->ccb.log = log;
+ cc->rst = rst;
+ cc->rand = rand;
+ cc->rand_ctx = *rand_ctx;
+ cc->initial_cwnd = cstat->cwnd;
+ bbr_init(cc, cstat, initial_ts);
+}
+
+void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem) {
+ ngtcp2_bbr_cc *bbr_cc;
+
+ bbr_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr_cc));
+ if (bbr_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_bbr_cc_init(bbr_cc, cstat, rst, initial_ts, rand, rand_ctx, log);
+
+ cc->ccb = &bbr_cc->ccb;
+ cc->on_pkt_acked = ngtcp2_cc_bbr_cc_on_pkt_acked;
+ cc->congestion_event = ngtcp2_cc_bbr_cc_congestion_event;
+ cc->on_spurious_congestion = ngtcp2_cc_bbr_cc_on_spurious_congestion;
+ cc->on_persistent_congestion = ngtcp2_cc_bbr_cc_on_persistent_congestion;
+ cc->on_ack_recv = ngtcp2_cc_bbr_cc_on_ack_recv;
+ cc->on_pkt_sent = ngtcp2_cc_bbr_cc_on_pkt_sent;
+ cc->new_rtt_sample = ngtcp2_cc_bbr_cc_new_rtt_sample;
+ cc->reset = ngtcp2_cc_bbr_cc_reset;
+ cc->event = ngtcp2_cc_bbr_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr_cc, ccb);
+
+ ngtcp2_bbr_cc_free(bbr_cc);
+ ngtcp2_mem_free(mem, bbr_cc);
+}
+
+void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)pkt;
+ (void)ts;
+}
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time) {
+ return cstat->congestion_recovery_start_ts != UINT64_MAX &&
+ sent_time <= cstat->congestion_recovery_start_ts;
+}
+
+void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+
+ if (cc->in_loss_recovery || cc->congestion_recovery_start_ts != UINT64_MAX ||
+ in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ cc->congestion_recovery_start_ts = ts;
+}
+
+void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)ts;
+
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ if (cc->in_loss_recovery) {
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+ bbr_restore_cwnd(cc, cstat);
+ }
+}
+
+void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)ts;
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+
+ bbr_save_cwnd(cc, cstat);
+ cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
+}
+
+void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+
+ bbr_update_on_ack(bbr_cc, cstat, ack, ts);
+}
+
+void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ (void)pkt;
+
+ bbr_on_transmit(bbr_cc, cstat);
+}
+
+void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)ts;
+}
+
+void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr_cc *bbr_cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr_cc, ccb);
+ bbr_init(bbr_cc, cstat, ts);
+}
+
+void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)event;
+ (void)ts;
+}
+
+static void bbr_update_on_ack(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ bbr_update_model_and_state(cc, cstat, ack, ts);
+ bbr_update_control_parameters(cc, cstat, ack);
+}
+
+static void bbr_update_model_and_state(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ bbr_update_btl_bw(cc, cstat, ack);
+ bbr_check_cycle_phase(cc, cstat, ack, ts);
+ bbr_check_full_pipe(cc);
+ bbr_check_drain(cc, cstat, ts);
+ bbr_update_rtprop(cc, cstat, ts);
+ bbr_check_probe_rtt(cc, cstat, ts);
+}
+
+static void bbr_update_control_parameters(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_set_pacing_rate(cc, cstat);
+ bbr_set_send_quantum(cc, cstat);
+ bbr_set_cwnd(cc, cstat, ack);
+}
+
+static void bbr_on_transmit(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ bbr_handle_restart_from_idle(cc, cstat);
+}
+
+static void bbr_init_round_counting(ngtcp2_bbr_cc *cc) {
+ cc->next_round_delivered = 0;
+ cc->round_start = 0;
+ cc->round_count = 0;
+}
+
+static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) {
+ if (ack->pkt_delivered >= cc->next_round_delivered) {
+ cc->next_round_delivered = cc->rst->delivered;
+ ++cc->round_count;
+ cc->round_start = 1;
+
+ return;
+ }
+
+ cc->round_start = 0;
+}
+
+static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (cc->in_loss_recovery) {
+ if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) {
+ cc->packet_conservation = 0;
+ }
+
+ if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
+ cc->in_loss_recovery = 0;
+ cc->packet_conservation = 0;
+ bbr_restore_cwnd(cc, cstat);
+ }
+
+ return;
+ }
+
+ if (cc->congestion_recovery_start_ts != UINT64_MAX) {
+ cc->in_loss_recovery = 1;
+ bbr_save_cwnd(cc, cstat);
+ cstat->cwnd =
+ cstat->bytes_in_flight +
+ ngtcp2_max(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
+
+ cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts;
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->packet_conservation = 1;
+ cc->congestion_recovery_next_round_delivered = cc->rst->delivered;
+ }
+}
+
+static void bbr_update_btl_bw(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_round(cc, ack);
+ bbr_handle_recovery(cc, cstat, ack);
+
+ if (cstat->delivery_rate_sec < cc->btl_bw && cc->rst->rs.is_app_limited) {
+ return;
+ }
+
+ ngtcp2_window_filter_update(&cc->btl_bw_filter, cstat->delivery_rate_sec,
+ cc->round_count);
+
+ cc->btl_bw = ngtcp2_window_filter_get_best(&cc->btl_bw_filter);
+}
+
+static void bbr_update_rtprop(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ cc->rtprop_expired = ts > cc->rtprop_stamp + NGTCP2_RTPROP_FILTERLEN;
+
+ /* Need valid RTT sample */
+ if (cstat->latest_rtt &&
+ (cstat->latest_rtt <= cc->rt_prop || cc->rtprop_expired)) {
+ cc->rt_prop = cstat->latest_rtt;
+ cc->rtprop_stamp = ts;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr update RTprop=%" PRIu64, cc->rt_prop);
+ }
+}
+
+static void bbr_init_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ double nominal_bandwidth =
+ (double)cc->initial_cwnd / (double)NGTCP2_MILLISECONDS;
+
+ cstat->pacing_rate = cc->pacing_gain * nominal_bandwidth;
+}
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain) {
+ double rate = pacing_gain * (double)cc->btl_bw / NGTCP2_SECONDS;
+
+ if (cc->filled_pipe || rate > cstat->pacing_rate) {
+ cstat->pacing_rate = rate;
+ }
+}
+
+static void bbr_set_pacing_rate(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ bbr_set_pacing_rate_with_gain(cc, cstat, cc->pacing_gain);
+}
+
+static void bbr_set_send_quantum(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ uint64_t send_quantum;
+ (void)cc;
+
+ if (cstat->pacing_rate < 1.2 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
+ cstat->send_quantum = cstat->max_tx_udp_payload_size;
+ } else if (cstat->pacing_rate < 24.0 * 1024 * 1024 / 8 / NGTCP2_SECONDS) {
+ cstat->send_quantum = cstat->max_tx_udp_payload_size * 2;
+ } else {
+ send_quantum =
+ (uint64_t)(cstat->pacing_rate * (double)(cstat->min_rtt == UINT64_MAX
+ ? NGTCP2_MILLISECONDS
+ : cstat->min_rtt));
+ cstat->send_quantum = (size_t)ngtcp2_min(send_quantum, 64 * 1024);
+ }
+
+ cstat->send_quantum =
+ ngtcp2_max(cstat->send_quantum, cstat->max_tx_udp_payload_size * 10);
+}
+
+static uint64_t bbr_inflight(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ double gain) {
+ uint64_t quanta = 3 * cstat->send_quantum;
+ double estimated_bdp;
+
+ if (cc->rt_prop == UINT64_MAX) {
+ /* no valid RTT samples yet */
+ return cc->initial_cwnd;
+ }
+
+ estimated_bdp = (double)cc->btl_bw * (double)cc->rt_prop / NGTCP2_SECONDS;
+
+ return (uint64_t)(gain * estimated_bdp) + quanta;
+}
+
+static void bbr_update_target_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ cc->target_cwnd = bbr_inflight(cc, cstat, cc->cwnd_gain);
+}
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (ack->bytes_lost > 0) {
+ if (cstat->cwnd > ack->bytes_lost) {
+ cstat->cwnd -= ack->bytes_lost;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_tx_udp_payload_size);
+ } else {
+ cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
+ }
+ }
+
+ if (cc->packet_conservation) {
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
+ }
+}
+
+static void bbr_save_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ if (!cc->in_loss_recovery && cc->state != NGTCP2_BBR_STATE_PROBE_RTT) {
+ cc->prior_cwnd = cstat->cwnd;
+ return;
+ }
+
+ cc->prior_cwnd = ngtcp2_max(cc->prior_cwnd, cstat->cwnd);
+}
+
+static void bbr_restore_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat) {
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, cc->prior_cwnd);
+}
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
+ return max_udp_payload_size * 4;
+}
+
+static void bbr_modulate_cwnd_for_probe_rtt(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat) {
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
+ cstat->cwnd =
+ ngtcp2_min(cstat->cwnd, min_pipe_cwnd(cstat->max_tx_udp_payload_size));
+ }
+}
+
+static void bbr_set_cwnd(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_target_cwnd(cc, cstat);
+ bbr_modulate_cwnd_for_recovery(cc, cstat, ack);
+
+ if (!cc->packet_conservation) {
+ if (cc->filled_pipe) {
+ cstat->cwnd =
+ ngtcp2_min(cstat->cwnd + ack->bytes_delivered, cc->target_cwnd);
+ } else if (cstat->cwnd < cc->target_cwnd ||
+ cc->rst->delivered < cc->initial_cwnd) {
+ cstat->cwnd += ack->bytes_delivered;
+ }
+
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_tx_udp_payload_size));
+ }
+
+ bbr_modulate_cwnd_for_probe_rtt(cc, cstat);
+}
+
+static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts) {
+ cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
+ cc->prior_cwnd = 0;
+ cc->target_cwnd = 0;
+ cc->btl_bw = 0;
+ cc->rt_prop = UINT64_MAX;
+ cc->rtprop_stamp = initial_ts;
+ cc->cycle_stamp = UINT64_MAX;
+ cc->probe_rtt_done_stamp = UINT64_MAX;
+ cc->cycle_index = 0;
+ cc->rtprop_expired = 0;
+ cc->idle_restart = 0;
+ cc->packet_conservation = 0;
+ cc->probe_rtt_round_done = 0;
+
+ cc->congestion_recovery_start_ts = UINT64_MAX;
+ cc->congestion_recovery_next_round_delivered = 0;
+ cc->in_loss_recovery = 0;
+
+ cstat->send_quantum = cstat->max_tx_udp_payload_size * 10;
+
+ ngtcp2_window_filter_init(&cc->btl_bw_filter, NGTCP2_BBR_BTL_BW_FILTERLEN);
+
+ bbr_init_round_counting(cc);
+ bbr_init_full_pipe(cc);
+ bbr_init_pacing_rate(cc, cstat);
+ bbr_enter_startup(cc);
+}
+
+static void bbr_enter_startup(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_STARTUP;
+ cc->pacing_gain = NGTCP2_BBR_HIGH_GAIN;
+ cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
+}
+
+static void bbr_init_full_pipe(ngtcp2_bbr_cc *cc) {
+ cc->filled_pipe = 0;
+ cc->full_bw = 0;
+ cc->full_bw_count = 0;
+}
+
+static void bbr_check_full_pipe(ngtcp2_bbr_cc *cc) {
+ if (cc->filled_pipe || !cc->round_start || cc->rst->rs.is_app_limited) {
+ /* no need to check for a full pipe now. */
+ return;
+ }
+
+ /* cc->btl_bw still growing? */
+ if (cc->btl_bw * 100 >= cc->full_bw * 125) {
+ /* record new baseline level */
+ cc->full_bw = cc->btl_bw;
+ cc->full_bw_count = 0;
+ return;
+ }
+ /* another round w/o much growth */
+ ++cc->full_bw_count;
+ if (cc->full_bw_count >= 3) {
+ cc->filled_pipe = 1;
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr filled pipe, btl_bw=%" PRIu64, cc->btl_bw);
+ }
+}
+
+static void bbr_enter_drain(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_DRAIN;
+ /* pace slowly */
+ cc->pacing_gain = 1.0 / NGTCP2_BBR_HIGH_GAIN;
+ /* maintain cwnd */
+ cc->cwnd_gain = NGTCP2_BBR_HIGH_GAIN;
+}
+
+static void bbr_check_drain(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cc->state == NGTCP2_BBR_STATE_STARTUP && cc->filled_pipe) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit Startup and enter Drain");
+
+ bbr_enter_drain(cc);
+ }
+
+ if (cc->state == NGTCP2_BBR_STATE_DRAIN &&
+ cstat->bytes_in_flight <= bbr_inflight(cc, cstat, 1.0)) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit Drain and enter ProbeBW");
+
+ /* we estimate queue is drained */
+ bbr_enter_probe_bw(cc, ts);
+ }
+}
+
+static void bbr_enter_probe_bw(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ uint8_t rand;
+
+ cc->state = NGTCP2_BBR_STATE_PROBE_BW;
+ cc->pacing_gain = 1;
+ cc->cwnd_gain = 2;
+
+ assert(cc->rand);
+
+ cc->rand(&rand, 1, &cc->rand_ctx);
+
+ cc->cycle_index = NGTCP2_BBR_GAIN_CYCLELEN - 1 - (size_t)(rand * 7 / 256);
+ bbr_advance_cycle_phase(cc, ts);
+}
+
+static void bbr_check_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_BW &&
+ bbr_is_next_cycle_phase(cc, cstat, ack, ts)) {
+ bbr_advance_cycle_phase(cc, ts);
+ }
+}
+
+static void bbr_advance_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ cc->cycle_stamp = ts;
+ cc->cycle_index = (cc->cycle_index + 1) & (NGTCP2_BBR_GAIN_CYCLELEN - 1);
+ cc->pacing_gain = pacing_gain_cycle[cc->cycle_index];
+}
+
+static int bbr_is_next_cycle_phase(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ int is_full_length = (ts - cc->cycle_stamp) > cc->rt_prop;
+
+ if (cc->pacing_gain > 1) {
+ return is_full_length && (ack->bytes_lost > 0 ||
+ ack->prior_bytes_in_flight >=
+ bbr_inflight(cc, cstat, cc->pacing_gain));
+ }
+
+ if (cc->pacing_gain < 1) {
+ return is_full_length ||
+ ack->prior_bytes_in_flight <= bbr_inflight(cc, cstat, 1);
+ }
+
+ return is_full_length;
+}
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr_cc *cc,
+ ngtcp2_conn_stat *cstat) {
+ if (cstat->bytes_in_flight == 0 && cc->rst->app_limited) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr restart from idle");
+
+ cc->idle_restart = 1;
+
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_BW) {
+ bbr_set_pacing_rate_with_gain(cc, cstat, 1);
+ }
+ }
+}
+
+static void bbr_check_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cc->state != NGTCP2_BBR_STATE_PROBE_RTT && cc->rtprop_expired &&
+ !cc->idle_restart) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr enter ProbeRTT");
+
+ bbr_enter_probe_rtt(cc);
+ bbr_save_cwnd(cc, cstat);
+ cc->probe_rtt_done_stamp = UINT64_MAX;
+ }
+
+ if (cc->state == NGTCP2_BBR_STATE_PROBE_RTT) {
+ bbr_handle_probe_rtt(cc, cstat, ts);
+ }
+
+ cc->idle_restart = 0;
+}
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr_cc *cc) {
+ cc->state = NGTCP2_BBR_STATE_PROBE_RTT;
+ cc->pacing_gain = 1;
+ cc->cwnd_gain = 1;
+}
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ uint64_t app_limited = cc->rst->delivered + cstat->bytes_in_flight;
+
+ /* Ignore low rate samples during NGTCP2_BBR_STATE_PROBE_RTT. */
+ cc->rst->app_limited = app_limited ? app_limited : 1;
+
+ if (cc->probe_rtt_done_stamp == UINT64_MAX &&
+ cstat->bytes_in_flight <= min_pipe_cwnd(cstat->max_tx_udp_payload_size)) {
+ cc->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
+ cc->probe_rtt_round_done = 0;
+ cc->next_round_delivered = cc->rst->delivered;
+
+ return;
+ }
+
+ if (cc->probe_rtt_done_stamp != UINT64_MAX) {
+ if (cc->round_start) {
+ cc->probe_rtt_round_done = 1;
+ }
+
+ if (cc->probe_rtt_round_done && ts > cc->probe_rtt_done_stamp) {
+ cc->rtprop_stamp = ts;
+ bbr_restore_cwnd(cc, cstat);
+ bbr_exit_probe_rtt(cc, ts);
+ }
+ }
+}
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr_cc *cc, ngtcp2_tstamp ts) {
+ if (cc->filled_pipe) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit ProbeRTT and enter ProbeBW");
+
+ bbr_enter_probe_bw(cc, ts);
+
+ return;
+ }
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr exit ProbeRTT and enter Startup");
+
+ bbr_enter_startup(cc);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
new file mode 100644
index 0000000..7311f05
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
@@ -0,0 +1,156 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BBR_H
+#define NGTCP2_BBR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cc.h"
+#include "ngtcp2_window_filter.h"
+
+typedef struct ngtcp2_rst ngtcp2_rst;
+
+typedef enum ngtcp2_bbr_state {
+ NGTCP2_BBR_STATE_STARTUP,
+ NGTCP2_BBR_STATE_DRAIN,
+ NGTCP2_BBR_STATE_PROBE_BW,
+ NGTCP2_BBR_STATE_PROBE_RTT,
+} ngtcp2_bbr_state;
+
+/*
+ * ngtcp2_bbr_cc is BBR congestion controller, described in
+ * https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00
+ */
+typedef struct ngtcp2_bbr_cc {
+ ngtcp2_cc_base ccb;
+
+ /* The max filter used to estimate BBR.BtlBw. */
+ ngtcp2_window_filter btl_bw_filter;
+ uint64_t initial_cwnd;
+ ngtcp2_rst *rst;
+ ngtcp2_rand rand;
+ ngtcp2_rand_ctx rand_ctx;
+
+ /* BBR variables */
+
+ /* The dynamic gain factor used to scale BBR.BtlBw to
+ produce BBR.pacing_rate. */
+ double pacing_gain;
+ /* The dynamic gain factor used to scale the estimated BDP to produce a
+ congestion window (cwnd). */
+ double cwnd_gain;
+ uint64_t full_bw;
+ /* packet.delivered value denoting the end of a packet-timed round trip. */
+ uint64_t next_round_delivered;
+ /* Count of packet-timed round trips. */
+ uint64_t round_count;
+ uint64_t prior_cwnd;
+ /* target_cwnd is the upper bound on the volume of data BBR
+ allows in flight. */
+ uint64_t target_cwnd;
+ /* BBR's estimated bottleneck bandwidth available to the
+ transport flow, estimated from the maximum delivery rate sample in a
+ sliding window. */
+ uint64_t btl_bw;
+ /* BBR's estimated two-way round-trip propagation delay of
+ the path, estimated from the windowed minimum recent round-trip delay
+ sample. */
+ ngtcp2_duration rt_prop;
+ /* The wall clock time at which the current BBR.RTProp
+ sample was obtained. */
+ ngtcp2_tstamp rtprop_stamp;
+ ngtcp2_tstamp cycle_stamp;
+ ngtcp2_tstamp probe_rtt_done_stamp;
+ /* congestion_recovery_start_ts is the time when congestion recovery
+ period started.*/
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ uint64_t congestion_recovery_next_round_delivered;
+ size_t full_bw_count;
+ size_t cycle_index;
+ ngtcp2_bbr_state state;
+ /* A boolean that records whether BBR estimates that it has ever fully
+ utilized its available bandwidth ("filled the pipe"). */
+ int filled_pipe;
+ /* A boolean that BBR sets to true once per packet-timed round trip,
+ on ACKs that advance BBR.round_count. */
+ int round_start;
+ int rtprop_expired;
+ int idle_restart;
+ int packet_conservation;
+ int probe_rtt_round_done;
+ /* in_loss_recovery becomes nonzero when BBR enters loss recovery
+ period. */
+ int in_loss_recovery;
+} ngtcp2_bbr_cc;
+
+int ngtcp2_cc_bbr_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_bbr_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_bbr_cc_init(ngtcp2_bbr_cc *bbr_cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log);
+
+void ngtcp2_bbr_cc_free(ngtcp2_bbr_cc *cc);
+
+void ngtcp2_cc_bbr_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+void ngtcp2_cc_bbr_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_bbr_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_BBR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
new file mode 100644
index 0000000..508ab55
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
@@ -0,0 +1,1489 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_bbr2.h"
+
+#include <assert.h>
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+
+#define NGTCP2_BBR_MAX_BW_FILTERLEN 2
+
+#define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10
+
+#define NGTCP2_BBR_STARTUP_PACING_GAIN ((double)2.77)
+
+#define NGTCP2_BBR_STARTUP_CWND_GAIN 2
+
+#define NGTCP2_BBR_PROBE_RTT_CWND_GAIN ((double)0.5)
+
+#define NGTCP2_BBR_BETA_NUMER 7
+#define NGTCP2_BBR_BETA_DENOM 10
+
+#define NGTCP2_BBR_LOSS_THRESH_NUMER 2
+#define NGTCP2_BBR_LOSS_THRESH_DENOM 100
+
+#define NGTCP2_BBR_HEADROOM_NUMER 15
+#define NGTCP2_BBR_HEADROOM_DENOM 100
+
+#define NGTCP2_BBR_PROBE_RTT_INTERVAL (5 * NGTCP2_SECONDS)
+#define NGTCP2_BBR_MIN_RTT_FILTERLEN (10 * NGTCP2_SECONDS)
+
+#define NGTCP2_BBR_PROBE_RTT_DURATION (200 * NGTCP2_MILLISECONDS)
+
+#define NGTCP2_BBR_PACING_MARGIN_PERCENT 1
+
+static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts);
+
+static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain);
+
+static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+static void bbr_update_model_and_state(ngtcp2_bbr2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_update_control_parameters(ngtcp2_bbr2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack);
+
+static void bbr_start_round(ngtcp2_bbr2_cc *bbr);
+
+static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_duration interval, ngtcp2_tstamp ts);
+
+static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr);
+
+static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static int is_inflight_too_high(const ngtcp2_rs *rs);
+
+static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs, ngtcp2_tstamp ts);
+
+static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_rs *rs,
+ const ngtcp2_cc_pkt *pkt);
+
+static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts);
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw, double gain);
+
+static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ uint64_t inflight);
+
+static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ uint64_t bw, double gain);
+
+static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size);
+
+static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr);
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat);
+
+static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat);
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time);
+
+static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
+
+static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp initial_ts) {
+ ngtcp2_window_filter_init(&bbr->max_bw_filter, NGTCP2_BBR_MAX_BW_FILTERLEN);
+ ngtcp2_window_filter_init(&bbr->extra_acked_filter,
+ NGTCP2_BBR_EXTRA_ACKED_FILTERLEN);
+
+ bbr->min_rtt = UINT64_MAX;
+ bbr->min_rtt_stamp = initial_ts;
+ /* remark: Use UINT64_MAX instead of 0 for consistency. */
+ bbr->probe_rtt_done_stamp = UINT64_MAX;
+ bbr->probe_rtt_round_done = 0;
+ bbr->prior_cwnd = 0;
+ bbr->idle_restart = 0;
+ bbr->extra_acked_interval_start = initial_ts;
+ bbr->extra_acked_delivered = 0;
+
+ bbr_reset_congestion_signals(bbr);
+ bbr_reset_lower_bounds(bbr);
+ bbr_init_round_counting(bbr);
+ bbr_init_full_pipe(bbr);
+ bbr_init_pacing_rate(bbr, cstat);
+ bbr_enter_startup(bbr);
+
+ cstat->send_quantum = cstat->max_tx_udp_payload_size * 10;
+
+ /* Missing in documentation */
+ bbr->loss_round_start = 0;
+ bbr->loss_round_delivered = UINT64_MAX;
+
+ bbr->rounds_since_bw_probe = 0;
+
+ bbr->max_bw = 0;
+ bbr->bw = 0;
+
+ bbr->cycle_count = 0;
+
+ bbr->extra_acked = 0;
+
+ bbr->bytes_lost_in_round = 0;
+ bbr->loss_events_in_round = 0;
+
+ bbr->offload_budget = 0;
+
+ bbr->probe_up_cnt = UINT64_MAX;
+ bbr->cycle_stamp = UINT64_MAX;
+ bbr->ack_phase = 0;
+ bbr->bw_probe_wait = 0;
+ bbr->bw_probe_samples = 0;
+ bbr->bw_probe_up_rounds = 0;
+ bbr->bw_probe_up_acks = 0;
+
+ bbr->inflight_hi = UINT64_MAX;
+ bbr->bw_hi = UINT64_MAX;
+
+ bbr->probe_rtt_expired = 0;
+ bbr->probe_rtt_min_delay = UINT64_MAX;
+ bbr->probe_rtt_min_stamp = initial_ts;
+
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+
+ bbr->max_inflight = 0;
+
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->congestion_recovery_next_round_delivered = 0;
+
+ bbr->prior_inflight_lo = 0;
+ bbr->prior_inflight_hi = 0;
+ bbr->prior_bw_lo = 0;
+}
+
+static void bbr_reset_congestion_signals(ngtcp2_bbr2_cc *bbr) {
+ bbr->loss_in_round = 0;
+ bbr->bw_latest = 0;
+ bbr->inflight_latest = 0;
+}
+
+static void bbr_reset_lower_bounds(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw_lo = UINT64_MAX;
+ bbr->inflight_lo = UINT64_MAX;
+}
+
+static void bbr_init_round_counting(ngtcp2_bbr2_cc *bbr) {
+ bbr->next_round_delivered = 0;
+ bbr->round_start = 0;
+ bbr->round_count = 0;
+}
+
+static void bbr_init_full_pipe(ngtcp2_bbr2_cc *bbr) {
+ bbr->filled_pipe = 0;
+ bbr->full_bw = 0;
+ bbr->full_bw_count = 0;
+}
+
+static void bbr_check_startup_full_bandwidth(ngtcp2_bbr2_cc *bbr) {
+ if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) {
+ return;
+ }
+
+ if (bbr->max_bw * 100 >= bbr->full_bw * 125) {
+ bbr->full_bw = bbr->max_bw;
+ bbr->full_bw_count = 0;
+ }
+
+ ++bbr->full_bw_count;
+
+ if (bbr->full_bw_count >= 3) {
+ bbr->filled_pipe = 1;
+
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 filled pipe, full_bw=%" PRIu64, bbr->full_bw);
+ }
+}
+
+static void bbr_check_startup_high_loss(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack) {
+ if (bbr->filled_pipe || !bbr->round_start || bbr->rst->rs.is_app_limited) {
+ return;
+ }
+
+ if (bbr->loss_events_in_round <= 3) {
+ return;
+ }
+
+ /* loss_thresh = 2% */
+ if (bbr->bytes_lost_in_round * 100 <= ack->prior_bytes_in_flight * 2) {
+ return;
+ }
+
+ bbr->filled_pipe = 1;
+}
+
+static void bbr_init_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ double nominal_bandwidth = (double)bbr->initial_cwnd;
+
+ cstat->pacing_rate = NGTCP2_BBR_STARTUP_PACING_GAIN * nominal_bandwidth /
+ (double)NGTCP2_MILLISECONDS;
+}
+
+static void bbr_set_pacing_rate_with_gain(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ double pacing_gain) {
+ double rate = pacing_gain * (double)bbr->bw *
+ (100 - NGTCP2_BBR_PACING_MARGIN_PERCENT) / 100 / NGTCP2_SECONDS;
+
+ if (bbr->filled_pipe || rate > cstat->pacing_rate) {
+ cstat->pacing_rate = rate;
+ }
+}
+
+static void bbr_set_pacing_rate(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ bbr_set_pacing_rate_with_gain(bbr, cstat, bbr->pacing_gain);
+}
+
+static void bbr_enter_startup(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Startup");
+
+ bbr->state = NGTCP2_BBR2_STATE_STARTUP;
+ bbr->pacing_gain = NGTCP2_BBR_STARTUP_PACING_GAIN;
+ bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN;
+}
+
+static void bbr_check_startup_done(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_cc_ack *ack) {
+ bbr_check_startup_full_bandwidth(bbr);
+ bbr_check_startup_high_loss(bbr, ack);
+
+ if (bbr->state == NGTCP2_BBR2_STATE_STARTUP && bbr->filled_pipe) {
+ bbr_enter_drain(bbr);
+ }
+}
+
+static void bbr_on_transmit(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ bbr_handle_restart_from_idle(bbr, cstat, ts);
+}
+
+static void bbr_update_on_ack(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ bbr_update_model_and_state(bbr, cstat, ack, ts);
+ bbr_update_control_parameters(bbr, cstat, ack);
+}
+
+static void bbr_update_model_and_state(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ bbr_update_latest_delivery_signals(bbr, cstat);
+ bbr_update_congestion_signals(bbr, cstat, ack);
+ bbr_update_ack_aggregation(bbr, cstat, ack, ts);
+ bbr_check_startup_done(bbr, ack);
+ bbr_check_drain(bbr, cstat, ts);
+ bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts);
+ bbr_update_min_rtt(bbr, ack, ts);
+ bbr_check_probe_rtt(bbr, cstat, ts);
+ bbr_advance_latest_delivery_signals(bbr, cstat);
+ bbr_bound_bw_for_model(bbr);
+}
+
+static void bbr_update_control_parameters(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_set_pacing_rate(bbr, cstat);
+ bbr_set_send_quantum(bbr, cstat);
+ bbr_set_cwnd(bbr, cstat, ack);
+}
+
+static void bbr_update_on_loss(ngtcp2_bbr2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ bbr_handle_lost_packet(cc, cstat, pkt, ts);
+}
+
+static void bbr_update_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ bbr->loss_round_start = 0;
+ bbr->bw_latest = ngtcp2_max(bbr->bw_latest, cstat->delivery_rate_sec);
+ bbr->inflight_latest =
+ ngtcp2_max(bbr->inflight_latest, bbr->rst->rs.delivered);
+
+ if (bbr->rst->rs.prior_delivered >= bbr->loss_round_delivered) {
+ bbr->loss_round_delivered = bbr->rst->delivered;
+ bbr->loss_round_start = 1;
+ }
+}
+
+static void bbr_advance_latest_delivery_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (bbr->loss_round_start) {
+ bbr->bw_latest = cstat->delivery_rate_sec;
+ bbr->inflight_latest = bbr->rst->rs.delivered;
+ }
+}
+
+static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_max_bw(bbr, cstat, ack);
+
+ if (ack->bytes_lost) {
+ bbr->bytes_lost_in_round += ack->bytes_lost;
+ ++bbr->loss_events_in_round;
+
+ if (!bbr->loss_in_round) {
+ bbr->loss_in_round = 1;
+ bbr->loss_round_delivered = bbr->rst->delivered;
+ }
+ }
+
+ if (!bbr->loss_round_start) {
+ return;
+ }
+
+ bbr_adapt_lower_bounds_from_congestion(bbr, cstat);
+
+ bbr->loss_in_round = 0;
+}
+
+static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) {
+ return;
+ }
+
+ if (bbr->loss_in_round) {
+ bbr_init_lower_bounds(bbr, cstat);
+ bbr_loss_lower_bounds(bbr);
+ }
+}
+
+static void bbr_init_lower_bounds(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ if (bbr->bw_lo == UINT64_MAX) {
+ bbr->bw_lo = bbr->max_bw;
+ }
+
+ if (bbr->inflight_lo == UINT64_MAX) {
+ bbr->inflight_lo = cstat->cwnd;
+ }
+}
+
+static void bbr_loss_lower_bounds(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw_lo = ngtcp2_max(bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER /
+ NGTCP2_BBR_BETA_DENOM);
+ bbr->inflight_lo = ngtcp2_max(bbr->inflight_latest,
+ bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER /
+ NGTCP2_BBR_BETA_DENOM);
+}
+
+static void bbr_bound_bw_for_model(ngtcp2_bbr2_cc *bbr) {
+ bbr->bw = ngtcp2_min(bbr->max_bw, bbr->bw_lo);
+ bbr->bw = ngtcp2_min(bbr->bw, bbr->bw_hi);
+}
+
+static void bbr_update_max_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ bbr_update_round(bbr, ack);
+ bbr_handle_recovery(bbr, cstat, ack);
+
+ if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) {
+ ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec,
+ bbr->cycle_count);
+
+ bbr->max_bw = ngtcp2_window_filter_get_best(&bbr->max_bw_filter);
+ }
+}
+
+static void bbr_update_round(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack) {
+ if (ack->pkt_delivered >= bbr->next_round_delivered) {
+ bbr_start_round(bbr);
+
+ ++bbr->round_count;
+ ++bbr->rounds_since_bw_probe;
+ bbr->round_start = 1;
+
+ bbr->bytes_lost_in_round = 0;
+ bbr->loss_events_in_round = 0;
+
+ bbr->rst->is_cwnd_limited = 0;
+
+ return;
+ }
+
+ bbr->round_start = 0;
+}
+
+static void bbr_start_round(ngtcp2_bbr2_cc *bbr) {
+ bbr->next_round_delivered = bbr->rst->delivered;
+}
+
+static int bbr_is_in_probe_bw_state(ngtcp2_bbr2_cc *bbr) {
+ switch (bbr->state) {
+ case NGTCP2_BBR2_STATE_PROBE_BW_DOWN:
+ case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE:
+ case NGTCP2_BBR2_STATE_PROBE_BW_REFILL:
+ case NGTCP2_BBR2_STATE_PROBE_BW_UP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void bbr_update_ack_aggregation(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ ngtcp2_duration interval = ts - bbr->extra_acked_interval_start;
+ uint64_t expected_delivered = bbr->bw * interval / NGTCP2_SECONDS;
+ uint64_t extra;
+
+ if (bbr->extra_acked_delivered <= expected_delivered) {
+ bbr->extra_acked_delivered = 0;
+ bbr->extra_acked_interval_start = ts;
+ expected_delivered = 0;
+ }
+
+ bbr->extra_acked_delivered += ack->bytes_delivered;
+ extra = bbr->extra_acked_delivered - expected_delivered;
+ extra = ngtcp2_min(extra, cstat->cwnd);
+
+ ngtcp2_window_filter_update(&bbr->extra_acked_filter, extra,
+ bbr->round_count);
+
+ bbr->extra_acked = ngtcp2_window_filter_get_best(&bbr->extra_acked_filter);
+}
+
+static void bbr_enter_drain(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter Drain");
+
+ bbr->state = NGTCP2_BBR2_STATE_DRAIN;
+ bbr->pacing_gain = 1. / NGTCP2_BBR_STARTUP_CWND_GAIN;
+ bbr->cwnd_gain = NGTCP2_BBR_STARTUP_CWND_GAIN;
+}
+
+static void bbr_check_drain(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->state == NGTCP2_BBR2_STATE_DRAIN &&
+ cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->bw, 1.0)) {
+ bbr_enter_probe_bw(bbr, ts);
+ }
+}
+
+static void bbr_enter_probe_bw(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ bbr_start_probe_bw_down(bbr, ts);
+}
+
+static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_DOWN");
+
+ bbr_reset_congestion_signals(bbr);
+
+ bbr->probe_up_cnt = UINT64_MAX;
+
+ bbr_pick_probe_wait(bbr);
+
+ bbr->cycle_stamp = ts;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING;
+
+ bbr_start_round(bbr);
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN;
+ bbr->pacing_gain = 0.9;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_cruise(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_CRUISE");
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_CRUISE;
+ bbr->pacing_gain = 1.0;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_refill(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 start ProbeBW_REFILL");
+
+ bbr_reset_lower_bounds(bbr);
+
+ bbr->bw_probe_up_rounds = 0;
+ bbr->bw_probe_up_acks = 0;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING;
+
+ bbr_start_round(bbr);
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_REFILL;
+ bbr->pacing_gain = 1.0;
+ bbr->cwnd_gain = 2;
+}
+
+static void bbr_start_probe_bw_up(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 start ProbeBW_UP");
+
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING;
+
+ bbr_start_round(bbr);
+
+ bbr->cycle_stamp = ts;
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_UP;
+ bbr->pacing_gain = 1.25;
+ bbr->cwnd_gain = 2;
+
+ bbr_raise_inflight_hi_slope(bbr, cstat);
+}
+
+static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ if (!bbr->filled_pipe) {
+ return;
+ }
+
+ bbr_adapt_upper_bounds(bbr, cstat, ack, ts);
+
+ if (!bbr_is_in_probe_bw_state(bbr)) {
+ return;
+ }
+
+ switch (bbr->state) {
+ case NGTCP2_BBR2_STATE_PROBE_BW_DOWN:
+ if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) {
+ return;
+ }
+
+ if (bbr_check_time_to_cruise(bbr, cstat, ts)) {
+ bbr_start_probe_bw_cruise(bbr);
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_CRUISE:
+ if (bbr_check_time_to_probe_bw(bbr, cstat, ts)) {
+ return;
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_REFILL:
+ if (bbr->round_start) {
+ bbr->bw_probe_samples = 1;
+ bbr_start_probe_bw_up(bbr, cstat, ts);
+ }
+
+ break;
+ case NGTCP2_BBR2_STATE_PROBE_BW_UP:
+ if (bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts) &&
+ cstat->bytes_in_flight > bbr_inflight(bbr, cstat, bbr->max_bw, 1.25)) {
+ bbr_start_probe_bw_down(bbr, ts);
+ }
+
+ break;
+ default:
+ break;
+ }
+}
+
+static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ (void)ts;
+
+ if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) {
+ return 0;
+ }
+
+ if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, bbr->max_bw, 1.0)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_duration interval,
+ ngtcp2_tstamp ts) {
+ return ts > bbr->cycle_stamp + interval;
+}
+
+static uint64_t bbr_inflight_with_headroom(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t headroom;
+ uint64_t mpcwnd;
+ if (bbr->inflight_hi == UINT64_MAX) {
+ return UINT64_MAX;
+ }
+
+ headroom = ngtcp2_max(cstat->max_tx_udp_payload_size,
+ bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER /
+ NGTCP2_BBR_HEADROOM_DENOM);
+ mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
+
+ if (bbr->inflight_hi > headroom) {
+ return ngtcp2_max(bbr->inflight_hi - headroom, mpcwnd);
+ }
+
+ return mpcwnd;
+}
+
+static void bbr_raise_inflight_hi_slope(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t growth_this_round = cstat->max_tx_udp_payload_size
+ << bbr->bw_probe_up_rounds;
+
+ bbr->bw_probe_up_rounds = ngtcp2_min(bbr->bw_probe_up_rounds + 1, 30);
+ bbr->probe_up_cnt = ngtcp2_max(cstat->cwnd / growth_this_round, 1) *
+ cstat->max_tx_udp_payload_size;
+}
+
+static void bbr_probe_inflight_hi_upward(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ uint64_t delta;
+
+ if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) {
+ return;
+ }
+
+ bbr->bw_probe_up_acks += ack->bytes_delivered;
+
+ if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) {
+ delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt;
+ bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt;
+ bbr->inflight_hi += delta * cstat->max_tx_udp_payload_size;
+ }
+
+ if (bbr->round_start) {
+ bbr_raise_inflight_hi_slope(bbr, cstat);
+ }
+}
+
+static void bbr_adapt_upper_bounds(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING &&
+ bbr->round_start) {
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK;
+ }
+
+ if (bbr->ack_phase == NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING &&
+ bbr->round_start) {
+ if (bbr_is_in_probe_bw_state(bbr) && !bbr->rst->rs.is_app_limited) {
+ bbr_advance_max_bw_filter(bbr);
+ }
+ }
+
+ if (!bbr_check_inflight_too_high(bbr, cstat, ts)) {
+ /* bbr->bw_hi never be updated */
+ if (bbr->inflight_hi == UINT64_MAX /* || bbr->bw_hi == UINT64_MAX */) {
+ return;
+ }
+
+ if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) {
+ bbr->inflight_hi = bbr->rst->rs.tx_in_flight;
+ }
+
+ if (cstat->delivery_rate_sec > bbr->bw_hi) {
+ bbr->bw_hi = cstat->delivery_rate_sec;
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ bbr_probe_inflight_hi_upward(bbr, cstat, ack);
+ }
+ }
+}
+
+static int bbr_check_time_to_probe_bw(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr_has_elapsed_in_phase(bbr, bbr->bw_probe_wait, ts) ||
+ bbr_is_reno_coexistence_probe_time(bbr, cstat)) {
+ bbr_start_probe_bw_refill(bbr);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void bbr_pick_probe_wait(ngtcp2_bbr2_cc *bbr) {
+ uint8_t rand;
+
+ bbr->rand(&rand, 1, &bbr->rand_ctx);
+
+ bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256);
+
+ bbr->rand(&rand, 1, &bbr->rand_ctx);
+
+ bbr->bw_probe_wait = 2 * NGTCP2_SECONDS +
+ (ngtcp2_tstamp)((double)rand / 255. * NGTCP2_SECONDS);
+}
+
+static int bbr_is_reno_coexistence_probe_time(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t reno_rounds =
+ bbr_target_inflight(bbr, cstat) / cstat->max_tx_udp_payload_size;
+
+ return bbr->rounds_since_bw_probe >= ngtcp2_min(reno_rounds, 63);
+}
+
+static uint64_t bbr_target_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t bdp = bbr_inflight(bbr, cstat, bbr->bw, 1.0);
+
+ return ngtcp2_min(bdp, cstat->cwnd);
+}
+
+static int bbr_check_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (is_inflight_too_high(&bbr->rst->rs)) {
+ if (bbr->bw_probe_samples) {
+ bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int is_inflight_too_high(const ngtcp2_rs *rs) {
+ return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM >
+ rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER;
+}
+
+static void bbr_handle_inflight_too_high(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_rs *rs,
+ ngtcp2_tstamp ts) {
+ bbr->bw_probe_samples = 0;
+
+ if (!rs->is_app_limited) {
+ bbr->prior_inflight_hi = bbr->inflight_hi;
+
+ bbr->inflight_hi = ngtcp2_max(
+ rs->tx_in_flight, bbr_target_inflight(bbr, cstat) *
+ NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ bbr_start_probe_bw_down(bbr, ts);
+ }
+}
+
+static void bbr_handle_lost_packet(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ ngtcp2_rs rs = {0};
+
+ if (!bbr->bw_probe_samples) {
+ return;
+ }
+
+ rs.tx_in_flight = pkt->tx_in_flight;
+ /* bbr->rst->lost is not incremented for pkt yet */
+ rs.lost = bbr->rst->lost + pkt->pktlen - pkt->lost;
+ rs.is_app_limited = pkt->is_app_limited;
+
+ if (is_inflight_too_high(&rs)) {
+ rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt);
+
+ bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
+ }
+}
+
+static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_bbr2_cc *bbr,
+ const ngtcp2_rs *rs,
+ const ngtcp2_cc_pkt *pkt) {
+ uint64_t inflight_prev, lost_prev, lost_prefix;
+ (void)bbr;
+
+ assert(rs->tx_in_flight >= pkt->pktlen);
+
+ inflight_prev = rs->tx_in_flight - pkt->pktlen;
+
+ assert(rs->lost >= pkt->pktlen);
+
+ lost_prev = rs->lost - pkt->pktlen;
+
+ if (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER <
+ lost_prev * NGTCP2_BBR_LOSS_THRESH_DENOM) {
+ return inflight_prev;
+ }
+
+ lost_prefix = (inflight_prev * NGTCP2_BBR_LOSS_THRESH_NUMER -
+ lost_prev * NGTCP2_BBR_LOSS_THRESH_DENOM) /
+ (NGTCP2_BBR_LOSS_THRESH_DENOM - NGTCP2_BBR_LOSS_THRESH_NUMER);
+
+ return inflight_prev + lost_prefix;
+}
+
+static void bbr_update_min_rtt(ngtcp2_bbr2_cc *bbr, const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ int min_rtt_expired;
+
+ bbr->probe_rtt_expired =
+ ts > bbr->probe_rtt_min_stamp + NGTCP2_BBR_PROBE_RTT_INTERVAL;
+
+ if (ack->rtt != UINT64_MAX &&
+ (ack->rtt < bbr->probe_rtt_min_delay || bbr->probe_rtt_expired)) {
+ bbr->probe_rtt_min_delay = ack->rtt;
+ bbr->probe_rtt_min_stamp = ts;
+ }
+
+ min_rtt_expired = ts > bbr->min_rtt_stamp + NGTCP2_BBR_MIN_RTT_FILTERLEN;
+
+ if (bbr->probe_rtt_min_delay < bbr->min_rtt || min_rtt_expired) {
+ bbr->min_rtt = bbr->probe_rtt_min_delay;
+ bbr->min_rtt_stamp = bbr->probe_rtt_min_stamp;
+
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 update min_rtt=%" PRIu64, bbr->min_rtt);
+ }
+}
+
+static void bbr_check_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT && bbr->probe_rtt_expired &&
+ !bbr->idle_restart) {
+ bbr_enter_probe_rtt(bbr);
+ bbr_save_cwnd(bbr, cstat);
+
+ bbr->probe_rtt_done_stamp = UINT64_MAX;
+ bbr->ack_phase = NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING;
+
+ bbr_start_round(bbr);
+ }
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr_handle_probe_rtt(bbr, cstat, ts);
+ }
+
+ if (bbr->rst->rs.delivered) {
+ bbr->idle_restart = 0;
+ }
+}
+
+static void bbr_enter_probe_rtt(ngtcp2_bbr2_cc *bbr) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV, "bbr2 enter ProbeRTT");
+
+ bbr->state = NGTCP2_BBR2_STATE_PROBE_RTT;
+ bbr->pacing_gain = 1;
+ bbr->cwnd_gain = NGTCP2_BBR_PROBE_RTT_CWND_GAIN;
+}
+
+static void bbr_handle_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ bbr_mark_connection_app_limited(bbr, cstat);
+
+ if (bbr->probe_rtt_done_stamp == UINT64_MAX &&
+ cstat->bytes_in_flight <= bbr_probe_rtt_cwnd(bbr, cstat)) {
+ bbr->probe_rtt_done_stamp = ts + NGTCP2_BBR_PROBE_RTT_DURATION;
+ bbr->probe_rtt_round_done = 0;
+
+ bbr_start_round(bbr);
+
+ return;
+ }
+
+ if (bbr->probe_rtt_done_stamp != UINT64_MAX) {
+ if (bbr->round_start) {
+ bbr->probe_rtt_round_done = 1;
+ }
+
+ if (bbr->probe_rtt_round_done) {
+ bbr_check_probe_rtt_done(bbr, cstat, ts);
+ }
+ }
+}
+
+static void bbr_check_probe_rtt_done(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (bbr->probe_rtt_done_stamp != UINT64_MAX &&
+ ts > bbr->probe_rtt_done_stamp) {
+ bbr->probe_rtt_min_stamp = ts;
+ bbr_restore_cwnd(bbr, cstat);
+ bbr_exit_probe_rtt(bbr, ts);
+ }
+}
+
+static void bbr_mark_connection_app_limited(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t app_limited = bbr->rst->delivered + cstat->bytes_in_flight;
+
+ if (app_limited) {
+ bbr->rst->app_limited = app_limited;
+ } else {
+ bbr->rst->app_limited = cstat->max_tx_udp_payload_size;
+ }
+}
+
+static void bbr_exit_probe_rtt(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
+ bbr_reset_lower_bounds(bbr);
+
+ if (bbr->filled_pipe) {
+ bbr_start_probe_bw_down(bbr, ts);
+ bbr_start_probe_bw_cruise(bbr);
+ } else {
+ bbr_enter_startup(bbr);
+ }
+}
+
+static void bbr_handle_restart_from_idle(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ if (cstat->bytes_in_flight == 0 && bbr->rst->app_limited) {
+ ngtcp2_log_info(bbr->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "bbr2 restart from idle");
+
+ bbr->idle_restart = 1;
+ bbr->extra_acked_interval_start = ts;
+
+ if (bbr_is_in_probe_bw_state(bbr)) {
+ bbr_set_pacing_rate_with_gain(bbr, cstat, 1);
+ } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr_check_probe_rtt_done(bbr, cstat, ts);
+ }
+ }
+}
+
+static uint64_t bbr_bdp_multiple(ngtcp2_bbr2_cc *bbr, uint64_t bw,
+ double gain) {
+ uint64_t bdp;
+
+ if (bbr->min_rtt == UINT64_MAX) {
+ return bbr->initial_cwnd;
+ }
+
+ bdp = bw * bbr->min_rtt / NGTCP2_SECONDS;
+
+ return (uint64_t)(gain * (double)bdp);
+}
+
+static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) {
+ return max_udp_payload_size * 4;
+}
+
+static uint64_t bbr_quantization_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ uint64_t inflight) {
+ bbr_update_offload_budget(bbr, cstat);
+
+ inflight = ngtcp2_max(inflight, bbr->offload_budget);
+ inflight =
+ ngtcp2_max(inflight, min_pipe_cwnd(cstat->max_tx_udp_payload_size));
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_UP) {
+ inflight += 2 * cstat->max_tx_udp_payload_size;
+ }
+
+ return inflight;
+}
+
+static uint64_t bbr_inflight(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ uint64_t bw, double gain) {
+ uint64_t inflight = bbr_bdp_multiple(bbr, bw, gain);
+
+ return bbr_quantization_budget(bbr, cstat, inflight);
+}
+
+static void bbr_update_max_inflight(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t inflight;
+
+ /* Not documented */
+ /* bbr_update_aggregation_budget(bbr); */
+
+ inflight = bbr_bdp_multiple(bbr, bbr->bw, bbr->cwnd_gain) + bbr->extra_acked;
+ bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight);
+}
+
+static void bbr_update_offload_budget(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ bbr->offload_budget = 3 * cstat->send_quantum;
+}
+
+static void bbr_advance_max_bw_filter(ngtcp2_bbr2_cc *bbr) {
+ ++bbr->cycle_count;
+}
+
+static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (ack->bytes_lost > 0) {
+ if (cstat->cwnd > ack->bytes_lost) {
+ cstat->cwnd -= ack->bytes_lost;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_tx_udp_payload_size);
+ } else {
+ cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
+ }
+ }
+
+ if (bbr->packet_conservation) {
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, cstat->bytes_in_flight + ack->bytes_delivered);
+ }
+}
+
+static void bbr_save_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ if (!bbr->in_loss_recovery && bbr->state != NGTCP2_BBR2_STATE_PROBE_RTT) {
+ bbr->prior_cwnd = cstat->cwnd;
+ return;
+ }
+
+ bbr->prior_cwnd = ngtcp2_max(bbr->prior_cwnd, cstat->cwnd);
+}
+
+static void bbr_restore_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, bbr->prior_cwnd);
+}
+
+static uint64_t bbr_probe_rtt_cwnd(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t probe_rtt_cwnd =
+ bbr_bdp_multiple(bbr, bbr->bw, NGTCP2_BBR_PROBE_RTT_CWND_GAIN);
+ uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
+
+ return ngtcp2_max(probe_rtt_cwnd, mpcwnd);
+}
+
+static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t probe_rtt_cwnd;
+
+ if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT) {
+ probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat);
+
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, probe_rtt_cwnd);
+ }
+}
+
+static void bbr_set_cwnd(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ uint64_t mpcwnd;
+
+ bbr_update_max_inflight(bbr, cstat);
+ bbr_modulate_cwnd_for_recovery(bbr, cstat, ack);
+
+ if (!bbr->packet_conservation) {
+ if (bbr->filled_pipe) {
+ cstat->cwnd += ack->bytes_delivered;
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, bbr->max_inflight);
+ } else if (cstat->cwnd < bbr->max_inflight ||
+ bbr->rst->delivered < bbr->initial_cwnd) {
+ cstat->cwnd += ack->bytes_delivered;
+ }
+
+ mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, mpcwnd);
+ }
+
+ bbr_bound_cwnd_for_probe_rtt(bbr, cstat);
+ bbr_bound_cwnd_for_model(bbr, cstat);
+}
+
+static void bbr_bound_cwnd_for_model(ngtcp2_bbr2_cc *bbr,
+ ngtcp2_conn_stat *cstat) {
+ uint64_t cap = UINT64_MAX;
+ uint64_t mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
+
+ if (bbr_is_in_probe_bw_state(bbr) &&
+ bbr->state != NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) {
+ cap = bbr->inflight_hi;
+ } else if (bbr->state == NGTCP2_BBR2_STATE_PROBE_RTT ||
+ bbr->state == NGTCP2_BBR2_STATE_PROBE_BW_CRUISE) {
+ cap = bbr_inflight_with_headroom(bbr, cstat);
+ }
+
+ cap = ngtcp2_min(cap, bbr->inflight_lo);
+ cap = ngtcp2_max(cap, mpcwnd);
+
+ cstat->cwnd = ngtcp2_min(cstat->cwnd, cap);
+}
+
+static void bbr_set_send_quantum(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat) {
+ size_t send_quantum =
+ (size_t)(cstat->pacing_rate * (double)(bbr->min_rtt == UINT64_MAX
+ ? NGTCP2_MILLISECONDS
+ : bbr->min_rtt));
+ (void)bbr;
+
+ cstat->send_quantum = ngtcp2_min(send_quantum, 64 * 1024);
+ cstat->send_quantum =
+ ngtcp2_max(cstat->send_quantum, cstat->max_tx_udp_payload_size * 10);
+}
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time) {
+ return cstat->congestion_recovery_start_ts != UINT64_MAX &&
+ sent_time <= cstat->congestion_recovery_start_ts;
+}
+
+static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
+ if (bbr->in_loss_recovery) {
+ if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) {
+ bbr->packet_conservation = 0;
+ }
+
+ if (!in_congestion_recovery(cstat, ack->largest_acked_sent_ts)) {
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+ bbr_restore_cwnd(bbr, cstat);
+ }
+
+ return;
+ }
+
+ if (bbr->congestion_recovery_start_ts != UINT64_MAX) {
+ bbr->in_loss_recovery = 1;
+ bbr_save_cwnd(bbr, cstat);
+ cstat->cwnd =
+ cstat->bytes_in_flight +
+ ngtcp2_max(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
+
+ cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->packet_conservation = 1;
+ bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered;
+ bbr->prior_inflight_lo = bbr->inflight_lo;
+ bbr->prior_bw_lo = bbr->bw_lo;
+ }
+}
+
+static void bbr2_cc_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
+ ngtcp2_rst *rst, ngtcp2_tstamp initial_ts,
+ ngtcp2_rand rand, const ngtcp2_rand_ctx *rand_ctx,
+ ngtcp2_log *log) {
+ bbr->ccb.log = log;
+ bbr->rst = rst;
+ bbr->rand = rand;
+ bbr->rand_ctx = *rand_ctx;
+ bbr->initial_cwnd = cstat->cwnd;
+
+ bbr_on_init(bbr, cstat, initial_ts);
+}
+
+static void bbr2_cc_free(ngtcp2_bbr2_cc *bbr) { (void)bbr; }
+
+static void bbr2_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)pkt;
+ (void)ts;
+}
+
+static void bbr2_cc_on_pkt_lost(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_update_on_loss(bbr, cstat, pkt, ts);
+}
+
+static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ if (!bbr->filled_pipe || bbr->in_loss_recovery ||
+ bbr->congestion_recovery_start_ts != UINT64_MAX ||
+ in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ bbr->congestion_recovery_start_ts = ts;
+}
+
+static void bbr2_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+ (void)ts;
+
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ if (bbr->in_loss_recovery) {
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+ bbr_restore_cwnd(bbr, cstat);
+ bbr->full_bw_count = 0;
+ bbr->loss_in_round = 0;
+ bbr->inflight_lo = ngtcp2_max(bbr->inflight_lo, bbr->prior_inflight_lo);
+ bbr->inflight_hi = ngtcp2_max(bbr->inflight_hi, bbr->prior_inflight_hi);
+ bbr->bw_lo = ngtcp2_max(bbr->bw_lo, bbr->prior_bw_lo);
+ }
+}
+
+static void bbr2_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+ (void)ts;
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->congestion_recovery_start_ts = UINT64_MAX;
+ bbr->in_loss_recovery = 0;
+ bbr->packet_conservation = 0;
+
+ bbr_save_cwnd(bbr, cstat);
+ cstat->cwnd = cstat->bytes_in_flight + cstat->max_tx_udp_payload_size;
+ cstat->cwnd =
+ ngtcp2_max(cstat->cwnd, min_pipe_cwnd(cstat->max_tx_udp_payload_size));
+}
+
+static void bbr2_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_update_on_ack(bbr, cstat, ack, ts);
+}
+
+static void bbr2_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_on_transmit(bbr, cstat, pkt->sent_ts);
+}
+
+static void bbr2_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)ts;
+}
+
+static void bbr2_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr_on_init(bbr, cstat, ts);
+}
+
+static void bbr2_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)cstat;
+ (void)event;
+ (void)ts;
+}
+
+int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem) {
+ ngtcp2_bbr2_cc *bbr;
+
+ bbr = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_bbr2_cc));
+ if (bbr == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ bbr2_cc_init(bbr, cstat, rst, initial_ts, rand, rand_ctx, log);
+
+ cc->ccb = &bbr->ccb;
+ cc->on_pkt_acked = bbr2_cc_on_pkt_acked;
+ cc->on_pkt_lost = bbr2_cc_on_pkt_lost;
+ cc->congestion_event = bbr2_cc_congestion_event;
+ cc->on_spurious_congestion = bbr2_cc_on_spurious_congestion;
+ cc->on_persistent_congestion = bbr2_cc_on_persistent_congestion;
+ cc->on_ack_recv = bbr2_cc_on_ack_recv;
+ cc->on_pkt_sent = bbr2_cc_on_pkt_sent;
+ cc->new_rtt_sample = bbr2_cc_new_rtt_sample;
+ cc->reset = bbr2_cc_reset;
+ cc->event = bbr2_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(cc->ccb, ngtcp2_bbr2_cc, ccb);
+
+ bbr2_cc_free(bbr);
+ ngtcp2_mem_free(mem, bbr);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
new file mode 100644
index 0000000..50dc05a
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
@@ -0,0 +1,149 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BBR2_H
+#define NGTCP2_BBR2_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cc.h"
+#include "ngtcp2_window_filter.h"
+
+typedef struct ngtcp2_rst ngtcp2_rst;
+
+typedef enum ngtcp2_bbr2_state {
+ NGTCP2_BBR2_STATE_STARTUP,
+ NGTCP2_BBR2_STATE_DRAIN,
+ NGTCP2_BBR2_STATE_PROBE_BW_DOWN,
+ NGTCP2_BBR2_STATE_PROBE_BW_CRUISE,
+ NGTCP2_BBR2_STATE_PROBE_BW_REFILL,
+ NGTCP2_BBR2_STATE_PROBE_BW_UP,
+ NGTCP2_BBR2_STATE_PROBE_RTT,
+} ngtcp2_bbr2_state;
+
+typedef enum ngtcp2_bbr2_ack_phase {
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STARTING,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_STOPPING,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_PROBE_FEEDBACK,
+ NGTCP2_BBR2_ACK_PHASE_ACKS_REFILLING,
+} ngtcp2_bbr2_ack_phase;
+
+/*
+ * ngtcp2_bbr2_cc is BBR v2 congestion controller, described in
+ * https://datatracker.ietf.org/doc/html/draft-cardwell-iccrg-bbr-congestion-control-01
+ */
+typedef struct ngtcp2_bbr2_cc {
+ ngtcp2_cc_base ccb;
+
+ uint64_t initial_cwnd;
+ ngtcp2_rst *rst;
+ ngtcp2_rand rand;
+ ngtcp2_rand_ctx rand_ctx;
+
+ /* max_bw_filter for tracking the maximum recent delivery rate
+ samples for estimating max_bw. */
+ ngtcp2_window_filter max_bw_filter;
+
+ ngtcp2_window_filter extra_acked_filter;
+
+ ngtcp2_duration min_rtt;
+ ngtcp2_tstamp min_rtt_stamp;
+ ngtcp2_tstamp probe_rtt_done_stamp;
+ int probe_rtt_round_done;
+ uint64_t prior_cwnd;
+ int idle_restart;
+ ngtcp2_tstamp extra_acked_interval_start;
+ uint64_t extra_acked_delivered;
+
+ /* Congestion signals */
+ int loss_in_round;
+ uint64_t bw_latest;
+ uint64_t inflight_latest;
+
+ /* Lower bounds */
+ uint64_t bw_lo;
+ uint64_t inflight_lo;
+
+ /* Round counting */
+ uint64_t next_round_delivered;
+ int round_start;
+ uint64_t round_count;
+
+ /* Full pipe */
+ int filled_pipe;
+ uint64_t full_bw;
+ size_t full_bw_count;
+
+ /* Pacing rate */
+ double pacing_gain;
+
+ ngtcp2_bbr2_state state;
+ double cwnd_gain;
+
+ int loss_round_start;
+ uint64_t loss_round_delivered;
+ uint64_t rounds_since_bw_probe;
+ uint64_t max_bw;
+ uint64_t bw;
+ uint64_t cycle_count;
+ uint64_t extra_acked;
+ uint64_t bytes_lost_in_round;
+ size_t loss_events_in_round;
+ uint64_t offload_budget;
+ uint64_t probe_up_cnt;
+ ngtcp2_tstamp cycle_stamp;
+ ngtcp2_bbr2_ack_phase ack_phase;
+ ngtcp2_duration bw_probe_wait;
+ int bw_probe_samples;
+ size_t bw_probe_up_rounds;
+ uint64_t bw_probe_up_acks;
+ uint64_t inflight_hi;
+ uint64_t bw_hi;
+ int probe_rtt_expired;
+ ngtcp2_duration probe_rtt_min_delay;
+ ngtcp2_tstamp probe_rtt_min_stamp;
+ int in_loss_recovery;
+ int packet_conservation;
+ uint64_t max_inflight;
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ uint64_t congestion_recovery_next_round_delivered;
+
+ uint64_t prior_inflight_lo;
+ uint64_t prior_inflight_hi;
+ uint64_t prior_bw_lo;
+} ngtcp2_bbr2_cc;
+
+int ngtcp2_cc_bbr2_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
+ ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
+ const ngtcp2_rand_ctx *rand_ctx,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_bbr2_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+#endif /* NGTCP2_BBR2_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c
new file mode 100644
index 0000000..75326d6
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c
@@ -0,0 +1,56 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_buf.h"
+#include "ngtcp2_mem.h"
+
+void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) {
+ buf->begin = buf->pos = buf->last = begin;
+ buf->end = begin + len;
+}
+
+void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; }
+
+size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) {
+ return (size_t)(buf->end - buf->begin);
+}
+
+int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
+ const ngtcp2_mem *mem) {
+ *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len);
+ if (*pbufchain == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pbufchain)->next = NULL;
+
+ ngtcp2_buf_init(&(*pbufchain)->buf,
+ (uint8_t *)(*pbufchain) + sizeof(ngtcp2_buf_chain), len);
+
+ return 0;
+}
+
+void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, bufchain);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h
new file mode 100644
index 0000000..107d413
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h
@@ -0,0 +1,108 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BUF_H
+#define NGTCP2_BUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_buf {
+ /* begin points to the beginning of the buffer. */
+ uint8_t *begin;
+ /* end points to the one beyond of the last byte of the buffer */
+ uint8_t *end;
+ /* pos pointers to the start of data. Typically, this points to the
+ point that next data should be read. Initially, it points to
+ |begin|. */
+ uint8_t *pos;
+ /* last points to the one beyond of the last data of the buffer.
+ Typically, new data is written at this point. Initially, it
+ points to |begin|. */
+ uint8_t *last;
+} ngtcp2_buf;
+
+/*
+ * ngtcp2_buf_init initializes |buf| with the given buffer.
+ */
+void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len);
+
+/*
+ * ngtcp2_buf_reset resets pos and last fields to match begin field to
+ * make ngtcp2_buf_len(buf) return 0.
+ */
+void ngtcp2_buf_reset(ngtcp2_buf *buf);
+
+/*
+ * ngtcp2_buf_left returns the number of additional bytes which can be
+ * written to the underlying buffer. In other words, it returns
+ * buf->end - buf->last.
+ */
+#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last)
+
+/*
+ * ngtcp2_buf_len returns the number of bytes left to read. In other
+ * words, it returns buf->last - buf->pos.
+ */
+#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos)
+
+/*
+ * ngtcp2_buf_cap returns the capacity of the buffer. In other words,
+ * it returns buf->end - buf->begin.
+ */
+size_t ngtcp2_buf_cap(const ngtcp2_buf *buf);
+
+/*
+ * ngtcp2_buf_chain is a linked list of ngtcp2_buf.
+ */
+typedef struct ngtcp2_buf_chain ngtcp2_buf_chain;
+
+struct ngtcp2_buf_chain {
+ ngtcp2_buf_chain *next;
+ ngtcp2_buf buf;
+};
+
+/*
+ * ngtcp2_buf_chain_new creates new ngtcp2_buf_chain and initializes
+ * the internal buffer with |len| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_buf_chain_del deletes the resource allocated by |bufchain|.
+ * It also deletes the memory pointed by |bufchain|.
+ */
+void ngtcp2_buf_chain_del(ngtcp2_buf_chain *bufchain, const ngtcp2_mem *mem);
+
+#endif /* NGTCP2_BUF_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
new file mode 100644
index 0000000..0536639
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
@@ -0,0 +1,615 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_cc.h"
+
+#include <assert.h>
+
+#if defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_rcvry.h"
+
+uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
+ uint64_t n = 2 * max_udp_payload_size;
+ n = ngtcp2_max(n, 14720);
+ return ngtcp2_min(10 * max_udp_payload_size, n);
+}
+
+ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
+ size_t pktlen, ngtcp2_pktns_id pktns_id,
+ ngtcp2_tstamp sent_ts, uint64_t lost,
+ uint64_t tx_in_flight, int is_app_limited) {
+ pkt->pkt_num = pkt_num;
+ pkt->pktlen = pktlen;
+ pkt->pktns_id = pktns_id;
+ pkt->sent_ts = sent_ts;
+ pkt->lost = lost;
+ pkt->tx_in_flight = tx_in_flight;
+ pkt->is_app_limited = is_app_limited;
+
+ return pkt;
+}
+
+static void reno_cc_reset(ngtcp2_reno_cc *cc) {
+ cc->max_delivery_rate_sec = 0;
+ cc->target_cwnd = 0;
+ cc->pending_add = 0;
+}
+
+void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log) {
+ cc->ccb.log = log;
+ reno_cc_reset(cc);
+}
+
+void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ ngtcp2_reno_cc *reno_cc;
+
+ reno_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_reno_cc));
+ if (reno_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_reno_cc_init(reno_cc, log);
+
+ cc->ccb = &reno_cc->ccb;
+ cc->on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked;
+ cc->congestion_event = ngtcp2_cc_reno_cc_congestion_event;
+ cc->on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion;
+ cc->on_ack_recv = ngtcp2_cc_reno_cc_on_ack_recv;
+ cc->reset = ngtcp2_cc_reno_cc_reset;
+
+ return 0;
+}
+
+void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_reno_cc *reno_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_reno_cc, ccb);
+
+ ngtcp2_reno_cc_free(reno_cc);
+ ngtcp2_mem_free(mem, reno_cc);
+}
+
+static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_time) {
+ return cstat->congestion_recovery_start_ts != UINT64_MAX &&
+ sent_time <= cstat->congestion_recovery_start_ts;
+}
+
+void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ uint64_t m;
+ (void)ts;
+
+ if (in_congestion_recovery(cstat, pkt->sent_ts)) {
+ return;
+ }
+
+ if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) {
+ return;
+ }
+
+ if (cstat->cwnd < cstat->ssthresh) {
+ cstat->cwnd += pkt->pktlen;
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd);
+ return;
+ }
+
+ m = cstat->max_tx_udp_payload_size * pkt->pktlen + cc->pending_add;
+ cc->pending_add = m % cstat->cwnd;
+
+ cstat->cwnd += m / cstat->cwnd;
+}
+
+void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ uint64_t min_cwnd;
+
+ if (in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ cstat->congestion_recovery_start_ts = ts;
+ cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS;
+ min_cwnd = 2 * cstat->max_tx_udp_payload_size;
+ cstat->cwnd = ngtcp2_max(cstat->cwnd, min_cwnd);
+ cstat->ssthresh = cstat->cwnd;
+
+ cc->pending_add = 0;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
+void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)ts;
+
+ cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+}
+
+void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ uint64_t target_cwnd, initcwnd;
+ (void)ack;
+ (void)ts;
+
+ /* TODO Use sliding window for min rtt measurement */
+ /* TODO Use sliding window */
+ cc->max_delivery_rate_sec =
+ ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec);
+
+ if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) {
+ target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS;
+ initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_tx_udp_payload_size);
+ cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64
+ " min_rtt=%" PRIu64,
+ cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt);
+ }
+}
+
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb);
+ (void)cstat;
+ (void)ts;
+
+ reno_cc_reset(cc);
+}
+
+static void cubic_cc_reset(ngtcp2_cubic_cc *cc) {
+ cc->max_delivery_rate_sec = 0;
+ cc->target_cwnd = 0;
+ cc->w_last_max = 0;
+ cc->w_tcp = 0;
+ cc->origin_point = 0;
+ cc->epoch_start = UINT64_MAX;
+ cc->k = 0;
+
+ cc->prior.cwnd = 0;
+ cc->prior.ssthresh = 0;
+ cc->prior.w_last_max = 0;
+ cc->prior.w_tcp = 0;
+ cc->prior.origin_point = 0;
+ cc->prior.epoch_start = UINT64_MAX;
+ cc->prior.k = 0;
+
+ cc->rtt_sample_count = 0;
+ cc->current_round_min_rtt = UINT64_MAX;
+ cc->last_round_min_rtt = UINT64_MAX;
+ cc->window_end = -1;
+}
+
+void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log) {
+ cc->ccb.log = log;
+ cubic_cc_reset(cc);
+}
+
+void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc) { (void)cc; }
+
+int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ ngtcp2_cubic_cc *cubic_cc;
+
+ cubic_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_cubic_cc));
+ if (cubic_cc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_cubic_cc_init(cubic_cc, log);
+
+ cc->ccb = &cubic_cc->ccb;
+ cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked;
+ cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
+ cc->on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
+ cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion;
+ cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
+ cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent;
+ cc->new_rtt_sample = ngtcp2_cc_cubic_cc_new_rtt_sample;
+ cc->reset = ngtcp2_cc_cubic_cc_reset;
+ cc->event = ngtcp2_cc_cubic_cc_event;
+
+ return 0;
+}
+
+void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) {
+ ngtcp2_cubic_cc *cubic_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_cubic_cc, ccb);
+
+ ngtcp2_cubic_cc_free(cubic_cc);
+ ngtcp2_mem_free(mem, cubic_cc);
+}
+
+static uint64_t ngtcp2_cbrt(uint64_t n) {
+ int d;
+ uint64_t a;
+
+ if (n == 0) {
+ return 0;
+ }
+
+#if defined(_MSC_VER)
+# if defined(_M_X64)
+ d = (int)__lzcnt64(n);
+# elif defined(_M_ARM64)
+ {
+ unsigned long index;
+ d = sizeof(uint64_t) * CHAR_BIT;
+ if (_BitScanReverse64(&index, n)) {
+ d = d - 1 - index;
+ }
+ }
+# else
+ if ((n >> 32) != 0) {
+ d = __lzcnt((unsigned int)(n >> 32));
+ } else {
+ d = 32 + __lzcnt((unsigned int)n);
+ }
+# endif
+#else
+ d = __builtin_clzll(n);
+#endif
+ a = 1ULL << ((64 - d) / 3 + 1);
+
+ for (; a * a * a > n;) {
+ a = (2 * a + n / a / a) / 3;
+ }
+ return a;
+}
+
+/* HyStart++ constants */
+#define NGTCP2_HS_MIN_SSTHRESH 16
+#define NGTCP2_HS_N_RTT_SAMPLE 8
+#define NGTCP2_HS_MIN_ETA (4 * NGTCP2_MILLISECONDS)
+#define NGTCP2_HS_MAX_ETA (16 * NGTCP2_MILLISECONDS)
+
+void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ ngtcp2_duration t, eta;
+ uint64_t target, cwnd_thres;
+ uint64_t tx, kx, time_delta, delta;
+ uint64_t add, tcp_add;
+ uint64_t m;
+
+ if (pkt->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && cc->window_end != -1 &&
+ cc->window_end <= pkt->pkt_num) {
+ cc->window_end = -1;
+ }
+
+ if (in_congestion_recovery(cstat, pkt->sent_ts)) {
+ return;
+ }
+
+ if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) {
+ return;
+ }
+
+ if (cstat->cwnd < cstat->ssthresh) {
+ /* slow-start */
+ cstat->cwnd += pkt->pktlen;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd);
+
+ if (cc->last_round_min_rtt != UINT64_MAX &&
+ cc->current_round_min_rtt != UINT64_MAX &&
+ cstat->cwnd >=
+ NGTCP2_HS_MIN_SSTHRESH * cstat->max_tx_udp_payload_size &&
+ cc->rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE) {
+ eta = cc->last_round_min_rtt / 8;
+
+ if (eta < NGTCP2_HS_MIN_ETA) {
+ eta = NGTCP2_HS_MIN_ETA;
+ } else if (eta > NGTCP2_HS_MAX_ETA) {
+ eta = NGTCP2_HS_MAX_ETA;
+ }
+
+ if (cc->current_round_min_rtt >= cc->last_round_min_rtt + eta) {
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "HyStart++ exit slow start");
+
+ cc->w_last_max = cstat->cwnd;
+ cstat->ssthresh = cstat->cwnd;
+ }
+ }
+
+ return;
+ }
+
+ /* congestion avoidance */
+
+ if (cc->epoch_start == UINT64_MAX) {
+ cc->epoch_start = ts;
+ if (cstat->cwnd < cc->w_last_max) {
+ cc->k = ngtcp2_cbrt((cc->w_last_max - cstat->cwnd) * 10 / 4 /
+ cstat->max_tx_udp_payload_size);
+ cc->origin_point = cc->w_last_max;
+ } else {
+ cc->k = 0;
+ cc->origin_point = cstat->cwnd;
+ }
+
+ cc->w_tcp = cstat->cwnd;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "cubic-ca epoch_start=%" PRIu64 " k=%" PRIu64
+ " origin_point=%" PRIu64,
+ cc->epoch_start, cc->k, cc->origin_point);
+
+ cc->pending_add = 0;
+ cc->pending_w_add = 0;
+ }
+
+ t = ts - cc->epoch_start;
+
+ tx = (t << 10) / NGTCP2_SECONDS;
+ kx = (cc->k << 10);
+
+ if (tx > kx) {
+ time_delta = tx - kx;
+ } else {
+ time_delta = kx - tx;
+ }
+
+ delta = cstat->max_tx_udp_payload_size *
+ ((((time_delta * time_delta) >> 10) * time_delta) >> 10) * 4 / 10;
+ delta >>= 10;
+
+ if (tx > kx) {
+ target = cc->origin_point + delta;
+ } else {
+ target = cc->origin_point - delta;
+ }
+
+ cwnd_thres =
+ (target * (((t + cstat->smoothed_rtt) << 10) / NGTCP2_SECONDS)) >> 10;
+ if (cwnd_thres < cstat->cwnd) {
+ target = cstat->cwnd;
+ } else if (2 * cwnd_thres > 3 * cstat->cwnd) {
+ target = cstat->cwnd * 3 / 2;
+ } else {
+ target = cwnd_thres;
+ }
+
+ if (target > cstat->cwnd) {
+ m = cc->pending_add +
+ cstat->max_tx_udp_payload_size * (target - cstat->cwnd);
+ add = m / cstat->cwnd;
+ cc->pending_add = m % cstat->cwnd;
+ } else {
+ m = cc->pending_add + cstat->max_tx_udp_payload_size;
+ add = m / (100 * cstat->cwnd);
+ cc->pending_add = m % (100 * cstat->cwnd);
+ }
+
+ m = cc->pending_w_add + cstat->max_tx_udp_payload_size * pkt->pktlen;
+
+ cc->w_tcp += m / cstat->cwnd;
+ cc->pending_w_add = m % cstat->cwnd;
+
+ if (cc->w_tcp > cstat->cwnd) {
+ tcp_add = cstat->max_tx_udp_payload_size * (cc->w_tcp - cstat->cwnd) /
+ cstat->cwnd;
+ if (tcp_add > add) {
+ add = tcp_add;
+ }
+ }
+
+ cstat->cwnd += add;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " acked, cubic-ca cwnd=%" PRIu64 " t=%" PRIu64
+ " k=%" PRIi64 " time_delta=%" PRIu64 " delta=%" PRIu64
+ " target=%" PRIu64 " w_tcp=%" PRIu64,
+ pkt->pkt_num, cstat->cwnd, t, cc->k, time_delta >> 4, delta,
+ target, cc->w_tcp);
+}
+
+void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ uint64_t min_cwnd;
+
+ if (in_congestion_recovery(cstat, sent_ts)) {
+ return;
+ }
+
+ if (cc->prior.cwnd < cstat->cwnd) {
+ cc->prior.cwnd = cstat->cwnd;
+ cc->prior.ssthresh = cstat->ssthresh;
+ cc->prior.w_last_max = cc->w_last_max;
+ cc->prior.w_tcp = cc->w_tcp;
+ cc->prior.origin_point = cc->origin_point;
+ cc->prior.epoch_start = cc->epoch_start;
+ cc->prior.k = cc->k;
+ }
+
+ cstat->congestion_recovery_start_ts = ts;
+
+ cc->epoch_start = UINT64_MAX;
+ if (cstat->cwnd < cc->w_last_max) {
+ cc->w_last_max = cstat->cwnd * 17 / 10 / 2;
+ } else {
+ cc->w_last_max = cstat->cwnd;
+ }
+
+ min_cwnd = 2 * cstat->max_tx_udp_payload_size;
+ cstat->ssthresh = cstat->cwnd * 7 / 10;
+ cstat->ssthresh = ngtcp2_max(cstat->ssthresh, min_cwnd);
+ cstat->cwnd = cstat->ssthresh;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "reduce cwnd because of packet loss cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
+void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)ts;
+
+ if (cstat->cwnd >= cc->prior.cwnd) {
+ return;
+ }
+
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+ cstat->cwnd = cc->prior.cwnd;
+ cstat->ssthresh = cc->prior.ssthresh;
+ cc->w_last_max = cc->prior.w_last_max;
+ cc->w_tcp = cc->prior.w_tcp;
+ cc->origin_point = cc->prior.origin_point;
+ cc->epoch_start = cc->prior.epoch_start;
+ cc->k = cc->prior.k;
+
+ cc->prior.cwnd = 0;
+ cc->prior.ssthresh = 0;
+ cc->prior.w_last_max = 0;
+ cc->prior.w_tcp = 0;
+ cc->prior.origin_point = 0;
+ cc->prior.epoch_start = UINT64_MAX;
+ cc->prior.k = 0;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "spurious congestion is detected and congestion state is "
+ "restored cwnd=%" PRIu64,
+ cstat->cwnd);
+}
+
+void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ (void)ccx;
+ (void)ts;
+
+ cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+}
+
+void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ uint64_t target_cwnd, initcwnd;
+ (void)ack;
+ (void)ts;
+
+ /* TODO Use sliding window for min rtt measurement */
+ /* TODO Use sliding window */
+ cc->max_delivery_rate_sec =
+ ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec);
+
+ if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) {
+ target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS;
+ initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_tx_udp_payload_size);
+ cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100;
+
+ ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV,
+ "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64
+ " min_rtt=%" PRIu64,
+ cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt);
+ }
+}
+
+void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)cstat;
+
+ if (pkt->pktns_id != NGTCP2_PKTNS_ID_APPLICATION || cc->window_end != -1) {
+ return;
+ }
+
+ cc->window_end = pkt->pkt_num;
+ cc->last_round_min_rtt = cc->current_round_min_rtt;
+ cc->current_round_min_rtt = UINT64_MAX;
+ cc->rtt_sample_count = 0;
+}
+
+void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)ts;
+
+ if (cc->window_end == -1) {
+ return;
+ }
+
+ cc->current_round_min_rtt =
+ ngtcp2_min(cc->current_round_min_rtt, cstat->latest_rtt);
+ ++cc->rtt_sample_count;
+}
+
+void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ (void)cstat;
+ (void)ts;
+
+ cubic_cc_reset(cc);
+}
+
+void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts) {
+ ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb);
+ ngtcp2_tstamp last_ts;
+
+ if (event != NGTCP2_CC_EVENT_TYPE_TX_START || cc->epoch_start == UINT64_MAX) {
+ return;
+ }
+
+ last_ts = cstat->last_tx_pkt_ts[NGTCP2_PKTNS_ID_APPLICATION];
+ if (last_ts == UINT64_MAX || last_ts <= cc->epoch_start) {
+ return;
+ }
+
+ assert(ts >= last_ts);
+
+ cc->epoch_start += ts - last_ts;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h
new file mode 100644
index 0000000..6d9e0c2
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h
@@ -0,0 +1,421 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CC_H
+#define NGTCP2_CC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1
+#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3
+
+typedef struct ngtcp2_log ngtcp2_log;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_base` is the base structure of custom congestion
+ * control algorithm. It must be the first field of custom congestion
+ * controller.
+ */
+typedef struct ngtcp2_cc_base {
+ /**
+ * :member:`log` is ngtcp2 library internal logger.
+ */
+ ngtcp2_log *log;
+} ngtcp2_cc_base;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_pkt` is a convenient structure to include
+ * acked/lost/sent packet.
+ */
+typedef struct ngtcp2_cc_pkt {
+ /**
+ * :member:`pkt_num` is the packet number
+ */
+ int64_t pkt_num;
+ /**
+ * :member:`pktlen` is the length of packet.
+ */
+ size_t pktlen;
+ /**
+ * :member:`pktns_id` is the ID of packet number space which this
+ * packet belongs to.
+ */
+ ngtcp2_pktns_id pktns_id;
+ /**
+ * :member:`sent_ts` is the timestamp when packet is sent.
+ */
+ ngtcp2_tstamp sent_ts;
+ /**
+ * :member:`lost` is the number of bytes lost when this packet was
+ * sent.
+ */
+ uint64_t lost;
+ /**
+ * :member:`tx_in_flight` is the bytes in flight when this packet
+ * was sent.
+ */
+ uint64_t tx_in_flight;
+ /**
+ * :member:`is_app_limited` is nonzero if the connection is
+ * app-limited when this packet was sent.
+ */
+ int is_app_limited;
+} ngtcp2_cc_pkt;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc_ack` is a convenient structure which stores
+ * acknowledged and lost bytes.
+ */
+typedef struct ngtcp2_cc_ack {
+ /**
+ * :member:`prior_bytes_in_flight` is the in-flight bytes before
+ * processing this ACK.
+ */
+ uint64_t prior_bytes_in_flight;
+ /**
+ * :member:`bytes_delivered` is the number of bytes acknowledged.
+ */
+ uint64_t bytes_delivered;
+ /**
+ * :member:`bytes_lost` is the number of bytes declared lost.
+ */
+ uint64_t bytes_lost;
+ /**
+ * :member:`pkt_delivered` is the cumulative acknowledged bytes when
+ * the last packet acknowledged by this ACK was sent.
+ */
+ uint64_t pkt_delivered;
+ /**
+ * :member:`largest_acked_sent_ts` is the time when the largest
+ * acknowledged packet was sent.
+ */
+ ngtcp2_tstamp largest_acked_sent_ts;
+ /**
+ * :member:`rtt` is the RTT sample. It is UINT64_MAX if no RTT
+ * sample is available.
+ */
+ ngtcp2_duration rtt;
+} ngtcp2_cc_ack;
+
+typedef struct ngtcp2_cc ngtcp2_cc;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is
+ * called with an acknowledged packet.
+ */
+typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_lost` is a callback function which is
+ * called with a lost packet.
+ */
+typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_congestion_event` is a callback function which is
+ * called when congestion event happens (e.g., when packet is lost).
+ */
+typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_spurious_congestion` is a callback function
+ * which is called when a spurious congestion is detected.
+ */
+typedef void (*ngtcp2_cc_on_spurious_congestion)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function
+ * which is called when persistent congestion is established.
+ */
+typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is
+ * called when an acknowledgement is received.
+ */
+typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is
+ * called when an ack-eliciting packet is sent.
+ */
+typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is
+ * called when new RTT sample is obtained.
+ */
+typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_reset` is a callback function which is called when
+ * congestion state must be reset.
+ */
+typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_cc_event_type` defines congestion control events.
+ */
+typedef enum ngtcp2_cc_event_type {
+ /**
+ * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet
+ * is sent and no other ack-eliciting packet is present.
+ */
+ NGTCP2_CC_EVENT_TYPE_TX_START
+} ngtcp2_cc_event_type;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_cc_event` is a callback function which is called when
+ * a specific event happens.
+ */
+typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cc` is congestion control algorithm interface to
+ * allow custom implementation.
+ */
+typedef struct ngtcp2_cc {
+ /**
+ * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which
+ * usually contains a state.
+ */
+ ngtcp2_cc_base *ccb;
+ /**
+ * :member:`on_pkt_acked` is a callback function which is called
+ * when a packet is acknowledged.
+ */
+ ngtcp2_cc_on_pkt_acked on_pkt_acked;
+ /**
+ * :member:`on_pkt_lost` is a callback function which is called when
+ * a packet is lost.
+ */
+ ngtcp2_cc_on_pkt_lost on_pkt_lost;
+ /**
+ * :member:`congestion_event` is a callback function which is called
+ * when congestion event happens (.e.g, packet is lost).
+ */
+ ngtcp2_cc_congestion_event congestion_event;
+ /**
+ * :member:`on_spurious_congestion` is a callback function which is
+ * called when a spurious congestion is detected.
+ */
+ ngtcp2_cc_on_spurious_congestion on_spurious_congestion;
+ /**
+ * :member:`on_persistent_congestion` is a callback function which
+ * is called when persistent congestion is established.
+ */
+ ngtcp2_cc_on_persistent_congestion on_persistent_congestion;
+ /**
+ * :member:`on_ack_recv` is a callback function which is called when
+ * an acknowledgement is received.
+ */
+ ngtcp2_cc_on_ack_recv on_ack_recv;
+ /**
+ * :member:`on_pkt_sent` is a callback function which is called when
+ * ack-eliciting packet is sent.
+ */
+ ngtcp2_cc_on_pkt_sent on_pkt_sent;
+ /**
+ * :member:`new_rtt_sample` is a callback function which is called
+ * when new RTT sample is obtained.
+ */
+ ngtcp2_cc_new_rtt_sample new_rtt_sample;
+ /**
+ * :member:`reset` is a callback function which is called when
+ * congestion control state must be reset.
+ */
+ ngtcp2_cc_reset reset;
+ /**
+ * :member:`event` is a callback function which is called when a
+ * specific event happens.
+ */
+ ngtcp2_cc_event event;
+} ngtcp2_cc;
+
+/*
+ * ngtcp2_cc_compute_initcwnd computes initial cwnd.
+ */
+uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size);
+
+ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
+ size_t pktlen, ngtcp2_pktns_id pktns_id,
+ ngtcp2_tstamp sent_ts, uint64_t lost,
+ uint64_t tx_in_flight, int is_app_limited);
+
+/* ngtcp2_reno_cc is the RENO congestion controller. */
+typedef struct ngtcp2_reno_cc {
+ ngtcp2_cc_base ccb;
+ uint64_t max_delivery_rate_sec;
+ uint64_t target_cwnd;
+ uint64_t pending_add;
+} ngtcp2_reno_cc;
+
+int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log);
+
+void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc);
+
+void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/* ngtcp2_cubic_cc is CUBIC congestion controller. */
+typedef struct ngtcp2_cubic_cc {
+ ngtcp2_cc_base ccb;
+ uint64_t max_delivery_rate_sec;
+ uint64_t target_cwnd;
+ uint64_t w_last_max;
+ uint64_t w_tcp;
+ uint64_t origin_point;
+ ngtcp2_tstamp epoch_start;
+ uint64_t k;
+ /* prior stores the congestion state when a congestion event occurs
+ in order to restore the state when it turns out that the event is
+ spurious. */
+ struct {
+ uint64_t cwnd;
+ uint64_t ssthresh;
+ uint64_t w_last_max;
+ uint64_t w_tcp;
+ uint64_t origin_point;
+ ngtcp2_tstamp epoch_start;
+ uint64_t k;
+ } prior;
+ /* HyStart++ variables */
+ size_t rtt_sample_count;
+ uint64_t current_round_min_rtt;
+ uint64_t last_round_min_rtt;
+ int64_t window_end;
+ uint64_t pending_add;
+ uint64_t pending_w_add;
+} ngtcp2_cubic_cc;
+
+int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem);
+
+void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log);
+
+void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc);
+
+void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp sent_ts,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
+ ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_pkt *pkt);
+
+void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ ngtcp2_cc_event_type event, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_CC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c
new file mode 100644
index 0000000..f3b92b5
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c
@@ -0,0 +1,147 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_cid.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_path.h"
+#include "ngtcp2_str.h"
+
+void ngtcp2_cid_zero(ngtcp2_cid *cid) { memset(cid, 0, sizeof(*cid)); }
+
+void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) {
+ assert(datalen <= NGTCP2_MAX_CIDLEN);
+
+ cid->datalen = datalen;
+ if (datalen) {
+ ngtcp2_cpymem(cid->data, data, datalen);
+ }
+}
+
+int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other) {
+ return cid->datalen == other->datalen &&
+ 0 == memcmp(cid->data, other->data, cid->datalen);
+}
+
+int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) {
+ int s = lhs->datalen < rhs->datalen;
+ size_t n = s ? lhs->datalen : rhs->datalen;
+ int c = memcmp(lhs->data, rhs->data, n);
+
+ return c < 0 || (c == 0 && s);
+}
+
+int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; }
+
+void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid) {
+ scid->pe.index = NGTCP2_PQ_BAD_INDEX;
+ scid->seq = seq;
+ scid->cid = *cid;
+ scid->retired_ts = UINT64_MAX;
+ scid->flags = NGTCP2_SCID_FLAG_NONE;
+}
+
+void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) {
+ ngtcp2_scid_init(dest, src->seq, &src->cid);
+ dest->retired_ts = src->retired_ts;
+ dest->flags = src->flags;
+}
+
+void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token) {
+ dcid->seq = seq;
+ dcid->cid = *cid;
+ if (token) {
+ memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ dcid->flags = NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ } else {
+ dcid->flags = NGTCP2_DCID_FLAG_NONE;
+ }
+ ngtcp2_path_storage_zero(&dcid->ps);
+ dcid->retired_ts = UINT64_MAX;
+ dcid->bound_ts = UINT64_MAX;
+ dcid->bytes_sent = 0;
+ dcid->bytes_recv = 0;
+ dcid->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+}
+
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token) {
+ assert(token);
+
+ dcid->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+}
+
+void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path) {
+ ngtcp2_path_copy(&dcid->ps.path, path);
+}
+
+void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
+ ngtcp2_dcid_init(dest, src->seq, &src->cid,
+ (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? src->token
+ : NULL);
+ ngtcp2_path_copy(&dest->ps.path, &src->ps.path);
+ dest->retired_ts = src->retired_ts;
+ dest->bound_ts = src->bound_ts;
+ dest->flags = src->flags;
+ dest->bytes_sent = src->bytes_sent;
+ dest->bytes_recv = src->bytes_recv;
+ dest->max_udp_payload_size = src->max_udp_payload_size;
+}
+
+void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) {
+ dest->seq = src->seq;
+ dest->cid = src->cid;
+ if (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
+ dest->flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ } else if (dest->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) {
+ dest->flags &= (uint8_t)~NGTCP2_DCID_FLAG_TOKEN_PRESENT;
+ }
+}
+
+int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
+ const ngtcp2_cid *cid, const uint8_t *token) {
+ if (dcid->seq == seq) {
+ return ngtcp2_cid_eq(&dcid->cid, cid) &&
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
+ memcmp(dcid->token, token,
+ NGTCP2_STATELESS_RESET_TOKENLEN) == 0
+ ? 0
+ : NGTCP2_ERR_PROTO;
+ }
+
+ return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO;
+}
+
+int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
+ const uint8_t *token) {
+ return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) &&
+ ngtcp2_cmemeq(dcid->token, token,
+ NGTCP2_STATELESS_RESET_TOKENLEN)
+ ? 0
+ : NGTCP2_ERR_INVALID_ARGUMENT;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h
new file mode 100644
index 0000000..0b37441
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.h
@@ -0,0 +1,175 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CID_H
+#define NGTCP2_CID_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pq.h"
+#include "ngtcp2_path.h"
+
+/* NGTCP2_SCID_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_SCID_FLAG_NONE 0x00u
+/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that
+ a remote endpoint uses a particular Connection ID. */
+#define NGTCP2_SCID_FLAG_USED 0x01u
+/* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID
+ is retired. */
+#define NGTCP2_SCID_FLAG_RETIRED 0x02u
+
+typedef struct ngtcp2_scid {
+ ngtcp2_pq_entry pe;
+ /* seq is the sequence number associated to the CID. */
+ uint64_t seq;
+ /* cid is a connection ID */
+ ngtcp2_cid cid;
+ /* retired_ts is the timestamp when peer tells that this CID is
+ retired. */
+ ngtcp2_tstamp retired_ts;
+ /* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */
+ uint8_t flags;
+} ngtcp2_scid;
+
+/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_DCID_FLAG_NONE 0x00u
+/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path
+ has been validated. */
+#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01u
+/* NGTCP2_DCID_FLAG_TOKEN_PRESENT indicates that a stateless reset
+ token is set in token field. */
+#define NGTCP2_DCID_FLAG_TOKEN_PRESENT 0x02u
+
+typedef struct ngtcp2_dcid {
+ /* seq is the sequence number associated to the CID. */
+ uint64_t seq;
+ /* cid is a connection ID */
+ ngtcp2_cid cid;
+ /* path is a path which cid is bound to. The addresses are zero
+ length if cid has not been bound to a particular path yet. */
+ ngtcp2_path_storage ps;
+ /* retired_ts is the timestamp when peer tells that this CID is
+ retired. */
+ ngtcp2_tstamp retired_ts;
+ /* bound_ts is the timestamp when this connection ID is bound to a
+ particular path. It is only assigned when a connection ID is
+ used just for sending PATH_RESPONSE and is not zero-length. */
+ ngtcp2_tstamp bound_ts;
+ /* bytes_sent is the number of bytes sent to an associated path. */
+ uint64_t bytes_sent;
+ /* bytes_recv is the number of bytes received from an associated
+ path. */
+ uint64_t bytes_recv;
+ /* max_udp_payload_size is the maximum size of UDP payload that is
+ allowed to send to this path. */
+ size_t max_udp_payload_size;
+ /* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */
+ uint8_t flags;
+ /* token is a stateless reset token associated to this CID.
+ Actually, the stateless reset token is tied to the connection,
+ not to the particular connection ID. */
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_dcid;
+
+/* ngtcp2_cid_zero makes |cid| zero-length. */
+void ngtcp2_cid_zero(ngtcp2_cid *cid);
+
+/*
+ * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller
+ * than |rhs|.
+ */
+int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs);
+
+/*
+ * ngtcp2_cid_empty returns nonzero if |cid| includes empty connection
+ * ID.
+ */
+int ngtcp2_cid_empty(const ngtcp2_cid *cid);
+
+/*
+ * ngtcp2_scid_init initializes |scid| with the given parameters.
+ */
+void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid);
+
+/*
+ * ngtcp2_scid_copy copies |src| into |dest|.
+ */
+void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src);
+
+/*
+ * ngtcp2_dcid_init initializes |dcid| with the given parameters. If
+ * |token| is NULL, the function fills dcid->token it with 0. |token|
+ * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ */
+void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token);
+
+/*
+ * ngtcp2_dcid_set_token sets |token| to |dcid|. |token| must not be
+ * NULL and must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long.
+ */
+void ngtcp2_dcid_set_token(ngtcp2_dcid *dcid, const uint8_t *token);
+
+/*
+ * ngtcp2_dcid_set_path sets |path| to |dcid|. It sets
+ * max_udp_payload_size to the minimum UDP payload size supported
+ * by the IP protocol version.
+ */
+void ngtcp2_dcid_set_path(ngtcp2_dcid *dcid, const ngtcp2_path *path);
+
+/*
+ * ngtcp2_dcid_copy copies |src| into |dest|.
+ */
+void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
+
+/*
+ * ngtcp2_dcid_copy_cid_token behaves like ngtcp2_dcid_copy, but it
+ * only copies cid, seq, and path.
+ */
+void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src);
+
+/*
+ * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|,
+ * |token|) tuple against |dcid|.
+ */
+int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq,
+ const ngtcp2_cid *cid, const uint8_t *token);
+
+/*
+ * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset
+ * token |token| against the one included in |dcid|. This function
+ * returns 0 if the verification succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Tokens do not match; or |dcid| does not contain a token.
+ */
+int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid,
+ const uint8_t *token);
+
+#endif /* NGTCP2_CID_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
new file mode 100644
index 0000000..fb00ca8
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
@@ -0,0 +1,13698 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conn.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_addr.h"
+#include "ngtcp2_path.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_unreachable.h"
+#include "ngtcp2_net.h"
+
+/* NGTCP2_FLOW_WINDOW_RTT_FACTOR is the factor of RTT when flow
+ control window auto-tuning is triggered. */
+#define NGTCP2_FLOW_WINDOW_RTT_FACTOR 2
+/* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow
+ control window. */
+#define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2
+/* NGTCP2_MIN_COALESCED_PAYLOADLEN is the minimum length of QUIC
+ packet payload that should be coalesced to a long packet. */
+#define NGTCP2_MIN_COALESCED_PAYLOADLEN 128
+
+/*
+ * conn_local_stream returns nonzero if |stream_id| indicates that it
+ * is the stream initiated by local endpoint.
+ */
+static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ return (uint8_t)(stream_id & 1) == conn->server;
+}
+
+/*
+ * bidi_stream returns nonzero if |stream_id| is a bidirectional
+ * stream ID.
+ */
+static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; }
+
+/*
+ * conn_is_handshake_completed returns nonzero if QUIC handshake has
+ * completed.
+ */
+static int conn_is_handshake_completed(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+ conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm;
+}
+
+static int conn_call_recv_client_initial(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid) {
+ int rv;
+
+ assert(conn->callbacks.recv_client_initial);
+
+ rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_handshake_completed(ngtcp2_conn *conn) {
+ int rv;
+
+ if (!conn->callbacks.handshake_completed) {
+ return 0;
+ }
+
+ rv = conn->callbacks.handshake_completed(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint32_t flags, uint64_t offset,
+ const uint8_t *data, size_t datalen) {
+ int rv;
+
+ if (!conn->callbacks.recv_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_stream_data(conn, flags, strm->stream_id, offset,
+ data, datalen, conn->user_data,
+ strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+
+ assert(conn->callbacks.recv_crypto_data);
+
+ rv = conn->callbacks.recv_crypto_data(conn, crypto_level, offset, data,
+ datalen, conn->user_data);
+ switch (rv) {
+ case 0:
+ case NGTCP2_ERR_CRYPTO:
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ case NGTCP2_ERR_PROTO:
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ case NGTCP2_ERR_NOMEM:
+ case NGTCP2_ERR_CALLBACK_FAILURE:
+ return rv;
+ default:
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+}
+
+static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ int rv;
+
+ if (!conn->callbacks.stream_open) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_open(conn, strm->stream_id, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ int rv;
+ uint32_t flags = NGTCP2_STREAM_CLOSE_FLAG_NONE;
+
+ if (!conn->callbacks.stream_close) {
+ return 0;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) {
+ flags |= NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET;
+ }
+
+ rv = conn->callbacks.stream_close(conn, flags, strm->stream_id,
+ strm->app_error_code, conn->user_data,
+ strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *stream_user_data) {
+ int rv;
+
+ if (!conn->callbacks.stream_reset) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_reset(conn, stream_id, final_size, app_error_code,
+ conn->user_data, stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_local_streams_bidi) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_local_streams_bidi(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_local_streams_uni) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_local_streams_uni(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen) {
+ int rv;
+
+ assert(conn->callbacks.get_new_connection_id);
+
+ rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_remove_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_cid *cid) {
+ int rv;
+
+ if (!conn->callbacks.remove_connection_id) {
+ return 0;
+ }
+
+ rv = conn->callbacks.remove_connection_id(conn, cid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv,
+ ngtcp2_path_validation_result res) {
+ int rv;
+ uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE;
+
+ if (!conn->callbacks.path_validation) {
+ return 0;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) {
+ flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR;
+ }
+
+ rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, res,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_select_preferred_addr(ngtcp2_conn *conn,
+ ngtcp2_path *dest) {
+ int rv;
+
+ if (!conn->callbacks.select_preferred_addr) {
+ return 0;
+ }
+
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->preferred_address_present);
+
+ rv = conn->callbacks.select_preferred_addr(
+ conn, dest, &conn->remote.transport_params->preferred_address,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_remote_streams_bidi(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_remote_streams_bidi) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_remote_streams_bidi(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_remote_streams_uni(ngtcp2_conn *conn,
+ uint64_t max_streams) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_remote_streams_uni) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_remote_streams_uni(conn, max_streams,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_extend_max_stream_data(ngtcp2_conn *conn,
+ ngtcp2_strm *strm,
+ int64_t stream_id,
+ uint64_t datalen) {
+ int rv;
+
+ if (!conn->callbacks.extend_max_stream_data) {
+ return 0;
+ }
+
+ rv = conn->callbacks.extend_max_stream_data(
+ conn, stream_id, datalen, conn->user_data, strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_dcid_status(ngtcp2_conn *conn,
+ ngtcp2_connection_id_status_type type,
+ const ngtcp2_dcid *dcid) {
+ int rv;
+
+ if (!conn->callbacks.dcid_status) {
+ return 0;
+ }
+
+ rv = conn->callbacks.dcid_status(
+ conn, (int)type, dcid->seq, &dcid->cid,
+ (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) ? dcid->token : NULL,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) {
+ return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE,
+ dcid);
+}
+
+static int conn_call_deactivate_dcid(ngtcp2_conn *conn,
+ const ngtcp2_dcid *dcid) {
+ return conn_call_dcid_status(
+ conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid);
+}
+
+static int conn_call_stream_stop_sending(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *stream_user_data) {
+ int rv;
+
+ if (!conn->callbacks.stream_stop_sending) {
+ return 0;
+ }
+
+ rv = conn->callbacks.stream_stop_sending(conn, stream_id, app_error_code,
+ conn->user_data, stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (!aead_ctx->native_handle) {
+ return;
+ }
+
+ assert(conn->callbacks.delete_crypto_aead_ctx);
+
+ conn->callbacks.delete_crypto_aead_ctx(conn, aead_ctx, conn->user_data);
+}
+
+static void
+conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn,
+ ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (!cipher_ctx->native_handle) {
+ return;
+ }
+
+ assert(conn->callbacks.delete_crypto_cipher_ctx);
+
+ conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data);
+}
+
+static int conn_call_client_initial(ngtcp2_conn *conn) {
+ int rv;
+
+ assert(conn->callbacks.client_initial);
+
+ rv = conn->callbacks.client_initial(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data) {
+ int rv;
+
+ assert(conn->callbacks.get_path_challenge_data);
+
+ rv = conn->callbacks.get_path_challenge_data(conn, data, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_version_negotiation(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv) {
+ int rv;
+
+ if (!conn->callbacks.recv_version_negotiation) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_version_negotiation(conn, hd, sv, nsv,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) {
+ int rv;
+
+ assert(conn->callbacks.recv_retry);
+
+ rv = conn->callbacks.recv_retry(conn, hd, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int
+conn_call_recv_stateless_reset(ngtcp2_conn *conn,
+ const ngtcp2_pkt_stateless_reset *sr) {
+ int rv;
+
+ if (!conn->callbacks.recv_stateless_reset) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_stateless_reset(conn, sr, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_new_token(ngtcp2_conn *conn, const uint8_t *token,
+ size_t tokenlen) {
+ int rv;
+
+ if (!conn->callbacks.recv_new_token) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_new_token(conn, token, tokenlen, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_handshake_confirmed(ngtcp2_conn *conn) {
+ int rv;
+
+ if (!conn->callbacks.handshake_confirmed) {
+ return 0;
+ }
+
+ rv = conn->callbacks.handshake_confirmed(conn, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_datagram(ngtcp2_conn *conn,
+ const ngtcp2_datagram *fr) {
+ const uint8_t *data;
+ size_t datalen;
+ int rv;
+ uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE;
+
+ if (!conn->callbacks.recv_datagram) {
+ return 0;
+ }
+
+ if (fr->datacnt) {
+ assert(fr->datacnt == 1);
+
+ data = fr->data->base;
+ datalen = fr->data->len;
+ } else {
+ data = NULL;
+ datalen = 0;
+ }
+
+ if (!conn_is_handshake_completed(conn)) {
+ flags |= NGTCP2_DATAGRAM_FLAG_EARLY;
+ }
+
+ rv = conn->callbacks.recv_datagram(conn, flags, data, datalen,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int
+conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret,
+ const uint8_t *current_tx_secret, size_t secretlen) {
+ int rv;
+
+ assert(conn->callbacks.update_key);
+
+ rv = conn->callbacks.update_key(
+ conn, rx_secret, tx_secret, rx_aead_ctx, rx_iv, tx_aead_ctx, tx_iv,
+ current_rx_secret, current_tx_secret, secretlen, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *dcid) {
+ int rv;
+
+ assert(conn->callbacks.version_negotiation);
+
+ rv =
+ conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_rx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) {
+ int rv;
+
+ if (!conn->callbacks.recv_rx_key) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_rx_key(conn, level, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int conn_call_recv_tx_key(ngtcp2_conn *conn, ngtcp2_crypto_level level) {
+ int rv;
+
+ if (!conn->callbacks.recv_tx_key) {
+ return 0;
+ }
+
+ rv = conn->callbacks.recv_tx_key(conn, level, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs < *(int64_t *)rhs;
+}
+
+static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
+ ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
+ int rv;
+
+ memset(pktns, 0, sizeof(*pktns));
+
+ ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
+
+ pktns->tx.last_pkt_num = -1;
+ pktns->rx.max_pkt_num = -1;
+ pktns->rx.max_ack_eliciting_pkt_num = -1;
+
+ rv = ngtcp2_acktr_init(&pktns->acktr, log, mem);
+ if (rv != 0) {
+ goto fail_acktr_init;
+ }
+
+ ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL,
+ NULL, mem);
+
+ ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t),
+ mem);
+
+ ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log,
+ qlog, rtb_entry_objalloc, frc_objalloc, mem);
+
+ return 0;
+
+fail_acktr_init:
+ ngtcp2_gaptr_free(&pktns->rx.pngap);
+
+ return rv;
+}
+
+static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
+ ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
+ ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
+ int rv;
+
+ *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
+ if (*ppktns == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc,
+ frc_objalloc, mem);
+ if (rv != 0) {
+ ngtcp2_mem_free(mem, *ppktns);
+ }
+
+ return rv;
+}
+
+static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) {
+ ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe);
+ ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe);
+
+ if (ls->cycle == rs->cycle) {
+ return ls->stream_id < rs->stream_id;
+ }
+
+ return rs->cycle - ls->cycle <= 1;
+}
+
+static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
+ ngtcp2_pkt_chain *next;
+
+ for (; pc;) {
+ next = pc->next;
+ ngtcp2_pkt_chain_del(pc, mem);
+ pc = next;
+ }
+}
+
+static void delete_buf_chain(ngtcp2_buf_chain *bufchain,
+ const ngtcp2_mem *mem) {
+ ngtcp2_buf_chain *next;
+
+ for (; bufchain;) {
+ next = bufchain->next;
+ ngtcp2_buf_chain_del(bufchain, mem);
+ bufchain = next;
+ }
+}
+
+static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ delete_buf_chain(pktns->crypto.tx.data, mem);
+
+ delete_buffed_pkts(pktns->rx.buffed_pkts, mem);
+
+ ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc,
+ mem);
+
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem);
+
+ for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem);
+ }
+
+ ngtcp2_ksl_free(&pktns->crypto.tx.frq);
+ ngtcp2_rtb_free(&pktns->rtb);
+ ngtcp2_strm_free(&pktns->crypto.strm);
+ ngtcp2_acktr_free(&pktns->acktr);
+ ngtcp2_gaptr_free(&pktns->rx.pngap);
+}
+
+static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
+ if (pktns == NULL) {
+ return;
+ }
+
+ pktns_free(pktns, mem);
+
+ ngtcp2_mem_free(mem, pktns);
+}
+
+static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo,
+ const ngtcp2_mem *mem) {
+ switch (cc_algo) {
+ case NGTCP2_CC_ALGO_RENO:
+ ngtcp2_cc_reno_cc_free(cc, mem);
+ break;
+ case NGTCP2_CC_ALGO_CUBIC:
+ ngtcp2_cc_cubic_cc_free(cc, mem);
+ break;
+ case NGTCP2_CC_ALGO_BBR:
+ ngtcp2_cc_bbr_cc_free(cc, mem);
+ break;
+ case NGTCP2_CC_ALGO_BBR2:
+ ngtcp2_cc_bbr2_cc_free(cc, mem);
+ break;
+ default:
+ break;
+ }
+}
+
+static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return ngtcp2_cid_less(lhs, rhs);
+}
+
+static int retired_ts_less(const ngtcp2_pq_entry *lhs,
+ const ngtcp2_pq_entry *rhs) {
+ const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe);
+ const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe);
+
+ return a->retired_ts < b->retired_ts;
+}
+
+/*
+ * conn_reset_conn_stat_cc resets congestion state in |cstat|.
+ */
+static void conn_reset_conn_stat_cc(ngtcp2_conn *conn,
+ ngtcp2_conn_stat *cstat) {
+ cstat->latest_rtt = 0;
+ cstat->min_rtt = UINT64_MAX;
+ cstat->smoothed_rtt = conn->local.settings.initial_rtt;
+ cstat->rttvar = conn->local.settings.initial_rtt / 2;
+ cstat->first_rtt_sample_ts = UINT64_MAX;
+ cstat->pto_count = 0;
+ cstat->loss_detection_timer = UINT64_MAX;
+ cstat->cwnd =
+ ngtcp2_cc_compute_initcwnd(conn->local.settings.max_tx_udp_payload_size);
+ cstat->ssthresh = UINT64_MAX;
+ cstat->congestion_recovery_start_ts = UINT64_MAX;
+ cstat->bytes_in_flight = 0;
+ cstat->delivery_rate_sec = 0;
+ cstat->pacing_rate = 0.0;
+ cstat->send_quantum = 64 * 1024;
+}
+
+/*
+ * reset_conn_stat_recovery resets the fields related to the recovery
+ * function
+ */
+static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) {
+ /* Initializes them with UINT64_MAX. */
+ memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time));
+ memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts));
+}
+
+/*
+ * conn_reset_conn_stat resets |cstat|. The following fields are not
+ * reset: initial_rtt and max_udp_payload_size.
+ */
+static void conn_reset_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) {
+ conn_reset_conn_stat_cc(conn, cstat);
+ reset_conn_stat_recovery(cstat);
+}
+
+static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) {
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_ksl_begin(scids); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_mem_free(mem, ngtcp2_ksl_it_get(&it));
+ }
+}
+
+/*
+ * compute_pto computes PTO.
+ */
+static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt,
+ ngtcp2_duration rttvar,
+ ngtcp2_duration max_ack_delay) {
+ ngtcp2_duration var = ngtcp2_max(4 * rttvar, NGTCP2_GRANULARITY);
+ return smoothed_rtt + var + max_ack_delay;
+}
+
+/*
+ * conn_compute_initial_pto computes PTO using the initial RTT.
+ */
+static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt;
+ ngtcp2_duration max_ack_delay;
+
+ if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION &&
+ conn->remote.transport_params) {
+ max_ack_delay = conn->remote.transport_params->max_ack_delay;
+ } else {
+ max_ack_delay = 0;
+ }
+ return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay);
+}
+
+/*
+ * conn_compute_pto computes the current PTO.
+ */
+static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_duration max_ack_delay;
+
+ if (pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION &&
+ conn->remote.transport_params) {
+ max_ack_delay = conn->remote.transport_params->max_ack_delay;
+ } else {
+ max_ack_delay = 0;
+ }
+ return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay);
+}
+
+ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ return conn_compute_pto(conn, pktns);
+}
+
+static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns,
+ const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) {
+ assert(pi);
+
+ if (pi->ecn != NGTCP2_ECN_NOT_ECT) {
+ /* We have already made a transition of validation state and
+ deceided to send UDP datagram with ECN bit set. Coalesced QUIC
+ packets also bear ECN bits set. */
+ if (pktns->tx.ecn.start_pkt_num == INT64_MAX) {
+ pktns->tx.ecn.start_pkt_num = hd->pkt_num;
+ }
+
+ ++pktns->tx.ecn.validation_pkt_sent;
+
+ if (prtb_entry_flags) {
+ *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN;
+ }
+
+ ++pktns->tx.ecn.ect0;
+
+ return;
+ }
+
+ switch (conn->tx.ecn.state) {
+ case NGTCP2_ECN_STATE_TESTING:
+ if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+ assert(0 == pktns->tx.ecn.validation_pkt_sent);
+ assert(0 == pktns->tx.ecn.validation_pkt_lost);
+
+ conn->tx.ecn.validation_start_ts = ts;
+ } else if (ts - conn->tx.ecn.validation_start_ts >=
+ 3 * conn_compute_pto(conn, pktns)) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ break;
+ }
+
+ if (pktns->tx.ecn.start_pkt_num == INT64_MAX) {
+ pktns->tx.ecn.start_pkt_num = hd->pkt_num;
+ }
+
+ ++pktns->tx.ecn.validation_pkt_sent;
+
+ if (++conn->tx.ecn.dgram_sent == NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ }
+ /* fall through */
+ case NGTCP2_ECN_STATE_CAPABLE:
+ /* pi is provided per UDP datagram. */
+ assert(NGTCP2_ECN_NOT_ECT == pi->ecn);
+
+ pi->ecn = NGTCP2_ECN_ECT_0;
+
+ if (prtb_entry_flags) {
+ *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN;
+ }
+
+ ++pktns->tx.ecn.ect0;
+ break;
+ case NGTCP2_ECN_STATE_UNKNOWN:
+ case NGTCP2_ECN_STATE_FAILED:
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_TESTING;
+ conn->tx.ecn.validation_start_ts = UINT64_MAX;
+ conn->tx.ecn.dgram_sent = 0;
+
+ if (in_pktns) {
+ in_pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ in_pktns->tx.ecn.validation_pkt_sent = 0;
+ in_pktns->tx.ecn.validation_pkt_lost = 0;
+ }
+
+ if (hs_pktns) {
+ hs_pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ hs_pktns->tx.ecn.validation_pkt_sent = 0;
+ hs_pktns->tx.ecn.validation_pkt_lost = 0;
+ }
+
+ pktns->tx.ecn.start_pkt_num = INT64_MAX;
+ pktns->tx.ecn.validation_pkt_sent = 0;
+ pktns->tx.ecn.validation_pkt_lost = 0;
+}
+
+/* server_default_available_versions is the default available_versions
+ field sent by server. */
+static uint8_t server_default_available_versions[] = {0, 0, 0, 1};
+
+/*
+ * available_versions_new allocates new buffer, and writes |versions|
+ * of length |versionslen| in network byte order, suitable for sending
+ * in available_versions field of version_information QUIC transport
+ * parameter. The pointer to the allocated buffer is assigned to
+ * |*pbuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the negative
+ * error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int available_versions_new(uint8_t **pbuf, const uint32_t *versions,
+ size_t versionslen, const ngtcp2_mem *mem) {
+ size_t i;
+ uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen);
+
+ if (buf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ *pbuf = buf;
+
+ for (i = 0; i < versionslen; ++i) {
+ buf = ngtcp2_put_uint32be(buf, versions[i]);
+ }
+
+ return 0;
+}
+
+static void
+conn_set_local_transport_params(ngtcp2_conn *conn,
+ const ngtcp2_transport_params *params) {
+ ngtcp2_transport_params *p = &conn->local.transport_params;
+ uint32_t chosen_version = p->version_info.chosen_version;
+
+ *p = *params;
+
+ /* grease_quic_bit is always enabled. */
+ p->grease_quic_bit = 1;
+
+ if (conn->server) {
+ p->version_info.chosen_version = chosen_version;
+ } else {
+ p->version_info.chosen_version = conn->client_chosen_version;
+ }
+ p->version_info.available_versions = conn->vneg.available_versions;
+ p->version_info.available_versionslen = conn->vneg.available_versionslen;
+ p->version_info_present = 1;
+}
+
+static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_path *path,
+ uint32_t client_chosen_version, int callbacks_version,
+ const ngtcp2_callbacks *callbacks, int settings_version,
+ const ngtcp2_settings *settings,
+ int transport_params_version,
+ const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data, int server) {
+ int rv;
+ ngtcp2_scid *scident;
+ uint8_t *buf;
+ uint8_t fixed_bit_byte;
+ size_t i;
+ uint32_t *preferred_versions;
+ (void)callbacks_version;
+ (void)settings_version;
+ (void)transport_params_version;
+
+ assert(settings->max_window <= NGTCP2_MAX_VARINT);
+ assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
+ assert(settings->max_tx_udp_payload_size);
+ assert(settings->max_tx_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE);
+ assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
+ assert(params->initial_max_data <= NGTCP2_MAX_VARINT);
+ assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT);
+ assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT);
+ assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT);
+ assert(server || callbacks->client_initial);
+ assert(!server || callbacks->recv_client_initial);
+ assert(callbacks->recv_crypto_data);
+ assert(callbacks->encrypt);
+ assert(callbacks->decrypt);
+ assert(callbacks->hp_mask);
+ assert(server || callbacks->recv_retry);
+ assert(callbacks->rand);
+ assert(callbacks->get_new_connection_id);
+ assert(callbacks->update_key);
+ assert(callbacks->delete_crypto_aead_ctx);
+ assert(callbacks->delete_crypto_cipher_ctx);
+ assert(callbacks->get_path_challenge_data);
+ assert(!server || !ngtcp2_is_reserved_version(client_chosen_version));
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ *pconn = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_conn));
+ if (*pconn == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_conn;
+ }
+
+ ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem);
+ ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem);
+ ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem);
+
+ ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound);
+
+ ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused);
+
+ ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired);
+
+ ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
+
+ ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
+
+ ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem);
+
+ ngtcp2_map_init(&(*pconn)->strms, mem);
+
+ ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem);
+
+ ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem);
+
+ ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
+
+ ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge);
+
+ ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf,
+ settings->initial_ts, user_data);
+ ngtcp2_qlog_init(&(*pconn)->qlog, settings->qlog.write, settings->initial_ts,
+ user_data);
+ if ((*pconn)->qlog.write) {
+ buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN);
+ if (buf == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_qlog_buf;
+ }
+ ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN);
+ }
+
+ (*pconn)->local.settings = *settings;
+
+ if (settings->tokenlen) {
+ buf = ngtcp2_mem_malloc(mem, settings->tokenlen);
+ if (buf == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_token;
+ }
+ memcpy(buf, settings->token, settings->tokenlen);
+ (*pconn)->local.settings.token = buf;
+ } else {
+ (*pconn)->local.settings.token = NULL;
+ }
+
+ if (!(*pconn)->local.settings.original_version) {
+ (*pconn)->local.settings.original_version = client_chosen_version;
+ }
+
+ conn_reset_conn_stat(*pconn, &(*pconn)->cstat);
+ (*pconn)->cstat.initial_rtt = settings->initial_rtt;
+ (*pconn)->cstat.max_tx_udp_payload_size =
+ (*pconn)->local.settings.max_tx_udp_payload_size;
+
+ ngtcp2_rst_init(&(*pconn)->rst);
+
+ (*pconn)->cc_algo = settings->cc_algo;
+
+ switch (settings->cc_algo) {
+ case NGTCP2_CC_ALGO_RENO:
+ rv = ngtcp2_cc_reno_cc_init(&(*pconn)->cc, &(*pconn)->log, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_CUBIC:
+ rv = ngtcp2_cc_cubic_cc_init(&(*pconn)->cc, &(*pconn)->log, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_BBR:
+ rv = ngtcp2_cc_bbr_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat,
+ &(*pconn)->rst, settings->initial_ts,
+ callbacks->rand, &settings->rand_ctx, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ case NGTCP2_CC_ALGO_BBR2:
+ rv = ngtcp2_cc_bbr2_cc_init(&(*pconn)->cc, &(*pconn)->log, &(*pconn)->cstat,
+ &(*pconn)->rst, settings->initial_ts,
+ callbacks->rand, &settings->rand_ctx, mem);
+ if (rv != 0) {
+ goto fail_cc_init;
+ }
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
+ if (rv != 0) {
+ goto fail_in_pktns_init;
+ }
+
+ rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst,
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
+ if (rv != 0) {
+ goto fail_hs_pktns_init;
+ }
+
+ rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst,
+ &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+ &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
+ if (rv != 0) {
+ goto fail_pktns_init;
+ }
+
+ scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
+ if (scident == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_scident;
+ }
+
+ /* Set stateless reset token later if it is available in the local
+ transport parameters */
+ ngtcp2_scid_init(scident, 0, scid);
+
+ rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident);
+ if (rv != 0) {
+ goto fail_scid_set_insert;
+ }
+
+ scident = NULL;
+
+ ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL);
+ ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path);
+
+ rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1);
+ if (rv != 0) {
+ goto fail_seqgap_push;
+ }
+
+ if (settings->preferred_versionslen) {
+ if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ for (i = 0; i < settings->preferred_versionslen; ++i) {
+ if (settings->preferred_versions[i] == client_chosen_version) {
+ break;
+ }
+ }
+
+ assert(i < settings->preferred_versionslen);
+ }
+
+ preferred_versions = ngtcp2_mem_malloc(
+ mem, sizeof(uint32_t) * settings->preferred_versionslen);
+ if (preferred_versions == NULL) {
+ rv = NGTCP2_ERR_NOMEM;
+ goto fail_preferred_versions;
+ }
+
+ for (i = 0; i < settings->preferred_versionslen; ++i) {
+ assert(ngtcp2_is_supported_version(settings->preferred_versions[i]));
+
+ preferred_versions[i] = settings->preferred_versions[i];
+ }
+
+ (*pconn)->vneg.preferred_versions = preferred_versions;
+ (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen;
+ }
+
+ if (settings->available_versionslen) {
+ if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ for (i = 0; i < settings->available_versionslen; ++i) {
+ if (settings->available_versions[i] == client_chosen_version) {
+ break;
+ }
+ }
+
+ assert(i < settings->available_versionslen);
+ }
+
+ for (i = 0; i < settings->available_versionslen; ++i) {
+ assert(ngtcp2_is_reserved_version(settings->available_versions[i]) ||
+ ngtcp2_is_supported_version(settings->available_versions[i]));
+ }
+
+ rv = available_versions_new(&buf, settings->available_versions,
+ settings->available_versionslen, mem);
+ if (rv != 0) {
+ goto fail_available_versions;
+ }
+
+ (*pconn)->vneg.available_versions = buf;
+ (*pconn)->vneg.available_versionslen =
+ sizeof(uint32_t) * settings->available_versionslen;
+ } else if (server) {
+ if (settings->preferred_versionslen) {
+ rv = available_versions_new(&buf, settings->preferred_versions,
+ settings->preferred_versionslen, mem);
+ if (rv != 0) {
+ goto fail_available_versions;
+ }
+
+ (*pconn)->vneg.available_versions = buf;
+ (*pconn)->vneg.available_versionslen =
+ sizeof(uint32_t) * settings->preferred_versionslen;
+ } else {
+ (*pconn)->vneg.available_versions = server_default_available_versions;
+ (*pconn)->vneg.available_versionslen =
+ sizeof(server_default_available_versions);
+ }
+ } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+ rv = available_versions_new(&buf, &client_chosen_version, 1, mem);
+ if (rv != 0) {
+ goto fail_available_versions;
+ }
+
+ (*pconn)->vneg.available_versions = buf;
+ (*pconn)->vneg.available_versionslen = sizeof(uint32_t);
+ }
+
+ (*pconn)->client_chosen_version = client_chosen_version;
+
+ conn_set_local_transport_params(*pconn, params);
+
+ callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx);
+ if (fixed_bit_byte & 1) {
+ (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT;
+ }
+
+ (*pconn)->keep_alive.last_ts = UINT64_MAX;
+
+ (*pconn)->server = server;
+ (*pconn)->oscid = *scid;
+ (*pconn)->callbacks = *callbacks;
+ (*pconn)->mem = mem;
+ (*pconn)->user_data = user_data;
+ (*pconn)->idle_ts = settings->initial_ts;
+ (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX;
+ (*pconn)->tx.last_max_data_ts = UINT64_MAX;
+ (*pconn)->tx.pacing.next_ts = UINT64_MAX;
+ (*pconn)->early.discard_started_ts = UINT64_MAX;
+
+ conn_reset_ecn_validation_state(*pconn);
+
+ ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid,
+ server);
+
+ return 0;
+
+fail_available_versions:
+ ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions);
+fail_preferred_versions:
+fail_seqgap_push:
+fail_scid_set_insert:
+ ngtcp2_mem_free(mem, scident);
+fail_scident:
+ pktns_free(&(*pconn)->pktns, mem);
+fail_pktns_init:
+ pktns_del((*pconn)->hs_pktns, mem);
+fail_hs_pktns_init:
+ pktns_del((*pconn)->in_pktns, mem);
+fail_in_pktns_init:
+ cc_del(&(*pconn)->cc, settings->cc_algo, mem);
+fail_cc_init:
+ ngtcp2_mem_free(mem, (uint8_t *)(*pconn)->local.settings.token);
+fail_token:
+ ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin);
+fail_qlog_buf:
+ ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr);
+ ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr);
+ ngtcp2_map_free(&(*pconn)->strms);
+ delete_scid(&(*pconn)->scid.set, mem);
+ ngtcp2_ksl_free(&(*pconn)->scid.set);
+ ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap);
+ ngtcp2_objalloc_free(&(*pconn)->strm_objalloc);
+ ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&(*pconn)->frc_objalloc);
+ ngtcp2_mem_free(mem, *pconn);
+fail_conn:
+ return rv;
+}
+
+int ngtcp2_conn_client_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+ callbacks_version, callbacks, settings_version, settings,
+ transport_params_version, params, mem, user_data, 0);
+ if (rv != 0) {
+ return rv;
+ }
+ (*pconn)->rcid = *dcid;
+ (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL;
+ (*pconn)->local.bidi.next_stream_id = 0;
+ (*pconn)->local.uni.next_stream_id = 2;
+
+ rv = ngtcp2_conn_commit_local_transport_params(*pconn);
+ if (rv != 0) {
+ ngtcp2_conn_del(*pconn);
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_server_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data) {
+ int rv;
+
+ rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+ callbacks_version, callbacks, settings_version, settings,
+ transport_params_version, params, mem, user_data, 1);
+ if (rv != 0) {
+ return rv;
+ }
+
+ (*pconn)->state = NGTCP2_CS_SERVER_INITIAL;
+ (*pconn)->local.bidi.next_stream_id = 1;
+ (*pconn)->local.uni.next_stream_id = 3;
+
+ if ((*pconn)->local.settings.tokenlen) {
+ /* Usage of token lifts amplification limit */
+ (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_fc_credits returns the number of bytes allowed to be sent to
+ * the given stream. Both connection and stream level flow control
+ * credits are considered.
+ */
+static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ return ngtcp2_min(strm->tx.max_offset - strm->tx.offset,
+ conn->tx.max_offset - conn->tx.offset);
+}
+
+/*
+ * conn_enforce_flow_control returns the number of bytes allowed to be
+ * sent to the given stream. |len| might be shorted because of
+ * available flow control credits.
+ */
+static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t len) {
+ uint64_t fc_credits = conn_fc_credits(conn, strm);
+ return ngtcp2_min(len, fc_credits);
+}
+
+static int delete_strms_each(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_strm *s = data;
+
+ ngtcp2_strm_free(s);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
+
+ return 0;
+}
+
+static void conn_vneg_crypto_free(ngtcp2_conn *conn) {
+ if (conn->vneg.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+
+ if (conn->vneg.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+
+ ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+}
+
+void ngtcp2_conn_del(ngtcp2_conn *conn) {
+ if (conn == NULL) {
+ return;
+ }
+
+ ngtcp2_qlog_end(&conn->qlog);
+
+ if (conn->early.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx);
+
+ if (conn->crypto.key_update.old_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx);
+ }
+ if (conn->crypto.key_update.new_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.new_rx_ckm->aead_ctx);
+ }
+ if (conn->crypto.key_update.new_tx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.new_tx_ckm->aead_ctx);
+ }
+
+ if (conn->pktns.crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn,
+ &conn->pktns.crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.rx.hp_ctx);
+
+ if (conn->pktns.crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn,
+ &conn->pktns.crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.tx.hp_ctx);
+
+ if (conn->hs_pktns) {
+ if (conn->hs_pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->hs_pktns->crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.rx.hp_ctx);
+
+ if (conn->hs_pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->hs_pktns->crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.tx.hp_ctx);
+ }
+ if (conn->in_pktns) {
+ if (conn->in_pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->in_pktns->crypto.rx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.rx.hp_ctx);
+
+ if (conn->in_pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->in_pktns->crypto.tx.ckm->aead_ctx);
+ }
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.tx.hp_ctx);
+ }
+
+ conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx);
+
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+ ngtcp2_transport_params_del(conn->remote.pending_transport_params, conn->mem);
+
+ conn_vneg_crypto_free(conn);
+
+ ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions);
+ if (conn->vneg.available_versions != server_default_available_versions) {
+ ngtcp2_mem_free(conn->mem, conn->vneg.available_versions);
+ }
+
+ ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base);
+ ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base);
+ ngtcp2_mem_free(conn->mem, (uint8_t *)conn->local.settings.token);
+
+ ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem);
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+
+ pktns_free(&conn->pktns, conn->mem);
+ pktns_del(conn->hs_pktns, conn->mem);
+ pktns_del(conn->in_pktns, conn->mem);
+
+ cc_del(&conn->cc, conn->cc_algo, conn->mem);
+
+ ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin);
+
+ ngtcp2_pmtud_del(conn->pmtud);
+ ngtcp2_pv_del(conn->pv);
+
+ ngtcp2_mem_free(conn->mem, (uint8_t *)conn->rx.ccerr.reason);
+
+ ngtcp2_idtr_free(&conn->remote.uni.idtr);
+ ngtcp2_idtr_free(&conn->remote.bidi.idtr);
+ ngtcp2_mem_free(conn->mem, conn->tx.ack);
+ ngtcp2_pq_free(&conn->tx.strmq);
+ ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn);
+ ngtcp2_map_free(&conn->strms);
+
+ ngtcp2_pq_free(&conn->scid.used);
+ delete_scid(&conn->scid.set, conn->mem);
+ ngtcp2_ksl_free(&conn->scid.set);
+ ngtcp2_gaptr_free(&conn->dcid.seqgap);
+
+ ngtcp2_objalloc_free(&conn->strm_objalloc);
+ ngtcp2_objalloc_free(&conn->rtb_entry_objalloc);
+ ngtcp2_objalloc_free(&conn->frc_objalloc);
+
+ ngtcp2_mem_free(conn->mem, conn);
+}
+
+/*
+ * conn_ensure_ack_ranges makes sure that conn->tx.ack->ack.ranges can
+ * contain at least |n| additional ngtcp2_ack_range.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_ack_ranges(ngtcp2_conn *conn, size_t n) {
+ ngtcp2_frame *fr;
+ size_t max = conn->tx.max_ack_ranges;
+
+ if (n <= max) {
+ return 0;
+ }
+
+ max *= 2;
+
+ assert(max >= n);
+
+ fr = ngtcp2_mem_realloc(conn->mem, conn->tx.ack,
+ sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_range) * max);
+ if (fr == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ conn->tx.ack = fr;
+ conn->tx.max_ack_ranges = max;
+
+ return 0;
+}
+
+/*
+ * conn_compute_ack_delay computes ACK delay for outgoing protected
+ * ACK.
+ */
+static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) {
+ return ngtcp2_min(conn->local.transport_params.max_ack_delay,
+ conn->cstat.smoothed_rtt / 8);
+}
+
+/*
+ * conn_create_ack_frame creates ACK frame, and assigns its pointer to
+ * |*pfr| if there are any received packets to acknowledge. If there
+ * are no packets to acknowledge, this function returns 0, and |*pfr|
+ * is untouched. The caller is advised to set |*pfr| to NULL before
+ * calling this function, and check it after this function returns.
+ * If |nodelay| is nonzero, delayed ACK timer is ignored.
+ *
+ * The memory for ACK frame is dynamically allocated by this function.
+ * A caller is responsible to free it.
+ *
+ * Call ngtcp2_acktr_commit_ack after a created ACK frame is
+ * successfully serialized into a packet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
+ ngtcp2_pktns *pktns, uint8_t type,
+ ngtcp2_tstamp ts, ngtcp2_duration ack_delay,
+ uint64_t ack_delay_exponent) {
+ /* TODO Measure an actual size of ACK blocks to find the best
+ default value. */
+ const size_t initial_max_ack_ranges = 8;
+ int64_t last_pkt_num;
+ ngtcp2_acktr *acktr = &pktns->acktr;
+ ngtcp2_ack_range *range;
+ ngtcp2_ksl_it it;
+ ngtcp2_acktr_entry *rpkt;
+ ngtcp2_ack *ack;
+ size_t range_idx;
+ ngtcp2_tstamp largest_ack_ts;
+ int rv;
+
+ if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) {
+ ack_delay = 0;
+ }
+
+ if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) {
+ return 0;
+ }
+
+ it = ngtcp2_acktr_get(acktr);
+ if (ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_acktr_commit_ack(acktr);
+ return 0;
+ }
+
+ if (conn->tx.ack == NULL) {
+ conn->tx.ack = ngtcp2_mem_malloc(
+ conn->mem,
+ sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_range) * initial_max_ack_ranges);
+ if (conn->tx.ack == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ conn->tx.max_ack_ranges = initial_max_ack_ranges;
+ }
+
+ ack = &conn->tx.ack->ack;
+
+ if (pktns->rx.ecn.ect0 || pktns->rx.ecn.ect1 || pktns->rx.ecn.ce) {
+ ack->type = NGTCP2_FRAME_ACK_ECN;
+ ack->ecn.ect0 = pktns->rx.ecn.ect0;
+ ack->ecn.ect1 = pktns->rx.ecn.ect1;
+ ack->ecn.ce = pktns->rx.ecn.ce;
+ } else {
+ ack->type = NGTCP2_FRAME_ACK;
+ }
+ ack->rangecnt = 0;
+
+ rpkt = ngtcp2_ksl_it_get(&it);
+
+ if (rpkt->pkt_num == pktns->rx.max_pkt_num) {
+ last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
+ largest_ack_ts = rpkt->tstamp;
+ ack->largest_ack = rpkt->pkt_num;
+ ack->first_ack_range = rpkt->len - 1;
+
+ ngtcp2_ksl_it_next(&it);
+ } else {
+ assert(rpkt->pkt_num < pktns->rx.max_pkt_num);
+
+ last_pkt_num = pktns->rx.max_pkt_num;
+ largest_ack_ts = pktns->rx.max_pkt_ts;
+ ack->largest_ack = pktns->rx.max_pkt_num;
+ ack->first_ack_range = 0;
+ }
+
+ if (type == NGTCP2_PKT_1RTT) {
+ ack->ack_delay_unscaled = ts - largest_ack_ts;
+ ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS /
+ (1ULL << ack_delay_exponent);
+ } else {
+ ack->ack_delay_unscaled = 0;
+ ack->ack_delay = 0;
+ }
+
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ if (ack->rangecnt == NGTCP2_MAX_ACK_RANGES) {
+ break;
+ }
+
+ rpkt = ngtcp2_ksl_it_get(&it);
+
+ range_idx = ack->rangecnt++;
+ rv = conn_ensure_ack_ranges(conn, ack->rangecnt);
+ if (rv != 0) {
+ return rv;
+ }
+ ack = &conn->tx.ack->ack;
+ range = &ack->ranges[range_idx];
+ range->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2);
+ range->len = rpkt->len - 1;
+
+ last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1);
+ }
+
+ /* TODO Just remove entries which cannot fit into a single ACK frame
+ for now. */
+ if (!ngtcp2_ksl_it_end(&it)) {
+ ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it));
+ }
+
+ *pfr = conn->tx.ack;
+
+ return 0;
+}
+
+/*
+ * conn_ppe_write_frame writes |fr| to |ppe|. If |hd_logged| is not
+ * NULL and |*hd_logged| is zero, packet header is logged, and 1 is
+ * assigned to |*hd_logged|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too small.
+ */
+static int conn_ppe_write_frame_hd_log(ngtcp2_conn *conn, ngtcp2_ppe *ppe,
+ int *hd_logged, const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame *fr) {
+ int rv;
+
+ rv = ngtcp2_ppe_encode_frame(ppe, fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ if (hd_logged && !*hd_logged) {
+ *hd_logged = 1;
+ ngtcp2_log_tx_pkt_hd(&conn->log, hd);
+ ngtcp2_qlog_pkt_sent_start(&conn->qlog);
+ }
+
+ ngtcp2_log_tx_fr(&conn->log, hd, fr);
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+
+ return 0;
+}
+
+/*
+ * conn_ppe_write_frame writes |fr| to |ppe|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too small.
+ */
+static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe,
+ const ngtcp2_pkt_hd *hd, ngtcp2_frame *fr) {
+ return conn_ppe_write_frame_hd_log(conn, ppe, NULL, hd, fr);
+}
+
+/*
+ * conn_on_pkt_sent is called when new non-ACK-only packet is sent.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb,
+ ngtcp2_rtb_entry *ent) {
+ int rv;
+
+ /* This function implements OnPacketSent, but it handles only
+ non-ACK-only packet. */
+ rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ conn->cstat.last_tx_pkt_ts[rtb->pktns_id] = ent->ts;
+ }
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ent->ts);
+
+ return 0;
+}
+
+/*
+ * pktns_select_pkt_numlen selects shortest packet number encoding for
+ * the next packet number based on the largest acknowledged packet
+ * number. It returns the number of bytes to encode the packet
+ * number.
+ */
+static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
+ int64_t pkt_num = pktns->tx.last_pkt_num + 1;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num;
+
+ if (NGTCP2_MAX_PKT_NUM / 2 < n) {
+ return 4;
+ }
+
+ n = n * 2 - 1;
+
+ if (n > 0xffffff) {
+ return 4;
+ }
+ if (n > 0xffff) {
+ return 3;
+ }
+ if (n > 0xff) {
+ return 2;
+ }
+ return 1;
+}
+
+/*
+ * conn_get_cwnd returns cwnd for the current path.
+ */
+static uint64_t conn_get_cwnd(ngtcp2_conn *conn) {
+ return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
+ ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_tx_udp_payload_size)
+ : conn->cstat.cwnd;
+}
+
+/*
+ * conn_cwnd_is_zero returns nonzero if the number of bytes the local
+ * endpoint can sent at this time is zero.
+ */
+static int conn_cwnd_is_zero(ngtcp2_conn *conn) {
+ uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
+ uint64_t cwnd = conn_get_cwnd(conn);
+
+ if (bytes_in_flight >= cwnd) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "cwnd limited bytes_in_flight=%lu cwnd=%lu",
+ bytes_in_flight, cwnd);
+ }
+
+ return bytes_in_flight >= cwnd;
+}
+
+/*
+ * conn_retry_early_payloadlen returns the estimated wire length of
+ * the first STREAM frame of 0-RTT packet which should be
+ * retransmitted due to Retry packet.
+ */
+static uint64_t conn_retry_early_payloadlen(ngtcp2_conn *conn) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_strm *strm;
+ uint64_t len;
+
+ if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
+ return 0;
+ }
+
+ for (; !ngtcp2_pq_empty(&conn->tx.strmq);) {
+ strm = ngtcp2_conn_tx_strmq_top(conn);
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ frc = ngtcp2_strm_streamfrq_top(strm);
+
+ len = ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) +
+ NGTCP2_STREAM_OVERHEAD;
+
+ /* Take the min because in conn_should_pad_pkt we take max in
+ order to deal with unbreakable DATAGRAM. */
+ return ngtcp2_min(len, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ }
+
+ return 0;
+}
+
+static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ }
+ ngtcp2_ksl_clear(&pktns->crypto.tx.frq);
+}
+
+/*
+ * conn_cryptofrq_unacked_offset returns the CRYPTO frame offset by
+ * taking into account acknowledged offset. If there is no data to
+ * send, this function returns (uint64_t)-1.
+ */
+static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_crypto *fr;
+ ngtcp2_range gap;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ ngtcp2_ksl_it it;
+ uint64_t datalen;
+
+ (void)conn;
+
+ for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.crypto;
+
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->offset);
+
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (gap.begin <= fr->offset) {
+ return fr->offset;
+ }
+ if (gap.begin < fr->offset + datalen) {
+ return gap.begin;
+ }
+ }
+
+ return (uint64_t)-1;
+}
+
+static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain *frc, *nfrc;
+ ngtcp2_crypto *fr, *nfr;
+ uint64_t offset, end_offset;
+ size_t idx, end_idx;
+ uint64_t base_offset, end_base_offset;
+ ngtcp2_range gap;
+ ngtcp2_rtb *rtb = &pktns->rtb;
+ ngtcp2_vec *v;
+ int rv;
+ ngtcp2_ksl_it it;
+
+ *pfrc = NULL;
+
+ for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.crypto;
+
+ ngtcp2_ksl_remove_hint(&pktns->crypto.tx.frq, &it, &it, &fr->offset);
+
+ idx = 0;
+ offset = fr->offset;
+ base_offset = 0;
+
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, offset);
+ if (gap.begin < offset) {
+ gap.begin = offset;
+ }
+
+ for (; idx < fr->datacnt && offset < gap.begin; ++idx) {
+ v = &fr->data[idx];
+ if (offset + v->len > gap.begin) {
+ base_offset = gap.begin - offset;
+ break;
+ }
+
+ offset += v->len;
+ }
+
+ if (idx == fr->datacnt) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+
+ assert(gap.begin == offset + base_offset);
+
+ end_idx = idx;
+ end_offset = offset;
+ end_base_offset = 0;
+
+ for (; end_idx < fr->datacnt; ++end_idx) {
+ v = &fr->data[end_idx];
+ if (end_offset + v->len > gap.end) {
+ end_base_offset = gap.end - end_offset;
+ break;
+ }
+
+ end_offset += v->len;
+ }
+
+ if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) {
+ *pfrc = frc;
+ return 0;
+ }
+
+ if (fr->datacnt == end_idx) {
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ nfr->type = NGTCP2_FRAME_CRYPTO;
+ memcpy(nfr->data, fr->data + end_idx,
+ sizeof(nfr->data[0]) * (fr->datacnt - end_idx));
+
+ assert(nfr->data[0].len > end_base_offset);
+
+ nfr->offset = end_offset + end_base_offset;
+ nfr->datacnt = fr->datacnt - end_idx;
+ nfr->data[0].base += end_base_offset;
+ nfr->data[0].len -= (size_t)end_base_offset;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ if (end_base_offset) {
+ ++end_idx;
+ }
+
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ if (end_base_offset) {
+ assert(fr->data[fr->datacnt - 1].len > end_base_offset);
+ fr->data[fr->datacnt - 1].len = (size_t)end_base_offset;
+ }
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ return 0;
+}
+static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
+ ngtcp2_pktns *pktns, size_t left) {
+ ngtcp2_crypto *fr, *nfr;
+ ngtcp2_frame_chain *frc, *nfrc;
+ int rv;
+ size_t nmerged;
+ uint64_t datalen;
+ ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT];
+ ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT];
+ size_t acnt, bcnt;
+ ngtcp2_ksl_it it;
+
+ rv = conn_cryptofrq_unacked_pop(conn, pktns, &frc);
+ if (rv != 0) {
+ return rv;
+ }
+ if (frc == NULL) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ fr = &frc->fr.crypto;
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (datalen > left) {
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ bcnt = 0;
+ ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_CRYPTO_DATACNT);
+
+ assert(acnt > 0);
+ assert(bcnt > 0);
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, bcnt, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ nfr->type = NGTCP2_FRAME_CRYPTO;
+ nfr->offset = fr->offset + left;
+ nfr->datacnt = bcnt;
+ ngtcp2_vec_copy(nfr->data, b, bcnt);
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, acnt, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+ }
+
+ left -= (size_t)datalen;
+
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ for (; left && ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq);
+ nfrc = ngtcp2_ksl_it_get(&it);
+ nfr = &nfrc->fr.crypto;
+
+ if (nfr->offset != fr->offset + datalen) {
+ assert(fr->offset + datalen < nfr->offset);
+ break;
+ }
+
+ rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+ if (nfrc == NULL) {
+ break;
+ }
+
+ nfr = &nfrc->fr.crypto;
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left,
+ NGTCP2_MAX_CRYPTO_DATACNT);
+ if (nmerged == 0) {
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+ break;
+ }
+
+ datalen += nmerged;
+ left -= nmerged;
+
+ if (nfr->datacnt == 0) {
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+
+ nfr->offset += nmerged;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ break;
+ }
+
+ if (acnt == fr->datacnt) {
+ assert(acnt > 0);
+ fr->data[acnt - 1] = a[acnt - 1];
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ assert(acnt > fr->datacnt);
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, acnt, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.crypto;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+}
+
+/*
+ * conn_verify_dcid verifies that destination connection ID in |hd| is
+ * valid for the connection. If it is successfully verified and the
+ * remote endpoint uses new DCID in the packet, nonzero value is
+ * assigned to |*pnew_cid_used| if it is not NULL. Otherwise 0 is
+ * assigned to it.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |dcid| is not known to the local endpoint.
+ */
+static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used,
+ const ngtcp2_pkt_hd *hd) {
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+ int rv;
+
+ it = ngtcp2_ksl_lower_bound(&conn->scid.set, &hd->dcid);
+ if (ngtcp2_ksl_it_end(&it)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ scid = ngtcp2_ksl_it_get(&it);
+ if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) {
+ scid->flags |= NGTCP2_SCID_FLAG_USED;
+
+ if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) {
+ rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pnew_cid_used) {
+ *pnew_cid_used = 1;
+ }
+ } else if (pnew_cid_used) {
+ *pnew_cid_used = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_should_pad_pkt returns nonzero if the packet should be padded.
+ * |type| is the type of packet. |left| is the space left in packet
+ * buffer. |write_datalen| is the number of bytes which will be sent
+ * in the next, coalesced 0-RTT or 1RTT packet.
+ */
+static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
+ uint64_t write_datalen, int ack_eliciting,
+ int require_padding) {
+ uint64_t min_payloadlen;
+
+ if (type == NGTCP2_PKT_INITIAL) {
+ if (conn->server) {
+ if (!ack_eliciting) {
+ return 0;
+ }
+
+ if (conn->hs_pktns->crypto.tx.ckm &&
+ (conn->hs_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
+ !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
+ /* If we have something to send in Handshake packet, then add
+ PADDING in Handshake packet. */
+ min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN;
+ } else {
+ return 1;
+ }
+ } else {
+ if (conn->hs_pktns->crypto.tx.ckm &&
+ (conn->hs_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) ||
+ !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) {
+ /* If we have something to send in Handshake packet, then add
+ PADDING in Handshake packet. */
+ min_payloadlen = NGTCP2_MIN_COALESCED_PAYLOADLEN;
+ } else if ((!conn->early.ckm && !conn->pktns.crypto.tx.ckm) ||
+ write_datalen == 0) {
+ return 1;
+ } else {
+ /* If we have something to send in 0RTT or 1RTT packet, then
+ add PADDING in that packet. Take maximum in case that
+ write_datalen includes DATAGRAM which cannot be split. */
+ min_payloadlen =
+ ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ }
+ }
+ } else {
+ assert(type == NGTCP2_PKT_HANDSHAKE);
+
+ if (!require_padding) {
+ return 0;
+ }
+
+ if (!conn->pktns.crypto.tx.ckm || write_datalen == 0) {
+ return 1;
+ }
+
+ min_payloadlen = ngtcp2_max(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ }
+
+ /* TODO the next packet type should be taken into account */
+ return left <
+ /* TODO Assuming that pkt_num is encoded in 1 byte. */
+ NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen +
+ conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen +
+ NGTCP2_MAX_AEAD_OVERHEAD;
+}
+
+static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ conn->idle_ts = ts;
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
+}
+
+static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ conn->idle_ts = ts;
+ conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
+}
+
+/*
+ * conn_keep_alive_enabled returns nonzero if keep-alive is enabled.
+ */
+static int conn_keep_alive_enabled(ngtcp2_conn *conn) {
+ return conn->keep_alive.last_ts != UINT64_MAX && conn->keep_alive.timeout;
+}
+
+/*
+ * conn_keep_alive_expired returns nonzero if keep-alive timer has
+ * expired.
+ */
+static int conn_keep_alive_expired(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ return conn_keep_alive_enabled(conn) &&
+ conn->keep_alive.last_ts + conn->keep_alive.timeout <= ts;
+}
+
+/*
+ * conn_keep_alive_expiry returns the expiry time of keep-alive timer.
+ */
+static ngtcp2_tstamp conn_keep_alive_expiry(ngtcp2_conn *conn) {
+ if ((conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) ||
+ !conn_keep_alive_enabled(conn)) {
+ return UINT64_MAX;
+ }
+
+ return conn->keep_alive.last_ts + conn->keep_alive.timeout;
+}
+
+/*
+ * conn_cancel_expired_keep_alive_timer cancels the expired keep-alive
+ * timer.
+ */
+static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED) &&
+ conn_keep_alive_expired(conn, ts)) {
+ conn->flags |= NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
+ }
+}
+
+/*
+ * conn_update_keep_alive_last_ts updates the base time point of
+ * keep-alive timer.
+ */
+static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ conn->keep_alive.last_ts = ts;
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
+}
+
+void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
+ ngtcp2_duration timeout) {
+ conn->keep_alive.timeout = timeout;
+}
+
+/*
+ * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event
+ * loop. Packet pacing might require sub milliseconds packet spacing,
+ * but userspace event loop might not offer such precision.
+ * Typically, if delay is 0.5 microseconds, the actual delay after
+ * which we can send packet is well over 1 millisecond when event loop
+ * is involved (which includes other stuff, like reading packets etc
+ * in a typical single threaded use case).
+ */
+#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS
+
+static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ if (conn->tx.pacing.next_ts == UINT64_MAX) {
+ return;
+ }
+
+ if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) {
+ return;
+ }
+
+ conn->tx.pacing.next_ts = UINT64_MAX;
+}
+
+static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ return conn->tx.pacing.next_ts == UINT64_MAX ||
+ conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD;
+}
+
+static uint8_t conn_pkt_flags(ngtcp2_conn *conn) {
+ if (conn->remote.transport_params &&
+ conn->remote.transport_params->grease_quic_bit &&
+ (conn->flags & NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT)) {
+ return NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
+ }
+
+ return NGTCP2_PKT_FLAG_NONE;
+}
+
+static uint8_t conn_pkt_flags_long(ngtcp2_conn *conn) {
+ return NGTCP2_PKT_FLAG_LONG_FORM | conn_pkt_flags(conn);
+}
+
+static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) {
+ return (uint8_t)(conn_pkt_flags(conn) | ((conn->pktns.crypto.tx.ckm->flags &
+ NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)
+ ? NGTCP2_PKT_FLAG_KEY_PHASE
+ : NGTCP2_PKT_FLAG_NONE));
+}
+
+/*
+ * conn_write_handshake_pkt writes handshake packet in the buffer
+ * pointed by |dest| whose length is |destlen|. |type| specifies long
+ * packet type. It should be either NGTCP2_PKT_INITIAL or
+ * NGTCP2_PKT_HANDSHAKE_PKT.
+ *
+ * |write_datalen| is the minimum length of application data ready to
+ * send in subsequent 0RTT or 1RTT packet.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize
+conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint8_t type, uint8_t flags,
+ uint64_t write_datalen, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_frame_chain *frq = NULL, **pfrc = &frq;
+ ngtcp2_frame_chain *nfrc;
+ ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_rtb_entry *rtbent;
+ ngtcp2_pktns *pktns;
+ size_t left;
+ uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+ int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0;
+ int pkt_empty = 1;
+ int padded = 0;
+ int hd_logged = 0;
+ uint64_t crypto_offset;
+ ngtcp2_ssize num_reclaimed;
+ uint32_t version;
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->in_pktns) {
+ return 0;
+ }
+ assert(conn->in_pktns->crypto.tx.ckm);
+ pktns = conn->in_pktns;
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (version == conn->client_chosen_version) {
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ } else {
+ assert(conn->vneg.version == version);
+
+ cc.ckm = conn->vneg.tx.ckm;
+ cc.hp_ctx = conn->vneg.tx.hp_ctx;
+ }
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) {
+ return 0;
+ }
+ pktns = conn->hs_pktns;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ cc.aead = pktns->crypto.ctx.aead;
+ cc.hp = pktns->crypto.ctx.hp;
+ cc.encrypt = conn->callbacks.encrypt;
+ cc.hp_mask = conn->callbacks.hp_mask;
+
+ ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type,
+ &conn->dcid.current.cid, &conn->oscid,
+ pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
+ version, 0);
+
+ if (!conn->server && type == NGTCP2_PKT_INITIAL &&
+ conn->local.settings.tokenlen) {
+ hd.token = conn->local.settings.token;
+ hd.tokenlen = conn->local.settings.tokenlen;
+ }
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return 0;
+ }
+
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts,
+ /* ack_delay = */ 0,
+ NGTCP2_DEFAULT_ACK_DELAY_EXPONENT);
+ if (rv != 0) {
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ if (ackfr) {
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ ngtcp2_acktr_commit_ack(&pktns->acktr);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack);
+ pkt_empty = 0;
+ }
+ }
+
+ /* Server requires at least NGTCP2_MAX_UDP_PAYLOAD_SIZE bytes in
+ order to send ack-eliciting Initial packet. */
+ if (!conn->server || type != NGTCP2_PKT_INITIAL ||
+ destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ build_pkt:
+ for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ left = ngtcp2_ppe_left(&ppe);
+
+ crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns);
+ if (crypto_offset == (size_t)-1) {
+ conn_cryptofrq_clear(conn, pktns);
+ break;
+ }
+
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ }
+
+ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) {
+ num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
+ if (num_reclaimed < 0) {
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+ conn->mem);
+ return rv;
+ }
+ if (num_reclaimed) {
+ goto build_pkt;
+ }
+ /* We had pktns->rtb.num_retransmittable > 0 but the contents of
+ those packets have been acknowledged (i.e., retransmission in
+ another packet). For server, in this case, we don't have to
+ send any probe packet. Client needs to send probe packets
+ until it knows that server has completed address validation or
+ handshake has been confirmed. */
+ if (pktns->rtb.num_pto_eliciting == 0 &&
+ (conn->server ||
+ (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+ pktns->rtb.probe_pkt_left = 0;
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ /* TODO If packet is empty, we should return now if cwnd is
+ zero. */
+ }
+ }
+
+ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ pktns->rtb.probe_pkt_left) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ } else {
+ rtb_entry_flags |=
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PROBE;
+ pkt_empty = 0;
+ }
+ }
+
+ if (!pkt_empty) {
+ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ /* The intention of smaller limit is get more chance to measure
+ RTT samples in early phase. */
+ if (pktns->tx.num_non_ack_pkt >= 1) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ } else {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ } else {
+ ++pktns->tx.num_non_ack_pkt;
+ }
+ } else {
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ }
+ }
+
+ if (pkt_empty) {
+ return 0;
+ }
+
+ /* If we cannot write another packet, then we need to add padding to
+ Initial here. */
+ if (conn_should_pad_pkt(
+ conn, type, ngtcp2_ppe_left(&ppe), write_datalen,
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0,
+ require_padding)) {
+ lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ lfr.type = NGTCP2_FRAME_PADDING;
+ lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+ }
+
+ if (lfr.padding.len) {
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, &hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ spktlen = ngtcp2_ppe_final(&ppe, NULL);
+ if (spktlen < 0) {
+ assert(ngtcp2_err_is_fatal((int)spktlen));
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
+ return spktlen;
+ }
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen);
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) {
+ if (pi) {
+ conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
+ }
+
+ rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
+ if (rv != 0) {
+ ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+ } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
+ }
+
+ if (pktns->rtb.probe_pkt_left &&
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ --pktns->rtb.probe_pkt_left;
+ }
+
+ conn_update_keep_alive_last_ts(conn, ts);
+
+ conn->dcid.current.bytes_sent += (uint64_t)spktlen;
+
+ conn->tx.pacing.pktlen += (size_t)spktlen;
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return spktlen;
+}
+
+/*
+ * conn_write_ack_pkt writes QUIC packet for type |type| which only
+ * includes ACK frame in the buffer pointed by |dest| whose length is
+ * |destlen|.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint8_t type, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_frame *ackfr;
+ ngtcp2_pktns *pktns;
+ ngtcp2_duration ack_delay;
+ uint64_t ack_delay_exponent;
+ ngtcp2_ssize spktlen;
+
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ assert(conn->server);
+ pktns = conn->in_pktns;
+ ack_delay = 0;
+ ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ ack_delay = 0;
+ ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ break;
+ case NGTCP2_PKT_1RTT:
+ pktns = &conn->pktns;
+ ack_delay = conn_compute_ack_delay(conn);
+ ack_delay_exponent = conn->local.transport_params.ack_delay_exponent;
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ if (!pktns->crypto.tx.ckm) {
+ return 0;
+ }
+
+ ackfr = NULL;
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay,
+ ack_delay_exponent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!ackfr) {
+ return 0;
+ }
+
+ spktlen = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+
+ if (spktlen <= 0) {
+ return spktlen;
+ }
+
+ conn->dcid.current.bytes_sent += (uint64_t)spktlen;
+
+ return spktlen;
+}
+
+static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pktns *pktns = *ppktns;
+ uint64_t bytes_in_flight;
+
+ bytes_in_flight = pktns->rtb.cc_bytes_in_flight;
+
+ assert(conn->cstat.bytes_in_flight >= bytes_in_flight);
+
+ conn->cstat.bytes_in_flight -= bytes_in_flight;
+ conn->cstat.pto_count = 0;
+ conn->cstat.last_tx_pkt_ts[pktns->rtb.pktns_id] = UINT64_MAX;
+ conn->cstat.loss_time[pktns->rtb.pktns_id] = UINT64_MAX;
+
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx);
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx);
+
+ pktns_del(pktns, conn->mem);
+ *ppktns = NULL;
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+}
+
+/*
+ * conn_discard_initial_state discards state for Initial packet number
+ * space.
+ */
+static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ if (!conn->in_pktns) {
+ return;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discarding Initial packet number space");
+
+ conn_discard_pktns(conn, &conn->in_pktns, ts);
+
+ conn_vneg_crypto_free(conn);
+
+ memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx));
+ memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx));
+}
+
+/*
+ * conn_discard_handshake_state discards state for Handshake packet
+ * number space.
+ */
+static void conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ if (!conn->hs_pktns) {
+ return;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discarding Handshake packet number space");
+
+ conn_discard_pktns(conn, &conn->hs_pktns, ts);
+}
+
+/*
+ * conn_discard_early_key discards early key.
+ */
+static void conn_discard_early_key(ngtcp2_conn *conn) {
+ assert(conn->early.ckm);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "discarding early key");
+
+ conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx);
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx);
+ memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+ conn->early.ckm = NULL;
+}
+
+/*
+ * conn_write_handshake_ack_pkts writes packets which contain ACK
+ * frame only. This function writes at most 2 packets for each
+ * Initial and Handshake packet.
+ */
+static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize res = 0, nwrite = 0;
+
+ /* In the most cases, client sends ACK in conn_write_handshake_pkt.
+ This function is only called when it is CWND limited or pacing
+ limited. It is not required for client to send ACK for server
+ Initial. This is because once it gets server Initial, it gets
+ Handshake tx key and discards Initial key. The only good reason
+ to send ACK is give server RTT measurement early. */
+ if (conn->server && conn->in_pktns) {
+ nwrite =
+ conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (conn->hs_pktns->crypto.tx.ckm) {
+ nwrite =
+ conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (!conn->server && nwrite) {
+ conn_discard_initial_state(conn, ts);
+ }
+ }
+
+ return res;
+}
+
+/*
+ * conn_write_client_initial writes Initial packet in the buffer
+ * pointed by |dest| whose length is |destlen|.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint64_t early_datalen,
+ ngtcp2_tstamp ts) {
+ int rv;
+
+ rv = conn_call_client_initial(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen,
+ ts);
+}
+
+/*
+ * dcid_tx_left returns the maximum number of bytes that server is
+ * allowed to send to an unvalidated path associated to |dcid|.
+ */
+static uint64_t dcid_tx_left(ngtcp2_dcid *dcid) {
+ if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
+ return SIZE_MAX;
+ }
+ /* From QUIC spec: Prior to validating the client address, servers
+ MUST NOT send more than three times as many bytes as the number
+ of bytes they have received. */
+ assert(dcid->bytes_recv * 3 >= dcid->bytes_sent);
+
+ return dcid->bytes_recv * 3 - dcid->bytes_sent;
+}
+
+/*
+ * conn_server_tx_left returns the maximum number of bytes that server
+ * is allowed to send to an unvalidated path.
+ */
+static uint64_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) {
+ assert(conn->server);
+
+ /* If pv->dcid has the current path, use conn->dcid.current. This
+ is because conn->dcid.current gets update for bytes_recv and
+ bytes_sent. */
+ if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) {
+ return dcid_tx_left(&conn->dcid.current);
+ }
+
+ return dcid_tx_left(dcid);
+}
+
+/*
+ * conn_write_handshake_pkts writes Initial and Handshake packets in
+ * the buffer pointed by |dest| whose length is |destlen|.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint64_t write_datalen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nwrite;
+ ngtcp2_ssize res = 0;
+ ngtcp2_rtb_entry *rtbent;
+ uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_ksl_it it;
+
+ /* As a client, we would like to discard Initial packet number space
+ when sending the first Handshake packet. When sending Handshake
+ packet, it should be one of 1) sending ACK, 2) sending PTO probe
+ packet, or 3) sending CRYPTO. If we have pending acknowledgement
+ for Initial, then do not discard Initial packet number space.
+ Otherwise, if either 1) or 2) is satisfied, discard Initial
+ packet number space. When sending Handshake CRYPTO, it indicates
+ that client has received Handshake CRYPTO from server. Initial
+ packet number space is discarded because 1) is met. If there is
+ pending Initial ACK, Initial packet number space is discarded
+ after writing the first Handshake packet.
+ */
+ if (!conn->server && conn->hs_pktns->crypto.tx.ckm && conn->in_pktns &&
+ !ngtcp2_acktr_require_active_ack(&conn->in_pktns->acktr,
+ /* max_ack_delay = */ 0, ts) &&
+ (ngtcp2_acktr_require_active_ack(&conn->hs_pktns->acktr,
+ /* max_ack_delay = */ 0, ts) ||
+ conn->hs_pktns->rtb.probe_pkt_left)) {
+ /* Discard Initial state here so that Handshake packet is not
+ padded. */
+ conn_discard_initial_state(conn, ts);
+ } else if (conn->in_pktns) {
+ nwrite =
+ conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ if (nwrite == 0) {
+ if (conn->server && (conn->in_pktns->rtb.probe_pkt_left ||
+ ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) {
+ if (cstat->loss_detection_timer != UINT64_MAX &&
+ conn_server_tx_left(conn, &conn->dcid.current) <
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
+ cstat->loss_detection_timer = UINT64_MAX;
+ }
+
+ return 0;
+ }
+ } else {
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+
+ if (destlen) {
+ /* We might have already added padding to Initial, but in that
+ case, we should have destlen == 0 and no Handshake packet
+ will be written. */
+ if (conn->server) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ if (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ }
+ } else {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ }
+ }
+ }
+
+ nwrite = conn_write_handshake_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, wflags, write_datalen, ts);
+ if (nwrite < 0) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (!conn->server && conn->hs_pktns->crypto.tx.ckm && nwrite) {
+ /* We don't need to send further Initial packet if we have
+ Handshake key and sent something with it. So discard initial
+ state here. */
+ conn_discard_initial_state(conn, ts);
+ }
+
+ return res;
+}
+
+/*
+ * conn_initial_stream_rx_offset returns the initial maximum offset of
+ * data for a stream denoted by |stream_id|.
+ */
+static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn,
+ int64_t stream_id) {
+ int local_stream = conn_local_stream(conn, stream_id);
+
+ if (bidi_stream(stream_id)) {
+ if (local_stream) {
+ return conn->local.transport_params.initial_max_stream_data_bidi_local;
+ }
+ return conn->local.transport_params.initial_max_stream_data_bidi_remote;
+ }
+
+ if (local_stream) {
+ return 0;
+ }
+ return conn->local.transport_params.initial_max_stream_data_uni;
+}
+
+/*
+ * conn_should_send_max_stream_data returns nonzero if MAX_STREAM_DATA
+ * frame should be send for |strm|.
+ */
+static int conn_should_send_max_stream_data(ngtcp2_conn *conn,
+ ngtcp2_strm *strm) {
+ uint64_t inc = strm->rx.unsent_max_offset - strm->rx.max_offset;
+ (void)conn;
+
+ return strm->rx.window < 2 * inc;
+}
+
+/*
+ * conn_should_send_max_data returns nonzero if MAX_DATA frame should
+ * be sent.
+ */
+static int conn_should_send_max_data(ngtcp2_conn *conn) {
+ uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset;
+
+ return conn->rx.window < 2 * inc;
+}
+
+/*
+ * conn_required_num_new_connection_id returns the number of
+ * additional connection ID the local endpoint has to provide to the
+ * remote endpoint.
+ */
+static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) {
+ uint64_t n;
+ size_t len = ngtcp2_ksl_len(&conn->scid.set);
+
+ if (len >= NGTCP2_MAX_SCID_POOL_SIZE) {
+ return 0;
+ }
+
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->active_connection_id_limit);
+
+ /* len includes retired CID. We don't provide extra CID if doing so
+ exceeds NGTCP2_MAX_SCID_POOL_SIZE. */
+
+ n = conn->remote.transport_params->active_connection_id_limit +
+ conn->scid.num_retired;
+
+ return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len;
+}
+
+/*
+ * conn_enqueue_new_connection_id generates additional connection IDs
+ * and prepares to send them to the remote endpoint.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
+ size_t i, need = conn_required_num_new_connection_id(conn);
+ size_t cidlen = conn->oscid.datalen;
+ ngtcp2_cid cid;
+ uint64_t seq;
+ int rv;
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ ngtcp2_frame_chain *nfrc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_scid *scid;
+ ngtcp2_ksl_it it;
+
+ for (i = 0; i < need; ++i) {
+ rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (cid.datalen != cidlen) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ /* Assert uniqueness */
+ it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid);
+ if (!ngtcp2_ksl_it_end(&it) &&
+ ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ seq = ++conn->scid.last_seq;
+
+ scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid));
+ if (scid == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_scid_init(scid, seq, &cid);
+
+ rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid);
+ if (rv != 0) {
+ ngtcp2_mem_free(conn->mem, scid);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ nfrc->fr.new_connection_id.seq = seq;
+ nfrc->fr.new_connection_id.retire_prior_to = 0;
+ nfrc->fr.new_connection_id.cid = cid;
+ memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token,
+ sizeof(token));
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_remove_retired_connection_id removes the already retired
+ * connection ID. It waits PTO before actually removing a connection
+ * ID after it receives RETIRE_CONNECTION_ID from peer to catch
+ * reordered packets.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
+ ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_duration timeout = pto;
+ ngtcp2_scid *scid;
+ ngtcp2_dcid *dcid;
+ int rv;
+
+ for (; !ngtcp2_pq_empty(&conn->scid.used);) {
+ scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe);
+
+ if (scid->retired_ts == UINT64_MAX || scid->retired_ts + timeout >= ts) {
+ break;
+ }
+
+ assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED);
+
+ rv = conn_call_remove_connection_id(conn, &scid->cid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ksl_remove(&conn->scid.set, NULL, &scid->cid);
+ ngtcp2_pq_pop(&conn->scid.used);
+ ngtcp2_mem_free(conn->mem, scid);
+
+ assert(conn->scid.num_retired);
+ --conn->scid.num_retired;
+ }
+
+ for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
+ if (dcid->retired_ts + timeout >= ts) {
+ break;
+ }
+
+ rv = conn_call_deactivate_dcid(conn, dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb);
+ }
+
+ return 0;
+}
+
+/*
+ * conn_min_short_pktlen returns the minimum length of Short packet
+ * this endpoint sends.
+ */
+static size_t conn_min_short_pktlen(ngtcp2_conn *conn) {
+ return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN;
+}
+
+/*
+ * conn_handle_unconfirmed_key_update_from_remote deals with key
+ * update which has not been confirmed yet and initiated by the remote
+ * endpoint.
+ *
+ * If key update was initiated by the remote endpoint, acknowledging a
+ * packet encrypted with the new key completes key update procedure.
+ */
+static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn,
+ int64_t largest_ack,
+ ngtcp2_tstamp ts) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) ||
+ largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) {
+ return;
+ }
+
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ conn->crypto.key_update.confirmed_ts = ts;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
+}
+
+/*
+ * conn_write_pkt writes a protected packet in the buffer pointed by
+ * |dest| whose length if |destlen|. |type| specifies the type of
+ * packet. It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT.
+ *
+ * This function can send new stream data. In order to send stream
+ * data, specify the underlying stream and parameters to
+ * |vmsg|->stream. If |vmsg|->stream.fin is set to nonzero, it
+ * signals that the given data is the final portion of the stream.
+ * |vmsg|->stream.data vector of length |vmsg|->stream.datacnt
+ * specifies stream data to send. The number of bytes sent to the
+ * stream is assigned to *|vmsg|->stream.pdatalen. If 0 length STREAM
+ * data is sent, 0 is assigned to it. The caller should initialize
+ * *|vmsg|->stream.pdatalen to -1.
+ *
+ * If |require_padding| is nonzero, padding bytes are added to occupy
+ * the remaining packet payload.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_STREAM_DATA_BLOCKED
+ * Stream data could not be written because of flow control.
+ */
+static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp ts) {
+ int rv = 0;
+ ngtcp2_crypto_cc *cc = &conn->pkt.cc;
+ ngtcp2_ppe *ppe = &conn->pkt.ppe;
+ ngtcp2_pkt_hd *hd = &conn->pkt.hd;
+ ngtcp2_frame *ackfr = NULL, lfr;
+ ngtcp2_ssize nwrite;
+ ngtcp2_frame_chain **pfrc, *nfrc, *frc;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_strm *strm;
+ int pkt_empty = 1;
+ uint64_t ndatalen = 0;
+ int send_stream = 0;
+ int stream_blocked = 0;
+ int send_datagram = 0;
+ ngtcp2_pktns *pktns = &conn->pktns;
+ size_t left;
+ uint64_t datalen = 0;
+ ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
+ size_t datacnt;
+ uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+ int hd_logged = 0;
+ ngtcp2_path_challenge_entry *pcent;
+ uint8_t hd_flags = NGTCP2_PKT_FLAG_NONE;
+ int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0;
+ int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0;
+ int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ size_t min_pktlen = conn_min_short_pktlen(conn);
+ int padded = 0;
+ ngtcp2_cc_pkt cc_pkt;
+ uint64_t crypto_offset;
+ uint64_t stream_offset;
+ ngtcp2_ssize num_reclaimed;
+ int fin;
+ uint64_t target_max_data;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ uint64_t delta;
+ const ngtcp2_cid *scid = NULL;
+ int keep_alive_expired = 0;
+ uint32_t version = 0;
+
+ /* Return 0 if destlen is less than minimum packet length which can
+ trigger Stateless Reset */
+ if (destlen < min_pktlen) {
+ return 0;
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ ndatalen = conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ /* 0 length STREAM frame is allowed */
+ if (ndatalen || datalen == 0) {
+ send_stream = 1;
+ } else {
+ stream_blocked = 1;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt);
+ send_datagram = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!ppe_pending) {
+ switch (type) {
+ case NGTCP2_PKT_1RTT:
+ hd_flags = conn_pkt_flags_short(conn);
+ scid = NULL;
+ cc->aead = pktns->crypto.ctx.aead;
+ cc->hp = pktns->crypto.ctx.hp;
+ cc->ckm = pktns->crypto.tx.ckm;
+ cc->hp_ctx = pktns->crypto.tx.hp_ctx;
+
+ assert(conn->negotiated_version);
+
+ version = conn->negotiated_version;
+
+ /* transport parameter is only valid after handshake completion
+ which means we don't know how many connection ID that remote
+ peer can accept before handshake completion. */
+ if (conn->oscid.datalen && conn_is_handshake_completed(conn)) {
+ rv = conn_enqueue_new_connection_id(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ break;
+ case NGTCP2_PKT_0RTT:
+ assert(!conn->server);
+ if (!conn->early.ckm) {
+ return 0;
+ }
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
+ cc->aead = conn->early.ctx.aead;
+ cc->hp = conn->early.ctx.hp;
+ cc->ckm = conn->early.ckm;
+ cc->hp_ctx = conn->early.hp_ctx;
+ version = conn->client_chosen_version;
+ break;
+ default:
+ /* Unreachable */
+ ngtcp2_unreachable();
+ }
+
+ cc->encrypt = conn->callbacks.encrypt;
+ cc->hp_mask = conn->callbacks.hp_mask;
+
+ if (conn_should_send_max_data(conn)) {
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->local.settings.max_window &&
+ conn->tx.last_max_data_ts != UINT64_MAX &&
+ ts - conn->tx.last_max_data_ts <
+ NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt &&
+ conn->local.settings.max_window > conn->rx.window) {
+ target_max_data = NGTCP2_FLOW_WINDOW_SCALING_FACTOR * conn->rx.window;
+ if (target_max_data > conn->local.settings.max_window) {
+ target_max_data = conn->local.settings.max_window;
+ }
+
+ delta = target_max_data - conn->rx.window;
+ if (conn->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) {
+ delta = NGTCP2_MAX_VARINT - conn->rx.unsent_max_offset;
+ }
+
+ conn->rx.window = target_max_data;
+ } else {
+ delta = 0;
+ }
+
+ conn->tx.last_max_data_ts = ts;
+
+ nfrc->fr.type = NGTCP2_FRAME_MAX_DATA;
+ nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset + delta;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ conn->rx.max_offset = conn->rx.unsent_max_offset =
+ nfrc->fr.max_data.max_data;
+ }
+
+ ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid,
+ pktns->tx.last_pkt_num + 1,
+ pktns_select_pkt_numlen(pktns), version, 0);
+
+ ngtcp2_ppe_init(ppe, dest, destlen, cc);
+
+ rv = ngtcp2_ppe_encode_hd(ppe, hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(ppe)) {
+ return 0;
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
+
+ /* PATH_RESPONSE is bound to the path that the corresponding
+ PATH_CHALLENGE is received. */
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
+ lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memcpy(lfr.path_response.data, pcent->data,
+ sizeof(lfr.path_response.data));
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ require_padding =
+ !conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+ /* We don't retransmit PATH_RESPONSE. */
+ }
+ }
+ }
+
+ rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts,
+ conn_compute_ack_delay(conn),
+ conn->local.transport_params.ack_delay_exponent);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (ackfr) {
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ ngtcp2_acktr_commit_ack(&pktns->acktr);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num,
+ ackfr->ack.largest_ack);
+ if (type == NGTCP2_PKT_1RTT) {
+ conn_handle_unconfirmed_key_update_from_remote(
+ conn, ackfr->ack.largest_ack, ts);
+ }
+ pkt_empty = 0;
+ }
+ }
+
+ build_pkt:
+ for (pfrc = &pktns->tx.frq; *pfrc;) {
+ if ((*pfrc)->binder &&
+ ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STOP_SENDING:
+ strm =
+ ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id);
+ if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+
+ if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) {
+ strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED;
+
+ rv = conn_call_stream_stop_sending(
+ conn, (*pfrc)->fr.stop_sending.stream_id,
+ (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ }
+
+ break;
+ case NGTCP2_FRAME_STREAM:
+ ngtcp2_unreachable();
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ if ((*pfrc)->fr.max_streams.max_streams <
+ conn->remote.bidi.max_streams) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ if ((*pfrc)->fr.max_streams.max_streams <
+ conn->remote.uni.max_streams) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ strm = ngtcp2_conn_find_stream(conn,
+ (*pfrc)->fr.max_stream_data.stream_id);
+ if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) ||
+ (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ continue;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ ngtcp2_unreachable();
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ break;
+ }
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF) {
+ for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) {
+ left = ngtcp2_ppe_left(ppe);
+
+ crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns);
+ if (crypto_offset == (size_t)-1) {
+ conn_cryptofrq_clear(conn, pktns);
+ break;
+ }
+
+ left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left);
+
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ }
+ }
+
+ /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream
+ ID space in one packet. */
+ if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL &&
+ conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) {
+ rv = conn_call_extend_max_remote_streams_bidi(
+ conn, conn->remote.bidi.unsent_max_streams);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI;
+ nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams;
+ *pfrc = nfrc;
+
+ conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) {
+ if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) {
+ rv = conn_call_extend_max_remote_streams_uni(
+ conn, conn->remote.uni.unsent_max_streams);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI;
+ nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams;
+ *pfrc = nfrc;
+
+ conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd,
+ &(*pfrc)->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ } else {
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ }
+ }
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF) {
+ for (; !ngtcp2_pq_empty(&conn->tx.strmq);) {
+ strm = ngtcp2_conn_tx_strmq_top(conn);
+
+ if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ conn_should_send_max_stream_data(conn, strm)) {
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (conn->local.settings.max_stream_window &&
+ strm->tx.last_max_stream_data_ts != UINT64_MAX &&
+ ts - strm->tx.last_max_stream_data_ts <
+ NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt &&
+ conn->local.settings.max_stream_window > strm->rx.window) {
+ target_max_data =
+ NGTCP2_FLOW_WINDOW_SCALING_FACTOR * strm->rx.window;
+ if (target_max_data > conn->local.settings.max_stream_window) {
+ target_max_data = conn->local.settings.max_stream_window;
+ }
+
+ delta = target_max_data - strm->rx.window;
+ if (strm->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) {
+ delta = NGTCP2_MAX_VARINT - strm->rx.unsent_max_offset;
+ }
+
+ strm->rx.window = target_max_data;
+ } else {
+ delta = 0;
+ }
+
+ strm->tx.last_max_stream_data_ts = ts;
+
+ nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ nfrc->fr.max_stream_data.stream_id = strm->stream_id;
+ nfrc->fr.max_stream_data.max_stream_data =
+ strm->rx.unsent_max_offset + delta;
+ ngtcp2_list_insert(nfrc, pfrc);
+
+ rv =
+ conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ break;
+ }
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+ pfrc = &(*pfrc)->next;
+ strm->rx.max_offset = strm->rx.unsent_max_offset =
+ nfrc->fr.max_stream_data.max_stream_data;
+ }
+
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ continue;
+ }
+
+ stream_offset = ngtcp2_strm_streamfrq_unacked_offset(strm);
+ if (stream_offset == (uint64_t)-1) {
+ ngtcp2_strm_streamfrq_clear(strm);
+ ngtcp2_conn_tx_strmq_pop(conn);
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ continue;
+ }
+
+ left = ngtcp2_ppe_left(ppe);
+
+ left = ngtcp2_pkt_stream_max_datalen(strm->stream_id, stream_offset,
+ left, left);
+
+ if (left == (size_t)-1) {
+ break;
+ }
+
+ rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ if (nfrc == NULL) {
+ /* TODO Why? */
+ break;
+ }
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+
+ if (ngtcp2_strm_streamfrq_empty(strm)) {
+ ngtcp2_conn_tx_strmq_pop(conn);
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ continue;
+ }
+
+ ngtcp2_conn_tx_strmq_pop(conn);
+ ++strm->cycle;
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ }
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF && !send_stream && !send_datagram &&
+ !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ pktns->rtb.num_retransmittable && pktns->tx.frq == NULL &&
+ pktns->rtb.probe_pkt_left) {
+ num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
+ if (num_reclaimed < 0) {
+ return rv;
+ }
+ if (num_reclaimed) {
+ goto build_pkt;
+ }
+
+ /* We had pktns->rtb.num_retransmittable > 0 but we were unable
+ to reclaim any frame. In this case, we do not have to send
+ any probe packet. */
+ if (pktns->rtb.num_pto_eliciting == 0) {
+ pktns->rtb.probe_pkt_left = 0;
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ if (pkt_empty && conn_cwnd_is_zero(conn) && !require_padding) {
+ return 0;
+ }
+ }
+ }
+ } else {
+ pfrc = conn->pkt.pfrc;
+ rtb_entry_flags |= conn->pkt.rtb_entry_flags;
+ pkt_empty = conn->pkt.pkt_empty;
+ hd_logged = conn->pkt.hd_logged;
+ }
+
+ left = ngtcp2_ppe_left(ppe);
+
+ if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL &&
+ (ndatalen = ngtcp2_pkt_stream_max_datalen(
+ vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen,
+ left)) != (size_t)-1 &&
+ (ndatalen || datalen == 0)) {
+ datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT,
+ vmsg->stream.data, vmsg->stream.datacnt,
+ (size_t)ndatalen);
+ ndatalen = ngtcp2_vec_len(data, datacnt);
+
+ assert((datacnt == 0 && datalen == 0) || (datacnt && datalen));
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, datacnt, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ nfrc->fr.stream.type = NGTCP2_FRAME_STREAM;
+ nfrc->fr.stream.flags = 0;
+ nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id;
+ nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset;
+ nfrc->fr.stream.datacnt = datacnt;
+ ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt);
+
+ fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
+ ndatalen == datalen;
+ nfrc->fr.stream.fin = (uint8_t)fin;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ if (rv != 0) {
+ ngtcp2_unreachable();
+ }
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+
+ pkt_empty = 0;
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
+
+ vmsg->stream.strm->tx.offset += ndatalen;
+ conn->tx.offset += ndatalen;
+
+ if (fin) {
+ ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR);
+ }
+
+ if (vmsg->stream.pdatalen) {
+ *vmsg->stream.pdatalen = (ngtcp2_ssize)ndatalen;
+ }
+ } else {
+ send_stream = 0;
+ }
+
+ if (rv != NGTCP2_ERR_NOBUF && send_datagram &&
+ left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) {
+ if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) {
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ nfrc->fr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ nfrc->fr.datagram.dgram_id = vmsg->datagram.dgram_id;
+ nfrc->fr.datagram.datacnt = vmsg->datagram.datacnt;
+ nfrc->fr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
+ assert(rv == 0);
+
+ /* Because DATAGRAM will not be retransmitted, we do not use
+ data anymore. Just nullify it. The only reason to keep
+ track a frame is keep dgram_id to pass it to
+ ngtcp2_ack_datagram or ngtcp2_lost_datagram callbacks. */
+ nfrc->fr.datagram.datacnt = 0;
+ nfrc->fr.datagram.data = NULL;
+
+ *pfrc = nfrc;
+ pfrc = &(*pfrc)->next;
+ } else {
+ lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN;
+ lfr.datagram.datacnt = vmsg->datagram.datacnt;
+ lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ assert(rv == 0);
+ }
+
+ pkt_empty = 0;
+ rtb_entry_flags |=
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_DATAGRAM;
+
+ if (vmsg->datagram.paccepted) {
+ *vmsg->datagram.paccepted = 1;
+ }
+ } else {
+ send_datagram = 0;
+ }
+
+ if (pkt_empty) {
+ assert(rv == 0 || NGTCP2_ERR_NOBUF == rv);
+ if (rv == 0 && stream_blocked && ngtcp2_conn_get_max_data_left(conn)) {
+ return NGTCP2_ERR_STREAM_DATA_BLOCKED;
+ }
+
+ keep_alive_expired = conn_keep_alive_expired(conn, ts);
+
+ if (conn->pktns.rtb.probe_pkt_left == 0 && !keep_alive_expired &&
+ !require_padding) {
+ return 0;
+ }
+ } else if (write_more) {
+ conn->pkt.pfrc = pfrc;
+ conn->pkt.pkt_empty = pkt_empty;
+ conn->pkt.rtb_entry_flags = rtb_entry_flags;
+ conn->pkt.hd_logged = hd_logged;
+ conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING;
+
+ assert(vmsg);
+
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ if (send_stream) {
+ if (ngtcp2_ppe_left(ppe)) {
+ return NGTCP2_ERR_WRITE_MORE;
+ }
+ break;
+ }
+
+ if (ngtcp2_conn_get_max_data_left(conn) && stream_blocked) {
+ return NGTCP2_ERR_STREAM_DATA_BLOCKED;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ if (send_datagram && ngtcp2_ppe_left(ppe)) {
+ return NGTCP2_ERR_WRITE_MORE;
+ }
+ /* If DATAGRAM cannot be written due to insufficient space,
+ continue to create a packet with the hope that application
+ calls ngtcp2_conn_writev_datagram again. */
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+ }
+
+ if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT ||
+ keep_alive_expired || conn->pktns.rtb.probe_pkt_left) {
+ lfr.type = NGTCP2_FRAME_PING;
+
+ rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
+ if (rv != 0) {
+ assert(rv == NGTCP2_ERR_NOBUF);
+ /* TODO If buffer is too small, PING cannot be written if
+ packet is still empty. */
+ } else {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+ if (conn->pktns.rtb.probe_pkt_left) {
+ rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE;
+ }
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+ } else {
+ ++pktns->tx.num_non_ack_pkt;
+ }
+ } else {
+ pktns->tx.num_non_ack_pkt = 0;
+ }
+
+ /* TODO Push STREAM frame back to ngtcp2_strm if there is an error
+ before ngtcp2_rtb_entry is safely created and added. */
+ if (require_padding ||
+ /* Making full sized packet will help GSO a bit */
+ ngtcp2_ppe_left(ppe) < 10) {
+ lfr.padding.len = ngtcp2_ppe_padding(ppe);
+ } else {
+ lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
+ }
+
+ if (lfr.padding.len) {
+ lfr.type = NGTCP2_FRAME_PADDING;
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ nwrite = ngtcp2_ppe_final(ppe, NULL);
+ if (nwrite < 0) {
+ assert(ngtcp2_err_is_fatal((int)nwrite));
+ return nwrite;
+ }
+
+ ++cc->ckm->use_count;
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite);
+
+ /* TODO ack-eliciting vs needs-tracking */
+ /* probe packet needs tracking but it does not need ACK, could be lost. */
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) {
+ if (pi) {
+ conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts);
+ }
+
+ rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal((int)nwrite));
+ return rv;
+ }
+
+ if (*pfrc != pktns->tx.frq) {
+ ent->frc = pktns->tx.frq;
+ pktns->tx.frq = *pfrc;
+ *pfrc = NULL;
+ }
+
+ if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
+ pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) {
+ conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START,
+ ts);
+ }
+
+ rv = conn_on_pkt_sent(conn, &pktns->rtb, ent);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ if (conn->cc.on_pkt_sent) {
+ conn->cc.on_pkt_sent(
+ &conn->cc, &conn->cstat,
+ ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite,
+ NGTCP2_PKTNS_ID_APPLICATION, ts, ent->rst.lost,
+ ent->rst.tx_in_flight, ent->rst.is_app_limited));
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+ }
+ } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts);
+ }
+
+ conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
+
+ if (pktns->rtb.probe_pkt_left &&
+ (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ --pktns->rtb.probe_pkt_left;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
+ }
+
+ conn_update_keep_alive_last_ts(conn, ts);
+
+ conn->dcid.current.bytes_sent += (uint64_t)nwrite;
+
+ conn->tx.pacing.pktlen += (size_t)nwrite;
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
+ ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+ uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_ppe ppe;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_frame lfr;
+ ngtcp2_ssize nwrite;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_pktns *pktns;
+ uint8_t hd_flags;
+ ngtcp2_rtb_entry *rtbent;
+ int padded = 0;
+ const ngtcp2_cid *scid;
+ uint32_t version;
+
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ pktns = conn->in_pktns;
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (version == conn->client_chosen_version) {
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ } else {
+ assert(version == conn->vneg.version);
+
+ cc.ckm = conn->vneg.tx.ckm;
+ cc.hp_ctx = conn->vneg.tx.hp_ctx;
+ }
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ hd_flags = conn_pkt_flags_long(conn);
+ scid = &conn->oscid;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ break;
+ case NGTCP2_PKT_1RTT:
+ pktns = &conn->pktns;
+ hd_flags = conn_pkt_flags_short(conn);
+ scid = NULL;
+ version = conn->negotiated_version;
+ cc.ckm = pktns->crypto.tx.ckm;
+ cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+ break;
+ default:
+ /* We don't support 0-RTT packet in this function. */
+ ngtcp2_unreachable();
+ }
+
+ cc.aead = pktns->crypto.ctx.aead;
+ cc.hp = pktns->crypto.ctx.hp;
+ cc.encrypt = conn->callbacks.encrypt;
+ cc.hp_mask = conn->callbacks.hp_mask;
+
+ ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid,
+ pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
+ version, 0);
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return 0;
+ }
+
+ ngtcp2_log_tx_pkt_hd(&conn->log, &hd);
+ ngtcp2_qlog_pkt_sent_start(&conn->qlog);
+
+ rv = conn_ppe_write_frame(conn, &ppe, &hd, fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return 0;
+ }
+
+ lfr.type = NGTCP2_FRAME_PADDING;
+ if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) {
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ switch (fr->type) {
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+ } else {
+ lfr.padding.len = 0;
+ }
+ break;
+ default:
+ if (type == NGTCP2_PKT_1RTT) {
+ lfr.padding.len =
+ ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn));
+ } else {
+ lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+ }
+ }
+ }
+ if (lfr.padding.len) {
+ padded = 1;
+ ngtcp2_log_tx_fr(&conn->log, &hd, &lfr);
+ ngtcp2_qlog_write_frame(&conn->qlog, &lfr);
+ }
+
+ nwrite = ngtcp2_ppe_final(&ppe, NULL);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (type == NGTCP2_PKT_1RTT) {
+ ++cc.ckm->use_count;
+ }
+
+ ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite);
+
+ /* Do this when we are sure that there is no error. */
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ ngtcp2_acktr_commit_ack(&pktns->acktr);
+ ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack);
+ if (type == NGTCP2_PKT_1RTT) {
+ conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack,
+ ts);
+ }
+ break;
+ }
+
+ if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
+ (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) {
+ if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)) {
+ conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
+ }
+
+ rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite,
+ rtb_entry_flags,
+ &conn->rtb_entry_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
+ if (rv != 0) {
+ ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
+ conn_restart_timer_on_write(conn, ts);
+ }
+
+ if (pktns->rtb.probe_pkt_left && path &&
+ ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ --pktns->rtb.probe_pkt_left;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td",
+ nwrite);
+ }
+ }
+ } else if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) &&
+ conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+ conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
+ }
+
+ if (path && ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn_update_keep_alive_last_ts(conn, ts);
+ }
+
+ if (!padded) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ break;
+ default:
+ conn->tx.pacing.pktlen += (size_t)nwrite;
+ }
+ } else {
+ conn->tx.pacing.pktlen += (size_t)nwrite;
+ }
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ ++pktns->tx.last_pkt_num;
+
+ return nwrite;
+}
+
+/*
+ * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet.
+ */
+static void conn_process_early_rtb(ngtcp2_conn *conn) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_rtb *rtb = &conn->pktns.rtb;
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_rtb_head(rtb); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if ((ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) == 0 ||
+ ent->hd.type != NGTCP2_PKT_0RTT) {
+ continue;
+ }
+
+ /* 0-RTT packet is retransmitted as a 1RTT packet. */
+ ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM;
+ ent->hd.type = NGTCP2_PKT_1RTT;
+ }
+}
+
+/*
+ * conn_handshake_remnants_left returns nonzero if there may be
+ * handshake packets the local endpoint has to send, including new
+ * packets and lost ones.
+ */
+static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+
+ return !conn_is_handshake_completed(conn) ||
+ (in_pktns && (in_pktns->rtb.num_pto_eliciting ||
+ ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) ||
+ (hs_pktns && (hs_pktns->rtb.num_pto_eliciting ||
+ ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq)));
+}
+
+/*
+ * conn_retire_dcid_seq retires destination connection ID denoted by
+ * |seq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ * The number of unacknowledged retirement exceeds the limit.
+ */
+static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_frame_chain *nfrc;
+ int rv;
+
+ rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ nfrc->fr.retire_connection_id.seq = seq;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ return 0;
+}
+
+/*
+ * conn_retire_dcid retires |dcid|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ringbuf *rb = &conn->dcid.retired.rb;
+ ngtcp2_dcid *dest, *stale_dcid;
+ int rv;
+
+ assert(dcid->cid.datalen);
+
+ if (ngtcp2_ringbuf_full(rb)) {
+ stale_dcid = ngtcp2_ringbuf_get(rb, 0);
+ rv = conn_call_deactivate_dcid(conn, stale_dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(rb);
+ }
+
+ dest = ngtcp2_ringbuf_push_back(rb);
+ ngtcp2_dcid_copy(dest, dcid);
+ dest->retired_ts = ts;
+
+ return conn_retire_dcid_seq(conn, dcid->seq);
+}
+
+/*
+ * conn_bind_dcid stores the DCID to |*pdcid| bound to |path|. If
+ * such DCID is not found, bind the new DCID to |path| and stores it
+ * to |*pdcid|. If a remote endpoint uses zero-length connection ID,
+ * the pointer to conn->dcid.current is assigned to |*pdcid|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No unused DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
+ const ngtcp2_path *path, ngtcp2_tstamp ts) {
+ ngtcp2_dcid *dcid, *ndcid;
+ ngtcp2_cid cid;
+ size_t i, len;
+ int rv;
+
+ assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path));
+ assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path));
+ assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
+ !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path));
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+
+ if (ngtcp2_path_eq(&dcid->ps.path, path)) {
+ *pdcid = dcid;
+ return 0;
+ }
+ }
+
+ if (conn->dcid.current.cid.datalen == 0) {
+ ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
+ ngtcp2_cid_zero(&cid);
+ ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL);
+ ngtcp2_dcid_set_path(ndcid, path);
+
+ *pdcid = ndcid;
+
+ return 0;
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
+ return NGTCP2_ERR_CONN_ID_BLOCKED;
+ }
+
+ if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0);
+ rv = conn_retire_dcid(conn, dcid, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
+
+ ngtcp2_dcid_copy(ndcid, dcid);
+ ndcid->bound_ts = ts;
+ ngtcp2_dcid_set_path(ndcid, path);
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ *pdcid = ndcid;
+
+ return 0;
+}
+
+static int conn_start_pmtud(ngtcp2_conn *conn) {
+ int rv;
+ size_t hard_max_udp_payload_size;
+
+ assert(!conn->local.settings.no_pmtud);
+ assert(!conn->pmtud);
+ assert(conn_is_handshake_completed(conn));
+ assert(conn->remote.transport_params);
+ assert(conn->remote.transport_params->max_udp_payload_size >=
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ hard_max_udp_payload_size = (size_t)ngtcp2_min(
+ conn->remote.transport_params->max_udp_payload_size,
+ (uint64_t)conn->local.settings.max_tx_udp_payload_size);
+
+ rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size,
+ hard_max_udp_payload_size,
+ conn->pktns.tx.last_pkt_num + 1, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) {
+ return conn_start_pmtud(conn);
+}
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) {
+ if (!conn->pmtud) {
+ return;
+ }
+
+ ngtcp2_pmtud_del(conn->pmtud);
+
+ conn->pmtud = NULL;
+}
+
+static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ size_t probelen;
+ ngtcp2_ssize nwrite;
+ ngtcp2_frame lfr;
+
+ assert(conn->pmtud);
+ assert(!ngtcp2_pmtud_finished(conn->pmtud));
+
+ if (!ngtcp2_pmtud_require_probe(conn->pmtud)) {
+ return 0;
+ }
+
+ probelen = ngtcp2_pmtud_probelen(conn->pmtud);
+ if (probelen > destlen) {
+ return 0;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "sending PMTUD probe packet len=%zu", probelen);
+
+ lfr.type = NGTCP2_FRAME_PING;
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, probelen, NGTCP2_PKT_1RTT,
+ NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr,
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+ NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE,
+ NULL, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ assert(nwrite);
+
+ ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns),
+ ts);
+
+ return nwrite;
+}
+
+/*
+ * conn_stop_pv stops the path validation which is currently running.
+ * This function does nothing if no path validation is currently being
+ * performed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv = 0;
+ ngtcp2_pv *pv = conn->pv;
+
+ if (pv == NULL) {
+ return 0;
+ }
+
+ if (pv->dcid.cid.datalen && pv->dcid.seq != conn->dcid.current.seq) {
+ rv = conn_retire_dcid(conn, &pv->dcid, ts);
+ if (rv != 0) {
+ goto fin;
+ }
+ }
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.cid.datalen &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
+ if (rv != 0) {
+ goto fin;
+ }
+ }
+
+fin:
+ ngtcp2_pv_del(pv);
+ conn->pv = NULL;
+
+ return rv;
+}
+
+/*
+ * conn_abort_pv aborts the current path validation and frees
+ * resources allocated for it. This function assumes that conn->pv is
+ * not NULL.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ int rv;
+
+ assert(pv);
+
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ rv = conn_call_path_validation(conn, pv,
+ NGTCP2_PATH_VALIDATION_RESULT_ABORTED);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return conn_stop_pv(conn, ts);
+}
+
+static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ size_t payloadlen) {
+ if (conn->remote.transport_params &&
+ conn->remote.transport_params->max_udp_payload_size) {
+ assert(conn->remote.transport_params->max_udp_payload_size >=
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ payloadlen =
+ (size_t)ngtcp2_min((uint64_t)payloadlen,
+ conn->remote.transport_params->max_udp_payload_size);
+ }
+
+ payloadlen =
+ ngtcp2_min(payloadlen, conn->local.settings.max_tx_udp_payload_size);
+
+ if (conn->local.settings.no_tx_udp_payload_size_shaping) {
+ return payloadlen;
+ }
+
+ return ngtcp2_min(payloadlen, dcid->max_udp_payload_size);
+}
+
+static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
+/*
+ * conn_on_path_validation_failed is called when path validation
+ * fails. This function may delete |pv|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv,
+ ngtcp2_tstamp ts) {
+ int rv;
+
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ rv = conn_call_path_validation(conn, pv,
+ NGTCP2_PATH_VALIDATION_RESULT_FAILURE);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_MTU_PROBE) {
+ return NGTCP2_ERR_NO_VIABLE_PATH;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid);
+ conn_reset_congestion_state(conn, ts);
+ }
+
+ return conn_stop_pv(conn, ts);
+}
+
+/*
+ * conn_write_path_challenge writes a packet which includes
+ * PATH_CHALLENGE frame into |dest| of length |destlen|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nwrite;
+ ngtcp2_tstamp expiry;
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_frame lfr;
+ ngtcp2_duration timeout;
+ uint8_t flags;
+ uint64_t tx_left;
+ int rv;
+
+ if (ngtcp2_pv_validation_timed_out(pv, ts)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path validation was timed out");
+ rv = conn_on_path_validation_failed(conn, pv, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* We might set path to the one which we just failed validate.
+ Set it to the current path here. */
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ return 0;
+ }
+
+ ngtcp2_pv_handle_entry_expiry(pv, ts);
+
+ if (!ngtcp2_pv_should_send_probe(pv)) {
+ return 0;
+ }
+
+ rv = conn_call_get_path_challenge_data(conn, lfr.path_challenge.data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ lfr.type = NGTCP2_FRAME_PATH_CHALLENGE;
+
+ timeout = conn_compute_pto(conn, &conn->pktns);
+ timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt);
+ expiry = ts + timeout * (1ULL << pv->round);
+
+ destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ if (conn->server) {
+ if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ tx_left = conn_server_tx_left(conn, &pv->dcid);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left);
+ if (destlen == 0) {
+ return 0;
+ }
+ }
+
+ if (destlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED;
+ } else {
+ flags = NGTCP2_PV_ENTRY_FLAG_NONE;
+ }
+ } else {
+ flags = NGTCP2_PV_ENTRY_FLAG_NONE;
+ }
+
+ ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts);
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &pv->dcid.cid, &lfr,
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING,
+ &pv->dcid.ps.path, ts);
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &pv->dcid.ps.path);
+ }
+
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) {
+ conn->dcid.current.bytes_sent += (uint64_t)nwrite;
+ } else {
+ pv->dcid.bytes_sent += (uint64_t)nwrite;
+ }
+
+ return nwrite;
+}
+
+/*
+ * conn_write_path_response writes a packet which includes
+ * PATH_RESPONSE frame into |dest| of length |destlen|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_path_challenge_entry *pcent = NULL;
+ ngtcp2_dcid *dcid = NULL;
+ ngtcp2_frame lfr;
+ ngtcp2_ssize nwrite;
+ int rv;
+ uint64_t tx_left;
+
+ for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) {
+ pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
+ /* Send PATH_RESPONSE from conn_write_pkt. */
+ return 0;
+ }
+
+ if (pv) {
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &pcent->ps.path)) {
+ dcid = &pv->dcid;
+ break;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&pv->fallback_dcid.ps.path, &pcent->ps.path)) {
+ dcid = &pv->fallback_dcid;
+ break;
+ }
+ }
+
+ if (conn->server) {
+ break;
+ }
+
+ /* Client does not expect to respond to path validation against
+ unknown path */
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
+ pcent = NULL;
+ }
+
+ if (pcent == NULL) {
+ return 0;
+ }
+
+ if (dcid == NULL) {
+ /* client is expected to have |path| in conn->dcid.current or
+ conn->pv. */
+ assert(conn->server);
+
+ rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return 0;
+ }
+ }
+
+ destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+ if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ tx_left = conn_server_tx_left(conn, dcid);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, tx_left);
+ if (destlen == 0) {
+ return 0;
+ }
+ }
+
+ lfr.type = NGTCP2_FRAME_PATH_RESPONSE;
+ memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data));
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path,
+ ts);
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &pcent->ps.path);
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
+
+ dcid->bytes_sent += (uint64_t)nwrite;
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_tstamp ts) {
+ return ngtcp2_conn_writev_stream_versioned(
+ conn, path, pkt_info_version, pi, dest, destlen,
+ /* pdatalen = */ NULL, NGTCP2_WRITE_STREAM_FLAG_NONE,
+ /* stream_id = */ -1,
+ /* datav = */ NULL, /* datavcnt = */ 0, ts);
+}
+
+/*
+ * conn_on_version_negotiation is called when Version Negotiation
+ * packet is received. The function decodes the data in the buffer
+ * pointed by |payload| whose length is |payloadlen| as Version
+ * Negotiation packet payload. The packet header is given in |hd|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Packet payload is badly formatted.
+ */
+static int conn_on_version_negotiation(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ uint32_t sv[16];
+ uint32_t *p;
+ int rv = 0;
+ size_t nsv;
+ size_t i;
+
+ if (payloadlen % sizeof(uint32_t)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ /* Version Negotiation packet is ignored if client has reacted upon
+ Version Negotiation packet. */
+ if (conn->local.settings.original_version != conn->client_chosen_version) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (payloadlen > sizeof(sv)) {
+ p = ngtcp2_mem_malloc(conn->mem, payloadlen);
+ if (p == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ } else {
+ p = sv;
+ }
+
+ nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen);
+
+ ngtcp2_log_rx_vn(&conn->log, hd, p, nsv);
+
+ ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv);
+
+ if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) {
+ for (i = 0; i < nsv; ++i) {
+ if (p[i] == conn->local.settings.original_version) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "ignore Version Negotiation because it contains the "
+ "original version");
+
+ rv = NGTCP2_ERR_INVALID_ARGUMENT;
+ goto fin;
+ }
+ }
+ }
+
+ rv = conn_call_recv_version_negotiation(conn, hd, p, nsv);
+ if (rv != 0) {
+ goto fin;
+ }
+
+fin:
+ if (p != sv) {
+ ngtcp2_mem_free(conn->mem, p);
+ }
+
+ return rv;
+}
+
+static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_pq_empty(&conn->tx.strmq)) {
+ return 0;
+ }
+
+ strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+ return strm->cycle;
+}
+
+uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_pq_empty(&conn->tx.strmq)) {
+ return 0;
+ }
+
+ strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+ return strm->cycle;
+}
+
+int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain **first = pfrc;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_stream *sfr;
+ ngtcp2_strm *strm;
+ int rv;
+ int streamfrq_empty;
+
+ if (*pfrc == NULL) {
+ return 0;
+ }
+
+ for (; *pfrc;) {
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+ sfr = &frc->fr.stream;
+
+ strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
+ if (!strm) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ break;
+ }
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
+ rv = ngtcp2_strm_streamfrq_push(strm, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &frc->fr.crypto.offset, frc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+ break;
+ default:
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ *pfrc = pktns->tx.frq;
+ pktns->tx.frq = *first;
+
+ return 0;
+}
+
+/*
+ * conn_on_retry is called when Retry packet is received. The
+ * function decodes the data in the buffer pointed by |pkt| whose
+ * length is |pktlen| as Retry packet. The length of long packet
+ * header is given in |hdpktlen|. |pkt| includes packet header.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Packet payload is badly formatted.
+ * NGTCP2_ERR_PROTO
+ * ODCID does not match; or Token is empty.
+ */
+static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ size_t hdpktlen, const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_pkt_retry retry;
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_rtb *rtb = &conn->pktns.rtb;
+ ngtcp2_rtb *in_rtb;
+ uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1];
+ uint8_t *token;
+
+ if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) {
+ return 0;
+ }
+
+ in_rtb = &in_pktns->rtb;
+
+ rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ retry.odcid = conn->dcid.current.cid;
+
+ rv = ngtcp2_pkt_verify_retry_tag(
+ conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt,
+ &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx);
+ if (rv != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "unable to verify Retry packet integrity");
+ return rv;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s",
+ (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data,
+ retry.odcid.datalen));
+
+ if (retry.tokenlen == 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) {
+ return 0;
+ }
+
+ ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry);
+
+ /* DCID must be updated before invoking callback because client
+ generates new initial keys there. */
+ conn->dcid.current.cid = hd->scid;
+ conn->retry_scid = hd->scid;
+
+ conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY;
+
+ rv = conn_call_recv_retry(conn, hd);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->state = NGTCP2_CS_CLIENT_INITIAL;
+
+ /* Just freeing memory is dangerous because we might free twice. */
+
+ rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_mem_free(conn->mem, (uint8_t *)conn->local.settings.token);
+ conn->local.settings.token = NULL;
+ conn->local.settings.tokenlen = 0;
+
+ token = ngtcp2_mem_malloc(conn->mem, retry.tokenlen);
+ if (token == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_cpymem(token, retry.token, retry.tokenlen);
+
+ conn->local.settings.token = token;
+ conn->local.settings.tokenlen = retry.tokenlen;
+
+ reset_conn_stat_recovery(&conn->cstat);
+ conn_reset_congestion_state(conn, ts);
+ conn_reset_ecn_validation_state(conn);
+
+ return 0;
+}
+
+int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ return ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, ts);
+}
+
+/*
+ * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_PROTO
+ * |fr| acknowledges a packet this endpoint has not sent.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_frame_chain *frc = NULL;
+ ngtcp2_ssize num_acked;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+
+ if (pktns->tx.last_pkt_num < fr->largest_ack) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ rv = ngtcp2_pkt_validate_ack(fr);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_acktr_recv_ack(&pktns->acktr, fr);
+
+ num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns,
+ pkt_ts, ts);
+ if (num_acked < 0) {
+ /* TODO assert this */
+ assert(ngtcp2_err_is_fatal((int)num_acked));
+ ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return (int)num_acked;
+ }
+
+ if (num_acked == 0) {
+ return 0;
+ }
+
+ pktns->rtb.probe_pkt_left = 0;
+
+ if (cstat->pto_count &&
+ (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) {
+ /* Reset PTO count but no less than 2 to avoid frequent probe
+ packet transmission. */
+ cstat->pto_count = ngtcp2_min(cstat->pto_count, 2);
+ }
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+/*
+ * conn_assign_recved_ack_delay_unscaled assigns
+ * fr->ack_delay_unscaled.
+ */
+static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr,
+ uint64_t ack_delay_exponent) {
+ fr->ack_delay_unscaled =
+ fr->ack_delay * (1ULL << ack_delay_exponent) * NGTCP2_MICROSECONDS;
+}
+
+/*
+ * conn_recv_max_stream_data processes received MAX_STREAM_DATA frame
+ * |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * Stream ID indicates that it is a local stream, and the local
+ * endpoint has not initiated it; or stream is peer initiated
+ * unidirectional stream.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * Stream ID exceeds allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_recv_max_stream_data(ngtcp2_conn *conn,
+ const ngtcp2_max_stream_data *fr) {
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+ int rv;
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ /* Stream has been closed. */
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ /* Stream has been closed. */
+ return 0;
+ }
+
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
+ if (rv != 0) {
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (strm->tx.max_offset < fr->max_stream_data) {
+ strm->tx.max_offset = fr->max_stream_data;
+
+ /* Don't call callback if stream is half-closed local */
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ return 0;
+ }
+
+ rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id,
+ fr->max_stream_data);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_max_data processes received MAX_DATA frame |fr|.
+ */
+static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) {
+ conn->tx.max_offset = ngtcp2_max(conn->tx.max_offset, fr->max_data);
+}
+
+/*
+ * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from
+ * |*ppc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ const ngtcp2_path *path, const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc;
+ size_t i;
+ for (i = 0; *ppc && i < NGTCP2_MAX_NUM_BUFFED_RX_PKTS;
+ ppc = &(*ppc)->next, ++i)
+ ;
+
+ if (i == NGTCP2_MAX_NUM_BUFFED_RX_PKTS) {
+ return 0;
+ }
+
+ rv =
+ ngtcp2_pkt_chain_new(&pc, path, pi, pkt, pktlen, dgramlen, ts, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ *ppc = pc;
+
+ return 0;
+}
+
+static int ensure_decrypt_buffer(ngtcp2_vec *vec, size_t n, size_t initial,
+ const ngtcp2_mem *mem) {
+ uint8_t *nbuf;
+ size_t len;
+
+ if (vec->len >= n) {
+ return 0;
+ }
+
+ len = vec->len == 0 ? initial : vec->len * 2;
+ for (; len < n; len *= 2)
+ ;
+ nbuf = ngtcp2_mem_realloc(mem, vec->base, len);
+ if (nbuf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ vec->base = nbuf;
+ vec->len = len;
+
+ return 0;
+}
+
+/*
+ * conn_ensure_decrypt_hp_buffer ensures that
+ * conn->crypto.decrypt_hp_buf has at least |n| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_decrypt_hp_buffer(ngtcp2_conn *conn, size_t n) {
+ return ensure_decrypt_buffer(&conn->crypto.decrypt_hp_buf, n, 256, conn->mem);
+}
+
+/*
+ * conn_ensure_decrypt_buffer ensures that conn->crypto.decrypt_buf
+ * has at least |n| bytes space.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_ensure_decrypt_buffer(ngtcp2_conn *conn, size_t n) {
+ return ensure_decrypt_buffer(&conn->crypto.decrypt_buf, n, 2048, conn->mem);
+}
+
+/*
+ * decrypt_pkt decrypts the data pointed by |payload| whose length is
+ * |payloadlen|, and writes plaintext data to the buffer pointed by
+ * |dest|. The buffer pointed by |aad| is the Additional
+ * Authenticated Data, and its length is |aadlen|. |pkt_num| is used
+ * to create a nonce. |ckm| is the cryptographic key, and iv to use.
+ * |decrypt| is a callback function which actually decrypts a packet.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ * NGTCP2_ERR_DECRYPT
+ * Failed to decrypt a packet.
+ */
+static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const uint8_t *payload, size_t payloadlen,
+ const uint8_t *aad, size_t aadlen,
+ int64_t pkt_num, ngtcp2_crypto_km *ckm,
+ ngtcp2_decrypt decrypt) {
+ /* TODO nonce is limited to 64 bytes. */
+ uint8_t nonce[64];
+ int rv;
+
+ assert(sizeof(nonce) >= ckm->iv.len);
+
+ ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num);
+
+ rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce,
+ ckm->iv.len, aad, aadlen);
+
+ if (rv != 0) {
+ if (rv == NGTCP2_ERR_DECRYPT) {
+ return rv;
+ }
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ assert(payloadlen >= aead->max_overhead);
+
+ return (ngtcp2_ssize)(payloadlen - aead->max_overhead);
+}
+
+/*
+ * decrypt_hp decryptes packet header. The packet number starts at
+ * |pkt| + |pkt_num_offset|. The entire plaintext QUIC packet header
+ * will be written to the buffer pointed by |dest| whose capacity is
+ * |destlen|.
+ *
+ * This function returns the number of bytes written to |dest|, or one
+ * of the following negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Packet is badly formatted
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed; or it does not return
+ * expected result.
+ */
+static ngtcp2_ssize
+decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) {
+ size_t sample_offset;
+ uint8_t *p = dest;
+ uint8_t mask[NGTCP2_HP_SAMPLELEN];
+ size_t i;
+ int rv;
+
+ assert(hp_mask);
+
+ if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ p = ngtcp2_cpymem(p, pkt, pkt_num_offset);
+
+ sample_offset = pkt_num_offset + 4;
+
+ rv = hp_mask(mask, hp, hp_ctx, pkt + sample_offset);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f));
+ } else {
+ dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f));
+ if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) {
+ hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE;
+ }
+ }
+
+ hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1);
+
+ for (i = 0; i < hd->pkt_numlen; ++i) {
+ *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1];
+ }
+
+ hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen);
+
+ return p - dest;
+}
+
+/*
+ * conn_emit_pending_crypto_data delivers pending stream data to the
+ * application due to packet reordering.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed
+ * NGTCP2_ERR_CRYPTO
+ * TLS backend reported error
+ */
+static int conn_emit_pending_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *strm,
+ uint64_t rx_offset) {
+ size_t datalen;
+ const uint8_t *data;
+ int rv;
+ uint64_t offset;
+
+ if (!strm->rx.rob) {
+ return 0;
+ }
+
+ for (;;) {
+ datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset);
+ if (datalen == 0) {
+ assert(rx_offset == ngtcp2_strm_rx_offset(strm));
+ return 0;
+ }
+
+ offset = rx_offset;
+ rx_offset += datalen;
+
+ rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen);
+ }
+}
+
+/*
+ * conn_recv_connection_close is called when CONNECTION_CLOSE or
+ * APPLICATION_CLOSE frame is received.
+ */
+static int conn_recv_connection_close(ngtcp2_conn *conn,
+ ngtcp2_connection_close *fr) {
+ ngtcp2_connection_close_error *ccerr = &conn->rx.ccerr;
+
+ conn->state = NGTCP2_CS_DRAINING;
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT;
+ } else {
+ ccerr->type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION;
+ }
+ ccerr->error_code = fr->error_code;
+ ccerr->frame_type = fr->frame_type;
+
+ if (!fr->reasonlen) {
+ ccerr->reasonlen = 0;
+
+ return 0;
+ }
+
+ if (ccerr->reason == NULL) {
+ ccerr->reason = ngtcp2_mem_malloc(
+ conn->mem, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN);
+ if (ccerr->reason == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ }
+
+ ccerr->reasonlen =
+ ngtcp2_min(fr->reasonlen, NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN);
+ ngtcp2_cpymem((uint8_t *)ccerr->reason, fr->reason, ccerr->reasonlen);
+
+ return 0;
+}
+
+static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_path_challenge *fr) {
+ ngtcp2_path_challenge_entry *ent;
+
+ /* client only responds to PATH_CHALLENGE from the current path or
+ path which client is migrating to. */
+ if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
+ (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path))) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "discard PATH_CHALLENGE from the path which is not current "
+ "or endpoint is migrating to");
+ return;
+ }
+
+ ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb);
+ ngtcp2_path_challenge_entry_init(ent, path, fr->data);
+}
+
+/*
+ * conn_reset_congestion_state resets congestion state.
+ */
+static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ conn_reset_conn_stat_cc(conn, &conn->cstat);
+
+ conn->cc.reset(&conn->cc, &conn->cstat, ts);
+
+ if (conn->hs_pktns) {
+ ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb,
+ conn->hs_pktns->tx.last_pkt_num + 1);
+ }
+ ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1);
+ ngtcp2_rst_init(&conn->rst);
+
+ conn->tx.pacing.next_ts = UINT64_MAX;
+}
+
+static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_duration pto, timeout;
+ ngtcp2_pv *pv = conn->pv, *npv;
+ uint8_t ent_flags;
+
+ if (!pv) {
+ return 0;
+ }
+
+ rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data);
+ if (rv != 0) {
+ assert(!ngtcp2_err_is_fatal(rv));
+
+ return 0;
+ }
+
+ if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) {
+ if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ assert(conn->dcid.current.cid.datalen);
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
+ }
+ conn_reset_congestion_state(conn, ts);
+ conn_reset_ecn_validation_state(conn);
+ }
+
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) {
+ conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+
+ if (!conn->local.settings.no_pmtud) {
+ ngtcp2_conn_stop_pmtud(conn);
+
+ if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ rv = conn_call_path_validation(conn, pv,
+ NGTCP2_PATH_VALIDATION_RESULT_SUCCESS);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+ pto = conn_compute_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, pv->fallback_pto);
+
+ if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) {
+ assert(conn->server);
+
+ /* Validate path again */
+ rv = ngtcp2_pv_new(&npv, &pv->dcid, timeout,
+ NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE |
+ NGTCP2_PV_FLAG_MTU_PROBE,
+ &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ ngtcp2_dcid_copy(&npv->fallback_dcid, &pv->fallback_dcid);
+ npv->fallback_pto = pv->fallback_pto;
+ } else {
+ rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, timeout,
+ NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ /* Unset the flag bit so that conn_stop_pv does not retire
+ DCID. */
+ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE;
+
+ rv = conn_stop_pv(conn, ts);
+ if (rv != 0) {
+ ngtcp2_pv_del(npv);
+ return rv;
+ }
+
+ conn->pv = npv;
+
+ return 0;
+ }
+
+ return conn_stop_pv(conn, ts);
+}
+
+/*
+ * pkt_num_bits returns the number of bits available when packet
+ * number is encoded in |pkt_numlen| bytes.
+ */
+static size_t pkt_num_bits(size_t pkt_numlen) {
+ switch (pkt_numlen) {
+ case 1:
+ return 8;
+ case 2:
+ return 16;
+ case 3:
+ return 24;
+ case 4:
+ return 32;
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+/*
+ * pktns_pkt_num_is_duplicate returns nonzero if |pkt_num| is
+ * duplicated packet number.
+ */
+static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) {
+ return ngtcp2_gaptr_is_pushed(&pktns->rx.pngap, (uint64_t)pkt_num, 1);
+}
+
+/*
+ * pktns_commit_recv_pkt_num marks packet number |pkt_num| as
+ * received.
+ */
+static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num,
+ int ack_eliciting, ngtcp2_tstamp ts) {
+ int rv;
+
+ if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num + 1 != pkt_num) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+ if (pktns->rx.max_pkt_num < pkt_num) {
+ pktns->rx.max_pkt_num = pkt_num;
+ pktns->rx.max_pkt_ts = ts;
+ }
+ if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num < pkt_num) {
+ pktns->rx.max_ack_eliciting_pkt_num = pkt_num;
+ }
+
+ rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) {
+ ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap);
+ }
+
+ return 0;
+}
+
+/*
+ * verify_token verifies |hd| contains |token| in its token field. It
+ * returns 0 if it succeeds, or NGTCP2_ERR_PROTO.
+ */
+static int verify_token(const uint8_t *token, size_t tokenlen,
+ const ngtcp2_pkt_hd *hd) {
+ if (tokenlen == hd->tokenlen && ngtcp2_cmemeq(token, hd->token, tokenlen)) {
+ return 0;
+ }
+ return NGTCP2_ERR_PROTO;
+}
+
+static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns,
+ const ngtcp2_pkt_info *pi) {
+ switch (pi->ecn & NGTCP2_ECN_MASK) {
+ case NGTCP2_ECN_ECT_0:
+ ++pktns->rx.ecn.ect0;
+ break;
+ case NGTCP2_ECN_ECT_1:
+ ++pktns->rx.ecn.ect1;
+ break;
+ case NGTCP2_ECN_CE:
+ ++pktns->rx.ecn.ce;
+ break;
+ }
+}
+
+/*
+ * vneg_available_versions_includes returns nonzero if
+ * |available_versions| of length |available_versionslen| includes
+ * |version|. |available_versions| is the wire image of
+ * available_versions field of version_information transport
+ * parameter, and each version is encoded in network byte order.
+ */
+static int vneg_available_versions_includes(const uint8_t *available_versions,
+ size_t available_versionslen,
+ uint32_t version) {
+ size_t i;
+ uint32_t v;
+
+ assert(!(available_versionslen & 0x3));
+
+ if (available_versionslen == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < available_versionslen; i += sizeof(uint32_t)) {
+ available_versions = ngtcp2_get_uint32(&v, available_versions);
+
+ if (version == v) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *strm, const ngtcp2_crypto *fr);
+
+static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts);
+
+static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts);
+
+/*
+ * conn_recv_handshake_pkt processes received packet |pkt| whose
+ * length is |pktlen| during handshake period. The buffer pointed by
+ * |pkt| might contain multiple packets. This function only processes
+ * one packet. |pkt_ts| is the timestamp when packet is received.
+ * |ts| should be the current time. Usually they are the same, but
+ * for buffered packets, |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of bytes it reads if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_RECV_VERSION_NEGOTIATION
+ * Version Negotiation packet is received.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded because plain text header was malformed;
+ * or its payload could not be decrypted.
+ * NGTCP2_ERR_FRAME_FORMAT
+ * Frame is badly formatted
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_CRYPTO
+ * TLS stack reported error.
+ * NGTCP2_ERR_PROTO
+ * Generic QUIC protocol error.
+ *
+ * In addition to the above error codes, error codes returned from
+ * conn_recv_pkt are also returned.
+ */
+static ngtcp2_ssize
+conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp pkt_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int rv;
+ int require_ack = 0;
+ size_t hdpktlen;
+ const uint8_t *payload;
+ size_t payloadlen;
+ ngtcp2_ssize nwrite;
+ ngtcp2_crypto_aead *aead;
+ ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx *hp_ctx;
+ ngtcp2_hp_mask hp_mask;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_pktns *pktns;
+ ngtcp2_strm *crypto;
+ ngtcp2_crypto_level crypto_level;
+ int invalid_reserved_bits = 0;
+
+ if (pktlen == 0) {
+ return 0;
+ }
+
+ if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) {
+ if (conn->state == NGTCP2_CS_SERVER_INITIAL) {
+ /* Ignore 1RTT packet unless server's first Handshake packet has
+ been transmitted. */
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ if (conn->pktns.crypto.rx.ckm) {
+ return 0;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 1RTT packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen,
+ ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen);
+ if (nread < 0) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) {
+ hdpktlen = (size_t)nread;
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Receiving Version Negotiation packet after getting Handshake
+ packet from server is invalid. */
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!ngtcp2_cid_eq(&conn->oscid, &hd.dcid)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ /* Just discard invalid Version Negotiation packet */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ rv = conn_on_version_negotiation(conn, &hd, pkt + hdpktlen,
+ pktlen - hdpktlen);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return NGTCP2_ERR_RECV_VERSION_NEGOTIATION;
+ } else if (hd.type == NGTCP2_PKT_RETRY) {
+ hdpktlen = (size_t)nread;
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Receiving Retry packet after getting Initial packet from server
+ is invalid. */
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (conn->client_chosen_version != hd.version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ if (pktlen < (size_t)nread + hd.len) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktlen = (size_t)nread + hd.len;
+
+ if (!ngtcp2_is_supported_version(hd.version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (conn->server) {
+ if (hd.version != conn->client_chosen_version &&
+ (!conn->negotiated_version || hd.version != conn->negotiated_version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ } else if (hd.version != conn->client_chosen_version &&
+ conn->negotiated_version &&
+ hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Quoted from spec: if subsequent packets of those types include a
+ different Source Connection ID, they MUST be discarded. */
+ if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) &&
+ !ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ switch (hd.type) {
+ case NGTCP2_PKT_0RTT:
+ if (!conn->server) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (hd.version != conn->client_chosen_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
+ if (conn->early.ckm) {
+ ngtcp2_ssize nread2;
+ /* TODO Avoid to parse header twice. */
+ nread2 =
+ conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, pkt_ts, ts);
+ if (nread2 < 0) {
+ return nread2;
+ }
+ }
+
+ /* Discard 0-RTT packet if we don't have a key to decrypt it. */
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ /* Buffer re-ordered 0-RTT packet. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 0-RTT packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, conn->in_pktns, path, pi, pkt, pktlen, dgramlen,
+ ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->in_pktns) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Initial packet is discarded because keys have been discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ assert(conn->in_pktns);
+
+ if (conn->server) {
+ if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Initial packet was ignored because it is included in UDP datagram "
+ "less than %zu bytes: %zu bytes",
+ NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ if (conn->local.settings.tokenlen) {
+ rv = verify_token(conn->local.settings.token,
+ conn->local.settings.tokenlen, &hd);
+ if (rv != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because token is invalid");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) {
+ /* Set rcid here so that it is available to callback. If this
+ packet is discarded later in this function and no packet is
+ processed in this connection attempt so far, connection
+ will be dropped. */
+ conn->rcid = hd.dcid;
+
+ rv = conn_call_recv_client_initial(conn, &hd.dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ if (hd.tokenlen != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because token is not empty");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (hd.version != conn->client_chosen_version &&
+ !conn->negotiated_version && conn->vneg.version != hd.version) {
+ if (!vneg_available_versions_includes(conn->vneg.available_versions,
+ conn->vneg.available_versionslen,
+ hd.version)) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ /* Install new Initial keys using QUIC version = hd.version */
+ rv = conn_call_version_negotiation(
+ conn, hd.version,
+ (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)
+ ? &conn->dcid.current.cid
+ : &conn->rcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ assert(conn->vneg.version == hd.version);
+ }
+ }
+
+ pktns = conn->in_pktns;
+ crypto = &pktns->crypto.strm;
+ crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL;
+
+ if (hd.version == conn->client_chosen_version) {
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ } else {
+ assert(conn->vneg.version == hd.version);
+
+ ckm = conn->vneg.rx.ckm;
+ hp_ctx = &conn->vneg.rx.hp_ctx;
+ }
+
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!conn->hs_pktns->crypto.rx.ckm) {
+ if (conn->server) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PKT,
+ "Handshake packet at this point is unexpected and discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering Handshake packet len=%zu", pktlen);
+
+ rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pi, pkt, pktlen,
+ dgramlen, ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = conn->hs_pktns;
+ crypto = &pktns->crypto.strm;
+ crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+
+ break;
+ default:
+ /* unknown packet type */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of unknown packet type");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ aead = &pktns->crypto.ctx.aead;
+ hp = &pktns->crypto.ctx.hp;
+
+ assert(ckm);
+ assert(hp_mask);
+ assert(decrypt);
+
+ rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
+ (size_t)nread, hp_ctx, hp_mask);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ hdpktlen = (size_t)nwrite;
+ payload = pkt + hdpktlen;
+ payloadlen = hd.len - hd.pkt_numlen;
+
+ hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num,
+ pkt_num_bits(hd.pkt_numlen));
+ if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]);
+ if (rv != 0) {
+ invalid_reserved_bits = 1;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet has incorrect reserved bits");
+
+ /* Will return error after decrypting payload */
+ }
+
+ if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was discarded because of duplicated packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_ensure_decrypt_buffer(conn, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen,
+ conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num,
+ ckm, decrypt);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (invalid_reserved_bits) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (!conn->server && hd.version != conn->client_chosen_version &&
+ !conn->negotiated_version) {
+ conn->negotiated_version = hd.version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ }
+
+ payload = conn->crypto.decrypt_buf.base;
+ payloadlen = (size_t)nwrite;
+
+ switch (hd.type) {
+ case NGTCP2_PKT_INITIAL:
+ if (!conn->server || ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) &&
+ !ngtcp2_cid_eq(&conn->rcid, &hd.dcid))) {
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ if (hd.type == NGTCP2_PKT_INITIAL) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (hd.type == NGTCP2_PKT_INITIAL &&
+ !(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) {
+ conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED;
+ if (!conn->server) {
+ conn->dcid.current.cid = hd.scid;
+ }
+ }
+
+ ngtcp2_qlog_pkt_received_start(&conn->qlog);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ fr->ack.ack_delay = 0;
+ fr->ack.ack_delay_unscaled = 0;
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PADDING:
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ if (!conn->server && !conn->negotiated_version &&
+ ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) {
+ conn->negotiated_version = hd.version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ }
+
+ rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto);
+ if (rv != 0) {
+ return rv;
+ }
+ require_ack = 1;
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PING:
+ require_ack = 1;
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ if (hd.type == NGTCP2_PKT_HANDSHAKE) {
+ /* Successful processing of Handshake packet from a remote
+ endpoint validates its source address. */
+ conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen);
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ /* TODO Initial and Handshake are always acknowledged without
+ delay. */
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
+ : (ngtcp2_ssize)pktlen;
+}
+
+static int is_unrecoverable_error(int liberr) {
+ switch (liberr) {
+ case NGTCP2_ERR_CRYPTO:
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_handshake_cpkt processes compound packet during
+ * handshake. The buffer pointed by |pkt| might contain multiple
+ * packets. The 1RTT packet must be the last one because it does not
+ * have payload length field.
+ *
+ * This function returns the same error code returned by
+ * conn_recv_handshake_pkt.
+ */
+static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ size_t dgramlen = pktlen;
+ const uint8_t *origpkt = pkt;
+ uint32_t version;
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn->dcid.current.bytes_recv += dgramlen;
+ }
+
+ while (pktlen) {
+ nread =
+ conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts);
+ if (nread < 0) {
+ if (ngtcp2_err_is_fatal((int)nread)) {
+ return nread;
+ }
+
+ if (nread == NGTCP2_ERR_DRAINING) {
+ return NGTCP2_ERR_DRAINING;
+ }
+
+ if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) {
+ /* Not a Version Negotiation packet */
+ ngtcp2_get_uint32(&version, &pkt[1]);
+ if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) {
+ if (conn->server) {
+ if (is_unrecoverable_error((int)nread)) {
+ /* If server gets crypto error from TLS stack, it is
+ unrecoverable, therefore drop connection. */
+ return nread;
+ }
+
+ /* If server discards first Initial, then drop connection
+ state. This is because SCID in packet might be corrupted
+ and the current connection state might wrongly discard
+ valid packet and prevent the handshake from
+ completing. */
+ if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) {
+ return NGTCP2_ERR_DROP_CONN;
+ }
+
+ return (ngtcp2_ssize)dgramlen;
+ }
+ /* client */
+ if (is_unrecoverable_error((int)nread)) {
+ /* If client gets crypto error from TLS stack, it is
+ unrecoverable, therefore drop connection. */
+ return nread;
+ }
+ return (ngtcp2_ssize)dgramlen;
+ }
+ }
+
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ return (ngtcp2_ssize)dgramlen;
+ }
+
+ return nread;
+ }
+
+ if (nread == 0) {
+ assert(!(pkt[0] & NGTCP2_HEADER_FORM_BIT));
+ return pkt - origpkt;
+ }
+
+ assert(pktlen >= (size_t)nread);
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
+ }
+
+ return (ngtcp2_ssize)dgramlen;
+}
+
+int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ int64_t stream_id, void *stream_user_data) {
+ int rv;
+ uint64_t max_rx_offset;
+ uint64_t max_tx_offset;
+ int local_stream = conn_local_stream(conn, stream_id);
+
+ assert(conn->remote.transport_params);
+
+ if (bidi_stream(stream_id)) {
+ if (local_stream) {
+ max_rx_offset =
+ conn->local.transport_params.initial_max_stream_data_bidi_local;
+ max_tx_offset =
+ conn->remote.transport_params->initial_max_stream_data_bidi_remote;
+ } else {
+ max_rx_offset =
+ conn->local.transport_params.initial_max_stream_data_bidi_remote;
+ max_tx_offset =
+ conn->remote.transport_params->initial_max_stream_data_bidi_local;
+ }
+ } else if (local_stream) {
+ max_rx_offset = 0;
+ max_tx_offset = conn->remote.transport_params->initial_max_stream_data_uni;
+ } else {
+ max_rx_offset = conn->local.transport_params.initial_max_stream_data_uni;
+ max_tx_offset = 0;
+ }
+
+ ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset,
+ max_tx_offset, stream_user_data, &conn->frc_objalloc,
+ conn->mem);
+
+ rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id,
+ strm);
+ if (rv != 0) {
+ assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_strm_free(strm);
+ return rv;
+}
+
+/*
+ * conn_emit_pending_stream_data passes buffered ordered stream data
+ * to the application. |rx_offset| is the first offset to deliver to
+ * the application. This function assumes that the data up to
+ * |rx_offset| has been delivered already. This function only passes
+ * the ordered data without any gap. If there is a gap, it stops
+ * providing the data to the application, and returns.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t rx_offset) {
+ size_t datalen;
+ const uint8_t *data;
+ int rv;
+ uint64_t offset;
+ uint32_t sdflags;
+ int handshake_completed = conn_is_handshake_completed(conn);
+
+ if (!strm->rx.rob) {
+ return 0;
+ }
+
+ for (;;) {
+ /* Stop calling callback if application has called
+ ngtcp2_conn_shutdown_stream_read() inside the callback.
+ Because it doubly counts connection window. */
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ return 0;
+ }
+
+ datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset);
+ if (datalen == 0) {
+ assert(rx_offset == ngtcp2_strm_rx_offset(strm));
+ return 0;
+ }
+
+ offset = rx_offset;
+ rx_offset += datalen;
+
+ sdflags = NGTCP2_STREAM_DATA_FLAG_NONE;
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ rx_offset == strm->rx.last_offset) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
+ }
+ if (!handshake_completed) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
+ }
+
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen);
+ }
+}
+
+/*
+ * conn_recv_crypto is called when CRYPTO frame |fr| is received.
+ * |rx_offset_base| is the offset in the entire TLS handshake stream.
+ * fr->offset specifies the offset in each encryption level.
+ * |max_rx_offset| is, if it is nonzero, the maximum offset in the
+ * entire TLS handshake stream that |fr| can carry. |crypto_level| is
+ * the encryption level where this data is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * CRYPTO frame has invalid offset.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CRYPTO
+ * TLS stack reported error.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * The end offset exceeds the maximum value.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
+ ngtcp2_strm *crypto, const ngtcp2_crypto *fr) {
+ uint64_t fr_end_offset;
+ uint64_t rx_offset;
+ int rv;
+
+ if (fr->datacnt == 0) {
+ return 0;
+ }
+
+ fr_end_offset = fr->offset + fr->data[0].len;
+
+ if (NGTCP2_MAX_VARINT < fr_end_offset) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ rx_offset = ngtcp2_strm_rx_offset(crypto);
+
+ if (fr_end_offset <= rx_offset) {
+ if (conn->server &&
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) &&
+ crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL) {
+ /* recovery draft: Speeding Up Handshake Completion
+
+ When a server receives an Initial packet containing duplicate
+ CRYPTO data, it can assume the client did not receive all of
+ the server's CRYPTO data sent in Initial packets, or the
+ client's estimated RTT is too small. ... To speed up
+ handshake completion under these conditions, an endpoint MAY
+ send a packet containing unacknowledged CRYPTO data earlier
+ than the PTO expiry, subject to address validation limits;
+ ... */
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT;
+ conn->in_pktns->rtb.probe_pkt_left = 1;
+ conn->hs_pktns->rtb.probe_pkt_left = 1;
+ }
+ return 0;
+ }
+
+ crypto->rx.last_offset = ngtcp2_max(crypto->rx.last_offset, fr_end_offset);
+
+ /* TODO Before dispatching incoming data to TLS stack, make sure
+ that previous data in previous encryption level has been
+ completely sent to TLS stack. Usually, if data is left, it is an
+ error because key is generated after consuming all data in the
+ previous encryption level. */
+ if (fr->offset <= rx_offset) {
+ size_t ncut = (size_t)(rx_offset - fr->offset);
+ const uint8_t *data = fr->data[0].base + ncut;
+ size_t datalen = fr->data[0].len - ncut;
+ uint64_t offset = rx_offset;
+
+ rx_offset += datalen;
+ rv = ngtcp2_strm_update_rx_offset(crypto, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_crypto_data(conn, crypto_level, crypto, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+ }
+
+ if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) {
+ return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED;
+ }
+
+ return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len,
+ fr->offset);
+}
+
+/*
+ * conn_max_data_violated returns nonzero if receiving |datalen|
+ * violates connection flow control on local endpoint.
+ */
+static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) {
+ return conn->rx.max_offset - conn->rx.offset < datalen;
+}
+
+/*
+ * conn_recv_stream is called when STREAM frame |fr| is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * STREAM frame is received for a local stream which is not
+ * initiated; or STREAM frame is received for a local
+ * unidirectional stream
+ * NGTCP2_ERR_STREAM_LIMIT
+ * STREAM frame has remote stream ID which is strictly greater
+ * than the allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated; or the end offset of stream
+ * data is beyond the NGTCP2_MAX_VARINT.
+ * NGTCP2_ERR_FINAL_SIZE
+ * STREAM frame has strictly larger end offset than it is
+ * permitted.
+ */
+static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
+ int rv;
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ uint64_t rx_offset, fr_end_offset;
+ int local_stream;
+ int bidi;
+ uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+ uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE;
+
+ local_stream = conn_local_stream(conn, fr->stream_id);
+ bidi = bidi_stream(fr->stream_id);
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (local_stream) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ if (NGTCP2_MAX_VARINT - datalen < fr->offset) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ /* TODO The stream has been closed. This should be responded
+ with RESET_STREAM, or simply ignored. */
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ /* TODO The stream has been closed. This should be responded
+ with RESET_STREAM, or simply ignored. */
+ return 0;
+ }
+
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ /* TODO Perhaps, call new_stream callback? */
+ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
+ if (rv != 0) {
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ if (!bidi) {
+ ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR);
+ strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED;
+ }
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ fr_end_offset = fr->offset + datalen;
+
+ if (strm->rx.max_offset < fr_end_offset) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ if (strm->rx.last_offset < fr_end_offset) {
+ uint64_t len = fr_end_offset - strm->rx.last_offset;
+
+ if (conn_max_data_violated(conn, len)) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ conn->rx.offset += len;
+
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ ngtcp2_conn_extend_max_offset(conn, len);
+ }
+ }
+
+ rx_offset = ngtcp2_strm_rx_offset(strm);
+
+ if (fr->fin) {
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) {
+ if (strm->rx.last_offset != fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
+ return 0;
+ }
+
+ if (rx_offset == fr_end_offset) {
+ return 0;
+ }
+ } else if (strm->rx.last_offset > fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ } else {
+ strm->rx.last_offset = fr_end_offset;
+
+ ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
+ }
+ } else {
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ strm->rx.last_offset < fr_end_offset) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ strm->rx.last_offset = ngtcp2_max(strm->rx.last_offset, fr_end_offset);
+
+ if (fr_end_offset <= rx_offset) {
+ return 0;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
+ return 0;
+ }
+ }
+
+ if (fr->offset <= rx_offset) {
+ size_t ncut = (size_t)(rx_offset - fr->offset);
+ uint64_t offset = rx_offset;
+ const uint8_t *data;
+ int fin;
+
+ if (fr->datacnt) {
+ data = fr->data[0].base + ncut;
+ datalen -= ncut;
+
+ rx_offset += datalen;
+ rv = ngtcp2_strm_update_rx_offset(strm, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ data = NULL;
+ datalen = 0;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+ }
+
+ fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ rx_offset == strm->rx.last_offset;
+
+ if (fin || datalen) {
+ if (fin) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
+ }
+ if (!conn_is_handshake_completed(conn)) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
+ }
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
+ (size_t)datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (fr->datacnt) {
+ rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len,
+ fr->offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+}
+
+/*
+ * conn_reset_stream adds RESET_STREAM frame to the transmission
+ * queue.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frc->fr.type = NGTCP2_FRAME_RESET_STREAM;
+ frc->fr.reset_stream.stream_id = strm->stream_id;
+ frc->fr.reset_stream.app_error_code = app_error_code;
+ frc->fr.reset_stream.final_size = strm->tx.offset;
+
+ /* TODO This prepends RESET_STREAM to pktns->tx.frq. */
+ frc->next = pktns->tx.frq;
+ pktns->tx.frq = frc;
+
+ return 0;
+}
+
+/*
+ * conn_stop_sending adds STOP_SENDING frame to the transmission
+ * queue.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ frc->fr.type = NGTCP2_FRAME_STOP_SENDING;
+ frc->fr.stop_sending.stream_id = strm->stream_id;
+ frc->fr.stop_sending.app_error_code = app_error_code;
+
+ /* TODO This prepends STOP_SENDING to pktns->tx.frq. */
+ frc->next = pktns->tx.frq;
+ pktns->tx.frq = frc;
+
+ return 0;
+}
+
+/*
+ * handle_max_remote_streams_extension extends
+ * |*punsent_max_remote_streams| by |n| if a condition allows it.
+ */
+static void
+handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams,
+ size_t n) {
+ if (
+#if SIZE_MAX > UINT32_MAX
+ NGTCP2_MAX_STREAMS < n ||
+#endif /* SIZE_MAX > UINT32_MAX */
+ *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) {
+ *punsent_max_remote_streams = NGTCP2_MAX_STREAMS;
+ } else {
+ *punsent_max_remote_streams += n;
+ }
+}
+
+/*
+ * conn_recv_reset_stream is called when RESET_STREAM |fr| is
+ * received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * RESET_STREAM frame is received to the local stream which is not
+ * initiated.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * RESET_STREAM frame has remote stream ID which is strictly
+ * greater than the allowed limit.
+ * NGTCP2_ERR_PROTO
+ * RESET_STREAM frame is received to the local unidirectional
+ * stream
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated; or the final size is beyond the
+ * NGTCP2_MAX_VARINT.
+ * NGTCP2_ERR_FINAL_SIZE
+ * The final offset is strictly larger than it is permitted.
+ */
+static int conn_recv_reset_stream(ngtcp2_conn *conn,
+ const ngtcp2_reset_stream *fr) {
+ ngtcp2_strm *strm;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+ uint64_t datalen;
+ ngtcp2_idtr *idtr;
+ int rv;
+
+ /* TODO share this piece of code */
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (local_stream) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ if (NGTCP2_MAX_VARINT < fr->final_size) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ return 0;
+ }
+
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ return 0;
+ }
+
+ if (conn_initial_stream_rx_offset(conn, fr->stream_id) < fr->final_size ||
+ conn_max_data_violated(conn, fr->final_size)) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ /* Stream is reset before we create ngtcp2_strm object. */
+ conn->rx.offset += fr->final_size;
+ ngtcp2_conn_extend_max_offset(conn, fr->final_size);
+
+ rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size,
+ fr->app_error_code, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* There will be no activity in this stream because we got
+ RESET_STREAM and don't write stream data any further. This
+ effectively allows another new stream for peer. */
+ if (bidi) {
+ handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams,
+ 1);
+ } else {
+ handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams,
+ 1);
+ }
+
+ return 0;
+ }
+
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
+ if (strm->rx.last_offset != fr->final_size) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+ } else if (strm->rx.last_offset > fr->final_size) {
+ return NGTCP2_ERR_FINAL_SIZE;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_RECV_RST) {
+ return 0;
+ }
+
+ if (strm->rx.max_offset < fr->final_size) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ datalen = fr->final_size - strm->rx.last_offset;
+
+ if (conn_max_data_violated(conn, datalen)) {
+ return NGTCP2_ERR_FLOW_CONTROL;
+ }
+
+ rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size,
+ fr->app_error_code, strm->stream_user_data);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Extend connection flow control window for the amount of data
+ which are not passed to application. */
+ if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
+ ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset -
+ ngtcp2_strm_rx_offset(strm));
+ }
+
+ conn->rx.offset += datalen;
+ ngtcp2_conn_extend_max_offset(conn, datalen);
+
+ strm->rx.last_offset = fr->final_size;
+ strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST;
+
+ ngtcp2_strm_set_app_error_code(strm, fr->app_error_code);
+
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+}
+
+/*
+ * conn_recv_stop_sending is called when STOP_SENDING |fr| is received.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_STREAM_STATE
+ * STOP_SENDING frame is received for a local stream which is not
+ * initiated; or STOP_SENDING frame is received for a local
+ * unidirectional stream.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * STOP_SENDING frame has remote stream ID which is strictly
+ * greater than the allowed limit.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_stop_sending(ngtcp2_conn *conn,
+ const ngtcp2_stop_sending *fr) {
+ int rv;
+ ngtcp2_strm *strm;
+ ngtcp2_idtr *idtr;
+ int local_stream = conn_local_stream(conn, fr->stream_id);
+ int bidi = bidi_stream(fr->stream_id);
+
+ if (bidi) {
+ if (local_stream) {
+ if (conn->local.bidi.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+ } else if (conn->remote.bidi.max_streams <
+ ngtcp2_ord_stream_id(fr->stream_id)) {
+ return NGTCP2_ERR_STREAM_LIMIT;
+ }
+
+ idtr = &conn->remote.bidi.idtr;
+ } else {
+ if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) {
+ return NGTCP2_ERR_STREAM_STATE;
+ }
+
+ idtr = &conn->remote.uni.idtr;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, fr->stream_id);
+ if (strm == NULL) {
+ if (local_stream) {
+ return 0;
+ }
+ rv = ngtcp2_idtr_open(idtr, fr->stream_id);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ assert(rv == NGTCP2_ERR_STREAM_IN_USE);
+ return 0;
+ }
+
+ /* Frame is received reset before we create ngtcp2_strm
+ object. */
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
+ if (rv != 0) {
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ rv = conn_call_stream_open(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ ngtcp2_strm_set_app_error_code(strm, fr->app_error_code);
+
+ /* No RESET_STREAM is required if we have sent FIN and all data have
+ been acknowledged. */
+ if (!ngtcp2_strm_is_all_tx_data_fin_acked(strm) &&
+ !(strm->flags & NGTCP2_STRM_FLAG_SENT_RST)) {
+ rv = conn_reset_stream(conn, strm, fr->app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
+
+ if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
+
+ ngtcp2_strm_streamfrq_clear(strm);
+
+ return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+}
+
+/*
+ * check_stateless_reset returns nonzero if Stateless Reset |sr|
+ * coming via |path| is valid against |dcid|.
+ */
+static int check_stateless_reset(const ngtcp2_dcid *dcid,
+ const ngtcp2_path *path,
+ const ngtcp2_pkt_stateless_reset *sr) {
+ return ngtcp2_path_eq(&dcid->ps.path, path) &&
+ ngtcp2_dcid_verify_stateless_reset_token(
+ dcid, sr->stateless_reset_token) == 0;
+}
+
+/*
+ * conn_on_stateless_reset decodes Stateless Reset from the buffer
+ * pointed by |payload| whose length is |payloadlen|. |payload|
+ * should start after first byte of packet.
+ *
+ * If Stateless Reset is decoded, and the Stateless Reset Token is
+ * validated, the connection is closed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Could not decode Stateless Reset; or Stateless Reset Token does
+ * not match; or No stateless reset token is available.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const uint8_t *payload, size_t payloadlen) {
+ int rv = 1;
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *dcid;
+ ngtcp2_pkt_stateless_reset sr;
+ size_t len, i;
+
+ rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!check_stateless_reset(&conn->dcid.current, path, &sr) &&
+ (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) &&
+ (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
+ !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) {
+ len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
+ if (check_stateless_reset(dcid, path, &sr)) {
+ break;
+ }
+ }
+
+ if (i == len) {
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+ if (check_stateless_reset(dcid, path, &sr)) {
+ break;
+ }
+ }
+
+ if (i == len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ }
+ }
+
+ conn->state = NGTCP2_CS_DRAINING;
+
+ ngtcp2_log_rx_sr(&conn->log, &sr);
+
+ ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr);
+
+ return conn_call_recv_stateless_reset(conn, &sr);
+}
+
+/*
+ * conn_recv_max_streams processes the incoming MAX_STREAMS frame
+ * |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * The maximum streams field exceeds the maximum value.
+ */
+static int conn_recv_max_streams(ngtcp2_conn *conn,
+ const ngtcp2_max_streams *fr) {
+ uint64_t n;
+
+ if (fr->max_streams > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_min(fr->max_streams, NGTCP2_MAX_STREAMS);
+
+ if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
+ if (conn->local.bidi.max_streams < n) {
+ conn->local.bidi.max_streams = n;
+ return conn_call_extend_max_local_streams_bidi(conn, n);
+ }
+ return 0;
+ }
+
+ if (conn->local.uni.max_streams < n) {
+ conn->local.uni.max_streams = n;
+ return conn_call_extend_max_local_streams_uni(conn, n);
+ }
+ return 0;
+}
+
+static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb,
+ uint64_t retire_prior_to) {
+ size_t i;
+ ngtcp2_dcid *dcid, *last;
+ int rv;
+
+ for (i = 0; i < ngtcp2_ringbuf_len(rb);) {
+ dcid = ngtcp2_ringbuf_get(rb, i);
+ if (dcid->seq >= retire_prior_to) {
+ ++i;
+ continue;
+ }
+
+ rv = conn_retire_dcid_seq(conn, dcid->seq);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (i == 0) {
+ ngtcp2_ringbuf_pop_front(rb);
+ continue;
+ }
+
+ if (i == ngtcp2_ringbuf_len(rb) - 1) {
+ ngtcp2_ringbuf_pop_back(rb);
+ break;
+ }
+
+ last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
+ ngtcp2_dcid_copy(dcid, last);
+ ngtcp2_ringbuf_pop_back(rb);
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_new_connection_id processes the incoming
+ * NEW_CONNECTION_ID frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * |fr| has the duplicated sequence number with different CID or
+ * token; or DCID is zero-length.
+ */
+static int conn_recv_new_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_new_connection_id *fr) {
+ size_t i, len;
+ ngtcp2_dcid *dcid;
+ ngtcp2_pv *pv = conn->pv;
+ int rv;
+ int found = 0;
+ size_t extra_dcid = 0;
+
+ if (conn->dcid.current.cid.datalen == 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (fr->retire_prior_to > fr->seq) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) {
+ found = 1;
+ }
+
+ if (pv) {
+ rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+ rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i);
+ rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
+ fr->stateless_reset_token);
+ if (rv != 0) {
+ return NGTCP2_ERR_PROTO;
+ }
+ if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) {
+ found = 1;
+ }
+ }
+
+ if (conn->dcid.retire_prior_to < fr->retire_prior_to) {
+ conn->dcid.retire_prior_to = fr->retire_prior_to;
+
+ rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb,
+ fr->retire_prior_to);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb,
+ conn->dcid.retire_prior_to);
+ if (rv != 0) {
+ return rv;
+ }
+ } else if (fr->seq < conn->dcid.retire_prior_to) {
+ /* If packets are reordered, we might have retire_prior_to which
+ is larger than fr->seq.
+
+ A malicious peer might send crafted NEW_CONNECTION_ID to force
+ local endpoint to create lots of RETIRE_CONNECTION_ID frames.
+ For example, a peer might send seq = 50000 and retire_prior_to
+ = 50000. Then send NEW_CONNECTION_ID frames with seq <
+ 50000. */
+ return conn_retire_dcid_seq(conn, fr->seq);
+ }
+
+ if (found) {
+ return 0;
+ }
+
+ if (ngtcp2_gaptr_is_pushed(&conn->dcid.seqgap, fr->seq, 1)) {
+ return 0;
+ }
+
+ rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, fr->seq, 1);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&conn->dcid.seqgap.gap) > 32) {
+ ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap);
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
+
+ if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq &&
+ pv->dcid.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) {
+ ++extra_dcid;
+ }
+ }
+
+ if (conn->local.transport_params.active_connection_id_limit <=
+ len + extra_dcid) {
+ return NGTCP2_ERR_CONNECTION_ID_LIMIT;
+ }
+
+ if (len >= NGTCP2_MAX_DCID_POOL_SIZE) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID");
+ return 0;
+ }
+
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
+ ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token);
+
+ return 0;
+}
+
+/*
+ * conn_post_process_recv_new_connection_id handles retirement request
+ * of active DCIDs.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *dcid;
+ int rv;
+
+ if (conn->dcid.current.seq < conn->dcid.retire_prior_to) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
+ return 0;
+ }
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ if (pv) {
+ if (conn->dcid.current.seq == pv->dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ conn->dcid.current.seq == pv->fallback_dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ }
+ }
+
+ ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (pv) {
+ if (pv->dcid.seq < conn->dcid.retire_prior_to) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
+ rv = conn_retire_dcid(conn, &pv->dcid, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->dcid.seq == pv->fallback_dcid.seq) {
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ }
+
+ ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path migration is aborted because connection ID is"
+ "retired and no unused connection ID is available");
+
+ return conn_abort_pv(conn, ts);
+ }
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq < conn->dcid.retire_prior_to) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
+ rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ rv = conn_call_activate_dcid(conn, &pv->fallback_dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ /* Now we have no fallback dcid. */
+ return conn_abort_pv(conn, ts);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_retire_connection_id processes the incoming
+ * RETIRE_CONNECTION_ID frame |fr|. |hd| is a packet header which
+ * |fr| is included.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_PROTO
+ * SCID is zero-length.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Attempt to retire CID which is used as DCID to send this frame.
+ */
+static int conn_recv_retire_connection_id(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const ngtcp2_retire_connection_id *fr,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+
+ if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ scid = ngtcp2_ksl_it_get(&it);
+ if (scid->seq == fr->seq) {
+ if (ngtcp2_cid_eq(&scid->cid, &hd->dcid)) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) {
+ scid->flags |= NGTCP2_SCID_FLAG_RETIRED;
+ ++conn->scid.num_retired;
+ }
+
+ if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) {
+ ngtcp2_pq_remove(&conn->scid.used, &scid->pe);
+ scid->pe.index = NGTCP2_PQ_BAD_INDEX;
+ }
+
+ scid->retired_ts = ts;
+
+ return ngtcp2_pq_push(&conn->scid.used, &scid->pe);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Token is empty
+ * NGTCP2_ERR_PROTO:
+ * Server received NEW_TOKEN.
+ */
+static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) {
+ if (conn->server) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (fr->tokenlen == 0) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ return conn_call_recv_new_token(conn, fr->token, fr->tokenlen);
+}
+
+/*
+ * conn_recv_streams_blocked_bidi processes the incoming
+ * STREAMS_BLOCKED (0x16).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Maximum Streams is larger than advertised value.
+ */
+static int conn_recv_streams_blocked_bidi(ngtcp2_conn *conn,
+ ngtcp2_streams_blocked *fr) {
+ if (fr->max_streams > conn->remote.bidi.max_streams) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_streams_blocked_uni processes the incoming
+ * STREAMS_BLOCKED (0x17).
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Maximum Streams is larger than advertised value.
+ */
+static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn,
+ ngtcp2_streams_blocked *fr) {
+ if (fr->max_streams > conn->remote.uni.max_streams) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_select_preferred_addr asks a client application to select a
+ * server address from preferred addresses received from server. If a
+ * client chooses the address, path validation will start.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_select_preferred_addr(ngtcp2_conn *conn) {
+ ngtcp2_path_storage ps;
+ int rv;
+ ngtcp2_duration pto, initial_pto, timeout;
+ ngtcp2_pv *pv;
+ ngtcp2_dcid *dcid;
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
+ return 0;
+ }
+
+ ngtcp2_path_storage_zero(&ps);
+ ngtcp2_addr_copy(&ps.path.local, &conn->dcid.current.ps.path.local);
+
+ rv = conn_call_select_preferred_addr(conn, &ps.path);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ps.path.remote.addrlen == 0 ||
+ ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) {
+ return 0;
+ }
+
+ assert(conn->pv == NULL);
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, &ps.path);
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_PREFERRED_ADDR,
+ &conn->log, conn->mem);
+ if (rv != 0) {
+ /* TODO Call ngtcp2_dcid_free here if it is introduced */
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+ conn->pv = pv;
+
+ return conn_call_activate_dcid(conn, &pv->dcid);
+}
+
+/*
+ * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE
+ * frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Server received HANDSHAKE_DONE frame.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+
+ if (conn->server) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) {
+ return 0;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED |
+ NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+
+ conn->pktns.rtb.persistent_congestion_start_ts = ts;
+
+ conn_discard_handshake_state(conn, ts);
+
+ assert(conn->remote.transport_params);
+
+ if (conn->remote.transport_params->preferred_address_present) {
+ rv = conn_select_preferred_addr(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_call_handshake_confirmed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Re-arm loss detection timer after handshake has been
+ confirmed. */
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+/*
+ * conn_recv_datagram processes the incoming DATAGRAM frame |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) {
+ assert(conn->local.transport_params.max_datagram_frame_size);
+
+ return conn_call_recv_datagram(conn, fr);
+}
+
+/*
+ * conn_key_phase_changed returns nonzero if |hd| indicates that the
+ * key phase has unexpected value.
+ */
+static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^
+ !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE);
+}
+
+/*
+ * conn_prepare_key_update installs new updated keys.
+ */
+static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm;
+ ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm;
+ ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm;
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}, tx_aead_ctx = {0};
+ size_t secretlen, ivlen;
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) &&
+ tx_ckm->use_count >= pktns->crypto.ctx.max_encryption &&
+ ngtcp2_conn_initiate_key_update(conn, ts) != 0) {
+ return NGTCP2_ERR_AEAD_LIMIT_REACHED;
+ }
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ (confirmed_ts != UINT64_MAX && confirmed_ts + pto > ts)) {
+ return 0;
+ }
+
+ if (conn->crypto.key_update.new_rx_ckm ||
+ conn->crypto.key_update.new_tx_ckm) {
+ assert(conn->crypto.key_update.new_rx_ckm);
+ assert(conn->crypto.key_update.new_tx_ckm);
+ return 0;
+ }
+
+ secretlen = rx_ckm->secret.len;
+ ivlen = rx_ckm->iv.len;
+
+ rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm,
+ secretlen, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm,
+ secretlen, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ new_rx_ckm = conn->crypto.key_update.new_rx_ckm;
+ new_tx_ckm = conn->crypto.key_update.new_tx_ckm;
+
+ rv = conn_call_update_key(
+ conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx,
+ new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base,
+ rx_ckm->secret.base, tx_ckm->secret.base, secretlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ new_rx_ckm->aead_ctx = rx_aead_ctx;
+ new_tx_ckm->aead_ctx = tx_aead_ctx;
+
+ if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) {
+ new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE;
+ new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE;
+ }
+
+ if (conn->crypto.key_update.old_rx_ckm) {
+ conn_call_delete_crypto_aead_ctx(
+ conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem);
+ conn->crypto.key_update.old_rx_ckm = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_rotate_keys rotates keys. The current key moves to old key,
+ * and new key moves to the current key. If the local endpoint
+ * initiated this key update, pass nonzero as |initiator|.
+ */
+static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num,
+ int initiator) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ assert(conn->crypto.key_update.new_rx_ckm);
+ assert(conn->crypto.key_update.new_tx_ckm);
+ assert(!conn->crypto.key_update.old_rx_ckm);
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm;
+
+ pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm;
+ conn->crypto.key_update.new_rx_ckm = NULL;
+ pktns->crypto.rx.ckm->pkt_num = pkt_num;
+
+ assert(pktns->crypto.tx.ckm);
+
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+
+ pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm;
+ conn->crypto.key_update.new_tx_ckm = NULL;
+ pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1;
+
+ conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+ if (initiator) {
+ conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR;
+ }
+}
+
+/*
+ * conn_path_validation_in_progress returns nonzero if path validation
+ * against |path| is underway.
+ */
+static int conn_path_validation_in_progress(ngtcp2_conn *conn,
+ const ngtcp2_path *path) {
+ ngtcp2_pv *pv = conn->pv;
+
+ return pv && ngtcp2_path_eq(&pv->dcid.ps.path, path);
+}
+
+/*
+ * conn_recv_non_probing_pkt_on_new_path is called when non-probing
+ * packet is received via new path. It starts path validation against
+ * the new path.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ size_t dgramlen,
+ int new_cid_used,
+ ngtcp2_tstamp ts) {
+
+ ngtcp2_dcid dcid, *bound_dcid, *last;
+ ngtcp2_pv *pv;
+ int rv;
+ ngtcp2_duration pto, initial_pto, timeout;
+ int require_new_cid;
+ int local_addr_eq;
+ uint32_t remote_addr_cmp;
+ size_t len, i;
+
+ assert(conn->server);
+
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) {
+ /* If new path equals fallback path, that means connection
+ migrated back to the original path. Fallback path is
+ considered to be validated. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path is migrated back to the original path");
+ ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid);
+ conn_reset_congestion_state(conn, ts);
+ conn->dcid.current.bytes_recv += dgramlen;
+ conn_reset_ecn_validation_state(conn);
+
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Run PMTUD just in case if it is prematurely aborted */
+ assert(!conn->pmtud);
+
+ return conn_start_pmtud(conn);
+ }
+
+ remote_addr_cmp =
+ ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote);
+ local_addr_eq =
+ ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local);
+
+ /*
+ * When to change DCID? RFC 9002 section 9.5 says:
+ *
+ * An endpoint MUST NOT reuse a connection ID when sending from more
+ * than one local address -- for example, when initiating connection
+ * migration as described in Section 9.2 or when probing a new
+ * network path as described in Section 9.1.
+ *
+ * Similarly, an endpoint MUST NOT reuse a connection ID when
+ * sending to more than one destination address. Due to network
+ * changes outside the control of its peer, an endpoint might
+ * receive packets from a new source address with the same
+ * Destination Connection ID field value, in which case it MAY
+ * continue to use the current connection ID with the new remote
+ * address while still sending from the same local address.
+ */
+ require_new_cid = conn->dcid.current.cid.datalen &&
+ ((new_cid_used && remote_addr_cmp) || !local_addr_eq);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "non-probing packet was received from new remote address");
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+
+ for (i = 0; i < len; ++i) {
+ bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+ if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_CON,
+ "Found DCID which has already been bound to the new path");
+
+ ngtcp2_dcid_copy(&dcid, bound_dcid);
+ if (i == 0) {
+ ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
+ } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ } else {
+ last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1);
+ ngtcp2_dcid_copy(bound_dcid, last);
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ }
+ require_new_cid = 0;
+
+ if (dcid.cid.datalen) {
+ rv = conn_call_activate_dcid(conn, &dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ break;
+ }
+ }
+
+ if (i == len) {
+ if (require_new_cid) {
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
+ return NGTCP2_ERR_CONN_ID_BLOCKED;
+ }
+ ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0));
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ rv = conn_call_activate_dcid(conn, &dcid);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ /* Use the current DCID if a remote endpoint does not change
+ DCID. */
+ ngtcp2_dcid_copy(&dcid, &conn->dcid.current);
+ dcid.bytes_sent = 0;
+ dcid.bytes_recv = 0;
+ dcid.flags &= (uint8_t)~NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ }
+ }
+
+ ngtcp2_dcid_set_path(&dcid, path);
+ dcid.bytes_recv += dgramlen;
+
+ rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE,
+ &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) {
+ ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid);
+ pv->fallback_pto = conn->pv->fallback_pto;
+ /* Unset the flag bit so that conn_stop_pv does not retire
+ DCID. */
+ conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE;
+ } else {
+ ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current);
+ pv->fallback_pto = pto;
+ }
+
+ if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR |
+ NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) {
+ conn_reset_congestion_state(conn, ts);
+ } else {
+ /* For NAT rebinding, keep max_udp_payload_size since client most
+ likely does not send a padded PATH_CHALLENGE. */
+ dcid.max_udp_payload_size = ngtcp2_max(
+ dcid.max_udp_payload_size, conn->dcid.current.max_udp_payload_size);
+ }
+
+ ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
+
+ conn_reset_ecn_validation_state(conn);
+
+ ngtcp2_conn_stop_pmtud(conn);
+
+ if (conn->pv) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_PTV,
+ "path migration is aborted because new migration has started");
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ conn->pv = pv;
+
+ return 0;
+}
+
+/*
+ * conn_recv_pkt_from_new_path is called when a 1RTT packet is
+ * received from new path (not current path). This packet would be a
+ * packet which only contains probing frame, or reordered packet, or a
+ * path is being validated.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONN_ID_BLOCKED
+ * No unused DCID is available
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn,
+ const ngtcp2_path *path, size_t dgramlen,
+ int path_challenge_recved,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_dcid *bound_dcid;
+ int rv;
+
+ if (pv) {
+ if (ngtcp2_path_eq(&pv->dcid.ps.path, path)) {
+ pv->dcid.bytes_recv += dgramlen;
+ return 0;
+ }
+
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)) {
+ pv->fallback_dcid.bytes_recv += dgramlen;
+ return 0;
+ }
+ }
+
+ if (!path_challenge_recved) {
+ return 0;
+ }
+
+ rv = conn_bind_dcid(conn, &bound_dcid, path, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_dcid_set_path(bound_dcid, path);
+ bound_dcid->bytes_recv += dgramlen;
+
+ return 0;
+}
+
+/*
+ * conn_recv_delayed_handshake_pkt processes the received Handshake
+ * packet which is received after handshake completed. This function
+ * does the minimal job, and its purpose is send acknowledgement of
+ * this packet to the peer. We assume that hd->type ==
+ * NGTCP2_PKT_HANDSHAKE.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Frame is badly formatted; or frame type is unknown.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded.
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_PROTO
+ * Frame that is not allowed in Handshake packet is received.
+ */
+static int
+conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi,
+ const ngtcp2_pkt_hd *hd, size_t pktlen,
+ const uint8_t *payload, size_t payloadlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int rv;
+ int require_ack = 0;
+ ngtcp2_pktns *pktns;
+
+ assert(hd->type == NGTCP2_PKT_HANDSHAKE);
+
+ pktns = conn->hs_pktns;
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_pkt_received_start(&conn->qlog);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ fr->ack.ack_delay = 0;
+ fr->ack.ack_delay_unscaled = 0;
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, hd, fr);
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PADDING:
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ case NGTCP2_FRAME_PING:
+ require_ack = 1;
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen);
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return 0;
+}
+
+/*
+ * conn_allow_path_change_under_disable_active_migration returns
+ * nonzero if a packet from |path| is acceptable under
+ * disable_active_migration is on.
+ */
+static int
+conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn,
+ const ngtcp2_path *path) {
+ uint32_t remote_addr_cmp;
+ const ngtcp2_preferred_addr *paddr;
+ ngtcp2_addr addr;
+
+ assert(conn->server);
+ assert(conn->local.transport_params.disable_active_migration);
+
+ /* If local address does not change, it must be passive migration
+ (NAT rebinding). */
+ if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) {
+ remote_addr_cmp =
+ ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote);
+
+ return (remote_addr_cmp | NGTCP2_ADDR_COMPARE_FLAG_PORT) ==
+ NGTCP2_ADDR_COMPARE_FLAG_PORT;
+ }
+
+ /* If local address changes, it must be one of the preferred
+ addresses. */
+
+ if (!conn->local.transport_params.preferred_address_present) {
+ return 0;
+ }
+
+ paddr = &conn->local.transport_params.preferred_address;
+
+ if (paddr->ipv4_present) {
+ ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv4,
+ sizeof(paddr->ipv4));
+
+ if (ngtcp2_addr_eq(&addr, &path->local)) {
+ return 1;
+ }
+ }
+
+ if (paddr->ipv6_present) {
+ ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv6,
+ sizeof(paddr->ipv6));
+
+ if (ngtcp2_addr_eq(&addr, &path->local)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_pkt processes a packet contained in the buffer pointed by
+ * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets.
+ * This function only processes the first packet. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of bytes processed if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_DISCARD_PKT
+ * Packet was discarded because plain text header was malformed;
+ * or its payload could not be decrypted.
+ * NGTCP2_ERR_PROTO
+ * Packet is badly formatted; or 0RTT packet contains other than
+ * PADDING or STREAM frames; or other QUIC protocol violation is
+ * found.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Frame is badly formatted; or frame type is unknown.
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed.
+ * NGTCP2_ERR_STREAM_STATE
+ * Frame is received to the local stream which is not initiated.
+ * NGTCP2_ERR_STREAM_LIMIT
+ * Frame has remote stream ID which is strictly greater than the
+ * allowed limit.
+ * NGTCP2_ERR_FLOW_CONTROL
+ * Flow control limit is violated.
+ * NGTCP2_ERR_FINAL_SIZE
+ * Frame has strictly larger end offset than it is permitted.
+ */
+static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen,
+ ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+ ngtcp2_pkt_hd hd;
+ int rv = 0;
+ size_t hdpktlen;
+ const uint8_t *payload;
+ size_t payloadlen;
+ ngtcp2_ssize nread, nwrite;
+ ngtcp2_max_frame mfr;
+ ngtcp2_frame *fr = &mfr.fr;
+ int require_ack = 0;
+ ngtcp2_crypto_aead *aead;
+ ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx *hp_ctx;
+ ngtcp2_hp_mask hp_mask;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_pktns *pktns;
+ int non_probing_pkt = 0;
+ int key_phase_bit_changed = 0;
+ int force_decrypt_failure = 0;
+ int recv_ncid = 0;
+ int new_cid_used = 0;
+ int path_challenge_recved = 0;
+
+ if (conn->server && conn->local.transport_params.disable_active_migration &&
+ !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
+ !conn_allow_path_change_under_disable_active_migration(conn, path)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet is discarded because active migration is disabled");
+
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
+ nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen);
+ if (nread < 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decode long header");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (pktlen < (size_t)nread + hd.len) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ assert(conn->negotiated_version);
+
+ if (hd.version != conn->client_chosen_version &&
+ hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktlen = (size_t)nread + hd.len;
+
+ /* Quoted from spec: if subsequent packets of those types include
+ a different Source Connection ID, they MUST be discarded. */
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) {
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched SCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ switch (hd.type) {
+ case NGTCP2_PKT_INITIAL:
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "delayed Initial packet was discarded");
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_HANDSHAKE:
+ if (hd.version != conn->negotiated_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!conn->hs_pktns) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "delayed Handshake packet was discarded");
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = conn->hs_pktns;
+ aead = &pktns->crypto.ctx.aead;
+ hp = &pktns->crypto.ctx.hp;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ break;
+ case NGTCP2_PKT_0RTT:
+ if (!conn->server || hd.version != conn->client_chosen_version) {
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ if (!conn->early.ckm) {
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ pktns = &conn->pktns;
+ aead = &conn->early.ctx.aead;
+ hp = &conn->early.ctx.hp;
+ ckm = conn->early.ckm;
+ hp_ctx = &conn->early.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ break;
+ default:
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet type 0x%02x was ignored", hd.type);
+ return (ngtcp2_ssize)pktlen;
+ }
+ } else {
+ nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->oscid.datalen);
+ if (nread < 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decode short header");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ pktns = &conn->pktns;
+ aead = &pktns->crypto.ctx.aead;
+ hp = &pktns->crypto.ctx.hp;
+ ckm = pktns->crypto.rx.ckm;
+ hp_ctx = &pktns->crypto.rx.hp_ctx;
+ hp_mask = conn->callbacks.hp_mask;
+ decrypt = conn->callbacks.decrypt;
+ }
+
+ rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
+ (size_t)nread, hp_ctx, hp_mask);
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ hdpktlen = (size_t)nwrite;
+ payload = pkt + hdpktlen;
+ payloadlen = pktlen - hdpktlen;
+
+ hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num,
+ pkt_num_bits(hd.pkt_numlen));
+ if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num);
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
+
+ if (hd.type == NGTCP2_PKT_1RTT) {
+ key_phase_bit_changed = conn_key_phase_changed(conn, &hd);
+ }
+
+ rv = conn_ensure_decrypt_buffer(conn, payloadlen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (key_phase_bit_changed) {
+ assert(hd.type == NGTCP2_PKT_1RTT);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE");
+
+ if (ckm->pkt_num > hd.pkt_num) {
+ if (conn->crypto.key_update.old_rx_ckm) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "decrypting with old key");
+ ckm = conn->crypto.key_update.old_rx_ckm;
+ } else {
+ force_decrypt_failure = 1;
+ }
+ } else if (pktns->rx.max_pkt_num < hd.pkt_num) {
+ assert(ckm->pkt_num < hd.pkt_num);
+ if (!conn->crypto.key_update.new_rx_ckm) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "new key is not available");
+ force_decrypt_failure = 1;
+ } else {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "decrypting with new key");
+ ckm = conn->crypto.key_update.new_rx_ckm;
+ }
+ } else {
+ force_decrypt_failure = 1;
+ }
+ }
+
+ nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen,
+ conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num,
+ ckm, decrypt);
+
+ if (force_decrypt_failure) {
+ nwrite = NGTCP2_ERR_DECRYPT;
+ }
+
+ if (nwrite < 0) {
+ if (ngtcp2_err_is_fatal((int)nwrite)) {
+ return nwrite;
+ }
+
+ assert(NGTCP2_ERR_DECRYPT == nwrite);
+
+ if (hd.type == NGTCP2_PKT_1RTT &&
+ ++conn->crypto.decryption_failure_count >=
+ pktns->crypto.ctx.max_decryption_failure) {
+ return NGTCP2_ERR_AEAD_LIMIT_REACHED;
+ }
+
+ if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "could not decrypt packet payload");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]);
+ if (rv != 0) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet has incorrect reserved bits");
+
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was discarded because of duplicated packet number");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ payload = conn->crypto.decrypt_buf.base;
+ payloadlen = (size_t)nwrite;
+
+ if (payloadlen == 0) {
+ /* QUIC packet must contain at least one frame */
+ return NGTCP2_ERR_PROTO;
+ }
+
+ if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ switch (hd.type) {
+ case NGTCP2_PKT_HANDSHAKE:
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+
+ rv = conn_recv_delayed_handshake_pkt(conn, pi, &hd, pktlen, payload,
+ payloadlen, pkt_ts, ts);
+ if (rv < 0) {
+ return (ngtcp2_ssize)rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_PKT_0RTT:
+ if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) {
+ rv = conn_verify_dcid(conn, NULL, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+ break;
+ default:
+ /* Unreachable */
+ ngtcp2_unreachable();
+ }
+ } else {
+ rv = conn_verify_dcid(conn, &new_cid_used, &hd);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet was ignored because of mismatched DCID");
+ return NGTCP2_ERR_DISCARD_PKT;
+ }
+ }
+
+ ngtcp2_qlog_pkt_received_start(&conn->qlog);
+
+ for (; payloadlen;) {
+ nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen);
+ if (nread < 0) {
+ return nread;
+ }
+
+ payload += nread;
+ payloadlen -= (size_t)nread;
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) &&
+ hd.type == NGTCP2_PKT_0RTT) {
+ return NGTCP2_ERR_PROTO;
+ }
+ assert(conn->remote.transport_params);
+ assign_recved_ack_delay_unscaled(
+ &fr->ack, conn->remote.transport_params->ack_delay_exponent);
+ break;
+ }
+
+ ngtcp2_log_rx_fr(&conn->log, &hd, fr);
+
+ if (hd.type == NGTCP2_PKT_0RTT) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_PADDING:
+ case NGTCP2_FRAME_PING:
+ case NGTCP2_FRAME_RESET_STREAM:
+ case NGTCP2_FRAME_STOP_SENDING:
+ case NGTCP2_FRAME_STREAM:
+ case NGTCP2_FRAME_MAX_DATA:
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ break;
+ default:
+ return NGTCP2_ERR_PROTO;
+ }
+ }
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ case NGTCP2_FRAME_PADDING:
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ break;
+ default:
+ require_ack = 1;
+ }
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (!conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED;
+ }
+ rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAM:
+ rv = conn_recv_stream(conn, &fr->stream);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ rv = conn_recv_crypto(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION,
+ &pktns->crypto.strm, &fr->crypto);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ rv = conn_recv_reset_stream(conn, &fr->reset_stream);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STOP_SENDING:
+ rv = conn_recv_stop_sending(conn, &fr->stop_sending);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ rv = conn_recv_max_stream_data(conn, &fr->max_stream_data);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ conn_recv_max_data(conn, &fr->max_data);
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ rv = conn_recv_max_streams(conn, &fr->max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ rv = conn_recv_connection_close(conn, &fr->connection_close);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_PING:
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ conn_recv_path_challenge(conn, path, &fr->path_challenge);
+ path_challenge_recved = 1;
+ break;
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ rv = conn_recv_path_response(conn, &fr->path_response, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ rv = conn_recv_new_connection_id(conn, &fr->new_connection_id);
+ if (rv != 0) {
+ return rv;
+ }
+ recv_ncid = 1;
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id,
+ ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ rv = conn_recv_new_token(conn, &fr->new_token);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ rv = conn_recv_handshake_done(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ rv = conn_recv_streams_blocked_bidi(conn, &fr->streams_blocked);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ rv = conn_recv_streams_blocked_uni(conn, &fr->streams_blocked);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ /* TODO Not implemented yet */
+ non_probing_pkt = 1;
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ if ((uint64_t)nread >
+ conn->local.transport_params.max_datagram_frame_size) {
+ return NGTCP2_ERR_PROTO;
+ }
+ rv = conn_recv_datagram(conn, &fr->datagram);
+ if (rv != 0) {
+ return rv;
+ }
+ non_probing_pkt = 1;
+ break;
+ }
+
+ ngtcp2_qlog_write_frame(&conn->qlog, fr);
+ }
+
+ ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen);
+
+ if (recv_ncid) {
+ rv = conn_post_process_recv_new_connection_id(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn->server && hd.type == NGTCP2_PKT_1RTT &&
+ !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num &&
+ !conn_path_validation_in_progress(conn, path)) {
+ rv = conn_recv_non_probing_pkt_on_new_path(conn, path, dgramlen,
+ new_cid_used, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* DCID is not available. Just continue. */
+ assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv);
+ }
+ } else {
+ rv = conn_recv_pkt_from_new_path(conn, path, dgramlen,
+ path_challenge_recved, ts);
+ if (rv != 0) {
+ if (ngtcp2_err_is_fatal(rv)) {
+ return rv;
+ }
+
+ /* DCID is not available. Just continue. */
+ assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv);
+ }
+ }
+ }
+
+ if (hd.type == NGTCP2_PKT_1RTT) {
+ if (ckm == conn->crypto.key_update.new_rx_ckm) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys");
+ conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0);
+ } else if (ckm->pkt_num > hd.pkt_num) {
+ ckm->pkt_num = hd.pkt_num;
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts == UINT64_MAX) {
+ conn->early.discard_started_ts = ts;
+ }
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn_update_keep_alive_last_ts(conn, ts);
+ }
+ }
+
+ rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns_increase_ecn_counts(pktns, pi);
+
+ if (require_ack &&
+ (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh ||
+ (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
+ ngtcp2_acktr_immediate_ack(&pktns->acktr);
+ }
+
+ rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack,
+ pkt_ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_restart_timer_on_read(conn, ts);
+
+ ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
+
+ return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
+ : (ngtcp2_ssize)pktlen;
+}
+
+/*
+ * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT
+ * packets.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_pkt.
+ */
+static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_chain **ppc, *next;
+ int rv;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "processing buffered protected packet");
+
+ for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
+ next = (*ppc)->next;
+ nread = conn_recv_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi, (*ppc)->pkt,
+ (*ppc)->pktlen, (*ppc)->dgramlen, (*ppc)->ts, ts);
+ if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) &&
+ nread != NGTCP2_ERR_DRAINING) {
+ /* TODO We don't know this is the first QUIC packet in a
+ datagram. */
+ rv = conn_on_stateless_reset(conn, &(*ppc)->path.path, (*ppc)->pkt,
+ (*ppc)->pktlen);
+ if (rv == 0) {
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ return NGTCP2_ERR_DRAINING;
+ }
+ }
+
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ if (nread < 0) {
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ continue;
+ }
+ return (int)nread;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_process_buffered_handshake_pkt processes buffered Handshake
+ * packets.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_handshake_pkt.
+ */
+static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pktns *pktns = conn->hs_pktns;
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_chain **ppc, *next;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "processing buffered handshake packet");
+
+ for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
+ next = (*ppc)->next;
+ nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi,
+ (*ppc)->pkt, (*ppc)->pktlen,
+ (*ppc)->dgramlen, (*ppc)->ts, ts);
+ ngtcp2_pkt_chain_del(*ppc, conn->mem);
+ *ppc = next;
+ if (nread < 0) {
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ continue;
+ }
+ return (int)nread;
+ }
+ }
+
+ return 0;
+}
+
+static void conn_sync_stream_id_limit(ngtcp2_conn *conn) {
+ ngtcp2_transport_params *params = conn->remote.transport_params;
+
+ assert(params);
+
+ conn->local.bidi.max_streams = params->initial_max_streams_bidi;
+ conn->local.uni.max_streams = params->initial_max_streams_uni;
+}
+
+static int strm_set_max_offset(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_transport_params *params = conn->remote.transport_params;
+ ngtcp2_strm *strm = data;
+ uint64_t max_offset;
+ int rv;
+
+ assert(params);
+
+ if (!conn_local_stream(conn, strm->stream_id)) {
+ return 0;
+ }
+
+ if (bidi_stream(strm->stream_id)) {
+ max_offset = params->initial_max_stream_data_bidi_remote;
+ } else {
+ max_offset = params->initial_max_stream_data_uni;
+ }
+
+ if (strm->tx.max_offset < max_offset) {
+ strm->tx.max_offset = max_offset;
+
+ /* Don't call callback if stream is half-closed local */
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ return 0;
+ }
+
+ rv = conn_call_extend_max_stream_data(conn, strm, strm->stream_id,
+ strm->tx.max_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static int conn_sync_stream_data_limit(ngtcp2_conn *conn) {
+ return ngtcp2_map_each(&conn->strms, strm_set_max_offset, conn);
+}
+
+/*
+ * conn_handshake_completed is called once cryptographic handshake has
+ * completed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed.
+ */
+static int conn_handshake_completed(ngtcp2_conn *conn) {
+ int rv;
+
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED;
+
+ rv = conn_call_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->local.bidi.max_streams > 0) {
+ rv = conn_call_extend_max_local_streams_bidi(conn,
+ conn->local.bidi.max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ if (conn->local.uni.max_streams > 0) {
+ rv = conn_call_extend_max_local_streams_uni(conn,
+ conn->local.uni.max_streams);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_recv_cpkt processes compound packet after handshake. The
+ * buffer pointed by |pkt| might contain multiple packets. The 1RTT
+ * packet must be the last one because it does not have payload length
+ * field.
+ *
+ * This function returns 0 if it succeeds, or the same negative error
+ * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT.
+ */
+static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nread;
+ int rv;
+ const uint8_t *origpkt = pkt;
+ size_t dgramlen = pktlen;
+
+ if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ conn->dcid.current.bytes_recv += dgramlen;
+ }
+
+ while (pktlen) {
+ nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts);
+ if (nread < 0) {
+ if (ngtcp2_err_is_fatal((int)nread)) {
+ return (int)nread;
+ }
+
+ if (nread == NGTCP2_ERR_DRAINING) {
+ return NGTCP2_ERR_DRAINING;
+ }
+
+ if (origpkt == pkt) {
+ rv = conn_on_stateless_reset(conn, path, origpkt, dgramlen);
+ if (rv == 0) {
+ return NGTCP2_ERR_DRAINING;
+ }
+ }
+ if (nread == NGTCP2_ERR_DISCARD_PKT) {
+ return 0;
+ }
+ return (int)nread;
+ }
+
+ assert(pktlen >= (size_t)nread);
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "read packet %td left %zu", nread, pktlen);
+ }
+
+ return 0;
+}
+
+/*
+ * conn_is_retired_path returns nonzero if |path| is included in
+ * retired path list.
+ */
+static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) {
+ size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
+ ngtcp2_dcid *dcid;
+
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
+ if (ngtcp2_path_eq(&dcid->ps.path, path)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for
+ * transmission.
+ */
+static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_frame_chain *nfrc;
+ int rv;
+
+ assert(conn->server);
+
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE;
+ nfrc->next = pktns->tx.frq;
+ pktns->tx.frq = nfrc;
+
+ return 0;
+}
+
+/**
+ * @function
+ *
+ * `conn_read_handshake` performs QUIC cryptographic handshake by
+ * reading given data. |pkt| points to the buffer to read and
+ * |pktlen| is the length of the buffer. |path| is the network path.
+ *
+ * This function returns the number of bytes processed. Unless the
+ * last packet is 1RTT packet and an application decryption key has
+ * been installed, it returns |pktlen| if it succeeds. If it finds
+ * 1RTT packet and an application decryption key has been installed,
+ * it returns the number of bytes just before 1RTT packet begins.
+ *
+ * This function returns the number of bytes processed if it succeeds,
+ * or one of the following negative error codes: (TBD).
+ */
+static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_ssize nread;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ /* TODO Better to log something when we ignore input */
+ return (ngtcp2_ssize)pktlen;
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
+ }
+
+ if (conn->state == NGTCP2_CS_CLIENT_INITIAL) {
+ /* Retry packet was received */
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ assert(conn->hs_pktns);
+
+ if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) {
+ rv = conn_process_buffered_handshake_pkt(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn_is_handshake_completed(conn) &&
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ rv = conn_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nread;
+ case NGTCP2_CS_SERVER_INITIAL:
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
+ }
+
+ /*
+ * Client ServerHello might not fit into single Initial packet
+ * (e.g., resuming session with client authentication). If we get
+ * Client Initial which does not increase offset or it is 0RTT
+ * packet buffered, perform address validation in order to buffer
+ * validated data only.
+ */
+ if (ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0) {
+ if (conn->in_pktns->crypto.strm.rx.rob &&
+ ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) {
+ /* Address has been validated with token */
+ if (conn->local.settings.tokenlen) {
+ return nread;
+ }
+ return NGTCP2_ERR_RETRY;
+ }
+ if (conn->in_pktns->rx.buffed_pkts) {
+ /* 0RTT is buffered, force retry */
+ return NGTCP2_ERR_RETRY;
+ }
+ /* If neither CRYPTO frame nor 0RTT packet is processed, just
+ drop connection. */
+ return NGTCP2_ERR_DROP_CONN;
+ }
+
+ /* Process re-ordered 0-RTT packets which arrived before Initial
+ packet. */
+ if (conn->early.ckm) {
+ assert(conn->in_pktns);
+
+ rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return nread;
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ nread = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return nread;
+ }
+
+ if (conn->hs_pktns->crypto.rx.ckm) {
+ rv = conn_process_buffered_handshake_pkt(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn->hs_pktns->rx.max_pkt_num != -1) {
+ conn_discard_initial_state(conn, ts);
+ }
+
+ if (!conn_is_handshake_completed(conn)) {
+ /* If server hits amplification limit, it cancels loss detection
+ timer. If server receives a packet from client, the limit is
+ increased and server can send more. If server has
+ ack-eliciting Initial or Handshake packets, it should resend
+ it if timer fired but timer is not armed in this case. So
+ instead of resending Initial/Handshake packets, if server has
+ 1RTT data to send, it might send them and then might hit
+ amplification limit again until it hits stream data limit.
+ Initial/Handshake data is not resent. In order to avoid this
+ situation, try to arm loss detection and check the expiry
+ here so that on next write call, we can resend
+ Initial/Handshake first. */
+ if (conn->cstat.loss_detection_timer == UINT64_MAX) {
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ if ((size_t)nread < pktlen) {
+ /* We have 1RTT packet and application rx key, but the
+ handshake has not completed yet. */
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "buffering 1RTT packet len=%zu",
+ pktlen - (size_t)nread);
+
+ rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread,
+ pktlen - (size_t)nread, pktlen, ts);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ return (ngtcp2_ssize)pktlen;
+ }
+
+ return nread;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ rv = conn_handshake_completed(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_discard_handshake_state(conn, ts);
+
+ rv = conn_enqueue_handshake_done(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ conn->pktns.rtb.persistent_congestion_start_ts = ts;
+
+ /* Re-arm loss detection timer here after handshake has been
+ confirmed. */
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return nread;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return (ngtcp2_ssize)pktlen;
+ }
+}
+
+int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
+ int pkt_info_version,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts) {
+ int rv = 0;
+ ngtcp2_ssize nread = 0;
+ const ngtcp2_pkt_info zero_pi = {0};
+ (void)pkt_info_version;
+
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu",
+ pktlen);
+
+ if (pktlen == 0) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ /* client does not expect a packet from unknown path. */
+ if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
+ (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) &&
+ !conn_is_retired_path(conn, path)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "ignore packet from unknown path");
+ return 0;
+ }
+
+ if (!pi) {
+ pi = &zero_pi;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED:
+ nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return 0;
+ }
+
+ assert(conn->pktns.crypto.rx.ckm);
+
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ break;
+ case NGTCP2_CS_SERVER_INITIAL:
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
+ if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "ignore packet from unknown path during handshake");
+
+ if (conn->state == NGTCP2_CS_SERVER_INITIAL &&
+ ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0 &&
+ (!conn->in_pktns->crypto.strm.rx.rob ||
+ !ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob))) {
+ return NGTCP2_ERR_DROP_CONN;
+ }
+
+ return 0;
+ }
+
+ nread = conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ if ((size_t)nread == pktlen) {
+ return 0;
+ }
+
+ assert(conn->pktns.crypto.rx.ckm);
+
+ pkt += nread;
+ pktlen -= (size_t)nread;
+
+ break;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ case NGTCP2_CS_POST_HANDSHAKE:
+ rv = conn_prepare_key_update(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
+}
+
+/*
+ * conn_check_pkt_num_exhausted returns nonzero if packet number is
+ * exhausted in at least one of packet number space.
+ */
+static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+
+ return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) ||
+ (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) ||
+ conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM;
+}
+
+/*
+ * conn_retransmit_retry_early retransmits 0RTT packet after Retry is
+ * received from server.
+ */
+static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint8_t flags,
+ ngtcp2_tstamp ts) {
+ return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags,
+ ts);
+}
+
+/*
+ * conn_handshake_probe_left returns nonzero if there are probe
+ * packets to be sent for Initial or Handshake packet number space
+ * left.
+ */
+static int conn_handshake_probe_left(ngtcp2_conn *conn) {
+ return (conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) ||
+ conn->hs_pktns->rtb.probe_pkt_left;
+}
+
+/*
+ * conn_validate_early_transport_params_limits validates that the
+ * limits in transport parameters remembered by client for early data
+ * are not reduced. This function is only used by client and should
+ * only be called when early data is accepted by server.
+ */
+static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) {
+ const ngtcp2_transport_params *params = conn->remote.transport_params;
+
+ assert(!conn->server);
+ assert(params);
+
+ if (conn->early.transport_params.active_connection_id_limit >
+ params->active_connection_id_limit ||
+ conn->early.transport_params.initial_max_data >
+ params->initial_max_data ||
+ conn->early.transport_params.initial_max_stream_data_bidi_local >
+ params->initial_max_stream_data_bidi_local ||
+ conn->early.transport_params.initial_max_stream_data_bidi_remote >
+ params->initial_max_stream_data_bidi_remote ||
+ conn->early.transport_params.initial_max_stream_data_uni >
+ params->initial_max_stream_data_uni ||
+ conn->early.transport_params.initial_max_streams_bidi >
+ params->initial_max_streams_bidi ||
+ conn->early.transport_params.initial_max_streams_uni >
+ params->initial_max_streams_uni ||
+ conn->early.transport_params.max_datagram_frame_size >
+ params->max_datagram_frame_size) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ return 0;
+}
+
+/*
+ * conn_write_handshake writes QUIC handshake packets to the buffer
+ * pointed by |dest| of length |destlen|. |write_datalen| specifies
+ * the expected length of 0RTT or 1RTT packet payload. Specify 0 to
+ * |write_datalen| if there is no such data.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * NGTCP2_ERR_PKT_NUM_EXHAUSTED
+ * Packet number is exhausted.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM
+ * Required transport parameter is missing.
+ * NGTCP2_CS_CLOSING
+ * Connection is in closing state.
+ * NGTCP2_CS_DRAINING
+ * Connection is in draining state.
+ *
+ * In addition to the above negative error codes, the same error codes
+ * from conn_recv_pkt may also be returned.
+ */
+static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ uint64_t write_datalen,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0;
+ size_t origlen = destlen;
+ uint64_t pending_early_datalen;
+ ngtcp2_dcid *dcid;
+ ngtcp2_preferred_addr *paddr;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ pending_early_datalen = conn_retry_early_payloadlen(conn);
+ if (pending_early_datalen) {
+ write_datalen = pending_early_datalen;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) {
+ nwrite =
+ conn_write_client_initial(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+ } else {
+ nwrite = conn_write_handshake_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ }
+
+ if (pending_early_datalen) {
+ early_spktlen = conn_retransmit_retry_early(
+ conn, pi, dest + nwrite, destlen - (size_t)nwrite,
+ nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
+ : NGTCP2_WRITE_PKT_FLAG_NONE,
+ ts);
+
+ if (early_spktlen < 0) {
+ assert(ngtcp2_err_is_fatal((int)early_spktlen));
+ return early_spktlen;
+ }
+ }
+
+ conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE;
+
+ res = nwrite + early_spktlen;
+
+ return res;
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ if (!conn_handshake_probe_left(conn) && conn_cwnd_is_zero(conn)) {
+ destlen = 0;
+ } else {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ pending_early_datalen = conn_retry_early_payloadlen(conn);
+ if (pending_early_datalen) {
+ write_datalen = pending_early_datalen;
+ }
+ }
+
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (!conn_is_handshake_completed(conn)) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
+ nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen,
+ NGTCP2_WRITE_PKT_FLAG_NONE, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ }
+
+ if (res == 0) {
+ nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ res = nwrite;
+ }
+
+ return res;
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED) &&
+ !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
+ rv = conn_validate_early_transport_params_limits(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ /* Server might increase stream data limits. Extend it if we have
+ streams created for early data. */
+ rv = conn_sync_stream_data_limit(conn);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->state = NGTCP2_CS_POST_HANDSHAKE;
+
+ assert(conn->remote.transport_params);
+
+ if (conn->remote.transport_params->preferred_address_present) {
+ assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb));
+
+ paddr = &conn->remote.transport_params->preferred_address;
+ dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
+ ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token);
+
+ rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1);
+ if (rv != 0) {
+ return (ngtcp2_ssize)rv;
+ }
+ }
+
+ if (conn->remote.transport_params->stateless_reset_token_present) {
+ assert(conn->dcid.current.seq == 0);
+ assert(!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT));
+ ngtcp2_dcid_set_token(
+ &conn->dcid.current,
+ conn->remote.transport_params->stateless_reset_token);
+ }
+
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn_process_early_rtb(conn);
+
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return res;
+ case NGTCP2_CS_SERVER_INITIAL:
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (nwrite) {
+ conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE;
+ }
+
+ return nwrite;
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) {
+ nwrite =
+ conn_write_handshake_pkts(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (res == 0) {
+ nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+ dest += nwrite;
+ origlen -= (size_t)nwrite;
+ }
+
+ return res;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * @function
+ *
+ * `conn_client_write_handshake` writes client side handshake data and
+ * 0RTT packet.
+ *
+ * In order to send STREAM data in 0RTT packet, specify
+ * |vmsg|->stream. |vmsg|->stream.strm, |vmsg|->stream.fin,
+ * |vmsg|->stream.data, and |vmsg|->stream.datacnt are stream to which
+ * 0-RTT data is sent, whether it is a last data chunk in this stream,
+ * a vector of 0-RTT data, and its number of elements respectively.
+ * The amount of 0RTT data sent is assigned to
+ * *|vmsg|->stream.pdatalen. If no data is sent, -1 is assigned.
+ * Note that 0 length STREAM frame is allowed in QUIC, so 0 might be
+ * assigned to *|vmsg|->stream.pdatalen.
+ *
+ * This function returns 0 if it cannot write any frame because buffer
+ * is too small, or packet is congestion limited. Application should
+ * keep reading and wait for congestion window to grow.
+ *
+ * This function returns the number of bytes written to the buffer
+ * pointed by |dest| if it succeeds, or one of the following negative
+ * error codes: (TBD).
+ */
+static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
+ ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts) {
+ int send_stream = 0;
+ int send_datagram = 0;
+ ngtcp2_ssize spktlen, early_spktlen;
+ uint64_t datalen;
+ uint64_t write_datalen = 0;
+ uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ uint32_t version;
+
+ assert(!conn->server);
+
+ /* conn->early.ckm might be created in the first call of
+ conn_handshake(). Check it later. */
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ send_stream =
+ conn_retry_early_payloadlen(conn) == 0 &&
+ /* 0 length STREAM frame is allowed */
+ (datalen == 0 ||
+ (datalen > 0 &&
+ (vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) &&
+ (conn->tx.max_offset - conn->tx.offset)));
+ if (send_stream) {
+ write_datalen =
+ conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ write_datalen =
+ ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ write_datalen += NGTCP2_STREAM_OVERHEAD;
+
+ if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ } else {
+ vmsg = NULL;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt);
+ send_datagram = conn_retry_early_payloadlen(conn) == 0;
+ if (send_datagram) {
+ write_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD;
+
+ if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ } else {
+ vmsg = NULL;
+ }
+ break;
+ }
+ }
+
+ if (!ppe_pending) {
+ spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts);
+
+ if (spktlen < 0) {
+ return spktlen;
+ }
+
+ if ((conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) ||
+ !conn->early.ckm || (!send_stream && !send_datagram)) {
+ return spktlen;
+ }
+
+ /* If spktlen > 0, we are making a compound packet. If Initial
+ packet is written, we have to pad bytes to 0-RTT packet. */
+ version = conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version;
+ if (spktlen > 0 &&
+ ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ conn->pkt.require_padding = 1;
+ } else {
+ conn->pkt.require_padding = 0;
+ }
+ } else {
+ assert(!conn->pktns.crypto.rx.ckm);
+ assert(!conn->pktns.crypto.tx.ckm);
+ assert(conn->early.ckm);
+
+ if (conn->pkt.require_padding) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ spktlen = conn->pkt.hs_spktlen;
+ }
+
+ dest += spktlen;
+ destlen -= (size_t)spktlen;
+
+ if (conn_cwnd_is_zero(conn)) {
+ return spktlen;
+ }
+
+ early_spktlen = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_0RTT,
+ wflags, ts);
+
+ if (early_spktlen < 0) {
+ switch (early_spktlen) {
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ return spktlen;
+ case NGTCP2_ERR_WRITE_MORE:
+ conn->pkt.hs_spktlen = spktlen;
+ break;
+ }
+ return early_spktlen;
+ }
+
+ return spktlen + early_spktlen;
+}
+
+void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) {
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED;
+ if (conn->server) {
+ conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED;
+ }
+}
+
+int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) {
+ return conn_is_handshake_completed(conn) &&
+ (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED);
+}
+
+int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr,
+ int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) {
+ int rv;
+ (void)conn;
+
+ rv = ngtcp2_acktr_add(acktr, pkt_num, active_ack, ts);
+ if (rv != 0) {
+ assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) {
+ ngtcp2_ssize nread;
+ ngtcp2_pkt_hd hd, *p;
+
+ if (dest) {
+ p = dest;
+ } else {
+ p = &hd;
+ }
+
+ if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen);
+ if (nread < 0) {
+ return (int)nread;
+ }
+
+ switch (p->type) {
+ case NGTCP2_PKT_INITIAL:
+ break;
+ case NGTCP2_PKT_0RTT:
+ /* 0-RTT packet may arrive before Initial packet due to
+ re-ordering. ngtcp2 does not buffer 0RTT packet unless the
+ very first Initial packet is received or token is received. */
+ return NGTCP2_ERR_RETRY;
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (pktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE ||
+ (p->tokenlen == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_initial_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx,
+ const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) {
+ ngtcp2_pktns *pktns = conn->in_pktns;
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(pktns);
+
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx);
+ pktns->crypto.rx.hp_ctx.native_handle = NULL;
+
+ if (pktns->crypto.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+ }
+
+ conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx);
+ pktns->crypto.tx.hp_ctx.native_handle = NULL;
+
+ if (pktns->crypto.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+ }
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, NULL, rx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Take owner ship after we are sure that no failure occurs, so that
+ caller can delete these contexts on failure. */
+ pktns->crypto.rx.ckm->aead_ctx = *rx_aead_ctx;
+ pktns->crypto.rx.hp_ctx = *rx_hp_ctx;
+ pktns->crypto.tx.ckm->aead_ctx = *tx_aead_ctx;
+ pktns->crypto.tx.hp_ctx = *tx_hp_ctx;
+
+ return 0;
+}
+
+int ngtcp2_conn_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+ const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) {
+ int rv;
+
+ assert(ivlen >= 8);
+
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+ conn->vneg.rx.hp_ctx.native_handle = NULL;
+
+ if (conn->vneg.rx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+ conn->vneg.rx.ckm = NULL;
+ }
+
+ conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+ conn->vneg.tx.hp_ctx.native_handle = NULL;
+
+ if (conn->vneg.tx.ckm) {
+ conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+ ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+ conn->vneg.tx.ckm = NULL;
+ }
+
+ rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ /* Take owner ship after we are sure that no failure occurs, so that
+ caller can delete these contexts on failure. */
+ conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx;
+ conn->vneg.rx.hp_ctx = *rx_hp_ctx;
+ conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx;
+ conn->vneg.tx.hp_ctx = *tx_hp_ctx;
+ conn->vneg.version = version;
+
+ return 0;
+}
+
+int ngtcp2_conn_install_rx_handshake_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pktns *pktns = conn->hs_pktns;
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(pktns);
+ assert(!pktns->crypto.rx.hp_ctx.native_handle);
+ assert(!pktns->crypto.rx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.rx.hp_ctx = *hp_ctx;
+
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+
+ memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_tx_handshake_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pktns *pktns = conn->hs_pktns;
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(pktns);
+ assert(!pktns->crypto.tx.hp_ctx.native_handle);
+ assert(!pktns->crypto.tx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.tx.hp_ctx = *hp_ctx;
+
+ if (conn->server) {
+ rv = ngtcp2_conn_commit_local_transport_params(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+
+ memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
+
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_early_key(ngtcp2_conn *conn,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(!conn->early.hp_ctx.native_handle);
+ assert(!conn->early.ckm);
+
+ rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, aead_ctx, iv, ivlen,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->early.hp_ctx = *hp_ctx;
+
+ conn->flags |= NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED;
+
+ if (conn->server) {
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY);
+ } else {
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_EARLY);
+ }
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(conn->early.ckm, conn->mem);
+ conn->early.ckm = NULL;
+
+ memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx));
+
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(!pktns->crypto.rx.hp_ctx.native_handle);
+ assert(!pktns->crypto.rx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, secret, secretlen, aead_ctx,
+ iv, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.rx.hp_ctx = *hp_ctx;
+
+ if (!conn->server) {
+ if (conn->remote.pending_transport_params) {
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+
+ conn->remote.transport_params = conn->remote.pending_transport_params;
+ conn->remote.pending_transport_params = NULL;
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
+ }
+
+ if (conn->early.ckm) {
+ conn_discard_early_key(conn);
+ }
+ }
+
+ rv = conn_call_recv_rx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem);
+ pktns->crypto.rx.ckm = NULL;
+
+ memset(&pktns->crypto.rx.hp_ctx, 0, sizeof(pktns->crypto.rx.hp_ctx));
+
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pktns *pktns = &conn->pktns;
+ int rv;
+
+ assert(ivlen >= 8);
+ assert(!pktns->crypto.tx.hp_ctx.native_handle);
+ assert(!pktns->crypto.tx.ckm);
+
+ rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, secret, secretlen, aead_ctx,
+ iv, ivlen, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ pktns->crypto.tx.hp_ctx = *hp_ctx;
+
+ if (conn->server) {
+ if (conn->remote.pending_transport_params) {
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+
+ conn->remote.transport_params = conn->remote.pending_transport_params;
+ conn->remote.pending_transport_params = NULL;
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
+ }
+ } else if (conn->early.ckm) {
+ conn_discard_early_key(conn);
+ }
+
+ rv = conn_call_recv_tx_key(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION);
+ if (rv != 0) {
+ ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem);
+ pktns->crypto.tx.ckm = NULL;
+
+ memset(&pktns->crypto.tx.hp_ctx, 0, sizeof(pktns->crypto.tx.hp_ctx));
+
+ return rv;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+
+ assert(conn->state == NGTCP2_CS_POST_HANDSHAKE);
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) ||
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+ !conn->crypto.key_update.new_tx_ckm ||
+ !conn->crypto.key_update.new_rx_ckm ||
+ (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1);
+
+ return 0;
+}
+
+/*
+ * conn_retire_stale_bound_dcid retires stale destination connection
+ * ID in conn->dcid.bound to keep some unused destination connection
+ * IDs available.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ size_t i;
+ ngtcp2_dcid *dcid, *last;
+ int rv;
+
+ for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+
+ assert(dcid->cid.datalen);
+
+ if (dcid->bound_ts + timeout > ts) {
+ ++i;
+ continue;
+ }
+
+ rv = conn_retire_dcid_seq(conn, dcid->seq);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (i == 0) {
+ ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
+ continue;
+ }
+
+ if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ break;
+ }
+
+ last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb,
+ ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1);
+ ngtcp2_dcid_copy(dcid, last);
+ ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
+ }
+
+ return 0;
+}
+
+ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) {
+ return conn->cstat.loss_detection_timer;
+}
+
+ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) {
+ ngtcp2_tstamp res = UINT64_MAX, t;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_scid *scid;
+ ngtcp2_dcid *dcid;
+ size_t i, len;
+
+ if (conn->pv) {
+ res = ngtcp2_pv_next_expiry(conn->pv);
+ }
+
+ if (conn->pmtud) {
+ res = ngtcp2_min(res, conn->pmtud->expiry);
+ }
+
+ if (!ngtcp2_pq_empty(&conn->scid.used)) {
+ scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe);
+ if (scid->retired_ts != UINT64_MAX) {
+ t = scid->retired_ts + pto;
+ res = ngtcp2_min(res, t);
+ }
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
+ t = dcid->retired_ts + pto;
+ res = ngtcp2_min(res, t);
+ }
+
+ if (conn->dcid.current.cid.datalen) {
+ len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
+
+ assert(dcid->cid.datalen);
+ assert(dcid->bound_ts != UINT64_MAX);
+
+ t = dcid->bound_ts + 3 * pto;
+ res = ngtcp2_min(res, t);
+ }
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts != UINT64_MAX) {
+ t = conn->early.discard_started_ts + 3 * pto;
+ res = ngtcp2_min(res, t);
+ }
+
+ return res;
+}
+
+ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) {
+ ngtcp2_acktr *acktr = &conn->pktns.acktr;
+
+ if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+ acktr->first_unacked_ts != UINT64_MAX) {
+ return acktr->first_unacked_ts + conn_compute_ack_delay(conn);
+ }
+ return UINT64_MAX;
+}
+
+static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) {
+ if (conn_is_handshake_completed(conn) ||
+ conn->local.settings.handshake_timeout == UINT64_MAX) {
+ return UINT64_MAX;
+ }
+
+ return conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout;
+}
+
+ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) {
+ ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn);
+ ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn);
+ ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn);
+ ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn);
+ ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn);
+ ngtcp2_tstamp t6 = conn_handshake_expiry(conn);
+ ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn);
+ ngtcp2_tstamp res = ngtcp2_min(t1, t2);
+ res = ngtcp2_min(res, t3);
+ res = ngtcp2_min(res, t4);
+ res = ngtcp2_min(res, t5);
+ res = ngtcp2_min(res, t6);
+ res = ngtcp2_min(res, t7);
+ return ngtcp2_min(res, conn->tx.pacing.next_ts);
+}
+
+int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
+
+ assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING));
+
+ if (ngtcp2_conn_get_idle_expiry(conn) <= ts) {
+ return NGTCP2_ERR_IDLE_CLOSE;
+ }
+
+ ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts);
+
+ conn_cancel_expired_keep_alive_timer(conn, ts);
+
+ conn_cancel_expired_pkt_tx_timer(conn, ts);
+
+ ngtcp2_conn_remove_lost_pkt(conn, ts);
+
+ if (conn->pv) {
+ ngtcp2_pv_cancel_expired_timer(conn->pv, ts);
+ }
+
+ if (conn->pmtud) {
+ ngtcp2_pmtud_handle_expiry(conn->pmtud, ts);
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+ }
+
+ if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
+ rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (conn->dcid.current.cid.datalen) {
+ rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_remove_retired_connection_id(conn, pto, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->server && conn->early.ckm &&
+ conn->early.discard_started_ts != UINT64_MAX) {
+ if (conn->early.discard_started_ts + 3 * pto <= ts) {
+ conn_discard_early_key(conn);
+ }
+ }
+
+ if (!conn_is_handshake_completed(conn) &&
+ conn->local.settings.handshake_timeout != UINT64_MAX &&
+ conn->local.settings.initial_ts +
+ conn->local.settings.handshake_timeout <=
+ ts) {
+ return NGTCP2_ERR_HANDSHAKE_TIMEOUT;
+ }
+
+ return 0;
+}
+
+static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr,
+ ngtcp2_duration max_ack_delay,
+ ngtcp2_tstamp ts) {
+ if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) &&
+ acktr->first_unacked_ts != UINT64_MAX &&
+ acktr->first_unacked_ts + max_ack_delay <= ts) {
+ acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER;
+ }
+}
+
+void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_duration ack_delay = conn_compute_ack_delay(conn);
+
+ if (conn->in_pktns) {
+ acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, 0, ts);
+ }
+ if (conn->hs_pktns) {
+ acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, 0, ts);
+ }
+ acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ack_delay, ts);
+}
+
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) {
+ ngtcp2_tstamp res = UINT64_MAX, ts;
+
+ if (conn->in_pktns) {
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, conn->in_pktns);
+ res = ngtcp2_min(res, ts);
+ }
+ }
+
+ if (conn->hs_pktns) {
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, conn->hs_pktns);
+ res = ngtcp2_min(res, ts);
+ }
+ }
+
+ ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb);
+ if (ts != UINT64_MAX) {
+ ts += conn_compute_pto(conn, &conn->pktns);
+ res = ngtcp2_min(res, ts);
+ }
+
+ return res;
+}
+
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_duration pto;
+
+ if (conn->in_pktns) {
+ pto = conn_compute_pto(conn, conn->in_pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts);
+ }
+ if (conn->hs_pktns) {
+ pto = conn_compute_pto(conn, conn->hs_pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts);
+ }
+ pto = conn_compute_pto(conn, &conn->pktns);
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts);
+}
+
+/*
+ * select_preferred_version selects the most preferred version.
+ * |fallback_version| is chosen if no preference is made, or
+ * |preferred_versions| does not include any of |chosen_version| or
+ * |available_versions|. |chosen_version| is treated as an extra
+ * other version.
+ */
+static uint32_t select_preferred_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ uint32_t chosen_version,
+ const uint8_t *available_versions,
+ size_t available_versionslen,
+ uint32_t fallback_version) {
+ size_t i, j;
+ const uint8_t *p;
+ uint32_t v;
+
+ if (!preferred_versionslen ||
+ (!available_versionslen && chosen_version == fallback_version)) {
+ return fallback_version;
+ }
+
+ for (i = 0; i < preferred_versionslen; ++i) {
+ if (preferred_versions[i] == chosen_version) {
+ return chosen_version;
+ }
+ for (j = 0, p = available_versions; j < available_versionslen;
+ j += sizeof(uint32_t)) {
+ p = ngtcp2_get_uint32(&v, p);
+
+ if (preferred_versions[i] == v) {
+ return v;
+ }
+ }
+ }
+
+ return fallback_version;
+}
+
+/*
+ * conn_client_validate_transport_params validates |params| as client.
+ * |params| must be sent with Encrypted Extensions.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_TRANSPORT_PARAM
+ * params contains preferred address but server chose zero-length
+ * connection ID.
+ * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE
+ * Validation against version negotiation parameters failed.
+ */
+static int
+conn_client_validate_transport_params(ngtcp2_conn *conn,
+ const ngtcp2_transport_params *params) {
+ if (!ngtcp2_cid_eq(&conn->rcid, &params->original_dcid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) {
+ if (!params->retry_scid_present) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+ if (!ngtcp2_cid_eq(&conn->retry_scid, &params->retry_scid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+ } else if (params->retry_scid_present) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->preferred_address_present &&
+ conn->dcid.current.cid.datalen == 0) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->version_info_present) {
+ if (conn->negotiated_version != params->version_info.chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ assert(vneg_available_versions_includes(conn->vneg.available_versions,
+ conn->vneg.available_versionslen,
+ conn->negotiated_version));
+ } else if (conn->client_chosen_version != conn->negotiated_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ /* When client reacted upon Version Negotiation */
+ if (conn->local.settings.original_version != conn->client_chosen_version) {
+ if (!params->version_info_present) {
+ assert(conn->client_chosen_version == conn->negotiated_version);
+
+ /* QUIC v1 (and the supported draft versions) are treated
+ specially. If version_info is missing, no further validation
+ is necessary.
+ https://datatracker.ietf.org/doc/html/draft-ietf-quic-version-negotiation-10#section-8
+ */
+ if (conn->client_chosen_version == NGTCP2_PROTO_VER_V1 ||
+ (NGTCP2_PROTO_VER_DRAFT_MIN <= conn->client_chosen_version &&
+ conn->client_chosen_version <= NGTCP2_PROTO_VER_DRAFT_MAX)) {
+ return 0;
+ }
+
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ /* Server choose original version after Version Negotiation.
+ Draft does not say this particular case, but this smells like
+ misbehaved server because server should accept original_version
+ in the original connection. */
+ if (conn->local.settings.original_version ==
+ params->version_info.chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ /* Check version downgrade on incompatible version negotiation. */
+ if (params->version_info.available_versionslen == 0) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ if (conn->client_chosen_version !=
+ select_preferred_version(conn->vneg.preferred_versions,
+ conn->vneg.preferred_versionslen,
+ params->version_info.chosen_version,
+ params->version_info.available_versions,
+ params->version_info.available_versionslen,
+ /* fallback_version = */ 0)) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+ }
+
+ return 0;
+}
+
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+ const ngtcp2_version_info *version_info) {
+ assert(conn->server);
+ assert(conn->client_chosen_version == version_info->chosen_version);
+
+ return select_preferred_version(
+ conn->vneg.preferred_versions, conn->vneg.preferred_versionslen,
+ version_info->chosen_version, version_info->available_versions,
+ version_info->available_versionslen, version_info->chosen_version);
+}
+
+int ngtcp2_conn_set_remote_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
+ int rv;
+
+ /* We expect this function is called once per QUIC connection, but
+ GnuTLS server seems to call TLS extension callback twice if it
+ sends HelloRetryRequest. In practice, same QUIC transport
+ parameters are sent in the 2nd client flight, just returning 0
+ would cause no harm. */
+ if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) {
+ return 0;
+ }
+
+ /* Assume that ngtcp2_decode_transport_params sets default value if
+ active_connection_id_limit is omitted. */
+ if (params->active_connection_id_limit <
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ /* We assume that conn->dcid.current.cid is still the initial one.
+ This requires that transport parameter must be fed into
+ ngtcp2_conn as early as possible. */
+ if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &params->initial_scid)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->max_udp_payload_size < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (conn->server) {
+ if (params->version_info_present) {
+ if (!vneg_available_versions_includes(
+ params->version_info.available_versions,
+ params->version_info.available_versionslen,
+ params->version_info.chosen_version)) {
+ return NGTCP2_ERR_TRANSPORT_PARAM;
+ }
+
+ if (params->version_info.chosen_version != conn->client_chosen_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+ }
+
+ conn->negotiated_version =
+ ngtcp2_conn_server_negotiate_version(conn, &params->version_info);
+ if (conn->negotiated_version != conn->client_chosen_version) {
+ rv = conn_call_version_negotiation(conn, conn->negotiated_version,
+ &conn->rcid);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ conn->negotiated_version = conn->client_chosen_version;
+ }
+
+ conn->local.transport_params.version_info.chosen_version =
+ conn->negotiated_version;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "the negotiated version is 0x%08x",
+ conn->negotiated_version);
+ } else {
+ rv = conn_client_validate_transport_params(conn, params);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ ngtcp2_log_remote_tp(&conn->log,
+ conn->server
+ ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ params);
+
+ ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server,
+ NGTCP2_QLOG_SIDE_REMOTE);
+
+ if ((conn->server && conn->pktns.crypto.tx.ckm) ||
+ (!conn->server && conn->pktns.crypto.rx.ckm)) {
+ ngtcp2_transport_params_del(conn->remote.transport_params, conn->mem);
+ conn->remote.transport_params = NULL;
+
+ rv = ngtcp2_transport_params_copy_new(&conn->remote.transport_params,
+ params, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ conn_sync_stream_id_limit(conn);
+ conn->tx.max_offset = conn->remote.transport_params->initial_max_data;
+ } else {
+ assert(!conn->remote.pending_transport_params);
+
+ rv = ngtcp2_transport_params_copy_new(
+ &conn->remote.pending_transport_params, params, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED;
+
+ return 0;
+}
+
+int ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn,
+ const uint8_t *data,
+ size_t datalen) {
+ ngtcp2_transport_params params;
+ int rv;
+
+ rv = ngtcp2_decode_transport_params(
+ &params,
+ conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS,
+ data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return ngtcp2_conn_set_remote_transport_params(conn, &params);
+}
+
+const ngtcp2_transport_params *
+ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn) {
+ if (conn->remote.pending_transport_params) {
+ return conn->remote.pending_transport_params;
+ }
+
+ return conn->remote.transport_params;
+}
+
+void ngtcp2_conn_set_early_remote_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params) {
+ ngtcp2_transport_params *p;
+ (void)transport_params_version;
+
+ assert(!conn->server);
+ assert(!conn->remote.transport_params);
+
+ /* Assume that all pointer fields in p are NULL */
+ p = ngtcp2_mem_calloc(conn->mem, 1, sizeof(*p));
+
+ conn->remote.transport_params = p;
+
+ p->initial_max_streams_bidi = params->initial_max_streams_bidi;
+ p->initial_max_streams_uni = params->initial_max_streams_uni;
+ p->initial_max_stream_data_bidi_local =
+ params->initial_max_stream_data_bidi_local;
+ p->initial_max_stream_data_bidi_remote =
+ params->initial_max_stream_data_bidi_remote;
+ p->initial_max_stream_data_uni = params->initial_max_stream_data_uni;
+ p->initial_max_data = params->initial_max_data;
+ p->active_connection_id_limit =
+ ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ p->max_idle_timeout = params->max_idle_timeout;
+ if (!params->max_udp_payload_size) {
+ p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ } else {
+ p->max_udp_payload_size =
+ ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size);
+ }
+ p->disable_active_migration = params->disable_active_migration;
+ p->max_datagram_frame_size = params->max_datagram_frame_size;
+
+ /* These parameters are treated specially. If server accepts early
+ data, it must not set values for these parameters that are
+ smaller than these remembered values. */
+ conn->early.transport_params.initial_max_streams_bidi =
+ params->initial_max_streams_bidi;
+ conn->early.transport_params.initial_max_streams_uni =
+ params->initial_max_streams_uni;
+ conn->early.transport_params.initial_max_stream_data_bidi_local =
+ params->initial_max_stream_data_bidi_local;
+ conn->early.transport_params.initial_max_stream_data_bidi_remote =
+ params->initial_max_stream_data_bidi_remote;
+ conn->early.transport_params.initial_max_stream_data_uni =
+ params->initial_max_stream_data_uni;
+ conn->early.transport_params.initial_max_data = params->initial_max_data;
+ conn->early.transport_params.active_connection_id_limit =
+ params->active_connection_id_limit;
+ conn->early.transport_params.max_datagram_frame_size =
+ params->max_datagram_frame_size;
+
+ conn_sync_stream_id_limit(conn);
+
+ conn->tx.max_offset = p->initial_max_data;
+
+ ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, conn->server,
+ NGTCP2_QLOG_SIDE_REMOTE);
+}
+
+int ngtcp2_conn_set_local_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params) {
+ (void)transport_params_version;
+
+ assert(conn->server);
+ assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
+
+ if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ conn_set_local_transport_params(conn, params);
+
+ return 0;
+}
+
+int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) {
+ const ngtcp2_mem *mem = conn->mem;
+ ngtcp2_transport_params *params = &conn->local.transport_params;
+ ngtcp2_scid *scident;
+ int rv;
+
+ assert(1 == ngtcp2_ksl_len(&conn->scid.set));
+
+ if (params->active_connection_id_limit == 0) {
+ params->active_connection_id_limit =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ }
+
+ params->initial_scid = conn->oscid;
+
+ if (conn->oscid.datalen == 0) {
+ params->preferred_address_present = 0;
+ }
+
+ if (conn->server && params->preferred_address_present) {
+ scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
+ if (scident == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_scid_init(scident, 1, &params->preferred_address.cid);
+
+ rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident);
+ if (rv != 0) {
+ ngtcp2_mem_free(mem, scident);
+ return rv;
+ }
+
+ conn->scid.last_seq = 1;
+ }
+
+ conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset =
+ params->initial_max_data;
+ conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi;
+ conn->remote.bidi.max_streams = params->initial_max_streams_bidi;
+ conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni;
+ conn->remote.uni.max_streams = params->initial_max_streams_uni;
+
+ conn->flags |= NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED;
+
+ ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server,
+ NGTCP2_QLOG_SIDE_LOCAL);
+
+ return 0;
+}
+
+const ngtcp2_transport_params *
+ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn) {
+ return &conn->local.transport_params;
+}
+
+ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(ngtcp2_conn *conn,
+ uint8_t *dest,
+ size_t destlen) {
+ return ngtcp2_encode_transport_params(
+ dest, destlen,
+ conn->server ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
+ : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
+ &conn->local.transport_params);
+}
+
+int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
+ void *stream_user_data) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_conn_get_streams_bidi_left(conn) == 0) {
+ return NGTCP2_ERR_STREAM_ID_BLOCKED;
+ }
+
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id,
+ stream_user_data);
+ if (rv != 0) {
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+
+ *pstream_id = conn->local.bidi.next_stream_id;
+ conn->local.bidi.next_stream_id += 4;
+
+ return 0;
+}
+
+int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
+ void *stream_user_data) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ if (ngtcp2_conn_get_streams_uni_left(conn) == 0) {
+ return NGTCP2_ERR_STREAM_ID_BLOCKED;
+ }
+
+ strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
+ if (strm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id,
+ stream_user_data);
+ if (rv != 0) {
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+ return rv;
+ }
+ ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
+
+ *pstream_id = conn->local.uni.next_stream_id;
+ conn->local.uni.next_stream_id += 4;
+
+ return 0;
+}
+
+ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ return ngtcp2_map_find(&conn->strms, (uint64_t)stream_id);
+}
+
+ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen,
+ ngtcp2_tstamp ts) {
+ ngtcp2_vec datav;
+
+ datav.len = datalen;
+ datav.base = (uint8_t *)data;
+
+ return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi,
+ dest, destlen, pdatalen, flags,
+ stream_id, &datav, 1, ts);
+}
+
+static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn,
+ ngtcp2_path *path,
+ int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_ssize nwrite;
+ int undersized;
+
+ nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest,
+ destlen, vmsg, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (cstat->bytes_in_flight >= cstat->cwnd) {
+ conn->rst.is_cwnd_limited = 1;
+ }
+
+ if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd &&
+ conn->tx.strmq_nretrans == 0) {
+ if (conn->local.settings.no_tx_udp_payload_size_shaping) {
+ undersized =
+ (size_t)nwrite < conn->local.settings.max_tx_udp_payload_size;
+ } else {
+ undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size;
+ }
+
+ if (undersized) {
+ conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight;
+
+ if (conn->rst.app_limited == 0) {
+ conn->rst.app_limited = cstat->max_tx_udp_payload_size;
+ }
+ }
+ }
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_vmsg vmsg, *pvmsg;
+ ngtcp2_strm *strm;
+ int64_t datalen;
+
+ if (pdatalen) {
+ *pdatalen = -1;
+ }
+
+ if (stream_id != -1) {
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) {
+ return NGTCP2_ERR_STREAM_SHUT_WR;
+ }
+
+ datalen = ngtcp2_vec_len_varint(datav, datavcnt);
+ if (datalen == -1) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if ((uint64_t)datalen > NGTCP2_MAX_VARINT - strm->tx.offset ||
+ (uint64_t)datalen > NGTCP2_MAX_VARINT - conn->tx.offset) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ vmsg.type = NGTCP2_VMSG_TYPE_STREAM;
+ vmsg.stream.strm = strm;
+ vmsg.stream.flags = flags;
+ vmsg.stream.data = datav;
+ vmsg.stream.datacnt = datavcnt;
+ vmsg.stream.pdatalen = pdatalen;
+
+ pvmsg = &vmsg;
+ } else {
+ pvmsg = NULL;
+ }
+
+ return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
+ destlen, pvmsg, ts);
+}
+
+ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted,
+ uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts) {
+ ngtcp2_vmsg vmsg;
+ int64_t datalen;
+
+ if (paccepted) {
+ *paccepted = 0;
+ }
+
+ if (conn->remote.transport_params == NULL ||
+ conn->remote.transport_params->max_datagram_frame_size == 0) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ datalen = ngtcp2_vec_len_varint(datav, datavcnt);
+ if (datalen == -1
+#if UINT64_MAX > SIZE_MAX
+ || (uint64_t)datalen > SIZE_MAX
+#endif /* UINT64_MAX > SIZE_MAX */
+ ) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ if (conn->remote.transport_params->max_datagram_frame_size <
+ ngtcp2_pkt_datagram_framelen((size_t)datalen)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM;
+ vmsg.datagram.dgram_id = dgram_id;
+ vmsg.datagram.flags = flags;
+ vmsg.datagram.data = datav;
+ vmsg.datagram.datacnt = datavcnt;
+ vmsg.datagram.paccepted = paccepted;
+
+ return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
+ destlen, &vmsg, ts);
+}
+
+ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
+ int pkt_info_version, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nwrite;
+ size_t origlen;
+ size_t origdestlen = destlen;
+ int rv;
+ uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_ssize res = 0;
+ uint64_t server_tx_left;
+ uint64_t datalen;
+ uint64_t write_datalen = 0;
+ int64_t prev_in_pkt_num = -1;
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *rtbent;
+ (void)pkt_info_version;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ origlen = destlen =
+ conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
+ if (!ppe_pending && pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
+ case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED:
+ if (!conn_pacing_pkt_tx_allowed(conn, ts)) {
+ assert(!ppe_pending);
+
+ return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ }
+
+ nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts);
+ /* We might be unable to write a packet because of depletion of
+ congestion window budget, perhaps due to packet loss that
+ shrinks the window drastically. */
+ if (nwrite <= 0) {
+ return nwrite;
+ }
+ if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+ return nwrite;
+ }
+
+ assert(nwrite);
+ assert(dest[0] & NGTCP2_HEADER_FORM_BIT);
+ assert(conn->negotiated_version);
+
+ if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
+ NGTCP2_PKT_INITIAL) {
+ /* We have added padding already, but in that case, there is no
+ space left to write 1RTT packet. */
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ /* Break here so that we can coalesces 1RTT packet. */
+ break;
+ case NGTCP2_CS_SERVER_INITIAL:
+ case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
+ case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
+ if (!conn_pacing_pkt_tx_allowed(conn, ts)) {
+ assert(!ppe_pending);
+
+ if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ if (server_tx_left == 0) {
+ return 0;
+ }
+
+ origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left);
+ }
+
+ return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ }
+
+ if (!ppe_pending) {
+ if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ if (server_tx_left == 0) {
+ if (cstat->loss_detection_timer != UINT64_MAX) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
+ cstat->loss_detection_timer = UINT64_MAX;
+ }
+
+ return 0;
+ }
+
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt);
+ if (datalen == 0 || (datalen > 0 &&
+ (vmsg->stream.strm->tx.max_offset -
+ vmsg->stream.strm->tx.offset) &&
+ (conn->tx.max_offset - conn->tx.offset))) {
+ write_datalen =
+ conn_enforce_flow_control(conn, vmsg->stream.strm, datalen);
+ write_datalen =
+ ngtcp2_min(write_datalen, NGTCP2_MIN_COALESCED_PAYLOADLEN);
+ write_datalen += NGTCP2_STREAM_OVERHEAD;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ write_datalen =
+ ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt) +
+ NGTCP2_DATAGRAM_OVERHEAD;
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ if (conn->in_pktns && write_datalen > 0) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ prev_in_pkt_num = rtbent->hd.pkt_num;
+ }
+ }
+ }
+
+ nwrite = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+
+ if (conn->in_pktns && write_datalen > 0) {
+ it = ngtcp2_rtb_head(&conn->in_pktns->rtb);
+ if (!ngtcp2_ksl_it_end(&it)) {
+ rtbent = ngtcp2_ksl_it_get(&it);
+ if (rtbent->hd.pkt_num != prev_in_pkt_num &&
+ (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ /* We have added padding already, but in that case, there
+ is no space left to write 1RTT packet. */
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ }
+ }
+ }
+ if (conn->state != NGTCP2_CS_POST_HANDSHAKE &&
+ conn->pktns.crypto.tx.ckm == NULL) {
+ return res;
+ }
+ break;
+ case NGTCP2_CS_POST_HANDSHAKE:
+ if (!conn_pacing_pkt_tx_allowed(conn, ts)) {
+ assert(!ppe_pending);
+
+ if (conn->server &&
+ !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ if (server_tx_left == 0) {
+ return 0;
+ }
+
+ origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left);
+ }
+
+ return conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts);
+ }
+
+ break;
+ case NGTCP2_CS_CLOSING:
+ return NGTCP2_ERR_CLOSING;
+ case NGTCP2_CS_DRAINING:
+ return NGTCP2_ERR_DRAINING;
+ default:
+ return 0;
+ }
+
+ assert(conn->pktns.crypto.tx.ckm);
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ if (vmsg) {
+ switch (vmsg->type) {
+ case NGTCP2_VMSG_TYPE_STREAM:
+ if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ break;
+ case NGTCP2_VMSG_TYPE_DATAGRAM:
+ if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_MORE;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (ppe_pending) {
+ res = conn->pkt.hs_spktlen;
+ conn->pkt.hs_spktlen = 0;
+ if (conn->pkt.require_padding) {
+ wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+ /* dest and destlen have already been adjusted in ppe in the first
+ run. They are adjusted for probe packet later. */
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
+ wflags, ts);
+ goto fin;
+ } else {
+ conn->pkt.require_padding =
+ (wflags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING);
+
+ if (conn->state == NGTCP2_CS_POST_HANDSHAKE) {
+ rv = conn_prepare_key_update(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) {
+ destlen = 0;
+ } else {
+ if (res == 0) {
+ nwrite =
+ conn_write_path_response(conn, path, pi, dest, origdestlen, ts);
+ if (nwrite) {
+ goto fin;
+ }
+
+ if (conn->pv) {
+ nwrite =
+ conn_write_path_challenge(conn, path, pi, dest, origdestlen, ts);
+ if (nwrite) {
+ goto fin;
+ }
+ }
+
+ if (conn->pmtud &&
+ (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) &&
+ (!conn->hs_pktns ||
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) {
+ nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts);
+ if (nwrite) {
+ goto fin;
+ }
+ }
+ }
+ }
+
+ if (conn->server &&
+ !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ origlen = (size_t)ngtcp2_min((uint64_t)origlen, server_tx_left);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+
+ if (server_tx_left == 0 &&
+ conn->cstat.loss_detection_timer != UINT64_MAX) {
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled due to amplification limit");
+ conn->cstat.loss_detection_timer = UINT64_MAX;
+ }
+ }
+ }
+
+ if (res == 0) {
+ if (conn_handshake_remnants_left(conn)) {
+ if (conn_handshake_probe_left(conn) ||
+ /* Allow exceeding CWND if an Handshake packet needs to be
+ sent in order to avoid dead lock. In some situation,
+ typically for client, 1 RTT packets may occupy in-flight
+ bytes (e.g., some large requests and PMTUD), and
+ Handshake packet loss shrinks CWND, and we may get in the
+ situation that we are unable to send Handshake packet. */
+ (conn->hs_pktns->rtb.num_pto_eliciting == 0 &&
+ ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq))) {
+ destlen = origlen;
+ }
+ nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen,
+ /* write_datalen = */ 0, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ if (nwrite > 0) {
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ } else if (destlen == 0) {
+ res = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
+ if (res) {
+ return res;
+ }
+ }
+ }
+ }
+
+ if (conn->pktns.rtb.probe_pkt_left) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "transmit probe pkt left=%zu",
+ conn->pktns.rtb.probe_pkt_left);
+
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
+ wflags, ts);
+
+ goto fin;
+ }
+
+ nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
+ wflags, ts);
+ if (nwrite) {
+ assert(nwrite != NGTCP2_ERR_NOBUF);
+ goto fin;
+ }
+
+ if (res == 0) {
+ nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts);
+ }
+
+fin:
+ conn->pkt.hs_spktlen = 0;
+
+ if (nwrite >= 0) {
+ res += nwrite;
+ return res;
+ }
+ /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above.
+ ppe_pending cannot be used here. */
+ if (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) {
+ conn->pkt.hs_spktlen = res;
+ }
+
+ return nwrite;
+}
+
+static ngtcp2_ssize
+conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen, uint8_t pkt_type,
+ uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_ssize res = 0, nwrite;
+ ngtcp2_frame fr;
+ uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE;
+
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ fr.connection_close.error_code = error_code;
+ fr.connection_close.frame_type = 0;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) &&
+ pkt_type != NGTCP2_PKT_INITIAL) {
+ if (in_pktns && conn->server) {
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+ NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ res += nwrite;
+ }
+
+ if (pkt_type != NGTCP2_PKT_HANDSHAKE && hs_pktns &&
+ hs_pktns->crypto.tx.ckm) {
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE,
+ NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ res += nwrite;
+ }
+ }
+
+ if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) {
+ flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+ }
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr,
+ NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (res == 0) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ return res;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ uint8_t pkt_type;
+ ngtcp2_ssize nwrite;
+ uint64_t server_tx_left;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ return NGTCP2_ERR_INVALID_STATE;
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ return 0;
+ default:
+ break;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
+ if (pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ if (conn->server) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
+ if (conn->state == NGTCP2_CS_POST_HANDSHAKE ||
+ (conn->server && conn->pktns.crypto.tx.ckm)) {
+ pkt_type = NGTCP2_PKT_1RTT;
+ } else if (hs_pktns && hs_pktns->crypto.tx.ckm) {
+ pkt_type = NGTCP2_PKT_HANDSHAKE;
+ } else if (in_pktns && in_pktns->crypto.tx.ckm) {
+ pkt_type = NGTCP2_PKT_INITIAL;
+ } else {
+ /* This branch is taken if server has not read any Initial packet
+ from client. */
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type,
+ error_code, reason, reasonlen, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ conn->state = NGTCP2_CS_CLOSING;
+
+ return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts) {
+ ngtcp2_ssize nwrite;
+ ngtcp2_ssize res = 0;
+ ngtcp2_frame fr;
+ uint64_t server_tx_left;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ if (conn_check_pkt_num_exhausted(conn)) {
+ return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
+ }
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLIENT_INITIAL:
+ return NGTCP2_ERR_INVALID_STATE;
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ return 0;
+ default:
+ break;
+ }
+
+ if (path) {
+ ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
+ }
+
+ destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
+ if (pi) {
+ pi->ecn = NGTCP2_ECN_NOT_ECT;
+ }
+
+ if (conn->server) {
+ server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+ destlen = (size_t)ngtcp2_min((uint64_t)destlen, server_tx_left);
+ }
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
+ nwrite = conn_write_connection_close(conn, pi, dest, destlen,
+ conn->hs_pktns->crypto.tx.ckm
+ ? NGTCP2_PKT_HANDSHAKE
+ : NGTCP2_PKT_INITIAL,
+ NGTCP2_APPLICATION_ERROR, NULL, 0, ts);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+ res = nwrite;
+ dest += nwrite;
+ destlen -= (size_t)nwrite;
+ }
+
+ if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+ assert(res);
+
+ if (!conn->server || !conn->pktns.crypto.tx.ckm) {
+ return res;
+ }
+ }
+
+ assert(conn->pktns.crypto.tx.ckm);
+
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP;
+ fr.connection_close.error_code = app_error_code;
+ fr.connection_close.frame_type = 0;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
+
+ nwrite = ngtcp2_conn_write_single_frame_pkt(
+ conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+ &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ res += nwrite;
+
+ if (res == 0) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ conn->state = NGTCP2_CS_CLOSING;
+
+ return res;
+}
+
+static void
+connection_close_error_init(ngtcp2_connection_close_error *ccerr,
+ ngtcp2_connection_close_error_code_type type,
+ uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen) {
+ ccerr->type = type;
+ ccerr->error_code = error_code;
+ ccerr->frame_type = 0;
+ ccerr->reason = (uint8_t *)reason;
+ ccerr->reasonlen = reasonlen;
+}
+
+void ngtcp2_connection_close_error_default(
+ ngtcp2_connection_close_error *ccerr) {
+ connection_close_error_init(ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ NGTCP2_NO_ERROR, NULL, 0);
+}
+
+void ngtcp2_connection_close_error_set_transport_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen) {
+ connection_close_error_init(ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ error_code, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_liberr(
+ ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+ size_t reasonlen) {
+ switch (liberr) {
+ case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
+ connection_close_error_init(
+ ccerr,
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+ NGTCP2_NO_ERROR, reason, reasonlen);
+
+ return;
+ case NGTCP2_ERR_IDLE_CLOSE:
+ connection_close_error_init(
+ ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE,
+ NGTCP2_NO_ERROR, reason, reasonlen);
+
+ return;
+ };
+
+ ngtcp2_connection_close_error_set_transport_error(
+ ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
+ reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+ const uint8_t *reason, size_t reasonlen) {
+ ngtcp2_connection_close_error_set_transport_error(
+ ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_application_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen) {
+ connection_close_error_init(
+ ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code,
+ reason, reasonlen);
+}
+
+ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) {
+ (void)pkt_info_version;
+
+ switch (ccerr->type) {
+ case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT:
+ return ngtcp2_conn_write_connection_close_pkt(
+ conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+ ccerr->reasonlen, ts);
+ case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION:
+ return ngtcp2_conn_write_application_close_pkt(
+ conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+ ccerr->reasonlen, ts);
+ default:
+ return 0;
+ }
+}
+
+int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) {
+ return conn->state == NGTCP2_CS_CLOSING;
+}
+
+int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) {
+ return conn->state == NGTCP2_CS_DRAINING;
+}
+
+int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ int rv;
+
+ rv = conn_call_stream_close(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_map_remove(&conn->strms, (ngtcp2_map_key_type)strm->stream_id);
+ if (rv != 0) {
+ assert(rv != NGTCP2_ERR_INVALID_ARGUMENT);
+ return rv;
+ }
+
+ if (ngtcp2_strm_is_tx_queued(strm)) {
+ ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe);
+ if (!ngtcp2_strm_streamfrq_empty(strm)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
+ }
+
+ ngtcp2_strm_free(strm);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
+
+ return 0;
+}
+
+int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn,
+ ngtcp2_strm *strm) {
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) ==
+ NGTCP2_STRM_FLAG_SHUT_RDWR &&
+ ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) ||
+ ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) &&
+ (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) &&
+ (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) ||
+ ngtcp2_strm_is_all_tx_data_fin_acked(strm))) {
+ return ngtcp2_conn_close_stream(conn, strm);
+ }
+ return 0;
+}
+
+/*
+ * conn_shutdown_stream_write closes send stream with error code
+ * |app_error_code|. RESET_STREAM frame is scheduled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ ngtcp2_strm_set_app_error_code(strm, app_error_code);
+
+ if ((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) ||
+ ngtcp2_strm_is_all_tx_data_fin_acked(strm)) {
+ return 0;
+ }
+
+ /* Set this flag so that we don't accidentally send DATA to this
+ stream. */
+ strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
+
+ ngtcp2_strm_streamfrq_clear(strm);
+
+ return conn_reset_stream(conn, strm, app_error_code);
+}
+
+/*
+ * conn_shutdown_stream_read closes read stream with error code
+ * |app_error_code|. STOP_SENDING frame is scheduled.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ ngtcp2_strm_set_app_error_code(strm, app_error_code);
+
+ if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
+ return 0;
+ }
+ if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
+ ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) {
+ return 0;
+ }
+
+ /* Extend connection flow control window for the amount of data
+ which are not passed to application. */
+ if (!(strm->flags &
+ (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST))) {
+ ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset -
+ ngtcp2_strm_rx_offset(strm));
+ }
+
+ strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING;
+
+ return conn_stop_sending(conn, strm, app_error_code);
+}
+
+int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ int rv;
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return 0;
+ }
+
+ if (bidi_stream(stream_id) || !conn_local_stream(conn, stream_id)) {
+ rv = conn_shutdown_stream_read(conn, strm, app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (bidi_stream(stream_id) || conn_local_stream(conn, stream_id)) {
+ rv = conn_shutdown_stream_write(conn, strm, app_error_code);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ ngtcp2_strm *strm;
+
+ if (!bidi_stream(stream_id) && !conn_local_stream(conn, stream_id)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return conn_shutdown_stream_write(conn, strm, app_error_code);
+}
+
+int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code) {
+ ngtcp2_strm *strm;
+
+ if (!bidi_stream(stream_id) && conn_local_stream(conn, stream_id)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return conn_shutdown_stream_read(conn, strm, app_error_code);
+}
+
+/*
+ * conn_extend_max_stream_offset extends stream level flow control
+ * window by |datalen| of the stream denoted by |strm|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ uint64_t datalen) {
+ ngtcp2_strm *top;
+
+ if (datalen > NGTCP2_MAX_VARINT ||
+ strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) {
+ strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT;
+ } else {
+ strm->rx.unsent_max_offset += datalen;
+ }
+
+ if (!(strm->flags &
+ (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) &&
+ !ngtcp2_strm_is_tx_queued(strm) &&
+ conn_should_send_max_stream_data(conn, strm)) {
+ if (!ngtcp2_pq_empty(&conn->tx.strmq)) {
+ top = ngtcp2_conn_tx_strmq_top(conn);
+ strm->cycle = top->cycle;
+ }
+ strm->cycle = conn_tx_strmq_first_cycle(conn);
+ return ngtcp2_conn_tx_strmq_push(conn, strm);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t datalen) {
+ ngtcp2_strm *strm;
+
+ strm = ngtcp2_conn_find_stream(conn, stream_id);
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return conn_extend_max_stream_offset(conn, strm, datalen);
+}
+
+void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) {
+ if (NGTCP2_MAX_VARINT < datalen ||
+ conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) {
+ conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT;
+ return;
+ }
+
+ conn->rx.unsent_max_offset += datalen;
+}
+
+void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) {
+ handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n);
+}
+
+void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) {
+ handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n);
+}
+
+const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) {
+ return &conn->dcid.current.cid;
+}
+
+const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) {
+ return &conn->rcid;
+}
+
+uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) {
+ return conn->client_chosen_version;
+}
+
+uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) {
+ return conn->negotiated_version;
+}
+
+static int delete_strms_pq_each(void *data, void *ptr) {
+ ngtcp2_conn *conn = ptr;
+ ngtcp2_strm *s = data;
+
+ if (ngtcp2_strm_is_tx_queued(s)) {
+ ngtcp2_pq_remove(&conn->tx.strmq, &s->pe);
+ if (!ngtcp2_strm_streamfrq_empty(s)) {
+ assert(conn->tx.strmq_nretrans);
+ --conn->tx.strmq_nretrans;
+ }
+ }
+
+ ngtcp2_strm_free(s);
+ ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
+
+ return 0;
+}
+
+/*
+ * conn_discard_early_data_state discards any connection states which
+ * are altered by any operations during early data transfer.
+ */
+static void conn_discard_early_data_state(ngtcp2_conn *conn) {
+ ngtcp2_frame_chain **pfrc, *frc;
+
+ ngtcp2_rtb_remove_early_data(&conn->pktns.rtb, &conn->cstat);
+
+ ngtcp2_map_each_free(&conn->strms, delete_strms_pq_each, conn);
+ ngtcp2_map_clear(&conn->strms);
+
+ conn->tx.offset = 0;
+
+ conn->rx.unsent_max_offset = conn->rx.max_offset =
+ conn->local.transport_params.initial_max_data;
+
+ conn->remote.bidi.unsent_max_streams = conn->remote.bidi.max_streams =
+ conn->local.transport_params.initial_max_streams_bidi;
+
+ conn->remote.uni.unsent_max_streams = conn->remote.uni.max_streams =
+ conn->local.transport_params.initial_max_streams_uni;
+
+ if (conn->server) {
+ conn->local.bidi.next_stream_id = 1;
+ conn->local.uni.next_stream_id = 3;
+ } else {
+ conn->local.bidi.next_stream_id = 0;
+ conn->local.uni.next_stream_id = 2;
+ }
+
+ for (pfrc = &conn->pktns.tx.frq; *pfrc;) {
+ frc = *pfrc;
+ *pfrc = (*pfrc)->next;
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ }
+}
+
+int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
+ if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
+ return 0;
+ }
+
+ conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
+
+ conn_discard_early_data_state(conn);
+
+ if (conn->callbacks.early_data_rejected) {
+ return conn->callbacks.early_data_rejected(conn, conn->user_data);
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_get_early_data_rejected(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) != 0;
+}
+
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+
+ if (cstat->min_rtt == UINT64_MAX) {
+ cstat->latest_rtt = rtt;
+ cstat->min_rtt = rtt;
+ cstat->smoothed_rtt = rtt;
+ cstat->rttvar = rtt / 2;
+ cstat->first_rtt_sample_ts = ts;
+ } else {
+ if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) {
+ assert(conn->remote.transport_params);
+
+ ack_delay =
+ ngtcp2_min(ack_delay, conn->remote.transport_params->max_ack_delay);
+ } else if (ack_delay > 0 && rtt >= cstat->min_rtt &&
+ rtt < cstat->min_rtt + ack_delay) {
+ /* Ignore RTT sample if adjusting ack_delay causes the sample
+ less than min_rtt before handshake confirmation. */
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64
+ " min_rtt=%" PRIu64 " ack_delay=%" PRIu64,
+ rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS,
+ ack_delay / NGTCP2_MILLISECONDS);
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ cstat->latest_rtt = rtt;
+ cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt);
+
+ if (rtt >= cstat->min_rtt + ack_delay) {
+ rtt -= ack_delay;
+ }
+
+ cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt
+ ? rtt - cstat->smoothed_rtt
+ : cstat->smoothed_rtt - rtt)) /
+ 4;
+ cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8;
+ }
+
+ ngtcp2_log_info(
+ &conn->log, NGTCP2_LOG_EVENT_RCV,
+ "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64
+ " rttvar=%" PRIu64 " ack_delay=%" PRIu64,
+ cstat->latest_rtt / NGTCP2_MILLISECONDS,
+ cstat->min_rtt / NGTCP2_MILLISECONDS,
+ cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
+ cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS);
+
+ return 0;
+}
+
+void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
+ int conn_stat_version,
+ ngtcp2_conn_stat *cstat) {
+ (void)conn_stat_version;
+
+ *cstat = conn->cstat;
+}
+
+static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn,
+ ngtcp2_tstamp *ploss_time,
+ ngtcp2_pktns **ppktns) {
+ ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns};
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_duration *loss_time = cstat->loss_time + 1;
+ ngtcp2_tstamp earliest_loss_time = cstat->loss_time[NGTCP2_PKTNS_ID_INITIAL];
+ ngtcp2_pktns *pktns = conn->in_pktns;
+ size_t i;
+
+ for (i = 0; i < ngtcp2_arraylen(ns); ++i) {
+ if (ns[i] == NULL || loss_time[i] >= earliest_loss_time) {
+ continue;
+ }
+
+ earliest_loss_time = loss_time[i];
+ pktns = ns[i];
+ }
+
+ if (ploss_time) {
+ *ploss_time = earliest_loss_time;
+ }
+ if (ppktns) {
+ *ppktns = pktns;
+ }
+}
+
+static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns};
+ size_t i;
+ ngtcp2_tstamp earliest_ts = UINT64_MAX, t;
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_tstamp *times = cstat->last_tx_pkt_ts;
+ ngtcp2_duration duration =
+ compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) *
+ (1ULL << cstat->pto_count);
+
+ for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
+ if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
+ (times[i] == UINT64_MAX ||
+ (i == NGTCP2_PKTNS_ID_APPLICATION &&
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+ continue;
+ }
+
+ t = times[i] + duration;
+
+ if (i == NGTCP2_PKTNS_ID_APPLICATION) {
+ assert(conn->remote.transport_params);
+ t += conn->remote.transport_params->max_ack_delay *
+ (1ULL << cstat->pto_count);
+ }
+
+ if (t < earliest_ts) {
+ earliest_ts = t;
+ }
+ }
+
+ if (earliest_ts == UINT64_MAX) {
+ return ts + duration;
+ }
+
+ return earliest_ts;
+}
+
+void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ ngtcp2_duration timeout;
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_pktns *pktns = &conn->pktns;
+ ngtcp2_tstamp earliest_loss_time;
+
+ conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL);
+
+ if (earliest_loss_time != UINT64_MAX) {
+ cstat->loss_detection_timer = earliest_loss_time;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss_detection_timer=%" PRIu64 " nonzero crypto loss time",
+ cstat->loss_detection_timer);
+ return;
+ }
+
+ if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) &&
+ (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) &&
+ (pktns->rtb.num_pto_eliciting == 0 ||
+ !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) &&
+ (conn->server ||
+ (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
+ NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+ if (cstat->loss_detection_timer != UINT64_MAX) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer canceled");
+ cstat->loss_detection_timer = UINT64_MAX;
+ cstat->pto_count = 0;
+ }
+ return;
+ }
+
+ cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts);
+
+ timeout =
+ cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64,
+ cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS);
+}
+
+int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ ngtcp2_conn_stat *cstat = &conn->cstat;
+ int rv;
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_tstamp earliest_loss_time;
+ ngtcp2_pktns *loss_pktns = NULL;
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ switch (conn->state) {
+ case NGTCP2_CS_CLOSING:
+ case NGTCP2_CS_DRAINING:
+ cstat->loss_detection_timer = UINT64_MAX;
+ cstat->pto_count = 0;
+ return 0;
+ default:
+ break;
+ }
+
+ if (cstat->loss_detection_timer == UINT64_MAX) {
+ return 0;
+ }
+
+ conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+ "loss detection timer fired");
+
+ if (earliest_loss_time != UINT64_MAX) {
+ assert(loss_pktns);
+
+ rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+ return 0;
+ }
+
+ if (!conn->server && !conn_is_handshake_completed(conn)) {
+ if (hs_pktns->crypto.tx.ckm) {
+ hs_pktns->rtb.probe_pkt_left = 1;
+ } else {
+ in_pktns->rtb.probe_pkt_left = 1;
+ }
+ } else {
+ if (in_pktns && in_pktns->rtb.num_pto_eliciting) {
+ in_pktns->rtb.probe_pkt_left = 1;
+
+ assert(hs_pktns);
+
+ if (conn->server && hs_pktns->rtb.num_pto_eliciting) {
+ /* let server coalesce packets */
+ hs_pktns->rtb.probe_pkt_left = 1;
+ }
+ } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) {
+ hs_pktns->rtb.probe_pkt_left = 1;
+ } else {
+ conn->pktns.rtb.probe_pkt_left = 2;
+ }
+ }
+
+ ++cstat->pto_count;
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu",
+ cstat->pto_count);
+
+ ngtcp2_conn_set_loss_detection_timer(conn, ts);
+
+ return 0;
+}
+
+static int conn_buffer_crypto_data(ngtcp2_conn *conn, const uint8_t **pdata,
+ ngtcp2_pktns *pktns, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+ ngtcp2_buf_chain **pbufchain = &pktns->crypto.tx.data;
+
+ if (*pbufchain) {
+ for (; (*pbufchain)->next; pbufchain = &(*pbufchain)->next)
+ ;
+
+ if (ngtcp2_buf_left(&(*pbufchain)->buf) < datalen) {
+ pbufchain = &(*pbufchain)->next;
+ }
+ }
+
+ if (!*pbufchain) {
+ rv = ngtcp2_buf_chain_new(pbufchain, ngtcp2_max(1024, datalen), conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ *pdata = (*pbufchain)->buf.last;
+ (*pbufchain)->buf.last = ngtcp2_cpymem((*pbufchain)->buf.last, data, datalen);
+
+ return 0;
+}
+
+int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, const size_t datalen) {
+ ngtcp2_pktns *pktns;
+ ngtcp2_frame_chain *frc;
+ ngtcp2_crypto *fr;
+ int rv;
+
+ if (datalen == 0) {
+ return 0;
+ }
+
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ assert(conn->in_pktns);
+ pktns = conn->in_pktns;
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ assert(conn->hs_pktns);
+ pktns = conn->hs_pktns;
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ pktns = &conn->pktns;
+ break;
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ rv = conn_buffer_crypto_data(conn, &data, pktns, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ fr = &frc->fr.crypto;
+
+ fr->type = NGTCP2_FRAME_CRYPTO;
+ fr->offset = pktns->crypto.tx.offset;
+ fr->datacnt = 1;
+ fr->data[0].len = datalen;
+ fr->data[0].base = (uint8_t *)data;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ pktns->crypto.strm.tx.offset += datalen;
+ pktns->crypto.tx.offset += datalen;
+
+ return 0;
+}
+
+int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token,
+ size_t tokenlen) {
+ int rv;
+ ngtcp2_frame_chain *nfrc;
+
+ assert(conn->server);
+ assert(token);
+ assert(tokenlen);
+
+ rv = ngtcp2_frame_chain_new_token_objalloc_new(
+ &nfrc, token, tokenlen, &conn->frc_objalloc, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->next = conn->pktns.tx.frq;
+ conn->pktns.tx.frq = nfrc;
+
+ return 0;
+}
+
+ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) {
+ assert(!ngtcp2_pq_empty(&conn->tx.strmq));
+ return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe);
+}
+
+void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) {
+ ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn);
+ assert(strm);
+ ngtcp2_pq_pop(&conn->tx.strmq);
+ strm->pe.index = NGTCP2_PQ_BAD_INDEX;
+}
+
+int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) {
+ return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe);
+}
+
+static int conn_has_uncommited_preferred_address_cid(ngtcp2_conn *conn) {
+ return conn->server &&
+ !(conn->flags & NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED) &&
+ conn->oscid.datalen &&
+ conn->local.transport_params.preferred_address_present;
+}
+
+size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) {
+ return ngtcp2_ksl_len(&conn->scid.set) +
+ (size_t)conn_has_uncommited_preferred_address_cid(conn);
+}
+
+size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) {
+ ngtcp2_cid *origdest = dest;
+ ngtcp2_ksl_it it;
+ ngtcp2_scid *scid;
+
+ for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ scid = ngtcp2_ksl_it_get(&it);
+ *dest++ = scid->cid;
+ }
+
+ if (conn_has_uncommited_preferred_address_cid(conn)) {
+ *dest++ = conn->local.transport_params.preferred_address.cid;
+ }
+
+ return (size_t)(dest - origdest);
+}
+
+size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) {
+ size_t n = 1; /* for conn->dcid.current */
+ ngtcp2_pv *pv = conn->pv;
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ return 0;
+ }
+
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ ++n;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ ++n;
+ }
+ }
+
+ n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
+
+ return n;
+}
+
+static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest,
+ const ngtcp2_dcid *src) {
+ dest->seq = src->seq;
+ dest->cid = src->cid;
+ ngtcp2_path_storage_init2(&dest->ps, &src->ps.path);
+ if ((dest->token_present =
+ (src->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) != 0)) {
+ memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ }
+}
+
+size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
+ ngtcp2_pv *pv = conn->pv;
+ ngtcp2_cid_token *orig = dest;
+ ngtcp2_dcid *dcid;
+ size_t len, i;
+
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
+ return 0;
+ }
+
+ copy_dcid_to_cid_token(dest, &conn->dcid.current);
+ ++dest;
+
+ if (pv) {
+ if (pv->dcid.seq != conn->dcid.current.seq) {
+ copy_dcid_to_cid_token(dest, &pv->dcid);
+ ++dest;
+ }
+ if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
+ pv->fallback_dcid.seq != conn->dcid.current.seq &&
+ pv->fallback_dcid.seq != pv->dcid.seq) {
+ copy_dcid_to_cid_token(dest, &pv->fallback_dcid);
+ ++dest;
+ }
+ }
+
+ len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
+ for (i = 0; i < len; ++i) {
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
+ copy_dcid_to_cid_token(dest, dcid);
+ ++dest;
+ }
+
+ return (size_t)(dest - orig);
+}
+
+void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) {
+ ngtcp2_addr *dest = &conn->dcid.current.ps.path.local;
+
+ assert(addr->addrlen <=
+ (ngtcp2_socklen)sizeof(conn->dcid.current.ps.local_addrbuf));
+ ngtcp2_addr_copy(dest, addr);
+}
+
+void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) {
+ conn->dcid.current.ps.path.user_data = path_user_data;
+}
+
+const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) {
+ return &conn->dcid.current.ps.path;
+}
+
+size_t ngtcp2_conn_get_max_tx_udp_payload_size(ngtcp2_conn *conn) {
+ return conn->local.settings.max_tx_udp_payload_size;
+}
+
+size_t ngtcp2_conn_get_path_max_tx_udp_payload_size(ngtcp2_conn *conn) {
+ if (conn->local.settings.no_tx_udp_payload_size_shaping) {
+ return ngtcp2_conn_get_max_tx_udp_payload_size(conn);
+ }
+
+ return conn->dcid.current.max_udp_payload_size;
+}
+
+static int conn_initiate_migration_precheck(ngtcp2_conn *conn,
+ const ngtcp2_addr *local_addr) {
+ if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) ||
+ conn->remote.transport_params->disable_active_migration ||
+ conn->dcid.current.cid.datalen == 0 ||
+ (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR))) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
+ return NGTCP2_ERR_CONN_ID_BLOCKED;
+ }
+
+ if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, local_addr)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ return 0;
+}
+
+int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_dcid *dcid;
+ ngtcp2_duration pto, initial_pto, timeout;
+ ngtcp2_pv *pv;
+
+ assert(!conn->server);
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ rv = conn_initiate_migration_precheck(conn, &path->local);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_conn_stop_pmtud(conn);
+
+ if (conn->pv) {
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rv = conn_retire_dcid(conn, &conn->dcid.current, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, path);
+
+ ngtcp2_dcid_copy(&conn->dcid.current, dcid);
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+
+ conn_reset_congestion_state(conn, ts);
+ conn_reset_ecn_validation_state(conn);
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ /* TODO It might be better to add a new flag which indicates that a
+ connection should be closed if this path validation failed. The
+ current design allows an application to continue, by migrating
+ into yet another path. */
+ rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->pv = pv;
+
+ return conn_call_activate_dcid(conn, &conn->dcid.current);
+}
+
+int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_dcid *dcid;
+ ngtcp2_duration pto, initial_pto, timeout;
+ ngtcp2_pv *pv;
+
+ assert(!conn->server);
+
+ conn->log.last_ts = ts;
+ conn->qlog.last_ts = ts;
+
+ rv = conn_initiate_migration_precheck(conn, &path->local);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->pv) {
+ rv = conn_abort_pv(conn, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+ ngtcp2_dcid_set_path(dcid, path);
+
+ pto = conn_compute_pto(conn, &conn->pktns);
+ initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
+ timeout = 3 * ngtcp2_max(pto, initial_pto);
+
+ rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &conn->log,
+ conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
+ conn->pv = pv;
+
+ return conn_call_activate_dcid(conn, &pv->dcid);
+}
+
+uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) {
+ return conn->local.uni.max_streams;
+}
+
+uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) {
+ return conn->tx.max_offset - conn->tx.offset;
+}
+
+uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn,
+ int64_t stream_id) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return strm->tx.max_offset - strm->tx.offset;
+}
+
+uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) {
+ uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id);
+
+ return n > conn->local.bidi.max_streams
+ ? 0
+ : conn->local.bidi.max_streams - n + 1;
+}
+
+uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) {
+ uint64_t n = ngtcp2_ord_stream_id(conn->local.uni.next_stream_id);
+
+ return n > conn->local.uni.max_streams ? 0
+ : conn->local.uni.max_streams - n + 1;
+}
+
+uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) {
+ uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
+ uint64_t cwnd = conn_get_cwnd(conn);
+
+ if (cwnd > bytes_in_flight) {
+ return cwnd - bytes_in_flight;
+ }
+
+ return 0;
+}
+
+ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
+ ngtcp2_duration trpto;
+ ngtcp2_duration idle_timeout;
+
+ /* TODO Remote max_idle_timeout becomes effective after handshake
+ completion. */
+
+ if (!conn_is_handshake_completed(conn) ||
+ conn->remote.transport_params->max_idle_timeout == 0 ||
+ (conn->local.transport_params.max_idle_timeout &&
+ conn->local.transport_params.max_idle_timeout <
+ conn->remote.transport_params->max_idle_timeout)) {
+ idle_timeout = conn->local.transport_params.max_idle_timeout;
+ } else {
+ idle_timeout = conn->remote.transport_params->max_idle_timeout;
+ }
+
+ if (idle_timeout == 0) {
+ return UINT64_MAX;
+ }
+
+ trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn)
+ ? &conn->pktns
+ : conn->hs_pktns);
+
+ return conn->idle_ts + ngtcp2_max(idle_timeout, trpto);
+}
+
+ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) {
+ return conn_compute_pto(
+ conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns);
+}
+
+void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ assert(conn->in_pktns);
+ conn->in_pktns->crypto.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) {
+ assert(conn->in_pktns);
+ return &conn->in_pktns->crypto.ctx;
+}
+
+void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ assert(!conn->crypto.retry_aead_ctx.native_handle);
+
+ conn->crypto.retry_aead = *aead;
+ conn->crypto.retry_aead_ctx = *aead_ctx;
+}
+
+void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ assert(conn->hs_pktns);
+ conn->hs_pktns->crypto.ctx = *ctx;
+ conn->pktns.crypto.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) {
+ return &conn->pktns.crypto.ctx;
+}
+
+void ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx) {
+ conn->early.ctx = *ctx;
+}
+
+const ngtcp2_crypto_ctx *ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn) {
+ return &conn->early.ctx;
+}
+
+void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn) {
+ return conn->crypto.tls_native_handle;
+}
+
+void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn,
+ void *tls_native_handle) {
+ conn->crypto.tls_native_handle = tls_native_handle;
+}
+
+void ngtcp2_conn_get_connection_close_error(
+ ngtcp2_conn *conn, ngtcp2_connection_close_error *ccerr) {
+ *ccerr = conn->rx.ccerr;
+}
+
+void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) {
+ conn->crypto.tls_error = liberr;
+}
+
+int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) {
+ return conn->crypto.tls_error;
+}
+
+void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert) {
+ conn->crypto.tls_alert = alert;
+}
+
+uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn) {
+ return conn->crypto.tls_alert;
+}
+
+int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
+ return conn_local_stream(conn, stream_id);
+}
+
+int ngtcp2_conn_is_server(ngtcp2_conn *conn) { return conn->server; }
+
+int ngtcp2_conn_after_retry(ngtcp2_conn *conn) {
+ return (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) != 0;
+}
+
+int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id,
+ void *stream_user_data) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return NGTCP2_ERR_STREAM_NOT_FOUND;
+ }
+
+ strm->stream_user_data = stream_user_data;
+
+ return 0;
+}
+
+void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+ double pacing_rate;
+ ngtcp2_duration interval;
+
+ if (conn->tx.pacing.pktlen == 0) {
+ return;
+ }
+
+ if (conn->cstat.pacing_rate > 0) {
+ pacing_rate = conn->cstat.pacing_rate;
+ } else {
+ /* 1.25 is the under-utilization avoidance factor described in
+ https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
+ pacing_rate =
+ (double)conn->cstat.cwnd / (double)conn->cstat.smoothed_rtt * 1.25;
+ }
+
+ interval = (ngtcp2_duration)((double)conn->tx.pacing.pktlen / pacing_rate);
+
+ conn->tx.pacing.next_ts = ts + interval;
+ conn->tx.pacing.pktlen = 0;
+}
+
+size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) {
+ return conn->cstat.send_quantum;
+}
+
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+ size_t i;
+
+ if (conn->dcid.retire_unacked.len >=
+ ngtcp2_arraylen(conn->dcid.retire_unacked.seqs)) {
+ return NGTCP2_ERR_CONNECTION_ID_LIMIT;
+ }
+
+ /* Make sure that we do not have a duplicate */
+ for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+ if (conn->dcid.retire_unacked.seqs[i] == seq) {
+ ngtcp2_unreachable();
+ }
+ }
+
+ conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq;
+
+ return 0;
+}
+
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+ size_t i;
+
+ for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+ if (conn->dcid.retire_unacked.seqs[i] != seq) {
+ continue;
+ }
+
+ if (i != conn->dcid.retire_unacked.len - 1) {
+ conn->dcid.retire_unacked.seqs[i] =
+ conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1];
+ }
+
+ --conn->dcid.retire_unacked.len;
+
+ return;
+ }
+}
+
+size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) {
+ ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+ if (strm == NULL) {
+ return 0;
+ }
+
+ return strm->tx.loss_count;
+}
+
+void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
+ const ngtcp2_path *path,
+ const uint8_t *data) {
+ ngtcp2_path_storage_init2(&pcent->ps, path);
+ memcpy(pcent->data, data, sizeof(pcent->data));
+}
+
+void ngtcp2_settings_default_versioned(int settings_version,
+ ngtcp2_settings *settings) {
+ (void)settings_version;
+
+ memset(settings, 0, sizeof(*settings));
+ settings->cc_algo = NGTCP2_CC_ALGO_CUBIC;
+ settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
+ settings->ack_thresh = 2;
+ settings->max_tx_udp_payload_size = 1500 - 48;
+ settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT;
+}
+
+void ngtcp2_transport_params_default_versioned(
+ int transport_params_version, ngtcp2_transport_params *params) {
+ (void)transport_params_version;
+
+ memset(params, 0, sizeof(*params));
+ params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params->active_connection_id_limit =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+}
+
+/* The functions prefixed with ngtcp2_pkt_ are usually put inside
+ ngtcp2_pkt.c. This function uses encryption construct and uses
+ test data defined only in ngtcp2_conn_test.c, so it is written
+ here. */
+ngtcp2_ssize ngtcp2_pkt_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv,
+ ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx) {
+ ngtcp2_pkt_hd hd;
+ ngtcp2_crypto_km ckm;
+ ngtcp2_crypto_cc cc;
+ ngtcp2_ppe ppe;
+ ngtcp2_frame fr = {0};
+ int rv;
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid,
+ scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version,
+ /* len = */ 0);
+
+ ngtcp2_vec_init(&ckm.secret, NULL, 0);
+ ngtcp2_vec_init(&ckm.iv, iv, 12);
+ ckm.aead_ctx = *aead_ctx;
+ ckm.pkt_num = 0;
+ ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
+
+ cc.aead = *aead;
+ cc.hp = *hp;
+ cc.ckm = &ckm;
+ cc.hp_ctx = *hp_ctx;
+ cc.encrypt = encrypt;
+ cc.hp_mask = hp_mask;
+
+ ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
+
+ rv = ngtcp2_ppe_encode_hd(&ppe, &hd);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
+ fr.connection_close.error_code = error_code;
+ fr.connection_close.reasonlen = reasonlen;
+ fr.connection_close.reason = (uint8_t *)reason;
+
+ rv = ngtcp2_ppe_encode_frame(&ppe, &fr);
+ if (rv != 0) {
+ assert(NGTCP2_ERR_NOBUF == rv);
+ return rv;
+ }
+
+ return ngtcp2_ppe_final(&ppe, NULL);
+}
+
+int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); }
+
+uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ const uint32_t *offered_versions,
+ size_t offered_versionslen) {
+ size_t i, j;
+
+ if (!preferred_versionslen || !offered_versionslen) {
+ return 0;
+ }
+
+ for (i = 0; i < preferred_versionslen; ++i) {
+ assert(ngtcp2_is_supported_version(preferred_versions[i]));
+
+ for (j = 0; j < offered_versionslen; ++j) {
+ if (preferred_versions[i] == offered_versions[j]) {
+ return preferred_versions[i];
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
new file mode 100644
index 0000000..aace7d9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
@@ -0,0 +1,1115 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONN_H
+#define NGTCP2_CONN_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_crypto.h"
+#include "ngtcp2_acktr.h"
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_strm.h"
+#include "ngtcp2_idtr.h"
+#include "ngtcp2_str.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_pq.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_bbr.h"
+#include "ngtcp2_bbr2.h"
+#include "ngtcp2_pv.h"
+#include "ngtcp2_pmtud.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_ppe.h"
+#include "ngtcp2_qlog.h"
+#include "ngtcp2_rst.h"
+
+typedef enum {
+ /* Client specific handshake states */
+ NGTCP2_CS_CLIENT_INITIAL,
+ NGTCP2_CS_CLIENT_WAIT_HANDSHAKE,
+ NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED,
+ /* Server specific handshake states */
+ NGTCP2_CS_SERVER_INITIAL,
+ NGTCP2_CS_SERVER_WAIT_HANDSHAKE,
+ NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED,
+ /* Shared by both client and server */
+ NGTCP2_CS_POST_HANDSHAKE,
+ NGTCP2_CS_CLOSING,
+ NGTCP2_CS_DRAINING,
+} ngtcp2_conn_state;
+
+/* NGTCP2_MAX_STREAMS is the maximum number of streams. */
+#define NGTCP2_MAX_STREAMS (1LL << 60)
+
+/* NGTCP2_MAX_NUM_BUFFED_RX_PKTS is the maximum number of buffered
+ reordered packets. */
+#define NGTCP2_MAX_NUM_BUFFED_RX_PKTS 4
+
+/* NGTCP2_MAX_REORDERED_CRYPTO_DATA is the maximum offset of crypto
+ data which is not continuous. In other words, there is a gap of
+ unreceived data. */
+#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536
+
+/* NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA is the maximum offset of received
+ crypto stream in Initial packet. We set this hard limit here
+ because crypto stream is unbounded. */
+#define NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA 65536
+/* NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA is the maximum offset of
+ received crypto stream in Handshake packet. We set this hard limit
+ here because crypto stream is unbounded. */
+#define NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA 65536
+
+/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can
+ accept. */
+#define NGTCP2_MAX_RETRIES 3
+
+/* NGTCP2_MAX_BOUND_DCID_POOL_SIZE is the maximum number of
+ destination connection ID which have been bound to a particular
+ path, but not yet used as primary path and path validation is not
+ performed from the local endpoint. */
+#define NGTCP2_MAX_BOUND_DCID_POOL_SIZE 4
+/* NGTCP2_MAX_DCID_POOL_SIZE is the maximum number of destination
+ connection ID the remote endpoint provides to store. It must be
+ the power of 2. */
+#define NGTCP2_MAX_DCID_POOL_SIZE 8
+/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID
+ kept to catch in-flight packet on retired path. */
+#define NGTCP2_MAX_DCID_RETIRED_SIZE 2
+/* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source
+ connection ID the local endpoint provides to the remote endpoint.
+ The chosen value was described in old draft. Now a remote endpoint
+ tells the maximum value. The value can be quite large, and we have
+ to put the sane limit.*/
+#define NGTCP2_MAX_SCID_POOL_SIZE 8
+
+/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non
+ ACK-eliciting packets. */
+#define NGTCP2_MAX_NON_ACK_TX_PKT 3
+
+/* NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS is the maximum number of ECN marked
+ packets sent in NGTCP2_ECN_STATE_TESTING period. */
+#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10
+
+/* NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN is the maximum length
+ of reason phrase to remember. If the received reason phrase is
+ longer than this value, it is truncated. */
+#define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024
+
+/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00u
+/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
+ than Initial packet should be padded. Initial packet might be
+ padded based on QUIC requirement regardless of this flag. */
+#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01u
+/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
+ and it should be encoded into the current packet. */
+#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02u
+
+/*
+ * ngtcp2_max_frame is defined so that it covers the largest ACK
+ * frame.
+ */
+typedef union ngtcp2_max_frame {
+ ngtcp2_frame fr;
+ struct {
+ ngtcp2_ack ack;
+ /* ack includes 1 ngtcp2_ack_range. */
+ ngtcp2_ack_range ranges[NGTCP2_MAX_ACK_RANGES - 1];
+ } ackfr;
+} ngtcp2_max_frame;
+
+typedef struct ngtcp2_path_challenge_entry {
+ ngtcp2_path_storage ps;
+ uint8_t data[8];
+} ngtcp2_path_challenge_entry;
+
+void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
+ const ngtcp2_path *path,
+ const uint8_t *data);
+
+/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_CONN_FLAG_NONE 0x00u
+/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares
+ that TLS handshake has completed. The condition of this
+ declaration varies between TLS implementations and this flag does
+ not indicate the completion of QUIC handshake. Some
+ implementations declare TLS handshake completion as server when
+ they write off Server Finished and before deriving application rx
+ secret. */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01u
+/* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is
+ negotiated. This is only used for client. */
+#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02u
+/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport
+ parameters are received. */
+#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04u
+/* NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED is set when a
+ local transport parameters are applied. */
+#define NGTCP2_CONN_FLAG_LOCAL_TRANSPORT_PARAMS_COMMITTED 0x08u
+/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry
+ packet. */
+#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10u
+/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is
+ rejected by a peer. */
+#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20u
+/* NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED is set when the expired
+ keep-alive timer has been cancelled. */
+#define NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED 0x40u
+/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint
+ confirmed completion of handshake. */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80u
+/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the
+ library transitions its state to "post handshake". */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100u
+/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early
+ handshake retransmission has done when server receives overlapping
+ Initial crypto data. */
+#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200u
+/* NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT indicates that the local endpoint
+ sends a QUIC packet without Fixed Bit set if a remote endpoint
+ supports Greasing QUIC Bit extension. */
+#define NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT 0x0400u
+/* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is
+ not confirmed by the local endpoint. That is, it has not received
+ ACK frame which acknowledges packet which is encrypted with new
+ key. */
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800u
+/* NGTCP2_CONN_FLAG_PPE_PENDING is set when
+ NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of
+ ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */
+#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000u
+/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer
+ should be restarted on next write. */
+#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000u
+/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer
+ verified client address. This flag is only used by client. */
+#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000u
+/* NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED indicates that an early key is
+ installed. conn->early.ckm cannot be used for this purpose because
+ it might be discarded when a certain condition is met. */
+#define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000u
+/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local
+ endpoint has initiated key update. */
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u
+
+typedef struct ngtcp2_crypto_data {
+ ngtcp2_buf buf;
+ /* pkt_type is the type of packet to send data in buf. If it is 0,
+ it must be sent in Short packet. Otherwise, it is sent the long
+ packet type denoted by pkt_type. */
+ uint8_t pkt_type;
+} ngtcp2_crypto_data;
+
+typedef struct ngtcp2_pktns {
+ struct {
+ /* last_pkt_num is the packet number which the local endpoint sent
+ last time.*/
+ int64_t last_pkt_num;
+ ngtcp2_frame_chain *frq;
+ /* num_non_ack_pkt is the number of continuous non ACK-eliciting
+ packets. */
+ size_t num_non_ack_pkt;
+
+ struct {
+ /* ect0 is the number of QUIC packets, not UDP datagram, which
+ are sent in UDP datagram with ECT0 marking. */
+ size_t ect0;
+ /* start_pkt_num is the lowest packet number that are sent
+ during ECN validation period. */
+ int64_t start_pkt_num;
+ /* validation_pkt_sent is the number of QUIC packets sent during
+ validation period. */
+ size_t validation_pkt_sent;
+ /* validation_pkt_lost is the number of QUIC packets lost during
+ validation period. */
+ size_t validation_pkt_lost;
+ } ecn;
+ } tx;
+
+ struct {
+ /* pngap tracks received packet number in order to suppress
+ duplicated packet number. */
+ ngtcp2_gaptr pngap;
+ /* max_pkt_num is the largest packet number received so far. */
+ int64_t max_pkt_num;
+ /* max_pkt_ts is the timestamp when max_pkt_num packet is
+ received. */
+ ngtcp2_tstamp max_pkt_ts;
+ /* max_ack_eliciting_pkt_num is the largest ack-eliciting packet
+ number received so far. */
+ int64_t max_ack_eliciting_pkt_num;
+ /*
+ * buffed_pkts is buffered packets which cannot be decrypted with
+ * the current encryption level.
+ *
+ * In server Initial encryption level, 0-RTT packet may be buffered.
+ * In server Handshake encryption level, Short packet may be buffered.
+ *
+ * In client Initial encryption level, Handshake or Short packet may
+ * be buffered. In client Handshake encryption level, Short packet
+ * may be buffered.
+ *
+ * - 0-RTT packet is only buffered in server Initial encryption
+ * level ngtcp2_pktns.
+ *
+ * - Handshake packet is only buffered in client Handshake
+ * encryption level ngtcp2_pktns.
+ *
+ * - Short packet is only buffered in Short encryption level
+ * ngtcp2_pktns.
+ */
+ ngtcp2_pkt_chain *buffed_pkts;
+
+ struct {
+ /* ect0, ect1, and ce are the number of QUIC packets received
+ with those markings. */
+ size_t ect0;
+ size_t ect1;
+ size_t ce;
+ struct {
+ /* ect0, ect1, ce are the ECN counts received in the latest
+ ACK frame. */
+ uint64_t ect0;
+ uint64_t ect1;
+ uint64_t ce;
+ } ack;
+ } ecn;
+ } rx;
+
+ struct {
+ struct {
+ /* frq contains crypto data sorted by their offset. */
+ ngtcp2_ksl frq;
+ /* offset is the offset of crypto stream in this packet number
+ space. */
+ uint64_t offset;
+ /* ckm is a cryptographic key, and iv to encrypt outgoing
+ packets. */
+ ngtcp2_crypto_km *ckm;
+ /* hp_ctx is cipher context for packet header protection. */
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ /* data is the submitted crypto data. */
+ ngtcp2_buf_chain *data;
+ } tx;
+
+ struct {
+ /* ckm is a cryptographic key, and iv to decrypt incoming
+ packets. */
+ ngtcp2_crypto_km *ckm;
+ /* hp_ctx is cipher context for packet header protection. */
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } rx;
+
+ ngtcp2_strm strm;
+ ngtcp2_crypto_ctx ctx;
+ } crypto;
+
+ ngtcp2_acktr acktr;
+ ngtcp2_rtb rtb;
+} ngtcp2_pktns;
+
+typedef enum ngtcp2_ecn_state {
+ NGTCP2_ECN_STATE_TESTING,
+ NGTCP2_ECN_STATE_UNKNOWN,
+ NGTCP2_ECN_STATE_FAILED,
+ NGTCP2_ECN_STATE_CAPABLE,
+} ngtcp2_ecn_state;
+
+ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
+ sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(path_challenge, 4,
+ sizeof(ngtcp2_path_challenge_entry));
+
+ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent);
+
+struct ngtcp2_conn {
+ ngtcp2_objalloc frc_objalloc;
+ ngtcp2_objalloc rtb_entry_objalloc;
+ ngtcp2_objalloc strm_objalloc;
+ ngtcp2_conn_state state;
+ ngtcp2_callbacks callbacks;
+ /* rcid is a connection ID present in Initial or 0-RTT packet from
+ client as destination connection ID. Server uses this field to
+ check that duplicated Initial or 0-RTT packet are indeed sent to
+ this connection. Client uses this field to validate
+ original_destination_connection_id transport parameter. */
+ ngtcp2_cid rcid;
+ /* oscid is the source connection ID initially used by the local
+ endpoint. */
+ ngtcp2_cid oscid;
+ /* retry_scid is the source connection ID from Retry packet. Client
+ records it in order to verify retry_source_connection_id
+ transport parameter. Server does not use this field. */
+ ngtcp2_cid retry_scid;
+ ngtcp2_pktns *in_pktns;
+ ngtcp2_pktns *hs_pktns;
+ ngtcp2_pktns pktns;
+
+ struct {
+ /* current is the current destination connection ID. */
+ ngtcp2_dcid current;
+ /* bound is a set of destination connection IDs which are bound to
+ particular paths. These paths are not validated yet. */
+ ngtcp2_static_ringbuf_dcid_bound bound;
+ /* unused is a set of unused CID received from peer. */
+ ngtcp2_static_ringbuf_dcid_unused unused;
+ /* retired is a set of CID retired by local endpoint. Keep them
+ in 3*PTO to catch packets in flight along the old path. */
+ ngtcp2_static_ringbuf_dcid_retired retired;
+ /* seqgap tracks received sequence numbers in order to ignore
+ retransmitted duplicated NEW_CONNECTION_ID frame. */
+ ngtcp2_gaptr seqgap;
+ /* retire_prior_to is the largest retire_prior_to received so
+ far. */
+ uint64_t retire_prior_to;
+ struct {
+ /* seqs contains sequence number of Connection ID whose
+ retirement is not acknowledged by the remote endpoint yet. */
+ uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2];
+ /* len is the number of sequence numbers that seq contains. */
+ size_t len;
+ } retire_unacked;
+ /* zerolen_seq is a pseudo sequence number of zero-length
+ Destination Connection ID in order to distinguish between
+ them. */
+ uint64_t zerolen_seq;
+ } dcid;
+
+ struct {
+ /* set is a set of CID sent to peer. The peer can use any CIDs in
+ this set. This includes used CID as well as unused ones. */
+ ngtcp2_ksl set;
+ /* used is a set of CID used by peer. The sort function of this
+ priority queue takes timestamp when CID is retired and sorts
+ them in ascending order. */
+ ngtcp2_pq used;
+ /* last_seq is the last sequence number of connection ID. */
+ uint64_t last_seq;
+ /* num_retired is the number of retired Connection ID still
+ included in set. */
+ size_t num_retired;
+ } scid;
+
+ struct {
+ /* strmq contains ngtcp2_strm which has frames to send. */
+ ngtcp2_pq strmq;
+ /* strmq_nretrans is the number of entries in strmq which has
+ stream data to resent. */
+ size_t strmq_nretrans;
+ /* ack is ACK frame. The underlying buffer is reused. */
+ ngtcp2_frame *ack;
+ /* max_ack_ranges is the number of additional ngtcp2_ack_range
+ which ack can contain. */
+ size_t max_ack_ranges;
+ /* offset is the offset the local endpoint has sent to the remote
+ endpoint. */
+ uint64_t offset;
+ /* max_offset is the maximum offset that local endpoint can
+ send. */
+ uint64_t max_offset;
+ /* last_max_data_ts is the timestamp when last MAX_DATA frame is
+ sent. */
+ ngtcp2_tstamp last_max_data_ts;
+
+ struct {
+ /* state is the state of ECN validation */
+ ngtcp2_ecn_state state;
+ /* validation_start_ts is the timestamp when ECN validation is
+ started. It is UINT64_MAX if it has not started yet. */
+ ngtcp2_tstamp validation_start_ts;
+ /* dgram_sent is the number of UDP datagram sent during ECN
+ validation period. */
+ size_t dgram_sent;
+ } ecn;
+
+ struct {
+ /* pktlen is the number of bytes written before calling
+ ngtcp2_conn_update_pkt_tx_time which resets this field to
+ 0. */
+ size_t pktlen;
+ /* next_ts is the time to send next packet. It is UINT64_MAX if
+ packet pacing is disabled or expired.*/
+ ngtcp2_tstamp next_ts;
+ } pacing;
+ } tx;
+
+ struct {
+ /* unsent_max_offset is the maximum offset that remote endpoint
+ can send without extending MAX_DATA. This limit is not yet
+ notified to the remote endpoint. */
+ uint64_t unsent_max_offset;
+ /* offset is the cumulative sum of stream data received for this
+ connection. */
+ uint64_t offset;
+ /* max_offset is the maximum offset that remote endpoint can
+ send. */
+ uint64_t max_offset;
+ /* window is the connection-level flow control window size. */
+ uint64_t window;
+ /* path_challenge stores received PATH_CHALLENGE data. */
+ ngtcp2_static_ringbuf_path_challenge path_challenge;
+ /* ccerr is the received connection close error. */
+ ngtcp2_connection_close_error ccerr;
+ } rx;
+
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ ngtcp2_crypto_ctx ctx;
+ /* discard_started_ts is the timestamp when the timer to discard
+ early key has started. Used by server only. */
+ ngtcp2_tstamp discard_started_ts;
+ /* transport_params is the values remembered by client from the
+ previous session. These are set by
+ ngtcp2_conn_set_early_remote_transport_params(). Server does
+ not use this field. Server must not set values for these
+ parameters that are smaller than the remembered values. */
+ struct {
+ uint64_t initial_max_streams_bidi;
+ uint64_t initial_max_streams_uni;
+ uint64_t initial_max_stream_data_bidi_local;
+ uint64_t initial_max_stream_data_bidi_remote;
+ uint64_t initial_max_stream_data_uni;
+ uint64_t initial_max_data;
+ uint64_t active_connection_id_limit;
+ uint64_t max_datagram_frame_size;
+ } transport_params;
+ } early;
+
+ struct {
+ ngtcp2_settings settings;
+ /* transport_params is the local transport parameters. It is used
+ for Short packet only. */
+ ngtcp2_transport_params transport_params;
+ struct {
+ /* max_streams is the maximum number of bidirectional streams which
+ the local endpoint can open. */
+ uint64_t max_streams;
+ /* next_stream_id is the bidirectional stream ID which the local
+ endpoint opens next. */
+ int64_t next_stream_id;
+ } bidi;
+
+ struct {
+ /* max_streams is the maximum number of unidirectional streams
+ which the local endpoint can open. */
+ uint64_t max_streams;
+ /* next_stream_id is the unidirectional stream ID which the
+ local endpoint opens next. */
+ int64_t next_stream_id;
+ } uni;
+ } local;
+
+ struct {
+ /* transport_params is the received transport parameters during
+ handshake. It is used for Short packet only. */
+ ngtcp2_transport_params *transport_params;
+ /* pending_transport_params is received transport parameters
+ during handshake. It is copied to transport_params when 1RTT
+ key is available. */
+ ngtcp2_transport_params *pending_transport_params;
+ struct {
+ ngtcp2_idtr idtr;
+ /* unsent_max_streams is the maximum number of streams of peer
+ initiated bidirectional stream which the local endpoint can
+ accept. This limit is not yet notified to the remote
+ endpoint. */
+ uint64_t unsent_max_streams;
+ /* max_streams is the maximum number of streams of peer
+ initiated bidirectional stream which the local endpoint can
+ accept. */
+ uint64_t max_streams;
+ } bidi;
+
+ struct {
+ ngtcp2_idtr idtr;
+ /* unsent_max_streams is the maximum number of streams of peer
+ initiated unidirectional stream which the local endpoint can
+ accept. This limit is not yet notified to the remote
+ endpoint. */
+ uint64_t unsent_max_streams;
+ /* max_streams is the maximum number of streams of peer
+ initiated unidirectional stream which the local endpoint can
+ accept. */
+ uint64_t max_streams;
+ } uni;
+ } remote;
+
+ struct {
+ struct {
+ /* new_tx_ckm is a new sender 1RTT key which has not been
+ used. */
+ ngtcp2_crypto_km *new_tx_ckm;
+ /* new_rx_ckm is a new receiver 1RTT key which has not
+ successfully decrypted incoming packet yet. */
+ ngtcp2_crypto_km *new_rx_ckm;
+ /* old_rx_ckm is an old receiver 1RTT key. */
+ ngtcp2_crypto_km *old_rx_ckm;
+ /* confirmed_ts is the time instant when the key update is
+ confirmed by the local endpoint last time. UINT64_MAX means
+ undefined value. */
+ ngtcp2_tstamp confirmed_ts;
+ } key_update;
+
+ /* tls_native_handle is a native handle to TLS session object. */
+ void *tls_native_handle;
+ /* decrypt_hp_buf is a buffer which is used to write unprotected
+ packet header. */
+ ngtcp2_vec decrypt_hp_buf;
+ /* decrypt_buf is a buffer which is used to write decrypted data. */
+ ngtcp2_vec decrypt_buf;
+ /* retry_aead is AEAD to verify Retry packet integrity. It is
+ used by client only. */
+ ngtcp2_crypto_aead retry_aead;
+ /* retry_aead_ctx is AEAD cipher context to verify Retry packet
+ integrity. It is used by client only. */
+ ngtcp2_crypto_aead_ctx retry_aead_ctx;
+ /* tls_error is TLS related error. */
+ int tls_error;
+ /* tls_alert is TLS alert generated by the local endpoint. */
+ uint8_t tls_alert;
+ /* decryption_failure_count is the number of received packets that
+ fail authentication. */
+ uint64_t decryption_failure_count;
+ } crypto;
+
+ /* pkt contains the packet intermediate construction data to support
+ NGTCP2_WRITE_STREAM_FLAG_MORE */
+ struct {
+ ngtcp2_crypto_cc cc;
+ ngtcp2_pkt_hd hd;
+ ngtcp2_ppe ppe;
+ ngtcp2_frame_chain **pfrc;
+ int pkt_empty;
+ int hd_logged;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_RTB_ENTRY_FLAG_*. */
+ uint16_t rtb_entry_flags;
+ ngtcp2_ssize hs_spktlen;
+ int require_padding;
+ } pkt;
+
+ struct {
+ /* last_ts is a timestamp when a last packet is sent or received
+ on a current path. */
+ ngtcp2_tstamp last_ts;
+ /* timeout is keep-alive timeout. When it expires, a packet
+ should be sent to a current path to keep connection alive. It
+ might be used to keep NAT binding intact. If 0 is set,
+ keep-alive timer is disabled. */
+ ngtcp2_duration timeout;
+ } keep_alive;
+
+ struct {
+ /* Initial keys for negotiated version. If original version ==
+ negotiated version, these fields are not used. */
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } rx;
+ struct {
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ } tx;
+ /* version is QUIC version that the above Initial keys are created
+ for. */
+ uint32_t version;
+ /* preferred_versions is the array of versions that are preferred
+ by the local endpoint. Server negotiates one of those versions
+ in this array if a client initially selects a less preferred
+ version. Client uses this field and original_version field to
+ prevent version downgrade attack if it reacted upon Version
+ Negotiation packet. */
+ uint32_t *preferred_versions;
+ /* preferred_versionslen is the number of versions stored in the
+ array pointed by preferred_versions. This field is only used
+ by server. */
+ size_t preferred_versionslen;
+ /* available_versions is the versions that the local endpoint
+ sends in version_information transport parameter. This is the
+ wire image of available_versions field of version_information
+ transport parameter. */
+ uint8_t *available_versions;
+ /* available_versionslen is the length of data pointed by
+ available_versions field. */
+ size_t available_versionslen;
+ } vneg;
+
+ ngtcp2_map strms;
+ ngtcp2_conn_stat cstat;
+ ngtcp2_pv *pv;
+ ngtcp2_pmtud *pmtud;
+ ngtcp2_log log;
+ ngtcp2_qlog qlog;
+ ngtcp2_rst rst;
+ ngtcp2_cc_algo cc_algo;
+ ngtcp2_cc cc;
+ const ngtcp2_mem *mem;
+ /* idle_ts is the time instant when idle timer started. */
+ ngtcp2_tstamp idle_ts;
+ void *user_data;
+ uint32_t client_chosen_version;
+ uint32_t negotiated_version;
+ /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
+ uint32_t flags;
+ int server;
+};
+
+typedef enum ngtcp2_vmsg_type {
+ NGTCP2_VMSG_TYPE_STREAM,
+ NGTCP2_VMSG_TYPE_DATAGRAM,
+} ngtcp2_vmsg_type;
+
+typedef struct ngtcp2_vmsg_stream {
+ /* strm is a stream that data is sent to. */
+ ngtcp2_strm *strm;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_STREAM_FLAG_*. */
+ uint32_t flags;
+ /* data is the pointer to ngtcp2_vec array which contains the stream
+ data to send. */
+ const ngtcp2_vec *data;
+ /* datacnt is the number of ngtcp2_vec pointed by data. */
+ size_t datacnt;
+ /* pdatalen is the pointer to the variable which the number of bytes
+ written is assigned to if pdatalen is not NULL. */
+ ngtcp2_ssize *pdatalen;
+} ngtcp2_vmsg_stream;
+
+typedef struct ngtcp2_vmsg_datagram {
+ /* data is the pointer to ngtcp2_vec array which contains the data
+ to send. */
+ const ngtcp2_vec *data;
+ /* datacnt is the number of ngtcp2_vec pointed by data. */
+ size_t datacnt;
+ /* dgram_id is an opaque identifier chosen by an application. */
+ uint64_t dgram_id;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_WRITE_DATAGRAM_FLAG_*. */
+ uint32_t flags;
+ /* paccepted is the pointer to the variable which, if it is not
+ NULL, is assigned nonzero if data is written to a packet. */
+ int *paccepted;
+} ngtcp2_vmsg_datagram;
+
+typedef struct ngtcp2_vmsg {
+ ngtcp2_vmsg_type type;
+ union {
+ ngtcp2_vmsg_stream stream;
+ ngtcp2_vmsg_datagram datagram;
+ };
+} ngtcp2_vmsg;
+
+/*
+ * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its
+ * reception timestamp |ts| in order to send its ACK.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_PROTO
+ * Same packet number has already been added.
+ */
+int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr,
+ int64_t pkt_num, int active_ack, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_find_stream returns a stream whose stream ID is
+ * |stream_id|. If no such stream is found, it returns NULL.
+ */
+ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id);
+
+/*
+ * conn_init_stream initializes |strm|. Its stream ID is |stream_id|.
+ * This function adds |strm| to conn->strms. |strm| must be allocated
+ * by the caller.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-callback function failed.
+ */
+int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
+ int64_t stream_id, void *stream_user_data);
+
+/*
+ * ngtcp2_conn_close_stream closes stream |strm|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Stream is not found.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_conn_close_stream closes stream |strm| if no further
+ * transmission and reception are allowed, and all reordered incoming
+ * data are emitted to the application, and the transmitted data are
+ * acked.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Stream is not found.
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest
+ * RTT which is not adjusted by ack delay. |ack_delay| is unscaled
+ * ack_delay included in ACK frame. |ack_delay| is actually tainted
+ * (sent by peer), so don't assume that |ack_delay| is always smaller
+ * than, or equals to |rtt|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * RTT sample is ignored.
+ */
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+ ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
+
+void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
+int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_detect_lost_pkt detects lost packets.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the
+ * top of queue. tx_strmq must not be empty.
+ */
+ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue.
+ * tx_strmq must not be empty.
+ */
+void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_conn_internal_expiry returns the minimum expiry time among
+ * all timers in |conn|.
+ */
+ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn);
+
+ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
+ int pkt_info_version, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_write_single_frame_pkt writes a packet which contains
+ * |fr| frame only in the buffer pointed by |dest| whose length if
+ * |destlen|. |type| is a long packet type to send. If |type| is 0,
+ * Short packet is used. |dcid| is used as a destination connection
+ * ID. |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*. Only
+ * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized.
+ *
+ * The packet written by this function will not be retransmitted.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
+ ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+ uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_commit_local_transport_params commits the local
+ * transport parameters, which is currently set to
+ * conn->local.settings.transport_params. This function will do some
+ * amends on transport parameters for adjusting default values.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * CID in preferred address equals to the original SCID.
+ */
+int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of
+ * lost packet.
+ */
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_remove_lost_pkt removes the expired lost packet.
+ */
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_conn_resched_frames reschedules frames linked from |*pfrc|
+ * for retransmission.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_frame_chain **pfrc);
+
+uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of
+ * delayed protected ACK. One should call
+ * `ngtcp2_conn_cancel_expired_ack_delay_timer` and
+ * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it
+ * expires. It returns UINT64_MAX if there is no expiry.
+ */
+ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK
+ * delay timer. |ts| is the current time. This function must be
+ * called when `ngtcp2_conn_ack_delay_expiry` <= ts.
+ */
+void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point
+ * of loss detection timer. One should call
+ * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt`
+ * (or `ngtcp2_conn_writev_stream`) when it expires. It returns
+ * UINT64_MAX if loss detection timer is not armed.
+ */
+ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_idle_expiry` returns the time when a connection
+ * should be closed if it continues to be idle. If idle timeout is
+ * disabled, this function returns ``UINT64_MAX``.
+ */
+ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
+
+ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns);
+
+/*
+ * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq|
+ * of unacknowledged retiring Destination Connection ID.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ * The number of unacknowledged retirement exceeds the limit.
+ */
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number
+ * |seq| of unacknowledged retiring Destination Connection ID. It is
+ * fine if such sequence number is not found.
+ */
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_server_negotiate_version negotiates QUIC version. It
+ * is compatible version negotiation. It returns the negotiated QUIC
+ * version. This function must not be called by client.
+ */
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+ const ngtcp2_version_info *version_info);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_application_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * If handshake has not been confirmed yet, CONNECTION_CLOSE (type
+ * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
+ * instead.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
+ ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+ size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_tstamp ts);
+
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn);
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_remote_transport_params` sets transport parameter
+ * |params| from a remote endpoint to |conn|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * Failed to validate a remote transport parameters.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * Version negotiation failure.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+int ngtcp2_conn_set_remote_transport_params(
+ ngtcp2_conn *conn, const ngtcp2_transport_params *params);
+
+#endif /* NGTCP2_CONN_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
new file mode 100644
index 0000000..3367217
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
@@ -0,0 +1,291 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_conv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_net.h"
+#include "ngtcp2_unreachable.h"
+
+const uint8_t *ngtcp2_get_uint64(uint64_t *dest, const uint8_t *p) {
+ uint64_t n;
+ memcpy(&n, p, sizeof(n));
+ *dest = ngtcp2_ntohl64(n);
+ return p + sizeof(n);
+}
+
+const uint8_t *ngtcp2_get_uint48(uint64_t *dest, const uint8_t *p) {
+ uint64_t n = 0;
+ memcpy(((uint8_t *)&n) + 2, p, 6);
+ *dest = ngtcp2_ntohl64(n);
+ return p + 6;
+}
+
+const uint8_t *ngtcp2_get_uint32(uint32_t *dest, const uint8_t *p) {
+ uint32_t n;
+ memcpy(&n, p, sizeof(n));
+ *dest = ngtcp2_ntohl(n);
+ return p + sizeof(n);
+}
+
+const uint8_t *ngtcp2_get_uint24(uint32_t *dest, const uint8_t *p) {
+ uint32_t n = 0;
+ memcpy(((uint8_t *)&n) + 1, p, 3);
+ *dest = ngtcp2_ntohl(n);
+ return p + 3;
+}
+
+const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p) {
+ uint16_t n;
+ memcpy(&n, p, sizeof(n));
+ *dest = ngtcp2_ntohs(n);
+ return p + sizeof(n);
+}
+
+const uint8_t *ngtcp2_get_uint16be(uint16_t *dest, const uint8_t *p) {
+ memcpy(dest, p, sizeof(*dest));
+ return p + sizeof(*dest);
+}
+
+static uint64_t get_uvarint(size_t *plen, const uint8_t *p) {
+ union {
+ uint8_t n8;
+ uint16_t n16;
+ uint32_t n32;
+ uint64_t n64;
+ } n;
+
+ *plen = (size_t)(1u << (*p >> 6));
+
+ switch (*plen) {
+ case 1:
+ return *p;
+ case 2:
+ memcpy(&n, p, 2);
+ n.n8 &= 0x3f;
+ return ngtcp2_ntohs(n.n16);
+ case 4:
+ memcpy(&n, p, 4);
+ n.n8 &= 0x3f;
+ return ngtcp2_ntohl(n.n32);
+ case 8:
+ memcpy(&n, p, 8);
+ n.n8 &= 0x3f;
+ return ngtcp2_ntohl64(n.n64);
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+const uint8_t *ngtcp2_get_uvarint(uint64_t *dest, const uint8_t *p) {
+ size_t len;
+
+ *dest = get_uvarint(&len, p);
+
+ return p + len;
+}
+
+const uint8_t *ngtcp2_get_varint(int64_t *dest, const uint8_t *p) {
+ size_t len;
+
+ *dest = (int64_t)get_uvarint(&len, p);
+
+ return p + len;
+}
+
+int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) {
+ uint32_t l;
+ uint16_t s;
+
+ switch (pkt_numlen) {
+ case 1:
+ return *p;
+ case 2:
+ ngtcp2_get_uint16(&s, p);
+ return (int64_t)s;
+ case 3:
+ ngtcp2_get_uint24(&l, p);
+ return (int64_t)l;
+ case 4:
+ ngtcp2_get_uint32(&l, p);
+ return (int64_t)l;
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) {
+ n = ngtcp2_htonl64(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n) {
+ n = ngtcp2_htonl64(n);
+ return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 2, 6);
+}
+
+uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n) {
+ n = ngtcp2_htonl(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) {
+ n = ngtcp2_htonl(n);
+ return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3);
+}
+
+uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) {
+ n = ngtcp2_htons(n);
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uint16(uint8_t *p, uint16_t n) {
+ return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n));
+}
+
+uint8_t *ngtcp2_put_uvarint(uint8_t *p, uint64_t n) {
+ uint8_t *rv;
+ if (n < 64) {
+ *p++ = (uint8_t)n;
+ return p;
+ }
+ if (n < 16384) {
+ rv = ngtcp2_put_uint16be(p, (uint16_t)n);
+ *p |= 0x40;
+ return rv;
+ }
+ if (n < 1073741824) {
+ rv = ngtcp2_put_uint32be(p, (uint32_t)n);
+ *p |= 0x80;
+ return rv;
+ }
+ assert(n < 4611686018427387904ULL);
+ rv = ngtcp2_put_uint64be(p, n);
+ *p |= 0xc0;
+ return rv;
+}
+
+uint8_t *ngtcp2_put_uvarint30(uint8_t *p, uint32_t n) {
+ uint8_t *rv;
+
+ assert(n < 1073741824);
+
+ rv = ngtcp2_put_uint32be(p, n);
+ *p |= 0x80;
+
+ return rv;
+}
+
+uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) {
+ switch (len) {
+ case 1:
+ *p++ = (uint8_t)pkt_num;
+ return p;
+ case 2:
+ ngtcp2_put_uint16be(p, (uint16_t)pkt_num);
+ return p + 2;
+ case 3:
+ ngtcp2_put_uint24be(p, (uint32_t)pkt_num);
+ return p + 3;
+ case 4:
+ ngtcp2_put_uint32be(p, (uint32_t)pkt_num);
+ return p + 4;
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+size_t ngtcp2_get_uvarintlen(const uint8_t *p) {
+ return (size_t)(1u << (*p >> 6));
+}
+
+size_t ngtcp2_put_uvarintlen(uint64_t n) {
+ if (n < 64) {
+ return 1;
+ }
+ if (n < 16384) {
+ return 2;
+ }
+ if (n < 1073741824) {
+ return 4;
+ }
+ assert(n < 4611686018427387904ULL);
+ return 8;
+}
+
+int64_t ngtcp2_nth_server_bidi_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_SERVER_STREAM_ID_BIDI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x01);
+}
+
+int64_t ngtcp2_nth_client_bidi_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_CLIENT_STREAM_ID_BIDI;
+ }
+
+ return (int64_t)((n - 1) << 2);
+}
+
+int64_t ngtcp2_nth_server_uni_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_SERVER_STREAM_ID_UNI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x03);
+}
+
+int64_t ngtcp2_nth_client_uni_id(uint64_t n) {
+ if (n == 0) {
+ return 0;
+ }
+
+ if ((NGTCP2_MAX_VARINT >> 2) < n - 1) {
+ return NGTCP2_MAX_CLIENT_STREAM_ID_UNI;
+ }
+
+ return (int64_t)(((n - 1) << 2) | 0x02);
+}
+
+uint64_t ngtcp2_ord_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2) + 1;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h
new file mode 100644
index 0000000..ef089a9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h
@@ -0,0 +1,208 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CONV_H
+#define NGTCP2_CONV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| in host byte order. It returns |p| + 8.
+ */
+const uint8_t *ngtcp2_get_uint64(uint64_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint48 reads 6 bytes from |p| as 48 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| in host byte order. It returns |p| + 6.
+ */
+const uint8_t *ngtcp2_get_uint48(uint64_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint32 reads 4 bytes from |p| as 32 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| in host byte order. It returns |p| + 4.
+ */
+const uint8_t *ngtcp2_get_uint32(uint32_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint24 reads 3 bytes from |p| as 24 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| in host byte order. It returns |p| + 3.
+ */
+const uint8_t *ngtcp2_get_uint24(uint32_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| in host byte order. It returns |p| + 2.
+ */
+const uint8_t *ngtcp2_get_uint16(uint16_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uint16be reads 2 bytes from |p| as 16 bits unsigned
+ * integer encoded as network byte order, and stores it in the buffer
+ * pointed by |dest| as is. It returns |p| + 2.
+ */
+const uint8_t *ngtcp2_get_uint16be(uint16_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_uvarint reads variable-length unsigned integer from |p|,
+ * and stores it in the buffer pointed by |dest| in host byte order.
+ * It returns |p| plus the number of bytes read from |p|.
+ */
+const uint8_t *ngtcp2_get_uvarint(uint64_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_varint reads variable-length unsigned integer from |p|,
+ * and casts it to the signed integer, and stores it in the buffer
+ * pointed by |dest| in host byte order. No information should be
+ * lost in this cast, because the variable-length integer is 62
+ * bits. It returns |p| plus the number of bytes read from |p|.
+ */
+const uint8_t *ngtcp2_get_varint(int64_t *dest, const uint8_t *p);
+
+/*
+ * ngtcp2_get_pkt_num reads encoded packet number from |p|. The
+ * packet number is encoed in |pkt_numlen| bytes.
+ */
+int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen);
+
+/*
+ * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n);
+
+/*
+ * ngtcp2_put_uint48be writes |n| in host byte order in |p| in network
+ * byte order. It writes only least significant 48 bits. It returns
+ * the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n);
+
+/*
+ * ngtcp2_put_uint32be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n);
+
+/*
+ * ngtcp2_put_uint24be writes |n| in host byte order in |p| in network
+ * byte order. It writes only least significant 24 bits. It returns
+ * the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n);
+
+/*
+ * ngtcp2_put_uint16be writes |n| in host byte order in |p| in network
+ * byte order. It returns the one beyond of the last written
+ * position.
+ */
+uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n);
+
+/*
+ * ngtcp2_put_uint16 writes |n| as is in |p|. It returns the one
+ * beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uint16(uint8_t *p, uint16_t n);
+
+/*
+ * ngtcp2_put_uvarint writes |n| in |p| using variable-length integer
+ * encoding. It returns the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_uvarint(uint8_t *p, uint64_t n);
+
+/*
+ * ngtcp2_put_uvarint30 writes |n| in |p| using variable-length
+ * integer encoding. |n| must be strictly less than 1073741824. The
+ * function always encodes |n| in 4 bytes. It returns the one beyond
+ * of the last written position.
+ */
+uint8_t *ngtcp2_put_uvarint30(uint8_t *p, uint32_t n);
+
+/*
+ * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It
+ * returns the one beyond of the last written position.
+ */
+uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len);
+
+/*
+ * ngtcp2_get_uvarintlen returns the required number of bytes to read
+ * variable-length integer starting at |p|.
+ */
+size_t ngtcp2_get_uvarintlen(const uint8_t *p);
+
+/*
+ * ngtcp2_put_uvarintlen returns the required number of bytes to
+ * encode |n|.
+ */
+size_t ngtcp2_put_uvarintlen(uint64_t n);
+
+/*
+ * ngtcp2_nth_server_bidi_id returns |n|-th server bidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_SERVER_STREAM_ID_BIDI, this function returns
+ * NGTCP2_MAX_SERVER_STREAM_ID_BIDI.
+ */
+int64_t ngtcp2_nth_server_bidi_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_client_bidi_id returns |n|-th client bidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_CLIENT_STREAM_ID_BIDI, this function returns
+ * NGTCP2_MAX_CLIENT_STREAM_ID_BIDI.
+ */
+int64_t ngtcp2_nth_client_bidi_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_server_uni_id returns |n|-th server unidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_SERVER_STREAM_ID_UNI, this function returns
+ * NGTCP2_MAX_SERVER_STREAM_ID_UNI.
+ */
+int64_t ngtcp2_nth_server_uni_id(uint64_t n);
+
+/*
+ * ngtcp2_nth_client_uni_id returns |n|-th client unidirectional
+ * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is
+ * larger than NGTCP2_MAX_CLIENT_STREAM_ID_UNI, this function returns
+ * NGTCP2_MAX_CLIENT_STREAM_ID_UNI.
+ */
+int64_t ngtcp2_nth_client_uni_id(uint64_t n);
+
+/*
+ * ngtcp2_ord_stream_id returns the ordinal number of |stream_id|.
+ */
+uint64_t ngtcp2_ord_stream_id(int64_t stream_id);
+
+#endif /* NGTCP2_CONV_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
new file mode 100644
index 0000000..24210a2
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
@@ -0,0 +1,895 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_crypto.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_conn.h"
+#include "ngtcp2_net.h"
+
+int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_mem *mem) {
+ int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (secretlen) {
+ memcpy((*pckm)->secret.base, secret, secretlen);
+ }
+ if (aead_ctx) {
+ (*pckm)->aead_ctx = *aead_ctx;
+ }
+ memcpy((*pckm)->iv.base, iv, ivlen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
+ size_t ivlen, const ngtcp2_mem *mem) {
+ size_t len;
+ uint8_t *p;
+
+ len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen;
+
+ *pckm = ngtcp2_mem_malloc(mem, len);
+ if (*pckm == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km);
+ (*pckm)->secret.base = p;
+ (*pckm)->secret.len = secretlen;
+ p += secretlen;
+ (*pckm)->iv.base = p;
+ (*pckm)->iv.len = ivlen;
+ (*pckm)->aead_ctx.native_handle = NULL;
+ (*pckm)->pkt_num = -1;
+ (*pckm)->use_count = 0;
+ (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE;
+
+ return 0;
+}
+
+void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) {
+ if (ckm == NULL) {
+ return;
+ }
+
+ ngtcp2_mem_free(mem, ckm);
+}
+
+void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
+ int64_t pkt_num) {
+ size_t i;
+ uint64_t n;
+
+ assert(ivlen >= 8);
+
+ memcpy(dest, iv, ivlen);
+ n = ngtcp2_htonl64((uint64_t)pkt_num);
+
+ for (i = 0; i < 8; ++i) {
+ dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i];
+ }
+}
+
+/*
+ * varint_paramlen returns the length of a single transport parameter
+ * which has variable integer in its parameter.
+ */
+static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) {
+ size_t valuelen = ngtcp2_put_uvarintlen(param);
+ return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(valuelen) + valuelen;
+}
+
+/*
+ * write_varint_param writes parameter |id| of the given |value| in
+ * varint encoding. It returns p + the number of bytes written.
+ */
+static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id,
+ uint64_t value) {
+ p = ngtcp2_put_uvarint(p, id);
+ p = ngtcp2_put_uvarint(p, ngtcp2_put_uvarintlen(value));
+ return ngtcp2_put_uvarint(p, value);
+}
+
+/*
+ * cid_paramlen returns the length of a single transport parameter
+ * which has |cid| as value.
+ */
+static size_t cid_paramlen(ngtcp2_transport_param_id id,
+ const ngtcp2_cid *cid) {
+ return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(cid->datalen) +
+ cid->datalen;
+}
+
+/*
+ * write_cid_param writes parameter |id| of the given |cid|. It
+ * returns p + the number of bytes written.
+ */
+static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id,
+ const ngtcp2_cid *cid) {
+ assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN);
+ assert(cid->datalen <= NGTCP2_MAX_CIDLEN);
+
+ p = ngtcp2_put_uvarint(p, id);
+ p = ngtcp2_put_uvarint(p, cid->datalen);
+ if (cid->datalen) {
+ p = ngtcp2_cpymem(p, cid->data, cid->datalen);
+ }
+ return p;
+}
+
+static const uint8_t empty_address[16];
+
+ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
+ uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
+ int transport_params_version, const ngtcp2_transport_params *params) {
+ uint8_t *p;
+ size_t len = 0;
+ /* For some reason, gcc 7.3.0 requires this initialization. */
+ size_t preferred_addrlen = 0;
+ size_t version_infolen = 0;
+ const ngtcp2_sockaddr_in *sa_in;
+ const ngtcp2_sockaddr_in6 *sa_in6;
+ (void)transport_params_version;
+
+ switch (exttype) {
+ case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO:
+ break;
+ case NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS:
+ len +=
+ cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID,
+ &params->original_dcid);
+
+ if (params->stateless_reset_token_present) {
+ len +=
+ ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) +
+ ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) +
+ NGTCP2_STATELESS_RESET_TOKENLEN;
+ }
+ if (params->preferred_address_present) {
+ assert(params->preferred_address.cid.datalen >= NGTCP2_MIN_CIDLEN);
+ assert(params->preferred_address.cid.datalen <= NGTCP2_MAX_CIDLEN);
+ preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ +
+ 16 /* ipv6Address */ + 2 /* ipv6Port */
+ + 1 +
+ params->preferred_address.cid.datalen /* CID */ +
+ NGTCP2_STATELESS_RESET_TOKENLEN;
+ len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) +
+ ngtcp2_put_uvarintlen(preferred_addrlen) + preferred_addrlen;
+ }
+ if (params->retry_scid_present) {
+ len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID,
+ &params->retry_scid);
+ }
+ break;
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID,
+ &params->initial_scid);
+
+ if (params->initial_max_stream_data_bidi_local) {
+ len += varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params->initial_max_stream_data_bidi_local);
+ }
+ if (params->initial_max_stream_data_bidi_remote) {
+ len += varint_paramlen(
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params->initial_max_stream_data_bidi_remote);
+ }
+ if (params->initial_max_stream_data_uni) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params->initial_max_stream_data_uni);
+ }
+ if (params->initial_max_data) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params->initial_max_data);
+ }
+ if (params->initial_max_streams_bidi) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params->initial_max_streams_bidi);
+ }
+ if (params->initial_max_streams_uni) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params->initial_max_streams_uni);
+ }
+ if (params->max_udp_payload_size !=
+ NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params->max_udp_payload_size);
+ }
+ if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params->ack_delay_exponent);
+ }
+ if (params->disable_active_migration) {
+ len +=
+ ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) +
+ ngtcp2_put_uvarintlen(0);
+ }
+ if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ }
+ if (params->max_idle_timeout) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ }
+ if (params->active_connection_id_limit &&
+ params->active_connection_id_limit !=
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ }
+ if (params->max_datagram_frame_size) {
+ len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params->max_datagram_frame_size);
+ }
+ if (params->grease_quic_bit) {
+ len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
+ ngtcp2_put_uvarintlen(0);
+ }
+ if (params->version_info_present) {
+ version_infolen =
+ sizeof(uint32_t) + params->version_info.available_versionslen;
+ len += ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION) +
+ ngtcp2_put_uvarintlen(version_infolen) + version_infolen;
+ }
+
+ if (dest == NULL && destlen == 0) {
+ return (ngtcp2_ssize)len;
+ }
+
+ if (destlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = dest;
+
+ if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ p = write_cid_param(
+ p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID,
+ &params->original_dcid);
+
+ if (params->stateless_reset_token_present) {
+ p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN);
+ p = ngtcp2_put_uvarint(p, sizeof(params->stateless_reset_token));
+ p = ngtcp2_cpymem(p, params->stateless_reset_token,
+ sizeof(params->stateless_reset_token));
+ }
+ if (params->preferred_address_present) {
+ p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS);
+ p = ngtcp2_put_uvarint(p, preferred_addrlen);
+
+ if (params->preferred_address.ipv4_present) {
+ sa_in = &params->preferred_address.ipv4;
+ p = ngtcp2_cpymem(p, &sa_in->sin_addr, sizeof(sa_in->sin_addr));
+ p = ngtcp2_put_uint16(p, sa_in->sin_port);
+ } else {
+ p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in->sin_addr));
+ p = ngtcp2_put_uint16(p, 0);
+ }
+
+ if (params->preferred_address.ipv6_present) {
+ sa_in6 = &params->preferred_address.ipv6;
+ p = ngtcp2_cpymem(p, &sa_in6->sin6_addr, sizeof(sa_in6->sin6_addr));
+ p = ngtcp2_put_uint16(p, sa_in6->sin6_port);
+ } else {
+ p = ngtcp2_cpymem(p, empty_address, sizeof(sa_in6->sin6_addr));
+ p = ngtcp2_put_uint16(p, 0);
+ }
+
+ *p++ = (uint8_t)params->preferred_address.cid.datalen;
+ if (params->preferred_address.cid.datalen) {
+ p = ngtcp2_cpymem(p, params->preferred_address.cid.data,
+ params->preferred_address.cid.datalen);
+ }
+ p = ngtcp2_cpymem(
+ p, params->preferred_address.stateless_reset_token,
+ sizeof(params->preferred_address.stateless_reset_token));
+ }
+ if (params->retry_scid_present) {
+ p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID,
+ &params->retry_scid);
+ }
+ }
+
+ p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID,
+ &params->initial_scid);
+
+ if (params->initial_max_stream_data_bidi_local) {
+ p = write_varint_param(
+ p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
+ params->initial_max_stream_data_bidi_local);
+ }
+
+ if (params->initial_max_stream_data_bidi_remote) {
+ p = write_varint_param(
+ p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
+ params->initial_max_stream_data_bidi_remote);
+ }
+
+ if (params->initial_max_stream_data_uni) {
+ p = write_varint_param(p,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI,
+ params->initial_max_stream_data_uni);
+ }
+
+ if (params->initial_max_data) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA,
+ params->initial_max_data);
+ }
+
+ if (params->initial_max_streams_bidi) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI,
+ params->initial_max_streams_bidi);
+ }
+
+ if (params->initial_max_streams_uni) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI,
+ params->initial_max_streams_uni);
+ }
+
+ if (params->max_udp_payload_size !=
+ NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE,
+ params->max_udp_payload_size);
+ }
+
+ if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT,
+ params->ack_delay_exponent);
+ }
+
+ if (params->disable_active_migration) {
+ p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION);
+ p = ngtcp2_put_uvarint(p, 0);
+ }
+
+ if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ }
+
+ if (params->max_idle_timeout) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ }
+
+ if (params->active_connection_id_limit &&
+ params->active_connection_id_limit !=
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT,
+ params->active_connection_id_limit);
+ }
+
+ if (params->max_datagram_frame_size) {
+ p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE,
+ params->max_datagram_frame_size);
+ }
+
+ if (params->grease_quic_bit) {
+ p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT);
+ p = ngtcp2_put_uvarint(p, 0);
+ }
+
+ if (params->version_info_present) {
+ p = ngtcp2_put_uvarint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION);
+ p = ngtcp2_put_uvarint(p, version_infolen);
+ p = ngtcp2_put_uint32be(p, params->version_info.chosen_version);
+ if (params->version_info.available_versionslen) {
+ p = ngtcp2_cpymem(p, params->version_info.available_versions,
+ params->version_info.available_versionslen);
+ }
+ }
+
+ assert((size_t)(p - dest) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+/*
+ * decode_varint decodes a single varint from the buffer pointed by
+ * |*pp| of length |end - *pp|. If it decodes an integer
+ * successfully, it stores the integer in |*pdest|, increment |*pp| by
+ * the number of bytes read from |*pp|, and returns 0. Otherwise it
+ * returns -1.
+ */
+static int decode_varint(uint64_t *pdest, const uint8_t **pp,
+ const uint8_t *end) {
+ const uint8_t *p = *pp;
+ size_t len;
+
+ if (p == end) {
+ return -1;
+ }
+
+ len = ngtcp2_get_uvarintlen(p);
+ if ((uint64_t)(end - p) < len) {
+ return -1;
+ }
+
+ *pp = ngtcp2_get_uvarint(pdest, p);
+
+ return 0;
+}
+
+/*
+ * decode_varint_param decodes length prefixed value from the buffer
+ * pointed by |*pp| of length |end - *pp|. The length and value are
+ * encoded in varint form. If it decodes a value successfully, it
+ * stores the value in |*pdest|, increment |*pp| by the number of
+ * bytes read from |*pp|, and returns 0. Otherwise it returns -1.
+ */
+static int decode_varint_param(uint64_t *pdest, const uint8_t **pp,
+ const uint8_t *end) {
+ const uint8_t *p = *pp;
+ uint64_t valuelen;
+
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return -1;
+ }
+
+ if (p == end) {
+ return -1;
+ }
+
+ if ((uint64_t)(end - p) < valuelen) {
+ return -1;
+ }
+
+ if (ngtcp2_get_uvarintlen(p) != valuelen) {
+ return -1;
+ }
+
+ *pp = ngtcp2_get_uvarint(pdest, p);
+
+ return 0;
+}
+
+/*
+ * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer
+ * pointed by |*pp| of length |end - *pp|. The length is encoded in
+ * varint form. If it decodes a value successfully, it stores the
+ * value in |*pdest|, increment |*pp| by the number of read from
+ * |*pp|, and returns the number of bytes read. Otherwise it returns
+ * the one of the negative error code:
+ *
+ * NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM
+ * Could not decode Connection ID.
+ */
+static int decode_cid_param(ngtcp2_cid *pdest, const uint8_t **pp,
+ const uint8_t *end) {
+ const uint8_t *p = *pp;
+ uint64_t valuelen;
+
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) ||
+ valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ ngtcp2_cid_init(pdest, p, (size_t)valuelen);
+
+ p += valuelen;
+
+ *pp = p;
+
+ return 0;
+}
+
+int ngtcp2_decode_transport_params_versioned(
+ int transport_params_version, ngtcp2_transport_params *params,
+ ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen) {
+ const uint8_t *p, *end, *lend;
+ size_t len;
+ uint64_t param_type;
+ uint64_t valuelen;
+ int rv;
+ int initial_scid_present = 0;
+ int original_dcid_present = 0;
+ ngtcp2_sockaddr_in *sa_in;
+ ngtcp2_sockaddr_in6 *sa_in6;
+ uint32_t version;
+
+ (void)transport_params_version;
+
+ if (datalen == 0) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ /* Set default values */
+ memset(params, 0, sizeof(*params));
+ params->initial_max_streams_bidi = 0;
+ params->initial_max_streams_uni = 0;
+ params->initial_max_stream_data_bidi_local = 0;
+ params->initial_max_stream_data_bidi_remote = 0;
+ params->initial_max_stream_data_uni = 0;
+ params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+ params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
+ params->stateless_reset_token_present = 0;
+ params->preferred_address_present = 0;
+ params->disable_active_migration = 0;
+ params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
+ params->max_idle_timeout = 0;
+ params->active_connection_id_limit =
+ NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ params->retry_scid_present = 0;
+ params->max_datagram_frame_size = 0;
+ memset(&params->retry_scid, 0, sizeof(params->retry_scid));
+ memset(&params->initial_scid, 0, sizeof(params->initial_scid));
+ memset(&params->original_dcid, 0, sizeof(params->original_dcid));
+ params->version_info_present = 0;
+
+ p = data;
+ end = data + datalen;
+
+ for (; (size_t)(end - p) >= 2;) {
+ if (decode_varint(&param_type, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ switch (param_type) {
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL:
+ if (decode_varint_param(&params->initial_max_stream_data_bidi_local, &p,
+ end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE:
+ if (decode_varint_param(&params->initial_max_stream_data_bidi_remote, &p,
+ end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI:
+ if (decode_varint_param(&params->initial_max_stream_data_uni, &p, end) !=
+ 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA:
+ if (decode_varint_param(&params->initial_max_data, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI:
+ if (decode_varint_param(&params->initial_max_streams_bidi, &p, end) !=
+ 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI:
+ if (decode_varint_param(&params->initial_max_streams_uni, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT:
+ if (decode_varint_param(&params->max_idle_timeout, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->max_idle_timeout *= NGTCP2_MILLISECONDS;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE:
+ if (decode_varint_param(&params->max_udp_payload_size, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)valuelen != sizeof(params->stateless_reset_token)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ p = ngtcp2_get_bytes(params->stateless_reset_token, p,
+ sizeof(params->stateless_reset_token));
+ params->stateless_reset_token_present = 1;
+
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT:
+ if (decode_varint_param(&params->ack_delay_exponent, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->ack_delay_exponent > 20) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ +
+ 2 /* ipv6Port */
+ + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN;
+ if (valuelen < len) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ sa_in = &params->preferred_address.ipv4;
+
+ p = ngtcp2_get_bytes(&sa_in->sin_addr, p, sizeof(sa_in->sin_addr));
+ p = ngtcp2_get_uint16be(&sa_in->sin_port, p);
+
+ if (sa_in->sin_port || memcmp(empty_address, &sa_in->sin_addr,
+ sizeof(sa_in->sin_addr)) != 0) {
+ sa_in->sin_family = AF_INET;
+ params->preferred_address.ipv4_present = 1;
+ }
+
+ sa_in6 = &params->preferred_address.ipv6;
+
+ p = ngtcp2_get_bytes(&sa_in6->sin6_addr, p, sizeof(sa_in6->sin6_addr));
+ p = ngtcp2_get_uint16be(&sa_in6->sin6_port, p);
+
+ if (sa_in6->sin6_port || memcmp(empty_address, &sa_in6->sin6_addr,
+ sizeof(sa_in6->sin6_addr)) != 0) {
+ sa_in6->sin6_family = AF_INET6;
+ params->preferred_address.ipv6_present = 1;
+ }
+
+ /* cid */
+ params->preferred_address.cid.datalen = *p++;
+ len += params->preferred_address.cid.datalen;
+ if (valuelen != len ||
+ params->preferred_address.cid.datalen > NGTCP2_MAX_CIDLEN ||
+ params->preferred_address.cid.datalen < NGTCP2_MIN_CIDLEN) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->preferred_address.cid.datalen) {
+ p = ngtcp2_get_bytes(params->preferred_address.cid.data, p,
+ params->preferred_address.cid.datalen);
+ }
+
+ /* stateless reset token */
+ p = ngtcp2_get_bytes(
+ params->preferred_address.stateless_reset_token, p,
+ sizeof(params->preferred_address.stateless_reset_token));
+ params->preferred_address_present = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION:
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (valuelen != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->disable_active_migration = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ rv = decode_cid_param(&params->original_dcid, &p, end);
+ if (rv != 0) {
+ return rv;
+ }
+ original_dcid_present = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID:
+ if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ rv = decode_cid_param(&params->retry_scid, &p, end);
+ if (rv != 0) {
+ return rv;
+ }
+ params->retry_scid_present = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID:
+ rv = decode_cid_param(&params->initial_scid, &p, end);
+ if (rv != 0) {
+ return rv;
+ }
+ initial_scid_present = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY:
+ if (decode_varint_param(&params->max_ack_delay, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (params->max_ack_delay >= 16384) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->max_ack_delay *= NGTCP2_MILLISECONDS;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT:
+ if (decode_varint_param(&params->active_connection_id_limit, &p, end) !=
+ 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE:
+ if (decode_varint_param(&params->max_datagram_frame_size, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ break;
+ case NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT:
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (valuelen != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ params->grease_quic_bit = 1;
+ break;
+ case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION:
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p = ngtcp2_get_uint32(&params->version_info.chosen_version, p);
+ if (params->version_info.chosen_version == 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if (valuelen > sizeof(uint32_t)) {
+ params->version_info.available_versions = (uint8_t *)p;
+ params->version_info.available_versionslen =
+ (size_t)valuelen - sizeof(uint32_t);
+
+ for (lend = p + (valuelen - sizeof(uint32_t)); p != lend;) {
+ p = ngtcp2_get_uint32(&version, p);
+ if (version == 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ }
+ }
+ params->version_info_present = 1;
+ break;
+ default:
+ /* Ignore unknown parameter */
+ if (decode_varint(&valuelen, &p, end) != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ if ((size_t)(end - p) < valuelen) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+ p += valuelen;
+ break;
+ }
+ }
+
+ if (end - p != 0) {
+ return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+ }
+
+ if (!initial_scid_present ||
+ (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS &&
+ !original_dcid_present)) {
+ return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM;
+ }
+
+ return 0;
+}
+
+static int transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem) {
+ size_t len = sizeof(**pdest);
+ ngtcp2_transport_params *dest;
+ uint8_t *p;
+
+ if (src->version_info_present) {
+ len += src->version_info.available_versionslen;
+ }
+
+ dest = ngtcp2_mem_malloc(mem, len);
+ if (dest == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ *dest = *src;
+
+ if (src->version_info_present && src->version_info.available_versionslen) {
+ p = (uint8_t *)dest + sizeof(*dest);
+ memcpy(p, src->version_info.available_versions,
+ src->version_info.available_versionslen);
+ dest->version_info.available_versions = p;
+ }
+
+ *pdest = dest;
+
+ return 0;
+}
+
+int ngtcp2_decode_transport_params_new(ngtcp2_transport_params **pparams,
+ ngtcp2_transport_params_type exttype,
+ const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem) {
+ int rv;
+ ngtcp2_transport_params params;
+
+ rv = ngtcp2_decode_transport_params(&params, exttype, data, datalen);
+ if (rv < 0) {
+ return rv;
+ }
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ return transport_params_copy_new(pparams, &params, mem);
+}
+
+void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem) {
+ if (params == NULL) {
+ return;
+ }
+
+ if (mem == NULL) {
+ mem = ngtcp2_mem_default();
+ }
+
+ ngtcp2_mem_free(mem, params);
+}
+
+int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem) {
+ if (src == NULL) {
+ *pdest = NULL;
+ return 0;
+ }
+
+ return transport_params_copy_new(pdest, src, mem);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h
new file mode 100644
index 0000000..3b91ce9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h
@@ -0,0 +1,148 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_H
+#define NGTCP2_CRYPTO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial
+ packets. Because QUIC uses AEAD_AES_128_GCM, the overhead is 16
+ bytes. */
+#define NGTCP2_INITIAL_AEAD_OVERHEAD 16
+
+/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */
+#define NGTCP2_MAX_AEAD_OVERHEAD 16
+
+/* ngtcp2_transport_param_id is the registry of QUIC transport
+ parameter ID. */
+typedef enum ngtcp2_transport_param_id {
+ NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000,
+ NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001,
+ NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002,
+ NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009,
+ NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a,
+ NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b,
+ NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c,
+ NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d,
+ NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
+ NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
+ NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
+ /* https://datatracker.ietf.org/doc/html/rfc9221 */
+ NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020,
+ NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2,
+ /* https://datatracker.ietf.org/doc/html/draft-ietf-quic-version-negotiation-14
+ */
+ NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION = 0x11,
+} ngtcp2_transport_param_id;
+
+/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00u
+/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is
+ set. */
+#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01u
+
+typedef struct ngtcp2_crypto_km {
+ ngtcp2_vec secret;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_vec iv;
+ /* pkt_num is a packet number of a packet which uses this keying
+ material. For encryption key, it is the lowest packet number of
+ a packet. For decryption key, it is the lowest packet number of
+ a packet which can be decrypted with this keying material. */
+ int64_t pkt_num;
+ /* use_count is the number of encryption applied with this key.
+ This field is only used for tx key. */
+ uint64_t use_count;
+ /* flags is the bitwise OR of zero or more of
+ NGTCP2_CRYPTO_KM_FLAG_*. */
+ uint8_t flags;
+} ngtcp2_crypto_km;
+
+/*
+ * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object and
+ * assigns its pointer to |*pckm|. The |secret| of length
+ * |secretlen|, the |key| of length |keylen| and the |iv| of length
+ * |ivlen| are copied to |*pckm|. If |secretlen| == 0, the function
+ * assumes no secret is given which is acceptable. The sole reason to
+ * store secret is update keys. Only 1RTT key can be updated.
+ */
+int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but
+ * it does not copy secret, key and IV.
+ */
+int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen,
+ size_t ivlen, const ngtcp2_mem *mem);
+
+void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem);
+
+typedef struct ngtcp2_crypto_cc {
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_cipher hp;
+ ngtcp2_crypto_km *ckm;
+ ngtcp2_crypto_cipher_ctx hp_ctx;
+ ngtcp2_encrypt encrypt;
+ ngtcp2_decrypt decrypt;
+ ngtcp2_hp_mask hp_mask;
+} ngtcp2_crypto_cc;
+
+void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen,
+ int64_t pkt_num);
+
+/*
+ * ngtcp2_transport_params_copy_new makes a copy of |src|, and assigns
+ * it to |*pdest|. If |src| is NULL, NULL is assigned to |*pdest|.
+ *
+ * Caller is responsible to call ngtcp2_transport_params_del to free
+ * the memory assigned to |*pdest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_transport_params_copy_new(ngtcp2_transport_params **pdest,
+ const ngtcp2_transport_params *src,
+ const ngtcp2_mem *mem);
+
+#endif /* NGTCP2_CRYPTO_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
new file mode 100644
index 0000000..ab932bd
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
@@ -0,0 +1,154 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_err.h"
+
+const char *ngtcp2_strerror(int liberr) {
+ switch (liberr) {
+ case 0:
+ return "NO_ERROR";
+ case NGTCP2_ERR_INVALID_ARGUMENT:
+ return "ERR_INVALID_ARGUMENT";
+ case NGTCP2_ERR_NOBUF:
+ return "ERR_NOBUF";
+ case NGTCP2_ERR_PROTO:
+ return "ERR_PROTO";
+ case NGTCP2_ERR_INVALID_STATE:
+ return "ERR_INVALID_STATE";
+ case NGTCP2_ERR_ACK_FRAME:
+ return "ERR_ACK_FRAME";
+ case NGTCP2_ERR_STREAM_ID_BLOCKED:
+ return "ERR_STREAM_ID_BLOCKED";
+ case NGTCP2_ERR_STREAM_IN_USE:
+ return "ERR_STREAM_IN_USE";
+ case NGTCP2_ERR_STREAM_DATA_BLOCKED:
+ return "ERR_STREAM_DATA_BLOCKED";
+ case NGTCP2_ERR_FLOW_CONTROL:
+ return "ERR_FLOW_CONTROL";
+ case NGTCP2_ERR_CONNECTION_ID_LIMIT:
+ return "ERR_CONNECTION_ID_LIMIT";
+ case NGTCP2_ERR_STREAM_LIMIT:
+ return "ERR_STREAM_LIMIT";
+ case NGTCP2_ERR_FINAL_SIZE:
+ return "ERR_FINAL_SIZE";
+ case NGTCP2_ERR_CRYPTO:
+ return "ERR_CRYPTO";
+ case NGTCP2_ERR_PKT_NUM_EXHAUSTED:
+ return "ERR_PKT_NUM_EXHAUSTED";
+ case NGTCP2_ERR_NOMEM:
+ return "ERR_NOMEM";
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ return "ERR_REQUIRED_TRANSPORT_PARAM";
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ return "ERR_MALFORMED_TRANSPORT_PARAM";
+ case NGTCP2_ERR_FRAME_ENCODING:
+ return "ERR_FRAME_ENCODING";
+ case NGTCP2_ERR_DECRYPT:
+ return "ERR_DECRYPT";
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ return "ERR_STREAM_SHUT_WR";
+ case NGTCP2_ERR_STREAM_NOT_FOUND:
+ return "ERR_STREAM_NOT_FOUND";
+ case NGTCP2_ERR_STREAM_STATE:
+ return "ERR_STREAM_STATE";
+ case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
+ return "ERR_RECV_VERSION_NEGOTIATION";
+ case NGTCP2_ERR_CLOSING:
+ return "ERR_CLOSING";
+ case NGTCP2_ERR_DRAINING:
+ return "ERR_DRAINING";
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ return "ERR_TRANSPORT_PARAM";
+ case NGTCP2_ERR_DISCARD_PKT:
+ return "ERR_DISCARD_PKT";
+ case NGTCP2_ERR_CONN_ID_BLOCKED:
+ return "ERR_CONN_ID_BLOCKED";
+ case NGTCP2_ERR_CALLBACK_FAILURE:
+ return "ERR_CALLBACK_FAILURE";
+ case NGTCP2_ERR_INTERNAL:
+ return "ERR_INTERNAL";
+ case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED:
+ return "ERR_CRYPTO_BUFFER_EXCEEDED";
+ case NGTCP2_ERR_WRITE_MORE:
+ return "ERR_WRITE_MORE";
+ case NGTCP2_ERR_RETRY:
+ return "ERR_RETRY";
+ case NGTCP2_ERR_DROP_CONN:
+ return "ERR_DROP_CONN";
+ case NGTCP2_ERR_AEAD_LIMIT_REACHED:
+ return "ERR_AEAD_LIMIT_REACHED";
+ case NGTCP2_ERR_NO_VIABLE_PATH:
+ return "ERR_NO_VIABLE_PATH";
+ case NGTCP2_ERR_VERSION_NEGOTIATION:
+ return "ERR_VERSION_NEGOTIATION";
+ case NGTCP2_ERR_HANDSHAKE_TIMEOUT:
+ return "ERR_HANDSHAKE_TIMEOUT";
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return "ERR_VERSION_NEGOTIATION_FAILURE";
+ case NGTCP2_ERR_IDLE_CLOSE:
+ return "ERR_IDLE_CLOSE";
+ default:
+ return "(unknown)";
+ }
+}
+
+int ngtcp2_err_is_fatal(int liberr) { return liberr < NGTCP2_ERR_FATAL; }
+
+uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
+ switch (liberr) {
+ case 0:
+ return NGTCP2_NO_ERROR;
+ case NGTCP2_ERR_ACK_FRAME:
+ case NGTCP2_ERR_FRAME_ENCODING:
+ return NGTCP2_FRAME_ENCODING_ERROR;
+ case NGTCP2_ERR_FLOW_CONTROL:
+ return NGTCP2_FLOW_CONTROL_ERROR;
+ case NGTCP2_ERR_CONNECTION_ID_LIMIT:
+ return NGTCP2_CONNECTION_ID_LIMIT_ERROR;
+ case NGTCP2_ERR_STREAM_LIMIT:
+ return NGTCP2_STREAM_LIMIT_ERROR;
+ case NGTCP2_ERR_FINAL_SIZE:
+ return NGTCP2_FINAL_SIZE_ERROR;
+ case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
+ case NGTCP2_ERR_TRANSPORT_PARAM:
+ return NGTCP2_TRANSPORT_PARAMETER_ERROR;
+ case NGTCP2_ERR_INVALID_ARGUMENT:
+ case NGTCP2_ERR_NOMEM:
+ case NGTCP2_ERR_CALLBACK_FAILURE:
+ return NGTCP2_INTERNAL_ERROR;
+ case NGTCP2_ERR_STREAM_STATE:
+ return NGTCP2_STREAM_STATE_ERROR;
+ case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED:
+ return NGTCP2_CRYPTO_BUFFER_EXCEEDED;
+ case NGTCP2_ERR_AEAD_LIMIT_REACHED:
+ return NGTCP2_AEAD_LIMIT_REACHED;
+ case NGTCP2_ERR_NO_VIABLE_PATH:
+ return NGTCP2_NO_VIABLE_PATH;
+ case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ return NGTCP2_VERSION_NEGOTIATION_ERROR;
+ default:
+ return NGTCP2_PROTOCOL_VIOLATION;
+ }
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h
new file mode 100644
index 0000000..9229f54
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h
@@ -0,0 +1,34 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ERR_H
+#define NGTCP2_ERR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#endif /* NGTCP2_ERR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
new file mode 100644
index 0000000..87c2389
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
@@ -0,0 +1,167 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_gaptr.h"
+
+#include <string.h>
+#include <assert.h>
+
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
+ ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
+
+ gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
+ ngtcp2_range range = {0, UINT64_MAX};
+ int rv;
+
+ rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return 0;
+}
+
+void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) {
+ if (gaptr == NULL) {
+ return;
+ }
+
+ ngtcp2_ksl_free(&gaptr->gap);
+}
+
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) {
+ int rv;
+ ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
+ ngtcp2_ksl_it it;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ rv = gaptr_gap_init(gaptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+ m = ngtcp2_range_intersect(&q, &k);
+ if (!ngtcp2_range_len(&m)) {
+ break;
+ }
+
+ if (ngtcp2_range_eq(&k, &m)) {
+ ngtcp2_ksl_remove_hint(&gaptr->gap, &it, &it, &k);
+ continue;
+ }
+ ngtcp2_range_cut(&l, &r, &k, &m);
+ if (ngtcp2_range_len(&l)) {
+ ngtcp2_ksl_update_key(&gaptr->gap, &k, &l);
+
+ if (ngtcp2_range_len(&r)) {
+ rv = ngtcp2_ksl_insert(&gaptr->gap, &it, &r, NULL);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else if (ngtcp2_range_len(&r)) {
+ ngtcp2_ksl_update_key(&gaptr->gap, &k, &r);
+ }
+ ngtcp2_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) {
+ ngtcp2_ksl_it it;
+ ngtcp2_range r;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_begin(&gaptr->gap);
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ return r.begin;
+}
+
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ uint64_t offset) {
+ ngtcp2_range q = {offset, offset + 1};
+ ngtcp2_ksl_it it;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ ngtcp2_range r = {0, UINT64_MAX};
+ return r;
+ }
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ assert(!ngtcp2_ksl_it_end(&it));
+
+ return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+}
+
+int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen) {
+ ngtcp2_range q = {offset, offset + datalen};
+ ngtcp2_ksl_it it;
+ ngtcp2_range k;
+ ngtcp2_range m;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+ k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+ m = ngtcp2_range_intersect(&q, &k);
+
+ return ngtcp2_range_len(&m) == 0;
+}
+
+void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) {
+ ngtcp2_ksl_it it;
+ ngtcp2_range r;
+
+ if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+ return;
+ }
+
+ it = ngtcp2_ksl_begin(&gaptr->gap);
+
+ assert(!ngtcp2_ksl_it_end(&it));
+
+ r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
+ ngtcp2_ksl_remove_hint(&gaptr->gap, NULL, &it, &r);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
new file mode 100644
index 0000000..0f100a8
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
@@ -0,0 +1,98 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_GAPTR_H
+#define NGTCP2_GAPTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_range.h"
+
+/*
+ * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
+ */
+typedef struct ngtcp2_gaptr {
+ /* gap maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ ngtcp2_ksl gap;
+ /* mem is custom memory allocator */
+ const ngtcp2_mem *mem;
+} ngtcp2_gaptr;
+
+/*
+ * ngtcp2_gaptr_init initializes |gaptr|.
+ */
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_gaptr_free frees resources allocated for |gaptr|.
+ */
+void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr);
+
+/*
+ * ngtcp2_gaptr_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen);
+
+/*
+ * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr);
+
+/*
+ * ngtcp2_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
+ */
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+ uint64_t offset);
+
+/*
+ * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset +
+ * datalen) is completely pushed into this object.
+ */
+int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
+ uint64_t datalen);
+
+/*
+ * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if
+ * the range is pushed. This function assumes that at least one gap
+ * exists.
+ */
+void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr);
+
+#endif /* NGTCP2_GAPTR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
new file mode 100644
index 0000000..d988022
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
@@ -0,0 +1,79 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_idtr.h"
+
+#include <assert.h>
+
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
+ ngtcp2_gaptr_init(&idtr->gap, mem);
+
+ idtr->server = server;
+}
+
+void ngtcp2_idtr_free(ngtcp2_idtr *idtr) {
+ if (idtr == NULL) {
+ return;
+ }
+
+ ngtcp2_gaptr_free(&idtr->gap);
+}
+
+/*
+ * id_from_stream_id translates |stream_id| to id space used by
+ * ngtcp2_idtr.
+ */
+static uint64_t id_from_stream_id(int64_t stream_id) {
+ return (uint64_t)(stream_id >> 2);
+}
+
+int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ if (ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1)) {
+ return NGTCP2_ERR_STREAM_IN_USE;
+ }
+
+ return ngtcp2_gaptr_push(&idtr->gap, q, 1);
+}
+
+int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id) {
+ uint64_t q;
+
+ assert((idtr->server && (stream_id % 2)) ||
+ (!idtr->server && (stream_id % 2)) == 0);
+
+ q = id_from_stream_id(stream_id);
+
+ return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1);
+}
+
+uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr) {
+ return ngtcp2_gaptr_first_gap_offset(&idtr->gap);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h
new file mode 100644
index 0000000..edb8c68
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h
@@ -0,0 +1,89 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_IDTR_H
+#define NGTCP2_IDTR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_gaptr.h"
+
+/*
+ * ngtcp2_idtr tracks the usage of stream ID.
+ */
+typedef struct ngtcp2_idtr {
+ /* gap maintains the range of ID which is not used yet. Initially,
+ its range is [0, UINT64_MAX). */
+ ngtcp2_gaptr gap;
+ /* server is nonzero if this object records server initiated stream
+ ID. */
+ int server;
+} ngtcp2_idtr;
+
+/*
+ * ngtcp2_idtr_init initializes |idtr|.
+ *
+ * If this object records server initiated ID (even number), set
+ * |server| to nonzero.
+ */
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_idtr_free frees resources allocated for |idtr|.
+ */
+void ngtcp2_idtr_free(ngtcp2_idtr *idtr);
+
+/*
+ * ngtcp2_idtr_open claims that |stream_id| is in used.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_STREAM_IN_USE
+ * ID has already been used.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id);
+
+/*
+ * ngtcp2_idtr_open tells whether ID |stream_id| is in used or not.
+ *
+ * It returns nonzero if |stream_id| is used.
+ */
+int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id);
+
+/*
+ * ngtcp2_idtr_first_gap returns the first id of first gap. If there
+ * is no gap, it returns UINT64_MAX. The returned id is an id space
+ * used in this object internally, and not stream ID.
+ */
+uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr);
+
+#endif /* NGTCP2_IDTR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
new file mode 100644
index 0000000..0bd424c
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
@@ -0,0 +1,819 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ksl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_range.h"
+
+static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
+static size_t ksl_nodelen(size_t keylen) {
+ return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfu) &
+ ~(uintptr_t)0xfu;
+}
+
+static size_t ksl_blklen(size_t nodelen) {
+ return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK -
+ sizeof(uint64_t);
+}
+
+/*
+ * ksl_node_set_key sets |key| to |node|.
+ */
+static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
+ const void *key) {
+ memcpy(node->key, key, ksl->keylen);
+}
+
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem) {
+ size_t nodelen = ksl_nodelen(keylen);
+
+ ngtcp2_objalloc_init(&ksl->blkalloc,
+ ((ksl_blklen(nodelen) + 0xfu) & ~(uintptr_t)0xfu) * 8,
+ mem);
+
+ ksl->head = NULL;
+ ksl->front = ksl->back = NULL;
+ ksl->compar = compar;
+ ksl->keylen = keylen;
+ ksl->nodelen = nodelen;
+ ksl->n = 0;
+}
+
+static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) {
+ return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+ ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+ ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+ if (!head) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ head->next = head->prev = NULL;
+ head->n = 0;
+ head->leaf = 1;
+
+ ksl->head = head;
+ ksl->front = ksl->back = head;
+
+ return 0;
+}
+
+#ifdef NOMEMPOOL
+/*
+ * ksl_free_blk frees |blk| recursively.
+ */
+static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+ size_t i;
+
+ if (!blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk);
+ }
+ }
+
+ ksl_blk_objalloc_del(ksl, blk);
+}
+#endif /* NOMEMPOOL */
+
+void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
+ if (!ksl || !ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ ngtcp2_objalloc_free(&ksl->blkalloc);
+}
+
+/*
+ * ksl_split_blk splits |blk| into 2 ngtcp2_ksl_blk objects. The new
+ * ngtcp2_ksl_blk is always the "right" block.
+ *
+ * It returns the pointer to the ngtcp2_ksl_blk created which is the
+ * located at the right of |blk|, or NULL which indicates out of
+ * memory error.
+ */
+static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+ ngtcp2_ksl_blk *rblk;
+
+ rblk = ksl_blk_objalloc_new(ksl);
+ if (rblk == NULL) {
+ return NULL;
+ }
+
+ rblk->next = blk->next;
+ blk->next = rblk;
+ if (rblk->next) {
+ rblk->next->prev = rblk;
+ } else if (ksl->back == blk) {
+ ksl->back = rblk;
+ }
+ rblk->prev = blk;
+ rblk->leaf = blk->leaf;
+
+ rblk->n = blk->n / 2;
+
+ memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n),
+ ksl->nodelen * rblk->n);
+
+ blk->n -= rblk->n;
+
+ assert(blk->n >= NGTCP2_KSL_MIN_NBLK);
+ assert(rblk->n >= NGTCP2_KSL_MIN_NBLK);
+
+ return rblk;
+}
+
+/*
+ * ksl_split_node splits a node included in |blk| at the position |i|
+ * into 2 adjacent nodes. The new node is always inserted at the
+ * position |i+1|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *node;
+ ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk;
+
+ rblk = ksl_split_blk(ksl, lblk);
+ if (rblk == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ memmove(blk->nodes + (i + 2) * ksl->nodelen,
+ blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+ node->blk = rblk;
+ ++blk->n;
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+
+ return 0;
+}
+
+/*
+ * ksl_split_head splits a head (root) block. It increases the height
+ * of skip list by 1.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+static int ksl_split_head(ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL;
+ ngtcp2_ksl_node *node;
+
+ rblk = ksl_split_blk(ksl, ksl->head);
+ if (rblk == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ lblk = ksl->head;
+
+ nhead = ksl_blk_objalloc_new(ksl);
+ if (nhead == NULL) {
+ ksl_blk_objalloc_del(ksl, rblk);
+ return NGTCP2_ERR_NOMEM;
+ }
+ nhead->next = nhead->prev = NULL;
+ nhead->n = 2;
+ nhead->leaf = 0;
+
+ node = ngtcp2_ksl_nth_node(ksl, nhead, 0);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ node->blk = lblk;
+
+ node = ngtcp2_ksl_nth_node(ksl, nhead, 1);
+ ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key);
+ node->blk = rblk;
+
+ ksl->head = nhead;
+
+ return 0;
+}
+
+/*
+ * insert_node inserts a node whose key is |key| with the associated
+ * |data| at the index of |i|. This function assumes that the number
+ * of nodes contained by |blk| is strictly less than
+ * NGTCP2_KSL_MAX_NBLK.
+ */
+static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i,
+ const ngtcp2_ksl_key *key, void *data) {
+ ngtcp2_ksl_node *node;
+
+ assert(blk->n < NGTCP2_KSL_MAX_NBLK);
+
+ memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen,
+ ksl->nodelen * (blk->n - i));
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ ksl_node_set_key(ksl, node, key);
+ node->data = data;
+
+ ++blk->n;
+}
+
+static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) {
+ size_t i;
+ ngtcp2_ksl_node *node;
+
+ for (i = 0, node = (ngtcp2_ksl_node *)(void *)blk->nodes;
+ i < blk->n && compar((ngtcp2_ksl_key *)node->key, key);
+ ++i, node = (ngtcp2_ksl_node *)(void *)((uint8_t *)node + ksl->nodelen))
+ ;
+
+ return i;
+}
+
+int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key, void *data) {
+ ngtcp2_ksl_blk *blk;
+ ngtcp2_ksl_node *node;
+ size_t i;
+ int rv;
+
+ if (!ksl->head) {
+ rv = ksl_head_init(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ blk = ksl->head;
+
+ if (blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_head(ksl);
+ if (rv != 0) {
+ return rv;
+ }
+ blk = ksl->head;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i < blk->n &&
+ !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ ksl_insert_node(ksl, blk, i, key, data);
+ ++ksl->n;
+ if (it) {
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
+ }
+ return 0;
+ }
+
+ if (i == blk->n) {
+ /* This insertion extends the largest key in this subtree. */
+ for (; !blk->leaf;) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, blk->n - 1);
+ if (rv != 0) {
+ return rv;
+ }
+ node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1);
+ }
+ ksl_node_set_key(ksl, node, key);
+ blk = node->blk;
+ }
+ ksl_insert_node(ksl, blk, blk->n, key, data);
+ ++ksl->n;
+ if (it) {
+ ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1);
+ }
+ return 0;
+ }
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n == NGTCP2_KSL_MAX_NBLK) {
+ rv = ksl_split_node(ksl, blk, i);
+ if (rv != 0) {
+ return rv;
+ }
+ if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+ if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) {
+ ksl_node_set_key(ksl, node, key);
+ }
+ }
+ }
+
+ blk = node->blk;
+ }
+}
+
+/*
+ * ksl_remove_node removes the node included in |blk| at the index of
+ * |i|.
+ */
+static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen,
+ ksl->nodelen * (blk->n - (i + 1)));
+
+ --blk->n;
+}
+
+/*
+ * ksl_merge_node merges 2 nodes which are the nodes at the index of
+ * |i| and |i + 1|.
+ *
+ * If |blk| is the direct descendant of head (root) block and the head
+ * block contains just 2 nodes, the merged block becomes head block,
+ * which decreases the height of |ksl| by 1.
+ *
+ * This function returns the pointer to the merged block.
+ */
+static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
+ size_t i) {
+ ngtcp2_ksl_blk *lblk, *rblk;
+
+ assert(i + 1 < blk->n);
+
+ lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk;
+
+ assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK);
+
+ memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes,
+ ksl->nodelen * rblk->n);
+
+ lblk->n += rblk->n;
+ lblk->next = rblk->next;
+ if (lblk->next) {
+ lblk->next->prev = lblk;
+ } else if (ksl->back == rblk) {
+ ksl->back = lblk;
+ }
+
+ ksl_blk_objalloc_del(ksl, rblk);
+
+ if (ksl->head == blk && blk->n == 2) {
+ ksl_blk_objalloc_del(ksl, ksl->head);
+ ksl->head = lblk;
+ } else {
+ ksl_remove_node(ksl, blk, i + 1);
+ ksl_node_set_key(ksl, ngtcp2_ksl_nth_node(ksl, blk, i),
+ ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key);
+ }
+
+ return lblk;
+}
+
+/*
+ * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i - 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible.
+ */
+static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i > 0);
+
+ lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1);
+ rnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK);
+ assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n);
+ assert(rnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n);
+
+ memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes,
+ ksl->nodelen * n);
+
+ lnode->blk->n += (uint32_t)n;
+ rnode->blk->n -= (uint32_t)n;
+
+ ksl_node_set_key(
+ ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+
+ memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n,
+ ksl->nodelen * rnode->blk->n);
+}
+
+/*
+ * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes
+ * to blk->nodes[i + 1]->blk->nodes in a manner that they have the
+ * same amount of nodes as much as possible..
+ */
+static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) {
+ ngtcp2_ksl_node *lnode, *rnode;
+ size_t n;
+
+ assert(i < blk->n - 1);
+
+ lnode = ngtcp2_ksl_nth_node(ksl, blk, i);
+ rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1);
+
+ assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK);
+ assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK);
+
+ n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n;
+
+ assert(n > 0);
+ assert(lnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n);
+ assert(rnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n);
+
+ memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes,
+ ksl->nodelen * rnode->blk->n);
+
+ rnode->blk->n += (uint32_t)n;
+ lnode->blk->n -= (uint32_t)n;
+
+ memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n,
+ ksl->nodelen * n);
+
+ ksl_node_set_key(
+ ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key);
+}
+
+/*
+ * key_equal returns nonzero if |lhs| and |rhs| are equal using the
+ * function |compar|.
+ */
+static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ return !compar(lhs, rhs) && !compar(rhs, lhs);
+}
+
+int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_it *hint,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = hint->blk;
+
+ assert(ksl->head);
+
+ if (blk->n <= NGTCP2_KSL_MIN_NBLK) {
+ return ngtcp2_ksl_remove(ksl, it, key);
+ }
+
+ ksl_remove_node(ksl, blk, hint->i);
+
+ --ksl->n;
+
+ if (it) {
+ if (hint->i == blk->n && blk->next) {
+ ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ ngtcp2_ksl_it_init(it, ksl, blk, hint->i);
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_node *node;
+ size_t i;
+
+ if (!ksl->head) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!blk->leaf && blk->n == 2 &&
+ ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK &&
+ ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) {
+ blk = ksl_merge_node(ksl, ksl->head, 0);
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (i == blk->n) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (blk->leaf) {
+ if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) {
+ if (it) {
+ *it = ngtcp2_ksl_end(ksl);
+ }
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ ksl_remove_node(ksl, blk, i);
+ --ksl->n;
+ if (it) {
+ if (blk->n == i && blk->next) {
+ ngtcp2_ksl_it_init(it, ksl, blk->next, 0);
+ } else {
+ ngtcp2_ksl_it_init(it, ksl, blk, i);
+ }
+ }
+ return 0;
+ }
+
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (node->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ blk = node->blk;
+ continue;
+ }
+
+ assert(node->blk->n == NGTCP2_KSL_MIN_NBLK);
+
+ if (i + 1 < blk->n &&
+ ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_left(ksl, blk, i + 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i > 0 &&
+ ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) {
+ ksl_shift_right(ksl, blk, i - 1);
+ blk = node->blk;
+ continue;
+ }
+
+ if (i + 1 < blk->n) {
+ blk = ksl_merge_node(ksl, blk, i);
+ continue;
+ }
+
+ assert(i > 0);
+
+ blk = ksl_merge_node(ksl, blk, i - 1);
+ }
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, ksl->compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_it it;
+ size_t i;
+
+ if (!blk) {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ return it;
+ }
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, key, compar);
+
+ if (blk->leaf) {
+ if (i == blk->n && blk->next) {
+ blk = blk->next;
+ i = 0;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+
+ if (i == blk->n) {
+ /* This happens if descendant has smaller key. Fast forward to
+ find last node in this subtree. */
+ for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk)
+ ;
+ if (blk->next) {
+ blk = blk->next;
+ i = 0;
+ } else {
+ i = blk->n;
+ }
+ ngtcp2_ksl_it_init(&it, ksl, blk, i);
+ return it;
+ }
+ blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk;
+ }
+}
+
+void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
+ const ngtcp2_ksl_key *new_key) {
+ ngtcp2_ksl_blk *blk = ksl->head;
+ ngtcp2_ksl_node *node;
+ size_t i;
+
+ assert(ksl->head);
+
+ for (;;) {
+ i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
+
+ assert(i < blk->n);
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+
+ if (blk->leaf) {
+ assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key));
+ ksl_node_set_key(ksl, node, new_key);
+ return;
+ }
+
+ if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) ||
+ ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) {
+ ksl_node_set_key(ksl, node, new_key);
+ }
+
+ blk = node->blk;
+ }
+}
+
+static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
+ size_t i;
+ ngtcp2_ksl_node *node;
+
+ fprintf(stderr, "LV=%zu n=%u\n", level, blk->n);
+
+ if (blk->leaf) {
+ for (i = 0; i < blk->n; ++i) {
+ node = ngtcp2_ksl_nth_node(ksl, blk, i);
+ fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key);
+ }
+ fprintf(stderr, "\n");
+ return;
+ }
+
+ for (i = 0; i < blk->n; ++i) {
+ ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1);
+ }
+}
+
+size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; }
+
+void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+#ifdef NOMEMPOOL
+ ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+ ksl->front = ksl->back = ksl->head = NULL;
+ ksl->n = 0;
+
+ ngtcp2_objalloc_clear(&ksl->blkalloc);
+}
+
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl) {
+ if (!ksl->head) {
+ return;
+ }
+
+ ksl_print(ksl, ksl->head, 0);
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_it it;
+
+ if (ksl->head) {
+ ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+ } else {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
+ ngtcp2_ksl_it it;
+
+ if (ksl->head) {
+ ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+ } else {
+ ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+ }
+
+ return it;
+}
+
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i) {
+ it->ksl = ksl;
+ it->blk = blk;
+ it->i = i;
+}
+
+void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) {
+ assert(!ngtcp2_ksl_it_begin(it));
+
+ if (it->i == 0) {
+ it->blk = it->blk->prev;
+ it->i = it->blk->n - 1;
+ } else {
+ --it->i;
+ }
+}
+
+int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) {
+ return it->i == 0 && it->blk->prev == NULL;
+}
+
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs, *b = rhs;
+ return a->begin < b->begin;
+}
+
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs) {
+ const ngtcp2_range *a = lhs, *b = rhs;
+ return a->begin < b->begin &&
+ !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end));
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h
new file mode 100644
index 0000000..312a151
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h
@@ -0,0 +1,345 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_KSL_H
+#define NGTCP2_KSL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stdlib.h>
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_objalloc.h"
+
+/*
+ * Skip List using single key instead of range.
+ */
+
+#define NGTCP2_KSL_DEGR 16
+/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single
+ block can contain. */
+#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1)
+/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single
+ block other than root must contains. */
+#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1)
+
+/*
+ * ngtcp2_ksl_key represents key in ngtcp2_ksl.
+ */
+typedef void ngtcp2_ksl_key;
+
+typedef struct ngtcp2_ksl_node ngtcp2_ksl_node;
+
+typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk;
+
+/*
+ * ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or
+ * opaque data. If a node is an internal node, it contains
+ * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the
+ * location starting at key.
+ */
+struct ngtcp2_ksl_node {
+ union {
+ ngtcp2_ksl_blk *blk;
+ void *data;
+ };
+ union {
+ uint64_t align;
+ /* key is a buffer to include key associated to this node.
+ Because the length of key is unknown until ngtcp2_ksl_init is
+ called, the actual buffer will be allocated after this
+ field. */
+ uint8_t key[1];
+ };
+};
+
+/*
+ * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects.
+ */
+struct ngtcp2_ksl_blk {
+ union {
+ struct {
+ /* next points to the next block if leaf field is nonzero. */
+ ngtcp2_ksl_blk *next;
+ /* prev points to the previous block if leaf field is nonzero. */
+ ngtcp2_ksl_blk *prev;
+ /* n is the number of nodes this object contains in nodes. */
+ uint32_t n;
+ /* leaf is nonzero if this block contains leaf nodes. */
+ uint32_t leaf;
+ union {
+ uint64_t align;
+ /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
+ ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is
+ allocated along with the additional variable length key
+ storage, the size of buffer is unknown until ngtcp2_ksl_init is
+ called. */
+ uint8_t nodes[1];
+ };
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
+};
+
+ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent);
+
+/*
+ * ngtcp2_ksl_compar is a function type which returns nonzero if key
+ * |lhs| should be placed before |rhs|. It returns 0 otherwise.
+ */
+typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+typedef struct ngtcp2_ksl ngtcp2_ksl;
+
+typedef struct ngtcp2_ksl_it ngtcp2_ksl_it;
+
+/*
+ * ngtcp2_ksl_it is a forward iterator to iterate nodes.
+ */
+struct ngtcp2_ksl_it {
+ const ngtcp2_ksl *ksl;
+ ngtcp2_ksl_blk *blk;
+ size_t i;
+};
+
+/*
+ * ngtcp2_ksl is a deterministic paged skip list.
+ */
+struct ngtcp2_ksl {
+ ngtcp2_objalloc blkalloc;
+ /* head points to the root block. */
+ ngtcp2_ksl_blk *head;
+ /* front points to the first leaf block. */
+ ngtcp2_ksl_blk *front;
+ /* back points to the last leaf block. */
+ ngtcp2_ksl_blk *back;
+ ngtcp2_ksl_compar compar;
+ size_t n;
+ /* keylen is the size of key */
+ size_t keylen;
+ /* nodelen is the actual size of ngtcp2_ksl_node including key
+ storage. */
+ size_t nodelen;
+};
+
+/*
+ * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare
+ * function. |keylen| is the length of key.
+ */
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is
+ * NULL, this function does nothing. It does not free the memory
+ * region pointed by |ksl| itself.
+ */
+void ngtcp2_ksl_free(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_insert inserts |key| with its associated |data|. On
+ * successful insertion, the iterator points to the inserted node is
+ * stored in |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |key| already exists.
+ */
+int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key, void *data);
+
+/*
+ * ngtcp2_ksl_remove removes the |key| from |ksl|.
+ *
+ * This function assigns the iterator to |*it|, which points to the
+ * node which is located at the right next of the removed node if |it|
+ * is not NULL. If |key| is not found, no deletion takes place and
+ * the return value of ngtcp2_ksl_end(ksl) is assigned to |*it|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |key| does not exist.
+ */
+int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_key *key);
+
+/*
+ * ngtcp2_ksl_remove_hint removes the |key| from |ksl|. |hint| must
+ * point to the same node denoted by |key|. |hint| is used to remove
+ * a node efficiently in some cases. Other than that, it behaves
+ * exactly like ngtcp2_ksl_remove. |it| and |hint| can point to the
+ * same object.
+ */
+int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
+ const ngtcp2_ksl_it *hint,
+ const ngtcp2_ksl_key *key);
+
+/*
+ * ngtcp2_ksl_lower_bound returns the iterator which points to the
+ * first node which has the key which is equal to |key| or the last
+ * node which satisfies !compar(&node->key, key). If there is no such
+ * node, it returns the iterator which satisfies ngtcp2_ksl_it_end(it)
+ * != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key);
+
+/*
+ * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound,
+ * but it takes custom function |compar| to do lower bound search.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_key *key,
+ ngtcp2_ksl_compar compar);
+
+/*
+ * ngtcp2_ksl_update_key replaces the key of nodes which has |old_key|
+ * with |new_key|. |new_key| must be strictly greater than the
+ * previous node and strictly smaller than the next node.
+ */
+void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
+ const ngtcp2_ksl_key *new_key);
+
+/*
+ * ngtcp2_ksl_begin returns the iterator which points to the first
+ * node. If there is no node in |ksl|, it returns the iterator which
+ * satisfies ngtcp2_ksl_it_end(it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_end returns the iterator which points to the node
+ * following the last node. The returned object satisfies
+ * ngtcp2_ksl_it_end(). If there is no node in |ksl|, it returns the
+ * iterator which satisfies ngtcp2_ksl_it_begin(it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_len returns the number of elements stored in |ksl|.
+ */
+size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_clear removes all elements stored in |ksl|.
+ */
+void ngtcp2_ksl_clear(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_nth_node returns the |n|th node under |blk|.
+ */
+#define ngtcp2_ksl_nth_node(KSL, BLK, N) \
+ ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+
+/*
+ * ngtcp2_ksl_print prints its internal state in stderr. It assumes
+ * that the key is of type int64_t. This function should be used for
+ * the debugging purpose only.
+ */
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl);
+
+/*
+ * ngtcp2_ksl_it_init initializes |it|.
+ */
+void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl,
+ ngtcp2_ksl_blk *blk, size_t i);
+
+/*
+ * ngtcp2_ksl_it_get returns the data associated to the node which
+ * |it| points to. It is undefined to call this function when
+ * ngtcp2_ksl_it_end(it) returns nonzero.
+ */
+#define ngtcp2_ksl_it_get(IT) \
+ ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
+
+/*
+ * ngtcp2_ksl_it_next advances the iterator by one. It is undefined
+ * if this function is called when ngtcp2_ksl_it_end(it) returns
+ * nonzero.
+ */
+#define ngtcp2_ksl_it_next(IT) \
+ (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
+ ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
+ : 0)
+
+/*
+ * ngtcp2_ksl_it_prev moves backward the iterator by one. It is
+ * undefined if this function is called when ngtcp2_ksl_it_begin(it)
+ * returns nonzero.
+ */
+void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it);
+
+/*
+ * ngtcp2_ksl_it_end returns nonzero if |it| points to the beyond the
+ * last node.
+ */
+#define ngtcp2_ksl_it_end(IT) \
+ ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+
+/*
+ * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first
+ * node. |it| might satisfy both ngtcp2_ksl_it_begin(&it) and
+ * ngtcp2_ksl_it_end(&it) if the skip list has no node.
+ */
+int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it);
+
+/*
+ * ngtcp2_ksl_key returns the key of the node which |it| points to.
+ * It is undefined to call this function when ngtcp2_ksl_it_end(it)
+ * returns nonzero.
+ */
+#define ngtcp2_ksl_it_key(IT) \
+ ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+
+/*
+ * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar.
+ * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the
+ * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin
+ * < (const ngtcp2_range *)(rhs->ptr)->begin.
+ */
+int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+/*
+ * ngtcp2_ksl_range_exclusive_compar is an implementation of
+ * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to
+ * ngtcp2_range object and the function returns nonzero if (const
+ * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range
+ * *)(rhs->ptr)->begin and the 2 ranges do not intersect.
+ */
+int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs,
+ const ngtcp2_ksl_key *rhs);
+
+#endif /* NGTCP2_KSL_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
new file mode 100644
index 0000000..790adeb
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
@@ -0,0 +1,822 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_log.h"
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_unreachable.h"
+#include "ngtcp2_net.h"
+
+void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
+ ngtcp2_printf log_printf, ngtcp2_tstamp ts,
+ void *user_data) {
+ if (scid) {
+ ngtcp2_encode_hex(log->scid, scid->data, scid->datalen);
+ } else {
+ log->scid[0] = '\0';
+ }
+ log->log_printf = log_printf;
+ log->ts = log->last_ts = ts;
+ log->user_data = user_data;
+}
+
+/*
+ * # Log header
+ *
+ * <LEVEL><TIMESTAMP> <SCID> <EVENT>
+ *
+ * <LEVEL>:
+ * Log level. I=Info, W=Warning, E=Error
+ *
+ * <TIMESTAMP>:
+ * Timestamp relative to ngtcp2_log.ts field in milliseconds
+ * resolution.
+ *
+ * <SCID>:
+ * Source Connection ID in hex string.
+ *
+ * <EVENT>:
+ * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto,
+ * con=connection(catch all)
+ *
+ * # Frame event
+ *
+ * <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>)
+ *
+ * <DIR>:
+ * Flow direction. tx=transmission, rx=reception
+ *
+ * <PKN>:
+ * Packet number.
+ *
+ * <PKTNAME>:
+ * Packet name. (e.g., Initial, Handshake, 1RTT)
+ *
+ * <FRAMENAME>:
+ * Frame name. (e.g., STREAM, ACK, PING)
+ *
+ * <FRAMETYPE>:
+ * Frame type in hex string.
+ */
+
+#define NGTCP2_LOG_BUFLEN 4096
+
+/* TODO Split second and remaining fraction with comma */
+#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s"
+#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s"
+#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters"
+
+#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \
+ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \
+ (DIR), hd->pkt_num, strpkttype(hd)
+
+#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \
+ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \
+ (DIR), hd->pkt_num, strpkttype(hd)
+
+#define NGTCP2_LOG_TP_HD_FIELDS \
+ timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
+
+static const char *strerrorcode(uint64_t error_code) {
+ switch (error_code) {
+ case NGTCP2_NO_ERROR:
+ return "NO_ERROR";
+ case NGTCP2_INTERNAL_ERROR:
+ return "INTERNAL_ERROR";
+ case NGTCP2_CONNECTION_REFUSED:
+ return "CONNECTION_REFUSED";
+ case NGTCP2_FLOW_CONTROL_ERROR:
+ return "FLOW_CONTROL_ERROR";
+ case NGTCP2_STREAM_LIMIT_ERROR:
+ return "STREAM_LIMIT_ERROR";
+ case NGTCP2_STREAM_STATE_ERROR:
+ return "STREAM_STATE_ERROR";
+ case NGTCP2_FINAL_SIZE_ERROR:
+ return "FINAL_SIZE_ERROR";
+ case NGTCP2_FRAME_ENCODING_ERROR:
+ return "FRAME_ENCODING_ERROR";
+ case NGTCP2_TRANSPORT_PARAMETER_ERROR:
+ return "TRANSPORT_PARAMETER_ERROR";
+ case NGTCP2_CONNECTION_ID_LIMIT_ERROR:
+ return "CONNECTION_ID_LIMIT_ERROR";
+ case NGTCP2_PROTOCOL_VIOLATION:
+ return "PROTOCOL_VIOLATION";
+ case NGTCP2_INVALID_TOKEN:
+ return "INVALID_TOKEN";
+ case NGTCP2_APPLICATION_ERROR:
+ return "APPLICATION_ERROR";
+ case NGTCP2_CRYPTO_BUFFER_EXCEEDED:
+ return "CRYPTO_BUFFER_EXCEEDED";
+ case NGTCP2_KEY_UPDATE_ERROR:
+ return "KEY_UPDATE_ERROR";
+ case NGTCP2_VERSION_NEGOTIATION_ERROR:
+ return "VERSION_NEGOTIATION_ERROR";
+ default:
+ if (0x100u <= error_code && error_code <= 0x1ffu) {
+ return "CRYPTO_ERROR";
+ }
+ return "(unknown)";
+ }
+}
+
+static const char *strapperrorcode(uint64_t app_error_code) {
+ (void)app_error_code;
+ return "(unknown)";
+}
+
+static const char *strpkttype_long(uint8_t type) {
+ switch (type) {
+ case NGTCP2_PKT_INITIAL:
+ return "Initial";
+ case NGTCP2_PKT_RETRY:
+ return "Retry";
+ case NGTCP2_PKT_HANDSHAKE:
+ return "Handshake";
+ case NGTCP2_PKT_0RTT:
+ return "0RTT";
+ default:
+ return "(unknown)";
+ }
+}
+
+static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ return strpkttype_long(hd->type);
+ }
+
+ switch (hd->type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ return "VN";
+ case NGTCP2_PKT_STATELESS_RESET:
+ return "SR";
+ case NGTCP2_PKT_1RTT:
+ return "1RTT";
+ default:
+ return "(unknown)";
+ }
+}
+
+static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) {
+ ngtcp2_pkt_hd hd = {0};
+
+ hd.type = type;
+ hd.flags = flags;
+
+ return strpkttype(&hd);
+}
+
+static const char *strevent(ngtcp2_log_event ev) {
+ switch (ev) {
+ case NGTCP2_LOG_EVENT_CON:
+ return "con";
+ case NGTCP2_LOG_EVENT_PKT:
+ return "pkt";
+ case NGTCP2_LOG_EVENT_FRM:
+ return "frm";
+ case NGTCP2_LOG_EVENT_RCV:
+ return "rcv";
+ case NGTCP2_LOG_EVENT_CRY:
+ return "cry";
+ case NGTCP2_LOG_EVENT_PTV:
+ return "ptv";
+ case NGTCP2_LOG_EVENT_NONE:
+ default:
+ return "non";
+ }
+}
+
+static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; }
+
+static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_stream *fr, const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 " fin=%d offset=%" PRIu64
+ " len=%" PRIu64 " uni=%d"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, fr->stream_id,
+ fr->fin, fr->offset, ngtcp2_vec_len(fr->data, fr->datacnt),
+ (fr->stream_id & 0x2) != 0);
+}
+
+static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_ack *fr, const char *dir) {
+ int64_t largest_ack, min_ack;
+ size_t i;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) largest_ack=%" PRId64
+ " ack_delay=%" PRIu64 "(%" PRIu64
+ ") ack_range_count=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack,
+ fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay,
+ fr->rangecnt);
+
+ largest_ack = fr->largest_ack;
+ min_ack = fr->largest_ack - (int64_t)fr->first_ack_range;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) range=[%" PRId64 "..%" PRId64
+ "] len=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack,
+ fr->first_ack_range);
+
+ for (i = 0; i < fr->rangecnt; ++i) {
+ const ngtcp2_ack_range *range = &fr->ranges[i];
+ largest_ack = min_ack - (int64_t)range->gap - 2;
+ min_ack = largest_ack - (int64_t)range->len;
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) range=[%" PRId64 "..%" PRId64
+ "] gap=%" PRIu64 " len=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack,
+ min_ack, range->gap, range->len);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " ACK(0x%02x) ect0=%" PRIu64
+ " ect1=%" PRIu64 " ce=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0,
+ fr->ecn.ect1, fr->ecn.ce);
+ }
+}
+
+static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_padding *fr, const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PADDING(0x%02x) len=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len);
+}
+
+static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_reset_stream *fr,
+ const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " RESET_STREAM(0x%02x) id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size);
+}
+
+static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_connection_close *fr,
+ const char *dir) {
+ char reason[256];
+ size_t reasonlen = ngtcp2_min(sizeof(reason) - 1, fr->reasonlen);
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT
+ " CONNECTION_CLOSE(0x%02x) error_code=%s(0x%" PRIx64 ") "
+ "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ fr->type == NGTCP2_FRAME_CONNECTION_CLOSE
+ ? strerrorcode(fr->error_code)
+ : strapperrorcode(fr->error_code),
+ fr->error_code, fr->frame_type, fr->reasonlen,
+ ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen));
+}
+
+static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_data *fr, const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_DATA(0x%02x) max_data=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data);
+}
+
+static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_stream_data *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02x) id=0x%" PRIx64
+ " max_stream_data=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->max_stream_data);
+}
+
+static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_max_streams *fr, const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02x) max_streams=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+}
+
+static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_ping *fr, const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02x)"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+}
+
+static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_data_blocked *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02x) offset=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset);
+}
+
+static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_stream_data_blocked *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02x) id=0x%" PRIx64
+ " offset=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ fr->offset);
+}
+
+static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_streams_blocked *fr,
+ const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02x) max_streams=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams);
+}
+
+static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_new_connection_id *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1];
+ uint8_t cid[sizeof(fr->cid.data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02x) seq=%" PRIu64
+ " cid=0x%s retire_prior_to=%" PRIu64
+ " stateless_reset_token=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq,
+ (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen),
+ fr->retire_prior_to,
+ (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token,
+ sizeof(fr->stateless_reset_token)));
+}
+
+static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_stop_sending *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " STOP_SENDING(0x%02x) id=0x%" PRIx64
+ " app_error_code=%s(0x%" PRIx64 ")"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id,
+ strapperrorcode(fr->app_error_code), fr->app_error_code);
+}
+
+static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_path_challenge *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02x) data=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+}
+
+static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_path_response *fr,
+ const char *dir) {
+ uint8_t buf[sizeof(fr->data) * 2 + 1];
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02x) data=0x%s"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data)));
+}
+
+static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_crypto *fr, const char *dir) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset,
+ ngtcp2_vec_len(fr->data, fr->datacnt));
+}
+
+static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_new_token *fr, const char *dir) {
+ /* Show at most first 64 bytes of token. If token is longer than 64
+ bytes, log first 64 bytes and then append "*" */
+ uint8_t buf[128 + 1 + 1];
+ uint8_t *p;
+
+ if (fr->tokenlen > 64) {
+ p = ngtcp2_encode_hex(buf, fr->token, 64);
+ p[128] = '*';
+ p[129] = '\0';
+ } else {
+ p = ngtcp2_encode_hex(buf, fr->token, fr->tokenlen);
+ }
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s len=%zu"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->tokenlen);
+}
+
+static void log_fr_retire_connection_id(ngtcp2_log *log,
+ const ngtcp2_pkt_hd *hd,
+ const ngtcp2_retire_connection_id *fr,
+ const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02x) seq=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq);
+}
+
+static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_handshake_done *fr,
+ const char *dir) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type);
+}
+
+static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_datagram *fr, const char *dir) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%" PRIu64),
+ NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type,
+ ngtcp2_vec_len(fr->data, fr->datacnt));
+}
+
+static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr, const char *dir) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_STREAM:
+ log_fr_stream(log, hd, &fr->stream, dir);
+ break;
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ log_fr_ack(log, hd, &fr->ack, dir);
+ break;
+ case NGTCP2_FRAME_PADDING:
+ log_fr_padding(log, hd, &fr->padding, dir);
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ log_fr_reset_stream(log, hd, &fr->reset_stream, dir);
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ log_fr_connection_close(log, hd, &fr->connection_close, dir);
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ log_fr_max_data(log, hd, &fr->max_data, dir);
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ log_fr_max_stream_data(log, hd, &fr->max_stream_data, dir);
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ log_fr_max_streams(log, hd, &fr->max_streams, dir);
+ break;
+ case NGTCP2_FRAME_PING:
+ log_fr_ping(log, hd, &fr->ping, dir);
+ break;
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ log_fr_data_blocked(log, hd, &fr->data_blocked, dir);
+ break;
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ log_fr_stream_data_blocked(log, hd, &fr->stream_data_blocked, dir);
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ log_fr_streams_blocked(log, hd, &fr->streams_blocked, dir);
+ break;
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ log_fr_new_connection_id(log, hd, &fr->new_connection_id, dir);
+ break;
+ case NGTCP2_FRAME_STOP_SENDING:
+ log_fr_stop_sending(log, hd, &fr->stop_sending, dir);
+ break;
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ log_fr_path_challenge(log, hd, &fr->path_challenge, dir);
+ break;
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ log_fr_path_response(log, hd, &fr->path_response, dir);
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ log_fr_crypto(log, hd, &fr->crypto, dir);
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ log_fr_new_token(log, hd, &fr->new_token, dir);
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir);
+ break;
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ log_fr_handshake_done(log, hd, &fr->handshake_done, dir);
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ log_fr_datagram(log, hd, &fr->datagram, dir);
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+}
+
+void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr) {
+ if (!log->log_printf) {
+ return;
+ }
+
+ log_fr(log, hd, fr, "rx");
+}
+
+void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr) {
+ if (!log->log_printf) {
+ return;
+ }
+
+ log_fr(log, hd, fr, "tx");
+}
+
+void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv) {
+ size_t i;
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ for (i = 0; i < nsv; ++i) {
+ log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"),
+ NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]);
+ }
+}
+
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
+ uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1];
+ ngtcp2_pkt_hd shd;
+ ngtcp2_pkt_hd *hd = &shd;
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ memset(&shd, 0, sizeof(shd));
+
+ shd.type = NGTCP2_PKT_STATELESS_RESET;
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
+ NGTCP2_LOG_PKT_HD_FIELDS("rx"),
+ (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token,
+ sizeof(sr->stateless_reset_token)),
+ sr->randlen);
+}
+
+void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
+ const ngtcp2_transport_params *params) {
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
+ uint8_t addr[16 * 2 + 7 + 1];
+ uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
+ size_t i;
+ const ngtcp2_sockaddr_in *sa_in;
+ const ngtcp2_sockaddr_in6 *sa_in6;
+ const uint8_t *p;
+ uint32_t version;
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) {
+ if (params->stateless_reset_token_present) {
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " stateless_reset_token=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ token, params->stateless_reset_token,
+ sizeof(params->stateless_reset_token)));
+ }
+
+ if (params->preferred_address_present) {
+ if (params->preferred_address.ipv4_present) {
+ sa_in = &params->preferred_address.ipv4;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_ipv4(
+ addr, (const uint8_t *)&sa_in->sin_addr));
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"),
+ NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in->sin_port));
+ }
+
+ if (params->preferred_address.ipv6_present) {
+ sa_in6 = &params->preferred_address.ipv6;
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_ipv6(
+ addr, (const uint8_t *)&sa_in6->sin6_addr));
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"),
+ NGTCP2_LOG_TP_HD_FIELDS, ngtcp2_ntohs(sa_in6->sin6_port));
+ }
+
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.cid=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ cid, params->preferred_address.cid.data,
+ params->preferred_address.cid.datalen));
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(
+ token, params->preferred_address.stateless_reset_token,
+ sizeof(params->preferred_address.stateless_reset_token)));
+ }
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data,
+ params->original_dcid.datalen));
+
+ if (params->retry_scid_present) {
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data,
+ params->retry_scid.datalen));
+ }
+ }
+
+ log->log_printf(
+ log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data,
+ params->initial_scid.datalen));
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local);
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ params->max_idle_timeout / NGTCP2_MILLISECONDS);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS,
+ params->max_ack_delay / NGTCP2_MILLISECONDS);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " disable_active_migration=%d"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration);
+ log->log_printf(log->user_data,
+ (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64),
+ NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size);
+ log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit);
+
+ if (params->version_info_present) {
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"),
+ NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version);
+
+ assert(!(params->version_info.available_versionslen & 0x3));
+
+ for (i = 0, p = params->version_info.available_versions;
+ i < params->version_info.available_versionslen;
+ i += sizeof(uint32_t)) {
+ p = ngtcp2_get_uint32(&version, p);
+
+ log->log_printf(
+ log->user_data,
+ (NGTCP2_LOG_TP " version_information.available_versions[%zu]=0x%08x"),
+ NGTCP2_LOG_TP_HD_FIELDS, i >> 2, version);
+ }
+ }
+}
+
+void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp sent_ts) {
+ if (!log->log_printf) {
+ return;
+ }
+
+ ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num,
+ strpkttype_type_flags(type, flags), sent_ts);
+}
+
+static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const char *dir) {
+ uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1];
+ uint8_t scid[sizeof(hd->scid.data) * 2 + 1];
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ if (hd->type == NGTCP2_PKT_1RTT) {
+ ngtcp2_log_info(
+ log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d",
+ dir, hd->pkt_num,
+ (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+ strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
+ } else {
+ ngtcp2_log_info(
+ log, NGTCP2_LOG_EVENT_PKT,
+ "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu",
+ dir, hd->pkt_num,
+ (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+ (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
+ hd->version, strpkttype(hd), hd->len);
+ }
+}
+
+void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
+ log_pkt_hd(log, hd, "rx");
+}
+
+void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
+ log_pkt_hd(log, hd, "tx");
+}
+
+void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
+ ...) {
+ va_list ap;
+ int n;
+ char buf[NGTCP2_LOG_BUFLEN];
+
+ if (!log->log_printf) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || (size_t)n >= sizeof(buf)) {
+ return;
+ }
+
+ log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"),
+ timestamp_cast(log->last_ts - log->ts), log->scid,
+ strevent(ev), buf);
+}
+
+void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
+ ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT,
+ "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num,
+ strpkttype(hd));
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h
new file mode 100644
index 0000000..029ef1b
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.h
@@ -0,0 +1,123 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_LOG_H
+#define NGTCP2_LOG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+
+typedef struct ngtcp2_log {
+ /* log_printf is a sink to write log. NULL means no logging
+ output. */
+ ngtcp2_printf log_printf;
+ /* ts is the time point used to write time delta in the log. */
+ ngtcp2_tstamp ts;
+ /* last_ts is the most recent time point that this object is
+ told. */
+ ngtcp2_tstamp last_ts;
+ /* user_data is user-defined opaque data which is passed to
+ log_pritnf. */
+ void *user_data;
+ /* scid is SCID encoded as NULL-terminated hex string. */
+ uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1];
+} ngtcp2_log;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_log_event` defines an event of ngtcp2 library
+ * internal logger.
+ */
+typedef enum ngtcp2_log_event {
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event.
+ */
+ NGTCP2_LOG_EVENT_NONE,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event
+ */
+ NGTCP2_LOG_EVENT_CON,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event.
+ */
+ NGTCP2_LOG_EVENT_PKT,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event.
+ */
+ NGTCP2_LOG_EVENT_FRM,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event.
+ */
+ NGTCP2_LOG_EVENT_RCV,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event.
+ */
+ NGTCP2_LOG_EVENT_CRY,
+ /**
+ * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event.
+ */
+ NGTCP2_LOG_EVENT_PTV,
+} ngtcp2_log_event;
+
+void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
+ ngtcp2_printf log_printf, ngtcp2_tstamp ts,
+ void *user_data);
+
+void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr);
+void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_frame *fr);
+
+void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv);
+
+void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr);
+
+void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
+ const ngtcp2_transport_params *params);
+
+void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
+ uint8_t flags, ngtcp2_tstamp sent_ts);
+
+void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+
+void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+
+void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd);
+
+/**
+ * @function
+ *
+ * `ngtcp2_log_info` writes info level log.
+ */
+void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
+ ...);
+
+#endif /* NGTCP2_LOG_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h
new file mode 100644
index 0000000..28d3461
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_macro.h
@@ -0,0 +1,58 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MACRO_H
+#define NGTCP2_MACRO_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <stddef.h>
+
+#include <ngtcp2/ngtcp2.h>
+
+#define ngtcp2_min(A, B) ((A) < (B) ? (A) : (B))
+#define ngtcp2_max(A, B) ((A) > (B) ? (A) : (B))
+
+#define ngtcp2_struct_of(ptr, type, member) \
+ ((type *)(void *)((char *)(ptr)-offsetof(type, member)))
+
+/* ngtcp2_list_insert inserts |T| before |*PD|. The contract is that
+ this is singly linked list, and the next element is pointed by next
+ field of the previous element. |PD| must be a pointer to the
+ pointer to the next field of the previous element of |*PD|: if C is
+ the previous element of |PD|, PD = &C->next. */
+#define ngtcp2_list_insert(T, PD) \
+ do { \
+ (T)->next = *(PD); \
+ *(PD) = (T); \
+ } while (0)
+
+/*
+ * ngtcp2_arraylen returns the number of elements in array |A|.
+ */
+#define ngtcp2_arraylen(A) (sizeof(A) / sizeof(A[0]))
+
+#endif /* NGTCP2_MACRO_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
new file mode 100644
index 0000000..12bc6e8
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
@@ -0,0 +1,336 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_map.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "ngtcp2_conv.h"
+
+#define NGTCP2_INITIAL_TABLE_LENBITS 4
+
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
+ map->mem = mem;
+ map->tablelen = 0;
+ map->tablelenbits = 0;
+ map->table = NULL;
+ map->size = 0;
+}
+
+void ngtcp2_map_free(ngtcp2_map *map) {
+ if (!map) {
+ return;
+ }
+
+ ngtcp2_mem_free(map->mem, map->table);
+}
+
+void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ uint32_t i;
+ ngtcp2_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ func(bkt->data, ptr);
+ }
+}
+
+int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
+ void *ptr) {
+ int rv;
+ uint32_t i;
+ ngtcp2_map_bucket *bkt;
+
+ if (map->size == 0) {
+ return 0;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ continue;
+ }
+
+ rv = func(bkt->data, ptr);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+static uint32_t hash(ngtcp2_map_key_type key) {
+ return (uint32_t)((key * 11400714819323198485llu) >> 32);
+}
+
+static size_t h2idx(uint32_t hash, uint32_t bits) {
+ return hash >> (32 - bits);
+}
+
+static size_t distance(uint32_t tablelen, uint32_t tablelenbits,
+ ngtcp2_map_bucket *bkt, size_t idx) {
+ return (idx - h2idx(bkt->hash, tablelenbits)) & (tablelen - 1);
+}
+
+static void map_bucket_swap(ngtcp2_map_bucket *bkt, uint32_t *phash,
+ ngtcp2_map_key_type *pkey, void **pdata) {
+ uint32_t h = bkt->hash;
+ ngtcp2_map_key_type key = bkt->key;
+ void *data = bkt->data;
+
+ bkt->hash = *phash;
+ bkt->key = *pkey;
+ bkt->data = *pdata;
+
+ *phash = h;
+ *pkey = key;
+ *pdata = data;
+}
+
+static void map_bucket_set_data(ngtcp2_map_bucket *bkt, uint32_t hash,
+ ngtcp2_map_key_type key, void *data) {
+ bkt->hash = hash;
+ bkt->key = key;
+ bkt->data = data;
+}
+
+void ngtcp2_map_print_distance(ngtcp2_map *map) {
+ uint32_t i;
+ size_t idx;
+ ngtcp2_map_bucket *bkt;
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+
+ if (bkt->data == NULL) {
+ fprintf(stderr, "@%u <EMPTY>\n", i);
+ continue;
+ }
+
+ idx = h2idx(bkt->hash, map->tablelenbits);
+ fprintf(stderr, "@%u hash=%08x key=%" PRIu64 " base=%zu distance=%zu\n", i,
+ bkt->hash, bkt->key, idx,
+ distance(map->tablelen, map->tablelenbits, bkt, idx));
+ }
+}
+
+static int insert(ngtcp2_map_bucket *table, uint32_t tablelen,
+ uint32_t tablelenbits, uint32_t hash, ngtcp2_map_key_type key,
+ void *data) {
+ size_t idx = h2idx(hash, tablelenbits);
+ size_t d = 0, dd;
+ ngtcp2_map_bucket *bkt;
+
+ for (;;) {
+ bkt = &table[idx];
+
+ if (bkt->data == NULL) {
+ map_bucket_set_data(bkt, hash, key, data);
+ return 0;
+ }
+
+ dd = distance(tablelen, tablelenbits, bkt, idx);
+ if (d > dd) {
+ map_bucket_swap(bkt, &hash, &key, &data);
+ d = dd;
+ } else if (bkt->key == key) {
+ /* TODO This check is just a waste after first swap or if this
+ function is called from map_resize. That said, there is no
+ difference with or without this conditional in performance
+ wise. */
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ ++d;
+ idx = (idx + 1) & (tablelen - 1);
+ }
+}
+
+/* new_tablelen must be power of 2 and new_tablelen == (1 <<
+ new_tablelenbits) must hold. */
+static int map_resize(ngtcp2_map *map, uint32_t new_tablelen,
+ uint32_t new_tablelenbits) {
+ uint32_t i;
+ ngtcp2_map_bucket *new_table;
+ ngtcp2_map_bucket *bkt;
+ int rv;
+ (void)rv;
+
+ new_table =
+ ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket));
+ if (new_table == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ for (i = 0; i < map->tablelen; ++i) {
+ bkt = &map->table[i];
+ if (bkt->data == NULL) {
+ continue;
+ }
+ rv = insert(new_table, new_tablelen, new_tablelenbits, bkt->hash, bkt->key,
+ bkt->data);
+
+ assert(0 == rv);
+ }
+
+ ngtcp2_mem_free(map->mem, map->table);
+ map->tablelen = new_tablelen;
+ map->tablelenbits = new_tablelenbits;
+ map->table = new_table;
+
+ return 0;
+}
+
+int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
+ int rv;
+
+ assert(data);
+
+ /* Load factor is 0.75 */
+ if ((map->size + 1) * 4 > map->tablelen * 3) {
+ if (map->tablelen) {
+ rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+ if (rv != 0) {
+ return rv;
+ }
+ } else {
+ rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS,
+ NGTCP2_INITIAL_TABLE_LENBITS);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ rv = insert(map->table, map->tablelen, map->tablelenbits, hash(key), key,
+ data);
+ if (rv != 0) {
+ return rv;
+ }
+ ++map->size;
+ return 0;
+}
+
+void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) {
+ uint32_t h;
+ size_t idx;
+ ngtcp2_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NULL;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NULL;
+ }
+
+ if (bkt->key == key) {
+ return bkt->data;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
+ uint32_t h;
+ size_t idx, didx;
+ ngtcp2_map_bucket *bkt;
+ size_t d = 0;
+
+ if (map->size == 0) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ h = hash(key);
+ idx = h2idx(h, map->tablelenbits);
+
+ for (;;) {
+ bkt = &map->table[idx];
+
+ if (bkt->data == NULL ||
+ d > distance(map->tablelen, map->tablelenbits, bkt, idx)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (bkt->key == key) {
+ map_bucket_set_data(bkt, 0, 0, NULL);
+
+ didx = idx;
+ idx = (idx + 1) & (map->tablelen - 1);
+
+ for (;;) {
+ bkt = &map->table[idx];
+ if (bkt->data == NULL ||
+ distance(map->tablelen, map->tablelenbits, bkt, idx) == 0) {
+ break;
+ }
+
+ map->table[didx] = *bkt;
+ map_bucket_set_data(bkt, 0, 0, NULL);
+ didx = idx;
+
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+
+ --map->size;
+
+ return 0;
+ }
+
+ ++d;
+ idx = (idx + 1) & (map->tablelen - 1);
+ }
+}
+
+void ngtcp2_map_clear(ngtcp2_map *map) {
+ if (map->tablelen == 0) {
+ return;
+ }
+
+ memset(map->table, 0, sizeof(*map->table) * map->tablelen);
+ map->size = 0;
+}
+
+size_t ngtcp2_map_size(ngtcp2_map *map) { return map->size; }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h
new file mode 100644
index 0000000..a64344a
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h
@@ -0,0 +1,136 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MAP_H
+#define NGTCP2_MAP_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/* Implementation of unordered map */
+
+typedef uint64_t ngtcp2_map_key_type;
+
+typedef struct ngtcp2_map_bucket {
+ uint32_t hash;
+ ngtcp2_map_key_type key;
+ void *data;
+} ngtcp2_map_bucket;
+
+typedef struct ngtcp2_map {
+ ngtcp2_map_bucket *table;
+ const ngtcp2_mem *mem;
+ size_t size;
+ uint32_t tablelen;
+ uint32_t tablelenbits;
+} ngtcp2_map;
+
+/*
+ * Initializes the map |map|.
+ */
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |map|. The stored entries
+ * are not freed by this function. Use ngtcp2_map_each_free() to free
+ * each entries.
+ */
+void ngtcp2_map_free(ngtcp2_map *map);
+
+/*
+ * Deallocates each entries using |func| function and any resources
+ * allocated for |map|. The |func| function is responsible for freeing
+ * given the |data| object. The |ptr| will be passed to the |func| as
+ * send argument. The return value of the |func| will be ignored.
+ */
+void ngtcp2_map_each_free(ngtcp2_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+/*
+ * Inserts the new |data| with the |key| to the map |map|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * The item associated by |key| already exists.
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data);
+
+/*
+ * Returns the data associated by the key |key|. If there is no such
+ * data, this function returns NULL.
+ */
+void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key);
+
+/*
+ * Removes the data associated by the key |key| from the |map|. The
+ * removed data is not freed by this function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * The data associated by |key| does not exist.
+ */
+int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key);
+
+/*
+ * Removes all entries from |map|.
+ */
+void ngtcp2_map_clear(ngtcp2_map *map);
+
+/*
+ * Returns the number of items stored in the map |map|.
+ */
+size_t ngtcp2_map_size(ngtcp2_map *map);
+
+/*
+ * Applies the function |func| to each data in the |map| with the
+ * optional user supplied pointer |ptr|.
+ *
+ * If the |func| returns 0, this function calls the |func| with the
+ * next data. If the |func| returns nonzero, it will not call the
+ * |func| for further entries and return the return value of the
+ * |func| immediately. Thus, this function returns 0 if all the
+ * invocations of the |func| return 0, or nonzero value which the last
+ * invocation of |func| returns.
+ *
+ * Don't use this function to free each data. Use
+ * ngtcp2_map_each_free() instead.
+ */
+int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
+ void *ptr);
+
+void ngtcp2_map_print_distance(ngtcp2_map *map);
+
+#endif /* NGTCP2_MAP_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
new file mode 100644
index 0000000..bcce0b5
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
@@ -0,0 +1,113 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_mem.h"
+
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+ (void)user_data;
+
+ return malloc(size);
+}
+
+static void default_free(void *ptr, void *user_data) {
+ (void)user_data;
+
+ free(ptr);
+}
+
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+ (void)user_data;
+
+ return calloc(nmemb, size);
+}
+
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+ (void)user_data;
+
+ return realloc(ptr, size);
+}
+
+static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free,
+ default_calloc, default_realloc};
+
+const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; }
+
+#ifndef MEMDEBUG
+void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) {
+ return mem->malloc(size, mem->user_data);
+}
+
+void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) {
+ mem->free(ptr, mem->user_data);
+}
+
+void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) {
+ return mem->calloc(nmemb, size, mem->user_data);
+}
+
+void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) {
+ return mem->realloc(ptr, size, mem->user_data);
+}
+#else /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+ const char *func, const char *file, size_t line) {
+ void *nptr = mem->malloc(size, mem->user_data);
+
+ fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+ file, line);
+
+ return nptr;
+}
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line) {
+ fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+ mem->free(ptr, mem->user_data);
+}
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+ const char *func, const char *file, size_t line) {
+ void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+ fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+ size, func, file, line);
+
+ return nptr;
+}
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file,
+ size_t line) {
+ void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+ fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+ size, func, file, line);
+
+ return nptr;
+}
+#endif /* MEMDEBUG */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h
new file mode 100644
index 0000000..c99b6c5
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h
@@ -0,0 +1,72 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2014 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_MEM_H
+#define NGTCP2_MEM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* Convenient wrapper functions to call allocator function in
+ |mem|. */
+#ifndef MEMDEBUG
+void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size);
+
+void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr);
+
+void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size);
+
+void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_malloc(MEM, SIZE) \
+ ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+ const char *file, size_t line);
+
+# define ngtcp2_mem_free(MEM, PTR) \
+ ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_calloc(MEM, NMEMB, SIZE) \
+ ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__, \
+ __LINE__)
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+ const char *func, const char *file, size_t line);
+
+# define ngtcp2_mem_realloc(MEM, PTR, SIZE) \
+ ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__)
+#endif /* MEMDEBUG */
+
+#endif /* NGTCP2_MEM_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h
new file mode 100644
index 0000000..cd73d2b
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h
@@ -0,0 +1,136 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_NET_H
+#define NGTCP2_NET_H
+
+/* This header file is explicitly allowed to be shared with
+ ngtcp2_crypto library. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_BYTESWAP_H
+# include <byteswap.h>
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_ENDIAN_H
+# include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+# include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#if defined(HAVE_BSWAP_64) || \
+ (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+# define ngtcp2_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+# define ngtcp2_bswap64(N) \
+ ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \
+ ngtcp2_ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) || \
+ (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+# define ngtcp2_ntohl64(N) be64toh(N)
+# define ngtcp2_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+# if defined(WORDS_BIGENDIAN)
+# define ngtcp2_ntohl64(N) (N)
+# define ngtcp2_htonl64(N) (N)
+# else /* !WORDS_BIGENDIAN */
+# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
+# define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
+# endif /* !WORDS_BIGENDIAN */
+#endif /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family functions. We
+ define inline functions for those function so that we don't have
+ dependeny on that lib. */
+
+# ifdef _MSC_VER
+# define STIN static __inline
+# else
+# define STIN static inline
+# endif
+
+STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostlong >> 24);
+ *p++ = (hostlong >> 16) & 0xffu;
+ *p++ = (hostlong >> 8) & 0xffu;
+ *p = hostlong & 0xffu;
+ return res;
+}
+
+STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&res;
+ *p++ = (unsigned char)(hostshort >> 8);
+ *p = hostshort & 0xffu;
+ return res;
+}
+
+STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
+ uint32_t res;
+ unsigned char *p = (unsigned char *)&netlong;
+ res = (uint32_t)(*p++ << 24);
+ res += (uint32_t)(*p++ << 16);
+ res += (uint32_t)(*p++ << 8);
+ res += *p;
+ return res;
+}
+
+STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
+ uint16_t res;
+ unsigned char *p = (unsigned char *)&netshort;
+ res = (uint16_t)(*p++ << 8);
+ res += *p;
+ return res;
+}
+
+#else /* !WIN32 */
+
+# define ngtcp2_htonl htonl
+# define ngtcp2_htons htons
+# define ngtcp2_ntohl ntohl
+# define ngtcp2_ntohs ntohs
+
+#endif /* !WIN32 */
+
+#endif /* NGTCP2_NET_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
new file mode 100644
index 0000000..8b06cdd
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
@@ -0,0 +1,40 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_objalloc.h"
+
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+ const ngtcp2_mem *mem) {
+ ngtcp2_balloc_init(&objalloc->balloc, blklen, mem);
+ ngtcp2_opl_init(&objalloc->opl);
+}
+
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) {
+ ngtcp2_balloc_free(&objalloc->balloc);
+}
+
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) {
+ ngtcp2_opl_clear(&objalloc->opl);
+ ngtcp2_balloc_clear(&objalloc->balloc);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
new file mode 100644
index 0000000..f1bbd3a
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
@@ -0,0 +1,140 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OBJALLOC_H
+#define NGTCP2_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_balloc.h"
+#include "ngtcp2_opl.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+
+/*
+ * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides
+ * an object pool with the custom allocator to reduce the allocation
+ * and deallocation overheads for small objects.
+ */
+typedef struct ngtcp2_objalloc {
+ ngtcp2_balloc balloc;
+ ngtcp2_opl opl;
+} ngtcp2_objalloc;
+
+/*
+ * ngtcp2_objalloc_init initializes |objalloc|. |blklen| is directly
+ * passed to ngtcp2_balloc_init.
+ */
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_objalloc_free releases all allocated resources.
+ */
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void ngtcp2_objalloc_##NAME##_init( \
+ ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
+ ngtcp2_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
+ ngtcp2_objalloc *objalloc) { \
+ ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = \
+ ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
+ ngtcp2_objalloc *objalloc, size_t len) { \
+ ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl); \
+ TYPE *obj; \
+ int rv; \
+ \
+ if (!oplent) { \
+ rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len); \
+ if (rv != 0) { \
+ return NULL; \
+ } \
+ \
+ return obj; \
+ } \
+ \
+ return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD); \
+ } \
+ \
+ inline static void ngtcp2_objalloc_##NAME##_release( \
+ ngtcp2_objalloc *objalloc, TYPE *obj) { \
+ ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD); \
+ }
+#else /* NOMEMPOOL */
+# define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD) \
+ inline static void ngtcp2_objalloc_##NAME##_init( \
+ ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) { \
+ ngtcp2_objalloc_init( \
+ objalloc, ((sizeof(TYPE) + 0xfu) & ~(uintptr_t)0xfu) * nmemb, mem); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_get( \
+ ngtcp2_objalloc *objalloc) { \
+ return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE)); \
+ } \
+ \
+ inline static TYPE *ngtcp2_objalloc_##NAME##_len_get( \
+ ngtcp2_objalloc *objalloc, size_t len) { \
+ return ngtcp2_mem_malloc(objalloc->balloc.mem, len); \
+ } \
+ \
+ inline static void ngtcp2_objalloc_##NAME##_release( \
+ ngtcp2_objalloc *objalloc, TYPE *obj) { \
+ ngtcp2_mem_free(objalloc->balloc.mem, obj); \
+ }
+#endif /* NOMEMPOOL */
+
+#endif /* NGTCP2_OBJALLOC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
new file mode 100644
index 0000000..a29361c
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
@@ -0,0 +1,46 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_opl.h"
+
+void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; }
+
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) {
+ ent->next = opl->head;
+ opl->head = ent;
+}
+
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) {
+ ngtcp2_opl_entry *ent = opl->head;
+
+ if (!ent) {
+ return NULL;
+ }
+
+ opl->head = ent->next;
+
+ return ent;
+}
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h
new file mode 100644
index 0000000..714aa36
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h
@@ -0,0 +1,65 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OPL_H
+#define NGTCP2_OPL_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_opl_entry ngtcp2_opl_entry;
+
+struct ngtcp2_opl_entry {
+ ngtcp2_opl_entry *next;
+};
+
+/*
+ * ngtcp2_opl is an object memory pool.
+ */
+typedef struct ngtcp2_opl {
+ ngtcp2_opl_entry *head;
+} ngtcp2_opl;
+
+/*
+ * ngtcp2_opl_init initializes |opl|.
+ */
+void ngtcp2_opl_init(ngtcp2_opl *opl);
+
+/*
+ * ngtcp2_opl_push inserts |ent| to |opl| head.
+ */
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent);
+
+/*
+ * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and
+ * returns it. If |opl| does not have any entry, it returns NULL.
+ */
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl);
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl);
+
+#endif /* NGTCP2_OPL_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
new file mode 100644
index 0000000..8323873
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
@@ -0,0 +1,77 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_path.h"
+
+#include <string.h>
+
+#include "ngtcp2_addr.h"
+
+void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
+ const ngtcp2_addr *remote) {
+ path->local = *local;
+ path->remote = *remote;
+}
+
+void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) {
+ ngtcp2_addr_copy(&dest->local, &src->local);
+ ngtcp2_addr_copy(&dest->remote, &src->remote);
+ dest->user_data = src->user_data;
+}
+
+int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
+ return ngtcp2_addr_eq(&a->local, &b->local) &&
+ ngtcp2_addr_eq(&a->remote, &b->remote);
+}
+
+void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
+ const ngtcp2_sockaddr *local_addr,
+ ngtcp2_socklen local_addrlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, void *user_data) {
+ ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
+ 0);
+ ngtcp2_addr_init(&ps->path.remote,
+ (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
+
+ ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen);
+ ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen);
+
+ ps->path.user_data = user_data;
+}
+
+void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
+ const ngtcp2_path *path) {
+ ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen,
+ path->remote.addr, path->remote.addrlen,
+ path->user_data);
+}
+
+void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) {
+ ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
+ 0);
+ ngtcp2_addr_init(&ps->path.remote,
+ (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
+ ps->path.user_data = NULL;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h
new file mode 100644
index 0000000..0c360e9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h
@@ -0,0 +1,49 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PATH_H
+#define NGTCP2_PATH_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_path_init initializes |path| with the given addresses. Note
+ * that the buffer pointed by local->addr and remote->addr are not
+ * copied. Their pointer values are assigned instead.
+ */
+void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local,
+ const ngtcp2_addr *remote);
+
+/*
+ * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial
+ * data.
+ */
+void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
+ const ngtcp2_path *path);
+
+#endif /* NGTCP2_PATH_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
new file mode 100644
index 0000000..7ca63b3
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
@@ -0,0 +1,2527 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pkt.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_conv.h"
+#include "ngtcp2_str.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_cid.h"
+#include "ngtcp2_mem.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_unreachable.h"
+#include "ngtcp2_str.h"
+
+int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts,
+ const ngtcp2_mem *mem) {
+ *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen);
+ if (*ppc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_path_storage_init2(&(*ppc)->path, path);
+ (*ppc)->pi = *pi;
+ (*ppc)->next = NULL;
+ (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain);
+ (*ppc)->pktlen = pktlen;
+ (*ppc)->dgramlen = dgramlen;
+ (*ppc)->ts = ts;
+
+ memcpy((*ppc)->pkt, pkt, pktlen);
+
+ return 0;
+}
+
+void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, pc);
+}
+
+int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data,
+ size_t datalen, size_t short_dcidlen) {
+ size_t len;
+ uint32_t version;
+ size_t dcidlen, scidlen;
+ int supported_version;
+
+ assert(datalen);
+
+ if (data[0] & NGTCP2_HEADER_FORM_BIT) {
+ /* 1 byte (Header Form, Fixed Bit, Long Packet Type, Type-Specific bits)
+ * 4 bytes Version
+ * 1 byte DCID Length
+ * 1 byte SCID Length
+ */
+ len = 1 + 4 + 1 + 1;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dcidlen = data[5];
+ len += dcidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ scidlen = data[5 + 1 + dcidlen];
+ len += scidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ ngtcp2_get_uint32(&version, &data[1]);
+
+ supported_version = ngtcp2_is_supported_version(version);
+
+ if (supported_version &&
+ (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (version && !supported_version &&
+ datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->version = version;
+ dest->dcid = &data[6];
+ dest->dcidlen = dcidlen;
+ dest->scid = &data[6 + dcidlen + 1];
+ dest->scidlen = scidlen;
+
+ if (!version) {
+ /* VN */
+ return 0;
+ }
+
+ if (!supported_version) {
+ return NGTCP2_ERR_VERSION_NEGOTIATION;
+ }
+ return 0;
+ }
+
+ assert(short_dcidlen <= NGTCP2_MAX_CIDLEN);
+
+ len = 1 + short_dcidlen;
+ if (datalen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->version = 0;
+ dest->dcid = &data[1];
+ dest->dcidlen = short_dcidlen;
+ dest->scid = NULL;
+ dest->scidlen = 0;
+
+ return 0;
+}
+
+void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ int64_t pkt_num, size_t pkt_numlen, uint32_t version,
+ size_t len) {
+ hd->flags = flags;
+ hd->type = type;
+ if (dcid) {
+ hd->dcid = *dcid;
+ } else {
+ ngtcp2_cid_zero(&hd->dcid);
+ }
+ if (scid) {
+ hd->scid = *scid;
+ } else {
+ ngtcp2_cid_zero(&hd->scid);
+ }
+ hd->pkt_num = pkt_num;
+ hd->token = NULL;
+ hd->tokenlen = 0;
+ hd->pkt_numlen = pkt_numlen;
+ hd->version = version;
+ hd->len = len;
+}
+
+static int has_mask(uint8_t b, uint8_t mask) { return (b & mask) == mask; }
+
+ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen) {
+ uint8_t type;
+ uint32_t version;
+ size_t dcil, scil;
+ const uint8_t *p;
+ size_t len = 0;
+ size_t n;
+ size_t ntokenlen = 0;
+ const uint8_t *token = NULL;
+ size_t tokenlen = 0;
+ uint64_t vi;
+ uint8_t flags = NGTCP2_PKT_FLAG_LONG_FORM;
+
+ if (pktlen < 5) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ ngtcp2_get_uint32(&version, &pkt[1]);
+
+ if (version == 0) {
+ type = NGTCP2_PKT_VERSION_NEGOTIATION;
+ /* Version Negotiation is not a long header packet. */
+ flags = NGTCP2_PKT_FLAG_NONE;
+ /* This must be Version Negotiation packet which lacks packet
+ number and payload length fields. */
+ len = 5 + 2;
+ } else {
+ if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
+ flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
+ }
+
+ type = ngtcp2_pkt_get_type_long(version, pkt[0]);
+ switch (type) {
+ case 0:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ case NGTCP2_PKT_INITIAL:
+ len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN -
+ 1; /* Cut packet number field */
+ break;
+ case NGTCP2_PKT_RETRY:
+ /* Retry packet does not have packet number and length fields */
+ len = 5 + 2;
+ break;
+ case NGTCP2_PKT_HANDSHAKE:
+ case NGTCP2_PKT_0RTT:
+ len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */
+ break;
+ default:
+ /* Unreachable */
+ ngtcp2_unreachable();
+ }
+ }
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = &pkt[5];
+ dcil = *p;
+ if (dcil > NGTCP2_MAX_CIDLEN) {
+ /* QUIC v1 implementation never expect to receive CID length more
+ than NGTCP2_MAX_CIDLEN. */
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ len += dcil;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p += 1 + dcil;
+ scil = *p;
+ if (scil > NGTCP2_MAX_CIDLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ len += scil;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p += 1 + scil;
+
+ if (type == NGTCP2_PKT_INITIAL) {
+ /* Token Length */
+ ntokenlen = ngtcp2_get_uvarintlen(p);
+ len += ntokenlen - 1;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (pktlen - len < vi) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ tokenlen = (size_t)vi;
+ len += tokenlen;
+
+ if (tokenlen) {
+ token = p;
+ }
+
+ p += tokenlen;
+ }
+
+ switch (type) {
+ case NGTCP2_PKT_RETRY:
+ break;
+ default:
+ if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+ assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+ /* Version Negotiation is not a long header packet. */
+ break;
+ }
+
+ /* Length */
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ }
+
+ dest->flags = flags;
+ dest->type = type;
+ dest->version = version;
+ dest->pkt_num = 0;
+ dest->pkt_numlen = 0;
+
+ p = &pkt[6];
+ ngtcp2_cid_init(&dest->dcid, p, dcil);
+ p += dcil + 1;
+ ngtcp2_cid_init(&dest->scid, p, scil);
+ p += scil;
+
+ dest->token = token;
+ dest->tokenlen = tokenlen;
+ p += ntokenlen + tokenlen;
+
+ switch (type) {
+ case NGTCP2_PKT_RETRY:
+ dest->len = 0;
+ break;
+ default:
+ if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+ assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+ /* Version Negotiation is not a long header packet. */
+ dest->len = 0;
+ break;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (vi > SIZE_MAX) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+ dest->len = (size_t)vi;
+ }
+
+ assert((size_t)(p - pkt) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen, size_t dcidlen) {
+ size_t len = 1 + dcidlen;
+ const uint8_t *p = pkt;
+ uint8_t flags = NGTCP2_PKT_FLAG_NONE;
+
+ assert(dcidlen <= NGTCP2_MAX_CIDLEN);
+
+ if (pktlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) {
+ flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
+ }
+
+ p = &pkt[1];
+
+ dest->type = NGTCP2_PKT_1RTT;
+
+ ngtcp2_cid_init(&dest->dcid, p, dcidlen);
+ p += dcidlen;
+
+ /* Set 0 to SCID so that we don't accidentally reference it and gets
+ garbage. */
+ ngtcp2_cid_zero(&dest->scid);
+
+ dest->flags = flags;
+ dest->version = 0;
+ dest->len = 0;
+ dest->pkt_num = 0;
+ dest->pkt_numlen = 0;
+ dest->token = NULL;
+ dest->tokenlen = 0;
+
+ assert((size_t)(p - pkt) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd) {
+ uint8_t *p;
+ size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen -
+ 2; /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for
+ len and 1 byte for packet number. */
+
+ if (hd->type != NGTCP2_PKT_RETRY) {
+ len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen;
+ }
+
+ if (hd->type == NGTCP2_PKT_INITIAL) {
+ len += ngtcp2_put_uvarintlen(hd->tokenlen) + hd->tokenlen;
+ }
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT |
+ (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) |
+ (uint8_t)(hd->pkt_numlen - 1));
+ if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
+ *p |= NGTCP2_FIXED_BIT_MASK;
+ }
+
+ ++p;
+
+ p = ngtcp2_put_uint32be(p, hd->version);
+ *p++ = (uint8_t)hd->dcid.datalen;
+ if (hd->dcid.datalen) {
+ p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen);
+ }
+ *p++ = (uint8_t)hd->scid.datalen;
+ if (hd->scid.datalen) {
+ p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen);
+ }
+
+ if (hd->type == NGTCP2_PKT_INITIAL) {
+ p = ngtcp2_put_uvarint(p, hd->tokenlen);
+ if (hd->tokenlen) {
+ p = ngtcp2_cpymem(p, hd->token, hd->tokenlen);
+ }
+ }
+
+ if (hd->type != NGTCP2_PKT_RETRY) {
+ p = ngtcp2_put_uvarint30(p, (uint32_t)hd->len);
+ p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd) {
+ uint8_t *p;
+ size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p = (uint8_t)(hd->pkt_numlen - 1);
+ if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
+ *p |= NGTCP2_FIXED_BIT_MASK;
+ }
+ if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) {
+ *p |= NGTCP2_SHORT_KEY_PHASE_BIT;
+ }
+
+ ++p;
+
+ if (hd->dcid.datalen) {
+ p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen);
+ }
+
+ p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
+ size_t payloadlen) {
+ uint8_t type;
+
+ if (payloadlen == 0) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ switch (type) {
+ case NGTCP2_FRAME_PADDING:
+ return ngtcp2_pkt_decode_padding_frame(&dest->padding, payload, payloadlen);
+ case NGTCP2_FRAME_RESET_STREAM:
+ return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload,
+ payloadlen);
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close,
+ payload, payloadlen);
+ case NGTCP2_FRAME_MAX_DATA:
+ return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload,
+ payloadlen);
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data,
+ payload, payloadlen);
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload,
+ payloadlen);
+ case NGTCP2_FRAME_PING:
+ return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen);
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload,
+ payloadlen);
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ return ngtcp2_pkt_decode_stream_data_blocked_frame(
+ &dest->stream_data_blocked, payload, payloadlen);
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked,
+ payload, payloadlen);
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id,
+ payload, payloadlen);
+ case NGTCP2_FRAME_STOP_SENDING:
+ return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload,
+ payloadlen);
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen);
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge,
+ payload, payloadlen);
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload,
+ payloadlen);
+ case NGTCP2_FRAME_CRYPTO:
+ return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen);
+ case NGTCP2_FRAME_NEW_TOKEN:
+ return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload,
+ payloadlen);
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ return ngtcp2_pkt_decode_retire_connection_id_frame(
+ &dest->retire_connection_id, payload, payloadlen);
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done,
+ payload, payloadlen);
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ return ngtcp2_pkt_decode_datagram_frame(&dest->datagram, payload,
+ payloadlen);
+ default:
+ if (has_mask(type, NGTCP2_FRAME_STREAM)) {
+ return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen);
+ }
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ uint8_t type;
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t datalen;
+ size_t ndatalen = 0;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ if (type & NGTCP2_STREAM_OFF_BIT) {
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+
+ if (type & NGTCP2_STREAM_LEN_BIT) {
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ ndatalen = ngtcp2_get_uvarintlen(p);
+ len += ndatalen - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ /* p = */ ngtcp2_get_uvarint(&vi, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ datalen = (size_t)vi;
+ len += datalen;
+ } else {
+ len = payloadlen;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STREAM;
+ dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM);
+ dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0;
+ p = ngtcp2_get_varint(&dest->stream_id, p);
+
+ if (type & NGTCP2_STREAM_OFF_BIT) {
+ p = ngtcp2_get_uvarint(&dest->offset, p);
+ } else {
+ dest->offset = 0;
+ }
+
+ if (type & NGTCP2_STREAM_LEN_BIT) {
+ p += ndatalen;
+ } else {
+ datalen = payloadlen - (size_t)(p - payload);
+ }
+
+ if (datalen) {
+ dest->data[0].len = datalen;
+ dest->data[0].base = (uint8_t *)p;
+ dest->datacnt = 1;
+ p += datalen;
+ } else {
+ dest->datacnt = 0;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t rangecnt, max_rangecnt;
+ size_t nrangecnt;
+ size_t len = 1 + 1 + 1 + 1 + 1;
+ const uint8_t *p;
+ size_t i, j;
+ ngtcp2_ack_range *range;
+ size_t n;
+ uint8_t type;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ /* Largest Acknowledged */
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ /* ACK Delay */
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ /* ACK Range Count */
+ nrangecnt = ngtcp2_get_uvarintlen(p);
+ len += nrangecnt - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (vi > SIZE_MAX / (1 + 1) || payloadlen - len < vi * (1 + 1)) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ rangecnt = (size_t)vi;
+ len += rangecnt * (1 + 1);
+
+ /* First ACK Range */
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ for (i = 0; i < rangecnt; ++i) {
+ /* Gap, and Additional ACK Range */
+ for (j = 0; j < 2; ++j) {
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+ }
+
+ if (type == NGTCP2_FRAME_ACK_ECN) {
+ len += 3;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ for (i = 0; i < 3; ++i) {
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+ }
+
+ /* TODO We might not decode all ranges. It could be very large. */
+ max_rangecnt = ngtcp2_min(NGTCP2_MAX_ACK_RANGES, rangecnt);
+
+ p = payload + 1;
+
+ dest->type = type;
+ p = ngtcp2_get_varint(&dest->largest_ack, p);
+ p = ngtcp2_get_uvarint(&dest->ack_delay, p);
+ /* This value will be assigned in the upper layer. */
+ dest->ack_delay_unscaled = 0;
+ dest->rangecnt = max_rangecnt;
+ p += nrangecnt;
+ p = ngtcp2_get_uvarint(&dest->first_ack_range, p);
+
+ for (i = 0; i < max_rangecnt; ++i) {
+ range = &dest->ranges[i];
+ p = ngtcp2_get_uvarint(&range->gap, p);
+ p = ngtcp2_get_uvarint(&range->len, p);
+ }
+ for (i = max_rangecnt; i < rangecnt; ++i) {
+ p += ngtcp2_get_uvarintlen(p);
+ p += ngtcp2_get_uvarintlen(p);
+ }
+
+ if (type == NGTCP2_FRAME_ACK_ECN) {
+ p = ngtcp2_get_uvarint(&dest->ecn.ect0, p);
+ p = ngtcp2_get_uvarint(&dest->ecn.ect1, p);
+ p = ngtcp2_get_uvarint(&dest->ecn.ce, p);
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *p, *ep;
+
+ assert(payloadlen > 0);
+
+ p = payload + 1;
+ ep = payload + payloadlen;
+
+ for (; p != ep && *p == NGTCP2_FRAME_PADDING; ++p)
+ ;
+
+ dest->type = NGTCP2_FRAME_PADDING;
+ dest->len = (size_t)(p - payload);
+
+ return (ngtcp2_ssize)dest->len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_RESET_STREAM;
+ p = ngtcp2_get_varint(&dest->stream_id, p);
+ p = ngtcp2_get_uvarint(&dest->app_error_code, p);
+ p = ngtcp2_get_uvarint(&dest->final_size, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame(
+ ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t reasonlen;
+ size_t nreasonlen;
+ size_t n;
+ uint8_t type;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ if (type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ ++len;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+ }
+
+ nreasonlen = ngtcp2_get_uvarintlen(p);
+ len += nreasonlen - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ reasonlen = (size_t)vi;
+ len += reasonlen;
+
+ p = payload + 1;
+
+ dest->type = type;
+ p = ngtcp2_get_uvarint(&dest->error_code, p);
+ if (type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ p = ngtcp2_get_uvarint(&dest->frame_type, p);
+ } else {
+ dest->frame_type = 0;
+ }
+ dest->reasonlen = reasonlen;
+ p += nreasonlen;
+ if (reasonlen == 0) {
+ dest->reason = NULL;
+ } else {
+ dest->reason = (uint8_t *)p;
+ p += reasonlen;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_MAX_DATA;
+ p = ngtcp2_get_uvarint(&dest->max_data, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame(
+ ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_MAX_STREAM_DATA;
+ p = ngtcp2_get_varint(&dest->stream_id, p);
+ p = ngtcp2_get_uvarint(&dest->max_stream_data, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = payload[0];
+ p = ngtcp2_get_uvarint(&dest->max_streams, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ (void)payload;
+ (void)payloadlen;
+
+ dest->type = NGTCP2_FRAME_PING;
+ return 1;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_DATA_BLOCKED;
+ p = ngtcp2_get_uvarint(&dest->offset, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
+ p = ngtcp2_get_varint(&dest->stream_id, p);
+ p = ngtcp2_get_uvarint(&dest->offset, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame(
+ ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = payload[0];
+ p = ngtcp2_get_uvarint(&dest->max_streams, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame(
+ ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen) {
+ size_t len = 1 + 1 + 1 + 1 + 16;
+ const uint8_t *p;
+ size_t n;
+ size_t cil;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ cil = *p;
+ if (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ len += cil;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ p = ngtcp2_get_uvarint(&dest->seq, p);
+ p = ngtcp2_get_uvarint(&dest->retire_prior_to, p);
+ ++p;
+ ngtcp2_cid_init(&dest->cid, p, cil);
+ p += cil;
+ p = ngtcp2_get_bytes(dest->stateless_reset_token, p,
+ NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ p += n;
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_STOP_SENDING;
+ p = ngtcp2_get_varint(&dest->stream_id, p);
+ p = ngtcp2_get_uvarint(&dest->app_error_code, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 8;
+ const uint8_t *p;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_PATH_CHALLENGE;
+ ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ p += sizeof(dest->data);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 8;
+ const uint8_t *p;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_PATH_RESPONSE;
+ ngtcp2_cpymem(dest->data, p, sizeof(dest->data));
+ p += sizeof(dest->data);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1 + 1;
+ const uint8_t *p;
+ size_t datalen;
+ size_t ndatalen;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p += n;
+
+ ndatalen = ngtcp2_get_uvarintlen(p);
+ len += ndatalen - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ datalen = (size_t)vi;
+ len += datalen;
+
+ p = payload + 1;
+
+ dest->type = NGTCP2_FRAME_CRYPTO;
+ p = ngtcp2_get_uvarint(&dest->offset, p);
+ dest->data[0].len = datalen;
+ p += ndatalen;
+ if (dest->data[0].len) {
+ dest->data[0].base = (uint8_t *)p;
+ p += dest->data[0].len;
+ dest->datacnt = 1;
+ } else {
+ dest->data[0].base = NULL;
+ dest->datacnt = 0;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+ size_t datalen;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+ datalen = (size_t)vi;
+ len += datalen;
+
+ dest->type = NGTCP2_FRAME_NEW_TOKEN;
+ dest->tokenlen = datalen;
+ dest->token = (uint8_t *)p;
+ p += dest->tokenlen;
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1 + 1;
+ const uint8_t *p;
+ size_t n;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = payload + 1;
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+ p = ngtcp2_get_uvarint(&dest->seq, p);
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ (void)payload;
+ (void)payloadlen;
+
+ dest->type = NGTCP2_FRAME_HANDSHAKE_DONE;
+ return 1;
+}
+
+ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = 1;
+ const uint8_t *p;
+ uint8_t type;
+ size_t datalen;
+ size_t n;
+ uint64_t vi;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ type = payload[0];
+
+ p = payload + 1;
+
+ switch (type) {
+ case NGTCP2_FRAME_DATAGRAM:
+ datalen = payloadlen - 1;
+ len = payloadlen;
+ break;
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ ++len;
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ n = ngtcp2_get_uvarintlen(p);
+ len += n - 1;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ p = ngtcp2_get_uvarint(&vi, p);
+ if (payloadlen - len < vi) {
+ return NGTCP2_ERR_FRAME_ENCODING;
+ }
+
+ datalen = (size_t)vi;
+ len += datalen;
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ dest->type = type;
+
+ if (datalen == 0) {
+ dest->datacnt = 0;
+ dest->data = NULL;
+ } else {
+ dest->datacnt = 1;
+ dest->data = dest->rdata;
+ dest->rdata[0].len = datalen;
+
+ dest->rdata[0].base = (uint8_t *)p;
+ p += datalen;
+ }
+
+ assert((size_t)(p - payload) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
+ ngtcp2_frame *fr) {
+ switch (fr->type) {
+ case NGTCP2_FRAME_STREAM:
+ return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream);
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack);
+ case NGTCP2_FRAME_PADDING:
+ return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding);
+ case NGTCP2_FRAME_RESET_STREAM:
+ return ngtcp2_pkt_encode_reset_stream_frame(out, outlen, &fr->reset_stream);
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ return ngtcp2_pkt_encode_connection_close_frame(out, outlen,
+ &fr->connection_close);
+ case NGTCP2_FRAME_MAX_DATA:
+ return ngtcp2_pkt_encode_max_data_frame(out, outlen, &fr->max_data);
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ return ngtcp2_pkt_encode_max_stream_data_frame(out, outlen,
+ &fr->max_stream_data);
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ return ngtcp2_pkt_encode_max_streams_frame(out, outlen, &fr->max_streams);
+ case NGTCP2_FRAME_PING:
+ return ngtcp2_pkt_encode_ping_frame(out, outlen, &fr->ping);
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ return ngtcp2_pkt_encode_data_blocked_frame(out, outlen, &fr->data_blocked);
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ return ngtcp2_pkt_encode_stream_data_blocked_frame(
+ out, outlen, &fr->stream_data_blocked);
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ return ngtcp2_pkt_encode_streams_blocked_frame(out, outlen,
+ &fr->streams_blocked);
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen,
+ &fr->new_connection_id);
+ case NGTCP2_FRAME_STOP_SENDING:
+ return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending);
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ return ngtcp2_pkt_encode_path_challenge_frame(out, outlen,
+ &fr->path_challenge);
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ return ngtcp2_pkt_encode_path_response_frame(out, outlen,
+ &fr->path_response);
+ case NGTCP2_FRAME_CRYPTO:
+ return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto);
+ case NGTCP2_FRAME_NEW_TOKEN:
+ return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token);
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ return ngtcp2_pkt_encode_retire_connection_id_frame(
+ out, outlen, &fr->retire_connection_id);
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ return ngtcp2_pkt_encode_handshake_done_frame(out, outlen,
+ &fr->handshake_done);
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ return ngtcp2_pkt_encode_datagram_frame(out, outlen, &fr->datagram);
+ default:
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen,
+ ngtcp2_stream *fr) {
+ size_t len = 1;
+ uint8_t flags = NGTCP2_STREAM_LEN_BIT;
+ uint8_t *p;
+ size_t i;
+ size_t datalen = 0;
+
+ if (fr->fin) {
+ flags |= NGTCP2_STREAM_FIN_BIT;
+ }
+
+ if (fr->offset) {
+ flags |= NGTCP2_STREAM_OFF_BIT;
+ len += ngtcp2_put_uvarintlen(fr->offset);
+ }
+
+ len += ngtcp2_put_uvarintlen((uint64_t)fr->stream_id);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ datalen += fr->data[i].len;
+ }
+
+ len += ngtcp2_put_uvarintlen(datalen);
+ len += datalen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = flags | NGTCP2_FRAME_STREAM;
+
+ fr->flags = flags;
+
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id);
+
+ if (fr->offset) {
+ p = ngtcp2_put_uvarint(p, fr->offset);
+ }
+
+ p = ngtcp2_put_uvarint(p, datalen);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].len);
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
+ ngtcp2_ack *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->largest_ack) +
+ ngtcp2_put_uvarintlen(fr->ack_delay) +
+ ngtcp2_put_uvarintlen(fr->rangecnt) +
+ ngtcp2_put_uvarintlen(fr->first_ack_range);
+ uint8_t *p;
+ size_t i;
+ const ngtcp2_ack_range *range;
+
+ for (i = 0; i < fr->rangecnt; ++i) {
+ range = &fr->ranges[i];
+ len += ngtcp2_put_uvarintlen(range->gap);
+ len += ngtcp2_put_uvarintlen(range->len);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ len += ngtcp2_put_uvarintlen(fr->ecn.ect0) +
+ ngtcp2_put_uvarintlen(fr->ecn.ect1) +
+ ngtcp2_put_uvarintlen(fr->ecn.ce);
+ }
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->largest_ack);
+ p = ngtcp2_put_uvarint(p, fr->ack_delay);
+ p = ngtcp2_put_uvarint(p, fr->rangecnt);
+ p = ngtcp2_put_uvarint(p, fr->first_ack_range);
+
+ for (i = 0; i < fr->rangecnt; ++i) {
+ range = &fr->ranges[i];
+ p = ngtcp2_put_uvarint(p, range->gap);
+ p = ngtcp2_put_uvarint(p, range->len);
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ p = ngtcp2_put_uvarint(p, fr->ecn.ect0);
+ p = ngtcp2_put_uvarint(p, fr->ecn.ect1);
+ p = ngtcp2_put_uvarint(p, fr->ecn.ce);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_padding *fr) {
+ if (outlen < fr->len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ memset(out, 0, fr->len);
+
+ return (ngtcp2_ssize)fr->len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_reset_stream *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->stream_id) +
+ ngtcp2_put_uvarintlen(fr->app_error_code) +
+ ngtcp2_put_uvarintlen(fr->final_size);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_RESET_STREAM;
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_uvarint(p, fr->app_error_code);
+ p = ngtcp2_put_uvarint(p, fr->final_size);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_connection_close *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->error_code) +
+ (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE
+ ? ngtcp2_put_uvarintlen(fr->frame_type)
+ : 0) +
+ ngtcp2_put_uvarintlen(fr->reasonlen) + fr->reasonlen;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_uvarint(p, fr->error_code);
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ p = ngtcp2_put_uvarint(p, fr->frame_type);
+ }
+ p = ngtcp2_put_uvarint(p, fr->reasonlen);
+ if (fr->reasonlen) {
+ p = ngtcp2_cpymem(p, fr->reason, fr->reasonlen);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_data *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->max_data);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_MAX_DATA;
+ p = ngtcp2_put_uvarint(p, fr->max_data);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_stream_data *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->stream_id) +
+ ngtcp2_put_uvarintlen(fr->max_stream_data);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_MAX_STREAM_DATA;
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_uvarint(p, fr->max_stream_data);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_streams *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->max_streams);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_uvarint(p, fr->max_streams);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_ping *fr) {
+ (void)fr;
+
+ if (outlen < 1) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *out++ = NGTCP2_FRAME_PING;
+
+ return 1;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_data_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->offset);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_DATA_BLOCKED;
+ p = ngtcp2_put_uvarint(p, fr->offset);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->stream_id) +
+ ngtcp2_put_uvarintlen(fr->offset);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED;
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_uvarint(p, fr->offset);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_streams_blocked *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->max_streams);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ p = ngtcp2_put_uvarint(p, fr->max_streams);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_connection_id *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->seq) +
+ ngtcp2_put_uvarintlen(fr->retire_prior_to) + 1 +
+ fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_NEW_CONNECTION_ID;
+ p = ngtcp2_put_uvarint(p, fr->seq);
+ p = ngtcp2_put_uvarint(p, fr->retire_prior_to);
+ *p++ = (uint8_t)fr->cid.datalen;
+ p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen);
+ p = ngtcp2_cpymem(p, fr->stateless_reset_token,
+ NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_stop_sending *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen((uint64_t)fr->stream_id) +
+ ngtcp2_put_uvarintlen(fr->app_error_code);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_STOP_SENDING;
+ p = ngtcp2_put_uvarint(p, (uint64_t)fr->stream_id);
+ p = ngtcp2_put_uvarint(p, fr->app_error_code);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_challenge *fr) {
+ size_t len = 1 + 8;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_PATH_CHALLENGE;
+ p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_response *fr) {
+ size_t len = 1 + 8;
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_PATH_RESPONSE;
+ p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data));
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_crypto *fr) {
+ size_t len = 1;
+ uint8_t *p;
+ size_t i;
+ size_t datalen = 0;
+
+ len += ngtcp2_put_uvarintlen(fr->offset);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ datalen += fr->data[i].len;
+ }
+
+ len += ngtcp2_put_uvarintlen(datalen);
+ len += datalen;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_CRYPTO;
+
+ p = ngtcp2_put_uvarint(p, fr->offset);
+ p = ngtcp2_put_uvarint(p, datalen);
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_token *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->tokenlen) + fr->tokenlen;
+ uint8_t *p;
+
+ assert(fr->tokenlen);
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_NEW_TOKEN;
+
+ p = ngtcp2_put_uvarint(p, fr->tokenlen);
+ p = ngtcp2_cpymem(p, fr->token, fr->tokenlen);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr) {
+ size_t len = 1 + ngtcp2_put_uvarintlen(fr->seq);
+ uint8_t *p;
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID;
+
+ p = ngtcp2_put_uvarint(p, fr->seq);
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_handshake_done *fr) {
+ (void)fr;
+
+ if (outlen < 1) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *out++ = NGTCP2_FRAME_HANDSHAKE_DONE;
+
+ return 1;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_datagram *fr) {
+ uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+ uint64_t len =
+ 1 +
+ (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_uvarintlen(datalen)) +
+ datalen;
+ uint8_t *p;
+ size_t i;
+
+ assert(fr->type == NGTCP2_FRAME_DATAGRAM ||
+ fr->type == NGTCP2_FRAME_DATAGRAM_LEN);
+
+ if (outlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = out;
+
+ *p++ = fr->type;
+ if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) {
+ p = ngtcp2_put_uvarint(p, datalen);
+ }
+
+ for (i = 0; i < fr->datacnt; ++i) {
+ assert(fr->data[i].len);
+ assert(fr->data[i].base);
+ p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
+ }
+
+ assert((size_t)(p - out) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+ngtcp2_ssize ngtcp2_pkt_write_version_negotiation(
+ uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid,
+ size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv,
+ size_t nsv) {
+ size_t len = 1 + 4 + 1 + dcidlen + 1 + scidlen + nsv * 4;
+ uint8_t *p;
+ size_t i;
+
+ assert(dcidlen < 256);
+ assert(scidlen < 256);
+
+ if (destlen < len) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = dest;
+
+ *p++ = 0x80 | unused_random;
+ p = ngtcp2_put_uint32be(p, 0);
+ *p++ = (uint8_t)dcidlen;
+ if (dcidlen) {
+ p = ngtcp2_cpymem(p, dcid, dcidlen);
+ }
+ *p++ = (uint8_t)scidlen;
+ if (scidlen) {
+ p = ngtcp2_cpymem(p, scid, scidlen);
+ }
+
+ for (i = 0; i < nsv; ++i) {
+ p = ngtcp2_put_uint32be(p, sv[i]);
+ }
+
+ assert((size_t)(p - dest) == len);
+
+ return (ngtcp2_ssize)len;
+}
+
+size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *end = payload + payloadlen;
+
+ assert((payloadlen % sizeof(uint32_t)) == 0);
+
+ for (; payload != end;) {
+ payload = ngtcp2_get_uint32(dest++, payload);
+ }
+
+ return payloadlen / sizeof(uint32_t);
+}
+
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+ const uint8_t *payload,
+ size_t payloadlen) {
+ const uint8_t *p = payload;
+
+ if (payloadlen <
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ sr->rand = p;
+ sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN;
+ p += sr->randlen;
+ memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN);
+
+ return 0;
+}
+
+int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload,
+ size_t payloadlen) {
+ size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN;
+
+ if (payloadlen < len) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ dest->token = (uint8_t *)payload;
+ dest->tokenlen = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN);
+ ngtcp2_cpymem(dest->tag, payload + dest->tokenlen, NGTCP2_RETRY_TAGLEN);
+
+ return 0;
+}
+
+int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num,
+ size_t n) {
+ int64_t expected = max_pkt_num + 1;
+ int64_t win = (int64_t)1 << n;
+ int64_t hwin = win / 2;
+ int64_t mask = win - 1;
+ int64_t cand = (expected & ~mask) | pkt_num;
+
+ if (cand <= expected - hwin) {
+ assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win);
+ return cand + win;
+ }
+ if (cand > expected + hwin && cand >= win) {
+ return cand - win;
+ }
+ return cand;
+}
+
+int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) {
+ int64_t largest_ack = fr->largest_ack;
+ size_t i;
+
+ if (largest_ack < (int64_t)fr->first_ack_range) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->first_ack_range;
+
+ for (i = 0; i < fr->rangecnt; ++i) {
+ if (largest_ack < (int64_t)fr->ranges[i].gap + 2) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->ranges[i].gap + 2;
+
+ if (largest_ack < (int64_t)fr->ranges[i].len) {
+ return NGTCP2_ERR_ACK_FRAME;
+ }
+
+ largest_ack -= (int64_t)fr->ranges[i].len;
+ }
+
+ return 0;
+}
+
+ngtcp2_ssize
+ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen,
+ const uint8_t *stateless_reset_token,
+ const uint8_t *rand, size_t randlen) {
+ uint8_t *p;
+
+ if (destlen <
+ NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ if (randlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ p = dest;
+
+ randlen = ngtcp2_min(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen);
+
+ p = ngtcp2_cpymem(p, rand, randlen);
+ p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ *dest = (uint8_t)((*dest & 0x7fu) | 0x40u);
+
+ return p - dest;
+}
+
+ngtcp2_ssize ngtcp2_pkt_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ ngtcp2_pkt_hd hd;
+ uint8_t pseudo_retry[1500];
+ ngtcp2_ssize pseudo_retrylen;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+ int rv;
+ uint8_t *p;
+ size_t offset;
+ const uint8_t *nonce;
+ size_t noncelen;
+
+ assert(tokenlen > 0);
+ assert(!ngtcp2_cid_eq(scid, odcid));
+
+ /* Retry packet is sent at most once per one connection attempt. In
+ the first connection attempt, client has to send random DCID
+ which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */
+ if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+ }
+
+ ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid,
+ scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version,
+ /* len = */ 0);
+
+ pseudo_retrylen =
+ ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd,
+ /* unused = */ 0, odcid, token, tokenlen);
+ if (pseudo_retrylen < 0) {
+ return pseudo_retrylen;
+ }
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ break;
+ default:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ /* OpenSSL does not like NULL plaintext. */
+ rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen,
+ pseudo_retry, (size_t)pseudo_retrylen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ offset = 1 + odcid->datalen;
+ if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ p = ngtcp2_cpymem(dest, pseudo_retry + offset,
+ (size_t)pseudo_retrylen - offset);
+ p = ngtcp2_cpymem(p, tag, sizeof(tag));
+
+ return p - dest;
+}
+
+ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry(
+ uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused,
+ const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) {
+ uint8_t *p = dest;
+ ngtcp2_ssize nwrite;
+
+ if (destlen < 1 + odcid->datalen) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *p++ = (uint8_t)odcid->datalen;
+ p = ngtcp2_cpymem(p, odcid->data, odcid->datalen);
+ destlen -= (size_t)(p - dest);
+
+ nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd);
+ if (nwrite < 0) {
+ return nwrite;
+ }
+
+ if (destlen < (size_t)nwrite + tokenlen) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ *p &= 0xf0;
+ *p |= unused;
+
+ p += nwrite;
+
+ p = ngtcp2_cpymem(p, token, tokenlen);
+
+ return p - dest;
+}
+
+int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_encrypt encrypt,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx) {
+ uint8_t pseudo_retry[1500];
+ size_t pseudo_retrylen;
+ uint8_t *p = pseudo_retry;
+ int rv;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+ const uint8_t *nonce;
+ size_t noncelen;
+
+ assert(pktlen >= sizeof(retry->tag));
+
+ if (sizeof(pseudo_retry) <
+ 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ *p++ = (uint8_t)retry->odcid.datalen;
+ p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen);
+ p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag));
+
+ pseudo_retrylen = (size_t)(p - pseudo_retry);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2) - 1;
+ break;
+ default:
+ nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ /* OpenSSL does not like NULL plaintext. */
+ rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen,
+ pseudo_retry, pseudo_retrylen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) {
+ return NGTCP2_ERR_PROTO;
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
+ uint64_t len, size_t left) {
+ size_t n = 1 /* type */ + ngtcp2_put_uvarintlen((uint64_t)stream_id) +
+ (offset ? ngtcp2_put_uvarintlen(offset) : 0);
+
+ if (left <= n) {
+ return (size_t)-1;
+ }
+
+ left -= n;
+
+ if (left > 8 + 1073741823 && len > 1073741823) {
+#if SIZE_MAX > UINT32_MAX
+ len = ngtcp2_min(len, 4611686018427387903lu);
+#endif /* SIZE_MAX > UINT32_MAX */
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 8));
+ }
+
+ if (left > 4 + 16383 && len > 16383) {
+ len = ngtcp2_min(len, 1073741823);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 4));
+ }
+
+ if (left > 2 + 63 && len > 63) {
+ len = ngtcp2_min(len, 16383);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 2));
+ }
+
+ len = ngtcp2_min(len, 63);
+ return (size_t)ngtcp2_min(len, (uint64_t)(left - 1));
+}
+
+size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) {
+ size_t n = 1 /* type */ + ngtcp2_put_uvarintlen(offset);
+
+ /* CRYPTO frame must contain nonzero length data. Return -1 if
+ there is no space to write crypto data. */
+ if (left <= n + 1) {
+ return (size_t)-1;
+ }
+
+ left -= n;
+
+ if (left > 8 + 1073741823 && len > 1073741823) {
+#if SIZE_MAX > UINT32_MAX
+ len = ngtcp2_min(len, 4611686018427387903lu);
+#endif /* SIZE_MAX > UINT32_MAX */
+ return ngtcp2_min(len, left - 8);
+ }
+
+ if (left > 4 + 16383 && len > 16383) {
+ len = ngtcp2_min(len, 1073741823);
+ return ngtcp2_min(len, left - 4);
+ }
+
+ if (left > 2 + 63 && len > 63) {
+ len = ngtcp2_min(len, 16383);
+ return ngtcp2_min(len, left - 2);
+ }
+
+ len = ngtcp2_min(len, 63);
+ return ngtcp2_min(len, left - 1);
+}
+
+size_t ngtcp2_pkt_datagram_framelen(size_t len) {
+ return 1 /* type */ + ngtcp2_put_uvarintlen(len) + len;
+}
+
+int ngtcp2_is_supported_version(uint32_t version) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ case NGTCP2_PROTO_VER_V2:
+ return 1;
+ default:
+ return NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
+ version <= NGTCP2_PROTO_VER_DRAFT_MAX;
+ }
+}
+
+int ngtcp2_is_reserved_version(uint32_t version) {
+ return (version & NGTCP2_RESERVED_VERSION_MASK) ==
+ NGTCP2_RESERVED_VERSION_MASK;
+}
+
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) {
+ uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2:
+ switch (pkt_type) {
+ case NGTCP2_PKT_TYPE_INITIAL_V2:
+ return NGTCP2_PKT_INITIAL;
+ case NGTCP2_PKT_TYPE_0RTT_V2:
+ return NGTCP2_PKT_0RTT;
+ case NGTCP2_PKT_TYPE_HANDSHAKE_V2:
+ return NGTCP2_PKT_HANDSHAKE;
+ case NGTCP2_PKT_TYPE_RETRY_V2:
+ return NGTCP2_PKT_RETRY;
+ default:
+ return 0;
+ }
+ default:
+ if (!ngtcp2_is_supported_version(version)) {
+ return 0;
+ }
+
+ /* QUIC v1 and draft versions share the same numeric packet
+ types. */
+ switch (pkt_type) {
+ case NGTCP2_PKT_TYPE_INITIAL_V1:
+ return NGTCP2_PKT_INITIAL;
+ case NGTCP2_PKT_TYPE_0RTT_V1:
+ return NGTCP2_PKT_0RTT;
+ case NGTCP2_PKT_TYPE_HANDSHAKE_V1:
+ return NGTCP2_PKT_HANDSHAKE;
+ case NGTCP2_PKT_TYPE_RETRY_V1:
+ return NGTCP2_PKT_RETRY;
+ default:
+ return 0;
+ }
+ }
+}
+
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) {
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2:
+ switch (pkt_type) {
+ case NGTCP2_PKT_INITIAL:
+ return NGTCP2_PKT_TYPE_INITIAL_V2;
+ case NGTCP2_PKT_0RTT:
+ return NGTCP2_PKT_TYPE_0RTT_V2;
+ case NGTCP2_PKT_HANDSHAKE:
+ return NGTCP2_PKT_TYPE_HANDSHAKE_V2;
+ case NGTCP2_PKT_RETRY:
+ return NGTCP2_PKT_TYPE_RETRY_V2;
+ default:
+ ngtcp2_unreachable();
+ }
+ default:
+ /* Assume that unsupported versions share the numeric long packet
+ types with QUIC v1 in order to send a packet to elicit Version
+ Negotiation packet. */
+
+ /* QUIC v1 and draft versions share the same numeric packet
+ types. */
+ switch (pkt_type) {
+ case NGTCP2_PKT_INITIAL:
+ return NGTCP2_PKT_TYPE_INITIAL_V1;
+ case NGTCP2_PKT_0RTT:
+ return NGTCP2_PKT_TYPE_0RTT_V1;
+ case NGTCP2_PKT_HANDSHAKE:
+ return NGTCP2_PKT_TYPE_HANDSHAKE_V1;
+ case NGTCP2_PKT_RETRY:
+ return NGTCP2_PKT_TYPE_RETRY_V1;
+ default:
+ ngtcp2_unreachable();
+ }
+ }
+}
+
+int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
+ if (c & NGTCP2_HEADER_FORM_BIT) {
+ return (c & NGTCP2_LONG_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO;
+ }
+ return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
new file mode 100644
index 0000000..9db62b0
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
@@ -0,0 +1,1235 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PKT_H
+#define NGTCP2_PKT_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* QUIC header macros */
+#define NGTCP2_HEADER_FORM_BIT 0x80
+#define NGTCP2_FIXED_BIT_MASK 0x40
+#define NGTCP2_PKT_NUMLEN_MASK 0x03
+
+/* Long header specific macros */
+#define NGTCP2_LONG_TYPE_MASK 0x30
+#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c
+
+/* Short header specific macros */
+#define NGTCP2_SHORT_SPIN_BIT_MASK 0x20
+#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18
+#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04
+
+/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */
+#define NGTCP2_SR_TYPE 0x1f
+
+/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header.
+ That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> +
+ LENGTH<1> + PKN<1> */
+#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1 + 1)
+
+#define NGTCP2_STREAM_FIN_BIT 0x01
+#define NGTCP2_STREAM_LEN_BIT 0x02
+#define NGTCP2_STREAM_OFF_BIT 0x04
+
+/* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required
+ other than payload for STREAM frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_STREAM_OVERHEAD (1 + 8 + 8 + 8)
+
+/* NGTCP2_CRYPTO_OVERHEAD is the maximum number of bytes required
+ other than payload for CRYPTO frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_CRYPTO_OVERHEAD (1 + 8 + 8)
+
+/* NGTCP2_DATAGRAM_OVERHEAD is the maximum number of bytes required
+ other than payload for DATAGRAM frame. That is from type field to
+ the beginning of the payload. */
+#define NGTCP2_DATAGRAM_OVERHEAD (1 + 8)
+
+/* NGTCP2_MIN_FRAME_PAYLOADLEN is the minimum frame payload length. */
+#define NGTCP2_MIN_FRAME_PAYLOADLEN 16
+
+/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional
+ server stream ID. */
+#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll)
+/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional
+ client stream ID. */
+#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll)
+/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional
+ server stream ID. */
+#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll)
+/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional
+ client stream ID. */
+#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell)
+
+/* NGTCP2_MAX_NUM_ACK_RANGES is the maximum number of Additional ACK
+ ranges which this library can create, or decode. */
+#define NGTCP2_MAX_ACK_RANGES 32
+
+/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */
+#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1))
+
+/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion in
+ addition to the minimum DCID length to hide/trigger Stateless
+ Reset. */
+#define NGTCP2_MIN_PKT_EXPANDLEN 22
+
+/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
+#define NGTCP2_RETRY_TAGLEN 16
+
+/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size
+ that this library can write. */
+#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1)
+
+/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by
+ Length field in Long packet header. */
+#define NGTCP2_PKT_LENGTHLEN 4
+
+/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for
+ QUIC v1. */
+#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0
+/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC
+ v1. */
+#define NGTCP2_PKT_TYPE_0RTT_V1 0x1
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type
+ for QUIC v1. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2
+/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC
+ v1. */
+#define NGTCP2_PKT_TYPE_RETRY_V1 0x3
+
+/* NGTCP2_PKT_TYPE_INITIAL_V2 is Initial long header packet type for
+ QUIC v2. */
+#define NGTCP2_PKT_TYPE_INITIAL_V2 0x1
+/* NGTCP2_PKT_TYPE_0RTT_V2 is 0RTT long header packet type for QUIC
+ v2. */
+#define NGTCP2_PKT_TYPE_0RTT_V2 0x2
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V2 is Handshake long header packet type
+ for QUIC v2. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V2 0x3
+/* NGTCP2_PKT_TYPE_RETRY_V2 is Retry long header packet type for QUIC
+ v2. */
+#define NGTCP2_PKT_TYPE_RETRY_V2 0x0
+
+typedef struct ngtcp2_pkt_retry {
+ ngtcp2_cid odcid;
+ uint8_t *token;
+ size_t tokenlen;
+ uint8_t tag[NGTCP2_RETRY_TAGLEN];
+} ngtcp2_pkt_retry;
+
+typedef enum {
+ NGTCP2_FRAME_PADDING = 0x00,
+ NGTCP2_FRAME_PING = 0x01,
+ NGTCP2_FRAME_ACK = 0x02,
+ NGTCP2_FRAME_ACK_ECN = 0x03,
+ NGTCP2_FRAME_RESET_STREAM = 0x04,
+ NGTCP2_FRAME_STOP_SENDING = 0x05,
+ NGTCP2_FRAME_CRYPTO = 0x06,
+ NGTCP2_FRAME_NEW_TOKEN = 0x07,
+ NGTCP2_FRAME_STREAM = 0x08,
+ NGTCP2_FRAME_MAX_DATA = 0x10,
+ NGTCP2_FRAME_MAX_STREAM_DATA = 0x11,
+ NGTCP2_FRAME_MAX_STREAMS_BIDI = 0x12,
+ NGTCP2_FRAME_MAX_STREAMS_UNI = 0x13,
+ NGTCP2_FRAME_DATA_BLOCKED = 0x14,
+ NGTCP2_FRAME_STREAM_DATA_BLOCKED = 0x15,
+ NGTCP2_FRAME_STREAMS_BLOCKED_BIDI = 0x16,
+ NGTCP2_FRAME_STREAMS_BLOCKED_UNI = 0x17,
+ NGTCP2_FRAME_NEW_CONNECTION_ID = 0x18,
+ NGTCP2_FRAME_RETIRE_CONNECTION_ID = 0x19,
+ NGTCP2_FRAME_PATH_CHALLENGE = 0x1a,
+ NGTCP2_FRAME_PATH_RESPONSE = 0x1b,
+ NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c,
+ NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d,
+ NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e,
+ NGTCP2_FRAME_DATAGRAM = 0x30,
+ NGTCP2_FRAME_DATAGRAM_LEN = 0x31,
+} ngtcp2_frame_type;
+
+typedef struct ngtcp2_stream {
+ uint8_t type;
+ /**
+ * flags of decoded STREAM frame. This gets ignored when encoding
+ * STREAM frame.
+ */
+ uint8_t flags;
+ uint8_t fin;
+ int64_t stream_id;
+ uint64_t offset;
+ /* datacnt is the number of elements that data contains. Although
+ the length of data is 1 in this definition, the library may
+ allocate extra bytes to hold more elements. */
+ size_t datacnt;
+ /* data is the array of ngtcp2_vec which references data. */
+ ngtcp2_vec data[1];
+} ngtcp2_stream;
+
+typedef struct ngtcp2_ack_range {
+ uint64_t gap;
+ uint64_t len;
+} ngtcp2_ack_range;
+
+typedef struct ngtcp2_ack {
+ uint8_t type;
+ int64_t largest_ack;
+ uint64_t ack_delay;
+ /**
+ * ack_delay_unscaled is an ack_delay multiplied by
+ * 2**ack_delay_component * NGTCP2_MICROSECONDS.
+ */
+ ngtcp2_duration ack_delay_unscaled;
+ struct {
+ uint64_t ect0;
+ uint64_t ect1;
+ uint64_t ce;
+ } ecn;
+ uint64_t first_ack_range;
+ size_t rangecnt;
+ ngtcp2_ack_range ranges[1];
+} ngtcp2_ack;
+
+typedef struct ngtcp2_padding {
+ uint8_t type;
+ /**
+ * The length of contiguous PADDING frames.
+ */
+ size_t len;
+} ngtcp2_padding;
+
+typedef struct ngtcp2_reset_stream {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t app_error_code;
+ uint64_t final_size;
+} ngtcp2_reset_stream;
+
+typedef struct ngtcp2_connection_close {
+ uint8_t type;
+ uint64_t error_code;
+ uint64_t frame_type;
+ size_t reasonlen;
+ uint8_t *reason;
+} ngtcp2_connection_close;
+
+typedef struct ngtcp2_max_data {
+ uint8_t type;
+ /**
+ * max_data is Maximum Data.
+ */
+ uint64_t max_data;
+} ngtcp2_max_data;
+
+typedef struct ngtcp2_max_stream_data {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t max_stream_data;
+} ngtcp2_max_stream_data;
+
+typedef struct ngtcp2_max_streams {
+ uint8_t type;
+ uint64_t max_streams;
+} ngtcp2_max_streams;
+
+typedef struct ngtcp2_ping {
+ uint8_t type;
+} ngtcp2_ping;
+
+typedef struct ngtcp2_data_blocked {
+ uint8_t type;
+ uint64_t offset;
+} ngtcp2_data_blocked;
+
+typedef struct ngtcp2_stream_data_blocked {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t offset;
+} ngtcp2_stream_data_blocked;
+
+typedef struct ngtcp2_streams_blocked {
+ uint8_t type;
+ uint64_t max_streams;
+} ngtcp2_streams_blocked;
+
+typedef struct ngtcp2_new_connection_id {
+ uint8_t type;
+ uint64_t seq;
+ uint64_t retire_prior_to;
+ ngtcp2_cid cid;
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_new_connection_id;
+
+typedef struct ngtcp2_stop_sending {
+ uint8_t type;
+ int64_t stream_id;
+ uint64_t app_error_code;
+} ngtcp2_stop_sending;
+
+typedef struct ngtcp2_path_challenge {
+ uint8_t type;
+ uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
+} ngtcp2_path_challenge;
+
+typedef struct ngtcp2_path_response {
+ uint8_t type;
+ uint8_t data[NGTCP2_PATH_CHALLENGE_DATALEN];
+} ngtcp2_path_response;
+
+typedef struct ngtcp2_crypto {
+ uint8_t type;
+ uint64_t offset;
+ /* datacnt is the number of elements that data contains. Although
+ the length of data is 1 in this definition, the library may
+ allocate extra bytes to hold more elements. */
+ size_t datacnt;
+ /* data is the array of ngtcp2_vec which references data. */
+ ngtcp2_vec data[1];
+} ngtcp2_crypto;
+
+typedef struct ngtcp2_new_token {
+ uint8_t type;
+ uint8_t *token;
+ size_t tokenlen;
+} ngtcp2_new_token;
+
+typedef struct ngtcp2_retire_connection_id {
+ uint8_t type;
+ uint64_t seq;
+} ngtcp2_retire_connection_id;
+
+typedef struct ngtcp2_handshake_done {
+ uint8_t type;
+} ngtcp2_handshake_done;
+
+typedef struct ngtcp2_datagram {
+ uint8_t type;
+ /* dgram_id is an opaque identifier chosen by an application. */
+ uint64_t dgram_id;
+ /* datacnt is the number of elements that data contains. */
+ size_t datacnt;
+ /* data is a pointer to ngtcp2_vec array that stores data. */
+ ngtcp2_vec *data;
+ /* rdata is conveniently embedded to ngtcp2_datagram, so that data
+ field can just point to the address of this field to store a
+ single vector which is the case when DATAGRAM is received from a
+ remote endpoint. */
+ ngtcp2_vec rdata[1];
+} ngtcp2_datagram;
+
+typedef union ngtcp2_frame {
+ uint8_t type;
+ ngtcp2_stream stream;
+ ngtcp2_ack ack;
+ ngtcp2_padding padding;
+ ngtcp2_reset_stream reset_stream;
+ ngtcp2_connection_close connection_close;
+ ngtcp2_max_data max_data;
+ ngtcp2_max_stream_data max_stream_data;
+ ngtcp2_max_streams max_streams;
+ ngtcp2_ping ping;
+ ngtcp2_data_blocked data_blocked;
+ ngtcp2_stream_data_blocked stream_data_blocked;
+ ngtcp2_streams_blocked streams_blocked;
+ ngtcp2_new_connection_id new_connection_id;
+ ngtcp2_stop_sending stop_sending;
+ ngtcp2_path_challenge path_challenge;
+ ngtcp2_path_response path_response;
+ ngtcp2_crypto crypto;
+ ngtcp2_new_token new_token;
+ ngtcp2_retire_connection_id retire_connection_id;
+ ngtcp2_handshake_done handshake_done;
+ ngtcp2_datagram datagram;
+} ngtcp2_frame;
+
+typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain;
+
+/*
+ * ngtcp2_pkt_chain is the chain of incoming packets buffered.
+ */
+struct ngtcp2_pkt_chain {
+ ngtcp2_path_storage path;
+ ngtcp2_pkt_info pi;
+ ngtcp2_pkt_chain *next;
+ uint8_t *pkt;
+ /* pktlen is length of a QUIC packet. */
+ size_t pktlen;
+ /* dgramlen is length of UDP datagram that a QUIC packet is
+ included. */
+ size_t dgramlen;
+ ngtcp2_tstamp ts;
+};
+
+/*
+ * ngtcp2_pkt_chain_new allocates ngtcp2_pkt_chain objects, and
+ * assigns its pointer to |*ppc|. The content of buffer pointed by
+ * |pkt| of length |pktlen| is copied into |*ppc|. The packet is
+ * obtained via the network |path|. The values of path->local and
+ * path->remote are copied into |*ppc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
+ const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+ size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pkt_chain_del deallocates |pc|. It also frees the memory
+ * pointed by |pc|.
+ */
+void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pkt_hd_init initializes |hd| with the given values. If
+ * |dcid| and/or |scid| is NULL, DCID and SCID of |hd| is empty
+ * respectively. |pkt_numlen| is the number of bytes used to encode
+ * |pkt_num| and either 1, 2, or 4. |version| is QUIC version for
+ * long header. |len| is the length field of Initial, 0RTT, and
+ * Handshake packets.
+ */
+void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ int64_t pkt_num, size_t pkt_numlen, uint32_t version,
+ size_t len);
+
+/*
+ * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into
+ * |out| which has length |outlen|. It returns the number of bytes
+ * written into |outlen| if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too short
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd);
+
+/*
+ * ngtcp2_pkt_encode_hd_short encodes |hd| as QUIC short header into
+ * |out| which has length |outlen|. It returns the number of bytes
+ * written into |outlen| if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer is too short
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen,
+ const ngtcp2_pkt_hd *hd);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer
+ * pointed by |payload| whose length is |payloadlen|.
+ *
+ * This function returns the number of bytes read to decode a single
+ * frame if it succeeds, or one of the following negative error codes:
+ *
+ * :enum:`NGTCP2_ERR_FRAME_ENCODING`
+ * Frame is badly formatted; or frame type is unknown; or
+ * |payloadlen| is 0.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload,
+ size_t payloadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_encode_frame` encodes a frame |fm| into the buffer
+ * pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :enum:`NGTCP2_ERR_NOBUF`
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
+ ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_pkt_decode_version_negotiation decodes Version Negotiation
+ * packet payload |payload| of length |payloadlen|, and stores the
+ * result in |dest|. |dest| must have enough capacity to store the
+ * result. |payloadlen| also must be a multiple of sizeof(uint32_t).
+ *
+ * This function returns the number of versions written in |dest|.
+ */
+size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stateless_reset decodes Stateless Reset payload
+ * |payload| of length |payloadlen|. The |payload| must start with
+ * Stateless Reset Token.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Payloadlen is too short.
+ */
+int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of
+ * length |payloadlen|. The |payload| must start with Retry token
+ * field.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * Payloadlen is too short.
+ */
+int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stream_frame decodes STREAM frame from |payload|
+ * of length |payloadlen|. The result is stored in the object pointed
+ * by |dest|. STREAM frame must start at payload[0]. This function
+ * finishes when it decodes one STREAM frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_ack_frame decodes ACK frame from |payload| of
+ * length |payloadlen|. The result is stored in the object pointed by
+ * |dest|. ACK frame must start at payload[0]. This function
+ * finishes when it decodes one ACK frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include ACK frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_padding_frame decodes contiguous PADDING frames
+ * from |payload| of length |payloadlen|. It continues to parse
+ * frames as long as the frame type is PADDING. This finishes when it
+ * encounters the frame type which is not PADDING, or all input data
+ * is read. The first byte (payload[0]) must be NGTCP2_FRAME_PADDING.
+ * This function returns the exact number of bytes read to decode
+ * PADDING frames.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_reset_stream_frame decodes RESET_STREAM frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. RESET_STREAM frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * RESET_STREAM frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include RESET_STREAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_connection_close_frame decodes CONNECTION_CLOSE
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. CONNECTION_CLOSE frame must start
+ * at payload[0]. This function finishes it decodes one
+ * CONNECTION_CLOSE frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include CONNECTION_CLOSE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame(
+ ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_data_frame decodes MAX_DATA frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. MAX_DATA frame must start at payload[0].
+ * This function finishes when it decodes one MAX_DATA frame, and
+ * returns the exact number of bytes read to decode a frame if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_DATA frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_stream_data_frame decodes MAX_STREAM_DATA
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. MAX_STREAM_DATA frame must start
+ * at payload[0]. This function finishes when it decodes one
+ * MAX_STREAM_DATA frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_STREAM_DATA frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame(
+ ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_max_streams_frame decodes MAX_STREAMS frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. MAX_STREAMS frame must start at
+ * payload[0]. This function finishes when it decodes one MAX_STREAMS
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include MAX_STREAMS frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_ping_frame decodes PING frame from |payload| of
+ * length |payloadlen|. The result is stored in the object pointed by
+ * |dest|. PING frame must start at payload[0]. This function
+ * finishes when it decodes one PING frame, and returns the exact
+ * number of bytes read to decode a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_data_blocked_frame decodes DATA_BLOCKED frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. DATA_BLOCKED frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * DATA_BLOCKED frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include DATA_BLOCKED frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stream_data_blocked_frame decodes
+ * STREAM_DATA_BLOCKED frame from |payload| of length |payloadlen|.
+ * The result is stored in the object pointed by |dest|.
+ * STREAM_DATA_BLOCKED frame must start at payload[0]. This function
+ * finishes when it decodes one STREAM_DATA_BLOCKED frame, and returns
+ * the exact number of bytes read to decode a frame if it succeeds, or
+ * one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAM_DATA_BLOCKED frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_streams_blocked_frame decodes STREAMS_BLOCKED
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. STREAMS_BLOCKED frame must start
+ * at payload[0]. This function finishes when it decodes one
+ * STREAMS_BLOCKED frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STREAMS_BLOCKED frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame(
+ ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_new_connection_id_frame decodes NEW_CONNECTION_ID
+ * frame from |payload| of length |payloadlen|. The result is stored
+ * in the object pointed by |dest|. NEW_CONNECTION_ID frame must
+ * start at payload[0]. This function finishes when it decodes one
+ * NEW_CONNECTION_ID frame, and returns the exact number of bytes read
+ * to decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include NEW_CONNECTION_ID frame; or the
+ * length of CID is strictly less than NGTCP2_MIN_CIDLEN or
+ * greater than NGTCP2_MAX_CIDLEN.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame(
+ ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. STOP_SENDING frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * STOP_SENDING frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include STOP_SENDING frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_path_challenge_frame decodes PATH_CHALLENGE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. PATH_CHALLENGE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * PATH_CHALLENGE frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include PATH_CHALLENGE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_path_response_frame decodes PATH_RESPONSE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. PATH_RESPONSE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * PATH_RESPONSE frame, and returns the exact number of bytes read to
+ * decode a frame if it succeeds, or one of the following negative
+ * error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include PATH_RESPONSE frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_crypto_frame decodes CRYPTO frame from |payload|
+ * of length |payloadlen|. The result is stored in the object pointed
+ * by |dest|. CRYPTO frame must start at payload[0]. This function
+ * finishes when it decodes one CRYPTO frame, and returns the exact
+ * number of bytes read to decode a frame if it succeeds, or one of
+ * the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include CRYPTO frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. NEW_TOKEN frame must start at
+ * payload[0]. This function finishes when it decodes one NEW_TOKEN
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include NEW_TOKEN frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_retire_connection_id_frame decodes RETIRE_CONNECTION_ID
+ * frame from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. RETIRE_CONNECTION_ID frame must start at
+ * payload[0]. This function finishes when it decodes one RETIRE_CONNECTION_ID
+ * frame, and returns the exact number of bytes read to decode a frame
+ * if it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include RETIRE_CONNECTION_ID frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame
+ * from |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. HANDSHAKE_DONE frame must start at
+ * payload[0]. This function finishes when it decodes one
+ * HANDSHAKE_DONE frame, and returns the exact number of bytes read to
+ * decode a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_decode_datagram_frame decodes DATAGRAM frame from
+ * |payload| of length |payloadlen|. The result is stored in the
+ * object pointed by |dest|. DATAGRAM frame must start at payload[0].
+ * This function finishes when it decodes one DATAGRAM frame, and
+ * returns the exact number of bytes read to decode a frame if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_FRAME_ENCODING
+ * Payload is too short to include DATAGRAM frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest,
+ const uint8_t *payload,
+ size_t payloadlen);
+
+/*
+ * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function assigns <the serialized frame type> &
+ * ~NGTCP2_FRAME_STREAM to fr->flags.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen,
+ ngtcp2_stream *fr);
+
+/*
+ * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer
+ * pointed by |out| of length |outlen|.
+ *
+ * This function assigns <the serialized frame type> &
+ * ~NGTCP2_FRAME_ACK to fr->flags.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
+ ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_pkt_encode_padding_frame encodes PADDING frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function encodes consecutive fr->len PADDING frames.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write frame(s).
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_padding *fr);
+
+/*
+ * ngtcp2_pkt_encode_reset_stream_frame encodes RESET_STREAM frame
+ * |fr| into the buffer pointed by |out| of length |buflen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_reset_stream *fr);
+
+/*
+ * ngtcp2_pkt_encode_connection_close_frame encodes CONNECTION_CLOSE
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_connection_close *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_data_frame encodes MAX_DATA frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_data *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_stream_data_frame encodes MAX_STREAM_DATA
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_stream_data *fr);
+
+/*
+ * ngtcp2_pkt_encode_max_streams_frame encodes MAX_STREAMS
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_max_streams *fr);
+
+/*
+ * ngtcp2_pkt_encode_ping_frame encodes PING frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_ping *fr);
+
+/*
+ * ngtcp2_pkt_encode_data_blocked_frame encodes DATA_BLOCKED frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_data_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_stream_data_blocked_frame encodes
+ * STREAM_DATA_BLOCKED frame |fr| into the buffer pointed by |out| of
+ * length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_streams_blocked_frame encodes STREAMS_BLOCKED
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_streams_blocked *fr);
+
+/*
+ * ngtcp2_pkt_encode_new_connection_id_frame encodes NEW_CONNECTION_ID
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_connection_id *fr);
+
+/*
+ * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_stop_sending *fr);
+
+/*
+ * ngtcp2_pkt_encode_path_challenge_frame encodes PATH_CHALLENGE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_challenge *fr);
+
+/*
+ * ngtcp2_pkt_encode_path_response_frame encodes PATH_RESPONSE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_path_response *fr);
+
+/*
+ * ngtcp2_pkt_encode_crypto_frame encodes CRYPTO frame |fr| into the
+ * buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_crypto *fr);
+
+/*
+ * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_new_token *fr);
+
+/*
+ * ngtcp2_pkt_encode_retire_connection_id_frame encodes RETIRE_CONNECTION_ID
+ * frame |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame(
+ uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr);
+
+/*
+ * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame
+ * |fr| into the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize
+ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_handshake_done *fr);
+
+/*
+ * ngtcp2_pkt_encode_datagram_frame encodes DATAGRAM frame |fr| into
+ * the buffer pointed by |out| of length |outlen|.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * Buffer does not have enough capacity to write a frame.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen,
+ const ngtcp2_datagram *fr);
+
+/*
+ * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for
+ * |pkt_num|, which is expected to be least significant |n| bits. The
+ * |max_pkt_num| is the highest successfully authenticated packet
+ * number.
+ */
+int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num,
+ size_t n);
+
+/*
+ * ngtcp2_pkt_validate_ack checks that ack is malformed or not.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_ACK_FRAME
+ * ACK frame is malformed
+ */
+int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr);
+
+/*
+ * ngtcp2_pkt_stream_max_datalen returns the maximum number of bytes
+ * which can be sent for stream denoted by |stream_id|. |offset| is
+ * an offset of within the stream. |len| is the estimated number of
+ * bytes to be sent. |left| is the size of buffer. If |left| is too
+ * small to write STREAM frame, this function returns (size_t)-1.
+ */
+size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset,
+ uint64_t len, size_t left);
+
+/*
+ * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes
+ * which can be sent for crypto stream. |offset| is an offset of
+ * within the crypto stream. |len| is the estimated number of bytes
+ * to be sent. |left| is the size of buffer. If |left| is too small
+ * to write CRYPTO frame, this function returns (size_t)-1.
+ */
+size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left);
+
+/*
+ * ngtcp2_pkt_datagram_framelen returns the length of DATAGRAM frame
+ * to encode |len| bytes of data.
+ */
+size_t ngtcp2_pkt_datagram_framelen(size_t len);
+
+/*
+ * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of
+ * the packet header has the correct reserved bits.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Reserved bits has wrong value.
+ */
+int ngtcp2_pkt_verify_reserved_bits(uint8_t c);
+
+/*
+ * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the
+ * buffer pointed by |dest| of length |destlen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_BUF
+ * Buffer is too short.
+ */
+ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry(
+ uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused,
+ const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen);
+
+/*
+ * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer
+ * pointed by |pkt| of length |pktlen| must contain Retry packet
+ * including packet header. The odcid and tag fields of |retry| must
+ * be specified. |aead| must be AEAD_AES_128_GCM.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PROTO
+ * Verification failed.
+ */
+int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_encrypt encrypt,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/*
+ * ngtcp2_pkt_versioned_type returns versioned packet type for a
+ * version |version| that corresponds to the version-independent
+ * |pkt_type|.
+ */
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_get_type_long` returns the version-independent long
+ * packet type. |version| is the QUIC version. |c| is the first byte
+ * of Long packet header. If |version| is not supported by the
+ * library, it returns 0.
+ */
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c);
+
+#endif /* NGTCP2_PKT_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
new file mode 100644
index 0000000..771ef5e
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
@@ -0,0 +1,160 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pmtud.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_macro.h"
+
+/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent
+ for each probe. */
+#define NGTCP2_PMTUD_PROBE_NUM_MAX 3
+
+static size_t mtu_probes[] = {
+ 1454 - 48, /* The well known MTU used by a domestic optic fiber
+ service in Japan. */
+ 1390 - 48, /* Typical Tunneled MTU */
+ 1280 - 48, /* IPv6 minimum MTU */
+ 1492 - 48, /* PPPoE */
+};
+
+#define NGTCP2_MTU_PROBESLEN ngtcp2_arraylen(mtu_probes)
+
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+ size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+ const ngtcp2_mem *mem) {
+ ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud));
+
+ if (pmtud == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ pmtud->mem = mem;
+ pmtud->mtu_idx = 0;
+ pmtud->num_pkts_sent = 0;
+ pmtud->expiry = UINT64_MAX;
+ pmtud->tx_pkt_num = tx_pkt_num;
+ pmtud->max_udp_payload_size = max_udp_payload_size;
+ pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size;
+ pmtud->min_fail_udp_payload_size = SIZE_MAX;
+
+ for (; pmtud->mtu_idx < NGTCP2_MTU_PROBESLEN; ++pmtud->mtu_idx) {
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+ continue;
+ }
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+ break;
+ }
+ }
+
+ *ppmtud = pmtud;
+
+ return 0;
+}
+
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) {
+ if (!pmtud) {
+ return;
+ }
+
+ ngtcp2_mem_free(pmtud->mem, pmtud);
+}
+
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) {
+ assert(pmtud->mtu_idx < NGTCP2_MTU_PROBESLEN);
+
+ return mtu_probes[pmtud->mtu_idx];
+}
+
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_tstamp timeout;
+
+ if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+ timeout = pto;
+ } else {
+ timeout = 3 * pto;
+ }
+
+ pmtud->expiry = ts + timeout;
+}
+
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) {
+ return pmtud->expiry == UINT64_MAX;
+}
+
+static void pmtud_next_probe(ngtcp2_pmtud *pmtud) {
+ assert(pmtud->mtu_idx < NGTCP2_MTU_PROBESLEN);
+
+ ++pmtud->mtu_idx;
+ pmtud->num_pkts_sent = 0;
+ pmtud->expiry = UINT64_MAX;
+
+ for (; pmtud->mtu_idx < NGTCP2_MTU_PROBESLEN; ++pmtud->mtu_idx) {
+ if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size ||
+ mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+ continue;
+ }
+
+ if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) {
+ break;
+ }
+ }
+}
+
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) {
+ pmtud->max_udp_payload_size =
+ ngtcp2_max(pmtud->max_udp_payload_size, payloadlen);
+
+ assert(pmtud->mtu_idx < NGTCP2_MTU_PROBESLEN);
+
+ if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+ return;
+ }
+
+ pmtud_next_probe(pmtud);
+}
+
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) {
+ if (ts < pmtud->expiry) {
+ return;
+ }
+
+ pmtud->expiry = UINT64_MAX;
+
+ if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+ return;
+ }
+
+ pmtud->min_fail_udp_payload_size =
+ ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]);
+
+ pmtud_next_probe(pmtud);
+}
+
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) {
+ return pmtud->mtu_idx >= NGTCP2_MTU_PROBESLEN;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
new file mode 100644
index 0000000..6b2e691
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
@@ -0,0 +1,123 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PMTUD_H
+#define NGTCP2_PMTUD_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_pmtud {
+ const ngtcp2_mem *mem;
+ /* mtu_idx is the index of UDP payload size candidates to try
+ out. */
+ size_t mtu_idx;
+ /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload
+ sent */
+ size_t num_pkts_sent;
+ /* expiry is the expired, if it is reached, send out the next UDP
+ datagram. UINT64_MAX means no expiry, or expiration is canceled.
+ In either case, new probe packet should be sent unless we have
+ done all attempts. */
+ ngtcp2_tstamp expiry;
+ /* tx_pkt_num is the smallest outgoing packet number where the
+ current discovery is performed. In other words, acknowledging
+ packet whose packet number lower than that does not indicate the
+ success of Path MTU Discovery. */
+ int64_t tx_pkt_num;
+ /* max_udp_payload_size is the maximum UDP payload size which is
+ known to work. */
+ size_t max_udp_payload_size;
+ /* hard_max_udp_payload_size is the maximum UDP payload size that is
+ going to be probed. */
+ size_t hard_max_udp_payload_size;
+ /* min_fail_udp_payload_size is the minimum UDP payload size that is
+ known to fail. */
+ size_t min_fail_udp_payload_size;
+} ngtcp2_pmtud;
+
+/*
+ * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its
+ * pointer to |*ppmtud|. |max_udp_payload_size| is the maximum UDP
+ * payload size that is known to work for the current path.
+ * |tx_pkt_num| should be the next packet number to send, which is
+ * used to differentiate the PMTUD probe packet sent by the previous
+ * PMTUD. PMTUD might finish immediately if |max_udp_payload_size| is
+ * larger than or equal to all UDP payload probe candidates.
+ * Therefore, call ngtcp2_pmtud_finished to check this situation.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+ size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pmtud_del deletes |pmtud|.
+ */
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probelen returns the length of UDP payload size for a
+ * PMTUD probe packet.
+ */
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is
+ * sent.
+ */
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet
+ * should be sent.
+ */
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe
+ * UDP datagram sized |payloadlen| is acknowledged.
+ */
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen);
+
+/*
+ * ngtcp2_pmtud_handle_expiry handles expiry.
+ */
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished.
+ */
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud);
+
+#endif /* NGTCP2_PMTUD_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
new file mode 100644
index 0000000..ffba131
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
@@ -0,0 +1,230 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ppe.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_conv.h"
+
+void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen,
+ ngtcp2_crypto_cc *cc) {
+ ngtcp2_buf_init(&ppe->buf, out, outlen);
+
+ ppe->hdlen = 0;
+ ppe->len_offset = 0;
+ ppe->pkt_num_offset = 0;
+ ppe->pkt_numlen = 0;
+ ppe->pkt_num = 0;
+ ppe->sample_offset = 0;
+ ppe->cc = cc;
+}
+
+int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
+ ngtcp2_ssize rv;
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + 1 + hd->scid.datalen;
+ if (hd->type == NGTCP2_PKT_INITIAL) {
+ ppe->len_offset += ngtcp2_put_uvarintlen(hd->tokenlen) + hd->tokenlen;
+ }
+ ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN;
+ rv = ngtcp2_pkt_encode_hd_long(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ } else {
+ ppe->pkt_num_offset = 1 + hd->dcid.datalen;
+ rv = ngtcp2_pkt_encode_hd_short(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ }
+ if (rv < 0) {
+ return (int)rv;
+ }
+
+ ppe->sample_offset = ppe->pkt_num_offset + 4;
+
+ buf->last += rv;
+
+ ppe->pkt_numlen = hd->pkt_numlen;
+ ppe->hdlen = (size_t)rv;
+
+ ppe->pkt_num = hd->pkt_num;
+
+ return 0;
+}
+
+int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) {
+ ngtcp2_ssize rv;
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ return NGTCP2_ERR_NOBUF;
+ }
+
+ rv = ngtcp2_pkt_encode_frame(
+ buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr);
+ if (rv < 0) {
+ return (int)rv;
+ }
+
+ buf->last += rv;
+
+ return 0;
+}
+
+ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
+ ngtcp2_buf *buf = &ppe->buf;
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ uint8_t *payload = buf->begin + ppe->hdlen;
+ size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen;
+ uint8_t mask[NGTCP2_HP_SAMPLELEN];
+ uint8_t *p;
+ size_t i;
+ int rv;
+
+ assert(cc->encrypt);
+ assert(cc->hp_mask);
+
+ if (ppe->len_offset) {
+ ngtcp2_put_uvarint30(
+ buf->begin + ppe->len_offset,
+ (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead));
+ }
+
+ ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len,
+ ppe->pkt_num);
+
+ rv = cc->encrypt(payload, &cc->aead, &cc->ckm->aead_ctx, payload, payloadlen,
+ ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ buf->last = payload + payloadlen + cc->aead.max_overhead;
+
+ /* TODO Check that we have enough space to get sample */
+ assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf));
+
+ rv = cc->hp_mask(mask, &cc->hp, &cc->hp_ctx, buf->begin + ppe->sample_offset);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ p = buf->begin;
+ if (*p & NGTCP2_HEADER_FORM_BIT) {
+ *p = (uint8_t)(*p ^ (mask[0] & 0x0f));
+ } else {
+ *p = (uint8_t)(*p ^ (mask[0] & 0x1f));
+ }
+
+ p = buf->begin + ppe->pkt_num_offset;
+ for (i = 0; i < ppe->pkt_numlen; ++i) {
+ *(p + i) ^= mask[i + 1];
+ }
+
+ if (ppkt != NULL) {
+ *ppkt = buf->begin;
+ }
+
+ return (ngtcp2_ssize)ngtcp2_buf_len(buf);
+}
+
+size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) {
+ return 0;
+ }
+
+ return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead;
+}
+
+size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+
+ return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead;
+}
+
+size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t len;
+
+ assert(ngtcp2_buf_left(buf) >= cc->aead.max_overhead);
+
+ len = ngtcp2_buf_left(buf) - cc->aead.max_overhead;
+ memset(buf->last, 0, len);
+ buf->last += len;
+
+ return len;
+}
+
+size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t max_samplelen;
+ size_t len = 0;
+
+ assert(cc->aead.max_overhead);
+
+ max_samplelen =
+ ngtcp2_buf_len(buf) + cc->aead.max_overhead - ppe->sample_offset;
+ if (max_samplelen < NGTCP2_HP_SAMPLELEN) {
+ len = NGTCP2_HP_SAMPLELEN - max_samplelen;
+ assert(ngtcp2_ppe_left(ppe) >= len);
+ memset(buf->last, 0, len);
+ buf->last += len;
+ }
+
+ return len;
+}
+
+size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) {
+ ngtcp2_crypto_cc *cc = ppe->cc;
+ ngtcp2_buf *buf = &ppe->buf;
+ size_t pktlen = ngtcp2_buf_len(buf) + cc->aead.max_overhead;
+ size_t len;
+
+ if (pktlen >= n) {
+ return 0;
+ }
+
+ len = n - pktlen;
+ buf->last = ngtcp2_setmem(buf->last, 0, len);
+
+ return len;
+}
+
+int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe) {
+ ngtcp2_buf *buf = &ppe->buf;
+ return ngtcp2_buf_left(buf) >= (4 - ppe->pkt_numlen) + NGTCP2_HP_SAMPLELEN;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h
new file mode 100644
index 0000000..bf220df
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h
@@ -0,0 +1,153 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PPE_H
+#define NGTCP2_PPE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_crypto.h"
+
+/*
+ * ngtcp2_ppe is the Protected Packet Encoder.
+ */
+typedef struct ngtcp2_ppe {
+ ngtcp2_buf buf;
+ ngtcp2_crypto_cc *cc;
+ /* hdlen is the number of bytes for packet header written in buf. */
+ size_t hdlen;
+ /* len_offset is the offset to Length field. */
+ size_t len_offset;
+ /* pkt_num_offset is the offset to packet number field. */
+ size_t pkt_num_offset;
+ /* pkt_numlen is the number of bytes used to encode a packet
+ number */
+ size_t pkt_numlen;
+ /* sample_offset is the offset to sample for packet number
+ encryption. */
+ size_t sample_offset;
+ /* pkt_num is the packet number written in buf. */
+ int64_t pkt_num;
+ /* nonce is the buffer to store nonce. It should be equal or longer
+ than then length of IV. */
+ uint8_t nonce[32];
+} ngtcp2_ppe;
+
+/*
+ * ngtcp2_ppe_init initializes |ppe| with the given buffer.
+ */
+void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen,
+ ngtcp2_crypto_cc *cc);
+
+/*
+ * ngtcp2_ppe_encode_hd encodes |hd|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * The buffer is too small.
+ */
+int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd);
+
+/*
+ * ngtcp2_ppe_encode_frame encodes |fr|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOBUF
+ * The buffer is too small.
+ */
+int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_ppe_final encrypts QUIC packet payload. If |**ppkt| is not
+ * NULL, the pointer to the packet is assigned to it.
+ *
+ * This function returns the length of QUIC packet, including header,
+ * and payload if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User-defined callback function failed.
+ */
+ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt);
+
+/*
+ * ngtcp2_ppe_left returns the number of bytes left to write
+ * additional frames. This does not count AEAD overhead.
+ */
+size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_pktlen returns the provisional packet length. It
+ * includes AEAD overhead.
+ */
+size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe);
+
+/**
+ * @function
+ *
+ * `ngtcp2_ppe_padding` encodes PADDING frames to the end of the
+ * buffer. This function returns the number of bytes padded.
+ */
+size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_padding_hp_sample adds PADDING frame if the current
+ * payload does not have enough space for header protection sample.
+ * This function should be called just before calling
+ * ngtcp2_ppe_final().
+ *
+ * This function returns the number of bytes added as padding.
+ */
+size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe);
+
+/*
+ * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC
+ * packet is at least |n| bytes long. If it is unable to add PADDING
+ * in that way, this function still adds PADDING frame as much as
+ * possible. This function should be called just before calling
+ * ngtcp2_ppe_final(). For Short packet, this function should be
+ * called instead of ngtcp2_ppe_padding_hp_sample.
+ *
+ * This function returns the number of bytes added as padding.
+ */
+size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n);
+
+/*
+ * ngtcp2_ppe_ensure_hp_sample returns nonzero if the buffer has
+ * enough space for header protection sample. This should be called
+ * right after packet header is written.
+ */
+int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe);
+
+#endif /* NGTCP2_PPE_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c
new file mode 100644
index 0000000..5e1003d
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c
@@ -0,0 +1,164 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pq.h"
+
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem) {
+ pq->mem = mem;
+ pq->capacity = 0;
+ pq->q = NULL;
+ pq->length = 0;
+ pq->less = less;
+}
+
+void ngtcp2_pq_free(ngtcp2_pq *pq) {
+ ngtcp2_mem_free(pq->mem, pq->q);
+ pq->q = NULL;
+}
+
+static void swap(ngtcp2_pq *pq, size_t i, size_t j) {
+ ngtcp2_pq_entry *a = pq->q[i];
+ ngtcp2_pq_entry *b = pq->q[j];
+
+ pq->q[i] = b;
+ b->index = i;
+ pq->q[j] = a;
+ a->index = j;
+}
+
+static void bubble_up(ngtcp2_pq *pq, size_t index) {
+ size_t parent;
+ while (index != 0) {
+ parent = (index - 1) / 2;
+ if (!pq->less(pq->q[index], pq->q[parent])) {
+ return;
+ }
+ swap(pq, parent, index);
+ index = parent;
+ }
+}
+
+int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item) {
+ if (pq->capacity <= pq->length) {
+ void *nq;
+ size_t ncapacity;
+
+ ncapacity = ngtcp2_max(4, (pq->capacity * 2));
+
+ nq = ngtcp2_mem_realloc(pq->mem, pq->q,
+ ncapacity * sizeof(ngtcp2_pq_entry *));
+ if (nq == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+ pq->capacity = ncapacity;
+ pq->q = nq;
+ }
+ pq->q[pq->length] = item;
+ item->index = pq->length;
+ ++pq->length;
+ bubble_up(pq, pq->length - 1);
+ return 0;
+}
+
+ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq) {
+ assert(pq->length);
+ return pq->q[0];
+}
+
+static void bubble_down(ngtcp2_pq *pq, size_t index) {
+ size_t i, j, minindex;
+ for (;;) {
+ j = index * 2 + 1;
+ minindex = index;
+ for (i = 0; i < 2; ++i, ++j) {
+ if (j >= pq->length) {
+ break;
+ }
+ if (pq->less(pq->q[j], pq->q[minindex])) {
+ minindex = j;
+ }
+ }
+ if (minindex == index) {
+ return;
+ }
+ swap(pq, index, minindex);
+ index = minindex;
+ }
+}
+
+void ngtcp2_pq_pop(ngtcp2_pq *pq) {
+ if (pq->length > 0) {
+ pq->q[0] = pq->q[pq->length - 1];
+ pq->q[0]->index = 0;
+ --pq->length;
+ bubble_down(pq, 0);
+ }
+}
+
+void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) {
+ assert(pq->q[item->index] == item);
+
+ if (item->index == 0) {
+ ngtcp2_pq_pop(pq);
+ return;
+ }
+
+ if (item->index == pq->length - 1) {
+ --pq->length;
+ return;
+ }
+
+ pq->q[item->index] = pq->q[pq->length - 1];
+ pq->q[item->index]->index = item->index;
+ --pq->length;
+
+ if (pq->less(item, pq->q[item->index])) {
+ bubble_down(pq, item->index);
+ } else {
+ bubble_up(pq, item->index);
+ }
+}
+
+int ngtcp2_pq_empty(ngtcp2_pq *pq) { return pq->length == 0; }
+
+size_t ngtcp2_pq_size(ngtcp2_pq *pq) { return pq->length; }
+
+int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg) {
+ size_t i;
+
+ if (pq->length == 0) {
+ return 0;
+ }
+ for (i = 0; i < pq->length; ++i) {
+ if ((*fun)(pq->q[i], arg)) {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h
new file mode 100644
index 0000000..720c309
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.h
@@ -0,0 +1,126 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2012 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PQ_H
+#define NGTCP2_PQ_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/* Implementation of priority queue */
+
+/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates
+ that an entry is not queued. Assigning this value to
+ ngtcp2_pq_entry.index can check that the entry is queued or not. */
+#define NGTCP2_PQ_BAD_INDEX SIZE_MAX
+
+typedef struct ngtcp2_pq_entry {
+ size_t index;
+} ngtcp2_pq_entry;
+
+/* "less" function, return nonzero if |lhs| is less than |rhs|. */
+typedef int (*ngtcp2_less)(const ngtcp2_pq_entry *lhs,
+ const ngtcp2_pq_entry *rhs);
+
+typedef struct ngtcp2_pq {
+ /* The pointer to the pointer to the item stored */
+ ngtcp2_pq_entry **q;
+ /* Memory allocator */
+ const ngtcp2_mem *mem;
+ /* The number of items stored */
+ size_t length;
+ /* The maximum number of items this pq can store. This is
+ automatically extended when length is reached to this value. */
+ size_t capacity;
+ /* The less function between items */
+ ngtcp2_less less;
+} ngtcp2_pq;
+
+/*
+ * Initializes priority queue |pq| with compare function |cmp|.
+ */
+void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem);
+
+/*
+ * Deallocates any resources allocated for |pq|. The stored items are
+ * not freed by this function.
+ */
+void ngtcp2_pq_free(ngtcp2_pq *pq);
+
+/*
+ * Adds |item| to the priority queue |pq|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item);
+
+/*
+ * Returns item at the top of the queue |pq|. It is undefined if the
+ * queue is empty.
+ */
+ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq);
+
+/*
+ * Pops item at the top of the queue |pq|. The popped item is not
+ * freed by this function.
+ */
+void ngtcp2_pq_pop(ngtcp2_pq *pq);
+
+/*
+ * Returns nonzero if the queue |pq| is empty.
+ */
+int ngtcp2_pq_empty(ngtcp2_pq *pq);
+
+/*
+ * Returns the number of items in the queue |pq|.
+ */
+size_t ngtcp2_pq_size(ngtcp2_pq *pq);
+
+typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg);
+
+/*
+ * Applys |fun| to each item in |pq|. The |arg| is passed as arg
+ * parameter to callback function. This function must not change the
+ * ordering key. If the return value from callback is nonzero, this
+ * function returns 1 immediately without iterating remaining items.
+ * Otherwise this function returns 0.
+ */
+int ngtcp2_pq_each(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg);
+
+/*
+ * Removes |item| from priority queue.
+ */
+void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item);
+
+#endif /* NGTCP2_PQ_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
new file mode 100644
index 0000000..314e005
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
@@ -0,0 +1,172 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pv.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_addr.h"
+
+void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags) {
+ memcpy(pvent->data, data, sizeof(pvent->data));
+ pvent->expiry = expiry;
+ pvent->flags = flags;
+}
+
+int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
+ ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log,
+ const ngtcp2_mem *mem) {
+ (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv));
+ if (*ppv == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents);
+
+ ngtcp2_dcid_copy(&(*ppv)->dcid, dcid);
+
+ (*ppv)->mem = mem;
+ (*ppv)->log = log;
+ (*ppv)->timeout = timeout;
+ (*ppv)->fallback_pto = 0;
+ (*ppv)->started_ts = UINT64_MAX;
+ (*ppv)->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT;
+ (*ppv)->round = 0;
+ (*ppv)->flags = flags;
+
+ return 0;
+}
+
+void ngtcp2_pv_del(ngtcp2_pv *pv) {
+ if (pv == NULL) {
+ return;
+ }
+ ngtcp2_mem_free(pv->mem, pv);
+}
+
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags,
+ ngtcp2_tstamp ts) {
+ ngtcp2_pv_entry *ent;
+
+ assert(pv->probe_pkt_left);
+
+ if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
+ pv->started_ts = ts;
+ }
+
+ ent = ngtcp2_ringbuf_push_back(&pv->ents.rb);
+ ngtcp2_pv_entry_init(ent, data, expiry, flags);
+
+ pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER;
+ --pv->probe_pkt_left;
+}
+
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
+ size_t len = ngtcp2_ringbuf_len(&pv->ents.rb);
+ size_t i;
+ ngtcp2_pv_entry *ent;
+
+ if (len == 0) {
+ return NGTCP2_ERR_INVALID_STATE;
+ }
+
+ for (i = 0; i < len; ++i) {
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, i);
+ if (memcmp(ent->data, data, sizeof(ent->data)) == 0) {
+ *pflags = ent->flags;
+ ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated");
+ return 0;
+ }
+ }
+
+ return NGTCP2_ERR_INVALID_ARGUMENT;
+}
+
+void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_pv_entry *ent;
+
+ if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
+ return;
+ }
+
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
+
+ if (ent->expiry > ts) {
+ return;
+ }
+
+ ++pv->round;
+ pv->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT;
+}
+
+int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv) {
+ return pv->probe_pkt_left > 0;
+}
+
+int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp t;
+ ngtcp2_pv_entry *ent;
+
+ if (pv->started_ts == UINT64_MAX) {
+ return 0;
+ }
+
+ assert(ngtcp2_ringbuf_len(&pv->ents.rb));
+
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
+
+ t = pv->started_ts + pv->timeout;
+ t = ngtcp2_max(t, ent->expiry);
+
+ return t <= ts;
+}
+
+ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) {
+ ngtcp2_pv_entry *ent;
+
+ if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) ||
+ ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
+ return UINT64_MAX;
+ }
+
+ ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
+
+ return ent->expiry;
+}
+
+void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp expiry = ngtcp2_pv_next_expiry(pv);
+
+ if (expiry > ts) {
+ return;
+ }
+
+ pv->flags |= NGTCP2_PV_FLAG_CANCEL_TIMER;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h
new file mode 100644
index 0000000..293cbca
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h
@@ -0,0 +1,198 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PV_H
+#define NGTCP2_PV_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_cid.h"
+#include "ngtcp2_ringbuf.h"
+
+/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that
+ ngtcp2_pv can contain. It must be power of 2. */
+#define NGTCP2_PV_MAX_ENTRIES 8
+/* NGTCP2_PV_NUM_PROBE_PKT is the number of probe packets containing
+ PATH_CHALLENGE sent at a time. */
+#define NGTCP2_PV_NUM_PROBE_PKT 2
+
+typedef struct ngtcp2_log ngtcp2_log;
+
+typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
+
+/* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00u
+/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which
+ contains PATH_CHALLENGE is undersized (< 1200 bytes) */
+#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01u
+
+typedef struct ngtcp2_pv_entry {
+ /* expiry is the timestamp when this PATH_CHALLENGE expires. */
+ ngtcp2_tstamp expiry;
+ /* flags is zero or more of NGTCP2_PV_ENTRY_FLAG_*. */
+ uint8_t flags;
+ /* data is a byte string included in PATH_CHALLENGE. */
+ uint8_t data[8];
+} ngtcp2_pv_entry;
+
+void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags);
+
+/* NGTCP2_PV_FLAG_NONE indicates no flag is set. */
+#define NGTCP2_PV_FLAG_NONE 0x00u
+/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path
+ validation should be ignored entirely. */
+#define NGTCP2_PV_FLAG_DONT_CARE 0x01u
+/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is
+ cancelled. */
+#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u
+/* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID is
+ available in ngtcp2_pv. If path validation fails, fallback to the
+ fallback DCID. If path validation succeeds, fallback DCID is
+ retired if it does not equal to the current DCID. */
+#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u
+/* NGTCP2_PV_FLAG_MTU_PROBE indicates that a validation must probe
+ least MTU that QUIC requires, which is 1200 bytes. If it fails, a
+ path is not viable. */
+#define NGTCP2_PV_FLAG_MTU_PROBE 0x08u
+/* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to
+ server's preferred address. This flag is only used by client. */
+#define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u
+
+typedef struct ngtcp2_pv ngtcp2_pv;
+
+ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES,
+ sizeof(ngtcp2_pv_entry));
+/*
+ * ngtcp2_pv is the context of a single path validation.
+ */
+struct ngtcp2_pv {
+ const ngtcp2_mem *mem;
+ ngtcp2_log *log;
+ /* dcid is DCID and path this path validation uses. */
+ ngtcp2_dcid dcid;
+ /* fallback_dcid is the usually validated DCID and used as a
+ fallback if this path validation fails. */
+ ngtcp2_dcid fallback_dcid;
+ /* ents is the ring buffer of ngtcp2_pv_entry */
+ ngtcp2_static_ringbuf_pv_ents ents;
+ /* timeout is the duration within which this path validation should
+ succeed. */
+ ngtcp2_duration timeout;
+ /* fallback_pto is PTO of fallback connection. */
+ ngtcp2_duration fallback_pto;
+ /* started_ts is the timestamp this path validation starts. */
+ ngtcp2_tstamp started_ts;
+ /* round is the number of times that probe_pkt_left is reset. */
+ size_t round;
+ /* probe_pkt_left is the number of probe packets containing
+ PATH_CHALLENGE which can be send without waiting for an
+ expiration of a previous flight. */
+ size_t probe_pkt_left;
+ /* flags is bitwise-OR of zero or more of NGTCP2_PV_FLAG_*. */
+ uint8_t flags;
+};
+
+/*
+ * ngtcp2_pv_new creates new ngtcp2_pv object and assigns its pointer
+ * to |*ppv|. This function makes a copy of |dcid|. |timeout| is a
+ * duration within which this path validation must succeed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
+ ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pv_del deallocates |pv|. This function frees memory |pv|
+ * points too.
+ */
+void ngtcp2_pv_del(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the
+ * expiry time of the entry.
+ */
+void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
+ ngtcp2_tstamp expiry, uint8_t flags, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_full returns nonzero if |pv| is full of ngtcp2_pv_entry.
+ */
+int ngtcp2_pv_full(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_validate validates that the received |data| matches the
+ * one of the existing entry. The flag of ngtcp2_pv_entry that
+ * matches |data| is assigned to |*pflags| if this function succeeds.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_PATH_VALIDATION_FAILED
+ * path validation has failed and must be abandoned
+ * NGTCP2_ERR_INVALID_STATE
+ * |pv| includes no entry
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ * |pv| does not have an entry which has |data| and |path|
+ */
+int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data);
+
+/*
+ * ngtcp2_pv_handle_entry_expiry checks expiry of existing entries.
+ */
+void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_should_send_probe returns nonzero if new entry can be
+ * added by ngtcp2_pv_add_entry.
+ */
+int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_validation_timed_out returns nonzero if the path
+ * validation fails because of timeout.
+ */
+int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pv_next_expiry returns the earliest expiry.
+ */
+ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv);
+
+/*
+ * ngtcp2_pv_cancel_expired_timer cancels the expired timer.
+ */
+void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts);
+
+#endif /* NGTCP2_PV_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
new file mode 100644
index 0000000..5107f44
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
@@ -0,0 +1,1218 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_qlog.h"
+
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_net.h"
+#include "ngtcp2_unreachable.h"
+
+void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
+ ngtcp2_tstamp ts, void *user_data) {
+ qlog->write = write;
+ qlog->ts = qlog->last_ts = ts;
+ qlog->user_data = user_data;
+}
+
+#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), sizeof(S) - 1)
+
+static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data,
+ size_t datalen) {
+ *p++ = '"';
+ if (datalen) {
+ p = ngtcp2_cpymem(p, data, datalen);
+ }
+ *p++ = '"';
+ return p;
+}
+
+#define write_string(DEST, S) \
+ write_string_impl((DEST), (const uint8_t *)(S), sizeof(S) - 1)
+
+#define NGTCP2_LOWER_XDIGITS "0123456789abcdef"
+
+static uint8_t *write_hex(uint8_t *p, const uint8_t *data, size_t datalen) {
+ const uint8_t *b = data, *end = data + datalen;
+ *p++ = '"';
+ for (; b != end; ++b) {
+ *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4];
+ *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf];
+ }
+ *p++ = '"';
+ return p;
+}
+
+static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) {
+ return write_hex(p, cid->data, cid->datalen);
+}
+
+static uint8_t *write_number(uint8_t *p, uint64_t n) {
+ size_t nlen = 0;
+ uint64_t t;
+ uint8_t *res;
+
+ if (n == 0) {
+ *p++ = '0';
+ return p;
+ }
+ for (t = n; t; t /= 10, ++nlen)
+ ;
+ p += nlen;
+ res = p;
+ for (; n; n /= 10) {
+ *--p = (uint8_t)((n % 10) + '0');
+ }
+ return res;
+}
+
+static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) {
+ return write_number(p, ts / NGTCP2_MILLISECONDS);
+}
+
+static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) {
+ return write_number(p, duration / NGTCP2_MILLISECONDS);
+}
+
+static uint8_t *write_bool(uint8_t *p, int b) {
+ if (b) {
+ return ngtcp2_cpymem(p, "true", sizeof("true") - 1);
+ }
+ return ngtcp2_cpymem(p, "false", sizeof("false") - 1);
+}
+
+static uint8_t *write_pair_impl(uint8_t *p, const uint8_t *name, size_t namelen,
+ const ngtcp2_vec *value) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_string_impl(p, value->base, value->len);
+}
+
+#define write_pair(DEST, NAME, VALUE) \
+ write_pair_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, (VALUE))
+
+static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, const uint8_t *value,
+ size_t valuelen) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_hex(p, value, valuelen);
+}
+
+#define write_pair_hex(DEST, NAME, VALUE, VALUELEN) \
+ write_pair_hex_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE), (VALUELEN))
+
+static uint8_t *write_pair_number_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, uint64_t value) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_number(p, value);
+}
+
+#define write_pair_number(DEST, NAME, VALUE) \
+ write_pair_number_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen,
+ ngtcp2_duration duration) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_duration(p, duration);
+}
+
+#define write_pair_duration(DEST, NAME, VALUE) \
+ write_pair_duration_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, ngtcp2_tstamp ts) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_tstamp(p, ts);
+}
+
+#define write_pair_tstamp(DEST, NAME, VALUE) \
+ write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, int b) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_bool(p, b);
+}
+
+#define write_pair_bool(DEST, NAME, VALUE) \
+ write_pair_bool_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name,
+ size_t namelen, const ngtcp2_cid *cid) {
+ p = write_string_impl(p, name, namelen);
+ *p++ = ':';
+ return write_cid(p, cid);
+}
+
+#define write_pair_cid(DEST, NAME, VALUE) \
+ write_pair_cid_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \
+ (VALUE))
+
+#define ngtcp2_make_vec_lit(S) \
+ { (uint8_t *)(S), sizeof((S)) - 1 }
+
+static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) {
+ p = write_verbatim(
+ p, "\"common_fields\":{\"protocol_type\":[\"QUIC\"],\"time_format\":"
+ "\"relative\",\"reference_time\":0,\"group_id\":");
+ p = write_cid(p, odcid);
+ *p++ = '}';
+ return p;
+}
+
+static uint8_t *write_trace(uint8_t *p, int server, const ngtcp2_cid *odcid) {
+ p = write_verbatim(
+ p, "\"trace\":{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":");
+ if (server) {
+ p = write_string(p, "server");
+ } else {
+ p = write_string(p, "client");
+ }
+ p = write_verbatim(p, "},");
+ p = write_common_fields(p, odcid);
+ *p++ = '}';
+ return p;
+}
+
+void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ p = write_verbatim(
+ p, "\x1e{\"qlog_format\":\"JSON-SEQ\",\"qlog_version\":\"0.3\",");
+ p = write_trace(p, server, odcid);
+ p = write_verbatim(p, "}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_end(ngtcp2_qlog *qlog) {
+ uint8_t buf[1] = {0};
+
+ if (!qlog->write) {
+ return;
+ }
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, &buf, 0);
+}
+
+static ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial");
+static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake");
+static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT");
+static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT");
+static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry");
+static ngtcp2_vec vec_pkt_type_version_negotiation =
+ ngtcp2_make_vec_lit("version_negotiation");
+static ngtcp2_vec vec_pkt_type_stateless_reset =
+ ngtcp2_make_vec_lit("stateless_reset");
+static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown");
+
+static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
+ if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
+ switch (hd->type) {
+ case NGTCP2_PKT_INITIAL:
+ return &vec_pkt_type_initial;
+ case NGTCP2_PKT_HANDSHAKE:
+ return &vec_pkt_type_handshake;
+ case NGTCP2_PKT_0RTT:
+ return &vec_pkt_type_0rtt;
+ case NGTCP2_PKT_RETRY:
+ return &vec_pkt_type_retry;
+ default:
+ return &vec_pkt_type_unknown;
+ }
+ }
+
+ switch (hd->type) {
+ case NGTCP2_PKT_VERSION_NEGOTIATION:
+ return &vec_pkt_type_version_negotiation;
+ case NGTCP2_PKT_STATELESS_RESET:
+ return &vec_pkt_type_stateless_reset;
+ case NGTCP2_PKT_1RTT:
+ return &vec_pkt_type_1rtt;
+ default:
+ return &vec_pkt_type_unknown;
+ }
+}
+
+static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) {
+ /*
+ * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}}
+ */
+#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95
+
+ *p++ = '{';
+ p = write_pair(p, "packet_type", qlog_pkt_type(hd));
+ *p++ = ',';
+ p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num);
+ if (hd->type == NGTCP2_PKT_INITIAL && hd->tokenlen) {
+ p = write_verbatim(p, ",\"token\":{");
+ p = write_pair_hex(p, "data", hd->token, hd->tokenlen);
+ *p++ = '}';
+ }
+ /* TODO Write DCIL and DCID */
+ /* TODO Write SCIL and SCID */
+ *p++ = '}';
+ return p;
+}
+
+static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) {
+ (void)fr;
+
+ /* {"frame_type":"padding"} */
+#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24
+
+ return write_verbatim(p, "{\"frame_type\":\"padding\"}");
+}
+
+static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) {
+ (void)fr;
+
+ /* {"frame_type":"ping"} */
+#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21
+
+ return write_verbatim(p, "{\"frame_type\":\"ping\"}");
+}
+
+static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
+ int64_t largest_ack, min_ack;
+ size_t i;
+ const ngtcp2_ack_range *range;
+
+ /*
+ * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]}
+ *
+ * each range:
+ * [0000000000000000000,0000000000000000000],
+ *
+ * ecn:
+ * ,"ect1":0000000000000000000,"ect0":0000000000000000000,"ce":0000000000000000000
+ */
+#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70
+#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 42
+#define NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD 79
+
+ p = write_verbatim(p, "{\"frame_type\":\"ack\",");
+ p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled);
+ p = write_verbatim(p, ",\"acked_ranges\":[");
+
+ largest_ack = fr->largest_ack;
+ min_ack = fr->largest_ack - (int64_t)fr->first_ack_range;
+
+ *p++ = '[';
+ p = write_number(p, (uint64_t)min_ack);
+ if (largest_ack != min_ack) {
+ *p++ = ',';
+ p = write_number(p, (uint64_t)largest_ack);
+ }
+ *p++ = ']';
+
+ for (i = 0; i < fr->rangecnt; ++i) {
+ range = &fr->ranges[i];
+ largest_ack = min_ack - (int64_t)range->gap - 2;
+ min_ack = largest_ack - (int64_t)range->len;
+ *p++ = ',';
+ *p++ = '[';
+ p = write_number(p, (uint64_t)min_ack);
+ if (largest_ack != min_ack) {
+ *p++ = ',';
+ p = write_number(p, (uint64_t)largest_ack);
+ }
+ *p++ = ']';
+ }
+
+ *p++ = ']';
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ *p++ = ',';
+ p = write_pair_number(p, "ect1", fr->ecn.ect1);
+ *p++ = ',';
+ p = write_pair_number(p, "ect0", fr->ecn.ect0);
+ *p++ = ',';
+ p = write_pair_number(p, "ce", fr->ecn.ce);
+ }
+
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_reset_stream_frame(uint8_t *p,
+ const ngtcp2_reset_stream *fr) {
+ /*
+ * {"frame_type":"reset_stream","stream_id":0000000000000000000,"error_code":0000000000000000000,"final_size":0000000000000000000}
+ */
+#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 127
+
+ p = write_verbatim(p, "{\"frame_type\":\"reset_stream\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->app_error_code);
+ *p++ = ',';
+ p = write_pair_number(p, "final_size", fr->final_size);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_stop_sending_frame(uint8_t *p,
+ const ngtcp2_stop_sending *fr) {
+ /*
+ * {"frame_type":"stop_sending","stream_id":0000000000000000000,"error_code":0000000000000000000}
+ */
+#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 94
+
+ p = write_verbatim(p, "{\"frame_type\":\"stop_sending\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->app_error_code);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) {
+ /*
+ * {"frame_type":"crypto","offset":0000000000000000000,"length":0000000000000000000}
+ */
+#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 81
+
+ p = write_verbatim(p, "{\"frame_type\":\"crypto\",");
+ p = write_pair_number(p, "offset", fr->offset);
+ *p++ = ',';
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) {
+ /*
+ * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}}
+ */
+#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75
+
+ p = write_verbatim(p, "{\"frame_type\":\"new_token\",");
+ p = write_pair_number(p, "length", fr->tokenlen);
+ p = write_verbatim(p, ",\"token\":{");
+ p = write_pair_hex(p, "data", fr->token, fr->tokenlen);
+ *p++ = '}';
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) {
+ /*
+ * {"frame_type":"stream","stream_id":0000000000000000000,"offset":0000000000000000000,"length":0000000000000000000,"fin":true}
+ */
+#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 124
+
+ p = write_verbatim(p, "{\"frame_type\":\"stream\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "offset", fr->offset);
+ *p++ = ',';
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ if (fr->fin) {
+ *p++ = ',';
+ p = write_pair_bool(p, "fin", 1);
+ }
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) {
+ /*
+ * {"frame_type":"max_data","maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 55
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_data\",");
+ p = write_pair_number(p, "maximum", fr->max_data);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_stream_data_frame(uint8_t *p,
+ const ngtcp2_max_stream_data *fr) {
+ /*
+ * {"frame_type":"max_stream_data","stream_id":0000000000000000000,"maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 94
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_stream_data\",");
+ p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id);
+ *p++ = ',';
+ p = write_pair_number(p, "maximum", fr->max_stream_data);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_max_streams_frame(uint8_t *p,
+ const ngtcp2_max_streams *fr) {
+ /*
+ * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":0000000000000000000}
+ */
+#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89
+
+ p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":");
+ if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
+ p = write_string(p, "bidirectional");
+ } else {
+ p = write_string(p, "unidirectional");
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "maximum", fr->max_streams);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_data_blocked_frame(uint8_t *p,
+ const ngtcp2_data_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"data_blocked"}
+ */
+#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 29
+
+ /* TODO log limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"data_blocked\"}");
+}
+
+static uint8_t *
+write_stream_data_blocked_frame(uint8_t *p,
+ const ngtcp2_stream_data_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"stream_data_blocked"}
+ */
+#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 36
+
+ /* TODO log limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"stream_data_blocked\"}");
+}
+
+static uint8_t *write_streams_blocked_frame(uint8_t *p,
+ const ngtcp2_streams_blocked *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"streams_blocked"}
+ */
+#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 32
+
+ /* TODO Log stream_type and limit */
+
+ return write_verbatim(p, "{\"frame_type\":\"streams_blocked\"}");
+}
+
+static uint8_t *
+write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
+ /*
+ * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}
+ */
+#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280
+
+ p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\",");
+ p = write_pair_number(p, "sequence_number", fr->seq);
+ *p++ = ',';
+ p = write_pair_number(p, "retire_prior_to", fr->retire_prior_to);
+ *p++ = ',';
+ p = write_pair_number(p, "connection_id_length", fr->cid.datalen);
+ *p++ = ',';
+ p = write_pair_cid(p, "connection_id", &fr->cid);
+ p = write_verbatim(p, ",\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", fr->stateless_reset_token,
+ sizeof(fr->stateless_reset_token));
+ *p++ = '}';
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *
+write_retire_connection_id_frame(uint8_t *p,
+ const ngtcp2_retire_connection_id *fr) {
+ /*
+ * {"frame_type":"retire_connection_id","sequence_number":0000000000000000000}
+ */
+#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 75
+
+ p = write_verbatim(p, "{\"frame_type\":\"retire_connection_id\",");
+ p = write_pair_number(p, "sequence_number", fr->seq);
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_path_challenge_frame(uint8_t *p,
+ const ngtcp2_path_challenge *fr) {
+ /*
+ * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"}
+ */
+#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57
+
+ p = write_verbatim(p, "{\"frame_type\":\"path_challenge\",");
+ p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_path_response_frame(uint8_t *p,
+ const ngtcp2_path_response *fr) {
+ /*
+ * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"}
+ */
+#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56
+
+ p = write_verbatim(p, "{\"frame_type\":\"path_response\",");
+ p = write_pair_hex(p, "data", fr->data, sizeof(fr->data));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *
+write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) {
+ /*
+ * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000}
+ */
+#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131
+
+ p = write_verbatim(p,
+ "{\"frame_type\":\"connection_close\",\"error_space\":");
+ if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
+ p = write_string(p, "transport");
+ } else {
+ p = write_string(p, "application");
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "error_code", fr->error_code);
+ *p++ = ',';
+ p = write_pair_number(p, "raw_error_code", fr->error_code);
+ /* TODO Write reason by escaping non-printables */
+ /* TODO Write trigger_frame_type */
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *write_handshake_done_frame(uint8_t *p,
+ const ngtcp2_handshake_done *fr) {
+ (void)fr;
+
+ /*
+ * {"frame_type":"handshake_done"}
+ */
+#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31
+
+ return write_verbatim(p, "{\"frame_type\":\"handshake_done\"}");
+}
+
+static uint8_t *write_datagram_frame(uint8_t *p, const ngtcp2_datagram *fr) {
+ /*
+ * {"frame_type":"datagram","length":0000000000000000000}
+ */
+#define NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD 54
+
+ p = write_verbatim(p, "{\"frame_type\":\"datagram\",");
+ p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt));
+ *p++ = '}';
+
+ return p;
+}
+
+static uint8_t *qlog_write_time(ngtcp2_qlog *qlog, uint8_t *p) {
+ return write_pair_tstamp(p, "time", qlog->last_ts - qlog->ts);
+}
+
+static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) {
+ uint8_t *p;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_reset(&qlog->buf);
+ p = qlog->buf.last;
+
+ *p++ = '\x1e';
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(p, ",\"name\":");
+ if (sent) {
+ p = write_string(p, "transport:packet_sent");
+ } else {
+ p = write_string(p, "transport:packet_received");
+ }
+ p = write_verbatim(p, ",\"data\":{\"frames\":[");
+ qlog->buf.last = p;
+}
+
+static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ uint8_t *p = qlog->buf.last;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ /*
+ * ],"header":,"raw":{"length":0000000000000000000}}}
+ *
+ * plus, terminating LF
+ */
+#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \
+ (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD)
+
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->tokenlen * 2) {
+ return;
+ }
+
+ assert(ngtcp2_buf_len(&qlog->buf));
+
+ /* Eat last ',' */
+ if (*(p - 1) == ',') {
+ --p;
+ }
+
+ p = write_verbatim(p, "],\"header\":");
+ p = write_pkt_hd(p, hd);
+ p = write_verbatim(p, ",\"raw\":{\"length\":");
+ p = write_number(p, pktlen);
+ p = write_verbatim(p, "}}}\n");
+
+ qlog->buf.last = p;
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, qlog->buf.pos,
+ ngtcp2_buf_len(&qlog->buf));
+}
+
+void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
+ uint8_t *p = qlog->buf.last;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ switch (fr->type) {
+ case NGTCP2_FRAME_PADDING:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_padding_frame(p, &fr->padding);
+ break;
+ case NGTCP2_FRAME_PING:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_ping_frame(p, &fr->ping);
+ break;
+ case NGTCP2_FRAME_ACK:
+ case NGTCP2_FRAME_ACK_ECN:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD +
+ (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN
+ ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD
+ : 0) +
+ NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.rangecnt) + 1) {
+ return;
+ }
+ p = write_ack_frame(p, &fr->ack);
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_reset_stream_frame(p, &fr->reset_stream);
+ break;
+ case NGTCP2_FRAME_STOP_SENDING:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_stop_sending_frame(p, &fr->stop_sending);
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_crypto_frame(p, &fr->crypto);
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + fr->new_token.tokenlen * 2 + 1) {
+ return;
+ }
+ p = write_new_token_frame(p, &fr->new_token);
+ break;
+ case NGTCP2_FRAME_STREAM:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_stream_frame(p, &fr->stream);
+ break;
+ case NGTCP2_FRAME_MAX_DATA:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_max_data_frame(p, &fr->max_data);
+ break;
+ case NGTCP2_FRAME_MAX_STREAM_DATA:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_max_stream_data_frame(p, &fr->max_stream_data);
+ break;
+ case NGTCP2_FRAME_MAX_STREAMS_BIDI:
+ case NGTCP2_FRAME_MAX_STREAMS_UNI:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_max_streams_frame(p, &fr->max_streams);
+ break;
+ case NGTCP2_FRAME_DATA_BLOCKED:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_data_blocked_frame(p, &fr->data_blocked);
+ break;
+ case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked);
+ break;
+ case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
+ case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_streams_blocked_frame(p, &fr->streams_blocked);
+ break;
+ case NGTCP2_FRAME_NEW_CONNECTION_ID:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_new_connection_id_frame(p, &fr->new_connection_id);
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_retire_connection_id_frame(p, &fr->retire_connection_id);
+ break;
+ case NGTCP2_FRAME_PATH_CHALLENGE:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_path_challenge_frame(p, &fr->path_challenge);
+ break;
+ case NGTCP2_FRAME_PATH_RESPONSE:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_path_response_frame(p, &fr->path_response);
+ break;
+ case NGTCP2_FRAME_CONNECTION_CLOSE:
+ case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_connection_close_frame(p, &fr->connection_close);
+ break;
+ case NGTCP2_FRAME_HANDSHAKE_DONE:
+ if (ngtcp2_buf_left(&qlog->buf) <
+ NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_handshake_done_frame(p, &fr->handshake_done);
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) {
+ return;
+ }
+ p = write_datagram_frame(p, &fr->datagram);
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ *p++ = ',';
+
+ qlog->buf.last = p;
+}
+
+void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog) {
+ qlog_pkt_write_start(qlog, /* sent = */ 0);
+}
+
+void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ qlog_pkt_write_end(qlog, hd, pktlen);
+}
+
+void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog) {
+ qlog_pkt_write_start(qlog, /* sent = */ 1);
+}
+
+void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen) {
+ qlog_pkt_write_end(qlog, hd, pktlen);
+}
+
+void ngtcp2_qlog_parameters_set_transport_params(
+ ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server,
+ ngtcp2_qlog_side side) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+ const ngtcp2_preferred_addr *paddr;
+ const ngtcp2_sockaddr_in *sa_in;
+ const ngtcp2_sockaddr_in6 *sa_in6;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '\x1e';
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(
+ p, ",\"name\":\"transport:parameters_set\",\"data\":{\"owner\":");
+
+ if (side == NGTCP2_QLOG_SIDE_LOCAL) {
+ p = write_string(p, "local");
+ } else {
+ p = write_string(p, "remote");
+ }
+
+ *p++ = ',';
+ p = write_pair_cid(p, "initial_source_connection_id", &params->initial_scid);
+ *p++ = ',';
+ if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) {
+ p = write_pair_cid(p, "original_destination_connection_id",
+ &params->original_dcid);
+ *p++ = ',';
+ }
+ if (params->retry_scid_present) {
+ p = write_pair_cid(p, "retry_source_connection_id", &params->retry_scid);
+ *p++ = ',';
+ }
+ if (params->stateless_reset_token_present) {
+ p = write_verbatim(p, "\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", params->stateless_reset_token,
+ sizeof(params->stateless_reset_token));
+ *p++ = '}';
+ *p++ = ',';
+ }
+ p = write_pair_bool(p, "disable_active_migration",
+ params->disable_active_migration);
+ *p++ = ',';
+ p = write_pair_duration(p, "max_idle_timeout", params->max_idle_timeout);
+ *p++ = ',';
+ p = write_pair_number(p, "max_udp_payload_size",
+ params->max_udp_payload_size);
+ *p++ = ',';
+ p = write_pair_number(p, "ack_delay_exponent", params->ack_delay_exponent);
+ *p++ = ',';
+ p = write_pair_duration(p, "max_ack_delay", params->max_ack_delay);
+ *p++ = ',';
+ p = write_pair_number(p, "active_connection_id_limit",
+ params->active_connection_id_limit);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_data", params->initial_max_data);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_bidi_local",
+ params->initial_max_stream_data_bidi_local);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_bidi_remote",
+ params->initial_max_stream_data_bidi_remote);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_stream_data_uni",
+ params->initial_max_stream_data_uni);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_streams_bidi",
+ params->initial_max_streams_bidi);
+ *p++ = ',';
+ p = write_pair_number(p, "initial_max_streams_uni",
+ params->initial_max_streams_uni);
+ if (params->preferred_address_present) {
+ *p++ = ',';
+ paddr = &params->preferred_address;
+ p = write_string(p, "preferred_address");
+ *p++ = ':';
+ *p++ = '{';
+
+ if (paddr->ipv4_present) {
+ sa_in = &paddr->ipv4;
+
+ p = write_pair_hex(p, "ip_v4", (const uint8_t *)&sa_in->sin_addr,
+ sizeof(sa_in->sin_addr));
+ *p++ = ',';
+ p = write_pair_number(p, "port_v4", ngtcp2_ntohs(sa_in->sin_port));
+ *p++ = ',';
+ }
+
+ if (paddr->ipv6_present) {
+ sa_in6 = &paddr->ipv6;
+
+ p = write_pair_hex(p, "ip_v6", (const uint8_t *)&sa_in6->sin6_addr,
+ sizeof(sa_in6->sin6_addr));
+ *p++ = ',';
+ p = write_pair_number(p, "port_v6", ngtcp2_ntohs(sa_in6->sin6_port));
+ *p++ = ',';
+ }
+
+ p = write_pair_cid(p, "connection_id", &paddr->cid);
+ p = write_verbatim(p, ",\"stateless_reset_token\":{");
+ p = write_pair_hex(p, "data", paddr->stateless_reset_token,
+ sizeof(paddr->stateless_reset_token));
+ *p++ = '}';
+ *p++ = '}';
+ }
+ *p++ = ',';
+ p = write_pair_number(p, "max_datagram_frame_size",
+ params->max_datagram_frame_size);
+ *p++ = ',';
+ p = write_pair_bool(p, "grease_quic_bit", params->grease_quic_bit);
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
+ const ngtcp2_conn_stat *cstat) {
+ uint8_t buf[1024];
+ uint8_t *p = buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '\x1e';
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{");
+
+ if (cstat->min_rtt != UINT64_MAX) {
+ p = write_pair_duration(p, "min_rtt", cstat->min_rtt);
+ *p++ = ',';
+ }
+ p = write_pair_duration(p, "smoothed_rtt", cstat->smoothed_rtt);
+ *p++ = ',';
+ p = write_pair_duration(p, "latest_rtt", cstat->latest_rtt);
+ *p++ = ',';
+ p = write_pair_duration(p, "rtt_variance", cstat->rttvar);
+ *p++ = ',';
+ p = write_pair_number(p, "pto_count", cstat->pto_count);
+ *p++ = ',';
+ p = write_pair_number(p, "congestion_window", cstat->cwnd);
+ *p++ = ',';
+ p = write_pair_number(p, "bytes_in_flight", cstat->bytes_in_flight);
+ if (cstat->ssthresh != UINT64_MAX) {
+ *p++ = ',';
+ p = write_pair_number(p, "ssthresh", cstat->ssthresh);
+ }
+
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
+ uint8_t buf[256];
+ uint8_t *p = buf;
+ ngtcp2_pkt_hd hd = {0};
+
+ if (!qlog->write) {
+ return;
+ }
+
+ *p++ = '\x1e';
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(
+ p, ",\"name\":\"recovery:packet_lost\",\"data\":{\"header\":");
+
+ hd.type = ent->hd.type;
+ hd.flags = ent->hd.flags;
+ hd.pkt_num = ent->hd.pkt_num;
+
+ p = write_pkt_hd(p, &hd);
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_pkt_retry *retry) {
+ uint8_t rawbuf[1024];
+ ngtcp2_buf buf;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+ *buf.last++ = '\x1e';
+ *buf.last++ = '{';
+ buf.last = qlog_write_time(qlog, buf.last);
+ buf.last = write_verbatim(
+ buf.last,
+ ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+
+ if (ngtcp2_buf_left(&buf) <
+ NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->tokenlen * 2 +
+ sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 +
+ retry->tokenlen * 2) {
+ return;
+ }
+
+ buf.last = write_pkt_hd(buf.last, hd);
+ buf.last = write_verbatim(buf.last, ",\"retry_token\":{");
+ buf.last = write_pair_hex(buf.last, "data", retry->token, retry->tokenlen);
+ buf.last = write_verbatim(buf.last, "}}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+ ngtcp2_buf_len(&buf));
+}
+
+void ngtcp2_qlog_stateless_reset_pkt_received(
+ ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) {
+ uint8_t buf[256];
+ uint8_t *p = buf;
+ ngtcp2_pkt_hd hd = {0};
+
+ if (!qlog->write) {
+ return;
+ }
+
+ hd.type = NGTCP2_PKT_STATELESS_RESET;
+
+ *p++ = '\x1e';
+ *p++ = '{';
+ p = qlog_write_time(qlog, p);
+ p = write_verbatim(
+ p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+ p = write_pkt_hd(p, &hd);
+ *p++ = ',';
+ p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token,
+ NGTCP2_STATELESS_RESET_TOKENLEN);
+ p = write_verbatim(p, "}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
+ (size_t)(p - buf));
+}
+
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv,
+ size_t nsv) {
+ uint8_t rawbuf[512];
+ ngtcp2_buf buf;
+ size_t i;
+ uint32_t v;
+
+ if (!qlog->write) {
+ return;
+ }
+
+ ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+ *buf.last++ = '\x1e';
+ *buf.last++ = '{';
+ buf.last = qlog_write_time(qlog, buf.last);
+ buf.last = write_verbatim(
+ buf.last,
+ ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+ buf.last = write_pkt_hd(buf.last, hd);
+ buf.last = write_verbatim(buf.last, ",\"supported_versions\":[");
+
+ if (nsv) {
+ if (ngtcp2_buf_left(&buf) <
+ (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) {
+ return;
+ }
+
+ v = ngtcp2_htonl(sv[0]);
+ buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+
+ for (i = 1; i < nsv; ++i) {
+ *buf.last++ = ',';
+ v = ngtcp2_htonl(sv[i]);
+ buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+ }
+ }
+
+ buf.last = write_verbatim(buf.last, "]}}\n");
+
+ qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+ ngtcp2_buf_len(&buf));
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h
new file mode 100644
index 0000000..b9107c0
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h
@@ -0,0 +1,161 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_QLOG_H
+#define NGTCP2_QLOG_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_buf.h"
+#include "ngtcp2_rtb.h"
+
+/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for
+ qlog. */
+#define NGTCP2_QLOG_BUFLEN 4096
+
+typedef enum ngtcp2_qlog_side {
+ NGTCP2_QLOG_SIDE_LOCAL,
+ NGTCP2_QLOG_SIDE_REMOTE,
+} ngtcp2_qlog_side;
+
+typedef struct ngtcp2_qlog {
+ /* write is a callback function to write qlog. */
+ ngtcp2_qlog_write write;
+ /* ts is the initial timestamp */
+ ngtcp2_tstamp ts;
+ /* last_ts is the timestamp observed last time. */
+ ngtcp2_tstamp last_ts;
+ /* buf is a heap allocated buffer to write exclusively
+ packet_received and packet_sent. */
+ ngtcp2_buf buf;
+ /* user_data is an opaque pointer which is passed to write
+ callback. */
+ void *user_data;
+} ngtcp2_qlog;
+
+/*
+ * ngtcp2_qlog_init initializes |qlog|.
+ */
+void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
+ ngtcp2_tstamp ts, void *user_data);
+
+/*
+ * ngtcp2_qlog_start writes qlog preamble.
+ */
+void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server);
+
+/*
+ * ngtcp2_qlog_end writes closing part of qlog.
+ */
+void ngtcp2_qlog_end(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_write_frame writes |fr| to qlog->buf.
+ * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must
+ * be called before calling this function.
+ */
+void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr);
+
+/*
+ * ngtcp2_qlog_pkt_received_start starts to write packet_received
+ * event. It initializes qlog->buf. It writes qlog to qlog->buf.
+ * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to
+ * write callback.
+ */
+void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_pkt_received_end ends packet_received event and sends
+ * the content of qlog->buf to qlog->write callback.
+ */
+void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen);
+
+/*
+ * ngtcp2_qlog_pkt_sent_start starts to write packet_sent event. It
+ * initializes qlog->buf. It writes qlog to qlog->buf.
+ * ngtcp2_qlog_pkt_sent_end will flush the content of qlog->buf to
+ * write callback.
+ */
+void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog);
+
+/*
+ * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the
+ * content of qlog->buf to qlog->write callback.
+ */
+void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ size_t pktlen);
+
+/*
+ * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog
+ * as parameters_set event. |server| is nonzero if the local endpoint
+ * is server. If |local| is nonzero, it is "owner" field becomes
+ * "local", otherwise "remote".
+ */
+void ngtcp2_qlog_parameters_set_transport_params(
+ ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server,
+ ngtcp2_qlog_side side);
+
+/*
+ * ngtcp2_qlog_metrics_updated writes metrics_updated event of
+ * recovery category.
+ */
+void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog,
+ const ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_qlog_pkt_lost writes packet_lost event.
+ */
+void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent);
+
+/*
+ * ngtcp2_qlog_retry_pkt_received writes packet_received event for a
+ * received Retry packet.
+ */
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_pkt_retry *retry);
+
+/*
+ * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received
+ * event for a received Stateless Reset packet.
+ */
+void ngtcp2_qlog_stateless_reset_pkt_received(
+ ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr);
+
+/*
+ * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received
+ * event for a received Version Negotiation packet.
+ */
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv,
+ size_t nsv);
+
+#endif /* NGTCP2_QLOG_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c
new file mode 100644
index 0000000..9379496
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c
@@ -0,0 +1,61 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_range.h"
+#include "ngtcp2_macro.h"
+
+void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) {
+ r->begin = begin;
+ r->end = end;
+}
+
+ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a,
+ const ngtcp2_range *b) {
+ ngtcp2_range r = {0, 0};
+ uint64_t begin = ngtcp2_max(a->begin, b->begin);
+ uint64_t end = ngtcp2_min(a->end, b->end);
+ if (begin < end) {
+ ngtcp2_range_init(&r, begin, end);
+ }
+ return r;
+}
+
+uint64_t ngtcp2_range_len(const ngtcp2_range *r) { return r->end - r->begin; }
+
+int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) {
+ return a->begin == b->begin && a->end == b->end;
+}
+
+void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right,
+ const ngtcp2_range *a, const ngtcp2_range *b) {
+ /* Assume that b is included in a */
+ left->begin = a->begin;
+ left->end = b->begin;
+ right->begin = b->end;
+ right->end = a->end;
+}
+
+int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) {
+ return a->end <= b->end;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h
new file mode 100644
index 0000000..a776c4e
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h
@@ -0,0 +1,80 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RANGE_H
+#define NGTCP2_RANGE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_range represents half-closed range [begin, end).
+ */
+typedef struct ngtcp2_range {
+ uint64_t begin;
+ uint64_t end;
+} ngtcp2_range;
+
+/*
+ * ngtcp2_range_init initializes |r| with the range [|begin|, |end|).
+ */
+void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end);
+
+/*
+ * ngtcp2_range_intersect returns the intersection of |a| and |b|. If
+ * they do not overlap, it returns empty range.
+ */
+ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a,
+ const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_len returns the length of |r|.
+ */
+uint64_t ngtcp2_range_len(const ngtcp2_range *r);
+
+/*
+ * ngtcp2_range_eq returns nonzero if |a| equals |b|, such that
+ * a->begin == b->begin, and a->end == b->end hold.
+ */
+int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_cut returns the left and right range after removing
+ * |b| from |a|. This function assumes that |a| completely includes
+ * |b|. In other words, a->begin <= b->begin and b->end <= a->end
+ * hold.
+ */
+void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right,
+ const ngtcp2_range *a, const ngtcp2_range *b);
+
+/*
+ * ngtcp2_range_not_after returns nonzero if the right edge of |a|
+ * does not go beyond of the right edge of |b|.
+ */
+int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b);
+
+#endif /* NGTCP2_RANGE_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
new file mode 100644
index 0000000..4cb4088
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h
@@ -0,0 +1,40 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RCVRY_H
+#define NGTCP2_RCVRY_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in RFC 9002. */
+#define NGTCP2_PKT_THRESHOLD 3
+
+/* NGTCP2_GRANULARITY is kGranularity described in RFC 9002. */
+#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS
+
+#endif /* NGTCP2_RCVRY_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
new file mode 100644
index 0000000..a6b3f73
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
@@ -0,0 +1,120 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_ringbuf.h"
+
+#include <assert.h>
+#ifdef WIN32
+# include <intrin.h>
+#endif
+
+#include "ngtcp2_macro.h"
+
+#if defined(_MSC_VER) && defined(_M_ARM64)
+unsigned int __popcnt(unsigned int x) {
+ unsigned int c = 0;
+ for (; x; ++c) {
+ x &= x - 1;
+ }
+ return c;
+}
+#endif
+
+int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ const ngtcp2_mem *mem) {
+ uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size);
+ if (buf == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem);
+
+ return 0;
+}
+
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ uint8_t *buf, const ngtcp2_mem *mem) {
+#ifdef WIN32
+ assert(1 == __popcnt((unsigned int)nmemb));
+#else
+ assert(1 == __builtin_popcount((unsigned int)nmemb));
+#endif
+
+ rb->buf = buf;
+ rb->mem = mem;
+ rb->nmemb = nmemb;
+ rb->size = size;
+ rb->first = 0;
+ rb->len = 0;
+}
+
+void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) {
+ if (rb == NULL) {
+ return;
+ }
+
+ ngtcp2_mem_free(rb->mem, rb->buf);
+}
+
+void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb) {
+ rb->first = (rb->first - 1) & (rb->nmemb - 1);
+ rb->len = ngtcp2_min(rb->nmemb, rb->len + 1);
+
+ return (void *)&rb->buf[rb->first * rb->size];
+}
+
+void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) {
+ size_t offset = (rb->first + rb->len) & (rb->nmemb - 1);
+
+ if (rb->len == rb->nmemb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ } else {
+ ++rb->len;
+ }
+
+ return (void *)&rb->buf[offset * rb->size];
+}
+
+void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) {
+ rb->first = (rb->first + 1) & (rb->nmemb - 1);
+ --rb->len;
+}
+
+void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb) {
+ assert(rb->len);
+ --rb->len;
+}
+
+void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len) {
+ assert(len <= rb->nmemb);
+ rb->len = len;
+}
+
+void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset) {
+ assert(offset < rb->len);
+ offset = (rb->first + offset) & (rb->nmemb - 1);
+ return &rb->buf[offset * rb->size];
+}
+
+int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->nmemb; }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
new file mode 100644
index 0000000..16635c9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
@@ -0,0 +1,132 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RINGBUF_H
+#define NGTCP2_RINGBUF_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+typedef struct ngtcp2_ringbuf {
+ /* buf points to the underlying buffer. */
+ uint8_t *buf;
+ const ngtcp2_mem *mem;
+ /* nmemb is the number of elements that can be stored in this ring
+ buffer. */
+ size_t nmemb;
+ /* size is the size of each element. */
+ size_t size;
+ /* first is the offset to the first element. */
+ size_t first;
+ /* len is the number of elements actually stored. */
+ size_t len;
+} ngtcp2_ringbuf;
+
+/*
+ * ngtcp2_ringbuf_init initializes |rb|. |nmemb| is the number of
+ * elements that can be stored in this buffer. |size| is the size of
+ * each element. |size| must be power of 2.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and
+ * size.
+ */
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+ uint8_t *buf, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_ringbuf_free frees resources allocated for |rb|. This
+ * function does not free the memory pointed by |rb|.
+ */
+void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_push_front moves the offset to the first element in
+ the buffer backward, and returns the pointer to the element.
+ Caller can store data to the buffer pointed by the returned
+ pointer. If this action exceeds the capacity of the ring buffer,
+ the last element is silently overwritten, and rb->len remains
+ unchanged. */
+void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_push_back moves the offset to the last element in
+ the buffer forward, and returns the pointer to the element. Caller
+ can store data to the buffer pointed by the returned pointer. If
+ this action exceeds the capacity of the ring buffer, the first
+ element is silently overwritten, and rb->len remains unchanged. */
+void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb);
+
+/*
+ * ngtcp2_ringbuf_pop_front removes first element in |rb|.
+ */
+void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb);
+
+/*
+ * ngtcp2_ringbuf_pop_back removes the last element in |rb|.
+ */
+void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_ringbuf_resize changes the number of elements stored. This
+ does not change the capacity of the underlying buffer. */
+void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len);
+
+/* ngtcp2_ringbuf_get returns the pointer to the element at
+ |offset|. */
+void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset);
+
+/* ngtcp2_ringbuf_len returns the number of elements stored. */
+#define ngtcp2_ringbuf_len(RB) ((RB)->len)
+
+/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
+int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb);
+
+/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper
+ which uses a statically allocated buffer that is suitable for a
+ usage that does not change buffer size with ngtcp2_ringbuf_resize.
+ ngtcp2_ringbuf_free should never be called for rb field. */
+#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE) \
+ typedef struct ngtcp2_static_ringbuf_##NAME { \
+ ngtcp2_ringbuf rb; \
+ uint8_t buf[(NMEMB) * (SIZE)]; \
+ } ngtcp2_static_ringbuf_##NAME; \
+ \
+ static inline void ngtcp2_static_ringbuf_##NAME##_init( \
+ ngtcp2_static_ringbuf_##NAME *srb) { \
+ ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL); \
+ }
+
+#endif /* NGTCP2_RINGBUF_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
new file mode 100644
index 0000000..9c3d75d
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
@@ -0,0 +1,319 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rob.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+
+int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end,
+ const ngtcp2_mem *mem) {
+ *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap));
+ if (*pg == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pg)->range.begin = begin;
+ (*pg)->range.end = end;
+
+ return 0;
+}
+
+void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, g);
+}
+
+int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk,
+ const ngtcp2_mem *mem) {
+ *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk);
+ if (*pd == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ (*pd)->range.begin = offset;
+ (*pd)->range.end = offset + chunk;
+ (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data);
+ (*pd)->end = (*pd)->begin + chunk;
+
+ return 0;
+}
+
+void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, d);
+}
+
+int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
+ int rv;
+ ngtcp2_rob_gap *g;
+
+ ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
+
+ rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem);
+ if (rv != 0) {
+ goto fail_rob_gap_new;
+ }
+
+ rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, &g->range, g);
+ if (rv != 0) {
+ goto fail_gapksl_ksl_insert;
+ }
+
+ ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+ mem);
+
+ rob->chunk = chunk;
+ rob->mem = mem;
+
+ return 0;
+
+fail_gapksl_ksl_insert:
+ ngtcp2_rob_gap_del(g, mem);
+fail_rob_gap_new:
+ ngtcp2_ksl_free(&rob->gapksl);
+ return rv;
+}
+
+void ngtcp2_rob_free(ngtcp2_rob *rob) {
+ ngtcp2_ksl_it it;
+
+ if (rob == NULL) {
+ return;
+ }
+
+ for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem);
+ }
+
+ for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem);
+ }
+
+ ngtcp2_ksl_free(&rob->dataksl);
+ ngtcp2_ksl_free(&rob->gapksl);
+}
+
+static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t len) {
+ size_t n;
+ int rv;
+ ngtcp2_rob_data *d;
+ ngtcp2_range range = {offset, offset + len};
+ ngtcp2_ksl_it it;
+
+ for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, &range,
+ ngtcp2_ksl_range_exclusive_compar);
+ len; ngtcp2_ksl_it_next(&it)) {
+ if (ngtcp2_ksl_it_end(&it)) {
+ d = NULL;
+ } else {
+ d = ngtcp2_ksl_it_get(&it);
+ }
+
+ if (d == NULL || offset < d->range.begin) {
+ rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk,
+ rob->chunk, rob->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_ksl_insert(&rob->dataksl, &it, &d->range, d);
+ if (rv != 0) {
+ ngtcp2_rob_data_del(d, rob->mem);
+ return rv;
+ }
+ }
+
+ n = (size_t)ngtcp2_min((uint64_t)len, d->range.begin + rob->chunk - offset);
+ memcpy(d->begin + (offset - d->range.begin), data, n);
+ offset += n;
+ data += n;
+ len -= n;
+ }
+
+ return 0;
+}
+
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t datalen) {
+ int rv;
+ ngtcp2_rob_gap *g;
+ ngtcp2_range m, l, r, q = {offset, offset + datalen};
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, &q,
+ ngtcp2_ksl_range_exclusive_compar);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
+
+ m = ngtcp2_range_intersect(&q, &g->range);
+ if (!ngtcp2_range_len(&m)) {
+ break;
+ }
+ if (ngtcp2_range_eq(&g->range, &m)) {
+ ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range);
+ ngtcp2_rob_gap_del(g, rob->mem);
+ rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
+ (size_t)ngtcp2_range_len(&m));
+ if (rv != 0) {
+ return rv;
+ }
+
+ continue;
+ }
+ ngtcp2_range_cut(&l, &r, &g->range, &m);
+ if (ngtcp2_range_len(&l)) {
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &l);
+ g->range = l;
+
+ if (ngtcp2_range_len(&r)) {
+ ngtcp2_rob_gap *ng;
+ rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ rv = ngtcp2_ksl_insert(&rob->gapksl, &it, &ng->range, ng);
+ if (rv != 0) {
+ ngtcp2_rob_gap_del(ng, rob->mem);
+ return rv;
+ }
+ }
+ } else if (ngtcp2_range_len(&r)) {
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r);
+ g->range = r;
+ }
+ rv = rob_write_data(rob, m.begin, data + (m.begin - offset),
+ (size_t)ngtcp2_range_len(&m));
+ if (rv != 0) {
+ return rv;
+ }
+ ngtcp2_ksl_it_next(&it);
+ }
+ return 0;
+}
+
+int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) {
+ ngtcp2_rob_gap *g;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_begin(&rob->gapksl);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ g = ngtcp2_ksl_it_get(&it);
+ if (offset <= g->range.begin) {
+ break;
+ }
+ if (offset < g->range.end) {
+ ngtcp2_range r = {offset, g->range.end};
+ ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r);
+ g->range.begin = offset;
+ break;
+ }
+ ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range);
+ ngtcp2_rob_gap_del(g, rob->mem);
+ }
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ d = ngtcp2_ksl_it_get(&it);
+ if (offset < d->range.begin + rob->chunk) {
+ return 0;
+ }
+ ngtcp2_ksl_remove_hint(&rob->dataksl, &it, &it, &d->range);
+ ngtcp2_rob_data_del(d, rob->mem);
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+ uint64_t offset) {
+ ngtcp2_rob_gap *g;
+ ngtcp2_rob_data *d;
+ ngtcp2_ksl_it it;
+
+ it = ngtcp2_ksl_begin(&rob->gapksl);
+ if (ngtcp2_ksl_it_end(&it)) {
+ return 0;
+ }
+
+ g = ngtcp2_ksl_it_get(&it);
+
+ if (g->range.begin <= offset) {
+ return 0;
+ }
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
+
+ assert(d);
+ assert(d->range.begin <= offset);
+ assert(offset < d->range.begin + rob->chunk);
+
+ *pdest = d->begin + (offset - d->range.begin);
+
+ return (size_t)(ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) -
+ offset);
+}
+
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rob_data *d;
+
+ it = ngtcp2_ksl_begin(&rob->dataksl);
+ d = ngtcp2_ksl_it_get(&it);
+
+ assert(d);
+
+ if (offset + len < d->range.begin + rob->chunk) {
+ return;
+ }
+
+ ngtcp2_ksl_remove_hint(&rob->dataksl, NULL, &it, &d->range);
+ ngtcp2_rob_data_del(d, rob->mem);
+}
+
+uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) {
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl);
+ ngtcp2_rob_gap *g;
+
+ if (ngtcp2_ksl_it_end(&it)) {
+ return UINT64_MAX;
+ }
+
+ g = ngtcp2_ksl_it_get(&it);
+
+ return g->range.begin;
+}
+
+int ngtcp2_rob_data_buffered(ngtcp2_rob *rob) {
+ return ngtcp2_ksl_len(&rob->dataksl) != 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h
new file mode 100644
index 0000000..c7688df
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h
@@ -0,0 +1,197 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_ROB_H
+#define NGTCP2_ROB_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_range.h"
+#include "ngtcp2_ksl.h"
+
+/*
+ * ngtcp2_rob_gap represents the gap, which is the range of stream
+ * data that is not received yet.
+ */
+typedef struct ngtcp2_rob_gap {
+ /* range is the range of this gap. */
+ ngtcp2_range range;
+} ngtcp2_rob_gap;
+
+/*
+ * ngtcp2_rob_gap_new allocates new ngtcp2_rob_gap object, and assigns
+ * its pointer to |*pg|. The caller should call ngtcp2_rob_gap_del to
+ * delete it when it is no longer used. The range of the gap is
+ * [begin, end). |mem| is custom memory allocator to allocate memory.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_gap_del deallocates |g|. It deallocates the memory
+ * pointed by |g| it self. |mem| is custom memory allocator to
+ * deallocate memory.
+ */
+void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_data holds the buffered stream data.
+ */
+typedef struct ngtcp2_rob_data {
+ /* range is the range of this gap. */
+ ngtcp2_range range;
+ /* begin points to the buffer. */
+ uint8_t *begin;
+ /* end points to the one beyond of the last byte of the buffer */
+ uint8_t *end;
+} ngtcp2_rob_data;
+
+/*
+ * ngtcp2_rob_data_new allocates new ngtcp2_rob_data object, and
+ * assigns its pointer to |*pd|. The caller should call
+ * ngtcp2_rob_data_del to delete it when it is no longer used.
+ * |offset| is the stream offset of the first byte of this data.
+ * |chunk| is the size of the buffer. |offset| must be multiple of
+ * |chunk|. |mem| is custom memory allocator to allocate memory.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_data_del deallocates |d|. It deallocates the memory
+ * pointed by |d| itself. |mem| is custom memory allocator to
+ * deallocate memory.
+ */
+void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob is the reorder buffer which reassembles stream data
+ * received in out of order.
+ */
+typedef struct ngtcp2_rob {
+ /* gapksl maintains the range of offset which is not received
+ yet. Initially, its range is [0, UINT64_MAX). */
+ ngtcp2_ksl gapksl;
+ /* dataksl maintains the list of buffers which store received data
+ ordered by stream offset. */
+ ngtcp2_ksl dataksl;
+ /* mem is custom memory allocator */
+ const ngtcp2_mem *mem;
+ /* chunk is the size of each buffer in data field */
+ size_t chunk;
+} ngtcp2_rob;
+
+/*
+ * ngtcp2_rob_init initializes |rob|. |chunk| is the size of buffer
+ * per chunk.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rob_free frees resources allocated for |rob|.
+ */
+void ngtcp2_rob_free(ngtcp2_rob *rob);
+
+/*
+ * ngtcp2_rob_push adds new data of length |datalen| at the stream
+ * offset |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data,
+ size_t datalen);
+
+/*
+ * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It
+ * also removes data buffer if it is completely included in |offset|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset);
+
+/*
+ * ngtcp2_rob_data_at stores the pointer to the buffer of stream
+ * offset |offset| to |*pdest| if it is available, and returns the
+ * valid length of available data. If no data is available, it
+ * returns 0.
+ */
+size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest,
+ uint64_t offset);
+
+/*
+ * ngtcp2_rob_pop clears data at stream offset |offset| of length
+ * |len|.
+ *
+ * |offset| must be the offset given in ngtcp2_rob_data_at. |len|
+ * must be the return value of ngtcp2_rob_data_at when |offset| is
+ * passed.
+ *
+ * Caller should call this function from offset 0 in non-decreasing
+ * order.
+ */
+void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len);
+
+/*
+ * ngtcp2_rob_first_gap_offset returns the offset to the first gap.
+ * If there is no gap, it returns UINT64_MAX.
+ */
+uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob);
+
+/*
+ * ngtcp2_rob_data_buffered returns nonzero if any data is buffered.
+ */
+int ngtcp2_rob_data_buffered(ngtcp2_rob *rob);
+
+#endif /* NGTCP2_ROB_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
new file mode 100644
index 0000000..7b50f98
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
@@ -0,0 +1,137 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rst.h"
+
+#include <assert.h>
+
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_macro.h"
+
+void ngtcp2_rs_init(ngtcp2_rs *rs) {
+ rs->interval = UINT64_MAX;
+ rs->delivered = 0;
+ rs->prior_delivered = 0;
+ rs->prior_ts = 0;
+ rs->tx_in_flight = 0;
+ rs->lost = 0;
+ rs->prior_lost = 0;
+ rs->send_elapsed = 0;
+ rs->ack_elapsed = 0;
+ rs->is_app_limited = 0;
+}
+
+void ngtcp2_rst_init(ngtcp2_rst *rst) {
+ ngtcp2_rs_init(&rst->rs);
+ ngtcp2_window_filter_init(&rst->wf, 12);
+ rst->delivered = 0;
+ rst->delivered_ts = 0;
+ rst->first_sent_ts = 0;
+ rst->app_limited = 0;
+ rst->next_round_delivered = 0;
+ rst->round_count = 0;
+ rst->is_cwnd_limited = 0;
+ rst->lost = 0;
+}
+
+void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
+ const ngtcp2_conn_stat *cstat) {
+ if (cstat->bytes_in_flight == 0) {
+ rst->first_sent_ts = rst->delivered_ts = ent->ts;
+ }
+ ent->rst.first_sent_ts = rst->first_sent_ts;
+ ent->rst.delivered_ts = rst->delivered_ts;
+ ent->rst.delivered = rst->delivered;
+ ent->rst.is_app_limited = rst->app_limited != 0;
+ ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen;
+ ent->rst.lost = rst->lost;
+}
+
+int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat,
+ uint64_t pkt_delivered) {
+ ngtcp2_rs *rs = &rst->rs;
+ uint64_t rate;
+
+ if (rst->app_limited && rst->delivered > rst->app_limited) {
+ rst->app_limited = 0;
+ }
+
+ if (pkt_delivered >= rst->next_round_delivered) {
+ rst->next_round_delivered = pkt_delivered;
+ ++rst->round_count;
+ }
+
+ if (rs->prior_ts == 0) {
+ return 0;
+ }
+
+ rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed);
+
+ rs->delivered = rst->delivered - rs->prior_delivered;
+ rs->lost = rst->lost - rs->prior_lost;
+
+ if (rs->interval < cstat->min_rtt) {
+ rs->interval = UINT64_MAX;
+ return 0;
+ }
+
+ if (!rs->interval) {
+ return 0;
+ }
+
+ rate = rs->delivered * NGTCP2_SECONDS / rs->interval;
+
+ if (rate > ngtcp2_window_filter_get_best(&rst->wf) || !rst->app_limited) {
+ ngtcp2_window_filter_update(&rst->wf, rate, rst->round_count);
+ cstat->delivery_rate_sec = ngtcp2_window_filter_get_best(&rst->wf);
+ }
+
+ return 0;
+}
+
+void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
+ ngtcp2_tstamp ts) {
+ ngtcp2_rs *rs = &rst->rs;
+
+ rst->delivered += ent->pktlen;
+ rst->delivered_ts = ts;
+
+ if (ent->rst.delivered > rs->prior_delivered) {
+ rs->prior_delivered = ent->rst.delivered;
+ rs->prior_ts = ent->rst.delivered_ts;
+ rs->is_app_limited = ent->rst.is_app_limited;
+ rs->send_elapsed = ent->ts - ent->rst.first_sent_ts;
+ rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts;
+ rs->tx_in_flight = ent->rst.tx_in_flight;
+ rs->prior_lost = ent->rst.lost;
+ rst->first_sent_ts = ent->ts;
+ }
+}
+
+void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+ (void)rst;
+ (void)cstat;
+ /* TODO Not implemented */
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h
new file mode 100644
index 0000000..488c655
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h
@@ -0,0 +1,85 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RST_H
+#define NGTCP2_RST_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_window_filter.h"
+
+typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
+
+/**
+ * @struct
+ *
+ * ngtcp2_rs contains connection state for delivery rate estimation.
+ */
+typedef struct ngtcp2_rs {
+ ngtcp2_duration interval;
+ uint64_t delivered;
+ uint64_t prior_delivered;
+ ngtcp2_tstamp prior_ts;
+ uint64_t tx_in_flight;
+ uint64_t lost;
+ uint64_t prior_lost;
+ ngtcp2_duration send_elapsed;
+ ngtcp2_duration ack_elapsed;
+ int is_app_limited;
+} ngtcp2_rs;
+
+void ngtcp2_rs_init(ngtcp2_rs *rs);
+
+/*
+ * ngtcp2_rst implements delivery rate estimation described in
+ * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00
+ */
+typedef struct ngtcp2_rst {
+ ngtcp2_rs rs;
+ ngtcp2_window_filter wf;
+ uint64_t delivered;
+ ngtcp2_tstamp delivered_ts;
+ ngtcp2_tstamp first_sent_ts;
+ uint64_t app_limited;
+ uint64_t next_round_delivered;
+ uint64_t round_count;
+ uint64_t lost;
+ int is_cwnd_limited;
+} ngtcp2_rst;
+
+void ngtcp2_rst_init(ngtcp2_rst *rst);
+
+void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
+ const ngtcp2_conn_stat *cstat);
+int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat,
+ uint64_t pkt_delivered);
+void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
+ ngtcp2_tstamp ts);
+void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
+
+#endif /* NGTCP2_RST_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
new file mode 100644
index 0000000..7fb0cc7
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
@@ -0,0 +1,1676 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_rtb.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_conn.h"
+#include "ngtcp2_log.h"
+#include "ngtcp2_vec.h"
+#include "ngtcp2_cc.h"
+#include "ngtcp2_rcvry.h"
+#include "ngtcp2_rst.h"
+#include "ngtcp2_unreachable.h"
+
+int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) {
+ *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain));
+ if (*pfrc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_frame_chain_init(*pfrc);
+
+ return 0;
+}
+
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+ ngtcp2_objalloc *objalloc) {
+ *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc);
+ if (*pfrc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_frame_chain_init(*pfrc);
+
+ return 0;
+}
+
+int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
+ const ngtcp2_mem *mem) {
+ *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen);
+ if (*pfrc == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_frame_chain_init(*pfrc);
+
+ return 0;
+}
+
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream);
+
+ if (datacnt > 1) {
+ need = sizeof(ngtcp2_vec) * (datacnt - 1);
+
+ if (need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
+ }
+
+ return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
+}
+
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto);
+
+ if (datacnt > 1) {
+ need = sizeof(ngtcp2_vec) * (datacnt - 1);
+
+ if (need > avail) {
+ return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem);
+ }
+ }
+
+ return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
+}
+
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+ const uint8_t *token,
+ size_t tokenlen,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token);
+ int rv;
+ uint8_t *p;
+ ngtcp2_frame *fr;
+
+ if (tokenlen > avail) {
+ rv = ngtcp2_frame_chain_extralen_new(pfrc, tokenlen - avail, mem);
+ } else {
+ rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
+ }
+ if (rv != 0) {
+ return rv;
+ }
+
+ fr = &(*pfrc)->fr;
+ fr->type = NGTCP2_FRAME_NEW_TOKEN;
+
+ p = (uint8_t *)fr + sizeof(ngtcp2_new_token);
+ memcpy(p, token, tokenlen);
+
+ fr->new_token.token = p;
+ fr->new_token.tokenlen = tokenlen;
+
+ return 0;
+}
+
+void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+
+ if (frc == NULL) {
+ return;
+ }
+
+ binder = frc->binder;
+ if (binder && --binder->refcount == 0) {
+ ngtcp2_mem_free(mem, binder);
+ }
+
+ ngtcp2_mem_free(mem, frc);
+}
+
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+
+ if (frc == NULL) {
+ return;
+ }
+
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ if (frc->fr.stream.datacnt &&
+ sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ if (frc->fr.crypto.datacnt &&
+ sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ if (frc->fr.new_token.tokenlen >
+ sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) {
+ ngtcp2_frame_chain_del(frc, mem);
+
+ return;
+ }
+
+ break;
+ }
+
+ binder = frc->binder;
+ if (binder && --binder->refcount == 0) {
+ ngtcp2_mem_free(mem, binder);
+ }
+
+ frc->binder = NULL;
+
+ ngtcp2_objalloc_frame_chain_release(objalloc, frc);
+}
+
+void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) {
+ frc->next = NULL;
+ frc->binder = NULL;
+}
+
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain *next;
+
+ for (; frc; frc = next) {
+ next = frc->next;
+
+ ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem);
+ }
+}
+
+int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
+ const ngtcp2_mem *mem) {
+ *pbinder = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_frame_chain_binder));
+ if (*pbinder == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ return 0;
+}
+
+int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_binder *binder;
+ int rv;
+
+ assert(b->binder == NULL);
+
+ if (a->binder == NULL) {
+ rv = ngtcp2_frame_chain_binder_new(&binder, mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ a->binder = binder;
+ ++a->binder->refcount;
+ }
+
+ b->binder = a->binder;
+ ++b->binder->refcount;
+
+ return 0;
+}
+
+static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags) {
+ memset(ent, 0, sizeof(*ent));
+
+ ent->hd.pkt_num = hd->pkt_num;
+ ent->hd.type = hd->type;
+ ent->hd.flags = hd->flags;
+ ent->frc = frc;
+ ent->ts = ts;
+ ent->lost_ts = UINT64_MAX;
+ ent->pktlen = pktlen;
+ ent->flags = flags;
+ ent->next = NULL;
+}
+
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+ const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags,
+ ngtcp2_objalloc *objalloc) {
+ *pent = ngtcp2_objalloc_rtb_entry_get(objalloc);
+ if (*pent == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rtb_entry_init(*pent, hd, frc, ts, pktlen, flags);
+
+ return 0;
+}
+
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+ ngtcp2_objalloc *objalloc,
+ ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem) {
+ ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem);
+
+ ent->frc = NULL;
+
+ ngtcp2_objalloc_rtb_entry_release(objalloc, ent);
+}
+
+static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs > *(int64_t *)rhs;
+}
+
+void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
+ ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
+ ngtcp2_log *log, ngtcp2_qlog *qlog,
+ ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
+ rtb->rtb_entry_objalloc = rtb_entry_objalloc;
+ rtb->frc_objalloc = frc_objalloc;
+ ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem);
+ rtb->crypto = crypto;
+ rtb->rst = rst;
+ rtb->cc = cc;
+ rtb->log = log;
+ rtb->qlog = qlog;
+ rtb->mem = mem;
+ rtb->largest_acked_tx_pkt_num = -1;
+ rtb->num_ack_eliciting = 0;
+ rtb->num_retransmittable = 0;
+ rtb->num_pto_eliciting = 0;
+ rtb->probe_pkt_left = 0;
+ rtb->pktns_id = pktns_id;
+ rtb->cc_pkt_num = 0;
+ rtb->cc_bytes_in_flight = 0;
+ rtb->persistent_congestion_start_ts = UINT64_MAX;
+ rtb->num_lost_pkts = 0;
+ rtb->num_lost_pmtud_pkts = 0;
+}
+
+void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
+ ngtcp2_ksl_it it;
+
+ if (rtb == NULL) {
+ return;
+ }
+
+ it = ngtcp2_ksl_begin(&rtb->ents);
+
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it),
+ rtb->rtb_entry_objalloc, rtb->frc_objalloc,
+ rtb->mem);
+ }
+
+ ngtcp2_ksl_free(&rtb->ents);
+}
+
+static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
+
+ assert(rtb->cc_pkt_num <= ent->hd.pkt_num);
+
+ cstat->bytes_in_flight += ent->pktlen;
+ rtb->cc_bytes_in_flight += ent->pktlen;
+
+ ngtcp2_rst_update_app_limited(rtb->rst, cstat);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ++rtb->num_ack_eliciting;
+ }
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
+ ++rtb->num_retransmittable;
+ }
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ ++rtb->num_pto_eliciting;
+ }
+}
+
+static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ assert(rtb->num_lost_pkts);
+ --rtb->num_lost_pkts;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ assert(rtb->num_lost_pmtud_pkts);
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ return 0;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ assert(rtb->num_ack_eliciting);
+ --rtb->num_ack_eliciting;
+ }
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
+ !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) {
+ assert(rtb->num_retransmittable);
+ --rtb->num_retransmittable;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ assert(rtb->num_pto_eliciting);
+ --rtb->num_pto_eliciting;
+ }
+
+ if (rtb->cc_pkt_num <= ent->hd.pkt_num) {
+ assert(cstat->bytes_in_flight >= ent->pktlen);
+ cstat->bytes_in_flight -= ent->pktlen;
+
+ assert(rtb->cc_bytes_in_flight >= ent->pktlen);
+ rtb->cc_bytes_in_flight -= ent->pktlen;
+
+ /* If PMTUD packet is lost, we do not report the lost bytes to the
+ caller in order to ignore loss of PMTUD packet. */
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ return 0;
+ }
+
+ return ent->pktlen;
+ }
+
+ return 0;
+}
+
+/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_RECLAIM_FLAG_NONE 0x00u
+/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed
+ because of the packet loss.*/
+#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u
+
+/*
+ * rtb_reclaim_frame queues unacknowledged frames included in |ent|
+ * for retransmission. The re-queued frames are not deleted from
+ * |ent|. It returns the number of frames queued. |flags| is bitwise
+ * OR of 0 or more of NGTCP2_RECLAIM_FLAG_*.
+ */
+static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq;
+ ngtcp2_frame *fr;
+ ngtcp2_strm *strm;
+ ngtcp2_range gap, range;
+ size_t num_reclaimed = 0;
+ int rv;
+ int streamfrq_empty;
+
+ assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE);
+
+ /* TODO Reconsider the order of pfrc */
+ for (frc = ent->frc; frc; frc = frc->next) {
+ fr = &frc->fr;
+ /* Check that a late ACK acknowledged this frame. */
+ if (frc->binder &&
+ (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
+ continue;
+ }
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id);
+ if (strm == NULL) {
+ continue;
+ }
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, fr->stream.offset);
+
+ range.begin = fr->stream.offset;
+ range.end = fr->stream.offset +
+ ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt);
+ range = ngtcp2_range_intersect(&range, &gap);
+ if (ngtcp2_range_len(&range) == 0) {
+ if (!fr->stream.fin) {
+ /* 0 length STREAM frame with offset == 0 must be
+ retransmitted if no non-empty data is sent to this stream
+ and no data in this stream is acknowledged. */
+ if (fr->stream.offset != 0 || fr->stream.datacnt != 0 ||
+ strm->tx.offset || (strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) {
+ continue;
+ }
+ } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
+ continue;
+ }
+ }
+
+ if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) &&
+ ent->hd.pkt_num != strm->tx.last_lost_pkt_num) {
+ strm->tx.last_lost_pkt_num = ent->hd.pkt_num;
+ ++strm->tx.loss_count;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+ ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
+ fr->stream.datacnt);
+
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
+ rv = ngtcp2_strm_streamfrq_push(strm, nfrc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
+
+ ++num_reclaimed;
+
+ continue;
+ case NGTCP2_FRAME_CRYPTO:
+ /* Don't resend CRYPTO frame if the whole region it contains has
+ been acknowledged */
+ gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->crypto.offset);
+
+ range.begin = fr->crypto.offset;
+ range.end = fr->crypto.offset +
+ ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt);
+ range = ngtcp2_range_intersect(&range, &gap);
+ if (ngtcp2_range_len(&range) == 0) {
+ continue;
+ }
+
+ rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+ &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+ ngtcp2_vec_copy(nfrc->fr.crypto.data, fr->crypto.data,
+ fr->crypto.datacnt);
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &nfrc->fr.crypto.offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
+ return rv;
+ }
+
+ ++num_reclaimed;
+
+ continue;
+ case NGTCP2_FRAME_NEW_TOKEN:
+ rv = ngtcp2_frame_chain_new_token_objalloc_new(
+ &nfrc, fr->new_token.token, fr->new_token.tokenlen, rtb->frc_objalloc,
+ rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ continue;
+ default:
+ rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc);
+ if (rv != 0) {
+ return rv;
+ }
+
+ nfrc->fr = *fr;
+
+ rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ break;
+ }
+
+ ++num_reclaimed;
+
+ nfrc->next = *pfrc;
+ *pfrc = nfrc;
+ pfrc = &nfrc->next;
+ }
+
+ return (ngtcp2_ssize)num_reclaimed;
+}
+
+/*
+ * conn_process_lost_datagram calls ngtcp2_lost_datagram callback for
+ * lost DATAGRAM frames.
+ */
+static int conn_process_lost_datagram(ngtcp2_conn *conn,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain *frc;
+ int rv;
+
+ for (frc = ent->frc; frc; frc = frc->next) {
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ assert(conn->callbacks.lost_datagram);
+
+ rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
+ ngtcp2_rtb_entry *ent, ngtcp2_conn_stat *cstat,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts) {
+ int rv;
+ ngtcp2_ssize reclaimed;
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_cc_pkt pkt;
+
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
+ ent->ts);
+
+ if (rtb->qlog) {
+ ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ ++rtb->num_lost_pmtud_pkts;
+ } else if (rtb->cc->on_pkt_lost) {
+ cc->on_pkt_lost(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ rtb->pktns_id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
+ ts);
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " has already been reclaimed on PTO",
+ ent->hd.pkt_num);
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(UINT64_MAX == ent->lost_ts);
+
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = ts;
+
+ ++rtb->num_lost_pkts;
+
+ ngtcp2_ksl_it_next(it);
+
+ return 0;
+ }
+
+ if (conn->callbacks.lost_datagram &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) {
+ rv = conn_process_lost_datagram(conn, ent);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
+ assert(ent->frc);
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(UINT64_MAX == ent->lost_ts);
+
+ reclaimed =
+ rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent);
+ if (reclaimed < 0) {
+ return (int)reclaimed;
+ }
+ }
+
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+ ent->lost_ts = ts;
+
+ ++rtb->num_lost_pkts;
+
+ ngtcp2_ksl_it_next(it);
+
+ return 0;
+}
+
+int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ int rv;
+
+ rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rtb_on_add(rtb, ent, cstat);
+
+ return 0;
+}
+
+ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) {
+ return ngtcp2_ksl_begin(&rtb->ents);
+}
+
+static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
+ ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat) {
+ int rv;
+ (void)rv;
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num);
+ assert(0 == rv);
+ rtb_on_remove(rtb, ent, cstat);
+
+ assert(ent->next == NULL);
+
+ ngtcp2_list_insert(ent, pent);
+}
+
+static void conn_ack_crypto_data(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ uint64_t datalen) {
+ ngtcp2_buf_chain **pbufchain, *bufchain;
+ size_t left;
+
+ for (pbufchain = &pktns->crypto.tx.data; *pbufchain;) {
+ left = ngtcp2_buf_len(&(*pbufchain)->buf);
+ if (left > datalen) {
+ (*pbufchain)->buf.pos += datalen;
+ return;
+ }
+
+ bufchain = *pbufchain;
+ *pbufchain = bufchain->next;
+
+ ngtcp2_mem_free(conn->mem, bufchain);
+
+ datalen -= left;
+
+ if (datalen == 0) {
+ return;
+ }
+ }
+
+ assert(datalen == 0);
+
+ return;
+}
+
+static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn *conn) {
+ ngtcp2_frame_chain *frc;
+ uint64_t prev_stream_offset, stream_offset;
+ ngtcp2_strm *strm;
+ int rv;
+ uint64_t datalen;
+ ngtcp2_strm *crypto = rtb->crypto;
+ ngtcp2_pktns *pktns = NULL;
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud &&
+ conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) {
+ ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen);
+
+ conn->dcid.current.max_udp_payload_size =
+ ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen);
+
+ if (ngtcp2_pmtud_finished(conn->pmtud)) {
+ ngtcp2_conn_stop_pmtud(conn);
+ }
+ }
+
+ for (frc = ent->frc; frc; frc = frc->next) {
+ if (frc->binder) {
+ frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK;
+ }
+
+ switch (frc->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id);
+ if (strm == NULL) {
+ break;
+ }
+
+ strm->flags |= NGTCP2_STRM_FLAG_ANY_ACKED;
+
+ if (frc->fr.stream.fin) {
+ strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED;
+ }
+
+ prev_stream_offset = ngtcp2_strm_get_acked_offset(strm);
+ rv = ngtcp2_strm_ack_data(
+ strm, frc->fr.stream.offset,
+ ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt));
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (conn->callbacks.acked_stream_data_offset) {
+ stream_offset = ngtcp2_strm_get_acked_offset(strm);
+ datalen = stream_offset - prev_stream_offset;
+ if (datalen == 0 && !frc->fr.stream.fin) {
+ break;
+ }
+
+ rv = conn->callbacks.acked_stream_data_offset(
+ conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data,
+ strm->stream_user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ prev_stream_offset = ngtcp2_strm_get_acked_offset(crypto);
+ rv = ngtcp2_strm_ack_data(
+ crypto, frc->fr.crypto.offset,
+ ngtcp2_vec_len(frc->fr.crypto.data, frc->fr.crypto.datacnt));
+ if (rv != 0) {
+ return rv;
+ }
+
+ stream_offset = ngtcp2_strm_get_acked_offset(crypto);
+ datalen = stream_offset - prev_stream_offset;
+ if (datalen == 0) {
+ break;
+ }
+
+ switch (rtb->pktns_id) {
+ case NGTCP2_PKTNS_ID_INITIAL:
+ pktns = conn->in_pktns;
+ break;
+ case NGTCP2_PKTNS_ID_HANDSHAKE:
+ pktns = conn->hs_pktns;
+ break;
+ case NGTCP2_PKTNS_ID_APPLICATION:
+ pktns = &conn->pktns;
+ break;
+ default:
+ ngtcp2_unreachable();
+ }
+
+ conn_ack_crypto_data(conn, pktns, datalen);
+
+ break;
+ case NGTCP2_FRAME_RESET_STREAM:
+ strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id);
+ if (strm == NULL) {
+ break;
+ }
+ strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED;
+ rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
+ ngtcp2_conn_untrack_retired_dcid_seq(conn,
+ frc->fr.retire_connection_id.seq);
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ if (!conn->callbacks.ack_datagram) {
+ break;
+ }
+
+ rv = conn->callbacks.ack_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_cc_pkt pkt;
+
+ ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts);
+
+ cc->on_pkt_acked(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ rtb->pktns_id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
+ ts);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
+ cstat->pto_count = 0;
+ }
+}
+
+static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
+ const ngtcp2_ack *fr, size_t ecn_acked,
+ ngtcp2_tstamp largest_acked_sent_ts,
+ ngtcp2_tstamp ts) {
+ if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) {
+ return;
+ }
+
+ if ((ecn_acked && fr->type == NGTCP2_FRAME_ACK) ||
+ (fr->type == NGTCP2_FRAME_ACK_ECN &&
+ (pktns->rx.ecn.ack.ect0 > fr->ecn.ect0 ||
+ pktns->rx.ecn.ack.ect1 > fr->ecn.ect1 ||
+ pktns->rx.ecn.ack.ce > fr->ecn.ce ||
+ (fr->ecn.ect0 - pktns->rx.ecn.ack.ect0) +
+ (fr->ecn.ce - pktns->rx.ecn.ack.ce) <
+ ecn_acked ||
+ fr->ecn.ect0 > pktns->tx.ecn.ect0 || fr->ecn.ect1))) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+ "path is not ECN capable");
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+ return;
+ }
+
+ if (conn->tx.ecn.state != NGTCP2_ECN_STATE_CAPABLE && ecn_acked) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "path is ECN capable");
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_CAPABLE;
+ }
+
+ if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+ if (largest_acked_sent_ts != UINT64_MAX &&
+ fr->ecn.ce > pktns->rx.ecn.ack.ce) {
+ cc->congestion_event(cc, cstat, largest_acked_sent_ts, ts);
+ }
+
+ pktns->rx.ecn.ack.ect0 = fr->ecn.ect0;
+ pktns->rx.ecn.ack.ect1 = fr->ecn.ect1;
+ pktns->rx.ecn.ack.ce = fr->ecn.ce;
+ }
+}
+
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
+
+ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
+ ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts,
+ ngtcp2_tstamp ts) {
+ ngtcp2_rtb_entry *ent;
+ int64_t largest_ack = fr->largest_ack, min_ack;
+ size_t i;
+ int rv;
+ ngtcp2_ksl_it it;
+ ngtcp2_ssize num_acked = 0;
+ ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX;
+ ngtcp2_tstamp largest_acked_sent_ts = UINT64_MAX;
+ int64_t pkt_num;
+ ngtcp2_cc *cc = rtb->cc;
+ ngtcp2_rtb_entry *acked_ent = NULL;
+ int ack_eliciting_pkt_acked = 0;
+ size_t ecn_acked = 0;
+ int verify_ecn = 0;
+ ngtcp2_cc_ack cc_ack = {0};
+ size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts;
+
+ cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight;
+ cc_ack.rtt = UINT64_MAX;
+
+ if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
+ (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) &&
+ largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
+ conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED |
+ NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR);
+ conn->crypto.key_update.confirmed_ts = ts;
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
+ }
+
+ if (rtb->largest_acked_tx_pkt_num < largest_ack) {
+ rtb->largest_acked_tx_pkt_num = largest_ack;
+ verify_ecn = 1;
+ }
+
+ /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
+ it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
+ if (ngtcp2_ksl_it_end(&it)) {
+ if (conn && verify_ecn) {
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
+ largest_acked_sent_ts, ts);
+ }
+ return 0;
+ }
+
+ min_ack = largest_ack - (int64_t)fr->first_ack_range;
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it);
+
+ assert(pkt_num <= largest_ack);
+
+ if (pkt_num < min_ack) {
+ break;
+ }
+
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (largest_ack == pkt_num) {
+ largest_pkt_sent_ts = ent->ts;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ack_eliciting_pkt_acked = 1;
+ }
+
+ rtb_remove(rtb, &it, &acked_ent, ent, cstat);
+ ++num_acked;
+ }
+
+ for (i = 0; i < fr->rangecnt;) {
+ largest_ack = min_ack - (int64_t)fr->ranges[i].gap - 2;
+ min_ack = largest_ack - (int64_t)fr->ranges[i].len;
+
+ it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
+ if (ngtcp2_ksl_it_end(&it)) {
+ break;
+ }
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it);
+ if (pkt_num < min_ack) {
+ break;
+ }
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+ ack_eliciting_pkt_acked = 1;
+ }
+
+ rtb_remove(rtb, &it, &acked_ent, ent, cstat);
+ ++num_acked;
+ }
+
+ ++i;
+ }
+
+ if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
+ cc_ack.rtt = pkt_ts - largest_pkt_sent_ts;
+
+ rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
+ if (rv == 0 && cc->new_rtt_sample) {
+ cc->new_rtt_sample(cc, cstat, ts);
+ }
+ }
+
+ if (conn) {
+ for (ent = acked_ent; ent; ent = acked_ent) {
+ if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) {
+ ++ecn_acked;
+ }
+
+ assert(largest_acked_sent_ts == UINT64_MAX ||
+ largest_acked_sent_ts <= ent->ts);
+
+ largest_acked_sent_ts = ent->ts;
+
+ rv = rtb_process_acked_pkt(rtb, ent, conn);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (ent->hd.pkt_num >= rtb->cc_pkt_num) {
+ assert(cc_ack.pkt_delivered <= ent->rst.delivered);
+
+ cc_ack.bytes_delivered += ent->pktlen;
+ cc_ack.pkt_delivered = ent->rst.delivered;
+ }
+
+ rtb_on_pkt_acked(rtb, ent, cstat, ts);
+ acked_ent = ent->next;
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+
+ if (verify_ecn) {
+ conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
+ largest_acked_sent_ts, ts);
+ }
+ } else {
+ /* For unit tests */
+ for (ent = acked_ent; ent; ent = acked_ent) {
+ rtb_on_pkt_acked(rtb, ent, cstat, ts);
+ acked_ent = ent->next;
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+ }
+
+ if (rtb->cc->on_spurious_congestion && num_lost_pkts &&
+ rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts == 0) {
+ rtb->cc->on_spurious_congestion(cc, cstat, ts);
+ }
+
+ ngtcp2_rst_on_ack_recv(rtb->rst, cstat, cc_ack.pkt_delivered);
+
+ if (conn && num_acked > 0) {
+ rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ rtb->rst->lost += cc_ack.bytes_lost;
+
+ cc_ack.largest_acked_sent_ts = largest_acked_sent_ts;
+ cc->on_ack_recv(cc, cstat, &cc_ack, ts);
+
+ return num_acked;
+
+fail:
+ for (ent = acked_ent; ent; ent = acked_ent) {
+ acked_ent = ent->next;
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+
+ return rv;
+}
+
+static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat,
+ const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay,
+ size_t pkt_thres, ngtcp2_tstamp ts) {
+ ngtcp2_tstamp loss_time;
+
+ if (ent->ts + loss_delay <= ts ||
+ rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) {
+ return 1;
+ }
+
+ loss_time = cstat->loss_time[rtb->pktns_id];
+
+ if (loss_time == UINT64_MAX) {
+ loss_time = ent->ts + loss_delay;
+ } else {
+ loss_time = ngtcp2_min(loss_time, ent->ts + loss_delay);
+ }
+
+ cstat->loss_time[rtb->pktns_id] = loss_time;
+
+ return 0;
+}
+
+/*
+ * rtb_compute_pkt_loss_delay computes loss delay.
+ */
+static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_conn_stat *cstat) {
+ /* 9/8 is kTimeThreshold */
+ ngtcp2_duration loss_delay =
+ ngtcp2_max(cstat->latest_rtt, cstat->smoothed_rtt) * 9 / 8;
+ return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY);
+}
+
+/*
+ * conn_all_ecn_pkt_lost returns nonzero if all ECN QUIC packets are
+ * lost during validation period.
+ */
+static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) {
+ ngtcp2_pktns *in_pktns = conn->in_pktns;
+ ngtcp2_pktns *hs_pktns = conn->hs_pktns;
+ ngtcp2_pktns *pktns = &conn->pktns;
+
+ return (!in_pktns || in_pktns->tx.ecn.validation_pkt_sent ==
+ in_pktns->tx.ecn.validation_pkt_lost) &&
+ (!hs_pktns || hs_pktns->tx.ecn.validation_pkt_sent ==
+ hs_pktns->tx.ecn.validation_pkt_lost) &&
+ pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost;
+}
+
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+ ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_duration loss_delay;
+ ngtcp2_ksl_it it;
+ ngtcp2_tstamp latest_ts, oldest_ts;
+ int64_t last_lost_pkt_num;
+ ngtcp2_duration loss_window, congestion_period;
+ ngtcp2_cc *cc = rtb->cc;
+ int rv;
+ uint64_t pkt_thres =
+ rtb->cc_bytes_in_flight / cstat->max_tx_udp_payload_size / 2;
+ size_t ecn_pkt_lost = 0;
+ ngtcp2_tstamp start_ts;
+ ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns);
+ uint64_t bytes_lost = 0;
+ ngtcp2_duration max_ack_delay;
+
+ pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD);
+ pkt_thres = ngtcp2_min(pkt_thres, 256);
+ cstat->loss_time[rtb->pktns_id] = UINT64_MAX;
+ loss_delay = compute_pkt_loss_delay(cstat);
+
+ it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num);
+ for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ break;
+ }
+
+ if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, ts)) {
+ /* All entries from ent are considered to be lost. */
+ latest_ts = oldest_ts = ent->ts;
+ last_lost_pkt_num = ent->hd.pkt_num;
+ max_ack_delay = conn->remote.transport_params
+ ? conn->remote.transport_params->max_ack_delay
+ : 0;
+
+ congestion_period =
+ (cstat->smoothed_rtt +
+ ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + max_ack_delay) *
+ NGTCP2_PERSISTENT_CONGESTION_THRESHOLD;
+
+ start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts,
+ cstat->first_rtt_sample_ts);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (last_lost_pkt_num == ent->hd.pkt_num + 1 && ent->ts >= start_ts) {
+ last_lost_pkt_num = ent->hd.pkt_num;
+ oldest_ts = ent->ts;
+ } else {
+ last_lost_pkt_num = -1;
+ }
+
+ if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) {
+ if (rtb->pktns_id != NGTCP2_PKTNS_ID_APPLICATION ||
+ last_lost_pkt_num == -1 ||
+ latest_ts - oldest_ts >= congestion_period) {
+ break;
+ }
+ ngtcp2_ksl_it_next(&it);
+ continue;
+ }
+
+ if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num &&
+ (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) {
+ ++ecn_pkt_lost;
+ }
+
+ bytes_lost += rtb_on_remove(rtb, ent, cstat);
+ rv = rtb_on_pkt_lost(rtb, &it, ent, cstat, conn, pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ /* If only PMTUD packets are lost, do not trigger congestion
+ event. */
+ if (bytes_lost == 0) {
+ break;
+ }
+
+ switch (conn->tx.ecn.state) {
+ case NGTCP2_ECN_STATE_TESTING:
+ if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+ break;
+ }
+ if (ts - conn->tx.ecn.validation_start_ts < 3 * pto) {
+ pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost;
+ assert(pktns->tx.ecn.validation_pkt_sent >=
+ pktns->tx.ecn.validation_pkt_lost);
+ break;
+ }
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+ /* fall through */
+ case NGTCP2_ECN_STATE_UNKNOWN:
+ pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost;
+ assert(pktns->tx.ecn.validation_pkt_sent >=
+ pktns->tx.ecn.validation_pkt_lost);
+ if (conn_all_ecn_pkt_lost(conn)) {
+ conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+ }
+ break;
+ default:
+ break;
+ }
+
+ cc->congestion_event(cc, cstat, latest_ts, ts);
+
+ loss_window = latest_ts - oldest_ts;
+ /* Persistent congestion situation is only evaluated for app
+ * packet number space and for the packets sent after handshake
+ * is confirmed. During handshake, there is not much packets
+ * sent and also people seem to do lots of effort not to trigger
+ * persistent congestion there, then it is a lot easier to just
+ * not enable it during handshake.
+ */
+ if (rtb->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && loss_window > 0) {
+ if (loss_window >= congestion_period) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "persistent congestion loss_window=%" PRIu64
+ " congestion_period=%" PRIu64,
+ loss_window, congestion_period);
+
+ /* Reset min_rtt, srtt, and rttvar here. Next new RTT
+ sample will be used to recalculate these values. */
+ cstat->min_rtt = UINT64_MAX;
+ cstat->smoothed_rtt = conn->local.settings.initial_rtt;
+ cstat->rttvar = conn->local.settings.initial_rtt / 2;
+ cstat->first_rtt_sample_ts = UINT64_MAX;
+
+ cc->on_persistent_congestion(cc, cstat, ts);
+ }
+ }
+
+ break;
+ }
+ }
+
+ ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres);
+
+ if (ppkt_lost) {
+ *ppkt_lost = bytes_lost;
+ }
+
+ return 0;
+}
+
+int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
+ return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat,
+ ts);
+}
+
+void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
+ ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents);
+ ngtcp2_rtb_entry *ent;
+ int rv;
+ (void)rv;
+
+ for (; rtb->num_lost_pkts > n;) {
+ assert(ngtcp2_ksl_it_end(&it));
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED);
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+
+ --rtb->num_lost_pkts;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+}
+
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ int rv;
+ (void)rv;
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return;
+ }
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+
+ for (;;) {
+ assert(ngtcp2_ksl_it_end(&it));
+
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) ||
+ ts - ent->lost_ts < pto) {
+ return;
+ }
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
+
+ --rtb->num_lost_pkts;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return;
+ }
+ }
+}
+
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+
+ if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ return UINT64_MAX;
+ }
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) {
+ return UINT64_MAX;
+ }
+
+ return ent->lost_ts;
+}
+
+static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain **pfrc, *frc;
+ ngtcp2_stream *sfr;
+ ngtcp2_strm *strm;
+ int rv;
+ int streamfrq_empty;
+
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
+ ent->ts);
+
+ if (rtb->qlog) {
+ ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " is a probe packet, no retransmission is necessary",
+ ent->hd.pkt_num);
+ return 0;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " is a PMTUD probe packet, no retransmission is necessary",
+ ent->hd.pkt_num);
+ return 0;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
+ --rtb->num_lost_pkts;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+ --rtb->num_lost_pmtud_pkts;
+ }
+
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64
+ " was declared lost and has already been retransmitted",
+ ent->hd.pkt_num);
+ return 0;
+ }
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+ "pkn=%" PRId64 " has already been reclaimed on PTO",
+ ent->hd.pkt_num);
+ return 0;
+ }
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
+ (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) ||
+ !conn->callbacks.lost_datagram)) {
+ /* PADDING only (or PADDING + ACK ) packets will have NULL
+ ent->frc. */
+ return 0;
+ }
+
+ pfrc = &ent->frc;
+
+ for (; *pfrc;) {
+ switch ((*pfrc)->fr.type) {
+ case NGTCP2_FRAME_STREAM:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+ sfr = &frc->fr.stream;
+
+ strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
+ if (!strm) {
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
+ break;
+ }
+ streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
+ rv = ngtcp2_strm_streamfrq_push(strm, frc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
+ return rv;
+ }
+ if (!ngtcp2_strm_is_tx_queued(strm)) {
+ strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn);
+ rv = ngtcp2_conn_tx_strmq_push(conn, strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ if (streamfrq_empty) {
+ ++conn->tx.strmq_nretrans;
+ }
+ break;
+ case NGTCP2_FRAME_CRYPTO:
+ frc = *pfrc;
+
+ *pfrc = frc->next;
+ frc->next = NULL;
+
+ rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL,
+ &frc->fr.crypto.offset, frc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
+ return rv;
+ }
+ break;
+ case NGTCP2_FRAME_DATAGRAM:
+ case NGTCP2_FRAME_DATAGRAM_LEN:
+ frc = *pfrc;
+
+ if (conn->callbacks.lost_datagram) {
+ rv = conn->callbacks.lost_datagram(conn, frc->fr.datagram.dgram_id,
+ conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+
+ *pfrc = (*pfrc)->next;
+
+ ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
+ break;
+ default:
+ pfrc = &(*pfrc)->next;
+ }
+ }
+
+ *pfrc = pktns->tx.frq;
+ pktns->tx.frq = ent->frc;
+ ent->frc = NULL;
+
+ return 0;
+}
+
+int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ int rv;
+
+ it = ngtcp2_ksl_begin(&rtb->ents);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ rtb_on_remove(rtb, ent, cstat);
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+
+ rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent);
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return 0;
+}
+
+void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) {
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ksl_it it;
+ int rv;
+ (void)rv;
+
+ it = ngtcp2_ksl_begin(&rtb->ents);
+
+ for (; !ngtcp2_ksl_it_end(&it);) {
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if (ent->hd.type != NGTCP2_PKT_0RTT) {
+ ngtcp2_ksl_it_next(&it);
+ continue;
+ }
+
+ rtb_on_remove(rtb, ent, cstat);
+ rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
+ assert(0 == rv);
+
+ ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+ rtb->frc_objalloc, rtb->mem);
+ }
+}
+
+int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) {
+ return ngtcp2_ksl_len(&rtb->ents) == 0;
+}
+
+void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num) {
+ rtb->cc_pkt_num = cc_pkt_num;
+ rtb->cc_bytes_in_flight = 0;
+}
+
+ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, size_t num_pkts) {
+ ngtcp2_ksl_it it;
+ ngtcp2_rtb_entry *ent;
+ ngtcp2_ssize reclaimed;
+ size_t atmost = num_pkts;
+
+ it = ngtcp2_ksl_end(&rtb->ents);
+ for (; !ngtcp2_ksl_it_begin(&it) && num_pkts >= 1;) {
+ ngtcp2_ksl_it_prev(&it);
+ ent = ngtcp2_ksl_it_get(&it);
+
+ if ((ent->flags & (NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED |
+ NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) ||
+ !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE)) {
+ continue;
+ }
+
+ assert(ent->frc);
+
+ reclaimed =
+ rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent);
+ if (reclaimed < 0) {
+ return reclaimed;
+ }
+
+ /* Mark reclaimed even if reclaimed == 0 so that we can skip it in
+ the next run. */
+ ent->flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED;
+
+ assert(rtb->num_retransmittable);
+ --rtb->num_retransmittable;
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+ ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING;
+ assert(rtb->num_pto_eliciting);
+ --rtb->num_pto_eliciting;
+ }
+
+ if (reclaimed) {
+ --num_pkts;
+ }
+ }
+
+ return (ngtcp2_ssize)(atmost - num_pkts);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
new file mode 100644
index 0000000..5183aed
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
@@ -0,0 +1,467 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_RTB_H
+#define NGTCP2_RTB_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pq.h"
+#include "ngtcp2_objalloc.h"
+
+typedef struct ngtcp2_conn ngtcp2_conn;
+typedef struct ngtcp2_pktns ngtcp2_pktns;
+typedef struct ngtcp2_log ngtcp2_log;
+typedef struct ngtcp2_qlog ngtcp2_qlog;
+typedef struct ngtcp2_strm ngtcp2_strm;
+typedef struct ngtcp2_rst ngtcp2_rst;
+typedef struct ngtcp2_cc ngtcp2_cc;
+
+/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is
+ set. */
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00u
+/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information
+ which a frame carries has been acknowledged. */
+#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01u
+
+/*
+ * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to
+ * share the acknowledgement state. In general, all
+ * ngtcp2_frame_chains bound to the same binder must have the same
+ * information.
+ */
+typedef struct ngtcp2_frame_chain_binder {
+ size_t refcount;
+ /* flags is bitwise OR of zero or more of
+ NGTCP2_FRAME_CHAIN_BINDER_FLAG_*. */
+ uint32_t flags;
+} ngtcp2_frame_chain_binder;
+
+int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder,
+ const ngtcp2_mem *mem);
+
+typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
+
+/*
+ * ngtcp2_frame_chain chains frames in a single packet.
+ */
+struct ngtcp2_frame_chain {
+ union {
+ struct {
+ ngtcp2_frame_chain *next;
+ ngtcp2_frame_chain_binder *binder;
+ ngtcp2_frame fr;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
+};
+
+ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent);
+
+/*
+ * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using
+ * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL
+ * a->binder. |b| must not have non-NULL b->binder.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
+ const ngtcp2_mem *mem);
+
+/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that
+ a ngtcp2_stream can include. */
+#define NGTCP2_MAX_STREAM_DATACNT 256
+
+/* NGTCP2_MAX_CRYPTO_DATACNT is the maximum number of ngtcp2_vec that
+ a ngtcp2_crypto can include. */
+#define NGTCP2_MAX_CRYPTO_DATACNT 8
+
+/*
+ * ngtcp2_frame_chain_new allocates ngtcp2_frame_chain object and
+ * assigns its pointer to |*pfrc|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory.
+ */
+int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_objalloc_new behaves like
+ * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object.
+ */
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+ ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new,
+ * but it allocates extra memory |extralen| in order to extend
+ * ngtcp2_frame.
+ */
+int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like
+ * ngtcp2_frame_chain_new, but it allocates enough data to store
+ * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream
+ * object. If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
+ */
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like
+ * ngtcp2_frame_chain_new, but it allocates enough data to store
+ * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto
+ * object. If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
+ */
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+ size_t datacnt,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+ const uint8_t *token,
+ size_t tokenlen,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the
+ * memory pointed by |frc|.
+ */
+void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse.
+ * It might just delete |frc| depending on the frame type and the size
+ * of |frc|.
+ */
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_frame_chain_init initializes |frc|.
+ */
+void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc);
+
+/*
+ * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain
+ * linked from |frc| to |objalloc| for reuse. Depending on the frame type
+ * and its size, ngtcp2_frame_chain might be deleted instead.
+ */
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+ ngtcp2_objalloc *objalloc,
+ const ngtcp2_mem *mem);
+
+/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00u
+/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a
+ probe packet. */
+#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01u
+/* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry
+ includes a frame which must be retransmitted until it is
+ acknowledged. In most cases, this flag is used along with
+ NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and
+ NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */
+#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02u
+/* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry
+ elicits acknowledgement. */
+#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04u
+/* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has
+ been reclaimed on PTO. It is not marked lost yet and still
+ consumes congestion window. */
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08u
+/* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry
+ has been marked lost and, optionally, scheduled to retransmit. */
+#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10u
+/* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a
+ UDP datagram with ECN marking. */
+#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20u
+/* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes
+ DATAGRAM frame. */
+#define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40u
+/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes
+ a PMTUD probe packet. */
+#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80u
+/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry
+ includes a packet which elicits PTO probe packets. */
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u
+
+typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
+
+/*
+ * ngtcp2_rtb_entry is an object stored in ngtcp2_rtb. It corresponds
+ * to the one packet which is waiting for its ACK.
+ */
+struct ngtcp2_rtb_entry {
+ union {
+ struct {
+ ngtcp2_rtb_entry *next;
+
+ struct {
+ int64_t pkt_num;
+ uint8_t type;
+ uint8_t flags;
+ } hd;
+ ngtcp2_frame_chain *frc;
+ /* ts is the time point when a packet included in this entry is sent
+ to a peer. */
+ ngtcp2_tstamp ts;
+ /* lost_ts is the time when this entry is marked lost. */
+ ngtcp2_tstamp lost_ts;
+ /* pktlen is the length of QUIC packet */
+ size_t pktlen;
+ struct {
+ uint64_t delivered;
+ ngtcp2_tstamp delivered_ts;
+ ngtcp2_tstamp first_sent_ts;
+ uint64_t tx_in_flight;
+ uint64_t lost;
+ int is_app_limited;
+ } rst;
+ /* flags is bitwise-OR of zero or more of
+ NGTCP2_RTB_ENTRY_FLAG_*. */
+ uint16_t flags;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
+};
+
+ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent);
+
+/*
+ * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns
+ * its pointer to |*pent|.
+ */
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+ const ngtcp2_pkt_hd *hd,
+ ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+ size_t pktlen, uint16_t flags,
+ ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse.
+ * ngtcp2_frame_chain linked from ent->frc are also added to
+ * |frc_objalloc| depending on their frame type and size.
+ */
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+ ngtcp2_objalloc *objalloc,
+ ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rtb tracks sent packets, and its ACK timeout for
+ * retransmission.
+ */
+typedef struct ngtcp2_rtb {
+ ngtcp2_objalloc *frc_objalloc;
+ ngtcp2_objalloc *rtb_entry_objalloc;
+ /* ents includes ngtcp2_rtb_entry sorted by decreasing order of
+ packet number. */
+ ngtcp2_ksl ents;
+ /* crypto is CRYPTO stream. */
+ ngtcp2_strm *crypto;
+ ngtcp2_rst *rst;
+ ngtcp2_cc *cc;
+ ngtcp2_log *log;
+ ngtcp2_qlog *qlog;
+ const ngtcp2_mem *mem;
+ /* largest_acked_tx_pkt_num is the largest packet number
+ acknowledged by the peer. */
+ int64_t largest_acked_tx_pkt_num;
+ /* num_ack_eliciting is the number of ACK eliciting entries. */
+ size_t num_ack_eliciting;
+ /* num_retransmittable is the number of packets which contain frames
+ that must be retransmitted on loss. */
+ size_t num_retransmittable;
+ /* num_pto_eliciting is the number of packets that elicit PTO probe
+ packets. */
+ size_t num_pto_eliciting;
+ /* probe_pkt_left is the number of probe packet to send */
+ size_t probe_pkt_left;
+ /* pktns_id is the identifier of packet number space. */
+ ngtcp2_pktns_id pktns_id;
+ /* cc_pkt_num is the smallest packet number that is contributed to
+ ngtcp2_conn_stat.bytes_in_flight. */
+ int64_t cc_pkt_num;
+ /* cc_bytes_in_flight is the number of in-flight bytes that is
+ contributed to ngtcp2_conn_stat.bytes_in_flight. It only
+ includes the bytes after congestion state is reset. */
+ uint64_t cc_bytes_in_flight;
+ /* persistent_congestion_start_ts is the time when persistent
+ congestion evaluation is started. It happens roughly after
+ handshake is confirmed. */
+ ngtcp2_tstamp persistent_congestion_start_ts;
+ /* num_lost_pkts is the number entries in ents which has
+ NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */
+ size_t num_lost_pkts;
+ /* num_lost_pmtud_pkts is the number of entries in ents which have
+ both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and
+ NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */
+ size_t num_lost_pmtud_pkts;
+} ngtcp2_rtb;
+
+/*
+ * ngtcp2_rtb_init initializes |rtb|.
+ */
+void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
+ ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
+ ngtcp2_log *log, ngtcp2_qlog *qlog,
+ ngtcp2_objalloc *rtb_entry_objalloc,
+ ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_rtb_free deallocates resources allocated for |rtb|.
+ */
+void ngtcp2_rtb_free(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_add adds |ent| to |rtb|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+ ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_rtb_head returns the iterator which points to the entry
+ * which has the largest packet number. If there is no entry,
+ * returned value satisfies ngtcp2_ksl_it_end(&it) != 0.
+ */
+ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|.
+ * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the
+ * timestamp when packet is received. |ts| should be the current
+ * time. Usually they are the same, but for buffered packets,
+ * |pkt_ts| would be earlier than |ts|.
+ *
+ * This function returns the number of newly acknowledged packets if
+ * it succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_CALLBACK_FAILURE
+ * User callback failed
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
+ ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the
+ * frames contained them to |*pfrc|. Even when this function fails,
+ * some frames might be prepended to |*pfrc| and the caller should
+ * handle them.
+ */
+int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet.
+ */
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+ ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still
+ * retained packet was lost. It returns UINT64_MAX if no such packet
+ * exists.
+ */
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends
+ * all frames to |*pfrc|. Even when this function fails, some frames
+ * might be prepended to |*pfrc| and the caller should handle them.
+ */
+int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets.
+ */
+void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat);
+
+/*
+ * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry.
+ */
+int ngtcp2_rtb_empty(ngtcp2_rtb *rtb);
+
+/*
+ * ngtcp2_rtb_reset_cc_state resets congestion state in |rtb|.
+ * |cc_pkt_num| is the next outbound packet number which is sent under
+ * new congestion state.
+ */
+void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num);
+
+/*
+ * ngtcp2_rtb_remove_expired_lost_pkt ensures that the number of lost
+ * packets at most |n|.
+ */
+void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n);
+
+/*
+ * ngtcp2_rtb_reclaim_on_pto reclaims up to |num_pkts| packets which
+ * are in-flight and not marked lost to send them in PTO probe. The
+ * reclaimed frames are chained to |*pfrc|.
+ *
+ * This function returns the number of packets reclaimed if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, size_t num_pkts);
+
+#endif /* NGTCP2_RTB_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c
new file mode 100644
index 0000000..a61636d
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.c
@@ -0,0 +1,233 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_str.h"
+
+#include <string.h>
+
+#include "ngtcp2_macro.h"
+
+void *ngtcp2_cpymem(void *dest, const void *src, size_t n) {
+ memcpy(dest, src, n);
+ return (uint8_t *)dest + n;
+}
+
+uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) {
+ memset(dest, b, n);
+ return dest + n;
+}
+
+const void *ngtcp2_get_bytes(void *dest, const void *src, size_t n) {
+ memcpy(dest, src, n);
+ return (uint8_t *)src + n;
+}
+
+#define LOWER_XDIGITS "0123456789abcdef"
+
+uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) {
+ size_t i;
+ uint8_t *p = dest;
+
+ for (i = 0; i < len; ++i) {
+ *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4];
+ *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf];
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
+ size_t len) {
+ size_t i;
+ char *p = dest;
+ uint8_t c;
+
+ for (i = 0; i < len; ++i) {
+ c = data[i];
+ if (0x20 <= c && c <= 0x7e) {
+ *p++ = (char)c;
+ } else {
+ *p++ = '.';
+ }
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+/*
+ * write_uint writes |n| to the buffer pointed by |p| in decimal
+ * representation. It returns |p| plus the number of bytes written.
+ * The function assumes that the buffer has enough capacity to contain
+ * a string.
+ */
+static uint8_t *write_uint(uint8_t *p, uint64_t n) {
+ size_t nlen = 0;
+ uint64_t t;
+ uint8_t *res;
+
+ if (n == 0) {
+ *p++ = '0';
+ return p;
+ }
+ for (t = n; t; t /= 10, ++nlen)
+ ;
+ p += nlen;
+ res = p;
+ for (; n; n /= 10) {
+ *--p = (uint8_t)((n % 10) + '0');
+ }
+ return res;
+}
+
+uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) {
+ size_t i;
+ uint8_t *p = dest;
+
+ p = write_uint(p, addr[0]);
+
+ for (i = 1; i < 4; ++i) {
+ *p++ = '.';
+ p = write_uint(p, addr[i]);
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+/*
+ * write_hex_zsup writes the content of buffer pointed by |data| of
+ * length |len| to |dest| in hex string. Any leading zeros are
+ * suppressed. It returns |dest| plus the number of bytes written.
+ */
+static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) {
+ size_t i;
+ uint8_t *p = dest;
+ uint8_t d;
+
+ for (i = 0; i < len; ++i) {
+ d = data[i];
+ if (d >> 4) {
+ break;
+ }
+
+ d &= 0xf;
+
+ if (d) {
+ *p++ = (uint8_t)LOWER_XDIGITS[d];
+ ++i;
+ break;
+ }
+ }
+
+ if (p == dest && i == len) {
+ *p++ = '0';
+ return p;
+ }
+
+ for (; i < len; ++i) {
+ d = data[i];
+ *p++ = (uint8_t)LOWER_XDIGITS[d >> 4];
+ *p++ = (uint8_t)LOWER_XDIGITS[d & 0xf];
+ }
+
+ return p;
+}
+
+uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) {
+ uint16_t blks[8];
+ size_t i;
+ size_t zlen, zoff;
+ size_t max_zlen = 0, max_zoff = 8;
+ uint8_t *p = dest;
+
+ for (i = 0; i < 16; i += sizeof(uint16_t)) {
+ /* Copy in network byte order. */
+ memcpy(&blks[i / sizeof(uint16_t)], addr + i, sizeof(uint16_t));
+ }
+
+ for (i = 0; i < 8;) {
+ if (blks[i]) {
+ ++i;
+ continue;
+ }
+
+ zlen = 1;
+ zoff = i;
+
+ ++i;
+ for (; i < 8 && blks[i] == 0; ++i, ++zlen)
+ ;
+ if (zlen > max_zlen) {
+ max_zlen = zlen;
+ max_zoff = zoff;
+ }
+ }
+
+ /* Do not suppress a single '0' block */
+ if (max_zlen == 1) {
+ max_zoff = 8;
+ }
+
+ if (max_zoff != 0) {
+ p = write_hex_zsup(p, (const uint8_t *)blks, sizeof(uint16_t));
+
+ for (i = 1; i < max_zoff; ++i) {
+ *p++ = ':';
+ p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t));
+ }
+ }
+
+ if (max_zoff != 8) {
+ *p++ = ':';
+
+ if (max_zoff + max_zlen == 8) {
+ *p++ = ':';
+ } else {
+ for (i = max_zoff + max_zlen; i < 8; ++i) {
+ *p++ = ':';
+ p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t));
+ }
+ }
+ }
+
+ *p = '\0';
+
+ return dest;
+}
+
+int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) {
+ size_t i;
+ int rv = 0;
+
+ for (i = 0; i < n; ++i) {
+ rv |= a[i] ^ b[i];
+ }
+
+ return rv == 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h
new file mode 100644
index 0000000..deb75e3
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_str.h
@@ -0,0 +1,94 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STR_H
+#define NGTCP2_STR_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+void *ngtcp2_cpymem(void *dest, const void *src, size_t n);
+
+/*
+ * ngtcp2_setmem writes a string of length |n| consisting only |b| to
+ * the buffer pointed by |dest|. It returns dest + n;
+ */
+uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n);
+
+/*
+ * ngtcp2_get_bytes copies |n| bytes from |src| to |dest|, and returns
+ * |src| + |n|.
+ */
+const void *ngtcp2_get_bytes(void *dest, const void *src, size_t n);
+
+/*
+ * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It
+ * writes additional NULL bytes at the end of the buffer. The buffer
+ * pointed by |dest| must have at least |len| * 2 + 1 bytes space.
+ * This function returns |dest|.
+ */
+uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len);
+
+/*
+ * ngtcp2_encode_ipv4 encodes binary form IPv4 address stored in
+ * |addr| to human readable text form in the buffer pointed by |dest|.
+ * The capacity of buffer must have enough length to store a text form
+ * plus a terminating NULL byte. The resulting text form ends with
+ * NULL byte. The function returns |dest|.
+ */
+uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr);
+
+/*
+ * ngtcp2_encode_ipv6 encodes binary form IPv6 address stored in
+ * |addr| to human readable text form in the buffer pointed by |dest|.
+ * The capacity of buffer must have enough length to store a text form
+ * plus a terminating NULL byte. The resulting text form ends with
+ * NULL byte. The function produces the canonical form of IPv6 text
+ * representation described in
+ * https://tools.ietf.org/html/rfc5952#section-4. The function
+ * returns |dest|.
+ */
+uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr);
+
+/*
+ * ngtcp2_encode_printable_ascii encodes |data| of length |len| in
+ * |dest| in the following manner: printable ascii characters are
+ * copied as is. The other characters are converted to ".". It
+ * writes additional NULL bytes at the end of the buffer. |dest| must
+ * have at least |len| + 1 bytes. This function returns |dest|.
+ */
+char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data,
+ size_t len);
+
+/*
+ * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers
+ * pointed by |a| and |b| are equal. The comparison is done in a
+ * constant time manner.
+ */
+int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n);
+
+#endif /* NGTCP2_STR_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
new file mode 100644
index 0000000..6f20e86
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
@@ -0,0 +1,698 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_strm.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_rtb.h"
+#include "ngtcp2_pkt.h"
+#include "ngtcp2_vec.h"
+
+static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
+ return *(int64_t *)lhs < *(int64_t *)rhs;
+}
+
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+ uint64_t max_rx_offset, uint64_t max_tx_offset,
+ void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem) {
+ strm->frc_objalloc = frc_objalloc;
+ strm->cycle = 0;
+ strm->tx.acked_offset = NULL;
+ strm->tx.cont_acked_offset = 0;
+ strm->tx.streamfrq = NULL;
+ strm->tx.offset = 0;
+ strm->tx.max_offset = max_tx_offset;
+ strm->tx.last_max_stream_data_ts = UINT64_MAX;
+ strm->tx.loss_count = 0;
+ strm->tx.last_lost_pkt_num = -1;
+ strm->rx.rob = NULL;
+ strm->rx.cont_offset = 0;
+ strm->rx.last_offset = 0;
+ strm->stream_id = stream_id;
+ strm->flags = flags;
+ strm->stream_user_data = stream_user_data;
+ strm->rx.window = strm->rx.max_offset = strm->rx.unsent_max_offset =
+ max_rx_offset;
+ strm->pe.index = NGTCP2_PQ_BAD_INDEX;
+ strm->mem = mem;
+ strm->app_error_code = 0;
+}
+
+void ngtcp2_strm_free(ngtcp2_strm *strm) {
+ ngtcp2_ksl_it it;
+
+ if (strm == NULL) {
+ return;
+ }
+
+ if (strm->tx.streamfrq) {
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it),
+ strm->frc_objalloc, strm->mem);
+ }
+
+ ngtcp2_ksl_free(strm->tx.streamfrq);
+ ngtcp2_mem_free(strm->mem, strm->tx.streamfrq);
+ }
+
+ if (strm->rx.rob) {
+ ngtcp2_rob_free(strm->rx.rob);
+ ngtcp2_mem_free(strm->mem, strm->rx.rob);
+ }
+
+ if (strm->tx.acked_offset) {
+ ngtcp2_gaptr_free(strm->tx.acked_offset);
+ ngtcp2_mem_free(strm->mem, strm->tx.acked_offset);
+ }
+}
+
+static int strm_rob_init(ngtcp2_strm *strm) {
+ int rv;
+ ngtcp2_rob *rob = ngtcp2_mem_malloc(strm->mem, sizeof(*rob));
+
+ if (rob == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ rv = ngtcp2_rob_init(rob, 8 * 1024, strm->mem);
+ if (rv != 0) {
+ ngtcp2_mem_free(strm->mem, rob);
+ return rv;
+ }
+
+ strm->rx.rob = rob;
+
+ return 0;
+}
+
+uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) {
+ if (strm->rx.rob == NULL) {
+ return strm->rx.cont_offset;
+ }
+ return ngtcp2_rob_first_gap_offset(strm->rx.rob);
+}
+
+/* strm_rob_heavily_fragmented returns nonzero if the number of gaps
+ in |rob| exceeds the limit. */
+static int strm_rob_heavily_fragmented(ngtcp2_rob *rob) {
+ return ngtcp2_ksl_len(&rob->gapksl) >= 1000;
+}
+
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset) {
+ int rv;
+
+ if (strm->rx.rob == NULL) {
+ rv = strm_rob_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (strm->rx.cont_offset) {
+ rv = ngtcp2_rob_remove_prefix(strm->rx.rob, strm->rx.cont_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
+
+ if (strm_rob_heavily_fragmented(strm->rx.rob)) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
+ return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+}
+
+int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) {
+ if (strm->rx.rob == NULL) {
+ strm->rx.cont_offset = offset;
+ return 0;
+ }
+
+ return ngtcp2_rob_remove_prefix(strm->rx.rob, offset);
+}
+
+void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) {
+ strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR;
+}
+
+static int strm_streamfrq_init(ngtcp2_strm *strm) {
+ ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq));
+ if (streamfrq == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem);
+
+ strm->tx.streamfrq = streamfrq;
+
+ return 0;
+}
+
+int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) {
+ int rv;
+
+ assert(frc->fr.type == NGTCP2_FRAME_STREAM);
+ assert(frc->next == NULL);
+
+ if (strm->tx.streamfrq == NULL) {
+ rv = strm_streamfrq_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset,
+ frc);
+}
+
+static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
+ ngtcp2_frame_chain **pfrc) {
+ ngtcp2_frame_chain *frc, *nfrc;
+ ngtcp2_stream *fr, *nfr;
+ uint64_t offset, end_offset;
+ size_t idx, end_idx;
+ uint64_t base_offset, end_base_offset;
+ ngtcp2_range gap;
+ ngtcp2_vec *v;
+ int rv;
+ ngtcp2_ksl_it it;
+
+ *pfrc = NULL;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.stream;
+
+ ngtcp2_ksl_remove_hint(strm->tx.streamfrq, &it, &it, &fr->offset);
+
+ idx = 0;
+ offset = fr->offset;
+ base_offset = 0;
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, offset);
+ if (gap.begin < offset) {
+ gap.begin = offset;
+ }
+
+ for (; idx < fr->datacnt && offset < gap.begin; ++idx) {
+ v = &fr->data[idx];
+ if (offset + v->len > gap.begin) {
+ base_offset = gap.begin - offset;
+ break;
+ }
+
+ offset += v->len;
+ }
+
+ if (idx == fr->datacnt) {
+ if (fr->fin) {
+ if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0);
+ return 0;
+ }
+
+ fr->offset += ngtcp2_vec_len(fr->data, fr->datacnt);
+ fr->datacnt = 0;
+
+ *pfrc = frc;
+
+ return 0;
+ }
+
+ if (fr->offset == 0 && fr->datacnt == 0 && strm->tx.offset == 0 &&
+ !(strm->flags & NGTCP2_STRM_FLAG_ANY_ACKED)) {
+ *pfrc = frc;
+
+ return 0;
+ }
+
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ continue;
+ }
+
+ assert(gap.begin == offset + base_offset);
+
+ end_idx = idx;
+ end_offset = offset;
+ end_base_offset = 0;
+
+ for (; end_idx < fr->datacnt; ++end_idx) {
+ v = &fr->data[end_idx];
+ if (end_offset + v->len > gap.end) {
+ end_base_offset = gap.end - end_offset;
+ break;
+ }
+
+ end_offset += v->len;
+ }
+
+ if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) {
+ *pfrc = frc;
+ return 0;
+ }
+
+ if (fr->datacnt == end_idx) {
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ memcpy(nfr->data, fr->data + end_idx,
+ sizeof(nfr->data[0]) * (fr->datacnt - end_idx));
+
+ assert(nfr->data[0].len > end_base_offset);
+
+ nfr->type = NGTCP2_FRAME_STREAM;
+ nfr->flags = 0;
+ nfr->fin = fr->fin;
+ nfr->stream_id = fr->stream_id;
+ nfr->offset = end_offset + end_base_offset;
+ nfr->datacnt = fr->datacnt - end_idx;
+ nfr->data[0].base += end_base_offset;
+ nfr->data[0].len -= (size_t)end_base_offset;
+
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ if (end_base_offset) {
+ ++end_idx;
+ }
+
+ memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx));
+
+ assert(fr->data[0].len > base_offset);
+
+ fr->fin = 0;
+ fr->offset = offset + base_offset;
+ fr->datacnt = end_idx - idx;
+ if (end_base_offset) {
+ assert(fr->data[fr->datacnt - 1].len > end_base_offset);
+ fr->data[fr->datacnt - 1].len = (size_t)end_base_offset;
+ }
+ fr->data[0].base += base_offset;
+ fr->data[0].len -= (size_t)base_offset;
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ return 0;
+}
+
+int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
+ size_t left) {
+ ngtcp2_stream *fr, *nfr;
+ ngtcp2_frame_chain *frc, *nfrc;
+ int rv;
+ size_t nmerged;
+ uint64_t datalen;
+ ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT];
+ ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT];
+ size_t acnt, bcnt;
+ uint64_t unacked_offset;
+
+ if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ rv = strm_streamfrq_unacked_pop(strm, &frc);
+ if (rv != 0) {
+ return rv;
+ }
+ if (frc == NULL) {
+ *pfrc = NULL;
+ return 0;
+ }
+
+ fr = &frc->fr.stream;
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (left == 0) {
+ /* datalen could be zero if 0 length STREAM has been sent */
+ if (datalen || ngtcp2_ksl_len(strm->tx.streamfrq) > 1) {
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+ *pfrc = NULL;
+ return 0;
+ }
+ }
+
+ if (datalen > left) {
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ bcnt = 0;
+ ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_STREAM_DATACNT);
+
+ assert(acnt > 0);
+ assert(bcnt > 0);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, bcnt, strm->frc_objalloc, strm->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ nfr->type = NGTCP2_FRAME_STREAM;
+ nfr->flags = 0;
+ nfr->fin = fr->fin;
+ nfr->stream_id = fr->stream_id;
+ nfr->offset = fr->offset + left;
+ nfr->datacnt = bcnt;
+ ngtcp2_vec_copy(nfr->data, b, bcnt);
+
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, acnt, strm->frc_objalloc, strm->mem);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ *nfr = *fr;
+ nfr->fin = 0;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+ }
+
+ left -= (size_t)datalen;
+
+ ngtcp2_vec_copy(a, fr->data, fr->datacnt);
+ acnt = fr->datacnt;
+
+ for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) {
+ unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm);
+ if (unacked_offset != fr->offset + datalen) {
+ assert(fr->offset + datalen < unacked_offset);
+ break;
+ }
+
+ rv = strm_streamfrq_unacked_pop(strm, &nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+ if (nfrc == NULL) {
+ break;
+ }
+
+ nfr = &nfrc->fr.stream;
+
+ if (nfr->fin && nfr->datacnt == 0) {
+ fr->fin = 1;
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ break;
+ }
+
+ nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left,
+ NGTCP2_MAX_STREAM_DATACNT);
+ if (nmerged == 0) {
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+ break;
+ }
+
+ datalen += nmerged;
+ left -= nmerged;
+
+ if (nfr->datacnt == 0) {
+ fr->fin = nfr->fin;
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ continue;
+ }
+
+ nfr->offset += nmerged;
+
+ rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ break;
+ }
+
+ if (acnt == fr->datacnt) {
+ if (acnt > 0) {
+ fr->data[acnt - 1] = a[acnt - 1];
+ }
+
+ *pfrc = frc;
+ return 0;
+ }
+
+ assert(acnt > fr->datacnt);
+
+ rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+ &nfrc, acnt, strm->frc_objalloc, strm->mem);
+ if (rv != 0) {
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ return rv;
+ }
+
+ nfr = &nfrc->fr.stream;
+ *nfr = *fr;
+ nfr->datacnt = acnt;
+ ngtcp2_vec_copy(nfr->data, a, acnt);
+
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+
+ *pfrc = nfrc;
+
+ return 0;
+}
+
+uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_stream *fr;
+ ngtcp2_range gap;
+ ngtcp2_ksl_it it;
+ uint64_t datalen;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ fr = &frc->fr.stream;
+
+ gap = ngtcp2_strm_get_unacked_range_after(strm, fr->offset);
+
+ datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
+
+ if (gap.begin <= fr->offset) {
+ return fr->offset;
+ }
+ if (gap.begin < fr->offset + datalen) {
+ return gap.begin;
+ }
+ if (fr->offset + datalen == gap.begin && fr->fin &&
+ !(strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED)) {
+ return fr->offset + datalen;
+ }
+ }
+
+ return (uint64_t)-1;
+}
+
+ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) {
+ ngtcp2_ksl_it it;
+
+ assert(strm->tx.streamfrq);
+ assert(ngtcp2_ksl_len(strm->tx.streamfrq));
+
+ it = ngtcp2_ksl_begin(strm->tx.streamfrq);
+ return ngtcp2_ksl_it_get(&it);
+}
+
+int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm) {
+ return strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0;
+}
+
+void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) {
+ ngtcp2_frame_chain *frc;
+ ngtcp2_ksl_it it;
+
+ if (strm->tx.streamfrq == NULL) {
+ return;
+ }
+
+ for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
+ ngtcp2_ksl_it_next(&it)) {
+ frc = ngtcp2_ksl_it_get(&it);
+ ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
+ }
+ ngtcp2_ksl_clear(strm->tx.streamfrq);
+}
+
+int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm) {
+ return strm->pe.index != NGTCP2_PQ_BAD_INDEX;
+}
+
+int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) {
+ if (strm->tx.acked_offset == NULL) {
+ return strm->tx.cont_acked_offset == strm->tx.offset;
+ }
+
+ return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset) ==
+ strm->tx.offset;
+}
+
+int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm) {
+ return (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) &&
+ ngtcp2_strm_is_all_tx_data_acked(strm);
+}
+
+ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
+ uint64_t offset) {
+ ngtcp2_range gap;
+
+ if (strm->tx.acked_offset == NULL) {
+ gap.begin = strm->tx.cont_acked_offset;
+ gap.end = UINT64_MAX;
+ return gap;
+ }
+
+ return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
+}
+
+uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
+ if (strm->tx.acked_offset == NULL) {
+ return strm->tx.cont_acked_offset;
+ }
+
+ return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset);
+}
+
+static int strm_acked_offset_init(ngtcp2_strm *strm) {
+ ngtcp2_gaptr *acked_offset =
+ ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset));
+
+ if (acked_offset == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ ngtcp2_gaptr_init(acked_offset, strm->mem);
+
+ strm->tx.acked_offset = acked_offset;
+
+ return 0;
+}
+
+int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) {
+ int rv;
+
+ if (strm->tx.acked_offset == NULL) {
+ if (strm->tx.cont_acked_offset == offset) {
+ strm->tx.cont_acked_offset += len;
+ return 0;
+ }
+
+ rv = strm_acked_offset_init(strm);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv =
+ ngtcp2_gaptr_push(strm->tx.acked_offset, 0, strm->tx.cont_acked_offset);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+
+ return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+}
+
+void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm,
+ uint64_t app_error_code) {
+ if (strm->flags & NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET) {
+ return;
+ }
+
+ assert(0 == strm->app_error_code);
+
+ strm->flags |= NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET;
+ strm->app_error_code = app_error_code;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
new file mode 100644
index 0000000..8e3cfe8
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
@@ -0,0 +1,310 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_STRM_H
+#define NGTCP2_STRM_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_rob.h"
+#include "ngtcp2_map.h"
+#include "ngtcp2_gaptr.h"
+#include "ngtcp2_ksl.h"
+#include "ngtcp2_pq.h"
+
+typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
+
+/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_STRM_FLAG_NONE 0x00u
+/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream
+ data is not allowed. */
+#define NGTCP2_STRM_FLAG_SHUT_RD 0x01u
+/* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of
+ stream data is not allowed. */
+#define NGTCP2_STRM_FLAG_SHUT_WR 0x02u
+#define NGTCP2_STRM_FLAG_SHUT_RDWR \
+ (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR)
+/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from
+ the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is also
+ set. */
+#define NGTCP2_STRM_FLAG_SENT_RST 0x04u
+/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received
+ from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD
+ is also set. */
+#define NGTCP2_STRM_FLAG_RECV_RST 0x08u
+/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent
+ from the local endpoint. */
+#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10u
+/* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM
+ is acknowledged by peer. */
+#define NGTCP2_STRM_FLAG_RST_ACKED 0x20u
+/* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set
+ is acknowledged by a remote endpoint. */
+#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40u
+/* NGTCP2_STRM_FLAG_ANY_ACKED indicates that any portion of stream
+ data, including 0 length segment, is acknowledged. */
+#define NGTCP2_STRM_FLAG_ANY_ACKED 0x80u
+/* NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET indicates that app_error_code
+ field is set. This resolves the ambiguity that the initial
+ app_error_code value 0 might be a proper application error code.
+ In this case, without this flag, we are unable to distinguish
+ assigned value from unassigned one. */
+#define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100u
+/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when
+ stream_stop_sending callback is called. */
+#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200u
+
+typedef struct ngtcp2_strm ngtcp2_strm;
+
+struct ngtcp2_strm {
+ union {
+ struct {
+ ngtcp2_pq_entry pe;
+ uint64_t cycle;
+ ngtcp2_objalloc *frc_objalloc;
+
+ struct {
+ /* acked_offset tracks acknowledged outgoing data. */
+ ngtcp2_gaptr *acked_offset;
+ /* cont_acked_offset is the offset that all data up to this offset
+ is acknowledged by a remote endpoint. It is used until the
+ remote endpoint acknowledges data in out-of-order. After that,
+ acked_offset is used instead. */
+ uint64_t cont_acked_offset;
+ /* streamfrq contains STREAM frame for retransmission. The flow
+ control credits have been paid when they are transmitted first
+ time. There are no restriction regarding flow control for
+ retransmission. */
+ ngtcp2_ksl *streamfrq;
+ /* offset is the next offset of outgoing data. In other words, it
+ is the number of bytes sent in this stream without
+ duplication. */
+ uint64_t offset;
+ /* max_tx_offset is the maximum offset that local endpoint can
+ send for this stream. */
+ uint64_t max_offset;
+ /* last_max_stream_data_ts is the timestamp when last
+ MAX_STREAM_DATA frame is sent. */
+ ngtcp2_tstamp last_max_stream_data_ts;
+ /* loss_count is the number of packets that contain STREAM
+ frame for this stream and are declared to be lost. It may
+ include the spurious losses. It does not include a packet
+ whose contents have been reclaimed for PTO and which is
+ later declared to be lost. Those data are not blocked by
+ the flow control and will be sent immediately if no other
+ restrictions are applied. */
+ size_t loss_count;
+ /* last_lost_pkt_num is the packet number of the packet that
+ is counted to loss_count. It is used to avoid to count
+ multiple STREAM frames in one lost packet. */
+ int64_t last_lost_pkt_num;
+ } tx;
+
+ struct {
+ /* rob is the reorder buffer for incoming stream data. The data
+ received in out of order is buffered and sorted by its offset
+ in this object. */
+ ngtcp2_rob *rob;
+ /* cont_offset is the largest offset of consecutive data. It is
+ used until the endpoint receives out-of-order data. After
+ that, rob is used to track the offset and data. */
+ uint64_t cont_offset;
+ /* last_offset is the largest offset of stream data received for
+ this stream. */
+ uint64_t last_offset;
+ /* max_offset is the maximum offset that remote endpoint can send
+ to this stream. */
+ uint64_t max_offset;
+ /* unsent_max_offset is the maximum offset that remote endpoint
+ can send to this stream, and it is not notified to the remote
+ endpoint. unsent_max_offset >= max_offset must be hold. */
+ uint64_t unsent_max_offset;
+ /* window is the stream-level flow control window size. */
+ uint64_t window;
+ } rx;
+
+ const ngtcp2_mem *mem;
+ int64_t stream_id;
+ void *stream_user_data;
+ /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
+ uint32_t flags;
+ /* app_error_code is an error code the local endpoint sent in
+ RESET_STREAM or STOP_SENDING, or received from a remote endpoint
+ in RESET_STREAM or STOP_SENDING. First application error code is
+ chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is
+ set in flags field. */
+ uint64_t app_error_code;
+ };
+
+ ngtcp2_opl_entry oplent;
+ };
+};
+
+/*
+ * ngtcp2_strm_init initializes |strm|.
+ */
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+ uint64_t max_rx_offset, uint64_t max_tx_offset,
+ void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_strm_free deallocates memory allocated for |strm|. This
+ * function does not free the memory pointed by |strm| itself.
+ */
+void ngtcp2_strm_free(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_rx_offset returns the minimum offset of stream data
+ * which is not received yet.
+ */
+uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_recv_reordering handles reordered data.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
+ size_t datalen, uint64_t offset);
+
+/*
+ * ngtcp2_strm_update_rx_offset tells that data up to offset bytes are
+ * received in order.
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset);
+
+/*
+ * ngtcp2_strm_shutdown shutdowns |strm|. |flags| should be
+ * NGTCP2_STRM_FLAG_SHUT_RD, and/or NGTCP2_STRM_FLAG_SHUT_WR.
+ */
+void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags);
+
+/*
+ * ngtcp2_strm_streamfrq_push pushes |frc| to streamfrq for
+ * retransmission.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc);
+
+/*
+ * ngtcp2_strm_streamfrq_pop pops the first ngtcp2_frame_chain and
+ * assigns it to |*pfrc|. This function splits into or merges several
+ * ngtcp2_frame_chain objects so that the returned ngtcp2_frame_chain
+ * has at most |left| data length. If there is no frames to send,
+ * this function returns 0 and |*pfrc| is NULL.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ * Out of memory
+ */
+int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
+ size_t left);
+
+/*
+ * ngtcp2_strm_streamfrq_unacked_offset returns the smallest offset of
+ * unacknowledged stream data held in strm->tx.streamfrq.
+ */
+uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain.
+ * The queue must not be empty.
+ */
+ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty.
+ */
+int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_streamfrq_clear removes all frames from streamfrq.
+ */
+void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_is_tx_queued returns nonzero if |strm| is queued.
+ */
+int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_is_all_tx_data_acked returns nonzero if all outgoing
+ * data for |strm| which have sent so far have been acknowledged.
+ */
+int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_is_all_tx_data_fin_acked behaves like
+ * ngtcp2_strm_is_all_tx_data_acked, but it also requires that STREAM
+ * frame with fin bit set is acknowledged.
+ */
+int ngtcp2_strm_is_all_tx_data_fin_acked(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_get_unacked_range_after returns the range that is not
+ * acknowledged yet and intersects or comes after |offset|.
+ */
+ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
+ uint64_t offset);
+
+/*
+ * ngtcp2_strm_get_acked_offset returns offset, that is the data up to
+ * this offset have been acknowledged by a remote endpoint. It
+ * returns 0 if no data is acknowledged.
+ */
+uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm);
+
+/*
+ * ngtcp2_strm_ack_data tells |strm| that the data [offset,
+ * offset+len) is acknowledged by a remote endpoint.
+ */
+int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len);
+
+/*
+ * ngtcp2_strm_set_app_error_code sets |app_error_code| to |strm| and
+ * set NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag. If the flag is
+ * already set, this function does nothing.
+ */
+void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm, uint64_t app_error_code);
+
+#endif /* NGTCP2_STRM_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c
new file mode 100644
index 0000000..7c7d9ae
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.c
@@ -0,0 +1,71 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_unreachable.h"
+
+#include <stdio.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <stdlib.h>
+#ifdef WIN32
+# include <io.h>
+#endif /* WIN32 */
+
+void ngtcp2_unreachable_fail(const char *file, int line, const char *func) {
+ char *buf;
+ size_t buflen;
+ int rv;
+
+#define NGTCP2_UNREACHABLE_TEMPLATE "%s:%d %s: Unreachable.\n"
+
+ rv = snprintf(NULL, 0, NGTCP2_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+ /* here we explicitly use system malloc */
+ buflen = (size_t)rv + 1;
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ abort();
+ }
+
+ rv = snprintf(buf, buflen, NGTCP2_UNREACHABLE_TEMPLATE, file, line, func);
+ if (rv < 0) {
+ abort();
+ }
+
+#ifndef WIN32
+ while (write(STDERR_FILENO, buf, (size_t)rv) == -1 && errno == EINTR)
+ ;
+#else /* WIN32 */
+ _write(_fileno(stderr), buf, (unsigned int)rv);
+#endif /* WIN32 */
+
+ free(buf);
+
+ abort();
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h
new file mode 100644
index 0000000..11a6aaa
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_unreachable.h
@@ -0,0 +1,46 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_UNREACHABLE_H
+#define NGTCP2_UNREACHABLE_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#define ngtcp2_unreachable() \
+ ngtcp2_unreachable_fail(__FILE__, __LINE__, __func__)
+
+#ifdef _MSC_VER
+__declspec(noreturn)
+#endif /* _MSC_VER */
+ void ngtcp2_unreachable_fail(const char *file, int line, const char *func)
+#ifndef _MSC_VER
+ __attribute__((noreturn))
+#endif /* !_MSC_VER */
+ ;
+
+#endif /* NGTCP2_UNREACHABLE_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c
new file mode 100644
index 0000000..257332e
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c
@@ -0,0 +1,243 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_vec.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_str.h"
+
+ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) {
+ vec->base = (uint8_t *)base;
+ vec->len = len;
+ return vec;
+}
+
+int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem) {
+ size_t len;
+ uint8_t *p;
+
+ len = sizeof(ngtcp2_vec) + datalen;
+
+ *pvec = ngtcp2_mem_malloc(mem, len);
+ if (*pvec == NULL) {
+ return NGTCP2_ERR_NOMEM;
+ }
+
+ p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec);
+ (*pvec)->base = p;
+ (*pvec)->len = datalen;
+ if (datalen) {
+ /* p = */ ngtcp2_cpymem(p, data, datalen);
+ }
+
+ return 0;
+}
+
+void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) {
+ ngtcp2_mem_free(mem, vec);
+}
+
+uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) {
+ size_t i;
+ size_t res = 0;
+
+ for (i = 0; i < n; ++i) {
+ res += vec[i].len;
+ }
+
+ return res;
+}
+
+int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n) {
+ uint64_t res = 0;
+ size_t len;
+ size_t i;
+
+ for (i = 0; i < n; ++i) {
+ len = vec[i].len;
+ if (len > NGTCP2_MAX_VARINT - res) {
+ return -1;
+ }
+
+ res += len;
+ }
+
+ return (int64_t)res;
+}
+
+ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst,
+ size_t *pdstcnt, size_t left, size_t maxcnt) {
+ size_t i;
+ size_t srccnt = *psrccnt;
+ size_t nmove;
+ size_t extra = 0;
+
+ for (i = 0; i < srccnt; ++i) {
+ if (left >= src[i].len) {
+ left -= src[i].len;
+ continue;
+ }
+
+ if (*pdstcnt && src[srccnt - 1].base + src[srccnt - 1].len == dst[0].base) {
+ if (*pdstcnt + srccnt - i - 1 > maxcnt) {
+ return -1;
+ }
+
+ dst[0].len += src[srccnt - 1].len;
+ dst[0].base = src[srccnt - 1].base;
+ extra = src[srccnt - 1].len;
+ --srccnt;
+ } else if (*pdstcnt + srccnt - i > maxcnt) {
+ return -1;
+ }
+
+ if (left == 0) {
+ *psrccnt = i;
+ } else {
+ *psrccnt = i + 1;
+ }
+
+ nmove = srccnt - i;
+ if (nmove) {
+ memmove(dst + nmove, dst, sizeof(ngtcp2_vec) * (*pdstcnt));
+ *pdstcnt += nmove;
+ memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove);
+ }
+
+ dst[0].len -= left;
+ dst[0].base += left;
+ src[i].len = left;
+
+ if (nmove == 0) {
+ extra -= left;
+ }
+
+ return (ngtcp2_ssize)(ngtcp2_vec_len(dst, nmove) + extra);
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
+ size_t *psrccnt, size_t left, size_t maxcnt) {
+ size_t orig_left = left;
+ size_t i;
+ ngtcp2_vec *a, *b;
+
+ assert(maxcnt);
+
+ if (*pdstcnt == 0) {
+ if (*psrccnt == 0) {
+ return 0;
+ }
+
+ a = &dst[0];
+ b = &src[0];
+
+ if (left >= b->len) {
+ *a = *b;
+ ++*pdstcnt;
+ left -= b->len;
+ i = 1;
+ } else {
+ a->len = left;
+ a->base = b->base;
+
+ b->len -= left;
+ b->base += left;
+
+ return left;
+ }
+ } else {
+ i = 0;
+ }
+
+ for (; left && i < *psrccnt; ++i) {
+ a = &dst[*pdstcnt - 1];
+ b = &src[i];
+
+ if (left >= b->len) {
+ if (a->base + a->len == b->base) {
+ a->len += b->len;
+ } else if (*pdstcnt == maxcnt) {
+ break;
+ } else {
+ dst[(*pdstcnt)++] = *b;
+ }
+ left -= b->len;
+ continue;
+ }
+
+ if (a->base + a->len == b->base) {
+ a->len += left;
+ } else if (*pdstcnt == maxcnt) {
+ break;
+ } else {
+ dst[*pdstcnt].len = left;
+ dst[*pdstcnt].base = b->base;
+ ++*pdstcnt;
+ }
+
+ b->len -= left;
+ b->base += left;
+ left = 0;
+
+ break;
+ }
+
+ memmove(src, src + i, sizeof(ngtcp2_vec) * (*psrccnt - i));
+ *psrccnt -= i;
+
+ return orig_left - left;
+}
+
+size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
+ const ngtcp2_vec *src, size_t srccnt,
+ size_t left) {
+ size_t i, j;
+
+ for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) {
+ if (src[i].len == 0) {
+ ++i;
+ continue;
+ }
+ dst[j] = src[i];
+ if (dst[j].len > left) {
+ dst[j].len = left;
+ return j + 1;
+ }
+ left -= dst[j].len;
+ ++i;
+ ++j;
+ }
+
+ return j;
+}
+
+void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) {
+ memcpy(dst, src, sizeof(ngtcp2_vec) * cnt);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h
new file mode 100644
index 0000000..a39c439
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h
@@ -0,0 +1,120 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2018 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_VEC_H
+#define NGTCP2_VEC_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_mem.h"
+
+/*
+ * ngtcp2_vec_lit is a convenient macro to fill the object pointed by
+ * |DEST| with the literal string |LIT|.
+ */
+#define ngtcp2_vec_lit(DEST, LIT) \
+ ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST))
+
+/*
+ * ngtcp2_vec_init initializes |vec| with the given parameters. It
+ * returns |vec|.
+ */
+ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len);
+
+/*
+ * ngtcp2_vec_new allocates and initializes |*pvec| with given |data|
+ * of length |datalen|. This function allocates memory for |*pvec|
+ * and the given data with a single allocation, and the contents
+ * pointed by |data| is copied into the allocated memory space. To
+ * free the allocated memory, call ngtcp2_vec_del.
+ */
+int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen,
+ const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_vec_del frees the memory allocated by |vec| which is
+ * allocated and initialized by ngtcp2_vec_new.
+ */
+void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements.
+ */
+uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n);
+
+/*
+ * ngtcp2_vec_len_varint is similar to ngtcp2_vec_len, but it returns
+ * -1 if the sum of the length exceeds NGTCP2_MAX_VARINT.
+ */
+int64_t ngtcp2_vec_len_varint(const ngtcp2_vec *vec, size_t n);
+
+/*
+ * ngtcp2_vec_split splits |src| to |dst| so that the sum of the
+ * length in |src| does not exceed |left| bytes. The |maxcnt| is the
+ * maximum number of elements which |dst| array can contain. The
+ * caller must set |*psrccnt| to the number of elements of |src|.
+ * Similarly, the caller must set |*pdstcnt| to the number of elements
+ * of |dst|. The split does not necessarily occur at the boundary of
+ * ngtcp2_vec object. After split has done, this function updates
+ * |*psrccnt| and |*pdstcnt|. This function returns the number of
+ * bytes moved from |src| to |dst|. If split cannot be made because
+ * doing so exceeds |maxcnt|, this function returns -1.
+ */
+ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst,
+ size_t *pdstcnt, size_t left, size_t maxcnt);
+
+/*
+ * ngtcp2_vec_merge merges |src| into |dst| by moving at most |left|
+ * bytes from |src|. The |maxcnt| is the maximum number of elements
+ * which |dst| array can contain. The caller must set |*pdstcnt| to
+ * the number of elements of |dst|. Similarly, the caller must set
+ * |*psrccnt| to the number of elements of |src|. After merge has
+ * done, this function updates |*psrccnt| and |*pdstcnt|. This
+ * function returns the number of bytes moved from |src| to |dst|.
+ */
+size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src,
+ size_t *psrccnt, size_t left, size_t maxcnt);
+
+/*
+ * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of
+ * length |dstcnt|. The total number of bytes which the copied
+ * ngtcp2_vec refers to is at most |left|. The empty elements in
+ * |src| are ignored. This function returns the number of elements
+ * copied.
+ */
+size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt,
+ const ngtcp2_vec *src, size_t srccnt,
+ size_t left);
+
+/*
+ * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must
+ * have sufficient capacity.
+ */
+void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt);
+
+#endif /* NGTCP2_VEC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
new file mode 100644
index 0000000..b31162c
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
@@ -0,0 +1,39 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM,
+ NGTCP2_VERSION};
+
+const ngtcp2_info *ngtcp2_version(int least_version) {
+ if (least_version > NGTCP2_VERSION_NUM) {
+ return NULL;
+ }
+ return &version;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
new file mode 100644
index 0000000..71c816e
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c
@@ -0,0 +1,99 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Translated to C from the original C++ code
+ * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h
+ * with the following license:
+ *
+ * // Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * // Use of this source code is governed by a BSD-style license that can be
+ * // found in the LICENSE file.
+ */
+#include "ngtcp2_window_filter.h"
+
+#include <string.h>
+
+void ngtcp2_window_filter_init(ngtcp2_window_filter *wf,
+ uint64_t window_length) {
+ wf->window_length = window_length;
+ memset(wf->estimates, 0, sizeof(wf->estimates));
+}
+
+void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time) {
+ if (wf->estimates[0].sample == 0 || new_sample > wf->estimates[0].sample ||
+ new_time - wf->estimates[2].time > wf->window_length) {
+ ngtcp2_window_filter_reset(wf, new_sample, new_time);
+ return;
+ }
+
+ if (new_sample > wf->estimates[1].sample) {
+ wf->estimates[1].sample = new_sample;
+ wf->estimates[1].time = new_time;
+ wf->estimates[2] = wf->estimates[1];
+ } else if (new_sample > wf->estimates[2].sample) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ }
+
+ if (new_time - wf->estimates[0].time > wf->window_length) {
+ wf->estimates[0] = wf->estimates[1];
+ wf->estimates[1] = wf->estimates[2];
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+
+ if (new_time - wf->estimates[0].time > wf->window_length) {
+ wf->estimates[0] = wf->estimates[1];
+ wf->estimates[1] = wf->estimates[2];
+ }
+ return;
+ }
+
+ if (wf->estimates[1].sample == wf->estimates[0].sample &&
+ new_time - wf->estimates[1].time > wf->window_length >> 2) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ wf->estimates[1] = wf->estimates[2];
+ return;
+ }
+
+ if (wf->estimates[2].sample == wf->estimates[1].sample &&
+ new_time - wf->estimates[2].time > wf->window_length >> 1) {
+ wf->estimates[2].sample = new_sample;
+ wf->estimates[2].time = new_time;
+ }
+}
+
+void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time) {
+ wf->estimates[0].sample = new_sample;
+ wf->estimates[0].time = new_time;
+ wf->estimates[1] = wf->estimates[2] = wf->estimates[0];
+}
+
+uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf) {
+ return wf->estimates[0].sample;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h
new file mode 100644
index 0000000..50415f1
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h
@@ -0,0 +1,65 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2021 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Translated to C from the original C++ code
+ * https://quiche.googlesource.com/quiche/+/5be974e29f7e71a196e726d6e2272676d33ab77d/quic/core/congestion_control/windowed_filter.h
+ * with the following license:
+ *
+ * // Copyright (c) 2016 The Chromium Authors. All rights reserved.
+ * // Use of this source code is governed by a BSD-style license that can be
+ * // found in the LICENSE file.
+ */
+#ifndef NGTCP2_WINDOW_FILTER_H
+#define NGTCP2_WINDOW_FILTER_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_window_filter_sample {
+ uint64_t sample;
+ uint64_t time;
+} ngtcp2_window_filter_sample;
+
+typedef struct ngtcp2_window_filter {
+ uint64_t window_length;
+ ngtcp2_window_filter_sample estimates[3];
+} ngtcp2_window_filter;
+
+void ngtcp2_window_filter_init(ngtcp2_window_filter *wf,
+ uint64_t window_length);
+
+void ngtcp2_window_filter_update(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time);
+
+void ngtcp2_window_filter_reset(ngtcp2_window_filter *wf, uint64_t new_sample,
+ uint64_t new_time);
+
+uint64_t ngtcp2_window_filter_get_best(ngtcp2_window_filter *wf);
+
+#endif /* NGTCP2_WINDOW_FILTER_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h
new file mode 100644
index 0000000..d4ba09d
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h
@@ -0,0 +1,5906 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2017 ngtcp2 contributors
+ * Copyright (c) 2017 nghttp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_H
+#define NGTCP2_H
+
+/* Define WIN32 when build target is Win32 API (borrowed from
+ libcurl) */
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable : 4324)
+#endif
+
+#include <stdlib.h>
+#if defined(_MSC_VER) && (_MSC_VER < 1800)
+/* MSVC < 2013 does not have inttypes.h because it is not C99
+ compliant. See compiler macros and version number in
+ https://sourceforge.net/p/predef/wiki/Compilers/ */
+# include <stdint.h>
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+# include <inttypes.h>
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifndef NGTCP2_USE_GENERIC_SOCKADDR
+# ifdef WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <ws2tcpip.h>
+# else
+# include <sys/socket.h>
+# include <netinet/in.h>
+# endif
+#endif
+
+#ifdef AF_INET
+# define NGTCP2_AF_INET AF_INET
+#else
+# define NGTCP2_AF_INET 2
+#endif
+
+#ifdef AF_INET6
+# define NGTCP2_AF_INET6 AF_INET6
+#else
+# define NGTCP2_AF_INET6 23
+# define NGTCP2_USE_GENERIC_IPV6_SOCKADDR
+#endif
+
+#include <ngtcp2/version.h>
+
+#ifdef NGTCP2_STATICLIB
+# define NGTCP2_EXTERN
+#elif defined(WIN32)
+# ifdef BUILDING_NGTCP2
+# define NGTCP2_EXTERN __declspec(dllexport)
+# else /* !BUILDING_NGTCP2 */
+# define NGTCP2_EXTERN __declspec(dllimport)
+# endif /* !BUILDING_NGTCP2 */
+#else /* !defined(WIN32) */
+# ifdef BUILDING_NGTCP2
+# define NGTCP2_EXTERN __attribute__((visibility("default")))
+# else /* !BUILDING_NGTCP2 */
+# define NGTCP2_EXTERN
+# endif /* !BUILDING_NGTCP2 */
+#endif /* !defined(WIN32) */
+
+#ifdef _MSC_VER
+# define NGTCP2_ALIGN(N) __declspec(align(N))
+#else /* !_MSC_VER */
+# define NGTCP2_ALIGN(N) __attribute__((aligned(N)))
+#endif /* !_MSC_VER */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_ssize` is signed counterpart of size_t.
+ */
+typedef ptrdiff_t ngtcp2_ssize;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_malloc` is a custom memory allocator to replace
+ * :manpage:`malloc(3)`. The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
+ */
+typedef void *(*ngtcp2_malloc)(size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_free` is a custom memory allocator to replace
+ * :manpage:`free(3)`. The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
+ */
+typedef void (*ngtcp2_free)(void *ptr, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_calloc` is a custom memory allocator to replace
+ * :manpage:`calloc(3)`. The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
+ */
+typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_realloc` is a custom memory allocator to replace
+ * :manpage:`realloc(3)`. The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
+ */
+typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_mem` is a custom memory allocator. The
+ * :member:`user_data` field is passed to each allocator function.
+ * This can be used, for example, to achieve per-connection memory
+ * pool.
+ *
+ * In the following example code, ``my_malloc``, ``my_free``,
+ * ``my_calloc`` and ``my_realloc`` are the replacement of the
+ * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
+ * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
+ *
+ * void *my_malloc_cb(size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_malloc(size);
+ * }
+ *
+ * void my_free_cb(void *ptr, void *user_data) {
+ * (void)user_data;
+ * my_free(ptr);
+ * }
+ *
+ * void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_calloc(nmemb, size);
+ * }
+ *
+ * void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ * (void)user_data;
+ * return my_realloc(ptr, size);
+ * }
+ *
+ * void conn_new() {
+ * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb,
+ * my_realloc_cb};
+ *
+ * ...
+ * }
+ */
+typedef struct ngtcp2_mem {
+ /**
+ * :member:`user_data` is an arbitrary user supplied data. This
+ * is passed to each allocator function.
+ */
+ void *user_data;
+ /**
+ * :member:`malloc` is a custom allocator function to replace
+ * :manpage:`malloc(3)`.
+ */
+ ngtcp2_malloc malloc;
+ /**
+ * :member:`free` is a custom allocator function to replace
+ * :manpage:`free(3)`.
+ */
+ ngtcp2_free free;
+ /**
+ * :member:`calloc` is a custom allocator function to replace
+ * :manpage:`calloc(3)`.
+ */
+ ngtcp2_calloc calloc;
+ /**
+ * :member:`realloc` is a custom allocator function to replace
+ * :manpage:`realloc(3)`.
+ */
+ ngtcp2_realloc realloc;
+} ngtcp2_mem;
+
+/**
+ * @macrosection
+ *
+ * Time related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second.
+ */
+#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds
+ * to 1 millisecond.
+ */
+#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
+ * to 1 microsecond.
+ */
+#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
+ * 1 nanosecond.
+ */
+#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL)
+
+/**
+ * @macrosection
+ *
+ * QUIC protocol version macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1.
+ */
+#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_V2` is the QUIC version 2.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_PROTO_VER_V2 ((uint32_t)0x6b3343cfu)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_DRAFT_MAX` is the maximum QUIC draft
+ * version that this library supports.
+ */
+#define NGTCP2_PROTO_VER_DRAFT_MAX 0xff000020u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_DRAFT_MIN` is the minimum QUIC draft
+ * version that this library supports.
+ */
+#define NGTCP2_PROTO_VER_DRAFT_MIN 0xff00001du
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_MAX` is the highest QUIC version that this
+ * library supports.
+ */
+#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER_V1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_MIN` is the lowest QUIC version that this
+ * library supports.
+ */
+#define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved
+ * version.
+ */
+#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au
+
+/**
+ * @macrosection
+ *
+ * UDP datagram related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP
+ * datagram payload size that this endpoint transmits.
+ */
+#define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP
+ * datagram payload size that Path MTU Discovery can discover.
+ */
+#define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452
+
+/**
+ * @macrosection
+ *
+ * QUIC specific macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_VARINT` is the maximum value which can be
+ * encoded in variable-length integer encoding.
+ */
+#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` is the length of Stateless
+ * Reset Token.
+ */
+#define NGTCP2_STATELESS_RESET_TOKENLEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length
+ * of random bytes (Unpredictable Bits) in Stateless Reset packet
+ */
+#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` is the length of
+ * PATH_CHALLENGE data.
+ */
+#define NGTCP2_PATH_CHALLENGE_DATALEN 8
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_DRAFT` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC draft versions.
+ */
+#define NGTCP2_RETRY_KEY_DRAFT \
+ "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_DRAFT` is nonce used when generating
+ * integrity tag of Retry packet. It is used for QUIC draft versions.
+ */
+#define NGTCP2_RETRY_NONCE_DRAFT \
+ "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_V1` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC v1.
+ */
+#define NGTCP2_RETRY_KEY_V1 \
+ "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_V1` is nonce used when generating integrity
+ * tag of Retry packet. It is used for QUIC v1.
+ */
+#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_V2` is an encryption key to create
+ * integrity tag of Retry packet. It is used for QUIC v2.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_KEY_V2 \
+ "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_V2` is nonce used when generating
+ * integrity tag of Retry packet. It is used for QUIC v2.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_HP_MASKLEN` is the length of header protection mask.
+ */
+#define NGTCP2_HP_MASKLEN 5
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_HP_SAMPLELEN` is the number bytes sampled when
+ * encrypting a packet header.
+ */
+#define NGTCP2_HP_SAMPLELEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` is a default initial RTT.
+ */
+#define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_CIDLEN` is the maximum length of Connection ID.
+ */
+#define NGTCP2_MAX_CIDLEN 20
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_CIDLEN` is the minimum length of Connection ID.
+ */
+#define NGTCP2_MIN_CIDLEN 1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN` is the minimum length of
+ * Destination Connection ID in Client Initial packet if it does not
+ * bear token from Retry packet.
+ */
+#define NGTCP2_MIN_INITIAL_DCIDLEN 8
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT` is the default handshake
+ * timeout.
+ */
+#define NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS)
+
+/**
+ * @macrosection
+ *
+ * ECN related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_NOT_ECT` indicates no ECN marking.
+ */
+#define NGTCP2_ECN_NOT_ECT 0x0
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_ECT_1` is ECT(1) codepoint.
+ */
+#define NGTCP2_ECN_ECT_1 0x1
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_ECT_0` is ECT(0) codepoint.
+ */
+#define NGTCP2_ECN_ECT_0 0x2
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_CE` is CE codepoint.
+ */
+#define NGTCP2_ECN_CE 0x3
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ECN_MASK` is a bit mask to get ECN marking.
+ */
+#define NGTCP2_ECN_MASK 0x3
+
+#define NGTCP2_PKT_INFO_VERSION_V1 1
+#define NGTCP2_PKT_INFO_VERSION NGTCP2_PKT_INFO_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_info` is a packet metadata.
+ */
+typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
+ /**
+ * :member:`ecn` is ECN marking and when passing
+ * `ngtcp2_conn_read_pkt()`, and it should be either
+ * :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`,
+ * :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`.
+ */
+ uint32_t ecn;
+} ngtcp2_pkt_info;
+
+/**
+ * @macrosection
+ *
+ * ngtcp2 library error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` indicates that a passed
+ * argument is invalid.
+ */
+#define NGTCP2_ERR_INVALID_ARGUMENT -201
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_NOBUF` indicates that a provided buffer does not
+ * have enough space to store data.
+ */
+#define NGTCP2_ERR_NOBUF -203
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_PROTO` indicates a general protocol error.
+ */
+#define NGTCP2_ERR_PROTO -205
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE` indicates that a requested
+ * operation is not allowed at the current connection state.
+ */
+#define NGTCP2_ERR_INVALID_STATE -206
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_ACK_FRAME` indicates that an invalid ACK frame
+ * is received.
+ */
+#define NGTCP2_ERR_ACK_FRAME -207
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` indicates that there is no
+ * spare stream ID available.
+ */
+#define NGTCP2_ERR_STREAM_ID_BLOCKED -208
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_IN_USE` indicates that a stream ID is
+ * already in use.
+ */
+#define NGTCP2_ERR_STREAM_IN_USE -209
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` indicates that stream data
+ * cannot be sent because of flow control.
+ */
+#define NGTCP2_ERR_STREAM_DATA_BLOCKED -210
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FLOW_CONTROL` indicates flow control error.
+ */
+#define NGTCP2_ERR_FLOW_CONTROL -211
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CONNECTION_ID_LIMIT` indicates that the number
+ * of received Connection ID exceeds acceptable limit.
+ */
+#define NGTCP2_ERR_CONNECTION_ID_LIMIT -212
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_LIMIT` indicates that a remote endpoint
+ * opens more streams that is permitted.
+ */
+#define NGTCP2_ERR_STREAM_LIMIT -213
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FINAL_SIZE` indicates that inconsistent final
+ * size of a stream.
+ */
+#define NGTCP2_ERR_FINAL_SIZE -214
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CRYPTO` indicates crypto (TLS) related error.
+ */
+#define NGTCP2_ERR_CRYPTO -215
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` indicates that packet number
+ * is exhausted.
+ */
+#define NGTCP2_ERR_PKT_NUM_EXHAUSTED -216
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` indicates that a
+ * required transport parameter is missing.
+ */
+#define NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM -217
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` indicates that a
+ * transport parameter is malformed.
+ */
+#define NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM -218
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FRAME_ENCODING` indicates there is an error in
+ * frame encoding.
+ */
+#define NGTCP2_ERR_FRAME_ENCODING -219
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DECRYPT` indicates a decryption failure.
+ */
+#define NGTCP2_ERR_DECRYPT -220
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` indicates no more data can be
+ * sent to a stream.
+ */
+#define NGTCP2_ERR_STREAM_SHUT_WR -221
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` indicates that a stream was not
+ * found.
+ */
+#define NGTCP2_ERR_STREAM_NOT_FOUND -222
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_STREAM_STATE` indicates that a requested
+ * operation is not allowed at the current stream state.
+ */
+#define NGTCP2_ERR_STREAM_STATE -226
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION` indicates that Version
+ * Negotiation packet was received.
+ */
+#define NGTCP2_ERR_RECV_VERSION_NEGOTIATION -229
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CLOSING` indicates that connection is in closing
+ * state.
+ */
+#define NGTCP2_ERR_CLOSING -230
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DRAINING` indicates that connection is in
+ * draining state.
+ */
+#define NGTCP2_ERR_DRAINING -231
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` indicates a general transport
+ * parameter error.
+ */
+#define NGTCP2_ERR_TRANSPORT_PARAM -234
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded.
+ */
+#define NGTCP2_ERR_DISCARD_PKT -235
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` indicates that there is no
+ * spare Connection ID available.
+ */
+#define NGTCP2_ERR_CONN_ID_BLOCKED -237
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_INTERNAL` indicates an internal error.
+ */
+#define NGTCP2_ERR_INTERNAL -238
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED` indicates that a crypto
+ * buffer exceeded.
+ */
+#define NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED -239
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_WRITE_MORE` indicates
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used and a function call
+ * succeeded.
+ */
+#define NGTCP2_ERR_WRITE_MORE -240
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_RETRY` indicates that server should send Retry
+ * packet.
+ */
+#define NGTCP2_ERR_RETRY -241
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_DROP_CONN` indicates that an endpoint should
+ * drop connection immediately.
+ */
+#define NGTCP2_ERR_DROP_CONN -242
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_AEAD_LIMIT_REACHED` indicates AEAD encryption
+ * limit is reached and key update is not available. An endpoint
+ * should drop connection immediately.
+ */
+#define NGTCP2_ERR_AEAD_LIMIT_REACHED -243
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation
+ * could not probe that a path is capable of sending UDP datagram
+ * payload of size at least 1200 bytes.
+ */
+#define NGTCP2_ERR_NO_VIABLE_PATH -244
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION` indicates that server
+ * should send Version Negotiation packet.
+ */
+#define NGTCP2_ERR_VERSION_NEGOTIATION -245
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` indicates that QUIC
+ * connection is not established before the specified deadline.
+ */
+#define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the
+ * version negotiation failed.
+ */
+#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be
+ * closed silently because of idle timeout.
+ */
+#define NGTCP2_ERR_IDLE_CLOSE -248
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_FATAL` indicates that error codes less than this
+ * value is fatal error. When this error is returned, an endpoint
+ * should drop connection immediately.
+ */
+#define NGTCP2_ERR_FATAL -500
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_NOMEM` indicates out of memory.
+ */
+#define NGTCP2_ERR_NOMEM -501
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` indicates that user defined
+ * callback function failed.
+ */
+#define NGTCP2_ERR_CALLBACK_FAILURE -502
+
+/**
+ * @macrosection
+ *
+ * QUIC packet header flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_PKT_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet
+ * header.
+ */
+#define NGTCP2_PKT_FLAG_LONG_FORM 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR` indicates that Fixed Bit
+ * (aka QUIC bit) is not set.
+ */
+#define NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR 0x02u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set.
+ */
+#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04u
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC
+ * packet types.
+ */
+typedef enum ngtcp2_pkt_type {
+ /**
+ * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2
+ * for convenience.
+ */
+ NGTCP2_PKT_VERSION_NEGOTIATION = 0x80,
+ /**
+ * :enum:`NGTCP2_PKT_STATELESS_RESET` is defined by libngtcp2 for
+ * convenience.
+ */
+ NGTCP2_PKT_STATELESS_RESET = 0x81,
+ /**
+ * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet.
+ */
+ NGTCP2_PKT_INITIAL = 0x10,
+ /**
+ * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet.
+ */
+ NGTCP2_PKT_0RTT = 0x11,
+ /**
+ * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet.
+ */
+ NGTCP2_PKT_HANDSHAKE = 0x12,
+ /**
+ * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet.
+ */
+ NGTCP2_PKT_RETRY = 0x13,
+ /**
+ * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience.
+ */
+ NGTCP2_PKT_1RTT = 0x40
+} ngtcp2_pkt_type;
+
+/**
+ * @macrosection
+ *
+ * QUIC transport error code
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NO_ERROR` is QUIC transport error code ``NO_ERROR``.
+ */
+#define NGTCP2_NO_ERROR 0x0u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INTERNAL_ERROR` is QUIC transport error code
+ * ``INTERNAL_ERROR``.
+ */
+#define NGTCP2_INTERNAL_ERROR 0x1u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CONNECTION_REFUSED` is QUIC transport error code
+ * ``CONNECTION_REFUSED``.
+ */
+#define NGTCP2_CONNECTION_REFUSED 0x2u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FLOW_CONTROL_ERROR` is QUIC transport error code
+ * ``FLOW_CONTROL_ERROR``.
+ */
+#define NGTCP2_FLOW_CONTROL_ERROR 0x3u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_LIMIT_ERROR` is QUIC transport error code
+ * ``STREAM_LIMIT_ERROR``.
+ */
+#define NGTCP2_STREAM_LIMIT_ERROR 0x4u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_STATE_ERROR` is QUIC transport error code
+ * ``STREAM_STATE_ERROR``.
+ */
+#define NGTCP2_STREAM_STATE_ERROR 0x5u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FINAL_SIZE_ERROR` is QUIC transport error code
+ * ``FINAL_SIZE_ERROR``.
+ */
+#define NGTCP2_FINAL_SIZE_ERROR 0x6u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_FRAME_ENCODING_ERROR` is QUIC transport error code
+ * ``FRAME_ENCODING_ERROR``.
+ */
+#define NGTCP2_FRAME_ENCODING_ERROR 0x7u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TRANSPORT_PARAMETER_ERROR` is QUIC transport error
+ * code ``TRANSPORT_PARAMETER_ERROR``.
+ */
+#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CONNECTION_ID_LIMIT_ERROR` is QUIC transport error
+ * code ``CONNECTION_ID_LIMIT_ERROR``.
+ */
+#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTOCOL_VIOLATION` is QUIC transport error code
+ * ``PROTOCOL_VIOLATION``.
+ */
+#define NGTCP2_PROTOCOL_VIOLATION 0xau
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INVALID_TOKEN` is QUIC transport error code
+ * ``INVALID_TOKEN``.
+ */
+#define NGTCP2_INVALID_TOKEN 0xbu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_APPLICATION_ERROR` is QUIC transport error code
+ * ``APPLICATION_ERROR``.
+ */
+#define NGTCP2_APPLICATION_ERROR 0xcu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_BUFFER_EXCEEDED` is QUIC transport error code
+ * ``CRYPTO_BUFFER_EXCEEDED``.
+ */
+#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_KEY_UPDATE_ERROR` is QUIC transport error code
+ * ``KEY_UPDATE_ERROR``.
+ */
+#define NGTCP2_KEY_UPDATE_ERROR 0xeu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_AEAD_LIMIT_REACHED` is QUIC transport error code
+ * ``AEAD_LIMIT_REACHED``.
+ */
+#define NGTCP2_AEAD_LIMIT_REACHED 0xfu
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_NO_VIABLE_PATH` is QUIC transport error code
+ * ``NO_VIABLE_PATH``.
+ */
+#define NGTCP2_NO_VIABLE_PATH 0x10u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_ERROR` is QUIC transport error code
+ * ``CRYPTO_ERROR``.
+ */
+#define NGTCP2_CRYPTO_ERROR 0x100u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR` is QUIC transport error
+ * code ``VERSION_NEGOTIATION_ERROR``.
+ *
+ * https://datatracker.ietf.org/doc/html/draft-ietf-quic-version-negotiation-14
+ */
+#define NGTCP2_VERSION_NEGOTIATION_ERROR 0x11
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_path_validation_result` defines path validation
+ * result code.
+ */
+typedef enum ngtcp2_path_validation_result {
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS` indicates
+ * successful validation.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_SUCCESS,
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates
+ * validation failure.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_FAILURE,
+ /**
+ * :enum:`NGTCP2_PATH_VALIDATION_RESULT_ABORTED` indicates that path
+ * validation was aborted.
+ */
+ NGTCP2_PATH_VALIDATION_RESULT_ABORTED
+} ngtcp2_path_validation_result;
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_tstamp` is a timestamp with nanosecond resolution.
+ * ``UINT64_MAX`` is an invalid value.
+ */
+typedef uint64_t ngtcp2_tstamp;
+
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_duration` is a period of time in nanosecond
+ * resolution. ``UINT64_MAX`` is an invalid value.
+ */
+typedef uint64_t ngtcp2_duration;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cid` holds a Connection ID.
+ */
+typedef struct ngtcp2_cid {
+ /**
+ * :member:`datalen` is the length of Connection ID.
+ */
+ size_t datalen;
+ /**
+ * :member:`data` is the buffer to store Connection ID.
+ */
+ uint8_t data[NGTCP2_MAX_CIDLEN];
+} ngtcp2_cid;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_vec` is struct iovec compatible structure to
+ * reference arbitrary array of bytes.
+ */
+typedef struct ngtcp2_vec {
+ /**
+ * :member:`base` points to the data.
+ */
+ uint8_t *base;
+ /**
+ * :member:`len` is the number of bytes which the buffer pointed by
+ * base contains.
+ */
+ size_t len;
+} ngtcp2_vec;
+
+/**
+ * @function
+ *
+ * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte
+ * string pointed by |data| and its length is |datalen|. |datalen|
+ * must be at most :macro:`NGTCP2_MAX_CIDLEN`.
+ */
+NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data,
+ size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_cid_eq` returns nonzero if |a| and |b| share the same
+ * Connection ID.
+ */
+NGTCP2_EXTERN int ngtcp2_cid_eq(const ngtcp2_cid *a, const ngtcp2_cid *b);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_hd` represents QUIC packet header.
+ */
+typedef struct ngtcp2_pkt_hd {
+ /**
+ * :member:`dcid` is Destination Connection ID.
+ */
+ ngtcp2_cid dcid;
+ /**
+ * :member:`scid` is Source Connection ID.
+ */
+ ngtcp2_cid scid;
+ /**
+ * :member:`pkt_num` is a packet number.
+ */
+ int64_t pkt_num;
+ /**
+ * :member:`token` contains token for Initial
+ * packet.
+ */
+ const uint8_t *token;
+ /**
+ * :member:`tokenlen` is the length of :member:`token`.
+ */
+ size_t tokenlen;
+ /**
+ * :member:`pkt_numlen` is the number of bytes spent to encode
+ * :member:`pkt_num`.
+ */
+ size_t pkt_numlen;
+ /**
+ * :member:`len` is the sum of :member:`pkt_numlen` and the length
+ * of QUIC packet payload.
+ */
+ size_t len;
+ /**
+ * :member:`version` is QUIC version.
+ */
+ uint32_t version;
+ /**
+ * :member:`type` is a type of QUIC packet. See
+ * :type:`ngtcp2_pkt_type`.
+ */
+ uint8_t type;
+ /**
+ * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_*
+ * <NGTCP2_PKT_FLAG_NONE>`.
+ */
+ uint8_t flags;
+} ngtcp2_pkt_hd;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_pkt_stateless_reset` represents Stateless Reset.
+ */
+typedef struct ngtcp2_pkt_stateless_reset {
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`rand` points a buffer which contains random bytes
+ * section.
+ */
+ const uint8_t *rand;
+ /**
+ * :member:`randlen` is the number of random bytes.
+ */
+ size_t randlen;
+} ngtcp2_pkt_stateless_reset;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_transport_params_type` defines TLS message type which
+ * carries transport parameters.
+ */
+typedef enum ngtcp2_transport_params_type {
+ /**
+ * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO` is Client Hello
+ * TLS message.
+ */
+ NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
+ /**
+ * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS` is
+ * Encrypted Extensions TLS message.
+ */
+ NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS
+} ngtcp2_transport_params_type;
+
+/**
+ * @macrosection
+ *
+ * QUIC transport parameters related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` is the default
+ * value of max_udp_payload_size transport parameter.
+ */
+#define NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE 65527
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` is a default value of
+ * scaling factor of ACK Delay field in ACK frame.
+ */
+#define NGTCP2_DEFAULT_ACK_DELAY_EXPONENT 3
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` is a default value of the
+ * maximum amount of time in nanoseconds by which endpoint delays
+ * sending acknowledgement.
+ */
+#define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` is the default
+ * value of active_connection_id_limit transport parameter value if
+ * omitted.
+ */
+#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1` is TLS
+ * extension type of quic_transport_parameters.
+ */
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1 0x39u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT` is TLS
+ * extension type of quic_transport_parameters used during draft
+ * development.
+ */
+#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_DRAFT 0xffa5u
+
+#ifdef NGTCP2_USE_GENERIC_SOCKADDR
+typedef struct ngtcp2_sockaddr {
+ uint16_t sa_family;
+ uint8_t sa_data[14];
+} ngtcp2_sockaddr;
+
+typedef struct ngtcp2_in_addr {
+ uint32_t s_addr;
+} ngtcp2_in_addr;
+
+typedef struct ngtcp2_sockaddr_in {
+ uint16_t sin_family;
+ uint16_t sin_port;
+ ngtcp2_in_addr sin_addr;
+ uint8_t sin_zero[8];
+} ngtcp2_sockaddr_in;
+
+# define NGTCP2_SS_MAXSIZE 128
+# define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t))
+# define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t))
+# define NGTCP2_SS_PAD2SIZE \
+ (NGTCP2_SS_MAXSIZE - \
+ (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE))
+
+typedef struct ngtcp2_sockaddr_storage {
+ uint16_t ss_family;
+ uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE];
+ uint64_t _ss_align;
+ uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE];
+} ngtcp2_sockaddr_storage;
+
+# undef NGTCP2_SS_PAD2SIZE
+# undef NGTCP2_SS_PAD1SIZE
+# undef NGTCP2_SS_ALIGNSIZE
+# undef NGTCP2_SS_MAXSIZE
+
+typedef uint32_t ngtcp2_socklen;
+#else
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr. If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * the generic struct sockaddr defined in ngtcp2.h.
+ */
+typedef struct sockaddr ngtcp2_sockaddr;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr_storage` is typedefed to struct
+ * sockaddr_storage. If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is
+ * defined, it is typedefed to the generic struct sockaddr_storage
+ * defined in ngtcp2.h.
+ */
+typedef struct sockaddr_storage ngtcp2_sockaddr_storage;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr_in` is typedefed to struct sockaddr_in. If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * the generic struct sockaddr_in defined in ngtcp2.h.
+ */
+typedef struct sockaddr_in ngtcp2_sockaddr_in;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_socklen` is typedefed to socklen_t. If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * uint32_t.
+ */
+typedef socklen_t ngtcp2_socklen;
+#endif
+
+#if defined(NGTCP2_USE_GENERIC_SOCKADDR) || \
+ defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR)
+typedef struct ngtcp2_in6_addr {
+ uint8_t in6_addr[16];
+} ngtcp2_in6_addr;
+
+typedef struct ngtcp2_sockaddr_in6 {
+ uint16_t sin6_family;
+ uint16_t sin6_port;
+ uint32_t sin6_flowinfo;
+ ngtcp2_in6_addr sin6_addr;
+ uint32_t sin6_scope_id;
+} ngtcp2_sockaddr_in6;
+#else
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr_in6` is typedefed to struct sockaddr_in6.
+ * If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed
+ * to the generic struct sockaddr_in6 defined in ngtcp2.h.
+ */
+typedef struct sockaddr_in6 ngtcp2_sockaddr_in6;
+#endif
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_sockaddr_union` conveniently includes all supported
+ * address types.
+ */
+typedef union ngtcp2_sockaddr_union {
+ ngtcp2_sockaddr sa;
+ ngtcp2_sockaddr_in in;
+ ngtcp2_sockaddr_in6 in6;
+} ngtcp2_sockaddr_union;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_preferred_addr` represents preferred address
+ * structure.
+ */
+typedef struct ngtcp2_preferred_addr {
+ /**
+ * :member:`cid` is a Connection ID.
+ */
+ ngtcp2_cid cid;
+ /**
+ * :member:`ipv4` contains IPv4 address and port.
+ */
+ ngtcp2_sockaddr_in ipv4;
+ /**
+ * :member:`ipv6` contains IPv4 address and port.
+ */
+ ngtcp2_sockaddr_in6 ipv6;
+ /**
+ * :member:`ipv4_present` indicates that :member:`ipv4` contains
+ * IPv4 address and port.
+ */
+ uint8_t ipv4_present;
+ /**
+ * :member:`ipv6_present` indicates that :member:`ipv6` contains
+ * IPv6 address and port.
+ */
+ uint8_t ipv6_present;
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+} ngtcp2_preferred_addr;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_version_info` represents version_information
+ * structure.
+ */
+typedef struct ngtcp2_version_info {
+ /**
+ * :member:`chosen_version` is the version chosen by the sender.
+ */
+ uint32_t chosen_version;
+ /**
+ * :member:`available_versions` points the wire image of
+ * available_versions field. The each version is therefore in
+ * network byte order.
+ */
+ const uint8_t *available_versions;
+ /**
+ * :member:`available_versionslen` is the number of bytes pointed by
+ * :member:`available_versions`, not the number of versions
+ * included.
+ */
+ size_t available_versionslen;
+} ngtcp2_version_info;
+
+#define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1
+#define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_transport_params` represents QUIC transport
+ * parameters.
+ */
+typedef struct ngtcp2_transport_params {
+ /**
+ * :member:`preferred_address` contains preferred address if
+ * :member:`preferred_address_present` is nonzero.
+ */
+ ngtcp2_preferred_addr preferred_address;
+ /**
+ * :member:`original_dcid` is the Destination Connection ID field
+ * from the first Initial packet from client. Server must specify
+ * this field. It is expected that application knows the original
+ * Destination Connection ID even if it sends Retry packet, for
+ * example, by including it in retry token. Otherwise, application
+ * should not specify this field.
+ */
+ ngtcp2_cid original_dcid;
+ /**
+ * :member:`initial_scid` is the Source Connection ID field from the
+ * first Initial packet the endpoint sends. Application should not
+ * specify this field.
+ */
+ ngtcp2_cid initial_scid;
+ /**
+ * :member:`retry_scid` is the Source Connection ID field from Retry
+ * packet. Only server uses this field. If server application
+ * received Initial packet with retry token from client and server
+ * verified its token, server application must set Destination
+ * Connection ID field from the Initial packet to this field and set
+ * :member:`retry_scid_present` to nonzero. Server application must
+ * verify that the Destination Connection ID from Initial packet was
+ * sent in Retry packet by, for example, including the Connection ID
+ * in a token, or including it in AAD when encrypting a token.
+ */
+ ngtcp2_cid retry_scid;
+ /**
+ * :member:`initial_max_stream_data_bidi_local` is the size of flow
+ * control window of locally initiated stream. This is the number
+ * of bytes that the remote endpoint can send and the local endpoint
+ * must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_bidi_local;
+ /**
+ * :member:`initial_max_stream_data_bidi_remote` is the size of flow
+ * control window of remotely initiated stream. This is the number
+ * of bytes that the remote endpoint can send and the local endpoint
+ * must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_bidi_remote;
+ /**
+ * :member:`initial_max_stream_data_uni` is the size of flow control
+ * window of remotely initiated unidirectional stream. This is the
+ * number of bytes that the remote endpoint can send and the local
+ * endpoint must ensure that it has enough buffer to receive them.
+ */
+ uint64_t initial_max_stream_data_uni;
+ /**
+ * :member:`initial_max_data` is the connection level flow control
+ * window.
+ */
+ uint64_t initial_max_data;
+ /**
+ * :member:`initial_max_streams_bidi` is the number of concurrent
+ * streams that the remote endpoint can create.
+ */
+ uint64_t initial_max_streams_bidi;
+ /**
+ * :member:`initial_max_streams_uni` is the number of concurrent
+ * unidirectional streams that the remote endpoint can create.
+ */
+ uint64_t initial_max_streams_uni;
+ /**
+ * :member:`max_idle_timeout` is a duration during which sender
+ * allows quiescent.
+ */
+ ngtcp2_duration max_idle_timeout;
+ /**
+ * :member:`max_udp_payload_size` is the maximum datagram size that
+ * the endpoint can receive.
+ */
+ uint64_t max_udp_payload_size;
+ /**
+ * :member:`active_connection_id_limit` is the maximum number of
+ * Connection ID that sender can store.
+ */
+ uint64_t active_connection_id_limit;
+ /**
+ * :member:`ack_delay_exponent` is the exponent used in ACK Delay
+ * field in ACK frame.
+ */
+ uint64_t ack_delay_exponent;
+ /**
+ * :member:`max_ack_delay` is the maximum acknowledgement delay by
+ * which the endpoint will delay sending acknowledgements.
+ */
+ ngtcp2_duration max_ack_delay;
+ /**
+ * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM
+ * frame that this endpoint willingly receives. Specifying 0
+ * disables DATAGRAM support. See
+ * https://datatracker.ietf.org/doc/html/rfc9221
+ */
+ uint64_t max_datagram_frame_size;
+ /**
+ * :member:`stateless_reset_token_present` is nonzero if
+ * :member:`stateless_reset_token` field is set.
+ */
+ uint8_t stateless_reset_token_present;
+ /**
+ * :member:`disable_active_migration` is nonzero if the endpoint
+ * does not support active connection migration.
+ */
+ uint8_t disable_active_migration;
+ /**
+ * :member:`retry_scid_present` is nonzero if :member:`retry_scid`
+ * field is set.
+ */
+ uint8_t retry_scid_present;
+ /**
+ * :member:`preferred_address_present` is nonzero if
+ * :member:`preferred_address` is set.
+ */
+ uint8_t preferred_address_present;
+ /**
+ * :member:`stateless_reset_token` contains stateless reset token.
+ */
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`grease_quic_bit` is nonzero if sender supports "Greasing
+ * the QUIC Bit" extension. See :rfc:`9287`. Note that the local
+ * endpoint always enables greasing QUIC bit regardless of this
+ * field value.
+ */
+ uint8_t grease_quic_bit;
+ /**
+ * :member:`version_info` contains version_information field if
+ * :member:`version_info_present` is nonzero. Application should
+ * not specify this field.
+ */
+ ngtcp2_version_info version_info;
+ /**
+ * :member:`version_info_present` is nonzero if
+ * :member:`version_info` is set. Application should not specify
+ * this field.
+ */
+ uint8_t version_info_present;
+} ngtcp2_transport_params;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_pktns_id` defines packet number space identifier.
+ */
+typedef enum ngtcp2_pktns_id {
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_INITIAL` is the Initial packet number
+ * space.
+ */
+ NGTCP2_PKTNS_ID_INITIAL,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_HANDSHAKE` is the Handshake packet number
+ * space.
+ */
+ NGTCP2_PKTNS_ID_HANDSHAKE,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_APPLICATION` is the Application data
+ * packet number space.
+ */
+ NGTCP2_PKTNS_ID_APPLICATION,
+ /**
+ * :enum:`NGTCP2_PKTNS_ID_MAX` is defined to get the number of
+ * packet number spaces.
+ */
+ NGTCP2_PKTNS_ID_MAX
+} ngtcp2_pktns_id;
+
+#define NGTCP2_CONN_STAT_VERSION_V1 1
+#define NGTCP2_CONN_STAT_VERSION NGTCP2_CONN_STAT_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_conn_stat` holds various connection statistics, and
+ * computed data for recovery and congestion controller.
+ */
+typedef struct ngtcp2_conn_stat {
+ /**
+ * :member:`latest_rtt` is the latest RTT sample which is not
+ * adjusted by acknowledgement delay.
+ */
+ ngtcp2_duration latest_rtt;
+ /**
+ * :member:`min_rtt` is the minimum RTT seen so far. It is not
+ * adjusted by acknowledgement delay.
+ */
+ ngtcp2_duration min_rtt;
+ /**
+ * :member:`smoothed_rtt` is the smoothed RTT.
+ */
+ ngtcp2_duration smoothed_rtt;
+ /**
+ * :member:`rttvar` is a mean deviation of observed RTT.
+ */
+ ngtcp2_duration rttvar;
+ /**
+ * :member:`initial_rtt` is the initial RTT which is used when no
+ * RTT sample is available.
+ */
+ ngtcp2_duration initial_rtt;
+ /**
+ * :member:`first_rtt_sample_ts` is the timestamp when the first RTT
+ * sample is obtained.
+ */
+ ngtcp2_tstamp first_rtt_sample_ts;
+ /**
+ * :member:`pto_count` is the count of successive PTO timer
+ * expiration.
+ */
+ size_t pto_count;
+ /**
+ * :member:`loss_detection_timer` is the deadline of the current
+ * loss detection timer.
+ */
+ ngtcp2_tstamp loss_detection_timer;
+ /**
+ * :member:`last_tx_pkt_ts` corresponds to
+ * time_of_last_ack_eliciting_packet in :rfc:`9002`.
+ */
+ ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX];
+ /**
+ * :member:`loss_time` corresponds to loss_time in :rfc:`9002`.
+ */
+ ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX];
+ /**
+ * :member:`cwnd` is the size of congestion window.
+ */
+ uint64_t cwnd;
+ /**
+ * :member:`ssthresh` is slow start threshold.
+ */
+ uint64_t ssthresh;
+ /**
+ * :member:`congestion_recovery_start_ts` is the timestamp when
+ * congestion recovery started.
+ */
+ ngtcp2_tstamp congestion_recovery_start_ts;
+ /**
+ * :member:`bytes_in_flight` is the number in bytes of all sent
+ * packets which have not been acknowledged.
+ */
+ uint64_t bytes_in_flight;
+ /**
+ * :member:`max_tx_udp_payload_size` is the maximum size of UDP
+ * datagram payload that this endpoint transmits. It is used by
+ * congestion controller to compute congestion window.
+ */
+ size_t max_tx_udp_payload_size;
+ /**
+ * :member:`delivery_rate_sec` is the current sending rate measured
+ * in byte per second.
+ */
+ uint64_t delivery_rate_sec;
+ /**
+ * :member:`pacing_rate` is the current packet sending rate computed
+ * by a congestion controller. 0 if a congestion controller does
+ * not set pacing rate. Even if this value is set to 0, the library
+ * paces packets.
+ */
+ double pacing_rate;
+ /**
+ * :member:`send_quantum` is the maximum size of a data aggregate
+ * scheduled and transmitted together.
+ */
+ size_t send_quantum;
+} ngtcp2_conn_stat;
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_cc_algo` defines congestion control algorithms.
+ */
+typedef enum ngtcp2_cc_algo {
+ /**
+ * :enum:`NGTCP2_CC_ALGO_RENO` represents Reno.
+ */
+ NGTCP2_CC_ALGO_RENO = 0x00,
+ /**
+ * :enum:`NGTCP2_CC_ALGO_CUBIC` represents Cubic.
+ */
+ NGTCP2_CC_ALGO_CUBIC = 0x01,
+ /**
+ * :enum:`NGTCP2_CC_ALGO_BBR` represents BBR.
+ */
+ NGTCP2_CC_ALGO_BBR = 0x02,
+ /**
+ * :enum:`NGTCP2_CC_ALGO_BBR2` represents BBR v2.
+ */
+ NGTCP2_CC_ALGO_BBR2 = 0x03
+} ngtcp2_cc_algo;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_printf` is a callback function for logging.
+ * |user_data| is the same object passed to `ngtcp2_conn_client_new`
+ * or `ngtcp2_conn_server_new`.
+ */
+typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...);
+
+/**
+ * @macrosection
+ *
+ * QLog related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_QLOG_WRITE_FLAG_NONE 0x00u
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the
+ * final call to :type:`ngtcp2_qlog_write` in the current connection.
+ */
+#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01u
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_rand_ctx` is a wrapper around native random number
+ * generator. It is opaque to the ngtcp2 library. This might be
+ * useful if application needs to specify random number generator per
+ * thread or per connection.
+ */
+typedef struct ngtcp2_rand_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying random
+ * number generator.
+ */
+ void *native_handle;
+} ngtcp2_rand_ctx;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_qlog_write` is a callback function which is called to
+ * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR
+ * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_*
+ * <NGTCP2_QLOG_WRITE_FLAG_NONE>`. If
+ * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0.
+ */
+typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags,
+ const void *data, size_t datalen);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_qlog_settings` is a set of settings for qlog.
+ */
+typedef struct ngtcp2_qlog_settings {
+ /**
+ * :member:`odcid` is Original Destination Connection ID sent by
+ * client. It is used as group_id and ODCID fields. Client ignores
+ * this field and uses dcid parameter passed to
+ * `ngtcp2_conn_client_new()`.
+ */
+ ngtcp2_cid odcid;
+ /**
+ * :member:`write` is a callback function to write qlog. Setting
+ * ``NULL`` disables qlog.
+ */
+ ngtcp2_qlog_write write;
+} ngtcp2_qlog_settings;
+
+#define NGTCP2_SETTINGS_VERSION_V1 1
+#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_settings` defines QUIC connection settings.
+ */
+typedef struct ngtcp2_settings {
+ /**
+ * :member:`qlog` is qlog settings.
+ */
+ ngtcp2_qlog_settings qlog;
+ /**
+ * :member:`cc_algo` specifies congestion control algorithm.
+ */
+ ngtcp2_cc_algo cc_algo;
+ /**
+ * :member:`initial_ts` is an initial timestamp given to the
+ * library.
+ */
+ ngtcp2_tstamp initial_ts;
+ /**
+ * :member:`initial_rtt` is an initial RTT.
+ */
+ ngtcp2_duration initial_rtt;
+ /**
+ * :member:`log_printf` is a function that the library uses to write
+ * logs. ``NULL`` means no logging output. It is nothing to do
+ * with qlog.
+ */
+ ngtcp2_printf log_printf;
+ /**
+ * :member:`max_tx_udp_payload_size` is the maximum size of UDP
+ * datagram payload that this endpoint transmits. It is used by
+ * congestion controller to compute congestion window.
+ */
+ size_t max_tx_udp_payload_size;
+ /**
+ * :member:`token` is a token from Retry packet or NEW_TOKEN frame.
+ *
+ * Server sets this field if it received the token in Client Initial
+ * packet and successfully validated.
+ *
+ * Client sets this field if it intends to send token in its Initial
+ * packet.
+ *
+ * `ngtcp2_conn_server_new` and `ngtcp2_conn_client_new` make a copy
+ * of token.
+ */
+ const uint8_t *token;
+ /**
+ * :member:`tokenlen` is the length of :member:`token`.
+ */
+ size_t tokenlen;
+ /**
+ * :member:`rand_ctx` is an optional random number generator to be
+ * passed to :type:`ngtcp2_rand` callback.
+ */
+ ngtcp2_rand_ctx rand_ctx;
+ /**
+ * :member:`max_window` is the maximum connection-level flow control
+ * window if connection-level window auto-tuning is enabled. The
+ * connection-level window auto tuning is enabled if nonzero value
+ * is specified in this field. The initial value of window size is
+ * :member:`ngtcp2_transport_params.initial_max_data`. The window
+ * size is scaled up to the value specified in this field.
+ */
+ uint64_t max_window;
+ /**
+ * :member:`max_stream_window` is the maximum stream-level flow
+ * control window if stream-level window auto-tuning is enabled.
+ * The stream-level window auto-tuning is enabled if nonzero value
+ * is specified in this field. The initial value of window size is
+ * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote`,
+ * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local`,
+ * or :member:`ngtcp2_transport_params.initial_max_stream_data_uni`,
+ * depending on the type of stream. The window size is scaled up to
+ * the value specified in this field.
+ */
+ uint64_t max_stream_window;
+ /**
+ * :member:`ack_thresh` is the minimum number of the received ACK
+ * eliciting packets that triggers the immediate acknowledgement.
+ */
+ size_t ack_thresh;
+ /**
+ * :member:`no_tx_udp_payload_size_shaping`, if set to nonzero,
+ * instructs the library not to limit the UDP payload size to
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by
+ * Path MTU Discovery) and instead use the mininum size among the
+ * given buffer size, :member:`max_tx_udp_payload_size`, and the
+ * received max_udp_payload QUIC transport parameter.
+ */
+ int no_tx_udp_payload_size_shaping;
+ /**
+ * :member:`handshake_timeout` is the period of time before giving
+ * up QUIC connection establishment. If QUIC handshake is not
+ * complete within this period, `ngtcp2_conn_handle_expiry` returns
+ * :macro:`NGTCP2_ERR_HANDSHAKE_TIMEOUT` error. The deadline is
+ * :member:`initial_ts` + :member:`handshake_timeout`. If this
+ * field is set to ``UINT64_MAX``, no handshake timeout is set.
+ */
+ ngtcp2_duration handshake_timeout;
+ /**
+ * :member:`preferred_versions` is the array of versions that are
+ * preferred by the local endpoint. All versions set in this array
+ * must be supported by the library, and compatible to QUIC v1. The
+ * reserved versions are not allowed. They are sorted in the order
+ * of preference.
+ *
+ * On compatible version negotiation, server will negotiate one of
+ * those versions contained in this array if there is some overlap
+ * between these versions and the versions offered by the client.
+ * If there is no overlap, but the client chosen version is
+ * supported by the library, the server chooses the client chosen
+ * version as the negotiated version. This version set corresponds
+ * to Offered Versions in QUIC Version Negotiation draft, and it
+ * should be included in Version Negotiation packet.
+ *
+ * Client uses this field and :member:`original_version` to prevent
+ * version downgrade attack if it reacted upon Version Negotiation
+ * packet. If this field is specified, client must include
+ * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless
+ * |client_chosen_version| is a reserved version.
+ */
+ const uint32_t *preferred_versions;
+ /**
+ * :member:`preferred_versionslen` is the number of versions that
+ * are contained in the array pointed by
+ * :member:`preferred_versions`.
+ */
+ size_t preferred_versionslen;
+ /**
+ * :member:`available_versions` is the array of versions that are
+ * going to be set in :member:`available_versions
+ * <ngtcp2_version_info.available_versions>` field of outgoing
+ * version_information QUIC transport parameter.
+ *
+ * For server, this corresponds to Fully-Deployed Versions in QUIC
+ * Version Negotiation draft. If this field is set not, it is set
+ * to :member:`preferred_versions` internally if
+ * :member:`preferred_versionslen` is not zero. If this field is
+ * not set, and :member:`preferred_versionslen` is zero, this field
+ * is set to :macro:`NGTCP2_PROTO_VER_V1` internally.
+ *
+ * Client must include |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new` in this array if this field is set and
+ * |client_chosen_version| is not a reserved version. If this field
+ * is not set, |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new` will be set in this field internally
+ * unless |client_chosen_version| is a reserved version.
+ */
+ const uint32_t *available_versions;
+ /**
+ * :member:`available_versionslen` is the number of versions that
+ * are contained in the array pointed by
+ * :member:`available_versions`.
+ */
+ size_t available_versionslen;
+ /**
+ * :member:`original_version` is the original version that client
+ * initially used to make a connection attempt. If it is set, and
+ * it differs from |client_chosen_version| passed to
+ * `ngtcp2_conn_client_new`, the library assumes that client reacted
+ * upon Version Negotiation packet. Server does not use this field.
+ */
+ uint32_t original_version;
+ /**
+ * :member:`no_pmtud`, if set to nonzero, disables Path MTU
+ * Discovery.
+ */
+ int no_pmtud;
+} ngtcp2_settings;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_addr` is the endpoint address.
+ */
+typedef struct ngtcp2_addr {
+ /**
+ * :member:`addr` points to the buffer which contains endpoint
+ * address. It must not be ``NULL``.
+ */
+ ngtcp2_sockaddr *addr;
+ /**
+ * :member:`addrlen` is the length of addr.
+ */
+ ngtcp2_socklen addrlen;
+} ngtcp2_addr;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_path` is the network endpoints where a packet is sent
+ * and received.
+ */
+typedef struct ngtcp2_path {
+ /**
+ * :member:`local` is the address of local endpoint.
+ */
+ ngtcp2_addr local;
+ /**
+ * :member:`remote` is the address of remote endpoint.
+ */
+ ngtcp2_addr remote;
+ /**
+ * :member:`user_data` is an arbitrary data and opaque to the
+ * library.
+ *
+ * Note that :type:`ngtcp2_path` is generally passed to
+ * :type:`ngtcp2_conn` by an application, and :type:`ngtcp2_conn`
+ * stores their copies. Unfortunately, there is no way for the
+ * application to know when :type:`ngtcp2_conn` finishes using a
+ * specific :type:`ngtcp2_path` object in mid connection, which
+ * means that the application cannot free the data pointed by this
+ * field. Therefore, it is advised to use this field only when the
+ * data pointed by this field persists in an entire lifetime of the
+ * connection.
+ */
+ void *user_data;
+} ngtcp2_path;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_path_storage` is a convenient struct to have buffers
+ * to store the longest addresses.
+ */
+typedef struct ngtcp2_path_storage {
+ /**
+ * :member:`path` stores network path.
+ */
+ ngtcp2_path path;
+ /**
+ * :member:`local_addrbuf` is a buffer to store local address.
+ */
+ ngtcp2_sockaddr_union local_addrbuf;
+ /**
+ * :member:`remote_addrbuf` is a buffer to store remote address.
+ */
+ ngtcp2_sockaddr_union remote_addrbuf;
+} ngtcp2_path_storage;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_md` is a wrapper around native message digest
+ * object.
+ */
+typedef struct ngtcp2_crypto_md {
+ /**
+ * :member:`native_handle` is a pointer to an underlying message
+ * digest object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_md;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_aead` is a wrapper around native AEAD object.
+ */
+typedef struct ngtcp2_crypto_aead {
+ /**
+ * :member:`native_handle` is a pointer to an underlying AEAD
+ * object.
+ */
+ void *native_handle;
+ /**
+ * :member:`max_overhead` is the number of additional bytes which
+ * AEAD encryption needs on encryption.
+ */
+ size_t max_overhead;
+} ngtcp2_crypto_aead;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_cipher` is a wrapper around native cipher
+ * object.
+ */
+typedef struct ngtcp2_crypto_cipher {
+ /**
+ * :member:`native_handle` is a pointer to an underlying cipher
+ * object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_cipher;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_aead_ctx` is a wrapper around native AEAD
+ * cipher context object. It should be initialized with a specific
+ * key. ngtcp2 library reuses this context object to encrypt or
+ * decrypt multiple packets.
+ */
+typedef struct ngtcp2_crypto_aead_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying AEAD
+ * context object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_aead_ctx;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_cipher_ctx` is a wrapper around native cipher
+ * context object. It should be initialized with a specific key.
+ * ngtcp2 library reuses this context object to encrypt or decrypt
+ * multiple packet headers.
+ */
+typedef struct ngtcp2_crypto_cipher_ctx {
+ /**
+ * :member:`native_handle` is a pointer to an underlying cipher
+ * context object.
+ */
+ void *native_handle;
+} ngtcp2_crypto_cipher_ctx;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all
+ * crypto related objects in one place. Use
+ * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial
+ * packet encryption. For Handshake and 1RTT packets, use
+ * `ngtcp2_crypto_ctx_tls`.
+ */
+typedef struct ngtcp2_crypto_ctx {
+ /**
+ * :member:`aead` is AEAD object.
+ */
+ ngtcp2_crypto_aead aead;
+ /**
+ * :member:`md` is message digest object.
+ */
+ ngtcp2_crypto_md md;
+ /**
+ * :member:`hp` is header protection cipher.
+ */
+ ngtcp2_crypto_cipher hp;
+ /**
+ * :member:`max_encryption` is the number of encryption which this
+ * key can be used with.
+ */
+ uint64_t max_encryption;
+ /**
+ * :member:`max_decryption_failure` is the number of decryption
+ * failure with this key.
+ */
+ uint64_t max_decryption_failure;
+} ngtcp2_crypto_ctx;
+
+/**
+ * @function
+ *
+ * `ngtcp2_encode_transport_params` encodes |params| in |dest| of
+ * length |destlen|.
+ *
+ * If |dest| is NULL, and |destlen| is zero, this function just
+ * returns the number of bytes required to store the encoded transport
+ * parameters.
+ *
+ * This function returns the number of written, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |exttype| is invalid.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
+ uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype,
+ int transport_params_version, const ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_decode_transport_params` decodes transport parameters in
+ * |data| of length |datalen|, and stores the result in the object
+ * pointed by |params|.
+ *
+ * If the optional parameters are missing, the default value is
+ * assigned.
+ *
+ * The following fields may point to somewhere inside the buffer
+ * pointed by |data| of length |datalen|:
+ *
+ * - :member:`ngtcp2_transport_params.version_info.available_versions
+ * <ngtcp2_version_info.available_versions>`
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * The required parameter is missing.
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * The input is malformed.
+ */
+NGTCP2_EXTERN int ngtcp2_decode_transport_params_versioned(
+ int transport_params_version, ngtcp2_transport_params *params,
+ ngtcp2_transport_params_type exttype, const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_decode_transport_params_new` decodes transport parameters
+ * in |data| of length |datalen|, and stores the result in the object
+ * allocated dynamically. The pointer to the allocated object is
+ * assigned to |*pparams|. Unlike `ngtcp2_decode_transport_params`,
+ * all direct and indirect fields are also allocated dynamically if
+ * needed.
+ *
+ * |mem| is a memory allocator to allocate memory. If |mem| is
+ * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()`
+ * is used.
+ *
+ * If the optional parameters are missing, the default value is
+ * assigned.
+ *
+ * `ngtcp2_transport_params_del` frees the memory allocated by this
+ * function.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * The required parameter is missing.
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * The input is malformed.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_decode_transport_params_new(
+ ngtcp2_transport_params **pparams, ngtcp2_transport_params_type exttype,
+ const uint8_t *data, size_t datalen, const ngtcp2_mem *mem);
+
+/**
+ * @function
+ *
+ * `ngtcp2_transport_params_del` frees the |params| which must be
+ * dynamically allocated by `ngtcp2_decode_transport_params_new`.
+ *
+ * |mem| is a memory allocator that allocated |params|. If |mem| is
+ * ``NULL``, the memory allocator returned by `ngtcp2_mem_default()`
+ * is used.
+ *
+ * If |params| is ``NULL``, this function does nothing.
+ */
+NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_version_cid` is a convenient struct to store the
+ * result of `ngtcp2_pkt_decode_version_cid`.
+ */
+typedef struct ngtcp2_version_cid {
+ /**
+ * :member:`version` stores QUIC version.
+ */
+ uint32_t version;
+ /**
+ * :member:`dcid` points to the Destination Connection ID.
+ */
+ const uint8_t *dcid;
+ /**
+ * :member:`dcidlen` is the length of the Destination Connection ID
+ * pointed by :member:`dcid`.
+ */
+ size_t dcidlen;
+ /**
+ * :member:`scid` points to the Source Connection ID.
+ */
+ const uint8_t *scid;
+ /**
+ * :member:`scidlen` is the length of the Source Connection ID
+ * pointed by :member:`scid`.
+ */
+ size_t scidlen;
+} ngtcp2_version_cid;
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_version_cid` extracts QUIC version, Destination
+ * Connection ID and Source Connection ID from the packet pointed by
+ * |data| of length |datalen|. This function can handle Connection ID
+ * up to 255 bytes unlike `ngtcp2_pkt_decode_hd_long` or
+ * `ngtcp2_pkt_decode_hd_short` which are only capable of handling
+ * Connection ID less than or equal to :macro:`NGTCP2_MAX_CIDLEN`.
+ * Longer Connection ID is only valid if the version is unsupported
+ * QUIC version.
+ *
+ * If the given packet is Long header packet, this function extracts
+ * the version from the packet and assigns it to
+ * :member:`dest->version <ngtcp2_version_cid.version>`. It also
+ * extracts the pointer to the Destination Connection ID and its
+ * length and assigns them to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>` and :member:`dest->dcidlen
+ * <ngtcp2_version_cid.dcidlen>` respectively. Similarly, it extracts
+ * the pointer to the Source Connection ID and its length and assigns
+ * them to :member:`dest->scid <ngtcp2_version_cid.scid>` and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` respectively.
+ *
+ * If the given packet is Short header packet, :member:`dest->version
+ * <ngtcp2_version_cid.version>` will be 0, :member:`dest->scid
+ * <ngtcp2_version_cid.scid>` will be ``NULL``, and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` will be 0.
+ * Because the Short header packet does not have the length of
+ * Destination Connection ID, the caller has to pass the length in
+ * |short_dcidlen|. This function extracts the pointer to the
+ * Destination Connection ID and assigns it to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>`. |short_dcidlen| is assigned to
+ * :member:`dest->dcidlen <ngtcp2_version_cid.dcidlen>`.
+ *
+ * If Version Negotiation is required, this function returns
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`. Unlike the other error
+ * cases, all fields of |dest| are assigned as described above.
+ *
+ * This function returns 0 if it succeeds. Otherwise, one of the
+ * following negative error code:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The function could not decode the packet header.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`
+ * Version Negotiation packet should be sent.
+ */
+NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest,
+ const uint8_t *data,
+ size_t datalen,
+ size_t short_dcidlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in
+ * |pkt| of length |pktlen|. This function only parses the input just
+ * before packet number field.
+ *
+ * This function does not verify that length field is correct. In
+ * other words, this function succeeds even if length > |pktlen|.
+ *
+ * This function can handle Connection ID up to
+ * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use
+ * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID.
+ *
+ * This function handles Version Negotiation specially. If version
+ * field is 0, |pkt| must contain Version Negotiation packet. Version
+ * Negotiation packet has random type in wire format. For
+ * convenience, this function sets
+ * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to
+ * :member:`dest->type <ngtcp2_pkt_hd.type>`, clears
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags
+ * <ngtcp2_pkt_hd.flags>`, and sets 0 to :member:`dest->len
+ * <ngtcp2_pkt_hd.len>`. Version Negotiation packet occupies a single
+ * packet.
+ *
+ * It stores the result in the object pointed by |dest|, and returns
+ * the number of bytes decoded to read the packet header if it
+ * succeeds, or one of the following error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * Packet is too short; or it is not a long header
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest,
+ const uint8_t *pkt,
+ size_t pktlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_decode_hd_short` decodes QUIC short header packet
+ * header in |pkt| of length |pktlen|. |dcidlen| is the length of
+ * DCID in packet header. Short header packet does not encode the
+ * length of connection ID, thus we need the input from the outside.
+ * This function only parses the input just before packet number
+ * field. This function can handle Connection ID up to
+ * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use
+ * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It
+ * stores the result in the object pointed by |dest|, and returns the
+ * number of bytes decoded to read the packet header if it succeeds,
+ * or one of the following error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * Packet is too short; or it is not a short header
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest,
+ const uint8_t *pkt,
+ size_t pktlen,
+ size_t dcidlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_stateless_reset` writes Stateless Reset packet in
+ * the buffer pointed by |dest| whose length is |destlen|.
+ * |stateless_reset_token| is a pointer to the Stateless Reset Token,
+ * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN`
+ * bytes long. |rand| specifies the random octets preceding Stateless
+ * Reset Token. The length of |rand| is specified by |randlen| which
+ * must be at least :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` bytes
+ * long.
+ *
+ * If |randlen| is too long to write them all in the buffer, |rand| is
+ * written to the buffer as much as possible, and is truncated.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |randlen| is strictly less than
+ * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN`.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset(
+ uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token,
+ const uint8_t *rand, size_t randlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_version_negotiation` writes Version Negotiation
+ * packet in the buffer pointed by |dest| whose length is |destlen|.
+ * |unused_random| should be generated randomly. |dcid| is the
+ * destination connection ID which appears in a packet as a source
+ * connection ID sent by client which caused version negotiation.
+ * Similarly, |scid| is the source connection ID which appears in a
+ * packet as a destination connection ID sent by client. |sv| is a
+ * list of supported versions, and |nsv| specifies the number of
+ * supported versions included in |sv|.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_version_negotiation(
+ uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid,
+ size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv,
+ size_t nsv);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_conn` represents a single QUIC connection.
+ */
+typedef struct ngtcp2_conn ngtcp2_conn;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_client_initial` is invoked when client application
+ * asks TLS stack to produce first TLS cryptographic handshake data.
+ *
+ * This implementation of this callback must get the first handshake
+ * data from TLS stack and pass it to ngtcp2 library using
+ * `ngtcp2_conn_submit_crypto_data` function. Make sure that before
+ * calling `ngtcp2_conn_submit_crypto_data` function, client
+ * application must create initial packet protection keys and IVs, and
+ * provide them to ngtcp2 library using
+ * `ngtcp2_conn_install_initial_key`.
+ *
+ * This callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_client_initial` is invoked when server receives
+ * Initial packet from client. An server application must implement
+ * this callback, and generate initial keys and IVs for both
+ * transmission and reception. Install them using
+ * `ngtcp2_conn_install_initial_key`. |dcid| is the destination
+ * connection ID which client generated randomly. It is used to
+ * derive initial packet protection keys.
+ *
+ * The callback function must return 0 if it succeeds. If an error
+ * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the
+ * library call return immediately.
+ */
+typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_crypto_level` is encryption level.
+ */
+typedef enum ngtcp2_crypto_level {
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_INITIAL` is Initial Keys encryption
+ * level.
+ */
+ NGTCP2_CRYPTO_LEVEL_INITIAL,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_HANDSHAKE` is Handshake Keys
+ * encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_HANDSHAKE,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_APPLICATION` is Application Data
+ * (1-RTT) Keys encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_APPLICATION,
+ /**
+ * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY` is Early Data (0-RTT) Keys
+ * encryption level.
+ */
+ NGTCP2_CRYPTO_LEVEL_EARLY
+} ngtcp2_crypto_level;
+
+/**
+ * @functypedef
+ *
+ * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is
+ * received. The received data is pointed to by |data|, and its
+ * length is |datalen|. The |offset| specifies the offset where
+ * |data| is positioned. |user_data| is the arbitrary pointer passed
+ * to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. The
+ * ngtcp2 library ensures that the crypto data is passed to the
+ * application in the increasing order of |offset|. |datalen| is
+ * always strictly greater than 0. |crypto_level| indicates the
+ * encryption level where this data is received. Crypto data can
+ * never be received in
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`.
+ *
+ * The application should provide the given data to TLS stack.
+ *
+ * The callback function must return 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * - :macro:`NGTCP2_ERR_CRYPTO`
+ * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_PROTO`
+ * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * - :macro:`NGTCP2_ERR_NOMEM`
+ * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *
+ * If the other value is returned, it is treated as
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ *
+ * If application encounters fatal error, return
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_handshake_completed` is invoked when QUIC
+ * cryptographic handshake has completed.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC
+ * cryptographic handshake is confirmed. The handshake confirmation
+ * means that both endpoints agree that handshake has finished.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_version_negotiation` is invoked when Version
+ * Negotiation packet is received. |hd| is the pointer to the QUIC
+ * packet header object. The vector |sv| of |nsv| elements contains
+ * the QUIC version the server supports. Since Version Negotiation is
+ * only sent by server, this callback function is used by client only.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ const uint32_t *sv, size_t nsv,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received.
+ * This callback is client use only.
+ *
+ * Application must regenerate packet protection key, IV, and header
+ * protection key for Initial packets using the destination connection
+ * ID obtained by :member:`hd->scid <ngtcp2_pkt_hd.scid>` and install
+ * them by calling `ngtcp2_conn_install_initial_key()`.
+ *
+ * 0-RTT data accepted by the ngtcp2 library will be automatically
+ * retransmitted as 0-RTT data by the library.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the
+ * application to encrypt packet payload. The packet payload to
+ * encrypt is passed as |plaintext| of length |plaintextlen|. The
+ * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context
+ * object which is initialized with encryption key. The nonce is
+ * passed as |nonce| of length |noncelen|. The Additional
+ * Authenticated Data is passed as |aad| of length |aadlen|.
+ *
+ * The implementation of this callback must encrypt |plaintext| using
+ * the negotiated cipher suite and write the ciphertext into the
+ * buffer pointed by |dest|. |dest| has enough capacity to store the
+ * ciphertext and any additional AEAD tag data.
+ *
+ * |dest| and |plaintext| may point to the same buffer.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the
+ * application to decrypt packet payload. The packet payload to
+ * decrypt is passed as |ciphertext| of length |ciphertextlen|. The
+ * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context
+ * object which is initialized with decryption key. The nonce is
+ * passed as |nonce| of length |noncelen|. The Additional
+ * Authenticated Data is passed as |aad| of length |aadlen|.
+ *
+ * The implementation of this callback must decrypt |ciphertext| using
+ * the negotiated cipher suite and write the ciphertext into the
+ * buffer pointed by |dest|. |dest| has enough capacity to store the
+ * cleartext.
+ *
+ * |dest| and |ciphertext| may point to the same buffer.
+ *
+ * The callback function must return 0 if it succeeds. If TLS stack
+ * fails to decrypt data, return :macro:`NGTCP2_ERR_DECRYPT`. For any
+ * other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which
+ * makes the library call return immediately.
+ */
+typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the
+ * application to produce a mask to encrypt or decrypt packet header.
+ * The encryption cipher is |hp|. |hp_ctx| is the cipher context
+ * object which is initialized with header protection key. The sample
+ * is passed as |sample| which is :macro:`NGTCP2_HP_SAMPLELEN` bytes
+ * long.
+ *
+ * The implementation of this callback must produce a mask using the
+ * header protection cipher suite specified by QUIC specification and
+ * write the result into the buffer pointed by |dest|. The length of
+ * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`. The library
+ * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the
+ * produced mask. The buffer pointed by |dest| is guaranteed to have
+ * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for
+ * convenience.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
+ */
+typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @macrosection
+ *
+ * Stream data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of
+ * data is final piece of an incoming stream.
+ */
+#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` indicates that this chunk of
+ * data contains data received in 0RTT packet and the handshake has
+ * not completed yet, which means that the data might be replayed.
+ */
+#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02u
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_stream_data` is invoked when stream data is
+ * received. The stream is specified by |stream_id|. |flags| is the
+ * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_*
+ * <NGTCP2_STREAM_DATA_FLAG_NONE>`. If |flags| &
+ * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of
+ * the data is the last data in this stream. |offset| is the offset
+ * where this data begins. The library ensures that data is passed to
+ * the application in the non-decreasing order of |offset| without any
+ * overlap. The data is passed as |data| of length |datalen|.
+ * |datalen| may be 0 if and only if |fin| is nonzero.
+ *
+ * If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it
+ * indicates that a part of or whole data was received in 0RTT packet
+ * and a handshake has not completed yet.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_open` is a callback function which is called
+ * when remote stream is opened by peer. This function is not called
+ * if stream is opened by implicitly (we might reconsider this
+ * behaviour).
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ */
+typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id,
+ void *user_data);
+
+/**
+ * @macrosection
+ *
+ * Stream close flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_STREAM_CLOSE_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` indicates that
+ * app_error_code parameter is set.
+ */
+#define NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET 0x01u
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_close` is invoked when a stream is closed.
+ * This callback is not called when QUIC connection is closed before
+ * existing streams are closed. |flags| is the bitwise-OR of zero or
+ * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_*
+ * <NGTCP2_STREAM_CLOSE_FLAG_NONE>`. |app_error_code| indicates the
+ * error code of this closure if
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in
+ * |flags|. If it is not set, the stream was closed without any error
+ * code, which generally means success.
+ *
+ * |app_error_code| is the first application error code sent by a
+ * local endpoint, or received from a remote endpoint. If a stream is
+ * closed cleanly, no application error code is exchanged. Since QUIC
+ * stack does not know the application error code which indicates "no
+ * errors", |app_error_code| is set to 0 and
+ * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is not set in
+ * |flags| in this case.
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ */
+typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t app_error_code,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_reset` is invoked when a stream identified by
+ * |stream_id| is reset by a remote endpoint.
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ */
+typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_acked_stream_data_offset` is a callback function
+ * which is called when stream data is acked, and application can free
+ * the data. The acked range of data is [offset, offset + datalen).
+ * For a given stream_id, this callback is called sequentially in
+ * increasing order of |offset| without any overlap. |datalen| is
+ * normally strictly greater than 0. One exception is that when a
+ * packet which includes STREAM frame which has fin flag set, and 0
+ * length data, this callback is invoked with 0 passed as |datalen|.
+ *
+ * If a stream is closed prematurely and stream data is still
+ * in-flight, this callback function is not called for those data.
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ */
+typedef int (*ngtcp2_acked_stream_data_offset)(
+ ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen,
+ void *user_data, void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_stateless_reset` is a callback function which is
+ * called when Stateless Reset packet is received. The stateless
+ * reset details are given in |sr|.
+ *
+ * The implementation of this callback should return 0 if it succeeds.
+ * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library
+ * call return immediately.
+ */
+typedef int (*ngtcp2_recv_stateless_reset)(ngtcp2_conn *conn,
+ const ngtcp2_pkt_stateless_reset *sr,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_extend_max_streams` is a callback function which is
+ * called every time max stream ID is strictly extended.
+ * |max_streams| is the cumulative number of streams which an endpoint
+ * can open.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_extend_max_streams)(ngtcp2_conn *conn,
+ uint64_t max_streams, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_extend_max_stream_data` is a callback function which
+ * is invoked when max stream data is extended. |stream_id|
+ * identifies the stream. |max_data| is a cumulative number of bytes
+ * the endpoint can send on this stream.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_rand` is a callback function to get randomized byte
+ * string from application. Application must fill random |destlen|
+ * bytes to the buffer pointed by |dest|. The generated bytes are
+ * used only in non-cryptographic context.
+ */
+typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_new_connection_id` is a callback function to ask
+ * an application for new connection ID. Application must generate
+ * new unused connection ID with the exact |cidlen| bytes and store it
+ * in |cid|. It also has to generate stateless reset token into
+ * |token|. The length of stateless reset token is
+ * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that
+ * the buffer pointed by |cid| has the sufficient space to store the
+ * token.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_get_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_remove_connection_id` is a callback function which
+ * notifies the application that connection ID |cid| is no longer used
+ * by remote endpoint.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn,
+ const ngtcp2_cid *cid,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_update_key` is a callback function which tells the
+ * application that it must generate new packet protection keying
+ * materials and AEAD cipher context objects with new keys. The
+ * current set of secrets are given as |current_rx_secret| and
+ * |current_tx_secret| of length |secretlen|. They are decryption and
+ * encryption secrets respectively.
+ *
+ * The application has to generate new secrets and keys for both
+ * encryption and decryption, and write decryption secret and IV to
+ * the buffer pointed by |rx_secret| and |rx_iv| respectively. It
+ * also has to create new AEAD cipher context object with new
+ * decryption key and initialize |rx_aead_ctx| with it. Similarly,
+ * write encryption secret and IV to the buffer pointed by |tx_secret|
+ * and |tx_iv|. Create new AEAD cipher context object with new
+ * encryption key and initialize |tx_aead_ctx| with it. All given
+ * buffers have the enough capacity to store secret, key and IV.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_update_key)(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data);
+
+/**
+ * @macrosection
+ *
+ * Path validation related macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_PATH_VALIDATION_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the
+ * validation involving server preferred address. This flag is only
+ * set for client.
+ */
+#define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01u
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_path_validation` is a callback function which tells
+ * the application the outcome of path validation. |flags| is zero or
+ * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
+ * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that was
+ * validated. If |res| is
+ * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`,
+ * the path validation succeeded. If |res| is
+ * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`,
+ * the path validation failed.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags,
+ const ngtcp2_path *path,
+ ngtcp2_path_validation_result res,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_select_preferred_addr` is a callback function which
+ * asks a client application to choose server address from preferred
+ * addresses |paddr| received from server. An application should
+ * write a network path for a selected preferred address in |dest|.
+ * More specifically, the selected preferred address must be set to
+ * :member:`dest->remote <ngtcp2_path.remote>`, a client source
+ * address must be set to :member:`dest->local <ngtcp2_path.local>`.
+ * If a client source address does not change for the new server
+ * address, leave :member:`dest->local <ngtcp2_path.local>`
+ * unmodified, or copy the value of :member:`local
+ * <ngtcp2_path.local>` field of the current network path obtained
+ * from `ngtcp2_conn_get_path()`. Both :member:`dest->local.addr
+ * <ngtcp2_addr.addr>` and :member:`dest->remote.addr
+ * <ngtcp2_addr.addr>` point to buffers which are at least
+ * ``sizeof(struct sockaddr_storage)`` bytes long, respectively. If
+ * an application denies the preferred addresses, just leave |dest|
+ * unmodified (or set :member:`dest->remote.addrlen
+ * <ngtcp2_addr.addrlen>` to 0) and return 0.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn,
+ ngtcp2_path *dest,
+ const ngtcp2_preferred_addr *paddr,
+ void *user_data);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_connection_id_status_type` defines a set of status
+ * for Destination Connection ID.
+ */
+typedef enum ngtcp2_connection_id_status_type {
+ /**
+ * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE` indicates that
+ * a local endpoint starts using new destination Connection ID.
+ */
+ NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE,
+ /**
+ * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE` indicates
+ * that a local endpoint stops using a given destination Connection
+ * ID.
+ */
+ NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE
+} ngtcp2_connection_id_status_type;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_connection_id_status` is a callback function which is
+ * called when the status of Connection ID changes.
+ *
+ * |token| is the associated stateless reset token and it is ``NULL``
+ * if no token is present.
+ *
+ * |type| is the one of the value defined in
+ * :type:`ngtcp2_connection_id_status_type`. The new value might be
+ * added in the future release.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type,
+ uint64_t seq, const ngtcp2_cid *cid,
+ const uint8_t *token,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_new_token` is a callback function which is
+ * called when new token is received from server.
+ *
+ * |token| is the received token of length |tokenlen| bytes long.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const uint8_t *token,
+ size_t tokenlen, void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which
+ * must delete the native object pointed by
+ * :member:`aead_ctx->native_handle
+ * <ngtcp2_crypto_aead_ctx.native_handle>`.
+ */
+typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function
+ * which must delete the native object pointed by
+ * :member:`cipher_ctx->native_handle
+ * <ngtcp2_crypto_cipher_ctx.native_handle>`.
+ */
+typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
+
+/**
+ * @macrosection
+ *
+ * Datagram flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_DATAGRAM_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` indicates that DATAGRAM frame
+ * is received in 0RTT packet and the handshake has not completed yet,
+ * which means that the data might be replayed.
+ */
+#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01u
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is
+ * received. |flags| is bitwise-OR of zero or more of
+ * :macro:`NGTCP2_DATAGRAM_FLAG_* <NGTCP2_DATAGRAM_FLAG_NONE>`.
+ *
+ * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it
+ * indicates that DATAGRAM frame was received in 0RTT packet and a
+ * handshake has not completed yet.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags,
+ const uint8_t *data, size_t datalen,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_ack_datagram` is invoked when a packet which contains
+ * DATAGRAM frame which is identified by |dgram_id| is acknowledged.
+ * |dgram_id| is the valued passed to `ngtcp2_conn_writev_datagram`.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_lost_datagram` is invoked when a packet which
+ * contains DATAGRAM frame which is identified by |dgram_id| is
+ * declared lost. |dgram_id| is the valued passed to
+ * `ngtcp2_conn_writev_datagram`. Note that the loss might be
+ * spurious, and DATAGRAM frame might be acknowledged later.
+ *
+ * The callback function must return 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
+ * immediately.
+ */
+typedef int (*ngtcp2_lost_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_get_path_challenge_data` is a callback function to
+ * ask an application for new data that is sent in PATH_CHALLENGE
+ * frame. Application must generate new unpredictable exactly
+ * :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes of random data and
+ * store them into the buffer pointed by |data|.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_get_path_challenge_data)(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_stream_stop_sending` is invoked when a stream is no
+ * longer read by a local endpoint before it receives all stream data.
+ * This function is called at most once per stream. |app_error_code|
+ * is the error code passed to `ngtcp2_conn_shutdown_stream_read` or
+ * `ngtcp2_conn_shutdown_stream`.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *user_data,
+ void *stream_user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_version_negotiation` is invoked when the compatible
+ * version negotiation takes place. For client, it is called when it
+ * sees a change in version field of a long header packet. This
+ * callback function might be called multiple times for client. For
+ * server, it is called once when the version is negotiated.
+ *
+ * The implementation of this callback must install new Initial keys
+ * for |version|. Use `ngtcp2_conn_install_vneg_initial_key` to
+ * install keys.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_recv_key` is invoked when new key is installed to
+ * |conn| during QUIC cryptographic handshake.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_recv_key)(ngtcp2_conn *conn, ngtcp2_crypto_level level,
+ void *user_data);
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_early_data_rejected` is invoked when early data was
+ * rejected by server, or client decided not to attempt early data.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_early_data_rejected)(ngtcp2_conn *conn, void *user_data);
+
+#define NGTCP2_CALLBACKS_VERSION_V1 1
+#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_callbacks` holds a set of callback functions.
+ */
+typedef struct ngtcp2_callbacks {
+ /**
+ * :member:`client_initial` is a callback function which is invoked
+ * when client asks TLS stack to produce first TLS cryptographic
+ * handshake message. This callback function must be specified for
+ * a client application.
+ */
+ ngtcp2_client_initial client_initial;
+ /**
+ * :member:`recv_client_initial` is a callback function which is
+ * invoked when a server receives the first packet from client.
+ * This callback function must be specified for a server application.
+ */
+ ngtcp2_recv_client_initial recv_client_initial;
+ /**
+ * :member:`recv_crypto_data` is a callback function which is
+ * invoked when cryptographic data (CRYPTO frame, in other words,
+ * TLS message) is received. This callback function must be
+ * specified.
+ */
+ ngtcp2_recv_crypto_data recv_crypto_data;
+ /**
+ * :member:`handshake_completed` is a callback function which is
+ * invoked when QUIC cryptographic handshake has completed. This
+ * callback function is optional.
+ */
+ ngtcp2_handshake_completed handshake_completed;
+ /**
+ * :member:`recv_version_negotiation` is a callback function which
+ * is invoked when Version Negotiation packet is received by a
+ * client. This callback function is optional.
+ */
+ ngtcp2_recv_version_negotiation recv_version_negotiation;
+ /**
+ * :member:`encrypt` is a callback function which is invoked to
+ * encrypt a QUIC packet. This callback function must be specified.
+ */
+ ngtcp2_encrypt encrypt;
+ /**
+ * :member:`decrypt` is a callback function which is invoked to
+ * decrypt a QUIC packet. This callback function must be specified.
+ */
+ ngtcp2_decrypt decrypt;
+ /**
+ * :member:`hp_mask` is a callback function which is invoked to get
+ * a mask to encrypt or decrypt packet header. This callback
+ * function must be specified.
+ */
+ ngtcp2_hp_mask hp_mask;
+ /**
+ * :member:`recv_stream_data` is a callback function which is
+ * invoked when STREAM data, which includes application data, is
+ * received. This callback function is optional.
+ */
+ ngtcp2_recv_stream_data recv_stream_data;
+ /**
+ * :member:`acked_stream_data_offset` is a callback function which
+ * is invoked when STREAM data, which includes application data, is
+ * acknowledged by a remote endpoint. It tells an application the
+ * largest offset of acknowledged STREAM data without a gap so that
+ * application can free memory for the data. This callback function
+ * is optional.
+ */
+ ngtcp2_acked_stream_data_offset acked_stream_data_offset;
+ /**
+ * :member:`stream_open` is a callback function which is invoked
+ * when new remote stream is opened by a remote endpoint. This
+ * callback function is optional.
+ */
+ ngtcp2_stream_open stream_open;
+ /**
+ * :member:`stream_close` is a callback function which is invoked
+ * when a stream is closed. This callback function is optional.
+ */
+ ngtcp2_stream_close stream_close;
+ /**
+ * :member:`recv_stateless_reset` is a callback function which is
+ * invoked when Stateless Reset packet is received. This callback
+ * function is optional.
+ */
+ ngtcp2_recv_stateless_reset recv_stateless_reset;
+ /**
+ * :member:`recv_retry` is a callback function which is invoked when
+ * a client receives Retry packet. For client, this callback
+ * function must be specified. Server never receive Retry packet.
+ */
+ ngtcp2_recv_retry recv_retry;
+ /**
+ * :member:`extend_max_local_streams_bidi` is a callback function
+ * which is invoked when the number of bidirectional stream which a
+ * local endpoint can open is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_streams extend_max_local_streams_bidi;
+ /**
+ * :member:`extend_max_local_streams_uni` is a callback function
+ * which is invoked when the number of unidirectional stream which a
+ * local endpoint can open is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_streams extend_max_local_streams_uni;
+ /**
+ * :member:`rand` is a callback function which is invoked when the
+ * library needs sequence of random data. This callback function
+ * must be specified.
+ */
+ ngtcp2_rand rand;
+ /**
+ * :member:`get_new_connection_id` is a callback function which is
+ * invoked when the library needs new connection ID. This callback
+ * function must be specified.
+ */
+ ngtcp2_get_new_connection_id get_new_connection_id;
+ /**
+ * :member:`remove_connection_id` is a callback function which
+ * notifies an application that connection ID is no longer used by a
+ * remote endpoint. This callback function is optional.
+ */
+ ngtcp2_remove_connection_id remove_connection_id;
+ /**
+ * :member:`update_key` is a callback function which is invoked when
+ * the library tells an application that it must update keying
+ * materials and install new keys. This callback function must be
+ * specified.
+ */
+ ngtcp2_update_key update_key;
+ /**
+ * :member:`path_validation` is a callback function which is invoked
+ * when path validation completed. This callback function is
+ * optional.
+ */
+ ngtcp2_path_validation path_validation;
+ /**
+ * :member:`select_preferred_addr` is a callback function which is
+ * invoked when the library asks a client to select preferred
+ * address presented by a server. This callback function is
+ * optional.
+ */
+ ngtcp2_select_preferred_addr select_preferred_addr;
+ /**
+ * :member:`stream_reset` is a callback function which is invoked
+ * when a stream is reset by a remote endpoint. This callback
+ * function is optional.
+ */
+ ngtcp2_stream_reset stream_reset;
+ /**
+ * :member:`extend_max_remote_streams_bidi` is a callback function
+ * which is invoked when the number of bidirectional streams which a
+ * remote endpoint can open is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_streams extend_max_remote_streams_bidi;
+ /**
+ * :member:`extend_max_remote_streams_uni` is a callback function
+ * which is invoked when the number of unidirectional streams which
+ * a remote endpoint can open is increased. This callback function
+ * is optional.
+ */
+ ngtcp2_extend_max_streams extend_max_remote_streams_uni;
+ /**
+ * :member:`extend_max_stream_data` is callback function which is
+ * invoked when the maximum offset of STREAM data that a local
+ * endpoint can send is increased. This callback function is
+ * optional.
+ */
+ ngtcp2_extend_max_stream_data extend_max_stream_data;
+ /**
+ * :member:`dcid_status` is a callback function which is invoked
+ * when the new destination Connection ID is activated or the
+ * activated destination Connection ID is now deactivated. This
+ * callback function is optional.
+ */
+ ngtcp2_connection_id_status dcid_status;
+ /**
+ * :member:`handshake_confirmed` is a callback function which is
+ * invoked when both endpoints agree that handshake has finished.
+ * This field is ignored by server because handshake_completed
+ * indicates the handshake confirmation for server. This callback
+ * function is optional.
+ */
+ ngtcp2_handshake_confirmed handshake_confirmed;
+ /**
+ * :member:`recv_new_token` is a callback function which is invoked
+ * when new token is received from server. This field is ignored by
+ * server. This callback function is optional.
+ */
+ ngtcp2_recv_new_token recv_new_token;
+ /**
+ * :member:`delete_crypto_aead_ctx` is a callback function which
+ * deletes a given AEAD cipher context object. This callback
+ * function must be specified.
+ */
+ ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx;
+ /**
+ * :member:`delete_crypto_cipher_ctx` is a callback function which
+ * deletes a given cipher context object. This callback function
+ * must be specified.
+ */
+ ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx;
+ /**
+ * :member:`recv_datagram` is a callback function which is invoked
+ * when DATAGRAM frame is received. This callback function is
+ * optional.
+ */
+ ngtcp2_recv_datagram recv_datagram;
+ /**
+ * :member:`ack_datagram` is a callback function which is invoked
+ * when a packet containing DATAGRAM frame is acknowledged. This
+ * callback function is optional.
+ */
+ ngtcp2_ack_datagram ack_datagram;
+ /**
+ * :member:`lost_datagram` is a callback function which is invoked
+ * when a packet containing DATAGRAM frame is declared lost. This
+ * callback function is optional.
+ */
+ ngtcp2_lost_datagram lost_datagram;
+ /**
+ * :member:`get_path_challenge_data` is a callback function which is
+ * invoked when the library needs new PATH_CHALLENGE data. This
+ * callback must be specified.
+ */
+ ngtcp2_get_path_challenge_data get_path_challenge_data;
+ /**
+ * :member:`stream_stop_sending` is a callback function which is
+ * invoked when a local endpoint no longer reads from a stream
+ * before it receives all stream data. This callback function is
+ * optional.
+ */
+ ngtcp2_stream_stop_sending stream_stop_sending;
+ /**
+ * :member:`version_negotiation` is a callback function which is
+ * invoked when the compatible version negotiation takes place.
+ * This callback function must be specified.
+ */
+ ngtcp2_version_negotiation version_negotiation;
+ /**
+ * :member:`recv_rx_key` is a callback function which is invoked
+ * when a new key for decrypting packets is installed during QUIC
+ * cryptographic handshake. It is not called for
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`.
+ */
+ ngtcp2_recv_key recv_rx_key;
+ /**
+ * :member:`recv_tx_key` is a callback function which is invoked
+ * when a new key for encrypting packets is installed during QUIC
+ * cryptographic handshake. It is not called for
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_INITIAL`.
+ */
+ ngtcp2_recv_key recv_tx_key;
+ /**
+ * :member:`ngtcp2_early_data_rejected` is a callback function which
+ * is invoked when an attempt to send early data by client was
+ * rejected by server, or client decided not to attempt early data.
+ * This callback function is only used by client.
+ */
+ ngtcp2_early_data_rejected early_data_rejected;
+} ngtcp2_callbacks;
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_connection_close` writes Initial packet
+ * containing CONNECTION_CLOSE frame with the given |error_code| and
+ * the optional |reason| of length |reasonlen| to the buffer pointed
+ * by |dest| of length |destlen|. All encryption parameters are for
+ * Initial packet encryption. The packet number is always 0.
+ *
+ * The primary use case of this function is for server to send
+ * CONNECTION_CLOSE frame in Initial packet to close connection
+ * without committing the state when validating Retry token fails.
+ *
+ * This function returns the number of bytes written if it succeeds,
+ * or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * Callback function failed.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv,
+ ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed
+ * by |dest| whose length is |destlen|. |dcid| is the destination
+ * connection ID which appeared in a packet as a source connection ID
+ * sent by client. |scid| is a server chosen source connection ID.
+ * |odcid| specifies Original Destination Connection ID which appeared
+ * in a packet as a destination connection ID sent by client. |token|
+ * specifies Retry Token, and |tokenlen| specifies its length. |aead|
+ * must be AEAD_AES_128_GCM. |aead_ctx| must be initialized with
+ * :macro:`NGTCP2_RETRY_KEY` as an encryption key.
+ *
+ * This function returns the number of bytes written to the buffer, or
+ * one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * Callback function failed.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * :member:`odcid->datalen <ngtcp2_cid.datalen>` is less than
+ * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_accept` is used by server implementation, and decides
+ * whether packet |pkt| of length |pktlen| from client is acceptable
+ * for the very initial packet to a connection.
+ *
+ * If |dest| is not ``NULL`` and the function returns 0, or
+ * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to
+ * the object pointed by |dest|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_RETRY`
+ * Retry packet should be sent.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The packet is not acceptable for the very first packet to a new
+ * connection; or the function failed to parse the packet header.
+ */
+NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
+ size_t pktlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and
+ * initializes it as client. |dcid| is randomized destination
+ * connection ID. |scid| is source connection ID.
+ * |client_chosen_version| is a QUIC version that a client chooses.
+ * |path| is the network path where this QUIC connection is being
+ * established and must not be ``NULL``. |callbacks|, |settings|, and
+ * |params| must not be ``NULL``, and the function make a copy of each
+ * of them. |params| is local QUIC transport parameters and sent to a
+ * remote endpoint during handshake. |user_data| is the arbitrary
+ * pointer which is passed to the user-defined callback functions. If
+ * |mem| is ``NULL``, the memory allocator returned by
+ * `ngtcp2_mem_default()` is used.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and
+ * initializes it as server. |dcid| is a destination connection ID.
+ * |scid| is a source connection ID. |path| is the network path where
+ * this QUIC connection is being established and must not be ``NULL``.
+ * |client_chosen_version| is a QUIC version that a client chooses.
+ * |callbacks|, |settings|, and |params| must not be ``NULL``, and the
+ * function make a copy of each of them. |params| is local QUIC
+ * transport parameters and sent to a remote endpoint during
+ * handshake. |user_data| is the arbitrary pointer which is passed to
+ * the user-defined callback functions. If |mem| is ``NULL``, the
+ * memory allocator returned by `ngtcp2_mem_default()` is used.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned(
+ ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ const ngtcp2_path *path, uint32_t client_chosen_version,
+ int callbacks_version, const ngtcp2_callbacks *callbacks,
+ int settings_version, const ngtcp2_settings *settings,
+ int transport_params_version, const ngtcp2_transport_params *params,
+ const ngtcp2_mem *mem, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_del` frees resources allocated for |conn|. It also
+ * frees memory pointed by |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of
+ * length |pktlen| and processes it. |path| is the network path the
+ * packet is delivered and must not be ``NULL``. |pi| is packet
+ * metadata and may be ``NULL``. This function performs QUIC handshake
+ * as well.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * This function returns 0 if it succeeds, or negative error codes.
+ * If :macro:`NGTCP2_ERR_RETRY` is returned, application must be a
+ * server and it must perform address validation by sending Retry
+ * packet and discard the connection state. If
+ * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must
+ * drop the connection silently (without sending any CONNECTION_CLOSE
+ * frame) and discard connection state. If
+ * :macro:`NGTCP2_ERR_DRAINING` is returned, a connection has entered
+ * the draining state, and no further packet transmission is allowed.
+ * If :macro:`NGTCP2_ERR_CRYPTO` is returned, the error happened in
+ * TLS stack and `ngtcp2_conn_get_tls_alert` returns TLS alert if set.
+ *
+ * If any other negative errors are returned, call
+ * `ngtcp2_conn_write_connection_close` to get terminal packet, and
+ * sending it makes QUIC connection enter the closing state.
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
+ int pkt_info_version, const ngtcp2_pkt_info *pi,
+ const uint8_t *pkt, size_t pktlen,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_pkt` is equivalent to calling
+ * `ngtcp2_conn_writev_stream` with -1 as stream_id, no stream data, and
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack
+ * declares TLS handshake completion. This does not mean QUIC
+ * handshake has completed. The library needs extra conditions to be
+ * met.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake
+ * has completed.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_initial_key` installs packet protection keying
+ * materials for Initial packets. |rx_aead_ctx| is AEAD cipher
+ * context object and must be initialized with a decryption key.
+ * |rx_iv| is IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is
+ * a packet header protection cipher context object for decryption.
+ * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for
+ * encrypting outgoing packets and are the same length with the
+ * decryption counterpart . If they have already been set, they are
+ * overwritten.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|,
+ * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * After receiving Retry packet, the DCID most likely changes. In
+ * that case, client application must generate these keying materials
+ * again based on new DCID and install them again.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_initial_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx,
+ const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_vneg_initial_key` installs packet protection
+ * keying materials for Initial packets on compatible version
+ * negotiation for |version|. |rx_aead_ctx| is AEAD cipher context
+ * object and must be initialized with a decryption key. |rx_iv| is
+ * IV of length |rx_ivlen| for decryption. |rx_hp_ctx| is a packet
+ * header protection cipher context object for decryption. Similarly,
+ * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing
+ * packets and are the same length with the decryption counterpart .
+ * If they have already been set, they are overwritten.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|,
+ * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+ const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+ const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+ const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_rx_handshake_key` installs packet protection
+ * keying materials for decrypting incoming Handshake packets.
+ * |aead_ctx| is AEAD cipher context object which must be initialized
+ * with a decryption key. |iv| is IV of length |ivlen|. |hp_ctx| is
+ * a packet header protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx|,
+ * and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_tx_handshake_key` installs packet protection
+ * keying materials for encrypting outgoing Handshake packets.
+ * |aead_ctx| is AEAD cipher context object which must be initialized
+ * with an encryption key. |iv| is IV of length |ivlen|. |hp_ctx| is
+ * a packet header protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx| and
+ * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_early_key` installs packet protection AEAD
+ * cipher context object |aead_ctx|, IV |iv| of length |ivlen|, and
+ * packet header protection cipher context object |hp_ctx| to encrypt
+ * (for client) or decrypt (for server) 0RTT packets.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx| and
+ * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_early_key(
+ ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_rx_key` installs packet protection keying
+ * materials for decrypting Short header packets. |secret| of length
+ * |secretlen| is the decryption secret which is used to derive keying
+ * materials passed to this function. |aead_ctx| is AEAD cipher
+ * context object which must be initialized with a decryption key.
+ * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header
+ * protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx| and
+ * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_rx_key(
+ ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_tx_key` installs packet protection keying
+ * materials for encrypting Short header packets. |secret| of length
+ * |secretlen| is the encryption secret which is used to derive keying
+ * materials passed to this function. |aead_ctx| is AEAD cipher
+ * context object which must be initialized with an encryption key.
+ * |iv| is IV of length |ivlen|. |hp_ctx| is a packet header
+ * protection cipher context object.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx| and
+ * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used. If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_tx_key(
+ ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_initiate_key_update` initiates the key update.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The previous key update has not been confirmed yet; or key
+ * update is too frequent; or new keys are not available yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_tls_error` sets the TLS related error |liberr| in
+ * |conn|. |liberr| must be one of ngtcp2 library error codes (which
+ * is defined as NGTCP2_ERR_* macro, such as
+ * :macro:`NGTCP2_ERR_DECRYPT`). In general, error code should be
+ * propagated via return value, but sometimes ngtcp2 API is called
+ * inside callback function of TLS stack and it does not allow to
+ * return ngtcp2 error code directly. In this case, implementation
+ * can set the error code (e.g.,
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`) using this function.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_error` returns the value set by
+ * `ngtcp2_conn_set_tls_error`. If no value is set, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_tls_alert` sets a TLS alert |alert| generated by a
+ * local endpoint to |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_alert(ngtcp2_conn *conn, uint8_t alert);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_alert` returns the value set by
+ * `ngtcp2_conn_set_tls_alert`. If no value is set, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN uint8_t ngtcp2_conn_get_tls_alert(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_keep_alive_timeout` sets keep-alive timeout. If
+ * nonzero value is given, after a connection is idle at least in a
+ * given amount of time, a keep-alive packet is sent. If 0 is set,
+ * keep-alive functionality is disabled and this is the default.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
+ ngtcp2_duration timeout);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_expiry` returns the next expiry time. It returns
+ * ``UINT64_MAX`` if there is no next expiry.
+ *
+ * Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or
+ * `ngtcp2_conn_writev_stream`) if expiry time is passed.
+ */
+NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_handle_expiry` handles expired timer. It does nothing
+ * if timer is not expired.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO).
+ */
+NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_decode_remote_transport_params` decodes QUIC transport
+ * parameters from the buffer pointed by |data| of length |datalen|,
+ * and sets the result to |conn|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * The required parameter is missing.
+ * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * The input is malformed.
+ * :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * Failed to validate the remote QUIC transport parameters.
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * Version negotiation failure.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_decode_remote_transport_params(ngtcp2_conn *conn,
+ const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_remote_transport_params` returns a pointer to the
+ * remote QUIC transport parameters. If no remote transport
+ * parameters are set, it returns NULL.
+ */
+NGTCP2_EXTERN const ngtcp2_transport_params *
+ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as
+ * transport parameters previously received from a server. The
+ * parameters are used to send early data. QUIC requires that client
+ * application should remember transport parameters along with a
+ * session ticket.
+ *
+ * At least following fields should be set:
+ *
+ * - initial_max_stream_id_bidi
+ * - initial_max_stream_id_uni
+ * - initial_max_stream_data_bidi_local
+ * - initial_max_stream_data_bidi_remote
+ * - initial_max_stream_data_uni
+ * - initial_max_data
+ * - active_connection_id_limit
+ * - max_datagram_frame_size (if DATAGRAM extension was negotiated)
+ *
+ * The following fields are ignored:
+ *
+ * - ack_delay_exponent
+ * - max_ack_delay
+ * - initial_scid
+ * - original_dcid
+ * - preferred_address and preferred_address_present
+ * - retry_scid and retry_scid_present
+ * - stateless_reset_token and stateless_reset_token_present
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_local_transport_params` sets the local transport
+ * parameters |params|. This function can only be called by server.
+ * Although the local transport parameters are passed to
+ * `ngtcp2_conn_server_new`, server might want to update them after
+ * ALPN is chosen. In that case, server can update the transport
+ * parameter with this function. Server must call this function
+ * before calling `ngtcp2_conn_install_tx_handshake_key`.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * `ngtcp2_conn_install_tx_handshake_key` has been called.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_set_local_transport_params_versioned(
+ ngtcp2_conn *conn, int transport_params_version,
+ const ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_local_transport_params` returns a pointer to the
+ * local QUIC transport parameters.
+ */
+NGTCP2_EXTERN const ngtcp2_transport_params *
+ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_encode_local_transport_params` encodes the local QUIC
+ * transport parameters in |dest| of length |destlen|. This is
+ * equivalent to calling `ngtcp2_conn_get_local_transport_params` and
+ * then `ngtcp2_encode_transport_params`.
+ *
+ * This function returns the number of written, or one of the
+ * following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_encode_local_transport_params(
+ ngtcp2_conn *conn, uint8_t *dest, size_t destlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream. The
+ * |stream_user_data| is the user data specific to the stream. The
+ * open stream ID is stored in |*pstream_id|.
+ *
+ * Application can call this function before handshake completes. For
+ * 0RTT packet, application can call this function after calling
+ * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet,
+ * application can call this function after calling
+ * `ngtcp2_conn_decode_remote_transport_params` and
+ * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is
+ * used, application can call this function after calling
+ * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED`
+ * The remote peer does not allow |stream_id| yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn,
+ int64_t *pstream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_open_uni_stream` opens new unidirectional stream. The
+ * |stream_user_data| is the user data specific to the stream. The
+ * open stream ID is stored in |*pstream_id|.
+ *
+ * Application can call this function before handshake completes. For
+ * 0RTT packet, application can call this function after calling
+ * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet,
+ * application can call this function after calling
+ * `ngtcp2_conn_decode_remote_transport_params` and
+ * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is
+ * used, application can call this function after calling
+ * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED`
+ * The remote peer does not allow |stream_id| yet.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn,
+ int64_t *pstream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream` closes stream denoted by |stream_id|
+ * abruptly. |app_error_code| is one of application error codes, and
+ * indicates the reason of shutdown. Successful call of this function
+ * does not immediately erase the state of the stream. The actual
+ * deletion is done when the remote endpoint sends acknowledgement.
+ * Calling this function is equivalent to call
+ * `ngtcp2_conn_shutdown_stream_read`, and
+ * `ngtcp2_conn_shutdown_stream_write` sequentially with the following
+ * differences. If |stream_id| refers to a local unidirectional
+ * stream, this function only shutdowns write side of the stream. If
+ * |stream_id| refers to a remote unidirectional stream, this function
+ * only shutdowns read side of the stream.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ */
+NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t app_error_code);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream_write` closes write-side of stream
+ * denoted by |stream_id| abruptly. |app_error_code| is one of
+ * application error codes, and indicates the reason of shutdown. If
+ * this function succeeds, no application data is sent to the remote
+ * endpoint. It discards all data which has not been acknowledged
+ * yet.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |stream_id| refers to a remote unidirectional stream.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t app_error_code);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_shutdown_stream_read` closes read-side of stream
+ * denoted by |stream_id| abruptly. |app_error_code| is one of
+ * application error codes, and indicates the reason of shutdown. If
+ * this function succeeds, no application data is forwarded to an
+ * application layer.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |stream_id| refers to a local unidirectional stream.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t app_error_code);
+
+/**
+ * @macrosection
+ *
+ * Write stream data flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may
+ * come and should be coalesced into the same packet if possible.
+ */
+#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that the passed
+ * data is the final part of a stream.
+ */
+#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_stream` is just like
+ * `ngtcp2_conn_writev_stream`. The only difference is that it
+ * conveniently accepts a single buffer.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_writev_stream` writes a packet containing stream data
+ * of stream denoted by |stream_id|. The buffer of the packet is
+ * pointed by |dest| of length |destlen|. This function performs QUIC
+ * handshake as well.
+ *
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_tx_udp_payload_size`.
+ *
+ * Specifying -1 to |stream_id| means no new stream data to send.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings. When calling
+ * this function again after it returns
+ * :macro:`NGTCP2_ERR_WRITE_MORE`, caller must pass the same |pi| to
+ * this function.
+ *
+ * If the all given data is encoded as STREAM frame in |dest|, and if
+ * |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, fin
+ * flag is set to outgoing STREAM frame. Otherwise, fin flag in
+ * STREAM frame is not set.
+ *
+ * This packet may contain frames other than STREAM frame. The packet
+ * might not contain STREAM frame if other frames occupy the packet.
+ * In that case, |*pdatalen| would be -1 if |pdatalen| is not
+ * ``NULL``.
+ *
+ * If |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, and
+ * 0 length STREAM frame is successfully serialized, |*pdatalen| would
+ * be 0.
+ *
+ * The number of data encoded in STREAM frame is stored in |*pdatalen|
+ * if it is not ``NULL``. The caller must keep the portion of data
+ * covered by |*pdatalen| bytes in tact until
+ * :type:`ngtcp2_acked_stream_data_offset` indicates that they are
+ * acknowledged by a remote endpoint or the stream is closed.
+ *
+ * If |flags| equals to :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this
+ * function produces a single payload of UDP packet. If the given
+ * stream data is small (e.g., few bytes), the packet might be
+ * severely under filled. Too many small packet might increase
+ * overall packet processing costs. Unless there are retransmissions,
+ * by default, application can only send 1 STREAM frame in one QUIC
+ * packet. In order to include more than 1 STREAM frame in one QUIC
+ * packet, specify :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|.
+ * This is analogous to ``MSG_MORE`` flag in :manpage:`send(2)`. If
+ * the :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4
+ * outcomes:
+ *
+ * - The function returns the written length of packet just like
+ * without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because
+ * packet is nearly full and the library decided to make a complete
+ * packet. |*pdatalen| might be -1 or >= 0. It may return 0 which
+ * indicates that no packet transmission is possible at the moment
+ * for some reason.
+ *
+ * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this
+ * case, |*pdatalen| >= 0 is asserted. It indicates that
+ * application can still call this function with different stream
+ * data (or `ngtcp2_conn_writev_datagram` if it has data to send in
+ * unreliable datagram) to pack them into the same packet.
+ * Application has to specify the same |conn|, |path|, |pi|, |dest|,
+ * |destlen|, and |ts| parameters, otherwise the behaviour is
+ * undefined. The application can change |flags|.
+ *
+ * - The function returns one of the following negative error codes:
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`,
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`,
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR`. In this case, |*pdatalen| ==
+ * -1 is asserted. Application can still write the stream data of
+ * the other streams by calling this function (or
+ * `ngtcp2_conn_writev_datagram` if it has data to send in
+ * unreliable datagram) to pack them into the same packet.
+ * Application has to specify the same |conn|, |path|, |pi|, |dest|,
+ * |destlen|, and |ts| parameters, otherwise the behaviour is
+ * undefined. The application can change |flags|.
+ *
+ * - The other negative error codes might be returned just like
+ * without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`. These errors
+ * should be treated as a connection error.
+ *
+ * When application uses :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` at
+ * least once, it must not call other ngtcp2 API functions
+ * (application can still call `ngtcp2_conn_write_connection_close` to
+ * handle error from this function), just keep calling this function
+ * (or `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram`)
+ * until it returns 0, a positive number (which indicates a complete
+ * packet is ready), or the error codes other than
+ * :macro:`NGTCP2_ERR_WRITE_MORE`,
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`,
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`, and
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR`. If there is no stream data to
+ * include, call this function with |stream_id| as -1 to stop
+ * coalescing and write a packet.
+ *
+ * This function returns 0 if it cannot write any frame because buffer
+ * is too small, or packet is congestion limited. Application should
+ * keep reading and wait for congestion window to grow.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * `ngtcp2_conn_update_pkt_tx_time` must be called after this
+ * function. Application may call this function multiple times before
+ * calling `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
+ * Stream does not exist
+ * :macro:`NGTCP2_ERR_STREAM_SHUT_WR`
+ * Stream is half closed (local); or stream is being reset.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The total length of stream data is too large.
+ * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED`
+ * Stream is blocked because of flow control.
+ * :macro:`NGTCP2_ERR_WRITE_MORE`
+ * (Only when :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified)
+ * Application can call this function to pack more stream data
+ * into the same packet. See above to know how it works.
+ *
+ * In general, if the error code which satisfies
+ * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned,
+ * the application should just close the connection by calling
+ * `ngtcp2_conn_write_connection_close` or just delete the QUIC
+ * connection using `ngtcp2_conn_del`. It is undefined to call the
+ * other library functions.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
+ uint32_t flags, int64_t stream_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts);
+
+/**
+ * @macrosection
+ *
+ * Write datagram flags
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set.
+ */
+#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00u
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data
+ * may come and should be coalesced into the same packet if possible.
+ */
+#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_writev_datagram` writes a packet containing unreliable
+ * data in DATAGRAM frame. The buffer of the packet is pointed by
+ * |dest| of length |destlen|. This function performs QUIC handshake
+ * as well.
+ *
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_tx_udp_payload_size`.
+ *
+ * For |path| and |pi| parameters, refer to
+ * `ngtcp2_conn_writev_stream`.
+ *
+ * If the given data is written to the buffer, nonzero value is
+ * assigned to |*paccepted| if it is not NULL. The data in DATAGRAM
+ * frame cannot be fragmented; writing partial data is not possible.
+ *
+ * |dgram_id| is an opaque identifier which should uniquely identify
+ * the given DATAGRAM. It is passed to :type:`ngtcp2_ack_datagram`
+ * callback when a packet that contains DATAGRAM frame is
+ * acknowledged. It is passed to :type:`ngtcp2_lost_datagram`
+ * callback when a packet that contains DATAGRAM frame is declared
+ * lost. If an application uses neither of those callbacks, it can
+ * sets 0 to this parameter.
+ *
+ * This function might write other frames other than DATAGRAM, just
+ * like `ngtcp2_conn_writev_stream`.
+ *
+ * If the function returns 0, it means that no more data cannot be
+ * sent because of congestion control limit; or, data does not fit
+ * into the provided buffer; or, a local endpoint, as a server, is
+ * unable to send data because of its amplification limit. In this
+ * case, |*paccepted| is assigned zero if it is not NULL.
+ *
+ * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is set in |flags|,
+ * there are 3 outcomes:
+ *
+ * - The function returns the written length of packet just like
+ * without :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`. This is
+ * because packet is nearly full and the library decided to make a
+ * complete packet. |*paccepted| might be zero or nonzero.
+ *
+ * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this
+ * case, |*paccepted| != 0 is asserted. This indicates that
+ * application can call this function with another unreliable data
+ * (or `ngtcp2_conn_writev_stream` if it has stream data to send) to
+ * pack them into the same packet. Application has to specify the
+ * same |conn|, |path|, |pi|, |dest|, |destlen|, and |ts|
+ * parameters, otherwise the behaviour is undefined. The
+ * application can change |flags|.
+ *
+ * - The other error might be returned just like without
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`.
+ *
+ * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not
+ * call other ngtcp2 API functions (application can still call
+ * `ngtcp2_conn_write_connection_close` to handle error from this
+ * function). Just keep calling `ngtcp2_conn_writev_datagram`,
+ * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it
+ * returns a positive number (which indicates a complete packet is
+ * ready).
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ * :macro:`NGTCP2_ERR_WRITE_MORE`
+ * (Only when :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is
+ * specified) Application can call this function to pack more data
+ * into the same packet. See above to know how it works.
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * A remote endpoint did not express the DATAGRAM frame support.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * The provisional DATAGRAM frame size exceeds the maximum
+ * DATAGRAM frame size that a remote endpoint can receive.
+ *
+ * In general, if the error code which satisfies
+ * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned,
+ * the application should just close the connection by calling
+ * `ngtcp2_conn_write_connection_close` or just delete the QUIC
+ * connection using `ngtcp2_conn_del`. It is undefined to call the
+ * other library functions.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted,
+ uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in
+ * the closing period.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in
+ * the draining period.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_stream_offset` extends stream's max stream
+ * data value by |datalen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn,
+ int64_t stream_id,
+ uint64_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_offset` extends max data offset by
+ * |datalen|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn,
+ uint64_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum
+ * local bidirectional streams that a remote endpoint can open by |n|.
+ *
+ * The library does not increase maximum stream limit automatically.
+ * The exception is when a stream is closed without
+ * :type:`ngtcp2_stream_open` callback being called. In this case,
+ * stream limit is increased automatically.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn,
+ size_t n);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_extend_max_streams_uni` extends the number of maximum
+ * local unidirectional streams that a remote endpoint can open by
+ * |n|.
+ *
+ * The library does not increase maximum stream limit automatically.
+ * The exception is when a stream is closed without
+ * :type:`ngtcp2_stream_open` callback being called. In this case,
+ * stream limit is increased automatically.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn,
+ size_t n);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to destination
+ * connection ID. If no destination connection ID is present, the
+ * return value is not ``NULL``, and its datalen field is 0.
+ */
+NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_client_initial_dcid` returns the non-NULL pointer
+ * to the Destination Connection ID that client sent in its Initial
+ * packet.
+ */
+NGTCP2_EXTERN const ngtcp2_cid *
+ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_num_scid` returns the number of source connection
+ * IDs which the local endpoint has provided to the peer and have not
+ * retired.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_scid` writes the all source connection IDs which
+ * the local endpoint has provided to the peer and have not retired in
+ * |dest|. The buffer pointed by |dest| must have
+ * ``sizeof(ngtcp2_cid) * n`` bytes available, where n is the return
+ * value of `ngtcp2_conn_get_num_scid()`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_num_active_dcid` returns the number of the active
+ * destination connection ID.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_cid_token` is the convenient struct to store
+ * Connection ID, its associated path, and stateless reset token.
+ */
+typedef struct ngtcp2_cid_token {
+ /**
+ * :member:`seq` is the sequence number of this Connection ID.
+ */
+ uint64_t seq;
+ /**
+ * :member:`cid` is Connection ID.
+ */
+ ngtcp2_cid cid;
+ /**
+ * :member:`ps` is the path which is associated to this Connection
+ * ID.
+ */
+ ngtcp2_path_storage ps;
+ /**
+ * :member:`token` is the stateless reset token for this Connection
+ * ID.
+ */
+ uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ /**
+ * :member:`token_present` is nonzero if token contains stateless
+ * reset token.
+ */
+ uint8_t token_present;
+} ngtcp2_cid_token;
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_active_dcid` writes the all active destination
+ * connection IDs and tokens to |dest|. The buffer pointed by |dest|
+ * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n
+ * is the return value of `ngtcp2_conn_get_num_active_dcid()`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn,
+ ngtcp2_cid_token *dest);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_client_chosen_version` returns the client chosen
+ * version.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_negotiated_version` returns the negotiated version.
+ *
+ * Until the version is negotiated, this function returns 0.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_early_data_rejected` tells |conn| that early data was
+ * rejected by a server, or client decided not to attempt early data
+ * for some reason. |conn| discards the following connection states:
+ *
+ * - Any opended streams.
+ * - Stream identifier allocations.
+ * - Max data extended by `ngtcp2_conn_extend_max_offset`.
+ * - Max bidi streams extended by `ngtcp2_conn_extend_max_streams_bidi`.
+ * - Max uni streams extended by `ngtcp2_conn_extend_max_streams_uni`.
+ *
+ * Application which wishes to retransmit early data, it has to open
+ * streams and send stream data again.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_early_data_rejected` returns nonzero if
+ * `ngtcp2_conn_early_data_rejected` has been called.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_get_early_data_rejected(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to
+ * |*cstat|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
+ int conn_stat_version,
+ ngtcp2_conn_stat *cstat);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_submit_crypto_data` submits crypto stream data |data|
+ * of length |datalen| to the library for transmission. The
+ * encryption level is given in |crypto_level|.
+ *
+ * The library makes a copy of the buffer pointed by |data| of length
+ * |datalen|. Application can discard |data|.
+ */
+NGTCP2_EXTERN int
+ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, const size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_submit_new_token` submits address validation token.
+ * It is sent in NEW_TOKEN frame. Only server can call this function.
+ * |tokenlen| must not be 0.
+ *
+ * This function makes a copy of the buffer pointed by |token| of
+ * length |tokenlen|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn,
+ const uint8_t *token,
+ size_t tokenlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to
+ * the current path of |conn|.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn,
+ const ngtcp2_addr *addr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_path_user_data` sets the |path_user_data| to the
+ * current path (see :member:`ngtcp2_path.user_data`).
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn,
+ void *path_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_path` returns the current path.
+ */
+NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_tx_udp_payload_size` returns the maximum UDP
+ * payload size that this local endpoint would send. This is the
+ * value of :member:`ngtcp2_settings.max_tx_udp_payload_size` that is
+ * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_max_tx_udp_payload_size(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_path_max_tx_udp_payload_size` returns the maximum
+ * UDP payload size for the current path. If
+ * :member:`ngtcp2_settings.no_tx_udp_payload_size_shaping` is set to
+ * nonzero, this function is equivalent to
+ * `ngtcp2_conn_get_max_tx_udp_payload_size`. Otherwise, it returns
+ * the maximum UDP payload size that is probed for the current path.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_conn_get_path_max_tx_udp_payload_size(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_initiate_immediate_migration` starts connection
+ * migration to the given |path|. Only client can initiate migration.
+ * This function does immediate migration; while the path validation
+ * is nonetheless performed, this function does not wait for it to
+ * succeed.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * Migration is disabled; or handshake is not yet confirmed; or
+ * client is migrating to server's preferred address.
+ * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED`
+ * No unused connection ID is available.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |local_addr| equals the current local address.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_immediate_migration(
+ ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_initiate_migration` starts connection migration to the
+ * given |path|. Only client can initiate migration. Unlike
+ * `ngtcp2_conn_initiate_immediate_migration`, this function starts a
+ * path validation with a new path and migrate to the new path after
+ * successful path validation.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * Migration is disabled; or handshake is not yet confirmed; or
+ * client is migrating to server's preferred address.
+ * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED`
+ * No unused connection ID is available.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ * |local_addr| equals the current local address.
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ */
+NGTCP2_EXTERN int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn,
+ const ngtcp2_path *path,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_local_streams_uni` returns the cumulative
+ * number of streams which local endpoint can open.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_data_left` returns the number of bytes that
+ * this local endpoint can send in this connection.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_max_stream_data_left` returns the number of bytes
+ * that this local endpoint can send to a stream identified by
+ * |stream_id|. If no such stream is found, this function returns 0.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_stream_data_left(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_streams_bidi_left` returns the number of
+ * bidirectional streams which the local endpoint can open without
+ * violating stream concurrency limit.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_streams_uni_left` returns the number of
+ * unidirectional streams which the local endpoint can open without
+ * violating stream concurrency limit.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of
+ * bytes in flight on the current path. If the former is smaller than
+ * the latter, this function returns 0.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for Initial packet
+ * encryption. The passed data will be passed to
+ * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_initial_crypto_ctx` returns
+ * :type:`ngtcp2_crypto_ctx` object for Initial packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet
+ * encryption. The passed data will be passed to
+ * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_tls_native_handle` returns TLS native handle set by
+ * `ngtcp2_conn_set_tls_native_handle()`.
+ */
+NGTCP2_EXTERN void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_tls_native_handle` sets TLS native handle
+ * |tls_native_handle| to |conn|. Internally, it is used as an opaque
+ * pointer.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn,
+ void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_retry_aead` sets |aead| and |aead_ctx| for Retry
+ * integrity tag verification. |aead| must be AEAD_AES_128_GCM.
+ * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as
+ * encryption key. This function must be called if |conn| is
+ * initialized as client. Server does not verify the tag and has no
+ * need to call this function.
+ *
+ * If this function succeeds, |conn| takes ownership of |aead_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` will be called to delete this
+ * object when it is no longer used. If this function fails, the
+ * caller is responsible to delete it.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx`
+ * object for Handshake/1RTT packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_early_crypto_ctx` sets |ctx| for 0RTT packet
+ * encryption. The passed data will be passed to
+ * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
+ * :type:`ngtcp2_hp_mask` callbacks.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn,
+ const ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_early_crypto_ctx` returns
+ * :type:`ngtcp2_crypto_ctx` object for 0RTT packet encryption.
+ */
+NGTCP2_EXTERN const ngtcp2_crypto_ctx *
+ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_connection_close_error_code_type` defines connection
+ * error code type.
+ */
+typedef enum ngtcp2_connection_close_error_code_type {
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`
+ * indicates the error code is QUIC transport error code.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`
+ * indicates the error code is application error code.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`
+ * is a special case of QUIC transport error, and it indicates that
+ * client receives Version Negotiation packet.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+ /**
+ * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`
+ * is a special case of QUIC transport error, and it indicates that
+ * connection is closed because of idle timeout.
+ */
+ NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE
+} ngtcp2_connection_close_error_code_type;
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_connection_close_error` contains connection
+ * error code, its type, and the optional reason phrase.
+ */
+typedef struct ngtcp2_connection_close_error {
+ /**
+ * :member:`type` is the type of :member:`error_code`.
+ */
+ ngtcp2_connection_close_error_code_type type;
+ /**
+ * :member:`error_code` is the error code for connection closure.
+ */
+ uint64_t error_code;
+ /**
+ * :member:`frame_type` is the type of QUIC frame which triggers
+ * this connection error. This field is set to 0 if the frame type
+ * is unknown.
+ */
+ uint64_t frame_type;
+ /**
+ * :member:`reason` points to the buffer which contains a reason
+ * phrase. It may be NULL if there is no reason phrase. If it is
+ * received from a remote endpoint, it is truncated to at most 1024
+ * bytes.
+ */
+ const uint8_t *reason;
+ /**
+ * :member:`reasonlen` is the length of data pointed by
+ * :member:`reason`.
+ */
+ size_t reasonlen;
+} ngtcp2_connection_close_error;
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_default` initializes |ccerr| with
+ * the default values. It sets the following fields:
+ *
+ * - :member:`type <ngtcp2_connection_close_error.type>` =
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`
+ * - :member:`error_code <ngtcp2_connection_close_error.error_code>` =
+ * :macro:`NGTCP2_NO_ERROR`.
+ * - :member:`frame_type <ngtcp2_connection_close_error.frame_type>` =
+ * 0
+ * - :member:`reason <ngtcp2_connection_close_error.reason>` = NULL
+ * - :member:`reasonlen <ngtcp2_connection_close_error.reasonlen>` = 0
+ */
+NGTCP2_EXTERN void
+ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|. This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_liberr` sets
+ * type and error_code based on |liberr|.
+ *
+ * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`,
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` is set
+ * to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`. If |liberr| is
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`. Otherwise, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` is set to an error code
+ * inferred by |liberr| (see
+ * `ngtcp2_err_infer_quic_transport_error_code`). |reason| is the
+ * reason phrase of length |reasonlen|. This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr(
+ ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+ size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to bitwise-OR of
+ * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|. |reason| is the
+ * reason phrase of length |reasonlen|. This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+ ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_application_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|. This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error(
+ ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+ const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close` writes a packet which contains
+ * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |destlen|.
+ *
+ * For client, |destlen| should be at least
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent. Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long. The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds. The metadata includes ECN markings.
+ *
+ * If :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * this function sends CONNECTION_CLOSE (type 0x1c) frame. If
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * it sends CONNECTION_CLOSE (type 0x1d) frame. Otherwise, it does
+ * not produce any data, and returns 0.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close. We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ * Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ * Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ * The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ * Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ * User callback failed
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+ ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+ ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+ const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_connection_close_error` stores the received
+ * connection close error in |ccerr|.
+ */
+NGTCP2_EXTERN void
+ngtcp2_conn_get_connection_close_error(ngtcp2_conn *conn,
+ ngtcp2_connection_close_error *ccerr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| denotes the
+ * stream which a local endpoint issues.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_is_server` returns nonzero if |conn| is initialized as
+ * server.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_is_server(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_after_retry` returns nonzero if |conn| as a client has
+ * received Retry packet from server and successfully validated it.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_after_retry(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_set_stream_user_data` sets |stream_user_data| to the
+ * stream identified by |stream_id|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
+ * Stream does not exist
+ */
+NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
+ int64_t stream_id,
+ void *stream_user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_update_pkt_tx_time` sets the time instant of the next
+ * packet transmission. This function must be called after (multiple
+ * invocation of) `ngtcp2_conn_writev_stream`. If packet aggregation
+ * (e.g., packet batching, GSO) is used, call this function after all
+ * aggregated datagrams are sent, which indicates multiple invocation
+ * of `ngtcp2_conn_writev_stream`.
+ */
+NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_send_quantum` returns the maximum number of bytes
+ * that can be sent in one go without packet spacing.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_stream_loss_count` returns the number of packets
+ * that contain STREAM frame for a stream identified by |stream_id|
+ * and are declared to be lost. The number may include the spurious
+ * losses. If no stream identified by |stream_id| is found, this
+ * function returns 0.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn,
+ int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `ngtcp2_strerror` returns the text representation of |liberr|.
+ * |liberr| must be one of ngtcp2 library error codes (which is
+ * defined as NGTCP2_ERR_* macro, such as
+ * :macro:`NGTCP2_ERR_DECRYPT`).
+ */
+NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error.
+ * |liberr| must be one of ngtcp2 library error codes (which is
+ * defined as NGTCP2_ERR_* macro, such as
+ * :macro:`NGTCP2_ERR_DECRYPT`).
+ */
+NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC
+ * transport error code which corresponds to |liberr|. |liberr| must
+ * be one of ngtcp2 library error codes (which is defined as
+ * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_DECRYPT`).
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_addr_init` initializes |dest| with the given arguments and
+ * returns |dest|.
+ */
+NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
+ const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_addr_copy_byte` copies |addr| of length |addrlen| into the
+ * buffer pointed by :member:`dest->addr <ngtcp2_addr.addr>`.
+ * :member:`dest->addrlen <ngtcp2_addr.addrlen>` is updated to have
+ * |addrlen|. This function assumes that :member:`dest->addr
+ * <ngtcp2_addr.addr>` points to a buffer which has a sufficient
+ * capacity to store the copy.
+ */
+NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest,
+ const ngtcp2_sockaddr *addr,
+ ngtcp2_socklen addrlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_storage_init` initializes |ps| with the given
+ * arguments. This function copies |local_addr| and |remote_addr|.
+ */
+NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
+ const ngtcp2_sockaddr *local_addr,
+ ngtcp2_socklen local_addrlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_storage_zero` initializes |ps| with the zero length
+ * addresses.
+ */
+NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
+
+/**
+ * @function
+ *
+ * `ngtcp2_settings_default` initializes |settings| with the default
+ * values. First this function fills |settings| with 0 and set the
+ * default value to the following fields:
+ *
+ * * :type:`cc_algo <ngtcp2_settings.cc_algo>` =
+ * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC`
+ * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` =
+ * :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
+ * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
+ * * :type:`max_tx_udp_payload_size
+ * <ngtcp2_settings.max_tx_udp_payload_size>` = 1452
+ * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
+ * :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`.
+ */
+NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version,
+ ngtcp2_settings *settings);
+
+/**
+ * @function
+ *
+ * `ngtcp2_transport_params_default` initializes |params| with the
+ * default values. First this function fills |params| with 0 and set
+ * the default value to the following fields:
+ *
+ * * :type:`max_udp_payload_size
+ * <ngtcp2_transport_params.max_udp_payload_size>` =
+ * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE`
+ * * :type:`ack_delay_exponent
+ * <ngtcp2_transport_params.ack_delay_exponent>` =
+ * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT`
+ * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
+ * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY`
+ * * :type:`active_connection_id_limit
+ * <ngtcp2_transport_params.active_connection_id_limit>` =
+ * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT`
+ */
+NGTCP2_EXTERN void
+ngtcp2_transport_params_default_versioned(int transport_params_version,
+ ngtcp2_transport_params *params);
+
+/**
+ * @function
+ *
+ * `ngtcp2_mem_default` returns the default, system standard memory
+ * allocator.
+ */
+NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void);
+
+/**
+ * @macrosection
+ *
+ * ngtcp2_info macros
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_VERSION_AGE` is the age of :type:`ngtcp2_info`
+ */
+#define NGTCP2_VERSION_AGE 1
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_info` is what `ngtcp2_version()` returns. It holds
+ * information about the particular ngtcp2 version.
+ */
+typedef struct ngtcp2_info {
+ /**
+ * :member:`age` is the age of this struct. This instance of ngtcp2
+ * sets it to :macro:`NGTCP2_VERSION_AGE` but a future version may
+ * bump it and add more struct fields at the bottom
+ */
+ int age;
+ /**
+ * :member:`version_num` is the :macro:`NGTCP2_VERSION_NUM` number
+ * (since age ==1)
+ */
+ int version_num;
+ /**
+ * :member:`version_str` points to the :macro:`NGTCP2_VERSION`
+ * string (since age ==1)
+ */
+ const char *version_str;
+ /* -------- the above fields all exist when age == 1 */
+} ngtcp2_info;
+
+/**
+ * @function
+ *
+ * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with
+ * version information about the run-time library in use. The
+ * |least_version| argument can be set to a 24 bit numerical value for
+ * the least accepted version number and if the condition is not met,
+ * this function will return a ``NULL``. Pass in 0 to skip the
+ * version checking.
+ */
+NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version);
+
+/**
+ * @function
+ *
+ * `ngtcp2_is_bidi_stream` returns nonzero if |stream_id| denotes
+ * bidirectional stream.
+ */
+NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes
+ * that |dest| has enough buffer to store the deep copy of
+ * :member:`src->local <ngtcp2_path.local>` and :member:`src->remote
+ * <ngtcp2_path.remote>`.
+ */
+NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src);
+
+/**
+ * @function
+ *
+ * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same
+ * local and remote addresses.
+ */
+NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
+
+/**
+ * @function
+ *
+ * `ngtcp2_is_supported_version` returns nonzero if the library supports
+ * QUIC version |version|.
+ */
+NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version);
+
+/*
+ * @function
+ *
+ * `ngtcp2_is_reserved_version` returns nonzero if |version| is a
+ * reserved version.
+ */
+NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version);
+
+/**
+ * @function
+ *
+ * `ngtcp2_select_version` selects and returns a version from the
+ * version set |offered_versions| of |offered_versionslen| elements.
+ * |preferred_versions| of |preferred_versionslen| elements specifies
+ * the preference of versions, which is sorted in the order of
+ * preference. All versions included in |preferred_versions| must be
+ * supported by the library, that is, passing a version to
+ * `ngtcp2_is_supported_version` must return nonzero. This function
+ * is intended to be used by client when it receives Version
+ * Negotiation packet. If no version is selected, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+ size_t preferred_versionslen,
+ const uint32_t *offered_versions,
+ size_t offered_versionslen);
+
+/*
+ * Versioned function wrappers
+ */
+
+/*
+ * `ngtcp2_conn_read_pkt` is a wrapper around
+ * `ngtcp2_conn_read_pkt_versioned` to set the correct struct version.
+ */
+#define ngtcp2_conn_read_pkt(CONN, PATH, PI, PKT, PKTLEN, TS) \
+ ngtcp2_conn_read_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \
+ (PI), (PKT), (PKTLEN), (TS))
+
+/*
+ * `ngtcp2_conn_write_pkt` is a wrapper around
+ * `ngtcp2_conn_write_pkt_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_write_pkt(CONN, PATH, PI, DEST, DESTLEN, TS) \
+ ngtcp2_conn_write_pkt_versioned((CONN), (PATH), NGTCP2_PKT_INFO_VERSION, \
+ (PI), (DEST), (DESTLEN), (TS))
+
+/*
+ * `ngtcp2_conn_write_stream` is a wrapper around
+ * `ngtcp2_conn_write_stream_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_write_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \
+ FLAGS, STREAM_ID, DATA, DATALEN, TS) \
+ ngtcp2_conn_write_stream_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PDATALEN), (FLAGS), (STREAM_ID), (DATA), (DATALEN), (TS))
+
+/*
+ * `ngtcp2_conn_writev_stream` is a wrapper around
+ * `ngtcp2_conn_writev_stream_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_writev_stream(CONN, PATH, PI, DEST, DESTLEN, PDATALEN, \
+ FLAGS, STREAM_ID, DATAV, DATAVCNT, TS) \
+ ngtcp2_conn_writev_stream_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PDATALEN), (FLAGS), (STREAM_ID), (DATAV), (DATAVCNT), (TS))
+
+/*
+ * `ngtcp2_conn_writev_datagram` is a wrapper around
+ * `ngtcp2_conn_writev_datagram_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_writev_datagram(CONN, PATH, PI, DEST, DESTLEN, PACCEPTED, \
+ FLAGS, DGRAM_ID, DATAV, DATAVCNT, TS) \
+ ngtcp2_conn_writev_datagram_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (PACCEPTED), (FLAGS), (DGRAM_ID), (DATAV), (DATAVCNT), (TS))
+
+/*
+ * `ngtcp2_conn_write_connection_close` is a wrapper around
+ * `ngtcp2_conn_write_connection_close_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN, \
+ CCERR, TS) \
+ ngtcp2_conn_write_connection_close_versioned( \
+ (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN), \
+ (CCERR), (TS))
+
+/*
+ * `ngtcp2_encode_transport_params` is a wrapper around
+ * `ngtcp2_encode_transport_params_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_encode_transport_params(DEST, DESTLEN, EXTTYPE, PARAMS) \
+ ngtcp2_encode_transport_params_versioned( \
+ (DEST), (DESTLEN), (EXTTYPE), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_decode_transport_params` is a wrapper around
+ * `ngtcp2_decode_transport_params_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_decode_transport_params(PARAMS, EXTTYPE, DATA, DATALEN) \
+ ngtcp2_decode_transport_params_versioned( \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (EXTTYPE), (DATA), (DATALEN))
+
+/*
+ * `ngtcp2_conn_client_new` is a wrapper around
+ * `ngtcp2_conn_client_new_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_client_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \
+ SETTINGS, PARAMS, MEM, USER_DATA) \
+ ngtcp2_conn_client_new_versioned( \
+ (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \
+ (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA))
+
+/*
+ * `ngtcp2_conn_server_new` is a wrapper around
+ * `ngtcp2_conn_server_new_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_server_new(PCONN, DCID, SCID, PATH, VERSION, CALLBACKS, \
+ SETTINGS, PARAMS, MEM, USER_DATA) \
+ ngtcp2_conn_server_new_versioned( \
+ (PCONN), (DCID), (SCID), (PATH), (VERSION), NGTCP2_CALLBACKS_VERSION, \
+ (CALLBACKS), NGTCP2_SETTINGS_VERSION, (SETTINGS), \
+ NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS), (MEM), (USER_DATA))
+
+/*
+ * `ngtcp2_conn_set_early_remote_transport_params` is a wrapper around
+ * `ngtcp2_conn_set_early_remote_transport_params_versioned` to set
+ * the correct struct version.
+ */
+#define ngtcp2_conn_set_early_remote_transport_params(CONN, PARAMS) \
+ ngtcp2_conn_set_early_remote_transport_params_versioned( \
+ (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_conn_set_local_transport_params` is a wrapper around
+ * `ngtcp2_conn_set_local_transport_params_versioned` to set the
+ * correct struct version.
+ */
+#define ngtcp2_conn_set_local_transport_params(CONN, PARAMS) \
+ ngtcp2_conn_set_local_transport_params_versioned( \
+ (CONN), NGTCP2_TRANSPORT_PARAMS_VERSION, (PARAMS))
+
+/*
+ * `ngtcp2_transport_params_default` is a wrapper around
+ * `ngtcp2_transport_params_default_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_transport_params_default(PARAMS) \
+ ngtcp2_transport_params_default_versioned(NGTCP2_TRANSPORT_PARAMS_VERSION, \
+ (PARAMS))
+
+/*
+ * `ngtcp2_conn_get_conn_stat` is a wrapper around
+ * `ngtcp2_conn_get_conn_stat_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_conn_get_conn_stat(CONN, CSTAT) \
+ ngtcp2_conn_get_conn_stat_versioned((CONN), NGTCP2_CONN_STAT_VERSION, (CSTAT))
+
+/*
+ * `ngtcp2_settings_default` is a wrapper around
+ * `ngtcp2_settings_default_versioned` to set the correct struct
+ * version.
+ */
+#define ngtcp2_settings_default(SETTINGS) \
+ ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS))
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
new file mode 100644
index 0000000..4736b51
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
@@ -0,0 +1,893 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_H
+#define NGTCP2_CRYPTO_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <ws2tcpip.h>
+#endif /* WIN32 */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_SECRETLEN` is the length of secret
+ * for Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_KEYLEN` is the length of key for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_IVLEN` is the length of IV for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_IVLEN 12
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated
+ * ciphers and message digests from native TLS session
+ * |tls_native_handle|. This is used for encrypting/decrypting
+ * Handshake and Short header packets.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_tls_early` initializes |ctx| by extracting early
+ * ciphers and message digests from native TLS session
+ * |tls_native_handle|. This is used for encrypting/decrypting 0RTT
+ * packets.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *
+ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_md_init` initializes |md| with the provided
+ * |md_native_handle| which is an underlying message digest object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |md_native_handle| must be a
+ * pointer to EVP_MD.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |md_native_handle| must be
+ * gnutls_mac_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |md_native_handle| must be
+ * a pointer to EVP_MD.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_md_hashlen` returns the length of |md| output.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for
+ * |aead|.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The
+ * result is the length of |md| and is stored to the buffer pointed by
+ * |dest|. The caller is responsible to specify the buffer that can
+ * store the output.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_expand` performs HKDF expand operation. The
+ * result is |destlen| bytes long and is stored to the buffer pointed
+ * by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen,
+ const uint8_t *info,
+ size_t infolen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf` performs HKDF operation. The result is
+ * |destlen| bytes long and is stored to the buffer pointed by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The
+ * result is |destlen| bytes long and is stored to the buffer pointed
+ * by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen,
+ const uint8_t *label,
+ size_t labellen);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_crypto_side` indicates which side the application
+ * implements; client or server.
+ */
+typedef enum ngtcp2_crypto_side {
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_CLIENT` indicates that the application
+ * is client.
+ */
+ NGTCP2_CRYPTO_SIDE_CLIENT,
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_SERVER` indicates that the application
+ * is server.
+ */
+ NGTCP2_CRYPTO_SIDE_SERVER
+} ngtcp2_crypto_side;
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV
+ * used to encrypt QUIC packet.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length
+ * |plaintextlen| and writes the ciphertext into the buffer pointed by
+ * |dest|. The length of ciphertext is plaintextlen +
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * ciphertext. It is allowed to specify the same value to |dest| and
+ * |plaintext|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext,
+ size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_encrypt_cb` is a wrapper function around
+ * `ngtcp2_crypto_encrypt`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.encrypt` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length
+ * |ciphertextlen| and writes the plaintext into the buffer pointed by
+ * |dest|. The length of plaintext is ciphertextlen -
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * plaintext. It is allowed to specify the same value to |dest| and
+ * |ciphertext|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext,
+ size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_decrypt_cb` is a wrapper function around
+ * `ngtcp2_crypto_decrypt`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.decrypt` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_TLS_DECRYPT`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hp_mask` generates mask which is used in packet
+ * header encryption. The mask is written to the buffer pointed by
+ * |dest|. The sample is passed as |sample| which is
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must
+ * be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the
+ * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The
+ * buffer pointed by |dest| must have at least
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes available.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest,
+ const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around
+ * `ngtcp2_crypto_hp_mask`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.hp_mask` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_rx_key` derives the rx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for decryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for decryption is written to the
+ * buffer pointed by |hp|.
+ *
+ * |secretlen| specifies the length of |secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
+ *
+ * In the first call of this function, it calls
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as client, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_tx_key` derives the tx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for encryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for encryption is written to the
+ * buffer pointed by |hp|.
+ *
+ * |secretlen| specifies the length of |secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
+ *
+ * In the first call of this function, it calls
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as server, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_key` updates traffic keying materials.
+ *
+ * The new traffic secret for decryption is written to the buffer
+ * pointed by |rx_secret|. The length of secret is |secretlen| bytes,
+ * and |rx_secret| must point to the buffer which has enough capacity.
+ *
+ * The new traffic secret for encryption is written to the buffer
+ * pointed by |tx_secret|. The length of secret is |secretlen| bytes,
+ * and |tx_secret| must point to the buffer which has enough capacity.
+ *
+ * The derived packet protection key for decryption is written to the
+ * buffer pointed by |rx_key|. The derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|.
+ * |rx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * The derived packet protection key for encryption is written to the
+ * buffer pointed by |tx_key|. The derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|.
+ * |tx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * |current_rx_secret| and |current_tx_secret| are the current traffic
+ * secrets for decryption and encryption. |secretlen| specifies the
+ * length of |rx_secret| and |tx_secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_update_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_key_cb` is a wrapper function around
+ * `ngtcp2_crypto_update_key`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.update_key` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_client_initial_cb` installs initial secrets and
+ * encryption keys and sets QUIC transport parameters.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.client_initial` field. It is only used
+ * by client.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in
+ * response to incoming Retry packet.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.recv_retry` field. It is only used
+ * by client.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in
+ * response to an incoming Initial packet from client, and sets QUIC
+ * transport parameters.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.recv_client_initial` field. It is
+ * only used by server.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of
+ * length |datalen| in encryption level |crypto_level| and may feed
+ * outgoing CRYPTO data to |conn|. This function can drive handshake.
+ * This function can be also used after handshake completes. It is
+ * allowed to call this function with |datalen| == 0. In this case,
+ * no additional read operation is done.
+ *
+ * This function returns 0 if it succeeds, or a negative error code.
+ * The generic error code is -1 if a specific error code is not
+ * suitable. The error codes less than -10000 are specific to
+ * underlying TLS implementation. For OpenSSL, the error codes are
+ * defined in *ngtcp2_crypto_openssl.h*.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around
+ * `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed
+ * to :member:`ngtcp2_callbacks.recv_crypto_data` field.
+ *
+ * If this function is used, the TLS implementation specific error
+ * codes described in `ngtcp2_crypto_read_write_crypto_data` are
+ * treated as if it returns -1. Do not use this function if an
+ * application wishes to use the TLS implementation specific error
+ * codes.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset,
+ const uint8_t *data, size_t datalen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_stateless_reset_token` generates a
+ * stateless reset token using HKDF extraction using the given |cid|
+ * and static key |secret| as input. The token will be written to
+ * the buffer pointed by |token| and it must have a capacity of at
+ * least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_cid *cid);
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random
+ * data added to a token generated by
+ * `ngtcp2_crypto_generate_retry_token` or
+ * `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for
+ * Retry token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a
+ * token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of
+ * a token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \
+ (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \
+ sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
+ * of a token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
+ (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_retry_token` generates a token in the
+ * buffer pointed by |token| that is sent with Retry packet. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |version| is QUIC version. |remote_addr| of
+ * length |remote_addrlen| is an address of client. |retry_scid| is a
+ * Source Connection ID chosen by server and set in Retry packet.
+ * |odcid| is a Destination Connection ID in Initial packet sent by
+ * client. |ts| is the timestamp when the token is generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in
+ * the buffer pointed by |token| of length |tokenlen|. |secret| of
+ * length |secretlen| is an initial keying material to generate keys
+ * to decrypt the token. |version| is QUIC version of the Initial
+ * packet that contains this token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |dcid| is a Destination
+ * Connection ID in Initial packet sent by client. |timeout| is the
+ * period during which the token is valid. |ts| is the current
+ * timestamp. When validation succeeds, the extracted Destination
+ * Connection ID (which is the Destination Connection ID in Initial
+ * packet sent by client that triggered Retry packet) is stored to the
+ * buffer pointed by |odcid|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_regular_token` generates a token in the
+ * buffer pointed by |token| that is sent with NEW_TOKEN frame. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |remote_addr| of length |remote_addrlen| is an
+ * address of client. |ts| is the timestamp when the token is
+ * generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_regular_token` verifies a regular token
+ * stored in the buffer pointed by |token| of length |tokenlen|.
+ * |secret| of length |secretlen| is an initial keying material to
+ * generate keys to decrypt the token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |timeout| is the period
+ * during which the token is valid. |ts| is the current timestamp.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
+ const uint8_t *token, size_t tokenlen, const uint8_t *secret,
+ size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_write_connection_close` writes Initial packet
+ * containing CONNECTION_CLOSE with the given |error_code| and the
+ * optional |reason| of length |reasonlen| to the buffer pointed by
+ * |dest| of length |destlen|. This function is designed for server
+ * to close connection without committing the state when validating
+ * Retry token fails. This function must not be used by client. The
+ * |dcid| must be the Source Connection ID in Initial packet from
+ * client. The |scid| must be the Destination Connection ID in
+ * Initial packet from client. |scid| is used to derive initial
+ * keying materials.
+ *
+ * This function wraps around `ngtcp2_pkt_write_connection_close` for
+ * easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer
+ * pointed by |dest| of length |destlen|. |odcid| specifies Original
+ * Destination Connection ID. |token| specifies Retry Token, and
+ * |tokenlen| specifies its length.
+ *
+ * This function wraps around `ngtcp2_pkt_write_retry` for easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for decryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_free` frees up resources used by
+ * |aead_ctx|. This function does not free the memory pointed by
+ * |aead_ctx| itself.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field.
+ */
+NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given
+ * |cipher_ctx|.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field.
+ */
+NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable
+ * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data|
+ * which is sent with PATH_CHALLENGE frame.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.get_path_challenge_data` field.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
+ uint8_t *data,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for
+ * |version| which is negotiated or being negotiated. |client_dcid|
+ * is the destination connection ID in first Initial packet of client.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.version_negotiation` field.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data);
+
+typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_crypto_get_conn` is a callback function to get a
+ * pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation
+ * must return non-NULL :type:`ngtcp2_conn` object.
+ */
+typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)(
+ ngtcp2_crypto_conn_ref *conn_ref);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to
+ * :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as
+ * an application specific data (e.g. SSL_set_app_data in OpenSSL).
+ */
+typedef struct ngtcp2_crypto_conn_ref {
+ /**
+ * :member:`get_conn` is a callback function to get a pointer to
+ * :type:`ngtcp2_conn` object.
+ */
+ ngtcp2_crypto_get_conn get_conn;
+ /**
+ * :member:`user_data` is a pointer to arbitrary user data.
+ */
+ void *user_data;
+} ngtcp2_crypto_conn_ref;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h
new file mode 100644
index 0000000..af5503f
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h
@@ -0,0 +1,107 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_GNUTLS_H
+#define NGTCP2_CRYPTO_GNUTLS_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <gnutls/gnutls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level`
+ * translates |gtls_level| to :type:`ngtcp2_crypto_level`. This
+ * function is only available for GnuTLS backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(
+ gnutls_record_encryption_level_t gtls_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to gnutls_record_encryption_level_t. This function
+ * is only available for GnuTLS backend.
+ */
+NGTCP2_EXTERN gnutls_record_encryption_level_t
+ngtcp2_crypto_gnutls_from_ngtcp2_level(ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_configure_server_session` configures
+ * |session| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set gnutls_handshake_set_secret_function.
+ * - Set gnutls_handshake_set_read_function.
+ * - Set gnutls_alert_set_read_function.
+ * - Register a TLS extension handler for QUIC Transport Parameters.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * gnutls_session_t object by calling gnutls_session_set_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_gnutls_configure_server_session(gnutls_session_t session);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_configure_client_session` configures
+ * |session| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set gnutls_handshake_set_secret_function.
+ * - Set gnutls_handshake_set_read_function.
+ * - Set gnutls_alert_set_read_function.
+ * - Register a TLS extension handler for QUIC Transport Parameters.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * gnutls_session_t object by calling gnutls_session_set_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_gnutls_configure_client_session(gnutls_session_t session);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_GNUTLS_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/version.h b/src/contrib/libngtcp2/ngtcp2/version.h
new file mode 100644
index 0000000..54b2035
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/version.h
@@ -0,0 +1,51 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2016 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef VERSION_H
+#define VERSION_H
+
+/**
+ * @macrosection
+ *
+ * Library version macros
+ */
+
+/**
+ * @macro
+ *
+ * Version number of the ngtcp2 library release.
+ */
+#define NGTCP2_VERSION "0.13.1"
+
+/**
+ * @macro
+ *
+ * Numerical representation of the version number of the ngtcp2
+ * library release. This is a 24 bit number with 8 bits for major
+ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
+ * becomes 0x010203.
+ */
+#define NGTCP2_VERSION_NUM 0x000d01
+
+#endif /* VERSION_H */
diff --git a/src/contrib/licenses/0BSD b/src/contrib/licenses/0BSD
new file mode 100644
index 0000000..56c5528
--- /dev/null
+++ b/src/contrib/licenses/0BSD
@@ -0,0 +1,12 @@
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
diff --git a/src/contrib/licenses/BSD-3-Clause b/src/contrib/licenses/BSD-3-Clause
new file mode 100644
index 0000000..8041f21
--- /dev/null
+++ b/src/contrib/licenses/BSD-3-Clause
@@ -0,0 +1,21 @@
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/contrib/licenses/LGPL-2.0 b/src/contrib/licenses/LGPL-2.0
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/src/contrib/licenses/LGPL-2.0
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 2 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/src/contrib/licenses/LGPL-2.1 b/src/contrib/licenses/LGPL-2.1
new file mode 100644
index 0000000..27bb434
--- /dev/null
+++ b/src/contrib/licenses/LGPL-2.1
@@ -0,0 +1,503 @@
+Valid-License-Identifier: LGPL-2.1
+Valid-License-Identifier: LGPL-2.1+
+SPDX-URL: https://spdx.org/licenses/LGPL-2.1.html
+Usage-Guide:
+ To use this license in source code, put one of the following SPDX
+ tag/value pairs into a comment according to the placement
+ guidelines in the licensing rules documentation.
+ For 'GNU Lesser General Public License (LGPL) version 2.1 only' use:
+ SPDX-License-Identifier: LGPL-2.1
+ For 'GNU Lesser General Public License (LGPL) version 2.1 or any later
+ version' use:
+ SPDX-License-Identifier: LGPL-2.1+
+License-Text:
+
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 2.1, February 1999
+
+Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts as
+the successor of the GNU Library Public License, version 2, hence the
+version number 2.1.]
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public Licenses are
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users.
+
+This license, the Lesser General Public License, applies to some specially
+designated software packages--typically libraries--of the Free Software
+Foundation and other authors who decide to use it. You can use it too, but
+we suggest you first think carefully about whether this license or the
+ordinary General Public License is the better strategy to use in any
+particular case, based on the explanations below.
+
+When we speak of free software, we are referring to freedom of use, not
+price. Our General Public Licenses are designed to make sure that you have
+the freedom to distribute copies of free software (and charge for this
+service if you wish); that you receive source code or can get it if you
+want it; that you can change the software and use pieces of it in new free
+programs; and that you are informed that you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for you if
+you distribute copies of the library or if you modify it.
+
+For example, if you distribute copies of the library, whether gratis or for
+a fee, you must give the recipients all the rights that we gave you. You
+must make sure that they, too, receive or can get the source code. If you
+link other code with the library, you must provide complete object files to
+the recipients, so that they can relink them with the library after making
+changes to the library and recompiling it. And you must show them these
+terms so they know their rights.
+
+We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+To protect each distributor, we want to make it very clear that there is no
+warranty for the free library. Also, if the library is modified by someone
+else and passed on, the recipients should know that what they have is not
+the original version, so that the original author's reputation will not be
+affected by problems that might be introduced by others.
+
+Finally, software patents pose a constant threat to the existence of any
+free program. We wish to make sure that a company cannot effectively
+restrict the users of a free program by obtaining a restrictive license
+from a patent holder. Therefore, we insist that any patent license obtained
+for a version of the library must be consistent with the full freedom of
+use specified in this license.
+
+Most GNU software, including some libraries, is covered by the ordinary GNU
+General Public License. This license, the GNU Lesser General Public
+License, applies to certain designated libraries, and is quite different
+from the ordinary General Public License. We use this license for certain
+libraries in order to permit linking those libraries into non-free
+programs.
+
+When a program is linked with a library, whether statically or using a
+shared library, the combination of the two is legally speaking a combined
+work, a derivative of the original library. The ordinary General Public
+License therefore permits such linking only if the entire combination fits
+its criteria of freedom. The Lesser General Public License permits more lax
+criteria for linking other code with the library.
+
+We call this license the "Lesser" General Public License because it does
+Less to protect the user's freedom than the ordinary General Public
+License. It also provides other free software developers Less of an
+advantage over competing non-free programs. These disadvantages are the
+reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+For example, on rare occasions, there may be a special need to encourage
+the widest possible use of a certain library, so that it becomes a de-facto
+standard. To achieve this, non-free programs must be allowed to use the
+library. A more frequent case is that a free library does the same job as
+widely used non-free libraries. In this case, there is little to gain by
+limiting the free library to free software only, so we use the Lesser
+General Public License.
+
+In other cases, permission to use a particular library in non-free programs
+enables a greater number of people to use a large body of free
+software. For example, permission to use the GNU C Library in non-free
+programs enables many more people to use the whole GNU operating system, as
+well as its variant, the GNU/Linux operating system.
+
+Although the Lesser General Public License is Less protective of the users'
+freedom, it does ensure that the user of a program that is linked with the
+Library has the freedom and the wherewithal to run that program using a
+modified version of the Library.
+
+The precise terms and conditions for copying, distribution and modification
+follow. Pay close attention to the difference between a "work based on the
+library" and a "work that uses the library". The former contains code
+derived from the library, whereas the latter must be combined with the
+library in order to run.
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License Agreement applies to any software library or other program
+ which contains a notice placed by the copyright holder or other
+ authorized party saying it may be distributed under the terms of this
+ Lesser General Public License (also called "this License"). Each
+ licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+ prepared so as to be conveniently linked with application programs
+ (which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work which
+ has been distributed under these terms. A "work based on the Library"
+ means either the Library or any derivative work under copyright law:
+ that is to say, a work containing the Library or a portion of it, either
+ verbatim or with modifications and/or translated straightforwardly into
+ another language. (Hereinafter, translation is included without
+ limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for making
+ modifications to it. For a library, complete source code means all the
+ source code for all modules it contains, plus any associated interface
+ definition files, plus the scripts used to control compilation and
+ installation of the library.
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of running
+ a program using the Library is not restricted, and output from such a
+ program is covered only if its contents constitute a work based on the
+ Library (independent of the use of the Library in a tool for writing
+ it). Whether that is true depends on what the Library does and what the
+ program that uses the Library does.
+
+1. You may copy and distribute verbatim copies of the Library's complete
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an appropriate
+ copyright notice and disclaimer of warranty; keep intact all the notices
+ that refer to this License and to the absence of any warranty; and
+ distribute a copy of this License along with the Library.
+
+ You may charge a fee for the physical act of transferring a copy, and
+ you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Library or any portion of it,
+ thus forming a work based on the Library, and copy and distribute such
+ modifications or work under the terms of Section 1 above, provided that
+ you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices stating
+ that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no charge to
+ all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a table
+ of data to be supplied by an application program that uses the
+ facility, other than as an argument passed when the facility is
+ invoked, then you must make a good faith effort to ensure that, in
+ the event an application does not supply such function or table, the
+ facility still operates, and performs whatever part of its purpose
+ remains meaningful.
+
+ (For example, a function in a library to compute square roots has a
+ purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must be
+ optional: if the application does not supply it, the square root
+ function must still compute square roots.)
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the Library, and
+ can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works. But when you
+ distribute the same sections as part of a whole which is a work based on
+ the Library, the distribution of the whole must be on the terms of this
+ License, whose permissions for other licensees extend to the entire
+ whole, and thus to each and every part regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Library.
+
+ In addition, mere aggregation of another work not based on the Library
+ with the Library (or with a work based on the Library) on a volume of a
+ storage or distribution medium does not bring the other work under the
+ scope of this License.
+
+3. You may opt to apply the terms of the ordinary GNU General Public
+ License instead of this License to a given copy of the Library. To do
+ this, you must alter all the notices that refer to this License, so that
+ they refer to the ordinary GNU General Public License, version 2,
+ instead of to this License. (If a newer version than version 2 of the
+ ordinary GNU General Public License has appeared, then you can specify
+ that version instead if you wish.) Do not make any other change in these
+ notices.
+
+ Once this change is made in a given copy, it is irreversible for that
+ copy, so the ordinary GNU General Public License applies to all
+ subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of the
+ Library into a program that is not a library.
+
+4. You may copy and distribute the Library (or a portion or derivative of
+ it, under Section 2) in object code or executable form under the terms
+ of Sections 1 and 2 above provided that you accompany it with the
+ complete corresponding machine-readable source code, which must be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy from a
+ designated place, then offering equivalent access to copy the source
+ code from the same place satisfies the requirement to distribute the
+ source code, even though third parties are not compelled to copy the
+ source along with the object code.
+
+5. A program that contains no derivative of any portion of the Library, but
+ is designed to work with the Library by being compiled or linked with
+ it, is called a "work that uses the Library". Such a work, in isolation,
+ is not a derivative work of the Library, and therefore falls outside the
+ scope of this License.
+
+ However, linking a "work that uses the Library" with the Library creates
+ an executable that is a derivative of the Library (because it contains
+ portions of the Library), rather than a "work that uses the
+ library". The executable is therefore covered by this License. Section 6
+ states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+ that is part of the Library, the object code for the work may be a
+ derivative work of the Library even though the source code is
+ not. Whether this is true is especially significant if the work can be
+ linked without the Library, or if the work is itself a library. The
+ threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data structure
+ layouts and accessors, and small macros and small inline functions (ten
+ lines or less in length), then the use of the object file is
+ unrestricted, regardless of whether it is legally a derivative
+ work. (Executables containing this object code plus portions of the
+ Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+ distribute the object code for the work under the terms of Section
+ 6. Any executables containing that work also fall under Section 6,
+ whether or not they are linked directly with the Library itself.
+
+6. As an exception to the Sections above, you may also combine or link a
+ "work that uses the Library" with the Library to produce a work
+ containing portions of the Library, and distribute that work under terms
+ of your choice, provided that the terms permit modification of the work
+ for the customer's own use and reverse engineering for debugging such
+ modifications.
+
+ You must give prominent notice with each copy of the work that the
+ Library is used in it and that the Library and its use are covered by
+ this License. You must supply a copy of this License. If the work during
+ execution displays copyright notices, you must include the copyright
+ notice for the Library among them, as well as a reference directing the
+ user to the copy of this License. Also, you must do one of these things:
+
+ a) Accompany the work with the complete corresponding machine-readable
+ source code for the Library including whatever changes were used in
+ the work (which must be distributed under Sections 1 and 2 above);
+ and, if the work is an executable linked with the Library, with the
+ complete machine-readable "work that uses the Library", as object
+ code and/or source code, so that the user can modify the Library and
+ then relink to produce a modified executable containing the modified
+ Library. (It is understood that the user who changes the contents of
+ definitions files in the Library will not necessarily be able to
+ recompile the application to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a copy
+ of the library already present on the user's computer system, rather
+ than copying library functions into the executable, and (2) will
+ operate properly with a modified version of the library, if the user
+ installs one, as long as the modified version is interface-compatible
+ with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at least three
+ years, to give the same user the materials specified in Subsection
+ 6a, above, for a charge no more than the cost of performing this
+ distribution.
+
+ d) If distribution of the work is made by offering access to copy from a
+ designated place, offer equivalent access to copy the above specified
+ materials from the same place.
+
+ e) Verify that the user has already received a copy of these materials
+ or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the Library"
+ must include any data and utility programs needed for reproducing the
+ executable from it. However, as a special exception, the materials to be
+ distributed need not include anything that is normally distributed (in
+ either source or binary form) with the major components (compiler,
+ kernel, and so on) of the operating system on which the executable runs,
+ unless that component itself accompanies the executable.
+
+ It may happen that this requirement contradicts the license restrictions
+ of other proprietary libraries that do not normally accompany the
+ operating system. Such a contradiction means you cannot use both them
+ and the Library together in an executable that you distribute.
+
+7. You may place library facilities that are a work based on the Library
+ side-by-side in a single library together with other library facilities
+ not covered by this License, and distribute such a combined library,
+ provided that the separate distribution of the work based on the Library
+ and of the other library facilities is otherwise permitted, and provided
+ that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work based on
+ the Library, uncombined with any other library facilities. This must
+ be distributed under the terms of the Sections above.
+
+ b) Give prominent notice with the combined library of the fact that part
+ of it is a work based on the Library, and explaining where to find
+ the accompanying uncombined form of the same work.
+
+8. You may not copy, modify, sublicense, link with, or distribute the
+ Library except as expressly provided under this License. Any attempt
+ otherwise to copy, modify, sublicense, link with, or distribute the
+ Library is void, and will automatically terminate your rights under this
+ License. However, parties who have received copies, or rights, from you
+ under this License will not have their licenses terminated so long as
+ such parties remain in full compliance.
+
+9. You are not required to accept this License, since you have not signed
+ it. However, nothing else grants you permission to modify or distribute
+ the Library or its derivative works. These actions are prohibited by law
+ if you do not accept this License. Therefore, by modifying or
+ distributing the Library (or any work based on the Library), you
+ indicate your acceptance of this License to do so, and all its terms and
+ conditions for copying, distributing or modifying the Library or works
+ based on it.
+
+10. Each time you redistribute the Library (or any work based on the
+ Library), the recipient automatically receives a license from the
+ original licensor to copy, distribute, link with or modify the Library
+ subject to these terms and conditions. You may not impose any further
+ restrictions on the recipients' exercise of the rights granted
+ herein. You are not responsible for enforcing compliance by third
+ parties with this License.
+
+11. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot
+ distribute so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you
+ may not distribute the Library at all. For example, if a patent license
+ would not permit royalty-free redistribution of the Library by all
+ those who receive copies directly or indirectly through you, then the
+ only way you could satisfy both it and this License would be to refrain
+ entirely from distribution of the Library.
+
+ If any portion of this section is held invalid or unenforceable under
+ any particular circumstance, the balance of the section is intended to
+ apply, and the section as a whole is intended to apply in other
+ circumstances.
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system which is implemented
+ by public license practices. Many people have made generous
+ contributions to the wide range of software distributed through that
+ system in reliance on consistent application of that system; it is up
+ to the author/donor to decide if he or she is willing to distribute
+ software through any other system and a licensee cannot impose that
+ choice.
+
+ This section is intended to make thoroughly clear what is believed to
+ be a consequence of the rest of this License.
+
+12. If the distribution and/or use of the Library is restricted in certain
+ countries either by patents or by copyrighted interfaces, the original
+ copyright holder who places the Library under this License may add an
+ explicit geographical distribution limitation excluding those
+ countries, so that distribution is permitted only in or among countries
+ not thus excluded. In such case, this License incorporates the
+ limitation as if written in the body of this License.
+
+13. The Free Software Foundation may publish revised and/or new versions of
+ the Lesser General Public License from time to time. Such new versions
+ will be similar in spirit to the present version, but may differ in
+ detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the Library
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and
+ conditions either of that version or of any later version published by
+ the Free Software Foundation. If the Library does not specify a license
+ version number, you may choose any version ever published by the Free
+ Software Foundation.
+
+14. If you wish to incorporate parts of the Library into other free
+ programs whose distribution conditions are incompatible with these,
+ write to the author to ask for permission. For software which is
+ copyrighted by the Free Software Foundation, write to the Free Software
+ Foundation; we sometimes make exceptions for this. Our decision will be
+ guided by the two goals of preserving the free status of all
+ derivatives of our free software and of promoting the sharing and reuse
+ of software generally.
+
+NO WARRANTY
+
+15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
+ EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH
+ YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+ NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+ DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY
+ (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+ INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+ THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR
+ OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Libraries
+
+If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+one line to give the library's name and an idea of what it does.
+Copyright (C) year name of author
+
+This library is free software; you can redistribute it and/or modify it
+under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at
+your option) any later version.
+
+This library 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 Lesser General Public License
+for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this library; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add
+information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in
+the library `Frob' (a library for tweaking knobs) written
+by James Random Hacker.
+
+signature of Ty Coon, 1 April 1990
+Ty Coon, President of Vice
+That's all there is to it!
diff --git a/src/contrib/licenses/MIT b/src/contrib/licenses/MIT
new file mode 100644
index 0000000..fb122a0
--- /dev/null
+++ b/src/contrib/licenses/MIT
@@ -0,0 +1,19 @@
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/contrib/macros.h b/src/contrib/macros.h
new file mode 100644
index 0000000..78df47e
--- /dev/null
+++ b/src/contrib/macros.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2021 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 Common macros.
+ */
+
+#pragma once
+
+#ifndef MIN
+/*! \brief Type-safe minimum macro. */
+#define MIN(a, b) \
+ ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
+
+/*! \brief Type-safe maximum macro. */
+#define MAX(a, b) \
+ ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
+#endif
+
+#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
diff --git a/src/contrib/mempattern.c b/src/contrib/mempattern.c
new file mode 100644
index 0000000..f57139d
--- /dev/null
+++ b/src/contrib/mempattern.c
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 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 <stdlib.h>
+
+#include "contrib/mempattern.h"
+#include "contrib/string.h"
+#include "contrib/ucw/mempool.h"
+
+static void mm_nofree(void *p)
+{
+ /* nop */
+}
+
+static void *mm_malloc(void *ctx, size_t n)
+{
+ (void)ctx;
+ return malloc(n);
+}
+
+void *mm_alloc(knot_mm_t *mm, size_t size)
+{
+ if (mm) {
+ return mm->alloc(mm->ctx, size);
+ } else {
+ return malloc(size);
+ }
+}
+
+void *mm_calloc(knot_mm_t *mm, size_t nmemb, size_t size)
+{
+ if (nmemb == 0 || size == 0) {
+ return NULL;
+ }
+ if (mm) {
+ size_t total_size = nmemb * size;
+ if (total_size / nmemb != size) { // Overflow check
+ return NULL;
+ }
+ void *mem = mm_alloc(mm, total_size);
+ if (mem == NULL) {
+ return NULL;
+ }
+ return memzero(mem, total_size);
+ } else {
+ return calloc(nmemb, size);
+ }
+}
+
+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);
+ }
+}
+
+char *mm_strdup(knot_mm_t *mm, const char *s)
+{
+ if (s == NULL) {
+ return NULL;
+ }
+ if (mm) {
+ size_t len = strlen(s) + 1;
+ void *mem = mm_alloc(mm, len);
+ if (mem == NULL) {
+ return NULL;
+ }
+ return memcpy(mem, s, len);
+ } else {
+ return strdup(s);
+ }
+}
+
+void mm_free(knot_mm_t *mm, void *what)
+{
+ if (mm) {
+ if (mm->free) {
+ mm->free(what);
+ }
+ } else {
+ free(what);
+ }
+}
+
+void mm_ctx_init(knot_mm_t *mm)
+{
+ mm->ctx = NULL;
+ mm->alloc = mm_malloc;
+ mm->free = free;
+}
+
+void mm_ctx_mempool(knot_mm_t *mm, size_t chunk_size)
+{
+ mm->ctx = mp_new(chunk_size);
+ mm->alloc = (knot_mm_alloc_t)mp_alloc;
+ mm->free = mm_nofree;
+}
diff --git a/src/contrib/mempattern.h b/src/contrib/mempattern.h
new file mode 100644
index 0000000..c74f983
--- /dev/null
+++ b/src/contrib/mempattern.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2018 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 Memory allocation related functions.
+ */
+
+#pragma once
+
+#include "libknot/mm_ctx.h"
+
+/*! \brief Default memory block size. */
+#define MM_DEFAULT_BLKSIZE 4096
+
+/*! \brief Allocs using 'mm' if any, uses system malloc() otherwise. */
+void *mm_alloc(knot_mm_t *mm, size_t size);
+
+/*! \brief Callocs using 'mm' if any, uses system calloc() otherwise. */
+void *mm_calloc(knot_mm_t *mm, size_t nmemb, size_t size);
+
+/*! \brief Reallocs using 'mm' if any, uses system realloc() otherwise. */
+void *mm_realloc(knot_mm_t *mm, void *what, size_t size, size_t prev_size);
+
+/*! \brief Strdups using 'mm' if any, uses system strdup() otherwise. */
+char *mm_strdup(knot_mm_t *mm, const char *s);
+
+/*! \brief Free using 'mm' if any, uses system free() otherwise. */
+void mm_free(knot_mm_t *mm, void *what);
+
+/*! \brief Initialize default memory allocation context. */
+void mm_ctx_init(knot_mm_t *mm);
+
+/*! \brief Memory pool context. */
+void mm_ctx_mempool(knot_mm_t *mm, size_t chunk_size);
diff --git a/src/contrib/musl/inet_ntop.c b/src/contrib/musl/inet_ntop.c
new file mode 100644
index 0000000..1b72a61
--- /dev/null
+++ b/src/contrib/musl/inet_ntop.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/openbsd/strlcpy.h"
+
+const char *knot_inet_ntop(int af, const void *restrict a0, char *restrict s, socklen_t l)
+{
+ const unsigned char *a = a0;
+ int i, j, max, best;
+ char buf[100];
+
+ switch (af) {
+ case AF_INET:
+ if (snprintf(s, l, "%d.%d.%d.%d", a[0],a[1],a[2],a[3]) < l)
+ return s;
+ break;
+ case AF_INET6:
+ if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377", 12))
+ (void)snprintf(buf, sizeof buf,
+ "%x:%x:%x:%x:%x:%x:%x:%x",
+ 256*a[0]+a[1],256*a[2]+a[3],
+ 256*a[4]+a[5],256*a[6]+a[7],
+ 256*a[8]+a[9],256*a[10]+a[11],
+ 256*a[12]+a[13],256*a[14]+a[15]);
+ else
+ (void)snprintf(buf, sizeof buf,
+ "%x:%x:%x:%x:%x:%x:%d.%d.%d.%d",
+ 256*a[0]+a[1],256*a[2]+a[3],
+ 256*a[4]+a[5],256*a[6]+a[7],
+ 256*a[8]+a[9],256*a[10]+a[11],
+ a[12],a[13],a[14],a[15]);
+ /* Replace longest /(^0|:)[:0]{2,}/ with "::" */
+ for (i=best=0, max=2; buf[i]; i++) {
+ if (i && buf[i] != ':') continue;
+ j = strspn(buf+i, ":0");
+ if (j>max) best=i, max=j;
+ }
+ if (max>3) {
+ buf[best] = buf[best+1] = ':';
+ memmove(buf+best+2, buf+best+max, i-best-max+1);
+ }
+ if (strlen(buf) < l) {
+ strlcpy(s, buf, l);
+ return s;
+ }
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ return 0;
+ }
+ errno = ENOSPC;
+ return 0;
+}
diff --git a/src/contrib/musl/inet_ntop.h b/src/contrib/musl/inet_ntop.h
new file mode 100644
index 0000000..73faaee
--- /dev/null
+++ b/src/contrib/musl/inet_ntop.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+
+const char *knot_inet_ntop(int af, const void *restrict a0, char *restrict s, socklen_t l);
diff --git a/src/contrib/net.c b/src/contrib/net.c
new file mode 100644
index 0000000..3de4a71
--- /dev/null
+++ b/src/contrib/net.c
@@ -0,0 +1,747 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h> // OpenBSD
+#include <netinet/tcp.h> // TCP_FASTOPEN
+#include <netinet/in.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "libknot/errcode.h"
+#include "contrib/macros.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+
+/*!
+ * \brief Enable socket option.
+ */
+static int sockopt_enable(int sock, int level, int optname)
+{
+ const int enable = 1;
+ if (setsockopt(sock, level, optname, &enable, sizeof(enable)) != 0) {
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Create a non-blocking socket.
+ *
+ * Prefer SOCK_NONBLOCK if available to save one fcntl() syscall.
+ *
+ */
+static int socket_create(int family, int type, int proto)
+{
+#ifdef SOCK_NONBLOCK
+ type |= SOCK_NONBLOCK;
+#endif
+ int sock = socket(family, type, proto);
+ if (sock < 0) {
+ return knot_map_errno();
+ }
+
+#ifndef SOCK_NONBLOCK
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) {
+ int ret = knot_map_errno();
+ close(sock);
+ return ret;
+ }
+#endif
+
+/*
+ * OS X doesn't support MSG_NOSIGNAL. Use SO_NOSIGPIPE socket option instead.
+ */
+#if defined(__APPLE__) && !defined(MSG_NOSIGNAL)
+#define MSG_NOSIGNAL 0
+ int ret = sockopt_enable(sock, SOL_SOCKET, SO_NOSIGPIPE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+#endif
+
+ return sock;
+}
+
+int net_unbound_socket(int type, const struct sockaddr_storage *addr)
+{
+ if (addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Create socket. */
+ return socket_create(addr->ss_family, type, 0);
+}
+
+struct option {
+ int level;
+ int name;
+};
+
+/*!
+ * \brief Get setsock option for binding non-local address.
+ */
+static const struct option *nonlocal_option(int family)
+{
+ static const struct option ipv4 = {
+ #if defined(IP_FREEBIND)
+ IPPROTO_IP, IP_FREEBIND
+ #elif defined(IP_BINDANY)
+ IPPROTO_IP, IP_BINDANY
+ #else
+ 0, 0
+ #endif
+ };
+
+ static const struct option ipv6 = {
+ #if defined(IP_FREEBIND)
+ IPPROTO_IP, IP_FREEBIND
+ #elif defined(IPV6_BINDANY)
+ IPPROTO_IPV6, IPV6_BINDANY
+ #else
+ 0, 0
+ #endif
+
+ };
+
+ switch (family) {
+ case AF_INET: return &ipv4;
+ case AF_INET6: return &ipv6;
+ default:
+ return NULL;
+ }
+}
+
+static int enable_nonlocal(int sock, int family)
+{
+ const struct option *opt = nonlocal_option(family);
+ if (opt == NULL || opt->name == 0) {
+ return KNOT_ENOTSUP;
+ }
+
+ return sockopt_enable(sock, opt->level, opt->name);
+}
+
+static int enable_reuseport(int sock)
+{
+#ifdef ENABLE_REUSEPORT
+# if defined(__FreeBSD__)
+ return sockopt_enable(sock, SOL_SOCKET, SO_REUSEPORT_LB);
+# else
+ return sockopt_enable(sock, SOL_SOCKET, SO_REUSEPORT);
+# endif
+#else
+ return KNOT_ENOTSUP;
+#endif
+}
+
+static void unlink_unix_socket(const struct sockaddr_storage *addr)
+{
+ char path[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(path, sizeof(path), addr);
+ unlink(path);
+}
+
+int net_bound_socket(int type, const struct sockaddr_storage *addr,
+ net_bind_flag_t flags, mode_t unix_mode)
+{
+ /* Create socket. */
+ int sock = net_unbound_socket(type, addr);
+ if (sock < 0) {
+ return sock;
+ }
+
+ /* Unlink UNIX sock if exists. */
+ if (addr->ss_family == AF_UNIX) {
+ unlink_unix_socket(addr);
+ }
+
+ /* Reuse old address if taken. */
+ int ret = sockopt_enable(sock, SOL_SOCKET, SO_REUSEADDR);
+ if (ret != KNOT_EOK) {
+ close(sock);
+ return ret;
+ }
+
+#if defined(__linux__)
+ /* Set MSS (Maximum Segment Size) limit. */
+ if (addr->ss_family != AF_UNIX && type == SOCK_STREAM) {
+ const int mss = KNOT_TCP_MSS;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss)) != 0) {
+ ret = knot_map_errno();
+ close(sock);
+ return ret;
+ }
+ }
+#endif
+
+ /* Don't bind IPv4 for IPv6 any address. */
+ if (addr->ss_family == AF_INET6) {
+ ret = sockopt_enable(sock, IPPROTO_IPV6, IPV6_V6ONLY);
+ if (ret != KNOT_EOK) {
+ close(sock);
+ return ret;
+ }
+ }
+
+ /* Allow bind to non-local address. */
+ if (flags & NET_BIND_NONLOCAL) {
+ ret = enable_nonlocal(sock, addr->ss_family);
+ if (ret != KNOT_EOK) {
+ close(sock);
+ return ret;
+ }
+ }
+
+ /* Allow to bind the same address by multiple threads. */
+ if (flags & NET_BIND_MULTIPLE) {
+ ret = enable_reuseport(sock);
+ if (ret != KNOT_EOK) {
+ close(sock);
+ return ret;
+ }
+ }
+
+ /* Bind to specified address. */
+ ret = bind(sock, (const struct sockaddr *)addr, sockaddr_len(addr));
+ if (ret < 0) {
+ ret = knot_map_errno();
+ close(sock);
+ return ret;
+ }
+
+ if (addr->ss_family == AF_UNIX && unix_mode != 0) {
+ const char *path = ((const struct sockaddr_un *)addr)->sun_path;
+ if (chmod(path, unix_mode) != 0) {
+ ret = knot_map_errno();
+ close(sock);
+ return ret;
+ }
+ }
+
+ return sock;
+}
+
+static int tfo_connect(int sock, const struct sockaddr_storage *addr)
+{
+#if defined(__linux__)
+ /* connect() will be called implicitly with sendmsg(). */
+ return KNOT_EOK;
+#elif defined(__FreeBSD__)
+ return sockopt_enable(sock, IPPROTO_TCP, TCP_FASTOPEN);
+#elif defined(__APPLE__)
+ /* Connection is performed lazily when first data is sent. */
+ sa_endpoints_t ep = {
+ .sae_dstaddr = (const struct sockaddr *)addr,
+ .sae_dstaddrlen = sockaddr_len(addr)
+ };
+ int flags = CONNECT_DATA_IDEMPOTENT | CONNECT_RESUME_ON_READ_WRITE;
+
+ int ret = connectx(sock, &ep, SAE_ASSOCID_ANY, flags, NULL, 0, NULL, NULL);
+ return (ret == 0 ? KNOT_EOK : knot_map_errno());
+#else
+ return KNOT_ENOTSUP;
+#endif
+}
+
+int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
+ const struct sockaddr_storage *src_addr, bool tfo)
+{
+ if (dst_addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Check port. */
+ if (sockaddr_port(dst_addr) == 0) {
+ return KNOT_NET_EADDR;
+ }
+
+ /* Bind to specific source address - if set. */
+ int sock = -1;
+ if (src_addr && src_addr->ss_family != AF_UNSPEC) {
+ sock = net_bound_socket(type, src_addr, 0, 0);
+ } else {
+ sock = net_unbound_socket(type, dst_addr);
+ }
+ if (sock < 0) {
+ return sock;
+ }
+
+ /* Connect to destination. */
+ if (tfo && net_is_stream(sock)) {
+ int ret = tfo_connect(sock, dst_addr);
+ if (ret != KNOT_EOK) {
+ close(sock);
+ return ret;
+ }
+ } else {
+ int ret = connect(sock, (const struct sockaddr *)dst_addr,
+ sockaddr_len(dst_addr));
+ if (ret != 0 && errno != EINPROGRESS) {
+ ret = knot_map_errno();
+ close(sock);
+ return ret;
+ }
+ }
+
+ return sock;
+}
+
+int net_bound_tfo(int sock, int backlog)
+{
+#if defined(TCP_FASTOPEN)
+#if defined(__APPLE__)
+ if (backlog > 0) {
+ backlog = 1; // just on-off switch on macOS
+ }
+#endif
+ if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) != 0) {
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+#endif
+ return KNOT_ENOTSUP;
+}
+
+bool net_is_connected(int sock)
+{
+ struct sockaddr_storage addr;
+ socklen_t len = sizeof(addr);
+ return (getpeername(sock, (struct sockaddr *)&addr, &len) == 0);
+}
+
+int net_socktype(int sock)
+{
+ int type;
+ socklen_t size = sizeof(type);
+
+ if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &type, &size) == 0) {
+ return type;
+ } else {
+ return AF_UNSPEC;
+ }
+}
+
+bool net_is_stream(int sock)
+{
+ return net_socktype(sock) == SOCK_STREAM;
+}
+
+int net_accept(int sock, struct sockaddr_storage *addr)
+{
+ socklen_t len = sizeof(*addr);
+ socklen_t *addr_len = (addr != NULL) ? &len : NULL;
+
+ int remote = -1;
+
+#if defined(HAVE_ACCEPT4) && defined(SOCK_NONBLOCK)
+ remote = accept4(sock, (struct sockaddr *)addr, addr_len, SOCK_NONBLOCK);
+ if (remote < 0) {
+ return knot_map_errno();
+ }
+#else
+ remote = accept(sock, (struct sockaddr *)addr, addr_len);
+ if (fcntl(remote, F_SETFL, O_NONBLOCK) != 0) {
+ int error = knot_map_errno();
+ close(remote);
+ return error;
+ }
+#endif
+
+ return remote;
+}
+
+void net_reset(int sock)
+{
+ struct sockaddr unspec = { .sa_family = AF_UNSPEC };
+ (void)connect(sock, &unspec, sizeof(unspec));
+}
+
+/* -- I/O interface handling partial -------------------------------------- */
+
+/*!
+ * \brief Perform \a poll() on one socket.
+ */
+static int poll_one(int fd, int events, int timeout_ms)
+{
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = events
+ };
+
+ return poll(&pfd, 1, timeout_ms);
+}
+
+/*!
+ * \brief Check if we should wait for I/O readiness.
+ *
+ * \param error \a errno set by the failed I/O operation.
+ */
+static bool io_should_wait(int error)
+{
+ if (error == EAGAIN || error == EWOULDBLOCK || /* Socket data not ready. */
+ error == ENOMEM || error == ENOBUFS) { /* Insufficient resources. */
+ return true;
+ }
+
+#ifndef __linux__
+ /* FreeBSD: connection in progress. */
+ if (error == ENOTCONN) {
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+/*!
+ * \brief Check if we should wait again.
+ *
+ * \param error \a errno set by the failed wait operation.
+ */
+static bool wait_should_retry(int error)
+{
+ if (error == EINTR || /* System call interrupted. */
+ error == EAGAIN || error == ENOMEM) { /* Insufficient resources. */
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * \brief I/O operation callbacks.
+ */
+struct io {
+ ssize_t (*process)(int sockfd, struct msghdr *msg, int timeout_ms);
+ int (*wait)(int sockfd, int timeout_ms);
+};
+
+/*!
+ * \brief Get total size of I/O vector in a message.
+ */
+static size_t msg_iov_len(const struct msghdr *msg)
+{
+ size_t total = 0;
+
+ for (int i = 0; i < msg->msg_iovlen; i++) {
+ total += msg->msg_iov[i].iov_len;
+ }
+
+ return total;
+}
+
+/*!
+ * \brief Shift processed data out of message IO vectors.
+ */
+static void msg_iov_shift(struct msghdr *msg, size_t done)
+{
+ struct iovec *iov = msg->msg_iov;
+ int iovlen = msg->msg_iovlen;
+
+ for (int i = 0; i < iovlen && done > 0; i++) {
+ if (iov[i].iov_len > done) {
+ iov[i].iov_base += done;
+ iov[i].iov_len -= done;
+ done = 0;
+ } else {
+ done -= iov[i].iov_len;
+ msg->msg_iov += 1;
+ msg->msg_iovlen -= 1;
+ }
+ }
+
+ assert(done == 0);
+}
+
+#define TIMEOUT_CTX_INIT \
+ struct timespec begin, end; \
+ if (*timeout_ptr > 0) { \
+ clock_gettime(CLOCK_MONOTONIC, &begin); \
+ }
+
+#define TIMEOUT_CTX_UPDATE \
+ if (*timeout_ptr > 0) { \
+ clock_gettime(CLOCK_MONOTONIC, &end); \
+ int running_ms = time_diff_ms(&begin, &end); \
+ *timeout_ptr = MAX(*timeout_ptr - running_ms, 0); \
+ }
+
+/*!
+ * \brief Perform an I/O operation with a socket with waiting.
+ *
+ * \param oneshot If set, doesn't wait until the buffer is fully processed.
+ */
+static ssize_t io_exec(const struct io *io, int fd, struct msghdr *msg,
+ bool oneshot, int *timeout_ptr)
+{
+ size_t done = 0;
+ size_t total = msg_iov_len(msg);
+
+ for (;;) {
+ /* Perform I/O. */
+ ssize_t ret = io->process(fd, msg, *timeout_ptr);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ }
+ if (ret > 0) {
+ done += ret;
+ if (oneshot || done == total) {
+ break;
+ }
+ msg_iov_shift(msg, ret);
+ }
+
+ /* Wait for data readiness. */
+ if (ret > 0 || (ret == -1 && io_should_wait(errno))) {
+ for (;;) {
+ TIMEOUT_CTX_INIT
+
+ ret = io->wait(fd, *timeout_ptr);
+
+ if (ret == 1) {
+ TIMEOUT_CTX_UPDATE
+ /* Ready, retry process. */
+ break;
+ } else if (ret == -1 && wait_should_retry(errno)) {
+ TIMEOUT_CTX_UPDATE
+ /* Interrupted or transient error, continue waiting. */
+ continue;
+ } else if (ret == 0) {
+ /* Timed out, exit. */
+ return KNOT_ETIMEOUT;
+ } else {
+ /* In specific circumstances with Valgrind,
+ * poll() returns wrong value.
+ */
+ assert(ret <= 1);
+ assert(ret >= -1);
+ /* Other error, exit. */
+ return KNOT_ECONN;
+ }
+ }
+ } else {
+ /* Disconnect or error. */
+ return KNOT_ECONN;
+ }
+ }
+
+ return done;
+}
+
+static ssize_t recv_process(int fd, struct msghdr *msg, int timeout_ms)
+{
+ return recvmsg(fd, msg, MSG_DONTWAIT | MSG_NOSIGNAL);
+}
+
+static int recv_wait(int fd, int timeout_ms)
+{
+ return poll_one(fd, POLLIN, timeout_ms);
+}
+
+static ssize_t recv_data(int sock, struct msghdr *msg, bool oneshot, int *timeout_ptr)
+{
+ static const struct io RECV_IO = {
+ .process = recv_process,
+ .wait = recv_wait
+ };
+
+ return io_exec(&RECV_IO, sock, msg, oneshot, timeout_ptr);
+}
+
+static ssize_t send_process_tfo(int fd, struct msghdr *msg, int timeout_ms)
+{
+#if defined(__linux__)
+ int ret = sendmsg(fd, msg, MSG_FASTOPEN);
+ if (ret != 0 && errno == EINPROGRESS) {
+ if (poll_one(fd, POLLOUT, timeout_ms) != 1) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ ret = sendmsg(fd, msg, MSG_NOSIGNAL);
+ }
+ return ret;
+#else
+ return sendmsg(fd, msg, MSG_NOSIGNAL);
+#endif
+}
+
+static ssize_t send_process(int fd, struct msghdr *msg, int timeout_ms)
+{
+ return sendmsg(fd, msg, MSG_NOSIGNAL);
+}
+
+static int send_wait(int fd, int timeout_ms)
+{
+ return poll_one(fd, POLLOUT, timeout_ms);
+}
+
+static ssize_t send_data(int sock, struct msghdr *msg, int *timeout_ptr, bool tfo)
+{
+ static const struct io SEND_IO = {
+ .process = send_process,
+ .wait = send_wait
+ };
+ static const struct io SEND_IO_TFO = {
+ .process = send_process_tfo,
+ .wait = send_wait
+ };
+
+ return io_exec(tfo ? &SEND_IO_TFO : &SEND_IO, sock, msg, false, timeout_ptr);
+}
+
+/* -- generic stream and datagram I/O -------------------------------------- */
+
+ssize_t net_base_send(int sock, const uint8_t *buffer, size_t size,
+ const struct sockaddr_storage *addr, int timeout_ms)
+{
+ if (sock < 0 || buffer == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct iovec iov = { 0 };
+ iov.iov_base = (void *)buffer;
+ iov.iov_len = size;
+
+ struct msghdr msg = { 0 };
+ msg.msg_name = (void *)addr;
+ msg.msg_namelen = sockaddr_len(addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ int ret = send_data(sock, &msg, &timeout_ms, false);
+ if (ret < 0) {
+ return ret;
+ } else if (ret != size) {
+ return KNOT_ECONN;
+ }
+
+ return ret;
+}
+
+ssize_t net_base_recv(int sock, uint8_t *buffer, size_t size,
+ struct sockaddr_storage *addr, int timeout_ms)
+{
+ if (sock < 0 || buffer == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct iovec iov = { 0 };
+ iov.iov_base = buffer;
+ iov.iov_len = size;
+
+ struct msghdr msg = { 0 };
+ msg.msg_name = (void *)addr;
+ msg.msg_namelen = addr ? sizeof(*addr) : 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ return recv_data(sock, &msg, true, &timeout_ms);
+}
+
+ssize_t net_dgram_send(int sock, const uint8_t *buffer, size_t size,
+ const struct sockaddr_storage *addr)
+{
+ return net_base_send(sock, buffer, size, addr, 0);
+}
+
+ssize_t net_dgram_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms)
+{
+ return net_base_recv(sock, buffer, size, NULL, timeout_ms);
+}
+
+ssize_t net_stream_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms)
+{
+ return net_base_send(sock, buffer, size, NULL, timeout_ms);
+}
+
+ssize_t net_stream_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms)
+{
+ return net_base_recv(sock, buffer, size, NULL, timeout_ms);
+}
+
+/* -- DNS specific I/O ----------------------------------------------------- */
+
+ssize_t net_dns_tcp_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms,
+ struct sockaddr_storage *tfo_addr)
+{
+ if (sock < 0 || buffer == NULL || size > UINT16_MAX) {
+ return KNOT_EINVAL;
+ }
+
+ struct iovec iov[2];
+ uint16_t pktsize = htons(size);
+ iov[0].iov_base = &pktsize;
+ iov[0].iov_len = sizeof(uint16_t);
+ iov[1].iov_base = (void *)buffer;
+ iov[1].iov_len = size;
+
+ struct msghdr msg = { 0 };
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+ msg.msg_name = (void *)tfo_addr;
+ msg.msg_namelen = tfo_addr ? sizeof(*tfo_addr) : 0;
+
+ ssize_t ret = send_data(sock, &msg, &timeout_ms, tfo_addr != NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return size; /* Do not count the size prefix. */
+}
+
+ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms)
+{
+ if (sock < 0 || buffer == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint16_t pktsize = 0;
+
+ struct iovec iov = {
+ .iov_base = &pktsize,
+ .iov_len = sizeof(pktsize)
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1
+ };
+
+ /* Receive size. */
+ int ret = recv_data(sock, &msg, false, &timeout_ms);
+ if (ret != sizeof(pktsize)) {
+ return ret;
+ }
+ pktsize = ntohs(pktsize);
+
+ /* Check packet size */
+ if (size < pktsize) {
+ return KNOT_ESPACE;
+ }
+
+ /* Receive payload. */
+ iov.iov_base = buffer;
+ iov.iov_len = pktsize;
+
+ return recv_data(sock, &msg, false, &timeout_ms);
+}
diff --git a/src/contrib/net.h b/src/contrib/net.h
new file mode 100644
index 0000000..85a8bd6
--- /dev/null
+++ b/src/contrib/net.h
@@ -0,0 +1,209 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+// 1280 (IPv6 minimum link MTU) - 40 (IPv6 fixed header) - 20 (TCP fixed header)
+#define KNOT_TCP_MSS 1220
+
+/*!
+ * \brief Network interface flags.
+ */
+typedef enum {
+ NET_BIND_NONLOCAL = (1 << 0), //!< Allow to bind unavailable address.
+ NET_BIND_MULTIPLE = (1 << 1), //!< Allow to bind address multiple times.
+} net_bind_flag_t;
+
+/*!
+ * \brief Create unbound socket of given family and type.
+ *
+ * \note The socket is set to non-blocking mode.
+ *
+ * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param addr Socket address.
+ *
+ * \return socket or error code
+ */
+int net_unbound_socket(int type, const struct sockaddr_storage *addr);
+
+/*!
+ * \brief Create socket bound to given address.
+ *
+ * The socket is set to non-blocking mode.
+ *
+ * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param addr Socket address.
+ * \param flags Socket binding options.
+ * \param unix_mode Socket file mode (UNIX socket only). 0 means don't set mode.
+ *
+ * \return socket or error code
+ */
+int net_bound_socket(int type, const struct sockaddr_storage *addr,
+ net_bind_flag_t flags, mode_t unix_mode);
+
+/*!
+ * \brief Create socket connected (asynchronously) to destination address.
+ *
+ * \note The socket is set to non-blocking mode.
+ *
+ * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM).
+ * \param dst_addr Destination address.
+ * \param src_addr Source address (can be NULL).
+ * \param tfo Enable TCP Fast Open.
+ *
+ * \return socket or error code
+ */
+int net_connected_socket(int type, const struct sockaddr_storage *dst_addr,
+ const struct sockaddr_storage *src_addr, bool tfo);
+
+/*!
+ * \brief Enables TCP Fast Open on a bound socket.
+ *
+ * \param sock Socket.
+ *
+ * \return KNOT_EOK or error code
+ */
+int net_bound_tfo(int sock, int backlog);
+
+/*!
+ * \brief Return true if the socket is fully connected.
+ *
+ * \param sock Socket.
+ *
+ * \return true if connected
+ */
+bool net_is_connected(int sock);
+
+/*!
+ * \brief Get socket type (e.g. \a SOCK_STREAM).
+ *
+ * \param sock Socket.
+ */
+int net_socktype(int sock);
+
+/*!
+ * \brief Check if socket is a SOCK_STREAM socket.
+ */
+bool net_is_stream(int sock);
+
+/*!
+ * \brief Accept a connection on a listening socket.
+ *
+ * \brief The socket is set to non-blocking mode.
+ *
+ * \param sock Socket
+ * \param addr Remote address (can be NULL).
+ *
+ * \return socket or error code
+ */
+int net_accept(int sock, struct sockaddr_storage *addr);
+
+/*!
+ * \brief Reset a TCP connection (with a RST packet).
+ */
+void net_reset(int sock);
+
+/*!
+ * \brief Send a message on a socket.
+ *
+ * The socket can be SOCK_STREAM or SOCK_DGRAM.
+ *
+ * The implementation handles partial-writes automatically.
+ *
+ * \param[in] sock Socket.
+ * \param[in] buffer Message buffer.
+ * \param[in] size Size of the message.
+ * \param[in] addr Remote address (ignored for SOCK_STREAM).
+ * \param[in] timeout_ms Write timeout in milliseconds (-1 for infinity,
+ * not valid for SOCK_DGRAM).
+ *
+ * \return Number of bytes sent or negative error code.
+ */
+ssize_t net_base_send(int sock, const uint8_t *buffer, size_t size,
+ const struct sockaddr_storage *addr, int timeout_ms);
+
+/*!
+ * \brief Receive a message from a socket.
+ *
+ * \param[in] sock Socket.
+ * \param[out] buffer Receiving buffer.
+ * \param[in] size Capacity of the receiving buffer.
+ * \param[out] addr Remote address (can be NULL).
+ * \param[in] timeout_ms Read timeout in milliseconds (-1 for infinity).
+ *
+ * \return Number of bytes read or negative error code.
+ */
+ssize_t net_base_recv(int sock, uint8_t *buffer, size_t size,
+ struct sockaddr_storage *addr, int timeout_ms);
+
+/*!
+ * \brief Send a message on a SOCK_DGRAM socket.
+ *
+ * \see net_base_send
+ */
+ssize_t net_dgram_send(int sock, const uint8_t *buffer, size_t size,
+ const struct sockaddr_storage *addr);
+
+/*!
+ * \brief Receive a message from a SOCK_DGRAM socket.
+ *
+ * \see net_base_recv
+ */
+ssize_t net_dgram_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms);
+
+/*!
+ * \brief Send a message on a SOCK_STREAM socket.
+ *
+ * \see net_base_send
+ */
+ssize_t net_stream_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms);
+
+/*!
+ * \brief Receive a message from a SOCK_STREAM socket.
+ *
+ * \see net_base_recv
+ */
+ssize_t net_stream_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms);
+
+/*!
+ * \brief Send a DNS message on a TCP socket.
+ *
+ * The outgoing message is prefixed with a two-byte value carrying the DNS
+ * message size according to the specification. These two bytes are not
+ * reflected in the return value.
+ *
+ * \param[in] tfo_addr If not NULL, send using TCP Fast Open to this address.
+ *
+ * \see net_base_send
+ */
+ssize_t net_dns_tcp_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms,
+ struct sockaddr_storage *tfo_addr);
+
+/*!
+ * \brief Receive a DNS message from a TCP socket.
+ *
+ * The first two bytes of the incoming message are interpreted as a DNS message
+ * size according to the specification. These two bytes are not included in
+ * the returned size. Only a complete DNS message is retrieved.
+ *
+ * \see net_base_recv
+ */
+ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms);
diff --git a/src/contrib/openbsd/LICENSE b/src/contrib/openbsd/LICENSE
new file mode 100644
index 0000000..e9a1aaa
--- /dev/null
+++ b/src/contrib/openbsd/LICENSE
@@ -0,0 +1,2 @@
+../licenses/0BSD
+../licenses/BSD-3-Clause \ No newline at end of file
diff --git a/src/contrib/openbsd/siphash.c b/src/contrib/openbsd/siphash.c
new file mode 100644
index 0000000..26b8cfc
--- /dev/null
+++ b/src/contrib/openbsd/siphash.c
@@ -0,0 +1,176 @@
+/* $OpenBSD: siphash.c,v 1.6 2017/04/12 17:41:49 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d
+ * are the number of compression rounds and the number of finalization rounds.
+ * A compression round is identical to a finalization round and this round
+ * function is called SipRound. Given a 128-bit key k and a (possibly empty)
+ * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m).
+ *
+ * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18,
+ * by Jean-Philippe Aumasson and Daniel J. Bernstein,
+ * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa
+ * https://131002.net/siphash/siphash.pdf
+ * https://131002.net/siphash/
+ */
+
+#include <string.h>
+
+#include "libknot/endian.h"
+#include "contrib/string.h"
+#include "contrib/openbsd/siphash.h"
+
+static void SipHash_CRounds(SIPHASH_CTX *, int);
+static void SipHash_Rounds(SIPHASH_CTX *, int);
+
+void
+SipHash_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key)
+{
+ uint64_t k0, k1;
+
+ k0 = le64toh(key->k0);
+ k1 = le64toh(key->k1);
+
+ ctx->v[0] = 0x736f6d6570736575ULL ^ k0;
+ ctx->v[1] = 0x646f72616e646f6dULL ^ k1;
+ ctx->v[2] = 0x6c7967656e657261ULL ^ k0;
+ ctx->v[3] = 0x7465646279746573ULL ^ k1;
+
+ memset(ctx->buf, 0, sizeof(ctx->buf));
+ ctx->bytes = 0;
+}
+
+void
+SipHash_Update(SIPHASH_CTX *ctx, int rc, int rf, const void *src, size_t len)
+{
+ const uint8_t *ptr = src;
+ size_t left, used;
+
+ if (len == 0)
+ return;
+
+ used = ctx->bytes % sizeof(ctx->buf);
+ ctx->bytes += len;
+
+ if (used > 0) {
+ left = sizeof(ctx->buf) - used;
+
+ if (len >= left) {
+ memcpy(&ctx->buf[used], ptr, left);
+ SipHash_CRounds(ctx, rc);
+ len -= left;
+ ptr += left;
+ } else {
+ memcpy(&ctx->buf[used], ptr, len);
+ return;
+ }
+ }
+
+ while (len >= sizeof(ctx->buf)) {
+ memcpy(ctx->buf, ptr, sizeof(ctx->buf));
+ SipHash_CRounds(ctx, rc);
+ len -= sizeof(ctx->buf);
+ ptr += sizeof(ctx->buf);
+ }
+
+ if (len > 0)
+ memcpy(&ctx->buf, ptr, len);
+}
+
+uint64_t
+SipHash_End(SIPHASH_CTX *ctx, int rc, int rf)
+{
+ uint64_t r;
+ size_t left, used;
+
+ used = ctx->bytes % sizeof(ctx->buf);
+ left = sizeof(ctx->buf) - used;
+ memset(&ctx->buf[used], 0, left - 1);
+ ctx->buf[7] = ctx->bytes;
+
+ SipHash_CRounds(ctx, rc);
+ ctx->v[2] ^= 0xff;
+ SipHash_Rounds(ctx, rf);
+
+ r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]);
+ memzero(ctx, sizeof(*ctx));
+ return htole64(r);
+}
+
+uint64_t
+SipHash(const SIPHASH_KEY *key, int rc, int rf, const void *src, size_t len)
+{
+ SIPHASH_CTX ctx;
+
+ SipHash_Init(&ctx, key);
+ SipHash_Update(&ctx, rc, rf, src, len);
+ return (SipHash_End(&ctx, rc, rf));
+}
+
+#define SIP_ROTL(x, b) ((x) << (b)) | ( (x) >> (64 - (b)))
+
+static void
+SipHash_Rounds(SIPHASH_CTX *ctx, int rounds)
+{
+ while (rounds--) {
+ ctx->v[0] += ctx->v[1];
+ ctx->v[2] += ctx->v[3];
+ ctx->v[1] = SIP_ROTL(ctx->v[1], 13);
+ ctx->v[3] = SIP_ROTL(ctx->v[3], 16);
+
+ ctx->v[1] ^= ctx->v[0];
+ ctx->v[3] ^= ctx->v[2];
+ ctx->v[0] = SIP_ROTL(ctx->v[0], 32);
+
+ ctx->v[2] += ctx->v[1];
+ ctx->v[0] += ctx->v[3];
+ ctx->v[1] = SIP_ROTL(ctx->v[1], 17);
+ ctx->v[3] = SIP_ROTL(ctx->v[3], 21);
+
+ ctx->v[1] ^= ctx->v[2];
+ ctx->v[3] ^= ctx->v[0];
+ ctx->v[2] = SIP_ROTL(ctx->v[2], 32);
+ }
+}
+
+static void
+SipHash_CRounds(SIPHASH_CTX *ctx, int rounds)
+{
+ uint64_t tmp;
+
+ memcpy(&tmp, ctx->buf, sizeof(tmp));
+ uint64_t m = le64toh(tmp);
+
+ ctx->v[3] ^= m;
+ SipHash_Rounds(ctx, rounds);
+ ctx->v[0] ^= m;
+}
diff --git a/src/contrib/openbsd/siphash.h b/src/contrib/openbsd/siphash.h
new file mode 100644
index 0000000..59d952d
--- /dev/null
+++ b/src/contrib/openbsd/siphash.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $OpenBSD: siphash.h,v 1.3 2015/02/20 11:51:03 tedu Exp $
+ */
+
+/*
+ * SipHash is a family of pseudorandom functions (a.k.a. keyed hash functions)
+ * optimized for speed on short messages returning a 64bit hash/digest value.
+ *
+ * The number of rounds is defined during the initialization:
+ * SipHash24_Init() for the fast and reasonable strong version
+ * SipHash48_Init() for the strong version (half as fast)
+ *
+ * struct SIPHASH_CTX ctx;
+ * SipHash24_Init(&ctx);
+ * SipHash_SetKey(&ctx, "16bytes long key");
+ * SipHash_Update(&ctx, pointer_to_string, length_of_string);
+ * SipHash_End(&ctx);
+ */
+
+#ifndef _SIPHASH_H_
+#define _SIPHASH_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define SIPHASH_BLOCK_LENGTH 8
+#define SIPHASH_KEY_LENGTH 16
+#define SIPHASH_DIGEST_LENGTH 8
+
+typedef struct _SIPHASH_CTX {
+ uint64_t v[4];
+ uint8_t buf[SIPHASH_BLOCK_LENGTH];
+ uint32_t bytes;
+} SIPHASH_CTX;
+
+typedef struct {
+ uint64_t k0;
+ uint64_t k1;
+} SIPHASH_KEY;
+
+void SipHash_Init(SIPHASH_CTX *, const SIPHASH_KEY *);
+void SipHash_Update(SIPHASH_CTX *, int, int, const void *, size_t);
+uint64_t SipHash_End(SIPHASH_CTX *, int, int);
+uint64_t SipHash(const SIPHASH_KEY *, int, int, const void *, size_t);
+
+#define SipHash24_Init(_c, _k) SipHash_Init((_c), (_k))
+#define SipHash24_Update(_c, _p, _l) SipHash_Update((_c), 2, 4, (_p), (_l))
+#define SipHash24_End(_d) SipHash_End((_d), 2, 4)
+#define SipHash24(_k, _p, _l) SipHash((_k), 2, 4, (_p), (_l))
+
+#define SipHash48_Init(_c, _k) SipHash_Init((_c), (_k))
+#define SipHash48_Update(_c, _p, _l) SipHash_Update((_c), 4, 8, (_p), (_l))
+#define SipHash48_End(_d) SipHash_End((_d), 4, 8)
+#define SipHash48(_k, _p, _l) SipHash((_k), 4, 8, (_p), (_l))
+
+#endif /* _SIPHASH_H_ */
diff --git a/src/contrib/openbsd/strlcat.c b/src/contrib/openbsd/strlcat.c
new file mode 100644
index 0000000..1409062
--- /dev/null
+++ b/src/contrib/openbsd/strlcat.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "contrib/openbsd/strlcat.h"
+
+size_t
+knot_strlcat(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end */
+ while (n-- != 0 && *d != '\0')
+ d++;
+ dlen = d - dst;
+ n = siz - dlen;
+
+ if (n == 0)
+ return(dlen + strlen(s));
+ while (*s != '\0') {
+ if (n != 1) {
+ *d++ = *s;
+ n--;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ return(dlen + (s - src)); /* count does not include NUL */
+}
diff --git a/src/contrib/openbsd/strlcat.h b/src/contrib/openbsd/strlcat.h
new file mode 100644
index 0000000..7016069
--- /dev/null
+++ b/src/contrib/openbsd/strlcat.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#ifndef HAVE_STRLCAT
+#define strlcat(dst, src, size) knot_strlcat(dst, src, size)
+#endif
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left). At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
+ * If retval >= siz, truncation occurred.
+ */
+size_t
+knot_strlcat(char *dst, const char *src, size_t siz);
diff --git a/src/contrib/openbsd/strlcpy.c b/src/contrib/openbsd/strlcpy.c
new file mode 100644
index 0000000..eafc0e4
--- /dev/null
+++ b/src/contrib/openbsd/strlcpy.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "contrib/openbsd/strlcpy.h"
+
+size_t
+knot_strlcpy(char *dst, const char *src, size_t siz)
+{
+ char *d = dst;
+ const char *s = src;
+ size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0) {
+ while (--n != 0) {
+ if ((*d++ = *s++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }
+
+ return(s - src - 1); /* count does not include NUL */
+}
diff --git a/src/contrib/openbsd/strlcpy.h b/src/contrib/openbsd/strlcpy.h
new file mode 100644
index 0000000..6421068
--- /dev/null
+++ b/src/contrib/openbsd/strlcpy.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#ifndef HAVE_STRLCPY
+#define strlcpy(dst, src, size) knot_strlcpy(dst, src, size)
+#endif
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t
+knot_strlcpy(char *dst, const char *src, size_t siz);
diff --git a/src/contrib/os.h b/src/contrib/os.h
new file mode 100644
index 0000000..8d4a2e2
--- /dev/null
+++ b/src/contrib/os.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+
+inline static bool linux_at_least(unsigned version_first, unsigned version_second)
+{
+#if defined(__linux__)
+ struct utsname info;
+ unsigned first, second;
+ if (uname(&info) != 0 || sscanf(info.release, "%u.%u.", &first, &second) != 2) {
+ return false;
+ } else {
+ return first > version_first ||
+ (first = version_first && second >= version_second);
+ }
+#else
+ return false;
+#endif
+}
diff --git a/src/contrib/proxyv2/proxyv2.c b/src/contrib/proxyv2/proxyv2.c
new file mode 100644
index 0000000..0e5c90b
--- /dev/null
+++ b/src/contrib/proxyv2/proxyv2.c
@@ -0,0 +1,281 @@
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ Copyright (C) 2021 Fastly, Inc.
+
+ 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 <arpa/inet.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "contrib/proxyv2/proxyv2.h"
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+
+/*
+ * Minimal implementation of the haproxy PROXY v2 protocol.
+ *
+ * Supports extracting the original client address and client port number from
+ * the haproxy PROXY v2 protocol's address block.
+ *
+ * See https://www.haproxy.org/download/2.5/doc/proxy-protocol.txt for the
+ * protocol specification.
+ */
+
+static const char PROXYV2_SIG[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
+
+/*
+ * The part of the PROXY v2 payload following the signature.
+ */
+struct proxyv2_hdr {
+ /*
+ * The protocol version and command.
+ *
+ * The upper four bits contain the version which must be \x2 and the
+ * receiver must only accept this value.
+ *
+ * The lower four bits represent the command, which is \x0 for LOCAL
+ * and \x1 for PROXY.
+ */
+ uint8_t ver_cmd;
+
+ /*
+ * The transport protocol and address family. The upper four bits
+ * contain the address family and the lower four bits contain the
+ * protocol.
+ *
+ * The relevant values for DNS are:
+ * \x11: TCP over IPv4
+ * \x12: UDP over IPv4
+ * \x21: TCP over IPv6
+ * \x22: UDP over IPv6
+ */
+ uint8_t fam_addr;
+
+ /*
+ * The number of PROXY v2 payload bytes following this header to skip
+ * to reach the proxied packet (i.e., start of the original DNS message).
+ */
+ uint16_t len;
+};
+
+/*
+ * The PROXY v2 address block for IPv4.
+ */
+struct proxyv2_addr_ipv4 {
+ uint8_t src_addr[4];
+ uint8_t dst_addr[4];
+ uint16_t src_port;
+ uint16_t dst_port;
+};
+
+/*
+ * The PROXY v2 address block for IPv6.
+ */
+struct proxyv2_addr_ipv6 {
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+};
+
+const size_t PROXYV2_HEADER_MAXLEN = sizeof(PROXYV2_SIG) +
+ sizeof(struct proxyv2_hdr) +
+ sizeof(struct proxyv2_addr_ipv6);
+
+/*
+ * Make sure the C compiler lays out the PROXY v2 address block structs so that
+ * they can be memcpy()'d off the wire.
+ */
+#if (__STDC_VERSION__ >= 201112L)
+_Static_assert(sizeof(struct proxyv2_hdr) == 4,
+ "struct proxyv2_hdr is correct size");
+_Static_assert(sizeof(struct proxyv2_addr_ipv4) == 12,
+ "struct proxyv2_addr_ipv4 is correct size");
+_Static_assert(sizeof(struct proxyv2_addr_ipv6) == 36,
+ "struct proxyv2_addr_ipv6 is correct size");
+#endif
+
+int proxyv2_header_offset(void *base, size_t len_base)
+{
+ /*
+ * Check that 'base' has enough bytes to read the PROXY v2 signature
+ * and header, and if so whether the PROXY v2 signature is present.
+ */
+ if (len_base < (sizeof(PROXYV2_SIG) + sizeof(struct proxyv2_hdr)) ||
+ memcmp(base, PROXYV2_SIG, sizeof(PROXYV2_SIG)) != 0)
+ {
+ /* Failure. */
+ return KNOT_EMALF;
+ }
+
+ /* Read the PROXY v2 header. */
+ struct proxyv2_hdr *hdr = base + sizeof(PROXYV2_SIG);
+
+ /*
+ * Check that this is a version 2, command "PROXY" payload.
+ *
+ * XXX: The PROXY v2 spec mandates support for the "LOCAL" command
+ * (byte 0x20).
+ */
+ if (hdr->ver_cmd != 0x21) {
+ /* Failure. */
+ return KNOT_EMALF;
+ }
+
+ /*
+ * Calculate the offset of the original DNS message inside the packet.
+ * This needs to account for the length of the PROXY v2 signature,
+ * PROXY v2 header, and the bytes of variable length PROXY v2 data
+ * following the PROXY v2 header.
+ */
+ const size_t offset_dns = sizeof(PROXYV2_SIG) +
+ sizeof(struct proxyv2_hdr) + ntohs(hdr->len);
+ if (offset_dns < len_base) {
+ return offset_dns;
+ }
+
+ return KNOT_EMALF;
+}
+
+int proxyv2_addr_store(void *base, size_t len_base, struct sockaddr_storage *ss)
+{
+ /*
+ * Calculate the offset of the PROXY v2 address block. This is the data
+ * immediately following the PROXY v2 header.
+ */
+ const size_t offset_proxy_addr = sizeof(PROXYV2_SIG) +
+ sizeof(struct proxyv2_hdr);
+ struct proxyv2_hdr *hdr = base + sizeof(PROXYV2_SIG);
+
+ /*
+ * Handle proxied UDP-over-IPv4 and UDP-over-IPv6 packets.
+ */
+ //TODO What about TCP?
+ if (hdr->fam_addr == 0x12) {
+ /* This is a proxied UDP-over-IPv4 packet. */
+ struct proxyv2_addr_ipv4 *addr;
+
+ /*
+ * Check that the packet is large enough to contain the IPv4
+ * address block.
+ */
+ if (offset_proxy_addr + sizeof(*addr) < len_base) {
+ /* Read the PROXY v2 address block. */
+ addr = base + offset_proxy_addr;
+
+ /* Copy the client's IPv4 address to the caller. */
+ sockaddr_set_raw(ss, AF_INET, addr->src_addr,
+ sizeof(addr->src_addr));
+
+ /* Copy the client's port to the caller. */
+ sockaddr_port_set(ss, ntohs(addr->src_port));
+
+ /* Success. */
+ return KNOT_EOK;
+ }
+ } else if (hdr->fam_addr == 0x22) {
+ /* This is a proxied UDP-over-IPv6 packet. */
+ struct proxyv2_addr_ipv6 *addr;
+
+ /*
+ * Check that the packet is large enough to contain the IPv6
+ * address block.
+ */
+ if (offset_proxy_addr + sizeof(*addr) < len_base) {
+ /* Read the PROXY v2 address block. */
+ addr = base + offset_proxy_addr;
+
+ /* Copy the client's IPv6 address to the caller. */
+ sockaddr_set_raw(ss, AF_INET6, addr->src_addr,
+ sizeof(addr->src_addr));
+
+ /* Copy the client's port to the caller. */
+ sockaddr_port_set(ss, ntohs(addr->src_port));
+
+ /* Success. */
+ return KNOT_EOK;
+ }
+ }
+
+ /* Failure. */
+ return KNOT_EMALF;
+}
+
+int proxyv2_write_header(char *buf, size_t buflen, int socktype, const struct sockaddr *src,
+ const struct sockaddr *dst)
+{
+ if (buflen < PROXYV2_HEADER_MAXLEN) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t fam_addr = 0;
+ int family = src->sa_family;
+ if (socktype == SOCK_DGRAM) {
+ fam_addr += 0x2;
+ } else if (socktype == SOCK_STREAM) {
+ fam_addr += 0x1;
+ } else {
+ return KNOT_EINVAL;
+ }
+ if (family == AF_INET) {
+ fam_addr += 0x10;
+ } else if (family == AF_INET6) {
+ fam_addr += 0x20;
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ struct proxyv2_hdr hdr = {
+ .ver_cmd = 0x21,
+ .fam_addr = fam_addr,
+ .len = (family == AF_INET)
+ ? htons(sizeof(struct proxyv2_addr_ipv4))
+ : htons(sizeof(struct proxyv2_addr_ipv6))
+ };
+
+ size_t offset = 0;
+ memcpy(buf, PROXYV2_SIG, sizeof(PROXYV2_SIG));
+ offset += sizeof(PROXYV2_SIG);
+ memcpy(buf + offset, &hdr, sizeof(hdr));
+ offset += sizeof(hdr);
+
+ if (family == AF_INET) {
+ struct proxyv2_addr_ipv4 ipv4 = { 0 };
+ struct sockaddr_in *p_src = (struct sockaddr_in *)src;
+ struct sockaddr_in *p_dst = (struct sockaddr_in *)dst;
+ memcpy(ipv4.src_addr, &p_src->sin_addr, sizeof(p_src->sin_addr));
+ memcpy(ipv4.dst_addr, &p_dst->sin_addr, sizeof(p_dst->sin_addr));
+ ipv4.src_port = p_src->sin_port;
+ ipv4.dst_port = p_dst->sin_port;
+
+ // Store in buffer
+ memcpy(buf + offset, &ipv4, sizeof(ipv4));
+ offset += sizeof(ipv4);
+ } else {
+ struct proxyv2_addr_ipv6 ipv6 = { 0 };
+ struct sockaddr_in6 *p_src = (struct sockaddr_in6 *)src;
+ struct sockaddr_in6 *p_dst = (struct sockaddr_in6 *)dst;
+ memcpy(ipv6.src_addr, &p_src->sin6_addr, sizeof(p_src->sin6_addr));
+ memcpy(ipv6.dst_addr, &p_dst->sin6_addr, sizeof(p_dst->sin6_addr));
+ ipv6.src_port = p_src->sin6_port;
+ ipv6.dst_port = p_dst->sin6_port;
+
+ // Store in buffer
+ memcpy(buf + offset, &ipv6, sizeof(ipv6));
+ offset += sizeof(ipv6);
+ }
+
+ return offset;
+}
diff --git a/src/contrib/proxyv2/proxyv2.h b/src/contrib/proxyv2/proxyv2.h
new file mode 100644
index 0000000..8fd8299
--- /dev/null
+++ b/src/contrib/proxyv2/proxyv2.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <sys/socket.h>
+
+extern const size_t PROXYV2_HEADER_MAXLEN;
+
+int proxyv2_header_offset(void *base, size_t len_base);
+
+int proxyv2_addr_store(void *base, size_t len_base, struct sockaddr_storage *ss);
+
+int proxyv2_write_header(char *buf, size_t buflen, int socktype, const struct sockaddr *src,
+ const struct sockaddr *dst);
diff --git a/src/contrib/qp-trie/trie.c b/src/contrib/qp-trie/trie.c
new file mode 100644
index 0000000..30dcb42
--- /dev/null
+++ b/src/contrib/qp-trie/trie.c
@@ -0,0 +1,1451 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ Copyright (C) 2018 Tony Finch <dot@dotat.at>
+
+ 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/>.
+
+ The code originated from https://github.com/fanf2/qp/blob/master/qp.c
+ at revision 5f6d93753.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "libknot/errcode.h"
+
+typedef unsigned int uint;
+typedef uint64_t trie_index_t; /*!< nibble index into a key */
+typedef uint64_t word; /*!< A type-punned word */
+typedef uint bitmap_t; /*!< Bit-maps, using the range of 1<<0 to 1<<16 (inclusive). */
+
+typedef char static_assert_pointer_fits_in_word
+ [sizeof(word) >= sizeof(uintptr_t) ? 1 : -1];
+
+#define KEYLENBITS 31
+
+/*! \brief trie keys have lengths
+ *
+ * 32 bits are enough for key lengths; probably even 16 bits would be.
+ * However, a 32 bit length means the alignment will be a multiple of
+ * 4, allowing us to stash the COW and BRANCH flags in the bottom bits
+ * of a pointer to a key.
+ *
+ * We need to steal a couple of bits from the length to keep the COW
+ * state of key allocations.
+ */
+typedef struct {
+ uint32_t cow:1, len:KEYLENBITS;
+ trie_key_t chars[];
+} tkey_t;
+
+/*! \brief A trie node is a pair of words.
+ *
+ * Each word is type-punned, depending on whether this is a branch
+ * node or a leaf node. We'll define some accessor functions to wrap
+ * this up into something reasonably safe.
+ *
+ * We aren't using a union to avoid problems with strict aliasing, and
+ * we aren't using bitfields because we want to control exactly which
+ * bits in the word are used by each field (in particular the flags).
+ *
+ * Branch nodes are never allocated individually: they are always part
+ * of either the root node or the twigs array of their parent branch.
+ *
+ * In a branch:
+ *
+ * `i` contains flags, bitmap, and index, explained in more detail below.
+ *
+ * `p` is a pointer to the "twigs", an array of child nodes.
+ *
+ * In a leaf:
+ *
+ * `i` is cast from a pointer to a tkey_t, with flags in the bottom bits.
+ *
+ * `p` is a trie_val_t.
+ */
+typedef struct node {
+ word i;
+ void *p;
+} node_t;
+
+struct trie {
+ node_t root; // undefined when weight == 0, see empty_root()
+ size_t weight;
+ knot_mm_t mm;
+};
+
+/*! \brief size (in bits) of nibble (half-byte) indexes into keys
+ *
+ * The bottom bit is clear for the upper nibble, and set for the lower
+ * nibble, big-endian style, since the tree has to be in lexicographic
+ * order. The index increases from one branch node to the next as you
+ * go deeper into the trie. All the keys below a branch are identical
+ * up to the nibble identified by the branch.
+ *
+ * (see also tkey_t.len above)
+ */
+#define TWIDTH_INDEX 33
+
+/*! \brief exclusive limit on indexes */
+#define TMAX_INDEX (BIG1 << TWIDTH_INDEX)
+
+/*! \brief size (in bits) of branch bitmap
+ *
+ * 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 an extra nibble value, ordered
+ * before all others. So there are 16 possible real nibble values,
+ * plus one value for nibbles past the end of the key.
+ */
+#define TWIDTH_BMP 17
+
+/*
+ * We're constructing the layout of the branch `i` field in a careful
+ * way to avoid mistakes, getting the compiler to calculate values
+ * rather than typing them in by hand.
+ */
+enum {
+ TSHIFT_BRANCH = 0,
+ TSHIFT_COW,
+ TSHIFT_BMP,
+ TOP_BMP = TSHIFT_BMP + TWIDTH_BMP,
+ TSHIFT_INDEX = TOP_BMP,
+ TOP_INDEX = TSHIFT_INDEX + TWIDTH_INDEX,
+};
+
+typedef char static_assert_fields_fit_in_word
+ [TOP_INDEX <= sizeof(word) * CHAR_BIT ? 1 : -1];
+
+typedef char static_assert_bmp_fits
+ [TOP_BMP <= sizeof(bitmap_t) * CHAR_BIT ? 1 : -1];
+
+#define BIG1 ((word)1)
+#define TMASK(width, shift) (((BIG1 << (width)) - BIG1) << (shift))
+
+/*! \brief is this node a branch or a leaf? */
+#define TFLAG_BRANCH (BIG1 << TSHIFT_BRANCH)
+
+/*! \brief copy-on-write flag, used in both leaves and branches */
+#define TFLAG_COW (BIG1 << TSHIFT_COW)
+
+/*! \brief for extracting pointer to key */
+#define TMASK_LEAF (~(word)(TFLAG_BRANCH | TFLAG_COW))
+
+/*! \brief mask for extracting nibble index */
+#define TMASK_INDEX TMASK(TWIDTH_INDEX, TSHIFT_INDEX)
+
+/*! \brief mask for extracting bitmap */
+#define TMASK_BMP TMASK(TWIDTH_BMP, TSHIFT_BMP)
+
+/*! \brief bitmap entry for NOBYTE */
+#define BMP_NOBYTE (BIG1 << TSHIFT_BMP)
+
+/*! \brief Initialize a new leaf, copying the key, and returning failure code. */
+static int mkleaf(node_t *leaf, const trie_key_t *key, uint32_t len, knot_mm_t *mm)
+{
+ if (unlikely((word)len > (BIG1 << KEYLENBITS)))
+ return KNOT_ENOMEM;
+ tkey_t *lkey = mm_alloc(mm, sizeof(tkey_t) + len);
+ if (unlikely(!lkey))
+ return KNOT_ENOMEM;
+ lkey->cow = 0;
+ lkey->len = len;
+ memcpy(lkey->chars, key, len);
+ word i = (uintptr_t)lkey;
+ assert((i & TFLAG_BRANCH) == 0);
+ *leaf = (node_t){ .i = i, .p = NULL };
+ return KNOT_EOK;
+}
+
+/*! \brief construct a branch node */
+static node_t mkbranch(trie_index_t index, bitmap_t bmp, node_t *twigs)
+{
+ word i = TFLAG_BRANCH | bmp
+ | (index << TSHIFT_INDEX);
+ assert(index < TMAX_INDEX);
+ assert((bmp & ~TMASK_BMP) == 0);
+ return (node_t){ .i = i, .p = twigs };
+}
+
+/*! \brief Make an empty root node. */
+static node_t empty_root(void)
+{
+ return mkbranch(TMAX_INDEX-1, 0, NULL);
+}
+
+/*! \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 Test flags to determine type of this node. */
+static bool isbranch(const node_t *t)
+{
+ return t->i & TFLAG_BRANCH;
+}
+
+static tkey_t *tkey(const node_t *t)
+{
+ assert(!isbranch(t));
+ return (tkey_t *)(uintptr_t)(t->i & TMASK_LEAF);
+}
+
+static trie_val_t *tvalp(node_t *t)
+{
+ assert(!isbranch(t));
+ return &t->p;
+}
+
+/*! \brief Given a branch node, return the index of the corresponding nibble in the key. */
+static trie_index_t branch_index(const node_t *t)
+{
+ assert(isbranch(t));
+ return (t->i & TMASK_INDEX) >> TSHIFT_INDEX;
+}
+
+static bitmap_t branch_bmp(const node_t *t)
+{
+ assert(isbranch(t));
+ return (t->i & TMASK_BMP);
+}
+
+/*!
+ * \brief Count the number of set bits.
+ *
+ * \TODO This implementation may be relatively slow on some HW.
+ */
+static uint branch_weight(const node_t *t)
+{
+ assert(isbranch(t));
+ uint n = __builtin_popcount(t->i & TMASK_BMP);
+ assert(n > 1 && n <= TWIDTH_BMP);
+ return n;
+}
+
+/*! \brief Compute offset of an existing child in a branch node. */
+static uint twigoff(const node_t *t, bitmap_t bit)
+{
+ assert(isbranch(t));
+ assert(__builtin_popcount(bit) == 1);
+ return __builtin_popcount(t->i & TMASK_BMP & (bit - 1));
+}
+
+/*! \brief Extract a nibble from a key and turn it into a bitmask. */
+static bitmap_t keybit(trie_index_t ni, const trie_key_t *key, uint32_t len)
+{
+ trie_index_t bytei = ni >> 1;
+
+ if (bytei >= len)
+ return BMP_NOBYTE;
+
+ uint8_t ki = (uint8_t)key[bytei];
+ uint nibble = (ni & 1) ? (ki & 0xf) : (ki >> 4);
+
+ // skip one for NOBYTE nibbles after the end of the key
+ return BIG1 << (nibble + 1 + TSHIFT_BMP);
+}
+
+/*! \brief Extract a nibble from a key and turn it into a bitmask. */
+static bitmap_t twigbit(const node_t *t, const trie_key_t *key, uint32_t len)
+{
+ assert(isbranch(t));
+ return keybit(branch_index(t), key, len);
+}
+
+/*! \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));
+ assert((bit & ~TMASK_BMP) == 0);
+ assert(__builtin_popcount(bit) == 1);
+ return t->i & bit;
+}
+
+/*! \brief Get pointer to packed array of child nodes. */
+static node_t* twigs(node_t *t)
+{
+ assert(isbranch(t));
+ return t->p;
+}
+
+/*! \brief Get pointer to a particular child of a branch node. */
+static node_t* twig(node_t *t, uint i)
+{
+ assert(i < branch_weight(t));
+ return twigs(t) + i;
+}
+
+/*! \brief Get twig number of a child node TODO: better description. */
+static uint twig_number(node_t *child, node_t *parent)
+{
+ // twig array index using pointer arithmetic
+ ptrdiff_t num = child - twigs(parent);
+ assert(num >= 0 && num < branch_weight(parent));
+ return (uint)num;
+}
+
+/*! \brief Simple string comparator. */
+static int key_cmp(const trie_key_t *k1, uint32_t k1_len,
+ const trie_key_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)
+{
+ trie_t *trie = mm_alloc(mm, sizeof(trie_t));
+ if (trie != NULL) {
+ trie->root = empty_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, tkey(trie));
+ } else {
+ uint n = branch_weight(trie);
+ for (uint i = 0; i < n; ++i)
+ clear_trie(twig(trie, i), mm);
+ mm_free(mm, twigs(trie));
+ }
+}
+
+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);
+ tbl->root = empty_root();
+ tbl->weight = 0;
+}
+
+static bool dup_trie(node_t *copy, const node_t *orig, trie_dup_cb dup_cb, knot_mm_t *mm)
+{
+ if (isbranch(orig)) {
+ uint n = branch_weight(orig);
+ node_t *cotw = mm_alloc(mm, n * sizeof(*cotw));
+ if (cotw == NULL) {
+ return NULL;
+ }
+ const node_t *ortw = twigs((node_t *)orig);
+ for (uint i = 0; i < n; ++i) {
+ if (!dup_trie(cotw + i, ortw + i, dup_cb, mm)) {
+ while (i-- > 0) {
+ clear_trie(cotw + i, mm);
+ }
+ mm_free(mm, cotw);
+ return false;
+ }
+ }
+ *copy = mkbranch(branch_index(orig), branch_bmp(orig), cotw);
+ } else {
+ tkey_t *key = tkey(orig);
+ if (mkleaf(copy, key->chars, key->len, mm) != KNOT_EOK) {
+ return false;
+ }
+ if ((copy->p = dup_cb(orig->p, mm)) == NULL) {
+ mm_free(mm, tkey(copy));
+ return false;
+ }
+ }
+ return true;
+}
+
+trie_t* trie_dup(const trie_t *orig, trie_dup_cb dup_cb, knot_mm_t *mm)
+{
+ if (orig == NULL) {
+ return NULL;
+ }
+ trie_t *copy = mm_alloc(mm, sizeof(*copy));
+ if (copy == NULL) {
+ return NULL;
+ }
+ copy->weight = orig->weight;
+ if (mm != NULL) {
+ copy->mm = *mm;
+ } else {
+ mm_ctx_init(&copy->mm);
+ }
+ if (copy->weight) {
+ if (!dup_trie(&copy->root, &orig->root, dup_cb, mm)) {
+ mm_free(mm, copy);
+ return NULL;
+ }
+ }
+ return copy;
+}
+
+size_t trie_weight(const trie_t *tbl)
+{
+ assert(tbl);
+ return tbl->weight;
+}
+
+trie_val_t* trie_get_try(trie_t *tbl, const trie_key_t *key, uint32_t len)
+{
+ assert(tbl);
+ if (!tbl->weight)
+ return NULL;
+ node_t *t = &tbl->root;
+ while (isbranch(t)) {
+ __builtin_prefetch(twigs(t));
+ bitmap_t b = twigbit(t, key, len);
+ if (!hastwig(t, b))
+ return NULL;
+ t = twig(t, twigoff(t, b));
+ }
+ tkey_t *lkey = tkey(t);
+ if (key_cmp(key, len, lkey->chars, lkey->len) != 0)
+ return NULL;
+ return tvalp(t);
+}
+
+/* Optimization: the approach isn't ideal, as e.g. walking through the prefix
+ * is duplicated and we explicitly construct the wildcard key. Still, it's close
+ * to optimum which would be significantly more complicated and error-prone to write. */
+trie_val_t* trie_get_try_wildcard(trie_t *tbl, const trie_key_t *key, uint32_t len)
+{
+ assert(tbl);
+ if (!tbl->weight)
+ return NULL;
+ // Find leaf sharing the longest common prefix; see ns_find_branch() for explanation.
+ node_t *t = &tbl->root;
+ while (isbranch(t)) {
+ __builtin_prefetch(twigs(t));
+ bitmap_t b = twigbit(t, key, len);
+ uint i = hastwig(t, b) ? twigoff(t, b) : 0;
+ t = twig(t, i);
+ }
+ const tkey_t * const lcp_key = tkey(t);
+
+ // Find the last matching zero byte or -1 (source of synthesis)
+ int i_lmz = -1;
+ for (int i = 0; i < len && i < lcp_key->len && key[i] == lcp_key->chars[i]; ++i) {
+ if (key[i] == '\0' && i < len - 1) // do not count the terminating zero
+ i_lmz = i;
+ // Shortcut: we may have found an exact match.
+ if (i == len - 1 && len == lcp_key->len)
+ return tvalp(t);
+ }
+ if (len == 0) // The empty name needs separate handling.
+ return lcp_key->len == 0 ? tvalp(t) : NULL;
+
+ // Construct the key of the wildcard we need and look it up.
+ const int wild_len = i_lmz + 3;
+ uint8_t wild_key[wild_len];
+ memcpy(wild_key, key, wild_len - 2);
+ wild_key[wild_len - 2] = '*';
+ wild_key[wild_len - 1] = '\0'; // LF is always 0-terminated ATM
+ return trie_get_try(tbl, wild_key, wild_len);
+}
+
+/*! \brief Delete leaf t with parent p; b is the bit for t under p.
+ * Optionally return the deleted value via val. The function can't fail. */
+static void del_found(trie_t *tbl, node_t *t, node_t *p, bitmap_t b, trie_val_t *val)
+{
+ assert(!tkey(t)->cow);
+ mm_free(&tbl->mm, tkey(t));
+ if (val != NULL)
+ *val = *tvalp(t); // we return trie_val_t directly when deleting
+ --tbl->weight;
+ if (unlikely(!p)) { // whole trie was a single leaf
+ assert(tbl->weight == 0);
+ tbl->root = empty_root();
+ return;
+ }
+ // remove leaf t as child of p
+ node_t *tp = twigs(p);
+ uint ci = twig_number(t, p);
+ uint cc = branch_weight(p); // child count
+
+ if (cc == 2) {
+ // collapse binary node p: move the other child to the parent
+ *p = tp[1 - ci];
+ mm_free(&tbl->mm, tp);
+ return;
+ }
+ memmove(tp + ci, tp + ci + 1, sizeof(node_t) * (cc - ci - 1));
+ p->i &= ~b;
+ node_t *newt = mm_realloc(&tbl->mm, tp, sizeof(node_t) * (cc - 1),
+ sizeof(node_t) * cc);
+ if (likely(newt != NULL))
+ p->p = newt;
+ // We can ignore mm_realloc failure because an oversized twig
+ // array is OK - only beware that next time the prev_size
+ // passed to mm_realloc will not be correct; TODO?
+}
+
+int trie_del(trie_t *tbl, const trie_key_t *key, uint32_t len, trie_val_t *val)
+{
+ assert(tbl);
+ if (!tbl->weight)
+ return KNOT_ENOENT;
+ node_t *t = &tbl->root; // current and parent node
+ node_t *p = NULL;
+ bitmap_t b = 0;
+ while (isbranch(t)) {
+ __builtin_prefetch(twigs(t));
+ b = twigbit(t, key, len);
+ if (!hastwig(t, b))
+ return KNOT_ENOENT;
+ p = t;
+ t = twig(t, twigoff(t, b));
+ }
+ tkey_t *lkey = tkey(t);
+ if (key_cmp(key, len, lkey->chars, lkey->len) != 0)
+ return KNOT_ENOENT;
+ del_found(tbl, t, p, b, val);
+ return KNOT_EOK;
+}
+
+/*!
+ * \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).
+ * stack[0] is always a valid pointer to the root -> ns_gettrie()
+ */
+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 most use cases. */
+ node_t* stack_init[250];
+} 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]);
+ ns->stack[0] = &tbl->root;
+ ns->len = (tbl->weight > 0);
+}
+
+static inline trie_t * ns_gettrie(nstack_t *ns)
+{
+ assert(ns && ns->stack && ns->stack[0]);
+ return (struct trie *)ns->stack[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 = 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 idiff Set the index of first differing nibble, or TMAX_INDEX for an exact match
+ * \param tbit Set the bit of the closest leaf's nibble at index idiff
+ * \param kbit Set the bit of the key's nibble at index idiff
+ *
+ * \return KNOT_EOK or KNOT_ENOMEM.
+ */
+static int ns_find_branch(nstack_t *ns, const trie_key_t *key, uint32_t len,
+ trie_index_t *idiff, bitmap_t *tbit, bitmap_t *kbit)
+{
+ assert(ns && ns->len && idiff);
+ // 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(twigs(t));
+ 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 = tkey(ns->stack[ns->len-1]);
+ // Find index of the first char that differs.
+ size_t bytei = 0;
+ uint32_t klen = lkey->len;
+ for (bytei = 0; bytei < MIN(len,klen); bytei++) {
+ if (key[bytei] != lkey->chars[bytei])
+ break;
+ }
+ // Find which half-byte has matched.
+ trie_index_t index = bytei << 1;
+ if (bytei == len && len == lkey->len) { // found equivalent key
+ index = TMAX_INDEX;
+ goto success;
+ }
+ if (likely(bytei < MIN(len,klen))) {
+ uint8_t k2 = (uint8_t)lkey->chars[bytei];
+ uint8_t k1 = (uint8_t)key[bytei];
+ if (((k1 ^ k2) & 0xf0) == 0)
+ index += 1;
+ }
+ // now go up the trie from the current leaf
+ node_t *t;
+ do {
+ if (unlikely(ns->len == 1))
+ goto success; // only the root stays on the stack
+ t = ns->stack[ns->len - 2];
+ if (branch_index(t) < index)
+ 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];
+ assert(branch_index(t) >= index);
+ }
+ if (ns->len > 1) {
+ t = ns->stack[ns->len - 2];
+ assert(branch_index(t) < index || index == TMAX_INDEX);
+ }
+ #endif
+ *idiff = index;
+ *tbit = keybit(index, lkey->chars, lkey->len);
+ *kbit = keybit(index, key, len);
+ 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;
+ uint lasti = branch_weight(t) - 1;
+ 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];
+ // Beware: BMP_NOBYTE child is ordered *before* its parent.
+ if (isbranch(t) && hastwig(t, BMP_NOBYTE)) {
+ ERR_RETURN(ns_longer(ns));
+ ns->stack[ns->len++] = twig(t, 0);
+ return KNOT_EOK;
+ }
+
+ for (; ns->len >= 2; --ns->len) {
+ t = ns->stack[ns->len - 1];
+ node_t *p = ns->stack[ns->len - 2];
+ uint ci = twig_number(t, p);
+ if (ci == 0) // we've got to go up again
+ continue;
+ // t isn't the first child -> go down the previous one
+ ns->stack[ns->len - 1] = twig(p, ci - 1);
+ return ns_last_leaf(ns);
+ }
+ return KNOT_ENOENT; // root without empty key has no previous leaf
+}
+
+/*!
+ * \brief Advance the node stack to the leaf that is successor to the current node.
+ *
+ * \param skip_prefixed skip any nodes whose key is a prefix of the current one.
+ * If false, 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, const bool skip_pefixed)
+{
+ assert(ns && ns->len > 0);
+
+ node_t *t = ns->stack[ns->len - 1];
+ if (!skip_pefixed && isbranch(t))
+ return ns_first_leaf(ns);
+ for (; ns->len >= 2; --ns->len) {
+ t = ns->stack[ns->len - 1];
+ node_t *p = ns->stack[ns->len - 2];
+ uint ci = twig_number(t, p);
+ if (skip_pefixed && ci == 0 && hastwig(t, BMP_NOBYTE)) {
+ // Keys in the subtree of p are suffixes of the key of t,
+ // so we've got to go one level higher
+ // (this can't happen more than once)
+ continue;
+ }
+ uint cc = branch_weight(p);
+ assert(ci + 1 <= cc);
+ if (ci + 1 == cc) {
+ // t is the last child of p, so we need to keep climbing
+ continue;
+ }
+ // go down the next child of p
+ ns->stack[ns->len - 1] = twig(p, ci + 1);
+ return ns_first_leaf(ns);
+ }
+ return KNOT_ENOENT; // not found, as no more parent is available
+}
+
+/*! \brief Advance the node stack to leaf with longest prefix of the current key. */
+static int ns_prefix(nstack_t *ns)
+{
+ assert(ns && ns->len > 0);
+ const node_t *start = ns->stack[ns->len - 1];
+ // Walk up the trie until we find a BMP_NOBYTE child.
+ while (--ns->len > 0) {
+ node_t *p = ns->stack[ns->len - 1];
+ if (!hastwig(p, BMP_NOBYTE))
+ continue;
+ node_t *end = twig(p, 0);
+ // In case we started in a BMP_NOBYTE leaf, the first step up
+ // did NOT shorten the key and we would get back into the same
+ // node again.
+ if (end == start)
+ continue;
+ ns->stack[ns->len++] = end;
+ return KNOT_EOK;
+ }
+ return KNOT_ENOENT; // not found, as no more parent is available
+}
+
+/*! \brief less-or-equal search.
+ *
+ * \return KNOT_EOK for exact match, 1 for previous, KNOT_ENOENT for not-found,
+ * or KNOT_E*.
+ */
+static int ns_get_leq(nstack_t *ns, const trie_key_t *key, uint32_t len)
+{
+ // First find the key with longest-matching prefix
+ trie_index_t idiff;
+ bitmap_t tbit, kbit;
+ ERR_RETURN(ns_find_branch(ns, key, len, &idiff, &tbit, &kbit));
+ node_t *t = ns->stack[ns->len - 1];
+ if (idiff == TMAX_INDEX) // found exact match
+ return KNOT_EOK;
+ // Get t: the last node on matching path
+ bitmap_t b;
+ if (isbranch(t) && branch_index(t) == idiff) {
+ // t is OK
+ b = kbit;
+ } else {
+ // the top of the stack was the first unmatched node -> step up
+ if (ns->len == 1) {
+ // root was unmatched already
+ if (kbit < tbit)
+ return KNOT_ENOENT;
+ ERR_RETURN(ns_last_leaf(ns));
+ return 1;
+ }
+ --ns->len;
+ t = ns->stack[ns->len - 1];
+ b = twigbit(t, key, len);
+ }
+ // 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)
+ int i = hastwig(t, b)
+ ? (int)twigoff(t, b) - (kbit < tbit)
+ : (int)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));
+ }
+ return 1;
+}
+
+int trie_get_leq(trie_t *tbl, const trie_key_t *key, uint32_t len, trie_val_t **val)
+{
+ assert(tbl && val);
+ if (tbl->weight == 0) {
+ if (val) *val = NULL;
+ return KNOT_ENOENT;
+ }
+ // We try to do without malloc.
+ nstack_t ns_local;
+ ns_init(&ns_local, tbl);
+ nstack_t *ns = &ns_local;
+
+ int ret = ns_get_leq(ns, key, len);
+ if (ret == KNOT_EOK || ret == 1) {
+ assert(!isbranch(ns->stack[ns->len - 1]));
+ if (val) *val = tvalp(ns->stack[ns->len - 1]);
+ } else {
+ if (val) *val = NULL;
+ }
+ ns_cleanup(ns);
+ return ret;
+}
+
+int trie_it_get_leq(trie_it_t *it, const trie_key_t *key, uint32_t len)
+{
+ assert(it && it->stack[0] && it->alen);
+ const trie_t *tbl = ns_gettrie(it);
+ if (tbl->weight == 0) {
+ it->len = 0;
+ return KNOT_ENOENT;
+ }
+ it->len = 1;
+ int ret = ns_get_leq(it, key, len);
+ if (ret == KNOT_EOK || ret == 1) {
+ assert(trie_it_key(it, NULL));
+ } else {
+ it->len = 0;
+ }
+ return ret;
+}
+
+/* see below */
+static int cow_pushdown(trie_cow_t *cow, nstack_t *ns);
+
+/*! \brief implementation of trie_get_ins() and trie_get_cow() */
+static trie_val_t* cow_get_ins(trie_cow_t *cow, trie_t *tbl,
+ const trie_key_t *key, uint32_t len)
+{
+ assert(tbl);
+ // First leaf in an empty tbl?
+ if (unlikely(!tbl->weight)) {
+ if (unlikely(mkleaf(&tbl->root, key, len, &tbl->mm)))
+ return NULL;
+ ++tbl->weight;
+ return tvalp(&tbl->root);
+ }
+ { // 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;
+ trie_index_t idiff;
+ bitmap_t tbit, kbit;
+ if (unlikely(ns_find_branch(ns, key, len, &idiff, &tbit, &kbit)))
+ return NULL;
+ if (unlikely(cow && cow_pushdown(cow, ns) != KNOT_EOK))
+ return NULL;
+ node_t *t = ns->stack[ns->len - 1];
+ if (idiff == TMAX_INDEX) // the same key was already present
+ return tvalp(t);
+ node_t leaf, *leafp;
+ if (unlikely(mkleaf(&leaf, key, len, &tbl->mm)))
+ return NULL;
+
+ if (isbranch(t) && branch_index(t) == idiff) {
+ // The node t needs a new leaf child.
+ assert(!hastwig(t, kbit));
+ // new child position and original child count
+ uint s = twigoff(t, kbit);
+ uint m = branch_weight(t);
+ node_t *nt = mm_realloc(&tbl->mm, twigs(t),
+ sizeof(node_t) * (m + 1), sizeof(node_t) * m);
+ if (unlikely(!nt))
+ goto err_leaf;
+ memmove(nt + s + 1, nt + s, sizeof(node_t) * (m - s));
+ leafp = nt + s;
+ *t = mkbranch(idiff, branch_bmp(t) | kbit, nt);
+ } 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 *nt = mm_alloc(&tbl->mm, sizeof(node_t) * 2);
+ if (unlikely(!nt))
+ goto err_leaf;
+ node_t t2 = *t; // Save before overwriting t.
+ *t = mkbranch(idiff, tbit | kbit, nt);
+ *twig(t, twigoff(t, tbit)) = t2;
+ leafp = twig(t, twigoff(t, kbit));
+ };
+ *leafp = leaf;
+ ++tbl->weight;
+ return tvalp(leafp);
+err_leaf:
+ mm_free(&tbl->mm, tkey(&leaf));
+ return NULL;
+ }
+}
+
+trie_val_t* trie_get_ins(trie_t *tbl, const trie_key_t *key, uint32_t len)
+{
+ return cow_get_ins(NULL, tbl, key, len);
+}
+
+/*! \brief Apply a function to every trie_val_t*, in order; a recursive solution. */
+static int apply_nodes(node_t *t, int (*f)(trie_val_t *, void *), void *d)
+{
+ assert(t);
+ if (!isbranch(t))
+ return f(tvalp(t), d);
+ uint n = branch_weight(t);
+ for (uint i = 0; i < n; ++i)
+ ERR_RETURN(apply_nodes(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_nodes(&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;
+}
+
+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);
+}
+
+trie_it_t *trie_it_clone(const trie_it_t *it)
+{
+ if (!it) // TODO: or should that be an assertion?
+ return NULL;
+ trie_it_t *it2 = malloc(sizeof(nstack_t));
+ if (!it2)
+ return NULL;
+ it2->len = it->len;
+ it2->alen = it->alen; // we _might_ change it in the rare malloc case, but...
+ if (likely(it->stack == it->stack_init)) {
+ it2->stack = it2->stack_init;
+ assert(it->alen == sizeof(it->stack_init) / sizeof(it->stack_init[0]));
+ } else {
+ it2->stack = malloc(it2->alen * sizeof(it2->stack[0]));
+ if (!it2->stack) {
+ free(it2);
+ return NULL;
+ }
+ }
+ memcpy(it2->stack, it->stack, it->len * sizeof(it->stack[0]));
+ return it2;
+}
+
+const trie_key_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 = tkey(t);
+ 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 tvalp(t);
+}
+
+void trie_it_next(trie_it_t *it)
+{
+ assert(it && it->len);
+ if (ns_next_leaf(it, false) != KNOT_EOK)
+ it->len = 0;
+}
+
+void trie_it_next_loop(trie_it_t *it)
+{
+ assert(it && it->len);
+ int ret = ns_next_leaf(it, false);
+ if (ret == KNOT_ENOENT) {
+ it->len = 1;
+ ret = ns_first_leaf(it);
+ }
+ if (ret)
+ it->len = 0;
+}
+
+void trie_it_next_nosuffix(trie_it_t *it)
+{
+ assert(it && it->len);
+ if (ns_next_leaf(it, true) != KNOT_EOK)
+ it->len = 0;
+}
+
+void trie_it_prev(trie_it_t *it)
+{
+ assert(it && it->len);
+ if (ns_prev_leaf(it) != KNOT_EOK)
+ it->len = 0;
+}
+
+void trie_it_prev_loop(trie_it_t *it)
+{
+ assert(it && it->len);
+ int ret = ns_prev_leaf(it);
+ if (ret == KNOT_ENOENT) {
+ it->len = 1;
+ ret = ns_last_leaf(it);
+ }
+ if (ret)
+ it->len = 0;
+}
+
+void trie_it_parent(trie_it_t *it)
+{
+ assert(it && it->len);
+ if (ns_prefix(it))
+ it->len = 0;
+}
+
+void trie_it_del(trie_it_t *it)
+{
+ assert(it && it->len);
+ if (it->len == 0)
+ return;
+ node_t *t = it->stack[it->len - 1];
+ assert(!isbranch(t));
+ bitmap_t b; // del_found() needs to know which bit to zero in the bitmap
+ node_t *p;
+ if (it->len == 1) { // deleting the root
+ p = NULL;
+ b = 0; // unused
+ } else {
+ p = it->stack[it->len - 2];
+ assert(isbranch(p));
+ size_t len;
+ const trie_key_t *key = trie_it_key(it, &len);
+ b = twigbit(p, key, len);
+ }
+ // We could trie_it_{next,prev,...}(it) now, in case we wanted that semantics.
+ it->len = 0;
+ del_found(ns_gettrie(it), t, p, b, NULL);
+}
+
+
+/*!\file
+ *
+ * \section About copy-on-write
+ *
+ * In these notes I'll use the term "object" to refer to either the
+ * twig array of a branch, or the application's data that is referred
+ * to by a leaf's trie_val_t pointer. Note that for COW we don't care
+ * about trie node_t structs themselves, but the objects that they
+ * point to.
+ *
+ * \subsection COW states
+ *
+ * During a COW transaction an object can be in one of three states:
+ * shared, only in the old trie, or only in the new trie. When a
+ * transaction is rolled back, the only-new objects are freed; when a
+ * transaction is committed the new trie takes the place of the old
+ * one and only-old objects are freed.
+ *
+ * \subsection branch marks and regions
+ *
+ * A branch object can be marked by setting the COW flag in the first
+ * element of its twig array. Marked branches partition the trie into
+ * regions; an object's state depends on its region.
+ *
+ * The unmarked branch objects between a trie's root and the marked
+ * branches (excluding the marked branches themselves) is exclusively
+ * owned: either old-only (if you started from the old root) or
+ * new-only (if you started from the new root).
+ *
+ * Marked branch objects, and all objects reachable from marked branch
+ * objects, are in the shared region accessible from both old and new
+ * roots. All branch objects below a marked branch must be unmarked.
+ * (That is, there is at most one marked branch object on any path
+ * from the root of a trie.)
+ *
+ * Branch nodes in the new-only region can be modified in place, in
+ * the same way as an original qp trie. Branch nodes in the old-only
+ * or shared regions must not be modified.
+ *
+ * \subsection app object states
+ *
+ * The app objects reachable from the new-only and old-only regions
+ * explicitly record their state in a way determined by the
+ * application. (These app objects are reachable from the old and new
+ * roots by traversing only unmarked branch objects.)
+ *
+ * The app objects reachable from marked branch objects are implicitly
+ * shared, but their state field has an indeterminate value. If an app
+ * object was previously touched by a rolled-back transaction it may
+ * be marked shared or old-only; if it was previously touched by a
+ * committed transaction it may be marked shared or new-only.
+ *
+ * \subsection key states
+ *
+ * The memory allocated for tkey_t objects also needs to track its
+ * sharing state. They have a "cow" flag to mark when they are shared.
+ * Keys are relatively lazily copied (to make them exclusive) when
+ * their leaf node is touched by a COW mutation.
+ *
+ * [An alternative technique might be to copy them more eagerly, in
+ * cow_pushdown(), which would avoid the need for a flag bit at the
+ * cost of more allocator churn in a transaction.]
+ *
+ * \subsection outside COW
+ *
+ * When a COW transaction is not in progress, there are no marked
+ * branch objects, so everything is exclusively owned. When a COW
+ * transaction is finished (committed or rolled back), the branch
+ * marks are removed. Since they are in the shared region, this branch
+ * cleanup is visible to both old and new tries.
+ *
+ * However the state of app objects is not clean between COW
+ * transactions. When a COW transaction is committed, we traverse the
+ * old-only region to find old-only app objects that should be freed
+ * (and vice versa for rollback). In general, there will be app
+ * objects that are only reachable from the new-only region, and that
+ * have a mixture of shared and new states.
+ */
+
+/*! \brief Trie copy-on-write state */
+struct trie_cow {
+ trie_t *old;
+ trie_t *new;
+ trie_cb *mark_shared;
+ void *d;
+};
+
+/*! \brief is this a marked branch object */
+static bool cow_marked(node_t *t)
+{
+ return isbranch(t) && (twigs(t)->i & TFLAG_COW);
+}
+
+/*! \brief is this a leaf with a marked key */
+static bool cow_key(node_t *t)
+{
+ return !isbranch(t) && tkey(t)->cow;
+}
+
+/*! \brief remove mark from a branch object */
+static void clear_cow(node_t *t)
+{
+ assert(isbranch(t));
+ twigs(t)->i &= ~TFLAG_COW;
+}
+
+/*! \brief mark a node as shared
+ *
+ * For branches this marks the twig array (in COW terminology, the
+ * branch object); for leaves it uses the callback to mark the app
+ * object.
+ */
+static void mark_cow(trie_cow_t *cow, node_t *t)
+{
+ if (isbranch(t)) {
+ node_t *object = twigs(t);
+ object->i |= TFLAG_COW;
+ } else {
+ tkey_t *lkey = tkey(t);
+ lkey->cow = 1;
+ if (cow->mark_shared != NULL) {
+ trie_val_t *valp = tvalp(t);
+ cow->mark_shared(*valp, lkey->chars, lkey->len, cow->d);
+ }
+ }
+}
+
+/*! \brief push exclusive COW region down one node */
+static int cow_pushdown_one(trie_cow_t *cow, node_t *t)
+{
+ uint cc = branch_weight(t);
+ node_t *nt = mm_alloc(&cow->new->mm, sizeof(node_t) * cc);
+ if (nt == NULL)
+ return KNOT_ENOMEM;
+ /* mark all the children */
+ for (uint ci = 0; ci < cc; ++ci)
+ mark_cow(cow, twig(t, ci));
+ /* this node must be unmarked in both old and new versions */
+ clear_cow(t);
+ t->p = memcpy(nt, twigs(t), sizeof(node_t) * cc);
+ return KNOT_EOK;
+}
+
+/*! \brief push exclusive COW region to cover a whole node stack */
+static int cow_pushdown(trie_cow_t *cow, nstack_t *ns)
+{
+ node_t *new_twigs = NULL;
+ node_t *old_twigs = NULL;
+ for (uint i = 0; i < ns->len; i++) {
+ /* if we did a pushdown on the previous iteration, we
+ need to update this stack entry so it points into
+ the parent's new twigs instead of the old ones */
+ if (new_twigs != old_twigs)
+ ns->stack[i] = new_twigs + (ns->stack[i] - old_twigs);
+ if (cow_marked(ns->stack[i])) {
+ old_twigs = twigs(ns->stack[i]);
+ if (cow_pushdown_one(cow, ns->stack[i]))
+ return KNOT_ENOMEM;
+ new_twigs = twigs(ns->stack[i]);
+ } else {
+ new_twigs = NULL;
+ old_twigs = NULL;
+ /* ensure key is exclusively owned */
+ if (cow_key(ns->stack[i])) {
+ node_t oleaf = *ns->stack[i];
+ tkey_t *okey = tkey(&oleaf);
+ if(mkleaf(ns->stack[i], okey->chars, okey->len,
+ &cow->new->mm))
+ return KNOT_ENOMEM;
+ ns->stack[i]->p = oleaf.p;
+ okey->cow = 0;
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+trie_cow_t* trie_cow(trie_t *old, trie_cb *mark_shared, void *d)
+{
+ knot_mm_t *mm = &old->mm;
+ trie_t *new = mm_alloc(mm, sizeof(trie_t));
+ trie_cow_t *cow = mm_alloc(mm, sizeof(trie_cow_t));
+ if (new == NULL || cow == NULL) {
+ mm_free(mm, new);
+ mm_free(mm, cow);
+ return NULL;
+ }
+ new->mm = old->mm;
+ new->root = old->root;
+ new->weight = old->weight;
+ cow->old = old;
+ cow->new = new;
+ cow->mark_shared = mark_shared;
+ cow->d = d;
+ if (old->weight)
+ mark_cow(cow, &old->root);
+ return cow;
+}
+
+trie_t* trie_cow_new(trie_cow_t *cow)
+{
+ assert(cow != NULL);
+ return cow->new;
+}
+
+trie_val_t* trie_get_cow(trie_cow_t *cow, const trie_key_t *key, uint32_t len)
+{
+ return cow_get_ins(cow, cow->new, key, len);
+}
+
+int trie_del_cow(trie_cow_t *cow, const trie_key_t *key, uint32_t len, trie_val_t *val)
+{
+ trie_t *tbl = cow->new;
+ if (unlikely(!tbl->weight))
+ return KNOT_ENOENT;
+ { // 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;
+ trie_index_t idiff;
+ bitmap_t tbit, kbit;
+ ERR_RETURN(ns_find_branch(ns, key, len, &idiff, &tbit, &kbit));
+ if (idiff != TMAX_INDEX)
+ return KNOT_ENOENT;
+ ERR_RETURN(cow_pushdown(cow, ns));
+ node_t *t = ns->stack[ns->len - 1];
+ node_t *p = ns->len >= 2 ? ns->stack[ns->len - 2] : NULL;
+ del_found(tbl, t, p, p ? twigbit(p, key, len) : 0, val);
+ }
+ return KNOT_EOK;
+}
+
+/*! \brief clean up after a COW transaction, recursively */
+static void cow_cleanup(trie_cow_t *cow, node_t *t, trie_cb *cb, void *d)
+{
+ if (cow_marked(t)) {
+ // we have hit the shared region, so just reset the mark
+ clear_cow(t);
+ return;
+ } else if (isbranch(t)) {
+ // traverse and free the exclusive region
+ uint cc = branch_weight(t);
+ for (uint ci = 0; ci < cc; ++ci)
+ cow_cleanup(cow, twig(t, ci), cb, d);
+ mm_free(&cow->new->mm, twigs(t));
+ return;
+ } else {
+ // application must decide how to clean up its values
+ tkey_t *lkey = tkey(t);
+ if (cb != NULL) {
+ trie_val_t *valp = tvalp(t);
+ cb(*valp, lkey->chars, lkey->len, d);
+ }
+ // clean up exclusively-owned keys
+ if (lkey->cow)
+ lkey->cow = 0;
+ else
+ mm_free(&cow->new->mm, lkey);
+ return;
+ }
+}
+
+trie_t* trie_cow_commit(trie_cow_t *cow, trie_cb *cb, void *d)
+{
+ trie_t *ret = cow->new;
+ if (cow->old->weight)
+ cow_cleanup(cow, &cow->old->root, cb, d);
+ mm_free(&ret->mm, cow->old);
+ mm_free(&ret->mm, cow);
+ return ret;
+}
+
+trie_t* trie_cow_rollback(trie_cow_t *cow, trie_cb *cb, void *d)
+{
+ trie_t *ret = cow->old;
+ if (cow->new->weight)
+ cow_cleanup(cow, &cow->new->root, cb, d);
+ mm_free(&ret->mm, cow->new);
+ mm_free(&ret->mm, cow);
+ return ret;
+}
diff --git a/src/contrib/qp-trie/trie.h b/src/contrib/qp-trie/trie.h
new file mode 100644
index 0000000..d479d3e
--- /dev/null
+++ b/src/contrib/qp-trie/trie.h
@@ -0,0 +1,280 @@
+/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ Copyright (C) 2018 Tony Finch <dot@dotat.at>
+
+ 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "libknot/mm_ctx.h"
+
+/*!
+ * \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 Key for indexing tries. Sign could be flipped easily. */
+typedef uint8_t trie_key_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 Callback for cloning trie values. */
+typedef trie_val_t (*trie_dup_cb)(const trie_val_t val, knot_mm_t *mm);
+
+/*! \brief Callback for performing actions on a trie leaf
+ *
+ * Used during copy-on-write transactions
+ *
+ * \param val The value of the element to be altered
+ * \param key The key of the element to be altered
+ * \param len The length of key
+ * \param d Additional user data
+ */
+typedef void trie_cb(trie_val_t val, const trie_key_t *key, size_t len, void *d);
+
+/*! \brief Opaque type for holding the copy-on-write state for a QP-trie. */
+typedef struct trie_cow trie_cow_t;
+
+/*! \brief Create a trie instance. */
+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 Create a clone of existing trie. */
+trie_t* trie_dup(const trie_t *orig, trie_dup_cb dup_cb, knot_mm_t *mm);
+
+/*! \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 trie_key_t *key, uint32_t len);
+
+/*! \brief Search the trie including DNS wildcard semantics, returning NULL on failure.
+ *
+ * \note We assume the key is in knot_dname_lf() format, i.e. labels are ordered
+ * from root to leaf and separated by zero bytes (and no other zeros are allowed).
+ * \note Beware that DNS wildcard matching is not exactly what normal people would expect.
+ */
+trie_val_t* trie_get_try_wildcard(trie_t *tbl, const trie_key_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 trie_key_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 (optional) Value found; 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 trie_key_t *key, uint32_t len, trie_val_t **val);
+
+/*!
+ * \brief Apply a function to every trie_val_t, in order.
+ *
+ * \return KNOT_EOK if success or KNOT_E* if error.
+ */
+int trie_apply(trie_t *tbl, int (*f)(trie_val_t *, void *), void *d);
+
+/*!
+ * \brief Remove an item, returning KNOT_EOK if succeeded or KNOT_ENOENT if not found.
+ *
+ * If val!=NULL and deletion succeeded, the deleted value is set.
+ */
+int trie_del(trie_t *tbl, const trie_key_t *key, uint32_t len, trie_val_t *val);
+
+
+/*! \brief Create a new iterator pointing to the first element (if any).
+ *
+ * trie_it_* functions deal with these iterators capable of walking and jumping
+ * over the trie. Note that any modification to key-set stored by the trie
+ * will in general invalidate all iterators and you will need to begin anew.
+ * (It won't be detected - you may end up reading freed memory, etc.)
+ */
+trie_it_t* trie_it_begin(trie_t *tbl);
+
+/*! \brief Test if the iterator has gone "past the end" (and points nowhere). */
+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 Copy the iterator. See the warning in trie_it_begin(). */
+trie_it_t *trie_it_clone(const trie_it_t *it);
+
+/*!
+ * \brief Return pointer to the key of the current element.
+ *
+ * \note The len is uint32_t internally but size_t is better for our usage
+ * as it is without an additional type conversion.
+ */
+const trie_key_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);
+
+/*!
+ * \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.
+ *
+ * \TODO: in most iterator operations, ENOMEM is very unlikely
+ * but it leads to a _finished() iterator (silently).
+ * Perhaps the functions should simply return KNOT_E*
+ */
+void trie_it_next(trie_it_t *it);
+/*! \brief Advance the iterator to the previous element. See trie_it_next(). */
+void trie_it_prev(trie_it_t *it);
+
+/*! \brief Advance iterator to the next element, looping to first after last. */
+void trie_it_next_loop(trie_it_t *it);
+/*! \brief Advance iterator to the previous element, looping to last after first. */
+void trie_it_prev_loop(trie_it_t *it);
+
+/*! \brief Advance iterator to the next element while ignoring the subtree.
+ *
+ * \note Another formulation: skip keys that are prefixed by the current key.
+ * \TODO: name, maybe _unprefixed? The thing is that in the "subtree" meaning
+ * doesn't correspond to how the pointers go in the implementation,
+ * but we may not care much for implementation in the API...
+ */
+void trie_it_next_nosub(trie_it_t *it);
+
+/*! \brief Advance iterator to the longest prefix of the current key.
+ *
+ * \TODO: name, maybe _prefix? Arguments similar to _nosub vs. _unprefixed.
+ */
+void trie_it_parent(trie_it_t *it);
+
+/*! \brief trie_get_leq() but with an iterator. */
+int trie_it_get_leq(trie_it_t *it, const trie_key_t *key, uint32_t len);
+
+/*! \brief Remove the current element. The iterator will get trie_it_finished() */
+void trie_it_del(trie_it_t *it);
+
+
+/*! \brief Start a COW transaction
+ *
+ * A copy-on-write transaction starts by obtaining a write lock (in
+ * your application code) followed by a call to trie_cow(). This
+ * creates a shared clone of the trie and saves both old and new roots
+ * in the COW context.
+ *
+ * During the COW transaction, you call trie_cow_ins() or
+ * trie_cow_del() as necessary. These calls ensure that the relevant
+ * parts of the (new) trie are copied so that they can be modified
+ * freely.
+ *
+ * Your trie_val_t objects must be able to distinguish their
+ * reachability, either shared, or old-only, or new-only. Before a COW
+ * transaction the reachability of your objects is indeterminate.
+ * During a transaction, any trie_val_t objects that might be affected
+ * (because they are adjacent to a trie_get_cow() or trie_del_cow())
+ * are first marked as shared using the callback you pass to
+ * trie_cow().
+ *
+ * When the transaction is complete, to commit, call trie_cow_new() to
+ * get the new root, swap the old and new trie roots (e.g. with
+ * rcu_xchg_pointer()), wait for readers to finish with the old trie
+ * (e.g. using synchronize_rcu()), then call trie_cow_commit(). For a
+ * rollback, you can just call trie_cow_rollback() without waiting
+ * since that doesn't conflict with readers. After trie_cow_commit()
+ * or trie_cow_rollback() have finished, you can release your write
+ * lock.
+ *
+ * Concurrent reading of the old trie is allowed during a transaction
+ * provided that it is known when all readers have finished with the
+ * old version, e.g. using rcu_read_lock() and rcu_read_unlock().
+ * There must be only one write transaction at a time.
+ *
+ * \param old the old trie
+ * \param mark_shared callback to mark a leaf as shared (can be NULL)
+ * \param d extra data for the callback
+ * \return a pointer to a COW context,
+ * or NULL if there was a failure
+ */
+trie_cow_t* trie_cow(trie_t *old, trie_cb *mark_shared, void *d);
+
+/*! \brief get the new trie from a COW context */
+trie_t* trie_cow_new(trie_cow_t *cow);
+
+/*! \brief variant of trie_get_ins() for use during COW transactions
+ *
+ * As necessary, this copies path from the root of the trie to the
+ * leaf, so that it is no longer shared. Any leaves adjacent to this
+ * path are marked as shared using the mark_shared callback passed to
+ * trie_cow().
+ *
+ * It is your responsibility to COW your trie_val_t objects. If you copy an
+ * object you must change the original's reachability from shared to old-only.
+ * New objects (including copies) must have new-only reachability.
+ */
+trie_val_t* trie_get_cow(trie_cow_t *cow, const trie_key_t *key, uint32_t len);
+
+/*!
+ * \brief variant of trie_del() for use during COW transactions
+ *
+ * The mark_shared callback is invoked as necessary, in the same way
+ * as trie_get_cow().
+ *
+ * Returns KNOT_EOK if the key was removed or KNOT_ENOENT if not found.
+ * If val!=NULL and deletion succeeded, the *val is set to the deleted
+ * value pointer.
+ */
+int trie_del_cow(trie_cow_t *cow, const trie_key_t *key, uint32_t len, trie_val_t *val);
+
+/*! \brief clean up the old trie after committing a COW transaction
+ *
+ * Your callback is invoked for any trie_val_t objects that might need
+ * cleaning up; you must free any objects you have marked as old-only
+ * and retain objects with shared reachability.
+ *
+ * \note The callback can be NULL.
+ *
+ * The cow object is free()d, and the new trie root is returned.
+ */
+trie_t* trie_cow_commit(trie_cow_t *cow, trie_cb *cb, void *d);
+
+/*! \brief clean up the new trie after rolling back a COW transaction
+ *
+ * Your callback is invoked for any trie_val_t objects that might need
+ * cleaning up; you must free any objects you have marked as new-only
+ * and retain objects with shared reachability.
+ *
+ * \note The callback can be NULL.
+ *
+ * The cow object is free()d, and the old trie root is returned.
+ */
+trie_t* trie_cow_rollback(trie_cow_t *cow, trie_cb *cb, void *d);
diff --git a/src/contrib/semaphore.c b/src/contrib/semaphore.c
new file mode 100644
index 0000000..ad50dcc
--- /dev/null
+++ b/src/contrib/semaphore.c
@@ -0,0 +1,80 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "semaphore.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#if defined(__APPLE__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+void knot_sem_init(knot_sem_t *sem, unsigned int value)
+{
+ int ret = sem_init(&sem->semaphore, 1, value);
+ if (ret == 0) {
+ sem->status = -1;
+ } else {
+ sem->status = value;
+ sem->status_lock = malloc(sizeof(*sem->status_lock));
+ pthread_mutex_init(&sem->status_lock->mutex, NULL);
+ pthread_cond_init(&sem->status_lock->cond, NULL);
+ }
+}
+
+void knot_sem_wait(knot_sem_t *sem)
+{
+ if (sem->status < 0) {
+ int semret;
+ do {
+ semret = sem_wait(&sem->semaphore);
+ } while (semret != 0); // repeat wait as it might be interrupted by a signal
+ } else {
+ pthread_mutex_lock(&sem->status_lock->mutex);
+ while (sem->status == 0) {
+ pthread_cond_wait(&sem->status_lock->cond, &sem->status_lock->mutex);
+ }
+ sem->status--;
+ pthread_mutex_unlock(&sem->status_lock->mutex);
+ }
+}
+
+void knot_sem_post(knot_sem_t *sem)
+{
+ if (sem->status < 0) {
+ int semret = sem_post(&sem->semaphore);
+ (void)semret;
+ assert(semret == 0);
+ } else {
+ pthread_mutex_lock(&sem->status_lock->mutex);
+ sem->status++;
+ pthread_cond_signal(&sem->status_lock->cond);
+ pthread_mutex_unlock(&sem->status_lock->mutex);
+ }
+}
+
+void knot_sem_destroy(knot_sem_t *sem)
+{
+ knot_sem_wait(sem);
+ if (sem->status < 0) {
+ sem_destroy(&sem->semaphore);
+ } else {
+ pthread_cond_destroy(&sem->status_lock->cond);
+ pthread_mutex_destroy(&sem->status_lock->mutex);
+ free(sem->status_lock);
+ }
+}
diff --git a/src/contrib/semaphore.h b/src/contrib/semaphore.h
new file mode 100644
index 0000000..be49b7f
--- /dev/null
+++ b/src/contrib/semaphore.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <pthread.h>
+#include <semaphore.h>
+
+#pragma once
+
+typedef struct {
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+} knot_sem_mutex_t;
+
+typedef struct {
+ int status;
+ union {
+ sem_t semaphore;
+ knot_sem_mutex_t *status_lock;
+ };
+} knot_sem_t;
+
+void knot_sem_init(knot_sem_t *sem, unsigned int value);
+
+void knot_sem_wait(knot_sem_t *sem);
+
+void knot_sem_post(knot_sem_t *sem);
+
+void knot_sem_destroy(knot_sem_t *sem);
diff --git a/src/contrib/sockaddr.c b/src/contrib/sockaddr.c
new file mode 100644
index 0000000..2b3218b
--- /dev/null
+++ b/src/contrib/sockaddr.c
@@ -0,0 +1,368 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#include "libknot/errcode.h"
+#include "contrib/sockaddr.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/macros.h"
+#include "contrib/musl/inet_ntop.h"
+
+int sockaddr_len(const struct sockaddr_storage *ss)
+{
+ if (ss == NULL) {
+ return 0;
+ }
+
+ switch(ss->ss_family) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_UNIX:
+ return sizeof(struct sockaddr_un);
+ default:
+ return 0;
+ }
+}
+
+static int cmp_ipv4(const struct sockaddr_in *a, const struct sockaddr_in *b,
+ bool ignore_port)
+{
+ if (a->sin_addr.s_addr < b->sin_addr.s_addr) {
+ return -1;
+ } else if (a->sin_addr.s_addr > b->sin_addr.s_addr) {
+ return 1;
+ } else {
+ return ignore_port ? 0 : a->sin_port - b->sin_port;
+ }
+}
+
+static int cmp_ipv6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b,
+ bool ignore_port)
+{
+ int ret = memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(struct in6_addr));
+ if (ret == 0) {
+ ret = ignore_port ? 0 : a->sin6_port - b->sin6_port;
+ }
+
+ return ret;
+}
+
+static int cmp_unix(const struct sockaddr_un *a, const struct sockaddr_un *b)
+{
+ int len_a = strnlen(a->sun_path, sizeof(a->sun_path));
+ int len_b = strnlen(b->sun_path, sizeof(b->sun_path));
+ int len_min = len_a <= len_b ? len_a : len_b;
+
+ int ret = strncmp(a->sun_path, b->sun_path, len_min);
+ if (ret == 0) {
+ ret = len_a - len_b;
+ }
+
+ return ret;
+}
+
+int sockaddr_cmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
+ bool ignore_port)
+{
+ assert(a);
+ assert(b);
+ if (a->ss_family != b->ss_family) {
+ return (int)a->ss_family - (int)b->ss_family;
+ }
+
+ switch (a->ss_family) {
+ case AF_UNSPEC:
+ return 0;
+ case AF_INET:
+ return cmp_ipv4((struct sockaddr_in *)a, (struct sockaddr_in *)b,
+ ignore_port);
+ case AF_INET6:
+ return cmp_ipv6((struct sockaddr_in6 *)a, (struct sockaddr_in6 *)b,
+ ignore_port);
+ case AF_UNIX:
+ return cmp_unix((struct sockaddr_un *)a, (struct sockaddr_un *)b);
+ default:
+ return 1;
+ }
+}
+
+int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port)
+{
+ if (ss == NULL || straddr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Set family and port. */
+ memset(ss, 0, sizeof(*ss));
+ ss->ss_family = family;
+ sockaddr_port_set(ss, port);
+
+ /* Initialize address depending on address family. */
+ if (family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
+ if (inet_pton(family, straddr, &ipv6->sin6_addr) < 1) {
+ return KNOT_ERROR;
+ }
+ return KNOT_EOK;
+ } else if (family == AF_INET) {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
+ if (inet_pton(family, straddr, &ipv4->sin_addr) < 1) {
+ return KNOT_ERROR;
+ }
+ return KNOT_EOK;
+ } else if (family == AF_UNIX) {
+ struct sockaddr_un *un = (struct sockaddr_un *)ss;
+ size_t ret = strlcpy(un->sun_path, straddr, sizeof(un->sun_path));
+ if (ret >= sizeof(un->sun_path)) {
+ return KNOT_ESPACE;
+ }
+ return KNOT_EOK;
+ }
+
+ return KNOT_EINVAL;
+}
+
+void *sockaddr_raw(const struct sockaddr_storage *ss, size_t *addr_size)
+{
+ if (ss == NULL || addr_size == NULL) {
+ return NULL;
+ }
+
+ if (ss->ss_family == AF_INET) {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
+ *addr_size = sizeof(ipv4->sin_addr);
+ return &ipv4->sin_addr;
+ } else if (ss->ss_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
+ *addr_size = sizeof(ipv6->sin6_addr);
+ return &ipv6->sin6_addr;
+ } else if (ss->ss_family == AF_UNIX) {
+ struct sockaddr_un *un = (struct sockaddr_un *)ss;
+ *addr_size = sizeof(un->sun_path);
+ return un->sun_path;
+ } else {
+ return NULL;
+ }
+}
+
+int sockaddr_set_raw(struct sockaddr_storage *ss, int family,
+ const uint8_t *raw_addr, size_t raw_addr_size)
+{
+ if (ss == NULL || raw_addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(ss, 0, sizeof(*ss));
+ ss->ss_family = family;
+
+ size_t ss_size = 0;
+ void *ss_data = sockaddr_raw(ss, &ss_size);
+ if (ss_data == NULL ||
+ (family != AF_UNIX && ss_size != raw_addr_size) ||
+ (family == AF_UNIX && ss_size <= raw_addr_size)) {
+ return KNOT_EINVAL;
+ }
+
+ memcpy(ss_data, raw_addr, raw_addr_size);
+
+ return KNOT_EOK;
+}
+
+int sockaddr_tostr(char *buf, size_t maxlen, const struct sockaddr_storage *ss)
+{
+ if (ss == NULL || buf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const char *out = NULL;
+
+ /* Convert network address string. */
+ if (ss->ss_family == AF_INET6) {
+ const struct sockaddr_in6 *s = (const struct sockaddr_in6 *)ss;
+ out = knot_inet_ntop(ss->ss_family, &s->sin6_addr, buf, maxlen);
+ } else if (ss->ss_family == AF_INET) {
+ const struct sockaddr_in *s = (const struct sockaddr_in *)ss;
+ out = knot_inet_ntop(ss->ss_family, &s->sin_addr, buf, maxlen);
+ } else if (ss->ss_family == AF_UNIX) {
+ const struct sockaddr_un *s = (const struct sockaddr_un *)ss;
+ const char *path = (s->sun_path[0] != '\0' ? s->sun_path : "UNIX socket");
+ size_t ret = strlcpy(buf, path, maxlen);
+ out = (ret < maxlen) ? buf : NULL;
+ } else {
+ *buf = '\0';
+ return KNOT_EINVAL;
+ }
+
+ if (out == NULL) {
+ *buf = '\0';
+ return KNOT_ESPACE;
+ }
+
+ /* Write separator and port. */
+ int written = strlen(buf);
+ int port = sockaddr_port(ss);
+ if (port > 0) {
+ int ret = snprintf(&buf[written], maxlen - written, "@%d", port);
+ if (ret <= 0 || (size_t)ret >= maxlen - written) {
+ *buf = '\0';
+ return KNOT_ESPACE;
+ }
+
+ written += ret;
+ }
+
+ return written;
+}
+
+int sockaddr_port(const struct sockaddr_storage *ss)
+{
+ if (ss == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ return ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
+ } else if (ss->ss_family == AF_INET) {
+ return ntohs(((struct sockaddr_in *)ss)->sin_port);
+ } else {
+ return KNOT_EINVAL;
+ }
+}
+
+void sockaddr_port_set(struct sockaddr_storage *ss, uint16_t port)
+{
+ if (ss == NULL) {
+ return;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ ((struct sockaddr_in6 *)ss)->sin6_port = htons(port);
+ } else if (ss->ss_family == AF_INET) {
+ ((struct sockaddr_in *)ss)->sin_port = htons(port);
+ }
+}
+
+char *sockaddr_hostname(void)
+{
+ /* Fetch hostname. */
+ char host[256] = "";
+ if (gethostname(host, sizeof(host)) != 0) {
+ return NULL;
+ }
+ /* Just to be sure. */
+ host[sizeof(host) - 1] = '\0';
+
+ /* Fetch canonical name for this address/DNS. */
+ struct addrinfo hints, *info = NULL;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_CANONNAME;
+ if (getaddrinfo(host, "domain", &hints, &info) != 0) {
+ return NULL;
+ }
+
+ /* Fetch first valid hostname. */
+ char *hname = NULL;
+ struct addrinfo *p = NULL;
+ for (p = info; p != NULL; p = p->ai_next) {
+ if (p->ai_canonname) {
+ hname = strdup(p->ai_canonname);
+ break;
+ }
+ }
+
+ /* No valid hostname found, resort to gethostname() result */
+ if (hname == NULL) {
+ hname = strdup(host);
+ }
+
+ freeaddrinfo(info);
+ return hname;
+}
+
+bool sockaddr_is_any(const struct sockaddr_storage *ss)
+{
+ if (ss == NULL) {
+ return false;
+ }
+
+ if (ss->ss_family == AF_INET) {
+ const struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss;
+ return ipv4->sin_addr.s_addr == INADDR_ANY;
+ }
+
+ if (ss->ss_family == AF_INET6) {
+ const struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss;
+ return memcmp(&ipv6->sin6_addr, &in6addr_any, sizeof(ipv6->sin6_addr)) == 0;
+ }
+
+ return false;
+}
+
+bool sockaddr_net_match(const struct sockaddr_storage *ss1,
+ const struct sockaddr_storage *ss2,
+ unsigned prefix)
+{
+ if (ss1 == NULL || ss2 == NULL) {
+ return false;
+ }
+
+ if (ss1->ss_family != ss2->ss_family) {
+ return false;
+ }
+
+ size_t raw_len = 0;
+ const uint8_t *raw_1 = sockaddr_raw(ss1, &raw_len);
+ const uint8_t *raw_2 = sockaddr_raw(ss2, &raw_len);
+
+ prefix = MIN(prefix, raw_len * 8);
+ unsigned bytes = prefix / 8;
+ unsigned bits = prefix % 8;
+
+ /* Compare full bytes. */
+ if (memcmp(raw_1, raw_2, bytes) != 0) {
+ return false;
+ }
+
+ /* Compare last partial byte. */
+ return bits == 0 ||
+ (raw_1[bytes] >> (8 - bits) == raw_2[bytes] >> (8 - bits));
+}
+
+bool sockaddr_range_match(const struct sockaddr_storage *ss,
+ const struct sockaddr_storage *ss_min,
+ const struct sockaddr_storage *ss_max)
+{
+ if (ss == NULL || ss_min == NULL || ss_max == NULL) {
+ return false;
+ }
+
+ if (ss_min->ss_family != ss_max->ss_family ||
+ ss_min->ss_family != ss->ss_family) {
+ return false;
+ }
+
+ return sockaddr_cmp(ss, ss_min, true) >= 0 &&
+ sockaddr_cmp(ss, ss_max, true) <= 0;
+}
diff --git a/src/contrib/sockaddr.h b/src/contrib/sockaddr.h
new file mode 100644
index 0000000..f9228d7
--- /dev/null
+++ b/src/contrib/sockaddr.h
@@ -0,0 +1,160 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <unistd.h>
+
+/* Subnet maximum prefix length. */
+#define IPV4_PREFIXLEN 32
+#define IPV6_PREFIXLEN 128
+
+/* Address string "address[@port]" maximum length. */
+#define SOCKADDR_STRLEN_EXT (1 + 6) /* '@', 5 digits number, \0 */
+#define SOCKADDR_STRLEN (sizeof(struct sockaddr_un) + SOCKADDR_STRLEN_EXT)
+
+/*!
+ * \brief Calculate current structure length based on address family.
+ *
+ * \param ss Socket address.
+ *
+ * \return Number of bytes or error code.
+ */
+int sockaddr_len(const struct sockaddr_storage *ss);
+
+/*!
+ * \brief Compare addresses.
+ *
+ * \param a First address.
+ * \param b Second address.
+ * \param ignore_port Ignore port indication.
+ *
+ * \return like memcmp(3)
+ */
+int sockaddr_cmp(const struct sockaddr_storage *a, const struct sockaddr_storage *b,
+ bool ignore_port);
+
+/*!
+ * \brief Set address and port.
+ *
+ * \param ss Socket address.
+ * \param family Address family.
+ * \param straddr IP address in string format.
+ * \param port Port.
+ *
+ * \return KNOT_EOK on success or an error code.
+ */
+int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port);
+
+/*!
+ * \brief Return raw network address in network byte order.
+ *
+ * \param[in] ss Socket address.
+ * \param[out] addr_size Address length.
+ *
+ * \return Pointer to binary buffer of size addr_size.
+ */
+void *sockaddr_raw(const struct sockaddr_storage *ss, size_t *addr_size);
+
+/*!
+ * \brief Set raw address.
+ *
+ * \param ss Socket address.
+ * \param family Address family.
+ * \param raw_addr IP address in binary format.
+ * \param raw_addr_size Size of the binary address.
+ *
+ * \return KNOT_EOK on success or an error code.
+ */
+int sockaddr_set_raw(struct sockaddr_storage *ss, int family,
+ const uint8_t *raw_addr, size_t raw_addr_size);
+
+/*!
+ * \brief Return string representation of socket address.
+ *
+ * \note String format: \<address>[@<port>], f.e. '127.0.0.1@53'
+ *
+ * \param buf Destination for string representation.
+ * \param maxlen Maximum number of written bytes.
+ * \param ss Socket address.
+ *
+ * \return Number of bytes written on success, error code on failure.
+ */
+int sockaddr_tostr(char *buf, size_t maxlen, const struct sockaddr_storage *ss);
+
+/*!
+ * \brief Return port number from address.
+ *
+ * \param ss Socket address.
+ *
+ * \return Port number or error code.
+ */
+int sockaddr_port(const struct sockaddr_storage *ss);
+
+/*!
+ * \brief Set port number.
+ *
+ * \param ss Socket address.
+ * \param port Port to set.
+ */
+void sockaddr_port_set(struct sockaddr_storage *ss, uint16_t port);
+
+/*!
+ * \brief Get host FQDN address.
+ *
+ * \return Hostname string or NULL.
+ */
+char *sockaddr_hostname(void);
+
+/*!
+ * \brief Check if address is ANY address.
+ *
+ * \param ss Socket address.
+ */
+bool sockaddr_is_any(const struct sockaddr_storage *ss);
+
+/*!
+ * \brief Check if two addresses match the given network prefix.
+ *
+ * \param ss1 First address.
+ * \param ss2 Second address.
+ * \param prefix Prefix length.
+ *
+ * \return True on match.
+ */
+bool sockaddr_net_match(const struct sockaddr_storage *ss1,
+ const struct sockaddr_storage *ss2,
+ unsigned prefix);
+
+/*!
+ * \brief Check if the address is within the given address range (inclusive).
+ *
+ * \param ss Address to check.
+ * \param ss_min Minimum address.
+ * \param ss_max Maximum address.
+ *
+ * \return True on match.
+ */
+bool sockaddr_range_match(const struct sockaddr_storage *ss,
+ const struct sockaddr_storage *ss_min,
+ const struct sockaddr_storage *ss_max);
diff --git a/src/contrib/spinlock.h b/src/contrib/spinlock.h
new file mode 100644
index 0000000..837f500
--- /dev/null
+++ b/src/contrib/spinlock.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2021 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 Multiplatform spinlock.
+ */
+
+#pragma once
+
+#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
+/* Not tested and activated yet. */
+/* #define HAVE_STDATOMIC */
+#endif
+
+#if defined(__APPLE__)
+# if defined(MAC_OS_X_VERSION_10_12) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
+# define APPLE_SPIN_NEW
+# else
+# define APPLE_SPIN_OLD
+# endif /* MAC_OS_X_VERSION_10_12 ... */
+#endif /* __APPLE__ */
+
+#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
+# include <stdbool.h>
+#elif defined(HAVE_STDATOMIC)
+# include <stdbool.h>
+# include <stdatomic.h>
+#elif defined(APPLE_SPIN_NEW)
+# include <os/lock.h>
+#elif defined(APPLE_SPIN_OLD)
+# include <libkern/OSAtomic.h>
+#else /* POSIX pthread spinlock. */
+# include <pthread.h>
+#endif
+
+/*! \brief Spinlock lock variable type. */
+typedef
+#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
+ bool /*!< Spinlock lock - a simple & fast atomic version. */
+#elif defined(HAVE_STDATOMIC)
+ atomic_bool /*!< Spinlock lock - a simple & fast atomic version, C11 */
+#elif defined(APPLE_SPIN_NEW)
+ os_unfair_lock /*!< Spinlock lock - a newer macOS version (macOS >= 10.12). */
+#elif defined(APPLE_SPIN_OLD)
+ OSSpinLock /*!< Spinlock lock - an older macOS version (macOS < 10.12). */
+#else /* POSIX */
+ pthread_spinlock_t /*!< Spinlock lock - a POSIX pthread version. */
+#endif
+ knot_spin_t;
+
+/*! \brief Initialize the spinlock pointed to by the parameter "lock". */
+static inline void knot_spin_init(knot_spin_t *lock)
+{
+#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC)
+ *lock = false;
+#elif defined(HAVE_STDATOMIC)
+ atomic_init(lock, false);
+#elif defined(APPLE_SPIN_NEW)
+ *lock = OS_UNFAIR_LOCK_INIT;
+#elif defined(APPLE_SPIN_OLD)
+ *lock = OS_SPINLOCK_INIT;
+#else /* POSIX */
+ pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE);
+#endif
+}
+
+/*! \brief Destroy the spinlock pointed to by the parameter "lock". */
+static inline void knot_spin_destroy(knot_spin_t *lock)
+{
+#if defined(HAVE_SYNC_ATOMIC) || defined(HAVE_ATOMIC) || defined(HAVE_STDATOMIC) || \
+ defined(APPLE_SPIN_NEW) || defined(APPLE_SPIN_OLD)
+ /* Nothing needed here. */
+#else /* POSIX */
+ pthread_spin_destroy(lock);
+#endif
+}
+
+/*! \brief Lock the spinlock pointed to by the parameter "lock". */
+static inline void knot_spin_lock(knot_spin_t *lock)
+{
+#if defined(HAVE_SYNC_ATOMIC)
+ while (__sync_lock_test_and_set(lock, 1)) {
+ }
+#elif defined(HAVE_ATOMIC)
+ int expected = 0;
+ while (!__atomic_compare_exchange_n(lock, &expected, 1, false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
+ expected = 0;
+ }
+#elif defined(HAVE_STDATOMIC)
+ int expected = 0;
+ while (!atomic_compare_exchange_strong(lock, &expected, false)) {
+ expected = 0;
+ }
+#elif defined(APPLE_SPIN_NEW)
+ os_unfair_lock_lock(lock);
+#elif defined(APPLE_SPIN_OLD)
+ OSSpinLockLock(lock);
+#else /* POSIX */
+ pthread_spin_lock(lock);
+#endif
+}
+
+/*! \brief Unlock the spinlock pointed to by the parameter "lock". */
+static inline void knot_spin_unlock(knot_spin_t *lock)
+{
+#if defined(HAVE_SYNC_ATOMIC)
+ __sync_lock_release(lock);
+#elif defined(HAVE_ATOMIC)
+ __atomic_clear(lock, __ATOMIC_RELAXED);
+#elif defined(HAVE_STDATOMIC)
+ atomic_store(lock, false);
+#elif defined(APPLE_SPIN_NEW)
+ os_unfair_lock_unlock(lock);
+#elif defined(APPLE_SPIN_OLD)
+ OSSpinLockUnlock(lock);
+#else /* POSIX */
+ pthread_spin_unlock(lock);
+#endif
+}
diff --git a/src/contrib/string.c b/src/contrib/string.c
new file mode 100644
index 0000000..272116e
--- /dev/null
+++ b/src/contrib/string.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(HAVE_EXPLICIT_BZERO)
+ #if defined(HAVE_BSD_STRING_H)
+ #include <bsd/string.h>
+ #endif
+ /* #include <string.h> is needed. */
+#elif defined(HAVE_EXPLICIT_MEMSET)
+ /* #include <string.h> is needed. */
+#elif defined(HAVE_GNUTLS_MEMSET)
+ #include <gnutls/gnutls.h>
+#else
+ #define USE_CUSTOM_MEMSET
+#endif
+
+#include "contrib/string.h"
+#include "contrib/ctype.h"
+#include "contrib/tolower.h"
+
+uint8_t *memdup(const uint8_t *data, size_t data_size)
+{
+ uint8_t *result = (uint8_t *)malloc(data_size);
+ if (!result) {
+ return NULL;
+ }
+
+ return memcpy(result, data, data_size);
+}
+
+int strmemcmp(const char *str, const uint8_t *mem, size_t mem_size)
+{
+ if (mem_size == 0) {
+ return 1;
+ }
+ size_t cmp_len = strnlen(str, mem_size - 1) + 1;
+ return memcmp(str, mem, cmp_len);
+}
+
+char *sprintf_alloc(const char *fmt, ...)
+{
+ char *strp = NULL;
+ va_list ap;
+
+ va_start(ap, fmt);
+ int ret = vasprintf(&strp, fmt, ap);
+ va_end(ap);
+
+ if (ret < 0) {
+ return NULL;
+ }
+ return strp;
+}
+
+char *strcdup(const char *s1, const char *s2)
+{
+ if (!s1 || !s2) {
+ return NULL;
+ }
+
+ size_t s1len = strlen(s1);
+ size_t s2len = strlen(s2);
+ size_t nlen = s1len + s2len + 1;
+
+ char* dst = malloc(nlen);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ memcpy(dst, s1, s1len);
+ memcpy(dst + s1len, s2, s2len + 1);
+ return dst;
+}
+
+char *strstrip(const char *str)
+{
+ // leading white-spaces
+ const char *scan = str;
+ while (is_space(scan[0])) {
+ scan += 1;
+ }
+
+ // trailing white-spaces
+ size_t len = strlen(scan);
+ while (len > 0 && is_space(scan[len - 1])) {
+ len -= 1;
+ }
+
+ char *trimmed = malloc(len + 1);
+ if (!trimmed) {
+ return NULL;
+ }
+
+ memcpy(trimmed, scan, len);
+ trimmed[len] = '\0';
+
+ return trimmed;
+}
+
+void strtolower(char *str)
+{
+ if (str == NULL) {
+ return;
+ }
+
+ for (char *it = str; *it != '\0'; ++it) {
+ *it = knot_tolower(*it);
+ }
+}
+
+int const_time_memcmp(const void *s1, const void *s2, size_t n)
+{
+ volatile uint8_t equal = 0;
+
+ for (size_t i = 0; i < n; i++) {
+ equal |= ((uint8_t *)s1)[i] ^ ((uint8_t *)s2)[i];
+ }
+
+ return equal;
+}
+
+#if defined(USE_CUSTOM_MEMSET)
+typedef void *(*memset_t)(void *, int, size_t);
+static volatile memset_t volatile_memset = memset;
+#endif
+
+void *memzero(void *s, size_t n)
+{
+#if defined(HAVE_EXPLICIT_BZERO) /* In OpenBSD since 5.5. */
+ /* In FreeBSD since 11.0. */
+ /* In glibc since 2.25. */
+ /* In DragonFly BSD since 5.5. */
+# if defined(__has_feature)
+# if __has_feature(memory_sanitizer)
+ #warning "Memory sanitizer detected. Using bzero() instead of explicit_bzero()."
+ bzero(s, n);
+# else
+ explicit_bzero(s, n);
+# endif
+# else
+ explicit_bzero(s, n);
+# endif
+ return s;
+#elif defined(HAVE_EXPLICIT_MEMSET) /* In NetBSD since 7.0. */
+ return explicit_memset(s, 0, n);
+#elif defined(HAVE_GNUTLS_MEMSET) /* In GnuTLS since 3.4.0. */
+ gnutls_memset(s, 0, n);
+ return s;
+#else /* Knot custom solution as a fallback. */
+ /* Warning: the use of the return value is *probably* needed
+ * so as to avoid the volatile_memset() to be optimized out.
+ */
+ return volatile_memset(s, 0, n);
+#endif
+}
+
+static const char BIN_TO_HEX[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+char *bin_to_hex(const uint8_t *bin, size_t bin_len, bool upper_case)
+{
+ if (bin == NULL) {
+ return NULL;
+ }
+
+ size_t hex_size = bin_len * 2;
+ char *hex = malloc(hex_size + 1);
+ if (hex == NULL) {
+ return NULL;
+ }
+
+ unsigned offset = upper_case ? 16 : 0;
+ for (size_t i = 0; i < bin_len; i++) {
+ hex[2 * i] = BIN_TO_HEX[offset + (bin[i] >> 4)];
+ hex[2 * i + 1] = BIN_TO_HEX[offset + (bin[i] & 0x0f)];
+ }
+ hex[hex_size] = '\0';
+
+ return hex;
+}
+
+/*!
+ * Convert HEX character to numeric value (assumes valid input).
+ */
+static uint8_t hex_to_number(const char hex)
+{
+ if (hex >= '0' && hex <= '9') {
+ return hex - '0';
+ } else if (hex >= 'a' && hex <= 'f') {
+ return hex - 'a' + 10;
+ } else {
+ assert(hex >= 'A' && hex <= 'F');
+ return hex - 'A' + 10;
+ }
+}
+
+uint8_t *hex_to_bin(const char *hex, size_t *out_len)
+{
+ if (hex == NULL || out_len == NULL) {
+ return NULL;
+ }
+
+ size_t hex_len = strlen(hex);
+ if (hex_len % 2 != 0) {
+ return NULL;
+ }
+
+ size_t bin_len = hex_len / 2;
+ uint8_t *bin = malloc(bin_len + 1);
+ if (bin == NULL) {
+ return NULL;
+ }
+
+ for (size_t i = 0; i < bin_len; i++) {
+ if (!is_xdigit(hex[2 * i]) || !is_xdigit(hex[2 * i + 1])) {
+ free(bin);
+ return NULL;
+ }
+ uint8_t high = hex_to_number(hex[2 * i]);
+ uint8_t low = hex_to_number(hex[2 * i + 1]);
+ bin[i] = high << 4 | low;
+ }
+
+ *out_len = bin_len;
+
+ return bin;
+}
diff --git a/src/contrib/string.h b/src/contrib/string.h
new file mode 100644
index 0000000..ad3c990
--- /dev/null
+++ b/src/contrib/string.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2022 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 String manipulations.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/*!
+ * \brief Create a copy of a binary buffer.
+ *
+ * Like \c strdup, but for binary data.
+ */
+uint8_t *memdup(const uint8_t *data, size_t data_size);
+
+/*!
+ * \brief Compare a zero-terminated string with fixed-size memory.
+ */
+int strmemcmp(const char *str, const uint8_t *mem, size_t mem_size);
+
+/*!
+ * \brief Format string and take care of allocating memory.
+ *
+ * \note sprintf(3) manual page reference implementation.
+ *
+ * \param fmt Message format.
+ * \return formatted message or NULL.
+ */
+char *sprintf_alloc(const char *fmt, ...);
+
+/*!
+ * \brief Create new string from a concatenation of s1 and s2.
+ *
+ * \param s1 First string.
+ * \param s2 Second string.
+ *
+ * \retval Newly allocated string on success.
+ * \retval NULL on error.
+ */
+char *strcdup(const char *s1, const char *s2);
+
+/*!
+ * \brief Create a copy of a string skipping leading and trailing white spaces.
+ *
+ * \return Newly allocated string, NULL in case of error.
+ */
+char *strstrip(const char *str);
+
+/*!
+ * \brief Convert upper-case letters to lower-case in a string.
+ */
+void strtolower(char *str);
+
+/*!
+ * \brief Compare data in time based on string length.
+ * This function just checks for (in)equality not for relation
+ *
+ * \param s1 The first address to compare.
+ * \param s2 The second address to compare.
+ * \param n The size of memory to compare.
+ *
+ * \return Non zero on difference and zero if the buffers are identical.
+ */
+int const_time_memcmp(const void *s1, const void *s2, size_t n);
+
+/*!
+ * \brief Fill memory with zeroes.
+ *
+ * Inspired by OPENSSL_cleanse. Such a memset shouldn't be optimized out.
+ *
+ * \param s The address to fill.
+ * \param n The size of memory to fill.
+ *
+ * \return Pointer to the memory.
+ */
+void *memzero(void *s, size_t n);
+
+/*!
+ * \brief Convert binary data to hexadecimal string.
+ */
+char *bin_to_hex(const uint8_t *bin, size_t bin_len, bool upper_case);
+
+/*!
+ * \brief Convert hex encoded string to binary data.
+ */
+uint8_t *hex_to_bin(const char *hex, size_t *out_len);
diff --git a/src/contrib/strtonum.h b/src/contrib/strtonum.h
new file mode 100644
index 0000000..849f66f
--- /dev/null
+++ b/src/contrib/strtonum.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libknot/errcode.h"
+#include "contrib/ctype.h"
+
+inline static int intmax_from_str(const char *src, intmax_t *dst,
+ intmax_t min, intmax_t max)
+{
+ if (!is_digit(*src) && *src != '-' && *src != '+') {
+ return KNOT_EINVAL;
+ }
+
+ errno = 0;
+ char *end = NULL;
+ intmax_t result = strtoimax(src, &end, 10);
+
+ if (errno == ERANGE) {
+ return KNOT_ERANGE;
+ }
+
+ if (src == end || *end != '\0') {
+ return KNOT_EINVAL;
+ }
+
+ if (result < min || result > max) {
+ return KNOT_ERANGE;
+ }
+
+ *dst = result;
+ return KNOT_EOK;
+}
+
+inline static int uintmax_from_str(const char *src, uintmax_t *dst,
+ uintmax_t min, uintmax_t max)
+{
+ if (!is_digit(*src) && *src != '+') {
+ return KNOT_EINVAL;
+ }
+
+ errno = 0;
+ char *end = NULL;
+ uintmax_t result = strtoumax(src, &end, 10);
+
+ if (errno == ERANGE) {
+ return KNOT_ERANGE;
+ }
+
+ if (src == end || *end != '\0') {
+ return KNOT_EINVAL;
+ }
+
+ if (result < min || result > max) {
+ return KNOT_ERANGE;
+ }
+
+ *dst = result;
+ return KNOT_EOK;
+}
+
+#define CONVERT(prefix, type, min, max, src, dst) \
+{ \
+ assert(src && dst); \
+ prefix##max_t value; \
+ int result = prefix##max_from_str(src, &value, min, max); \
+ if (result != KNOT_EOK) { \
+ return result; \
+ } \
+ *dst = (type)value; \
+ return KNOT_EOK; \
+}
+
+inline static int str_to_int(const char *src, int *dst, int min, int max)
+{
+ CONVERT(int, int, min, max, src, dst);
+}
+
+inline static int str_to_u8(const char *src, uint8_t *dst)
+{
+ CONVERT(uint, uint8_t, 0, UINT8_MAX, src, dst);
+}
+
+inline static int str_to_u16(const char *src, uint16_t *dst)
+{
+ CONVERT(uint, uint16_t, 0, UINT16_MAX, src, dst);
+}
+
+inline static int str_to_u32(const char *src, uint32_t *dst)
+{
+ CONVERT(uint, uint32_t, 0, UINT32_MAX, src, dst);
+}
+
+inline static int str_to_u64(const char *src, uint64_t *dst)
+{
+ CONVERT(uint, uint64_t, 0, UINT64_MAX, src, dst);
+}
+
+inline static int str_to_size(const char *src, size_t *dst, size_t min, size_t max)
+{
+ CONVERT(uint, size_t, min, max, src, dst);
+}
+
+#undef CONVERT
diff --git a/src/contrib/time.c b/src/contrib/time.c
new file mode 100644
index 0000000..a3a4cdf
--- /dev/null
+++ b/src/contrib/time.c
@@ -0,0 +1,441 @@
+/* Copyright (C) 2017 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 <assert.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/time.h"
+#include "contrib/ctype.h"
+#ifndef HAVE_CLOCK_GETTIME
+ #include <sys/time.h>
+#endif
+
+struct timespec time_now(void)
+{
+ struct timespec result = { 0 };
+
+#ifdef HAVE_CLOCK_GETTIME
+ clock_gettime(CLOCK_MONOTONIC, &result);
+#else // OS X < Sierra fallback.
+ struct timeval tmp = { 0 };
+ gettimeofday(&tmp, NULL);
+ result.tv_sec = tmp.tv_sec;
+ result.tv_nsec = 1000 * tmp.tv_usec;
+#endif
+
+ return result;
+}
+
+struct timespec time_diff(const struct timespec *begin, const struct timespec *end)
+{
+ struct timespec result = { 0 };
+
+ if (end->tv_nsec >= begin->tv_nsec) {
+ result.tv_sec = end->tv_sec - begin->tv_sec;
+ result.tv_nsec = end->tv_nsec - begin->tv_nsec;
+ } else {
+ result.tv_sec = end->tv_sec - begin->tv_sec - 1;
+ result.tv_nsec = 1000000000 - begin->tv_nsec + end->tv_nsec;
+ }
+
+ return result;
+}
+
+double time_diff_ms(const struct timespec *begin, const struct timespec *end)
+{
+ struct timespec result = time_diff(begin, end);
+
+ return (result.tv_sec * 1e3) + (result.tv_nsec / 1e6);
+}
+
+typedef struct {
+ const char *format;
+ const char *timespec;
+ const char *parsed;
+ knot_timediff_t offset;
+ char offset_sign;
+ char offset_unit;
+ struct tm calendar;
+ int error;
+} time_ctx_t;
+
+// After casting (struct tm) to (int []), we can use indexes...
+static int calendar_index(char ind)
+{
+ switch (ind) {
+ case 'Y': return 5;
+ case 'M': return 4;
+ case 'D': return 3;
+ case 'h': return 2;
+ case 'm': return 1;
+ case 's': return 0;
+ default: assert(0); return 6;
+ }
+}
+
+static size_t calendar_digits(int index)
+{
+ return index == 5 ? 4 : 2;
+}
+
+static size_t unit_value(char unit)
+{
+ size_t val = 1;
+ switch (unit) {
+ case 'M':
+ return 3600 * 24 * 30;
+ case 'Y':
+ val *= 365;
+ // FALLTHROUGH
+ case 'D':
+ val *= 24;
+ // FALLTHROUGH
+ case 'h':
+ val *= 60;
+ // FALLTHROUGH
+ case 'm':
+ val *= 60;
+ // FALLTHROUGH
+ case 's':
+ default:
+ return val;
+ }
+}
+
+static knot_time_t time_ctx_finalize(time_ctx_t *ctx)
+{
+ if (ctx->offset_sign) {
+ ctx->offset *= unit_value(ctx->offset_unit);
+ return knot_time_add(knot_time(), (ctx->offset_sign == '-' ? -1 : 1) * ctx->offset);
+ } else if (ctx->offset) {
+ return (knot_time_t)ctx->offset;
+ } else if (ctx->calendar.tm_year != 0) {
+ ctx->calendar.tm_isdst = -1;
+ ctx->calendar.tm_year -= 1900;
+ ctx->calendar.tm_mon -= 1;
+ // Set UTC timezone before using mktime
+ putenv("TZ=UTC");
+ tzset();
+ return (knot_time_t)mktime(&ctx->calendar);
+ } else {
+ return (knot_time_t)0;
+ }
+}
+
+static void time_ctx_reset(time_ctx_t *ctx)
+{
+ ctx->parsed = ctx->timespec;
+ ctx->offset = 0;
+ ctx->offset_sign = 0;
+ memset(&ctx->calendar, 0, sizeof(ctx->calendar));
+ ctx->error = 0;
+}
+
+static void parse_quote(time_ctx_t *ctx)
+{
+ while (*ctx->format != '|' && *ctx->format != '\0') {
+ if (*ctx->format == '\'') {
+ ctx->format++;
+ return;
+ }
+ if (*ctx->format++ != *ctx->parsed++) {
+ ctx->error = -1;
+ return;
+ }
+ }
+ ctx->error = -2;
+ return;
+}
+
+static void parse_offset(time_ctx_t *ctx)
+{
+ ctx->offset = 0;
+ ctx->error = -1;
+ while (is_digit(*ctx->parsed)) {
+ ctx->offset *= 10;
+ ctx->offset += *ctx->parsed++ - '0';
+ ctx->error = 0;
+ }
+}
+
+static void parse_calendar(time_ctx_t *ctx, int index)
+{
+ int *cal_arr = (int *)&ctx->calendar;
+ cal_arr[index] = 0;
+ for (size_t i = 0; i < calendar_digits(index); i++) {
+ if (!is_digit(*ctx->parsed)) {
+ ctx->error = -1;
+ return;
+ }
+ cal_arr[index] *= 10;
+ cal_arr[index] += *ctx->parsed++ - '0';
+ }
+}
+
+static void parse_sign(time_ctx_t *ctx)
+{
+ char sign1 = *(ctx->format - 1), sign2 = *ctx->format;
+
+ bool use_sign2 = (sign2 == '+' || sign2 == '-');
+
+ bool allow_plus = (sign1 == '+' || (sign1 == '-' && sign2 == '+'));
+ bool allow_minus = (sign1 == '-' || (sign1 == '+' && sign2 == '-'));
+ assert(sign1 == '+' || sign1 == '-');
+
+ if ((*ctx->parsed == '+' && allow_plus) || (*ctx->parsed == '-' && allow_minus)) {
+ ctx->offset_sign = *ctx->parsed++;
+ ctx->format += (use_sign2 ? 1 : 0);
+ } else {
+ ctx->error = -11;
+ }
+}
+
+static void parse_unit1(time_ctx_t *ctx)
+{
+ char u = *ctx->parsed++;
+ switch (u) {
+ case 'Y':
+ case 'M':
+ case 'D':
+ case 'h':
+ case 'm':
+ case 's':
+ ctx->offset_unit = u;
+ break;
+ default:
+ ctx->error = -1;
+ }
+}
+
+static void parse_unit2(time_ctx_t *ctx)
+{
+ char u = *ctx->parsed++;
+ switch (u) {
+ case 'y':
+ case 'd':
+ ctx->offset_unit = toupper((unsigned char)u);
+ break;
+ case 'h':
+ case 's':
+ ctx->offset_unit = u;
+ break;
+ case 'm':
+ switch (*ctx->parsed++) {
+ case 'o':
+ ctx->offset_unit = 'M';
+ break;
+ case 'i':
+ ctx->offset_unit = 'm';
+ break;
+ default:
+ ctx->error = -1;
+ }
+ break;
+ default:
+ ctx->error = -1;
+ }
+}
+
+int knot_time_parse(const char *format, const char *timespec, knot_time_t *time)
+{
+ if (format == NULL || timespec == NULL || time == NULL) {
+ return -1;
+ }
+
+ time_ctx_t ctx = {
+ .format = format,
+ .timespec = timespec,
+ .parsed = timespec,
+ .offset = 0,
+ .offset_sign = 0,
+ // we hope that .calendar is zeroed by default
+ .error = 0,
+ };
+
+ while (ctx.error == 0 && *ctx.format != '\0') {
+ switch (*ctx.format++) {
+ case '|':
+ if (*ctx.parsed == '\0') {
+ *time = time_ctx_finalize(&ctx);
+ return 0;
+ } else {
+ time_ctx_reset(&ctx);
+ }
+ break;
+ case '\'':
+ parse_quote(&ctx);
+ break;
+ case '#':
+ parse_offset(&ctx);
+ break;
+ case 'Y':
+ case 'M':
+ case 'D':
+ case 'h':
+ case 'm':
+ case 's':
+ parse_calendar(&ctx, calendar_index(*(ctx.format - 1)));
+ break;
+ case '+':
+ case '-':
+ parse_sign(&ctx);
+ break;
+ case 'U':
+ parse_unit1(&ctx);
+ break;
+ case 'u':
+ parse_unit2(&ctx);
+ break;
+ default:
+ return -1;
+ }
+
+ if (ctx.error < 0) {
+ while (*ctx.format != '|' && *ctx.format != '\0') {
+ ctx.format++;
+ }
+ time_ctx_reset(&ctx);
+ ctx.error = (*ctx.format == '\0' ? -1 : 0);
+ }
+ }
+
+ if (ctx.error == 0 && *ctx.parsed == '\0') {
+ *time = time_ctx_finalize(&ctx);
+ return 0;
+ }
+ return -1;
+}
+
+static char *unit_names_mixed[] = { "Y", "M", "D", "h", "m", "s" };
+static char *unit_names_lower[] = { "y", "mo", "d", "h", "mi", "s" };
+static size_t unit_sizes[] = { 3600*24*365, 3600*24*30, 3600*24, 3600, 60, 1 };
+static const size_t unit_count = 6;
+
+static int print_unit(char *dst, size_t dst_len, char *unit_names[unit_count],
+ size_t max_units, knot_time_t time)
+{
+ int ret;
+ if (time == 0) {
+ ret = snprintf(dst, dst_len, "0");
+ return (ret < 0 || ret >= dst_len ? -1 : 0);
+ }
+ knot_timediff_t diff = knot_time_diff(time, knot_time());
+ if (dst_len-- < 1) {
+ return -1;
+ }
+ *dst++ = (diff < 0 ? '-' : '+');
+ if (diff < 0) {
+ diff = -diff;
+ } else if (diff == 0) {
+ ret = snprintf(dst, dst_len, "0%s", unit_names[unit_count - 1]);
+ return (ret < 0 || ret >= dst_len ? -1 : 0);
+ }
+ size_t curr_unit = 0, used_units = 0;
+ while (curr_unit < unit_count && used_units < max_units) {
+ if (diff >= unit_sizes[curr_unit]) {
+ ret = snprintf(dst, dst_len, "%"KNOT_TIMEDIFF_PRINTF"%s",
+ diff / unit_sizes[curr_unit],
+ unit_names[curr_unit]);
+ if (ret < 0 || ret >= dst_len) {
+ return -1;
+ }
+ dst += ret;
+ dst_len -= ret;
+ used_units++;
+ diff %= unit_sizes[curr_unit];
+ }
+ curr_unit++;
+ }
+ return 0;
+}
+
+int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len)
+{
+ if (dst == NULL) {
+ return -1;
+ }
+
+ int ret;
+ switch (format) {
+ case TIME_PRINT_UNIX:
+ ret = snprintf(dst, dst_len, "%"KNOT_TIME_PRINTF, time);
+ return ((ret >= 0 && ret < dst_len) ? 0 : -1);
+ case TIME_PRINT_ISO8601:
+ if (time > LONG_MAX) {
+ return -1;
+ }
+
+ // Set timezone to UTC before using timezone dependent functions
+ putenv("TZ=UTC");
+ tzset();
+
+ struct tm lt;
+ time_t tt = (time_t)time;
+ ret = (localtime_r(&tt, &lt) == NULL ? -1 :
+ strftime(dst, dst_len, "%Y-%m-%dT%H:%M:%SZ", &lt));
+ return (ret > 0 ? 0 : -1);
+ case TIME_PRINT_RELSEC:
+ ret = snprintf(dst, dst_len, "%+"KNOT_TIMEDIFF_PRINTF,
+ knot_time_diff(time, knot_time()));
+ return ((ret >= 0 && ret < dst_len) ? 0 : -1);
+ case TIME_PRINT_HUMAN_MIXED:
+ return print_unit(dst, dst_len, unit_names_mixed, unit_count, time);
+ case TIME_PRINT_HUMAN_LOWER:
+ return print_unit(dst, dst_len, unit_names_lower, unit_count, time);
+ default:
+ return -1;
+ }
+}
+
+int knot_time_print_human(knot_time_t time, char *dst, size_t dst_len, bool condensed)
+{
+ int ret;
+ knot_time_t num;
+ bool empty = true;
+ size_t total_len = 0;
+
+#define tths_process(unit, unit_name, unit_size) \
+ num = time / (unit_size); \
+ if (num > 0) { \
+ ret = snprintf(dst + total_len, dst_len - total_len, \
+ "%s%"PRIu64"%s%s", \
+ (!empty && !condensed ? " " : ""), \
+ num, \
+ (condensed ? unit : unit_name), \
+ (num > 1 && !condensed ? "s" : "")); \
+ if (ret <= 0 || (size_t)ret >= dst_len - total_len) { \
+ return -1; \
+ } \
+ empty = false; \
+ total_len += ret; \
+ time -= num * (unit_size); \
+ }
+
+ tths_process("w", " week", 604800);
+ tths_process("d", " day", 86400);
+ tths_process("h", " hour", 3600);
+ tths_process("m", " minute", 60);
+ tths_process("s", " second", 1);
+
+#undef tths_process
+
+ return total_len > 0 ? total_len : -1;
+}
diff --git a/src/contrib/time.h b/src/contrib/time.h
new file mode 100644
index 0000000..20d241e
--- /dev/null
+++ b/src/contrib/time.h
@@ -0,0 +1,211 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+#ifdef __APPLE__
+ #define st_mtim st_mtimespec
+#endif
+
+/*!
+ * \brief Specify output format for knot_time_print().
+ */
+typedef enum {
+ TIME_PRINT_UNIX, // numeric UNIX time
+ TIME_PRINT_ISO8601, // 2016-12-31T23:59:00
+ TIME_PRINT_RELSEC, // relative +6523
+ TIME_PRINT_HUMAN_MIXED, // relative with mixed-case units
+ TIME_PRINT_HUMAN_LOWER, // relative with lower-case units
+} knot_time_print_t;
+
+/*!
+ * \brief Get current time.
+ */
+struct timespec time_now(void);
+
+/*!
+ * \brief Get time elapsed between two events.
+ */
+struct timespec time_diff(const struct timespec *begin, const struct timespec *end);
+
+/*!
+ * \brief Get time elapsed between two events in milliseconds.
+ */
+double time_diff_ms(const struct timespec *begin, const struct timespec *end);
+
+/*!
+ * \brief Data type for keeping UNIX timestamps.
+ *
+ * This is because time_t can be 32-bit on some systems, which is bad.
+ * Zero value represents infinity.
+ */
+typedef uint64_t knot_time_t;
+
+/*!
+ * \brief Data type for keeping time differences.
+ */
+typedef int64_t knot_timediff_t;
+
+#define KNOT_TIMEDIFF_MIN INT64_MIN
+#define KNOT_TIMEDIFF_MAX INT64_MAX
+
+#define KNOT_TIME_PRINTF PRIu64
+#define KNOT_TIMEDIFF_PRINTF PRId64
+
+/*!
+ * \brief Returns current time sice epoch.
+ */
+inline static knot_time_t knot_time(void)
+{
+ return (knot_time_t)time(NULL);
+}
+
+/*!
+ * \brief Compare two timestamps.
+ *
+ * \return 0 if equal, -1 if the former is smaller (=earlier), 1 else.
+ */
+inline static int knot_time_cmp(knot_time_t a, knot_time_t b)
+{
+ return (a == b ? 0 : 1) * ((a && b) == 0 ? -1 : 1) * (a < b ? -1 : 1);
+}
+
+/*!
+ * \brief Return the smaller (=earlier) from given two timestamps.
+ */
+inline static knot_time_t knot_time_min(knot_time_t a, knot_time_t b)
+{
+ if ((a && b) == 0) {
+ return a + b;
+ } else {
+ return (a < b ? a : b);
+ }
+}
+
+/*!
+ * \brief Return the difference between two timestamps (to "minus" from).
+ *
+ * \note If both are zero (=infinity), KNOT_TIMEDIFF_MAX is returned.
+ */
+inline static knot_timediff_t knot_time_diff(knot_time_t to, knot_time_t from)
+{
+ if ((to && from) == 0) {
+ return (to > from ? KNOT_TIMEDIFF_MIN : KNOT_TIMEDIFF_MAX);
+ } else {
+ return (knot_timediff_t)to - (knot_timediff_t)from;
+ }
+}
+
+/*!
+ * \brief Add a time difference to timestamp.
+ */
+inline static knot_time_t knot_time_add(knot_time_t since, knot_timediff_t howlong)
+{
+ return (since != 0 ? since + howlong : since);
+}
+
+inline static knot_time_t knot_time_plus(knot_time_t a, knot_time_t b)
+{
+ return ((a && b) ? a + b : 0);
+}
+
+/*!
+ * \brief Convert uint32_t-encoded timestamp to knot_time_t.
+ *
+ * In RRSIG rdata, there are inception and expiration timestamps in uint32_t format.
+ * One shall use 'serial arithmetics' to decode them.
+ *
+ * The result of this function is a timestamp that equals to
+ * given 32-bit time in lower 32 bits, and does not differ from
+ * now by more than 2^31.
+ */
+inline static knot_time_t knot_time_from_u32(uint32_t u32time, knot_time_t now)
+{
+ if (now == 0) {
+ now = knot_time();
+ }
+
+ uint32_t now_lower32 = (uint32_t)now;
+ uint64_t now_upper32 = now >> 32;
+ if (now_lower32 > u32time && now_lower32 - u32time > INT32_MAX) {
+ now_upper32++;
+ } else if (now_lower32 < u32time && u32time - now_lower32 > INT32_MAX) {
+ now_upper32--;
+ }
+
+ return (now_upper32 << 32) + u32time;
+}
+
+/*!
+ * \brief Parse a text-formatted timestamp to knot_time_t using format specification.
+ *
+ * \param format The timestamp text format specification.
+ * \param timespec Text-formatted timestamp.
+ * \param time The parsed timestamp.
+ *
+ * The format specification basics:
+ * <format 1>|<format 2> - The pipe sign separates two time format specifications. Leftmost
+ * specification matching the timespec is used.
+ * '<a string>' - Matches exactly <a string> (not containing apostrophes) in timespec.
+ * # - Hashtag matches for a number in timespec, stands for either a UNIX timestamp,
+ * or, within a context of an unit, as a number of such units.
+ * Y, M, D, h, m, s - Matches a number, stands for a number of years, months, days, hours,
+ * minutes and seconds, respectively.
+ * +, - - The + and - signs declaring that following timespec is relative to "now".
+ * A single sign can be used to limit the timestamp being in future or in past,
+ * or both +- allow the timestamp to select any (just one) of them.
+ * U - Matches one of Y, M, D, h, m, s in the timespec standing for a time unit.
+ * u - Like U, but the unit in the timestamp is from: y, mo, d, h, mi, s.
+ *
+ * \retval -1 An error occurred, out_time has no sense.
+ * \return 0 OK, timestamp parsed successfully.
+ */
+int knot_time_parse(const char *format, const char *timespec, knot_time_t *time);
+
+/*!
+ * \brief Print the timestamp in specified format into a string buffer.
+ *
+ * \param format The timestamp text format specification.
+ * \param time The timestamp to be printed.
+ * \param dst The destination buffer pointer with text-formatted timestamp.
+ * \param dst_len The destination buffer length.
+ *
+ * \retval -1 An error occurred, the buffer may be filled with nonsense.
+ * \return 0 OK, timestamp printed successfully.
+ */
+int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len);
+
+/*!
+ * \brief Print the timestamp in a predefined human format.
+ *
+ * Condensed format (zone file compatible): 1w2d3h4m5s
+ * Normal format: 1 week 2 days 3 hours 4 minutes 5 seconds
+ *
+ * \param time The timestamp to be printed.
+ * \param dst The destination buffer pointer with text-formatted timestamp.
+ * \param dst_len The destination buffer length.
+ * \param condensed Condensed format indication.
+ *
+ * \retval -1 An error occurred, the buffer may be filled with nonsense.
+ * \return >0 OK, timestamp printed successfully.
+ */
+int knot_time_print_human(knot_time_t time, char *dst, size_t dst_len, bool condensed);
diff --git a/src/contrib/toeplitz.h b/src/contrib/toeplitz.h
new file mode 100644
index 0000000..02974d2
--- /dev/null
+++ b/src/contrib/toeplitz.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 2022 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 Microsoft Toeplitz-based hash implementation
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "libknot/endian.h"
+
+/*!
+ * \brief Computes a Toeplitz hash value for given key and data.
+ *
+ * \param key Key vector
+ * \param key_len Length of the key vector in bytes.
+ * \param data Input data to compute hash from.
+ * \param data_len Lenght of the input data in bytes.
+ *
+ * \return A Toeplitz hash value.
+ */
+inline static uint32_t toeplitz_hash(const uint8_t *key, const size_t key_len,
+ const uint8_t *data, const size_t data_len)
+{
+ assert(key_len >= 4 + 2 * (16 + 2));
+
+ uint32_t key32 = be32toh(*(const uint32_t *)key);
+ key += sizeof(uint32_t);
+
+ int ret = 0;
+
+ for (int i = 0; i < data_len; i++) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (data[i] & (1 << bit)) {
+ ret ^= key32;
+ }
+
+ key32 <<= 1;
+ key32 |= !!(key[0] & (1 << bit));
+ }
+ key++;
+ }
+
+ return ret;
+}
+
+/*!
+ * \brief Toeplitz hash context for divided processing.
+ */
+typedef struct {
+ const uint8_t *data;
+ const uint8_t *data_end;
+ const uint8_t *key;
+ uint32_t hash;
+ uint32_t key32;
+} toeplitz_ctx_t;
+
+inline static void toeplitz_init(toeplitz_ctx_t *ctx, uint8_t count,
+ const uint8_t *key, const uint8_t key_len,
+ const uint8_t *data, const uint8_t data_len)
+{
+ assert(key_len >= 40);
+
+ ctx->data = data;
+ ctx->data_end = data + data_len;
+ ctx->key = key + sizeof(uint32_t);
+ ctx->hash = 0;
+ ctx->key32 = be32toh(*(const uint32_t *)key);
+
+ const uint8_t *stop = ctx->data + count;
+ assert(stop <= ctx->data_end);
+
+ while (ctx->data < stop) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (*ctx->data & (1 << bit)) {
+ ctx->hash ^= ctx->key32;
+ }
+
+ ctx->key32 <<= 1;
+ ctx->key32 |= !!(*ctx->key & (1 << bit));
+ }
+ ctx->data++;
+ ctx->key++;
+ }
+}
+
+inline static uint32_t toeplitz_finish(toeplitz_ctx_t *ctx)
+{
+ uint32_t hash = ctx->hash;
+ uint32_t key32 = ctx->key32;
+ const uint8_t *key = ctx->key;
+
+ for (const uint8_t *in = ctx->data; in < ctx->data_end; in++) {
+ for (int bit = 7; bit >= 0; bit--) {
+ if (*in & (1 << bit)) {
+ hash ^= key32;
+ }
+
+ key32 <<= 1;
+ key32 |= !!(*key & (1 << bit));
+ }
+ key++;
+ }
+
+ return hash;
+}
diff --git a/src/contrib/tolower.h b/src/contrib/tolower.h
new file mode 100644
index 0000000..c8b4897
--- /dev/null
+++ b/src/contrib/tolower.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2018 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 Table for converting ASCII characters to lowercase.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Converts binary character to lowercase.
+ *
+ * \param c Character code.
+ *
+ * \return \a c converted to lowercase (or \a c if not applicable).
+ */
+static inline uint8_t knot_tolower(uint8_t c) {
+ const uint8_t *tolower_table = (uint8_t *)
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
+ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F"
+ "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
+ "\x40\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
+ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x5B\x5C\x5D\x5E\x5F"
+ "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F"
+ "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F"
+ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F"
+ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F"
+ "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF"
+ "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"
+ "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF"
+ "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF"
+ "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF"
+ "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
+
+ return tolower_table[c];
+}
diff --git a/src/contrib/trim.h b/src/contrib/trim.h
new file mode 100644
index 0000000..2b89249
--- /dev/null
+++ b/src/contrib/trim.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2018 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 Heap memory trimmer.
+ */
+
+#pragma once
+
+#ifdef HAVE_MALLOC_TRIM
+#include <malloc.h>
+#endif
+
+/*!
+ * \brief Trim excess heap memory.
+ */
+static inline void mem_trim(void)
+{
+#ifdef HAVE_MALLOC_TRIM
+ malloc_trim(0);
+#endif
+ return;
+}
diff --git a/src/contrib/ucw/LICENSE b/src/contrib/ucw/LICENSE
new file mode 100644
index 0000000..b463d57
--- /dev/null
+++ b/src/contrib/ucw/LICENSE
@@ -0,0 +1 @@
+../licenses/LGPL-2.0 \ No newline at end of file
diff --git a/src/contrib/ucw/array-sort.h b/src/contrib/ucw/array-sort.h
new file mode 100644
index 0000000..1ff1377
--- /dev/null
+++ b/src/contrib/ucw/array-sort.h
@@ -0,0 +1,195 @@
+/*
+ * UCW Library -- Universal Simple Array Sorter
+ *
+ * (c) 2003--2008 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#pragma once
+
+#include "contrib/macros.h"
+
+/*
+ * This is not a normal header file, it's a generator of sorting
+ * routines. Each time you include it with parameters set in the
+ * corresponding preprocessor macros, it generates an array sorter
+ * with the parameters given.
+ *
+ * You might wonder why the heck do we implement our own array sorter
+ * instead of using qsort(). The primary reason is that qsort handles
+ * only continuous arrays, but we need to sort array-like data structures
+ * where the only way to access elements is by using an indexing macro.
+ * Besides that, we are more than 2 times faster.
+ *
+ * So much for advocacy, there are the parameters (those marked with [*]
+ * are mandatory):
+ *
+ * ASORT_PREFIX(x) [*] add a name prefix (used on all global names
+ * defined by the sorter)
+ * ASORT_KEY_TYPE [*] data type of a single array entry key
+ * ASORT_ELT(i) returns the key of i-th element; if this macro is not
+ * defined, the function gets a pointer to an array to be sorted
+ * ASORT_LT(x,y) x < y for ASORT_KEY_TYPE (default: "x<y")
+ * ASORT_SWAP(i,j) swap i-th and j-th element (default: assume _ELT
+ * is an l-value and swap just the keys)
+ * ASORT_THRESHOLD threshold for switching between quicksort and insertsort
+ * ASORT_EXTRA_ARGS extra arguments for the sort function (they are always
+ * visible in all the macros supplied above), starts with comma
+ *
+ * After including this file, a function ASORT_PREFIX(sort)(unsigned array_size)
+ * or ASORT_PREFIX(sort)(ASORT_KEY_TYPE *array, unsigned array_size) [if ASORT_ELT
+ * is not defined] is declared and all parameter macros are automatically
+ * undef'd.
+ */
+
+#ifndef ASORT_LT
+#define ASORT_LT(x,y) ((x) < (y))
+#endif
+
+#ifndef ASORT_SWAP
+#define ASORT_SWAP(i,j) do { ASORT_KEY_TYPE tmp = ASORT_ELT(i); ASORT_ELT(i)=ASORT_ELT(j); ASORT_ELT(j)=tmp; } while (0)
+#endif
+
+#ifndef ASORT_THRESHOLD
+#define ASORT_THRESHOLD 8 /* Guesswork and experimentation */
+#endif
+
+#ifndef ASORT_EXTRA_ARGS
+#define ASORT_EXTRA_ARGS
+#endif
+
+#ifndef ASORT_ELT
+#define ASORT_ARRAY_ARG ASORT_KEY_TYPE *array,
+#define ASORT_ELT(i) array[i]
+#else
+#define ASORT_ARRAY_ARG
+#endif
+
+/**
+ * The generated sorting function. If `ASORT_ELT` macro is not provided, the
+ * @ASORT_ARRAY_ARG is equal to `ASORT_KEY_TYPE *array` and is the array to be
+ * sorted. If the macro is provided, this parameter is omitted. In that case,
+ * you can sort global variables or pass your structure by @ASORT_EXTRA_ARGS.
+ **/
+static void ASORT_PREFIX(sort)(ASORT_ARRAY_ARG unsigned array_size ASORT_EXTRA_ARGS)
+{
+ struct stk { int l, r; } stack[8*sizeof(unsigned)];
+ int l, r, left, right, m;
+ unsigned sp = 0;
+ ASORT_KEY_TYPE pivot;
+
+ if (array_size <= 1)
+ return;
+
+ /* QuickSort with optimizations a'la Sedgewick, but stop at ASORT_THRESHOLD */
+
+ left = 0;
+ right = array_size - 1;
+ for(;;)
+ {
+ l = left;
+ r = right;
+ m = (l+r)/2;
+ if (ASORT_LT(ASORT_ELT(m), ASORT_ELT(l)))
+ ASORT_SWAP(l,m);
+ if (ASORT_LT(ASORT_ELT(r), ASORT_ELT(m)))
+ {
+ ASORT_SWAP(m,r);
+ if (ASORT_LT(ASORT_ELT(m), ASORT_ELT(l)))
+ ASORT_SWAP(l,m);
+ }
+ pivot = ASORT_ELT(m);
+ do
+ {
+ while (ASORT_LT(ASORT_ELT(l), pivot))
+ l++;
+ while (ASORT_LT(pivot, ASORT_ELT(r)))
+ r--;
+ if (l < r)
+ {
+ ASORT_SWAP(l,r);
+ l++;
+ r--;
+ }
+ else if (l == r)
+ {
+ l++;
+ r--;
+ }
+ }
+ while (l <= r);
+ if ((r - left) >= ASORT_THRESHOLD && (right - l) >= ASORT_THRESHOLD)
+ {
+ /* Both partitions ok => push the larger one */
+ if ((r - left) > (right - l))
+ {
+ stack[sp].l = left;
+ stack[sp].r = r;
+ left = l;
+ }
+ else
+ {
+ stack[sp].l = l;
+ stack[sp].r = right;
+ right = r;
+ }
+ sp++;
+ }
+ else if ((r - left) >= ASORT_THRESHOLD)
+ {
+ /* Left partition OK, right undersize */
+ right = r;
+ }
+ else if ((right - l) >= ASORT_THRESHOLD)
+ {
+ /* Right partition OK, left undersize */
+ left = l;
+ }
+ else
+ {
+ /* Both partitions undersize => pop */
+ if (!sp)
+ break;
+ sp--;
+ left = stack[sp].l;
+ right = stack[sp].r;
+ }
+ }
+
+ /*
+ * We have a partially sorted array, finish by insertsort. Inspired
+ * by qsort() in GNU libc.
+ */
+
+ /* Find minimal element which will serve as a barrier */
+ r = MIN(array_size, ASORT_THRESHOLD);
+ m = 0;
+ for (l=1; l<r; l++)
+ if (ASORT_LT(ASORT_ELT(l),ASORT_ELT(m)))
+ m = l;
+ ASORT_SWAP(0,m);
+
+ /* Insertion sort */
+ for (m=1; m<(int)array_size; m++)
+ {
+ l=m;
+ while (ASORT_LT(ASORT_ELT(m),ASORT_ELT(l-1)))
+ l--;
+ while (l < m)
+ {
+ ASORT_SWAP(l,m);
+ l++;
+ }
+ }
+}
+
+#undef ASORT_PREFIX
+#undef ASORT_KEY_TYPE
+#undef ASORT_ELT
+#undef ASORT_LT
+#undef ASORT_SWAP
+#undef ASORT_THRESHOLD
+#undef ASORT_EXTRA_ARGS
+#undef ASORT_ARRAY_ARG
diff --git a/src/contrib/ucw/binsearch.h b/src/contrib/ucw/binsearch.h
new file mode 100644
index 0000000..b791d39
--- /dev/null
+++ b/src/contrib/ucw/binsearch.h
@@ -0,0 +1,50 @@
+/*
+ * UCW Library -- Generic Binary Search
+ *
+ * (c) 2005 Martin Mares <mj@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#pragma once
+
+/***
+ * [[defs]]
+ * Definitions
+ * -----------
+ ***/
+
+/**
+ * Find the first element not lower than \p x in the sorted array \p ary of \p N elements (non-decreasing order).
+ * Returns the index of the found element or \p N if no exists. Uses `ary_lt_x(ary,i,x)` to compare the i'th element with \p x.
+ * The time complexity is `O(log(N))`.
+ **/
+#define BIN_SEARCH_FIRST_GE_CMP(ary, N, ary_lt_x, x, ...) ({ \
+ unsigned l = 0, r = (N); \
+ while (l < r) \
+ { \
+ unsigned m = (l+r)/2; \
+ if (ary_lt_x(ary, m, x, __VA_ARGS__)) \
+ l = m+1; \
+ else \
+ r = m; \
+ } \
+ l; \
+})
+
+/**
+ * The default comparison macro for \ref BIN_SEARCH_FIRST_GE_CMP().
+ **/
+#define ARY_LT_NUM(ary,i,x) (ary)[i] < (x)
+
+/**
+ * Same as \ref BIN_SEARCH_FIRST_GE_CMP(), but uses the default `<` operator for comparisons.
+ **/
+#define BIN_SEARCH_FIRST_GE(ary,N,x) BIN_SEARCH_FIRST_GE_CMP(ary,N,ARY_LT_NUM,x)
+
+/**
+ * Search the sorted array \p ary of \p N elements (non-decreasing) for the first occurrence of \p x.
+ * Returns the index or -1 if no such element exists. Uses the `<` operator for comparisons.
+ **/
+#define BIN_SEARCH_EQ(ary,N,x) ({ int i = BIN_SEARCH_FIRST_GE(ary,N,x); if (i >= (N) || (ary)[i] != (x)) i=-1; i; })
diff --git a/src/contrib/ucw/heap.c b/src/contrib/ucw/heap.c
new file mode 100644
index 0000000..7d74aeb
--- /dev/null
+++ b/src/contrib/ucw/heap.c
@@ -0,0 +1,153 @@
+/*
+ * Binary heap
+ *
+ * (c) 2012 Ondrej Filip <feela@network.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+/***
+ * Introduction
+ * ------------
+ *
+ * Binary heap is a simple data structure, which for example supports efficient insertions, deletions
+ * and access to the minimal inserted item. We define several macros for such operations.
+ * Note that because of simplicity of heaps, we have decided to define direct macros instead
+ * of a <<generic:,macro generator>> as for several other data structures in the Libucw.
+ *
+ * A heap is represented by a number of elements and by an array of values. Beware that we
+ * index this array from one, not from zero as do the standard C arrays.
+ *
+ * Most macros use these parameters:
+ *
+ * - @num - a variable (signed or unsigned integer) with the number of elements
+ * - @heap - a C array of type @type; the heap is stored in `heap[1] .. heap[num]`; `heap[0]` is unused
+ *
+ * A valid heap must follow these rules:
+ *
+ * - `num >= 0`
+ * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]`
+ *
+ * The first element `heap[1]` is always lower or equal to all other elements.
+ ***/
+
+#include <string.h>
+#include <stdlib.h>
+#include "contrib/ucw/heap.h"
+
+static inline void heap_swap(heap_val_t **e1, heap_val_t **e2)
+{
+ if (e1 == e2) return; /* Stack tmp should be faster than tmpelem. */
+ heap_val_t *tmp = *e1; /* Even faster than 2-XOR nowadays. */
+ *e1 = *e2;
+ *e2 = tmp;
+ int pos = (*e1)->pos;
+ (*e1)->pos = (*e2)->pos;
+ (*e2)->pos = pos;
+}
+
+int heap_init(struct heap *h, int (*cmp)(void *, void *), int init_size)
+{
+ int isize = init_size ? init_size : INITIAL_HEAP_SIZE;
+
+ h->num = 0;
+ h->max_size = isize;
+ h->cmp = cmp;
+ h->data = malloc((isize + 1) * sizeof(heap_val_t*)); /* Temp element unused. */
+
+ return h->data ? 1 : 0;
+}
+
+void heap_deinit(struct heap *h)
+{
+ free(h->data);
+ memset(h, 0, sizeof(*h));
+}
+
+static inline void _heap_bubble_down(struct heap *h, int e)
+{
+ int e1;
+ for (;;) {
+ e1 = 2 * e;
+ if (e1 > h->num) break;
+ if ((h->cmp(*HELEMENT(h, e), *HELEMENT(h, e1)) < 0) &&
+ (e1 == h->num || (h->cmp(*HELEMENT(h, e), *HELEMENT(h, e1 + 1)) < 0))) break;
+ if ((e1 != h->num) && (h->cmp(*HELEMENT(h, e1 + 1), *HELEMENT(h, e1)) < 0)) e1++;
+ heap_swap(HELEMENT(h, e), HELEMENT(h, e1));
+ e = e1;
+ }
+}
+
+static inline void _heap_bubble_up(struct heap *h, int e)
+{
+ int e1;
+ while (e > 1) {
+ e1 = e / 2;
+ if (h->cmp(*HELEMENT(h, e1), *HELEMENT(h, e)) < 0) break;
+ heap_swap(HELEMENT(h, e), HELEMENT(h, e1));
+ e = e1;
+ }
+}
+
+void heap_replace(struct heap *h, int pos, heap_val_t *e)
+{
+ *HELEMENT(h, pos) = e;
+ e->pos = pos;
+
+ if (pos == 1 || h->cmp(*HELEMENT(h, pos / 2), e) < 0) {
+ _heap_bubble_down(h, pos);
+ } else {
+ _heap_bubble_up(h, pos);
+ }
+}
+
+void heap_delmin(struct heap *h)
+{
+ if (h->num == 0) return;
+ if (h->num > 1) {
+ heap_swap(HHEAD(h), HELEMENT(h, h->num));
+ }
+ (*HELEMENT(h, h->num))->pos = 0;
+ h->num--;
+ _heap_bubble_down(h, 1);
+}
+
+int heap_insert(struct heap *h, heap_val_t *e)
+{
+ if (h->num == h->max_size) {
+ h->max_size = h->max_size * HEAP_INCREASE_STEP;
+ h->data = realloc(h->data, (h->max_size + 1) * sizeof(heap_val_t*));
+ if (!h->data) {
+ return 0;
+ }
+ }
+
+ h->num++;
+ *HELEMENT(h, h->num) = e;
+ e->pos = h->num;
+ _heap_bubble_up(h, h->num);
+ return 1;
+}
+
+int heap_find(struct heap *h, heap_val_t *elm)
+{
+ return ((struct heap_val *) elm)->pos;
+}
+
+void heap_delete(struct heap *h, int e)
+{
+ heap_swap(HELEMENT(h, e), HELEMENT(h, h->num));
+ (*HELEMENT(h, h->num))->pos = 0;
+ h->num--;
+ if (h->cmp(*HELEMENT(h, e), *HELEMENT(h, h->num + 1)) < 0) {
+ _heap_bubble_up(h, e);
+ } else {
+ _heap_bubble_down(h, e);
+ }
+
+ if ((h->num > INITIAL_HEAP_SIZE) && (h->num < h->max_size / HEAP_DECREASE_THRESHOLD)) {
+ h->max_size = h->max_size / HEAP_INCREASE_STEP;
+ h->data = realloc(h->data, (h->max_size + 1) * sizeof(heap_val_t*));
+ }
+}
diff --git a/src/contrib/ucw/heap.h b/src/contrib/ucw/heap.h
new file mode 100644
index 0000000..f85bb82
--- /dev/null
+++ b/src/contrib/ucw/heap.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+struct heap_val {
+ int pos;
+};
+
+typedef struct heap_val heap_val_t;
+
+struct heap {
+ int num; /* Number of elements */
+ int max_size; /* Size of allocated memory */
+ int (*cmp)(void *, void *);
+ heap_val_t **data;
+}; /* Array follows */
+
+#define INITIAL_HEAP_SIZE 512 /* initial heap size */
+#define HEAP_INCREASE_STEP 2 /* multiplier for each inflation, keep conservative */
+#define HEAP_DECREASE_THRESHOLD 2 /* threshold for deflation, keep conservative */
+#define HELEMENT(h,num) ((h)->data + (num))
+#define HHEAD(h) HELEMENT((h), 1)
+#define EMPTY_HEAP(h) ((h)->num == 0)
+
+int heap_init(struct heap *, int (*cmp)(void *, void *), int);
+void heap_deinit(struct heap *);
+
+void heap_delmin(struct heap *);
+int heap_insert(struct heap *, heap_val_t *);
+int heap_find(struct heap *, heap_val_t *);
+void heap_delete(struct heap *, int);
+void heap_replace(struct heap *, int, heap_val_t *);
diff --git a/src/contrib/ucw/lists.c b/src/contrib/ucw/lists.c
new file mode 100644
index 0000000..01af28f
--- /dev/null
+++ b/src/contrib/ucw/lists.c
@@ -0,0 +1,264 @@
+/*
+ * BIRD Library -- Linked Lists
+ *
+ * (c) 1998 Martin Mares <mj@ucw.cz>
+ * (c) 2015, 2020-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Linked lists
+ *
+ * The BIRD library provides a set of functions for operating on linked
+ * lists. The lists are internally represented as standard doubly linked
+ * lists with synthetic head and tail which makes all the basic operations
+ * run in constant time and contain no extra end-of-list checks. Each list
+ * is described by a &list structure, nodes can have any format as long
+ * as they start with a &node structure. If you want your nodes to belong
+ * to multiple lists at once, you can embed multiple &node structures in them
+ * and use the SKIP_BACK() macro to calculate a pointer to the start of the
+ * structure from a &node pointer, but beware of obscurity.
+ *
+ * There also exist safe linked lists (&slist, &snode and all functions
+ * being prefixed with |s_|) which support asynchronous walking very
+ * similar to that used in the &fib structure.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/ucw/lists.h"
+#include "contrib/mempattern.h"
+
+/**
+ * add_tail - append a node to a list
+ * \p l: linked list
+ * \p n: list node
+ *
+ * add_tail() takes a node \p n and appends it at the end of the list \p l.
+ */
+void
+add_tail(list_t *l, node_t *n)
+{
+ node_t *z = &l->tail;
+
+ n->next = z;
+ n->prev = z->prev;
+ z->prev->next = n;
+ z->prev = n;
+ assert(z->next == NULL);
+}
+
+/**
+ * add_head - prepend a node to a list
+ * \p l: linked list
+ * \p n: list node
+ *
+ * add_head() takes a node \p n and prepends it at the start of the list \p l.
+ */
+void
+add_head(list_t *l, node_t *n)
+{
+ node_t *z = &l->head;
+
+ n->next = z->next;
+ n->prev = z;
+ z->next->prev = n;
+ z->next = n;
+ assert(z->prev == NULL);
+}
+
+/**
+ * insert_node - insert a node to a list
+ * \p n: a new list node
+ * \p after: a node of a list
+ *
+ * Inserts a node \p n to a linked list after an already inserted
+ * node \p after.
+ */
+void
+insert_node(node_t *n, node_t *after)
+{
+ node_t *z = after->next;
+
+ n->next = z;
+ n->prev = after;
+ after->next = n;
+ z->prev = n;
+}
+
+/**
+ * rem_node - remove a node from a list
+ * \p n: node to be removed
+ *
+ * Removes a node \p n from the list it's linked in.
+ */
+void
+rem_node(node_t *n)
+{
+ node_t *z = n->prev;
+ node_t *x = n->next;
+
+ z->next = x;
+ x->prev = z;
+ n->prev = 0;
+ n->next = 0;
+}
+
+/**
+ * init_list - create an empty list
+ * \p l: list
+ *
+ * init_list() takes a &list structure and initializes its
+ * fields, so that it represents an empty list.
+ */
+void
+init_list(list_t *l)
+{
+ l->head.next = &l->tail;
+ l->head.prev = NULL;
+ l->tail.next = NULL;
+ l->tail.prev = &l->head;
+}
+
+/**
+ * add_tail_list - concatenate two lists
+ * \p to: destination list
+ * \p l: source list
+ *
+ * This function appends all elements of the list \p l to
+ * the list \p to in constant time.
+ */
+void
+add_tail_list(list_t *to, list_t *l)
+{
+ node_t *p = to->tail.prev;
+ node_t *q = l->head.next;
+
+ p->next = q;
+ q->prev = p;
+ to->tail.prev = l->tail.prev;
+}
+
+/**
+ * list_dup - duplicate list
+ * \p to: destination list
+ * \p l: source list
+ *
+ * This function duplicates all elements of the list \p l to
+ * the list \p to in linear time.
+ *
+ * This function only works with a homogenous item size.
+ */
+void list_dup(list_t *dst, list_t *src, size_t itemsz)
+{
+ node_t *n;
+ WALK_LIST(n, *src) {
+ node_t *i = malloc(itemsz);
+ memcpy(i, n, itemsz);
+ add_tail(dst, i);
+ }
+}
+
+/**
+ * list_size - gets number of nodes
+ * \p l: list
+ *
+ * This function counts nodes in list \p l and returns this number.
+ */
+size_t list_size(const list_t *l)
+{
+ size_t count = 0;
+
+ node_t *n;
+ WALK_LIST(n, *l) {
+ count++;
+ }
+
+ return count;
+}
+
+/**
+ * fix_list - correction of head/tail pointers when list had been memmove'd
+ * \p l: list
+ *
+ * WARNING: must not be called on empty list
+ */
+void fix_list(list_t *l)
+{
+ node_t *n = HEAD(*l);
+ assert(n->next != NULL);
+ n->prev = &l->head;
+
+ n = TAIL(*l);
+ assert(n->prev != NULL);
+ n->next = &l->tail;
+}
+
+/**
+ * ptrlist_add - add pointer to pointer list
+ * \p to: destination list
+ * \p val: added pointer
+ * \p mm: memory context
+ */
+ptrnode_t *ptrlist_add(list_t *to, void *val, knot_mm_t *mm)
+{
+ ptrnode_t *node = mm_alloc(mm , sizeof(ptrnode_t));
+ if (node == NULL) {
+ return NULL;
+ } else {
+ node->d = val;
+ }
+ add_tail(to, &node->n);
+ return node;
+}
+
+/**
+ * ptrlist_free - free all nodes in pointer list
+ * \p list: list nodes
+ * \p mm: memory context
+ */
+void ptrlist_free(list_t *list, knot_mm_t *mm)
+{
+ node_t *n, *nxt;
+ WALK_LIST_DELSAFE(n, nxt, *list) {
+ mm_free(mm, n);
+ }
+ init_list(list);
+}
+
+/**
+ * ptrlist_rem - remove pointer node
+ * \p val: pointer to remove
+ * \p mm: memory context
+ */
+void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm)
+{
+ rem_node(&node->n);
+ mm_free(mm, node);
+}
+
+/**
+ * ptrlist_deep_free - free all nodes incl referenced data
+ * \p list: list nodes
+ * \p mm: memory context
+ */
+void ptrlist_deep_free(list_t *l, knot_mm_t *mm)
+{
+ ptrnode_t *n;
+ WALK_LIST(n, *l) {
+ mm_free(mm, n->d);
+ }
+ ptrlist_free(l, mm);
+}
+
+void ptrlist_free_custom(list_t *l, knot_mm_t *mm, ptrlist_free_cb free_cb)
+{
+ ptrnode_t *n;
+ WALK_LIST(n, *l) {
+ free_cb(n->d);
+ }
+ ptrlist_free(l, mm);
+}
diff --git a/src/contrib/ucw/lists.h b/src/contrib/ucw/lists.h
new file mode 100644
index 0000000..1a3ca95
--- /dev/null
+++ b/src/contrib/ucw/lists.h
@@ -0,0 +1,74 @@
+/*
+ * BIRD Library -- Linked Lists
+ *
+ * (c) 1998 Martin Mares <mj@ucw.cz>
+ * (c) 2015, 2020-2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#pragma once
+
+#include <string.h>
+#include "libknot/mm_ctx.h"
+
+typedef struct node {
+ struct node *next, *prev;
+} node_t;
+
+typedef struct list {
+ node_t head, tail;
+} list_t;
+
+#define NODE (node_t *)
+#define HEAD(list) ((void *)((list).head.next))
+#define TAIL(list) ((void *)((list).tail.prev))
+#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; \
+ n=(void *)((NODE (n))->next))
+#define WALK_LIST_DELSAFE(n,nxt,list) \
+ for(n=HEAD(list); (nxt=(void *)((NODE (n))->next)); n=(void *) nxt)
+/* WALK_LIST_FIRST supposes that called code removes each processed node */
+#define WALK_LIST_FIRST(n,list) \
+ while(n=HEAD(list), (NODE (n))->next)
+#define WALK_LIST_BACKWARDS(n,list) for(n=TAIL(list);(NODE (n))->prev; \
+ n=(void *)((NODE (n))->prev))
+#define WALK_LIST_BACKWARDS_DELSAFE(n,prv,list) \
+ for(n=TAIL(list); prv=(void *)((NODE (n))->prev); n=(void *) prv)
+
+#define EMPTY_LIST(list) (!(NODE HEAD(list))->next)
+
+/*! \brief Free every node in the list. */
+#define WALK_LIST_FREE(list) \
+ do { \
+ node_t *n=0,*nxt=0; \
+ WALK_LIST_DELSAFE(n,nxt,list) { \
+ free(n); \
+ } \
+ init_list(&list); \
+ } while(0)
+
+void add_tail(list_t *, node_t *);
+void add_head(list_t *, node_t *);
+void rem_node(node_t *);
+void add_tail_list(list_t *, list_t *);
+void init_list(list_t *);
+void insert_node(node_t *, node_t *);
+void list_dup(list_t *dst, list_t *src, size_t itemsz);
+size_t list_size(const list_t *);
+void fix_list(list_t *);
+
+/*!
+ * \brief Generic pointer list implementation.
+ */
+typedef struct ptrnode {
+ node_t n;
+ void *d;
+} ptrnode_t;
+
+ptrnode_t *ptrlist_add(list_t *, void *, knot_mm_t *);
+void ptrlist_free(list_t *, knot_mm_t *);
+void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm);
+void ptrlist_deep_free(list_t *, knot_mm_t *);
+
+typedef void (*ptrlist_free_cb)(void *);
+void ptrlist_free_custom(list_t *l, knot_mm_t *mm, ptrlist_free_cb free_cb);
diff --git a/src/contrib/ucw/mempool.c b/src/contrib/ucw/mempool.c
new file mode 100644
index 0000000..8e835c1
--- /dev/null
+++ b/src/contrib/ucw/mempool.c
@@ -0,0 +1,323 @@
+/*
+ * UCW Library -- Memory Pools (One-Time Allocation)
+ *
+ * (c) 1997--2001 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
+ * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#undef LOCAL_DEBUG
+
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "contrib/asan.h"
+#include "contrib/macros.h"
+#include "contrib/ucw/mempool.h"
+
+/** \todo This shouldn't be precalculated, but computed on load. */
+#define CPU_PAGE_SIZE 4096
+
+/** Align an integer \p s to the nearest higher multiple of \p a (which should be a power of two) **/
+#define ALIGN_TO(s, a) (((s)+a-1)&~(a-1))
+#define MP_CHUNK_TAIL ALIGN_TO(sizeof(struct mempool_chunk), CPU_STRUCT_ALIGN)
+#define MP_SIZE_MAX (~0U - MP_CHUNK_TAIL - CPU_PAGE_SIZE)
+#define DBG(s, ...)
+
+/** \note Imported MMAP backend from bigalloc.c */
+#define CONFIG_UCW_POOL_IS_MMAP
+#ifdef CONFIG_UCW_POOL_IS_MMAP
+#include <sys/mman.h>
+static void *
+page_alloc(uint64_t len)
+{
+ if (!len) {
+ return NULL;
+ }
+ if (len > SIZE_MAX) {
+ return NULL;
+ }
+ assert(!(len & (CPU_PAGE_SIZE-1)));
+ uint8_t *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
+ if (p == (uint8_t*) MAP_FAILED) {
+ return NULL;
+ }
+ return p;
+}
+
+static void
+page_free(void *start, uint64_t len)
+{
+ assert(!(len & (CPU_PAGE_SIZE-1)));
+ assert(!((uintptr_t) start & (CPU_PAGE_SIZE-1)));
+ munmap(start, len);
+}
+#endif
+
+struct mempool_chunk {
+ struct mempool_chunk *next;
+ unsigned size;
+};
+
+static unsigned
+mp_align_size(unsigned size)
+{
+#ifdef CONFIG_UCW_POOL_IS_MMAP
+ return ALIGN_TO(size + MP_CHUNK_TAIL, CPU_PAGE_SIZE) - MP_CHUNK_TAIL;
+#else
+ return ALIGN_TO(size, CPU_STRUCT_ALIGN);
+#endif
+}
+
+void
+mp_init(struct mempool *pool, unsigned chunk_size)
+{
+ chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
+ *pool = (struct mempool) {
+ .chunk_size = chunk_size,
+ .threshold = chunk_size >> 1,
+ .last_big = &pool->last_big
+ };
+}
+
+static void *
+mp_new_big_chunk(unsigned size)
+{
+ uint8_t *data = malloc(size + MP_CHUNK_TAIL);
+ if (!data) {
+ return NULL;
+ }
+ ASAN_POISON_MEMORY_REGION(data, size);
+ struct mempool_chunk *chunk = (struct mempool_chunk *)(data + size);
+ chunk->size = size;
+ return chunk;
+}
+
+static void
+mp_free_big_chunk(struct mempool_chunk *chunk)
+{
+ void *ptr = (uint8_t *)chunk - chunk->size;
+ ASAN_UNPOISON_MEMORY_REGION(ptr, chunk->size);
+ free(ptr);
+}
+
+static void *
+mp_new_chunk(unsigned size)
+{
+#ifdef CONFIG_UCW_POOL_IS_MMAP
+ uint8_t *data = page_alloc(size + MP_CHUNK_TAIL);
+ if (!data) {
+ return NULL;
+ }
+ ASAN_POISON_MEMORY_REGION(data, size);
+ struct mempool_chunk *chunk = (struct mempool_chunk *)(data + size);
+ chunk->size = size;
+ return chunk;
+#else
+ return mp_new_big_chunk(size);
+#endif
+}
+
+static void
+mp_free_chunk(struct mempool_chunk *chunk)
+{
+#ifdef CONFIG_UCW_POOL_IS_MMAP
+ uint8_t *data = (uint8_t *)chunk - chunk->size;
+ ASAN_UNPOISON_MEMORY_REGION(data, chunk->size);
+ page_free(data, chunk->size + MP_CHUNK_TAIL);
+#else
+ mp_free_big_chunk(chunk);
+#endif
+}
+
+struct mempool *
+mp_new(unsigned chunk_size)
+{
+ chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
+ struct mempool_chunk *chunk = mp_new_chunk(chunk_size);
+ struct mempool *pool = (void *)chunk - chunk_size;
+ ASAN_UNPOISON_MEMORY_REGION(pool, sizeof(*pool));
+ DBG("Creating mempool %p with %u bytes long chunks", pool, chunk_size);
+ chunk->next = NULL;
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ *pool = (struct mempool) {
+ .state = { .free = { chunk_size - sizeof(*pool) }, .last = { chunk } },
+ .chunk_size = chunk_size,
+ .threshold = chunk_size >> 1,
+ .last_big = &pool->last_big
+ };
+ return pool;
+}
+
+static void
+mp_free_chain(struct mempool_chunk *chunk)
+{
+ while (chunk) {
+ ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ struct mempool_chunk *next = chunk->next;
+ mp_free_chunk(chunk);
+ chunk = next;
+ }
+}
+
+static void
+mp_free_big_chain(struct mempool_chunk *chunk)
+{
+ while (chunk) {
+ ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ struct mempool_chunk *next = chunk->next;
+ mp_free_big_chunk(chunk);
+ chunk = next;
+ }
+}
+
+void
+mp_delete(struct mempool *pool)
+{
+ if (pool == NULL) {
+ return;
+ }
+ DBG("Deleting mempool %p", pool);
+ mp_free_big_chain(pool->state.last[1]);
+ mp_free_chain(pool->unused);
+ mp_free_chain(pool->state.last[0]); // can contain the mempool structure
+}
+
+void
+mp_flush(struct mempool *pool)
+{
+ mp_free_big_chain(pool->state.last[1]);
+ struct mempool_chunk *chunk = pool->state.last[0], *next;
+ while (chunk) {
+ ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ if ((uint8_t *)chunk - chunk->size == (uint8_t *)pool) {
+ break;
+ }
+ next = chunk->next;
+ chunk->next = pool->unused;
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ pool->unused = chunk;
+ chunk = next;
+ }
+ pool->state.last[0] = chunk;
+ if (chunk) {
+ pool->state.free[0] = chunk->size - sizeof(*pool);
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ } else {
+ pool->state.free[0] = 0;
+ }
+ pool->state.last[1] = NULL;
+ pool->state.free[1] = 0;
+ pool->last_big = &pool->last_big;
+}
+
+static void
+mp_stats_chain(struct mempool_chunk *chunk, struct mempool_stats *stats, unsigned idx)
+{
+ struct mempool_chunk *next;
+ while (chunk) {
+ ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ stats->chain_size[idx] += chunk->size + sizeof(*chunk);
+ stats->chain_count[idx]++;
+ next = chunk->next;
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ chunk = next;
+ }
+ stats->total_size += stats->chain_size[idx];
+}
+
+void
+mp_stats(struct mempool *pool, struct mempool_stats *stats)
+{
+ bzero(stats, sizeof(*stats));
+ mp_stats_chain(pool->state.last[0], stats, 0);
+ mp_stats_chain(pool->state.last[1], stats, 1);
+ mp_stats_chain(pool->unused, stats, 2);
+}
+
+uint64_t
+mp_total_size(struct mempool *pool)
+{
+ struct mempool_stats stats;
+ mp_stats(pool, &stats);
+ return stats.total_size;
+}
+
+static void *
+mp_alloc_internal(struct mempool *pool, unsigned size)
+{
+ struct mempool_chunk *chunk;
+ if (size <= pool->threshold) {
+ pool->idx = 0;
+ if (pool->unused) {
+ chunk = pool->unused;
+ ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ pool->unused = chunk->next;
+ } else {
+ chunk = mp_new_chunk(pool->chunk_size);
+ }
+ chunk->next = pool->state.last[0];
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ pool->state.last[0] = chunk;
+ pool->state.free[0] = pool->chunk_size - size;
+ return (uint8_t *)chunk - pool->chunk_size;
+ } else if (size <= MP_SIZE_MAX) {
+ pool->idx = 1;
+ unsigned aligned = ALIGN_TO(size, CPU_STRUCT_ALIGN);
+ chunk = mp_new_big_chunk(aligned);
+ if (!chunk) {
+ return NULL;
+ }
+ chunk->next = pool->state.last[1];
+ ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk));
+ pool->state.last[1] = chunk;
+ pool->state.free[1] = aligned - size;
+ return pool->last_big = (uint8_t *)chunk - aligned;
+ } else {
+ fprintf(stderr, "Cannot allocate %u bytes from a mempool", size);
+ assert(0);
+ return NULL;
+ }
+}
+
+void *
+mp_alloc(struct mempool *pool, unsigned size)
+{
+ unsigned avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1);
+ void *ptr = NULL;
+ if (size <= avail) {
+ pool->state.free[0] = avail - size;
+ ptr = (uint8_t*)pool->state.last[0] - avail;
+ } else {
+ ptr = mp_alloc_internal(pool, size);
+ }
+ ASAN_UNPOISON_MEMORY_REGION(ptr, size);
+ return ptr;
+}
+
+void *
+mp_alloc_noalign(struct mempool *pool, unsigned size)
+{
+ void *ptr = NULL;
+ if (size <= pool->state.free[0]) {
+ ptr = (uint8_t*)pool->state.last[0] - pool->state.free[0];
+ pool->state.free[0] -= size;
+ } else {
+ ptr = mp_alloc_internal(pool, size);
+ }
+ ASAN_UNPOISON_MEMORY_REGION(ptr, size);
+ return ptr;
+}
+
+void *
+mp_alloc_zero(struct mempool *pool, unsigned size)
+{
+ void *ptr = mp_alloc(pool, size);
+ bzero(ptr, size);
+ return ptr;
+}
diff --git a/src/contrib/ucw/mempool.h b/src/contrib/ucw/mempool.h
new file mode 100644
index 0000000..c5a4fa8
--- /dev/null
+++ b/src/contrib/ucw/mempool.h
@@ -0,0 +1,124 @@
+/*
+ * UCW Library -- Memory Pools
+ *
+ * (c) 1997--2005 Martin Mares <mj@ucw.cz>
+ * (c) 2007 Pavel Charvat <pchar@ucw.cz>
+ * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#pragma once
+
+#include <string.h>
+#include <stdint.h>
+
+#define CPU_STRUCT_ALIGN (sizeof(void*))
+
+/***
+ * [[defs]]
+ * Definitions
+ * -----------
+ ***/
+
+/**
+ * Memory pool state (see mp_push(), ...).
+ * You should use this one as an opaque handle only, the insides are internal.
+ **/
+struct mempool_state {
+ unsigned free[2];
+ void *last[2];
+};
+
+/**
+ * Memory pool.
+ * You should use this one as an opaque handle only, the insides are internal.
+ **/
+struct mempool {
+ struct mempool_state state;
+ void *unused, *last_big;
+ unsigned chunk_size, threshold, idx;
+};
+
+struct mempool_stats { /** Mempool statistics. See mp_stats(). **/
+ uint64_t total_size; /** Real allocated size in bytes. */
+ unsigned chain_count[3]; /** Number of allocated chunks in small/big/unused chains. */
+ unsigned chain_size[3]; /** Size of allocated chunks in small/big/unused chains. */
+};
+
+/***
+ * [[basic]]
+ * Basic manipulation
+ * ------------------
+ ***/
+
+/**
+ * Initialize a given mempool structure.
+ * \p chunk_size must be in the interval `[1, UINT_MAX / 2]`.
+ * It will allocate memory by this large chunks and take
+ * memory to satisfy requests from them.
+ *
+ * Memory pools can be treated as <<trans:respools,resources>>, see <<trans:res_mempool()>>.
+ **/
+void mp_init(struct mempool *pool, unsigned chunk_size);
+
+/**
+ * Allocate and initialize a new memory pool.
+ * See \ref mp_init() for \p chunk_size limitations.
+ *
+ * The new mempool structure is allocated on the new mempool.
+ *
+ * Memory pools can be treated as <<trans:respools,resources>>, see <<trans:res_mempool()>>.
+ **/
+struct mempool *mp_new(unsigned chunk_size);
+
+/**
+ * Cleanup mempool initialized by mp_init or mp_new.
+ * Frees all the memory allocated by this mempool and,
+ * if created by \ref mp_new(), the \p pool itself.
+ **/
+void mp_delete(struct mempool *pool);
+
+/**
+ * Frees all data on a memory pool, but leaves it working.
+ * It can keep some of the chunks allocated to serve
+ * further allocation requests. Leaves the \p pool alive,
+ * even if it was created with \ref mp_new().
+ **/
+void mp_flush(struct mempool *pool);
+
+/**
+ * Compute some statistics for debug purposes.
+ * See the definition of the <<struct_mempool_stats,mempool_stats structure>>.
+ **/
+void mp_stats(struct mempool *pool, struct mempool_stats *stats);
+uint64_t mp_total_size(struct mempool *pool); /** How many bytes were allocated by the pool. **/
+
+/***
+ * [[alloc]]
+ * Allocation routines
+ * -------------------
+ ***/
+
+/**
+ * The function allocates new \p size bytes on a given memory pool.
+ * If the \p size is zero, the resulting pointer is undefined,
+ * but it may be safely reallocated or used as the parameter
+ * to other functions below.
+ *
+ * The resulting pointer is always aligned to a multiple of
+ * `CPU_STRUCT_ALIGN` bytes and this condition remains true also
+ * after future reallocations.
+ **/
+void *mp_alloc(struct mempool *pool, unsigned size);
+
+/**
+ * The same as \ref mp_alloc(), but the result may be unaligned.
+ **/
+void *mp_alloc_noalign(struct mempool *pool, unsigned size);
+
+/**
+ * The same as \ref mp_alloc(), but fills the newly allocated memory with zeroes.
+ **/
+void *mp_alloc_zero(struct mempool *pool, unsigned size);
diff --git a/src/contrib/url-parser/LICENSE b/src/contrib/url-parser/LICENSE
new file mode 100644
index 0000000..0161703
--- /dev/null
+++ b/src/contrib/url-parser/LICENSE
@@ -0,0 +1 @@
+../licenses/MIT \ No newline at end of file
diff --git a/src/contrib/url-parser/README.md b/src/contrib/url-parser/README.md
new file mode 100644
index 0000000..c3b1919
--- /dev/null
+++ b/src/contrib/url-parser/README.md
@@ -0,0 +1,14 @@
+url-parser
+==========
+
+This is Joyent's [URL parser][original-source] from their
+[http-parser][joyent-http-parser] library.
+
+## LICENSE
+
+This derivative is licensed under the same terms as NGINX and copyright Joyent,
+Inc. and other Node contributors as outlined in their [LICENSE][].
+
+[original-source]: https://github.com/joyent/http-parser/blob/e01811e7f4894d7f0f7f4bd8492cccec6f6b4038/http_parser.c#L2343
+[joyent-http-parser]: https://github.com/joyent/http-parser
+[LICENSE]: https://github.com/joyent/http-parser/blob/master/LICENSE-MIT
diff --git a/src/contrib/url-parser/url_parser.c b/src/contrib/url-parser/url_parser.c
new file mode 100644
index 0000000..5084740
--- /dev/null
+++ b/src/contrib/url-parser/url_parser.c
@@ -0,0 +1,635 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/**
+ * This is the http_parser_parse_url function extracted from joyent's
+ * http-parser library.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "url_parser.h"
+
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+ (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
+
+#ifndef BIT_AT
+# define BIT_AT(a, i) \
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
+ (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+static const uint8_t normal_url_char[32] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) \
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_first_http_major
+ , s_res_http_major
+ , s_res_first_http_minor
+ , s_res_http_minor
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status_start
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_server_start
+ , s_req_server
+ , s_req_server_with_at
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_first_http_major
+ , s_req_http_major
+ , s_req_first_http_minor
+ , s_req_http_minor
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_discard_ws
+ , s_header_value_discard_ws_almost_done
+ , s_header_value_discard_lws
+ , s_header_value_start
+ , s_header_value
+ , s_header_value_lws
+
+ , s_header_almost_done
+
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
+ , s_headers_almost_done
+ , s_headers_done
+
+ /* Important: 's_headers_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+
+ , s_message_done
+ };
+
+
+enum http_host_state
+ {
+ s_http_host_dead = 1
+ , s_http_userinfo_start
+ , s_http_userinfo
+ , s_http_host_start
+ , s_http_host_v6_start
+ , s_http_host
+ , s_http_host_v6
+ , s_http_host_v6_end
+ , s_http_host_v6_zone_start
+ , s_http_host_v6_zone
+ , s_http_host_port_start
+ , s_http_host_port
+};
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
+
+ switch (s) {
+ case s_req_spaces_before_url:
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * All methods except CONNECT are followed by '/' or '*'.
+ */
+
+ if (ch == '/' || ch == '*') {
+ return s_req_path;
+ }
+
+ if (IS_ALPHA(ch)) {
+ return s_req_schema;
+ }
+
+ break;
+
+ case s_req_schema:
+ if (IS_ALPHA(ch)) {
+ return s;
+ }
+
+ if (ch == ':') {
+ return s_req_schema_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash:
+ if (ch == '/') {
+ return s_req_schema_slash_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash_slash:
+ if (ch == '/') {
+ return s_req_server_start;
+ }
+
+ break;
+
+ case s_req_server_with_at:
+ if (ch == '@') {
+ return s_dead;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_server_start:
+ case s_req_server:
+ if (ch == '/') {
+ return s_req_path;
+ }
+
+ if (ch == '?') {
+ return s_req_query_string_start;
+ }
+
+ if (ch == '@') {
+ return s_req_server_with_at;
+ }
+
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+ return s_req_server;
+ }
+
+ break;
+
+ case s_req_path:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_query_string_start;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_query_string_start:
+ case s_req_query_string:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_query_string;
+ }
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ return s_req_query_string;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_fragment_start:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_fragment;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_fragment;
+
+ case '#':
+ return s;
+ }
+
+ break;
+
+ case s_req_fragment:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ case '#':
+ return s;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* We should never fall out of the switch above unless there's an error */
+ return s_dead;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+ switch(s) {
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ if (ch == '@') {
+ return s_http_host_start;
+ }
+
+ if (IS_USERINFO_CHAR(ch)) {
+ return s_http_userinfo;
+ }
+ break;
+
+ case s_http_host_start:
+ if (ch == '[') {
+ return s_http_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ break;
+
+ case s_http_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_end:
+ if (ch == ':') {
+ return s_http_host_port_start;
+ }
+
+ break;
+
+ case s_http_host_v6:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_start:
+ if (IS_HEX(ch) || ch == ':' || ch == '.') {
+ return s_http_host_v6;
+ }
+
+ if (s == s_http_host_v6 && ch == '%') {
+ return s_http_host_v6_zone_start;
+ }
+ break;
+
+ case s_http_host_v6_zone:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_zone_start:
+ /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+ ch == '~') {
+ return s_http_host_v6_zone;
+ }
+ break;
+
+ case s_http_host_port:
+ case s_http_host_port_start:
+ if (IS_NUM(ch)) {
+ return s_http_host_port;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+ assert(u->field_set & (1 << UF_HOST));
+ enum http_host_state s;
+
+ const char *p;
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+ u->field_data[UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+ enum http_host_state new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return 1;
+ }
+
+ switch(new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ u->field_data[UF_PORT].off = p - buf;
+ u->field_data[UF_PORT].len = 0;
+ u->field_set |= (1 << UF_PORT);
+ }
+ u->field_data[UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ u->field_data[UF_USERINFO].off = p - buf ;
+ u->field_data[UF_USERINFO].len = 0;
+ u->field_set |= (1 << UF_USERINFO);
+ }
+ u->field_data[UF_USERINFO].len++;
+ break;
+
+ default:
+ break;
+ }
+ s = new_s;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_http_host_start:
+ case s_http_host_v6_start:
+ case s_http_host_v6:
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ case s_http_host_port_start:
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u)
+{
+ enum state s;
+ const char *p;
+ enum http_parser_url_fields uf, old_uf;
+ int found_at = 0;
+
+ u->port = u->field_set = 0;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+ old_uf = UF_MAX;
+
+ for (p = buf; p < buf + buflen; p++) {
+ s = parse_url_char(s, *p);
+
+ /* Figure out the next field that we're operating on */
+ switch (s) {
+ case s_dead:
+ return 1;
+
+ /* Skip delimiters */
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_query_string_start:
+ case s_req_fragment_start:
+ continue;
+
+ case s_req_schema:
+ uf = UF_SCHEMA;
+ break;
+
+ case s_req_server_with_at:
+ found_at = 1;
+
+ /* FALLTHROUGH */
+ case s_req_server:
+ uf = UF_HOST;
+ break;
+
+ case s_req_path:
+ uf = UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = UF_FRAGMENT;
+ break;
+
+ default:
+ assert(!"Unexpected state");
+ return 1;
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ u->field_data[uf].len++;
+ continue;
+ }
+
+ u->field_data[uf].off = p - buf;
+ u->field_data[uf].len = 1;
+
+ u->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((u->field_set & (1 << UF_SCHEMA)) &&
+ (u->field_set & (1 << UF_HOST)) == 0) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_HOST)) {
+ if (http_parse_host(buf, u, found_at) != 0) {
+ return 1;
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_PORT)) {
+ /* Don't bother with endp; we've already validated the string */
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return 1;
+ }
+
+ u->port = (uint16_t) v;
+ }
+
+ return 0;
+}
diff --git a/src/contrib/url-parser/url_parser.h b/src/contrib/url-parser/url_parser.h
new file mode 100644
index 0000000..0f916e0
--- /dev/null
+++ b/src/contrib/url-parser/url_parser.h
@@ -0,0 +1,64 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/**
+ * This is the http_parser_parse_url function extracted from joyent's
+ * http-parser library.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+enum http_parser_url_fields
+ { UF_SCHEMA = 0
+ , UF_HOST = 1
+ , UF_PORT = 2
+ , UF_PATH = 3
+ , UF_QUERY = 4
+ , UF_FRAGMENT = 5
+ , UF_USERINFO = 6
+ , UF_MAX = 7
+ };
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+ uint16_t field_set; /* Bitmask of (1 << UF_*) values */
+ uint16_t port; /* Converted UF_PORT string */
+
+ struct {
+ uint16_t off; /* Offset into buffer in which field starts */
+ uint16_t len; /* Length of run in buffer */
+ } field_data[UF_MAX];
+};
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u);
diff --git a/src/contrib/vpool/vpool.c b/src/contrib/vpool/vpool.c
new file mode 100644
index 0000000..f130a47
--- /dev/null
+++ b/src/contrib/vpool/vpool.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/vpool/vpool.h"
+
+static void vpool_shift(struct vpool *pool);
+static int vpool_new_size(struct vpool *pool, size_t datsize,
+ size_t *size);
+static int vpool_resize(struct vpool *pool, size_t datsize);
+
+static void
+vpool_shift(struct vpool *pool)
+{
+ if (pool->v_buf != pool->v_basebuf) {
+ memmove(pool->v_basebuf, pool->v_buf, pool->v_off);
+ pool->v_buf = pool->v_basebuf;
+ }
+}
+
+static int
+vpool_new_size(struct vpool *pool, size_t datsize, size_t *size)
+{
+ size_t need;
+ size_t rem;
+
+ if (datsize <= pool->v_size - pool->v_off) {
+ *size = pool->v_size;
+ return (0);
+ }
+
+ /* Check limit of new requested size */
+ if (pool->v_limit - pool->v_off < datsize) {
+ return (EFBIG);
+ }
+ need = pool->v_off + datsize;
+
+ /* Check limit of new size aligned to block size */
+ rem = need % pool->v_blksize;
+ if (rem != 0) {
+ if (pool->v_limit - pool->v_off >=
+ datsize + (pool->v_blksize - rem)) {
+ need += pool->v_blksize - rem;
+ } else {
+ need = pool->v_limit;
+ }
+ }
+
+ *size = need;
+ return (0);
+}
+
+static int
+vpool_resize(struct vpool *pool, size_t datsize)
+{
+ void *ret;
+ size_t size;
+ int error;
+
+ error = vpool_new_size(pool, datsize, &size);
+ if (error != 0) {
+ return (error);
+ }
+
+ if (size > pool->v_size) {
+ ret = malloc(size);
+ if (ret == NULL) {
+ return (ENOMEM);
+ }
+
+ if (pool->v_off > 0) {
+ memcpy(ret, pool->v_buf, pool->v_off);
+ }
+ free(pool->v_basebuf);
+ pool->v_basebuf = pool->v_buf = ret;
+ pool->v_size = size;
+ } else if ((pool->v_size - pool->v_off) -
+ (pool->v_buf - pool->v_basebuf) < datsize) {
+ vpool_shift(pool);
+ }
+
+ return (0);
+}
+
+void
+vpool_init(struct vpool *pool, size_t blksize, size_t limit)
+{
+
+ pool->v_basebuf = pool->v_buf = NULL;
+ pool->v_off = pool->v_size = 0;
+
+ pool->v_blksize = (blksize == 0) ? 4096 : blksize; /* XXX */
+ pool->v_limit = (limit == 0) ? SIZE_MAX : limit;
+
+ pool->v_lasterr = 0;
+}
+
+void
+vpool_final(struct vpool *pool)
+{
+ free(pool->v_basebuf);
+}
+
+void
+vpool_reset(struct vpool *pool)
+{
+ free(pool->v_basebuf);
+ pool->v_basebuf = pool->v_buf = NULL;
+ pool->v_off = pool->v_size = 0;
+ pool->v_lasterr = 0;
+}
+
+void
+vpool_wipe(struct vpool *pool)
+{
+ pool->v_off = 0;
+ pool->v_lasterr = 0;
+}
+
+void *
+vpool_insert(struct vpool *pool, size_t where, void *data, size_t datsize)
+{
+ void *ret;
+ int error;
+
+ error = vpool_resize(pool, datsize);
+ if (error != 0) {
+ pool->v_lasterr = error;
+ return (NULL);
+ }
+
+ /*
+ * If ``where'' is greater than or equal to offset then
+ * we are appending data to the end of the buffer.
+ */
+ if (where > pool->v_off) {
+ where = pool->v_off;
+ }
+
+ ret = (uint8_t *)pool->v_buf + where;
+ if (pool->v_off - where > 0) {
+ memmove(ret + datsize, ret, pool->v_off - where);
+ }
+ memcpy(ret, data, datsize);
+ pool->v_off += datsize;
+ pool->v_lasterr = 0;
+
+ return (ret);
+}
+
+void *
+vpool_expand(struct vpool *pool, size_t where, size_t size)
+{
+ void *ret;
+ int error;
+
+ error = vpool_resize(pool, size);
+ if (error != 0) {
+ pool->v_lasterr = error;
+ return (NULL);
+ }
+
+ /*
+ * If ``where'' is greater than or equal to offset then
+ * we are appending data to the end of the buffer.
+ */
+ if (where > pool->v_off) {
+ where = pool->v_off;
+ }
+
+ ret = (uint8_t *)pool->v_buf + where;
+ if (pool->v_off - where > 0) {
+ memmove(ret + size, ret, pool->v_off - where);
+ }
+ pool->v_off += size;
+ pool->v_lasterr = 0;
+
+ return (ret);
+}
+
+int
+vpool_truncate(struct vpool *pool,
+ size_t where, size_t size, enum vpool_trunc how)
+{
+ /* Check if caller wants to remove more data than we have */
+ if (where >= pool->v_off ||
+ size > pool->v_off || pool->v_off - size < where) {
+ pool->v_lasterr = ERANGE;
+ return (pool->v_lasterr);
+ }
+
+ if (how == VPOOL_EXCLUDE) {
+ if (where == 0) {
+ /*
+ * Optimization.
+ * Don't move data, just adjust pointer.
+ */
+ pool->v_buf = (uint8_t *)pool->v_buf + size;
+ } else {
+ memmove((uint8_t *)pool->v_buf + where,
+ (uint8_t *)pool->v_buf + where + size,
+ pool->v_off - size - where);
+ }
+ pool->v_off -= size;
+ } else {
+ pool->v_buf = (uint8_t *)pool->v_buf + where;
+ pool->v_off = size;
+ }
+
+ pool->v_lasterr = 0;
+ return (0);
+}
+
+void
+vpool_export(struct vpool *pool, void **buf, size_t *size)
+{
+ vpool_shift(pool);
+ *buf = pool->v_buf;
+ *size = pool->v_off;
+ pool->v_basebuf = pool->v_buf = NULL;
+ pool->v_off = pool->v_size = 0;
+ pool->v_lasterr = 0;
+}
diff --git a/src/contrib/vpool/vpool.h b/src/contrib/vpool/vpool.h
new file mode 100644
index 0000000..82e3d66
--- /dev/null
+++ b/src/contrib/vpool/vpool.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * VPool: implementation of pool of data with a variable size.
+ */
+#ifndef _VPOOL_H_
+#define _VPOOL_H_
+
+#include <stddef.h>
+#include <limits.h>
+
+struct vpool {
+ void *v_basebuf; /* pointer returned by (re|m)alloc() */
+ void *v_buf; /* actual data starts here */
+ size_t v_off;
+ size_t v_size;
+
+ size_t v_blksize;
+ size_t v_limit;
+ int v_lasterr;
+};
+
+enum vpool_trunc {VPOOL_EXCLUDE, VPOOL_INCLUDE};
+#define VPOOL_TAIL UINT_MAX
+
+void vpool_init(struct vpool *pool, size_t blksize, size_t limit);
+void vpool_final(struct vpool *pool);
+
+void vpool_reset(struct vpool *pool);
+void vpool_wipe(struct vpool *pool);
+
+void * vpool_insert(struct vpool *pool,
+ size_t where, void *data, size_t datsize);
+void * vpool_expand(struct vpool *pool, size_t where, size_t size);
+
+int vpool_truncate(struct vpool *pool,
+ size_t where, size_t size, enum vpool_trunc how);
+
+#define vpool_is_empty(pool) ((pool)->v_off == 0)
+#define vpool_get_buf(pool) ((pool)->v_buf)
+#define vpool_get_length(pool) ((pool)->v_off)
+#define vpool_get_error(pool) ((pool)->v_lasterr)
+
+void vpool_export(struct vpool *pool, void **buf, size_t *size);
+
+#endif /* !_VPOOL_H_ */
diff --git a/src/contrib/wire_ctx.h b/src/contrib/wire_ctx.h
new file mode 100644
index 0000000..f4f1789
--- /dev/null
+++ b/src/contrib/wire_ctx.h
@@ -0,0 +1,355 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "contrib/string.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+
+/*!
+ * \brief Struct to keep the wire context.
+ */
+typedef struct wire_ctx {
+ size_t size;
+ uint8_t *wire;
+ uint8_t *position;
+ int error;
+ bool readonly;
+} wire_ctx_t;
+
+/*!
+ * \brief Initialize wire context.
+ */
+static inline wire_ctx_t wire_ctx_init(uint8_t *data, size_t size)
+{
+ assert(data);
+
+ wire_ctx_t result = {
+ .size = size,
+ .wire = data,
+ .position = data,
+ .error = KNOT_EOK,
+ .readonly = false
+ };
+
+ return result;
+}
+
+/*!
+ * \brief Initialize read only wire context.
+ *
+ * \note No write is performed, and error is set to KNOT_EACCES.
+ *
+ */
+static inline wire_ctx_t wire_ctx_init_const(const uint8_t *data, size_t size)
+{
+ assert(data);
+
+ wire_ctx_t result = wire_ctx_init((uint8_t *)data, size);
+ result.readonly = true;
+
+ return result;
+}
+
+/*!
+ * \brief Gets actual position.
+ *
+ * \return position from the begin.
+ */
+static inline size_t wire_ctx_offset(wire_ctx_t *ctx)
+{
+ assert(ctx);
+
+ return ctx->position - ctx->wire;
+}
+
+/*!
+ * \brief Set position offset from the begin.
+ *
+ * \param offset Wire offset (starts from 0).
+ *
+ * \note Noop if previous error.
+ */
+static inline void wire_ctx_set_offset(wire_ctx_t *ctx, size_t offset)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return;
+ }
+
+ if (offset > ctx->size) {
+ ctx->error = KNOT_ERANGE;
+ return;
+ }
+
+ ctx->position = ctx->wire + offset;
+}
+
+/*!
+ * \brief Gets available bytes.
+ *
+ * \return Number of bytes to end.
+ */
+static inline size_t wire_ctx_available(wire_ctx_t *ctx)
+{
+ assert(ctx);
+
+ return ctx->size - wire_ctx_offset(ctx);
+}
+
+/*!
+ * \brief Add offset to the current position.
+ *
+ * \note Noop if previous error.
+ */
+static inline void wire_ctx_skip(wire_ctx_t *ctx, ssize_t offset)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return;
+ }
+
+ // Check for out of scope skip.
+ if (offset >= 0) {
+ if (offset > wire_ctx_available(ctx)) {
+ ctx->error = KNOT_ERANGE;
+ return;
+ }
+ } else {
+ if (-offset > wire_ctx_offset(ctx)) {
+ ctx->error = KNOT_ERANGE;
+ return;
+ }
+ }
+
+ ctx->position += offset;
+}
+
+/*!
+ * \brief Check the context if reading is possible.
+ */
+static inline int wire_ctx_can_read(wire_ctx_t *ctx, size_t size)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return ctx->error;
+ }
+
+ if (wire_ctx_available(ctx) < size) {
+ return KNOT_EFEWDATA;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Check the context if writing is possible.
+ */
+static inline int wire_ctx_can_write(wire_ctx_t *ctx, size_t size)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return ctx->error;
+ }
+
+ if (ctx->readonly) {
+ return KNOT_EACCES;
+ }
+
+ if (wire_ctx_available(ctx) < size) {
+ return KNOT_ESPACE;
+ }
+
+ return KNOT_EOK;
+}
+
+
+static inline void wire_ctx_read(wire_ctx_t *ctx, void *data, size_t size)
+{
+ assert(ctx);
+ assert(data);
+
+ if (ctx->error != KNOT_EOK) {
+ /* Avoid leaving data uninitialized. */
+ memzero(data, size);
+ return;
+ }
+
+ int ret = wire_ctx_can_read(ctx, size);
+ if (ret != KNOT_EOK) {
+ ctx->error = ret;
+ memzero(data, size);
+ return;
+ }
+
+ memcpy(data, ctx->position, size);
+ ctx->position += size;
+}
+
+static inline uint8_t wire_ctx_read_u8(wire_ctx_t *ctx)
+{
+ uint8_t result;
+ wire_ctx_read(ctx, &result, sizeof(result));
+
+ return result;
+}
+
+static inline uint16_t wire_ctx_read_u16(wire_ctx_t *ctx)
+{
+ uint16_t result;
+ wire_ctx_read(ctx, &result, sizeof(result));
+
+ return be16toh(result);
+}
+
+static inline uint32_t wire_ctx_read_u32(wire_ctx_t *ctx)
+{
+ uint32_t result;
+ wire_ctx_read(ctx, &result, sizeof(result));
+
+ return be32toh(result);
+}
+
+static inline uint64_t wire_ctx_read_u48(wire_ctx_t *ctx)
+{
+ /* This case is slightly tricky. */
+ uint64_t result = 0;
+ wire_ctx_read(ctx, (uint8_t *)&result + 1, 6);
+
+ return be64toh(result) >> 8;
+}
+
+static inline uint64_t wire_ctx_read_u64(wire_ctx_t *ctx)
+{
+ uint64_t result;
+ wire_ctx_read(ctx, &result, sizeof(result));
+
+ return be64toh(result);
+}
+
+
+static inline void wire_ctx_write(wire_ctx_t *ctx, const void *data, size_t size)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return;
+ }
+
+ if (size == 0) {
+ return;
+ }
+
+ assert(data);
+
+ int ret = wire_ctx_can_write(ctx, size);
+ if (ret != KNOT_EOK) {
+ ctx->error = ret;
+ return;
+ }
+
+ memcpy(ctx->position, data, size);
+ ctx->position += size;
+}
+
+static inline void wire_ctx_write_u8(wire_ctx_t *ctx, uint8_t value)
+{
+ wire_ctx_write(ctx, &value, sizeof(value));
+}
+
+static inline void wire_ctx_write_u16(wire_ctx_t *ctx, uint16_t value)
+{
+ uint16_t beval = htobe16(value);
+ wire_ctx_write(ctx, &beval, sizeof(beval));
+}
+
+static inline void wire_ctx_write_u32(wire_ctx_t *ctx, uint32_t value)
+{
+ uint32_t beval = htobe32(value);
+ wire_ctx_write(ctx, &beval, sizeof(beval));
+}
+
+static inline void wire_ctx_write_u48(wire_ctx_t *ctx, uint64_t value)
+{
+ /* This case is slightly tricky. */
+ uint64_t swapped = htobe64(value << 8);
+ wire_ctx_write(ctx, (uint8_t *)&swapped + 1, 6);
+}
+
+static inline void wire_ctx_write_u64(wire_ctx_t *ctx, uint64_t value)
+{
+ uint64_t beval = htobe64(value);
+ wire_ctx_write(ctx, &beval, sizeof(beval));
+}
+
+static inline void wire_ctx_clear(wire_ctx_t *ctx, size_t size)
+{
+ assert(ctx);
+
+ if (ctx->error != KNOT_EOK) {
+ return;
+ }
+
+ if (size == 0) {
+ return;
+ }
+
+ int ret = wire_ctx_can_write(ctx, size);
+ if (ret != KNOT_EOK) {
+ ctx->error = ret;
+ return;
+ }
+
+ memzero(ctx->position, size);
+ ctx->position += size;
+}
+
+static inline void wire_ctx_copy(wire_ctx_t *dst, wire_ctx_t *src, size_t size)
+{
+ assert(dst);
+ assert(src);
+
+ if (size == 0 || dst->error != KNOT_EOK) {
+ return;
+ }
+
+ if (wire_ctx_can_read(src, size) != KNOT_EOK) {
+ dst->error = KNOT_EFEWDATA;
+ return;
+ }
+
+ int ret = wire_ctx_can_write(dst, size);
+ if (ret != KNOT_EOK) {
+ dst->error = ret;
+ return;
+ }
+
+ memcpy(dst->position, src->position, size);
+ dst->position += size;
+ src->position += size;
+}
diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc
new file mode 100644
index 0000000..c28e6a8
--- /dev/null
+++ b/src/knot/Makefile.inc
@@ -0,0 +1,240 @@
+libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libkqueue_CFLAGS) \
+ $(liburcu_CFLAGS) $(lmdb_CFLAGS) $(systemd_CFLAGS) \
+ $(gnutls_CFLAGS) $(libngtcp2_CFLAGS) -DKNOTD_MOD_STATIC
+libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_'
+libknotd_la_LIBADD = $(dlopen_LIBS) $(libkqueue_LIBS) $(pthread_LIBS) \
+ $(libngtcp2_LIBS)
+libknotd_LIBS = libknotd.la libknot.la libdnssec.la libzscanner.la \
+ $(libcontrib_LIBS) $(liburcu_LIBS) $(lmdb_LIBS) \
+ $(systemd_LIBS) $(gnutls_LIBS)
+
+if EMBEDDED_LIBNGTCP2
+libknotd_la_LIBADD += $(libembngtcp2_LIBS)
+endif EMBEDDED_LIBNGTCP2
+
+include_libknotddir = $(includedir)/knot
+include_libknotd_HEADERS = \
+ knot/include/module.h
+
+libknotd_la_SOURCES = \
+ knot/catalog/catalog_db.c \
+ knot/catalog/catalog_db.h \
+ knot/catalog/catalog_update.c \
+ knot/catalog/catalog_update.h \
+ knot/catalog/generate.c \
+ knot/catalog/generate.h \
+ knot/catalog/interpret.c \
+ knot/catalog/interpret.h \
+ knot/conf/base.c \
+ knot/conf/base.h \
+ knot/conf/conf.c \
+ knot/conf/conf.h \
+ knot/conf/confdb.c \
+ knot/conf/confdb.h \
+ knot/conf/confio.c \
+ knot/conf/confio.h \
+ knot/conf/migration.c \
+ knot/conf/migration.h \
+ knot/conf/module.h \
+ knot/conf/module.c \
+ knot/conf/schema.c \
+ knot/conf/schema.h \
+ knot/conf/tools.c \
+ knot/conf/tools.h \
+ knot/ctl/commands.c \
+ knot/ctl/commands.h \
+ knot/ctl/process.c \
+ knot/ctl/process.h \
+ knot/dnssec/context.c \
+ knot/dnssec/context.h \
+ knot/dnssec/ds_query.c \
+ knot/dnssec/ds_query.h \
+ knot/dnssec/kasp/kasp_db.c \
+ knot/dnssec/kasp/kasp_db.h \
+ knot/dnssec/kasp/kasp_zone.c \
+ knot/dnssec/kasp/kasp_zone.h \
+ knot/dnssec/kasp/keystate.c \
+ knot/dnssec/kasp/keystate.h \
+ knot/dnssec/kasp/keystore.c \
+ knot/dnssec/kasp/keystore.h \
+ knot/dnssec/kasp/policy.h \
+ knot/dnssec/key-events.c \
+ knot/dnssec/key-events.h \
+ knot/dnssec/key_records.c \
+ knot/dnssec/key_records.h \
+ knot/dnssec/nsec-chain.c \
+ knot/dnssec/nsec-chain.h \
+ knot/dnssec/nsec3-chain.c \
+ knot/dnssec/nsec3-chain.h \
+ knot/dnssec/policy.c \
+ knot/dnssec/policy.h \
+ knot/dnssec/rrset-sign.c \
+ knot/dnssec/rrset-sign.h \
+ knot/dnssec/zone-events.c \
+ knot/dnssec/zone-events.h \
+ knot/dnssec/zone-keys.c \
+ knot/dnssec/zone-keys.h \
+ knot/dnssec/zone-nsec.c \
+ knot/dnssec/zone-nsec.h \
+ knot/dnssec/zone-sign.c \
+ knot/dnssec/zone-sign.h \
+ knot/events/events.c \
+ knot/events/events.h \
+ knot/events/handlers.h \
+ knot/events/handlers/backup.c \
+ knot/events/handlers/dnssec.c \
+ knot/events/handlers/ds_check.c \
+ knot/events/handlers/ds_push.c \
+ knot/events/handlers/expire.c \
+ knot/events/handlers/flush.c \
+ knot/events/handlers/freeze_thaw.c \
+ knot/events/handlers/load.c \
+ knot/events/handlers/notify.c \
+ knot/events/handlers/refresh.c \
+ knot/events/handlers/update.c \
+ knot/events/replan.c \
+ knot/events/replan.h \
+ knot/nameserver/axfr.c \
+ knot/nameserver/axfr.h \
+ knot/nameserver/chaos.c \
+ knot/nameserver/chaos.h \
+ knot/nameserver/internet.c \
+ knot/nameserver/internet.h \
+ knot/nameserver/ixfr.c \
+ knot/nameserver/ixfr.h \
+ knot/nameserver/log.h \
+ knot/nameserver/notify.c \
+ knot/nameserver/notify.h \
+ knot/nameserver/nsec_proofs.c \
+ knot/nameserver/nsec_proofs.h \
+ knot/nameserver/process_query.c \
+ knot/nameserver/process_query.h \
+ knot/nameserver/query_module.c \
+ knot/nameserver/query_module.h \
+ knot/nameserver/tsig_ctx.c \
+ knot/nameserver/tsig_ctx.h \
+ knot/nameserver/update.c \
+ knot/nameserver/update.h \
+ knot/nameserver/xfr.c \
+ knot/nameserver/xfr.h \
+ knot/query/capture.c \
+ knot/query/capture.h \
+ knot/query/layer.h \
+ knot/query/query.c \
+ knot/query/query.h \
+ knot/query/requestor.c \
+ knot/query/requestor.h \
+ knot/common/evsched.c \
+ knot/common/evsched.h \
+ knot/common/fdset.c \
+ knot/common/fdset.h \
+ knot/common/log.c \
+ knot/common/log.h \
+ knot/common/process.c \
+ knot/common/process.h \
+ knot/common/stats.c \
+ knot/common/stats.h \
+ knot/common/systemd.c \
+ knot/common/systemd.h \
+ knot/common/unreachable.c \
+ knot/common/unreachable.h \
+ knot/journal/journal_basic.c \
+ knot/journal/journal_basic.h \
+ knot/journal/journal_metadata.c \
+ knot/journal/journal_metadata.h \
+ knot/journal/journal_read.c \
+ knot/journal/journal_read.h \
+ knot/journal/journal_write.c \
+ knot/journal/journal_write.h \
+ knot/journal/knot_lmdb.c \
+ knot/journal/knot_lmdb.h \
+ knot/journal/serialization.c \
+ knot/journal/serialization.h \
+ knot/server/dthreads.c \
+ knot/server/dthreads.h \
+ knot/server/proxyv2.c \
+ knot/server/proxyv2.h \
+ knot/server/server.c \
+ knot/server/server.h \
+ knot/server/tcp-handler.c \
+ knot/server/tcp-handler.h \
+ knot/server/udp-handler.c \
+ knot/server/udp-handler.h \
+ knot/server/xdp-handler.c \
+ knot/server/xdp-handler.h \
+ knot/updates/acl.c \
+ knot/updates/acl.h \
+ knot/updates/apply.c \
+ knot/updates/apply.h \
+ knot/updates/changesets.c \
+ knot/updates/changesets.h \
+ knot/updates/ddns.c \
+ knot/updates/ddns.h \
+ knot/updates/zone-update.c \
+ knot/updates/zone-update.h \
+ knot/worker/pool.c \
+ knot/worker/pool.h \
+ knot/worker/queue.c \
+ knot/worker/queue.h \
+ knot/zone/adds_tree.c \
+ knot/zone/adds_tree.h \
+ knot/zone/adjust.c \
+ knot/zone/adjust.h \
+ knot/zone/backup.c \
+ knot/zone/backup.h \
+ knot/zone/backup_dir.c \
+ knot/zone/backup_dir.h \
+ knot/zone/contents.c \
+ knot/zone/contents.h \
+ knot/zone/digest.c \
+ knot/zone/digest.h \
+ knot/zone/measure.h \
+ knot/zone/measure.c \
+ knot/zone/node.c \
+ knot/zone/node.h \
+ knot/zone/semantic-check.c \
+ knot/zone/semantic-check.h \
+ knot/zone/serial.c \
+ knot/zone/serial.h \
+ knot/zone/timers.c \
+ knot/zone/timers.h \
+ knot/zone/zone-diff.c \
+ knot/zone/zone-diff.h \
+ knot/zone/zone-dump.c \
+ knot/zone/zone-dump.h \
+ knot/zone/zone-load.c \
+ knot/zone/zone-load.h \
+ knot/zone/zone-tree.c \
+ knot/zone/zone-tree.h \
+ knot/zone/zone.c \
+ knot/zone/zone.h \
+ knot/zone/zonedb-load.c \
+ knot/zone/zonedb-load.h \
+ knot/zone/zonedb.c \
+ knot/zone/zonedb.h \
+ knot/zone/zonefile.c \
+ knot/zone/zonefile.h
+
+if HAVE_DAEMON
+noinst_LTLIBRARIES += libknotd.la
+pkgconfig_DATA += knotd.pc
+endif HAVE_DAEMON
+
+KNOTD_MOD_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY)
+KNOTD_MOD_LDFLAGS = $(AM_LDFLAGS) -module -shared -avoid-version
+
+pkglibdir = $(module_instdir)
+pkglib_LTLIBRARIES =
+
+include $(srcdir)/knot/modules/cookies/Makefile.inc
+include $(srcdir)/knot/modules/dnsproxy/Makefile.inc
+include $(srcdir)/knot/modules/dnstap/Makefile.inc
+include $(srcdir)/knot/modules/geoip/Makefile.inc
+include $(srcdir)/knot/modules/noudp/Makefile.inc
+include $(srcdir)/knot/modules/onlinesign/Makefile.inc
+include $(srcdir)/knot/modules/probe/Makefile.inc
+include $(srcdir)/knot/modules/queryacl/Makefile.inc
+include $(srcdir)/knot/modules/rrl/Makefile.inc
+include $(srcdir)/knot/modules/stats/Makefile.inc
+include $(srcdir)/knot/modules/synthrecord/Makefile.inc
+include $(srcdir)/knot/modules/whoami/Makefile.inc
diff --git a/src/knot/catalog/catalog_db.c b/src/knot/catalog/catalog_db.c
new file mode 100644
index 0000000..b483f4d
--- /dev/null
+++ b/src/knot/catalog/catalog_db.c
@@ -0,0 +1,347 @@
+/* Copyright (C) 2021 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 <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <urcu.h>
+
+#include "contrib/files.h"
+#include "knot/catalog/catalog_db.h"
+#include "knot/common/log.h"
+
+static const MDB_val catalog_iter_prefix = { 1, "" };
+
+size_t catalog_dname_append(knot_dname_storage_t storage, const knot_dname_t *name)
+{
+ size_t old_len = knot_dname_size(storage);
+ size_t name_len = knot_dname_size(name);
+ size_t new_len = old_len - 1 + name_len;
+ if (old_len == 0 || name_len == 0 || new_len > KNOT_DNAME_MAXLEN) {
+ return 0;
+ }
+ memcpy(storage + old_len - 1, name, name_len);
+ return new_len;
+}
+
+int catalog_bailiwick_shift(const knot_dname_t *subname, const knot_dname_t *name)
+{
+ const knot_dname_t *res = subname;
+ while (!knot_dname_is_equal(res, name)) {
+ if (*res == '\0') {
+ return -1;
+ }
+ res = knot_wire_next_label(res, NULL);
+ }
+ return res - subname;
+}
+
+void catalog_init(catalog_t *cat, const char *path, size_t mapsize)
+{
+ knot_lmdb_init(&cat->db, path, mapsize, MDB_NOTLS, NULL);
+}
+
+static void ensure_cat_version(knot_lmdb_txn_t *ro_txn, knot_lmdb_txn_t *rw_txn)
+{
+ MDB_val key = { 8, "\x01version" };
+ if (knot_lmdb_find(ro_txn, &key, KNOT_LMDB_EXACT)) {
+ if (strncmp(CATALOG_VERSION, ro_txn->cur_val.mv_data,
+ ro_txn->cur_val.mv_size) != 0) {
+ log_warning("catalog version mismatch");
+ }
+ } else if (rw_txn != NULL) {
+ MDB_val val = { strlen(CATALOG_VERSION), CATALOG_VERSION };
+ knot_lmdb_insert(rw_txn, &key, &val);
+ }
+}
+
+// does NOT check for catalog zone version by RFC, this is Knot-specific in the cat LMDB !
+static void check_cat_version(catalog_t *cat)
+{
+ if (cat->ro_txn->ret == KNOT_EOK) {
+ ensure_cat_version(cat->ro_txn, cat->rw_txn);
+ }
+}
+
+int catalog_open(catalog_t *cat)
+{
+ if (!knot_lmdb_is_open(&cat->db)) {
+ int ret = knot_lmdb_open(&cat->db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ if (cat->ro_txn == NULL) {
+ knot_lmdb_txn_t *ro_txn = calloc(1, sizeof(*ro_txn));
+ if (ro_txn == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_lmdb_begin(&cat->db, ro_txn, false);
+ cat->ro_txn = ro_txn;
+ }
+ check_cat_version(cat);
+ return cat->ro_txn->ret;
+}
+
+int catalog_begin(catalog_t *cat)
+{
+ int ret = catalog_open(cat);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t *rw_txn = calloc(1, sizeof(*rw_txn));
+ if (rw_txn == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_lmdb_begin(&cat->db, rw_txn, true);
+ if (rw_txn->ret != KNOT_EOK) {
+ ret = rw_txn->ret;
+ free(rw_txn);
+ return ret;
+ }
+ assert(cat->rw_txn == NULL); // LMDB prevents two existing RW txns at a time
+ cat->rw_txn = rw_txn;
+ check_cat_version(cat);
+ return cat->rw_txn->ret;
+}
+
+int catalog_commit(catalog_t *cat)
+{
+ knot_lmdb_txn_t *rw_txn = rcu_xchg_pointer(&cat->rw_txn, NULL);
+ knot_lmdb_commit(rw_txn);
+ int ret = rw_txn->ret;
+ free(rw_txn);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // now refresh RO txn
+ knot_lmdb_txn_t *ro_txn = calloc(1, sizeof(*ro_txn));
+ if (ro_txn == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_lmdb_begin(&cat->db, ro_txn, false);
+ cat->old_ro_txn = rcu_xchg_pointer(&cat->ro_txn, ro_txn);
+
+ return KNOT_EOK;
+}
+
+void catalog_abort(catalog_t *cat)
+{
+ knot_lmdb_txn_t *rw_txn = rcu_xchg_pointer(&cat->rw_txn, NULL);
+ if (rw_txn != NULL) {
+ knot_lmdb_abort(rw_txn);
+ free(rw_txn);
+ }
+}
+
+void catalog_commit_cleanup(catalog_t *cat)
+{
+ knot_lmdb_txn_t *old_ro_txn = rcu_xchg_pointer(&cat->old_ro_txn, NULL);
+ if (old_ro_txn != NULL) {
+ knot_lmdb_abort(old_ro_txn);
+ free(old_ro_txn);
+ }
+}
+
+void catalog_deinit(catalog_t *cat)
+{
+ assert(cat->rw_txn == NULL);
+ if (cat->ro_txn != NULL) {
+ knot_lmdb_abort(cat->ro_txn);
+ free(cat->ro_txn);
+ }
+ if (cat->old_ro_txn != NULL) {
+ knot_lmdb_abort(cat->old_ro_txn);
+ free(cat->old_ro_txn);
+ }
+ knot_lmdb_deinit(&cat->db);
+}
+
+int catalog_add(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catzone,
+ const char *group)
+{
+ if (cat->rw_txn == NULL) {
+ return KNOT_EINVAL;
+ }
+ int bail = catalog_bailiwick_shift(owner, catzone);
+ if (bail < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+ assert(bail >= 0 && bail < 256);
+ MDB_val key = knot_lmdb_make_key("BN", 0, member); // 0 for future purposes
+ MDB_val val = knot_lmdb_make_key("BBNS", 0, bail, owner, group);
+
+ knot_lmdb_insert(cat->rw_txn, &key, &val);
+ free(key.mv_data);
+ free(val.mv_data);
+ return cat->rw_txn->ret;
+}
+
+int catalog_del(catalog_t *cat, const knot_dname_t *member)
+{
+ if (cat->rw_txn == NULL) {
+ return KNOT_EINVAL;
+ }
+ MDB_val key = knot_lmdb_make_key("BN", 0, member);
+ knot_lmdb_del_prefix(cat->rw_txn, &key); // deletes one record
+ free(key.mv_data);
+ return cat->rw_txn->ret;
+}
+
+static void unmake_val(MDB_val *val, const knot_dname_t **owner,
+ const knot_dname_t **catz, const char **group)
+{
+ uint8_t zero, shift;
+ *group = ""; // backward compatibility with Knot 3.0
+ knot_lmdb_unmake_key(val->mv_data, val->mv_size, "BBNS", &zero, &shift,
+ owner, group);
+ *catz = *owner + shift;
+}
+
+static int find_threadsafe(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t **owner, const knot_dname_t **catz,
+ const char **group, void **tofree)
+{
+ *tofree = NULL;
+ if (cat->ro_txn == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ MDB_val key = knot_lmdb_make_key("BN", 0, member), val = { 0 };
+
+ int ret = knot_lmdb_find_threadsafe(cat->ro_txn, &key, &val, KNOT_LMDB_EXACT);
+ if (ret == KNOT_EOK) {
+ unmake_val(&val, owner, catz, group);
+ *tofree = val.mv_data;
+ }
+ free(key.mv_data);
+ return ret;
+}
+
+int catalog_get_catz(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t **catz, const char **group, void **tofree)
+{
+ const knot_dname_t *unused;
+ return find_threadsafe(cat, member, &unused, catz, group, tofree);
+}
+
+bool catalog_has_member(catalog_t *cat, const knot_dname_t *member)
+{
+ const knot_dname_t *catz;
+ const char *group;
+ void *tofree = NULL;
+ int ret = catalog_get_catz(cat, member, &catz, &group, &tofree);
+ free(tofree);
+ return (ret == KNOT_EOK);
+}
+
+bool catalog_contains_exact(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catz)
+{
+ const knot_dname_t *found_owner, *found_catz;
+ const char *found_group;
+ void *tofree = NULL;
+ int ret = find_threadsafe(cat, member, &found_owner, &found_catz, &found_group, &tofree);
+ if (ret == KNOT_EOK && (!knot_dname_is_equal(owner, found_owner) ||
+ !knot_dname_is_equal(catz, found_catz))) {
+ ret = KNOT_ENOENT;
+ }
+ free(tofree);
+ return (ret == KNOT_EOK);
+}
+
+typedef struct {
+ catalog_apply_cb_t cb;
+ void *ctx;
+} catalog_apply_ctx_t;
+
+static int catalog_apply_cb(MDB_val *key, MDB_val *val, void *ctx)
+{
+ catalog_apply_ctx_t *iter_ctx = ctx;
+ uint8_t zero;
+ const knot_dname_t *mem = NULL, *ow = NULL, *cz = NULL;
+ const char *gr = NULL;
+ knot_lmdb_unmake_key(key->mv_data, key->mv_size, "BN", &zero, &mem);
+ unmake_val(val, &ow, &cz, &gr);
+ if (mem == NULL || ow == NULL || cz == NULL) {
+ return KNOT_EMALF;
+ }
+ return iter_ctx->cb(mem, ow, cz, gr, iter_ctx->ctx);
+}
+
+int catalog_apply(catalog_t *cat, const knot_dname_t *for_member,
+ catalog_apply_cb_t cb, void *ctx, bool rw)
+{
+ MDB_val prefix = knot_lmdb_make_key(for_member == NULL ? "B" : "BN", 0, for_member);
+ catalog_apply_ctx_t iter_ctx = { cb, ctx };
+ knot_lmdb_txn_t *use_txn = rw ? cat->rw_txn : cat->ro_txn;
+ int ret = knot_lmdb_apply_threadsafe(use_txn, &prefix, true, catalog_apply_cb, &iter_ctx);
+ free(prefix.mv_data);
+ return ret;
+}
+
+static bool same_catalog(knot_lmdb_txn_t *txn, const knot_dname_t *catalog)
+{
+ if (catalog == NULL) {
+ return true;
+ }
+ const knot_dname_t *txn_cat = NULL, *unused;
+ const char *grunused;
+ unmake_val(&txn->cur_val, &unused, &txn_cat, &grunused);
+ return knot_dname_is_equal(txn_cat, catalog);
+}
+
+int catalog_copy(knot_lmdb_db_t *from, knot_lmdb_db_t *to,
+ const knot_dname_t *cat_only, bool read_rw_txn)
+{
+ if (knot_lmdb_exists(from) == KNOT_ENODB) {
+ return KNOT_EOK;
+ }
+ int ret = knot_lmdb_open(from);
+ if (ret == KNOT_EOK) {
+ ret = make_path(to->path, S_IRWXU | S_IRWXG);
+ if (ret == KNOT_EOK) {
+ ret = knot_lmdb_open(to);
+ }
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn_r = { 0 }, txn_w = { 0 };
+ knot_lmdb_begin(from, &txn_r, read_rw_txn); // using RW txn not to conflict with still-open RO txn
+ knot_lmdb_begin(to, &txn_w, true);
+ knot_lmdb_foreach(&txn_w, (MDB_val *)&catalog_iter_prefix) {
+ if (same_catalog(&txn_w, cat_only)) {
+ knot_lmdb_del_cur(&txn_w);
+ }
+ }
+ knot_lmdb_foreach(&txn_r, (MDB_val *)&catalog_iter_prefix) {
+ if (same_catalog(&txn_r, cat_only)) {
+ knot_lmdb_insert(&txn_w, &txn_r.cur_key, &txn_r.cur_val);
+ }
+ }
+ ensure_cat_version(&txn_w, &txn_w);
+ if (txn_r.ret != KNOT_EOK) {
+ knot_lmdb_abort(&txn_r);
+ knot_lmdb_abort(&txn_w);
+ return txn_r.ret;
+ }
+ knot_lmdb_commit(&txn_r);
+ knot_lmdb_commit(&txn_w);
+ return txn_w.ret;
+}
diff --git a/src/knot/catalog/catalog_db.h b/src/knot/catalog/catalog_db.h
new file mode 100644
index 0000000..d0abd3b
--- /dev/null
+++ b/src/knot/catalog/catalog_db.h
@@ -0,0 +1,187 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/journal/knot_lmdb.h"
+#include "libknot/libknot.h"
+
+#define CATALOG_VERSION "1.0"
+#define CATALOG_ZONE_VERSION "2" // must be just one char long
+#define CATALOG_ZONES_LABEL "\x05""zones"
+#define CATALOG_GROUP_LABEL "\x05""group"
+#define CATALOG_GROUP_MAXLEN 255
+
+typedef struct catalog {
+ knot_lmdb_db_t db;
+ knot_lmdb_txn_t *ro_txn; // persistent RO transaction
+ knot_lmdb_txn_t *rw_txn; // temporary RW transaction
+
+ // private
+ knot_lmdb_txn_t *old_ro_txn;
+} catalog_t;
+
+/*!
+ * \brief Append a prefix dname to a dname in a storage.
+ *
+ * \return New dname length.
+ */
+size_t catalog_dname_append(knot_dname_storage_t storage, const knot_dname_t *name);
+
+/*!
+ * \brief Return the number of bytes that subname has more than name.
+ *
+ * \return -1 if subname is not subname of name
+ */
+int catalog_bailiwick_shift(const knot_dname_t *subname, const knot_dname_t *name);
+
+/*!
+ * \brief Initialize catalog structure.
+ *
+ * \param cat Catalog structure.
+ * \param path Path to LMDB for catalog.
+ * \param mapsize Mapsize of the LMDB.
+ */
+void catalog_init(catalog_t *cat, const char *path, size_t mapsize);
+
+/*!
+ * \brief Open the catalog LMDB, create it if not exists.
+ *
+ * \param cat Catlog to be opened.
+ *
+ * \return KNOT_E*
+ */
+int catalog_open(catalog_t *cat);
+
+/*!
+ * \brief Start a temporary RW transaction in the catalog.
+ *
+ * \param cat Catalog in question.
+ *
+ * \return KNOT_E*
+ */
+int catalog_begin(catalog_t *cat);
+
+/*!
+ * \brief End using the temporary RW txn, refresh the persistent RO txn.
+ *
+ * \param cat Catalog in question.
+ *
+ * \return KNOT_E*
+ */
+int catalog_commit(catalog_t *cat);
+
+/*!
+ * \brief Abort temporary RW txn.
+ */
+void catalog_abort(catalog_t *cat);
+
+/*!
+ * \brief Free up old txns.
+ *
+ * \note This must be called after catalog_commit() with a delay of synchronize_rcu().
+ *
+ * \param cat Catalog.
+ */
+void catalog_commit_cleanup(catalog_t *cat);
+
+/*!
+ * \brief Close the catalog and de-init the structure.
+ *
+ * \param cat Catalog to be closed.
+ */
+void catalog_deinit(catalog_t *cat);
+
+/*!
+ * \brief Add a member zone to the catalog database.
+ *
+ * \param cat Catalog to be augmented.
+ * \param member Member zone name.
+ * \param owner Owner of the PTR record in catalog zone, respective to the member zone.
+ * \param catzone Name of the catalog zone whose it's the member.
+ * \param group Configuration group of the member.
+ *
+ * \return KNOT_E*
+ */
+int catalog_add(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catzone,
+ const char *group);
+
+/*!
+ * \brief Delete a member zone from the catalog database.
+ *
+ * \param cat Catalog to be removed from.
+ * \param member Member zone to be removed.
+ *
+ * \return KNOT_E*
+ */
+int catalog_del(catalog_t *cat, const knot_dname_t *member);
+
+/*!
+ * \brief Find catz name of the catalog owning this member.
+ *
+ * \note This function may be called in multithreaded operation.
+ *
+ * \param cat Catalog database.
+ * \param member Member to search for.
+ * \param catz Out: name of catalog zone it resides in.
+ * \param group Out: configuration group the member resides in.
+ * \param tofree Out: a pointer that has to be freed later.
+ *
+ * \return KNOT_E*
+ */
+int catalog_get_catz(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t **catz, const char **group, void **tofree);
+
+/*!
+ * \brief Check if this member exists in any catalog zone.
+ */
+bool catalog_has_member(catalog_t *cat, const knot_dname_t *member);
+
+/*!
+ * \brief Check if exactly this record (member, owner, catz) is in catalog DB.
+ */
+bool catalog_contains_exact(catalog_t *cat, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catz);
+
+typedef int (*catalog_apply_cb_t)(const knot_dname_t *member, const knot_dname_t *owner,
+ const knot_dname_t *catz, const char *group, void *ctx);
+/*!
+ * \brief Iterate through catalog database, applying callback.
+ *
+ * \param cat Catalog to be iterated.
+ * \param for_member (Optional) Iterate only on records for this member name.
+ * \param cb Callback to be called.
+ * \param ctx Context for this callback.
+ * \param rw Use read-write transaction.
+ *
+ * \return KNOT_E*
+ */
+int catalog_apply(catalog_t *cat, const knot_dname_t *for_member,
+ catalog_apply_cb_t cb, void *ctx, bool rw);
+
+/*!
+ * \brief Copy records from one catalog database to other.
+ *
+ * \param from Catalog DB to copy from.
+ * \param to Catalog DB to copy to.
+ * \param cat_only Optional: copy only records for this catalog zone.
+ * \param read_rw_txn Use RW txn for read operations.
+ *
+ * \return KNOT_E*
+ */
+int catalog_copy(knot_lmdb_db_t *from, knot_lmdb_db_t *to,
+ const knot_dname_t *cat_only, bool read_rw_txn);
diff --git a/src/knot/catalog/catalog_update.c b/src/knot/catalog/catalog_update.c
new file mode 100644
index 0000000..50f38cb
--- /dev/null
+++ b/src/knot/catalog/catalog_update.c
@@ -0,0 +1,407 @@
+/* Copyright (C) 2022 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 <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "knot/catalog/catalog_update.h"
+#include "knot/common/log.h"
+#include "knot/conf/base.h"
+#include "knot/server/server.h"
+
+int catalog_update_init(catalog_update_t *u)
+{
+ u->upd = trie_create(NULL);
+ if (u->upd == NULL) {
+ return KNOT_ENOMEM;
+ }
+ pthread_mutex_init(&u->mutex, 0);
+ u->error = KNOT_EOK;
+ return KNOT_EOK;
+}
+
+catalog_update_t *catalog_update_new(void)
+{
+ catalog_update_t *u = calloc(1, sizeof(*u));
+ if (u != NULL) {
+ int ret = catalog_update_init(u);
+ if (ret != KNOT_EOK) {
+ free(u);
+ u = NULL;
+ }
+ }
+ return u;
+}
+
+static void catalog_upd_val_free(catalog_upd_val_t *val)
+{
+ free(val->add_owner);
+ free(val->rem_owner);
+ free(val->new_group);
+ free(val);
+}
+
+static int freecb(trie_val_t *tval, _unused_ void *unused)
+{
+ catalog_upd_val_t *val = *tval;
+ if (val != NULL) {
+ catalog_upd_val_free(val);
+ }
+ return 0;
+}
+
+void catalog_update_clear(catalog_update_t *u)
+{
+ trie_apply(u->upd, freecb, NULL);
+ trie_clear(u->upd);
+ u->error = KNOT_EOK;
+}
+
+void catalog_update_deinit(catalog_update_t *u)
+{
+ pthread_mutex_destroy(&u->mutex);
+ trie_free(u->upd);
+}
+
+void catalog_update_free(catalog_update_t *u)
+{
+ if (u != NULL) {
+ catalog_update_deinit(u);
+ free(u);
+ }
+}
+
+static catalog_upd_val_t *upd_val_new(const knot_dname_t *member, int bail,
+ const knot_dname_t *owner, catalog_upd_type_t type)
+{
+ assert(bail <= (int)knot_dname_size(owner));
+ size_t member_size = knot_dname_size(member);
+
+ catalog_upd_val_t *val = malloc(sizeof(*val) + member_size);
+ if (val == NULL) {
+ return NULL;
+ }
+ val->member = (knot_dname_t *)(val + 1);
+ memcpy(val->member, member, member_size);
+ knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL);
+ if (owner_cpy == NULL) {
+ free(val);
+ return NULL;
+ }
+ val->type = type;
+ val->new_group = NULL;
+ if (type == CAT_UPD_REM) {
+ val->add_owner = NULL;
+ val->add_catz = NULL;
+ val->rem_owner = owner_cpy;
+ val->rem_catz = owner_cpy + bail;
+ } else {
+ val->add_owner = owner_cpy;
+ val->add_catz = owner_cpy + bail;
+ val->rem_owner = NULL;
+ val->rem_catz = NULL;
+ }
+ return val;
+}
+
+static const knot_dname_t *get_uniq(const knot_dname_t *ptr_owner,
+ const knot_dname_t *catz)
+{
+ int labels = knot_dname_labels(ptr_owner, NULL);
+ labels -= knot_dname_labels(catz, NULL);
+ assert(labels >= 2);
+ return ptr_owner + knot_dname_prefixlen(ptr_owner, labels - 2, NULL);
+}
+
+static bool same_uniq(const knot_dname_t *owner1, const knot_dname_t *catz1,
+ const knot_dname_t *owner2, const knot_dname_t *catz2)
+{
+ const knot_dname_t *uniq1 = get_uniq(owner1, catz1), *uniq2 = get_uniq(owner2, catz2);
+ if (*uniq1 != *uniq2) {
+ return false;
+ }
+ return memcmp(uniq1 + 1, uniq2 + 1, *uniq1) == 0;
+}
+
+static int upd_val_update(catalog_upd_val_t *val, int bail,
+ const knot_dname_t *owner, bool rem)
+{
+ if ((rem && val->type != CAT_UPD_ADD) ||
+ (!rem && val->type != CAT_UPD_REM)) {
+ log_zone_error(val->member, "duplicate addition/removal of the member node, ignoring");
+ return KNOT_EOK;
+ }
+ knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL);
+ if (owner_cpy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ if (rem) {
+ val->rem_owner = owner_cpy;
+ val->rem_catz = owner_cpy + bail;
+ } else {
+ val->add_owner = owner_cpy;
+ val->add_catz = owner_cpy + bail;
+ }
+ if (same_uniq(val->rem_owner, val->rem_catz, val->add_owner, val->add_catz)) {
+ val->type = CAT_UPD_MINOR;
+ } else {
+ val->type = CAT_UPD_UNIQ;
+ }
+ return KNOT_EOK;
+}
+
+static int upd_val_set_prop(catalog_upd_val_t *val, const knot_dname_t *check_ow,
+ const knot_dname_t *check_catz, const char *group,
+ size_t group_len)
+{
+ if (check_catz != NULL) {
+ if (val->type == CAT_UPD_REM ||
+ !knot_dname_is_equal(check_ow, val->add_owner) || // TODO consider removing those checks. Are they worth the performance?
+ !knot_dname_is_equal(check_catz, val->add_catz)) {
+ return KNOT_EOK; // ignore invalid property set
+ }
+ }
+ if (val->new_group != NULL) {
+ free(val->new_group);
+ }
+ val->new_group = strndup(group, group_len);
+ return val->new_group == NULL ? KNOT_ENOMEM : KNOT_EOK;
+}
+
+int catalog_update_add(catalog_update_t *u, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catzone,
+ catalog_upd_type_t type, const char *group,
+ size_t group_len, catalog_t *check_rem)
+{
+ int bail = catalog_bailiwick_shift(owner, catzone);
+ if (bail < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+ assert(bail >= 0 && bail <= KNOT_DNAME_MAXLEN);
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(member, lf_storage);
+
+ trie_val_t *found = trie_get_try(u->upd, lf + 1, lf[0]);
+
+ if ((type == CAT_UPD_REM || type == CAT_UPD_PROP) && check_rem != NULL &&
+ !catalog_contains_exact(check_rem, member, owner, catzone)) {
+ if (found == NULL) {
+ // we need to perform this check immediately because
+ // garbage removal would block legitimate removal
+ return KNOT_EOK;
+ }
+ if (type == CAT_UPD_REM) {
+ catalog_upd_val_t *val = *found;
+ catalog_upd_val_free(val);
+ trie_del(u->upd, lf + 1, lf[0], NULL);
+ return KNOT_EOK;
+ }
+ }
+
+ if (found != NULL) {
+ catalog_upd_val_t *val = *found;
+ assert(knot_dname_is_equal(val->member, member));
+ if (type == CAT_UPD_PROP) {
+ return upd_val_set_prop(val, owner, catzone, group, group_len);
+ } else {
+ return upd_val_update(val, bail, owner, type == CAT_UPD_REM);
+ }
+ }
+
+ catalog_upd_val_t *val = upd_val_new(member, bail, owner, type);
+ if (val == NULL) {
+ return KNOT_ENOMEM;
+ }
+ if (group_len > 0) {
+ int ret = upd_val_set_prop(val, NULL, NULL, group, group_len);
+ if (ret != KNOT_EOK) {
+ catalog_upd_val_free(val);
+ return ret;
+ }
+ }
+ trie_val_t *added = trie_get_ins(u->upd, lf + 1, lf[0]);
+ if (added == NULL) {
+ catalog_upd_val_free(val);
+ return KNOT_ENOMEM;
+ }
+ assert(*added == NULL);
+ *added = val;
+ return KNOT_EOK;
+}
+
+catalog_upd_val_t *catalog_update_get(catalog_update_t *u, const knot_dname_t *member)
+{
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(member, lf_storage);
+
+ trie_val_t *found = trie_get_try(u->upd, lf + 1, lf[0]);
+ return found == NULL ? NULL : *(catalog_upd_val_t **)found;
+}
+
+static bool check_member(catalog_upd_val_t *val, conf_t *conf, catalog_t *cat)
+{
+ if (val->type == CAT_UPD_REM || val->type == CAT_UPD_INVALID || val->type == CAT_UPD_PROP) {
+ return true;
+ }
+ if (!conf_rawid_exists(conf, C_ZONE, val->add_catz, knot_dname_size(val->add_catz))) {
+ knot_dname_txt_storage_t cat_str;
+ (void)knot_dname_to_str(cat_str, val->add_catz, sizeof(cat_str));
+ log_zone_error(val->member, "catalog template zone '%s' not configured, ignoring", cat_str);
+ return false;
+ }
+ if (conf_rawid_exists(conf, C_ZONE, val->member, knot_dname_size(val->member))) {
+ log_zone_error(val->member, "member zone already configured, ignoring");
+ return false;
+ }
+ if (val->type == CAT_UPD_ADD && catalog_has_member(cat, val->member)) {
+ log_zone_error(val->member, "member zone already configured by catalog, ignoring");
+ return false;
+ }
+ return true;
+}
+
+typedef struct {
+ conf_t *conf;
+ catalog_update_t *cup;
+} rem_conflict_ctx_t;
+
+static int rem_conf_conflict(const knot_dname_t *mem, const knot_dname_t *ow,
+ const knot_dname_t *cz, _unused_ const char *gr, void *ctx)
+{
+ rem_conflict_ctx_t *rcctx = ctx;
+
+ if (conf_rawid_exists(rcctx->conf, C_ZONE, mem, knot_dname_size(mem))) {
+ return catalog_update_add(rcctx->cup, mem, ow, cz, CAT_UPD_REM, NULL, 0, NULL);
+ }
+ return KNOT_EOK;
+}
+
+void catalog_update_finalize(catalog_update_t *u, catalog_t *cat, conf_t *conf)
+{
+ catalog_it_t *it = catalog_it_begin(u);
+ while (!catalog_it_finished(it)) {
+ catalog_upd_val_t *val = catalog_it_val(it);
+ if (!check_member(val, conf, cat)) {
+ val->type = (val->type == CAT_UPD_ADD ? CAT_UPD_INVALID : CAT_UPD_REM);
+ }
+ catalog_it_next(it);
+ }
+ catalog_it_free(it);
+
+ // This checks if the configuration file has not changed in the way
+ // it conflicts with existing member zone and let config take precedence.
+ if (cat->ro_txn != NULL) {
+ rem_conflict_ctx_t rcctx = { conf, u };
+ (void)catalog_apply(cat, NULL, rem_conf_conflict, &rcctx, false);
+ }
+}
+
+int catalog_update_commit(catalog_update_t *u, catalog_t *cat)
+{
+ catalog_it_t *it = catalog_it_begin(u);
+ if (catalog_it_finished(it)) {
+ catalog_it_free(it);
+ return KNOT_EOK;
+ }
+ int ret = catalog_begin(cat);
+ while (!catalog_it_finished(it) && ret == KNOT_EOK) {
+ catalog_upd_val_t *val = catalog_it_val(it);
+ switch (val->type) {
+ case CAT_UPD_ADD:
+ case CAT_UPD_MINOR: // catalog_add will simply update/overwrite existing data
+ case CAT_UPD_UNIQ:
+ case CAT_UPD_PROP:
+ ret = catalog_add(cat, val->member, val->add_owner, val->add_catz,
+ val->new_group == NULL ? "" : val->new_group);
+ break;
+ case CAT_UPD_REM:
+ ret = catalog_del(cat, val->member);
+ break;
+ case CAT_UPD_INVALID:
+ break; // no action
+ default:
+ assert(0);
+ ret = KNOT_ERROR;
+ }
+ catalog_it_next(it);
+ }
+ catalog_it_free(it);
+ if (ret == KNOT_EOK) {
+ ret = catalog_commit(cat);
+ } else {
+ catalog_abort(cat);
+ }
+ return ret;
+}
+
+typedef struct {
+ const knot_dname_t *zone;
+ catalog_update_t *u;
+} del_all_ctx_t;
+
+static int del_all_cb(const knot_dname_t *member, const knot_dname_t *owner,
+ const knot_dname_t *catz, _unused_ const char *group, void *dactx)
+{
+ del_all_ctx_t *ctx = dactx;
+ if (knot_dname_is_equal(catz, ctx->zone)) {
+ // TODO possible speedup by indexing which member zones belong to a catalog zone
+ return catalog_update_add(ctx->u, member, owner, catz, CAT_UPD_REM, NULL, 0, NULL);
+ } else {
+ return KNOT_EOK;
+ }
+}
+
+int catalog_update_del_all(catalog_update_t *u, catalog_t *cat, const knot_dname_t *zone, ssize_t *upd_count)
+{
+ pthread_mutex_lock(&u->mutex);
+ del_all_ctx_t ctx = { zone, u };
+ *upd_count -= trie_weight(u->upd);
+ int ret = catalog_apply(cat, NULL, del_all_cb, &ctx, false);
+ *upd_count += trie_weight(u->upd);
+ pthread_mutex_unlock(&u->mutex);
+ return ret;
+}
+
+int catalog_zone_purge(server_t *server, conf_t *conf, const knot_dname_t *zone)
+{
+ assert(server);
+ assert(zone);
+
+ if (server->catalog.ro_txn == NULL) {
+ return KNOT_EOK; // no catalog at all
+ }
+
+ if (conf != NULL) {
+ conf_val_t role = conf_zone_get(conf, C_CATALOG_ROLE, zone);
+ if (conf_opt(&role) != CATALOG_ROLE_INTERPRET) {
+ return KNOT_EOK;
+ }
+ }
+
+ ssize_t members = 0;
+ int ret = catalog_update_del_all(&server->catalog_upd, &server->catalog, zone, &members);
+ if (ret == KNOT_EOK && members > 0) {
+ log_zone_info(zone, "catalog zone purged, %zd member zones deconfigured", members);
+ server->catalog_upd_signal = true;
+ if (kill(getpid(), SIGUSR1) != 0) {
+ ret = knot_map_errno();
+ }
+ }
+ return ret;
+}
diff --git a/src/knot/catalog/catalog_update.h b/src/knot/catalog/catalog_update.h
new file mode 100644
index 0000000..3726372
--- /dev/null
+++ b/src/knot/catalog/catalog_update.h
@@ -0,0 +1,171 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "contrib/qp-trie/trie.h"
+#include "knot/catalog/catalog_db.h"
+#include "knot/conf/conf.h"
+
+struct server; // "knot/server/server.h" causes preprocessor problems when included.
+
+typedef enum {
+ CAT_UPD_INVALID, // invalid value
+ CAT_UPD_ADD, // member addition
+ CAT_UPD_REM, // member removal
+ CAT_UPD_MINOR, // owner or catzone change, uniqID preserved
+ CAT_UPD_UNIQ, // uniqID change
+ CAT_UPD_PROP, // ONLY change of properties of existing member
+ CAT_UPD_MAX, // number of options in the enum
+} catalog_upd_type_t;
+
+typedef struct catalog_upd_val {
+ knot_dname_t *member; // name of catalog member zone
+ catalog_upd_type_t type; // what kind of update this is
+
+ knot_dname_t *rem_owner; // owner of PTR record being removed
+ knot_dname_t *rem_catz; // catalog zone the member being removed from
+ knot_dname_t *add_owner; // owner of PTR record being added
+ knot_dname_t *add_catz; // catalog zone the member being added to
+
+ char *new_group; // the desired configuration group for the member
+} catalog_upd_val_t;
+
+typedef struct {
+ trie_t *upd; // tree of catalog_upd_val_t, that gonna be changed in catalog
+ int error; // error occurred during generating of upd
+ pthread_mutex_t mutex; // lock for accessing this struct
+} catalog_update_t;
+
+/*!
+ * \brief Initialize catalog update structure.
+ *
+ * \param u Catalog update to be initialized.
+ *
+ * \return KNOT_EOK, KNOT_ENOMEM
+ */
+int catalog_update_init(catalog_update_t *u);
+catalog_update_t *catalog_update_new(void);
+
+/*!
+ * \brief Clear contents of catalog update structure.
+ *
+ * \param u Catalog update structure to be cleared.
+ */
+void catalog_update_clear(catalog_update_t *u);
+
+/*!
+ * \brief Free catalog update structure.
+ *
+ * \param u Catalog update structure.
+ */
+void catalog_update_deinit(catalog_update_t *u);
+void catalog_update_free(catalog_update_t *u);
+
+/*!
+ * \brief Add a new record to catalog update structure.
+ *
+ * \param u Catalog update.
+ * \param member Member zone name to be added.
+ * \param owner Owner of respective PTR record.
+ * \param catzone Catalog zone holding the member.
+ * \param type CAT_UPD_REM, CAT_UPD_ADD, CAT_UPD_PROP.
+ * \param group Optional: member group property value.
+ * \param group_len Length of 'group' string (if not NULL).
+ * \param check_rem Check catalog DB for existing record to be removed.
+ *
+ * \return KNOT_E*
+ */
+int catalog_update_add(catalog_update_t *u, const knot_dname_t *member,
+ const knot_dname_t *owner, const knot_dname_t *catzone,
+ catalog_upd_type_t type, const char *group,
+ size_t group_len, catalog_t *check_rem);
+
+/*!
+ * \brief Read catalog update record for given member zone.
+ *
+ * \param u Catalog update.
+ * \param member Member zone name.
+ * \param remove Search in remove section.
+ *
+ * \return Found update record for given member zone; or NULL.
+ */
+catalog_upd_val_t *catalog_update_get(catalog_update_t *u, const knot_dname_t *member);
+
+/*!
+ * \brief Catalog update iteration.
+ */
+typedef trie_it_t catalog_it_t;
+
+inline static catalog_it_t *catalog_it_begin(catalog_update_t *u)
+{
+ return trie_it_begin(u->upd);
+}
+
+inline static catalog_upd_val_t *catalog_it_val(catalog_it_t *it)
+{
+ return *(catalog_upd_val_t **)trie_it_val(it);
+}
+
+inline static bool catalog_it_finished(catalog_it_t *it)
+{
+ return it == NULL || trie_it_finished(it);
+}
+
+#define catalog_it_next trie_it_next
+#define catalog_it_free trie_it_free
+
+/*!
+ * \brief Check Catalog update for conflicts with conf or other catalogs.
+ *
+ * \param u Catalog update to be aligned in-place.
+ * \param cat Catalog DB to check against.
+ * \param conf Relevant configuration.
+ */
+void catalog_update_finalize(catalog_update_t *u, catalog_t *cat, conf_t *conf);
+
+/*!
+ * \brief Put changes from Catalog Update into persistent Catalog database.
+ *
+ * \param u Catalog update to be committed.
+ * \param cat Catalog to be updated.
+ *
+ * \return KNOT_E*
+ */
+int catalog_update_commit(catalog_update_t *u, catalog_t *cat);
+
+/*!
+ * \brief Add to catalog update removals of all member zones of a single catalog zone.
+ *
+ * \param u Catalog update to be updated.
+ * \param cat Catalog database to be iterated.
+ * \param zone Name of catalog zone whose members gonna be removed.
+ * \param upd_count Output: number of resulting updates to catalog database.
+ *
+ * \return KNOT_E*
+ */
+int catalog_update_del_all(catalog_update_t *u, catalog_t *cat, const knot_dname_t *zone, ssize_t *upd_count);
+
+/*!
+ * \brief Destroy all members of specified catalog zone.
+ *
+ * \param server Server with catalog DB.
+ * \param conf Optional: check conf to skip if zone not catalog.
+ * \param zone Catalog zone name.
+ *
+ * \return KNOT_E*
+ */
+int catalog_zone_purge(struct server *server, conf_t *conf, const knot_dname_t *zone);
diff --git a/src/knot/catalog/generate.c b/src/knot/catalog/generate.c
new file mode 100644
index 0000000..e05442b
--- /dev/null
+++ b/src/knot/catalog/generate.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 2022 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 <string.h>
+
+#include "knot/catalog/generate.h"
+#include "knot/common/log.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/zonedb.h"
+#include "contrib/openbsd/siphash.h"
+#include "contrib/wire_ctx.h"
+
+static knot_dname_t *catalog_member_owner(const knot_dname_t *member,
+ const knot_dname_t *catzone,
+ time_t member_time)
+{
+ SIPHASH_CTX hash;
+ SIPHASH_KEY shkey = { 0 }; // only used for hashing -> zero key
+ SipHash24_Init(&hash, &shkey);
+ SipHash24_Update(&hash, member, knot_dname_size(member));
+ uint64_t u64time = htobe64(member_time);
+ SipHash24_Update(&hash, &u64time, sizeof(u64time));
+ uint64_t hashres = SipHash24_End(&hash);
+
+ char *hexhash = bin_to_hex((uint8_t *)&hashres, sizeof(hashres), false);
+ if (hexhash == NULL) {
+ return NULL;
+ }
+ size_t hexlen = strlen(hexhash);
+ assert(hexlen == 16);
+ size_t zoneslen = knot_dname_size((uint8_t *)CATALOG_ZONES_LABEL);
+ assert(hexlen <= KNOT_DNAME_MAXLABELLEN && zoneslen <= KNOT_DNAME_MAXLABELLEN);
+ size_t catzlen = knot_dname_size(catzone);
+
+ size_t outlen = hexlen + zoneslen + catzlen;
+ knot_dname_t *out;
+ if (outlen > KNOT_DNAME_MAXLEN || (out = malloc(outlen)) == NULL) {
+ free(hexhash);
+ return NULL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(out, outlen);
+ wire_ctx_write_u8(&wire, hexlen);
+ wire_ctx_write(&wire, hexhash, hexlen);
+ wire_ctx_write(&wire, CATALOG_ZONES_LABEL, zoneslen);
+ wire_ctx_skip(&wire, -1);
+ wire_ctx_write(&wire, catzone, catzlen);
+ assert(wire.error == KNOT_EOK);
+
+ free(hexhash);
+ return out;
+}
+
+static bool same_group(zone_t *old_z, zone_t *new_z)
+{
+ if (old_z->catalog_group == NULL || new_z->catalog_group == NULL) {
+ return (old_z->catalog_group == new_z->catalog_group);
+ } else {
+ return (strcmp(old_z->catalog_group, new_z->catalog_group) == 0);
+ }
+}
+
+void catalogs_generate(struct knot_zonedb *db_new, struct knot_zonedb *db_old)
+{
+ // general comment: catz->contents!=NULL means incremental update of catalog
+
+ if (db_old != NULL) {
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old);
+ while (!knot_zonedb_iter_finished(it)) {
+ zone_t *zone = knot_zonedb_iter_val(it);
+ knot_dname_t *cg = zone->catalog_gen;
+ if (cg != NULL && knot_zonedb_find(db_new, zone->name) == NULL) {
+ zone_t *catz = knot_zonedb_find(db_new, cg);
+ if (catz != NULL && catz->contents != NULL) {
+ assert(catz->cat_members != NULL); // if this failed to allocate, catz wasn't added to zonedb
+ knot_dname_t *owner = catalog_member_owner(zone->name, cg, zone->timers.catalog_member);
+ if (owner == NULL) {
+ catz->cat_members->error = KNOT_ENOENT;
+ knot_zonedb_iter_next(it);
+ continue;
+ }
+ int ret = catalog_update_add(catz->cat_members, zone->name, owner,
+ cg, CAT_UPD_REM, NULL, 0, NULL);
+ free(owner);
+ if (ret != KNOT_EOK) {
+ catz->cat_members->error = ret;
+ } else {
+ zone_events_schedule_now(catz, ZONE_EVENT_LOAD);
+ }
+ }
+ }
+ knot_zonedb_iter_next(it);
+ }
+ knot_zonedb_iter_free(it);
+ }
+
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_new);
+ while (!knot_zonedb_iter_finished(it)) {
+ zone_t *zone = knot_zonedb_iter_val(it);
+ knot_dname_t *cg = zone->catalog_gen;
+ if (cg == NULL) {
+ knot_zonedb_iter_next(it);
+ continue;
+ }
+ zone_t *catz = knot_zonedb_find(db_new, cg);
+ zone_t *old = knot_zonedb_find(db_old, zone->name);
+ knot_dname_t *owner = catalog_member_owner(zone->name, cg, zone->timers.catalog_member);
+ size_t cgroup_size = zone->catalog_group == NULL ? 0 : strlen(zone->catalog_group);
+ if (catz == NULL) {
+ log_zone_warning(zone->name, "member zone belongs to non-existing catalog zone");
+ } else if (catz->contents == NULL || old == NULL) {
+ assert(catz->cat_members != NULL);
+ if (owner == NULL) {
+ catz->cat_members->error = KNOT_ENOENT;
+ knot_zonedb_iter_next(it);
+ continue;
+ }
+ int ret = catalog_update_add(catz->cat_members, zone->name, owner,
+ cg, CAT_UPD_ADD, zone->catalog_group,
+ cgroup_size, NULL);
+ if (ret != KNOT_EOK) {
+ catz->cat_members->error = ret;
+ } else {
+ zone_events_schedule_now(catz, ZONE_EVENT_LOAD);
+ }
+ } else if (!same_group(zone, old)) {
+ int ret = catalog_update_add(catz->cat_members, zone->name, owner,
+ cg, CAT_UPD_PROP, zone->catalog_group,
+ cgroup_size, NULL);
+ if (ret != KNOT_EOK) {
+ catz->cat_members->error = ret;
+ } else {
+ zone_events_schedule_now(catz, ZONE_EVENT_LOAD);
+ }
+ }
+ free(owner);
+ knot_zonedb_iter_next(it);
+ }
+ knot_zonedb_iter_free(it);
+}
+
+static void set_rdata(knot_rrset_t *rrset, uint8_t *data, uint16_t len)
+{
+ knot_rdata_init(rrset->rrs.rdata, len, data);
+ rrset->rrs.size = knot_rdata_size(len);
+}
+
+#define def_txt_owner(ptr_owner) \
+ knot_dname_storage_t txt_owner = "\x05""group"; \
+ size_t _ptr_ow_len = knot_dname_size(ptr_owner); \
+ size_t _ptr_ow_ind = strlen((const char *)txt_owner); \
+ if (_ptr_ow_ind + _ptr_ow_len > sizeof(txt_owner)) { \
+ return KNOT_ERANGE; \
+ } \
+ memcpy(txt_owner + _ptr_ow_ind, (ptr_owner), _ptr_ow_len);
+
+static int add_group_txt(const knot_dname_t *ptr_owner, const char *group,
+ zone_contents_t *conts, zone_update_t *up)
+{
+ assert((conts == NULL) != (up == NULL));
+ size_t group_len;
+ if (group == NULL || (group_len = strlen(group)) < 1) {
+ return KNOT_EOK;
+ }
+ assert(group_len <= 255);
+
+ def_txt_owner(ptr_owner);
+
+ uint8_t data[256] = { group_len };
+ memcpy(data + 1, group, group_len);
+
+ knot_rrset_t txt;
+ knot_rrset_init(&txt, txt_owner, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 0);
+ uint8_t txt_rd[256] = { 0 };
+ txt.rrs.rdata = (knot_rdata_t *)txt_rd;
+ txt.rrs.count = 1;
+ set_rdata(&txt, data, 1 + group_len );
+
+ int ret;
+ if (conts != NULL) {
+ zone_node_t *unused = NULL;
+ ret = zone_contents_add_rr(conts, &txt, &unused);
+ } else {
+ ret = zone_update_add(up, &txt);
+ }
+
+ return ret;
+}
+
+static int rem_group_txt(const knot_dname_t *ptr_owner, zone_update_t *up)
+{
+ def_txt_owner(ptr_owner);
+
+ int ret = zone_update_remove_rrset(up, txt_owner, KNOT_RRTYPE_TXT);
+ if (ret == KNOT_ENOENT || ret == KNOT_ENONODE) {
+ ret = KNOT_EOK;
+ }
+
+ return ret;
+}
+
+struct zone_contents *catalog_update_to_zone(catalog_update_t *u, const knot_dname_t *catzone,
+ uint32_t soa_serial)
+{
+ if (u->error != KNOT_EOK) {
+ return NULL;
+ }
+ zone_contents_t *c = zone_contents_new(catzone, true);
+ if (c == NULL) {
+ return c;
+ }
+
+ zone_node_t *unused = NULL;
+ uint8_t invalid[9] = "\x07""invalid";
+ uint8_t version[9] = "\x07""version";
+ uint8_t cat_version[2] = "\x01" CATALOG_ZONE_VERSION;
+
+ // prepare common rrset with one rdata item
+ uint8_t rdata[256] = { 0 };
+ knot_rrset_t rrset;
+ knot_rrset_init(&rrset, (knot_dname_t *)catzone, KNOT_RRTYPE_SOA, KNOT_CLASS_IN, 0);
+ rrset.rrs.rdata = (knot_rdata_t *)rdata;
+ rrset.rrs.count = 1;
+
+ // set catalog zone's SOA
+ uint8_t data[250];
+ assert(sizeof(knot_rdata_t) + sizeof(data) <= sizeof(rdata));
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+ wire_ctx_write(&wire, invalid, sizeof(invalid));
+ wire_ctx_write(&wire, invalid, sizeof(invalid));
+ wire_ctx_write_u32(&wire, soa_serial);
+ wire_ctx_write_u32(&wire, CATALOG_SOA_REFRESH);
+ wire_ctx_write_u32(&wire, CATALOG_SOA_RETRY);
+ wire_ctx_write_u32(&wire, CATALOG_SOA_EXPIRE);
+ wire_ctx_write_u32(&wire, 0);
+ set_rdata(&rrset, data, wire_ctx_offset(&wire));
+ if (zone_contents_add_rr(c, &rrset, &unused) != KNOT_EOK) {
+ goto fail;
+ }
+
+ // set catalog zone's NS
+ unused = NULL;
+ rrset.type = KNOT_RRTYPE_NS;
+ set_rdata(&rrset, invalid, sizeof(invalid));
+ if (zone_contents_add_rr(c, &rrset, &unused) != KNOT_EOK) {
+ goto fail;
+ }
+
+ // set catalog zone's version TXT
+ unused = NULL;
+ knot_dname_storage_t owner;
+ if (knot_dname_store(owner, version) == 0 || catalog_dname_append(owner, catzone) == 0) {
+ goto fail;
+ }
+ rrset.owner = owner;
+ rrset.type = KNOT_RRTYPE_TXT;
+ set_rdata(&rrset, cat_version, sizeof(cat_version));
+ if (zone_contents_add_rr(c, &rrset, &unused) != KNOT_EOK) {
+ goto fail;
+ }
+
+ // insert member zone PTR records
+ rrset.type = KNOT_RRTYPE_PTR;
+ catalog_it_t *it = catalog_it_begin(u);
+ while (!catalog_it_finished(it)) {
+ catalog_upd_val_t *val = catalog_it_val(it);
+ if (val->add_owner == NULL) {
+ continue;
+ }
+ rrset.owner = val->add_owner;
+ set_rdata(&rrset, val->member, knot_dname_size(val->member));
+ unused = NULL;
+ if (zone_contents_add_rr(c, &rrset, &unused) != KNOT_EOK ||
+ add_group_txt(val->add_owner, val->new_group, c, NULL) != KNOT_EOK) {
+ catalog_it_free(it);
+ goto fail;
+ }
+ catalog_it_next(it);
+ }
+ catalog_it_free(it);
+
+ return c;
+fail:
+ zone_contents_deep_free(c);
+ return NULL;
+}
+
+int catalog_update_to_update(catalog_update_t *u, struct zone_update *zu)
+{
+ knot_rrset_t ptr;
+ knot_rrset_init(&ptr, NULL, KNOT_RRTYPE_PTR, KNOT_CLASS_IN, 0);
+ uint8_t tmp[KNOT_DNAME_MAXLEN + sizeof(knot_rdata_t)];
+ ptr.rrs.rdata = (knot_rdata_t *)tmp;
+ ptr.rrs.count = 1;
+
+ int ret = u->error;
+ catalog_it_t *it = catalog_it_begin(u);
+ while (!catalog_it_finished(it) && ret == KNOT_EOK) {
+ catalog_upd_val_t *val = catalog_it_val(it);
+ if (val->type == CAT_UPD_INVALID) {
+ catalog_it_next(it);
+ continue;
+ }
+
+ if (val->type == CAT_UPD_PROP && knot_dname_is_equal(zu->zone->name, val->add_catz)) {
+ ret = rem_group_txt(val->add_owner, zu);
+ if (ret == KNOT_EOK) {
+ ret = add_group_txt(val->add_owner, val->new_group, NULL, zu);
+ }
+ catalog_it_next(it);
+ continue;
+ }
+
+ set_rdata(&ptr, val->member, knot_dname_size(val->member));
+ if (val->type == CAT_UPD_REM && knot_dname_is_equal(zu->zone->name, val->rem_catz)) {
+ ptr.owner = val->rem_owner;
+ ret = zone_update_remove(zu, &ptr);
+ if (ret == KNOT_EOK) {
+ ret = rem_group_txt(val->rem_owner, zu);
+ }
+ }
+ if (val->type == CAT_UPD_ADD && knot_dname_is_equal(zu->zone->name, val->add_catz)) {
+ ptr.owner = val->add_owner;
+ ret = zone_update_add(zu, &ptr);
+ if (ret == KNOT_EOK) {
+ ret = add_group_txt(val->add_owner, val->new_group, NULL, zu);
+ }
+ }
+ catalog_it_next(it);
+ }
+ catalog_it_free(it);
+ return ret;
+}
diff --git a/src/knot/catalog/generate.h b/src/knot/catalog/generate.h
new file mode 100644
index 0000000..721c1ef
--- /dev/null
+++ b/src/knot/catalog/generate.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "knot/catalog/catalog_update.h"
+
+#define CATALOG_SOA_REFRESH 3600
+#define CATALOG_SOA_RETRY 600
+#define CATALOG_SOA_EXPIRE (INT32_MAX - 1)
+
+struct knot_zonedb;
+
+/*!
+ * \brief Compare old and new zonedb, create incremental catalog upd in each catz->cat_members
+ */
+void catalogs_generate(struct knot_zonedb *db_new, struct knot_zonedb *db_old);
+
+struct zone_contents;
+
+/*!
+ * \brief Generate catalog zone contents from (full) catalog update.
+ *
+ * \param u Catalog update to read.
+ * \param catzone Catalog zone name.
+ * \param soa_serial SOA serial of the generated zone.
+ *
+ * \return Catalog zone contents, or NULL if ENOMEM.
+ */
+struct zone_contents *catalog_update_to_zone(catalog_update_t *u, const knot_dname_t *catzone,
+ uint32_t soa_serial);
+
+struct zone_update;
+
+/*!
+ * \brief Incrementally update catalog zone from catalog update.
+ *
+ * \param u Catalog update to read.
+ * \param zu Zone update to be updated.
+ *
+ * \return KNOT_E*
+ */
+int catalog_update_to_update(catalog_update_t *u, struct zone_update *zu);
diff --git a/src/knot/catalog/interpret.c b/src/knot/catalog/interpret.c
new file mode 100644
index 0000000..e7a5cf0
--- /dev/null
+++ b/src/knot/catalog/interpret.c
@@ -0,0 +1,257 @@
+/* Copyright (C) 2022 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 <pthread.h>
+#include <stdio.h>
+
+#include "knot/catalog/interpret.h"
+#include "knot/journal/serialization.h"
+
+struct cat_upd_ctx;
+typedef int (*cat_interpret_cb_t)(zone_node_t *node, struct cat_upd_ctx *ctx);
+
+typedef struct cat_upd_ctx {
+ catalog_update_t *u;
+ const zone_contents_t *complete_conts;
+ int apex_labels;
+ bool remove;
+ bool zone_diff;
+ catalog_t *check;
+ cat_interpret_cb_t member_cb;
+ cat_interpret_cb_t property_cb;
+} cat_upd_ctx_t;
+
+static bool label_eq(const knot_dname_t *a, const char *_b)
+{
+ const knot_dname_t *b = (const knot_dname_t *)_b;
+ return a[0] == b[0] && memcmp(a + 1, b + 1, a[0]) == 0;
+}
+
+static bool check_zone_version(const zone_contents_t *zone)
+{
+ size_t zone_size = knot_dname_size(zone->apex->owner);
+ knot_dname_t sub[zone_size + 8];
+ memcpy(sub, "\x07""version", 8);
+ memcpy(sub + 8, zone->apex->owner, zone_size);
+
+ const zone_node_t *ver_node = zone_contents_find_node(zone, sub);
+ knot_rdataset_t *ver_rr = node_rdataset(ver_node, KNOT_RRTYPE_TXT);
+ if (ver_rr == NULL) {
+ return false;
+ }
+
+ knot_rdata_t *rdata = ver_rr->rdata;
+ for (int i = 0; i < ver_rr->count; i++) {
+ if (rdata->len == 2 && rdata->data[1] == CATALOG_ZONE_VERSION[0]) {
+ return true;
+ }
+ rdata = knot_rdataset_next(rdata);
+ }
+ return false;
+}
+
+static int interpret_node(zone_node_t *node, void * _ctx)
+{
+ cat_upd_ctx_t *ctx = _ctx;
+
+ int labels_diff = knot_dname_labels(node->owner, NULL) - ctx->apex_labels
+ - 1 /* "zones" label */ - 1 /* unique-N label */;
+ assert(labels_diff >= 0);
+
+ switch (labels_diff) {
+ case 0:
+ return ctx->member_cb(node, ctx);
+ case 1:
+ return ctx->property_cb(node, ctx);
+ default:
+ return KNOT_EOK;
+ }
+}
+
+static int interpret_zone(zone_diff_t *zdiff, cat_upd_ctx_t *ctx)
+{
+ knot_dname_storage_t sub;
+ if (knot_dname_store(sub, (uint8_t *)CATALOG_ZONES_LABEL) == 0 ||
+ catalog_dname_append(sub, zdiff->apex->owner) == 0) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone_tree_get(&zdiff->nodes, sub) == NULL) {
+ return KNOT_EOK;
+ }
+
+ return zone_tree_sub_apply(&zdiff->nodes, sub, true, interpret_node, ctx);
+}
+
+static const knot_dname_t *property_get_member(const zone_node_t *prop_node,
+ const zone_contents_t *complete_conts,
+ const knot_dname_t **owner)
+{
+ assert(prop_node != NULL);
+ knot_rdataset_t *ptr = node_rdataset(prop_node->parent, KNOT_RRTYPE_PTR);
+ if (ptr == NULL) {
+ // fallback: search in provided complete zone contents
+ const knot_dname_t *memb_name = knot_wire_next_label(prop_node->owner, NULL);
+ const zone_node_t *memb_node = zone_contents_find_node(complete_conts, memb_name);
+ ptr = node_rdataset(memb_node, KNOT_RRTYPE_PTR);
+ if (memb_node != NULL) {
+ *owner = memb_node->owner;
+ }
+ } else {
+ *owner = prop_node->parent->owner;
+ }
+ if (*owner == NULL || ptr == NULL || ptr->count != 1) {
+ return NULL;
+ }
+ return knot_ptr_name(ptr->rdata);
+}
+
+static int cat_update_add_memb(zone_node_t *node, cat_upd_ctx_t *ctx)
+{
+ const knot_rdataset_t *ptr = node_rdataset(node, KNOT_RRTYPE_PTR);
+ if (ptr == NULL) {
+ return KNOT_EOK;
+ } else if (ptr->count != 1) {
+ return KNOT_ERROR;
+ }
+
+ const knot_rdataset_t *counter_ptr = node_rdataset(binode_counterpart(node), KNOT_RRTYPE_PTR);
+ if (knot_rdataset_subset(ptr, counter_ptr)) {
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *rdata = ptr->rdata;
+ int ret = KNOT_EOK;
+ for (int i = 0; ret == KNOT_EOK && i < ptr->count; i++) {
+ const knot_dname_t *member = knot_ptr_name(rdata);
+ ret = catalog_update_add(ctx->u, member, node->owner, ctx->complete_conts->apex->owner,
+ ctx->remove ? CAT_UPD_REM : CAT_UPD_ADD,
+ NULL, 0, ctx->check);
+ rdata = knot_rdataset_next(rdata);
+ }
+ return ret;
+}
+
+static int cat_update_add_grp(zone_node_t *node, cat_upd_ctx_t *ctx)
+{
+ if (!label_eq(node->owner, CATALOG_GROUP_LABEL)) {
+ return KNOT_EOK;
+ }
+
+ const knot_dname_t *owner = NULL;
+ const knot_dname_t *member = property_get_member(node, ctx->complete_conts, &owner);
+ if (member == NULL) {
+ return KNOT_EOK; // just ignore property w/o member
+ }
+
+ const knot_rdataset_t *txt = node_rdataset(node, KNOT_RRTYPE_TXT);
+ if (txt == NULL) {
+ return KNOT_EOK;
+ } else if (txt->count != 1) {
+ return KNOT_ERROR;
+ }
+
+ const knot_rdataset_t *counter_txt = node_rdataset(binode_counterpart(node), KNOT_RRTYPE_TXT);
+ if (knot_rdataset_subset(txt, counter_txt)) {
+ return KNOT_EOK;
+ }
+
+ const char *newgr = "";
+ size_t grlen = 0;
+ if (!ctx->remove) {
+ assert(txt->count == 1);
+ // TXT rdata consists of one or more 1-byte prefixed strings.
+ if (txt->rdata->len != txt->rdata->data[0] + 1) {
+ return KNOT_EMALF;
+ }
+ newgr = (const char *)txt->rdata->data + 1;
+ grlen = txt->rdata->data[0];
+ assert(grlen <= CATALOG_GROUP_MAXLEN);
+ }
+
+ return catalog_update_add(ctx->u, member, owner, ctx->complete_conts->apex->owner,
+ CAT_UPD_PROP, newgr, grlen, ctx->check);
+}
+
+int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone,
+ const zone_diff_t *zone_diff,
+ const struct zone_contents *complete_contents,
+ bool remove, catalog_t *check, ssize_t *upd_count)
+{
+ int ret = KNOT_EOK;
+ zone_diff_t zdiff;
+ assert(zone == NULL || zone_diff == NULL);
+ if (zone != NULL) {
+ zone_diff_from_zone(&zdiff, zone);
+ } else {
+ zdiff = *zone_diff;
+ }
+ cat_upd_ctx_t ctx = { u, complete_contents, knot_dname_labels(zdiff.apex->owner, NULL),
+ remove, zone_diff != NULL, check, cat_update_add_memb, cat_update_add_grp };
+
+ pthread_mutex_lock(&u->mutex);
+ *upd_count -= trie_weight(u->upd);
+ if (zone_diff != NULL) {
+ zone_diff_reverse(&zdiff);
+ ctx.remove = true;
+ ret = interpret_zone(&zdiff, &ctx);
+ zone_diff_reverse(&zdiff);
+ ctx.remove = false;
+ ctx.check = NULL;
+ }
+ if (ret == KNOT_EOK) {
+ ret = interpret_zone(&zdiff, &ctx);
+ }
+ *upd_count += trie_weight(u->upd);
+ pthread_mutex_unlock(&u->mutex);
+ return ret;
+}
+
+static int rr_count(const zone_node_t *node, uint16_t type)
+{
+ const knot_rdataset_t *rd = node_rdataset(node, type);
+ return rd == NULL ? 0 : rd->count;
+}
+
+static int member_verify(zone_node_t *node, cat_upd_ctx_t *ctx)
+{
+ return rr_count(node, KNOT_RRTYPE_PTR) > 1 ? KNOT_EISRECORD : KNOT_EOK;
+}
+
+static int prop_verify(zone_node_t *node, cat_upd_ctx_t *ctx)
+{
+ if (label_eq(node->owner, CATALOG_GROUP_LABEL) &&
+ rr_count(node, KNOT_RRTYPE_TXT) > 1) {
+ return KNOT_EISRECORD;
+ }
+
+ return KNOT_EOK;
+}
+
+int catalog_zone_verify(const struct zone_contents *zone)
+{
+ cat_upd_ctx_t ctx = { NULL, zone, knot_dname_labels(zone->apex->owner, NULL),
+ false, false, NULL, member_verify, prop_verify };
+
+ if (!check_zone_version(zone)) {
+ return KNOT_EZONEINVAL;
+ }
+
+ zone_diff_t zdiff;
+ zone_diff_from_zone(&zdiff, zone);
+
+ return interpret_zone(&zdiff, &ctx);
+}
diff --git a/src/knot/catalog/interpret.h b/src/knot/catalog/interpret.h
new file mode 100644
index 0000000..20928b7
--- /dev/null
+++ b/src/knot/catalog/interpret.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/catalog/catalog_update.h"
+
+struct zone_contents;
+struct zone_diff;
+
+/*!
+ * \brief Validate if given zone is valid catalog.
+ *
+ * \param zone Catalog zone in question.
+ *
+ * \retval KNOT_EZONEINVAL Invalid version record.
+ * \retval KNOT_EISRECORD Some of single-record RRSets has multiple RRs.
+ * \return KNOT_EOK All OK.
+ */
+int catalog_zone_verify(const struct zone_contents *zone);
+
+/*!
+ * \brief Iterate over PTR records in given zone contents and add members to catalog update.
+ *
+ * \param u Catalog update to be updated.
+ * \param zone Zone contents to be searched for member PTR records.
+ * \param zone_diff Zone diff to interpret for removals and additions.
+ * \param complete_contents Complete zone contents (zone might be from a changeset).
+ * \param remove Add removals of found member zones.
+ * \param check Optional: existing catalog database to be checked for existence
+ * of such record (useful for removals).
+ * \param upd_count Output: number of resulting updates to catalog database.
+ *
+ * \return KNOT_E*
+ */
+int catalog_update_from_zone(catalog_update_t *u, struct zone_contents *zone,
+ const struct zone_diff *zone_diff,
+ const struct zone_contents *complete_contents,
+ bool remove, catalog_t *check, ssize_t *upd_count);
diff --git a/src/knot/common/evsched.c b/src/knot/common/evsched.c
new file mode 100644
index 0000000..0d65c6a
--- /dev/null
+++ b/src/knot/common/evsched.c
@@ -0,0 +1,268 @@
+/* Copyright (C) 2022 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 <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "libknot/libknot.h"
+#include "knot/server/dthreads.h"
+#include "knot/common/evsched.h"
+
+/*! \brief Some implementations of timercmp >= are broken, this is for compat.*/
+static inline int timercmp_ge(struct timeval *a, struct timeval *b) {
+ return !timercmp(a, b, <);
+}
+
+static int compare_event_heap_nodes(void *e1, void *e2)
+{
+ if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, <)) return -1;
+ if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, >)) return 1;
+ return 0;
+}
+
+/*!
+ * \brief Get time T (now) + dt milliseconds.
+ */
+static struct timeval timeval_in(uint32_t dt)
+{
+ struct timeval tv = { 0 };
+ gettimeofday(&tv, NULL);
+
+ /* Add number of seconds. */
+ tv.tv_sec += dt / 1000;
+
+ /* Add the number of microseconds. */
+ tv.tv_usec += (dt % 1000) * 1000;
+
+ /* Check for overflow. */
+ while (tv.tv_usec > 999999) {
+ tv.tv_sec += 1;
+ tv.tv_usec -= 1 * 1000 * 1000;
+ }
+
+ return tv;
+}
+
+/*! \brief Event scheduler loop. */
+static int evsched_run(dthread_t *thread)
+{
+ evsched_t *sched = (evsched_t*)thread->data;
+ if (sched == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Run event loop. */
+ pthread_mutex_lock(&sched->heap_lock);
+ while (!dt_is_cancelled(thread)) {
+ if (!!EMPTY_HEAP(&sched->heap) || sched->paused) {
+ pthread_cond_wait(&sched->notify, &sched->heap_lock);
+ continue;
+ }
+
+ /* Get current time. */
+ struct timeval dt;
+ gettimeofday(&dt, 0);
+
+ /* Get next event. */
+ event_t *ev = *((event_t**)HHEAD(&sched->heap));
+ assert(ev != NULL);
+
+ if (timercmp_ge(&dt, &ev->tv)) {
+ heap_delmin(&sched->heap);
+ ev->cb(ev);
+ } else {
+ /* Wait for next event or interrupt. Unlock calendar. */
+ struct timespec ts;
+ ts.tv_sec = ev->tv.tv_sec;
+ ts.tv_nsec = ev->tv.tv_usec * 1000L;
+ pthread_cond_timedwait(&sched->notify, &sched->heap_lock, &ts);
+ }
+ }
+ pthread_mutex_unlock(&sched->heap_lock);
+
+ return KNOT_EOK;
+}
+
+int evsched_init(evsched_t *sched, void *ctx)
+{
+ memset(sched, 0, sizeof(evsched_t));
+ sched->ctx = ctx;
+
+ /* Initialize event calendar. */
+ pthread_mutex_init(&sched->heap_lock, 0);
+ pthread_cond_init(&sched->notify, 0);
+ heap_init(&sched->heap, compare_event_heap_nodes, 0);
+
+ sched->thread = dt_create(1, evsched_run, NULL, sched);
+
+ if (sched->thread == NULL) {
+ evsched_deinit(sched);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+void evsched_deinit(evsched_t *sched)
+{
+ if (sched == NULL) {
+ return;
+ }
+
+ /* Deinitialize event calendar. */
+ pthread_mutex_destroy(&sched->heap_lock);
+ pthread_cond_destroy(&sched->notify);
+
+ while (!EMPTY_HEAP(&sched->heap)) {
+ event_t *e = (event_t *)*HHEAD(&sched->heap);
+ heap_delmin(&sched->heap);
+ evsched_event_free(e);
+ }
+
+ heap_deinit(&sched->heap);
+
+ if (sched->thread != NULL) {
+ dt_delete(&sched->thread);
+ }
+
+ /* Clear the structure. */
+ memset(sched, 0, sizeof(evsched_t));
+}
+
+event_t *evsched_event_create(evsched_t *sched, event_cb_t cb, void *data)
+{
+ /* Create event. */
+ if (sched == NULL) {
+ return NULL;
+ }
+
+ /* Allocate. */
+ event_t *e = malloc(sizeof(event_t));
+ if (e == NULL) {
+ return NULL;
+ }
+
+ /* Initialize. */
+ memset(e, 0, sizeof(event_t));
+ e->sched = sched;
+ e->cb = cb;
+ e->data = data;
+ e->hpos.pos = 0;
+
+ return e;
+}
+
+void evsched_event_free(event_t *ev)
+{
+ if (ev == NULL) {
+ return;
+ }
+
+ free(ev);
+}
+
+int evsched_schedule(event_t *ev, uint32_t dt)
+{
+ if (ev == NULL || ev->sched == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct timeval new_time = timeval_in(dt);
+
+ evsched_t *sched = ev->sched;
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&sched->heap_lock);
+
+ ev->tv = new_time;
+
+ /* Make sure it's not already enqueued. */
+ int found = heap_find(&sched->heap, (heap_val_t *)ev);
+ if (found > 0) {
+ /* "Replacing" with itself -- just repositioning it. */
+ heap_replace(&sched->heap, found, (heap_val_t *)ev);
+ } else {
+ heap_insert(&sched->heap, (heap_val_t *)ev);
+ }
+
+ /* Unlock calendar. */
+ pthread_cond_signal(&sched->notify);
+ pthread_mutex_unlock(&sched->heap_lock);
+
+ return KNOT_EOK;
+}
+
+int evsched_cancel(event_t *ev)
+{
+ if (ev == NULL || ev->sched == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ evsched_t *sched = ev->sched;
+
+ /* Lock calendar. */
+ pthread_mutex_lock(&sched->heap_lock);
+
+ int found = heap_find(&sched->heap, (heap_val_t *)ev);
+ if (found > 0) {
+ heap_delete(&sched->heap, found);
+ pthread_cond_signal(&sched->notify);
+ }
+
+ /* Unlock calendar. */
+ pthread_mutex_unlock(&sched->heap_lock);
+
+ /* Reset event timer. */
+ memset(&ev->tv, 0, sizeof(struct timeval));
+
+ return KNOT_EOK;
+}
+
+void evsched_start(evsched_t *sched)
+{
+ dt_start(sched->thread);
+}
+
+void evsched_stop(evsched_t *sched)
+{
+ pthread_mutex_lock(&sched->heap_lock);
+ dt_stop(sched->thread);
+ pthread_cond_signal(&sched->notify);
+ pthread_mutex_unlock(&sched->heap_lock);
+}
+
+void evsched_join(evsched_t *sched)
+{
+ dt_join(sched->thread);
+}
+
+void evsched_pause(evsched_t *sched)
+{
+ pthread_mutex_lock(&sched->heap_lock);
+ sched->paused = true;
+ pthread_mutex_unlock(&sched->heap_lock);
+}
+
+void evsched_resume(evsched_t *sched)
+{
+ pthread_mutex_lock(&sched->heap_lock);
+ sched->paused = false;
+ pthread_cond_signal(&sched->notify);
+ pthread_mutex_unlock(&sched->heap_lock);
+}
diff --git a/src/knot/common/evsched.h b/src/knot/common/evsched.h
new file mode 100644
index 0000000..762c3f8
--- /dev/null
+++ b/src/knot/common/evsched.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief Event scheduler.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/time.h>
+
+#include "knot/server/dthreads.h"
+#include "contrib/ucw/heap.h"
+
+/* Forward decls. */
+struct evsched;
+struct event;
+
+/*!
+ * \brief Event callback.
+ *
+ * Pointer to whole event structure is passed to the callback.
+ * Callback should return 0 on success and negative integer on error.
+ *
+ * Example callback:
+ * \code
+ * void print_callback(event_t *t) {
+ * printf("Callback: %s\n", t->data);
+ * }
+ * \endcode
+ */
+typedef void (*event_cb_t)(struct event *);
+
+/*!
+ * \brief Event structure.
+ */
+typedef struct event {
+ struct heap_val hpos;
+ struct timeval tv; /*!< Event scheduled time. */
+ void *data; /*!< Usable data ptr. */
+ event_cb_t cb; /*!< Event callback. */
+ struct evsched *sched; /*!< Scheduler for this event. */
+} event_t;
+
+/*!
+ * \brief Event scheduler structure.
+ */
+typedef struct evsched {
+ volatile bool paused; /*!< Temporarily stop processing events. */
+ pthread_mutex_t heap_lock; /*!< Event heap locking. */
+ pthread_cond_t notify; /*!< Event heap notification. */
+ struct heap heap; /*!< Event heap. */
+ void *ctx; /*!< Scheduler context. */
+ dt_unit_t *thread;
+} evsched_t;
+
+/*!
+ * \brief Initialize event scheduler instance.
+ *
+ * \retval New instance on success.
+ * \retval NULL on error.
+ */
+int evsched_init(evsched_t *sched, void *ctx);
+
+/*!
+ * \brief Deinitialize and free event scheduler instance.
+ *
+ * \param sched Pointer to event scheduler instance.
+ */
+void evsched_deinit(evsched_t *sched);
+
+/*!
+ * \brief Create a callback event.
+ *
+ * \note Scheduler takes ownership of scheduled events. Created, but unscheduled
+ * events are in the ownership of the caller.
+ *
+ * \param sched Pointer to event scheduler instance.
+ * \param cb Callback handler.
+ * \param data Data for callback.
+ *
+ * \retval New instance on success.
+ * \retval NULL on error.
+ */
+event_t *evsched_event_create(evsched_t *sched, event_cb_t cb, void *data);
+
+/*!
+ * \brief Dispose event instance.
+ *
+ * \param ev Event instance.
+ */
+void evsched_event_free(event_t *ev);
+
+/*!
+ * \brief Schedule an event.
+ *
+ * \note This function checks if the event was already scheduled, if it was
+ * then it replaces this timer with the newer value.
+ * Running events are not canceled or waited for.
+ *
+ * \param ev Prepared event.
+ * \param dt Time difference in milliseconds from now (dt is relative).
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL
+ */
+int evsched_schedule(event_t *ev, uint32_t dt);
+
+/*!
+ * \brief Cancel a scheduled event.
+ *
+ * \warning May block until current running event is finished (as it cannot
+ * interrupt running event).
+ *
+ * \warning Never cancel event in it's callback. As it never finishes,
+ * it deadlocks.
+ *
+ * \param ev Scheduled event.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int evsched_cancel(event_t *ev);
+
+/*! \brief Start event processing threads. */
+void evsched_start(evsched_t *sched);
+
+/*! \brief Stop event processing threads. */
+void evsched_stop(evsched_t *sched);
+
+/*! \brief Join event processing threads. */
+void evsched_join(evsched_t *sched);
+
+/*! \brief Temporarily stop processing events. */
+void evsched_pause(evsched_t *sched);
+
+/*! \brief Resume processing events. */
+void evsched_resume(evsched_t *sched);
diff --git a/src/knot/common/fdset.c b/src/knot/common/fdset.c
new file mode 100644
index 0000000..a4b37d9
--- /dev/null
+++ b/src/knot/common/fdset.c
@@ -0,0 +1,336 @@
+/* Copyright (C) 2021 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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "knot/common/fdset.h"
+#include "contrib/time.h"
+#include "contrib/macros.h"
+
+#define MEM_RESIZE(p, n) { \
+ void *tmp = NULL; \
+ if ((tmp = realloc((p), (n) * sizeof(*p))) == NULL) { \
+ return KNOT_ENOMEM; \
+ } \
+ (p) = tmp; \
+}
+
+static int fdset_resize(fdset_t *set, const unsigned size)
+{
+ assert(set);
+
+ MEM_RESIZE(set->ctx, size);
+ MEM_RESIZE(set->timeout, size);
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ MEM_RESIZE(set->ev, size);
+#else
+ MEM_RESIZE(set->pfd, size);
+#endif
+ set->size = size;
+ return KNOT_EOK;
+}
+
+int fdset_init(fdset_t *set, const unsigned size)
+{
+ if (set == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(set, 0, sizeof(*set));
+
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+#ifdef HAVE_EPOLL
+ set->pfd = epoll_create1(0);
+#elif HAVE_KQUEUE
+ set->pfd = kqueue();
+#endif
+ if (set->pfd < 0) {
+ return knot_map_errno();
+ }
+#endif
+ int ret = fdset_resize(set, size);
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ if (ret != KNOT_EOK) {
+ close(set->pfd);
+ }
+#endif
+ return ret;
+}
+
+void fdset_clear(fdset_t *set)
+{
+ if (set == NULL) {
+ return;
+ }
+
+ free(set->ctx);
+ free(set->timeout);
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ free(set->ev);
+ free(set->recv_ev);
+ close(set->pfd);
+#else
+ free(set->pfd);
+#endif
+ memset(set, 0, sizeof(*set));
+}
+
+int fdset_add(fdset_t *set, const int fd, const fdset_event_t events, void *ctx)
+{
+ if (set == NULL || fd < 0) {
+ return KNOT_EINVAL;
+ }
+
+ if (set->n == set->size &&
+ fdset_resize(set, set->size + FDSET_RESIZE_STEP) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ const int idx = set->n++;
+ set->ctx[idx] = ctx;
+ set->timeout[idx] = 0;
+#ifdef HAVE_EPOLL
+ set->ev[idx].data.fd = fd;
+ set->ev[idx].events = events;
+ struct epoll_event ev = {
+ .data.u64 = idx,
+ .events = events
+ };
+ if (epoll_ctl(set->pfd, EPOLL_CTL_ADD, fd, &ev) != 0) {
+ return knot_map_errno();
+ }
+#elif HAVE_KQUEUE
+ EV_SET(&set->ev[idx], fd, events, EV_ADD, 0, 0, (void *)(intptr_t)idx);
+ if (kevent(set->pfd, &set->ev[idx], 1, NULL, 0, NULL) < 0) {
+ return knot_map_errno();
+ }
+#else
+ set->pfd[idx].fd = fd;
+ set->pfd[idx].events = events;
+ set->pfd[idx].revents = 0;
+#endif
+
+ return idx;
+}
+
+int fdset_remove(fdset_t *set, const unsigned idx)
+{
+ if (set == NULL || idx >= set->n) {
+ return KNOT_EINVAL;
+ }
+
+ const int fd = fdset_get_fd(set, idx);
+#ifdef HAVE_EPOLL
+ /* This is necessary as DDNS duplicates file descriptors! */
+ if (epoll_ctl(set->pfd, EPOLL_CTL_DEL, fd, NULL) != 0) {
+ close(fd);
+ return knot_map_errno();
+ }
+#elif HAVE_KQUEUE
+ /* Return delete flag back to original filter number. */
+#if defined(__NetBSD__)
+ if ((signed short)set->ev[idx].filter < 0)
+#else
+ if (set->ev[idx].filter >= 0)
+#endif
+ {
+ set->ev[idx].filter = ~set->ev[idx].filter;
+ }
+ set->ev[idx].flags = EV_DELETE;
+ if (kevent(set->pfd, &set->ev[idx], 1, NULL, 0, NULL) < 0) {
+ close(fd);
+ return knot_map_errno();
+ }
+#endif
+ close(fd);
+
+ const unsigned last = --set->n;
+ /* Nothing else if it is the last one. Move last -> i if some remain. */
+ if (idx < last) {
+ set->ctx[idx] = set->ctx[last];
+ set->timeout[idx] = set->timeout[last];
+#if defined(HAVE_EPOLL) || defined (HAVE_KQUEUE)
+ set->ev[idx] = set->ev[last];
+#ifdef HAVE_EPOLL
+ struct epoll_event ev = {
+ .data.u64 = idx,
+ .events = set->ev[idx].events
+ };
+ if (epoll_ctl(set->pfd, EPOLL_CTL_MOD, set->ev[last].data.fd, &ev) != 0) {
+ return knot_map_errno();
+ }
+#elif HAVE_KQUEUE
+ EV_SET(&set->ev[idx], set->ev[last].ident, set->ev[last].filter,
+ EV_ADD, 0, 0, (void *)(intptr_t)idx);
+ if (kevent(set->pfd, &set->ev[idx], 1, NULL, 0, NULL) < 0) {
+ return knot_map_errno();
+ }
+#endif
+#else
+ set->pfd[idx] = set->pfd[last];
+#endif
+ }
+
+ return KNOT_EOK;
+}
+
+int fdset_poll(fdset_t *set, fdset_it_t *it, const unsigned offset, const int timeout_ms)
+{
+ if (it == NULL) {
+ return KNOT_EINVAL;
+ }
+ it->unprocessed = 0;
+
+ if (set == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ it->set = set;
+ it->idx = offset;
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ if (set->recv_size != set->size) {
+ MEM_RESIZE(set->recv_ev, set->size);
+ set->recv_size = set->size;
+ }
+ it->ptr = set->recv_ev;
+ it->dirty = 0;
+#ifdef HAVE_EPOLL
+ if (set->n == 0) {
+ return 0;
+ }
+ if ((it->unprocessed = epoll_wait(set->pfd, set->recv_ev, set->recv_size,
+ timeout_ms)) == -1) {
+ return knot_map_errno();
+ }
+#ifndef NDEBUG
+ /* In specific circumstances with valgrind, it sometimes happens that
+ * `set->n < it->unprocessed`. */
+ if (it->unprocessed > 0 && unlikely(it->unprocessed > set->n)) {
+ assert(it->unprocessed == 232);
+ it->unprocessed = 0;
+ }
+#endif
+#elif HAVE_KQUEUE
+ struct timespec timeout = {
+ .tv_sec = timeout_ms / 1000,
+ .tv_nsec = (timeout_ms % 1000) * 1000000
+ };
+ if ((it->unprocessed = kevent(set->pfd, NULL, 0, set->recv_ev, set->recv_size,
+ (timeout_ms >= 0) ? &timeout : NULL)) == -1) {
+ return knot_map_errno();
+ }
+#endif
+ /*
+ * NOTE: Can't skip offset without bunch of syscalls!
+ * Because of that it waits for `ctx->n` (every socket). Offset is set when TCP
+ * throttling is ON. Sometimes it can return with sockets where none of them is
+ * connected socket, but it should not be common.
+ */
+ while (it->unprocessed > 0 && fdset_it_get_idx(it) < it->idx) {
+ it->ptr++;
+ it->unprocessed--;
+ }
+ return it->unprocessed;
+#else
+ it->unprocessed = poll(&set->pfd[offset], set->n - offset, timeout_ms);
+#ifndef NDEBUG
+ /* In specific circumstances with valgrind, it sometimes happens that
+ * `set->n < it->unprocessed`. */
+ if (it->unprocessed > 0 && unlikely(it->unprocessed > set->n - offset)) {
+ assert(it->unprocessed == 7);
+ it->unprocessed = 0;
+ }
+#endif
+ while (it->unprocessed > 0 && set->pfd[it->idx].revents == 0) {
+ it->idx++;
+ }
+ return it->unprocessed;
+#endif
+}
+
+void fdset_it_commit(fdset_it_t *it)
+{
+ if (it == NULL) {
+ return;
+ }
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ /* NOTE: reverse iteration to avoid as much "remove last" operations
+ * as possible. I'm not sure about performance improvement. It
+ * will skip some syscalls at begin of iteration, but what
+ * performance increase do we get is a question.
+ */
+ fdset_t *set = it->set;
+ for (int i = set->n - 1; it->dirty > 0 && i >= 0; --i) {
+#ifdef HAVE_EPOLL
+ if (set->ev[i].events == FDSET_REMOVE_FLAG)
+#else
+#if defined(__NetBSD__)
+ if ((signed short)set->ev[i].filter < 0)
+#else
+ if (set->ev[i].filter >= 0)
+#endif
+#endif
+ {
+ (void)fdset_remove(set, i);
+ it->dirty--;
+ }
+ }
+ assert(it->dirty == 0);
+#endif
+}
+
+int fdset_set_watchdog(fdset_t *set, const unsigned idx, const int interval)
+{
+ if (set == NULL || idx >= set->n) {
+ return KNOT_EINVAL;
+ }
+
+ /* Lift watchdog if interval is negative. */
+ if (interval < 0) {
+ set->timeout[idx] = 0;
+ return KNOT_EOK;
+ }
+
+ /* Update clock. */
+ const struct timespec now = time_now();
+ set->timeout[idx] = now.tv_sec + interval; /* Only seconds precision. */
+
+ return KNOT_EOK;
+}
+
+void fdset_sweep(fdset_t *set, const fdset_sweep_cb_t cb, void *data)
+{
+ if (set == NULL || cb == NULL) {
+ return;
+ }
+
+ /* Get time threshold. */
+ const struct timespec now = time_now();
+ unsigned idx = 0;
+ while (idx < set->n) {
+ /* Check sweep state, remove if requested. */
+ if (set->timeout[idx] > 0 && set->timeout[idx] <= now.tv_sec) {
+ const int fd = fdset_get_fd(set, idx);
+ if (cb(set, fd, data) == FDSET_SWEEP) {
+ (void)fdset_remove(set, idx);
+ continue;
+ }
+ }
+ ++idx;
+ }
+}
diff --git a/src/knot/common/fdset.h b/src/knot/common/fdset.h
new file mode 100644
index 0000000..95a5c61
--- /dev/null
+++ b/src/knot/common/fdset.h
@@ -0,0 +1,382 @@
+/* Copyright (C) 2021 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 I/O multiplexing with context and timeouts for each fd.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <time.h>
+
+#ifdef HAVE_EPOLL
+#include <sys/epoll.h>
+#elif HAVE_KQUEUE
+#include <sys/event.h>
+#else
+#include <poll.h>
+#endif
+
+#include "libknot/errcode.h"
+
+#define FDSET_RESIZE_STEP 256
+#ifdef HAVE_EPOLL
+#define FDSET_REMOVE_FLAG ~0U
+#endif
+
+/*! \brief Set of file descriptors with associated context and timeouts. */
+typedef struct {
+ unsigned n; /*!< Active fds. */
+ unsigned size; /*!< Array size (allocated). */
+ void **ctx; /*!< Context for each fd. */
+ time_t *timeout; /*!< Timeout for each fd (seconds precision). */
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+#ifdef HAVE_EPOLL
+ struct epoll_event *ev; /*!< Epoll event storage for each fd. */
+ struct epoll_event *recv_ev; /*!< Array for polled events. */
+#elif HAVE_KQUEUE
+ struct kevent *ev; /*!< Kqueue event storage for each fd. */
+ struct kevent *recv_ev; /*!< Array for polled events. */
+#endif
+ unsigned recv_size; /*!< Size of array for polled events. */
+ int pfd; /*!< File descriptor of kernel polling structure (epoll or kqueue). */
+#else
+ struct pollfd *pfd; /*!< Poll state for each fd. */
+#endif
+} fdset_t;
+
+/*! \brief State of iterator over received events */
+typedef struct {
+ fdset_t *set; /*!< Source fdset_t. */
+ unsigned idx; /*!< Event index offset. */
+ int unprocessed; /*!< Unprocessed events left. */
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+#ifdef HAVE_EPOLL
+ struct epoll_event *ptr; /*!< Pointer on processed event. */
+#elif HAVE_KQUEUE
+ struct kevent *ptr; /*!< Pointer on processed event. */
+#endif
+ unsigned dirty; /*!< Number of fd to be removed on commit. */
+#endif
+} fdset_it_t;
+
+typedef enum {
+#ifdef HAVE_EPOLL
+ FDSET_POLLIN = EPOLLIN,
+ FDSET_POLLOUT = EPOLLOUT,
+#elif HAVE_KQUEUE
+ FDSET_POLLIN = EVFILT_READ,
+ FDSET_POLLOUT = EVFILT_WRITE,
+#else
+ FDSET_POLLIN = POLLIN,
+ FDSET_POLLOUT = POLLOUT,
+#endif
+} fdset_event_t;
+
+/*! \brief Mark-and-sweep state. */
+typedef enum {
+ FDSET_KEEP,
+ FDSET_SWEEP
+} fdset_sweep_state_t;
+
+/*! \brief Sweep callback (set, index, data) */
+typedef fdset_sweep_state_t (*fdset_sweep_cb_t)(fdset_t *, int, void *);
+
+/*!
+ * \brief Initialize fdset to given size.
+ *
+ * \param set Target set.
+ * \param size Initial set size.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int fdset_init(fdset_t *set, const unsigned size);
+
+/*!
+ * \brief Clear whole context of the fdset.
+ *
+ * \param set Target set.
+ */
+void fdset_clear(fdset_t *set);
+
+/*!
+ * \brief Add file descriptor to watched set.
+ *
+ * \param set Target set.
+ * \param fd Added file descriptor.
+ * \param events Mask of watched events.
+ * \param ctx Context (optional).
+ *
+ * \retval ret >= 0 is index of the added fd.
+ * \retval ret < 0 on error.
+ */
+int fdset_add(fdset_t *set, const int fd, const fdset_event_t events, void *ctx);
+
+/*!
+ * \brief Remove and close file descriptor from watched set.
+ *
+ * \param set Target set.
+ * \param idx Index of the removed fd.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int fdset_remove(fdset_t *set, const unsigned idx);
+
+/*!
+ * \brief Wait for receive events.
+ *
+ * Skip events based on offset and set iterator on first event.
+ *
+ * \param set Target set.
+ * \param it Event iterator storage.
+ * \param offset Index of first event.
+ * \param timeout_ms Timeout of operation in milliseconds (use -1 for unlimited).
+ *
+ * \retval ret >= 0 represents number of events received.
+ * \retval ret < 0 on error.
+ */
+int fdset_poll(fdset_t *set, fdset_it_t *it, const unsigned offset, const int timeout_ms);
+
+/*!
+ * \brief Set file descriptor watchdog interval.
+ *
+ * Set time (interval from now) after which the associated file descriptor
+ * should be sweeped (see fdset_sweep). Good example is setting a grace period
+ * of N seconds between socket activity. If socket is not active within
+ * <now, now + interval>, it is sweeped and closed.
+ *
+ * \param set Target set.
+ * \param idx Index of the file descriptor.
+ * \param interval Allowed interval without activity (seconds).
+ * -1 disables watchdog timer.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int fdset_set_watchdog(fdset_t *set, const unsigned idx, const int interval);
+
+/*!
+ * \brief Sweep file descriptors with exceeding inactivity period.
+ *
+ * \param set Target set.
+ * \param cb Callback for sweeped descriptors.
+ * \param data Pointer to extra data.
+ */
+void fdset_sweep(fdset_t *set, const fdset_sweep_cb_t cb, void *data);
+
+/*!
+ * \brief Returns file descriptor based on index.
+ *
+ * \param set Target set.
+ * \param idx Index of the file descriptor.
+ *
+ * \retval ret >= 0 for file descriptor.
+ * \retval ret < 0 on errors.
+ */
+inline static int fdset_get_fd(const fdset_t *set, const unsigned idx)
+{
+ assert(set && idx < set->n);
+
+#ifdef HAVE_EPOLL
+ return set->ev[idx].data.fd;
+#elif HAVE_KQUEUE
+ return set->ev[idx].ident;
+#else
+ return set->pfd[idx].fd;
+#endif
+}
+
+/*!
+ * \brief Returns number of file descriptors stored in set.
+ *
+ * \param set Target set.
+ *
+ * \retval Number of descriptors stored
+ */
+inline static unsigned fdset_get_length(const fdset_t *set)
+{
+ assert(set);
+
+ return set->n;
+}
+
+/*!
+ * \brief Get index of event in set referenced by iterator.
+ *
+ * \param it Target iterator.
+ *
+ * \retval Index of event.
+ */
+inline static unsigned fdset_it_get_idx(const fdset_it_t *it)
+{
+ assert(it);
+
+#ifdef HAVE_EPOLL
+ return it->ptr->data.u64;
+#elif HAVE_KQUEUE
+ return (unsigned)(intptr_t)it->ptr->udata;
+#else
+ return it->idx;
+#endif
+}
+
+/*!
+ * \brief Get file descriptor of event referenced by iterator.
+ *
+ * \param it Target iterator.
+ *
+ * \retval ret >= 0 for file descriptor.
+ * \retval ret < 0 on errors.
+ */
+inline static int fdset_it_get_fd(const fdset_it_t *it)
+{
+ assert(it);
+
+#ifdef HAVE_EPOLL
+ return it->set->ev[fdset_it_get_idx(it)].data.fd;
+#elif HAVE_KQUEUE
+ return it->ptr->ident;
+#else
+ return it->set->pfd[it->idx].fd;
+#endif
+}
+
+/*!
+ * \brief Move iterator on next received event.
+ *
+ * \param it Target iterator.
+ */
+inline static void fdset_it_next(fdset_it_t *it)
+{
+ assert(it);
+
+#if defined(HAVE_EPOLL) || defined(HAVE_KQUEUE)
+ do {
+ it->ptr++;
+ it->unprocessed--;
+ } while (it->unprocessed > 0 && fdset_it_get_idx(it) < it->idx);
+#else
+ if (--it->unprocessed > 0) {
+ while (it->set->pfd[++it->idx].revents == 0); /* nop */
+ }
+#endif
+}
+
+/*!
+ * \brief Remove file descriptor referenced by iterator from watched set.
+ *
+ * \param it Target iterator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+inline static void fdset_it_remove(fdset_it_t *it)
+{
+ assert(it);
+
+#ifdef HAVE_EPOLL
+ const int idx = fdset_it_get_idx(it);
+ it->set->ev[idx].events = FDSET_REMOVE_FLAG;
+ it->dirty++;
+#elif HAVE_KQUEUE
+ const int idx = fdset_it_get_idx(it);
+ /* Bitwise negated filter marks event for delete. */
+ /* Filters become: */
+ /* [FreeBSD] */
+ /* EVFILT_READ (-1) -> 0 */
+ /* EVFILT_WRITE (-2) -> 1 */
+ /* [NetBSD] */
+ /* EVFILT_READ (0) -> -1 */
+ /* EVFILT_WRITE (1) -> -2 */
+ /* If not marked for delete then mark for delete. */
+#if defined(__NetBSD__)
+ if ((signed short)it->set->ev[idx].filter >= 0)
+#else
+ if (it->set->ev[idx].filter < 0)
+#endif
+ {
+ it->set->ev[idx].filter = ~it->set->ev[idx].filter;
+ }
+ it->dirty++;
+#else
+ (void)fdset_remove(it->set, fdset_it_get_idx(it));
+ /* Iterator should return on last valid already processed element. */
+ /* On `next` call (in for-loop) will point on first unprocessed. */
+ it->idx--;
+#endif
+}
+
+/*!
+ * \brief Commit changes made in fdset using iterator.
+ *
+ * \param it Target iterator.
+ */
+void fdset_it_commit(fdset_it_t *it);
+
+/*!
+ * \brief Decide if there is more received events.
+ *
+ * \param it Target iterator.
+ *
+ * \retval Logical flag representing 'done' state.
+ */
+inline static bool fdset_it_is_done(const fdset_it_t *it)
+{
+ assert(it);
+
+ return it->unprocessed <= 0;
+}
+
+/*!
+ * \brief Decide if event referenced by iterator is POLLIN event.
+ *
+ * \param it Target iterator.
+ *
+ * \retval Logical flag represents 'POLLIN' event received.
+ */
+inline static bool fdset_it_is_pollin(const fdset_it_t *it)
+{
+ assert(it);
+
+#ifdef HAVE_EPOLL
+ return it->ptr->events & EPOLLIN;
+#elif HAVE_KQUEUE
+ return it->ptr->filter == EVFILT_READ;
+#else
+ return it->set->pfd[it->idx].revents & POLLIN;
+#endif
+}
+
+/*!
+ * \brief Decide if event referenced by iterator is error event.
+ *
+ * \param it Target iterator.
+ *
+ * \retval Logical flag represents error event received.
+ */
+inline static bool fdset_it_is_error(const fdset_it_t *it)
+{
+ assert(it);
+
+#ifdef HAVE_EPOLL
+ return it->ptr->events & (EPOLLERR | EPOLLHUP);
+#elif HAVE_KQUEUE
+ return it->ptr->flags & EV_ERROR;
+#else
+ return it->set->pfd[it->idx].revents & (POLLERR | POLLHUP | POLLNVAL);
+#endif
+}
diff --git a/src/knot/common/log.c b/src/knot/common/log.c
new file mode 100644
index 0000000..8bbdc51
--- /dev/null
+++ b/src/knot/common/log.c
@@ -0,0 +1,491 @@
+/* Copyright (C) 2021 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 <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <time.h>
+#include <urcu.h>
+
+#ifdef ENABLE_SYSTEMD
+#define SD_JOURNAL_SUPPRESS_LOCATION 1
+#include <systemd/sd-journal.h>
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "knot/common/log.h"
+#include "libknot/libknot.h"
+#include "contrib/ucw/lists.h"
+
+/*! Single log message buffer length (one line). */
+#define LOG_BUFLEN 512
+#define NULL_ZONE_STR "?"
+
+#ifdef ENABLE_SYSTEMD
+int use_journal = 0;
+#endif
+
+/*! Log context. */
+typedef struct {
+ size_t target_count; /*!< Log target count. */
+ int *target; /*!< Log targets. */
+ size_t file_count; /*!< Open files count. */
+ FILE **file; /*!< Open files. */
+ log_flag_t flags; /*!< Formatting flags. */
+} log_t;
+
+/*! Log singleton. */
+log_t *s_log = NULL;
+
+static bool log_isopen(void)
+{
+ return s_log != NULL;
+}
+
+static void sink_free(log_t *log)
+{
+ if (log == NULL) {
+ return;
+ }
+
+ // Close open log files.
+ for (int i = 0; i < log->file_count; ++i) {
+ fclose(log->file[i]);
+ }
+ free(log->target);
+ free(log->file);
+ free(log);
+}
+
+/*!
+ * \brief Create logging targets respecting their canonical order.
+ *
+ * Facilities ordering: Syslog, Stderr, Stdout, File0...
+ */
+static log_t *sink_setup(size_t file_count)
+{
+ log_t *log = malloc(sizeof(*log));
+ if (log == NULL) {
+ return NULL;
+ }
+ memset(log, 0, sizeof(*log));
+
+ // Reserve space for targets.
+ log->target_count = LOG_TARGET_FILE + file_count;
+ log->target = malloc(LOG_SOURCE_ANY * sizeof(int) * log->target_count);
+ if (!log->target) {
+ free(log);
+ return NULL;
+ }
+ memset(log->target, 0, LOG_SOURCE_ANY * sizeof(int) * log->target_count);
+
+ // Reserve space for log files.
+ if (file_count > 0) {
+ log->file = malloc(sizeof(FILE *) * file_count);
+ if (!log->file) {
+ free(log->target);
+ free(log);
+ return NULL;
+ }
+ memset(log->file, 0, sizeof(FILE *) * file_count);
+ }
+
+ return log;
+}
+
+static void sink_publish(log_t *log)
+{
+ log_t **current_log = &s_log;
+ log_t *old_log = rcu_xchg_pointer(current_log, log);
+ synchronize_rcu();
+ sink_free(old_log);
+}
+
+static int *src_levels(log_t *log, log_target_t target, log_source_t src)
+{
+ assert(src < LOG_SOURCE_ANY);
+ return &log->target[LOG_SOURCE_ANY * target + src];
+}
+
+static void sink_levels_set(log_t *log, log_target_t target, log_source_t src, int levels)
+{
+ // Assign levels to the specified source.
+ if (src != LOG_SOURCE_ANY) {
+ *src_levels(log, target, src) = levels;
+ } else {
+ // ANY ~ set levels to all sources.
+ for (int i = 0; i < LOG_SOURCE_ANY; ++i) {
+ *src_levels(log, target, i) = levels;
+ }
+ }
+}
+
+static void sink_levels_add(log_t *log, log_target_t target, log_source_t src, int levels)
+{
+ // Add levels to the specified source.
+ if (src != LOG_SOURCE_ANY) {
+ *src_levels(log, target, src) |= levels;
+ } else {
+ // ANY ~ add levels to all sources.
+ for (int i = 0; i < LOG_SOURCE_ANY; ++i) {
+ *src_levels(log, target, i) |= levels;
+ }
+ }
+}
+
+void log_init(void)
+{
+ // Setup initial state.
+ int emask = LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING);
+ int imask = LOG_MASK(LOG_NOTICE) | LOG_MASK(LOG_INFO);
+
+ // Publish base log sink.
+ log_t *log = sink_setup(0);
+ if (log == NULL) {
+ fprintf(stderr, "Failed to setup logging\n");
+ return;
+ }
+
+#ifdef ENABLE_SYSTEMD
+ // Should only use the journal if system was booted with systemd.
+ use_journal = sd_booted();
+#endif
+
+ sink_levels_set(log, LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, emask);
+ sink_levels_set(log, LOG_TARGET_STDERR, LOG_SOURCE_ANY, emask);
+ sink_levels_set(log, LOG_TARGET_STDOUT, LOG_SOURCE_ANY, imask);
+ sink_publish(log);
+
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON);
+}
+
+void log_close(void)
+{
+ sink_publish(NULL);
+
+ fflush(stdout);
+ fflush(stderr);
+
+ closelog();
+}
+
+void log_flag_set(log_flag_t flag)
+{
+ if (log_isopen()) {
+ s_log->flags |= flag;
+ }
+}
+
+void log_levels_set(log_target_t target, log_source_t src, int levels)
+{
+ if (log_isopen()) {
+ sink_levels_set(s_log, target, src, levels);
+ }
+}
+
+void log_levels_add(log_target_t target, log_source_t src, int levels)
+{
+ if (log_isopen()) {
+ sink_levels_add(s_log, target, src, levels);
+ }
+}
+
+static void emit_log_msg(int level, log_source_t src, const char *zone,
+ size_t zone_len, const char *msg, const char *param)
+{
+ log_t *log = s_log;
+
+ // Syslog target.
+ if (*src_levels(log, LOG_TARGET_SYSLOG, src) & LOG_MASK(level)) {
+#ifdef ENABLE_SYSTEMD
+ if (use_journal) {
+ char *zone_fmt = zone ? "ZONE=%.*s." : NULL;
+ sd_journal_send("PRIORITY=%d", level,
+ "MESSAGE=%s", msg,
+ zone_fmt, zone_len, zone,
+ param, NULL);
+ } else
+#endif
+ {
+ syslog(level, "%s", msg);
+ }
+ }
+
+ // Prefix date and time.
+ char tstr[LOG_BUFLEN] = { 0 };
+ if (!(s_log->flags & LOG_FLAG_NOTIMESTAMP)) {
+ struct tm lt;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ time_t sec = tv.tv_sec;
+ if (localtime_r(&sec, &lt) != NULL) {
+ strftime(tstr, sizeof(tstr), KNOT_LOG_TIME_FORMAT " ", &lt);
+ }
+ }
+
+ // Other log targets.
+ for (int i = LOG_TARGET_STDERR; i < LOG_TARGET_FILE + log->file_count; ++i) {
+ if (*src_levels(log, i, src) & LOG_MASK(level)) {
+ FILE *stream;
+ switch (i) {
+ case LOG_TARGET_STDERR: stream = stderr; break;
+ case LOG_TARGET_STDOUT: stream = stdout; break;
+ default: stream = log->file[i - LOG_TARGET_FILE]; break;
+ }
+
+ // Print the message.
+ fprintf(stream, "%s%s\n", tstr, msg);
+ if (stream == stdout) {
+ fflush(stream);
+ }
+ }
+ }
+}
+
+static const char *level_prefix(int level)
+{
+ switch (level) {
+ case LOG_DEBUG: return "debug";
+ case LOG_INFO: return "info";
+ case LOG_NOTICE: return "notice";
+ case LOG_WARNING: return "warning";
+ case LOG_ERR: return "error";
+ case LOG_CRIT: return "critical";
+ default: return NULL;
+ };
+}
+
+static int log_msg_add(char **write, size_t *capacity, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ int written = vsnprintf(*write, *capacity, fmt, args);
+ va_end(args);
+
+ if (written < 0 || written >= *capacity) {
+ return KNOT_ESPACE;
+ }
+
+ *write += written;
+ *capacity -= written;
+
+ return KNOT_EOK;
+}
+
+static void log_msg_text(int level, log_source_t src, const char *zone,
+ const char *fmt, va_list args, const char *param)
+{
+ if (!log_isopen() || src == LOG_SOURCE_ANY) {
+ return;
+ }
+
+ // Buffer for log message.
+ char buff[LOG_BUFLEN];
+ char *write = buff;
+ size_t capacity = sizeof(buff);
+
+ rcu_read_lock();
+
+ // Prefix error level.
+ if (level != LOG_INFO || !(s_log->flags & LOG_FLAG_NOINFO)) {
+ const char *prefix = level_prefix(level);
+ int ret = log_msg_add(&write, &capacity, "%s: ", prefix);
+ if (ret != KNOT_EOK) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ // Prefix zone name.
+ size_t zone_len = 0;
+ if (zone != NULL) {
+ zone_len = strlen(zone);
+ if (zone_len > 0 && zone[zone_len - 1] == '.') {
+ zone_len--;
+ }
+
+ int ret = log_msg_add(&write, &capacity, "[%.*s.] ", (int)zone_len, zone);
+ if (ret != KNOT_EOK) {
+ rcu_read_unlock();
+ return;
+ }
+ }
+
+ // Compile log message.
+ int ret = vsnprintf(write, capacity, fmt, args);
+ if (ret >= 0) {
+ // Send to logging targets.
+ emit_log_msg(level, src, zone, zone_len, buff, param);
+ }
+
+ rcu_read_unlock();
+}
+
+void log_fmt(int priority, log_source_t src, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ log_msg_text(priority, src, NULL, fmt, args, NULL);
+ va_end(args);
+}
+
+void log_fmt_zone(int priority, log_source_t src, const knot_dname_t *zone,
+ const char *param, const char *fmt, ...)
+{
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone, sizeof(buff));
+ if (zone_str == NULL) {
+ zone_str = NULL_ZONE_STR;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ log_msg_text(priority, src, zone_str, fmt, args, param);
+ va_end(args);
+}
+
+void log_fmt_zone_str(int priority, log_source_t src, const char *zone,
+ const char *fmt, ...)
+{
+ if (zone == NULL) {
+ zone = NULL_ZONE_STR;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ log_msg_text(priority, src, zone, fmt, args, NULL);
+ va_end(args);
+}
+
+int log_update_privileges(int uid, int gid)
+{
+ if (!log_isopen()) {
+ return KNOT_EOK;
+ }
+
+ for (int i = 0; i < s_log->file_count; ++i) {
+ if (fchown(fileno(s_log->file[i]), uid, gid) < 0) {
+ log_error("failed to change log file owner");
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static log_target_t get_logtype(const char *logname)
+{
+ assert(logname);
+
+ if (strcasecmp(logname, "syslog") == 0) {
+ return LOG_TARGET_SYSLOG;
+ } else if (strcasecmp(logname, "stderr") == 0) {
+ return LOG_TARGET_STDERR;
+ } else if (strcasecmp(logname, "stdout") == 0) {
+ return LOG_TARGET_STDOUT;
+ } else {
+ return LOG_TARGET_FILE;
+ }
+}
+
+static int log_open_file(log_t *log, const char *filename)
+{
+ assert(LOG_TARGET_FILE + log->file_count < log->target_count);
+
+ // Open the file.
+ log->file[log->file_count] = fopen(filename, "a");
+ if (log->file[log->file_count] == NULL) {
+ return knot_map_errno();
+ }
+
+ // Disable buffering.
+ setvbuf(log->file[log->file_count], NULL, _IONBF, 0);
+
+ return LOG_TARGET_FILE + log->file_count++;
+}
+
+void log_reconfigure(conf_t *conf)
+{
+ // Use defaults if no 'log' section is configured.
+ if (conf_id_count(conf, C_LOG) == 0) {
+ log_close();
+ log_init();
+ return;
+ }
+
+ // Find maximum log target id.
+ unsigned files = 0;
+ for (conf_iter_t iter = conf_iter(conf, C_LOG); iter.code == KNOT_EOK;
+ conf_iter_next(conf, &iter)) {
+ conf_val_t id = conf_iter_id(conf, &iter);
+ if (get_logtype(conf_str(&id)) == LOG_TARGET_FILE) {
+ ++files;
+ }
+ }
+
+ // Initialize logsystem.
+ log_t *log = sink_setup(files);
+ if (log == NULL) {
+ fprintf(stderr, "Failed to setup logging\n");
+ return;
+ }
+
+ // Setup logs.
+ for (conf_iter_t iter = conf_iter(conf, C_LOG); iter.code == KNOT_EOK;
+ conf_iter_next(conf, &iter)) {
+ conf_val_t id = conf_iter_id(conf, &iter);
+ const char *logname = conf_str(&id);
+
+ // Get target.
+ int target = get_logtype(logname);
+ if (target == LOG_TARGET_FILE) {
+ target = log_open_file(log, logname);
+ if (target < 0) {
+ log_error("failed to open log, file '%s' (%s)",
+ logname, knot_strerror(target));
+ continue;
+ }
+ }
+
+ conf_val_t levels_val;
+ unsigned levels;
+
+ // Set SERVER logging.
+ levels_val = conf_id_get(conf, C_LOG, C_SERVER, &id);
+ levels = conf_opt(&levels_val);
+ sink_levels_add(log, target, LOG_SOURCE_SERVER, levels);
+
+ // Set CONTROL logging.
+ levels_val = conf_id_get(conf, C_LOG, C_CTL, &id);
+ levels = conf_opt(&levels_val);
+ sink_levels_add(log, target, LOG_SOURCE_CONTROL, levels);
+
+ // Set ZONE logging.
+ levels_val = conf_id_get(conf, C_LOG, C_ZONE, &id);
+ levels = conf_opt(&levels_val);
+ sink_levels_add(log, target, LOG_SOURCE_ZONE, levels);
+
+ // Set ANY logging.
+ levels_val = conf_id_get(conf, C_LOG, C_ANY, &id);
+ levels = conf_opt(&levels_val);
+ sink_levels_add(log, target, LOG_SOURCE_ANY, levels);
+ }
+
+ sink_publish(log);
+}
diff --git a/src/knot/common/log.h b/src/knot/common/log.h
new file mode 100644
index 0000000..49a8375
--- /dev/null
+++ b/src/knot/common/log.h
@@ -0,0 +1,187 @@
+/* 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 Logging facility.
+ *
+ * Supported log levels/priorities:
+ * LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, and LOG_DEBUG.
+ *
+ * \see syslog.h
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <syslog.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/dname.h"
+#include "knot/conf/conf.h"
+
+/*! \brief Format for timestamps in log files. */
+#define KNOT_LOG_TIME_FORMAT "%Y-%m-%dT%H:%M:%S%z"
+
+/*! \brief Logging targets. */
+typedef enum {
+ LOG_TARGET_SYSLOG = 0, /*!< System log. */
+ LOG_TARGET_STDERR = 1, /*!< Standard error stream. */
+ LOG_TARGET_STDOUT = 2, /*!< Standard output stream. */
+ LOG_TARGET_FILE = 3 /*!< Generic logging to a file (unbuffered). */
+} log_target_t;
+
+/*! \brief Logging sources. */
+typedef enum {
+ LOG_SOURCE_SERVER = 0, /*!< Server module. */
+ LOG_SOURCE_CONTROL = 1, /*!< Server control module. */
+ LOG_SOURCE_ZONE = 2, /*!< Zone manipulation module. */
+ LOG_SOURCE_ANY = 3 /*!< Any module. */
+} log_source_t;
+
+/*! \brief Logging format flags. */
+typedef enum {
+ LOG_FLAG_NOTIMESTAMP = 1 << 0, /*!< Don't print timestamp prefix. */
+ LOG_FLAG_NOINFO = 1 << 1 /*!< Don't print info level prefix. */
+} log_flag_t;
+
+/*!
+ * \brief Setup logging subsystem.
+ */
+void log_init(void);
+
+/*!
+ * \brief Close and deinitialize log.
+ */
+void log_close(void);
+
+/*!
+ * \brief Set logging format flag.
+ */
+void log_flag_set(log_flag_t flag);
+
+/*!
+ * \brief Set log levels for given target.
+ *
+ * \param target Logging target index (LOG_TARGET_SYSLOG...).
+ * \param src Logging source (LOG_SOURCE_SERVER...LOG_SOURCE_ANY).
+ * \param levels Bitmask of specified log levels.
+ */
+void log_levels_set(log_target_t target, log_source_t src, int levels);
+
+/*!
+ * \brief Add log levels to a given target.
+ *
+ * New levels are added on top of existing, the resulting levels set is
+ * "old_levels OR new_levels".
+ *
+ * \param target Logging target index (LOG_TARGET_SYSLOG...).
+ * \param src Logging source (LOG_SOURCE_SERVER...LOG_SOURCE_ANY).
+ * \param levels Bitmask of specified log levels.
+ */
+void log_levels_add(log_target_t target, log_source_t src, int levels);
+
+/*!
+ * \brief Log message into server category.
+ *
+ * Function follows printf() format.
+ *
+ * \note LOG_SOURCE_ANY is not a valid value for the src parameter.
+ *
+ * \param priority Message priority.
+ * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE).
+ * \param fmt Content of the logged message.
+ */
+void log_fmt(int priority, log_source_t src, const char *fmt, ...)
+__attribute__((format(printf, 3, 4)));
+
+/*!
+ * \brief Log message into zone category.
+ *
+ * \see log_fmt
+ *
+ * \param priority Message priority.
+ * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE).
+ * \param zone Zone name in wire format.
+ * \param param Optional key-value parameter for structured logging.
+ * \param fmt Content of the logged message.
+ */
+void log_fmt_zone(int priority, log_source_t src, const knot_dname_t *zone,
+ const char *param, const char *fmt, ...)
+__attribute__((format(printf, 5, 6)));
+
+/*!
+ * \brief Log message into zone category.
+ *
+ * \see log_fmt
+ *
+ * \param zone Zone name as an ASCII string.
+ * \param priority Message priority.
+ * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE).
+ * \param fmt Content of the logged message.
+ */
+void log_fmt_zone_str(int priority, log_source_t src, const char *zone, const char *fmt, ...)
+__attribute__((format(printf, 4, 5)));
+
+/*!
+ * \brief Convenient logging macros.
+ */
+#define log_fatal(msg, ...) log_fmt(LOG_CRIT, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+#define log_error(msg, ...) log_fmt(LOG_ERR, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+#define log_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+#define log_notice(msg, ...) log_fmt(LOG_NOTICE, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+#define log_info(msg, ...) log_fmt(LOG_INFO, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+#define log_debug(msg, ...) log_fmt(LOG_DEBUG, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__)
+
+#define log_ctl_fatal(msg, ...) log_fmt(LOG_CRIT, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+#define log_ctl_error(msg, ...) log_fmt(LOG_ERR, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+#define log_ctl_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+#define log_ctl_notice(msg, ...) log_fmt(LOG_NOTICE, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+#define log_ctl_info(msg, ...) log_fmt(LOG_INFO, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+#define log_ctl_debug(msg, ...) log_fmt(LOG_DEBUG, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__)
+
+#define log_ctl_zone_str_error(zone, msg, ...) log_fmt_zone_str(LOG_ERR, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)
+#define log_ctl_zone_str_info(zone, msg, ...) log_fmt_zone_str(LOG_INFO, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)
+#define log_ctl_zone_str_debug(zone, msg, ...) log_fmt_zone_str(LOG_DEBUG, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__)
+
+#define log_zone_fatal(zone, msg, ...) log_fmt_zone(LOG_CRIT, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+#define log_zone_error(zone, msg, ...) log_fmt_zone(LOG_ERR, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+#define log_zone_warning(zone, msg, ...) log_fmt_zone(LOG_WARNING, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+#define log_zone_notice(zone, msg, ...) log_fmt_zone(LOG_NOTICE, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+#define log_zone_info(zone, msg, ...) log_fmt_zone(LOG_INFO, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+#define log_zone_debug(zone, msg, ...) log_fmt_zone(LOG_DEBUG, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__)
+
+#define log_zone_str_fatal(zone, msg, ...) log_fmt_zone_str(LOG_CRIT, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+#define log_zone_str_error(zone, msg, ...) log_fmt_zone_str(LOG_ERR, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+#define log_zone_str_warning(zone, msg, ...) log_fmt_zone_str(LOG_WARNING, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+#define log_zone_str_notice(zone, msg, ...) log_fmt_zone_str(LOG_NOTICE, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+#define log_zone_str_info(zone, msg, ...) log_fmt_zone_str(LOG_INFO, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+#define log_zone_str_debug(zone, msg, ...) log_fmt_zone_str(LOG_DEBUG, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__)
+
+/*!
+ * \brief Update open files ownership.
+ *
+ * \param uid New owner id.
+ * \param gid New group id.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int log_update_privileges(int uid, int gid);
+
+/*!
+ * \brief Setup logging facilities from config.
+ */
+void log_reconfigure(conf_t *conf);
diff --git a/src/knot/common/process.c b/src/knot/common/process.c
new file mode 100644
index 0000000..bdec1d4
--- /dev/null
+++ b/src/knot/common/process.c
@@ -0,0 +1,194 @@
+/* Copyright (C) 2022 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 <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "knot/common/log.h"
+#include "knot/common/process.h"
+#include "knot/conf/conf.h"
+#include "libknot/errcode.h"
+
+static char* pid_filename(void)
+{
+ conf_val_t val = conf_get(conf(), C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&val, NULL);
+ val = conf_get(conf(), C_SRV, C_PIDFILE);
+ char *pidfile = conf_abs_path(&val, rundir);
+ free(rundir);
+
+ return pidfile;
+}
+
+static pid_t pid_read(const char *filename)
+{
+ if (filename == NULL) {
+ return 0;
+ }
+
+ size_t len = 0;
+ char buf[64] = { 0 };
+
+ FILE *fp = fopen(filename, "r");
+ if (fp == NULL) {
+ return 0;
+ }
+
+ /* Read the content of the file. */
+ len = fread(buf, 1, sizeof(buf) - 1, fp);
+ fclose(fp);
+ if (len < 1) {
+ return 0;
+ }
+
+ /* Convert pid. */
+ errno = 0;
+ char *end = 0;
+ unsigned long pid = strtoul(buf, &end, 10);
+ if (end == buf || *end != '\0'|| errno != 0) {
+ return 0;
+ }
+
+ return (pid_t)pid;
+}
+
+static int pid_write(const char *filename, pid_t pid)
+{
+ if (filename == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Convert. */
+ char buf[64];
+ int len = 0;
+ len = snprintf(buf, sizeof(buf), "%lu", (unsigned long)pid);
+ if (len < 0 || len >= sizeof(buf)) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Create file. */
+ int ret = KNOT_EOK;
+ int fd = open(filename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP);
+ if (fd >= 0) {
+ if (write(fd, buf, len) != len) {
+ ret = knot_map_errno();
+ }
+ close(fd);
+ } else {
+ ret = knot_map_errno();
+ }
+
+ return ret;
+}
+
+unsigned long pid_check_and_create(void)
+{
+ struct stat st;
+ char *pidfile = pid_filename();
+ pid_t pid = pid_read(pidfile);
+
+ /* Check PID for existence and liveness. */
+ if (pid > 0 && pid_running(pid)) {
+ log_fatal("server PID found, already running");
+ free(pidfile);
+ return 0;
+ } else if (stat(pidfile, &st) == 0) {
+ assert(pidfile);
+ log_warning("removing stale PID file '%s'", pidfile);
+ pid_cleanup();
+ }
+
+ /* Get current PID. */
+ pid = getpid();
+
+ /* Create a PID file. */
+ int ret = pid_write(pidfile, pid);
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to create a PID file '%s' (%s)", pidfile,
+ knot_strerror(ret));
+ free(pidfile);
+ return 0;
+ }
+ free(pidfile);
+
+ return (unsigned long)pid;
+}
+
+void pid_cleanup(void)
+{
+ char *pidfile = pid_filename();
+ if (pidfile != NULL) {
+ (void)unlink(pidfile);
+ free(pidfile);
+ }
+}
+
+bool pid_running(pid_t pid)
+{
+ return kill(pid, 0) == 0;
+}
+
+int proc_update_privileges(int uid, int gid)
+{
+#ifdef HAVE_SETGROUPS
+ /* Drop supplementary groups. */
+ if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) {
+ if (setgroups(0, NULL) < 0) {
+ log_warning("failed to drop supplementary groups for "
+ "UID %d (%s)", getuid(), strerror(errno));
+ }
+# ifdef HAVE_INITGROUPS
+ struct passwd *pw;
+ if ((pw = getpwuid(uid)) == NULL) {
+ log_warning("failed to get passwd entry for UID %d (%s)",
+ uid, strerror(errno));
+ } else {
+ if (initgroups(pw->pw_name, gid) < 0) {
+ log_warning("failed to set supplementary groups "
+ "for UID %d (%s)", uid, strerror(errno));
+ }
+ }
+# endif /* HAVE_INITGROUPS */
+ }
+#endif /* HAVE_SETGROUPS */
+
+ /* Watch uid/gid. */
+ if ((gid_t)gid != getgid()) {
+ log_info("changing GID to %d", gid);
+ if (setregid(gid, gid) < 0) {
+ log_error("failed to change GID to %d", gid);
+ return KNOT_ERROR;
+ }
+ }
+ if ((uid_t)uid != getuid()) {
+ log_info("changing UID to %d", uid);
+ if (setreuid(uid, uid) < 0) {
+ log_error("failed to change UID to %d", uid);
+ return KNOT_ERROR;
+ }
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/common/process.h b/src/knot/common/process.h
new file mode 100644
index 0000000..14ca34e
--- /dev/null
+++ b/src/knot/common/process.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2021 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 Functions for POSIX process handling.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <unistd.h>
+
+/*!
+ * \brief Check if PID file exists and create it if possible.
+ *
+ * \retval 0 if failed.
+ * \retval Current PID.
+ */
+unsigned long pid_check_and_create(void);
+
+/*!
+ * \brief Remove PID file.
+ *
+ * \warning PID file content won't be checked.
+ */
+void pid_cleanup(void);
+
+/*!
+ * \brief Return true if the PID is running.
+ *
+ * \param pid Process ID.
+ *
+ * \retval 1 if running.
+ * \retval 0 if not running (or error).
+ */
+bool pid_running(pid_t pid);
+
+/*!
+ * \brief Update process privileges to new UID/GID.
+ *
+ * \param uid New user ID.
+ * \param gid New group ID.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_ERROR if UID or GID change failed.
+ */
+int proc_update_privileges(int uid, int gid);
diff --git a/src/knot/common/stats.c b/src/knot/common/stats.c
new file mode 100644
index 0000000..2b8cb09
--- /dev/null
+++ b/src/knot/common/stats.c
@@ -0,0 +1,309 @@
+/* Copyright (C) 2021 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 <inttypes.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include "contrib/files.h"
+#include "knot/common/stats.h"
+#include "knot/common/log.h"
+#include "knot/nameserver/query_module.h"
+
+struct {
+ bool active_dumper;
+ pthread_t dumper;
+ uint32_t timer;
+ server_t *server;
+} stats = { 0 };
+
+typedef struct {
+ FILE *fd;
+ const list_t *query_modules;
+ const knot_dname_t *zone;
+ bool zone_emitted;
+} dump_ctx_t;
+
+#define DUMP_STR(fd, level, name, ...) do { \
+ fprintf(fd, "%-.*s"name": %s\n", level, " ", ##__VA_ARGS__); \
+ } while (0)
+#define DUMP_CTR(fd, level, name, ...) do { \
+ fprintf(fd, "%-.*s"name": %"PRIu64"\n", level, " ", ##__VA_ARGS__); \
+ } while (0)
+
+uint64_t server_zone_count(server_t *server)
+{
+ return knot_zonedb_size(server->zone_db);
+}
+
+const stats_item_t server_stats[] = {
+ { "zone-count", server_zone_count },
+ { 0 }
+};
+
+uint64_t stats_get_counter(uint64_t **stats_vals, uint32_t offset, unsigned threads)
+{
+ uint64_t res = 0;
+ for (unsigned i = 0; i < threads; i++) {
+ res += ATOMIC_GET(stats_vals[i][offset]);
+ }
+ return res;
+}
+
+static void dump_counters(FILE *fd, int level, mod_ctr_t *ctr, uint64_t **stats_vals, unsigned threads)
+{
+ for (uint32_t j = 0; j < ctr->count; j++) {
+ uint64_t counter = stats_get_counter(stats_vals, ctr->offset + j, threads);
+
+ // Skip empty counters.
+ if (counter == 0) {
+ continue;
+ }
+
+ if (ctr->idx_to_str != NULL) {
+ char *str = ctr->idx_to_str(j, ctr->count);
+ if (str != NULL) {
+ DUMP_CTR(fd, level, "%s", str, counter);
+ free(str);
+ }
+ } else {
+ DUMP_CTR(fd, level, "%u", j, counter);
+ }
+ }
+}
+
+static void dump_modules(dump_ctx_t *ctx)
+{
+ int level = 0;
+ knotd_mod_t *mod;
+ WALK_LIST(mod, *ctx->query_modules) {
+ // Skip modules without statistics.
+ if (mod->stats_count == 0) {
+ continue;
+ }
+
+ // Dump zone name.
+ if (ctx->zone != NULL) {
+ // Prevent from zone section override.
+ if (!ctx->zone_emitted) {
+ DUMP_STR(ctx->fd, 0, "zone", "");
+ ctx->zone_emitted = true;
+ }
+ level = 1;
+
+ knot_dname_txt_storage_t name;
+ if (knot_dname_to_str(name, ctx->zone, sizeof(name)) == NULL) {
+ return;
+ }
+ DUMP_STR(ctx->fd, level++, "\"%s\"", name, "");
+ } else {
+ level = 0;
+ }
+
+ unsigned threads = knotd_mod_threads(mod);
+
+ // Dump module counters.
+ DUMP_STR(ctx->fd, level, "%s", mod->id->name + 1, "");
+ for (int i = 0; i < mod->stats_count; i++) {
+ mod_ctr_t *ctr = mod->stats_info + i;
+ if (ctr->name == NULL) {
+ // Empty counter.
+ continue;
+ }
+ if (ctr->count == 1) {
+ // Simple counter.
+ uint64_t counter = stats_get_counter(mod->stats_vals,
+ ctr->offset, threads);
+ DUMP_CTR(ctx->fd, level + 1, "%s", ctr->name, counter);
+ } else {
+ // Array of counters.
+ DUMP_STR(ctx->fd, level + 1, "%s", ctr->name, "");
+ dump_counters(ctx->fd, level + 2, ctr, mod->stats_vals, threads);
+ }
+ }
+ }
+}
+
+static void zone_stats_dump(zone_t *zone, dump_ctx_t *ctx)
+{
+ if (EMPTY_LIST(zone->query_modules)) {
+ return;
+ }
+
+ ctx->query_modules = &zone->query_modules;
+ ctx->zone = zone->name;
+
+ dump_modules(ctx);
+}
+
+static void dump_to_file(FILE *fd, server_t *server)
+{
+ char date[64] = "";
+
+ // Get formatted current time string.
+ struct tm tm;
+ time_t now = time(NULL);
+ localtime_r(&now, &tm);
+ strftime(date, sizeof(date), KNOT_LOG_TIME_FORMAT, &tm);
+
+ // Get the server identity.
+ conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
+ const char *ident = conf_str(&val);
+ if (ident == NULL || ident[0] == '\0') {
+ ident = conf()->hostname;
+ }
+
+ // Dump record header.
+ fprintf(fd,
+ "---\n"
+ "time: %s\n"
+ "identity: %s\n",
+ date, ident);
+
+ // Dump server statistics.
+ DUMP_STR(fd, 0, "server", "");
+ for (const stats_item_t *item = server_stats; item->name != NULL; item++) {
+ DUMP_CTR(fd, 1, "%s", item->name, item->val(server));
+ }
+
+ dump_ctx_t ctx = {
+ .fd = fd,
+ .query_modules = conf()->query_modules,
+ };
+
+ // Dump global statistics.
+ dump_modules(&ctx);
+
+ // Dump zone statistics.
+ knot_zonedb_foreach(server->zone_db, zone_stats_dump, &ctx);
+}
+
+static void dump_stats(server_t *server)
+{
+ conf_t *pconf = conf();
+ conf_val_t val = conf_get(pconf, C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&val, NULL);
+ val = conf_get(pconf, C_STATS, C_FILE);
+ char *file_name = conf_abs_path(&val, rundir);
+ free(rundir);
+
+ val = conf_get(pconf, C_STATS, C_APPEND);
+ bool append = conf_bool(&val);
+
+ // Open or create output file.
+ FILE *fd = NULL;
+ char *tmp_name = NULL;
+ if (append) {
+ fd = fopen(file_name, "a");
+ if (fd == NULL) {
+ log_error("stats, failed to append file '%s' (%s)",
+ file_name, knot_strerror(knot_map_errno()));
+ free(file_name);
+ return;
+ }
+ } else {
+ int ret = open_tmp_file(file_name, &tmp_name, &fd,
+ S_IRUSR | S_IWUSR | S_IRGRP);
+ if (ret != KNOT_EOK) {
+ log_error("stats, failed to open file '%s' (%s)",
+ file_name, knot_strerror(ret));
+ free(file_name);
+ return;
+ }
+ }
+ assert(fd);
+
+ // Dump stats into the file.
+ dump_to_file(fd, server);
+
+ fflush(fd);
+ fclose(fd);
+
+ // Switch the file contents.
+ if (!append) {
+ int ret = rename(tmp_name, file_name);
+ if (ret != 0) {
+ log_error("stats, failed to access file '%s' (%s)",
+ file_name, knot_strerror(knot_map_errno()));
+ unlink(tmp_name);
+ }
+ free(tmp_name);
+ }
+
+ log_debug("stats, dumped into file '%s'", file_name);
+ free(file_name);
+}
+
+static void *dumper(void *data)
+{
+ rcu_register_thread();
+ while (true) {
+ assert(stats.timer > 0);
+ sleep(stats.timer);
+
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ rcu_read_lock();
+ dump_stats(stats.server);
+ rcu_read_unlock();
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ }
+ rcu_unregister_thread();
+ return NULL;
+}
+
+void stats_reconfigure(conf_t *conf, server_t *server)
+{
+ if (conf == NULL || server == NULL) {
+ return;
+ }
+
+ // Update server context.
+ stats.server = server;
+
+ conf_val_t val = conf_get(conf, C_STATS, C_TIMER);
+ stats.timer = conf_int(&val);
+ if (stats.timer > 0) {
+ // Check if dumping is already running.
+ if (stats.active_dumper) {
+ return;
+ }
+
+ int ret = pthread_create(&stats.dumper, NULL, dumper, NULL);
+ if (ret != 0) {
+ log_error("stats, failed to launch periodic dumping (%s)",
+ knot_strerror(knot_map_errno_code(ret)));
+ } else {
+ stats.active_dumper = true;
+ }
+ // Stop current dumping.
+ } else if (stats.active_dumper) {
+ pthread_cancel(stats.dumper);
+ pthread_join(stats.dumper, NULL);
+ stats.active_dumper = false;
+ }
+}
+
+void stats_deinit(void)
+{
+ if (stats.active_dumper) {
+ pthread_cancel(stats.dumper);
+ pthread_join(stats.dumper, NULL);
+ }
+
+ memset(&stats, 0, sizeof(stats));
+}
diff --git a/src/knot/common/stats.h b/src/knot/common/stats.h
new file mode 100644
index 0000000..bd6df6d
--- /dev/null
+++ b/src/knot/common/stats.h
@@ -0,0 +1,53 @@
+/* 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 Server statistics general API.
+ */
+
+#pragma once
+
+#include "knot/server/server.h"
+
+typedef uint64_t (*stats_val_f)(server_t *server);
+
+/*!
+ * \brief Statistics metrics item.
+ */
+typedef struct {
+ const char *name; /*!< Metrics name. */
+ stats_val_f val; /*!< Metrics value getter. */
+} stats_item_t;
+
+/*!
+ * \brief Basic server metrics.
+ */
+extern const stats_item_t server_stats[];
+
+/*!
+ * \brief Read out value of single counter summed across threads.
+ */
+uint64_t stats_get_counter(uint64_t **stats_vals, uint32_t offset, unsigned threads);
+
+/*!
+ * \brief Reconfigures the statistics facility.
+ */
+void stats_reconfigure(conf_t *conf, server_t *server);
+
+/*!
+ * \brief Deinitializes the statistics facility.
+ */
+void stats_deinit(void);
diff --git a/src/knot/common/systemd.c b/src/knot/common/systemd.c
new file mode 100644
index 0000000..13c83e6
--- /dev/null
+++ b/src/knot/common/systemd.c
@@ -0,0 +1,168 @@
+/* Copyright (C) 2022 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "knot/common/systemd.h"
+#include "contrib/strtonum.h"
+
+#ifdef ENABLE_SYSTEMD
+#include <systemd/sd-daemon.h>
+
+#define ZONE_LOAD_TIMEOUT_DEFAULT 60
+
+static int zone_load_timeout_s;
+
+static int systemd_zone_load_timeout(void)
+{
+ const char *timeout = getenv("KNOT_ZONE_LOAD_TIMEOUT_SEC");
+
+ int out;
+ if (timeout != NULL && timeout[0] != '\0' &&
+ str_to_int(timeout, &out, 0, 24 * 3600) == KNOT_EOK) {
+ return out;
+ } else {
+ return ZONE_LOAD_TIMEOUT_DEFAULT;
+ }
+}
+#endif
+
+#ifdef ENABLE_DBUS
+#include <systemd/sd-bus.h>
+
+static sd_bus *_dbus = NULL;
+#endif
+
+void systemd_zone_load_timeout_notify(void)
+{
+#ifdef ENABLE_SYSTEMD
+ if (zone_load_timeout_s == 0) {
+ zone_load_timeout_s = systemd_zone_load_timeout();
+ }
+ sd_notifyf(0, "EXTEND_TIMEOUT_USEC=%d000000", zone_load_timeout_s);
+#endif
+}
+
+void systemd_tasks_status_notify(int tasks)
+{
+#ifdef ENABLE_SYSTEMD
+ if (tasks > 0) {
+ sd_notifyf(0, "STATUS=Waiting for %d tasks to finish...", tasks);
+ } else {
+ sd_notify(0, "STATUS=");
+ }
+#endif
+}
+
+void systemd_ready_notify(void)
+{
+#ifdef ENABLE_SYSTEMD
+ sd_notify(0, "READY=1\nSTATUS=");
+#endif
+}
+
+void systemd_reloading_notify(void)
+{
+#ifdef ENABLE_SYSTEMD
+ sd_notify(0, "RELOADING=1\nSTATUS=");
+#endif
+}
+
+void systemd_stopping_notify(void)
+{
+#ifdef ENABLE_SYSTEMD
+ sd_notify(0, "STOPPING=1\nSTATUS=");
+#endif
+}
+
+int systemd_dbus_open(void)
+{
+#ifdef ENABLE_DBUS
+ if (_dbus != NULL) {
+ return KNOT_EOK;
+ }
+
+ int ret = sd_bus_open_system(&_dbus);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Take a well-known service name so that clients can find us. */
+ ret = sd_bus_request_name(_dbus, KNOT_DBUS_NAME, 0);
+ if (ret < 0) {
+ systemd_dbus_close();
+ return ret;
+ }
+
+ return KNOT_EOK;
+#else
+ return KNOT_ENOTSUP;
+#endif
+}
+
+void systemd_dbus_close(void)
+{
+#ifdef ENABLE_DBUS
+ _dbus = sd_bus_unref(_dbus);
+#endif
+}
+
+#define emit_event(event, ...) \
+ sd_bus_emit_signal(_dbus, KNOT_DBUS_PATH, KNOT_DBUS_NAME".events", \
+ event, __VA_ARGS__)
+
+void systemd_emit_running(bool up)
+{
+#ifdef ENABLE_DBUS
+ emit_event(up ? KNOT_BUS_EVENT_STARTED : KNOT_BUS_EVENT_STOPPED, "");
+#endif
+}
+
+void systemd_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial)
+{
+#ifdef ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_UPD, "su", zone_str, serial);
+ }
+#endif
+}
+
+void systemd_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
+ const char *keyid)
+{
+#ifdef ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_KSK_SUBM, "sqs", zone_str, keytag, keyid);
+ }
+#endif
+}
+
+void systemd_emit_zone_invalid(const knot_dname_t *zone_name)
+{
+#ifdef ENABLE_DBUS
+ knot_dname_txt_storage_t buff;
+ char *zone_str = knot_dname_to_str(buff, zone_name, sizeof(buff));
+ if (zone_str != NULL) {
+ emit_event(KNOT_BUS_EVENT_ZONE_INVALID, "s", zone_str);
+ }
+#endif
+}
diff --git a/src/knot/common/systemd.h b/src/knot/common/systemd.h
new file mode 100644
index 0000000..1cefd9c
--- /dev/null
+++ b/src/knot/common/systemd.h
@@ -0,0 +1,105 @@
+/* Copyright (C) 2022 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 Systemd API wrappers.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+
+#define KNOT_DBUS_NAME "cz.nic.knotd"
+#define KNOT_DBUS_PATH "/cz/nic/knotd"
+
+#define KNOT_BUS_EVENT_STARTED "started"
+#define KNOT_BUS_EVENT_STOPPED "stopped"
+#define KNOT_BUS_EVENT_ZONE_UPD "zone_updated"
+#define KNOT_BUS_EVENT_ZONE_KSK_SUBM "zone_ksk_submission"
+#define KNOT_BUS_EVENT_ZONE_INVALID "zone_dnssec_invalid"
+
+/*!
+ * \brief Notify systemd about zone loading start.
+ */
+void systemd_zone_load_timeout_notify(void);
+
+/*!
+ * \brief Update systemd service status with information about number
+ * of scheduled tasks.
+ *
+ * \param tasks Number of tasks to be done.
+ */
+void systemd_tasks_status_notify(int tasks);
+
+/*!
+ * \brief Notify systemd about service is ready.
+ */
+void systemd_ready_notify(void);
+
+/*!
+ * \brief Notify systemd about service is reloading.
+ */
+void systemd_reloading_notify(void);
+
+/*!
+ * \brief Notify systemd about service is stopping.
+ */
+void systemd_stopping_notify(void);
+
+/*!
+ * \brief Creates unique D-Bus sender reference (common for whole process).
+ *
+ * \retval KNOT_EOK on successful create of reference.
+ * \retval Negative value on error.
+ */
+int systemd_dbus_open(void);
+
+/*!
+ * \brief Closes D-Bus.
+ */
+void systemd_dbus_close(void);
+
+/*!
+ * \brief Emit event signal for started daemon.
+ *
+ * \param up Indication if the server has been started.
+ */
+void systemd_emit_running(bool up);
+
+/*!
+ * \brief Emit event signal for updated zones.
+ *
+ * \param zone_name Zone name.
+ * \param serial Current zone SOA serial.
+ */
+void systemd_emit_zone_updated(const knot_dname_t *zone_name, uint32_t serial);
+
+/*!
+ * \brief Emit event signal for KSK submission.
+ *
+ * \param zone_name Zone name.
+ * \param keytag Keytag of the ready key.
+ * \param keyid KASP id of the ready key.
+ */
+void systemd_emit_zone_submission(const knot_dname_t *zone_name, uint16_t keytag,
+ const char *keyid);
+
+/*!
+ * \brief Emit event signal for failed DNSSEC validation.
+ *
+ * \param zone_name Zone name.
+ */
+void systemd_emit_zone_invalid(const knot_dname_t *zone_name);
diff --git a/src/knot/common/unreachable.c b/src/knot/common/unreachable.c
new file mode 100644
index 0000000..e137f3d
--- /dev/null
+++ b/src/knot/common/unreachable.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "unreachable.h"
+
+knot_unreachables_t *global_unreachables = NULL;
+
+static uint32_t get_timestamp(void)
+{
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ uint64_t res = (uint64_t)t.tv_sec * 1000;
+ res += (uint64_t)t.tv_nsec / 1000000;
+ return res & 0xffffffff; // overflow does not matter since we are working with differences
+}
+
+knot_unreachables_t *knot_unreachables_init(uint32_t ttl_ms)
+{
+ knot_unreachables_t *res = calloc(1, sizeof(*res));
+ if (res != NULL) {
+ pthread_mutex_init(&res->mutex, NULL);
+ res->ttl_ms = ttl_ms;
+ init_list(&res->urs);
+ }
+ return res;
+}
+
+uint32_t knot_unreachables_ttl(knot_unreachables_t *urs, uint32_t new_ttl_ms)
+{
+ if (urs == NULL) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&urs->mutex);
+
+ uint32_t prev = urs->ttl_ms;
+ urs->ttl_ms = new_ttl_ms;
+
+ pthread_mutex_unlock(&urs->mutex);
+
+ return prev;
+}
+
+void knot_unreachables_deinit(knot_unreachables_t **urs)
+{
+ if (urs != NULL && *urs != NULL) {
+ knot_unreachable_t *ur, *nxt;
+ WALK_LIST_DELSAFE(ur, nxt, (*urs)->urs) {
+ rem_node((node_t *)ur);
+ free(ur);
+ }
+ pthread_mutex_destroy(&(*urs)->mutex);
+ free(*urs);
+ *urs = NULL;
+ }
+}
+
+static bool clear_old(knot_unreachable_t *ur, uint32_t now, uint32_t ttl_ms)
+{
+ if (ur->time_ms != 0 && now - ur->time_ms > ttl_ms) {
+ rem_node((node_t *)ur);
+ free(ur);
+ return true;
+ }
+ return false;
+}
+
+// also clears up (some) expired unreachables
+static knot_unreachable_t *get_ur(knot_unreachables_t *urs,
+ const struct sockaddr_storage *addr,
+ const struct sockaddr_storage *via)
+{
+ assert(urs != NULL);
+
+ uint32_t now = get_timestamp();
+ knot_unreachable_t *ur, *nxt;
+ WALK_LIST_DELSAFE(ur, nxt, urs->urs) {
+ if (clear_old(ur, now, urs->ttl_ms)) {
+ continue;
+ }
+
+ if (sockaddr_cmp(&ur->addr, addr, false) == 0 &&
+ sockaddr_cmp(&ur->via, via, true) == 0) {
+ return ur;
+ }
+ }
+
+ return NULL;
+}
+
+bool knot_unreachable_is(knot_unreachables_t *urs,
+ const struct sockaddr_storage *addr,
+ const struct sockaddr_storage *via)
+{
+ if (urs == NULL) {
+ return false;
+ }
+ assert(addr);
+ assert(via);
+
+ pthread_mutex_lock(&urs->mutex);
+
+ bool res = (get_ur(urs, addr, via) != NULL);
+
+ pthread_mutex_unlock(&urs->mutex);
+
+ return res;
+}
+
+void knot_unreachable_add(knot_unreachables_t *urs,
+ const struct sockaddr_storage *addr,
+ const struct sockaddr_storage *via)
+{
+ if (urs == NULL) {
+ return;
+ }
+ assert(addr);
+ assert(via);
+
+ pthread_mutex_lock(&urs->mutex);
+
+ knot_unreachable_t *ur = malloc(sizeof(*ur));
+ if (ur != NULL) {
+ memcpy(&ur->addr, addr, sizeof(ur->addr));
+ memcpy(&ur->via, via, sizeof(ur->via));
+ ur->time_ms = get_timestamp();
+ add_head(&urs->urs, (node_t *)ur);
+ }
+
+ pthread_mutex_unlock(&urs->mutex);
+}
diff --git a/src/knot/common/unreachable.h b/src/knot/common/unreachable.h
new file mode 100644
index 0000000..40094f9
--- /dev/null
+++ b/src/knot/common/unreachable.h
@@ -0,0 +1,87 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/lists.h"
+
+typedef struct {
+ node_t n;
+ struct sockaddr_storage addr;
+ struct sockaddr_storage via;
+ uint32_t time_ms;
+} knot_unreachable_t;
+
+typedef struct {
+ pthread_mutex_t mutex;
+ uint32_t ttl_ms;
+ list_t urs;
+} knot_unreachables_t;
+
+extern knot_unreachables_t *global_unreachables;
+
+/*!
+ * \brief Allocate Unreachables structure.
+ *
+ * \param ttl TTL for unreachable in milliseconds.
+ *
+ * \return Allocated structure, or NULL.
+ */
+knot_unreachables_t *knot_unreachables_init(uint32_t ttl_ms);
+
+/*!
+ * \brief Free Unreachables structure.
+ */
+void knot_unreachables_deinit(knot_unreachables_t **urs);
+
+/*!
+ * \brief Get and/or set the TTL.
+ *
+ * \param urs Unreachables structure.
+ * \param new_ttl_ms New TTL value in milliseconds.
+ *
+ * \return Previous value of TTL.
+ */
+uint32_t knot_unreachables_ttl(knot_unreachables_t *urs, uint32_t new_ttl_ms);
+
+/*!
+ * \brief Determine if given address is unreachable.
+ *
+ * \param urs Unreachables structure.
+ * \param addr Address and port in question.
+ * \param via Local outgoing address.
+ *
+ * \return True iff unreachable within TTL.
+ */
+bool knot_unreachable_is(knot_unreachables_t *urs,
+ const struct sockaddr_storage *addr,
+ const struct sockaddr_storage *via);
+
+/*!
+ * \brief Add an unreachable into Unreachables structure.
+ *
+ * \param urs Unreachables structure.
+ * \param addr Address and port being unreachable.
+ * \param via Local outgoing address.
+ */
+void knot_unreachable_add(knot_unreachables_t *urs,
+ const struct sockaddr_storage *addr,
+ const struct sockaddr_storage *via);
diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c
new file mode 100644
index 0000000..1670929
--- /dev/null
+++ b/src/knot/conf/base.c
@@ -0,0 +1,1056 @@
+/* Copyright (C) 2022 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 <string.h>
+#include <urcu.h>
+
+#include "knot/conf/base.h"
+#include "knot/conf/confdb.h"
+#include "knot/conf/module.h"
+#include "knot/conf/tools.h"
+#include "knot/common/log.h"
+#include "knot/nameserver/query_module.h"
+#include "libknot/libknot.h"
+#include "libknot/yparser/ypformat.h"
+#include "libknot/yparser/yptrafo.h"
+#include "contrib/files.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+
+// The active configuration.
+conf_t *s_conf;
+
+conf_t* conf(void) {
+ return s_conf;
+}
+
+static int init_and_check(
+ conf_t *conf,
+ conf_flag_t flags)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_db_txn_t txn;
+ unsigned txn_flags = (flags & CONF_FREADONLY) ? KNOT_DB_RDONLY : 0;
+ int ret = conf->api->txn_begin(conf->db, &txn, txn_flags);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Initialize the database.
+ if (!(flags & CONF_FREADONLY)) {
+ ret = conf_db_init(conf, &txn, false);
+ if (ret != KNOT_EOK) {
+ conf->api->txn_abort(&txn);
+ return ret;
+ }
+ }
+
+ // Check the database.
+ if (!(flags & CONF_FNOCHECK)) {
+ ret = conf_db_check(conf, &txn);
+ if (ret < KNOT_EOK) {
+ conf->api->txn_abort(&txn);
+ return ret;
+ }
+ }
+
+ if (flags & CONF_FREADONLY) {
+ conf->api->txn_abort(&txn);
+ return KNOT_EOK;
+ } else {
+ return conf->api->txn_commit(&txn);
+ }
+}
+
+int conf_refresh_txn(
+ conf_t *conf)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Close previously opened transaction.
+ conf->api->txn_abort(&conf->read_txn);
+
+ return conf->api->txn_begin(conf->db, &conf->read_txn, KNOT_DB_RDONLY);
+}
+
+static void refresh_hostname(
+ conf_t *conf)
+{
+ if (conf == NULL) {
+ return;
+ }
+
+ free(conf->hostname);
+ conf->hostname = sockaddr_hostname();
+ if (conf->hostname == NULL) {
+ // Empty hostname fallback, NULL cannot be passed to strlen!
+ conf->hostname = strdup("");
+ }
+}
+
+static int infinite_adjust(
+ int timeout)
+{
+ return (timeout > 0) ? timeout : -1;
+}
+
+static void init_cache(
+ conf_t *conf,
+ bool reinit_cache)
+{
+ /*
+ * For UDP, TCP, XDP, and background workers, cache the number of running
+ * workers. Cache the setting of TCP reuseport too. These values
+ * can't change in runtime, while config data can.
+ */
+
+ static bool first_init = true;
+ static bool running_tcp_reuseport;
+ static bool running_socket_affinity;
+ static bool running_xdp_udp;
+ static bool running_xdp_tcp;
+ static uint16_t running_xdp_quic;
+ static bool running_route_check;
+ static size_t running_udp_threads;
+ static size_t running_tcp_threads;
+ static size_t running_xdp_threads;
+ static size_t running_bg_threads;
+ static size_t running_quic_clients;
+ static size_t running_quic_outbufs;
+ static size_t running_quic_idle;
+
+ if (first_init || reinit_cache) {
+ running_tcp_reuseport = conf_get_bool(conf, C_SRV, C_TCP_REUSEPORT);
+ running_socket_affinity = conf_get_bool(conf, C_SRV, C_SOCKET_AFFINITY);
+ running_xdp_udp = conf_get_bool(conf, C_XDP, C_UDP);
+ running_xdp_tcp = conf_get_bool(conf, C_XDP, C_TCP);
+ running_xdp_quic = 0;
+ if (conf_get_bool(conf, C_XDP, C_QUIC)) {
+ running_xdp_quic = conf_get_int(conf, C_XDP, C_QUIC_PORT);
+ }
+ running_route_check = conf_get_bool(conf, C_XDP, C_ROUTE_CHECK);
+ running_udp_threads = conf_udp_threads(conf);
+ running_tcp_threads = conf_tcp_threads(conf);
+ running_xdp_threads = conf_xdp_threads(conf);
+ running_bg_threads = conf_bg_threads(conf);
+ running_quic_clients = conf_get_int(conf, C_SRV, C_QUIC_MAX_CLIENTS);
+ running_quic_outbufs = conf_get_int(conf, C_SRV, C_QUIC_OUTBUF_MAX_SIZE);
+ running_quic_idle = conf_get_int(conf, C_SRV, C_QUIC_IDLE_CLOSE);
+
+ first_init = false;
+ }
+
+ conf_val_t val = conf_get(conf, C_SRV, C_UDP_MAX_PAYLOAD_IPV4);
+ if (val.code != KNOT_EOK) {
+ val = conf_get(conf, C_SRV, C_UDP_MAX_PAYLOAD);
+ }
+ conf->cache.srv_udp_max_payload_ipv4 = conf_int(&val);
+
+ val = conf_get(conf, C_SRV, C_UDP_MAX_PAYLOAD_IPV6);
+ if (val.code != KNOT_EOK) {
+ val = conf_get(conf, C_SRV, C_UDP_MAX_PAYLOAD);
+ }
+ conf->cache.srv_udp_max_payload_ipv6 = conf_int(&val);
+
+ val = conf_get(conf, C_SRV, C_TCP_IDLE_TIMEOUT);
+ conf->cache.srv_tcp_idle_timeout = conf_int(&val);
+
+ val = conf_get(conf, C_SRV, C_TCP_IO_TIMEOUT);
+ conf->cache.srv_tcp_io_timeout = infinite_adjust(conf_int(&val));
+
+ val = conf_get(conf, C_SRV, C_TCP_RMT_IO_TIMEOUT);
+ conf->cache.srv_tcp_remote_io_timeout = infinite_adjust(conf_int(&val));
+
+ val = conf_get(conf, C_SRV, C_TCP_FASTOPEN);
+ conf->cache.srv_tcp_fastopen = conf_bool(&val);
+
+ conf->cache.srv_quic_max_clients = running_quic_clients;
+
+ conf->cache.srv_quic_idle_close = running_quic_idle;
+
+ conf->cache.srv_quic_obuf_max_size = running_quic_outbufs;
+
+ conf->cache.srv_tcp_reuseport = running_tcp_reuseport;
+
+ conf->cache.srv_socket_affinity = running_socket_affinity;
+
+ val = conf_get(conf, C_SRV, C_DBUS_EVENT);
+ while (val.code == KNOT_EOK) {
+ conf->cache.srv_dbus_event |= conf_opt(&val);
+ conf_val_next(&val);
+ }
+
+ conf->cache.srv_udp_threads = running_udp_threads;
+
+ conf->cache.srv_tcp_threads = running_tcp_threads;
+
+ conf->cache.srv_xdp_threads = running_xdp_threads;
+
+ conf->cache.srv_bg_threads = running_bg_threads;
+
+ conf->cache.srv_tcp_max_clients = conf_tcp_max_clients(conf);
+
+ val = conf_get(conf, C_XDP, C_TCP_MAX_CLIENTS);
+ conf->cache.xdp_tcp_max_clients = conf_int(&val);
+
+ val = conf_get(conf, C_XDP, C_TCP_INBUF_MAX_SIZE);
+ conf->cache.xdp_tcp_inbuf_max_size = conf_int(&val);
+
+ val = conf_get(conf, C_XDP, C_TCP_OUTBUF_MAX_SIZE);
+ conf->cache.xdp_tcp_outbuf_max_size = conf_int(&val);
+
+ val = conf_get(conf, C_XDP, C_TCP_IDLE_CLOSE);
+ conf->cache.xdp_tcp_idle_close = conf_int(&val);
+
+ val = conf_get(conf, C_XDP, C_TCP_IDLE_RESET);
+ conf->cache.xdp_tcp_idle_reset = conf_int(&val);
+
+ val = conf_get(conf, C_XDP, C_TCP_RESEND);
+ conf->cache.xdp_tcp_idle_resend = conf_int(&val);
+
+ conf->cache.xdp_udp = running_xdp_udp;
+
+ conf->cache.xdp_tcp = running_xdp_tcp;
+
+ conf->cache.xdp_quic = running_xdp_quic;
+
+ conf->cache.xdp_route_check = running_route_check;
+
+ val = conf_get(conf, C_CTL, C_TIMEOUT);
+ conf->cache.ctl_timeout = conf_int(&val) * 1000;
+ /* infinite_adjust() call isn't needed, 0 is adjusted later anyway. */
+
+ val = conf_get(conf, C_SRV, C_NSID);
+ if (val.code != KNOT_EOK) {
+ if (conf->hostname == NULL) {
+ conf->cache.srv_nsid_data = (const uint8_t *)"";
+ conf->cache.srv_nsid_len = 0;
+ } else {
+ conf->cache.srv_nsid_data = (const uint8_t *)conf->hostname;
+ conf->cache.srv_nsid_len = strlen(conf->hostname);
+ }
+ } else {
+ conf->cache.srv_nsid_data = conf_bin(&val, &conf->cache.srv_nsid_len);
+ }
+
+ val = conf_get(conf, C_SRV, C_ECS);
+ conf->cache.srv_ecs = conf_bool(&val);
+
+ val = conf_get(conf, C_SRV, C_ANS_ROTATION);
+ conf->cache.srv_ans_rotate = conf_bool(&val);
+
+ val = conf_get(conf, C_SRV, C_AUTO_ACL);
+ conf->cache.srv_auto_acl = conf_bool(&val);
+
+ val = conf_get(conf, C_SRV, C_PROXY_ALLOWLIST);
+ conf->cache.srv_proxy_enabled = (conf_val_count(&val) > 0);
+}
+
+int conf_new(
+ conf_t **conf,
+ const yp_item_t *schema,
+ const char *db_dir,
+ size_t max_conf_size,
+ conf_flag_t flags)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ conf_t *out = malloc(sizeof(conf_t));
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, sizeof(conf_t));
+
+ // Initialize config schema.
+ int ret = yp_schema_copy(&out->schema, schema);
+ if (ret != KNOT_EOK) {
+ goto new_error;
+ }
+
+ // Initialize query modules list.
+ out->query_modules = malloc(sizeof(list_t));
+ if (out->query_modules == NULL) {
+ ret = KNOT_ENOMEM;
+ goto new_error;
+ }
+ init_list(out->query_modules);
+
+ // Set the DB api.
+ out->mapsize = max_conf_size;
+ out->api = knot_db_lmdb_api();
+ struct knot_db_lmdb_opts lmdb_opts = KNOT_DB_LMDB_OPTS_INITIALIZER;
+ lmdb_opts.mapsize = out->mapsize;
+ lmdb_opts.maxreaders = CONF_MAX_DB_READERS;
+ lmdb_opts.flags.env = KNOT_DB_LMDB_NOTLS;
+
+ // Open the database.
+ if (db_dir == NULL) {
+ // Prepare a temporary database.
+ char tpl[] = "/tmp/knot-confdb.XXXXXX";
+ lmdb_opts.path = mkdtemp(tpl);
+ if (lmdb_opts.path == NULL) {
+ CONF_LOG(LOG_ERR, "failed to create temporary directory (%s)",
+ knot_strerror(knot_map_errno()));
+ ret = KNOT_ENOMEM;
+ goto new_error;
+ }
+
+ ret = out->api->init(&out->db, NULL, &lmdb_opts);
+
+ // Remove the database to ensure it is temporary.
+ if (!remove_path(lmdb_opts.path)) {
+ CONF_LOG(LOG_WARNING, "failed to purge temporary directory '%s'",
+ lmdb_opts.path);
+ }
+ } else {
+ // Set the specified database.
+ lmdb_opts.path = db_dir;
+
+ // Set the read-only mode.
+ if (flags & CONF_FREADONLY) {
+ lmdb_opts.flags.env |= KNOT_DB_LMDB_RDONLY;
+ }
+
+ ret = out->api->init(&out->db, NULL, &lmdb_opts);
+ }
+ if (ret != KNOT_EOK) {
+ goto new_error;
+ }
+
+ // Initialize and check the database.
+ ret = init_and_check(out, flags);
+ if (ret != KNOT_EOK) {
+ goto new_error;
+ }
+
+ // Open common read-only transaction.
+ ret = conf_refresh_txn(out);
+ if (ret != KNOT_EOK) {
+ goto new_error;
+ }
+
+ // Cache the current hostname.
+ if (!(flags & CONF_FNOHOSTNAME)) {
+ refresh_hostname(out);
+ }
+
+ // Initialize cached values.
+ init_cache(out, false);
+
+ // Load module schemas.
+ if (flags & (CONF_FREQMODULES | CONF_FOPTMODULES)) {
+ ret = conf_mod_load_common(out);
+ if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) {
+ goto new_error;
+ }
+
+ for (conf_iter_t iter = conf_iter(out, C_MODULE);
+ iter.code == KNOT_EOK; conf_iter_next(out, &iter)) {
+ conf_val_t id = conf_iter_id(out, &iter);
+ conf_val_t file = conf_id_get(out, C_MODULE, C_FILE, &id);
+ ret = conf_mod_load_extra(out, conf_str(&id), conf_str(&file),
+ MOD_EXPLICIT);
+ if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) {
+ conf_iter_finish(out, &iter);
+ goto new_error;
+ }
+ }
+
+ conf_mod_load_purge(out, false);
+ }
+
+ *conf = out;
+
+ return KNOT_EOK;
+new_error:
+ conf_free(out);
+
+ return ret;
+}
+
+int conf_clone(
+ conf_t **conf)
+{
+ if (conf == NULL || s_conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ conf_t *out = malloc(sizeof(conf_t));
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, sizeof(conf_t));
+
+ // Initialize config schema.
+ int ret = yp_schema_copy(&out->schema, s_conf->schema);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ // Set shared items.
+ out->api = s_conf->api;
+ out->db = s_conf->db;
+
+ // Initialize query modules list.
+ out->query_modules = malloc(sizeof(list_t));
+ if (out->query_modules == NULL) {
+ yp_schema_free(out->schema);
+ free(out);
+ return KNOT_ENOMEM;
+ }
+ init_list(out->query_modules);
+
+ // Open common read-only transaction.
+ ret = conf_refresh_txn(out);
+ if (ret != KNOT_EOK) {
+ free(out->query_modules);
+ yp_schema_free(out->schema);
+ free(out);
+ return ret;
+ }
+
+ // Copy the filename.
+ if (s_conf->filename != NULL) {
+ out->filename = strdup(s_conf->filename);
+ }
+
+ // Copy the hostname.
+ if (s_conf->hostname != NULL) {
+ out->hostname = strdup(s_conf->hostname);
+ }
+
+ out->catalog = s_conf->catalog;
+
+ // Initialize cached values.
+ init_cache(out, false);
+
+ out->is_clone = true;
+
+ *conf = out;
+
+ return KNOT_EOK;
+}
+
+conf_t *conf_update(
+ conf_t *conf,
+ conf_update_flag_t flags)
+{
+ // Remove the clone flag for new master configuration.
+ if (conf != NULL) {
+ conf->is_clone = false;
+
+ if ((flags & CONF_UPD_FCONFIO) && s_conf != NULL) {
+ conf->io.flags = s_conf->io.flags;
+ conf->io.zones = s_conf->io.zones;
+ }
+ if ((flags & CONF_UPD_FMODULES) && s_conf != NULL) {
+ free(conf->query_modules);
+ conf->query_modules = s_conf->query_modules;
+ conf->query_plan = s_conf->query_plan;
+ }
+ }
+
+ conf_t **current_conf = &s_conf;
+ conf_t *old_conf = rcu_xchg_pointer(current_conf, conf);
+
+ synchronize_rcu();
+
+ if (old_conf != NULL) {
+ // Remove the clone flag if a single configuration.
+ old_conf->is_clone = (conf != NULL) ? true : false;
+
+ if (flags & CONF_UPD_FCONFIO) {
+ old_conf->io.zones = NULL;
+ }
+ if (flags & CONF_UPD_FMODULES) {
+ old_conf->query_modules = NULL;
+ old_conf->query_plan = NULL;
+ }
+ if (!(flags & CONF_UPD_FNOFREE)) {
+ conf_free(old_conf);
+ old_conf = NULL;
+ }
+ }
+
+ return old_conf;
+}
+
+void conf_free(
+ conf_t *conf)
+{
+ if (conf == NULL) {
+ return;
+ }
+
+ yp_schema_free(conf->schema);
+ free(conf->filename);
+ free(conf->hostname);
+ if (conf->api != NULL) {
+ conf->api->txn_abort(&conf->read_txn);
+ }
+
+ if (conf->io.txn != NULL && conf->api != NULL) {
+ conf->api->txn_abort(conf->io.txn_stack);
+ }
+ if (conf->io.zones != NULL) {
+ trie_free(conf->io.zones);
+ }
+
+ conf_mod_load_purge(conf, false);
+ conf_deactivate_modules(conf->query_modules, &conf->query_plan);
+ free(conf->query_modules);
+ conf_mod_unload_shared(conf);
+
+ if (!conf->is_clone) {
+ if (conf->api != NULL) {
+ conf->api->deinit(conf->db);
+ }
+ }
+
+ free(conf);
+}
+
+#define CONF_LOG_LINE(file, line, msg, ...) do { \
+ CONF_LOG(LOG_ERR, "%s%s%sline %zu" msg, \
+ (file != NULL ? "file '" : ""), (file != NULL ? file : ""), \
+ (file != NULL ? "', " : ""), line, ##__VA_ARGS__); \
+ } while (0)
+
+static void log_parser_err(
+ yp_parser_t *parser,
+ int ret)
+{
+ if (parser->event == YP_ENULL) {
+ CONF_LOG_LINE(parser->file.name, parser->line_count,
+ " (%s)", knot_strerror(ret));
+ } else {
+ CONF_LOG_LINE(parser->file.name, parser->line_count,
+ ", item '%s'%s%.*s%s (%s)", parser->key,
+ (parser->data_len > 0) ? ", value '" : "",
+ (int)parser->data_len,
+ (parser->data_len > 0) ? parser->data : "",
+ (parser->data_len > 0) ? "'" : "",
+ knot_strerror(ret));
+ }
+}
+
+static void log_parser_schema_err(
+ yp_parser_t *parser,
+ int ret)
+{
+ // Emit better message for 'unknown module' error.
+ if (ret == KNOT_YP_EINVAL_ITEM && parser->event == YP_EKEY0 &&
+ strncmp(parser->key, KNOTD_MOD_NAME_PREFIX, strlen(KNOTD_MOD_NAME_PREFIX)) == 0) {
+ CONF_LOG_LINE(parser->file.name, parser->line_count,
+ ", unknown module '%s'", parser->key);
+ } else {
+ log_parser_err(parser, ret);
+ }
+}
+
+static void log_call_err(
+ yp_parser_t *parser,
+ knotd_conf_check_args_t *args,
+ int ret)
+{
+ CONF_LOG_LINE(args->extra->file_name, args->extra->line,
+ ", item '%s'%s%s%s (%s)", args->item->name + 1,
+ (parser->data_len > 0) ? ", value '" : "",
+ (parser->data_len > 0) ? parser->data : "",
+ (parser->data_len > 0) ? "'" : "",
+ (args->err_str != NULL) ? args->err_str : knot_strerror(ret));
+}
+
+static void log_prev_err(
+ knotd_conf_check_args_t *args,
+ int ret)
+{
+ char buff[512] = { 0 };
+ size_t len = sizeof(buff);
+
+ // Get the previous textual identifier.
+ if ((args->item->flags & YP_FMULTI) != 0) {
+ if (yp_item_to_txt(args->item->var.g.id, args->id, args->id_len,
+ buff, &len, YP_SNOQUOTE) != KNOT_EOK) {
+ buff[0] = '\0';
+ }
+ }
+
+ CONF_LOG_LINE(args->extra->file_name, args->extra->line - 1,
+ ", section '%s%s%s%s' (%s)", args->item->name + 1,
+ (buff[0] != '\0') ? "[" : "",
+ buff,
+ (buff[0] != '\0') ? "]" : "",
+ args->err_str != NULL ? args->err_str : knot_strerror(ret));
+}
+
+static int finalize_previous_section(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ yp_parser_t *parser,
+ yp_check_ctx_t *ctx)
+{
+ yp_node_t *node = &ctx->nodes[0];
+
+ // Return if no previous section or include or empty multi-section.
+ if (node->item == NULL || node->item->type != YP_TGRP ||
+ (node->id_len == 0 && (node->item->flags & YP_FMULTI) != 0)) {
+ return KNOT_EOK;
+ }
+
+ knotd_conf_check_extra_t extra = {
+ .conf = conf,
+ .txn = txn,
+ .file_name = parser->file.name,
+ .line = parser->line_count
+ };
+ knotd_conf_check_args_t args = {
+ .item = node->item,
+ .id = node->id,
+ .id_len = node->id_len,
+ .data = node->data,
+ .data_len = node->data_len,
+ .extra = &extra
+ };
+
+ int ret = conf_exec_callbacks(&args);
+ if (ret != KNOT_EOK) {
+ log_prev_err(&args, ret);
+ }
+
+ return ret;
+}
+
+static int finalize_item(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ yp_parser_t *parser,
+ yp_check_ctx_t *ctx)
+{
+ yp_node_t *node = &ctx->nodes[ctx->current];
+
+ // Section callbacks are executed before another section.
+ if (node->item->type == YP_TGRP && node->id_len == 0) {
+ return KNOT_EOK;
+ }
+
+ knotd_conf_check_extra_t extra = {
+ .conf = conf,
+ .txn = txn,
+ .file_name = parser->file.name,
+ .line = parser->line_count
+ };
+ knotd_conf_check_args_t args = {
+ .item = (parser->event == YP_EID) ? node->item->var.g.id : node->item,
+ .id = node->id,
+ .id_len = node->id_len,
+ .data = node->data,
+ .data_len = node->data_len,
+ .extra = &extra
+ };
+
+ int ret = conf_exec_callbacks(&args);
+ if (ret != KNOT_EOK) {
+ log_call_err(parser, &args, ret);
+ }
+
+ return ret;
+}
+
+int conf_parse(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const char *input,
+ bool is_file)
+{
+ if (conf == NULL || txn == NULL || input == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ yp_parser_t *parser = malloc(sizeof(yp_parser_t));
+ if (parser == NULL) {
+ return KNOT_ENOMEM;
+ }
+ yp_init(parser);
+
+ int ret;
+
+ // Set parser source.
+ if (is_file) {
+ ret = yp_set_input_file(parser, input);
+ } else {
+ ret = yp_set_input_string(parser, input, strlen(input));
+ }
+ if (ret != KNOT_EOK) {
+ CONF_LOG(LOG_ERR, "failed to load file '%s' (%s)",
+ input, knot_strerror(ret));
+ goto parse_error;
+ }
+
+ // Initialize parser check context.
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf->schema);
+ if (ctx == NULL) {
+ ret = KNOT_ENOMEM;
+ goto parse_error;
+ }
+
+ int check_ret = KNOT_EOK;
+
+ // Parse the configuration.
+ while ((ret = yp_parse(parser)) == KNOT_EOK) {
+ if (parser->event == YP_EKEY0 || parser->event == YP_EID) {
+ check_ret = finalize_previous_section(conf, txn, parser, ctx);
+ if (check_ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ check_ret = yp_schema_check_parser(ctx, parser);
+ if (check_ret != KNOT_EOK) {
+ log_parser_schema_err(parser, check_ret);
+ break;
+ }
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ if (parent == NULL) {
+ check_ret = conf_db_set(conf, txn, node->item->name,
+ NULL, node->id, node->id_len,
+ node->data, node->data_len);
+ } else {
+ check_ret = conf_db_set(conf, txn, parent->item->name,
+ node->item->name, parent->id,
+ parent->id_len, node->data,
+ node->data_len);
+ }
+ if (check_ret != KNOT_EOK) {
+ log_parser_err(parser, check_ret);
+ break;
+ }
+
+ check_ret = finalize_item(conf, txn, parser, ctx);
+ if (check_ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ if (ret == KNOT_EOF) {
+ ret = finalize_previous_section(conf, txn, parser, ctx);
+ } else if (ret != KNOT_EOK) {
+ log_parser_err(parser, ret);
+ } else {
+ ret = check_ret;
+ }
+
+ yp_schema_check_deinit(ctx);
+parse_error:
+ yp_deinit(parser);
+ free(parser);
+
+ return ret;
+}
+
+int conf_import(
+ conf_t *conf,
+ const char *input,
+ bool is_file,
+ bool reinit_cache)
+{
+ if (conf == NULL || input == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+
+ knot_db_txn_t txn;
+ ret = conf->api->txn_begin(conf->db, &txn, 0);
+ if (ret != KNOT_EOK) {
+ goto import_error;
+ }
+
+ // Initialize the DB.
+ ret = conf_db_init(conf, &txn, true);
+ if (ret != KNOT_EOK) {
+ conf->api->txn_abort(&txn);
+ goto import_error;
+ }
+
+ // Parse and import given file.
+ ret = conf_parse(conf, &txn, input, is_file);
+ if (ret != KNOT_EOK) {
+ conf->api->txn_abort(&txn);
+ goto import_error;
+ }
+ // Load purge must be here as conf_parse may be called recursively!
+ conf_mod_load_purge(conf, false);
+
+ // Commit new configuration.
+ ret = conf->api->txn_commit(&txn);
+ if (ret != KNOT_EOK) {
+ goto import_error;
+ }
+
+ // Update read-only transaction.
+ ret = conf_refresh_txn(conf);
+ if (ret != KNOT_EOK) {
+ goto import_error;
+ }
+
+ // Update cached values.
+ init_cache(conf, reinit_cache);
+
+ // Reset the filename.
+ free(conf->filename);
+ conf->filename = NULL;
+ if (is_file) {
+ conf->filename = strdup(input);
+ }
+
+ ret = KNOT_EOK;
+import_error:
+
+ return ret;
+}
+
+static int export_group_name(
+ FILE *fp,
+ const yp_item_t *group,
+ char *out,
+ size_t out_len,
+ yp_style_t style)
+{
+ int ret = yp_format_key0(group, NULL, 0, out, out_len, style, true, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ fprintf(fp, "%s", out);
+
+ return KNOT_EOK;
+}
+
+static int export_group(
+ conf_t *conf,
+ FILE *fp,
+ const yp_item_t *group,
+ const uint8_t *id,
+ size_t id_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool *exported)
+{
+ // Export the multi-group name.
+ if ((group->flags & YP_FMULTI) != 0 && !(*exported)) {
+ int ret = export_group_name(fp, group, out, out_len, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *exported = true;
+ }
+
+ // Iterate through all possible group items.
+ for (yp_item_t *item = group->sub_items; item->name != NULL; item++) {
+ // Export the identifier.
+ if (group->var.g.id == item && (group->flags & YP_FMULTI) != 0) {
+ int ret = yp_format_id(group->var.g.id, id, id_len, out,
+ out_len, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ fprintf(fp, "%s", out);
+ continue;
+ }
+
+ conf_val_t bin;
+ conf_db_get(conf, &conf->read_txn, group->name, item->name,
+ id, id_len, &bin);
+ if (bin.code == KNOT_ENOENT) {
+ continue;
+ } else if (bin.code != KNOT_EOK) {
+ return bin.code;
+ }
+
+ // Export the single-group name if an item is set.
+ if ((group->flags & YP_FMULTI) == 0 && !(*exported)) {
+ int ret = export_group_name(fp, group, out, out_len, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *exported = true;
+ }
+
+ // Format single/multiple-valued item.
+ size_t values = conf_val_count(&bin);
+ for (size_t i = 1; i <= values; i++) {
+ conf_val(&bin);
+ int ret = yp_format_key1(item, bin.data, bin.len, out,
+ out_len, style, i == 1,
+ i == values);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ fprintf(fp, "%s", out);
+
+ if (values > 1) {
+ conf_val_next(&bin);
+ }
+ }
+ }
+
+ if (*exported) {
+ fprintf(fp, "\n");
+ }
+
+ return KNOT_EOK;
+}
+
+static int export_item(
+ conf_t *conf,
+ FILE *fp,
+ const yp_item_t *item,
+ char *buff,
+ size_t buff_len,
+ yp_style_t style)
+{
+ bool exported = false;
+
+ // Skip non-group items (include).
+ if (item->type != YP_TGRP) {
+ return KNOT_EOK;
+ }
+
+ // Export simple group without identifiers.
+ if (!(item->flags & YP_FMULTI)) {
+ return export_group(conf, fp, item, NULL, 0, buff, buff_len,
+ style, &exported);
+ }
+
+ // Iterate over all identifiers.
+ conf_iter_t iter;
+ int ret = conf_db_iter_begin(conf, &conf->read_txn, item->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+
+ while (ret == KNOT_EOK) {
+ const uint8_t *id;
+ size_t id_len;
+ ret = conf_db_iter_id(conf, &iter, &id, &id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf, &iter);
+ return ret;
+ }
+
+ // Export group with identifiers.
+ ret = export_group(conf, fp, item, id, id_len, buff, buff_len,
+ style, &exported);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf, &iter);
+ return ret;
+ }
+
+ ret = conf_db_iter_next(conf, &iter);
+ }
+ if (ret != KNOT_EOF) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_export(
+ conf_t *conf,
+ const char *file_name,
+ yp_style_t style)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Prepare common buffer;
+ const size_t buff_len = 2 * CONF_MAX_DATA_LEN; // Rough limit.
+ char *buff = malloc(buff_len);
+ if (buff == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ FILE *fp = (file_name != NULL) ? fopen(file_name, "w") : stdout;
+ if (fp == NULL) {
+ free(buff);
+ return knot_map_errno();
+ }
+
+ fprintf(fp, "# Configuration export (Knot DNS %s)\n\n", PACKAGE_VERSION);
+
+ const char *mod_prefix = KNOTD_MOD_NAME_PREFIX;
+ const size_t mod_prefix_len = strlen(mod_prefix);
+
+ int ret;
+
+ // Iterate over the schema.
+ for (yp_item_t *item = conf->schema; item->name != NULL; item++) {
+ // Don't export module sections again.
+ if (strncmp(item->name + 1, mod_prefix, mod_prefix_len) == 0) {
+ break;
+ }
+
+ // Export module sections before the template section.
+ if (strcmp(&item->name[1], &C_TPL[1]) == 0) {
+ for (yp_item_t *mod = item + 1; mod->name != NULL; mod++) {
+ // Skip non-module sections.
+ if (strncmp(mod->name + 1, mod_prefix, mod_prefix_len) != 0) {
+ continue;
+ }
+
+ // Export module section.
+ ret = export_item(conf, fp, mod, buff, buff_len, style);
+ if (ret != KNOT_EOK) {
+ goto export_error;
+ }
+ }
+ }
+
+ // Export non-module section.
+ ret = export_item(conf, fp, item, buff, buff_len, style);
+ if (ret != KNOT_EOK) {
+ goto export_error;
+ }
+ }
+
+ ret = KNOT_EOK;
+export_error:
+ if (file_name != NULL) {
+ fclose(fp);
+ }
+ free(buff);
+
+ return ret;
+}
diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h
new file mode 100644
index 0000000..693ffd6
--- /dev/null
+++ b/src/knot/conf/base.h
@@ -0,0 +1,322 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+#include "libknot/yparser/ypschema.h"
+#include "contrib/qp-trie/trie.h"
+#include "contrib/ucw/lists.h"
+#include "libknot/dynarray.h"
+#include "knot/include/module.h"
+
+/*! Default template identifier. */
+#define CONF_DEFAULT_ID ((uint8_t *)"\x08""default\0")
+/*! Default configuration file. */
+#define CONF_DEFAULT_FILE (CONFIG_DIR "/knot.conf")
+/*! Default configuration database. */
+#define CONF_DEFAULT_DBDIR (STORAGE_DIR "/confdb")
+/*! Maximum depth of nested transactions. */
+#define CONF_MAX_TXN_DEPTH 5
+
+/*! Maximum number of UDP workers. */
+#define CONF_MAX_UDP_WORKERS 256
+/*! Maximum number of TCP workers. */
+#define CONF_MAX_TCP_WORKERS 256
+/*! Maximum number of background workers. */
+#define CONF_MAX_BG_WORKERS 512
+/*! Maximum number of concurrent DB readers. */
+#define CONF_MAX_DB_READERS (CONF_MAX_UDP_WORKERS + CONF_MAX_TCP_WORKERS + \
+ CONF_MAX_BG_WORKERS + 10 + 128 /* Utils, XDP workers */)
+
+/*! Configuration specific logging. */
+#define CONF_LOG(severity, msg, ...) do { \
+ log_fmt(severity, LOG_SOURCE_SERVER, "config, " msg, ##__VA_ARGS__); \
+ } while (0)
+
+#define CONF_LOG_ZONE(severity, zone, msg, ...) do { \
+ log_fmt_zone(severity, LOG_SOURCE_ZONE, zone, NULL, "config, " msg, ##__VA_ARGS__); \
+ } while (0)
+
+/*! Configuration getter output. */
+typedef struct {
+ /*! Item description. */
+ const yp_item_t *item;
+ /*! Whole data (can be array). */
+ const uint8_t *blob;
+ /*! Whole data length. */
+ size_t blob_len;
+ // Public items.
+ /*! Current single data. */
+ const uint8_t *data;
+ /*! Current single data length. */
+ size_t len;
+ /*! Value getter return code. */
+ int code;
+} conf_val_t;
+
+/*! Shared module types. */
+typedef enum {
+ /*! Static module. */
+ MOD_STATIC = 0,
+ /*! Implicit shared module which is always loaded. */
+ MOD_IMPLICIT,
+ /*! Explicit shared module which is currently loaded. */
+ MOD_EXPLICIT,
+ /*! Explicit shared temporary module which is loaded during config check. */
+ MOD_TEMPORARY
+} module_type_t;
+
+/*! Query module context. */
+typedef struct {
+ /*! Module interface. */
+ const knotd_mod_api_t *api;
+ /*! Shared library dlopen handler. */
+ void *lib_handle;
+ /*! Module type. */
+ module_type_t type;
+} module_t;
+
+knot_dynarray_declare(mod, module_t *, DYNARRAY_VISIBILITY_NORMAL, 16)
+knot_dynarray_declare(old_schema, yp_item_t *, DYNARRAY_VISIBILITY_NORMAL, 16)
+
+struct knot_catalog;
+
+/*! Configuration context. */
+typedef struct {
+ /*! Cloned configuration indicator. */
+ bool is_clone;
+ /*! Currently used namedb api. */
+ const struct knot_db_api *api;
+ /*! Configuration schema. */
+ yp_item_t *schema;
+ /*! Configuration database. */
+ knot_db_t *db;
+ /*! LMDB mapsize. */
+ size_t mapsize;
+
+ /*! Read-only transaction for config access. */
+ knot_db_txn_t read_txn;
+
+ struct {
+ /*! The current writing transaction. */
+ knot_db_txn_t *txn;
+ /*! Stack of nested writing transactions. */
+ knot_db_txn_t txn_stack[CONF_MAX_TXN_DEPTH];
+ /*! Master transaction flags. */
+ yp_flag_t flags;
+ /*! Changed zones. */
+ trie_t *zones;
+ } io;
+
+ /*! Current config file (for reload if started with config file). */
+ char *filename;
+
+ /*! Prearranged hostname string (for automatic NSID or CH ident value). */
+ char *hostname;
+
+ /*! Cached critical confdb items. */
+ struct {
+ uint16_t srv_udp_max_payload_ipv4;
+ uint16_t srv_udp_max_payload_ipv6;
+ int srv_tcp_idle_timeout;
+ int srv_tcp_io_timeout;
+ int srv_tcp_remote_io_timeout;
+ bool srv_tcp_reuseport;
+ bool srv_tcp_fastopen;
+ bool srv_socket_affinity;
+ unsigned srv_dbus_event;
+ size_t srv_udp_threads;
+ size_t srv_tcp_threads;
+ size_t srv_xdp_threads;
+ size_t srv_bg_threads;
+ size_t srv_tcp_max_clients;
+ size_t xdp_tcp_max_clients;
+ size_t xdp_tcp_inbuf_max_size;
+ size_t xdp_tcp_outbuf_max_size;
+ uint32_t xdp_tcp_idle_close;
+ uint32_t xdp_tcp_idle_reset;
+ uint32_t xdp_tcp_idle_resend;
+ size_t srv_quic_max_clients;
+ size_t srv_quic_obuf_max_size;
+ uint32_t srv_quic_idle_close;
+ bool xdp_udp;
+ bool xdp_tcp;
+ uint16_t xdp_quic;
+ bool xdp_route_check;
+ int ctl_timeout;
+ const uint8_t *srv_nsid_data;
+ size_t srv_nsid_len;
+ bool srv_ecs;
+ bool srv_ans_rotate;
+ bool srv_auto_acl;
+ bool srv_proxy_enabled;
+ } cache;
+
+ /*! List of dynamically loaded modules. */
+ mod_dynarray_t modules;
+ /*! List of old schemas (lazy freed). */
+ old_schema_dynarray_t old_schemas;
+ /*! List of active query modules. */
+ list_t *query_modules;
+ /*! Default query modules plan. */
+ struct query_plan *query_plan;
+ /*! Zone catalog database. */
+ struct catalog *catalog;
+} conf_t;
+
+/*!
+ * Configuration access flags.
+ */
+typedef enum {
+ CONF_FNONE = 0, /*!< Empty flag. */
+ CONF_FREADONLY = 1 << 0, /*!< Read only access. */
+ CONF_FNOCHECK = 1 << 1, /*!< Disabled confdb check. */
+ CONF_FNOHOSTNAME = 1 << 2, /*!< Don't set the hostname. */
+ CONF_FREQMODULES = 1 << 3, /*!< Load module schemas (must succeed). */
+ CONF_FOPTMODULES = 1 << 4, /*!< Load module schemas (may fail). */
+} conf_flag_t;
+
+/*!
+ * Configuration update flags.
+ */
+typedef enum {
+ CONF_UPD_FNONE = 0, /*!< Empty flag. */
+ CONF_UPD_FNOFREE = 1 << 0, /*!< Disable auto-free of previous config. */
+ CONF_UPD_FMODULES = 1 << 1, /*!< Reuse previous global modules. */
+ CONF_UPD_FCONFIO = 1 << 2, /*!< Reuse previous confio reload context. */
+} conf_update_flag_t;
+
+/*!
+ * Returns the active configuration.
+ */
+conf_t* conf(void);
+
+/*!
+ * Refreshes common read-only transaction.
+ *
+ * \param[in] conf Configuration.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_refresh_txn(
+ conf_t *conf
+);
+
+/*!
+ * Creates new or opens old configuration database.
+ *
+ * \param[out] conf Configuration.
+ * \param[in] schema Configuration schema.
+ * \param[in] db_dir Database path or NULL.
+ * \param[in] max_conf_size Maximum configuration DB size in bytes (LMDB mapsize).
+ * \param[in] flags Access flags.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_new(
+ conf_t **conf,
+ const yp_item_t *schema,
+ const char *db_dir,
+ size_t max_conf_size,
+ conf_flag_t flags
+);
+
+/*!
+ * Creates a partial copy of the active configuration.
+ *
+ * Shared objects: api, mm, db, filename.
+ *
+ * \param[out] conf Configuration.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_clone(
+ conf_t **conf
+);
+
+/*!
+ * Replaces the active configuration with the specified one.
+ *
+ * \param[in] conf New configuration.
+ * \param[in] flags Update flags.
+ *
+ * \return Previous config if CONF_UPD_FNOFREE, else NULL.
+ */
+conf_t *conf_update(
+ conf_t *conf,
+ conf_update_flag_t flags
+);
+
+/*!
+ * Removes the specified configuration.
+ *
+ * \param[in] conf Configuration.
+ */
+void conf_free(
+ conf_t *conf
+);
+
+/*!
+ * Parses textual configuration from the string or from the file.
+ *
+ * This function is not for direct using, just for includes processing!
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Transaction.
+ * \param[in] input Configuration string or filename.
+ * \param[in] is_file Specifies if the input is string or input filename.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_parse(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const char *input,
+ bool is_file
+);
+
+/*!
+ * Imports textual configuration.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] input Configuration string or input filename.
+ * \param[in] is_file Specifies if the input is string or filename.
+ * \param[in] reinit_cache Indication if cache reinitialization needed.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_import(
+ conf_t *conf,
+ const char *input,
+ bool is_file,
+ bool reinit_cache
+);
+
+/*!
+ * Exports configuration to textual file.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] file_name Output filename (stdout is used if NULL).
+ * \param[in] style Formatting style.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_export(
+ conf_t *conf,
+ const char *file_name,
+ yp_style_t style
+);
diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c
new file mode 100644
index 0000000..016f01e
--- /dev/null
+++ b/src/knot/conf/conf.c
@@ -0,0 +1,1469 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+
+#include "knot/conf/base.h"
+#include "knot/conf/confdb.h"
+#include "knot/catalog/catalog_db.h"
+#include "knot/common/log.h"
+#include "knot/server/dthreads.h"
+#include "libknot/libknot.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/xdp.h"
+#include "contrib/files.h"
+#include "contrib/macros.h"
+#include "contrib/sockaddr.h"
+#include "contrib/strtonum.h"
+#include "contrib/string.h"
+#include "contrib/wire_ctx.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define DBG_LOG(err) CONF_LOG(LOG_DEBUG, "%s (%s)", __func__, knot_strerror((err)));
+
+#define DFLT_MIN_TCP_WORKERS 10
+#define DFLT_MAX_BG_WORKERS 10
+#define FALLBACK_MAX_TCP_CLIENTS 100
+
+bool conf_db_exists(
+ const char *db_dir)
+{
+ if (db_dir == NULL) {
+ return false;
+ }
+
+ struct stat st;
+ char data_mdb[strlen(db_dir) + 10];
+ (void)snprintf(data_mdb, sizeof(data_mdb), "%s/data.mdb", db_dir);
+ return (stat(data_mdb, &st) == 0 && st.st_size > 0);
+}
+
+conf_val_t conf_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name)
+{
+ conf_val_t val = { NULL };
+
+ if (key0_name == NULL || key1_name == NULL) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ conf_db_get(conf, txn, key0_name, key1_name, NULL, 0, &val);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)",
+ key0_name + 1, key1_name + 1, knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ return val;
+ }
+}
+
+conf_val_t conf_rawid_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ const uint8_t *id,
+ size_t id_len)
+{
+ conf_val_t val = { NULL };
+
+ if (key0_name == NULL || key1_name == NULL || id == NULL) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ conf_db_get(conf, txn, key0_name, key1_name, id, id_len, &val);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read '%s/%s' with identifier (%s)",
+ key0_name + 1, key1_name + 1, knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ return val;
+ }
+}
+
+conf_val_t conf_id_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ conf_val_t *id)
+{
+ conf_val_t val = { NULL };
+
+ if (key0_name == NULL || key1_name == NULL || id == NULL ||
+ id->code != KNOT_EOK) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ conf_val(id);
+
+ conf_db_get(conf, txn, key0_name, key1_name, id->data, id->len, &val);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read '%s/%s' with identifier (%s)",
+ key0_name + 1, key1_name + 1, knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ return val;
+ }
+}
+
+conf_val_t conf_mod_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name,
+ const conf_mod_id_t *mod_id)
+{
+ conf_val_t val = { NULL };
+
+ if (key1_name == NULL || mod_id == NULL) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ conf_db_get(conf, txn, mod_id->name, key1_name, mod_id->data, mod_id->len,
+ &val);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)",
+ mod_id->name + 1, key1_name + 1, knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ return val;
+ }
+}
+
+conf_val_t conf_zone_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name,
+ const knot_dname_t *dname)
+{
+ conf_val_t val = { NULL };
+
+ if (key1_name == NULL || dname == NULL) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ size_t dname_size = knot_dname_size(dname);
+
+ // Try to get explicit value.
+ conf_db_get(conf, txn, C_ZONE, key1_name, dname, dname_size, &val);
+ switch (val.code) {
+ case KNOT_EOK:
+ return val;
+ default:
+ CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
+ &C_ZONE[1], &key1_name[1], knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_YP_EINVAL_ID:
+ case KNOT_ENOENT:
+ break;
+ }
+
+ // Check if a template is available.
+ conf_db_get(conf, txn, C_ZONE, C_TPL, dname, dname_size, &val);
+ switch (val.code) {
+ case KNOT_EOK:
+ // Use the specified template.
+ conf_val(&val);
+ conf_db_get(conf, txn, C_TPL, key1_name, val.data, val.len, &val);
+ goto got_template;
+ default:
+ CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
+ &C_ZONE[1], &C_TPL[1], knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ break;
+ }
+
+ // Check if this is a catalog member zone.
+ if (conf->catalog != NULL) {
+ void *tofree = NULL;
+ const knot_dname_t *catalog;
+ const char *group;
+ int ret = catalog_get_catz(conf->catalog, dname, &catalog, &group, &tofree);
+ if (ret == KNOT_EOK) {
+ val = conf_zone_get_txn(conf, txn, C_CATALOG_TPL, catalog);
+ if (val.code == KNOT_EOK) {
+ conf_val(&val);
+ while (val.code == KNOT_EOK) {
+ if (strmemcmp(group, val.data, val.len) == 0) {
+ break;
+ }
+ conf_val_next(&val);
+ }
+ conf_val(&val); // Use first value if no match.
+ free(tofree);
+
+ conf_db_get(conf, txn, C_TPL, key1_name, val.data,
+ val.len, &val);
+ goto got_template;
+ } else {
+ CONF_LOG_ZONE(LOG_ERR, catalog,
+ "orphaned catalog database record (%s)",
+ knot_strerror(val.code));
+ free(tofree);
+ }
+ }
+ }
+
+ // Use the default template.
+ conf_db_get(conf, txn, C_TPL, key1_name, CONF_DEFAULT_ID + 1,
+ CONF_DEFAULT_ID[0], &val);
+
+got_template:
+ switch (val.code) {
+ default:
+ CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)",
+ &C_TPL[1], &key1_name[1], knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ break;
+ }
+
+ return val;
+}
+
+conf_val_t conf_default_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name)
+{
+ conf_val_t val = { NULL };
+
+ if (key1_name == NULL) {
+ val.code = KNOT_EINVAL;
+ DBG_LOG(val.code);
+ return val;
+ }
+
+ conf_db_get(conf, txn, C_TPL, key1_name, CONF_DEFAULT_ID + 1,
+ CONF_DEFAULT_ID[0], &val);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read default '%s/%s' (%s)",
+ &C_TPL[1], &key1_name[1], knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ break;
+ }
+
+ return val;
+}
+
+bool conf_rawid_exists_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const uint8_t *id,
+ size_t id_len)
+{
+ if (key0_name == NULL || id == NULL) {
+ DBG_LOG(KNOT_EINVAL);
+ return false;
+ }
+
+ int ret = conf_db_get(conf, txn, key0_name, NULL, id, id_len, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ return true;
+ default:
+ CONF_LOG(LOG_ERR, "failed to check '%s' for identifier (%s)",
+ key0_name + 1, knot_strerror(ret));
+ // FALLTHROUGH
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ return false;
+ }
+}
+
+bool conf_id_exists_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ conf_val_t *id)
+{
+ if (key0_name == NULL || id == NULL || id->code != KNOT_EOK) {
+ DBG_LOG(KNOT_EINVAL);
+ return false;
+ }
+
+ conf_val(id);
+
+ int ret = conf_db_get(conf, txn, key0_name, NULL, id->data, id->len, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ return true;
+ default:
+ CONF_LOG(LOG_ERR, "failed to check '%s' for identifier (%s)",
+ key0_name + 1, knot_strerror(ret));
+ // FALLTHROUGH
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ return false;
+ }
+}
+
+size_t conf_id_count_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name)
+{
+ size_t count = 0;
+
+ for (conf_iter_t iter = conf_iter_txn(conf, txn, key0_name);
+ iter.code == KNOT_EOK; conf_iter_next(conf, &iter)) {
+ count++;
+ }
+
+ return count;
+}
+
+conf_iter_t conf_iter_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name)
+{
+ conf_iter_t iter = { NULL };
+
+ (void)conf_db_iter_begin(conf, txn, key0_name, &iter);
+ switch (iter.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to iterate through '%s' (%s)",
+ key0_name + 1, knot_strerror(iter.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ return iter;
+ }
+}
+
+void conf_iter_next(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ (void)conf_db_iter_next(conf, iter);
+ switch (iter->code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read next item (%s)",
+ knot_strerror(iter->code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ case KNOT_EOF:
+ return;
+ }
+}
+
+conf_val_t conf_iter_id(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ conf_val_t val = { NULL };
+
+ val.code = conf_db_iter_id(conf, iter, &val.blob, &val.blob_len);
+ switch (val.code) {
+ default:
+ CONF_LOG(LOG_ERR, "failed to read identifier (%s)",
+ knot_strerror(val.code));
+ // FALLTHROUGH
+ case KNOT_EOK:
+ val.item = iter->item;
+ return val;
+ }
+}
+
+void conf_iter_finish(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ conf_db_iter_finish(conf, iter);
+}
+
+size_t conf_val_count(
+ conf_val_t *val)
+{
+ if (val == NULL || val->code != KNOT_EOK) {
+ return 0;
+ }
+
+ if (!(val->item->flags & YP_FMULTI)) {
+ return 1;
+ }
+
+ size_t count = 0;
+ conf_val(val);
+ while (val->code == KNOT_EOK) {
+ count++;
+ conf_val_next(val);
+ }
+ if (val->code != KNOT_EOF) {
+ return 0;
+ }
+
+ // Reset to the initial state.
+ conf_val(val);
+
+ return count;
+}
+
+void conf_val(
+ conf_val_t *val)
+{
+ assert(val != NULL);
+ assert(val->code == KNOT_EOK || val->code == KNOT_EOF);
+
+ if (val->item->flags & YP_FMULTI) {
+ // Check if already called and not at the end.
+ if (val->data != NULL && val->code != KNOT_EOF) {
+ return;
+ }
+ // Otherwise set to the first value.
+ conf_val_reset(val);
+ } else {
+ // Check for empty data.
+ if (val->blob_len == 0) {
+ val->data = NULL;
+ val->len = 0;
+ val->code = KNOT_EOK;
+ return;
+ } else {
+ assert(val->blob != NULL);
+ val->data = val->blob;
+ val->len = val->blob_len;
+ val->code = KNOT_EOK;
+ }
+ }
+}
+
+void conf_val_next(
+ conf_val_t *val)
+{
+ assert(val != NULL);
+ assert(val->code == KNOT_EOK);
+ assert(val->item->flags & YP_FMULTI);
+
+ // Check for the 'zero' call.
+ if (val->data == NULL) {
+ conf_val(val);
+ return;
+ }
+
+ if (val->data + val->len < val->blob + val->blob_len) {
+ wire_ctx_t ctx = wire_ctx_init_const(val->blob, val->blob_len);
+ size_t offset = val->data + val->len - val->blob;
+ wire_ctx_skip(&ctx, offset);
+ uint16_t len = wire_ctx_read_u16(&ctx);
+ assert(ctx.error == KNOT_EOK);
+
+ val->data = ctx.position;
+ val->len = len;
+ val->code = KNOT_EOK;
+ } else {
+ val->data = NULL;
+ val->len = 0;
+ val->code = KNOT_EOF;
+ }
+}
+
+void conf_val_reset(conf_val_t *val)
+{
+ assert(val != NULL);
+ assert(val->code == KNOT_EOK || val->code == KNOT_EOF);
+ assert(val->item->flags & YP_FMULTI);
+
+ assert(val->blob != NULL);
+ wire_ctx_t ctx = wire_ctx_init_const(val->blob, val->blob_len);
+ uint16_t len = wire_ctx_read_u16(&ctx);
+ assert(ctx.error == KNOT_EOK);
+
+ val->data = ctx.position;
+ val->len = len;
+ val->code = KNOT_EOK;
+}
+
+bool conf_val_equal(
+ conf_val_t *val1,
+ conf_val_t *val2)
+{
+ if (val1->blob_len == val2->blob_len &&
+ memcmp(val1->blob, val2->blob, val1->blob_len) == 0) {
+ return true;
+ }
+
+ return false;
+}
+
+void conf_mix_iter_init(
+ conf_t *conf,
+ conf_val_t *mix_id,
+ conf_mix_iter_t *iter)
+{
+ assert(mix_id != NULL && mix_id->item != NULL);
+ assert(mix_id->item->type == YP_TREF &&
+ mix_id->item->var.r.ref != NULL &&
+ mix_id->item->var.r.grp_ref != NULL &&
+ mix_id->item->var.r.ref->var.g.id->type == YP_TSTR &&
+ mix_id->item->var.r.grp_ref->var.g.id->type == YP_TSTR);
+
+ iter->conf = conf;
+ iter->mix_id = mix_id;
+ iter->id = mix_id;
+ iter->nested = false;
+
+ if (mix_id->code != KNOT_EOK) {
+ return;
+ }
+
+ iter->sub_id = conf_id_get_txn(conf, &conf->read_txn,
+ mix_id->item->var.r.grp_ref_name,
+ mix_id->item->var.r.ref_name,
+ mix_id);
+ if (iter->sub_id.code == KNOT_EOK) {
+ conf_val(&iter->sub_id);
+ iter->id = &iter->sub_id;
+ iter->nested = true;
+ }
+}
+
+void conf_mix_iter_next(
+ conf_mix_iter_t *iter)
+{
+ conf_val_next(iter->id);
+ if (iter->nested) {
+ if (iter->id->code == KNOT_EOK) {
+ return;
+ }
+ assert(iter->id->code == KNOT_EOF);
+ conf_val_next(iter->mix_id);
+ if (iter->mix_id->code != KNOT_EOK) {
+ return;
+ }
+ } else if (iter->id->code != KNOT_EOK){
+ return;
+ }
+
+ iter->sub_id = conf_id_get_txn(iter->conf, &iter->conf->read_txn,
+ iter->mix_id->item->var.r.grp_ref_name,
+ iter->mix_id->item->var.r.ref_name,
+ iter->mix_id);
+ if (iter->sub_id.code == KNOT_EOK) {
+ conf_val(&iter->sub_id);
+ iter->id = &iter->sub_id;
+ iter->nested = true;
+ } else {
+ iter->id = iter->mix_id;
+ iter->nested = false;
+ }
+}
+
+int64_t conf_int(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TINT ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TINT));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ return yp_int(val->data);
+ } else {
+ return val->item->var.i.dflt;
+ }
+}
+
+bool conf_bool(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TBOOL ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TBOOL));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ return yp_bool(val->data);
+ } else {
+ return val->item->var.b.dflt;
+ }
+}
+
+unsigned conf_opt(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TOPT ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TOPT));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ return yp_opt(val->data);
+ } else {
+ return val->item->var.o.dflt;
+ }
+}
+
+const char* conf_str(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TSTR ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TSTR));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ return yp_str(val->data);
+ } else {
+ return val->item->var.s.dflt;
+ }
+}
+
+const knot_dname_t* conf_dname(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TDNAME ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TDNAME));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ return yp_dname(val->data);
+ } else {
+ return (const knot_dname_t *)val->item->var.d.dflt;
+ }
+}
+
+const uint8_t* conf_bin(
+ conf_val_t *val,
+ size_t *len)
+{
+ assert(val != NULL && val->item != NULL && len != NULL);
+ assert(val->item->type == YP_THEX || val->item->type == YP_TB64 ||
+ (val->item->type == YP_TREF &&
+ (val->item->var.r.ref->var.g.id->type == YP_THEX ||
+ val->item->var.r.ref->var.g.id->type == YP_TB64)));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ *len = yp_bin_len(val->data);
+ return yp_bin(val->data);
+ } else {
+ *len = val->item->var.d.dflt_len;
+ return val->item->var.d.dflt;
+ }
+}
+
+const uint8_t* conf_data(
+ conf_val_t *val,
+ size_t *len)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TDATA ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TDATA));
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ *len = val->len;
+ return val->data;
+ } else {
+ *len = val->item->var.d.dflt_len;
+ return val->item->var.d.dflt;
+ }
+}
+
+struct sockaddr_storage conf_addr(
+ conf_val_t *val,
+ const char *sock_base_dir)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TADDR ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TADDR));
+
+ struct sockaddr_storage out = { AF_UNSPEC };
+
+ if (val->code == KNOT_EOK) {
+ bool no_port;
+ conf_val(val);
+ assert(val->data);
+ out = yp_addr(val->data, &no_port);
+
+ if (out.ss_family == AF_UNIX) {
+ // val->data[0] is socket type identifier!
+ if (val->data[1] != '/' && sock_base_dir != NULL) {
+ char *tmp = sprintf_alloc("%s/%s", sock_base_dir,
+ val->data + 1);
+ val->code = sockaddr_set(&out, AF_UNIX, tmp, 0);
+ free(tmp);
+ }
+ } else if (no_port) {
+ sockaddr_port_set(&out, val->item->var.a.dflt_port);
+ }
+ } else {
+ const char *dflt_socket = val->item->var.a.dflt_socket;
+ if (dflt_socket != NULL) {
+ if (dflt_socket[0] == '/' || sock_base_dir == NULL) {
+ val->code = sockaddr_set(&out, AF_UNIX,
+ dflt_socket, 0);
+ } else {
+ char *tmp = sprintf_alloc("%s/%s", sock_base_dir,
+ dflt_socket);
+ val->code = sockaddr_set(&out, AF_UNIX, tmp, 0);
+ free(tmp);
+ }
+ }
+ }
+
+ return out;
+}
+
+bool conf_addr_match(
+ conf_val_t *match,
+ const struct sockaddr_storage *addr)
+{
+ if (match == NULL || addr == NULL) {
+ return false;
+ }
+
+ while (match->code == KNOT_EOK) {
+ struct sockaddr_storage maddr = conf_addr(match, NULL);
+ if (sockaddr_cmp(&maddr, addr, true) == 0) {
+ return true;
+ }
+
+ conf_val_next(match);
+ }
+
+ return false;
+}
+
+struct sockaddr_storage conf_addr_range(
+ conf_val_t *val,
+ struct sockaddr_storage *max_ss,
+ int *prefix_len)
+{
+ assert(val != NULL && val->item != NULL && max_ss != NULL &&
+ prefix_len != NULL);
+ assert(val->item->type == YP_TNET ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TNET));
+
+ struct sockaddr_storage out = { AF_UNSPEC };
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ assert(val->data);
+ out = yp_addr_noport(val->data);
+ // addr_type, addr, format, formatted_data (port| addr| empty).
+ const uint8_t *format = val->data + sizeof(uint8_t) +
+ ((out.ss_family == AF_INET) ?
+ IPV4_PREFIXLEN / 8 : IPV6_PREFIXLEN / 8);
+ // See addr_range_to_bin.
+ switch (*format) {
+ case 1:
+ max_ss->ss_family = AF_UNSPEC;
+ *prefix_len = yp_int(format + sizeof(uint8_t));
+ break;
+ case 2:
+ *max_ss = yp_addr_noport(format + sizeof(uint8_t));
+ *prefix_len = -1;
+ break;
+ default:
+ max_ss->ss_family = AF_UNSPEC;
+ *prefix_len = -1;
+ break;
+ }
+ } else {
+ max_ss->ss_family = AF_UNSPEC;
+ *prefix_len = -1;
+ }
+
+ return out;
+}
+
+bool conf_addr_range_match(
+ conf_val_t *range,
+ const struct sockaddr_storage *addr)
+{
+ if (range == NULL || addr == NULL) {
+ return false;
+ }
+
+ while (range->code == KNOT_EOK) {
+ int mask;
+ struct sockaddr_storage min, max;
+
+ min = conf_addr_range(range, &max, &mask);
+ if (max.ss_family == AF_UNSPEC) {
+ if (sockaddr_net_match(addr, &min, mask)) {
+ return true;
+ }
+ } else {
+ if (sockaddr_range_match(addr, &min, &max)) {
+ return true;
+ }
+ }
+
+ conf_val_next(range);
+ }
+
+ return false;
+}
+
+char* conf_abs_path(
+ conf_val_t *val,
+ const char *base_dir)
+{
+ const char *path = conf_str(val);
+ return abs_path(path, base_dir);
+}
+
+conf_mod_id_t* conf_mod_id(
+ conf_val_t *val)
+{
+ assert(val != NULL && val->item != NULL);
+ assert(val->item->type == YP_TDATA ||
+ (val->item->type == YP_TREF &&
+ val->item->var.r.ref->var.g.id->type == YP_TDATA));
+
+ conf_mod_id_t *mod_id = NULL;
+
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ assert(val->data);
+
+ mod_id = malloc(sizeof(conf_mod_id_t));
+ if (mod_id == NULL) {
+ return NULL;
+ }
+
+ // Set module name in yp_name_t format + add zero termination.
+ size_t name_len = 1 + val->data[0];
+ mod_id->name = malloc(name_len + 1);
+ if (mod_id->name == NULL) {
+ free(mod_id);
+ return NULL;
+ }
+ memcpy(mod_id->name, val->data, name_len);
+ mod_id->name[name_len] = '\0';
+
+ // Set module identifier.
+ mod_id->len = val->len - name_len;
+ mod_id->data = malloc(mod_id->len);
+ if (mod_id->data == NULL) {
+ free(mod_id->name);
+ free(mod_id);
+ return NULL;
+ }
+ memcpy(mod_id->data, val->data + name_len, mod_id->len);
+ }
+
+ return mod_id;
+}
+
+void conf_free_mod_id(
+ conf_mod_id_t *mod_id)
+{
+ if (mod_id == NULL) {
+ return;
+ }
+
+ free(mod_id->name);
+ free(mod_id->data);
+ free(mod_id);
+}
+
+static int get_index(
+ const char **start,
+ const char *end,
+ unsigned *index1,
+ unsigned *index2)
+{
+ char c, *p;
+ if (sscanf(*start, "[%u%c", index1, &c) != 2) {
+ return KNOT_EINVAL;
+ }
+ switch (c) {
+ case '-':
+ p = strchr(*start, '-') + 1;
+ if (end - p < 2 || index2 == NULL ||
+ sscanf(p, "%u%c", index2, &c) != 2 || c != ']') {
+ return KNOT_EINVAL;
+ }
+ break;
+ case ']':
+ if (index2 != NULL) {
+ *index2 = *index1;
+ }
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ *start = strchr(*start, ']') + 1;
+ return ((*index1 < 256 && (index2 == NULL || *index2 < 256)
+ && end - *start >= 0 && (index2 == NULL || *index2 >= *index1))
+ ? KNOT_EOK : KNOT_EINVAL);
+}
+
+static void replace_slashes(
+ char *name,
+ bool remove_dot)
+{
+ // Replace possible slashes with underscores.
+ char *ch;
+ for (ch = name; *ch != '\0'; ch++) {
+ if (*ch == '/') {
+ *ch = '_';
+ }
+ }
+
+ // Remove trailing dot.
+ if (remove_dot && ch > name) {
+ assert(*(ch - 1) == '.');
+ *(ch - 1) = '\0';
+ }
+}
+
+static int str_char(
+ const knot_dname_t *zone,
+ char *buff,
+ size_t buff_len,
+ unsigned index1,
+ unsigned index2)
+{
+ assert(buff);
+
+ if (knot_dname_to_str(buff, zone, buff_len) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t zone_len = strlen(buff);
+ assert(zone_len > 0);
+
+ // Get the block length.
+ size_t len = index2 - index1 + 1;
+
+ // Check for out of scope block.
+ if (index1 >= zone_len) {
+ buff[0] = '\0';
+ return KNOT_EOK;
+ }
+ // Check for partial block.
+ if (index2 >= zone_len) {
+ len = zone_len - index1;
+ }
+
+ // Copy the block.
+ memmove(buff, buff + index1, len);
+ buff[len] = '\0';
+
+ // Replace possible slashes with underscores.
+ replace_slashes(buff, false);
+
+ return KNOT_EOK;
+}
+
+static int str_zone(
+ const knot_dname_t *zone,
+ char *buff,
+ size_t buff_len)
+{
+ assert(buff);
+
+ if (knot_dname_to_str(buff, zone, buff_len) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Replace possible slashes with underscores.
+ replace_slashes(buff, true);
+
+ return KNOT_EOK;
+}
+
+static int str_label(
+ const knot_dname_t *zone,
+ char *buff,
+ size_t buff_len,
+ size_t right_index)
+{
+ size_t labels = knot_dname_labels(zone, NULL);
+
+ // Check for root label of the root zone.
+ if (labels == 0 && right_index == 0) {
+ return str_zone(zone, buff, buff_len);
+ // Check for labels error or for an exceeded index.
+ } else if (labels < 1 || labels <= right_index) {
+ buff[0] = '\0';
+ return KNOT_EOK;
+ }
+
+ // ~ Label length + label + root label.
+ knot_dname_t label[1 + KNOT_DNAME_MAXLABELLEN + 1];
+
+ // Compute the index from the left.
+ assert(labels > right_index);
+ size_t index = labels - right_index - 1;
+
+ // Create a dname from the single label.
+ size_t prefix_len = knot_dname_prefixlen(zone, index, NULL);
+ size_t label_len = *(zone + prefix_len);
+ memcpy(label, zone + prefix_len, 1 + label_len);
+ label[1 + label_len] = '\0';
+
+ return str_zone(label, buff, buff_len);
+}
+
+static char* get_filename(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const knot_dname_t *zone,
+ const char *name)
+{
+ assert(name);
+
+ const char *end = name + strlen(name);
+ char out[1024] = "";
+
+ do {
+ // Search for a formatter.
+ const char *pos = strchr(name, '%');
+
+ // If no formatter, copy the rest of the name.
+ if (pos == NULL) {
+ if (strlcat(out, name, sizeof(out)) >= sizeof(out)) {
+ CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
+ return NULL;
+ }
+ break;
+ }
+
+ // Copy constant block.
+ char *block = strndup(name, pos - name);
+ if (block == NULL ||
+ strlcat(out, block, sizeof(out)) >= sizeof(out)) {
+ CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
+ free(block);
+ return NULL;
+ }
+ free(block);
+
+ // Move name pointer behind the formatter.
+ name = pos + 2;
+
+ char buff[512] = "";
+ unsigned idx1, idx2;
+ bool failed = false;
+
+ const char type = *(pos + 1);
+ switch (type) {
+ case '%':
+ strlcat(buff, "%", sizeof(buff));
+ break;
+ case 'c':
+ if (get_index(&name, end, &idx1, &idx2) != KNOT_EOK ||
+ str_char(zone, buff, sizeof(buff), idx1, idx2) != KNOT_EOK) {
+ failed = true;
+ }
+ break;
+ case 'l':
+ if (get_index(&name, end, &idx1, NULL) != KNOT_EOK ||
+ str_label(zone, buff, sizeof(buff), idx1) != KNOT_EOK) {
+ failed = true;
+ }
+ break;
+ case 's':
+ if (str_zone(zone, buff, sizeof(buff)) != KNOT_EOK) {
+ failed = true;
+ }
+ break;
+ case '\0':
+ CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring missing "
+ "trailing zonefile formatter");
+ continue;
+ default:
+ CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring zonefile "
+ "formatter '%%%c'", type);
+ continue;
+ }
+
+ if (failed) {
+ CONF_LOG_ZONE(LOG_WARNING, zone, "failed to process "
+ "zonefile formatter '%%%c'", type);
+ return NULL;
+ }
+
+ if (strlcat(out, buff, sizeof(out)) >= sizeof(out)) {
+ CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
+ return NULL;
+ }
+ } while (name < end);
+
+ // Use storage prefix if not absolute path.
+ if (out[0] == '/') {
+ return strdup(out);
+ } else {
+ conf_val_t val = conf_zone_get_txn(conf, txn, C_STORAGE, zone);
+ char *storage = conf_abs_path(&val, NULL);
+ if (storage == NULL) {
+ return NULL;
+ }
+ char *abs = sprintf_alloc("%s/%s", storage, out);
+ free(storage);
+ return abs;
+ }
+}
+
+char* conf_zonefile_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const knot_dname_t *zone)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ conf_val_t val = conf_zone_get_txn(conf, txn, C_FILE, zone);
+ const char *file = conf_str(&val);
+
+ // Use default zonefile name pattern if not specified.
+ if (file == NULL) {
+ file = "%s.zone";
+ }
+
+ return get_filename(conf, txn, zone, file);
+}
+
+char* conf_db_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *db_type)
+{
+ conf_val_t storage_val = conf_get_txn(conf, txn, C_DB, C_STORAGE);
+ char *storage = conf_abs_path(&storage_val, NULL);
+
+ if (db_type == NULL) {
+ return storage;
+ }
+
+ conf_val_t db_val = conf_get_txn(conf, txn, C_DB, db_type);
+ char *dbdir = conf_abs_path(&db_val, storage);
+ free(storage);
+
+ return dbdir;
+}
+
+char *conf_tls_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *tls_item)
+{
+ conf_val_t tls_val = conf_get_txn(conf, txn, C_SRV, tls_item);
+ if (conf_str(&tls_val) == NULL) {
+ return NULL;
+ }
+
+ return conf_abs_path(&tls_val, CONFIG_DIR);
+}
+
+size_t conf_udp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_UDP_WORKERS);
+ int64_t workers = conf_int(&val);
+ assert(workers <= CONF_MAX_UDP_WORKERS);
+ if (workers == YP_NIL) {
+ return MIN(dt_optimal_size(), CONF_MAX_UDP_WORKERS);
+ }
+
+ return workers;
+}
+
+size_t conf_tcp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_TCP_WORKERS);
+ int64_t workers = conf_int(&val);
+ assert(workers <= CONF_MAX_TCP_WORKERS);
+ if (workers == YP_NIL) {
+ size_t optimal = MAX(dt_optimal_size(), DFLT_MIN_TCP_WORKERS);
+ return MIN(optimal, CONF_MAX_TCP_WORKERS);
+ }
+
+ return workers;
+}
+
+size_t conf_xdp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ size_t workers = 0;
+
+ conf_val_t val = conf_get_txn(conf, txn, C_XDP, C_LISTEN);
+ while (val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&val, NULL);
+ conf_xdp_iface_t iface;
+ int ret = conf_xdp_iface(&addr, &iface);
+ if (ret == KNOT_EOK) {
+ workers += iface.queues;
+ }
+ conf_val_next(&val);
+ }
+
+ return workers;
+}
+
+size_t conf_bg_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_BG_WORKERS);
+ int64_t workers = conf_int(&val);
+ assert(workers <= CONF_MAX_BG_WORKERS);
+ if (workers == YP_NIL) {
+ assert(DFLT_MAX_BG_WORKERS <= CONF_MAX_BG_WORKERS);
+ return MIN(dt_optimal_size(), DFLT_MAX_BG_WORKERS);
+ }
+
+ return workers;
+}
+
+size_t conf_tcp_max_clients_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_TCP_MAX_CLIENTS);
+ int64_t clients = conf_int(&val);
+ if (clients == YP_NIL) {
+ static size_t permval = 0;
+ if (permval == 0) {
+ struct rlimit numfiles;
+ if (getrlimit(RLIMIT_NOFILE, &numfiles) == 0) {
+ permval = (size_t)numfiles.rlim_cur / 2;
+ } else {
+ permval = FALLBACK_MAX_TCP_CLIENTS;
+ }
+ }
+ return permval;
+ }
+
+ return clients;
+}
+
+int conf_user_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ int *uid,
+ int *gid)
+{
+ if (uid == NULL || gid == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_USER);
+ if (val.code == KNOT_EOK) {
+ char *user = strdup(conf_str(&val));
+
+ // Search for user:group separator.
+ char *sep_pos = strchr(user, ':');
+ if (sep_pos != NULL) {
+ // Process group name.
+ struct group *grp = getgrnam(sep_pos + 1);
+ if (grp != NULL) {
+ *gid = grp->gr_gid;
+ } else {
+ CONF_LOG(LOG_ERR, "invalid group name '%s'",
+ sep_pos + 1);
+ free(user);
+ return KNOT_EINVAL;
+ }
+
+ // Cut off group part.
+ *sep_pos = '\0';
+ } else {
+ *gid = getgid();
+ }
+
+ // Process user name.
+ struct passwd *pwd = getpwnam(user);
+ if (pwd != NULL) {
+ *uid = pwd->pw_uid;
+ } else {
+ CONF_LOG(LOG_ERR, "invalid user name '%s'", user);
+ free(user);
+ return KNOT_EINVAL;
+ }
+
+ free(user);
+ return KNOT_EOK;
+ } else if (val.code == KNOT_ENOENT) {
+ *uid = getuid();
+ *gid = getgid();
+ return KNOT_EOK;
+ } else {
+ return val.code;
+ }
+}
+
+conf_remote_t conf_remote_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ conf_val_t *id,
+ size_t index)
+{
+ assert(id != NULL && id->item != NULL);
+ assert(id->item->type == YP_TSTR ||
+ (id->item->type == YP_TREF &&
+ id->item->var.r.ref->var.g.id->type == YP_TSTR));
+
+ conf_remote_t out = { { AF_UNSPEC } };
+
+ conf_val_t rundir_val = conf_get_txn(conf, txn, C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&rundir_val, NULL);
+
+ // Get indexed remote address.
+ conf_val_t val = conf_id_get_txn(conf, txn, C_RMT, C_ADDR, id);
+ for (size_t i = 0; val.code == KNOT_EOK && i < index; i++) {
+ if (i == 0) {
+ conf_val(&val);
+ }
+ conf_val_next(&val);
+ }
+ // Index overflow causes empty socket.
+ out.addr = conf_addr(&val, rundir);
+
+ // Get outgoing address if family matches (optional).
+ val = conf_id_get_txn(conf, txn, C_RMT, C_VIA, id);
+ while (val.code == KNOT_EOK) {
+ struct sockaddr_storage via = conf_addr(&val, rundir);
+ if (via.ss_family == out.addr.ss_family) {
+ out.via = conf_addr(&val, rundir);
+ break;
+ }
+ conf_val_next(&val);
+ }
+
+ // Get TSIG key (optional).
+ conf_val_t key_id = conf_id_get_txn(conf, txn, C_RMT, C_KEY, id);
+ if (key_id.code == KNOT_EOK) {
+ out.key.name = (knot_dname_t *)conf_dname(&key_id);
+
+ val = conf_id_get_txn(conf, txn, C_KEY, C_ALG, &key_id);
+ out.key.algorithm = conf_opt(&val);
+
+ val = conf_id_get_txn(conf, txn, C_KEY, C_SECRET, &key_id);
+ out.key.secret.data = (uint8_t *)conf_bin(&val, &out.key.secret.size);
+ }
+
+ free(rundir);
+
+ val = conf_id_get_txn(conf, txn, C_RMT, C_BLOCK_NOTIFY_XFR, id);
+ out.block_notify_after_xfr = conf_bool(&val);
+
+ val = conf_id_get_txn(conf, txn, C_RMT, C_NO_EDNS, id);
+ out.no_edns = conf_bool(&val);
+
+ return out;
+}
+
+int conf_xdp_iface(
+ struct sockaddr_storage *addr,
+ conf_xdp_iface_t *iface)
+{
+#ifndef ENABLE_XDP
+ return KNOT_ENOTSUP;
+#else
+ if (addr == NULL || iface == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (addr->ss_family == AF_UNIX) {
+ const char *addr_str = ((struct sockaddr_un *)addr)->sun_path;
+ strlcpy(iface->name, addr_str, sizeof(iface->name));
+
+ const char *port = strchr(addr_str, '@');
+ if (port != NULL) {
+ iface->name[port - addr_str] = '\0';
+ int ret = str_to_u16(port + 1, &iface->port);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (iface->port == 0) {
+ return KNOT_EINVAL;
+ }
+ } else {
+ iface->port = 53;
+ }
+ } else {
+ int ret = knot_eth_name_from_addr(addr, iface->name, sizeof(iface->name));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ret = sockaddr_port(addr);
+ if (ret < 0) { // Cannot check for 0 as don't know if port specified.
+ return KNOT_EINVAL;
+ }
+ iface->port = ret;
+ }
+
+ int queues = knot_eth_queues(iface->name);
+ if (queues <= 0) {
+ assert(queues != 0);
+ return queues;
+ }
+ iface->queues = queues;
+
+ return KNOT_EOK;
+#endif
+}
diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h
new file mode 100644
index 0000000..83dfd1d
--- /dev/null
+++ b/src/knot/conf/conf.h
@@ -0,0 +1,939 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+
+#include "knot/conf/base.h"
+#include "knot/conf/schema.h"
+
+/*! Configuration remote getter output. */
+typedef struct {
+ /*! Target socket address. */
+ struct sockaddr_storage addr;
+ /*! Local outgoing socket address. */
+ struct sockaddr_storage via;
+ /*! TSIG key. */
+ knot_tsig_key_t key;
+ /*! Suppress sending NOTIFY after zone transfer from this master. */
+ bool block_notify_after_xfr;
+ /*! Disable EDNS on XFR queries. */
+ bool no_edns;
+} conf_remote_t;
+
+/*! Configuration section iterator. */
+typedef struct {
+ /*! Item description. */
+ const yp_item_t *item;
+ /*! Namedb iterator. */
+ knot_db_iter_t *iter;
+ /*! Key0 database code. */
+ uint8_t key0_code;
+ // Public items.
+ /*! Iterator return code. */
+ int code;
+} conf_iter_t;
+
+/*! Configuration iterator over mixed references (e.g. remote and remotes). */
+typedef struct {
+ /*! Configuration context. */
+ conf_t *conf;
+ /*! Mixed references. */
+ conf_val_t *mix_id;
+ /*! Temporary nested references. */
+ conf_val_t sub_id;
+ /*! Current (possibly expanded) reference to use. */
+ conf_val_t *id;
+ /*! Nested references in use indication. */
+ bool nested;
+} conf_mix_iter_t;
+
+/*! Configuration module getter output. */
+typedef struct {
+ /*! Module name. */
+ yp_name_t *name;
+ /*! Module id data. */
+ uint8_t *data;
+ /*! Module id data length. */
+ size_t len;
+} conf_mod_id_t;
+
+/*!
+ * Check if the configuration database exists on the filesystem.
+ *
+ * \param[in] db_dir Database path.
+ *
+ * \return True if it already exists.
+ */
+
+bool conf_db_exists(
+ const char *db_dir
+);
+
+/*!
+ * Gets the configuration item value of the section without identifiers.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ * \param[in] key1_name Item name.
+ *
+ * \return Item value.
+ */
+conf_val_t conf_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name
+);
+static inline conf_val_t conf_get(
+ conf_t *conf,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name)
+{
+ return conf_get_txn(conf, &conf->read_txn, key0_name, key1_name);
+}
+
+/*!
+ * Gets the configuration item value of the section with identifiers (raw version).
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ * \param[in] key1_name Item name.
+ * \param[in] id Section identifier (raw value).
+ * \param[in] id_len Length of the section identifier.
+ *
+ * \return Item value.
+ */
+conf_val_t conf_rawid_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ const uint8_t *id,
+ size_t id_len
+);
+static inline conf_val_t conf_rawid_get(
+ conf_t *conf,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ const uint8_t *id,
+ size_t id_len)
+{
+ return conf_rawid_get_txn(conf, &conf->read_txn, key0_name, key1_name,
+ id, id_len);
+}
+
+/*!
+ * Gets the configuration item value of the section with identifiers.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ * \param[in] key1_name Item name.
+ * \param[in] id Section identifier (output of a config getter).
+ *
+ * \return Item value.
+ */
+conf_val_t conf_id_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ conf_val_t *id
+);
+static inline conf_val_t conf_id_get(
+ conf_t *conf,
+ const yp_name_t *key0_name,
+ const yp_name_t *key1_name,
+ conf_val_t *id)
+{
+ return conf_id_get_txn(conf, &conf->read_txn, key0_name, key1_name, id);
+}
+
+/*!
+ * Gets the configuration item value of the module section.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key1_name Item name.
+ * \param[in] mod_id Module identifier.
+ *
+ * \return Item value.
+ */
+conf_val_t conf_mod_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name,
+ const conf_mod_id_t *mod_id
+);
+static inline conf_val_t conf_mod_get(
+ conf_t *conf,
+ const yp_name_t *key1_name,
+ const conf_mod_id_t *mod_id)
+{
+ return conf_mod_get_txn(conf, &conf->read_txn, key1_name, mod_id);
+}
+
+/*!
+ * Gets the configuration item value of the zone section.
+ *
+ * \note A possibly associated template is taken into account.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key1_name Item name.
+ * \param[in] dname Zone name.
+ *
+ * \return Item value.
+ */
+conf_val_t conf_zone_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name,
+ const knot_dname_t *dname
+);
+static inline conf_val_t conf_zone_get(
+ conf_t *conf,
+ const yp_name_t *key1_name,
+ const knot_dname_t *dname)
+{
+ return conf_zone_get_txn(conf, &conf->read_txn, key1_name, dname);
+}
+
+/*!
+ * Gets the configuration item value of the default template.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key1_name Item name.
+ *
+ * \return Item value.
+ */
+conf_val_t conf_default_get_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key1_name
+);
+static inline conf_val_t conf_default_get(
+ conf_t *conf,
+ const yp_name_t *key1_name)
+{
+ return conf_default_get_txn(conf, &conf->read_txn, key1_name);
+}
+
+/*!
+ * Checks the configuration section for the identifier (raw version).
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ * \param[in] id Section identifier (raw value).
+ * \param[in] id_len Length of the section identifier.
+ *
+ * \return True if exists.
+ */
+bool conf_rawid_exists_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ const uint8_t *id,
+ size_t id_len
+);
+static inline bool conf_rawid_exists(
+ conf_t *conf,
+ const yp_name_t *key0_name,
+ const uint8_t *id,
+ size_t id_len)
+{
+ return conf_rawid_exists_txn(conf, &conf->read_txn, key0_name, id, id_len);
+}
+
+/*!
+ * Checks the configuration section for the identifier.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ * \param[in] id Section identifier (output of a config getter).
+ *
+ * \return True if exists.
+ */
+bool conf_id_exists_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name,
+ conf_val_t *id
+);
+static inline bool conf_id_exists(
+ conf_t *conf,
+ const yp_name_t *key0_name,
+ conf_val_t *id)
+{
+ return conf_id_exists_txn(conf, &conf->read_txn, key0_name, id);
+}
+
+/*!
+ * Gets the number of section identifiers.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ *
+ * \return Number of identifiers.
+ */
+size_t conf_id_count_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name
+);
+static inline size_t conf_id_count(
+ conf_t *conf,
+ const yp_name_t *key0_name)
+{
+ return conf_id_count_txn(conf, &conf->read_txn, key0_name);
+}
+
+/*!
+ * Gets a configuration section iterator.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0_name Section name.
+ *
+ * \return Section iterator.
+ */
+conf_iter_t conf_iter_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0_name
+);
+static inline conf_iter_t conf_iter(
+ conf_t *conf,
+ const yp_name_t *key0_name)
+{
+ return conf_iter_txn(conf, &conf->read_txn, key0_name);
+}
+
+/*!
+ * Moves the configuration section iterator to the next identifier.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] iter Configuration iterator.
+ */
+void conf_iter_next(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Gets the current iterator value (identifier).
+ *
+ * \param[in] conf Configuration.
+ * \param[in] iter Configuration iterator.
+ *
+ * \return Section identifier.
+ */
+conf_val_t conf_iter_id(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Deletes the section iterator.
+ *
+ * This function should be called when the iterating is early interrupted,
+ * otherwise this is done automatically at KNOT_EOF.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] iter Configuration iterator.
+ */
+void conf_iter_finish(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Prepares the value for the direct access.
+ *
+ * The following access is through val->len and val->data.
+ *
+ * \param[in] val Item value.
+ */
+void conf_val(
+ conf_val_t *val
+);
+
+/*!
+ * Moves to the next value of a multi-valued item.
+ *
+ * \param[in] val Item value.
+ */
+void conf_val_next(
+ conf_val_t *val
+);
+
+/*!
+ * Resets to the first value of a multi-valued item.
+ *
+ * \param[in] val Item value.
+ */
+void conf_val_reset(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the number of values if multivalued item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Number of values.
+ */
+size_t conf_val_count(
+ conf_val_t *val
+);
+
+/*!
+ * Checks if two item values are equal.
+ *
+ * \param[in] val1 First item value.
+ * \param[in] val2 Second item value.
+ *
+ * \return true if equal, false if not.
+ */
+bool conf_val_equal(
+ conf_val_t *val1,
+ conf_val_t *val2
+);
+
+/*!
+ * Initializes a mixed reference iterator.
+ *
+ * The following access is through iter->id.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] mix_id First mixed reference.
+ * \param[out] iter Iterator to be initialized.
+ */
+void conf_mix_iter_init(
+ conf_t *conf,
+ conf_val_t *mix_id,
+ conf_mix_iter_t *iter
+);
+
+/*!
+ * Increments the mixed iterator.
+ *
+ * \param[in] iter Mixed reference iterator.
+ */
+void conf_mix_iter_next(
+ conf_mix_iter_t *iter
+);
+
+/*!
+ * Gets the numeric value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Integer.
+ */
+int64_t conf_int(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the boolean value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Boolean.
+ */
+bool conf_bool(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the option value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Option id.
+ */
+unsigned conf_opt(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the string value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return String pointer.
+ */
+const char* conf_str(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the dname value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Dname pointer.
+ */
+const knot_dname_t* conf_dname(
+ conf_val_t *val
+);
+
+/*!
+ * Gets the length-prefixed data value of the item.
+ *
+ * \param[in] val Item value.
+ * \param[out] len Output length.
+ *
+ * \return Data pointer.
+ */
+const uint8_t* conf_bin(
+ conf_val_t *val,
+ size_t *len
+);
+
+/*!
+ * Gets the generic data value of the item.
+ *
+ * \param[in] val Item value.
+ * \param[out] len Output length.
+ *
+ * \return Data pointer.
+ */
+const uint8_t* conf_data(
+ conf_val_t *val,
+ size_t *len
+);
+
+/*!
+ * Gets the socket address value of the item.
+ *
+ * \param[in] val Item value.
+ * \param[in] sock_base_dir Path prefix for a relative UNIX socket location.
+ *
+ * \return Socket address.
+ */
+struct sockaddr_storage conf_addr(
+ conf_val_t *val,
+ const char *sock_base_dir
+);
+
+/*!
+ * Checks the configured address if equal to given one (except port).
+ *
+ * \param[in] match Configured address.
+ * \param[in] addr Address to check.
+ *
+ * \return True if matches.
+ */
+bool conf_addr_match(
+ conf_val_t *match,
+ const struct sockaddr_storage *addr
+);
+
+/*!
+ * Gets the socket address range value of the item.
+ *
+ * \param[in] val Item value.
+ * \param[out] max_ss Upper address bound or AF_UNSPEC family if not specified.
+ * \param[out] prefix_len Network subnet prefix length or -1 if not specified.
+ *
+ * \return Socket address.
+ */
+struct sockaddr_storage conf_addr_range(
+ conf_val_t *val,
+ struct sockaddr_storage *max_ss,
+ int *prefix_len
+);
+
+/*!
+ * Checks the address if matches given address range/network block.
+ *
+ * \param[in] range Address range/network block.
+ * \param[in] addr Address to check.
+ *
+ * \return True if matches.
+ */
+bool conf_addr_range_match(
+ conf_val_t *range,
+ const struct sockaddr_storage *addr
+);
+
+/*!
+ * Gets the absolute string value of the item.
+ *
+ * \note The result must be explicitly deallocated.
+ *
+ * \param[in] val Item value.
+ * \param[in] base_dir Path prefix for a relative string.
+ *
+ * \return Absolute path string pointer.
+ */
+char* conf_abs_path(
+ conf_val_t *val,
+ const char *base_dir
+);
+
+/*!
+ * Ensures empty 'default' identifier value.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Empty item value.
+ */
+static inline void conf_id_fix_default(conf_val_t *val)
+{
+ if (val->code != KNOT_EOK) {
+ conf_val_t empty = {
+ .item = val->item,
+ .code = KNOT_EOK
+ };
+
+ *val = empty;
+ }
+}
+
+/*!
+ * Gets the module identifier value of the item.
+ *
+ * \param[in] val Item value.
+ *
+ * \return Module identifier.
+ */
+conf_mod_id_t* conf_mod_id(
+ conf_val_t *val
+);
+
+/*!
+ * Destroys the module identifier.
+ *
+ * \param[in] mod_id Module identifier.
+ */
+void conf_free_mod_id(
+ conf_mod_id_t *mod_id
+);
+
+/*!
+ * Gets the absolute zone file path.
+ *
+ * \note The result must be explicitly deallocated.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] zone Zone name.
+ *
+ * \return Absolute zone file path string pointer.
+ */
+char* conf_zonefile_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const knot_dname_t *zone
+);
+static inline char* conf_zonefile(
+ conf_t *conf,
+ const knot_dname_t *zone)
+{
+ return conf_zonefile_txn(conf, &conf->read_txn, zone);
+}
+
+/*!
+ * Gets the absolute directory path for a database.
+ *
+ * e.g. Journal, KASP db, Timers
+ *
+ * \note The result must be explicitly deallocated.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] db_type Database name.
+ *
+ * \return Absolute database path string pointer.
+ */
+char* conf_db_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *db_type
+);
+static inline char* conf_db(
+ conf_t *conf,
+ const yp_name_t *db_type)
+{
+ return conf_db_txn(conf, &conf->read_txn, db_type);
+}
+
+/*!
+ * Gets the absolute directory path for a TLS key/cert file.
+ *
+ * \note The result must be explicitly deallocated.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] db_type TLS configuration option.
+ *
+ * \return Absolute path string pointer.
+ */
+char *conf_tls_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *tls_item);
+static inline char* conf_tls(
+ conf_t *conf,
+ const yp_name_t *tls_item)
+{
+ return conf_tls_txn(conf, &conf->read_txn, tls_item);
+}
+
+/*!
+ * Gets database-specific parameter.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] param Parameter name.
+ *
+ * \return Item value.
+ */
+static inline conf_val_t conf_db_param(
+ conf_t *conf,
+ const yp_name_t *param)
+{
+ return conf_get_txn(conf, &conf->read_txn, C_DB, param);
+}
+
+/*!
+ * Gets the configured setting of the bool option in the specified section.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] section Section name.
+ * \param[in] param Parameter name.
+ *
+ * \return True if enabled, false otherwise.
+ */
+static inline bool conf_get_bool(
+ conf_t *conf,
+ const yp_name_t *section,
+ const yp_name_t *param)
+{
+ conf_val_t val = conf_get_txn(conf, &conf->read_txn, section, param);
+ return conf_bool(&val);
+}
+
+/*!
+ * Gets the configured setting of the int option in the specified section.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] section Section name.
+ * \param[in] param Parameter name.
+ *
+ * \return Configured integer value.
+ */
+static inline int64_t conf_get_int(
+ conf_t *conf,
+ const yp_name_t *section,
+ const yp_name_t *param)
+{
+ conf_val_t val = conf_get_txn(conf, &conf->read_txn, section, param);
+ return conf_int(&val);
+}
+
+/*!
+ * Gets the configured number of UDP threads.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Number of threads.
+ */
+size_t conf_udp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+static inline size_t conf_udp_threads(
+ conf_t *conf)
+{
+ return conf_udp_threads_txn(conf, &conf->read_txn);
+}
+
+/*!
+ * Gets the configured number of TCP threads.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Number of threads.
+ */
+size_t conf_tcp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+static inline size_t conf_tcp_threads(
+ conf_t *conf)
+{
+ return conf_tcp_threads_txn(conf, &conf->read_txn);
+}
+
+/*!
+ * Gets the number of used XDP threads.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Number of threads.
+ */
+size_t conf_xdp_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+static inline size_t conf_xdp_threads(
+ conf_t *conf)
+{
+ return conf_xdp_threads_txn(conf, &conf->read_txn);
+}
+
+/*!
+ * Gets the configured number of worker threads.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Number of threads.
+ */
+size_t conf_bg_threads_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+static inline size_t conf_bg_threads(
+ conf_t *conf)
+{
+ return conf_bg_threads_txn(conf, &conf->read_txn);
+}
+
+/*!
+ * Gets the required LMDB readers limit based on the current configuration.
+ *
+ * \note The resulting value is a common limit to journal, kasp, timers,
+ * and catalog databases. So it's over-estimated for simplicity reasons.
+ *
+ * \note This function cannot be used for the configuration database setting :-/
+ *
+ * \param[in] conf Configuration.
+ *
+ * \return Number of readers.
+ */
+static inline size_t conf_lmdb_readers(
+ conf_t *conf)
+{
+ if (conf == NULL) { // Return default in tests.
+ return 126;
+ }
+ return conf_udp_threads(conf) + conf_tcp_threads(conf) +
+ conf_bg_threads(conf) + conf_xdp_threads(conf) + 2; // Main thread, utils.
+}
+
+/*!
+ * Gets the configured maximum number of TCP clients.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Maximum number of TCP clients.
+ */
+size_t conf_tcp_max_clients_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+static inline size_t conf_tcp_max_clients(
+ conf_t *conf)
+{
+ return conf_tcp_max_clients_txn(conf, &conf->read_txn);
+}
+
+/*!
+ * Gets the configured user and group identifiers.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[out] uid User identifier.
+ * \param[out] gid Group identifier.
+ *
+ * \return Knot error code.
+ */
+int conf_user_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ int *uid,
+ int *gid
+);
+static inline int conf_user(
+ conf_t *conf,
+ int *uid,
+ int *gid)
+{
+ return conf_user_txn(conf, &conf->read_txn, uid, gid);
+}
+
+/*!
+ * Gets the remote parameters for the given identifier.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] id Remote identifier.
+ * \param[in] index Remote index (counted from 0).
+ *
+ * \return Remote parameters.
+ */
+conf_remote_t conf_remote_txn(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ conf_val_t *id,
+ size_t index
+);
+static inline conf_remote_t conf_remote(
+ conf_t *conf,
+ conf_val_t *id,
+ size_t index)
+{
+ return conf_remote_txn(conf, &conf->read_txn, id, index);
+}
+
+/*! XDP interface parameters. */
+typedef struct {
+ /*! Interface name. */
+ char name[32];
+ /*! UDP port to listen on. */
+ uint16_t port;
+ /*! Number of active IO queues. */
+ uint16_t queues;
+} conf_xdp_iface_t;
+
+/*!
+ * Gets the XDP interface parameters for a given configuration value.
+ *
+ * \param[in] addr XDP interface name stored in the configuration.
+ * \param[out] iface Interface parameters.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_xdp_iface(
+ struct sockaddr_storage *addr,
+ conf_xdp_iface_t *iface
+);
diff --git a/src/knot/conf/confdb.c b/src/knot/conf/confdb.c
new file mode 100644
index 0000000..e1262c2
--- /dev/null
+++ b/src/knot/conf/confdb.c
@@ -0,0 +1,951 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "knot/conf/confdb.h"
+#include "libknot/errcode.h"
+#include "libknot/yparser/yptrafo.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/wire_ctx.h"
+
+/*
+ * A simple configuration:
+ *
+ * server.identity: "knot"
+ * server.version: "version"
+ * template[tpl1].storage: "directory1"
+ * template[tpl2].storage: "directory2"
+ * template[tpl2].master: [ "master1", "master2" ]
+ *
+ * And the corresponding configuration DB content:
+ *
+ * # DB structure version.
+ * [00][FF]: [02]
+ * # Sections codes.
+ * [00][00]server: [02]
+ * [00][00]template: [03]
+ * # Server section items codes.
+ * [02][00]identity: [02]
+ * [02][00]version: [03]
+ * # Server items values.
+ * [02][02]: knot\0
+ * [02][03]: version\0
+ * # Template section items codes.
+ * [03][00]master: [03]
+ * [03][00]storage: [02]
+ * # Template identifiers.
+ * [03][01]tpl1\0
+ * [03][01]tpl2\0
+ * # Template items values.
+ * [03][02]tpl1\0: directory1\0
+ * [03][02]tpl2\0: directory2\0
+ * [03][03]tpl2\0: [00][08]master1\0 [00][08]master2\0
+ */
+
+typedef enum {
+ KEY0_ROOT = 0,
+ KEY1_ITEMS = 0,
+ KEY1_ID = 1,
+ KEY1_FIRST = 2,
+ KEY1_LAST = 200,
+ KEY1_VERSION = 255
+} db_code_t;
+
+typedef enum {
+ KEY0_POS = 0,
+ KEY1_POS = 1,
+ NAME_POS = 2
+} db_code_pos_t;
+
+typedef enum {
+ DB_GET,
+ DB_SET,
+ DB_DEL
+} db_action_t;
+
+static int db_check_version(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ uint8_t k[2] = { KEY0_ROOT, KEY1_VERSION };
+ knot_db_val_t key = { k, sizeof(k) };
+ knot_db_val_t data;
+
+ // Get conf-DB version.
+ int ret = conf->api->find(txn, &key, &data, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check conf-DB version.
+ if (data.len != 1 || ((uint8_t *)data.data)[0] != CONF_DB_VERSION) {
+ return KNOT_CONF_EVERSION;
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_db_init(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ bool purge)
+{
+ if (conf == NULL || txn == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t k[2] = { KEY0_ROOT, KEY1_VERSION };
+ knot_db_val_t key = { k, sizeof(k) };
+
+ int ret = conf->api->count(txn);
+ if (ret == 0) { // Initialize empty DB with DB version.
+ uint8_t d[1] = { CONF_DB_VERSION };
+ knot_db_val_t data = { d, sizeof(d) };
+ return conf->api->insert(txn, &key, &data, 0);
+ } else if (ret > 0) { // Non-empty DB.
+ if (purge) {
+ // Purge the DB.
+ ret = conf->api->clear(txn);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ return conf_db_init(conf, txn, false);
+ }
+ return KNOT_EOK;
+ } else { // DB error.
+ return ret;
+ }
+}
+
+int conf_db_check(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ int ret = conf->api->count(txn);
+ if (ret == 0) { // Not initialized DB.
+ return KNOT_CONF_ENOTINIT;
+ } else if (ret > 0) { // Check the DB.
+ int count = ret;
+
+ ret = db_check_version(conf, txn);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (count == 1) {
+ return KNOT_EOK; // Empty but initialized DB.
+ } else {
+ return count - 1; // Non-empty DB.
+ }
+ } else { // DB error.
+ return ret;
+ }
+}
+
+static int db_code(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ uint8_t section_code,
+ const yp_name_t *name,
+ db_action_t action,
+ uint8_t *code)
+{
+ if (name == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_db_val_t key;
+ uint8_t k[CONF_MIN_KEY_LEN + YP_MAX_ITEM_NAME_LEN];
+ k[KEY0_POS] = section_code;
+ k[KEY1_POS] = KEY1_ITEMS;
+ memcpy(k + NAME_POS, name + 1, name[0]);
+ key.data = k;
+ key.len = CONF_MIN_KEY_LEN + name[0];
+
+ // Check if the item is already registered.
+ knot_db_val_t data;
+ int ret = conf->api->find(txn, &key, &data, 0);
+ switch (ret) {
+ case KNOT_EOK:
+ if (action == DB_DEL) {
+ return conf->api->del(txn, &key);
+ }
+ if (code != NULL) {
+ *code = ((uint8_t *)data.data)[0];
+ }
+ return KNOT_EOK;
+ case KNOT_ENOENT:
+ if (action != DB_SET) {
+ return KNOT_ENOENT;
+ }
+ break;
+ default:
+ return ret;
+ }
+
+ // Reduce the key to common prefix only.
+ key.len = CONF_MIN_KEY_LEN;
+
+ bool codes[KEY1_LAST + 1] = { false };
+
+ // Find all used item codes.
+ knot_db_iter_t *it = conf->api->iter_begin(txn, KNOT_DB_NOOP);
+ it = conf->api->iter_seek(it, &key, KNOT_DB_GEQ);
+ while (it != NULL) {
+ knot_db_val_t iter_key;
+ ret = conf->api->iter_key(it, &iter_key);
+ if (ret != KNOT_EOK) {
+ conf->api->iter_finish(it);
+ return ret;
+ }
+ uint8_t *key_data = (uint8_t *)iter_key.data;
+
+ // Check for database prefix end.
+ if (key_data[KEY0_POS] != k[KEY0_POS] ||
+ key_data[KEY1_POS] != k[KEY1_POS]) {
+ break;
+ }
+
+ knot_db_val_t iter_val;
+ ret = conf->api->iter_val(it, &iter_val);
+ if (ret != KNOT_EOK) {
+ conf->api->iter_finish(it);
+ return ret;
+ }
+ uint8_t used_code = ((uint8_t *)iter_val.data)[0];
+ codes[used_code] = true;
+
+ it = conf->api->iter_next(it);
+ }
+ conf->api->iter_finish(it);
+
+ // Find the smallest unused item code.
+ uint8_t new_code = KEY1_FIRST;
+ while (codes[new_code]) {
+ new_code++;
+ if (new_code > KEY1_LAST) {
+ return KNOT_ESPACE;
+ }
+ }
+
+ // Restore the full key.
+ key.len = CONF_MIN_KEY_LEN + name[0];
+
+ // Fill the data with a new code.
+ data.data = &new_code;
+ data.len = sizeof(new_code);
+
+ // Register new item code.
+ ret = conf->api->insert(txn, &key, &data, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (code != NULL) {
+ *code = new_code;
+ }
+
+ return KNOT_EOK;
+}
+
+static uint8_t *find_data(
+ const knot_db_val_t *value,
+ const knot_db_val_t *current)
+{
+ wire_ctx_t ctx = wire_ctx_init_const(current->data, current->len);
+
+ // Loop over the data array. Each item has 2B length prefix.
+ while (wire_ctx_available(&ctx) > 0) {
+ uint16_t len = wire_ctx_read_u16(&ctx);
+ assert(ctx.error == KNOT_EOK);
+
+ // Check for the same data.
+ if (len == value->len &&
+ (len == 0 || memcmp(ctx.position, value->data, value->len) == 0)) {
+ wire_ctx_skip(&ctx, -sizeof(uint16_t));
+ assert(ctx.error == KNOT_EOK);
+ return ctx.position;
+ }
+ wire_ctx_skip(&ctx, len);
+ }
+
+ assert(ctx.error == KNOT_EOK && wire_ctx_available(&ctx) == 0);
+
+ return NULL;
+}
+
+static int db_set(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ knot_db_val_t *key,
+ knot_db_val_t *data,
+ bool multi)
+{
+ if (!multi) {
+ if (data->len > CONF_MAX_DATA_LEN) {
+ return KNOT_ERANGE;
+ }
+
+ // Insert new (overwrite old) data.
+ return conf->api->insert(txn, key, data, 0);
+ }
+
+ knot_db_val_t d;
+
+ if (data->len > UINT16_MAX) {
+ return KNOT_ERANGE;
+ }
+
+ int ret = conf->api->find(txn, key, &d, 0);
+ if (ret == KNOT_ENOENT) {
+ d.len = 0;
+ } else if (ret == KNOT_EOK) {
+ // Check for duplicate data.
+ if (find_data(data, &d) != NULL) {
+ return KNOT_EOK;
+ }
+ } else {
+ return ret;
+ }
+
+ // Prepare buffer for all data.
+ size_t new_len = d.len + sizeof(uint16_t) + data->len;
+ if (new_len > CONF_MAX_DATA_LEN) {
+ return KNOT_ESPACE;
+ }
+
+ uint8_t *new_data = malloc(new_len);
+ if (new_data == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ wire_ctx_t ctx = wire_ctx_init(new_data, new_len);
+
+ // Copy current data array.
+ wire_ctx_write(&ctx, d.data, d.len);
+ // Copy length prefix for the new data item.
+ wire_ctx_write_u16(&ctx, data->len);
+ // Copy the new data item.
+ wire_ctx_write(&ctx, data->data, data->len);
+
+ assert(ctx.error == KNOT_EOK && wire_ctx_available(&ctx) == 0);
+
+ d.data = new_data;
+ d.len = new_len;
+
+ // Insert new (or append) data.
+ ret = conf->api->insert(txn, key, &d, 0);
+
+ free(new_data);
+
+ return ret;
+}
+
+int conf_db_set(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ const uint8_t *data,
+ size_t data_len)
+{
+ if (conf == NULL || txn == NULL || key0 == NULL ||
+ (id == NULL && id_len > 0) || (data == NULL && data_len > 0)) {
+ return KNOT_EINVAL;
+ }
+
+ // Check for valid keys.
+ const yp_item_t *item = yp_schema_find(key1 != NULL ? key1 : key0,
+ key1 != NULL ? key0 : NULL,
+ conf->schema);
+ if (item == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Ignore alone key0 insertion.
+ if (key1 == NULL && id_len == 0) {
+ return KNOT_EOK;
+ }
+
+ // Ignore group id as a key1.
+ if (item->parent != NULL && (item->parent->flags & YP_FMULTI) != 0 &&
+ item->parent->var.g.id == item) {
+ key1 = NULL;
+ }
+
+ uint8_t k[CONF_MAX_KEY_LEN] = { 0 };
+ knot_db_val_t key = { k, CONF_MIN_KEY_LEN };
+
+ // Set key0 code.
+ int ret = db_code(conf, txn, KEY0_ROOT, key0, DB_SET, &k[KEY0_POS]);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Set id part.
+ if (id_len > 0) {
+ if (id_len > YP_MAX_ID_LEN) {
+ return KNOT_YP_EINVAL_ID;
+ }
+ memcpy(k + CONF_MIN_KEY_LEN, id, id_len);
+ key.len += id_len;
+
+ k[KEY1_POS] = KEY1_ID;
+ knot_db_val_t val = { NULL };
+
+ // Insert id.
+ if (key1 == NULL) {
+ ret = conf->api->find(txn, &key, &val, 0);
+ if (ret == KNOT_EOK) {
+ return KNOT_CONF_EREDEFINE;
+ }
+ ret = db_set(conf, txn, &key, &val, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ // Check for existing id.
+ } else {
+ ret = conf->api->find(txn, &key, &val, 0);
+ if (ret != KNOT_EOK) {
+ return KNOT_YP_EINVAL_ID;
+ }
+ }
+ }
+
+ // Insert key1 data.
+ if (key1 != NULL) {
+ // Set key1 code.
+ ret = db_code(conf, txn, k[KEY0_POS], key1, DB_SET, &k[KEY1_POS]);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_db_val_t val = { (uint8_t *)data, data_len };
+ ret = db_set(conf, txn, &key, &val, item->flags & YP_FMULTI);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int db_unset(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ knot_db_val_t *key,
+ knot_db_val_t *data,
+ bool multi)
+{
+ // No item data can be zero length.
+ if (data->len == 0) {
+ return conf->api->del(txn, key);
+ }
+
+ knot_db_val_t d;
+
+ int ret = conf->api->find(txn, key, &d, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Process singlevalued data.
+ if (!multi) {
+ if (d.len != data->len ||
+ memcmp((uint8_t *)d.data, data->data, d.len) != 0) {
+ return KNOT_ENOENT;
+ }
+ return conf->api->del(txn, key);
+ }
+
+ // Check if the data exists.
+ uint8_t *pos = find_data(data, &d);
+ if (pos == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ // Prepare buffer for reduced data.
+ size_t total_len = d.len - sizeof(uint16_t) - data->len;
+ if (total_len == 0) {
+ return conf->api->del(txn, key);
+ }
+
+ uint8_t *new_data = malloc(total_len);
+ if (new_data == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ size_t new_len = 0;
+
+ // Copy leading data block.
+ assert(pos >= (uint8_t *)d.data);
+ size_t head_len = pos - (uint8_t *)d.data;
+ if (head_len > 0) {
+ memcpy(new_data, d.data, head_len);
+ new_len += head_len;
+ }
+
+ pos += sizeof(uint16_t) + data->len;
+
+ // Copy trailing data block.
+ assert(pos <= (uint8_t *)d.data + d.len);
+ size_t tail_len = (uint8_t *)d.data + d.len - pos;
+ if (tail_len > 0) {
+ memcpy(new_data + new_len, pos, tail_len);
+ new_len += tail_len;
+ }
+
+ d.data = new_data;
+ d.len = new_len;
+
+ // Insert reduced data.
+ ret = conf->api->insert(txn, key, &d, 0);
+
+ free(new_data);
+
+ return ret;
+}
+
+int conf_db_unset(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ const uint8_t *data,
+ size_t data_len,
+ bool delete_key1)
+{
+ if (conf == NULL || txn == NULL || key0 == NULL ||
+ (id == NULL && id_len > 0) || (data == NULL && data_len > 0)) {
+ return KNOT_EINVAL;
+ }
+
+ // Check for valid keys.
+ const yp_item_t *item = yp_schema_find(key1 != NULL ? key1 : key0,
+ key1 != NULL ? key0 : NULL,
+ conf->schema);
+ if (item == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Delete the key0.
+ if (key1 == NULL && id_len == 0) {
+ return db_code(conf, txn, KEY0_ROOT, key0, DB_DEL, NULL);
+ }
+
+ // Ignore group id as a key1.
+ if (item->parent != NULL && (item->parent->flags & YP_FMULTI) != 0 &&
+ item->parent->var.g.id == item) {
+ key1 = NULL;
+ }
+
+ uint8_t k[CONF_MAX_KEY_LEN] = { 0 };
+ knot_db_val_t key = { k, CONF_MIN_KEY_LEN };
+
+ // Set the key0 code.
+ int ret = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &k[KEY0_POS]);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Set the id part.
+ if (id_len > 0) {
+ if (id_len > YP_MAX_ID_LEN) {
+ return KNOT_YP_EINVAL_ID;
+ }
+ memcpy(k + CONF_MIN_KEY_LEN, id, id_len);
+ key.len += id_len;
+
+ k[KEY1_POS] = KEY1_ID;
+ knot_db_val_t val = { NULL };
+
+ // Delete the id.
+ if (key1 == NULL) {
+ return conf->api->del(txn, &key);
+ // Check for existing id.
+ } else {
+ ret = conf->api->find(txn, &key, &val, 0);
+ if (ret != KNOT_EOK) {
+ return KNOT_YP_EINVAL_ID;
+ }
+ }
+ }
+
+ if (key1 != NULL) {
+ // Set the key1 code.
+ ret = db_code(conf, txn, k[KEY0_POS], key1, DB_GET, &k[KEY1_POS]);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Delete the key1.
+ if (data_len == 0 && delete_key1) {
+ ret = db_code(conf, txn, k[KEY0_POS], key1, DB_DEL, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ // Delete the item data.
+ } else {
+ knot_db_val_t val = { (uint8_t *)data, data_len };
+ ret = db_unset(conf, txn, &key, &val, item->flags & YP_FMULTI);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_db_get(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ conf_val_t *data)
+{
+ conf_val_t out = { NULL };
+
+ if (conf == NULL || txn == NULL || key0 == NULL ||
+ (id == NULL && id_len > 0)) {
+ out.code = KNOT_EINVAL;
+ goto get_error;
+ }
+
+ // Check for valid keys.
+ out.item = yp_schema_find(key1 != NULL ? key1 : key0,
+ key1 != NULL ? key0 : NULL,
+ conf->schema);
+ if (out.item == NULL) {
+ out.code = KNOT_YP_EINVAL_ITEM;
+ goto get_error;
+ }
+
+ // At least key1 or id must be specified.
+ if (key1 == NULL && id_len == 0) {
+ out.code = KNOT_EINVAL;
+ goto get_error;
+ }
+
+ // Ignore group id as a key1.
+ if (out.item->parent != NULL && (out.item->parent->flags & YP_FMULTI) != 0 &&
+ out.item->parent->var.g.id == out.item) {
+ key1 = NULL;
+ }
+
+ uint8_t k[CONF_MAX_KEY_LEN] = { 0 };
+ knot_db_val_t key = { k, CONF_MIN_KEY_LEN };
+ knot_db_val_t val = { NULL };
+
+ // Set the key0 code.
+ out.code = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &k[KEY0_POS]);
+ if (out.code != KNOT_EOK) {
+ if (id_len > 0) {
+ out.code = KNOT_YP_EINVAL_ID;
+ }
+ goto get_error;
+ }
+
+ // Set the id part.
+ if (id_len > 0) {
+ if (id_len > YP_MAX_ID_LEN) {
+ out.code = KNOT_YP_EINVAL_ID;
+ goto get_error;
+ }
+ memcpy(k + CONF_MIN_KEY_LEN, id, id_len);
+ key.len += id_len;
+
+ k[KEY1_POS] = KEY1_ID;
+
+ // Check for existing id.
+ out.code = conf->api->find(txn, &key, &val, 0);
+ if (out.code != KNOT_EOK) {
+ out.code = KNOT_YP_EINVAL_ID;
+ goto get_error;
+ }
+ }
+
+ // Set the key1 code.
+ if (key1 != NULL) {
+ out.code = db_code(conf, txn, k[KEY0_POS], key1, DB_GET, &k[KEY1_POS]);
+ if (out.code != KNOT_EOK) {
+ goto get_error;
+ }
+ }
+
+ // Get the data.
+ out.code = conf->api->find(txn, &key, &val, 0);
+ if (out.code == KNOT_EOK) {
+ out.blob = val.data;
+ out.blob_len = val.len;
+ }
+get_error:
+ // Set the output.
+ if (data != NULL) {
+ *data = out;
+ }
+
+ return out.code;
+}
+
+static int check_iter(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ knot_db_val_t key;
+
+ // Get the current key.
+ int ret = conf->api->iter_key(iter->iter, &key);
+ if (ret != KNOT_EOK) {
+ return KNOT_ENOENT;
+ }
+ uint8_t *key_data = (uint8_t *)key.data;
+
+ // Check for key overflow.
+ if (key_data[KEY0_POS] != iter->key0_code || key_data[KEY1_POS] != KEY1_ID) {
+ return KNOT_EOF;
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_db_iter_begin(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ conf_iter_t *iter)
+{
+ conf_iter_t out = { NULL };
+
+ if (conf == NULL || txn == NULL || key0 == NULL || iter == NULL) {
+ out.code = KNOT_EINVAL;
+ goto iter_begin_error;
+ }
+
+ // Look-up group id item in the schema.
+ const yp_item_t *grp = yp_schema_find(key0, NULL, conf->schema);
+ if (grp == NULL) {
+ out.code = KNOT_YP_EINVAL_ITEM;
+ goto iter_begin_error;
+ }
+ if (grp->type != YP_TGRP || (grp->flags & YP_FMULTI) == 0) {
+ out.code = KNOT_ENOTSUP;
+ goto iter_begin_error;
+ }
+ out.item = grp->var.g.id;
+
+ // Get key0 code.
+ out.code = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &out.key0_code);
+ if (out.code != KNOT_EOK) {
+ goto iter_begin_error;
+ }
+
+ // Prepare key prefix.
+ uint8_t k[2] = { out.key0_code, KEY1_ID };
+ knot_db_val_t key = { k, sizeof(k) };
+
+ // Get the data.
+ out.iter = conf->api->iter_begin(txn, KNOT_DB_NOOP);
+ out.iter = conf->api->iter_seek(out.iter, &key, KNOT_DB_GEQ);
+
+ // Check for no section id.
+ out.code = check_iter(conf, &out);
+ if (out.code != KNOT_EOK) {
+ out.code = KNOT_ENOENT; // Treat all errors as no entry.
+ conf_db_iter_finish(conf, &out);
+ goto iter_begin_error;
+ }
+
+iter_begin_error:
+ // Set the output.
+ if (iter != NULL) {
+ *iter = out;
+ }
+
+ return out.code;
+}
+
+int conf_db_iter_next(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ if (conf == NULL || iter == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (iter->code != KNOT_EOK) {
+ return iter->code;
+ }
+ assert(iter->iter != NULL);
+
+ // Move to the next key-value.
+ iter->iter = conf->api->iter_next(iter->iter);
+ if (iter->iter == NULL) {
+ conf_db_iter_finish(conf, iter);
+ iter->code = KNOT_EOF;
+ return iter->code;
+ }
+
+ // Check for key overflow.
+ iter->code = check_iter(conf, iter);
+ if (iter->code != KNOT_EOK) {
+ conf_db_iter_finish(conf, iter);
+ return iter->code;
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_db_iter_id(
+ conf_t *conf,
+ conf_iter_t *iter,
+ const uint8_t **data,
+ size_t *data_len)
+{
+ if (conf == NULL || iter == NULL || iter->iter == NULL ||
+ data == NULL || data_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_db_val_t key;
+ int ret = conf->api->iter_key(iter->iter, &key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ *data = (uint8_t *)key.data + CONF_MIN_KEY_LEN;
+ *data_len = key.len - CONF_MIN_KEY_LEN;
+
+ return KNOT_EOK;
+}
+
+int conf_db_iter_del(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ if (conf == NULL || iter == NULL || iter->iter == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return knot_db_lmdb_iter_del(iter->iter);
+}
+
+void conf_db_iter_finish(
+ conf_t *conf,
+ conf_iter_t *iter)
+{
+ if (conf == NULL || iter == NULL) {
+ return;
+ }
+
+ if (iter->iter != NULL) {
+ conf->api->iter_finish(iter->iter);
+ iter->iter = NULL;
+ }
+}
+
+int conf_db_raw_dump(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const char *file_name)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Use the current config read transaction if not specified.
+ if (txn == NULL) {
+ txn = &conf->read_txn;
+ }
+
+ FILE *fp = stdout;
+ if (file_name != NULL) {
+ fp = fopen(file_name, "w");
+ if (fp == NULL) {
+ return KNOT_ERROR;
+ }
+ }
+
+ int ret = KNOT_EOK;
+
+ knot_db_iter_t *it = conf->api->iter_begin(txn, KNOT_DB_FIRST);
+ while (it != NULL) {
+ knot_db_val_t key;
+ ret = conf->api->iter_key(it, &key);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ knot_db_val_t data;
+ ret = conf->api->iter_val(it, &data);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ uint8_t *k = (uint8_t *)key.data;
+ uint8_t *d = (uint8_t *)data.data;
+ if (k[1] == KEY1_ITEMS) {
+ fprintf(fp, "[%i][%i]%.*s", k[0], k[1],
+ (int)key.len - 2, k + 2);
+ fprintf(fp, ": %u\n", d[0]);
+ } else if (k[1] == KEY1_ID) {
+ fprintf(fp, "[%i][%i](%zu){", k[0], k[1], key.len - 2);
+ for (size_t i = 2; i < key.len; i++) {
+ fprintf(fp, "%02x", (uint8_t)k[i]);
+ }
+ fprintf(fp, "}\n");
+ } else {
+ fprintf(fp, "[%i][%i]", k[0], k[1]);
+ if (key.len > 2) {
+ fprintf(fp, "(%zu){", key.len - 2);
+ for (size_t i = 2; i < key.len; i++) {
+ fprintf(fp, "%02x", (uint8_t)k[i]);
+ }
+ fprintf(fp, "}");
+ }
+ fprintf(fp, ": (%zu)<", data.len);
+ for (size_t i = 0; i < data.len; i++) {
+ fprintf(fp, "%02x", (uint8_t)d[i]);
+ }
+ fprintf(fp, ">\n");
+ }
+
+ it = conf->api->iter_next(it);
+ }
+ conf->api->iter_finish(it);
+
+ if (file_name != NULL) {
+ fclose(fp);
+ } else {
+ fflush(fp);
+ }
+
+ return ret;
+}
diff --git a/src/knot/conf/confdb.h b/src/knot/conf/confdb.h
new file mode 100644
index 0000000..927200e
--- /dev/null
+++ b/src/knot/conf/confdb.h
@@ -0,0 +1,230 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "knot/conf/conf.h"
+#include "libknot/libknot.h"
+#include "libknot/yparser/ypschema.h"
+
+/*! Current version of the configuration database structure. */
+#define CONF_DB_VERSION 2
+/*! Minimum length of a database key ([category_id, item_id]. */
+#define CONF_MIN_KEY_LEN (2 * sizeof(uint8_t))
+/*! Maximum length of a database key ([category_id, item_id, identifier]. */
+#define CONF_MAX_KEY_LEN (CONF_MIN_KEY_LEN + YP_MAX_ID_LEN)
+/*! Maximum size of database data. */
+#define CONF_MAX_DATA_LEN 65536
+
+/*!
+ * Initializes the configuration DB if empty.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] purge Purge the DB indicator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_init(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ bool purge
+);
+
+/*!
+ * Checks the configuration DB and returns the number of items.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ *
+ * \return Error code, KNOT_EOK if ok and empty, > 0 number of records.
+ */
+int conf_db_check(
+ conf_t *conf,
+ knot_db_txn_t *txn
+);
+
+/*!
+ * Sets the item with data in the configuration DB.
+ *
+ * Singlevalued data is rewritten, multivalued data is appended.
+ *
+ * \note Setting of key0 without key1 has no effect.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0 Section name.
+ * \param[in] key1 Item name.
+ * \param[in] id Section identifier.
+ * \param[in] id_len Length of the section identifier.
+ * \param[in] data Item data.
+ * \param[in] data_len Length of the item data.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_set(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ const uint8_t *data,
+ size_t data_len
+);
+
+/*!
+ * Unsets the item data in the configuration DB.
+ *
+ * If no data is provided, the whole item is remove.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0 Section name.
+ * \param[in] key1 Item name.
+ * \param[in] id Section identifier.
+ * \param[in] id_len Length of the section identifier.
+ * \param[in] data Item data.
+ * \param[in] data_len Length of the item data.
+ * \param[in] delete_key1 Set to unregister the item from the DB.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_unset(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ const uint8_t *data,
+ size_t data_len,
+ bool delete_key1
+);
+
+/*!
+ * Gets the item data from the configuration DB.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0 Section name.
+ * \param[in] key1 Item name.
+ * \param[in] id Section identifier.
+ * \param[in] id_len Length of the section identifier.
+ * \param[out] data Item data.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_get(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ conf_val_t *data
+);
+
+/*!
+ * Gets a configuration DB section iterator.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] key0 Section name.
+ * \param[out] iter Section iterator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_iter_begin(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ conf_iter_t *iter
+);
+
+/*!
+ * Moves the section iterator to the next identifier.
+ *
+ * \param[in] conf Configuration.
+ * \param[in,out] iter Section iterator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_iter_next(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Gets the current section iterator value (identifier).
+ *
+ * \param[in] conf Configuration.
+ * \param[in] iter Section iterator.
+ * \param[out] data Identifier.
+ * \param[out] data_len Length of the identifier.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_iter_id(
+ conf_t *conf,
+ conf_iter_t *iter,
+ const uint8_t **data,
+ size_t *data_len
+);
+
+/*!
+ * Deletes the current section iterator value (identifier).
+ *
+ * \param[in] conf Configuration.
+ * \param[in,out] iter Section iterator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_iter_del(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Deletes the section iterator.
+ *
+ * \param[in] conf Configuration.
+ * \param[in,out] iter Section iterator.
+ */
+void conf_db_iter_finish(
+ conf_t *conf,
+ conf_iter_t *iter
+);
+
+/*!
+ * Dumps the configuration DB in the textual form.
+ *
+ * \note This function is intended for debugging.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] txn Configuration DB transaction.
+ * \param[in] file_name File name to dump to (NULL to dump to stdout).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_db_raw_dump(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const char *file_name
+);
diff --git a/src/knot/conf/confio.c b/src/knot/conf/confio.c
new file mode 100644
index 0000000..817f693
--- /dev/null
+++ b/src/knot/conf/confio.c
@@ -0,0 +1,1612 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/confdb.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/module.h"
+#include "knot/conf/tools.h"
+
+#define FCN(io) (io->fcn != NULL) ? io->fcn(io) : KNOT_EOK;
+
+static void io_reset_val(
+ conf_io_t *io,
+ const yp_item_t *key0,
+ const yp_item_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ bool id_as_data,
+ conf_val_t *val)
+{
+ io->key0 = key0;
+ io->key1 = key1;
+ io->id = id;
+ io->id_len = id_len;
+ io->id_as_data = id_as_data;
+ io->data.val = val;
+ io->data.bin = NULL;
+}
+
+static void io_reset_bin(
+ conf_io_t *io,
+ const yp_item_t *key0,
+ const yp_item_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ const uint8_t *bin,
+ size_t bin_len)
+{
+ io_reset_val(io, key0, key1, id, id_len, false, NULL);
+ io->data.bin = bin;
+ io->data.bin_len = bin_len;
+}
+
+int conf_io_begin(
+ bool child)
+{
+ assert(conf() != NULL);
+
+ if (conf()->io.txn != NULL && !child) {
+ return KNOT_TXN_EEXISTS;
+ } else if (conf()->io.txn == NULL && child) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ knot_db_txn_t *parent = conf()->io.txn;
+ knot_db_txn_t *txn = (parent == NULL) ? conf()->io.txn_stack : parent + 1;
+ if (txn >= conf()->io.txn_stack + CONF_MAX_TXN_DEPTH) {
+ return KNOT_TXN_EEXISTS;
+ }
+
+ if (conf()->filename != NULL && !child) {
+ log_ctl_notice("control, persistent configuration database "
+ "not available");
+ }
+
+ // Start the writing transaction.
+ int ret = knot_db_lmdb_txn_begin(conf()->db, txn, parent, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ conf()->io.txn = txn;
+
+ // Reset master transaction flags.
+ if (!child) {
+ conf()->io.flags = CONF_IO_FACTIVE;
+ if (conf()->io.zones != NULL) {
+ trie_clear(conf()->io.zones);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_io_commit(
+ bool child)
+{
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL ||
+ (child && conf()->io.txn == conf()->io.txn_stack)) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ knot_db_txn_t *txn = child ? conf()->io.txn : conf()->io.txn_stack;
+
+ // Commit the writing transaction.
+ int ret = conf()->api->txn_commit(txn);
+
+ conf()->io.txn = child ? txn - 1 : NULL;
+
+ return ret;
+}
+
+void conf_io_abort(
+ bool child)
+{
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL ||
+ (child && conf()->io.txn == conf()->io.txn_stack)) {
+ return;
+ }
+
+ knot_db_txn_t *txn = child ? conf()->io.txn : conf()->io.txn_stack;
+
+ // Abort the writing transaction.
+ conf()->api->txn_abort(txn);
+ conf()->io.txn = child ? txn - 1 : NULL;
+
+ // Reset master transaction flags.
+ if (!child) {
+ conf()->io.flags = YP_FNONE;
+ if (conf()->io.zones != NULL) {
+ trie_clear(conf()->io.zones);
+ }
+ }
+}
+
+static int list_section(
+ const yp_item_t *items,
+ const yp_item_t **item,
+ conf_io_t *io)
+{
+ for (*item = items; (*item)->name != NULL; (*item)++) {
+ int ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_io_list(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ bool list_schema,
+ bool get_current,
+ conf_io_t *io)
+{
+ if (io == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL && !get_current) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // List schema sections by default.
+ if (key0 == NULL) {
+ io_reset_val(io, NULL, NULL, NULL, 0, false, NULL);
+
+ return list_section(conf()->schema, &io->key0, io);
+ }
+
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Check the input.
+ int ret = yp_schema_check_str(ctx, key0, key1, id, NULL);
+ if (ret != KNOT_EOK) {
+ goto list_error;
+ }
+
+ knot_db_txn_t *txn = get_current ? &conf()->read_txn : conf()->io.txn;
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ // List group items.
+ if (list_schema && key1 == NULL) {
+ if (node->item->type != YP_TGRP) { // Ignore non-group section.
+ ret = KNOT_EOK;
+ goto list_error;
+ }
+
+ io_reset_val(io, node->item, NULL, NULL, 0, false, NULL);
+
+ ret = list_section(node->item->sub_items, &io->key1, io);
+ // List option item values.
+ } else if (list_schema) {
+ if (node->item->type != YP_TOPT) { // Ignore non-option item.
+ ret = KNOT_EOK;
+ goto list_error;
+ }
+
+ for (const knot_lookup_t *o = node->item->var.o.opts; o->name != NULL; o++) {
+ uint8_t val = o->id;
+ io_reset_bin(io, parent->item, node->item, parent->id,
+ parent->id_len, &val, sizeof(val));
+ ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ goto list_error;
+ }
+ }
+ // List group identifiers.
+ } else if (parent == NULL) {
+ if (node->item->type != YP_TGRP) { // Ignore non-group section.
+ ret = KNOT_EOK;
+ goto list_error;
+ }
+
+ // If key1 != NULL, it's used for a value completion (zone.domain).
+ io_reset_val(io, node->item, NULL, NULL, 0, key1 != NULL, NULL);
+
+ conf_iter_t iter;
+ ret = conf_db_iter_begin(conf(), txn, io->key0->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto list_error;
+ default:
+ goto list_error;
+ }
+
+ while (ret == KNOT_EOK) {
+ // Set the section identifier.
+ ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto list_error;
+ }
+
+ ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto list_error;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ ret = KNOT_EOK;
+ // List item values.
+ } else {
+ io_reset_val(io, parent->item, node->item, parent->id,
+ parent->id_len, false, NULL);
+
+ // Get the item value.
+ conf_val_t data;
+ ret = conf_db_get(conf(), txn, io->key0->name, io->key1->name,
+ io->id, io->id_len, &data);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto list_error;
+ default:
+ goto list_error;
+ }
+
+ io->data.val = &data;
+
+ ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ goto list_error;
+ }
+ }
+list_error:
+ yp_schema_check_deinit(ctx);
+
+ return ret;
+}
+
+static int diff_item(
+ conf_io_t *io)
+{
+ // Process an identifier item.
+ if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == io->key1) {
+ bool old_id, new_id;
+
+ // Check if a removed identifier.
+ int ret = conf_db_get(conf(), &conf()->read_txn, io->key0->name,
+ NULL, io->id, io->id_len, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ old_id = true;
+ break;
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ old_id = false;
+ break;
+ default:
+ return ret;
+ }
+
+ // Check if an added identifier.
+ ret = conf_db_get(conf(), conf()->io.txn, io->key0->name, NULL,
+ io->id, io->id_len, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ new_id = true;
+ break;
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ new_id = false;
+ break;
+ default:
+ return ret;
+ }
+
+ // Check if valid identifier.
+ if (!old_id && !new_id) {
+ return KNOT_YP_EINVAL_ID;
+ }
+
+ if (old_id != new_id) {
+ io->id_as_data = true;
+ io->type = old_id ? OLD : NEW;
+
+ // Process the callback.
+ ret = FCN(io);
+
+ // Reset the modified parameters.
+ io->id_as_data = false;
+ io->type = NONE;
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+ }
+
+ conf_val_t old_val, new_val;
+
+ // Get the old item value.
+ conf_db_get(conf(), &conf()->read_txn, io->key0->name, io->key1->name,
+ io->id, io->id_len, &old_val);
+ switch (old_val.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ break;
+ default:
+ return old_val.code;
+ }
+
+ // Get the new item value.
+ conf_db_get(conf(), conf()->io.txn, io->key0->name, io->key1->name,
+ io->id, io->id_len, &new_val);
+ switch (new_val.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ if (old_val.code != KNOT_EOK) {
+ return KNOT_EOK;
+ }
+ break;
+ default:
+ return new_val.code;
+ }
+
+ // Process the value difference.
+ if (old_val.code != KNOT_EOK) {
+ io->data.val = &new_val;
+ io->type = NEW;
+ int ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else if (new_val.code != KNOT_EOK) {
+ io->data.val = &old_val;
+ io->type = OLD;
+ int ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else if (!conf_val_equal(&old_val, &new_val)) {
+ io->data.val = &old_val;
+ io->type = OLD;
+ int ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ io->data.val = &new_val;
+ io->type = NEW;
+ ret = FCN(io);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Reset the modified parameters.
+ io->data.val = NULL;
+ io->type = NONE;
+
+ return KNOT_EOK;
+}
+
+static int diff_section(
+ conf_io_t *io)
+{
+ // Get the value for the specified item.
+ if (io->key1 != NULL) {
+ return diff_item(io);
+ }
+
+ // Get the values for all items.
+ for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) {
+ io->key1 = i;
+
+ int ret = diff_item(io);
+
+ // Reset the modified parameters.
+ io->key1 = NULL;
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int diff_iter_section(
+ conf_io_t *io)
+{
+ // First compare the section with the old and common identifiers.
+ conf_iter_t iter;
+ int ret = conf_db_iter_begin(conf(), &conf()->read_txn, io->key0->name,
+ &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ // Continue to the second step.
+ ret = KNOT_EOF;
+ break;
+ default:
+ return ret;
+ }
+
+ while (ret == KNOT_EOK) {
+ ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = diff_section(io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ return ret;
+ }
+
+ // Second compare the section with the new identifiers.
+ ret = conf_db_iter_begin(conf(), conf()->io.txn, io->key0->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+
+ while (ret == KNOT_EOK) {
+ ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ // Ignore old and common identifiers.
+ ret = conf_db_get(conf(), &conf()->read_txn, io->key0->name,
+ NULL, io->id, io->id_len, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ ret = conf_db_iter_next(conf(), &iter);
+ continue;
+ case KNOT_ENOENT:
+ case KNOT_YP_EINVAL_ID:
+ break;
+ default:
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = diff_section(io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int diff_zone_section(
+ conf_io_t *io)
+{
+ assert(io->key0->flags & CONF_IO_FZONE);
+
+ if (conf()->io.zones == NULL) {
+ return KNOT_EOK;
+ }
+
+ trie_it_t *it = trie_it_begin(conf()->io.zones);
+ for (; !trie_it_finished(it); trie_it_next(it)) {
+ io->id = (const uint8_t *)trie_it_key(it, &io->id_len);
+
+ // Get the difference for specific zone.
+ int ret = diff_section(io);
+ if (ret != KNOT_EOK) {
+ trie_it_free(it);
+ return ret;
+ }
+ }
+ trie_it_free(it);
+
+ return KNOT_EOK;
+}
+
+int conf_io_diff(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ conf_io_t *io)
+{
+ if (io == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // Compare all sections by default.
+ if (key0 == NULL) {
+ for (yp_item_t *i = conf()->schema; i->name != NULL; i++) {
+ // Skip non-group item.
+ if (i->type != YP_TGRP) {
+ continue;
+ }
+
+ int ret = conf_io_diff(i->name + 1, key1, NULL, io);
+
+ // Reset parameters after each section.
+ io_reset_val(io, NULL, NULL, NULL, 0, false, NULL);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+ }
+
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Check the input.
+ int ret = yp_schema_check_str(ctx, key0, key1, id, NULL);
+ if (ret != KNOT_EOK) {
+ goto diff_error;
+ }
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ // Key1 is not a group identifier.
+ if (parent != NULL) {
+ io_reset_val(io, parent->item, node->item, parent->id,
+ parent->id_len, false, NULL);
+ // Key1 is a group identifier.
+ } else if (key1 != NULL && strlen(key1) != 0) {
+ assert(node->item->type == YP_TGRP &&
+ (node->item->flags & YP_FMULTI) != 0);
+
+ io_reset_val(io, node->item, node->item->var.g.id, node->id,
+ node->id_len, true, NULL);
+ // No key1 specified.
+ } else {
+ io_reset_val(io, node->item, NULL, node->id, node->id_len,
+ false, NULL);
+ }
+
+ // Check for a non-group item.
+ if (io->key0->type != YP_TGRP) {
+ ret = KNOT_ENOTSUP;
+ goto diff_error;
+ }
+
+ // Compare the section with all identifiers by default.
+ if ((io->key0->flags & YP_FMULTI) != 0 && io->id_len == 0) {
+ // The zone section has an optimized diff.
+ if (io->key0->flags & CONF_IO_FZONE) {
+ // Full diff by default.
+ if (!(conf()->io.flags & CONF_IO_FACTIVE)) {
+ ret = diff_iter_section(io);
+ // Full diff if all zones changed.
+ } else if (conf()->io.flags & CONF_IO_FDIFF_ZONES) {
+ ret = diff_iter_section(io);
+ // Optimized diff for specific zones.
+ } else {
+ ret = diff_zone_section(io);
+ }
+ } else {
+ ret = diff_iter_section(io);
+ }
+
+ goto diff_error;
+ }
+
+ // Compare the section with a possible identifier.
+ ret = diff_section(io);
+diff_error:
+ yp_schema_check_deinit(ctx);
+
+ return ret;
+}
+
+static int get_section(
+ knot_db_txn_t *txn,
+ conf_io_t *io)
+{
+ conf_val_t data;
+
+ // Get the value for the specified item.
+ if (io->key1 != NULL) {
+ if (!io->id_as_data) {
+ // Get the item value.
+ conf_db_get(conf(), txn, io->key0->name, io->key1->name,
+ io->id, io->id_len, &data);
+ switch (data.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return data.code;
+ }
+
+ io->data.val = &data;
+ }
+
+ // Process the callback.
+ int ret = FCN(io);
+
+ // Reset the modified parameters.
+ io->data.val = NULL;
+
+ return ret;
+ }
+
+ // Get the values for all section items by default.
+ for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) {
+ // Process the (first) identifier item.
+ if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == i) {
+ // Check if existing identifier.
+ conf_db_get(conf(), txn, io->key0->name, NULL, io->id,
+ io->id_len, &data);
+ switch (data.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ continue;
+ default:
+ return data.code;
+ }
+
+ io->key1 = i;
+ io->id_as_data = true;
+
+ // Process the callback.
+ int ret = FCN(io);
+
+ // Reset the modified parameters.
+ io->key1 = NULL;
+ io->id_as_data = false;
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ continue;
+ }
+
+ // Get the item value.
+ conf_db_get(conf(), txn, io->key0->name, i->name, io->id,
+ io->id_len, &data);
+ switch (data.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ continue;
+ default:
+ return data.code;
+ }
+
+ io->key1 = i;
+ io->data.val = &data;
+
+ // Process the callback.
+ int ret = FCN(io);
+
+ // Reset the modified parameters.
+ io->key1 = NULL;
+ io->data.val = NULL;
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int conf_io_get(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ bool get_current,
+ conf_io_t *io)
+{
+ if (io == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL && !get_current) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // List all sections by default.
+ if (key0 == NULL) {
+ for (yp_item_t *i = conf()->schema; i->name != NULL; i++) {
+ // Skip non-group item.
+ if (i->type != YP_TGRP) {
+ continue;
+ }
+
+ int ret = conf_io_get(i->name + 1, key1, NULL,
+ get_current, io);
+ // Reset parameters after each section.
+ io_reset_val(io, NULL, NULL, NULL, 0, false, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+ }
+
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Check the input.
+ int ret = yp_schema_check_str(ctx, key0, key1, id, NULL);
+ if (ret != KNOT_EOK) {
+ goto get_error;
+ }
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ // Key1 is not a group identifier.
+ if (parent != NULL) {
+ io_reset_val(io, parent->item, node->item, parent->id,
+ parent->id_len, false, NULL);
+ // Key1 is a group identifier.
+ } else if (key1 != NULL && strlen(key1) != 0) {
+ assert(node->item->type == YP_TGRP &&
+ (node->item->flags & YP_FMULTI) != 0);
+
+ io_reset_val(io, node->item, node->item->var.g.id, node->id,
+ node->id_len, true, NULL);
+ // No key1 specified.
+ } else {
+ io_reset_val(io, node->item, NULL, node->id, node->id_len, false,
+ NULL);
+ }
+
+ knot_db_txn_t *txn = get_current ? &conf()->read_txn : conf()->io.txn;
+
+ // Check for a non-group item.
+ if (io->key0->type != YP_TGRP) {
+ ret = KNOT_ENOTSUP;
+ goto get_error;
+ }
+
+ // List the section with all identifiers by default.
+ if ((io->key0->flags & YP_FMULTI) != 0 && io->id_len == 0) {
+ conf_iter_t iter;
+ ret = conf_db_iter_begin(conf(), txn, io->key0->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto get_error;
+ default:
+ goto get_error;
+ }
+
+ while (ret == KNOT_EOK) {
+ // Set the section identifier.
+ ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto get_error;
+ }
+
+ ret = get_section(txn, io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto get_error;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+
+ ret = KNOT_EOK;
+ goto get_error;
+ }
+
+ // List the section with a possible identifier.
+ ret = get_section(txn, io);
+get_error:
+ yp_schema_check_deinit(ctx);
+
+ return ret;
+}
+
+static void upd_changes(
+ const conf_io_t *io,
+ conf_io_type_t type,
+ yp_flag_t flags,
+ bool any_id)
+{
+ // Update common flags.
+ conf()->io.flags |= flags;
+
+ // Return if not important change.
+ if (type == CONF_IO_TNONE) {
+ return;
+ }
+
+ // Update reference item.
+ if (flags & CONF_IO_FREF) {
+ // Expected an identifier, which cannot be changed.
+ assert(type != CONF_IO_TCHANGE);
+
+ // Re-check and reload all zones if a reference has been removed.
+ if (type == CONF_IO_TUNSET) {
+ conf()->io.flags |= CONF_IO_FCHECK_ZONES | CONF_IO_FRLD_ZONES;
+ }
+ return;
+ // Return if no specific zone operation.
+ } else if (!(flags & CONF_IO_FZONE)) {
+ return;
+ }
+
+ // Don't process each zone individually, process all instead.
+ if (any_id) {
+ // Diff all zone changes.
+ conf()->io.flags |= CONF_IO_FCHECK_ZONES | CONF_IO_FDIFF_ZONES;
+
+ // Reload just with important changes.
+ if (flags & CONF_IO_FRLD_ZONE) {
+ conf()->io.flags |= CONF_IO_FRLD_ZONES;
+ }
+ return;
+ }
+
+ // Prepare zone changes storage if it doesn't exist.
+ trie_t *zones = conf()->io.zones;
+ if (zones == NULL) {
+ zones = trie_create(NULL);
+ if (zones == NULL) {
+ return;
+ }
+ conf()->io.zones = zones;
+ }
+
+ // Get zone status or create new.
+ trie_val_t *val = trie_get_ins(zones, io->id, io->id_len);
+ conf_io_type_t *current = (conf_io_type_t *)val;
+
+ switch (type) {
+ case CONF_IO_TSET:
+ // Revert remove zone, but don't remove (probably changed).
+ if (*current & CONF_IO_TUNSET) {
+ *current &= ~CONF_IO_TUNSET;
+ } else {
+ // Must be a new zone.
+ assert(*current == CONF_IO_TNONE);
+ // Mark added zone.
+ *current = type;
+ }
+ break;
+ case CONF_IO_TUNSET:
+ if (*current & CONF_IO_TSET) {
+ // Remove inserted zone -> no change.
+ trie_del(zones, io->id, io->id_len, NULL);
+ } else {
+ // Remove existing zone.
+ *current |= type;
+ }
+ break;
+ case CONF_IO_TCHANGE:
+ *current |= type;
+ // Mark zone to reload if required.
+ if (flags & CONF_IO_FRLD_ZONE) {
+ *current |= CONF_IO_TRELOAD;
+ }
+ break;
+ case CONF_IO_TRELOAD:
+ default:
+ assert(0);
+ }
+}
+
+static int set_item(
+ conf_io_t *io)
+{
+ int ret = conf_db_set(conf(), conf()->io.txn, io->key0->name,
+ (io->key1 != NULL) ? io->key1->name : NULL,
+ io->id, io->id_len, io->data.bin, io->data.bin_len);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Postpone group callbacks to config check.
+ if (io->key0->type == YP_TGRP && io->id_len == 0) {
+ return KNOT_EOK;
+ }
+
+ knotd_conf_check_extra_t extra = {
+ .conf = conf(),
+ .txn = conf()->io.txn
+ };
+ knotd_conf_check_args_t args = {
+ .item = (io->key1 != NULL) ? io->key1 :
+ ((io->id_len == 0) ? io->key0 : io->key0->var.g.id),
+ .id = io->id,
+ .id_len = io->id_len,
+ .data = io->data.bin,
+ .data_len = io->data.bin_len,
+ .extra = &extra
+ };
+
+ // Call the item callbacks (include, item check, mod-id check).
+ ret = conf_exec_callbacks(&args);
+ if (ret != KNOT_EOK) {
+ CONF_LOG(LOG_DEBUG, "item '%s' (%s)", args.item->name + 1,
+ args.err_str != NULL ? args.err_str : knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+int conf_io_set(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data)
+{
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // At least key0 must be specified.
+ if (key0 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Check the input.
+ int ret = yp_schema_check_str(ctx, key0, key1, id, data);
+ if (ret != KNOT_EOK) {
+ goto set_error;
+ }
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ yp_flag_t upd_flags = node->item->flags;
+ conf_io_type_t upd_type = CONF_IO_TNONE;
+
+ conf_io_t io = { NULL };
+
+ // Key1 is not a group identifier.
+ if (parent != NULL) {
+ if (node->data_len == 0) {
+ ret = KNOT_YP_ENODATA;
+ goto set_error;
+ }
+ upd_type = CONF_IO_TCHANGE;
+ upd_flags |= parent->item->flags;
+ io_reset_bin(&io, parent->item, node->item, parent->id,
+ parent->id_len, node->data, node->data_len);
+ // A group identifier or whole group.
+ } else if (node->item->type == YP_TGRP) {
+ upd_type = CONF_IO_TSET;
+ if ((node->item->flags & YP_FMULTI) != 0) {
+ if (node->id_len == 0) {
+ ret = KNOT_YP_ENOID;
+ goto set_error;
+ }
+ upd_flags |= node->item->var.g.id->flags;
+ } else {
+ ret = KNOT_ENOTSUP;
+ goto set_error;
+ }
+ assert(node->data_len == 0);
+ io_reset_bin(&io, node->item, NULL, node->id, node->id_len,
+ NULL, 0);
+ // A non-group item with data (include).
+ } else if (node->data_len > 0) {
+ io_reset_bin(&io, node->item, NULL, NULL, 0, node->data,
+ node->data_len);
+ } else {
+ ret = KNOT_YP_ENODATA;
+ goto set_error;
+ }
+
+ // Set the item for all identifiers by default.
+ if (io.key0->type == YP_TGRP && io.key1 != NULL &&
+ (io.key0->flags & YP_FMULTI) != 0 && io.id_len == 0) {
+ conf_iter_t iter;
+ ret = conf_db_iter_begin(conf(), conf()->io.txn, io.key0->name,
+ &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto set_error;
+ default:
+ goto set_error;
+ }
+
+ uint8_t copied_id[YP_MAX_ID_LEN];
+ io.id = copied_id;
+ while (ret == KNOT_EOK) {
+ // Get the identifier and copy it because of next DB update.
+ const uint8_t *tmp_id;
+ ret = conf_db_iter_id(conf(), &iter, &tmp_id, &io.id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto set_error;
+ }
+ memcpy(copied_id, tmp_id, io.id_len);
+
+ // Set the data.
+ ret = set_item(&io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto set_error;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ goto set_error;
+ }
+
+ upd_changes(&io, upd_type, upd_flags, true);
+
+ ret = KNOT_EOK;
+ goto set_error;
+ }
+
+ // Set the item with a possible identifier.
+ ret = set_item(&io);
+
+ if (ret == KNOT_EOK) {
+ upd_changes(&io, upd_type, upd_flags, false);
+ }
+set_error:
+ yp_schema_check_deinit(ctx);
+
+ return ret;
+}
+
+static int unset_section_data(
+ conf_io_t *io)
+{
+ // Unset the value for the specified item.
+ if (io->key1 != NULL) {
+ return conf_db_unset(conf(), conf()->io.txn, io->key0->name,
+ io->key1->name, io->id, io->id_len,
+ io->data.bin, io->data.bin_len, false);
+ }
+
+ // Unset the whole section by default.
+ for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) {
+ // Skip the identifier item.
+ if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == i) {
+ continue;
+ }
+
+ int ret = conf_db_unset(conf(), conf()->io.txn, io->key0->name,
+ i->name, io->id, io->id_len, io->data.bin,
+ io->data.bin_len, false);
+ switch (ret) {
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ continue;
+ default:
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int unset_section(
+ const yp_item_t *key0)
+{
+ // Unset the section items.
+ for (yp_item_t *i = key0->sub_items; i->name != NULL; i++) {
+ // Skip the identifier item.
+ if ((key0->flags & YP_FMULTI) != 0 && key0->var.g.id == i) {
+ continue;
+ }
+
+ int ret = conf_db_unset(conf(), conf()->io.txn, key0->name,
+ i->name, NULL, 0, NULL, 0, true);
+ switch (ret) {
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ continue;
+ default:
+ return ret;
+ }
+ }
+
+ // Unset the section.
+ int ret = conf_db_unset(conf(), conf()->io.txn, key0->name, NULL, NULL,
+ 0, NULL, 0, false);
+ switch (ret) {
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+}
+
+int conf_io_unset(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data)
+{
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // Unset all sections by default.
+ if (key0 == NULL) {
+ for (yp_item_t *i = conf()->schema; i->name != NULL; i++) {
+ // Skip non-group item.
+ if (i->type != YP_TGRP) {
+ continue;
+ }
+
+ int ret = conf_io_unset(i->name + 1, key1, NULL, NULL);
+ switch (ret) {
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ break;
+ default:
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+ }
+
+ yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Check the input.
+ int ret = yp_schema_check_str(ctx, key0, key1, id, data);
+ if (ret != KNOT_EOK) {
+ goto unset_error;
+ }
+
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+
+ yp_flag_t upd_flags = node->item->flags;
+ conf_io_type_t upd_type = CONF_IO_TNONE;
+
+ conf_io_t io = { NULL };
+
+ // Key1 is not a group identifier.
+ if (parent != NULL) {
+ upd_type = CONF_IO_TCHANGE;
+ upd_flags |= parent->item->flags;
+ io_reset_bin(&io, parent->item, node->item, parent->id,
+ parent->id_len, node->data, node->data_len);
+ // A group identifier or whole group.
+ } else if (node->item->type == YP_TGRP) {
+ upd_type = CONF_IO_TUNSET;
+ if ((node->item->flags & YP_FMULTI) != 0) {
+ upd_flags |= node->item->var.g.id->flags;
+ }
+ assert(node->data_len == 0);
+ io_reset_bin(&io, node->item, NULL, node->id, node->id_len,
+ NULL, 0);
+ // A non-group item (include).
+ } else {
+ ret = KNOT_ENOTSUP;
+ goto unset_error;
+ }
+
+ // Unset the section with all identifiers by default.
+ if ((io.key0->flags & YP_FMULTI) != 0 && io.id_len == 0) {
+ conf_iter_t iter;
+ ret = conf_db_iter_begin(conf(), conf()->io.txn, io.key0->name,
+ &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto unset_error;
+ default:
+ goto unset_error;
+ }
+
+ uint8_t copied_id[YP_MAX_ID_LEN];
+ io.id = copied_id;
+ while (ret == KNOT_EOK) {
+ // Get the identifier and copy it because of next DB update.
+ const uint8_t *tmp_id;
+ ret = conf_db_iter_id(conf(), &iter, &tmp_id, &io.id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto unset_error;
+ }
+ memcpy(copied_id, tmp_id, io.id_len);
+
+ // Unset the section data.
+ ret = unset_section_data(&io);
+ switch (ret) {
+ case KNOT_EOK:
+ case KNOT_ENOENT:
+ break;
+ default:
+ conf_db_iter_finish(conf(), &iter);
+ goto unset_error;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ goto unset_error;
+ }
+
+ if (io.key1 == NULL) {
+ // Unset all identifiers.
+ ret = conf_db_iter_begin(conf(), conf()->io.txn,
+ io.key0->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ ret = KNOT_EOK;
+ goto unset_error;
+ default:
+ goto unset_error;
+ }
+
+ while (ret == KNOT_EOK) {
+ ret = conf_db_iter_del(conf(), &iter);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ goto unset_error;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ goto unset_error;
+ }
+
+ // Unset the section.
+ ret = unset_section(io.key0);
+ if (ret != KNOT_EOK) {
+ goto unset_error;
+ }
+ }
+
+ upd_changes(&io, upd_type, upd_flags, true);
+
+ ret = KNOT_EOK;
+ goto unset_error;
+ }
+
+ // Unset the section data.
+ ret = unset_section_data(&io);
+ if (ret != KNOT_EOK) {
+ goto unset_error;
+ }
+
+ if (io.key1 == NULL) {
+ // Unset the identifier.
+ if (io.id_len != 0) {
+ ret = conf_db_unset(conf(), conf()->io.txn, io.key0->name,
+ NULL, io.id, io.id_len, NULL, 0, false);
+ if (ret != KNOT_EOK) {
+ goto unset_error;
+ }
+ // Unset the section.
+ } else {
+ ret = unset_section(io.key0);
+ if (ret != KNOT_EOK) {
+ goto unset_error;
+ }
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ upd_changes(&io, upd_type, upd_flags, false);
+ }
+unset_error:
+ yp_schema_check_deinit(ctx);
+
+ return ret;
+}
+
+static int check_section(
+ const yp_item_t *group,
+ const uint8_t *id,
+ size_t id_len,
+ conf_io_t *io)
+{
+ knotd_conf_check_extra_t extra = {
+ .conf = conf(),
+ .txn = conf()->io.txn,
+ .check = true
+ };
+ knotd_conf_check_args_t args = {
+ .id = id,
+ .id_len = id_len,
+ .extra = &extra
+ };
+
+ bool non_empty = false;
+
+ conf_val_t bin; // Must be in the scope of the error processing.
+ for (yp_item_t *item = group->sub_items; item->name != NULL; item++) {
+ args.item = item;
+
+ // Check the identifier.
+ if ((group->flags & YP_FMULTI) != 0 && group->var.g.id == item) {
+ io->error.code = conf_exec_callbacks(&args);
+ if (io->error.code != KNOT_EOK) {
+ io_reset_val(io, group, item, NULL, 0, false, NULL);
+ goto check_section_error;
+ }
+ continue;
+ }
+
+ // Get the item value.
+ conf_db_get(conf(), conf()->io.txn, group->name, item->name, id,
+ id_len, &bin);
+ if (bin.code == KNOT_ENOENT) {
+ continue;
+ } else if (bin.code != KNOT_EOK) {
+ return bin.code;
+ }
+
+ non_empty = true;
+
+ // Check the item value(s).
+ size_t values = conf_val_count(&bin);
+ for (size_t i = 1; i <= values; i++) {
+ conf_val(&bin);
+ args.data = bin.data;
+ args.data_len = bin.len;
+
+ io->error.code = conf_exec_callbacks(&args);
+ if (io->error.code != KNOT_EOK) {
+ io_reset_val(io, group, item, id, id_len, false,
+ &bin);
+ io->data.index = i;
+ goto check_section_error;
+ }
+
+ if (values > 1) {
+ conf_val_next(&bin);
+ }
+ }
+ }
+
+ // Check the whole section if not empty.
+ if (id != NULL || non_empty) {
+ args.item = group;
+ args.data = NULL;
+ args.data_len = 0;
+
+ io->error.code = conf_exec_callbacks(&args);
+ if (io->error.code != KNOT_EOK) {
+ io_reset_val(io, group, NULL, id, id_len, false, NULL);
+ goto check_section_error;
+ }
+ }
+
+ return KNOT_EOK;
+
+check_section_error:
+ io->error.str = args.err_str;
+ int ret = FCN(io);
+ if (ret == KNOT_EOK) {
+ return io->error.code;
+ }
+ return ret;
+}
+
+static int check_iter_section(
+ const yp_item_t *item,
+ conf_io_t *io)
+{
+ // Iterate over all identifiers.
+ conf_iter_t iter;
+ int ret = conf_db_iter_begin(conf(), conf()->io.txn, item->name, &iter);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+
+ while (ret == KNOT_EOK) {
+ size_t id_len;
+ const uint8_t *id;
+ ret = conf_db_iter_id(conf(), &iter, &id, &id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ // Check specific section item.
+ ret = check_section(item, id, id_len, io);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_zone_section(
+ const yp_item_t *item,
+ conf_io_t *io)
+{
+ assert(item->flags & CONF_IO_FZONE);
+
+ if (conf()->io.zones == NULL) {
+ return KNOT_EOK;
+ }
+
+ trie_it_t *it = trie_it_begin(conf()->io.zones);
+ for (; !trie_it_finished(it); trie_it_next(it)) {
+ size_t id_len;
+ const uint8_t *id = (const uint8_t *)trie_it_key(it, &id_len);
+
+ conf_io_type_t type = conf_io_trie_val(it);
+ if (type == CONF_IO_TUNSET) {
+ // Nothing to check.
+ continue;
+ }
+
+ // Check specific zone.
+ int ret = check_section(item, id, id_len, io);
+ if (ret != KNOT_EOK) {
+ trie_it_free(it);
+ return ret;
+ }
+ }
+ trie_it_free(it);
+
+ return KNOT_EOK;
+}
+
+int conf_io_check(
+ conf_io_t *io)
+{
+ if (io == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(conf() != NULL);
+
+ if (conf()->io.txn == NULL) {
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ int ret;
+
+ // Iterate over the schema.
+ for (yp_item_t *item = conf()->schema; item->name != NULL; item++) {
+ // Skip non-group items (include).
+ if (item->type != YP_TGRP) {
+ continue;
+ }
+
+ // Check simple group without identifiers.
+ if ((item->flags & YP_FMULTI) == 0) {
+ ret = check_section(item, NULL, 0, io);
+ if (ret != KNOT_EOK) {
+ goto check_error;
+ }
+ continue;
+ }
+
+ // The zone section has an optimized check.
+ if (item->flags & CONF_IO_FZONE) {
+ // Full check by default.
+ if (!(conf()->io.flags & CONF_IO_FACTIVE)) {
+ ret = check_iter_section(item, io);
+ // Full check if all zones changed.
+ } else if (conf()->io.flags & CONF_IO_FCHECK_ZONES) {
+ ret = check_iter_section(item, io);
+ // Optimized check for specific zones.
+ } else {
+ ret = check_zone_section(item, io);
+ }
+ } else {
+ ret = check_iter_section(item, io);
+ }
+ if (ret != KNOT_EOK) {
+ goto check_error;
+ }
+ }
+
+ ret = KNOT_EOK;
+check_error:
+ conf_mod_load_purge(conf(), true);
+
+ return ret;
+}
diff --git a/src/knot/conf/confio.h b/src/knot/conf/confio.h
new file mode 100644
index 0000000..be08e97
--- /dev/null
+++ b/src/knot/conf/confio.h
@@ -0,0 +1,231 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+
+/*! Configuration schema additional flags for dynamic changes. */
+#define CONF_IO_FACTIVE YP_FUSR1 /*!< Active confio transaction indicator. */
+#define CONF_IO_FZONE YP_FUSR2 /*!< Zone section indicator. */
+#define CONF_IO_FREF YP_FUSR3 /*!< Possibly referenced id from a zone. */
+#define CONF_IO_FDIFF_ZONES YP_FUSR4 /*!< All zones config has changed. */
+#define CONF_IO_FCHECK_ZONES YP_FUSR5 /*!< All zones config needs to check. */
+#define CONF_IO_FRLD_SRV YP_FUSR6 /*!< Reload server. */
+#define CONF_IO_FRLD_LOG YP_FUSR7 /*!< Reload logging. */
+#define CONF_IO_FRLD_MOD YP_FUSR8 /*!< Reload global modules. */
+#define CONF_IO_FRLD_ZONE YP_FUSR9 /*!< Reload a specific zone. */
+#define CONF_IO_FRLD_ZONES YP_FUSR10 /*!< Reload all zones. */
+#define CONF_IO_FRLD_ALL (CONF_IO_FRLD_SRV | CONF_IO_FRLD_LOG | \
+ CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES)
+
+/*! Zone configuration change type. */
+typedef enum {
+ CONF_IO_TNONE = 0, /*!< Unspecified. */
+ CONF_IO_TSET = 1 << 0, /*!< Zone added. */
+ CONF_IO_TUNSET = 1 << 1, /*!< Zone removed. */
+ CONF_IO_TCHANGE = 1 << 2, /*!< Zone has changed configuration. */
+ CONF_IO_TRELOAD = 1 << 3, /*!< Zone must be reloaded. */
+} conf_io_type_t;
+
+/*! Configuration interface output. */
+typedef struct conf_io conf_io_t;
+struct conf_io {
+ /*! Section. */
+ const yp_item_t *key0;
+ /*! Section item. */
+ const yp_item_t *key1;
+ /*! Section identifier. */
+ const uint8_t *id;
+ /*! Section identifier length. */
+ size_t id_len;
+ /*! Consider item identifier as item data. */
+ bool id_as_data;
+
+ enum {
+ /*! Default item state. */
+ NONE,
+ /*! New item indicator. */
+ NEW,
+ /*! Old item indicator. */
+ OLD
+ } type;
+
+ struct {
+ /*! Section item data (NULL if not used). */
+ conf_val_t *val;
+ /*! Index of data value to format (counted from 1, 0 means all). */
+ size_t index;
+ /*! Binary data value (NULL if not used). */
+ const uint8_t *bin;
+ /*! Length of the binary data value. */
+ size_t bin_len;
+ } data;
+
+ struct {
+ /*! Edit operation return code. */
+ int code;
+ /*! Edit operation return error message. */
+ const char *str;
+ } error;
+
+ /*! Optional processing callback. */
+ int (*fcn)(conf_io_t *);
+ /*! Miscellaneous data useful for the callback. */
+ void *misc;
+};
+
+inline static conf_io_type_t conf_io_trie_val(trie_it_t *it)
+{
+ return (conf_io_type_t)(uintptr_t)(*trie_it_val(it));
+}
+
+/*!
+ * Starts new writing transaction.
+ *
+ * \param[in] child Nested transaction indicator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_begin(
+ bool child
+);
+
+/*!
+ * Commits the current writing transaction.
+ *
+ * \note Remember to call conf_refresh to publish the changes into the common
+ * configuration.
+ *
+ * \param[in] child Nested transaction indicator.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_commit(
+ bool child
+);
+
+/*!
+ * Aborts the current writing transaction.
+ *
+ * \param[in] child Nested transaction indicator.
+ */
+void conf_io_abort(
+ bool child
+);
+
+/*!
+ * Gets the configuration sections list or section items list.
+ *
+ * \param[in] key0 Section name (NULL to get section list).
+ * \param[in] key1 Item name (non-NULL to get value list).
+ * \param[in] id Section identifier name if needed for value list.
+ * \param[in] list_schema List schema items or option values.
+ * \param[in] get_current The current configuration or the active transaction switch.
+ * \param[out] io Operation output.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_list(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ bool list_schema,
+ bool get_current,
+ conf_io_t *io
+);
+
+/*!
+ * Gets the configuration difference between the current configuration and
+ * the active transaction.
+ *
+ * \param[in] key0 Section name (NULL to diff all sections).
+ * \param[in] key1 Item name (NULL to diff all section items).
+ * \param[in] id Section identifier name (NULL to consider all section identifiers).
+ * \param[out] io Operation output.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_diff(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ conf_io_t *io
+);
+
+/*!
+ * Gets the configuration item(s) value(s).
+ *
+ * \param[in] key0 Section name (NULL to get all sections).
+ * \param[in] key1 Item name (NULL to get all section items).
+ * \param[in] id Section identifier name (NULL to consider all section identifiers).
+ * \param[in] get_current The current configuration or the active transaction switch.
+ * \param[out] io Operation output.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_get(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ bool get_current,
+ conf_io_t *io
+);
+
+/*!
+ * Sets the configuration item(s) value.
+ *
+ * \param[in] key0 Section name.
+ * \param[in] key1 Item name (NULL to add identifier only).
+ * \param[in] id Section identifier name (NULL to consider all section identifiers).
+ * \param[in] data Item data to set/add.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_set(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data
+);
+
+/*!
+ * Unsets the configuration item(s) value(s).
+ *
+ * \param[in] key0 Section name (NULL to unset all sections).
+ * \param[in] key1 Item name (NULL to unset the whole section).
+ * \param[in] id Section identifier name (NULL to consider all section identifiers).
+ * \param[in] data Item data (NULL to unset all data).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_unset(
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data
+);
+
+/*!
+ * Checks the configuration database semantics in the current writing transaction.
+ *
+ * \param[out] io Operation output.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_io_check(
+ conf_io_t *io
+);
diff --git a/src/knot/conf/migration.c b/src/knot/conf/migration.c
new file mode 100644
index 0000000..7e881b6
--- /dev/null
+++ b/src/knot/conf/migration.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2017 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 "knot/common/log.h"
+#include "knot/conf/migration.h"
+#include "knot/conf/confdb.h"
+
+/*
+static void try_unset(conf_t *conf, knot_db_txn_t *txn, yp_name_t *key0, yp_name_t *key1)
+{
+ int ret = conf_db_unset(conf, txn, key0, key1, NULL, 0, NULL, 0, true);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ log_warning("conf, migration, failed to unset '%s%s%s' (%s)",
+ key0 + 1,
+ (key1 != NULL) ? "/" : "",
+ (key1 != NULL) ? key1 + 1 : "",
+ knot_strerror(ret));
+ }
+}
+
+#define check_set(conf, txn, key0, key1, id, id_len, data, data_len) \
+ ret = conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len); \
+ if (ret != KNOT_EOK && ret != KNOT_CONF_EREDEFINE) { \
+ log_error("conf, migration, failed to set '%s%s%s' (%s)", \
+ key0 + 1, \
+ (key1 != NULL) ? "/" : "", \
+ (key1 != NULL) ? key1 + 1 : "", \
+ knot_strerror(ret)); \
+ return ret; \
+ }
+
+static int migrate_(
+ conf_t *conf,
+ knot_db_txn_t *txn)
+{
+ return KNOT_EOK;
+}
+*/
+
+int conf_migrate(
+ conf_t *conf)
+{
+ return KNOT_EOK;
+ /*
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_db_txn_t txn;
+ int ret = conf->api->txn_begin(conf->db, &txn, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = migrate_(conf, &txn);
+ if (ret != KNOT_EOK) {
+ conf->api->txn_abort(&txn);
+ return ret;
+ }
+
+ ret = conf->api->txn_commit(&txn);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return conf_refresh_txn(conf);
+ */
+}
diff --git a/src/knot/conf/migration.h b/src/knot/conf/migration.h
new file mode 100644
index 0000000..f8d0793
--- /dev/null
+++ b/src/knot/conf/migration.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/base.h"
+
+/*!
+ * Migrates from an old configuration schema.
+ *
+ * \param[in] conf Configuration.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_migrate(
+ conf_t *conf
+);
diff --git a/src/knot/conf/module.c b/src/knot/conf/module.c
new file mode 100644
index 0000000..d5d9642
--- /dev/null
+++ b/src/knot/conf/module.c
@@ -0,0 +1,509 @@
+/* Copyright (C) 2023 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 <dlfcn.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include "knot/conf/conf.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/module.h"
+#include "knot/common/log.h"
+#include "knot/modules/static_modules.h"
+#include "knot/nameserver/query_module.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/string.h"
+
+#define LIB_EXTENSION ".so"
+
+knot_dynarray_define(mod, module_t *, DYNARRAY_VISIBILITY_NORMAL)
+knot_dynarray_define(old_schema, yp_item_t *, DYNARRAY_VISIBILITY_NORMAL)
+
+static module_t STATIC_MODULES[] = {
+ STATIC_MODULES_INIT
+ { NULL }
+};
+
+module_t *conf_mod_find(
+ conf_t *conf,
+ const char *name,
+ size_t len,
+ bool temporary)
+{
+ if (conf == NULL || name == NULL) {
+ return NULL;
+ }
+
+ // First, search in static modules.
+ for (module_t *mod = STATIC_MODULES; mod->api != NULL; mod++) {
+ if (strncmp(name, mod->api->name, len) == 0) {
+ return mod;
+ }
+ }
+
+ module_type_t excluded_type = temporary ? MOD_EXPLICIT : MOD_TEMPORARY;
+
+ // Second, search in dynamic modules.
+ knot_dynarray_foreach(mod, module_t *, module, conf->modules) {
+ if ((*module) != NULL && (*module)->type != excluded_type &&
+ strncmp(name, (*module)->api->name, len) == 0) {
+ return (*module);
+ }
+ }
+
+ return NULL;
+}
+
+static int mod_load(
+ conf_t *conf,
+ module_t *mod)
+{
+ static const yp_item_t module_common[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+ };
+
+ yp_item_t *sub_items = NULL;
+
+ int ret;
+ if (mod->api->config != NULL) {
+ ret = yp_schema_merge(&sub_items, module_common, mod->api->config);
+ } else {
+ ret = yp_schema_copy(&sub_items, module_common);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Synthesise module config section name. */
+ const size_t name_len = strlen(mod->api->name);
+ if (name_len > YP_MAX_ITEM_NAME_LEN) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+ char name[1 + YP_MAX_ITEM_NAME_LEN + 1];
+ name[0] = name_len;
+ memcpy(name + 1, mod->api->name, name_len + 1);
+
+ const yp_item_t schema[] = {
+ { name, YP_TGRP, YP_VGRP = { sub_items },
+ YP_FALLOC | YP_FMULTI | CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES,
+ { mod->api->config_check } },
+ { NULL }
+ };
+
+ yp_item_t *merged = NULL;
+ ret = yp_schema_merge(&merged, conf->schema, schema);
+ yp_schema_free(sub_items);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Update configuration schema (with lazy free).
+ yp_item_t **current_schema = &conf->schema;
+ yp_item_t *old_schema = rcu_xchg_pointer(current_schema, merged);
+ synchronize_rcu();
+ old_schema_dynarray_add(&conf->old_schemas, &old_schema);
+
+ return KNOT_EOK;
+}
+
+int conf_mod_load_common(
+ conf_t *conf)
+{
+ if (conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ // First, load static modules.
+ for (module_t *mod = STATIC_MODULES; mod->api != NULL; mod++) {
+ ret = mod_load(conf, mod);
+ if (ret != KNOT_EOK) {
+ log_error("module '%s', failed to load (%s)",
+ mod->api->name, knot_strerror(ret));
+ break;
+ }
+
+ log_debug("module '%s', loaded static", mod->api->name);
+ }
+
+ // Second, try to load implicit shared modules if configured.
+ if (strlen(MODULE_DIR) > 0) {
+ struct stat path_stat;
+ glob_t glob_buf = { 0 };
+
+ char *path = sprintf_alloc("%s/*%s", MODULE_DIR, LIB_EXTENSION);
+ if (path == NULL) {
+ ret = KNOT_ENOMEM;
+ } else if (stat(MODULE_DIR, &path_stat) != 0 ||
+ !S_ISDIR(path_stat.st_mode)) {
+ if (errno == ENOENT) {
+ // Module directory doesn't exist.
+ ret = KNOT_EOK;
+ } else {
+ log_error("module, invalid directory '%s'",
+ MODULE_DIR);
+ ret = KNOT_EINVAL;
+ }
+ } else if (access(MODULE_DIR, F_OK | R_OK) != 0) {
+ log_error("module, failed to access directory '%s'",
+ MODULE_DIR);
+ ret = KNOT_EACCES;
+ } else {
+ ret = glob(path, 0, NULL, &glob_buf);
+ if (ret != 0 && ret != GLOB_NOMATCH) {
+ log_error("module, failed to read directory '%s'",
+ MODULE_DIR);
+ ret = KNOT_EACCES;
+ } else {
+ ret = KNOT_EOK;
+ }
+ }
+
+ // Process each module in the directory.
+ for (size_t i = 0; i < glob_buf.gl_pathc; i++) {
+ (void)conf_mod_load_extra(conf, NULL, glob_buf.gl_pathv[i],
+ MOD_IMPLICIT);
+ }
+
+ globfree(&glob_buf);
+ free(path);
+ }
+
+ conf_mod_load_purge(conf, false);
+
+ return ret;
+}
+
+int conf_mod_load_extra(
+ conf_t *conf,
+ const char *mod_name,
+ const char *file_name,
+ module_type_t type)
+{
+ if (conf == NULL || (mod_name == NULL && file_name == NULL)) {
+ return KNOT_EINVAL;
+ }
+
+ // Synthesize module file name if not specified.
+ char *tmp_name = NULL;
+ if (file_name == NULL) {
+ tmp_name = sprintf_alloc("%s/%s%s", MODULE_INSTDIR,
+ mod_name + strlen(KNOTD_MOD_NAME_PREFIX),
+ LIB_EXTENSION);
+ if (tmp_name == NULL) {
+ return KNOT_ENOMEM;
+ }
+ file_name = tmp_name;
+ }
+
+ void *handle = dlopen(file_name, RTLD_NOW | RTLD_LOCAL);
+ if (handle == NULL) {
+ log_error("module, failed to open '%s' (%s)", file_name, dlerror());
+ free(tmp_name);
+ return KNOT_ENOENT;
+ }
+ (void)dlerror();
+
+ knotd_mod_api_t *api = dlsym(handle, "knotd_mod_api");
+ if (api == NULL) {
+ char *err = dlerror();
+ if (err == NULL) {
+ err = "empty symbol";
+ }
+ log_error("module, invalid library '%s' (%s)", file_name, err);
+ dlclose(handle);
+ free(tmp_name);
+ return KNOT_ENOENT;
+ }
+ free(tmp_name);
+
+ if (api->version != KNOTD_MOD_ABI_VERSION) {
+ log_error("module '%s', incompatible version", api->name);
+ dlclose(handle);
+ return KNOT_ENOTSUP;
+ }
+
+ if (api->name == NULL || (mod_name != NULL && strcmp(api->name, mod_name) != 0)) {
+ log_error("module '%s', module name mismatch", api->name);
+ dlclose(handle);
+ return KNOT_ENOTSUP;
+ }
+
+ // Check if the module is already loaded.
+ module_t *found = conf_mod_find(conf, api->name, strlen(api->name),
+ type == MOD_TEMPORARY);
+ if (found != NULL) {
+ log_error("module '%s', duplicate module", api->name);
+ dlclose(handle);
+ return KNOT_EEXIST;
+ }
+
+ module_t *mod = calloc(1, sizeof(*mod));
+ if (mod == NULL) {
+ dlclose(handle);
+ return KNOT_ENOMEM;
+ }
+ mod->api = api;
+ mod->lib_handle = handle;
+ mod->type = type;
+
+ int ret = mod_load(conf, mod);
+ if (ret != KNOT_EOK) {
+ log_error("module '%s', failed to load (%s)", api->name,
+ knot_strerror(ret));
+ dlclose(handle);
+ free(mod);
+ return ret;
+ }
+
+ mod_dynarray_add(&conf->modules, &mod);
+
+ log_debug("module '%s', loaded shared", api->name);
+
+ return KNOT_EOK;
+}
+
+static void unload_shared(
+ module_t *mod)
+{
+ if (mod != NULL) {
+ assert(mod->lib_handle);
+ (void)dlclose(mod->lib_handle);
+ free(mod);
+ }
+}
+
+void conf_mod_load_purge(
+ conf_t *conf,
+ bool temporary)
+{
+ if (conf == NULL) {
+ return;
+ }
+
+ // Switch the current temporary schema with the initial one.
+ if (temporary && conf->old_schemas.size > 0) {
+ yp_item_t **current_schema = &conf->schema;
+ yp_item_t **initial = &(conf->old_schemas.arr(&conf->old_schemas))[0];
+
+ yp_item_t *old_schema = rcu_xchg_pointer(current_schema, *initial);
+ synchronize_rcu();
+ *initial = old_schema;
+ }
+
+ knot_dynarray_foreach(old_schema, yp_item_t *, schema, conf->old_schemas) {
+ yp_schema_free(*schema);
+ }
+ old_schema_dynarray_free(&conf->old_schemas);
+
+ knot_dynarray_foreach(mod, module_t *, module, conf->modules) {
+ if ((*module) != NULL && (*module)->type == MOD_TEMPORARY) {
+ unload_shared((*module));
+ *module = NULL; // Cannot remove from dynarray.
+ }
+ }
+}
+
+void conf_mod_unload_shared(
+ conf_t *conf)
+{
+ if (conf == NULL) {
+ return;
+ }
+
+ knot_dynarray_foreach(mod, module_t *, module, conf->modules) {
+ unload_shared((*module));
+ }
+ mod_dynarray_free(&conf->modules);
+}
+
+#define LOG_ARGS(mod_id, msg) "module '%s%s%.*s', " msg, \
+ mod_id->name + 1, (mod_id->len > 0) ? "/" : "", (int)mod_id->len, \
+ mod_id->data
+
+#define MOD_ID_LOG(zone, level, mod_id, msg, ...) \
+ if (zone != NULL) \
+ log_zone_##level(zone, LOG_ARGS(mod_id, msg), ##__VA_ARGS__); \
+ else \
+ log_##level(LOG_ARGS(mod_id, msg), ##__VA_ARGS__);
+
+void conf_activate_modules(
+ conf_t *conf,
+ struct server *server,
+ const knot_dname_t *zone_name,
+ list_t *query_modules,
+ struct query_plan **query_plan)
+{
+ int ret = KNOT_EOK;
+
+ if (conf == NULL || query_modules == NULL || query_plan == NULL) {
+ ret = KNOT_EINVAL;
+ goto activate_error;
+ }
+
+ conf_val_t val;
+
+ // Get list of associated modules.
+ if (zone_name != NULL) {
+ val = conf_zone_get(conf, C_MODULE, zone_name);
+ } else {
+ val = conf_default_get(conf, C_GLOBAL_MODULE);
+ }
+
+ switch (val.code) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT: // Check if a module is configured at all.
+ case KNOT_YP_EINVAL_ID:
+ return;
+ default:
+ ret = val.code;
+ goto activate_error;
+ }
+
+ // Create query plan.
+ *query_plan = query_plan_create();
+ if (*query_plan == NULL) {
+ ret = KNOT_ENOMEM;
+ goto activate_error;
+ }
+
+ // Initialize query modules list.
+ init_list(query_modules);
+
+ // Open the modules.
+ while (val.code == KNOT_EOK) {
+ conf_mod_id_t *mod_id = conf_mod_id(&val);
+ if (mod_id == NULL) {
+ ret = KNOT_ENOMEM;
+ goto activate_error;
+ }
+
+ // Open the module.
+ knotd_mod_t *mod = query_module_open(conf, server, mod_id, *query_plan,
+ zone_name);
+ if (mod == NULL) {
+ MOD_ID_LOG(zone_name, error, mod_id, "failed to open");
+ conf_free_mod_id(mod_id);
+ goto skip_module;
+ }
+
+ // Check the module scope.
+ if ((zone_name == NULL && !(mod->api->flags & KNOTD_MOD_FLAG_SCOPE_GLOBAL)) ||
+ (zone_name != NULL && !(mod->api->flags & KNOTD_MOD_FLAG_SCOPE_ZONE))) {
+ MOD_ID_LOG(zone_name, error, mod_id, "out of scope");
+ query_module_close(mod);
+ goto skip_module;
+ }
+
+ // Check if the module is loadable.
+ if (mod->api->load == NULL) {
+ MOD_ID_LOG(zone_name, debug, mod_id, "empty module, not loaded");
+ query_module_close(mod);
+ goto skip_module;
+ }
+
+ // Load the module.
+ ret = mod->api->load(mod);
+ if (ret != KNOT_EOK) {
+ MOD_ID_LOG(zone_name, error, mod_id, "failed to load (%s)",
+ knot_strerror(ret));
+ query_module_close(mod);
+ goto skip_module;
+ }
+ mod->config = NULL; // Invalidate the current config.
+
+ add_tail(query_modules, &mod->node);
+skip_module:
+ conf_val_next(&val);
+ }
+
+ return;
+activate_error:
+ CONF_LOG(LOG_ERR, "failed to activate modules (%s)", knot_strerror(ret));
+}
+
+void conf_deactivate_modules(
+ list_t *query_modules,
+ struct query_plan **query_plan)
+{
+ if (query_modules == NULL || query_plan == NULL) {
+ return;
+ }
+
+ // Free query plan.
+ query_plan_free(*query_plan);
+ *query_plan = NULL;
+
+ // Free query modules list.
+ knotd_mod_t *mod, *next;
+ WALK_LIST_DELSAFE(mod, next, *query_modules) {
+ if (mod->api->unload != NULL) {
+ mod->api->unload(mod);
+ }
+ query_module_close(mod);
+ }
+ init_list(query_modules);
+}
+
+void conf_reset_modules(
+ conf_t *conf,
+ list_t *query_modules,
+ struct query_plan **query_plan)
+{
+ if (query_modules == NULL || query_plan == NULL) {
+ return;
+ }
+
+ struct query_plan *new_plan = query_plan_create();
+ if (new_plan == NULL) {
+ CONF_LOG(LOG_ERR, "failed to activate modules (%s)", knot_strerror(KNOT_ENOMEM));
+ return;
+ }
+
+ struct query_plan *old_plan = rcu_xchg_pointer(query_plan, NULL);
+ synchronize_rcu();
+ query_plan_free(old_plan);
+
+ knotd_mod_t *mod;
+ WALK_LIST(mod, *query_modules) {
+ if (mod->api->unload != NULL) {
+ mod->api->unload(mod);
+ }
+ query_module_reset(conf, mod, new_plan);
+ }
+
+ knotd_mod_t *next;
+ WALK_LIST_DELSAFE(mod, next, *query_modules) {
+ int ret = mod->api->load(mod);
+ if (ret != KNOT_EOK) {
+ MOD_ID_LOG(mod->zone, error, mod->id, "failed to load (%s)",
+ knot_strerror(ret));
+ rem_node(&mod->node);
+ query_module_close(mod);
+ continue;
+ }
+ mod->config = NULL; // Invalidate the current config.
+ }
+
+ (void)rcu_xchg_pointer(query_plan, new_plan);
+}
diff --git a/src/knot/conf/module.h b/src/knot/conf/module.h
new file mode 100644
index 0000000..a821792
--- /dev/null
+++ b/src/knot/conf/module.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/base.h"
+
+struct server;
+
+/*!
+ * Finds specific module in static or dynamic modules.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] name Module name.
+ * \param[in] len Module name length.
+ * \param[in] temporary Find only a temporary module indication.
+ *
+ * \return Module, NULL if not found.
+ */
+module_t *conf_mod_find(
+ conf_t *conf,
+ const char *name,
+ size_t len,
+ bool temporary
+);
+
+/*!
+ * Loads common static and shared modules.
+ *
+ * \param[in] conf Configuration.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_mod_load_common(
+ conf_t *conf
+);
+
+/*!
+ * Loads extra shared module.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] mod_name Module name.
+ * \param[in] file_name Shared library file name.
+ * \param[in] type Type of module.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int conf_mod_load_extra(
+ conf_t *conf,
+ const char *mod_name,
+ const char *file_name,
+ module_type_t type
+);
+
+/*!
+ * Purges temporary schemas and modules after all modules loading.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] temporary Purge only temporary modules indication.
+ */
+void conf_mod_load_purge(
+ conf_t *conf,
+ bool temporary
+);
+
+/*!
+ * Unloads all shared modules.
+ *
+ * \param[in] conf Configuration.
+ */
+void conf_mod_unload_shared(
+ conf_t *conf
+);
+
+/*!
+ * Activates configured query modules for the specified zone or for all zones.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] zone_name Zone name, NULL for all zones.
+ * \param[in] query_modules Destination query modules list.
+ * \param[in] query_plan Destination query plan.
+ */
+void conf_activate_modules(
+ conf_t *conf,
+ struct server *server,
+ const knot_dname_t *zone_name,
+ list_t *query_modules,
+ struct query_plan **query_plan
+);
+
+/*!
+ * Deactivates query modules list.
+ *
+ * \param[in] query_modules Destination query modules list.
+ * \param[in] query_plan Destination query plan.
+ */
+void conf_deactivate_modules(
+ list_t *query_modules,
+ struct query_plan **query_plan
+);
+
+/*!
+ * Re-activates query modules in list.
+ *
+ * \param[in] conf Configuration.
+ * \param[in] query_modules Query module list.
+ * \param[in] query_plan Query plan.
+ */
+void conf_reset_modules(
+ conf_t *conf,
+ list_t *query_modules,
+ struct query_plan **query_plan
+);
diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c
new file mode 100644
index 0000000..d8472b3
--- /dev/null
+++ b/src/knot/conf/schema.c
@@ -0,0 +1,530 @@
+/* Copyright (C) 2023 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 <limits.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "knot/conf/schema.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/tools.h"
+#include "knot/common/log.h"
+#include "knot/updates/acl.h"
+#include "libknot/rrtype/opt.h"
+#include "libdnssec/tsig.h"
+#include "libdnssec/key.h"
+
+#define HOURS(x) ((x) * 3600)
+#define DAYS(x) ((x) * HOURS(24))
+
+#define KILO(x) (1024LLU * (x))
+#define MEGA(x) (KILO(1024) * (x))
+#define GIGA(x) (MEGA(1024) * (x))
+#define TERA(x) (GIGA(1024) * (x))
+
+#define VIRT_MEM_TOP_32BIT MEGA(500)
+#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP_32BIT)) \
+ ? VIRT_MEM_TOP_32BIT : (x))
+
+static const knot_lookup_t keystore_backends[] = {
+ { KEYSTORE_BACKEND_PEM, "pem" },
+ { KEYSTORE_BACKEND_PKCS11, "pkcs11" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t tsig_key_algs[] = {
+ { DNSSEC_TSIG_HMAC_MD5, "hmac-md5" },
+ { DNSSEC_TSIG_HMAC_SHA1, "hmac-sha1" },
+ { DNSSEC_TSIG_HMAC_SHA224, "hmac-sha224" },
+ { DNSSEC_TSIG_HMAC_SHA256, "hmac-sha256" },
+ { DNSSEC_TSIG_HMAC_SHA384, "hmac-sha384" },
+ { DNSSEC_TSIG_HMAC_SHA512, "hmac-sha512" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t dnssec_key_algs[] = {
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA1, "rsasha1" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3, "rsasha1-nsec3-sha1" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA256, "rsasha256" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA512, "rsasha512" },
+ { DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256, "ecdsap256sha256" },
+ { DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, "ecdsap384sha384" },
+#ifdef HAVE_ED25519
+ { DNSSEC_KEY_ALGORITHM_ED25519, "ed25519" },
+#endif
+#ifdef HAVE_ED448
+ { DNSSEC_KEY_ALGORITHM_ED448, "ed448" },
+#endif
+ { 0, NULL }
+};
+
+static const knot_lookup_t unsafe_operation[] = {
+ { UNSAFE_NONE, "none" },
+ { UNSAFE_KEYSET, "no-check-keyset" },
+ { UNSAFE_DNSKEY, "no-update-dnskey" },
+ { UNSAFE_NSEC, "no-update-nsec" },
+ { UNSAFE_EXPIRED, "no-update-expired" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t cds_cdnskey[] = {
+ { CDS_CDNSKEY_NONE, "none" },
+ { CDS_CDNSKEY_EMPTY, "delete-dnssec" },
+ { CDS_CDNSKEY_ROLLOVER, "rollover" },
+ { CDS_CDNSKEY_ALWAYS, "always" },
+ { CDS_CDNSKEY_DOUBLE_DS, "double-ds" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t dnskey_mgmt[] = {
+ { DNSKEY_MGMT_FULL, "full" },
+ { DNSKEY_MGMT_INCREMENTAL, "incremental" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t cds_digesttype[] = {
+ { DNSSEC_KEY_DIGEST_SHA256, "sha256" },
+ { DNSSEC_KEY_DIGEST_SHA384, "sha384" },
+ { 0, NULL }
+};
+
+const knot_lookup_t acl_actions[] = {
+ { ACL_ACTION_QUERY, "query" },
+ { ACL_ACTION_NOTIFY, "notify" },
+ { ACL_ACTION_TRANSFER, "transfer" },
+ { ACL_ACTION_UPDATE, "update" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t acl_update_owner[] = {
+ { ACL_UPDATE_OWNER_KEY, "key" },
+ { ACL_UPDATE_OWNER_ZONE, "zone" },
+ { ACL_UPDATE_OWNER_NAME, "name" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t acl_update_owner_match[] = {
+ { ACL_UPDATE_MATCH_SUBEQ, "sub-or-equal" },
+ { ACL_UPDATE_MATCH_EQ, "equal" },
+ { ACL_UPDATE_MATCH_SUB, "sub" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t serial_policies[] = {
+ { SERIAL_POLICY_INCREMENT, "increment" },
+ { SERIAL_POLICY_UNIXTIME, "unixtime" },
+ { SERIAL_POLICY_DATESERIAL, "dateserial" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t semantic_checks[] = {
+ { SEMCHECKS_OFF, "off" },
+ { SEMCHECKS_OFF, "false" },
+ { SEMCHECKS_ON, "on" },
+ { SEMCHECKS_ON, "true" },
+ { SEMCHECKS_SOFT, "soft" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t zone_digest[] = {
+ { ZONE_DIGEST_NONE, "none" },
+ { ZONE_DIGEST_SHA384, "zonemd-sha384" },
+ { ZONE_DIGEST_SHA512, "zonemd-sha512" },
+ { ZONE_DIGEST_REMOVE, "remove" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t journal_content[] = {
+ { JOURNAL_CONTENT_NONE, "none" },
+ { JOURNAL_CONTENT_CHANGES, "changes" },
+ { JOURNAL_CONTENT_ALL, "all" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t zonefile_load[] = {
+ { ZONEFILE_LOAD_NONE, "none" },
+ { ZONEFILE_LOAD_DIFF, "difference" },
+ { ZONEFILE_LOAD_DIFSE, "difference-no-serial" },
+ { ZONEFILE_LOAD_WHOLE, "whole" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t log_severities[] = {
+ { LOG_UPTO(LOG_CRIT), "critical" },
+ { LOG_UPTO(LOG_ERR), "error" },
+ { LOG_UPTO(LOG_WARNING), "warning" },
+ { LOG_UPTO(LOG_NOTICE), "notice" },
+ { LOG_UPTO(LOG_INFO), "info" },
+ { LOG_UPTO(LOG_DEBUG), "debug" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t journal_modes[] = {
+ { JOURNAL_MODE_ROBUST, "robust" },
+ { JOURNAL_MODE_ASYNC, "asynchronous" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t catalog_roles[] = {
+ { CATALOG_ROLE_NONE, "none" },
+ { CATALOG_ROLE_INTERPRET, "interpret" },
+ { CATALOG_ROLE_GENERATE, "generate" },
+ { CATALOG_ROLE_MEMBER, "member" },
+ { 0, NULL }
+};
+
+static const knot_lookup_t dbus_events[] = {
+ { DBUS_EVENT_NONE, "none" },
+ { DBUS_EVENT_RUNNING, "running" },
+ { DBUS_EVENT_ZONE_UPDATED, "zone-updated" },
+ { DBUS_EVENT_ZONE_SUBMISSION, "ksk-submission" },
+ { DBUS_EVENT_ZONE_INVALID, "dnssec-invalid" },
+ { 0, NULL }
+};
+
+static const yp_item_t desc_module[] = {
+ { C_ID, YP_TSTR, YP_VNONE, YP_FNONE, { check_module_id } },
+ { C_FILE, YP_TSTR, YP_VNONE },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_server[] = {
+ { C_IDENT, YP_TSTR, YP_VNONE },
+ { C_VERSION, YP_TSTR, YP_VNONE },
+ { C_NSID, YP_THEX, YP_VNONE },
+ { C_RUNDIR, YP_TSTR, YP_VSTR = { RUN_DIR } },
+ { C_USER, YP_TSTR, YP_VNONE },
+ { C_PIDFILE, YP_TSTR, YP_VSTR = { "knot.pid" } },
+ { C_UDP_WORKERS, YP_TINT, YP_VINT = { 1, CONF_MAX_UDP_WORKERS, YP_NIL } },
+ { C_TCP_WORKERS, YP_TINT, YP_VINT = { 1, CONF_MAX_TCP_WORKERS, YP_NIL } },
+ { C_BG_WORKERS, YP_TINT, YP_VINT = { 1, CONF_MAX_BG_WORKERS, YP_NIL } },
+ { C_ASYNC_START, YP_TBOOL, YP_VNONE },
+ { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VINT = { 1, INT32_MAX, 10, YP_STIME } },
+ { C_TCP_IO_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 500 } },
+ { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5000 } },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, YP_NIL } },
+ { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE },
+ { C_TCP_FASTOPEN, YP_TBOOL, YP_VNONE },
+ { C_QUIC_MAX_CLIENTS, YP_TINT, YP_VINT = { 128, INT32_MAX, 10000 } },
+ { C_QUIC_OUTBUF_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), SSIZE_MAX, MEGA(100), YP_SSIZE } },
+ { C_QUIC_IDLE_CLOSE, YP_TINT, YP_VINT = { 1, INT32_MAX, 4, YP_STIME } },
+ { C_RMT_POOL_LIMIT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 } },
+ { C_RMT_POOL_TIMEOUT, YP_TINT, YP_VINT = { 1, INT32_MAX, 5, YP_STIME } },
+ { C_RMT_RETRY_DELAY, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 } },
+ { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD,
+ KNOT_EDNS_MAX_UDP_PAYLOAD,
+ 1232, YP_SSIZE } },
+ { C_UDP_MAX_PAYLOAD_IPV4, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD,
+ KNOT_EDNS_MAX_UDP_PAYLOAD,
+ 1232, YP_SSIZE } },
+ { C_UDP_MAX_PAYLOAD_IPV6, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD,
+ KNOT_EDNS_MAX_UDP_PAYLOAD,
+ 1232, YP_SSIZE } },
+ { C_CERT_FILE, YP_TSTR, YP_VNONE, YP_FNONE, { check_file } },
+ { C_KEY_FILE, YP_TSTR, YP_VNONE, YP_FNONE, { check_file } },
+ { C_ECS, YP_TBOOL, YP_VNONE },
+ { C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
+ { C_AUTO_ACL, YP_TBOOL, YP_VNONE },
+ { C_PROXY_ALLOWLIST, YP_TNET, YP_VNONE, YP_FMULTI},
+ { C_DBUS_EVENT, YP_TOPT, YP_VOPT = { dbus_events, DBUS_EVENT_NONE }, YP_FMULTI },
+ { C_DBUS_INIT_DELAY, YP_TINT, YP_VINT = { 0, INT32_MAX, 1, YP_STIME } },
+ { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_listen } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ // Legacy items.
+ { C_LISTEN_XDP, YP_TADDR, YP_VADDR = { 0 }, YP_FMULTI, { legacy_item } },
+ { C_MAX_TCP_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, 0 }, YP_FNONE, { legacy_item } },
+ { C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } },
+ { C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } },
+ { C_MAX_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { C_MAX_IPV4_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VINT = { 0, INT32_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { NULL }
+};
+
+static const yp_item_t desc_xdp[] = {
+ { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI, { check_xdp_listen } },
+ { C_UDP, YP_TBOOL, YP_VBOOL = { true } },
+ { C_TCP, YP_TBOOL, YP_VNONE },
+ { C_QUIC, YP_TBOOL, YP_VNONE },
+ { C_QUIC_PORT, YP_TINT, YP_VINT = { 1, 65535, 853 } },
+ { C_QUIC_LOG, YP_TBOOL, YP_VNONE },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VINT = { 1024, INT32_MAX, 1000000 } },
+ { C_TCP_INBUF_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), SSIZE_MAX, MEGA(100), YP_SSIZE } },
+ { C_TCP_OUTBUF_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), SSIZE_MAX, MEGA(100), YP_SSIZE } },
+ { C_TCP_IDLE_CLOSE, YP_TINT, YP_VINT = { 1, INT32_MAX, 10, YP_STIME } },
+ { C_TCP_IDLE_RESET, YP_TINT, YP_VINT = { 1, INT32_MAX, 20, YP_STIME } },
+ { C_TCP_RESEND, YP_TINT, YP_VINT = { 1, INT32_MAX, 5, YP_STIME } },
+ { C_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_control[] = {
+ { C_LISTEN, YP_TSTR, YP_VSTR = { "knot.sock" } },
+ { C_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX / 1000, 5, YP_STIME } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_log[] = {
+ { C_TARGET, YP_TSTR, YP_VNONE },
+ { C_SERVER, YP_TOPT, YP_VOPT = { log_severities, 0 } },
+ { C_CTL, YP_TOPT, YP_VOPT = { log_severities, 0 } },
+ { C_ZONE, YP_TOPT, YP_VOPT = { log_severities, 0 } },
+ { C_ANY, YP_TOPT, YP_VOPT = { log_severities, 0 } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_stats[] = {
+ { C_TIMER, YP_TINT, YP_VINT = { 1, UINT32_MAX, 0, YP_STIME } },
+ { C_FILE, YP_TSTR, YP_VSTR = { "stats.yaml" } },
+ { C_APPEND, YP_TBOOL, YP_VNONE },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_database[] = {
+ { C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR } },
+ { C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "journal" } },
+ { C_JOURNAL_DB_MODE, YP_TOPT, YP_VOPT = { journal_modes, JOURNAL_MODE_ROBUST } },
+ { C_JOURNAL_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), VIRT_MEM_LIMIT(TERA(100)),
+ VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE } },
+ { C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" } },
+ { C_KASP_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(5), VIRT_MEM_LIMIT(GIGA(100)),
+ MEGA(500), YP_SSIZE } },
+ { C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" } },
+ { C_TIMER_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(1), VIRT_MEM_LIMIT(GIGA(100)),
+ MEGA(100), YP_SSIZE } },
+ { C_CATALOG_DB, YP_TSTR, YP_VSTR = { "catalog" } },
+ { C_CATALOG_DB_MAX_SIZE, YP_TINT, YP_VINT = { MEGA(5), VIRT_MEM_LIMIT(GIGA(100)),
+ VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_keystore[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_BACKEND, YP_TOPT, YP_VOPT = { keystore_backends, KEYSTORE_BACKEND_PEM },
+ CONF_IO_FRLD_ZONES },
+ { C_CONFIG, YP_TSTR, YP_VSTR = { "keys" }, CONF_IO_FRLD_ZONES },
+ { C_KEY_LABEL, YP_TBOOL, YP_VNONE },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_key[] = {
+ { C_ID, YP_TDNAME, YP_VNONE },
+ { C_ALG, YP_TOPT, YP_VOPT = { tsig_key_algs, DNSSEC_TSIG_UNKNOWN } },
+ { C_SECRET, YP_TB64, YP_VNONE },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_remote[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_ADDR, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI },
+ { C_VIA, YP_TADDR, YP_VNONE, YP_FMULTI },
+ { C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FNONE, { check_ref } },
+ { C_BLOCK_NOTIFY_XFR, YP_TBOOL, YP_VNONE },
+ { C_NO_EDNS, YP_TBOOL, YP_VNONE },
+ { C_AUTO_ACL, YP_TBOOL, YP_VBOOL = { true } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_remotes[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_RMT, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_acl[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_ADDR, YP_TNET, YP_VNONE, YP_FMULTI },
+ { C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FMULTI, { check_ref } },
+ { C_RMT, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } },
+ { C_ACTION, YP_TOPT, YP_VOPT = { acl_actions, ACL_ACTION_QUERY }, YP_FMULTI },
+ { C_DENY, YP_TBOOL, YP_VNONE },
+ { C_UPDATE_TYPE, YP_TDATA, YP_VDATA = { 0, NULL, rrtype_to_bin, rrtype_to_txt },
+ YP_FMULTI, },
+ { C_UPDATE_OWNER, YP_TOPT, YP_VOPT = { acl_update_owner, ACL_UPDATE_OWNER_NONE } },
+ { C_UPDATE_OWNER_MATCH, YP_TOPT, YP_VOPT = { acl_update_owner_match, ACL_UPDATE_MATCH_SUBEQ } },
+ { C_UPDATE_OWNER_NAME, YP_TDATA, YP_VDATA = { 0, NULL, rdname_to_bin, rdname_to_txt },
+ YP_FMULTI, },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_submission[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_PARENT, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } },
+ { C_CHK_INTERVAL, YP_TINT, YP_VINT = { 1, UINT32_MAX, HOURS(1), YP_STIME } },
+ { C_TIMEOUT, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_PARENT_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_policy[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_KEYSTORE, YP_TREF, YP_VREF = { C_KEYSTORE }, CONF_IO_FRLD_ZONES,
+ { check_ref_dflt } },
+ { C_MANUAL, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_SINGLE_TYPE_SIGNING, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_ALG, YP_TOPT, YP_VOPT = { dnssec_key_algs,
+ DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256 },
+ CONF_IO_FRLD_ZONES },
+ { C_KSK_SIZE, YP_TINT, YP_VINT = { 0, UINT16_MAX, YP_NIL, YP_SSIZE },
+ CONF_IO_FRLD_ZONES },
+ { C_ZSK_SIZE, YP_TINT, YP_VINT = { 0, UINT16_MAX, YP_NIL, YP_SSIZE },
+ CONF_IO_FRLD_ZONES },
+ { C_KSK_SHARED, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_DNSKEY_TTL, YP_TINT, YP_VINT = { 0, INT32_MAX, YP_NIL, YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_ZONE_MAX_TTL, YP_TINT, YP_VINT = { 0, INT32_MAX, YP_NIL, YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_KSK_LIFETIME, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_ZSK_LIFETIME, YP_TINT, YP_VINT = { 0, UINT32_MAX, DAYS(30), YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_DELETE_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME } },
+ { C_PROPAG_DELAY, YP_TINT, YP_VINT = { 0, INT32_MAX, HOURS(1), YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_RRSIG_LIFETIME, YP_TINT, YP_VINT = { 1, INT32_MAX, DAYS(14), YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_RRSIG_REFRESH, YP_TINT, YP_VINT = { 1, INT32_MAX, YP_NIL, YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_RRSIG_PREREFRESH, YP_TINT, YP_VINT = { 0, INT32_MAX, HOURS(1), YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_REPRO_SIGNING, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_NSEC3, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_NSEC3_ITER, YP_TINT, YP_VINT = { 0, UINT16_MAX, 0 }, CONF_IO_FRLD_ZONES },
+ { C_NSEC3_OPT_OUT, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_NSEC3_SALT_LEN, YP_TINT, YP_VINT = { 0, UINT8_MAX, 8 }, CONF_IO_FRLD_ZONES },
+ { C_NSEC3_SALT_LIFETIME, YP_TINT, YP_VINT = { -1, UINT32_MAX, DAYS(30), YP_STIME },
+ CONF_IO_FRLD_ZONES },
+ { C_SIGNING_THREADS, YP_TINT, YP_VINT = { 1, UINT16_MAX, 1 } },
+ { C_KSK_SBM, YP_TREF, YP_VREF = { C_SBM }, CONF_IO_FRLD_ZONES,
+ { check_ref } },
+ { C_DS_PUSH, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | CONF_IO_FRLD_ZONES,
+ { check_ref } },
+ { C_CDS_CDNSKEY, YP_TOPT, YP_VOPT = { cds_cdnskey, CDS_CDNSKEY_ROLLOVER },
+ CONF_IO_FRLD_ZONES },
+ { C_CDS_DIGESTTYPE, YP_TOPT, YP_VOPT = { cds_digesttype, DNSSEC_KEY_DIGEST_SHA256 },
+ CONF_IO_FRLD_ZONES },
+ { C_DNSKEY_MGMT, YP_TOPT, YP_VOPT = { dnskey_mgmt, DNSKEY_MGMT_FULL },
+ CONF_IO_FRLD_ZONES },
+ { C_OFFLINE_KSK, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES },
+ { C_UNSAFE_OPERATION, YP_TOPT, YP_VOPT = { unsafe_operation, UNSAFE_NONE }, YP_FMULTI },
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+ { NULL }
+};
+
+#define ZONE_ITEMS(FLAGS) \
+ { C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \
+ { C_FILE, YP_TSTR, YP_VNONE, FLAGS }, \
+ { C_MASTER, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } }, \
+ { C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \
+ { C_NOTIFY, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI, { check_ref } }, \
+ { C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \
+ { C_PROVIDE_IXFR, YP_TBOOL, YP_VBOOL = { true } }, \
+ { C_SEM_CHECKS, YP_TOPT, YP_VOPT = { semantic_checks, SEMCHECKS_OFF }, FLAGS }, \
+ { C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \
+ { C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { zonefile_load, ZONEFILE_LOAD_WHOLE } }, \
+ { C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { journal_content, JOURNAL_CONTENT_CHANGES }, FLAGS }, \
+ { C_JOURNAL_MAX_USAGE, YP_TINT, YP_VINT = { KILO(40), SSIZE_MAX, MEGA(100), YP_SSIZE } }, \
+ { C_JOURNAL_MAX_DEPTH, YP_TINT, YP_VINT = { 2, SSIZE_MAX, 20 } }, \
+ { C_ZONE_MAX_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, SSIZE_MAX, YP_SSIZE }, FLAGS }, \
+ { C_ADJUST_THR, YP_TINT, YP_VINT = { 1, UINT16_MAX, 1 } }, \
+ { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE, FLAGS }, \
+ { C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE, FLAGS }, \
+ { C_DNSSEC_POLICY, YP_TREF, YP_VREF = { C_POLICY }, FLAGS, { check_ref_dflt } }, \
+ { C_DS_PUSH, YP_TREF, YP_VREF = { C_RMT, C_RMTS }, YP_FMULTI | FLAGS, \
+ { check_ref } }, \
+ { C_SERIAL_POLICY, YP_TOPT, YP_VOPT = { serial_policies, SERIAL_POLICY_INCREMENT } }, \
+ { C_ZONEMD_GENERATE, YP_TOPT, YP_VOPT = { zone_digest, ZONE_DIGEST_NONE }, FLAGS }, \
+ { C_ZONEMD_VERIFY, YP_TBOOL, YP_VNONE, FLAGS }, \
+ { C_REFRESH_MIN_INTERVAL,YP_TINT, YP_VINT = { 2, UINT32_MAX, 2, YP_STIME } }, \
+ { C_REFRESH_MAX_INTERVAL,YP_TINT, YP_VINT = { 2, UINT32_MAX, UINT32_MAX, YP_STIME } }, \
+ { C_RETRY_MIN_INTERVAL, YP_TINT, YP_VINT = { 1, UINT32_MAX, 1, YP_STIME } }, \
+ { C_RETRY_MAX_INTERVAL, YP_TINT, YP_VINT = { 1, UINT32_MAX, UINT32_MAX, YP_STIME } }, \
+ { C_EXPIRE_MIN_INTERVAL, YP_TINT, YP_VINT = { 3, UINT32_MAX, 3, YP_STIME } }, \
+ { C_EXPIRE_MAX_INTERVAL, YP_TINT, YP_VINT = { 3, UINT32_MAX, UINT32_MAX, YP_STIME } }, \
+ { C_CATALOG_ROLE, YP_TOPT, YP_VOPT = { catalog_roles, CATALOG_ROLE_NONE }, FLAGS }, \
+ { C_CATALOG_TPL, YP_TREF, YP_VREF = { C_TPL }, YP_FMULTI | FLAGS, { check_ref } }, \
+ { C_CATALOG_ZONE, YP_TDNAME,YP_VNONE, FLAGS | CONF_IO_FRLD_ZONES }, \
+ { C_CATALOG_GROUP, YP_TSTR, YP_VNONE, FLAGS | CONF_IO_FRLD_ZONES, { check_catalog_group } }, \
+ { C_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \
+ YP_FMULTI | FLAGS, { check_modref } }, \
+ { C_COMMENT, YP_TSTR, YP_VNONE }, \
+ /* Legacy items.*/ \
+ { C_DISABLE_ANY, YP_TBOOL, YP_VNONE, YP_FNONE, { legacy_item } }, \
+ { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } }, \
+ { C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } }, \
+ { C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0 }, YP_FNONE, { legacy_item } }, \
+ { C_MAX_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } }, \
+ { C_MIN_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_STIME }, YP_FNONE, { legacy_item } }, \
+
+static const yp_item_t desc_template[] = {
+ { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF },
+ { C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt },
+ YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } },
+ ZONE_ITEMS(CONF_IO_FRLD_ZONES)
+ // Legacy items.
+ { C_TIMER_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
+ { C_MAX_TIMER_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
+ { C_JOURNAL_DB_MODE, YP_TOPT, YP_VOPT = { journal_modes, 0 }, YP_FNONE, { legacy_item } },
+ { C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { C_KASP_DB, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { legacy_item } },
+ { C_MAX_KASP_DB_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, 0, YP_SSIZE }, YP_FNONE, { legacy_item } },
+ { NULL }
+};
+
+static const yp_item_t desc_zone[] = {
+ { C_DOMAIN, YP_TDNAME, YP_VNONE, CONF_IO_FRLD_ZONE },
+ { C_TPL, YP_TREF, YP_VREF = { C_TPL }, CONF_IO_FRLD_ZONE, { check_ref } },
+ ZONE_ITEMS(CONF_IO_FRLD_ZONE)
+ { NULL }
+};
+
+const yp_item_t conf_schema[] = {
+ { C_MODULE, YP_TGRP, YP_VGRP = { desc_module }, YP_FMULTI | CONF_IO_FRLD_ALL |
+ CONF_IO_FCHECK_ZONES, { load_module } },
+ { C_SRV, YP_TGRP, YP_VGRP = { desc_server }, CONF_IO_FRLD_SRV, { check_server } },
+ { C_XDP, YP_TGRP, YP_VGRP = { desc_xdp }, CONF_IO_FRLD_SRV, { check_xdp } },
+ { C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
+ { C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI | CONF_IO_FRLD_LOG },
+ { C_STATS, YP_TGRP, YP_VGRP = { desc_stats }, CONF_IO_FRLD_SRV },
+ { C_DB, YP_TGRP, YP_VGRP = { desc_database }, CONF_IO_FRLD_SRV, { check_database } },
+ { C_KEYSTORE, YP_TGRP, YP_VGRP = { desc_keystore }, YP_FMULTI, { check_keystore } },
+ { C_KEY, YP_TGRP, YP_VGRP = { desc_key }, YP_FMULTI, { check_key } },
+ { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
+ { C_RMTS, YP_TGRP, YP_VGRP = { desc_remotes }, YP_FMULTI, { check_remotes } },
+ { C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI, { check_acl } },
+ { C_SBM, YP_TGRP, YP_VGRP = { desc_submission }, YP_FMULTI },
+ { C_POLICY, YP_TGRP, YP_VGRP = { desc_policy }, YP_FMULTI, { check_policy } },
+ { C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
+ { C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI | CONF_IO_FZONE, { check_zone } },
+ { C_INCL, YP_TSTR, YP_VNONE, CONF_IO_FDIFF_ZONES | CONF_IO_FRLD_ALL, { include_file } },
+ { NULL }
+};
diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h
new file mode 100644
index 0000000..5850acc
--- /dev/null
+++ b/src/knot/conf/schema.h
@@ -0,0 +1,279 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include "libknot/lookup.h"
+#include "libknot/yparser/ypschema.h"
+
+#define C_ACL "\x03""acl"
+#define C_ACTION "\x06""action"
+#define C_ADDR "\x07""address"
+#define C_ADJUST_THR "\x0E""adjust-threads"
+#define C_ALG "\x09""algorithm"
+#define C_ANS_ROTATION "\x0F""answer-rotation"
+#define C_ANY "\x03""any"
+#define C_APPEND "\x06""append"
+#define C_ASYNC_START "\x0B""async-start"
+#define C_AUTO_ACL "\x0D""automatic-acl"
+#define C_BACKEND "\x07""backend"
+#define C_BG_WORKERS "\x12""background-workers"
+#define C_BLOCK_NOTIFY_XFR "\x1B""block-notify-after-transfer"
+#define C_CATALOG_DB "\x0A""catalog-db"
+#define C_CATALOG_DB_MAX_SIZE "\x13""catalog-db-max-size"
+#define C_CATALOG_GROUP "\x0D""catalog-group"
+#define C_CATALOG_ROLE "\x0C""catalog-role"
+#define C_CATALOG_TPL "\x10""catalog-template"
+#define C_CATALOG_ZONE "\x0C""catalog-zone"
+#define C_CDS_CDNSKEY "\x13""cds-cdnskey-publish"
+#define C_CDS_DIGESTTYPE "\x0F""cds-digest-type"
+#define C_CERT_FILE "\x09""cert-file"
+#define C_CHK_INTERVAL "\x0E""check-interval"
+#define C_COMMENT "\x07""comment"
+#define C_CONFIG "\x06""config"
+#define C_CTL "\x07""control"
+#define C_DB "\x08""database"
+#define C_DBUS_EVENT "\x0A""dbus-event"
+#define C_DBUS_INIT_DELAY "\x0F""dbus-init-delay"
+#define C_DDNS_MASTER "\x0B""ddns-master"
+#define C_DENY "\x04""deny"
+#define C_DNSKEY_MGMT "\x11""dnskey-management"
+#define C_DNSKEY_TTL "\x0A""dnskey-ttl"
+#define C_DNSSEC_POLICY "\x0D""dnssec-policy"
+#define C_DNSSEC_SIGNING "\x0E""dnssec-signing"
+#define C_DNSSEC_VALIDATION "\x11""dnssec-validation"
+#define C_DOMAIN "\x06""domain"
+#define C_DS_PUSH "\x07""ds-push"
+#define C_ECS "\x12""edns-client-subnet"
+#define C_EXPIRE_MAX_INTERVAL "\x13""expire-max-interval"
+#define C_EXPIRE_MIN_INTERVAL "\x13""expire-min-interval"
+#define C_FILE "\x04""file"
+#define C_GLOBAL_MODULE "\x0D""global-module"
+#define C_ID "\x02""id"
+#define C_IDENT "\x08""identity"
+#define C_INCL "\x07""include"
+#define C_JOURNAL_CONTENT "\x0F""journal-content"
+#define C_JOURNAL_DB "\x0A""journal-db"
+#define C_JOURNAL_DB_MAX_SIZE "\x13""journal-db-max-size"
+#define C_JOURNAL_DB_MODE "\x0F""journal-db-mode"
+#define C_JOURNAL_MAX_DEPTH "\x11""journal-max-depth"
+#define C_JOURNAL_MAX_USAGE "\x11""journal-max-usage"
+#define C_KASP_DB "\x07""kasp-db"
+#define C_KASP_DB_MAX_SIZE "\x10""kasp-db-max-size"
+#define C_DELETE_DELAY "\x0C""delete-delay"
+#define C_KEY "\x03""key"
+#define C_KEYSTORE "\x08""keystore"
+#define C_KEY_FILE "\x08""key-file"
+#define C_KEY_LABEL "\x09""key-label"
+#define C_KSK_LIFETIME "\x0C""ksk-lifetime"
+#define C_KSK_SBM "\x0E""ksk-submission"
+#define C_KSK_SHARED "\x0a""ksk-shared"
+#define C_KSK_SIZE "\x08""ksk-size"
+#define C_LISTEN "\x06""listen"
+#define C_LOG "\x03""log"
+#define C_MANUAL "\x06""manual"
+#define C_MASTER "\x06""master"
+#define C_MODULE "\x06""module"
+#define C_NO_EDNS "\x07""no-edns"
+#define C_NOTIFY "\x06""notify"
+#define C_NSEC3 "\x05""nsec3"
+#define C_NSEC3_ITER "\x10""nsec3-iterations"
+#define C_NSEC3_OPT_OUT "\x0D""nsec3-opt-out"
+#define C_NSEC3_SALT_LEN "\x11""nsec3-salt-length"
+#define C_NSEC3_SALT_LIFETIME "\x13""nsec3-salt-lifetime"
+#define C_NSID "\x04""nsid"
+#define C_OFFLINE_KSK "\x0B""offline-ksk"
+#define C_PARENT "\x06""parent"
+#define C_PARENT_DELAY "\x0C""parent-delay"
+#define C_PIDFILE "\x07""pidfile"
+#define C_POLICY "\x06""policy"
+#define C_PROPAG_DELAY "\x11""propagation-delay"
+#define C_PROVIDE_IXFR "\x0C""provide-ixfr"
+#define C_PROXY_ALLOWLIST "\x0F""proxy-allowlist"
+#define C_QUIC "\x04""quic"
+#define C_QUIC_IDLE_CLOSE "\x17""quic-idle-close-timeout"
+#define C_QUIC_LOG "\x08""quic-log"
+#define C_QUIC_MAX_CLIENTS "\x10""quic-max-clients"
+#define C_QUIC_OUTBUF_MAX_SIZE "\x14""quic-outbuf-max-size"
+#define C_QUIC_PORT "\x09""quic-port"
+#define C_REFRESH_MAX_INTERVAL "\x14""refresh-max-interval"
+#define C_REFRESH_MIN_INTERVAL "\x14""refresh-min-interval"
+#define C_REPRO_SIGNING "\x14""reproducible-signing"
+#define C_RETRY_MAX_INTERVAL "\x12""retry-max-interval"
+#define C_RETRY_MIN_INTERVAL "\x12""retry-min-interval"
+#define C_RMT "\x06""remote"
+#define C_RMTS "\x07""remotes"
+#define C_RMT_POOL_LIMIT "\x11""remote-pool-limit"
+#define C_RMT_POOL_TIMEOUT "\x13""remote-pool-timeout"
+#define C_RMT_RETRY_DELAY "\x12""remote-retry-delay"
+#define C_ROUTE_CHECK "\x0B""route-check"
+#define C_RRSIG_LIFETIME "\x0E""rrsig-lifetime"
+#define C_RRSIG_PREREFRESH "\x11""rrsig-pre-refresh"
+#define C_RRSIG_REFRESH "\x0D""rrsig-refresh"
+#define C_RUNDIR "\x06""rundir"
+#define C_SBM "\x0A""submission"
+#define C_SECRET "\x06""secret"
+#define C_SEM_CHECKS "\x0F""semantic-checks"
+#define C_SERIAL_POLICY "\x0D""serial-policy"
+#define C_SERVER "\x06""server"
+#define C_SIGNING_THREADS "\x0F""signing-threads"
+#define C_SINGLE_TYPE_SIGNING "\x13""single-type-signing"
+#define C_SOCKET_AFFINITY "\x0F""socket-affinity"
+#define C_SRV "\x06""server"
+#define C_STATS "\x0A""statistics"
+#define C_STORAGE "\x07""storage"
+#define C_TARGET "\x06""target"
+#define C_TCP "\x03""tcp"
+#define C_TCP_FASTOPEN "\x0C""tcp-fastopen"
+#define C_TCP_IDLE_CLOSE "\x16""tcp-idle-close-timeout"
+#define C_TCP_IDLE_RESET "\x16""tcp-idle-reset-timeout"
+#define C_TCP_IDLE_TIMEOUT "\x10""tcp-idle-timeout"
+#define C_TCP_INBUF_MAX_SIZE "\x12""tcp-inbuf-max-size"
+#define C_TCP_IO_TIMEOUT "\x0E""tcp-io-timeout"
+#define C_TCP_MAX_CLIENTS "\x0F""tcp-max-clients"
+#define C_TCP_OUTBUF_MAX_SIZE "\x13""tcp-outbuf-max-size"
+#define C_TCP_RESEND "\x12""tcp-resend-timeout"
+#define C_TCP_REUSEPORT "\x0D""tcp-reuseport"
+#define C_TCP_RMT_IO_TIMEOUT "\x15""tcp-remote-io-timeout"
+#define C_TCP_WORKERS "\x0B""tcp-workers"
+#define C_TIMEOUT "\x07""timeout"
+#define C_TIMER "\x05""timer"
+#define C_TIMER_DB "\x08""timer-db"
+#define C_TIMER_DB_MAX_SIZE "\x11""timer-db-max-size"
+#define C_TPL "\x08""template"
+#define C_UDP "\x03""udp"
+#define C_UDP_MAX_PAYLOAD "\x0F""udp-max-payload"
+#define C_UDP_MAX_PAYLOAD_IPV4 "\x14""udp-max-payload-ipv4"
+#define C_UDP_MAX_PAYLOAD_IPV6 "\x14""udp-max-payload-ipv6"
+#define C_UDP_WORKERS "\x0B""udp-workers"
+#define C_UNSAFE_OPERATION "\x10""unsafe-operation"
+#define C_UPDATE_OWNER "\x0C""update-owner"
+#define C_UPDATE_OWNER_MATCH "\x12""update-owner-match"
+#define C_UPDATE_OWNER_NAME "\x11""update-owner-name"
+#define C_UPDATE_TYPE "\x0B""update-type"
+#define C_USER "\x04""user"
+#define C_VERSION "\x07""version"
+#define C_VIA "\x03""via"
+#define C_XDP "\x03""xdp"
+#define C_ZONE "\x04""zone"
+#define C_ZONEFILE_LOAD "\x0D""zonefile-load"
+#define C_ZONEFILE_SYNC "\x0D""zonefile-sync"
+#define C_ZONEMD_GENERATE "\x0F""zonemd-generate"
+#define C_ZONEMD_VERIFY "\x0D""zonemd-verify"
+#define C_ZONE_MAX_SIZE "\x0D""zone-max-size"
+#define C_ZONE_MAX_TTL "\x0C""zone-max-ttl"
+#define C_ZSK_LIFETIME "\x0C""zsk-lifetime"
+#define C_ZSK_SIZE "\x08""zsk-size"
+
+// Legacy items.
+#define C_DISABLE_ANY "\x0B""disable-any"
+#define C_LISTEN_XDP "\x0A""listen-xdp"
+#define C_MAX_TIMER_DB_SIZE "\x11""max-timer-db-size"
+#define C_MAX_JOURNAL_DB_SIZE "\x13""max-journal-db-size"
+#define C_MAX_KASP_DB_SIZE "\x10""max-kasp-db-size"
+#define C_TCP_HSHAKE_TIMEOUT "\x15""tcp-handshake-timeout"
+#define C_TCP_REPLY_TIMEOUT "\x11""tcp-reply-timeout"
+#define C_MAX_TCP_CLIENTS "\x0F""max-tcp-clients"
+#define C_MAX_UDP_PAYLOAD "\x0F""max-udp-payload"
+#define C_MAX_IPV4_UDP_PAYLOAD "\x14""max-ipv4-udp-payload"
+#define C_MAX_IPV6_UDP_PAYLOAD "\x14""max-ipv6-udp-payload"
+#define C_MAX_ZONE_SIZE "\x0D""max-zone-size"
+#define C_MAX_REFRESH_INTERVAL "\x14""max-refresh-interval"
+#define C_MIN_REFRESH_INTERVAL "\x14""min-refresh-interval"
+#define C_MAX_JOURNAL_DEPTH "\x11""max-journal-depth"
+#define C_MAX_JOURNAL_USAGE "\x11""max-journal-usage"
+
+enum {
+ KEYSTORE_BACKEND_PEM = 1,
+ KEYSTORE_BACKEND_PKCS11 = 2,
+};
+
+enum {
+ UNSAFE_NONE = 0,
+ UNSAFE_KEYSET = (1 << 0),
+ UNSAFE_DNSKEY = (1 << 1),
+ UNSAFE_NSEC = (1 << 2),
+ UNSAFE_EXPIRED = (1 << 3),
+};
+
+enum {
+ CDS_CDNSKEY_NONE = 0,
+ CDS_CDNSKEY_EMPTY = 1,
+ CDS_CDNSKEY_ROLLOVER = 2,
+ CDS_CDNSKEY_ALWAYS = 3,
+ CDS_CDNSKEY_DOUBLE_DS = 4,
+};
+
+enum {
+ DNSKEY_MGMT_FULL = 0,
+ DNSKEY_MGMT_INCREMENTAL = 1,
+};
+
+enum {
+ SERIAL_POLICY_INCREMENT = 1,
+ SERIAL_POLICY_UNIXTIME = 2,
+ SERIAL_POLICY_DATESERIAL = 3,
+};
+
+enum {
+ SEMCHECKS_OFF = 0,
+ SEMCHECKS_ON = 1,
+ SEMCHECKS_SOFT = 2,
+};
+
+enum {
+ ZONE_DIGEST_NONE = 0,
+ ZONE_DIGEST_SHA384 = 1,
+ ZONE_DIGEST_SHA512 = 2,
+ ZONE_DIGEST_REMOVE = 255,
+};
+
+enum {
+ JOURNAL_CONTENT_NONE = 0,
+ JOURNAL_CONTENT_CHANGES = 1,
+ JOURNAL_CONTENT_ALL = 2,
+};
+
+enum {
+ JOURNAL_MODE_ROBUST = 0, // Robust journal DB disk synchronization.
+ JOURNAL_MODE_ASYNC = 1, // Asynchronous journal DB disk synchronization.
+};
+
+enum {
+ ZONEFILE_LOAD_NONE = 0,
+ ZONEFILE_LOAD_DIFF = 1,
+ ZONEFILE_LOAD_WHOLE = 2,
+ ZONEFILE_LOAD_DIFSE = 3,
+};
+
+enum {
+ CATALOG_ROLE_NONE = 0,
+ CATALOG_ROLE_INTERPRET = 1,
+ CATALOG_ROLE_GENERATE = 2,
+ CATALOG_ROLE_MEMBER = 3,
+};
+
+enum {
+ DBUS_EVENT_NONE = 0,
+ DBUS_EVENT_RUNNING = (1 << 0),
+ DBUS_EVENT_ZONE_UPDATED = (1 << 1),
+ DBUS_EVENT_ZONE_SUBMISSION = (1 << 2),
+ DBUS_EVENT_ZONE_INVALID = (1 << 3),
+};
+
+extern const knot_lookup_t acl_actions[];
+
+extern const yp_item_t conf_schema[];
diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c
new file mode 100644
index 0000000..6823a05
--- /dev/null
+++ b/src/knot/conf/tools.c
@@ -0,0 +1,1069 @@
+/* Copyright (C) 2022 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 <glob.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifdef ENABLE_XDP
+#include <netinet/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#endif
+
+#include "libdnssec/key.h"
+#include "knot/catalog/catalog_db.h"
+#include "knot/conf/tools.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/module.h"
+#include "knot/conf/schema.h"
+#include "knot/common/log.h"
+#include "libknot/errcode.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/xdp.h"
+#include "contrib/files.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "contrib/wire_ctx.h"
+
+#define MAX_INCLUDE_DEPTH 5
+
+char check_str[1024];
+
+int legacy_item(
+ knotd_conf_check_args_t *args)
+{
+ CONF_LOG(LOG_NOTICE, "line %zu, option '%s.%s' is obsolete and has no effect",
+ args->extra->line, args->item->parent->name + 1,
+ args->item->name + 1);
+
+ return KNOT_EOK;
+}
+
+static bool is_default_id(
+ const uint8_t *id,
+ size_t id_len)
+{
+ return id_len == CONF_DEFAULT_ID[0] &&
+ memcmp(id, CONF_DEFAULT_ID + 1, id_len) == 0;
+}
+
+int conf_exec_callbacks(
+ knotd_conf_check_args_t *args)
+{
+ if (args == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ for (size_t i = 0; i < YP_MAX_MISC_COUNT; i++) {
+ int (*fcn)(knotd_conf_check_args_t *) = args->item->misc[i];
+ if (fcn == NULL) {
+ break;
+ }
+
+ int ret = fcn(args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int mod_id_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Check for "mod_name/mod_id" format.
+ const uint8_t *pos = (uint8_t *)strchr((char *)in->position, '/');
+ if (pos == in->position) {
+ // Missing module name.
+ return KNOT_EINVAL;
+ } else if (pos >= stop - 1) {
+ // Missing module identifier after slash.
+ return KNOT_EINVAL;
+ }
+
+ // Write mod_name in the yp_name_t format.
+ uint8_t name_len = (pos != NULL) ? (pos - in->position) :
+ wire_ctx_available(in);
+ wire_ctx_write_u8(out, name_len);
+ wire_ctx_write(out, in->position, name_len);
+ wire_ctx_skip(in, name_len);
+
+ // Check for mod_id.
+ if (pos != NULL) {
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Write mod_id as a zero terminated string.
+ int ret = yp_str_to_bin(in, out, stop);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ YP_CHECK_RET;
+}
+
+int mod_id_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Write mod_name.
+ uint8_t name_len = wire_ctx_read_u8(in);
+ wire_ctx_write(out, in->position, name_len);
+ wire_ctx_skip(in, name_len);
+
+ // Check for mod_id.
+ if (wire_ctx_available(in) > 0) {
+ // Write the separator.
+ wire_ctx_write_u8(out, '/');
+
+ // Write mod_id.
+ int ret = yp_str_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ YP_CHECK_RET;
+}
+
+int rrtype_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ uint16_t type;
+ int ret = knot_rrtype_from_string((char *)in->position, &type);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_write_u64(out, type);
+
+ YP_CHECK_RET;
+}
+
+int rrtype_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ uint16_t type = (uint16_t)wire_ctx_read_u64(in);
+ int ret = knot_rrtype_to_string(type, (char *)out->position, out->size);
+ if (ret < 0) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+int rdname_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ int ret = yp_dname_to_bin(in, out, stop);
+ if (ret == KNOT_EOK && in->wire[in->size - 1] != '.') {
+ // If non-FQDN, trim off the zero label.
+ wire_ctx_skip(out, -1);
+ }
+
+ YP_CHECK_RET;
+}
+
+int rdname_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Temporarily normalize the input.
+ if (in->wire[in->size - 1] == '\0') {
+ return yp_dname_to_txt(in, out);
+ }
+
+ knot_dname_storage_t full_name;
+ wire_ctx_t ctx = wire_ctx_init(full_name, sizeof(full_name));
+ wire_ctx_write(&ctx, in->wire, in->size);
+ wire_ctx_write(&ctx, "\0", 1);
+ wire_ctx_set_offset(&ctx, 0);
+
+ int ret = yp_dname_to_txt(&ctx, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Trim off the trailing dot.
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+int check_ref(
+ knotd_conf_check_args_t *args)
+{
+ const yp_item_t *ref = args->item->var.r.ref;
+ const yp_item_t *ref2 = args->item->var.r.grp_ref;
+
+ bool found1 = false, found2 = false;
+
+ // Try to find the id in the first section.
+ found1 = conf_rawid_exists_txn(args->extra->conf, args->extra->txn,
+ ref->name, args->data, args->data_len);
+ if (ref2 != NULL) {
+ // Try to find the id in the second section if supported.
+ found2 = conf_rawid_exists_txn(args->extra->conf, args->extra->txn,
+ ref2->name, args->data, args->data_len);
+ }
+
+ if (found1 == found2) {
+ if (found1) {
+ args->err_str = "ambiguous reference";
+ return KNOT_ENOENT;
+ } else {
+ args->err_str = "invalid reference";
+ return KNOT_ENOENT;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int check_ref_dflt(
+ knotd_conf_check_args_t *args)
+{
+ if (check_ref(args) != KNOT_EOK && !is_default_id(args->data, args->data_len)) {
+ args->err_str = "invalid reference";
+ return KNOT_ENOENT;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_listen(
+ knotd_conf_check_args_t *args)
+{
+ bool no_port;
+ struct sockaddr_storage ss = yp_addr(args->data, &no_port);
+ if (!no_port && sockaddr_port(&ss) == 0) {
+ args->err_str = "invalid port";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_xdp_listen(
+ knotd_conf_check_args_t *args)
+{
+#ifndef ENABLE_XDP
+ args->err_str = "XDP is not available";
+ return KNOT_ENOTSUP;
+#else
+ bool no_port;
+ struct sockaddr_storage ss = yp_addr(args->data, &no_port);
+ conf_xdp_iface_t if_new;
+ int ret = conf_xdp_iface(&ss, &if_new);
+ if (ret != KNOT_EOK) {
+ args->err_str = "invalid XDP interface specification";
+ return ret;
+ } else if (!no_port && if_new.port == 0) {
+ args->err_str = "invalid port";
+ return KNOT_EINVAL;
+ }
+
+ conf_val_t xdp = conf_get_txn(args->extra->conf, args->extra->txn, C_XDP,
+ C_LISTEN);
+ size_t count = conf_val_count(&xdp);
+ while (xdp.code == KNOT_EOK && count-- > 1) {
+ struct sockaddr_storage addr = conf_addr(&xdp, NULL);
+ conf_xdp_iface_t if_prev;
+ ret = conf_xdp_iface(&addr, &if_prev);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (strcmp(if_new.name, if_prev.name) == 0) {
+ args->err_str = "duplicate XDP interface specification";
+ return KNOT_EINVAL;
+ }
+ conf_val_next(&xdp);
+ }
+
+ return KNOT_EOK;
+#endif
+}
+
+static int dir_exists(const char *dir)
+{
+ struct stat st;
+ if (stat(dir, &st) != 0) {
+ return knot_map_errno();
+ } else if (!S_ISDIR(st.st_mode)) {
+ return KNOT_ENOTDIR;
+ } else if (access(dir, W_OK) != 0) {
+ return knot_map_errno();
+ } else {
+ return KNOT_EOK;
+ }
+}
+
+static int dir_can_create(const char *dir)
+{
+ int ret = dir_exists(dir);
+ if (ret == KNOT_ENOENT) {
+ return KNOT_EOK;
+ } else {
+ return ret;
+ }
+}
+
+static void check_db(
+ knotd_conf_check_args_t *args,
+ const yp_name_t *db_type,
+ int (*check_fun)(const char *),
+ const char *desc)
+{
+ if (db_type != NULL) {
+ conf_val_t val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_DB, db_type);
+ if (val.code != KNOT_EOK) {
+ // Don't check implicit database values.
+ return;
+ }
+ }
+
+ char *db = conf_db_txn(args->extra->conf, args->extra->txn, db_type);
+ int ret = check_fun(db);
+ if (ret != KNOT_EOK) {
+ CONF_LOG(LOG_WARNING, "%s '%s' %s", desc, db,
+ (ret == KNOT_EACCES ? "not writable" : knot_strerror(ret)));
+ }
+ free(db);
+}
+
+int check_database(
+ knotd_conf_check_args_t *args)
+{
+ check_db(args, NULL, dir_exists, "database storage");
+ check_db(args, C_TIMER_DB, dir_can_create, "timer database");
+ check_db(args, C_JOURNAL_DB, dir_can_create, "journal database");
+ check_db(args, C_KASP_DB, dir_can_create, "KASP database");
+ check_db(args, C_CATALOG_DB, dir_can_create, "catalog database");
+
+ return KNOT_EOK;
+}
+
+int check_modref(
+ knotd_conf_check_args_t *args)
+{
+ const yp_name_t *mod_name = (const yp_name_t *)args->data;
+ const uint8_t *id = args->data + 1 + args->data[0];
+ size_t id_len = args->data_len - 1 - args->data[0];
+
+ // Check if the module is ever available.
+ const module_t *mod = conf_mod_find(args->extra->conf, mod_name + 1,
+ mod_name[0], args->extra->check);
+ if (mod == NULL) {
+ args->err_str = "unknown module";
+ return KNOT_EINVAL;
+ }
+
+ // Check if the module requires some configuration.
+ if (id_len == 0) {
+ if (mod->api->flags & KNOTD_MOD_FLAG_OPT_CONF) {
+ return KNOT_EOK;
+ } else {
+ args->err_str = "missing module configuration";
+ return KNOT_YP_ENOID;
+ }
+ }
+
+ // Try to find a module with the id.
+ if (!conf_rawid_exists_txn(args->extra->conf, args->extra->txn, mod_name,
+ id, id_len)) {
+ args->err_str = "invalid module reference";
+ return KNOT_ENOENT;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_module_id(
+ knotd_conf_check_args_t *args)
+{
+ const size_t len = strlen(KNOTD_MOD_NAME_PREFIX);
+
+ if (strncmp((const char *)args->id, KNOTD_MOD_NAME_PREFIX, len) != 0) {
+ args->err_str = "required 'mod-' prefix";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_file(
+ knotd_conf_check_args_t *args)
+{
+ char *path = abs_path((const char *)args->data, CONFIG_DIR);
+
+ struct stat st;
+ int ret = stat(path, &st);
+ free(path);
+
+ if (ret != 0) {
+ args->err_str = "invalid file";
+ return KNOT_EINVAL;
+ } else if(!S_ISREG(st.st_mode)) {
+ args->err_str = "not a file";
+ return KNOT_EINVAL;
+ } else {
+ return KNOT_EOK;
+ }
+}
+
+#define CHECK_LEGACY_NAME(section, old_item, new_item) { \
+ conf_val_t val = conf_get_txn(args->extra->conf, args->extra->txn, \
+ section, old_item); \
+ if (val.code == KNOT_EOK) { \
+ CONF_LOG(LOG_NOTICE, "option '%s.%s' has no effect, " \
+ "use option '%s.%s' instead", \
+ &section[1], &old_item[1], \
+ &section[1], &new_item[1]); \
+ } \
+}
+
+#define CHECK_LEGACY_NAME_ID(section, old_item, new_item) { \
+ conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, \
+ section, old_item, args->id, args->id_len); \
+ if (val.code == KNOT_EOK) { \
+ CONF_LOG(LOG_NOTICE, "option '%s.%s' has no effect, " \
+ "use option '%s.%s' instead", \
+ &section[1], &old_item[1], \
+ &section[1], &new_item[1]); \
+ } \
+}
+
+static void check_mtu(knotd_conf_check_args_t *args, conf_val_t *xdp_listen)
+{
+#ifdef ENABLE_XDP
+ conf_val_t val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_UDP_MAX_PAYLOAD_IPV4);
+ if (val.code != KNOT_EOK) {
+ val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_UDP_MAX_PAYLOAD);
+ }
+ int64_t ipv4_max = conf_int(&val) + sizeof(struct udphdr) + 4 + // Eth. CRC
+ sizeof(struct iphdr) + sizeof(struct ethhdr);
+
+ val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_UDP_MAX_PAYLOAD_IPV6);
+ if (val.code != KNOT_EOK) {
+ val = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_UDP_MAX_PAYLOAD);
+ }
+ int64_t ipv6_max = conf_int(&val) + sizeof(struct udphdr) + 4 + // Eth. CRC
+ sizeof(struct ipv6hdr) + sizeof(struct ethhdr);
+
+ if (ipv6_max > KNOT_XDP_MAX_MTU || ipv4_max > KNOT_XDP_MAX_MTU) {
+ CONF_LOG(LOG_WARNING, "maximum UDP payload not compatible with XDP MTU (%u)",
+ KNOT_XDP_MAX_MTU);
+ }
+
+ while (xdp_listen->code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(xdp_listen, NULL);
+ conf_xdp_iface_t iface;
+ int ret = conf_xdp_iface(&addr, &iface);
+ if (ret != KNOT_EOK) {
+ CONF_LOG(LOG_WARNING, "failed to check XDP interface MTU");
+ return;
+ }
+ int mtu = knot_eth_mtu(iface.name);
+ if (mtu < 0) {
+ CONF_LOG(LOG_WARNING, "failed to read MTU of interface %s",
+ iface.name);
+ continue;
+ }
+ mtu += sizeof(struct ethhdr) + 4;
+ if (ipv6_max > mtu || ipv4_max > mtu) {
+ CONF_LOG(LOG_WARNING, "maximum UDP payload not compatible "
+ "with MTU of interface %s", iface.name);
+ }
+ conf_val_next(xdp_listen);
+ }
+#endif
+}
+
+int check_server(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t key_file = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_KEY_FILE);
+ conf_val_t crt_file = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_CERT_FILE);
+ if (key_file.code != crt_file.code) {
+ args->err_str = "both server certificate and key must be set";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_xdp(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t xdp_listen = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_XDP, C_LISTEN);
+ conf_val_t srv_listen = conf_get_txn(args->extra->conf, args->extra->txn,
+ C_SRV, C_LISTEN);
+ conf_val_t udp = conf_get_txn(args->extra->conf, args->extra->txn, C_XDP,
+ C_UDP);
+ conf_val_t tcp = conf_get_txn(args->extra->conf, args->extra->txn, C_XDP,
+ C_TCP);
+ conf_val_t quic = conf_get_txn(args->extra->conf, args->extra->txn, C_XDP,
+ C_QUIC);
+ if (xdp_listen.code == KNOT_EOK) {
+ if (!conf_bool(&udp) && !conf_bool(&tcp) && !conf_bool(&quic)) {
+ args->err_str = "XDP processing requires UDP, TCP, or QUIC enabled";
+ return KNOT_EINVAL;
+ }
+
+ if (srv_listen.code != KNOT_EOK && tcp.code != KNOT_EOK) {
+ CONF_LOG(LOG_WARNING, "TCP processing not available");
+ }
+ check_mtu(args, &xdp_listen);
+ }
+
+ if (conf_bool(&quic)) {
+#ifdef ENABLE_QUIC
+ conf_val_t port = conf_get_txn(args->extra->conf, args->extra->txn, C_XDP,
+ C_QUIC_PORT);
+ uint16_t quic_port = conf_int(&port);
+
+ while (xdp_listen.code == KNOT_EOK) {
+ conf_xdp_iface_t iface;
+ struct sockaddr_storage udp_addr = conf_addr(&xdp_listen, NULL);
+ if (conf_xdp_iface(&udp_addr, &iface) == KNOT_EOK && iface.port == quic_port) {
+ args->err_str = "QUIC has to listen on different port than UDP";
+ return KNOT_EINVAL;
+ }
+ conf_val_next(&xdp_listen);
+ }
+#else
+ args->err_str = "QUIC processing not available";
+ return KNOT_EINVAL;
+#endif // ENABLE_QUIC
+ }
+
+ return KNOT_EOK;
+}
+
+int check_keystore(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t backend = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEYSTORE,
+ C_BACKEND, args->id, args->id_len);
+ conf_val_t config = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEYSTORE,
+ C_CONFIG, args->id, args->id_len);
+ conf_val_t key_label = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEYSTORE,
+ C_KEY_LABEL, args->id, args->id_len);
+ if (conf_opt(&backend) == KEYSTORE_BACKEND_PKCS11 && conf_str(&config) == NULL) {
+ args->err_str = "no PKCS #11 configuration defined";
+ return KNOT_EINVAL;
+ }
+ if (conf_opt(&backend) != KEYSTORE_BACKEND_PKCS11 && conf_bool(&key_label)) {
+ args->err_str = "key labels not supported with the specified keystore";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_policy(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t sts = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_SINGLE_TYPE_SIGNING, args->id, args->id_len);
+ conf_val_t alg = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_ALG, args->id, args->id_len);
+ conf_val_t ksk = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_KSK_SIZE, args->id, args->id_len);
+ conf_val_t zsk = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_ZSK_SIZE, args->id, args->id_len);
+ conf_val_t lifetime = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_RRSIG_LIFETIME, args->id, args->id_len);
+ conf_val_t refresh = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_RRSIG_REFRESH, args->id, args->id_len);
+ conf_val_t prerefresh = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_RRSIG_PREREFRESH, args->id, args->id_len);
+ conf_val_t prop_del = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_PROPAG_DELAY, args->id, args->id_len);
+ conf_val_t zsk_life = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_ZSK_LIFETIME, args->id, args->id_len);
+ conf_val_t ksk_life = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_KSK_LIFETIME, args->id, args->id_len);
+ conf_val_t dnskey_ttl = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_DNSKEY_TTL, args->id, args->id_len);
+ conf_val_t zone_max_ttl = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_ZONE_MAX_TTL, args->id, args->id_len);
+ conf_val_t nsec3 = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_NSEC3, args->id, args->id_len);
+ conf_val_t nsec3_iters = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_NSEC3_ITER, args->id, args->id_len);
+
+ unsigned algorithm = conf_opt(&alg);
+ if (algorithm < DNSSEC_KEY_ALGORITHM_RSA_SHA256) {
+ CONF_LOG(LOG_NOTICE, "algorithm %u is deprecated and shouldn't be used for DNSSEC signing",
+ algorithm);
+ }
+
+ int64_t ksk_size = conf_int(&ksk);
+ if (ksk_size != YP_NIL && !dnssec_algorithm_key_size_check(algorithm, ksk_size)) {
+ args->err_str = "KSK key size not compatible with the algorithm";
+ return KNOT_EINVAL;
+ }
+
+ int64_t zsk_size = conf_int(&zsk);
+ if (zsk_size != YP_NIL && !dnssec_algorithm_key_size_check(algorithm, zsk_size)) {
+ args->err_str = "ZSK key size not compatible with the algorithm";
+ return KNOT_EINVAL;
+ }
+
+ int64_t lifetime_val = conf_int(&lifetime);
+ int64_t refresh_val = conf_int(&refresh);
+ int64_t preref_val = conf_int(&prerefresh);
+ if (lifetime_val <= refresh_val + preref_val) {
+ args->err_str = "RRSIG refresh + pre-refresh has to be lower than RRSIG lifetime";
+ return KNOT_EINVAL;
+ }
+
+ bool sts_val = conf_bool(&sts);
+ int64_t prop_del_val = conf_int(&prop_del);
+ int64_t zsk_life_val = conf_int(&zsk_life);
+ int64_t ksk_life_val = conf_int(&ksk_life);
+ int64_t dnskey_ttl_val = conf_int(&dnskey_ttl);
+ if (dnskey_ttl_val == YP_NIL) {
+ dnskey_ttl_val = 0;
+ }
+ int64_t zone_max_ttl_val = conf_int(&zone_max_ttl);
+ if (zone_max_ttl_val == YP_NIL) {
+ zone_max_ttl_val = dnskey_ttl_val; // Better than 0.
+ }
+
+ if (sts_val) {
+ if (ksk_life_val != 0 && ksk_life_val < 2 * prop_del_val + dnskey_ttl_val + zone_max_ttl_val) {
+ args->err_str = "CSK lifetime too low according to propagation delay, DNSKEY TTL, "
+ "and maximum zone TTL";
+ return KNOT_EINVAL;
+ }
+ } else {
+ if (ksk_life_val != 0 && ksk_life_val < 2 * prop_del_val + 2 * dnskey_ttl_val) {
+ args->err_str = "KSK lifetime too low according to propagation delay and DNSKEY TTL";
+ return KNOT_EINVAL;
+ }
+ if (zsk_life_val != 0 && zsk_life_val < 2 * prop_del_val + dnskey_ttl_val + zone_max_ttl_val) {
+ args->err_str = "ZSK lifetime too low according to propagation delay, DNSKEY TTL, "
+ "and maximum zone TTL";
+ return KNOT_EINVAL;
+ }
+ }
+
+ conf_val_t cds_cdnskey = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_CDS_CDNSKEY, args->id, args->id_len);
+ conf_val_t ds_push = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_DS_PUSH, args->id, args->id_len);
+
+ if (conf_val_count(&ds_push) > 0 && conf_opt(&cds_cdnskey) == CDS_CDNSKEY_NONE) {
+ args->err_str = "DS push requires enabled CDS/CDNSKEY publication";
+ return KNOT_EINVAL;
+ }
+
+#ifndef HAVE_GNUTLS_REPRODUCIBLE
+ conf_val_t repro_sign = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_REPRO_SIGNING, args->id, args->id_len);
+ if (conf_bool(&repro_sign)) {
+ CONF_LOG(LOG_WARNING, "reproducible signing not available, signing normally");
+ }
+#endif
+
+ if (conf_bool(&nsec3)) {
+ uint16_t iters = conf_int(&nsec3_iters);
+ if (nsec3_iters.code != KNOT_EOK && iters != 0) {
+ CONF_LOG(LOG_WARNING, "policy[%s].nsec3-iterations defaults to %u, "
+ "since version 3.2 the default becomes 0", args->id, iters);
+ }
+ if (iters > 20) {
+ CONF_LOG(LOG_NOTICE, "policy[%s].nsec3-iterations=%u is too high, "
+ "the recommended value is 0", args->id, iters);
+ }
+ }
+
+ conf_val_t dnskey_mgmt = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_DNSKEY_MGMT, args->id, args->id_len);
+ conf_val_t offline_ksk = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_OFFLINE_KSK, args->id, args->id_len);
+ conf_val_t delete_dely = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY,
+ C_DELETE_DELAY, args->id, args->id_len);
+ if (conf_opt(&dnskey_mgmt) != DNSKEY_MGMT_FULL) {
+ if (conf_bool(&offline_ksk)) {
+ args->err_str = "incremental DNSKEY management can't be used with offline-ksk";
+ return KNOT_EINVAL;
+ }
+ if (conf_int(&delete_dely) <= 0) {
+ args->err_str = "incremental DNSKEY management requires configured delete-delay";
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int check_key(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t alg = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEY,
+ C_ALG, args->id, args->id_len);
+ if (conf_val_count(&alg) == 0) {
+ args->err_str = "no key algorithm defined";
+ return KNOT_EINVAL;
+ }
+
+ conf_val_t secret = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEY,
+ C_SECRET, args->id, args->id_len);
+ if (conf_val_count(&secret) == 0) {
+ args->err_str = "no key secret defined";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_acl(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t addr = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_ACL,
+ C_ADDR, args->id, args->id_len);
+ conf_val_t key = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_ACL,
+ C_KEY, args->id, args->id_len);
+ conf_val_t remote = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_ACL,
+ C_RMT, args->id, args->id_len);
+ if (remote.code != KNOT_ENOENT &&
+ (addr.code != KNOT_ENOENT || key.code != KNOT_ENOENT)) {
+ args->err_str = "specified ACL/remote together with address or key";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_remote(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t addr = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_RMT,
+ C_ADDR, args->id, args->id_len);
+ if (conf_val_count(&addr) == 0) {
+ args->err_str = "no remote address defined";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_remotes(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t remote = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_RMTS,
+ C_RMT, args->id, args->id_len);
+ if (remote.code != KNOT_EOK) {
+ args->err_str = "no remote defined";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+#define CHECK_DFLT(item, name) { \
+ conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, \
+ C_TPL, item, args->id, args->id_len); \
+ if (val.code == KNOT_EOK) { \
+ args->err_str = name " in non-default template"; \
+ return KNOT_EINVAL; \
+ } \
+}
+
+int check_catalog_group(
+ knotd_conf_check_args_t *args)
+{
+ assert(args->data_len > 0);
+ if (args->data_len - 1 > CATALOG_GROUP_MAXLEN) {
+ args->err_str = "group name longer than 255 characters";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int check_template(
+ knotd_conf_check_args_t *args)
+{
+ if (!is_default_id(args->id, args->id_len)) {
+ CHECK_DFLT(C_GLOBAL_MODULE, "global module");
+ }
+
+ return KNOT_EOK;
+}
+
+#define CHECK_ZONE_INTERVALS(low_item, high_item) { \
+ conf_val_t high = conf_zone_get_txn(args->extra->conf, args->extra->txn, \
+ high_item, yp_dname(args->id)); \
+ if (high.code == KNOT_EOK) { \
+ conf_val_t low = conf_zone_get_txn(args->extra->conf, args->extra->txn, \
+ low_item, yp_dname(args->id)); \
+ if (low.code == KNOT_EOK && conf_int(&low) > conf_int(&high)) { \
+ if (snprintf(check_str, sizeof(check_str), "'%s' is higher than '%s'", \
+ &low_item[1], &high_item[1]) < 0) { \
+ check_str[0] = '\0'; \
+ } \
+ args->err_str = check_str; \
+ return KNOT_EINVAL; \
+ } \
+ } \
+}
+
+#define CHECK_CATZ_TPL(option, option_string) \
+{ \
+ conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, \
+ C_TPL, option, catalog_tpl.data, \
+ catalog_tpl.len); \
+ if (val.code == KNOT_EOK) { \
+ args->err_str = "'" option_string "' in a catalog template"; \
+ return KNOT_EINVAL; \
+ } \
+}
+
+int check_zone(
+ knotd_conf_check_args_t *args)
+{
+ CHECK_ZONE_INTERVALS(C_REFRESH_MIN_INTERVAL, C_REFRESH_MAX_INTERVAL);
+ CHECK_ZONE_INTERVALS(C_RETRY_MIN_INTERVAL, C_RETRY_MAX_INTERVAL);
+ CHECK_ZONE_INTERVALS(C_EXPIRE_MIN_INTERVAL, C_EXPIRE_MAX_INTERVAL);
+
+ conf_val_t zf_load = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_ZONEFILE_LOAD, yp_dname(args->id));
+ if (conf_opt(&zf_load) == ZONEFILE_LOAD_DIFSE) {
+ conf_val_t journal = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_JOURNAL_CONTENT, yp_dname(args->id));
+ if (conf_opt(&journal) != JOURNAL_CONTENT_ALL) {
+ args->err_str = "'zonefile-load: difference-no-serial' requires 'journal-content: all'";
+ return KNOT_EINVAL;
+ }
+ }
+
+ conf_val_t validation = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_DNSSEC_VALIDATION, yp_dname(args->id));
+ if (conf_bool(&validation)) {
+ conf_val_t signing = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_DNSSEC_SIGNING, yp_dname(args->id));
+ if (conf_bool(&signing)) {
+ args->err_str = "'dnssec-validation' is not compatible with 'dnssec-signing'";
+ return KNOT_EINVAL;
+ }
+ }
+
+ conf_val_t catalog_role = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_CATALOG_ROLE, yp_dname(args->id));
+ conf_val_t catalog_tpl = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_CATALOG_TPL, yp_dname(args->id));
+ conf_val_t catalog_zone = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_CATALOG_ZONE, yp_dname(args->id));
+ conf_val_t catalog_serial = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_SERIAL_POLICY, yp_dname(args->id));
+
+ unsigned role = conf_opt(&catalog_role);
+ if ((bool)(role == CATALOG_ROLE_INTERPRET) != (bool)(catalog_tpl.code == KNOT_EOK)) {
+ args->err_str = "'catalog-role' must correspond to configured 'catalog-template'";
+ return KNOT_EINVAL;
+ }
+ if ((bool)(role == CATALOG_ROLE_MEMBER) != (bool)(catalog_zone.code == KNOT_EOK)) {
+ args->err_str = "'catalog-role' must correspond to configured 'catalog-zone'";
+ return KNOT_EINVAL;
+ }
+ if (role == CATALOG_ROLE_GENERATE &&
+ conf_opt(&catalog_serial) != SERIAL_POLICY_UNIXTIME && // Default doesn't harm.
+ catalog_serial.code == KNOT_EOK) {
+ args->err_str = "'serial-policy' must be 'unixtime' for generated catalog zones";
+ return KNOT_EINVAL;
+ }
+ if (role == CATALOG_ROLE_INTERPRET) {
+ conf_val(&catalog_tpl);
+ while (catalog_tpl.code == KNOT_EOK) {
+ CHECK_CATZ_TPL(C_CATALOG_TPL, "catalog-template");
+ CHECK_CATZ_TPL(C_CATALOG_ROLE, "catalog-role");
+ CHECK_CATZ_TPL(C_CATALOG_ZONE, "catalog-zone");
+ CHECK_CATZ_TPL(C_CATALOG_GROUP, "catalog-group");
+ conf_val_next(&catalog_tpl);
+ }
+ }
+
+ conf_val_t ds_push = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_DS_PUSH, yp_dname(args->id));
+ if (ds_push.code == KNOT_EOK) {
+ conf_val_t policy_id = conf_zone_get_txn(args->extra->conf, args->extra->txn,
+ C_DNSSEC_POLICY, yp_dname(args->id));
+ if (policy_id.code == KNOT_EOK) {
+ conf_val_t cds_cdnskey = conf_id_get_txn(args->extra->conf, args->extra->txn,
+ C_POLICY, C_CDS_CDNSKEY,
+ &policy_id);
+ if (conf_val_count(&ds_push) > 0 && conf_opt(&cds_cdnskey) == CDS_CDNSKEY_NONE) {
+ args->err_str = "DS push requires enabled CDS/CDNSKEY publication";
+ return KNOT_EINVAL;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int glob_error(
+ const char *epath,
+ int eerrno)
+{
+ CONF_LOG(LOG_WARNING, "failed to access '%s' (%s)", epath,
+ knot_strerror(knot_map_errno_code(eerrno)));
+
+ return 0;
+}
+
+int include_file(
+ knotd_conf_check_args_t *args)
+{
+ if (args->data_len == 0) {
+ return KNOT_YP_ENODATA;
+ }
+
+ // This function should not be called in more threads.
+ static int depth = 0;
+ glob_t glob_buf = { 0 };
+ char *path = NULL;
+ int ret;
+
+ // Check for include loop.
+ if (depth++ > MAX_INCLUDE_DEPTH) {
+ CONF_LOG(LOG_ERR, "include loop detected");
+ ret = KNOT_EPARSEFAIL;
+ goto include_error;
+ }
+
+ // Prepare absolute include path.
+ if (args->data[0] == '/') {
+ path = sprintf_alloc("%.*s", (int)args->data_len, args->data);
+ } else {
+ const char *file_name = args->extra->file_name != NULL ?
+ args->extra->file_name : "./";
+ char *full_current_name = realpath(file_name, NULL);
+ if (full_current_name == NULL) {
+ ret = KNOT_ENOMEM;
+ goto include_error;
+ }
+
+ path = sprintf_alloc("%s/%.*s", dirname(full_current_name),
+ (int)args->data_len, args->data);
+ free(full_current_name);
+ }
+ if (path == NULL) {
+ ret = KNOT_ESPACE;
+ goto include_error;
+ }
+
+ // Evaluate include pattern (empty wildcard match is also valid).
+ ret = glob(path, 0, glob_error, &glob_buf);
+ if (ret != 0 && (ret != GLOB_NOMATCH || strchr(path, '*') == NULL)) {
+ ret = KNOT_EFILE;
+ goto include_error;
+ }
+
+ // Process glob result.
+ for (size_t i = 0; i < glob_buf.gl_pathc; i++) {
+ // Get file status.
+ struct stat file_stat;
+ if (stat(glob_buf.gl_pathv[i], &file_stat) != 0) {
+ CONF_LOG(LOG_WARNING, "failed to get file status for '%s'",
+ glob_buf.gl_pathv[i]);
+ continue;
+ }
+
+ // Ignore directory or non-regular file.
+ if (S_ISDIR(file_stat.st_mode)) {
+ continue;
+ } else if (!S_ISREG(file_stat.st_mode)) {
+ CONF_LOG(LOG_WARNING, "invalid include file '%s'",
+ glob_buf.gl_pathv[i]);
+ continue;
+ }
+
+ // Include regular file.
+ ret = conf_parse(args->extra->conf, args->extra->txn,
+ glob_buf.gl_pathv[i], true);
+ if (ret != KNOT_EOK) {
+ goto include_error;
+ }
+ }
+
+ ret = KNOT_EOK;
+include_error:
+ globfree(&glob_buf);
+ free(path);
+ depth--;
+
+ return ret;
+}
+
+int load_module(
+ knotd_conf_check_args_t *args)
+{
+ conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn,
+ C_MODULE, C_FILE, args->id, args->id_len);
+ const char *file_name = conf_str(&val);
+
+ char *mod_name = strndup((const char *)args->id, args->id_len);
+ if (mod_name == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = conf_mod_load_extra(args->extra->conf, mod_name, file_name,
+ args->extra->check ? MOD_TEMPORARY : MOD_EXPLICIT);
+ free(mod_name);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Update currently iterating item.
+ const yp_item_t *section = yp_schema_find(C_MODULE, NULL, args->extra->conf->schema);
+ assert(section);
+ args->item = section->var.g.id;
+
+ return ret;
+}
diff --git a/src/knot/conf/tools.h b/src/knot/conf/tools.h
new file mode 100644
index 0000000..a8875bd
--- /dev/null
+++ b/src/knot/conf/tools.h
@@ -0,0 +1,147 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "knot/conf/conf.h"
+#include "libknot/yparser/ypschema.h"
+
+typedef struct knotd_conf_check_extra {
+ conf_t *conf;
+ knot_db_txn_t *txn;
+ const char *file_name;
+ size_t line;
+ bool check; /*!< Indication of the confio check mode. */
+} knotd_conf_check_extra_t;
+
+int legacy_item(
+ knotd_conf_check_args_t *args
+);
+
+int conf_exec_callbacks(
+ knotd_conf_check_args_t *args
+);
+
+int mod_id_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int mod_id_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int rrtype_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int rrtype_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int rdname_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int rdname_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int check_ref(
+ knotd_conf_check_args_t *args
+);
+
+int check_ref_dflt(
+ knotd_conf_check_args_t *args
+);
+
+int check_listen(
+ knotd_conf_check_args_t *args
+);
+
+int check_xdp_listen(
+ knotd_conf_check_args_t *args
+);
+
+int check_database(
+ knotd_conf_check_args_t *args
+);
+
+int check_modref(
+ knotd_conf_check_args_t *args
+);
+
+int check_module_id(
+ knotd_conf_check_args_t *args
+);
+
+int check_file(
+ knotd_conf_check_args_t *args
+);
+
+int check_server(
+ knotd_conf_check_args_t *args
+);
+
+int check_xdp(
+ knotd_conf_check_args_t *args
+);
+
+int check_keystore(
+ knotd_conf_check_args_t *args
+);
+
+int check_policy(
+ knotd_conf_check_args_t *args
+);
+
+int check_key(
+ knotd_conf_check_args_t *args
+);
+
+int check_acl(
+ knotd_conf_check_args_t *args
+);
+
+int check_remote(
+ knotd_conf_check_args_t *args
+);
+
+int check_remotes(
+ knotd_conf_check_args_t *args
+);
+
+int check_catalog_group(
+ knotd_conf_check_args_t *args
+);
+
+int check_template(
+ knotd_conf_check_args_t *args
+);
+
+int check_zone(
+ knotd_conf_check_args_t *args
+);
+
+int include_file(
+ knotd_conf_check_args_t *args
+);
+
+int load_module(
+ knotd_conf_check_args_t *args
+);
diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c
new file mode 100644
index 0000000..7d4c592
--- /dev/null
+++ b/src/knot/ctl/commands.c
@@ -0,0 +1,2331 @@
+/* Copyright (C) 2022 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 <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <urcu.h>
+
+#include "knot/common/log.h"
+#include "knot/common/stats.h"
+#include "knot/conf/confio.h"
+#include "knot/ctl/commands.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/events/events.h"
+#include "knot/events/handlers.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/nameserver/query_module.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/backup.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/timers.h"
+#include "knot/zone/zonedb-load.h"
+#include "knot/zone/zonefile.h"
+#include "libknot/libknot.h"
+#include "libknot/yparser/yptrafo.h"
+#include "contrib/files.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/ucw/lists.h"
+#include "libzscanner/scanner.h"
+
+#define MATCH_OR_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] == NULL || \
+ strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL)
+
+#define MATCH_AND_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] != NULL && \
+ strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL)
+
+typedef struct {
+ ctl_args_t *args;
+ int type_filter; // -1: no specific type, [0, 2^16]: specific type.
+ knot_dump_style_t style;
+ knot_ctl_data_t data;
+ knot_dname_txt_storage_t zone;
+ knot_dname_txt_storage_t owner;
+ char ttl[16];
+ char type[32];
+ char rdata[2 * 65536];
+} send_ctx_t;
+
+static struct {
+ send_ctx_t send_ctx;
+ zs_scanner_t scanner;
+ char txt_rr[sizeof(((send_ctx_t *)0)->owner) +
+ sizeof(((send_ctx_t *)0)->ttl) +
+ sizeof(((send_ctx_t *)0)->type) +
+ sizeof(((send_ctx_t *)0)->rdata)];
+} ctl_globals;
+
+/*!
+ * Evaluates a filter pair and checks for conflicting filters.
+ *
+ * \param[in] args Command arguments.
+ * \param[out] param The filter to be set.
+ * \param[in] dflt Default filter value.
+ * \param[in] filter Name of the filter.
+ * \param[in] neg_filter Name of the negative filter.
+ *
+ * \return false if there is a filter conflict, true otherwise.
+ */
+
+static bool eval_opposite_filters(ctl_args_t *args, bool *param, bool dflt,
+ int filter, int neg_filter)
+{
+ bool set = MATCH_AND_FILTER(args, filter);
+ bool unset = MATCH_AND_FILTER(args, neg_filter);
+
+ *param = dflt ? (set || !unset) : (set && !unset);
+ return !(set && unset);
+}
+
+static int schedule_trigger(zone_t *zone, ctl_args_t *args, zone_event_type_t event,
+ bool user)
+{
+ int ret = KNOT_EOK;
+
+ if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_BLOCKING)) {
+ ret = zone_events_schedule_blocking(zone, event, user);
+ } else if (user) {
+ zone_events_schedule_user(zone, event);
+ } else {
+ zone_events_schedule_now(zone, event);
+ }
+
+ return ret;
+}
+
+static void ctl_log_conf_data(knot_ctl_data_t *data)
+{
+ if (data == NULL) {
+ return;
+ }
+
+ const char *section = (*data)[KNOT_CTL_IDX_SECTION];
+ const char *item = (*data)[KNOT_CTL_IDX_ITEM];
+ const char *id = (*data)[KNOT_CTL_IDX_ID];
+
+ if (section != NULL) {
+ log_ctl_debug("control, config item '%s%s%s%s%s%s'", section,
+ (id != NULL ? "[" : ""),
+ (id != NULL ? id : ""),
+ (id != NULL ? "]" : ""),
+ (item != NULL ? "." : ""),
+ (item != NULL ? item : ""));
+ }
+}
+
+static void send_error(ctl_args_t *args, const char *msg)
+{
+ knot_ctl_data_t data;
+ memcpy(&data, args->data, sizeof(data));
+
+ data[KNOT_CTL_IDX_ERROR] = msg;
+
+ int ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data);
+ if (ret != KNOT_EOK) {
+ log_ctl_debug("control, failed to send error (%s)", knot_strerror(ret));
+ }
+}
+
+static int get_zone(ctl_args_t *args, zone_t **zone)
+{
+ const char *name = args->data[KNOT_CTL_IDX_ZONE];
+ assert(name != NULL);
+
+ knot_dname_storage_t buff;
+ knot_dname_t *dname = knot_dname_from_str(buff, name, sizeof(buff));
+ if (dname == NULL) {
+ return KNOT_EINVAL;
+ }
+ knot_dname_to_lower(dname);
+
+ *zone = knot_zonedb_find(args->server->zone_db, dname);
+ if (*zone == NULL) {
+ return KNOT_ENOZONE;
+ }
+
+ return KNOT_EOK;
+}
+
+static int zones_apply(ctl_args_t *args, int (*fcn)(zone_t *, ctl_args_t *))
+{
+ int ret;
+
+ // Process all configured zones if none is specified.
+ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+ bool failed = false;
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin(args->server->zone_db);
+ while (!knot_zonedb_iter_finished(it)) {
+ args->suppress = false;
+ ret = fcn((zone_t *)knot_zonedb_iter_val(it), args);
+ if (ret != KNOT_EOK && !args->suppress) {
+ failed = true;
+ }
+ knot_zonedb_iter_next(it);
+ }
+ knot_zonedb_iter_free(it);
+
+ if (failed) {
+ ret = KNOT_CTL_EZONE;
+ log_ctl_error("control, error (%s)", knot_strerror(ret));
+ send_error(args, knot_strerror(ret));
+ }
+
+ return KNOT_EOK;
+ }
+
+ while (true) {
+ zone_t *zone;
+ ret = get_zone(args, &zone);
+ if (ret == KNOT_EOK) {
+ ret = fcn(zone, args);
+ }
+ if (ret != KNOT_EOK) {
+ log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
+ "control, error (%s)", knot_strerror(ret));
+ send_error(args, knot_strerror(ret));
+ }
+
+ // Get next zone name.
+ ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+ if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+ break;
+ }
+ strtolower((char *)args->data[KNOT_CTL_IDX_ZONE]);
+
+ // Log the other zones the same way as the first one from process.c.
+ log_ctl_zone_str_info(args->data[KNOT_CTL_IDX_ZONE],
+ "control, received command '%s'",
+ args->data[KNOT_CTL_IDX_CMD]);
+ }
+
+ return ret;
+}
+
+static int zone_status(zone_t *zone, ctl_args_t *args)
+{
+ knot_dname_txt_storage_t name;
+ if (knot_dname_to_str(name, zone->name, sizeof(name)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ char flags[16] = "";
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_ZONE] = name,
+ [KNOT_CTL_IDX_FLAGS] = flags
+ };
+
+ const bool slave = zone_is_slave(conf(), zone);
+ if (slave) {
+ strlcat(flags, CTL_FLAG_STATUS_SLAVE, sizeof(flags));
+ }
+ const bool empty = (zone->contents == NULL);
+ if (empty) {
+ strlcat(flags, CTL_FLAG_STATUS_EMPTY, sizeof(flags));
+ }
+ const bool member = (zone->flags & ZONE_IS_CAT_MEMBER);
+ if (member) {
+ strlcat(flags, CTL_FLAG_STATUS_MEMBER, sizeof(flags));
+ }
+
+ int ret;
+ char buff[128];
+ knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
+
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_ROLE)) {
+ data[KNOT_CTL_IDX_TYPE] = "role";
+
+ if (slave) {
+ data[KNOT_CTL_IDX_DATA] = "slave";
+ } else {
+ data[KNOT_CTL_IDX_DATA] = "master";
+ }
+
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+ }
+
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_SERIAL)) {
+ data[KNOT_CTL_IDX_TYPE] = "serial";
+
+ if (empty) {
+ ret = snprintf(buff, sizeof(buff), STATUS_EMPTY);
+ } else {
+ knot_rdataset_t *soa = node_rdataset(zone->contents->apex,
+ KNOT_RRTYPE_SOA);
+ ret = snprintf(buff, sizeof(buff), "%u", knot_soa_serial(soa->rdata));
+ }
+ if (ret < 0 || ret >= sizeof(buff)) {
+ return KNOT_ESPACE;
+ }
+
+ data[KNOT_CTL_IDX_DATA] = buff;
+
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+ }
+
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_TRANSACTION)) {
+ data[KNOT_CTL_IDX_TYPE] = "transaction";
+ data[KNOT_CTL_IDX_DATA] = (zone->control_update != NULL) ? "open" : STATUS_EMPTY;
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+ }
+
+ const bool ufrozen = zone->events.ufrozen;
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_FREEZE)) {
+ data[KNOT_CTL_IDX_TYPE] = "freeze";
+ if (ufrozen) {
+ if (zone_events_get_time(zone, ZONE_EVENT_UTHAW) < time(NULL)) {
+ data[KNOT_CTL_IDX_DATA] = "yes";
+ } else {
+ data[KNOT_CTL_IDX_DATA] = "thawing";
+ }
+ } else {
+ if (zone_events_get_time(zone, ZONE_EVENT_UFREEZE) < time(NULL)) {
+ data[KNOT_CTL_IDX_DATA] = STATUS_EMPTY;
+ } else {
+ data[KNOT_CTL_IDX_DATA] = "freezing";
+ }
+ }
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+
+ data[KNOT_CTL_IDX_TYPE] = "XFR-freeze";
+ if (zone_get_flag(zone, ZONE_XFR_FROZEN, false)) {
+ data[KNOT_CTL_IDX_DATA] = "yes";
+ } else {
+ data[KNOT_CTL_IDX_DATA] = STATUS_EMPTY;
+ }
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_CATALOG)) {
+ char buf[1 + KNOT_DNAME_TXT_MAXLEN + 1 + CATALOG_GROUP_MAXLEN + 1] = "";
+ data[KNOT_CTL_IDX_TYPE] = "catalog";
+ data[KNOT_CTL_IDX_DATA] = buf;
+
+ if (member) {
+ const knot_dname_t *catz;
+ const char *group;
+ void *to_free;
+ ret = catalog_get_catz(zone_catalog(zone), zone->name,
+ &catz, &group, &to_free);
+ if (ret == KNOT_EOK) {
+ if (knot_dname_to_str(buf, catz, sizeof(buf)) == NULL) {
+ buf[0] = '\0';
+ }
+ if (group[0] != '\0') {
+ size_t idx = strlcat(buf, "#", sizeof(buf));
+ (void)strlcat(buf + idx, group, sizeof(buf) - idx);
+ }
+ free(to_free);
+ }
+ } else {
+ conf_val_t val = conf_zone_get(conf(), C_CATALOG_ROLE, zone->name);
+ switch (conf_opt(&val)) {
+ case CATALOG_ROLE_INTERPRET:
+ data[KNOT_CTL_IDX_DATA] = "interpret";
+ break;
+ case CATALOG_ROLE_GENERATE:
+ data[KNOT_CTL_IDX_DATA] = "generate";
+ break;
+ case CATALOG_ROLE_MEMBER:
+ buf[0] = '@';
+ val = conf_zone_get(conf(), C_CATALOG_ZONE, zone->name);
+ if (knot_dname_to_str(buf + 1, conf_dname(&val), sizeof(buf) - 1) == NULL) {
+ buf[1] = '\0';
+ }
+ val = conf_zone_get(conf(), C_CATALOG_GROUP, zone->name);
+ if (val.code == KNOT_EOK) {
+ size_t idx = strlcat(buf, "#", sizeof(buf));
+ (void)strlcat(buf + idx, conf_str(&val), sizeof(buf) - idx);
+ }
+ break;
+ default:
+ data[KNOT_CTL_IDX_DATA] = STATUS_EMPTY;
+ }
+ }
+
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+ }
+
+ if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_EVENTS)) {
+ for (zone_event_type_t i = 0; i < ZONE_EVENT_COUNT; i++) {
+ // Events not worth showing or used elsewhere.
+ if (i == ZONE_EVENT_UFREEZE || i == ZONE_EVENT_UTHAW) {
+ continue;
+ }
+
+ data[KNOT_CTL_IDX_TYPE] = zone_events_get_name(i);
+ time_t ev_time = zone_events_get_time(zone, i);
+ if (zone->events.running && zone->events.type == i) {
+ ret = snprintf(buff, sizeof(buff), "running");
+ } else if (ev_time <= 0) {
+ ret = snprintf(buff, sizeof(buff), STATUS_EMPTY);
+ } else if (ev_time <= time(NULL)) {
+ bool frozen = ufrozen && ufreeze_applies(i);
+ ret = snprintf(buff, sizeof(buff), frozen ? "frozen" : "pending");
+ } else {
+ ret = knot_time_print(TIME_PRINT_HUMAN_MIXED,
+ ev_time, buff, sizeof(buff));
+ }
+ if (ret < 0 || ret >= sizeof(buff)) {
+ return KNOT_ESPACE;
+ }
+ data[KNOT_CTL_IDX_DATA] = buff;
+
+ ret = knot_ctl_send(args->ctl, type, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ type = KNOT_CTL_TYPE_EXTRA;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int zone_reload(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (zone_expired(zone)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) {
+ return zone_reload_modules(conf(), args->server, zone->name);
+ }
+
+ return schedule_trigger(zone, args, ZONE_EVENT_LOAD, true);
+}
+
+static int zone_refresh(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (!zone_is_slave(conf(), zone)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ zone->zonefile.bootstrap_cnt = 0; // restart delays
+ return schedule_trigger(zone, args, ZONE_EVENT_REFRESH, true);
+}
+
+static int zone_retransfer(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (!zone_is_slave(conf(), zone)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ zone_set_flag(zone, ZONE_FORCE_AXFR);
+ zone->zonefile.bootstrap_cnt = 0; // restart delays
+ return schedule_trigger(zone, args, ZONE_EVENT_REFRESH, true);
+}
+
+static int zone_notify(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ zone_notifailed_clear(zone);
+ return schedule_trigger(zone, args, ZONE_EVENT_NOTIFY, true);
+}
+
+static int zone_flush(zone_t *zone, ctl_args_t *args)
+{
+ if (MATCH_AND_FILTER(args, CTL_FILTER_FLUSH_OUTDIR)) {
+ rcu_read_lock();
+ int ret = zone_dump_to_dir(conf(), zone, args->data[KNOT_CTL_IDX_DATA]);
+ rcu_read_unlock();
+ if (ret != KNOT_EOK) {
+ log_zone_warning(zone->name, "failed to update zone file (%s)",
+ knot_strerror(ret));
+ }
+ return ret;
+ }
+
+ zone_set_flag(zone, ZONE_USER_FLUSH);
+ if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) {
+ zone_set_flag(zone, ZONE_FORCE_FLUSH);
+ }
+
+ return schedule_trigger(zone, args, ZONE_EVENT_FLUSH, true);
+}
+
+static int init_backup(ctl_args_t *args, bool restore_mode)
+{
+ if (!MATCH_AND_FILTER(args, CTL_FILTER_BACKUP_OUTDIR)) {
+ return KNOT_ENOPARAM;
+ }
+
+ // Make sure that the backup outdir is not the same as the server DB storage.
+ conf_val_t db_storage_val = conf_db_param(conf(), C_STORAGE);
+ const char *db_storage = conf_str(&db_storage_val);
+
+ const char *backup_dir = args->data[KNOT_CTL_IDX_DATA];
+
+ if (same_path(backup_dir, db_storage)) {
+ char *msg = sprintf_alloc("%s the database storage directory not allowed",
+ restore_mode ? "restore from" : "backup to");
+
+ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+ log_ctl_error("%s", msg);
+ } else {
+ log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE], "%s", msg);
+ }
+ free(msg);
+ return KNOT_EINVAL;
+ }
+
+ // Evaluate filters (and possibly fail) before writing to the filesystem.
+ bool filter_zonefile, filter_journal, filter_timers, filter_kaspdb, filter_catalog;
+
+ // The default filter values are set just in this paragraph.
+ if (!(eval_opposite_filters(args, &filter_zonefile, true,
+ CTL_FILTER_BACKUP_ZONEFILE, CTL_FILTER_BACKUP_NOZONEFILE) &&
+ eval_opposite_filters(args, &filter_journal, false,
+ CTL_FILTER_BACKUP_JOURNAL, CTL_FILTER_BACKUP_NOJOURNAL) &&
+ eval_opposite_filters(args, &filter_timers, true,
+ CTL_FILTER_BACKUP_TIMERS, CTL_FILTER_BACKUP_NOTIMERS) &&
+ eval_opposite_filters(args, &filter_kaspdb, true,
+ CTL_FILTER_BACKUP_KASPDB, CTL_FILTER_BACKUP_NOKASPDB) &&
+ eval_opposite_filters(args, &filter_catalog, true,
+ CTL_FILTER_BACKUP_CATALOG, CTL_FILTER_BACKUP_NOCATALOG))) {
+ return KNOT_EXPARAM;
+ }
+
+ bool forced = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE);
+
+ zone_backup_ctx_t *ctx;
+
+ // The present timer db size is not up-to-date, use the maximum one.
+ conf_val_t timer_db_size = conf_db_param(conf(), C_TIMER_DB_MAX_SIZE);
+
+ int ret = zone_backup_init(restore_mode, forced,
+ args->data[KNOT_CTL_IDX_DATA],
+ knot_lmdb_copy_size(&args->server->kaspdb),
+ conf_int(&timer_db_size),
+ knot_lmdb_copy_size(&args->server->journaldb),
+ knot_lmdb_copy_size(&args->server->catalog.db),
+ &ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ assert(ctx != NULL);
+ ctx->backup_zonefile = filter_zonefile;
+ ctx->backup_journal = filter_journal;
+ ctx->backup_timers = filter_timers;
+ ctx->backup_kaspdb = filter_kaspdb;
+ ctx->backup_catalog = filter_catalog;
+
+ zone_backups_add(&args->server->backup_ctxs, ctx);
+
+ return ret;
+}
+
+static zone_backup_ctx_t *latest_backup_ctx(ctl_args_t *args)
+{
+ // no need to mutex in this case
+ return (zone_backup_ctx_t *)TAIL(args->server->backup_ctxs.ctxs);
+}
+
+static int deinit_backup(ctl_args_t *args)
+{
+ return zone_backup_deinit(latest_backup_ctx(args));
+}
+
+static int zone_backup_cmd(zone_t *zone, ctl_args_t *args)
+{
+ zone_backup_ctx_t *ctx = latest_backup_ctx(args);
+ if (!ctx->restore_mode && ctx->failed) {
+ // No need to proceed with already faulty backup.
+ return KNOT_EOK;
+ }
+
+ if (zone->backup_ctx != NULL) {
+ log_zone_warning(zone->name, "backup or restore already in progress, skipping zone");
+ ctx->failed = true;
+ return KNOT_EPROGRESS;
+ }
+
+ zone->backup_ctx = ctx;
+ pthread_mutex_lock(&ctx->readers_mutex);
+ ctx->readers++;
+ pthread_mutex_unlock(&ctx->readers_mutex);
+ ctx->zone_count++;
+
+ int ret = schedule_trigger(zone, args, ZONE_EVENT_BACKUP, true);
+
+ if (ret == KNOT_EOK && !ctx->backup_global && (ctx->restore_mode || !ctx->failed)) {
+ ret = global_backup(ctx, zone_catalog(zone), zone->name);
+ }
+
+ return ret;
+}
+
+static int zones_apply_backup(ctl_args_t *args, bool restore_mode)
+{
+ int ret_deinit;
+ int ret = init_backup(args, restore_mode);
+
+ if (ret != KNOT_EOK) {
+ char *msg = sprintf_alloc("%s init failed (%s)",
+ restore_mode ? "restore" : "backup",
+ knot_strerror(ret));
+
+ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+ log_ctl_error("%s", msg);
+ } else {
+ log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
+ "%s", msg);
+ }
+ free (msg);
+
+ /* Warning: zone name in the control command params discarded here. */
+ args->data[KNOT_CTL_IDX_ZONE] = NULL;
+ send_error(args, knot_strerror(ret));
+ return KNOT_CTL_EZONE;
+ }
+
+ /* Global catalog zones backup. */
+ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+ zone_backup_ctx_t *ctx = latest_backup_ctx(args);
+ ctx->backup_global = true;
+ ret = global_backup(ctx, &args->server->catalog, NULL);
+ if (ret != KNOT_EOK) {
+ log_ctl_error("control, error (%s)", knot_strerror(ret));
+ send_error(args, knot_strerror(ret));
+ ret = KNOT_EOK;
+ goto done;
+ }
+ }
+
+ ret = zones_apply(args, zone_backup_cmd);
+
+done:
+ ret_deinit = deinit_backup(args);
+ return ret != KNOT_EOK ? ret : ret_deinit;
+}
+
+static int zone_sign(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
+ if (!conf_bool(&val)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ zone_set_flag(zone, ZONE_FORCE_RESIGN);
+ return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
+}
+
+static int zone_keys_load(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
+ if (!conf_bool(&val)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
+}
+
+static int zone_key_roll(zone_t *zone, ctl_args_t *args)
+{
+ conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
+ if (!conf_bool(&val)) {
+ args->suppress = true;
+ return KNOT_ENOTSUP;
+ }
+
+ const char *key_type = args->data[KNOT_CTL_IDX_TYPE];
+ if (strncasecmp(key_type, "ksk", 3) == 0) {
+ zone_set_flag(zone, ZONE_FORCE_KSK_ROLL);
+ } else if (strncasecmp(key_type, "zsk", 3) == 0) {
+ zone_set_flag(zone, ZONE_FORCE_ZSK_ROLL);
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ return schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, true);
+}
+
+static int zone_ksk_sbm_confirm(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ kdnssec_ctx_t ctx = { 0 };
+
+ int ret = kdnssec_ctx_init(conf(), &ctx, zone->name, zone_kaspdb(zone), NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_dnssec_ksk_sbm_confirm(&ctx, 0);
+ kdnssec_ctx_deinit(&ctx);
+
+ conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
+ if (ret == KNOT_EOK && conf_bool(&val)) {
+ // NOT zone_events_schedule_user(), intentionally!
+ ret = schedule_trigger(zone, args, ZONE_EVENT_DNSSEC, false);
+ }
+
+ return ret;
+}
+
+static int zone_freeze(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ return schedule_trigger(zone, args, ZONE_EVENT_UFREEZE, false);
+}
+
+static int zone_thaw(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ return schedule_trigger(zone, args, ZONE_EVENT_UTHAW, false);
+}
+
+static int zone_xfr_freeze(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ zone_set_flag(zone, ZONE_XFR_FROZEN);
+
+ log_zone_info(zone->name, "outgoing XFR frozen");
+
+ return KNOT_EOK;
+}
+
+static int zone_xfr_thaw(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ zone_unset_flag(zone, ZONE_XFR_FROZEN);
+
+ log_zone_info(zone->name, "outgoing XFR unfrozen");
+
+ return KNOT_EOK;
+}
+
+static int zone_txn_begin(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (zone->control_update != NULL) {
+ return KNOT_TXN_EEXISTS;
+ }
+
+ zone->control_update = malloc(sizeof(zone_update_t));
+ if (zone->control_update == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ zone_update_flags_t type = (zone->contents == NULL) ? UPDATE_FULL : UPDATE_INCREMENTAL;
+ int ret = zone_update_init(zone->control_update, zone, type | UPDATE_STRICT);
+ if (ret != KNOT_EOK) {
+ free(zone->control_update);
+ zone->control_update = NULL;
+ }
+
+ return ret;
+}
+
+static int zone_txn_commit(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ int ret = zone_update_semcheck(conf(), zone->control_update);
+ if (ret != KNOT_EOK) {
+ return ret; // Recoverable error.
+ }
+
+ // NOOP if empty changeset/contents.
+ if (((zone->control_update->flags & UPDATE_INCREMENTAL) &&
+ changeset_empty(&zone->control_update->change)) ||
+ ((zone->control_update->flags & UPDATE_FULL) &&
+ zone_contents_is_empty(zone->control_update->new_cont))) {
+ zone_control_clear(zone);
+ return KNOT_EOK;
+ }
+
+ // Sign update.
+ conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name);
+ bool dnssec_enable = conf_bool(&val);
+ val = conf_zone_get(conf(), C_ZONEMD_GENERATE, zone->name);
+ unsigned digest_alg = conf_opt(&val);
+ if (dnssec_enable) {
+ if (zone->control_update->flags & UPDATE_FULL) {
+ zone_sign_reschedule_t resch = { 0 };
+ zone_sign_roll_flags_t rflags = KEY_ROLL_ALLOW_ALL;
+ ret = knot_dnssec_zone_sign(zone->control_update, conf(), 0, rflags, 0, &resch);
+ event_dnssec_reschedule(conf(), zone, &resch, false);
+ } else {
+ ret = knot_dnssec_sign_update(zone->control_update, conf());
+ }
+ } else if (digest_alg != ZONE_DIGEST_NONE) {
+ if (zone_update_to(zone->control_update) == NULL) {
+ ret = zone_update_increment_soa(zone->control_update, conf());
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add_digest(zone->control_update, digest_alg, false);
+ }
+ }
+ if (ret != KNOT_EOK) {
+ zone_control_clear(zone);
+ return ret;
+ }
+
+ ret = zone_update_commit(conf(), zone->control_update);
+ if (ret != KNOT_EOK) {
+ zone_control_clear(zone);
+ return ret;
+ }
+
+ free(zone->control_update);
+ zone->control_update = NULL;
+
+ zone_schedule_notify(zone, 0);
+
+ return KNOT_EOK;
+}
+
+static int zone_txn_abort(zone_t *zone, _unused_ ctl_args_t *args)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ zone_control_clear(zone);
+
+ return KNOT_EOK;
+}
+
+static int init_send_ctx(send_ctx_t *ctx, const knot_dname_t *zone_name,
+ ctl_args_t *args)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->args = args;
+
+ // Set the dump style.
+ ctx->style.show_ttl = true;
+ ctx->style.original_ttl = true;
+ ctx->style.human_timestamp = true;
+
+ // Set the output data buffers.
+ ctx->data[KNOT_CTL_IDX_ZONE] = ctx->zone;
+ ctx->data[KNOT_CTL_IDX_OWNER] = ctx->owner;
+ ctx->data[KNOT_CTL_IDX_TTL] = ctx->ttl;
+ ctx->data[KNOT_CTL_IDX_TYPE] = ctx->type;
+ ctx->data[KNOT_CTL_IDX_DATA] = ctx->rdata;
+
+ // Set the ZONE.
+ if (knot_dname_to_str(ctx->zone, zone_name, sizeof(ctx->zone)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Set the TYPE filter.
+ if (args->data[KNOT_CTL_IDX_TYPE] != NULL) {
+ uint16_t type;
+ if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) {
+ return KNOT_EINVAL;
+ }
+ ctx->type_filter = type;
+ } else {
+ ctx->type_filter = -1;
+ }
+
+ return KNOT_EOK;
+}
+
+static int send_rrset(knot_rrset_t *rrset, send_ctx_t *ctx)
+{
+ if (rrset->type != KNOT_RRTYPE_RRSIG) {
+ int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u", rrset->ttl);
+ if (ret <= 0 || ret >= sizeof(ctx->ttl)) {
+ return KNOT_ESPACE;
+ }
+ }
+
+ if (knot_rrtype_to_string(rrset->type, ctx->type, sizeof(ctx->type)) < 0) {
+ return KNOT_ESPACE;
+ }
+
+ for (size_t i = 0; i < rrset->rrs.count; ++i) {
+ if (rrset->type == KNOT_RRTYPE_RRSIG) {
+ int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u",
+ knot_rrsig_original_ttl(knot_rdataset_at(&rrset->rrs, i)));
+ if (ret <= 0 || ret >= sizeof(ctx->ttl)) {
+ return KNOT_ESPACE;
+ }
+ }
+
+ int ret = knot_rrset_txt_dump_data(rrset, i, ctx->rdata,
+ sizeof(ctx->rdata), &ctx->style);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = knot_ctl_send(ctx->args->ctl, KNOT_CTL_TYPE_DATA, &ctx->data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int send_node(zone_node_t *node, void *ctx_void)
+{
+ send_ctx_t *ctx = ctx_void;
+ if (knot_dname_to_str(ctx->owner, node->owner, sizeof(ctx->owner)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ for (size_t i = 0; i < node->rrset_count; ++i) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+
+ // Check for requested TYPE.
+ if (ctx->type_filter != -1 && rrset.type != ctx->type_filter) {
+ continue;
+ }
+
+ int ret = send_rrset(&rrset, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int get_owner(uint8_t *out, size_t out_len, knot_dname_t *origin,
+ ctl_args_t *args)
+{
+ const char *owner = args->data[KNOT_CTL_IDX_OWNER];
+ assert(owner != NULL);
+
+ bool fqdn = false;
+ size_t prefix_len = 0;
+
+ size_t owner_len = strlen(owner);
+ if (owner_len > 0 && (owner_len != 1 || owner[0] != '@')) {
+ // Check if the owner is FQDN.
+ if (owner[owner_len - 1] == '.') {
+ fqdn = true;
+ }
+
+ if (knot_dname_from_str(out, owner, out_len) == NULL) {
+ return KNOT_EINVAL;
+ }
+ knot_dname_to_lower(out);
+
+ prefix_len = knot_dname_size(out);
+ if (prefix_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Ignore trailing dot.
+ prefix_len--;
+ }
+
+ // Append the origin.
+ if (!fqdn) {
+ size_t origin_len = knot_dname_size(origin);
+ if (origin_len == 0 || origin_len > out_len - prefix_len) {
+ return KNOT_EINVAL;
+ }
+ memcpy(out + prefix_len, origin, origin_len);
+ }
+
+ return KNOT_EOK;
+}
+
+static int zone_read(zone_t *zone, ctl_args_t *args)
+{
+ send_ctx_t *ctx = &ctl_globals.send_ctx;
+ int ret = init_send_ctx(ctx, zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (args->data[KNOT_CTL_IDX_OWNER] != NULL) {
+ knot_dname_storage_t owner;
+
+ ret = get_owner(owner, sizeof(owner), zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ const zone_node_t *node = zone_contents_node_or_nsec3(zone->contents, owner);
+ if (node == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ ret = send_node((zone_node_t *)node, ctx);
+ } else if (zone->contents != NULL) {
+ ret = zone_contents_apply(zone->contents, send_node, ctx);
+ if (ret == KNOT_EOK) {
+ ret = zone_contents_nsec3_apply(zone->contents, send_node, ctx);
+ }
+ }
+
+ return ret;
+}
+
+static int zone_flag_txn_get(zone_t *zone, ctl_args_t *args, const char *flag)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ send_ctx_t *ctx = &ctl_globals.send_ctx;
+ int ret = init_send_ctx(ctx, zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ctx->data[KNOT_CTL_IDX_FLAGS] = flag;
+
+ if (args->data[KNOT_CTL_IDX_OWNER] != NULL) {
+ knot_dname_storage_t owner;
+
+ ret = get_owner(owner, sizeof(owner), zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ const zone_node_t *node = zone_contents_node_or_nsec3(zone->control_update->new_cont, owner);
+ if (node == NULL) {
+ return KNOT_ENONODE;
+
+ }
+
+ ret = send_node((zone_node_t *)node, ctx);
+ } else {
+ zone_tree_it_t it = { 0 };
+ ret = zone_tree_it_double_begin(zone->control_update->new_cont->nodes,
+ zone->control_update->new_cont->nsec3_nodes,
+ &it);
+ while (ret == KNOT_EOK && !zone_tree_it_finished(&it)) {
+ ret = send_node(zone_tree_it_val(&it), ctx);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ }
+
+ return ret;
+}
+
+static int zone_txn_get(zone_t *zone, ctl_args_t *args)
+{
+ return zone_flag_txn_get(zone, args, NULL);
+}
+
+static int send_changeset_part(changeset_t *ch, send_ctx_t *ctx, bool from)
+{
+ ctx->data[KNOT_CTL_IDX_FLAGS] = from ? CTL_FLAG_DIFF_REM : CTL_FLAG_DIFF_ADD;
+
+ // Send SOA only if explicitly changed.
+ if (ch->soa_to != NULL) {
+ knot_rrset_t *soa = from ? ch->soa_from : ch->soa_to;
+ assert(soa);
+
+ char *owner = knot_dname_to_str(ctx->owner, soa->owner, sizeof(ctx->owner));
+ if (owner == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = send_rrset(soa, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Send other records.
+ changeset_iter_t it;
+ int ret = from ? changeset_iter_rem(&it, ch) : changeset_iter_add(&it, ch);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_rrset_t rrset = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&rrset)) {
+ char *owner = knot_dname_to_str(ctx->owner, rrset.owner, sizeof(ctx->owner));
+ if (owner == NULL) {
+ changeset_iter_clear(&it);
+ return KNOT_EINVAL;
+ }
+
+ ret = send_rrset(&rrset, ctx);
+ if (ret != KNOT_EOK) {
+ changeset_iter_clear(&it);
+ return ret;
+ }
+
+ rrset = changeset_iter_next(&it);
+ }
+ changeset_iter_clear(&it);
+
+ return KNOT_EOK;
+}
+
+static int send_changeset(changeset_t *ch, send_ctx_t *ctx)
+{
+ // First send 'from' changeset part.
+ int ret = send_changeset_part(ch, ctx, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Second send 'to' changeset part.
+ return send_changeset_part(ch, ctx, false);
+}
+
+static int zone_txn_diff(zone_t *zone, ctl_args_t *args)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ // FULL update has no changeset to print, do a 'get' instead.
+ if (zone->control_update->flags & UPDATE_FULL) {
+ return zone_flag_txn_get(zone, args, CTL_FLAG_DIFF_ADD);
+ }
+
+ send_ctx_t *ctx = &ctl_globals.send_ctx;
+ int ret = init_send_ctx(ctx, zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return send_changeset(&zone->control_update->change, ctx);
+}
+
+static int get_ttl(zone_t *zone, ctl_args_t *args, uint32_t *ttl)
+{
+ knot_dname_storage_t owner;
+
+ int ret = get_owner(owner, sizeof(owner), zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ const zone_node_t *node = zone_contents_node_or_nsec3(zone->control_update->new_cont, owner);
+ if (node == NULL) {
+ return KNOT_ENOTTL;
+ }
+
+ uint16_t type;
+ if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t rrset = node_rrset(node, type);
+ if (knot_rrset_empty(&rrset)) {
+ return KNOT_ENOTTL;
+ }
+ *ttl = rrset.ttl;
+
+ return KNOT_EOK;
+}
+
+static int create_rrset(knot_rrset_t **rrset, zone_t *zone, ctl_args_t *args,
+ bool need_ttl)
+{
+ knot_dname_txt_storage_t origin_buff;
+ char *origin = knot_dname_to_str(origin_buff, zone->name, sizeof(origin_buff));
+ if (origin == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const char *owner = args->data[KNOT_CTL_IDX_OWNER];
+ const char *type = args->data[KNOT_CTL_IDX_TYPE];
+ const char *data = args->data[KNOT_CTL_IDX_DATA];
+ const char *ttl = need_ttl ? args->data[KNOT_CTL_IDX_TTL] : NULL;
+
+ // Prepare a buffer for a reconstructed record.
+ const size_t buff_len = sizeof(ctl_globals.txt_rr);
+ char *buff = ctl_globals.txt_rr;
+
+ uint32_t default_ttl = 0;
+ if (ttl == NULL) {
+ int ret = get_ttl(zone, args, &default_ttl);
+ if (need_ttl && ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Reconstruct the record.
+ int ret = snprintf(buff, buff_len, "%s %s %s %s\n",
+ (owner != NULL ? owner : ""),
+ (ttl != NULL ? ttl : ""),
+ (type != NULL ? type : ""),
+ (data != NULL ? data : ""));
+ if (ret <= 0 || ret >= buff_len) {
+ return KNOT_ESPACE;
+ }
+ size_t rdata_len = ret;
+
+ // Parse the record.
+ zs_scanner_t *scanner = &ctl_globals.scanner;
+ if (zs_init(scanner, origin, KNOT_CLASS_IN, default_ttl) != 0 ||
+ zs_set_input_string(scanner, buff, rdata_len) != 0 ||
+ zs_parse_record(scanner) != 0 ||
+ scanner->state != ZS_STATE_DATA) {
+ ret = KNOT_EPARSEFAIL;
+ goto parser_failed;
+ }
+ knot_dname_to_lower(scanner->r_owner);
+
+ // Create output rrset.
+ *rrset = knot_rrset_new(scanner->r_owner, scanner->r_type,
+ scanner->r_class, scanner->r_ttl, NULL);
+ if (*rrset == NULL) {
+ ret = KNOT_ENOMEM;
+ goto parser_failed;
+ }
+
+ ret = knot_rrset_add_rdata(*rrset, scanner->r_data, scanner->r_data_length,
+ NULL);
+parser_failed:
+ zs_deinit(scanner);
+
+ return ret;
+}
+
+static int zone_txn_set(zone_t *zone, ctl_args_t *args)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ if (args->data[KNOT_CTL_IDX_OWNER] == NULL ||
+ args->data[KNOT_CTL_IDX_TYPE] == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t *rrset;
+ int ret = create_rrset(&rrset, zone, args, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_update_add(zone->control_update, rrset);
+ knot_rrset_free(rrset, NULL);
+
+ return ret;
+}
+
+static int zone_txn_unset(zone_t *zone, ctl_args_t *args)
+{
+ if (zone->control_update == NULL) {
+ args->suppress = true;
+ return KNOT_TXN_ENOTEXISTS;
+ }
+
+ if (args->data[KNOT_CTL_IDX_OWNER] == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Remove specific record.
+ if (args->data[KNOT_CTL_IDX_DATA] != NULL) {
+ if (args->data[KNOT_CTL_IDX_TYPE] == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t *rrset;
+ int ret = create_rrset(&rrset, zone, args, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_update_remove(zone->control_update, rrset);
+ knot_rrset_free(rrset, NULL);
+ return ret;
+ } else {
+ knot_dname_storage_t owner;
+
+ int ret = get_owner(owner, sizeof(owner), zone->name, args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Remove whole rrset.
+ if (args->data[KNOT_CTL_IDX_TYPE] != NULL) {
+ uint16_t type;
+ if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE],
+ &type) != 0) {
+ return KNOT_EINVAL;
+ }
+
+ return zone_update_remove_rrset(zone->control_update, owner, type);
+ // Remove whole node.
+ } else {
+ return zone_update_remove_node(zone->control_update, owner);
+ }
+ }
+}
+
+static bool zone_exists(const knot_dname_t *zone, void *data)
+{
+ assert(zone);
+ assert(data);
+
+ knot_zonedb_t *db = data;
+
+ return knot_zonedb_find(db, zone) != NULL;
+}
+
+static bool zone_names_distinct(const knot_dname_t *zone, void *data)
+{
+ assert(zone);
+ assert(data);
+
+ knot_dname_t *zone_to_purge = data;
+
+ return !knot_dname_is_equal(zone, zone_to_purge);
+}
+
+static int drop_journal_if_orphan(const knot_dname_t *for_zone, void *ctx)
+{
+ server_t *server = ctx;
+ zone_journal_t j = { &server->journaldb, for_zone };
+ if (!zone_exists(for_zone, server->zone_db)) {
+ return journal_scrape_with_md(j, false);
+ }
+ return KNOT_EOK;
+}
+
+static int purge_orphan_member_cb(const knot_dname_t *member, const knot_dname_t *owner,
+ const knot_dname_t *catz, const char *group, void *ctx)
+{
+ server_t *server = ctx;
+ if (zone_exists(member, server->zone_db)) {
+ return KNOT_EOK;
+ }
+
+ const char *err_str = NULL;
+
+ rcu_read_lock();
+ zone_t *cat_z = knot_zonedb_find(server->zone_db, catz);
+ if (cat_z == NULL) {
+ err_str = "existing";
+ } else if (!cat_z->is_catalog_flag) {
+ err_str = "catalog";
+ }
+ rcu_read_unlock();
+
+ if (err_str == NULL) {
+ return KNOT_EOK;
+ }
+
+ knot_dname_txt_storage_t catz_str;
+ (void)knot_dname_to_str(catz_str, catz, sizeof(catz_str));
+ log_zone_info(member, "member of a non-%s zone %s",
+ err_str, catz_str);
+
+ // Single-purpose fake zone_t containing only minimal data.
+ // malloc() should suffice here, but clean zone_t is more mishandling-proof.
+ zone_t *orphan = calloc(1, sizeof(zone_t));
+ if (orphan == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ orphan->name = (knot_dname_t *)member;
+ orphan->server = server;
+
+ purge_flag_t params =
+ PURGE_ZONE_TIMERS | PURGE_ZONE_JOURNAL | PURGE_ZONE_KASPDB |
+ PURGE_ZONE_BEST | PURGE_ZONE_LOG;
+
+ int ret = selective_zone_purge(conf(), orphan, params);
+ free(orphan);
+ if (ret != KNOT_EOK) {
+ log_zone_error(member, "purge of an orphaned zone failed (%s)",
+ knot_strerror(ret));
+ }
+
+ // this deleting inside catalog DB iteration is OK, since
+ // the deletion happens in RW txn, while the iteration in persistent RO txn
+ ret = catalog_del(&server->catalog, member);
+ if (ret != KNOT_EOK) {
+ log_zone_error(member, "remove of an orphan from catalog failed (%s)",
+ knot_strerror(ret));
+ }
+
+ return KNOT_EOK;
+}
+
+static int catalog_orphans_sweep(server_t *server)
+{
+ catalog_t *cat = &server->catalog;
+ int ret2 = KNOT_EOK;
+ int ret = catalog_begin(cat);
+ if (ret == KNOT_EOK) {
+ ret = catalog_apply(cat, NULL,
+ purge_orphan_member_cb,
+ server, false);
+ if (ret != KNOT_EOK) {
+ log_error("failed to purge orphan members data (%s)",
+ knot_strerror(ret));
+ }
+ ret2 = catalog_commit(cat);
+ synchronize_rcu();
+ catalog_commit_cleanup(cat);
+ if (ret2 != KNOT_EOK) {
+ log_error("failed to update catalog (%s)",
+ knot_strerror(ret));
+ }
+ } else {
+ log_error("can't open catalog for purging (%s)",
+ knot_strerror(ret));
+ }
+
+ return (ret == KNOT_EOK) ? ret2 : ret;
+}
+
+static void log_if_orphans_error(knot_dname_t *zone_name, int err, char *db_type,
+ bool *failed)
+{
+ if (err == KNOT_EOK || err == KNOT_ENOENT || err == KNOT_EFILE) {
+ return;
+ }
+
+ *failed = true;
+ const char *error = knot_strerror(err);
+
+ char *msg = sprintf_alloc("control, failed to purge orphan from %s database (%s)",
+ db_type, error);
+ if (msg == NULL) {
+ return;
+ }
+
+ if (zone_name == NULL) {
+ log_error("%s", msg);
+ } else {
+ log_zone_error(zone_name, "%s", msg);
+ }
+ free(msg);
+}
+
+static int orphans_purge(ctl_args_t *args)
+{
+ assert(args->data[KNOT_CTL_IDX_FILTER] != NULL);
+ bool only_orphan = (strlen(args->data[KNOT_CTL_IDX_FILTER]) == 1);
+ int ret;
+ bool failed = false;
+
+ if (args->data[KNOT_CTL_IDX_ZONE] == NULL) {
+ // Purge KASP DB.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
+ ret = kasp_db_sweep(&args->server->kaspdb,
+ zone_exists, args->server->zone_db);
+ log_if_orphans_error(NULL, ret, "KASP", &failed);
+ }
+
+ // Purge zone journals of unconfigured zones.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
+ ret = journals_walk(&args->server->journaldb,
+ drop_journal_if_orphan, args->server);
+ log_if_orphans_error(NULL, ret, "journal", &failed);
+ }
+
+ // Purge timers of unconfigured zones.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
+ ret = zone_timers_sweep(&args->server->timerdb,
+ zone_exists, args->server->zone_db);
+ log_if_orphans_error(NULL, ret, "timer", &failed);
+ }
+
+ // Purge and remove orphan members of non-existing/non-catalog zones.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_CATALOG)) {
+ ret = catalog_orphans_sweep(args->server);
+ log_if_orphans_error(NULL, ret, "catalog", &failed);
+ }
+
+ if (failed) {
+ send_error(args, knot_strerror(KNOT_CTL_EZONE));
+ }
+ } else {
+ knot_dname_storage_t buff;
+ while (true) {
+ knot_dname_t *zone_name =
+ knot_dname_from_str(buff, args->data[KNOT_CTL_IDX_ZONE],
+ sizeof(buff));
+ if (zone_name == NULL) {
+ log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE],
+ "control, error (%s)",
+ knot_strerror(KNOT_EINVAL));
+ send_error(args, knot_strerror(KNOT_EINVAL));
+ return KNOT_EINVAL;
+ }
+ knot_dname_to_lower(zone_name);
+
+ if (!zone_exists(zone_name, args->server->zone_db)) {
+ // Purge KASP DB.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) {
+ if (knot_lmdb_open(&args->server->kaspdb) == KNOT_EOK) {
+ ret = kasp_db_delete_all(&args->server->kaspdb, zone_name);
+ log_if_orphans_error(zone_name, ret, "KASP", &failed);
+ }
+ }
+
+ // Purge zone journal.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) {
+ zone_journal_t j = { &args->server->journaldb, zone_name };
+ ret = journal_scrape_with_md(j, true);
+ log_if_orphans_error(zone_name, ret, "journal", &failed);
+ }
+
+ // Purge zone timers.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) {
+ ret = zone_timers_sweep(&args->server->timerdb,
+ zone_names_distinct, zone_name);
+ log_if_orphans_error(zone_name, ret, "timer", &failed);
+ }
+
+ // Purge Catalog.
+ if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_CATALOG)) {
+ ret = catalog_zone_purge(args->server, NULL, zone_name);
+ log_if_orphans_error(zone_name, ret, "catalog", &failed);
+ }
+
+ if (failed) {
+ send_error(args, knot_strerror(KNOT_ERROR));
+ failed = false;
+ }
+ }
+
+ // Get next zone name.
+ ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+ if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+ break;
+ }
+ strtolower((char *)args->data[KNOT_CTL_IDX_ZONE]);
+
+ // Log the other zones the same way as the first one from process.c.
+ log_ctl_zone_str_info(args->data[KNOT_CTL_IDX_ZONE],
+ "control, received command '%s'",
+ args->data[KNOT_CTL_IDX_CMD]);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int zone_purge(zone_t *zone, ctl_args_t *args)
+{
+ if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_EXPIRE)) {
+ // Abort possible editing transaction.
+ int ret = zone_txn_abort(zone, args);
+ if (ret != KNOT_EOK && ret != KNOT_TXN_ENOTEXISTS) {
+ log_zone_error(zone->name,
+ "failed to abort pending transaction (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ // Expire the zone.
+ // KNOT_EOK is the only return value from event_expire().
+ (void)schedule_trigger(zone, args, ZONE_EVENT_EXPIRE, true);
+ }
+
+ purge_flag_t params =
+ MATCH_OR_FILTER(args, CTL_FILTER_PURGE_TIMERS) * PURGE_ZONE_TIMERS |
+ MATCH_OR_FILTER(args, CTL_FILTER_PURGE_ZONEFILE) * PURGE_ZONE_ZONEFILE |
+ MATCH_OR_FILTER(args, CTL_FILTER_PURGE_JOURNAL) * PURGE_ZONE_JOURNAL |
+ MATCH_OR_FILTER(args, CTL_FILTER_PURGE_KASPDB) * PURGE_ZONE_KASPDB |
+ MATCH_OR_FILTER(args, CTL_FILTER_PURGE_CATALOG) * PURGE_ZONE_CATALOG |
+ PURGE_ZONE_NOSYNC; // Purge even zonefiles with disabled syncing.
+
+ // Purge the requested zone data.
+ return selective_zone_purge(conf(), zone, params);
+}
+
+static int send_stats_ctr(mod_ctr_t *ctr, uint64_t **stats_vals, unsigned threads,
+ ctl_args_t *args, knot_ctl_data_t *data)
+{
+ char index[128];
+ char value[32];
+
+ if (ctr->count == 1) {
+ uint64_t counter = stats_get_counter(stats_vals, ctr->offset, threads);
+ int ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
+ if (ret <= 0 || ret >= sizeof(value)) {
+ return KNOT_ESPACE;
+ }
+
+ (*data)[KNOT_CTL_IDX_ID] = NULL;
+ (*data)[KNOT_CTL_IDX_DATA] = value;
+
+ ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ bool force = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS],
+ CTL_FLAG_FORCE);
+
+ for (uint32_t i = 0; i < ctr->count; i++) {
+ uint64_t counter = stats_get_counter(stats_vals, ctr->offset + i, threads);
+
+ // Skip empty counters.
+ if (counter == 0 && !force) {
+ continue;
+ }
+
+ int ret;
+ if (ctr->idx_to_str) {
+ char *str = ctr->idx_to_str(i, ctr->count);
+ if (str == NULL) {
+ continue;
+ }
+ ret = snprintf(index, sizeof(index), "%s", str);
+ free(str);
+ } else {
+ ret = snprintf(index, sizeof(index), "%u", i);
+ }
+ if (ret <= 0 || ret >= sizeof(index)) {
+ return KNOT_ESPACE;
+ }
+
+ ret = snprintf(value, sizeof(value), "%"PRIu64, counter);
+ if (ret <= 0 || ret >= sizeof(value)) {
+ return KNOT_ESPACE;
+ }
+
+ (*data)[KNOT_CTL_IDX_ID] = index;
+ (*data)[KNOT_CTL_IDX_DATA] = value;
+
+ knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA :
+ KNOT_CTL_TYPE_EXTRA;
+ ret = knot_ctl_send(args->ctl, type, data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int modules_stats(list_t *query_modules, ctl_args_t *args, knot_dname_t *zone)
+{
+ if (query_modules == NULL) {
+ return KNOT_EOK;
+ }
+
+ const char *section = args->data[KNOT_CTL_IDX_SECTION];
+ const char *item = args->data[KNOT_CTL_IDX_ITEM];
+
+ knot_dname_txt_storage_t name = "";
+ knot_ctl_data_t data = { 0 };
+
+ bool section_found = (section == NULL) ? true : false;
+ bool item_found = (item == NULL) ? true : false;
+
+ knotd_mod_t *mod;
+ WALK_LIST(mod, *query_modules) {
+ // Skip modules without statistics.
+ if (mod->stats_count == 0) {
+ continue;
+ }
+
+ // Check for specific module.
+ if (section != NULL) {
+ if (section_found) {
+ break;
+ } else if (strcasecmp(mod->id->name + 1, section) == 0) {
+ section_found = true;
+ } else {
+ continue;
+ }
+ }
+
+ data[KNOT_CTL_IDX_SECTION] = mod->id->name + 1;
+
+ unsigned threads = knotd_mod_threads(mod);
+
+ for (int i = 0; i < mod->stats_count; i++) {
+ mod_ctr_t *ctr = mod->stats_info + i;
+
+ // Skip empty counter.
+ if (ctr->name == NULL) {
+ continue;
+ }
+
+ // Check for specific counter.
+ if (item != NULL) {
+ if (item_found) {
+ break;
+ } else if (strcasecmp(ctr->name, item) == 0) {
+ item_found = true;
+ } else {
+ continue;
+ }
+ }
+
+ // Prepare zone name if not already prepared.
+ if (zone != NULL && name[0] == '\0') {
+ if (knot_dname_to_str(name, zone, sizeof(name)) == NULL) {
+ return KNOT_EINVAL;
+ }
+ data[KNOT_CTL_IDX_ZONE] = name;
+ }
+
+ data[KNOT_CTL_IDX_ITEM] = ctr->name;
+
+ // Send the counters.
+ int ret = send_stats_ctr(ctr, mod->stats_vals, threads, args, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return (section_found && item_found) ? KNOT_EOK : KNOT_ENOENT;
+}
+
+static int zone_stats(zone_t *zone, ctl_args_t *args)
+{
+ return modules_stats(&zone->query_modules, args, zone->name);
+}
+
+static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ switch (cmd) {
+ case CTL_ZONE_STATUS:
+ return zones_apply(args, zone_status);
+ case CTL_ZONE_RELOAD:
+ return zones_apply(args, zone_reload);
+ case CTL_ZONE_REFRESH:
+ return zones_apply(args, zone_refresh);
+ case CTL_ZONE_RETRANSFER:
+ return zones_apply(args, zone_retransfer);
+ case CTL_ZONE_NOTIFY:
+ return zones_apply(args, zone_notify);
+ case CTL_ZONE_FLUSH:
+ return zones_apply(args, zone_flush);
+ case CTL_ZONE_BACKUP:
+ return zones_apply_backup(args, false);
+ case CTL_ZONE_RESTORE:
+ return zones_apply_backup(args, true);
+ case CTL_ZONE_SIGN:
+ return zones_apply(args, zone_sign);
+ case CTL_ZONE_KEYS_LOAD:
+ return zones_apply(args, zone_keys_load);
+ case CTL_ZONE_KEY_ROLL:
+ return zones_apply(args, zone_key_roll);
+ case CTL_ZONE_KSK_SBM:
+ return zones_apply(args, zone_ksk_sbm_confirm);
+ case CTL_ZONE_FREEZE:
+ return zones_apply(args, zone_freeze);
+ case CTL_ZONE_THAW:
+ return zones_apply(args, zone_thaw);
+ case CTL_ZONE_XFR_FREEZE:
+ return zones_apply(args, zone_xfr_freeze);
+ case CTL_ZONE_XFR_THAW:
+ return zones_apply(args, zone_xfr_thaw);
+ case CTL_ZONE_READ:
+ return zones_apply(args, zone_read);
+ case CTL_ZONE_BEGIN:
+ return zones_apply(args, zone_txn_begin);
+ case CTL_ZONE_COMMIT:
+ return zones_apply(args, zone_txn_commit);
+ case CTL_ZONE_ABORT:
+ return zones_apply(args, zone_txn_abort);
+ case CTL_ZONE_DIFF:
+ return zones_apply(args, zone_txn_diff);
+ case CTL_ZONE_GET:
+ return zones_apply(args, zone_txn_get);
+ case CTL_ZONE_SET:
+ return zones_apply(args, zone_txn_set);
+ case CTL_ZONE_UNSET:
+ return zones_apply(args, zone_txn_unset);
+ case CTL_ZONE_PURGE:
+ if (MATCH_AND_FILTER(args, CTL_FILTER_PURGE_ORPHAN)) {
+ return orphans_purge(args);
+ } else {
+ return zones_apply(args, zone_purge);
+ }
+ case CTL_ZONE_STATS:
+ return zones_apply(args, zone_stats);
+ default:
+ assert(0);
+ return KNOT_EINVAL;
+ }
+}
+
+static int server_status(ctl_args_t *args)
+{
+ const char *type = args->data[KNOT_CTL_IDX_TYPE];
+
+ if (type == NULL || strlen(type) == 0) {
+ return KNOT_EOK;
+ }
+
+ char buff[4096] = "";
+
+ int ret;
+ if (strcasecmp(type, "version") == 0) {
+ ret = snprintf(buff, sizeof(buff), "Version: %s", PACKAGE_VERSION);
+ } else if (strcasecmp(type, "workers") == 0) {
+ int running_bkg_wrk, wrk_queue;
+ worker_pool_status(args->server->workers, false, &running_bkg_wrk, &wrk_queue);
+ ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers: %zu, "
+ "XDP workers: %zu, background workers: %zu (running: %d, pending: %d)",
+ conf()->cache.srv_udp_threads, conf()->cache.srv_tcp_threads,
+ conf()->cache.srv_xdp_threads, conf()->cache.srv_bg_threads,
+ running_bkg_wrk, wrk_queue);
+ } else if (strcasecmp(type, "configure") == 0) {
+ ret = snprintf(buff, sizeof(buff), "%s", CONFIGURE_SUMMARY);
+ } else {
+ return KNOT_EINVAL;
+ }
+ if (ret <= 0 || ret >= sizeof(buff)) {
+ return KNOT_ESPACE;
+ }
+
+ args->data[KNOT_CTL_IDX_DATA] = buff;
+
+ return knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &args->data);
+}
+
+static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ int ret = KNOT_EOK;
+
+ switch (cmd) {
+ case CTL_STATUS:
+ ret = server_status(args);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ }
+ break;
+ case CTL_STOP:
+ ret = KNOT_CTL_ESTOP;
+ break;
+ case CTL_RELOAD:
+ ret = server_reload(args->server, RELOAD_FULL);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ }
+ break;
+ default:
+ assert(0);
+ ret = KNOT_EINVAL;
+ }
+
+ return ret;
+}
+
+static int ctl_stats(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ const char *section = args->data[KNOT_CTL_IDX_SECTION];
+ const char *item = args->data[KNOT_CTL_IDX_ITEM];
+
+ bool found = (section == NULL) ? true : false;
+
+ // Process server metrics.
+ if (section == NULL || strcasecmp(section, "server") == 0) {
+ char value[32];
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_SECTION] = "server",
+ [KNOT_CTL_IDX_DATA] = value
+ };
+
+ for (const stats_item_t *i = server_stats; i->name != NULL; i++) {
+ if (item != NULL) {
+ if (found) {
+ break;
+ } else if (strcmp(i->name, item) == 0) {
+ found = true;
+ } else {
+ continue;
+ }
+ } else {
+ found = true;
+ }
+
+ data[KNOT_CTL_IDX_ITEM] = i->name;
+ int ret = snprintf(value, sizeof(value), "%"PRIu64,
+ i->val(args->server));
+ if (ret <= 0 || ret >= sizeof(value)) {
+ ret = KNOT_ESPACE;
+ send_error(args, knot_strerror(ret));
+ return ret;
+ }
+
+ ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ return ret;
+ }
+ }
+ }
+
+ // Process modules metrics.
+ if (section == NULL || strncasecmp(section, "mod-", strlen("mod-")) == 0) {
+ int ret = modules_stats(conf()->query_modules, args, NULL);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ return ret;
+ }
+
+ found = true;
+ }
+
+ if (!found) {
+ send_error(args, knot_strerror(KNOT_EINVAL));
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int send_block_data(conf_io_t *io, knot_ctl_data_t *data)
+{
+ knot_ctl_t *ctl = (knot_ctl_t *)io->misc;
+
+ const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0;
+ assert(item != NULL);
+
+ char buff[YP_MAX_TXT_DATA_LEN + 1] = "\0";
+
+ (*data)[KNOT_CTL_IDX_DATA] = buff;
+
+ // Format explicit binary data value.
+ if (io->data.bin != NULL) {
+ size_t buff_len = sizeof(buff);
+ int ret = yp_item_to_txt(item, io->data.bin, io->data.bin_len, buff,
+ &buff_len, YP_SNOQUOTE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data);
+ // Format all multivalued item data if no specified index.
+ } else if ((item->flags & YP_FMULTI) && io->data.index == 0) {
+ size_t values = conf_val_count(io->data.val);
+ for (size_t i = 0; i < values; i++) {
+ conf_val(io->data.val);
+ size_t buff_len = sizeof(buff);
+ int ret = yp_item_to_txt(item, io->data.val->data,
+ io->data.val->len, buff,&buff_len,
+ YP_SNOQUOTE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA :
+ KNOT_CTL_TYPE_EXTRA;
+ ret = knot_ctl_send(ctl, type, data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ conf_val_next(io->data.val);
+ }
+ return KNOT_EOK;
+ // Format singlevalued item data or a specified one from multivalued.
+ } else {
+ conf_val(io->data.val);
+ size_t buff_len = sizeof(buff);
+ int ret = yp_item_to_txt(item, io->data.val->data, io->data.val->len,
+ buff, &buff_len, YP_SNOQUOTE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data);
+ }
+}
+
+static int send_block(conf_io_t *io)
+{
+ knot_ctl_t *ctl = (knot_ctl_t *)io->misc;
+
+ // Get possible error message.
+ const char *err = io->error.str;
+ if (err == NULL && io->error.code != KNOT_EOK) {
+ err = knot_strerror(io->error.code);
+ }
+
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_ERROR] = err,
+ };
+
+ if (io->key0 != NULL) {
+ data[KNOT_CTL_IDX_SECTION] = io->key0->name + 1;
+ }
+ if (io->key1 != NULL) {
+ data[KNOT_CTL_IDX_ITEM] = io->key1->name + 1;
+ }
+
+ // Get the item prefix.
+ switch (io->type) {
+ case NEW: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_DIFF_ADD; break;
+ case OLD: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_DIFF_REM; break;
+ default: break;
+ }
+
+ knot_dname_txt_storage_t id;
+
+ // Get the textual item id.
+ if (io->id_len > 0 && io->key0 != NULL) {
+ size_t id_len = sizeof(id);
+ int ret = yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len,
+ id, &id_len, YP_SNOQUOTE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (io->id_as_data) {
+ data[KNOT_CTL_IDX_DATA] = id;
+ } else {
+ data[KNOT_CTL_IDX_ID] = id;
+ }
+ }
+
+ if (io->data.val == NULL && io->data.bin == NULL) {
+ return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &data);
+ } else {
+ return send_block_data(io, &data);
+ }
+}
+
+static int ctl_conf_txn(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ conf_io_t io = {
+ .fcn = send_block,
+ .misc = args->ctl
+ };
+
+ int ret = KNOT_EOK;
+
+ switch (cmd) {
+ case CTL_CONF_BEGIN:
+ ret = conf_io_begin(false);
+ break;
+ case CTL_CONF_ABORT:
+ conf_io_abort(false);
+ ret = KNOT_EOK;
+ break;
+ case CTL_CONF_COMMIT:
+ // First check the database.
+ ret = conf_io_check(&io);
+ if (ret != KNOT_EOK) {
+ // A semantic error is already sent by the check function.
+ if (io.error.code != KNOT_EOK) {
+ return KNOT_EOK;
+ }
+ // No transaction abort!
+ break;
+ }
+
+ ret = conf_io_commit(false);
+ if (ret != KNOT_EOK) {
+ conf_io_abort(false);
+ break;
+ }
+
+ ret = server_reload(args->server, RELOAD_COMMIT);
+ break;
+ default:
+ assert(0);
+ ret = KNOT_EINVAL;
+ }
+
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+static void list_zone(zone_t *zone, knot_ctl_t *ctl)
+{
+ knot_dname_txt_storage_t buff;
+ knot_dname_to_str(buff, zone->name, sizeof(buff));
+
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_SECTION] = "zone",
+ [KNOT_CTL_IDX_ID] = buff
+ };
+
+ (void)knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &data);
+}
+
+static int list_zones(knot_zonedb_t *zonedb, knot_ctl_t *ctl)
+{
+ assert(zonedb != NULL && ctl != NULL);
+
+ knot_zonedb_foreach(zonedb, list_zone, ctl);
+
+ return KNOT_EOK;
+}
+
+static int ctl_conf_list(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ conf_io_t io = {
+ .fcn = send_block,
+ .misc = args->ctl
+ };
+
+ int ret = KNOT_EOK;
+
+ while (true) {
+ const char *key0 = args->data[KNOT_CTL_IDX_SECTION];
+ const char *key1 = args->data[KNOT_CTL_IDX_ITEM];
+ const char *id = args->data[KNOT_CTL_IDX_ID];
+ const char *flags = args->data[KNOT_CTL_IDX_FLAGS];
+
+ bool schema = ctl_has_flag(flags, CTL_FLAG_LIST_SCHEMA);
+ bool current = !ctl_has_flag(flags, CTL_FLAG_LIST_TXN);
+ bool zones = ctl_has_flag(flags, CTL_FLAG_LIST_ZONES);
+
+ if (zones) {
+ ret = list_zones(args->server->zone_db, args->ctl);
+ } else {
+ ret = conf_io_list(key0, key1, id, schema, current, &io);
+ }
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ break;
+ }
+
+ // Get next data unit.
+ ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+ if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int ctl_conf_read(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ conf_io_t io = {
+ .fcn = send_block,
+ .misc = args->ctl
+ };
+
+ int ret = KNOT_EOK;
+
+ while (true) {
+ const char *key0 = args->data[KNOT_CTL_IDX_SECTION];
+ const char *key1 = args->data[KNOT_CTL_IDX_ITEM];
+ const char *id = args->data[KNOT_CTL_IDX_ID];
+
+ ctl_log_conf_data(&args->data);
+
+ switch (cmd) {
+ case CTL_CONF_READ:
+ ret = conf_io_get(key0, key1, id, true, &io);
+ break;
+ case CTL_CONF_DIFF:
+ ret = conf_io_diff(key0, key1, id, &io);
+ break;
+ case CTL_CONF_GET:
+ ret = conf_io_get(key0, key1, id, false, &io);
+ break;
+ default:
+ assert(0);
+ ret = KNOT_EINVAL;
+ }
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ break;
+ }
+
+ // Get next data unit.
+ ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+ if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd)
+{
+ // Start child transaction.
+ int ret = conf_io_begin(true);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ return ret;
+ }
+
+ while (true) {
+ const char *key0 = args->data[KNOT_CTL_IDX_SECTION];
+ const char *key1 = args->data[KNOT_CTL_IDX_ITEM];
+ const char *id = args->data[KNOT_CTL_IDX_ID];
+ const char *data = args->data[KNOT_CTL_IDX_DATA];
+
+ ctl_log_conf_data(&args->data);
+
+ switch (cmd) {
+ case CTL_CONF_SET:
+ ret = conf_io_set(key0, key1, id, data);
+ break;
+ case CTL_CONF_UNSET:
+ ret = conf_io_unset(key0, key1, id, data);
+ break;
+ default:
+ assert(0);
+ ret = KNOT_EINVAL;
+ }
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ break;
+ }
+
+ // Get next data unit.
+ ret = knot_ctl_receive(args->ctl, &args->type, &args->data);
+ if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) {
+ break;
+ }
+ }
+
+ // Finish child transaction.
+ if (ret == KNOT_EOK) {
+ ret = conf_io_commit(true);
+ if (ret != KNOT_EOK) {
+ send_error(args, knot_strerror(ret));
+ }
+ } else {
+ conf_io_abort(true);
+ }
+
+ return ret;
+}
+
+typedef struct {
+ const char *name;
+ int (*fcn)(ctl_args_t *, ctl_cmd_t);
+} desc_t;
+
+static const desc_t cmd_table[] = {
+ [CTL_NONE] = { "" },
+
+ [CTL_STATUS] = { "status", ctl_server },
+ [CTL_STOP] = { "stop", ctl_server },
+ [CTL_RELOAD] = { "reload", ctl_server },
+ [CTL_STATS] = { "stats", ctl_stats },
+
+ [CTL_ZONE_STATUS] = { "zone-status", ctl_zone },
+ [CTL_ZONE_RELOAD] = { "zone-reload", ctl_zone },
+ [CTL_ZONE_REFRESH] = { "zone-refresh", ctl_zone },
+ [CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone },
+ [CTL_ZONE_NOTIFY] = { "zone-notify", ctl_zone },
+ [CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone },
+ [CTL_ZONE_BACKUP] = { "zone-backup", ctl_zone },
+ [CTL_ZONE_RESTORE] = { "zone-restore", ctl_zone },
+ [CTL_ZONE_SIGN] = { "zone-sign", ctl_zone },
+ [CTL_ZONE_KEYS_LOAD] = { "zone-keys-load", ctl_zone },
+ [CTL_ZONE_KEY_ROLL] = { "zone-key-rollover", ctl_zone },
+ [CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone },
+ [CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone },
+ [CTL_ZONE_THAW] = { "zone-thaw", ctl_zone },
+ [CTL_ZONE_XFR_FREEZE] = { "zone-xfr-freeze", ctl_zone },
+ [CTL_ZONE_XFR_THAW] = { "zone-xfr-thaw", ctl_zone },
+
+ [CTL_ZONE_READ] = { "zone-read", ctl_zone },
+ [CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone },
+ [CTL_ZONE_COMMIT] = { "zone-commit", ctl_zone },
+ [CTL_ZONE_ABORT] = { "zone-abort", ctl_zone },
+ [CTL_ZONE_DIFF] = { "zone-diff", ctl_zone },
+ [CTL_ZONE_GET] = { "zone-get", ctl_zone },
+ [CTL_ZONE_SET] = { "zone-set", ctl_zone },
+ [CTL_ZONE_UNSET] = { "zone-unset", ctl_zone },
+ [CTL_ZONE_PURGE] = { "zone-purge", ctl_zone },
+ [CTL_ZONE_STATS] = { "zone-stats", ctl_zone },
+
+ [CTL_CONF_LIST] = { "conf-list", ctl_conf_list },
+ [CTL_CONF_READ] = { "conf-read", ctl_conf_read },
+ [CTL_CONF_BEGIN] = { "conf-begin", ctl_conf_txn },
+ [CTL_CONF_COMMIT] = { "conf-commit", ctl_conf_txn },
+ [CTL_CONF_ABORT] = { "conf-abort", ctl_conf_txn },
+ [CTL_CONF_DIFF] = { "conf-diff", ctl_conf_read },
+ [CTL_CONF_GET] = { "conf-get", ctl_conf_read },
+ [CTL_CONF_SET] = { "conf-set", ctl_conf_modify },
+ [CTL_CONF_UNSET] = { "conf-unset", ctl_conf_modify },
+};
+
+#define MAX_CTL_CODE (sizeof(cmd_table) / sizeof(desc_t) - 1)
+
+const char *ctl_cmd_to_str(ctl_cmd_t cmd)
+{
+ if (cmd <= CTL_NONE || cmd > MAX_CTL_CODE) {
+ return NULL;
+ }
+
+ return cmd_table[cmd].name;
+}
+
+ctl_cmd_t ctl_str_to_cmd(const char *cmd_str)
+{
+ if (cmd_str == NULL) {
+ return CTL_NONE;
+ }
+
+ for (ctl_cmd_t cmd = CTL_NONE + 1; cmd <= MAX_CTL_CODE; cmd++) {
+ if (strcmp(cmd_str, cmd_table[cmd].name) == 0) {
+ return cmd;
+ }
+ }
+
+ return CTL_NONE;
+}
+
+int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args)
+{
+ if (args == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return cmd_table[cmd].fcn(args, cmd);
+}
+
+bool ctl_has_flag(const char *flags, const char *flag)
+{
+ if (flags == NULL || flag == NULL) {
+ return false;
+ }
+
+ return strstr(flags, flag) != NULL;
+}
diff --git a/src/knot/ctl/commands.h b/src/knot/ctl/commands.h
new file mode 100644
index 0000000..ab7984e
--- /dev/null
+++ b/src/knot/ctl/commands.h
@@ -0,0 +1,160 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+#include "knot/server/server.h"
+
+#define CTL_FLAG_FORCE "F"
+#define CTL_FLAG_BLOCKING "B"
+
+#define CTL_FLAG_DIFF_ADD "+"
+#define CTL_FLAG_DIFF_REM "-"
+
+#define CTL_FLAG_LIST_SCHEMA "s"
+#define CTL_FLAG_LIST_TXN "t"
+#define CTL_FLAG_LIST_ZONES "z"
+
+#define CTL_FLAG_STATUS_EMPTY "e"
+#define CTL_FLAG_STATUS_SLAVE "s"
+#define CTL_FLAG_STATUS_MEMBER "m"
+
+#define CTL_FILTER_FLUSH_OUTDIR 'd'
+
+#define CTL_FILTER_STATUS_ROLE 'r'
+#define CTL_FILTER_STATUS_SERIAL 's'
+#define CTL_FILTER_STATUS_TRANSACTION 't'
+#define CTL_FILTER_STATUS_FREEZE 'f'
+#define CTL_FILTER_STATUS_CATALOG 'c'
+#define CTL_FILTER_STATUS_EVENTS 'e'
+
+#define CTL_FILTER_PURGE_EXPIRE 'e'
+#define CTL_FILTER_PURGE_ZONEFILE 'f'
+#define CTL_FILTER_PURGE_JOURNAL 'j'
+#define CTL_FILTER_PURGE_TIMERS 't'
+#define CTL_FILTER_PURGE_KASPDB 'k'
+#define CTL_FILTER_PURGE_CATALOG 'c'
+#define CTL_FILTER_PURGE_ORPHAN 'o'
+
+#define CTL_FILTER_BACKUP_OUTDIR 'd'
+#define CTL_FILTER_BACKUP_ZONEFILE 'z'
+#define CTL_FILTER_BACKUP_NOZONEFILE 'Z'
+#define CTL_FILTER_BACKUP_JOURNAL 'j'
+#define CTL_FILTER_BACKUP_NOJOURNAL 'J'
+#define CTL_FILTER_BACKUP_TIMERS 't'
+#define CTL_FILTER_BACKUP_NOTIMERS 'T'
+#define CTL_FILTER_BACKUP_KASPDB 'k'
+#define CTL_FILTER_BACKUP_NOKASPDB 'K'
+#define CTL_FILTER_BACKUP_CATALOG 'c'
+#define CTL_FILTER_BACKUP_NOCATALOG 'C'
+
+#define STATUS_EMPTY "-"
+
+/*! Control commands. */
+typedef enum {
+ CTL_NONE,
+
+ CTL_STATUS,
+ CTL_STOP,
+ CTL_RELOAD,
+ CTL_STATS,
+
+ CTL_ZONE_STATUS,
+ CTL_ZONE_RELOAD,
+ CTL_ZONE_REFRESH,
+ CTL_ZONE_RETRANSFER,
+ CTL_ZONE_NOTIFY,
+ CTL_ZONE_FLUSH,
+ CTL_ZONE_BACKUP,
+ CTL_ZONE_RESTORE,
+ CTL_ZONE_SIGN,
+ CTL_ZONE_KEYS_LOAD,
+ CTL_ZONE_KEY_ROLL,
+ CTL_ZONE_KSK_SBM,
+ CTL_ZONE_FREEZE,
+ CTL_ZONE_THAW,
+ CTL_ZONE_XFR_FREEZE,
+ CTL_ZONE_XFR_THAW,
+
+ CTL_ZONE_READ,
+ CTL_ZONE_BEGIN,
+ CTL_ZONE_COMMIT,
+ CTL_ZONE_ABORT,
+ CTL_ZONE_DIFF,
+ CTL_ZONE_GET,
+ CTL_ZONE_SET,
+ CTL_ZONE_UNSET,
+ CTL_ZONE_PURGE,
+ CTL_ZONE_STATS,
+
+ CTL_CONF_LIST,
+ CTL_CONF_READ,
+ CTL_CONF_BEGIN,
+ CTL_CONF_COMMIT,
+ CTL_CONF_ABORT,
+ CTL_CONF_DIFF,
+ CTL_CONF_GET,
+ CTL_CONF_SET,
+ CTL_CONF_UNSET,
+} ctl_cmd_t;
+
+/*! Control command parameters. */
+typedef struct {
+ knot_ctl_t *ctl;
+ knot_ctl_type_t type;
+ knot_ctl_data_t data;
+ server_t *server;
+ bool suppress; // Suppress error reporting in the "all zones" ctl commands.
+} ctl_args_t;
+
+/*!
+ * Returns a string equivalent of the command.
+ *
+ * \param[in] cmd Command.
+ *
+ * \return Command string or NULL.
+ */
+const char *ctl_cmd_to_str(ctl_cmd_t cmd);
+
+/*!
+ * Returns a command corresponding to the string.
+ *
+ * \param[in] cmd_str Command string.
+ *
+ * \return Command.
+ */
+ctl_cmd_t ctl_str_to_cmd(const char *cmd_str);
+
+/*!
+ * Executes a control command.
+ *
+ * \param[in] cmd Control command.
+ * \param[in] args Command arguments.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args);
+
+/*!
+ * Checks flag presence in flags.
+ *
+ * \param[in] flags Flags to check presence in.
+ * \param[in] flag Checked flag.
+ *
+ * \return True if presented.
+ */
+bool ctl_has_flag(const char *flags, const char *flag);
diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c
new file mode 100644
index 0000000..50fde21
--- /dev/null
+++ b/src/knot/ctl/process.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 2022 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 "knot/common/log.h"
+#include "knot/ctl/commands.h"
+#include "knot/ctl/process.h"
+#include "libknot/error.h"
+#include "contrib/string.h"
+
+int ctl_process(knot_ctl_t *ctl, server_t *server)
+{
+ if (ctl == NULL || server == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ ctl_args_t args = {
+ .ctl = ctl,
+ .type = KNOT_CTL_TYPE_END,
+ .server = server
+ };
+
+ // Strip redundant/unprocessed data units in the current block.
+ bool strip = false;
+
+ while (true) {
+ // Receive data unit.
+ int ret = knot_ctl_receive(args.ctl, &args.type, &args.data);
+ if (ret != KNOT_EOK) {
+ log_ctl_debug("control, failed to receive (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ // Decide what to do.
+ switch (args.type) {
+ case KNOT_CTL_TYPE_DATA:
+ // Leading data unit with a command name.
+ if (!strip) {
+ // Set to strip unprocessed data unit.
+ strip = true;
+ break;
+ }
+ // FALLTHROUGH
+ case KNOT_CTL_TYPE_EXTRA:
+ // All non-first data units should be parsed in a callback.
+ // Ignore if probable previous error.
+ continue;
+ case KNOT_CTL_TYPE_BLOCK:
+ strip = false;
+ continue;
+ case KNOT_CTL_TYPE_END:
+ return KNOT_EOF;
+ default:
+ assert(0);
+ }
+
+ strtolower((char *)args.data[KNOT_CTL_IDX_ZONE]);
+
+ const char *cmd_name = args.data[KNOT_CTL_IDX_CMD];
+ const char *zone_name = args.data[KNOT_CTL_IDX_ZONE];
+
+ ctl_cmd_t cmd = ctl_str_to_cmd(cmd_name);
+ if (cmd == CTL_CONF_LIST) {
+ log_ctl_debug("control, received command '%s'", cmd_name);
+ } else if (cmd != CTL_NONE) {
+ if (zone_name != NULL) {
+ log_ctl_zone_str_info(zone_name,
+ "control, received command '%s'", cmd_name);
+ } else {
+ log_ctl_info("control, received command '%s'", cmd_name);
+ }
+ } else if (cmd_name != NULL){
+ log_ctl_debug("control, invalid command '%s'", cmd_name);
+ continue;
+ } else {
+ log_ctl_debug("control, empty command");
+ continue;
+ }
+
+ // Execute the command.
+ int cmd_ret = ctl_exec(cmd, &args);
+ switch (cmd_ret) {
+ case KNOT_EOK:
+ strip = false;
+ case KNOT_CTL_ESTOP:
+ case KNOT_CTL_EZONE:
+ // KNOT_CTL_EZONE - don't change strip, but don't be reported
+ // as a ctl/communication error either.
+ break;
+ default:
+ log_ctl_debug("control, command '%s' (%s)", cmd_name,
+ knot_strerror(cmd_ret));
+ break;
+ }
+
+ // Finalize the answer block.
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
+ if (ret != KNOT_EOK) {
+ log_ctl_debug("control, failed to reply (%s)",
+ knot_strerror(ret));
+ }
+
+ // Stop if required.
+ if (cmd_ret == KNOT_CTL_ESTOP) {
+ // Finalize the answer message.
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ if (ret != KNOT_EOK) {
+ log_ctl_debug("control, failed to reply (%s)",
+ knot_strerror(ret));
+ }
+
+ return cmd_ret;
+ }
+ }
+}
diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h
new file mode 100644
index 0000000..ab0f75f
--- /dev/null
+++ b/src/knot/ctl/process.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+#include "knot/server/server.h"
+
+/*!
+ * Processes incoming control commands.
+ *
+ * \param[in] ctl Control context.
+ * \param[in] server Server instance.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int ctl_process(knot_ctl_t *ctl, server_t *server);
diff --git a/src/knot/dnssec/context.c b/src/knot/dnssec/context.c
new file mode 100644
index 0000000..f2a3685
--- /dev/null
+++ b/src/knot/dnssec/context.c
@@ -0,0 +1,351 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <string.h>
+
+#include "contrib/macros.h"
+#include "contrib/time.h"
+#include "libknot/libknot.h"
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/kasp/keystore.h"
+#include "knot/dnssec/key_records.h"
+#include "knot/server/dthreads.h"
+
+knot_dynarray_define(parent, knot_kasp_parent_t, DYNARRAY_VISIBILITY_NORMAL)
+
+static void policy_load(knot_kasp_policy_t *policy, conf_t *conf, conf_val_t *id,
+ const knot_dname_t *zone_name)
+{
+ if (conf_str(id) == NULL) {
+ policy->string = strdup("default");
+ } else {
+ policy->string = strdup(conf_str(id));
+ }
+
+ conf_val_t val = conf_id_get(conf, C_POLICY, C_MANUAL, id);
+ policy->manual = conf_bool(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_SINGLE_TYPE_SIGNING, id);
+ policy->single_type_signing = conf_bool(&val);
+ policy->sts_default = (val.code != KNOT_EOK);
+
+ val = conf_id_get(conf, C_POLICY, C_ALG, id);
+ policy->algorithm = conf_opt(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_KSK_SHARED, id);
+ policy->ksk_shared = conf_bool(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_KSK_SIZE, id);
+ int64_t num = conf_int(&val);
+ policy->ksk_size = (num != YP_NIL) ? num :
+ dnssec_algorithm_key_size_default(policy->algorithm);
+
+ val = conf_id_get(conf, C_POLICY, C_ZSK_SIZE, id);
+ num = conf_int(&val);
+ policy->zsk_size = (num != YP_NIL) ? num :
+ dnssec_algorithm_key_size_default(policy->algorithm);
+
+ val = conf_id_get(conf, C_POLICY, C_DNSKEY_TTL, id);
+ int64_t ttl = conf_int(&val);
+ policy->dnskey_ttl = (ttl != YP_NIL) ? ttl : UINT32_MAX;
+
+ val = conf_id_get(conf, C_POLICY, C_ZONE_MAX_TTL, id);
+ ttl = conf_int(&val);
+ policy->zone_maximal_ttl = (ttl != YP_NIL) ? ttl : UINT32_MAX;
+
+ val = conf_id_get(conf, C_POLICY, C_ZSK_LIFETIME, id);
+ policy->zsk_lifetime = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_KSK_LIFETIME, id);
+ policy->ksk_lifetime = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_DELETE_DELAY, id);
+ policy->delete_delay = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_PROPAG_DELAY, id);
+ policy->propagation_delay = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_RRSIG_LIFETIME, id);
+ policy->rrsig_lifetime = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_RRSIG_REFRESH, id);
+ num = conf_int(&val);
+ policy->rrsig_refresh_before = (num != YP_NIL) ? num : UINT32_MAX;
+ if (policy->rrsig_refresh_before == UINT32_MAX && policy->zone_maximal_ttl != UINT32_MAX) {
+ policy->rrsig_refresh_before = policy->propagation_delay + policy->zone_maximal_ttl;
+ }
+
+ val = conf_id_get(conf, C_POLICY, C_RRSIG_PREREFRESH, id);
+ policy->rrsig_prerefresh = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_REPRO_SIGNING, id);
+ policy->reproducible_sign = conf_bool(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_NSEC3, id);
+ policy->nsec3_enabled = conf_bool(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_NSEC3_OPT_OUT, id);
+ policy->nsec3_opt_out = conf_bool(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_NSEC3_ITER, id);
+ policy->nsec3_iterations = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_NSEC3_SALT_LEN, id);
+ policy->nsec3_salt_length = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_NSEC3_SALT_LIFETIME, id);
+ policy->nsec3_salt_lifetime = conf_int(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_CDS_CDNSKEY, id);
+ policy->cds_cdnskey_publish = conf_opt(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_CDS_DIGESTTYPE, id);
+ policy->cds_dt = conf_opt(&val);
+
+ val = conf_id_get(conf, C_POLICY, C_DNSKEY_MGMT, id);
+ policy->incremental = (conf_opt(&val) == DNSKEY_MGMT_INCREMENTAL);
+
+ conf_val_t ksk_sbm = conf_id_get(conf, C_POLICY, C_KSK_SBM, id);
+ if (ksk_sbm.code == KNOT_EOK) {
+ val = conf_id_get(conf, C_SBM, C_CHK_INTERVAL, &ksk_sbm);
+ policy->ksk_sbm_check_interval = conf_int(&val);
+
+ val = conf_id_get(conf, C_SBM, C_TIMEOUT, &ksk_sbm);
+ policy->ksk_sbm_timeout = conf_int(&val);
+
+ val = conf_id_get(conf, C_SBM, C_PARENT, &ksk_sbm);
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &val, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ knot_kasp_parent_t p = { .addrs = conf_val_count(&addr) };
+ p.addr = p.addrs ? malloc(p.addrs * sizeof(*p.addr)) : NULL;
+ if (p.addr != NULL) {
+ for (size_t i = 0; i < p.addrs; i++) {
+ p.addr[i] = conf_remote(conf, iter.id, i);
+ }
+ parent_dynarray_add(&policy->parents, &p);
+ }
+ conf_mix_iter_next(&iter);
+ }
+
+ val = conf_id_get(conf, C_SBM, C_PARENT_DELAY, &ksk_sbm);
+ policy->ksk_sbm_delay = conf_int(&val);
+ }
+
+ val = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, id);
+ policy->signing_threads = conf_int(&val);
+
+ val = conf_zone_get(conf, C_DS_PUSH, zone_name);
+ if (val.code != KNOT_EOK) {
+ val = conf_id_get(conf, C_POLICY, C_DS_PUSH, id);
+ }
+ policy->ds_push = conf_val_count(&val) > 0;
+
+ val = conf_id_get(conf, C_POLICY, C_OFFLINE_KSK, id);
+ policy->offline_ksk = conf_bool(&val);
+
+ policy->unsafe = 0;
+ val = conf_id_get(conf, C_POLICY, C_UNSAFE_OPERATION, id);
+ while (val.code == KNOT_EOK) {
+ policy->unsafe |= conf_opt(&val);
+ conf_val_next(&val);
+ }
+}
+
+int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kaspdb, const conf_mod_id_t *from_module)
+{
+ if (ctx == NULL || zone_name == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->zone = calloc(1, sizeof(*ctx->zone));
+ if (ctx->zone == NULL) {
+ ret = KNOT_ENOMEM;
+ goto init_error;
+ }
+
+ ctx->kasp_db = kaspdb;
+ ret = knot_lmdb_open(ctx->kasp_db);
+ if (ret != KNOT_EOK) {
+ goto init_error;
+ }
+
+ ret = kasp_zone_load(ctx->zone, zone_name, ctx->kasp_db,
+ &ctx->keytag_conflict);
+ if (ret != KNOT_EOK) {
+ goto init_error;
+ }
+
+ ctx->kasp_zone_path = conf_db(conf, C_KASP_DB);
+ if (ctx->kasp_zone_path == NULL) {
+ ret = KNOT_ENOMEM;
+ goto init_error;
+ }
+
+ ctx->policy = calloc(1, sizeof(*ctx->policy));
+ if (ctx->policy == NULL) {
+ ret = KNOT_ENOMEM;
+ goto init_error;
+ }
+
+ ret = kasp_db_get_saved_ttls(ctx->kasp_db, zone_name,
+ &ctx->policy->saved_max_ttl,
+ &ctx->policy->saved_key_ttl);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ return ret;
+ }
+
+ conf_val_t policy_id;
+ if (from_module == NULL) {
+ policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone_name);
+ } else {
+ policy_id = conf_mod_get(conf, C_POLICY, from_module);
+ }
+ conf_id_fix_default(&policy_id);
+ policy_load(ctx->policy, conf, &policy_id, ctx->zone->dname);
+
+ ret = zone_init_keystore(conf, &policy_id, &ctx->keystore, NULL,
+ &ctx->policy->key_label);
+ if (ret != KNOT_EOK) {
+ goto init_error;
+ }
+
+ ctx->dbus_event = conf->cache.srv_dbus_event;
+
+ ctx->now = knot_time();
+
+ key_records_init(ctx, &ctx->offline_records);
+ if (ctx->policy->offline_ksk) {
+ ret = kasp_db_load_offline_records(ctx->kasp_db, ctx->zone->dname,
+ &ctx->now, &ctx->offline_next_time,
+ &ctx->offline_records);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ goto init_error;
+ }
+ }
+
+ return KNOT_EOK;
+init_error:
+ kdnssec_ctx_deinit(ctx);
+ return ret;
+}
+
+int kdnssec_ctx_commit(kdnssec_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->kasp_zone_path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (ctx->policy->dnskey_ttl != UINT32_MAX &&
+ ctx->policy->zone_maximal_ttl != UINT32_MAX) {
+ int ret = kasp_db_set_saved_ttls(ctx->kasp_db, ctx->zone->dname,
+ ctx->policy->zone_maximal_ttl,
+ ctx->policy->dnskey_ttl);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return kasp_zone_save(ctx->zone, ctx->zone->dname, ctx->kasp_db);
+}
+
+void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->policy != NULL) {
+ free(ctx->policy->string);
+ knot_dynarray_foreach(parent, knot_kasp_parent_t, i, ctx->policy->parents) {
+ free(i->addr);
+ }
+ free(ctx->policy);
+ }
+ key_records_clear(&ctx->offline_records);
+ dnssec_keystore_deinit(ctx->keystore);
+ kasp_zone_free(&ctx->zone);
+ free(ctx->kasp_zone_path);
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+// expects policy struct to be zeroed
+static void policy_from_zone(knot_kasp_policy_t *policy, const zone_contents_t *zone)
+{
+ knot_rdataset_t *dnskey = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY);
+ knot_rdataset_t *n3p = node_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
+
+ policy->manual = true;
+ policy->single_type_signing = (dnskey != NULL && dnskey->count == 1);
+
+ if (n3p != NULL) {
+ policy->nsec3_enabled = true;
+ policy->nsec3_iterations = knot_nsec3param_iters(n3p->rdata);
+ policy->nsec3_salt_length = knot_nsec3param_salt_len(n3p->rdata);
+ }
+ policy->signing_threads = 1;
+}
+
+int kdnssec_validation_ctx(conf_t *conf, kdnssec_ctx_t *ctx, const zone_contents_t *zone)
+{
+ if (ctx == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->zone = calloc(1, sizeof(*ctx->zone));
+ if (ctx->zone == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ ctx->policy = calloc(1, sizeof(*ctx->policy));
+ if (ctx->policy == NULL) {
+ free(ctx->zone);
+ return KNOT_ENOMEM;
+ }
+
+ policy_from_zone(ctx->policy, zone);
+ if (conf != NULL) {
+ conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->apex->owner);
+ conf_id_fix_default(&policy_id);
+ conf_val_t num_threads = conf_id_get(conf, C_POLICY, C_SIGNING_THREADS, &policy_id);
+ ctx->policy->signing_threads = conf_int(&num_threads);
+ } else {
+ ctx->policy->signing_threads = MAX(dt_optimal_size(), 1);
+ }
+
+ int ret = kasp_zone_from_contents(ctx->zone, zone, ctx->policy->single_type_signing,
+ ctx->policy->nsec3_enabled, &ctx->policy->nsec3_iterations,
+ &ctx->keytag_conflict);
+ if (ret != KNOT_EOK) {
+ memset(ctx->zone, 0, sizeof(*ctx->zone));
+ kdnssec_ctx_deinit(ctx);
+ return ret;
+ }
+
+ ctx->now = knot_time();
+ ctx->validation_mode = true;
+ return KNOT_EOK;
+}
diff --git a/src/knot/dnssec/context.h b/src/knot/dnssec/context.h
new file mode 100644
index 0000000..55a2f3c
--- /dev/null
+++ b/src/knot/dnssec/context.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <time.h>
+
+#include "libdnssec/keystore.h"
+
+#include "knot/conf/conf.h"
+#include "knot/dnssec/kasp/kasp_zone.h"
+#include "knot/dnssec/kasp/policy.h"
+
+/*!
+ * \brief DNSSEC signing context.
+ */
+typedef struct {
+ knot_time_t now;
+
+ knot_lmdb_db_t *kasp_db;
+ knot_kasp_zone_t *zone;
+ knot_kasp_policy_t *policy;
+ dnssec_keystore_t *keystore;
+
+ char *kasp_zone_path;
+
+ bool rrsig_drop_existing;
+ bool keep_deleted_keys;
+ bool keytag_conflict;
+ bool validation_mode;
+
+ unsigned dbus_event;
+
+ key_records_t offline_records;
+ knot_time_t offline_next_time;
+} kdnssec_ctx_t;
+
+/*!
+ * \brief Initialize DNSSEC signing context.
+ *
+ * \param conf Configuration.
+ * \param ctx Signing context to be initialized.
+ * \param zone_name Name of the zone.
+ * \param kaspdb Key and signature policy database.
+ * \param from_module Module identifier if initialized from a module.
+ */
+int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kaspdb, const conf_mod_id_t *from_module);
+
+/*!
+ * \brief Initialize DNSSEC validating context.
+ *
+ * \param conf Configuration.
+ * \param ctx Signing context to be initialized.
+ * \param zone Zone contents to be validated.
+ *
+ * \return KNOT_E*
+ */
+int kdnssec_validation_ctx(conf_t *conf, kdnssec_ctx_t *ctx, const zone_contents_t *zone);
+
+/*!
+ * \brief Save the changes in ctx (in kasp zone).
+ */
+int kdnssec_ctx_commit(kdnssec_ctx_t *ctx);
+
+/*!
+ * \brief Cleanup DNSSEC signing context.
+ */
+void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx);
diff --git a/src/knot/dnssec/ds_query.c b/src/knot/dnssec/ds_query.c
new file mode 100644
index 0000000..918ae5d
--- /dev/null
+++ b/src/knot/dnssec/ds_query.c
@@ -0,0 +1,288 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "contrib/macros.h"
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/ds_query.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/query/layer.h"
+#include "knot/query/query.h"
+#include "knot/query/requestor.h"
+
+static bool match_key_ds(knot_kasp_key_t *key, knot_rdata_t *ds)
+{
+ assert(key);
+ assert(ds);
+
+ dnssec_binary_t ds_rdata = {
+ .size = ds->len,
+ .data = ds->data,
+ };
+
+ dnssec_binary_t cds_rdata = { 0 };
+
+ int ret = dnssec_key_create_ds(key->key, knot_ds_digest_type(ds), &cds_rdata);
+ if (ret != KNOT_EOK) {
+ return false;
+ }
+
+ ret = (dnssec_binary_cmp(&cds_rdata, &ds_rdata) == 0);
+ dnssec_binary_free(&cds_rdata);
+ return ret;
+}
+
+static bool match_key_ds_rrset(knot_kasp_key_t *key, const knot_rrset_t *rr)
+{
+ if (key == NULL) {
+ return false;
+ }
+ knot_rdata_t *rd = rr->rrs.rdata;
+ for (int i = 0; i < rr->rrs.count; i++) {
+ if (match_key_ds(key, rd)) {
+ return true;
+ }
+ rd = knot_rdataset_next(rd);
+ }
+ return false;
+}
+
+struct ds_query_data {
+ conf_t *conf;
+
+ const knot_dname_t *zone_name;
+ const struct sockaddr *remote;
+
+ knot_kasp_key_t *key;
+ knot_kasp_key_t *not_key;
+
+ query_edns_data_t edns;
+
+ bool ds_ok;
+ bool result_logged;
+
+ uint32_t ttl;
+};
+
+static int ds_query_begin(knot_layer_t *layer, void *params)
+{
+ layer->data = params;
+
+ return KNOT_STATE_PRODUCE;
+}
+
+static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct ds_query_data *data = layer->data;
+
+ query_init_pkt(pkt);
+
+ int r = knot_pkt_put_question(pkt, data->zone_name, KNOT_CLASS_IN, KNOT_RRTYPE_DS);
+ if (r != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ r = query_put_edns(pkt, &data->edns);
+ if (r != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ knot_wire_set_rd(pkt->wire);
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct ds_query_data *data = layer->data;
+ data->result_logged = true;
+
+ uint16_t rcode = knot_pkt_ext_rcode(pkt);
+ if (rcode != KNOT_RCODE_NOERROR) {
+ ns_log((rcode == KNOT_RCODE_NXDOMAIN ? LOG_NOTICE : LOG_WARNING),
+ data->zone_name, LOG_OPERATION_DS_CHECK,
+ LOG_DIRECTION_OUT, data->remote,
+ layer->flags & KNOT_REQUESTOR_REUSED,
+ "failed (%s)", knot_pkt_ext_rcode_name(pkt));
+ return KNOT_STATE_FAIL;
+ }
+
+ const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+
+ bool match = false, match_not = false;
+
+ for (size_t j = 0; j < answer->count; j++) {
+ const knot_rrset_t *rr = knot_pkt_rr(answer, j);
+ switch ((rr && rr->rrs.count > 0) ? rr->type : 0) {
+ case KNOT_RRTYPE_DS:
+ if (match_key_ds_rrset(data->key, rr)) {
+ match = true;
+ if (data->ttl == 0) { // fallback: if there is no RRSIG
+ data->ttl = rr->ttl;
+ }
+ }
+ if (match_key_ds_rrset(data->not_key, rr)) {
+ match_not = true;
+ }
+ break;
+ case KNOT_RRTYPE_RRSIG:
+ data->ttl = knot_rrsig_original_ttl(rr->rrs.rdata);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (match_not) {
+ match = false;
+ }
+
+ ns_log(LOG_INFO, data->zone_name, LOG_OPERATION_DS_CHECK,
+ LOG_DIRECTION_OUT, data->remote, layer->flags & KNOT_REQUESTOR_REUSED,
+ "KSK submission check: %s", (match ? "positive" : "negative"));
+
+ if (match) {
+ data->ds_ok = true;
+ }
+ return KNOT_STATE_DONE;
+}
+
+static const knot_layer_api_t ds_query_api = {
+ .begin = ds_query_begin,
+ .produce = ds_query_produce,
+ .consume = ds_query_consume,
+ .reset = NULL,
+ .finish = NULL,
+};
+
+static int try_ds(conf_t *conf, const knot_dname_t *zone_name, const conf_remote_t *parent,
+ knot_kasp_key_t *key, knot_kasp_key_t *not_key, size_t timeout, uint32_t *ds_ttl)
+{
+ // TODO: Abstract interface to issue DNS queries. This is almost copy-pasted.
+
+ assert(zone_name);
+ assert(parent);
+
+ struct ds_query_data data = {
+ .zone_name = zone_name,
+ .remote = (struct sockaddr *)&parent->addr,
+ .key = key,
+ .not_key = not_key,
+ .edns = query_edns_data_init(conf, parent->addr.ss_family,
+ QUERY_EDNS_OPT_DO),
+ .ds_ok = false,
+ .result_logged = false,
+ .ttl = 0,
+ };
+
+ knot_requestor_t requestor;
+ knot_requestor_init(&requestor, &ds_query_api, &data, NULL);
+
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (!pkt) {
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ const struct sockaddr_storage *dst = &parent->addr;
+ const struct sockaddr_storage *src = &parent->via;
+ knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0);
+ if (!req) {
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_requestor_exec(&requestor, req, timeout);
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+
+ // alternative: we could put answer back through ctx instead of errcode
+ if (ret == KNOT_EOK && !data.ds_ok) {
+ ret = KNOT_ENORECORD;
+ }
+
+ if (ret != KNOT_EOK && !data.result_logged) {
+ ns_log(LOG_WARNING, zone_name, LOG_OPERATION_DS_CHECK,
+ LOG_DIRECTION_OUT, data.remote,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "failed (%s)", knot_strerror(ret));
+ }
+
+ *ds_ttl = data.ttl;
+
+ return ret;
+}
+
+static knot_kasp_key_t *get_not_key(kdnssec_ctx_t *kctx, knot_kasp_key_t *key)
+{
+ knot_kasp_key_t *not_key = knot_dnssec_key2retire(kctx, key);
+
+ if (not_key == NULL || dnssec_key_get_algorithm(not_key->key) == dnssec_key_get_algorithm(key->key)) {
+ return NULL;
+ }
+
+ return not_key;
+}
+
+static bool parents_have_ds(conf_t *conf, kdnssec_ctx_t *kctx, knot_kasp_key_t *key,
+ size_t timeout, uint32_t *max_ds_ttl)
+{
+ bool success = false;
+ knot_dynarray_foreach(parent, knot_kasp_parent_t, i, kctx->policy->parents) {
+ success = false;
+ for (size_t j = 0; j < i->addrs; j++) {
+ uint32_t ds_ttl = 0;
+ int ret = try_ds(conf, kctx->zone->dname, &i->addr[j], key,
+ get_not_key(kctx, key), timeout, &ds_ttl);
+ if (ret == KNOT_EOK) {
+ *max_ds_ttl = MAX(*max_ds_ttl, ds_ttl);
+ success = true;
+ break;
+ } else if (ret == KNOT_ENORECORD) {
+ // parent was queried successfully, answer was negative
+ break;
+ }
+ }
+ // Each parent must succeed.
+ if (!success) {
+ return false;
+ }
+ }
+ return success;
+}
+
+int knot_parent_ds_query(conf_t *conf, kdnssec_ctx_t *kctx, size_t timeout)
+{
+ uint32_t max_ds_ttl = 0;
+
+ for (size_t i = 0; i < kctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &kctx->zone->keys[i];
+ if (!key->is_pub_only &&
+ knot_time_cmp(key->timing.ready, kctx->now) <= 0 &&
+ knot_time_cmp(key->timing.active, kctx->now) > 0) {
+ assert(key->is_ksk);
+ if (parents_have_ds(conf, kctx, key, timeout, &max_ds_ttl)) {
+ return knot_dnssec_ksk_sbm_confirm(kctx, max_ds_ttl + kctx->policy->ksk_sbm_delay);
+ } else {
+ return KNOT_ENOENT;
+ }
+ }
+ }
+ return KNOT_NO_READY_KEY;
+}
diff --git a/src/knot/dnssec/ds_query.h b/src/knot/dnssec/ds_query.h
new file mode 100644
index 0000000..1144d21
--- /dev/null
+++ b/src/knot/dnssec/ds_query.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/dnssec/zone-keys.h"
+#include "knot/dnssec/context.h"
+
+int knot_parent_ds_query(conf_t *conf, kdnssec_ctx_t *kctx, size_t timeout);
diff --git a/src/knot/dnssec/kasp/kasp_db.c b/src/knot/dnssec/kasp/kasp_db.c
new file mode 100644
index 0000000..29c6a7d
--- /dev/null
+++ b/src/knot/dnssec/kasp/kasp_db.c
@@ -0,0 +1,610 @@
+/* Copyright (C) 2022 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 "knot/dnssec/kasp/kasp_db.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+
+#include "contrib/strtonum.h"
+#include "contrib/wire_ctx.h"
+#include "knot/dnssec/key_records.h"
+
+typedef enum {
+ KASPDBKEY_PARAMS = 0x1,
+ KASPDBKEY_POLICYLAST = 0x2,
+ KASPDBKEY_NSEC3SALT = 0x3,
+ KASPDBKEY_NSEC3TIME = 0x4,
+ KASPDBKEY_MASTERSERIAL = 0x5,
+ KASPDBKEY_LASTSIGNEDSERIAL = 0x6,
+ KASPDBKEY_OFFLINE_RECORDS = 0x7,
+ KASPDBKEY_SAVED_TTLS = 0x8,
+} keyclass_t;
+
+static const keyclass_t zone_related_classes[] = {
+ KASPDBKEY_PARAMS,
+ KASPDBKEY_NSEC3SALT,
+ KASPDBKEY_NSEC3TIME,
+ KASPDBKEY_MASTERSERIAL,
+ KASPDBKEY_LASTSIGNEDSERIAL,
+ KASPDBKEY_OFFLINE_RECORDS,
+ KASPDBKEY_SAVED_TTLS,
+};
+static const size_t zone_related_classes_size = sizeof(zone_related_classes) / sizeof(*zone_related_classes);
+
+static bool is_zone_related_class(uint8_t class)
+{
+ for (size_t i = 0; i < zone_related_classes_size; i++) {
+ if (zone_related_classes[i] == class) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool is_zone_related(const MDB_val *key)
+{
+ return is_zone_related_class(*(uint8_t *)key->mv_data);
+}
+
+static MDB_val make_key_str(keyclass_t kclass, const knot_dname_t *dname, const char *str)
+{
+ switch (kclass) {
+ case KASPDBKEY_POLICYLAST:
+ assert(dname == NULL && str != NULL);
+ return knot_lmdb_make_key("BS", (int)kclass, str);
+ case KASPDBKEY_NSEC3SALT:
+ case KASPDBKEY_NSEC3TIME:
+ case KASPDBKEY_LASTSIGNEDSERIAL:
+ case KASPDBKEY_MASTERSERIAL:
+ case KASPDBKEY_SAVED_TTLS:
+ assert(dname != NULL && str == NULL);
+ return knot_lmdb_make_key("BN", (int)kclass, dname);
+ case KASPDBKEY_PARAMS:
+ case KASPDBKEY_OFFLINE_RECORDS:
+ assert(dname != NULL);
+ if (str == NULL) {
+ return knot_lmdb_make_key("BN", (int)kclass, dname);
+ } else {
+ return knot_lmdb_make_key("BNS", (int)kclass, dname, str);
+ }
+ default:
+ assert(0);
+ MDB_val empty = { 0 };
+ return empty;
+ }
+}
+
+static MDB_val make_key_time(keyclass_t kclass, const knot_dname_t *dname, knot_time_t time)
+{
+ char tmp[21];
+ (void)snprintf(tmp, sizeof(tmp), "%0*"PRIu64, (int)(sizeof(tmp) - 1), time);
+ return make_key_str(kclass, dname, tmp);
+}
+
+static bool unmake_key_str(const MDB_val *keyv, char **str)
+{
+ uint8_t kclass;
+ const knot_dname_t *dname;
+ const char *s;
+ return (knot_lmdb_unmake_key(keyv->mv_data, keyv->mv_size, "BNS", &kclass, &dname, &s) &&
+ ((*str = strdup(s)) != NULL));
+}
+
+static bool unmake_key_time(const MDB_val *keyv, knot_time_t *time)
+{
+ uint8_t kclass;
+ const knot_dname_t *dname;
+ const char *s;
+ return (knot_lmdb_unmake_key(keyv->mv_data, keyv->mv_size, "BNS", &kclass, &dname, &s) &&
+ str_to_u64(s, time) == KNOT_EOK);
+}
+
+static MDB_val params_serialize(const key_params_t *params)
+{
+ uint8_t flags = 0x02;
+ flags |= (params->is_ksk ? 0x01 : 0);
+ flags |= (params->is_pub_only ? 0x04 : 0);
+ flags |= (params->is_csk ? 0x08 : 0);
+
+ return knot_lmdb_make_key("LLHBBLLLLLLLLLDL", (uint64_t)params->public_key.size,
+ (uint64_t)sizeof(params->timing.revoke), params->keytag, params->algorithm, flags,
+ params->timing.created, params->timing.pre_active, params->timing.publish,
+ params->timing.ready, params->timing.active, params->timing.retire_active,
+ params->timing.retire, params->timing.post_active, params->timing.remove,
+ params->public_key.data, params->public_key.size, params->timing.revoke);
+}
+
+// this is no longer compatible with keys created by Knot 2.5.x (and unmodified since)
+static bool params_deserialize(const MDB_val *val, key_params_t *params)
+{
+ if (val->mv_size < 2 * sizeof(uint64_t)) {
+ return false;
+ }
+ uint64_t keylen = knot_wire_read_u64(val->mv_data);
+ uint64_t future = knot_wire_read_u64(val->mv_data + sizeof(keylen));
+ uint8_t flags;
+
+ if ((params->public_key.data = malloc(keylen)) == NULL) {
+ return false;
+ }
+
+ if (knot_lmdb_unmake_key(val->mv_data, val->mv_size - future, "LLHBBLLLLLLLLLD",
+ &keylen, &future, &params->keytag, &params->algorithm, &flags,
+ &params->timing.created, &params->timing.pre_active, &params->timing.publish,
+ &params->timing.ready, &params->timing.active, &params->timing.retire_active,
+ &params->timing.retire, &params->timing.post_active, &params->timing.remove,
+ params->public_key.data, (size_t)keylen)) {
+
+ params->public_key.size = keylen;
+ params->is_ksk = ((flags & 0x01) ? true : false);
+ params->is_pub_only = ((flags & 0x04) ? true : false);
+ params->is_csk = ((flags & 0x08) ? true : false);
+
+ if (future > 0) {
+ if (future < sizeof(params->timing.revoke)) {
+ free(params->public_key.data);
+ params->public_key.data = NULL;
+ return false;
+ }
+ // 'revoked' timer is part of 'future' section since it was added later
+ params->timing.revoke = knot_wire_read_u64(val->mv_data + val->mv_size - future);
+ }
+
+ if ((flags & 0x02) && (params->is_ksk || !params->is_csk)) {
+ return true;
+ }
+ }
+ free(params->public_key.data);
+ params->public_key.data = NULL;
+ return false;
+}
+
+static key_params_t *txn2params(knot_lmdb_txn_t *txn)
+{
+ key_params_t *p = calloc(1, sizeof(*p));
+ if (p == NULL) {
+ txn->ret = KNOT_ENOMEM;
+ } else {
+ if (!params_deserialize(&txn->cur_val, p) ||
+ !unmake_key_str(&txn->cur_key, &p->id)) {
+ txn->ret = KNOT_EMALF;
+ free(p);
+ p = NULL;
+ }
+ }
+ return p;
+}
+
+int kasp_db_list_keys(knot_lmdb_db_t *db, const knot_dname_t *zone_name, list_t *dst)
+{
+ init_list(dst);
+ knot_lmdb_txn_t txn = { 0 };
+ MDB_val prefix = make_key_str(KASPDBKEY_PARAMS, zone_name, NULL);
+ knot_lmdb_begin(db, &txn, false);
+ knot_lmdb_foreach(&txn, &prefix) {
+ key_params_t *p = txn2params(&txn);
+ if (p != NULL) {
+ ptrlist_add(dst, p, NULL);
+ }
+ }
+ knot_lmdb_abort(&txn);
+ free(prefix.mv_data);
+ if (txn.ret != KNOT_EOK) {
+ ptrlist_deep_free(dst, NULL);
+ return txn.ret;
+ }
+ return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK);
+}
+
+int kasp_db_get_key_algorithm(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ const char *key_id)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ MDB_val search = make_key_str(KASPDBKEY_PARAMS, zone_name, key_id);
+ knot_lmdb_begin(db, &txn, false);
+ int ret = txn.ret == KNOT_EOK ? KNOT_ENOENT : txn.ret;
+ if (knot_lmdb_find(&txn, &search, KNOT_LMDB_EXACT)) {
+ key_params_t p = { 0 };
+ ret = params_deserialize(&txn.cur_val, &p) ? p.algorithm : KNOT_EMALF;
+ free(p.public_key.data);
+ }
+ knot_lmdb_abort(&txn);
+ free(search.mv_data);
+ return ret;
+}
+
+static bool keyid_inuse(knot_lmdb_txn_t *txn, const char *key_id, key_params_t **params)
+{
+ uint8_t pf = KASPDBKEY_PARAMS;
+ MDB_val prefix = { sizeof(pf), &pf };
+ knot_lmdb_foreach(txn, &prefix) {
+ char *found_id = NULL;
+ if (unmake_key_str(&txn->cur_key, &found_id) &&
+ strcmp(found_id, key_id) == 0) {
+ if (params != NULL) {
+ *params = txn2params(txn);
+ }
+ free(found_id);
+ return true;
+ }
+ free(found_id);
+ }
+ return false;
+}
+
+
+int kasp_db_delete_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used)
+{
+ MDB_val search = make_key_str(KASPDBKEY_PARAMS, zone_name, key_id);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_del_prefix(&txn, &search);
+ if (still_used != NULL) {
+ *still_used = keyid_inuse(&txn, key_id, NULL);
+ }
+ knot_lmdb_commit(&txn);
+ free(search.mv_data);
+ return txn.ret;
+}
+
+
+int kasp_db_delete_all(knot_lmdb_db_t *db, const knot_dname_t *zone)
+{
+ MDB_val prefix = make_key_str(KASPDBKEY_PARAMS, zone, NULL);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ for (size_t i = 0; i < zone_related_classes_size && prefix.mv_data != NULL; i++) {
+ *(uint8_t *)prefix.mv_data = zone_related_classes[i];
+ knot_lmdb_del_prefix(&txn, &prefix);
+ }
+ knot_lmdb_commit(&txn);
+ free(prefix.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_sweep(knot_lmdb_db_t *db, sweep_cb keep_zone, void *cb_data)
+{
+ if (knot_lmdb_exists(db) == KNOT_ENODB) {
+ return KNOT_EOK;
+ }
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_forwhole(&txn) {
+ if (is_zone_related(&txn.cur_key) &&
+ !keep_zone((const knot_dname_t *)txn.cur_key.mv_data + 1, cb_data)) {
+ knot_lmdb_del_cur(&txn);
+ }
+ }
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int kasp_db_list_zones(knot_lmdb_db_t *db, list_t *zones)
+{
+ if (knot_lmdb_exists(db) == KNOT_ENODB) {
+ return KNOT_EOK;
+ }
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+
+ uint8_t prefix_data = KASPDBKEY_PARAMS;
+ MDB_val prefix = { sizeof(prefix_data), &prefix_data };
+ knot_lmdb_foreach(&txn, &prefix) {
+ const knot_dname_t *found = txn.cur_key.mv_data + sizeof(prefix_data);
+ if (!knot_dname_is_equal(found, ((ptrnode_t *)TAIL(*zones))->d)) {
+ knot_dname_t *copy = knot_dname_copy(found, NULL);
+ if (copy == NULL || ptrlist_add(zones, copy, NULL) == NULL) {
+ free(copy);
+ ptrlist_deep_free(zones, NULL);
+ return KNOT_ENOMEM;
+ }
+ }
+ }
+ knot_lmdb_abort(&txn);
+ if (txn.ret != KNOT_EOK) {
+ ptrlist_deep_free(zones, NULL);
+ }
+ return txn.ret;
+}
+
+int kasp_db_add_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const key_params_t *params)
+{
+ MDB_val v = params_serialize(params);
+ MDB_val k = make_key_str(KASPDBKEY_PARAMS, zone_name, params->id);
+ return knot_lmdb_quick_insert(db, k, v);
+}
+
+int kasp_db_share_key(knot_lmdb_db_t *db, const knot_dname_t *zone_from,
+ const knot_dname_t *zone_to, const char *key_id)
+{
+ MDB_val from = make_key_str(KASPDBKEY_PARAMS, zone_from, key_id);
+ MDB_val to = make_key_str(KASPDBKEY_PARAMS, zone_to, key_id);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ if (knot_lmdb_find(&txn, &from, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ knot_lmdb_insert(&txn, &to, &txn.cur_val);
+ }
+ knot_lmdb_commit(&txn);
+ free(from.mv_data);
+ free(to.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_store_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ const dnssec_binary_t *nsec3salt, knot_time_t salt_created)
+{
+ MDB_val key = make_key_str(KASPDBKEY_NSEC3SALT, zone_name, NULL);
+ MDB_val val1 = { nsec3salt->size, nsec3salt->data };
+ uint64_t tmp = htobe64(salt_created);
+ MDB_val val2 = { sizeof(tmp), &tmp };
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_insert(&txn, &key, &val1);
+ if (key.mv_data != NULL) {
+ *(uint8_t *)key.mv_data = KASPDBKEY_NSEC3TIME;
+ }
+ knot_lmdb_insert(&txn, &key, &val2);
+ knot_lmdb_commit(&txn);
+ free(key.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_load_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ dnssec_binary_t *nsec3salt, knot_time_t *salt_created)
+{
+ MDB_val key = make_key_str(KASPDBKEY_NSEC3SALT, zone_name, NULL);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ if (nsec3salt != NULL) {
+ memset(nsec3salt, 0, sizeof(*nsec3salt));
+ if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ nsec3salt->size = txn.cur_val.mv_size;
+ nsec3salt->data = malloc(txn.cur_val.mv_size + 1); // +1 because it can be zero
+ if (nsec3salt->data == NULL) {
+ txn.ret = KNOT_ENOMEM;
+ } else {
+ memcpy(nsec3salt->data, txn.cur_val.mv_data, txn.cur_val.mv_size);
+ }
+ }
+ }
+ *(uint8_t *)key.mv_data = KASPDBKEY_NSEC3TIME;
+ if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ knot_lmdb_unmake_curval(&txn, "L", salt_created);
+ }
+ knot_lmdb_abort(&txn);
+ free(key.mv_data);
+ if (txn.ret != KNOT_EOK && nsec3salt != NULL) {
+ free(nsec3salt->data);
+ }
+ return txn.ret;
+}
+
+int kasp_db_store_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ kaspdb_serial_t serial_type, uint32_t serial)
+{
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ MDB_val k = make_key_str((keyclass_t)serial_type, zone_name, NULL);
+ MDB_val v = knot_lmdb_make_key("I", serial);
+ return knot_lmdb_quick_insert(db, k, v);
+}
+
+int kasp_db_load_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ kaspdb_serial_t serial_type, uint32_t *serial)
+{
+ if (knot_lmdb_exists(db) == KNOT_ENODB) {
+ return KNOT_ENOENT;
+ }
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ MDB_val k = make_key_str((keyclass_t)serial_type, zone_name, NULL);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ knot_lmdb_unmake_curval(&txn, "I", serial);
+ }
+ knot_lmdb_abort(&txn);
+ free(k.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_get_policy_last(knot_lmdb_db_t *db, const char *policy_string,
+ knot_dname_t **lp_zone, char **lp_keyid)
+{
+ MDB_val k = make_key_str(KASPDBKEY_POLICYLAST, NULL, policy_string);
+ uint8_t kclass = 0;
+ knot_lmdb_txn_t txn = { 0 };
+ *lp_zone = NULL;
+ *lp_keyid = NULL;
+ knot_lmdb_begin(db, &txn, false);
+ if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE) &&
+ knot_lmdb_unmake_curval(&txn, "BNS", &kclass, lp_zone, lp_keyid)) {
+ assert(*lp_zone != NULL && *lp_keyid != NULL);
+ *lp_zone = knot_dname_copy(*lp_zone, NULL);
+ *lp_keyid = strdup(*lp_keyid);
+ if (kclass != KASPDBKEY_PARAMS) {
+ txn.ret = KNOT_EMALF;
+ } else if (*lp_keyid == NULL || *lp_zone == NULL) {
+ txn.ret = KNOT_ENOMEM;
+ } else {
+ // check that the referenced key really exists
+ knot_lmdb_find(&txn, &txn.cur_val, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE);
+ }
+ }
+ knot_lmdb_abort(&txn);
+ free(k.mv_data);
+
+ return txn.ret;
+}
+
+int kasp_db_set_policy_last(knot_lmdb_db_t *db, const char *policy_string, const char *last_lp_keyid,
+ const knot_dname_t *new_lp_zone, const char *new_lp_keyid)
+{
+ MDB_val k = make_key_str(KASPDBKEY_POLICYLAST, NULL, policy_string);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT)) {
+ // check that the last_lp_keyid matches
+ uint8_t unuse1, *unuse2;
+ const char *real_last_keyid;
+ if (knot_lmdb_unmake_curval(&txn, "BNS", &unuse1, &unuse2, &real_last_keyid) &&
+ (last_lp_keyid == NULL || strcmp(last_lp_keyid, real_last_keyid) != 0)) {
+ txn.ret = KNOT_ESEMCHECK;
+ }
+ }
+ MDB_val v = make_key_str(KASPDBKEY_PARAMS, new_lp_zone, new_lp_keyid);
+ knot_lmdb_insert(&txn, &k, &v);
+ free(k.mv_data);
+ free(v.mv_data);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int kasp_db_store_offline_records(knot_lmdb_db_t *db, knot_time_t for_time, const key_records_t *r)
+{
+ MDB_val k = make_key_time(KASPDBKEY_OFFLINE_RECORDS, r->rrsig.owner, for_time);
+ MDB_val v = { key_records_serialized_size(r), NULL };
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ if (knot_lmdb_insert(&txn, &k, &v)) {
+ wire_ctx_t wire = wire_ctx_init(v.mv_data, v.mv_size);
+ txn.ret = key_records_serialize(&wire, r);
+ }
+ knot_lmdb_commit(&txn);
+ free(k.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_load_offline_records(knot_lmdb_db_t *db, const knot_dname_t *for_dname,
+ knot_time_t *for_time, knot_time_t *next_time,
+ key_records_t *r)
+{
+ MDB_val prefix = make_key_str(KASPDBKEY_OFFLINE_RECORDS, for_dname, NULL);
+ if (prefix.mv_data == NULL) {
+ return KNOT_ENOMEM;
+ }
+ unsigned operator = KNOT_LMDB_GEQ;
+ MDB_val search = prefix;
+ bool zero_for_time = (*for_time == 0);
+ if (!zero_for_time) {
+ operator = KNOT_LMDB_LEQ;
+ search = make_key_time(KASPDBKEY_OFFLINE_RECORDS, for_dname, *for_time);
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ if (knot_lmdb_find(&txn, &search, operator) &&
+ knot_lmdb_is_prefix_of(&prefix, &txn.cur_key)) {
+ wire_ctx_t wire = wire_ctx_init(txn.cur_val.mv_data, txn.cur_val.mv_size);
+ txn.ret = key_records_deserialize(&wire, r);
+ if (zero_for_time) {
+ unmake_key_time(&txn.cur_key, for_time);
+ }
+ if (!knot_lmdb_next(&txn) || !knot_lmdb_is_prefix_of(&prefix, &txn.cur_key) ||
+ !unmake_key_time(&txn.cur_key, next_time)) {
+ *next_time = 0;
+ }
+ } else if (txn.ret == KNOT_EOK) {
+ txn.ret = KNOT_ENOENT;
+ }
+ knot_lmdb_abort(&txn);
+ if (!zero_for_time) {
+ free(search.mv_data);
+ }
+ free(prefix.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_delete_offline_records(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ knot_time_t from_time, knot_time_t to_time)
+{
+ MDB_val prefix = make_key_str(KASPDBKEY_OFFLINE_RECORDS, zone, NULL);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_foreach(&txn, &prefix) {
+ knot_time_t found;
+ if (unmake_key_time(&txn.cur_key, &found) &&
+ knot_time_cmp(found, from_time) >= 0 &&
+ knot_time_cmp(found, to_time) <= 0) {
+ knot_lmdb_del_cur(&txn);
+ }
+ }
+ knot_lmdb_commit(&txn);
+ free(prefix.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_get_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ uint32_t *max_ttl, uint32_t *key_ttl)
+{
+ MDB_val key = make_key_str(KASPDBKEY_SAVED_TTLS, zone, NULL);
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ if (knot_lmdb_find(&txn, &key, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ knot_lmdb_unmake_curval(&txn, "II", max_ttl, key_ttl);
+ }
+ knot_lmdb_abort(&txn);
+ free(key.mv_data);
+ return txn.ret;
+}
+
+int kasp_db_set_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ uint32_t max_ttl, uint32_t key_ttl)
+{
+ MDB_val key = make_key_str(KASPDBKEY_SAVED_TTLS, zone, NULL);
+ MDB_val val = knot_lmdb_make_key("II", max_ttl, key_ttl);
+ return knot_lmdb_quick_insert(db, key, val);
+}
+
+void kasp_db_ensure_init(knot_lmdb_db_t *db, conf_t *conf)
+{
+ if (db->path == NULL) {
+ char *kasp_dir = conf_db(conf, C_KASP_DB);
+ conf_val_t kasp_size = conf_db_param(conf, C_KASP_DB_MAX_SIZE);
+ knot_lmdb_init(db, kasp_dir, conf_int(&kasp_size), 0, "keys_db");
+ free(kasp_dir);
+ assert(db->path != NULL);
+ }
+}
+
+int kasp_db_backup(const knot_dname_t *zone, knot_lmdb_db_t *db, knot_lmdb_db_t *backup_db)
+{
+ size_t n_prefs = zone_related_classes_size + 1; // NOTE: this and following must match number of record types
+ MDB_val prefixes[n_prefs];
+ prefixes[0] = knot_lmdb_make_key("B", KASPDBKEY_POLICYLAST); // we copy all policy-last records, that doesn't harm
+ for (size_t i = 1; i < n_prefs; i++) {
+ prefixes[i] = make_key_str(zone_related_classes[i - 1], zone, NULL);
+ }
+
+ int ret = knot_lmdb_copy_prefixes(db, backup_db, prefixes, n_prefs);
+
+ for (int i = 0; i < n_prefs; i++) {
+ free(prefixes[i].mv_data);
+ }
+ return ret;
+}
diff --git a/src/knot/dnssec/kasp/kasp_db.h b/src/knot/dnssec/kasp/kasp_db.h
new file mode 100644
index 0000000..e9eea4f
--- /dev/null
+++ b/src/knot/dnssec/kasp/kasp_db.h
@@ -0,0 +1,296 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <time.h>
+
+#include "contrib/time.h"
+#include "contrib/ucw/lists.h"
+#include "libknot/db/db_lmdb.h"
+#include "libknot/dname.h"
+#include "knot/dnssec/kasp/policy.h"
+#include "knot/journal/knot_lmdb.h"
+
+typedef struct kasp_db kasp_db_t;
+
+typedef enum { // the enum values MUST match those from keyclass_t !!
+ KASPDB_SERIAL_MASTER = 0x5,
+ KASPDB_SERIAL_LASTSIGNED = 0x6,
+} kaspdb_serial_t;
+
+/*!
+ * \brief For given zone, list all keys (their IDs) belonging to it.
+ *
+ * \param db KASP db
+ * \param zone_name name of the zone in question
+ * \param dst output if KNOT_EOK: ptrlist of keys' params
+ *
+ * \return KNOT_E* (KNOT_ENOENT if no keys)
+ */
+int kasp_db_list_keys(knot_lmdb_db_t *db, const knot_dname_t *zone_name, list_t *dst);
+
+/*!
+ * \brief Obtain the algorithm of a key.
+ *
+ * \param db KASP db.
+ * \param zone_name name of the zone
+ * \param key_id ID of the key in question
+ *
+ * \retval KNOT_E* if error
+ * \return >0 The algorithm of the key.
+ */
+int kasp_db_get_key_algorithm(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ const char *key_id);
+
+/*!
+ * \brief Remove a key from zone. Delete the key if no zone has it anymore.
+ *
+ * \param db KASP db
+ * \param zone_name zone to be removed from
+ * \param key_id ID of key to be removed
+ * \param still_used output if KNOT_EOK: is the key still in use by other zones?
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_delete_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used);
+
+/*!
+ * \brief Remove all zone's keys from DB, including nsec3param
+ *
+ * \param db KASP db
+ * \param zone_name zone to be removed
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_delete_all(knot_lmdb_db_t *db, const knot_dname_t *zone_name);
+
+/*!
+ * \brief Selectively delete zones from the database.
+ *
+ * \param db KASP database.
+ * \param keep_zone Filtering callback.
+ * \param cb_data Data passed to callback function.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_sweep(knot_lmdb_db_t *db, sweep_cb keep_zone, void *cb_data);
+
+/*!
+ * \brief List all zones that have at least one key in KASP db.
+ *
+ * \param db KASP database.
+ * \param zones Output: ptrlist with zone names.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_list_zones(knot_lmdb_db_t *db, list_t *zones);
+
+/*!
+ * \brief Add a key to the DB (possibly overwrite) and link it to a zone.
+ *
+ * Stores new key with given params into KASP db. If a key with the same ID had been present
+ * in KASP db already, its params get silently overwritten by those new params.
+ * Moreover, the key ID is linked to the zone.
+ *
+ * \param db KASP db
+ * \param zone_name name of the zone the new key shall belong to
+ * \param params key params, incl. ID
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_add_key(knot_lmdb_db_t *db, const knot_dname_t *zone_name, const key_params_t *params);
+
+/*!
+ * \brief Link a key from another zone.
+ *
+ * \param db KASP db
+ * \param zone_from name of the zone the key belongs to
+ * \param zone_to name of the zone the key shall belong to as well
+ * \param key_id ID of the key in question
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_share_key(knot_lmdb_db_t *db, const knot_dname_t *zone_from,
+ const knot_dname_t *zone_to, const char *key_id);
+
+/*!
+ * \brief Store NSEC3 salt for given zone (possibly overwrites old salt).
+ *
+ * \param db KASP db
+ * \param zone_name zone name
+ * \param nsec3salt new NSEC3 salt
+ * \param salt_created timestamp when the salt was created
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_store_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ const dnssec_binary_t *nsec3salt, knot_time_t salt_created);
+
+/*!
+ * \brief Load NSEC3 salt for given zone.
+ *
+ * \param db KASP db
+ * \param zone_name zone name
+ * \param nsec3salt output if KNOT_EOK: the zone's NSEC3 salt
+ * \param salt_created output if KNOT_EOK: timestamp when the salt was created
+ *
+ * \return KNOT_E* (KNOT_ENOENT if not stored before)
+ */
+int kasp_db_load_nsec3salt(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ dnssec_binary_t *nsec3salt, knot_time_t *salt_created);
+
+/*!
+ * \brief Store SOA serial number of master or last signed serial.
+ *
+ * \param db KASP db
+ * \param zone_name zone name
+ * \param serial_type kind of serial to be stored
+ * \param serial new serial to be stored
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_store_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ kaspdb_serial_t serial_type, uint32_t serial);
+
+/*!
+ * \brief Load saved SOA serial number of master or last signed serial.
+ *
+ * \param db KASP db
+ * \param zone_name zone name
+ * \param serial_type kind of serial to be loaded
+ * \param serial output if KNOT_EOK: desired serial number
+ *
+ * \return KNOT_E* (KNOT_ENOENT if not stored before)
+ */
+int kasp_db_load_serial(knot_lmdb_db_t *db, const knot_dname_t *zone_name,
+ kaspdb_serial_t serial_type, uint32_t *serial);
+
+/*!
+ * \brief For given policy name, obtain last generated key.
+ *
+ * \param db KASP db
+ * \param policy_string a name identifying the signing policy with shared keys
+ * \param lp_zone out: the zone owning the last generated key
+ * \param lp_keyid out: the ID of the last generated key
+ *
+ * \note lp_zone and lp_keyid must be freed even when an error is returned
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_get_policy_last(knot_lmdb_db_t *db, const char *policy_string,
+ knot_dname_t **lp_zone, char **lp_keyid);
+
+/*!
+ * \brief For given policy name, try to reset last generated key.
+ *
+ * \param db KASP db
+ * \param policy_string a name identifying the signing policy with shared keys
+ * \param last_lp_keyid just for check: ID of the key the caller thinks is the policy-last
+ * \param new_lp_zone zone name of the new policy-last key
+ * \param new_lp_keyid ID of the new policy-last key
+ *
+ * \retval KNOT_ESEMCHECK lasp_lp_keyid does not correspond to real last key. Probably another zone
+ * changed policy-last key in the meantime. Re-run kasp_db_get_policy_last()
+ * \retval KNOT_EOK policy-last key set up successfully to given zone/ID
+ * \return KNOT_E* common error
+ */
+int kasp_db_set_policy_last(knot_lmdb_db_t *db, const char *policy_string, const char *last_lp_keyid,
+ const knot_dname_t *new_lp_zone, const char *new_lp_keyid);
+
+/*!
+ * \brief Store pre-generated records for offline KSK usage.
+ *
+ * \param db KASP db.
+ * \param for_time Timestamp in future in which the RRSIG shall be used.
+ * \param r Records to be stored.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_store_offline_records(knot_lmdb_db_t *db, knot_time_t for_time, const key_records_t *r);
+
+/*!
+ * \brief Load pregenerated records for offline signing.
+ *
+ * \param db KASP db.
+ * \param for_dname Name of the related zone.
+ * \param for_time Now. Closest RRSIG (timestamp equals or is closest lower).
+ * If zero, the first record is returned and its time is stored.
+ * \param next_time Out: timestamp of next saved RRSIG (for easy "iteration").
+ * \param r Out: offline records.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_load_offline_records(knot_lmdb_db_t *db, const knot_dname_t *for_dname,
+ knot_time_t *for_time, knot_time_t *next_time,
+ key_records_t *r);
+
+/*!
+ * \brief Delete pregenerated records for specified time interval.
+ *
+ * \param db KASP db.
+ * \param zone Zone in question.
+ * \param from_time Lower bound of the time interval (0 = infinity).
+ * \param to_time Upper bound of the time interval (0 = infinity).
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_delete_offline_records(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ knot_time_t from_time, knot_time_t to_time);
+
+/*!
+ * \brief Load saved zone-max-TTL and DNSKEY-TTL.
+ *
+ * \param db KASP db.
+ * \param max_ttl Out: saved zone max TTL.
+ * \param key_ttl Out: saved DNSKEY TTL.
+ *
+ * \retval KNOT_ENOENT If not saved yet.
+ * \return KNOT_E*
+ */
+int kasp_db_get_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ uint32_t *max_ttl, uint32_t *key_ttl);
+
+/*!
+ * \brief Save current zone-max-TTL and DNSKEY-TTL.
+ *
+ * \param db KASP db.
+ * \param max_ttl Current zone max TTL.
+ * \param key_ttl Current DNSKEY TTL.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_set_saved_ttls(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ uint32_t max_ttl, uint32_t key_ttl);
+
+/*!
+ * \brief Initialize KASP database according to conf, if not already.
+ *
+ * \param db KASP DB to be initialized.
+ * \param conf COnfiguration to take options from.
+ */
+void kasp_db_ensure_init(knot_lmdb_db_t *db, conf_t *conf);
+
+/*!
+ * \brief Backup KASP DB for one zone with keys and all metadata to backup location.
+ *
+ * \param zone Name of the zone to be backed up.
+ * \param db DB to backup from.
+ * \param backup_db DB to backup to.
+ *
+ * \return KNOT_E*
+ */
+int kasp_db_backup(const knot_dname_t *zone, knot_lmdb_db_t *db, knot_lmdb_db_t *backup_db);
diff --git a/src/knot/dnssec/kasp/kasp_zone.c b/src/knot/dnssec/kasp/kasp_zone.c
new file mode 100644
index 0000000..58925fa
--- /dev/null
+++ b/src/knot/dnssec/kasp/kasp_zone.c
@@ -0,0 +1,447 @@
+/* Copyright (C) 2022 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 "knot/dnssec/kasp/kasp_zone.h"
+#include "knot/dnssec/kasp/keystore.h"
+#include "knot/dnssec/zone-keys.h"
+#include "libdnssec/binary.h"
+
+// FIXME DNSSEC errors versus knot errors
+
+/*!
+ * Check if key parameters allow to create a key.
+ */
+static int key_params_check(key_params_t *params)
+{
+ assert(params);
+
+ if (params->algorithm == 0) {
+ return KNOT_INVALID_KEY_ALGORITHM;
+ }
+
+ if (params->public_key.size == 0) {
+ return KNOT_NO_PUBLIC_KEY;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Determine presence of SEP bit by trial-end-error using known keytag. */
+static int dnskey_guess_flags(dnssec_key_t *key, uint16_t keytag)
+{
+ dnssec_key_set_flags(key, DNSKEY_FLAGS_KSK);
+ if (dnssec_key_get_keytag(key) == keytag) {
+ return KNOT_EOK;
+ }
+
+ dnssec_key_set_flags(key, DNSKEY_FLAGS_ZSK);
+ if (dnssec_key_get_keytag(key) == keytag) {
+ return KNOT_EOK;
+ }
+
+ dnssec_key_set_flags(key, DNSKEY_FLAGS_REVOKED);
+ if (dnssec_key_get_keytag(key) == keytag) {
+ return KNOT_EOK;
+ }
+
+ return KNOT_EMALF;
+}
+
+static int params2dnskey(const knot_dname_t *dname, key_params_t *params,
+ dnssec_key_t **key_ptr)
+{
+ assert(dname);
+ assert(params);
+ assert(key_ptr);
+
+ int ret = key_params_check(params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dnssec_key_t *key = NULL;
+ ret = dnssec_key_new(&key);
+ if (ret != KNOT_EOK) {
+ return knot_error_from_libdnssec(ret);
+ }
+
+ ret = dnssec_key_set_dname(key, dname);
+ if (ret != KNOT_EOK) {
+ dnssec_key_free(key);
+ return knot_error_from_libdnssec(ret);
+ }
+
+ dnssec_key_set_algorithm(key, params->algorithm);
+
+ ret = dnssec_key_set_pubkey(key, &params->public_key);
+ if (ret != KNOT_EOK) {
+ dnssec_key_free(key);
+ return knot_error_from_libdnssec(ret);
+ }
+
+ ret = dnskey_guess_flags(key, params->keytag);
+ if (ret != KNOT_EOK) {
+ dnssec_key_free(key);
+ return ret;
+ }
+
+ *key_ptr = key;
+
+ return KNOT_EOK;
+}
+
+static int params2kaspkey(const knot_dname_t *dname, key_params_t *params,
+ knot_kasp_key_t *key)
+{
+ assert(dname != NULL);
+ assert(params != NULL);
+ assert(key != NULL);
+
+ int ret = params2dnskey(dname, params, &key->key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ key->id = strdup(params->id);
+ if (key->id == NULL) {
+ dnssec_key_free(key->key);
+ return KNOT_ENOMEM;
+ }
+
+ key->timing = params->timing;
+ key->is_pub_only = params->is_pub_only;
+ assert(params->is_ksk || !params->is_csk);
+ key->is_ksk = params->is_ksk;
+ key->is_zsk = (params->is_csk || !params->is_ksk);
+ return KNOT_EOK;
+}
+
+static void kaspkey2params(knot_kasp_key_t *key, key_params_t *params)
+{
+ assert(key);
+ assert(params);
+
+ params->id = key->id;
+ params->keytag = dnssec_key_get_keytag(key->key);
+ dnssec_key_get_pubkey(key->key, &params->public_key);
+ params->algorithm = dnssec_key_get_algorithm(key->key);
+ params->is_ksk = key->is_ksk;
+ params->is_csk = (key->is_ksk && key->is_zsk);
+ params->timing = key->timing;
+ params->is_pub_only = key->is_pub_only;
+}
+
+static void detect_keytag_conflict(knot_kasp_zone_t *zone, bool *kt_cfl)
+{
+ *kt_cfl = false;
+ if (zone->num_keys == 0) {
+ return;
+ }
+ uint16_t keytags[zone->num_keys];
+ for (size_t i = 0; i < zone->num_keys; i++) {
+ keytags[i] = dnssec_key_get_keytag(zone->keys[i].key);
+ for (size_t j = 0; j < i; j++) {
+ if (keytags[j] == keytags[i]) {
+ *kt_cfl = true;
+ return;
+ }
+ }
+ }
+}
+
+int kasp_zone_load(knot_kasp_zone_t *zone,
+ const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kdb,
+ bool *kt_cfl)
+{
+ if (zone == NULL || zone_name == NULL || kdb == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_kasp_key_t *dkeys = NULL;
+ size_t num_dkeys = 0;
+ dnssec_binary_t salt = { 0 };
+ knot_time_t sc = 0;
+
+ list_t key_params;
+ init_list(&key_params);
+ int ret = kasp_db_list_keys(kdb, zone_name, &key_params);
+ if (ret == KNOT_ENOENT) {
+ zone->keys = NULL;
+ zone->num_keys = 0;
+ ret = KNOT_EOK;
+ goto kzl_salt;
+ } else if (ret != KNOT_EOK) {
+ goto kzl_end;
+ }
+
+ num_dkeys = list_size(&key_params);
+ dkeys = calloc(num_dkeys, sizeof(*dkeys));
+ if (dkeys == NULL) {
+ goto kzl_end;
+ }
+
+ ptrnode_t *n;
+ int i = 0;
+ WALK_LIST(n, key_params) {
+ key_params_t *parm = n->d;
+ ret = params2kaspkey(zone_name, parm, &dkeys[i++]);
+ free_key_params(parm);
+ if (ret != KNOT_EOK) {
+ goto kzl_end;
+ }
+ }
+
+kzl_salt:
+ (void)kasp_db_load_nsec3salt(kdb, zone_name, &salt, &sc);
+ // if error, salt was probably not present, no problem to have zero ?
+
+ zone->dname = knot_dname_copy(zone_name, NULL);
+ if (zone->dname == NULL) {
+ ret = KNOT_ENOMEM;
+ goto kzl_end;
+ }
+ zone->keys = dkeys;
+ zone->num_keys = num_dkeys;
+ zone->nsec3_salt = salt;
+ zone->nsec3_salt_created = sc;
+
+ detect_keytag_conflict(zone, kt_cfl);
+
+kzl_end:
+ ptrlist_deep_free(&key_params, NULL);
+ if (ret != KNOT_EOK) {
+ free(dkeys);
+ }
+ return ret;
+}
+
+int kasp_zone_append(knot_kasp_zone_t *zone, const knot_kasp_key_t *appkey)
+{
+ if (zone == NULL || appkey == NULL || (zone->keys == NULL && zone->num_keys > 0)) {
+ return KNOT_EINVAL;
+ }
+
+ size_t new_num_keys = zone->num_keys + 1;
+ knot_kasp_key_t *new_keys = calloc(new_num_keys, sizeof(*new_keys));
+ if (!new_keys) {
+ return KNOT_ENOMEM;
+ }
+ if (zone->num_keys > 0) {
+ memcpy(new_keys, zone->keys, zone->num_keys * sizeof(*new_keys));
+ }
+ memcpy(&new_keys[new_num_keys - 1], appkey, sizeof(*appkey));
+ free(zone->keys);
+ zone->keys = new_keys;
+ zone->num_keys = new_num_keys;
+ return KNOT_EOK;
+}
+
+int kasp_zone_save(const knot_kasp_zone_t *zone,
+ const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kdb)
+{
+ if (zone == NULL || zone_name == NULL || kdb == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ key_params_t parm;
+ for (size_t i = 0; i < zone->num_keys; i++) {
+ kaspkey2params(&zone->keys[i], &parm);
+
+ // Force overwrite already existing key-val pairs.
+ int ret = kasp_db_add_key(kdb, zone_name, &parm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+
+ return kasp_db_store_nsec3salt(kdb, zone_name, &zone->nsec3_salt,
+ zone->nsec3_salt_created);
+}
+
+static void kasp_zone_clear_keys(knot_kasp_zone_t *zone)
+{
+ for (size_t i = 0; i < zone->num_keys; i++) {
+ dnssec_key_free(zone->keys[i].key);
+ free(zone->keys[i].id);
+ }
+ free(zone->keys);
+ zone->keys = NULL;
+ zone->num_keys = 0;
+}
+
+void kasp_zone_clear(knot_kasp_zone_t *zone)
+{
+ if (zone == NULL) {
+ return;
+ }
+ knot_dname_free(zone->dname, NULL);
+ kasp_zone_clear_keys(zone);
+ free(zone->nsec3_salt.data);
+ memset(zone, 0, sizeof(*zone));
+}
+
+void kasp_zone_free(knot_kasp_zone_t **zone)
+{
+ if (zone != NULL) {
+ kasp_zone_clear(*zone);
+ free(*zone);
+ *zone = NULL;
+ }
+}
+
+void free_key_params(key_params_t *parm)
+{
+ if (parm != NULL) {
+ free(parm->id);
+ dnssec_binary_free(&parm->public_key);
+ memset(parm, 0 , sizeof(*parm));
+ }
+}
+
+int zone_init_keystore(conf_t *conf, conf_val_t *policy_id,
+ dnssec_keystore_t **keystore, unsigned *backend, bool *key_label)
+{
+ char *zone_path = conf_db(conf, C_KASP_DB);
+ if (zone_path == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ conf_id_fix_default(policy_id);
+
+ conf_val_t keystore_id = conf_id_get(conf, C_POLICY, C_KEYSTORE, policy_id);
+ conf_id_fix_default(&keystore_id);
+
+ conf_val_t val = conf_id_get(conf, C_KEYSTORE, C_BACKEND, &keystore_id);
+ unsigned _backend = conf_opt(&val);
+
+ val = conf_id_get(conf, C_KEYSTORE, C_CONFIG, &keystore_id);
+ const char *config = conf_str(&val);
+
+ if (key_label != NULL) {
+ val = conf_id_get(conf, C_KEYSTORE, C_KEY_LABEL, &keystore_id);
+ *key_label = conf_bool(&val);
+ }
+
+ int ret = keystore_load(config, _backend, zone_path, keystore);
+
+ if (backend != NULL) {
+ *backend = _backend;
+ }
+
+ free(zone_path);
+ return ret;
+}
+
+int kasp_zone_keys_from_rr(knot_kasp_zone_t *zone,
+ const knot_rdataset_t *zone_dnskey,
+ bool policy_single_type_signing,
+ bool *keytag_conflict)
+{
+ if (zone == NULL || zone_dnskey == NULL || keytag_conflict == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ kasp_zone_clear_keys(zone);
+
+ zone->num_keys = zone_dnskey->count;
+ zone->keys = calloc(zone->num_keys, sizeof(*zone->keys));
+ if (zone->keys == NULL) {
+ zone->num_keys = 0;
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdata_t *zkey = zone_dnskey->rdata;
+ for (int i = 0; i < zone->num_keys; i++) {
+ int ret = dnssec_key_from_rdata(&zone->keys[i].key, zone->dname,
+ zkey->data, zkey->len);
+ if (ret == KNOT_EOK) {
+ ret = dnssec_key_get_keyid(zone->keys[i].key, &zone->keys[i].id);
+ }
+ if (ret != KNOT_EOK) {
+ free(zone->keys);
+ zone->keys = NULL;
+ zone->num_keys = 0;
+ return ret;
+ }
+ zone->keys[i].is_pub_only = true;
+
+ zone->keys[i].is_ksk = (knot_dnskey_flags(zkey) == DNSKEY_FLAGS_KSK);
+ zone->keys[i].is_zsk = policy_single_type_signing || !zone->keys[i].is_ksk;
+
+ zone->keys[i].timing.publish = 1;
+ zone->keys[i].timing.active = 1;
+
+ zkey = knot_rdataset_next(zkey);
+ }
+
+ detect_keytag_conflict(zone, keytag_conflict);
+ return KNOT_EOK;
+}
+
+int kasp_zone_from_contents(knot_kasp_zone_t *zone,
+ const zone_contents_t *contents,
+ bool policy_single_type_signing,
+ bool policy_nsec3,
+ uint16_t *policy_nsec3_iters,
+ bool *keytag_conflict)
+{
+ if (zone == NULL || contents == NULL || contents->apex == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(zone, 0, sizeof(*zone));
+ zone->dname = knot_dname_copy(contents->apex->owner, NULL);
+ if (zone->dname == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rdataset_t *zone_dnskey = node_rdataset(contents->apex, KNOT_RRTYPE_DNSKEY);
+ if (zone_dnskey == NULL || zone_dnskey->count < 1) {
+ free(zone->dname);
+ return KNOT_DNSSEC_ENOKEY;
+ }
+
+ int ret = kasp_zone_keys_from_rr(zone, zone_dnskey, policy_single_type_signing, keytag_conflict);
+ if (ret != KNOT_EOK) {
+ free(zone->dname);
+ return ret;
+ }
+
+ zone->nsec3_salt_created = 0;
+ if (policy_nsec3) {
+ knot_rdataset_t *zone_ns3p = node_rdataset(contents->apex, KNOT_RRTYPE_NSEC3PARAM);
+ if (zone_ns3p == NULL || zone_ns3p->count != 1) {
+ kasp_zone_clear(zone);
+ return KNOT_ENSEC3PAR;
+ }
+ zone->nsec3_salt.size = knot_nsec3param_salt_len(zone_ns3p->rdata);
+ zone->nsec3_salt.data = malloc(zone->nsec3_salt.size);
+ if (zone->nsec3_salt.data == NULL) {
+ kasp_zone_clear(zone);
+ return KNOT_ENOMEM;
+ }
+ memcpy(zone->nsec3_salt.data,
+ knot_nsec3param_salt(zone_ns3p->rdata),
+ zone->nsec3_salt.size);
+
+ *policy_nsec3_iters = knot_nsec3param_iters(zone_ns3p->rdata);
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/dnssec/kasp/kasp_zone.h b/src/knot/dnssec/kasp/kasp_zone.h
new file mode 100644
index 0000000..c4df282
--- /dev/null
+++ b/src/knot/dnssec/kasp/kasp_zone.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/dnssec/kasp/kasp_db.h"
+#include "knot/zone/contents.h"
+#include "libdnssec/keystore.h"
+
+typedef struct {
+ knot_dname_t *dname;
+
+ knot_kasp_key_t *keys;
+ size_t num_keys;
+
+ dnssec_binary_t nsec3_salt;
+ knot_time_t nsec3_salt_created;
+} knot_kasp_zone_t;
+
+int kasp_zone_load(knot_kasp_zone_t *zone,
+ const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kdb,
+ bool *kt_cfl);
+
+int kasp_zone_save(const knot_kasp_zone_t *zone,
+ const knot_dname_t *zone_name,
+ knot_lmdb_db_t *kdb);
+
+int kasp_zone_append(knot_kasp_zone_t *zone,
+ const knot_kasp_key_t *appkey);
+
+void kasp_zone_clear(knot_kasp_zone_t *zone);
+void kasp_zone_free(knot_kasp_zone_t **zone);
+
+void free_key_params(key_params_t *parm);
+
+int zone_init_keystore(conf_t *conf, conf_val_t *policy_id,
+ dnssec_keystore_t **keystore, unsigned *backend, bool *key_label);
+
+int kasp_zone_keys_from_rr(knot_kasp_zone_t *zone,
+ const knot_rdataset_t *zone_dnskey,
+ bool policy_single_type_signing,
+ bool *keytag_conflict);
+
+int kasp_zone_from_contents(knot_kasp_zone_t *zone,
+ const zone_contents_t *contents,
+ bool policy_single_type_signing,
+ bool policy_nsec3,
+ uint16_t *policy_nsec3_iters,
+ bool *keytag_conflict);
diff --git a/src/knot/dnssec/kasp/keystate.c b/src/knot/dnssec/kasp/keystate.c
new file mode 100644
index 0000000..f1eaa54
--- /dev/null
+++ b/src/knot/dnssec/kasp/keystate.c
@@ -0,0 +1,74 @@
+/* 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 "knot/dnssec/kasp/keystate.h"
+
+key_state_t get_key_state(const knot_kasp_key_t *key, knot_time_t moment)
+{
+ if (!key || moment <= 0) {
+ return DNSSEC_KEY_STATE_INVALID;
+ }
+
+ const knot_kasp_key_timing_t *t = &key->timing;
+
+ bool removed = (knot_time_cmp(t->remove, moment) <= 0);
+ bool revoked = (knot_time_cmp(t->revoke, moment) <= 0);
+ bool post_active = (knot_time_cmp(t->post_active, moment) <= 0);
+ bool retired = (knot_time_cmp(t->retire, moment) <= 0);
+ bool retire_active = (knot_time_cmp(t->retire_active, moment) <= 0);
+ bool active = (knot_time_cmp(t->active, moment) <= 0);
+ bool ready = (knot_time_cmp(t->ready, moment) <= 0);
+ bool published = (knot_time_cmp(t->publish, moment) <= 0);
+ bool pre_active = (knot_time_cmp(t->pre_active, moment) <= 0);
+ bool created = (knot_time_cmp(t->created, moment) <= 0);
+
+ if (removed) {
+ return DNSSEC_KEY_STATE_REMOVED;
+ }
+ if (revoked) {
+ return DNSSEC_KEY_STATE_REVOKED;
+ }
+ if (post_active) {
+ if (retired) {
+ return DNSSEC_KEY_STATE_INVALID;
+ } else {
+ return DNSSEC_KEY_STATE_POST_ACTIVE;
+ }
+ }
+ if (retired) {
+ return DNSSEC_KEY_STATE_RETIRED;
+ }
+ if (retire_active) {
+ return DNSSEC_KEY_STATE_RETIRE_ACTIVE;
+ }
+ if (active) {
+ return DNSSEC_KEY_STATE_ACTIVE;
+ }
+ if (ready) {
+ return DNSSEC_KEY_STATE_READY;
+ }
+ if (published) {
+ return DNSSEC_KEY_STATE_PUBLISHED;
+ }
+ if (pre_active) {
+ return DNSSEC_KEY_STATE_PRE_ACTIVE;
+ }
+ if (created) {
+ // don't care
+ }
+
+ return DNSSEC_KEY_STATE_INVALID;
+}
diff --git a/src/knot/dnssec/kasp/keystate.h b/src/knot/dnssec/kasp/keystate.h
new file mode 100644
index 0000000..6b7d398
--- /dev/null
+++ b/src/knot/dnssec/kasp/keystate.h
@@ -0,0 +1,35 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include "contrib/time.h"
+#include "knot/dnssec/kasp/policy.h"
+
+typedef enum {
+ DNSSEC_KEY_STATE_INVALID = 0,
+ DNSSEC_KEY_STATE_PRE_ACTIVE,
+ DNSSEC_KEY_STATE_PUBLISHED,
+ DNSSEC_KEY_STATE_READY,
+ DNSSEC_KEY_STATE_ACTIVE,
+ DNSSEC_KEY_STATE_RETIRE_ACTIVE,
+ DNSSEC_KEY_STATE_RETIRED,
+ DNSSEC_KEY_STATE_POST_ACTIVE,
+ DNSSEC_KEY_STATE_REVOKED,
+ DNSSEC_KEY_STATE_REMOVED,
+} key_state_t;
+
+key_state_t get_key_state(const knot_kasp_key_t *key, knot_time_t moment);
diff --git a/src/knot/dnssec/kasp/keystore.c b/src/knot/dnssec/kasp/keystore.c
new file mode 100644
index 0000000..2ec5cd1
--- /dev/null
+++ b/src/knot/dnssec/kasp/keystore.c
@@ -0,0 +1,89 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libdnssec/error.h"
+#include "knot/dnssec/kasp/keystore.h"
+#include "knot/conf/schema.h"
+#include "libknot/error.h"
+
+static char *fix_path(const char *config, const char *base_path)
+{
+ assert(config);
+ assert(base_path);
+
+ char *path = NULL;
+
+ if (config[0] == '/') {
+ path = strdup(config);
+ } else {
+ if (asprintf(&path, "%s/%s", base_path, config) == -1) {
+ path = NULL;
+ }
+ }
+
+ return path;
+}
+
+int keystore_load(const char *config, unsigned backend,
+ const char *kasp_base_path, dnssec_keystore_t **keystore)
+{
+ int ret = DNSSEC_EINVAL;
+ char *fixed_config = NULL;
+
+ switch (backend) {
+ case KEYSTORE_BACKEND_PEM:
+ ret = dnssec_keystore_init_pkcs8(keystore);
+ fixed_config = fix_path(config, kasp_base_path);
+ break;
+ case KEYSTORE_BACKEND_PKCS11:
+ ret = dnssec_keystore_init_pkcs11(keystore);
+ fixed_config = strdup(config);
+ break;
+ default:
+ assert(0);
+ }
+ if (ret != DNSSEC_EOK) {
+ free(fixed_config);
+ return knot_error_from_libdnssec(ret);
+ }
+ if (fixed_config == NULL) {
+ dnssec_keystore_deinit(*keystore);
+ *keystore = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ ret = dnssec_keystore_init(*keystore, fixed_config);
+ if (ret != DNSSEC_EOK) {
+ free(fixed_config);
+ dnssec_keystore_deinit(*keystore);
+ *keystore = NULL;
+ return knot_error_from_libdnssec(ret);
+ }
+
+ ret = dnssec_keystore_open(*keystore, fixed_config);
+ free(fixed_config);
+ if (ret != DNSSEC_EOK) {
+ dnssec_keystore_deinit(*keystore);
+ *keystore = NULL;
+ return knot_error_from_libdnssec(ret);
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/dnssec/kasp/keystore.h b/src/knot/dnssec/kasp/keystore.h
new file mode 100644
index 0000000..bd62347
--- /dev/null
+++ b/src/knot/dnssec/kasp/keystore.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libdnssec/keystore.h"
+
+int keystore_load(const char *config, unsigned backend,
+ const char *kasp_base_path, dnssec_keystore_t **keystore);
diff --git a/src/knot/dnssec/kasp/policy.h b/src/knot/dnssec/kasp/policy.h
new file mode 100644
index 0000000..4354b95
--- /dev/null
+++ b/src/knot/dnssec/kasp/policy.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "contrib/time.h"
+#include "libdnssec/key.h"
+#include "knot/conf/conf.h"
+
+/*!
+ * KASP key timing information.
+ */
+typedef struct {
+ knot_time_t created; /*!< Time the key was generated/imported. */
+ knot_time_t pre_active; /*!< Signing start with new algorithm. */
+ knot_time_t publish; /*!< Time of DNSKEY record publication. */
+ knot_time_t ready; /*!< Start of RRSIG generation, waiting for parent zone. */
+ knot_time_t active; /*!< RRSIG records generating, other keys can be retired */
+ knot_time_t retire_active; /*!< Still active, but obsoleted. */
+ knot_time_t retire; /*!< End of RRSIG records generating. */
+ knot_time_t post_active; /*!< Still signing with old algorithm, not published. */
+ knot_time_t revoke; /*!< RFC 5011 state of KSK with 'revoked' flag and signed by self. */
+ knot_time_t remove; /*!< Time of DNSKEY record removal. */
+} knot_kasp_key_timing_t;
+
+/*!
+ * Key parameters as writing in zone config file.
+ */
+typedef struct {
+ char *id;
+ bool is_ksk;
+ bool is_csk;
+ bool is_pub_only;
+ uint16_t keytag;
+ uint8_t algorithm;
+ dnssec_binary_t public_key;
+ knot_kasp_key_timing_t timing;
+} key_params_t;
+
+/*!
+ * Zone key.
+ */
+typedef struct {
+ char *id; /*!< Keystore unique key ID. */
+ dnssec_key_t *key; /*!< Instance of the key. */
+ knot_kasp_key_timing_t timing; /*!< Key timing information. */
+ bool is_pub_only;
+ bool is_ksk;
+ bool is_zsk;
+} knot_kasp_key_t;
+
+/*!
+ * Parent for DS checks.
+ */
+typedef struct {
+ conf_remote_t *addr;
+ size_t addrs;
+} knot_kasp_parent_t;
+
+knot_dynarray_declare(parent, knot_kasp_parent_t, DYNARRAY_VISIBILITY_NORMAL, 3)
+
+/*!
+ * Set of DNSSEC key related records.
+ */
+typedef struct {
+ knot_rrset_t dnskey;
+ knot_rrset_t cdnskey;
+ knot_rrset_t cds;
+ knot_rrset_t rrsig;
+} key_records_t;
+
+/*!
+ * Key and signature policy.
+ */
+typedef struct {
+ bool manual;
+ char *string;
+ // DNSKEY
+ dnssec_key_algorithm_t algorithm;
+ uint16_t ksk_size;
+ uint16_t zsk_size;
+ uint32_t dnskey_ttl;
+ uint32_t zsk_lifetime; // like knot_time_t
+ uint32_t ksk_lifetime; // like knot_time_t
+ uint32_t delete_delay; // like knot_timediff_t
+ bool ksk_shared;
+ bool single_type_signing;
+ bool sts_default; // single-type-signing was set to default value
+ // RRSIG
+ bool reproducible_sign; // (EC)DSA creates reproducible signatures
+ uint32_t rrsig_lifetime; // like knot_time_t
+ uint32_t rrsig_refresh_before; // like knot_timediff_t
+ uint32_t rrsig_prerefresh; // like knot_timediff_t
+ // NSEC3
+ bool nsec3_enabled;
+ bool nsec3_opt_out;
+ int64_t nsec3_salt_lifetime; // like knot_time_t
+ uint16_t nsec3_iterations;
+ uint8_t nsec3_salt_length;
+ // zone
+ uint32_t zone_maximal_ttl; // like knot_timediff_t
+ uint32_t saved_max_ttl;
+ uint32_t saved_key_ttl;
+ // data propagation delay
+ uint32_t propagation_delay; // like knot_timediff_t
+ // various
+ uint32_t ksk_sbm_timeout; // like knot_time_t
+ uint32_t ksk_sbm_check_interval; // like knot_time_t
+ uint32_t ksk_sbm_delay;
+ unsigned cds_cdnskey_publish;
+ dnssec_key_digest_t cds_dt; // digest type for CDS
+ parent_dynarray_t parents;
+ uint16_t signing_threads;
+ bool ds_push;
+ bool offline_ksk;
+ bool incremental;
+ bool key_label;
+ unsigned unsafe;
+} knot_kasp_policy_t;
+// TODO make the time parameters knot_timediff_t ??
diff --git a/src/knot/dnssec/key-events.c b/src/knot/dnssec/key-events.c
new file mode 100644
index 0000000..170f5a9
--- /dev/null
+++ b/src/knot/dnssec/key-events.c
@@ -0,0 +1,863 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "contrib/macros.h"
+#include "knot/common/log.h"
+#include "knot/common/systemd.h"
+#include "knot/dnssec/kasp/keystate.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/policy.h"
+#include "knot/dnssec/zone-keys.h"
+
+static bool key_present(const kdnssec_ctx_t *ctx, bool ksk, bool zsk)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ const knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (key->is_ksk == ksk && key->is_zsk == zsk && !key->is_pub_only &&
+ get_key_state(key, ctx->now) != DNSSEC_KEY_STATE_REMOVED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool key_id_present(const kdnssec_ctx_t *ctx, const char *keyid, bool want_ksk)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ const knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (strcmp(keyid, key->id) == 0 &&
+ key->is_ksk == want_ksk &&
+ get_key_state(key, ctx->now) != DNSSEC_KEY_STATE_REMOVED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static unsigned algorithm_present(const kdnssec_ctx_t *ctx, uint8_t alg)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ unsigned ret = 0;
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ const knot_kasp_key_t *key = &ctx->zone->keys[i];
+ knot_time_t activated = knot_time_min(key->timing.pre_active, key->timing.ready);
+ if (knot_time_cmp(knot_time_min(activated, key->timing.active), ctx->now) <= 0 &&
+ get_key_state(key, ctx->now) != DNSSEC_KEY_STATE_REMOVED &&
+ dnssec_key_get_algorithm(key->key) == alg && !key->is_pub_only) {
+ ret++;
+ }
+ }
+ return ret;
+}
+
+static bool signing_scheme_present(const kdnssec_ctx_t *ctx)
+{
+ if (ctx->policy->single_type_signing) {
+ return (!key_present(ctx, true, false) || !key_present(ctx, false, true) || key_present(ctx, true, true));
+ } else {
+ return (key_present(ctx, true, false) && key_present(ctx, false, true));
+ }
+}
+
+static knot_kasp_key_t *key_get_by_id(kdnssec_ctx_t *ctx, const char *keyid)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (strcmp(keyid, key->id) == 0) {
+ return key;
+ }
+ }
+ return NULL;
+}
+
+static int generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags,
+ knot_time_t when_active, bool pre_active)
+{
+ assert(!pre_active || when_active == 0);
+
+ knot_kasp_key_t *key = NULL;
+ int ret = kdnssec_generate_key(ctx, flags, &key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ key->timing.remove = 0;
+ key->timing.retire = 0;
+ key->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active);
+ key->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0);
+ key->timing.publish = (pre_active ? 0 : ctx->now);
+ key->timing.pre_active = (pre_active ? ctx->now : 0);
+
+ return KNOT_EOK;
+}
+
+static int share_or_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags,
+ knot_time_t when_active, bool pre_active)
+{
+ assert(!pre_active || when_active == 0);
+
+ knot_dname_t *borrow_zone = NULL;
+ char *borrow_key = NULL;
+
+ if (!(flags & DNSKEY_GENERATE_KSK)) {
+ return KNOT_EINVAL;
+ } // for now not designed for rotating shared ZSK
+
+ int ret = kasp_db_get_policy_last(ctx->kasp_db, ctx->policy->string,
+ &borrow_zone, &borrow_key);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ free(borrow_zone);
+ free(borrow_key);
+ return ret;
+ }
+
+ // if we already have the policy-last key, we have to generate new one
+ if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, true) ||
+ kasp_db_get_key_algorithm(ctx->kasp_db, borrow_zone, borrow_key) != (int)ctx->policy->algorithm) {
+ knot_kasp_key_t *key = NULL;
+ ret = kdnssec_generate_key(ctx, flags, &key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ key->timing.remove = 0;
+ key->timing.retire = 0;
+ key->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active);
+ key->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0);
+ key->timing.publish = (pre_active ? 0 : ctx->now);
+ key->timing.pre_active = (pre_active ? ctx->now : 0);
+
+ ret = kdnssec_ctx_commit(ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = kasp_db_set_policy_last(ctx->kasp_db, ctx->policy->string,
+ borrow_key, ctx->zone->dname, key->id);
+ free(borrow_zone);
+ free(borrow_key);
+ borrow_zone = NULL;
+ borrow_key = NULL;
+ if (ret != KNOT_ESEMCHECK) {
+ // all ok, we generated new kay and updated policy-last
+ return ret;
+ } else {
+ // another zone updated policy-last key in the meantime
+ ret = kdnssec_delete_key(ctx, key);
+ if (ret == KNOT_EOK) {
+ ret = kdnssec_ctx_commit(ctx);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = kasp_db_get_policy_last(ctx->kasp_db, ctx->policy->string,
+ &borrow_zone, &borrow_key);
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ ret = kdnssec_share_key(ctx, borrow_zone, borrow_key);
+ if (ret == KNOT_EOK) {
+ knot_kasp_key_t *newkey = key_get_by_id(ctx, borrow_key);
+ assert(newkey != NULL);
+ newkey->timing.remove = 0;
+ newkey->timing.retire = 0;
+ newkey->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active);
+ newkey->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0);
+ newkey->timing.publish = (pre_active ? 0 : ctx->now);
+ newkey->timing.pre_active = (pre_active ? ctx->now : 0);
+ newkey->is_ksk = (flags & DNSKEY_GENERATE_KSK);
+ newkey->is_zsk = (flags & DNSKEY_GENERATE_ZSK);
+ }
+ }
+ free(borrow_zone);
+ free(borrow_key);
+ return ret;
+}
+
+#define GEN_KSK_FLAGS (DNSKEY_GENERATE_KSK | (ctx->policy->single_type_signing ? DNSKEY_GENERATE_ZSK : 0))
+
+static int generate_ksk(kdnssec_ctx_t *ctx, knot_time_t when_active, bool pre_active)
+{
+ if (ctx->policy->ksk_shared) {
+ return share_or_generate_key(ctx, GEN_KSK_FLAGS, when_active, pre_active);
+ } else {
+ return generate_key(ctx, GEN_KSK_FLAGS, when_active, pre_active);
+ }
+}
+
+static bool running_rollover(const kdnssec_ctx_t *ctx)
+{
+ bool res = false;
+ bool ready_ksk = false, active_ksk = false;
+
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (key->is_pub_only) {
+ continue;
+ }
+ switch (get_key_state(key, ctx->now)) {
+ case DNSSEC_KEY_STATE_PRE_ACTIVE:
+ res = true;
+ break;
+ case DNSSEC_KEY_STATE_PUBLISHED:
+ res = true;
+ break;
+ case DNSSEC_KEY_STATE_READY:
+ ready_ksk = (ready_ksk || key->is_ksk);
+ break;
+ case DNSSEC_KEY_STATE_ACTIVE:
+ active_ksk = (active_ksk || key->is_ksk);
+ break;
+ case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
+ case DNSSEC_KEY_STATE_POST_ACTIVE:
+ res = true;
+ break;
+ case DNSSEC_KEY_STATE_RETIRED:
+ case DNSSEC_KEY_STATE_REMOVED:
+ default:
+ break;
+ }
+ }
+ if (ready_ksk && active_ksk) {
+ res = true;
+ }
+ return res;
+}
+
+typedef enum {
+ INVALID = 0,
+ GENERATE = 1,
+ PUBLISH,
+ SUBMIT,
+ REPLACE,
+ RETIRE,
+ REMOVE,
+ REALLY_REMOVE,
+} roll_action_type_t;
+
+typedef struct {
+ roll_action_type_t type;
+ bool ksk;
+ knot_time_t time;
+ knot_kasp_key_t *key;
+ uint16_t ready_keytag;
+ const char *ready_keyid;
+} roll_action_t;
+
+static const char *roll_action_name(roll_action_type_t type)
+{
+ switch (type) {
+ case GENERATE: return "generate";
+ case PUBLISH: return "publish";
+ case SUBMIT: return "submit";
+ case REPLACE: return "replace";
+ case RETIRE: return "retire";
+ case REMOVE: return "remove";
+ case INVALID:
+ // FALLTHROUGH
+ default: return "invalid";
+ }
+}
+
+static knot_time_t zsk_rollover_time(knot_time_t active_time, const kdnssec_ctx_t *ctx)
+{
+ if (active_time <= 0 || ctx->policy->zsk_lifetime == 0) {
+ return 0;
+ }
+ return knot_time_plus(active_time, ctx->policy->zsk_lifetime);
+}
+
+static knot_time_t zsk_active_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
+{
+ if (publish_time <= 0) {
+ return 0;
+ }
+ return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->saved_key_ttl);
+}
+
+static knot_time_t zsk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx)
+{
+ if (retire_time <= 0) {
+ return 0;
+ }
+ return knot_time_add(retire_time, ctx->policy->propagation_delay + ctx->policy->saved_max_ttl);
+}
+
+static knot_time_t ksk_rollover_time(knot_time_t created_time, const kdnssec_ctx_t *ctx)
+{
+ if (created_time <= 0 || ctx->policy->ksk_lifetime == 0) {
+ return 0;
+ }
+ return knot_time_plus(created_time, ctx->policy->ksk_lifetime);
+}
+
+static knot_time_t ksk_ready_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx)
+{
+ if (publish_time <= 0) {
+ return 0;
+ }
+ return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->saved_key_ttl);
+}
+
+static knot_time_t ksk_sbm_max_time(knot_time_t ready_time, const kdnssec_ctx_t *ctx)
+{
+ if (ready_time <= 0 || ctx->policy->ksk_sbm_timeout == 0) {
+ return 0;
+ }
+ return knot_time_plus(ready_time, ctx->policy->ksk_sbm_timeout);
+}
+
+static knot_time_t ksk_retire_time(knot_time_t retire_active_time, const kdnssec_ctx_t *ctx)
+{
+ if (retire_active_time <= 0) {
+ return 0;
+ }
+ // this is not correct! It should be parent DS TTL.
+ return knot_time_add(retire_active_time, ctx->policy->propagation_delay + ctx->policy->saved_key_ttl);
+}
+
+static knot_time_t ksk_remove_time(knot_time_t retire_time, bool is_csk, const kdnssec_ctx_t *ctx)
+{
+ if (retire_time <= 0) {
+ return 0;
+ }
+ knot_timediff_t use_ttl = ctx->policy->saved_key_ttl;
+ if (is_csk) {
+ use_ttl = ctx->policy->saved_max_ttl;
+ }
+ return knot_time_add(retire_time, ctx->policy->propagation_delay + use_ttl);
+}
+
+static knot_time_t ksk_really_remove_time(knot_time_t remove_time, const kdnssec_ctx_t *ctx)
+{
+ if (ctx->keep_deleted_keys) {
+ return 0;
+ }
+ return knot_time_add(remove_time, ctx->policy->delete_delay);
+}
+
+static knot_time_t zsk_really_remove_time(knot_time_t remove_time, const kdnssec_ctx_t *ctx)
+{
+ if (ctx->keep_deleted_keys) {
+ return 0;
+ }
+ return knot_time_add(remove_time, ctx->policy->delete_delay);
+}
+
+// algorithm rollover related timers must be the same for KSK and ZSK
+
+static knot_time_t alg_publish_time(knot_time_t pre_active_time, const kdnssec_ctx_t *ctx)
+{
+ if (pre_active_time <= 0) {
+ return 0;
+ }
+ return knot_time_add(pre_active_time, ctx->policy->propagation_delay + ctx->policy->saved_max_ttl);
+}
+
+static knot_time_t alg_remove_time(knot_time_t post_active_time, const kdnssec_ctx_t *ctx)
+{
+ return knot_time_add(post_active_time, ctx->policy->propagation_delay + ctx->policy->saved_key_ttl);
+}
+
+static roll_action_t next_action(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags)
+{
+ roll_action_t res = { 0 };
+
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ knot_time_t keytime = 0;
+ roll_action_type_t restype = INVALID;
+ if (key->is_pub_only ||
+ (key->is_ksk && !(flags & KEY_ROLL_ALLOW_KSK_ROLL)) ||
+ (key->is_zsk && !(flags & KEY_ROLL_ALLOW_ZSK_ROLL))) {
+ continue;
+ }
+ if (key->is_ksk) {
+ switch (get_key_state(key, ctx->now)) {
+ case DNSSEC_KEY_STATE_PRE_ACTIVE:
+ keytime = alg_publish_time(key->timing.pre_active, ctx);
+ restype = PUBLISH;
+ break;
+ case DNSSEC_KEY_STATE_PUBLISHED:
+ keytime = ksk_ready_time(key->timing.publish, ctx);
+ restype = SUBMIT;
+ break;
+ case DNSSEC_KEY_STATE_READY:
+ keytime = ksk_sbm_max_time(key->timing.ready, ctx);
+ restype = REPLACE;
+ res.ready_keyid = key->id;
+ res.ready_keytag = dnssec_key_get_keytag(key->key);
+ break;
+ case DNSSEC_KEY_STATE_ACTIVE:
+ if (!running_rollover(ctx) &&
+ dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) {
+ knot_time_t ksk_created = key->timing.created == 0 ?
+ key->timing.active :
+ key->timing.created;
+ keytime = ksk_rollover_time(ksk_created, ctx);
+ restype = GENERATE;
+ }
+ break;
+ case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
+ if (key->timing.retire == 0 && key->timing.post_active == 0 && key->timing.remove == 0) { // this shouldn't normally happen
+ // when a KSK is retire_active, it has already some following timer set
+ keytime = ksk_retire_time(key->timing.retire_active, ctx);
+ restype = RETIRE;
+ }
+ break;
+ case DNSSEC_KEY_STATE_POST_ACTIVE:
+ keytime = alg_remove_time(key->timing.post_active, ctx);
+ restype = REMOVE;
+ break;
+ case DNSSEC_KEY_STATE_RETIRED:
+ keytime = knot_time_min(key->timing.retire, key->timing.remove);
+ keytime = ksk_remove_time(keytime, key->is_zsk, ctx);
+ restype = REMOVE;
+ break;
+ case DNSSEC_KEY_STATE_REMOVED:
+ keytime = ksk_really_remove_time(key->timing.remove, ctx);
+ if (knot_time_cmp(keytime, ctx->now) > 0) {
+ keytime = 0;
+ }
+ restype = REALLY_REMOVE;
+ break;
+ default:
+ continue;
+ }
+ } else {
+ switch (get_key_state(key, ctx->now)) {
+ case DNSSEC_KEY_STATE_PRE_ACTIVE:
+ keytime = alg_publish_time(key->timing.pre_active, ctx);
+ restype = PUBLISH;
+ break;
+ case DNSSEC_KEY_STATE_PUBLISHED:
+ keytime = zsk_active_time(key->timing.publish, ctx);
+ restype = REPLACE;
+ break;
+ case DNSSEC_KEY_STATE_ACTIVE:
+ if (!running_rollover(ctx) &&
+ dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) {
+ keytime = zsk_rollover_time(key->timing.active, ctx);
+ restype = GENERATE;
+ }
+ break;
+ case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
+ // simply waiting for submitted KSK to retire me.
+ break;
+ case DNSSEC_KEY_STATE_POST_ACTIVE:
+ keytime = alg_remove_time(key->timing.post_active, ctx);
+ restype = REMOVE;
+ break;
+ case DNSSEC_KEY_STATE_RETIRED:
+ keytime = knot_time_min(key->timing.retire, key->timing.remove);
+ keytime = zsk_remove_time(keytime, ctx);
+ restype = REMOVE;
+ break;
+ case DNSSEC_KEY_STATE_REMOVED:
+ keytime = zsk_really_remove_time(key->timing.remove, ctx);
+ if (knot_time_cmp(keytime, ctx->now) > 0) {
+ keytime = 0;
+ }
+ restype = REALLY_REMOVE;
+ break;
+ case DNSSEC_KEY_STATE_READY:
+ default:
+ continue;
+ }
+ }
+ if (knot_time_cmp(keytime, res.time) < 0) {
+ res.key = key;
+ res.ksk = key->is_ksk;
+ res.time = keytime;
+ res.type = restype;
+ }
+ }
+
+ return res;
+}
+
+static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
+{
+ assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
+ assert(newkey->is_ksk);
+
+ // pushing from READY into ACTIVE decreases the other key's cds_priority
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (key->is_ksk && !key->is_pub_only &&
+ get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
+ key->timing.active = ctx->now;
+ }
+ }
+
+ newkey->timing.ready = ctx->now;
+ return KNOT_EOK;
+}
+
+knot_kasp_key_t *knot_dnssec_key2retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ key_state_t keystate = get_key_state(key, ctx->now);
+ if (((newkey->is_ksk && key->is_ksk) || (!newkey->is_ksk && !key->is_ksk))
+ && (keystate == DNSSEC_KEY_STATE_ACTIVE)) {
+ return key;
+ }
+ }
+ return NULL;
+}
+
+static knot_kasp_key_t *zsk2retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *newksk)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ key_state_t keystate = get_key_state(key, ctx->now);
+ uint8_t keyalg = dnssec_key_get_algorithm(key->key);
+ bool algdiff = (keyalg != dnssec_key_get_algorithm(newksk->key));
+
+ if (key->is_zsk && !key->is_ksk &&
+ (algdiff || newksk->is_zsk) &&
+ (keystate == DNSSEC_KEY_STATE_ACTIVE ||
+ keystate == DNSSEC_KEY_STATE_RETIRE_ACTIVE)) {
+ return key;
+ }
+ }
+ return NULL;
+}
+
+static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey, uint32_t active_retire_delay)
+{
+ if (newkey->is_ksk) {
+ log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed");
+ }
+
+ knot_kasp_key_t *oldkey = knot_dnssec_key2retire(ctx, newkey), *oldzsk = NULL;
+ if (oldkey != NULL) {
+ uint8_t keyalg = dnssec_key_get_algorithm(oldkey->key);
+ bool algdiff = (keyalg != dnssec_key_get_algorithm(newkey->key));
+
+ if (algdiff) {
+ oldkey->timing.retire_active = ctx->now;
+ if (oldkey->is_ksk) {
+ oldkey->timing.post_active = ctx->now + active_retire_delay;
+ }
+ } else if (oldkey->is_ksk) {
+ oldkey->timing.retire_active = ctx->now;
+ if (oldkey->is_zsk) { // CSK
+ oldkey->timing.retire = ctx->now + active_retire_delay;
+ } else {
+ oldkey->timing.remove = ctx->now + active_retire_delay;
+ }
+ } else {
+ oldkey->timing.retire = ctx->now;
+ }
+
+ if (newkey->is_ksk && (oldzsk = zsk2retire(ctx, newkey)) != NULL) {
+ if (algdiff) {
+ oldzsk->timing.post_active = ctx->now + active_retire_delay;
+ } else {
+ oldzsk->timing.retire = ctx->now;
+ }
+ }
+ }
+
+ if (newkey->is_ksk) {
+ assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY);
+ } else {
+ assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
+ }
+ newkey->timing.active = knot_time_min(ctx->now, newkey->timing.active);
+
+ return KNOT_EOK;
+}
+
+static int exec_publish(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
+{
+ assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_PRE_ACTIVE);
+ key->timing.publish = ctx->now;
+
+ return KNOT_EOK;
+}
+
+static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
+{
+ bool alg_rollover = false;
+ knot_kasp_key_t *alg_rollover_friend = NULL;
+
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *k = &ctx->zone->keys[i];
+ int magic = (k->is_ksk && k->is_zsk ? 2 : 3); // :(
+ if (k->is_zsk && get_key_state(k, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE &&
+ algorithm_present(ctx, dnssec_key_get_algorithm(k->key)) < magic) {
+ alg_rollover = true;
+ alg_rollover_friend = k;
+ }
+ }
+
+ assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE);
+
+ if (alg_rollover) {
+ key->timing.post_active = ctx->now;
+ alg_rollover_friend->timing.post_active = ctx->now;
+ } else {
+ key->timing.retire = ctx->now;
+ }
+
+ return KNOT_EOK;
+}
+
+static int exec_remove_old_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
+{
+ assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRED ||
+ get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_POST_ACTIVE ||
+ get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_REMOVED);
+ key->timing.remove = ctx->now;
+ return KNOT_EOK;
+}
+
+static int exec_really_remove(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
+{
+ assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_REMOVED);
+ assert(!ctx->keep_deleted_keys);
+ return kdnssec_delete_key(ctx, key);
+}
+
+int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
+ zone_sign_reschedule_t *reschedule)
+{
+ if (ctx == NULL || reschedule == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (ctx->policy->manual) {
+ if ((flags & (KEY_ROLL_FORCE_KSK_ROLL | KEY_ROLL_FORCE_ZSK_ROLL))) {
+ log_zone_notice(ctx->zone->dname, "DNSSEC, ignoring forced key rollover "
+ "due to manual policy");
+ }
+ return KNOT_EOK;
+ }
+ int ret = KNOT_EOK;
+ uint16_t ready_keytag = 0;
+ const char *ready_keyid = NULL;
+ bool allowed_general_roll = ((flags & KEY_ROLL_ALLOW_KSK_ROLL) && (flags & KEY_ROLL_ALLOW_ZSK_ROLL));
+ // generate initial keys if missing
+ if (!key_present(ctx, true, false) && !key_present(ctx, true, true)) {
+ if ((flags & KEY_ROLL_ALLOW_KSK_ROLL)) {
+ if (ctx->policy->ksk_shared) {
+ ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
+ } else {
+ ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false);
+ }
+ if (ret == KNOT_EOK) {
+ reschedule->plan_ds_check = true;
+ ready_keyid = ctx->zone->keys[0].id;
+ ready_keytag = dnssec_key_get_keytag(ctx->zone->keys[0].key);
+ }
+ }
+ if (ret == KNOT_EOK && (flags & KEY_ROLL_ALLOW_ZSK_ROLL)) {
+ reschedule->keys_changed = true;
+ if (!ctx->policy->single_type_signing &&
+ !key_present(ctx, false, true)) {
+ ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, ctx->now, false);
+ }
+ }
+ }
+ // forced KSK rollover
+ if ((flags & KEY_ROLL_FORCE_KSK_ROLL) && ret == KNOT_EOK && (flags & KEY_ROLL_ALLOW_KSK_ROLL)) {
+ flags &= ~KEY_ROLL_FORCE_KSK_ROLL;
+ if (running_rollover(ctx)) {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, ignoring forced KSK rollover "
+ "due to running rollover");
+ } else {
+ ret = generate_ksk(ctx, 0, false);
+ if (ret == KNOT_EOK) {
+ reschedule->keys_changed = true;
+ log_zone_info(ctx->zone->dname, "DNSSEC, KSK rollover started");
+ }
+ }
+ }
+ // forced ZSK rollover
+ if ((flags & KEY_ROLL_FORCE_ZSK_ROLL) && ret == KNOT_EOK && (flags & KEY_ROLL_ALLOW_ZSK_ROLL)) {
+ flags &= ~KEY_ROLL_FORCE_ZSK_ROLL;
+ if (running_rollover(ctx)) {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, ignoring forced ZSK rollover "
+ "due to running rollover");
+ } else {
+ ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, false);
+ if (ret == KNOT_EOK) {
+ reschedule->keys_changed = true;
+ log_zone_info(ctx->zone->dname, "DNSSEC, ZSK rollover started");
+ }
+ }
+ }
+ // algorithm rollover
+ if (algorithm_present(ctx, ctx->policy->algorithm) == 0 &&
+ !running_rollover(ctx) && allowed_general_roll && ret == KNOT_EOK) {
+ ret = generate_ksk(ctx, 0, true);
+ if (!ctx->policy->single_type_signing && ret == KNOT_EOK) {
+ ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, true);
+ }
+ log_zone_info(ctx->zone->dname, "DNSSEC, algorithm rollover started");
+ if (ret == KNOT_EOK) {
+ reschedule->keys_changed = true;
+ }
+ }
+ // scheme rollover
+ if (!signing_scheme_present(ctx) && allowed_general_roll &&
+ !running_rollover(ctx) && ret == KNOT_EOK) {
+ ret = generate_ksk(ctx, 0, false);
+ if (!ctx->policy->single_type_signing && ret == KNOT_EOK) {
+ ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, false);
+ }
+ log_zone_info(ctx->zone->dname, "DNSSEC, signing scheme rollover started");
+ if (ret == KNOT_EOK) {
+ reschedule->keys_changed = true;
+ }
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ roll_action_t next = next_action(ctx, flags);
+
+ reschedule->next_rollover = next.time;
+
+ if (knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
+ bool log_keytag = true;
+ switch (next.type) {
+ case GENERATE:
+ if (next.ksk) {
+ ret = generate_ksk(ctx, 0, false);
+ } else {
+ ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, false);
+ }
+ if (ret == KNOT_EOK) {
+ log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started",
+ (next.ksk ? 'K' : 'Z'));
+ }
+ log_keytag = false;
+ break;
+ case PUBLISH:
+ ret = exec_publish(ctx, next.key);
+ break;
+ case SUBMIT:
+ ret = submit_key(ctx, next.key);
+ if (ret == KNOT_EOK) {
+ reschedule->plan_ds_check = true;
+ ready_keyid = next.key->id;
+ ready_keytag = dnssec_key_get_keytag(next.key->key);
+ }
+ break;
+ case REPLACE:
+ ret = exec_new_signatures(ctx, next.key, 0);
+ break;
+ case RETIRE:
+ ret = exec_ksk_retire(ctx, next.key);
+ break;
+ case REMOVE:
+ ret = exec_remove_old_key(ctx, next.key);
+ break;
+ case REALLY_REMOVE:
+ ret = exec_really_remove(ctx, next.key);
+ break;
+ default:
+ log_keytag = false;
+ ret = KNOT_EINVAL;
+ }
+
+ if (ret == KNOT_EOK) {
+ reschedule->keys_changed = true;
+ next = next_action(ctx, flags);
+ reschedule->next_rollover = next.time;
+ } else {
+ if (log_keytag) {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, key rollover, tag %5d, action %s (%s)",
+ dnssec_key_get_keytag(next.key->key),
+ roll_action_name(next.type), knot_strerror(ret));
+ } else {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, key rollover, action %s (%s)",
+ roll_action_name(next.type), knot_strerror(ret));
+ }
+ }
+ }
+
+ if (ret == KNOT_EOK && next.ready_keyid != NULL) {
+ // just to make sure DS check is scheduled
+ reschedule->plan_ds_check = true;
+ ready_keyid = next.ready_keyid;
+ ready_keytag = next.ready_keytag;
+ }
+
+ if (ret == KNOT_EOK && knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) {
+ return knot_dnssec_key_rollover(ctx, flags, reschedule);
+ }
+
+ if (ret == KNOT_EOK && reschedule->keys_changed) {
+ ret = kdnssec_ctx_commit(ctx);
+ }
+
+ if (ret == KNOT_EOK && reschedule->plan_ds_check) {
+ char param[32];
+ (void)snprintf(param, sizeof(param), "KEY_SUBMISSION=%hu", ready_keytag);
+ log_fmt_zone(LOG_NOTICE, LOG_SOURCE_ZONE, ctx->zone->dname, param,
+ "DNSSEC, KSK submission, waiting for confirmation");
+ if (ctx->dbus_event & DBUS_EVENT_ZONE_SUBMISSION) {
+ systemd_emit_zone_submission(ctx->zone->dname, ready_keytag, ready_keyid);
+ }
+ }
+
+ return ret;
+}
+
+int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (key->is_ksk && !key->is_pub_only &&
+ get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
+ int ret = exec_new_signatures(ctx, key, retire_delay);
+ if (ret == KNOT_EOK) {
+ ret = kdnssec_ctx_commit(ctx);
+ }
+ return ret;
+ }
+ }
+ return KNOT_NO_READY_KEY;
+}
+
+bool zone_has_key_sbm(const kdnssec_ctx_t *ctx)
+{
+ assert(ctx->zone);
+
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (key->is_ksk && !key->is_pub_only &&
+ (get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY ||
+ get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/src/knot/dnssec/key-events.h b/src/knot/dnssec/key-events.h
new file mode 100644
index 0000000..d216f90
--- /dev/null
+++ b/src/knot/dnssec/key-events.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/zone-events.h"
+
+/*!
+ * \brief Perform correct ZSK and KSK rollover action and plan next one.
+ *
+ * For given zone, check keys in KASP db and decide what shall be done
+ * according to their timers. Perform the action if they shall be done now,
+ * and tell the user the next time it shall be called.
+ *
+ * This function is optimized to be called from KEY_ROLLOVER_EVENT,
+ * but also during zone load so that the zone gets loaded already with
+ * proper DNSSEC chain.
+ *
+ * \param ctx Zone signing context
+ * \param flags Determine if some actions are forced
+ * \param reschedule Out: timestamp of desired next invoke
+ *
+ * \return KNOT_E*
+ */
+int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_roll_flags_t flags,
+ zone_sign_reschedule_t *reschedule);
+
+/*!
+ * \brief Get the key that ought to be retired by activating given new key.
+ *
+ * \param ctx DNSSEC context.
+ * \param newkey New key being rolled in.
+ *
+ * \return Old key being rolled out.
+ */
+knot_kasp_key_t *knot_dnssec_key2retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey);
+
+/*!
+ * \brief Set the submitted KSK to active state and the active one to retired
+ *
+ * \param ctx Zone signing context.
+ * \param retire_delay Retire event delay.
+ *
+ * \return KNOT_E*
+ */
+int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay);
+
+/*!
+ * \brief Is there a key in submission phase?
+ *
+ * \param ctx zone signing context
+ *
+ * \return False if there is no submitted key or if error; True otherwise
+ */
+bool zone_has_key_sbm(const kdnssec_ctx_t *ctx);
diff --git a/src/knot/dnssec/key_records.c b/src/knot/dnssec/key_records.c
new file mode 100644
index 0000000..9b22f7a
--- /dev/null
+++ b/src/knot/dnssec/key_records.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 2022 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 "knot/dnssec/key_records.h"
+
+#include "libdnssec/error.h"
+#include "libdnssec/sign.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/journal/serialization.h"
+
+void key_records_init(const kdnssec_ctx_t *ctx, key_records_t *r)
+{
+ knot_rrset_init(&r->dnskey, knot_dname_copy(ctx->zone->dname, NULL),
+ KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+ knot_rrset_init(&r->cdnskey, knot_dname_copy(ctx->zone->dname, NULL),
+ KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->cds, knot_dname_copy(ctx->zone->dname, NULL),
+ KNOT_RRTYPE_CDS, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->rrsig, knot_dname_copy(ctx->zone->dname, NULL),
+ KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, ctx->policy->dnskey_ttl);
+}
+
+void key_records_from_apex(const zone_node_t *apex, key_records_t *r)
+{
+ r->dnskey = node_rrset(apex, KNOT_RRTYPE_DNSKEY);
+ r->cdnskey = node_rrset(apex, KNOT_RRTYPE_CDNSKEY);
+ r->cds = node_rrset(apex, KNOT_RRTYPE_CDS);
+ knot_rrset_init_empty(&r->rrsig);
+}
+
+int key_records_add_rdata(key_records_t *r, uint16_t rrtype, uint8_t *rdata, uint16_t rdlen, uint32_t ttl)
+{
+ knot_rrset_t *to_add;
+ switch(rrtype) {
+ case KNOT_RRTYPE_DNSKEY:
+ to_add = &r->dnskey;
+ break;
+ case KNOT_RRTYPE_CDNSKEY:
+ to_add = &r->cdnskey;
+ break;
+ case KNOT_RRTYPE_CDS:
+ to_add = &r->cds;
+ break;
+ case KNOT_RRTYPE_RRSIG:
+ to_add = &r->rrsig;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ int ret = knot_rrset_add_rdata(to_add, rdata, rdlen, NULL);
+ if (ret == KNOT_EOK) {
+ to_add->ttl = ttl;
+ }
+ return ret;
+}
+
+void key_records_clear(key_records_t *r)
+{
+ knot_rrset_clear(&r->dnskey, NULL);
+ knot_rrset_clear(&r->cdnskey, NULL);
+ knot_rrset_clear(&r->cds, NULL);
+ knot_rrset_clear(&r->rrsig, NULL);
+}
+
+void key_records_clear_rdatasets(key_records_t *r)
+{
+ knot_rdataset_clear(&r->dnskey.rrs, NULL);
+ knot_rdataset_clear(&r->cdnskey.rrs, NULL);
+ knot_rdataset_clear(&r->cds.rrs, NULL);
+ knot_rdataset_clear(&r->rrsig.rrs, NULL);
+}
+
+static int add_one(const knot_rrset_t *rr, changeset_t *ch,
+ bool rem, changeset_flag_t fl, int ret)
+{
+ if (ret == KNOT_EOK && !knot_rrset_empty(rr)) {
+ if (rem) {
+ ret = changeset_add_removal(ch, rr, fl);
+ } else {
+ ret = changeset_add_addition(ch, rr, fl);
+ }
+ }
+ return ret;
+}
+
+int key_records_to_changeset(const key_records_t *r, changeset_t *ch,
+ bool rem, changeset_flag_t chfl)
+{
+ int ret = KNOT_EOK;
+ ret = add_one(&r->dnskey, ch, rem, chfl, ret);
+ ret = add_one(&r->cdnskey, ch, rem, chfl, ret);
+ ret = add_one(&r->cds, ch, rem, chfl, ret);
+ return ret;
+}
+
+static int subtract_one(knot_rrset_t *from, const knot_rrset_t *what,
+ int (*fcn)(knot_rdataset_t *, const knot_rdataset_t *, knot_mm_t *),
+ int ret)
+{
+ if (ret == KNOT_EOK && !knot_rrset_empty(from)) {
+ ret = fcn(&from->rrs, &what->rrs, NULL);
+ }
+ return ret;
+}
+
+int key_records_subtract(key_records_t *r, const key_records_t *against)
+{
+ int ret = KNOT_EOK;
+ ret = subtract_one(&r->dnskey, &against->dnskey, knot_rdataset_subtract, ret);
+ ret = subtract_one(&r->cdnskey, &against->cdnskey, knot_rdataset_subtract, ret);
+ ret = subtract_one(&r->cds, &against->cds, knot_rdataset_subtract, ret);
+ return ret;
+}
+
+int key_records_intersect(key_records_t *r, const key_records_t *against)
+{
+ int ret = KNOT_EOK;
+ ret = subtract_one(&r->dnskey, &against->dnskey, knot_rdataset_intersect2, ret);
+ ret = subtract_one(&r->cdnskey, &against->cdnskey, knot_rdataset_intersect2, ret);
+ ret = subtract_one(&r->cds, &against->cds, knot_rdataset_intersect2, ret);
+ return ret;
+}
+
+int key_records_dump(char **buf, size_t *buf_size, const key_records_t *r, bool verbose)
+{
+ if (*buf == NULL) {
+ if (*buf_size == 0) {
+ *buf_size = 512;
+ }
+ *buf = malloc(*buf_size);
+ if (*buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+
+ const knot_dump_style_t verb_style = {
+ .wrap = true,
+ .show_ttl = true,
+ .verbose = true,
+ .original_ttl = true,
+ .human_timestamp = true
+ };
+ const knot_dump_style_t *style = verbose ? &verb_style : &KNOT_DUMP_STYLE_DEFAULT;
+
+ int ret = 0;
+ size_t total = 1;
+ const knot_rrset_t *all_rr[4] = { &r->dnskey, &r->cdnskey, &r->cds, &r->rrsig };
+ // first go: just detect the size
+ for (int i = 0; i < 4; i++) {
+ if (ret >= 0 && !knot_rrset_empty(all_rr[i])) {
+ ret = knot_rrset_txt_dump(all_rr[i], buf, buf_size, style);
+ (void)buf;
+ total += ret;
+ }
+ }
+ if (ret >= 0 && total > *buf_size) {
+ free(*buf);
+ *buf_size = total;
+ *buf = malloc(*buf_size);
+ if (*buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+ char *fake_buf = *buf;
+ size_t fake_size = *buf_size;
+ //second go: do it
+ for (int i = 0; i < 4; i++) {
+ if (ret >= 0 && !knot_rrset_empty(all_rr[i])) {
+ ret = knot_rrset_txt_dump(all_rr[i], &fake_buf, &fake_size, style);
+ fake_buf += ret, fake_size -= ret;
+ }
+ }
+ assert(fake_buf - *buf == total - 1);
+ return ret >= 0 ? KNOT_EOK : ret;
+}
+
+int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx, knot_time_t *expires)
+{
+ dnssec_sign_ctx_t *sign_ctx;
+ int ret = dnssec_sign_new(&sign_ctx, key->key);
+ if (ret != DNSSEC_EOK) {
+ ret = knot_error_from_libdnssec(ret);
+ }
+
+ if (!knot_rrset_empty(&r->dnskey) && knot_zone_sign_use_key(key, &r->dnskey)) {
+ ret = knot_sign_rrset(&r->rrsig, &r->dnskey, key->key, sign_ctx, kctx, NULL, expires);
+ }
+ if (ret == KNOT_EOK && !knot_rrset_empty(&r->cdnskey) && knot_zone_sign_use_key(key, &r->cdnskey)) {
+ ret = knot_sign_rrset(&r->rrsig, &r->cdnskey, key->key, sign_ctx, kctx, NULL, expires);
+ }
+ if (ret == KNOT_EOK && !knot_rrset_empty(&r->cds) && knot_zone_sign_use_key(key, &r->cds)) {
+ ret = knot_sign_rrset(&r->rrsig, &r->cds, key->key, sign_ctx, kctx, NULL, expires);
+ }
+
+ dnssec_sign_free(sign_ctx);
+ return ret;
+}
+
+int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp)
+{
+ kctx->now = timestamp;
+ int ret = kasp_zone_keys_from_rr(kctx->zone, &r->dnskey.rrs, false, &kctx->keytag_conflict);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ zone_sign_ctx_t *sign_ctx = zone_validation_ctx(kctx);
+ if (sign_ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ ret = knot_validate_rrsigs(&r->dnskey, &r->rrsig, sign_ctx, false);
+ if (ret == KNOT_EOK && !knot_rrset_empty(&r->cdnskey)) {
+ ret = knot_validate_rrsigs(&r->cdnskey, &r->rrsig, sign_ctx, false);
+ }
+ if (ret == KNOT_EOK && !knot_rrset_empty(&r->cds)) {
+ ret = knot_validate_rrsigs(&r->cds, &r->rrsig, sign_ctx, false);
+ }
+
+ zone_sign_ctx_free(sign_ctx);
+ return ret;
+}
+
+size_t key_records_serialized_size(const key_records_t *r)
+{
+ return rrset_serialized_size(&r->dnskey) + rrset_serialized_size(&r->cdnskey) +
+ rrset_serialized_size(&r->cds) + rrset_serialized_size(&r->rrsig);
+}
+
+int key_records_serialize(wire_ctx_t *wire, const key_records_t *r)
+{
+ int ret = serialize_rrset(wire, &r->dnskey);
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(wire, &r->cdnskey);
+ }
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(wire, &r->cds);
+ }
+ if (ret == KNOT_EOK) {
+ ret = serialize_rrset(wire, &r->rrsig);
+ }
+ return ret;
+}
+
+int key_records_deserialize(wire_ctx_t *wire, key_records_t *r)
+{
+ int ret = deserialize_rrset(wire, &r->dnskey);
+ if (ret == KNOT_EOK) {
+ ret = deserialize_rrset(wire, &r->cdnskey);
+ }
+ if (ret == KNOT_EOK) {
+ ret = deserialize_rrset(wire, &r->cds);
+ }
+ if (ret == KNOT_EOK) {
+ ret = deserialize_rrset(wire, &r->rrsig);
+ }
+ return ret;
+}
+
+int key_records_last_timestamp(kdnssec_ctx_t *ctx, knot_time_t *last)
+{
+ knot_time_t from = 0;
+ while (true) {
+ knot_time_t next;
+ key_records_t r = { { 0 } };
+ int ret = kasp_db_load_offline_records(ctx->kasp_db, ctx->zone->dname,
+ &from, &next, &r);
+ key_records_clear(&r);
+ if (ret == KNOT_ENOENT) {
+ break;
+ } else if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (next == 0) {
+ break;
+ }
+ from = next;
+ }
+ if (from == 0) {
+ from = knot_time();
+ }
+ *last = from;
+ return KNOT_EOK;
+}
diff --git a/src/knot/dnssec/key_records.h b/src/knot/dnssec/key_records.h
new file mode 100644
index 0000000..b53ed86
--- /dev/null
+++ b/src/knot/dnssec/key_records.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "contrib/wire_ctx.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/updates/changesets.h"
+
+void key_records_init(const kdnssec_ctx_t *ctx, key_records_t *r);
+
+void key_records_from_apex(const zone_node_t *apex, key_records_t *r);
+
+int key_records_add_rdata(key_records_t *r, uint16_t rrtype, uint8_t *rdata, uint16_t rdlen, uint32_t ttl);
+
+void key_records_clear(key_records_t *r);
+
+void key_records_clear_rdatasets(key_records_t *r);
+
+int key_records_to_changeset(const key_records_t *r, changeset_t *ch,
+ bool rem, changeset_flag_t chfl);
+
+int key_records_subtract(key_records_t *r, const key_records_t *against);
+
+int key_records_intersect(key_records_t *r, const key_records_t *against);
+
+int key_records_dump(char **buf, size_t *buf_size, const key_records_t *r, bool verbose);
+
+int key_records_sign(const zone_key_t *key, key_records_t *r, const kdnssec_ctx_t *kctx, knot_time_t *expires);
+
+// WARNING this modifies 'kctx' with updated timestamp and with zone_keys from r->dnskey
+int key_records_verify(key_records_t *r, kdnssec_ctx_t *kctx, knot_time_t timestamp);
+
+size_t key_records_serialized_size(const key_records_t *r);
+
+int key_records_serialize(wire_ctx_t *wire, const key_records_t *r);
+
+int key_records_deserialize(wire_ctx_t *wire, key_records_t *r);
+
+// Returns now if no records available.
+int key_records_last_timestamp(kdnssec_ctx_t *ctx, knot_time_t *last);
diff --git a/src/knot/dnssec/nsec-chain.c b/src/knot/dnssec/nsec-chain.c
new file mode 100644
index 0000000..dc35097
--- /dev/null
+++ b/src/knot/dnssec/nsec-chain.c
@@ -0,0 +1,797 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "contrib/base32hex.h"
+#include "knot/dnssec/nsec-chain.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/zone/adjust.h"
+
+void bitmap_add_node_rrsets(dnssec_nsec_bitmap_t *bitmap, const zone_node_t *node,
+ bool exact)
+{
+ bool deleg = node->flags & NODE_FLAGS_DELEG;
+ for (int i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rr = node_rrset_at(node, i);
+ if (deleg && (rr.type != KNOT_RRTYPE_NS && rr.type != KNOT_RRTYPE_DS &&
+ rr.type != KNOT_RRTYPE_NSEC)) {
+ if (rr.type != KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+ if (!rrsig_covers_type(&rr, KNOT_RRTYPE_DS) &&
+ !rrsig_covers_type(&rr, KNOT_RRTYPE_NSEC)) {
+ continue;
+ }
+ }
+ if (!exact && (rr.type == KNOT_RRTYPE_NSEC || rr.type == KNOT_RRTYPE_RRSIG)) {
+ continue;
+ }
+
+ dnssec_nsec_bitmap_add(bitmap, rr.type);
+ }
+}
+
+/* - NSEC chain construction ------------------------------------------------ */
+
+static int create_nsec_base(knot_rrset_t *rrset, knot_dname_t *from_owner,
+ const knot_dname_t *to_owner, uint32_t ttl,
+ size_t bitmap_size, uint8_t **bitmap_writeto)
+{
+ knot_rrset_init(rrset, from_owner, KNOT_RRTYPE_NSEC, KNOT_CLASS_IN, ttl);
+
+ size_t next_owner_size = knot_dname_size(to_owner);
+ size_t rdsize = next_owner_size + bitmap_size;
+ uint8_t rdata[rdsize];
+ memcpy(rdata, to_owner, next_owner_size);
+
+ int ret = knot_rrset_add_rdata(rrset, rdata, rdsize, NULL);
+
+ assert(ret != KNOT_EOK || rrset->rrs.rdata->len == rdsize);
+ *bitmap_writeto = rrset->rrs.rdata->data + next_owner_size;
+
+ return ret;
+}
+
+/*!
+ * \brief Create NSEC RR set.
+ *
+ * \param rrset RRSet to be initialized.
+ * \param from Node that should contain the new RRSet.
+ * \param to Node that should be pointed to from 'from'.
+ * \param ttl Record TTL (SOA's minimum TTL).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int create_nsec_rrset(knot_rrset_t *rrset, const zone_node_t *from,
+ const knot_dname_t *to, uint32_t ttl)
+{
+ assert(from);
+ assert(to);
+
+ dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new();
+ if (!rr_types) {
+ return KNOT_ENOMEM;
+ }
+
+ bitmap_add_node_rrsets(rr_types, from, false);
+ dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC);
+ dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_RRSIG);
+
+ uint8_t *bitmap_write;
+ int ret = create_nsec_base(rrset, from->owner, to, ttl,
+ dnssec_nsec_bitmap_size(rr_types), &bitmap_write);
+ if (ret == KNOT_EOK) {
+ dnssec_nsec_bitmap_write(rr_types, bitmap_write);
+ }
+ dnssec_nsec_bitmap_free(rr_types);
+
+ return ret;
+}
+
+/*!
+ * \brief Connect two nodes by adding a NSEC RR into the first node.
+ *
+ * Callback function, signature chain_iterate_cb.
+ *
+ * \param a First node.
+ * \param b Second node (immediate follower of a).
+ * \param data Pointer to nsec_chain_iterate_data_t holding parameters
+ * including changeset.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int connect_nsec_nodes(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data)
+{
+ assert(a);
+ assert(b);
+ assert(data);
+
+ if (b->rrset_count == 0 || b->flags & NODE_FLAGS_NONAUTH) {
+ return NSEC_NODE_SKIP;
+ }
+
+ int ret = KNOT_EOK;
+
+ /*!
+ * If the node has no other RRSets than NSEC (and possibly RRSIGs),
+ * just remove the NSEC and its RRSIG, they are redundant
+ */
+ if (node_rrtype_exists(b, KNOT_RRTYPE_NSEC)
+ && knot_nsec_empty_nsec_and_rrsigs_in_node(b)) {
+ ret = knot_nsec_changeset_remove(b, data->update);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ // Skip the 'b' node
+ return NSEC_NODE_SKIP;
+ }
+
+ // create new NSEC
+ knot_rrset_t new_nsec;
+ ret = create_nsec_rrset(&new_nsec, a, b->owner, data->ttl);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_rrset_t old_nsec = node_rrset(a, KNOT_RRTYPE_NSEC);
+
+ if (!knot_rrset_empty(&old_nsec)) {
+ /* Convert old NSEC to lowercase, just in case it's not. */
+ knot_rrset_t *old_nsec_lc = knot_rrset_copy(&old_nsec, NULL);
+ ret = knot_rrset_rr_to_canonical(old_nsec_lc);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(old_nsec_lc, NULL);
+ return ret;
+ }
+
+ bool equal = knot_rrset_equal(&new_nsec, old_nsec_lc, true);
+ knot_rrset_free(old_nsec_lc, NULL);
+
+ if (equal) {
+ // current NSEC is valid, do nothing
+ knot_rdataset_clear(&new_nsec.rrs, NULL);
+ return KNOT_EOK;
+ }
+
+ ret = knot_nsec_changeset_remove(a, data->update);
+ if (ret != KNOT_EOK) {
+ knot_rdataset_clear(&new_nsec.rrs, NULL);
+ return ret;
+ }
+ }
+
+ // Add new NSEC to the changeset (no matter if old was removed)
+ ret = zone_update_add(data->update, &new_nsec);
+ knot_rdataset_clear(&new_nsec.rrs, NULL);
+ return ret;
+}
+
+/*!
+ * \brief Replace b's NSEC "next" field with a's, keeping the NSEC bitmap.
+ *
+ * \param a Node to take the NSEC "next" field from.
+ * \param b Node to update the NSEC "next" field in.
+ * \param data Contains changeset to be updated.
+ *
+ * \return KNOT_E*
+ */
+static int reconnect_nsec_nodes(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data)
+{
+ assert(a);
+ assert(b);
+ assert(data);
+
+ knot_rrset_t an = node_rrset(a, KNOT_RRTYPE_NSEC);
+ assert(!knot_rrset_empty(&an));
+
+ knot_rrset_t bnorig = node_rrset(b, KNOT_RRTYPE_NSEC);
+ assert(!knot_rrset_empty(&bnorig));
+
+ size_t b_bitmap_len = knot_nsec_bitmap_len(bnorig.rrs.rdata);
+
+ knot_rrset_t bnnew;
+ uint8_t *bitmap_write;
+ int ret = create_nsec_base(&bnnew, bnorig.owner, knot_nsec_next(an.rrs.rdata),
+ bnorig.ttl, b_bitmap_len, &bitmap_write);
+ if (ret == KNOT_EOK) {
+ memcpy(bitmap_write, knot_nsec_bitmap(bnorig.rrs.rdata), b_bitmap_len);
+ }
+
+ ret = zone_update_remove(data->update, &bnorig);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add(data->update, &bnnew);
+ }
+
+ knot_rdataset_clear(&bnnew.rrs, NULL);
+ return ret;
+}
+
+static bool node_no_nsec(zone_node_t *node)
+{
+ return ((node->flags & NODE_FLAGS_DELETED) ||
+ (node->flags & NODE_FLAGS_NONAUTH) ||
+ node->rrset_count == 0);
+}
+
+/*!
+ * \brief Create or fix the node's NSEC record with correct bitmap.
+ *
+ * \param node Node to fix the NSEC bitmap in.
+ * \param data_voidp NSEC creation data.
+ *
+ * \return KNOT_E*
+ */
+static int nsec_update_bitmap(zone_node_t *node,
+ nsec_chain_iterate_data_t *data)
+{
+ if (node_no_nsec(node) || knot_nsec_empty_nsec_and_rrsigs_in_node(node)) {
+ return knot_nsec_changeset_remove(node, data->update);
+ }
+
+ knot_rrset_t old_nsec = node_rrset(node, KNOT_RRTYPE_NSEC);
+ const knot_dname_t *next = knot_rrset_empty(&old_nsec) ?
+ (const knot_dname_t *)"" :
+ knot_nsec_next(old_nsec.rrs.rdata);
+ knot_rrset_t new_nsec;
+ int ret = create_nsec_rrset(&new_nsec, node, next, data->ttl);
+
+ if (ret == KNOT_EOK && !knot_rrset_empty(&old_nsec)) {
+ ret = zone_update_remove(data->update, &old_nsec);
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add(data->update, &new_nsec);
+ }
+ knot_rdataset_clear(&new_nsec.rrs, NULL);
+ return ret;
+}
+
+static int nsec_update_bitmaps(zone_tree_t *node_ptrs,
+ nsec_chain_iterate_data_t *data)
+{
+ zone_tree_delsafe_it_t it = { 0 };
+ int ret = zone_tree_delsafe_it_begin(node_ptrs, &it, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ while (!zone_tree_delsafe_it_finished(&it) && ret == KNOT_EOK) {
+ ret = nsec_update_bitmap(zone_tree_delsafe_it_val(&it), data);
+ zone_tree_delsafe_it_next(&it);
+ }
+ zone_tree_delsafe_it_free(&it);
+ return ret;
+}
+
+static bool node_nsec3_unmatching(const zone_node_t *node, const dnssec_nsec3_params_t *params)
+{
+ knot_rdataset_t *nsec3 = node_rdataset(node, KNOT_RRTYPE_NSEC3);
+ if (nsec3 == NULL || nsec3->count < 1 || params == NULL) {
+ return false;
+ }
+ knot_rdata_t *rdata = nsec3->rdata;
+ for (int i = 0; i < nsec3->count; i++) {
+ if (knot_nsec3_alg(rdata) == params->algorithm &&
+ knot_nsec3_iters(rdata) == params->iterations &&
+ knot_nsec3_salt_len(rdata) == params->salt.size &&
+ memcmp(knot_nsec3_salt(rdata), params->salt.data, params->salt.size) == 0) {
+ return false;
+ }
+ rdata = knot_rdataset_next(rdata);
+ }
+ return true;
+}
+
+int nsec_check_connect_nodes(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data)
+{
+ if (node_no_nsec(b) || node_nsec3_unmatching(b, data->nsec3_params)) {
+ return NSEC_NODE_SKIP;
+ }
+ knot_rdataset_t *nsec = node_rdataset(a, data->nsec_type);
+ if (nsec == NULL || nsec->count != 1) {
+ data->update->validation_hint.node = a->owner;
+ data->update->validation_hint.rrtype = KNOT_RRTYPE_ANY;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+ if (data->nsec_type == KNOT_RRTYPE_NSEC) {
+ const knot_dname_t *a_next = knot_nsec_next(nsec->rdata);
+ if (!knot_dname_is_case_equal(a_next, b->owner)) {
+ data->update->validation_hint.node = a->owner;
+ data->update->validation_hint.rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+ } else {
+ uint8_t next_len = knot_nsec3_next_len(nsec->rdata);
+ uint8_t bdecoded[next_len];
+ int len = knot_base32hex_decode(b->owner + 1, b->owner[0], bdecoded, next_len);
+ if (len != next_len ||
+ memcmp(knot_nsec3_next(nsec->rdata), bdecoded, len) != 0) {
+ data->update->validation_hint.node = a->owner;
+ data->update->validation_hint.rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+ }
+ return KNOT_EOK;
+}
+
+static zone_node_t *nsec_prev(zone_node_t *node, const dnssec_nsec3_params_t *matching_params)
+{
+ zone_node_t *res = node;
+ do {
+ res = node_prev(res);
+ } while (res != NULL && ((res->flags & NODE_FLAGS_NONAUTH) ||
+ res->rrset_count == 0 ||
+ node_nsec3_unmatching(res, matching_params)));
+ assert(res == NULL || !knot_nsec_empty_nsec_and_rrsigs_in_node(res));
+ return res;
+}
+
+static int nsec_check_prev_next(zone_node_t *node, void *ctx)
+{
+ if (node_no_nsec(node)) {
+ return KNOT_EOK;
+ }
+
+ nsec_chain_iterate_data_t *data = ctx;
+ int ret = nsec_check_connect_nodes(nsec_prev(node, data->nsec3_params), node, data);
+ if (ret == NSEC_NODE_SKIP) {
+ return KNOT_EOK;
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ dnssec_validation_hint_t *hint = &data->update->validation_hint;
+ knot_rdataset_t *nsec = node_rdataset(node, data->nsec_type);
+ if (nsec == NULL || nsec->count != 1) {
+ hint->node = node->owner;
+ hint->rrtype = KNOT_RRTYPE_ANY;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+
+ const zone_node_t *nn;
+ if (data->nsec_type == KNOT_RRTYPE_NSEC) {
+ if (knot_dname_store(hint->next, knot_nsec_next(nsec->rdata)) == 0) {
+ return KNOT_EINVAL;
+ }
+ knot_dname_to_lower(hint->next);
+ nn = zone_contents_find_node(data->update->new_cont, hint->next);
+ } else {
+ ret = knot_nsec3_hash_to_dname(hint->next, sizeof(hint->next),
+ knot_nsec3_next(nsec->rdata),
+ knot_nsec3_next_len(nsec->rdata),
+ data->update->new_cont->apex->owner);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ nn = zone_contents_find_nsec3_node(data->update->new_cont, hint->next);
+ }
+ if (nn == NULL) {
+ hint->node = hint->next;
+ hint->rrtype = KNOT_RRTYPE_ANY;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+ if (nsec_prev((zone_node_t *)nn, data->nsec3_params) != node) {
+ hint->node = node->owner;
+ hint->rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC_CHAIN;
+ }
+ return KNOT_EOK;
+}
+
+int nsec_check_new_connects(zone_tree_t *tree, nsec_chain_iterate_data_t *data)
+{
+ return zone_tree_apply(tree, nsec_check_prev_next, data);
+}
+
+static int check_subtree_optout(zone_node_t *node, void *ctx)
+{
+ bool *res = ctx;
+ if ((node->flags & NODE_FLAGS_NONAUTH) || !*res) {
+ return KNOT_EOK;
+ }
+ if (node_nsec3_get(node) != NULL &&
+ node_rdataset(node_nsec3_get(node), KNOT_RRTYPE_NSEC3) != NULL) {
+ *res = false;
+ }
+ return KNOT_EOK;
+}
+
+static int check_nsec_bitmap(zone_node_t *node, void *ctx)
+{
+ nsec_chain_iterate_data_t *data = ctx;
+ assert((bool)(data->nsec_type == KNOT_RRTYPE_NSEC3) == (bool)(data->nsec3_params != NULL));
+ const zone_node_t *nsec_node = node;
+ bool shall_no_nsec = node_no_nsec(node);
+ if (data->nsec3_params != NULL) {
+ if ((node->flags & NODE_FLAGS_DELETED) ||
+ node_rrtype_exists(node, KNOT_RRTYPE_NSEC3)) {
+ // this can happen when checking nodes from adjust_ptrs
+ return KNOT_EOK;
+ }
+ nsec_node = node_nsec3_get(node);
+ shall_no_nsec = (node->flags & NODE_FLAGS_DELETED) ||
+ (node->flags & NODE_FLAGS_NONAUTH);
+ }
+ bool may_no_nsec = (data->nsec3_params != NULL && !(node->flags & NODE_FLAGS_SUBTREE_AUTH));
+ knot_rdataset_t *nsec = node_rdataset(nsec_node, data->nsec_type);
+ if (may_no_nsec && nsec == NULL) {
+ int ret = zone_tree_sub_apply(data->update->new_cont->nodes, node->owner,
+ true, check_subtree_optout, &may_no_nsec);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ if ((nsec == NULL || nsec->count != 1) && !shall_no_nsec && !may_no_nsec) {
+ data->update->validation_hint.node = (nsec_node == NULL ? node->owner : nsec_node->owner);
+ data->update->validation_hint.rrtype = KNOT_RRTYPE_ANY;
+ return KNOT_DNSSEC_ENONSEC;
+ }
+ if (shall_no_nsec && nsec != NULL && nsec->count > 0) {
+ data->update->validation_hint.node = nsec_node->owner;
+ data->update->validation_hint.rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC_BITMAP;
+ }
+ if (shall_no_nsec) {
+ return KNOT_EOK;
+ }
+ if (may_no_nsec && nsec == NULL) {
+ assert(data->nsec_type == KNOT_RRTYPE_NSEC3);
+ const zone_node_t *found_nsec3 = NULL, *prev_nsec3 = NULL;
+ if (node->nsec3_hash == NULL ||
+ zone_contents_find_nsec3(data->update->new_cont, node->nsec3_hash, &found_nsec3, &prev_nsec3) != ZONE_NAME_NOT_FOUND ||
+ found_nsec3 != NULL) {
+ return KNOT_ERROR;
+ }
+ if (prev_nsec3 == NULL) {
+ data->update->validation_hint.node = (nsec_node == NULL ? node->owner : nsec_node->owner);
+ data->update->validation_hint.rrtype = KNOT_RRTYPE_ANY;
+ return KNOT_DNSSEC_ENONSEC;
+ }
+ knot_rdataset_t *nsec3 = node_rdataset(prev_nsec3, KNOT_RRTYPE_NSEC3);
+ if (nsec3 == NULL) {
+ return KNOT_ERROR;
+ }
+ if (nsec3->count != 1 || !(knot_nsec3param_flags(nsec3->rdata) & KNOT_NSEC3_FLAG_OPT_OUT)) {
+ data->update->validation_hint.node = prev_nsec3->owner;
+ data->update->validation_hint.rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC3_OPTOUT;
+ }
+ return KNOT_EOK;
+ }
+
+ dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new();
+ if (rr_types == NULL) {
+ return KNOT_ENOMEM;
+ }
+ bitmap_add_node_rrsets(rr_types, node, true);
+
+ uint16_t node_wire_size = dnssec_nsec_bitmap_size(rr_types);
+ uint8_t *node_wire = malloc(node_wire_size);
+ if (node_wire == NULL) {
+ dnssec_nsec_bitmap_free(rr_types);
+ return KNOT_ENOMEM;
+ }
+ dnssec_nsec_bitmap_write(rr_types, node_wire);
+ dnssec_nsec_bitmap_free(rr_types);
+
+ const uint8_t *nsec_wire = NULL;
+ uint16_t nsec_wire_size = 0;
+ if (data->nsec3_params == NULL) {
+ nsec_wire = knot_nsec_bitmap(nsec->rdata);
+ nsec_wire_size = knot_nsec_bitmap_len(nsec->rdata);
+ } else {
+ nsec_wire = knot_nsec3_bitmap(nsec->rdata);
+ nsec_wire_size = knot_nsec3_bitmap_len(nsec->rdata);
+ }
+
+ if (node_wire_size != nsec_wire_size ||
+ memcmp(node_wire, nsec_wire, node_wire_size) != 0) {
+ free(node_wire);
+ data->update->validation_hint.node = node->owner;
+ data->update->validation_hint.rrtype = data->nsec_type;
+ return KNOT_DNSSEC_ENSEC_BITMAP;
+ }
+ free(node_wire);
+ return KNOT_EOK;
+}
+
+int nsec_check_bitmaps(zone_tree_t *nsec_ptrs, nsec_chain_iterate_data_t *data)
+{
+ return zone_tree_apply(nsec_ptrs, check_nsec_bitmap, data);
+}
+
+/*! \brief Return the one from those nodes which has
+ * closest lower (lexicographically) owner name to ref. */
+static zone_node_t *node_nearer(zone_node_t *a, zone_node_t *b, zone_node_t *ref)
+{
+ if (a == NULL || a == b) {
+ return b;
+ } else if (b == NULL) {
+ return a;
+ } else {
+ int abigger = knot_dname_cmp(a->owner, ref->owner) >= 0 ? 1 : 0;
+ int bbigger = knot_dname_cmp(b->owner, ref->owner) >= 0 ? 1 : 0;
+ int cmp = knot_dname_cmp(a->owner, b->owner);
+ if (abigger != bbigger) {
+ cmp = -cmp;
+ }
+ return cmp < 0 ? b : a;
+ }
+}
+
+/* - API - iterations ------------------------------------------------------- */
+
+/*!
+ * \brief Call a function for each piece of the chain formed by sorted nodes.
+ */
+int knot_nsec_chain_iterate_create(zone_tree_t *nodes,
+ chain_iterate_create_cb callback,
+ nsec_chain_iterate_data_t *data)
+{
+ assert(nodes);
+ assert(callback);
+
+ zone_tree_delsafe_it_t it = { 0 };
+ int result = zone_tree_delsafe_it_begin(nodes, &it, false);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ if (zone_tree_delsafe_it_finished(&it)) {
+ zone_tree_delsafe_it_free(&it);
+ return KNOT_EINVAL;
+ }
+
+ zone_node_t *first = zone_tree_delsafe_it_val(&it);
+ zone_node_t *previous = first;
+ zone_node_t *current = first;
+
+ zone_tree_delsafe_it_next(&it);
+
+ while (!zone_tree_delsafe_it_finished(&it)) {
+ current = zone_tree_delsafe_it_val(&it);
+
+ result = callback(previous, current, data);
+ if (result == NSEC_NODE_SKIP) {
+ // No NSEC should be created for 'current' node, skip
+ ;
+ } else if (result == KNOT_EOK) {
+ previous = current;
+ } else {
+ zone_tree_delsafe_it_free(&it);
+ return result;
+ }
+ zone_tree_delsafe_it_next(&it);
+ }
+
+ zone_tree_delsafe_it_free(&it);
+
+ return result == NSEC_NODE_SKIP ? callback(previous, first, data) :
+ callback(current, first, data);
+}
+
+int knot_nsec_chain_iterate_fix(zone_tree_t *node_ptrs,
+ chain_iterate_create_cb callback,
+ chain_iterate_create_cb cb_reconn,
+ nsec_chain_iterate_data_t *data)
+{
+ zone_tree_delsafe_it_t it = { 0 };
+ int ret = zone_tree_delsafe_it_begin(node_ptrs, &it, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ zone_node_t *prev_it = NULL;
+ zone_node_t *started_with = NULL;
+ while (ret == KNOT_EOK) {
+ if (zone_tree_delsafe_it_finished(&it)) {
+ assert(started_with != NULL);
+ zone_tree_delsafe_it_restart(&it);
+ }
+
+ zone_node_t *curr_new = zone_tree_delsafe_it_val(&it);
+ zone_node_t *curr_old = binode_counterpart(curr_new);
+ bool del_new = node_no_nsec(curr_new);
+ bool del_old = node_no_nsec(curr_old);
+
+ if (started_with == curr_new) {
+ assert(started_with != NULL);
+ break;
+ }
+ if (!del_old && !del_new && started_with == NULL) {
+ // Once this must happen since the NSEC(3) node belonging
+ // to zone apex is always present.
+ started_with = curr_new;
+ }
+
+ if (!del_old && del_new && started_with != NULL) {
+ zone_node_t *prev_old = curr_old, *prev_new;
+ do {
+ prev_old = nsec_prev(prev_old, NULL);
+ prev_new = binode_counterpart(prev_old);
+ } while (node_no_nsec(prev_new));
+
+ zone_node_t *prev_near = node_nearer(prev_new, prev_it, curr_old);
+ ret = cb_reconn(curr_old, prev_near, data);
+ }
+ if (del_old && !del_new && started_with != NULL) {
+ zone_node_t *prev_new = nsec_prev(curr_new, NULL);
+ ret = cb_reconn(prev_new, curr_new, data);
+ if (ret == KNOT_EOK) {
+ ret = callback(prev_new, curr_new, data);
+ }
+ prev_it = curr_new;
+ }
+
+ zone_tree_delsafe_it_next(&it);
+ }
+ zone_tree_delsafe_it_free(&it);
+ return ret;
+}
+
+/* - API - utility functions ------------------------------------------------ */
+
+/*!
+ * \brief Add entry for removed NSEC to the changeset.
+ */
+int knot_nsec_changeset_remove(const zone_node_t *n, zone_update_t *update)
+{
+ if (update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int result = KNOT_EOK;
+ knot_rrset_t nsec_rem = node_rrset(n, KNOT_RRTYPE_NSEC);
+ knot_rrset_t nsec3_rem = node_rrset(n, KNOT_RRTYPE_NSEC3);
+ knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG);
+
+ if (!knot_rrset_empty(&nsec_rem)) {
+ result = zone_update_remove(update, &nsec_rem);
+ }
+ if (result == KNOT_EOK && !knot_rrset_empty(&nsec3_rem)) {
+ result = zone_update_remove(update, &nsec3_rem);
+ }
+ if (!knot_rrset_empty(&rrsigs) && result == KNOT_EOK) {
+ knot_rrset_t synth_rrsigs;
+ knot_rrset_init(&synth_rrsigs, n->owner, KNOT_RRTYPE_RRSIG,
+ KNOT_CLASS_IN, rrsigs.ttl);
+ result = knot_synth_rrsig(KNOT_RRTYPE_NSEC, &rrsigs.rrs,
+ &synth_rrsigs.rrs, NULL);
+ if (result == KNOT_ENOENT) {
+ // Try removing NSEC3 RRSIGs
+ result = knot_synth_rrsig(KNOT_RRTYPE_NSEC3, &rrsigs.rrs,
+ &synth_rrsigs.rrs, NULL);
+ }
+
+ if (result != KNOT_EOK) {
+ knot_rdataset_clear(&synth_rrsigs.rrs, NULL);
+ if (result != KNOT_ENOENT) {
+ return result;
+ }
+ return KNOT_EOK;
+ }
+
+ // store RRSIG
+ result = zone_update_remove(update, &synth_rrsigs);
+ knot_rdataset_clear(&synth_rrsigs.rrs, NULL);
+ }
+
+ return result;
+}
+
+/*!
+ * \brief Checks whether the node is empty or eventually contains only NSEC and
+ * RRSIGs.
+ */
+bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n)
+{
+ assert(n);
+ for (int i = 0; i < n->rrset_count; ++i) {
+ knot_rrset_t rrset = node_rrset_at(n, i);
+ if (rrset.type != KNOT_RRTYPE_NSEC &&
+ rrset.type != KNOT_RRTYPE_RRSIG) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* - API - Chain creation --------------------------------------------------- */
+
+/*!
+ * \brief Create new NSEC chain, add differences from current into a changeset.
+ */
+int knot_nsec_create_chain(zone_update_t *update, uint32_t ttl)
+{
+ assert(update);
+ assert(update->new_cont->nodes);
+
+ nsec_chain_iterate_data_t data = { ttl, update, KNOT_RRTYPE_NSEC };
+
+ return knot_nsec_chain_iterate_create(update->new_cont->nodes,
+ connect_nsec_nodes, &data);
+}
+
+int knot_nsec_fix_chain(zone_update_t *update, uint32_t ttl)
+{
+ assert(update);
+ assert(update->zone->contents->nodes);
+ assert(update->new_cont->nodes);
+
+ nsec_chain_iterate_data_t data = { ttl, update, KNOT_RRTYPE_NSEC };
+
+ int ret = nsec_update_bitmaps(update->a_ctx->node_ptrs, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_adjust_contents(update->new_cont, adjust_cb_void, NULL, false, true, 1, update->a_ctx->node_ptrs);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // ensure that zone root is in list of changed nodes
+ ret = zone_tree_insert(update->a_ctx->node_ptrs, &update->new_cont->apex);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return knot_nsec_chain_iterate_fix(update->a_ctx->node_ptrs,
+ connect_nsec_nodes, reconnect_nsec_nodes, &data);
+}
+
+int knot_nsec_check_chain(zone_update_t *update)
+{
+ if (!zone_tree_is_empty(update->new_cont->nsec3_nodes)) {
+ update->validation_hint.node = update->zone->name;
+ update->validation_hint.rrtype = KNOT_RRTYPE_NSEC3;
+ return KNOT_DNSSEC_ENSEC_BITMAP;
+ }
+
+ nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC };
+
+ int ret = nsec_check_bitmaps(update->new_cont->nodes, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return knot_nsec_chain_iterate_create(update->new_cont->nodes,
+ nsec_check_connect_nodes, &data);
+}
+
+int knot_nsec_check_chain_fix(zone_update_t *update)
+{
+ if (!zone_tree_is_empty(update->new_cont->nsec3_nodes)) {
+ update->validation_hint.node = update->zone->name;
+ update->validation_hint.rrtype = KNOT_RRTYPE_NSEC3;
+ return KNOT_DNSSEC_ENSEC_BITMAP;
+ }
+
+ nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC };
+
+ int ret = nsec_check_bitmaps(update->a_ctx->node_ptrs, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return nsec_check_new_connects(update->a_ctx->node_ptrs, &data);
+}
diff --git a/src/knot/dnssec/nsec-chain.h b/src/knot/dnssec/nsec-chain.h
new file mode 100644
index 0000000..362780e
--- /dev/null
+++ b/src/knot/dnssec/nsec-chain.h
@@ -0,0 +1,174 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "knot/zone/contents.h"
+#include "knot/updates/zone-update.h"
+#include "libdnssec/nsec.h"
+
+/*!
+ * \brief Parameters to be used in connect_nsec_nodes callback.
+ */
+typedef struct {
+ uint32_t ttl; // TTL for NSEC(3) records
+ zone_update_t *update; // The zone update for NSECs
+ uint16_t nsec_type; // NSEC or NSEC3
+ const dnssec_nsec3_params_t *nsec3_params;
+} nsec_chain_iterate_data_t;
+
+/*!
+ * \brief Used to control changeset iteration functions.
+ */
+enum {
+ NSEC_NODE_SKIP = 1,
+};
+
+/*!
+ * \brief Callback used when creating NSEC chains.
+ */
+typedef int (*chain_iterate_create_cb)(zone_node_t *, zone_node_t *,
+ nsec_chain_iterate_data_t *);
+
+/*!
+ * \brief Add all RR types from a node into the bitmap.
+ */
+void bitmap_add_node_rrsets(dnssec_nsec_bitmap_t *bitmap, const zone_node_t *node,
+ bool exact);
+
+/*!
+ * \brief Check that the NSEC(3) record in node A points to B.
+ *
+ * \param a Node A.
+ * \param b Node B.
+ * \param data Validation context.
+ *
+ * \retval NSEC_NODE_SKIP Node B is not part of NSEC chain, call again with A and B->next.
+ * \retval KNOT_DNSSEC_ENSEC_CHAIN The NSEC(3) chain is broken.
+ * \return KNOT_E*
+ */
+int nsec_check_connect_nodes(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data);
+
+/*!
+ * \brief Check NSEC connections of updated nodes.
+ *
+ * \param tree Trie with updated nodes.
+ * \param data Validation context.
+ *
+ * \return KNOT_DNSSEC_ENSEC_CHAIN, KNOT_E*
+ */
+int nsec_check_new_connects(zone_tree_t *tree, nsec_chain_iterate_data_t *data);
+
+/*!
+ * \brief Check NSEC(3) bitmaps for updated nodes.
+ *
+ * \param nsec_ptrs Trie with nodes to be checked.
+ * \param data Validation context.
+ *
+ * \return KNOT_DNSSEC_ENSEC_BITMAP, KNOT_E*
+ */
+int nsec_check_bitmaps(zone_tree_t *nsec_ptrs, nsec_chain_iterate_data_t *data);
+
+/*!
+ * \brief Call a function for each piece of the chain formed by sorted nodes.
+ *
+ * \note If the callback function returns anything other than KNOT_EOK, the
+ * iteration is terminated and the error code is propagated.
+ *
+ * \param nodes Zone nodes.
+ * \param callback Callback function.
+ * \param data Custom data supplied to the callback function.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_nsec_chain_iterate_create(zone_tree_t *nodes,
+ chain_iterate_create_cb callback,
+ nsec_chain_iterate_data_t *data);
+
+/*!
+ * \brief Call the chain-connecting function for modified records and their neighbours.
+ *
+ * \param node_ptrs Tree of those nodes that have ben changed by the update.
+ * \param callback Callback function.
+ * \param cb_reconn Callback for re-connecting "next" link to another node.
+ * \param data Custom data supplied, incl. changeset to be updated.
+ *
+ * \retval KNOT_ENORECORD if the chain must be recreated from scratch.
+ * \return KNOT_E*
+ */
+int knot_nsec_chain_iterate_fix(zone_tree_t *node_ptrs,
+ chain_iterate_create_cb callback,
+ chain_iterate_create_cb cb_reconn,
+ nsec_chain_iterate_data_t *data);
+
+/*!
+ * \brief Add entry for removed NSEC(3) and its RRSIG to the changeset.
+ *
+ * \param n Node to extract NSEC(3) from.
+ * \param update Update to add the old RR removal into.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_nsec_changeset_remove(const zone_node_t *n, zone_update_t *update);
+
+/*!
+ * \brief Checks whether the node is empty or eventually contains only NSEC and
+ * RRSIGs.
+ *
+ * \param n Node to check.
+ *
+ * \retval true if the node is empty or contains only NSEC and RRSIGs.
+ * \retval false otherwise.
+ */
+bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n);
+
+/*!
+ * \brief Create new NSEC chain.
+ *
+ * \param update Zone update to create NSEC chain for.
+ * \param ttl TTL for created NSEC records.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_nsec_create_chain(zone_update_t *update, uint32_t ttl);
+
+/*!
+ * \brief Fix existing NSEC chain to cover the changes in zone contents.
+ *
+ * \param update Zone update to update NSEC chain for.
+ * \param ttl TTL for created NSEC records.
+ *
+ * \retval KNOT_ENORECORD if the chain must be recreated from scratch.
+ * \return KNOT_E*
+ */
+int knot_nsec_fix_chain(zone_update_t *update, uint32_t ttl);
+
+/*!
+ * \brief Validate NSEC chain in new_cont as whole.
+ *
+ * \note new_cont must have been adjusted already!
+ */
+int knot_nsec_check_chain(zone_update_t *update);
+
+/*!
+ * \brief Validate NSEC chain in new_cont incrementally.
+ */
+int knot_nsec_check_chain_fix(zone_update_t *update);
diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c
new file mode 100644
index 0000000..97010be
--- /dev/null
+++ b/src/knot/dnssec/nsec3-chain.c
@@ -0,0 +1,733 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "libknot/dname.h"
+#include "knot/dnssec/nsec-chain.h"
+#include "knot/dnssec/nsec3-chain.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/zone-diff.h"
+#include "contrib/base32hex.h"
+#include "contrib/wire_ctx.h"
+
+static bool nsec3_empty(const zone_node_t *node, const dnssec_nsec3_params_t *params)
+{
+ bool opt_out = (params->flags & KNOT_NSEC3_FLAG_OPT_OUT);
+ return opt_out ? !(node->flags & NODE_FLAGS_SUBTREE_AUTH) : !(node->flags & NODE_FLAGS_SUBTREE_DATA);
+}
+
+/*!
+ * \brief Check whether at least one RR type in node should be signed,
+ * used when signing with NSEC3.
+ *
+ * \param node Node for which the check is done.
+ *
+ * \return true/false.
+ */
+static bool node_should_be_signed_nsec3(const zone_node_t *n)
+{
+ for (int i = 0; i < n->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(n, i);
+ if (rrset.type == KNOT_RRTYPE_NSEC ||
+ rrset.type == KNOT_RRTYPE_RRSIG) {
+ continue;
+ }
+
+ if (knot_zone_sign_rr_should_be_signed(n, &rrset)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*!
+ * \brief Custom NSEC3 tree free function.
+ *
+ */
+static void free_nsec3_tree(zone_tree_t *nodes)
+{
+ assert(nodes);
+
+ zone_tree_it_t it = { 0 };
+ for ((void)zone_tree_it_begin(nodes, &it); !zone_tree_it_finished(&it); zone_tree_it_next(&it)) {
+ zone_node_t *node = zone_tree_it_val(&it);
+ // newly allocated NSEC3 nodes
+ knot_rdataset_t *nsec3 = node_rdataset(node, KNOT_RRTYPE_NSEC3);
+ knot_rdataset_t *rrsig = node_rdataset(node, KNOT_RRTYPE_RRSIG);
+ knot_rdataset_clear(nsec3, NULL);
+ knot_rdataset_clear(rrsig, NULL);
+ node_free(node, NULL);
+ }
+
+ zone_tree_it_free(&it);
+ zone_tree_free(&nodes);
+}
+
+/* - NSEC3 nodes construction ----------------------------------------------- */
+
+/*!
+ * \brief Get NSEC3 RDATA size.
+ */
+static size_t nsec3_rdata_size(const dnssec_nsec3_params_t *params,
+ const dnssec_nsec_bitmap_t *rr_types)
+{
+ assert(params);
+ assert(rr_types);
+
+ return 6 + params->salt.size
+ + dnssec_nsec3_hash_length(params->algorithm)
+ + dnssec_nsec_bitmap_size(rr_types);
+}
+
+/*!
+ * \brief Fill NSEC3 RDATA.
+ *
+ * \note Content of next hash field is not changed.
+ */
+static int nsec3_fill_rdata(uint8_t *rdata, size_t rdata_len,
+ const dnssec_nsec3_params_t *params,
+ const dnssec_nsec_bitmap_t *rr_types,
+ const uint8_t *next_hashed)
+{
+ assert(rdata);
+ assert(params);
+ assert(rr_types);
+
+ uint8_t hash_length = dnssec_nsec3_hash_length(params->algorithm);
+
+ wire_ctx_t wire = wire_ctx_init(rdata, rdata_len);
+
+ wire_ctx_write_u8(&wire, params->algorithm);
+ wire_ctx_write_u8(&wire, params->flags);
+ wire_ctx_write_u16(&wire, params->iterations);
+ wire_ctx_write_u8(&wire, params->salt.size);
+ wire_ctx_write(&wire, params->salt.data, params->salt.size);
+ wire_ctx_write_u8(&wire, hash_length);
+
+ if (next_hashed != NULL) {
+ wire_ctx_write(&wire, next_hashed, hash_length);
+ } else {
+ wire_ctx_skip(&wire, hash_length);
+ }
+
+ if (wire.error != KNOT_EOK) {
+ return wire.error;
+ }
+
+ dnssec_nsec_bitmap_write(rr_types, wire.position);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Creates NSEC3 RRSet.
+ *
+ * \param owner Owner for the RRSet.
+ * \param params Parsed NSEC3PARAM.
+ * \param rr_types Bitmap.
+ * \param next_hashed Next hashed.
+ * \param ttl TTL for the RRSet.
+ *
+ * \return Pointer to created RRSet on success, NULL on errors.
+ */
+static int create_nsec3_rrset(knot_rrset_t *rrset,
+ const knot_dname_t *owner,
+ const dnssec_nsec3_params_t *params,
+ const dnssec_nsec_bitmap_t *rr_types,
+ const uint8_t *next_hashed,
+ uint32_t ttl)
+{
+ assert(rrset);
+ assert(owner);
+ assert(params);
+ assert(rr_types);
+
+ knot_dname_t *owner_copy = knot_dname_copy(owner, NULL);
+ if (owner_copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_init(rrset, owner_copy, KNOT_RRTYPE_NSEC3, KNOT_CLASS_IN, ttl);
+
+ size_t rdata_size = nsec3_rdata_size(params, rr_types);
+ uint8_t rdata[rdata_size];
+ memset(rdata, 0, rdata_size);
+ int ret = nsec3_fill_rdata(rdata, rdata_size, params, rr_types,
+ next_hashed);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(owner_copy, NULL);
+ return ret;
+ }
+
+ ret = knot_rrset_add_rdata(rrset, rdata, rdata_size, NULL);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(owner_copy, NULL);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Create NSEC3 node.
+ */
+static zone_node_t *create_nsec3_node(const knot_dname_t *owner,
+ const dnssec_nsec3_params_t *nsec3_params,
+ zone_node_t *apex_node,
+ const dnssec_nsec_bitmap_t *rr_types,
+ uint32_t ttl)
+{
+ assert(owner);
+ assert(nsec3_params);
+ assert(apex_node);
+ assert(rr_types);
+
+ zone_node_t *new_node = node_new(owner, false, false, NULL);
+ if (!new_node) {
+ return NULL;
+ }
+
+ knot_rrset_t nsec3_rrset;
+ int ret = create_nsec3_rrset(&nsec3_rrset, owner, nsec3_params,
+ rr_types, NULL, ttl);
+ if (ret != KNOT_EOK) {
+ node_free(new_node, NULL);
+ return NULL;
+ }
+
+ ret = node_add_rrset(new_node, &nsec3_rrset, NULL);
+ knot_rrset_clear(&nsec3_rrset, NULL);
+ if (ret != KNOT_EOK) {
+ node_free(new_node, NULL);
+ return NULL;
+ }
+
+ return new_node;
+}
+
+/*!
+ * \brief Create new NSEC3 node for given regular node.
+ *
+ * \param node Node for which the NSEC3 node is created.
+ * \param apex Zone apex node.
+ * \param params NSEC3 hash function parameters.
+ * \param ttl TTL of the new NSEC3 node.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static zone_node_t *create_nsec3_node_for_node(const zone_node_t *node,
+ zone_node_t *apex,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl)
+{
+ assert(node);
+ assert(apex);
+ assert(params);
+
+ knot_dname_storage_t nsec3_owner;
+ int ret = knot_create_nsec3_owner(nsec3_owner, sizeof(nsec3_owner),
+ node->owner, apex->owner, params);
+ if (ret != KNOT_EOK) {
+ return NULL;
+ }
+
+ dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new();
+ if (!rr_types) {
+ return NULL;
+ }
+
+ bitmap_add_node_rrsets(rr_types, node, false);
+ if (node->rrset_count > 0 && node_should_be_signed_nsec3(node)) {
+ dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_RRSIG);
+ }
+ if (node == apex) {
+ dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC3PARAM);
+ }
+
+ zone_node_t *nsec3_node = create_nsec3_node(nsec3_owner, params, apex,
+ rr_types, ttl);
+ dnssec_nsec_bitmap_free(rr_types);
+
+ return nsec3_node;
+}
+
+/* - NSEC3 chain creation --------------------------------------------------- */
+
+// see connect_nsec3_nodes() for what this function does
+static int connect_nsec3_base(knot_rdataset_t *a_rrs, const knot_dname_t *b_name)
+{
+ assert(a_rrs);
+ uint8_t algorithm = knot_nsec3_alg(a_rrs->rdata);
+ if (algorithm == 0) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t raw_length = knot_nsec3_next_len(a_rrs->rdata);
+ assert(raw_length == dnssec_nsec3_hash_length(algorithm));
+ uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(a_rrs->rdata);
+ if (raw_hash == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(b_name);
+ uint8_t b32_length = b_name[0];
+ const uint8_t *b32_hash = &(b_name[1]);
+ int32_t written = knot_base32hex_decode(b32_hash, b32_length, raw_hash, raw_length);
+ if (written != raw_length) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Connect two nodes by filling 'hash' field of NSEC3 RDATA of the first node.
+ *
+ * \param a First node. Gets modified in-place!
+ * \param b Second node (immediate follower of a).
+ * \param data Unused parameter.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b,
+ _unused_ nsec_chain_iterate_data_t *data)
+{
+ assert(a);
+ assert(b);
+ assert(a->rrset_count == 1);
+
+ return connect_nsec3_base(node_rdataset(a, KNOT_RRTYPE_NSEC3), b->owner);
+}
+
+/*!
+ * \brief Connect two nodes by updating the changeset.
+ *
+ * \param a First node.
+ * \param b Second node.
+ * \param data Contains the changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int connect_nsec3_nodes2(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data)
+{
+ assert(data);
+
+ knot_rrset_t aorig = node_rrset(a, KNOT_RRTYPE_NSEC3);
+ assert(!knot_rrset_empty(&aorig));
+
+ // prepare a copy of NSEC3 rrsets in question
+ knot_rrset_t *acopy = knot_rrset_copy(&aorig, NULL);
+ if (acopy == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // connect the copied rrset
+ int ret = connect_nsec3_base(&acopy->rrs, b->owner);
+ if (ret != KNOT_EOK || knot_rrset_equal(&aorig, acopy, true)) {
+ knot_rrset_free(acopy, NULL);
+ return ret;
+ }
+
+ // add the removed original and the updated copy to changeset
+ ret = zone_update_remove(data->update, &aorig);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add(data->update, acopy);
+ }
+ knot_rrset_free(acopy, NULL);
+ return ret;
+}
+
+/*!
+ * \brief Replace the "next hash" field in b's NSEC3 by that in a's NSEC3, by updating the changeset.
+ *
+ * \param a A node to take the "next hash" from.
+ * \param b A node to put the "next hash" into.
+ * \param data Contains the changeset to be updated.
+ *
+ * \return KNOT_E*
+ */
+static int reconnect_nsec3_nodes2(zone_node_t *a, zone_node_t *b,
+ nsec_chain_iterate_data_t *data)
+{
+ assert(data);
+
+ knot_rrset_t an = node_rrset(a, KNOT_RRTYPE_NSEC3);
+ assert(!knot_rrset_empty(&an));
+
+ knot_rrset_t bnorig = node_rrset(b, KNOT_RRTYPE_NSEC3);
+ assert(!knot_rrset_empty(&bnorig));
+
+ // prepare a copy of NSEC3 rrsets in question
+ knot_rrset_t *bnnew = knot_rrset_copy(&bnorig, NULL);
+ if (bnnew == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ uint8_t raw_length = knot_nsec3_next_len(an.rrs.rdata);
+ uint8_t *a_hash = (uint8_t *)knot_nsec3_next(an.rrs.rdata);
+ uint8_t *bnew_hash = (uint8_t *)knot_nsec3_next(bnnew->rrs.rdata);
+ if (a_hash == NULL || bnew_hash == NULL ||
+ raw_length != knot_nsec3_next_len(bnnew->rrs.rdata)) {
+ knot_rrset_free(bnnew, NULL);
+ return KNOT_ERROR;
+ }
+ memcpy(bnew_hash, a_hash, raw_length);
+
+ int ret = zone_update_remove(data->update, &bnorig);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add(data->update, bnnew);
+ }
+ knot_rrset_free(bnnew, NULL);
+ return ret;
+}
+
+/*!
+ * \brief Create NSEC3 node for each regular node in the zone.
+ *
+ * \param zone Zone.
+ * \param params NSEC3 params.
+ * \param ttl TTL for the created NSEC records.
+ * \param cds_in_apex Hint to guess apex node type bitmap: false=just DNSKEY, true=DNSKEY,CDS,CDNSKEY.
+ * \param nsec3_nodes Tree whereto new NSEC3 nodes will be added.
+ * \param update Zone update for possible NSEC removals
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int create_nsec3_nodes(const zone_contents_t *zone,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl,
+ zone_tree_t *nsec3_nodes,
+ zone_update_t *update)
+{
+ assert(zone);
+ assert(nsec3_nodes);
+ assert(update);
+
+ zone_tree_delsafe_it_t it = { 0 };
+ int result = zone_tree_delsafe_it_begin(zone->nodes, &it, false); // delsafe - removing nodes that contain only NSEC+RRSIG
+
+ while (!zone_tree_delsafe_it_finished(&it)) {
+ zone_node_t *node = zone_tree_delsafe_it_val(&it);
+
+ /*!
+ * Remove possible NSEC from the node. (Do not allow both NSEC
+ * and NSEC3 in the zone at once.)
+ */
+ result = knot_nsec_changeset_remove(node, update);
+ if (result != KNOT_EOK) {
+ break;
+ }
+ if (node->flags & NODE_FLAGS_NONAUTH || nsec3_empty(node, params) || node->flags & NODE_FLAGS_DELETED) {
+ zone_tree_delsafe_it_next(&it);
+ continue;
+ }
+
+ zone_node_t *nsec3_node;
+ nsec3_node = create_nsec3_node_for_node(node, zone->apex,
+ params, ttl);
+ if (!nsec3_node) {
+ result = KNOT_ENOMEM;
+ break;
+ }
+
+ result = zone_tree_insert(nsec3_nodes, &nsec3_node);
+ if (result != KNOT_EOK) {
+ break;
+ }
+
+ zone_tree_delsafe_it_next(&it);
+ }
+
+ zone_tree_delsafe_it_free(&it);
+
+ return result;
+}
+
+/*!
+ * \brief For given dname, check if anything changed in zone_update, and recreate (possibly unconnected) NSEC3 nodes appropriately.
+ *
+ * \param update Zone update structure holding zone contents changes.
+ * \param params NSEC3 params.
+ * \param ttl TTL for newly created NSEC3 records.
+ * \param for_node Domain name of the node in question.
+ *
+ * \retval KNOT_ENORECORD if the NSEC3 chain shall be rather recreated completely.
+ * \return KNOT_EOK, KNOT_E* if any error.
+ */
+static int fix_nsec3_for_node(zone_update_t *update, const dnssec_nsec3_params_t *params,
+ uint32_t ttl, const knot_dname_t *for_node)
+{
+ // check if we need to do something
+ const zone_node_t *old_n = zone_contents_find_node(update->zone->contents, for_node);
+ const zone_node_t *new_n = zone_contents_find_node(update->new_cont, for_node);
+
+ bool had_no_nsec = (old_n == NULL || old_n->nsec3_node == NULL || !(old_n->flags & NODE_FLAGS_NSEC3_NODE));
+ bool shall_no_nsec = (new_n == NULL || new_n->flags & NODE_FLAGS_NONAUTH || nsec3_empty(new_n, params) || new_n->flags & NODE_FLAGS_DELETED);
+
+ if (had_no_nsec == shall_no_nsec && node_bitmap_equal(old_n, new_n)) {
+ return KNOT_EOK;
+ }
+
+ knot_dname_storage_t for_node_hashed;
+ int ret = knot_create_nsec3_owner(for_node_hashed, sizeof(for_node_hashed),
+ for_node, update->new_cont->apex->owner, params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // saved hash of next node
+ uint8_t *next_hash = NULL;
+ uint8_t next_length = 0;
+
+ // remove (all) existing NSEC3
+ const zone_node_t *old_nsec3_n = zone_contents_find_nsec3_node(update->new_cont, for_node_hashed);
+ assert((bool)(old_nsec3_n == NULL) == had_no_nsec);
+ if (old_nsec3_n != NULL) {
+ knot_rrset_t rem_nsec3 = node_rrset(old_nsec3_n, KNOT_RRTYPE_NSEC3);
+ if (!knot_rrset_empty(&rem_nsec3)) {
+ knot_rrset_t rem_rrsig = node_rrset(old_nsec3_n, KNOT_RRTYPE_RRSIG);
+ ret = zone_update_remove(update, &rem_nsec3);
+ if (ret == KNOT_EOK && !knot_rrset_empty(&rem_rrsig)) {
+ ret = zone_update_remove(update, &rem_rrsig);
+ }
+ assert(update->flags & UPDATE_INCREMENTAL); // to make sure the following pointer remains valid
+ next_hash = (uint8_t *)knot_nsec3_next(rem_nsec3.rrs.rdata);
+ next_length = knot_nsec3_next_len(rem_nsec3.rrs.rdata);
+ }
+ }
+
+ // add NSEC3 with correct bitmap
+ if (!shall_no_nsec && ret == KNOT_EOK) {
+ zone_node_t *new_nsec3_n = create_nsec3_node_for_node(new_n, update->new_cont->apex, params, ttl);
+ if (new_nsec3_n == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_t nsec3 = node_rrset(new_nsec3_n, KNOT_RRTYPE_NSEC3);
+ assert(!knot_rrset_empty(&nsec3));
+
+ // copy hash of next element from removed record
+ if (next_hash != NULL) {
+ uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(nsec3.rrs.rdata);
+ uint8_t raw_length = knot_nsec3_next_len(nsec3.rrs.rdata);
+ assert(raw_hash != NULL);
+ if (raw_length != next_length) {
+ ret = KNOT_EMALF;
+ } else {
+ memcpy(raw_hash, next_hash, raw_length);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add(update, &nsec3);
+ }
+ binode_unify(new_nsec3_n, false, NULL);
+ node_free_rrsets(new_nsec3_n, NULL);
+ node_free(new_nsec3_n, NULL);
+ }
+
+ return ret;
+}
+
+static int fix_nsec3_nodes(zone_update_t *update, const dnssec_nsec3_params_t *params,
+ uint32_t ttl)
+{
+ assert(update);
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin(update->a_ctx->node_ptrs, &it);
+
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ zone_node_t *n = zone_tree_it_val(&it);
+ ret = fix_nsec3_for_node(update, params, ttl, n->owner);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+
+ return ret;
+}
+
+static int zone_update_nsec3_nodes(zone_update_t *up, zone_tree_t *nsec3n)
+{
+ int ret = KNOT_EOK;
+ zone_tree_delsafe_it_t dit = { 0 };
+ zone_tree_it_t it = { 0 };
+ if (up->new_cont->nsec3_nodes == NULL) {
+ goto add_nsec3n;
+ }
+ ret = zone_tree_delsafe_it_begin(up->new_cont->nsec3_nodes, &dit, false);
+ while (ret == KNOT_EOK && !zone_tree_delsafe_it_finished(&dit)) {
+ zone_node_t *nold = zone_tree_delsafe_it_val(&dit);
+ knot_rrset_t ns3old = node_rrset(nold, KNOT_RRTYPE_NSEC3);
+ zone_node_t *nnew = zone_tree_get(nsec3n, nold->owner);
+ if (!knot_rrset_empty(&ns3old)) {
+ knot_rrset_t ns3new = node_rrset(nnew, KNOT_RRTYPE_NSEC3);
+ if (knot_rrset_equal(&ns3old, &ns3new, true)) {
+ node_remove_rdataset(nnew, KNOT_RRTYPE_NSEC3);
+ } else {
+ ret = knot_nsec_changeset_remove(nold, up);
+ }
+ } else if (node_rrtype_exists(nold, KNOT_RRTYPE_RRSIG)) {
+ ret = knot_nsec_changeset_remove(nold, up);
+ }
+ zone_tree_delsafe_it_next(&dit);
+ }
+ zone_tree_delsafe_it_free(&dit);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+add_nsec3n:
+ ret = zone_tree_it_begin(nsec3n, &it);
+ while (ret == KNOT_EOK && !zone_tree_it_finished(&it)) {
+ zone_node_t *nnew = zone_tree_it_val(&it);
+ knot_rrset_t ns3new = node_rrset(nnew, KNOT_RRTYPE_NSEC3);
+ if (!knot_rrset_empty(&ns3new)) {
+ ret = zone_update_add(up, &ns3new);
+ }
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
+
+/* - Public API ------------------------------------------------------------- */
+
+int delete_nsec3_chain(zone_update_t *up)
+{
+ zone_tree_t *empty = zone_tree_create(false);
+ if (empty == NULL) {
+ return KNOT_ENOMEM;
+ }
+ int ret = zone_update_nsec3_nodes(up, empty);
+ zone_tree_free(&empty);
+ return ret;
+}
+
+/*!
+ * \brief Create new NSEC3 chain, add differences from current into a changeset.
+ */
+int knot_nsec3_create_chain(const zone_contents_t *zone,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl,
+ zone_update_t *update)
+{
+ assert(zone);
+ assert(params);
+
+ zone_tree_t *nsec3_nodes = zone_tree_create(false);
+ if (!nsec3_nodes) {
+ return KNOT_ENOMEM;
+ }
+
+ int result = create_nsec3_nodes(zone, params, ttl, nsec3_nodes, update);
+ if (result != KNOT_EOK) {
+ free_nsec3_tree(nsec3_nodes);
+ return result;
+ }
+
+ result = knot_nsec_chain_iterate_create(nsec3_nodes,
+ connect_nsec3_nodes, NULL);
+ if (result != KNOT_EOK) {
+ free_nsec3_tree(nsec3_nodes);
+ return result;
+ }
+
+ result = zone_update_nsec3_nodes(update, nsec3_nodes);
+
+ free_nsec3_tree(nsec3_nodes);
+
+ return result;
+}
+
+int knot_nsec3_fix_chain(zone_update_t *update,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl)
+{
+ assert(update);
+ assert(params);
+
+ // ensure that the salt has not changed
+ if (!knot_nsec3param_uptodate(update->new_cont, params)) {
+ int ret = knot_nsec3param_update(update, params, ttl);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ return knot_nsec3_create_chain(update->new_cont, params, ttl, update);
+ }
+
+ int ret = fix_nsec3_nodes(update, params, ttl);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_adjust_contents(update->new_cont, NULL, adjust_cb_void, false, true, 1, update->a_ctx->nsec3_ptrs);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // ensure that nsec3 node for zone root is in list of changed nodes
+ const zone_node_t *nsec3_for_root = NULL, *unused;
+ ret = zone_contents_find_nsec3_for_name(update->new_cont, update->zone->name, &nsec3_for_root, &unused);
+ if (ret >= 0) {
+ assert(ret == ZONE_NAME_FOUND);
+ assert(!(nsec3_for_root->flags & NODE_FLAGS_DELETED));
+ assert(!(binode_counterpart((zone_node_t *)nsec3_for_root)->flags & NODE_FLAGS_DELETED));
+ ret = zone_tree_insert(update->a_ctx->nsec3_ptrs, (zone_node_t **)&nsec3_for_root);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ nsec_chain_iterate_data_t data = { ttl, update, KNOT_RRTYPE_NSEC3 };
+
+ ret = knot_nsec_chain_iterate_fix(update->a_ctx->nsec3_ptrs,
+ connect_nsec3_nodes2, reconnect_nsec3_nodes2, &data);
+
+ return ret;
+}
+
+int knot_nsec3_check_chain(zone_update_t *update, const dnssec_nsec3_params_t *params)
+{
+ nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC3, params };
+
+ int ret = nsec_check_bitmaps(update->new_cont->nodes, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return knot_nsec_chain_iterate_create(update->new_cont->nsec3_nodes,
+ nsec_check_connect_nodes, &data);
+}
+
+int knot_nsec3_check_chain_fix(zone_update_t *update, const dnssec_nsec3_params_t *params)
+{
+ nsec_chain_iterate_data_t data = { 0, update, KNOT_RRTYPE_NSEC3, params };
+
+ int ret = nsec_check_bitmaps(update->a_ctx->node_ptrs, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = nsec_check_bitmaps(update->a_ctx->adjust_ptrs, &data); // adjust_ptrs contain also NSEC3-nodes. See check_nsec_bitmap() how this is handled.
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return nsec_check_new_connects(update->a_ctx->nsec3_ptrs, &data);
+}
diff --git a/src/knot/dnssec/nsec3-chain.h b/src/knot/dnssec/nsec3-chain.h
new file mode 100644
index 0000000..5b3708f
--- /dev/null
+++ b/src/knot/dnssec/nsec3-chain.h
@@ -0,0 +1,69 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include "libdnssec/nsec.h"
+#include "knot/updates/changesets.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/contents.h"
+
+/*!
+ * \brief delete_nsec3_chain Delete all NSEC3 records and their RRSIGs.
+ */
+int delete_nsec3_chain(zone_update_t *up);
+
+/*!
+ * \brief Creates new NSEC3 chain, add differences from current into a changeset.
+ *
+ * \param zone Zone to be checked.
+ * \param params NSEC3 parameters.
+ * \param ttl TTL for new records.
+ * \param update Zone update to stare immediate changes into.
+ *
+ * \return KNOT_E*
+ */
+int knot_nsec3_create_chain(const zone_contents_t *zone,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl,
+ zone_update_t *update);
+
+/*!
+ * \brief Updates zone's NSEC3 chain to follow the differences in zone update.
+ *
+ * \param update Zone Update structure holding the zone and its update. Also modified!
+ * \param params NSEC3 parameters.
+ * \param ttl TTL for new records.
+ *
+ * \retval KNOT_ENORECORD if the chain must be recreated from scratch.
+ * \return KNOT_E*
+ */
+int knot_nsec3_fix_chain(zone_update_t *update,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl);
+
+/*!
+ * \brief Validate NSEC3 chain in new_cont as whole.
+ *
+ * \note new_cont must have been adjusted already!
+ */
+int knot_nsec3_check_chain(zone_update_t *update, const dnssec_nsec3_params_t *params);
+
+/*!
+ * \brief Validate NSEC3 chain in new_cont incrementally.
+ */
+int knot_nsec3_check_chain_fix(zone_update_t *update, const dnssec_nsec3_params_t *params);
diff --git a/src/knot/dnssec/policy.c b/src/knot/dnssec/policy.c
new file mode 100644
index 0000000..2589ae6
--- /dev/null
+++ b/src/knot/dnssec/policy.c
@@ -0,0 +1,51 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/dnssec/policy.h"
+#include "libknot/rrtype/soa.h"
+
+static uint32_t zone_soa_ttl(const zone_contents_t *zone)
+{
+ knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
+ return soa.ttl;
+}
+
+void update_policy_from_zone(knot_kasp_policy_t *policy,
+ const zone_contents_t *zone)
+{
+ assert(policy);
+ assert(zone);
+
+ if (policy->dnskey_ttl == UINT32_MAX) {
+ policy->dnskey_ttl = zone_soa_ttl(zone);
+ }
+ if (policy->saved_key_ttl == 0) { // possibly not set yet
+ policy->saved_key_ttl = policy->dnskey_ttl;
+ }
+
+ if (policy->zone_maximal_ttl == UINT32_MAX) {
+ policy->zone_maximal_ttl = zone->max_ttl;
+ if (policy->rrsig_refresh_before == UINT32_MAX) {
+ policy->rrsig_refresh_before = policy->propagation_delay +
+ policy->zone_maximal_ttl;
+ }
+ }
+ if (policy->saved_max_ttl == 0) { // possibly not set yet
+ policy->saved_max_ttl = policy->zone_maximal_ttl;
+ }
+}
diff --git a/src/knot/dnssec/policy.h b/src/knot/dnssec/policy.h
new file mode 100644
index 0000000..8c0149b
--- /dev/null
+++ b/src/knot/dnssec/policy.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "knot/dnssec/context.h"
+#include "knot/zone/contents.h"
+
+/*!
+ * \brief Update policy parameters depending on zone content.
+ */
+void update_policy_from_zone(knot_kasp_policy_t *policy,
+ const zone_contents_t *zone);
diff --git a/src/knot/dnssec/rrset-sign.c b/src/knot/dnssec/rrset-sign.c
new file mode 100644
index 0000000..3522a24
--- /dev/null
+++ b/src/knot/dnssec/rrset-sign.c
@@ -0,0 +1,425 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "contrib/wire_ctx.h"
+#include "libdnssec/error.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/zone/serial.h" // DNS uint32 arithmetics
+#include "libknot/libknot.h"
+
+#define RRSIG_RDATA_SIGNER_OFFSET 18
+
+#define RRSIG_INCEPT_IN_PAST (90 * 60)
+
+/*- Creating of RRSIGs -------------------------------------------------------*/
+
+/*!
+ * \brief Get size of RRSIG RDATA for a given key without signature.
+ */
+static size_t rrsig_rdata_header_size(const dnssec_key_t *key)
+{
+ if (!key) {
+ return 0;
+ }
+
+ size_t size;
+
+ // static part
+
+ size = sizeof(uint16_t) // type covered
+ + sizeof(uint8_t) // algorithm
+ + sizeof(uint8_t) // labels
+ + sizeof(uint32_t) // original TTL
+ + sizeof(uint32_t) // signature expiration
+ + sizeof(uint32_t) // signature inception
+ + sizeof(uint16_t); // key tag (footprint)
+
+ assert(size == RRSIG_RDATA_SIGNER_OFFSET);
+
+ // variable part
+
+ size += knot_dname_size(dnssec_key_get_dname(key));
+
+ return size;
+}
+
+/*!
+ * \brief Write RRSIG RDATA except signature.
+ *
+ * \note This can be also used for SIG(0) if proper parameters are supplied.
+ *
+ * \param rdata_len Length of RDATA.
+ * \param rdata Pointer to RDATA.
+ * \param key Key used for signing.
+ * \param covered_type Type of the covered RR.
+ * \param owner_labels Number of labels covered by the signature.
+ * \param sig_incepted Timestamp of signature inception.
+ * \param sig_expires Timestamp of signature expiration.
+ */
+static int rrsig_write_rdata(uint8_t *rdata, size_t rdata_len,
+ const dnssec_key_t *key,
+ uint16_t covered_type, uint8_t owner_labels,
+ uint32_t owner_ttl, uint32_t sig_incepted,
+ uint32_t sig_expires)
+{
+ if (!rdata || !key || serial_compare(sig_incepted, sig_expires) != SERIAL_LOWER) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t algorithm = dnssec_key_get_algorithm(key);
+ uint16_t keytag = dnssec_key_get_keytag(key);
+ const uint8_t *signer = dnssec_key_get_dname(key);
+ assert(signer);
+
+ wire_ctx_t wire = wire_ctx_init(rdata, rdata_len);
+
+ wire_ctx_write_u16(&wire, covered_type); // type covered
+ wire_ctx_write_u8(&wire, algorithm); // algorithm
+ wire_ctx_write_u8(&wire, owner_labels); // labels
+ wire_ctx_write_u32(&wire, owner_ttl); // original TTL
+ wire_ctx_write_u32(&wire, sig_expires); // signature expiration
+ wire_ctx_write_u32(&wire, sig_incepted); // signature inception
+ wire_ctx_write_u16(&wire, keytag); // key fingerprint
+ assert(wire_ctx_offset(&wire) == RRSIG_RDATA_SIGNER_OFFSET);
+ wire_ctx_write(&wire, signer, knot_dname_size(signer)); // signer
+
+ return wire.error;
+}
+
+/*- Computation of signatures ------------------------------------------------*/
+
+/*!
+ * \brief Add RRSIG RDATA without signature to signing context.
+ *
+ * Requires signer name in RDATA in canonical form.
+ *
+ * \param ctx Signing context.
+ * \param rdata Pointer to RRSIG RDATA.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_ctx_add_self(dnssec_sign_ctx_t *ctx, const uint8_t *rdata)
+{
+ assert(ctx);
+ assert(rdata);
+
+ int result;
+
+ // static header
+
+ dnssec_binary_t header = { 0 };
+ header.data = (uint8_t *)rdata;
+ header.size = RRSIG_RDATA_SIGNER_OFFSET;
+
+ result = dnssec_sign_add(ctx, &header);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ // signer name
+
+ const uint8_t *rdata_signer = rdata + RRSIG_RDATA_SIGNER_OFFSET;
+ dnssec_binary_t signer = { 0 };
+ signer.data = knot_dname_copy(rdata_signer, NULL);
+ signer.size = knot_dname_size(signer.data);
+
+ result = dnssec_sign_add(ctx, &signer);
+ free(signer.data);
+
+ return result;
+}
+
+/*!
+ * \brief Add covered RRs to signing context.
+ *
+ * Requires all DNAMEs in canonical form and all RRs ordered canonically.
+ *
+ * \param ctx Signing context.
+ * \param covered Covered RRs.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered)
+{
+ // huge block of rrsets can be optionally created
+ uint8_t *rrwf = malloc(KNOT_WIRE_MAX_PKTSIZE);
+ if (!rrwf) {
+ return KNOT_ENOMEM;
+ }
+
+ int written = knot_rrset_to_wire(covered, rrwf, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (written < 0) {
+ free(rrwf);
+ return written;
+ }
+
+ dnssec_binary_t rrset_wire = { 0 };
+ rrset_wire.size = written;
+ rrset_wire.data = rrwf;
+ int result = dnssec_sign_add(ctx, &rrset_wire);
+ free(rrwf);
+
+ return result;
+}
+
+int knot_sign_ctx_add_data(dnssec_sign_ctx_t *ctx,
+ const uint8_t *rrsig_rdata,
+ const knot_rrset_t *covered)
+{
+ if (!ctx || !rrsig_rdata || knot_rrset_empty(covered)) {
+ return KNOT_EINVAL;
+ }
+
+ int result = sign_ctx_add_self(ctx, rrsig_rdata);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ return sign_ctx_add_records(ctx, covered);
+}
+
+/*!
+ * \brief Create RRSIG RDATA.
+ *
+ * \param[in] rrsigs RR set with RRSIGS.
+ * \param[in] ctx DNSSEC signing context.
+ * \param[in] covered RR covered by the signature.
+ * \param[in] key Key used for signing.
+ * \param[in] sig_incepted Timestamp of signature inception.
+ * \param[in] sig_expires Timestamp of signature expiration.
+ * \param[in] sign_flags Signing flags.
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int rrsigs_create_rdata(knot_rrset_t *rrsigs, dnssec_sign_ctx_t *ctx,
+ const knot_rrset_t *covered,
+ const dnssec_key_t *key,
+ uint32_t sig_incepted, uint32_t sig_expires,
+ dnssec_sign_flags_t sign_flags,
+ knot_mm_t *mm)
+{
+ assert(rrsigs);
+ assert(rrsigs->type == KNOT_RRTYPE_RRSIG);
+ assert(!knot_rrset_empty(covered));
+ assert(key);
+
+ size_t header_size = rrsig_rdata_header_size(key);
+ assert(header_size != 0);
+
+ uint8_t owner_labels = knot_dname_labels(covered->owner, NULL);
+ if (knot_dname_is_wildcard(covered->owner)) {
+ owner_labels -= 1;
+ }
+
+ uint8_t header[header_size];
+ int res = rrsig_write_rdata(header, header_size,
+ key, covered->type, owner_labels,
+ covered->ttl, sig_incepted, sig_expires);
+ assert(res == KNOT_EOK);
+
+ res = dnssec_sign_init(ctx);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ res = knot_sign_ctx_add_data(ctx, header, covered);
+ if (res != KNOT_EOK) {
+ return res;
+ }
+
+ dnssec_binary_t signature = { 0 };
+ res = dnssec_sign_write(ctx, sign_flags, &signature);
+ if (res != DNSSEC_EOK) {
+ return res;
+ }
+ assert(signature.size > 0);
+
+ size_t rrsig_size = header_size + signature.size;
+ uint8_t rrsig[rrsig_size];
+ memcpy(rrsig, header, header_size);
+ memcpy(rrsig + header_size, signature.data, signature.size);
+
+ dnssec_binary_free(&signature);
+
+ return knot_rrset_add_rdata(rrsigs, rrsig, rrsig_size, mm);
+}
+
+int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered,
+ const dnssec_key_t *key, dnssec_sign_ctx_t *sign_ctx,
+ const kdnssec_ctx_t *dnssec_ctx, knot_mm_t *mm, knot_time_t *expires)
+{
+ if (knot_rrset_empty(covered) || !key || !sign_ctx || !dnssec_ctx ||
+ rrsigs->type != KNOT_RRTYPE_RRSIG ||
+ !knot_dname_is_equal(rrsigs->owner, covered->owner)
+ ) {
+ return KNOT_EINVAL;
+ }
+
+ uint64_t sig_incept = dnssec_ctx->now - RRSIG_INCEPT_IN_PAST;
+ uint64_t sig_expire = dnssec_ctx->now + dnssec_ctx->policy->rrsig_lifetime;
+ dnssec_sign_flags_t sign_flags = dnssec_ctx->policy->reproducible_sign ?
+ DNSSEC_SIGN_REPRODUCIBLE : DNSSEC_SIGN_NORMAL;
+
+ int ret = rrsigs_create_rdata(rrsigs, sign_ctx, covered, key, (uint32_t)sig_incept,
+ (uint32_t)sig_expire, sign_flags, mm);
+ if (ret == KNOT_EOK && expires != NULL) {
+ *expires = knot_time_min(*expires, sig_expire);
+ }
+ return ret;
+}
+
+int knot_sign_rrset2(knot_rrset_t *rrsigs, const knot_rrset_t *rrset,
+ zone_sign_ctx_t *sign_ctx, knot_mm_t *mm)
+{
+ if (rrsigs == NULL || rrset == NULL || sign_ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ for (size_t i = 0; i < sign_ctx->count; i++) {
+ zone_key_t *key = &sign_ctx->keys[i];
+
+ if (!knot_zone_sign_use_key(key, rrset)) {
+ continue;
+ }
+
+ int ret = knot_sign_rrset(rrsigs, rrset, key->key, sign_ctx->sign_ctxs[i],
+ sign_ctx->dnssec_ctx, mm, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_synth_rrsig(uint16_t type, const knot_rdataset_t *rrsig_rrs,
+ knot_rdataset_t *out_sig, knot_mm_t *mm)
+{
+ if (rrsig_rrs == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ if (out_sig == NULL || out_sig->count > 0) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rr_to_copy = rrsig_rrs->rdata;
+ for (int i = 0; i < rrsig_rrs->count; ++i) {
+ if (type == KNOT_RRTYPE_ANY) {
+ type = knot_rrsig_type_covered(rr_to_copy);
+ }
+ if (type == knot_rrsig_type_covered(rr_to_copy)) {
+ int ret = knot_rdataset_add(out_sig, rr_to_copy, mm);
+ if (ret != KNOT_EOK) {
+ knot_rdataset_clear(out_sig, mm);
+ return ret;
+ }
+ }
+ rr_to_copy = knot_rdataset_next(rr_to_copy);
+ }
+
+ return out_sig->count > 0 ? KNOT_EOK : KNOT_ENOENT;
+}
+
+bool knot_synth_rrsig_exists(uint16_t type, const knot_rdataset_t *rrsig_rrs)
+{
+ if (rrsig_rrs == NULL) {
+ return false;
+ }
+
+ knot_rdata_t *rr = rrsig_rrs->rdata;
+ for (int i = 0; i < rrsig_rrs->count; ++i) {
+ if (type == knot_rrsig_type_covered(rr)) {
+ return true;
+ }
+ rr = knot_rdataset_next(rr);
+ }
+
+ return false;
+}
+
+/*- Verification of signatures -----------------------------------------------*/
+
+static bool is_expired_signature(const knot_rdata_t *rrsig, knot_time_t now,
+ uint32_t refresh_before)
+{
+ assert(rrsig);
+
+ uint32_t expire32 = knot_rrsig_sig_expiration(rrsig);
+ uint32_t incept32 = knot_rrsig_sig_inception(rrsig);
+ knot_time_t expire64 = knot_time_from_u32(expire32, now);
+ knot_time_t incept64 = knot_time_from_u32(incept32, now);
+
+ return now >= expire64 - refresh_before || now < incept64;
+}
+
+int knot_check_signature(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs, size_t pos,
+ const dnssec_key_t *key,
+ dnssec_sign_ctx_t *sign_ctx,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_timediff_t refresh,
+ bool skip_crypto)
+{
+ if (knot_rrset_empty(covered) || knot_rrset_empty(rrsigs) || !key ||
+ !sign_ctx || !dnssec_ctx) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rrsig = knot_rdataset_at(&rrsigs->rrs, pos);
+ assert(rrsig);
+
+ if (!(dnssec_ctx->policy->unsafe & UNSAFE_EXPIRED) &&
+ is_expired_signature(rrsig, dnssec_ctx->now, refresh)) {
+ return DNSSEC_INVALID_SIGNATURE;
+ }
+
+ if (skip_crypto) {
+ return KNOT_EOK;
+ }
+
+ // identify fields in the signature being validated
+
+ dnssec_binary_t signature = {
+ .size = knot_rrsig_signature_len(rrsig),
+ .data = (uint8_t *)knot_rrsig_signature(rrsig)
+ };
+ if (signature.data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // perform the validation
+
+ int result = dnssec_sign_init(sign_ctx);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ result = knot_sign_ctx_add_data(sign_ctx, rrsig->data, covered);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ bool sign_cmp = dnssec_algorithm_reproducible(
+ dnssec_ctx->policy->algorithm,
+ dnssec_ctx->policy->reproducible_sign);
+
+ return dnssec_sign_verify(sign_ctx, sign_cmp, &signature);
+}
diff --git a/src/knot/dnssec/rrset-sign.h b/src/knot/dnssec/rrset-sign.h
new file mode 100644
index 0000000..8e00402
--- /dev/null
+++ b/src/knot/dnssec/rrset-sign.h
@@ -0,0 +1,123 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "libdnssec/key.h"
+#include "libdnssec/sign.h"
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/zone-keys.h"
+#include "libknot/rrset.h"
+
+/*!
+ * \brief Create RRSIG RR for given RR set.
+ *
+ * \param rrsigs RR set with RRSIGs into which the result will be added.
+ * \param covered RR set to create a new signature for.
+ * \param key Signing key.
+ * \param sign_ctx Signing context.
+ * \param dnssec_ctx DNSSEC context.
+ * \param mm Memory context.
+ * \param expires Out: When will the new RRSIG expire.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_sign_rrset(knot_rrset_t *rrsigs,
+ const knot_rrset_t *covered,
+ const dnssec_key_t *key,
+ dnssec_sign_ctx_t *sign_ctx,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_mm_t *mm,
+ knot_time_t *expires);
+
+/*!
+ * \brief Create RRSIG RR for given RR set, choose which key to use.
+ *
+ * \param rrsigs RR set with RRSIGs into which the result will be added.
+ * \param rrset RR set to create a new signature for.
+ * \param sign_ctx Zone signing context.
+ * \param mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_sign_rrset2(knot_rrset_t *rrsigs,
+ const knot_rrset_t *rrset,
+ zone_sign_ctx_t *sign_ctx,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Add all data covered by signature into signing context.
+ *
+ * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature)
+ * and all matching RR records, which are ordered canonically.
+ *
+ * Requires all DNAMEs in canonical form and all RRs ordered canonically.
+ *
+ * \param ctx Signing context.
+ * \param rrsig_rdata RRSIG RDATA with populated fields except signature.
+ * \param covered Covered RRs.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_sign_ctx_add_data(dnssec_sign_ctx_t *ctx,
+ const uint8_t *rrsig_rdata,
+ const knot_rrset_t *covered);
+
+/*!
+ * \brief Creates new RRS using \a rrsig_rrs as a source. Only those RRs that
+ * cover given \a type are copied into \a out_sig
+ *
+ * \note If given \a type is ANY, put a random subset, not all.
+ *
+ * \param type Covered type.
+ * \param rrsig_rrs Source RRS.
+ * \param out_sig Output RRS.
+ * \param mm Memory context.
+ *
+ * \retval KNOT_EOK if some RRSIG was found.
+ * \retval KNOT_EINVAL if no RRSIGs were found.
+ * \retval Error code other than EINVAL on error.
+ */
+int knot_synth_rrsig(uint16_t type, const knot_rdataset_t *rrsig_rrs,
+ knot_rdataset_t *out_sig, knot_mm_t *mm);
+
+/*!
+ * \brief Determines if a RRSIG exists, covering the specified type.
+ */
+bool knot_synth_rrsig_exists(uint16_t type, const knot_rdataset_t *rrsig_rrs);
+
+/*!
+ * \brief Check if RRSIG signature is valid.
+ *
+ * \param covered RRs covered by the signature.
+ * \param rrsigs RR set with RRSIGs.
+ * \param pos Number of RRSIG RR in 'rrsigs' to be validated.
+ * \param key Signing key.
+ * \param sign_ctx Signing context.
+ * \param dnssec_ctx DNSSEC context.
+ * \param refresh Consider RRSIG expired when gonna expire this soon.
+ * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
+ *
+ * \return Error code, KNOT_EOK if successful and the signature is valid.
+ * \retval KNOT_DNSSEC_EINVALID_SIGNATURE The signature is invalid.
+ */
+int knot_check_signature(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs, size_t pos,
+ const dnssec_key_t *key,
+ dnssec_sign_ctx_t *sign_ctx,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_timediff_t refresh,
+ bool skip_crypto);
diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c
new file mode 100644
index 0000000..22a2a76
--- /dev/null
+++ b/src/knot/dnssec/zone-events.c
@@ -0,0 +1,470 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+#include "libknot/libknot.h"
+#include "knot/conf/conf.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/key_records.h"
+#include "knot/dnssec/policy.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/digest.h"
+
+static knot_time_t schedule_next(kdnssec_ctx_t *kctx, const zone_keyset_t *keyset,
+ knot_time_t keys_expire, knot_time_t rrsigs_expire)
+{
+ knot_time_t rrsigs_refresh = knot_time_add(rrsigs_expire, -(knot_timediff_t)kctx->policy->rrsig_refresh_before);
+ knot_time_t zone_refresh = knot_time_min(keys_expire, rrsigs_refresh);
+
+ knot_time_t dnskey_update = knot_get_next_zone_key_event(keyset);
+ knot_time_t next = knot_time_min(zone_refresh, dnskey_update);
+
+ return next;
+}
+
+static int generate_salt(dnssec_binary_t *salt, uint16_t length)
+{
+ assert(salt);
+ dnssec_binary_t new_salt = { 0 };
+
+ if (length > 0) {
+ int r = dnssec_binary_alloc(&new_salt, length);
+ if (r != KNOT_EOK) {
+ return knot_error_from_libdnssec(r);
+ }
+
+ r = dnssec_random_binary(&new_salt);
+ if (r != KNOT_EOK) {
+ dnssec_binary_free(&new_salt);
+ return knot_error_from_libdnssec(r);
+ }
+ }
+
+ dnssec_binary_free(salt);
+ *salt = new_salt;
+
+ return KNOT_EOK;
+}
+
+int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool soa_rrsigs_ok,
+ knot_time_t *salt_changed, knot_time_t *when_resalt)
+{
+ int ret = KNOT_EOK;
+
+ if (!ctx->policy->nsec3_enabled) {
+ return KNOT_EOK;
+ }
+
+ if (ctx->policy->nsec3_salt_lifetime < 0 && !soa_rrsigs_ok) {
+ *when_resalt = ctx->now;
+ } else if (ctx->zone->nsec3_salt.size != ctx->policy->nsec3_salt_length || ctx->zone->nsec3_salt_created == 0) {
+ *when_resalt = ctx->now;
+ } else if (knot_time_cmp(ctx->now, ctx->zone->nsec3_salt_created) < 0) {
+ return KNOT_EINVAL;
+ } else if (ctx->policy->nsec3_salt_lifetime > 0) {
+ *when_resalt = knot_time_plus(ctx->zone->nsec3_salt_created, ctx->policy->nsec3_salt_lifetime);
+ }
+
+ if (knot_time_cmp(*when_resalt, ctx->now) <= 0) {
+ if (ctx->policy->nsec3_salt_length == 0) {
+ ctx->zone->nsec3_salt.size = 0;
+ ctx->zone->nsec3_salt_created = ctx->now;
+ *salt_changed = ctx->now;
+ *when_resalt = 0;
+ return kdnssec_ctx_commit(ctx);
+ }
+
+ ret = generate_salt(&ctx->zone->nsec3_salt, ctx->policy->nsec3_salt_length);
+ if (ret == KNOT_EOK) {
+ ctx->zone->nsec3_salt_created = ctx->now;
+ ret = kdnssec_ctx_commit(ctx);
+ *salt_changed = ctx->now;
+ }
+ // continue to planning next resalt even if NOK
+ if (ctx->policy->nsec3_salt_lifetime > 0) {
+ *when_resalt = knot_time_plus(ctx->now, ctx->policy->nsec3_salt_lifetime);
+ }
+ }
+
+ return ret;
+}
+
+static int check_offline_records(kdnssec_ctx_t *ctx)
+{
+ if (!ctx->policy->offline_ksk) {
+ return KNOT_EOK;
+ }
+
+ if (ctx->offline_records.dnskey.rrs.count == 0 ||
+ ctx->offline_records.rrsig.rrs.count == 0) {
+ log_zone_error(ctx->zone->dname,
+ "DNSSEC, no offline KSK records available");
+ return KNOT_ENOENT;
+ }
+
+ int ret;
+ knot_time_t last;
+ if (ctx->offline_next_time == 0) {
+ log_zone_warning(ctx->zone->dname,
+ "DNSSEC, using last offline KSK records available, "
+ "import new SKR before RRSIGs expire");
+ } else if ((ret = key_records_last_timestamp(ctx, &last)) != KNOT_EOK) {
+ log_zone_error(ctx->zone->dname,
+ "DNSSEC, failed to load offline KSK records (%s)",
+ knot_strerror(ret));
+ } else if (knot_time_diff(last, ctx->now) < 7 * 24 * 3600) {
+ log_zone_notice(ctx->zone->dname,
+ "DNSSEC, having offline KSK records for less than "
+ "a week, import new SKR");
+ }
+
+ return KNOT_EOK;
+}
+
+int knot_dnssec_zone_sign(zone_update_t *update,
+ conf_t *conf,
+ zone_sign_flags_t flags,
+ zone_sign_roll_flags_t roll_flags,
+ knot_time_t adjust_now,
+ zone_sign_reschedule_t *reschedule)
+{
+ if (!update || !reschedule) {
+ return KNOT_EINVAL;
+ }
+
+ const knot_dname_t *zone_name = update->new_cont->apex->owner;
+ kdnssec_ctx_t ctx = { 0 };
+ zone_keyset_t keyset = { 0 };
+ knot_time_t zone_expire = 0;
+
+ int result = kdnssec_ctx_init(conf, &ctx, zone_name, zone_kaspdb(update->zone), NULL);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to initialize signing context (%s)",
+ knot_strerror(result));
+ return result;
+ }
+ if (adjust_now) {
+ ctx.now = adjust_now;
+ }
+
+ // update policy based on the zone content
+ update_policy_from_zone(ctx.policy, update->new_cont);
+
+ if (ctx.policy->rrsig_refresh_before < ctx.policy->zone_maximal_ttl + ctx.policy->propagation_delay) {
+ log_zone_error(zone_name, "DNSSEC, rrsig-refresh too low to prevent expired RRSIGs in resolver caches");
+ result = KNOT_EINVAL;
+ goto done;
+ }
+ if (ctx.policy->rrsig_lifetime <= ctx.policy->rrsig_refresh_before) {
+ log_zone_error(zone_name, "DNSSEC, rrsig-lifetime lower than rrsig-refresh");
+ result = KNOT_EINVAL;
+ goto done;
+ }
+
+ // perform key rollover if needed
+ result = knot_dnssec_key_rollover(&ctx, roll_flags, reschedule);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update key set (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ ctx.rrsig_drop_existing = flags & ZONE_SIGN_DROP_SIGNATURES;
+
+ conf_val_t val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone_name);
+ unsigned zonemd_alg = conf_opt(&val);
+ if (zonemd_alg != ZONE_DIGEST_NONE) {
+ result = zone_update_add_digest(update, zonemd_alg, true);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to reserve dummy ZONEMD (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ uint32_t ms;
+ if (zone_is_slave(conf, update->zone) && zone_get_master_serial(update->zone, &ms) == KNOT_ENOENT) {
+ // zone had been XFRed before on-slave-signing turned on
+ zone_set_master_serial(update->zone, zone_contents_serial(update->new_cont));
+ }
+
+ result = load_zone_keys(&ctx, &keyset, true);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ // perform nsec3resalt if pending
+ if (roll_flags & KEY_ROLL_ALLOW_NSEC3RESALT) {
+ knot_rdataset_t *rrsig = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
+ bool issbaz = is_soa_signed_by_all_zsks(&keyset, rrsig);
+ result = knot_dnssec_nsec3resalt(&ctx, issbaz, &reschedule->last_nsec3resalt, &reschedule->next_nsec3resalt);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update NSEC3 salt (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ log_zone_info(zone_name, "DNSSEC, signing started");
+
+ result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update DNSKEY records (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ result = zone_adjust_contents(update->new_cont, adjust_cb_flags, NULL,
+ false, false, 1, update->a_ctx->node_ptrs);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ result = knot_zone_create_nsec_chain(update, &ctx);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to create NSEC%s chain (%s)",
+ ctx.policy->nsec3_enabled ? "3" : "",
+ knot_strerror(result));
+ goto done;
+ }
+
+ result = check_offline_records(&ctx);
+ if (result != KNOT_EOK) {
+ goto done;
+ }
+
+ result = knot_zone_sign(update, &keyset, &ctx, &zone_expire);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to sign zone content (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ // SOA finishing
+
+ if (zone_update_no_change(update)) {
+ log_zone_info(zone_name, "DNSSEC, zone is up-to-date");
+ update->zone->zonefile.resigned = false;
+ goto done;
+ } else {
+ update->zone->zonefile.resigned = true;
+ }
+
+ if (!(flags & ZONE_SIGN_KEEP_SERIAL) && zone_update_to(update) == NULL) {
+ result = zone_update_increment_soa(update, conf);
+ if (result == KNOT_EOK) {
+ result = knot_zone_sign_apex_rr(update, KNOT_RRTYPE_SOA, &keyset, &ctx);
+ }
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update SOA record (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ if (zonemd_alg != ZONE_DIGEST_NONE) {
+ result = zone_update_add_digest(update, zonemd_alg, false);
+ if (result == KNOT_EOK) {
+ result = knot_zone_sign_apex_rr(update, KNOT_RRTYPE_ZONEMD, &keyset, &ctx);
+ }
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update ZONEMD record (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ log_zone_info(zone_name, "DNSSEC, successfully signed");
+
+done:
+ if (result == KNOT_EOK) {
+ reschedule->next_sign = schedule_next(&ctx, &keyset, ctx.offline_next_time, zone_expire);
+ } else {
+ reschedule->next_sign = knot_dnssec_failover_delay(&ctx);
+ reschedule->next_rollover = 0;
+ }
+
+ free_zone_keys(&keyset);
+ kdnssec_ctx_deinit(&ctx);
+
+ return result;
+}
+
+int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf)
+{
+ if (update == NULL || conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const knot_dname_t *zone_name = update->new_cont->apex->owner;
+ kdnssec_ctx_t ctx = { 0 };
+ zone_keyset_t keyset = { 0 };
+ knot_time_t zone_expire = 0;
+
+ int result = kdnssec_ctx_init(conf, &ctx, zone_name, zone_kaspdb(update->zone), NULL);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to initialize signing context (%s)",
+ knot_strerror(result));
+ return result;
+ }
+
+ update_policy_from_zone(ctx.policy, update->new_cont);
+
+ conf_val_t val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone_name);
+ unsigned zonemd_alg = conf_opt(&val);
+ if (zonemd_alg != ZONE_DIGEST_NONE) {
+ result = zone_update_add_digest(update, zonemd_alg, true);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to reserve dummy ZONEMD (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ result = load_zone_keys(&ctx, &keyset, false);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ if (zone_update_changes_dnskey(update)) {
+ result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update DNSKEY records (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ result = zone_adjust_contents(update->new_cont, adjust_cb_flags, NULL,
+ false, false, 1, update->a_ctx->node_ptrs);
+ if (result != KNOT_EOK) {
+ goto done;
+ }
+
+ result = check_offline_records(&ctx);
+ if (result != KNOT_EOK) {
+ goto done;
+ }
+
+ result = knot_zone_sign_update(update, &keyset, &ctx, &zone_expire);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to sign changeset (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ result = knot_zone_fix_nsec_chain(update, &keyset, &ctx);
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to fix NSEC%s chain (%s)",
+ ctx.policy->nsec3_enabled ? "3" : "",
+ knot_strerror(result));
+ goto done;
+ }
+
+ bool soa_changed = (knot_soa_serial(node_rdataset(update->zone->contents->apex, KNOT_RRTYPE_SOA)->rdata) !=
+ knot_soa_serial(node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA)->rdata));
+
+ if (zone_update_no_change(update) && !soa_changed) {
+ log_zone_info(zone_name, "DNSSEC, zone is up-to-date");
+ update->zone->zonefile.resigned = false;
+ goto done;
+ } else {
+ update->zone->zonefile.resigned = true;
+ }
+
+ if (!soa_changed) {
+ // incrementing SOA just of it has not been modified by the update
+ result = zone_update_increment_soa(update, conf);
+ }
+ if (result == KNOT_EOK) {
+ result = knot_zone_sign_apex_rr(update, KNOT_RRTYPE_SOA, &keyset, &ctx);
+ }
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update SOA record (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+
+ if (zonemd_alg != ZONE_DIGEST_NONE) {
+ result = zone_update_add_digest(update, zonemd_alg, false);
+ if (result == KNOT_EOK) {
+ result = knot_zone_sign_apex_rr(update, KNOT_RRTYPE_ZONEMD, &keyset, &ctx);
+ }
+ if (result != KNOT_EOK) {
+ log_zone_error(zone_name, "DNSSEC, failed to update ZONEMD record (%s)",
+ knot_strerror(result));
+ goto done;
+ }
+ }
+
+ log_zone_info(zone_name, "DNSSEC, incrementally signed");
+
+done:
+ if (result == KNOT_EOK) {
+ knot_time_t next = knot_time_min(ctx.offline_next_time, zone_expire);
+ // NOTE: this is usually NOOP since signing planned earlier
+ zone_events_schedule_at(update->zone, ZONE_EVENT_DNSSEC, next ? next : -1);
+ }
+
+ free_zone_keys(&keyset);
+ kdnssec_ctx_deinit(&ctx);
+
+ return result;
+}
+
+knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx)
+{
+ if (ctx->policy == NULL) {
+ return ctx->now + 3600; // failed before allocating ctx->policy, use default
+ } else {
+ return ctx->now + ctx->policy->rrsig_prerefresh;
+ }
+}
+
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental)
+{
+ kdnssec_ctx_t ctx = { 0 };
+ int ret = kdnssec_validation_ctx(conf, &ctx, update->new_cont);
+ if (now != 0) {
+ ctx.now = now;
+ }
+ if (ret == KNOT_EOK) {
+ ret = knot_zone_check_nsec_chain(update, &ctx, incremental);
+ }
+ if (ret == KNOT_EOK) {
+ knot_time_t unused = 0;
+ assert(ctx.validation_mode);
+ if (incremental) {
+ ret = knot_zone_sign_update(update, NULL, &ctx, &unused);
+ } else {
+ ret = knot_zone_sign(update, NULL, &ctx, &unused);
+ }
+ }
+ kdnssec_ctx_deinit(&ctx);
+ return ret;
+}
diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h
new file mode 100644
index 0000000..d3667f3
--- /dev/null
+++ b/src/knot/dnssec/zone-events.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <time.h>
+
+#include "knot/zone/zone.h"
+#include "knot/updates/changesets.h"
+#include "knot/updates/zone-update.h"
+#include "knot/dnssec/context.h"
+
+enum zone_sign_flags {
+ ZONE_SIGN_NONE = 0,
+ ZONE_SIGN_DROP_SIGNATURES = (1 << 0),
+ ZONE_SIGN_KEEP_SERIAL = (1 << 1),
+};
+
+typedef enum zone_sign_flags zone_sign_flags_t;
+
+typedef enum {
+ KEY_ROLL_ALLOW_KSK_ROLL = (1 << 0),
+ KEY_ROLL_FORCE_KSK_ROLL = (1 << 1),
+ KEY_ROLL_ALLOW_ZSK_ROLL = (1 << 2),
+ KEY_ROLL_FORCE_ZSK_ROLL = (1 << 3),
+ KEY_ROLL_ALLOW_NSEC3RESALT = (1 << 4),
+ KEY_ROLL_ALLOW_ALL = KEY_ROLL_ALLOW_KSK_ROLL |
+ KEY_ROLL_ALLOW_ZSK_ROLL |
+ KEY_ROLL_ALLOW_NSEC3RESALT
+} zone_sign_roll_flags_t;
+
+typedef struct {
+ knot_time_t next_sign;
+ knot_time_t next_rollover;
+ knot_time_t next_nsec3resalt;
+ knot_time_t last_nsec3resalt;
+ bool keys_changed;
+ bool plan_ds_check;
+} zone_sign_reschedule_t;
+
+/*!
+ * \brief Generate/rollover keys in keystore as needed.
+ *
+ * \param kctx Pointers to the keytore, policy, etc.
+ * \param zone_name Zone name.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_sign_process_events(const kdnssec_ctx_t *kctx,
+ const knot_dname_t *zone_name);
+
+/*!
+ * \brief DNSSEC re-sign zone, store new records into changeset. Valid signatures
+ * and NSEC(3) records will not be changed.
+ *
+ * \param update Zone Update structure with current zone contents to be updated by signing.
+ * \param conf Knot configuration.
+ * \param flags Zone signing flags.
+ * \param roll_flags Key rollover flags.
+ * \param adjust_now If not zero: adjust "now" to this timestamp.
+ * \param reschedule Signature refresh time of the oldest signature in zone.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_zone_sign(zone_update_t *update,
+ conf_t *conf,
+ zone_sign_flags_t flags,
+ zone_sign_roll_flags_t roll_flags,
+ knot_time_t adjust_now,
+ zone_sign_reschedule_t *reschedule);
+
+/*!
+ * \brief Sign changeset (inside incremental Zone Update) created by DDNS or so...
+ *
+ * \param update Zone Update structure with current zone contents, changes to be signed and to be updated with signatures.
+ * \param conf Knot configuration.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf);
+
+/*!
+ * \brief Create new NCES3 salt if the old one is too old, and plan next resalt.
+ *
+ * For given zone, check NSEC3 salt in KASP db and decide if it shall be recreated
+ * and tell the user the next time it shall be called.
+ *
+ * This function is optimized to be called from NSEC3RESALT_EVENT,
+ * but also during zone load so that the zone gets loaded already with
+ * proper DNSSEC chain.
+ *
+ * \param ctx zone signing context
+ * \param soa_rrsigs_ok Zone is signed by current active ZSKs.
+ * \param salt_changed output if KNOT_EOK: when was the salt last changed? (either ctx->now or 0)
+ * \param when_resalt output: timestamp when next resalt takes place
+ *
+ * \return KNOT_E*
+ */
+int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool soa_rrsigs_ok,
+ knot_time_t *salt_changed, knot_time_t *when_resalt);
+
+/*!
+ * \brief When DNSSEC signing failed, re-plan on this time.
+ *
+ * \param ctx zone signing context
+ *
+ * \return Timestamp of next signing attempt.
+ */
+knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx);
+
+/*!
+ * \brief Validate zone DNSSEC based on its contents.
+ *
+ * \param update Zone update with contents.
+ * \param conf Knot configuration.
+ * \param now If not zero: adjust "now" to this timestamp.
+ * \param incremental Try to validate incrementally.
+ *
+ * \return KNOT_E*
+ */
+int knot_dnssec_validate_zone(zone_update_t *update, conf_t *conf, knot_time_t now, bool incremental);
diff --git a/src/knot/dnssec/zone-keys.c b/src/knot/dnssec/zone-keys.c
new file mode 100644
index 0000000..a76c4be
--- /dev/null
+++ b/src/knot/dnssec/zone-keys.c
@@ -0,0 +1,767 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "libdnssec/error.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/zone-keys.h"
+#include "libknot/libknot.h"
+#include "contrib/openbsd/strlcat.h"
+
+#define MAX_KEY_INFO 128
+
+typedef struct {
+ char msg[MAX_KEY_INFO];
+ knot_time_t key_time;
+} key_info_t;
+
+knot_dynarray_define(keyptr, zone_key_t *, DYNARRAY_VISIBILITY_NORMAL)
+
+void normalize_generate_flags(kdnssec_generate_flags_t *flags)
+{
+ if (!(*flags & DNSKEY_GENERATE_KSK) && !(*flags & DNSKEY_GENERATE_ZSK)) {
+ *flags |= DNSKEY_GENERATE_ZSK;
+ }
+ if (!(*flags & DNSKEY_GENERATE_SEP_SPEC)) {
+ if ((*flags & DNSKEY_GENERATE_KSK)) {
+ *flags |= DNSKEY_GENERATE_SEP_ON;
+ } else {
+ *flags &= ~DNSKEY_GENERATE_SEP_ON;
+ }
+ }
+}
+
+static int generate_dnssec_key(dnssec_keystore_t *keystore,
+ const knot_dname_t *zone_name,
+ const char *key_label,
+ dnssec_key_algorithm_t alg,
+ unsigned size,
+ kdnssec_generate_flags_t flags,
+ char **id,
+ dnssec_key_t **key)
+{
+ *key = NULL;
+ *id = NULL;
+
+ int ret = dnssec_keystore_generate(keystore, alg, size, key_label, id);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = dnssec_key_new(key);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ ret = dnssec_key_set_dname(*key, zone_name);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ dnssec_key_set_flags(*key, dnskey_flags(flags & DNSKEY_GENERATE_SEP_ON));
+ dnssec_key_set_algorithm(*key, alg);
+
+ ret = dnssec_keystore_get_private(keystore, *id, *key);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ return KNOT_EOK;
+
+fail:
+ dnssec_key_free(*key);
+ *key = NULL;
+ free(*id);
+ *id = NULL;
+ return ret;
+}
+
+static bool keytag_in_use(kdnssec_ctx_t *ctx, uint16_t keytag)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ uint16_t used = dnssec_key_get_keytag(ctx->zone->keys[i].key);
+ if (used == keytag) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#define GENERATE_KEYTAG_ATTEMPTS (20)
+
+static int generate_keytag_unconflict(kdnssec_ctx_t *ctx,
+ kdnssec_generate_flags_t flags,
+ char **id,
+ dnssec_key_t **key)
+{
+ unsigned size = (flags & DNSKEY_GENERATE_KSK) ? ctx->policy->ksk_size :
+ ctx->policy->zsk_size;
+
+ const char *label = NULL;
+
+ char label_buf[sizeof(knot_dname_txt_storage_t) + 16];
+ if (ctx->policy->key_label &&
+ knot_dname_to_str(label_buf, ctx->zone->dname, sizeof(label_buf)) != NULL) {
+ const char *key_type = (flags & DNSKEY_GENERATE_KSK) ? " KSK" : " ZSK" ;
+ strlcat(label_buf, key_type, sizeof(label_buf));
+ label = label_buf;
+ }
+
+ for (size_t i = 0; i < GENERATE_KEYTAG_ATTEMPTS; i++) {
+ dnssec_key_free(*key);
+ free(*id);
+
+ int ret = generate_dnssec_key(ctx->keystore, ctx->zone->dname, label,
+ ctx->policy->algorithm, size, flags,
+ id, key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (!keytag_in_use(ctx, dnssec_key_get_keytag(*key))) {
+ return KNOT_EOK;
+ }
+ }
+
+ log_zone_notice(ctx->zone->dname, "generated key with conflicting keytag %hu",
+ dnssec_key_get_keytag(*key));
+ return KNOT_EOK;
+}
+
+int kdnssec_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags,
+ knot_kasp_key_t **key_ptr)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ assert(ctx->keystore);
+ assert(ctx->policy);
+
+ normalize_generate_flags(&flags);
+
+ // generate key in the keystore
+
+ char *id = NULL;
+ dnssec_key_t *dnskey = NULL;
+
+ int r = generate_keytag_unconflict(ctx, flags, &id, &dnskey);
+ if (r != KNOT_EOK) {
+ return r;
+ }
+
+ knot_kasp_key_t *key = calloc(1, sizeof(*key));
+ if (!key) {
+ dnssec_key_free(dnskey);
+ free(id);
+ return KNOT_ENOMEM;
+ }
+
+ key->id = id;
+ key->key = dnskey;
+ key->is_ksk = (flags & DNSKEY_GENERATE_KSK);
+ key->is_zsk = (flags & DNSKEY_GENERATE_ZSK);
+ key->timing.created = ctx->now;
+
+ r = kasp_zone_append(ctx->zone, key);
+ free(key);
+ if (r != KNOT_EOK) {
+ dnssec_key_free(dnskey);
+ free(id);
+ return r;
+ }
+
+ if (key_ptr) {
+ *key_ptr = &ctx->zone->keys[ctx->zone->num_keys - 1];
+ }
+
+ return KNOT_EOK;
+}
+
+int kdnssec_share_key(kdnssec_ctx_t *ctx, const knot_dname_t *from_zone, const char *key_id)
+{
+ knot_dname_t *to_zone = knot_dname_copy(ctx->zone->dname, NULL);
+ if (to_zone == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = kdnssec_ctx_commit(ctx);
+ if (ret != KNOT_EOK) {
+ free(to_zone);
+ return ret;
+ }
+
+ ret = kasp_db_share_key(ctx->kasp_db, from_zone, ctx->zone->dname, key_id);
+ if (ret != KNOT_EOK) {
+ free(to_zone);
+ return ret;
+ }
+
+ kasp_zone_clear(ctx->zone);
+ ret = kasp_zone_load(ctx->zone, to_zone, ctx->kasp_db,
+ &ctx->keytag_conflict);
+ free(to_zone);
+ return ret;
+}
+
+int kdnssec_delete_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key_ptr)
+{
+ assert(ctx);
+ assert(ctx->zone);
+ assert(ctx->keystore);
+ assert(ctx->policy);
+
+ ssize_t key_index = key_ptr - ctx->zone->keys;
+
+ if (key_index < 0 || key_index >= ctx->zone->num_keys) {
+ return KNOT_EINVAL;
+ }
+
+ bool key_still_used_in_keystore = false;
+ int ret = kasp_db_delete_key(ctx->kasp_db, ctx->zone->dname, key_ptr->id, &key_still_used_in_keystore);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (!key_still_used_in_keystore && !key_ptr->is_pub_only) {
+ ret = dnssec_keystore_remove(ctx->keystore, key_ptr->id);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ dnssec_key_free(key_ptr->key);
+ free(key_ptr->id);
+ memmove(key_ptr, key_ptr + 1, (ctx->zone->num_keys - key_index - 1) * sizeof(*key_ptr));
+ ctx->zone->num_keys--;
+ return KNOT_EOK;
+}
+
+static bool is_published(knot_kasp_key_timing_t *timing, knot_time_t now)
+{
+ return (knot_time_cmp(timing->publish, now) <= 0 &&
+ knot_time_cmp(timing->post_active, now) > 0 &&
+ knot_time_cmp(timing->remove, now) > 0);
+}
+
+static bool is_ready(knot_kasp_key_timing_t *timing, knot_time_t now)
+{
+ return (knot_time_cmp(timing->ready, now) <= 0 &&
+ knot_time_cmp(timing->active, now) > 0);
+}
+
+static bool is_active(knot_kasp_key_timing_t *timing, knot_time_t now)
+{
+ return (knot_time_cmp(timing->active, now) <= 0 &&
+ knot_time_cmp(timing->retire, now) > 0 &&
+ knot_time_cmp(timing->retire_active, now) > 0 &&
+ knot_time_cmp(timing->remove, now) > 0);
+}
+
+static bool alg_has_active_zsk(kdnssec_ctx_t *ctx, uint8_t alg)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *k = &ctx->zone->keys[i];
+ if (dnssec_key_get_algorithm(k->key) == alg &&
+ k->is_zsk && is_active(&k->timing, ctx->now)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void fix_revoked_flag(knot_kasp_key_t *key)
+{
+ uint16_t flags = dnssec_key_get_flags(key->key);
+ if ((flags & DNSKEY_FLAGS_REVOKED) != DNSKEY_FLAGS_REVOKED) {
+ dnssec_key_set_flags(key->key, flags | DNSKEY_FLAGS_REVOKED); // FYI leading to change of keytag
+ }
+}
+
+/*!
+ * \brief Get key feature flags from key parameters.
+ */
+static void set_key(knot_kasp_key_t *kasp_key, knot_time_t now,
+ zone_key_t *zone_key, bool same_alg_act_zsk)
+{
+ assert(kasp_key);
+ assert(zone_key);
+
+ knot_kasp_key_timing_t *timing = &kasp_key->timing;
+
+ zone_key->id = kasp_key->id;
+ zone_key->key = kasp_key->key;
+
+ // next event computation
+
+ knot_time_t next = 0;
+ knot_time_t timestamps[] = {
+ timing->pre_active,
+ timing->publish,
+ timing->ready,
+ timing->active,
+ timing->retire_active,
+ timing->retire,
+ timing->post_active,
+ timing->revoke,
+ timing->remove,
+ };
+
+ for (int i = 0; i < sizeof(timestamps) / sizeof(knot_time_t); i++) {
+ knot_time_t ts = timestamps[i];
+ if (knot_time_cmp(now, ts) < 0 && knot_time_cmp(ts, next) < 0) {
+ next = ts;
+ }
+ }
+
+ zone_key->next_event = next;
+
+ zone_key->is_ksk = kasp_key->is_ksk;
+ zone_key->is_zsk = kasp_key->is_zsk;
+
+ zone_key->is_public = is_published(timing, now);
+ zone_key->is_ready = (zone_key->is_ksk && is_ready(timing, now));
+ zone_key->is_active = is_active(timing, now);
+
+ zone_key->is_ksk_active_plus = zone_key->is_public && zone_key->is_ksk && !zone_key->is_active; // KSK is active+ whenever published
+ zone_key->is_zsk_active_plus = zone_key->is_ready && !same_alg_act_zsk;
+ if (knot_time_cmp(timing->pre_active, now) <= 0 &&
+ knot_time_cmp(timing->ready, now) > 0 &&
+ knot_time_cmp(timing->active, now) > 0 &&
+ knot_time_cmp(timing->remove, now) > 0) {
+ zone_key->is_zsk_active_plus = zone_key->is_zsk;
+ // zone_key->is_ksk_active_plus = (knot_time_cmp(timing->publish, now) <= 0 && zone_key->is_ksk); // redundant, but helps understand
+ }
+ if (knot_time_cmp(timing->retire, now) <= 0 &&
+ knot_time_cmp(timing->remove, now) > 0) {
+ zone_key->is_ksk_active_plus = false;
+ zone_key->is_public = zone_key->is_zsk;
+ }
+ if (knot_time_cmp(timing->retire_active, now) <= 0 &&
+ knot_time_cmp(timing->retire, now) > 0 &&
+ knot_time_cmp(timing->remove, now) > 0) {
+ zone_key->is_ksk_active_plus = zone_key->is_ksk;
+ zone_key->is_zsk_active_plus = !same_alg_act_zsk;
+ } // not "else" !
+ if (knot_time_cmp(timing->post_active, now) <= 0 &&
+ knot_time_cmp(timing->remove, now) > 0) {
+ zone_key->is_ksk_active_plus = false;
+ zone_key->is_zsk_active_plus = zone_key->is_zsk;
+ }
+ if (zone_key->is_ksk &&
+ knot_time_cmp(timing->revoke, now) <= 0 &&
+ knot_time_cmp(timing->remove, now) > 0) {
+ zone_key->is_ready = false;
+ zone_key->is_active = false;
+ zone_key->is_ksk_active_plus = true;
+ zone_key->is_public = true;
+ zone_key->is_revoked = true;
+ fix_revoked_flag(kasp_key);
+ }
+ if (kasp_key->is_pub_only) {
+ zone_key->is_active = false;
+ zone_key->is_ksk_active_plus = false;
+ zone_key->is_zsk_active_plus = false;
+ zone_key->is_pub_only = true;
+ }
+}
+
+/*!
+ * \brief Check if algorithm is allowed with NSEC3.
+ */
+static bool is_nsec3_allowed(uint8_t algorithm)
+{
+ switch (algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int walk_algorithms(kdnssec_ctx_t *ctx, zone_keyset_t *keyset)
+{
+ if (ctx->policy->unsafe & UNSAFE_KEYSET) {
+ return KNOT_EOK;
+ }
+
+ uint8_t alg_usage[256] = { 0 };
+ bool have_active_alg = false;
+
+ for (size_t i = 0; i < keyset->count; i++) {
+ zone_key_t *key = &keyset->keys[i];
+ if (key->is_pub_only) {
+ continue;
+ }
+ uint8_t alg = dnssec_key_get_algorithm(key->key);
+
+ if (ctx->policy->nsec3_enabled && !is_nsec3_allowed(alg)) {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, key %d "
+ "cannot be used with NSEC3",
+ dnssec_key_get_keytag(key->key));
+ key->is_public = false;
+ key->is_active = false;
+ key->is_ready = false;
+ key->is_ksk_active_plus = false;
+ key->is_zsk_active_plus = false;
+ continue;
+ }
+
+ if (key->is_ksk && key->is_public) { alg_usage[alg] |= 1; }
+ if (key->is_zsk && key->is_public) { alg_usage[alg] |= 2; }
+ if (key->is_ksk && (key->is_active || key->is_ksk_active_plus)) { alg_usage[alg] |= 4; }
+ if (key->is_zsk && (key->is_active || key->is_zsk_active_plus)) { alg_usage[alg] |= 8; }
+ }
+
+ for (size_t i = 0; i < sizeof(alg_usage); i++) {
+ if (!(alg_usage[i] & 3)) {
+ continue; // no public keys, ignore
+ }
+ switch (alg_usage[i]) {
+ case 15: // all keys ready for signing
+ have_active_alg = true;
+ break;
+ case 5:
+ case 10:
+ if (ctx->policy->offline_ksk) {
+ have_active_alg = true;
+ break;
+ }
+ // else FALLTHROUGH
+ default:
+ return KNOT_DNSSEC_EMISSINGKEYTYPE;
+ }
+ }
+
+ if (!have_active_alg) {
+ return KNOT_DNSSEC_ENOKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Load private keys for active keys.
+ */
+static int load_private_keys(dnssec_keystore_t *keystore, zone_keyset_t *keyset)
+{
+ assert(keystore);
+ assert(keyset);
+
+ for (size_t i = 0; i < keyset->count; i++) {
+ zone_key_t *key = &keyset->keys[i];
+ if (!key->is_active && !key->is_ksk_active_plus && !key->is_zsk_active_plus) {
+ continue;
+ }
+ int r = dnssec_keystore_get_private(keystore, key->id, key->key);
+ switch (r) {
+ case DNSSEC_EOK:
+ case DNSSEC_KEY_ALREADY_PRESENT:
+ break;
+ default:
+ return r;
+ }
+ }
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * \brief Log information about zone keys.
+ */
+static void log_key_info(const zone_key_t *key, char *out, size_t out_len)
+{
+ assert(key);
+ assert(out);
+
+ uint8_t alg_code = dnssec_key_get_algorithm(key->key);
+ const knot_lookup_t *alg = knot_lookup_by_id(knot_dnssec_alg_names, alg_code);
+
+ char alg_code_str[8] = "";
+ if (alg == NULL) {
+ (void)snprintf(alg_code_str, sizeof(alg_code_str), "%d", alg_code);
+ }
+
+ (void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s%s",
+ dnssec_key_get_keytag(key->key),
+ (alg != NULL ? alg->name : alg_code_str),
+ (key->is_ksk ? (key->is_zsk ? ", CSK" : ", KSK") : ""),
+ (key->is_public ? ", public" : ""),
+ (key->is_ready ? ", ready" : ""),
+ (key->is_active ? ", active" : ""),
+ (key->is_ksk_active_plus || key->is_zsk_active_plus ? ", active+" : ""));
+}
+
+static int log_key_sort(const void *a, const void *b)
+{
+ const key_info_t *x = a, *y = b;
+ return knot_time_cmp(x->key_time, y->key_time);
+}
+
+/*!
+ * \brief Load zone keys and init cryptographic context.
+ */
+int load_zone_keys(kdnssec_ctx_t *ctx, zone_keyset_t *keyset_ptr, bool verbose)
+{
+ if (!ctx || !keyset_ptr) {
+ return KNOT_EINVAL;
+ }
+
+ zone_keyset_t keyset = { 0 };
+
+ if (ctx->zone->num_keys < 1) {
+ log_zone_error(ctx->zone->dname, "DNSSEC, no keys are available");
+ return KNOT_DNSSEC_ENOKEY;
+ }
+
+ keyset.count = ctx->zone->num_keys;
+ keyset.keys = calloc(keyset.count, sizeof(zone_key_t));
+ if (!keyset.keys) {
+ free_zone_keys(&keyset);
+ return KNOT_ENOMEM;
+ }
+
+ key_info_t key_info[ctx->zone->num_keys];
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *kasp_key = &ctx->zone->keys[i];
+ uint8_t kk_alg = dnssec_key_get_algorithm(kasp_key->key);
+ bool same_alg_zsk = alg_has_active_zsk(ctx, kk_alg);
+ set_key(kasp_key, ctx->now, &keyset.keys[i], same_alg_zsk);
+ if (verbose) {
+ log_key_info(&keyset.keys[i], key_info[i].msg, MAX_KEY_INFO);
+ if (knot_time_cmp(kasp_key->timing.pre_active, kasp_key->timing.publish) < 0) {
+ key_info[i].key_time = kasp_key->timing.pre_active;
+ } else {
+ key_info[i].key_time = kasp_key->timing.publish;
+ }
+ }
+ }
+
+ // Sort the keys by publish/pre_active timestamps.
+ if (verbose) {
+ qsort(key_info, ctx->zone->num_keys, sizeof(key_info[0]), log_key_sort);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ log_zone_info(ctx->zone->dname, "%s", key_info[i].msg);
+ }
+ }
+
+ int ret = walk_algorithms(ctx, &keyset);
+ if (ret != KNOT_EOK) {
+ log_zone_error(ctx->zone->dname, "DNSSEC, keys validation failed (%s)",
+ knot_strerror(ret));
+ free_zone_keys(&keyset);
+ return ret;
+ }
+
+ ret = load_private_keys(ctx->keystore, &keyset);
+ ret = knot_error_from_libdnssec(ret);
+ if (ret != KNOT_EOK) {
+ log_zone_error(ctx->zone->dname, "DNSSEC, failed to load private "
+ "keys (%s)", knot_strerror(ret));
+ free_zone_keys(&keyset);
+ return ret;
+ }
+
+ *keyset_ptr = keyset;
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Free structure with zone keys and associated DNSSEC contexts.
+ */
+void free_zone_keys(zone_keyset_t *keyset)
+{
+ if (!keyset) {
+ return;
+ }
+
+ for (size_t i = 0; i < keyset->count; i++) {
+ dnssec_binary_free(&keyset->keys[i].precomputed_ds);
+ }
+
+ free(keyset->keys);
+
+ memset(keyset, '\0', sizeof(*keyset));
+}
+
+/*!
+ * \brief Get timestamp of next key event.
+ */
+knot_time_t knot_get_next_zone_key_event(const zone_keyset_t *keyset)
+{
+ assert(keyset);
+
+ knot_time_t result = 0;
+
+ for (size_t i = 0; i < keyset->count; i++) {
+ zone_key_t *key = &keyset->keys[i];
+ if (knot_time_cmp(key->next_event, result) < 0) {
+ result = key->next_event;
+ }
+ }
+
+ return result;
+}
+
+/*!
+ * \brief Compute DS record rdata from key + cache it.
+ */
+int zone_key_calculate_ds(zone_key_t *for_key, dnssec_key_digest_t digesttype,
+ dnssec_binary_t *out_donotfree)
+{
+ assert(for_key);
+ assert(out_donotfree);
+
+ int ret = KNOT_EOK;
+
+ if (for_key->precomputed_ds.data == NULL || for_key->precomputed_digesttype != digesttype) {
+ dnssec_binary_free(&for_key->precomputed_ds);
+ ret = dnssec_key_create_ds(for_key->key, digesttype, &for_key->precomputed_ds);
+ ret = knot_error_from_libdnssec(ret);
+ for_key->precomputed_digesttype = digesttype;
+ }
+
+ *out_donotfree = for_key->precomputed_ds;
+ return ret;
+}
+
+zone_sign_ctx_t *zone_sign_ctx(const zone_keyset_t *keyset, const kdnssec_ctx_t *dnssec_ctx)
+{
+ zone_sign_ctx_t *ctx = calloc(1, sizeof(*ctx) + keyset->count * sizeof(*ctx->sign_ctxs));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->sign_ctxs = (dnssec_sign_ctx_t **)(ctx + 1);
+ ctx->count = keyset->count;
+ ctx->keys = keyset->keys;
+ ctx->dnssec_ctx = dnssec_ctx;
+ for (size_t i = 0; i < ctx->count; i++) {
+ int ret = dnssec_sign_new(&ctx->sign_ctxs[i], ctx->keys[i].key);
+ if (ret != DNSSEC_EOK) {
+ zone_sign_ctx_free(ctx);
+ return NULL;
+ }
+ }
+
+ return ctx;
+}
+
+zone_sign_ctx_t *zone_validation_ctx(const kdnssec_ctx_t *dnssec_ctx)
+{
+ size_t count = dnssec_ctx->zone->num_keys;
+ zone_sign_ctx_t *ctx = calloc(1, sizeof(*ctx) + count * sizeof(*ctx->sign_ctxs));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->sign_ctxs = (dnssec_sign_ctx_t **)(ctx + 1);
+ ctx->count = count;
+ ctx->keys = NULL;
+ ctx->dnssec_ctx = dnssec_ctx;
+ for (size_t i = 0; i < ctx->count; i++) {
+ int ret = dnssec_sign_new(&ctx->sign_ctxs[i], dnssec_ctx->zone->keys[i].key);
+ if (ret != DNSSEC_EOK) {
+ zone_sign_ctx_free(ctx);
+ return NULL;
+ }
+ }
+
+ return ctx;
+}
+
+void zone_sign_ctx_free(zone_sign_ctx_t *ctx)
+{
+ if (ctx != NULL) {
+ for (size_t i = 0; i < ctx->count; i++) {
+ dnssec_sign_free(ctx->sign_ctxs[i]);
+ }
+ free(ctx);
+ }
+}
+
+int dnssec_key_from_rdata(dnssec_key_t **key, const knot_dname_t *owner,
+ const uint8_t *rdata, size_t rdlen)
+{
+ if (key == NULL || rdata == NULL || rdlen == 0) {
+ return KNOT_EINVAL;
+ }
+
+ const dnssec_binary_t binary_key = {
+ .size = rdlen,
+ .data = (uint8_t *)rdata
+ };
+
+ dnssec_key_t *new_key = NULL;
+ int ret = dnssec_key_new(&new_key);
+ if (ret != DNSSEC_EOK) {
+ return knot_error_from_libdnssec(ret);
+ }
+ ret = dnssec_key_set_rdata(new_key, &binary_key);
+ if (ret != DNSSEC_EOK) {
+ dnssec_key_free(new_key);
+ return knot_error_from_libdnssec(ret);
+ }
+ if (owner != NULL) {
+ ret = dnssec_key_set_dname(new_key, owner);
+ if (ret != DNSSEC_EOK) {
+ dnssec_key_free(new_key);
+ return knot_error_from_libdnssec(ret);
+ }
+ }
+
+ *key = new_key;
+ return KNOT_EOK;
+}
+
+static bool soa_signed_by_key(const zone_key_t *key, const knot_rdataset_t *apex_rrsig)
+{
+ assert(key != NULL);
+ if (apex_rrsig == NULL) {
+ return false;
+ }
+ uint16_t keytag = dnssec_key_get_keytag(key->key);
+
+ knot_rdata_t *rr = apex_rrsig->rdata;
+ for (int i = 0; i < apex_rrsig->count; i++) {
+ if (knot_rrsig_type_covered(rr) == KNOT_RRTYPE_SOA &&
+ knot_rrsig_key_tag(rr) == keytag) {
+ return true;
+ }
+ rr = knot_rdataset_next(rr);
+ }
+
+ return false;
+}
+
+int is_soa_signed_by_all_zsks(const zone_keyset_t *keyset,
+ const knot_rdataset_t *apex_rrsig)
+{
+ if (keyset == NULL || keyset->count == 0) {
+ return false;
+ }
+
+ for (size_t i = 0; i < keyset->count; i++) {
+ const zone_key_t *key = &keyset->keys[i];
+ if (key->is_zsk && key->is_active &&
+ !soa_signed_by_key(key, apex_rrsig)) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/knot/dnssec/zone-keys.h b/src/knot/dnssec/zone-keys.h
new file mode 100644
index 0000000..6d72572
--- /dev/null
+++ b/src/knot/dnssec/zone-keys.h
@@ -0,0 +1,213 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "libknot/dynarray.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/sign.h"
+#include "knot/dnssec/kasp/kasp_zone.h"
+#include "knot/dnssec/kasp/policy.h"
+#include "knot/dnssec/context.h"
+
+/*!
+ * \brief Zone key context used during signing.
+ */
+typedef struct {
+ const char *id;
+ dnssec_key_t *key;
+
+ dnssec_binary_t precomputed_ds;
+ dnssec_key_digest_t precomputed_digesttype;
+
+ knot_time_t next_event;
+
+ bool is_ksk;
+ bool is_zsk;
+ bool is_active;
+ bool is_public;
+ bool is_ready;
+ bool is_zsk_active_plus;
+ bool is_ksk_active_plus;
+ bool is_pub_only;
+ bool is_revoked;
+} zone_key_t;
+
+knot_dynarray_declare(keyptr, zone_key_t *, DYNARRAY_VISIBILITY_NORMAL, 1)
+
+typedef struct {
+ size_t count;
+ zone_key_t *keys;
+} zone_keyset_t;
+
+/*!
+ * \brief Signing context used for single signing thread.
+ */
+typedef struct {
+ size_t count; // number of keys in keyset
+ zone_key_t *keys; // keys in keyset
+ dnssec_sign_ctx_t **sign_ctxs; // signing buffers for keys in keyset
+ const kdnssec_ctx_t *dnssec_ctx; // dnssec context
+} zone_sign_ctx_t;
+
+/*!
+ * \brief Flags determining key type
+ */
+enum {
+ DNSKEY_FLAGS_ZSK = KNOT_DNSKEY_FLAG_ZONE,
+ DNSKEY_FLAGS_KSK = KNOT_DNSKEY_FLAG_ZONE | KNOT_DNSKEY_FLAG_SEP,
+ DNSKEY_FLAGS_REVOKED = KNOT_DNSKEY_FLAG_ZONE | KNOT_DNSKEY_FLAG_SEP | KNOT_DNSKEY_FLAG_REVOKE,
+};
+
+inline static uint16_t dnskey_flags(bool is_ksk)
+{
+ return is_ksk ? DNSKEY_FLAGS_KSK : DNSKEY_FLAGS_ZSK;
+}
+
+typedef enum {
+ DNSKEY_GENERATE_KSK = (1 << 0), // KSK flag in metadata
+ DNSKEY_GENERATE_ZSK = (1 << 1), // ZSK flag in metadata
+ DNSKEY_GENERATE_SEP_SPEC = (1 << 2), // not (SEP bit set iff KSK)
+ DNSKEY_GENERATE_SEP_ON = (1 << 3), // SEP bit set on
+} kdnssec_generate_flags_t;
+
+void normalize_generate_flags(kdnssec_generate_flags_t *flags);
+
+/*!
+ * \brief Generate new key, store all details in new kasp key structure.
+ *
+ * \param ctx kasp context
+ * \param flags determine if to use the key as KSK and/or ZSK and SEP flag
+ * \param key_ptr output if KNOT_EOK: new pointer to generated key
+ *
+ * \return KNOT_E*
+ */
+int kdnssec_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags,
+ knot_kasp_key_t **key_ptr);
+
+/*!
+ * \brief Take a key from another zone (copying info, sharing privkey).
+ *
+ * \param ctx kasp context
+ * \param from_zone name of the zone to take from
+ * \param key_id ID of the key to take
+ *
+ * \return KNOT_E*
+ */
+int kdnssec_share_key(kdnssec_ctx_t *ctx, const knot_dname_t *from_zone, const char *key_id);
+
+/*!
+ * \brief Remove key from zone.
+ *
+ * Deletes the key in keystore, unlinks the key from the zone in KASP db,
+ * moreover if no more zones use this key in KASP db, deletes it completely there
+ * and deletes it also from key storage (PKCS8dir/PKCS11).
+ *
+ * \param ctx kasp context (zone, keystore, kaspdb) to be modified
+ * \param key_ptr pointer to key to be removed, must be inside keystore structure, NOT a copy of it!
+ *
+ * \return KNOT_E*
+ */
+int kdnssec_delete_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key_ptr);
+
+/*!
+ * \brief Load zone keys and init cryptographic context.
+ *
+ * \param ctx Zone signing context.
+ * \param keyset_ptr Resulting zone keyset.
+ * \param verbose Print key summary into log.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int load_zone_keys(kdnssec_ctx_t *ctx, zone_keyset_t *keyset_ptr, bool verbose);
+
+/*!
+ * \brief Free structure with zone keys and associated DNSSEC contexts.
+ *
+ * \param keyset Zone keys.
+ */
+void free_zone_keys(zone_keyset_t *keyset);
+
+/*!
+ * \brief Get timestamp of next key event.
+ *
+ * \param keyset Zone keys.
+ *
+ * \return Timestamp of next key event.
+ */
+knot_time_t knot_get_next_zone_key_event(const zone_keyset_t *keyset);
+
+/*!
+ * \brief Returns DS record rdata for given key.
+ *
+ * This function caches the results, so calling again with the same key returns immediately.
+ *
+ * \param for_key The key to compute DS for.
+ * \param digesttype DS digest algorithm.
+ * \param out_donotfree Output: the DS record rdata. Do not call dnssec_binary_free() on this ever.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int zone_key_calculate_ds(zone_key_t *for_key, dnssec_key_digest_t digesttype,
+ dnssec_binary_t *out_donotfree);
+
+/*!
+ * \brief Initialize local signing context.
+ *
+ * \param keyset Key set.
+ * \param dnssec_ctx DNSSEC context.
+ *
+ * \return New local signing context or NULL.
+ */
+zone_sign_ctx_t *zone_sign_ctx(const zone_keyset_t *keyset, const kdnssec_ctx_t *dnssec_ctx);
+
+/*!
+ * \brief Initialize local validating context.
+ * \param dnssec_ctx DNSSEC context.
+ * \return New local validating context or NULL.
+ */
+zone_sign_ctx_t *zone_validation_ctx(const kdnssec_ctx_t *dnssec_ctx);
+
+/*!
+ * \brief Free local signing context.
+ *
+ * \note This doesn't free the underlying keyset.
+ *
+ * \param ctx Local context to be freed.
+ */
+void zone_sign_ctx_free(zone_sign_ctx_t *ctx);
+
+/*!
+ * \brief Create key signing structure from DNSKEY zone record.
+ *
+ * \param key Dnssec key to be allocated.
+ * \param owner Zone name.
+ * \param rdata DNSKEY rdata.
+ * \param rdlen DNSKEY rdata length.
+ *
+ * \return KNOT_E*
+ */
+int dnssec_key_from_rdata(dnssec_key_t **key, const knot_dname_t *owner,
+ const uint8_t *rdata, size_t rdlen);
+
+/*!
+ * \brief Tell if apex SOA is signed by all active ZSKs.
+ *
+ * \param keyset Zone key set.
+ * \param apex_rrsig Apex RRSIG RRSet.
+ */
+int is_soa_signed_by_all_zsks(const zone_keyset_t *keyset,
+ const knot_rdataset_t *apex_rrsig);
diff --git a/src/knot/dnssec/zone-nsec.c b/src/knot/dnssec/zone-nsec.c
new file mode 100644
index 0000000..e952061
--- /dev/null
+++ b/src/knot/dnssec/zone-nsec.c
@@ -0,0 +1,429 @@
+/* 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 <assert.h>
+
+#include "libdnssec/error.h"
+#include "libknot/descriptor.h"
+#include "libknot/rrtype/nsec3.h"
+#include "libknot/rrtype/soa.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/nsec-chain.h"
+#include "knot/dnssec/nsec3-chain.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/zone/zone-diff.h"
+#include "contrib/base32hex.h"
+#include "contrib/wire_ctx.h"
+
+int knot_nsec3_hash_to_dname(uint8_t *out, size_t out_size, const uint8_t *hash,
+ size_t hash_size, const knot_dname_t *zone_apex)
+
+{
+ if (out == NULL || hash == NULL || zone_apex == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Encode raw hash to the first label.
+ uint8_t label[KNOT_DNAME_MAXLABELLEN];
+ int32_t label_size = knot_base32hex_encode(hash, hash_size, label, sizeof(label));
+ if (label_size <= 0) {
+ return label_size;
+ }
+
+ // Write the result, which already is in lower-case.
+ wire_ctx_t wire = wire_ctx_init(out, out_size);
+
+ wire_ctx_write_u8(&wire, label_size);
+ wire_ctx_write(&wire, label, label_size);
+ wire_ctx_write(&wire, zone_apex, knot_dname_size(zone_apex));
+
+ return wire.error;
+}
+
+int knot_create_nsec3_owner(uint8_t *out, size_t out_size,
+ const knot_dname_t *owner, const knot_dname_t *zone_apex,
+ const dnssec_nsec3_params_t *params)
+{
+ if (out == NULL || owner == NULL || zone_apex == NULL || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ dnssec_binary_t data = {
+ .data = (uint8_t *)owner,
+ .size = knot_dname_size(owner)
+ };
+
+ dnssec_binary_t hash = { 0 };
+
+ int ret = dnssec_nsec3_hash(&data, params, &hash);
+ if (ret != DNSSEC_EOK) {
+ return knot_error_from_libdnssec(ret);
+ }
+
+ ret = knot_nsec3_hash_to_dname(out, out_size, hash.data, hash.size, zone_apex);
+
+ dnssec_binary_free(&hash);
+
+ return ret;
+}
+
+knot_dname_t *node_nsec3_hash(zone_node_t *node, const zone_contents_t *zone)
+{
+ if (node->nsec3_hash == NULL && knot_is_nsec3_enabled(zone)) {
+ assert(!(node->flags & NODE_FLAGS_NSEC3_NODE));
+ size_t hash_size = zone_nsec3_name_len(zone);
+ knot_dname_t *hash = malloc(hash_size);
+ if (hash == NULL) {
+ return NULL;
+ }
+ if (knot_create_nsec3_owner(hash, hash_size, node->owner, zone->apex->owner,
+ &zone->nsec3_params) != KNOT_EOK) {
+ free(hash);
+ return NULL;
+ }
+ node->nsec3_hash = hash;
+ }
+
+ if (node->flags & NODE_FLAGS_NSEC3_NODE) {
+ return node->nsec3_node->owner;
+ } else {
+ return node->nsec3_hash;
+ }
+}
+
+zone_node_t *node_nsec3_node(zone_node_t *node, const zone_contents_t *zone)
+{
+ if (!(node->flags & NODE_FLAGS_NSEC3_NODE) && knot_is_nsec3_enabled(zone)) {
+ knot_dname_t *hash = node_nsec3_hash(node, zone);
+ zone_node_t *nsec3 = zone_tree_get(zone->nsec3_nodes, hash);
+ if (nsec3 != NULL) {
+ if (node->nsec3_hash != binode_counterpart(node)->nsec3_hash) {
+ free(node->nsec3_hash);
+ }
+ node->nsec3_node = binode_first(nsec3);
+ node->flags |= NODE_FLAGS_NSEC3_NODE;
+ }
+ }
+
+ return node_nsec3_get(node);
+}
+
+int binode_fix_nsec3_pointer(zone_node_t *node, const zone_contents_t *zone)
+{
+ zone_node_t *counter = binode_counterpart(node);
+ if (counter->nsec3_hash == NULL) {
+ (void)node_nsec3_node(node, zone);
+ return KNOT_EOK;
+ }
+ assert(counter->nsec3_node != NULL); // shut up cppcheck
+
+ zone_node_t *nsec3_counter = (counter->flags & NODE_FLAGS_NSEC3_NODE) ?
+ counter->nsec3_node : NULL;
+ if (nsec3_counter != NULL && !(binode_node_as(nsec3_counter, node)->flags & NODE_FLAGS_DELETED)) {
+ assert(node->flags & NODE_FLAGS_NSEC3_NODE);
+ node->flags |= NODE_FLAGS_NSEC3_NODE;
+ assert(!(nsec3_counter->flags & NODE_FLAGS_SECOND));
+ node->nsec3_node = nsec3_counter;
+ } else {
+ node->flags &= ~NODE_FLAGS_NSEC3_NODE;
+ if (counter->flags & NODE_FLAGS_NSEC3_NODE) {
+ // downgrade the NSEC3 node pointer to NSEC3 name
+ node->nsec3_hash = knot_dname_copy(counter->nsec3_node->owner, NULL);
+ } else {
+ node->nsec3_hash = counter->nsec3_hash;
+ }
+ (void)node_nsec3_node(node, zone);
+ }
+ return KNOT_EOK;
+}
+
+static bool nsec3param_valid(const knot_rdataset_t *rrs,
+ const dnssec_nsec3_params_t *params)
+{
+ assert(rrs);
+ assert(params);
+
+ // NSEC3 disabled
+ if (params->algorithm == 0) {
+ return false;
+ }
+
+ // multiple NSEC3 records
+ if (rrs->count != 1) {
+ return false;
+ }
+
+ dnssec_binary_t rdata = {
+ .size = rrs->rdata->len,
+ .data = rrs->rdata->data,
+ };
+
+ dnssec_nsec3_params_t parsed = { 0 };
+ int r = dnssec_nsec3_params_from_rdata(&parsed, &rdata);
+ if (r != DNSSEC_EOK) {
+ return false;
+ }
+
+ bool equal = parsed.algorithm == params->algorithm &&
+ parsed.flags == 0 && // opt-out flag is always 0 in NSEC3PARAM
+ parsed.iterations == params->iterations &&
+ dnssec_binary_cmp(&parsed.salt, &params->salt) == 0;
+
+ dnssec_nsec3_params_free(&parsed);
+
+ return equal;
+}
+
+static int remove_nsec3param(zone_update_t *update, bool also_rrsig)
+{
+ knot_rrset_t rrset = node_rrset(update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
+ int ret = zone_update_remove(update, &rrset);
+
+ rrset = node_rrset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
+ if (!knot_rrset_empty(&rrset) && ret == KNOT_EOK && also_rrsig) {
+ knot_rrset_t rrsig;
+ knot_rrset_init(&rrsig, update->new_cont->apex->owner,
+ KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, 0);
+ ret = knot_synth_rrsig(KNOT_RRTYPE_NSEC3PARAM, &rrset.rrs, &rrsig.rrs, NULL);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_remove(update, &rrsig);
+ }
+ knot_rdataset_clear(&rrsig.rrs, NULL);
+ }
+
+ return ret;
+}
+
+static int set_nsec3param(knot_rrset_t *rrset, const dnssec_nsec3_params_t *params)
+{
+ assert(rrset);
+ assert(params);
+
+ // Prepare wire rdata.
+ size_t rdata_len = 3 * sizeof(uint8_t) + sizeof(uint16_t) + params->salt.size;
+ uint8_t rdata[rdata_len];
+ wire_ctx_t wire = wire_ctx_init(rdata, rdata_len);
+
+ wire_ctx_write_u8(&wire, params->algorithm);
+ wire_ctx_write_u8(&wire, 0); // (RFC 5155 Section 4.1.2)
+ wire_ctx_write_u16(&wire, params->iterations);
+ wire_ctx_write_u8(&wire, params->salt.size);
+ wire_ctx_write(&wire, params->salt.data, params->salt.size);
+
+ if (wire.error != KNOT_EOK) {
+ return wire.error;
+ }
+
+ assert(wire_ctx_available(&wire) == 0);
+
+ return knot_rrset_add_rdata(rrset, rdata, rdata_len, NULL);
+}
+
+static int add_nsec3param(zone_update_t *update,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl)
+{
+ assert(update);
+ assert(params);
+
+ knot_rrset_t *rrset = NULL;
+ rrset = knot_rrset_new(update->new_cont->apex->owner, KNOT_RRTYPE_NSEC3PARAM,
+ KNOT_CLASS_IN, ttl, NULL);
+ if (rrset == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int r = set_nsec3param(rrset, params);
+ if (r == KNOT_EOK) {
+ r = zone_update_add(update, rrset);
+ }
+ knot_rrset_free(rrset, NULL);
+ return r;
+}
+
+bool knot_nsec3param_uptodate(const zone_contents_t *zone,
+ const dnssec_nsec3_params_t *params)
+{
+ assert(zone);
+ assert(params);
+
+ knot_rdataset_t *nsec3param = node_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM);
+
+ return (nsec3param != NULL && nsec3param_valid(nsec3param, params));
+}
+
+int knot_nsec3param_update(zone_update_t *update,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl)
+{
+ assert(update);
+ assert(params);
+
+ knot_rdataset_t *nsec3param = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
+ bool valid = nsec3param && nsec3param_valid(nsec3param, params);
+
+ if (nsec3param && !valid) {
+ int r = remove_nsec3param(update, params->algorithm == 0);
+ if (r != KNOT_EOK) {
+ return r;
+ }
+ }
+
+ if (params->algorithm != 0 && !valid) {
+ return add_nsec3param(update, params, ttl);
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Initialize NSEC3PARAM based on the signing policy.
+ *
+ * \note For NSEC, the algorithm number is set to 0.
+ */
+static dnssec_nsec3_params_t nsec3param_init(const knot_kasp_policy_t *policy,
+ const knot_kasp_zone_t *zone)
+{
+ assert(policy);
+ assert(zone);
+
+ dnssec_nsec3_params_t params = { 0 };
+ if (policy->nsec3_enabled) {
+ params.algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1;
+ params.iterations = policy->nsec3_iterations;
+ params.salt = zone->nsec3_salt;
+ params.flags = (policy->nsec3_opt_out ? KNOT_NSEC3_FLAG_OPT_OUT : 0);
+ }
+
+ return params;
+}
+
+// int: returns KNOT_E* if error
+static int zone_nsec_ttl(zone_contents_t *zone)
+{
+ knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
+ if (knot_rrset_empty(&soa)) {
+ return KNOT_EINVAL;
+ }
+
+ return MIN(knot_soa_minimum(soa.rrs.rdata), soa.ttl);
+}
+
+int knot_zone_create_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx)
+{
+ if (update == NULL || ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (ctx->policy->unsafe & UNSAFE_NSEC) {
+ return KNOT_EOK;
+ }
+
+ int nsec_ttl = zone_nsec_ttl(update->new_cont);
+ if (nsec_ttl < 0) {
+ return nsec_ttl;
+ }
+
+ dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
+
+ int ret = knot_nsec3param_update(update, &params, nsec_ttl);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (ctx->policy->nsec3_enabled) {
+ ret = knot_nsec3_create_chain(update->new_cont, &params, nsec_ttl,
+ update);
+ } else {
+ ret = knot_nsec_create_chain(update, nsec_ttl);
+ if (ret == KNOT_EOK) {
+ ret = delete_nsec3_chain(update);
+ }
+ }
+ return ret;
+}
+
+int knot_zone_fix_nsec_chain(zone_update_t *update,
+ const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *ctx)
+{
+ if (update == NULL || ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (ctx->policy->unsafe & UNSAFE_NSEC) {
+ return KNOT_EOK;
+ }
+
+ int nsec_ttl_old = zone_nsec_ttl(update->zone->contents);
+ int nsec_ttl_new = zone_nsec_ttl(update->new_cont);
+ if (nsec_ttl_old < 0 || nsec_ttl_new < 0) {
+ return MIN(nsec_ttl_old, nsec_ttl_new);
+ }
+
+ dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
+
+ int ret;
+ if (nsec_ttl_old != nsec_ttl_new || (update->flags & UPDATE_CHANGED_NSEC)) {
+ ret = KNOT_ENORECORD;
+ } else if (ctx->policy->nsec3_enabled) {
+ ret = knot_nsec3_fix_chain(update, &params, nsec_ttl_new);
+ } else {
+ ret = knot_nsec_fix_chain(update, nsec_ttl_new);
+ }
+ if (ret == KNOT_ENORECORD) {
+ log_zone_info(update->zone->name, "DNSSEC, re-creating whole NSEC%s chain",
+ (ctx->policy->nsec3_enabled ? "3" : ""));
+ if (ctx->policy->nsec3_enabled) {
+ ret = knot_nsec3_create_chain(update->new_cont, &params,
+ nsec_ttl_new, update);
+ } else {
+ ret = knot_nsec_create_chain(update, nsec_ttl_new);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ ret = knot_zone_sign_nsecs_in_changeset(zone_keys, ctx, update);
+ }
+ return ret;
+}
+
+int knot_zone_check_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx,
+ bool incremental)
+{
+ int ret = KNOT_EOK;
+ dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone);
+
+ if (incremental) {
+ ret = ctx->policy->nsec3_enabled
+ ? knot_nsec3_check_chain_fix(update, &params)
+ : knot_nsec_check_chain_fix(update);
+ }
+ if (ret == KNOT_ENORECORD) {
+ log_zone_info(update->zone->name, "DNSSEC, re-validating whole NSEC%s chain",
+ (ctx->policy->nsec3_enabled ? "3" : ""));
+ incremental = false;
+ }
+
+ if (incremental) {
+ return ret;
+ }
+
+ return ctx->policy->nsec3_enabled ? knot_nsec3_check_chain(update, &params) :
+ knot_nsec_check_chain(update);
+}
diff --git a/src/knot/dnssec/zone-nsec.h b/src/knot/dnssec/zone-nsec.h
new file mode 100644
index 0000000..c43b658
--- /dev/null
+++ b/src/knot/dnssec/zone-nsec.h
@@ -0,0 +1,163 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/contents.h"
+
+/*!
+ * Check if NSEC3 is enabled for the given zone.
+ *
+ * \param zone Zone to be checked.
+ *
+ * \return NSEC3 is enabled.
+ */
+inline static bool knot_is_nsec3_enabled(const zone_contents_t *zone)
+{
+ return zone != NULL && zone->nsec3_params.algorithm != 0;
+}
+
+inline static size_t zone_nsec3_hash_len(const zone_contents_t *zone)
+{
+ return knot_is_nsec3_enabled(zone) ? dnssec_nsec3_hash_length(zone->nsec3_params.algorithm) : 0;
+}
+
+inline static size_t zone_nsec3_name_len(const zone_contents_t *zone)
+{
+ return 1 + ((zone_nsec3_hash_len(zone) + 4) / 5) * 8 + knot_dname_size(zone->apex->owner);
+}
+
+/*!
+ * \brief Create NSEC3 owner name from hash and zone apex.
+ *
+ * \param out Output buffer.
+ * \param out_size Size of the output buffer.
+ * \param hash Raw hash.
+ * \param hash_size Size of the hash.
+ * \param zone_apex Zone apex.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_nsec3_hash_to_dname(uint8_t *out, size_t out_size, const uint8_t *hash,
+ size_t hash_size, const knot_dname_t *zone_apex);
+
+/*!
+ * \brief Create NSEC3 owner name from regular owner name.
+ *
+ * \param out Output buffer.
+ * \param out_size Size of the output buffer.
+ * \param owner Node owner name.
+ * \param zone_apex Zone apex name.
+ * \param params Params for NSEC3 hashing function.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_create_nsec3_owner(uint8_t *out, size_t out_size,
+ const knot_dname_t *owner, const knot_dname_t *zone_apex,
+ const dnssec_nsec3_params_t *params);
+
+/*!
+ * \brief Return (and compute of needed) the corresponding NSEC3 node's name.
+ *
+ * \param node Normal node.
+ * \param zone Optional: zone contents with NSEC3 params.
+ *
+ * \return NSEC3 node owner.
+ *
+ * \note The result is also stored in (node), unless zone == NULL;
+ */
+knot_dname_t *node_nsec3_hash(zone_node_t *node, const zone_contents_t *zone);
+
+/*!
+ * \brief Return (and compute if needed) the corresponding NSEC3 node.
+ *
+ * \param node Normal node.
+ * \param zone Optional: zone contents with NSEC3 params and NSEC3 tree.
+ *
+ * \return NSEC3 node.
+ *
+ * \note The result is also stored in (node), unless zone == NULL;
+ */
+zone_node_t *node_nsec3_node(zone_node_t *node, const zone_contents_t *zone);
+
+/*!
+ * \brief Update node's NSEC3 pointer (or hash), taking it from bi-node counterpart if possible.
+ *
+ * \param node Bi-node with this node to be updated.
+ * \param zone Zone contents the node is in.
+ *
+ * \return KNOT_EOK :)
+ */
+int binode_fix_nsec3_pointer(zone_node_t *node, const zone_contents_t *zone);
+
+/*!
+ * \brief Check if NSEC3 record in zone is consistent with configured params.
+ */
+bool knot_nsec3param_uptodate(const zone_contents_t *zone,
+ const dnssec_nsec3_params_t *params);
+
+/*!
+ * \brief Update NSEC3PARAM in zone to be consistent with configured params.
+ *
+ * \param update Zone to be updated.
+ * \param params NSEC3 params.
+ * \param ttl Desired TTL for NSEC3PARAM.
+ *
+ * \return KNOT_E*
+ */
+int knot_nsec3param_update(zone_update_t *update,
+ const dnssec_nsec3_params_t *params,
+ uint32_t ttl);
+
+/*!
+ * \brief Create NSEC or NSEC3 chain in the zone.
+ *
+ * \param update Zone Update with current zone contents and to be updated with NSEC chain.
+ * \param ctx Signing context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_create_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx);
+
+/*!
+ * \brief Fix NSEC or NSEC3 chain after zone was updated, and sign the changed NSECs.
+ *
+ * \param update Zone Update with the update and to be update with NSEC chain.
+ * \param zone_keys Zone keys used for NSEC(3) creation.
+ * \param ctx Signing context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_fix_nsec_chain(zone_update_t *update,
+ const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *ctx);
+
+/*!
+ * \brief Validate NSEC or NSEC3 chain in the zone.
+ *
+ * \param update Zone update with current/previous contents.
+ * \param ctx Signing context.
+ * \param incremental Validate incremental update.
+ *
+ * \return KNOT_E*
+ */
+int knot_zone_check_nsec_chain(zone_update_t *update, const kdnssec_ctx_t *ctx,
+ bool incremental);
diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c
new file mode 100644
index 0000000..ffa10c4
--- /dev/null
+++ b/src/knot/dnssec/zone-sign.c
@@ -0,0 +1,1081 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <pthread.h>
+#include <sys/types.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/keytag.h"
+#include "libdnssec/sign.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/key_records.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-sign.h"
+#include "libknot/libknot.h"
+#include "libknot/dynarray.h"
+#include "contrib/wire_ctx.h"
+
+typedef struct {
+ node_t n;
+ uint16_t type;
+} type_node_t;
+
+typedef struct {
+ knot_dname_t *dname;
+ knot_dname_t *hashed_dname;
+ list_t *type_list;
+} signed_info_t;
+
+/*- private API - common functions -------------------------------------------*/
+
+/*!
+ * \brief Initializes RR set and set owner and rclass from template RR set.
+ */
+static knot_rrset_t rrset_init_from(const knot_rrset_t *src, uint16_t type)
+{
+ assert(src);
+ knot_rrset_t rrset;
+ knot_rrset_init(&rrset, src->owner, type, src->rclass, src->ttl);
+ return rrset;
+}
+
+/*!
+ * \brief Create empty RRSIG RR set for a given RR set to be covered.
+ */
+static knot_rrset_t create_empty_rrsigs_for(const knot_rrset_t *covered)
+{
+ assert(!knot_rrset_empty(covered));
+ return rrset_init_from(covered, KNOT_RRTYPE_RRSIG);
+}
+
+static bool apex_rr_changed(const zone_node_t *old_apex,
+ const zone_node_t *new_apex,
+ uint16_t type)
+{
+ assert(old_apex);
+ assert(new_apex);
+ knot_rrset_t old_rr = node_rrset(old_apex, type);
+ knot_rrset_t new_rr = node_rrset(new_apex, type);
+
+ return !knot_rrset_equal(&old_rr, &new_rr, false);
+}
+
+static bool apex_dnssec_changed(zone_update_t *update)
+{
+ if (update->zone->contents == NULL || update->new_cont == NULL) {
+ return false;
+ }
+ return apex_rr_changed(update->zone->contents->apex,
+ update->new_cont->apex, KNOT_RRTYPE_DNSKEY) ||
+ apex_rr_changed(update->zone->contents->apex,
+ update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM);
+}
+
+/*- private API - signing of in-zone nodes -----------------------------------*/
+
+/*!
+ * \brief Check if there is a valid signature for a given RR set and key.
+ *
+ * \param covered RR set with covered records.
+ * \param rrsigs RR set with RRSIGs.
+ * \param key Signing key.
+ * \param ctx Signing context.
+ * \param policy DNSSEC policy.
+ * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
+ * \param refresh Consider RRSIG expired when gonna expire this soon.
+ * \param found_invalid Out: some matching but expired%invalid RRSIG found.
+ * \param at Out: RRSIG position.
+ *
+ * \return The signature exists and is valid.
+ */
+static bool valid_signature_exists(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ const dnssec_key_t *key,
+ dnssec_sign_ctx_t *ctx,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_timediff_t refresh,
+ bool skip_crypto,
+ int *found_invalid,
+ uint16_t *at)
+{
+ assert(key);
+
+ if (knot_rrset_empty(rrsigs)) {
+ return false;
+ }
+
+ uint16_t rrsigs_rdata_count = rrsigs->rrs.count;
+ knot_rdata_t *rdata = rrsigs->rrs.rdata;
+ bool found_valid = false;
+ for (uint16_t i = 0; i < rrsigs_rdata_count; i++) {
+ uint16_t rr_keytag = knot_rrsig_key_tag(rdata);
+ uint16_t rr_covered = knot_rrsig_type_covered(rdata);
+ uint8_t rr_algo = knot_rrsig_alg(rdata);
+ rdata = knot_rdataset_next(rdata);
+
+ uint16_t keytag = dnssec_key_get_keytag(key);
+ uint8_t algo = dnssec_key_get_algorithm(key);
+ if (rr_keytag != keytag || rr_algo != algo || rr_covered != covered->type) {
+ continue;
+ }
+
+ int ret = knot_check_signature(covered, rrsigs, i, key, ctx,
+ dnssec_ctx, refresh, skip_crypto);
+ if (ret == KNOT_EOK) {
+ if (at != NULL) {
+ *at = i;
+ }
+ if (found_invalid == NULL) {
+ return true;
+ } else {
+ found_valid = true; // continue searching for invalid RRSIG
+ }
+ } else if (found_invalid != NULL) {
+ *found_invalid = ret;
+ }
+ }
+
+ return found_valid;
+}
+
+/*!
+ * \brief Note earliest expiration of a signature.
+ *
+ * \param rrsig RRSIG rdata.
+ * \param now Current 64-bit timestamp.
+ * \param expires_at Current earliest expiration, will be updated.
+ */
+static void note_earliest_expiration(const knot_rdata_t *rrsig, knot_time_t now,
+ knot_time_t *expires_at)
+{
+ assert(rrsig);
+ if (expires_at == NULL) {
+ return;
+ }
+
+ uint32_t curr_rdata = knot_rrsig_sig_expiration(rrsig);
+ knot_time_t current = knot_time_from_u32(curr_rdata, now);
+
+ *expires_at = knot_time_min(current, *expires_at);
+}
+
+bool rrsig_covers_type(const knot_rrset_t *rrsig, uint16_t type)
+{
+ if (knot_rrset_empty(rrsig)) {
+ return false;
+ }
+ assert(rrsig->type == KNOT_RRTYPE_RRSIG);
+ knot_rdata_t *one_rr = rrsig->rrs.rdata;
+ for (int i = 0; i < rrsig->rrs.count; i++) {
+ if (type == knot_rrsig_type_covered(one_rr)) {
+ return true;
+ }
+ one_rr = knot_rdataset_next(one_rr);
+ }
+ return false;
+}
+
+/*!
+ * \brief Add missing RRSIGs into the changeset for adding.
+ *
+ * \note Also removes invalid RRSIGs.
+ *
+ * \param covered RR set with covered records.
+ * \param rrsigs RR set with RRSIGs.
+ * \param sign_ctx Local zone signing context.
+ * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
+ * \param changeset Changeset to be updated.
+ * \param update Zone update to be updated. Exactly one of "changeset" and "update" must be NULL!
+ * \param expires_at Earliest RRSIG expiration.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int add_missing_rrsigs(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx,
+ bool skip_crypto,
+ changeset_t *changeset,
+ zone_update_t *update,
+ knot_time_t *expires_at)
+{
+ assert(!knot_rrset_empty(covered));
+ assert(sign_ctx);
+ assert((bool)changeset != (bool)update);
+
+ knot_rrset_t to_add = create_empty_rrsigs_for(covered);
+ knot_rrset_t to_remove = create_empty_rrsigs_for(covered);
+ int result = (!rrsig_covers_type(rrsigs, covered->type) ? KNOT_EOK :
+ knot_synth_rrsig(covered->type, &rrsigs->rrs, &to_remove.rrs, NULL));
+
+ if (result == KNOT_EOK && sign_ctx->dnssec_ctx->offline_records.rrsig.rrs.count > 0 &&
+ knot_dname_cmp(sign_ctx->dnssec_ctx->offline_records.rrsig.owner, covered->owner) == 0 &&
+ rrsig_covers_type(&sign_ctx->dnssec_ctx->offline_records.rrsig, covered->type)) {
+ result = knot_synth_rrsig(covered->type,
+ &sign_ctx->dnssec_ctx->offline_records.rrsig.rrs, &to_add.rrs, NULL);
+ if (result == KNOT_EOK) {
+ // don't remove what shall be added
+ result = knot_rdataset_subtract(&to_remove.rrs, &to_add.rrs, NULL);
+ }
+ if (result == KNOT_EOK && !knot_rrset_empty(rrsigs)) {
+ // don't add what's already present
+ result = knot_rdataset_subtract(&to_add.rrs, &rrsigs->rrs, NULL);
+ }
+ }
+
+ for (size_t i = 0; i < sign_ctx->count && result == KNOT_EOK; i++) {
+ const zone_key_t *key = &sign_ctx->keys[i];
+ if (!knot_zone_sign_use_key(key, covered)) {
+ continue;
+ }
+
+ uint16_t valid_at;
+ knot_timediff_t refresh = sign_ctx->dnssec_ctx->policy->rrsig_refresh_before +
+ sign_ctx->dnssec_ctx->policy->rrsig_prerefresh;
+ if (valid_signature_exists(covered, rrsigs, key->key, sign_ctx->sign_ctxs[i],
+ sign_ctx->dnssec_ctx, refresh, skip_crypto, NULL, &valid_at)) {
+ knot_rdata_t *valid_rr = knot_rdataset_at(&rrsigs->rrs, valid_at);
+ result = knot_rdataset_remove(&to_remove.rrs, valid_rr, NULL);
+ note_earliest_expiration(valid_rr, sign_ctx->dnssec_ctx->now, expires_at);
+ continue;
+ }
+ result = knot_sign_rrset(&to_add, covered, key->key, sign_ctx->sign_ctxs[i],
+ sign_ctx->dnssec_ctx, NULL, expires_at);
+ }
+
+ if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) {
+ if (changeset != NULL) {
+ result = changeset_add_removal(changeset, &to_remove, 0);
+ } else {
+ result = zone_update_remove(update, &to_remove);
+ }
+ }
+
+ if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) {
+ if (changeset != NULL) {
+ result = changeset_add_addition(changeset, &to_add, 0);
+ } else {
+ result = zone_update_add(update, &to_add);
+ }
+ }
+
+ knot_rdataset_clear(&to_add.rrs, NULL);
+ knot_rdataset_clear(&to_remove.rrs, NULL);
+
+ return result;
+}
+
+static bool key_used(bool ksk, bool zsk, uint16_t type,
+ const knot_dname_t *owner, const knot_dname_t *zone_apex)
+{
+ if (knot_dname_cmp(owner, zone_apex) != 0) {
+ return zsk;
+ }
+ switch (type) {
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ case KNOT_RRTYPE_CDS:
+ return ksk;
+ default:
+ return zsk;
+ }
+}
+
+int knot_validate_rrsigs(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx,
+ bool skip_crypto)
+{
+ if (covered == NULL || rrsigs == NULL || sign_ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ bool valid_exists = false;
+ int ret = KNOT_EOK;
+ for (size_t i = 0; i < sign_ctx->count; i++) {
+ const knot_kasp_key_t *key = &sign_ctx->dnssec_ctx->zone->keys[i];
+ if (!key_used(key->is_ksk, key->is_zsk, covered->type,
+ covered->owner, sign_ctx->dnssec_ctx->zone->dname)) {
+ continue;
+ }
+
+ uint16_t valid_at;
+ if (valid_signature_exists(covered, rrsigs, key->key, sign_ctx->sign_ctxs[i],
+ sign_ctx->dnssec_ctx, 0, skip_crypto, &ret, &valid_at)) {
+ valid_exists = true;
+ }
+ }
+
+ return valid_exists ? ret : KNOT_DNSSEC_ENOSIG;
+}
+
+/*!
+ * \brief Add all RRSIGs into the changeset for removal.
+ *
+ * \param covered RR set with covered records.
+ * \param changeset Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type,
+ const knot_rrset_t *rrsigs,
+ changeset_t *changeset)
+{
+ assert(owner);
+ assert(changeset);
+ knot_rrset_t synth_rrsig;
+ knot_rrset_init(&synth_rrsig, (knot_dname_t *)owner,
+ KNOT_RRTYPE_RRSIG, rrsigs->rclass, rrsigs->ttl);
+ int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrsig.rrs, NULL);
+ if (ret != KNOT_EOK) {
+ if (ret != KNOT_ENOENT) {
+ return ret;
+ }
+ return KNOT_EOK;
+ }
+
+ ret = changeset_add_removal(changeset, &synth_rrsig, 0);
+ knot_rdataset_clear(&synth_rrsig.rrs, NULL);
+
+ return ret;
+}
+
+/*!
+ * \brief Drop all existing and create new RRSIGs for covered records.
+ *
+ * \param covered RR set with covered records.
+ * \param rrsigs Existing RRSIGs for covered RR set.
+ * \param sign_ctx Local zone signing context.
+ * \param changeset Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int force_resign_rrset(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx,
+ changeset_t *changeset)
+{
+ assert(!knot_rrset_empty(covered));
+
+ if (!knot_rrset_empty(rrsigs)) {
+ int result = remove_rrset_rrsigs(covered->owner, covered->type,
+ rrsigs, changeset);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+ }
+
+ return add_missing_rrsigs(covered, NULL, sign_ctx, false, changeset, NULL, NULL);
+}
+
+/*!
+ * \brief Drop all expired and create new RRSIGs for covered records.
+ *
+ * \param covered RR set with covered records.
+ * \param rrsigs Existing RRSIGs for covered RR set.
+ * \param sign_ctx Local zone signing context.
+ * \param skip_crypto All RRSIGs in this node have been verified, just check validity.
+ * \param changeset Changeset to be updated.
+ * \param expires_at Current earliest expiration, will be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int resign_rrset(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx,
+ bool skip_crypto,
+ changeset_t *changeset,
+ knot_time_t *expires_at)
+{
+ assert(!knot_rrset_empty(covered));
+
+ return add_missing_rrsigs(covered, rrsigs, sign_ctx, skip_crypto, changeset, NULL, expires_at);
+}
+
+static int remove_standalone_rrsigs(const zone_node_t *node,
+ const knot_rrset_t *rrsigs,
+ changeset_t *changeset)
+{
+ if (rrsigs == NULL) {
+ return KNOT_EOK;
+ }
+
+ uint16_t rrsigs_rdata_count = rrsigs->rrs.count;
+ knot_rdata_t *rdata = rrsigs->rrs.rdata;
+ for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) {
+ uint16_t type_covered = knot_rrsig_type_covered(rdata);
+ if (!node_rrtype_exists(node, type_covered)) {
+ knot_rrset_t to_remove;
+ knot_rrset_init(&to_remove, rrsigs->owner, rrsigs->type,
+ rrsigs->rclass, rrsigs->ttl);
+ int ret = knot_rdataset_add(&to_remove.rrs, rdata, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ret = changeset_add_removal(changeset, &to_remove, 0);
+ knot_rdataset_clear(&to_remove.rrs, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ rdata = knot_rdataset_next(rdata);
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Update RRSIGs in a given node by updating changeset.
+ *
+ * \param node Node to be signed.
+ * \param sign_ctx Local zone signing context.
+ * \param changeset Changeset to be updated.
+ * \param expires_at Current earliest expiration, will be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int sign_node_rrsets(const zone_node_t *node,
+ zone_sign_ctx_t *sign_ctx,
+ changeset_t *changeset,
+ knot_time_t *expires_at,
+ dnssec_validation_hint_t *hint)
+{
+ assert(node);
+ assert(sign_ctx);
+
+ int result = KNOT_EOK;
+ knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
+ bool skip_crypto = (node->flags & NODE_FLAGS_RRSIGS_VALID) &&
+ !sign_ctx->dnssec_ctx->keytag_conflict;
+
+ for (int i = 0; result == KNOT_EOK && i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ assert(rrset.type != KNOT_RRTYPE_ANY);
+
+ if (!knot_zone_sign_rr_should_be_signed(node, &rrset)) {
+ if (!sign_ctx->dnssec_ctx->validation_mode) {
+ result = remove_rrset_rrsigs(rrset.owner, rrset.type, &rrsigs, changeset);
+ } else {
+ if (knot_synth_rrsig_exists(rrset.type, &rrsigs.rrs)) {
+ hint->node = node->owner;
+ hint->rrtype = rrset.type;
+ result = KNOT_DNSSEC_ENOSIG;
+ }
+ }
+ continue;
+ }
+
+ if (sign_ctx->dnssec_ctx->validation_mode) {
+ result = knot_validate_rrsigs(&rrset, &rrsigs, sign_ctx, skip_crypto);
+ if (result != KNOT_EOK) {
+ hint->node = node->owner;
+ hint->rrtype = rrset.type;
+ }
+ } else if (sign_ctx->dnssec_ctx->rrsig_drop_existing) {
+ result = force_resign_rrset(&rrset, &rrsigs,
+ sign_ctx, changeset);
+ } else {
+ result = resign_rrset(&rrset, &rrsigs, sign_ctx, skip_crypto,
+ changeset, expires_at);
+ }
+ }
+
+ if (result == KNOT_EOK) {
+ result = remove_standalone_rrsigs(node, &rrsigs, changeset);
+ }
+ return result;
+}
+
+/*!
+ * \brief Struct to carry data for 'sign_data' callback function.
+ */
+typedef struct {
+ zone_tree_t *tree;
+ zone_sign_ctx_t *sign_ctx;
+ changeset_t changeset;
+ knot_time_t expires_at;
+ dnssec_validation_hint_t *hint;
+ size_t num_threads;
+ size_t thread_index;
+ size_t rrset_index;
+ int errcode;
+ int thread_init_errcode;
+ pthread_t thread;
+} node_sign_args_t;
+
+/*!
+ * \brief Sign node (callback function).
+ *
+ * \param node Node to be signed.
+ * \param data Callback data, node_sign_args_t.
+ */
+static int sign_node(zone_node_t *node, void *data)
+{
+ assert(node);
+ assert(data);
+
+ node_sign_args_t *args = (node_sign_args_t *)data;
+
+ if (node->rrset_count == 0) {
+ return KNOT_EOK;
+ }
+
+ if (args->rrset_index++ % args->num_threads != args->thread_index) {
+ return KNOT_EOK;
+ }
+
+ int result = sign_node_rrsets(node, args->sign_ctx,
+ &args->changeset, &args->expires_at,
+ args->hint);
+
+ return result;
+}
+
+static void *tree_sign_thread(void *_arg)
+{
+ node_sign_args_t *arg = _arg;
+ arg->errcode = zone_tree_apply(arg->tree, sign_node, _arg);
+ return NULL;
+}
+
+static int set_signed(zone_node_t *node, _unused_ void *data)
+{
+ node->flags |= NODE_FLAGS_RRSIGS_VALID;
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Update RRSIGs in a given zone tree by updating changeset.
+ *
+ * \param tree Zone tree to be signed.
+ * \param num_threads Number of threads to use for parallel signing.
+ * \param zone_keys Zone keys.
+ * \param policy DNSSEC policy.
+ * \param update Zone update structure to be updated.
+ * \param expires_at Expiration time of the oldest signature in zone.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static int zone_tree_sign(zone_tree_t *tree,
+ size_t num_threads,
+ zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ zone_update_t *update,
+ knot_time_t *expires_at)
+{
+ assert(zone_keys || dnssec_ctx->validation_mode);
+ assert(dnssec_ctx);
+ assert(update || dnssec_ctx->validation_mode);
+
+ int ret = KNOT_EOK;
+ node_sign_args_t args[num_threads];
+ memset(args, 0, sizeof(args));
+ *expires_at = knot_time_plus(dnssec_ctx->now, dnssec_ctx->policy->rrsig_lifetime);
+
+ // init context structures
+ for (size_t i = 0; i < num_threads; i++) {
+ args[i].tree = tree;
+ args[i].sign_ctx = dnssec_ctx->validation_mode
+ ? zone_validation_ctx(dnssec_ctx)
+ : zone_sign_ctx(zone_keys, dnssec_ctx);
+ if (args[i].sign_ctx == NULL) {
+ ret = KNOT_ENOMEM;
+ break;
+ }
+ ret = changeset_init(&args[i].changeset, dnssec_ctx->zone->dname);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ args[i].expires_at = 0;
+ args[i].hint = &update->validation_hint;
+ args[i].num_threads = num_threads;
+ args[i].thread_index = i;
+ args[i].rrset_index = 0;
+ args[i].errcode = KNOT_EOK;
+ args[i].thread_init_errcode = -1;
+ }
+ if (ret != KNOT_EOK) {
+ for (size_t i = 0; i < num_threads; i++) {
+ changeset_clear(&args[i].changeset);
+ zone_sign_ctx_free(args[i].sign_ctx);
+ }
+ return ret;
+ }
+
+ if (num_threads == 1) {
+ args[0].thread_init_errcode = 0;
+ tree_sign_thread(&args[0]);
+ } else {
+ // start working threads
+ for (size_t i = 0; i < num_threads; i++) {
+ args[i].thread_init_errcode =
+ pthread_create(&args[i].thread, NULL, tree_sign_thread, &args[i]);
+ }
+
+ // join those threads that have been really started
+ for (size_t i = 0; i < num_threads; i++) {
+ if (args[i].thread_init_errcode == 0) {
+ args[i].thread_init_errcode = pthread_join(args[i].thread, NULL);
+ }
+ }
+ }
+
+ // collect return code and results
+ for (size_t i = 0; i < num_threads; i++) {
+ if (ret == KNOT_EOK) {
+ if (args[i].thread_init_errcode != 0) {
+ ret = knot_map_errno_code(args[i].thread_init_errcode);
+ } else {
+ ret = args[i].errcode;
+ if (ret == KNOT_EOK && !dnssec_ctx->validation_mode) {
+ ret = zone_update_apply_changeset(update, &args[i].changeset); // _fix not needed
+ *expires_at = knot_time_min(*expires_at, args[i].expires_at);
+ }
+ }
+ }
+ assert(!dnssec_ctx->validation_mode || changeset_empty(&args[i].changeset));
+ changeset_clear(&args[i].changeset);
+ zone_sign_ctx_free(args[i].sign_ctx);
+ }
+
+ return ret;
+}
+
+/*- private API - signing of NSEC(3) in changeset ----------------------------*/
+
+/*!
+ * \brief Struct to carry data for changeset signing callback functions.
+ */
+typedef struct {
+ const zone_contents_t *zone;
+ changeset_iter_t itt;
+ zone_sign_ctx_t *sign_ctx;
+ changeset_t changeset;
+ knot_time_t expires_at;
+ size_t num_threads;
+ size_t thread_index;
+ size_t rrset_index;
+ int errcode;
+ int thread_init_errcode;
+ pthread_t thread;
+} changeset_signing_data_t;
+
+int rrset_add_zone_key(knot_rrset_t *rrset, zone_key_t *zone_key)
+{
+ if (rrset == NULL || zone_key == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ dnssec_binary_t dnskey_rdata = { 0 };
+ dnssec_key_get_rdata(zone_key->key, &dnskey_rdata);
+
+ return knot_rrset_add_rdata(rrset, dnskey_rdata.data, dnskey_rdata.size, NULL);
+}
+
+static int rrset_add_zone_ds(knot_rrset_t *rrset, zone_key_t *zone_key, dnssec_key_digest_t dt)
+{
+ assert(rrset);
+ assert(zone_key);
+
+ dnssec_binary_t cds_rdata = { 0 };
+ zone_key_calculate_ds(zone_key, dt, &cds_rdata);
+
+ return knot_rrset_add_rdata(rrset, cds_rdata.data, cds_rdata.size, NULL);
+}
+
+int knot_zone_sign(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *expire_at)
+{
+ if (!update || !dnssec_ctx || !expire_at ||
+ dnssec_ctx->policy->signing_threads < 1 ||
+ (zone_keys == NULL && !dnssec_ctx->validation_mode)) {
+ return KNOT_EINVAL;
+ }
+
+ int result;
+
+ knot_time_t normal_expire = 0;
+ result = zone_tree_sign(update->new_cont->nodes, dnssec_ctx->policy->signing_threads,
+ zone_keys, dnssec_ctx, update, &normal_expire);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ knot_time_t nsec3_expire = 0;
+ result = zone_tree_sign(update->new_cont->nsec3_nodes, dnssec_ctx->policy->signing_threads,
+ zone_keys, dnssec_ctx, update, &nsec3_expire);
+ if (result != KNOT_EOK) {
+ return result;
+ }
+
+ bool whole = !(update->flags & UPDATE_INCREMENTAL);
+ result = zone_tree_apply(whole ? update->new_cont->nodes : update->a_ctx->node_ptrs, set_signed, NULL);
+ if (result == KNOT_EOK) {
+ result = zone_tree_apply(whole ? update->new_cont->nsec3_nodes : update->a_ctx->nsec3_ptrs, set_signed, NULL);
+ }
+
+ *expire_at = knot_time_min(normal_expire, nsec3_expire);
+
+ return result;
+}
+
+keyptr_dynarray_t knot_zone_sign_get_cdnskeys(const kdnssec_ctx_t *ctx,
+ zone_keyset_t *zone_keys)
+{
+ keyptr_dynarray_t r = { 0 };
+ unsigned crp = ctx->policy->cds_cdnskey_publish;
+ unsigned cds_published = 0;
+ uint8_t ready_alg = 0;
+
+ if (crp == CDS_CDNSKEY_ROLLOVER || crp == CDS_CDNSKEY_ALWAYS ||
+ crp == CDS_CDNSKEY_DOUBLE_DS) {
+ // first, add strictly-ready keys
+ for (int i = 0; i < zone_keys->count; i++) {
+ zone_key_t *key = &zone_keys->keys[i];
+ if (key->is_ready) {
+ assert(key->is_ksk);
+ ready_alg = dnssec_key_get_algorithm(key->key);
+ keyptr_dynarray_add(&r, &key);
+ if (!key->is_pub_only) {
+ cds_published++;
+ }
+ }
+ }
+
+ // second, add active keys
+ if ((crp == CDS_CDNSKEY_ALWAYS && cds_published == 0) ||
+ (crp == CDS_CDNSKEY_DOUBLE_DS)) {
+ for (int i = 0; i < zone_keys->count; i++) {
+ zone_key_t *key = &zone_keys->keys[i];
+ if (key->is_ksk && key->is_active && !key->is_ready &&
+ (cds_published == 0 || ready_alg == dnssec_key_get_algorithm(key->key))) {
+ keyptr_dynarray_add(&r, &key);
+ }
+ }
+ }
+
+ if ((crp != CDS_CDNSKEY_DOUBLE_DS && cds_published > 1) ||
+ (cds_published > 2)) {
+ log_zone_warning(ctx->zone->dname, "DNSSEC, published CDS/CDNSKEY records for too many (%u) keys", cds_published);
+ }
+ }
+
+ return r;
+}
+
+int knot_zone_sign_add_dnskeys(zone_keyset_t *zone_keys, const kdnssec_ctx_t *dnssec_ctx,
+ key_records_t *add_r, key_records_t *rem_r, key_records_t *orig_r)
+{
+ if (add_r == NULL || (rem_r != NULL && orig_r == NULL)) {
+ return KNOT_EINVAL;
+ }
+
+ bool incremental = (dnssec_ctx->policy->incremental && rem_r != NULL);
+ dnssec_key_digest_t cds_dt = dnssec_ctx->policy->cds_dt;
+ int ret = KNOT_EOK;
+
+ for (int i = 0; i < zone_keys->count; i++) {
+ zone_key_t *key = &zone_keys->keys[i];
+ if (key->is_public) {
+ ret = rrset_add_zone_key(&add_r->dnskey, key);
+ } else if (incremental) {
+ ret = rrset_add_zone_key(&rem_r->dnskey, key);
+ }
+
+ // add all possible known CDNSKEYs and CDSs to removals. Sort it out later
+ if (incremental && ret == KNOT_EOK) {
+ ret = rrset_add_zone_key(&rem_r->cdnskey, key);
+ }
+ if (incremental && ret == KNOT_EOK) {
+ ret = rrset_add_zone_ds(&rem_r->cds, key, cds_dt);
+ }
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ keyptr_dynarray_t kcdnskeys = knot_zone_sign_get_cdnskeys(dnssec_ctx, zone_keys);
+ knot_dynarray_foreach(keyptr, zone_key_t *, ksk_for_cds, kcdnskeys) {
+ ret = rrset_add_zone_key(&add_r->cdnskey, *ksk_for_cds);
+ if (ret == KNOT_EOK) {
+ ret = rrset_add_zone_ds(&add_r->cds, *ksk_for_cds, cds_dt);
+ }
+ }
+
+ if (incremental && ret == KNOT_EOK) { // else rem_r is empty
+ ret = key_records_subtract(rem_r, add_r);
+ if (ret == KNOT_EOK) {
+ ret = key_records_intersect(rem_r, orig_r);
+ }
+ if (ret == KNOT_EOK) {
+ ret = key_records_subtract(add_r, orig_r);
+ }
+ }
+
+ if (dnssec_ctx->policy->cds_cdnskey_publish == CDS_CDNSKEY_EMPTY && ret == KNOT_EOK) {
+ const uint8_t cdnskey_empty[5] = { 0, 0, 3, 0, 0 };
+ const uint8_t cds_empty[5] = { 0, 0, 0, 0, 0 };
+ ret = knot_rrset_add_rdata(&add_r->cdnskey, cdnskey_empty, sizeof(cdnskey_empty), NULL);
+ if (ret == KNOT_EOK) {
+ ret = knot_rrset_add_rdata(&add_r->cds, cds_empty, sizeof(cds_empty), NULL);
+ }
+ }
+
+ keyptr_dynarray_free(&kcdnskeys);
+ return ret;
+}
+
+int knot_zone_sign_update_dnskeys(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ kdnssec_ctx_t *dnssec_ctx)
+{
+ if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (dnssec_ctx->policy->unsafe & UNSAFE_DNSKEY) {
+ return KNOT_EOK;
+ }
+
+ const zone_node_t *apex = update->new_cont->apex;
+ knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA);
+ if (knot_rrset_empty(&soa)) {
+ return KNOT_EINVAL;
+ }
+
+ key_records_t orig_r;
+ key_records_from_apex(apex, &orig_r);
+
+ changeset_t ch;
+ int ret = changeset_init(&ch, apex->owner);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (!dnssec_ctx->policy->incremental) {
+ // remove all. This will cancel out with additions later
+ ret = key_records_to_changeset(&orig_r, &ch, true, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ key_records_t add_r, rem_r;
+ key_records_init(dnssec_ctx, &add_r);
+ key_records_init(dnssec_ctx, &rem_r);
+
+#define CHECK_RET if (ret != KNOT_EOK) goto cleanup
+
+ if (dnssec_ctx->policy->offline_ksk) {
+ key_records_t *r = &dnssec_ctx->offline_records;
+ log_zone_info(dnssec_ctx->zone->dname,
+ "DNSSEC, using offline records, DNSKEYs %hu, CDNSKEYs %hu, CDs %hu, RRSIGs %hu",
+ r->dnskey.rrs.count, r->cdnskey.rrs.count, r->cds.rrs.count, r->rrsig.rrs.count);
+ ret = key_records_to_changeset(r, &ch, false, CHANGESET_CHECK);
+ CHECK_RET;
+ } else {
+ ret = knot_zone_sign_add_dnskeys(zone_keys, dnssec_ctx, &add_r, &rem_r, &orig_r);
+ CHECK_RET;
+ ret = key_records_to_changeset(&rem_r, &ch, true, CHANGESET_CHECK);
+ CHECK_RET;
+ ret = key_records_to_changeset(&add_r, &ch, false, CHANGESET_CHECK);
+ CHECK_RET;
+ }
+
+ if (dnssec_ctx->policy->ds_push && node_rrtype_exists(ch.add->apex, KNOT_RRTYPE_CDS)) {
+ // there is indeed a change to CDS
+ update->zone->timers.next_ds_push = time(NULL) + dnssec_ctx->policy->propagation_delay;
+ zone_events_schedule_at(update->zone, ZONE_EVENT_DS_PUSH, update->zone->timers.next_ds_push);
+ }
+
+ ret = zone_update_apply_changeset(update, &ch);
+
+#undef CHECK_RET
+
+cleanup:
+ key_records_clear(&add_r);
+ key_records_clear(&rem_r);
+ changeset_clear(&ch);
+ return ret;
+}
+
+bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered)
+{
+ if (key == NULL || covered == NULL) {
+ return false;
+ }
+
+ bool active_ksk = ((key->is_active || key->is_ksk_active_plus) && key->is_ksk);
+ bool active_zsk = ((key->is_active || key->is_zsk_active_plus) && key->is_zsk);;
+
+ // this may be a problem with offline KSK
+ bool cds_sign_by_ksk = true;
+
+ assert(key->is_zsk || key->is_ksk);
+ bool is_apex = knot_dname_is_equal(covered->owner,
+ dnssec_key_get_dname(key->key));
+ if (!is_apex) {
+ return active_zsk;
+ }
+
+ switch (covered->type) {
+ case KNOT_RRTYPE_DNSKEY:
+ return active_ksk;
+ case KNOT_RRTYPE_CDS:
+ case KNOT_RRTYPE_CDNSKEY:
+ return (cds_sign_by_ksk ? active_ksk : active_zsk);
+ default:
+ return active_zsk;
+ }
+}
+
+static int sign_in_changeset(zone_node_t *node, uint16_t rrtype, knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx, int ret_prev,
+ bool skip_crypto, zone_update_t *up)
+{
+ if (ret_prev != KNOT_EOK) {
+ return ret_prev;
+ }
+ knot_rrset_t rr = node_rrset(node, rrtype);
+ if (knot_rrset_empty(&rr)) {
+ return KNOT_EOK;
+ }
+ return add_missing_rrsigs(&rr, rrsigs, sign_ctx, skip_crypto, NULL, up, NULL);
+}
+
+int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ zone_update_t *update)
+{
+ if (zone_keys == NULL || dnssec_ctx == NULL || update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
+ if (sign_ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_double_begin(update->a_ctx->node_ptrs, update->a_ctx->nsec3_ptrs, &it);
+
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ zone_node_t *n = zone_tree_it_val(&it);
+ bool skip_crypto = (n->flags & NODE_FLAGS_RRSIGS_VALID) && !dnssec_ctx->keytag_conflict;
+
+ knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC, &rrsigs, sign_ctx, ret, skip_crypto, update);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3, &rrsigs, sign_ctx, ret, skip_crypto, update);
+ ret = sign_in_changeset(n, KNOT_RRTYPE_NSEC3PARAM, &rrsigs, sign_ctx, ret, skip_crypto, update);
+
+ if (ret == KNOT_EOK) {
+ n->flags |= NODE_FLAGS_RRSIGS_VALID; // non-NSEC RRSIGs had been validated in knot_dnssec_sign_update()
+ }
+
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ zone_sign_ctx_free(sign_ctx);
+
+ return ret;
+}
+
+bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
+ const knot_rrset_t *rrset)
+{
+ if (node == NULL || knot_rrset_empty(rrset)) {
+ return false;
+ }
+
+ if (rrset->type == KNOT_RRTYPE_RRSIG || (node->flags & NODE_FLAGS_NONAUTH)) {
+ return false;
+ }
+
+ // At delegation points we only want to sign NSECs and DSs
+ if (node->flags & NODE_FLAGS_DELEG) {
+ if (!(rrset->type == KNOT_RRTYPE_NSEC ||
+ rrset->type == KNOT_RRTYPE_DS)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int knot_zone_sign_update(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *expire_at)
+{
+ if (update == NULL || dnssec_ctx == NULL || expire_at == NULL ||
+ dnssec_ctx->policy->signing_threads < 1 ||
+ (zone_keys == NULL && !dnssec_ctx->validation_mode)) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ /* Check if the UPDATE changed DNSKEYs or NSEC3PARAM.
+ * If so, we have to sign the whole zone. */
+ const bool full_sign = apex_dnssec_changed(update);
+ if (full_sign) {
+ ret = knot_zone_sign(update, zone_keys, dnssec_ctx, expire_at);
+ } else {
+ ret = zone_tree_sign(update->a_ctx->node_ptrs, dnssec_ctx->policy->signing_threads,
+ zone_keys, dnssec_ctx, update, expire_at);
+ if (ret == KNOT_EOK) {
+ ret = zone_tree_apply(update->a_ctx->node_ptrs, set_signed, NULL);
+ }
+ if (ret == KNOT_EOK && dnssec_ctx->validation_mode) {
+ ret = zone_tree_sign(update->a_ctx->nsec3_ptrs, dnssec_ctx->policy->signing_threads,
+ zone_keys, dnssec_ctx, update, expire_at);
+ }
+ if (ret == KNOT_EOK && dnssec_ctx->validation_mode) {
+ ret = zone_tree_apply(update->a_ctx->nsec3_ptrs, set_signed, NULL);
+ }
+ }
+
+ return ret;
+}
+
+int knot_zone_sign_apex_rr(zone_update_t *update, uint16_t rrtype,
+ const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx)
+{
+ knot_rrset_t rr = node_rrset(update->new_cont->apex, rrtype);
+ knot_rrset_t rrsig = node_rrset(update->new_cont->apex, KNOT_RRTYPE_RRSIG);
+ changeset_t ch;
+ int ret = changeset_init(&ch, update->zone->name);
+ if (ret == KNOT_EOK) {
+ zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx);
+ if (sign_ctx == NULL) {
+ changeset_clear(&ch);
+ return KNOT_ENOMEM;
+ }
+ ret = force_resign_rrset(&rr, &rrsig, sign_ctx, &ch);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_apply_changeset(update, &ch);
+ }
+ zone_sign_ctx_free(sign_ctx);
+ }
+ changeset_clear(&ch);
+ return ret;
+}
diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h
new file mode 100644
index 0000000..ba6e2b2
--- /dev/null
+++ b/src/knot/dnssec/zone-sign.h
@@ -0,0 +1,162 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/updates/changesets.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/contents.h"
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/zone-keys.h"
+
+int rrset_add_zone_key(knot_rrset_t *rrset, zone_key_t *zone_key);
+
+bool rrsig_covers_type(const knot_rrset_t *rrsig, uint16_t type);
+
+/*!
+ * \brief Prepare DNSKEYs, CDNSKEYs and CDSs to be added to the zone into rrsets.
+ *
+ * \param zone_keys Zone keyset.
+ * \param dnssec_ctx KASP context.
+ * \param add_r RRSets to be added.
+ * \param rem_r RRSets to be removed (only for incremental policy).
+ * \param orig_r RRSets that was originally in zone (only for incremental policy).
+ *
+ * \return KNOT_E*
+ */
+int knot_zone_sign_add_dnskeys(zone_keyset_t *zone_keys, const kdnssec_ctx_t *dnssec_ctx,
+ key_records_t *add_r, key_records_t *rem_r, key_records_t *orig_r);
+
+/*!
+ * \brief Adds/removes DNSKEY (and CDNSKEY, CDS) records to zone according to zone keyset.
+ *
+ * \param update Structure holding zone contents and to be updated with changes.
+ * \param zone_keys Keyset with private keys.
+ * \param dnssec_ctx KASP context.
+ *
+ * \return KNOT_E*
+ */
+int knot_zone_sign_update_dnskeys(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ kdnssec_ctx_t *dnssec_ctx);
+
+/*!
+ * \brief Check if key can be used to sign given RR.
+ *
+ * \param key Zone key.
+ * \param covered RR to be checked.
+ *
+ * \return The RR should be signed.
+ */
+bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered);
+
+/*!
+ * \brief Return those keys for whose the CDNSKEY/CDS records shall be created.
+ *
+ * \param ctx DNSSEC context.
+ * \param zone_keys Zone keyset, including ZSKs.
+ *
+ * \return Dynarray containing pointers on some KSKs in keyset.
+ */
+keyptr_dynarray_t knot_zone_sign_get_cdnskeys(const kdnssec_ctx_t *ctx,
+ zone_keyset_t *zone_keys);
+
+/*!
+ * \brief Check that at least one correct signature exists to at least one DNSKEY and that none incorrect exists.
+ *
+ * \param covered RRSet bein validated.
+ * \param rrsigs RRSIG with signatures.
+ * \param sign_ctx Signing context (with keys == NULL)
+ * \param skip_crypto Crypto operations might be skipped as they had been successful earlier.
+ *
+ * \return KNOT_E*
+ */
+int knot_validate_rrsigs(const knot_rrset_t *covered,
+ const knot_rrset_t *rrsigs,
+ zone_sign_ctx_t *sign_ctx,
+ bool skip_crypto);
+
+/*!
+ * \brief Update zone signatures and store performed changes in update.
+ *
+ * Updates RRSIGs, NSEC(3)s, and DNSKEYs.
+ *
+ * \param update Zone Update containing the zone and to be updated with new DNSKEYs and RRSIGs.
+ * \param zone_keys Zone keys.
+ * \param dnssec_ctx DNSSEC context.
+ * \param expire_at Time, when the oldest signature in the zone expires.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *expire_at);
+
+/*!
+ * \brief Sign NSEC/NSEC3 nodes in changeset and update the changeset.
+ *
+ * \param zone_keys Zone keys.
+ * \param dnssec_ctx DNSSEC context.
+ * \param changeset Changeset to be updated.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ zone_update_t *update);
+
+/*!
+ * \brief Checks whether RRSet in a node has to be signed. Will not return
+ * true for all types that should be signed, do not use this as an
+ * universal function, it is implementation specific.
+ *
+ * \param node Node containing the RRSet.
+ * \param rrset RRSet we are checking for.
+ *
+ * \retval true if should be signed.
+ */
+bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node,
+ const knot_rrset_t *rrset);
+
+/*!
+ * \brief Sign updates of the zone, storing new RRSIGs in this update again.
+ *
+ * \param update Zone Update structure.
+ * \param zone_keys Zone keys.
+ * \param dnssec_ctx DNSSEC context.
+ * \param expire_at Time, when the oldest signature in the update expires.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_zone_sign_update(zone_update_t *update,
+ zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx,
+ knot_time_t *expire_at);
+
+/*!
+ * \brief Force re-sign of a RRSet in zone apex.
+ *
+ * \param update Zone update to be updated.
+ * \param rrtype Type of the apex RR.
+ * \param zone_keys Zone keyset.
+ * \param dnssec_ctx DNSSEC context.
+ *
+ * \return KNOT_E*
+ */
+int knot_zone_sign_apex_rr(zone_update_t *update, uint16_t rrtype,
+ const zone_keyset_t *zone_keys,
+ const kdnssec_ctx_t *dnssec_ctx);
diff --git a/src/knot/events/events.c b/src/knot/events/events.c
new file mode 100644
index 0000000..4dba950
--- /dev/null
+++ b/src/knot/events/events.c
@@ -0,0 +1,564 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdarg.h>
+#include <time.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include "libknot/libknot.h"
+#include "knot/common/log.h"
+#include "knot/events/events.h"
+#include "knot/events/handlers.h"
+#include "knot/events/replan.h"
+#include "knot/zone/zone.h"
+
+#define ZONE_EVENT_IMMEDIATE 1 /* Fast-track to worker queue. */
+
+typedef int (*zone_event_cb)(conf_t *conf, zone_t *zone);
+
+typedef struct event_info {
+ zone_event_type_t type;
+ const zone_event_cb callback;
+ const char *name;
+} event_info_t;
+
+static const event_info_t EVENT_INFO[] = {
+ { ZONE_EVENT_LOAD, event_load, "load" },
+ { ZONE_EVENT_REFRESH, event_refresh, "refresh" },
+ { ZONE_EVENT_UPDATE, event_update, "update" },
+ { ZONE_EVENT_EXPIRE, event_expire, "expiration" },
+ { ZONE_EVENT_FLUSH, event_flush, "flush" },
+ { ZONE_EVENT_BACKUP, event_backup, "backup/restore" },
+ { ZONE_EVENT_NOTIFY, event_notify, "notify" },
+ { ZONE_EVENT_DNSSEC, event_dnssec, "re-sign" },
+ { ZONE_EVENT_UFREEZE, event_ufreeze, "update-freeze" },
+ { ZONE_EVENT_UTHAW, event_uthaw, "update-thaw" },
+ { ZONE_EVENT_DS_CHECK, event_ds_check, "DS-check" },
+ { ZONE_EVENT_DS_PUSH, event_ds_push, "DS-push" },
+ { 0 }
+};
+
+static const event_info_t *get_event_info(zone_event_type_t type)
+{
+ const event_info_t *info;
+ for (info = EVENT_INFO; info->callback != NULL; info++) {
+ if (info->type == type) {
+ return info;
+ }
+ }
+
+ assert(0);
+ return NULL;
+}
+
+static bool valid_event(zone_event_type_t type)
+{
+ return (type > ZONE_EVENT_INVALID && type < ZONE_EVENT_COUNT);
+}
+
+bool ufreeze_applies(zone_event_type_t type)
+{
+ switch (type) {
+ case ZONE_EVENT_LOAD:
+ case ZONE_EVENT_REFRESH:
+ case ZONE_EVENT_UPDATE:
+ case ZONE_EVENT_FLUSH:
+ case ZONE_EVENT_DNSSEC:
+ case ZONE_EVENT_DS_CHECK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*! \brief Return remaining time to planned event (seconds). */
+static time_t time_until(time_t planned)
+{
+ time_t now = time(NULL);
+ return now < planned ? (planned - now) : 0;
+}
+
+/*!
+ * \brief Set time of a given event type.
+ */
+static void event_set_time(zone_events_t *events, zone_event_type_t type, time_t time)
+{
+ assert(events);
+ assert(valid_event(type));
+
+ events->time[type] = time;
+}
+
+/*!
+ * \brief Get time of a given event type.
+ */
+static time_t event_get_time(zone_events_t *events, zone_event_type_t type)
+{
+ assert(events);
+ assert(valid_event(type));
+
+ return events->time[type];
+}
+
+/*!
+ * \brief Find next scheduled zone event.
+ *
+ * \note Afer the UTHAW event, get_next_event() is also invoked. In that situation,
+ * all the events are suddenly allowed, and those which were planned into
+ * the ufrozen interval, start to be performed one-by-one sorted by their times.
+ *
+ * \param events Zone events.
+ *
+ * \return Zone event type, or ZONE_EVENT_INVALID if no event is scheduled.
+ */
+static zone_event_type_t get_next_event(zone_events_t *events)
+{
+ if (!events) {
+ return ZONE_EVENT_INVALID;
+ }
+
+ zone_event_type_t next_type = ZONE_EVENT_INVALID;
+ time_t next = 0;
+
+ for (int i = 0; i < ZONE_EVENT_COUNT; i++) {
+ time_t current = events->time[i];
+
+ if ((next == 0 || current < next) && (current != 0) &&
+ (events->forced[i] || !events->ufrozen || !ufreeze_applies(i))) {
+ next = current;
+ next_type = i;
+ }
+ }
+
+ return next_type;
+}
+
+/*!
+ * \brief Fined time of next scheduled event.
+ */
+static time_t get_next_time(zone_events_t *events)
+{
+ zone_event_type_t type = get_next_event(events);
+ return valid_event(type) ? event_get_time(events, type) : 0;
+}
+
+/*!
+ * \brief Cancel scheduled item, schedule first enqueued item.
+ *
+ * \param mx_handover events->mx already locked. Take it over and unlock when done.
+ */
+static void reschedule(zone_events_t *events, bool mx_handover)
+{
+ assert(events);
+
+ if (!mx_handover) {
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+ }
+
+ if (!events->event || events->running || events->frozen) {
+ pthread_mutex_unlock(&events->mx);
+ pthread_mutex_unlock(&events->reschedule_lock);
+ return;
+ }
+
+ zone_event_type_t type = get_next_event(events);
+ if (!valid_event(type)) {
+ pthread_mutex_unlock(&events->mx);
+ pthread_mutex_unlock(&events->reschedule_lock);
+ return;
+ }
+
+ time_t diff = time_until(event_get_time(events, type));
+
+ pthread_mutex_unlock(&events->mx);
+
+ evsched_schedule(events->event, diff * 1000);
+
+ pthread_mutex_unlock(&events->reschedule_lock);
+}
+
+/*!
+ * \brief Zone event wrapper, expected to be called from a worker thread.
+ *
+ * 1. Takes the next planned event.
+ * 2. Resets the event's scheduled time (and forced flag).
+ * 3. Perform the event's callback.
+ * 4. Schedule next event planned event.
+ */
+static void event_wrap(worker_task_t *task)
+{
+ assert(task);
+ assert(task->ctx);
+
+ zone_t *zone = task->ctx;
+ zone_events_t *events = &zone->events;
+
+ pthread_mutex_lock(&events->mx);
+ zone_event_type_t type = get_next_event(events);
+ pthread_cond_t *blocking = events->blocking[type];
+ if (!valid_event(type)) {
+ events->running = false;
+ pthread_mutex_unlock(&events->mx);
+ return;
+ }
+ events->type = type;
+ event_set_time(events, type, 0);
+ events->forced[type] = false;
+ pthread_mutex_unlock(&events->mx);
+
+ const event_info_t *info = get_event_info(type);
+
+ /* Create a configuration copy just for this event. */
+ conf_t *conf;
+ rcu_read_lock();
+ int ret = conf_clone(&conf);
+ rcu_read_unlock();
+ if (ret == KNOT_EOK) {
+ /* Execute the event callback. */
+ ret = info->callback(conf, zone);
+ conf_free(conf);
+ }
+
+ if (ret != KNOT_EOK) {
+ log_zone_error(zone->name, "zone event '%s' failed (%s)",
+ info->name, knot_strerror(ret));
+ }
+
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+ events->running = false;
+ events->type = ZONE_EVENT_INVALID;
+
+ if (blocking != NULL) {
+ events->blocking[type] = NULL;
+ events->result[type] = ret;
+ pthread_cond_broadcast(blocking);
+ }
+
+ if (events->run_end != NULL) {
+ pthread_cond_broadcast(events->run_end);
+ }
+
+ reschedule(events, true); // unlocks events->mx
+}
+
+/*!
+ * \brief Called by scheduler thread if the event occurs.
+ */
+static void event_dispatch(event_t *event)
+{
+ assert(event);
+ assert(event->data);
+
+ zone_events_t *events = event->data;
+
+ pthread_mutex_lock(&events->mx);
+ if (!events->running && !events->frozen) {
+ events->running = true;
+ worker_pool_assign(events->pool, &events->task);
+ }
+ pthread_mutex_unlock(&events->mx);
+}
+
+int zone_events_init(zone_t *zone)
+{
+ if (!zone) {
+ return KNOT_EINVAL;
+ }
+
+ zone_events_t *events = &zone->events;
+
+ memset(&zone->events, 0, sizeof(zone->events));
+ pthread_mutex_init(&events->mx, NULL);
+ pthread_mutex_init(&events->reschedule_lock, NULL);
+ events->task.ctx = zone;
+ events->task.run = event_wrap;
+
+ return KNOT_EOK;
+}
+
+int zone_events_setup(struct zone *zone, worker_pool_t *workers,
+ evsched_t *scheduler)
+{
+ if (!zone || !workers || !scheduler) {
+ return KNOT_EINVAL;
+ }
+
+ event_t *event;
+ event = evsched_event_create(scheduler, event_dispatch, &zone->events);
+ if (!event) {
+ return KNOT_ENOMEM;
+ }
+
+ zone->events.event = event;
+ zone->events.pool = workers;
+
+ return KNOT_EOK;
+}
+
+void zone_events_deinit(zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ zone_events_t *events = &zone->events;
+
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+
+ evsched_cancel(events->event);
+ evsched_event_free(events->event);
+
+ pthread_mutex_unlock(&events->mx);
+ pthread_mutex_destroy(&events->mx);
+ pthread_mutex_unlock(&events->reschedule_lock);
+ pthread_mutex_destroy(&events->reschedule_lock);
+
+ memset(events, 0, sizeof(*events));
+}
+
+void _zone_events_schedule_at(zone_t *zone, ...)
+{
+ zone_events_t *events = &zone->events;
+ va_list args;
+ va_start(args, zone);
+
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+
+ time_t old_next = get_next_time(events);
+
+ // update timers
+ for (int type = va_arg(args, int); valid_event(type); type = va_arg(args, int)) {
+ time_t planned = va_arg(args, time_t);
+ if (planned < 0) {
+ continue;
+ }
+
+ time_t current = event_get_time(events, type);
+ if (current == 0 || (planned == 0 && !events->forced[type]) ||
+ (planned > 0 && planned < current)) {
+ event_set_time(events, type, planned);
+ }
+ }
+
+ // reschedule if changed
+ time_t next = get_next_time(events);
+ if (old_next != next) {
+ reschedule(events, true); // unlocks events->mx
+ } else {
+ pthread_mutex_unlock(&events->mx);
+ pthread_mutex_unlock(&events->reschedule_lock);
+ }
+
+ va_end(args);
+}
+
+void zone_events_schedule_user(zone_t *zone, zone_event_type_t type)
+{
+ if (!zone || !valid_event(type)) {
+ return;
+ }
+
+ zone_events_t *events = &zone->events;
+ pthread_mutex_lock(&events->mx);
+ events->forced[type] = true;
+ pthread_mutex_unlock(&events->mx);
+
+ zone_events_schedule_now(zone, type);
+
+ // reschedule because get_next_event result changed outside of _zone_events_schedule_at
+ reschedule(events, false);
+}
+
+int zone_events_schedule_blocking(zone_t *zone, zone_event_type_t type, bool user)
+{
+ if (!zone || !valid_event(type)) {
+ return KNOT_EINVAL;
+ }
+
+ zone_events_t *events = &zone->events;
+ pthread_cond_t local_cond;
+ pthread_cond_init(&local_cond, NULL);
+
+ pthread_mutex_lock(&events->mx);
+ while (events->blocking[type] != NULL) {
+ pthread_cond_wait(events->blocking[type], &events->mx);
+ }
+ events->blocking[type] = &local_cond;
+ pthread_mutex_unlock(&events->mx);
+
+ if (user) {
+ zone_events_schedule_user(zone, type);
+ } else {
+ zone_events_schedule_now(zone, type);
+ }
+
+ pthread_mutex_lock(&events->mx);
+ while (events->blocking[type] == &local_cond) {
+ pthread_cond_wait(&local_cond, &events->mx);
+ }
+ int ret = events->result[type];
+ pthread_mutex_unlock(&events->mx);
+ pthread_cond_destroy(&local_cond);
+
+ return ret;
+}
+
+void zone_events_enqueue(zone_t *zone, zone_event_type_t type)
+{
+ if (!zone || !valid_event(type)) {
+ return;
+ }
+
+ zone_events_t *events = &zone->events;
+
+ pthread_mutex_lock(&events->mx);
+
+ /* Bypass scheduler if no event is running. */
+ if (!events->running && !events->frozen &&
+ (!events->ufrozen || !ufreeze_applies(type))) {
+ events->running = true;
+ events->type = type;
+ event_set_time(events, type, ZONE_EVENT_IMMEDIATE);
+ worker_pool_assign(events->pool, &events->task);
+ pthread_mutex_unlock(&events->mx);
+ return;
+ }
+
+ pthread_mutex_unlock(&events->mx);
+
+ /* Execute as soon as possible. */
+ zone_events_schedule_now(zone, type);
+}
+
+void zone_events_freeze(zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ zone_events_t *events = &zone->events;
+
+ /* Prevent new events being enqueued. */
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+ events->frozen = true;
+ pthread_mutex_unlock(&events->mx);
+
+ /* Cancel current event. */
+ evsched_cancel(events->event);
+ pthread_mutex_unlock(&events->reschedule_lock);
+}
+
+void zone_events_freeze_blocking(zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ zone_events_freeze(zone);
+
+ zone_events_t *events = &zone->events;
+
+ /* Wait for running event to finish. */
+ pthread_cond_t cond;
+ pthread_cond_init(&cond, NULL);
+ pthread_mutex_lock(&events->mx);
+ while (events->running) {
+ events->run_end = &cond;
+ pthread_cond_wait(&cond, &events->mx);
+ }
+ events->run_end = NULL;
+ pthread_mutex_unlock(&events->mx);
+ pthread_cond_destroy(&cond);
+}
+
+void zone_events_start(zone_t *zone)
+{
+ if (!zone) {
+ return;
+ }
+
+ zone_events_t *events = &zone->events;
+
+ /* Unlock the events queue. */
+ pthread_mutex_lock(&events->reschedule_lock);
+ pthread_mutex_lock(&events->mx);
+ events->frozen = false;
+
+ reschedule(events, true); //unlocks events->mx
+}
+
+time_t zone_events_get_time(const struct zone *zone, zone_event_type_t type)
+{
+ if (zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ time_t event_time = KNOT_ENOENT;
+ zone_events_t *events = (zone_events_t *)&zone->events;
+
+ pthread_mutex_lock(&events->mx);
+
+ /* Get next valid event. */
+ if (valid_event(type)) {
+ event_time = event_get_time(events, type);
+ }
+
+ pthread_mutex_unlock(&events->mx);
+
+ return event_time;
+}
+
+const char *zone_events_get_name(zone_event_type_t type)
+{
+ /* Get information about the event and time. */
+ const event_info_t *info = get_event_info(type);
+ if (info == NULL) {
+ return NULL;
+ }
+
+ return info->name;
+}
+
+time_t zone_events_get_next(const struct zone *zone, zone_event_type_t *type)
+{
+ if (zone == NULL || type == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ time_t next_time = KNOT_ENOENT;
+ zone_events_t *events = (zone_events_t *)&zone->events;
+
+ pthread_mutex_lock(&events->mx);
+
+ /* Get time of next valid event. */
+ *type = get_next_event(events);
+ if (valid_event(*type)) {
+ next_time = event_get_time(events, *type);
+ } else {
+ *type = ZONE_EVENT_INVALID;
+ }
+
+ pthread_mutex_unlock(&events->mx);
+
+ return next_time;
+}
diff --git a/src/knot/events/events.h b/src/knot/events/events.h
new file mode 100644
index 0000000..8ede5fb
--- /dev/null
+++ b/src/knot/events/events.h
@@ -0,0 +1,214 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include "knot/conf/conf.h"
+#include "knot/common/evsched.h"
+#include "knot/worker/pool.h"
+#include "libknot/db/db.h"
+
+struct zone;
+
+typedef enum zone_event_type {
+ ZONE_EVENT_INVALID = -1,
+ // supported event types
+ ZONE_EVENT_LOAD = 0,
+ ZONE_EVENT_REFRESH,
+ ZONE_EVENT_UPDATE,
+ ZONE_EVENT_EXPIRE,
+ ZONE_EVENT_FLUSH,
+ ZONE_EVENT_BACKUP,
+ ZONE_EVENT_NOTIFY,
+ ZONE_EVENT_DNSSEC,
+ ZONE_EVENT_UFREEZE,
+ ZONE_EVENT_UTHAW,
+ ZONE_EVENT_DS_CHECK,
+ ZONE_EVENT_DS_PUSH,
+ // terminator
+ ZONE_EVENT_COUNT,
+} zone_event_type_t;
+
+typedef struct zone_events {
+ pthread_mutex_t mx; //!< Mutex protecting the struct.
+ pthread_mutex_t reschedule_lock;//!< Prevent concurrent reschedule() making mess.
+
+ zone_event_type_t type; //!< Type of running event.
+ bool running; //!< Some zone event is being run.
+ pthread_cond_t *run_end; //!< Notify this one after finishing a job.
+
+ bool frozen; //!< Terminated, don't schedule new events.
+ bool ufrozen; //!< Updates to the zone temporarily frozen by user.
+
+ event_t *event; //!< Scheduler event.
+ worker_pool_t *pool; //!< Server worker pool.
+
+ worker_task_t task; //!< Event execution context.
+ time_t time[ZONE_EVENT_COUNT]; //!< Event execution times.
+ bool forced[ZONE_EVENT_COUNT]; //!< Flag that the event was invoked by user ctl.
+ pthread_cond_t *blocking[ZONE_EVENT_COUNT]; //!< For blocking events: dispatching cond.
+ int result[ZONE_EVENT_COUNT]; //!< Event return values (in blocking operations).
+} zone_events_t;
+
+/*!
+ * \brief Initialize zone events.
+ *
+ * The function will not set up the scheduling, use \ref zone_events_setup
+ * to do that.
+ *
+ * \param zone Pointer to zone (context of execution).
+ *
+ * \return KNOT_E*
+ */
+int zone_events_init(struct zone *zone);
+
+/*!
+ * \brief Set up zone events execution.
+ *
+ * \param zone Zone to setup.
+ * \param workers Worker thread pool.
+ * \param scheduler Event scheduler.
+ *
+ * \return KNOT_E*
+ */
+int zone_events_setup(struct zone *zone, worker_pool_t *workers,
+ evsched_t *scheduler);
+
+/*!
+ * \brief Deinitialize zone events.
+ *
+ * \param zone Zone whose events we want to deinitialize.
+ */
+void zone_events_deinit(struct zone *zone);
+
+/*!
+ * \brief Enqueue event type for asynchronous execution.
+ *
+ * \note This is similar to the scheduling an event for NOW, but it can
+ * bypass the event scheduler if no event is running at the moment.
+ *
+ * \param zone Zone to schedule new event for.
+ * \param type Type of event.
+ */
+void zone_events_enqueue(struct zone *zone, zone_event_type_t type);
+
+/*!
+ * \brief Schedule new zone event.
+ *
+ * The function allows to set multiple events at once.
+ *
+ * The function interprets time values (t) as follows:
+ *
+ * t > 0: schedule timer for a given time
+ * t = 0: cancel the timer
+ * t < 0: ignore change in the timer
+ *
+ * If the event is already scheduled, the new time will be set only if the
+ * new time is earlier than the currently scheduled one. To override the
+ * check, cancel and schedule the event in a single function call.
+ *
+ * \param zone Zone to schedule new event for.
+ * \param ... Sequence of zone_event_type_t and time_t terminated with
+ * ZONE_EVENT_INVALID.
+ */
+void _zone_events_schedule_at(struct zone *zone, ...);
+
+#define zone_events_schedule_at(zone, events...) \
+ _zone_events_schedule_at(zone, events, ZONE_EVENT_INVALID)
+
+#define zone_events_schedule_now(zone, type) \
+ zone_events_schedule_at(zone, type, time(NULL))
+
+/*!
+ * \brief Schedule zone event to now, with forced flag.
+ */
+void zone_events_schedule_user(struct zone *zone, zone_event_type_t type);
+
+/*!
+ * \brief Schedule new zone event as soon as possible and wait for it's
+ * completion (end of task run), with optional forced flag.
+ *
+ * \param zone Zone to schedule new event for.
+ * \param type Zone event type.
+ * \param user Forced flag indication.
+ *
+ * \return KNOT_E*
+ */
+int zone_events_schedule_blocking(struct zone *zone, zone_event_type_t type, bool user);
+
+/*!
+ * \brief Freeze all zone events and prevent new events from running.
+ *
+ * \param zone Zone to freeze events for.
+ */
+void zone_events_freeze(struct zone *zone);
+
+/*!
+ * \brief Freeze zone events and wait for running event to finish.
+ *
+ * \param zone Zone to freeze events for.
+ */
+void zone_events_freeze_blocking(struct zone *zone);
+
+/*!
+ * \brief ufreeze_applies
+ * \param type Type of event to be checked
+ * \return true / false if user freeze applies
+ */
+bool ufreeze_applies(zone_event_type_t type);
+
+/*!
+ * \brief Start the events processing.
+ *
+ * \param zone Zone to start processing for.
+ */
+void zone_events_start(struct zone *zone);
+
+/*!
+ * \brief Return time of the occurrence of the given event.
+ *
+ * \param zone Zone to get event time from.
+ * \param type Event type.
+ *
+ * \retval time of the event when event found
+ * \retval 0 when the event is not planned
+ * \retval negative value if event is invalid
+ */
+time_t zone_events_get_time(const struct zone *zone, zone_event_type_t type);
+
+/*!
+ * \brief Return text name of the event.
+ *
+ * \param type Type of event.
+ *
+ * \retval String with event name if it exists.
+ * \retval NULL if the event does not exist.
+ */
+const char *zone_events_get_name(zone_event_type_t type);
+
+/*!
+ * \brief Return time and type of the next event.
+ *
+ * \param zone Zone to get next event from.
+ * \param type [out] Type of the next event will be stored in the parameter.
+ *
+ * \return time of the next event or an error (negative number)
+ */
+time_t zone_events_get_next(const struct zone *zone, zone_event_type_t *type);
diff --git a/src/knot/events/handlers.h b/src/knot/events/handlers.h
new file mode 100644
index 0000000..e6dfd6c
--- /dev/null
+++ b/src/knot/events/handlers.h
@@ -0,0 +1,49 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/zone/zone.h"
+#include "knot/dnssec/zone-events.h" // zone_sign_reschedule_t
+
+/*! \brief Loads or reloads potentially changed zone. */
+int event_load(conf_t *conf, zone_t *zone);
+/*! \brief Refresh a zone from a master. */
+int event_refresh(conf_t *conf, zone_t *zone);
+/*! \brief Processes DDNS updates in the zone's DDNS queue. */
+int event_update(conf_t *conf, zone_t *zone);
+/*! \brief Empties in-memory zone contents. */
+int event_expire(conf_t *conf, zone_t *zone);
+/*! \brief Flushes zone contents into text file. */
+int event_flush(conf_t *conf, zone_t *zone);
+/*! \brief Backs up zone contents, metadata, keys, etc to a directory. */
+int event_backup(conf_t *conf, zone_t *zone);
+/*! \brief Sends notify to slaves. */
+int event_notify(conf_t *conf, zone_t *zone);
+/*! \brief Signs the zone using its DNSSEC keys, perform key rollovers. */
+int event_dnssec(conf_t *conf, zone_t *zone);
+/*! \brief NOT A HANDLER, just a helper function to reschedule based on reschedule_t */
+void event_dnssec_reschedule(conf_t *conf, zone_t *zone,
+ const zone_sign_reschedule_t *refresh, bool zone_changed);
+/*! \brief Freeze those events causing zone contents change. */
+int event_ufreeze(conf_t *conf, zone_t *zone);
+/*! \brief Unfreeze zone updates. */
+int event_uthaw(conf_t *conf, zone_t *zone);
+/*! \brief When CDS/CDNSKEY published, look for matching DS */
+int event_ds_check(conf_t *conf, zone_t *zone);
+/*! \brief After change of CDS/CDNSKEY, push the new DS to parent zone as DDNS. */
+int event_ds_push(conf_t *conf, zone_t *zone);
diff --git a/src/knot/events/handlers/backup.c b/src/knot/events/handlers/backup.c
new file mode 100644
index 0000000..a6b258c
--- /dev/null
+++ b/src/knot/events/handlers/backup.c
@@ -0,0 +1,71 @@
+/* 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 <assert.h>
+#include <urcu.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/events/handlers.h"
+#include "knot/zone/backup.h"
+
+int event_backup(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ zone_backup_ctx_t *ctx = zone->backup_ctx;
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ bool restore = ctx->restore_mode;
+
+ if (!restore && ctx->failed) {
+ // No need to proceed with already faulty backup.
+ return KNOT_EOK;
+ }
+
+ char *back_dir = strdup(ctx->backup_dir);
+ if (back_dir == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (restore) {
+ // expire zone
+ zone_contents_t *expired = zone_switch_contents(zone, NULL);
+ synchronize_rcu();
+ knot_sem_wait(&zone->cow_lock);
+ zone_contents_deep_free(expired);
+ knot_sem_post(&zone->cow_lock);
+ zone->zonefile.exists = false;
+ }
+
+ int ret = zone_backup(conf, zone);
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "zone %s '%s'",
+ restore ? "restored from" : "backed up to", back_dir);
+ } else {
+ log_zone_warning(zone->name, "zone %s failed (%s)",
+ restore ? "restore" : "backup", knot_strerror(ret));
+ }
+
+ if (restore && ret == KNOT_EOK) {
+ zone_reset(conf, zone);
+ }
+
+ free(back_dir);
+ return ret;
+}
diff --git a/src/knot/events/handlers/dnssec.c b/src/knot/events/handlers/dnssec.c
new file mode 100644
index 0000000..8263b0d
--- /dev/null
+++ b/src/knot/events/handlers/dnssec.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/updates/apply.h"
+#include "knot/zone/zone.h"
+#include "libknot/errcode.h"
+
+static void log_dnssec_next(const knot_dname_t *zone, knot_time_t refresh_at)
+{
+ char time_str[64] = { 0 };
+ struct tm time_gm = { 0 };
+ time_t refresh = refresh_at;
+ localtime_r(&refresh, &time_gm);
+ strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm);
+ if (refresh_at == 0) {
+ log_zone_warning(zone, "DNSSEC, next signing not scheduled");
+ } else {
+ log_zone_info(zone, "DNSSEC, next signing at %s", time_str);
+ }
+}
+
+void event_dnssec_reschedule(conf_t *conf, zone_t *zone,
+ const zone_sign_reschedule_t *refresh, bool zone_changed)
+{
+ time_t now = time(NULL);
+ time_t ignore = -1;
+ knot_time_t refresh_at = refresh->next_sign;
+
+ refresh_at = knot_time_min(refresh_at, refresh->next_rollover);
+ refresh_at = knot_time_min(refresh_at, refresh->next_nsec3resalt);
+
+ log_dnssec_next(zone->name, (time_t)refresh_at);
+
+ if (refresh->plan_ds_check) {
+ zone->timers.next_ds_check = now;
+ }
+
+ zone_events_schedule_at(zone,
+ ZONE_EVENT_DNSSEC, refresh_at ? (time_t)refresh_at : ignore,
+ ZONE_EVENT_DS_CHECK, refresh->plan_ds_check ? now : ignore
+ );
+ if (zone_changed) {
+ zone_schedule_notify(zone, 0);
+ }
+}
+
+int event_dnssec(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ zone_sign_reschedule_t resch = { 0 };
+ zone_sign_roll_flags_t r_flags = KEY_ROLL_ALLOW_ALL;
+ int sign_flags = 0;
+ bool zone_changed = false;
+
+ if (zone_get_flag(zone, ZONE_FORCE_RESIGN, true)) {
+ log_zone_info(zone->name, "DNSSEC, dropping previous "
+ "signatures, re-signing zone");
+ sign_flags = ZONE_SIGN_DROP_SIGNATURES;
+ } else {
+ log_zone_info(zone->name, "DNSSEC, signing zone");
+ sign_flags = 0;
+ }
+
+ if (zone_get_flag(zone, ZONE_FORCE_KSK_ROLL, true)) {
+ r_flags |= KEY_ROLL_FORCE_KSK_ROLL;
+ }
+ if (zone_get_flag(zone, ZONE_FORCE_ZSK_ROLL, true)) {
+ r_flags |= KEY_ROLL_FORCE_ZSK_ROLL;
+ }
+
+ zone_update_t up;
+ int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_dnssec_zone_sign(&up, conf, sign_flags, r_flags, 0, &resch);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
+ zone_changed = !zone_update_no_change(&up);
+
+ ret = zone_update_commit(conf, &up);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
+done:
+ // Schedule dependent events
+ event_dnssec_reschedule(conf, zone, &resch, zone_changed);
+
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ }
+ return ret;
+}
diff --git a/src/knot/events/handlers/ds_check.c b/src/knot/events/handlers/ds_check.c
new file mode 100644
index 0000000..0138bed
--- /dev/null
+++ b/src/knot/events/handlers/ds_check.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2022 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 "knot/dnssec/ds_query.h"
+#include "knot/zone/zone.h"
+
+int event_ds_check(conf_t *conf, zone_t *zone)
+{
+ kdnssec_ctx_t ctx = { 0 };
+
+ int ret = kdnssec_ctx_init(conf, &ctx, zone->name, zone_kaspdb(zone), NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_parent_ds_query(conf, &ctx, conf->cache.srv_tcp_remote_io_timeout);
+
+ zone->timers.next_ds_check = 0;
+ switch (ret) {
+ case KNOT_NO_READY_KEY:
+ break;
+ case KNOT_EOK:
+ zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
+ break;
+ default:
+ if (ctx.policy->ksk_sbm_check_interval > 0) {
+ time_t next_check = time(NULL) + ctx.policy->ksk_sbm_check_interval;
+ zone->timers.next_ds_check = next_check;
+ zone_events_schedule_at(zone, ZONE_EVENT_DS_CHECK, next_check);
+ }
+ }
+
+ kdnssec_ctx_deinit(&ctx);
+
+ return KNOT_EOK; // allways ok, if failure it has been rescheduled
+}
diff --git a/src/knot/events/handlers/ds_push.c b/src/knot/events/handlers/ds_push.c
new file mode 100644
index 0000000..11aef75
--- /dev/null
+++ b/src/knot/events/handlers/ds_push.c
@@ -0,0 +1,277 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/query/query.h"
+#include "knot/query/requestor.h"
+#include "knot/zone/zone.h"
+#include "libknot/errcode.h"
+
+struct ds_push_data {
+ const knot_dname_t *zone;
+ const knot_dname_t *parent_query;
+ knot_dname_t *parent_soa;
+ knot_rrset_t del_old_ds;
+ knot_rrset_t new_ds;
+ const struct sockaddr *remote;
+ query_edns_data_t edns;
+};
+
+#define DS_PUSH_RETRY 600
+
+#define DS_PUSH_LOG(priority, zone, remote, reused, fmt, ...) \
+ ns_log(priority, zone, LOG_OPERATION_DS_PUSH, LOG_DIRECTION_OUT, remote, \
+ reused, fmt, ## __VA_ARGS__)
+
+static const knot_rdata_t remove_cds = { 5, { 0, 0, 0, 0, 0 } };
+
+static int ds_push_begin(knot_layer_t *layer, void *params)
+{
+ layer->data = params;
+
+ return KNOT_STATE_PRODUCE;
+}
+
+static int parent_soa_produce(struct ds_push_data *data, knot_pkt_t *pkt)
+{
+ assert(data->parent_query[0] != '\0');
+ data->parent_query = knot_wire_next_label(data->parent_query, NULL);
+
+ int ret = knot_pkt_put_question(pkt, data->parent_query, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ ret = query_put_edns(pkt, &data->edns);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int ds_push_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct ds_push_data *data = layer->data;
+
+ query_init_pkt(pkt);
+
+ if (data->parent_soa == NULL) {
+ return parent_soa_produce(data, pkt);
+ }
+
+ knot_wire_set_opcode(pkt->wire, KNOT_OPCODE_UPDATE);
+ int ret = knot_pkt_put_question(pkt, data->parent_soa, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ knot_pkt_begin(pkt, KNOT_AUTHORITY);
+
+ assert(data->del_old_ds.type == KNOT_RRTYPE_DS);
+ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &data->del_old_ds, 0);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ assert(data->new_ds.type == KNOT_RRTYPE_DS);
+ assert(!knot_rrset_empty(&data->new_ds));
+ if (knot_rdata_cmp(data->new_ds.rrs.rdata, &remove_cds) != 0) {
+ // Otherwise only remove DS - it was a special "remove CDS".
+ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, &data->new_ds, 0);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ query_put_edns(pkt, &data->edns);
+
+ return KNOT_STATE_CONSUME;
+}
+
+static const knot_rrset_t *sect_soa(const knot_pkt_t *pkt, knot_section_t sect)
+{
+ const knot_pktsection_t *s = knot_pkt_section(pkt, sect);
+ const knot_rrset_t *rr = s->count > 0 ? knot_pkt_rr(s, 0) : NULL;
+ if (rr == NULL || rr->type != KNOT_RRTYPE_SOA || rr->rrs.count != 1) {
+ return NULL;
+ }
+ return rr;
+}
+
+static int ds_push_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct ds_push_data *data = layer->data;
+
+ if (data->parent_soa != NULL) {
+ // DS push has already been sent, just finish the action.
+ return KNOT_STATE_DONE;
+ }
+
+ const knot_rrset_t *parent_soa = sect_soa(pkt, KNOT_ANSWER);
+ if (parent_soa != NULL) {
+ // parent SOA obtained, continue with DS push
+ data->parent_soa = knot_dname_copy(parent_soa->owner, NULL);
+ return KNOT_STATE_RESET;
+ }
+
+ if (data->parent_query[0] == '\0') {
+ // query for parent SOA systematically fails
+ DS_PUSH_LOG(LOG_WARNING, data->zone, data->remote,
+ layer->flags & KNOT_REQUESTOR_REUSED,
+ "unable to query parent SOA");
+ return KNOT_STATE_FAIL;
+ }
+
+ return KNOT_STATE_RESET; // cut off one more label and re-query
+}
+
+static int ds_push_reset(knot_layer_t *layer)
+{
+ (void)layer;
+ return KNOT_STATE_PRODUCE;
+}
+
+static int ds_push_finish(knot_layer_t *layer)
+{
+ struct ds_push_data *data = layer->data;
+ free(data->parent_soa);
+ data->parent_soa = NULL;
+ return layer->state;
+}
+
+static const knot_layer_api_t DS_PUSH_API = {
+ .begin = ds_push_begin,
+ .produce = ds_push_produce,
+ .reset = ds_push_reset,
+ .consume = ds_push_consume,
+ .finish = ds_push_finish,
+};
+
+static int send_ds_push(conf_t *conf, zone_t *zone,
+ const conf_remote_t *parent, int timeout)
+{
+ knot_rrset_t zone_cds = node_rrset(zone->contents->apex, KNOT_RRTYPE_CDS);
+ if (knot_rrset_empty(&zone_cds)) {
+ return KNOT_EOK; // No CDS, do nothing.
+ }
+ zone_cds.type = KNOT_RRTYPE_DS;
+ zone_cds.ttl = node_rrset(zone->contents->apex, KNOT_RRTYPE_DNSKEY).ttl;
+
+ struct ds_push_data data = {
+ .zone = zone->name,
+ .parent_query = zone->name,
+ .new_ds = zone_cds,
+ .remote = (struct sockaddr *)&parent->addr,
+ .edns = query_edns_data_init(conf, parent->addr.ss_family, 0)
+ };
+
+ knot_rrset_init(&data.del_old_ds, zone->name, KNOT_RRTYPE_DS, KNOT_CLASS_ANY, 0);
+ int ret = knot_rrset_add_rdata(&data.del_old_ds, NULL, 0, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_requestor_t requestor;
+ knot_requestor_init(&requestor, &DS_PUSH_API, &data, NULL);
+
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (pkt == NULL) {
+ knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ const struct sockaddr_storage *dst = &parent->addr;
+ const struct sockaddr_storage *src = &parent->via;
+ knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0);
+ if (req == NULL) {
+ knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ ret = knot_requestor_exec(&requestor, req, timeout);
+
+ if (ret == KNOT_EOK && knot_pkt_ext_rcode(req->resp) == 0) {
+ DS_PUSH_LOG(LOG_INFO, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "success");
+ } else if (knot_pkt_ext_rcode(req->resp) == 0) {
+ DS_PUSH_LOG(LOG_WARNING, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "failed (%s)", knot_strerror(ret));
+ } else {
+ DS_PUSH_LOG(LOG_WARNING, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "server responded with error '%s'",
+ knot_pkt_ext_rcode_name(req->resp));
+ }
+
+ knot_rdataset_clear(&data.del_old_ds.rrs, NULL);
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+
+ return ret;
+}
+
+int event_ds_push(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ if (zone_contents_is_empty(zone->contents)) {
+ return KNOT_EOK;
+ }
+
+ int timeout = conf->cache.srv_tcp_remote_io_timeout;
+
+ conf_val_t ds_push = conf_zone_get(conf, C_DS_PUSH, zone->name);
+ if (ds_push.code != KNOT_EOK) {
+ conf_val_t policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
+ conf_id_fix_default(&policy_id);
+ ds_push = conf_id_get(conf, C_POLICY, C_DS_PUSH, &policy_id);
+ }
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &ds_push, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ size_t addr_count = conf_val_count(&addr);
+
+ int ret = KNOT_EOK;
+ for (int i = 0; i < addr_count; i++) {
+ conf_remote_t parent = conf_remote(conf, iter.id, i);
+ ret = send_ds_push(conf, zone, &parent, timeout);
+ if (ret == KNOT_EOK) {
+ zone->timers.next_ds_push = 0;
+ break;
+ }
+ }
+
+ if (ret != KNOT_EOK) {
+ time_t next_push = time(NULL) + DS_PUSH_RETRY;
+ zone_events_schedule_at(zone, ZONE_EVENT_DS_PUSH, next_push);
+ zone->timers.next_ds_push = next_push;
+ }
+
+ conf_mix_iter_next(&iter);
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/events/handlers/expire.c b/src/knot/events/handlers/expire.c
new file mode 100644
index 0000000..d7deedd
--- /dev/null
+++ b/src/knot/events/handlers/expire.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <urcu.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/events/handlers.h"
+#include "knot/events/replan.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/zone.h"
+
+int event_expire(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ zone_contents_t *expired = zone_switch_contents(zone, NULL);
+ log_zone_info(zone->name, "zone expired");
+
+ synchronize_rcu();
+ knot_sem_wait(&zone->cow_lock);
+ zone_contents_deep_free(expired);
+ knot_sem_post(&zone->cow_lock);
+
+ zone->zonefile.exists = false;
+
+ zone->timers.next_expire = time(NULL);
+ zone->timers.next_refresh = zone->timers.next_expire;
+ replan_from_timers(conf, zone);
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/events/handlers/flush.c b/src/knot/events/handlers/flush.c
new file mode 100644
index 0000000..65663cb
--- /dev/null
+++ b/src/knot/events/handlers/flush.c
@@ -0,0 +1,33 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <time.h>
+
+#include "knot/conf/conf.h"
+#include "knot/zone/zone.h"
+
+int event_flush(conf_t *conf, zone_t *zone)
+{
+ assert(conf);
+ assert(zone);
+
+ if (zone_contents_is_empty(zone->contents)) {
+ return KNOT_EOK;
+ }
+
+ return zone_flush_journal(conf, zone, true);
+}
diff --git a/src/knot/events/handlers/freeze_thaw.c b/src/knot/events/handlers/freeze_thaw.c
new file mode 100644
index 0000000..dfa867f
--- /dev/null
+++ b/src/knot/events/handlers/freeze_thaw.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2021 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 "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/events/events.h"
+#include "knot/zone/zone.h"
+
+int event_ufreeze(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ pthread_mutex_lock(&zone->events.mx);
+ zone->events.ufrozen = true;
+ pthread_mutex_unlock(&zone->events.mx);
+
+ log_zone_info(zone->name, "zone updates frozen");
+
+ return KNOT_EOK;
+}
+
+int event_uthaw(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ pthread_mutex_lock(&zone->events.mx);
+ zone->events.ufrozen = false;
+ pthread_mutex_unlock(&zone->events.mx);
+
+ log_zone_info(zone->name, "zone updates unfrozen");
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c
new file mode 100644
index 0000000..13e3298
--- /dev/null
+++ b/src/knot/events/handlers/load.c
@@ -0,0 +1,406 @@
+/* Copyright (C) 2023 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 <assert.h>
+
+#include "knot/catalog/generate.h"
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/events/handlers.h"
+#include "knot/events/replan.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/serial.h"
+#include "knot/zone/zone-diff.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zonefile.h"
+#include "knot/updates/acl.h"
+
+static bool dontcare_load_error(conf_t *conf, const zone_t *zone)
+{
+ return (zone->contents == NULL && zone_load_can_bootstrap(conf, zone->name));
+}
+
+static bool allowed_xfr(conf_t *conf, const zone_t *zone)
+{
+ conf_val_t acl = conf_zone_get(conf, C_ACL, zone->name);
+ while (acl.code == KNOT_EOK) {
+ conf_val_t action = conf_id_get(conf, C_ACL, C_ACTION, &acl);
+ while (action.code == KNOT_EOK) {
+ if (conf_opt(&action) == ACL_ACTION_TRANSFER) {
+ return true;
+ }
+ conf_val_next(&action);
+ }
+ conf_val_next(&acl);
+ }
+
+ return false;
+}
+
+int event_load(conf_t *conf, zone_t *zone)
+{
+ zone_update_t up = { 0 };
+ zone_contents_t *journal_conts = NULL, *zf_conts = NULL;
+ bool old_contents_exist = (zone->contents != NULL), zone_in_journal_exists = false;
+
+ conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, zone->name);
+ unsigned load_from = conf_opt(&val);
+
+ val = conf_zone_get(conf, C_ZONEFILE_LOAD, zone->name);
+ unsigned zf_from = conf_opt(&val);
+
+ int ret = KNOT_EOK;
+
+ // If configured, load journal contents.
+ if (!old_contents_exist &&
+ ((load_from == JOURNAL_CONTENT_ALL && zf_from != ZONEFILE_LOAD_WHOLE) ||
+ zone->cat_members != NULL)) {
+ ret = zone_load_from_journal(conf, zone, &journal_conts);
+ switch (ret) {
+ case KNOT_EOK:
+ zone_in_journal_exists = true;
+ break;
+ case KNOT_ENOENT:
+ zone_in_journal_exists = false;
+ break;
+ default:
+ goto cleanup;
+ }
+ } else {
+ zone_in_journal_exists = zone_journal_has_zij(zone);
+ }
+
+ // If configured, attempt to load zonefile.
+ if (zf_from != ZONEFILE_LOAD_NONE && zone->cat_members == NULL) {
+ struct timespec mtime;
+ char *filename = conf_zonefile(conf, zone->name);
+ ret = zonefile_exists(filename, &mtime);
+ if (ret == KNOT_EOK) {
+ conf_val_t semchecks = conf_zone_get(conf, C_SEM_CHECKS, zone->name);
+ semcheck_optional_t mode = conf_opt(&semchecks);
+ if (mode == SEMCHECK_DNSSEC_AUTO) {
+ conf_val_t validation = conf_zone_get(conf, C_DNSSEC_VALIDATION, zone->name);
+ if (conf_bool(&validation)) {
+ /* Disable duplicate DNSSEC checks, which are the
+ same as DNSSEC validation in zone update commit. */
+ mode = SEMCHECK_DNSSEC_OFF;
+ }
+ }
+
+ ret = zone_load_contents(conf, zone->name, &zf_conts, mode, false);
+ }
+ if (ret != KNOT_EOK) {
+ assert(!zf_conts);
+ if (dontcare_load_error(conf, zone)) {
+ log_zone_info(zone->name, "failed to parse zone file '%s' (%s)",
+ filename, knot_strerror(ret));
+ } else {
+ log_zone_error(zone->name, "failed to parse zone file '%s' (%s)",
+ filename, knot_strerror(ret));
+ }
+ free(filename);
+ goto load_end;
+ }
+ free(filename);
+
+ // Save zonefile information.
+ zone->zonefile.serial = zone_contents_serial(zf_conts);
+ zone->zonefile.exists = (zf_conts != NULL);
+ zone->zonefile.mtime = mtime;
+
+ // If configured and possible, fix the SOA serial of zonefile.
+ zone_contents_t *relevant = (zone->contents != NULL ? zone->contents : journal_conts);
+ if (zf_conts != NULL && zf_from == ZONEFILE_LOAD_DIFSE && relevant != NULL) {
+ uint32_t serial = zone_contents_serial(relevant);
+ conf_val_t policy = conf_zone_get(conf, C_SERIAL_POLICY, zone->name);
+ uint32_t set = serial_next(serial, conf_opt(&policy), 1);
+ zone_contents_set_soa_serial(zf_conts, set);
+ log_zone_info(zone->name, "zone file parsed, serial updated %u -> %u",
+ zone->zonefile.serial, set);
+ zone->zonefile.serial = set;
+ } else {
+ log_zone_info(zone->name, "zone file parsed, serial %u",
+ zone->zonefile.serial);
+ }
+
+ // If configured and appliable to zonefile, load journal changes.
+ if (load_from != JOURNAL_CONTENT_NONE) {
+ ret = zone_load_journal(conf, zone, zf_conts);
+ if (ret != KNOT_EOK) {
+ zone_contents_deep_free(zf_conts);
+ zf_conts = NULL;
+ log_zone_warning(zone->name, "failed to load journal (%s)",
+ knot_strerror(ret));
+ }
+ }
+ }
+ if (zone->cat_members != NULL && !old_contents_exist) {
+ uint32_t serial = journal_conts == NULL ? 1 : zone_contents_serial(journal_conts);
+ serial = serial_next(serial, SERIAL_POLICY_UNIXTIME, 1); // unixtime hardcoded
+ zf_conts = catalog_update_to_zone(zone->cat_members, zone->name, serial);
+ if (zf_conts == NULL) {
+ ret = zone->cat_members->error == KNOT_EOK ? KNOT_ENOMEM : zone->cat_members->error;
+ goto cleanup;
+ }
+ }
+
+ // If configured contents=all, but not present, store zonefile.
+ if ((load_from == JOURNAL_CONTENT_ALL || zone->cat_members != NULL) &&
+ !zone_in_journal_exists && (zf_conts != NULL || old_contents_exist)) {
+ zone_contents_t *store_c = old_contents_exist ? zone->contents : zf_conts;
+ ret = zone_in_journal_store(conf, zone, store_c);
+ if (ret != KNOT_EOK) {
+ log_zone_warning(zone->name, "failed to write zone-in-journal (%s)",
+ knot_strerror(ret));
+ } else {
+ zone_in_journal_exists = true;
+ }
+ }
+
+ val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
+ bool dnssec_enable = (conf_bool(&val) && zone->cat_members == NULL), zu_from_zf_conts = false;
+ bool do_diff = (zf_from == ZONEFILE_LOAD_DIFF || zf_from == ZONEFILE_LOAD_DIFSE || zone->cat_members != NULL);
+ bool ignore_dnssec = (do_diff && dnssec_enable);
+
+ val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone->name);
+ unsigned digest_alg = conf_opt(&val);
+ bool update_zonemd = (digest_alg != ZONE_DIGEST_NONE);
+
+ // Create zone_update structure according to current state.
+ if (old_contents_exist) {
+ if (zone->cat_members != NULL) {
+ ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL);
+ if (ret == KNOT_EOK) {
+ ret = catalog_update_to_update(zone->cat_members, &up);
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_increment_soa(&up, conf);
+ }
+ } else if (zf_conts == NULL) {
+ // nothing to be re-loaded
+ ret = KNOT_EOK;
+ goto cleanup;
+ } else if (zf_from == ZONEFILE_LOAD_WHOLE) {
+ // throw old zone contents and load new from ZF
+ ret = zone_update_from_contents(&up, zone, zf_conts,
+ (load_from == JOURNAL_CONTENT_NONE ?
+ UPDATE_FULL : UPDATE_HYBRID));
+ zu_from_zf_conts = true;
+ } else {
+ // compute ZF diff and if success, apply it
+ ret = zone_update_from_differences(&up, zone, NULL, zf_conts, UPDATE_INCREMENTAL,
+ ignore_dnssec, update_zonemd);
+ }
+ } else {
+ if (journal_conts != NULL && (zf_from != ZONEFILE_LOAD_WHOLE || zone->cat_members != NULL)) {
+ if (zf_conts == NULL) {
+ // load zone-in-journal
+ ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_HYBRID);
+ } else {
+ // load zone-in-journal, compute ZF diff and if success, apply it
+ ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts,
+ UPDATE_HYBRID, ignore_dnssec, update_zonemd);
+ if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) {
+ log_zone_warning(zone->name,
+ "zone file changed with SOA serial %s, "
+ "ignoring zone file and loading from journal",
+ (ret == KNOT_ESEMCHECK ? "unupdated" : "decreased"));
+ zone_contents_deep_free(zf_conts);
+ zf_conts = NULL;
+ ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_HYBRID);
+ }
+ }
+ } else {
+ if (zf_conts == NULL) {
+ // nothing to be loaded
+ ret = KNOT_ENOENT;
+ } else {
+ // load from ZF
+ ret = zone_update_from_contents(&up, zone, zf_conts,
+ (load_from == JOURNAL_CONTENT_NONE ?
+ UPDATE_FULL : UPDATE_HYBRID));
+ if (zf_from == ZONEFILE_LOAD_WHOLE) {
+ zu_from_zf_conts = true;
+ }
+ }
+ }
+ }
+
+load_end:
+ if (ret != KNOT_EOK) {
+ switch (ret) {
+ case KNOT_ENOENT:
+ if (zone_load_can_bootstrap(conf, zone->name)) {
+ log_zone_info(zone->name, "zone will be bootstrapped");
+ } else {
+ log_zone_info(zone->name, "zone not found");
+ }
+ break;
+ case KNOT_ESEMCHECK:
+ log_zone_warning(zone->name, "zone file changed without SOA serial update");
+ break;
+ case KNOT_ERANGE:
+ if (serial_compare(zone->zonefile.serial, zone_contents_serial(zone->contents)) == SERIAL_INCOMPARABLE) {
+ log_zone_warning(zone->name, "zone file changed with incomparable SOA serial");
+ } else {
+ log_zone_warning(zone->name, "zone file changed with decreased SOA serial");
+ }
+ break;
+ }
+ goto cleanup;
+ }
+
+ bool zf_serial_updated = (zf_conts != NULL && zone_contents_serial(zf_conts) != zone_contents_serial(zone->contents));
+
+ // The contents are already part of zone_update.
+ zf_conts = NULL;
+ journal_conts = NULL;
+
+ ret = zone_update_verify_digest(conf, &up);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ uint32_t middle_serial = zone_contents_serial(up.new_cont);
+
+ if (do_diff && old_contents_exist && dnssec_enable && zf_serial_updated &&
+ !zone_in_journal_exists) {
+ ret = zone_update_start_extra(&up, conf);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+
+ // Sign zone using DNSSEC if configured.
+ zone_sign_reschedule_t dnssec_refresh = { 0 };
+ if (dnssec_enable) {
+ ret = knot_dnssec_zone_sign(&up, conf, 0, KEY_ROLL_ALLOW_ALL, 0, &dnssec_refresh);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ if (zu_from_zf_conts && (up.flags & UPDATE_HYBRID) && allowed_xfr(conf, zone)) {
+ log_zone_warning(zone->name,
+ "with automatic DNSSEC signing and outgoing transfers enabled, "
+ "'zonefile-load: difference' should be set to avoid malformed "
+ "IXFR after manual zone file update");
+ }
+ } else if (update_zonemd) {
+ /* Don't update ZONEMD if no change and ZONEMD is up-to-date.
+ * If ZONEFILE_LOAD_DIFSE, the change is non-empty and ZONEMD
+ * is directly updated without its verification. */
+ if (!zone_update_no_change(&up) || !zone_contents_digest_exists(up.new_cont, digest_alg, false)) {
+ if (zone_update_to(&up) == NULL || middle_serial == zone->zonefile.serial) {
+ ret = zone_update_increment_soa(&up, conf);
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add_digest(&up, digest_alg, false);
+ }
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+ }
+
+ // If the change is only automatically incremented SOA serial, make it no change.
+ if ((zf_from == ZONEFILE_LOAD_DIFSE || zone->cat_members != NULL) &&
+ (up.flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) &&
+ changeset_differs_just_serial(&up.change, update_zonemd)) {
+ changeset_t *cpy = changeset_clone(&up.change);
+ if (cpy == NULL) {
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+ ret = zone_update_apply_changeset_reverse(&up, cpy);
+ if (ret != KNOT_EOK) {
+ changeset_free(cpy);
+ goto cleanup;
+ }
+
+ // If the original ZONEMD is outdated, use the reverted changeset again.
+ if (update_zonemd && !zone_contents_digest_exists(up.new_cont, digest_alg, false)) {
+ ret = zone_update_apply_changeset(&up, cpy);
+ changeset_free(cpy);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ } else {
+ changeset_free(cpy);
+ // Revert automatic zone serial increment.
+ zone->zonefile.serial = zone_contents_serial(up.new_cont);
+ /* Reset possibly set the resigned flag. Note that dnssec
+ * reschedule isn't reverted, but shouldn't be a problem
+ * for non-empty zones as SOA, ZONEMD, and their RRSIGs
+ * are always updated with other changes in the zone. */
+ zone->zonefile.resigned = false;
+ }
+ }
+
+ uint32_t old_serial = 0, new_serial = zone_contents_serial(up.new_cont);
+ char old_serial_str[11] = "none", new_serial_str[15] = "";
+ if (old_contents_exist) {
+ old_serial = zone_contents_serial(zone->contents);
+ (void)snprintf(old_serial_str, sizeof(old_serial_str), "%u", old_serial);
+ }
+ if (new_serial != middle_serial) {
+ (void)snprintf(new_serial_str, sizeof(new_serial_str), " -> %u", new_serial);
+ }
+
+ // Commit zone_update back to zone (including journal update, rcu,...).
+ ret = zone_update_commit(conf, &up);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+
+ char expires_in[32] = "";
+ if (zone->timers.next_expire > 0) {
+ (void)snprintf(expires_in, sizeof(expires_in),
+ ", expires in %u seconds",
+ (uint32_t)MAX(zone->timers.next_expire - time(NULL), 0));
+ }
+
+ log_zone_info(zone->name, "loaded, serial %s -> %u%s, %zu bytes%s",
+ old_serial_str, middle_serial, new_serial_str, zone->contents->size, expires_in);
+
+ if (zone->cat_members != NULL) {
+ catalog_update_clear(zone->cat_members);
+ }
+
+ // Schedule dependent events.
+ if (dnssec_enable) {
+ event_dnssec_reschedule(conf, zone, &dnssec_refresh, false); // false since we handle NOTIFY below
+ }
+
+ replan_from_timers(conf, zone);
+
+ if (!zone_timers_serial_notified(&zone->timers, new_serial)) {
+ zone_schedule_notify(zone, 0);
+ }
+
+ return KNOT_EOK;
+
+cleanup:
+ // Try to bootstrap the zone if local error.
+ replan_from_timers(conf, zone);
+
+ zone_update_clear(&up);
+ zone_contents_deep_free(zf_conts);
+ zone_contents_deep_free(journal_conts);
+
+ return (dontcare_load_error(conf, zone) ? KNOT_EOK : ret);
+}
diff --git a/src/knot/events/handlers/notify.c b/src/knot/events/handlers/notify.c
new file mode 100644
index 0000000..dc3965d
--- /dev/null
+++ b/src/knot/events/handlers/notify.c
@@ -0,0 +1,212 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "contrib/openbsd/siphash.h"
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/query/query.h"
+#include "knot/query/requestor.h"
+#include "knot/zone/zone.h"
+#include "libknot/errcode.h"
+
+static notifailed_rmt_hash notifailed_hash(conf_val_t *rmt_id)
+{
+ SIPHASH_KEY zero_key = { 0, 0 };
+ SIPHASH_CTX ctx;
+ SipHash24_Init(&ctx, &zero_key);
+ SipHash24_Update(&ctx, rmt_id->data, rmt_id->len);
+ return SipHash24_End(&ctx);
+}
+
+/*!
+ * \brief NOTIFY message processing data.
+ */
+struct notify_data {
+ const knot_dname_t *zone;
+ const knot_rrset_t *soa;
+ const struct sockaddr *remote;
+ query_edns_data_t edns;
+};
+
+static int notify_begin(knot_layer_t *layer, void *params)
+{
+ layer->data = params;
+
+ return KNOT_STATE_PRODUCE;
+}
+
+static int notify_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct notify_data *data = layer->data;
+
+ // mandatory: NOTIFY opcode, AA flag, SOA qtype
+ query_init_pkt(pkt);
+ knot_wire_set_opcode(pkt->wire, KNOT_OPCODE_NOTIFY);
+ knot_wire_set_aa(pkt->wire);
+ knot_pkt_put_question(pkt, data->zone, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+
+ // unsecure hint: new SOA
+ if (data->soa) {
+ knot_pkt_begin(pkt, KNOT_ANSWER);
+ knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, data->soa, 0);
+ }
+
+ query_put_edns(pkt, &data->edns);
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int notify_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ return KNOT_STATE_DONE;
+}
+
+static const knot_layer_api_t NOTIFY_API = {
+ .begin = notify_begin,
+ .produce = notify_produce,
+ .consume = notify_consume,
+};
+
+#define NOTIFY_OUT_LOG(priority, zone, remote, reused, fmt, ...) \
+ ns_log(priority, zone, LOG_OPERATION_NOTIFY, LOG_DIRECTION_OUT, remote, \
+ (reused), fmt, ## __VA_ARGS__)
+
+static int send_notify(conf_t *conf, zone_t *zone, const knot_rrset_t *soa,
+ const conf_remote_t *slave, int timeout, bool retry)
+{
+ struct notify_data data = {
+ .zone = zone->name,
+ .soa = soa,
+ .remote = (struct sockaddr *)&slave->addr,
+ .edns = query_edns_data_init(conf, slave->addr.ss_family, 0)
+ };
+
+ knot_requestor_t requestor;
+ knot_requestor_init(&requestor, &NOTIFY_API, &data, NULL);
+
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (!pkt) {
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ const struct sockaddr_storage *dst = &slave->addr;
+ const struct sockaddr_storage *src = &slave->via;
+ knot_request_flag_t flags = conf->cache.srv_tcp_fastopen ? KNOT_REQUEST_TFO : 0;
+ knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &slave->key, flags);
+ if (!req) {
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_requestor_exec(&requestor, req, timeout);
+
+ const char *log_retry = retry ? "retry, " : "";
+
+ if (ret == KNOT_EOK && knot_pkt_ext_rcode(req->resp) == 0) {
+ NOTIFY_OUT_LOG(LOG_INFO, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "%sserial %u", log_retry, knot_soa_serial(soa->rrs.rdata));
+ zone->timers.last_notified_serial = (knot_soa_serial(soa->rrs.rdata) | LAST_NOTIFIED_SERIAL_VALID);
+ } else if (knot_pkt_ext_rcode(req->resp) == 0) {
+ NOTIFY_OUT_LOG(LOG_WARNING, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "%sfailed (%s)", log_retry, knot_strerror(ret));
+ } else {
+ NOTIFY_OUT_LOG(LOG_WARNING, zone->name, dst,
+ requestor.layer.flags & KNOT_REQUESTOR_REUSED,
+ "%sserver responded with error '%s'",
+ log_retry, knot_pkt_ext_rcode_name(req->resp));
+ }
+
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+
+ return ret;
+}
+
+int event_notify(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ bool failed = false;
+
+ if (zone_contents_is_empty(zone->contents)) {
+ return KNOT_EOK;
+ }
+
+ // NOTIFY content
+ int timeout = conf->cache.srv_tcp_remote_io_timeout;
+ knot_rrset_t soa = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
+
+ // in case of re-try, NOTIFY only failed remotes
+ pthread_mutex_lock(&zone->preferred_lock);
+ bool retry = (zone->notifailed.size > 0);
+
+ // send NOTIFY to each remote, use working address
+ conf_val_t notify = conf_zone_get(conf, C_NOTIFY, zone->name);
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &notify, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ notifailed_rmt_hash rmt_hash = notifailed_hash(iter.id);
+ if (retry && notifailed_rmt_dynarray_bsearch(&zone->notifailed, &rmt_hash) == NULL) {
+ conf_mix_iter_next(&iter);
+ continue;
+ }
+ pthread_mutex_unlock(&zone->preferred_lock);
+
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ size_t addr_count = conf_val_count(&addr);
+
+ int ret = KNOT_EOK;
+
+ for (int i = 0; i < addr_count; i++) {
+ conf_remote_t slave = conf_remote(conf, iter.id, i);
+ ret = send_notify(conf, zone, &soa, &slave, timeout, retry);
+ if (ret == KNOT_EOK) {
+ break;
+ }
+ }
+
+ pthread_mutex_lock(&zone->preferred_lock);
+ if (ret != KNOT_EOK) {
+ failed = true;
+ notifailed_rmt_dynarray_add(&zone->notifailed, &rmt_hash);
+ } else {
+ notifailed_rmt_dynarray_remove(&zone->notifailed, &rmt_hash);
+ }
+
+ conf_mix_iter_next(&iter);
+ }
+
+ if (failed) {
+ notifailed_rmt_dynarray_sort_dedup(&zone->notifailed);
+
+ uint32_t retry_in = knot_soa_retry(soa.rrs.rdata);
+ conf_val_t val = conf_zone_get(conf, C_RETRY_MIN_INTERVAL, zone->name);
+ retry_in = MAX(retry_in, conf_int(&val));
+ val = conf_zone_get(conf, C_RETRY_MAX_INTERVAL, zone->name);
+ retry_in = MIN(retry_in, conf_int(&val));
+
+ zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + retry_in);
+ }
+ pthread_mutex_unlock(&zone->preferred_lock);
+
+ return failed ? KNOT_ERROR : KNOT_EOK;
+}
diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c
new file mode 100644
index 0000000..9125aac
--- /dev/null
+++ b/src/knot/events/handlers/refresh.c
@@ -0,0 +1,1391 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdint.h>
+
+#include "contrib/mempattern.h"
+#include "libdnssec/random.h"
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/events/handlers.h"
+#include "knot/events/replan.h"
+#include "knot/nameserver/ixfr.h"
+#include "knot/query/layer.h"
+#include "knot/query/query.h"
+#include "knot/query/requestor.h"
+#include "knot/updates/changesets.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/serial.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zonefile.h"
+#include "libknot/errcode.h"
+
+/*!
+ * \brief Refresh event processing.
+ *
+ * The following diagram represents refresh event processing.
+ *
+ * \verbatim
+ * O
+ * |
+ * +-----v-----+
+ * | BEGIN |
+ * +---+---+---+
+ * has SOA | | no SOA
+ * +-------------------+ +------------------------------+
+ * | |
+ * +------v------+ outdated +--------------+ error +-------v------+
+ * | SOA query +------------> IXFR query +-----------> AXFR query |
+ * +-----+---+---+ +------+-------+ +----+----+----+
+ * error | | current | success success | | error
+ * | +-----+ +---------------+ | |
+ * | | | +--------------------------------------+ |
+ * | | | | +----------+ +--------------+
+ * | | | | | | |
+ * | +--v-v-v--+ | +--v--v--+
+ * | | DONE | | | FAIL |
+ * | +---------+ | +--------+
+ * +----------------------------+
+ *
+ * \endverbatim
+ */
+
+#define REFRESH_LOG(priority, data, direction, msg...) \
+ ns_log(priority, (data)->zone->name, LOG_OPERATION_REFRESH, direction, \
+ (data)->remote, (data)->layer->flags & KNOT_REQUESTOR_REUSED, msg)
+
+#define AXFRIN_LOG(priority, data, msg...) \
+ ns_log(priority, (data)->zone->name, LOG_OPERATION_AXFR, LOG_DIRECTION_IN, \
+ (data)->remote, (data)->layer->flags & KNOT_REQUESTOR_REUSED, msg)
+
+#define IXFRIN_LOG(priority, data, msg...) \
+ ns_log(priority, (data)->zone->name, LOG_OPERATION_IXFR, LOG_DIRECTION_IN, \
+ (data)->remote, (data)->layer->flags & KNOT_REQUESTOR_REUSED, msg)
+
+enum state {
+ REFRESH_STATE_INVALID = 0,
+ STATE_SOA_QUERY,
+ STATE_TRANSFER,
+};
+
+enum xfr_type {
+ XFR_TYPE_NOTIMP = -2,
+ XFR_TYPE_ERROR = -1,
+ XFR_TYPE_UNDETERMINED = 0,
+ XFR_TYPE_UPTODATE,
+ XFR_TYPE_AXFR,
+ XFR_TYPE_IXFR,
+};
+
+struct refresh_data {
+ knot_layer_t *layer; //!< Used for reading requestor flags.
+
+ // transfer configuration, initialize appropriately:
+
+ zone_t *zone; //!< Zone to eventually updated.
+ conf_t *conf; //!< Server configuration.
+ const struct sockaddr *remote; //!< Remote endpoint.
+ const knot_rrset_t *soa; //!< Local SOA (NULL for AXFR).
+ const size_t max_zone_size; //!< Maximal zone size.
+ bool use_edns; //!< Allow EDNS in SOA/AXFR/IXFR queries.
+ query_edns_data_t edns; //!< EDNS data to be used in queries.
+ zone_master_fallback_t *fallback; //!< Flags allowing zone_master_try() fallbacks.
+ bool fallback_axfr; //!< Flag allowing fallback to AXFR,
+ uint32_t expire_timer; //!< Result: expire timer from answer EDNS.
+
+ // internal state, initialize with zeroes:
+
+ int ret; //!< Error code.
+ enum state state; //!< Event processing state.
+ enum xfr_type xfr_type; //!< Transer type (mostly IXFR versus AXFR).
+ knot_rrset_t *initial_soa_copy; //!< Copy of the received initial SOA.
+ struct xfr_stats stats; //!< Transfer statistics.
+ struct timespec started; //!< When refresh started.
+ size_t change_size; //!< Size of added and removed RRs.
+
+ struct {
+ zone_contents_t *zone; //!< AXFR result, new zone.
+ } axfr;
+
+ struct {
+ struct ixfr_proc *proc; //!< IXFR processing context.
+ knot_rrset_t *final_soa; //!< SOA denoting end of transfer.
+ list_t changesets; //!< IXFR result, zone updates.
+ } ixfr;
+
+ bool updated; // TODO: Can we fid a better way to check if zone was updated?
+ knot_mm_t *mm; // TODO: This used to be used in IXFR. Remove or reuse.
+};
+
+static const uint32_t EXPIRE_TIMER_INVALID = ~0U;
+
+static bool serial_is_current(uint32_t local_serial, uint32_t remote_serial)
+{
+ return (serial_compare(local_serial, remote_serial) & SERIAL_MASK_GEQ);
+}
+
+static time_t bootstrap_next(uint8_t *count)
+{
+ // Let the increment gradually grow in a sensible way.
+ time_t increment = 5 * (*count) * (*count);
+
+ if (increment < 7200) { // two hours
+ (*count)++;
+ } else {
+ increment = 7200;
+ }
+
+ // Add a random delay to prevent burst refresh.
+ return increment + dnssec_random_uint16_t() % 30;
+}
+
+static void limit_timer(conf_t *conf, const knot_dname_t *zone, uint32_t *timer,
+ const char *tm_name, const yp_name_t *low, const yp_name_t *upp)
+{
+ uint32_t tlow = 0;
+ if (low > 0) {
+ conf_val_t val1 = conf_zone_get(conf, low, zone);
+ tlow = conf_int(&val1);
+ }
+ conf_val_t val2 = conf_zone_get(conf, upp, zone);
+ uint32_t tupp = conf_int(&val2);
+
+ const char *msg = "%s timer trimmed to '%s-%s-interval'";
+ if (*timer < tlow) {
+ *timer = tlow;
+ log_zone_debug(zone, msg, tm_name, tm_name, "min");
+ } else if (*timer > tupp) {
+ *timer = tupp;
+ log_zone_debug(zone, msg, tm_name, tm_name, "max");
+ }
+}
+
+/*!
+ * \brief Modify the expire timer wrt the received EDNS EXPIRE (RFC 7314, section 4)
+ *
+ * \param data The refresh data.
+ * \param pkt A received packet to parse.
+ * \param strictly_follow Strictly use EDNS EXPIRE as the expire timer value.
+ * (false == RFC 7314, section 4, second paragraph,
+ * true == third paragraph)
+ */
+static void consume_edns_expire(struct refresh_data *data, knot_pkt_t *pkt, bool strictly_follow)
+{
+ if (data->zone->is_catalog_flag) {
+ data->expire_timer = EXPIRE_TIMER_INVALID;
+ return;
+ }
+
+ uint8_t *expire_opt = knot_pkt_edns_option(pkt, KNOT_EDNS_OPTION_EXPIRE);
+ if (expire_opt != NULL && knot_edns_opt_get_length(expire_opt) == sizeof(uint32_t)) {
+ uint32_t edns_expire = knot_wire_read_u32(knot_edns_opt_get_data(expire_opt));
+ data->expire_timer = strictly_follow ? edns_expire :
+ MAX(edns_expire, data->zone->timers.next_expire - time(NULL));
+ }
+}
+
+static void finalize_timers(struct refresh_data *data)
+{
+ conf_t *conf = data->conf;
+ zone_t *zone = data->zone;
+
+ // EDNS EXPIRE -- RFC 7314, section 4, fourth paragraph.
+ data->expire_timer = MIN(data->expire_timer, zone_soa_expire(data->zone));
+ assert(data->expire_timer != EXPIRE_TIMER_INVALID);
+
+ time_t now = time(NULL);
+ const knot_rdataset_t *soa = zone_soa(zone);
+
+ uint32_t soa_refresh = knot_soa_refresh(soa->rdata);
+ limit_timer(conf, zone->name, &soa_refresh, "refresh",
+ C_REFRESH_MIN_INTERVAL, C_REFRESH_MAX_INTERVAL);
+ zone->timers.next_refresh = now + soa_refresh;
+ zone->timers.last_refresh_ok = true;
+
+ if (zone->is_catalog_flag) {
+ // It's already zero in most cases.
+ zone->timers.next_expire = 0;
+ } else {
+ limit_timer(conf, zone->name, &data->expire_timer, "expire",
+ // Limit min if not received as EDNS Expire.
+ data->expire_timer == knot_soa_expire(soa->rdata) ?
+ C_EXPIRE_MIN_INTERVAL : 0,
+ C_EXPIRE_MAX_INTERVAL);
+ zone->timers.next_expire = now + data->expire_timer;
+ }
+}
+
+static void fill_expires_in(char *expires_in, size_t size, const struct refresh_data *data)
+{
+ assert(!data->zone->is_catalog_flag || data->zone->timers.next_expire == 0);
+ if (data->zone->timers.next_expire > 0) {
+ (void)snprintf(expires_in, size,
+ ", expires in %u seconds", data->expire_timer);
+ }
+}
+
+static void xfr_log_publish(const struct refresh_data *data,
+ const uint32_t old_serial,
+ const uint32_t new_serial,
+ const uint32_t master_serial,
+ bool has_master_serial,
+ bool axfr_bootstrap)
+{
+ struct timespec finished = time_now();
+ double duration = time_diff_ms(&data->started, &finished) / 1000.0;
+
+ char old_info[32] = "none";
+ if (!axfr_bootstrap) {
+ (void)snprintf(old_info, sizeof(old_info), "%u", old_serial);
+ }
+
+ char master_info[32] = "";
+ if (has_master_serial) {
+ (void)snprintf(master_info, sizeof(master_info),
+ ", remote serial %u", master_serial);
+ }
+
+ char expires_in[32] = "";
+ fill_expires_in(expires_in, sizeof(expires_in), data);
+
+ REFRESH_LOG(LOG_INFO, data, LOG_DIRECTION_NONE,
+ "zone updated, %0.2f seconds, serial %s -> %u%s%s",
+ duration, old_info, new_serial, master_info, expires_in);
+}
+
+static void xfr_log_read_ms(const knot_dname_t *zone, int ret)
+{
+ log_zone_error(zone, "failed reading master serial from KASP DB (%s)", knot_strerror(ret));
+}
+
+static int axfr_init(struct refresh_data *data)
+{
+ zone_contents_t *new_zone = zone_contents_new(data->zone->name, true);
+ if (new_zone == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ data->axfr.zone = new_zone;
+ return KNOT_EOK;
+}
+
+static void axfr_cleanup(struct refresh_data *data)
+{
+ zone_contents_deep_free(data->axfr.zone);
+ data->axfr.zone = NULL;
+}
+
+static void axfr_slave_sign_serial(zone_contents_t *new_contents, zone_t *zone,
+ conf_t *conf, uint32_t *master_serial)
+{
+ // Update slave's serial to ensure it's growing and consistent with
+ // its serial policy.
+ conf_val_t val = conf_zone_get(conf, C_SERIAL_POLICY, zone->name);
+ unsigned serial_policy = conf_opt(&val);
+
+ *master_serial = zone_contents_serial(new_contents);
+
+ uint32_t new_serial, lastsigned_serial;
+ if (zone->contents != NULL) {
+ // Retransfer or AXFR-fallback - increment current serial.
+ new_serial = serial_next(zone_contents_serial(zone->contents), serial_policy, 1);
+ } else if (zone_get_lastsigned_serial(zone, &lastsigned_serial) == KNOT_EOK) {
+ // Bootstrap - increment stored serial.
+ new_serial = serial_next(lastsigned_serial, serial_policy, 1);
+ } else {
+ // Bootstrap - try to reuse master serial, considering policy.
+ new_serial = serial_next(*master_serial, serial_policy, 0);
+ }
+ zone_contents_set_soa_serial(new_contents, new_serial);
+}
+
+static int axfr_finalize(struct refresh_data *data)
+{
+ zone_contents_t *new_zone = data->axfr.zone;
+
+ conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name);
+ bool dnssec_enable = conf_bool(&val);
+ uint32_t old_serial = zone_contents_serial(data->zone->contents), master_serial = 0;
+ bool bootstrap = (data->zone->contents == NULL);
+
+ if (dnssec_enable) {
+ axfr_slave_sign_serial(new_zone, data->zone, data->conf, &master_serial);
+ }
+
+ zone_update_t up = { 0 };
+ int ret = zone_update_from_contents(&up, data->zone, new_zone, UPDATE_FULL);
+ if (ret != KNOT_EOK) {
+ data->fallback->remote = false;
+ return ret;
+ }
+ // Seized by zone_update. Don't free the contents again in axfr_cleanup.
+ data->axfr.zone = NULL;
+
+ ret = zone_update_semcheck(data->conf, &up);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_verify_digest(data->conf, &up);
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ return ret;
+ }
+
+ val = conf_zone_get(data->conf, C_ZONEMD_GENERATE, data->zone->name);
+ unsigned digest_alg = conf_opt(&val);
+
+ if (dnssec_enable) {
+ zone_sign_reschedule_t resch = { 0 };
+ ret = knot_dnssec_zone_sign(&up, data->conf, ZONE_SIGN_KEEP_SERIAL, KEY_ROLL_ALLOW_ALL, 0, &resch);
+ event_dnssec_reschedule(data->conf, data->zone, &resch, true);
+ } else if (digest_alg != ZONE_DIGEST_NONE) {
+ assert(zone_update_to(&up) != NULL);
+ ret = zone_update_add_digest(&up, digest_alg, false);
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ data->fallback->remote = false;
+ return ret;
+ }
+
+ ret = zone_update_commit(data->conf, &up);
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ AXFRIN_LOG(LOG_WARNING, data,
+ "failed to store changes (%s)", knot_strerror(ret));
+ data->fallback->remote = false;
+ return ret;
+ }
+
+ if (dnssec_enable) {
+ ret = zone_set_master_serial(data->zone, master_serial);
+ if (ret != KNOT_EOK) {
+ log_zone_warning(data->zone->name,
+ "unable to save master serial, future transfers might be broken");
+ }
+ }
+
+ finalize_timers(data);
+ xfr_log_publish(data, old_serial, zone_contents_serial(new_zone),
+ master_serial, dnssec_enable, bootstrap);
+
+ return KNOT_EOK;
+}
+
+static int axfr_consume_rr(const knot_rrset_t *rr, struct refresh_data *data)
+{
+ assert(rr);
+ assert(data);
+ assert(data->axfr.zone);
+
+ // zc is stateless structure which can be initialized for each rr
+ // the changes are stored only in data->axfr.zone (aka zc.z)
+ zcreator_t zc = {
+ .z = data->axfr.zone,
+ .master = false,
+ .ret = KNOT_EOK
+ };
+
+ if (rr->type == KNOT_RRTYPE_SOA &&
+ node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) {
+ return KNOT_STATE_DONE;
+ }
+
+ data->ret = zcreator_step(&zc, rr);
+ if (data->ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ data->change_size += knot_rrset_size(rr);
+ if (data->change_size > data->max_zone_size) {
+ AXFRIN_LOG(LOG_WARNING, data,
+ "zone size exceeded");
+ data->ret = KNOT_EZONESIZE;
+ return KNOT_STATE_FAIL;
+ }
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int axfr_consume_packet(knot_pkt_t *pkt, struct refresh_data *data)
+{
+ assert(pkt);
+ assert(data);
+
+ const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+ int ret = KNOT_STATE_CONSUME;
+ for (uint16_t i = 0; i < answer->count && ret == KNOT_STATE_CONSUME; ++i) {
+ ret = axfr_consume_rr(knot_pkt_rr(answer, i), data);
+ }
+ return ret;
+}
+
+static int axfr_consume(knot_pkt_t *pkt, struct refresh_data *data, bool reuse_soa)
+{
+ assert(pkt);
+ assert(data);
+
+ // Check RCODE
+ if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ AXFRIN_LOG(LOG_WARNING, data,
+ "server responded with error '%s'",
+ knot_pkt_ext_rcode_name(pkt));
+ data->ret = KNOT_EDENIED;
+ return KNOT_STATE_FAIL;
+ }
+
+ // Initialize with first packet
+ if (data->axfr.zone == NULL) {
+ data->ret = axfr_init(data);
+ if (data->ret != KNOT_EOK) {
+ AXFRIN_LOG(LOG_WARNING, data,
+ "failed to initialize (%s)",
+ knot_strerror(data->ret));
+ data->fallback->remote = false;
+ return KNOT_STATE_FAIL;
+ }
+
+ AXFRIN_LOG(LOG_INFO, data, "started");
+ xfr_stats_begin(&data->stats);
+ data->change_size = 0;
+ }
+
+ int next;
+ // Process saved SOA if fallback from IXFR
+ if (data->initial_soa_copy != NULL) {
+ next = reuse_soa ? axfr_consume_rr(data->initial_soa_copy, data) :
+ KNOT_STATE_CONSUME;
+ knot_rrset_free(data->initial_soa_copy, data->mm);
+ data->initial_soa_copy = NULL;
+ if (next != KNOT_STATE_CONSUME) {
+ return next;
+ }
+ }
+
+ // Process answer packet
+ xfr_stats_add(&data->stats, pkt->size);
+ next = axfr_consume_packet(pkt, data);
+
+ // Finalize
+ if (next == KNOT_STATE_DONE) {
+ xfr_stats_end(&data->stats);
+ }
+
+ return next;
+}
+
+/*! \brief Initialize IXFR-in processing context. */
+static int ixfr_init(struct refresh_data *data)
+{
+ struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(*proc));
+ if (proc == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(proc, 0, sizeof(struct ixfr_proc));
+ proc->state = IXFR_START;
+ proc->mm = data->mm;
+
+ data->ixfr.proc = proc;
+ data->ixfr.final_soa = NULL;
+
+ init_list(&data->ixfr.changesets);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Clean up data allocated by IXFR-in processing. */
+static void ixfr_cleanup(struct refresh_data *data)
+{
+ if (data->ixfr.proc == NULL) {
+ return;
+ }
+
+ knot_rrset_free(data->ixfr.final_soa, data->mm);
+ data->ixfr.final_soa = NULL;
+ mm_free(data->mm, data->ixfr.proc);
+ data->ixfr.proc = NULL;
+
+ changesets_free(&data->ixfr.changesets);
+}
+
+static bool ixfr_serial_once(changeset_t *ch, int policy, uint32_t *master_serial, uint32_t *local_serial)
+{
+ uint32_t ch_from = changeset_from(ch), ch_to = changeset_to(ch);
+
+ if (ch_from != *master_serial || (serial_compare(ch_from, ch_to) & SERIAL_MASK_GEQ)) {
+ return false;
+ }
+
+ uint32_t new_from = *local_serial;
+ uint32_t new_to = serial_next(new_from, policy, 1);
+ knot_soa_serial_set(ch->soa_from->rrs.rdata, new_from);
+ knot_soa_serial_set(ch->soa_to->rrs.rdata, new_to);
+
+ *master_serial = ch_to;
+ *local_serial = new_to;
+
+ return true;
+}
+
+static int ixfr_slave_sign_serial(list_t *changesets, zone_t *zone,
+ conf_t *conf, uint32_t *master_serial)
+{
+ uint32_t local_serial = zone_contents_serial(zone->contents), lastsigned;
+
+ if (zone_get_lastsigned_serial(zone, &lastsigned) != KNOT_EOK || lastsigned != local_serial) {
+ // this is kind of assert
+ return KNOT_ERROR;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_SERIAL_POLICY, zone->name);
+ unsigned serial_policy = conf_opt(&val);
+
+ int ret = zone_get_master_serial(zone, master_serial);
+ if (ret != KNOT_EOK) {
+ log_zone_error(zone->name, "failed to read master serial"
+ "from KASP DB (%s)", knot_strerror(ret));
+ return ret;
+ }
+ changeset_t *chs;
+ WALK_LIST(chs, *changesets) {
+ if (!ixfr_serial_once(chs, serial_policy, master_serial, &local_serial)) {
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int ixfr_finalize(struct refresh_data *data)
+{
+ conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name);
+ bool dnssec_enable = conf_bool(&val);
+ uint32_t master_serial = 0, old_serial = zone_contents_serial(data->zone->contents);
+
+ if (dnssec_enable) {
+ int ret = ixfr_slave_sign_serial(&data->ixfr.changesets, data->zone, data->conf, &master_serial);
+ if (ret != KNOT_EOK) {
+ IXFRIN_LOG(LOG_WARNING, data,
+ "failed to adjust SOA serials from unsigned remote (%s)",
+ knot_strerror(ret));
+ data->fallback_axfr = false;
+ data->fallback->remote = false;
+ return ret;
+ }
+ }
+
+ zone_update_t up = { 0 };
+ int ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT | UPDATE_NO_CHSET);
+ if (ret != KNOT_EOK) {
+ data->fallback_axfr = false;
+ data->fallback->remote = false;
+ return ret;
+ }
+
+ changeset_t *set;
+ WALK_LIST(set, data->ixfr.changesets) {
+ ret = zone_update_apply_changeset(&up, set);
+ if (ret != KNOT_EOK) {
+ uint32_t serial_from = knot_soa_serial(set->soa_from->rrs.rdata);
+ uint32_t serial_to = knot_soa_serial(set->soa_to->rrs.rdata);
+ zone_update_clear(&up);
+ IXFRIN_LOG(LOG_WARNING, data,
+ "serial %u -> %u, failed to apply changes to zone (%s)",
+ serial_from, serial_to, knot_strerror(ret));
+ return ret;
+ }
+ }
+
+ ret = zone_update_semcheck(data->conf, &up);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_verify_digest(data->conf, &up);
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ data->fallback_axfr = false;
+ return ret;
+ }
+
+ val = conf_zone_get(data->conf, C_ZONEMD_GENERATE, data->zone->name);
+ unsigned digest_alg = conf_opt(&val);
+
+ if (dnssec_enable) {
+ ret = knot_dnssec_sign_update(&up, data->conf);
+ } else if (digest_alg != ZONE_DIGEST_NONE) {
+ assert(zone_update_to(&up) != NULL);
+ ret = zone_update_add_digest(&up, digest_alg, false);
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ data->fallback_axfr = false;
+ data->fallback->remote = false;
+ return ret;
+ }
+
+ ret = zone_update_commit(data->conf, &up);
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ IXFRIN_LOG(LOG_WARNING, data,
+ "failed to store changes (%s)", knot_strerror(ret));
+ return ret;
+ }
+
+ if (dnssec_enable && !EMPTY_LIST(data->ixfr.changesets)) {
+ ret = zone_set_master_serial(data->zone, master_serial);
+ if (ret != KNOT_EOK) {
+ log_zone_warning(data->zone->name,
+ "unable to save master serial, future transfers might be broken");
+ }
+ }
+
+ finalize_timers(data);
+ xfr_log_publish(data, old_serial, zone_contents_serial(data->zone->contents),
+ master_serial, dnssec_enable, false);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Stores starting SOA into changesets structure. */
+static int ixfr_solve_start(const knot_rrset_t *rr, struct refresh_data *data)
+{
+ assert(data->ixfr.final_soa == NULL);
+ if (rr->type != KNOT_RRTYPE_SOA) {
+ return KNOT_EMALF;
+ }
+
+ // Store terminal SOA
+ data->ixfr.final_soa = knot_rrset_copy(rr, data->mm);
+ if (data->ixfr.final_soa == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Initialize list for changes
+ init_list(&data->ixfr.changesets);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Decides what to do with a starting SOA (deletions). */
+static int ixfr_solve_soa_del(const knot_rrset_t *rr, struct refresh_data *data)
+{
+ if (rr->type != KNOT_RRTYPE_SOA) {
+ return KNOT_EMALF;
+ }
+
+ // Create new changeset.
+ changeset_t *change = changeset_new(data->zone->name);
+ if (change == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Store SOA into changeset.
+ change->soa_from = knot_rrset_copy(rr, NULL);
+ if (change->soa_from == NULL) {
+ changeset_free(change);
+ return KNOT_ENOMEM;
+ }
+
+ // Add changeset.
+ add_tail(&data->ixfr.changesets, &change->n);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Stores ending SOA into changeset. */
+static int ixfr_solve_soa_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
+{
+ if (rr->type != KNOT_RRTYPE_SOA) {
+ return KNOT_EMALF;
+ }
+
+ change->soa_to = knot_rrset_copy(rr, NULL);
+ if (change->soa_to == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Adds single RR into remove section of changeset. */
+static int ixfr_solve_del(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
+{
+ return changeset_add_removal(change, rr, 0);
+}
+
+/*! \brief Adds single RR into add section of changeset. */
+static int ixfr_solve_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm)
+{
+ return changeset_add_addition(change, rr, 0);
+}
+
+/*! \brief Decides what the next IXFR-in state should be. */
+static int ixfr_next_state(struct refresh_data *data, const knot_rrset_t *rr)
+{
+ const bool soa = (rr->type == KNOT_RRTYPE_SOA);
+ enum ixfr_state state = data->ixfr.proc->state;
+
+ if ((state == IXFR_SOA_ADD || state == IXFR_ADD) &&
+ knot_rrset_equal(rr, data->ixfr.final_soa, true)) {
+ return IXFR_DONE;
+ }
+
+ switch (state) {
+ case IXFR_START:
+ // Final SOA already stored or transfer start.
+ return data->ixfr.final_soa ? IXFR_SOA_DEL : IXFR_START;
+ case IXFR_SOA_DEL:
+ // Empty delete section or start of delete section.
+ return soa ? IXFR_SOA_ADD : IXFR_DEL;
+ case IXFR_SOA_ADD:
+ // Empty add section or start of add section.
+ return soa ? IXFR_SOA_DEL : IXFR_ADD;
+ case IXFR_DEL:
+ // End of delete section or continue.
+ return soa ? IXFR_SOA_ADD : IXFR_DEL;
+ case IXFR_ADD:
+ // End of add section or continue.
+ return soa ? IXFR_SOA_DEL : IXFR_ADD;
+ default:
+ assert(0);
+ return IXFR_INVALID;
+ }
+}
+
+/*!
+ * \brief Processes single RR according to current IXFR-in state. The states
+ * correspond with IXFR-in message structure, in the order they are
+ * mentioned in the code.
+ *
+ * \param rr RR to process.
+ * \param proc Processing context.
+ *
+ * \return KNOT_E*
+ */
+static int ixfr_step(const knot_rrset_t *rr, struct refresh_data *data)
+{
+ data->ixfr.proc->state = ixfr_next_state(data, rr);
+ changeset_t *change = TAIL(data->ixfr.changesets);
+
+ switch (data->ixfr.proc->state) {
+ case IXFR_START:
+ return ixfr_solve_start(rr, data);
+ case IXFR_SOA_DEL:
+ return ixfr_solve_soa_del(rr, data);
+ case IXFR_DEL:
+ return ixfr_solve_del(rr, change, data->mm);
+ case IXFR_SOA_ADD:
+ return ixfr_solve_soa_add(rr, change, data->mm);
+ case IXFR_ADD:
+ return ixfr_solve_add(rr, change, data->mm);
+ case IXFR_DONE:
+ return KNOT_EOK;
+ default:
+ return KNOT_ERROR;
+ }
+}
+
+static int ixfr_consume_rr(const knot_rrset_t *rr, struct refresh_data *data)
+{
+ if (knot_dname_in_bailiwick(rr->owner, data->zone->name) < 0) {
+ return KNOT_STATE_CONSUME;
+ }
+
+ data->ret = ixfr_step(rr, data);
+ if (data->ret != KNOT_EOK) {
+ IXFRIN_LOG(LOG_WARNING, data,
+ "failed (%s)", knot_strerror(data->ret));
+ return KNOT_STATE_FAIL;
+ }
+
+ data->change_size += knot_rrset_size(rr);
+ if (data->change_size / 2 > data->max_zone_size) {
+ IXFRIN_LOG(LOG_WARNING, data,
+ "transfer size exceeded");
+ data->ret = KNOT_EZONESIZE;
+ return KNOT_STATE_FAIL;
+ }
+
+ if (data->ixfr.proc->state == IXFR_DONE) {
+ return KNOT_STATE_DONE;
+ }
+
+ return KNOT_STATE_CONSUME;
+}
+
+/*!
+ * \brief Processes IXFR reply packet and fills in the changesets structure.
+ *
+ * \param pkt Packet containing the IXFR reply in wire format.
+ * \param adata Answer data, including processing context.
+ *
+ * \return KNOT_STATE_CONSUME, KNOT_STATE_DONE, KNOT_STATE_FAIL
+ */
+static int ixfr_consume_packet(knot_pkt_t *pkt, struct refresh_data *data)
+{
+ // Process RRs in the message.
+ const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+ int ret = KNOT_STATE_CONSUME;
+ for (uint16_t i = 0; i < answer->count && ret == KNOT_STATE_CONSUME; ++i) {
+ ret = ixfr_consume_rr(knot_pkt_rr(answer, i), data);
+ }
+ return ret;
+}
+
+static enum xfr_type determine_xfr_type(const knot_pktsection_t *answer,
+ uint32_t zone_serial, const knot_rrset_t *initial_soa)
+{
+ if (answer->count < 1) {
+ return XFR_TYPE_NOTIMP;
+ }
+
+ const knot_rrset_t *rr_one = knot_pkt_rr(answer, 0);
+ if (initial_soa != NULL) {
+ if (rr_one->type == KNOT_RRTYPE_SOA) {
+ return knot_rrset_equal(initial_soa, rr_one, true) ?
+ XFR_TYPE_AXFR : XFR_TYPE_IXFR;
+ }
+ return XFR_TYPE_AXFR;
+ }
+
+ if (answer->count == 1) {
+ if (rr_one->type == KNOT_RRTYPE_SOA) {
+ return serial_is_current(zone_serial, knot_soa_serial(rr_one->rrs.rdata)) ?
+ XFR_TYPE_UPTODATE : XFR_TYPE_UNDETERMINED;
+ }
+ return XFR_TYPE_ERROR;
+ }
+
+ const knot_rrset_t *rr_two = knot_pkt_rr(answer, 1);
+ if (answer->count == 2 && rr_one->type == KNOT_RRTYPE_SOA &&
+ knot_rrset_equal(rr_one, rr_two, true)) {
+ return XFR_TYPE_AXFR;
+ }
+
+ return (rr_one->type == KNOT_RRTYPE_SOA && rr_two->type != KNOT_RRTYPE_SOA) ?
+ XFR_TYPE_AXFR : XFR_TYPE_IXFR;
+}
+
+static int ixfr_consume(knot_pkt_t *pkt, struct refresh_data *data)
+{
+ assert(pkt);
+ assert(data);
+
+ // Check RCODE
+ if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ IXFRIN_LOG(LOG_WARNING, data,
+ "server responded with error '%s'",
+ knot_pkt_ext_rcode_name(pkt));
+ data->ret = KNOT_EDENIED;
+ return KNOT_STATE_FAIL;
+ }
+
+ // Initialize with first packet
+ if (data->ixfr.proc == NULL) {
+ const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+
+ uint32_t master_serial;
+ data->ret = slave_zone_serial(data->zone, data->conf, &master_serial);
+ if (data->ret != KNOT_EOK) {
+ xfr_log_read_ms(data->zone->name, data->ret);
+ data->fallback_axfr = false;
+ data->fallback->remote = false;
+ return KNOT_STATE_FAIL;
+ }
+ data->xfr_type = determine_xfr_type(answer, master_serial,
+ data->initial_soa_copy);
+ switch (data->xfr_type) {
+ case XFR_TYPE_ERROR:
+ IXFRIN_LOG(LOG_WARNING, data,
+ "malformed response SOA");
+ data->ret = KNOT_EMALF;
+ data->xfr_type = XFR_TYPE_IXFR; // unrecognisable IXFR type is the same as failed IXFR
+ return KNOT_STATE_FAIL;
+ case XFR_TYPE_NOTIMP:
+ IXFRIN_LOG(LOG_WARNING, data,
+ "not supported by remote");
+ data->ret = KNOT_ENOTSUP;
+ data->xfr_type = XFR_TYPE_IXFR;
+ return KNOT_STATE_FAIL;
+ case XFR_TYPE_UNDETERMINED:
+ // Store the SOA and check with next packet
+ data->initial_soa_copy = knot_rrset_copy(knot_pkt_rr(answer, 0), data->mm);
+ if (data->initial_soa_copy == NULL) {
+ data->ret = KNOT_ENOMEM;
+ return KNOT_STATE_FAIL;
+ }
+ xfr_stats_add(&data->stats, pkt->size);
+ return KNOT_STATE_CONSUME;
+ case XFR_TYPE_AXFR:
+ IXFRIN_LOG(LOG_INFO, data,
+ "receiving AXFR-style IXFR");
+ return axfr_consume(pkt, data, true);
+ case XFR_TYPE_UPTODATE:
+ consume_edns_expire(data, pkt, false);
+ finalize_timers(data);
+ char expires_in[32] = "";
+ fill_expires_in(expires_in, sizeof(expires_in), data);
+ IXFRIN_LOG(LOG_INFO, data,
+ "zone is up-to-date%s", expires_in);
+ xfr_stats_begin(&data->stats);
+ xfr_stats_add(&data->stats, pkt->size);
+ xfr_stats_end(&data->stats);
+ return KNOT_STATE_DONE;
+ case XFR_TYPE_IXFR:
+ break;
+ default:
+ assert(0);
+ data->ret = KNOT_EPROCESSING;
+ return KNOT_STATE_FAIL;
+ }
+
+ data->ret = ixfr_init(data);
+ if (data->ret != KNOT_EOK) {
+ IXFRIN_LOG(LOG_WARNING, data,
+ "failed to initialize (%s)", knot_strerror(data->ret));
+ data->fallback_axfr = false;
+ data->fallback->remote = false;
+ return KNOT_STATE_FAIL;
+ }
+
+ IXFRIN_LOG(LOG_INFO, data, "started");
+ xfr_stats_begin(&data->stats);
+ data->change_size = 0;
+ }
+
+ int next;
+ // Process saved SOA if existing
+ if (data->initial_soa_copy != NULL) {
+ next = ixfr_consume_rr(data->initial_soa_copy, data);
+ knot_rrset_free(data->initial_soa_copy, data->mm);
+ data->initial_soa_copy = NULL;
+ if (next != KNOT_STATE_CONSUME) {
+ return next;
+ }
+ }
+
+ // Process answer packet
+ xfr_stats_add(&data->stats, pkt->size);
+ next = ixfr_consume_packet(pkt, data);
+
+ // Finalize
+ if (next == KNOT_STATE_DONE) {
+ xfr_stats_end(&data->stats);
+ }
+
+ return next;
+}
+
+static int soa_query_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+
+ query_init_pkt(pkt);
+
+ data->ret = knot_pkt_put_question(pkt, data->zone->name, KNOT_CLASS_IN,
+ KNOT_RRTYPE_SOA);
+ if (data->ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ if (data->use_edns) {
+ data->ret = query_put_edns(pkt, &data->edns);
+ if (data->ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int soa_query_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+
+ if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) {
+ REFRESH_LOG(LOG_WARNING, data, LOG_DIRECTION_IN,
+ "server responded with error '%s'",
+ knot_pkt_ext_rcode_name(pkt));
+ data->ret = KNOT_EDENIED;
+ return KNOT_STATE_FAIL;
+ }
+
+ const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER);
+ const knot_rrset_t *rr = answer->count == 1 ? knot_pkt_rr(answer, 0) : NULL;
+ if (!rr || rr->type != KNOT_RRTYPE_SOA || rr->rrs.count != 1) {
+ REFRESH_LOG(LOG_WARNING, data, LOG_DIRECTION_IN,
+ "malformed message");
+ conf_val_t val = conf_zone_get(data->conf, C_SEM_CHECKS, data->zone->name);
+ if (conf_opt(&val) == SEMCHECKS_SOFT) {
+ data->xfr_type = XFR_TYPE_AXFR;
+ data->state = STATE_TRANSFER;
+ return KNOT_STATE_RESET;
+ } else {
+ data->ret = KNOT_EMALF;
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ uint32_t local_serial;
+ data->ret = slave_zone_serial(data->zone, data->conf, &local_serial);
+ if (data->ret != KNOT_EOK) {
+ xfr_log_read_ms(data->zone->name, data->ret);
+ data->fallback->remote = false;
+ return KNOT_STATE_FAIL;
+ }
+ uint32_t remote_serial = knot_soa_serial(rr->rrs.rdata);
+ bool current = serial_is_current(local_serial, remote_serial);
+ bool master_uptodate = serial_is_current(remote_serial, local_serial);
+
+ if (!current) {
+ REFRESH_LOG(LOG_INFO, data, LOG_DIRECTION_NONE,
+ "remote serial %u, zone is outdated", remote_serial);
+ data->state = STATE_TRANSFER;
+ return KNOT_STATE_RESET; // continue with transfer
+ } else if (master_uptodate) {
+ consume_edns_expire(data, pkt, false);
+ finalize_timers(data);
+ char expires_in[32] = "";
+ fill_expires_in(expires_in, sizeof(expires_in), data);
+ REFRESH_LOG(LOG_INFO, data, LOG_DIRECTION_NONE,
+ "remote serial %u, zone is up-to-date%s",
+ remote_serial, expires_in);
+ return KNOT_STATE_DONE;
+ } else {
+ REFRESH_LOG(LOG_INFO, data, LOG_DIRECTION_NONE,
+ "remote serial %u, remote is outdated", remote_serial);
+ return KNOT_STATE_FAIL;
+ }
+}
+
+static int transfer_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+
+ query_init_pkt(pkt);
+
+ bool ixfr = (data->xfr_type == XFR_TYPE_IXFR);
+
+ data->ret = knot_pkt_put_question(pkt, data->zone->name, KNOT_CLASS_IN,
+ ixfr ? KNOT_RRTYPE_IXFR : KNOT_RRTYPE_AXFR);
+ if (data->ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ if (ixfr) {
+ assert(data->soa);
+ knot_rrset_t *sending_soa = knot_rrset_copy(data->soa, data->mm);
+ uint32_t master_serial;
+ data->ret = slave_zone_serial(data->zone, data->conf, &master_serial);
+ if (data->ret != KNOT_EOK) {
+ data->fallback->remote = false;
+ xfr_log_read_ms(data->zone->name, data->ret);
+ }
+ if (sending_soa == NULL || data->ret != KNOT_EOK) {
+ knot_rrset_free(sending_soa, data->mm);
+ return KNOT_STATE_FAIL;
+ }
+ knot_soa_serial_set(sending_soa->rrs.rdata, master_serial);
+ knot_pkt_begin(pkt, KNOT_AUTHORITY);
+ knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, sending_soa, 0);
+ knot_rrset_free(sending_soa, data->mm);
+ }
+
+ if (data->use_edns) {
+ data->ret = query_put_edns(pkt, &data->edns);
+ if (data->ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ return KNOT_STATE_CONSUME;
+}
+
+static int transfer_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+
+ consume_edns_expire(data, pkt, true);
+ if (data->expire_timer < 2) {
+ REFRESH_LOG(LOG_WARNING, data, LOG_DIRECTION_NONE,
+ "remote is expired, ignoring");
+ return KNOT_STATE_IGNORE;
+ }
+
+ data->fallback_axfr = (data->xfr_type == XFR_TYPE_IXFR);
+
+ int next = (data->xfr_type == XFR_TYPE_AXFR) ? axfr_consume(pkt, data, false) :
+ ixfr_consume(pkt, data);
+
+ // Transfer completed
+ if (next == KNOT_STATE_DONE) {
+ // Log transfer even if we still can fail
+ xfr_log_finished(data->zone->name,
+ data->xfr_type == XFR_TYPE_IXFR ||
+ data->xfr_type == XFR_TYPE_UPTODATE ?
+ LOG_OPERATION_IXFR : LOG_OPERATION_AXFR,
+ LOG_DIRECTION_IN, data->remote,
+ layer->flags & KNOT_REQUESTOR_REUSED,
+ &data->stats);
+
+ /*
+ * TODO: Move finialization into finish
+ * callback. And update requestor to allow reset from fallback
+ * as we need IXFR to AXFR failover.
+ */
+ if (tsig_unsigned_count(layer->tsig) != 0) {
+ data->ret = KNOT_EMALF;
+ return KNOT_STATE_FAIL;
+ }
+
+ // Finalize and publish the zone
+ switch (data->xfr_type) {
+ case XFR_TYPE_IXFR:
+ data->ret = ixfr_finalize(data);
+ break;
+ case XFR_TYPE_AXFR:
+ data->ret = axfr_finalize(data);
+ break;
+ default:
+ return next;
+ }
+ if (data->ret == KNOT_EOK) {
+ data->updated = true;
+ } else {
+ next = KNOT_STATE_FAIL;
+ }
+ }
+
+ return next;
+}
+
+static int refresh_begin(knot_layer_t *layer, void *_data)
+{
+ layer->data = _data;
+ struct refresh_data *data = _data;
+ data->layer = layer;
+
+ if (data->soa) {
+ data->state = STATE_SOA_QUERY;
+ data->xfr_type = XFR_TYPE_IXFR;
+ data->initial_soa_copy = NULL;
+ } else {
+ data->state = STATE_TRANSFER;
+ data->xfr_type = XFR_TYPE_AXFR;
+ data->initial_soa_copy = NULL;
+ }
+
+ data->started = time_now();
+
+ return KNOT_STATE_PRODUCE;
+}
+
+static int refresh_produce(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+ data->layer = layer;
+
+ switch (data->state) {
+ case STATE_SOA_QUERY: return soa_query_produce(layer, pkt);
+ case STATE_TRANSFER: return transfer_produce(layer, pkt);
+ default:
+ return KNOT_STATE_FAIL;
+ }
+}
+
+static int refresh_consume(knot_layer_t *layer, knot_pkt_t *pkt)
+{
+ struct refresh_data *data = layer->data;
+ data->layer = layer;
+
+ data->fallback->address = false; // received something, other address not needed
+
+ switch (data->state) {
+ case STATE_SOA_QUERY: return soa_query_consume(layer, pkt);
+ case STATE_TRANSFER: return transfer_consume(layer, pkt);
+ default:
+ return KNOT_STATE_FAIL;
+ }
+}
+
+static int refresh_reset(knot_layer_t *layer)
+{
+ return KNOT_STATE_PRODUCE;
+}
+
+static int refresh_finish(knot_layer_t *layer)
+{
+ struct refresh_data *data = layer->data;
+ data->layer = layer;
+
+ // clean processing context
+ axfr_cleanup(data);
+ ixfr_cleanup(data);
+
+ return KNOT_STATE_NOOP;
+}
+
+static const knot_layer_api_t REFRESH_API = {
+ .begin = refresh_begin,
+ .produce = refresh_produce,
+ .consume = refresh_consume,
+ .reset = refresh_reset,
+ .finish = refresh_finish,
+};
+
+static size_t max_zone_size(conf_t *conf, const knot_dname_t *zone)
+{
+ conf_val_t val = conf_zone_get(conf, C_ZONE_MAX_SIZE, zone);
+ return conf_int(&val);
+}
+
+typedef struct {
+ bool force_axfr;
+ bool send_notify;
+} try_refresh_ctx_t;
+
+static int try_refresh(conf_t *conf, zone_t *zone, const conf_remote_t *master,
+ void *ctx, zone_master_fallback_t *fallback)
+{
+ // TODO: Abstract interface to issue DNS queries. This is almost copy-pasted.
+
+ assert(zone);
+ assert(master);
+ assert(ctx);
+ assert(fallback);
+
+ try_refresh_ctx_t *trctx = ctx;
+
+ knot_rrset_t soa = { 0 };
+ if (zone->contents) {
+ soa = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
+ }
+
+ struct refresh_data data = {
+ .zone = zone,
+ .conf = conf,
+ .remote = (struct sockaddr *)&master->addr,
+ .soa = zone->contents && !trctx->force_axfr ? &soa : NULL,
+ .max_zone_size = max_zone_size(conf, zone->name),
+ .use_edns = !master->no_edns,
+ .edns = query_edns_data_init(conf, master->addr.ss_family,
+ QUERY_EDNS_OPT_EXPIRE),
+ .expire_timer = EXPIRE_TIMER_INVALID,
+ .fallback = fallback,
+ .fallback_axfr = false, // will be set upon IXFR consume
+ };
+
+ knot_requestor_t requestor;
+ knot_requestor_init(&requestor, &REFRESH_API, &data, NULL);
+
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (!pkt) {
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ const struct sockaddr_storage *dst = &master->addr;
+ const struct sockaddr_storage *src = &master->via;
+ knot_request_flag_t flags = conf->cache.srv_tcp_fastopen ? KNOT_REQUEST_TFO : 0;
+ knot_request_t *req = knot_request_make(NULL, dst, src, pkt, &master->key, flags);
+ if (!req) {
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+ return KNOT_ENOMEM;
+ }
+
+ int timeout = conf->cache.srv_tcp_remote_io_timeout;
+
+ int ret;
+
+ // while loop runs 0x or 1x; IXFR to AXFR failover
+ while (ret = knot_requestor_exec(&requestor, req, timeout),
+ ret = (data.ret == KNOT_EOK ? ret : data.ret),
+ data.fallback_axfr && ret != KNOT_EOK) {
+ REFRESH_LOG(LOG_WARNING, &data, LOG_DIRECTION_IN,
+ "fallback to AXFR (%s)", knot_strerror(ret));
+ ixfr_cleanup(&data);
+ data.ret = KNOT_EOK;
+ data.xfr_type = XFR_TYPE_AXFR;
+ data.fallback_axfr = false,
+ requestor.layer.state = KNOT_STATE_RESET;
+ requestor.layer.flags |= KNOT_REQUESTOR_CLOSE;
+ }
+ knot_request_free(req, NULL);
+ knot_requestor_clear(&requestor);
+
+ if (ret == KNOT_EOK) {
+ trctx->send_notify = data.updated && !master->block_notify_after_xfr;
+ trctx->force_axfr = false;
+ }
+
+ return ret;
+}
+
+int event_refresh(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ if (!zone_is_slave(conf, zone)) {
+ return KNOT_ENOTSUP;
+ }
+
+ try_refresh_ctx_t trctx = { 0 };
+
+ // TODO: Flag on zone is ugly. Event specific parameters would be nice.
+ if (zone_get_flag(zone, ZONE_FORCE_AXFR, true)) {
+ trctx.force_axfr = true;
+ zone->zonefile.retransfer = true;
+ }
+
+ int ret = zone_master_try(conf, zone, try_refresh, &trctx, "refresh");
+ zone_clear_preferred_master(zone);
+ if (ret != KNOT_EOK) {
+ const knot_rdataset_t *soa = zone_soa(zone);
+ uint32_t next;
+
+ if (soa) {
+ next = knot_soa_retry(soa->rdata);
+ } else {
+ next = bootstrap_next(&zone->zonefile.bootstrap_cnt);
+ }
+
+ limit_timer(conf, zone->name, &next, "retry",
+ C_RETRY_MIN_INTERVAL, C_RETRY_MAX_INTERVAL);
+ zone->timers.next_refresh = time(NULL) + next;
+ zone->timers.last_refresh_ok = false;
+
+ char time_str[64] = { 0 };
+ struct tm time_gm = { 0 };
+ localtime_r(&zone->timers.next_refresh, &time_gm);
+ strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm);
+
+ log_zone_error(zone->name, "refresh, failed (%s), next retry at %s",
+ knot_strerror(ret), time_str);
+ } else {
+ zone->zonefile.bootstrap_cnt = 0;
+ }
+
+ /* Reschedule events. */
+ replan_from_timers(conf, zone);
+ if (trctx.send_notify) {
+ zone_schedule_notify(zone, 1);
+ }
+
+ return ret;
+}
diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c
new file mode 100644
index 0000000..f337eb5
--- /dev/null
+++ b/src/knot/events/handlers/update.c
@@ -0,0 +1,433 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/events/handlers.h"
+#include "knot/nameserver/log.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/query/capture.h"
+#include "knot/query/requestor.h"
+#include "knot/updates/ddns.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/zone.h"
+#include "libdnssec/random.h"
+#include "libknot/libknot.h"
+#include "contrib/net.h"
+#include "contrib/time.h"
+
+#define UPDATE_LOG(priority, qdata, fmt...) \
+ ns_log(priority, knot_pkt_qname(qdata->query), LOG_OPERATION_UPDATE, \
+ LOG_DIRECTION_IN, (struct sockaddr *)knotd_qdata_remote_addr(qdata), \
+ false, fmt)
+
+static void init_qdata_from_request(knotd_qdata_t *qdata,
+ zone_t *zone,
+ knot_request_t *req,
+ knotd_qdata_params_t *params,
+ knotd_qdata_extra_t *extra)
+{
+ memset(qdata, 0, sizeof(*qdata));
+ qdata->params = params;
+ qdata->query = req->query;
+ qdata->sign = req->sign;
+ qdata->extra = extra;
+ memset(extra, 0, sizeof(*extra));
+ qdata->extra->zone = zone;
+}
+
+static int check_prereqs(knot_request_t *request,
+ const zone_t *zone, zone_update_t *update,
+ knotd_qdata_t *qdata)
+{
+ uint16_t rcode = KNOT_RCODE_NOERROR;
+ int ret = ddns_process_prereqs(request->query, update, &rcode);
+ if (ret != KNOT_EOK) {
+ UPDATE_LOG(LOG_WARNING, qdata, "prerequisites not met (%s)",
+ knot_strerror(ret));
+ assert(rcode != KNOT_RCODE_NOERROR);
+ knot_wire_set_rcode(request->resp->wire, rcode);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int process_single_update(knot_request_t *request,
+ const zone_t *zone, zone_update_t *update,
+ knotd_qdata_t *qdata)
+{
+ uint16_t rcode = KNOT_RCODE_NOERROR;
+ int ret = ddns_process_update(zone, request->query, update, &rcode);
+ if (ret != KNOT_EOK) {
+ UPDATE_LOG(LOG_WARNING, qdata, "failed to apply (%s)",
+ knot_strerror(ret));
+ assert(rcode != KNOT_RCODE_NOERROR);
+ knot_wire_set_rcode(request->resp->wire, rcode);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static void set_rcodes(list_t *requests, const uint16_t rcode)
+{
+ ptrnode_t *node;
+ WALK_LIST(node, *requests) {
+ knot_request_t *req = node->d;
+ if (knot_wire_get_rcode(req->resp->wire) == KNOT_RCODE_NOERROR) {
+ knot_wire_set_rcode(req->resp->wire, rcode);
+ }
+ }
+}
+
+static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up)
+{
+ // Walk all the requests and process.
+ ptrnode_t *node;
+ WALK_LIST(node, *requests) {
+ knot_request_t *req = node->d;
+ // Init qdata structure for logging (unique per-request).
+ knotd_qdata_params_t params = {
+ .remote = &req->remote
+ };
+ knotd_qdata_t qdata;
+ knotd_qdata_extra_t extra;
+ init_qdata_from_request(&qdata, zone, req, &params, &extra);
+
+ int ret = check_prereqs(req, zone, up, &qdata);
+ if (ret != KNOT_EOK) {
+ // Skip updates with failed prereqs.
+ continue;
+ }
+
+ ret = process_single_update(req, zone, up, &qdata);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int process_normal(conf_t *conf, zone_t *zone, list_t *requests)
+{
+ assert(requests);
+
+ // Init zone update structure
+ zone_update_t up;
+ int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_NO_CHSET);
+ if (ret != KNOT_EOK) {
+ set_rcodes(requests, KNOT_RCODE_SERVFAIL);
+ return ret;
+ }
+
+ // Process all updates.
+ ret = process_bulk(zone, requests, &up);
+ if (ret == KNOT_EOK) {
+ ret = zone_update_verify_digest(conf, &up);
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ set_rcodes(requests, KNOT_RCODE_SERVFAIL);
+ return ret;
+ }
+
+ // Sign update.
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
+ bool dnssec_enable = conf_bool(&val);
+ val = conf_zone_get(conf, C_ZONEMD_GENERATE, zone->name);
+ unsigned digest_alg = conf_opt(&val);
+ if (dnssec_enable) {
+ ret = knot_dnssec_sign_update(&up, conf);
+ } else if (digest_alg != ZONE_DIGEST_NONE) {
+ if (zone_update_to(&up) == NULL) {
+ ret = zone_update_increment_soa(&up, conf);
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_update_add_digest(&up, digest_alg, false);
+ }
+ }
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ set_rcodes(requests, KNOT_RCODE_SERVFAIL);
+ return ret;
+ }
+
+ // Apply changes.
+ ret = zone_update_commit(conf, &up);
+ if (ret != KNOT_EOK) {
+ zone_update_clear(&up);
+ if (ret == KNOT_EZONESIZE) {
+ set_rcodes(requests, KNOT_RCODE_REFUSED);
+ } else {
+ set_rcodes(requests, KNOT_RCODE_SERVFAIL);
+ }
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static void process_requests(conf_t *conf, zone_t *zone, list_t *requests)
+{
+ assert(zone);
+ assert(requests);
+
+ /* Keep original state. */
+ struct timespec t_start = time_now();
+ const uint32_t old_serial = zone_contents_serial(zone->contents);
+
+ /* Process authenticated packet. */
+ int ret = process_normal(conf, zone, requests);
+ if (ret != KNOT_EOK) {
+ log_zone_error(zone->name, "DDNS, processing failed (%s)",
+ knot_strerror(ret));
+ return;
+ }
+
+ /* Evaluate response. */
+ const uint32_t new_serial = zone_contents_serial(zone->contents);
+ if (new_serial == old_serial) {
+ log_zone_info(zone->name, "DDNS, finished, no changes to the zone were made");
+ return;
+ }
+
+ struct timespec t_end = time_now();
+ log_zone_info(zone->name, "DDNS, finished, serial %u -> %u, "
+ "%.02f seconds", old_serial, new_serial,
+ time_diff_ms(&t_start, &t_end) / 1000.0);
+
+ zone_schedule_notify(zone, 1);
+}
+
+static int remote_forward(conf_t *conf, knot_request_t *request, conf_remote_t *remote)
+{
+ /* Copy request and assign new ID. */
+ knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL);
+ int ret = knot_pkt_copy(query, request->query);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(query);
+ return ret;
+ }
+ knot_wire_set_id(query->wire, dnssec_random_uint16_t());
+ knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr);
+
+ /* Prepare packet capture layer. */
+ const knot_layer_api_t *capture = query_capture_api();
+ struct capture_param capture_param = {
+ .sink = request->resp
+ };
+
+ /* Create requestor instance. */
+ knot_requestor_t re;
+ ret = knot_requestor_init(&re, capture, &capture_param, NULL);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(query);
+ return ret;
+ }
+
+ /* Create a request. */
+ const struct sockaddr_storage *dst = &remote->addr;
+ const struct sockaddr_storage *src = &remote->via;
+ knot_request_flag_t flags = conf->cache.srv_tcp_fastopen ? KNOT_REQUEST_TFO : 0;
+ knot_request_t *req = knot_request_make(re.mm, dst, src, query, NULL, flags);
+ if (req == NULL) {
+ knot_requestor_clear(&re);
+ knot_pkt_free(query);
+ return KNOT_ENOMEM;
+ }
+
+ /* Execute the request. */
+ int timeout = conf->cache.srv_tcp_remote_io_timeout;
+ ret = knot_requestor_exec(&re, req, timeout);
+
+ knot_request_free(req, re.mm);
+ knot_requestor_clear(&re);
+
+ return ret;
+}
+
+static void forward_request(conf_t *conf, zone_t *zone, knot_request_t *request)
+{
+ /* Read the ddns master or the first master. */
+ conf_val_t remote = conf_zone_get(conf, C_DDNS_MASTER, zone->name);
+ if (remote.code != KNOT_EOK) {
+ remote = conf_zone_get(conf, C_MASTER, zone->name);
+ }
+
+ /* Get the number of remote addresses. */
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &remote);
+ size_t addr_count = conf_val_count(&addr);
+ assert(addr_count > 0);
+
+ /* Try all remote addresses to forward the request to. */
+ int ret = KNOT_EOK;
+ for (size_t i = 0; i < addr_count; i++) {
+ conf_remote_t master = conf_remote(conf, &remote, i);
+
+ ret = remote_forward(conf, request, &master);
+ if (ret == KNOT_EOK) {
+ break;
+ }
+ }
+
+ /* Restore message ID and TSIG. */
+ knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire));
+ knot_tsig_append(request->resp->wire, &request->resp->size,
+ request->resp->max_size, request->resp->tsig_rr);
+
+ /* Set RCODE if forwarding failed. */
+ if (ret != KNOT_EOK) {
+ knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL);
+ log_zone_error(zone->name, "DDNS, failed to forward updates to the master (%s)",
+ knot_strerror(ret));
+ } else {
+ log_zone_info(zone->name, "DDNS, updates forwarded to the master");
+ }
+}
+
+static void forward_requests(conf_t *conf, zone_t *zone, list_t *requests)
+{
+ assert(zone);
+ assert(requests);
+
+ ptrnode_t *node;
+ WALK_LIST(node, *requests) {
+ knot_request_t *req = node->d;
+ forward_request(conf, zone, req);
+ }
+}
+
+static void send_update_response(conf_t *conf, zone_t *zone, knot_request_t *req)
+{
+ if (req->resp) {
+ if (!zone_is_slave(conf, zone)) {
+ // Sign the response with TSIG where applicable
+ knotd_qdata_t qdata;
+ knotd_qdata_extra_t extra;
+ init_qdata_from_request(&qdata, zone, req, NULL, &extra);
+
+ (void)process_query_sign_response(req->resp, &qdata);
+ }
+
+ if (net_is_stream(req->fd)) {
+ net_dns_tcp_send(req->fd, req->resp->wire, req->resp->size,
+ conf->cache.srv_tcp_remote_io_timeout, NULL);
+ } else {
+ net_dgram_send(req->fd, req->resp->wire, req->resp->size,
+ &req->remote);
+ }
+ }
+}
+
+static void free_request(knot_request_t *req)
+{
+ close(req->fd);
+ knot_pkt_free(req->query);
+ knot_pkt_free(req->resp);
+ dnssec_binary_free(&req->sign.tsig_key.secret);
+ free(req);
+}
+
+static void send_update_responses(conf_t *conf, zone_t *zone, list_t *updates)
+{
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, *updates) {
+ knot_request_t *req = node->d;
+ send_update_response(conf, zone, req);
+ free_request(req);
+ }
+ ptrlist_free(updates, NULL);
+}
+
+static int init_update_responses(list_t *updates)
+{
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, *updates) {
+ knot_request_t *req = node->d;
+ req->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ if (req->resp == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ assert(req->query);
+ knot_pkt_init_response(req->resp, req->query);
+ }
+
+ return KNOT_EOK;
+}
+
+static size_t update_dequeue(zone_t *zone, list_t *updates)
+{
+ assert(zone);
+ assert(updates);
+
+ pthread_mutex_lock(&zone->ddns_lock);
+
+ if (EMPTY_LIST(zone->ddns_queue)) {
+ /* Lost race during reload. */
+ pthread_mutex_unlock(&zone->ddns_lock);
+ return 0;
+ }
+
+ *updates = zone->ddns_queue;
+ size_t update_count = zone->ddns_queue_size;
+ init_list(&zone->ddns_queue);
+ zone->ddns_queue_size = 0;
+
+ pthread_mutex_unlock(&zone->ddns_lock);
+
+ return update_count;
+}
+
+int event_update(conf_t *conf, zone_t *zone)
+{
+ assert(zone);
+
+ /* Get list of pending updates. */
+ list_t updates;
+ size_t update_count = update_dequeue(zone, &updates);
+ if (update_count == 0) {
+ return KNOT_EOK;
+ }
+
+ /* Init updates responses. */
+ int ret = init_update_responses(&updates);
+ if (ret != KNOT_EOK) {
+ /* Send what responses we can. */
+ set_rcodes(&updates, KNOT_RCODE_SERVFAIL);
+ send_update_responses(conf, zone, &updates);
+ return ret;
+ }
+
+ /* Process update list - forward if zone has master, or execute.
+ RCODEs are set. */
+ if (zone_is_slave(conf, zone)) {
+ log_zone_info(zone->name,
+ "DDNS, forwarding %zu updates", update_count);
+ forward_requests(conf, zone, &updates);
+ } else {
+ log_zone_info(zone->name,
+ "DDNS, processing %zu updates", update_count);
+ process_requests(conf, zone, &updates);
+ }
+
+ /* Send responses. */
+ send_update_responses(conf, zone, &updates);
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/events/replan.c b/src/knot/events/replan.c
new file mode 100644
index 0000000..ed03fe1
--- /dev/null
+++ b/src/knot/events/replan.c
@@ -0,0 +1,210 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <time.h>
+
+#include "knot/dnssec/kasp/kasp_db.h"
+#include "knot/events/replan.h"
+
+#define TIME_CANCEL 0
+#define TIME_IGNORE (-1)
+
+/*!
+ * \brief Move DDNS queue from old zone to new zone and replan if necessary.
+ *
+ * New zone will contain references from the old zone. New zone will free
+ * the data.
+ */
+static void replan_ddns(zone_t *zone, zone_t *old_zone)
+{
+ if (old_zone->ddns_queue_size == 0) {
+ return;
+ }
+
+ ptrnode_t *node;
+ WALK_LIST(node, old_zone->ddns_queue) {
+ ptrlist_add(&zone->ddns_queue, node->d, NULL);
+ }
+ zone->ddns_queue_size = old_zone->ddns_queue_size;
+
+ ptrlist_free(&old_zone->ddns_queue, NULL);
+
+ zone_events_schedule_now(zone, ZONE_EVENT_UPDATE);
+}
+
+/*!
+ * \brief Replan events that are already planned for the old zone.
+ *
+ * \notice Preserves notifailed.
+ */
+static void replan_from_zone(zone_t *zone, zone_t *old_zone)
+{
+ assert(zone);
+ assert(old_zone);
+
+ replan_ddns(zone, old_zone);
+
+ const zone_event_type_t types[] = {
+ ZONE_EVENT_REFRESH,
+ ZONE_EVENT_FLUSH,
+ ZONE_EVENT_BACKUP,
+ ZONE_EVENT_NOTIFY,
+ ZONE_EVENT_UFREEZE,
+ ZONE_EVENT_UTHAW,
+ ZONE_EVENT_INVALID
+ };
+
+ for (const zone_event_type_t *type = types; *type != ZONE_EVENT_INVALID; type++) {
+ time_t when = zone_events_get_time(old_zone, *type);
+ if (when > 0) {
+ zone_events_schedule_at(zone, *type, when);
+ }
+ }
+}
+
+/*!
+ * \brief Replan DNSSEC if automatic signing enabled.
+ *
+ * This is required as the configuration could have changed.
+ */
+static void replan_dnssec(conf_t *conf, zone_t *zone)
+{
+ assert(conf);
+ assert(zone);
+
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
+ if (conf_bool(&val)) {
+ zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC);
+ }
+}
+
+/*!
+ * \brief Replan events that depend on zone timers (REFRESH, EXPIRE, FLUSH, RESALT, PARENT DS QUERY).
+ */
+void replan_from_timers(conf_t *conf, zone_t *zone)
+{
+ assert(conf);
+ assert(zone);
+
+ time_t now = time(NULL);
+
+ time_t refresh = TIME_CANCEL;
+ if (zone_is_slave(conf, zone)) {
+ refresh = zone->timers.next_refresh;
+ if (zone->contents == NULL && zone->timers.last_refresh_ok) { // zone disappeared w/o expiry
+ refresh = now;
+ }
+ assert(refresh > 0);
+ }
+
+ time_t expire_pre = TIME_IGNORE;
+ time_t expire = TIME_IGNORE;
+ if (zone_is_slave(conf, zone) && zone->contents != NULL) {
+ expire_pre = TIME_CANCEL;
+ expire = zone->timers.next_expire;
+ }
+
+ time_t flush = TIME_IGNORE;
+ if (!zone_is_slave(conf, zone) || zone->contents != NULL) {
+ conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name);
+ int64_t sync_timeout = conf_int(&val);
+ if (sync_timeout > 0) {
+ flush = zone->timers.last_flush + sync_timeout;
+ }
+ }
+
+ time_t resalt = TIME_IGNORE;
+ time_t ds_check = TIME_CANCEL;
+ time_t ds_push = TIME_CANCEL;
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
+ if (conf_bool(&val)) {
+ conf_val_t policy = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name);
+ conf_id_fix_default(&policy);
+ val = conf_id_get(conf, C_POLICY, C_NSEC3, &policy);
+ if (conf_bool(&val)) {
+ knot_time_t last_resalt = 0;
+ if (knot_lmdb_open(zone_kaspdb(zone)) == KNOT_EOK) {
+ (void)kasp_db_load_nsec3salt(zone_kaspdb(zone), zone->name, NULL, &last_resalt);
+ }
+ if (last_resalt == 0) {
+ resalt = now;
+ } else {
+ val = conf_id_get(conf, C_POLICY, C_NSEC3_SALT_LIFETIME, &policy);
+ if (conf_int(&val) > 0) {
+ resalt = last_resalt + conf_int(&val);
+ }
+ }
+ }
+
+ ds_check = zone->timers.next_ds_check;
+ if (ds_check == 0) {
+ ds_check = TIME_IGNORE;
+ }
+ ds_push = zone->timers.next_ds_push;
+ if (ds_push == 0) {
+ ds_push = TIME_IGNORE;
+ }
+ }
+
+ zone_events_schedule_at(zone,
+ ZONE_EVENT_REFRESH, refresh,
+ ZONE_EVENT_EXPIRE, expire_pre,
+ ZONE_EVENT_EXPIRE, expire,
+ ZONE_EVENT_FLUSH, flush,
+ ZONE_EVENT_DNSSEC, resalt,
+ ZONE_EVENT_DS_CHECK, ds_check,
+ ZONE_EVENT_DS_PUSH, ds_push);
+}
+
+void replan_load_new(zone_t *zone, bool gen_catalog)
+{
+ if (gen_catalog) {
+ /* Catalog generation must wait until the zonedb
+ * is fully created. */
+ zone_events_schedule_now(zone, ZONE_EVENT_LOAD);
+ } else {
+ /* Enqueue directly, make first load waitable,
+ * other events will cascade from load. */
+ zone_events_enqueue(zone, ZONE_EVENT_LOAD);
+ }
+}
+
+void replan_load_bootstrap(conf_t *conf, zone_t *zone)
+{
+ replan_from_timers(conf, zone);
+}
+
+void replan_load_current(conf_t *conf, zone_t *zone, zone_t *old_zone)
+{
+ replan_from_zone(zone, old_zone);
+
+ if (zone->contents != NULL || zone_expired(zone)) {
+ replan_from_timers(conf, zone);
+ replan_dnssec(conf, zone);
+ } else {
+ zone_events_schedule_now(zone, ZONE_EVENT_LOAD);
+ }
+}
+
+void replan_load_updated(zone_t *zone, zone_t *old_zone)
+{
+ zone_notifailed_clear(zone);
+ replan_from_zone(zone, old_zone);
+
+ // other events will cascade from load
+ zone_events_schedule_now(zone, ZONE_EVENT_LOAD);
+}
diff --git a/src/knot/events/replan.h b/src/knot/events/replan.h
new file mode 100644
index 0000000..62ebeb2
--- /dev/null
+++ b/src/knot/events/replan.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/zone/zone.h"
+
+/*!
+ * \brief Replan timer dependent refresh, expire, and flush.
+ */
+void replan_from_timers(conf_t *conf, zone_t *zone);
+
+/*!
+ * \defgroup replan_load Replan timers after zone load or reload.
+ * @{
+ */
+void replan_load_new(zone_t *zone, bool gen_catalog);
+void replan_load_bootstrap(conf_t *conf, zone_t *zone);
+void replan_load_current(conf_t *conf, zone_t *zone, zone_t *old_zone);
+void replan_load_updated(zone_t *zone, zone_t *old_zone);
+/*! @} */
diff --git a/src/knot/include/module.h b/src/knot/include/module.h
new file mode 100644
index 0000000..8190828
--- /dev/null
+++ b/src/knot/include/module.h
@@ -0,0 +1,602 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Knot DNS module interface.
+ *
+ * \addtogroup module
+ * @{
+ */
+
+#pragma once
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <syslog.h>
+#include <sys/socket.h>
+
+#include <libknot/libknot.h>
+#include <libknot/yparser/ypschema.h>
+
+/*** Query module API. ***/
+
+/*! Current module ABI version. */
+#define KNOTD_MOD_ABI_VERSION 400
+/*! Module configuration name prefix. */
+#define KNOTD_MOD_NAME_PREFIX "mod-"
+
+/*! Configuration check function context. */
+typedef struct {
+ const yp_item_t *item; /*!< Current item descriptor. */
+ const uint8_t *id; /*!< Current section identifier. */
+ size_t id_len; /*!< Current section identifier length. */
+ const uint8_t *data; /*!< Current item data. */
+ size_t data_len; /*!< Current item data length. */
+ const char *err_str; /*!< Output error message. */
+ struct knotd_conf_check_extra *extra; /*!< Private items (conf/tools.h). */
+} knotd_conf_check_args_t;
+
+/*! Module context. */
+typedef struct knotd_mod knotd_mod_t;
+
+/*!
+ * Module load callback.
+ *
+ * Responsibilities:
+ * - Query processing hooks registration
+ * - Optional module specific context initialization
+ * - Module configuration processing
+ * - Query statistics counters registration
+ *
+ * \param[in] mod Module context.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+typedef int (*knotd_mod_load_f)(knotd_mod_t *mod);
+
+/*!
+ * Module unload callback.
+ *
+ * Responsibilities:
+ * - Optional module specific context deinitialization
+ *
+ * \param[in] mod Module context.
+ */
+typedef void (*knotd_mod_unload_f)(knotd_mod_t *mod);
+
+/*!
+ * Module configuration section check callback.
+ *
+ * Responsibilities:
+ * - Optional module configuration section items checks.
+ *
+ * \note Set args.err_str to proper error message if error.
+ *
+ * \param[in] args Configuration check arguments.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+typedef int (*knotd_conf_check_f)(knotd_conf_check_args_t *args);
+
+/*! Module flags. */
+typedef enum {
+ KNOTD_MOD_FLAG_NONE = 0, /*!< Unspecified. */
+ KNOTD_MOD_FLAG_OPT_CONF = 1 << 0, /*!< Optional module configuration. */
+ KNOTD_MOD_FLAG_SCOPE_GLOBAL = 1 << 1, /*!< Can be specified as global module. */
+ KNOTD_MOD_FLAG_SCOPE_ZONE = 1 << 2, /*!< Can be specified as zone module. */
+ KNOTD_MOD_FLAG_SCOPE_ANY = KNOTD_MOD_FLAG_SCOPE_GLOBAL |
+ KNOTD_MOD_FLAG_SCOPE_ZONE,
+} knotd_mod_flag_t;
+
+/*! Module API. */
+typedef struct {
+ uint32_t version; /*!< Embedded version of the module ABI. */
+ const char *name; /*!< Module name. */
+ knotd_mod_flag_t flags; /*!< Module flags. */
+ knotd_mod_load_f load; /*!< Module load callback. */
+ knotd_mod_unload_f unload; /*!< Module unload callback. */
+ const yp_item_t *config; /*!< Module configuration schema. */
+ knotd_conf_check_f config_check; /*!< Module configuration check callback. */
+} knotd_mod_api_t;
+
+/*! Static module API symbol must have a unique name. */
+#ifdef KNOTD_MOD_STATIC
+ #define KNOTD_MOD_API_NAME(mod_name) knotd_mod_api_##mod_name
+#else
+ #define KNOTD_MOD_API_NAME(mod_name) knotd_mod_api
+#endif
+
+/*! Module API instance initialization helper macro. */
+#define KNOTD_MOD_API(mod_name, mod_flags, mod_load, mod_unload, mod_conf, mod_conf_check) \
+ __attribute__((visibility("default"))) \
+ const knotd_mod_api_t KNOTD_MOD_API_NAME(mod_name) = { \
+ .version = KNOTD_MOD_ABI_VERSION, \
+ .name = KNOTD_MOD_NAME_PREFIX #mod_name, \
+ .flags = mod_flags, \
+ .load = mod_load, \
+ .unload = mod_unload, \
+ .config = mod_conf, \
+ .config_check = mod_conf_check, \
+ }
+
+/*** Configuration, statistics, logging,... API. ***/
+
+/*!
+ * Checks reference item (YP_TREF) value if the destination exists.
+ *
+ * \note This function is intended to be used in module schema.
+ *
+ * \param[in] args Configuration check arguments.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_conf_check_ref(knotd_conf_check_args_t *args);
+
+/*!
+ * Gets optional module context.
+ *
+ * \param[in] mod Module context.
+ *
+ * \return Pointer to optional module context.
+ */
+void *knotd_mod_ctx(knotd_mod_t *mod);
+
+/*!
+ * Sets optional module context.
+ *
+ * \param[in] mod Module context.
+ * \param[in] ctx Optional module context.
+ */
+void knotd_mod_ctx_set(knotd_mod_t *mod, void *ctx);
+
+/*!
+ * Gets the zone name the module is configured for.
+ *
+ * \param[in] mod Module context.
+ *
+ * \return Zone name.
+ */
+const knot_dname_t *knotd_mod_zone(knotd_mod_t *mod);
+
+/*!
+ * Emits a module specific log message.
+ *
+ * \param[in] mod Module context.
+ * \param[in] priority Message priority (LOG_DEBUG...LOG_CRIT).
+ * \param[in] fmt Content of the message.
+ */
+void knotd_mod_log(knotd_mod_t *mod, int priority, const char *fmt, ...);
+
+/*!
+ * Emits a module specific log message (va_list variant).
+ *
+ * \param[in] mod Module context.
+ * \param[in] priority Message priority (LOG_DEBUG...LOG_CRIT).
+ * \param[in] fmt Content of the message.
+ * \param[in] args Variable argument list.
+ */
+void knotd_mod_vlog(knotd_mod_t *mod, int priority, const char *fmt, va_list args);
+
+/*!
+ * Statistics multi-counter index to name transformation callback.
+ *
+ * \param[in] idx Multi-counter index.
+ * \param[in] idx_count Number of subcounters.
+ *
+ * \return Index name string.
+ */
+typedef char* (*knotd_mod_idx_to_str_f)(uint32_t idx, uint32_t idx_count);
+
+/*!
+ * Registers a statistics counter.
+ *
+ * \param[in] mod Module context.
+ * \param[in] ctr_name Counter name
+ * \param[in] idx_count Number of subcounters (set 1 for single-counter).
+ * \param[in] idx_to_str Subcounter index to name transformation callback
+ * (set NULL for single-counter).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_stats_add(knotd_mod_t *mod, const char *ctr_name, uint32_t idx_count,
+ knotd_mod_idx_to_str_f idx_to_str);
+
+/*!
+ * Increments a statistics counter.
+ *
+ * \param[in] mod Module context.
+ * \param[in] thr_id Index of worker thread.
+ * \param[in] ctr_id Counter id (counted in the order the counters were registered).
+ * \param[in] idx Subcounter index (set 0 for single-counter).
+ * \param[in] val Value increment.
+ */
+void knotd_mod_stats_incr(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val);
+
+/*!
+ * Decrements a statistics counter.
+ *
+ * \param[in] mod Module context.
+ * \param[in] thr_id Index of worker thread.
+ * \param[in] ctr_id Counter id (counted in the order the counters were registered).
+ * \param[in] idx Subcounter index (set 0 for single-counter).
+ * \param[in] val Value decrement.
+ */
+void knotd_mod_stats_decr(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val);
+
+/*!
+ * Sets a statistics counter value.
+ *
+ * \param[in] mod Module context.
+ * \param[in] thr_id Index of worker thread.
+ * \param[in] ctr_id Counter id (counted in the order the counters were registered).
+ * \param[in] idx Subcounter index (set 0 for single-counter).
+ * \param[in] val Value.
+ */
+void knotd_mod_stats_store(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val);
+
+/*! Configuration single-value abstraction. */
+typedef union {
+ int64_t integer;
+ unsigned option;
+ bool boolean;
+ const char *string;
+ const knot_dname_t *dname;
+ struct {
+ struct sockaddr_storage addr;
+ struct sockaddr_storage addr_max;
+ int addr_mask;
+ };
+ struct {
+ const uint8_t *data;
+ size_t data_len;
+ };
+} knotd_conf_val_t;
+
+/*! Configuration value. */
+typedef struct {
+ knotd_conf_val_t single; /*!< Single-valued item data. */
+ knotd_conf_val_t *multi; /*!< Multi-valued item data. */
+ size_t count; /*!< Number of items (0 if default single value). */
+} knotd_conf_t;
+
+/*! Environment items. */
+typedef enum {
+ KNOTD_CONF_ENV_VERSION = 0, /*!< Software version. */
+ KNOTD_CONF_ENV_HOSTNAME = 1, /*!< Current hostname. */
+ KNOTD_CONF_ENV_WORKERS_UDP = 2, /*!< Current number of UDP workers. */
+ KNOTD_CONF_ENV_WORKERS_TCP = 3, /*!< Current number of TCP workers. */
+ KNOTD_CONF_ENV_WORKERS_XDP = 4, /*!< Current number of UDP-over-XDP workers. */
+} knotd_conf_env_t;
+
+/*!
+ * Gets general configuration value.
+ *
+ * \param[in] mod Module context.
+ * \param[in] section_name Section name.
+ * \param[in] item_name Section item name.
+ * \param[in] id Section identifier (NULL for simple section).
+ *
+ * \return Configuration value.
+ */
+knotd_conf_t knotd_conf(knotd_mod_t *mod, const yp_name_t *section_name,
+ const yp_name_t *item_name, const knotd_conf_t *id);
+
+/*!
+ * Gets environment value.
+ *
+ * \param[in] mod Module context.
+ * \param[in] env Environment item.
+ *
+ * \return Configuration value.
+ */
+knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env);
+
+/*!
+ * Gets number of answering threads.
+ *
+ * \param[in] mod Module context.
+ *
+ * \return Number of worker threads.
+ */
+unsigned knotd_mod_threads(knotd_mod_t *mod);
+
+/*!
+ * Gets module configuration value.
+ *
+ * \param[in] mod Module context.
+ * \param[in] item_name Module section item name.
+ *
+ * \return Configuration value.
+ */
+knotd_conf_t knotd_conf_mod(knotd_mod_t *mod, const yp_name_t *item_name);
+
+/*!
+ * Gets zone configuration value.
+ *
+ * \param[in] mod Module context.
+ * \param[in] item_name Zone section item name.
+ * \param[in] zone Zone name.
+ *
+ * \return Configuration value.
+ */
+knotd_conf_t knotd_conf_zone(knotd_mod_t *mod, const yp_name_t *item_name,
+ const knot_dname_t *zone);
+
+/*!
+ * Gets module configuration value during the checking phase.
+ *
+ * \note This function is intended to be used in 'knotd_conf_check_f' callbacks.
+ *
+ * \param[in] args
+ * \param[in] item_name
+ *
+ * \return Configuration value.
+ */
+knotd_conf_t knotd_conf_check_item(knotd_conf_check_args_t *args,
+ const yp_name_t *item_name);
+
+/*!
+ * \brief Checks if address is in at least one of given ranges.
+ *
+ * \param[in] range
+ * \param[in] addr
+ *
+ * \return true if addr is in at least one range, false otherwise.
+ */
+bool knotd_conf_addr_range_match(const knotd_conf_t *range,
+ const struct sockaddr_storage *addr);
+
+/*!
+ * Deallocates multi-valued configuration values.
+ *
+ * \param[in] conf Configuration value.
+ */
+void knotd_conf_free(knotd_conf_t *conf);
+
+/*** Query processing API. ***/
+
+/*!
+ * DNS query type.
+ *
+ * This type encompasses the different query types distinguished by both the
+ * OPCODE and the QTYPE.
+ */
+typedef enum {
+ KNOTD_QUERY_TYPE_INVALID, /*!< Invalid query. */
+ KNOTD_QUERY_TYPE_NORMAL, /*!< Normal query. */
+ KNOTD_QUERY_TYPE_AXFR, /*!< Request for AXFR transfer. */
+ KNOTD_QUERY_TYPE_IXFR, /*!< Request for IXFR transfer. */
+ KNOTD_QUERY_TYPE_NOTIFY, /*!< NOTIFY query. */
+ KNOTD_QUERY_TYPE_UPDATE, /*!< Dynamic update. */
+} knotd_query_type_t;
+
+/*! Supported transport protocols. */
+typedef enum {
+ KNOTD_QUERY_PROTO_UDP = KNOT_PROBE_PROTO_UDP, /*!< Pure UDP. */
+ KNOTD_QUERY_PROTO_TCP = KNOT_PROBE_PROTO_TCP, /*!< Pure TCP. */
+ KNOTD_QUERY_PROTO_QUIC = KNOT_PROBE_PROTO_QUIC, /*!< QUIC/UDP. */
+} knotd_query_proto_t;
+
+/*! Query processing specific flags. */
+typedef enum {
+ KNOTD_QUERY_FLAG_COOKIE = 1 << 0, /*!< Valid DNS Cookie indication. */
+} knotd_query_flag_t;
+
+/*! Query processing data context parameters. */
+typedef struct {
+ knotd_query_proto_t proto; /*!< Transport protocol used. */
+ knotd_query_flag_t flags; /*!< Current query flags. */
+ const struct sockaddr_storage *remote; /*!< Current remote address. */
+ int socket; /*!< Current network socket. */
+ unsigned thread_id; /*!< Current thread id. */
+ void *server; /*!< Server object private item. */
+ const struct knot_xdp_msg *xdp_msg; /*!< Possible XDP message context. */
+ uint32_t measured_rtt; /*!< Measured RTT in usecs: QUIC or TCP-XDP. */
+} knotd_qdata_params_t;
+
+/*! Query processing data context. */
+typedef struct {
+ knot_pkt_t *query; /*!< Query to be solved. */
+ knotd_query_type_t type; /*!< Query packet type. */
+ const knot_dname_t *name; /*!< Currently processed name. */
+ uint16_t rcode; /*!< Resulting RCODE (Whole extended RCODE). */
+ uint16_t rcode_tsig; /*!< Resulting TSIG RCODE. */
+ int rcode_ede; /*!< Resulting Extended (EDE) RCODE. */
+ knot_rrset_t opt_rr; /*!< OPT record. */
+ knot_sign_context_t sign; /*!< Signing context. */
+ knot_edns_client_subnet_t *ecs; /*!< EDNS Client Subnet option. */
+ bool err_truncated; /*!< Set TC and AA bits if an error reply. */
+
+ /*! Persistent items on processing reset. */
+ knot_mm_t *mm; /*!< Memory context. */
+ knotd_qdata_params_t *params; /*!< Low-level processing parameters. */
+
+ struct knotd_qdata_extra *extra; /*!< Private items (process_query.h). */
+} knotd_qdata_t;
+
+/*!
+ * Gets the local (destination) address of the query.
+ *
+ * \param[in] qdata Query data.
+ * \param[out] buff Auxiliary buffer (not used for XDP).
+ *
+ * \return Local address or NULL if error.
+ */
+const struct sockaddr_storage *knotd_qdata_local_addr(knotd_qdata_t *qdata,
+ struct sockaddr_storage *buff);
+
+/*!
+ * Gets the remote (source) address of the query.
+ *
+ * \param[in] qdata Query data.
+ *
+ * \return Remote address or NULL if error.
+ */
+const struct sockaddr_storage *knotd_qdata_remote_addr(knotd_qdata_t *qdata);
+
+/*!
+ * Gets the measured TCP round-trip-time.
+ *
+ * \param[in] qdata Query data.
+ *
+ * \return RTT in microseconds or 0 if error or not available.
+ */
+uint32_t knotd_qdata_rtt(knotd_qdata_t *qdata);
+
+/*!
+ * Gets the current zone name.
+ *
+ * \param[in] qdata Query data.
+ *
+ * \return Zone name.
+ */
+const knot_dname_t *knotd_qdata_zone_name(knotd_qdata_t *qdata);
+
+/*!
+ * Gets the current zone apex rrset of the given type.
+ *
+ * \param[in] qdata Query data.
+ * \param[in] type Rrset type.
+ *
+ * \return A copy of the zone apex rrset.
+ */
+knot_rrset_t knotd_qdata_zone_apex_rrset(knotd_qdata_t *qdata, uint16_t type);
+
+/*! General query processing states. */
+typedef enum {
+ KNOTD_STATE_NOOP = 0, /*!< No response. */
+ KNOTD_STATE_DONE = 4, /*!< Finished. */
+ KNOTD_STATE_FAIL = 5, /*!< Error. */
+ KNOTD_STATE_FINAL = 6, /*!< Finished and finalized (QNAME, EDNS, TSIG). */
+} knotd_state_t;
+
+/*! brief Internet query processing states. */
+typedef enum {
+ KNOTD_IN_STATE_BEGIN, /*!< Begin name resolution. */
+ KNOTD_IN_STATE_NODATA, /*!< Positive result with NO data. */
+ KNOTD_IN_STATE_HIT, /*!< Positive result. */
+ KNOTD_IN_STATE_MISS, /*!< Negative result. */
+ KNOTD_IN_STATE_DELEG, /*!< Result is delegation. */
+ KNOTD_IN_STATE_FOLLOW, /*!< Resolution not complete (CNAME/DNAME chain). */
+ KNOTD_IN_STATE_TRUNC, /*!< Finished, packet size limit encountered. */
+ KNOTD_IN_STATE_ERROR, /*!< Resolution failed. */
+} knotd_in_state_t;
+
+/*! Query module processing stages. */
+typedef enum {
+ KNOTD_STAGE_BEGIN = 0, /*!< Before query processing. */
+ KNOTD_STAGE_PREANSWER, /*!< Before section processing. */
+ KNOTD_STAGE_ANSWER, /*!< Answer section processing. */
+ KNOTD_STAGE_AUTHORITY, /*!< Authority section processing. */
+ KNOTD_STAGE_ADDITIONAL, /*!< Additional section processing. */
+ KNOTD_STAGE_END, /*!< After query processing. */
+} knotd_stage_t;
+
+/*!
+ * General processing hook.
+ *
+ * \param[in] state Current processing state.
+ * \param[in,out] pkt Response packet.
+ * \param[in] qdata Query data.
+ * \param[in] mod Module context.
+ *
+ * \return Next processing state.
+ */
+typedef knotd_state_t (*knotd_mod_hook_f)
+ (knotd_state_t state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod);
+
+/*!
+ * Internet class processing hook.
+ *
+ * \param[in] state Current processing state.
+ * \param[in,out] pkt Response packet.
+ * \param[in] qdata Query data.
+ * \param[in] mod Module context.
+ *
+ * \return Next processing state.
+ */
+typedef knotd_in_state_t (*knotd_mod_in_hook_f)
+ (knotd_in_state_t state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod);
+
+/*!
+ * Registers general processing module hook.
+ *
+ * \param[in] mod Module context.
+ * \param[in] stage Processing stage (KNOTD_STAGE_BEGIN or KNOTD_STAGE_END).
+ * \param[in] hook Module hook.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_hook_f hook);
+
+/*!
+ * Registers Internet class module hook.
+ *
+ * \param[in] mod Module context.
+ * \param[in] stage Processing stage (KNOTD_STAGE_ANSWER..KNOTD_STAGE_ADDITIONAL).
+ * \param[in] hook Module hook.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_in_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_in_hook_f hook);
+
+/*** DNSSEC API. ***/
+
+/*!
+ * Initializes DNSSEC signing context.
+ *
+ * \param[in] mod Module context.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_dnssec_init(knotd_mod_t *mod);
+
+/*!
+ * Loads available DNSSEC signing keys.
+ *
+ * \param[in] mod Module context.
+ * \param[in] verbose Print key summary into log indication.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_dnssec_load_keyset(knotd_mod_t *mod, bool verbose);
+
+/*!
+ * Frees up resources before re-loading DNSSEC signing keys.
+ *
+ * \param[in] mod Module context.
+ */
+void knotd_mod_dnssec_unload_keyset(knotd_mod_t *mod);
+
+/*!
+ * Generates RRSIGs for given RRSet.
+ *
+ * \param[in] mod Module context.
+ * \param[out] rrsigs Output RRSIG RRSet.
+ * \param[in] rrset Input RRSet to generate RRSIGs for.
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int knotd_mod_dnssec_sign_rrset(knotd_mod_t *mod, knot_rrset_t *rrsigs,
+ const knot_rrset_t *rrset, knot_mm_t *mm);
+
+/*! @} */
diff --git a/src/knot/journal/journal_basic.c b/src/knot/journal/journal_basic.c
new file mode 100644
index 0000000..825130a
--- /dev/null
+++ b/src/knot/journal/journal_basic.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2022 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 "knot/journal/journal_basic.h"
+#include "knot/journal/journal_metadata.h"
+#include "libknot/error.h"
+
+MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const knot_dname_t *zone)
+{
+ if (zone_in_journal) {
+ return knot_lmdb_make_key("NIS", zone, (uint32_t)0, "bootstrap");
+ } else {
+ return knot_lmdb_make_key("NII", zone, (uint32_t)0, serial);
+ }
+}
+
+MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id)
+{
+ if (zij) {
+ return knot_lmdb_make_key("NISI", apex, (uint32_t)0, "bootstrap", chunk_id);
+ } else {
+ return knot_lmdb_make_key("NIII", apex, (uint32_t)0, ch_from, chunk_id);
+ }
+}
+
+MDB_val journal_zone_prefix(const knot_dname_t *zone)
+{
+ return knot_lmdb_make_key("NI", zone, (uint32_t)0);
+}
+
+void journal_del_zone(knot_lmdb_txn_t *txn, const knot_dname_t *zone)
+{
+ assert(txn->is_rw);
+ MDB_val prefix = journal_zone_prefix(zone);
+ knot_lmdb_del_prefix(txn, &prefix);
+ free(prefix.mv_data);
+}
+
+void journal_make_header(void *chunk, uint32_t ch_serial_to)
+{
+ knot_lmdb_make_key_part(chunk, JOURNAL_HEADER_SIZE, "IILLL", ch_serial_to,
+ (uint32_t)0 /* we no longer care for # of chunks */,
+ (uint64_t)0, (uint64_t)0, (uint64_t)0);
+}
+
+uint32_t journal_next_serial(const MDB_val *chunk)
+{
+ return knot_wire_read_u32(chunk->mv_data);
+}
+
+bool journal_serial_to(knot_lmdb_txn_t *txn, bool zij, uint32_t serial,
+ const knot_dname_t *zone, uint32_t *serial_to)
+{
+ MDB_val key = journal_changeset_id_to_key(zij, serial, zone);
+ bool found = knot_lmdb_find_prefix(txn, &key);
+ if (found && serial_to != NULL) {
+ *serial_to = journal_next_serial(&txn->cur_val);
+ }
+ free(key.mv_data);
+ return found;
+}
+
+bool journal_allow_flush(zone_journal_t j)
+{
+ conf_val_t val = conf_zone_get(j.conf, C_ZONEFILE_SYNC, j.zone);
+ return conf_int(&val) >= 0;
+}
+
+size_t journal_conf_max_usage(zone_journal_t j)
+{
+ conf_val_t val = conf_zone_get(j.conf, C_JOURNAL_MAX_USAGE, j.zone);
+ return conf_int(&val);
+}
+
+size_t journal_conf_max_changesets(zone_journal_t j)
+{
+ conf_val_t val = conf_zone_get(j.conf, C_JOURNAL_MAX_DEPTH, j.zone);
+ return conf_int(&val);
+}
diff --git a/src/knot/journal/journal_basic.h b/src/knot/journal/journal_basic.h
new file mode 100644
index 0000000..8804d7b
--- /dev/null
+++ b/src/knot/journal/journal_basic.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/journal/knot_lmdb.h"
+#include "knot/updates/changesets.h"
+#include "libknot/dname.h"
+
+typedef struct {
+ knot_lmdb_db_t *db;
+ const knot_dname_t *zone;
+ void *conf; // needed only for journal write operations
+} zone_journal_t;
+
+#define JOURNAL_CHUNK_MAX (70 * 1024) // must be at least 64k + 6B
+#define JOURNAL_CHUNK_THRESH (15 * 1024)
+#define JOURNAL_HEADER_SIZE (32)
+
+/*! \brief Convert journal_mode to LMDB environment flags. */
+inline static unsigned journal_env_flags(int journal_mode, bool readonly)
+{
+ return (journal_mode == JOURNAL_MODE_ASYNC ? (MDB_WRITEMAP | MDB_MAPASYNC) : 0) |
+ (readonly ? MDB_RDONLY : 0);
+}
+
+/*!
+ * \brief Create a database key prefix to search for a changeset.
+ *
+ * \param zone_in_journal True if searching for zone-in-journal special changeset.
+ * \param serial Serial-from of the changeset to be searched for. Ignored if 'zone_in_journal'.
+ * \param zone Name of the zone.
+ *
+ * \return DB key. 'mv_data' shall be freed later. 'mv_data' is NULL on failure.
+ */
+MDB_val journal_changeset_id_to_key(bool zone_in_journal, uint32_t serial, const knot_dname_t *zone);
+
+/*!
+ * \brief Create a database key for changeset chunk.
+ *
+ * \param apex Zone apex owner name.
+ * \param ch_from Serial "from" of the stored changeset.
+ * \param zij Zone-in-journal is stored.
+ * \param chunk_id Ordinal number of this changeset's chunk.
+ *
+ * \return DB key. 'mv_data' shall be freed later. 'mv_data' is NULL on failure.
+ */
+MDB_val journal_make_chunk_key(const knot_dname_t *apex, uint32_t ch_from, bool zij, uint32_t chunk_id);
+
+/*!
+ * \brief Return a key prefix to operate with all zone-related records.
+ */
+MDB_val journal_zone_prefix(const knot_dname_t *zone);
+
+/*!
+ * \brief Delete all zone-related records from journal with open read-write txn.
+ */
+void journal_del_zone(knot_lmdb_txn_t *txn, const knot_dname_t *zone);
+
+/*!
+ * \brief Initialise chunk header.
+ *
+ * \param chunk Pointer to the changeset chunk. It must be at least JOURNAL_HEADER_SIZE, perhaps more.
+ * \param ch Serial-to of the changeset being serialized.
+ */
+void journal_make_header(void *chunk, uint32_t ch_serial_to);
+
+/*!
+ * \brief Obtain serial-to of the serialized changeset.
+ *
+ * \param chunk Any chunk of a serialized changeset.
+ *
+ * \return The changeset's serial-to.
+ */
+uint32_t journal_next_serial(const MDB_val *chunk);
+
+/*!
+ * \brief Obtain serial-to of a changeset stored in journal.
+ *
+ * \param txn Journal DB transaction.
+ * \param zij True if changeset in question is zone-in-journal.
+ * \param serial Serial-from of the changeset in question.
+ * \param zone Zone name.
+ * \param serial_to Output: serial-to of the changeset in question.
+ *
+ * \return True if the changeset exists in the journal.
+ */
+bool journal_serial_to(knot_lmdb_txn_t *txn, bool zij, uint32_t serial,
+ const knot_dname_t *zone, uint32_t *serial_to);
+
+/*! \brief Return true if the changeset in question exists in the journal. */
+inline static bool journal_contains(knot_lmdb_txn_t *txn, bool zone, uint32_t serial, const knot_dname_t *zone_name)
+{
+ return journal_serial_to(txn, zone, serial, zone_name, NULL);
+}
+
+/*! \brief Return true if the journal may be flushed according to conf. */
+bool journal_allow_flush(zone_journal_t j);
+
+/*! \brief Return configured maximal per-zone usage of journal DB. */
+size_t journal_conf_max_usage(zone_journal_t j);
+
+/*! \brief Return configured maximal depth of journal. */
+size_t journal_conf_max_changesets(zone_journal_t j);
diff --git a/src/knot/journal/journal_metadata.c b/src/knot/journal/journal_metadata.c
new file mode 100644
index 0000000..b133534
--- /dev/null
+++ b/src/knot/journal/journal_metadata.c
@@ -0,0 +1,422 @@
+/* Copyright (C) 2021 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 "knot/journal/journal_metadata.h"
+
+#include "libknot/endian.h"
+#include "libknot/error.h"
+
+static void fix_endian(void *data, size_t data_size, bool in)
+{
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } before, after;
+
+ memcpy(&before, data, data_size);
+ switch (data_size) {
+ case sizeof(uint8_t):
+ return;
+ case sizeof(uint16_t):
+ after.u16 = in ? be16toh(before.u16) : htobe16(before.u16);
+ break;
+ case sizeof(uint32_t):
+ after.u32 = in ? be32toh(before.u32) : htobe32(before.u32);
+ break;
+ case sizeof(uint64_t):
+ after.u64 = in ? be64toh(before.u64) : htobe64(before.u64);
+ break;
+ default:
+ assert(0);
+ }
+ memcpy(data, &after, data_size);
+}
+
+static MDB_val metadata_key(const knot_dname_t *zone, const char *metadata)
+{
+ if (zone == NULL) {
+ return knot_lmdb_make_key("IS", (uint32_t)0, metadata);
+ } else {
+ return knot_lmdb_make_key("NIS", zone, (uint32_t)0, metadata);
+ }
+}
+
+static bool del_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata)
+{
+ MDB_val key = metadata_key(zone, metadata);
+ if (key.mv_data != NULL) {
+ knot_lmdb_del_prefix(txn, &key);
+ free(key.mv_data);
+ }
+ return (key.mv_data != NULL);
+}
+
+static bool get_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata)
+{
+ MDB_val key = metadata_key(zone, metadata);
+ bool ret = knot_lmdb_find(txn, &key, KNOT_LMDB_EXACT); // not FORCE
+ free(key.mv_data);
+ return ret;
+}
+
+static bool get_metadata_numeric(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ const char *metadata, void *result, size_t result_size)
+{
+ if (get_metadata(txn, zone, metadata)) {
+ if (txn->cur_val.mv_size == result_size) {
+ memcpy(result, txn->cur_val.mv_data, result_size);
+ fix_endian(result, result_size, true);
+ return true;
+ } else {
+ txn->ret = KNOT_EMALF;
+ }
+ }
+ return false;
+}
+
+bool get_metadata32(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ const char *metadata, uint32_t *result)
+{
+ return get_metadata_numeric(txn, zone, metadata, result, sizeof(*result));
+}
+
+bool get_metadata64(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ const char *metadata, uint64_t *result)
+{
+ return get_metadata_numeric(txn, zone, metadata, result, sizeof(*result));
+}
+
+bool get_metadata64or32(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ const char *metadata, uint64_t *result)
+{
+ if (txn->ret != KNOT_EOK) {
+ return false;
+ }
+ bool ret = get_metadata64(txn, zone, metadata, result);
+ if (txn->ret == KNOT_EMALF) {
+ uint32_t res32 = 0;
+ txn->ret = KNOT_EOK;
+ ret = get_metadata32(txn, zone, metadata, &res32);
+ *result = res32;
+ }
+ return ret;
+}
+
+void set_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const char *metadata,
+ const void *valp, size_t val_size, bool numeric)
+{
+ MDB_val key = metadata_key(zone, metadata);
+ MDB_val val = { val_size, NULL };
+ if (knot_lmdb_insert(txn, &key, &val)) {
+ memcpy(val.mv_data, valp, val_size);
+ if (numeric) {
+ fix_endian(val.mv_data, val_size, false);
+ }
+ }
+ free(key.mv_data);
+}
+
+static int64_t last_occupied_diff(knot_lmdb_txn_t *txn)
+{
+ uint64_t occupied_now = knot_lmdb_usage(txn), occupied_last = 0;
+ (void)get_metadata64(txn, NULL, "last_total_occupied", &occupied_last);
+ return (int64_t)occupied_now - (int64_t)occupied_last;
+}
+
+void update_last_inserter(knot_lmdb_txn_t *txn, const knot_dname_t *new_inserter)
+{
+ uint64_t occupied_now = knot_lmdb_usage(txn), lis_occupied = 0;
+ int64_t occupied_diff = last_occupied_diff(txn);
+ knot_dname_t *last_inserter = get_metadata(txn, NULL, "last_inserter_zone") ?
+ knot_dname_copy(txn->cur_val.mv_data, NULL) : NULL;
+ if (occupied_diff == 0 || last_inserter == NULL) {
+ goto update_inserter;
+ }
+ (void)get_metadata64(txn, last_inserter, "occupied", &lis_occupied);
+ lis_occupied = MAX(0, (int64_t)lis_occupied + occupied_diff);
+ set_metadata(txn, last_inserter, "occupied", &lis_occupied, sizeof(lis_occupied), true);
+
+update_inserter:
+ if (new_inserter == NULL) {
+ del_metadata(txn, NULL, "last_inserter_zone");
+ } else if (last_inserter == NULL || !knot_dname_is_equal(last_inserter, new_inserter)) {
+ set_metadata(txn, NULL, "last_inserter_zone", new_inserter, knot_dname_size(new_inserter), false);
+ }
+ free(last_inserter);
+ set_metadata(txn, NULL, "last_total_occupied", &occupied_now, sizeof(occupied_now), true);
+}
+
+uint64_t journal_get_occupied(knot_lmdb_txn_t *txn, const knot_dname_t *zone)
+{
+ uint64_t res = 0;
+ get_metadata64(txn, zone, "occupied", &res);
+ return res;
+}
+
+static int first_digit(char * of)
+{
+ unsigned maj, min;
+ return sscanf(of, "%u.%u", &maj, &min) == 2 ? maj : -1;
+}
+
+void journal_load_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, journal_metadata_t *md)
+{
+ memset(md, 0, sizeof(*md));
+ if (get_metadata(txn, NULL, "version")) {
+ switch (first_digit(txn->cur_val.mv_data)) {
+ case 3:
+ // TODO warning about downgrade
+ // FALLTHROUGH
+ case 1:
+ // still supported
+ // FALLTHROUGH
+ case 2:
+ // normal operation
+ break;
+ case 0:
+ // failed to read version
+ txn->ret = KNOT_ENOENT;
+ return;
+ default:
+ txn->ret = KNOT_ENOTSUP;
+ return;
+ }
+ }
+ md->_new_zone = !get_metadata32(txn, zone, "flags", &md->flags);
+ (void)get_metadata32(txn, zone, "first_serial", &md->first_serial);
+ (void)get_metadata32(txn, zone, "last_serial_to", &md->serial_to);
+ (void)get_metadata32(txn, zone, "merged_serial", &md->merged_serial);
+ (void)get_metadata32(txn, zone, "changeset_count", &md->changeset_count);
+ if (!get_metadata32(txn, zone, "flushed_upto", &md->flushed_upto)) {
+ // importing from version 1.0
+ if ((md->flags & JOURNAL_LAST_FLUSHED_VALID)) {
+ uint32_t last_flushed = 0;
+ if (!get_metadata32(txn, zone, "last_flushed", &last_flushed) ||
+ !journal_serial_to(txn, false, last_flushed, zone, &md->flushed_upto)) {
+ txn->ret = KNOT_EMALF;
+ } else {
+ md->flags &= ~JOURNAL_LAST_FLUSHED_VALID;
+ }
+ } else {
+ md->flushed_upto = md->first_serial;
+ }
+ }
+
+}
+
+void journal_store_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const journal_metadata_t *md)
+{
+ set_metadata(txn, zone, "first_serial", &md->first_serial, sizeof(md->first_serial), true);
+ set_metadata(txn, zone, "last_serial_to", &md->serial_to, sizeof(md->serial_to), true);
+ set_metadata(txn, zone, "flushed_upto", &md->flushed_upto, sizeof(md->flushed_upto), true);
+ set_metadata(txn, zone, "merged_serial", &md->merged_serial, sizeof(md->merged_serial), true);
+ set_metadata(txn, zone, "changeset_count", &md->changeset_count, sizeof(md->changeset_count), true);
+ set_metadata(txn, zone, "flags", &md->flags, sizeof(md->flags), true);
+ set_metadata(txn, NULL, "version", "2.0", 4, false);
+ if (md->_new_zone) {
+ uint64_t journal_count = 0;
+ (void)get_metadata64or32(txn, NULL, "journal_count", &journal_count);
+ ++journal_count;
+ set_metadata(txn, NULL, "journal_count", &journal_count, sizeof(journal_count), true);
+ }
+}
+
+void journal_metadata_after_delete(journal_metadata_t *md, uint32_t deleted_upto,
+ size_t deleted_count)
+{
+ if (deleted_count == 0) {
+ return;
+ }
+ assert((md->flags & JOURNAL_SERIAL_TO_VALID));
+ if (deleted_upto == md->serial_to) {
+ assert(md->flushed_upto == md->serial_to);
+ assert(md->changeset_count == deleted_count);
+ md->flags &= ~JOURNAL_SERIAL_TO_VALID;
+ }
+ md->first_serial = deleted_upto;
+ md->changeset_count -= deleted_count;
+}
+
+void journal_metadata_after_merge(journal_metadata_t *md, bool merged_zij, uint32_t merged_serial,
+ uint32_t merged_serial_to, uint32_t original_serial_to)
+{
+ md->flushed_upto = merged_serial_to;
+ if ((md->flags & JOURNAL_MERGED_SERIAL_VALID)) {
+ assert(!merged_zij);
+ assert(merged_serial == md->merged_serial);
+ } else if (!merged_zij) {
+ md->merged_serial = merged_serial;
+ md->flags |= JOURNAL_MERGED_SERIAL_VALID;
+ assert(merged_serial == md->first_serial);
+ journal_metadata_after_delete(md, original_serial_to, 1); // the merged changeset writes itself instead of first one
+ }
+}
+
+void journal_metadata_after_insert(journal_metadata_t *md, uint32_t serial, uint32_t serial_to)
+{
+ if (md->first_serial == md->serial_to) { // no changesets yet
+ md->first_serial = serial;
+ md->flushed_upto = serial;
+ }
+ md->serial_to = serial_to;
+ md->flags |= JOURNAL_SERIAL_TO_VALID;
+ md->changeset_count++;
+}
+
+void journal_metadata_after_extra(journal_metadata_t *md, uint32_t serial, uint32_t serial_to)
+{
+ assert(!(md->flags & JOURNAL_MERGED_SERIAL_VALID));
+ md->merged_serial = serial;
+ md->flushed_upto = serial_to;
+ md->flags |= (JOURNAL_MERGED_SERIAL_VALID | JOURNAL_LAST_FLUSHED_VALID);
+}
+
+void journal_del_zone_txn(knot_lmdb_txn_t *txn, const knot_dname_t *zone)
+{
+ uint64_t md_occupied = 0;
+ (void)get_metadata64(txn, zone, "occupied", &md_occupied);
+ journal_del_zone(txn, zone);
+ set_metadata(txn, zone, "occupied", &md_occupied, sizeof(md_occupied), true);
+}
+
+int journal_scrape_with_md(zone_journal_t j, bool check_existence)
+{
+ if (check_existence && !journal_is_existing(j)) {
+ return KNOT_EOK;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(j.db, &txn, true);
+
+ update_last_inserter(&txn, NULL);
+ journal_del_zone(&txn, j.zone);
+
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int journal_copy_with_md(knot_lmdb_db_t *from, knot_lmdb_db_t *to, const knot_dname_t *zone)
+{
+ knot_lmdb_txn_t tr = { 0 }, tw = { 0 };
+ tr.ret = knot_lmdb_open(from);
+ tw.ret = knot_lmdb_open(to);
+ if (tr.ret != KNOT_EOK || tw.ret != KNOT_EOK) {
+ goto done;
+ }
+ knot_lmdb_begin(from, &tr, true);
+ knot_lmdb_begin(to, &tw, true);
+ update_last_inserter(&tr, NULL);
+ MDB_val prefix = journal_zone_prefix(zone);
+ knot_lmdb_copy_prefix(&tr, &tw, &prefix);
+ free(prefix.mv_data);
+ knot_lmdb_commit(&tw);
+ knot_lmdb_commit(&tr);
+done:
+ return tr.ret == KNOT_EOK ? tw.ret : tr.ret;
+}
+
+int journal_set_flushed(zone_journal_t j)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ journal_metadata_t md = { 0 };
+ knot_lmdb_begin(j.db, &txn, true);
+ journal_load_metadata(&txn, j.zone, &md);
+
+ md.flushed_upto = md.serial_to;
+
+ journal_store_metadata(&txn, j.zone, &md);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int journal_info(zone_journal_t j, bool *exists, uint32_t *first_serial, bool *has_zij,
+ uint32_t *serial_to, bool *has_merged, uint32_t *merged_serial,
+ uint64_t *occupied, uint64_t *occupied_total)
+{
+ if (knot_lmdb_exists(j.db) == KNOT_ENODB) {
+ *exists = false;
+ return KNOT_EOK;
+ }
+ int ret = knot_lmdb_open(j.db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ journal_metadata_t md = { 0 };
+ knot_lmdb_begin(j.db, &txn, false);
+ journal_load_metadata(&txn, j.zone, &md);
+ *exists = (md.flags & JOURNAL_SERIAL_TO_VALID);
+ if (first_serial != NULL) {
+ *first_serial = md.first_serial;
+ }
+ if (has_zij != NULL) {
+ *has_zij = journal_contains(&txn, true, 0, j.zone);
+ }
+ if (serial_to != NULL) {
+ *serial_to = md.serial_to;
+ }
+ if (has_merged != NULL) {
+ *has_merged = (md.flags & JOURNAL_MERGED_SERIAL_VALID);
+ }
+ if (merged_serial != NULL) {
+ *merged_serial = md.merged_serial;
+ }
+ if (occupied != NULL) {
+ *occupied = 0;
+ get_metadata64(&txn, j.zone, "occupied", occupied);
+
+ if (get_metadata(&txn, NULL, "last_inserter_zone") &&
+ knot_dname_is_equal(j.zone, txn.cur_val.mv_data)) {
+ *occupied = MAX(0, (int64_t)*occupied + last_occupied_diff(&txn));
+ }
+ }
+ if (occupied_total != NULL) {
+ *occupied_total = knot_lmdb_usage(&txn);
+ }
+ knot_lmdb_abort(&txn);
+ return txn.ret;
+}
+
+int journals_walk(knot_lmdb_db_t *db, journals_walk_cb_t cb, void *ctx)
+{
+ int ret = knot_lmdb_exists(db);
+ if (ret == KNOT_EOK) {
+ ret = knot_lmdb_open(db);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ knot_dname_storage_t search_data = { 0 };
+ MDB_val search = { 1, search_data };
+ while (knot_lmdb_find(&txn, &search, KNOT_LMDB_GEQ)) {
+ knot_dname_t *found = txn.cur_key.mv_data;
+ uint32_t unused_flags;
+ if (get_metadata32(&txn, found, "flags", &unused_flags)) {
+ // matched journal DB key appears to be a zone name
+ txn.ret = cb(found, ctx);
+ }
+
+ // update searched key to next after found zone
+ search.mv_size = knot_dname_size(found);
+ memcpy(search.mv_data, found, search.mv_size);
+ ((uint8_t *)search.mv_data)[search.mv_size - 1]++;
+ }
+ knot_lmdb_abort(&txn);
+ return txn.ret;
+}
diff --git a/src/knot/journal/journal_metadata.h b/src/knot/journal/journal_metadata.h
new file mode 100644
index 0000000..246d899
--- /dev/null
+++ b/src/knot/journal/journal_metadata.h
@@ -0,0 +1,187 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include "knot/journal/journal_basic.h"
+
+typedef struct {
+ uint32_t first_serial;
+ uint32_t serial_to;
+ uint32_t flushed_upto;
+ uint32_t merged_serial;
+ uint32_t changeset_count;
+ uint32_t flags; // a bitmap of flags, see enum below
+ bool _new_zone; // private: if there were no metadata at all previously
+} journal_metadata_t;
+
+enum journal_metadata_flags {
+ JOURNAL_LAST_FLUSHED_VALID = (1 << 0), // deprecated
+ JOURNAL_SERIAL_TO_VALID = (1 << 1),
+ JOURNAL_MERGED_SERIAL_VALID = (1 << 2),
+};
+
+typedef int (*journals_walk_cb_t)(const knot_dname_t *zone, void *ctx);
+
+/*!
+ * \brief Update the computation of DB resources used by each zone.
+ *
+ * Because the amount of used space is bigger than sum of changesets' serialized_sizes,
+ * journal uses a complicated way to compute each zone's used space: there is a metadata
+ * showing always the previously-inserting zone. Before the next insert, it is computed
+ * how the total usage of the DB changed during the previous insert (or delete), and the
+ * usage increase (or decrease) is accounted on the bill of the previous inserter.
+ *
+ * \param txn Journal DB transaction.
+ * \param new_inserter Name of the zone that is going to insert now. Might be NULL if no insert nor delete will be done.
+ */
+void update_last_inserter(knot_lmdb_txn_t *txn, const knot_dname_t *new_inserter);
+
+/* \brief Return the journal database usage by given zone. */
+uint64_t journal_get_occupied(knot_lmdb_txn_t *txn, const knot_dname_t *zone);
+
+/*!
+ * \brief Load the metadata from DB into structure.
+ *
+ * \param txn Journal DB transaction.
+ * \param zone Zone name.
+ * \param md Output: metadata structure.
+ */
+void journal_load_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, journal_metadata_t *md);
+
+/*!
+ * \brief Store the metadata from structure into DB.
+ *
+ * \param txn Journal DB transaction.
+ * \param zone Zone name.
+ * \param md Metadata structure.
+ */
+void journal_store_metadata(knot_lmdb_txn_t *txn, const knot_dname_t *zone, const journal_metadata_t *md);
+
+/*!
+ * \brief Update metadata according to what was deleted.
+ *
+ * \param md Metadata structure to be updated.
+ * \param deleted_upto Serial-to of the last deleted changeset.
+ * \param deleted_count Number of deleted changesets.
+ */
+void journal_metadata_after_delete(journal_metadata_t *md, uint32_t deleted_upto,
+ size_t deleted_count);
+
+/*!
+ * \brief Update metadata according to what was merged.
+ *
+ * \param md Metadata structure to be updated.
+ * \param merged_zij True if it was a merge into zone-in-journal.
+ * \param merged_serial Serial-from of the merged changeset (ignored if 'merged_zij').
+ * \param merged_serial_to Serial-to of the merged changeset.
+ * \param original_serial_to Previous serial-to of the merged changeset before the merge.
+ */
+void journal_metadata_after_merge(journal_metadata_t *md, bool merged_zij, uint32_t merged_serial,
+ uint32_t merged_serial_to, uint32_t original_serial_to);
+
+/*!
+ * \brief Update metadata according to what was inserted.
+ *
+ * \param md Metadata structure to be updated.
+ * \param serial Serial-from of the inserted changeset.
+ * \param serial_to Serial-to of the inserted changeset.
+ */
+void journal_metadata_after_insert(journal_metadata_t *md, uint32_t serial, uint32_t serial_to);
+
+/*!
+ * \brief Update metadata according to inserted extra changeset.
+ *
+ * \param md Metadata structure to be updated.
+ * \param serial Serial-from of the inserted changeset.
+ * \param serial_to Serial-to of the inserted changeset.
+ */
+void journal_metadata_after_extra(journal_metadata_t *md, uint32_t serial, uint32_t serial_to);
+
+/*!
+ * \brief Delete all zone records in a txn that will later write to the same zone.
+ *
+ * \note The difference against journal_del_zone(), which purges even metadata, incl "occupied".
+ * \note This preserves keeping track of space occupied/freed by this zone.
+ */
+void journal_del_zone_txn(knot_lmdb_txn_t *txn, const knot_dname_t *zone);
+
+/*!
+ * \brief Completely delete all journal records belonging to this zone, including metadata.
+ *
+ * \param j Journal to be scraped.
+ * \param check_existence Don't operate if the journal seems not to exist.
+ *
+ * \return KNOT_E*
+ */
+int journal_scrape_with_md(zone_journal_t j, bool check_existence);
+
+/*!
+ * \brief Copy all records related to this zone from one journal DB to another.
+ *
+ * \param from DB to copy from.
+ * \param to DB to copy to.
+ * \param zone Journal zone.
+ *
+ * \return KNOT_E*
+ */
+int journal_copy_with_md(knot_lmdb_db_t *from, knot_lmdb_db_t *to, const knot_dname_t *zone);
+
+/*!
+ * \brief Update the metadata stored in journal DB after a zone flush.
+ *
+ * \param j Journal to be notified about flush.
+ *
+ * \return KNOT_E*
+ */
+int journal_set_flushed(zone_journal_t j);
+
+/*!
+ * \brief Obtain information about the zone's journal from the DB (mostly metadata).
+ *
+ * \param j Zone journal.
+ * \param exists Output: bool if the zone exists in the journal.
+ * \param first_serial Optional output: serial-from of the first changeset in journal.
+ * \param has_zij Optional output: bool if there is zone-in-journal.
+ * \param serial_to Optional output: serial.to of the last changeset in journal.
+ * \param has_merged Optional output: bool if there is a special (non zone-in-journal) merged changeset.
+ * \param merged_serial Optional output: serial-from of the merged changeset.
+ * \param occupied Optional output: DB space occupied by this zones.
+ * \param occupied_total Optional output: DB space occupied in total by all zones.
+ *
+ * \return KNOT_E*
+ */
+int journal_info(zone_journal_t j, bool *exists, uint32_t *first_serial, bool *has_zij,
+ uint32_t *serial_to, bool *has_merged, uint32_t *merged_serial,
+ uint64_t *occupied, uint64_t *occupied_total);
+
+/*! \brief Return true if this zone exists in journal DB. */
+inline static bool journal_is_existing(zone_journal_t j) {
+ bool ex = false;
+ (void)journal_info(j, &ex, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+ return ex;
+}
+
+/*!
+ * \brief Call a function for each zone being in the journal DB.
+ *
+ * \param db Journal database.
+ * \param cb Callback to be called for each zone-name found.
+ * \param ctx Arbitrary context to be passed to the callback.
+ *
+ * \return An error code from either journal operations or from the callback.
+ */
+int journals_walk(knot_lmdb_db_t *db, journals_walk_cb_t cb, void *ctx);
diff --git a/src/knot/journal/journal_read.c b/src/knot/journal/journal_read.c
new file mode 100644
index 0000000..6c4fc32
--- /dev/null
+++ b/src/knot/journal/journal_read.c
@@ -0,0 +1,436 @@
+/* Copyright (C) 2021 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 "knot/journal/journal_read.h"
+
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/knot_lmdb.h"
+
+#include "contrib/macros.h"
+#include "contrib/ucw/lists.h"
+#include "contrib/wire_ctx.h"
+#include "libknot/error.h"
+
+#include <stdlib.h>
+
+struct journal_read {
+ knot_lmdb_txn_t txn;
+ MDB_val key_prefix;
+ const knot_dname_t *zone;
+ wire_ctx_t wire;
+ uint32_t next;
+};
+
+int journal_read_get_error(const journal_read_t *ctx, int another_error)
+{
+ return (ctx == NULL || ctx->txn.ret == KNOT_EOK ? another_error : ctx->txn.ret);
+}
+
+static void update_ctx_wire(journal_read_t *ctx)
+{
+ ctx->wire = wire_ctx_init_const(ctx->txn.cur_val.mv_data, ctx->txn.cur_val.mv_size);
+ wire_ctx_skip(&ctx->wire, JOURNAL_HEADER_SIZE);
+}
+
+static bool go_next_changeset(journal_read_t *ctx, bool go_zone, const knot_dname_t *zone)
+{
+ free(ctx->key_prefix.mv_data);
+ ctx->key_prefix = journal_changeset_id_to_key(go_zone, ctx->next, zone);
+ if (!knot_lmdb_find_prefix(&ctx->txn, &ctx->key_prefix)) {
+ return false;
+ }
+ if (!go_zone && ctx->next == journal_next_serial(&ctx->txn.cur_val)) {
+ ctx->txn.ret = KNOT_ELOOP;
+ return false;
+ }
+ ctx->next = journal_next_serial(&ctx->txn.cur_val);
+ update_ctx_wire(ctx);
+ return true;
+}
+
+int journal_read_begin(zone_journal_t j, bool read_zone, uint32_t serial_from, journal_read_t **ctx)
+{
+ *ctx = NULL;
+ if (!journal_is_existing(j)) { // this also opens the LMDB if not already
+ return KNOT_ENOENT;
+ }
+
+ journal_read_t *newctx = calloc(1, sizeof(*newctx));
+ if (newctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ newctx->zone = j.zone;
+ newctx->next = serial_from;
+
+ knot_lmdb_begin(j.db, &newctx->txn, false);
+
+ if (go_next_changeset(newctx, read_zone, j.zone)) {
+ *ctx = newctx;
+ return KNOT_EOK;
+ } else {
+ journal_read_end(newctx);
+ return KNOT_ENOENT;
+ }
+}
+
+void journal_read_end(journal_read_t *ctx)
+{
+ if (ctx != NULL) {
+ free(ctx->key_prefix.mv_data);
+ knot_lmdb_abort(&ctx->txn);
+ free(ctx);
+ }
+}
+
+static bool make_data_available(journal_read_t *ctx)
+{
+ if (wire_ctx_available(&ctx->wire) == 0) {
+ if (!knot_lmdb_next(&ctx->txn)) {
+ return false;
+ }
+ if (!knot_lmdb_is_prefix_of(&ctx->key_prefix, &ctx->txn.cur_key)) {
+ return false;
+ }
+ if (ctx->next != journal_next_serial(&ctx->txn.cur_val)) {
+ // consistency check, see also MR !1270
+ ctx->txn.ret = KNOT_EMALF;
+ return false;
+ }
+ update_ctx_wire(ctx);
+ }
+ return true;
+}
+
+// thoughts for next design of journal serialization:
+// - one TTL per rrset
+// - endian
+// - optionally storing whole rdataset at once?
+
+bool journal_read_rrset(journal_read_t *ctx, knot_rrset_t *rrset, bool allow_next_changeset)
+{
+ //knot_rdataset_clear(&rrset->rrs, NULL);
+ //memset(rrset, 0, sizeof(*rrset));
+ if (!make_data_available(ctx)) {
+ if (!allow_next_changeset || !go_next_changeset(ctx, false, ctx->zone)) {
+ return false;
+ }
+ }
+ rrset->owner = knot_dname_copy(ctx->wire.position, NULL);
+ wire_ctx_skip(&ctx->wire, knot_dname_size(rrset->owner));
+ rrset->type = wire_ctx_read_u16(&ctx->wire);
+ rrset->rclass = wire_ctx_read_u16(&ctx->wire);
+ uint16_t rrs_count = wire_ctx_read_u16(&ctx->wire);
+ for (int i = 0; i < rrs_count && ctx->wire.error == KNOT_EOK; i++) {
+ if (!make_data_available(ctx)) {
+ ctx->wire.error = KNOT_EFEWDATA;
+ }
+ // TODO think of how to export serialized rr directly to knot_rdataset_add
+ // focus on: even address aligning
+ uint32_t ttl = wire_ctx_read_u32(&ctx->wire);
+ if (i == 0) {
+ rrset->ttl = ttl;
+ }
+ uint16_t len = wire_ctx_read_u16(&ctx->wire);
+ if (ctx->wire.error == KNOT_EOK) {
+ ctx->wire.error = knot_rrset_add_rdata(rrset, ctx->wire.position, len, NULL);
+ }
+ wire_ctx_skip(&ctx->wire, len);
+ }
+ if (ctx->txn.ret == KNOT_EOK) {
+ ctx->txn.ret = ctx->wire.error == KNOT_ERANGE ? KNOT_EMALF : ctx->wire.error;
+ }
+ if (ctx->txn.ret == KNOT_EOK) {
+ return true;
+ } else {
+ journal_read_clear_rrset(rrset);
+ return false;
+ }
+}
+
+void journal_read_clear_rrset(knot_rrset_t *rr)
+{
+ knot_rrset_clear(rr, NULL);
+}
+
+int journal_read_rrsets(journal_read_t *read, journal_read_cb_t cb, void *ctx)
+{
+ knot_rrset_t rr = { 0 };
+ bool in_remove_section = false;
+ int ret = KNOT_EOK;
+ while (ret == KNOT_EOK && journal_read_rrset(read, &rr, true)) {
+ if (rr_is_apex_soa(&rr, read->zone)) {
+ in_remove_section = !in_remove_section;
+ }
+ ret = cb(in_remove_section, &rr, ctx);
+ journal_read_clear_rrset(&rr);
+ }
+ ret = journal_read_get_error(read, ret);
+ journal_read_end(read);
+ return ret;
+}
+
+static int add_rr_to_contents(zone_contents_t *z, const knot_rrset_t *rrset)
+{
+ zone_node_t *n = NULL;
+ return zone_contents_add_rr(z, rrset, &n);
+ // Shall we ignore ETTL ?
+}
+
+bool journal_read_changeset(journal_read_t *ctx, changeset_t *ch)
+{
+ zone_contents_t *tree = zone_contents_new(ctx->zone, false);
+ knot_rrset_t *soa = calloc(1, sizeof(*soa)), rr = { 0 };
+ if (tree == NULL || soa == NULL) {
+ ctx->txn.ret = KNOT_ENOMEM;
+ goto fail;
+ }
+ memset(ch, 0, sizeof(*ch));
+
+ if (!journal_read_rrset(ctx, soa, true)) {
+ goto fail;
+ }
+ while (journal_read_rrset(ctx, &rr, false)) {
+ if (rr_is_apex_soa(&rr, ctx->zone)) {
+ if (ch->soa_from != NULL) {
+ ctx->txn.ret = KNOT_EMALF;
+ goto fail;
+ }
+ ch->soa_from = soa;
+ ch->remove = tree;
+ soa = malloc(sizeof(*soa));
+ tree = zone_contents_new(ctx->zone, false);
+ if (tree == NULL || soa == NULL) {
+ ctx->txn.ret = KNOT_ENOMEM;
+ goto fail;
+ }
+ *soa = rr; // note this tricky assignment
+ memset(&rr, 0, sizeof(rr));
+ } else {
+ ctx->txn.ret = add_rr_to_contents(tree, &rr);
+ journal_read_clear_rrset(&rr);
+ }
+ }
+
+ if (ctx->txn.ret == KNOT_EOK) {
+ ch->soa_to = soa;
+ ch->add = tree;
+ return true;
+ } else {
+fail:
+ journal_read_clear_rrset(&rr);
+ journal_read_clear_rrset(soa);
+ free(soa);
+ changeset_clear(ch);
+ zone_contents_deep_free(tree);
+ return false;
+ }
+}
+
+void journal_read_clear_changeset(changeset_t *ch)
+{
+ changeset_clear(ch);
+ memset(ch, 0, sizeof(*ch));
+}
+
+static int just_load_md(zone_journal_t j, journal_metadata_t *md, bool *has_zij)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(j.db, &txn, false);
+ journal_load_metadata(&txn, j.zone, md);
+ if (has_zij != NULL) {
+ *has_zij = journal_contains(&txn, true, 0, j.zone);
+ }
+ knot_lmdb_abort(&txn);
+ return txn.ret;
+}
+
+int journal_walk_from(zone_journal_t j, uint32_t from,
+ journal_walk_cb_t cb, void *ctx)
+{
+ bool at_least_one = false;
+ journal_metadata_t md = { 0 };
+ journal_read_t *read = NULL;
+ changeset_t ch;
+
+ int ret = just_load_md(j, &md, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if ((md.flags & JOURNAL_SERIAL_TO_VALID) && from != md.serial_to &&
+ ret == KNOT_EOK) {
+ ret = journal_read_begin(j, false, from, &read);
+ while (ret == KNOT_EOK && journal_read_changeset(read, &ch)) {
+ ret = cb(false, &ch, ctx);
+ at_least_one = true;
+ journal_read_clear_changeset(&ch);
+ }
+ ret = journal_read_get_error(read, ret);
+ journal_read_end(read);
+ }
+ if (!at_least_one && ret == KNOT_EOK) {
+ ret = cb(false, NULL, ctx);
+ }
+ return ret;
+}
+
+// beware, this function does not operate in single txn!
+int journal_walk(zone_journal_t j, journal_walk_cb_t cb, void *ctx)
+{
+ int ret = knot_lmdb_exists(j.db);
+ if (ret == KNOT_ENODB) {
+ ret = cb(true, NULL, ctx);
+ if (ret == KNOT_EOK) {
+ ret = cb(false, NULL, ctx);
+ }
+ return ret;
+ } else if (ret == KNOT_EOK) {
+ ret = knot_lmdb_open(j.db);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ journal_metadata_t md = { 0 };
+ journal_read_t *read = NULL;
+ changeset_t ch;
+ bool zone_in_j = false;
+ ret = just_load_md(j, &md, &zone_in_j);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (zone_in_j) {
+ ret = journal_read_begin(j, true, 0, &read);
+ goto read_one_special;
+ } else if ((md.flags & JOURNAL_MERGED_SERIAL_VALID)) {
+ ret = journal_read_begin(j, false, md.merged_serial, &read);
+read_one_special:
+ if (ret == KNOT_EOK && journal_read_changeset(read, &ch)) {
+ ret = cb(true, &ch, ctx);
+ journal_read_clear_changeset(&ch);
+ }
+ ret = journal_read_get_error(read, ret);
+ journal_read_end(read);
+ read = NULL;
+ } else {
+ ret = cb(true, NULL, ctx);
+ }
+
+ if (ret == KNOT_EOK) {
+ ret = journal_walk_from(j, md.first_serial, cb, ctx);
+ }
+ return ret;
+}
+
+typedef struct {
+ size_t observed_count;
+ size_t observed_merged;
+ uint32_t merged_serial;
+ size_t observed_zij;
+ uint32_t first_serial;
+ bool first_serial_valid;
+ uint32_t last_serial;
+ bool last_serial_valid;
+} check_ctx_t;
+
+static int check_cb(bool special, const changeset_t *ch, void *vctx)
+{
+ check_ctx_t *ctx = vctx;
+ if (special && ch != NULL) {
+ if (ch->remove == NULL) {
+ ctx->observed_zij++;
+ ctx->last_serial = changeset_to(ch);
+ ctx->last_serial_valid = true;
+ } else {
+ ctx->merged_serial = changeset_from(ch);
+ ctx->observed_merged++;
+ }
+ } else if (ch != NULL) {
+ if (!ctx->first_serial_valid) {
+ ctx->first_serial = changeset_from(ch);
+ ctx->first_serial_valid = true;
+ }
+ ctx->last_serial = changeset_to(ch);
+ ctx->last_serial_valid = true;
+ ctx->observed_count++;
+ }
+ return KNOT_EOK;
+}
+
+static bool eq(bool a, bool b)
+{
+ return a ? b : !b;
+}
+
+int journal_sem_check(zone_journal_t j)
+{
+ check_ctx_t ctx = { 0 };
+ journal_metadata_t md = { 0 };
+ bool has_zij = false;
+
+ if (!journal_is_existing(j)) {
+ return KNOT_EOK;
+ }
+
+ int ret = just_load_md(j, &md, &has_zij);
+ if (ret == KNOT_EOK) {
+ ret = journal_walk(j, check_cb, &ctx);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (!eq((md.flags & JOURNAL_SERIAL_TO_VALID), ctx.last_serial_valid)) {
+ return 101;
+ }
+ if (ctx.last_serial_valid && ctx.last_serial != md.serial_to) {
+ return 102;
+ }
+ if (!eq((md.flags & JOURNAL_MERGED_SERIAL_VALID), (ctx.observed_merged > 0))) {
+ return 103;
+ }
+ if (ctx.observed_merged > 1) {
+ return 104;
+ }
+ if (ctx.observed_merged == 1 && ctx.merged_serial != md.merged_serial) {
+ return 105;
+ }
+ if (!eq(has_zij, (ctx.observed_zij > 0))) {
+ return 106;
+ }
+ if (ctx.observed_zij > 1) {
+ return 107;
+ }
+ if (ctx.observed_zij + ctx.observed_merged > 1) {
+ return 108;
+ }
+ if (!eq(((md.flags & JOURNAL_SERIAL_TO_VALID) && md.first_serial != md.serial_to), ctx.first_serial_valid)) {
+ return 109;
+ }
+ if (!eq(ctx.first_serial_valid, (ctx.observed_count > 0))) {
+ return 110;
+ }
+ if (ctx.first_serial_valid && ctx.first_serial != md.first_serial) {
+ return 111;
+ }
+ if (ctx.observed_count != md.changeset_count) {
+ return 112;
+ }
+ if (ctx.observed_merged > 0 && ctx.observed_count == 0) {
+ return 113;
+ }
+ return KNOT_EOK;
+}
diff --git a/src/knot/journal/journal_read.h b/src/knot/journal/journal_read.h
new file mode 100644
index 0000000..92cad9f
--- /dev/null
+++ b/src/knot/journal/journal_read.h
@@ -0,0 +1,158 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "knot/journal/journal_basic.h"
+
+typedef struct journal_read journal_read_t;
+
+typedef int (*journal_read_cb_t)(bool in_remove_section, const knot_rrset_t *rr, void *ctx);
+
+typedef int (*journal_walk_cb_t)(bool special, const changeset_t *ch, void *ctx);
+
+/*!
+ * \brief Start reading journal from specified changeset.
+ *
+ * \param j Journal to be read.
+ * \param read_zone True if reading shall start with zone-in-journal.
+ * \param serial_from Serial-from of the changeset to be started at (ignored if 'read_zone').
+ * \param ctx Output: journal reading context initialised.
+ *
+ * \return KNOT_E*
+ */
+int journal_read_begin(zone_journal_t j, bool read_zone, uint32_t serial_from, journal_read_t **ctx);
+
+/*!
+ * \brief Read a single RRSet from a journal changeset.
+ *
+ * \param ctx Journal reading context.
+ * \param rr Output: RRSet to be filled with serialized data.
+ * \param allow_next_changeset True to allow jumping to next changeset.
+ *
+ * \return False if no more RRSet in this changeset/journal, or failure.
+ */
+bool journal_read_rrset(journal_read_t *ctx, knot_rrset_t *rr, bool allow_next_changeset);
+
+/*!
+ * \brief Free up heap allocations by journal_read_rrset().
+ *
+ * \param rr RRSet initialised by journal_read_rrset().
+ */
+void journal_read_clear_rrset(knot_rrset_t *rr);
+
+// TODO move somewhere. Libknot?
+inline static bool rr_is_apex_soa(const knot_rrset_t *rr, const knot_dname_t *apex)
+{
+ return (rr->type == KNOT_RRTYPE_SOA && knot_dname_is_equal(rr->owner, apex));
+}
+
+/*!
+ * \brief Read all RRSets up to the end of journal, calling a function for each.
+ *
+ * \note Closes reading context at the end.
+ *
+ * \param read Journal reading context.
+ * \param cb Callback to be called on each read.
+ * \param ctx Arbitrary context to be passed to the callback.
+ *
+ * \return An error code from either journal operations or from the callback.
+ */
+int journal_read_rrsets(journal_read_t *read, journal_read_cb_t cb, void *ctx);
+
+/*!
+ * \brief Read a single changeset from journal.
+ *
+ * \param ctx Journal reading context.
+ * \param ch Output: changeset to be filled with serialized data.
+ *
+ * \return False if no more changesets in the journal, or failure.
+ */
+bool journal_read_changeset(journal_read_t *ctx, changeset_t *ch);
+
+/*!
+ * \brief Free up heap allocations by journal_read_changeset().
+ *
+ * \param ch Changeset initialised by journal_read_changeset().
+ */
+void journal_read_clear_changeset(changeset_t *ch);
+
+/*!
+ * \brief Obtain error code from the journal_read operations previously performed.
+ *
+ * \param ctx Journal reading context.
+ * \param another_error An error code from outside the reading operations to be combined.
+ *
+ * \return KNOT_EOK if completely every operation succeeded, KNOT_E*
+ */
+int journal_read_get_error(const journal_read_t *ctx, int another_error);
+
+/*!
+ * \brief Finalise journal reading.
+ *
+ * \param ctx Journal reading context (will be freed).
+ */
+void journal_read_end(journal_read_t *ctx);
+
+/*!
+ * \brief Call a function for each changeset in journal.
+ *
+ * This is a variant of journal_walk() see below.
+ * The difference is that iteration starts at specified serial.
+ * Similarly to how IXFR works.
+ * The callback is called for each found changeset, or just once
+ * with ch=NULL if none is found.
+ *
+ * \param j Zone journal to be read.
+ * \param from SOA serial to start at.
+ * \param cb Callback to be called for each changeset (or its non-existence).
+ * \param ctx Arbitrary context to be passed to the callback.
+ *
+ * \return An error code from either journal operations or from the callback.
+ * \retval KNOT_ENOENT if the journal is not empty, but the requested serial not present.
+ */
+int journal_walk_from(zone_journal_t j, uint32_t from,
+ journal_walk_cb_t cb, void *ctx);
+
+/*!
+ * \brief Call a function for each changeset stored in journal.
+ *
+ * First, the callback will be called for the special changeset -
+ * either zone-in-journal or merged changeset, with special=true.
+ * If there is no such, it will be called anyway with ch=NULL.
+ *
+ * Than, the callback will be called for each regular changeset
+ * with special=false. If there is none, it will be called once
+ * with ch=NULL.
+ *
+ * \param j Zone journal to be read.
+ * \param cb Callback to be called for each changeset (or its non-existence).
+ * \param ctx Arbitrary context to be passed to the callback.
+ *
+ * \return An error code from either journal operations or from the callback.
+ */
+int journal_walk(zone_journal_t j, journal_walk_cb_t cb, void *ctx);
+
+/*!
+ * \brief Perform semantic check of the zone journal (consistency, metadata...).
+ *
+ * \param j Zone journal to be checked.
+ *
+ * \retval KNOT_E* ( < 0 ) if an error during journal operation.
+ * \retval > 100 if some inconsistency found.
+ * \return KNOT_EOK of all ok.
+ */
+int journal_sem_check(zone_journal_t j);
diff --git a/src/knot/journal/journal_write.c b/src/knot/journal/journal_write.c
new file mode 100644
index 0000000..ad1247b
--- /dev/null
+++ b/src/knot/journal/journal_write.c
@@ -0,0 +1,333 @@
+/* Copyright (C) 2022 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 "knot/journal/journal_write.h"
+
+#include "contrib/macros.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/journal_read.h"
+#include "knot/journal/serialization.h"
+#include "libknot/error.h"
+
+static void journal_write_serialize(knot_lmdb_txn_t *txn, serialize_ctx_t *ser,
+ const knot_dname_t *apex, bool zij, uint32_t ch_from, uint32_t ch_to)
+{
+ MDB_val chunk;
+ uint32_t i = 0;
+ while (serialize_unfinished(ser) && txn->ret == KNOT_EOK) {
+ serialize_prepare(ser, JOURNAL_CHUNK_THRESH - JOURNAL_HEADER_SIZE,
+ JOURNAL_CHUNK_MAX - JOURNAL_HEADER_SIZE, &chunk.mv_size);
+ if (chunk.mv_size == 0) {
+ break; // beware! If this is omitted, it creates empty chunk => EMALF when reading.
+ }
+ chunk.mv_size += JOURNAL_HEADER_SIZE;
+ chunk.mv_data = NULL;
+ MDB_val key = journal_make_chunk_key(apex, ch_from, zij, i);
+ if (knot_lmdb_insert(txn, &key, &chunk)) {
+ journal_make_header(chunk.mv_data, ch_to);
+ serialize_chunk(ser, chunk.mv_data + JOURNAL_HEADER_SIZE, chunk.mv_size - JOURNAL_HEADER_SIZE);
+ }
+ free(key.mv_data);
+ i++;
+ }
+ int ret = serialize_deinit(ser);
+ if (txn->ret == KNOT_EOK) {
+ txn->ret = ret;
+ }
+}
+
+void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch)
+{
+ serialize_ctx_t *ser = serialize_init(ch);
+ if (ser == NULL) {
+ txn->ret = KNOT_ENOMEM;
+ return;
+ }
+ if (ch->remove == NULL) {
+ journal_write_serialize(txn, ser, ch->soa_to->owner, true, 0, changeset_to(ch));
+ } else {
+ journal_write_serialize(txn, ser, ch->soa_to->owner, false, changeset_from(ch), changeset_to(ch));
+ }
+}
+
+void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z)
+{
+ serialize_ctx_t *ser = serialize_zone_init(z);
+ if (ser == NULL) {
+ txn->ret = KNOT_ENOMEM;
+ return;
+ }
+ journal_write_serialize(txn, ser, z->apex->owner, true, 0, zone_contents_serial(z));
+}
+
+void journal_write_zone_diff(knot_lmdb_txn_t *txn, const zone_diff_t *z)
+{
+ serialize_ctx_t *ser = serialize_zone_diff_init(z);
+ if (ser == NULL) {
+ txn->ret = KNOT_ENOMEM;
+ return;
+ }
+ journal_write_serialize(txn, ser, z->apex->owner, false, zone_diff_from(z), zone_diff_to(z));
+}
+
+static bool delete_one(knot_lmdb_txn_t *txn, bool del_zij, uint32_t del_serial,
+ const knot_dname_t *zone, uint64_t *freed, uint32_t *next_serial)
+{
+ *freed = 0;
+ MDB_val prefix = journal_changeset_id_to_key(del_zij, del_serial, zone);
+ knot_lmdb_foreach(txn, &prefix) {
+ *freed += txn->cur_val.mv_size;
+ *next_serial = journal_next_serial(&txn->cur_val);
+ knot_lmdb_del_cur(txn);
+ }
+ free(prefix.mv_data);
+ return (*freed > 0);
+}
+
+static int merge_cb(bool remove, const knot_rrset_t *rr, void *ctx)
+{
+ changeset_t *ch = ctx;
+ return remove ? (rr_is_apex_soa(rr, ch->soa_to->owner) ?
+ KNOT_EOK : changeset_add_removal(ch, rr, CHANGESET_CHECK))
+ : changeset_add_addition(ch, rr, CHANGESET_CHECK);
+}
+
+void journal_merge(zone_journal_t j, knot_lmdb_txn_t *txn, bool merge_zij,
+ uint32_t merge_serial, uint32_t *original_serial_to)
+{
+ changeset_t merge;
+ memset(&merge, 0, sizeof(merge));
+ journal_read_t *read = NULL;
+ txn->ret = journal_read_begin(j, merge_zij, merge_serial, &read);
+ if (txn->ret != KNOT_EOK) {
+ return;
+ }
+ if (journal_read_changeset(read, &merge)) {
+ *original_serial_to = changeset_to(&merge);
+ }
+ txn->ret = journal_read_rrsets(read, merge_cb, &merge);
+
+ // deleting seems redundant since the merge changeset will be overwritten
+ // but it would cause EMALF or invalid data if the new merged has less chunks than before
+ uint32_t del_next_serial;
+ uint64_t del_freed;
+ delete_one(txn, merge_zij, merge_serial, j.zone, &del_freed, &del_next_serial);
+ assert(del_freed > 0 && del_next_serial == *original_serial_to);
+
+ journal_write_changeset(txn, &merge);
+ journal_read_clear_changeset(&merge);
+}
+
+static void delete_merged(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ journal_metadata_t *md, uint64_t *freed)
+{
+ if (!(md->flags & JOURNAL_MERGED_SERIAL_VALID)) {
+ return;
+ }
+ uint32_t unused = 0;
+ delete_one(txn, false, md->merged_serial, zone, freed, &unused);
+ md->merged_serial = 0;
+ md->flags &= ~JOURNAL_MERGED_SERIAL_VALID;
+}
+
+bool journal_delete(knot_lmdb_txn_t *txn, uint32_t from, const knot_dname_t *zone,
+ uint64_t tofree_size, size_t tofree_count, uint32_t stop_at_serial,
+ uint64_t *freed_size, size_t *freed_count, uint32_t *stopped_at)
+{
+ *freed_size = 0;
+ *freed_count = 0;
+ uint64_t freed_now;
+ while (from != stop_at_serial &&
+ (*freed_size < tofree_size || *freed_count < tofree_count) &&
+ delete_one(txn, false, from, zone, &freed_now, stopped_at)) {
+ *freed_size += freed_now;
+ ++(*freed_count);
+ from = *stopped_at;
+ }
+ return (*freed_count > 0);
+}
+
+void journal_try_flush(zone_journal_t j, knot_lmdb_txn_t *txn, journal_metadata_t *md)
+{
+ bool flush = journal_allow_flush(j);
+ uint32_t merge_orig = 0;
+ if (journal_contains(txn, true, 0, j.zone)) {
+ journal_merge(j, txn, true, 0, &merge_orig);
+ if (!flush) {
+ journal_metadata_after_merge(md, true, 0, md->serial_to, merge_orig);
+ }
+ } else if (!flush) {
+ uint32_t merge_serial = ((md->flags & JOURNAL_MERGED_SERIAL_VALID) ? md->merged_serial : md->first_serial);
+ journal_merge(j, txn, false, merge_serial, &merge_orig);
+ journal_metadata_after_merge(md, false, merge_serial, md->serial_to, merge_orig);
+ }
+
+ if (flush) {
+ // delete merged serial if (very unlikely) exists
+ if ((md->flags & JOURNAL_MERGED_SERIAL_VALID)) {
+ uint64_t unused64;
+ uint32_t unused32;
+ (void)delete_one(txn, false, md->merged_serial, j.zone, &unused64, &unused32);
+ md->flags &= ~JOURNAL_MERGED_SERIAL_VALID;
+ }
+
+ // commit partial job and ask zone to flush itself
+ journal_store_metadata(txn, j.zone, md);
+ knot_lmdb_commit(txn);
+ if (txn->ret == KNOT_EOK) {
+ txn->ret = KNOT_EBUSY;
+ }
+ }
+}
+
+#define U_MINUS(minuend, subtrahend) ((minuend) - MIN((minuend), (subtrahend)))
+
+void journal_fix_occupation(zone_journal_t j, knot_lmdb_txn_t *txn, journal_metadata_t *md,
+ int64_t max_usage, ssize_t max_count)
+{
+ uint64_t occupied = journal_get_occupied(txn, j.zone), freed;
+ uint64_t need_tofree = U_MINUS(occupied, max_usage);
+ size_t count = md->changeset_count, removed;
+ size_t need_todel = U_MINUS(count, max_count);
+
+ while ((need_tofree > 0 || need_todel > 0) && txn->ret == KNOT_EOK) {
+ uint32_t del_from = md->first_serial; // don't move this line outside of the loop
+ uint32_t del_upto = md->flushed_upto;
+ (void)journal_serial_to(txn, true, 0, j.zone, &del_upto); // in case zij present and wrong flushed_upto, avoid discontinuity
+ freed = 0;
+ removed = 0;
+ journal_delete(txn, del_from, j.zone, need_tofree, need_todel,
+ del_upto, &freed, &removed, &del_from);
+ if (freed == 0) {
+ if (del_upto != md->serial_to) {
+ journal_try_flush(j, txn, md);
+ } else {
+ txn->ret = KNOT_ESPACE;
+ break;
+ }
+ } else {
+ journal_metadata_after_delete(md, del_from, removed);
+ need_tofree = U_MINUS(need_tofree, freed);
+ need_todel = U_MINUS(need_todel, removed);
+ }
+ }
+}
+
+int journal_insert_zone(zone_journal_t j, const zone_contents_t *z)
+{
+ changeset_t fake_ch = { .add = (zone_contents_t *)z };
+ size_t ch_size = changeset_serialized_size(&fake_ch);
+ size_t max_usage = journal_conf_max_usage(j);
+ if (ch_size >= max_usage) {
+ return KNOT_ESPACE;
+ }
+ int ret = knot_lmdb_open(j.db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(j.db, &txn, true);
+
+ update_last_inserter(&txn, j.zone);
+ journal_del_zone_txn(&txn, j.zone);
+
+ journal_write_zone(&txn, z);
+
+ journal_metadata_t md = { 0 };
+ md.flags = JOURNAL_SERIAL_TO_VALID;
+ md.serial_to = zone_contents_serial(z);
+ md.first_serial = md.serial_to;
+ journal_store_metadata(&txn, j.zone, &md);
+
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra,
+ const zone_diff_t *zdiff)
+{
+ assert(zdiff == NULL || (ch == NULL && extra == NULL));
+
+ size_t ch_size = zdiff == NULL ? changeset_serialized_size(ch) :
+ zone_diff_serialized_size(*zdiff);
+ size_t max_usage = journal_conf_max_usage(j);
+ if (ch_size >= max_usage) {
+ return KNOT_ESPACE;
+ }
+
+ uint32_t ch_from = zdiff == NULL ? changeset_from(ch) : zone_diff_from(zdiff);
+ uint32_t ch_to = zdiff == NULL ? changeset_to(ch) : zone_diff_to(zdiff);
+ if (extra != NULL && (changeset_to(extra) != ch_to ||
+ changeset_from(extra) == ch_from)) {
+ return KNOT_EINVAL;
+ }
+ int ret = knot_lmdb_open(j.db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ journal_metadata_t md = { 0 };
+ knot_lmdb_begin(j.db, &txn, true);
+ journal_load_metadata(&txn, j.zone, &md);
+
+ update_last_inserter(&txn, j.zone);
+
+ if (extra != NULL) {
+ if (journal_contains(&txn, true, 0, j.zone)) {
+ txn.ret = KNOT_ESEMCHECK;
+ }
+ uint64_t merged_freed = 0;
+ delete_merged(&txn, j.zone, &md, &merged_freed);
+ ch_size += changeset_serialized_size(extra);
+ ch_size -= merged_freed;
+ md.flushed_upto = md.serial_to; // set temporarily
+ md.flags |= JOURNAL_LAST_FLUSHED_VALID;
+ }
+
+ size_t chs_limit = journal_conf_max_changesets(j);
+ journal_fix_occupation(j, &txn, &md, max_usage - ch_size, chs_limit - 1);
+
+ // avoid discontinuity
+ if ((md.flags & JOURNAL_SERIAL_TO_VALID) && md.serial_to != ch_from) {
+ if (journal_contains(&txn, true, 0, j.zone)) {
+ txn.ret = KNOT_ESEMCHECK;
+ } else {
+ journal_del_zone_txn(&txn, j.zone);
+ memset(&md, 0, sizeof(md));
+ }
+ }
+
+ // avoid cycle
+ if (journal_contains(&txn, false, ch_to, j.zone)) {
+ journal_fix_occupation(j, &txn, &md, INT64_MAX, 1);
+ }
+
+ if (zdiff == NULL) {
+ journal_write_changeset(&txn, ch);
+ } else {
+ journal_write_zone_diff(&txn, zdiff);
+ }
+ journal_metadata_after_insert(&md, ch_from, ch_to);
+
+ if (extra != NULL) {
+ journal_write_changeset(&txn, extra);
+ journal_metadata_after_extra(&md, changeset_from(extra), changeset_to(extra));
+ }
+
+ journal_store_metadata(&txn, j.zone, &md);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
diff --git a/src/knot/journal/journal_write.h b/src/knot/journal/journal_write.h
new file mode 100644
index 0000000..a55fd34
--- /dev/null
+++ b/src/knot/journal/journal_write.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/journal/journal_basic.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/serialization.h"
+
+/*!
+ * \brief Serialize a changeset into chunks and write it into DB with no checks and metadata update.
+ *
+ * \param txn Journal DB transaction.
+ * \param ch Changeset to be written.
+ */
+void journal_write_changeset(knot_lmdb_txn_t *txn, const changeset_t *ch);
+
+/*!
+ * \brief Serialize zone contents aka "bootstrap" changeset into journal, no checks.
+ *
+ * \param txn Journal DB transaction.
+ * \param z Zone contents to be written.
+ */
+void journal_write_zone(knot_lmdb_txn_t *txn, const zone_contents_t *z);
+
+/*!
+ * \brief Merge all following changeset into one of journal changeset.
+ *
+ * \param j Zone journal.
+ * \param txn Journal DB transaction.
+ * \param merge_zij True if we shall merge into zone-in-journal.
+ * \param merge_serial Serial-from of the changeset to be merged into (ignored if 'merge_zij').
+ * \param original_serial_to Output: previous serial-to of the merged changeset before merge.
+ *
+ * \note The error code will be in thx->ret.
+ */
+void journal_merge(zone_journal_t j, knot_lmdb_txn_t *txn, bool merge_zij,
+ uint32_t merge_serial, uint32_t *original_serial_to);
+
+/*!
+ * \brief Delete some journal changesets in attempt to fulfill usage quotas.
+ *
+ * \param txn Journal DB transaction.
+ * \param from Serial-from of the first changeset to be deleted.
+ * \param zone Zone name.
+ * \param tofree_size Amount of data (in bytes) to be at least deleted.
+ * \param tofree_count Number of changesets to be at least deleted.
+ * \param stop_at_serial Must not delete the changeset with this serial-from.
+ * \param freed_size Output: amount of data really deleted.
+ * \param freed_count Output: number of changesets really freed.
+ * \param stopped_at Output: serial-to of the last deleted changeset.
+ *
+ * \return True if something was deleted (not necessarily fulfilling tofree_*).
+ */
+bool journal_delete(knot_lmdb_txn_t *txn, uint32_t from, const knot_dname_t *zone,
+ uint64_t tofree_size, size_t tofree_count, uint32_t stop_at_serial,
+ uint64_t *freed_size, size_t *freed_count, uint32_t *stopped_at);
+
+/*!
+ * \brief Perform a merge or zone flush in order to enable deleting more changesets.
+ *
+ * \param j Zone journal.
+ * \param txn Journal DB transaction.
+ * \param md Journal metadata.
+ *
+ * \note It might set txn->ret to KNOT_EBUSY to fail out from this operation and let the zone flush itself.
+ */
+void journal_try_flush(zone_journal_t j, knot_lmdb_txn_t *txn, journal_metadata_t *md);
+
+/*!
+ * \brief Perform delete/merge/flush operations to fulfill configured journal quotas.
+ *
+ * \param j Zone journal.
+ * \param txn Journal DB transaction.
+ * \param md Journal metadata.
+ * \param max_usage Configured maximum usage (in bytes) of journal DB by this zone.
+ * \param max_count Configured maximum number of changesets.
+ */
+void journal_fix_occupation(zone_journal_t j, knot_lmdb_txn_t *txn, journal_metadata_t *md,
+ int64_t max_usage, ssize_t max_count);
+
+/*!
+ * \brief Store zone-in-journal into the journal, update metadata.
+ *
+ * \param j Zone journal.
+ * \param z Zone contents to be stored.
+ *
+ * \return KNOT_E*
+ */
+int journal_insert_zone(zone_journal_t j, const zone_contents_t *z);
+
+/*!
+ * \brief Store changeset into journal, fulfilling quotas and updating metadata.
+ *
+ * \param j Zone journal.
+ * \param ch Changeset to be stored.
+ * \param extra Extra changeset to be stored in the role of merged changeset.
+ * \param zdiff Zone diff to be stored instead of changeset.
+ *
+ * \note The extra changesetis being stored on zone load, it is basically the diff
+ * between zonefile and loaded zone contents. Afterwards, it will be treated
+ * the same like merged changeset. Inserting it requires no zone-in-journal
+ * present and leads to deleting any previous merged changeset.
+ *
+ * \return KNOT_E*
+ */
+int journal_insert(zone_journal_t j, const changeset_t *ch, const changeset_t *extra,
+ const zone_diff_t *zdiff);
diff --git a/src/knot/journal/knot_lmdb.c b/src/knot/journal/knot_lmdb.c
new file mode 100644
index 0000000..bc17462
--- /dev/null
+++ b/src/knot/journal/knot_lmdb.c
@@ -0,0 +1,770 @@
+/* Copyright (C) 2022 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 <stdarg.h>
+#include <stdio.h> // snprintf
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "knot/journal/knot_lmdb.h"
+
+#include "knot/conf/conf.h"
+#include "contrib/files.h"
+#include "contrib/wire_ctx.h"
+#include "libknot/dname.h"
+#include "libknot/endian.h"
+#include "libknot/error.h"
+
+#define LMDB_DIR_MODE 0770
+#define LMDB_FILE_MODE 0660
+
+static void err_to_knot(int *err)
+{
+ switch (*err) {
+ case MDB_SUCCESS:
+ *err = KNOT_EOK;
+ break;
+ case MDB_NOTFOUND:
+ *err = KNOT_ENOENT;
+ break;
+ case MDB_TXN_FULL:
+ *err = KNOT_ELIMIT;
+ break;
+ case MDB_MAP_FULL:
+ case ENOSPC:
+ *err = KNOT_ESPACE;
+ break;
+ default:
+ *err = (*err < 0 ? *err : -*err);
+ }
+}
+
+void knot_lmdb_init(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags, const char *dbname)
+{
+#ifdef __OpenBSD__
+ /*
+ * Enforce that MDB_WRITEMAP is set.
+ *
+ * MDB assumes a unified buffer cache.
+ *
+ * See https://www.openldap.org/pub/hyc/mdm-paper.pdf section 3.1,
+ * references 17, 18, and 19.
+ *
+ * From Howard Chu: "This requirement can be relaxed in the
+ * current version of the library. If you create the environment
+ * with the MDB_WRITEMAP option then all reads and writes are
+ * performed using mmap, so the file buffer cache is irrelevant.
+ * Of course then you lose the protection that the read-only
+ * map offers."
+ */
+ env_flags |= MDB_WRITEMAP;
+#endif
+ db->env = NULL;
+ db->path = strdup(path);
+ db->mapsize = mapsize;
+ db->env_flags = env_flags;
+ db->dbname = dbname;
+ pthread_mutex_init(&db->opening_mutex, NULL);
+ db->maxdbs = 2;
+ db->maxreaders = conf_lmdb_readers(conf());
+}
+
+static int lmdb_stat(const char *lmdb_path, struct stat *st)
+{
+ char data_mdb[strlen(lmdb_path) + 10];
+ (void)snprintf(data_mdb, sizeof(data_mdb), "%s/data.mdb", lmdb_path);
+ if (stat(data_mdb, st) == 0) {
+ return st->st_size > 0 ? KNOT_EOK : KNOT_ENODB;
+ } else if (errno == ENOENT) {
+ return KNOT_ENODB;
+ } else {
+ return knot_map_errno();
+ }
+}
+
+int knot_lmdb_exists(knot_lmdb_db_t *db)
+{
+ if (db->env != NULL) {
+ return KNOT_EOK;
+ }
+ if (db->path == NULL) {
+ return KNOT_ENODB;
+ }
+ struct stat unused;
+ return lmdb_stat(db->path, &unused);
+}
+
+static int fix_mapsize(knot_lmdb_db_t *db)
+{
+ if (db->mapsize == 0) {
+ struct stat st;
+ int ret = lmdb_stat(db->path, &st);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ db->mapsize = st.st_size * 2; // twice the size as DB might grow while we read it
+ db->env_flags |= MDB_RDONLY;
+ }
+ return KNOT_EOK;
+}
+
+size_t knot_lmdb_copy_size(knot_lmdb_db_t *to_copy)
+{
+ size_t copy_size = 1048576;
+ struct stat st;
+ if (lmdb_stat(to_copy->path, &st) == KNOT_EOK) {
+ copy_size += st.st_size * 2;
+ }
+ return copy_size;
+}
+
+static int lmdb_open(knot_lmdb_db_t *db)
+{
+ MDB_txn *init_txn = NULL;
+
+ if (db->env != NULL) {
+ return KNOT_EOK;
+ }
+
+ if (db->path == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = fix_mapsize(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = make_dir(db->path, LMDB_DIR_MODE, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size <= 0) {
+ return KNOT_ERROR;
+ }
+ size_t mapsize = (db->mapsize / page_size + 1) * page_size;
+
+ ret = mdb_env_create(&db->env);
+ if (ret != MDB_SUCCESS) {
+ err_to_knot(&ret);
+ return ret;
+ }
+
+ ret = mdb_env_set_mapsize(db->env, mapsize);
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_env_set_maxdbs(db->env, db->maxdbs);
+ }
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_env_set_maxreaders(db->env, db->maxreaders);
+ }
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_env_open(db->env, db->path, db->env_flags, LMDB_FILE_MODE);
+ }
+ if (ret == MDB_SUCCESS) {
+ unsigned init_txn_flags = (db->env_flags & MDB_RDONLY);
+ ret = mdb_txn_begin(db->env, NULL, init_txn_flags, &init_txn);
+ if (ret == MDB_READERS_FULL) {
+ int cleared = 0;
+ ret = mdb_reader_check(db->env, &cleared);
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_txn_begin(db->env, NULL, init_txn_flags, &init_txn);
+ }
+ }
+ }
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_dbi_open(init_txn, db->dbname, MDB_CREATE, &db->dbi);
+ }
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_txn_commit(init_txn);
+ }
+
+ if (ret != MDB_SUCCESS) {
+ if (init_txn != NULL) {
+ mdb_txn_abort(init_txn);
+ }
+ mdb_env_close(db->env);
+ db->env = NULL;
+ }
+ err_to_knot(&ret);
+ return ret;
+}
+
+int knot_lmdb_open(knot_lmdb_db_t *db)
+{
+ pthread_mutex_lock(&db->opening_mutex);
+ int ret = lmdb_open(db);
+ pthread_mutex_unlock(&db->opening_mutex);
+ return ret;
+}
+
+static void lmdb_close(knot_lmdb_db_t *db)
+{
+ if (db->env != NULL) {
+ mdb_dbi_close(db->env, db->dbi);
+ mdb_env_close(db->env);
+ db->env = NULL;
+ }
+}
+
+void knot_lmdb_close(knot_lmdb_db_t *db)
+{
+ pthread_mutex_lock(&db->opening_mutex);
+ lmdb_close(db);
+ pthread_mutex_unlock(&db->opening_mutex);
+}
+
+static int lmdb_reinit(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags)
+{
+#ifdef __OpenBSD__
+ env_flags |= MDB_WRITEMAP;
+#endif
+ if (strcmp(db->path, path) == 0 && db->mapsize == mapsize && db->env_flags == env_flags) {
+ return KNOT_EOK;
+ }
+ if (db->env != NULL) {
+ return KNOT_EISCONN;
+ }
+ free(db->path);
+ db->path = strdup(path);
+ db->mapsize = mapsize;
+ db->env_flags = env_flags;
+ return KNOT_EOK;
+}
+
+int knot_lmdb_reinit(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags)
+{
+ pthread_mutex_lock(&db->opening_mutex);
+ int ret = lmdb_reinit(db, path, mapsize, env_flags);
+ pthread_mutex_unlock(&db->opening_mutex);
+ return ret;
+}
+
+int knot_lmdb_reconfigure(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags)
+{
+ pthread_mutex_lock(&db->opening_mutex);
+ int ret = lmdb_reinit(db, path, mapsize, env_flags);
+ if (ret != KNOT_EOK) {
+ lmdb_close(db);
+ ret = lmdb_reinit(db, path, mapsize, env_flags);
+ if (ret == KNOT_EOK) {
+ ret = lmdb_open(db);
+ }
+ }
+ pthread_mutex_unlock(&db->opening_mutex);
+ return ret;
+}
+
+void knot_lmdb_deinit(knot_lmdb_db_t *db)
+{
+ knot_lmdb_close(db);
+ pthread_mutex_destroy(&db->opening_mutex);
+ free(db->path);
+}
+
+void knot_lmdb_begin(knot_lmdb_db_t *db, knot_lmdb_txn_t *txn, bool rw)
+{
+ txn->ret = mdb_txn_begin(db->env, NULL, rw ? 0 : MDB_RDONLY, &txn->txn);
+ err_to_knot(&txn->ret);
+ if (txn->ret == KNOT_EOK) {
+ txn->opened = true;
+ txn->db = db;
+ txn->is_rw = rw;
+ }
+}
+
+void knot_lmdb_abort(knot_lmdb_txn_t *txn)
+{
+ if (txn->opened) {
+ if (txn->cursor != NULL) {
+ mdb_cursor_close(txn->cursor);
+ txn->cursor = NULL;
+ }
+ mdb_txn_abort(txn->txn);
+ txn->opened = false;
+ }
+}
+
+static bool txn_semcheck(knot_lmdb_txn_t *txn)
+{
+ if (!txn->opened && txn->ret == KNOT_EOK) {
+ txn->ret = KNOT_ESEMCHECK;
+ }
+ if (txn->ret != KNOT_EOK) {
+ knot_lmdb_abort(txn);
+ return false;
+ }
+ return true;
+}
+
+void knot_lmdb_commit(knot_lmdb_txn_t *txn)
+{
+ if (!txn_semcheck(txn)) {
+ return;
+ }
+ if (txn->cursor != NULL) {
+ mdb_cursor_close(txn->cursor);
+ txn->cursor = NULL;
+ }
+ txn->ret = mdb_txn_commit(txn->txn);
+ err_to_knot(&txn->ret);
+ txn->opened = false;
+}
+
+// save the programmer's frequent checking for ENOMEM when creating search keys
+static bool txn_enomem(knot_lmdb_txn_t *txn, const MDB_val *tocheck)
+{
+ if (tocheck->mv_data == NULL) {
+ txn->ret = KNOT_ENOMEM;
+ knot_lmdb_abort(txn);
+ return false;
+ }
+ return true;
+}
+
+static bool init_cursor(knot_lmdb_txn_t *txn)
+{
+ if (txn->cursor == NULL) {
+ txn->ret = mdb_cursor_open(txn->txn, txn->db->dbi, &txn->cursor);
+ err_to_knot(&txn->ret);
+ if (txn->ret != KNOT_EOK) {
+ knot_lmdb_abort(txn);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool curget(knot_lmdb_txn_t *txn, MDB_cursor_op op)
+{
+ txn->ret = mdb_cursor_get(txn->cursor, &txn->cur_key, &txn->cur_val, op);
+ err_to_knot(&txn->ret);
+ if (txn->ret == KNOT_ENOENT) {
+ txn->ret = KNOT_EOK;
+ return false;
+ }
+ return (txn->ret == KNOT_EOK);
+}
+
+static int mdb_val_clone(const MDB_val *orig, MDB_val *clone)
+{
+ clone->mv_data = malloc(orig->mv_size);
+ if (clone->mv_data == NULL) {
+ return KNOT_ENOMEM;
+ }
+ clone->mv_size = orig->mv_size;
+ memcpy(clone->mv_data, orig->mv_data, clone->mv_size);
+ return KNOT_EOK;
+}
+
+bool knot_lmdb_find(knot_lmdb_txn_t *txn, MDB_val *what, knot_lmdb_find_t how)
+{
+ if (!txn_semcheck(txn) || !init_cursor(txn) || !txn_enomem(txn, what)) {
+ return false;
+ }
+ txn->cur_key.mv_size = what->mv_size;
+ txn->cur_key.mv_data = what->mv_data;
+ txn->cur_val.mv_size = 0;
+ txn->cur_val.mv_data = NULL;
+ knot_lmdb_find_t cmp = (how & 3);
+ bool succ = curget(txn, cmp == KNOT_LMDB_EXACT ? MDB_SET : MDB_SET_RANGE);
+ if (cmp == KNOT_LMDB_LEQ && txn->ret == KNOT_EOK) {
+ // LEQ is not supported by LMDB, we use GEQ and go back
+ if (succ) {
+ if (txn->cur_key.mv_size != what->mv_size ||
+ memcmp(txn->cur_key.mv_data, what->mv_data, what->mv_size) != 0) {
+ succ = curget(txn, MDB_PREV);
+ }
+ } else {
+ succ = curget(txn, MDB_LAST);
+ }
+ }
+
+ if ((how & KNOT_LMDB_FORCE) && !succ && txn->ret == KNOT_EOK) {
+ txn->ret = KNOT_ENOENT;
+ }
+
+ return succ;
+}
+
+// this is not bulletproof thread-safe (in case of LMDB fail-teardown, but mostly OK
+int knot_lmdb_find_threadsafe(knot_lmdb_txn_t *txn, MDB_val *key, MDB_val *val, knot_lmdb_find_t how)
+{
+ assert(how == KNOT_LMDB_EXACT);
+ if (key->mv_data == NULL) {
+ return KNOT_ENOMEM;
+ }
+ if (!txn->opened) {
+ return KNOT_EINVAL;
+ }
+ if (txn->ret != KNOT_EOK) {
+ return txn->ret;
+ }
+ MDB_val tmp = { 0 };
+ int ret = mdb_get(txn->txn, txn->db->dbi, key, &tmp);
+ err_to_knot(&ret);
+ if (ret == KNOT_EOK) {
+ ret = mdb_val_clone(&tmp, val);
+ }
+ return ret;
+}
+
+bool knot_lmdb_first(knot_lmdb_txn_t *txn)
+{
+ return txn_semcheck(txn) && init_cursor(txn) && curget(txn, MDB_FIRST);
+}
+
+bool knot_lmdb_next(knot_lmdb_txn_t *txn)
+{
+ if (txn->cursor == NULL && txn->ret == KNOT_EOK) {
+ txn->ret = KNOT_EINVAL;
+ }
+ if (!txn_semcheck(txn)) {
+ return false;
+ }
+ return curget(txn, MDB_NEXT);
+}
+
+bool knot_lmdb_is_prefix_of(const MDB_val *prefix, const MDB_val *of)
+{
+ return prefix->mv_size <= of->mv_size &&
+ memcmp(prefix->mv_data, of->mv_data, prefix->mv_size) == 0;
+}
+
+void knot_lmdb_del_cur(knot_lmdb_txn_t *txn)
+{
+ if (txn_semcheck(txn)) {
+ txn->ret = mdb_cursor_del(txn->cursor, 0);
+ err_to_knot(&txn->ret);
+ }
+}
+
+void knot_lmdb_del_prefix(knot_lmdb_txn_t *txn, MDB_val *prefix)
+{
+ knot_lmdb_foreach(txn, prefix) {
+ knot_lmdb_del_cur(txn);
+ }
+}
+
+int knot_lmdb_apply_threadsafe(knot_lmdb_txn_t *txn, const MDB_val *key, bool prefix, lmdb_apply_cb cb, void *ctx)
+{
+ MDB_cursor *cursor;
+ int ret = mdb_cursor_open(txn->txn, txn->db->dbi, &cursor);
+ err_to_knot(&ret);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ MDB_val getkey = *key, getval = { 0 };
+ ret = mdb_cursor_get(cursor, &getkey, &getval, prefix ? MDB_SET_RANGE : MDB_SET);
+ err_to_knot(&ret);
+ if (ret != KNOT_EOK) {
+ mdb_cursor_close(cursor);
+ if (prefix && ret == KNOT_ENOENT) {
+ return KNOT_EOK;
+ }
+ return ret;
+ }
+
+ if (prefix) {
+ while (knot_lmdb_is_prefix_of(key, &getkey) && ret == KNOT_EOK) {
+ ret = cb(&getkey, &getval, ctx);
+ if (ret == KNOT_EOK) {
+ ret = mdb_cursor_get(cursor, &getkey, &getval, MDB_NEXT);
+ err_to_knot(&ret);
+ }
+ }
+ if (ret == KNOT_ENOENT) {
+ ret = KNOT_EOK;
+ }
+ } else {
+ ret = cb(&getkey, &getval, ctx);
+ }
+ mdb_cursor_close(cursor);
+ return ret;
+}
+
+bool knot_lmdb_insert(knot_lmdb_txn_t *txn, MDB_val *key, MDB_val *val)
+{
+ if (txn_semcheck(txn) && txn_enomem(txn, key)) {
+ unsigned flags = (val->mv_size > 0 && val->mv_data == NULL ? MDB_RESERVE : 0);
+ txn->ret = mdb_put(txn->txn, txn->db->dbi, key, val, flags);
+ err_to_knot(&txn->ret);
+ }
+ return (txn->ret == KNOT_EOK);
+}
+
+int knot_lmdb_quick_insert(knot_lmdb_db_t *db, MDB_val key, MDB_val val)
+{
+ if (val.mv_data == NULL) {
+ free(key.mv_data);
+ return KNOT_ENOMEM;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_insert(&txn, &key, &val);
+ free(key.mv_data);
+ free(val.mv_data);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int knot_lmdb_copy_prefix(knot_lmdb_txn_t *from, knot_lmdb_txn_t *to, MDB_val *prefix)
+{
+ knot_lmdb_foreach(to, prefix) {
+ knot_lmdb_del_cur(to);
+ }
+ if (to->ret != KNOT_EOK) {
+ return to->ret;
+ }
+ knot_lmdb_foreach(from, prefix) {
+ knot_lmdb_insert(to, &from->cur_key, &from->cur_val);
+ }
+ return from->ret == KNOT_EOK ? to->ret : from->ret;
+}
+
+int knot_lmdb_copy_prefixes(knot_lmdb_db_t *from, knot_lmdb_db_t *to,
+ MDB_val *prefixes, size_t n_prefixes)
+{
+ if (n_prefixes < 1) {
+ return KNOT_EOK;
+ }
+ if (from == NULL || to == NULL || prefixes == NULL) {
+ return KNOT_EINVAL;
+ }
+ int ret = knot_lmdb_open(from);
+ if (ret == KNOT_EOK) {
+ ret = knot_lmdb_open(to);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t tr = { 0 }, tw = { 0 };
+ knot_lmdb_begin(from, &tr, false);
+ knot_lmdb_begin(to, &tw, true);
+ for (size_t i = 0; i < n_prefixes && ret == KNOT_EOK; i++) {
+ ret = knot_lmdb_copy_prefix(&tr, &tw, &prefixes[i]);
+ }
+ knot_lmdb_commit(&tw);
+ knot_lmdb_commit(&tr);
+ return ret == KNOT_EOK ? tw.ret : ret;
+}
+
+size_t knot_lmdb_usage(knot_lmdb_txn_t *txn)
+{
+ if (!txn_semcheck(txn)) {
+ return 0;
+ }
+ MDB_stat st = { 0 };
+ txn->ret = mdb_stat(txn->txn, txn->db->dbi, &st);
+ err_to_knot(&txn->ret);
+
+ size_t pgs_used = st.ms_branch_pages + st.ms_leaf_pages + st.ms_overflow_pages;
+ return (pgs_used * st.ms_psize);
+}
+
+static bool make_key_part(void *key_data, size_t key_len, const char *format, va_list arg)
+{
+ wire_ctx_t wire = wire_ctx_init(key_data, key_len);
+ const char *tmp_s;
+ const knot_dname_t *tmp_d;
+ const void *tmp_v;
+ size_t tmp;
+
+ for (const char *f = format; *f != '\0'; f++) {
+ switch (*f) {
+ case 'B':
+ wire_ctx_write_u8(&wire, va_arg(arg, int));
+ break;
+ case 'H':
+ wire_ctx_write_u16(&wire, va_arg(arg, int));
+ break;
+ case 'I':
+ wire_ctx_write_u32(&wire, va_arg(arg, uint32_t));
+ break;
+ case 'L':
+ wire_ctx_write_u64(&wire, va_arg(arg, uint64_t));
+ break;
+ case 'S':
+ tmp_s = va_arg(arg, const char *);
+ wire_ctx_write(&wire, tmp_s, strlen(tmp_s) + 1);
+ break;
+ case 'N':
+ tmp_d = va_arg(arg, const knot_dname_t *);
+ wire_ctx_write(&wire, tmp_d, knot_dname_size(tmp_d));
+ break;
+ case 'D':
+ tmp_v = va_arg(arg, const void *);
+ tmp = va_arg(arg, size_t);
+ wire_ctx_write(&wire, tmp_v, tmp);
+ break;
+ }
+ }
+
+ return wire.error == KNOT_EOK && wire_ctx_available(&wire) == 0;
+}
+
+MDB_val knot_lmdb_make_key(const char *format, ...)
+{
+ MDB_val key = { 0 };
+ va_list arg;
+ const char *tmp_s;
+ const knot_dname_t *tmp_d;
+
+ // first, just determine the size of the key
+ va_start(arg, format);
+ for (const char *f = format; *f != '\0'; f++) {
+ switch (*f) {
+ case 'B':
+ key.mv_size += sizeof(uint8_t);
+ (void)va_arg(arg, int); // uint8_t will be promoted to int
+ break;
+ case 'H':
+ key.mv_size += sizeof(uint16_t);
+ (void)va_arg(arg, int); // uint16_t will be promoted to int
+ break;
+ case 'I':
+ key.mv_size += sizeof(uint32_t);
+ (void)va_arg(arg, uint32_t);
+ break;
+ case 'L':
+ key.mv_size += sizeof(uint64_t);
+ (void)va_arg(arg, uint64_t);
+ break;
+ case 'S':
+ tmp_s = va_arg(arg, const char *);
+ key.mv_size += strlen(tmp_s) + 1;
+ break;
+ case 'N':
+ tmp_d = va_arg(arg, const knot_dname_t *);
+ key.mv_size += knot_dname_size(tmp_d);
+ break;
+ case 'D':
+ (void)va_arg(arg, const void *);
+ key.mv_size += va_arg(arg, size_t);
+ break;
+ }
+ }
+ va_end(arg);
+
+ // second, alloc the key and fill it
+ if (key.mv_size > 0) {
+ key.mv_data = malloc(key.mv_size);
+ }
+ if (key.mv_data == NULL) {
+ return key;
+ }
+ va_start(arg, format);
+ bool succ = make_key_part(key.mv_data, key.mv_size, format, arg);
+ assert(succ);
+ (void)succ;
+ va_end(arg);
+ return key;
+}
+
+bool knot_lmdb_make_key_part(void *key_data, size_t key_len, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ bool succ = make_key_part(key_data, key_len, format, arg);
+ va_end(arg);
+ return succ;
+}
+
+static bool unmake_key_part(const void *key_data, size_t key_len, const char *format, va_list arg)
+{
+ if (key_data == NULL) {
+ return false;
+ }
+ wire_ctx_t wire = wire_ctx_init_const(key_data, key_len);
+ for (const char *f = format; *f != '\0' && wire.error == KNOT_EOK && wire_ctx_available(&wire) > 0; f++) {
+ void *tmp = va_arg(arg, void *);
+ size_t tmsize;
+ switch (*f) {
+ case 'B':
+ if (tmp == NULL) {
+ wire_ctx_skip(&wire, sizeof(uint8_t));
+ } else {
+ *(uint8_t *)tmp = wire_ctx_read_u8(&wire);
+ }
+ break;
+ case 'H':
+ if (tmp == NULL) {
+ wire_ctx_skip(&wire, sizeof(uint16_t));
+ } else {
+ *(uint16_t *)tmp = wire_ctx_read_u16(&wire);
+ }
+ break;
+ case 'I':
+ if (tmp == NULL) {
+ wire_ctx_skip(&wire, sizeof(uint32_t));
+ } else {
+ *(uint32_t *)tmp = wire_ctx_read_u32(&wire);
+ }
+ break;
+ case 'L':
+ if (tmp == NULL) {
+ wire_ctx_skip(&wire, sizeof(uint64_t));
+ } else {
+ *(uint64_t *)tmp = wire_ctx_read_u64(&wire);
+ }
+ break;
+ case 'S':
+ if (tmp != NULL) {
+ *(const char **)tmp = (const char *)wire.position;
+ }
+ wire_ctx_skip(&wire, strlen((const char *)wire.position) + 1);
+ break;
+ case 'N':
+ if (tmp != NULL) {
+ *(const knot_dname_t **)tmp = (const knot_dname_t *)wire.position;
+ }
+ wire_ctx_skip(&wire, knot_dname_size((const knot_dname_t *)wire.position));
+ break;
+ case 'D':
+ tmsize = va_arg(arg, size_t);
+ if (tmp != NULL) {
+ memcpy(tmp, wire.position, tmsize);
+ }
+ wire_ctx_skip(&wire, tmsize);
+ break;
+ }
+ }
+ return (wire.error == KNOT_EOK && wire_ctx_available(&wire) == 0);
+}
+
+bool knot_lmdb_unmake_key(const void *key_data, size_t key_len, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ bool succ = unmake_key_part(key_data, key_len, format, arg);
+ va_end(arg);
+ return succ;
+}
+
+bool knot_lmdb_unmake_curval(knot_lmdb_txn_t *txn, const char *format, ...)
+{
+ va_list arg;
+ va_start(arg, format);
+ bool succ = unmake_key_part(txn->cur_val.mv_data, txn->cur_val.mv_size, format, arg);
+ va_end(arg);
+ if (!succ && txn->ret == KNOT_EOK) {
+ txn->ret = KNOT_EMALF;
+ }
+ return succ;
+}
diff --git a/src/knot/journal/knot_lmdb.h b/src/knot/journal/knot_lmdb.h
new file mode 100644
index 0000000..6214a10
--- /dev/null
+++ b/src/knot/journal/knot_lmdb.h
@@ -0,0 +1,446 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <lmdb.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+typedef struct knot_lmdb_db {
+ MDB_dbi dbi;
+ MDB_env *env;
+ pthread_mutex_t opening_mutex;
+
+ // those are static options. Set them after knot_lmdb_init().
+ unsigned maxdbs;
+ unsigned maxreaders;
+
+ // those are internal options. Please don't touch them directly.
+ size_t mapsize;
+ unsigned env_flags; // MDB_NOTLS, MDB_RDONLY, MDB_WRITEMAP, MDB_DUPSORT, MDB_NOSYNC, MDB_MAPASYNC
+ const char *dbname;
+ char *path;
+} knot_lmdb_db_t;
+
+typedef struct {
+ MDB_txn *txn;
+ MDB_cursor *cursor;
+ MDB_val cur_key;
+ MDB_val cur_val;
+
+ bool opened;
+ bool is_rw;
+ int ret;
+ knot_lmdb_db_t *db;
+} knot_lmdb_txn_t;
+
+typedef enum {
+ KNOT_LMDB_EXACT = 3, /*! \brief Search for exactly matching key. */
+ KNOT_LMDB_LEQ = 1, /*! \brief Search lexicographically lower or equal key. */
+ KNOT_LMDB_GEQ = 2, /*! \brief Search lexicographically greater or equal key. */
+ KNOT_LMDB_FORCE = 4, /*! \brief If no matching key found, consider it a transaction failure (KNOT_ENOENT). */
+} knot_lmdb_find_t;
+
+/*!
+ * \brief Callback used in sweep functions.
+ *
+ * \retval true for zones to preserve.
+ * \retval false for zones to remove.
+ */
+typedef bool (*sweep_cb)(const uint8_t *zone, void *data);
+
+/*!
+ * \brief Callback used in copy functions.
+ *
+ * \retval true if the current record shall be copied
+ * \retval false if the current record shall be skipped
+ */
+typedef bool (*knot_lmdb_copy_cb)(MDB_val *cur_key, MDB_val *cur_val);
+
+/*!
+ * \brief Initialise the DB handling structure.
+ *
+ * \param db DB handling structure.
+ * \param path Path to LMDB database on filesystem.
+ * \param mapsize Maximum size of the DB on FS.
+ * \param env_flags LMDB environment flags (e.g. MDB_RDONLY)
+ * \param dbname Optional: name of the sub-database.
+ */
+void knot_lmdb_init(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags, const char *dbname);
+
+/*!
+ * \brief Check if the database exists on the filesystem.
+ *
+ * \param db The DB in question.
+ *
+ * \retval KNOT_EOK The database exists (and is accessible for stat() ).
+ * \retval KNOT_ENODB The database doesn't exist.
+ * \return KNOT_E* explaining why stat() failed.
+ */
+int knot_lmdb_exists(knot_lmdb_db_t *db);
+
+/*!
+ * \brief Big enough mapsize for new database to hold a copy of to_copy.
+ */
+size_t knot_lmdb_copy_size(knot_lmdb_db_t *to_copy);
+
+/*!
+ * \brief Open the previously initialised DB.
+ *
+ * \param db The DB to be opened.
+ *
+ * \note If db->mapsize is zero, it will be set to twice the current size, and DB opened read-only!
+ *
+ * \return KNOT_E*
+ */
+int knot_lmdb_open(knot_lmdb_db_t *db);
+
+/*!
+ * \brief Close the database, but keep it initialised.
+ *
+ * \param db The DB to be closed.
+ */
+void knot_lmdb_close(knot_lmdb_db_t *db);
+
+/*!
+ * \brief Re-initialise existing DB with modified parameters.
+ *
+ * \note If the parameters differ and DB is open, it will be refused.
+ *
+ * \param db The DB to be modified.
+ * \param path New path to the DB.
+ * \param mapsize New mapsize.
+ * \param env_flags New LMDB environment flags.
+ *
+ * \return KNOT_EOK on success, KNOT_EISCONN if not possible.
+ */
+int knot_lmdb_reinit(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags);
+
+/*!
+ * \brief Re-open opened DB with modified parameters.
+ *
+ * \note The DB will be first closed, re-initialised and finally opened again.
+ *
+ * \note There must not be any DB transaction during this process.
+ *
+ * \param db The DB to be modified.
+ * \param path New path to the DB.
+ * \param mapsize New mapsize.
+ * \param env_flags New LMDB environment flags.
+ *
+ * \return KNOT_E*
+ */
+int knot_lmdb_reconfigure(knot_lmdb_db_t *db, const char *path, size_t mapsize, unsigned env_flags);
+
+/*!
+ * \brief Close and de-initialise DB.
+ *
+ * \param db DB to be deinitialized.
+ */
+void knot_lmdb_deinit(knot_lmdb_db_t *db);
+
+/*!
+ * \brief Return true if DB is open.
+ */
+inline static bool knot_lmdb_is_open(knot_lmdb_db_t *db) { return db != NULL && db->env != NULL; }
+
+/*!
+ * \brief Start a DB transaction.
+ *
+ * \param db The database.
+ * \param txn Transaction handling structure to be initialised.
+ * \param rw True for read-write transaction, false for read-only.
+ *
+ * \note The error code will be stored in txn->ret.
+ */
+void knot_lmdb_begin(knot_lmdb_db_t *db, knot_lmdb_txn_t *txn, bool rw);
+
+/*!
+ * \brief Abort a transaction.
+ *
+ * \param txn Transaction to be aborted.
+ */
+void knot_lmdb_abort(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Commit a transaction, or abort it if id had failured.
+ *
+ * \param txn Transaction to be committed.
+ *
+ * \note If txn->ret equals KNOT_EOK afterwards, whole DB transaction was successful.
+ */
+void knot_lmdb_commit(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Find a key in database. The matched key will be in txn->cur_key and its value in txn->cur_val.
+ *
+ * \param txn DB transaction.
+ * \param what Key to be searched for.
+ * \param how Method of comparing keys. See comments at knot_lmdb_find_t.
+ *
+ * \note It's possible to use knot_lmdb_next() subsequently to iterate over following keys.
+ *
+ * \return True if a key found, false if none or failure.
+ */
+bool knot_lmdb_find(knot_lmdb_txn_t *txn, MDB_val *what, knot_lmdb_find_t how);
+
+/*!
+ * \brief Simple database lookup in case txn shared among threads.
+ *
+ * \param txn DB transaction share among threads.
+ * \param key Key to be searched for.
+ * \param val Output: database value.
+ * \param how Must be KNOT_LMDB_EXACT.
+ *
+ * \note Free val->mv_data afterwards!
+ *
+ * \retval KNOT_ENOENT no such key in DB.
+ * \return KNOT_E*
+ */
+int knot_lmdb_find_threadsafe(knot_lmdb_txn_t *txn, MDB_val *key, MDB_val *val, knot_lmdb_find_t how);
+
+/*!
+ * \brief Start iteration the whole DB from lexicographically first key.
+ *
+ * \note The first DB record will be in txn->cur_key and txn->cur_val.
+ *
+ * \param txn DB transaction.
+ *
+ * \return True if ok, false if no key at all or failure.
+ */
+bool knot_lmdb_first(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Iterate to the lexicographically next key (sets txn->cur_key and txn->cur_val).
+ *
+ * \param txn DB transaction.
+ *
+ * \return True if ok, false if behind the end of DB or failure.
+ */
+bool knot_lmdb_next(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Check if one DB key is a prefix of another,
+ *
+ * \param prefix DB key prefix.
+ * \param of Another DB key.
+ *
+ * \return True iff 'prefix' is a prefix of 'of'.
+ */
+bool knot_lmdb_is_prefix_of(const MDB_val *prefix, const MDB_val *of);
+
+/*!
+ * \brief Find leftmost key in DB matching given prefix.
+ *
+ * \param txn DB transaction.
+ * \param prefix Prefix searched for.
+ *
+ * \return True if found, false if none or failure.
+ */
+inline static bool knot_lmdb_find_prefix(knot_lmdb_txn_t *txn, MDB_val *prefix)
+{
+ return knot_lmdb_find(txn, prefix, KNOT_LMDB_GEQ) &&
+ knot_lmdb_is_prefix_of(prefix, &txn->cur_key);
+}
+
+/*!
+ * \brief Execute following block of commands for every key in DB matching given prefix.
+ *
+ * \param txn DB transaction.
+ * \param prefix Prefix searched for.
+ */
+#define knot_lmdb_foreach(txn, prefix) \
+ for (bool _knot_lmdb_foreach_found = knot_lmdb_find((txn), (prefix), KNOT_LMDB_GEQ); \
+ _knot_lmdb_foreach_found && knot_lmdb_is_prefix_of((prefix), &(txn)->cur_key); \
+ _knot_lmdb_foreach_found = knot_lmdb_next((txn)))
+
+/*!
+ * \brief Execute following block of commands for every key in DB.
+ *
+ * \param txn DB transaction.
+ */
+#define knot_lmdb_forwhole(txn) \
+ for (bool _knot_lmdb_forwhole_any = knot_lmdb_first((txn)); \
+ _knot_lmdb_forwhole_any; \
+ _knot_lmdb_forwhole_any = knot_lmdb_next((txn)))
+
+/*!
+ * \brief Delete the one DB record, that the iteration is currently pointing to.
+ *
+ * \note It's safe to delete during an uncomplicated iteration, e.g. knot_lmdb_foreach().
+ *
+ * \param txn DB transaction.
+ */
+void knot_lmdb_del_cur(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Delete all DB records matching given key prefix.
+ *
+ * \param txn DB transaction.
+ * \param prefix Prefix to be deleted.
+ */
+void knot_lmdb_del_prefix(knot_lmdb_txn_t *txn, MDB_val *prefix);
+
+typedef int (*lmdb_apply_cb)(MDB_val *key, MDB_val *val, void *ctx);
+
+/*!
+ * \brief Call a callback for any item matching given key.
+ *
+ * \note This function does not affect fields within txn struct,
+ * thus can be used on txn shared between threads.
+ *
+ * \param txn DB transaction.
+ * \param key Key to be searched for.
+ * \param prefix The 'key' is in fact prefix, apply on all items matching prefix.
+ * \param cb Callback to be called.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+int knot_lmdb_apply_threadsafe(knot_lmdb_txn_t *txn, const MDB_val *key, bool prefix, lmdb_apply_cb cb, void *ctx);
+
+/*!
+ * \brief Insert a new record into the DB.
+ *
+ * \note If a record with equal key already exists in the DB, its value will be quietly overwritten.
+ *
+ * \param txn DB transaction.
+ * \param key Inserted key.
+ * \param val Inserted value.
+ *
+ * \return False if failure.
+ */
+bool knot_lmdb_insert(knot_lmdb_txn_t *txn, MDB_val *key, MDB_val *val);
+
+/*!
+ * \brief Open a transaction, insert a record, commit and free key's and val's mv_data.
+ *
+ * \param db DB to be inserted into.
+ * \param key Inserted key.
+ * \param val Inserted val.
+ *
+ * \return KNOT_E*
+ */
+int knot_lmdb_quick_insert(knot_lmdb_db_t *db, MDB_val key, MDB_val val);
+
+/*!
+ * \brief Copy all records matching given key prefix.
+ *
+ * \param from Open RO/RW transaction in the database to copy from.
+ * \param to Open RW txn in the DB to copy to.
+ * \param prefix Prefix for matching records to be copied.
+ *
+ * \note Prior to copying, all records from the target DB, matching the prefix, will be deleted!
+ *
+ * \return KNOT_E*
+ *
+ * \note KNOT_EOK even if none records matched the prefix (and were copied).
+ */
+int knot_lmdb_copy_prefix(knot_lmdb_txn_t *from, knot_lmdb_txn_t *to, MDB_val *prefix);
+
+/*!
+ * \brief Copy all records matching any of multiple prefixes.
+ *
+ * \param from DB to copy from.
+ * \param to DB to copy to.
+ * \param prefixes List of prefixes to match.
+ * \param n_prefixes Number of prefixes in the list.
+ *
+ * \note Prior to copying, all records from the target DB, matching any of the prefixes, will be deleted!
+ *
+ * \return KNOT_E*
+ */
+int knot_lmdb_copy_prefixes(knot_lmdb_db_t *from, knot_lmdb_db_t *to,
+ MDB_val *prefixes, size_t n_prefixes);
+
+/*!
+ * \brief Amount of bytes used by the DB storage.
+ *
+ * \note According to LMDB design, it will be a multiple of page size, which is usually 4096.
+ *
+ * \param txn DB transaction.
+ *
+ * \return DB usage.
+ */
+size_t knot_lmdb_usage(knot_lmdb_txn_t *txn);
+
+/*!
+ * \brief Serialize various parameters into a DB key.
+ *
+ * \param format Specifies the number and type of parameters.
+ * \param ... For each character in 'format', one or two parameters with the actual values.
+ *
+ * \return DB key structure. 'mv_data' needs to be freed later. 'mv_data' is NULL on failure.
+ *
+ * Possible format characters are:
+ * - B for a byte
+ * - H for uint16
+ * - I for uint32
+ * - L for uint64, like H and I, the serialization converts them to big endian
+ * - S for zero-terminated string
+ * - N for a domain name (in knot_dname_t* format)
+ * - D for fixed-size data (takes two params: void* and size_t)
+ */
+MDB_val knot_lmdb_make_key(const char *format, ...);
+
+/*!
+ * \brief Serialize various parameters into prepared buffer.
+ *
+ * \param key_data Pointer to the buffer.
+ * \param key_len Size of the buffer.
+ * \param format Specifies the number and type of parameters.
+ * \param ... For each character in 'format', one or two parameters with the actual values.
+ *
+ * \note See comment at knot_lmdb_make_key().
+ *
+ * \return True if ok and the serialization took exactly 'key_len', false on failure.
+ */
+bool knot_lmdb_make_key_part(void *key_data, size_t key_len, const char *format, ...);
+
+/*!
+ * \brief Deserialize various parameters from a buffer.
+ *
+ * \note 'format' must exactly correspond with what the data in buffer actually are.
+ *
+ * \param key_data Pointer to the buffer.
+ * \param key_len Size of the buffer.
+ * \param format Specifies the number and type of parameters.
+ * \param ... For each character in 'format', pointer to where the values will be stored.
+ *
+ * \note For B, H, I, L; provide simply pointers to variables of corresponding type.
+ * \note For S, N; provide pointer to pointer - it will be set to pointing inside the buffer, so no allocation here.
+ * \note For D, provide void* and size_t, the data will be copied.
+ *
+ * \return True if no failure.
+ */
+bool knot_lmdb_unmake_key(const void *key_data, size_t key_len, const char *format, ...);
+
+/*!
+ * \brief Deserialize various parameters from txn->cur_val. Set txn->ret to KNOT_EMALF if failure.
+ *
+ * \param txn DB transaction.
+ * \param format Specifies the number and type of parameters.
+ * \param ... For each character in 'format', pointer to where the values will be stored.
+ *
+ * \note See comment at knot_lmdb_unmake_key().
+ *
+ * \return True if no failure.
+ */
+bool knot_lmdb_unmake_curval(knot_lmdb_txn_t *txn, const char *format, ...);
diff --git a/src/knot/journal/serialization.c b/src/knot/journal/serialization.c
new file mode 100644
index 0000000..5758481
--- /dev/null
+++ b/src/knot/journal/serialization.c
@@ -0,0 +1,501 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/journal/serialization.h"
+#include "knot/zone/zone-tree.h"
+
+#define SERIALIZE_RRSET_INIT (-1)
+#define SERIALIZE_RRSET_DONE ((1L<<16)+1)
+
+typedef enum {
+ PHASE_ZONE_SOA,
+ PHASE_ZONE_NODES,
+ PHASE_ZONE_NSEC3,
+ PHASE_SOA_1,
+ PHASE_REM,
+ PHASE_SOA_2,
+ PHASE_ADD,
+ PHASE_END,
+} serialize_phase_t;
+
+#define RRSET_BUF_MAXSIZE 256
+
+struct serialize_ctx {
+ zone_diff_t zdiff;
+ zone_tree_it_t zit;
+ zone_node_t *n;
+ uint16_t node_pos;
+ bool zone_diff;
+ bool zone_diff_add;
+ int ret;
+
+ const changeset_t *ch;
+ changeset_iter_t it;
+ serialize_phase_t changeset_phase;
+ long rrset_phase;
+ knot_rrset_t rrset_buf[RRSET_BUF_MAXSIZE];
+ size_t rrset_buf_size;
+ list_t free_rdatasets;
+};
+
+serialize_ctx_t *serialize_init(const changeset_t *ch)
+{
+ serialize_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->ch = ch;
+ ctx->changeset_phase = ch->soa_from != NULL ? PHASE_SOA_1 : PHASE_SOA_2;
+ ctx->rrset_phase = SERIALIZE_RRSET_INIT;
+ ctx->rrset_buf_size = 0;
+ init_list(&ctx->free_rdatasets);
+
+ return ctx;
+}
+
+serialize_ctx_t *serialize_zone_init(const zone_contents_t *z)
+{
+ serialize_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ zone_diff_from_zone(&ctx->zdiff, z);
+ ctx->changeset_phase = PHASE_ZONE_SOA;
+ ctx->rrset_phase = SERIALIZE_RRSET_INIT;
+ ctx->rrset_buf_size = 0;
+ init_list(&ctx->free_rdatasets);
+
+ return ctx;
+}
+
+serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z)
+{
+ serialize_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->zone_diff = true;
+ ctx->zdiff = *z;
+ zone_diff_reverse(&ctx->zdiff); // start with removals of counterparts
+
+ ctx->changeset_phase = PHASE_ZONE_SOA;
+ ctx->rrset_phase = SERIALIZE_RRSET_INIT;
+ ctx->rrset_buf_size = 0;
+ init_list(&ctx->free_rdatasets);
+
+ return ctx;
+}
+
+static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx)
+{
+ knot_rrset_t res;
+ knot_rrset_init_empty(&res);
+ switch (ctx->changeset_phase) {
+ case PHASE_ZONE_SOA:
+ zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit);
+ ctx->changeset_phase = PHASE_ZONE_NODES;
+ return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA);
+ case PHASE_ZONE_NODES:
+ case PHASE_ZONE_NSEC3:
+ while (ctx->n == NULL || ctx->node_pos >= ctx->n->rrset_count) {
+ if (zone_tree_it_finished(&ctx->zit)) {
+ zone_tree_it_free(&ctx->zit);
+ if (ctx->changeset_phase == PHASE_ZONE_NSEC3 ||
+ zone_tree_is_empty(&ctx->zdiff.nsec3s)) {
+ if (ctx->zone_diff && !ctx->zone_diff_add) {
+ ctx->zone_diff_add = true;
+ zone_diff_reverse(&ctx->zdiff);
+ zone_tree_it_begin(&ctx->zdiff.nodes, &ctx->zit);
+ ctx->changeset_phase = PHASE_ZONE_NODES;
+ return node_rrset(ctx->zdiff.apex, KNOT_RRTYPE_SOA);
+ } else {
+ ctx->changeset_phase = PHASE_END;
+ return res;
+ }
+ } else {
+ zone_tree_it_begin(&ctx->zdiff.nsec3s, &ctx->zit);
+ ctx->changeset_phase = PHASE_ZONE_NSEC3;
+ }
+ }
+ ctx->n = zone_tree_it_val(&ctx->zit);
+ zone_tree_it_next(&ctx->zit);
+ ctx->node_pos = 0;
+ }
+ res = node_rrset_at(ctx->n, ctx->node_pos++);
+ if (ctx->n == ctx->zdiff.apex && res.type == KNOT_RRTYPE_SOA) {
+ return get_next_rrset(ctx);
+ }
+ if (ctx->zone_diff) {
+ knot_rrset_t counter_rr = node_rrset(binode_counterpart(ctx->n), res.type);
+ if (counter_rr.ttl == res.ttl && !knot_rrset_empty(&counter_rr)) {
+ if (knot_rdataset_subset(&res.rrs, &counter_rr.rrs)) {
+ return get_next_rrset(ctx);
+ }
+ knot_rdataset_t rd_copy;
+ ctx->ret = knot_rdataset_copy(&rd_copy, &res.rrs, NULL);
+ if (ctx->ret == KNOT_EOK) {
+ knot_rdataset_subtract(&rd_copy, &counter_rr.rrs, NULL);
+ ptrlist_add(&ctx->free_rdatasets, rd_copy.rdata, NULL);
+ res.rrs = rd_copy;
+ assert(!knot_rrset_empty(&res));
+ } else {
+ ctx->changeset_phase = PHASE_END;
+ }
+ }
+ }
+ return res;
+ case PHASE_SOA_1:
+ changeset_iter_rem(&ctx->it, ctx->ch);
+ ctx->changeset_phase = PHASE_REM;
+ return *ctx->ch->soa_from;
+ case PHASE_REM:
+ res = changeset_iter_next(&ctx->it);
+ if (knot_rrset_empty(&res)) {
+ changeset_iter_clear(&ctx->it);
+ changeset_iter_add(&ctx->it, ctx->ch);
+ ctx->changeset_phase = PHASE_ADD;
+ return *ctx->ch->soa_to;
+ }
+ return res;
+ case PHASE_SOA_2:
+ if (ctx->it.node != NULL) {
+ changeset_iter_clear(&ctx->it);
+ }
+ changeset_iter_add(&ctx->it, ctx->ch);
+ ctx->changeset_phase = PHASE_ADD;
+ return *ctx->ch->soa_to;
+ case PHASE_ADD:
+ res = changeset_iter_next(&ctx->it);
+ if (knot_rrset_empty(&res)) {
+ changeset_iter_clear(&ctx->it);
+ ctx->changeset_phase = PHASE_END;
+ }
+ return res;
+ default:
+ return res;
+ }
+}
+
+void serialize_prepare(serialize_ctx_t *ctx, size_t thresh_size,
+ size_t max_size, size_t *realsize)
+{
+ *realsize = 0;
+
+ // check if we are in middle of a rrset
+ if (ctx->rrset_buf_size > 0) {
+ ctx->rrset_buf[0] = ctx->rrset_buf[ctx->rrset_buf_size - 1];
+ ctx->rrset_buf_size = 1;
+
+ // memory optimization: free all buffered rrsets except last one
+ ptrnode_t *n, *next;
+ WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) {
+ if (n != TAIL(ctx->free_rdatasets)) {
+ free(n->d);
+ rem_node(&n->n);
+ free(n);
+ }
+ }
+ } else {
+ ctx->rrset_buf[0] = get_next_rrset(ctx);
+ if (ctx->changeset_phase == PHASE_END) {
+ ctx->rrset_buf_size = 0;
+ return;
+ }
+ ctx->rrset_buf_size = 1;
+ }
+
+ size_t candidate = 0;
+ long tmp_phase = ctx->rrset_phase;
+ while (1) {
+ if (tmp_phase >= ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs.count) {
+ if (ctx->rrset_buf_size >= RRSET_BUF_MAXSIZE) {
+ return;
+ }
+ ctx->rrset_buf[ctx->rrset_buf_size++] = get_next_rrset(ctx);
+ if (ctx->changeset_phase == PHASE_END) {
+ ctx->rrset_buf_size--;
+ return;
+ }
+ tmp_phase = SERIALIZE_RRSET_INIT;
+ }
+ if (tmp_phase == SERIALIZE_RRSET_INIT) {
+ candidate += 3 * sizeof(uint16_t) +
+ knot_dname_size(ctx->rrset_buf[ctx->rrset_buf_size - 1].owner);
+ } else {
+ candidate += sizeof(uint32_t) + sizeof(uint16_t) +
+ knot_rdataset_at(&ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs, tmp_phase)->len;
+ }
+ if (candidate > max_size) {
+ return;
+ }
+ *realsize = candidate;
+ if (candidate >= thresh_size) {
+ return;
+ }
+ tmp_phase++;
+ }
+}
+
+void serialize_chunk(serialize_ctx_t *ctx, uint8_t *dst_chunk, size_t chunk_size)
+{
+ wire_ctx_t wire = wire_ctx_init(dst_chunk, chunk_size);
+
+ for (size_t i = 0; ; ) {
+ if (ctx->rrset_phase >= ctx->rrset_buf[i].rrs.count) {
+ if (++i >= ctx->rrset_buf_size) {
+ break;
+ }
+ ctx->rrset_phase = SERIALIZE_RRSET_INIT;
+ }
+ if (ctx->rrset_phase == SERIALIZE_RRSET_INIT) {
+ int size = knot_dname_to_wire(wire.position, ctx->rrset_buf[i].owner,
+ wire_ctx_available(&wire));
+ if (size < 0 || wire_ctx_available(&wire) < size + 3 * sizeof(uint16_t)) {
+ break;
+ }
+ wire_ctx_skip(&wire, size);
+ wire_ctx_write_u16(&wire, ctx->rrset_buf[i].type);
+ wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rclass);
+ wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rrs.count);
+ } else {
+ const knot_rdata_t *rr = knot_rdataset_at(&ctx->rrset_buf[i].rrs,
+ ctx->rrset_phase);
+ assert(rr);
+ uint16_t rdlen = rr->len;
+ if (wire_ctx_available(&wire) < sizeof(uint32_t) + sizeof(uint16_t) + rdlen) {
+ break;
+ }
+ // Compatibility, but one TTL per rrset would be enough.
+ wire_ctx_write_u32(&wire, ctx->rrset_buf[i].ttl);
+ wire_ctx_write_u16(&wire, rdlen);
+ wire_ctx_write(&wire, rr->data, rdlen);
+ }
+ ctx->rrset_phase++;
+ }
+ assert(wire.error == KNOT_EOK);
+}
+
+bool serialize_unfinished(serialize_ctx_t *ctx)
+{
+ return ctx->changeset_phase < PHASE_END;
+}
+
+int serialize_deinit(serialize_ctx_t *ctx)
+{
+ if (ctx->it.node != NULL) {
+ changeset_iter_clear(&ctx->it);
+ }
+ if (ctx->zit.tree != NULL) {
+ zone_tree_it_free(&ctx->zit);
+ }
+ ptrnode_t *n, *next;
+ WALK_LIST_DELSAFE(n, next, ctx->free_rdatasets) {
+ free(n->d);
+ rem_node(&n->n);
+ free(n);
+ }
+ int ret = ctx->ret;
+ free(ctx);
+ return ret;
+}
+
+static uint64_t rrset_binary_size(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL || rrset->rrs.count == 0) {
+ return 0;
+ }
+
+ // Owner size + type + class + RR count.
+ uint64_t size = knot_dname_size(rrset->owner) + 3 * sizeof(uint16_t);
+
+ // RRs.
+ knot_rdata_t *rr = rrset->rrs.rdata;
+ for (uint16_t i = 0; i < rrset->rrs.count; i++) {
+ // TTL + RR size + RR.
+ size += sizeof(uint32_t) + sizeof(uint16_t) + rr->len;
+ rr = knot_rdataset_next(rr);
+ }
+
+ return size;
+}
+
+static size_t node_diff_size(zone_node_t *node)
+{
+ size_t res = 0;
+ knot_rrset_t rr, counter_rr;
+ for (int i = 0; i < node->rrset_count; i++) {
+ rr = node_rrset_at(node, i);
+ counter_rr = node_rrset(binode_counterpart(node), rr.type);
+ if (!knot_rrset_equal(&rr, &counter_rr, true)) {
+ res += rrset_binary_size(&rr);
+ }
+ }
+ return res;
+}
+
+size_t zone_diff_serialized_size(zone_diff_t diff)
+{
+ size_t res = 0;
+ for (int i = 0; i < 2; i++) {
+ zone_diff_reverse(&diff);
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_double_begin(&diff.nodes, diff.nsec3s.trie != NULL ?
+ &diff.nsec3s : NULL, &it);
+ if (ret != KNOT_EOK) {
+ return 0;
+ }
+ while (!zone_tree_it_finished(&it)) {
+ res += node_diff_size(zone_tree_it_val(&it));
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ }
+ return res;
+}
+
+size_t changeset_serialized_size(const changeset_t *ch)
+{
+ if (ch == NULL) {
+ return 0;
+ }
+
+ size_t soa_from_size = rrset_binary_size(ch->soa_from);
+ size_t soa_to_size = rrset_binary_size(ch->soa_to);
+
+ changeset_iter_t it;
+ if (ch->remove == NULL) {
+ changeset_iter_add(&it, ch);
+ } else {
+ changeset_iter_all(&it, ch);
+ }
+
+ size_t change_size = 0;
+ knot_rrset_t rrset = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&rrset)) {
+ change_size += rrset_binary_size(&rrset);
+ rrset = changeset_iter_next(&it);
+ }
+
+ changeset_iter_clear(&it);
+
+ return soa_from_size + soa_to_size + change_size;
+}
+
+int serialize_rrset(wire_ctx_t *wire, const knot_rrset_t *rrset)
+{
+ assert(wire != NULL && rrset != NULL);
+
+ // write owner, type, class, rrcnt
+ int size = knot_dname_to_wire(wire->position, rrset->owner,
+ wire_ctx_available(wire));
+ if (size < 0 || wire_ctx_available(wire) < size + 3 * sizeof(uint16_t)) {
+ assert(0);
+ }
+ wire_ctx_skip(wire, size);
+ wire_ctx_write_u16(wire, rrset->type);
+ wire_ctx_write_u16(wire, rrset->rclass);
+ wire_ctx_write_u16(wire, rrset->rrs.count);
+
+ for (size_t phase = 0; phase < rrset->rrs.count; phase++) {
+ const knot_rdata_t *rr = knot_rdataset_at(&rrset->rrs, phase);
+ assert(rr);
+ uint16_t rdlen = rr->len;
+ if (wire_ctx_available(wire) < sizeof(uint32_t) + sizeof(uint16_t) + rdlen) {
+ assert(0);
+ }
+ wire_ctx_write_u32(wire, rrset->ttl);
+ wire_ctx_write_u16(wire, rdlen);
+ wire_ctx_write(wire, rr->data, rdlen);
+ assert(wire->error == KNOT_EOK);
+ }
+
+ return KNOT_EOK;
+}
+
+int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset)
+{
+ assert(wire != NULL && rrset != NULL);
+
+ // Read owner, rtype, rclass and RR count.
+ int size = knot_dname_size(wire->position);
+ if (size < 0) {
+ assert(0);
+ }
+ knot_dname_t *owner = knot_dname_copy(wire->position, NULL);
+ if (owner == NULL || wire_ctx_available(wire) < size + 3 * sizeof(uint16_t)) {
+ knot_dname_free(owner, NULL);
+ return KNOT_EMALF;
+ }
+ wire_ctx_skip(wire, size);
+ uint16_t type = wire_ctx_read_u16(wire);
+ uint16_t rclass = wire_ctx_read_u16(wire);
+ uint16_t rrcount = wire_ctx_read_u16(wire);
+ if (wire->error != KNOT_EOK) {
+ knot_dname_free(owner, NULL);
+ return wire->error;
+ }
+ if (rrset->owner != NULL) {
+ if (knot_dname_cmp(owner, rrset->owner) != 0) {
+ knot_dname_free(owner, NULL);
+ return KNOT_ESEMCHECK;
+ }
+ knot_rrset_clear(rrset, NULL);
+ }
+ knot_rrset_init(rrset, owner, type, rclass, 0);
+
+ for (size_t phase = 0; phase < rrcount && wire_ctx_available(wire) > 0; phase++) {
+ uint32_t ttl = wire_ctx_read_u32(wire);
+ uint32_t rdata_size = wire_ctx_read_u16(wire);
+ if (phase == 0) {
+ rrset->ttl = ttl;
+ }
+ if (wire->error != KNOT_EOK ||
+ wire_ctx_available(wire) < rdata_size ||
+ knot_rrset_add_rdata(rrset, wire->position, rdata_size,
+ NULL) != KNOT_EOK) {
+ knot_rrset_clear(rrset, NULL);
+ return KNOT_EMALF;
+ }
+ wire_ctx_skip(wire, rdata_size);
+ assert(wire->error == KNOT_EOK);
+ }
+
+ return KNOT_EOK;
+}
+
+size_t rrset_serialized_size(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return 0;
+ }
+
+ // Owner size + type + class + RR count.
+ size_t size = knot_dname_size(rrset->owner) + 3 * sizeof(uint16_t);
+
+ for (uint16_t i = 0; i < rrset->rrs.count; i++) {
+ const knot_rdata_t *rr = knot_rdataset_at(&rrset->rrs, i);
+ assert(rr);
+ // TTL + RR size + RR.
+ size += sizeof(uint32_t) + sizeof(uint16_t) + rr->len;
+ }
+
+ return size;
+}
diff --git a/src/knot/journal/serialization.h b/src/knot/journal/serialization.h
new file mode 100644
index 0000000..621dcdb
--- /dev/null
+++ b/src/knot/journal/serialization.h
@@ -0,0 +1,169 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/rrset.h"
+#include "libknot/rrtype/soa.h"
+#include "knot/updates/changesets.h"
+#include "contrib/wire_ctx.h"
+
+typedef struct zone_diff {
+ zone_tree_t nodes;
+ zone_tree_t nsec3s;
+ zone_node_t *apex;
+} zone_diff_t;
+
+inline static void zone_diff_reverse(zone_diff_t *diff)
+{
+ diff->nodes.flags ^= ZONE_TREE_BINO_SECOND;
+ diff->nsec3s.flags ^= ZONE_TREE_BINO_SECOND;
+ diff->apex = binode_counterpart(diff->apex);
+}
+
+inline static void zone_diff_from_zone(zone_diff_t *diff, const zone_contents_t *z)
+{
+ diff->nodes = *z->nodes;
+ if (z->nsec3_nodes != NULL) {
+ diff->nsec3s = *z->nsec3_nodes;
+ } else {
+ memset(&diff->nsec3s, 0, sizeof(diff->nsec3s));
+ }
+ diff->apex = z->apex;
+}
+
+inline static uint32_t zone_diff_to(const zone_diff_t *diff)
+{
+ return knot_soa_serial(node_rdataset(diff->apex, KNOT_RRTYPE_SOA)->rdata);
+}
+
+inline static uint32_t zone_diff_from(const zone_diff_t *diff)
+{
+ return knot_soa_serial(node_rdataset(binode_counterpart(diff->apex), KNOT_RRTYPE_SOA)->rdata);
+}
+
+typedef struct serialize_ctx serialize_ctx_t;
+
+/*!
+ * \brief Init serialization context.
+ *
+ * \param ch Changeset to be serialized.
+ *
+ * \return Context.
+ */
+serialize_ctx_t *serialize_init(const changeset_t *ch);
+
+/*!
+ * \brief Init serialization context.
+ *
+ * \param z Zone to be serialized like zone-in-journal changeset.
+ *
+ * \return Context.
+ */
+serialize_ctx_t *serialize_zone_init(const zone_contents_t *z);
+
+/*!
+ * \brief Init serialization context.
+ *
+ * \param z Zone with binodes being updated.
+ *
+ * \return Context.
+ */
+serialize_ctx_t *serialize_zone_diff_init(const zone_diff_t *z);
+
+/*!
+ * \brief Pre-check and space computation before serializing a chunk.
+ *
+ * \note This MUST be called before each serialize_chunk() !
+ *
+ * \param ctx Serializing context.
+ * \param thresh_size Optimal size of next chunk.
+ * \param max_size Maximum size of next chunk.
+ * \param realsize Output: real exact size of next chunk.
+ */
+void serialize_prepare(serialize_ctx_t *ctx, size_t thresh_size,
+ size_t max_size, size_t *realsize);
+
+/*!
+ * \brief Perform one step of serializiation: fill one chunk.
+ *
+ * \param ctx Serializing context.
+ * \param chunk Pointer on allocated memory to be serialized into.
+ * \param chunk_size Its size. It MUST be the same as returned from serialize_prepare().
+ */
+void serialize_chunk(serialize_ctx_t *ctx, uint8_t *chunk, size_t chunk_size);
+
+/*! \brief Tells if there remains something of the changeset
+ * to be serialized into next chunk(s) yet. */
+bool serialize_unfinished(serialize_ctx_t *ctx);
+
+/*!
+ * \brief Free serialization context.
+ *
+ * \return KNOT_E* if there were errors during serialization.
+ */
+int serialize_deinit(serialize_ctx_t *ctx);
+
+/*!
+ * \brief Returns size of serialized changeset from zone diff.
+ *
+ * \warning Not accurate! This is an upper bound, suitable for policy enforcement etc.
+ *
+ * \param[in] diff Zone diff structure to create changeset from.
+ *
+ * \return Size of the resulting changeset.
+ */
+size_t zone_diff_serialized_size(zone_diff_t diff);
+
+/*!
+ * \brief Returns size of changeset in serialized form.
+ *
+ * \param[in] ch Changeset whose size we want to compute.
+ *
+ * \return Size of the changeset.
+ */
+size_t changeset_serialized_size(const changeset_t *ch);
+
+/*!
+ * \brief Simply serialize RRset w/o any chunking.
+ *
+ * \param wire
+ * \param rrset
+ *
+ * \return KNOT_E*
+ */
+int serialize_rrset(wire_ctx_t *wire, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Simply deserialize RRset w/o any chunking.
+ *
+ * \param wire
+ * \param rrset
+ *
+ * \return KNOT_E*
+ */
+int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset);
+
+/*!
+ * \brief Space needed to serialize RRset.
+ *
+ * \param rrset RRset.
+ *
+ * \return RRset binary size.
+ */
+size_t rrset_serialized_size(const knot_rrset_t *rrset);
diff --git a/src/knot/modules/cookies/Makefile.inc b/src/knot/modules/cookies/Makefile.inc
new file mode 100644
index 0000000..0f0b342
--- /dev/null
+++ b/src/knot/modules/cookies/Makefile.inc
@@ -0,0 +1,13 @@
+knot_modules_cookies_la_SOURCES = knot/modules/cookies/cookies.c
+EXTRA_DIST += knot/modules/cookies/cookies.rst
+
+if STATIC_MODULE_cookies
+libknotd_la_SOURCES += $(knot_modules_cookies_la_SOURCES)
+endif
+
+if SHARED_MODULE_cookies
+knot_modules_cookies_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_cookies_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_cookies_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/cookies.la
+endif
diff --git a/src/knot/modules/cookies/cookies.c b/src/knot/modules/cookies/cookies.c
new file mode 100644
index 0000000..34c4b22
--- /dev/null
+++ b/src/knot/modules/cookies/cookies.c
@@ -0,0 +1,308 @@
+/* Copyright (C) 2022 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 <pthread.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "knot/include/module.h"
+#include "libknot/libknot.h"
+#include "contrib/string.h"
+#include "libdnssec/random.h"
+
+#ifdef HAVE_ATOMIC
+#define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
+#define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
+#define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
+#else
+#define ATOMIC_SET(dst, val) ((dst) = (val))
+#define ATOMIC_GET(src) (src)
+#define ATOMIC_ADD(dst, val) ((dst) += (val))
+#endif
+
+#define BADCOOKIE_CTR_INIT 1
+
+#define MOD_SECRET_LIFETIME "\x0F""secret-lifetime"
+#define MOD_BADCOOKIE_SLIP "\x0E""badcookie-slip"
+#define MOD_SECRET "\x06""secret"
+
+const yp_item_t cookies_conf[] = {
+ { MOD_SECRET_LIFETIME, YP_TINT, YP_VINT = { 1, 36*24*3600, 26*3600, YP_STIME } },
+ { MOD_BADCOOKIE_SLIP, YP_TINT, YP_VINT = { 1, INT32_MAX, 1 } },
+ { MOD_SECRET, YP_THEX, YP_VNONE },
+ { NULL }
+};
+
+int cookies_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t conf = knotd_conf_check_item(args, MOD_SECRET);
+ if (conf.count == 1 && conf.single.data_len != KNOT_EDNS_COOKIE_SECRET_SIZE) {
+ args->err_str = "the length of the cookie secret "
+ "MUST BE 16 bytes (32 HEX characters)";
+ return KNOT_EINVAL;
+ }
+ return KNOT_EOK;
+}
+
+typedef struct {
+ struct {
+ uint64_t variable;
+ uint64_t constant;
+ } secret;
+ pthread_t update_secret;
+ uint32_t secret_lifetime;
+ uint32_t badcookie_slip;
+ uint16_t badcookie_ctr; // Counter for BADCOOKIE answers.
+} cookies_ctx_t;
+
+static void update_ctr(cookies_ctx_t *ctx)
+{
+ assert(ctx);
+
+ if (ATOMIC_GET(ctx->badcookie_ctr) < ctx->badcookie_slip) {
+ ATOMIC_ADD(ctx->badcookie_ctr, 1);
+ } else {
+ ATOMIC_SET(ctx->badcookie_ctr, BADCOOKIE_CTR_INIT);
+ }
+}
+
+static int generate_secret(cookies_ctx_t *ctx)
+{
+ assert(ctx);
+
+ // Generate a new variable part of the server secret.
+ uint64_t new_secret;
+ int ret = dnssec_random_buffer((uint8_t *)&new_secret, sizeof(new_secret));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ATOMIC_SET(ctx->secret.variable, new_secret);
+
+ return KNOT_EOK;
+}
+
+static void *update_secret(void *data)
+{
+ knotd_mod_t *mod = (knotd_mod_t *)data;
+ cookies_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ while (true) {
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ int ret = generate_secret(ctx);
+ if (ret != KNOT_EOK) {
+ knotd_mod_log(mod, LOG_ERR, "failed to generate a secret (%s)",
+ knot_strerror(ret));
+ }
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+ sleep(ctx->secret_lifetime);
+ }
+
+ return NULL;
+}
+
+// Inserts the current cookie option into the answer's OPT RR.
+static int put_cookie(knotd_qdata_t *qdata, knot_pkt_t *pkt,
+ const knot_edns_cookie_t *cc, const knot_edns_cookie_t *sc)
+{
+ assert(qdata && pkt && cc && sc);
+
+ uint8_t *option = NULL;
+ uint16_t option_size = knot_edns_cookie_size(cc, sc);
+ int ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_COOKIE,
+ option_size, &option, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_edns_cookie_write(option, option_size, cc, sc);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Reserve extra space for the cookie option.
+ ret = knot_pkt_reserve(pkt, KNOT_EDNS_OPTION_HDRLEN + option_size);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static knotd_state_t cookies_process(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ cookies_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ // Check if the cookie option is present.
+ uint8_t *cookie_opt = knot_pkt_edns_option(qdata->query,
+ KNOT_EDNS_OPTION_COOKIE);
+ if (cookie_opt == NULL) {
+ return state;
+ }
+
+ // Increment the statistics counter.
+ knotd_mod_stats_incr(mod, qdata->params->thread_id, 0, 0, 1);
+
+ knot_edns_cookie_t cc;
+ knot_edns_cookie_t sc;
+
+ // Parse the cookie from wireformat.
+ const uint8_t *data = knot_edns_opt_get_data(cookie_opt);
+ uint16_t data_len = knot_edns_opt_get_length(cookie_opt);
+ int ret = knot_edns_cookie_parse(&cc, &sc, data, data_len);
+ if (ret != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ return KNOTD_STATE_FAIL;
+ }
+
+ // Prepare data for server cookie computation.
+ knot_edns_cookie_params_t params = {
+ .version = KNOT_EDNS_COOKIE_VERSION,
+ .timestamp = (uint32_t)time(NULL),
+ .lifetime_before = 3600,
+ .lifetime_after = 300,
+ .client_addr = knotd_qdata_remote_addr(qdata)
+ };
+ uint64_t current_secret = ATOMIC_GET(ctx->secret.variable);
+ memcpy(params.secret, &current_secret, sizeof(current_secret));
+ memcpy(params.secret + sizeof(current_secret), &ctx->secret.constant,
+ sizeof(ctx->secret.constant));
+
+ // Compare server cookie.
+ ret = knot_edns_cookie_server_check(&sc, &cc, &params);
+ if (ret != KNOT_EOK) {
+ // Established connection (TCP or QUIC) is taken into account,
+ // so a normal response is provided.
+ if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) {
+ if (knot_edns_cookie_server_generate(&sc, &cc, &params) != KNOT_EOK ||
+ put_cookie(qdata, pkt, &cc, &sc) != KNOT_EOK)
+ {
+ return KNOTD_STATE_FAIL;
+ }
+
+ return state;
+ } else if (ATOMIC_GET(ctx->badcookie_ctr) > BADCOOKIE_CTR_INIT) {
+ // Silently drop the response.
+ update_ctr(ctx);
+ knotd_mod_stats_incr(mod, qdata->params->thread_id, 1, 0, 1);
+ return KNOTD_STATE_NOOP;
+ } else {
+ if (ctx->badcookie_slip > 1) {
+ update_ctr(ctx);
+ }
+
+ if (knot_edns_cookie_server_generate(&sc, &cc, &params) != KNOT_EOK ||
+ put_cookie(qdata, pkt, &cc, &sc) != KNOT_EOK)
+ {
+ return KNOTD_STATE_FAIL;
+ }
+
+ qdata->rcode = KNOT_RCODE_BADCOOKIE;
+ return KNOTD_STATE_FAIL;
+ }
+ }
+
+ // Reuse valid server cookie.
+ ret = put_cookie(qdata, pkt, &cc, &sc);
+ if (ret != KNOT_EOK) {
+ return KNOTD_STATE_FAIL;
+ }
+
+ // Set the valid cookie flag.
+ qdata->params->flags |= KNOTD_QUERY_FLAG_COOKIE;
+
+ return state;
+}
+
+int cookies_load(knotd_mod_t *mod)
+{
+ // Create module context.
+ cookies_ctx_t *ctx = calloc(1, sizeof(cookies_ctx_t));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Initialize BADCOOKIE counter.
+ ctx->badcookie_ctr = BADCOOKIE_CTR_INIT;
+
+ // Set up configurable items.
+ knotd_conf_t conf = knotd_conf_mod(mod, MOD_BADCOOKIE_SLIP);
+ ctx->badcookie_slip = conf.single.integer;
+
+ // Set up statistics counters.
+ int ret = knotd_mod_stats_add(mod, "presence", 1, NULL);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ ret = knotd_mod_stats_add(mod, "dropped", 1, NULL);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ // Store module context before rollover thread is created.
+ knotd_mod_ctx_set(mod, ctx);
+
+ // Initialize the server secret.
+ conf = knotd_conf_mod(mod, MOD_SECRET);
+ if (conf.count == 1) {
+ assert(conf.single.data_len == KNOT_EDNS_COOKIE_SECRET_SIZE);
+ memcpy(&ctx->secret, conf.single.data, conf.single.data_len);
+ assert(ctx->secret_lifetime == 0);
+ } else {
+ ret = dnssec_random_buffer((uint8_t *)&ctx->secret, sizeof(ctx->secret));
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ conf = knotd_conf_mod(mod, MOD_SECRET_LIFETIME);
+ ctx->secret_lifetime = conf.single.integer;
+
+ // Start the secret rollover thread.
+ if (pthread_create(&ctx->update_secret, NULL, update_secret, (void *)mod)) {
+ knotd_mod_log(mod, LOG_ERR, "failed to create the secret rollover thread");
+ free(ctx);
+ return KNOT_ERROR;
+ }
+ }
+
+#ifndef HAVE_ATOMIC
+ knotd_mod_log(mod, LOG_WARNING, "the module might work slightly wrong on this platform");
+ ctx->badcookie_slip = 1;
+#endif
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, cookies_process);
+}
+
+void cookies_unload(knotd_mod_t *mod)
+{
+ cookies_ctx_t *ctx = knotd_mod_ctx(mod);
+ if (ctx->secret_lifetime > 0) {
+ (void)pthread_cancel(ctx->update_secret);
+ (void)pthread_join(ctx->update_secret, NULL);
+ }
+ memzero(&ctx->secret, sizeof(ctx->secret));
+ free(ctx);
+}
+
+KNOTD_MOD_API(cookies, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
+ cookies_load, cookies_unload, cookies_conf, cookies_conf_check);
diff --git a/src/knot/modules/cookies/cookies.rst b/src/knot/modules/cookies/cookies.rst
new file mode 100644
index 0000000..74bffe5
--- /dev/null
+++ b/src/knot/modules/cookies/cookies.rst
@@ -0,0 +1,110 @@
+.. _mod-cookies:
+
+``cookies`` — DNS Cookies
+=========================
+
+DNS Cookies (:rfc:`7873`) is a lightweight security mechanism against
+denial-of-service and amplification attacks. The server keeps a secret value
+(the Server Secret), which is used to generate a cookie, which is sent to
+the client in the OPT RR. The server then verifies the authenticity of the client
+by the presence of a correct cookie. Both the server and the client have to
+support DNS Cookies, otherwise they are not used.
+
+.. NOTE::
+ This module introduces two statistics counters:
+
+ - ``presence`` – The number of queries containing the COOKIE option.
+ - ``dropped`` – The number of dropped queries due to the slip limit.
+
+.. WARNING::
+ For effective module operation the :ref:`RRL<mod-rrl>` module must also
+ be enabled and configured after :ref:`Cookies<mod-cookies>`. See
+ :ref:`query-modules` how to configure modules.
+
+Example
+-------
+
+It is recommended to enable DNS Cookies globally, not per zone. The module may be used without any further configuration.
+
+::
+
+ template:
+ - id: default
+ global-module: mod-cookies # Enable DNS Cookies globally
+
+Module configuration may be supplied if necessary.
+
+::
+
+ mod-cookies:
+ - id: default
+ secret-lifetime: 30h # The Server Secret is regenerated every 30 hours
+ badcookie-slip: 3 # The server replies only to every third query with a wrong cookie
+
+ template:
+ - id: default
+ global-module: mod-cookies/default # Enable DNS Cookies globally
+
+The value of the Server Secret may also be managed manually using the :ref:`mod-cookies_secret` option. In this case
+the server does not automatically regenerate the Server Secret.
+
+::
+
+ mod-cookies:
+ - id: default
+ secret: 0xdeadbeefdeadbeefdeadbeefdeadbeef
+
+Module reference
+----------------
+
+::
+
+ mod-cookies:
+ - id: STR
+ secret-lifetime: TIME
+ badcookie-slip: INT
+ secret: STR | HEXSTR
+
+.. _mod-cookies_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-cookies_secret-lifetime:
+
+secret-lifetime
+...............
+
+This option configures in seconds how often the Server Secret is regenerated.
+The maximum allowed value is 36 days (:rfc:`7873#section-7.1`).
+
+*Default:* ``26h`` (26 hours)
+
+.. _mod-cookies_badcookie-slip:
+
+badcookie-slip
+..............
+
+This option configures how often the server responds to queries containing
+an invalid cookie by sending them the correct cookie.
+
+- The value **1** means that the server responds to every query.
+- The value **2** means that the server responds to every second query with
+ an invalid cookie, the rest of the queries is dropped.
+- The value **N > 2** means that the server responds to every N\ :sup:`th`
+ query with an invalid cookie, the rest of the queries is dropped.
+
+*Default:* ``1``
+
+.. _mod-cookies_secret:
+
+secret
+......
+
+Use this option to set the Server Secret manually. If this option is used, the
+Server Secret remains the same until changed manually and the :ref:`mod-cookies_secret-lifetime` option is ignored.
+The size of the Server Secret currently MUST BE 16 bytes, or 32 hexadecimal characters.
+
+*Default:* not set
diff --git a/src/knot/modules/dnsproxy/Makefile.inc b/src/knot/modules/dnsproxy/Makefile.inc
new file mode 100644
index 0000000..86f1577
--- /dev/null
+++ b/src/knot/modules/dnsproxy/Makefile.inc
@@ -0,0 +1,13 @@
+knot_modules_dnsproxy_la_SOURCES = knot/modules/dnsproxy/dnsproxy.c
+EXTRA_DIST += knot/modules/dnsproxy/dnsproxy.rst
+
+if STATIC_MODULE_dnsproxy
+libknotd_la_SOURCES += $(knot_modules_dnsproxy_la_SOURCES)
+endif
+
+if SHARED_MODULE_dnsproxy
+knot_modules_dnsproxy_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_dnsproxy_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_dnsproxy_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/dnsproxy.la
+endif
diff --git a/src/knot/modules/dnsproxy/dnsproxy.c b/src/knot/modules/dnsproxy/dnsproxy.c
new file mode 100644
index 0000000..b44b136
--- /dev/null
+++ b/src/knot/modules/dnsproxy/dnsproxy.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 2021 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 "contrib/net.h"
+#include "knot/include/module.h"
+#include "knot/conf/schema.h"
+#include "knot/query/capture.h" // Forces static module!
+#include "knot/query/requestor.h" // Forces static module!
+
+#define MOD_REMOTE "\x06""remote"
+#define MOD_ADDRESS "\x07""address"
+#define MOD_TCP_FASTOPEN "\x0C""tcp-fastopen"
+#define MOD_TIMEOUT "\x07""timeout"
+#define MOD_FALLBACK "\x08""fallback"
+#define MOD_CATCH_NXDOMAIN "\x0E""catch-nxdomain"
+
+const yp_item_t dnsproxy_conf[] = {
+ { MOD_REMOTE, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE,
+ { knotd_conf_check_ref } },
+ { MOD_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 500 } },
+ { MOD_ADDRESS, YP_TNET, YP_VNONE, YP_FMULTI },
+ { MOD_FALLBACK, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_TCP_FASTOPEN, YP_TBOOL, YP_VNONE },
+ { MOD_CATCH_NXDOMAIN, YP_TBOOL, YP_VNONE },
+ { NULL }
+};
+
+int dnsproxy_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t rmt = knotd_conf_check_item(args, MOD_REMOTE);
+ if (rmt.count == 0) {
+ args->err_str = "no remote server specified";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+typedef struct {
+ struct sockaddr_storage remote;
+ struct sockaddr_storage via;
+ knotd_conf_t addr;
+ bool fallback;
+ bool tfo;
+ bool catch_nxdomain;
+ int timeout;
+} dnsproxy_t;
+
+static knotd_state_t dnsproxy_fwd(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ dnsproxy_t *proxy = knotd_mod_ctx(mod);
+
+ /* Forward only queries ending with REFUSED (no zone) or NXDOMAIN (if configured) */
+ if (proxy->fallback && !(qdata->rcode == KNOT_RCODE_REFUSED ||
+ (qdata->rcode == KNOT_RCODE_NXDOMAIN && proxy->catch_nxdomain))) {
+ return state;
+ }
+
+ /* Forward from specified addresses only if configured. */
+ if (proxy->addr.count > 0) {
+ const struct sockaddr_storage *addr = knotd_qdata_remote_addr(qdata);
+ if (!knotd_conf_addr_range_match(&proxy->addr, addr)) {
+ return state;
+ }
+ }
+
+ /* Forward also original TSIG. */
+ if (qdata->query->tsig_rr != NULL && !proxy->fallback) {
+ knot_tsig_append(qdata->query->wire, &qdata->query->size,
+ qdata->query->max_size, qdata->query->tsig_rr);
+ }
+
+ /* Capture layer context. */
+ const knot_layer_api_t *capture = query_capture_api();
+ struct capture_param capture_param = {
+ .sink = pkt
+ };
+
+ /* Create a forwarding request. */
+ knot_requestor_t re;
+ int ret = knot_requestor_init(&re, capture, &capture_param, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return state; /* Ignore, not enough memory. */
+ }
+
+ knot_request_flag_t flags = KNOT_REQUEST_NONE;
+ if (!net_is_stream(qdata->params->socket)) {
+ flags = KNOT_REQUEST_UDP;
+ } else if (proxy->tfo) {
+ flags = KNOT_REQUEST_TFO;
+ }
+ const struct sockaddr_storage *dst = &proxy->remote;
+ const struct sockaddr_storage *src = &proxy->via;
+ knot_request_t *req = knot_request_make(re.mm, dst, src, qdata->query, NULL,
+ flags);
+ if (req == NULL) {
+ knot_requestor_clear(&re);
+ return state; /* Ignore, not enough memory. */
+ }
+
+ /* Forward request. */
+ ret = knot_requestor_exec(&re, req, proxy->timeout);
+
+ knot_request_free(req, re.mm);
+ knot_requestor_clear(&re);
+
+ /* Check result. */
+ if (ret != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_STATE_FAIL; /* Forwarding failed, SERVFAIL. */
+ } else {
+ qdata->rcode = knot_pkt_ext_rcode(pkt);
+ }
+
+ /* Respond also with TSIG. */
+ if (pkt->tsig_rr != NULL && !proxy->fallback) {
+ knot_tsig_append(pkt->wire, &pkt->size, pkt->max_size, pkt->tsig_rr);
+ }
+
+ return (proxy->fallback ? KNOTD_STATE_DONE : KNOTD_STATE_FINAL);
+}
+
+int dnsproxy_load(knotd_mod_t *mod)
+{
+ dnsproxy_t *proxy = calloc(1, sizeof(*proxy));
+ if (proxy == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knotd_conf_t remote = knotd_conf_mod(mod, MOD_REMOTE);
+ knotd_conf_t conf = knotd_conf(mod, C_RMT, C_ADDR, &remote);
+ if (conf.count > 0) {
+ proxy->remote = conf.multi[0].addr;
+ knotd_conf_free(&conf);
+ }
+ conf = knotd_conf(mod, C_RMT, C_VIA, &remote);
+ if (conf.count > 0) {
+ proxy->via = conf.multi[0].addr;
+ knotd_conf_free(&conf);
+ }
+
+ proxy->addr = knotd_conf_mod(mod, MOD_ADDRESS);
+
+ conf = knotd_conf_mod(mod, MOD_TIMEOUT);
+ proxy->timeout = conf.single.integer;
+
+ conf = knotd_conf_mod(mod, MOD_FALLBACK);
+ proxy->fallback = conf.single.boolean;
+
+ conf = knotd_conf_mod(mod, MOD_TCP_FASTOPEN);
+ proxy->tfo = conf.single.boolean;
+
+ conf = knotd_conf_mod(mod, MOD_CATCH_NXDOMAIN);
+ proxy->catch_nxdomain = conf.single.boolean;
+
+ knotd_mod_ctx_set(mod, proxy);
+
+ if (proxy->fallback) {
+ return knotd_mod_hook(mod, KNOTD_STAGE_END, dnsproxy_fwd);
+ } else {
+ return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, dnsproxy_fwd);
+ }
+}
+
+void dnsproxy_unload(knotd_mod_t *mod)
+{
+ dnsproxy_t *ctx = knotd_mod_ctx(mod);
+ if (ctx != NULL) {
+ knotd_conf_free(&ctx->addr);
+ }
+ free(ctx);
+}
+
+KNOTD_MOD_API(dnsproxy, KNOTD_MOD_FLAG_SCOPE_ANY,
+ dnsproxy_load, dnsproxy_unload, dnsproxy_conf, dnsproxy_conf_check);
diff --git a/src/knot/modules/dnsproxy/dnsproxy.rst b/src/knot/modules/dnsproxy/dnsproxy.rst
new file mode 100644
index 0000000..9493738
--- /dev/null
+++ b/src/knot/modules/dnsproxy/dnsproxy.rst
@@ -0,0 +1,125 @@
+.. _mod-dnsproxy:
+
+``dnsproxy`` – Tiny DNS proxy
+=============================
+
+The module forwards all queries, or all specific zone queries if configured
+per zone, to the indicated server for resolution. If configured in the fallback
+mode, only locally unsatisfied queries are forwarded. I.e. a tiny DNS proxy.
+There are several uses of this feature:
+
+* A substitute public-facing server in front of the real one
+* Local zones (poor man's "views"), rest is forwarded to the public-facing server
+* Using the fallback to forward queries to a resolver
+* etc.
+
+.. NOTE::
+ The module does not alter the query/response as the resolver would,
+ and the original transport protocol is kept as well.
+
+Example
+-------
+
+The configuration is straightforward and just a single remote server is
+required::
+
+ remote:
+ - id: hidden
+ address: 10.0.1.1
+
+ mod-dnsproxy:
+ - id: default
+ remote: hidden
+ fallback: on
+
+ template:
+ - id: default
+ global-module: mod-dnsproxy/default
+
+ zone:
+ - domain: local.zone
+
+When clients query for anything in the ``local.zone``, they will be
+responded to locally. The rest of the requests will be forwarded to the
+specified server (``10.0.1.1`` in this case).
+
+Module reference
+----------------
+
+::
+
+ mod-dnsproxy:
+ - id: STR
+ remote: remote_id
+ timeout: INT
+ address: ADDR[/INT] | ADDR-ADDR ...
+ fallback: BOOL
+ tcp-fastopen: BOOL
+ catch-nxdomain: BOOL
+
+.. _mod-dnsproxy_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-dnsproxy_remote:
+
+remote
+......
+
+A :ref:`reference<remote_id>` to a remote server where the queries are
+forwarded to.
+
+*Required*
+
+.. _mod-dnsproxy_timeout:
+
+timeout
+.......
+
+A remote response timeout in milliseconds.
+
+*Default:* ``500`` (milliseconds)
+
+.. _mod-dnsproxy_address:
+
+address
+.......
+
+An optional list of allowed ranges and/or subnets for query's source address.
+If the query's address does not fall into any of the configured ranges, the
+query isn't forwarded.
+
+*Default:* not set
+
+.. _mod-dnsproxy_fallback:
+
+fallback
+........
+
+If enabled, locally unsatisfied queries leading to REFUSED (no zone) are forwarded.
+If disabled, all queries are directly forwarded without any local attempts
+to resolve them.
+
+*Default:* ``on``
+
+.. _mod-dnsproxy_tcp-fastopen:
+
+tcp-fastopen
+............
+
+If enabled, TCP Fast Open is used when forwarding TCP queries.
+
+*Default:* ``off``
+
+.. _mod-dnsproxy_catch-nxdomain:
+
+catch-nxdomain
+..............
+
+If enabled, locally unsatisfied queries leading to NXDOMAIN are forwarded.
+This option is only relevant in the fallback mode.
+
+*Default:* ``off``
diff --git a/src/knot/modules/dnstap/Makefile.inc b/src/knot/modules/dnstap/Makefile.inc
new file mode 100644
index 0000000..e69b56c
--- /dev/null
+++ b/src/knot/modules/dnstap/Makefile.inc
@@ -0,0 +1,15 @@
+knot_modules_dnstap_la_SOURCES = knot/modules/dnstap/dnstap.c
+EXTRA_DIST += knot/modules/dnstap/dnstap.rst
+
+if STATIC_MODULE_dnstap
+libknotd_la_SOURCES += $(knot_modules_dnstap_la_SOURCES)
+libknotd_la_CPPFLAGS += $(DNSTAP_CFLAGS)
+libknotd_la_LIBADD += $(libdnstap_LIBS)
+endif
+
+if SHARED_MODULE_dnstap
+knot_modules_dnstap_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_dnstap_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(DNSTAP_CFLAGS)
+knot_modules_dnstap_la_LIBADD = $(libdnstap_LIBS)
+pkglib_LTLIBRARIES += knot/modules/dnstap.la
+endif
diff --git a/src/knot/modules/dnstap/dnstap.c b/src/knot/modules/dnstap/dnstap.c
new file mode 100644
index 0000000..6119ccd
--- /dev/null
+++ b/src/knot/modules/dnstap/dnstap.c
@@ -0,0 +1,338 @@
+/* Copyright (C) 2022 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 <netinet/in.h>
+#include <sys/socket.h>
+
+#include "contrib/dnstap/dnstap.h"
+#include "contrib/dnstap/dnstap.pb-c.h"
+#include "contrib/dnstap/message.h"
+#include "contrib/dnstap/writer.h"
+#include "contrib/time.h"
+#include "knot/include/module.h"
+
+#define MOD_SINK "\x04""sink"
+#define MOD_IDENTITY "\x08""identity"
+#define MOD_VERSION "\x07""version"
+#define MOD_QUERIES "\x0B""log-queries"
+#define MOD_RESPONSES "\x0D""log-responses"
+#define MOD_WITH_QUERIES "\x16""responses-with-queries"
+
+const yp_item_t dnstap_conf[] = {
+ { MOD_SINK, YP_TSTR, YP_VNONE },
+ { MOD_IDENTITY, YP_TSTR, YP_VNONE },
+ { MOD_VERSION, YP_TSTR, YP_VNONE },
+ { MOD_QUERIES, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_RESPONSES, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_WITH_QUERIES, YP_TBOOL, YP_VBOOL = { false } },
+ { NULL }
+};
+
+int dnstap_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t sink = knotd_conf_check_item(args, MOD_SINK);
+ if (sink.count == 0 || sink.single.string[0] == '\0') {
+ args->err_str = "no sink specified";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+typedef struct {
+ struct fstrm_iothr *iothread;
+ char *identity;
+ size_t identity_len;
+ char *version;
+ size_t version_len;
+ bool with_queries;
+} dnstap_ctx_t;
+
+static knotd_state_t log_message(knotd_state_t state, const knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ /* Skip empty packet. */
+ if (state == KNOTD_STATE_NOOP) {
+ return state;
+ }
+
+ dnstap_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ struct fstrm_iothr_queue *ioq =
+ fstrm_iothr_get_input_queue_idx(ctx->iothread, qdata->params->thread_id);
+
+ /* Unless we want to measure the time it takes to process each query,
+ * we can treat Q/R times the same. */
+ struct timespec tv = { 0 };
+ clock_gettime(CLOCK_REALTIME, &tv);
+
+ /* Determine query / response. */
+ Dnstap__Message__Type msgtype = DNSTAP__MESSAGE__TYPE__AUTH_QUERY;
+ if (knot_wire_get_qr(pkt->wire)) {
+ msgtype = DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE;
+ }
+
+ /* Determine whether we run on UDP/TCP. */
+ /* TODO: distinguish QUIC. */
+ int protocol = IPPROTO_UDP;
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_TCP) {
+ protocol = IPPROTO_TCP;
+ }
+
+ /* Create a dnstap message. */
+ struct sockaddr_storage buff;
+ Dnstap__Message msg;
+ int ret = dt_message_fill(&msg, msgtype,
+ (const struct sockaddr *)knotd_qdata_remote_addr(qdata),
+ (const struct sockaddr *)knotd_qdata_local_addr(qdata, &buff),
+ protocol, pkt->wire, pkt->size, &tv);
+ if (ret != KNOT_EOK) {
+ return state;
+ }
+
+ Dnstap__Dnstap dnstap = DNSTAP__DNSTAP__INIT;
+ dnstap.type = DNSTAP__DNSTAP__TYPE__MESSAGE;
+ dnstap.message = &msg;
+
+ /* Set message version and identity. */
+ if (ctx->identity_len > 0) {
+ dnstap.identity.data = (uint8_t *)ctx->identity;
+ dnstap.identity.len = ctx->identity_len;
+ dnstap.has_identity = 1;
+ }
+ if (ctx->version_len > 0) {
+ dnstap.version.data = (uint8_t *)ctx->version;
+ dnstap.version.len = ctx->version_len;
+ dnstap.has_version = 1;
+ }
+
+ /* Also add query message if 'responses-with-queries' is enabled and this is a response. */
+ if (ctx->with_queries &&
+ msgtype == DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE &&
+ qdata->query != NULL)
+ {
+ msg.query_message.len = qdata->query->size;
+ msg.query_message.data = qdata->query->wire;
+ msg.has_query_message = 1;
+ }
+
+ /* Pack the message. */
+ uint8_t *frame = NULL;
+ size_t size = 0;
+ dt_pack(&dnstap, &frame, &size);
+ if (frame == NULL) {
+ return state;
+ }
+
+ /* Submit a request. */
+ fstrm_res res = fstrm_iothr_submit(ctx->iothread, ioq, frame, size,
+ fstrm_free_wrapper, NULL);
+ if (res != fstrm_res_success) {
+ free(frame);
+ return state;
+ }
+
+ return state;
+}
+
+/*! \brief Submit message - query. */
+static knotd_state_t dnstap_message_log_query(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(qdata);
+
+ return log_message(state, qdata->query, qdata, mod);
+}
+
+/*! \brief Submit message - response. */
+static knotd_state_t dnstap_message_log_response(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ return log_message(state, pkt, qdata, mod);
+}
+
+/*! \brief Create a UNIX socket sink. */
+static struct fstrm_writer* dnstap_unix_writer(const char *path)
+{
+ struct fstrm_unix_writer_options *opt = NULL;
+ struct fstrm_writer_options *wopt = NULL;
+ struct fstrm_writer *writer = NULL;
+
+ opt = fstrm_unix_writer_options_init();
+ if (opt == NULL) {
+ goto finish;
+ }
+ fstrm_unix_writer_options_set_socket_path(opt, path);
+
+ wopt = fstrm_writer_options_init();
+ if (wopt == NULL) {
+ goto finish;
+ }
+ fstrm_writer_options_add_content_type(wopt, DNSTAP_CONTENT_TYPE,
+ strlen(DNSTAP_CONTENT_TYPE));
+ writer = fstrm_unix_writer_init(opt, wopt);
+
+finish:
+ fstrm_unix_writer_options_destroy(&opt);
+ fstrm_writer_options_destroy(&wopt);
+ return writer;
+}
+
+/*! \brief Create a basic file writer sink. */
+static struct fstrm_writer* dnstap_file_writer(const char *path)
+{
+ struct fstrm_file_options *fopt = NULL;
+ struct fstrm_writer_options *wopt = NULL;
+ struct fstrm_writer *writer = NULL;
+
+ fopt = fstrm_file_options_init();
+ if (fopt == NULL) {
+ goto finish;
+ }
+ fstrm_file_options_set_file_path(fopt, path);
+
+ wopt = fstrm_writer_options_init();
+ if (wopt == NULL) {
+ goto finish;
+ }
+ fstrm_writer_options_add_content_type(wopt, DNSTAP_CONTENT_TYPE,
+ strlen(DNSTAP_CONTENT_TYPE));
+ writer = fstrm_file_writer_init(fopt, wopt);
+
+finish:
+ fstrm_file_options_destroy(&fopt);
+ fstrm_writer_options_destroy(&wopt);
+ return writer;
+}
+
+/*! \brief Create a log sink according to the path string. */
+static struct fstrm_writer* dnstap_writer(const char *path)
+{
+ const char *prefix = "unix:";
+ const size_t prefix_len = strlen(prefix);
+
+ /* UNIX socket prefix. */
+ if (strlen(path) > prefix_len && strncmp(path, prefix, prefix_len) == 0) {
+ return dnstap_unix_writer(path + prefix_len);
+ }
+
+ return dnstap_file_writer(path);
+}
+
+int dnstap_load(knotd_mod_t *mod)
+{
+ /* Create dnstap context. */
+ dnstap_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Set identity. */
+ knotd_conf_t conf = knotd_conf_mod(mod, MOD_IDENTITY);
+ if (conf.count == 1) {
+ ctx->identity = (conf.single.string != NULL) ?
+ strdup(conf.single.string) : NULL;
+ } else {
+ knotd_conf_t host = knotd_conf_env(mod, KNOTD_CONF_ENV_HOSTNAME);
+ ctx->identity = strdup(host.single.string);
+ }
+ ctx->identity_len = (ctx->identity != NULL) ? strlen(ctx->identity) : 0;
+
+ /* Set version. */
+ conf = knotd_conf_mod(mod, MOD_VERSION);
+ if (conf.count == 1) {
+ ctx->version = (conf.single.string != NULL) ?
+ strdup(conf.single.string) : NULL;
+ } else {
+ knotd_conf_t version = knotd_conf_env(mod, KNOTD_CONF_ENV_VERSION);
+ ctx->version = strdup(version.single.string);
+ }
+ ctx->version_len = (ctx->version != NULL) ? strlen(ctx->version) : 0;
+
+ /* Set responses-with-queries. */
+ conf = knotd_conf_mod(mod, MOD_WITH_QUERIES);
+ ctx->with_queries = conf.single.boolean;
+
+ /* Set sink. */
+ conf = knotd_conf_mod(mod, MOD_SINK);
+ const char *sink = conf.single.string;
+
+ /* Set log_queries. */
+ conf = knotd_conf_mod(mod, MOD_QUERIES);
+ const bool log_queries = conf.single.boolean;
+
+ /* Set log_responses. */
+ conf = knotd_conf_mod(mod, MOD_RESPONSES);
+ const bool log_responses = conf.single.boolean;
+
+ /* Initialize the writer and the options. */
+ struct fstrm_writer *writer = dnstap_writer(sink);
+ if (writer == NULL) {
+ goto fail;
+ }
+
+ struct fstrm_iothr_options *opt = fstrm_iothr_options_init();
+ if (opt == NULL) {
+ fstrm_writer_destroy(&writer);
+ goto fail;
+ }
+
+ /* Initialize queues. */
+ fstrm_iothr_options_set_num_input_queues(opt, knotd_mod_threads(mod));
+
+ /* Create the I/O thread. */
+ ctx->iothread = fstrm_iothr_init(opt, &writer);
+ fstrm_iothr_options_destroy(&opt);
+ if (ctx->iothread == NULL) {
+ fstrm_writer_destroy(&writer);
+ goto fail;
+ }
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ /* Hook to the query plan. */
+ if (log_queries) {
+ knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, dnstap_message_log_query);
+ }
+ if (log_responses) {
+ knotd_mod_hook(mod, KNOTD_STAGE_END, dnstap_message_log_response);
+ }
+
+ return KNOT_EOK;
+fail:
+ knotd_mod_log(mod, LOG_ERR, "failed to init sink '%s'", sink);
+
+ free(ctx->identity);
+ free(ctx->version);
+ free(ctx);
+
+ return KNOT_ENOMEM;
+}
+
+void dnstap_unload(knotd_mod_t *mod)
+{
+ dnstap_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ fstrm_iothr_destroy(&ctx->iothread);
+ free(ctx->identity);
+ free(ctx->version);
+ free(ctx);
+}
+
+KNOTD_MOD_API(dnstap, KNOTD_MOD_FLAG_SCOPE_ANY,
+ dnstap_load, dnstap_unload, dnstap_conf, dnstap_conf_check);
diff --git a/src/knot/modules/dnstap/dnstap.rst b/src/knot/modules/dnstap/dnstap.rst
new file mode 100644
index 0000000..591bda5
--- /dev/null
+++ b/src/knot/modules/dnstap/dnstap.rst
@@ -0,0 +1,113 @@
+.. _mod-dnstap:
+
+``dnstap`` – Dnstap traffic logging
+===================================
+
+A module for query and response logging based on the dnstap_ library.
+You can capture either all or zone-specific queries and responses; usually
+you want to do the former.
+
+Example
+-------
+
+The configuration comprises only a :ref:`mod-dnstap_sink` path parameter,
+which can be either a file or a UNIX socket::
+
+ mod-dnstap:
+ - id: capture_all
+ sink: /tmp/capture.tap
+
+ template:
+ - id: default
+ global-module: mod-dnstap/capture_all
+
+.. NOTE::
+ To be able to use a Unix socket you need an external program to create it.
+ Knot DNS connects to it as a client using the libfstrm library. It operates
+ exactly like syslog.
+
+.. NOTE::
+ Dnstap log files can also be created or read using :doc:`kdig<man_kdig>`.
+
+.. _dnstap: https://dnstap.info/
+
+Module reference
+----------------
+
+For all queries logging, use this module in the *default* template. For
+zone-specific logging, use this module in the proper zone configuration.
+
+::
+
+ mod-dnstap:
+ - id: STR
+ sink: STR
+ identity: STR
+ version: STR
+ log-queries: BOOL
+ log-responses: BOOL
+ responses-with-queries: BOOL
+
+.. _mod-dnstap_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-dnstap_sink:
+
+sink
+....
+
+A sink path, which can be either a file or a UNIX socket when prefixed with
+``unix:``.
+
+*Required*
+
+.. WARNING::
+ File is overwritten on server startup or reload.
+
+.. _mod-dnstap_identity:
+
+identity
+........
+
+A DNS server identity. Set empty value to disable.
+
+*Default:* FQDN hostname
+
+.. _mod-dnstap_version:
+
+version
+.......
+
+A DNS server version. Set empty value to disable.
+
+*Default:* server version
+
+.. _mod-dnstap_log-queries:
+
+log-queries
+...........
+
+If enabled, query messages will be logged.
+
+*Default:* ``on``
+
+.. _mod-dnstap_log-responses:
+
+log-responses
+.............
+
+If enabled, response messages will be logged.
+
+*Default:* ``on``
+
+responses-with-queries
+......................
+
+If enabled, dnstap ``AUTH_RESPONSE`` messages will also include the original
+query message as well as the response message sent by the server.
+
+*Default:* ``off``
diff --git a/src/knot/modules/geoip/Makefile.inc b/src/knot/modules/geoip/Makefile.inc
new file mode 100644
index 0000000..9bf65ae
--- /dev/null
+++ b/src/knot/modules/geoip/Makefile.inc
@@ -0,0 +1,17 @@
+knot_modules_geoip_la_SOURCES = knot/modules/geoip/geoip.c \
+ knot/modules/geoip/geodb.c \
+ knot/modules/geoip/geodb.h
+EXTRA_DIST += knot/modules/geoip/geoip.rst
+
+if STATIC_MODULE_geoip
+libknotd_la_SOURCES += $(knot_modules_geoip_la_SOURCES)
+libknotd_la_CPPFLAGS += $(libmaxminddb_CFLAGS)
+libknotd_la_LIBADD += $(libmaxminddb_LIBS)
+endif
+
+if SHARED_MODULE_geoip
+knot_modules_geoip_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_geoip_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(libmaxminddb_CFLAGS)
+knot_modules_geoip_la_LIBADD = $(libcontrib_LIBS) $(libmaxminddb_LIBS)
+pkglib_LTLIBRARIES += knot/modules/geoip.la
+endif
diff --git a/src/knot/modules/geoip/geodb.c b/src/knot/modules/geoip/geodb.c
new file mode 100644
index 0000000..97b6609
--- /dev/null
+++ b/src/knot/modules/geoip/geodb.c
@@ -0,0 +1,216 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "knot/modules/geoip/geodb.h"
+#include "contrib/strtonum.h"
+#include "contrib/string.h"
+
+#if HAVE_MAXMINDDB
+static const uint16_t type_map[] = {
+ [GEODB_KEY_ID] = MMDB_DATA_TYPE_UINT32,
+ [GEODB_KEY_TXT] = MMDB_DATA_TYPE_UTF8_STRING
+};
+#endif
+
+int parse_geodb_path(geodb_path_t *path, const char *input)
+{
+ if (path == NULL || input == NULL) {
+ return -1;
+ }
+
+ // Parse optional type of key.
+ path->type = GEODB_KEY_TXT;
+ const char *delim = input;
+ if (input[0] == '(') {
+ delim = strchr(input, ')');
+ if (delim == NULL) {
+ return -1;
+ }
+ input++;
+ char *type = sprintf_alloc("%.*s", (int)(delim - input), input);
+ const knot_lookup_t *table = knot_lookup_by_name(geodb_key_types, type);
+ free(type);
+ if (table == NULL) {
+ return -1;
+ }
+ path->type = table->id;
+ input = delim + 1;
+ }
+
+ // Parse the path.
+ uint16_t len = 0;
+ while (1) {
+ delim = strchr(input, '/');
+ if (delim == NULL) {
+ delim = input + strlen(input);
+ }
+ path->path[len] = malloc(delim - input + 1);
+ if (path->path[len] == NULL) {
+ return -1;
+ }
+ memcpy(path->path[len], input, delim - input);
+ path->path[len][delim - input] = '\0';
+ len++;
+ if (*delim == 0 || len == GEODB_MAX_PATH_LEN) {
+ break;
+ }
+ input = delim + 1;
+ }
+
+ return 0;
+}
+
+int parse_geodb_data(const char *input, void **geodata, uint32_t *geodata_len,
+ uint8_t *geodepth, geodb_path_t *path, uint16_t path_cnt)
+{
+ for (uint16_t i = 0; i < path_cnt; i++) {
+ const char *delim = strchr(input, ';');
+ if (delim == NULL) {
+ delim = input + strlen(input);
+ }
+ uint16_t key_len = delim - input;
+ if (key_len > 0 && !(key_len == 1 && *input == '*')) {
+ *geodepth = i + 1;
+ switch (path[i].type) {
+ case GEODB_KEY_TXT:
+ geodata[i] = malloc(key_len + 1);
+ if (geodata[i] == NULL) {
+ return -1;
+ }
+ memcpy(geodata[i], input, key_len);
+ ((char *)geodata[i])[key_len] = '\0';
+ geodata_len[i] = key_len;
+ break;
+ case GEODB_KEY_ID:
+ geodata[i] = malloc(sizeof(uint32_t));
+ if (geodata[i] == NULL) {
+ return -1;
+ }
+ if (str_to_u32(input, (uint32_t *)geodata[i]) != KNOT_EOK) {
+ return -1;
+ }
+ geodata_len[i] = sizeof(uint32_t);
+ break;
+ default:
+ assert(0);
+ return -1;
+ }
+ }
+ if (*delim == '\0') {
+ break;
+ }
+ input = delim + 1;
+ }
+
+ return 0;
+}
+
+bool geodb_available(void)
+{
+#if HAVE_MAXMINDDB
+ return true;
+#else
+ return false;
+#endif
+}
+
+geodb_t *geodb_open(const char *filename)
+{
+#if HAVE_MAXMINDDB
+ MMDB_s *db = calloc(1, sizeof(MMDB_s));
+ if (db == NULL) {
+ return NULL;
+ }
+ int mmdb_error = MMDB_open(filename, MMDB_MODE_MMAP, db);
+ if (mmdb_error != MMDB_SUCCESS) {
+ free(db);
+ return NULL;
+ }
+ return db;
+#else
+ return NULL;
+#endif
+}
+
+void geodb_close(geodb_t *geodb)
+{
+#if HAVE_MAXMINDDB
+ MMDB_close(geodb);
+#endif
+}
+
+int geodb_query(geodb_t *geodb, geodb_data_t *entries, struct sockaddr *remote,
+ geodb_path_t *paths, uint16_t path_cnt, uint16_t *netmask)
+{
+#if HAVE_MAXMINDDB
+ int mmdb_error = 0;
+ MMDB_lookup_result_s res;
+ res = MMDB_lookup_sockaddr(geodb, remote, &mmdb_error);
+ if (mmdb_error != MMDB_SUCCESS || !res.found_entry) {
+ return -1;
+ }
+
+ // Save netmask.
+ *netmask = res.netmask;
+
+ for (uint16_t i = 0; i < path_cnt; i++) {
+ // Get the value of the next key.
+ mmdb_error = MMDB_aget_value(&res.entry, &entries[i], (const char *const*)paths[i].path);
+ if (mmdb_error != MMDB_SUCCESS && mmdb_error != MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) {
+ return -1;
+ }
+ if (mmdb_error == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR || !entries[i].has_data) {
+ entries[i].has_data = false;
+ continue;
+ }
+ // Check the type.
+ if (entries[i].type != type_map[paths[i].type]) {
+ entries[i].has_data = false;
+ continue;
+ }
+ }
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+void geodb_fill_geodata(geodb_data_t *entries, uint16_t path_cnt,
+ void **geodata, uint32_t *geodata_len, uint8_t *geodepth)
+{
+#if HAVE_MAXMINDDB
+ for (int i = 0; i < path_cnt; i++) {
+ if (entries[i].has_data) {
+ *geodepth = i + 1;
+ switch (entries[i].type) {
+ case MMDB_DATA_TYPE_UTF8_STRING:
+ geodata[i] = (void *)entries[i].utf8_string;
+ geodata_len[i] = entries[i].data_size;
+ break;
+ case MMDB_DATA_TYPE_UINT32:
+ geodata[i] = (void *)&entries[i].uint32;
+ geodata_len[i] = sizeof(uint32_t);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+ }
+#else
+ return;
+#endif
+}
diff --git a/src/knot/modules/geoip/geodb.h b/src/knot/modules/geoip/geodb.h
new file mode 100644
index 0000000..2ec8701
--- /dev/null
+++ b/src/knot/modules/geoip/geodb.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <libknot/libknot.h>
+#if HAVE_MAXMINDDB
+#include <maxminddb.h>
+#endif
+
+#if HAVE_MAXMINDDB
+#define geodb_t MMDB_s
+#define geodb_data_t MMDB_entry_data_s
+#else
+#define geodb_t void
+#define geodb_data_t char
+#endif
+
+// MaxMind DB related constants.
+#define GEODB_MAX_PATH_LEN 8
+#define GEODB_MAX_DEPTH 8
+
+typedef enum {
+ GEODB_KEY_ID,
+ GEODB_KEY_TXT
+} geodb_key_type_t;
+
+static const knot_lookup_t geodb_key_types[] = {
+ { GEODB_KEY_ID, "id" },
+ { GEODB_KEY_TXT, "" },
+ { 0, NULL }
+};
+
+typedef struct {
+ geodb_key_type_t type;
+ char *path[GEODB_MAX_PATH_LEN + 1]; // MMDB_aget_value() requires last member to be NULL.
+} geodb_path_t;
+
+int parse_geodb_path(geodb_path_t *path, const char *input);
+
+int parse_geodb_data(const char *input, void **geodata, uint32_t *geodata_len,
+ uint8_t *geodepth, geodb_path_t *path, uint16_t path_cnt);
+
+bool geodb_available(void);
+
+geodb_t *geodb_open(const char *filename);
+
+void geodb_close(geodb_t *geodb);
+
+int geodb_query(geodb_t *geodb, geodb_data_t *entries, struct sockaddr *remote,
+ geodb_path_t *paths, uint16_t path_cnt, uint16_t *netmask);
+
+void geodb_fill_geodata(geodb_data_t *entries, uint16_t path_cnt,
+ void **geodata, uint32_t *geodata_len, uint8_t *geodepth);
diff --git a/src/knot/modules/geoip/geoip.c b/src/knot/modules/geoip/geoip.c
new file mode 100644
index 0000000..4a8a2e3
--- /dev/null
+++ b/src/knot/modules/geoip/geoip.c
@@ -0,0 +1,1061 @@
+/* Copyright (C) 2023 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 <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "knot/conf/schema.h"
+#include "knot/include/module.h"
+#include "knot/modules/geoip/geodb.h"
+#include "libknot/libknot.h"
+#include "contrib/qp-trie/trie.h"
+#include "contrib/ucw/lists.h"
+#include "contrib/macros.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "libdnssec/random.h"
+#include "libzscanner/scanner.h"
+
+#define MOD_CONFIG_FILE "\x0B""config-file"
+#define MOD_TTL "\x03""ttl"
+#define MOD_MODE "\x04""mode"
+#define MOD_DNSSEC "\x06""dnssec"
+#define MOD_POLICY "\x06""policy"
+#define MOD_GEODB_FILE "\x0A""geodb-file"
+#define MOD_GEODB_KEY "\x09""geodb-key"
+
+enum operation_mode {
+ MODE_SUBNET,
+ MODE_GEODB,
+ MODE_WEIGHTED
+};
+
+static const knot_lookup_t modes[] = {
+ { MODE_SUBNET, "subnet" },
+ { MODE_GEODB, "geodb" },
+ { MODE_WEIGHTED, "weighted" },
+ { 0, NULL }
+};
+
+static const char* mode_key[] = {
+ [MODE_SUBNET] = "net",
+ [MODE_GEODB] = "geo",
+ [MODE_WEIGHTED] = "weight"
+};
+
+const yp_item_t geoip_conf[] = {
+ { MOD_CONFIG_FILE, YP_TSTR, YP_VNONE },
+ { MOD_TTL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 60, YP_STIME } },
+ { MOD_MODE, YP_TOPT, YP_VOPT = { modes, MODE_SUBNET} },
+ { MOD_DNSSEC, YP_TBOOL, YP_VNONE },
+ { MOD_POLICY, YP_TREF, YP_VREF = { C_POLICY }, YP_FNONE, { knotd_conf_check_ref } },
+ { MOD_GEODB_FILE, YP_TSTR, YP_VNONE },
+ { MOD_GEODB_KEY, YP_TSTR, YP_VSTR = { "country/iso_code" }, YP_FMULTI },
+ { NULL }
+};
+
+char geoip_check_str[1024];
+
+typedef struct {
+ knotd_conf_check_args_t *args; // Set for a dry run.
+ knotd_mod_t *mod; // Set for a real module load.
+} check_ctx_t;
+
+static int load_module(check_ctx_t *ctx);
+
+int geoip_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t conf = knotd_conf_check_item(args, MOD_CONFIG_FILE);
+ if (conf.count == 0) {
+ args->err_str = "no configuration file specified";
+ return KNOT_EINVAL;
+ }
+ conf = knotd_conf_check_item(args, MOD_MODE);
+ if (conf.count == 1 && conf.single.option == MODE_GEODB) {
+ if (!geodb_available()) {
+ args->err_str = "geodb mode not available";
+ return KNOT_EINVAL;
+ }
+
+ conf = knotd_conf_check_item(args, MOD_GEODB_FILE);
+ if (conf.count == 0) {
+ args->err_str = "no geodb file specified while in geodb mode";
+ return KNOT_EINVAL;
+ }
+
+ conf = knotd_conf_check_item(args, MOD_GEODB_KEY);
+ if (conf.count > GEODB_MAX_DEPTH) {
+ args->err_str = "maximal number of geodb-key items exceeded";
+ knotd_conf_free(&conf);
+ return KNOT_EINVAL;
+ }
+ for (size_t i = 0; i < conf.count; i++) {
+ geodb_path_t path = { 0 };
+ if (parse_geodb_path(&path, (char *)conf.multi[i].string) != 0) {
+ args->err_str = "unrecognized geodb-key format";
+ knotd_conf_free(&conf);
+ return KNOT_EINVAL;
+ }
+ for (int j = 0; j < GEODB_MAX_PATH_LEN; j++) {
+ free(path.path[j]);
+ }
+ }
+ knotd_conf_free(&conf);
+ }
+
+ check_ctx_t check = { .args = args };
+ return load_module(&check);
+}
+
+typedef struct {
+ enum operation_mode mode;
+ uint32_t ttl;
+ trie_t *geo_trie;
+ bool dnssec;
+ bool rotate;
+
+ geodb_t *geodb;
+ geodb_path_t paths[GEODB_MAX_DEPTH];
+ uint16_t path_count;
+} geoip_ctx_t;
+
+typedef struct {
+ struct sockaddr_storage *subnet;
+ uint8_t subnet_prefix;
+
+ void *geodata[GEODB_MAX_DEPTH]; // NULL if '*' is specified in config.
+ uint32_t geodata_len[GEODB_MAX_DEPTH];
+ uint8_t geodepth;
+
+ uint16_t weight;
+
+ // Index of the "parent" in the sorted view list.
+ // Equal to its own index if there is no parent.
+ size_t prev;
+
+ size_t count, avail;
+ knot_rrset_t *rrsets;
+ knot_rrset_t *rrsigs;
+
+ knot_dname_t *cname;
+} geo_view_t;
+
+typedef struct {
+ size_t count, avail;
+ geo_view_t *views;
+ uint16_t total_weight;
+} geo_trie_val_t;
+
+typedef int (*view_cmp_t)(const void *a, const void *b);
+
+int geodb_view_cmp(const void *a, const void *b)
+{
+ geo_view_t *va = (geo_view_t *)a;
+ geo_view_t *vb = (geo_view_t *)b;
+
+ int i = 0;
+ while (i < va->geodepth && i < vb->geodepth) {
+ if (va->geodata[i] == NULL) {
+ if (vb->geodata[i] != NULL) {
+ return -1;
+ }
+ } else {
+ if (vb->geodata[i] == NULL) {
+ return 1;
+ }
+ int len = MIN(va->geodata_len[i], vb->geodata_len[i]);
+ int ret = memcmp(va->geodata[i], vb->geodata[i], len);
+ if (ret < 0 || (ret == 0 && vb->geodata_len[i] > len)) {
+ return -1;
+ } else if (ret > 0 || (ret == 0 && va->geodata_len[i] > len)) {
+ return 1;
+ }
+ }
+ i++;
+ }
+ if (i < va->geodepth) {
+ return 1;
+ }
+ if (i < vb->geodepth) {
+ return -1;
+ }
+ return 0;
+}
+
+int subnet_view_cmp(const void *a, const void *b)
+{
+ geo_view_t *va = (geo_view_t *)a;
+ geo_view_t *vb = (geo_view_t *)b;
+
+ if (va->subnet->ss_family != vb->subnet->ss_family) {
+ return va->subnet->ss_family - vb->subnet->ss_family;
+ }
+
+ int ret = 0;
+ switch (va->subnet->ss_family) {
+ case AF_INET:
+ ret = memcmp(&((struct sockaddr_in *)va->subnet)->sin_addr,
+ &((struct sockaddr_in *)vb->subnet)->sin_addr,
+ sizeof(struct in_addr));
+ break;
+ case AF_INET6:
+ ret = memcmp(&((struct sockaddr_in6 *)va->subnet)->sin6_addr,
+ &((struct sockaddr_in6 *)vb->subnet)->sin6_addr,
+ sizeof(struct in6_addr));
+ }
+ if (ret == 0) {
+ return va->subnet_prefix - vb->subnet_prefix;
+ }
+ return ret;
+}
+
+int weighted_view_cmp(const void *a, const void *b)
+{
+ geo_view_t *va = (geo_view_t *)a;
+ geo_view_t *vb = (geo_view_t *)b;
+
+ return (int)va->weight - (int)vb->weight;
+}
+
+static view_cmp_t cmp_fct[] = {
+ [MODE_SUBNET] = &subnet_view_cmp,
+ [MODE_GEODB] = &geodb_view_cmp,
+ [MODE_WEIGHTED] = &weighted_view_cmp
+};
+
+static int add_view_to_trie(knot_dname_t *owner, geo_view_t *view, geoip_ctx_t *ctx)
+{
+ int ret = KNOT_EOK;
+
+ // Find the node belonging to the owner.
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(owner, lf_storage);
+ assert(lf);
+ trie_val_t *val = trie_get_ins(ctx->geo_trie, lf + 1, *lf);
+ geo_trie_val_t *cur_val = *val;
+
+ if (cur_val == NULL) {
+ // Create new node value.
+ geo_trie_val_t *new_val = calloc(1, sizeof(geo_trie_val_t));
+ new_val->avail = 1;
+ new_val->count = 1;
+ new_val->views = malloc(sizeof(geo_view_t));
+ if (ctx->mode == MODE_WEIGHTED) {
+ new_val->total_weight = view->weight;
+ view->weight = 0; // because it is the first view
+ }
+ new_val->views[0] = *view;
+
+ // Add new value to trie.
+ *val = new_val;
+ } else {
+ // Double the views array in size if necessary.
+ if (cur_val->avail == cur_val->count) {
+ void *alloc_ret = realloc(cur_val->views,
+ 2 * cur_val->avail * sizeof(geo_view_t));
+ if (alloc_ret == NULL) {
+ return KNOT_ENOMEM;
+ }
+ cur_val->views = alloc_ret;
+ cur_val->avail *= 2;
+ }
+
+ // Insert new element.
+ if (ctx->mode == MODE_WEIGHTED) {
+ cur_val->total_weight += view->weight;
+ view->weight = cur_val->total_weight - view->weight;
+ }
+ cur_val->views[cur_val->count++] = *view;
+ }
+
+ return ret;
+}
+
+static void geo_log(check_ctx_t *check, int priority, const char *fmt, ...)
+{
+ va_list vargs;
+ va_start(vargs, fmt);
+
+ if (check->args != NULL) {
+ if (vsnprintf(geoip_check_str, sizeof(geoip_check_str), fmt, vargs) < 0) {
+ geoip_check_str[0] = '\0';
+ }
+ check->args->err_str = geoip_check_str;
+ } else {
+ knotd_mod_vlog(check->mod, priority, fmt, vargs);
+ }
+
+ va_end(vargs);
+}
+
+static knotd_conf_t geo_conf(check_ctx_t *check, const yp_name_t *item_name)
+{
+ if (check->args != NULL) {
+ return knotd_conf_check_item(check->args, item_name);
+ } else {
+ return knotd_conf_mod(check->mod, item_name);
+ }
+}
+
+static int finalize_geo_view(check_ctx_t *check, geo_view_t *view, knot_dname_t *owner,
+ geoip_ctx_t *ctx)
+{
+ if (view == NULL || view->count == 0) {
+ return KNOT_EOK;
+ }
+
+ int ret = KNOT_EOK;
+ if (ctx->dnssec) {
+ assert(check->mod != NULL);
+ view->rrsigs = malloc(sizeof(knot_rrset_t) * view->count);
+ if (view->rrsigs == NULL) {
+ return KNOT_ENOMEM;
+ }
+ for (size_t i = 0; i < view->count; i++) {
+ knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL);
+ if (owner_cpy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_init(&view->rrsigs[i], owner_cpy, KNOT_RRTYPE_RRSIG,
+ KNOT_CLASS_IN, ctx->ttl);
+ ret = knotd_mod_dnssec_sign_rrset(check->mod, &view->rrsigs[i],
+ &view->rrsets[i], NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ ret = add_view_to_trie(owner, view, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ memset(view, 0, sizeof(*view));
+ return ret;
+}
+
+static int init_geo_view(geo_view_t *view)
+{
+ if (view == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ view->count = 0;
+ view->avail = 1;
+ view->rrsigs = NULL;
+ view->rrsets = malloc(sizeof(knot_rrset_t));
+ if (view->rrsets == NULL) {
+ return KNOT_ENOMEM;
+ }
+ view->cname = NULL;
+ return KNOT_EOK;
+}
+
+static void clear_geo_view(geo_view_t *view)
+{
+ if (view == NULL) {
+ return;
+ }
+ for (int i = 0; i < GEODB_MAX_DEPTH; i++) {
+ free(view->geodata[i]);
+ }
+ free(view->subnet);
+ for (int j = 0; j < view->count; j++) {
+ knot_rrset_clear(&view->rrsets[j], NULL);
+ if (view->rrsigs != NULL) {
+ knot_rrset_clear(&view->rrsigs[j], NULL);
+ }
+ }
+ free(view->rrsets);
+ view->rrsets = NULL;
+ free(view->rrsigs);
+ view->rrsigs = NULL;
+ free(view->cname);
+ view->cname = NULL;
+}
+
+static int parse_origin(yp_parser_t *yp, zs_scanner_t *scanner)
+{
+ char *set_origin = sprintf_alloc("$ORIGIN %s%s\n", yp->key,
+ (yp->key[yp->key_len - 1] == '.') ? "" : ".");
+ if (set_origin == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Set owner as origin for future record parses.
+ if (zs_set_input_string(scanner, set_origin, strlen(set_origin)) != 0 ||
+ zs_parse_record(scanner) != 0) {
+ free(set_origin);
+ return KNOT_EPARSEFAIL;
+ }
+ free(set_origin);
+ return KNOT_EOK;
+}
+
+static int parse_view(check_ctx_t *check, geoip_ctx_t *ctx, yp_parser_t *yp, geo_view_t *view)
+{
+ // Initialize new geo view.
+ memset(view, 0, sizeof(*view));
+ int ret = init_geo_view(view);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check view type syntax.
+ int key_len = strlen(mode_key[ctx->mode]);
+ if (yp->key_len != key_len || memcmp(yp->key, mode_key[ctx->mode], key_len) != 0) {
+ geo_log(check, LOG_ERR, "invalid key type '%s' on line %zu",
+ yp->key, yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ // Parse geodata/subnet.
+ if (ctx->mode == MODE_GEODB) {
+ if (parse_geodb_data((char *)yp->data, view->geodata, view->geodata_len,
+ &view->geodepth, ctx->paths, ctx->path_count) != 0) {
+ geo_log(check, LOG_ERR, "invalid geo format '%s' on line %zu",
+ yp->data, yp->line_count);
+ return KNOT_EINVAL;
+ }
+ } else if (ctx->mode == MODE_SUBNET) {
+ // Locate the optional slash in the subnet string.
+ char *slash = strchr(yp->data, '/');
+ if (slash == NULL) {
+ slash = yp->data + yp->data_len;
+ }
+ *slash = '\0';
+
+ // Parse address.
+ view->subnet = calloc(1, sizeof(struct sockaddr_storage));
+ if (view->subnet == NULL) {
+ return KNOT_ENOMEM;
+ }
+ // Try to parse as IPv4.
+ ret = sockaddr_set(view->subnet, AF_INET, yp->data, 0);
+ view->subnet_prefix = 32;
+ if (ret != KNOT_EOK) {
+ // Try to parse as IPv6.
+ ret = sockaddr_set(view->subnet, AF_INET6 ,yp->data, 0);
+ view->subnet_prefix = 128;
+ }
+ if (ret != KNOT_EOK) {
+ geo_log(check, LOG_ERR, "invalid address format '%s' on line %zu",
+ yp->data, yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ // Parse subnet prefix.
+ if (slash < yp->data + yp->data_len - 1) {
+ ret = str_to_u8(slash + 1, &view->subnet_prefix);
+ if (ret != KNOT_EOK) {
+ geo_log(check, LOG_ERR, "invalid prefix '%s' on line %zu",
+ slash + 1, yp->line_count);
+ return ret;
+ }
+ if (view->subnet->ss_family == AF_INET && view->subnet_prefix > 32) {
+ view->subnet_prefix = 32;
+ geo_log(check, LOG_WARNING, "IPv4 prefix too large on line %zu, set to 32",
+ yp->line_count);
+ }
+ if (view->subnet->ss_family == AF_INET6 && view->subnet_prefix > 128) {
+ view->subnet_prefix = 128;
+ geo_log(check, LOG_WARNING, "IPv6 prefix too large on line %zu, set to 128",
+ yp->line_count);
+ }
+ }
+ } else if (ctx->mode == MODE_WEIGHTED) {
+ uint8_t weight;
+ ret = str_to_u8(yp->data, &weight);
+ if (ret != KNOT_EOK) {
+ geo_log(check, LOG_ERR, "invalid weight '%s' on line %zu",
+ yp->data, yp->line_count);
+ return ret;
+ }
+ view->weight = weight;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_rr(check_ctx_t *check, yp_parser_t *yp, zs_scanner_t *scanner,
+ knot_dname_t *owner, geo_view_t *view, uint32_t ttl)
+{
+ uint16_t rr_type = KNOT_RRTYPE_A;
+ if (knot_rrtype_from_string(yp->key, &rr_type) != 0) {
+ geo_log(check, LOG_ERR, "invalid RR type '%s' on line %zu",
+ yp->key, yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ if (rr_type == KNOT_RRTYPE_CNAME && view->count > 0) {
+ geo_log(check, LOG_ERR, "cannot add CNAME to view with other RRs on line %zu",
+ yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ if (view->cname != NULL) {
+ geo_log(check, LOG_ERR, "cannot add RR to view with CNAME on line %zu",
+ yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ if (knot_rrtype_is_dnssec(rr_type)) {
+ geo_log(check, LOG_ERR, "DNSSEC record '%s' not allowed on line %zu",
+ yp->key, yp->line_count);
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t *add_rr = NULL;
+ for (size_t i = 0; i < view->count; i++) {
+ if (view->rrsets[i].type == rr_type) {
+ add_rr = &view->rrsets[i];
+ break;
+ }
+ }
+
+ if (add_rr == NULL) {
+ if (view->count == view->avail) {
+ void *alloc_ret = realloc(view->rrsets,
+ 2 * view->avail * sizeof(knot_rrset_t));
+ if (alloc_ret == NULL) {
+ return KNOT_ENOMEM;
+ }
+ view->rrsets = alloc_ret;
+ view->avail *= 2;
+ }
+ add_rr = &view->rrsets[view->count++];
+ knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL);
+ if (owner_cpy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_init(add_rr, owner_cpy, rr_type, KNOT_CLASS_IN, ttl);
+ }
+
+ // Parse record.
+ char *input_string = sprintf_alloc("@ %s %s\n", yp->key, yp->data);
+ if (input_string == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (zs_set_input_string(scanner, input_string, strlen(input_string)) != 0 ||
+ zs_parse_record(scanner) != 0 ||
+ scanner->state != ZS_STATE_DATA) {
+ free(input_string);
+ return KNOT_EPARSEFAIL;
+ }
+ free(input_string);
+
+ if (rr_type == KNOT_RRTYPE_CNAME) {
+ view->cname = knot_dname_from_str_alloc(yp->data);
+ }
+
+ // Add new rdata to current rrset.
+ return knot_rrset_add_rdata(add_rr, scanner->r_data, scanner->r_data_length, NULL);
+}
+
+static int geo_conf_yparse(check_ctx_t *check, geoip_ctx_t *ctx)
+{
+ int ret = KNOT_EOK;
+ yp_parser_t *yp = NULL;
+ zs_scanner_t *scanner = NULL;
+ knot_dname_storage_t owner_buff;
+ knot_dname_t *owner = NULL;
+ geo_view_t *view = calloc(1, sizeof(geo_view_t));
+ if (view == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Initialize yparser.
+ yp = malloc(sizeof(yp_parser_t));
+ if (yp == NULL) {
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+ yp_init(yp);
+ knotd_conf_t conf = geo_conf(check, MOD_CONFIG_FILE);
+ ret = yp_set_input_file(yp, conf.single.string);
+ if (ret != KNOT_EOK) {
+ geo_log(check, LOG_ERR, "failed to load module config file '%s' (%s)",
+ conf.single.string, knot_strerror(ret));
+ goto cleanup;
+ }
+
+ // Initialize zscanner.
+ scanner = malloc(sizeof(zs_scanner_t));
+ if (scanner == NULL) {
+ ret = KNOT_ENOMEM;
+ goto cleanup;
+ }
+ if (zs_init(scanner, NULL, KNOT_CLASS_IN, ctx->ttl) != 0) {
+ ret = KNOT_EPARSEFAIL;
+ goto cleanup;
+ }
+
+ // Main loop.
+ while (1) {
+ // Get the next item in config.
+ ret = yp_parse(yp);
+ if (ret == KNOT_EOF) {
+ ret = finalize_geo_view(check, view, owner, ctx);
+ goto cleanup;
+ }
+ if (ret != KNOT_EOK) {
+ geo_log(check, LOG_ERR,
+ "failed to parse module config file on line %zu (%s)",
+ yp->line_count, knot_strerror(ret));
+ goto cleanup;
+ }
+
+ // If the next item is not a rrset, the current view is finished.
+ if (yp->event != YP_EKEY1) {
+ ret = finalize_geo_view(check, view, owner, ctx);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+
+ // Next domain.
+ if (yp->event == YP_EKEY0) {
+ owner = knot_dname_from_str(owner_buff, yp->key, sizeof(owner_buff));
+ if (owner == NULL) {
+ geo_log(check, LOG_ERR,
+ "invalid domain name in module config file on line %zu",
+ yp->line_count);
+ ret = KNOT_EINVAL;
+ goto cleanup;
+ }
+ ret = parse_origin(yp, scanner);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+
+ // Next view.
+ if (yp->event == YP_EID) {
+ ret = parse_view(check, ctx, yp, view);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+
+ // Next RR of the current view.
+ if (yp->event == YP_EKEY1) {
+ // Check whether we really are in a view.
+ if (view->avail <= 0) {
+ const char *err_str[] = {
+ [MODE_SUBNET] = "- net: SUBNET",
+ [MODE_GEODB] = "- geo: LOCATION",
+ [MODE_WEIGHTED] = "- weight: WEIGHT"
+ };
+ geo_log(check, LOG_ERR,
+ "missing '%s' in module config file before line %zu",
+ err_str[ctx->mode], yp->line_count);
+ ret = KNOT_EINVAL;
+ goto cleanup;
+ }
+ ret = parse_rr(check, yp, scanner, owner, view, ctx->ttl);
+ if (ret != KNOT_EOK) {
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ if (ret != KNOT_EOK) {
+ clear_geo_view(view);
+ }
+ free(view);
+ zs_deinit(scanner);
+ free(scanner);
+ yp_deinit(yp);
+ free(yp);
+ return ret;
+}
+
+static void clear_geo_trie(trie_t *trie)
+{
+ trie_it_t *it = trie_it_begin(trie);
+ while (!trie_it_finished(it)) {
+ geo_trie_val_t *val = (geo_trie_val_t *) (*trie_it_val(it));
+ for (int i = 0; i < val->count; i++) {
+ clear_geo_view(&val->views[i]);
+ }
+ free(val->views);
+ free(val);
+ trie_it_next(it);
+ }
+ trie_it_free(it);
+ trie_clear(trie);
+}
+
+static void free_geoip_ctx(geoip_ctx_t *ctx)
+{
+ geodb_close(ctx->geodb);
+ free(ctx->geodb);
+ clear_geo_trie(ctx->geo_trie);
+ trie_free(ctx->geo_trie);
+ for (int i = 0; i < ctx->path_count; i++) {
+ for (int j = 0; j < GEODB_MAX_PATH_LEN; j++) {
+ free(ctx->paths[i].path[j]);
+ }
+ }
+ free(ctx);
+}
+
+static bool view_strictly_in_view(geo_view_t *view, geo_view_t *in,
+ enum operation_mode mode)
+{
+ switch (mode) {
+ case MODE_GEODB:
+ if (in->geodepth >= view->geodepth) {
+ return false;
+ }
+ for (int i = 0; i < in->geodepth; i++) {
+ if (in->geodata[i] != NULL) {
+ if (in->geodata_len[i] != view->geodata_len[i]) {
+ return false;
+ }
+ if (memcmp(in->geodata[i], view->geodata[i],
+ in->geodata_len[i]) != 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ case MODE_SUBNET:
+ if (in->subnet_prefix >= view->subnet_prefix) {
+ return false;
+ }
+ return sockaddr_net_match(view->subnet, in->subnet, in->subnet_prefix);
+ case MODE_WEIGHTED:
+ return true;
+ default:
+ assert(0);
+ return false;
+ }
+}
+
+static void geo_sort_and_link(geoip_ctx_t *ctx)
+{
+ trie_it_t *it = trie_it_begin(ctx->geo_trie);
+ while (!trie_it_finished(it)) {
+ geo_trie_val_t *val = (geo_trie_val_t *) (*trie_it_val(it));
+ qsort(val->views, val->count, sizeof(geo_view_t), cmp_fct[ctx->mode]);
+
+ for (int i = 1; i < val->count; i++) {
+ geo_view_t *cur_view = &val->views[i];
+ geo_view_t *prev_view = &val->views[i - 1];
+ cur_view->prev = i;
+ int prev = i - 1;
+ do {
+ if (view_strictly_in_view(cur_view, prev_view, ctx->mode)) {
+ cur_view->prev = prev;
+ break;
+ }
+ if (prev == prev_view->prev) {
+ break;
+ }
+ prev = prev_view->prev;
+ prev_view = &val->views[prev];
+ } while (1);
+ }
+ trie_it_next(it);
+ }
+ trie_it_free(it);
+}
+
+// Return the index of the last lower or equal element or -1 of not exists.
+static int geo_bin_search(geo_view_t *arr, int count, geo_view_t *x, view_cmp_t cmp)
+{
+ int l = 0, r = count;
+ while (l < r) {
+ int m = (l + r) / 2;
+ if (cmp(&arr[m], x) <= 0) {
+ l = m + 1;
+ } else {
+ r = m;
+ }
+ }
+ return l - 1; // l is the index of first greater element or N if not exists.
+}
+
+static geo_view_t *find_best_view(geo_view_t *dummy, geo_trie_val_t *data, geoip_ctx_t *ctx)
+{
+ view_cmp_t cmp = cmp_fct[ctx->mode];
+ int idx = geo_bin_search(data->views, data->count, dummy, cmp);
+ if (idx == -1) { // There is no suitable view.
+ return NULL;
+ }
+ if (cmp(dummy, &data->views[idx]) != 0 &&
+ !view_strictly_in_view(dummy, &data->views[idx], ctx->mode)) {
+ idx = data->views[idx].prev;
+ while (!view_strictly_in_view(dummy, &data->views[idx], ctx->mode)) {
+ if (idx == data->views[idx].prev) {
+ // We are at a root and we have found no suitable view.
+ return NULL;
+ }
+ idx = data->views[idx].prev;
+ }
+ }
+ return &data->views[idx];
+}
+
+static void find_rr_in_view(uint16_t qtype, geo_view_t *view,
+ knot_rrset_t **rr, knot_rrset_t **rrsig)
+{
+ knot_rrset_t *cname = NULL;
+ knot_rrset_t *cnamesig = NULL;
+ for (int i = 0; i < view->count; i++) {
+ if (view->rrsets[i].type == qtype) {
+ *rr = &view->rrsets[i];
+ *rrsig = (view->rrsigs) ? &view->rrsigs[i] : NULL;
+ } else if (view->rrsets[i].type == KNOT_RRTYPE_CNAME) {
+ cname = &view->rrsets[i];
+ cnamesig = (view->rrsigs) ? &view->rrsigs[i] : NULL;
+ }
+ }
+
+ // Return CNAME if only CNAME is found.
+ if (*rr == NULL && cname != NULL) {
+ *rr = cname;
+ *rrsig = cnamesig;
+ }
+}
+
+static knotd_in_state_t geoip_process(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ // Nothing to do if the query was already resolved by a previous module.
+ if (state == KNOTD_IN_STATE_HIT || state == KNOTD_IN_STATE_FOLLOW) {
+ return state;
+ }
+
+ geoip_ctx_t *ctx = (geoip_ctx_t *)knotd_mod_ctx(mod);
+
+ // Save the query type.
+ uint16_t qtype = knot_pkt_qtype(qdata->query);
+
+ // Check if geolocation is available for given query.
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(knot_pkt_qname(qdata->query), lf_storage);
+ // Exit if no qname.
+ if (lf == NULL) {
+ return state;
+ }
+ trie_val_t *val = trie_get_try_wildcard(ctx->geo_trie, lf + 1, *lf);
+ if (val == NULL) {
+ // Nothing to do in this module.
+ return state;
+ }
+
+ geo_trie_val_t *data = *val;
+
+ // Check if EDNS Client Subnet is available.
+ struct sockaddr_storage ecs_addr = { 0 };
+ const struct sockaddr_storage *remote = knotd_qdata_remote_addr(qdata);
+ if (knot_edns_client_subnet_get_addr(&ecs_addr, qdata->ecs) == KNOT_EOK) {
+ remote = &ecs_addr;
+ }
+
+ uint16_t netmask = 0;
+ geodb_data_t entries[GEODB_MAX_DEPTH];
+
+ // Create dummy view and fill it with data about the current remote.
+ geo_view_t dummy = { 0 };
+ switch(ctx->mode) {
+ case MODE_SUBNET:
+ dummy.subnet = (struct sockaddr_storage *)remote;
+ dummy.subnet_prefix = (remote->ss_family == AF_INET) ? 32 : 128;
+ break;
+ case MODE_GEODB:
+ if (geodb_query(ctx->geodb, entries, (struct sockaddr *)remote,
+ ctx->paths, ctx->path_count, &netmask) != 0) {
+ return state;
+ }
+ // MMDB may supply IPv6 prefixes even for IPv4 address, see man libmaxminddb.
+ if (remote->ss_family == AF_INET && netmask > 32) {
+ netmask -= 96;
+ }
+ geodb_fill_geodata(entries, ctx->path_count,
+ dummy.geodata, dummy.geodata_len, &dummy.geodepth);
+ break;
+ case MODE_WEIGHTED:
+ dummy.weight = dnssec_random_uint16_t() % data->total_weight;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ // Find last lower or equal view.
+ geo_view_t *view = find_best_view(&dummy, data, ctx);
+ if (view == NULL) { // No suitable view was found.
+ return state;
+ }
+
+ // Save netmask for ECS if in subnet mode.
+ if (ctx->mode == MODE_SUBNET) {
+ netmask = view->subnet_prefix;
+ }
+
+ // Fetch the correct rrset from found view.
+ knot_rrset_t *rr = NULL;
+ knot_rrset_t *rrsig = NULL;
+ find_rr_in_view(qtype, view, &rr, &rrsig);
+
+ // Answer the query if possible.
+ if (rr != NULL) {
+ // Update ECS if used.
+ if (qdata->ecs != NULL && netmask > 0) {
+ qdata->ecs->scope_len = netmask;
+ }
+
+ uint16_t rotate = ctx->rotate ? knot_wire_get_id(qdata->query->wire) : 0;
+ knot_pkt_put_rotate(pkt, KNOT_COMPR_HINT_QNAME, rr, rotate, 0);
+ if (ctx->dnssec && knot_pkt_has_dnssec(qdata->query) && rrsig != NULL) {
+ knot_pkt_put_rotate(pkt, KNOT_COMPR_HINT_QNAME, rrsig, rotate, 0);
+ }
+
+ // We've got an answer, set the AA bit.
+ knot_wire_set_aa(pkt->wire);
+
+ if (rr->type == KNOT_RRTYPE_CNAME && view->cname != NULL) {
+ // Trigger CNAME chain resolution
+ qdata->name = view->cname;
+ return KNOTD_IN_STATE_FOLLOW;
+ }
+
+ return KNOTD_IN_STATE_HIT;
+ } else {
+ // view was found, but no suitable rrtype
+ return KNOTD_IN_STATE_NODATA;
+ }
+}
+
+static int load_module(check_ctx_t *check)
+{
+ assert((check->args != NULL) != (check->mod != NULL));
+ knotd_mod_t *mod = check->mod;
+
+ // Create module context.
+ geoip_ctx_t *ctx = calloc(1, sizeof(geoip_ctx_t));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knotd_conf_t conf = geo_conf(check, MOD_TTL);
+ ctx->ttl = conf.single.integer;
+ conf = geo_conf(check, MOD_MODE);
+ ctx->mode = conf.single.option;
+
+ // Initialize the dname trie.
+ ctx->geo_trie = trie_create(NULL);
+ if (ctx->geo_trie == NULL) {
+ free_geoip_ctx(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ if (ctx->mode == MODE_GEODB) {
+ // Initialize geodb.
+ conf = geo_conf(check, MOD_GEODB_FILE);
+ ctx->geodb = geodb_open(conf.single.string);
+ if (ctx->geodb == NULL) {
+ geo_log(check, LOG_ERR, "failed to open geo DB");
+ free_geoip_ctx(ctx);
+ return KNOT_EINVAL;
+ }
+
+ // Load configured geodb keys.
+ conf = geo_conf(check, MOD_GEODB_KEY);
+ assert(conf.count <= GEODB_MAX_DEPTH);
+ ctx->path_count = conf.count;
+ for (size_t i = 0; i < conf.count; i++) {
+ (void)parse_geodb_path(&ctx->paths[i], (char *)conf.multi[i].string);
+ }
+ knotd_conf_free(&conf);
+ }
+
+ if (mod != NULL) {
+ // Is DNSSEC used on this zone?
+ conf = knotd_conf_mod(mod, MOD_DNSSEC);
+ if (conf.count == 0) {
+ conf = knotd_conf_zone(mod, C_DNSSEC_SIGNING, knotd_mod_zone(mod));
+ }
+ ctx->dnssec = conf.single.boolean;
+ if (ctx->dnssec) {
+ int ret = knotd_mod_dnssec_init(mod);
+ if (ret != KNOT_EOK) {
+ knotd_mod_log(mod, LOG_ERR, "failed to initialize DNSSEC");
+ free_geoip_ctx(ctx);
+ return ret;
+ }
+ ret = knotd_mod_dnssec_load_keyset(mod, false);
+ if (ret != KNOT_EOK) {
+ knotd_mod_log(mod, LOG_ERR, "failed to load DNSSEC keys");
+ free_geoip_ctx(ctx);
+ return ret;
+ }
+ }
+
+ conf = knotd_conf(mod, C_SRV, C_ANS_ROTATION, NULL);
+ ctx->rotate = conf.single.boolean;
+ }
+
+ // Parse geo configuration file.
+ int ret = geo_conf_yparse(check, ctx);
+ if (ret != KNOT_EOK) {
+ free_geoip_ctx(ctx);
+ return ret;
+ }
+
+ if (mod != NULL) {
+ // Prepare geo views for faster search.
+ geo_sort_and_link(ctx);
+
+ knotd_mod_ctx_set(mod, ctx);
+ } else {
+ free_geoip_ctx(ctx);
+ }
+
+ return ret;
+}
+
+int geoip_load(knotd_mod_t *mod)
+{
+ check_ctx_t check = { .mod = mod };
+ int ret = load_module(&check);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return knotd_mod_in_hook(mod, KNOTD_STAGE_PREANSWER, geoip_process);
+}
+
+void geoip_unload(knotd_mod_t *mod)
+{
+ geoip_ctx_t *ctx = knotd_mod_ctx(mod);
+ if (ctx != NULL) {
+ free_geoip_ctx(ctx);
+ }
+}
+
+KNOTD_MOD_API(geoip, KNOTD_MOD_FLAG_SCOPE_ZONE,
+ geoip_load, geoip_unload, geoip_conf, geoip_conf_check);
diff --git a/src/knot/modules/geoip/geoip.rst b/src/knot/modules/geoip/geoip.rst
new file mode 100644
index 0000000..d65c1cb
--- /dev/null
+++ b/src/knot/modules/geoip/geoip.rst
@@ -0,0 +1,324 @@
+.. _mod-geoip:
+
+``geoip`` — Geography-based responses
+=====================================
+
+This module offers response tailoring based on client's
+subnet, geographic location, or a statistical weight. It supports GeoIP databases
+in the MaxMind DB format, such as `GeoIP2 <https://dev.maxmind.com/geoip/geoip2/downloadable/>`_
+or the free version `GeoLite2 <https://dev.maxmind.com/geoip/geoip2/geolite2/>`_.
+
+The module can be enabled only per zone.
+
+.. NOTE::
+ If :ref:`EDNS Client Subnet<server_edns-client-subnet>` support is enabled
+ and if a query contains this option, the module takes advantage of this
+ information to provide a more accurate response.
+
+DNSSEC support
+--------------
+
+There are several ways to enable DNSSEC signing of tailored responses.
+
+Full zone signing
+.................
+
+If :ref:`automatic DNSSEC signing <zone_dnssec-signing>` is enabled,
+the whole zone is signed by the server and all alternative RRsets, which are responded
+by the module, are pre-signed when the module is loaded.
+
+This has a speed benefit, however note that every RRset configured in the module should
+have a **default** RRset of the same type contained in the zone, so that the NSEC(3)
+chain can be built correctly. Also, it is STRONGLY RECOMMENDED to use
+:ref:`manual key management <dnssec-manual-key-management>` in this setting,
+as the corresponding zone has to be reloaded when the signing key changes and to
+have better control over key synchronization to all instances of the server.
+
+.. NOTE::
+ DNSSEC keys for computing record signatures MUST exist in the KASP database
+ or be generated before the module is launched, otherwise the module fails to
+ compute the signatures and does not load.
+
+Module signing
+..............
+
+If :ref:`automatic DNSSEC signing <zone_dnssec-signing>` is disabled,
+it's possible to combine externally pre-signed zone with module pre-signing
+of the alternative RRsets when the module is loaded. In this mode, only ZSK
+has to be present in the KASP database. Also in this mode every RRset configured
+in the module should have a **default** RRset of the same type contained in the zone.
+
+Example:
+
+::
+
+ policy:
+ - id: presigned_zone
+ manual: on
+ unsafe-operation: no-check-keyset
+
+ mod-geoip:
+ - id: geo_dnssec
+ ...
+ dnssec: on
+ policy: presigned_zone
+
+ zone:
+ - domain: example.com.
+ module: mod-geoip/geo_dnssec
+
+Online signing
+..............
+
+Alternatively, the :ref:`geoip<mod-geoip>` module may be combined with the
+:ref:`onlinesign<mod-onlinesign>` module and the tailored responses can be signed
+on the fly. This approach is much more computationally demanding for the server.
+
+.. NOTE::
+ If the GeoIP module is used with online signing, it is recommended to set the :ref:`nsec-bitmap<mod-onlinesign_nsec-bitmap>`
+ option of the onlinesign module to contain all Resource Record types potentially generated by the module.
+
+Example
+-------
+
+An example configuration:
+
+::
+
+ mod-geoip:
+ - id: default
+ config-file: /path/to/geo.conf
+ ttl: 20
+ mode: geodb
+ geodb-file: /path/to/GeoLite2-City.mmdb
+ geodb-key: [ country/iso_code, city/names/en ]
+
+ zone:
+ - domain: example.com.
+ module: mod-geoip/default
+
+
+Configuration file
+------------------
+
+Every instance of the module requires an additional :ref:`mod-geoip_config-file`
+in which the desired responses to queries from various locations are configured.
+This file has the following simple format:
+
+::
+
+ domain-name1:
+ - geo|net|weight: value1
+ RR-Type1: RDATA
+ RR-Type2: RDATA
+ ...
+ - geo|net|weight: value2
+ RR-Type1: RDATA
+ ...
+ domain-name2:
+ ...
+
+
+Module configuration examples
+-----------------------------
+
+This section contains some examples for the module's :ref:`mod-geoip_config-file`.
+
+Using subnets
+.............
+
+::
+
+ foo.example.com:
+ - net: 10.0.0.0/24
+ A: [ 192.168.1.1, 192.168.1.2 ]
+ AAAA: [ 2001:DB8::1, 2001:DB8::2 ]
+ TXT: "subnet\ 10.0.0.0/24"
+ ...
+ bar.example.com:
+ - net: 2001:DB8::/32
+ A: 192.168.1.3
+ AAAA: 2001:DB8::3
+ TXT: "subnet\ 2001:DB8::/32"
+ ...
+
+Clients from the specified subnets will receive the responses defined in the
+module config. Others will receive the default records defined in the zone (if any).
+
+.. NOTE::
+ If a space or a quotation mark is a part of record data, such a character
+ must be prefixed with a backslash. The following notations are equivalent::
+
+ Multi-word\ string
+ "Multi-word\ string"
+ "\"Multi-word string\""
+
+Using geographic locations
+..........................
+
+::
+
+ foo.example.com:
+ - geo: "CZ;Prague"
+ CNAME: cz.foo.example.com.
+ - geo: "US;Las Vegas"
+ CNAME: vegas.foo.example.net.
+ - geo: "US;*"
+ CNAME: us.foo.example.net.
+ ...
+
+Clients from the specified geographic locations will receive the responses defined in the
+module config. Others will receive the default records defined in the zone (if any). See
+:ref:`mod-geoip_geodb-key` for the syntax and semantics of the location definitions.
+
+Using weighted records
+......................
+
+::
+
+ foo.example.com:
+ - weight: 1
+ CNAME: canary.foo.example.com.
+ - weight: 10
+ CNAME: prod1.foo.example.com.
+ - weight: 10
+ CNAME: prod2.foo.example.com.
+ ...
+
+Each response is generated through a random pick where each defined record has a likelihood
+of its weight over the sum of all weights for the requested name to. Records defined in the
+zone itself (if any) will never be served.
+
+Result:
+
+.. code-block:: console
+
+ $ for i in $(seq 1 100); do kdig @192.168.1.242 CNAME foo.example.com +short; done | sort | uniq -c
+ 3 canary.foo.example.com.foo.example.com.
+ 52 prod1.foo.example.net.foo.example.com.
+ 45 prod2.foo.example.net.foo.example.com.
+
+Module reference
+----------------
+
+::
+
+ mod-geoip:
+ - id: STR
+ config-file: STR
+ ttl: TIME
+ mode: geodb | subnet | weighted
+ dnssec: BOOL
+ policy: policy_id
+ geodb-file: STR
+ geodb-key: STR ...
+
+.. _mod-geoip_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-geoip_config-file:
+
+config-file
+...........
+
+Full path to the response configuration file as described above.
+
+*Required*
+
+.. _mod-geoip_ttl:
+
+ttl
+...
+
+The time to live of Resource Records returned by the module, in seconds.
+
+*Default:* ``60``
+
+.. _mod-geoip_mode:
+
+mode
+....
+
+The mode of operation of the module.
+
+Possible values:
+
+- ``subnet`` – Responses are tailored according to subnets.
+- ``geodb`` – Responses are tailored according to geographic data retrieved
+ from the configured database.
+- ``weighted`` – Responses are tailored according to a statistical weight.
+
+*Default:* ``subnet``
+
+.. _mod-geoip_dnssec:
+
+dnssec
+......
+
+If explicitly enabled, the module signs positive responses based on the module policy
+(:ref:`mod-geoip_policy`). If explicitly disabled, positive responses from the
+module are not signed even if the zone is pre-signed or signed by the server
+(:ref:`zone_dnssec-signing`).
+
+.. WARNING::
+ This configuration must be used carefully. Otherwise the zone responses
+ can be bogus.
+ DNSKEY rotation isn't supported. So :ref:`policy_manual` mode is highly
+ recommended.
+
+*Default:* current value of :ref:`zone_dnssec-signing` with :ref:`zone_dnssec-policy`
+
+.. _mod-geoip_policy:
+
+policy
+......
+
+A :ref:`reference<policy_id>` to DNSSEC signing policy which is used if
+:ref:`mod-geoip_dnssec` is enabled.
+
+*Default:* an imaginary policy with all default values
+
+.. _mod-geoip_geodb-file:
+
+geodb-file
+..........
+
+Full path to a .mmdb file containing the GeoIP database.
+
+*Required if* :ref:`mod-geoip_mode` *is set to* **geodb**
+
+.. _mod-geoip_geodb-key:
+
+geodb-key
+.........
+
+Multi-valued item, can be specified up to **8** times. Each **geodb-key** specifies
+a path to a key in a node in the supplied GeoIP database. The module currently supports
+two types of values: **string** or **32-bit unsigned int**. In the latter
+case, the key has to be prefixed with **(id)**. Common choices of keys include:
+
+* **continent/code**
+
+* **country/iso_code**
+
+* **(id)country/geoname_id**
+
+* **city/names/en**
+
+* **(id)city/geoname_id**
+
+* **isp**
+
+* ...
+
+The exact keys available depend on the database being used. To get the full list
+of keys available, you can e.g. do a sample lookup on your database with the
+`mmdblookup <https://maxmind.github.io/libmaxminddb/mmdblookup.html>`_ tool.
+
+In the zone's config file for the module the values of the keys are entered in the same order
+as the keys in the module's configuration, separated by a semicolon. Enter the value **"*"**
+if the key is allowed to have any value.
diff --git a/src/knot/modules/noudp/Makefile.inc b/src/knot/modules/noudp/Makefile.inc
new file mode 100644
index 0000000..cf26a35
--- /dev/null
+++ b/src/knot/modules/noudp/Makefile.inc
@@ -0,0 +1,12 @@
+knot_modules_noudp_la_SOURCES = knot/modules/noudp/noudp.c
+EXTRA_DIST += knot/modules/noudp/noudp.rst
+
+if STATIC_MODULE_noudp
+libknotd_la_SOURCES += $(knot_modules_noudp_la_SOURCES)
+endif
+
+if SHARED_MODULE_noudp
+knot_modules_noudp_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_noudp_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+pkglib_LTLIBRARIES += knot/modules/noudp.la
+endif
diff --git a/src/knot/modules/noudp/noudp.c b/src/knot/modules/noudp/noudp.c
new file mode 100644
index 0000000..e8f456b
--- /dev/null
+++ b/src/knot/modules/noudp/noudp.c
@@ -0,0 +1,110 @@
+/* Copyright (C) 2022 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 "knot/include/module.h"
+
+#define MOD_UDP_ALLOW_RATE "\x0e""udp-allow-rate"
+#define MOD_UDP_TRUNC_RATE "\x11""udp-truncate-rate"
+
+const yp_item_t noudp_conf[] = {
+ { MOD_UDP_ALLOW_RATE, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0 } },
+ { MOD_UDP_TRUNC_RATE, YP_TINT, YP_VINT = { 1, UINT32_MAX, 0 } },
+ { NULL }
+};
+
+int noudp_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t allow = knotd_conf_check_item(args, MOD_UDP_ALLOW_RATE);
+ knotd_conf_t trunc = knotd_conf_check_item(args, MOD_UDP_TRUNC_RATE);
+ if (allow.count == 1 && trunc.count == 1) {
+ args->err_str = "udp-allow-rate and udp-truncate-rate cannot be specified together";
+ return KNOT_EINVAL;
+ }
+ return KNOT_EOK;
+}
+
+typedef struct {
+ uint32_t rate;
+ uint32_t *counters;
+ bool trunc_mode;
+} noudp_ctx_t;
+
+static knotd_state_t noudp_begin(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) {
+ return state;
+ }
+
+ bool truncate = true;
+
+ noudp_ctx_t *ctx = knotd_mod_ctx(mod);
+ if (ctx->rate > 0) {
+ bool apply = false;
+ if (++ctx->counters[qdata->params->thread_id] >= ctx->rate) {
+ ctx->counters[qdata->params->thread_id] = 0;
+ apply = true;
+ }
+ truncate = (apply == ctx->trunc_mode);
+ }
+
+ if (truncate) {
+ knot_wire_set_tc(pkt->wire);
+ return KNOTD_STATE_DONE;
+ } else {
+ return state;
+ }
+}
+
+int noudp_load(knotd_mod_t *mod)
+{
+ noudp_ctx_t *ctx = calloc(1, sizeof(noudp_ctx_t));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knotd_conf_t allow = knotd_conf_mod(mod, MOD_UDP_ALLOW_RATE);
+ knotd_conf_t trunc = knotd_conf_mod(mod, MOD_UDP_TRUNC_RATE);
+
+ if (allow.count == 1) {
+ ctx->rate = allow.single.integer;
+ } else if (trunc.count == 1) {
+ ctx->rate = trunc.single.integer;
+ ctx->trunc_mode = true;
+ }
+
+ if (ctx->rate > 0) {
+ ctx->counters = calloc(knotd_mod_threads(mod), sizeof(uint32_t));
+ if (ctx->counters == NULL) {
+ free(ctx);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, noudp_begin);
+}
+
+void noudp_unload(knotd_mod_t *mod)
+{
+ noudp_ctx_t *ctx = knotd_mod_ctx(mod);
+ free(ctx->counters);
+ free(ctx);
+}
+
+KNOTD_MOD_API(noudp, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
+ noudp_load, noudp_unload, noudp_conf, noudp_conf_check);
diff --git a/src/knot/modules/noudp/noudp.rst b/src/knot/modules/noudp/noudp.rst
new file mode 100644
index 0000000..e430395
--- /dev/null
+++ b/src/knot/modules/noudp/noudp.rst
@@ -0,0 +1,68 @@
+.. _mod-noudp:
+
+``noudp`` — No UDP response
+===========================
+
+The module sends empty truncated reply to a query over UDP. Replies over TCP
+are not affected.
+
+Example
+-------
+
+To enable this module for all configured zones and every UDP reply::
+
+ template:
+ - id: default
+ global-module: mod-noudp
+
+Or with specified UDP allow rate::
+
+ mod-noudp:
+ - id: sometimes
+ udp-allow-rate: 1000 # Don't truncate every 1000th UDP reply
+
+ template:
+ - id: default
+ module: mod-noudp/sometimes
+
+Module reference
+----------------
+
+::
+
+ mod-noudp:
+ - id: STR
+ udp-allow-rate: INT
+ udp-truncate-rate: INT
+
+.. NOTE::
+ Both *udp-allow-rate* and *udp-truncate-rate* cannot be specified together.
+
+.. _mod-noudp_udp-allow-rate:
+
+udp-allow-rate
+..............
+
+Specifies frequency of UDP replies that are not truncated. A non-zero value means
+that every N\ :sup:`th` UDP reply is not truncated.
+
+.. NOTE::
+ The rate value is associated with one UDP worker. If more UDP workers are
+ configured, the specified value may not be obvious to clients.
+
+*Default:* not set
+
+.. _mod-noudp_udp-truncate-rate:
+
+udp-truncate-rate
+.................
+
+Specifies frequency of UDP replies that are truncated (opposite of
+:ref:`udp-allow-rate <mod-noudp_udp-allow-rate>`). A non-zero value means that
+every N\ :sup:`th` UDP reply is truncated.
+
+.. NOTE::
+ The rate value is associated with one UDP worker. If more UDP workers are
+ configured, the specified value may not be obvious to clients.
+
+*Default:* ``1``
diff --git a/src/knot/modules/onlinesign/Makefile.inc b/src/knot/modules/onlinesign/Makefile.inc
new file mode 100644
index 0000000..e7289fb
--- /dev/null
+++ b/src/knot/modules/onlinesign/Makefile.inc
@@ -0,0 +1,15 @@
+knot_modules_onlinesign_la_SOURCES = knot/modules/onlinesign/onlinesign.c \
+ knot/modules/onlinesign/nsec_next.c \
+ knot/modules/onlinesign/nsec_next.h
+EXTRA_DIST += knot/modules/onlinesign/onlinesign.rst
+
+if STATIC_MODULE_onlinesign
+libknotd_la_SOURCES += $(knot_modules_onlinesign_la_SOURCES)
+endif
+
+if SHARED_MODULE_onlinesign
+knot_modules_onlinesign_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_onlinesign_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_onlinesign_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/onlinesign.la
+endif
diff --git a/src/knot/modules/onlinesign/nsec_next.c b/src/knot/modules/onlinesign/nsec_next.c
new file mode 100644
index 0000000..2205f6b
--- /dev/null
+++ b/src/knot/modules/onlinesign/nsec_next.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "knot/modules/onlinesign/nsec_next.h"
+#include "libknot/libknot.h"
+
+static bool inc_label(const uint8_t *buffer, uint8_t **label_ptr)
+{
+ assert(buffer);
+ assert(label_ptr && *label_ptr);
+ assert(buffer <= *label_ptr && *label_ptr < buffer + KNOT_DNAME_MAXLEN);
+
+ const uint8_t *label = *label_ptr;
+ const uint8_t len = *label;
+ const uint8_t *first = *label_ptr + 1;
+ const uint8_t *last = *label_ptr + len;
+
+ assert(len <= KNOT_DNAME_MAXLABELLEN);
+
+ // jump over trailing 0xff chars
+ uint8_t *scan = (uint8_t *)last;
+ while (scan >= first && *scan == 0xff) {
+ scan -= 1;
+ }
+
+ // increase in place
+ if (scan >= first) {
+ if (*scan == 'A' - 1) {
+ *scan = 'Z' + 1;
+ } else {
+ *scan += 1;
+ }
+ memset(scan + 1, 0x00, last - scan);
+ return true;
+ }
+
+ // check name and label boundaries
+ if (scan - 1 < buffer || len == KNOT_DNAME_MAXLABELLEN) {
+ return false;
+ }
+
+ // append a zero byte at the end of the label
+ scan -= 1;
+ scan[0] = len + 1;
+ memmove(scan + 1, first, len);
+ scan[len + 1] = 0x00;
+
+ *label_ptr = scan;
+
+ return true;
+}
+
+static void strip_label(uint8_t **name_ptr)
+{
+ assert(name_ptr && *name_ptr);
+
+ uint8_t len = **name_ptr;
+ *name_ptr += 1 + len;
+}
+
+knot_dname_t *online_nsec_next(const knot_dname_t *dname, const knot_dname_t *apex)
+{
+ assert(dname);
+ assert(apex);
+
+ // right aligned copy of the domain name
+ knot_dname_storage_t copy = { 0 };
+ const size_t dname_len = knot_dname_size(dname);
+ const size_t empty_len = sizeof(copy) - dname_len;
+ memmove(copy + empty_len, dname, dname_len);
+
+ // add new zero-byte label
+ if (empty_len >= 2) {
+ uint8_t *pos = copy + empty_len - 2;
+ pos[0] = 0x01;
+ pos[1] = 0x00;
+ return knot_dname_copy(pos, NULL);
+ }
+
+ // find apex position in the buffer
+ size_t apex_len = knot_dname_size(apex);
+ const uint8_t *apex_pos = copy + sizeof(copy) - apex_len;
+ assert(knot_dname_is_equal(apex, apex_pos));
+
+ // find first label which can be incremented
+ uint8_t *pos = copy + empty_len;
+ while (pos != apex_pos) {
+ if (inc_label(copy, &pos)) {
+ return knot_dname_copy(pos, NULL);
+ }
+ strip_label(&pos);
+ }
+
+ // apex completes the chain
+ return knot_dname_copy(pos, NULL);
+}
diff --git a/src/knot/modules/onlinesign/nsec_next.h b/src/knot/modules/onlinesign/nsec_next.h
new file mode 100644
index 0000000..428b993
--- /dev/null
+++ b/src/knot/modules/onlinesign/nsec_next.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2015 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/>.
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+
+/*!
+ * \brief Get the very next possible name in NSEC chain.
+ *
+ * \param dname Current dname in the NSEC chain.
+ * \param apex Zone apex name, used when we reach the end of the chain.
+ *
+ * \return Successor of dname in the NSEC chain.
+ */
+knot_dname_t *online_nsec_next(const knot_dname_t *dname, const knot_dname_t *apex);
diff --git a/src/knot/modules/onlinesign/onlinesign.c b/src/knot/modules/onlinesign/onlinesign.c
new file mode 100644
index 0000000..56b1c03
--- /dev/null
+++ b/src/knot/modules/onlinesign/onlinesign.c
@@ -0,0 +1,736 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "contrib/string.h"
+#include "libdnssec/error.h"
+#include "knot/include/module.h"
+#include "knot/modules/onlinesign/nsec_next.h"
+// Next dependencies force static module!
+#include "knot/dnssec/ds_query.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/policy.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/nameserver/query_module.h"
+#include "knot/nameserver/process_query.h"
+
+#define MOD_POLICY "\x06""policy"
+#define MOD_NSEC_BITMAP "\x0B""nsec-bitmap"
+
+int policy_check(knotd_conf_check_args_t *args)
+{
+ int ret = knotd_conf_check_ref(args);
+ if (ret != KNOT_EOK && strcmp((const char *)args->data, "default") == 0) {
+ return KNOT_EOK;
+ }
+
+ return ret;
+}
+
+int bitmap_check(knotd_conf_check_args_t *args)
+{
+ uint16_t num;
+ int ret = knot_rrtype_from_string((const char *)args->data, &num);
+ if (ret != 0) {
+ args->err_str = "invalid RR type";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+const yp_item_t online_sign_conf[] = {
+ { MOD_POLICY, YP_TREF, YP_VREF = { C_POLICY }, YP_FNONE, { policy_check } },
+ { MOD_NSEC_BITMAP, YP_TSTR, YP_VNONE, YP_FMULTI, { bitmap_check } },
+ { NULL }
+};
+
+/*!
+ * We cannot determine the true NSEC bitmap because of dynamic modules which
+ * can synthesize some types on-the-fly. The base NSEC map will be determined
+ * from zone content and this list of types.
+ *
+ * The types in the NSEC bitmap really don't have to exist. Only the QTYPE
+ * must not be present. This will make the validation work with resolvers
+ * performing negative caching.
+ */
+
+static const uint16_t NSEC_FORCE_TYPES[] = {
+ KNOT_RRTYPE_A,
+ KNOT_RRTYPE_AAAA,
+ 0
+};
+
+typedef struct {
+ knot_time_t event_rollover;
+ knot_time_t event_parent_ds_q;
+ pthread_mutex_t event_mutex;
+ pthread_rwlock_t signing_mutex;
+
+ uint16_t *nsec_force_types;
+
+ bool zone_doomed;
+} online_sign_ctx_t;
+
+static bool want_dnssec(knotd_qdata_t *qdata)
+{
+ return knot_pkt_has_dnssec(qdata->query);
+}
+
+static uint32_t dnskey_ttl(knotd_qdata_t *qdata)
+{
+ knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA);
+ return soa.ttl;
+}
+
+static uint32_t nsec_ttl(knotd_qdata_t *qdata)
+{
+ knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA);
+ return knot_soa_minimum(soa.rrs.rdata);
+}
+
+/*!
+ * \brief Add bitmap records synthesized by online-signing.
+ */
+static void bitmap_add_synth(dnssec_nsec_bitmap_t *map, bool is_apex)
+{
+ dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_NSEC);
+ dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_RRSIG);
+ if (is_apex) {
+ dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_DNSKEY);
+ //dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_CDS);
+ }
+}
+
+/*!
+ * \brief Add bitmap records present in the zone.
+ */
+static void bitmap_add_zone(dnssec_nsec_bitmap_t *map, const zone_node_t *node)
+{
+ if (!node) {
+ return;
+ }
+
+ for (int i = 0; i < node->rrset_count; i++) {
+ dnssec_nsec_bitmap_add(map, node->rrs[i].type);
+ }
+}
+
+/*!
+ * \brief Add bitmap records which can be synthesized by other modules.
+ *
+ * \param qtype Current QTYPE, will never be added into the map.
+ */
+static void bitmap_add_forced(dnssec_nsec_bitmap_t *map, uint16_t qtype,
+ const uint16_t *force_types)
+{
+ for (int i = 0; force_types[i] > 0; i++) {
+ if (force_types[i] != qtype) {
+ dnssec_nsec_bitmap_add(map, force_types[i]);
+ }
+ }
+}
+
+/*!
+ * \brief Synthesize NSEC type bitmap.
+ *
+ * - The bitmap will contain types synthesized by this module.
+ * - The bitmap will contain types from zone and forced
+ * types which can be potentially synthesized by other query modules.
+ */
+static dnssec_nsec_bitmap_t *synth_bitmap(const knotd_qdata_t *qdata,
+ const uint16_t *force_types)
+{
+ dnssec_nsec_bitmap_t *map = dnssec_nsec_bitmap_new();
+ if (!map) {
+ return NULL;
+ }
+
+ uint16_t qtype = knot_pkt_qtype(qdata->query);
+ bool is_apex = (qdata->extra->contents != NULL &&
+ qdata->extra->node == qdata->extra->contents->apex);
+
+ bitmap_add_synth(map, is_apex);
+
+ bitmap_add_zone(map, qdata->extra->node);
+ if (force_types != NULL && !node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME)) {
+ bitmap_add_forced(map, qtype, force_types);
+ }
+
+ return map;
+}
+
+static bool is_deleg(const knot_pkt_t *pkt)
+{
+ return !knot_wire_get_aa(pkt->wire);
+}
+
+static knot_rrset_t *synth_nsec(knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod,
+ knot_mm_t *mm)
+{
+ const knot_dname_t *nsec_owner = is_deleg(pkt) ? qdata->extra->encloser->owner : qdata->name;
+ knot_rrset_t *nsec = knot_rrset_new(nsec_owner, KNOT_RRTYPE_NSEC,
+ KNOT_CLASS_IN, nsec_ttl(qdata), mm);
+ if (!nsec) {
+ return NULL;
+ }
+
+ knot_dname_t *next = online_nsec_next(nsec_owner, knotd_qdata_zone_name(qdata));
+ if (!next) {
+ knot_rrset_free(nsec, mm);
+ return NULL;
+ }
+
+ // If necessary, prepare types to force into NSEC bitmap.
+ uint16_t *force_types = NULL;
+ if (!is_deleg(pkt)) {
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ force_types = ctx->nsec_force_types;
+ }
+
+ dnssec_nsec_bitmap_t *bitmap = synth_bitmap(qdata, force_types);
+ if (!bitmap) {
+ free(next);
+ knot_rrset_free(nsec, mm);
+ return NULL;
+ }
+
+ size_t size = knot_dname_size(next) + dnssec_nsec_bitmap_size(bitmap);
+ uint8_t rdata[size];
+
+ int written = knot_dname_to_wire(rdata, next, size);
+ dnssec_nsec_bitmap_write(bitmap, rdata + written);
+
+ knot_dname_free(next, NULL);
+ dnssec_nsec_bitmap_free(bitmap);
+
+ if (knot_rrset_add_rdata(nsec, rdata, size, mm) != KNOT_EOK) {
+ knot_rrset_free(nsec, mm);
+ return NULL;
+ }
+
+ return nsec;
+}
+
+static knot_rrset_t *sign_rrset(const knot_dname_t *owner,
+ const knot_rrset_t *cover,
+ knotd_mod_t *mod,
+ zone_sign_ctx_t *sign_ctx,
+ knot_mm_t *mm)
+{
+ // copy of RR set with replaced owner name
+
+ knot_rrset_t *copy = knot_rrset_new(owner, cover->type, cover->rclass,
+ cover->ttl, NULL);
+ if (!copy) {
+ return NULL;
+ }
+
+ if (knot_rdataset_copy(&copy->rrs, &cover->rrs, NULL) != KNOT_EOK) {
+ knot_rrset_free(copy, NULL);
+ return NULL;
+ }
+
+ // resulting RRSIG
+
+ knot_rrset_t *rrsig = knot_rrset_new(owner, KNOT_RRTYPE_RRSIG, copy->rclass,
+ copy->ttl, mm);
+ if (!rrsig) {
+ knot_rrset_free(copy, NULL);
+ return NULL;
+ }
+
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ pthread_rwlock_rdlock(&ctx->signing_mutex);
+ int ret = knot_sign_rrset2(rrsig, copy, sign_ctx, mm);
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(copy, NULL);
+ knot_rrset_free(rrsig, mm);
+ return NULL;
+ }
+
+ knot_rrset_free(copy, NULL);
+
+ return rrsig;
+}
+
+static glue_t *find_glue_for(const knot_rrset_t *rr, const knot_pkt_t *pkt)
+{
+ for (int i = KNOT_ANSWER; i <= KNOT_AUTHORITY; i++) {
+ const knot_pktsection_t *section = knot_pkt_section(pkt, i);
+ for (int j = 0; j < section->count; j++) {
+ const knot_rrset_t *attempt = knot_pkt_rr(section, j);
+ const additional_t *a = attempt->additional;
+ for (int k = 0; a != NULL && k < a->count; k++) {
+ // no need for knot_dname_cmp because the pointers are assigned
+ if (a->glues[k].node->owner == rr->owner) {
+ return &a->glues[k];
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+static bool shall_sign_rr(const knot_rrset_t *rr, const knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt->current == KNOT_ADDITIONAL) {
+ glue_t *g = find_glue_for(rr, pkt);
+ assert(g); // finds actually the node which is rr in
+ const zone_node_t *gn = glue_node(g, qdata->extra->node);
+ return !(gn->flags & NODE_FLAGS_NONAUTH);
+ } else {
+ return !is_deleg(pkt) || rr->type == KNOT_RRTYPE_NSEC;
+ }
+}
+
+static knotd_in_state_t sign_section(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ if (!want_dnssec(qdata)) {
+ return state;
+ }
+
+ const knot_pktsection_t *section = knot_pkt_section(pkt, pkt->current);
+ assert(section);
+
+ zone_sign_ctx_t *sign_ctx = zone_sign_ctx(mod->keyset, mod->dnssec);
+ if (sign_ctx == NULL) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ uint16_t count_unsigned = section->count;
+ for (int i = 0; i < count_unsigned; i++) {
+ const knot_rrset_t *rr = knot_pkt_rr(section, i);
+ if (!shall_sign_rr(rr, pkt, qdata)) {
+ continue;
+ }
+
+ uint16_t rr_pos = knot_pkt_rr_offset(section, i);
+
+ knot_dname_storage_t owner;
+ knot_dname_unpack(owner, pkt->wire + rr_pos, sizeof(owner), pkt->wire);
+ knot_dname_to_lower(owner);
+
+ knot_rrset_t *rrsig = sign_rrset(owner, rr, mod, sign_ctx, &pkt->mm);
+ if (!rrsig) {
+ state = KNOTD_IN_STATE_ERROR;
+ break;
+ }
+
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, rrsig, KNOT_PF_FREE);
+ if (r != KNOT_EOK) {
+ knot_rrset_free(rrsig, &pkt->mm);
+ state = KNOTD_IN_STATE_ERROR;
+ break;
+ }
+ }
+
+ zone_sign_ctx_free(sign_ctx);
+
+ return state;
+}
+
+static knotd_in_state_t synth_authority(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ if (state == KNOTD_IN_STATE_HIT) {
+ return state;
+ }
+
+ // synthesise NSEC
+
+ if (want_dnssec(qdata)) {
+ knot_rrset_t *nsec = synth_nsec(pkt, qdata, mod, &pkt->mm);
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, nsec, KNOT_PF_FREE);
+ if (r != DNSSEC_EOK) {
+ knot_rrset_free(nsec, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+ }
+
+ // promote NXDOMAIN to NODATA
+
+ if (want_dnssec(qdata) && state == KNOTD_IN_STATE_MISS) {
+ //! \todo Override RCODE set in solver_authority. Review.
+ qdata->rcode = KNOT_RCODE_NOERROR;
+ return KNOTD_IN_STATE_NODATA;
+ }
+
+ return state;
+}
+
+static knot_rrset_t *synth_dnskey(knotd_qdata_t *qdata, knotd_mod_t *mod,
+ knot_mm_t *mm)
+{
+ knot_rrset_t *dnskey = knot_rrset_new(knotd_qdata_zone_name(qdata),
+ KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN,
+ dnskey_ttl(qdata), mm);
+ if (!dnskey) {
+ return 0;
+ }
+
+ dnssec_binary_t rdata = { 0 };
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ pthread_rwlock_rdlock(&ctx->signing_mutex);
+ for (size_t i = 0; i < mod->keyset->count; i++) {
+ if (!mod->keyset->keys[i].is_public) {
+ continue;
+ }
+
+ dnssec_key_get_rdata(mod->keyset->keys[i].key, &rdata);
+ assert(rdata.size > 0 && rdata.data);
+
+ int r = knot_rrset_add_rdata(dnskey, rdata.data, rdata.size, mm);
+ if (r != KNOT_EOK) {
+ knot_rrset_free(dnskey, mm);
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+ return NULL;
+ }
+ }
+
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+ return dnskey;
+}
+
+static knot_rrset_t *synth_cdnskey(knotd_qdata_t *qdata, knotd_mod_t *mod,
+ knot_mm_t *mm)
+{
+ knot_rrset_t *dnskey = knot_rrset_new(knotd_qdata_zone_name(qdata),
+ KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN,
+ 0, mm);
+ if (dnskey == NULL) {
+ return 0;
+ }
+
+ dnssec_binary_t rdata = { 0 };
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ pthread_rwlock_rdlock(&ctx->signing_mutex);
+ keyptr_dynarray_t kcdnskeys = knot_zone_sign_get_cdnskeys(mod->dnssec, mod->keyset);
+ knot_dynarray_foreach(keyptr, zone_key_t *, ksk_for_cdnskey, kcdnskeys) {
+ dnssec_key_get_rdata((*ksk_for_cdnskey)->key, &rdata);
+ assert(rdata.size > 0 && rdata.data);
+ (void)knot_rrset_add_rdata(dnskey, rdata.data, rdata.size, mm);
+ }
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+
+ return dnskey;
+}
+
+static knot_rrset_t *synth_cds(knotd_qdata_t *qdata, knotd_mod_t *mod,
+ knot_mm_t *mm)
+{
+ knot_rrset_t *ds = knot_rrset_new(knotd_qdata_zone_name(qdata),
+ KNOT_RRTYPE_CDS, KNOT_CLASS_IN,
+ 0, mm);
+ if (ds == NULL) {
+ return 0;
+ }
+
+ dnssec_binary_t rdata = { 0 };
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ pthread_rwlock_rdlock(&ctx->signing_mutex);
+ keyptr_dynarray_t kcdnskeys = knot_zone_sign_get_cdnskeys(mod->dnssec, mod->keyset);
+ knot_dynarray_foreach(keyptr, zone_key_t *, ksk_for_cds, kcdnskeys) {
+ zone_key_calculate_ds(*ksk_for_cds, mod->dnssec->policy->cds_dt, &rdata);
+ assert(rdata.size > 0 && rdata.data);
+ (void)knot_rrset_add_rdata(ds, rdata.data, rdata.size, mm);
+ }
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+
+ return ds;
+}
+
+static bool qtype_match(knotd_qdata_t *qdata, uint16_t type)
+{
+ uint16_t qtype = knot_pkt_qtype(qdata->query);
+ return (qtype == type);
+}
+
+static bool is_apex_query(knotd_qdata_t *qdata)
+{
+ return knot_dname_is_equal(qdata->name, knotd_qdata_zone_name(qdata));
+}
+
+static knotd_in_state_t pre_routine(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ online_sign_ctx_t *ctx = knotd_mod_ctx(mod);
+ zone_sign_reschedule_t resch = { 0 };
+
+ (void)pkt, (void)qdata;
+
+ pthread_mutex_lock(&ctx->event_mutex);
+ if (ctx->zone_doomed) {
+ pthread_mutex_unlock(&ctx->event_mutex);
+ return KNOTD_IN_STATE_ERROR;
+ }
+ mod->dnssec->now = time(NULL);
+ int ret = KNOT_ESEMCHECK;
+ if (knot_time_cmp(ctx->event_parent_ds_q, mod->dnssec->now) <= 0) {
+ pthread_rwlock_rdlock(&ctx->signing_mutex);
+ ret = knot_parent_ds_query(conf(), mod->dnssec, 1000);
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+ if (ret != KNOT_EOK && ret != KNOT_NO_READY_KEY && mod->dnssec->policy->ksk_sbm_check_interval > 0) {
+ ctx->event_parent_ds_q = mod->dnssec->now + mod->dnssec->policy->ksk_sbm_check_interval;
+ } else {
+ ctx->event_parent_ds_q = 0;
+ }
+ }
+ if (ret == KNOT_EOK || knot_time_cmp(ctx->event_rollover, mod->dnssec->now) <= 0) {
+ update_policy_from_zone(mod->dnssec->policy, qdata->extra->contents);
+ ret = knot_dnssec_key_rollover(mod->dnssec, KEY_ROLL_ALLOW_KSK_ROLL | KEY_ROLL_ALLOW_ZSK_ROLL, &resch);
+ if (ret != KNOT_EOK) {
+ ctx->event_rollover = knot_dnssec_failover_delay(mod->dnssec);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ if (resch.plan_ds_check && mod->dnssec->policy->ksk_sbm_check_interval > 0) {
+ ctx->event_parent_ds_q = mod->dnssec->now + mod->dnssec->policy->ksk_sbm_check_interval;
+ } else {
+ ctx->event_parent_ds_q = 0;
+ }
+
+ ctx->event_rollover = resch.next_rollover;
+
+ pthread_rwlock_wrlock(&ctx->signing_mutex);
+ knotd_mod_dnssec_unload_keyset(mod);
+ ret = knotd_mod_dnssec_load_keyset(mod, true);
+ if (ret != KNOT_EOK) {
+ ctx->zone_doomed = true;
+ state = KNOTD_IN_STATE_ERROR;
+ } else {
+ ctx->event_rollover = knot_time_min(ctx->event_rollover, knot_get_next_zone_key_event(mod->keyset));
+ }
+ pthread_rwlock_unlock(&ctx->signing_mutex);
+ }
+ pthread_mutex_unlock(&ctx->event_mutex);
+
+ return state;
+}
+
+static knotd_in_state_t synth_answer(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ // disallowed queries
+
+ if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_RRSIG) {
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ // synthesized DNSSEC answers
+
+ if (qtype_match(qdata, KNOT_RRTYPE_DNSKEY) && is_apex_query(qdata)) {
+ knot_rrset_t *dnskey = synth_dnskey(qdata, mod, &pkt->mm);
+ if (!dnskey) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, dnskey, KNOT_PF_FREE);
+ if (r != DNSSEC_EOK) {
+ knot_rrset_free(dnskey, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+ state = KNOTD_IN_STATE_HIT;
+ }
+
+ if (qtype_match(qdata, KNOT_RRTYPE_CDNSKEY) && is_apex_query(qdata)) {
+ knot_rrset_t *dnskey = synth_cdnskey(qdata, mod, &pkt->mm);
+ if (!dnskey) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, dnskey, KNOT_PF_FREE);
+ if (r != DNSSEC_EOK) {
+ knot_rrset_free(dnskey, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+ state = KNOTD_IN_STATE_HIT;
+ }
+
+ if (qtype_match(qdata, KNOT_RRTYPE_CDS) && is_apex_query(qdata)) {
+ knot_rrset_t *ds = synth_cds(qdata, mod, &pkt->mm);
+ if (!ds) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, ds, KNOT_PF_FREE);
+ if (r != DNSSEC_EOK) {
+ knot_rrset_free(ds, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+ state = KNOTD_IN_STATE_HIT;
+ }
+
+ if (qtype_match(qdata, KNOT_RRTYPE_NSEC)) {
+ knot_rrset_t *nsec = synth_nsec(pkt, qdata, mod, &pkt->mm);
+ if (!nsec) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, nsec, KNOT_PF_FREE);
+ if (r != DNSSEC_EOK) {
+ knot_rrset_free(nsec, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ state = KNOTD_IN_STATE_HIT;
+ }
+
+ return state;
+}
+
+static void online_sign_ctx_free(online_sign_ctx_t *ctx)
+{
+ pthread_mutex_destroy(&ctx->event_mutex);
+ pthread_rwlock_destroy(&ctx->signing_mutex);
+
+ free(ctx->nsec_force_types);
+ free(ctx);
+}
+
+static int online_sign_ctx_new(online_sign_ctx_t **ctx_ptr, knotd_mod_t *mod)
+{
+ online_sign_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knotd_mod_dnssec_init(mod);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ // Historically, the default scheme is Single-Type signing.
+ if (mod->dnssec->policy->sts_default) {
+ mod->dnssec->policy->single_type_signing = true;
+ }
+
+ zone_sign_reschedule_t resch = { 0 };
+ ret = knot_dnssec_key_rollover(mod->dnssec, KEY_ROLL_ALLOW_KSK_ROLL | KEY_ROLL_ALLOW_ZSK_ROLL, &resch);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ if (resch.plan_ds_check) {
+ ctx->event_parent_ds_q = time(NULL);
+ }
+ ctx->event_rollover = resch.next_rollover;
+
+ ret = knotd_mod_dnssec_load_keyset(mod, true);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ ctx->event_rollover = knot_time_min(ctx->event_rollover, knot_get_next_zone_key_event(mod->keyset));
+
+ pthread_mutex_init(&ctx->event_mutex, NULL);
+ pthread_rwlock_init(&ctx->signing_mutex, NULL);
+
+ *ctx_ptr = ctx;
+
+ return KNOT_EOK;
+}
+
+int load_nsec_bitmap(online_sign_ctx_t *ctx, knotd_conf_t *conf)
+{
+ int count = (conf->count > 0) ? conf->count : sizeof(NSEC_FORCE_TYPES) / sizeof(uint16_t);
+ ctx->nsec_force_types = calloc(count + 1, sizeof(uint16_t));
+ if (ctx->nsec_force_types == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (conf->count == 0) {
+ // Use the default list.
+ for (int i = 0; NSEC_FORCE_TYPES[i] > 0; i++) {
+ ctx->nsec_force_types[i] = NSEC_FORCE_TYPES[i];
+ }
+ } else {
+ for (int i = 0; i < conf->count; i++) {
+ int ret = knot_rrtype_from_string(conf->multi[i].string,
+ &ctx->nsec_force_types[i]);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int online_sign_load(knotd_mod_t *mod)
+{
+ knotd_conf_t conf = knotd_conf_zone(mod, C_DNSSEC_SIGNING,
+ knotd_mod_zone(mod));
+ if (conf.single.boolean) {
+ knotd_mod_log(mod, LOG_ERR, "incompatible with automatic signing");
+ return KNOT_ENOTSUP;
+ }
+
+ online_sign_ctx_t *ctx = NULL;
+ int ret = online_sign_ctx_new(&ctx, mod);
+ if (ret != KNOT_EOK) {
+ knotd_mod_log(mod, LOG_ERR, "failed to initialize signing key (%s)",
+ knot_strerror(ret));
+ return KNOT_ERROR;
+ }
+
+ if (mod->dnssec->policy->offline_ksk) {
+ knotd_mod_log(mod, LOG_ERR, "incompatible with offline KSK mode");
+ online_sign_ctx_free(ctx);
+ return KNOT_ENOTSUP;
+ }
+
+ conf = knotd_conf_mod(mod, MOD_NSEC_BITMAP);
+ ret = load_nsec_bitmap(ctx, &conf);
+ knotd_conf_free(&conf);
+ if (ret != KNOT_EOK) {
+ online_sign_ctx_free(ctx);
+ return ret;
+ }
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, pre_routine);
+
+ knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, synth_answer);
+ knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, sign_section);
+
+ knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, synth_authority);
+ knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, sign_section);
+
+ knotd_mod_in_hook(mod, KNOTD_STAGE_ADDITIONAL, sign_section);
+
+ return KNOT_EOK;
+}
+
+void online_sign_unload(knotd_mod_t *mod)
+{
+ online_sign_ctx_free(knotd_mod_ctx(mod));
+}
+
+KNOTD_MOD_API(onlinesign, KNOTD_MOD_FLAG_SCOPE_ZONE | KNOTD_MOD_FLAG_OPT_CONF,
+ online_sign_load, online_sign_unload, online_sign_conf, NULL);
diff --git a/src/knot/modules/onlinesign/onlinesign.rst b/src/knot/modules/onlinesign/onlinesign.rst
new file mode 100644
index 0000000..c1859e2
--- /dev/null
+++ b/src/knot/modules/onlinesign/onlinesign.rst
@@ -0,0 +1,158 @@
+.. _mod-onlinesign:
+
+``onlinesign`` — Online DNSSEC signing
+======================================
+
+The module provides online DNSSEC signing. Instead of pre-computing the zone
+signatures when the zone is loaded into the server or instead of loading an
+externally signed zone, the signatures are computed on-the-fly during
+answering.
+
+The main purpose of the module is to enable authenticated responses with
+zones which use other dynamic module (e.g., automatic reverse record
+synthesis) because these zones cannot be pre-signed. However, it can be also
+used as a simple signing solution for zones with low traffic and also as
+a protection against zone content enumeration (zone walking).
+
+In order to minimize the number of computed signatures per query, the module
+produces a bit different responses from the responses that would be sent if
+the zone was pre-signed. Still, the responses should be perfectly valid for
+a DNSSEC validating resolver.
+
+.. rubric:: Differences from statically signed zones:
+
+* The NSEC records are constructed as Minimally Covering NSEC Records
+ (:rfc:`7129#appendix-A`). Therefore the generated domain names cover
+ the complete domain name space in the zone's authority.
+
+* NXDOMAIN responses are promoted to NODATA responses. The module proves
+ that the query type does not exist rather than that the domain name does not
+ exist.
+
+* Domain names matching a wildcard are expanded. The module pretends and proves
+ that the domain name exists rather than proving a presence of the wildcard.
+
+.. rubric:: Records synthesized by the module:
+
+* DNSKEY record is synthesized in the zone apex and includes public key
+ material for the active signing key.
+
+* NSEC records are synthesized as needed.
+
+* RRSIG records are synthesized for authoritative content of the zone.
+
+* CDNSKEY and CDS records are generated as usual to publish valid Secure Entry Point.
+
+.. rubric:: Limitations:
+
+* Due to limited interaction between the server and the module,
+ after any change to KASP DB (including `knotc zone-ksk-submitted` command)
+ or when a scheduled DNSSEC event shall be processed (e.g. transition to next
+ DNSKEY rollover state) the server must be reloaded or queried to the zone
+ (with the DO bit set) to apply the change or to trigger the event. For optimal
+ operation, the recommended query frequency is at least ones per second for
+ each zone configured.
+
+* The NSEC records may differ for one domain name if queried for different
+ types. This is an implementation shortcoming as the dynamic modules
+ cooperate loosely. Possible synthesis of a type by other module cannot
+ be predicted. This dissimilarity should not affect response validation,
+ even with validators performing aggressive negative caching (:rfc:`8198`).
+
+* The module isn't compatible with the Offline KSK mode yet.
+
+.. rubric:: Recommendations:
+
+* Configure the module with an explicit signing policy which has the
+ :ref:`policy_rrsig-lifetime` value in the order of hours.
+
+* Note that :ref:`policy_single-type-signing` should be set explicitly to
+ avoid fallback to backward-compatible default.
+
+Example
+-------
+
+* Enable the module in the zone configuration with the default signing policy::
+
+ zone:
+ - domain: example.com
+ module: mod-onlinesign
+
+ Or with an explicit signing policy::
+
+ policy:
+ - id: rsa
+ algorithm: RSASHA256
+ ksk-size: 2048
+ rrsig-lifetime: 25h
+ rrsig-refresh: 20h
+
+ mod-onlinesign:
+ - id: explicit
+ policy: rsa
+
+ zone:
+ - domain: example.com
+ module: mod-onlinesign/explicit
+
+ Or use manual policy in an analogous manner, see
+ :ref:`Manual key management<dnssec-manual-key-management>`.
+
+* Make sure the zone is not signed and also that the automatic signing is
+ disabled. All is set, you are good to go. Reload (or start) the server:
+
+ .. code-block:: console
+
+ $ knotc reload
+
+The following example stacks the online signing with reverse record synthesis
+module::
+
+ mod-synthrecord:
+ - id: lan-forward
+ type: forward
+ prefix: ip-
+ ttl: 1200
+ network: 192.168.100.0/24
+
+ zone:
+ - domain: corp.example.net
+ module: [mod-synthrecord/lan-forward, mod-onlinesign]
+
+Module reference
+----------------
+
+::
+
+ mod-onlinesign:
+ - id: STR
+ policy: policy_id
+ nsec-bitmap: STR ...
+
+.. _mod-onlinesign_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-onlinesign_policy:
+
+policy
+......
+
+A :ref:`reference<policy_id>` to DNSSEC signing policy. A special *default*
+value can be used for the default policy setting.
+
+*Default:* an imaginary policy with all default values
+
+.. _mod-onlinesign_nsec-bitmap:
+
+nsec-bitmap
+...........
+
+A list of Resource Record types included in an NSEC bitmap generated by the module.
+This option should reflect zone contents or synthesized responses by modules,
+such as :ref:`synthrecord<mod-synthrecord>` and :ref:`GeoIP<mod-geoip>`.
+
+*Default:* ``[A, AAAA]``
diff --git a/src/knot/modules/probe/Makefile.inc b/src/knot/modules/probe/Makefile.inc
new file mode 100644
index 0000000..db14fc4
--- /dev/null
+++ b/src/knot/modules/probe/Makefile.inc
@@ -0,0 +1,12 @@
+knot_modules_probe_la_SOURCES = knot/modules/probe/probe.c
+EXTRA_DIST += knot/modules/probe/probe.rst
+
+if STATIC_MODULE_probe
+libknotd_la_SOURCES += $(knot_modules_probe_la_SOURCES)
+endif
+
+if SHARED_MODULE_probe
+knot_modules_probe_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_probe_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+pkglib_LTLIBRARIES += knot/modules/probe.la
+endif
diff --git a/src/knot/modules/probe/probe.c b/src/knot/modules/probe/probe.c
new file mode 100644
index 0000000..bcaa707
--- /dev/null
+++ b/src/knot/modules/probe/probe.c
@@ -0,0 +1,190 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <stdint.h>
+
+#include "knot/conf/schema.h"
+#include "knot/include/module.h"
+#include "contrib/string.h"
+#include "contrib/time.h"
+#include "libknot/libknot.h"
+
+#ifdef HAVE_ATOMIC
+#define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
+#define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
+#else
+#define ATOMIC_SET(dst, val) ((dst) = (val))
+#define ATOMIC_GET(src) (src)
+#endif
+
+#define MOD_PATH "\x04""path"
+#define MOD_CHANNELS "\x08""channels"
+#define MOD_MAX_RATE "\x08""max-rate"
+
+const yp_item_t probe_conf[] = {
+ { MOD_PATH, YP_TSTR, YP_VNONE },
+ { MOD_CHANNELS, YP_TINT, YP_VINT = { 1, UINT16_MAX, 1 } },
+ { MOD_MAX_RATE, YP_TINT, YP_VINT = { 0, UINT32_MAX, 100000 } },
+ { NULL }
+};
+
+typedef struct {
+ knot_probe_t **probes;
+ size_t probe_count;
+ uint64_t *last_times;
+ uint64_t min_diff_ns;
+ char *path;
+} probe_ctx_t;
+
+static void free_probe_ctx(probe_ctx_t *ctx)
+{
+ for (int i = 0; ctx->probes != NULL && i < ctx->probe_count; ++i) {
+ knot_probe_free(ctx->probes[i]);
+ }
+ free(ctx->probes);
+ free(ctx->last_times);
+ free(ctx->path);
+ free(ctx);
+}
+
+static knotd_state_t export(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata);
+
+ probe_ctx_t *ctx = knotd_mod_ctx(mod);
+ uint16_t idx = qdata->params->thread_id % ctx->probe_count;
+ knot_probe_t *probe = ctx->probes[idx];
+
+ // Check the rate limit if enabled.
+ if (ctx->min_diff_ns > 0) {
+ struct timespec now = time_now();
+ uint64_t now_ns = 1000000000 * now.tv_sec + now.tv_nsec;
+ uint64_t last_ns = ATOMIC_GET(ctx->last_times[idx]);
+ if (now_ns - last_ns < ctx->min_diff_ns) {
+ return state;
+ }
+ ATOMIC_SET(ctx->last_times[idx], now_ns);
+ }
+
+ // Prepare data sources.
+ struct sockaddr_storage buff;
+ const struct sockaddr_storage *local = knotd_qdata_local_addr(qdata, &buff);
+ const struct sockaddr_storage *remote = knotd_qdata_remote_addr(qdata);
+
+ knot_probe_proto_t proto = (knot_probe_proto_t)qdata->params->proto;
+ const knot_pkt_t *reply = (state != KNOTD_STATE_NOOP ? pkt : NULL);
+
+ uint16_t rcode = qdata->rcode;
+ if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
+ rcode = qdata->rcode_tsig;
+ }
+
+ // Fill out and export the data structure.
+ knot_probe_data_t d;
+ int ret = knot_probe_data_set(&d, proto, local, remote, qdata->query, reply, rcode);
+ if (ret == KNOT_EOK) {
+ d.tcp_rtt = knotd_qdata_rtt(qdata);
+ if (qdata->query->opt_rr != NULL) {
+ d.reply.ede = qdata->rcode_ede;
+ }
+ (void)knot_probe_produce(probe, &d, 1);
+ }
+
+ return state;
+}
+
+int probe_load(knotd_mod_t *mod)
+{
+ probe_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knotd_conf_t conf = knotd_conf_mod(mod, MOD_CHANNELS);
+ ctx->probe_count = conf.single.integer;
+
+ conf = knotd_conf_mod(mod, MOD_PATH);
+ if (conf.count == 0) {
+ conf = knotd_conf(mod, C_SRV, C_RUNDIR, NULL);
+ }
+ if (conf.single.string[0] != '/') {
+ char *cwd = realpath("./", NULL);
+ ctx->path = sprintf_alloc("%s/%s", cwd, conf.single.string);
+ free(cwd);
+ } else {
+ ctx->path = strdup(conf.single.string);
+ }
+ if (ctx->path == NULL) {
+ free_probe_ctx(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ ctx->probes = calloc(ctx->probe_count, sizeof(knot_probe_t *));
+ if (ctx->probes == NULL) {
+ free_probe_ctx(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ ctx->last_times = calloc(ctx->probe_count, sizeof(uint64_t));
+ if (ctx->last_times == NULL) {
+ free_probe_ctx(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ ctx->min_diff_ns = 0;
+ conf = knotd_conf_mod(mod, MOD_MAX_RATE);
+ if (conf.single.integer > 0) {
+ ctx->min_diff_ns = ctx->probe_count * 1000000000 / conf.single.integer;
+ }
+
+ for (int i = 0; i < ctx->probe_count; i++) {
+ knot_probe_t *probe = knot_probe_alloc();
+ if (probe == NULL) {
+ free_probe_ctx(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ int ret = knot_probe_set_producer(probe, ctx->path, i + 1);
+ switch (ret) {
+ case KNOT_ECONN:
+ knotd_mod_log(mod, LOG_NOTICE, "channel %i not connected", i + 1);
+ case KNOT_EOK:
+ break;
+ default:
+ free_probe_ctx(ctx);
+ return ret;
+ }
+
+ ctx->probes[i] = probe;
+ }
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_END, export);
+}
+
+void probe_unload(knotd_mod_t *mod)
+{
+ probe_ctx_t *ctx = knotd_mod_ctx(mod);
+ if (ctx != NULL) {
+ free_probe_ctx(ctx);
+ }
+}
+
+KNOTD_MOD_API(probe, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
+ probe_load, probe_unload, probe_conf, NULL);
diff --git a/src/knot/modules/probe/probe.rst b/src/knot/modules/probe/probe.rst
new file mode 100644
index 0000000..e3657b9
--- /dev/null
+++ b/src/knot/modules/probe/probe.rst
@@ -0,0 +1,89 @@
+.. _mod-probe:
+
+``probe`` — DNS traffic probe
+=============================
+
+The module allows the server to send simplified information about regular DNS
+traffic through *UNIX* sockets. The exported information consists of data blocks
+where each data block (datagram) describes one query/response pair. The response
+part can be empty. The receiver can be an arbitrary program using *libknot* interface
+(C or Python). In case of high traffic, more channels (sockets) can be configured
+to allow parallel processing.
+
+.. NOTE::
+ A simple `probe client <https://gitlab.nic.cz/knot/knot-dns/-/blob/master/scripts/probe_dump.py>`_ in Python.
+
+Example
+-------
+
+Default module configuration::
+
+ template:
+ - id: default
+ global-module: mod-probe
+
+Per zone probe with 8 channels and maximum 1M logs per second limit::
+
+ mod-probe:
+ - id: custom
+ path: /tmp/knot-probe
+ channels: 8
+ max-rate: 1000000
+
+ zone:
+ - domain: example.com.
+ module: mod-probe/custom
+
+
+Module reference
+----------------
+
+::
+
+ mod-probe:
+ - id: STR
+ path: STR
+ channels: INT
+ max-rate: INT
+
+.. _mod-probe_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-probe_path:
+
+path
+....
+
+A directory path the UNIX sockets are located.
+
+.. NOTE::
+ It's recommended to use a directory with the execute permission restricted
+ to the intended probe consumer process owner only.
+
+*Default:* :ref:`rundir<server_rundir>`
+
+.. _mod-probe_channels:
+
+channels
+........
+
+Number of channels (UNIX sockets) the traffic is distributed to. In case of
+high DNS traffic which is beeing processed by many UDP/XDP/TCP workers,
+using more channels reduces the module overhead.
+
+*Default:* ``1``
+
+.. _mod-probe_max-rate:
+
+max-rate
+........
+
+Maximum number of queries/replies per second the probe is allowed to transfer.
+If the limit is exceeded, the over-limit traffic is ignored. Zero value means
+no limit.
+
+*Default:* ``100000`` (one hundred thousand)
diff --git a/src/knot/modules/queryacl/Makefile.inc b/src/knot/modules/queryacl/Makefile.inc
new file mode 100644
index 0000000..25dcc38
--- /dev/null
+++ b/src/knot/modules/queryacl/Makefile.inc
@@ -0,0 +1,12 @@
+knot_modules_queryacl_la_SOURCES = knot/modules/queryacl/queryacl.c
+EXTRA_DIST += knot/modules/queryacl/queryacl.rst
+
+if STATIC_MODULE_queryacl
+libknotd_la_SOURCES += $(knot_modules_queryacl_la_SOURCES)
+endif
+
+if SHARED_MODULE_queryacl
+knot_modules_queryacl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_queryacl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+pkglib_LTLIBRARIES += knot/modules/queryacl.la
+endif
diff --git a/src/knot/modules/queryacl/queryacl.c b/src/knot/modules/queryacl/queryacl.c
new file mode 100644
index 0000000..e787083
--- /dev/null
+++ b/src/knot/modules/queryacl/queryacl.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2021 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 "knot/include/module.h"
+#include "contrib/sockaddr.h"
+
+#define MOD_ADDRESS "\x07""address"
+#define MOD_INTERFACE "\x09""interface"
+
+const yp_item_t queryacl_conf[] = {
+ { MOD_ADDRESS, YP_TNET, YP_VNONE, YP_FMULTI },
+ { MOD_INTERFACE, YP_TNET, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+typedef struct {
+ knotd_conf_t allow_addr;
+ knotd_conf_t allow_iface;
+} queryacl_ctx_t;
+
+static knotd_state_t queryacl_process(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ queryacl_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ // Continue only for regular queries.
+ if (qdata->type != KNOTD_QUERY_TYPE_NORMAL) {
+ return state;
+ }
+
+ if (ctx->allow_addr.count > 0) {
+ const struct sockaddr_storage *addr = knotd_qdata_remote_addr(qdata);
+ if (!knotd_conf_addr_range_match(&ctx->allow_addr, addr)) {
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ return KNOTD_STATE_FAIL;
+ }
+ }
+
+ if (ctx->allow_iface.count > 0) {
+ struct sockaddr_storage buff;
+ const struct sockaddr_storage *addr = knotd_qdata_local_addr(qdata, &buff);
+ if (!knotd_conf_addr_range_match(&ctx->allow_iface, addr)) {
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ return KNOTD_STATE_FAIL;
+ }
+ }
+
+ return state;
+}
+
+int queryacl_load(knotd_mod_t *mod)
+{
+ // Create module context.
+ queryacl_ctx_t *ctx = calloc(1, sizeof(queryacl_ctx_t));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ ctx->allow_addr = knotd_conf_mod(mod, MOD_ADDRESS);
+ ctx->allow_iface = knotd_conf_mod(mod, MOD_INTERFACE);
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, queryacl_process);
+}
+
+void queryacl_unload(knotd_mod_t *mod)
+{
+ queryacl_ctx_t *ctx = knotd_mod_ctx(mod);
+ if (ctx != NULL) {
+ knotd_conf_free(&ctx->allow_addr);
+ knotd_conf_free(&ctx->allow_iface);
+ }
+ free(ctx);
+}
+
+KNOTD_MOD_API(queryacl, KNOTD_MOD_FLAG_SCOPE_ANY,
+ queryacl_load, queryacl_unload, queryacl_conf, NULL);
diff --git a/src/knot/modules/queryacl/queryacl.rst b/src/knot/modules/queryacl/queryacl.rst
new file mode 100644
index 0000000..1a402f6
--- /dev/null
+++ b/src/knot/modules/queryacl/queryacl.rst
@@ -0,0 +1,70 @@
+.. _mod-queryacl:
+
+``queryacl`` — Limit queries by remote address or target interface
+==================================================================
+
+This module provides a simple way to whitelist incoming queries
+according to the query's source address or target interface.
+It can be used e.g. to create a restricted-access subzone with delegations from the corresponding public zone.
+The module may be enabled both globally and per-zone.
+
+.. NOTE::
+ The module limits only regular queries. Notify, transfer and update are handled by :ref:`ACL<ACL>`.
+
+Example
+-------
+
+::
+
+ mod-queryacl:
+ - id: default
+ address: [192.0.2.73-192.0.2.90, 203.0.113.0/24]
+ interface: 198.51.100
+
+ zone:
+ - domain: example.com
+ module: mod-queryacl/default
+
+Module reference
+----------------
+
+::
+
+ mod-queryacl:
+ - id: STR
+ address: ADDR[/INT] | ADDR-ADDR ...
+ interface: ADDR[/INT] | ADDR-ADDR ...
+
+.. _mod-queryacl_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-queryacl_address:
+
+address
+.......
+
+An optional list of allowed ranges and/or subnets for query's source address.
+If the query's address does not fall into any
+of the configured ranges, NOTAUTH rcode is returned.
+
+*Default:* not set
+
+.. _mod-queryacl_interface:
+
+interface
+.........
+
+An optional list of allowed ranges and/or subnets for query's target interface.
+If the interface does not fall into any
+of the configured ranges, NOTAUTH rcode is returned. Note that every interface
+used has to be configured in :ref:`listen<server_listen>`.
+
+.. NOTE::
+ Don't use values *0.0.0.0* and *::0*. These values are redundant and don't
+ work as expected.
+
+*Default:* not set
diff --git a/src/knot/modules/rrl/Makefile.inc b/src/knot/modules/rrl/Makefile.inc
new file mode 100644
index 0000000..d82edf9
--- /dev/null
+++ b/src/knot/modules/rrl/Makefile.inc
@@ -0,0 +1,15 @@
+knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \
+ knot/modules/rrl/functions.c \
+ knot/modules/rrl/functions.h
+EXTRA_DIST += knot/modules/rrl/rrl.rst
+
+if STATIC_MODULE_rrl
+libknotd_la_SOURCES += $(knot_modules_rrl_la_SOURCES)
+endif
+
+if SHARED_MODULE_rrl
+knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_rrl_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/rrl.la
+endif
diff --git a/src/knot/modules/rrl/functions.c b/src/knot/modules/rrl/functions.c
new file mode 100644
index 0000000..df35394
--- /dev/null
+++ b/src/knot/modules/rrl/functions.c
@@ -0,0 +1,554 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <time.h>
+
+#include "knot/modules/rrl/functions.h"
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+
+/* Hopscotch defines. */
+#define HOP_LEN (sizeof(unsigned)*8)
+/* Limits (class, ipv6 remote, dname) */
+#define RRL_CLSBLK_MAXLEN (1 + 8 + 255)
+/* CIDR block prefix lengths for v4/v6 */
+#define RRL_V4_PREFIX_LEN 3 /* /24 */
+#define RRL_V6_PREFIX_LEN 7 /* /56 */
+/* Defaults */
+#define RRL_SSTART 2 /* 1/Nth of the rate for slow start */
+#define RRL_PSIZE_LARGE 1024
+#define RRL_CAPACITY 4 /* Window size in seconds */
+#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */
+
+/* Classification */
+enum {
+ CLS_NULL = 0 << 0, /* Empty bucket. */
+ CLS_NORMAL = 1 << 0, /* Normal response. */
+ CLS_ERROR = 1 << 1, /* Error response. */
+ CLS_NXDOMAIN = 1 << 2, /* NXDOMAIN (special case of error). */
+ CLS_EMPTY = 1 << 3, /* Empty response. */
+ CLS_LARGE = 1 << 4, /* Response size over threshold (1024k). */
+ CLS_WILDCARD = 1 << 5, /* Wildcard query. */
+ CLS_ANY = 1 << 6, /* ANY query (spec. class). */
+ CLS_DNSSEC = 1 << 7 /* DNSSEC related RR query (spec. class) */
+};
+
+/* Classification string. */
+struct cls_name {
+ int code;
+ const char *name;
+};
+
+static const struct cls_name rrl_cls_names[] = {
+ { CLS_NORMAL, "POSITIVE" },
+ { CLS_ERROR, "ERROR" },
+ { CLS_NXDOMAIN, "NXDOMAIN"},
+ { CLS_EMPTY, "EMPTY"},
+ { CLS_LARGE, "LARGE"},
+ { CLS_WILDCARD, "WILDCARD"},
+ { CLS_ANY, "ANY"},
+ { CLS_DNSSEC, "DNSSEC"},
+ { CLS_NULL, "NULL"},
+ { CLS_NULL, NULL}
+};
+
+static inline const char *rrl_clsstr(int code)
+{
+ for (const struct cls_name *c = rrl_cls_names; c->name; c++) {
+ if (c->code == code) {
+ return c->name;
+ }
+ }
+
+ return "unknown class";
+}
+
+/* Bucket flags. */
+enum {
+ RRL_BF_NULL = 0 << 0, /* No flags. */
+ RRL_BF_SSTART = 1 << 0, /* Bucket in slow-start after collision. */
+ RRL_BF_ELIMIT = 1 << 1 /* Bucket is rate-limited. */
+};
+
+static uint8_t rrl_clsid(rrl_req_t *p)
+{
+ /* Check error code */
+ int ret = CLS_NULL;
+ switch (knot_wire_get_rcode(p->wire)) {
+ case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break;
+ case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break;
+ default: return CLS_ERROR; break;
+ }
+
+ /* Check if answered from a qname */
+ if (ret == CLS_NORMAL && p->flags & RRL_REQ_WILDCARD) {
+ return CLS_WILDCARD;
+ }
+
+ /* Check query type for spec. classes. */
+ if (p->query) {
+ switch(knot_pkt_qtype(p->query)) {
+ case KNOT_RRTYPE_ANY: /* ANY spec. class */
+ return CLS_ANY;
+ break;
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_DS: /* DNSSEC-related RR class. */
+ return CLS_DNSSEC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Check packet size for threshold. */
+ if (p->len >= RRL_PSIZE_LARGE) {
+ return CLS_LARGE;
+ }
+
+ /* Check ancount */
+ if (knot_wire_get_ancount(p->wire) == 0) {
+ return CLS_EMPTY;
+ }
+
+ return ret;
+}
+
+static int rrl_clsname(uint8_t *dst, size_t maxlen, uint8_t cls, rrl_req_t *req,
+ const knot_dname_t *name)
+{
+ if (name == NULL) {
+ /* Fallback for errors etc. */
+ name = (const knot_dname_t *)"\x00";
+ }
+
+ switch (cls) {
+ case CLS_ERROR: /* Could be a non-existent zone or garbage. */
+ case CLS_NXDOMAIN: /* Queries to non-existent names in zone. */
+ case CLS_WILDCARD: /* Queries to names covered by a wildcard. */
+ break;
+ default:
+ /* Use QNAME */
+ if (req->query) {
+ name = knot_pkt_qname(req->query);
+ }
+ break;
+ }
+
+ /* Write to wire */
+ return knot_dname_to_wire(dst, name, maxlen);
+}
+
+static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_storage *remote,
+ rrl_req_t *req, const knot_dname_t *name)
+{
+ /* Class */
+ uint8_t cls = rrl_clsid(req);
+ *dst = cls;
+ int blklen = sizeof(cls);
+
+ /* Address (in network byteorder, adjust masks). */
+ uint64_t netblk = 0;
+ if (remote->ss_family == AF_INET6) {
+ struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)remote;
+ memcpy(&netblk, &ipv6->sin6_addr, RRL_V6_PREFIX_LEN);
+ } else {
+ struct sockaddr_in *ipv4 = (struct sockaddr_in *)remote;
+ memcpy(&netblk, &ipv4->sin_addr, RRL_V4_PREFIX_LEN);
+ }
+ memcpy(dst + blklen, &netblk, sizeof(netblk));
+ blklen += sizeof(netblk);
+
+ /* Name */
+ int ret = rrl_clsname(dst + blklen, maxlen - blklen, cls, req, name);
+ if (ret < 0) {
+ return ret;
+ }
+ uint8_t len = ret;
+ blklen += len;
+
+ return blklen;
+}
+
+static int bucket_free(rrl_item_t *bucket, uint32_t now)
+{
+ return bucket->cls == CLS_NULL || (bucket->time + 1 < now);
+}
+
+static int bucket_match(rrl_item_t *bucket, rrl_item_t *match)
+{
+ return bucket->cls == match->cls &&
+ bucket->netblk == match->netblk &&
+ bucket->qname == match->qname;
+}
+
+static int find_free(rrl_table_t *tbl, unsigned id, uint32_t now)
+{
+ for (int i = id; i < tbl->size; i++) {
+ if (bucket_free(&tbl->arr[i], now)) {
+ return i - id;
+ }
+ }
+ for (int i = 0; i < id; i++) {
+ if (bucket_free(&tbl->arr[i], now)) {
+ return i + (tbl->size - id);
+ }
+ }
+
+ /* this happens if table is full... force vacate current elm */
+ return id;
+}
+
+static inline unsigned find_match(rrl_table_t *tbl, uint32_t id, rrl_item_t *m)
+{
+ unsigned new_id = 0;
+ unsigned hop = 0;
+ unsigned match_bitmap = tbl->arr[id].hop;
+ while (match_bitmap != 0) {
+ hop = __builtin_ctz(match_bitmap); /* offset of next potential match */
+ new_id = (id + hop) % tbl->size;
+ if (bucket_match(&tbl->arr[new_id], m)) {
+ return hop;
+ } else {
+ match_bitmap &= ~(1 << hop); /* clear potential match */
+ }
+ }
+
+ return HOP_LEN + 1;
+}
+
+static inline unsigned reduce_dist(rrl_table_t *tbl, unsigned id, unsigned dist, unsigned *free_id)
+{
+ unsigned rd = HOP_LEN - 1;
+ while (rd > 0) {
+ unsigned vacate_id = (tbl->size + *free_id - rd) % tbl->size; /* bucket to be vacated */
+ if (tbl->arr[vacate_id].hop != 0) {
+ unsigned hop = __builtin_ctz(tbl->arr[vacate_id].hop); /* offset of first valid bucket */
+ if (hop < rd) { /* only offsets in <vacate_id, free_id> are interesting */
+ unsigned new_id = (vacate_id + hop) % tbl->size; /* this item will be displaced to [free_id] */
+ unsigned keep_hop = tbl->arr[*free_id].hop; /* unpredictable padding */
+ memcpy(tbl->arr + *free_id, tbl->arr + new_id, sizeof(rrl_item_t));
+ tbl->arr[*free_id].hop = keep_hop;
+ tbl->arr[new_id].cls = CLS_NULL;
+ tbl->arr[vacate_id].hop &= ~(1 << hop);
+ tbl->arr[vacate_id].hop |= 1 << rd;
+ *free_id = new_id;
+ return dist - (rd - hop);
+ }
+ }
+ --rd;
+ }
+
+ assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */
+ *free_id = id;
+ dist = 0; /* force vacate initial element */
+ return dist;
+}
+
+static void subnet_tostr(char *dst, size_t maxlen, const struct sockaddr_storage *ss)
+{
+ const void *addr;
+ const char *suffix;
+
+ if (ss->ss_family == AF_INET6) {
+ addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
+ suffix = "/56";
+ } else {
+ addr = &((struct sockaddr_in *)ss)->sin_addr;
+ suffix = "/24";
+ }
+
+ if (knot_inet_ntop(ss->ss_family, addr, dst, maxlen) != NULL) {
+ strlcat(dst, suffix, maxlen);
+ } else {
+ dst[0] = '\0';
+ }
+}
+
+static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss,
+ uint16_t flags, uint8_t cls, const knot_dname_t *qname)
+{
+ if (mod == NULL || ss == NULL) {
+ return;
+ }
+
+ char addr_str[SOCKADDR_STRLEN];
+ subnet_tostr(addr_str, sizeof(addr_str), ss);
+
+ const char *what = "leaves";
+ if (flags & RRL_BF_ELIMIT) {
+ what = "enters";
+ }
+
+ knot_dname_txt_storage_t buf;
+ char *qname_str = knot_dname_to_str(buf, qname, sizeof(buf));
+ if (qname_str == NULL) {
+ qname_str = "?";
+ }
+
+ knotd_mod_log(mod, LOG_NOTICE, "address/subnet %s, class %s, qname %s, %s limiting",
+ addr_str, rrl_clsstr(cls), qname_str, what);
+}
+
+static void rrl_lock(rrl_table_t *tbl, int lk_id)
+{
+ assert(lk_id > -1);
+ pthread_mutex_lock(tbl->lk + lk_id);
+}
+
+static void rrl_unlock(rrl_table_t *tbl, int lk_id)
+{
+ assert(lk_id > -1);
+ pthread_mutex_unlock(tbl->lk + lk_id);
+}
+
+static int rrl_setlocks(rrl_table_t *tbl, uint32_t granularity)
+{
+ assert(!tbl->lk); /* Cannot change while locks are used. */
+ assert(granularity <= tbl->size / 10); /* Due to int. division err. */
+
+ if (pthread_mutex_init(&tbl->ll, NULL) < 0) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Alloc new locks. */
+ tbl->lk = malloc(granularity * sizeof(pthread_mutex_t));
+ if (!tbl->lk) {
+ return KNOT_ENOMEM;
+ }
+ memset(tbl->lk, 0, granularity * sizeof(pthread_mutex_t));
+
+ /* Initialize. */
+ for (size_t i = 0; i < granularity; ++i) {
+ if (pthread_mutex_init(tbl->lk + i, NULL) < 0) {
+ break;
+ }
+ ++tbl->lk_count;
+ }
+
+ /* Incomplete initialization */
+ if (tbl->lk_count != granularity) {
+ for (size_t i = 0; i < tbl->lk_count; ++i) {
+ pthread_mutex_destroy(tbl->lk + i);
+ }
+ free(tbl->lk);
+ tbl->lk_count = 0;
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+rrl_table_t *rrl_create(size_t size, uint32_t rate)
+{
+ if (size == 0) {
+ return NULL;
+ }
+
+ const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t);
+ rrl_table_t *tbl = calloc(1, tbl_len);
+ if (!tbl) {
+ return NULL;
+ }
+ tbl->size = size;
+ tbl->rate = rate;
+
+ if (dnssec_random_buffer((uint8_t *)&tbl->key, sizeof(tbl->key)) != DNSSEC_EOK) {
+ free(tbl);
+ return NULL;
+ }
+
+ if (rrl_setlocks(tbl, RRL_LOCK_GRANULARITY) != KNOT_EOK) {
+ free(tbl);
+ return NULL;
+ }
+
+ return tbl;
+}
+
+static knot_dname_t *buf_qname(uint8_t *buf)
+{
+ return buf + sizeof(uint8_t) + sizeof(uint64_t);
+}
+
+/*! \brief Get bucket for current combination of parameters. */
+static rrl_item_t *rrl_hash(rrl_table_t *tbl, const struct sockaddr_storage *remote,
+ rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp,
+ int *lock, uint8_t *buf, size_t buf_len)
+{
+ int len = rrl_classify(buf, buf_len, remote, req, zone);
+ if (len < 0) {
+ return NULL;
+ }
+
+ uint32_t id = SipHash24(&tbl->key, buf, len) % tbl->size;
+
+ /* Lock for lookup. */
+ pthread_mutex_lock(&tbl->ll);
+
+ /* Find an exact match in <id, id + HOP_LEN). */
+ knot_dname_t *qname = buf_qname(buf);
+ uint64_t netblk;
+ memcpy(&netblk, buf + sizeof(uint8_t), sizeof(netblk));
+ rrl_item_t match = {
+ .hop = 0,
+ .netblk = netblk,
+ .ntok = tbl->rate * RRL_CAPACITY,
+ .cls = buf[0],
+ .flags = RRL_BF_NULL,
+ .qname = SipHash24(&tbl->key, qname, knot_dname_size(qname)),
+ .time = stamp
+ };
+
+ unsigned dist = find_match(tbl, id, &match);
+ if (dist > HOP_LEN) { /* not an exact match, find free element [f] */
+ dist = find_free(tbl, id, stamp);
+ }
+
+ /* Reduce distance to fit <id, id + HOP_LEN) */
+ unsigned free_id = (id + dist) % tbl->size;
+ while (dist >= HOP_LEN) {
+ dist = reduce_dist(tbl, id, dist, &free_id);
+ }
+
+ /* Assign granular lock and unlock lookup. */
+ *lock = free_id % tbl->lk_count;
+ rrl_lock(tbl, *lock);
+ pthread_mutex_unlock(&tbl->ll);
+
+ /* found free bucket which is in <id, id + HOP_LEN) */
+ tbl->arr[id].hop |= (1 << dist);
+ rrl_item_t *bucket = &tbl->arr[free_id];
+ assert(free_id == (id + dist) % tbl->size);
+
+ /* Inspect bucket state. */
+ unsigned hop = bucket->hop;
+ if (bucket->cls == CLS_NULL) {
+ memcpy(bucket, &match, sizeof(rrl_item_t));
+ bucket->hop = hop;
+ }
+ /* Check for collisions. */
+ if (!bucket_match(bucket, &match)) {
+ if (!(bucket->flags & RRL_BF_SSTART)) {
+ memcpy(bucket, &match, sizeof(rrl_item_t));
+ bucket->hop = hop;
+ bucket->ntok = tbl->rate + tbl->rate / RRL_SSTART;
+ bucket->flags |= RRL_BF_SSTART;
+ }
+ }
+
+ return bucket;
+}
+
+int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
+ rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod)
+{
+ if (!rrl || !req || !remote) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t buf[RRL_CLSBLK_MAXLEN];
+
+ /* Calculate hash and fetch */
+ int ret = KNOT_EOK;
+ int lock = -1;
+ uint32_t now = time_now().tv_sec;
+ rrl_item_t *bucket = rrl_hash(rrl, remote, req, zone, now, &lock, buf, sizeof(buf));
+ if (!bucket) {
+ if (lock > -1) {
+ rrl_unlock(rrl, lock);
+ }
+ return KNOT_ERROR;
+ }
+
+ /* Calculate rate for dT */
+ uint32_t dt = now - bucket->time;
+ if (dt > RRL_CAPACITY) {
+ dt = RRL_CAPACITY;
+ }
+ /* Visit bucket. */
+ bucket->time = now;
+ if (dt > 0) { /* Window moved. */
+
+ /* Check state change. */
+ if ((bucket->ntok > 0 || dt > 1) && (bucket->flags & RRL_BF_ELIMIT)) {
+ bucket->flags &= ~RRL_BF_ELIMIT;
+ rrl_log_state(mod, remote, bucket->flags, bucket->cls,
+ knot_pkt_qname(req->query));
+ }
+
+ /* Add new tokens. */
+ uint32_t dn = rrl->rate * dt;
+ if (bucket->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */
+ bucket->flags &= ~RRL_BF_SSTART;
+ }
+ bucket->ntok += dn;
+ if (bucket->ntok > RRL_CAPACITY * rrl->rate) {
+ bucket->ntok = RRL_CAPACITY * rrl->rate;
+ }
+ }
+
+ /* Last item taken. */
+ if (bucket->ntok == 1 && !(bucket->flags & RRL_BF_ELIMIT)) {
+ bucket->flags |= RRL_BF_ELIMIT;
+ rrl_log_state(mod, remote, bucket->flags, bucket->cls,
+ knot_pkt_qname(req->query));
+ }
+
+ /* Decay current bucket. */
+ if (bucket->ntok > 0) {
+ --bucket->ntok;
+ } else if (bucket->ntok == 0) {
+ ret = KNOT_ELIMIT;
+ }
+
+ if (lock > -1) {
+ rrl_unlock(rrl, lock);
+ }
+ return ret;
+}
+
+bool rrl_slip_roll(int n_slip)
+{
+ switch (n_slip) {
+ case 0:
+ return false;
+ case 1:
+ return true;
+ default:
+ return (dnssec_random_uint16_t() % n_slip == 0);
+ }
+}
+
+void rrl_destroy(rrl_table_t *rrl)
+{
+ if (rrl) {
+ if (rrl->lk_count > 0) {
+ pthread_mutex_destroy(&rrl->ll);
+ }
+ for (size_t i = 0; i < rrl->lk_count; ++i) {
+ pthread_mutex_destroy(rrl->lk + i);
+ }
+ free(rrl->lk);
+ }
+
+ free(rrl);
+}
diff --git a/src/knot/modules/rrl/functions.h b/src/knot/modules/rrl/functions.h
new file mode 100644
index 0000000..0f09234
--- /dev/null
+++ b/src/knot/modules/rrl/functions.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <pthread.h>
+#include <sys/socket.h>
+
+#include "libknot/libknot.h"
+#include "knot/include/module.h"
+#include "contrib/openbsd/siphash.h"
+
+/*!
+ * \brief RRL hash bucket.
+ */
+typedef struct {
+ unsigned hop; /* Hop bitmap. */
+ uint64_t netblk; /* Prefix associated. */
+ uint16_t ntok; /* Tokens available. */
+ uint8_t cls; /* Bucket class. */
+ uint8_t flags; /* Flags. */
+ uint32_t qname; /* imputed(QNAME) hash. */
+ uint32_t time; /* Timestamp. */
+} rrl_item_t;
+
+/*!
+ * \brief RRL hash bucket table.
+ *
+ * Table is fixed size, so collisions may occur and are dealt with
+ * in a way, that hashbucket rate is reset and enters slow-start for 1 dt.
+ * When a bucket is in a slow-start mode, it cannot reset again for the time
+ * period.
+ *
+ * To avoid lock contention, N locks are created and distributed amongst buckets.
+ * As of now lock K for bucket N is calculated as K = N % (num_buckets).
+ */
+
+typedef struct {
+ SIPHASH_KEY key; /* Siphash key. */
+ uint32_t rate; /* Configured RRL limit. */
+ pthread_mutex_t ll;
+ pthread_mutex_t *lk; /* Table locks. */
+ unsigned lk_count; /* Table lock count (granularity). */
+ size_t size; /* Number of buckets. */
+ rrl_item_t arr[]; /* Buckets. */
+} rrl_table_t;
+
+/*! \brief RRL request flags. */
+typedef enum {
+ RRL_REQ_NOFLAG = 0 << 0, /*!< No flags. */
+ RRL_REQ_WILDCARD = 1 << 1 /*!< Query to wildcard name. */
+} rrl_req_flag_t;
+
+/*!
+ * \brief RRL request descriptor.
+ */
+typedef struct {
+ const uint8_t *wire;
+ uint16_t len;
+ rrl_req_flag_t flags;
+ knot_pkt_t *query;
+} rrl_req_t;
+
+/*!
+ * \brief Create a RRL table.
+ * \param size Fixed hashtable size (reasonable large prime is recommended).
+ * \param rate Rate (in pkts/sec).
+ * \return created table or NULL.
+ */
+rrl_table_t *rrl_create(size_t size, uint32_t rate);
+
+/*!
+ * \brief Query the RRL table for accept or deny, when the rate limit is reached.
+ *
+ * \param rrl RRL table.
+ * \param remote Source address.
+ * \param req RRL request (containing resp., flags and question).
+ * \param zone Zone name related to the response (or NULL).
+ * \param mod Query module (needed for logging).
+ * \retval KNOT_EOK if passed.
+ * \retval KNOT_ELIMIT when the limit is reached.
+ */
+int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *remote,
+ rrl_req_t *req, const knot_dname_t *zone, knotd_mod_t *mod);
+
+/*!
+ * \brief Roll a dice whether answer slips or not.
+ * \param n_slip Number represents every Nth answer that is slipped.
+ * \return true or false
+ */
+bool rrl_slip_roll(int n_slip);
+
+/*!
+ * \brief Destroy RRL table.
+ * \param rrl RRL table.
+ */
+void rrl_destroy(rrl_table_t *rrl);
diff --git a/src/knot/modules/rrl/rrl.c b/src/knot/modules/rrl/rrl.c
new file mode 100644
index 0000000..64f6cbf
--- /dev/null
+++ b/src/knot/modules/rrl/rrl.c
@@ -0,0 +1,208 @@
+/* Copyright (C) 2022 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 "knot/include/module.h"
+#include "knot/nameserver/process_query.h" // Dependency on qdata->extra!
+#include "knot/modules/rrl/functions.h"
+
+#define MOD_RATE_LIMIT "\x0A""rate-limit"
+#define MOD_SLIP "\x04""slip"
+#define MOD_TBL_SIZE "\x0A""table-size"
+#define MOD_WHITELIST "\x09""whitelist"
+
+const yp_item_t rrl_conf[] = {
+ { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 1, INT32_MAX } },
+ { MOD_SLIP, YP_TINT, YP_VINT = { 0, 100, 1 } },
+ { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 393241 } },
+ { MOD_WHITELIST, YP_TNET, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+int rrl_conf_check(knotd_conf_check_args_t *args)
+{
+ knotd_conf_t limit = knotd_conf_check_item(args, MOD_RATE_LIMIT);
+ if (limit.count == 0) {
+ args->err_str = "no rate limit specified";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+typedef struct {
+ rrl_table_t *rrl;
+ int slip;
+ knotd_conf_t whitelist;
+} rrl_ctx_t;
+
+static const knot_dname_t *name_from_rrsig(const knot_rrset_t *rr)
+{
+ if (rr == NULL) {
+ return NULL;
+ }
+ if (rr->type != KNOT_RRTYPE_RRSIG) {
+ return NULL;
+ }
+
+ // This is a signature.
+ return knot_rrsig_signer_name(rr->rrs.rdata);
+}
+
+static const knot_dname_t *name_from_authrr(const knot_rrset_t *rr)
+{
+ if (rr == NULL) {
+ return NULL;
+ }
+ if (rr->type != KNOT_RRTYPE_NS && rr->type != KNOT_RRTYPE_SOA) {
+ return NULL;
+ }
+
+ // This is a valid authority RR.
+ return rr->owner;
+}
+
+static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ rrl_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ // Rate limit is applied to pure UDP only.
+ if (qdata->params->proto != KNOTD_QUERY_PROTO_UDP) {
+ return state;
+ }
+
+ // Rate limit is not applied to responses with a valid cookie.
+ if (qdata->params->flags & KNOTD_QUERY_FLAG_COOKIE) {
+ return state;
+ }
+
+ // Exempt clients.
+ if (knotd_conf_addr_range_match(&ctx->whitelist, knotd_qdata_remote_addr(qdata))) {
+ return state;
+ }
+
+ rrl_req_t req = {
+ .wire = pkt->wire,
+ .query = qdata->query
+ };
+
+ if (!EMPTY_LIST(qdata->extra->wildcards)) {
+ req.flags = RRL_REQ_WILDCARD;
+ }
+
+ // Take the zone name if known.
+ const knot_dname_t *zone_name = knotd_qdata_zone_name(qdata);
+
+ // Take the signer name as zone name if there is an RRSIG.
+ if (zone_name == NULL) {
+ const knot_pktsection_t *ans = knot_pkt_section(pkt, KNOT_ANSWER);
+ for (int i = 0; i < ans->count; i++) {
+ zone_name = name_from_rrsig(knot_pkt_rr(ans, i));
+ if (zone_name != NULL) {
+ break;
+ }
+ }
+ }
+
+ // Take the NS or SOA owner name if there is no RRSIG.
+ if (zone_name == NULL) {
+ const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY);
+ for (int i = 0; i < auth->count; i++) {
+ zone_name = name_from_authrr(knot_pkt_rr(auth, i));
+ if (zone_name != NULL) {
+ break;
+ }
+ }
+ }
+
+ if (rrl_query(ctx->rrl, knotd_qdata_remote_addr(qdata), &req, zone_name, mod) == KNOT_EOK) {
+ // Rate limiting not applied.
+ return state;
+ }
+
+ if (rrl_slip_roll(ctx->slip)) {
+ // Slip the answer.
+ knotd_mod_stats_incr(mod, qdata->params->thread_id, 0, 0, 1);
+ qdata->err_truncated = true;
+ return KNOTD_STATE_FAIL;
+ } else {
+ // Drop the answer.
+ knotd_mod_stats_incr(mod, qdata->params->thread_id, 1, 0, 1);
+ return KNOTD_STATE_NOOP;
+ }
+}
+
+static void ctx_free(rrl_ctx_t *ctx)
+{
+ assert(ctx);
+
+ rrl_destroy(ctx->rrl);
+ free(ctx);
+}
+
+int rrl_load(knotd_mod_t *mod)
+{
+ // Create RRL context.
+ rrl_ctx_t *ctx = calloc(1, sizeof(rrl_ctx_t));
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Create table.
+ uint32_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT).single.integer;
+ size_t size = knotd_conf_mod(mod, MOD_TBL_SIZE).single.integer;
+ ctx->rrl = rrl_create(size, rate);
+ if (ctx->rrl == NULL) {
+ ctx_free(ctx);
+ return KNOT_ENOMEM;
+ }
+
+ // Get slip.
+ ctx->slip = knotd_conf_mod(mod, MOD_SLIP).single.integer;
+
+ // Get whitelist.
+ ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST);
+
+ // Set up statistics counters.
+ int ret = knotd_mod_stats_add(mod, "slipped", 1, NULL);
+ if (ret != KNOT_EOK) {
+ ctx_free(ctx);
+ return ret;
+ }
+
+ ret = knotd_mod_stats_add(mod, "dropped", 1, NULL);
+ if (ret != KNOT_EOK) {
+ ctx_free(ctx);
+ return ret;
+ }
+
+ knotd_mod_ctx_set(mod, ctx);
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_END, ratelimit_apply);
+}
+
+void rrl_unload(knotd_mod_t *mod)
+{
+ rrl_ctx_t *ctx = knotd_mod_ctx(mod);
+
+ knotd_conf_free(&ctx->whitelist);
+ ctx_free(ctx);
+}
+
+KNOTD_MOD_API(rrl, KNOTD_MOD_FLAG_SCOPE_ANY,
+ rrl_load, rrl_unload, rrl_conf, rrl_conf_check);
diff --git a/src/knot/modules/rrl/rrl.rst b/src/knot/modules/rrl/rrl.rst
new file mode 100644
index 0000000..3fc7892
--- /dev/null
+++ b/src/knot/modules/rrl/rrl.rst
@@ -0,0 +1,133 @@
+.. _mod-rrl:
+
+``rrl`` — Response rate limiting
+================================
+
+Response rate limiting (RRL) is a method to combat DNS reflection amplification
+attacks. These attacks rely on the fact that source address of a UDP query
+can be forged, and without a worldwide deployment of `BCP38
+<https://tools.ietf.org/html/bcp38>`_, such a forgery cannot be prevented.
+An attacker can use a DNS server (or multiple servers) as an amplification
+source and can flood a victim with a large number of unsolicited DNS responses.
+The RRL lowers the amplification factor of these attacks by sending some of
+the responses as truncated or by dropping them altogether.
+
+.. NOTE::
+ The module introduces two statistics counters. The number of slipped and
+ dropped responses.
+
+.. NOTE::
+ If the :ref:`Cookies<mod-cookies>` module is active, RRL is not applied
+ for responses with a valid DNS cookie.
+
+Example
+-------
+
+You can enable RRL by setting the module globally or per zone.
+
+::
+
+ mod-rrl:
+ - id: default
+ rate-limit: 200 # Allow 200 resp/s for each flow
+ slip: 2 # Approximately every other response slips
+
+ template:
+ - id: default
+ global-module: mod-rrl/default # Enable RRL globally
+
+Module reference
+----------------
+
+::
+
+ mod-rrl:
+ - id: STR
+ rate-limit: INT
+ slip: INT
+ table-size: INT
+ whitelist: ADDR[/INT] | ADDR-ADDR ...
+
+.. _mod-rrl_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-rrl_rate-limit:
+
+rate-limit
+..........
+
+Rate limiting is based on the token bucket scheme. A rate basically
+represents a number of tokens available each second. Each response is
+processed and classified (based on several discriminators, e.g.
+source netblock, query type, zone name, rcode, etc.). Classified responses are
+then hashed and assigned to a bucket containing number of available
+tokens, timestamp and metadata. When available tokens are exhausted,
+response is dropped or sent as truncated (see :ref:`mod-rrl_slip`).
+Number of available tokens is recalculated each second.
+
+*Required*
+
+.. _mod-rrl_table-size:
+
+table-size
+..........
+
+Size of the hash table in a number of buckets. The larger the hash table, the lesser
+the probability of a hash collision, but at the expense of additional memory costs.
+Each bucket is estimated roughly to 32 bytes. The size should be selected as
+a reasonably large prime due to better hash function distribution properties.
+Hash table is internally chained and works well up to a fill rate of 90 %, general
+rule of thumb is to select a prime near 1.2 * maximum_qps.
+
+*Default:* ``393241``
+
+.. _mod-rrl_slip:
+
+slip
+....
+
+As attacks using DNS/UDP are usually based on a forged source address,
+an attacker could deny services to the victim's netblock if all
+responses would be completely blocked. The idea behind SLIP mechanism
+is to send each N\ :sup:`th` response as truncated, thus allowing client to
+reconnect via TCP for at least some degree of service. It is worth
+noting, that some responses can't be truncated (e.g. SERVFAIL).
+
+- Setting the value to **0** will cause that all rate-limited responses will
+ be dropped. The outbound bandwidth and packet rate will be strictly capped
+ by the :ref:`mod-rrl_rate-limit` option. All legitimate requestors affected
+ by the limit will face denial of service and will observe excessive timeouts.
+ Therefore this setting is not recommended.
+
+- Setting the value to **1** will cause that all rate-limited responses will
+ be sent as truncated. The amplification factor of the attack will be reduced,
+ but the outbound data bandwidth won't be lower than the incoming bandwidth.
+ Also the outbound packet rate will be the same as without RRL.
+
+- Setting the value to **2** will cause that approximately half of the rate-limited responses
+ will be dropped, the other half will be sent as truncated. With this
+ configuration, both outbound bandwidth and packet rate will be lower than the
+ inbound. On the other hand, the dropped responses enlarge the time window
+ for possible cache poisoning attack on the resolver.
+
+- Setting the value to anything **larger than 2** will keep on decreasing
+ the outgoing rate-limited bandwidth, packet rate, and chances to notify
+ legitimate requestors to reconnect using TCP. These attributes are inversely
+ proportional to the configured value. Setting the value high is not advisable.
+
+*Default:* ``1``
+
+.. _mod-rrl_whitelist:
+
+whitelist
+.........
+
+A list of IP addresses, network subnets, or network ranges to exempt from
+rate limiting. Empty list means that no incoming connection will be
+white-listed.
+
+*Default:* not set
diff --git a/src/knot/modules/static_modules.h.in b/src/knot/modules/static_modules.h.in
new file mode 100644
index 0000000..1e1713e
--- /dev/null
+++ b/src/knot/modules/static_modules.h.in
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "knot/include/module.h"
+
+// Forward declarations of static modules (generated by configure).
+@STATIC_MODULES_DECLARS@
+
+// STATIC_MODULES initializer (generated by configure).
+#define STATIC_MODULES_INIT @STATIC_MODULES_INIT@
diff --git a/src/knot/modules/stats/Makefile.inc b/src/knot/modules/stats/Makefile.inc
new file mode 100644
index 0000000..8952d49
--- /dev/null
+++ b/src/knot/modules/stats/Makefile.inc
@@ -0,0 +1,13 @@
+knot_modules_stats_la_SOURCES = knot/modules/stats/stats.c
+EXTRA_DIST += knot/modules/stats/stats.rst
+
+if STATIC_MODULE_stats
+libknotd_la_SOURCES += $(knot_modules_stats_la_SOURCES)
+endif
+
+if SHARED_MODULE_stats
+knot_modules_stats_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_stats_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_stats_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/stats.la
+endif
diff --git a/src/knot/modules/stats/stats.c b/src/knot/modules/stats/stats.c
new file mode 100644
index 0000000..26262ac
--- /dev/null
+++ b/src/knot/modules/stats/stats.c
@@ -0,0 +1,676 @@
+/* Copyright (C) 2022 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 "contrib/macros.h"
+#include "contrib/wire_ctx.h"
+#include "knot/include/module.h"
+#include "knot/nameserver/xfr.h" // Dependency on qdata->extra!
+
+#define MOD_PROTOCOL "\x10""request-protocol"
+#define MOD_OPERATION "\x10""server-operation"
+#define MOD_REQ_BYTES "\x0D""request-bytes"
+#define MOD_RESP_BYTES "\x0E""response-bytes"
+#define MOD_EDNS "\x0D""edns-presence"
+#define MOD_FLAG "\x0D""flag-presence"
+#define MOD_RCODE "\x0D""response-code"
+#define MOD_REQ_EOPT "\x13""request-edns-option"
+#define MOD_RESP_EOPT "\x14""response-edns-option"
+#define MOD_NODATA "\x0C""reply-nodata"
+#define MOD_QTYPE "\x0A""query-type"
+#define MOD_QSIZE "\x0A""query-size"
+#define MOD_RSIZE "\x0A""reply-size"
+
+#define OTHER "other"
+
+const yp_item_t stats_conf[] = {
+ { MOD_PROTOCOL, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_OPERATION, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_REQ_BYTES, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_RESP_BYTES, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_EDNS, YP_TBOOL, YP_VNONE },
+ { MOD_FLAG, YP_TBOOL, YP_VNONE },
+ { MOD_RCODE, YP_TBOOL, YP_VBOOL = { true } },
+ { MOD_REQ_EOPT, YP_TBOOL, YP_VNONE },
+ { MOD_RESP_EOPT, YP_TBOOL, YP_VNONE },
+ { MOD_NODATA, YP_TBOOL, YP_VNONE },
+ { MOD_QTYPE, YP_TBOOL, YP_VNONE },
+ { MOD_QSIZE, YP_TBOOL, YP_VNONE },
+ { MOD_RSIZE, YP_TBOOL, YP_VNONE },
+ { NULL }
+};
+
+enum {
+ CTR_PROTOCOL,
+ CTR_OPERATION,
+ CTR_REQ_BYTES,
+ CTR_RESP_BYTES,
+ CTR_EDNS,
+ CTR_FLAG,
+ CTR_RCODE,
+ CTR_REQ_EOPT,
+ CTR_RESP_EOPT,
+ CTR_NODATA,
+ CTR_QTYPE,
+ CTR_QSIZE,
+ CTR_RSIZE,
+};
+
+typedef struct {
+ bool protocol;
+ bool operation;
+ bool req_bytes;
+ bool resp_bytes;
+ bool edns;
+ bool flag;
+ bool rcode;
+ bool req_eopt;
+ bool resp_eopt;
+ bool nodata;
+ bool qtype;
+ bool qsize;
+ bool rsize;
+} stats_t;
+
+typedef struct {
+ yp_name_t *conf_name;
+ size_t conf_offset;
+ uint32_t count;
+ knotd_mod_idx_to_str_f fcn;
+} ctr_desc_t;
+
+enum {
+ OPERATION_QUERY = 0,
+ OPERATION_UPDATE,
+ OPERATION_NOTIFY,
+ OPERATION_AXFR,
+ OPERATION_IXFR,
+ OPERATION_INVALID,
+ OPERATION__COUNT
+};
+
+static char *operation_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case OPERATION_QUERY: return strdup("query");
+ case OPERATION_UPDATE: return strdup("update");
+ case OPERATION_NOTIFY: return strdup("notify");
+ case OPERATION_AXFR: return strdup("axfr");
+ case OPERATION_IXFR: return strdup("ixfr");
+ case OPERATION_INVALID: return strdup("invalid");
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ PROTOCOL_UDP4 = 0,
+ PROTOCOL_TCP4,
+ PROTOCOL_QUIC4,
+ PROTOCOL_UDP6,
+ PROTOCOL_TCP6,
+ PROTOCOL_QUIC6,
+ PROTOCOL_UDP4_XDP,
+ PROTOCOL_TCP4_XDP,
+ PROTOCOL_QUIC4_XDP,
+ PROTOCOL_UDP6_XDP,
+ PROTOCOL_TCP6_XDP,
+ PROTOCOL_QUIC6_XDP,
+ PROTOCOL__COUNT
+};
+
+static char *protocol_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case PROTOCOL_UDP4: return strdup("udp4");
+ case PROTOCOL_TCP4: return strdup("tcp4");
+ case PROTOCOL_QUIC4: return strdup("quic4");
+ case PROTOCOL_UDP6: return strdup("udp6");
+ case PROTOCOL_TCP6: return strdup("tcp6");
+ case PROTOCOL_QUIC6: return strdup("quic6");
+ case PROTOCOL_UDP4_XDP: return strdup("udp4-xdp");
+ case PROTOCOL_TCP4_XDP: return strdup("tcp4-xdp");
+ case PROTOCOL_QUIC4_XDP: return strdup("quic4-xdp");
+ case PROTOCOL_UDP6_XDP: return strdup("udp6-xdp");
+ case PROTOCOL_TCP6_XDP: return strdup("tcp6-xdp");
+ case PROTOCOL_QUIC6_XDP: return strdup("quic6-xdp");
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ REQ_BYTES_QUERY = 0,
+ REQ_BYTES_UPDATE,
+ REQ_BYTES_OTHER,
+ REQ_BYTES__COUNT
+};
+
+static char *req_bytes_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case REQ_BYTES_QUERY: return strdup("query");
+ case REQ_BYTES_UPDATE: return strdup("update");
+ case REQ_BYTES_OTHER: return strdup(OTHER);
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ RESP_BYTES_REPLY = 0,
+ RESP_BYTES_TRANSFER,
+ RESP_BYTES_OTHER,
+ RESP_BYTES__COUNT
+};
+
+static char *resp_bytes_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case RESP_BYTES_REPLY: return strdup("reply");
+ case RESP_BYTES_TRANSFER: return strdup("transfer");
+ case RESP_BYTES_OTHER: return strdup(OTHER);
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ EDNS_REQ = 0,
+ EDNS_RESP,
+ EDNS__COUNT
+};
+
+static char *edns_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case EDNS_REQ: return strdup("request");
+ case EDNS_RESP: return strdup("response");
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ FLAG_DO = 0,
+ FLAG_TC,
+ FLAG__COUNT
+};
+
+static char *flag_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case FLAG_TC: return strdup("TC");
+ case FLAG_DO: return strdup("DO");
+ default: assert(0); return NULL;
+ }
+}
+
+enum {
+ NODATA_A = 0,
+ NODATA_AAAA,
+ NODATA_OTHER,
+ NODATA__COUNT
+};
+
+static char *nodata_to_str(uint32_t idx, uint32_t count)
+{
+ switch (idx) {
+ case NODATA_A: return strdup("A");
+ case NODATA_AAAA: return strdup("AAAA");
+ case NODATA_OTHER: return strdup(OTHER);
+ default: assert(0); return NULL;
+ }
+}
+
+#define RCODE_BADSIG 15 // Unassigned code internally used for BADSIG.
+#define RCODE_OTHER (KNOT_RCODE_BADCOOKIE + 1) // Other RCODES.
+
+static char *rcode_to_str(uint32_t idx, uint32_t count)
+{
+ const knot_lookup_t *rcode = NULL;
+
+ switch (idx) {
+ case RCODE_BADSIG:
+ rcode = knot_lookup_by_id(knot_tsig_rcode_names, KNOT_RCODE_BADSIG);
+ break;
+ case RCODE_OTHER:
+ return strdup(OTHER);
+ default:
+ rcode = knot_lookup_by_id(knot_rcode_names, idx);
+ break;
+ }
+
+ if (rcode != NULL) {
+ return strdup(rcode->name);
+ } else {
+ return NULL;
+ }
+}
+
+#define EOPT_OTHER (KNOT_EDNS_MAX_OPTION_CODE + 1)
+#define req_eopt_to_str eopt_to_str
+#define resp_eopt_to_str eopt_to_str
+
+static char *eopt_to_str(uint32_t idx, uint32_t count)
+{
+ if (idx >= EOPT_OTHER) {
+ return strdup(OTHER);
+ }
+
+ char str[32];
+ if (knot_opt_code_to_string(idx, str, sizeof(str)) < 0) {
+ return NULL;
+ } else {
+ return strdup(str);
+ }
+}
+
+enum {
+ QTYPE_OTHER = 0,
+ QTYPE_MIN1 = 1,
+ QTYPE_MAX1 = 65,
+ QTYPE_MIN2 = 99,
+ QTYPE_MAX2 = 110,
+ QTYPE_MIN3 = 255,
+ QTYPE_MAX3 = 260,
+ QTYPE_SHIFT2 = QTYPE_MIN2 - QTYPE_MAX1 - 1,
+ QTYPE_SHIFT3 = QTYPE_SHIFT2 + QTYPE_MIN3 - QTYPE_MAX2 - 1,
+ QTYPE__COUNT = QTYPE_MAX3 - QTYPE_SHIFT3 + 1
+};
+
+static char *qtype_to_str(uint32_t idx, uint32_t count)
+{
+ if (idx == QTYPE_OTHER) {
+ return strdup(OTHER);
+ }
+
+ uint16_t qtype;
+
+ if (idx <= QTYPE_MAX1) {
+ qtype = idx;
+ assert(qtype >= QTYPE_MIN1 && qtype <= QTYPE_MAX1);
+ } else if (idx <= QTYPE_MAX2 - QTYPE_SHIFT2) {
+ qtype = idx + QTYPE_SHIFT2;
+ assert(qtype >= QTYPE_MIN2 && qtype <= QTYPE_MAX2);
+ } else {
+ qtype = idx + QTYPE_SHIFT3;
+ assert(qtype >= QTYPE_MIN3 && qtype <= QTYPE_MAX3);
+ }
+
+ char str[32];
+ if (knot_rrtype_to_string(qtype, str, sizeof(str)) < 0) {
+ return NULL;
+ } else {
+ return strdup(str);
+ }
+}
+
+#define BUCKET_SIZE 16
+#define QSIZE_MAX_IDX (288 / BUCKET_SIZE)
+#define RSIZE_MAX_IDX (4096 / BUCKET_SIZE)
+
+static char *size_to_str(uint32_t idx, uint32_t count)
+{
+ char str[16];
+
+ int ret;
+ if (idx < count - 1) {
+ ret = snprintf(str, sizeof(str), "%u-%u", idx * BUCKET_SIZE,
+ (idx + 1) * BUCKET_SIZE - 1);
+ } else {
+ ret = snprintf(str, sizeof(str), "%u-65535", idx * BUCKET_SIZE);
+ }
+
+ if (ret <= 0 || (size_t)ret >= sizeof(str)) {
+ return NULL;
+ } else {
+ return strdup(str);
+ }
+}
+
+static char *qsize_to_str(uint32_t idx, uint32_t count)
+{
+ return size_to_str(idx, count);
+}
+
+static char *rsize_to_str(uint32_t idx, uint32_t count)
+{
+ return size_to_str(idx, count);
+}
+
+static const ctr_desc_t ctr_descs[] = {
+ #define item(macro, name, count) \
+ [CTR_##macro] = { MOD_##macro, offsetof(stats_t, name), (count), name##_to_str }
+ item(PROTOCOL, protocol, PROTOCOL__COUNT),
+ item(OPERATION, operation, OPERATION__COUNT),
+ item(REQ_BYTES, req_bytes, REQ_BYTES__COUNT),
+ item(RESP_BYTES, resp_bytes, RESP_BYTES__COUNT),
+ item(EDNS, edns, EDNS__COUNT),
+ item(FLAG, flag, FLAG__COUNT),
+ item(RCODE, rcode, RCODE_OTHER + 1),
+ item(REQ_EOPT, req_eopt, EOPT_OTHER + 1),
+ item(RESP_EOPT, resp_eopt, EOPT_OTHER + 1),
+ item(NODATA, nodata, NODATA__COUNT),
+ item(QTYPE, qtype, QTYPE__COUNT),
+ item(QSIZE, qsize, QSIZE_MAX_IDX + 1),
+ item(RSIZE, rsize, RSIZE_MAX_IDX + 1),
+ { NULL }
+};
+
+static void incr_edns_option(knotd_mod_t *mod, unsigned thr_id, const knot_pkt_t *pkt, unsigned ctr_name)
+{
+ if (!knot_pkt_has_edns(pkt)) {
+ return;
+ }
+
+ knot_rdata_t *rdata = pkt->opt_rr->rrs.rdata;
+ if (rdata == NULL || rdata->len == 0) {
+ return;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+ while (wire_ctx_available(&wire) > 0) {
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ if (wire.error != KNOT_EOK) {
+ break;
+ }
+ knotd_mod_stats_incr(mod, thr_id, ctr_name, MIN(opt_code, EOPT_OTHER), 1);
+ }
+}
+
+static knotd_state_t update_counters(knotd_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata);
+
+ stats_t *stats = knotd_mod_ctx(mod);
+
+ uint16_t operation;
+ unsigned xfr_packets = 0;
+ unsigned tid = qdata->params->thread_id;
+
+ // Get the server operation.
+ switch (qdata->type) {
+ case KNOTD_QUERY_TYPE_NORMAL:
+ operation = OPERATION_QUERY;
+ break;
+ case KNOTD_QUERY_TYPE_UPDATE:
+ operation = OPERATION_UPDATE;
+ break;
+ case KNOTD_QUERY_TYPE_NOTIFY:
+ operation = OPERATION_NOTIFY;
+ break;
+ case KNOTD_QUERY_TYPE_AXFR:
+ operation = OPERATION_AXFR;
+ if (qdata->extra->ext != NULL) {
+ xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages;
+ }
+ break;
+ case KNOTD_QUERY_TYPE_IXFR:
+ operation = OPERATION_IXFR;
+ if (qdata->extra->ext != NULL) {
+ xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages;
+ }
+ break;
+ default:
+ operation = OPERATION_INVALID;
+ break;
+ }
+
+ // Count request bytes.
+ if (stats->req_bytes) {
+ switch (operation) {
+ case OPERATION_QUERY:
+ knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_QUERY,
+ knot_pkt_size(qdata->query));
+ break;
+ case OPERATION_UPDATE:
+ knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_UPDATE,
+ knot_pkt_size(qdata->query));
+ break;
+ default:
+ if (xfr_packets <= 1) {
+ knotd_mod_stats_incr(mod, tid, CTR_REQ_BYTES, REQ_BYTES_OTHER,
+ knot_pkt_size(qdata->query));
+ }
+ break;
+ }
+ }
+
+ // Count response bytes.
+ if (stats->resp_bytes && state != KNOTD_STATE_NOOP) {
+ switch (operation) {
+ case OPERATION_QUERY:
+ knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_REPLY,
+ knot_pkt_size(pkt));
+ break;
+ case OPERATION_AXFR:
+ case OPERATION_IXFR:
+ knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_TRANSFER,
+ knot_pkt_size(pkt));
+ break;
+ default:
+ knotd_mod_stats_incr(mod, tid, CTR_RESP_BYTES, RESP_BYTES_OTHER,
+ knot_pkt_size(pkt));
+ break;
+ }
+ }
+
+ // Get the extended response code.
+ uint16_t rcode = qdata->rcode;
+ if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
+ rcode = qdata->rcode_tsig;
+ }
+
+ // Count the response code.
+ if (stats->rcode && state != KNOTD_STATE_NOOP) {
+ if (xfr_packets <= 1 || rcode != KNOT_RCODE_NOERROR) {
+ if (xfr_packets > 1) {
+ assert(rcode != KNOT_RCODE_NOERROR);
+ // Ignore the leading XFR message NOERROR.
+ knotd_mod_stats_decr(mod, tid, CTR_RCODE,
+ KNOT_RCODE_NOERROR, 1);
+ }
+
+ if (qdata->rcode_tsig == KNOT_RCODE_BADSIG) {
+ knotd_mod_stats_incr(mod, tid, CTR_RCODE, RCODE_BADSIG, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_RCODE,
+ MIN(rcode, RCODE_OTHER), 1);
+ }
+ }
+ }
+
+ // Return if non-first transfer message.
+ if (xfr_packets > 1) {
+ return state;
+ }
+
+ // Count the server operation.
+ if (stats->operation) {
+ knotd_mod_stats_incr(mod, tid, CTR_OPERATION, operation, 1);
+ }
+
+ // Count the request protocol.
+ if (stats->protocol) {
+ bool xdp = qdata->params->xdp_msg != NULL;
+ if (knotd_qdata_remote_addr(qdata)->ss_family == AF_INET) {
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_UDP4_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_UDP4, 1);
+ }
+ } else if (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_QUIC4_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_QUIC4, 1);
+ }
+ } else {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TCP4_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TCP4, 1);
+ }
+ }
+ } else {
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_UDP6_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_UDP6, 1);
+ }
+ } else if (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_QUIC6_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_QUIC6, 1);
+ }
+ } else {
+ if (xdp) {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TCP6_XDP, 1);
+ } else {
+ knotd_mod_stats_incr(mod, tid, CTR_PROTOCOL,
+ PROTOCOL_TCP6, 1);
+ }
+ }
+ }
+ }
+
+ // Count EDNS occurrences.
+ if (stats->edns) {
+ if (knot_pkt_has_edns(qdata->query)) {
+ knotd_mod_stats_incr(mod, tid, CTR_EDNS, EDNS_REQ, 1);
+ }
+ if (knot_pkt_has_edns(pkt) && state != KNOTD_STATE_NOOP) {
+ knotd_mod_stats_incr(mod, tid, CTR_EDNS, EDNS_RESP, 1);
+ }
+ }
+
+ // Count interesting message header flags.
+ if (stats->flag) {
+ if (state != KNOTD_STATE_NOOP && knot_wire_get_tc(pkt->wire)) {
+ knotd_mod_stats_incr(mod, tid, CTR_FLAG, FLAG_TC, 1);
+ }
+ if (knot_pkt_has_dnssec(pkt)) {
+ knotd_mod_stats_incr(mod, tid, CTR_FLAG, FLAG_DO, 1);
+ }
+ }
+
+ // Count EDNS options.
+ if (stats->req_eopt) {
+ incr_edns_option(mod, tid, qdata->query, CTR_REQ_EOPT);
+ }
+ if (stats->resp_eopt) {
+ incr_edns_option(mod, tid, pkt, CTR_RESP_EOPT);
+ }
+
+ // Return if not query operation.
+ if (operation != OPERATION_QUERY) {
+ return state;
+ }
+
+ // Count NODATA reply (RFC 2308, Section 2.2).
+ if (stats->nodata && rcode == KNOT_RCODE_NOERROR && state != KNOTD_STATE_NOOP &&
+ knot_wire_get_ancount(pkt->wire) == 0 && !knot_wire_get_tc(pkt->wire) &&
+ (knot_wire_get_nscount(pkt->wire) == 0 ||
+ knot_pkt_rr(knot_pkt_section(pkt, KNOT_AUTHORITY), 0)->type == KNOT_RRTYPE_SOA)) {
+ switch (knot_pkt_qtype(qdata->query)) {
+ case KNOT_RRTYPE_A:
+ knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_A, 1);
+ break;
+ case KNOT_RRTYPE_AAAA:
+ knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_AAAA, 1);
+ break;
+ default:
+ knotd_mod_stats_incr(mod, tid, CTR_NODATA, NODATA_OTHER, 1);
+ break;
+ }
+ }
+
+ // Count the query type.
+ if (stats->qtype) {
+ uint16_t qtype = knot_pkt_qtype(qdata->query);
+
+ uint16_t idx;
+ switch (qtype) {
+ case QTYPE_MIN1 ... QTYPE_MAX1: idx = qtype; break;
+ case QTYPE_MIN2 ... QTYPE_MAX2: idx = qtype - QTYPE_SHIFT2; break;
+ case QTYPE_MIN3 ... QTYPE_MAX3: idx = qtype - QTYPE_SHIFT3; break;
+ default: idx = QTYPE_OTHER; break;
+ }
+
+ knotd_mod_stats_incr(mod, tid, CTR_QTYPE, idx, 1);
+ }
+
+ // Count the query size.
+ if (stats->qsize) {
+ uint64_t idx = knot_pkt_size(qdata->query) / BUCKET_SIZE;
+ knotd_mod_stats_incr(mod, tid, CTR_QSIZE, MIN(idx, QSIZE_MAX_IDX), 1);
+ }
+
+ // Count the reply size.
+ if (stats->rsize && state != KNOTD_STATE_NOOP) {
+ uint64_t idx = knot_pkt_size(pkt) / BUCKET_SIZE;
+ knotd_mod_stats_incr(mod, tid, CTR_RSIZE, MIN(idx, RSIZE_MAX_IDX), 1);
+ }
+
+ return state;
+}
+
+int stats_load(knotd_mod_t *mod)
+{
+ stats_t *stats = calloc(1, sizeof(*stats));
+ if (stats == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ for (const ctr_desc_t *desc = ctr_descs; desc->conf_name != NULL; desc++) {
+ knotd_conf_t conf = knotd_conf_mod(mod, desc->conf_name);
+ bool enabled = conf.single.boolean;
+
+ // Initialize corresponding configuration item.
+ *(bool *)((uint8_t *)stats + desc->conf_offset) = enabled;
+
+ int ret = knotd_mod_stats_add(mod, enabled ? desc->conf_name + 1 : NULL,
+ enabled ? desc->count : 1, desc->fcn);
+ if (ret != KNOT_EOK) {
+ free(stats);
+ return ret;
+ }
+ }
+
+ knotd_mod_ctx_set(mod, stats);
+
+ return knotd_mod_hook(mod, KNOTD_STAGE_END, update_counters);
+}
+
+void stats_unload(knotd_mod_t *mod)
+{
+ free(knotd_mod_ctx(mod));
+}
+
+KNOTD_MOD_API(stats, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF,
+ stats_load, stats_unload, stats_conf, NULL);
diff --git a/src/knot/modules/stats/stats.rst b/src/knot/modules/stats/stats.rst
new file mode 100644
index 0000000..8acf1aa
--- /dev/null
+++ b/src/knot/modules/stats/stats.rst
@@ -0,0 +1,274 @@
+.. _mod-stats:
+
+``stats`` — Query statistics
+============================
+
+The module extends server statistics with incoming DNS request and corresponding
+response counters, such as used network protocol, total number of responded bytes,
+etc (see module reference for full list of supported counters).
+This module should be configured as the last module.
+
+.. NOTE::
+ Server initiated communication (outgoing NOTIFY, incoming \*XFR,...) is not
+ counted by this module.
+
+.. NOTE::
+ Leading 16-bit message size over TCP is not considered.
+
+Example
+-------
+
+Common statistics with default module configuration::
+
+ template:
+ - id: default
+ global-module: mod-stats
+
+Per zone statistics with explicit module configuration::
+
+ mod-stats:
+ - id: custom
+ edns-presence: on
+ query-type: on
+
+ template:
+ - id: default
+ module: mod-stats/custom
+
+Module reference
+----------------
+
+::
+
+ mod-stats:
+ - id: STR
+ request-protocol: BOOL
+ server-operation: BOOL
+ request-bytes: BOOL
+ response-bytes: BOOL
+ edns-presence: BOOL
+ flag-presence: BOOL
+ response-code: BOOL
+ request-edns-option: BOOL
+ response-edns-option: BOOL
+ reply-nodata: BOOL
+ query-type: BOOL
+ query-size: BOOL
+ reply-size: BOOL
+
+.. _mod-stats_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-stats_request-protocol:
+
+request-protocol
+................
+
+If enabled, all incoming requests are counted by the network protocol:
+
+* udp4 - UDP over IPv4
+* tcp4 - TCP over IPv4
+* quic4 - QUIC over IPv4
+* udp6 - UDP over IPv6
+* tcp6 - TCP over IPv6
+* quic6 - QUIC over IPv6
+* udp4-xdp - UDP over IPv4 through XDP
+* tcp4-xdp - TCP over IPv4 through XDP
+* quic4-xdp - QUIC over IPv4 through XDP
+* udp6-xdp - UDP over IPv6 through XDP
+* tcp6-xdp - TCP over IPv6 through XDP
+* quic6-xdp - QUIC over IPv6 through XDP
+
+*Default:* ``on``
+
+.. _mod-stats_server-operation:
+
+server-operation
+................
+
+If enabled, all incoming requests are counted by the server operation. The
+server operation is based on message header OpCode and message query (meta) type:
+
+* query - Normal query operation
+* update - Dynamic update operation
+* notify - NOTIFY request operation
+* axfr - Full zone transfer operation
+* ixfr - Incremental zone transfer operation
+* invalid - Invalid server operation
+
+*Default:* ``on``
+
+.. _mod-stats_request-bytes:
+
+request-bytes
+.............
+
+If enabled, all incoming request bytes are counted by the server operation:
+
+* query - Normal query bytes
+* update - Dynamic update bytes
+* other - Other request bytes
+
+*Default:* ``on``
+
+.. _mod-stats_response-bytes:
+
+response-bytes
+..............
+
+If enabled, outgoing response bytes are counted by the server operation:
+
+* reply - Normal response bytes
+* transfer - Zone transfer bytes
+* other - Other response bytes
+
+.. WARNING::
+ Dynamic update response bytes are not counted by this module.
+
+*Default:* ``on``
+
+.. _mod-stats_edns-presence:
+
+edns-presence
+.............
+
+If enabled, EDNS pseudo section presence is counted by the message direction:
+
+* request - EDNS present in request
+* response - EDNS present in response
+
+*Default:* ``off``
+
+.. _mod-stats_flag-presence:
+
+flag-presence
+.............
+
+If enabled, some message header flags are counted:
+
+* TC - Truncated Answer in response
+* DO - DNSSEC OK in request
+
+*Default:* ``off``
+
+.. _mod-stats_response-code:
+
+response-code
+.............
+
+If enabled, outgoing response code is counted:
+
+* NOERROR
+* ...
+* NOTZONE
+* BADVERS
+* ...
+* BADCOOKIE
+* other - All other codes
+
+.. NOTE::
+ In the case of multi-message zone transfer response, just one counter is
+ incremented.
+
+.. WARNING::
+ Dynamic update response code is not counted by this module.
+
+*Default:* ``on``
+
+.. _mod-stats_request-edns-option:
+
+request-edns-option
+...................
+
+If enabled, EDNS options in requests are counted by their code:
+
+* CODE0
+* ...
+* EDNS-KEY-TAG (CODE14)
+* other - All other codes
+
+*Default:* ``off``
+
+.. _mod-stats_response-edns-option:
+
+response-edns-option
+....................
+
+If enabled, EDNS options in responses are counted by their code. See
+:ref:`mod-stats_request-edns-option`.
+
+*Default:* ``off``
+
+.. _mod-stats_reply-nodata:
+
+reply-nodata
+............
+
+If enabled, NODATA pseudo RCODE (:rfc:`2308#section-2.2`) is counted by the
+query type:
+
+* A
+* AAAA
+* other - All other types
+
+*Default:* ``off``
+
+.. _mod-stats_query-type:
+
+query-type
+..........
+
+If enabled, normal query type is counted:
+
+* A (TYPE1)
+* ...
+* TYPE65
+* SPF (TYPE99)
+* ...
+* TYPE110
+* ANY (TYPE255)
+* ...
+* TYPE260
+* other - All other types
+
+.. NOTE::
+ Not all assigned meta types (IXFR, AXFR,...) have their own counters,
+ because such types are not processed as normal query.
+
+*Default:* ``off``
+
+.. _mod-stats_query-size:
+
+query-size
+..........
+
+If enabled, normal query message size distribution is counted by the size range
+in bytes:
+
+* 0-15
+* 16-31
+* ...
+* 272-287
+* 288-65535
+
+*Default:* ``off``
+
+.. _mod-stats_reply-size:
+
+reply-size
+..........
+
+If enabled, normal reply message size distribution is counted by the size range
+in bytes:
+
+* 0-15
+* 16-31
+* ...
+* 4080-4095
+* 4096-65535
+
+*Default:* ``off``
diff --git a/src/knot/modules/synthrecord/Makefile.inc b/src/knot/modules/synthrecord/Makefile.inc
new file mode 100644
index 0000000..9fae495
--- /dev/null
+++ b/src/knot/modules/synthrecord/Makefile.inc
@@ -0,0 +1,13 @@
+knot_modules_synthrecord_la_SOURCES = knot/modules/synthrecord/synthrecord.c
+EXTRA_DIST += knot/modules/synthrecord/synthrecord.rst
+
+if STATIC_MODULE_synthrecord
+libknotd_la_SOURCES += $(knot_modules_synthrecord_la_SOURCES)
+endif
+
+if SHARED_MODULE_synthrecord
+knot_modules_synthrecord_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_synthrecord_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+knot_modules_synthrecord_la_LIBADD = $(libcontrib_LIBS)
+pkglib_LTLIBRARIES += knot/modules/synthrecord.la
+endif
diff --git a/src/knot/modules/synthrecord/synthrecord.c b/src/knot/modules/synthrecord/synthrecord.c
new file mode 100644
index 0000000..d7af9a1
--- /dev/null
+++ b/src/knot/modules/synthrecord/synthrecord.c
@@ -0,0 +1,625 @@
+/* Copyright (C) 2022 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 "contrib/ctype.h"
+#include "contrib/macros.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+#include "contrib/wire_ctx.h"
+#include "knot/include/module.h"
+
+#define MOD_NET "\x07""network"
+#define MOD_ORIGIN "\x06""origin"
+#define MOD_PREFIX "\x06""prefix"
+#define MOD_TTL "\x03""ttl"
+#define MOD_TYPE "\x04""type"
+#define MOD_SHORT "\x0d""reverse-short"
+
+/*! \brief Supported answer synthesis template types. */
+enum synth_template_type {
+ SYNTH_NULL = 0,
+ SYNTH_FORWARD = 1,
+ SYNTH_REVERSE = 2
+};
+
+static const knot_lookup_t synthetic_types[] = {
+ { SYNTH_FORWARD, "forward" },
+ { SYNTH_REVERSE, "reverse" },
+ { 0, NULL }
+};
+
+int check_prefix(knotd_conf_check_args_t *args)
+{
+ if (strchr((const char *)args->data, '.') != NULL) {
+ args->err_str = "dot '.' is not allowed";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+const yp_item_t synth_record_conf[] = {
+ { MOD_TYPE, YP_TOPT, YP_VOPT = { synthetic_types, SYNTH_NULL } },
+ { MOD_PREFIX, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { check_prefix } },
+ { MOD_ORIGIN, YP_TDNAME, YP_VNONE },
+ { MOD_TTL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 3600, YP_STIME } },
+ { MOD_NET, YP_TNET, YP_VNONE, YP_FMULTI },
+ { MOD_SHORT, YP_TBOOL, YP_VBOOL = { true } },
+ { NULL }
+};
+
+int synth_record_conf_check(knotd_conf_check_args_t *args)
+{
+ // Check type.
+ knotd_conf_t type = knotd_conf_check_item(args, MOD_TYPE);
+ if (type.count == 0) {
+ args->err_str = "no synthesis type specified";
+ return KNOT_EINVAL;
+ }
+
+ // Check origin.
+ knotd_conf_t origin = knotd_conf_check_item(args, MOD_ORIGIN);
+ if (origin.count == 0 && type.single.option == SYNTH_REVERSE) {
+ args->err_str = "no origin specified";
+ return KNOT_EINVAL;
+ }
+ if (origin.count != 0 && type.single.option == SYNTH_FORWARD) {
+ args->err_str = "origin not allowed with forward type";
+ return KNOT_EINVAL;
+ }
+
+ // Check network subnet.
+ knotd_conf_t net = knotd_conf_check_item(args, MOD_NET);
+ if (net.count == 0) {
+ args->err_str = "no network subnet specified";
+ return KNOT_EINVAL;
+ }
+ knotd_conf_free(&net);
+
+ // Check reverse-short parameter is only for reverse synthrecord.
+ knotd_conf_t reverse_short = knotd_conf_check_item(args, MOD_SHORT);
+ if (reverse_short.count != 0 && type.single.option == SYNTH_FORWARD) {
+ args->err_str = "reverse-short not allowed with forward type";
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+#define ARPA_ZONE_LABELS 2
+#define IPV4_ADDR_LABELS 4
+#define IPV6_ADDR_LABELS 32
+#define IPV4_ARPA_DNAME (uint8_t *)"\x07""in-addr""\x04""arpa"
+#define IPV6_ARPA_DNAME (uint8_t *)"\x03""ip6""\x04""arpa"
+#define IPV4_ARPA_LEN 14
+#define IPV6_ARPA_LEN 10
+
+/*!
+ * \brief Synthetic response template.
+ */
+typedef struct {
+ struct sockaddr_storage addr;
+ struct sockaddr_storage addr_max;
+ int addr_mask;
+} synth_templ_addr_t;
+
+typedef struct {
+ enum synth_template_type type;
+ char *prefix;
+ size_t prefix_len;
+ char *zone;
+ size_t zone_len;
+ uint32_t ttl;
+ size_t addr_count;
+ synth_templ_addr_t *addr;
+ bool reverse_short;
+} synth_template_t;
+
+typedef union {
+ uint32_t b32;
+ uint8_t b4[4];
+} addr_block_t;
+
+/*! \brief Write one IPV4 address block without redundant leading zeros. */
+static unsigned block_write(addr_block_t *block, char *addr_str)
+{
+ unsigned len = 0;
+
+ if (block->b4[0] != '0') {
+ addr_str[len++] = block->b4[0];
+ }
+ if (len > 0 || block->b4[1] != '0') {
+ addr_str[len++] = block->b4[1];
+ }
+ if (len > 0 || block->b4[2] != '0') {
+ addr_str[len++] = block->b4[2];
+ }
+ addr_str[len++] = block->b4[3];
+
+ return len;
+}
+
+/*! \brief Substitute all occurrences of given character. */
+static void str_subst(char *str, size_t len, char from, char to)
+{
+ for (int i = 0; i < len; ++i) {
+ if (str[i] == from) {
+ str[i] = to;
+ }
+ }
+}
+
+/*! \brief Separator character for address family. */
+static char str_separator(int addr_family)
+{
+ return (addr_family == AF_INET6) ? ':' : '.';
+}
+
+/*! \brief Return true if query type is satisfied with provided address family. */
+static bool query_satisfied_by_family(uint16_t qtype, int family)
+{
+ switch (qtype) {
+ case KNOT_RRTYPE_A: return family == AF_INET;
+ case KNOT_RRTYPE_AAAA: return family == AF_INET6;
+ case KNOT_RRTYPE_ANY: return true;
+ default: return false;
+ }
+}
+
+/*! \brief Parse address from reverse query QNAME and return address family. */
+static int reverse_addr_parse(knotd_qdata_t *qdata, const synth_template_t *tpl,
+ char *addr_str, int *addr_family, bool *parent)
+{
+ /* QNAME required format is [address].[subnet/zone]
+ * f.e. [1.0...0].[h.g.f.e.0.0.0.0.d.c.b.a.ip6.arpa] represents
+ * [abcd:0:efgh::1] */
+ const knot_dname_t *label = qdata->name; // uncompressed name
+
+ static const char ipv4_zero[] = "0.0.0.0";
+
+ bool can_ipv4 = true;
+ bool can_ipv6 = true;
+ unsigned labels = 0;
+
+ uint8_t buf4[16], *buf4_end = buf4 + sizeof(buf4), *buf4_pos = buf4_end;
+ uint8_t buf6[32], *buf6_end = buf6 + sizeof(buf6), *buf6_pos = buf6_end;
+
+ for ( ; labels < IPV6_ADDR_LABELS; labels++) {
+ if (unlikely(*label == 0)) {
+ return KNOT_EINVAL;
+ }
+ if (label[1] == 'i') {
+ break;
+ }
+ if (labels < IPV4_ADDR_LABELS) {
+ switch (*label) {
+ case 1:
+ assert(buf4 + 1 < buf4_pos && buf6 < buf6_pos);
+ *--buf6_pos = label[1];
+ *--buf4_pos = label[1];
+ *--buf4_pos = '.';
+ break;
+ case 2:
+ case 3:
+ assert(buf4 + *label < buf4_pos);
+ can_ipv6 = false;
+ buf4_pos -= *label;
+ memcpy(buf4_pos, label + 1, *label);
+ *--buf4_pos = '.';
+ break;
+ case 4:
+ case 5:
+ case 6: // Ignore second possibly classless label (e.g. 0/25, 193/26).
+ if (labels-- != 1) {
+ return KNOT_EINVAL;
+ }
+ can_ipv6 = false;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ } else {
+ can_ipv4 = false;
+ if (!can_ipv6 || *label != 1) {
+ return KNOT_EINVAL;
+ }
+ assert(buf6 < buf6_pos);
+ *--buf6_pos = label[1];
+
+ }
+ label += *label + sizeof(*label);
+ }
+
+ if (can_ipv4 && knot_dname_is_equal(label, IPV4_ARPA_DNAME)) {
+ *addr_family = AF_INET;
+ *parent = (labels < IPV4_ADDR_LABELS);
+ int buf4_overweight = (buf4_end - buf4_pos) - (2 * labels);
+ assert(buf4_overweight >= 0);
+ memcpy(addr_str + buf4_overweight, ipv4_zero, sizeof(ipv4_zero));
+ if (labels > 0) {
+ buf4_pos++; // skip leading '.'
+ memcpy(addr_str, buf4_pos, buf4_end - buf4_pos);
+ }
+ return KNOT_EOK;
+ } else if (can_ipv6 && knot_dname_is_equal(label, IPV6_ARPA_DNAME)) {
+ *addr_family = AF_INET6;
+ *parent = (labels < IPV6_ADDR_LABELS);
+
+ addr_block_t blocks[8] = { { 0 } };
+ int compr_start = -1, compr_end = -1;
+
+ unsigned buf6_len = buf6_end - buf6_pos;
+ memcpy(blocks, buf6_pos, buf6_len);
+ memset(((uint8_t *)blocks) + buf6_len, 0x30, sizeof(blocks) - buf6_len);
+
+ for (int i = 0; i < 8; i++) {
+ addr_block_t *block = &blocks[i];
+
+ /* The Unicode string MUST NOT contain "--" in the third and fourth
+ character positions and MUST NOT start or end with a "-".
+ So we will not compress first, second, and last address blocks
+ for simplicity. And we will not compress a single block.
+
+ i: 0 1 2 3 4 5 6 7
+ label block: H:G:F:E:D:C:B:A
+ address block: A B C D E F G H
+ compressibles: 0 0 0 0 0
+ 0 0 0 0
+ 0 0 0
+ 0 0
+ */
+ // Check for trailing zero dual-blocks.
+ if (tpl->reverse_short && i > 1 && i < 6 &&
+ block[0].b32 == 0x30303030UL && block[1].b32 == 0x30303030UL) {
+ if (compr_start == -1) {
+ compr_start = i;
+ }
+ } else {
+ if (compr_start != -1 && compr_end == -1) {
+ compr_end = i;
+ }
+ }
+ }
+
+ // Write address blocks.
+ unsigned addr_len = 0;
+ for (int i = 0; i < 8; i++) {
+ if (compr_start == -1 || i < compr_start || i > compr_end) {
+ // Write regular address block.
+ if (tpl->reverse_short) {
+ addr_len += block_write(&blocks[i], addr_str + addr_len);
+ } else {
+ assert(sizeof(blocks[i]) == 4);
+ memcpy(addr_str + addr_len, &blocks[i], 4);
+ addr_len += 4;
+ }
+ // Write separator
+ if (i < 7) {
+ addr_str[addr_len++] = ':';
+ }
+ } else if (compr_start != -1 && compr_end == i) {
+ // Write compression double colon.
+ addr_str[addr_len++] = ':';
+ }
+ }
+ addr_str[addr_len] = '\0';
+
+ return KNOT_EOK;
+ }
+
+ return KNOT_EINVAL;
+}
+
+static int forward_addr_parse(knotd_qdata_t *qdata, const synth_template_t *tpl,
+ char *addr_str, int *addr_family)
+{
+ const knot_dname_t *label = qdata->name;
+
+ // Check for prefix mismatch.
+ if (label[0] <= tpl->prefix_len ||
+ memcmp(label + 1, tpl->prefix, tpl->prefix_len) != 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Copy address part.
+ unsigned addr_len = label[0] - tpl->prefix_len;
+ memcpy(addr_str, label + 1 + tpl->prefix_len, addr_len);
+ addr_str[addr_len] = '\0';
+
+ // Determine address family.
+ unsigned hyphen_cnt = 0;
+ const char *ch = addr_str;
+ while (hyphen_cnt < 4 && ch < addr_str + addr_len) {
+ if (*ch == '-') {
+ hyphen_cnt++;
+ if (*++ch == '-') { // Check for shortened IPv6 notation.
+ hyphen_cnt = 4;
+ break;
+ }
+ }
+ ch++;
+ }
+ // Valid IPv4 address looks like A-B-C-D.
+ *addr_family = (hyphen_cnt == 3) ? AF_INET : AF_INET6;
+
+ // Restore correct address format.
+ const char sep = str_separator(*addr_family);
+ str_subst(addr_str, addr_len, '-', sep);
+
+ return KNOT_EOK;
+}
+
+static int addr_parse(knotd_qdata_t *qdata, const synth_template_t *tpl, char *addr_str,
+ int *addr_family, bool *parent)
+{
+ switch (tpl->type) {
+ case SYNTH_REVERSE: return reverse_addr_parse(qdata, tpl, addr_str, addr_family, parent);
+ case SYNTH_FORWARD: return forward_addr_parse(qdata, tpl, addr_str, addr_family);
+ default: return KNOT_EINVAL;
+ }
+}
+
+static knot_dname_t *synth_ptrname(uint8_t *out, const char *addr_str,
+ const synth_template_t *tpl, int addr_family)
+{
+ knot_dname_txt_storage_t ptrname;
+ int addr_len = strlen(addr_str);
+ const char sep = str_separator(addr_family);
+
+ // PTR right-hand value is [prefix][address][zone]
+ wire_ctx_t ctx = wire_ctx_init((uint8_t *)ptrname, sizeof(ptrname));
+ wire_ctx_write(&ctx, tpl->prefix, tpl->prefix_len);
+ wire_ctx_write(&ctx, addr_str, addr_len);
+ wire_ctx_write_u8(&ctx, '.');
+ wire_ctx_write(&ctx, tpl->zone, tpl->zone_len);
+ wire_ctx_write_u8(&ctx, '\0');
+ if (ctx.error != KNOT_EOK) {
+ return NULL;
+ }
+
+ // Substitute address separator by '-'.
+ str_subst(ptrname + tpl->prefix_len, addr_len, sep, '-');
+
+ // Convert to domain name.
+ return knot_dname_from_str(out, ptrname, KNOT_DNAME_MAXLEN);
+}
+
+static int reverse_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt,
+ knot_rrset_t *rr, int addr_family)
+{
+ // Synthesize PTR record data.
+ knot_dname_storage_t ptrname;
+ if (synth_ptrname(ptrname, addr_str, tpl, addr_family) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ rr->type = KNOT_RRTYPE_PTR;
+ knot_rrset_add_rdata(rr, ptrname, knot_dname_size(ptrname), &pkt->mm);
+
+ return KNOT_EOK;
+}
+
+static int forward_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt,
+ knot_rrset_t *rr, int addr_family)
+{
+ struct sockaddr_storage query_addr;
+ sockaddr_set(&query_addr, addr_family, addr_str, 0);
+
+ // Specify address type and data.
+ if (addr_family == AF_INET6) {
+ rr->type = KNOT_RRTYPE_AAAA;
+ const struct sockaddr_in6* ip = (const struct sockaddr_in6*)&query_addr;
+ knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin6_addr,
+ sizeof(struct in6_addr), &pkt->mm);
+ } else if (addr_family == AF_INET) {
+ rr->type = KNOT_RRTYPE_A;
+ const struct sockaddr_in* ip = (const struct sockaddr_in*)&query_addr;
+ knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin_addr,
+ sizeof(struct in_addr), &pkt->mm);
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static knot_rrset_t *synth_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, int addr_family)
+{
+ knot_rrset_t *rr = knot_rrset_new(qdata->name, 0, KNOT_CLASS_IN, tpl->ttl,
+ &pkt->mm);
+ if (rr == NULL) {
+ return NULL;
+ }
+
+ // Fill in the specific data.
+ int ret = KNOT_ERROR;
+ switch (tpl->type) {
+ case SYNTH_REVERSE: ret = reverse_rr(addr_str, tpl, pkt, rr, addr_family); break;
+ case SYNTH_FORWARD: ret = forward_rr(addr_str, tpl, pkt, rr, addr_family); break;
+ default: break;
+ }
+
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(rr, &pkt->mm);
+ return NULL;
+ }
+
+ return rr;
+}
+
+/*! \brief Check if query fits the template requirements. */
+static knotd_in_state_t template_match(knotd_in_state_t state, const synth_template_t *tpl,
+ knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ int provided_af = AF_UNSPEC;
+ struct sockaddr_storage query_addr;
+ char addr_str[SOCKADDR_STRLEN];
+ assert(SOCKADDR_STRLEN > KNOT_DNAME_MAXLABELLEN);
+ bool parent = false; // querying empty-non-terminal being (possibly indirect) parent of synthesized name
+
+ // Parse address from query name.
+ if (addr_parse(qdata, tpl, addr_str, &provided_af, &parent) != KNOT_EOK ||
+ sockaddr_set(&query_addr, provided_af, addr_str, 0) != KNOT_EOK) {
+ return state;
+ }
+
+ // Try all available addresses.
+ int i;
+ for (i = 0; i < tpl->addr_count; i++) {
+ if (tpl->addr[i].addr_max.ss_family == AF_UNSPEC) {
+ if (sockaddr_net_match(&query_addr, &tpl->addr[i].addr,
+ tpl->addr[i].addr_mask)) {
+ break;
+ }
+ } else {
+ if (sockaddr_range_match(&query_addr, &tpl->addr[i].addr,
+ &tpl->addr[i].addr_max)) {
+ break;
+ }
+ }
+ }
+ if (i >= tpl->addr_count) {
+ return state;
+ }
+
+ // Check if the request is for an available query type.
+ uint16_t qtype = knot_pkt_qtype(qdata->query);
+ switch (tpl->type) {
+ case SYNTH_FORWARD:
+ assert(!parent);
+ if (!query_satisfied_by_family(qtype, provided_af)) {
+ qdata->rcode = KNOT_RCODE_NOERROR;
+ return KNOTD_IN_STATE_NODATA;
+ }
+ break;
+ case SYNTH_REVERSE:
+ if (parent || (qtype != KNOT_RRTYPE_PTR && qtype != KNOT_RRTYPE_ANY)) {
+ qdata->rcode = KNOT_RCODE_NOERROR;
+ return KNOTD_IN_STATE_NODATA;
+ }
+ break;
+ default:
+ return state;
+ }
+
+ // Synthesize record from template.
+ knot_rrset_t *rr = synth_rr(addr_str, tpl, pkt, qdata, provided_af);
+ if (rr == NULL) {
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ // Insert synthetic response into packet.
+ if (knot_pkt_put(pkt, 0, rr, KNOT_PF_FREE) != KNOT_EOK) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ // Authoritative response.
+ knot_wire_set_aa(pkt->wire);
+
+ return KNOTD_IN_STATE_HIT;
+}
+
+static knotd_in_state_t solve_synth_record(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata && mod);
+
+ // Applicable when search in zone fails.
+ if (state != KNOTD_IN_STATE_MISS) {
+ return state;
+ }
+
+ // Check if template fits.
+ return template_match(state, knotd_mod_ctx(mod), pkt, qdata);
+}
+
+int synth_record_load(knotd_mod_t *mod)
+{
+ // Create synthesis template.
+ synth_template_t *tpl = calloc(1, sizeof(*tpl));
+ if (tpl == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Set type.
+ knotd_conf_t conf = knotd_conf_mod(mod, MOD_TYPE);
+ tpl->type = conf.single.option;
+
+ /* Set prefix. */
+ conf = knotd_conf_mod(mod, MOD_PREFIX);
+ tpl->prefix = strdup(conf.single.string);
+ tpl->prefix_len = strlen(tpl->prefix);
+
+ // Set origin if generating reverse record.
+ if (tpl->type == SYNTH_REVERSE) {
+ conf = knotd_conf_mod(mod, MOD_ORIGIN);
+ tpl->zone = knot_dname_to_str_alloc(conf.single.dname);
+ if (tpl->zone == NULL) {
+ free(tpl->prefix);
+ free(tpl);
+ return KNOT_ENOMEM;
+ }
+ tpl->zone_len = strlen(tpl->zone);
+ }
+
+ // Set ttl.
+ conf = knotd_conf_mod(mod, MOD_TTL);
+ tpl->ttl = conf.single.integer;
+
+ // Set address.
+ conf = knotd_conf_mod(mod, MOD_NET);
+ tpl->addr_count = conf.count;
+ tpl->addr = calloc(conf.count, sizeof(*tpl->addr));
+ if (tpl->addr == NULL) {
+ knotd_conf_free(&conf);
+ free(tpl->zone);
+ free(tpl->prefix);
+ free(tpl);
+ return KNOT_ENOMEM;
+ }
+ for (size_t i = 0; i < conf.count; i++) {
+ tpl->addr[i].addr = conf.multi[i].addr;
+ tpl->addr[i].addr_max = conf.multi[i].addr_max;
+ tpl->addr[i].addr_mask = conf.multi[i].addr_mask;
+ }
+ knotd_conf_free(&conf);
+
+ // Set address shortening.
+ if (tpl->type == SYNTH_REVERSE) {
+ conf = knotd_conf_mod(mod, MOD_SHORT);
+ tpl->reverse_short = conf.single.boolean;
+ }
+
+ knotd_mod_ctx_set(mod, tpl);
+
+ return knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, solve_synth_record);
+}
+
+void synth_record_unload(knotd_mod_t *mod)
+{
+ synth_template_t *tpl = knotd_mod_ctx(mod);
+
+ free(tpl->addr);
+ free(tpl->zone);
+ free(tpl->prefix);
+ free(tpl);
+}
+
+KNOTD_MOD_API(synthrecord, KNOTD_MOD_FLAG_SCOPE_ZONE,
+ synth_record_load, synth_record_unload, synth_record_conf,
+ synth_record_conf_check);
diff --git a/src/knot/modules/synthrecord/synthrecord.rst b/src/knot/modules/synthrecord/synthrecord.rst
new file mode 100644
index 0000000..4ad0a4b
--- /dev/null
+++ b/src/knot/modules/synthrecord/synthrecord.rst
@@ -0,0 +1,170 @@
+.. _mod-synthrecord:
+
+``synthrecord`` – Automatic forward/reverse records
+===================================================
+
+This module is able to synthesize either forward or reverse records for
+a given prefix and subnet.
+
+Records are synthesized only if the query can't be satisfied from the zone.
+Both IPv4 and IPv6 are supported.
+
+Example
+-------
+
+Automatic forward records
+.........................
+
+::
+
+ mod-synthrecord:
+ - id: test1
+ type: forward
+ prefix: dynamic-
+ ttl: 400
+ network: 2620:0:b61::/52
+
+ zone:
+ - domain: test.
+ file: test.zone # Must exist
+ module: mod-synthrecord/test1
+
+Result:
+
+.. code-block:: console
+
+ $ kdig AAAA dynamic-2620-0-b61-100--1.test.
+ ...
+ ;; QUESTION SECTION:
+ ;; dynamic-2620-0-b61-100--1.test. IN AAAA
+
+ ;; ANSWER SECTION:
+ dynamic-2620-0-b61-100--1.test. 400 IN AAAA 2620:0:b61:100::1
+
+You can also have CNAME aliases to the dynamic records, which are going to be
+further resolved:
+
+.. code-block:: console
+
+ $ kdig AAAA alias.test.
+ ...
+ ;; QUESTION SECTION:
+ ;; alias.test. IN AAAA
+
+ ;; ANSWER SECTION:
+ alias.test. 3600 IN CNAME dynamic-2620-0-b61-100--2.test.
+ dynamic-2620-0-b61-100--2.test. 400 IN AAAA 2620:0:b61:100::2
+
+Automatic reverse records
+.........................
+
+::
+
+ mod-synthrecord:
+ - id: test2
+ type: reverse
+ prefix: dynamic-
+ origin: test
+ ttl: 400
+ network: 2620:0:b61::/52
+
+ zone:
+ - domain: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa.
+ file: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa.zone # Must exist
+ module: mod-synthrecord/test2
+
+Result:
+
+.. code-block:: console
+
+ $ kdig -x 2620:0:b61::1
+ ...
+ ;; QUESTION SECTION:
+ ;; 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. IN PTR
+
+ ;; ANSWER SECTION:
+ 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. 400 IN PTR dynamic-2620-0-b61--1.test.
+
+Module reference
+----------------
+
+::
+
+ mod-synthrecord:
+ - id: STR
+ type: forward | reverse
+ prefix: STR
+ origin: DNAME
+ ttl: INT
+ network: ADDR[/INT] | ADDR-ADDR ...
+ reverse-short: BOOL
+
+.. _mod-synthrecord_id:
+
+id
+..
+
+A module identifier.
+
+.. _mod-synthrecord_type:
+
+type
+....
+
+The type of generated records.
+
+Possible values:
+
+- ``forward`` – Forward records
+- ``reverse`` – Reverse records
+
+*Required*
+
+.. _mod-synthrecord_prefix:
+
+prefix
+......
+
+A record owner prefix.
+
+.. NOTE::
+ The value doesn’t allow dots, address parts in the synthetic names are
+ separated with a dash.
+
+*Default:* empty
+
+.. _mod-synthrecord_origin:
+
+origin
+......
+
+A zone origin (only valid for the :ref:`reverse type<mod-synthrecord_type>`).
+
+*Required*
+
+.. _mod-synthrecord_ttl:
+
+ttl
+...
+
+Time to live of the generated records.
+
+*Default:* ``3600``
+
+.. _mod-synthrecord_network:
+
+network
+.......
+
+An IP address, a network subnet, or a network range the query must match.
+
+*Required*
+
+.. _mod-synthrecord_reverse-short:
+
+reverse-short
+.............
+
+If enabled, a shortened IPv6 address can be used for reverse record rdata synthesis.
+
+*Default:* ``on``
diff --git a/src/knot/modules/whoami/Makefile.inc b/src/knot/modules/whoami/Makefile.inc
new file mode 100644
index 0000000..4d20fcb
--- /dev/null
+++ b/src/knot/modules/whoami/Makefile.inc
@@ -0,0 +1,12 @@
+knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c
+EXTRA_DIST += knot/modules/whoami/whoami.rst
+
+if STATIC_MODULE_whoami
+libknotd_la_SOURCES += $(knot_modules_whoami_la_SOURCES)
+endif
+
+if SHARED_MODULE_whoami
+knot_modules_whoami_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS)
+knot_modules_whoami_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS)
+pkglib_LTLIBRARIES += knot/modules/whoami.la
+endif
diff --git a/src/knot/modules/whoami/whoami.c b/src/knot/modules/whoami/whoami.c
new file mode 100644
index 0000000..99c4372
--- /dev/null
+++ b/src/knot/modules/whoami/whoami.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2017 Fastly, Inc.
+
+ 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 <netinet/in.h>
+
+#include "knot/include/module.h"
+
+static knotd_in_state_t whoami_query(knotd_in_state_t state, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, knotd_mod_t *mod)
+{
+ assert(pkt && qdata);
+
+ const knot_dname_t *zone_name = knotd_qdata_zone_name(qdata);
+ if (zone_name == NULL) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* Retrieve the query tuple. */
+ const knot_dname_t *qname = knot_pkt_qname(qdata->query);
+ const uint16_t qtype = knot_pkt_qtype(qdata->query);
+ const uint16_t qclass = knot_pkt_qclass(qdata->query);
+
+ /* We only generate A and AAAA records, which are Internet class. */
+ if (qclass != KNOT_CLASS_IN) {
+ return state;
+ }
+
+ /* Only handle queries with qname set to the zone name. */
+ if (!knot_dname_is_equal(qname, zone_name)) {
+ return state;
+ }
+
+ /* Only handle A and AAAA queries. */
+ if (qtype != KNOT_RRTYPE_A && qtype != KNOT_RRTYPE_AAAA) {
+ return state;
+ }
+
+ /* Retrieve the IP address that sent the query. */
+ const struct sockaddr_storage *query_source = knotd_qdata_remote_addr(qdata);
+ if (query_source == NULL) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* If the socket address family corresponds to the query type (i.e.,
+ * AF_INET <-> A and AF_INET6 <-> AAAA), put the socket address and
+ * length into 'rdata' and 'len_rdata'.
+ */
+ const void *rdata = NULL;
+ uint16_t len_rdata = 0;
+ if (query_source->ss_family == AF_INET && qtype == KNOT_RRTYPE_A) {
+ const struct sockaddr_in *sai = (struct sockaddr_in *)query_source;
+ rdata = &sai->sin_addr.s_addr;
+ len_rdata = sizeof(sai->sin_addr.s_addr);
+ } else if (query_source->ss_family == AF_INET6 && qtype == KNOT_RRTYPE_AAAA) {
+ const struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)query_source;
+ rdata = &sai6->sin6_addr;
+ len_rdata = sizeof(sai6->sin6_addr);
+ } else {
+ /* Query type didn't match address family. */
+ return state;
+ }
+
+ /* Synthesize the response RRset. */
+
+ /* TTL is taken from the TTL of the SOA record. */
+ knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA);
+
+ /* Owner name, type, and class are taken from the question. */
+ knot_rrset_t *rrset = knot_rrset_new(qname, qtype, qclass, soa.ttl, &pkt->mm);
+ if (rrset == NULL) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* Record data is the query source address. */
+ int ret = knot_rrset_add_rdata(rrset, rdata, len_rdata, &pkt->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(rrset, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* Add the new RRset to the response packet. */
+ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rrset, KNOT_PF_FREE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(rrset, &pkt->mm);
+ return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* Success. */
+ return KNOTD_IN_STATE_HIT;
+}
+
+int whoami_load(knotd_mod_t *mod)
+{
+ /* Hook to the query plan. */
+ knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, whoami_query);
+
+ return KNOT_EOK;
+}
+
+KNOTD_MOD_API(whoami, KNOTD_MOD_FLAG_SCOPE_ZONE | KNOTD_MOD_FLAG_OPT_CONF,
+ whoami_load, NULL, NULL, NULL);
diff --git a/src/knot/modules/whoami/whoami.rst b/src/knot/modules/whoami/whoami.rst
new file mode 100644
index 0000000..25d0174
--- /dev/null
+++ b/src/knot/modules/whoami/whoami.rst
@@ -0,0 +1,97 @@
+.. _mod-whoami:
+
+``whoami`` — Whoami response
+============================
+
+The module synthesizes an A or AAAA record containing the query source IP address,
+at the apex of the zone being served. It makes sure to allow Knot DNS to generate
+cacheable negative responses, and to allow fallback to extra records defined in the
+underlying zone file. The TTL of the synthesized record is copied from
+the TTL of the SOA record in the zone file.
+
+Because a DNS query for type A or AAAA has nothing to do with whether
+the query occurs over IPv4 or IPv6, this module requires a special
+zone configuration to support both address families. For A queries, the
+underlying zone must have a set of nameservers that only have IPv4
+addresses, and for AAAA queries, the underlying zone must have a set of
+nameservers that only have IPv6 addresses.
+
+Example
+-------
+
+To enable this module, you need to add something like the following to
+the Knot DNS configuration file::
+
+ zone:
+ - domain: whoami.domain.example
+ file: "/path/to/whoami.domain.example"
+ module: mod-whoami
+
+ zone:
+ - domain: whoami6.domain.example
+ file: "/path/to/whoami6.domain.example"
+ module: mod-whoami
+
+The whoami.domain.example zone file example:
+
+ .. code-block:: none
+
+ $TTL 1
+
+ @ SOA (
+ whoami.domain.example. ; MNAME
+ hostmaster.domain.example. ; RNAME
+ 2016051300 ; SERIAL
+ 86400 ; REFRESH
+ 86400 ; RETRY
+ 86400 ; EXPIRE
+ 1 ; MINIMUM
+ )
+
+ $TTL 86400
+
+ @ NS ns1.whoami.domain.example.
+ @ NS ns2.whoami.domain.example.
+ @ NS ns3.whoami.domain.example.
+ @ NS ns4.whoami.domain.example.
+
+ ns1 A 198.51.100.53
+ ns2 A 192.0.2.53
+ ns3 A 203.0.113.53
+ ns4 A 198.19.123.53
+
+The whoami6.domain.example zone file example:
+
+ .. code-block:: none
+
+ $TTL 1
+
+ @ SOA (
+ whoami6.domain.example. ; MNAME
+ hostmaster.domain.example. ; RNAME
+ 2016051300 ; SERIAL
+ 86400 ; REFRESH
+ 86400 ; RETRY
+ 86400 ; EXPIRE
+ 1 ; MINIMUM
+ )
+
+ $TTL 86400
+
+ @ NS ns1.whoami6.domain.example.
+ @ NS ns2.whoami6.domain.example.
+ @ NS ns3.whoami6.domain.example.
+ @ NS ns4.whoami6.domain.example.
+
+ ns1 AAAA 2001:db8:100::53
+ ns2 AAAA 2001:db8:200::53
+ ns3 AAAA 2001:db8:300::53
+ ns4 AAAA 2001:db8:400::53
+
+The parent domain would then delegate whoami.domain.example to
+ns[1-4].whoami.domain.example and whoami6.domain.example to
+ns[1-4].whoami6.domain.example, and include the corresponding A-only or
+AAAA-only glue records.
+
+.. NOTE::
+ This module is not configurable.
diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c
new file mode 100644
index 0000000..dac4a43
--- /dev/null
+++ b/src/knot/nameserver/axfr.c
@@ -0,0 +1,225 @@
+/* Copyright (C) 2022 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 <urcu.h>
+
+#include "contrib/mempattern.h"
+#include "contrib/sockaddr.h"
+#include "knot/nameserver/axfr.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/log.h"
+#include "knot/nameserver/xfr.h"
+#include "libknot/libknot.h"
+
+#define ZONE_NAME(qdata) knot_pkt_qname((qdata)->query)
+#define REMOTE(qdata) (struct sockaddr *)knotd_qdata_remote_addr(qdata)
+
+#define AXFROUT_LOG(priority, qdata, fmt...) \
+ ns_log(priority, ZONE_NAME(qdata), LOG_OPERATION_AXFR, \
+ LOG_DIRECTION_OUT, REMOTE(qdata), false, fmt)
+
+/* AXFR context. @note aliasing the generic xfr_proc */
+struct axfr_proc {
+ struct xfr_proc proc;
+ trie_it_t *i;
+ zone_tree_it_t it;
+ unsigned cur_rrset;
+};
+
+static int axfr_put_rrsets(knot_pkt_t *pkt, zone_node_t *node,
+ struct axfr_proc *state)
+{
+ assert(node != NULL);
+
+ /* Append all RRs. */
+ for (unsigned i = state->cur_rrset; i < node->rrset_count; ++i) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ if (rrset.type == KNOT_RRTYPE_SOA) {
+ continue;
+ }
+
+ int ret = knot_pkt_put(pkt, 0, &rrset, KNOT_PF_NOTRUNC | KNOT_PF_ORIGTTL);
+ if (ret != KNOT_EOK) {
+ /* If something failed, remember the current RR for later. */
+ state->cur_rrset = i;
+ return ret;
+ }
+ if (pkt->size > KNOT_WIRE_PTR_MAX) {
+ // optimization: once the XFR DNS message is > 16 KiB, compression
+ // is limited. Better wrap to next message.
+ state->cur_rrset = i + 1;
+ return KNOT_ESPACE;
+ }
+ }
+
+ state->cur_rrset = 0;
+
+ return KNOT_EOK;
+}
+
+static int axfr_process_node_tree(knot_pkt_t *pkt, const void *item,
+ struct xfr_proc *state)
+{
+ assert(item != NULL);
+
+ struct axfr_proc *axfr = (struct axfr_proc*)state;
+
+ int ret = zone_tree_it_begin((zone_tree_t *)item, &axfr->it); // does nothing if already iterating
+
+ /* Put responses. */
+ while (ret == KNOT_EOK && !zone_tree_it_finished(&axfr->it)) {
+ zone_node_t *node = zone_tree_it_val(&axfr->it);
+ ret = axfr_put_rrsets(pkt, node, axfr);
+ if (ret == KNOT_EOK) {
+ zone_tree_it_next(&axfr->it);
+ }
+ }
+
+ /* Finished all nodes. */
+ if (ret == KNOT_EOK) {
+ zone_tree_it_free(&axfr->it);
+ }
+ return ret;
+}
+
+static void axfr_query_cleanup(knotd_qdata_t *qdata)
+{
+ struct axfr_proc *axfr = (struct axfr_proc *)qdata->extra->ext;
+
+ zone_tree_it_free(&axfr->it);
+ ptrlist_free(&axfr->proc.nodes, qdata->mm);
+ mm_free(qdata->mm, axfr);
+
+ /* Allow zone changes (finished). */
+ rcu_read_unlock();
+}
+
+static int axfr_query_check(knotd_qdata_t *qdata)
+{
+ NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
+ NS_NEED_AUTH(qdata, ACL_ACTION_TRANSFER);
+ NS_NEED_ZONE_CONTENTS(qdata);
+
+ return KNOT_STATE_DONE;
+}
+
+static int axfr_query_init(knotd_qdata_t *qdata)
+{
+ assert(qdata);
+
+ /* Check AXFR query validity. */
+ if (axfr_query_check(qdata) == KNOT_STATE_FAIL) {
+ if (qdata->rcode == KNOT_RCODE_FORMERR) {
+ return KNOT_EMALF;
+ } else {
+ return KNOT_EDENIED;
+ }
+ }
+
+ if (zone_get_flag(qdata->extra->zone, ZONE_XFR_FROZEN, false)) {
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOT_READY;
+ return KNOT_EAGAIN;
+ }
+
+ /* Create transfer processing context. */
+ knot_mm_t *mm = qdata->mm;
+ struct axfr_proc *axfr = mm_alloc(mm, sizeof(struct axfr_proc));
+ if (axfr == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(axfr, 0, sizeof(struct axfr_proc));
+ init_list(&axfr->proc.nodes);
+
+ /* Put data to process. */
+ xfr_stats_begin(&axfr->proc.stats);
+ const zone_contents_t *contents = qdata->extra->contents;
+ /* Must be non-NULL for the first message. */
+ assert(contents);
+ ptrlist_add(&axfr->proc.nodes, contents->nodes, mm);
+ /* Put NSEC3 data if exists. */
+ if (!zone_tree_is_empty(contents->nsec3_nodes)) {
+ ptrlist_add(&axfr->proc.nodes, contents->nsec3_nodes, mm);
+ }
+
+ /* Set up cleanup callback. */
+ qdata->extra->ext = axfr;
+ qdata->extra->ext_cleanup = &axfr_query_cleanup;
+
+ /* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */
+ rcu_read_lock();
+
+ return KNOT_EOK;
+}
+
+int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt == NULL || qdata == NULL) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* AXFR over UDP isn't allowed, respond with NOTIMPL. */
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) {
+ qdata->rcode = KNOT_RCODE_NOTIMPL;
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Initialize on first call. */
+ struct axfr_proc *axfr = qdata->extra->ext;
+ if (axfr == NULL) {
+ int ret = axfr_query_init(qdata);
+ axfr = qdata->extra->ext;
+ switch (ret) {
+ case KNOT_EOK: /* OK */
+ AXFROUT_LOG(LOG_INFO, qdata, "started, serial %u",
+ zone_contents_serial(qdata->extra->contents));
+ break;
+ case KNOT_EDENIED: /* Not authorized, already logged. */
+ return KNOT_STATE_FAIL;
+ case KNOT_EMALF: /* Malformed query. */
+ AXFROUT_LOG(LOG_DEBUG, qdata, "malformed query");
+ return KNOT_STATE_FAIL;
+ case KNOT_EAGAIN: /* Outgoing AXFR temporarily disabled. */
+ AXFROUT_LOG(LOG_INFO, qdata, "outgoing AXFR frozen");
+ return KNOT_STATE_FAIL;
+ default:
+ AXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)",
+ knot_strerror(ret));
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ /* Reserve space for TSIG. */
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Answer current packet (or continue). */
+ ret = xfr_process_list(pkt, &axfr_process_node_tree, qdata);
+ switch (ret) {
+ case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */
+ return KNOT_STATE_PRODUCE; /* Check for more. */
+ case KNOT_EOK: /* Last response. */
+ xfr_stats_end(&axfr->proc.stats);
+ xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_AXFR, LOG_DIRECTION_OUT,
+ REMOTE(qdata), false, &axfr->proc.stats);
+ return KNOT_STATE_DONE;
+ default: /* Generic error. */
+ AXFROUT_LOG(LOG_ERR, qdata, "failed (%s)", knot_strerror(ret));
+ return KNOT_STATE_FAIL;
+ }
+}
diff --git a/src/knot/nameserver/axfr.h b/src/knot/nameserver/axfr.h
new file mode 100644
index 0000000..81fcad8
--- /dev/null
+++ b/src/knot/nameserver/axfr.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "knot/nameserver/process_query.h"
+#include "libknot/packet/pkt.h"
+
+/*!
+ * \brief Process an AXFR query message.
+ *
+ * \return KNOT_STATE_* processing states
+ */
+int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/chaos.c b/src/knot/nameserver/chaos.c
new file mode 100644
index 0000000..b83e2f5
--- /dev/null
+++ b/src/knot/nameserver/chaos.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 2018 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 <strings.h>
+#include <stdlib.h>
+
+#include "knot/nameserver/chaos.h"
+#include "knot/conf/conf.h"
+#include "libknot/libknot.h"
+
+#define WISH "Knot DNS developers wish you "
+#define HOPE "Knot DNS developers hope you "
+
+static const char *wishes[] = {
+ HOPE "have all your important life questions answered without SERVFAIL.",
+ WISH "many wonderful people in your domain.",
+ WISH "non-empty lymph nodes.",
+ HOPE "resolve the . of your problems.",
+ WISH "long enough TTL.",
+ HOPE "become authoritative master in your domain.",
+ HOPE "always find useful PTR in CHAOS.",
+ "Canonical name is known to both DNS experts and Ubuntu users.",
+ HOPE "never forget both your name and address.",
+ "Don't fix broken CNAME chains with glue!",
+ WISH "no Additional section in your TODO list.",
+ HOPE "won't find surprising news in today's journal.",
+ HOPE "perform rollover often just when playing roulette.",
+ HOPE "get notified before your domain registration expires.",
+};
+
+#undef WISH
+#undef HOPE
+
+static const char *get_txt_response_string(knot_pkt_t *response)
+{
+ char qname[32];
+ if (knot_dname_to_str(qname, knot_pkt_qname(response), sizeof(qname)) == NULL) {
+ return NULL;
+ }
+
+ const char *response_str = NULL;
+
+ /* Allow hostname.bind. for compatibility. */
+ if (strcasecmp("id.server.", qname) == 0 ||
+ strcasecmp("hostname.bind.", qname) == 0) {
+ conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
+ if (val.code == KNOT_EOK) {
+ response_str = conf_str(&val); // Can be NULL!
+ } else {
+ response_str = conf()->hostname;
+ }
+ /* Allow version.bind. for compatibility. */
+ } else if (strcasecmp("version.server.", qname) == 0 ||
+ strcasecmp("version.bind.", qname) == 0) {
+ conf_val_t val = conf_get(conf(), C_SRV, C_VERSION);
+ if (val.code == KNOT_EOK) {
+ response_str = conf_str(&val); // Can be NULL!
+ } else {
+ response_str = "Knot DNS " PACKAGE_VERSION;
+ }
+ } else if (strcasecmp("fortune.", qname) == 0) {
+ conf_val_t val = conf_get(conf(), C_SRV, C_VERSION);
+ if (val.code != KNOT_EOK) {
+ uint16_t wishno = knot_wire_get_id(response->wire) %
+ (sizeof(wishes) / sizeof(wishes[0]));
+ response_str = wishes[wishno];
+ }
+ }
+
+ return response_str;
+}
+
+static int create_txt_rrset(knot_rrset_t *rrset, const knot_dname_t *owner,
+ const char *response_str, knot_mm_t *mm)
+{
+ /* Truncate response to one TXT label. */
+ size_t response_len = strlen(response_str);
+ if (response_len > UINT8_MAX) {
+ response_len = UINT8_MAX;
+ }
+
+ knot_dname_t *rowner = knot_dname_copy(owner, mm);
+ if (rowner == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_init(rrset, rowner, KNOT_RRTYPE_TXT, KNOT_CLASS_CH, 0);
+ uint8_t rdata[response_len + 1];
+
+ rdata[0] = response_len;
+ memcpy(&rdata[1], response_str, response_len);
+
+ int ret = knot_rrset_add_rdata(rrset, rdata, response_len + 1, mm);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(rrset->owner, mm);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int answer_txt(knot_pkt_t *response)
+{
+ const char *response_str = get_txt_response_string(response);
+ if (response_str == NULL || response_str[0] == '\0') {
+ return KNOT_RCODE_REFUSED;
+ }
+
+ knot_rrset_t rrset;
+ int ret = create_txt_rrset(&rrset, knot_pkt_qname(response),
+ response_str, &response->mm);
+ if (ret != KNOT_EOK) {
+ return KNOT_RCODE_SERVFAIL;
+ }
+
+ int result = knot_pkt_put(response, 0, &rrset, KNOT_PF_FREE);
+ if (result != KNOT_EOK) {
+ knot_rrset_clear(&rrset, &response->mm);
+ return KNOT_RCODE_SERVFAIL;
+ }
+
+ return KNOT_RCODE_NOERROR;
+}
+
+int knot_chaos_answer(knot_pkt_t *pkt)
+{
+ if (knot_pkt_qtype(pkt) != KNOT_RRTYPE_TXT) {
+ return KNOT_RCODE_REFUSED;
+ }
+
+ return answer_txt(pkt);
+}
diff --git a/src/knot/nameserver/chaos.h b/src/knot/nameserver/chaos.h
new file mode 100644
index 0000000..f875abe
--- /dev/null
+++ b/src/knot/nameserver/chaos.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+
+/*!
+ * \brief Create a response for a given query in the CHAOS class.
+ */
+int knot_chaos_answer(knot_pkt_t *pkt);
diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c
new file mode 100644
index 0000000..51bde97
--- /dev/null
+++ b/src/knot/nameserver/internet.c
@@ -0,0 +1,728 @@
+/* Copyright (C) 2022 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 "libknot/libknot.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/nsec_proofs.h"
+#include "knot/nameserver/query_module.h"
+#include "knot/zone/serial.h"
+#include "contrib/mempattern.h"
+
+/*! \brief Check if given node was already visited. */
+static int wildcard_has_visited(knotd_qdata_t *qdata, const zone_node_t *node)
+{
+ struct wildcard_hit *item;
+ WALK_LIST(item, qdata->extra->wildcards) {
+ if (item->node == node) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*! \brief Mark given node as visited. */
+static int wildcard_visit(knotd_qdata_t *qdata, const zone_node_t *node,
+ const zone_node_t *prev, const knot_dname_t *sname)
+{
+ assert(qdata);
+ assert(node);
+
+ if (node->flags & NODE_FLAGS_NONAUTH) {
+ return KNOT_EOK;
+ }
+
+ knot_mm_t *mm = qdata->mm;
+ struct wildcard_hit *item = mm_alloc(mm, sizeof(struct wildcard_hit));
+ item->node = node;
+ item->prev = prev;
+ item->sname = sname;
+ add_tail(&qdata->extra->wildcards, (node_t *)item);
+ return KNOT_EOK;
+}
+
+/*! \brief Synthesizes a CNAME RR from a DNAME. */
+static int dname_cname_synth(const knot_rrset_t *dname_rr,
+ const knot_dname_t *qname,
+ knot_rrset_t *cname_rrset,
+ knot_mm_t *mm)
+{
+ if (cname_rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+ knot_dname_t *owner_copy = knot_dname_copy(qname, mm);
+ if (owner_copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_init(cname_rrset, owner_copy, KNOT_RRTYPE_CNAME, dname_rr->rclass,
+ dname_rr->ttl);
+
+ /* Replace last labels of qname with DNAME. */
+ const knot_dname_t *dname_wire = dname_rr->owner;
+ const knot_dname_t *dname_tgt = knot_dname_target(dname_rr->rrs.rdata);
+ size_t labels = knot_dname_labels(dname_wire, NULL);
+ knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt, mm);
+ if (cname == NULL) {
+ knot_dname_free(owner_copy, mm);
+ return KNOT_ENOMEM;
+ }
+
+ /* Store DNAME into RDATA. */
+ size_t cname_size = knot_dname_size(cname);
+ uint8_t cname_rdata[cname_size];
+ memcpy(cname_rdata, cname, cname_size);
+ knot_dname_free(cname, mm);
+
+ int ret = knot_rrset_add_rdata(cname_rrset, cname_rdata, cname_size, mm);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(owner_copy, mm);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Checks if the name created by replacing the owner of \a dname_rrset
+ * in the \a qname by the DNAME's target would be longer than allowed.
+ */
+static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname_t *qname)
+{
+ if (knot_dname_labels(qname, NULL) - knot_dname_labels(rrset->owner, NULL) +
+ knot_dname_labels(knot_dname_target(rrset->rrs.rdata), NULL) > KNOT_DNAME_MAXLABELS) {
+ return true;
+ } else if (knot_dname_size(qname) - knot_dname_size(rrset->owner) +
+ knot_dname_size(knot_dname_target(rrset->rrs.rdata)) > KNOT_DNAME_MAXLEN) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*! \brief DNSSEC both requested & available. */
+static bool have_dnssec(knotd_qdata_t *qdata)
+{
+ return knot_pkt_has_dnssec(qdata->query) &&
+ qdata->extra->contents->dnssec;
+}
+
+/*! \brief This is a wildcard-covered or any other terminal node for QNAME.
+ * e.g. positive answer.
+ */
+static int put_answer(knot_pkt_t *pkt, uint16_t type, knotd_qdata_t *qdata)
+{
+ /* Wildcard expansion or exact match, either way RRSet owner is
+ * is QNAME. We can fake name synthesis by setting compression hint to
+ * QNAME position. Just need to check if we're answering QNAME and not
+ * a CNAME target.
+ */
+ uint16_t compr_hint = KNOT_COMPR_HINT_NONE;
+ if (pkt->rrset_count == 0) { /* Guaranteed first answer. */
+ compr_hint = KNOT_COMPR_HINT_QNAME;
+ }
+
+ unsigned put_rr_flags = (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) ?
+ KNOT_PF_NULL : KNOT_PF_NOTRUNC;
+ put_rr_flags |= KNOT_PF_ORIGTTL;
+
+ knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
+ knot_rrset_t rrset;
+ switch (type) {
+ case KNOT_RRTYPE_ANY: /* Put one RRSet, not all. */
+ rrset = node_rrset_at(qdata->extra->node, 0);
+ break;
+ case KNOT_RRTYPE_RRSIG: /* Put some RRSIGs, not all. */
+ if (!knot_rrset_empty(&rrsigs)) {
+ knot_rrset_init(&rrset, rrsigs.owner, rrsigs.type, rrsigs.rclass, rrsigs.ttl);
+ int ret = knot_synth_rrsig(KNOT_RRTYPE_ANY, &rrsigs.rrs, &rrset.rrs, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ knot_rrset_init_empty(&rrset);
+ }
+ break;
+ default: /* Single RRSet of given type. */
+ rrset = node_rrset(qdata->extra->node, type);
+ break;
+ }
+
+ if (knot_rrset_empty(&rrset)) {
+ return KNOT_EOK;
+ }
+
+ return process_query_put_rr(pkt, qdata, &rrset, &rrsigs, compr_hint, put_rr_flags);
+}
+
+/*! \brief Puts optional SOA RRSet to the Authority section of the response. */
+static int put_authority_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ const zone_contents_t *zone)
+{
+ knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA);
+ knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG);
+ return process_query_put_rr(pkt, qdata, &soa, &rrsigs,
+ KNOT_COMPR_HINT_NONE,
+ KNOT_PF_NOTRUNC | KNOT_PF_SOAMINTTL);
+}
+
+/*! \brief Put the delegation NS RRSet to the Authority section. */
+static int put_delegation(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ /* Find closest delegation point. */
+ while (!(qdata->extra->node->flags & NODE_FLAGS_DELEG)) {
+ qdata->extra->node = node_parent(qdata->extra->node);
+ }
+
+ /* Insert NS record. */
+ knot_rrset_t rrset = node_rrset(qdata->extra->node, KNOT_RRTYPE_NS);
+ knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
+ return process_query_put_rr(pkt, qdata, &rrset, &rrsigs,
+ KNOT_COMPR_HINT_NONE, 0);
+}
+
+static int put_nsec3_bitmap(const zone_node_t *for_node, knot_pkt_t *pkt,
+ knotd_qdata_t *qdata, uint32_t flags)
+{
+ const zone_node_t *node = node_nsec3_get(for_node);
+ if (node == NULL) {
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t nsec3 = node_rrset(node, KNOT_RRTYPE_NSEC3);
+ if (knot_rrset_empty(&nsec3)) {
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t rrsig = node_rrset(node, KNOT_RRTYPE_RRSIG);
+ return process_query_put_rr(pkt, qdata, &nsec3, &rrsig,
+ KNOT_COMPR_HINT_NONE, flags);
+}
+
+/*! \brief Put additional records for given RR. */
+static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr,
+ knotd_qdata_t *qdata, knot_rrinfo_t *info, int state)
+{
+ if (rr->additional == NULL) {
+ return KNOT_EOK;
+ }
+
+ /* Valid types for ADDITIONALS insertion. */
+ /* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */
+ static uint16_t ar_type_list[] = { KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA, KNOT_RRTYPE_SVCB };
+ static const int ar_type_count_default = 2;
+
+ int ret = KNOT_EOK;
+
+ additional_t *additional = (additional_t *)rr->additional;
+
+ /* Iterate over the additionals. */
+ for (uint16_t i = 0; i < additional->count; i++) {
+ glue_t *glue = &additional->glues[i];
+ uint32_t flags = KNOT_PF_NULL;
+
+ /* Optional glue doesn't cause truncation. (RFC 1034/4.3.2 step 3b). */
+ if (state != KNOTD_IN_STATE_DELEG || glue->optional) {
+ flags |= KNOT_PF_NOTRUNC;
+ }
+
+ int ar_type_count = ar_type_count_default, ar_present = 0;
+ if (rr->type == KNOT_RRTYPE_SVCB || rr->type == KNOT_RRTYPE_HTTPS) {
+ ar_type_list[ar_type_count++] = rr->type;
+ }
+
+ uint16_t hint = knot_compr_hint(info, KNOT_COMPR_HINT_RDATA +
+ glue->ns_pos);
+ const zone_node_t *gluenode = glue_node(glue, qdata->extra->node);
+ knot_rrset_t rrsigs = node_rrset(gluenode, KNOT_RRTYPE_RRSIG);
+ for (int k = 0; k < ar_type_count; ++k) {
+ knot_rrset_t rrset = node_rrset(gluenode, ar_type_list[k]);
+ if (knot_rrset_empty(&rrset)) {
+ continue;
+ }
+ ret = process_query_put_rr(pkt, qdata, &rrset, &rrsigs,
+ hint, flags);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ ar_present++;
+ }
+
+ if ((rr->type == KNOT_RRTYPE_SVCB || rr->type == KNOT_RRTYPE_HTTPS) &&
+ ar_present < ar_type_count && have_dnssec(qdata)) {
+ // it would be nicer to have this in solve_additional_dnssec, but
+ // it seems infeasible to transfer all the context there
+
+ // adding an NSEC(3) record proving non-existence of some of the
+ // glue with its bitmap
+ if (knot_is_nsec3_enabled(qdata->extra->contents)) {
+ ret = put_nsec3_bitmap(gluenode, pkt, qdata, flags);
+ } else {
+ knot_rrset_t nsec = node_rrset(gluenode, KNOT_RRTYPE_NSEC);
+ if (!knot_rrset_empty(&nsec)) {
+ ret = process_query_put_rr(pkt, qdata, &nsec, &rrsigs,
+ KNOT_COMPR_HINT_NONE, flags);
+ }
+ }
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata)
+{
+ /* CNAME chain processing limit. */
+ if (++qdata->extra->cname_chain > CNAME_CHAIN_MAX) {
+ qdata->extra->node = NULL;
+ return KNOTD_IN_STATE_HIT;
+ }
+
+ const zone_node_t *cname_node = qdata->extra->node;
+ knot_rrset_t cname_rr = node_rrset(qdata->extra->node, rrtype);
+ knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
+
+ assert(!knot_rrset_empty(&cname_rr));
+
+ /* Check whether RR is already in the packet. */
+ uint16_t flags = KNOT_PF_CHECKDUP;
+
+ /* Now, try to put CNAME to answer. */
+ uint16_t rr_count_before = pkt->rrset_count;
+ int ret = process_query_put_rr(pkt, qdata, &cname_rr, &rrsigs, 0, flags);
+ switch (ret) {
+ case KNOT_EOK: break;
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
+ default: return KNOTD_IN_STATE_ERROR;
+ }
+
+ /* Synthesize CNAME if followed DNAME. */
+ if (rrtype == KNOT_RRTYPE_DNAME) {
+ if (dname_cname_cannot_synth(&cname_rr, qdata->name)) {
+ qdata->rcode = KNOT_RCODE_YXDOMAIN;
+ } else {
+ knot_rrset_t dname_rr = cname_rr;
+ ret = dname_cname_synth(&dname_rr, qdata->name,
+ &cname_rr, &pkt->mm);
+ if (ret != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ return KNOTD_IN_STATE_ERROR;
+ }
+ ret = process_query_put_rr(pkt, qdata, &cname_rr, NULL, 0, KNOT_PF_FREE);
+ switch (ret) {
+ case KNOT_EOK: break;
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
+ default: return KNOTD_IN_STATE_ERROR;
+ }
+ if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_CNAME) {
+ /* Synthesized CNAME is a perfect answer to query. */
+ return KNOTD_IN_STATE_HIT;
+ }
+ }
+ }
+
+ /* Check if RR count increased. */
+ if (pkt->rrset_count <= rr_count_before) {
+ qdata->extra->node = NULL; /* Act as if the name leads to nowhere. */
+ return KNOTD_IN_STATE_HIT;
+ }
+
+ /* If node is a wildcard, follow only if we didn't visit the same node
+ * earlier, as that would mean a CNAME loop. */
+ if (knot_dname_is_wildcard(cname_node->owner)) {
+
+ /* Check if is not in wildcard nodes (loop). */
+ if (wildcard_has_visited(qdata, cname_node)) {
+ qdata->extra->node = NULL; /* Act as if the name leads to nowhere. */
+
+ if (wildcard_visit(qdata, cname_node, qdata->extra->previous, qdata->name) != KNOT_EOK) { // in case of loop, re-add this cname_node because it might have different qdata->name
+ return KNOTD_IN_STATE_ERROR;
+ }
+ return KNOTD_IN_STATE_HIT;
+ }
+
+ /* Put to wildcard node list. */
+ if (wildcard_visit(qdata, cname_node, qdata->extra->previous, qdata->name) != KNOT_EOK) {
+ return KNOTD_IN_STATE_ERROR;
+ }
+ }
+
+ /* Now follow the next CNAME TARGET. */
+ qdata->name = knot_cname_name(cname_rr.rrs.rdata);
+
+ return KNOTD_IN_STATE_FOLLOW;
+}
+
+static int name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ uint16_t qtype = knot_pkt_qtype(pkt);
+
+ /* DS query at DP is answered normally, but everything else at/below DP
+ * triggers referral response. */
+ if (((qdata->extra->node->flags & NODE_FLAGS_DELEG) && qtype != KNOT_RRTYPE_DS) ||
+ (qdata->extra->node->flags & NODE_FLAGS_NONAUTH)) {
+ return KNOTD_IN_STATE_DELEG;
+ }
+
+ if (node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME)
+ && qtype != KNOT_RRTYPE_CNAME
+ && qtype != KNOT_RRTYPE_RRSIG
+ && qtype != KNOT_RRTYPE_NSEC
+ && qtype != KNOT_RRTYPE_ANY) {
+ return follow_cname(pkt, KNOT_RRTYPE_CNAME, qdata);
+ }
+
+ uint16_t old_rrcount = pkt->rrset_count;
+ int ret = put_answer(pkt, qtype, qdata);
+ if (ret != KNOT_EOK) {
+ if (ret == KNOT_ESPACE && (qdata->params->proto == KNOTD_QUERY_PROTO_UDP)) {
+ return KNOTD_IN_STATE_TRUNC;
+ } else {
+ return KNOTD_IN_STATE_ERROR;
+ }
+ }
+
+ /* Check for NODATA (=0 RRs added). */
+ if (old_rrcount == pkt->rrset_count) {
+ return KNOTD_IN_STATE_NODATA;
+ } else {
+ return KNOTD_IN_STATE_HIT;
+ }
+}
+
+static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ /* Name is covered by wildcard. */
+ if (qdata->extra->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) {
+ /* Find wildcard child in the zone. */
+ const zone_node_t *wildcard_node =
+ zone_contents_find_wildcard_child(
+ qdata->extra->contents, qdata->extra->encloser);
+
+ qdata->extra->node = wildcard_node;
+ assert(qdata->extra->node != NULL);
+
+ /* Follow expanded wildcard. */
+ int next_state = name_found(pkt, qdata);
+
+ /* Put to wildcard node list. */
+ if (wildcard_has_visited(qdata, wildcard_node)) {
+ return next_state;
+ }
+ if (wildcard_visit(qdata, wildcard_node, qdata->extra->previous, qdata->name) != KNOT_EOK) {
+ next_state = KNOTD_IN_STATE_ERROR;
+ }
+
+ return next_state;
+ }
+
+ /* Name is under DNAME, use it for substitution. */
+ bool encloser_auth = !(qdata->extra->encloser->flags & (NODE_FLAGS_NONAUTH | NODE_FLAGS_DELEG));
+ knot_rrset_t dname_rrset = node_rrset(qdata->extra->encloser, KNOT_RRTYPE_DNAME);
+ if (encloser_auth && !knot_rrset_empty(&dname_rrset)) {
+ qdata->extra->node = qdata->extra->encloser; /* Follow encloser as new node. */
+ return follow_cname(pkt, KNOT_RRTYPE_DNAME, qdata);
+ }
+
+ /* Look up an authoritative encloser or its parent. */
+ const zone_node_t *node = qdata->extra->encloser;
+ while (node->rrset_count == 0 || node->flags & NODE_FLAGS_NONAUTH) {
+ node = node_parent(node);
+ assert(node);
+ }
+
+ /* Name is below delegation. */
+ if ((node->flags & NODE_FLAGS_DELEG)) {
+ qdata->extra->node = node;
+ return KNOTD_IN_STATE_DELEG;
+ }
+
+ return KNOTD_IN_STATE_MISS;
+}
+
+static int solve_name(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ int ret = zone_contents_find_dname(qdata->extra->contents, qdata->name,
+ &qdata->extra->node, &qdata->extra->encloser,
+ &qdata->extra->previous);
+
+ switch (ret) {
+ case ZONE_NAME_FOUND:
+ return name_found(pkt, qdata);
+ case ZONE_NAME_NOT_FOUND:
+ return name_not_found(pkt, qdata);
+ case KNOT_EOUTOFZONE:
+ assert(state == KNOTD_IN_STATE_FOLLOW); /* CNAME/DNAME chain only. */
+ return KNOTD_IN_STATE_HIT;
+ default:
+ return KNOTD_IN_STATE_ERROR;
+ }
+}
+
+static int solve_answer(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+{
+ int old_state = state;
+
+ /* Do not solve if already solved, e.g. in a module. */
+ if (state == KNOTD_IN_STATE_HIT) {
+ return state;
+ }
+
+ /* Get answer to QNAME. */
+ state = solve_name(state, pkt, qdata);
+
+ /* Promote NODATA from a module if nothing found in zone. */
+ if (state == KNOTD_IN_STATE_MISS && old_state == KNOTD_IN_STATE_NODATA) {
+ state = old_state;
+ }
+
+ /* Is authoritative answer unless referral.
+ * Must check before we chase the CNAME chain. */
+ if (state != KNOTD_IN_STATE_DELEG) {
+ knot_wire_set_aa(pkt->wire);
+ }
+
+ /* Additional resolving for CNAME/DNAME chain. */
+ while (state == KNOTD_IN_STATE_FOLLOW) {
+ state = solve_name(state, pkt, qdata);
+ }
+
+ return state;
+}
+
+static int solve_answer_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+{
+ /* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */
+ int ret = nsec_append_rrsigs(pkt, qdata, false);
+ switch (ret) {
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
+ case KNOT_EOK: return state;
+ default: return KNOTD_IN_STATE_ERROR;
+ }
+}
+
+static int solve_authority(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+{
+ int ret = KNOT_ERROR;
+ const zone_contents_t *zone_contents = qdata->extra->contents;
+
+ switch (state) {
+ case KNOTD_IN_STATE_HIT: /* Positive response. */
+ ret = KNOT_EOK;
+ break;
+ case KNOTD_IN_STATE_MISS: /* MISS, set NXDOMAIN RCODE. */
+ qdata->rcode = KNOT_RCODE_NXDOMAIN;
+ ret = put_authority_soa(pkt, qdata, zone_contents);
+ break;
+ case KNOTD_IN_STATE_NODATA: /* NODATA append AUTHORITY SOA. */
+ ret = put_authority_soa(pkt, qdata, zone_contents);
+ break;
+ case KNOTD_IN_STATE_DELEG: /* Referral response. */
+ ret = put_delegation(pkt, qdata);
+ break;
+ case KNOTD_IN_STATE_TRUNC: /* Truncated ANSWER. */
+ ret = KNOT_ESPACE;
+ break;
+ case KNOTD_IN_STATE_ERROR: /* Error resolving ANSWER. */
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ /* Evaluate final state. */
+ switch (ret) {
+ case KNOT_EOK: return state; /* Keep current state. */
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
+ default: return KNOTD_IN_STATE_ERROR; /* Error. */
+ }
+}
+
+static int solve_authority_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+{
+ int ret = KNOT_ERROR;
+
+ /* Authenticated denial of existence. */
+ switch (state) {
+ case KNOTD_IN_STATE_HIT: ret = KNOT_EOK; break;
+ case KNOTD_IN_STATE_MISS: ret = nsec_prove_nxdomain(pkt, qdata); break;
+ case KNOTD_IN_STATE_NODATA: ret = nsec_prove_nodata(pkt, qdata); break;
+ case KNOTD_IN_STATE_DELEG: ret = nsec_prove_dp_security(pkt, qdata); break;
+ case KNOTD_IN_STATE_TRUNC: ret = KNOT_ESPACE; break;
+ case KNOTD_IN_STATE_ERROR: ret = KNOT_ERROR; break;
+ default:
+ assert(0);
+ break;
+ }
+
+ /* RFC4035 3.1.3 Prove visited wildcards.
+ * Wildcard expansion applies for Name Error, Wildcard Answer and
+ * No Data proofs if at one point the search expanded a wildcard node. */
+ if (ret == KNOT_EOK) {
+ ret = nsec_prove_wildcards(pkt, qdata);
+ }
+
+ /* RFC4035, section 3.1 RRSIGs for RRs in AUTHORITY are mandatory. */
+ if (ret == KNOT_EOK) {
+ ret = nsec_append_rrsigs(pkt, qdata, false);
+ }
+
+ /* Evaluate final state. */
+ switch (ret) {
+ case KNOT_EOK: return state; /* Keep current state. */
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
+ default: return KNOTD_IN_STATE_ERROR; /* Error. */
+ }
+}
+
+static int solve_additional(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ void *ctx)
+{
+ int ret = KNOT_EOK, rrset_count = pkt->rrset_count;
+
+ /* Scan all RRs in ANSWER/AUTHORITY. */
+ for (int i = 0; i < rrset_count; ++i) {
+ knot_rrset_t *rr = &pkt->rr[i];
+ knot_rrinfo_t *info = &pkt->rr_info[i];
+
+ /* Skip types for which it doesn't apply. */
+ if (!knot_rrtype_additional_needed(rr->type)) {
+ continue;
+ }
+
+ /* Put additional records for given type. */
+ ret = put_additional(pkt, rr, qdata, info, state);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ /* Evaluate final state. */
+ switch (ret) {
+ case KNOT_EOK: return state; /* Keep current state. */
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */
+ default: return KNOTD_IN_STATE_ERROR; /* Error. */
+ }
+}
+
+static int solve_additional_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx)
+{
+ /* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */
+ int ret = nsec_append_rrsigs(pkt, qdata, true);
+ switch (ret) {
+ case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC;
+ case KNOT_EOK: return state;
+ default: return KNOTD_IN_STATE_ERROR;
+ }
+}
+
+/*! \brief Helper for internet_query repetitive code. */
+#define SOLVE_STEP(solver, state, context) \
+ state = (solver)(state, pkt, qdata, context); \
+ if (state == KNOTD_IN_STATE_TRUNC) { \
+ return KNOT_STATE_DONE; \
+ } else if (state == KNOTD_IN_STATE_ERROR) { \
+ return KNOT_STATE_FAIL; \
+ }
+
+static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ int state = KNOTD_IN_STATE_BEGIN;
+ struct query_plan *plan = qdata->extra->zone->query_plan;
+ struct query_step *step;
+
+ bool with_dnssec = have_dnssec(qdata);
+
+ /* Resolve PREANSWER. */
+ if (plan != NULL) {
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_PREANSWER]) {
+ SOLVE_STEP(step->process, state, step->ctx);
+ }
+ }
+
+ /* Resolve ANSWER. */
+ knot_pkt_begin(pkt, KNOT_ANSWER);
+ SOLVE_STEP(solve_answer, state, NULL);
+ if (with_dnssec) {
+ SOLVE_STEP(solve_answer_dnssec, state, NULL);
+ }
+ if (plan != NULL) {
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_ANSWER]) {
+ SOLVE_STEP(step->process, state, step->ctx);
+ }
+ }
+
+ /* Resolve AUTHORITY. */
+ knot_pkt_begin(pkt, KNOT_AUTHORITY);
+ SOLVE_STEP(solve_authority, state, NULL);
+ if (with_dnssec) {
+ SOLVE_STEP(solve_authority_dnssec, state, NULL);
+ }
+ if (plan != NULL) {
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_AUTHORITY]) {
+ SOLVE_STEP(step->process, state, step->ctx);
+ }
+ }
+
+ /* Resolve ADDITIONAL. */
+ knot_pkt_begin(pkt, KNOT_ADDITIONAL);
+ SOLVE_STEP(solve_additional, state, NULL);
+ if (with_dnssec) {
+ SOLVE_STEP(solve_additional_dnssec, state, NULL);
+ }
+ if (plan != NULL) {
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_ADDITIONAL]) {
+ SOLVE_STEP(step->process, state, step->ctx);
+ }
+ }
+
+ /* Write resulting RCODE. */
+ knot_wire_set_rcode(pkt->wire, qdata->rcode);
+
+ return KNOT_STATE_DONE;
+}
+
+int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt == NULL || qdata == NULL) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Check if valid zone. */
+ NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED);
+
+ /* Check if a TSIG is present. */
+ if (knot_pkt_has_tsig(qdata->query)) {
+ NS_NEED_AUTH(qdata, ACL_ACTION_QUERY);
+
+ /* Reserve space for TSIG. */
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ /* Check if the zone is not empty or expired. */
+ NS_NEED_ZONE_CONTENTS(qdata);
+
+ /* Get answer to QNAME. */
+ qdata->name = knot_pkt_qname(qdata->query);
+
+ return answer_query(pkt, qdata);
+}
diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h
new file mode 100644
index 0000000..52afe62
--- /dev/null
+++ b/src/knot/nameserver/internet.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "knot/include/module.h"
+#include "knot/nameserver/process_query.h"
+
+/*! \brief Don't follow CNAME/DNAME chain beyond this depth. */
+#define CNAME_CHAIN_MAX 5
+
+/*!
+ * \brief Answer query from an IN class zone.
+ *
+ * \retval KNOT_STATE_FAIL if it encountered an error.
+ * \retval KNOT_STATE_DONE if finished.
+ */
+int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*! \brief Require given QUERY TYPE or return error code. */
+#define NS_NEED_QTYPE(qdata, qtype_want, error_rcode) \
+ if (knot_pkt_qtype((qdata)->query) != (qtype_want)) { \
+ qdata->rcode = (error_rcode); \
+ return KNOT_STATE_FAIL; \
+ }
+
+/*! \brief Require given QUERY NAME or return error code. */
+#define NS_NEED_QNAME(qdata, qname_want, error_rcode) \
+ if (!knot_dname_is_equal(knot_pkt_qname((qdata)->query), (qname_want))) { \
+ qdata->rcode = (error_rcode); \
+ return KNOT_STATE_FAIL; \
+ }
+
+/*! \brief Require existing zone or return failure. */
+#define NS_NEED_ZONE(qdata, error_rcode) \
+ if ((qdata)->extra->zone == NULL) { \
+ qdata->rcode = (error_rcode); \
+ if ((error_rcode) == KNOT_RCODE_REFUSED) { \
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOTAUTH; \
+ } \
+ return KNOT_STATE_FAIL; \
+ }
+
+/*! \brief Require existing zone contents or return failure. */
+#define NS_NEED_ZONE_CONTENTS(qdata) \
+ if ((qdata)->extra->contents == NULL) { \
+ qdata->rcode = KNOT_RCODE_SERVFAIL; \
+ qdata->rcode_ede = KNOT_EDNS_EDE_INV_DATA; \
+ return KNOT_STATE_FAIL; \
+ }
+
+/*! \brief Require authentication. */
+#define NS_NEED_AUTH(qdata, action) \
+ if (!process_query_acl_check(conf(), (action), (qdata)) || \
+ process_query_verify(qdata) != KNOT_EOK) { \
+ return KNOT_STATE_FAIL; \
+ }
+
+/*! \brief Require the zone not to be frozen. */
+#define NS_NEED_NOT_FROZEN(qdata) \
+ if ((qdata)->extra->zone->events.ufrozen) { \
+ (qdata)->rcode = KNOT_RCODE_REFUSED; \
+ (qdata)->rcode_ede = KNOT_EDNS_EDE_NOT_READY; \
+ return KNOT_STATE_FAIL; \
+ }
diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c
new file mode 100644
index 0000000..03a9fdf
--- /dev/null
+++ b/src/knot/nameserver/ixfr.c
@@ -0,0 +1,332 @@
+/* Copyright (C) 2023 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 <urcu.h>
+
+#include "contrib/mempattern.h"
+#include "contrib/sockaddr.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/nameserver/axfr.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/ixfr.h"
+#include "knot/nameserver/log.h"
+#include "knot/nameserver/xfr.h"
+#include "knot/zone/serial.h"
+#include "libknot/libknot.h"
+
+#define ZONE_NAME(qdata) knot_pkt_qname((qdata)->query)
+#define REMOTE(qdata) (struct sockaddr *)knotd_qdata_remote_addr(qdata)
+
+#define IXFROUT_LOG(priority, qdata, fmt...) \
+ ns_log(priority, ZONE_NAME(qdata), LOG_OPERATION_IXFR, \
+ LOG_DIRECTION_OUT, REMOTE(qdata), false, fmt)
+
+/*! \brief Helper macro for putting RRs into packet. */
+#define IXFR_SAFE_PUT(pkt, rr) \
+ int ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC | KNOT_PF_ORIGTTL); \
+ if (ret != KNOT_EOK) { \
+ return ret; \
+ }
+
+/*! \brief Puts current RR into packet, stores state for retries. */
+static int ixfr_put_chg_part(knot_pkt_t *pkt, struct ixfr_proc *ixfr,
+ journal_read_t *read)
+{
+ assert(pkt);
+ assert(ixfr);
+ assert(read);
+
+ if (!knot_rrset_empty(&ixfr->cur_rr)) {
+ IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
+ journal_read_clear_rrset(&ixfr->cur_rr);
+ }
+
+ while (journal_read_rrset(read, &ixfr->cur_rr, true)) {
+ if (ixfr->cur_rr.type == KNOT_RRTYPE_SOA) {
+ ixfr->in_remove_section = !ixfr->in_remove_section;
+
+ if (ixfr->in_remove_section) {
+ if (knot_soa_serial(ixfr->cur_rr.rrs.rdata) == ixfr->soa_to) {
+ break;
+ }
+ } else {
+ ixfr->soa_last = knot_soa_serial(ixfr->cur_rr.rrs.rdata);
+ }
+ }
+
+ if (pkt->size > KNOT_WIRE_PTR_MAX) {
+ // optimization: once the XFR DNS message is > 16 KiB, compression
+ // is limited. Better wrap to next message.
+ return KNOT_ESPACE;
+ }
+
+ IXFR_SAFE_PUT(pkt, &ixfr->cur_rr);
+ journal_read_clear_rrset(&ixfr->cur_rr);
+ }
+
+ return journal_read_get_error(read, KNOT_EOK);
+}
+
+/*!
+ * \brief Process the changes from journal.
+ * \note Keep in mind that this function must be able to resume processing,
+ * for example if it fills a packet and returns ESPACE, it is called again
+ * with next empty answer and it must resume the processing exactly where
+ * it's left off.
+ */
+static int ixfr_process_journal(knot_pkt_t *pkt, const void *item,
+ struct xfr_proc *xfer)
+{
+ int ret = KNOT_EOK;
+ struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer;
+ journal_read_t *read = (journal_read_t *)item;
+
+ ret = ixfr_put_chg_part(pkt, ixfr, read);
+
+ return ret;
+}
+
+#undef IXFR_SAFE_PUT
+
+static int ixfr_load_chsets(journal_read_t **journal_read, zone_t *zone,
+ const zone_contents_t *contents, const knot_rrset_t *their_soa)
+{
+ assert(journal_read);
+ assert(zone);
+
+ /* Compare serials. */
+ uint32_t serial_to = zone_contents_serial(contents), j_serial_to;
+ uint32_t serial_from = knot_soa_serial(their_soa->rrs.rdata);
+ if (serial_compare(serial_to, serial_from) & SERIAL_MASK_LEQ) { /* We have older/same age zone. */
+ return KNOT_EUPTODATE;
+ }
+
+ zone_journal_t j = zone_journal(zone);
+ bool j_exists = false;
+ int ret = journal_info(j, &j_exists, NULL, NULL, &j_serial_to, NULL, NULL, NULL, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (!j_exists) {
+ return KNOT_ENOENT;
+ }
+
+ // please note that the journal serial_to might differ from zone SOA serial
+ // it is because RCU lock is made at different moment than LMDB txn begin
+ return journal_read_begin(zone_journal(zone), false, serial_from, journal_read);
+}
+
+static int ixfr_query_check(knotd_qdata_t *qdata)
+{
+ NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
+ NS_NEED_AUTH(qdata, ACL_ACTION_TRANSFER);
+ NS_NEED_ZONE_CONTENTS(qdata);
+
+ /* Need SOA authority record. */
+ const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
+ const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
+ if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ return KNOT_STATE_FAIL;
+ }
+ /* SOA needs to match QNAME. */
+ NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR);
+
+ return KNOT_STATE_DONE;
+}
+
+static void ixfr_answer_cleanup(knotd_qdata_t *qdata)
+{
+ struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->extra->ext;
+ knot_mm_t *mm = qdata->mm;
+
+ knot_rrset_clear(&ixfr->cur_rr, NULL);
+ ptrlist_free(&ixfr->proc.nodes, mm);
+ journal_read_end(ixfr->journal_ctx);
+ mm_free(mm, qdata->extra->ext);
+
+ /* Allow zone changes (finished). */
+ rcu_read_unlock();
+}
+
+static int ixfr_answer_init(knotd_qdata_t *qdata, uint32_t *serial_from)
+{
+ assert(qdata);
+
+ if (ixfr_query_check(qdata) == KNOT_STATE_FAIL) {
+ if (qdata->rcode == KNOT_RCODE_FORMERR) {
+ return KNOT_EMALF;
+ } else {
+ return KNOT_EDENIED;
+ }
+ }
+
+ if (zone_get_flag(qdata->extra->zone, ZONE_XFR_FROZEN, false)) {
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ qdata->rcode_ede = KNOT_EDNS_EDE_NOT_READY;
+ return KNOT_EAGAIN;
+ }
+
+ conf_val_t provide = conf_zone_get(conf(), C_PROVIDE_IXFR,
+ qdata->extra->zone->name);
+ if (!conf_bool(&provide)) {
+ return KNOT_ENOTSUP;
+ }
+
+ const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY);
+ const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0);
+ *serial_from = knot_soa_serial(their_soa->rrs.rdata);
+
+ knot_mm_t *mm = qdata->mm;
+ struct ixfr_proc *xfer = mm_alloc(mm, sizeof(*xfer));
+ if (xfer == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(xfer, 0, sizeof(*xfer));
+
+ int ret = ixfr_load_chsets(&xfer->journal_ctx, (zone_t *)qdata->extra->zone,
+ qdata->extra->contents, their_soa);
+ if (ret != KNOT_EOK) {
+ mm_free(mm, xfer);
+ return ret;
+ }
+
+ xfr_stats_begin(&xfer->proc.stats);
+ xfer->state = IXFR_SOA_DEL;
+ init_list(&xfer->proc.nodes);
+ knot_rrset_init_empty(&xfer->cur_rr);
+ xfer->qdata = qdata;
+
+ ptrlist_add(&xfer->proc.nodes, xfer->journal_ctx, mm);
+
+ xfer->soa_from = knot_soa_serial(their_soa->rrs.rdata);
+ xfer->soa_to = zone_contents_serial(qdata->extra->contents);
+ xfer->soa_last = xfer->soa_from;
+
+ qdata->extra->ext = xfer;
+ qdata->extra->ext_cleanup = &ixfr_answer_cleanup;
+
+ /* No zone changes during multipacket answer (unlocked in ixfr_answer_cleanup) */
+ rcu_read_lock();
+
+ return KNOT_EOK;
+}
+
+static int ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ assert(pkt);
+ assert(qdata);
+
+ /* Check query. */
+ int state = ixfr_query_check(qdata);
+ if (state == KNOT_STATE_FAIL) {
+ return state; /* Malformed query. */
+ }
+
+ /* Reserve space for TSIG. */
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Guaranteed to have zone contents. */
+ const zone_node_t *apex = qdata->extra->contents->apex;
+ knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA);
+ if (knot_rrset_empty(&soa_rr)) {
+ return KNOT_STATE_FAIL;
+ }
+ ret = knot_pkt_put(pkt, 0, &soa_rr, 0);
+ if (ret != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ return KNOT_STATE_FAIL;
+ }
+
+ return KNOT_STATE_DONE;
+}
+
+int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt == NULL || qdata == NULL) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* IXFR over UDP is responded with SOA. */
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) {
+ return ixfr_answer_soa(pkt, qdata);
+ }
+
+ /* Initialize on first call. */
+ struct ixfr_proc *ixfr = qdata->extra->ext;
+ if (ixfr == NULL) {
+ uint32_t soa_from = 0;
+ int ret = ixfr_answer_init(qdata, &soa_from);
+ ixfr = qdata->extra->ext;
+ switch (ret) {
+ case KNOT_EOK: /* OK */
+ IXFROUT_LOG(LOG_INFO, qdata, "started, serial %u -> %u",
+ ixfr->soa_from, ixfr->soa_to);
+ break;
+ case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */
+ IXFROUT_LOG(LOG_INFO, qdata, "zone is up-to-date, serial %u", soa_from);
+ return ixfr_answer_soa(pkt, qdata);
+ case KNOT_ENOTSUP:
+ IXFROUT_LOG(LOG_INFO, qdata, "cannot provide, fallback to AXFR");
+ qdata->type = KNOTD_QUERY_TYPE_AXFR; /* Solve as AXFR. */
+ return axfr_process_query(pkt, qdata);
+ case KNOT_ERANGE: /* No history -> AXFR. */
+ case KNOT_ENOENT:
+ IXFROUT_LOG(LOG_INFO, qdata, "incomplete history, serial %u, fallback to AXFR", soa_from);
+ qdata->type = KNOTD_QUERY_TYPE_AXFR; /* Solve as AXFR. */
+ return axfr_process_query(pkt, qdata);
+ case KNOT_EDENIED: /* Not authorized, already logged. */
+ return KNOT_STATE_FAIL;
+ case KNOT_EMALF: /* Malformed query. */
+ IXFROUT_LOG(LOG_DEBUG, qdata, "malformed query");
+ return KNOT_STATE_FAIL;
+ case KNOT_EAGAIN: /* Outgoing IXFR temporarily disabled. */
+ IXFROUT_LOG(LOG_INFO, qdata, "outgoing IXFR frozen");
+ return KNOT_STATE_FAIL;
+ default: /* Server errors. */
+ IXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)",
+ knot_strerror(ret));
+ return KNOT_STATE_FAIL;
+ }
+ }
+
+ /* Reserve space for TSIG. */
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Answer current packet (or continue). */
+ ret = xfr_process_list(pkt, &ixfr_process_journal, qdata);
+ switch (ret) {
+ case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */
+ return KNOT_STATE_PRODUCE; /* Check for more. */
+ case KNOT_EOK: /* Last response. */
+ if (ixfr->soa_last != ixfr->soa_to) {
+ IXFROUT_LOG(LOG_ERR, qdata, "failed (inconsistent history)");
+ return KNOT_STATE_FAIL;
+ }
+ xfr_stats_end(&ixfr->proc.stats);
+ xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_IXFR, LOG_DIRECTION_OUT,
+ REMOTE(qdata), false, &ixfr->proc.stats);
+ return KNOT_STATE_DONE;
+ default: /* Generic error. */
+ IXFROUT_LOG(LOG_ERR, qdata, "failed (%s)", knot_strerror(ret));
+ return KNOT_STATE_FAIL;
+ }
+}
diff --git a/src/knot/nameserver/ixfr.h b/src/knot/nameserver/ixfr.h
new file mode 100644
index 0000000..3012be1
--- /dev/null
+++ b/src/knot/nameserver/ixfr.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/journal/journal_read.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/nameserver/xfr.h"
+#include "libknot/packet/pkt.h"
+
+/*! \brief IXFR-in processing states. */
+enum ixfr_state {
+ IXFR_INVALID = 0,
+ IXFR_START, /* IXFR-in starting, expecting final SOA. */
+ IXFR_SOA_DEL, /* Expecting starting SOA. */
+ IXFR_SOA_ADD, /* Expecting ending SOA. */
+ IXFR_DEL, /* Expecting RR to delete. */
+ IXFR_ADD, /* Expecting RR to add. */
+ IXFR_DONE /* Processing done, IXFR-in complete. */
+};
+
+/*! \brief Extended structure for IXFR-in/IXFR-out processing. */
+struct ixfr_proc {
+ /* Processing state. */
+ struct xfr_proc proc;
+ enum ixfr_state state;
+ bool in_remove_section;
+
+ /* Changes to be sent. */
+ journal_read_t *journal_ctx;
+
+ /* Currently processed RRSet. */
+ knot_rrset_t cur_rr;
+
+ /* Processing context. */
+ knotd_qdata_t *qdata;
+ knot_mm_t *mm;
+ uint32_t soa_from;
+ uint32_t soa_to;
+ uint32_t soa_last;
+};
+
+/*!
+ * \brief IXFR query processing module.
+ *
+ * \retval PRODUCE if it has an answer, but not yet finished.
+ * \retval FAIL if it encountered an error.
+ * \retval DONE if finished.
+ */
+int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/log.h b/src/knot/nameserver/log.h
new file mode 100644
index 0000000..fc79bd3
--- /dev/null
+++ b/src/knot/nameserver/log.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "contrib/sockaddr.h"
+#include "knot/common/log.h"
+#include "libknot/dname.h"
+
+typedef enum {
+ LOG_OPERATION_AXFR,
+ LOG_OPERATION_IXFR,
+ LOG_OPERATION_NOTIFY,
+ LOG_OPERATION_REFRESH,
+ LOG_OPERATION_UPDATE,
+ LOG_OPERATION_DS_CHECK,
+ LOG_OPERATION_DS_PUSH,
+} log_operation_t;
+
+typedef enum {
+ LOG_DIRECTION_NONE,
+ LOG_DIRECTION_IN,
+ LOG_DIRECTION_OUT,
+} log_direction_t;
+
+static inline const char *log_operation_name(log_operation_t operation)
+{
+ switch (operation) {
+ case LOG_OPERATION_AXFR:
+ return "AXFR";
+ case LOG_OPERATION_IXFR:
+ return "IXFR";
+ case LOG_OPERATION_NOTIFY:
+ return "notify";
+ case LOG_OPERATION_REFRESH:
+ return "refresh";
+ case LOG_OPERATION_UPDATE:
+ return "DDNS";
+ case LOG_OPERATION_DS_CHECK:
+ return "DS check";
+ case LOG_OPERATION_DS_PUSH:
+ return "DS push";
+ default:
+ return "?";
+ }
+}
+
+static inline const char *log_direction_name(log_direction_t direction)
+{
+ switch (direction) {
+ case LOG_DIRECTION_IN:
+ return ", incoming";
+ case LOG_DIRECTION_OUT:
+ return ", outgoing";
+ case LOG_DIRECTION_NONE:
+ default:
+ return "";
+ }
+}
+
+/*!
+ * \brief Generate log message for server communication.
+ *
+ * Example output:
+ *
+ * [example.com] NOTIFY, outgoing, remote 2001:db8::1@53, serial 123
+ */
+#define ns_log(priority, zone, op, dir, remote, pool, fmt, ...) \
+ do { \
+ char address[SOCKADDR_STRLEN] = ""; \
+ sockaddr_tostr(address, sizeof(address), (const struct sockaddr_storage *)remote); \
+ log_fmt_zone(priority, LOG_SOURCE_ZONE, zone, NULL, "%s%s, remote %s%s, " fmt, \
+ log_operation_name(op), log_direction_name(dir), address, \
+ (pool) ? " pool" : "", ## __VA_ARGS__); \
+ } while (0)
diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c
new file mode 100644
index 0000000..82fce70
--- /dev/null
+++ b/src/knot/nameserver/notify.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "knot/nameserver/notify.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/log.h"
+#include "knot/nameserver/tsig_ctx.h"
+#include "knot/zone/serial.h"
+#include "libdnssec/random.h"
+#include "libknot/libknot.h"
+
+#define NOTIFY_IN_LOG(priority, qdata, fmt...) \
+ ns_log(priority, knot_pkt_qname(qdata->query), LOG_OPERATION_NOTIFY, \
+ LOG_DIRECTION_IN, knotd_qdata_remote_addr(qdata), false, fmt)
+
+static int notify_check_query(knotd_qdata_t *qdata)
+{
+ NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
+ NS_NEED_AUTH(qdata, ACL_ACTION_NOTIFY);
+ /* RFC1996 requires SOA question. */
+ NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR);
+
+ return KNOT_STATE_DONE;
+}
+
+int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt == NULL || qdata == NULL) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* Validate notification query. */
+ int state = notify_check_query(qdata);
+ if (state == KNOT_STATE_FAIL) {
+ switch (qdata->rcode) {
+ case KNOT_RCODE_NOTAUTH: /* Not authorized, already logged. */
+ break;
+ default: /* Other errors. */
+ NOTIFY_IN_LOG(LOG_DEBUG, qdata, "invalid query");
+ break;
+ }
+ return state;
+ }
+
+ /* Reserve space for TSIG. */
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key));
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* SOA RR in answer may be included, recover serial. */
+ zone_t *zone = (zone_t *)qdata->extra->zone;
+ const knot_pktsection_t *answer = knot_pkt_section(qdata->query, KNOT_ANSWER);
+ if (answer->count > 0) {
+ const knot_rrset_t *soa = knot_pkt_rr(answer, 0);
+ if (soa->type == KNOT_RRTYPE_SOA) {
+ uint32_t zone_serial, serial = knot_soa_serial(soa->rrs.rdata);
+ NOTIFY_IN_LOG(LOG_INFO, qdata, "serial %u", serial);
+ if (zone->contents != NULL &&
+ slave_zone_serial(zone, conf(), &zone_serial) == KNOT_EOK &&
+ serial_equal(serial, zone_serial)) {
+ // NOTIFY serial == zone serial => ignore, keep timers
+ return KNOT_STATE_DONE;
+ }
+ } else { /* Complain, but accept N/A record. */
+ NOTIFY_IN_LOG(LOG_NOTICE, qdata, "bad record in answer section");
+ }
+ } else {
+ NOTIFY_IN_LOG(LOG_INFO, qdata, "serial none");
+ }
+
+ /* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */
+ zone_set_preferred_master(zone, knotd_qdata_remote_addr(qdata));
+ zone_events_schedule_now(zone, ZONE_EVENT_REFRESH);
+
+ return KNOT_STATE_DONE;
+}
diff --git a/src/knot/nameserver/notify.h b/src/knot/nameserver/notify.h
new file mode 100644
index 0000000..d0bff14
--- /dev/null
+++ b/src/knot/nameserver/notify.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "knot/nameserver/process_query.h"
+
+/*!
+ * \brief Answer IN class zone NOTIFY message (RFC1996).
+ *
+ * \retval FAIL if it encountered an error.
+ * \retval DONE if finished.
+ */
+int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/nsec_proofs.c b/src/knot/nameserver/nsec_proofs.c
new file mode 100644
index 0000000..71944b1
--- /dev/null
+++ b/src/knot/nameserver/nsec_proofs.c
@@ -0,0 +1,677 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "libknot/libknot.h"
+#include "knot/nameserver/nsec_proofs.h"
+#include "knot/nameserver/internet.h"
+#include "knot/dnssec/zone-nsec.h"
+
+/*!
+ * \brief Check if node is empty non-terminal.
+ */
+static bool empty_nonterminal(const zone_node_t *node)
+{
+ return node && node->rrset_count == 0;
+}
+
+/*!
+ * \brief Check if wildcard expansion happened for given node and QNAME.
+ */
+static bool wildcard_expanded(const zone_node_t *node, const knot_dname_t *qname)
+{
+ return !knot_dname_is_wildcard(qname) && knot_dname_is_wildcard(node->owner);
+}
+
+/*!
+ * \brief Check if opt-out can take an effect.
+ */
+static bool ds_optout(const zone_node_t *node)
+{
+ return node_nsec3_get(node) == NULL && !(node->flags & NODE_FLAGS_SUBTREE_AUTH);
+}
+
+/*!
+ * \brief Check if node is part of the NSEC chain.
+ *
+ * NSEC is created for each node with authoritative data or delegation.
+ *
+ * \see https://tools.ietf.org/html/rfc4035#section-2.3
+ */
+static bool node_in_nsec(const zone_node_t *node)
+{
+ return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !empty_nonterminal(node);
+}
+
+/*!
+ * \brief Check if node is part of the NSEC3 chain.
+ *
+ * NSEC3 is created for each node with authoritative data, empty-non terminal,
+ * and delegation (unless opt-out is in effect).
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-7.1
+ */
+static bool node_in_nsec3(const zone_node_t *node)
+{
+ return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !ds_optout(node);
+}
+
+/*!
+ * \brief Walk previous names until we reach a node in NSEC chain.
+ *
+ */
+static const zone_node_t *nsec_previous(const zone_node_t *previous)
+{
+ assert(previous);
+
+ while (!node_in_nsec(previous)) {
+ previous = node_prev(previous);
+ assert(previous);
+ }
+
+ return previous;
+}
+
+/*!
+ * \brief Get closest provable encloser from closest matching parent node.
+ */
+static const zone_node_t *nsec3_encloser(const zone_node_t *closest)
+{
+ assert(closest);
+
+ while (!node_in_nsec3(closest)) {
+ closest = node_parent(closest);
+ assert(closest);
+ }
+
+ return closest;
+}
+
+/*!
+ * \brief Create a 'next closer name' to the given domain name.
+ *
+ * Next closer is the name one label longer than the closest provable encloser
+ * of a name.
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-1.3
+ *
+ * \param closest_encloser Closest provable encloser of \a name.
+ * \param name Domain name to create the 'next closer' name to.
+ *
+ * \return Next closer name, NULL on error.
+ */
+static const knot_dname_t *get_next_closer(const knot_dname_t *closest_encloser,
+ const knot_dname_t *name)
+{
+ // make name only one label longer than closest_encloser
+ size_t ce_labels = knot_dname_labels(closest_encloser, NULL);
+ size_t qname_labels = knot_dname_labels(name, NULL);
+ for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) {
+ name = knot_wire_next_label(name, NULL);
+ }
+
+ // the common labels should match
+ assert(knot_dname_is_equal(knot_wire_next_label(name, NULL), closest_encloser));
+
+ return name;
+}
+
+/*!
+ * \brief Put NSEC/NSEC3 record with corresponding RRSIG into the response.
+ */
+static int put_nxt_from_node(const zone_node_t *node,
+ uint16_t type,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ assert(type == KNOT_RRTYPE_NSEC || type == KNOT_RRTYPE_NSEC3);
+
+ knot_rrset_t rrset = node_rrset(node, type);
+ if (knot_rrset_empty(&rrset)) {
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG);
+
+ return process_query_put_rr(resp, qdata, &rrset, &rrsigs,
+ KNOT_COMPR_HINT_NONE, KNOT_PF_CHECKDUP);
+}
+
+/*!
+ * \brief Put NSEC record with corresponding RRSIG into the response.
+ */
+static int put_nsec_from_node(const zone_node_t *node,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ return put_nxt_from_node(node, KNOT_RRTYPE_NSEC, qdata, resp);
+}
+
+/*!
+ * \brief Put NSEC3 record with corresponding RRSIG into the response.
+ */
+static int put_nsec3_from_node(const zone_node_t *node,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ return put_nxt_from_node(node, KNOT_RRTYPE_NSEC3, qdata, resp);
+}
+
+/*!
+ * \brief Find NSEC for given name and put it into the response.
+ *
+ * Note this function allows the name to match the QNAME. The NODATA proof
+ * for empty non-terminal is equivalent to NXDOMAIN proof, except that the
+ * names may exist. This is why.
+ */
+static int put_covering_nsec(const zone_contents_t *zone,
+ const knot_dname_t *name,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ const zone_node_t *match = NULL;
+ const zone_node_t *closest = NULL;
+ const zone_node_t *prev = NULL;
+
+ const zone_node_t *proof = NULL;
+
+ int ret = zone_contents_find_dname(zone, name, &match, &closest, &prev);
+ if (ret == ZONE_NAME_FOUND) {
+ proof = match;
+ } else if (ret == ZONE_NAME_NOT_FOUND) {
+ proof = nsec_previous(prev);
+ } else {
+ assert(ret < 0);
+ return ret;
+ }
+
+ return put_nsec_from_node(proof, qdata, resp);
+}
+
+/*!
+ * \brief Find NSEC3 covering the given name and put it into the response.
+ */
+static int put_covering_nsec3(const zone_contents_t *zone,
+ const knot_dname_t *name,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ const zone_node_t *prev = NULL;
+ const zone_node_t *node = NULL;
+
+ int match = zone_contents_find_nsec3_for_name(zone, name, &node, &prev);
+ if (match < 0) {
+ // ignore if missing
+ return KNOT_EOK;
+ }
+
+ if (match == ZONE_NAME_FOUND || prev == NULL){
+ return KNOT_ERROR;
+ }
+
+ return put_nsec3_from_node(prev, qdata, resp);
+}
+
+/*!
+ * \brief Add NSEC3 covering the next closer name to closest encloser.
+ *
+ * \param cpe Closest provable encloser of \a qname.
+ * \param qname Source QNAME.
+ * \param zone Source zone.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_nsec3_next_closer(const zone_node_t *cpe,
+ const knot_dname_t *qname,
+ const zone_contents_t *zone,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ const knot_dname_t *next_closer = get_next_closer(cpe->owner, qname);
+
+ return put_covering_nsec3(zone, next_closer, qdata, resp);
+}
+
+/*!
+ * \brief Add NSEC3s for closest encloser proof.
+ *
+ * Adds up to two NSEC3 records. The first one proves that closest encloser
+ * of the queried name exists, the second one proves that the name bellow the
+ * encloser doesn't.
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.1
+ *
+ * \param qname Source QNAME.
+ * \param zone Source zone.
+ * \param cpe Closest provable encloser of \a qname.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_closest_encloser_proof(const knot_dname_t *qname,
+ const zone_contents_t *zone,
+ const zone_node_t *cpe,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ // An NSEC3 RR that matches the closest (provable) encloser.
+
+ int ret = put_nsec3_from_node(node_nsec3_get(cpe), qdata, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // An NSEC3 RR that covers the "next closer" name to the closest encloser.
+
+ return put_nsec3_next_closer(cpe, qname, zone, qdata, resp);
+}
+
+/*!
+ * \brief Put NSEC for wildcard answer into the response.
+ *
+ * Add NSEC record proving that no better match on QNAME exists.
+ *
+ * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.3
+ *
+ * \param previous Previous name for QNAME.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_nsec_wildcard(const zone_node_t *previous,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ return put_nsec_from_node(previous, qdata, resp);
+}
+
+/*!
+ * \brief Put NSEC3s for wildcard answer into the response.
+ *
+ * Add NSEC3 record proving that no better match on QNAME exists.
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.6
+ *
+ * \param wildcard Wildcard node that was used for expansion.
+ * \param qname Source QNAME.
+ * \param zone Source zone.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ */
+static int put_nsec3_wildcard(const zone_node_t *wildcard,
+ const knot_dname_t *qname,
+ const zone_contents_t *zone,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ const zone_node_t *cpe = nsec3_encloser(node_parent(wildcard));
+
+ return put_nsec3_next_closer(cpe, qname, zone, qdata, resp);
+}
+
+/*!
+ * \brief Put NSECs or NSEC3s for wildcard expansion in the response.
+ *
+ * \return KNOT_E*
+ */
+static int put_wildcard_answer(const zone_node_t *wildcard,
+ const zone_node_t *previous,
+ const zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ if (!wildcard_expanded(wildcard, qname)) {
+ return KNOT_EOK;
+ }
+
+ int ret = 0;
+
+ if (knot_is_nsec3_enabled(zone)) {
+ ret = put_nsec3_wildcard(wildcard, qname, zone, qdata, resp);
+ } else {
+ previous = nsec_previous(previous);
+ ret = put_nsec_wildcard(previous, qdata, resp);
+ }
+
+ return ret;
+}
+
+/*!
+ * \brief Put NSECs for NXDOMAIN error into the response.
+ *
+ * Adds up to two NSEC records. We have to prove that the queried name doesn't
+ * exist and that no wildcard expansion is possible for that name.
+ *
+ * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.2
+ *
+ * \param zone Source zone.
+ * \param previous Previous node to QNAME.
+ * \param closest Closest matching parent of QNAME.
+ * \param qdata Query data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_nsec_nxdomain(const zone_contents_t *zone,
+ const zone_node_t *previous,
+ const zone_node_t *closest,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ assert(previous);
+ assert(closest);
+
+ // An NSEC RR proving that there is no exact match for <SNAME, SCLASS>.
+
+ previous = nsec_previous(previous);
+ int ret = put_nsec_from_node(previous, qdata, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // An NSEC RR proving that the zone contains no RRsets that would match
+ // <SNAME, SCLASS> via wildcard name expansion.
+
+ // NOTE: closest may be empty non-terminal and thus not authoritative.
+
+ size_t size = knot_dname_size(closest->owner);
+ if (size > KNOT_DNAME_MAXLEN - 2) {
+ return KNOT_EINVAL;
+ }
+ assert(size > 0);
+ uint8_t wildcard[2 + size];
+ memcpy(wildcard, "\x01""*", 2);
+ memcpy(wildcard + 2, closest->owner, size);
+
+ return put_covering_nsec(zone, wildcard, qdata, resp);
+}
+
+/*!
+ * \brief Put NSEC3s for NXDOMAIN error into the response.
+ *
+ * Adds up to three NSEC3 records. We have to prove that some parent name
+ * exists (closest encloser proof) and that no wildcard expansion is possible
+ * bellow that closest encloser.
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.2
+ *
+ * \param qname Source QNAME.
+ * \param zone Source zone.
+ * \param closest Closest matching parent of \a qname.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \retval KNOT_E*
+ */
+static int put_nsec3_nxdomain(const knot_dname_t *qname,
+ const zone_contents_t *zone,
+ const zone_node_t *closest,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ const zone_node_t *cpe = nsec3_encloser(closest);
+
+ // Closest encloser proof.
+
+ int ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // NSEC3 covering the (nonexistent) wildcard at the closest encloser.
+
+ const zone_node_t *nsec3_wildcard_prev, *ignored;
+ if (cpe->nsec3_wildcard_name == NULL ||
+ zone_contents_find_nsec3(zone, cpe->nsec3_wildcard_name, &ignored, &nsec3_wildcard_prev) == ZONE_NAME_FOUND) {
+ return KNOT_ERROR;
+ }
+
+ return put_nsec3_from_node(nsec3_wildcard_prev, qdata, resp);
+}
+
+/*!
+ * \brief Put NSECs or NSEC3s for the NXDOMAIN error into the response.
+ *
+ * \param zone Zone used for answering.
+ * \param previous Previous node to \a qname.
+ * \param closest Closest matching parent name for \a qname.
+ * \param qname Source QNAME.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_nxdomain(const zone_contents_t *zone,
+ const zone_node_t *previous,
+ const zone_node_t *closest,
+ const knot_dname_t *qname,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ if (knot_is_nsec3_enabled(zone)) {
+ return put_nsec3_nxdomain(qname, zone, closest, qdata, resp);
+ } else {
+ return put_nsec_nxdomain(zone, previous, closest, qdata, resp);
+ }
+}
+
+/*!
+ * \brief Put NSEC for NODATA error into the response.
+ *
+ * Then NSEC matching the QNAME must be added into the response and the bitmap
+ * will indicate that the QTYPE doesn't exist. As NSECs for empty non-terminals
+ * don't exist, the proof for NODATA match on non-terminal is proved like
+ * non-existence of the queried name.
+ *
+ * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.1
+ *
+ * \param match Node matching QNAME.
+ * \param previous Previous node to QNAME in the zone.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ *
+ * \return KNOT_E*
+ */
+static int put_nsec_nodata(const zone_node_t *match,
+ const zone_node_t *previous,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ if (empty_nonterminal(match)) {
+ return put_nsec_from_node(nsec_previous(previous), qdata, resp);
+ } else {
+ return put_nsec_from_node(match, qdata, resp);
+ }
+}
+
+/*!
+ * \brief Put NSEC3 for NODATA error into the response.
+ *
+ * The NSEC3 matching the QNAME is added into the response and the bitmap
+ * will indicate that the QTYPE doesn't exist. For QTYPE==DS, the server
+ * may alternatively serve a closest encloser proof with opt-out. For wildcard
+ * expansion, the closest encloser proof must included as well.
+ *
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.3
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.4
+ * \see https://tools.ietf.org/html/rfc5155#section-7.2.5
+ */
+static int put_nsec3_nodata(const knot_dname_t *qname,
+ const zone_contents_t *zone,
+ const zone_node_t *match,
+ const zone_node_t *closest,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ int ret = KNOT_EOK;
+
+ // NSEC3 matching QNAME is always included.
+
+ zone_node_t *nsec3_match = node_nsec3_get(match);
+ if (nsec3_match != NULL) {
+ ret = put_nsec3_from_node(nsec3_match, qdata, resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Closest encloser proof for wildcard effect or NSEC3 opt-out.
+
+ if (wildcard_expanded(match, qname) || ds_optout(match)) {
+ const zone_node_t *cpe = nsec3_encloser(closest);
+ ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp);
+ }
+
+ return ret;
+}
+
+/*!
+ * \brief Put NSECs or NSEC3s for the NODATA error into the response.
+ *
+ * \param node Source node.
+ * \param qdata Query processing data.
+ * \param resp Response packet.
+ */
+static int put_nodata(const zone_node_t *node,
+ const zone_node_t *closest,
+ const zone_node_t *previous,
+ const zone_contents_t *zone,
+ const knot_dname_t *qname,
+ knotd_qdata_t *qdata,
+ knot_pkt_t *resp)
+{
+ if (knot_is_nsec3_enabled(zone)) {
+ return put_nsec3_nodata(qname, zone, node, closest, qdata, resp);
+ } else {
+ return put_nsec_nodata(node, previous, qdata, resp);
+ }
+}
+
+int nsec_prove_wildcards(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (qdata->extra->contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ struct wildcard_hit *item;
+
+ WALK_LIST(item, qdata->extra->wildcards) {
+ if (item->node == NULL) {
+ return KNOT_EINVAL;
+ }
+ ret = put_wildcard_answer(item->node, item->prev,
+ qdata->extra->contents,
+ item->sname, qdata, pkt);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int nsec_prove_nodata(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (qdata->extra->contents == NULL || qdata->extra->node == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return put_nodata(qdata->extra->node, qdata->extra->encloser, qdata->extra->previous,
+ qdata->extra->contents, qdata->name, qdata, pkt);
+}
+
+int nsec_prove_nxdomain(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (qdata->extra->contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return put_nxdomain(qdata->extra->contents,
+ qdata->extra->previous, qdata->extra->encloser,
+ qdata->name, qdata, pkt);
+}
+
+int nsec_prove_dp_security(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (qdata->extra->node == NULL || qdata->extra->encloser == NULL ||
+ qdata->extra->contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Add DS into the response.
+
+ knot_rrset_t rrset = node_rrset(qdata->extra->node, KNOT_RRTYPE_DS);
+ if (!knot_rrset_empty(&rrset)) {
+ knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG);
+ return process_query_put_rr(pkt, qdata, &rrset, &rrsigs,
+ KNOT_COMPR_HINT_NONE, 0);
+ }
+
+ // Alternatively prove that DS doesn't exist.
+
+ return put_nodata(qdata->extra->node, qdata->extra->encloser, qdata->extra->previous,
+ qdata->extra->contents, qdata->name, qdata, pkt);
+}
+
+int nsec_append_rrsigs(knot_pkt_t *pkt, knotd_qdata_t *qdata, bool optional)
+{
+ int ret = KNOT_EOK;
+ uint16_t flags = optional ? KNOT_PF_NOTRUNC : KNOT_PF_NULL;
+ flags |= KNOT_PF_FREE; // Free all RRSIGs, they are synthesized
+ flags |= KNOT_PF_ORIGTTL;
+
+ /* Append RRSIGs for section. */
+ struct rrsig_info *info;
+ WALK_LIST(info, qdata->extra->rrsigs) {
+ knot_rrset_t *rrsig = &info->synth_rrsig;
+ uint16_t compr_hint = info->rrinfo->compress_ptr[KNOT_COMPR_HINT_OWNER];
+ uint16_t flags_mask = (info->rrinfo->flags & KNOT_PF_SOAMINTTL) ? KNOT_PF_ORIGTTL : 0;
+ ret = knot_pkt_put(pkt, compr_hint, rrsig, flags & ~flags_mask);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ /* RRSIG is owned by packet now. */
+ knot_rdataset_init(&info->synth_rrsig.rrs);
+ };
+
+ /* Clear the list. */
+ nsec_clear_rrsigs(qdata);
+
+ return ret;
+}
+
+void nsec_clear_rrsigs(knotd_qdata_t *qdata)
+{
+ if (qdata == NULL) {
+ return;
+ }
+
+ struct rrsig_info *info;
+ WALK_LIST(info, qdata->extra->rrsigs) {
+ knot_rrset_t *rrsig = &info->synth_rrsig;
+ knot_rrset_clear(rrsig, qdata->mm);
+ };
+
+ ptrlist_free(&qdata->extra->rrsigs, qdata->mm);
+ init_list(&qdata->extra->rrsigs);
+}
diff --git a/src/knot/nameserver/nsec_proofs.h b/src/knot/nameserver/nsec_proofs.h
new file mode 100644
index 0000000..09d5f2a
--- /dev/null
+++ b/src/knot/nameserver/nsec_proofs.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "knot/nameserver/process_query.h"
+
+/*! \brief Prove wildcards visited during answer resolution. */
+int nsec_prove_wildcards(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*! \brief Prove answer leading to non-existent name. */
+int nsec_prove_nxdomain(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*! \brief Prove empty answer. */
+int nsec_prove_nodata(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*! \brief Prove delegation point security. */
+int nsec_prove_dp_security(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*! \brief Append missing RRSIGs for current processing section. */
+int nsec_append_rrsigs(knot_pkt_t *pkt, knotd_qdata_t *qdata, bool optional);
+
+/*! \brief Clear RRSIG list. */
+void nsec_clear_rrsigs(knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c
new file mode 100644
index 0000000..34590df
--- /dev/null
+++ b/src/knot/nameserver/process_query.c
@@ -0,0 +1,978 @@
+/* Copyright (C) 2023 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 <urcu.h>
+
+#include "libdnssec/tsig.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/nameserver/query_module.h"
+#include "knot/nameserver/chaos.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/axfr.h"
+#include "knot/nameserver/ixfr.h"
+#include "knot/nameserver/update.h"
+#include "knot/nameserver/nsec_proofs.h"
+#include "knot/nameserver/notify.h"
+#include "knot/server/server.h"
+#include "libknot/libknot.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+
+/*! \brief Accessor to query-specific data. */
+#define QUERY_DATA(ctx) ((knotd_qdata_t *)(ctx)->data)
+
+static knotd_query_type_t query_type(const knot_pkt_t *pkt)
+{
+ switch (knot_wire_get_opcode(pkt->wire)) {
+ case KNOT_OPCODE_QUERY:
+ switch (knot_pkt_qtype(pkt)) {
+ case 0 /* RESERVED */: return KNOTD_QUERY_TYPE_INVALID;
+ case KNOT_RRTYPE_AXFR: return KNOTD_QUERY_TYPE_AXFR;
+ case KNOT_RRTYPE_IXFR: return KNOTD_QUERY_TYPE_IXFR;
+ default: return KNOTD_QUERY_TYPE_NORMAL;
+ }
+ case KNOT_OPCODE_NOTIFY: return KNOTD_QUERY_TYPE_NOTIFY;
+ case KNOT_OPCODE_UPDATE: return KNOTD_QUERY_TYPE_UPDATE;
+ default: return KNOTD_QUERY_TYPE_INVALID;
+ }
+}
+
+/*! \brief Reinitialize query data structure. */
+static void query_data_init(knot_layer_t *ctx, knotd_qdata_params_t *params,
+ knotd_qdata_extra_t *extra)
+{
+ /* Initialize persistent data. */
+ knotd_qdata_t *data = QUERY_DATA(ctx);
+ memset(data, 0, sizeof(*data));
+ data->mm = ctx->mm;
+ data->params = params;
+ data->extra = extra;
+ data->rcode_ede = KNOT_EDNS_EDE_NONE;
+
+ /* Initialize lists. */
+ memset(extra, 0, sizeof(*extra));
+ init_list(&extra->wildcards);
+ init_list(&extra->rrsigs);
+}
+
+static int process_query_begin(knot_layer_t *ctx, void *params)
+{
+ /* Initialize context. */
+ assert(ctx);
+ ctx->data = mm_alloc(ctx->mm, sizeof(knotd_qdata_t));
+ knotd_qdata_extra_t *extra = mm_alloc(ctx->mm, sizeof(*extra));
+
+ /* Initialize persistent data. */
+ query_data_init(ctx, params, extra);
+
+ /* Await packet. */
+ return KNOT_STATE_CONSUME;
+}
+
+static int process_query_reset(knot_layer_t *ctx)
+{
+ assert(ctx);
+ knotd_qdata_t *qdata = QUERY_DATA(ctx);
+
+ /* Remember persistent parameters. */
+ knotd_qdata_params_t *params = qdata->params;
+ knotd_qdata_extra_t *extra = qdata->extra;
+
+ /* Free allocated data. */
+ knot_rrset_clear(&qdata->opt_rr, qdata->mm);
+ ptrlist_free(&extra->wildcards, qdata->mm);
+ nsec_clear_rrsigs(qdata);
+ if (extra->ext_cleanup != NULL) {
+ extra->ext_cleanup(qdata);
+ }
+
+ /* Initialize persistent data. */
+ query_data_init(ctx, params, extra);
+
+ /* Await packet. */
+ return KNOT_STATE_CONSUME;
+}
+
+static int process_query_finish(knot_layer_t *ctx)
+{
+ process_query_reset(ctx);
+ mm_free(ctx->mm, ctx->data);
+ ctx->data = NULL;
+
+ return KNOT_STATE_NOOP;
+}
+
+static int process_query_in(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ assert(pkt && ctx);
+ knotd_qdata_t *qdata = QUERY_DATA(ctx);
+
+ /* Check if at least header is parsed. */
+ if (pkt->parsed < KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_STATE_NOOP; /* Ignore. */
+ }
+
+ /* Accept only queries. */
+ if (knot_wire_get_qr(pkt->wire)) {
+ return KNOT_STATE_NOOP; /* Ignore. */
+ }
+
+ /* Store for processing. */
+ qdata->query = pkt;
+ qdata->type = query_type(pkt);
+
+ /* Declare having response. */
+ return KNOT_STATE_PRODUCE;
+}
+
+/*!
+ * \brief Create a response for a given query in the INTERNET class.
+ */
+static int query_internet(knot_pkt_t *pkt, knot_layer_t *ctx)
+{
+ knotd_qdata_t *data = QUERY_DATA(ctx);
+
+ switch (data->type) {
+ case KNOTD_QUERY_TYPE_NORMAL: return internet_process_query(pkt, data);
+ case KNOTD_QUERY_TYPE_NOTIFY: return notify_process_query(pkt, data);
+ case KNOTD_QUERY_TYPE_AXFR: return axfr_process_query(pkt, data);
+ case KNOTD_QUERY_TYPE_IXFR: return ixfr_process_query(pkt, data);
+ case KNOTD_QUERY_TYPE_UPDATE: return update_process_query(pkt, data);
+ default:
+ /* Nothing else is supported. */
+ data->rcode = KNOT_RCODE_NOTIMPL;
+ return KNOT_STATE_FAIL;
+ }
+}
+
+/*!
+ * \brief Create a response for a given query in the CHAOS class.
+ */
+static int query_chaos(knot_pkt_t *pkt, knot_layer_t *ctx)
+{
+ knotd_qdata_t *data = QUERY_DATA(ctx);
+
+ /* Nothing except normal queries is supported. */
+ if (data->type != KNOTD_QUERY_TYPE_NORMAL) {
+ data->rcode = KNOT_RCODE_NOTIMPL;
+ return KNOT_STATE_FAIL;
+ }
+
+ data->rcode = knot_chaos_answer(pkt);
+ if (data->rcode != KNOT_RCODE_NOERROR) {
+ return KNOT_STATE_FAIL;
+ }
+
+ return KNOT_STATE_DONE;
+}
+
+/*! \brief Find zone for given question. */
+static zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb)
+{
+ uint16_t qtype = knot_pkt_qtype(query);
+ uint16_t qclass = knot_pkt_qclass(query);
+ const knot_dname_t *qname = knot_pkt_qname(query);
+ zone_t *zone = NULL;
+
+ // search for zone only for IN and ANY classes
+ if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) {
+ return NULL;
+ }
+
+ /* In case of DS query, we strip the leftmost label when searching for
+ * the zone (but use whole qname in search for the record), as the DS
+ * records are only present in a parent zone.
+ */
+ if (qtype == KNOT_RRTYPE_DS) {
+ const knot_dname_t *parent = knot_wire_next_label(qname, NULL);
+ zone = knot_zonedb_find_suffix(zonedb, parent);
+ /* If zone does not exist, search for its parent zone,
+ this will later result to NODATA answer. */
+ /*! \note This is not 100% right, it may lead to DS name for example
+ * when following a CNAME chain, that should also be answered
+ * from the parent zone (if it exists).
+ */
+ }
+
+ if (zone == NULL) {
+ if (query_type(query) == KNOTD_QUERY_TYPE_NORMAL) {
+ zone = knot_zonedb_find_suffix(zonedb, qname);
+ } else {
+ // Direct match required.
+ zone = knot_zonedb_find(zonedb, qname);
+ }
+ }
+
+ return zone;
+}
+
+static int answer_edns_reserve(knot_pkt_t *resp, knotd_qdata_t *qdata)
+{
+ if (knot_rrset_empty(&qdata->opt_rr)) {
+ return KNOT_EOK;
+ }
+
+ /* Reserve size in the response. */
+ return knot_pkt_reserve(resp, knot_edns_wire_size(&qdata->opt_rr));
+}
+
+static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp,
+ knotd_qdata_t *qdata)
+{
+ if (!knot_pkt_has_edns(query)) {
+ return KNOT_EOK;
+ }
+
+ /* Initialize OPT record. */
+ uint16_t max_payload;
+ switch (knotd_qdata_remote_addr(qdata)->ss_family) {
+ case AF_INET:
+ max_payload = conf()->cache.srv_udp_max_payload_ipv4;
+ break;
+ case AF_INET6:
+ max_payload = conf()->cache.srv_udp_max_payload_ipv6;
+ break;
+ case AF_UNIX:
+ max_payload = MIN(conf()->cache.srv_udp_max_payload_ipv4,
+ conf()->cache.srv_udp_max_payload_ipv6);
+ break;
+ default:
+ return KNOT_ERROR;
+ }
+ int ret = knot_edns_init(&qdata->opt_rr, max_payload, 0,
+ KNOT_EDNS_VERSION, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check supported version. */
+ if (knot_edns_get_version(query->opt_rr) != KNOT_EDNS_VERSION) {
+ qdata->rcode = KNOT_RCODE_BADVERS;
+ }
+
+ /* Set DO bit if set (DNSSEC requested). */
+ if (knot_pkt_has_dnssec(query)) {
+ knot_edns_set_do(&qdata->opt_rr);
+ }
+
+ /* Append NSID if requested and available. */
+ if (knot_pkt_edns_option(query, KNOT_EDNS_OPTION_NSID) != NULL) {
+ size_t nsid_len = conf()->cache.srv_nsid_len;
+ const uint8_t *nsid_data = conf()->cache.srv_nsid_data;
+
+ if (nsid_len > 0) {
+ ret = knot_edns_add_option(&qdata->opt_rr,
+ KNOT_EDNS_OPTION_NSID,
+ nsid_len, nsid_data,
+ qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ /* Initialize EDNS Client Subnet if configured and present in query. */
+ if (conf()->cache.srv_ecs) {
+ uint8_t *ecs_opt = knot_pkt_edns_option(query, KNOT_EDNS_OPTION_CLIENT_SUBNET);
+ if (ecs_opt != NULL) {
+ qdata->ecs = mm_alloc(qdata->mm, sizeof(knot_edns_client_subnet_t));
+ if (qdata->ecs == NULL) {
+ return KNOT_ENOMEM;
+ }
+ const uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt);
+ uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt);
+ ret = knot_edns_client_subnet_parse(qdata->ecs, ecs_data, ecs_len);
+ if (ret != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ return ret;
+ }
+ qdata->ecs->scope_len = 0;
+
+ /* Reserve space for the option in the answer. */
+ ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET,
+ ecs_len, NULL, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ } else {
+ qdata->ecs = NULL;
+ }
+
+ return answer_edns_reserve(resp, qdata);
+}
+
+static int answer_edns_put(knot_pkt_t *resp, knotd_qdata_t *qdata)
+{
+ if (knot_rrset_empty(&qdata->opt_rr)) {
+ return KNOT_EOK;
+ }
+
+ /* Add ECS if present. */
+ int ret = KNOT_EOK;
+ if (qdata->ecs != NULL) {
+ uint8_t *ecs_opt = knot_edns_get_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET, NULL);
+ if (ecs_opt != NULL) {
+ uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt);
+ uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt);
+ ret = knot_edns_client_subnet_write(ecs_data, ecs_len, qdata->ecs);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ size_t opt_wire_size = knot_edns_wire_size(&qdata->opt_rr);
+
+ /* Add EDE. Pragmatic: only if space in pkt. */
+ if (qdata->rcode_ede != KNOT_EDNS_EDE_NONE &&
+ knot_pkt_reserve(resp, KNOT_EDNS_EDE_MIN_LENGTH) == KNOT_EOK) {
+ ret = knot_pkt_reclaim(resp, KNOT_EDNS_EDE_MIN_LENGTH);
+ assert(ret == KNOT_EOK);
+
+ uint16_t ede_code = (uint16_t)qdata->rcode_ede;
+ assert((int)ede_code == qdata->rcode_ede);
+ ede_code = htobe16(ede_code);
+
+ ret = knot_edns_add_option(&qdata->opt_rr, KNOT_EDNS_OPTION_EDE,
+ sizeof(ede_code), (uint8_t *)&ede_code, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ /* Add EXPIRE if space and not catalog zone, which cannot expire. */
+ if (knot_pkt_edns_option(qdata->query, KNOT_EDNS_OPTION_EXPIRE) != NULL &&
+ qdata->extra->contents != NULL && !qdata->extra->zone->is_catalog_flag) {
+ int64_t timer = qdata->extra->zone->timers.next_expire == 0
+ ? zone_soa_expire(qdata->extra->zone)
+ : qdata->extra->zone->timers.next_expire - time(NULL);
+ timer = MAX(timer, 0);
+ uint32_t timer_be;
+ knot_wire_write_u32((uint8_t *)&timer_be, (uint32_t)timer);
+
+ uint16_t expire_size = KNOT_EDNS_OPTION_HDRLEN + sizeof(timer_be);
+ if (knot_pkt_reserve(resp, expire_size) == KNOT_EOK) {
+ ret = knot_pkt_reclaim(resp, expire_size);
+ assert(ret == KNOT_EOK);
+
+ ret = knot_edns_add_option(&qdata->opt_rr, KNOT_EDNS_OPTION_EXPIRE,
+ sizeof(timer_be), (uint8_t *)&timer_be,
+ qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ /* Align the response if QUIC with EDNS. */
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_QUIC) {
+ int pad_len = knot_pkt_default_padding_size(resp, &qdata->opt_rr);
+ if (pad_len > -1) {
+ ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_PADDING,
+ pad_len, NULL, qdata->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ /* Reclaim reserved size. */
+ ret = knot_pkt_reclaim(resp, opt_wire_size);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint8_t *wire_end = resp->wire + resp->size;
+
+ /* Write to packet. */
+ assert(resp->current == KNOT_ADDITIONAL);
+ ret = knot_pkt_put(resp, KNOT_COMPR_HINT_NONE, &qdata->opt_rr, 0);
+ if (ret == KNOT_EOK) {
+ /* Save position of the OPT RR. */
+ qdata->extra->opt_rr_pos = wire_end;
+ }
+
+ return ret;
+}
+
+/*! \brief Initialize response, sizes and find zone from which we're going to answer. */
+static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_layer_t *ctx)
+{
+ knotd_qdata_t *qdata = QUERY_DATA(ctx);
+ server_t *server = qdata->params->server;
+
+ /* Initialize response. */
+ int ret = knot_pkt_init_response(resp, query);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_wire_clear_cd(resp->wire);
+
+ /* Setup EDNS. */
+ ret = answer_edns_init(query, resp, qdata);
+ if (ret != KNOT_EOK || qdata->rcode != 0) {
+ return KNOT_ERROR;
+ }
+
+ /* Update maximal answer size. */
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP) {
+ resp->max_size = KNOT_WIRE_MIN_PKTSIZE;
+ if (knot_pkt_has_edns(query)) {
+ uint16_t server_size;
+ switch (knotd_qdata_remote_addr(qdata)->ss_family) {
+ case AF_INET:
+ server_size = conf()->cache.srv_udp_max_payload_ipv4;
+ break;
+ case AF_INET6:
+ server_size = conf()->cache.srv_udp_max_payload_ipv6;
+ break;
+ default:
+ return KNOT_ERROR;
+ }
+ uint16_t client_size = knot_edns_get_payload(query->opt_rr);
+ uint16_t transfer = MIN(client_size, server_size);
+ resp->max_size = MAX(resp->max_size, transfer);
+ }
+ } else {
+ resp->max_size = KNOT_WIRE_MAX_PKTSIZE;
+ }
+
+ /* All supported OPCODEs require a question. */
+ const knot_dname_t *qname = knot_pkt_qname(query);
+ if (qname == NULL) {
+ switch (knot_wire_get_opcode(query->wire)) {
+ case KNOT_OPCODE_QUERY:
+ case KNOT_OPCODE_NOTIFY:
+ case KNOT_OPCODE_UPDATE:
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ break;
+ default:
+ qdata->rcode = KNOT_RCODE_NOTIMPL;
+ }
+ return KNOT_ENOTSUP;
+ }
+
+ /* Find zone for QNAME. */
+ qdata->extra->zone = answer_zone_find(query, server->zone_db);
+ if (qdata->extra->zone != NULL && qdata->extra->contents == NULL) {
+ qdata->extra->contents = qdata->extra->zone->contents;
+ }
+
+ /* Allow normal queries to catalog only if not UDP and if allowed by ACL. */
+ if (qdata->extra->zone != NULL && qdata->extra->zone->is_catalog_flag &&
+ query_type(query) == KNOTD_QUERY_TYPE_NORMAL) {
+ if (qdata->params->proto == KNOTD_QUERY_PROTO_UDP ||
+ !process_query_acl_check(conf(), ACL_ACTION_TRANSFER, qdata)) {
+ qdata->extra->zone = NULL;
+ qdata->extra->contents = NULL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static void set_rcode_to_packet(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ uint8_t ext_rcode = KNOT_EDNS_RCODE_HI(qdata->rcode);
+
+ if (ext_rcode != 0) {
+ /* No OPT RR and Ext RCODE results in SERVFAIL. */
+ if (qdata->extra->opt_rr_pos == NULL) {
+ knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL);
+ return;
+ }
+
+ knot_edns_set_ext_rcode_wire(qdata->extra->opt_rr_pos, ext_rcode);
+ }
+
+ knot_wire_set_rcode(pkt->wire, KNOT_EDNS_RCODE_LO(qdata->rcode));
+}
+
+static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ assert(ctx && pkt);
+
+ knotd_qdata_t *qdata = QUERY_DATA(ctx);
+
+ /* Initialize response from query packet. */
+ knot_pkt_t *query = qdata->query;
+ (void)knot_pkt_init_response(pkt, query);
+ knot_wire_clear_cd(pkt->wire);
+
+ /* Set TC bit if required. */
+ if (qdata->err_truncated) {
+ knot_wire_set_aa(pkt->wire);
+ knot_wire_set_tc(pkt->wire);
+ }
+
+ /* Move to Additionals to add OPT and TSIG. */
+ if (pkt->current != KNOT_ADDITIONAL) {
+ (void)knot_pkt_begin(pkt, KNOT_ADDITIONAL);
+ }
+
+ /* Put OPT RR to the additional section. */
+ if (answer_edns_reserve(pkt, qdata) != KNOT_EOK ||
+ answer_edns_put(pkt, qdata) != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ }
+
+ /* Set final RCODE to packet. */
+ if (qdata->rcode == KNOT_RCODE_NOERROR && !qdata->err_truncated) {
+ /* Default RCODE is SERVFAIL if not otherwise specified. */
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ }
+ set_rcode_to_packet(pkt, qdata);
+
+ /* Transaction security (if applicable). */
+ if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
+ set_rcode_to_packet(pkt, qdata);
+ }
+
+ return KNOT_STATE_DONE;
+}
+
+#define PROCESS_BEGIN(plan, step, next_state, qdata) \
+ if (plan != NULL) { \
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_BEGIN]) { \
+ next_state = step->process(next_state, pkt, qdata, step->ctx); \
+ if (next_state == KNOT_STATE_FAIL) { \
+ goto finish; \
+ } \
+ } \
+ }
+
+#define PROCESS_END(plan, step, next_state, qdata) \
+ if (plan != NULL) { \
+ WALK_LIST(step, plan->stage[KNOTD_STAGE_END]) { \
+ next_state = step->process(next_state, pkt, qdata, step->ctx); \
+ if (next_state == KNOT_STATE_FAIL) { \
+ next_state = process_query_err(ctx, pkt); \
+ } \
+ } \
+ }
+
+static int process_query_out(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ assert(pkt && ctx);
+
+ rcu_read_lock();
+
+ knotd_qdata_t *qdata = QUERY_DATA(ctx);
+ struct query_plan *plan = conf()->query_plan;
+ struct query_plan *zone_plan = NULL;
+ struct query_step *step;
+
+ int next_state = KNOT_STATE_PRODUCE;
+
+ /* Check parse state. */
+ knot_pkt_t *query = qdata->query;
+ if (query->parsed < query->size) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ next_state = KNOT_STATE_FAIL;
+ goto finish;
+ }
+
+ /* Preprocessing. */
+ if (prepare_answer(query, pkt, ctx) != KNOT_EOK) {
+ next_state = KNOT_STATE_FAIL;
+ goto finish;
+ }
+
+ if (qdata->extra->zone != NULL && qdata->extra->zone->query_plan != NULL) {
+ zone_plan = qdata->extra->zone->query_plan;
+ }
+
+ /* Before query processing code. */
+ PROCESS_BEGIN(plan, step, next_state, qdata);
+ PROCESS_BEGIN(zone_plan, step, next_state, qdata);
+
+ /* Answer based on qclass. */
+ if (next_state == KNOT_STATE_PRODUCE) {
+ switch (knot_pkt_qclass(pkt)) {
+ case KNOT_CLASS_CH:
+ next_state = query_chaos(pkt, ctx);
+ break;
+ case KNOT_CLASS_ANY:
+ case KNOT_CLASS_IN:
+ next_state = query_internet(pkt, ctx);
+ break;
+ default:
+ qdata->rcode = KNOT_RCODE_REFUSED;
+ next_state = KNOT_STATE_FAIL;
+ break;
+ }
+ }
+
+ /* Postprocessing. */
+ if (next_state == KNOT_STATE_DONE || next_state == KNOT_STATE_PRODUCE) {
+ /* Move to Additionals to add OPT and TSIG. */
+ if (pkt->current != KNOT_ADDITIONAL) {
+ (void)knot_pkt_begin(pkt, KNOT_ADDITIONAL);
+ }
+
+ /* Put OPT RR to the additional section. */
+ if (answer_edns_put(pkt, qdata) != KNOT_EOK) {
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ next_state = KNOT_STATE_FAIL;
+ goto finish;
+ }
+
+ /* Transaction security (if applicable). */
+ if (process_query_sign_response(pkt, qdata) != KNOT_EOK) {
+ next_state = KNOT_STATE_FAIL;
+ goto finish;
+ }
+ }
+
+finish:
+ switch (next_state) {
+ case KNOT_STATE_NOOP:
+ break;
+ case KNOT_STATE_FAIL:
+ /* Error processing. */
+ next_state = process_query_err(ctx, pkt);
+ break;
+ case KNOT_STATE_FINAL:
+ /* Just skipped postprocessing. */
+ next_state = KNOT_STATE_DONE;
+ break;
+ default:
+ set_rcode_to_packet(pkt, qdata);
+ }
+
+ /* After query processing code. */
+ PROCESS_END(plan, step, next_state, qdata);
+ PROCESS_END(zone_plan, step, next_state, qdata);
+
+ rcu_read_unlock();
+
+ return next_state;
+}
+
+bool process_query_acl_check(conf_t *conf, acl_action_t action,
+ knotd_qdata_t *qdata)
+{
+ const knot_dname_t *zone_name = qdata->extra->zone->name;
+ knot_pkt_t *query = qdata->query;
+ const struct sockaddr_storage *query_source = knotd_qdata_remote_addr(qdata);
+ knot_tsig_key_t tsig = { 0 };
+
+ /* Skip if already checked and valid. */
+ if (qdata->sign.tsig_key.name != NULL) {
+ return true;
+ }
+
+ /* Authenticate with NOKEY if the packet isn't signed. */
+ if (query->tsig_rr) {
+ tsig.name = query->tsig_rr->owner;
+ tsig.algorithm = knot_tsig_rdata_alg(query->tsig_rr);
+ }
+
+ /* Log ACL details. */
+ char addr_str[SOCKADDR_STRLEN];
+ if (sockaddr_tostr(addr_str, sizeof(addr_str), query_source) <= 0) {
+ addr_str[0] = '\0';
+ }
+ knot_dname_txt_storage_t key_name;
+ if (knot_dname_to_str(key_name, tsig.name, sizeof(key_name)) == NULL) {
+ key_name[0] = '\0';
+ }
+ const knot_lookup_t *act = knot_lookup_by_id((knot_lookup_t *)acl_actions, action);
+
+ bool automatic = false;
+ bool allowed = false;
+
+ if (action != ACL_ACTION_UPDATE) {
+ // ACL_ACTION_QUERY is used for SOA/refresh query.
+ assert(action == ACL_ACTION_QUERY || action == ACL_ACTION_NOTIFY ||
+ action == ACL_ACTION_TRANSFER);
+ const yp_name_t *item = (action == ACL_ACTION_NOTIFY) ? C_MASTER : C_NOTIFY;
+ conf_val_t rmts = conf_zone_get(conf, item, zone_name);
+ allowed = rmt_allowed(conf, &rmts, query_source, &tsig);
+ automatic = allowed;
+ }
+ if (!allowed) {
+ conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name);
+ allowed = acl_allowed(conf, &acl, action, query_source, &tsig, zone_name, query);
+ }
+
+ log_zone_debug(zone_name,
+ "ACL, %s, action %s, remote %s, key %s%s%s%s",
+ allowed ? "allowed" : "denied",
+ (act != NULL) ? act->name : "query",
+ addr_str,
+ (key_name[0] != '\0') ? "'" : "",
+ (key_name[0] != '\0') ? key_name : "none",
+ (key_name[0] != '\0') ? "'" : "",
+ automatic ? ", automatic" : "");
+
+ /* Check if authorized. */
+ if (!allowed) {
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ qdata->rcode_tsig = KNOT_RCODE_BADKEY;
+ return false;
+ }
+
+ /* Remember used TSIG key. */
+ qdata->sign.tsig_key = tsig;
+
+ return true;
+}
+
+int process_query_verify(knotd_qdata_t *qdata)
+{
+ knot_pkt_t *query = qdata->query;
+ knot_sign_context_t *ctx = &qdata->sign;
+
+ /* NOKEY => no verification. */
+ if (query->tsig_rr == NULL) {
+ return KNOT_EOK;
+ }
+
+ /* Keep digest for signing response. */
+ /*! \note This memory will be rewritten for multi-pkt answers. */
+ ctx->tsig_digest = (uint8_t *)knot_tsig_rdata_mac(query->tsig_rr);
+ ctx->tsig_digestlen = knot_tsig_rdata_mac_length(query->tsig_rr);
+
+ /* Checking query. */
+ int ret = knot_tsig_server_check(query->tsig_rr, query->wire,
+ query->size, &ctx->tsig_key);
+
+ /* Evaluate TSIG check results. */
+ switch(ret) {
+ case KNOT_EOK:
+ qdata->rcode = KNOT_RCODE_NOERROR;
+ break;
+ case KNOT_TSIG_EBADKEY:
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ qdata->rcode_tsig = KNOT_RCODE_BADKEY;
+ break;
+ case KNOT_TSIG_EBADSIG:
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ qdata->rcode_tsig = KNOT_RCODE_BADSIG;
+ break;
+ case KNOT_TSIG_EBADTIME:
+ qdata->rcode = KNOT_RCODE_NOTAUTH;
+ qdata->rcode_tsig = KNOT_RCODE_BADTIME;
+ ctx->tsig_time_signed = knot_tsig_rdata_time_signed(query->tsig_rr);
+ break;
+ case KNOT_EMALF:
+ qdata->rcode = KNOT_RCODE_FORMERR;
+ break;
+ default:
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ break;
+ }
+
+ /* Log possible error. */
+ if (qdata->rcode == KNOT_RCODE_SERVFAIL) {
+ log_zone_error(qdata->extra->zone->name,
+ "TSIG, verification failed (%s)", knot_strerror(ret));
+ } else if (qdata->rcode != KNOT_RCODE_NOERROR) {
+ const knot_lookup_t *item = NULL;
+ if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
+ item = knot_lookup_by_id(knot_tsig_rcode_names, qdata->rcode_tsig);
+ if (item == NULL) {
+ item = knot_lookup_by_id(knot_rcode_names, qdata->rcode_tsig);
+ }
+ } else {
+ item = knot_lookup_by_id(knot_rcode_names, qdata->rcode);
+ }
+
+ char *key_name = knot_dname_to_str_alloc(ctx->tsig_key.name);
+ log_zone_debug(qdata->extra->zone->name,
+ "TSIG, key '%s', verification failed '%s'",
+ (key_name != NULL) ? key_name : "",
+ (item != NULL) ? item->name : "");
+ free(key_name);
+ }
+
+ return ret;
+}
+
+int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ if (pkt->size == 0) {
+ // Nothing to sign.
+ return KNOT_EOK;
+ }
+
+ int ret = KNOT_EOK;
+ knot_pkt_t *query = qdata->query;
+ knot_sign_context_t *ctx = &qdata->sign;
+
+ /* KEY provided and verified TSIG or BADTIME allows signing. */
+ if (ctx->tsig_key.name != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) {
+ /* Sign query response. */
+ size_t new_digest_len = dnssec_tsig_algorithm_size(ctx->tsig_key.algorithm);
+ if (ctx->pkt_count == 0) {
+ ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size,
+ ctx->tsig_digest, ctx->tsig_digestlen,
+ ctx->tsig_digest, &new_digest_len,
+ &ctx->tsig_key, qdata->rcode_tsig,
+ ctx->tsig_time_signed);
+ } else {
+ ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size,
+ ctx->tsig_digest, ctx->tsig_digestlen,
+ ctx->tsig_digest, &new_digest_len,
+ &ctx->tsig_key,
+ pkt->wire, pkt->size);
+ }
+ if (ret != KNOT_EOK) {
+ goto fail; /* Failed to sign. */
+ } else {
+ ++ctx->pkt_count;
+ }
+ } else {
+ /* Copy TSIG from query and set RCODE. */
+ if (query->tsig_rr && qdata->rcode_tsig != KNOT_RCODE_NOERROR) {
+ ret = knot_tsig_add(pkt->wire, &pkt->size, pkt->max_size,
+ qdata->rcode_tsig, query->tsig_rr);
+ if (ret != KNOT_EOK) {
+ goto fail; /* Whatever it is, it's server fail. */
+ }
+ }
+ }
+
+ return KNOT_EOK;
+
+ /* Server failure in signing. */
+fail:
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ qdata->rcode_tsig = KNOT_RCODE_NOERROR; /* Don't sign again. */
+ return ret;
+}
+
+/*! \brief Synthesize RRSIG for given parameters, store in 'qdata' for later use */
+static int put_rrsig(const knot_dname_t *sig_owner, uint16_t type,
+ const knot_rrset_t *rrsigs, knot_rrinfo_t *rrinfo,
+ uint32_t ttl_limit, knotd_qdata_t *qdata)
+{
+ knot_rdataset_t synth_rrs;
+ knot_rdataset_init(&synth_rrs);
+ assert(type != KNOT_RRTYPE_ANY);
+ int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrs, qdata->mm);
+ if (ret == KNOT_ENOENT) {
+ // No signature
+ return KNOT_EOK;
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Create rrsig info structure. */
+ struct rrsig_info *info = mm_alloc(qdata->mm, sizeof(struct rrsig_info));
+ if (info == NULL) {
+ knot_rdataset_clear(&synth_rrs, qdata->mm);
+ return KNOT_ENOMEM;
+ }
+
+ /* Store RRSIG into info structure. */
+ knot_dname_t *owner_copy = knot_dname_copy(sig_owner, qdata->mm);
+ if (owner_copy == NULL) {
+ mm_free(qdata->mm, info);
+ knot_rdataset_clear(&synth_rrs, qdata->mm);
+ return KNOT_ENOMEM;
+ }
+ uint32_t orig_ttl = knot_rrsig_original_ttl(synth_rrs.rdata);
+ knot_rrset_init(&info->synth_rrsig, owner_copy, rrsigs->type,
+ rrsigs->rclass, MIN(orig_ttl, ttl_limit));
+ /* Store filtered signature. */
+ info->synth_rrsig.rrs = synth_rrs;
+
+ info->rrinfo = rrinfo;
+ add_tail(&qdata->extra->rrsigs, &info->n);
+
+ return KNOT_EOK;
+}
+
+int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ const knot_rrset_t *rr, const knot_rrset_t *rrsigs,
+ uint16_t compr_hint, uint32_t flags)
+{
+ if (rr->rrs.count < 1) {
+ return KNOT_EMALF;
+ }
+
+ /* Wildcard expansion applies only for answers. */
+ bool expand = false;
+ if (pkt->current == KNOT_ANSWER) {
+ /* Expand if RR is wildcard. TRICK: if the asterix node is queried directly, we behave like if wildcard would be expanded. It's the same. */
+ expand = knot_dname_is_wildcard(rr->owner);
+ }
+
+ int ret = KNOT_EOK;
+
+ /* If we already have compressed name on the wire and compression hint,
+ * we can just insert RRSet and fake synthesis by using compression
+ * hint. */
+ knot_rrset_t to_add;
+ if (compr_hint == KNOT_COMPR_HINT_NONE && expand) {
+ knot_dname_t *qname_cpy = knot_dname_copy(qdata->name, &pkt->mm);
+ if (qname_cpy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_init(&to_add, qname_cpy, rr->type, rr->rclass, rr->ttl);
+ ret = knot_rdataset_copy(&to_add.rrs, &rr->rrs, &pkt->mm);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(qname_cpy, &pkt->mm);
+ return ret;
+ }
+ to_add.additional = rr->additional;
+ flags |= KNOT_PF_FREE;
+ } else {
+ to_add = *rr;
+ }
+
+ uint16_t rotate = conf()->cache.srv_ans_rotate ? knot_wire_get_id(qdata->query->wire) : 0;
+ uint16_t prev_count = pkt->rrset_count;
+ ret = knot_pkt_put_rotate(pkt, compr_hint, &to_add, rotate, flags);
+ if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) {
+ knot_rrset_clear(&to_add, &pkt->mm);
+ return ret;
+ }
+
+ uint32_t rrsig_ttl_limit = UINT32_MAX;
+ if ((flags & KNOT_PF_SOAMINTTL) && to_add.type == KNOT_RRTYPE_SOA) {
+ rrsig_ttl_limit = knot_soa_minimum(to_add.rrs.rdata);
+ }
+
+ const bool inserted = (prev_count != pkt->rrset_count);
+ if (inserted &&
+ !knot_rrset_empty(rrsigs) && rr->type != KNOT_RRTYPE_RRSIG) {
+ // Get rrinfo of just inserted RR.
+ knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count - 1];
+ ret = put_rrsig(rr->owner, rr->type, rrsigs, rrinfo, rrsig_ttl_limit, qdata);
+ }
+
+ return ret;
+}
+
+/*! \brief Module implementation. */
+const knot_layer_api_t *process_query_layer(void)
+{
+ static const knot_layer_api_t api = {
+ .begin = &process_query_begin,
+ .reset = &process_query_reset,
+ .finish = &process_query_finish,
+ .consume = &process_query_in,
+ .produce = &process_query_out,
+ };
+ return &api;
+}
diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h
new file mode 100644
index 0000000..bd7d42a
--- /dev/null
+++ b/src/knot/nameserver/process_query.h
@@ -0,0 +1,107 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/include/module.h"
+#include "knot/query/layer.h"
+#include "knot/updates/acl.h"
+#include "knot/zone/zone.h"
+
+/* Query processing module implementation. */
+const knot_layer_api_t *process_query_layer(void);
+
+/*! \brief Query processing intermediate data. */
+typedef struct knotd_qdata_extra {
+ zone_t *zone; /*!< Zone from which is answered. */
+ const zone_contents_t *contents; /*!< Zone contents from which is answered. */
+ list_t wildcards; /*!< Visited wildcards. */
+ list_t rrsigs; /*!< Section RRSIGs. */
+ uint8_t *opt_rr_pos; /*!< Place of the OPT RR in wire. */
+
+ /* Currently processed nodes. */
+ const zone_node_t *node, *encloser, *previous;
+
+ uint8_t cname_chain; /*!< Length of the CNAME chain so far. */
+
+ /* Extensions. */
+ void *ext;
+ void (*ext_cleanup)(knotd_qdata_t *); /*!< Extensions cleanup callback. */
+} knotd_qdata_extra_t;
+
+/*! \brief Visited wildcard node list. */
+struct wildcard_hit {
+ node_t n;
+ const zone_node_t *node; /* Visited node. */
+ const zone_node_t *prev; /* Previous node from the SNAME. */
+ const knot_dname_t *sname; /* Name leading to this node. */
+};
+
+/*! \brief RRSIG info node list. */
+struct rrsig_info {
+ node_t n;
+ knot_rrset_t synth_rrsig; /* Synthesized RRSIG. */
+ knot_rrinfo_t *rrinfo; /* RR info. */
+};
+
+/*!
+ * \brief Check current query against ACL.
+ *
+ * \param conf Configuration.
+ * \param action ACL action.
+ * \param qdata Query data.
+ * \return true if accepted, false if denied.
+ */
+bool process_query_acl_check(conf_t *conf, acl_action_t action,
+ knotd_qdata_t *qdata);
+
+/*!
+ * \brief Verify current query transaction security and update query data.
+ *
+ * \param qdata
+ * \retval KNOT_EOK
+ * \retval KNOT_TSIG_EBADKEY
+ * \retval KNOT_TSIG_EBADSIG
+ * \retval KNOT_TSIG_EBADTIME
+ * \retval (other generic errors)
+ */
+int process_query_verify(knotd_qdata_t *qdata);
+
+/*!
+ * \brief Sign current query using configured TSIG keys.
+ *
+ * \param pkt Outgoing message.
+ * \param qdata Query data.
+ *
+ * \retval KNOT_E*
+ */
+int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata);
+
+/*!
+ * \brief Puts RRSet to packet, will store its RRSIG for later use.
+ *
+ * \param pkt Packet to store RRSet into.
+ * \param qdata Query data structure.
+ * \param rr RRSet to be stored.
+ * \param rrsigs RRSIGs to be stored.
+ * \param compr_hint Compression hint.
+ * \param flags Flags.
+ *
+ * \return KNOT_E*
+ */
+int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ const knot_rrset_t *rr, const knot_rrset_t *rrsigs,
+ uint16_t compr_hint, uint32_t flags);
diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c
new file mode 100644
index 0000000..2837135
--- /dev/null
+++ b/src/knot/nameserver/query_module.c
@@ -0,0 +1,791 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/attribute.h"
+#include "libknot/probe/data.h"
+#include "libknot/xdp.h"
+#include "knot/common/log.h"
+#include "knot/conf/module.h"
+#include "knot/conf/tools.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-sign.h"
+#include "knot/nameserver/query_module.h"
+#include "knot/nameserver/process_query.h"
+
+#ifdef HAVE_ATOMIC
+ #define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_SUB(dst, val) __atomic_sub_fetch(&(dst), (val), __ATOMIC_RELAXED)
+ #define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED)
+#else
+ #warning "Statistics data can be inaccurate"
+ #define ATOMIC_ADD(dst, val) ((dst) += (val))
+ #define ATOMIC_SUB(dst, val) ((dst) -= (val))
+ #define ATOMIC_SET(dst, val) ((dst) = (val))
+#endif
+
+_public_
+int knotd_conf_check_ref(knotd_conf_check_args_t *args)
+{
+ return check_ref(args);
+}
+
+struct query_plan *query_plan_create(void)
+{
+ struct query_plan *plan = malloc(sizeof(struct query_plan));
+ if (plan == NULL) {
+ return NULL;
+ }
+
+ for (unsigned i = 0; i < KNOTD_STAGES; ++i) {
+ init_list(&plan->stage[i]);
+ }
+
+ return plan;
+}
+
+void query_plan_free(struct query_plan *plan)
+{
+ if (plan == NULL) {
+ return;
+ }
+
+ for (unsigned i = 0; i < KNOTD_STAGES; ++i) {
+ struct query_step *step, *next;
+ WALK_LIST_DELSAFE(step, next, plan->stage[i]) {
+ free(step);
+ }
+ }
+
+ free(plan);
+}
+
+static struct query_step *make_step(query_step_process_f process, void *ctx)
+{
+ struct query_step *step = calloc(1, sizeof(struct query_step));
+ if (step == NULL) {
+ return NULL;
+ }
+
+ step->process = process;
+ step->ctx = ctx;
+
+ return step;
+}
+
+int query_plan_step(struct query_plan *plan, knotd_stage_t stage,
+ query_step_process_f process, void *ctx)
+{
+ struct query_step *step = make_step(process, ctx);
+ if (step == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ add_tail(&plan->stage[stage], &step->node);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knotd_mod_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_hook_f hook)
+{
+ if (stage != KNOTD_STAGE_BEGIN && stage != KNOTD_STAGE_END) {
+ return KNOT_EINVAL;
+ }
+
+ return query_plan_step(mod->plan, stage, hook, mod);
+}
+
+_public_
+int knotd_mod_in_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_in_hook_f hook)
+{
+ if (stage == KNOTD_STAGE_BEGIN || stage == KNOTD_STAGE_END) {
+ return KNOT_EINVAL;
+ }
+
+ return query_plan_step(mod->plan, stage, hook, mod);
+}
+
+knotd_mod_t *query_module_open(conf_t *conf, server_t *server, conf_mod_id_t *mod_id,
+ struct query_plan *plan, const knot_dname_t *zone)
+{
+ if (conf == NULL || server == NULL || mod_id == NULL || plan == NULL) {
+ return NULL;
+ }
+
+ /* Locate the module. */
+ const module_t *mod = conf_mod_find(conf, mod_id->name + 1,
+ mod_id->name[0], false);
+ if (mod == NULL) {
+ return NULL;
+ }
+
+ /* Create query module. */
+ knotd_mod_t *module = calloc(1, sizeof(knotd_mod_t));
+ if (module == NULL) {
+ return NULL;
+ }
+
+ module->plan = plan;
+ module->config = conf;
+ module->server = server;
+ module->zone = zone;
+ module->id = mod_id;
+ module->api = mod->api;
+
+ return module;
+}
+
+static void module_reset(conf_t *conf, knotd_mod_t *module, struct query_plan *new_plan)
+{
+ // Keep ->node
+ module->config = conf;
+ // Keep ->server
+ // Keep ->id
+ module->plan = new_plan;
+ // Keep ->zone
+ // Keep ->api
+
+ // Reset DNSSEC
+ zone_sign_ctx_free(module->sign_ctx);
+ free_zone_keys(module->keyset);
+ free(module->keyset);
+ if (module->dnssec != NULL) {
+ kdnssec_ctx_deinit(module->dnssec);
+ free(module->dnssec);
+ }
+ module->dnssec = NULL;
+ module->keyset = NULL;
+ module->sign_ctx = NULL;
+
+ // Reset statistics
+ knotd_mod_stats_free(module);
+ module->stats_info = NULL;
+ module->stats_vals = NULL;
+ module->stats_count = 0;
+
+ // Keep ->ctx
+}
+
+void query_module_close(knotd_mod_t *module)
+{
+ if (module == NULL) {
+ return;
+ }
+
+ module_reset(NULL, module, NULL);
+ conf_free_mod_id(module->id);
+ free(module);
+}
+
+void query_module_reset(conf_t *conf, knotd_mod_t *module, struct query_plan *new_plan)
+{
+ if (module == NULL) {
+ return;
+ }
+
+ module_reset(conf, module, new_plan);
+}
+
+_public_
+void *knotd_mod_ctx(knotd_mod_t *mod)
+{
+ return (mod != NULL) ? mod->ctx : NULL;
+}
+
+_public_
+void knotd_mod_ctx_set(knotd_mod_t *mod, void *ctx)
+{
+ if (mod != NULL) mod->ctx = ctx;
+}
+
+_public_
+const knot_dname_t *knotd_mod_zone(knotd_mod_t *mod)
+{
+ return (mod != NULL) ? mod->zone : NULL;
+}
+
+_public_
+void knotd_mod_log(knotd_mod_t *mod, int priority, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ knotd_mod_vlog(mod, priority, fmt, args);
+ va_end(args);
+}
+
+_public_
+void knotd_mod_vlog(knotd_mod_t *mod, int priority, const char *fmt, va_list args)
+{
+ if (mod == NULL || fmt == NULL) {
+ return;
+ }
+
+ char msg[512];
+
+ if (vsnprintf(msg, sizeof(msg), fmt, args) < 0) {
+ msg[0] = '\0';
+ }
+
+ #define LOG_ARGS(mod_id, msg) "module '%s%s%.*s', %s", \
+ mod_id->name + 1, (mod_id->len > 0) ? "/" : "", (int)mod_id->len, \
+ mod_id->data, msg
+
+ if (mod->zone == NULL) {
+ log_fmt(priority, LOG_SOURCE_SERVER, LOG_ARGS(mod->id, msg));
+ } else {
+ log_fmt_zone(priority, LOG_SOURCE_ZONE, mod->zone, NULL,
+ LOG_ARGS(mod->id, msg));
+ }
+
+ #undef LOG_ARGS
+}
+
+_public_
+int knotd_mod_stats_add(knotd_mod_t *mod, const char *ctr_name, uint32_t idx_count,
+ knotd_mod_idx_to_str_f idx_to_str)
+{
+ if (mod == NULL || idx_count == 0) {
+ return KNOT_EINVAL;
+ }
+
+ unsigned threads = knotd_mod_threads(mod);
+
+ mod_ctr_t *stats = NULL;
+ uint32_t offset = 0;
+ if (mod->stats_info == NULL) {
+ assert(mod->stats_count == 0);
+ stats = malloc(sizeof(*stats));
+ if (stats == NULL) {
+ return KNOT_ENOMEM;
+ }
+ mod->stats_info = stats;
+
+ assert(mod->stats_vals == NULL);
+ mod->stats_vals = calloc(threads, sizeof(*mod->stats_vals));
+ if (mod->stats_vals == NULL) {
+ knotd_mod_stats_free(mod);
+ return KNOT_ENOMEM;
+ }
+
+ for (unsigned i = 0; i < threads; i++) {
+ mod->stats_vals[i] = calloc(idx_count, sizeof(**mod->stats_vals));
+ if (mod->stats_vals[i] == NULL) {
+ knotd_mod_stats_free(mod);
+ return KNOT_ENOMEM;
+ }
+ }
+ } else {
+ for (uint32_t i = 0; i < mod->stats_count; i++) {
+ offset += mod->stats_info[i].count;
+ }
+ assert(offset == mod->stats_info[mod->stats_count - 1].offset +
+ mod->stats_info[mod->stats_count - 1].count);
+
+ assert(mod->stats_count > 0);
+ size_t old_size = mod->stats_count * sizeof(*stats);
+ size_t new_size = old_size + sizeof(*stats);
+ stats = realloc(mod->stats_info, new_size);
+ if (stats == NULL) {
+ knotd_mod_stats_free(mod);
+ return KNOT_ENOMEM;
+ }
+ mod->stats_info = stats;
+ stats += mod->stats_count;
+
+ for (unsigned i = 0; i < threads; i++) {
+ uint64_t *new_vals = realloc(mod->stats_vals[i],
+ (offset + idx_count) * sizeof(*new_vals));
+ if (new_vals == NULL) {
+ knotd_mod_stats_free(mod);
+ return KNOT_ENOMEM;
+ }
+ mod->stats_vals[i] = new_vals;
+ new_vals += offset;
+ for (uint32_t j = 0; j < idx_count; j++) {
+ *new_vals++ = 0;
+ }
+ }
+ }
+
+ stats->name = ctr_name;
+ stats->count = idx_count;
+ stats->idx_to_str = idx_to_str;
+ stats->offset = offset;
+
+ mod->stats_count++;
+
+ return KNOT_EOK;
+}
+
+_public_
+void knotd_mod_stats_free(knotd_mod_t *mod)
+{
+ if (mod == NULL || mod->stats_info == NULL) {
+ return;
+ }
+
+ if (mod->stats_vals != NULL) {
+ unsigned threads = knotd_mod_threads(mod);
+ for (unsigned i = 0; i < threads; i++) {
+ free(mod->stats_vals[i]);
+ }
+ }
+
+ free(mod->stats_vals);
+ free(mod->stats_info);
+}
+
+#define STATS_BODY(OPERATION) { \
+ if (mod == NULL) return; \
+ \
+ mod_ctr_t *ctr = mod->stats_info + ctr_id; \
+ assert(idx < ctr->count); \
+ OPERATION(mod->stats_vals[thr_id][ctr->offset + idx], val); \
+}
+
+_public_
+void knotd_mod_stats_incr(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val)
+{
+ STATS_BODY(ATOMIC_ADD)
+}
+
+_public_
+void knotd_mod_stats_decr(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val)
+{
+ STATS_BODY(ATOMIC_SUB)
+}
+
+_public_
+void knotd_mod_stats_store(knotd_mod_t *mod, unsigned thr_id, uint32_t ctr_id,
+ uint32_t idx, uint64_t val)
+{
+ STATS_BODY(ATOMIC_SET)
+}
+
+_public_
+knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env)
+{
+ static const char *version = "Knot DNS " PACKAGE_VERSION;
+
+ knotd_conf_t out = { { 0 } };
+
+ if (mod == NULL) {
+ return out;
+ }
+
+ conf_t *config = (mod->config != NULL) ? mod->config : conf();
+
+ switch (env) {
+ case KNOTD_CONF_ENV_VERSION:
+ out.single.string = version;
+ break;
+ case KNOTD_CONF_ENV_HOSTNAME:
+ out.single.string = config->hostname;
+ break;
+ case KNOTD_CONF_ENV_WORKERS_UDP:
+ out.single.integer = config->cache.srv_udp_threads;
+ break;
+ case KNOTD_CONF_ENV_WORKERS_TCP:
+ out.single.integer = config->cache.srv_tcp_threads;
+ break;
+ case KNOTD_CONF_ENV_WORKERS_XDP:
+ out.single.integer = config->cache.srv_xdp_threads;
+ break;
+ default:
+ return out;
+ }
+
+ out.count = 1;
+
+ return out;
+}
+
+_public_
+unsigned knotd_mod_threads(knotd_mod_t *mod)
+{
+ knotd_conf_t udp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_UDP);
+ knotd_conf_t xdp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_XDP);
+ knotd_conf_t tcp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_TCP);
+ return udp.single.integer + xdp.single.integer + tcp.single.integer;
+}
+
+static void set_val(yp_type_t type, knotd_conf_val_t *item, conf_val_t *val)
+{
+ switch (type) {
+ case YP_TINT:
+ item->integer = conf_int(val);
+ break;
+ case YP_TBOOL:
+ item->boolean = conf_bool(val);
+ break;
+ case YP_TOPT:
+ item->option = conf_opt(val);
+ break;
+ case YP_TSTR:
+ item->string = conf_str(val);
+ break;
+ case YP_TDNAME:
+ item->dname = conf_dname(val);
+ break;
+ case YP_TADDR:
+ item->addr = conf_addr(val, NULL);
+ break;
+ case YP_TNET:
+ item->addr = conf_addr_range(val, &item->addr_max,
+ &item->addr_mask);
+ break;
+ case YP_TREF:
+ if (val->code == KNOT_EOK) {
+ conf_val(val);
+ item->data_len = val->len;
+ item->data = val->data;
+ }
+ break;
+ case YP_THEX:
+ case YP_TB64:
+ item->data = conf_bin(val, &item->data_len);
+ break;
+ case YP_TDATA:
+ item->data = conf_data(val, &item->data_len);
+ break;
+ default:
+ return;
+ }
+}
+
+static void set_conf_out(knotd_conf_t *out, conf_val_t *val)
+{
+ if (!(val->item->flags & YP_FMULTI)) {
+ out->count = (val->code == KNOT_EOK) ? 1 : 0;
+ set_val(val->item->type, &out->single, val);
+ } else {
+ size_t count = conf_val_count(val);
+ if (count == 0) {
+ return;
+ }
+
+ out->multi = malloc(count * sizeof(*out->multi));
+ if (out->multi == NULL) {
+ return;
+ }
+ memset(out->multi, 0, count * sizeof(*out->multi));
+
+ for (size_t i = 0; i < count; i++) {
+ set_val(val->item->type, &out->multi[i], val);
+ conf_val_next(val);
+ }
+ out->count = count;
+ }
+}
+
+_public_
+knotd_conf_t knotd_conf(knotd_mod_t *mod, const yp_name_t *section_name,
+ const yp_name_t *item_name, const knotd_conf_t *id)
+{
+ knotd_conf_t out = { { 0 } };
+
+ if (mod == NULL || section_name == NULL || item_name == NULL) {
+ return out;
+ }
+
+ conf_t *config = (mod->config != NULL) ? mod->config : conf();
+
+ conf_val_t val;
+ if (id != NULL) {
+ val = conf_rawid_get(config, section_name, item_name,
+ id->single.data, id->single.data_len);
+ } else {
+ val = conf_get(config, section_name, item_name);
+ }
+
+ set_conf_out(&out, &val);
+
+ return out;
+}
+
+_public_
+knotd_conf_t knotd_conf_mod(knotd_mod_t *mod, const yp_name_t *item_name)
+{
+ knotd_conf_t out = { { 0 } };
+
+ if (mod == NULL || item_name == NULL) {
+ return out;
+ }
+
+ conf_t *config = (mod->config != NULL) ? mod->config : conf();
+
+ conf_val_t val = conf_mod_get(config, item_name, mod->id);
+ if (val.item == NULL) {
+ return out;
+ }
+
+ set_conf_out(&out, &val);
+
+ return out;
+}
+
+_public_
+knotd_conf_t knotd_conf_zone(knotd_mod_t *mod, const yp_name_t *item_name,
+ const knot_dname_t *zone)
+{
+ knotd_conf_t out = { { 0 } };
+
+ if (mod == NULL || item_name == NULL || zone == NULL) {
+ return out;
+ }
+
+ conf_t *config = (mod->config != NULL) ? mod->config : conf();
+
+ conf_val_t val = conf_zone_get(config, item_name, zone);
+
+ set_conf_out(&out, &val);
+
+ return out;
+}
+
+_public_
+knotd_conf_t knotd_conf_check_item(knotd_conf_check_args_t *args,
+ const yp_name_t *item_name)
+{
+ knotd_conf_t out = { { 0 } };
+
+ conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn,
+ args->item->name, item_name,
+ args->id, args->id_len);
+
+ set_conf_out(&out, &val);
+
+ return out;
+}
+
+_public_
+bool knotd_conf_addr_range_match(const knotd_conf_t *range,
+ const struct sockaddr_storage *addr)
+{
+ if (range == NULL || addr == NULL) {
+ return false;
+ }
+
+ for (size_t i = 0; i < range->count; i++) {
+ knotd_conf_val_t *val = &range->multi[i];
+ if (val->addr_max.ss_family == AF_UNSPEC) {
+ if (sockaddr_net_match(addr, &val->addr, val->addr_mask)) {
+ return true;
+ }
+ } else {
+ if (sockaddr_range_match(addr, &val->addr, &val->addr_max)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+_public_
+void knotd_conf_free(knotd_conf_t *conf)
+{
+ if (conf == NULL) {
+ return;
+ }
+
+ if (conf->count > 0 && conf->multi != NULL) {
+ memset(conf->multi, 0, conf->count * sizeof(*conf->multi));
+ free(conf->multi);
+ }
+ memset(conf, 0, sizeof(*conf));
+}
+
+_public_
+const struct sockaddr_storage *knotd_qdata_local_addr(knotd_qdata_t *qdata,
+ struct sockaddr_storage *buff)
+{
+ if (qdata == NULL) {
+ return NULL;
+ }
+
+ if (qdata->params->xdp_msg != NULL) {
+#ifdef ENABLE_XDP
+ return (struct sockaddr_storage *)&qdata->params->xdp_msg->ip_to;
+#else
+ assert(0);
+ return NULL;
+#endif
+ } else {
+ socklen_t buff_len = sizeof(*buff);
+ if (getsockname(qdata->params->socket, (struct sockaddr *)buff,
+ &buff_len) != 0) {
+ return NULL;
+ }
+ return buff;
+ }
+}
+
+_public_
+const struct sockaddr_storage *knotd_qdata_remote_addr(knotd_qdata_t *qdata)
+{
+ if (qdata == NULL) {
+ return NULL;
+ }
+
+ if (qdata->params->xdp_msg != NULL) {
+#ifdef ENABLE_XDP
+ return (struct sockaddr_storage *)&qdata->params->xdp_msg->ip_from;
+#else
+ assert(0);
+ return NULL;
+#endif
+ } else {
+ return qdata->params->remote;
+ }
+}
+
+_public_
+uint32_t knotd_qdata_rtt(knotd_qdata_t *qdata)
+{
+ if (qdata == NULL) {
+ return 0;
+ }
+
+ switch (qdata->params->proto) {
+ case KNOTD_QUERY_PROTO_TCP:
+ if (qdata->params->xdp_msg != NULL) {
+#ifdef ENABLE_XDP
+ return qdata->params->measured_rtt;
+#else
+ assert(0);
+ return 0;
+#endif
+ } else {
+ return knot_probe_tcp_rtt(qdata->params->socket);
+ }
+ case KNOTD_QUERY_PROTO_QUIC:
+ return qdata->params->measured_rtt;
+ case KNOTD_QUERY_PROTO_UDP:
+ default:
+ return 0;
+ }
+}
+
+_public_
+const knot_dname_t *knotd_qdata_zone_name(knotd_qdata_t *qdata)
+{
+ if (qdata == NULL || qdata->extra->zone == NULL) {
+ return NULL;
+ }
+
+ return qdata->extra->zone->name;
+}
+
+_public_
+knot_rrset_t knotd_qdata_zone_apex_rrset(knotd_qdata_t *qdata, uint16_t type)
+{
+ if (qdata == NULL || qdata->extra->contents == NULL) {
+ return node_rrset(NULL, type);
+ }
+
+ return node_rrset(qdata->extra->contents->apex, type);
+}
+
+_public_
+int knotd_mod_dnssec_init(knotd_mod_t *mod)
+{
+ if (mod == NULL || mod->dnssec != NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_lmdb_db_t *kaspdb = &mod->server->kaspdb;
+ kasp_db_ensure_init(kaspdb, mod->config); // probably redundant
+
+ mod->dnssec = calloc(1, sizeof(*(mod->dnssec)));
+ if (mod->dnssec == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ conf_val_t conf = conf_zone_get(mod->config, C_DNSSEC_SIGNING, mod->zone);
+ int ret = kdnssec_ctx_init(mod->config, mod->dnssec, mod->zone, kaspdb,
+ conf_bool(&conf) ? NULL : mod->id);
+ if (ret != KNOT_EOK) {
+ free(mod->dnssec);
+ mod->dnssec = NULL;
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knotd_mod_dnssec_load_keyset(knotd_mod_t *mod, bool verbose)
+{
+ if (mod == NULL || mod->dnssec == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ mod->keyset = calloc(1, sizeof(*(mod->keyset)));
+ if (mod->keyset == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = load_zone_keys(mod->dnssec, mod->keyset, verbose);
+ if (ret != KNOT_EOK) {
+ free(mod->keyset);
+ mod->keyset = NULL;
+ return ret;
+ }
+
+ mod->sign_ctx = zone_sign_ctx(mod->keyset, mod->dnssec);
+ if (mod->sign_ctx == NULL) {
+ free_zone_keys(mod->keyset);
+ free(mod->keyset);
+ mod->keyset = NULL;
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+void knotd_mod_dnssec_unload_keyset(knotd_mod_t *mod)
+{
+ if (mod != NULL && mod->keyset != NULL) {
+ zone_sign_ctx_free(mod->sign_ctx);
+ mod->sign_ctx = NULL;
+
+ free_zone_keys(mod->keyset);
+ free(mod->keyset);
+ mod->keyset = NULL;
+ }
+}
+
+_public_
+int knotd_mod_dnssec_sign_rrset(knotd_mod_t *mod, knot_rrset_t *rrsigs,
+ const knot_rrset_t *rrset, knot_mm_t *mm)
+{
+ if (mod == NULL || rrsigs == NULL || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return knot_sign_rrset2(rrsigs, rrset, mod->sign_ctx, mm);
+}
diff --git a/src/knot/nameserver/query_module.h b/src/knot/nameserver/query_module.h
new file mode 100644
index 0000000..5cc905b
--- /dev/null
+++ b/src/knot/nameserver/query_module.h
@@ -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/>.
+ */
+
+#pragma once
+
+#include "libknot/libknot.h"
+#include "knot/conf/conf.h"
+#include "knot/dnssec/context.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/include/module.h"
+#include "knot/server/server.h"
+#include "contrib/ucw/lists.h"
+
+#ifdef HAVE_ATOMIC
+ #define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED)
+#else
+ #define ATOMIC_GET(src) (src)
+#endif
+
+#define KNOTD_STAGES (KNOTD_STAGE_END + 1)
+
+typedef unsigned (*query_step_process_f)
+ (unsigned state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod);
+
+/*! \brief Single processing step in query processing. */
+struct query_step {
+ node_t node;
+ void *ctx;
+ query_step_process_f process;
+};
+
+/*! Query plan represents a sequence of steps needed for query processing
+ * divided into several stages, where each stage represents a current response
+ * assembly phase, for example 'before processing', 'answer section' and so on.
+ */
+struct query_plan {
+ list_t stage[KNOTD_STAGES];
+};
+
+/*! \brief Create an empty query plan. */
+struct query_plan *query_plan_create(void);
+
+/*! \brief Free query plan and all planned steps. */
+void query_plan_free(struct query_plan *plan);
+
+/*! \brief Plan another step for given stage. */
+int query_plan_step(struct query_plan *plan, knotd_stage_t stage,
+ query_step_process_f process, void *ctx);
+
+/*! \brief Open query module identified by name. */
+knotd_mod_t *query_module_open(conf_t *conf, server_t *server, conf_mod_id_t *mod_id,
+ struct query_plan *plan, const knot_dname_t *zone);
+
+/*! \brief Close query module. */
+void query_module_close(knotd_mod_t *module);
+
+/*! \brief Close and open existing query module. */
+void query_module_reset(conf_t *conf, knotd_mod_t *module, struct query_plan *new_plan);
+
+typedef char* (*mod_idx_to_str_f)(uint32_t idx, uint32_t count);
+
+typedef struct {
+ const char *name;
+ mod_idx_to_str_f idx_to_str; // unused if count == 1
+ uint32_t offset; // offset of counters in stats_vals[thread_id]
+ uint32_t count;
+} mod_ctr_t;
+
+struct knotd_mod {
+ node_t node;
+ conf_t *config;
+ server_t *server;
+ conf_mod_id_t *id;
+ struct query_plan *plan;
+ const knot_dname_t *zone;
+ const knotd_mod_api_t *api;
+ kdnssec_ctx_t *dnssec;
+ zone_keyset_t *keyset;
+ zone_sign_ctx_t *sign_ctx;
+ mod_ctr_t *stats_info;
+ uint64_t **stats_vals;
+ uint32_t stats_count;
+ void *ctx;
+};
+
+void knotd_mod_stats_free(knotd_mod_t *mod);
diff --git a/src/knot/nameserver/tsig_ctx.c b/src/knot/nameserver/tsig_ctx.c
new file mode 100644
index 0000000..05383b1
--- /dev/null
+++ b/src/knot/nameserver/tsig_ctx.c
@@ -0,0 +1,189 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+
+#include "knot/nameserver/tsig_ctx.h"
+#include "contrib/string.h"
+#include "libknot/libknot.h"
+
+/*!
+ * Maximal total size for unsigned messages.
+ */
+static const size_t TSIG_BUFFER_MAX_SIZE = (UINT16_MAX * 100);
+
+void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key)
+{
+ if (!ctx) {
+ return;
+ }
+
+ memzero(ctx, sizeof(*ctx));
+ ctx->key = key;
+}
+
+void tsig_cleanup(tsig_ctx_t *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ free(ctx->buffer);
+ memzero(ctx, sizeof(*ctx));
+}
+
+void tsig_reset(tsig_ctx_t *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ const knot_tsig_key_t *backup = ctx->key;
+ tsig_cleanup(ctx);
+ tsig_init(ctx, backup);
+}
+
+int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
+{
+ if (!ctx || !packet) {
+ return KNOT_EINVAL;
+ }
+
+ if (ctx->key == NULL) {
+ return KNOT_EOK;
+ }
+
+ int ret = KNOT_ERROR;
+ if (ctx->digest_size == 0) {
+ ctx->digest_size = dnssec_tsig_algorithm_size(ctx->key->algorithm);
+ ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size,
+ NULL, 0,
+ ctx->digest, &ctx->digest_size,
+ ctx->key, 0, 0);
+ } else {
+ uint8_t previous_digest[ctx->digest_size];
+ memcpy(previous_digest, ctx->digest, ctx->digest_size);
+
+ ret = knot_tsig_sign_next(packet->wire, &packet->size, packet->max_size,
+ previous_digest, ctx->digest_size,
+ ctx->digest, &ctx->digest_size,
+ ctx->key, packet->wire, packet->size);
+ }
+
+ return ret;
+}
+
+static int update_ctx_after_verify(tsig_ctx_t *ctx, knot_rrset_t *tsig_rr)
+{
+ assert(ctx);
+ assert(tsig_rr);
+
+ if (ctx->digest_size != knot_tsig_rdata_mac_length(tsig_rr)) {
+ return KNOT_EMALF;
+ }
+
+ memcpy(ctx->digest, knot_tsig_rdata_mac(tsig_rr), ctx->digest_size);
+ ctx->prev_signed_time = knot_tsig_rdata_time_signed(tsig_rr);
+ ctx->unsigned_count = 0;
+ ctx->buffer_used = 0;
+
+ return KNOT_EOK;
+}
+
+static int buffer_add_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
+{
+ size_t need = ctx->buffer_used + packet->size;
+
+ // Inflate the buffer if necessary.
+
+ if (need > TSIG_BUFFER_MAX_SIZE) {
+ return KNOT_ENOMEM;
+ }
+
+ if (need > ctx->buffer_size) {
+ uint8_t *buffer = realloc(ctx->buffer, need);
+ if (!buffer) {
+ return KNOT_ENOMEM;
+ }
+
+ ctx->buffer = buffer;
+ ctx->buffer_size = need;
+ }
+
+ // Buffer the packet.
+
+ uint8_t *write = ctx->buffer + ctx->buffer_used;
+ memcpy(write, packet->wire, packet->size);
+ ctx->buffer_used = need;
+
+ return KNOT_EOK;
+}
+
+int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet)
+{
+ if (!ctx || !packet) {
+ return KNOT_EINVAL;
+ }
+
+ if (ctx->key == NULL) {
+ return KNOT_EOK;
+ }
+
+ int ret = buffer_add_packet(ctx, packet);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Unsigned packet.
+
+ if (packet->tsig_rr == NULL) {
+ ctx->unsigned_count += 1;
+ return KNOT_EOK;
+ }
+
+ // Signed packet.
+
+ if (ctx->prev_signed_time == 0) {
+ ret = knot_tsig_client_check(packet->tsig_rr, ctx->buffer,
+ ctx->buffer_used, ctx->digest,
+ ctx->digest_size, ctx->key, 0);
+ } else {
+ ret = knot_tsig_client_check_next(packet->tsig_rr, ctx->buffer,
+ ctx->buffer_used, ctx->digest,
+ ctx->digest_size, ctx->key,
+ ctx->prev_signed_time);
+ }
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = update_ctx_after_verify(ctx, packet->tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+unsigned tsig_unsigned_count(tsig_ctx_t *ctx)
+{
+ if (!ctx) {
+ return -1;
+ }
+
+ return ctx->unsigned_count;
+}
diff --git a/src/knot/nameserver/tsig_ctx.h b/src/knot/nameserver/tsig_ctx.h
new file mode 100644
index 0000000..3e91671
--- /dev/null
+++ b/src/knot/nameserver/tsig_ctx.h
@@ -0,0 +1,97 @@
+/* Copyright (C) 2015 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/packet/pkt.h"
+#include "libknot/tsig.h"
+
+#define TSIG_MAX_DIGEST_SIZE 64
+
+/*!
+ \brief TSIG context.
+ */
+typedef struct tsig_ctx {
+ const knot_tsig_key_t *key;
+ uint64_t prev_signed_time;
+
+ uint8_t digest[TSIG_MAX_DIGEST_SIZE];
+ size_t digest_size;
+
+ /* Unsigned packets handling. */
+ unsigned unsigned_count;
+ uint8_t *buffer;
+ size_t buffer_used;
+ size_t buffer_size;
+} tsig_ctx_t;
+
+/*!
+ * \brief Initialize TSIG context.
+ *
+ * \param ctx TSIG context to be initialized.
+ * \param key Key to be used for signing. If NULL, all performed operations
+ * will do nothing and always successful.
+ */
+void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key);
+
+/*!
+ * \brief Cleanup TSIG context.
+ *
+ * \param ctx TSIG context to be cleaned up.
+ */
+void tsig_cleanup(tsig_ctx_t *ctx);
+
+/*!
+ * \brief Reset TSIG context for new message exchange.
+ */
+void tsig_reset(tsig_ctx_t *ctx);
+
+/*!
+ * \brief Sign outgoing packet.
+ *
+ * \param ctx TSIG signing context.
+ * \param packet Packet to be signed.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet);
+
+/*!
+ * \brief Verify incoming packet.
+ *
+ * If the packet is not signed, the function will succeed, but an internal
+ * counter of unsigned packets is increased. When a packet is signed, the
+ * same counter is reset to zero.
+ *
+ * \see tsig_unsigned_count
+ *
+ * \param ctx TSIG signing context.
+ * \param packet Packet to be verified.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet);
+
+/*!
+ * \brief Get number of unsigned packets since the last signed one.
+ *
+ * \param ctx TSIG signing context.
+ *
+ * \return Number of unsigned packets since the last signed one.
+ */
+unsigned tsig_unsigned_count(tsig_ctx_t *ctx);
diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c
new file mode 100644
index 0000000..f43e1af
--- /dev/null
+++ b/src/knot/nameserver/update.c
@@ -0,0 +1,107 @@
+/* Copyright (C) 2021 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 <unistd.h>
+
+#include "knot/dnssec/zone-events.h"
+#include "knot/nameserver/internet.h"
+#include "knot/nameserver/update.h"
+#include "knot/query/requestor.h"
+#include "libknot/libknot.h"
+
+static int update_enqueue(zone_t *zone, knotd_qdata_t *qdata)
+{
+ assert(zone);
+ assert(qdata);
+
+ /* Create serialized request. */
+ knot_request_t *req = calloc(1, sizeof(*req));
+ if (req == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Store socket and remote address. */
+ req->fd = dup(qdata->params->socket);
+ memcpy(&req->remote, knotd_qdata_remote_addr(qdata), sizeof(req->remote));
+
+ /* Store update request. */
+ req->query = knot_pkt_new(NULL, qdata->query->max_size, NULL);
+ int ret = knot_pkt_copy(req->query, qdata->query);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(req->query);
+ free(req);
+ return ret;
+ }
+
+ /* Store and update possible TSIG context (see NS_NEED_AUTH). */
+ if (qdata->sign.tsig_key.name != NULL) {
+ req->sign = qdata->sign;
+ req->sign.tsig_digest = (uint8_t *)knot_tsig_rdata_mac(req->query->tsig_rr);
+ req->sign.tsig_key.name = req->query->tsig_rr->owner;
+ ret = dnssec_binary_dup(&qdata->sign.tsig_key.secret, &req->sign.tsig_key.secret);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(req->query);
+ free(req);
+ return ret;
+ }
+ assert(req->sign.tsig_digestlen == knot_tsig_rdata_mac_length(req->query->tsig_rr));
+ assert(req->sign.tsig_key.algorithm == knot_tsig_rdata_alg(req->query->tsig_rr));
+ }
+
+ pthread_mutex_lock(&zone->ddns_lock);
+
+ /* Enqueue created request. */
+ ptrlist_add(&zone->ddns_queue, req, NULL);
+ ++zone->ddns_queue_size;
+
+ pthread_mutex_unlock(&zone->ddns_lock);
+
+ /* Schedule UPDATE event. */
+ zone_events_schedule_now(zone, ZONE_EVENT_UPDATE);
+
+ return KNOT_EOK;
+}
+
+int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata)
+{
+ /* DDNS over XDP not supported. */
+ if (qdata->params->xdp_msg != NULL) {
+ qdata->rcode = KNOT_RCODE_SERVFAIL;
+ return KNOT_STATE_FAIL;
+ }
+
+ /* RFC1996 require SOA question. */
+ NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR);
+
+ /* Check valid zone. */
+ NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH);
+
+ /* Need valid transaction security. */
+ NS_NEED_AUTH(qdata, ACL_ACTION_UPDATE);
+ /* Check expiration. */
+ NS_NEED_ZONE_CONTENTS(qdata);
+ /* Check frozen zone. */
+ NS_NEED_NOT_FROZEN(qdata);
+
+ /* Store update into DDNS queue. */
+ int ret = update_enqueue((zone_t *)qdata->extra->zone, qdata);
+ if (ret != KNOT_EOK) {
+ return KNOT_STATE_FAIL;
+ }
+
+ /* No immediate response. */
+ return KNOT_STATE_NOOP;
+}
diff --git a/src/knot/nameserver/update.h b/src/knot/nameserver/update.h
new file mode 100644
index 0000000..609acd9
--- /dev/null
+++ b/src/knot/nameserver/update.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "knot/nameserver/process_query.h"
+
+/*!
+ * \brief UPDATE query processing module.
+ *
+ * \return KNOT_STATE_* processing states
+ */
+int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata);
diff --git a/src/knot/nameserver/xfr.c b/src/knot/nameserver/xfr.c
new file mode 100644
index 0000000..b54a4ff
--- /dev/null
+++ b/src/knot/nameserver/xfr.c
@@ -0,0 +1,96 @@
+/* Copyright (C) 2018 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 "knot/nameserver/xfr.h"
+#include "contrib/mempattern.h"
+
+int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb put, knotd_qdata_t *qdata)
+{
+ if (pkt == NULL || qdata == NULL || qdata->extra->ext == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ knot_mm_t *mm = qdata->mm;
+ struct xfr_proc *xfer = qdata->extra->ext;
+
+ /* Check if the zone wasn't expired during multi-message transfer. */
+ const zone_contents_t *contents = qdata->extra->contents;
+ if (contents == NULL) {
+ return KNOT_ENOZONE;
+ }
+ knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
+
+ /* Prepend SOA on first packet. */
+ if (xfer->stats.messages == 0) {
+ ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ /* Process all items in the list. */
+ while (!EMPTY_LIST(xfer->nodes)) {
+ ptrnode_t *head = HEAD(xfer->nodes);
+ ret = put(pkt, head->d, xfer);
+ if (ret == KNOT_EOK) { /* Finished. */
+ /* Complete change set. */
+ rem_node((node_t *)head);
+ mm_free(mm, head);
+ } else { /* Packet full or other error. */
+ break;
+ }
+ }
+
+ /* Append SOA on last packet. */
+ if (ret == KNOT_EOK) {
+ ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC);
+ }
+
+ /* Update counters. */
+ xfr_stats_add(&xfer->stats, pkt->size + knot_rrset_size(&qdata->opt_rr));
+
+ /* If a rrset is larger than the message,
+ * fail to avoid infinite loop of empty messages */
+ if (ret == KNOT_ESPACE && pkt->rrset_count < 1) {
+ return KNOT_ENOXFR;
+ }
+
+ return ret;
+}
+
+void xfr_stats_begin(struct xfr_stats *stats)
+{
+ assert(stats);
+
+ memset(stats, 0, sizeof(*stats));
+ stats->begin = time_now();
+}
+
+void xfr_stats_add(struct xfr_stats *stats, unsigned bytes)
+{
+ assert(stats);
+
+ stats->messages += 1;
+ stats->bytes += bytes;
+}
+
+void xfr_stats_end(struct xfr_stats *stats)
+{
+ assert(stats);
+
+ stats->end = time_now();
+}
diff --git a/src/knot/nameserver/xfr.h b/src/knot/nameserver/xfr.h
new file mode 100644
index 0000000..3347304
--- /dev/null
+++ b/src/knot/nameserver/xfr.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "contrib/time.h"
+#include "contrib/ucw/lists.h"
+#include "knot/nameserver/log.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/zone/contents.h"
+#include "libknot/packet/pkt.h"
+
+struct xfr_stats {
+ unsigned messages;
+ unsigned bytes;
+ struct timespec begin;
+ struct timespec end;
+};
+
+void xfr_stats_begin(struct xfr_stats *stats);
+void xfr_stats_add(struct xfr_stats *stats, unsigned bytes);
+void xfr_stats_end(struct xfr_stats *stats);
+
+static inline
+void xfr_log_finished(const knot_dname_t *zone, log_operation_t op,
+ log_direction_t dir, const struct sockaddr *remote,
+ bool reused, const struct xfr_stats *stats)
+{
+ ns_log(LOG_INFO, zone, op, dir, remote, reused,
+ "finished, %0.2f seconds, %u messages, %u bytes",
+ time_diff_ms(&stats->begin, &stats->end) / 1000.0,
+ stats->messages, stats->bytes);
+}
+
+/*!
+ * \brief Generic transfer processing state.
+ */
+struct xfr_proc {
+ list_t nodes; //!< Items to process (ptrnode_t).
+ zone_contents_t *contents; //!< Processed zone.
+ struct xfr_stats stats; //!< Packet transfer statistics.
+};
+
+/*!
+ * \brief Generic transfer processing.
+ *
+ * \return KNOT_EOK or an error
+ */
+typedef int (*xfr_put_cb)(knot_pkt_t *pkt, const void *item, struct xfr_proc *xfer);
+
+/*!
+ * \brief Put all items from xfr_proc.nodes to packet using a callback function.
+ *
+ * \note qdata->extra->ext points to struct xfr_proc* (this is xfer-specific context)
+ */
+int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb put, knotd_qdata_t *qdata);
diff --git a/src/knot/query/capture.c b/src/knot/query/capture.c
new file mode 100644
index 0000000..43f3e54
--- /dev/null
+++ b/src/knot/query/capture.c
@@ -0,0 +1,63 @@
+/* Copyright (C) 2016 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 <assert.h>
+
+#include "knot/query/capture.h"
+
+static int reset(knot_layer_t *ctx)
+{
+ return KNOT_STATE_PRODUCE;
+}
+
+static int finish(knot_layer_t *ctx)
+{
+ return KNOT_STATE_NOOP;
+}
+
+static int begin(knot_layer_t *ctx, void *module_param)
+{
+ ctx->data = module_param; /* struct capture_param */
+ return reset(ctx);
+}
+
+static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ return KNOT_STATE_CONSUME;
+}
+
+static int capture(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ assert(pkt && ctx && ctx->data);
+ struct capture_param *param = ctx->data;
+
+ knot_pkt_copy(param->sink, pkt);
+
+ return KNOT_STATE_DONE;
+}
+
+const knot_layer_api_t *query_capture_api(void)
+{
+ static const knot_layer_api_t API = {
+ .begin = begin,
+ .reset = reset,
+ .finish = finish,
+ .consume = capture,
+ .produce = prepare_query,
+ };
+
+ return &API;
+}
diff --git a/src/knot/query/capture.h b/src/knot/query/capture.h
new file mode 100644
index 0000000..41f8270
--- /dev/null
+++ b/src/knot/query/capture.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2016 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/>.
+ */
+
+#pragma once
+
+#include "knot/query/layer.h"
+#include "libknot/packet/pkt.h"
+
+/*!
+ * \brief Processing module for packet capture.
+ */
+const knot_layer_api_t *query_capture_api(void);
+
+/*!
+ * \brief Processing module parameters.
+ */
+struct capture_param {
+ knot_pkt_t *sink; /*!< Container for captured response. */
+};
diff --git a/src/knot/query/layer.h b/src/knot/query/layer.h
new file mode 100644
index 0000000..119ae5d
--- /dev/null
+++ b/src/knot/query/layer.h
@@ -0,0 +1,136 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "libknot/mm_ctx.h"
+#include "knot/nameserver/tsig_ctx.h"
+
+/*!
+ * \brief Layer processing states.
+ *
+ * Each state represents the state machine transition,
+ * and determines readiness for the next action.
+ */
+typedef enum {
+ KNOT_STATE_NOOP = 0, //!< Invalid.
+ KNOT_STATE_CONSUME, //!< Consume data.
+ KNOT_STATE_PRODUCE, //!< Produce data.
+ KNOT_STATE_RESET, //!< Restart processing.
+ KNOT_STATE_DONE, //!< Finished.
+ KNOT_STATE_FAIL, //!< Error.
+ KNOT_STATE_FINAL, //!< Finished and finalized.
+ KNOT_STATE_IGNORE, //!< Data has been ignored.
+} knot_layer_state_t;
+
+typedef struct knot_layer_api knot_layer_api_t;
+
+/*! \brief Packet processing context. */
+typedef struct {
+ const knot_layer_api_t *api; //!< Layer API.
+ knot_mm_t *mm; //!< Processing memory context.
+ knot_layer_state_t state; //!< Processing state.
+ void *data; //!< Module specific.
+ tsig_ctx_t *tsig; //!< TODO: remove
+ unsigned flags; //!< Custom flags.
+} knot_layer_t;
+
+/*! \brief Packet processing module API. */
+struct knot_layer_api {
+ int (*begin)(knot_layer_t *ctx, void *params);
+ int (*reset)(knot_layer_t *ctx);
+ int (*finish)(knot_layer_t *ctx);
+ int (*consume)(knot_layer_t *ctx, knot_pkt_t *pkt);
+ int (*produce)(knot_layer_t *ctx, knot_pkt_t *pkt);
+};
+
+/*! \brief Helper for conditional layer call. */
+#define LAYER_CALL(layer, func, ...) \
+ assert(layer->api); \
+ if (layer->api->func) { \
+ layer->state = layer->api->func(layer, ##__VA_ARGS__); \
+ }
+
+/*!
+ * \brief Initialize packet processing context.
+ *
+ * \param ctx Layer context.
+ * \param mm Memory context.
+ * \param api Layer API.
+ */
+inline static void knot_layer_init(knot_layer_t *ctx, knot_mm_t *mm,
+ const knot_layer_api_t *api)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->mm = mm;
+ ctx->api = api;
+ ctx->state = KNOT_STATE_NOOP;
+}
+
+/*!
+ * \brief Prepare packet processing.
+ *
+ * \param ctx Layer context.
+ * \param params Initialization params.
+ */
+inline static void knot_layer_begin(knot_layer_t *ctx, void *params)
+{
+ LAYER_CALL(ctx, begin, params);
+}
+
+/*!
+ * \brief Reset current packet processing context.
+ *
+ * \param ctx Layer context.
+ */
+inline static void knot_layer_reset(knot_layer_t *ctx)
+{
+ LAYER_CALL(ctx, reset);
+}
+
+/*!
+ * \brief Finish and close packet processing context.
+ *
+ * \param ctx Layer context.
+ */
+inline static void knot_layer_finish(knot_layer_t *ctx)
+{
+ LAYER_CALL(ctx, finish);
+}
+
+/*!
+ * \brief Add more data to layer processing.
+ *
+ * \param ctx Layer context.
+ * \param pkt Data packet.
+ */
+inline static void knot_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ LAYER_CALL(ctx, consume, pkt);
+}
+
+/*!
+ * \brief Generate output from layer.
+ *
+ * \param ctx Layer context.
+ * \param pkt Data packet.
+ */
+inline static void knot_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt)
+{
+ LAYER_CALL(ctx, produce, pkt);
+}
diff --git a/src/knot/query/query.c b/src/knot/query/query.c
new file mode 100644
index 0000000..877851a
--- /dev/null
+++ b/src/knot/query/query.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2022 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 "knot/query/query.h"
+
+#include "contrib/wire_ctx.h"
+#include "libdnssec/random.h"
+
+void query_init_pkt(knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ knot_pkt_clear(pkt);
+ knot_wire_set_id(pkt->wire, dnssec_random_uint16_t());
+}
+
+query_edns_data_t query_edns_data_init(conf_t *conf, int remote_family,
+ query_edns_opt_t opts)
+{
+ assert(conf);
+
+ query_edns_data_t edns = {
+ .max_payload = remote_family == AF_INET ?
+ conf->cache.srv_udp_max_payload_ipv4 :
+ conf->cache.srv_udp_max_payload_ipv6,
+ .do_flag = (opts & QUERY_EDNS_OPT_DO),
+ .expire_option = (opts & QUERY_EDNS_OPT_EXPIRE)
+ };
+
+ return edns;
+}
+
+int query_put_edns(knot_pkt_t *pkt, const query_edns_data_t *edns)
+{
+ if (!pkt || !edns) {
+ return KNOT_EINVAL;
+ }
+
+ // Construct EDNS RR
+
+ knot_rrset_t opt_rr = { 0 };
+ int ret = knot_edns_init(&opt_rr, edns->max_payload, 0, KNOT_EDNS_VERSION, &pkt->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (edns->do_flag) {
+ knot_edns_set_do(&opt_rr);
+ }
+
+ if (edns->expire_option) {
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_EXPIRE, 0, NULL, &pkt->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &pkt->mm);
+ return ret;
+ }
+ }
+
+ // Add result into the packet
+
+ knot_pkt_begin(pkt, KNOT_ADDITIONAL);
+
+ ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NOCOMP, &opt_rr, KNOT_PF_FREE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &pkt->mm);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/query/query.h b/src/knot/query/query.h
new file mode 100644
index 0000000..fbf437d
--- /dev/null
+++ b/src/knot/query/query.h
@@ -0,0 +1,66 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/nameserver/log.h"
+#include "libknot/packet/pkt.h"
+
+/*!
+ * \brief EDNS data.
+ */
+typedef struct {
+ uint16_t max_payload;
+ bool do_flag;
+ bool expire_option;
+} query_edns_data_t;
+
+typedef enum {
+ QUERY_EDNS_OPT_DO = 1 << 0,
+ QUERY_EDNS_OPT_EXPIRE = 1 << 1,
+} query_edns_opt_t;
+
+/*!
+ * \brief Initialize new packet.
+ *
+ * Clear the packet and generate random transaction ID.
+ *
+ * \param pkt Packet to initialize.
+ */
+void query_init_pkt(knot_pkt_t *pkt);
+
+/*!
+ * \brief Initialize EDNS parameters from server configuration.
+ *
+ * \param[in] conf Server configuration.
+ * \param[in] remote_family Address family for remote host.
+ * \param[in] opts EDNS options.
+ *
+ * \return EDNS parameters.
+ */
+query_edns_data_t query_edns_data_init(conf_t *conf, int remote_family,
+ query_edns_opt_t opts);
+
+/*!
+ * \brief Append EDNS into the packet.
+ *
+ * \param pkt Packet to add EDNS into.
+ * \param edns EDNS data.
+ *
+ * \return KNOT_E*
+ */
+int query_put_edns(knot_pkt_t *pkt, const query_edns_data_t *edns);
diff --git a/src/knot/query/requestor.c b/src/knot/query/requestor.c
new file mode 100644
index 0000000..8643f74
--- /dev/null
+++ b/src/knot/query/requestor.c
@@ -0,0 +1,378 @@
+/* Copyright (C) 2022 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 <assert.h>
+
+#include "libknot/attribute.h"
+#include "knot/common/unreachable.h"
+#include "knot/query/requestor.h"
+#include "libknot/errcode.h"
+#include "contrib/conn_pool.h"
+#include "contrib/mempattern.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+static bool use_tcp(knot_request_t *request)
+{
+ return (request->flags & KNOT_REQUEST_UDP) == 0;
+}
+
+static bool is_answer_to_query(const knot_pkt_t *query, const knot_pkt_t *answer)
+{
+ return knot_wire_get_id(query->wire) == knot_wire_get_id(answer->wire);
+}
+
+/*! \brief Ensure a socket is connected. */
+static int request_ensure_connected(knot_request_t *request, bool *reused_fd)
+{
+ if (request->fd >= 0) {
+ return KNOT_EOK;
+ }
+
+ int sock_type = use_tcp(request) ? SOCK_STREAM : SOCK_DGRAM;
+
+ if (sock_type == SOCK_STREAM) {
+ request->fd = conn_pool_get(global_conn_pool,
+ &request->source,
+ &request->remote);
+ if (request->fd >= 0) {
+ if (reused_fd != NULL) {
+ *reused_fd = true;
+ }
+ return KNOT_EOK;
+ }
+
+ if (knot_unreachable_is(global_unreachables, &request->remote,
+ &request->source)) {
+ return KNOT_EUNREACH;
+ }
+ }
+
+ request->fd = net_connected_socket(sock_type,
+ &request->remote,
+ &request->source,
+ request->flags & KNOT_REQUEST_TFO);
+ if (request->fd < 0) {
+ if (request->fd == KNOT_ETIMEOUT) {
+ knot_unreachable_add(global_unreachables, &request->remote,
+ &request->source);
+ }
+ return request->fd;
+ }
+
+ return KNOT_EOK;
+}
+
+static int request_send(knot_request_t *request, int timeout_ms, bool *reused_fd)
+{
+ /* Initiate non-blocking connect if not connected. */
+ *reused_fd = false;
+ int ret = request_ensure_connected(request, reused_fd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Send query, construct if not exists. */
+ knot_pkt_t *query = request->query;
+ uint8_t *wire = query->wire;
+ size_t wire_len = query->size;
+ struct sockaddr_storage *tfo_addr = (request->flags & KNOT_REQUEST_TFO) ?
+ &request->remote : NULL;
+
+ /* Send query. */
+ if (use_tcp(request)) {
+ ret = net_dns_tcp_send(request->fd, wire, wire_len, timeout_ms,
+ tfo_addr);
+ if (ret == KNOT_ETIMEOUT) { // Includes establishing conn which times out.
+ knot_unreachable_add(global_unreachables, &request->remote,
+ &request->source);
+ }
+ } else {
+ ret = net_dgram_send(request->fd, wire, wire_len, NULL);
+ }
+ if (ret < 0) {
+ return ret;
+ } else if (ret != wire_len) {
+ return KNOT_ECONN;
+ }
+
+ return KNOT_EOK;
+}
+
+static int request_recv(knot_request_t *request, int timeout_ms)
+{
+ knot_pkt_t *resp = request->resp;
+ knot_pkt_clear(resp);
+
+ /* Wait for readability */
+ int ret = request_ensure_connected(request, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Receive it */
+ if (use_tcp(request)) {
+ ret = net_dns_tcp_recv(request->fd, resp->wire, resp->max_size, timeout_ms);
+ } else {
+ ret = net_dgram_recv(request->fd, resp->wire, resp->max_size, timeout_ms);
+ }
+ if (ret <= 0) {
+ resp->size = 0;
+ if (ret == 0) {
+ return KNOT_ECONN;
+ }
+ return ret;
+ }
+
+ resp->size = ret;
+ return ret;
+}
+
+knot_request_t *knot_request_make(knot_mm_t *mm,
+ const struct sockaddr_storage *remote,
+ const struct sockaddr_storage *source,
+ knot_pkt_t *query,
+ const knot_tsig_key_t *tsig_key,
+ knot_request_flag_t flags)
+{
+ if (remote == NULL || query == NULL) {
+ return NULL;
+ }
+
+ knot_request_t *request = mm_calloc(mm, 1, sizeof(*request));
+ if (request == NULL) {
+ return NULL;
+ }
+
+ request->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, mm);
+ if (request->resp == NULL) {
+ mm_free(mm, request);
+ return NULL;
+ }
+
+ request->query = query;
+ request->fd = -1;
+ request->flags = flags;
+ memcpy(&request->remote, remote, sockaddr_len(remote));
+ if (source) {
+ memcpy(&request->source, source, sockaddr_len(source));
+ } else {
+ request->source.ss_family = AF_UNSPEC;
+ }
+
+ if (tsig_key && tsig_key->algorithm == DNSSEC_TSIG_UNKNOWN) {
+ tsig_key = NULL;
+ }
+ tsig_init(&request->tsig, tsig_key);
+
+ return request;
+}
+
+void knot_request_free(knot_request_t *request, knot_mm_t *mm)
+{
+ if (request == NULL) {
+ return;
+ }
+
+ if (request->fd >= 0 && use_tcp(request) &&
+ (request->flags & KNOT_REQUEST_KEEP)) {
+ request->fd = conn_pool_put(global_conn_pool,
+ &request->source,
+ &request->remote,
+ request->fd);
+ }
+ if (request->fd >= 0) {
+ close(request->fd);
+ }
+ knot_pkt_free(request->query);
+ knot_pkt_free(request->resp);
+ tsig_cleanup(&request->tsig);
+
+ mm_free(mm, request);
+}
+
+int knot_requestor_init(knot_requestor_t *requestor,
+ const knot_layer_api_t *proc, void *proc_param,
+ knot_mm_t *mm)
+{
+ if (requestor == NULL || proc == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(requestor, 0, sizeof(*requestor));
+
+ requestor->mm = mm;
+ knot_layer_init(&requestor->layer, mm, proc);
+ knot_layer_begin(&requestor->layer, proc_param);
+
+ return KNOT_EOK;
+}
+
+void knot_requestor_clear(knot_requestor_t *requestor)
+{
+ if (requestor == NULL) {
+ return;
+ }
+
+ knot_layer_finish(&requestor->layer);
+
+ memset(requestor, 0, sizeof(*requestor));
+}
+
+static int request_reset(knot_requestor_t *req, knot_request_t *last)
+{
+ knot_layer_reset(&req->layer);
+ tsig_reset(&last->tsig);
+
+ if (req->layer.flags & KNOT_REQUESTOR_CLOSE) {
+ req->layer.flags &= ~KNOT_REQUESTOR_CLOSE;
+ if (last->fd >= 0) {
+ close(last->fd);
+ last->fd = -1;
+ }
+ }
+
+ if (req->layer.state == KNOT_STATE_RESET) {
+ return KNOT_EPROCESSING;
+ }
+
+ return KNOT_EOK;
+}
+
+static int request_produce(knot_requestor_t *req, knot_request_t *last,
+ int timeout_ms)
+{
+ knot_layer_produce(&req->layer, last->query);
+
+ int ret = tsig_sign_packet(&last->tsig, last->query);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // TODO: verify condition
+ if (req->layer.state == KNOT_STATE_CONSUME) {
+ bool reused_fd = false;
+ ret = request_send(last, timeout_ms, &reused_fd);
+ if (reused_fd) {
+ req->layer.flags |= KNOT_REQUESTOR_REUSED;
+ } else {
+ req->layer.flags &= ~KNOT_REQUESTOR_REUSED;
+ }
+ }
+
+ return ret;
+}
+
+static int request_consume(knot_requestor_t *req, knot_request_t *last,
+ int timeout_ms)
+{
+ int ret = request_recv(last, timeout_ms);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ret = knot_pkt_parse(last->resp, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (!is_answer_to_query(last->query, last->resp)) {
+ return KNOT_EMALF;
+ }
+
+ ret = tsig_verify_packet(&last->tsig, last->resp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (tsig_unsigned_count(&last->tsig) >= 100) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ knot_layer_consume(&req->layer, last->resp);
+
+ return KNOT_EOK;
+}
+
+static bool layer_active(knot_layer_state_t state)
+{
+ switch (state) {
+ case KNOT_STATE_CONSUME:
+ case KNOT_STATE_PRODUCE:
+ case KNOT_STATE_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int request_io(knot_requestor_t *req, knot_request_t *last,
+ int timeout_ms)
+{
+ switch (req->layer.state) {
+ case KNOT_STATE_CONSUME:
+ return request_consume(req, last, timeout_ms);
+ case KNOT_STATE_PRODUCE:
+ return request_produce(req, last, timeout_ms);
+ case KNOT_STATE_RESET:
+ return request_reset(req, last);
+ default:
+ return KNOT_EINVAL;
+ }
+}
+
+int knot_requestor_exec(knot_requestor_t *requestor, knot_request_t *request,
+ int timeout_ms)
+{
+ if (requestor == NULL || request == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ requestor->layer.tsig = &request->tsig;
+
+ /* Do I/O until the processing is satisfied or fails. */
+ while (layer_active(requestor->layer.state)) {
+ ret = request_io(requestor, request, timeout_ms);
+ if (ret != KNOT_EOK) {
+ knot_layer_finish(&requestor->layer);
+ return ret;
+ }
+ }
+
+ /* Expect complete request. */
+ switch (requestor->layer.state) {
+ case KNOT_STATE_DONE:
+ request->flags |= KNOT_REQUEST_KEEP;
+ break;
+ case KNOT_STATE_IGNORE:
+ ret = KNOT_ERROR;
+ break;
+ default:
+ ret = KNOT_EPROCESSING;
+ }
+
+ /* Verify last TSIG */
+ if (tsig_unsigned_count(&request->tsig) != 0) {
+ ret = KNOT_TSIG_EBADSIG;
+ }
+
+ /* Finish current query processing. */
+ knot_layer_finish(&requestor->layer);
+
+ return ret;
+}
diff --git a/src/knot/query/requestor.h b/src/knot/query/requestor.h
new file mode 100644
index 0000000..aa90cd5
--- /dev/null
+++ b/src/knot/query/requestor.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include "knot/nameserver/tsig_ctx.h"
+#include "knot/query/layer.h"
+#include "libknot/mm_ctx.h"
+#include "libknot/rrtype/tsig.h"
+
+typedef enum {
+ KNOT_REQUEST_NONE = 0, /*!< Empty flag. */
+ KNOT_REQUEST_UDP = 1 << 0, /*!< Use UDP for requests. */
+ KNOT_REQUEST_TFO = 1 << 1, /*!< Enable TCP Fast Open for requests. */
+ KNOT_REQUEST_KEEP = 1 << 2, /*!< Keep upstream TCP connection in pool for later reuse. */
+} knot_request_flag_t;
+
+typedef enum {
+ KNOT_REQUESTOR_CLOSE = 1 << 0, /*!< Close the connection indication. */
+ KNOT_REQUESTOR_REUSED = 1 << 1, /*!< Reused FD indication. */
+} knot_requestor_flag_t;
+
+/*! \brief Requestor structure.
+ *
+ * Requestor holds a FIFO of pending queries.
+ */
+typedef struct {
+ knot_mm_t *mm; /*!< Memory context. */
+ knot_layer_t layer; /*!< Response processing layer. */
+} knot_requestor_t;
+
+/*! \brief Request data (socket, payload, response, TSIG and endpoints). */
+typedef struct {
+ int fd;
+ knot_request_flag_t flags;
+ struct sockaddr_storage remote, source;
+ knot_pkt_t *query;
+ knot_pkt_t *resp;
+ tsig_ctx_t tsig;
+
+ knot_sign_context_t sign; /*!< Required for async. DDNS processing. */
+} knot_request_t;
+
+/*!
+ * \brief Make request out of endpoints and query.
+ *
+ * \param mm Memory context.
+ * \param remote Remote endpoint address.
+ * \param source Source address (or NULL).
+ * \param query Query message.
+ * \param tsig_key TSIG key for authentication.
+ * \param flags Request flags.
+ *
+ * \return Prepared request or NULL in case of error.
+ */
+knot_request_t *knot_request_make(knot_mm_t *mm,
+ const struct sockaddr_storage *remote,
+ const struct sockaddr_storage *source,
+ knot_pkt_t *query,
+ const knot_tsig_key_t *tsig_key,
+ knot_request_flag_t flags);
+
+/*!
+ * \brief Free request and associated data.
+ *
+ * \param request Freed request.
+ * \param mm Memory context.
+ */
+void knot_request_free(knot_request_t *request, knot_mm_t *mm);
+
+/*!
+ * \brief Initialize requestor structure.
+ *
+ * \param requestor Requestor instance.
+ * \param proc Response processing module.
+ * \param proc_param Processing module context.
+ * \param mm Memory context.
+ *
+ * \return KNOT_EOK or error
+ */
+int knot_requestor_init(knot_requestor_t *requestor,
+ const knot_layer_api_t *proc, void *proc_param,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Clear the requestor structure and close pending queries.
+ *
+ * \param requestor Requestor instance.
+ */
+void knot_requestor_clear(knot_requestor_t *requestor);
+
+/*!
+ * \brief Execute a request.
+ *
+ * \param requestor Requestor instance.
+ * \param request Request instance.
+ * \param timeout_ms Timeout of each operation in milliseconds (-1 for infinity).
+ *
+ * \return KNOT_EOK or error
+ */
+int knot_requestor_exec(knot_requestor_t *requestor,
+ knot_request_t *request,
+ int timeout_ms);
diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c
new file mode 100644
index 0000000..74203ac
--- /dev/null
+++ b/src/knot/server/dthreads.c
@@ -0,0 +1,767 @@
+/* Copyright (C) 2021 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <urcu.h>
+
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif /* HAVE_PTHREAD_NP_H */
+
+#include "knot/server/dthreads.h"
+#include "libknot/libknot.h"
+
+/* BSD cpu set compatibility. */
+#if defined(HAVE_CPUSET_BSD)
+typedef cpuset_t cpu_set_t;
+#endif
+
+/*! \brief Lock thread state for R/W. */
+static inline void lock_thread_rw(dthread_t *thread)
+{
+ pthread_mutex_lock(&thread->_mx);
+}
+/*! \brief Unlock thread state for R/W. */
+static inline void unlock_thread_rw(dthread_t *thread)
+{
+ pthread_mutex_unlock(&thread->_mx);
+}
+
+/*! \brief Signalize thread state change. */
+static inline void unit_signalize_change(dt_unit_t *unit)
+{
+ pthread_mutex_lock(&unit->_report_mx);
+ pthread_cond_signal(&unit->_report);
+ pthread_mutex_unlock(&unit->_report_mx);
+}
+
+/*!
+ * \brief Update thread state with notification.
+ * \param thread Given thread.
+ * \param state New state for thread.
+ * \retval 0 on success.
+ * \retval <0 on error (EINVAL, ENOTSUP).
+ */
+static inline int dt_update_thread(dthread_t *thread, int state)
+{
+ if (thread == 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Cancel with lone thread
+ dt_unit_t *unit = thread->unit;
+ if (unit == 0) {
+ return KNOT_ENOTSUP;
+ }
+
+ // Cancel current runnable if running
+ pthread_mutex_lock(&unit->_notify_mx);
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+
+ // Update state
+ thread->state = state;
+ unlock_thread_rw(thread);
+
+ // Notify thread
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ } else {
+ /* Unable to update thread, it is already dead. */
+ unlock_thread_rw(thread);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Thread entrypoint function.
+ *
+ * When a thread is created and started, it immediately enters this function.
+ * Depending on thread state, it either enters runnable or
+ * blocks until it is awakened.
+ *
+ * This function also handles "ThreadIdle" state to quickly suspend and resume
+ * threads and mitigate thread creation costs. Also, thread runnable may
+ * be changed to alter the thread behavior on runtime
+ */
+static void *thread_ep(void *data)
+{
+ dthread_t *thread = (dthread_t *)data;
+ if (thread == 0) {
+ return 0;
+ }
+
+ // Check if is a member of unit
+ dt_unit_t *unit = thread->unit;
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Unblock SIGALRM for synchronization
+ sigset_t mask;
+ (void)sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
+
+ rcu_register_thread();
+
+ // Run loop
+ for (;;) {
+
+ // Check thread state
+ lock_thread_rw(thread);
+ if (thread->state == ThreadDead) {
+ unlock_thread_rw(thread);
+ break;
+ }
+
+ // Update data
+ thread->data = thread->_adata;
+ runnable_t _run = thread->run;
+
+ // Start runnable if thread is marked Active
+ if ((thread->state == ThreadActive) && (thread->run != 0)) {
+ unlock_thread_rw(thread);
+ _run(thread);
+ } else {
+ unlock_thread_rw(thread);
+ }
+
+ // If the runnable was cancelled, start new iteration
+ lock_thread_rw(thread);
+ if (thread->state & ThreadCancelled) {
+ thread->state &= ~ThreadCancelled;
+ unlock_thread_rw(thread);
+ continue;
+ }
+ unlock_thread_rw(thread);
+
+ // Runnable finished without interruption, mark as Idle
+ pthread_mutex_lock(&unit->_notify_mx);
+ lock_thread_rw(thread);
+ if (thread->state & ThreadActive) {
+ thread->state &= ~ThreadActive;
+ thread->state |= ThreadIdle;
+ }
+
+ // Go to sleep if idle
+ if (thread->state & ThreadIdle) {
+ unlock_thread_rw(thread);
+
+ // Signalize state change
+ unit_signalize_change(unit);
+
+ // Wait for notification from unit
+ pthread_cond_wait(&unit->_notify, &unit->_notify_mx);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ } else {
+ unlock_thread_rw(thread);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ }
+ }
+
+ // Thread destructor
+ if (thread->destruct) {
+ thread->destruct(thread);
+ }
+
+ // Report thread state change
+ unit_signalize_change(unit);
+ lock_thread_rw(thread);
+ thread->state |= ThreadJoinable;
+ unlock_thread_rw(thread);
+ rcu_unregister_thread();
+
+ // Return
+ return 0;
+}
+
+/*!
+ * \brief Create single thread.
+ * \retval New thread instance on success.
+ * \retval NULL on error.
+ */
+static dthread_t *dt_create_thread(dt_unit_t *unit)
+{
+ // Alloc thread
+ dthread_t *thread = malloc(sizeof(dthread_t));
+ if (thread == 0) {
+ return 0;
+ }
+
+ memset(thread, 0, sizeof(dthread_t));
+
+ // Blank thread state
+ thread->state = ThreadJoined;
+ pthread_mutex_init(&thread->_mx, 0);
+
+ // Set membership in unit
+ thread->unit = unit;
+
+ // Initialize attribute
+ pthread_attr_t *attr = &thread->_attr;
+ pthread_attr_init(attr);
+ //pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED);
+ //pthread_attr_setschedpolicy(attr, SCHED_OTHER);
+ pthread_attr_setstacksize(attr, 1024*1024);
+ return thread;
+}
+
+/*! \brief Delete single thread. */
+static void dt_delete_thread(dthread_t **thread)
+{
+ if (!thread || !*thread) {
+ return;
+ }
+
+ dthread_t* thr = *thread;
+ thr->unit = 0;
+ *thread = 0;
+
+ // Delete attribute
+ pthread_attr_destroy(&(thr)->_attr);
+
+ // Delete mutex
+ pthread_mutex_destroy(&(thr)->_mx);
+
+ // Free memory
+ free(thr);
+}
+
+static dt_unit_t *dt_create_unit(int count)
+{
+ if (count <= 0) {
+ return 0;
+ }
+
+ dt_unit_t *unit = malloc(sizeof(dt_unit_t));
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Initialize conditions
+ if (pthread_cond_init(&unit->_notify, 0) != 0) {
+ free(unit);
+ return 0;
+ }
+ if (pthread_cond_init(&unit->_report, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ free(unit);
+ return 0;
+ }
+
+ // Initialize mutexes
+ if (pthread_mutex_init(&unit->_notify_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ free(unit);
+ return 0;
+ }
+ if (pthread_mutex_init(&unit->_report_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ free(unit);
+ return 0;
+ }
+ if (pthread_mutex_init(&unit->_mx, 0) != 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ free(unit);
+ return 0;
+ }
+
+ // Save unit size
+ unit->size = count;
+
+ // Alloc threads
+ unit->threads = calloc(count, sizeof(dthread_t *));
+ if (unit->threads == 0) {
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ pthread_mutex_destroy(&unit->_mx);
+ free(unit);
+ return 0;
+ }
+
+ // Initialize threads
+ int init_success = 1;
+ for (int i = 0; i < count; ++i) {
+ unit->threads[i] = dt_create_thread(unit);
+ if (unit->threads[i] == 0) {
+ init_success = 0;
+ break;
+ }
+ }
+
+ // Check thread initialization
+ if (!init_success) {
+
+ // Delete created threads
+ for (int i = 0; i < count; ++i) {
+ dt_delete_thread(&unit->threads[i]);
+ }
+
+ // Free rest of the unit
+ pthread_cond_destroy(&unit->_notify);
+ pthread_cond_destroy(&unit->_report);
+ pthread_mutex_destroy(&unit->_notify_mx);
+ pthread_mutex_destroy(&unit->_report_mx);
+ pthread_mutex_destroy(&unit->_mx);
+ free(unit->threads);
+ free(unit);
+ return 0;
+ }
+
+ return unit;
+}
+
+dt_unit_t *dt_create(int count, runnable_t runnable, runnable_t destructor, void *data)
+{
+ if (count <= 0) {
+ return 0;
+ }
+
+ // Create unit
+ dt_unit_t *unit = dt_create_unit(count);
+ if (unit == 0) {
+ return 0;
+ }
+
+ // Set threads common purpose
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ for (int i = 0; i < count; ++i) {
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ thread->run = runnable;
+ thread->destruct = destructor;
+ thread->_adata = data;
+ unlock_thread_rw(thread);
+ }
+
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ return unit;
+}
+
+void dt_delete(dt_unit_t **unit)
+{
+ /*
+ * All threads must be stopped or idle at this point,
+ * or else the behavior is undefined.
+ * Sorry.
+ */
+
+ if (unit == 0) {
+ return;
+ }
+ if (*unit == 0) {
+ return;
+ }
+
+ // Compact and reclaim idle threads
+ dt_unit_t *d_unit = *unit;
+ dt_compact(d_unit);
+
+ // Delete threads
+ for (int i = 0; i < d_unit->size; ++i) {
+ dt_delete_thread(&d_unit->threads[i]);
+ }
+
+ // Deinit mutexes
+ pthread_mutex_destroy(&d_unit->_notify_mx);
+ pthread_mutex_destroy(&d_unit->_report_mx);
+ pthread_mutex_destroy(&d_unit->_mx);
+
+ // Deinit conditions
+ pthread_cond_destroy(&d_unit->_notify);
+ pthread_cond_destroy(&d_unit->_report);
+
+ // Free memory
+ free(d_unit->threads);
+ free(d_unit);
+ *unit = 0;
+}
+
+static int dt_start_id(dthread_t *thread)
+{
+ if (thread == 0) {
+ return KNOT_EINVAL;
+ }
+
+ lock_thread_rw(thread);
+
+ // Update state
+ int prev_state = thread->state;
+ thread->state |= ThreadActive;
+ thread->state &= ~ThreadIdle;
+ thread->state &= ~ThreadDead;
+ thread->state &= ~ThreadJoined;
+ thread->state &= ~ThreadJoinable;
+
+ // Do not re-create running threads
+ if (prev_state != ThreadJoined) {
+ unlock_thread_rw(thread);
+ return 0;
+ }
+
+ // Start thread
+ sigset_t mask_all, mask_old;
+ sigfillset(&mask_all);
+ sigdelset(&mask_all, SIGPROF);
+ pthread_sigmask(SIG_SETMASK, &mask_all, &mask_old);
+ int res = pthread_create(&thread->_thr, /* pthread_t */
+ &thread->_attr, /* pthread_attr_t */
+ thread_ep, /* routine: thread_ep */
+ thread); /* passed object: dthread_t */
+ pthread_sigmask(SIG_SETMASK, &mask_old, NULL);
+
+ // Unlock thread
+ unlock_thread_rw(thread);
+ return res;
+}
+
+int dt_start(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+ for (int i = 0; i < unit->size; ++i) {
+
+ dthread_t *thread = unit->threads[i];
+ int res = dt_start_id(thread);
+ if (res != 0) {
+ dt_unit_unlock(unit);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return res;
+ }
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+ return KNOT_EOK;
+}
+
+int dt_signalize(dthread_t *thread, int signum)
+{
+ if (thread == 0) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = pthread_kill(thread->_thr, signum);
+
+ /* Not thread id found or invalid signum. */
+ if (ret == EINVAL || ret == ESRCH) {
+ return KNOT_EINVAL;
+ }
+
+ /* Generic error. */
+ if (ret < 0) {
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+int dt_join(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ for (;;) {
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_report_mx);
+ dt_unit_lock(unit);
+
+ // Browse threads
+ int active_threads = 0;
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Count active or cancelled but pending threads
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadActive|ThreadCancelled)) {
+ ++active_threads;
+ }
+
+ // Reclaim dead threads, but only fast
+ if (thread->state & ThreadJoinable) {
+ unlock_thread_rw(thread);
+ pthread_join(thread->_thr, 0);
+ lock_thread_rw(thread);
+ thread->state = ThreadJoined;
+ unlock_thread_rw(thread);
+ } else {
+ unlock_thread_rw(thread);
+ }
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ // Check result
+ if (active_threads == 0) {
+ pthread_mutex_unlock(&unit->_report_mx);
+ break;
+ }
+
+ // Wait for a thread to finish
+ pthread_cond_wait(&unit->_report, &unit->_report_mx);
+ pthread_mutex_unlock(&unit->_report_mx);
+ }
+
+ return KNOT_EOK;
+}
+
+int dt_stop(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Signalize all threads to stop
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Lock thread
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle | ThreadActive)) {
+ thread->state = ThreadDead | ThreadCancelled;
+ dt_signalize(thread, SIGALRM);
+ }
+ unlock_thread_rw(thread);
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ // Broadcast notification
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ return KNOT_EOK;
+}
+
+int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count)
+{
+ if (thread == NULL) {
+ return KNOT_EINVAL;
+ }
+
+#ifdef HAVE_PTHREAD_SETAFFINITY_NP
+ int ret = -1;
+
+/* Linux, FreeBSD interface. */
+#if defined(HAVE_CPUSET_LINUX) || defined(HAVE_CPUSET_BSD)
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ for (unsigned i = 0; i < cpu_count; ++i) {
+ CPU_SET(cpu_id[i], &set);
+ }
+ ret = pthread_setaffinity_np(thread->_thr, sizeof(cpu_set_t), &set);
+/* NetBSD interface. */
+#elif defined(HAVE_CPUSET_NETBSD)
+ cpuset_t *set = cpuset_create();
+ if (set == NULL) {
+ return KNOT_ENOMEM;
+ }
+ cpuset_zero(set);
+ for (unsigned i = 0; i < cpu_count; ++i) {
+ cpuset_set(cpu_id[i], set);
+ }
+ ret = pthread_setaffinity_np(thread->_thr, cpuset_size(set), set);
+ cpuset_destroy(set);
+#endif /* interface */
+
+ if (ret < 0) {
+ return KNOT_ERROR;
+ }
+
+#else /* HAVE_PTHREAD_SETAFFINITY_NP */
+ return KNOT_ENOTSUP;
+#endif
+
+ return KNOT_EOK;
+}
+
+int dt_activate(dthread_t *thread)
+{
+ return dt_update_thread(thread, ThreadActive);
+}
+
+int dt_cancel(dthread_t *thread)
+{
+ return dt_update_thread(thread, ThreadIdle | ThreadCancelled);
+}
+
+int dt_compact(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Lock unit
+ pthread_mutex_lock(&unit->_notify_mx);
+ dt_unit_lock(unit);
+
+ // Reclaim all Idle threads
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Locked state update
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadIdle)) {
+ thread->state = ThreadDead | ThreadCancelled;
+ dt_signalize(thread, SIGALRM);
+ }
+ unlock_thread_rw(thread);
+ }
+
+ // Notify all threads
+ pthread_cond_broadcast(&unit->_notify);
+ pthread_mutex_unlock(&unit->_notify_mx);
+
+ // Join all threads
+ for (int i = 0; i < unit->size; ++i) {
+
+ // Reclaim all dead threads
+ dthread_t *thread = unit->threads[i];
+ lock_thread_rw(thread);
+ if (thread->state & (ThreadDead)) {
+ unlock_thread_rw(thread);
+ pthread_join(thread->_thr, 0);
+ lock_thread_rw(thread);
+ thread->state = ThreadJoined;
+ unlock_thread_rw(thread);
+ } else {
+ unlock_thread_rw(thread);
+ }
+ }
+
+ // Unlock unit
+ dt_unit_unlock(unit);
+
+ return KNOT_EOK;
+}
+
+int dt_online_cpus(void)
+{
+ int ret = -1;
+/* Linux, FreeBSD, NetBSD, OpenBSD, macOS/OS X 10.4+, Solaris */
+#ifdef _SC_NPROCESSORS_ONLN
+ ret = (int) sysconf(_SC_NPROCESSORS_ONLN);
+#else
+/* OS X < 10.4 and some other OS's (if not handled by sysconf() above) */
+/* hw.ncpu won't work on FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, and recent macOS/OS X. */
+#if HAVE_SYSCTLBYNAME
+ size_t rlen = sizeof(int);
+ if (sysctlbyname("hw.ncpu", &ret, &rlen, NULL, 0) < 0) {
+ ret = -1;
+ }
+#endif
+#endif
+ return ret;
+}
+
+int dt_optimal_size(void)
+{
+ int ret = dt_online_cpus();
+ if (ret > 1) {
+ return ret;
+ }
+
+ return DEFAULT_THR_COUNT;
+}
+
+int dt_is_cancelled(dthread_t *thread)
+{
+ if (thread == 0) {
+ return 0;
+ }
+
+ return thread->state & ThreadCancelled; /* No need to be locked. */
+}
+
+unsigned dt_get_id(dthread_t *thread)
+{
+ if (thread == NULL || thread->unit == NULL) {
+ return 0;
+ }
+
+ dt_unit_t *unit = thread->unit;
+ for(int tid = 0; tid < unit->size; ++tid) {
+ if (thread == unit->threads[tid]) {
+ return tid;
+ }
+ }
+
+ return 0;
+}
+
+int dt_unit_lock(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = pthread_mutex_lock(&unit->_mx);
+ if (ret < 0) {
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+int dt_unit_unlock(dt_unit_t *unit)
+{
+ if (unit == 0) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = pthread_mutex_unlock(&unit->_mx);
+ if (ret < 0) {
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/server/dthreads.h b/src/knot/server/dthreads.h
new file mode 100644
index 0000000..0c243a1
--- /dev/null
+++ b/src/knot/server/dthreads.h
@@ -0,0 +1,295 @@
+/* Copyright (C) 2018 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 Threading API.
+ *
+ * Dynamic threads provide:
+ * - coherent and incoherent threading capabilities
+ * - thread repurposing
+ * - thread prioritization
+ * - on-the-fly changing of threading unit size
+ *
+ * Coherent threading unit is when all threads execute
+ * the same runnable function.
+ *
+ * Incoherent function is when at least one thread executes
+ * a different runnable than the others.
+ */
+
+#pragma once
+
+#include <pthread.h>
+
+#define DEFAULT_THR_COUNT 2 /*!< Default thread count. */
+
+/* Forward decls */
+struct dthread;
+struct dt_unit;
+
+/*!
+ * \brief Thread state enumeration.
+ */
+typedef enum {
+ ThreadJoined = 1 << 0, /*!< Thread is finished and joined. */
+ ThreadJoinable = 1 << 1, /*!< Thread is waiting to be reclaimed. */
+ ThreadCancelled = 1 << 2, /*!< Thread is cancelled, finishing task. */
+ ThreadDead = 1 << 3, /*!< Thread is finished, exiting. */
+ ThreadIdle = 1 << 4, /*!< Thread is idle, waiting for purpose. */
+ ThreadActive = 1 << 5 /*!< Thread is active, working on a task. */
+} dt_state_t;
+
+/*!
+ * \brief Thread runnable prototype.
+ *
+ * Runnable is basically a pointer to function which is called on active
+ * thread runtime.
+ *
+ * \note When implementing a runnable, keep in mind to check thread state as
+ * it may change, and implement a cooperative cancellation point.
+ *
+ * Implement this by checking dt_is_cancelled() and return
+ * as soon as possible.
+ */
+typedef int (*runnable_t)(struct dthread *);
+
+/*!
+ * \brief Single thread descriptor public API.
+ */
+typedef struct dthread {
+ volatile unsigned state; /*!< Bitfield of dt_flag flags. */
+ runnable_t run; /*!< Runnable function or 0. */
+ runnable_t destruct; /*!< Destructor function or 0. */
+ void *data; /*!< Currently active data */
+ struct dt_unit *unit; /*!< Reference to assigned unit. */
+ void *_adata; /*!< Thread-specific data. */
+ pthread_t _thr; /*!< Thread */
+ pthread_attr_t _attr; /*!< Thread attributes */
+ pthread_mutex_t _mx; /*!< Thread state change lock. */
+} dthread_t;
+
+/*!
+ * \brief Thread unit descriptor API.
+ *
+ * Thread unit consists of 1..N threads.
+ * Unit is coherent if all threads execute
+ * the same runnable.
+ */
+typedef struct dt_unit {
+ int size; /*!< Unit width (number of threads) */
+ struct dthread **threads; /*!< Array of threads */
+ pthread_cond_t _notify; /*!< Notify thread */
+ pthread_mutex_t _notify_mx; /*!< Condition mutex */
+ pthread_cond_t _report; /*!< Report thread state */
+ pthread_mutex_t _report_mx; /*!< Condition mutex */
+ pthread_mutex_t _mx; /*!< Unit lock */
+} dt_unit_t;
+
+/*!
+ * \brief Create a set of coherent threads.
+ *
+ * Coherent means, that the threads will share a common runnable and the data.
+ *
+ * \param count Requested thread count.
+ * \param runnable Runnable function for all threads.
+ * \param destructor Destructor for all threads.
+ * \param data Any data passed onto threads.
+ *
+ * \retval New instance if successful
+ * \retval NULL on error
+ */
+dt_unit_t *dt_create(int count, runnable_t runnable, runnable_t destructor, void *data);
+
+/*!
+ * \brief Free unit.
+ *
+ * \warning Behavior is undefined if threads are still active, make sure
+ * to call dt_join() first.
+ *
+ * \param unit Unit to be deleted.
+ */
+void dt_delete(dt_unit_t **unit);
+
+/*!
+ * \brief Start all threads in selected unit.
+ *
+ * \param unit Unit to be started.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters (unit is null).
+ */
+int dt_start(dt_unit_t *unit);
+
+/*!
+ * \brief Send given signal to thread.
+ *
+ * \note This is useful to interrupt some blocking I/O as well, for example
+ * with SIGALRM, which is handled by default.
+ * \note Signal handler may be overridden in runnable.
+ *
+ * \param thread Target thread instance.
+ * \param signum Signal code.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ * \retval KNOT_ERROR unspecified error.
+ */
+int dt_signalize(dthread_t *thread, int signum);
+
+/*!
+ * \brief Wait for all thread in unit to finish.
+ *
+ * \param unit Unit to be joined.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int dt_join(dt_unit_t *unit);
+
+/*!
+ * \brief Stop all threads in unit.
+ *
+ * Thread is interrupted at the nearest runnable cancellation point.
+ *
+ * \param unit Unit to be stopped.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int dt_stop(dt_unit_t *unit);
+
+/*!
+ * \brief Set thread affinity to masked CPU's.
+ *
+ * \param thread Target thread instance.
+ * \param cpu_id Array of CPU IDs to set affinity to.
+ * \param cpu_count Number of CPUs in the array, set to 0 for no CPU.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count);
+
+/*!
+ * \brief Wake up thread from idle state.
+ *
+ * Thread is awoken from idle state and reenters runnable.
+ * This function only affects idle threads.
+ *
+ * \note Unit needs to be started with dt_start() first, as the function
+ * doesn't affect dead threads.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ * \retval KNOT_ENOTSUP operation not supported.
+ */
+int dt_activate(dthread_t *thread);
+
+/*!
+ * \brief Put thread to idle state, cancels current runnable function.
+ *
+ * Thread is flagged with Cancel flag and returns from runnable at the nearest
+ * cancellation point, which requires complying runnable function.
+ *
+ * \note Thread isn't disposed, but put to idle state until it's requested
+ * again or collected by dt_compact().
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int dt_cancel(dthread_t *thread);
+
+/*!
+ * \brief Collect and dispose idle threads.
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int dt_compact(dt_unit_t *unit);
+
+/*!
+ * \brief Return number of online processors.
+ *
+ * \retval Number of online CPU's if success.
+ * \retval <0 on failure.
+ */
+int dt_online_cpus(void);
+
+/*!
+ * \brief Return optimal number of threads for instance.
+ *
+ * It is estimated as NUM_CPUs + CONSTANT.
+ * Fallback is DEFAULT_THR_COUNT (\see common.h).
+ *
+ * \return Number of threads.
+ */
+int dt_optimal_size(void);
+
+/*!
+ * \brief Return true if thread is cancelled.
+ *
+ * Synchronously check for ThreadCancelled flag.
+ *
+ * \param thread Target thread instance.
+ *
+ * \retval 1 if cancelled.
+ * \retval 0 if not cancelled.
+ */
+int dt_is_cancelled(dthread_t *thread);
+
+/*!
+ * \brief Return thread index in threading unit.
+ *
+ * \note Returns 0 when thread doesn't have a unit.
+ *
+ * \param thread Target thread instance.
+ *
+ * \return Thread index.
+ */
+unsigned dt_get_id(dthread_t *thread);
+
+/*!
+ * \brief Lock unit to prevent parallel operations which could alter unit
+ * at the same time.
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ * \retval KNOT_EAGAIN lack of resources to lock unit, try again.
+ * \retval KNOT_ERROR unspecified error.
+ */
+int dt_unit_lock(dt_unit_t *unit);
+
+/*!
+ * \brief Unlock unit.
+ *
+ * \see dt_unit_lock()
+ *
+ * \param unit Target unit instance.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ * \retval KNOT_EAGAIN lack of resources to unlock unit, try again.
+ * \retval KNOT_ERROR unspecified error.
+ */
+int dt_unit_unlock(dt_unit_t *unit);
diff --git a/src/knot/server/proxyv2.c b/src/knot/server/proxyv2.c
new file mode 100644
index 0000000..ff92263
--- /dev/null
+++ b/src/knot/server/proxyv2.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 2022 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 "knot/server/proxyv2.h"
+
+#include "contrib/proxyv2/proxyv2.h"
+#include "knot/conf/conf.h"
+
+int proxyv2_header_strip(knot_pkt_t **query,
+ const struct sockaddr_storage *remote,
+ struct sockaddr_storage *new_remote)
+{
+ conf_t *pconf = conf();
+ if (!pconf->cache.srv_proxy_enabled) {
+ return KNOT_EDENIED;
+ }
+
+ uint8_t *pkt = (*query)->wire;
+ size_t pkt_len = (*query)->max_size;
+
+ int offset = proxyv2_header_offset(pkt, pkt_len);
+ if (offset <= 0) {
+ return KNOT_EMALF;
+ }
+
+ /*
+ * Check if the query was sent from an IP address authorized to send
+ * proxied DNS traffic.
+ */
+ conf_val_t whitelist_val = conf_get(pconf, C_SRV, C_PROXY_ALLOWLIST);
+ if (!conf_addr_range_match(&whitelist_val, remote)) {
+ return KNOT_EDENIED;
+ }
+
+ /*
+ * Store the provided remote address.
+ */
+ int ret = proxyv2_addr_store(pkt, pkt_len, new_remote);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /*
+ * Re-parse the query message using the data in the
+ * packet following the PROXY v2 payload. And replace the original
+ * query with the decapsulated one.
+ */
+ knot_pkt_t *q = knot_pkt_new(pkt + offset, pkt_len - offset, &(*query)->mm);
+ if (q == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_pkt_free(*query);
+ *query = q;
+
+ return knot_pkt_parse(q, 0);
+}
diff --git a/src/knot/server/proxyv2.h b/src/knot/server/proxyv2.h
new file mode 100644
index 0000000..5cb1251
--- /dev/null
+++ b/src/knot/server/proxyv2.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+
+int proxyv2_header_strip(knot_pkt_t **query,
+ const struct sockaddr_storage *remote,
+ struct sockaddr_storage *new_remote);
diff --git a/src/knot/server/server.c b/src/knot/server/server.c
new file mode 100644
index 0000000..684526d
--- /dev/null
+++ b/src/knot/server/server.c
@@ -0,0 +1,1335 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#define __APPLE_USE_RFC_3542
+
+#include <assert.h>
+#include <sys/types.h> // OpenBSD
+#include <netinet/tcp.h> // TCP_FASTOPEN
+#include <sys/resource.h>
+
+#include "libknot/libknot.h"
+#include "libknot/yparser/ypschema.h"
+#include "libknot/xdp.h"
+#if defined ENABLE_XDP && ENABLE_QUIC
+#include "libknot/xdp/quic.h"
+#endif // ENABLE_XDP && ENABLE_QUIC
+#include "knot/common/log.h"
+#include "knot/common/stats.h"
+#include "knot/common/systemd.h"
+#include "knot/common/unreachable.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/migration.h"
+#include "knot/conf/module.h"
+#include "knot/dnssec/kasp/kasp_db.h"
+#include "knot/journal/journal_basic.h"
+#include "knot/server/server.h"
+#include "knot/server/udp-handler.h"
+#include "knot/server/tcp-handler.h"
+#include "knot/zone/timers.h"
+#include "knot/zone/zonedb-load.h"
+#include "knot/worker/pool.h"
+#include "contrib/conn_pool.h"
+#include "contrib/net.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/os.h"
+#include "contrib/sockaddr.h"
+#include "contrib/trim.h"
+
+#ifdef ENABLE_XDP
+#include <net/if.h>
+#endif
+
+#ifdef SO_ATTACH_REUSEPORT_CBPF
+#include <linux/filter.h>
+#endif
+
+/*! \brief Minimal send/receive buffer sizes. */
+enum {
+ UDP_MIN_RCVSIZE = 4096,
+ UDP_MIN_SNDSIZE = 4096,
+ TCP_MIN_RCVSIZE = 4096,
+ TCP_MIN_SNDSIZE = sizeof(uint16_t) + UINT16_MAX
+};
+
+/*! \brief Unbind interface and clear the structure. */
+static void server_deinit_iface(iface_t *iface, bool dealloc)
+{
+ assert(iface);
+
+ /* Free UDP handler. */
+ if (iface->fd_udp != NULL) {
+ for (int i = 0; i < iface->fd_udp_count; i++) {
+ if (iface->fd_udp[i] > -1) {
+ close(iface->fd_udp[i]);
+ }
+ }
+ free(iface->fd_udp);
+ }
+
+ for (int i = 0; i < iface->fd_xdp_count; i++) {
+#ifdef ENABLE_XDP
+ knot_xdp_deinit(iface->xdp_sockets[i]);
+#else
+ assert(0);
+#endif
+ }
+ free(iface->fd_xdp);
+ free(iface->xdp_sockets);
+
+ /* Free TCP handler. */
+ if (iface->fd_tcp != NULL) {
+ for (int i = 0; i < iface->fd_tcp_count; i++) {
+ if (iface->fd_tcp[i] > -1) {
+ close(iface->fd_tcp[i]);
+ }
+ }
+ free(iface->fd_tcp);
+ }
+
+ if (dealloc) {
+ free(iface);
+ }
+}
+
+/*! \brief Deinit server interface list. */
+static void server_deinit_iface_list(iface_t *ifaces, size_t n)
+{
+ if (ifaces != NULL) {
+ for (size_t i = 0; i < n; i++) {
+ server_deinit_iface(ifaces + i, false);
+ }
+ free(ifaces);
+ }
+}
+
+/*!
+ * \brief Attach SO_REUSEPORT socket filter for perfect CPU locality.
+ *
+ * \param sock Socket where to attach the CBPF filter to.
+ * \param sock_count Number of sockets.
+ */
+static bool server_attach_reuseport_bpf(const int sock, const int sock_count)
+{
+#ifdef SO_ATTACH_REUSEPORT_CBPF
+ struct sock_filter code[] = {
+ /* A = raw_smp_processor_id(). */
+ { BPF_LD | BPF_W | BPF_ABS, 0, 0, SKF_AD_OFF + SKF_AD_CPU },
+ /* Adjust the CPUID to socket group size. */
+ { BPF_ALU | BPF_MOD | BPF_K, 0, 0, sock_count },
+ /* Return A. */
+ { BPF_RET | BPF_A, 0, 0, 0 },
+ };
+
+ struct sock_fprog prog = { 0 };
+ prog.len = sizeof(code) / sizeof(*code);
+ prog.filter = code;
+
+ return setsockopt(sock, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, &prog, sizeof(prog)) == 0;
+#else
+ return true;
+#endif
+}
+
+/*! \brief Set lower bound for socket option. */
+static bool setsockopt_min(int sock, int option, int min)
+{
+ int value = 0;
+ socklen_t len = sizeof(value);
+
+ if (getsockopt(sock, SOL_SOCKET, option, &value, &len) != 0) {
+ return false;
+ }
+
+ assert(len == sizeof(value));
+ if (value >= min) {
+ return true;
+ }
+
+ return setsockopt(sock, SOL_SOCKET, option, &min, sizeof(min)) == 0;
+}
+
+/*!
+ * \brief Enlarge send/receive buffers.
+ */
+static bool enlarge_net_buffers(int sock, int min_recvsize, int min_sndsize)
+{
+ return setsockopt_min(sock, SO_RCVBUF, min_recvsize) &&
+ setsockopt_min(sock, SO_SNDBUF, min_sndsize);
+}
+
+/*!
+ * \brief Enable source packet information retrieval.
+ */
+static bool enable_pktinfo(int sock, int family)
+{
+ int level = 0;
+ int option = 0;
+
+ switch (family) {
+ case AF_INET:
+ level = IPPROTO_IP;
+#if defined(IP_PKTINFO)
+ option = IP_PKTINFO; /* Linux */
+#elif defined(IP_RECVDSTADDR)
+ option = IP_RECVDSTADDR; /* BSD */
+#else
+ return false;
+#endif
+ break;
+ case AF_INET6:
+ level = IPPROTO_IPV6;
+ option = IPV6_RECVPKTINFO;
+ break;
+ default:
+ return false;
+ }
+
+ const int on = 1;
+ return setsockopt(sock, level, option, &on, sizeof(on)) == 0;
+}
+
+/*!
+ * Linux 3.15 has IP_PMTUDISC_OMIT which makes sockets
+ * ignore PMTU information and send packets with DF=0.
+ * Fragmentation is allowed if and only if the packet
+ * size exceeds the outgoing interface MTU or the packet
+ * encounters smaller MTU link in network.
+ * This mitigates DNS fragmentation attacks by preventing
+ * forged PMTU information.
+ * FreeBSD already has same semantics without setting
+ * the option.
+ */
+static int disable_pmtudisc(int sock, int family)
+{
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_OMIT)
+ if (family == AF_INET) {
+ int action_omit = IP_PMTUDISC_OMIT;
+ if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &action_omit,
+ sizeof(action_omit)) != 0) {
+ return knot_map_errno();
+ }
+ }
+#endif
+ return KNOT_EOK;
+}
+
+static iface_t *server_init_xdp_iface(struct sockaddr_storage *addr, bool route_check,
+ bool udp, bool tcp, uint16_t quic, unsigned *thread_id_start)
+{
+#ifndef ENABLE_XDP
+ assert(0);
+ return NULL;
+#else
+ conf_xdp_iface_t iface;
+ int ret = conf_xdp_iface(addr, &iface);
+ if (ret != KNOT_EOK) {
+ log_error("failed to initialize XDP interface (%s)",
+ knot_strerror(ret));
+ return NULL;
+ }
+
+ iface_t *new_if = calloc(1, sizeof(*new_if));
+ if (new_if == NULL) {
+ log_error("failed to initialize XDP interface");
+ return NULL;
+ }
+ memcpy(&new_if->addr, addr, sizeof(*addr));
+
+ new_if->fd_xdp = calloc(iface.queues, sizeof(int));
+ new_if->xdp_sockets = calloc(iface.queues, sizeof(*new_if->xdp_sockets));
+ if (new_if->fd_xdp == NULL || new_if->xdp_sockets == NULL) {
+ log_error("failed to initialize XDP interface");
+ server_deinit_iface(new_if, true);
+ return NULL;
+ }
+ new_if->xdp_first_thread_id = *thread_id_start;
+ *thread_id_start += iface.queues;
+
+ knot_xdp_filter_flag_t xdp_flags = udp ? KNOT_XDP_FILTER_UDP : 0;
+ if (tcp) {
+ xdp_flags |= KNOT_XDP_FILTER_TCP;
+ }
+ if (quic > 0) {
+ xdp_flags |= KNOT_XDP_FILTER_QUIC;
+ }
+ if (route_check) {
+ xdp_flags |= KNOT_XDP_FILTER_ROUTE;
+ }
+
+ for (int i = 0; i < iface.queues; i++) {
+ knot_xdp_load_bpf_t mode =
+ (i == 0 ? KNOT_XDP_LOAD_BPF_ALWAYS : KNOT_XDP_LOAD_BPF_NEVER);
+ ret = knot_xdp_init(new_if->xdp_sockets + i, iface.name, i,
+ xdp_flags, iface.port, quic, mode, NULL);
+ if (ret == -EBUSY && i == 0) {
+ log_notice("XDP interface %s@%u is busy, retrying initialization",
+ iface.name, iface.port);
+ ret = knot_xdp_init(new_if->xdp_sockets + i, iface.name, i,
+ xdp_flags, iface.port, quic,
+ KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD, NULL);
+ }
+ if (ret != KNOT_EOK) {
+ log_warning("failed to initialize XDP interface %s@%u, queue %d (%s)",
+ iface.name, iface.port, i, knot_strerror(ret));
+ server_deinit_iface(new_if, true);
+ new_if = NULL;
+ break;
+ }
+ new_if->fd_xdp[i] = knot_xdp_socket_fd(new_if->xdp_sockets[i]);
+ new_if->fd_xdp_count++;
+ }
+
+ if (ret == KNOT_EOK) {
+ char msg[128];
+ (void)snprintf(msg, sizeof(msg), "initialized XDP interface %s", iface.name);
+ if (udp || tcp) {
+ char buf[32] = "";
+ (void)snprintf(buf, sizeof(buf), ", %s%s%s port %u",
+ (udp ? "UDP" : ""),
+ (udp && tcp ? "/" : ""),
+ (tcp ? "TCP" : ""),
+ iface.port);
+ strlcat(msg, buf, sizeof(msg));
+ }
+ if (quic) {
+ char buf[32] = "";
+ (void)snprintf(buf, sizeof(buf), ", QUIC port %u", quic);
+ strlcat(msg, buf, sizeof(msg));
+ }
+
+ knot_xdp_mode_t mode = knot_eth_xdp_mode(if_nametoindex(iface.name));
+ log_info("%s, queues %d, %s mode%s", msg, iface.queues,
+ (mode == KNOT_XDP_MODE_FULL ? "native" : "emulated"),
+ route_check ? ", route check" : "");
+ }
+
+ return new_if;
+#endif
+}
+
+/*!
+ * \brief Create and initialize new interface.
+ *
+ * Both TCP and UDP sockets will be created for the interface.
+ *
+ * \param addr Socket address.
+ * \param udp_thread_count Number of created UDP workers.
+ * \param tcp_thread_count Number of created TCP workers.
+ * \param tcp_reuseport Indication if reuseport on TCP is enabled.
+ * \param socket_affinity Indication if CBPF should be attached.
+ *
+ * \retval Pointer to a new initialized interface.
+ * \retval NULL if error.
+ */
+static iface_t *server_init_iface(struct sockaddr_storage *addr,
+ int udp_thread_count, int tcp_thread_count,
+ bool tcp_reuseport, bool socket_affinity)
+{
+ iface_t *new_if = calloc(1, sizeof(*new_if));
+ if (new_if == NULL) {
+ log_error("failed to initialize interface");
+ return NULL;
+ }
+ memcpy(&new_if->addr, addr, sizeof(*addr));
+
+ /* Convert to string address format. */
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), addr);
+
+ int udp_socket_count = 1;
+ int udp_bind_flags = 0;
+ int tcp_socket_count = 1;
+ int tcp_bind_flags = 0;
+
+#ifdef ENABLE_REUSEPORT
+ udp_socket_count = udp_thread_count;
+ udp_bind_flags |= NET_BIND_MULTIPLE;
+
+ if (tcp_reuseport) {
+ tcp_socket_count = tcp_thread_count;
+ tcp_bind_flags |= NET_BIND_MULTIPLE;
+ }
+#endif
+
+ new_if->fd_udp = malloc(udp_socket_count * sizeof(int));
+ new_if->fd_tcp = malloc(tcp_socket_count * sizeof(int));
+ if (new_if->fd_udp == NULL || new_if->fd_tcp == NULL) {
+ log_error("failed to initialize interface");
+ server_deinit_iface(new_if, true);
+ return NULL;
+ }
+
+ const mode_t unix_mode = S_IWUSR | S_IWGRP | S_IWOTH;
+
+ bool warn_bind = true;
+ bool warn_cbpf = true;
+ bool warn_bufsize = true;
+ bool warn_pktinfo = true;
+ bool warn_flag_misc = true;
+
+ /* Create bound UDP sockets. */
+ for (int i = 0; i < udp_socket_count; i++) {
+ int sock = net_bound_socket(SOCK_DGRAM, addr, udp_bind_flags, unix_mode);
+ if (sock == KNOT_EADDRNOTAVAIL) {
+ udp_bind_flags |= NET_BIND_NONLOCAL;
+ sock = net_bound_socket(SOCK_DGRAM, addr, udp_bind_flags, unix_mode);
+ if (sock >= 0 && warn_bind) {
+ log_warning("address %s UDP bound, but required nonlocal bind", addr_str);
+ warn_bind = false;
+ }
+ }
+
+ if (sock < 0) {
+ log_error("cannot bind address %s UDP (%s)", addr_str,
+ knot_strerror(sock));
+ server_deinit_iface(new_if, true);
+ return NULL;
+ }
+
+ if ((udp_bind_flags & NET_BIND_MULTIPLE) && socket_affinity) {
+ if (!server_attach_reuseport_bpf(sock, udp_socket_count) &&
+ warn_cbpf) {
+ log_warning("cannot ensure optimal CPU locality for UDP");
+ warn_cbpf = false;
+ }
+ }
+
+ if (!enlarge_net_buffers(sock, UDP_MIN_RCVSIZE, UDP_MIN_SNDSIZE) &&
+ warn_bufsize) {
+ log_warning("failed to set network buffer sizes for UDP");
+ warn_bufsize = false;
+ }
+
+ if (sockaddr_is_any(addr) && !enable_pktinfo(sock, addr->ss_family) &&
+ warn_pktinfo) {
+ log_warning("failed to enable received packet information retrieval");
+ warn_pktinfo = false;
+ }
+
+ int ret = disable_pmtudisc(sock, addr->ss_family);
+ if (ret != KNOT_EOK && warn_flag_misc) {
+ log_warning("failed to disable Path MTU discovery for IPv4/UDP (%s)",
+ knot_strerror(ret));
+ warn_flag_misc = false;
+ }
+
+ new_if->fd_udp[new_if->fd_udp_count] = sock;
+ new_if->fd_udp_count += 1;
+ }
+
+ warn_bind = true;
+ warn_cbpf = true;
+ warn_bufsize = true;
+ warn_flag_misc = true;
+
+ /* Create bound TCP sockets. */
+ for (int i = 0; i < tcp_socket_count; i++) {
+ int sock = net_bound_socket(SOCK_STREAM, addr, tcp_bind_flags, unix_mode);
+ if (sock == KNOT_EADDRNOTAVAIL) {
+ tcp_bind_flags |= NET_BIND_NONLOCAL;
+ sock = net_bound_socket(SOCK_STREAM, addr, tcp_bind_flags, unix_mode);
+ if (sock >= 0 && warn_bind) {
+ log_warning("address %s TCP bound, but required nonlocal bind", addr_str);
+ warn_bind = false;
+ }
+ }
+
+ if (sock < 0) {
+ log_error("cannot bind address %s TCP (%s)", addr_str,
+ knot_strerror(sock));
+ server_deinit_iface(new_if, true);
+ return NULL;
+ }
+
+ if (!enlarge_net_buffers(sock, TCP_MIN_RCVSIZE, TCP_MIN_SNDSIZE) &&
+ warn_bufsize) {
+ log_warning("failed to set network buffer sizes for TCP");
+ warn_bufsize = false;
+ }
+
+ new_if->fd_tcp[new_if->fd_tcp_count] = sock;
+ new_if->fd_tcp_count += 1;
+
+ /* Listen for incoming connections. */
+ int ret = listen(sock, TCP_BACKLOG_SIZE);
+ if (ret < 0) {
+ log_error("failed to listen on TCP interface %s", addr_str);
+ server_deinit_iface(new_if, true);
+ return NULL;
+ }
+
+ if ((tcp_bind_flags & NET_BIND_MULTIPLE) && socket_affinity) {
+ if (!server_attach_reuseport_bpf(sock, tcp_socket_count) &&
+ warn_cbpf) {
+ log_warning("cannot ensure optimal CPU locality for TCP");
+ warn_cbpf = false;
+ }
+ }
+
+ /* Try to enable TCP Fast Open. */
+ ret = net_bound_tfo(sock, TCP_BACKLOG_SIZE);
+ if (ret != KNOT_EOK && ret != KNOT_ENOTSUP && warn_flag_misc) {
+ log_warning("failed to enable TCP Fast Open on %s (%s)",
+ addr_str, knot_strerror(ret));
+ warn_flag_misc = false;
+ }
+ }
+
+ return new_if;
+}
+
+static void log_sock_conf(conf_t *conf)
+{
+ char buf[128] = "";
+#if defined(ENABLE_REUSEPORT)
+ strlcat(buf, "UDP", sizeof(buf));
+ if (conf->cache.srv_tcp_reuseport) {
+ strlcat(buf, "/TCP", sizeof(buf));
+ }
+ strlcat(buf, " reuseport", sizeof(buf));
+ if (conf->cache.srv_socket_affinity) {
+ strlcat(buf, ", socket affinity", sizeof(buf));
+ }
+#endif
+#if defined(TCP_FASTOPEN)
+ if (buf[0] != '\0') {
+ strlcat(buf, ", ", sizeof(buf));
+ }
+ strlcat(buf, "incoming", sizeof(buf));
+ if (conf->cache.srv_tcp_fastopen) {
+ strlcat(buf, "/outgoing", sizeof(buf));
+ }
+ strlcat(buf, " TCP Fast Open", sizeof(buf));
+#endif
+ if (buf[0] != '\0') {
+ log_info("using %s", buf);
+ }
+}
+
+/*! \brief Initialize bound sockets according to configuration. */
+static int configure_sockets(conf_t *conf, server_t *s)
+{
+ if (s->state & ServerRunning) {
+ return KNOT_EOK;
+ }
+
+ conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN);
+ conf_val_t lisxdp_val = conf_get(conf, C_XDP, C_LISTEN);
+ conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR);
+
+ if (listen_val.code == KNOT_EOK) {
+ log_sock_conf(conf);
+ } else if (lisxdp_val.code != KNOT_EOK) {
+ log_warning("no network interface configured");
+ return KNOT_EOK;
+ }
+
+#ifdef ENABLE_XDP
+ if (lisxdp_val.code == KNOT_EOK && !linux_at_least(5, 11)) {
+ struct rlimit min_limit = { RLIM_INFINITY, RLIM_INFINITY };
+ struct rlimit cur_limit = { 0 };
+ if (getrlimit(RLIMIT_MEMLOCK, &cur_limit) != 0 ||
+ cur_limit.rlim_cur != min_limit.rlim_cur ||
+ cur_limit.rlim_max != min_limit.rlim_max) {
+ int ret = setrlimit(RLIMIT_MEMLOCK, &min_limit);
+ if (ret != 0) {
+ log_error("failed to increase RLIMIT_MEMLOCK (%s)",
+ knot_strerror(errno));
+ return KNOT_ESYSTEM;
+ }
+ }
+ }
+#endif
+
+ size_t real_nifs = 0;
+ size_t nifs = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val);
+ iface_t *newlist = calloc(nifs, sizeof(*newlist));
+ if (newlist == NULL) {
+ log_error("failed to allocate memory for network sockets");
+ return KNOT_ENOMEM;
+ }
+
+ /* Normal UDP and TCP sockets. */
+ unsigned size_udp = s->handlers[IO_UDP].handler.unit->size;
+ unsigned size_tcp = s->handlers[IO_TCP].handler.unit->size;
+ bool tcp_reuseport = conf->cache.srv_tcp_reuseport;
+ bool socket_affinity = conf->cache.srv_socket_affinity;
+ char *rundir = conf_abs_path(&rundir_val, NULL);
+ while (listen_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&listen_val, rundir);
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
+ log_info("binding to interface %s", addr_str);
+
+ iface_t *new_if = server_init_iface(&addr, size_udp, size_tcp,
+ tcp_reuseport, socket_affinity);
+ if (new_if == NULL) {
+ server_deinit_iface_list(newlist, nifs);
+ free(rundir);
+ return KNOT_ERROR;
+ }
+ memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist));
+ free(new_if);
+
+ conf_val_next(&listen_val);
+ }
+ free(rundir);
+
+ /* XDP sockets. */
+ bool xdp_udp = conf->cache.xdp_udp;
+ bool xdp_tcp = conf->cache.xdp_tcp;
+ uint16_t xdp_quic = conf->cache.xdp_quic;
+ bool route_check = conf->cache.xdp_route_check;
+ unsigned thread_id = s->handlers[IO_UDP].handler.unit->size +
+ s->handlers[IO_TCP].handler.unit->size;
+ while (lisxdp_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL);
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
+ log_info("binding to XDP interface %s", addr_str);
+
+ iface_t *new_if = server_init_xdp_iface(&addr, route_check, xdp_udp,
+ xdp_tcp, xdp_quic, &thread_id);
+ if (new_if == NULL) {
+ server_deinit_iface_list(newlist, nifs);
+ return KNOT_ERROR;
+ }
+ memcpy(&newlist[real_nifs++], new_if, sizeof(*newlist));
+ free(new_if);
+
+ conf_val_next(&lisxdp_val);
+ }
+ assert(real_nifs <= nifs);
+ nifs = real_nifs;
+
+#if defined ENABLE_XDP && ENABLE_QUIC
+ if (xdp_quic > 0) {
+ char *tls_cert = conf_tls(conf, C_CERT_FILE);
+ char *tls_key = conf_tls(conf, C_KEY_FILE);
+ if (tls_cert == NULL) {
+ log_notice("QUIC, no server certificate configured, using one-time one");
+ }
+ s->quic_creds = knot_xquic_init_creds(true, tls_cert, tls_key);
+ free(tls_cert);
+ free(tls_key);
+ if (s->quic_creds == NULL) {
+ log_error("QUIC, failed to initialize server credentials");
+ server_deinit_iface_list(newlist, nifs);
+ return KNOT_ERROR;
+ }
+ }
+#endif // ENABLE_XDP && ENABLE_QUIC
+
+ /* Publish new list. */
+ s->ifaces = newlist;
+ s->n_ifaces = nifs;
+
+ /* Assign thread identifiers unique per all handlers. */
+ unsigned thread_count = 0;
+ for (unsigned proto = IO_UDP; proto <= IO_XDP; ++proto) {
+ dt_unit_t *tu = s->handlers[proto].handler.unit;
+ for (unsigned i = 0; tu != NULL && i < tu->size; ++i) {
+ s->handlers[proto].handler.thread_id[i] = thread_count++;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int server_init(server_t *server, int bg_workers)
+{
+ if (server == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Clear the structure. */
+ memset(server, 0, sizeof(server_t));
+
+ /* Initialize event scheduler. */
+ if (evsched_init(&server->sched, server) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ server->workers = worker_pool_create(bg_workers);
+ if (server->workers == NULL) {
+ evsched_deinit(&server->sched);
+ return KNOT_ENOMEM;
+ }
+
+ int ret = catalog_update_init(&server->catalog_upd);
+ if (ret != KNOT_EOK) {
+ worker_pool_destroy(server->workers);
+ evsched_deinit(&server->sched);
+ return ret;
+ }
+
+ zone_backups_init(&server->backup_ctxs);
+
+ char *catalog_dir = conf_db(conf(), C_CATALOG_DB);
+ conf_val_t catalog_size = conf_db_param(conf(), C_CATALOG_DB_MAX_SIZE);
+ catalog_init(&server->catalog, catalog_dir, conf_int(&catalog_size));
+ free(catalog_dir);
+ conf()->catalog = &server->catalog;
+
+ char *journal_dir = conf_db(conf(), C_JOURNAL_DB);
+ conf_val_t journal_size = conf_db_param(conf(), C_JOURNAL_DB_MAX_SIZE);
+ conf_val_t journal_mode = conf_db_param(conf(), C_JOURNAL_DB_MODE);
+ knot_lmdb_init(&server->journaldb, journal_dir, conf_int(&journal_size), journal_env_flags(conf_opt(&journal_mode), false), NULL);
+ free(journal_dir);
+
+ kasp_db_ensure_init(&server->kaspdb, conf());
+
+ char *timer_dir = conf_db(conf(), C_TIMER_DB);
+ conf_val_t timer_size = conf_db_param(conf(), C_TIMER_DB_MAX_SIZE);
+ knot_lmdb_init(&server->timerdb, timer_dir, conf_int(&timer_size), 0, NULL);
+ free(timer_dir);
+
+ return KNOT_EOK;
+}
+
+void server_deinit(server_t *server)
+{
+ if (server == NULL) {
+ return;
+ }
+
+ zone_backups_deinit(&server->backup_ctxs);
+
+ /* Save zone timers. */
+ if (server->zone_db != NULL) {
+ log_info("updating persistent timer DB");
+ int ret = zone_timers_write_all(&server->timerdb, server->zone_db);
+ if (ret != KNOT_EOK) {
+ log_warning("failed to update persistent timer DB (%s)",
+ knot_strerror(ret));
+ }
+ }
+
+ /* Free remaining interfaces. */
+ server_deinit_iface_list(server->ifaces, server->n_ifaces);
+
+ /* Free threads and event handlers. */
+ worker_pool_destroy(server->workers);
+
+ /* Free zone database. */
+ knot_zonedb_deep_free(&server->zone_db, true);
+
+ /* Free remaining events. */
+ evsched_deinit(&server->sched);
+
+ /* Free catalog zone context. */
+ catalog_update_clear(&server->catalog_upd);
+ catalog_update_deinit(&server->catalog_upd);
+ catalog_deinit(&server->catalog);
+
+ /* Close persistent timers DB. */
+ knot_lmdb_deinit(&server->timerdb);
+
+ /* Close kasp_db. */
+ knot_lmdb_deinit(&server->kaspdb);
+
+ /* Close journal database if open. */
+ knot_lmdb_deinit(&server->journaldb);
+
+ /* Close and deinit connection pool. */
+ conn_pool_deinit(global_conn_pool);
+ global_conn_pool = NULL;
+ knot_unreachables_deinit(&global_unreachables);
+
+#if defined ENABLE_XDP && ENABLE_QUIC
+ knot_xquic_free_creds(server->quic_creds);
+#endif // ENABLE_XDP && ENABLE_QUIC
+}
+
+static int server_init_handler(server_t *server, int index, int thread_count,
+ runnable_t runnable, runnable_t destructor)
+{
+ /* Initialize */
+ iohandler_t *h = &server->handlers[index].handler;
+ memset(h, 0, sizeof(iohandler_t));
+ h->server = server;
+ h->unit = dt_create(thread_count, runnable, destructor, h);
+ if (h->unit == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ h->thread_state = calloc(thread_count, sizeof(unsigned));
+ if (h->thread_state == NULL) {
+ dt_delete(&h->unit);
+ return KNOT_ENOMEM;
+ }
+
+ h->thread_id = calloc(thread_count, sizeof(unsigned));
+ if (h->thread_id == NULL) {
+ free(h->thread_state);
+ dt_delete(&h->unit);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+static void server_free_handler(iohandler_t *h)
+{
+ if (h == NULL || h->server == NULL) {
+ return;
+ }
+
+ /* Wait for threads to finish */
+ if (h->unit) {
+ dt_stop(h->unit);
+ dt_join(h->unit);
+ }
+
+ /* Destroy worker context. */
+ dt_delete(&h->unit);
+ free(h->thread_state);
+ free(h->thread_id);
+}
+
+static void worker_wait_cb(worker_pool_t *pool)
+{
+ systemd_zone_load_timeout_notify();
+
+ static uint64_t last_ns = 0;
+ struct timespec now = time_now();
+ uint64_t now_ns = 1000000000 * now.tv_sec + now.tv_nsec;
+ /* Too frequent worker_pool_status() call with many zones is expensive. */
+ if (now_ns - last_ns > 1000000000) {
+ int running, queued;
+ worker_pool_status(pool, true, &running, &queued);
+ systemd_tasks_status_notify(running + queued);
+ last_ns = now_ns;
+ }
+}
+
+int server_start(server_t *server, bool async)
+{
+ if (server == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Start workers. */
+ worker_pool_start(server->workers);
+
+ /* Wait for enqueued events if not asynchronous. */
+ if (!async) {
+ worker_pool_wait_cb(server->workers, worker_wait_cb);
+ systemd_tasks_status_notify(0);
+ }
+
+ /* Start evsched handler. */
+ evsched_start(&server->sched);
+
+ /* Start I/O handlers. */
+ server->state |= ServerRunning;
+ for (int proto = IO_UDP; proto <= IO_XDP; ++proto) {
+ if (server->handlers[proto].size > 0) {
+ int ret = dt_start(server->handlers[proto].handler.unit);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+void server_wait(server_t *server)
+{
+ if (server == NULL) {
+ return;
+ }
+
+ evsched_join(&server->sched);
+ worker_pool_join(server->workers);
+
+ for (int proto = IO_UDP; proto <= IO_XDP; ++proto) {
+ if (server->handlers[proto].size > 0) {
+ server_free_handler(&server->handlers[proto].handler);
+ }
+ }
+}
+
+static int reload_conf(conf_t *new_conf)
+{
+ yp_schema_purge_dynamic(new_conf->schema);
+
+ /* Re-load common modules. */
+ int ret = conf_mod_load_common(new_conf);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Re-import config file if specified. */
+ const char *filename = conf()->filename;
+ if (filename != NULL) {
+ log_info("reloading configuration file '%s'", filename);
+
+ /* Import the configuration file. */
+ ret = conf_import(new_conf, filename, true, false);
+ if (ret != KNOT_EOK) {
+ log_error("failed to load configuration file (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+ } else {
+ log_info("reloading configuration database '%s'",
+ knot_db_lmdb_get_path(new_conf->db));
+
+ /* Re-load extra modules. */
+ for (conf_iter_t iter = conf_iter(new_conf, C_MODULE);
+ iter.code == KNOT_EOK; conf_iter_next(new_conf, &iter)) {
+ conf_val_t id = conf_iter_id(new_conf, &iter);
+ conf_val_t file = conf_id_get(new_conf, C_MODULE, C_FILE, &id);
+ ret = conf_mod_load_extra(new_conf, conf_str(&id), conf_str(&file),
+ MOD_EXPLICIT);
+ if (ret != KNOT_EOK) {
+ conf_iter_finish(new_conf, &iter);
+ return ret;
+ }
+ }
+ }
+
+ conf_mod_load_purge(new_conf, false);
+
+ // Migrate from old schema.
+ ret = conf_migrate(new_conf);
+ if (ret != KNOT_EOK) {
+ log_error("failed to migrate configuration (%s)", knot_strerror(ret));
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Check if parameter listen(-xdp) has been changed since knotd started. */
+static bool listen_changed(conf_t *conf, server_t *server)
+{
+ assert(server->ifaces);
+
+ conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN);
+ conf_val_t lisxdp_val = conf_get(conf, C_XDP, C_LISTEN);
+ size_t new_count = conf_val_count(&listen_val) + conf_val_count(&lisxdp_val);
+ size_t old_count = server->n_ifaces;
+ if (new_count != old_count) {
+ return true;
+ }
+
+ conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&rundir_val, NULL);
+ size_t matches = 0;
+
+ /* Find matching interfaces. */
+ while (listen_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&listen_val, rundir);
+ bool found = false;
+ for (size_t i = 0; i < server->n_ifaces; i++) {
+ if (sockaddr_cmp(&addr, &server->ifaces[i].addr, false) == 0) {
+ matches++;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ break;
+ }
+ conf_val_next(&listen_val);
+ }
+ free(rundir);
+
+ while (lisxdp_val.code == KNOT_EOK) {
+ struct sockaddr_storage addr = conf_addr(&lisxdp_val, NULL);
+ bool found = false;
+ for (size_t i = 0; i < server->n_ifaces; i++) {
+ if (sockaddr_cmp(&addr, &server->ifaces[i].addr, false) == 0) {
+ matches++;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ break;
+ }
+ conf_val_next(&lisxdp_val);
+ }
+
+ return matches != old_count;
+}
+
+/*! \brief Log warnings if config change requires a restart. */
+static void warn_server_reconfigure(conf_t *conf, server_t *server)
+{
+ const char *msg = "changes of %s require restart to take effect";
+
+ static bool warn_tcp_reuseport = true;
+ static bool warn_socket_affinity = true;
+ static bool warn_udp = true;
+ static bool warn_tcp = true;
+ static bool warn_bg = true;
+ static bool warn_listen = true;
+ static bool warn_xdp_udp = true;
+ static bool warn_xdp_tcp = true;
+ static bool warn_xdp_quic = true;
+ static bool warn_route_check = true;
+ static bool warn_rmt_pool_limit = true;
+
+ if (warn_tcp_reuseport && conf->cache.srv_tcp_reuseport != conf_get_bool(conf, C_SRV, C_TCP_REUSEPORT)) {
+ log_warning(msg, &C_TCP_REUSEPORT[1]);
+ warn_tcp_reuseport = false;
+ }
+
+ if (warn_socket_affinity && conf->cache.srv_socket_affinity != conf_get_bool(conf, C_SRV, C_SOCKET_AFFINITY)) {
+ log_warning(msg, &C_SOCKET_AFFINITY[1]);
+ warn_socket_affinity = false;
+ }
+
+ if (warn_udp && server->handlers[IO_UDP].size != conf_udp_threads(conf)) {
+ log_warning(msg, &C_UDP_WORKERS[1]);
+ warn_udp = false;
+ }
+
+ if (warn_tcp && server->handlers[IO_TCP].size != conf_tcp_threads(conf)) {
+ log_warning(msg, &C_TCP_WORKERS[1]);
+ warn_tcp = false;
+ }
+
+ if (warn_bg && conf->cache.srv_bg_threads != conf_bg_threads(conf)) {
+ log_warning(msg, &C_BG_WORKERS[1]);
+ warn_bg = false;
+ }
+
+ if (warn_listen && server->ifaces != NULL && listen_changed(conf, server)) {
+ log_warning(msg, "listen(-xdp)");
+ warn_listen = false;
+ }
+
+ if (warn_xdp_udp && conf->cache.xdp_udp != conf_get_bool(conf, C_XDP, C_UDP)) {
+ log_warning(msg, &C_UDP[1]);
+ warn_xdp_udp = false;
+ }
+
+ if (warn_xdp_tcp && conf->cache.xdp_tcp != conf_get_bool(conf, C_XDP, C_TCP)) {
+ log_warning(msg, &C_TCP[1]);
+ warn_xdp_tcp = false;
+ }
+
+ if (warn_xdp_quic && (bool)conf->cache.xdp_quic != conf_get_bool(conf, C_XDP, C_QUIC)) {
+ log_warning(msg, &C_QUIC[1]);
+ warn_xdp_quic = false;
+ }
+
+ if (warn_xdp_quic && conf->cache.xdp_quic > 0 &&
+ conf->cache.xdp_quic != conf_get_int(conf, C_XDP, C_QUIC_PORT)) {
+ log_warning(msg, &C_QUIC_PORT[1]);
+ warn_xdp_quic = false;
+ }
+
+ if (warn_route_check && conf->cache.xdp_route_check != conf_get_bool(conf, C_XDP, C_ROUTE_CHECK)) {
+ log_warning(msg, &C_ROUTE_CHECK[1]);
+ warn_route_check = false;
+ }
+
+ if (warn_rmt_pool_limit && global_conn_pool != NULL &&
+ global_conn_pool->capacity != conf_get_int(conf, C_SRV, C_RMT_POOL_LIMIT)) {
+ log_warning(msg, &C_RMT_POOL_LIMIT[1]);
+ warn_rmt_pool_limit = false;
+ }
+}
+
+int server_reload(server_t *server, reload_t mode)
+{
+ if (server == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ systemd_reloading_notify();
+
+ /* Check for no edit mode. */
+ if (conf()->io.txn != NULL) {
+ log_warning("reload aborted due to active configuration transaction");
+ systemd_ready_notify();
+ return KNOT_TXN_EEXISTS;
+ }
+
+ conf_t *new_conf = NULL;
+ int ret = conf_clone(&new_conf);
+ if (ret != KNOT_EOK) {
+ log_error("failed to initialize configuration (%s)",
+ knot_strerror(ret));
+ systemd_ready_notify();
+ return ret;
+ }
+
+ yp_flag_t flags = conf()->io.flags;
+ bool full = !(flags & CONF_IO_FACTIVE);
+ bool reuse_modules = !full && !(flags & CONF_IO_FRLD_MOD);
+
+ /* Reload configuration and modules if full reload or a module change. */
+ if (full || !reuse_modules) {
+ ret = reload_conf(new_conf);
+ if (ret != KNOT_EOK) {
+ conf_free(new_conf);
+ systemd_ready_notify();
+ return ret;
+ }
+
+ conf_activate_modules(new_conf, server, NULL, new_conf->query_modules,
+ &new_conf->query_plan);
+ }
+
+ conf_update_flag_t upd_flags = CONF_UPD_FNOFREE;
+ if (!full) {
+ upd_flags |= CONF_UPD_FCONFIO;
+ }
+ if (reuse_modules) {
+ upd_flags |= CONF_UPD_FMODULES;
+ }
+
+ /* Update to the new config. */
+ conf_t *old_conf = conf_update(new_conf, upd_flags);
+
+ /* Reload each component if full reload or a specific one if required. */
+ if (full || (flags & CONF_IO_FRLD_LOG)) {
+ log_reconfigure(conf());
+ }
+ if (full || (flags & CONF_IO_FRLD_SRV)) {
+ (void)server_reconfigure(conf(), server);
+ warn_server_reconfigure(conf(), server);
+ stats_reconfigure(conf(), server);
+ }
+ if (full || (flags & (CONF_IO_FRLD_ZONES | CONF_IO_FRLD_ZONE))) {
+ server_update_zones(conf(), server, mode);
+ }
+
+ /* Free old config needed for module unload in zone reload. */
+ conf_free(old_conf);
+
+ if (full) {
+ log_info("configuration reloaded");
+ } else {
+ // Reset confio reload context.
+ conf()->io.flags = YP_FNONE;
+ if (conf()->io.zones != NULL) {
+ trie_clear(conf()->io.zones);
+ }
+ }
+
+ systemd_ready_notify();
+
+ return KNOT_EOK;
+}
+
+void server_stop(server_t *server)
+{
+ log_info("stopping server");
+ systemd_stopping_notify();
+
+ /* Stop scheduler. */
+ evsched_stop(&server->sched);
+ /* Interrupt background workers. */
+ worker_pool_stop(server->workers);
+
+ /* Clear 'running' flag. */
+ server->state &= ~ServerRunning;
+}
+
+static int set_handler(server_t *server, int index, unsigned size, runnable_t run)
+{
+ /* Initialize I/O handlers. */
+ int ret = server_init_handler(server, index, size, run, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ server->handlers[index].size = size;
+
+ return KNOT_EOK;
+}
+
+static int configure_threads(conf_t *conf, server_t *server)
+{
+ int ret = set_handler(server, IO_UDP, conf->cache.srv_udp_threads, udp_master);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (conf->cache.srv_xdp_threads > 0) {
+ ret = set_handler(server, IO_XDP, conf->cache.srv_xdp_threads, udp_master);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return set_handler(server, IO_TCP, conf->cache.srv_tcp_threads, tcp_master);
+}
+
+static int reconfigure_journal_db(conf_t *conf, server_t *server)
+{
+ char *journal_dir = conf_db(conf, C_JOURNAL_DB);
+ conf_val_t journal_size = conf_db_param(conf, C_JOURNAL_DB_MAX_SIZE);
+ conf_val_t journal_mode = conf_db_param(conf, C_JOURNAL_DB_MODE);
+ int ret = knot_lmdb_reinit(&server->journaldb, journal_dir, conf_int(&journal_size),
+ journal_env_flags(conf_opt(&journal_mode), false));
+ if (ret != KNOT_EOK) {
+ log_warning("ignored reconfiguration of journal DB (%s)", knot_strerror(ret));
+ }
+ free(journal_dir);
+
+ return KNOT_EOK; // not "ret"
+}
+
+static int reconfigure_kasp_db(conf_t *conf, server_t *server)
+{
+ char *kasp_dir = conf_db(conf, C_KASP_DB);
+ conf_val_t kasp_size = conf_db_param(conf, C_KASP_DB_MAX_SIZE);
+ int ret = knot_lmdb_reinit(&server->kaspdb, kasp_dir, conf_int(&kasp_size), 0);
+ if (ret != KNOT_EOK) {
+ log_warning("ignored reconfiguration of KASP DB (%s)", knot_strerror(ret));
+ }
+ free(kasp_dir);
+
+ return KNOT_EOK; // not "ret"
+}
+
+static int reconfigure_timer_db(conf_t *conf, server_t *server)
+{
+ char *timer_dir = conf_db(conf, C_TIMER_DB);
+ conf_val_t timer_size = conf_db_param(conf, C_TIMER_DB_MAX_SIZE);
+ int ret = knot_lmdb_reconfigure(&server->timerdb, timer_dir, conf_int(&timer_size), 0);
+ free(timer_dir);
+ return ret;
+}
+
+static int reconfigure_remote_pool(conf_t *conf)
+{
+ conf_val_t val = conf_get(conf, C_SRV, C_RMT_POOL_LIMIT);
+ size_t limit = conf_int(&val);
+ val = conf_get(conf, C_SRV, C_RMT_POOL_TIMEOUT);
+ knot_timediff_t timeout = conf_int(&val);
+ if (global_conn_pool == NULL && limit > 0) {
+ conn_pool_t *new_pool = conn_pool_init(limit, timeout);
+ if (new_pool == NULL) {
+ return KNOT_ENOMEM;
+ }
+ global_conn_pool = new_pool;
+ } else {
+ (void)conn_pool_timeout(global_conn_pool, timeout);
+ }
+
+ val = conf_get(conf, C_SRV, C_RMT_RETRY_DELAY);
+ int delay_ms = conf_int(&val);
+ if (global_unreachables == NULL && delay_ms > 0) {
+ global_unreachables = knot_unreachables_init(delay_ms);
+ } else {
+ (void)knot_unreachables_ttl(global_unreachables, delay_ms);
+ }
+
+ return KNOT_EOK;
+}
+
+int server_reconfigure(conf_t *conf, server_t *server)
+{
+ if (conf == NULL || server == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+
+ /* First reconfiguration. */
+ if (!(server->state & ServerRunning)) {
+ log_info("Knot DNS %s starting", PACKAGE_VERSION);
+
+ size_t mapsize = conf->mapsize / (1024 * 1024);
+ if (conf->filename != NULL) {
+ log_info("loaded configuration file '%s', mapsize %zu MiB",
+ conf->filename, mapsize);
+ } else {
+ log_info("loaded configuration database '%s', mapsize %zu MiB",
+ knot_db_lmdb_get_path(conf->db), mapsize);
+ }
+
+ /* Configure server threads. */
+ if ((ret = configure_threads(conf, server)) != KNOT_EOK) {
+ log_error("failed to configure server threads (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Configure sockets. */
+ if ((ret = configure_sockets(conf, server)) != KNOT_EOK) {
+ return ret;
+ }
+
+ if (conf_lmdb_readers(conf) > CONF_MAX_DB_READERS) {
+ log_warning("config, exceeded number of database readers");
+ }
+ }
+
+ /* Reconfigure journal DB. */
+ if ((ret = reconfigure_journal_db(conf, server)) != KNOT_EOK) {
+ log_error("failed to reconfigure journal DB (%s)",
+ knot_strerror(ret));
+ }
+
+ /* Reconfigure KASP DB. */
+ if ((ret = reconfigure_kasp_db(conf, server)) != KNOT_EOK) {
+ log_error("failed to reconfigure KASP DB (%s)",
+ knot_strerror(ret));
+ }
+
+ /* Reconfigure Timer DB. */
+ if ((ret = reconfigure_timer_db(conf, server)) != KNOT_EOK) {
+ log_error("failed to reconfigure Timer DB (%s)",
+ knot_strerror(ret));
+ }
+
+ /* Reconfigure connection pool. */
+ if ((ret = reconfigure_remote_pool(conf)) != KNOT_EOK) {
+ log_error("failed to reconfigure remote pool (%s)",
+ knot_strerror(ret));
+ }
+
+ return KNOT_EOK;
+}
+
+void server_update_zones(conf_t *conf, server_t *server, reload_t mode)
+{
+ if (conf == NULL || server == NULL) {
+ return;
+ }
+
+ /* Prevent emitting of new zone events. */
+ if (server->zone_db) {
+ knot_zonedb_foreach(server->zone_db, zone_events_freeze);
+ }
+
+ /* Suspend adding events to worker pool queue, wait for queued events. */
+ evsched_pause(&server->sched);
+ worker_pool_wait(server->workers);
+
+ /* Reload zone database and free old zones. */
+ zonedb_reload(conf, server, mode);
+
+ /* Trim extra heap. */
+ mem_trim();
+
+ /* Resume processing events on new zones. */
+ evsched_resume(&server->sched);
+ if (server->zone_db) {
+ knot_zonedb_foreach(server->zone_db, zone_events_start);
+ }
+}
diff --git a/src/knot/server/server.h b/src/knot/server/server.h
new file mode 100644
index 0000000..5adafdb
--- /dev/null
+++ b/src/knot/server/server.h
@@ -0,0 +1,203 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdatomic.h>
+
+#include "knot/conf/conf.h"
+#include "knot/catalog/catalog_update.h"
+#include "knot/common/evsched.h"
+#include "knot/common/fdset.h"
+#include "knot/journal/knot_lmdb.h"
+#include "knot/server/dthreads.h"
+#include "knot/worker/pool.h"
+#include "knot/zone/backup.h"
+#include "knot/zone/zonedb.h"
+
+struct server;
+struct knot_xdp_socket;
+struct knot_quic_creds;
+
+/*!
+ * \brief I/O handler structure.
+ */
+typedef struct {
+ struct server *server; /*!< Reference to server. */
+ dt_unit_t *unit; /*!< Threading unit. */
+ unsigned *thread_state; /*!< Thread states. */
+ unsigned *thread_id; /*!< Thread identifiers per all handlers. */
+} iohandler_t;
+
+/*!
+ * \brief Server state flags.
+ */
+typedef enum {
+ ServerIdle = 0 << 0, /*!< Server is idle. */
+ ServerRunning = 1 << 0, /*!< Server is running. */
+} server_state_t;
+
+/*!
+ * \brief Server reload kinds.
+ */
+typedef enum {
+ RELOAD_NONE = 0,
+ RELOAD_FULL = 1 << 0, /*!< Reload the server and all zones. */
+ RELOAD_COMMIT = 1 << 1, /*!< Process changes from dynamic configuration. */
+ RELOAD_ZONES = 1 << 2, /*!< Reload all zones. */
+ RELOAD_CATALOG = 1 << 3, /*!< Process catalog zone changes. */
+} reload_t;
+
+/*!
+ * \brief Server interface structure.
+ */
+typedef struct {
+ int *fd_udp;
+ unsigned fd_udp_count;
+ int *fd_tcp;
+ unsigned fd_tcp_count;
+ int *fd_xdp;
+ unsigned fd_xdp_count;
+ unsigned xdp_first_thread_id;
+ struct knot_xdp_socket **xdp_sockets;
+ struct sockaddr_storage addr;
+} iface_t;
+
+/*!
+ * \brief Handler indexes.
+ */
+enum {
+ IO_UDP = 0,
+ IO_TCP = 1,
+ IO_XDP = 2,
+};
+
+/*!
+ * \brief Main server structure.
+ *
+ * Keeps references to all important structures needed for operation.
+ */
+typedef struct server {
+ /*! \brief Server state tracking. */
+ volatile unsigned state;
+
+ knot_zonedb_t *zone_db;
+ knot_lmdb_db_t timerdb;
+ knot_lmdb_db_t journaldb;
+ knot_lmdb_db_t kaspdb;
+ catalog_t catalog;
+
+ /*! \brief I/O handlers. */
+ struct {
+ unsigned size;
+ iohandler_t handler;
+ } handlers[3];
+
+ /*! \brief Background jobs. */
+ worker_pool_t *workers;
+
+ /*! \brief Event scheduler. */
+ evsched_t sched;
+
+ /*! \brief List of interfaces. */
+ iface_t *ifaces;
+ size_t n_ifaces;
+
+ /*! \brief Pending changes to catalog member zones, update indication. */
+ catalog_update_t catalog_upd;
+ atomic_bool catalog_upd_signal;
+
+ /*! \brief Context of pending zones' backup. */
+ zone_backup_ctxs_t backup_ctxs;
+
+ /*! \brief Crendentials context for QUIC. */
+ struct knot_quic_creds *quic_creds;
+} server_t;
+
+/*!
+ * \brief Initializes the server structure.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ */
+int server_init(server_t *server, int bg_workers);
+
+/*!
+ * \brief Properly destroys the server structure.
+ *
+ * \param server Server structure to be used for operation.
+ */
+void server_deinit(server_t *server);
+
+/*!
+ * \brief Starts the server.
+ *
+ * \param server Server structure to be used for operation.
+ * \param async Don't wait for zones to load if true.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL on invalid parameters.
+ *
+ */
+int server_start(server_t *server, bool async);
+
+/*!
+ * \brief Waits for the server to finish.
+ *
+ * \param server Server structure to be used for operation.
+ *
+ */
+void server_wait(server_t *server);
+
+/*!
+ * \brief Reload server configuration.
+ *
+ * \param server Server instance.
+ * \param mode Reload mode.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int server_reload(server_t *server, reload_t mode);
+
+/*!
+ * \brief Requests server to stop.
+ *
+ * \param server Server structure to be used for operation.
+ */
+void server_stop(server_t *server);
+
+/*!
+ * \brief Server reconfiguration routine.
+ *
+ * Routine for dynamic server reconfiguration.
+ *
+ * \param conf Configuration.
+ * \param server Server instance.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int server_reconfigure(conf_t *conf, server_t *server);
+
+/*!
+ * \brief Reconfigure zone database.
+ *
+ * Routine for dynamic server zones reconfiguration.
+ *
+ * \param conf Configuration.
+ * \param server Server instance.
+ * \param mode Reload mode.
+ */
+void server_update_zones(conf_t *conf, server_t *server, reload_t mode);
diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c
new file mode 100644
index 0000000..433ca9b
--- /dev/null
+++ b/src/knot/server/tcp-handler.c
@@ -0,0 +1,380 @@
+/* Copyright (C) 2023 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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <urcu.h>
+#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD)
+#include <sys/uio.h>
+#endif // HAVE_SYS_UIO_H
+
+#include "knot/server/server.h"
+#include "knot/server/tcp-handler.h"
+#include "knot/common/log.h"
+#include "knot/common/fdset.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/query/layer.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "contrib/net.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+#include "contrib/ucw/mempool.h"
+
+/*! \brief TCP context data. */
+typedef struct tcp_context {
+ knot_layer_t layer; /*!< Query processing layer. */
+ server_t *server; /*!< Name server structure. */
+ struct iovec iov[2]; /*!< TX/RX buffers. */
+ unsigned client_threshold; /*!< Index of first TCP client. */
+ struct timespec last_poll_time; /*!< Time of the last socket poll. */
+ bool is_throttled; /*!< TCP connections throttling switch. */
+ fdset_t set; /*!< Set of server/client sockets. */
+ unsigned thread_id; /*!< Thread identifier. */
+ unsigned max_worker_fds; /*!< Max TCP clients per worker configuration + no. of ifaces. */
+ int idle_timeout; /*!< [s] TCP idle timeout configuration. */
+ int io_timeout; /*!< [ms] TCP send/recv timeout configuration. */
+} tcp_context_t;
+
+#define TCP_SWEEP_INTERVAL 2 /*!< [secs] granularity of connection sweeping. */
+
+static void update_sweep_timer(struct timespec *timer)
+{
+ *timer = time_now();
+ timer->tv_sec += TCP_SWEEP_INTERVAL;
+}
+
+static void update_tcp_conf(tcp_context_t *tcp)
+{
+ rcu_read_lock();
+ conf_t *pconf = conf();
+ tcp->max_worker_fds = tcp->client_threshold + \
+ MAX(pconf->cache.srv_tcp_max_clients / pconf->cache.srv_tcp_threads, 1);
+ tcp->idle_timeout = pconf->cache.srv_tcp_idle_timeout;
+ tcp->io_timeout = pconf->cache.srv_tcp_io_timeout;
+ rcu_read_unlock();
+}
+
+/*! \brief Sweep TCP connection. */
+static fdset_sweep_state_t tcp_sweep(fdset_t *set, int fd, _unused_ void *data)
+{
+ assert(set && fd >= 0);
+
+ /* Best-effort, name and shame. */
+ struct sockaddr_storage ss = { 0 };
+ socklen_t len = sizeof(struct sockaddr_storage);
+ if (getpeername(fd, (struct sockaddr *)&ss, &len) == 0) {
+ char addr_str[SOCKADDR_STRLEN];
+ sockaddr_tostr(addr_str, sizeof(addr_str), &ss);
+ log_notice("TCP, terminated inactive client, address %s", addr_str);
+ }
+
+ return FDSET_SWEEP;
+}
+
+static bool tcp_active_state(int state)
+{
+ return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL);
+}
+
+static bool tcp_send_state(int state)
+{
+ return (state != KNOT_STATE_FAIL && state != KNOT_STATE_NOOP);
+}
+
+static void tcp_log_error(struct sockaddr_storage *ss, const char *operation, int ret)
+{
+ /* Don't log ECONN as it usually means client closed the connection. */
+ if (ret == KNOT_ETIMEOUT) {
+ char addr_str[SOCKADDR_STRLEN];
+ sockaddr_tostr(addr_str, sizeof(addr_str), ss);
+ log_debug("TCP, failed to %s due to IO timeout, closing connection, address %s",
+ operation, addr_str);
+ }
+}
+
+static unsigned tcp_set_ifaces(const iface_t *ifaces, size_t n_ifaces,
+ fdset_t *fds, int thread_id)
+{
+ if (n_ifaces == 0) {
+ return 0;
+ }
+
+ for (const iface_t *i = ifaces; i != ifaces + n_ifaces; i++) {
+ if (i->fd_tcp_count == 0) { // Ignore XDP interface.
+ assert(i->fd_xdp_count > 0);
+ continue;
+ }
+
+ int tcp_id = 0;
+#ifdef ENABLE_REUSEPORT
+ if (conf()->cache.srv_tcp_reuseport) {
+ /* Note: thread_ids start with UDP threads, TCP threads follow. */
+ assert((i->fd_udp_count <= thread_id) &&
+ (thread_id < i->fd_tcp_count + i->fd_udp_count));
+
+ tcp_id = thread_id - i->fd_udp_count;
+ }
+#endif
+ int ret = fdset_add(fds, i->fd_tcp[tcp_id], FDSET_POLLIN, NULL);
+ if (ret < 0) {
+ return 0;
+ }
+ }
+
+ return fdset_get_length(fds);
+}
+
+static int tcp_handle(tcp_context_t *tcp, int fd, struct iovec *rx, struct iovec *tx)
+{
+ /* Get peer name. */
+ struct sockaddr_storage ss;
+ socklen_t addrlen = sizeof(struct sockaddr_storage);
+ if (getpeername(fd, (struct sockaddr *)&ss, &addrlen) != 0) {
+ return KNOT_EADDRNOTAVAIL;
+ }
+
+ /* Create query processing parameter. */
+ knotd_qdata_params_t params = {
+ .proto = KNOTD_QUERY_PROTO_TCP,
+ .remote = &ss,
+ .socket = fd,
+ .server = tcp->server,
+ .thread_id = tcp->thread_id
+ };
+
+ rx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ tx->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+
+ /* Receive data. */
+ int recv = net_dns_tcp_recv(fd, rx->iov_base, rx->iov_len, tcp->io_timeout);
+ if (recv > 0) {
+ rx->iov_len = recv;
+ } else {
+ tcp_log_error(&ss, "receive", recv);
+ return KNOT_EOF;
+ }
+
+ /* Initialize processing layer. */
+ knot_layer_begin(&tcp->layer, &params);
+
+ /* Create packets. */
+ knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, tcp->layer.mm);
+ knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, tcp->layer.mm);
+
+ /* Input packet. */
+ int ret = knot_pkt_parse(query, 0);
+ if (ret != KNOT_EOK && query->parsed > 0) { // parsing failed (e.g. 2x OPT)
+ query->parsed--; // artificially decreasing "parsed" leads to FORMERR
+ }
+ knot_layer_consume(&tcp->layer, query);
+
+ /* Resolve until NOOP or finished. */
+ while (tcp_active_state(tcp->layer.state)) {
+ knot_layer_produce(&tcp->layer, ans);
+ /* Send, if response generation passed and wasn't ignored. */
+ if (ans->size > 0 && tcp_send_state(tcp->layer.state)) {
+ int sent = net_dns_tcp_send(fd, ans->wire, ans->size,
+ tcp->io_timeout, NULL);
+ if (sent != ans->size) {
+ tcp_log_error(&ss, "send", sent);
+ ret = KNOT_EOF;
+ break;
+ }
+ }
+ }
+
+ /* Reset after processing. */
+ knot_layer_finish(&tcp->layer);
+
+ /* Flush per-query memory (including query and answer packets). */
+ mp_flush(tcp->layer.mm->ctx);
+
+ return ret;
+}
+
+static void tcp_event_accept(tcp_context_t *tcp, unsigned i)
+{
+ /* Accept client. */
+ int fd = fdset_get_fd(&tcp->set, i);
+ int client = net_accept(fd, NULL);
+ if (client >= 0) {
+ /* Assign to fdset. */
+ int idx = fdset_add(&tcp->set, client, FDSET_POLLIN, NULL);
+ if (idx < 0) {
+ close(client);
+ return;
+ }
+
+ /* Update watchdog timer. */
+ (void)fdset_set_watchdog(&tcp->set, idx, tcp->idle_timeout);
+ }
+}
+
+static int tcp_event_serve(tcp_context_t *tcp, unsigned i)
+{
+ int ret = tcp_handle(tcp, fdset_get_fd(&tcp->set, i),
+ &tcp->iov[0], &tcp->iov[1]);
+ if (ret == KNOT_EOK) {
+ /* Update socket activity timer. */
+ (void)fdset_set_watchdog(&tcp->set, i, tcp->idle_timeout);
+ }
+
+ return ret;
+}
+
+static void tcp_wait_for_events(tcp_context_t *tcp)
+{
+ fdset_t *set = &tcp->set;
+
+ /* Check if throttled with many open TCP connections. */
+ assert(fdset_get_length(set) <= tcp->max_worker_fds);
+ tcp->is_throttled = fdset_get_length(set) == tcp->max_worker_fds;
+
+ /* If throttled, temporarily ignore new TCP connections. */
+ unsigned offset = tcp->is_throttled ? tcp->client_threshold : 0;
+
+ /* Wait for events. */
+ fdset_it_t it;
+ (void)fdset_poll(set, &it, offset, TCP_SWEEP_INTERVAL * 1000);
+
+ /* Mark the time of last poll call. */
+ tcp->last_poll_time = time_now();
+
+ /* Process events. */
+ for (; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ bool should_close = false;
+ unsigned int idx = fdset_it_get_idx(&it);
+ if (fdset_it_is_error(&it)) {
+ should_close = (idx >= tcp->client_threshold);
+ } else if (fdset_it_is_pollin(&it)) {
+ /* Master sockets - new connection to accept. */
+ if (idx < tcp->client_threshold) {
+ /* Don't accept more clients than configured. */
+ if (fdset_get_length(set) < tcp->max_worker_fds) {
+ tcp_event_accept(tcp, idx);
+ }
+ /* Client sockets - already accepted connection or
+ closed connection :-( */
+ } else if (tcp_event_serve(tcp, idx) != KNOT_EOK) {
+ should_close = true;
+ }
+ }
+
+ /* Evaluate. */
+ if (should_close) {
+ fdset_it_remove(&it);
+ }
+ }
+ fdset_it_commit(&it);
+}
+
+int tcp_master(dthread_t *thread)
+{
+ if (thread == NULL || thread->data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ iohandler_t *handler = (iohandler_t *)thread->data;
+ int thread_id = handler->thread_id[dt_get_id(thread)];
+
+#ifdef ENABLE_REUSEPORT
+ /* Set thread affinity to CPU core (overlaps with UDP/XDP). */
+ if (conf()->cache.srv_tcp_reuseport) {
+ unsigned cpu = dt_online_cpus();
+ if (cpu > 1) {
+ unsigned cpu_mask = (dt_get_id(thread) % cpu);
+ dt_setaffinity(thread, &cpu_mask, 1);
+ }
+ }
+#endif
+
+ int ret = KNOT_EOK;
+
+ /* Create big enough memory cushion. */
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, 16 * MM_DEFAULT_BLKSIZE);
+
+ /* Create TCP answering context. */
+ tcp_context_t tcp = {
+ .server = handler->server,
+ .is_throttled = false,
+ .thread_id = thread_id,
+ };
+ knot_layer_init(&tcp.layer, &mm, process_query_layer());
+
+ /* Create iovec abstraction. */
+ for (unsigned i = 0; i < 2; ++i) {
+ tcp.iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ tcp.iov[i].iov_base = malloc(tcp.iov[i].iov_len);
+ if (tcp.iov[i].iov_base == NULL) {
+ ret = KNOT_ENOMEM;
+ goto finish;
+ }
+ }
+
+ /* Initialize sweep interval and TCP configuration. */
+ struct timespec next_sweep;
+ update_sweep_timer(&next_sweep);
+ update_tcp_conf(&tcp);
+
+ /* Prepare initial buffer for listening and bound sockets. */
+ if (fdset_init(&tcp.set, FDSET_RESIZE_STEP) != KNOT_EOK) {
+ goto finish;
+ }
+
+ /* Set descriptors for the configured interfaces. */
+ tcp.client_threshold = tcp_set_ifaces(handler->server->ifaces,
+ handler->server->n_ifaces,
+ &tcp.set, thread_id);
+ if (tcp.client_threshold == 0) {
+ goto finish; /* Terminate on zero interfaces. */
+ }
+
+ for (;;) {
+ /* Check for cancellation. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Serve client requests. */
+ tcp_wait_for_events(&tcp);
+
+ /* Sweep inactive clients and refresh TCP configuration. */
+ if (tcp.last_poll_time.tv_sec >= next_sweep.tv_sec) {
+ fdset_sweep(&tcp.set, &tcp_sweep, NULL);
+ update_sweep_timer(&next_sweep);
+ update_tcp_conf(&tcp);
+ }
+ }
+
+finish:
+ free(tcp.iov[0].iov_base);
+ free(tcp.iov[1].iov_base);
+ mp_delete(mm.ctx);
+ fdset_clear(&tcp.set);
+
+ return ret;
+}
diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h
new file mode 100644
index 0000000..b60ce8f
--- /dev/null
+++ b/src/knot/server/tcp-handler.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2018 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 TCP sockets threading model.
+ *
+ * The master socket distributes incoming connections among
+ * the worker threads ("buckets"). Each threads processes it's own
+ * set of sockets, and eliminates mutual exclusion problem by doing so.
+ */
+
+#pragma once
+
+#include "knot/server/dthreads.h"
+
+#define TCP_BACKLOG_SIZE 10 /*!< TCP listen backlog size. */
+
+/*!
+ * \brief TCP handler thread runnable.
+ *
+ * Listens to both bound TCP sockets for client connections and
+ * serves TCP clients. This runnable is designed to be used as coherent
+ * and implements cancellation point.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL invalid parameters.
+ */
+int tcp_master(dthread_t *thread);
diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c
new file mode 100644
index 0000000..1e309d6
--- /dev/null
+++ b/src/knot/server/udp-handler.c
@@ -0,0 +1,575 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#define __APPLE_USE_RFC_3542
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD)
+#include <sys/uio.h>
+#endif /* HAVE_SYS_UIO_H */
+#include <unistd.h>
+
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/mempool.h"
+#include "knot/common/fdset.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/query/layer.h"
+#include "knot/server/proxyv2.h"
+#include "knot/server/server.h"
+#include "knot/server/udp-handler.h"
+#include "knot/server/xdp-handler.h"
+
+/* Buffer identifiers. */
+enum {
+ RX = 0,
+ TX = 1,
+ NBUFS = 2
+};
+
+/*! \brief UDP context data. */
+typedef struct {
+ knot_layer_t layer; /*!< Query processing layer. */
+ server_t *server; /*!< Name server structure. */
+ unsigned thread_id; /*!< Thread identifier. */
+} udp_context_t;
+
+static bool udp_state_active(int state)
+{
+ return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL);
+}
+
+static void udp_handle(udp_context_t *udp, int fd, struct sockaddr_storage *ss,
+ struct iovec *rx, struct iovec *tx, struct knot_xdp_msg *xdp_msg)
+{
+ /* Create query processing parameter. */
+ knotd_qdata_params_t params = {
+ .proto = KNOTD_QUERY_PROTO_UDP,
+ .remote = ss,
+ .socket = fd,
+ .server = udp->server,
+ .xdp_msg = xdp_msg,
+ .thread_id = udp->thread_id
+ };
+ struct sockaddr_storage proxied_remote;
+
+ /* Start query processing. */
+ knot_layer_begin(&udp->layer, &params);
+
+ /* Create packets. */
+ knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, udp->layer.mm);
+ knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, udp->layer.mm);
+
+ /* Input packet. */
+ int ret = knot_pkt_parse(query, 0);
+ if (ret != KNOT_EOK && query->parsed > 0) {
+ ret = proxyv2_header_strip(&query, params.remote, &proxied_remote);
+ if (ret == KNOT_EOK) {
+ params.remote = &proxied_remote;
+ } else {
+ query->parsed--; // artificially decreasing "parsed" leads to FORMERR
+ }
+ }
+ knot_layer_consume(&udp->layer, query);
+
+ /* Process answer. */
+ while (udp_state_active(udp->layer.state)) {
+ knot_layer_produce(&udp->layer, ans);
+ }
+
+ /* Send response only if finished successfully. */
+ if (udp->layer.state == KNOT_STATE_DONE) {
+ tx->iov_len = ans->size;
+ } else {
+ tx->iov_len = 0;
+ }
+
+ /* Reset after processing. */
+ knot_layer_finish(&udp->layer);
+
+ /* Flush per-query memory (including query and answer packets). */
+ mp_flush(udp->layer.mm->ctx);
+}
+
+typedef struct {
+ void* (*udp_init)(udp_context_t *, void *);
+ void (*udp_deinit)(void *);
+ int (*udp_recv)(int, void *);
+ void (*udp_handle)(udp_context_t *, void *);
+ void (*udp_send)(void *);
+ void (*udp_sweep)(void *); // Optional
+} udp_api_t;
+
+/*! \brief Control message to fit IP_PKTINFO or IPv6_RECVPKTINFO. */
+typedef union {
+ struct cmsghdr cmsg;
+ uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+} cmsg_pktinfo_t;
+
+static void udp_pktinfo_handle(const struct msghdr *rx, struct msghdr *tx)
+{
+ tx->msg_controllen = rx->msg_controllen;
+ if (tx->msg_controllen > 0) {
+ tx->msg_control = rx->msg_control;
+ } else {
+ // BSD has problem with zero length and not-null pointer
+ tx->msg_control = NULL;
+ }
+
+#if defined(__linux__) || defined(__APPLE__)
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(tx);
+ if (cmsg == NULL) {
+ return;
+ }
+
+ /* Unset the ifindex to not bypass the routing tables. */
+ if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
+ struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg);
+ info->ipi_spec_dst = info->ipi_addr;
+ info->ipi_ifindex = 0;
+ } else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+ struct in6_pktinfo *info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
+ info->ipi6_ifindex = 0;
+ }
+#endif
+}
+
+/* UDP recvfrom() request struct. */
+struct udp_recvfrom {
+ int fd;
+ struct sockaddr_storage addr;
+ struct msghdr msg[NBUFS];
+ struct iovec iov[NBUFS];
+ uint8_t buf[NBUFS][KNOT_WIRE_MAX_PKTSIZE];
+ cmsg_pktinfo_t pktinfo;
+};
+
+static void *udp_recvfrom_init(_unused_ udp_context_t *ctx, _unused_ void *xdp_sock)
+{
+ struct udp_recvfrom *rq = malloc(sizeof(struct udp_recvfrom));
+ if (rq == NULL) {
+ return NULL;
+ }
+ memset(rq, 0, sizeof(struct udp_recvfrom));
+
+ for (unsigned i = 0; i < NBUFS; ++i) {
+ rq->iov[i].iov_base = rq->buf + i;
+ rq->iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ rq->msg[i].msg_name = &rq->addr;
+ rq->msg[i].msg_namelen = sizeof(rq->addr);
+ rq->msg[i].msg_iov = &rq->iov[i];
+ rq->msg[i].msg_iovlen = 1;
+ rq->msg[i].msg_control = &rq->pktinfo.cmsg;
+ rq->msg[i].msg_controllen = sizeof(rq->pktinfo);
+ }
+ return rq;
+}
+
+static void udp_recvfrom_deinit(void *d)
+{
+ struct udp_recvfrom *rq = d;
+ free(rq);
+}
+
+static int udp_recvfrom_recv(int fd, void *d)
+{
+ /* Reset max lengths. */
+ struct udp_recvfrom *rq = (struct udp_recvfrom *)d;
+ rq->iov[RX].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ rq->msg[RX].msg_namelen = sizeof(struct sockaddr_storage);
+ rq->msg[RX].msg_controllen = sizeof(rq->pktinfo);
+
+ int ret = recvmsg(fd, &rq->msg[RX], MSG_DONTWAIT);
+ if (ret > 0) {
+ rq->fd = fd;
+ rq->iov[RX].iov_len = ret;
+ return 1;
+ }
+
+ return 0;
+}
+
+static void udp_recvfrom_handle(udp_context_t *ctx, void *d)
+{
+ struct udp_recvfrom *rq = d;
+
+ /* Prepare TX address. */
+ rq->msg[TX].msg_namelen = rq->msg[RX].msg_namelen;
+ rq->iov[TX].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+
+ udp_pktinfo_handle(&rq->msg[RX], &rq->msg[TX]);
+
+ /* Process received pkt. */
+ udp_handle(ctx, rq->fd, &rq->addr, &rq->iov[RX], &rq->iov[TX], NULL);
+}
+
+static void udp_recvfrom_send(void *d)
+{
+ struct udp_recvfrom *rq = d;
+ if (rq->iov[TX].iov_len > 0) {
+ (void)sendmsg(rq->fd, &rq->msg[TX], 0);
+ }
+}
+
+_unused_
+static udp_api_t udp_recvfrom_api = {
+ udp_recvfrom_init,
+ udp_recvfrom_deinit,
+ udp_recvfrom_recv,
+ udp_recvfrom_handle,
+ udp_recvfrom_send,
+};
+
+#ifdef ENABLE_RECVMMSG
+/* UDP recvmmsg() request struct. */
+struct udp_recvmmsg {
+ int fd;
+ struct sockaddr_storage addrs[RECVMMSG_BATCHLEN];
+ char *iobuf[NBUFS];
+ struct iovec *iov[NBUFS];
+ struct mmsghdr *msgs[NBUFS];
+ unsigned rcvd;
+ knot_mm_t mm;
+ cmsg_pktinfo_t pktinfo[RECVMMSG_BATCHLEN];
+};
+
+static void *udp_recvmmsg_init(_unused_ udp_context_t *ctx, _unused_ void *xdp_sock)
+{
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, sizeof(struct udp_recvmmsg));
+
+ struct udp_recvmmsg *rq = mm_alloc(&mm, sizeof(struct udp_recvmmsg));
+ memset(rq, 0, sizeof(*rq));
+ memcpy(&rq->mm, &mm, sizeof(knot_mm_t));
+
+ /* Initialize buffers. */
+ for (unsigned i = 0; i < NBUFS; ++i) {
+ rq->iobuf[i] = mm_alloc(&mm, KNOT_WIRE_MAX_PKTSIZE * RECVMMSG_BATCHLEN);
+ rq->iov[i] = mm_alloc(&mm, sizeof(struct iovec) * RECVMMSG_BATCHLEN);
+ rq->msgs[i] = mm_alloc(&mm, sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN);
+ memset(rq->msgs[i], 0, sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN);
+ for (unsigned k = 0; k < RECVMMSG_BATCHLEN; ++k) {
+ rq->iov[i][k].iov_base = rq->iobuf[i] + k * KNOT_WIRE_MAX_PKTSIZE;
+ rq->iov[i][k].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ rq->msgs[i][k].msg_hdr.msg_iov = rq->iov[i] + k;
+ rq->msgs[i][k].msg_hdr.msg_iovlen = 1;
+ rq->msgs[i][k].msg_hdr.msg_name = rq->addrs + k;
+ rq->msgs[i][k].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage);
+ rq->msgs[i][k].msg_hdr.msg_control = &rq->pktinfo[k].cmsg;
+ rq->msgs[i][k].msg_hdr.msg_controllen = sizeof(cmsg_pktinfo_t);
+ }
+ }
+
+ return rq;
+}
+
+static void udp_recvmmsg_deinit(void *d)
+{
+ struct udp_recvmmsg *rq = d;
+ if (rq != NULL) {
+ mp_delete(rq->mm.ctx);
+ }
+}
+
+static int udp_recvmmsg_recv(int fd, void *d)
+{
+ struct udp_recvmmsg *rq = d;
+
+ int n = recvmmsg(fd, rq->msgs[RX], RECVMMSG_BATCHLEN, MSG_DONTWAIT, NULL);
+ if (n > 0) {
+ rq->fd = fd;
+ rq->rcvd = n;
+ }
+ return n;
+}
+
+static void udp_recvmmsg_handle(udp_context_t *ctx, void *d)
+{
+ struct udp_recvmmsg *rq = d;
+
+ /* Handle each received message. */
+ unsigned j = 0;
+ for (unsigned i = 0; i < rq->rcvd; ++i) {
+ struct msghdr *rx = &rq->msgs[RX][i].msg_hdr;
+ struct msghdr *tx = &rq->msgs[TX][j].msg_hdr;
+
+ /* Set received bytes. */
+ rx->msg_iov->iov_len = rq->msgs[RX][i].msg_len;
+ /* Update mapping of address buffer. */
+ tx->msg_name = rx->msg_name;
+ tx->msg_namelen = rx->msg_namelen;
+
+ /* Update output message control buffer. */
+ udp_pktinfo_handle(rx, tx);
+
+ udp_handle(ctx, rq->fd, rq->addrs + i, rx->msg_iov, tx->msg_iov, NULL);
+
+ if (tx->msg_iov->iov_len > 0) {
+ rq->msgs[TX][j].msg_len = tx->msg_iov->iov_len;
+ j++;
+ } else {
+ /* Reset tainted output context. */
+ tx->msg_iov->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ }
+
+ /* Reset input context. */
+ rx->msg_iov->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ rx->msg_namelen = sizeof(struct sockaddr_storage);
+ rx->msg_controllen = sizeof(cmsg_pktinfo_t);
+ }
+ rq->rcvd = j;
+}
+
+static void udp_recvmmsg_send(void *d)
+{
+ struct udp_recvmmsg *rq = d;
+
+ (void)sendmmsg(rq->fd, rq->msgs[TX], rq->rcvd, 0);
+ for (unsigned i = 0; i < rq->rcvd; ++i) {
+ struct msghdr *tx = &rq->msgs[TX][i].msg_hdr;
+
+ /* Reset output context. */
+ tx->msg_iov->iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ }
+}
+
+static udp_api_t udp_recvmmsg_api = {
+ udp_recvmmsg_init,
+ udp_recvmmsg_deinit,
+ udp_recvmmsg_recv,
+ udp_recvmmsg_handle,
+ udp_recvmmsg_send,
+};
+#endif /* ENABLE_RECVMMSG */
+
+#ifdef ENABLE_XDP
+
+static void *xdp_recvmmsg_init(udp_context_t *ctx, void *xdp_sock)
+{
+ return xdp_handle_init(ctx->server, xdp_sock);
+}
+
+static void xdp_recvmmsg_deinit(void *d)
+{
+ if (d != NULL) {
+ xdp_handle_free(d);
+ }
+}
+
+static int xdp_recvmmsg_recv(_unused_ int fd, void *d)
+{
+ return xdp_handle_recv(d);
+}
+
+static void xdp_recvmmsg_handle(udp_context_t *ctx, void *d)
+{
+ xdp_handle_msgs(d, &ctx->layer, ctx->server, ctx->thread_id);
+}
+
+static void xdp_recvmmsg_send(void *d)
+{
+ xdp_handle_send(d);
+}
+
+static void xdp_recvmmsg_sweep(void *d)
+{
+ xdp_handle_reconfigure(d);
+ xdp_handle_sweep(d);
+}
+
+static udp_api_t xdp_recvmmsg_api = {
+ xdp_recvmmsg_init,
+ xdp_recvmmsg_deinit,
+ xdp_recvmmsg_recv,
+ xdp_recvmmsg_handle,
+ xdp_recvmmsg_send,
+ xdp_recvmmsg_sweep,
+};
+#endif /* ENABLE_XDP */
+
+static bool is_xdp_thread(const server_t *server, int thread_id)
+{
+ return server->handlers[IO_XDP].size > 0 &&
+ server->handlers[IO_XDP].handler.thread_id[0] <= thread_id;
+}
+
+static int iface_udp_fd(const iface_t *iface, int thread_id, bool xdp_thread,
+ void **xdp_socket)
+{
+ if (xdp_thread) {
+#ifdef ENABLE_XDP
+ if (thread_id < iface->xdp_first_thread_id ||
+ thread_id >= iface->xdp_first_thread_id + iface->fd_xdp_count) {
+ return -1; // Different XDP interface.
+ }
+ size_t xdp_wrk_id = thread_id - iface->xdp_first_thread_id;
+ assert(xdp_wrk_id < iface->fd_xdp_count);
+ *xdp_socket = iface->xdp_sockets[xdp_wrk_id];
+ return iface->fd_xdp[xdp_wrk_id];
+#else
+ assert(0);
+ return -1;
+#endif
+ } else { // UDP thread.
+ if (iface->fd_udp_count == 0) { // No UDP interfaces.
+ assert(iface->fd_xdp_count > 0);
+ return -1;
+ }
+#ifdef ENABLE_REUSEPORT
+ assert(thread_id < iface->fd_udp_count);
+ return iface->fd_udp[thread_id];
+#else
+ return iface->fd_udp[0];
+#endif
+ }
+}
+
+static unsigned udp_set_ifaces(const server_t *server, size_t n_ifaces, fdset_t *fds,
+ int thread_id, void **xdp_socket)
+{
+ if (n_ifaces == 0) {
+ return 0;
+ }
+
+ bool xdp_thread = is_xdp_thread(server, thread_id);
+ const iface_t *ifaces = server->ifaces;
+
+ for (const iface_t *i = ifaces; i != ifaces + n_ifaces; i++) {
+ int fd = iface_udp_fd(i, thread_id, xdp_thread, xdp_socket);
+ if (fd < 0) {
+ continue;
+ }
+ int ret = fdset_add(fds, fd, FDSET_POLLIN, NULL);
+ if (ret < 0) {
+ return 0;
+ }
+ }
+
+ assert(!xdp_thread || fdset_get_length(fds) == 1);
+ return fdset_get_length(fds);
+}
+
+int udp_master(dthread_t *thread)
+{
+ if (thread == NULL || thread->data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ iohandler_t *handler = (iohandler_t *)thread->data;
+ int thread_id = handler->thread_id[dt_get_id(thread)];
+
+ if (handler->server->n_ifaces == 0) {
+ return KNOT_EOK;
+ }
+
+ /* Set thread affinity to CPU core (same for UDP and XDP). */
+ unsigned cpu = dt_online_cpus();
+ if (cpu > 1) {
+ unsigned cpu_mask = (dt_get_id(thread) % cpu);
+ dt_setaffinity(thread, &cpu_mask, 1);
+ }
+
+ /* Choose processing API. */
+ udp_api_t *api = NULL;
+ if (is_xdp_thread(handler->server, thread_id)) {
+#ifdef ENABLE_XDP
+ api = &xdp_recvmmsg_api;
+#else
+ assert(0);
+#endif
+ } else {
+#ifdef ENABLE_RECVMMSG
+ api = &udp_recvmmsg_api;
+#else
+ api = &udp_recvfrom_api;
+#endif
+ }
+ void *api_ctx = NULL;
+
+ /* Create big enough memory cushion. */
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, 16 * MM_DEFAULT_BLKSIZE);
+
+ /* Create UDP answering context. */
+ udp_context_t udp = {
+ .server = handler->server,
+ .thread_id = thread_id,
+ };
+ knot_layer_init(&udp.layer, &mm, process_query_layer());
+
+ /* Allocate descriptors for the configured interfaces. */
+ void *xdp_socket = NULL;
+ size_t nifs = handler->server->n_ifaces;
+ fdset_t fds;
+ if (fdset_init(&fds, nifs) != KNOT_EOK) {
+ goto finish;
+ }
+ unsigned nfds = udp_set_ifaces(handler->server, nifs, &fds,
+ thread_id, &xdp_socket);
+ if (nfds == 0) {
+ goto finish;
+ }
+
+ /* Initialize the networking API. */
+ api_ctx = api->udp_init(&udp, xdp_socket);
+ if (api_ctx == NULL) {
+ goto finish;
+ }
+
+ /* Loop until all data is read. */
+ for (;;) {
+ /* Cancellation point. */
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ /* Wait for events. */
+ fdset_it_t it;
+ (void)fdset_poll(&fds, &it, 0, 1000);
+
+ /* Process the events. */
+ for (; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ if (!fdset_it_is_pollin(&it)) {
+ continue;
+ }
+ if (api->udp_recv(fdset_it_get_fd(&it), api_ctx) > 0) {
+ api->udp_handle(&udp, api_ctx);
+ api->udp_send(api_ctx);
+ }
+ }
+
+ /* Regular maintenance (XDP-TCP only). */
+ if (api->udp_sweep != NULL) {
+ api->udp_sweep(api_ctx);
+ }
+ }
+
+finish:
+ api->udp_deinit(api_ctx);
+ mp_delete(mm.ctx);
+ fdset_clear(&fds);
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h
new file mode 100644
index 0000000..b09e43e
--- /dev/null
+++ b/src/knot/server/udp-handler.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2021 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 UDP sockets threading model.
+ *
+ * The master socket locks one worker thread at a time
+ * and saves events in it's own backing store for asynchronous processing.
+ * The worker threads work asynchronously in thread pool.
+ */
+
+#pragma once
+
+#include "knot/server/dthreads.h"
+
+#define RECVMMSG_BATCHLEN 10 /*!< Default recvmmsg() batch size. */
+
+/*!
+ * \brief UDP handler thread runnable.
+ *
+ * Listen to DNS datagrams in a loop on a UDP socket and
+ * reply to them. This runnable is designed to be used as coherent
+ * and implements cancellation point.
+ *
+ * \param thread Associated thread from DThreads unit.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval KNOT_EINVAL invalid parameters.
+ */
+int udp_master(dthread_t *thread);
diff --git a/src/knot/server/xdp-handler.c b/src/knot/server/xdp-handler.c
new file mode 100644
index 0000000..3c9f6d6
--- /dev/null
+++ b/src/knot/server/xdp-handler.c
@@ -0,0 +1,506 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#ifdef ENABLE_XDP
+
+#include <assert.h>
+#include <stdlib.h>
+#include <urcu.h>
+
+#include "knot/server/xdp-handler.h"
+#include "knot/common/log.h"
+#include "knot/server/proxyv2.h"
+#include "knot/server/server.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+#include "contrib/ucw/mempool.h"
+#include "libknot/endian.h"
+#include "libknot/error.h"
+#ifdef ENABLE_QUIC
+#include "libknot/xdp/quic.h"
+#endif // ENABLE_QUIC
+#include "libknot/xdp/tcp.h"
+#include "libknot/xdp/tcp_iobuf.h"
+
+#define QUIC_MAX_SEND_PER_RECV 4
+#define QUIC_IBUFS_PER_CONN 512 /* Heuristic value: this means that e.g. for 100k allowed
+ QUIC conns, we will limit total size of input buffers to 50 MiB. */
+
+typedef struct {
+ uint64_t last_log;
+ knot_sweep_stats_t stats;
+} closed_log_ctx_t;
+
+typedef struct xdp_handle_ctx {
+ knot_xdp_socket_t *sock;
+ knot_xdp_msg_t msg_recv[XDP_BATCHLEN];
+ knot_xdp_msg_t msg_send_udp[XDP_BATCHLEN];
+ knot_tcp_relay_t relays[XDP_BATCHLEN];
+ uint32_t msg_recv_count;
+ uint32_t msg_udp_count;
+ knot_tcp_table_t *tcp_table;
+ knot_tcp_table_t *syn_table;
+
+#ifdef ENABLE_QUIC
+ knot_xquic_conn_t *quic_relays[XDP_BATCHLEN];
+ int quic_rets[XDP_BATCHLEN];
+ knot_xquic_table_t *quic_table;
+ closed_log_ctx_t quic_closed;
+#endif // ENABLE_QUIC
+
+ bool tcp;
+ size_t tcp_max_conns;
+ size_t tcp_syn_conns;
+ size_t tcp_max_inbufs;
+ size_t tcp_max_obufs;
+ uint32_t tcp_idle_close; // In microseconds.
+ uint32_t tcp_idle_reset; // In microseconds.
+ uint32_t tcp_idle_resend; // In microseconds.
+
+ uint16_t quic_port; // Network-byte order!
+ size_t quic_max_conns;
+ uint64_t quic_idle_close; // In nanoseconds.
+ size_t quic_max_inbufs;
+ size_t quic_max_obufs;
+
+ closed_log_ctx_t tcp_closed;
+} xdp_handle_ctx_t;
+
+static bool udp_state_active(int state)
+{
+ return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL);
+}
+
+static bool tcp_active_state(int state)
+{
+ return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL);
+}
+
+static bool tcp_send_state(int state)
+{
+ return (state != KNOT_STATE_FAIL && state != KNOT_STATE_NOOP);
+}
+
+static void log_closed(closed_log_ctx_t *ctx, bool tcp)
+{
+ struct timespec now = time_now();
+ uint64_t sec = now.tv_sec + now.tv_nsec / 1000000000;
+ if (sec - ctx->last_log <= 9 || (ctx->stats.total == 0)) {
+ return;
+ }
+
+ const char *proto = tcp ? "TCP" : "QUIC";
+
+ uint32_t timedout = ctx->stats.counters[KNOT_SWEEP_CTR_TIMEOUT];
+ uint32_t limit_conn = ctx->stats.counters[KNOT_SWEEP_CTR_LIMIT_CONN];
+ uint32_t limit_ibuf = ctx->stats.counters[KNOT_SWEEP_CTR_LIMIT_IBUF];
+ uint32_t limit_obuf = ctx->stats.counters[KNOT_SWEEP_CTR_LIMIT_OBUF];
+
+ if (tcp || ctx->stats.total != timedout) {
+ log_notice("%s, connection sweep, closed %u, count limit %u, inbuf limit %u, outbuf limit %u",
+ proto, timedout, limit_conn, limit_ibuf, limit_obuf);
+ } else {
+ log_debug("%s, timed out connections %u", proto, timedout);
+ }
+
+ ctx->last_log = sec;
+ knot_sweep_stats_reset(&ctx->stats);
+}
+
+void xdp_handle_reconfigure(xdp_handle_ctx_t *ctx)
+{
+ rcu_read_lock();
+ conf_t *pconf = conf();
+ ctx->tcp = pconf->cache.xdp_tcp;
+ ctx->quic_port = htobe16(pconf->cache.xdp_quic);
+ ctx->tcp_max_conns = pconf->cache.xdp_tcp_max_clients / pconf->cache.srv_xdp_threads;
+ ctx->tcp_syn_conns = 2 * ctx->tcp_max_conns;
+ ctx->tcp_max_inbufs = pconf->cache.xdp_tcp_inbuf_max_size / pconf->cache.srv_xdp_threads;
+ ctx->tcp_max_obufs = pconf->cache.xdp_tcp_outbuf_max_size / pconf->cache.srv_xdp_threads;
+ ctx->tcp_idle_close = pconf->cache.xdp_tcp_idle_close * 1000000;
+ ctx->tcp_idle_reset = pconf->cache.xdp_tcp_idle_reset * 1000000;
+ ctx->tcp_idle_resend= pconf->cache.xdp_tcp_idle_resend * 1000000;
+ ctx->quic_max_conns = pconf->cache.srv_quic_max_clients / pconf->cache.srv_xdp_threads;
+ ctx->quic_idle_close= pconf->cache.srv_quic_idle_close * 1000000000LU;
+ ctx->quic_max_inbufs= ctx->quic_max_conns * QUIC_IBUFS_PER_CONN;
+ ctx->quic_max_obufs = pconf->cache.srv_quic_obuf_max_size;
+ rcu_read_unlock();
+}
+
+void xdp_handle_free(xdp_handle_ctx_t *ctx)
+{
+ knot_tcp_table_free(ctx->tcp_table);
+ knot_tcp_table_free(ctx->syn_table);
+#ifdef ENABLE_QUIC
+ knot_xquic_table_free(ctx->quic_table);
+#endif // ENABLE_QUIC
+ free(ctx);
+}
+
+#ifdef ENABLE_QUIC
+static void quic_log_cb(const char *line)
+{
+ log_debug("QUIC: %s", line);
+}
+#endif // ENABLE_QUIC
+
+xdp_handle_ctx_t *xdp_handle_init(struct server *server, knot_xdp_socket_t *xdp_sock)
+{
+ xdp_handle_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+ ctx->sock = xdp_sock;
+
+ xdp_handle_reconfigure(ctx);
+
+ if (ctx->tcp) {
+ // NOTE: the table size don't have to equal its max usage!
+ ctx->tcp_table = knot_tcp_table_new(ctx->tcp_max_conns, NULL);
+ if (ctx->tcp_table == NULL) {
+ xdp_handle_free(ctx);
+ return NULL;
+ }
+ ctx->syn_table = knot_tcp_table_new(ctx->tcp_syn_conns, ctx->tcp_table);
+ if (ctx->syn_table == NULL) {
+ xdp_handle_free(ctx);
+ return NULL;
+ }
+ }
+
+ if (ctx->quic_port > 0) {
+#ifdef ENABLE_QUIC
+ conf_t *pconf = conf();
+ size_t udp_pl = MIN(pconf->cache.srv_udp_max_payload_ipv4, pconf->cache.srv_udp_max_payload_ipv6);
+ ctx->quic_table = knot_xquic_table_new(ctx->quic_max_conns, ctx->quic_max_inbufs,
+ ctx->quic_max_obufs, udp_pl, server->quic_creds);
+ if (ctx->quic_table == NULL) {
+ xdp_handle_free(ctx);
+ return NULL;
+ }
+ if (conf_get_bool(pconf, C_XDP, C_QUIC_LOG)) {
+ ctx->quic_table->log_cb = quic_log_cb;
+ }
+#else
+ assert(0); // verified in configuration checks
+#endif // ENABLE_QUIC
+ }
+
+ return ctx;
+}
+
+int xdp_handle_recv(xdp_handle_ctx_t *ctx)
+{
+ int ret = knot_xdp_recv(ctx->sock, ctx->msg_recv, XDP_BATCHLEN,
+ &ctx->msg_recv_count, NULL);
+ return ret == KNOT_EOK ? ctx->msg_recv_count : ret;
+}
+
+static void handle_init(knotd_qdata_params_t *params, knot_layer_t *layer,
+ knotd_query_proto_t proto, const knot_xdp_msg_t *msg,
+ const struct iovec *payload, struct sockaddr_storage *proxied_remote)
+{
+ params->proto = proto;
+ params->remote = (struct sockaddr_storage *)&msg->ip_from;
+ params->xdp_msg = msg;
+
+ knot_layer_begin(layer, params);
+
+ knot_pkt_t *query = knot_pkt_new(payload->iov_base, payload->iov_len, layer->mm);
+ int ret = knot_pkt_parse(query, 0);
+ if (ret != KNOT_EOK && query->parsed > 0) { // parsing failed (e.g. 2x OPT)
+ if (params->proto == KNOTD_QUERY_PROTO_UDP &&
+ proxyv2_header_strip(&query, params->remote, proxied_remote) == KNOT_EOK) {
+ assert(proxied_remote);
+ params->remote = proxied_remote;
+ } else {
+ query->parsed--; // artificially decreasing "parsed" leads to FORMERR
+ }
+ }
+ knot_layer_consume(layer, query);
+}
+
+static void handle_finish(knot_layer_t *layer)
+{
+ knot_layer_finish(layer);
+
+ // Flush per-query memory (including query and answer packets).
+ mp_flush(layer->mm->ctx);
+}
+
+static void handle_udp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
+ knotd_qdata_params_t *params)
+{
+ struct sockaddr_storage proxied_remote;
+
+ ctx->msg_udp_count = 0;
+
+ for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
+ knot_xdp_msg_t *msg_recv = &ctx->msg_recv[i];
+ knot_xdp_msg_t *msg_send = &ctx->msg_send_udp[ctx->msg_udp_count];
+
+ // Skip TCP or QUIC or marked (zero length) message.
+ if ((msg_recv->flags & KNOT_XDP_MSG_TCP) ||
+ msg_recv->ip_to.sin6_port == ctx->quic_port ||
+ msg_recv->payload.iov_len == 0) {
+ continue;
+ }
+
+ // Try to allocate a buffer for a reply.
+ if (knot_xdp_reply_alloc(ctx->sock, msg_recv, msg_send) != KNOT_EOK) {
+ log_notice("UDP, failed to send some packets");
+ break; // Drop the rest of the messages.
+ }
+ ctx->msg_udp_count++;
+
+ // Consume the query.
+ handle_init(params, layer, KNOTD_QUERY_PROTO_UDP, msg_recv, &msg_recv->payload,
+ &proxied_remote);
+
+ // Process the reply.
+ knot_pkt_t *ans = knot_pkt_new(msg_send->payload.iov_base,
+ msg_send->payload.iov_len, layer->mm);
+ while (udp_state_active(layer->state)) {
+ knot_layer_produce(layer, ans);
+ }
+ if (layer->state == KNOT_STATE_DONE) {
+ msg_send->payload.iov_len = ans->size;
+ } else {
+ // If not success, don't send any reply.
+ msg_send->payload.iov_len = 0;
+ }
+
+ // Reset the processing.
+ handle_finish(layer);
+ }
+}
+
+static void handle_tcp(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
+ knotd_qdata_params_t *params)
+{
+ int ret = knot_tcp_recv(ctx->relays, ctx->msg_recv, ctx->msg_recv_count,
+ ctx->tcp_table, ctx->syn_table, XDP_TCP_IGNORE_NONE);
+ if (ret != KNOT_EOK) {
+ log_notice("TCP, failed to process some packets (%s)", knot_strerror(ret));
+ return;
+ } else if (knot_tcp_relay_empty(&ctx->relays[0])) { // no TCP traffic
+ return;
+ }
+
+ uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE];
+
+ for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
+ knot_tcp_relay_t *rl = &ctx->relays[i];
+
+ // Process all complete DNS queries in one TCP stream.
+ for (size_t j = 0; j < rl->inbufs_count; j++) {
+ // Consume the query.
+ handle_init(params, layer, KNOTD_QUERY_PROTO_TCP, rl->msg, &rl->inbufs[j], NULL);
+ params->measured_rtt = rl->conn->establish_rtt;
+
+ // Process the reply.
+ knot_pkt_t *ans = knot_pkt_new(ans_buf, sizeof(ans_buf), layer->mm);
+ while (tcp_active_state(layer->state)) {
+ knot_layer_produce(layer, ans);
+ if (!tcp_send_state(layer->state)) {
+ continue;
+ }
+
+ (void)knot_tcp_reply_data(rl, ctx->tcp_table, false,
+ ans->wire, ans->size);
+ }
+
+ handle_finish(layer);
+ }
+ }
+}
+
+#ifdef ENABLE_QUIC
+static void handle_quic_stream(knot_xquic_conn_t *conn, int64_t stream_id, struct iovec *inbuf,
+ knot_layer_t *layer, knotd_qdata_params_t *params, uint8_t *ans_buf,
+ size_t ans_buf_size, const knot_xdp_msg_t *xdp_msg)
+{
+ // Consume the query.
+ handle_init(params, layer, KNOTD_QUERY_PROTO_QUIC, xdp_msg, inbuf, NULL);
+ params->measured_rtt = knot_xquic_conn_rtt(conn);
+
+ // Process the reply.
+ knot_pkt_t *ans = knot_pkt_new(ans_buf, ans_buf_size, layer->mm);
+ while (tcp_active_state(layer->state)) {
+ knot_layer_produce(layer, ans);
+ if (!tcp_send_state(layer->state)) {
+ continue;
+ }
+ if (knot_xquic_stream_add_data(conn, stream_id, ans->wire, ans->size) == NULL) {
+ break;
+ }
+ }
+
+ handle_finish(layer);
+}
+#endif // ENABLE_QUIC
+
+static void handle_quic(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
+ knotd_qdata_params_t *params)
+{
+#ifdef ENABLE_QUIC
+ if (ctx->quic_table == NULL) {
+ return;
+ }
+
+ uint8_t ans_buf[KNOT_WIRE_MAX_PKTSIZE];
+
+ for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
+ knot_xdp_msg_t *msg_recv = &ctx->msg_recv[i];
+ ctx->quic_relays[i] = NULL;
+
+ if ((msg_recv->flags & KNOT_XDP_MSG_TCP) ||
+ msg_recv->ip_to.sin6_port != ctx->quic_port ||
+ msg_recv->payload.iov_len == 0) {
+ continue;
+ }
+
+ ctx->quic_rets[i] = knot_xquic_handle(ctx->quic_table, msg_recv,
+ ctx->quic_idle_close,
+ &ctx->quic_relays[i]);
+ knot_xquic_conn_t *rl = ctx->quic_relays[i];
+
+ int64_t stream_id;
+ knot_xquic_stream_t *stream;
+
+ while (rl != NULL && (stream = knot_xquic_stream_get_process(rl, &stream_id)) != NULL) {
+ assert(stream->inbuf_fin != NULL);
+ assert(stream->inbuf_fin->iov_len > 0);
+ handle_quic_stream(rl, stream_id, stream->inbuf_fin, layer, params,
+ ans_buf, sizeof(ans_buf), &ctx->msg_recv[i]);
+ free(stream->inbuf_fin);
+ stream->inbuf_fin = NULL;
+ }
+ }
+#else
+ (void)(ctx);
+ (void)(layer);
+ (void)(params);
+#endif // ENABLE_QUIC
+}
+
+void xdp_handle_msgs(xdp_handle_ctx_t *ctx, knot_layer_t *layer,
+ server_t *server, unsigned thread_id)
+{
+ assert(ctx->msg_recv_count > 0);
+
+ knotd_qdata_params_t params = {
+ .socket = knot_xdp_socket_fd(ctx->sock),
+ .server = server,
+ .thread_id = thread_id,
+ };
+
+ knot_xdp_send_prepare(ctx->sock);
+
+ handle_udp(ctx, layer, &params);
+ if (ctx->tcp) {
+ handle_tcp(ctx, layer, &params);
+ }
+ handle_quic(ctx, layer, &params);
+
+ knot_xdp_recv_finish(ctx->sock, ctx->msg_recv, ctx->msg_recv_count);
+}
+
+void xdp_handle_send(xdp_handle_ctx_t *ctx)
+{
+ uint32_t unused;
+ int ret = knot_xdp_send(ctx->sock, ctx->msg_send_udp, ctx->msg_udp_count, &unused);
+ if (ret != KNOT_EOK) {
+ log_notice("UDP, failed to send some packets");
+ }
+ if (ctx->tcp) {
+ ret = knot_tcp_send(ctx->sock, ctx->relays, ctx->msg_recv_count,
+ XDP_BATCHLEN);
+ if (ret != KNOT_EOK) {
+ log_notice("TCP, failed to send some packets");
+ }
+ }
+#ifdef ENABLE_QUIC
+ for (uint32_t i = 0; i < ctx->msg_recv_count; i++) {
+ if (ctx->quic_relays[i] == NULL) {
+ continue;
+ }
+
+ ret = knot_xquic_send(ctx->quic_table, ctx->quic_relays[i], ctx->sock,
+ &ctx->msg_recv[i], ctx->quic_rets[i],
+ QUIC_MAX_SEND_PER_RECV, false);
+ if (ret != KNOT_EOK) {
+ log_notice("QUIC, failed to send some packets");
+ }
+ }
+ knot_xquic_cleanup(ctx->quic_relays, ctx->msg_recv_count);
+#endif // ENABLE_QUIC
+
+ (void)knot_xdp_send_finish(ctx->sock);
+
+ if (ctx->tcp) {
+ knot_tcp_cleanup(ctx->tcp_table, ctx->relays, ctx->msg_recv_count);
+ }
+}
+
+void xdp_handle_sweep(xdp_handle_ctx_t *ctx)
+{
+#ifdef ENABLE_QUIC
+ if (ctx->quic_table != NULL) {
+ knot_xquic_table_sweep(ctx->quic_table, &ctx->quic_closed.stats);
+ log_closed(&ctx->quic_closed, false);
+ }
+#endif // ENABLE_QUIC
+
+ if (!ctx->tcp) {
+ return;
+ }
+
+ int ret = KNOT_EOK;
+ uint32_t prev_total;
+ knot_tcp_relay_t sweep_relays[XDP_BATCHLEN];
+ do {
+ knot_xdp_send_prepare(ctx->sock);
+
+ prev_total = ctx->tcp_closed.stats.total;
+
+ ret = knot_tcp_sweep(ctx->tcp_table, ctx->tcp_idle_close, ctx->tcp_idle_reset,
+ ctx->tcp_idle_resend,
+ ctx->tcp_max_conns, ctx->tcp_max_inbufs, ctx->tcp_max_obufs,
+ sweep_relays, XDP_BATCHLEN, &ctx->tcp_closed.stats);
+ if (ret == KNOT_EOK) {
+ ret = knot_tcp_send(ctx->sock, sweep_relays, XDP_BATCHLEN, XDP_BATCHLEN);
+ }
+ knot_tcp_cleanup(ctx->tcp_table, sweep_relays, XDP_BATCHLEN);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ ret = knot_tcp_sweep(ctx->syn_table, UINT32_MAX, ctx->tcp_idle_reset,
+ UINT32_MAX, ctx->tcp_syn_conns, SIZE_MAX, SIZE_MAX,
+ sweep_relays, XDP_BATCHLEN, &ctx->tcp_closed.stats);
+ if (ret == KNOT_EOK) {
+ ret = knot_tcp_send(ctx->sock, sweep_relays, XDP_BATCHLEN, XDP_BATCHLEN);
+ }
+ knot_tcp_cleanup(ctx->syn_table, sweep_relays, XDP_BATCHLEN);
+
+ (void)knot_xdp_send_finish(ctx->sock);
+ } while (ret == KNOT_EOK && prev_total < ctx->tcp_closed.stats.total);
+
+ log_closed(&ctx->tcp_closed, true);
+}
+
+#endif // ENABLE_XDP
diff --git a/src/knot/server/xdp-handler.h b/src/knot/server/xdp-handler.h
new file mode 100644
index 0000000..e6374ca
--- /dev/null
+++ b/src/knot/server/xdp-handler.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#ifdef ENABLE_XDP
+
+#include "knot/query/layer.h"
+#include "libknot/xdp/xdp.h"
+
+#define XDP_BATCHLEN 32 /*!< XDP receive batch size. */
+
+struct xdp_handle_ctx;
+struct server;
+
+/*!
+ * \brief Initialize XDP packet handling context.
+ */
+struct xdp_handle_ctx *xdp_handle_init(struct server *server, knot_xdp_socket_t *sock);
+
+/*!
+ * \brief Deinitialize XDP packet handling context.
+ */
+void xdp_handle_free(struct xdp_handle_ctx *ctx);
+
+/*!
+ * \brief Receive packets thru XDP socket.
+ */
+int xdp_handle_recv(struct xdp_handle_ctx *ctx);
+
+/*!
+ * \brief Answer packets including DNS layers.
+ *
+ * \warning In case of TCP, this also sends some packets, e.g. ACK.
+ */
+void xdp_handle_msgs(struct xdp_handle_ctx *ctx, knot_layer_t *layer,
+ struct server *server, unsigned thread_id);
+
+/*!
+ * \brief Send packets thru XDP socket.
+ */
+void xdp_handle_send(struct xdp_handle_ctx *ctx);
+
+/*!
+ * \brief Check for old TCP connections and close/reset them.
+ */
+void xdp_handle_sweep(struct xdp_handle_ctx *ctx);
+
+/*!
+ * \brief Update configuration parameters of running ctx.
+ */
+void xdp_handle_reconfigure(struct xdp_handle_ctx *ctx);
+
+#endif // ENABLE_XDP
diff --git a/src/knot/updates/acl.c b/src/knot/updates/acl.c
new file mode 100644
index 0000000..b46c893
--- /dev/null
+++ b/src/knot/updates/acl.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 2022 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 "knot/updates/acl.h"
+#include "contrib/wire_ctx.h"
+
+static bool match_type(uint16_t type, conf_val_t *types)
+{
+ if (types == NULL) {
+ return true;
+ }
+
+ conf_val_reset(types);
+ while (types->code == KNOT_EOK) {
+ if (type == knot_wire_read_u64(types->data)) {
+ return true;
+ }
+ conf_val_next(types);
+ }
+
+ return false;
+}
+
+static bool match_name(const knot_dname_t *rr_owner, const knot_dname_t *name,
+ acl_update_owner_match_t match)
+{
+ if (name == NULL) {
+ return true;
+ }
+
+ int ret = knot_dname_in_bailiwick(rr_owner, name);
+ switch (match) {
+ case ACL_UPDATE_MATCH_SUBEQ:
+ return (ret >= 0);
+ case ACL_UPDATE_MATCH_EQ:
+ return (ret == 0);
+ case ACL_UPDATE_MATCH_SUB:
+ return (ret > 0);
+ default:
+ return false;
+ }
+}
+
+static bool match_names(const knot_dname_t *rr_owner, const knot_dname_t *zone_name,
+ conf_val_t *names, acl_update_owner_match_t match)
+{
+ if (names == NULL) {
+ return true;
+ }
+
+ conf_val_reset(names);
+ while (names->code == KNOT_EOK) {
+ knot_dname_storage_t full_name;
+ size_t len;
+ const uint8_t *name = conf_data(names, &len);
+ if (name[len - 1] != '\0') {
+ // Append zone name if non-FQDN.
+ wire_ctx_t ctx = wire_ctx_init(full_name, sizeof(full_name));
+ wire_ctx_write(&ctx, name, len);
+ wire_ctx_write(&ctx, zone_name, knot_dname_size(zone_name));
+ if (ctx.error != KNOT_EOK) {
+ return false;
+ }
+ name = full_name;
+ }
+ if (match_name(rr_owner, name, match)) {
+ return true;
+ }
+ conf_val_next(names);
+ }
+
+ return false;
+}
+
+static bool update_match(conf_t *conf, conf_val_t *acl, knot_dname_t *key_name,
+ const knot_dname_t *zone_name, knot_pkt_t *query)
+{
+ if (query == NULL) {
+ return true;
+ }
+
+ conf_val_t val_types = conf_id_get(conf, C_ACL, C_UPDATE_TYPE, acl);
+ conf_val_t *types = (conf_val_count(&val_types) > 0) ? &val_types : NULL;
+
+ conf_val_t val = conf_id_get(conf, C_ACL, C_UPDATE_OWNER, acl);
+ acl_update_owner_t owner = conf_opt(&val);
+
+ /* Return if no specific requirements configured. */
+ if (types == NULL && owner == ACL_UPDATE_OWNER_NONE) {
+ return true;
+ }
+
+ acl_update_owner_match_t match = ACL_UPDATE_MATCH_SUBEQ;
+ if (owner != ACL_UPDATE_OWNER_NONE) {
+ val = conf_id_get(conf, C_ACL, C_UPDATE_OWNER_MATCH, acl);
+ match = conf_opt(&val);
+ }
+
+ conf_val_t *names = NULL;
+ conf_val_t val_names;
+ if (owner == ACL_UPDATE_OWNER_NAME) {
+ val_names = conf_id_get(conf, C_ACL, C_UPDATE_OWNER_NAME, acl);
+ if (conf_val_count(&val_names) > 0) {
+ names = &val_names;
+ }
+ }
+
+ /* Updated RRs are contained in the Authority section of the query
+ * (RFC 2136 Section 2.2)
+ */
+ uint16_t pos = query->sections[KNOT_AUTHORITY].pos;
+ uint16_t count = query->sections[KNOT_AUTHORITY].count;
+
+ for (int i = pos; i < pos + count; i++) {
+ knot_rrset_t *rr = &query->rr[i];
+ if (!match_type(rr->type, types)) {
+ return false;
+ }
+
+ switch (owner) {
+ case ACL_UPDATE_OWNER_NAME:
+ if (!match_names(rr->owner, zone_name, names, match)) {
+ return false;
+ }
+ break;
+ case ACL_UPDATE_OWNER_KEY:
+ if (!match_name(rr->owner, key_name, match)) {
+ return false;
+ }
+ break;
+ case ACL_UPDATE_OWNER_ZONE:
+ if (!match_name(rr->owner, zone_name, match)) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return true;
+}
+
+static bool check_addr_key(conf_t *conf, conf_val_t *addr_val, conf_val_t *key_val,
+ bool remote, const struct sockaddr_storage *addr,
+ const knot_tsig_key_t *tsig, bool deny)
+{
+ /* Check if the address matches the acl address list or remote addresses. */
+ if (addr_val->code != KNOT_ENOENT) {
+ if (remote) {
+ if (!conf_addr_match(addr_val, addr)) {
+ return false;
+ }
+ } else {
+ if (!conf_addr_range_match(addr_val, addr)) {
+ return false;
+ }
+ }
+ }
+
+ /* Check if the key matches the acl key list or remote key. */
+ while (key_val->code == KNOT_EOK) {
+ /* No key provided, but required. */
+ if (tsig->name == NULL) {
+ goto next_key;
+ }
+
+ /* Compare key names (both in lower-case). */
+ const knot_dname_t *key_name = conf_dname(key_val);
+ if (!knot_dname_is_equal(key_name, tsig->name)) {
+ goto next_key;
+ }
+
+ /* Compare key algorithms. */
+ conf_val_t alg_val = conf_id_get(conf, C_KEY, C_ALG, key_val);
+ if (conf_opt(&alg_val) != tsig->algorithm) {
+ goto next_key;
+ }
+
+ break;
+ next_key:
+ if (remote) {
+ assert(!(key_val->item->flags & YP_FMULTI));
+ key_val->code = KNOT_EOF;
+ break;
+ } else {
+ assert(key_val->item->flags & YP_FMULTI);
+ conf_val_next(key_val);
+ }
+ }
+ switch (key_val->code) {
+ case KNOT_EOK:
+ // Key match.
+ break;
+ case KNOT_ENOENT:
+ // Empty list without key provided or denied.
+ if (tsig->name == NULL || deny) {
+ break;
+ }
+ // FALLTHROUGH
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
+ const struct sockaddr_storage *addr, knot_tsig_key_t *tsig,
+ const knot_dname_t *zone_name, knot_pkt_t *query)
+{
+ if (acl == NULL || addr == NULL || tsig == NULL) {
+ return false;
+ }
+
+ while (acl->code == KNOT_EOK) {
+ conf_val_t rmt_val = conf_id_get(conf, C_ACL, C_RMT, acl);
+ bool remote = (rmt_val.code == KNOT_EOK);
+ conf_val_t deny_val = conf_id_get(conf, C_ACL, C_DENY, acl);
+ bool deny = conf_bool(&deny_val);
+
+ /* Check if a remote matches given address and key. */
+ conf_val_t addr_val, key_val;
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &rmt_val, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ addr_val = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ key_val = conf_id_get(conf, C_RMT, C_KEY, iter.id);
+ if (check_addr_key(conf, &addr_val, &key_val, remote, addr, tsig, deny)) {
+ break;
+ }
+ conf_mix_iter_next(&iter);
+ }
+ if (iter.id->code == KNOT_EOF) {
+ goto next_acl;
+ }
+ /* Or check if acl address/key matches given address and key. */
+ if (!remote) {
+ addr_val = conf_id_get(conf, C_ACL, C_ADDR, acl);
+ key_val = conf_id_get(conf, C_ACL, C_KEY, acl);
+ if (!check_addr_key(conf, &addr_val, &key_val, remote, addr, tsig, deny)) {
+ goto next_acl;
+ }
+ }
+
+ /* Check if the action is allowed. */
+ if (action != ACL_ACTION_QUERY) {
+ conf_val_t val = conf_id_get(conf, C_ACL, C_ACTION, acl);
+ while (val.code == KNOT_EOK) {
+ if (conf_opt(&val) != action) {
+ conf_val_next(&val);
+ continue;
+ }
+
+ break;
+ }
+ switch (val.code) {
+ case KNOT_EOK: /* Check for action match. */
+ break;
+ case KNOT_ENOENT: /* Empty action list allowed with deny only. */
+ return false;
+ default: /* No match. */
+ goto next_acl;
+ }
+ }
+
+ /* If the action is update, check for update rule match. */
+ if (action == ACL_ACTION_UPDATE &&
+ !update_match(conf, acl, tsig->name, zone_name, query)) {
+ goto next_acl;
+ }
+
+ /* Check if denied. */
+ if (deny) {
+ return false;
+ }
+
+ /* Fill the output with tsig secret if provided. */
+ if (tsig->name != NULL) {
+ conf_val_t val = conf_id_get(conf, C_KEY, C_SECRET, &key_val);
+ tsig->secret.data = (uint8_t *)conf_bin(&val, &tsig->secret.size);
+ }
+
+ return true;
+next_acl:
+ conf_val_next(acl);
+ }
+
+ return false;
+}
+
+bool rmt_allowed(conf_t *conf, conf_val_t *rmts, const struct sockaddr_storage *addr,
+ knot_tsig_key_t *tsig)
+{
+ if (!conf->cache.srv_auto_acl) {
+ return false;
+ }
+
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, rmts, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ conf_val_t val = conf_id_get(conf, C_RMT, C_AUTO_ACL, iter.id);
+ if (!conf_bool(&val)) {
+ goto next_remote;
+ }
+
+ conf_val_t key_id = conf_id_get(conf, C_RMT, C_KEY, iter.id);
+ if (key_id.code == KNOT_EOK) {
+ /* No key provided, but required. */
+ if (tsig->name == NULL) {
+ goto next_remote;
+ }
+
+ /* Compare key names (both in lower-case). */
+ const knot_dname_t *key_name = conf_dname(&key_id);
+ if (!knot_dname_is_equal(key_name, tsig->name)) {
+ goto next_remote;
+ }
+
+ /* Compare key algorithms. */
+ val = conf_id_get(conf, C_KEY, C_ALG, &key_id);
+ if (conf_opt(&val) != tsig->algorithm) {
+ goto next_remote;
+ }
+ } else if (key_id.code == KNOT_ENOENT && tsig->name != NULL) {
+ /* Key provided but no key configured. */
+ goto next_remote;
+ }
+
+ /* Check if the address matches. */
+ val = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ if (!conf_addr_match(&val, addr)) {
+ goto next_remote;
+ }
+
+ /* Fill out the output with tsig secret if provided. */
+ if (tsig->name != NULL) {
+ val = conf_id_get(conf, C_KEY, C_SECRET, &key_id);
+ tsig->secret.data = (uint8_t *)conf_bin(&val, &tsig->secret.size);
+ }
+
+ return true;
+next_remote:
+ conf_mix_iter_next(&iter);
+ }
+
+ return false;
+}
diff --git a/src/knot/updates/acl.h b/src/knot/updates/acl.h
new file mode 100644
index 0000000..8c15acf
--- /dev/null
+++ b/src/knot/updates/acl.h
@@ -0,0 +1,83 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "libknot/tsig.h"
+#include "knot/conf/conf.h"
+
+/*! \brief ACL actions. */
+typedef enum {
+ ACL_ACTION_QUERY = 0,
+ ACL_ACTION_NOTIFY = 1,
+ ACL_ACTION_TRANSFER = 2,
+ ACL_ACTION_UPDATE = 3
+} acl_action_t;
+
+/*! \brief ACL update owner matching options. */
+typedef enum {
+ ACL_UPDATE_OWNER_NONE = 0,
+ ACL_UPDATE_OWNER_KEY = 1,
+ ACL_UPDATE_OWNER_ZONE = 2,
+ ACL_UPDATE_OWNER_NAME = 3,
+} acl_update_owner_t;
+
+/*! \bref ACL update owner comparison options. */
+typedef enum {
+ ACL_UPDATE_MATCH_SUBEQ = 0,
+ ACL_UPDATE_MATCH_EQ = 1,
+ ACL_UPDATE_MATCH_SUB = 2,
+} acl_update_owner_match_t;
+
+/*!
+ * \brief Checks if the address and/or tsig key matches given ACL list.
+ *
+ * If a proper ACL rule is found and tsig.name is not empty, tsig.secret is filled.
+ *
+ * \param conf Configuration.
+ * \param acl Pointer to ACL config multivalued identifier.
+ * \param action ACL action.
+ * \param addr IP address.
+ * \param tsig TSIG parameters.
+ * \param zone_name Zone name.
+ * \param query Update query.
+ *
+ * \retval True if authenticated.
+ */
+bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action,
+ const struct sockaddr_storage *addr, knot_tsig_key_t *tsig,
+ const knot_dname_t *zone_name, knot_pkt_t *query);
+
+/*!
+ * \brief Checks if the address and/or tsig key matches a remote from the list.
+ *
+ * Global (server.automatic-acl) and per remote automatic ACL functionality
+ * must be enabled in order to decide the remote is allowed.
+ *
+ * If a proper REMOTE is found and tsig.name is not empty, tsig.secret is filled.
+ *
+ * \param conf Configuration.
+ * \param rmts Pointer to REMOTE config multivalued identifier.
+ * \param addr IP address.
+ * \param tsig TSIG parameters.
+ *
+ * \retval True if authenticated.
+ */
+bool rmt_allowed(conf_t *conf, conf_val_t *rmts, const struct sockaddr_storage *addr,
+ knot_tsig_key_t *tsig);
diff --git a/src/knot/updates/apply.c b/src/knot/updates/apply.c
new file mode 100644
index 0000000..b96432e
--- /dev/null
+++ b/src/knot/updates/apply.c
@@ -0,0 +1,379 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "knot/common/log.h"
+#include "knot/updates/apply.h"
+#include "libknot/libknot.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+
+/*! \brief Replaces rdataset of given type with a copy. */
+static int replace_rdataset_with_copy(zone_node_t *node, uint16_t type)
+{
+ int ret = binode_prepare_change(node, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Find data to copy.
+ struct rr_data *data = NULL;
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == type) {
+ data = &node->rrs[i];
+ break;
+ }
+ }
+ if (data == NULL) {
+ return KNOT_EOK;
+ }
+
+ // Create new data.
+ knot_rdataset_t *rrs = &data->rrs;
+ void *copy = malloc(rrs->size);
+ if (copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(copy, rrs->rdata, rrs->size);
+
+ // Store new data into node RRS.
+ rrs->rdata = copy;
+
+ return KNOT_EOK;
+}
+
+/*! \brief Frees RR dataset. For use when a copy was made. */
+static void clear_new_rrs(zone_node_t *node, uint16_t type)
+{
+ knot_rdataset_t *new_rrs = node_rdataset(node, type);
+ if (new_rrs) {
+ knot_rdataset_clear(new_rrs, NULL);
+ }
+}
+
+/*! \brief Logs redundant rrset operation. */
+static void can_log_rrset(const knot_rrset_t *rrset, int pos, apply_ctx_t *ctx, bool remove)
+{
+ if (!(ctx->flags & APPLY_STRICT)) {
+ return;
+ }
+
+ char type[16];
+ char data[1024];
+ const char *msg = remove ? "cannot remove nonexisting RR" :
+ "cannot add existing RR";
+
+ char *owner = knot_dname_to_str_alloc(rrset->owner);
+ if (owner != NULL && knot_rrtype_to_string(rrset->type, type, sizeof(type)) > 0 &&
+ knot_rrset_txt_dump_data(rrset, pos, data, sizeof(data), &KNOT_DUMP_STYLE_DEFAULT) > 0) {
+ log_zone_debug(ctx->contents->apex->owner,
+ "node %s, type %s, data '%s', %s", owner, type, data, msg);
+ }
+ free(owner);
+}
+
+/*! \brief Returns true if given RR is present in node and can be removed. */
+static bool can_remove(const zone_node_t *node, const knot_rrset_t *rrset, apply_ctx_t *ctx)
+{
+ if (node == NULL) {
+ // Node does not exist, cannot remove anything.
+ can_log_rrset(rrset, 0, ctx, true);
+ return false;
+ }
+
+ const knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type);
+ if (node_rrs == NULL) {
+ // Node does not have this type at all.
+ can_log_rrset(rrset, 0, ctx, true);
+ return false;
+ }
+
+ knot_rdata_t *rr_cmp = rrset->rrs.rdata;
+ for (uint16_t i = 0; i < rrset->rrs.count; ++i) {
+ if (!knot_rdataset_member(node_rrs, rr_cmp)) {
+ // At least one RR doesnt' match.
+ can_log_rrset(rrset, i, ctx, true);
+ return false;
+ }
+ rr_cmp = knot_rdataset_next(rr_cmp);
+ }
+
+ return true;
+}
+
+/*! \brief Returns true if given RR is not present in node and can be added. */
+static bool can_add(const zone_node_t *node, const knot_rrset_t *rrset, apply_ctx_t *ctx)
+{
+ if (node == NULL) {
+ // Node does not exist, can add anything.
+ return true;
+ }
+ const knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type);
+ if (node_rrs == NULL) {
+ // Node does not have this type at all.
+ return true;
+ }
+
+ knot_rdata_t *rr_cmp = rrset->rrs.rdata;
+ for (uint16_t i = 0; i < rrset->rrs.count; ++i) {
+ if (knot_rdataset_member(node_rrs, rr_cmp)) {
+ // No RR must match.
+ can_log_rrset(rrset, i, ctx, false);
+ return false;
+ }
+ rr_cmp = knot_rdataset_next(rr_cmp);
+ }
+
+ return true;
+}
+
+int apply_init_ctx(apply_ctx_t *ctx, zone_contents_t *contents, uint32_t flags)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ ctx->contents = contents;
+
+ ctx->node_ptrs = zone_tree_create(true);
+ if (ctx->node_ptrs == NULL) {
+ return KNOT_ENOMEM;
+ }
+ ctx->node_ptrs->flags = contents->nodes->flags;
+
+ ctx->nsec3_ptrs = zone_tree_create(true);
+ if (ctx->nsec3_ptrs == NULL) {
+ zone_tree_free(&ctx->node_ptrs);
+ return KNOT_ENOMEM;
+ }
+ ctx->nsec3_ptrs->flags = contents->nodes->flags;
+
+ ctx->adjust_ptrs = zone_tree_create(true);
+ if (ctx->adjust_ptrs == NULL) {
+ zone_tree_free(&ctx->nsec3_ptrs);
+ zone_tree_free(&ctx->node_ptrs);
+ return KNOT_ENOMEM;
+ }
+ ctx->adjust_ptrs->flags = contents->nodes->flags;
+
+ ctx->flags = flags;
+
+ return KNOT_EOK;
+}
+
+static zone_node_t *add_node_cb(const knot_dname_t *owner, void *ctx)
+{
+ zone_tree_t *tree = ctx;
+ zone_node_t *node = zone_tree_get(tree, owner);
+ if (node == NULL) {
+ node = node_new_for_tree(owner, tree, NULL);
+ } else {
+ node->flags &= ~NODE_FLAGS_DELETED;
+ }
+ return node;
+}
+
+int apply_add_rr(apply_ctx_t *ctx, const knot_rrset_t *rr)
+{
+ zone_contents_t *contents = ctx->contents;
+ bool nsec3rel = knot_rrset_is_nsec3rel(rr);
+ zone_tree_t *ptrs = nsec3rel ? ctx->nsec3_ptrs : ctx->node_ptrs;
+ zone_tree_t *tree = zone_contents_tree_for_rr(contents, rr);
+ if (tree == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Get or create node with this owner, search changes first
+ zone_node_t *node = NULL;
+ int ret = zone_tree_add_node(tree, contents->apex, rr->owner, add_node_cb, ptrs, &node);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (!can_add(node, rr, ctx)) {
+ return (ctx->flags & APPLY_STRICT) ? KNOT_EISRECORD : KNOT_EOK;
+ }
+
+ ret = zone_tree_insert_with_parents(ptrs, node, nsec3rel);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (binode_rdata_shared(node, rr->type)) {
+ // Modifying existing RRSet.
+ ret = replace_rdataset_with_copy(node, rr->type);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Insert new RR to RRSet, data will be copied.
+ ret = node_add_rrset(node, rr, NULL);
+ if (ret == KNOT_ETTL) {
+ // this shall not happen except applying journal created before this bugfix
+ return KNOT_EOK;
+ }
+ return ret;
+}
+
+int apply_remove_rr(apply_ctx_t *ctx, const knot_rrset_t *rr)
+{
+ zone_contents_t *contents = ctx->contents;
+ bool nsec3rel = knot_rrset_is_nsec3rel(rr);
+ zone_tree_t *ptrs = nsec3rel ? ctx->nsec3_ptrs : ctx->node_ptrs;
+ zone_tree_t *tree = zone_contents_tree_for_rr(contents, rr);
+ if (tree == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Find node for this owner
+ zone_node_t *node = zone_contents_find_node_for_rr(contents, rr);
+ if (!can_remove(node, rr, ctx)) {
+ return (ctx->flags & APPLY_STRICT) ? KNOT_ENORECORD : KNOT_EOK;
+ }
+
+ int ret = zone_tree_insert_with_parents(ptrs, node, nsec3rel);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (binode_rdata_shared(node, rr->type)) {
+ ret = replace_rdataset_with_copy(node, rr->type);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ ret = node_remove_rrset(node, rr, NULL);
+ if (ret != KNOT_EOK) {
+ clear_new_rrs(node, rr->type);
+ return ret;
+ }
+
+ if (node->rrset_count == 0 && node->children == 0 && node != contents->apex) {
+ zone_tree_del_node(tree, node, false);
+ }
+
+ return KNOT_EOK;
+}
+
+int apply_replace_soa(apply_ctx_t *ctx, const knot_rrset_t *rr)
+{
+ zone_contents_t *contents = ctx->contents;
+
+ if (!knot_dname_is_equal(rr->owner, contents->apex->owner)) {
+ return KNOT_EDENIED;
+ }
+
+ knot_rrset_t old_soa = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
+
+ int ret = apply_remove_rr(ctx, &old_soa);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for SOA with proper serial but different rdata.
+ if (node_rrtype_exists(contents->apex, KNOT_RRTYPE_SOA)) {
+ return KNOT_ESOAINVAL;
+ }
+
+ return apply_add_rr(ctx, rr);
+}
+
+void apply_cleanup(apply_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->flags & APPLY_UNIFY_FULL) {
+ zone_trees_unify_binodes(ctx->contents->nodes, ctx->contents->nsec3_nodes, true);
+ } else {
+ zone_trees_unify_binodes(ctx->adjust_ptrs, NULL, false); // beware there might be duplicities in ctx->adjust_ptrs and ctx->node_ptrs, so we don't free here
+ zone_trees_unify_binodes(ctx->node_ptrs, ctx->nsec3_ptrs, true);
+ }
+
+ zone_tree_free(&ctx->node_ptrs);
+ zone_tree_free(&ctx->nsec3_ptrs);
+ zone_tree_free(&ctx->adjust_ptrs);
+
+ if (ctx->cow_mutex != NULL) {
+ knot_sem_post(ctx->cow_mutex);
+ }
+}
+
+void apply_rollback(apply_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->node_ptrs != NULL) {
+ ctx->node_ptrs->flags ^= ZONE_TREE_BINO_SECOND;
+ }
+ if (ctx->nsec3_ptrs != NULL) {
+ ctx->nsec3_ptrs->flags ^= ZONE_TREE_BINO_SECOND;
+ }
+ zone_trees_unify_binodes(ctx->node_ptrs, ctx->nsec3_ptrs, true);
+
+ zone_tree_free(&ctx->node_ptrs);
+ zone_tree_free(&ctx->nsec3_ptrs);
+ zone_tree_free(&ctx->adjust_ptrs);
+
+ trie_cow_rollback(ctx->contents->nodes->cow, NULL, NULL);
+ ctx->contents->nodes->cow = NULL;
+ if (ctx->contents->nsec3_nodes != NULL && ctx->contents->nsec3_nodes->cow != NULL) {
+ trie_cow_rollback(ctx->contents->nsec3_nodes->cow, NULL, NULL);
+ ctx->contents->nsec3_nodes->cow = NULL;
+ } else if (ctx->contents->nsec3_nodes != NULL) {
+ zone_tree_free(&ctx->contents->nsec3_nodes);
+ ctx->contents->nsec3_nodes = NULL;
+ }
+
+ free(ctx->contents->nodes);
+ free(ctx->contents->nsec3_nodes);
+
+ dnssec_nsec3_params_free(&ctx->contents->nsec3_params);
+
+ free(ctx->contents);
+
+ if (ctx->cow_mutex != NULL) {
+ knot_sem_post(ctx->cow_mutex);
+ }
+}
+
+void update_free_zone(zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return;
+ }
+
+ trie_cow_commit(contents->nodes->cow, NULL, NULL);
+ contents->nodes->cow = NULL;
+ if (contents->nsec3_nodes != NULL && contents->nsec3_nodes->cow != NULL) {
+ trie_cow_commit(contents->nsec3_nodes->cow, NULL, NULL);
+ contents->nsec3_nodes->cow = NULL;
+ }
+
+ free(contents->nodes);
+ free(contents->nsec3_nodes);
+
+ dnssec_nsec3_params_free(&contents->nsec3_params);
+
+ free(contents);
+}
diff --git a/src/knot/updates/apply.h b/src/knot/updates/apply.h
new file mode 100644
index 0000000..2d3588b
--- /dev/null
+++ b/src/knot/updates/apply.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "contrib/semaphore.h"
+#include "knot/zone/contents.h"
+#include "knot/updates/changesets.h"
+#include "contrib/ucw/lists.h"
+
+enum {
+ APPLY_STRICT = 1 << 0, /*!< Apply strictly, don't ignore removing non-existent RRs. */
+ APPLY_UNIFY_FULL = 1 << 1, /*!< When cleaning up successful update, perform full trees nodes unify. */
+};
+
+struct apply_ctx {
+ zone_contents_t *contents;
+ zone_tree_t *node_ptrs; /*!< Just pointers to the affected nodes in contents. */
+ zone_tree_t *nsec3_ptrs; /*!< The same for NSEC3 nodes. */
+ zone_tree_t *adjust_ptrs; /*!< Pointers to nodes affected by adjusting. */
+ uint32_t flags;
+ knot_sem_t *cow_mutex;
+};
+
+typedef struct apply_ctx apply_ctx_t;
+
+/*!
+ * \brief Initialize a new context structure.
+ *
+ * \param ctx Context to be initialized.
+ * \param contents Zone contents to apply changes onto.
+ * \param flags Flags to control the application process.
+ *
+ * \return KNOT_E*
+ */
+int apply_init_ctx(apply_ctx_t *ctx, zone_contents_t *contents, uint32_t flags);
+
+/*!
+ * \brief Adds a single RR into zone contents.
+ *
+ * \param ctx Apply context.
+ * \param rr RRSet to add.
+ *
+ * \return KNOT_E*
+ */
+int apply_add_rr(apply_ctx_t *ctx, const knot_rrset_t *rr);
+
+/*!
+ * \brief Removes single RR from zone contents.
+ *
+ * \param ctx Apply context.
+ * \param rr RRSet to remove.
+ *
+ * \return KNOT_E*
+ */
+int apply_remove_rr(apply_ctx_t *ctx, const knot_rrset_t *rr);
+
+/*!
+ * \brief Remove SOA and add a new SOA.
+ *
+ * \param ctx Apply context.
+ * \param rr New SOA to be added.
+ *
+ * \return KNOT_E*
+ */
+int apply_replace_soa(apply_ctx_t *ctx, const knot_rrset_t *rr);
+
+/*!
+ * \brief Cleanups successful zone update.
+ *
+ * \param ctx Context used to create the update.
+ */
+void apply_cleanup(apply_ctx_t *ctx);
+
+/*!
+ * \brief Rollbacks failed zone update.
+ *
+ * \param ctx Context used to create the update.
+ */
+void apply_rollback(apply_ctx_t *ctx);
+
+/*!
+ * \brief Shallow frees zone contents - either shallow copy after failed update
+ * or original zone contents after successful update.
+ *
+ * \param contents Contents to free.
+ */
+void update_free_zone(zone_contents_t *contents);
diff --git a/src/knot/updates/changesets.c b/src/knot/updates/changesets.c
new file mode 100644
index 0000000..1d1a0d3
--- /dev/null
+++ b/src/knot/updates/changesets.c
@@ -0,0 +1,628 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "knot/updates/changesets.h"
+#include "knot/updates/apply.h"
+#include "knot/zone/zone-dump.h"
+#include "contrib/color.h"
+#include "contrib/time.h"
+#include "libknot/libknot.h"
+
+static int handle_soa(knot_rrset_t **soa, const knot_rrset_t *rrset)
+{
+ assert(soa);
+ assert(rrset);
+
+ if (*soa != NULL) {
+ knot_rrset_free(*soa, NULL);
+ }
+
+ *soa = knot_rrset_copy(rrset, NULL);
+ if (*soa == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Adds RRSet to given zone. */
+static int add_rr_to_contents(zone_contents_t *z, const knot_rrset_t *rrset)
+{
+ _unused_ zone_node_t *n = NULL;
+ int ret = zone_contents_add_rr(z, rrset, &n);
+
+ // We don't care of TTLs.
+ return ret == KNOT_ETTL ? KNOT_EOK : ret;
+}
+
+/*! \brief Inits changeset iterator with given tries. */
+static int changeset_iter_init(changeset_iter_t *ch_it, size_t tries, ...)
+{
+ memset(ch_it, 0, sizeof(*ch_it));
+
+ va_list args;
+ va_start(args, tries);
+
+ assert(tries <= sizeof(ch_it->trees) / sizeof(*ch_it->trees));
+ for (size_t i = 0; i < tries; ++i) {
+ zone_tree_t *t = va_arg(args, zone_tree_t *);
+ if (t == NULL) {
+ continue;
+ }
+
+ ch_it->trees[ch_it->n_trees++] = t;
+ }
+
+ va_end(args);
+
+ assert(ch_it->n_trees);
+ return zone_tree_it_begin(ch_it->trees[0], &ch_it->it);
+}
+
+// removes from counterpart what is in rr.
+// fixed_rr is an output parameter, holding a copy of rr without what has been removed from counterpart
+static void check_redundancy(zone_contents_t *counterpart, const knot_rrset_t *rr, knot_rrset_t **fixed_rr)
+{
+ if (fixed_rr != NULL) {
+ *fixed_rr = knot_rrset_copy(rr, NULL);
+ }
+
+ zone_node_t *node = zone_contents_find_node_for_rr(counterpart, rr);
+ if (node == NULL) {
+ return;
+ }
+
+ if (!node_rrtype_exists(node, rr->type)) {
+ return;
+ }
+
+ uint32_t rrs_ttl = node_rrset(node, rr->type).ttl;
+
+ if (fixed_rr != NULL && *fixed_rr != NULL &&
+ ((*fixed_rr)->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG)) {
+ int ret = knot_rdataset_subtract(&(*fixed_rr)->rrs, node_rdataset(node, rr->type), NULL);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+ }
+
+ // TTL of RRSIGs is better determined by original_ttl field, which is compared as part of rdata anyway
+ if (rr->ttl == rrs_ttl || rr->type == KNOT_RRTYPE_RRSIG) {
+ int ret = node_remove_rrset(node, rr, NULL);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+ }
+
+ if (node->rrset_count == 0 && node->children == 0 && node != counterpart->apex) {
+ zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ?
+ counterpart->nsec3_nodes : counterpart->nodes;
+ zone_tree_del_node(t, node, true);
+ }
+
+ return;
+}
+
+int changeset_init(changeset_t *ch, const knot_dname_t *apex)
+{
+ memset(ch, 0, sizeof(changeset_t));
+
+ // Init local changes
+ ch->add = zone_contents_new(apex, false);
+ if (ch->add == NULL) {
+ return KNOT_ENOMEM;
+ }
+ ch->remove = zone_contents_new(apex, false);
+ if (ch->remove == NULL) {
+ zone_contents_free(ch->add);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+changeset_t *changeset_new(const knot_dname_t *apex)
+{
+ changeset_t *ret = malloc(sizeof(changeset_t));
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ if (changeset_init(ret, apex) == KNOT_EOK) {
+ return ret;
+ } else {
+ free(ret);
+ return NULL;
+ }
+}
+
+bool changeset_empty(const changeset_t *ch)
+{
+ if (ch == NULL) {
+ return true;
+ }
+
+ if (zone_contents_is_empty(ch->remove) &&
+ zone_contents_is_empty(ch->add)) {
+ if (ch->soa_to == NULL) {
+ return true;
+ }
+ if (ch->soa_from != NULL && ch->soa_to != NULL &&
+ knot_rrset_equal(ch->soa_from, ch->soa_to, false)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+size_t changeset_size(const changeset_t *ch)
+{
+ if (ch == NULL) {
+ return 0;
+ }
+
+ changeset_iter_t itt;
+ changeset_iter_all(&itt, ch);
+
+ size_t size = 0;
+ knot_rrset_t rr = changeset_iter_next(&itt);
+ while(!knot_rrset_empty(&rr)) {
+ ++size;
+ rr = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ if (!knot_rrset_empty(ch->soa_from)) {
+ size += 1;
+ }
+ if (!knot_rrset_empty(ch->soa_to)) {
+ size += 1;
+ }
+
+ return size;
+}
+
+int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
+{
+ if (!ch || !rrset) {
+ return KNOT_EINVAL;
+ }
+
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* Do not add SOAs into actual contents. */
+ return handle_soa(&ch->soa_to, rrset);
+ }
+
+ knot_rrset_t *rrset_cancelout = NULL;
+
+ /* Check if there's any removal and remove that, then add this
+ * addition anyway. Required to change TTLs. */
+ if (flags & CHANGESET_CHECK) {
+ /* If we delete the rrset, we need to hold a copy to add it later */
+ rrset = knot_rrset_copy(rrset, NULL);
+ if (rrset == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ check_redundancy(ch->remove, rrset, &rrset_cancelout);
+ }
+
+ const knot_rrset_t *to_add = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
+ int ret = knot_rrset_empty(to_add) ? KNOT_EOK : add_rr_to_contents(ch->add, to_add);
+
+ if (flags & CHANGESET_CHECK) {
+ knot_rrset_free((knot_rrset_t *)rrset, NULL);
+ }
+ knot_rrset_free(rrset_cancelout, NULL);
+
+ return ret;
+}
+
+int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags)
+{
+ if (!ch || !rrset) {
+ return KNOT_EINVAL;
+ }
+
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* Do not add SOAs into actual contents. */
+ return handle_soa(&ch->soa_from, rrset);
+ }
+
+ knot_rrset_t *rrset_cancelout = NULL;
+
+ /* Check if there's any addition and remove that, then add this
+ * removal anyway. */
+ if (flags & CHANGESET_CHECK) {
+ /* If we delete the rrset, we need to hold a copy to add it later */
+ rrset = knot_rrset_copy(rrset, NULL);
+ if (rrset == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ check_redundancy(ch->add, rrset, &rrset_cancelout);
+ }
+
+ const knot_rrset_t *to_remove = (rrset_cancelout == NULL ? rrset : rrset_cancelout);
+ int ret = (knot_rrset_empty(to_remove) || ch->remove == NULL) ? KNOT_EOK : add_rr_to_contents(ch->remove, to_remove);
+
+ if (flags & CHANGESET_CHECK) {
+ knot_rrset_free((knot_rrset_t *)rrset, NULL);
+ }
+ knot_rrset_free(rrset_cancelout, NULL);
+
+ return ret;
+}
+
+int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset)
+{
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* Do not add SOAs into actual contents. */
+ if (ch->soa_to != NULL) {
+ knot_rrset_free(ch->soa_to, NULL);
+ ch->soa_to = NULL;
+ }
+ return KNOT_EOK;
+ }
+
+ zone_node_t *n = NULL;
+ return zone_contents_remove_rr(ch->add, rrset, &n);
+}
+
+int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset)
+{
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* Do not add SOAs into actual contents. */
+ if (ch->soa_from != NULL) {
+ knot_rrset_free(ch->soa_from, NULL);
+ ch->soa_from = NULL;
+ }
+ return KNOT_EOK;
+ }
+
+ zone_node_t *n = NULL;
+ return zone_contents_remove_rr(ch->remove, rrset, &n);
+}
+
+int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags)
+{
+ changeset_iter_t itt;
+ changeset_iter_rem(&itt, ch2);
+
+ knot_rrset_t rrset = changeset_iter_next(&itt);
+ while (!knot_rrset_empty(&rrset)) {
+ int ret = changeset_add_removal(ch1, &rrset, CHANGESET_CHECK | flags);
+ if (ret != KNOT_EOK) {
+ changeset_iter_clear(&itt);
+ return ret;
+ }
+ rrset = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ changeset_iter_add(&itt, ch2);
+
+ rrset = changeset_iter_next(&itt);
+ while (!knot_rrset_empty(&rrset)) {
+ int ret = changeset_add_addition(ch1, &rrset, CHANGESET_CHECK | flags);
+ if (ret != KNOT_EOK) {
+ changeset_iter_clear(&itt);
+ return ret;
+ }
+ rrset = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ // Use soa_to and serial from the second changeset
+ // soa_to from the first changeset is redundant, delete it
+ if (ch2->soa_to == NULL && ch2->soa_from == NULL) {
+ // but not if ch2 has no soa change
+ return KNOT_EOK;
+ }
+ knot_rrset_t *soa_copy = knot_rrset_copy(ch2->soa_to, NULL);
+ if (soa_copy == NULL && ch2->soa_to) {
+ return KNOT_ENOMEM;
+ }
+ knot_rrset_free(ch1->soa_to, NULL);
+ ch1->soa_to = soa_copy;
+
+ return KNOT_EOK;
+}
+
+uint32_t changeset_from(const changeset_t *ch)
+{
+ return ch->soa_from == NULL ? 0 : knot_soa_serial(ch->soa_from->rrs.rdata);
+}
+
+uint32_t changeset_to(const changeset_t *ch)
+{
+ return ch->soa_to == NULL ? 0 : knot_soa_serial(ch->soa_to->rrs.rdata);
+}
+
+bool changeset_differs_just_serial(const changeset_t *ch, bool ignore_zonemd)
+{
+ if (ch == NULL || ch->soa_from == NULL || ch->soa_to == NULL) {
+ return false;
+ }
+
+ knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL);
+ knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata));
+
+ bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, true);
+ knot_rrset_free(soa_to_cpy, NULL);
+
+ changeset_iter_t itt;
+ changeset_iter_all(&itt, ch);
+
+ knot_rrset_t rrset = changeset_iter_next(&itt);
+ while (!knot_rrset_empty(&rrset) && ret) {
+ switch (rrset.type) {
+ case KNOT_RRTYPE_ZONEMD:
+ ret = ignore_zonemd;
+ break;
+ case KNOT_RRTYPE_RRSIG:
+ ; uint16_t covered = knot_rrsig_type_covered(rrset.rrs.rdata);
+ if (covered == KNOT_RRTYPE_SOA ||
+ (covered == KNOT_RRTYPE_ZONEMD && ignore_zonemd)) {
+ break;
+ }
+ // FALLTHROUGH
+ default:
+ ret = false;
+ break;
+ }
+ rrset = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ return ret;
+}
+
+void changesets_clear(list_t *chgs)
+{
+ if (chgs) {
+ changeset_t *chg, *nxt;
+ WALK_LIST_DELSAFE(chg, nxt, *chgs) {
+ changeset_clear(chg);
+ rem_node(&chg->n);
+ }
+ init_list(chgs);
+ }
+}
+
+void changesets_free(list_t *chgs)
+{
+ if (chgs) {
+ changeset_t *chg, *nxt;
+ WALK_LIST_DELSAFE(chg, nxt, *chgs) {
+ rem_node(&chg->n);
+ changeset_free(chg);
+ }
+ init_list(chgs);
+ }
+}
+
+void changeset_clear(changeset_t *ch)
+{
+ if (ch == NULL) {
+ return;
+ }
+
+ // Delete RRSets in lists, in case there are any left
+ zone_contents_deep_free(ch->add);
+ zone_contents_deep_free(ch->remove);
+ ch->add = NULL;
+ ch->remove = NULL;
+
+ knot_rrset_free(ch->soa_from, NULL);
+ knot_rrset_free(ch->soa_to, NULL);
+ ch->soa_from = NULL;
+ ch->soa_to = NULL;
+
+ // Delete binary data
+ free(ch->data);
+}
+
+changeset_t *changeset_clone(const changeset_t *ch)
+{
+ if (ch == NULL) {
+ return NULL;
+ }
+
+ changeset_t *res = changeset_new(ch->add->apex->owner);
+ if (res == NULL) {
+ return NULL;
+ }
+
+ res->soa_from = knot_rrset_copy(ch->soa_from, NULL);
+ res->soa_to = knot_rrset_copy(ch->soa_to, NULL);
+
+ int ret = KNOT_EOK;
+ changeset_iter_t itt;
+
+ changeset_iter_rem(&itt, ch);
+ knot_rrset_t rr = changeset_iter_next(&itt);
+ while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
+ ret = changeset_add_removal(res, &rr, 0);
+ rr = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ changeset_iter_add(&itt, ch);
+ rr = changeset_iter_next(&itt);
+ while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) {
+ ret = changeset_add_addition(res, &rr, 0);
+ rr = changeset_iter_next(&itt);
+ }
+ changeset_iter_clear(&itt);
+
+ if ((ch->soa_from != NULL && res->soa_from == NULL) ||
+ (ch->soa_to != NULL && res->soa_to == NULL) ||
+ ret != KNOT_EOK) {
+ changeset_free(res);
+ return NULL;
+ }
+
+ return res;
+}
+
+void changeset_free(changeset_t *ch)
+{
+ changeset_clear(ch);
+ free(ch);
+}
+
+int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch)
+{
+ return changeset_iter_init(itt, 2, ch->add->nodes, ch->add->nsec3_nodes);
+}
+
+int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch)
+{
+ return changeset_iter_init(itt, 2, ch->remove->nodes, ch->remove->nsec3_nodes);
+}
+
+int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch)
+{
+ return changeset_iter_init(itt, 4, ch->add->nodes, ch->add->nsec3_nodes,
+ ch->remove->nodes, ch->remove->nsec3_nodes);
+}
+
+knot_rrset_t changeset_iter_next(changeset_iter_t *it)
+{
+ assert(it);
+
+ knot_rrset_t rr;
+ while (it->node == NULL || it->node_pos >= it->node->rrset_count) {
+ if (it->node != NULL) {
+ zone_tree_it_next(&it->it);
+ }
+ while (zone_tree_it_finished(&it->it)) {
+ zone_tree_it_free(&it->it);
+ if (--it->n_trees > 0) {
+ for (size_t i = 0; i < it->n_trees; i++) {
+ it->trees[i] = it->trees[i + 1];
+ }
+ (void)zone_tree_it_begin(it->trees[0], &it->it);
+ } else {
+ knot_rrset_init_empty(&rr);
+ return rr;
+ }
+ }
+ it->node = zone_tree_it_val(&it->it);
+ it->node_pos = 0;
+ }
+ rr = node_rrset_at(it->node, it->node_pos++);
+ assert(!knot_rrset_empty(&rr));
+ return rr;
+}
+
+void changeset_iter_clear(changeset_iter_t *it)
+{
+ if (it) {
+ zone_tree_it_free(&it->it);
+ it->node = NULL;
+ it->node_pos = 0;
+ }
+}
+
+int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx)
+{
+ changeset_iter_t it;
+ int ret = changeset_iter_rem(&it, changeset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_rrset_t rrset = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&rrset)) {
+ ret = callback(&rrset, false, ctx);
+ if (ret != KNOT_EOK) {
+ changeset_iter_clear(&it);
+ return ret;
+ }
+ rrset = changeset_iter_next(&it);
+ }
+ changeset_iter_clear(&it);
+
+ if (changeset->soa_from != NULL) {
+ ret = callback(changeset->soa_from, false, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ ret = changeset_iter_add(&it, changeset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ rrset = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&rrset)) {
+ ret = callback(&rrset, true, ctx);
+ if (ret != KNOT_EOK) {
+ changeset_iter_clear(&it);
+ return ret;
+ }
+ rrset = changeset_iter_next(&it);
+ }
+ changeset_iter_clear(&it);
+
+ if (changeset->soa_to != NULL) {
+ ret = callback(changeset->soa_to, true, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+void changeset_print(const changeset_t *changeset, FILE *outfile, bool color)
+{
+ size_t buflen = 1024;
+ char *buff = malloc(buflen);
+
+ knot_dump_style_t style = KNOT_DUMP_STYLE_DEFAULT;
+ style.now = knot_time();
+
+ style.color = COL_RED(color);
+ if (changeset->soa_from != NULL || !zone_contents_is_empty(changeset->remove)) {
+ fprintf(outfile, "%s;; Removed%s\n", style.color, COL_RST(color));
+ }
+ if (changeset->soa_from != NULL && buff != NULL) {
+ (void)knot_rrset_txt_dump(changeset->soa_from, &buff, &buflen, &style);
+ fprintf(outfile, "%s%s%s", style.color, buff, COL_RST(color));
+ }
+ (void)zone_dump_text(changeset->remove, outfile, false, style.color);
+
+ style.color = COL_GRN(color);
+ if (changeset->soa_to != NULL || !zone_contents_is_empty(changeset->add)) {
+ fprintf(outfile, "%s;; Added%s\n", style.color, COL_RST(color));
+ }
+ if (changeset->soa_to != NULL && buff != NULL) {
+ (void)knot_rrset_txt_dump(changeset->soa_to, &buff, &buflen, &style);
+ fprintf(outfile, "%s%s%s", style.color, buff, COL_RST(color));
+ }
+ (void)zone_dump_text(changeset->add, outfile, false, style.color);
+
+ free(buff);
+}
diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h
new file mode 100644
index 0000000..1234cb9
--- /dev/null
+++ b/src/knot/updates/changesets.h
@@ -0,0 +1,290 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include "libknot/rrset.h"
+#include "knot/zone/contents.h"
+#include "contrib/ucw/lists.h"
+
+/*! \brief Changeset addition/removal flags */
+typedef enum {
+ CHANGESET_NONE = 0,
+ CHANGESET_CHECK = 1 << 0, /*! Perform redundancy check on additions/removals */
+} changeset_flag_t;
+
+/*! \brief One zone change, from 'soa_from' to 'soa_to'. */
+typedef struct {
+ node_t n; /*!< List node. */
+ knot_rrset_t *soa_from; /*!< Start SOA. */
+ knot_rrset_t *soa_to; /*!< Destination SOA. */
+ zone_contents_t *add; /*!< Change additions. */
+ zone_contents_t *remove; /*!< Change removals. */
+ size_t size; /*!< Size of serialized changeset. \todo Remove after old_journal removal! */
+ uint8_t *data; /*!< Serialized changeset. */
+} changeset_t;
+
+/*! \brief Changeset iteration structure. */
+typedef struct {
+ list_t iters; /*!< List of pending zone iterators. */
+ zone_tree_t *trees[4]; /*!< Pointers to zone trees to iterate over. */
+ size_t n_trees; /*!< Their count. */
+ zone_tree_it_t it; /*!< Zone tree iterator. */
+ const zone_node_t *node; /*!< Current zone node. */
+ uint16_t node_pos; /*!< Position in node. */
+} changeset_iter_t;
+
+/*!
+ * \brief Inits changeset structure.
+ *
+ * \param ch Changeset to init.
+ * \param apex Zone apex DNAME.
+ *
+ * \return KNOT_E*
+ */
+int changeset_init(changeset_t *ch, const knot_dname_t *apex);
+
+/*!
+ * \brief Creates new changeset structure and inits it.
+ *
+ * \param apex Zone apex DNAME.
+ *
+ * \return Changeset structure on success, NULL on errors.
+ */
+changeset_t *changeset_new(const knot_dname_t *apex);
+
+/*!
+ * \brief Checks whether changeset is empty, i.e. no change will happen after its application.
+ *
+ * \param ch Changeset to be checked.
+ *
+ * \retval true if changeset is empty.
+ * \retval false if changeset is not empty.
+ */
+bool changeset_empty(const changeset_t *ch);
+
+/*!
+ * \brief Get number of changes (additions and removals) in the changeset.
+ *
+ * \param ch Changeset to be checked.
+ *
+ * \return Number of changes in the changeset.
+ */
+size_t changeset_size(const changeset_t *ch);
+
+/*!
+ * \brief Add RRSet to 'add' part of changeset.
+ *
+ * \param ch Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ * \param flags Changeset flags.
+ *
+ * \return KNOT_E*
+ */
+int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags);
+
+/*!
+ * \brief Add RRSet to 'remove' part of changeset.
+ *
+ * \param ch Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ * \param flags Changeset flags.
+ *
+ * \return KNOT_E*
+ */
+int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags);
+
+
+/*!
+ * \brief Remove an RRSet from the 'add' part of changeset.
+ *
+ * \param ch Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ *
+ * \return KNOT_E*
+ */
+int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Remove an RRSet from the 'remove' part of changeset.
+ *
+ * \param ch Changeset to add RRSet into.
+ * \param rrset RRSet to be added.
+ *
+ * \return KNOT_E*
+ */
+int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Merges two changesets together.
+ *
+ * \param ch1 Merge into this changeset.
+ * \param ch2 Merge this changeset.
+ * \param flags Flags how to handle redundancies.
+ *
+ * \return KNOT_E*
+ */
+int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags);
+
+/*!
+ * \brief Get serial "from" of the changeset.
+ *
+ * \param ch Changeset in question.
+ *
+ * \return Its serial "from", or 0 if none.
+ */
+uint32_t changeset_from(const changeset_t *ch);
+
+/*!
+ * \brief Get serial "to" of the changeset.
+ *
+ * \param ch Changeset in question.
+ *
+ * \return Its serial "to", or 0 if none.
+ */
+uint32_t changeset_to(const changeset_t *ch);
+
+/*!
+ * \brief Check the changes and SOA, ignoring possibly updated SOA serial and ZONEMD.
+ *
+ * \note Also tolerates changed RRSIG of SOA or ZONEMD.
+ *
+ * \param ch Changeset in question.
+ * \param ignore_zonemd If enabled, possible ZONEMD records are ignored.
+ *
+ * \retval false If the changeset changes other records than SOA, or some SOA field
+ * other than serial changed or optionally ZONEMD.
+ * \retval true Otherwise.
+ */
+bool changeset_differs_just_serial(const changeset_t *ch, bool ignore_zonemd);
+
+/*!
+ * \brief Clears changesets in list. Changesets are not free'd. Legacy.
+ *
+ * \param chgs Changeset list to clear.
+ */
+void changesets_clear(list_t *chgs);
+
+/*!
+ * \brief Free changesets in list. Legacy.
+ *
+ * \param chgs Changeset list to free.
+ */
+void changesets_free(list_t *chgs);
+
+/*!
+ * \brief Clear single changeset.
+ *
+ * \param ch Changeset to clear.
+ */
+void changeset_clear(changeset_t *ch);
+
+/*!
+ * \brief Copy changeset to newly allocated space, all rrsigs are copied.
+ *
+ * \param ch Changeset to be copied.
+ *
+ * \return a copy, or NULL if error.
+ */
+changeset_t *changeset_clone(const changeset_t *ch);
+
+/*!
+ * \brief Frees single changeset.
+ *
+ * \param ch Changeset to free.
+ */
+void changeset_free(changeset_t *ch);
+
+/*!
+ * \brief Inits changeset iteration structure with changeset additions.
+ *
+ * \param itt Iterator to init.
+ * \param ch Changeset to use.
+ *
+ * \return KNOT_E*
+ */
+int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch);
+
+/*!
+ * \brief Inits changeset iteration structure with changeset removals.
+ *
+ * \param itt Iterator to init.
+ * \param ch Changeset to use.
+ *
+ * \return KNOT_E*
+ */
+int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch);
+
+/*!
+ * \brief Inits changeset iteration structure with changeset additions and removals.
+ *
+ * \param itt Iterator to init.
+ * \param ch Changeset to use.
+ *
+ * \return KNOT_E*
+ */
+int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch);
+
+/*!
+ * \brief Gets next RRSet from changeset iterator.
+ *
+ * \param it Changeset iterator.
+ *
+ * \return Next RRSet in iterator, empty RRSet if iteration done.
+ */
+knot_rrset_t changeset_iter_next(changeset_iter_t *it);
+
+/*!
+ * \brief Free resources allocated by changeset iterator.
+ *
+ * \param it Iterator to clear.
+ */
+void changeset_iter_clear(changeset_iter_t *it);
+
+/*!
+ * \brief A pointer type for callback for changeset_walk() function.
+ *
+ * \param rrset An actual removal/addition inside the changeset.
+ * \param addition Indicates addition against removal.
+ * \param ctx A context passed to the changeset_walk() function.
+ *
+ * \retval KNOT_EOK if all ok, iteration will continue
+ * \return KNOT_E* if error, iteration will stop immediately and changeset_walk() returns this error.
+ */
+typedef int (*changeset_walk_callback)(const knot_rrset_t *rrset, bool addition, void *ctx);
+
+/*!
+ * \brief Calls a callback for each removal/addition in the changeset.
+ *
+ * \param changeset Changeset.
+ * \param callback Callback.
+ * \param ctx Arbitrary context passed to the callback.
+ *
+ * \return KNOT_E*
+ */
+int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx);
+
+/*!
+ *
+ * \brief Dumps the changeset into text file.
+ *
+ * \param changeset Changeset.
+ * \param outfile File to write into.
+ * \param color Use unix tty color metacharacters.
+ */
+void changeset_print(const changeset_t *changeset, FILE *outfile, bool color);
diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c
new file mode 100644
index 0000000..eb75317
--- /dev/null
+++ b/src/knot/updates/ddns.c
@@ -0,0 +1,701 @@
+/* Copyright (C) 2023 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 <assert.h>
+
+#include "knot/common/log.h"
+#include "knot/updates/ddns.h"
+#include "knot/updates/changesets.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/serial.h"
+#include "libknot/libknot.h"
+#include "contrib/ucw/lists.h"
+
+/*!< \brief Clears prereq RRSet list. */
+static void rrset_list_clear(list_t *l)
+{
+ node_t *n, *nxt;
+ WALK_LIST_DELSAFE(n, nxt, *l) {
+ ptrnode_t *ptr_n = (ptrnode_t *)n;
+ knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d;
+ knot_rrset_free(rrset, NULL);
+ free(n);
+ };
+}
+
+/*!< \brief Adds RR to prereq RRSet list, merges RRs into RRSets. */
+static int add_rr_to_list(list_t *l, const knot_rrset_t *rr)
+{
+ node_t *n;
+ WALK_LIST(n, *l) {
+ ptrnode_t *ptr_n = (ptrnode_t *)n;
+ knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d;
+ if (rrset->type == rr->type && knot_dname_is_equal(rrset->owner, rr->owner)) {
+ return knot_rdataset_merge(&rrset->rrs, &rr->rrs, NULL);
+ }
+ };
+
+ knot_rrset_t *rr_copy = knot_rrset_copy(rr, NULL);
+ if (rr_copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ return ptrlist_add(l, rr_copy, NULL) != NULL ? KNOT_EOK : KNOT_ENOMEM;
+}
+
+/*!< \brief Checks whether RRSet exists in the zone. */
+static int check_rrset_exists(zone_update_t *update, const knot_rrset_t *rrset,
+ uint16_t *rcode)
+{
+ assert(rrset->type != KNOT_RRTYPE_ANY);
+
+ const zone_node_t *node = zone_update_get_node(update, rrset->owner);
+ if (node == NULL || !node_rrtype_exists(node, rrset->type)) {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ } else {
+ knot_rrset_t found = node_rrset(node, rrset->type);
+ assert(!knot_rrset_empty(&found));
+ if (knot_rrset_equal(&found, rrset, false)) {
+ return KNOT_EOK;
+ } else {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ }
+ }
+}
+
+/*!< \brief Checks whether RRSets in the list exist in the zone. */
+static int check_stored_rrsets(list_t *l, zone_update_t *update,
+ uint16_t *rcode)
+{
+ node_t *n;
+ WALK_LIST(n, *l) {
+ ptrnode_t *ptr_n = (ptrnode_t *)n;
+ knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d;
+ int ret = check_rrset_exists(update, rrset, rcode);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ };
+
+ return KNOT_EOK;
+}
+
+/*!< \brief Checks whether node of given owner, with given type exists. */
+static bool check_type(zone_update_t *update, const knot_rrset_t *rrset)
+{
+ assert(rrset->type != KNOT_RRTYPE_ANY);
+ const zone_node_t *node = zone_update_get_node(update, rrset->owner);
+ if (node == NULL || !node_rrtype_exists(node, rrset->type)) {
+ return false;
+ }
+
+ return true;
+}
+
+/*!< \brief Checks whether RR type exists in the zone. */
+static int check_type_exist(zone_update_t *update,
+ const knot_rrset_t *rrset, uint16_t *rcode)
+{
+ assert(rrset->rclass == KNOT_CLASS_ANY);
+ if (check_type(update, rrset)) {
+ return KNOT_EOK;
+ } else {
+ *rcode = KNOT_RCODE_NXRRSET;
+ return KNOT_EPREREQ;
+ }
+}
+
+/*!< \brief Checks whether RR type is not in the zone. */
+static int check_type_not_exist(zone_update_t *update,
+ const knot_rrset_t *rrset, uint16_t *rcode)
+{
+ assert(rrset->rclass == KNOT_CLASS_NONE);
+ if (check_type(update, rrset)) {
+ *rcode = KNOT_RCODE_YXRRSET;
+ return KNOT_EPREREQ;
+ } else {
+ return KNOT_EOK;
+ }
+}
+
+/*!< \brief Checks whether DNAME is in the zone. */
+static int check_in_use(zone_update_t *update,
+ const knot_dname_t *dname, uint16_t *rcode)
+{
+ const zone_node_t *node = zone_update_get_node(update, dname);
+ if (node == NULL || node->rrset_count == 0) {
+ *rcode = KNOT_RCODE_NXDOMAIN;
+ return KNOT_EPREREQ;
+ } else {
+ return KNOT_EOK;
+ }
+}
+
+/*!< \brief Checks whether DNAME is not in the zone. */
+static int check_not_in_use(zone_update_t *update,
+ const knot_dname_t *dname, uint16_t *rcode)
+{
+ const zone_node_t *node = zone_update_get_node(update, dname);
+ if (node == NULL || node->rrset_count == 0) {
+ return KNOT_EOK;
+ } else {
+ *rcode = KNOT_RCODE_YXDOMAIN;
+ return KNOT_EPREREQ;
+ }
+}
+
+/*!< \brief Returns true if rrset has 0 data or RDATA of size 0 (we need TTL). */
+static bool rrset_empty(const knot_rrset_t *rrset)
+{
+ switch (rrset->rrs.count) {
+ case 0:
+ return true;
+ case 1:
+ return rrset->rrs.rdata->len == 0;
+ default:
+ return false;
+ }
+}
+
+/*!< \brief Checks prereq for given packet RR. */
+static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass,
+ zone_update_t *update, uint16_t *rcode,
+ list_t *rrset_list)
+{
+ if (rrset->ttl != 0) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ if (knot_dname_in_bailiwick(rrset->owner, update->zone->name) < 0) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EOUTOFZONE;
+ }
+
+ if (rrset->rclass == KNOT_CLASS_ANY) {
+ if (!rrset_empty(rrset)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ if (rrset->type == KNOT_RRTYPE_ANY) {
+ return check_in_use(update, rrset->owner, rcode);
+ } else {
+ return check_type_exist(update, rrset, rcode);
+ }
+ } else if (rrset->rclass == KNOT_CLASS_NONE) {
+ if (!rrset_empty(rrset)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ if (rrset->type == KNOT_RRTYPE_ANY) {
+ return check_not_in_use(update, rrset->owner, rcode);
+ } else {
+ return check_type_not_exist(update, rrset, rcode);
+ }
+ } else if (rrset->rclass == qclass) {
+ // Store RRs for full check into list
+ int ret = add_rr_to_list(rrset_list, rrset);
+ if (ret != KNOT_EOK) {
+ *rcode = KNOT_RCODE_SERVFAIL;
+ }
+ return ret;
+ } else {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+}
+
+static inline bool is_addition(const knot_rrset_t *rr)
+{
+ return rr->rclass == KNOT_CLASS_IN;
+}
+
+static inline bool is_removal(const knot_rrset_t *rr)
+{
+ return rr->rclass == KNOT_CLASS_NONE || rr->rclass == KNOT_CLASS_ANY;
+}
+
+static inline bool is_rr_removal(const knot_rrset_t *rr)
+{
+ return rr->rclass == KNOT_CLASS_NONE;
+}
+
+static inline bool is_rrset_removal(const knot_rrset_t *rr)
+{
+ return rr->rclass == KNOT_CLASS_ANY && rr->type != KNOT_RRTYPE_ANY;
+}
+
+static inline bool is_node_removal(const knot_rrset_t *rr)
+{
+ return rr->rclass == KNOT_CLASS_ANY && rr->type == KNOT_RRTYPE_ANY;
+}
+
+/*!< \brief Returns true if last addition of certain types is to be replaced. */
+static bool should_replace(const knot_rrset_t *rrset)
+{
+ return rrset->type == KNOT_RRTYPE_CNAME ||
+ rrset->type == KNOT_RRTYPE_DNAME ||
+ rrset->type == KNOT_RRTYPE_NSEC3PARAM;
+}
+
+/*!< \brief Returns true if node contains given RR in its RRSets. */
+static bool node_contains_rr(const zone_node_t *node,
+ const knot_rrset_t *rrset)
+{
+ const knot_rdataset_t *zone_rrs = node_rdataset(node, rrset->type);
+ if (zone_rrs != NULL) {
+ assert(rrset->rrs.count == 1);
+ return knot_rdataset_member(zone_rrs, rrset->rrs.rdata);
+ } else {
+ return false;
+ }
+}
+
+/*!< \brief Returns true if CNAME is in this node. */
+static bool adding_to_cname(const knot_dname_t *owner,
+ const zone_node_t *node)
+{
+ if (node == NULL) {
+ // Node did not exist before update.
+ return false;
+ }
+
+ knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME);
+ if (knot_rrset_empty(&cname)) {
+ // Node did not contain CNAME before update.
+ return false;
+ }
+
+ // CNAME present
+ return true;
+}
+
+/*!< \brief Used to ignore SOA deletions and SOAs with lower serial than zone. */
+static bool skip_soa(const knot_rrset_t *rr, int64_t sn)
+{
+ if (rr->type == KNOT_RRTYPE_SOA &&
+ (rr->rclass == KNOT_CLASS_NONE || rr->rclass == KNOT_CLASS_ANY ||
+ (serial_compare(knot_soa_serial(rr->rrs.rdata), sn) != SERIAL_GREATER))) {
+ return true;
+ }
+
+ return false;
+}
+
+/*!< \brief Replaces possible singleton RR type in changeset. */
+static bool singleton_replaced(zone_update_t *update, const knot_rrset_t *rr)
+{
+ if (!should_replace(rr)) {
+ return false;
+ }
+
+ return zone_update_remove_rrset(update, rr->owner, rr->type) == KNOT_EOK;
+}
+
+/*!< \brief Adds RR into add section of changeset if it is deemed worthy. */
+static int add_rr_to_changeset(const knot_rrset_t *rr, zone_update_t *update)
+{
+ if (singleton_replaced(update, rr)) {
+ return KNOT_EOK;
+ }
+
+ return zone_update_add(update, rr);
+}
+
+/*!< \brief Processes CNAME addition (replace or ignore) */
+static int process_add_cname(const zone_node_t *node,
+ const knot_rrset_t *rr,
+ zone_update_t *update)
+{
+ knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME);
+ if (!knot_rrset_empty(&cname)) {
+ // If they are identical, ignore.
+ if (knot_rrset_equal(&cname, rr, true)) {
+ return KNOT_EOK;
+ }
+
+ int ret = zone_update_remove(update, &cname);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return add_rr_to_changeset(rr, update);
+ } else if (!node_empty(node)) {
+ // Other occupied node => ignore.
+ return KNOT_EOK;
+ } else {
+ // Can add.
+ return add_rr_to_changeset(rr, update);
+ }
+}
+
+/*!< \brief Processes NSEC3PARAM addition (ignore when not removed, or non-apex) */
+static int process_add_nsec3param(const zone_node_t *node,
+ const knot_rrset_t *rr,
+ zone_update_t *update)
+{
+ if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
+ // Ignore non-apex additions
+ char *owner = knot_dname_to_str_alloc(rr->owner);
+ log_warning("DDNS, refusing to add NSEC3PARAM to non-apex "
+ "node '%s'", owner);
+ free(owner);
+ return KNOT_EDENIED;
+ }
+ knot_rrset_t param = node_rrset(node, KNOT_RRTYPE_NSEC3PARAM);
+ if (knot_rrset_empty(&param)) {
+ return add_rr_to_changeset(rr, update);
+ }
+
+ char *owner = knot_dname_to_str_alloc(rr->owner);
+ log_warning("DDNS, refusing to add second NSEC3PARAM to node '%s'", owner);
+ free(owner);
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Processes SOA addition (ignore when non-apex), lower serials
+ * dropped before.
+ */
+static int process_add_soa(const zone_node_t *node,
+ const knot_rrset_t *rr,
+ zone_update_t *update)
+{
+ if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) {
+ // Adding SOA to non-apex node, ignore.
+ return KNOT_EOK;
+ }
+
+ // Get current SOA RR.
+ knot_rrset_t removed = node_rrset(node, KNOT_RRTYPE_SOA);
+ if (knot_rrset_equal(&removed, rr, true)) {
+ // If they are identical, ignore.
+ return KNOT_EOK;
+ }
+
+ return add_rr_to_changeset(rr, update);
+}
+
+/*!< \brief Adds normal RR, ignores when CNAME exists in node. */
+static int process_add_normal(const zone_node_t *node,
+ const knot_rrset_t *rr,
+ zone_update_t *update)
+{
+ if (adding_to_cname(rr->owner, node)) {
+ // Adding RR to CNAME node, ignore.
+ return KNOT_EOK;
+ }
+
+ if (node && node_contains_rr(node, rr)) {
+ // Adding existing RR, ignore.
+ return KNOT_EOK;
+ }
+
+ return add_rr_to_changeset(rr, update);
+}
+
+/*!< \brief Decides what to do with RR addition. */
+static int process_add(const knot_rrset_t *rr,
+ const zone_node_t *node,
+ zone_update_t *update)
+{
+ switch(rr->type) {
+ case KNOT_RRTYPE_CNAME:
+ return process_add_cname(node, rr, update);
+ case KNOT_RRTYPE_SOA:
+ return process_add_soa(node, rr, update);
+ case KNOT_RRTYPE_NSEC3PARAM:
+ return process_add_nsec3param(node, rr, update);
+ default:
+ return process_add_normal(node, rr, update);
+ }
+}
+
+/*!< \brief Removes single RR from zone. */
+static int process_rem_rr(const knot_rrset_t *rr,
+ const zone_node_t *node,
+ zone_update_t *update)
+{
+ if (node == NULL) {
+ // Removing from node that does not exist
+ return KNOT_EOK;
+ }
+
+ const bool apex_ns = node_rrtype_exists(node, KNOT_RRTYPE_SOA) &&
+ rr->type == KNOT_RRTYPE_NS;
+ if (apex_ns) {
+ const knot_rdataset_t *ns_rrs =
+ node_rdataset(node, KNOT_RRTYPE_NS);
+ if (ns_rrs == NULL) {
+ // Zone without apex NS.
+ return KNOT_EOK;
+ }
+ if (ns_rrs->count == 1) {
+ // Cannot remove last apex NS RR.
+ return KNOT_EOK;
+ }
+ }
+
+ knot_rrset_t to_modify = node_rrset(node, rr->type);
+ if (knot_rrset_empty(&to_modify)) {
+ // No such RRSet
+ return KNOT_EOK;
+ }
+
+ knot_rdataset_t *rrs = node_rdataset(node, rr->type);
+ if (!knot_rdataset_member(rrs, rr->rrs.rdata)) {
+ // Node does not contain this RR
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t rr_ttl = *rr;
+ rr_ttl.ttl = to_modify.ttl;
+
+ return zone_update_remove(update, &rr_ttl);
+}
+
+/*!< \brief Removes RRSet from zone. */
+static int process_rem_rrset(const knot_rrset_t *rrset,
+ const zone_node_t *node,
+ zone_update_t *update)
+{
+ bool is_apex = node_rrtype_exists(node, KNOT_RRTYPE_SOA);
+
+ if (is_apex && rrset->type == KNOT_RRTYPE_NS) {
+ // Ignore NS apex RRSet removals.
+ return KNOT_EOK;
+ }
+
+ if (node == NULL) {
+ // no such node in zone, ignore
+ return KNOT_EOK;
+ }
+
+ if (!node_rrtype_exists(node, rrset->type)) {
+ // no such RR, ignore
+ return KNOT_EOK;
+ }
+
+ knot_rrset_t to_remove = node_rrset(node, rrset->type);
+ return zone_update_remove(update, &to_remove);
+}
+
+/*!< \brief Removes node from zone. */
+static int process_rem_node(const knot_rrset_t *rr,
+ const zone_node_t *node, zone_update_t *update)
+{
+ if (node == NULL) {
+ return KNOT_EOK;
+ }
+
+ // Remove all RRSets from node
+ size_t rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; ++i) {
+ knot_rrset_t rrset = node_rrset_at(node, rrset_count - i - 1);
+ int ret = process_rem_rrset(&rrset, node, update);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*!< \brief Decides what to with removal. */
+static int process_remove(const knot_rrset_t *rr,
+ const zone_node_t *node,
+ zone_update_t *update)
+{
+ if (is_rr_removal(rr)) {
+ return process_rem_rr(rr, node, update);
+ } else if (is_rrset_removal(rr)) {
+ return process_rem_rrset(rr, node, update);
+ } else if (is_node_removal(rr)) {
+ return process_rem_node(rr, node, update);
+ } else {
+ return KNOT_EINVAL;
+ }
+}
+
+/*!< \brief Checks whether addition has not violated DNAME rules. */
+static bool sem_check(const knot_rrset_t *rr, const zone_node_t *zone_node,
+ zone_update_t *update)
+{
+ const zone_node_t *added_node = zone_contents_find_node(update->new_cont, rr->owner);
+
+ // we do this sem check AFTER adding the RR, so the node must exist
+ assert(added_node != NULL);
+
+ for (const zone_node_t *parent = added_node->parent;
+ parent != NULL; parent = parent->parent) {
+ if (node_rrtype_exists(parent, KNOT_RRTYPE_DNAME)) {
+ // Parent has DNAME RRSet, refuse update
+ return false;
+ }
+ }
+
+ if (rr->type != KNOT_RRTYPE_DNAME || zone_node == NULL) {
+ return true;
+ }
+
+ // Check that we have not created node with DNAME children.
+ if (zone_node->children > 0) {
+ // Updated node has children and DNAME was added, refuse update
+ return false;
+ }
+
+ return true;
+}
+
+/*!< \brief Checks whether we can accept this RR. */
+static int check_update(const knot_rrset_t *rrset, const knot_pkt_t *query,
+ uint16_t *rcode)
+{
+ /* Accept both subdomain and dname match. */
+ const knot_dname_t *owner = rrset->owner;
+ const knot_dname_t *qname = knot_pkt_qname(query);
+ const int in_bailiwick = knot_dname_in_bailiwick(owner, qname);
+ if (in_bailiwick < 0) {
+ *rcode = KNOT_RCODE_NOTZONE;
+ return KNOT_EOUTOFZONE;
+ }
+
+ if (rrset->rclass == knot_pkt_qclass(query)) {
+ if (knot_rrtype_is_metatype(rrset->type)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (rrset->rclass == KNOT_CLASS_ANY) {
+ if (!rrset_empty(rrset) ||
+ (knot_rrtype_is_metatype(rrset->type) &&
+ rrset->type != KNOT_RRTYPE_ANY)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else if (rrset->rclass == KNOT_CLASS_NONE) {
+ if (rrset->ttl != 0 || knot_rrtype_is_metatype(rrset->type)) {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+ } else {
+ *rcode = KNOT_RCODE_FORMERR;
+ return KNOT_EMALF;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!< \brief Checks RR and decides what to do with it. */
+static int process_rr(const knot_rrset_t *rr, zone_update_t *update)
+{
+ const zone_node_t *node = zone_update_get_node(update, rr->owner);
+
+ if (is_addition(rr)) {
+ int ret = process_add(rr, node, update);
+ if (ret == KNOT_EOK) {
+ if (!sem_check(rr, node, update)) {
+ return KNOT_EDENIED;
+ }
+ }
+ return ret;
+ } else if (is_removal(rr)) {
+ return process_remove(rr, node, update);
+ } else {
+ return KNOT_EMALF;
+ }
+}
+
+/*!< \brief Maps Knot return code to RCODE. */
+static uint16_t ret_to_rcode(int ret)
+{
+ if (ret == KNOT_EMALF) {
+ return KNOT_RCODE_FORMERR;
+ } else if (ret == KNOT_EDENIED) {
+ return KNOT_RCODE_REFUSED;
+ } else {
+ return KNOT_RCODE_SERVFAIL;
+ }
+}
+
+int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update,
+ uint16_t *rcode)
+{
+ if (query == NULL || rcode == NULL || update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ list_t rrset_list; // List used to store merged RRSets
+ init_list(&rrset_list);
+
+ const knot_pktsection_t *answer = knot_pkt_section(query, KNOT_ANSWER);
+ const knot_rrset_t *answer_rr = (answer->count > 0) ? knot_pkt_rr(answer, 0) : NULL;
+ for (int i = 0; i < answer->count; ++i) {
+ // Check what can be checked, store full RRs into list
+ ret = process_prereq(&answer_rr[i], knot_pkt_qclass(query),
+ update, rcode, &rrset_list);
+ if (ret != KNOT_EOK) {
+ rrset_list_clear(&rrset_list);
+ return ret;
+ }
+ }
+
+ // Check stored RRSets
+ ret = check_stored_rrsets(&rrset_list, update, rcode);
+ rrset_list_clear(&rrset_list);
+ return ret;
+}
+
+int ddns_process_update(const zone_t *zone, const knot_pkt_t *query,
+ zone_update_t *update, uint16_t *rcode)
+{
+ if (zone == NULL || query == NULL || update == NULL || rcode == NULL) {
+ if (rcode) {
+ *rcode = ret_to_rcode(KNOT_EINVAL);
+ }
+ return KNOT_EINVAL;
+ }
+
+ uint32_t sn_old = knot_soa_serial(zone_update_from(update)->rdata);
+
+ // Process all RRs in the authority section.
+ const knot_pktsection_t *authority = knot_pkt_section(query, KNOT_AUTHORITY);
+ const knot_rrset_t *authority_rr = (authority->count > 0) ? knot_pkt_rr(authority, 0) : NULL;
+ for (uint16_t i = 0; i < authority->count; ++i) {
+ const knot_rrset_t *rr = &authority_rr[i];
+ // Check if RR is correct.
+ int ret = check_update(rr, query, rcode);
+ if (ret != KNOT_EOK) {
+ assert(*rcode != KNOT_RCODE_NOERROR);
+ return ret;
+ }
+
+ if (skip_soa(rr, sn_old)) {
+ continue;
+ }
+
+ ret = process_rr(rr, update);
+ if (ret != KNOT_EOK) {
+ *rcode = ret_to_rcode(ret);
+ return ret;
+ }
+ }
+
+ *rcode = KNOT_RCODE_NOERROR;
+ return KNOT_EOK;
+}
diff --git a/src/knot/updates/ddns.h b/src/knot/updates/ddns.h
new file mode 100644
index 0000000..1d79218
--- /dev/null
+++ b/src/knot/updates/ddns.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "knot/updates/zone-update.h"
+#include "knot/zone/zone.h"
+#include "libknot/packet/pkt.h"
+
+/*!
+ * \brief Checks update prerequisite section.
+ *
+ * \param query DNS message containing the update.
+ * \param update Zone to be checked.
+ * \param rcode Returned DNS RCODE.
+ *
+ * \return KNOT_E*
+ */
+int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update,
+ uint16_t *rcode);
+
+/*!
+ * \brief Processes DNS update and creates a changeset out of it. Zone is left
+ * intact.
+ *
+ * \param zone Zone to be updated.
+ * \param query DNS message containing the update.
+ * \param update Output changeset.
+ * \param rcode Output DNS RCODE.
+ *
+ * \return KNOT_E*
+ */
+int ddns_process_update(const zone_t *zone, const knot_pkt_t *query,
+ zone_update_t *update, uint16_t *rcode);
diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c
new file mode 100644
index 0000000..81f3465
--- /dev/null
+++ b/src/knot/updates/zone-update.c
@@ -0,0 +1,1098 @@
+/* Copyright (C) 2023 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 <signal.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include "knot/catalog/interpret.h"
+#include "knot/common/log.h"
+#include "knot/common/systemd.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/server/server.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/adds_tree.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/serial.h"
+#include "knot/zone/zone-diff.h"
+#include "knot/zone/zonefile.h"
+#include "contrib/trim.h"
+#include "contrib/ucw/lists.h"
+
+// Call mem_trim() whenever accumulated size of updated zones reaches this size.
+#define UPDATE_MEMTRIM_AT (10 * 1024 * 1024)
+
+static int init_incremental(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents)
+{
+ if (old_contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = changeset_init(&update->change, zone->name);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (update->flags & UPDATE_HYBRID) {
+ update->new_cont = old_contents;
+ } else {
+ ret = zone_contents_cow(old_contents, &update->new_cont);
+ if (ret != KNOT_EOK) {
+ changeset_clear(&update->change);
+ return ret;
+ }
+ }
+
+ uint32_t apply_flags = (update->flags & UPDATE_STRICT) ? APPLY_STRICT : 0;
+ apply_flags |= (update->flags & UPDATE_HYBRID) ? APPLY_UNIFY_FULL : 0;
+ ret = apply_init_ctx(update->a_ctx, update->new_cont, apply_flags);
+ if (ret != KNOT_EOK) {
+ changeset_clear(&update->change);
+ return ret;
+ }
+
+ /* Copy base SOA RR. */
+ update->change.soa_from =
+ node_create_rrset(old_contents->apex, KNOT_RRTYPE_SOA);
+ if (update->change.soa_from == NULL) {
+ zone_contents_free(update->new_cont);
+ changeset_clear(&update->change);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+static int init_full(zone_update_t *update, zone_t *zone)
+{
+ update->new_cont = zone_contents_new(zone->name, true);
+ if (update->new_cont == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = apply_init_ctx(update->a_ctx, update->new_cont, APPLY_UNIFY_FULL);
+ if (ret != KNOT_EOK) {
+ zone_contents_free(update->new_cont);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static int replace_soa(zone_contents_t *contents, const knot_rrset_t *rr)
+{
+ /* SOA possible only within apex. */
+ if (!knot_dname_is_equal(rr->owner, contents->apex->owner)) {
+ return KNOT_EDENIED;
+ }
+
+ knot_rrset_t old_soa = node_rrset(contents->apex, KNOT_RRTYPE_SOA);
+ zone_node_t *n = contents->apex;
+ int ret = zone_contents_remove_rr(contents, &old_soa, &n);
+ if (ret != KNOT_EOK && ret != KNOT_EINVAL) {
+ return ret;
+ }
+
+ ret = zone_contents_add_rr(contents, rr, &n);
+ if (ret == KNOT_ETTL) {
+ return KNOT_EOK;
+ }
+
+ return ret;
+}
+
+static int init_base(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents,
+ zone_update_flags_t flags)
+{
+ if (update == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(update, 0, sizeof(*update));
+ update->zone = zone;
+ update->flags = flags;
+
+ update->a_ctx = calloc(1, sizeof(*update->a_ctx));
+ if (update->a_ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (zone->control_update != NULL && zone->control_update != update) {
+ log_zone_warning(zone->name, "blocked zone update due to open control transaction");
+ }
+
+ knot_sem_wait(&zone->cow_lock);
+ update->a_ctx->cow_mutex = &zone->cow_lock;
+
+ if (old_contents == NULL) {
+ old_contents = zone->contents; // don't obtain this pointer before any other zone_update ceased to exist!
+ }
+
+ int ret = KNOT_EINVAL;
+ if (flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ ret = init_incremental(update, zone, old_contents);
+ } else if (flags & UPDATE_FULL) {
+ ret = init_full(update, zone);
+ }
+ if (ret != KNOT_EOK) {
+ knot_sem_post(&zone->cow_lock);
+ free(update->a_ctx);
+ }
+
+ return ret;
+}
+
+/* ------------------------------- API -------------------------------------- */
+
+int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags)
+{
+ return init_base(update, zone, NULL, flags);
+}
+
+int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont,
+ zone_contents_t *new_cont, zone_update_flags_t flags,
+ bool ignore_dnssec, bool ignore_zonemd)
+{
+ if (update == NULL || zone == NULL || new_cont == NULL ||
+ !(flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) || (flags & UPDATE_FULL)) {
+ return KNOT_EINVAL;
+ }
+
+ changeset_t diff;
+ int ret = changeset_init(&diff, zone->name);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = init_base(update, zone, old_cont, flags);
+ if (ret != KNOT_EOK) {
+ changeset_clear(&diff);
+ return ret;
+ }
+
+ if (old_cont == NULL) {
+ old_cont = zone->contents;
+ }
+
+ ret = zone_contents_diff(old_cont, new_cont, &diff, ignore_dnssec, ignore_zonemd);
+ switch (ret) {
+ case KNOT_ENODIFF:
+ case KNOT_ESEMCHECK:
+ case KNOT_EOK:
+ break;
+ case KNOT_ERANGE:
+ additionals_tree_free(update->new_cont->adds_tree);
+ update->new_cont->adds_tree = NULL;
+ update->new_cont = NULL; // Prevent deep_free as old_cont will be used later.
+ update->a_ctx->flags &= ~APPLY_UNIFY_FULL; // Prevent Unify of old_cont that will be used later.
+ // FALLTHROUGH
+ default:
+ changeset_clear(&diff);
+ zone_update_clear(update);
+ return ret;
+ }
+
+ ret = zone_update_apply_changeset(update, &diff);
+ changeset_clear(&diff);
+ if (ret != KNOT_EOK) {
+ zone_update_clear(update);
+ return ret;
+ }
+
+ update->init_cont = new_cont;
+ return KNOT_EOK;
+}
+
+int zone_update_from_contents(zone_update_t *update, zone_t *zone_without_contents,
+ zone_contents_t *new_cont, zone_update_flags_t flags)
+{
+ if (update == NULL || zone_without_contents == NULL || new_cont == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ memset(update, 0, sizeof(*update));
+ update->zone = zone_without_contents;
+ update->flags = flags;
+ update->new_cont = new_cont;
+
+ update->a_ctx = calloc(1, sizeof(*update->a_ctx));
+ if (update->a_ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (zone_without_contents->control_update != NULL) {
+ log_zone_warning(zone_without_contents->name,
+ "blocked zone update due to open control transaction");
+ }
+
+ knot_sem_wait(&update->zone->cow_lock);
+ update->a_ctx->cow_mutex = &update->zone->cow_lock;
+
+ if (flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ int ret = changeset_init(&update->change, zone_without_contents->name);
+ if (ret != KNOT_EOK) {
+ free(update->a_ctx);
+ update->a_ctx = NULL;
+ knot_sem_post(&update->zone->cow_lock);
+ return ret;
+ }
+
+ update->change.soa_from = node_create_rrset(new_cont->apex, KNOT_RRTYPE_SOA);
+ if (update->change.soa_from == NULL) {
+ changeset_clear(&update->change);
+ free(update->a_ctx);
+ update->a_ctx = NULL;
+ knot_sem_post(&update->zone->cow_lock);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ uint32_t apply_flags = (update->flags & UPDATE_STRICT) ? APPLY_STRICT : 0;
+ int ret = apply_init_ctx(update->a_ctx, update->new_cont, apply_flags | APPLY_UNIFY_FULL);
+ if (ret != KNOT_EOK) {
+ changeset_clear(&update->change);
+ free(update->a_ctx);
+ update->a_ctx = NULL;
+ knot_sem_post(&update->zone->cow_lock);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+int zone_update_start_extra(zone_update_t *update, conf_t *conf)
+{
+ assert((update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)));
+
+ int ret = changeset_init(&update->extra_ch, update->new_cont->apex->owner);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (update->init_cont != NULL) {
+ ret = zone_update_increment_soa(update, conf);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_contents_diff(update->init_cont, update->new_cont,
+ &update->extra_ch, false, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ update->extra_ch.soa_from = node_create_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA);
+ if (update->extra_ch.soa_from == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ ret = zone_update_increment_soa(update, conf);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ update->extra_ch.soa_to = node_create_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA);
+ if (update->extra_ch.soa_to == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+
+ update->flags |= UPDATE_EXTRA_CHSET;
+ return KNOT_EOK;
+}
+
+const zone_node_t *zone_update_get_node(zone_update_t *update, const knot_dname_t *dname)
+{
+ if (update == NULL || dname == NULL) {
+ return NULL;
+ }
+
+ return zone_contents_node_or_nsec3(update->new_cont, dname);
+}
+
+uint32_t zone_update_current_serial(zone_update_t *update)
+{
+ const zone_node_t *apex = update->new_cont->apex;
+ if (apex != NULL) {
+ return knot_soa_serial(node_rdataset(apex, KNOT_RRTYPE_SOA)->rdata);
+ } else {
+ return 0;
+ }
+}
+
+bool zone_update_changed_nsec3param(const zone_update_t *update)
+{
+ if (update->zone->contents == NULL) {
+ return true;
+ }
+
+ dnssec_nsec3_params_t *orig = &update->zone->contents->nsec3_params;
+ dnssec_nsec3_params_t *upd = &update->new_cont->nsec3_params;
+ return !dnssec_nsec3_params_match(orig, upd);
+}
+
+const knot_rdataset_t *zone_update_from(zone_update_t *update)
+{
+ if (update == NULL) {
+ return NULL;
+ }
+
+ if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ const zone_node_t *apex = update->zone->contents->apex;
+ return node_rdataset(apex, KNOT_RRTYPE_SOA);
+ }
+
+ return NULL;
+}
+
+const knot_rdataset_t *zone_update_to(zone_update_t *update)
+{
+ if (update == NULL) {
+ return NULL;
+ }
+
+ if (update->flags & UPDATE_NO_CHSET) {
+ zone_diff_t diff = { .apex = update->new_cont->apex };
+ return zone_diff_to(&diff) == zone_diff_from(&diff) ?
+ NULL : node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA);
+ } else if (update->flags & UPDATE_FULL) {
+ const zone_node_t *apex = update->new_cont->apex;
+ return node_rdataset(apex, KNOT_RRTYPE_SOA);
+ } else {
+ if (update->change.soa_to == NULL) {
+ return NULL;
+ }
+ return &update->change.soa_to->rrs;
+ }
+
+ return NULL;
+}
+
+void zone_update_clear(zone_update_t *update)
+{
+ if (update == NULL || update->zone == NULL) {
+ return;
+ }
+
+ if (update->new_cont != NULL) {
+ additionals_tree_free(update->new_cont->adds_tree);
+ update->new_cont->adds_tree = NULL;
+ }
+
+ if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ changeset_clear(&update->change);
+ changeset_clear(&update->extra_ch);
+ }
+
+ zone_contents_deep_free(update->init_cont);
+
+ if (update->flags & (UPDATE_FULL | UPDATE_HYBRID)) {
+ apply_cleanup(update->a_ctx);
+ zone_contents_deep_free(update->new_cont);
+ } else {
+ apply_rollback(update->a_ctx);
+ }
+
+ free(update->a_ctx);
+ memset(update, 0, sizeof(*update));
+}
+
+inline static void update_affected_rrtype(zone_update_t *update, uint16_t rrtype)
+{
+ switch (rrtype) {
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_NSEC3:
+ update->flags |= UPDATE_CHANGED_NSEC;
+ break;
+ }
+}
+
+static int solve_add_different_ttl(zone_update_t *update, const knot_rrset_t *add)
+{
+ if (add->type == KNOT_RRTYPE_RRSIG || add->type == KNOT_RRTYPE_SOA) {
+ return KNOT_EOK;
+ }
+
+ const zone_node_t *exist_node = zone_contents_find_node(update->new_cont, add->owner);
+ const knot_rrset_t exist_rr = node_rrset(exist_node, add->type);
+ if (knot_rrset_empty(&exist_rr) || exist_rr.ttl == add->ttl) {
+ return KNOT_EOK;
+ }
+
+ knot_dname_txt_storage_t buff;
+ char *owner = knot_dname_to_str(buff, add->owner, sizeof(buff));
+ if (owner == NULL) {
+ owner = "";
+ }
+ char type[16] = "";
+ knot_rrtype_to_string(add->type, type, sizeof(type));
+ log_zone_notice(update->zone->name, "TTL mismatch, owner %s, type %s, "
+ "TTL set to %u", owner, type, add->ttl);
+
+ knot_rrset_t *exist_copy = knot_rrset_copy(&exist_rr, NULL);
+ if (exist_copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ int ret = zone_update_remove(update, exist_copy);
+ if (ret == KNOT_EOK) {
+ exist_copy->ttl = add->ttl;
+ ret = zone_update_add(update, exist_copy);
+ }
+ knot_rrset_free(exist_copy, NULL);
+ return ret;
+}
+
+int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset)
+{
+ if (update == NULL || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (knot_rrset_empty(rrset)) {
+ return KNOT_EOK;
+ }
+
+ if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ int ret = solve_add_different_ttl(update, rrset);
+ if (ret == KNOT_EOK && !(update->flags & UPDATE_NO_CHSET)) {
+ ret = changeset_add_addition(&update->change, rrset, CHANGESET_CHECK);
+ }
+ if (ret == KNOT_EOK && (update->flags & UPDATE_EXTRA_CHSET)) {
+ assert(!(update->flags & UPDATE_NO_CHSET));
+ ret = changeset_add_addition(&update->extra_ch, rrset, CHANGESET_CHECK);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (update->flags & UPDATE_INCREMENTAL) {
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ // replace previous SOA
+ int ret = apply_replace_soa(update->a_ctx, rrset);
+ if (ret != KNOT_EOK && !(update->flags & UPDATE_NO_CHSET)) {
+ changeset_remove_addition(&update->change, rrset);
+ }
+ return ret;
+ }
+
+ int ret = apply_add_rr(update->a_ctx, rrset);
+ if (ret != KNOT_EOK) {
+ if (!(update->flags & UPDATE_NO_CHSET)) {
+ changeset_remove_addition(&update->change, rrset);
+ }
+ return ret;
+ }
+
+ update_affected_rrtype(update, rrset->type);
+ return KNOT_EOK;
+ } else if (update->flags & (UPDATE_FULL | UPDATE_HYBRID)) {
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* replace previous SOA */
+ return replace_soa(update->new_cont, rrset);
+ }
+
+ zone_node_t *n = NULL;
+ int ret = zone_contents_add_rr(update->new_cont, rrset, &n);
+ if (ret == KNOT_ETTL) {
+ knot_dname_txt_storage_t buff;
+ char *owner = knot_dname_to_str(buff, rrset->owner, sizeof(buff));
+ if (owner == NULL) {
+ owner = "";
+ }
+ char type[16] = "";
+ knot_rrtype_to_string(rrset->type, type, sizeof(type));
+ log_zone_notice(update->new_cont->apex->owner,
+ "TTL mismatch, owner %s, type %s, "
+ "TTL set to %u", owner, type, rrset->ttl);
+ return KNOT_EOK;
+ }
+
+ return ret;
+ } else {
+ return KNOT_EINVAL;
+ }
+}
+
+int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset)
+{
+ if (update == NULL || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (knot_rrset_empty(rrset)) {
+ return KNOT_EOK;
+ }
+
+ if ((update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) &&
+ rrset->type != KNOT_RRTYPE_SOA && !(update->flags & UPDATE_NO_CHSET)) {
+ int ret = changeset_add_removal(&update->change, rrset, CHANGESET_CHECK);
+ if (ret == KNOT_EOK && (update->flags & UPDATE_EXTRA_CHSET)) {
+ assert(!(update->flags & UPDATE_NO_CHSET));
+ ret = changeset_add_removal(&update->extra_ch, rrset, CHANGESET_CHECK);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (update->flags & UPDATE_INCREMENTAL) {
+ if (rrset->type == KNOT_RRTYPE_SOA) {
+ /* SOA is replaced with addition */
+ return KNOT_EOK;
+ }
+
+ int ret = apply_remove_rr(update->a_ctx, rrset);
+ if (ret != KNOT_EOK) {
+ if (!(update->flags & UPDATE_NO_CHSET)) {
+ changeset_remove_removal(&update->change, rrset);
+ }
+ return ret;
+ }
+
+ update_affected_rrtype(update, rrset->type);
+ return KNOT_EOK;
+ } else if (update->flags & (UPDATE_FULL | UPDATE_HYBRID)) {
+ zone_node_t *n = NULL;
+ return zone_contents_remove_rr(update->new_cont, rrset, &n);
+ } else {
+ return KNOT_EINVAL;
+ }
+}
+
+int zone_update_remove_rrset(zone_update_t *update, knot_dname_t *owner, uint16_t type)
+{
+ if (update == NULL || owner == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const zone_node_t *node = zone_contents_node_or_nsec3(update->new_cont, owner);
+ if (node == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ knot_rrset_t rrset = node_rrset(node, type);
+ if (rrset.owner == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ return zone_update_remove(update, &rrset);
+}
+
+int zone_update_remove_node(zone_update_t *update, const knot_dname_t *owner)
+{
+ if (update == NULL || owner == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const zone_node_t *node = zone_contents_node_or_nsec3(update->new_cont, owner);
+ if (node == NULL) {
+ return KNOT_ENONODE;
+ }
+
+ size_t rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; ++i) {
+ knot_rrset_t rrset = node_rrset_at(node, rrset_count - 1 - i);
+ int ret = zone_update_remove(update, &rrset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int update_chset_step(const knot_rrset_t *rrset, bool addition, void *ctx)
+{
+ zone_update_t *update = ctx;
+ if (addition) {
+ return zone_update_add(update, rrset);
+ } else {
+ return zone_update_remove(update, rrset);
+ }
+}
+
+int zone_update_apply_changeset(zone_update_t *update, const changeset_t *changes)
+{
+ return changeset_walk(changes, update_chset_step, update);
+}
+
+int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes)
+{
+ changeset_t reverse;
+ reverse.remove = changes->add;
+ reverse.add = changes->remove;
+ reverse.soa_from = changes->soa_to;
+ reverse.soa_to = changes->soa_from;
+ return zone_update_apply_changeset(update, &reverse);
+}
+
+static int set_new_soa(zone_update_t *update, unsigned serial_policy)
+{
+ assert(update);
+
+ knot_rrset_t *soa_cpy = node_create_rrset(update->new_cont->apex,
+ KNOT_RRTYPE_SOA);
+ if (soa_cpy == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = zone_update_remove(update, soa_cpy);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(soa_cpy, NULL);
+ return ret;
+ }
+
+ uint32_t old_serial = knot_soa_serial(soa_cpy->rrs.rdata);
+ uint32_t new_serial = serial_next(old_serial, serial_policy, 1);
+ if (serial_compare(old_serial, new_serial) != SERIAL_LOWER) {
+ log_zone_warning(update->zone->name, "updated SOA serial is lower "
+ "than current, serial %u -> %u",
+ old_serial, new_serial);
+ ret = KNOT_ESOAINVAL;
+ } else {
+ knot_soa_serial_set(soa_cpy->rrs.rdata, new_serial);
+
+ ret = zone_update_add(update, soa_cpy);
+ }
+ knot_rrset_free(soa_cpy, NULL);
+
+ return ret;
+}
+
+int zone_update_increment_soa(zone_update_t *update, conf_t *conf)
+{
+ if (update == NULL || conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_SERIAL_POLICY, update->zone->name);
+ return set_new_soa(update, conf_opt(&val));
+}
+
+static void get_zone_diff(zone_diff_t *zdiff, zone_update_t *up)
+{
+ zdiff->nodes = *up->a_ctx->node_ptrs;
+ zdiff->nsec3s = *up->a_ctx->nsec3_ptrs;
+ zdiff->apex = up->new_cont->apex;
+}
+
+static int commit_journal(conf_t *conf, zone_update_t *update)
+{
+ conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name);
+ unsigned content = conf_opt(&val);
+ int ret = KNOT_EOK;
+ if (update->flags & UPDATE_NO_CHSET) {
+ zone_diff_t diff;
+ get_zone_diff(&diff, update);
+ if (content != JOURNAL_CONTENT_NONE && !zone_update_no_change(update)) {
+ ret = zone_diff_store(conf, update->zone, &diff);
+ }
+ } else if ((update->flags & UPDATE_INCREMENTAL) ||
+ (update->flags & UPDATE_HYBRID)) {
+ changeset_t *extra = (update->flags & UPDATE_EXTRA_CHSET) ? &update->extra_ch : NULL;
+ if (content != JOURNAL_CONTENT_NONE && !zone_update_no_change(update)) {
+ ret = zone_change_store(conf, update->zone, &update->change, extra);
+ }
+ } else {
+ if (content == JOURNAL_CONTENT_ALL) {
+ return zone_in_journal_store(conf, update->zone, update->new_cont);
+ } else if (content != JOURNAL_CONTENT_NONE) { // zone_in_journal_store does this automatically
+ return zone_changes_clear(conf, update->zone);
+ }
+ }
+ return ret;
+}
+
+static int commit_incremental(conf_t *conf, zone_update_t *update)
+{
+ assert(update);
+
+ if (zone_update_to(update) == NULL && !zone_update_no_change(update)) {
+ /* No SOA in the update, create one according to the current policy */
+ int ret = zone_update_increment_soa(update, conf);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int commit_full(conf_t *conf, zone_update_t *update)
+{
+ assert(update);
+
+ /* Check if we have SOA. We might consider adding full semantic check here.
+ * But if we wanted full sem-check I'd consider being it controlled by a flag
+ * - to enable/disable it on demand. */
+ if (!node_rrtype_exists(update->new_cont->apex, KNOT_RRTYPE_SOA)) {
+ return KNOT_ESEMCHECK;
+ }
+
+ return KNOT_EOK;
+}
+
+static int update_catalog(conf_t *conf, zone_update_t *update)
+{
+ conf_val_t val = conf_zone_get(conf, C_CATALOG_TPL, update->zone->name);
+ if (val.code != KNOT_EOK) {
+ return (val.code == KNOT_ENOENT || val.code == KNOT_YP_EINVAL_ID) ? KNOT_EOK : val.code;
+ }
+
+ int ret = catalog_zone_verify(update->new_cont);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ssize_t upd_count = 0;
+ if ((update->flags & UPDATE_NO_CHSET)) {
+ zone_diff_t diff;
+ get_zone_diff(&diff, update);
+ ret = catalog_update_from_zone(zone_catalog_upd(update->zone),
+ NULL, &diff, update->new_cont,
+ false, zone_catalog(update->zone), &upd_count);
+ } else if ((update->flags & UPDATE_INCREMENTAL)) {
+ ret = catalog_update_from_zone(zone_catalog_upd(update->zone),
+ update->change.remove, NULL, update->new_cont,
+ true, zone_catalog(update->zone), &upd_count);
+ if (ret == KNOT_EOK) {
+ ret = catalog_update_from_zone(zone_catalog_upd(update->zone),
+ update->change.add, NULL, update->new_cont,
+ false, NULL, &upd_count);
+ }
+ } else {
+ ret = catalog_update_del_all(zone_catalog_upd(update->zone),
+ zone_catalog(update->zone),
+ update->zone->name, &upd_count);
+ if (ret == KNOT_EOK) {
+ ret = catalog_update_from_zone(zone_catalog_upd(update->zone),
+ update->new_cont, NULL, update->new_cont,
+ false, NULL, &upd_count);
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ log_zone_info(update->zone->name, "catalog reloaded, %zd updates", upd_count);
+ update->zone->server->catalog_upd_signal = true;
+ if (kill(getpid(), SIGUSR1) != 0) {
+ ret = knot_map_errno();
+ }
+ } else {
+ // this cant normally happen, just some ENOMEM or so
+ (void)catalog_update_del_all(zone_catalog_upd(update->zone),
+ zone_catalog(update->zone),
+ update->zone->name, &upd_count);
+ }
+
+ return ret;
+}
+
+typedef struct {
+ pthread_mutex_t lock;
+ size_t counter;
+} counter_reach_t;
+
+static bool counter_reach(counter_reach_t *counter, size_t increment, size_t limit)
+{
+ bool reach = false;
+ pthread_mutex_lock(&counter->lock);
+ counter->counter += increment;
+ if (counter->counter >= limit) {
+ counter->counter = 0;
+ reach = true;
+ }
+ pthread_mutex_unlock(&counter->lock);
+ return reach;
+}
+
+/*! \brief Struct for what needs to be cleared after RCU.
+ *
+ * This can't be zone_update_t structure as this might be already freed at that time.
+ */
+typedef struct {
+ struct rcu_head rcuhead;
+
+ zone_contents_t *free_contents;
+ void (*free_method)(zone_contents_t *);
+
+ apply_ctx_t *cleanup_apply;
+
+ size_t new_cont_size;
+} update_clear_ctx_t;
+
+static void update_clear(struct rcu_head *param)
+{
+ static counter_reach_t counter = { PTHREAD_MUTEX_INITIALIZER, 0 };
+
+ update_clear_ctx_t *ctx = (update_clear_ctx_t *)param;
+
+ ctx->free_method(ctx->free_contents);
+ apply_cleanup(ctx->cleanup_apply);
+ free(ctx->cleanup_apply);
+
+ if (counter_reach(&counter, ctx->new_cont_size, UPDATE_MEMTRIM_AT)) {
+ mem_trim();
+ }
+
+ free(ctx);
+}
+
+static void discard_adds_tree(zone_update_t *update)
+{
+ additionals_tree_free(update->new_cont->adds_tree);
+ update->new_cont->adds_tree = NULL;
+}
+
+int zone_update_semcheck(conf_t *conf, zone_update_t *update)
+{
+ if (update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ zone_tree_t *node_ptrs = (update->flags & UPDATE_INCREMENTAL) ?
+ update->a_ctx->node_ptrs : NULL;
+
+ // adjust_cb_nsec3_pointer not needed as we don't check DNSSEC here
+ int ret = zone_adjust_contents(update->new_cont, adjust_cb_flags, NULL,
+ false, false, 1, node_ptrs);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ sem_handler_t handler = {
+ .cb = err_handler_logger
+ };
+
+ conf_val_t val = conf_zone_get(conf, C_SEM_CHECKS, update->zone->name);
+ semcheck_optional_t mode = (conf_opt(&val) == SEMCHECKS_SOFT) ?
+ SEMCHECK_MANDATORY_SOFT : SEMCHECK_MANDATORY_ONLY;
+
+ ret = sem_checks_process(update->new_cont, mode, &handler, time(NULL));
+ if (ret != KNOT_EOK) {
+ // error is logged by the error handler
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+int zone_update_verify_digest(conf_t *conf, zone_update_t *update)
+{
+ conf_val_t val = conf_zone_get(conf, C_ZONEMD_VERIFY, update->zone->name);
+ if (!conf_bool(&val)) {
+ return KNOT_EOK;
+ }
+
+ int ret = zone_contents_digest_verify(update->new_cont);
+ if (ret != KNOT_EOK) {
+ log_zone_error(update->zone->name, "ZONEMD, verification failed (%s)",
+ knot_strerror(ret));
+ } else {
+ log_zone_info(update->zone->name, "ZONEMD, verification successful");
+ }
+
+ return ret;
+}
+
+int zone_update_commit(conf_t *conf, zone_update_t *update)
+{
+ if (conf == NULL || update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ if ((update->flags & UPDATE_INCREMENTAL) && zone_update_no_change(update)) {
+ zone_update_clear(update);
+ return KNOT_EOK;
+ }
+
+ if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ ret = commit_incremental(conf, update);
+ } else {
+ ret = commit_full(conf, update);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, update->zone->name);
+ bool dnssec = conf_bool(&val);
+
+ conf_val_t thr = conf_zone_get(conf, C_ADJUST_THR, update->zone->name);
+ if ((update->flags & (UPDATE_HYBRID | UPDATE_FULL))) {
+ ret = zone_adjust_full(update->new_cont, conf_int(&thr));
+ } else {
+ ret = zone_adjust_incremental_update(update, conf_int(&thr));
+ }
+ if (ret != KNOT_EOK) {
+ discard_adds_tree(update);
+ return ret;
+ }
+
+ /* Check the zone size. */
+ val = conf_zone_get(conf, C_ZONE_MAX_SIZE, update->zone->name);
+ size_t size_limit = conf_int(&val);
+
+ if (update->new_cont->size > size_limit) {
+ discard_adds_tree(update);
+ return KNOT_EZONESIZE;
+ }
+
+ val = conf_zone_get(conf, C_DNSSEC_VALIDATION, update->zone->name);
+ if (conf_bool(&val)) {
+ bool incr_valid = update->flags & UPDATE_INCREMENTAL;
+ const char *msg_valid = incr_valid ? "incremental " : "";
+
+ ret = knot_dnssec_validate_zone(update, conf, 0, incr_valid);
+ if (ret != KNOT_EOK) {
+ log_zone_error(update->zone->name, "DNSSEC, %svalidation failed (%s)",
+ msg_valid, knot_strerror(ret));
+ char type_str[16];
+ knot_dname_txt_storage_t name_str;
+ if (knot_dname_to_str(name_str, update->validation_hint.node, sizeof(name_str)) != NULL &&
+ knot_rrtype_to_string(update->validation_hint.rrtype, type_str, sizeof(type_str)) >= 0) {
+ log_zone_error(update->zone->name, "DNSSEC, validation hint: %s %s",
+ name_str, type_str);
+ }
+ discard_adds_tree(update);
+ if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_INVALID) {
+ systemd_emit_zone_invalid(update->zone->name);
+ }
+ return ret;
+ } else {
+ log_zone_info(update->zone->name, "DNSSEC, %svalidation successful", msg_valid);
+ }
+ }
+
+ ret = update_catalog(conf, update);
+ if (ret != KNOT_EOK) {
+ log_zone_error(update->zone->name, "failed to process catalog zone (%s)", knot_strerror(ret));
+ discard_adds_tree(update);
+ return ret;
+ }
+
+ ret = commit_journal(conf, update);
+ if (ret != KNOT_EOK) {
+ discard_adds_tree(update);
+ return ret;
+ }
+
+ if (dnssec && zone_is_slave(conf, update->zone)) {
+ ret = zone_set_lastsigned_serial(update->zone,
+ zone_contents_serial(update->new_cont));
+ if (ret != KNOT_EOK) {
+ log_zone_warning(update->zone->name,
+ "unable to save lastsigned serial, "
+ "future transfers might be broken");
+ }
+ }
+
+ /* Switch zone contents. */
+ zone_contents_t *old_contents;
+ old_contents = zone_switch_contents(update->zone, update->new_cont);
+
+ if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ changeset_clear(&update->change);
+ changeset_clear(&update->extra_ch);
+ }
+ zone_contents_deep_free(update->init_cont);
+
+ update_clear_ctx_t *clear_ctx = calloc(1, sizeof(*clear_ctx));
+ if (clear_ctx != NULL) {
+ clear_ctx->free_contents = old_contents;
+ clear_ctx->free_method = (
+ (update->flags & (UPDATE_FULL | UPDATE_HYBRID)) ?
+ zone_contents_deep_free : update_free_zone
+ );
+ clear_ctx->cleanup_apply = update->a_ctx;
+ clear_ctx->new_cont_size = update->new_cont->size;
+
+ call_rcu((struct rcu_head *)clear_ctx, update_clear);
+ } else {
+ log_zone_error(update->zone->name, "failed to deallocate unused memory");
+ }
+
+ /* Sync zonefile immediately if configured. */
+ val = conf_zone_get(conf, C_ZONEFILE_SYNC, update->zone->name);
+ if (conf_int(&val) == 0) {
+ zone_events_schedule_now(update->zone, ZONE_EVENT_FLUSH);
+ }
+
+ if (conf->cache.srv_dbus_event & DBUS_EVENT_ZONE_UPDATED) {
+ systemd_emit_zone_updated(update->zone->name,
+ zone_contents_serial(update->zone->contents));
+ }
+
+ memset(update, 0, sizeof(*update));
+
+ return KNOT_EOK;
+}
+
+bool zone_update_no_change(zone_update_t *update)
+{
+ if (update == NULL) {
+ return true;
+ }
+
+ if (update->flags & UPDATE_NO_CHSET) {
+ zone_diff_t diff;
+ get_zone_diff(&diff, update);
+ return (zone_diff_serialized_size(diff) == 0);
+ } else if (update->flags & (UPDATE_INCREMENTAL | UPDATE_HYBRID)) {
+ return changeset_empty(&update->change);
+ } else {
+ /* This branch does not make much sense and FULL update will most likely
+ * be a change every time anyway, just return false. */
+ return false;
+ }
+}
+
+static bool zone_diff_rdataset(const zone_contents_t *c, uint16_t rrtype)
+{
+ const knot_rdataset_t *a = node_rdataset(binode_counterpart(c->apex), rrtype);
+ const knot_rdataset_t *b = node_rdataset(c->apex, rrtype);
+ if ((a == NULL && b == NULL) || (a != NULL && b != NULL && a->rdata == b->rdata)) {
+ return false;
+ } else {
+ return !knot_rdataset_eq(a, b);
+ }
+}
+
+static bool contents_have_dnskey(const zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return false;
+ }
+ assert(contents->apex != NULL);
+ return (node_rrtype_exists(contents->apex, KNOT_RRTYPE_DNSKEY) ||
+ node_rrtype_exists(contents->apex, KNOT_RRTYPE_CDNSKEY) ||
+ node_rrtype_exists(contents->apex, KNOT_RRTYPE_CDS));
+}
+
+bool zone_update_changes_dnskey(zone_update_t *update)
+{
+ if (update->flags & UPDATE_NO_CHSET) {
+ return (zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_DNSKEY) ||
+ zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDNSKEY) ||
+ zone_diff_rdataset(update->new_cont, KNOT_RRTYPE_CDS));
+ } else if (update->flags & UPDATE_FULL) {
+ return contents_have_dnskey(update->new_cont);
+ } else {
+ return (contents_have_dnskey(update->change.remove) ||
+ contents_have_dnskey(update->change.add));
+ }
+}
diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h
new file mode 100644
index 0000000..0499d72
--- /dev/null
+++ b/src/knot/updates/zone-update.h
@@ -0,0 +1,299 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include "knot/updates/apply.h"
+#include "knot/conf/conf.h"
+#include "knot/updates/changesets.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/zone.h"
+
+typedef struct {
+ knot_dname_storage_t next;
+ const knot_dname_t *node;
+ uint16_t rrtype;
+} dnssec_validation_hint_t;
+
+/*! \brief Structure for zone contents updating / querying. */
+typedef struct zone_update {
+ zone_t *zone; /*!< Zone being updated. */
+ zone_contents_t *new_cont; /*!< New zone contents for full updates. */
+ changeset_t change; /*!< Changes we want to apply. */
+ zone_contents_t *init_cont; /*!< Exact contents of the zonefile. */
+ changeset_t extra_ch; /*!< Extra changeset to store just diff btwn zonefile and result. */
+ apply_ctx_t *a_ctx; /*!< Context for applying changesets. */
+ uint32_t flags; /*!< Zone update flags. */
+ dnssec_validation_hint_t validation_hint;
+} zone_update_t;
+
+typedef struct {
+ zone_update_t *update; /*!< The update we're iterating over. */
+ zone_tree_it_t tree_it; /*!< Iterator for the new zone. */
+ const zone_node_t *cur_node; /*!< Current node in the new zone. */
+ bool nsec3; /*!< Set when we're using the NSEC3 node tree. */
+} zone_update_iter_t;
+
+typedef enum {
+ // Mutually exclusive flags
+ UPDATE_FULL = 1 << 0, /*!< Replace the old zone by a complete new one. */
+ UPDATE_HYBRID = 1 << 1, /*!< Changeset like for incremental, adjusting like full. */
+ UPDATE_INCREMENTAL = 1 << 2, /*!< Apply changes to the old zone. */
+ // Additional flags
+ UPDATE_STRICT = 1 << 4, /*!< Apply changes strictly, i.e. fail when removing nonexistent RR. */
+ UPDATE_EXTRA_CHSET = 1 << 6, /*!< Extra changeset in use, to store diff btwn zonefile and final contents. */
+ UPDATE_CHANGED_NSEC = 1 << 7, /*!< This incremental update affects NSEC or NSEC3 nodes in zone. */
+ UPDATE_NO_CHSET = 1 << 8, /*!< Avoid using changeset and serialize to journal from diff of bi-nodes. */
+} zone_update_flags_t;
+
+/*!
+ * \brief Inits given zone update structure, new memory context is created.
+ *
+ * \param update Zone update structure to init.
+ * \param zone Init with this zone.
+ * \param flags Flags to control the behavior of the update.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags);
+
+/*!
+ * \brief Inits update structure, the update is built like IXFR from differences.
+ *
+ * The existing zone with its own contents is taken as a base,
+ * the new candidate zone contents are taken as new contents,
+ * the diff is calculated, so that this update is INCREMENTAL.
+ *
+ * \param update Zone update structure to init.
+ * \param zone Init with this zone.
+ * \param old_cont The current zone contents the diff will be against. Probably zone->contents.
+ * \param new_cont New zone contents. Will be taken over (and later freed) by zone update.
+ * \param flags Flags for update. Must be UPDATE_INCREMENTAL or UPDATE_HYBRID.
+ * \param ignore_dnssec Ignore DNSSEC records.
+ * \param ignore_zonemd Ignore ZONEMD records.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont,
+ zone_contents_t *new_cont, zone_update_flags_t flags,
+ bool ignore_dnssec, bool ignore_zonemd);
+
+/*!
+ * \brief Inits a zone update based on new zone contents.
+ *
+ * \param update Zone update structure to init.
+ * \param zone_without_contents Init with this zone. Its contents may be NULL.
+ * \param new_cont New zone contents. Will be taken over (and later freed) by zone update.
+ * \param flags Flags for update.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_from_contents(zone_update_t *update, zone_t *zone_without_contents,
+ zone_contents_t *new_cont, zone_update_flags_t flags);
+
+/*!
+ * \brief Inits using extra changeset, increments SOA serial.
+ *
+ * This shall be used after from_differences, to start tracking changes that are against the loaded zonefile.
+ *
+ * \param update Zone update.
+ * \param conf Configuration.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_start_extra(zone_update_t *update, conf_t *conf);
+
+/*!
+ * \brief Returns node that would be in the zone after updating it.
+ *
+ * \note Returned node is either zone original or synthesized, do *not* free
+ * or modify. Returned node is allocated on local mempool.
+ *
+ * \param update Zone update.
+ * \param dname Dname to search for.
+ *
+ * \return Node after zone update.
+ */
+const zone_node_t *zone_update_get_node(zone_update_t *update,
+ const knot_dname_t *dname);
+
+/*!
+ * \brief Returns the serial from the current apex.
+ *
+ * \param update Zone update.
+ *
+ * \return 0 if no apex was found, its serial otherwise.
+ */
+uint32_t zone_update_current_serial(zone_update_t *update);
+
+/*! \brief Return true if NSEC3PARAM has been changed in this update. */
+bool zone_update_changed_nsec3param(const zone_update_t *update);
+
+/*!
+ * \brief Returns the SOA rdataset we're updating from.
+ *
+ * \param update Zone update.
+ *
+ * \return The original SOA rdataset.
+ */
+const knot_rdataset_t *zone_update_from(zone_update_t *update);
+
+/*!
+ * \brief Returns the SOA rdataset we're updating to.
+ *
+ * \param update Zone update.
+ *
+ * \return NULL if no new SOA has been added, new SOA otherwise.
+ *
+ * \todo Refactor this function according to its use.
+ */
+const knot_rdataset_t *zone_update_to(zone_update_t *update);
+
+/*!
+ * \brief Clear data allocated by given zone update structure.
+ *
+ * \param update Zone update to clear.
+ */
+void zone_update_clear(zone_update_t *update);
+
+/*!
+ * \brief Adds an RRSet to the zone.
+ *
+ * \warning Do not edit the zone_update when any iterator is active. Any
+ * zone_update modifications will invalidate the trie iterators
+ * in the zone_update iterator(s).
+ *
+ * \param update Zone update.
+ * \param rrset RRSet to add.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Removes an RRSet from the zone.
+ *
+ * \warning Do not edit the zone_update when any iterator is active. Any
+ * zone_update modifications will invalidate the trie iterators
+ * in the zone_update iterator(s).
+ *
+ * \param update Zone update.
+ * \param rrset RRSet to remove.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Removes a whole RRSet of specified type from the zone.
+ *
+ * \warning Do not edit the zone_update when any iterator is active. Any
+ * zone_update modifications will invalidate the trie iterators
+ * in the zone_update iterator(s).
+ *
+ * \param update Zone update.
+ * \param owner Node name to remove.
+ * \param type RRSet type to remove.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_remove_rrset(zone_update_t *update, knot_dname_t *owner, uint16_t type);
+
+/*!
+ * \brief Removes a whole node from the zone.
+ *
+ * \warning Do not edit the zone_update when any iterator is active. Any
+ * zone_update modifications will invalidate the trie iterators
+ * in the zone_update iterator(s).
+ *
+ * \param update Zone update.
+ * \param owner Node name to remove.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_remove_node(zone_update_t *update, const knot_dname_t *owner);
+
+/*!
+ * \brief Adds and removes RRsets to/from the zone according to the changeset.
+ *
+ * \param update Zone update.
+ * \param changes Changes to be made in zone.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_apply_changeset(zone_update_t *update, const changeset_t *changes);
+
+/*!
+ * \brief Applies the changeset in reverse, rsets from REM section are added and from ADD section removed.
+ *
+ * \param update Zone update.
+ * \param changes Changes to be un-done.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes);
+
+/*!
+ * \brief Increment SOA serial (according to configured policy) in the update.
+ *
+ * \param update Update to be modified.
+ * \param conf Configuration.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_increment_soa(zone_update_t *update, conf_t *conf);
+
+/*!
+ * \brief Executes mandatory semantic checks on the zone contents.
+ *
+ * \param conf Configuration.
+ * \param update Update to be checked.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_semcheck(conf_t *conf, zone_update_t *update);
+
+/*!
+ * \brief If configured, verify ZONEMD and log the result.
+ *
+ * \param conf Configuration.
+ * \param update Zone update.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_verify_digest(conf_t *conf, zone_update_t *update);
+
+/*!
+ * \brief Commits all changes to the zone, signs it, saves changes to journal.
+ *
+ * \param conf Configuration.
+ * \param update Zone update.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_commit(conf_t *conf, zone_update_t *update);
+
+/*!
+ * \brief Returns bool whether there are any changes at all.
+ *
+ * \param update Zone update.
+ */
+bool zone_update_no_change(zone_update_t *update);
+
+/*!
+ * \brief Return whether apex DNSKEY, CDNSKEY, or CDS is updated.
+ */
+bool zone_update_changes_dnskey(zone_update_t *update);
diff --git a/src/knot/worker/pool.c b/src/knot/worker/pool.c
new file mode 100644
index 0000000..ff74970
--- /dev/null
+++ b/src/knot/worker/pool.c
@@ -0,0 +1,254 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/libknot.h"
+#include "knot/server/dthreads.h"
+#include "knot/worker/pool.h"
+
+/*!
+ * \brief Worker pool state.
+ */
+struct worker_pool {
+ dt_unit_t *threads;
+
+ pthread_mutex_t lock;
+ pthread_cond_t wake;
+
+ bool terminating; /*!< Is the pool terminating? .*/
+ bool suspended; /*!< Is execution temporarily suspended? .*/
+ int running; /*!< Number of running threads. */
+ worker_queue_t tasks;
+};
+
+/*!
+ * \brief Worker thread.
+ *
+ * The thread takes a task from the tasks queue and runs it, while checking
+ * if the dispatching of new tasks is allowed by the thread pool.
+ *
+ * An execution of a running thread cannot be enforced.
+ *
+ */
+static int worker_main(dthread_t *thread)
+{
+ assert(thread);
+
+ worker_pool_t *pool = thread->data;
+
+ pthread_mutex_lock(&pool->lock);
+
+ for (;;) {
+ if (pool->terminating) {
+ break;
+ }
+
+ worker_task_t *task = NULL;
+ if (!pool->suspended) {
+ task = worker_queue_dequeue(&pool->tasks);
+ }
+
+ if (task == NULL) {
+ pthread_cond_wait(&pool->wake, &pool->lock);
+ continue;
+ }
+
+ assert(task->run);
+ pool->running += 1;
+
+ pthread_mutex_unlock(&pool->lock);
+ task->run(task);
+ pthread_mutex_lock(&pool->lock);
+
+ pool->running -= 1;
+ pthread_cond_broadcast(&pool->wake);
+ }
+
+ pthread_mutex_unlock(&pool->lock);
+
+ return KNOT_EOK;
+}
+
+/* -- public API ------------------------------------------------------------ */
+
+worker_pool_t *worker_pool_create(unsigned threads)
+{
+ worker_pool_t *pool = malloc(sizeof(worker_pool_t));
+ if (pool == NULL) {
+ return NULL;
+ }
+
+ memset(pool, 0, sizeof(worker_pool_t));
+ pool->threads = dt_create(threads, worker_main, NULL, pool);
+ if (pool->threads == NULL) {
+ goto fail;
+ }
+
+ if (pthread_mutex_init(&pool->lock, NULL) != 0) {
+ goto fail;
+ }
+
+ if (pthread_cond_init(&pool->wake, NULL) != 0) {
+ goto fail;
+ }
+
+ worker_queue_init(&pool->tasks);
+
+ return pool;
+
+fail:
+ dt_delete(&pool->threads);
+ free(pool);
+ return NULL;
+}
+
+void worker_pool_destroy(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ dt_delete(&pool->threads);
+
+ pthread_mutex_destroy(&pool->lock);
+ pthread_cond_destroy(&pool->wake);
+
+ worker_queue_deinit(&pool->tasks);
+
+ free(pool);
+}
+
+void worker_pool_start(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ dt_start(pool->threads);
+}
+
+void worker_pool_stop(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ pool->terminating = true;
+ pthread_cond_broadcast(&pool->wake);
+ pthread_mutex_unlock(&pool->lock);
+
+ dt_stop(pool->threads);
+}
+
+void worker_pool_suspend(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ pool->suspended = true;
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void worker_pool_resume(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ pool->suspended = false;
+ pthread_cond_broadcast(&pool->wake);
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void worker_pool_join(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ dt_join(pool->threads);
+}
+
+void worker_pool_wait_cb(worker_pool_t *pool, wait_callback_t cb)
+{
+ if (!pool) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ while (!EMPTY_LIST(pool->tasks.list) || pool->running > 0) {
+ if (cb != NULL) {
+ cb(pool);
+ }
+ pthread_cond_wait(&pool->wake, &pool->lock);
+ }
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void worker_pool_wait(worker_pool_t *pool)
+{
+ worker_pool_wait_cb(pool, NULL);
+}
+
+void worker_pool_assign(worker_pool_t *pool, struct task *task)
+{
+ if (!pool || !task) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ worker_queue_enqueue(&pool->tasks, task);
+ pthread_cond_signal(&pool->wake);
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void worker_pool_clear(worker_pool_t *pool)
+{
+ if (!pool) {
+ return;
+ }
+
+ pthread_mutex_lock(&pool->lock);
+ worker_queue_deinit(&pool->tasks);
+ worker_queue_init(&pool->tasks);
+ pthread_mutex_unlock(&pool->lock);
+}
+
+void worker_pool_status(worker_pool_t *pool, bool locked, int *running, int *queued)
+{
+ if (!pool) {
+ *running = *queued = 0;
+ return;
+ }
+
+ if (!locked) {
+ pthread_mutex_lock(&pool->lock);
+ }
+ *running = pool->running;
+ *queued = worker_queue_length(&pool->tasks);
+ if (!locked) {
+ pthread_mutex_unlock(&pool->lock);
+ }
+}
diff --git a/src/knot/worker/pool.h b/src/knot/worker/pool.h
new file mode 100644
index 0000000..f843ea7
--- /dev/null
+++ b/src/knot/worker/pool.h
@@ -0,0 +1,93 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "knot/worker/queue.h"
+
+struct worker_pool;
+typedef struct worker_pool worker_pool_t;
+
+typedef void(*wait_callback_t)(worker_pool_t *);
+
+/*!
+ * \brief Initialize worker pool.
+ *
+ * \param threads Number of threads to be created.
+ *
+ * \return Thread pool or NULL in case of error.
+ */
+worker_pool_t *worker_pool_create(unsigned threads);
+
+/*!
+ * \brief Destroy the worker pool.
+ */
+void worker_pool_destroy(worker_pool_t *pool);
+
+/*!
+ * \brief Start all threads in the worker pool.
+ */
+void worker_pool_start(worker_pool_t *pool);
+
+/*!
+ * \brief Stop processing of new tasks, start stopping worker threads when possible.
+ */
+void worker_pool_stop(worker_pool_t *pool);
+
+/*!
+ * \brief Temporarily suspend the execution of worker pool.
+ */
+void worker_pool_suspend(worker_pool_t *pool);
+
+/*!
+ * \brief Resume the execution of worker pool.
+ */
+void worker_pool_resume(worker_pool_t *pool);
+
+/*!
+ * \brief Wait for all threads to terminate.
+ */
+void worker_pool_join(worker_pool_t *pool);
+
+/*!
+ * \brief Wait till the number of pending tasks is zero.
+ */
+void worker_pool_wait(worker_pool_t *pool);
+
+/*!
+ * \brief Wait till the number of pending tasks is zero. Callback emitted on
+ * thread wakeup can be specified.
+ */
+void worker_pool_wait_cb(worker_pool_t *pool, wait_callback_t cb);
+
+/*!
+ * \brief Assign a task to be performed by a worker in the pool.
+ */
+void worker_pool_assign(worker_pool_t *pool, struct task *task);
+
+/*!
+ * \brief Clear all tasks enqueued in pool processing queue.
+ */
+void worker_pool_clear(worker_pool_t *pool);
+
+/*!
+ * \brief Obtain info regarding how the pool is busy.
+ *
+ * \note Locked means if the mutex `pool->lock` is locked.
+ */
+void worker_pool_status(worker_pool_t *pool, bool locked, int *running, int *queued);
diff --git a/src/knot/worker/queue.c b/src/knot/worker/queue.c
new file mode 100644
index 0000000..d9fc2b6
--- /dev/null
+++ b/src/knot/worker/queue.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 2021 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 "knot/worker/queue.h"
+#include "contrib/mempattern.h"
+
+void worker_queue_init(worker_queue_t *queue)
+{
+ if (!queue) {
+ return;
+ }
+
+ memset(queue, 0, sizeof(worker_queue_t));
+
+ init_list(&queue->list);
+ mm_ctx_init(&queue->mm_ctx);
+}
+
+void worker_queue_deinit(worker_queue_t *queue)
+{
+ ptrlist_free(&queue->list, &queue->mm_ctx);
+}
+
+void worker_queue_enqueue(worker_queue_t *queue, worker_task_t *task)
+{
+ if (!queue || !task) {
+ return;
+ }
+
+ ptrlist_add(&queue->list, task, &queue->mm_ctx);
+}
+
+worker_task_t *worker_queue_dequeue(worker_queue_t *queue)
+{
+ if (!queue) {
+ return NULL;
+ }
+
+ worker_task_t *task = NULL;
+
+ if (!EMPTY_LIST(queue->list)) {
+ ptrnode_t *node = HEAD(queue->list);
+ task = (void *)node->d;
+ rem_node(&node->n);
+ queue->mm_ctx.free(&node->n);
+ }
+
+ return task;
+}
+
+size_t worker_queue_length(worker_queue_t *queue)
+{
+ return queue ? list_size(&queue->list) : 0;
+}
diff --git a/src/knot/worker/queue.h b/src/knot/worker/queue.h
new file mode 100644
index 0000000..0ade7ab
--- /dev/null
+++ b/src/knot/worker/queue.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "contrib/ucw/lists.h"
+
+struct task;
+typedef void (*task_cb)(struct task *);
+
+/*!
+ * \brief Task executable by a worker.
+ */
+typedef struct task {
+ void *ctx;
+ task_cb run;
+} worker_task_t;
+
+/*!
+ * \brief Worker queue.
+ */
+typedef struct worker_queue {
+ knot_mm_t mm_ctx;
+ list_t list;
+} worker_queue_t;
+
+/*!
+ * \brief Initialize worker queue.
+ */
+void worker_queue_init(worker_queue_t *queue);
+
+/*!
+ * \brief Deinitialize worker queue.
+ */
+void worker_queue_deinit(worker_queue_t *queue);
+
+/*!
+ * \brief Insert new item into the queue.
+ */
+void worker_queue_enqueue(worker_queue_t *queue, worker_task_t *task);
+
+/*!
+ * \brief Remove item from the queue.
+ *
+ * \return Task or NULL if the queue is empty.
+ */
+worker_task_t *worker_queue_dequeue(worker_queue_t *queue);
+
+/*!
+ * \brief Return number of tasks in worker queue.
+ */
+size_t worker_queue_length(worker_queue_t *queue);
diff --git a/src/knot/zone/adds_tree.c b/src/knot/zone/adds_tree.c
new file mode 100644
index 0000000..6376724
--- /dev/null
+++ b/src/knot/zone/adds_tree.c
@@ -0,0 +1,262 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdlib.h>
+
+#include "knot/zone/adds_tree.h"
+
+#include "libknot/dynarray.h"
+#include "libknot/error.h"
+#include "libknot/rrtype/rdname.h"
+
+knot_dynarray_declare(nodeptr, zone_node_t *, DYNARRAY_VISIBILITY_STATIC, 2)
+knot_dynarray_define(nodeptr, zone_node_t *, DYNARRAY_VISIBILITY_STATIC)
+
+typedef struct {
+ nodeptr_dynarray_t array;
+ bool deduplicated;
+} a_t_node_t;
+
+static int free_a_t_node(trie_val_t *val, void *null)
+{
+ assert(null == NULL);
+ a_t_node_t *nodes = *(a_t_node_t **)val;
+ nodeptr_dynarray_free(&nodes->array);
+ free(nodes);
+ return 0;
+}
+
+void additionals_tree_free(additionals_tree_t *a_t)
+{
+ if (a_t != NULL) {
+ trie_apply(a_t, free_a_t_node, NULL);
+ trie_free(a_t);
+ }
+}
+
+int zone_node_additionals_foreach(const zone_node_t *node, const knot_dname_t *zone_apex,
+ zone_node_additionals_cb_t cb, void *ctx)
+{
+ int ret = KNOT_EOK;
+ for (int i = 0; ret == KNOT_EOK && i < node->rrset_count; i++) {
+ struct rr_data *rr_data = &node->rrs[i];
+ if (!knot_rrtype_additional_needed(rr_data->type)) {
+ continue;
+ }
+ knot_rdata_t *rdata = knot_rdataset_at(&rr_data->rrs, 0);
+ for (int j = 0; ret == KNOT_EOK && j < rr_data->rrs.count; j++) {
+ const knot_dname_t *name = knot_rdata_name(rdata, rr_data->type);
+ if (knot_dname_in_bailiwick(name, zone_apex) > 0) {
+ ret = cb(name, ctx);
+ }
+ rdata = knot_rdataset_next(rdata);
+ }
+ }
+ return ret;
+}
+
+typedef struct {
+ additionals_tree_t *a_t;
+ zone_node_t *node;
+} a_t_node_ctx_t;
+
+static int remove_node_from_a_t(const knot_dname_t *name, void *a_ctx)
+{
+ a_t_node_ctx_t *ctx = a_ctx;
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_try(ctx->a_t, lf + 1, *lf);
+ if (val == NULL) {
+ return KNOT_EOK;
+ }
+
+ a_t_node_t *nodes = *(a_t_node_t **)val;
+ if (nodes == NULL) {
+ trie_del(ctx->a_t, lf + 1, *lf, NULL);
+ return KNOT_EOK;
+ }
+
+ nodeptr_dynarray_remove(&nodes->array, &ctx->node);
+
+ if (nodes->array.size == 0) {
+ nodeptr_dynarray_free(&nodes->array);
+ free(nodes);
+ trie_del(ctx->a_t, lf + 1, *lf, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int add_node_to_a_t(const knot_dname_t *name, void *a_ctx)
+{
+ a_t_node_ctx_t *ctx = a_ctx;
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_ins(ctx->a_t, lf + 1, *lf);
+ if (*val == NULL) {
+ *val = calloc(1, sizeof(a_t_node_t));
+ if (*val == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+
+ a_t_node_t *nodes = *(a_t_node_t **)val;
+ nodeptr_dynarray_add(&nodes->array, &ctx->node);
+ nodes->deduplicated = false;
+ return KNOT_EOK;
+}
+
+int additionals_tree_update_node(additionals_tree_t *a_t, const knot_dname_t *zone_apex,
+ zone_node_t *old_node, zone_node_t *new_node)
+{
+ a_t_node_ctx_t ctx = { a_t, 0 };
+ int ret = KNOT_EOK;
+
+ if (a_t == NULL || zone_apex == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (binode_additionals_unchanged(old_node, new_node)) {
+ return KNOT_EOK;
+ }
+
+ // for every additional in old_node rrsets, remove mentioning of this node in tree
+ if (old_node != NULL && !(old_node->flags & NODE_FLAGS_DELETED)) {
+ ctx.node = binode_first(old_node);
+ ret = zone_node_additionals_foreach(old_node, zone_apex, remove_node_from_a_t, &ctx);
+ }
+
+ // for every additional in new_node rrsets, add reverse link into the tree
+ if (new_node != NULL && !(new_node->flags & NODE_FLAGS_DELETED) && ret == KNOT_EOK) {
+ ctx.node = binode_first(new_node);
+ ret = zone_node_additionals_foreach(new_node, zone_apex, add_node_to_a_t, &ctx);
+ }
+ return ret;
+}
+
+int additionals_tree_update_nsec3(additionals_tree_t *a_t, const zone_contents_t *zone,
+ zone_node_t *old_node, zone_node_t *new_node)
+{
+ if (!knot_is_nsec3_enabled(zone)) {
+ return KNOT_EOK;
+ }
+ bool oldex = (old_node != NULL && !(old_node->flags & NODE_FLAGS_DELETED));
+ bool newex = (new_node != NULL && !(new_node->flags & NODE_FLAGS_DELETED));
+ bool addn = (!oldex && newex), remn = (oldex && !newex);
+ if (!addn && !remn) {
+ return KNOT_EOK;
+ }
+ const knot_dname_t *nsec3_name = node_nsec3_hash(addn ? new_node : old_node, zone);
+ if (nsec3_name == NULL) {
+ return KNOT_ENOMEM;
+ }
+ a_t_node_ctx_t ctx = { a_t, addn ? binode_first(new_node) : binode_first(old_node) };
+ return (addn ? add_node_to_a_t : remove_node_from_a_t)(nsec3_name, &ctx);
+}
+
+int additionals_tree_from_zone(additionals_tree_t **a_t, const zone_contents_t *zone)
+{
+ *a_t = additionals_tree_new();
+ if (*a_t == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ bool do_nsec3 = knot_is_nsec3_enabled(zone);
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin(zone->nodes, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ ret = additionals_tree_update_node(*a_t, zone->apex->owner, NULL, zone_tree_it_val(&it));
+ if (do_nsec3 && ret == KNOT_EOK) {
+ ret = additionals_tree_update_nsec3(*a_t, zone,
+ NULL, zone_tree_it_val(&it));
+ }
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+
+ if (ret != KNOT_EOK) {
+ additionals_tree_free(*a_t);
+ *a_t = NULL;
+ }
+ return ret;
+}
+
+int additionals_tree_update_from_binodes(additionals_tree_t *a_t, const zone_tree_t *tree,
+ const zone_contents_t *zone)
+{
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin((zone_tree_t *)tree, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ zone_node_t *node = zone_tree_it_val(&it), *counter = binode_counterpart(node);
+ ret = additionals_tree_update_node(a_t, zone->apex->owner, counter, node);
+ if (ret == KNOT_EOK) {
+ ret = additionals_tree_update_nsec3(a_t, zone, counter, node);
+ }
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
+
+int additionals_reverse_apply(additionals_tree_t *a_t, const knot_dname_t *name,
+ node_apply_cb_t cb, void *ctx)
+{
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(name, lf_storage);
+
+ trie_val_t *val = trie_get_try(a_t, lf + 1, *lf);
+ if (val == NULL) {
+ return KNOT_EOK;
+ }
+
+ a_t_node_t *nodes = *(a_t_node_t **)val;
+ if (nodes == NULL) {
+ return KNOT_EOK;
+ }
+
+ if (!nodes->deduplicated) {
+ nodeptr_dynarray_sort_dedup(&nodes->array);
+ nodes->deduplicated = true;
+ }
+
+ knot_dynarray_foreach(nodeptr, zone_node_t *, node_in_arr, nodes->array) {
+ int ret = cb(*node_in_arr, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int additionals_reverse_apply_multi(additionals_tree_t *a_t, const zone_tree_t *tree,
+ node_apply_cb_t cb, void *ctx)
+{
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_begin((zone_tree_t *)tree, &it);
+ while (!zone_tree_it_finished(&it) && ret == KNOT_EOK) {
+ ret = additionals_reverse_apply(a_t, zone_tree_it_val(&it)->owner, cb, ctx);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
diff --git a/src/knot/zone/adds_tree.h b/src/knot/zone/adds_tree.h
new file mode 100644
index 0000000..386d43b
--- /dev/null
+++ b/src/knot/zone/adds_tree.h
@@ -0,0 +1,120 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "contrib/qp-trie/trie.h"
+#include "knot/zone/contents.h"
+#include "knot/dnssec/zone-nsec.h"
+
+typedef trie_t additionals_tree_t;
+
+inline static additionals_tree_t *additionals_tree_new(void) { return trie_create(NULL); }
+void additionals_tree_free(additionals_tree_t *a_t);
+
+/*!
+ * \brief Foreach additional in all node RRSets, do sth.
+ *
+ * \note This is not too related to additionals_tree, might be moved.
+ *
+ * \param node Zone node with possibly NS, MX, etc rrsets.
+ * \param zone_apex Name of the zone apex.
+ * \param cb Callback to be performed.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+typedef int (*zone_node_additionals_cb_t)(const knot_dname_t *additional, void *ctx);
+int zone_node_additionals_foreach(const zone_node_t *node, const knot_dname_t *zone_apex,
+ zone_node_additionals_cb_t cb, void *ctx);
+
+/*!
+ * \brief Update additionals tree according to changed RRsets in a zone node.
+ *
+ * \param a_t Additionals tree to be updated.
+ * \param zone_apex Zone apex owner.
+ * \param old_node Old state of the node (additionals will be removed).
+ * \param new_node New state of the node (additionals will be added).
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_update_node(additionals_tree_t *a_t, const knot_dname_t *zone_apex,
+ zone_node_t *old_node, zone_node_t *new_node);
+
+/*!
+ * \brief Update additionals tree with NSEC3 according to changed normal nodes.
+ *
+ * \param a_t Additionals tree to be updated.
+ * \param zone Zone contents with NSEC3PARAMS etc.
+ * \param old_node Old state of the node.
+ * \param new_node New state of the node.
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_update_nsec3(additionals_tree_t *a_t, const zone_contents_t *zone,
+ zone_node_t *old_node, zone_node_t *new_node);
+
+/*!
+ * \brief Create additionals tree from a zone (by scanning all additionals in zone RRsets).
+ *
+ * \param a_t Out: additionals tree to be created (NULL if error).
+ * \param zone Zone contents.
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_from_zone(additionals_tree_t **a_t, const zone_contents_t *zone);
+
+/*!
+ * \brief Update additionals tree according to changed RRsets in all nodes in a zone tree.
+ *
+ * \param a_t Additionals tree to be updated.
+ * \param tree Zone tree containing updated nodes as bi-nodes.
+ * \param zone Whole zone with some additional info.
+ *
+ * \return KNOT_E*
+ */
+int additionals_tree_update_from_binodes(additionals_tree_t *a_t, const zone_tree_t *tree,
+ const zone_contents_t *zone);
+
+/*!
+ * \brief Foreach node that has specified name in its additionals, do sth.
+ *
+ * \note The node passed to the callback might not be correct part of bi-node!
+ *
+ * \param a_t Additionals reverse tree.
+ * \param name Name to be looked up in the additionals.
+ * \param cb Callback to be called.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+typedef int (*node_apply_cb_t)(zone_node_t *node, void *ctx);
+int additionals_reverse_apply(additionals_tree_t *a_t, const knot_dname_t *name,
+ node_apply_cb_t cb, void *ctx);
+
+/*!
+ * \brief Call additionals_reverse_apply() for every name in specified tree.
+ *
+ * \param a_t Additionals reverse tree.
+ * \param tree Zone tree with names to be looked up in additionals.
+ * \param cb Callback to be called for each affected node.
+ * \param ctx Arbitrary context for the callback.
+ *
+ * \return KNOT_E*
+ */
+int additionals_reverse_apply_multi(additionals_tree_t *a_t, const zone_tree_t *tree,
+ node_apply_cb_t cb, void *ctx);
+
diff --git a/src/knot/zone/adjust.c b/src/knot/zone/adjust.c
new file mode 100644
index 0000000..1e014a6
--- /dev/null
+++ b/src/knot/zone/adjust.c
@@ -0,0 +1,628 @@
+/* Copyright (C) 2021 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 "knot/zone/adjust.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/zone/adds_tree.h"
+#include "knot/zone/measure.h"
+#include "libdnssec/error.h"
+
+static bool node_non_dnssec_exists(const zone_node_t *node)
+{
+ assert(node);
+
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ switch (node->rrs[i].type) {
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_NSEC3:
+ case KNOT_RRTYPE_RRSIG:
+ continue;
+ default:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int adjust_cb_flags(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ zone_node_t *parent = node_parent(node);
+ uint16_t flags_orig = node->flags;
+ bool set_subt_auth = false;
+ bool has_data = node_non_dnssec_exists(node);
+
+ assert(!(node->flags & NODE_FLAGS_DELETED));
+
+ node->flags &= ~(NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH | NODE_FLAGS_SUBTREE_AUTH | NODE_FLAGS_SUBTREE_DATA);
+
+ if (parent && (parent->flags & NODE_FLAGS_DELEG || parent->flags & NODE_FLAGS_NONAUTH)) {
+ node->flags |= NODE_FLAGS_NONAUTH;
+ } else if (node_rrtype_exists(node, KNOT_RRTYPE_NS) && node != ctx->zone->apex) {
+ node->flags |= NODE_FLAGS_DELEG;
+ if (node_rrtype_exists(node, KNOT_RRTYPE_DS)) {
+ set_subt_auth = true;
+ }
+ } else if (has_data) {
+ set_subt_auth = true;
+ }
+
+ if (set_subt_auth) {
+ node_set_flag_hierarch(node, NODE_FLAGS_SUBTREE_AUTH);
+ }
+ if (has_data) {
+ node_set_flag_hierarch(node, NODE_FLAGS_SUBTREE_DATA);
+ }
+
+ if (node->flags != flags_orig && ctx->changed_nodes != NULL) {
+ return zone_tree_insert(ctx->changed_nodes, &node);
+ }
+
+ return KNOT_EOK;
+}
+
+int unadjust_cb_point_to_nsec3(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ // downgrade the NSEC3 node pointer to NSEC3 name
+ if (node->flags & NODE_FLAGS_NSEC3_NODE) {
+ node->nsec3_hash = knot_dname_copy(node->nsec3_node->owner, NULL);
+ node->flags &= ~NODE_FLAGS_NSEC3_NODE;
+ }
+ assert(ctx->changed_nodes == NULL);
+ return KNOT_EOK;
+}
+
+int adjust_cb_wildcard_nsec3(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ if (!knot_is_nsec3_enabled(ctx->zone)) {
+ if (node->nsec3_wildcard_name != NULL && ctx->changed_nodes != NULL) {
+ zone_tree_insert(ctx->changed_nodes, &node);
+ }
+ node->nsec3_wildcard_name = NULL;
+ return KNOT_EOK;
+ }
+
+ if (ctx->nsec3_param_changed) {
+ node->nsec3_wildcard_name = NULL;
+ }
+
+ if (node->nsec3_wildcard_name != NULL) {
+ return KNOT_EOK;
+ }
+
+ size_t wildcard_size = knot_dname_size(node->owner) + 2;
+ size_t wildcard_nsec3 = zone_nsec3_name_len(ctx->zone);
+ if (wildcard_size > KNOT_DNAME_MAXLEN) {
+ return KNOT_EOK;
+ }
+
+ node->nsec3_wildcard_name = malloc(wildcard_nsec3);
+ if (node->nsec3_wildcard_name == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (ctx->changed_nodes != NULL) {
+ zone_tree_insert(ctx->changed_nodes, &node);
+ }
+
+ knot_dname_t wildcard[wildcard_size];
+ assert(wildcard_size > 2);
+ memcpy(wildcard, "\x01""*", 2);
+ memcpy(wildcard + 2, node->owner, wildcard_size - 2);
+ return knot_create_nsec3_owner(node->nsec3_wildcard_name, wildcard_nsec3,
+ wildcard, ctx->zone->apex->owner, &ctx->zone->nsec3_params);
+}
+
+static bool nsec3_params_match(const knot_rdataset_t *rrs,
+ const dnssec_nsec3_params_t *params,
+ size_t rdata_pos)
+{
+ assert(rrs != NULL);
+ assert(params != NULL);
+
+ knot_rdata_t *rdata = knot_rdataset_at(rrs, rdata_pos);
+
+ return (knot_nsec3_alg(rdata) == params->algorithm
+ && knot_nsec3_iters(rdata) == params->iterations
+ && knot_nsec3_salt_len(rdata) == params->salt.size
+ && memcmp(knot_nsec3_salt(rdata), params->salt.data,
+ params->salt.size) == 0);
+}
+
+int adjust_cb_nsec3_flags(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ uint16_t flags_orig = node->flags;
+
+ // check if this node belongs to correct chain
+ node->flags &= ~NODE_FLAGS_IN_NSEC3_CHAIN;
+ const knot_rdataset_t *nsec3_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC3);
+ for (uint16_t i = 0; nsec3_rrs != NULL && i < nsec3_rrs->count; i++) {
+ if (nsec3_params_match(nsec3_rrs, &ctx->zone->nsec3_params, i)) {
+ node->flags |= NODE_FLAGS_IN_NSEC3_CHAIN;
+ }
+ }
+
+ if (node->flags != flags_orig && ctx->changed_nodes != NULL) {
+ return zone_tree_insert(ctx->changed_nodes, &node);
+ }
+
+ return KNOT_EOK;
+}
+
+int adjust_cb_nsec3_pointer(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ uint16_t flags_orig = node->flags;
+ zone_node_t *ptr_orig = node->nsec3_node;
+ int ret = KNOT_EOK;
+ if (ctx->nsec3_param_changed) {
+ if (!(node->flags & NODE_FLAGS_NSEC3_NODE) &&
+ node->nsec3_hash != binode_counterpart(node)->nsec3_hash) {
+ free(node->nsec3_hash);
+ }
+ node->nsec3_hash = NULL;
+ node->flags &= ~NODE_FLAGS_NSEC3_NODE;
+ (void)node_nsec3_node(node, ctx->zone);
+ } else {
+ ret = binode_fix_nsec3_pointer(node, ctx->zone);
+ }
+ if (ret == KNOT_EOK && ctx->changed_nodes != NULL &&
+ (flags_orig != node->flags || ptr_orig != node->nsec3_node)) {
+ ret = zone_tree_insert(ctx->changed_nodes, &node);
+ }
+ return ret;
+}
+
+/*! \brief Link pointers to additional nodes for this RRSet. */
+static int discover_additionals(zone_node_t *adjn, uint16_t rr_at,
+ adjust_ctx_t *ctx)
+{
+ struct rr_data *rr_data = &adjn->rrs[rr_at];
+ assert(rr_data != NULL);
+
+ const knot_rdataset_t *rrs = &rr_data->rrs;
+ knot_rdata_t *rdata = knot_rdataset_at(rrs, 0);
+ uint16_t rdcount = rrs->count;
+
+ uint16_t mandatory_count = 0;
+ uint16_t others_count = 0;
+ glue_t mandatory[rdcount];
+ glue_t others[rdcount];
+
+ /* Scan new additional nodes. */
+ for (uint16_t i = 0; i < rdcount; i++) {
+ const knot_dname_t *dname = knot_rdata_name(rdata, rr_data->type);
+ const zone_node_t *node = NULL;
+
+ if (!zone_contents_find_node_or_wildcard(ctx->zone, dname, &node)) {
+ rdata = knot_rdataset_next(rdata);
+ continue;
+ }
+
+ glue_t *glue;
+ if ((node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH)) &&
+ rr_data->type == KNOT_RRTYPE_NS &&
+ knot_dname_in_bailiwick(node->owner, adjn->owner) >= 0) {
+ glue = &mandatory[mandatory_count++];
+ glue->optional = false;
+ } else {
+ glue = &others[others_count++];
+ glue->optional = true;
+ }
+ glue->node = node;
+ glue->ns_pos = i;
+ rdata = knot_rdataset_next(rdata);
+ }
+
+ /* Store sorted additionals by the type, mandatory first. */
+ size_t total_count = mandatory_count + others_count;
+ additional_t *new_addit = NULL;
+ if (total_count > 0) {
+ new_addit = malloc(sizeof(additional_t));
+ if (new_addit == NULL) {
+ return KNOT_ENOMEM;
+ }
+ new_addit->count = total_count;
+
+ size_t size = total_count * sizeof(glue_t);
+ new_addit->glues = malloc(size);
+ if (new_addit->glues == NULL) {
+ free(new_addit);
+ return KNOT_ENOMEM;
+ }
+
+ size_t mandatory_size = mandatory_count * sizeof(glue_t);
+ memcpy(new_addit->glues, mandatory, mandatory_size);
+ memcpy(new_addit->glues + mandatory_count, others,
+ size - mandatory_size);
+ }
+
+ /* If the result differs, shallow copy node and store additionals. */
+ if (!additional_equal(rr_data->additional, new_addit)) {
+ if (ctx->changed_nodes != NULL) {
+ zone_tree_insert(ctx->changed_nodes, &adjn);
+ }
+
+ if (!binode_additional_shared(adjn, adjn->rrs[rr_at].type)) {
+ // this happens when additionals are adjusted twice during one update, e.g. IXFR-from-diff
+ additional_clear(adjn->rrs[rr_at].additional);
+ }
+
+ int ret = binode_prepare_change(adjn, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ rr_data = &adjn->rrs[rr_at];
+
+ rr_data->additional = new_addit;
+ } else {
+ additional_clear(new_addit);
+ }
+
+ return KNOT_EOK;
+}
+
+int adjust_cb_additionals(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ /* Lookup additional records for specific nodes. */
+ for(uint16_t i = 0; i < node->rrset_count; ++i) {
+ struct rr_data *rr_data = &node->rrs[i];
+ if (knot_rrtype_additional_needed(rr_data->type)) {
+ int ret = discover_additionals(node, i, ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+int adjust_cb_flags_and_nsec3(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ int ret = adjust_cb_flags(node, ctx);
+ if (ret == KNOT_EOK) {
+ ret = adjust_cb_nsec3_pointer(node, ctx);
+ }
+ return ret;
+}
+
+int adjust_cb_nsec3_and_additionals(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ int ret = adjust_cb_nsec3_pointer(node, ctx);
+ if (ret == KNOT_EOK) {
+ ret = adjust_cb_wildcard_nsec3(node, ctx);
+ }
+ if (ret == KNOT_EOK) {
+ ret = adjust_cb_additionals(node, ctx);
+ }
+ return ret;
+}
+
+int adjust_cb_nsec3_and_wildcard(zone_node_t *node, adjust_ctx_t *ctx)
+{
+ int ret = adjust_cb_wildcard_nsec3(node, ctx);
+ if (ret == KNOT_EOK) {
+ ret = adjust_cb_nsec3_pointer(node, ctx);
+ }
+ return ret;
+}
+
+int adjust_cb_void(_unused_ zone_node_t *node, _unused_ adjust_ctx_t *ctx)
+{
+ return KNOT_EOK;
+}
+
+typedef struct {
+ zone_node_t *first_node;
+ adjust_ctx_t ctx;
+ zone_node_t *previous_node;
+ adjust_cb_t adjust_cb;
+ bool adjust_prevs;
+ measure_t *m;
+
+ // just for parallel
+ unsigned threads;
+ unsigned thr_id;
+ size_t i;
+ pthread_t thread;
+ int ret;
+ zone_tree_t *tree;
+} zone_adjust_arg_t;
+
+static int adjust_single(zone_node_t *node, void *data)
+{
+ assert(node != NULL);
+ assert(data != NULL);
+
+ zone_adjust_arg_t *args = (zone_adjust_arg_t *)data;
+
+ // parallel adjust support
+ if (args->threads > 1) {
+ if (args->i++ % args->threads != args->thr_id) {
+ return KNOT_EOK;
+ }
+ }
+
+ if (args->m != NULL) {
+ knot_measure_node(node, args->m);
+ }
+
+ if ((node->flags & NODE_FLAGS_DELETED)) {
+ return KNOT_EOK;
+ }
+
+ // remember first node
+ if (args->first_node == NULL) {
+ args->first_node = node;
+ }
+
+ // set pointer to previous node
+ if (args->adjust_prevs && args->previous_node != NULL &&
+ node->prev != args->previous_node &&
+ node->prev != binode_counterpart(args->previous_node)) {
+ zone_tree_insert(args->ctx.changed_nodes, &node);
+ node->prev = args->previous_node;
+ }
+
+ // update remembered previous pointer only if authoritative
+ if (!(node->flags & NODE_FLAGS_NONAUTH) && node->rrset_count > 0) {
+ args->previous_node = node;
+ }
+
+ return args->adjust_cb(node, &args->ctx);
+}
+
+static int zone_adjust_tree(zone_tree_t *tree, adjust_ctx_t *ctx, adjust_cb_t adjust_cb,
+ bool adjust_prevs, measure_t *measure_ctx)
+{
+ if (zone_tree_is_empty(tree)) {
+ return KNOT_EOK;
+ }
+
+ zone_adjust_arg_t arg = { 0 };
+ arg.ctx = *ctx;
+ arg.adjust_cb = adjust_cb;
+ arg.adjust_prevs = adjust_prevs;
+ arg.m = measure_ctx;
+
+ int ret = zone_tree_apply(tree, adjust_single, &arg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (adjust_prevs && arg.first_node != NULL) {
+ zone_tree_insert(ctx->changed_nodes, &arg.first_node);
+ arg.first_node->prev = arg.previous_node;
+ }
+
+ return KNOT_EOK;
+}
+
+static void *adjust_tree_thread(void *ctx)
+{
+ zone_adjust_arg_t *arg = ctx;
+
+ arg->ret = zone_tree_apply(arg->tree, adjust_single, ctx);
+
+ return NULL;
+}
+
+static int zone_adjust_tree_parallel(zone_tree_t *tree, adjust_ctx_t *ctx,
+ adjust_cb_t adjust_cb, unsigned threads)
+{
+ if (zone_tree_is_empty(tree)) {
+ return KNOT_EOK;
+ }
+
+ zone_adjust_arg_t args[threads];
+ memset(args, 0, sizeof(args));
+ int ret = KNOT_EOK;
+
+ for (unsigned i = 0; i < threads; i++) {
+ args[i].first_node = NULL;
+ args[i].ctx = *ctx;
+ args[i].adjust_cb = adjust_cb;
+ args[i].adjust_prevs = false;
+ args[i].m = NULL;
+ args[i].tree = tree;
+ args[i].threads = threads;
+ args[i].i = 0;
+ args[i].thr_id = i;
+ args[i].ret = -1;
+ if (ctx->changed_nodes != NULL) {
+ args[i].ctx.changed_nodes = zone_tree_create(true);
+ if (args[i].ctx.changed_nodes == NULL) {
+ ret = KNOT_ENOMEM;
+ break;
+ }
+ args[i].ctx.changed_nodes->flags = tree->flags;
+ }
+ }
+ if (ret != KNOT_EOK) {
+ for (unsigned i = 0; i < threads; i++) {
+ zone_tree_free(&args[i].ctx.changed_nodes);
+ }
+ return ret;
+ }
+
+ for (unsigned i = 0; i < threads; i++) {
+ args[i].ret = pthread_create(&args[i].thread, NULL, adjust_tree_thread, &args[i]);
+ }
+
+ for (unsigned i = 0; i < threads; i++) {
+ if (args[i].ret == 0) {
+ args[i].ret = pthread_join(args[i].thread, NULL);
+ }
+ if (args[i].ret != 0) {
+ ret = knot_map_errno_code(args[i].ret);
+ }
+ if (ret == KNOT_EOK && ctx->changed_nodes != NULL) {
+ ret = zone_tree_merge(ctx->changed_nodes, args[i].ctx.changed_nodes);
+ }
+ zone_tree_free(&args[i].ctx.changed_nodes);
+ }
+
+ return ret;
+}
+
+int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb,
+ bool measure_zone, bool adjust_prevs, unsigned threads,
+ zone_tree_t *add_changed)
+{
+ int ret = zone_contents_load_nsec3param(zone);
+ if (ret != KNOT_EOK) {
+ log_zone_error(zone->apex->owner,
+ "failed to load NSEC3 parameters (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+ zone->dnssec = node_rrtype_is_signed(zone->apex, KNOT_RRTYPE_SOA);
+
+ measure_t m = knot_measure_init(measure_zone, false);
+ adjust_ctx_t ctx = { zone, add_changed, true };
+
+ if (threads > 1) {
+ assert(nodes_cb != adjust_cb_flags); // This cb demands parent to be adjusted before child
+ // => required sequential adjusting (also true for
+ // adjust_cb_flags_and_nsec3) !!
+ assert(!measure_zone);
+ assert(!adjust_prevs);
+ if (nsec3_cb != NULL) {
+ ret = zone_adjust_tree_parallel(zone->nsec3_nodes, &ctx, nsec3_cb, threads);
+ }
+ if (ret == KNOT_EOK && nodes_cb != NULL) {
+ ret = zone_adjust_tree_parallel(zone->nodes, &ctx, nodes_cb, threads);
+ }
+ } else {
+ if (nsec3_cb != NULL) {
+ ret = zone_adjust_tree(zone->nsec3_nodes, &ctx, nsec3_cb, adjust_prevs, &m);
+ }
+ if (ret == KNOT_EOK && nodes_cb != NULL) {
+ ret = zone_adjust_tree(zone->nodes, &ctx, nodes_cb, adjust_prevs, &m);
+ }
+ }
+
+ if (ret == KNOT_EOK && measure_zone && nodes_cb != NULL && nsec3_cb != NULL) {
+ knot_measure_finish_zone(&m, zone);
+ }
+ return ret;
+}
+
+int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_diff)
+{
+ int ret = KNOT_EOK;
+ measure_t m = knot_measure_init(false, measure_diff);
+ adjust_ctx_t ctx = { update->new_cont, update->a_ctx->adjust_ptrs, zone_update_changed_nsec3param(update) };
+
+ if (nsec3_cb != NULL) {
+ ret = zone_adjust_tree(update->a_ctx->nsec3_ptrs, &ctx, nsec3_cb, false, &m);
+ }
+ if (ret == KNOT_EOK && nodes_cb != NULL) {
+ ret = zone_adjust_tree(update->a_ctx->node_ptrs, &ctx, nodes_cb, false, &m);
+ }
+ if (ret == KNOT_EOK && measure_diff && nodes_cb != NULL && nsec3_cb != NULL) {
+ knot_measure_finish_update(&m, update);
+ }
+ return ret;
+}
+
+int zone_adjust_full(zone_contents_t *zone, unsigned threads)
+{
+ int ret = zone_adjust_contents(zone, adjust_cb_flags, adjust_cb_nsec3_flags,
+ true, true, 1, NULL);
+ if (ret == KNOT_EOK) {
+ ret = zone_adjust_contents(zone, adjust_cb_nsec3_and_additionals, NULL,
+ false, false, threads, NULL);
+ }
+ if (ret == KNOT_EOK) {
+ additionals_tree_free(zone->adds_tree);
+ ret = additionals_tree_from_zone(&zone->adds_tree, zone);
+ }
+ return ret;
+}
+
+static int adjust_additionals_cb(zone_node_t *node, void *ctx)
+{
+ adjust_ctx_t *actx = ctx;
+ zone_node_t *real_node = zone_tree_fix_get(node, actx->zone->nodes);
+ return adjust_cb_additionals(real_node, actx);
+}
+
+static int adjust_point_to_nsec3_cb(zone_node_t *node, void *ctx)
+{
+ adjust_ctx_t *actx = ctx;
+ zone_node_t *real_node = zone_tree_fix_get(node, actx->zone->nodes);
+ return adjust_cb_nsec3_pointer(real_node, actx);
+}
+
+int zone_adjust_incremental_update(zone_update_t *update, unsigned threads)
+{
+ int ret = zone_contents_load_nsec3param(update->new_cont);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ bool nsec3change = zone_update_changed_nsec3param(update);
+ adjust_ctx_t ctx = { update->new_cont, update->a_ctx->adjust_ptrs, nsec3change };
+
+ ret = zone_adjust_contents(update->new_cont, adjust_cb_flags, adjust_cb_nsec3_flags,
+ false, true, 1, update->a_ctx->adjust_ptrs);
+ if (ret == KNOT_EOK) {
+ if (nsec3change) {
+ ret = zone_adjust_contents(update->new_cont, adjust_cb_nsec3_and_wildcard, NULL,
+ false, false, threads, update->a_ctx->adjust_ptrs);
+ if (ret == KNOT_EOK) {
+ // just measure zone size
+ ret = zone_adjust_update(update, adjust_cb_void, adjust_cb_void, true);
+ }
+ } else {
+ ret = zone_adjust_update(update, adjust_cb_wildcard_nsec3, adjust_cb_void, true);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ if (update->new_cont->adds_tree != NULL && !nsec3change) {
+ ret = additionals_tree_update_from_binodes(
+ update->new_cont->adds_tree,
+ update->a_ctx->node_ptrs,
+ update->new_cont
+ );
+ } else {
+ additionals_tree_free(update->new_cont->adds_tree);
+ ret = additionals_tree_from_zone(&update->new_cont->adds_tree, update->new_cont);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ ret = additionals_reverse_apply_multi(
+ update->new_cont->adds_tree,
+ update->a_ctx->node_ptrs,
+ adjust_additionals_cb,
+ &ctx
+ );
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_adjust_update(update, adjust_cb_additionals, adjust_cb_void, false);
+ }
+ if (ret == KNOT_EOK) {
+ if (!nsec3change) {
+ ret = additionals_reverse_apply_multi(
+ update->new_cont->adds_tree,
+ update->a_ctx->nsec3_ptrs,
+ adjust_point_to_nsec3_cb,
+ &ctx
+ );
+ }
+ }
+ return ret;
+}
diff --git a/src/knot/zone/adjust.h b/src/knot/zone/adjust.h
new file mode 100644
index 0000000..5828e5a
--- /dev/null
+++ b/src/knot/zone/adjust.h
@@ -0,0 +1,123 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/contents.h"
+#include "knot/updates/zone-update.h"
+
+typedef struct {
+ const zone_contents_t *zone;
+ zone_tree_t *changed_nodes;
+ bool nsec3_param_changed;
+} adjust_ctx_t;
+
+typedef int (*adjust_cb_t)(zone_node_t *, adjust_ctx_t *);
+
+/*
+ * \brief Various callbacks for adjusting zone node's params and pointers.
+ *
+ * \param node Node to be adjusted. Must be already inside the zone contents!
+ * \param zone Zone being adjusted.
+ *
+ * \return KNOT_E*
+ */
+
+// fix NORMAL node flags, like NODE_FLAGS_NONAUTH, NODE_FLAGS_DELEG etc.
+int adjust_cb_flags(zone_node_t *node, adjust_ctx_t *ctx);
+
+// reset pointer to NSEC3 node
+int unadjust_cb_point_to_nsec3(zone_node_t *node, adjust_ctx_t *ctx);
+
+// fix NORMAL node pointer to NSEC3 node proving nonexistence of wildcard
+int adjust_cb_wildcard_nsec3(zone_node_t *node, adjust_ctx_t *ctx);
+
+// fix NSEC3 node flags: NODE_FLAGS_IN_NSEC3_CHAIN
+int adjust_cb_nsec3_flags(zone_node_t *node, adjust_ctx_t *ctx);
+
+// fix pointer at corresponding NSEC3 node
+int adjust_cb_nsec3_pointer(zone_node_t *node, adjust_ctx_t *ctx);
+
+// fix NORMAL node flags to additionals, like NS records and glue...
+int adjust_cb_additionals(zone_node_t *node, adjust_ctx_t *ctx);
+
+// adjust_cb_flags and adjust_cb_nsec3_pointer at once
+int adjust_cb_flags_and_nsec3(zone_node_t *node, adjust_ctx_t *ctx);
+
+// adjust_cb_nsec3_pointer, adjust_cb_wildcard_nsec3 and adjust_cb_additionals at once
+int adjust_cb_nsec3_and_additionals(zone_node_t *node, adjust_ctx_t *ctx);
+
+// adjust_cb_wildcard_nsec3 and adjust_cb_nsec3_pointer at once
+int adjust_cb_nsec3_and_wildcard(zone_node_t *node, adjust_ctx_t *ctx);
+
+// dummy callback, just make prev pointers adjusting and zone size measuring work
+int adjust_cb_void(zone_node_t *node, adjust_ctx_t *ctx);
+
+/*!
+ * \brief Apply callback to NSEC3 and NORMAL nodes. Fix PREV pointers and measure zone size.
+ *
+ * \param zone Zone to be adjusted.
+ * \param nodes_cb Callback for NORMAL nodes.
+ * \param nsec3_cb Callback for NSEC3 nodes.
+ * \param measure_zone While adjusting, count the size and max TTL of the zone.
+ * \param adjust_prevs Also (re-)generate node->prev pointers.
+ * \param threads Operate in parallel using specified threads.
+ * \param add_changed Special tree to add any changed node (by adjusting) into.
+ *
+ * \return KNOT_E*
+ */
+int zone_adjust_contents(zone_contents_t *zone, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb,
+ bool measure_zone, bool adjust_prevs, unsigned threads,
+ zone_tree_t *add_changed);
+
+/*!
+ * \brief Apply callback to nodes affected by the zone update.
+ *
+ * \note Fixing PREV pointers and zone measurement does not make sense since we are not
+ * iterating over whole zone. The same applies for callback that reference other
+ * (unchanged, but indirectly affected) zone nodes.
+ *
+ * \param update Zone update being finalized.
+ * \param nodes_cb Callback for NORMAL nodes.
+ * \param nsec3_cb Callback for NSEC3 nodes.
+ * \param measure_diff While adjusting, count the size difference and max TTL change.
+ *
+ * \return KNOT_E*
+ */
+int zone_adjust_update(zone_update_t *update, adjust_cb_t nodes_cb, adjust_cb_t nsec3_cb, bool measure_diff);
+
+/*!
+ * \brief Do a general-purpose full update.
+ *
+ * This operates in two phases, first fix basic node flags and prev pointers,
+ * than nsec3-related pointers and additionals.
+ *
+ * \param zone Zone to be adjusted.
+ * \param threads Parallelize some adjusting using specified threads.
+ *
+ * \return KNOT_E*
+ */
+int zone_adjust_full(zone_contents_t *zone, unsigned threads);
+
+/*!
+ * \brief Do a generally approved adjust after incremental update.
+ *
+ * \param update Zone update to be adjusted incrementally.
+ * \param threads Parallelize some adjusting using specified threads.
+ *
+ * \return KNOT_E*
+ */
+int zone_adjust_incremental_update(zone_update_t *update, unsigned threads);
diff --git a/src/knot/zone/backup.c b/src/knot/zone/backup.c
new file mode 100644
index 0000000..704f2e2
--- /dev/null
+++ b/src/knot/zone/backup.c
@@ -0,0 +1,461 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "knot/zone/backup.h"
+
+#include "contrib/files.h"
+#include "contrib/getline.h"
+#include "contrib/macros.h"
+#include "contrib/string.h"
+#include "knot/catalog/catalog_db.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/kasp/kasp_zone.h"
+#include "knot/dnssec/kasp/keystore.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/zone/backup_dir.h"
+#include "knot/zone/zonefile.h"
+#include "libdnssec/error.h"
+
+// Current backup format version for output. Don't decrease it.
+#define BACKUP_VERSION BACKUP_FORMAT_2 // Starting with release 3.1.0.
+
+static void _backup_swap(zone_backup_ctx_t *ctx, void **local, void **remote)
+{
+ if (ctx->restore_mode) {
+ void *temp = *local;
+ *local = *remote;
+ *remote = temp;
+ }
+}
+
+#define BACKUP_SWAP(ctx, from, to) _backup_swap((ctx), (void **)&(from), (void **)&(to))
+
+int zone_backup_init(bool restore_mode, bool forced, const char *backup_dir,
+ size_t kasp_db_size, size_t timer_db_size, size_t journal_db_size,
+ size_t catalog_db_size, zone_backup_ctx_t **out_ctx)
+{
+ if (backup_dir == NULL || out_ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t backup_dir_len = strlen(backup_dir) + 1;
+
+ zone_backup_ctx_t *ctx = malloc(sizeof(*ctx) + backup_dir_len);
+ if (ctx == NULL) {
+ return KNOT_ENOMEM;
+ }
+ ctx->restore_mode = restore_mode;
+ ctx->forced = forced;
+ ctx->backup_format = BACKUP_VERSION;
+ ctx->backup_global = false;
+ ctx->readers = 1;
+ ctx->failed = false;
+ ctx->init_time = time(NULL);
+ ctx->zone_count = 0;
+ ctx->backup_dir = (char *)(ctx + 1);
+ memcpy(ctx->backup_dir, backup_dir, backup_dir_len);
+
+ // Backup directory, lock file, label file.
+ // In restore, set the backup format.
+ int ret = backupdir_init(ctx);
+ if (ret != KNOT_EOK) {
+ free(ctx);
+ return ret;
+ }
+
+ pthread_mutex_init(&ctx->readers_mutex, NULL);
+
+ char db_dir[backup_dir_len + 16];
+ (void)snprintf(db_dir, sizeof(db_dir), "%s/keys", backup_dir);
+ knot_lmdb_init(&ctx->bck_kasp_db, db_dir, kasp_db_size, 0, "keys_db");
+
+ (void)snprintf(db_dir, sizeof(db_dir), "%s/timers", backup_dir);
+ knot_lmdb_init(&ctx->bck_timer_db, db_dir, timer_db_size, 0, NULL);
+
+ (void)snprintf(db_dir, sizeof(db_dir), "%s/journal", backup_dir);
+ knot_lmdb_init(&ctx->bck_journal, db_dir, journal_db_size, 0, NULL);
+
+ (void)snprintf(db_dir, sizeof(db_dir), "%s/catalog", backup_dir);
+ knot_lmdb_init(&ctx->bck_catalog, db_dir, catalog_db_size, 0, NULL);
+
+ *out_ctx = ctx;
+ return KNOT_EOK;
+}
+
+int zone_backup_deinit(zone_backup_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ int ret = KNOT_EOK;
+
+ pthread_mutex_lock(&ctx->readers_mutex);
+ assert(ctx->readers > 0);
+ size_t left = --ctx->readers;
+ pthread_mutex_unlock(&ctx->readers_mutex);
+
+ if (left == 0) {
+ knot_lmdb_deinit(&ctx->bck_catalog);
+ knot_lmdb_deinit(&ctx->bck_journal);
+ knot_lmdb_deinit(&ctx->bck_timer_db);
+ knot_lmdb_deinit(&ctx->bck_kasp_db);
+ pthread_mutex_destroy(&ctx->readers_mutex);
+
+ ret = backupdir_deinit(ctx);
+ zone_backups_rem(ctx);
+ free(ctx);
+ }
+
+ return ret;
+}
+
+void zone_backups_init(zone_backup_ctxs_t *ctxs)
+{
+ init_list(&ctxs->ctxs);
+ pthread_mutex_init(&ctxs->mutex, NULL);
+}
+
+void zone_backups_deinit(zone_backup_ctxs_t *ctxs)
+{
+ zone_backup_ctx_t *ctx, *nxt;
+ WALK_LIST_DELSAFE(ctx, nxt, ctxs->ctxs) {
+ log_warning("backup to '%s' in progress, terminating, will be incomplete",
+ ctx->backup_dir);
+ ctx->readers = 1; // ensure full deinit
+ ctx->failed = true;
+ (void)zone_backup_deinit(ctx);
+ }
+ pthread_mutex_destroy(&ctxs->mutex);
+}
+
+void zone_backups_add(zone_backup_ctxs_t *ctxs, zone_backup_ctx_t *ctx)
+{
+ pthread_mutex_lock(&ctxs->mutex);
+ add_tail(&ctxs->ctxs, (node_t *)ctx);
+ pthread_mutex_unlock(&ctxs->mutex);
+}
+
+static zone_backup_ctxs_t *get_ctxs_trick(zone_backup_ctx_t *ctx)
+{
+ node_t *n = (node_t *)ctx;
+ while (n->prev != NULL) {
+ n = n->prev;
+ }
+ return (zone_backup_ctxs_t *)n;
+}
+
+void zone_backups_rem(zone_backup_ctx_t *ctx)
+{
+ zone_backup_ctxs_t *ctxs = get_ctxs_trick(ctx);
+ pthread_mutex_lock(&ctxs->mutex);
+ rem_node((node_t *)ctx);
+ pthread_mutex_unlock(&ctxs->mutex);
+}
+
+static char *dir_file(const char *dir_name, const char *file_name)
+{
+ const char *basename = strrchr(file_name, '/');
+ if (basename == NULL) {
+ basename = file_name;
+ } else {
+ basename++;
+ }
+
+ return sprintf_alloc("%s/%s", dir_name, basename);
+}
+
+static int backup_key(key_params_t *parm, dnssec_keystore_t *from, dnssec_keystore_t *to)
+{
+ dnssec_key_t *key = NULL;
+ int ret = dnssec_key_new(&key);
+ if (ret != DNSSEC_EOK) {
+ return knot_error_from_libdnssec(ret);
+ }
+ dnssec_key_set_algorithm(key, parm->algorithm);
+
+ ret = dnssec_keystore_get_private(from, parm->id, key);
+ if (ret == DNSSEC_EOK) {
+ ret = dnssec_keystore_set_private(to, key);
+ }
+
+ dnssec_key_free(key);
+ return knot_error_from_libdnssec(ret);
+}
+
+static conf_val_t get_zone_policy(conf_t *conf, const knot_dname_t *zone)
+{
+ conf_val_t policy;
+
+ // Global modules don't use DNSSEC policy so check zone modules only.
+ conf_val_t modules = conf_zone_get(conf, C_MODULE, zone);
+ while (modules.code == KNOT_EOK) {
+ conf_mod_id_t *mod_id = conf_mod_id(&modules);
+ if (mod_id != NULL && strcmp(mod_id->name + 1, "mod-onlinesign") == 0) {
+ policy = conf_mod_get(conf, C_POLICY, mod_id);
+ conf_id_fix_default(&policy);
+ conf_free_mod_id(mod_id);
+ return policy;
+ }
+ conf_free_mod_id(mod_id);
+ conf_val_next(&modules);
+ }
+
+ // Use default policy if none is configured.
+ policy = conf_zone_get(conf, C_DNSSEC_POLICY, zone);
+ conf_id_fix_default(&policy);
+ return policy;
+}
+
+#define LOG_FAIL(action) log_zone_warning(zone->name, "%s, %s failed (%s)", ctx->restore_mode ? \
+ "restore" : "backup", (action), knot_strerror(ret))
+#define LOG_MARK_FAIL(action) LOG_FAIL(action); \
+ ctx->failed = true
+
+#define ABORT_IF_ENOMEM(param) if (param == NULL) { \
+ ret = KNOT_ENOMEM; \
+ goto done; \
+ }
+
+static int backup_zonefile(conf_t *conf, zone_t *zone, zone_backup_ctx_t *ctx)
+{
+ int ret = KNOT_EOK;
+
+ char *local_zf = conf_zonefile(conf, zone->name);
+ char *backup_zfiles_dir = NULL, *backup_zf = NULL, *zone_name_str;
+
+ switch (ctx->backup_format) {
+ case BACKUP_FORMAT_1:
+ backup_zf = dir_file(ctx->backup_dir, local_zf);
+ ABORT_IF_ENOMEM(backup_zf);
+ break;
+ case BACKUP_FORMAT_2:
+ default:
+ backup_zfiles_dir = dir_file(ctx->backup_dir, "zonefiles");
+ ABORT_IF_ENOMEM(backup_zfiles_dir);
+ zone_name_str = knot_dname_to_str_alloc(zone->name);
+ ABORT_IF_ENOMEM(zone_name_str);
+ backup_zf = sprintf_alloc("%s/%szone", backup_zfiles_dir, zone_name_str);
+ free(zone_name_str);
+ ABORT_IF_ENOMEM(backup_zf);
+ }
+
+ if (ctx->restore_mode) {
+ struct stat st;
+ if (stat(backup_zf, &st) == 0) {
+ ret = make_path(local_zf, S_IRWXU | S_IRWXG);
+ if (ret == KNOT_EOK) {
+ ret = copy_file(local_zf, backup_zf);
+ }
+ } else {
+ ret = errno == ENOENT ? KNOT_EFILE : knot_map_errno();
+ /* If there's no zone file in the backup, remove any old zone file
+ * from the repository.
+ */
+ if (ret == KNOT_EFILE) {
+ unlink(local_zf);
+ }
+ }
+ } else {
+ conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name);
+ bool can_flush = (conf_int(&val) > -1);
+
+ // The value of ctx->backup_format is always at least BACKUP_FORMAT_2 for
+ // the backup mode, therefore backup_zfiles_dir is always filled at this point.
+ assert(backup_zfiles_dir != NULL);
+
+ ret = make_dir(backup_zfiles_dir, S_IRWXU | S_IRWXG, true);
+ if (ret == KNOT_EOK) {
+ if (can_flush) {
+ if (zone->contents != NULL) {
+ ret = zonefile_write(backup_zf, zone->contents);
+ } else {
+ log_zone_notice(zone->name,
+ "empty zone, skipping a zone file backup");
+ }
+ } else {
+ ret = copy_file(backup_zf, local_zf);
+ }
+ }
+ }
+
+done:
+ free(backup_zf);
+ free(backup_zfiles_dir);
+ free(local_zf);
+ if (ret == KNOT_EFILE) {
+ log_zone_notice(zone->name, "no zone file, skipping a zone file %s",
+ ctx->restore_mode ? "restore" : "backup");
+ ret = KNOT_EOK;
+ }
+
+ return ret;
+}
+
+static int backup_keystore(conf_t *conf, zone_t *zone, zone_backup_ctx_t *ctx)
+{
+ dnssec_keystore_t *from = NULL, *to = NULL;
+
+ conf_val_t policy_id = get_zone_policy(conf, zone->name);
+
+ unsigned backend_type = 0;
+ int ret = zone_init_keystore(conf, &policy_id, &from, &backend_type, NULL);
+ if (ret != KNOT_EOK) {
+ LOG_FAIL("keystore init");
+ return ret;
+ }
+ if (backend_type == KEYSTORE_BACKEND_PKCS11) {
+ log_zone_warning(zone->name, "private keys from PKCS#11 aren't subject of backup/restore");
+ (void)dnssec_keystore_deinit(from);
+ return KNOT_EOK;
+ }
+
+ char kasp_dir[strlen(ctx->backup_dir) + 6];
+ (void)snprintf(kasp_dir, sizeof(kasp_dir), "%s/keys", ctx->backup_dir);
+ ret = keystore_load("keys", KEYSTORE_BACKEND_PEM, kasp_dir, &to);
+ if (ret != KNOT_EOK) {
+ LOG_FAIL("keystore load");
+ goto done;
+ }
+
+ BACKUP_SWAP(ctx, from, to);
+
+ list_t key_params;
+ init_list(&key_params);
+ ret = kasp_db_list_keys(zone_kaspdb(zone), zone->name, &key_params);
+ ret = (ret == KNOT_ENOENT ? KNOT_EOK : ret);
+ if (ret != KNOT_EOK) {
+ LOG_FAIL("keystore list");
+ goto done;
+ }
+ ptrnode_t *n;
+ WALK_LIST(n, key_params) {
+ key_params_t *parm = n->d;
+ if (ret == KNOT_EOK && !parm->is_pub_only) {
+ ret = backup_key(parm, from, to);
+ }
+ free_key_params(parm);
+ }
+ if (ret != KNOT_EOK) {
+ LOG_FAIL("key copy");
+ }
+ ptrlist_deep_free(&key_params, NULL);
+
+done:
+ (void)dnssec_keystore_deinit(to);
+ (void)dnssec_keystore_deinit(from);
+ return ret;
+}
+
+int zone_backup(conf_t *conf, zone_t *zone)
+{
+ zone_backup_ctx_t *ctx = zone->backup_ctx;
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ int ret_deinit;
+
+ if (ctx->backup_zonefile) {
+ ret = backup_zonefile(conf, zone, ctx);
+ if (ret != KNOT_EOK) {
+ LOG_MARK_FAIL("zone file");
+ goto done;
+ }
+ }
+
+ if (ctx->backup_kaspdb) {
+ knot_lmdb_db_t *kasp_from = zone_kaspdb(zone), *kasp_to = &ctx->bck_kasp_db;
+ BACKUP_SWAP(ctx, kasp_from, kasp_to);
+
+ if (knot_lmdb_exists(kasp_from) != KNOT_ENODB) {
+ ret = kasp_db_backup(zone->name, kasp_from, kasp_to);
+ if (ret != KNOT_EOK) {
+ LOG_MARK_FAIL("KASP database");
+ goto done;
+ }
+
+ ret = backup_keystore(conf, zone, ctx);
+ if (ret != KNOT_EOK) {
+ ctx->failed = true;
+ goto done;
+ }
+ }
+ }
+
+ if (ctx->backup_journal) {
+ knot_lmdb_db_t *j_from = zone_journaldb(zone), *j_to = &ctx->bck_journal;
+ BACKUP_SWAP(ctx, j_from, j_to);
+
+ ret = journal_copy_with_md(j_from, j_to, zone->name);
+ } else if (ctx->restore_mode && ctx->backup_zonefile) {
+ ret = journal_scrape_with_md(zone_journal(zone), true);
+ }
+ if (ret != KNOT_EOK) {
+ LOG_MARK_FAIL("journal");
+ goto done;
+ }
+
+ if (ctx->backup_timers) {
+ ret = knot_lmdb_open(&ctx->bck_timer_db);
+ if (ret != KNOT_EOK) {
+ LOG_MARK_FAIL("timers open");
+ goto done;
+ }
+ if (ctx->restore_mode) {
+ ret = zone_timers_read(&ctx->bck_timer_db, zone->name, &zone->timers);
+ zone_timers_sanitize(conf, zone);
+ zone->zonefile.bootstrap_cnt = 0;
+ } else {
+ ret = zone_timers_write(&ctx->bck_timer_db, zone->name, &zone->timers);
+ }
+ if (ret != KNOT_EOK) {
+ LOG_MARK_FAIL("timers");
+ goto done;
+ }
+ }
+
+done:
+ ret_deinit = zone_backup_deinit(ctx);
+ zone->backup_ctx = NULL;
+ return (ret != KNOT_EOK) ? ret : ret_deinit;
+}
+
+int global_backup(zone_backup_ctx_t *ctx, catalog_t *catalog,
+ const knot_dname_t *zone_only)
+{
+ if (!ctx->backup_catalog) {
+ return KNOT_EOK;
+ }
+
+ knot_lmdb_db_t *cat_from = &catalog->db, *cat_to = &ctx->bck_catalog;
+ BACKUP_SWAP(ctx, cat_from, cat_to);
+ int ret = catalog_copy(cat_from, cat_to, zone_only, !ctx->restore_mode);
+ if (ret != KNOT_EOK) {
+ ctx->failed = true;
+ }
+ return ret;
+}
diff --git a/src/knot/zone/backup.h b/src/knot/zone/backup.h
new file mode 100644
index 0000000..b1d0e3e
--- /dev/null
+++ b/src/knot/zone/backup.h
@@ -0,0 +1,74 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdint.h>
+
+#include "knot/dnssec/kasp/kasp_db.h"
+#include "knot/zone/zone.h"
+
+/*! \bref Backup format versions. */
+typedef enum {
+ BACKUP_FORMAT_1 = 1, // in Knot DNS 3.0.x, no label file
+ BACKUP_FORMAT_2 = 2, // in Knot DNS 3.1.x
+ BACKUP_FORMAT_TERM,
+} knot_backup_format_t;
+
+typedef struct zone_backup_ctx {
+ node_t n; // ability to be put into list_t
+ bool restore_mode; // if true, this is not a backup, but restore
+ bool forced; // if true, the force flag has been set
+ bool backup_zonefile; // if true, also backup zone contents to a zonefile (default on)
+ bool backup_journal; // if true, also backup journal (default off)
+ bool backup_timers; // if true, also backup timers (default on)
+ bool backup_kaspdb; // if true, also backup KASP database (default on)
+ bool backup_catalog; // if true, also backup zone catalog (default on)
+ bool backup_global; // perform global backup for all zones
+ ssize_t readers; // when decremented to 0, all zones done, free this context
+ pthread_mutex_t readers_mutex; // mutex covering readers counter
+ char *backup_dir; // path of directory to backup to / restore from
+ knot_lmdb_db_t bck_kasp_db; // backup KASP db
+ knot_lmdb_db_t bck_timer_db; // backup timer DB
+ knot_lmdb_db_t bck_journal; // backup journal DB
+ knot_lmdb_db_t bck_catalog; // backup catalog DB
+ bool failed; // true if an error occurred in processing of any zone
+ knot_backup_format_t backup_format; // the backup format version used
+ time_t init_time; // time when the current backup operation has started
+ int zone_count; // count of backed up zones
+} zone_backup_ctx_t;
+
+typedef struct {
+ list_t ctxs;
+ pthread_mutex_t mutex;
+} zone_backup_ctxs_t;
+
+int zone_backup_init(bool restore_mode, bool forced, const char *backup_dir,
+ size_t kasp_db_size, size_t timer_db_size, size_t journal_db_size,
+ size_t catalog_db_size, zone_backup_ctx_t **out_ctx);
+
+int zone_backup_deinit(zone_backup_ctx_t *ctx);
+
+int zone_backup(conf_t *conf, zone_t *zone);
+
+int global_backup(zone_backup_ctx_t *ctx, catalog_t *catalog,
+ const knot_dname_t *zone_only);
+
+void zone_backups_init(zone_backup_ctxs_t *ctxs);
+void zone_backups_deinit(zone_backup_ctxs_t *ctxs);
+void zone_backups_add(zone_backup_ctxs_t *ctxs, zone_backup_ctx_t *ctx);
+void zone_backups_rem(zone_backup_ctx_t *ctx);
diff --git a/src/knot/zone/backup_dir.c b/src/knot/zone/backup_dir.c
new file mode 100644
index 0000000..7333b21
--- /dev/null
+++ b/src/knot/zone/backup_dir.c
@@ -0,0 +1,247 @@
+/* Copyright (C) 2023 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "knot/zone/backup_dir.h"
+
+#include "contrib/files.h"
+#include "contrib/getline.h"
+#include "knot/common/log.h"
+
+#define LABEL_FILE "knot_backup.label"
+#define LOCK_FILE "lock.knot_backup"
+
+#define LABEL_FILE_HEAD "label: Knot DNS Backup\n"
+#define LABEL_FILE_FORMAT "backup_format: %d\n"
+#define LABEL_FILE_TIME_FORMAT "%Y-%m-%d %H:%M:%S %Z"
+
+#define FNAME_MAX (MAX(sizeof(LABEL_FILE), sizeof(LOCK_FILE)))
+#define PREPARE_PATH(var, file) \
+ char var[path_size(ctx)]; \
+ get_full_path(ctx, file, var);
+
+static const char *label_file_name = LABEL_FILE;
+static const char *lock_file_name = LOCK_FILE;
+static const char *label_file_head = LABEL_FILE_HEAD;
+
+static void get_full_path(zone_backup_ctx_t *ctx, const char *filename, char *full_path)
+{
+ (void)sprintf(full_path, "%s/%s", ctx->backup_dir, filename);
+}
+
+static size_t path_size(zone_backup_ctx_t *ctx)
+{
+ // The \0 terminator is already included in the sizeof()/FNAME_MAX value,
+ // thus the sum covers one additional char for '/'.
+ return (strlen(ctx->backup_dir) + 1 + FNAME_MAX);
+}
+
+static int make_label_file(zone_backup_ctx_t *ctx)
+{
+ PREPARE_PATH(label_path, label_file_name);
+
+ FILE *file = fopen(label_path, "w");
+ if (file == NULL) {
+ return knot_map_errno();
+ }
+
+ // Prepare the server identity.
+ conf_val_t val = conf_get(conf(), C_SRV, C_IDENT);
+ const char *ident = conf_str(&val);
+ if (ident == NULL || ident[0] == '\0') {
+ ident = conf()->hostname;
+ }
+
+ // Prepare the timestamps.
+ char started_time[64], finished_time[64];
+ struct tm tm;
+
+ localtime_r(&ctx->init_time, &tm);
+ strftime(started_time, sizeof(started_time), LABEL_FILE_TIME_FORMAT, &tm);
+
+ time_t now = time(NULL);
+ localtime_r(&now, &tm);
+ strftime(finished_time, sizeof(finished_time), LABEL_FILE_TIME_FORMAT, &tm);
+
+ // Print the label contents.
+ int ret = fprintf(file,
+ "%s"
+ LABEL_FILE_FORMAT
+ "server_identity: %s\n"
+ "started_time: %s\n"
+ "finished_time: %s\n"
+ "knot_version: %s\n"
+ "parameters: +%szonefile +%sjournal +%stimers +%skaspdb +%scatalog "
+ "+backupdir %s\n"
+ "zone_count: %d\n",
+ label_file_head,
+ ctx->backup_format, ident, started_time, finished_time, PACKAGE_VERSION,
+ ctx->backup_zonefile ? "" : "no",
+ ctx->backup_journal ? "" : "no",
+ ctx->backup_timers ? "" : "no",
+ ctx->backup_kaspdb ? "" : "no",
+ ctx->backup_catalog ? "" : "no",
+ ctx->backup_dir,
+ ctx->zone_count);
+
+ ret = (ret < 0) ? knot_map_errno() : KNOT_EOK;
+
+ fclose(file);
+ return ret;
+}
+
+static int get_backup_format(zone_backup_ctx_t *ctx)
+{
+ PREPARE_PATH(label_path, label_file_name);
+
+ int ret = KNOT_EMALF;
+
+ struct stat sb;
+ if (stat(label_path, &sb) != 0) {
+ ret = knot_map_errno();
+ if (ret == KNOT_ENOENT) {
+ if (ctx->forced) {
+ ctx->backup_format = BACKUP_FORMAT_1;
+ ret = KNOT_EOK;
+ } else {
+ ret = KNOT_EMALF;
+ }
+ }
+ return ret;
+ }
+
+ // getline() from an empty file results in EAGAIN, therefore avoid doing so.
+ if (!S_ISREG(sb.st_mode) || sb.st_size == 0) {
+ return ret;
+ }
+
+ FILE *file = fopen(label_path, "r");
+ if (file == NULL) {
+ return knot_map_errno();
+ }
+
+ char *line = NULL;
+ size_t line_size = 0;
+
+ // Check for the header line first.
+ if (knot_getline(&line, &line_size, file) == -1) {
+ ret = knot_map_errno();
+ goto done;
+ }
+
+ if (strcmp(line, label_file_head) != 0) {
+ goto done;
+ }
+
+ while (knot_getline(&line, &line_size, file) != -1) {
+ int value;
+ if (sscanf(line, LABEL_FILE_FORMAT, &value) != 0) {
+ if (value >= BACKUP_FORMAT_TERM) {
+ ret = KNOT_ENOTSUP;
+ } else if (value > BACKUP_FORMAT_1) {
+ ctx->backup_format = value;
+ ret = KNOT_EOK;
+ }
+ break;
+ }
+ }
+
+done:
+ free(line);
+ fclose(file);
+ return ret;
+}
+
+int backupdir_init(zone_backup_ctx_t *ctx)
+{
+ int ret;
+ struct stat sb;
+
+ // Make sure the source/target backup directory exists.
+ if (ctx->restore_mode) {
+ if (stat(ctx->backup_dir, &sb) != 0) {
+ return knot_map_errno();
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ return KNOT_ENOTDIR;
+ }
+ } else {
+ ret = make_dir(ctx->backup_dir, S_IRWXU|S_IRWXG, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ char full_path[path_size(ctx)];
+
+ // Check for existence of a label file and the backup format used.
+ if (ctx->restore_mode) {
+ ret = get_backup_format(ctx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ get_full_path(ctx, label_file_name, full_path);
+ if (stat(full_path, &sb) == 0) {
+ return KNOT_EEXIST;
+ }
+ }
+
+ // Make (or check for existence of) a lock file.
+ get_full_path(ctx, lock_file_name, full_path);
+ if (ctx->restore_mode) {
+ // Just check.
+ if (stat(full_path, &sb) == 0) {
+ return KNOT_EBUSY;
+ }
+ } else {
+ // Create it (which also checks for its existence).
+ int lock_file = open(full_path, O_CREAT|O_EXCL,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ if (lock_file < 0) {
+ // Make the reported error better understandable than KNOT_EEXIST.
+ return errno == EEXIST ? KNOT_EBUSY : knot_map_errno();
+ }
+ close(lock_file);
+ }
+
+ return KNOT_EOK;
+}
+
+int backupdir_deinit(zone_backup_ctx_t *ctx)
+{
+ int ret = KNOT_EOK;
+
+ if (!ctx->restore_mode && !ctx->failed) {
+ // Create the label file first.
+ ret = make_label_file(ctx);
+ if (ret == KNOT_EOK) {
+ // Remove the lock file only when the label file has been created.
+ PREPARE_PATH(lock_path, lock_file_name);
+ unlink(lock_path);
+ } else {
+ log_error("failed to create a backup label in %s", (ctx)->backup_dir);
+ }
+ }
+
+ return ret;
+}
diff --git a/src/knot/zone/backup_dir.h b/src/knot/zone/backup_dir.h
new file mode 100644
index 0000000..7d19ffc
--- /dev/null
+++ b/src/knot/zone/backup_dir.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/backup.h"
+
+/*!
+ * Prepares the backup directory - verifies it exists and creates it for backup
+ * if it's needed. Verifies existence/non-existence of a lock file and a label file,
+ * in the backup mode it creates them, in the restore mode it sets ctx->backup_format.
+ *
+ * \param[in/out] ctx Backup context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int backupdir_init(zone_backup_ctx_t *ctx);
+
+/*!
+ * If the backup has been successful, it creates the label file
+ * and removes the lock file. Do nothing in the restore mode.
+ *
+ * \param[in] ctx Backup context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int backupdir_deinit(zone_backup_ctx_t *ctx);
diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c
new file mode 100644
index 0000000..cba13e8
--- /dev/null
+++ b/src/knot/zone/contents.c
@@ -0,0 +1,609 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "libdnssec/error.h"
+#include "knot/zone/adds_tree.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/contents.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "libknot/libknot.h"
+#include "contrib/qp-trie/trie.h"
+
+/*!
+ * \brief Destroys all RRSets in a node.
+ *
+ * \param node Node to destroy RRSets from.
+ * \param data Unused parameter.
+ */
+static int destroy_node_rrsets_from_tree(zone_node_t *node, _unused_ void *data)
+{
+ if (node != NULL) {
+ binode_unify(node, false, NULL);
+ node_free_rrsets(node, NULL);
+ node_free(node, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree.
+ *
+ * \param zone Zone to search in.
+ * \param name Domain name to find.
+ * \param node Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a name in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval true if the domain name was found. In such case \a node holds the
+ * zone node with \a name as its owner. \a previous is set
+ * properly.
+ * \retval false if the domain name was not found. \a node may hold any (or none)
+ * node. \a previous is set properly.
+ */
+static bool find_in_tree(zone_tree_t *tree, const knot_dname_t *name,
+ zone_node_t **node, zone_node_t **previous)
+{
+ assert(tree != NULL);
+ assert(name != NULL);
+ assert(node != NULL);
+ assert(previous != NULL);
+
+ zone_node_t *found = NULL, *prev = NULL;
+
+ int match = zone_tree_get_less_or_equal(tree, name, &found, &prev);
+ if (match < 0) {
+ assert(0);
+ return false;
+ }
+
+ *node = found;
+ *previous = prev;
+
+ return match > 0;
+}
+
+/*!
+ * \brief Create a node suitable for inserting into this contents.
+ */
+static zone_node_t *node_new_for_contents(const knot_dname_t *owner, const zone_contents_t *contents)
+{
+ assert(contents->nsec3_nodes == NULL || contents->nsec3_nodes->flags == contents->nodes->flags);
+ return node_new_for_tree(owner, contents->nodes, NULL);
+}
+
+static zone_node_t *get_node(const zone_contents_t *zone, const knot_dname_t *name)
+{
+ assert(zone);
+ assert(name);
+
+ return zone_tree_get(zone->nodes, name);
+}
+
+static zone_node_t *get_nsec3_node(const zone_contents_t *zone,
+ const knot_dname_t *name)
+{
+ assert(zone);
+ assert(name);
+
+ return zone_tree_get(zone->nsec3_nodes, name);
+}
+
+static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n)
+{
+ if (knot_rrset_empty(rr)) {
+ return KNOT_EINVAL;
+ }
+
+ if (*n == NULL) {
+ int ret = zone_tree_add_node(zone_contents_tree_for_rr(z, rr), z->apex, rr->owner,
+ (zone_tree_new_node_cb_t)node_new_for_contents, z, n);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return node_add_rrset(*n, rr, NULL);
+}
+
+static int remove_rr(zone_contents_t *z, const knot_rrset_t *rr,
+ zone_node_t **n, bool nsec3)
+{
+ if (knot_rrset_empty(rr)) {
+ return KNOT_EINVAL;
+ }
+
+ // check if the RRSet belongs to the zone
+ if (knot_dname_in_bailiwick(rr->owner, z->apex->owner) < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+
+ zone_node_t *node;
+ if (*n == NULL) {
+ node = nsec3 ? get_nsec3_node(z, rr->owner) : get_node(z, rr->owner);
+ if (node == NULL) {
+ return KNOT_ENONODE;
+ }
+ } else {
+ node = *n;
+ }
+
+ int ret = node_remove_rrset(node, rr, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (node->rrset_count == 0 && node->children == 0 && node != z->apex) {
+ zone_tree_del_node(nsec3 ? z->nsec3_nodes : z->nodes, node, true);
+ }
+
+ *n = node;
+ return KNOT_EOK;
+}
+
+// Public API
+
+zone_contents_t *zone_contents_new(const knot_dname_t *apex_name, bool use_binodes)
+{
+ if (apex_name == NULL) {
+ return NULL;
+ }
+
+ zone_contents_t *contents = calloc(1, sizeof(*contents));
+ if (contents == NULL) {
+ return NULL;
+ }
+
+ contents->nodes = zone_tree_create(use_binodes);
+ if (contents->nodes == NULL) {
+ goto cleanup;
+ }
+
+ contents->apex = node_new_for_contents(apex_name, contents);
+ if (contents->apex == NULL) {
+ goto cleanup;
+ }
+
+ if (zone_tree_insert(contents->nodes, &contents->apex) != KNOT_EOK) {
+ goto cleanup;
+ }
+ contents->apex->flags |= NODE_FLAGS_APEX;
+ contents->max_ttl = UINT32_MAX;
+
+ return contents;
+
+cleanup:
+ node_free(contents->apex, NULL);
+ free(contents->nodes);
+ free(contents);
+ return NULL;
+}
+
+zone_tree_t *zone_contents_tree_for_rr(zone_contents_t *contents, const knot_rrset_t *rr)
+{
+ bool nsec3rel = knot_rrset_is_nsec3rel(rr);
+
+ if (nsec3rel && contents->nsec3_nodes == NULL) {
+ contents->nsec3_nodes = zone_tree_create((contents->nodes->flags & ZONE_TREE_USE_BINODES));
+ if (contents->nsec3_nodes == NULL) {
+ return NULL;
+ }
+ contents->nsec3_nodes->flags = contents->nodes->flags;
+ }
+
+ return nsec3rel ? contents->nsec3_nodes : contents->nodes;
+}
+
+int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n)
+{
+ if (rr == NULL || n == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (z == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ return insert_rr(z, rr, n);
+}
+
+int zone_contents_remove_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n)
+{
+ if (rr == NULL || n == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (z == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ return remove_rr(z, rr, n, knot_rrset_is_nsec3rel(rr));
+}
+
+const zone_node_t *zone_contents_find_node(const zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ return get_node(zone, name);
+}
+
+const zone_node_t *zone_contents_node_or_nsec3(const zone_contents_t *zone, const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ const zone_node_t *node = get_node(zone, name);
+ if (node == NULL) {
+ node = get_nsec3_node(zone, name);
+ }
+ return node;
+}
+
+zone_node_t *zone_contents_find_node_for_rr(zone_contents_t *contents, const knot_rrset_t *rrset)
+{
+ if (contents == NULL || rrset == NULL) {
+ return NULL;
+ }
+
+ const bool nsec3 = knot_rrset_is_nsec3rel(rrset);
+ return nsec3 ? get_nsec3_node(contents, rrset->owner) :
+ get_node(contents, rrset->owner);
+}
+
+int zone_contents_find_dname(const zone_contents_t *zone,
+ const knot_dname_t *name,
+ const zone_node_t **match,
+ const zone_node_t **closest,
+ const zone_node_t **previous)
+{
+ if (name == NULL || match == NULL || closest == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ if (knot_dname_in_bailiwick(name, zone->apex->owner) < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+
+ zone_node_t *node = NULL;
+ zone_node_t *prev = NULL;
+
+ int found = zone_tree_get_less_or_equal(zone->nodes, name, &node, &prev);
+ if (found < 0) {
+ // error
+ return found;
+ } else if (found == 1 && previous != NULL) {
+ // exact match
+
+ assert(node && prev);
+
+ *match = node;
+ *closest = node;
+ *previous = prev;
+
+ return ZONE_NAME_FOUND;
+ } else if (found == 1 && previous == NULL) {
+ // exact match, zone not adjusted yet
+
+ assert(node);
+ *match = node;
+ *closest = node;
+
+ return ZONE_NAME_FOUND;
+ } else {
+ // closest match
+
+ assert(!node && prev);
+
+ node = prev;
+ size_t matched_labels = knot_dname_matched_labels(node->owner, name);
+ while (matched_labels < knot_dname_labels(node->owner, NULL)) {
+ node = node_parent(node);
+ assert(node);
+ }
+
+ *match = NULL;
+ *closest = node;
+ if (previous != NULL) {
+ *previous = prev;
+ }
+
+ return ZONE_NAME_NOT_FOUND;
+ }
+}
+
+const zone_node_t *zone_contents_find_nsec3_node(const zone_contents_t *zone,
+ const knot_dname_t *name)
+{
+ if (zone == NULL || name == NULL) {
+ return NULL;
+ }
+
+ return get_nsec3_node(zone, name);
+}
+
+int zone_contents_find_nsec3_for_name(const zone_contents_t *zone,
+ const knot_dname_t *name,
+ const zone_node_t **nsec3_node,
+ const zone_node_t **nsec3_previous)
+{
+ if (name == NULL || nsec3_node == NULL || nsec3_previous == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ // check if the NSEC3 tree is not empty
+ if (zone_tree_is_empty(zone->nsec3_nodes)) {
+ return KNOT_ENSEC3CHAIN;
+ }
+ if (!knot_is_nsec3_enabled(zone)) {
+ return KNOT_ENSEC3PAR;
+ }
+
+ knot_dname_storage_t nsec3_name;
+ int ret = knot_create_nsec3_owner(nsec3_name, sizeof(nsec3_name),
+ name, zone->apex->owner, &zone->nsec3_params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return zone_contents_find_nsec3(zone, nsec3_name, nsec3_node, nsec3_previous);
+}
+
+int zone_contents_find_nsec3(const zone_contents_t *zone,
+ const knot_dname_t *nsec3_name,
+ const zone_node_t **nsec3_node,
+ const zone_node_t **nsec3_previous)
+{
+ zone_node_t *found = NULL, *prev = NULL;
+ bool match = find_in_tree(zone->nsec3_nodes, nsec3_name, &found, &prev);
+
+ *nsec3_node = found;
+
+ if (prev == NULL) {
+ // either the returned node is the root of the tree, or it is
+ // the leftmost node in the tree; in both cases node was found
+ // set the previous node of the found node
+ assert(match);
+ assert(*nsec3_node != NULL);
+ *nsec3_previous = node_prev(*nsec3_node);
+ assert(*nsec3_previous != NULL);
+ } else {
+ *nsec3_previous = prev;
+ }
+
+ // The previous may be from wrong NSEC3 chain. Search for previous from the right chain.
+ const zone_node_t *original_prev = *nsec3_previous;
+ while (!((*nsec3_previous)->flags & NODE_FLAGS_IN_NSEC3_CHAIN)) {
+ *nsec3_previous = node_prev(*nsec3_previous);
+ if (*nsec3_previous == original_prev || *nsec3_previous == NULL) {
+ // cycle
+ *nsec3_previous = NULL;
+ break;
+ }
+ }
+
+ return (match ? ZONE_NAME_FOUND : ZONE_NAME_NOT_FOUND);
+}
+
+const zone_node_t *zone_contents_find_wildcard_child(const zone_contents_t *contents,
+ const zone_node_t *parent)
+{
+ if (contents == NULL || parent == NULL || parent->owner == NULL) {
+ return NULL;
+ }
+
+ knot_dname_storage_t wildcard = "\x01""*";
+ knot_dname_to_wire(wildcard + 2, parent->owner, sizeof(wildcard) - 2);
+
+ return zone_contents_find_node(contents, wildcard);
+}
+
+bool zone_contents_find_node_or_wildcard(const zone_contents_t *contents,
+ const knot_dname_t *find,
+ const zone_node_t **found)
+{
+ const zone_node_t *encloser = NULL;
+ zone_contents_find_dname(contents, find, found, &encloser, NULL);
+ if (*found == NULL && encloser != NULL && (encloser->flags & NODE_FLAGS_WILDCARD_CHILD)) {
+ *found = zone_contents_find_wildcard_child(contents, encloser);
+ assert(*found != NULL);
+ }
+ return (*found != NULL);
+}
+
+int zone_contents_apply(zone_contents_t *contents,
+ zone_tree_apply_cb_t function, void *data)
+{
+ if (contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+ return zone_tree_apply(contents->nodes, function, data);
+}
+
+int zone_contents_nsec3_apply(zone_contents_t *contents,
+ zone_tree_apply_cb_t function, void *data)
+{
+ if (contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+ return zone_tree_apply(contents->nsec3_nodes, function, data);
+}
+
+int zone_contents_cow(zone_contents_t *from, zone_contents_t **to)
+{
+ if (to == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (from == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ /* Copy to same destination as source. */
+ if (from == *to) {
+ return KNOT_EINVAL;
+ }
+
+ zone_contents_t *contents = calloc(1, sizeof(zone_contents_t));
+ if (contents == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ contents->nodes = zone_tree_cow(from->nodes);
+ if (contents->nodes == NULL) {
+ free(contents);
+ return KNOT_ENOMEM;
+ }
+ contents->apex = zone_tree_fix_get(from->apex, contents->nodes);
+
+ if (from->nsec3_nodes) {
+ contents->nsec3_nodes = zone_tree_cow(from->nsec3_nodes);
+ if (contents->nsec3_nodes == NULL) {
+ trie_cow_rollback(contents->nodes->cow, NULL, NULL);
+ free(contents->nodes);
+ free(contents);
+ return KNOT_ENOMEM;
+ }
+ }
+ contents->adds_tree = from->adds_tree;
+ from->adds_tree = NULL;
+ contents->size = from->size;
+ contents->max_ttl = from->max_ttl;
+
+ *to = contents;
+ return KNOT_EOK;
+}
+
+void zone_contents_free(zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return;
+ }
+
+ // free the zone tree, but only the structure
+ zone_tree_free(&contents->nodes);
+ zone_tree_free(&contents->nsec3_nodes);
+
+ dnssec_nsec3_params_free(&contents->nsec3_params);
+ additionals_tree_free(contents->adds_tree);
+
+ free(contents);
+}
+
+void zone_contents_deep_free(zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return;
+ }
+
+ if (contents != NULL) {
+ // Delete NSEC3 tree.
+ (void)zone_tree_apply(contents->nsec3_nodes,
+ destroy_node_rrsets_from_tree, NULL);
+
+ // Delete the normal tree.
+ (void)zone_tree_apply(contents->nodes,
+ destroy_node_rrsets_from_tree, NULL);
+ }
+
+ zone_contents_free(contents);
+}
+
+uint32_t zone_contents_serial(const zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return 0;
+ }
+
+ const knot_rdataset_t *soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA);
+ if (soa == NULL) {
+ return 0;
+ }
+
+ return knot_soa_serial(soa->rdata);
+}
+
+void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial)
+{
+ knot_rdataset_t *soa;
+ if (zone != NULL && (soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA)) != NULL) {
+ knot_soa_serial_set(soa->rdata, new_serial);
+ }
+}
+
+int zone_contents_load_nsec3param(zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ if (contents->apex == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const knot_rdataset_t *rrs = NULL;
+ rrs = node_rdataset(contents->apex, KNOT_RRTYPE_NSEC3PARAM);
+ if (rrs == NULL) {
+ dnssec_nsec3_params_free(&contents->nsec3_params);
+ return KNOT_EOK;
+ }
+
+ if (rrs->count != 1) {
+ return KNOT_EINVAL;
+ }
+
+ dnssec_binary_t rdata = {
+ .size = rrs->rdata->len,
+ .data = rrs->rdata->data,
+ };
+
+ dnssec_nsec3_params_t new_params = { 0 };
+ int r = dnssec_nsec3_params_from_rdata(&new_params, &rdata);
+ if (r != DNSSEC_EOK) {
+ return KNOT_EMALF;
+ }
+
+ dnssec_nsec3_params_free(&contents->nsec3_params);
+ contents->nsec3_params = new_params;
+ return KNOT_EOK;
+}
+
+bool zone_contents_is_empty(const zone_contents_t *zone)
+{
+ if (zone == NULL) {
+ return true;
+ }
+
+ bool apex_empty = (zone->apex == NULL || zone->apex->rrset_count == 0);
+ bool no_non_apex = (zone_tree_count(zone->nodes) <= (zone->apex != NULL ? 1 : 0));
+ bool no_nsec3 = zone_tree_is_empty(zone->nsec3_nodes);
+
+ return (apex_empty && no_non_apex && no_nsec3);
+}
diff --git a/src/knot/zone/contents.h b/src/knot/zone/contents.h
new file mode 100644
index 0000000..8f1f160
--- /dev/null
+++ b/src/knot/zone/contents.h
@@ -0,0 +1,291 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "libdnssec/nsec.h"
+#include "libknot/rrtype/nsec3param.h"
+#include "knot/zone/node.h"
+#include "knot/zone/zone-tree.h"
+
+enum zone_contents_find_dname_result {
+ ZONE_NAME_NOT_FOUND = 0,
+ ZONE_NAME_FOUND = 1
+};
+
+typedef struct zone_contents {
+ zone_node_t *apex; /*!< Apex node of the zone (holding SOA) */
+
+ zone_tree_t *nodes;
+ zone_tree_t *nsec3_nodes;
+
+ trie_t *adds_tree; // "additionals tree" for reverse lookup of nodes affected by additionals
+
+ dnssec_nsec3_params_t nsec3_params;
+ size_t size;
+ uint32_t max_ttl;
+ bool dnssec;
+} zone_contents_t;
+
+/*!
+ * \brief Allocate and create new zone contents.
+ *
+ * \param apex_name Name of the root node.
+ * \param use_binodes Zone trees shall consist of bi-nodes to enable zone updates.
+ *
+ * \return New contents or NULL on error.
+ */
+zone_contents_t *zone_contents_new(const knot_dname_t *apex_name, bool use_binodes);
+
+/*!
+ * \brief Returns zone tree for inserting given RR.
+ */
+zone_tree_t *zone_contents_tree_for_rr(zone_contents_t *contents, const knot_rrset_t *rr);
+
+/*!
+ * \brief Add an RR to contents.
+ *
+ * \param z Contents to add to.
+ * \param rr The RR to add.
+ * \param n Node to which the RR has been added to on success, unchanged otherwise.
+ *
+ * \return KNOT_E*
+ */
+int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n);
+
+/*!
+ * \brief Remove an RR from contents.
+ *
+ * \param z Contents to remove from.
+ * \param rr The RR to remove.
+ * \param n Node from which the RR to be removed from on success, unchanged otherwise.
+ *
+ * \return KNOT_E*
+ */
+int zone_contents_remove_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n);
+
+/*!
+ * \brief Tries to find a node with the specified name in the zone.
+ *
+ * \param contents Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const zone_node_t *zone_contents_find_node(const zone_contents_t *contents, const knot_dname_t *name);
+
+/*!
+ * \brief Tries to find a node in the zone, also searching in NSEC3 tree.
+ *
+ * \param zone Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Normal or NSEC3 node, or NULL.
+ */
+const zone_node_t *zone_contents_node_or_nsec3(const zone_contents_t *zone, const knot_dname_t *name);
+
+/*!
+ * \brief Find a node in which the given rrset may be inserted,
+ *
+ * \param contents Zone contents.
+ * \param rrset RRSet to be inserted later.
+ *
+ * \return Existing node in zone which the RRSet may be inserted in; or NULL if none present.
+ */
+zone_node_t *zone_contents_find_node_for_rr(zone_contents_t *contents, const knot_rrset_t *rrset);
+
+/*!
+ * \brief Tries to find a node by owner in the zone contents.
+ *
+ * \param[in] contents Zone to search for the name.
+ * \param[in] name Domain name to search for.
+ * \param[out] match Matching node or NULL.
+ * \param[out] closest Closest matching name in the zone.
+ * May match \a match if found exactly.
+ * \param[out] previous Previous domain name in canonical order.
+ * Always previous, won't match \a match.
+ *
+ * \note The encloser and previous mustn't be used directly for DNSSEC proofs.
+ * These nodes may be empty non-terminals or not authoritative.
+ *
+ * \retval ZONE_NAME_FOUND if node with owner \a name was found.
+ * \retval ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EEMPTYZONE
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EOUTOFZONE
+ */
+int zone_contents_find_dname(const zone_contents_t *contents,
+ const knot_dname_t *name,
+ const zone_node_t **match,
+ const zone_node_t **closest,
+ const zone_node_t **previous);
+
+/*!
+ * \brief Tries to find a node with the specified name among the NSEC3 nodes
+ * of the zone.
+ *
+ * \param contents Zone where the name should be searched for.
+ * \param name Name to find.
+ *
+ * \return Corresponding node if found, NULL otherwise.
+ */
+const zone_node_t *zone_contents_find_nsec3_node(const zone_contents_t *contents,
+ const knot_dname_t *name);
+
+/*!
+ * \brief Finds NSEC3 node and previous NSEC3 node in canonical order,
+ * corresponding to the given domain name.
+ *
+ * This functions creates a NSEC3 hash of \a name and tries to find NSEC3 node
+ * with the hashed domain name as owner.
+ *
+ * \param[in] contents Zone to search in.
+ * \param[in] name Domain name to get the corresponding NSEC3 nodes for.
+ * \param[out] nsec3_node NSEC3 node corresponding to \a name (if found,
+ * otherwise this may be an arbitrary NSEC3 node).
+ * \param[out] nsec3_previous The NSEC3 node immediately preceding hashed domain
+ * name corresponding to \a name in canonical order.
+ *
+ * \retval ZONE_NAME_FOUND if the corresponding NSEC3 node was found.
+ * \retval ZONE_NAME_NOT_FOUND if it was not found.
+ * \retval KNOT_EEMPTYZONE
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENSEC3PAR
+ * \retval KNOT_ECRYPTO
+ * \retval KNOT_ERROR
+ */
+int zone_contents_find_nsec3_for_name(const zone_contents_t *contents,
+ const knot_dname_t *name,
+ const zone_node_t **nsec3_node,
+ const zone_node_t **nsec3_previous);
+
+/*!
+ * \brief Finds NSEC3 node and previous NSEC3 node to specified NSEC3 name.
+ *
+ * Like previous function, but the NSEC3 hashed-name is already known.
+ *
+ * \param zone Zone contents to search in,
+ * \param nsec3_name NSEC3 name to be searched for.
+ * \param nsec3_node Out: NSEC3 node found.
+ * \param nsec3_previous Out: previous NSEC3 node.
+ *
+ * \return ZONE_NAME_FOUND, ZONE_NAME_NOT_FOUND, KNOT_E*
+ */
+int zone_contents_find_nsec3(const zone_contents_t *zone,
+ const knot_dname_t *nsec3_name,
+ const zone_node_t **nsec3_node,
+ const zone_node_t **nsec3_previous);
+
+/*!
+ * \brief For specified node, give a wildcard child if exists in zone.
+ *
+ * \param contents Zone contents.
+ * \param parent Given parent node.
+ *
+ * \return Node being a wildcard child; or NULL.
+ */
+const zone_node_t *zone_contents_find_wildcard_child(const zone_contents_t *contents,
+ const zone_node_t *parent);
+
+/*!
+ * \brief For given name, find either exactly matching node in zone, or a matching wildcard node.
+ *
+ * \param contents Zone contents to be searched in.
+ * \param find Name to be searched for.
+ * \param found Out: a node that either has owner "find" or is matching wildcard node.
+ *
+ * \return true iff found something
+ */
+bool zone_contents_find_node_or_wildcard(const zone_contents_t *contents,
+ const knot_dname_t *find,
+ const zone_node_t **found);
+
+/*!
+ * \brief Applies the given function to each regular node in the zone.
+ *
+ * \param contents Nodes of this zone will be used as parameters for the function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int zone_contents_apply(zone_contents_t *contents,
+ zone_tree_apply_cb_t function, void *data);
+
+/*!
+ * \brief Applies the given function to each NSEC3 node in the zone.
+ *
+ * \param contents NSEC3 nodes of this zone will be used as parameters for the
+ * function.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ */
+int zone_contents_nsec3_apply(zone_contents_t *contents,
+ zone_tree_apply_cb_t function, void *data);
+
+/*!
+ * \brief Create new zone_contents by COW copy of zone trees.
+ *
+ * \param from Original zone.
+ * \param to Copy of the zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EEMPTYZONE
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ */
+int zone_contents_cow(zone_contents_t *from, zone_contents_t **to);
+
+/*!
+ * \brief Deallocate directly owned data of zone contents.
+ *
+ * \param contents Zone contents to free.
+ */
+void zone_contents_free(zone_contents_t *contents);
+
+/*!
+ * \brief Deallocate node RRSets inside the trees, then call zone_contents_free.
+ *
+ * \param contents Zone contents to free.
+ */
+void zone_contents_deep_free(zone_contents_t *contents);
+
+/*!
+ * \brief Fetch zone serial.
+ *
+ * \param zone Zone.
+ *
+ * \return serial or 0
+ */
+uint32_t zone_contents_serial(const zone_contents_t *zone);
+
+/*!
+ * \brief Adjust zone serial.
+ *
+ * Works only if there is a SOA in given contents.
+ *
+ * \param zone Zone.
+ * \param new_serial New serial to be set.
+ */
+void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial);
+
+/*!
+ * \brief Load parameters from NSEC3PARAM record into contents->nsec3param structure.
+ */
+int zone_contents_load_nsec3param(zone_contents_t *contents);
+
+/*!
+ * \brief Return true if zone is empty.
+ */
+bool zone_contents_is_empty(const zone_contents_t *zone);
diff --git a/src/knot/zone/digest.c b/src/knot/zone/digest.c
new file mode 100644
index 0000000..c3d40a4
--- /dev/null
+++ b/src/knot/zone/digest.c
@@ -0,0 +1,305 @@
+/* Copyright (C) 2022 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 <stdio.h>
+
+#include "knot/zone/digest.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/updates/zone-update.h"
+#include "contrib/wire_ctx.h"
+#include "libdnssec/digest.h"
+#include "libknot/libknot.h"
+
+#define DIGEST_BUF_MIN 4096
+#define DIGEST_BUF_MAX (40 * 1024 * 1024)
+
+typedef struct {
+ size_t buf_size;
+ uint8_t *buf;
+ struct dnssec_digest_ctx *digest_ctx;
+ const zone_node_t *apex;
+} contents_digest_ctx_t;
+
+static int digest_rrset(knot_rrset_t *rrset, const zone_node_t *node, void *vctx)
+{
+ contents_digest_ctx_t *ctx = vctx;
+
+ // ignore apex ZONEMD
+ if (node == ctx->apex && rrset->type == KNOT_RRTYPE_ZONEMD) {
+ return KNOT_EOK;
+ }
+
+ // ignore RRSIGs of apex ZONEMD
+ if (node == ctx->apex && rrset->type == KNOT_RRTYPE_RRSIG) {
+ knot_rdataset_t cpy = rrset->rrs, zonemd_rrsig = { 0 };
+ int ret = knot_rdataset_copy(&rrset->rrs, &cpy, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = knot_synth_rrsig(KNOT_RRTYPE_ZONEMD, &rrset->rrs, &zonemd_rrsig, NULL);
+ if (ret == KNOT_EOK) {
+ ret = knot_rdataset_subtract(&rrset->rrs, &zonemd_rrsig, NULL);
+ knot_rdataset_clear(&zonemd_rrsig, NULL);
+ }
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ knot_rdataset_clear(&rrset->rrs, NULL);
+ return ret;
+ }
+ }
+
+ // serialize RRSet, expand buf as needed
+ int ret = knot_rrset_to_wire_extra(rrset, ctx->buf, ctx->buf_size, 0,
+ NULL, KNOT_PF_ORIGTTL);
+ while (ret == KNOT_ESPACE && ctx->buf_size < DIGEST_BUF_MAX) {
+ free(ctx->buf);
+ ctx->buf_size *= 2;
+ ctx->buf = malloc(ctx->buf_size);
+ if (ctx->buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+ ret = knot_rrset_to_wire_extra(rrset, ctx->buf, ctx->buf_size, 0,
+ NULL, KNOT_PF_ORIGTTL);
+ }
+
+ // cleanup apex RRSIGs mess
+ if (node == ctx->apex && rrset->type == KNOT_RRTYPE_RRSIG) {
+ knot_rdataset_clear(&rrset->rrs, NULL);
+ }
+
+ if (ret < 0) {
+ return ret;
+ }
+
+ // digest serialized RRSet
+ dnssec_binary_t bufbin = { ret, ctx->buf };
+ return dnssec_digest(ctx->digest_ctx, &bufbin);
+}
+
+static int digest_node(zone_node_t *node, void *ctx)
+{
+ int i = 0, ret = KNOT_EOK;
+ for ( ; i < node->rrset_count && ret == KNOT_EOK; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ ret = digest_rrset(&rrset, node, ctx);
+ }
+ return ret;
+}
+
+int zone_contents_digest(const zone_contents_t *contents, int algorithm,
+ uint8_t **out_digest, size_t *out_size)
+{
+ if (out_digest == NULL || out_size == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ contents_digest_ctx_t ctx = {
+ .buf_size = DIGEST_BUF_MIN,
+ .buf = malloc(DIGEST_BUF_MIN),
+ .apex = contents->apex,
+ };
+ if (ctx.buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = dnssec_digest_init(algorithm, &ctx.digest_ctx);
+ if (ret != DNSSEC_EOK) {
+ free(ctx.buf);
+ return knot_error_from_libdnssec(ret);
+ }
+
+ zone_tree_t *conts = contents->nodes;
+ if (!zone_tree_is_empty(contents->nsec3_nodes)) {
+ conts = zone_tree_shallow_copy(conts);
+ if (conts == NULL) {
+ ret = KNOT_ENOMEM;;
+ }
+ if (ret == KNOT_EOK) {
+ ret = zone_tree_merge(conts, contents->nsec3_nodes);
+ }
+ }
+
+ if (ret == KNOT_EOK) {
+ ret = zone_tree_apply(conts, digest_node, &ctx);
+ }
+
+ if (conts != contents->nodes) {
+ zone_tree_free(&conts);
+ }
+
+ dnssec_binary_t res = { 0 };
+ if (ret == KNOT_EOK) {
+ ret = dnssec_digest_finish(ctx.digest_ctx, &res);
+ }
+ free(ctx.buf);
+ *out_digest = res.data;
+ *out_size = res.size;
+ return ret;
+}
+
+static int verify_zonemd(const knot_rdata_t *zonemd, const zone_contents_t *contents)
+{
+ uint8_t *computed = NULL;
+ size_t comp_size = 0;
+ int ret = zone_contents_digest(contents, knot_zonemd_algorithm(zonemd),
+ &computed, &comp_size);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ assert(computed);
+
+ if (comp_size != knot_zonemd_digest_size(zonemd)) {
+ ret = KNOT_EFEWDATA;
+ } else if (memcmp(knot_zonemd_digest(zonemd), computed, comp_size) != 0) {
+ ret = KNOT_EMALF;
+ }
+ free(computed);
+ return ret;
+}
+
+bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify)
+{
+ if (alg == 0) {
+ return true;
+ }
+
+ knot_rdataset_t *zonemd = node_rdataset(contents->apex, KNOT_RRTYPE_ZONEMD);
+
+ if (alg == ZONE_DIGEST_REMOVE) {
+ return (zonemd == NULL || zonemd->count == 0);
+ }
+
+ if (zonemd == NULL || zonemd->count != 1 || knot_zonemd_algorithm(zonemd->rdata) != alg) {
+ return false;
+ }
+
+ if (no_verify) {
+ return true;
+ }
+
+ return verify_zonemd(zonemd->rdata, contents) == KNOT_EOK;
+}
+
+static bool check_duplicate_schalg(const knot_rdataset_t *zonemd, int check_upto,
+ uint8_t scheme, uint8_t alg)
+{
+ knot_rdata_t *check = zonemd->rdata;
+ assert(check_upto <= zonemd->count);
+ for (int i = 0; i < check_upto; i++) {
+ if (knot_zonemd_scheme(check) == scheme &&
+ knot_zonemd_algorithm(check) == alg) {
+ return false;
+ }
+ check = knot_rdataset_next(check);
+ }
+ return true;
+}
+
+int zone_contents_digest_verify(const zone_contents_t *contents)
+{
+ if (contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ knot_rdataset_t *zonemd = node_rdataset(contents->apex, KNOT_RRTYPE_ZONEMD);
+ if (zonemd == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ uint32_t soa_serial = zone_contents_serial(contents);
+
+ knot_rdata_t *rr = zonemd->rdata, *supported = NULL;
+ for (int i = 0; i < zonemd->count; i++) {
+ if (knot_zonemd_scheme(rr) == KNOT_ZONEMD_SCHEME_SIMPLE &&
+ knot_zonemd_digest_size(rr) > 0 &&
+ knot_zonemd_soa_serial(rr) == soa_serial) {
+ supported = rr;
+ }
+ if (!check_duplicate_schalg(zonemd, i, knot_zonemd_scheme(rr),
+ knot_zonemd_algorithm(rr))) {
+ return KNOT_ESEMCHECK;
+ }
+ rr = knot_rdataset_next(rr);
+ }
+
+ return supported == NULL ? KNOT_ENOTSUP : verify_zonemd(supported, contents);
+}
+
+static ptrdiff_t zonemd_hash_offs(void)
+{
+ knot_rdata_t fake = { 0 };
+ return knot_zonemd_digest(&fake) - fake.data;
+}
+
+int zone_update_add_digest(struct zone_update *update, int algorithm, bool placeholder)
+{
+ if (update == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *digest = NULL;
+ size_t dsize = 0;
+
+ knot_rrset_t exists = node_rrset(update->new_cont->apex, KNOT_RRTYPE_ZONEMD);
+ if (algorithm == ZONE_DIGEST_REMOVE) {
+ return zone_update_remove(update, &exists);
+ }
+ if (placeholder) {
+ if (!knot_rrset_empty(&exists) &&
+ !check_duplicate_schalg(&exists.rrs, exists.rrs.count,
+ KNOT_ZONEMD_SCHEME_SIMPLE, algorithm)) {
+ return KNOT_EOK;
+ }
+ } else {
+ int ret = zone_contents_digest(update->new_cont, algorithm, &digest, &dsize);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_update_remove(update, &exists);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ free(digest);
+ return ret;
+ }
+ }
+
+ knot_rrset_t zonemd, soa = node_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA);
+
+ uint8_t rdata[zonemd_hash_offs() + dsize];
+ wire_ctx_t wire = wire_ctx_init(rdata, sizeof(rdata));
+ wire_ctx_write_u32(&wire, knot_soa_serial(soa.rrs.rdata));
+ wire_ctx_write_u8(&wire, KNOT_ZONEMD_SCHEME_SIMPLE);
+ wire_ctx_write_u8(&wire, algorithm);
+ wire_ctx_write(&wire, digest, dsize);
+ assert(wire.error == KNOT_EOK && wire_ctx_available(&wire) == 0);
+
+ free(digest);
+
+ knot_rrset_init(&zonemd, update->new_cont->apex->owner, KNOT_RRTYPE_ZONEMD,
+ KNOT_CLASS_IN, soa.ttl);
+ int ret = knot_rrset_add_rdata(&zonemd, rdata, sizeof(rdata), NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_update_add(update, &zonemd);
+ knot_rdataset_clear(&zonemd.rrs, NULL);
+ return ret;
+}
diff --git a/src/knot/zone/digest.h b/src/knot/zone/digest.h
new file mode 100644
index 0000000..81d1617
--- /dev/null
+++ b/src/knot/zone/digest.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/contents.h"
+
+/*!
+ * \brief Compute hash over whole zone by concatenating RRSets in wire format.
+ *
+ * \param contents Zone contents to digest.
+ * \param algorithm Algorithm to use.
+ * \param out_digest Output: buffer with computed hash (to be freed).
+ * \param out_size Output: size of the resulting hash.
+ *
+ * \return KNOT_E*
+ */
+int zone_contents_digest(const zone_contents_t *contents, int algorithm,
+ uint8_t **out_digest, size_t *out_size);
+
+/*!
+ * \brief Check whether exactly one ZONEMD exists in the zone, is valid and matches given algorithm.
+ *
+ * \note Special value 255 of algorithm means that ZONEMD shall not exist.
+ *
+ * \param contents Zone contents to be verified.
+ * \param alg Required algorithm of the ZONEMD.
+ * \param no_verify Don't verify the validness of the digest in ZONEMD.
+ */
+bool zone_contents_digest_exists(const zone_contents_t *contents, int alg, bool no_verify);
+
+/*!
+ * \brief Verify zone dgest in ZONEMD record.
+ *
+ * \param contents Zone contents ot be verified.
+ *
+ * \retval KNOT_EEMPTYZONE The zone is empty.
+ * \retval KNOT_ENOENT There is no ZONEMD in contents' apex.
+ * \retval KNOT_ENOTSUP None of present ZONEMD is supported (scheme+algorithm+SOAserial).
+ * \retval KNOT_ESEMCHECK Duplicate ZONEMD with identical scheme+algorithm pair.
+ * \retval KNOT_EFEWDATA Error in hash length.
+ * \retval KNOT_EMALF The computed hash differs from ZONEMD.
+ * \return KNOT_E*
+ */
+int zone_contents_digest_verify(const zone_contents_t *contents);
+
+struct zone_update;
+/*!
+ * \brief Add ZONEMD record to zone_update.
+ *
+ * \param update Update with contents to be digested.
+ * \param algorithm ZONEMD algorithm.
+ * \param placeholder Don't calculate, just put placeholder (if ZONEMD not yet present).
+ *
+ * \note Special value 255 of algorithm means to remove ZONEMD.
+ *
+ * \return KNOT_E*
+ */
+int zone_update_add_digest(struct zone_update *update, int algorithm, bool placeholder);
diff --git a/src/knot/zone/measure.c b/src/knot/zone/measure.c
new file mode 100644
index 0000000..4c3ab5e
--- /dev/null
+++ b/src/knot/zone/measure.c
@@ -0,0 +1,133 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "knot/zone/measure.h"
+
+measure_t knot_measure_init(bool measure_whole, bool measure_diff)
+{
+ assert(!measure_whole || !measure_diff);
+ measure_t m = { 0 };
+ if (measure_whole) {
+ m.how_size = MEASURE_SIZE_WHOLE;
+ m.how_ttl = MEASURE_TTL_WHOLE;
+ }
+ if (measure_diff) {
+ m.how_size = MEASURE_SIZE_DIFF;
+ m.how_ttl = MEASURE_TTL_DIFF;
+ }
+ return m;
+}
+
+bool knot_measure_node(zone_node_t *node, measure_t *m)
+{
+ if (m->how_size == MEASURE_SIZE_NONE && (m->how_ttl == MEASURE_TTL_NONE ||
+ (m->how_ttl == MEASURE_TTL_LIMIT && m->max_ttl >= m->limit_max_ttl))) {
+ return false;
+ }
+
+ int rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; i++) {
+ if (m->how_size != MEASURE_SIZE_NONE) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ m->zone_size += knot_rrset_size(&rrset);
+ }
+ if (m->how_ttl != MEASURE_TTL_NONE) {
+ m->max_ttl = MAX(m->max_ttl, node->rrs[i].ttl);
+ }
+ }
+
+ if (m->how_size != MEASURE_SIZE_DIFF && m->how_ttl != MEASURE_TTL_DIFF) {
+ return true;
+ }
+
+ node = binode_counterpart(node);
+ rrset_count = node->rrset_count;
+ for (int i = 0; i < rrset_count; i++) {
+ if (m->how_size == MEASURE_SIZE_DIFF) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ m->zone_size -= knot_rrset_size(&rrset);
+ }
+ if (m->how_ttl == MEASURE_TTL_DIFF) {
+ m->rem_max_ttl = MAX(m->rem_max_ttl, node->rrs[i].ttl);
+ }
+ }
+
+ return true;
+}
+
+static uint32_t re_measure_max_ttl(zone_contents_t *zone, uint32_t limit)
+{
+ measure_t m = {0 };
+ m.how_ttl = MEASURE_TTL_LIMIT;
+ m.limit_max_ttl = limit;
+
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_double_begin(zone->nodes, zone->nsec3_nodes, &it);
+ if (ret != KNOT_EOK) {
+ return limit;
+ }
+
+ while (!zone_tree_it_finished(&it) && knot_measure_node(zone_tree_it_val(&it), &m)) {
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+
+ return m.max_ttl;
+}
+
+void knot_measure_finish_zone(measure_t *m, zone_contents_t *zone)
+{
+ assert(m->how_size == MEASURE_SIZE_WHOLE || m->how_size == MEASURE_SIZE_NONE);
+ assert(m->how_ttl == MEASURE_TTL_WHOLE || m->how_ttl == MEASURE_TTL_NONE);
+ if (m->how_size == MEASURE_SIZE_WHOLE) {
+ zone->size = m->zone_size;
+ }
+ if (m->how_ttl == MEASURE_TTL_WHOLE) {
+ zone->max_ttl = m->max_ttl;
+ }
+}
+
+void knot_measure_finish_update(measure_t *m, zone_update_t *update)
+{
+ switch (m->how_size) {
+ case MEASURE_SIZE_NONE:
+ break;
+ case MEASURE_SIZE_WHOLE:
+ update->new_cont->size = m->zone_size;
+ break;
+ case MEASURE_SIZE_DIFF:
+ update->new_cont->size = update->zone->contents->size + m->zone_size;
+ break;
+ }
+
+ switch (m->how_ttl) {
+ case MEASURE_TTL_NONE:
+ break;
+ case MEASURE_TTL_WHOLE:
+ case MEASURE_TTL_LIMIT:
+ update->new_cont->max_ttl = m->max_ttl;
+ break;
+ case MEASURE_TTL_DIFF:
+ if (m->max_ttl >= update->zone->contents->max_ttl) {
+ update->new_cont->max_ttl = m->max_ttl;
+ } else if (update->zone->contents->max_ttl > m->rem_max_ttl) {
+ update->new_cont->max_ttl = update->zone->contents->max_ttl;
+ } else {
+ update->new_cont->max_ttl = re_measure_max_ttl(update->new_cont, update->zone->contents->max_ttl);
+ }
+ break;
+ }
+}
diff --git a/src/knot/zone/measure.h b/src/knot/zone/measure.h
new file mode 100644
index 0000000..5c73c91
--- /dev/null
+++ b/src/knot/zone/measure.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "knot/updates/zone-update.h"
+
+typedef enum {
+ MEASURE_SIZE_NONE = 0, // don't measure size of zone
+ MEASURE_SIZE_WHOLE, // measure complete size of zone nodes
+ MEASURE_SIZE_DIFF, // measure difference in size for bi-nodes in zone update
+} measure_size_t;
+
+typedef enum {
+ MEASURE_TTL_NONE = 0, // don't measure max TTL of zone records
+ MEASURE_TTL_WHOLE, // measure max TTL among all zone records
+ MEASURE_TTL_DIFF, // check out zone update (bi-nodes) if the max TTL is affected
+ MEASURE_TTL_LIMIT, // measure max TTL whole; stop if a specific value is reached
+} measure_ttl_t;
+
+typedef struct {
+ measure_size_t how_size;
+ measure_ttl_t how_ttl;
+ ssize_t zone_size;
+ uint32_t max_ttl;
+ uint32_t rem_max_ttl;
+ uint32_t limit_max_ttl;
+} measure_t;
+
+/*! \brief Initialize measure struct. */
+measure_t knot_measure_init(bool measure_whole, bool measure_diff);
+
+/*!
+ * \brief Measure one node's size and max TTL, collecting into measure struct.
+ *
+ * \param node Node to be measured.
+ * \param m Measure context with instructions and results.
+ *
+ * \return False if no more measure is needed.
+ * \note You will probably ignore the return value.
+ */
+bool knot_measure_node(zone_node_t *node, measure_t *m);
+
+/*!
+ * \brief Collect the measured results and update the new zone with measured properties.
+ *
+ * \param zone Zone.
+ * \param m Measured results.
+ */
+void knot_measure_finish_zone(measure_t *m, zone_contents_t *zone);
+
+/*!
+ * \brief Collect the measured results and update the new zone with measured properties.
+ *
+ * \param update Zone update with the zone.
+ * \param m Measured results.
+ */
+void knot_measure_finish_update(measure_t *m, zone_update_t *update);
diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c
new file mode 100644
index 0000000..291454b
--- /dev/null
+++ b/src/knot/zone/node.c
@@ -0,0 +1,464 @@
+/* Copyright (C) 2021 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 "knot/zone/node.h"
+#include "libknot/libknot.h"
+
+void additional_clear(additional_t *additional)
+{
+ if (additional == NULL) {
+ return;
+ }
+
+ free(additional->glues);
+ free(additional);
+}
+
+bool additional_equal(additional_t *a, additional_t *b)
+{
+ if (a == NULL || b == NULL || a->count != b->count) {
+ return false;
+ }
+ for (int i = 0; i < a->count; i++) {
+ glue_t *ag = &a->glues[i], *bg = &b->glues[i];
+ if (ag->ns_pos != bg->ns_pos || ag->optional != bg->optional ||
+ binode_first((zone_node_t *)ag->node) != binode_first((zone_node_t *)bg->node)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/*! \brief Clears allocated data in RRSet entry. */
+static void rr_data_clear(struct rr_data *data, knot_mm_t *mm)
+{
+ knot_rdataset_clear(&data->rrs, mm);
+ memset(data, 0, sizeof(*data));
+}
+
+/*! \brief Clears allocated data in RRSet entry. */
+static int rr_data_from(const knot_rrset_t *rrset, struct rr_data *data, knot_mm_t *mm)
+{
+ int ret = knot_rdataset_copy(&data->rrs, &rrset->rrs, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ data->ttl = rrset->ttl;
+ data->type = rrset->type;
+ data->additional = NULL;
+
+ return KNOT_EOK;
+}
+
+/*! \brief Adds RRSet to node directly. */
+static int add_rrset_no_merge(zone_node_t *node, const knot_rrset_t *rrset,
+ knot_mm_t *mm)
+{
+ if (node == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const size_t prev_nlen = node->rrset_count * sizeof(struct rr_data);
+ const size_t nlen = (node->rrset_count + 1) * sizeof(struct rr_data);
+ void *p = mm_realloc(mm, node->rrs, nlen, prev_nlen);
+ if (p == NULL) {
+ return KNOT_ENOMEM;
+ }
+ node->rrs = p;
+
+ // ensure rrsets are sorted by rrtype
+ struct rr_data *insert_pos = node->rrs, *end = node->rrs + node->rrset_count;
+ while (insert_pos != end && insert_pos->type < rrset->type) {
+ insert_pos++;
+ }
+ memmove(insert_pos + 1, insert_pos, (uint8_t *)end - (uint8_t *)insert_pos);
+
+ int ret = rr_data_from(rrset, insert_pos, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ++node->rrset_count;
+
+ return KNOT_EOK;
+}
+
+/*! \brief Checks if the added RR has the same TTL as the first RR in the node. */
+static bool ttl_changed(struct rr_data *node_data, const knot_rrset_t *rrset)
+{
+ if (rrset->type == KNOT_RRTYPE_RRSIG || node_data->rrs.count == 0) {
+ return false;
+ }
+
+ return rrset->ttl != node_data->ttl;
+}
+
+zone_node_t *node_new(const knot_dname_t *owner, bool binode, bool second, knot_mm_t *mm)
+{
+ zone_node_t *ret = mm_alloc(mm, (binode ? 2 : 1) * sizeof(zone_node_t));
+ if (ret == NULL) {
+ return NULL;
+ }
+ memset(ret, 0, sizeof(*ret));
+
+ if (owner) {
+ ret->owner = knot_dname_copy(owner, mm);
+ if (ret->owner == NULL) {
+ mm_free(mm, ret);
+ return NULL;
+ }
+ }
+
+ // Node is authoritative by default.
+ ret->flags = NODE_FLAGS_AUTH;
+
+ if (binode) {
+ ret->flags |= NODE_FLAGS_BINODE;
+ if (second) {
+ ret->flags |= NODE_FLAGS_DELETED;
+ }
+ memcpy(ret + 1, ret, sizeof(*ret));
+ (ret + 1)->flags ^= NODE_FLAGS_SECOND | NODE_FLAGS_DELETED;
+ }
+
+ return ret;
+}
+
+zone_node_t *binode_counterpart(zone_node_t *node)
+{
+ zone_node_t *counterpart = NULL;
+
+ assert(node == NULL || (node->flags & NODE_FLAGS_BINODE) || !(node->flags & NODE_FLAGS_SECOND));
+ if (node != NULL && (node->flags & NODE_FLAGS_BINODE)) {
+ if ((node->flags & NODE_FLAGS_SECOND)) {
+ counterpart = node - 1;
+ assert(!(counterpart->flags & NODE_FLAGS_SECOND));
+ } else {
+ counterpart = node + 1;
+ assert((counterpart->flags & NODE_FLAGS_SECOND));
+ }
+ assert((counterpart->flags & NODE_FLAGS_BINODE));
+ }
+
+ return counterpart;
+}
+
+void binode_unify(zone_node_t *node, bool free_deleted, knot_mm_t *mm)
+{
+ zone_node_t *counter = binode_counterpart(node);
+ if (counter != NULL) {
+ if (counter->rrs != node->rrs) {
+ for (uint16_t i = 0; i < counter->rrset_count; ++i) {
+ if (!binode_additional_shared(node, counter->rrs[i].type)) {
+ additional_clear(counter->rrs[i].additional);
+ }
+ if (!binode_rdata_shared(node, counter->rrs[i].type)) {
+ rr_data_clear(&counter->rrs[i], mm);
+ }
+ }
+ mm_free(mm, counter->rrs);
+ }
+ if (counter->nsec3_wildcard_name != node->nsec3_wildcard_name) {
+ free(counter->nsec3_wildcard_name);
+ }
+ if (!(counter->flags & NODE_FLAGS_NSEC3_NODE) && node->nsec3_hash != counter->nsec3_hash) {
+ free(counter->nsec3_hash);
+ }
+ assert(((node->flags ^ counter->flags) & NODE_FLAGS_SECOND));
+ memcpy(counter, node, sizeof(*counter));
+ counter->flags ^= NODE_FLAGS_SECOND;
+
+ if (free_deleted && (node->flags & NODE_FLAGS_DELETED)) {
+ node_free(node, mm);
+ }
+ }
+}
+
+int binode_prepare_change(zone_node_t *node, knot_mm_t *mm)
+{
+ zone_node_t *counter = binode_counterpart(node);
+ if (counter != NULL && counter->rrs == node->rrs && counter->rrs != NULL) {
+ size_t rrlen = sizeof(struct rr_data) * counter->rrset_count;
+ node->rrs = mm_alloc(mm, rrlen);
+ if (node->rrs == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(node->rrs, counter->rrs, rrlen);
+ }
+ return KNOT_EOK;
+}
+
+bool binode_rdata_shared(zone_node_t *node, uint16_t type)
+{
+ if (node == NULL || !(node->flags & NODE_FLAGS_BINODE)) {
+ return false;
+ }
+ zone_node_t *counterpart = ((node->flags & NODE_FLAGS_SECOND) ? node - 1 : node + 1);
+ if (counterpart->rrs == node->rrs) {
+ return true;
+ }
+ knot_rdataset_t *r1 = node_rdataset(node, type), *r2 = node_rdataset(counterpart, type);
+ return (r1 != NULL && r2 != NULL && r1->rdata == r2->rdata);
+}
+
+static additional_t *node_type2addit(zone_node_t *node, uint16_t type)
+{
+ for (uint16_t i = 0; i < node->rrset_count; i++) {
+ if (node->rrs[i].type == type) {
+ return node->rrs[i].additional;
+ }
+ }
+ return NULL;
+}
+
+bool binode_additional_shared(zone_node_t *node, uint16_t type)
+{
+ if (node == NULL || !(node->flags & NODE_FLAGS_BINODE)) {
+ return false;
+ }
+ zone_node_t *counter = ((node->flags & NODE_FLAGS_SECOND) ? node - 1 : node + 1);
+ if (counter->rrs == node->rrs) {
+ return true;
+ }
+ additional_t *a1 = node_type2addit(node, type), *a2 = node_type2addit(counter, type);
+ return (a1 == a2);
+}
+
+bool binode_additionals_unchanged(zone_node_t *node, zone_node_t *counterpart)
+{
+ if (node == NULL || counterpart == NULL) {
+ return false;
+ }
+ if (counterpart->rrs == node->rrs) {
+ return true;
+ }
+ for (int i = 0; i < node->rrset_count; i++) {
+ struct rr_data *rr = &node->rrs[i];
+ if (knot_rrtype_additional_needed(rr->type)) {
+ knot_rdataset_t *counterr = node_rdataset(counterpart, rr->type);
+ if (counterr == NULL || counterr->rdata != rr->rrs.rdata) {
+ return false;
+ }
+ }
+ }
+ for (int i = 0; i < counterpart->rrset_count; i++) {
+ struct rr_data *rr = &counterpart->rrs[i];
+ if (knot_rrtype_additional_needed(rr->type)) {
+ knot_rdataset_t *counterr = node_rdataset(node, rr->type);
+ if (counterr == NULL || counterr->rdata != rr->rrs.rdata) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void node_free_rrsets(zone_node_t *node, knot_mm_t *mm)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ additional_clear(node->rrs[i].additional);
+ rr_data_clear(&node->rrs[i], mm);
+ }
+
+ mm_free(mm, node->rrs);
+ node->rrs = NULL;
+ node->rrset_count = 0;
+}
+
+void node_free(zone_node_t *node, knot_mm_t *mm)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ knot_dname_free(node->owner, mm);
+
+ assert((node->flags & NODE_FLAGS_BINODE) || !(node->flags & NODE_FLAGS_SECOND));
+ assert(binode_counterpart(node) == NULL ||
+ binode_counterpart(node)->nsec3_wildcard_name == node->nsec3_wildcard_name);
+
+ free(node->nsec3_wildcard_name);
+ if (!(node->flags & NODE_FLAGS_NSEC3_NODE)) {
+ free(node->nsec3_hash);
+ }
+
+ if (node->rrs != NULL) {
+ mm_free(mm, node->rrs);
+ }
+
+ mm_free(mm, binode_node(node, false));
+}
+
+int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm)
+{
+ if (node == NULL || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ node->flags &= ~NODE_FLAGS_RRSIGS_VALID;
+
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == rrset->type) {
+ struct rr_data *node_data = &node->rrs[i];
+ const bool ttl_change = ttl_changed(node_data, rrset);
+ if (ttl_change) {
+ node_data->ttl = rrset->ttl;
+ }
+
+ int ret = knot_rdataset_merge(&node_data->rrs,
+ &rrset->rrs, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else {
+ return ttl_change ? KNOT_ETTL : KNOT_EOK;
+ }
+ }
+ }
+
+ // New RRSet (with one RR)
+ return add_rrset_no_merge(node, rrset, mm);
+}
+
+void node_remove_rdataset(zone_node_t *node, uint16_t type)
+{
+ if (node == NULL) {
+ return;
+ }
+
+ node->flags &= ~NODE_FLAGS_RRSIGS_VALID;
+
+ for (int i = 0; i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == type) {
+ if (!binode_additional_shared(node, type)) {
+ additional_clear(node->rrs[i].additional);
+ }
+ if (!binode_rdata_shared(node, type)) {
+ rr_data_clear(&node->rrs[i], NULL);
+ }
+ memmove(node->rrs + i, node->rrs + i + 1,
+ (node->rrset_count - i - 1) * sizeof(struct rr_data));
+ --node->rrset_count;
+ return;
+ }
+ }
+}
+
+int node_remove_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm)
+{
+ if (node == NULL || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type);
+ if (node_rrs == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ node->flags &= ~NODE_FLAGS_RRSIGS_VALID;
+
+ int ret = knot_rdataset_subtract(node_rrs, &rrset->rrs, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (node_rrs->count == 0) {
+ node_remove_rdataset(node, rrset->type);
+ }
+
+ return KNOT_EOK;
+}
+
+knot_rrset_t *node_create_rrset(const zone_node_t *node, uint16_t type)
+{
+ if (node == NULL) {
+ return NULL;
+ }
+
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == type) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ return knot_rrset_copy(&rrset, NULL);
+ }
+ }
+
+ return NULL;
+}
+
+knot_rdataset_t *node_rdataset(const zone_node_t *node, uint16_t type)
+{
+ if (node == NULL) {
+ return NULL;
+ }
+
+ for (uint16_t i = 0; i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == type) {
+ return &node->rrs[i].rrs;
+ }
+ }
+
+ return NULL;
+}
+
+bool node_rrtype_is_signed(const zone_node_t *node, uint16_t type)
+{
+ if (node == NULL) {
+ return false;
+ }
+
+ const knot_rdataset_t *rrsigs = node_rdataset(node, KNOT_RRTYPE_RRSIG);
+ if (rrsigs == NULL) {
+ return false;
+ }
+
+ uint16_t rrsigs_rdata_count = rrsigs->count;
+ knot_rdata_t *rrsig = rrsigs->rdata;
+ for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) {
+ if (knot_rrsig_type_covered(rrsig) == type) {
+ return true;
+ }
+ rrsig = knot_rdataset_next(rrsig);
+ }
+
+ return false;
+}
+
+bool node_bitmap_equal(const zone_node_t *a, const zone_node_t *b)
+{
+ if (a == NULL || b == NULL || a->rrset_count != b->rrset_count) {
+ return false;
+ }
+
+ uint16_t i;
+ // heuristics: try if they are equal including order
+ for (i = 0; i < a->rrset_count; i++) {
+ if (a->rrs[i].type != b->rrs[i].type) {
+ break;
+ }
+ }
+ if (i == a->rrset_count) {
+ return true;
+ }
+
+ for (i = 0; i < a->rrset_count; i++) {
+ if (node_rdataset(b, a->rrs[i].type) == NULL) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h
new file mode 100644
index 0000000..d30cc6e
--- /dev/null
+++ b/src/knot/zone/node.h
@@ -0,0 +1,419 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "libknot/descriptor.h"
+#include "libknot/dname.h"
+#include "libknot/rrset.h"
+#include "libknot/rdataset.h"
+
+struct rr_data;
+
+/*!
+ * \brief Structure representing one node in a domain name tree, i.e. one domain
+ * name in a zone.
+ */
+typedef struct zone_node {
+ knot_dname_t *owner; /*!< Domain name being the owner of this node. */
+ struct zone_node *parent; /*!< Parent node in the name hierarchy. */
+
+ /*! \brief Array with data of RRSets belonging to this node. */
+ struct rr_data *rrs;
+
+ /*!
+ * \brief Previous node in canonical order. Only authoritative
+ * nodes or delegation points are referenced by this.
+ */
+ struct zone_node *prev;
+ union {
+ knot_dname_t *nsec3_hash; /*! Name of the NSEC3 corresponding to this node. */
+ struct zone_node *nsec3_node; /*! NSEC3 node corresponding to this node.
+ \warning This always points to first part of that bi-node!
+ assert(!(node->nsec3_node & NODE_FLAGS_SECOND)); */
+ };
+ knot_dname_t *nsec3_wildcard_name; /*! Name of NSEC3 node proving wildcard nonexistence. */
+ uint32_t children; /*!< Count of children nodes in DNS hierarchy. */
+ uint16_t rrset_count; /*!< Number of RRSets stored in the node. */
+ uint16_t flags; /*!< \ref node_flags enum. */
+} zone_node_t;
+
+/*!< \brief Glue node context. */
+typedef struct {
+ const zone_node_t *node; /*!< Glue node. */
+ uint16_t ns_pos; /*!< Corresponding NS record position (for compression). */
+ bool optional; /*!< Optional glue indicator. */
+} glue_t;
+
+/*!< \brief Additional data. */
+typedef struct {
+ glue_t *glues; /*!< Glue data. */
+ uint16_t count; /*!< Number of glue nodes. */
+} additional_t;
+
+/*!< \brief Structure storing RR data. */
+struct rr_data {
+ uint32_t ttl; /*!< RRSet TTL. */
+ uint16_t type; /*!< RR type of data. */
+ knot_rdataset_t rrs; /*!< Data of given type. */
+ additional_t *additional; /*!< Additional nodes with glues. */
+};
+
+/*! \brief Flags used to mark nodes with some property. */
+enum node_flags {
+ /*! \brief Node is authoritative, default. */
+ NODE_FLAGS_AUTH = 0 << 0,
+ /*! \brief Node is a delegation point (i.e. marking a zone cut). */
+ NODE_FLAGS_DELEG = 1 << 0,
+ /*! \brief Node is not authoritative (i.e. below a zone cut). */
+ NODE_FLAGS_NONAUTH = 1 << 1,
+ /*! \brief RRSIGs in node have been cryptographically validated by Knot. */
+ NODE_FLAGS_RRSIGS_VALID = 1 << 2,
+ /*! \brief Node is empty and will be deleted after update. */
+ NODE_FLAGS_EMPTY = 1 << 3,
+ /*! \brief Node has a wildcard child. */
+ NODE_FLAGS_WILDCARD_CHILD = 1 << 4,
+ /*! \brief Is this NSEC3 node compatible with zone's NSEC3PARAMS ? */
+ NODE_FLAGS_IN_NSEC3_CHAIN = 1 << 5,
+ /*! \brief Node is the zone Apex. */
+ NODE_FLAGS_APEX = 1 << 6,
+ /*! \brief The nsec3_node pointer is valid and and nsec3_hash pointer invalid. */
+ NODE_FLAGS_NSEC3_NODE = 1 << 7,
+ /*! \brief Is this i bi-node? */
+ NODE_FLAGS_BINODE = 1 << 8, // this value shall be fixed
+ /*! \brief Is this the second half of bi-node? */
+ NODE_FLAGS_SECOND = 1 << 9, // this value shall be fixed
+ /*! \brief The node shall be deleted. It's just not because it's a bi-node and the counterpart still exists. */
+ NODE_FLAGS_DELETED = 1 << 10,
+ /*! \brief The node or some node in subtree has some authoritative data in it (possibly also DS at deleg). */
+ NODE_FLAGS_SUBTREE_AUTH = 1 << 11,
+ /*! \brief The node or some node in subtree has any data in it, possibly just insec deleg. */
+ NODE_FLAGS_SUBTREE_DATA = 1 << 12,
+};
+
+typedef void (*node_addrem_cb)(zone_node_t *, void *);
+typedef zone_node_t *(*node_new_cb)(const knot_dname_t *, void *);
+
+/*!
+ * \brief Clears additional structure.
+ *
+ * \param additional Additional to clear.
+ */
+void additional_clear(additional_t *additional);
+
+/*!
+ * \brief Compares additional structures on equivalency.
+ */
+bool additional_equal(additional_t *a, additional_t *b);
+
+/*!
+ * \brief Creates and initializes new node structure.
+ *
+ * \param owner Node's owner, will be duplicated.
+ * \param binode Create bi-node.
+ * \param second The second part of the bi-node shall be used now.
+ * \param mm Memory context to use.
+ *
+ * \return Newly created node or NULL if an error occurred.
+ */
+zone_node_t *node_new(const knot_dname_t *owner, bool binode, bool second, knot_mm_t *mm);
+
+/*!
+ * \brief Synchronize contents of both binode's nodes.
+ *
+ * \param node Pointer to either of nodes in a binode.
+ * \param free_deleted When the unified node has DELETED flag, free it afterwards.
+ * \param mm Memory context.
+ */
+void binode_unify(zone_node_t *node, bool free_deleted, knot_mm_t *mm);
+
+/*!
+ * \brief This must be called before any change to either of the bi-node's node's rdatasets.
+ */
+int binode_prepare_change(zone_node_t *node, knot_mm_t *mm);
+
+/*!
+ * \brief Get the correct node of a binode.
+ *
+ * \param node Pointer to either of nodes in a binode.
+ * \param second Get the second node (first otherwise).
+ *
+ * \return Pointer to correct node.
+ */
+inline static zone_node_t *binode_node(zone_node_t *node, bool second)
+{
+ if (unlikely(node == NULL || !(node->flags & NODE_FLAGS_BINODE))) {
+ assert(node == NULL || !(node->flags & NODE_FLAGS_SECOND));
+ return node;
+ }
+ return node + (second - (int)((node->flags & NODE_FLAGS_SECOND) >> 9));
+}
+
+inline static zone_node_t *binode_first(zone_node_t *node)
+{
+ return binode_node(node, false);
+}
+
+inline static zone_node_t *binode_node_as(zone_node_t *node, const zone_node_t *as)
+{
+ assert(node == NULL || (as->flags & NODE_FLAGS_BINODE) == (node->flags & NODE_FLAGS_BINODE));
+ return binode_node(node, (as->flags & NODE_FLAGS_SECOND));
+}
+
+/*!
+ * \brief Return the other node from a bi-node.
+ *
+ * \param node A node in a bi-node.
+ *
+ * \return The counterpart node in the same bi-node.
+ */
+zone_node_t *binode_counterpart(zone_node_t *node);
+
+/*!
+ * \brief Return true if the rdataset of specified type is shared (shallow-copied) among both parts of bi-node.
+ */
+bool binode_rdata_shared(zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Return true if the additionals to rdataset of specified type are shared among both parts of bi-node.
+ */
+bool binode_additional_shared(zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Return true if the additionals are unchanged between two nodes (usually a bi-node).
+ */
+bool binode_additionals_unchanged(zone_node_t *node, zone_node_t *counterpart);
+
+/*!
+ * \brief Destroys allocated data within the node
+ * structure, but not the node itself.
+ *
+ * \param node Node that contains data to be destroyed.
+ * \param mm Memory context to use.
+ */
+void node_free_rrsets(zone_node_t *node, knot_mm_t *mm);
+
+/*!
+ * \brief Destroys the node structure.
+ *
+ * Does not destroy the data within the node.
+ *
+ * \param node Node to be destroyed.
+ * \param mm Memory context to use.
+ */
+void node_free(zone_node_t *node, knot_mm_t *mm);
+
+/*!
+ * \brief Adds an RRSet to the node. All data are copied. Owner and class are
+ * not used at all.
+ *
+ * \param node Node to add the RRSet to.
+ * \param rrset RRSet to add.
+ * \param mm Memory context to use.
+ *
+ * \return KNOT_E*
+ * \retval KNOT_ETTL RRSet TTL was updated.
+ */
+int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm);
+
+/*!
+ * \brief Removes data for given RR type from node.
+ *
+ * \param node Node we want to delete from.
+ * \param type RR type to delete.
+ */
+void node_remove_rdataset(zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Remove all RRs from RRSet from the node.
+ *
+ * \param node Node to remove from.
+ * \param rrset RRSet with RRs to be removed.
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+int node_remove_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm);
+
+/*!
+ * \brief Returns the RRSet of the given type from the node. RRSet is allocated.
+ *
+ * \param node Node to get the RRSet from.
+ * \param type RR type of the RRSet to retrieve.
+ *
+ * \return RRSet from node \a node having type \a type, or NULL if no such
+ * RRSet exists in this node.
+ */
+knot_rrset_t *node_create_rrset(const zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Gets rdata set structure of given type from node.
+ *
+ * \param node Node to get data from.
+ * \param type RR type of data to get.
+ *
+ * \return Pointer to data if found, NULL otherwise.
+ */
+knot_rdataset_t *node_rdataset(const zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Returns parent node (fixing bi-node issue) of given node.
+ */
+inline static zone_node_t *node_parent(const zone_node_t *node)
+{
+ return binode_node_as(node->parent, node);
+}
+
+/*!
+ * \brief Returns previous (lexicographically in same zone tree) node (fixing bi-node issue) of given node.
+ */
+inline static zone_node_t *node_prev(const zone_node_t *node)
+{
+ return binode_node_as(node->prev, node);
+}
+
+/*!
+ * \brief Return node referenced by a glue.
+ *
+ * \param glue Glue in question.
+ * \param another_zone_node Another node from the same zone.
+ *
+ * \return Glue node.
+ */
+inline static const zone_node_t *glue_node(const glue_t *glue, const zone_node_t *another_zone_node)
+{
+ return binode_node_as((zone_node_t *)glue->node, another_zone_node);
+}
+
+/*!
+ * \brief Add a flag to this node and all (grand-)parents until the flag is present.
+ */
+inline static void node_set_flag_hierarch(zone_node_t *node, uint16_t fl)
+{
+ for (zone_node_t *i = node; i != NULL && (i->flags & fl) != fl; i = node_parent(i)) {
+ i->flags |= fl;
+ }
+}
+
+/*!
+ * \brief Checks whether node contains any RRSIG for given type.
+ *
+ * \param node Node to check in.
+ * \param type Type to check for.
+ *
+ * \return True/False.
+ */
+bool node_rrtype_is_signed(const zone_node_t *node, uint16_t type);
+
+/*!
+ * \brief Checks whether node contains RRSet for given type.
+ *
+ * \param node Node to check in.
+ * \param type Type to check for.
+ *
+ * \return True/False.
+ */
+inline static bool node_rrtype_exists(const zone_node_t *node, uint16_t type)
+{
+ return node_rdataset(node, type) != NULL;
+}
+
+/*!
+ * \brief Checks whether node is empty. Node is empty when NULL or when no
+ * RRSets are in it.
+ *
+ * \param node Node to check in.
+ *
+ * \return True/False.
+ */
+inline static bool node_empty(const zone_node_t *node)
+{
+ return node == NULL || node->rrset_count == 0;
+}
+
+/*!
+ * \brief Check whether two nodes have equal set of rrtypes.
+ *
+ * \param a A node.
+ * \param b Another node.
+ *
+ * \return True/False.
+ */
+bool node_bitmap_equal(const zone_node_t *a, const zone_node_t *b);
+
+/*!
+ * \brief Returns RRSet structure initialized with data from node.
+ *
+ * \param node Node containing RRSet.
+ * \param type RRSet type we want to get.
+ *
+ * \return RRSet structure with wanted type, or empty RRSet.
+ */
+static inline knot_rrset_t node_rrset(const zone_node_t *node, uint16_t type)
+{
+ knot_rrset_t rrset;
+ for (uint16_t i = 0; node && i < node->rrset_count; ++i) {
+ if (node->rrs[i].type == type) {
+ struct rr_data *rr_data = &node->rrs[i];
+ knot_rrset_init(&rrset, node->owner, type, KNOT_CLASS_IN,
+ rr_data->ttl);
+ rrset.rrs = rr_data->rrs;
+ rrset.additional = rr_data->additional;
+ return rrset;
+ }
+ }
+ knot_rrset_init_empty(&rrset);
+ return rrset;
+}
+
+/*!
+ * \brief Returns RRSet structure initialized with data from node at position
+ * equal to \a pos.
+ *
+ * \param node Node containing RRSet.
+ * \param pos RRSet position we want to get.
+ *
+ * \return RRSet structure with data from wanted position, or empty RRSet.
+ */
+static inline knot_rrset_t node_rrset_at(const zone_node_t *node, size_t pos)
+{
+ knot_rrset_t rrset;
+ if (node == NULL || pos >= node->rrset_count) {
+ knot_rrset_init_empty(&rrset);
+ return rrset;
+ }
+
+ struct rr_data *rr_data = &node->rrs[pos];
+ knot_rrset_init(&rrset, node->owner, rr_data->type, KNOT_CLASS_IN,
+ rr_data->ttl);
+ rrset.rrs = rr_data->rrs;
+ rrset.additional = rr_data->additional;
+ return rrset;
+}
+
+/*!
+ * \brief Return the relevant NSEC3 node (if specified by adjusting), or NULL.
+ */
+static inline zone_node_t *node_nsec3_get(const zone_node_t *node)
+{
+ if (!(node->flags & NODE_FLAGS_NSEC3_NODE) || node->nsec3_node == NULL) {
+ return NULL;
+ } else {
+ return binode_node_as(node->nsec3_node, node);
+ }
+}
diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c
new file mode 100644
index 0000000..b3f1930
--- /dev/null
+++ b/src/knot/zone/semantic-check.c
@@ -0,0 +1,562 @@
+/* Copyright (C) 2022 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 <stdio.h>
+
+#include "knot/zone/semantic-check.h"
+
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "contrib/string.h"
+#include "libknot/libknot.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/updates/zone-update.h"
+
+static const char *error_messages[SEM_ERR_UNKNOWN + 1] = {
+ [SEM_ERR_SOA_NONE] =
+ "missing SOA at the zone apex",
+
+ [SEM_ERR_CNAME_EXTRA_RECORDS] =
+ "another record exists beside CNAME",
+ [SEM_ERR_CNAME_MULTIPLE] =
+ "multiple CNAME records",
+
+ [SEM_ERR_DNAME_CHILDREN] =
+ "child record exists under DNAME",
+ [SEM_ERR_DNAME_MULTIPLE] =
+ "multiple DNAME records",
+ [SEM_ERR_DNAME_EXTRA_NS] =
+ "NS record exists beside DNAME",
+
+ [SEM_ERR_NS_APEX] =
+ "missing NS at the zone apex",
+ [SEM_ERR_NS_GLUE] =
+ "missing glue record",
+
+ [SEM_ERR_RRSIG_UNVERIFIABLE] =
+ "no valid signature for a record",
+
+ [SEM_ERR_NSEC_NONE] =
+ "missing NSEC(3) record",
+ [SEM_ERR_NSEC_RDATA_BITMAP] =
+ "wrong NSEC(3) bitmap",
+ [SEM_ERR_NSEC_RDATA_CHAIN] =
+ "inconsistent NSEC(3) chain",
+ [SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT] =
+ "wrong NSEC3 opt-out",
+
+ [SEM_ERR_NSEC3PARAM_RDATA_FLAGS] =
+ "invalid flags in NSEC3PARAM",
+ [SEM_ERR_NSEC3PARAM_RDATA_ALG] =
+ "invalid algorithm in NSEC3PARAM",
+
+ [SEM_ERR_DS_RDATA_ALG] =
+ "invalid algorithm in DS",
+ [SEM_ERR_DS_RDATA_DIGLEN] =
+ "invalid digest length in DS",
+
+ [SEM_ERR_DNSKEY_NONE] =
+ "missing DNSKEY",
+ [SEM_ERR_DNSKEY_INVALID] =
+ "invalid DNSKEY",
+
+ [SEM_ERR_CDS_NONE] =
+ "missing CDS",
+ [SEM_ERR_CDS_NOT_MATCH] =
+ "CDS not match CDNSKEY",
+
+ [SEM_ERR_CDNSKEY_NONE] =
+ "missing CDNSKEY",
+ [SEM_ERR_CDNSKEY_NO_DNSKEY] =
+ "CDNSKEY not match DNSKEY",
+ [SEM_ERR_CDNSKEY_NO_CDS] =
+ "CDNSKEY without corresponding CDS",
+ [SEM_ERR_CDNSKEY_INVALID_DELETE] =
+ "invalid CDNSKEY/CDS for DNSSEC delete algorithm",
+
+ [SEM_ERR_UNKNOWN] =
+ "unknown error"
+};
+
+const char *sem_error_msg(sem_error_t code)
+{
+ if (code > SEM_ERR_UNKNOWN) {
+ code = SEM_ERR_UNKNOWN;
+ }
+ return error_messages[code];
+}
+
+typedef enum {
+ MANDATORY = 1 << 0,
+ SOFT = 1 << 1,
+ OPTIONAL = 1 << 2,
+ DNSSEC = 1 << 3,
+} check_level_t;
+
+typedef struct {
+ zone_contents_t *zone;
+ sem_handler_t *handler;
+ check_level_t level;
+ time_t time;
+} semchecks_data_t;
+
+static int check_soa(const zone_node_t *node, semchecks_data_t *data);
+static int check_cname(const zone_node_t *node, semchecks_data_t *data);
+static int check_dname(const zone_node_t *node, semchecks_data_t *data);
+static int check_delegation(const zone_node_t *node, semchecks_data_t *data);
+static int check_nsec3param(const zone_node_t *node, semchecks_data_t *data);
+static int check_submission(const zone_node_t *node, semchecks_data_t *data);
+static int check_ds(const zone_node_t *node, semchecks_data_t *data);
+
+struct check_function {
+ int (*function)(const zone_node_t *, semchecks_data_t *);
+ check_level_t level;
+};
+
+static const struct check_function CHECK_FUNCTIONS[] = {
+ { check_soa, MANDATORY },
+ { check_cname, MANDATORY | SOFT },
+ { check_dname, MANDATORY | SOFT },
+ { check_delegation, MANDATORY | SOFT }, // mandatory for apex, optional for others
+ { check_ds, OPTIONAL },
+ { check_nsec3param, DNSSEC },
+ { check_submission, DNSSEC },
+};
+
+static const int CHECK_FUNCTIONS_LEN = sizeof(CHECK_FUNCTIONS)
+ / sizeof(struct check_function);
+
+static int check_delegation(const zone_node_t *node, semchecks_data_t *data)
+{
+ if (!((node->flags & NODE_FLAGS_DELEG) || data->zone->apex == node)) {
+ return KNOT_EOK;
+ }
+
+ // always check zone apex
+ if (!(data->level & OPTIONAL) && data->zone->apex != node) {
+ return KNOT_EOK;
+ }
+
+ const knot_rdataset_t *ns_rrs = node_rdataset(node, KNOT_RRTYPE_NS);
+ if (ns_rrs == NULL) {
+ assert(data->zone->apex == node);
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_NS_APEX, NULL);
+ return KNOT_EOK;
+ }
+
+ // check glue record for delegation
+ for (int i = 0; i < ns_rrs->count; ++i) {
+ knot_rdata_t *ns_rr = knot_rdataset_at(ns_rrs, i);
+ const knot_dname_t *ns_dname = knot_ns_name(ns_rr);
+ const zone_node_t *glue_node = NULL, *glue_encloser = NULL;
+ int ret = zone_contents_find_dname(data->zone, ns_dname, &glue_node,
+ &glue_encloser, NULL);
+ switch (ret) {
+ case KNOT_EOUTOFZONE:
+ continue; // NS is out of bailiwick
+ case ZONE_NAME_NOT_FOUND:
+ if (glue_encloser != node &&
+ glue_encloser->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH)) {
+ continue; // NS is below another delegation
+ }
+
+ // check if covered by wildcard
+ knot_dname_storage_t wildcard = "\x01""*";
+ knot_dname_to_wire(wildcard + 2, glue_encloser->owner,
+ sizeof(wildcard) - 2);
+ glue_node = zone_contents_find_node(data->zone, wildcard);
+ break; // continue in checking glue existence
+ case ZONE_NAME_FOUND:
+ break; // continue in checking glue existence
+ default:
+ return ret;
+ }
+ if (!node_rrtype_exists(glue_node, KNOT_RRTYPE_A) &&
+ !node_rrtype_exists(glue_node, KNOT_RRTYPE_AAAA)) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_NS_GLUE, NULL);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_submission(const zone_node_t *node, semchecks_data_t *data)
+{
+ const knot_rdataset_t *cdss = node_rdataset(node, KNOT_RRTYPE_CDS);
+ const knot_rdataset_t *cdnskeys = node_rdataset(node, KNOT_RRTYPE_CDNSKEY);
+ if (cdss == NULL && cdnskeys == NULL) {
+ return KNOT_EOK;
+ } else if (cdss == NULL) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDS_NONE, NULL);
+ return KNOT_EOK;
+ } else if (cdnskeys == NULL) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDNSKEY_NONE, NULL);
+ return KNOT_EOK;
+ }
+
+ const knot_rdataset_t *dnskeys = node_rdataset(data->zone->apex,
+ KNOT_RRTYPE_DNSKEY);
+ if (dnskeys == NULL) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DNSKEY_NONE, NULL);
+ }
+
+ const uint8_t *empty_cds = (uint8_t *)"\x00\x00\x00\x00\x00";
+ const uint8_t *empty_cdnskey = (uint8_t *)"\x00\x00\x03\x00\x00";
+ bool delete_cds = false, delete_cdnskey = false;
+
+ // check every CDNSKEY for corresponding DNSKEY
+ for (int i = 0; i < cdnskeys->count; i++) {
+ knot_rdata_t *cdnskey = knot_rdataset_at(cdnskeys, i);
+
+ // skip delete-dnssec CDNSKEY
+ if (cdnskey->len == 5 && memcmp(cdnskey->data, empty_cdnskey, 5) == 0) {
+ delete_cdnskey = true;
+ continue;
+ }
+
+ bool match = false;
+ for (int j = 0; dnskeys != NULL && j < dnskeys->count; j++) {
+ knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, j);
+
+ if (knot_rdata_cmp(dnskey, cdnskey) == 0) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDNSKEY_NO_DNSKEY, NULL);
+ }
+ }
+
+ // check every CDS for corresponding CDNSKEY
+ for (int i = 0; i < cdss->count; i++) {
+ knot_rdata_t *cds = knot_rdataset_at(cdss, i);
+ uint8_t digest_type = knot_ds_digest_type(cds);
+
+ // skip delete-dnssec CDS
+ if (cds->len == 5 && memcmp(cds->data, empty_cds, 5) == 0) {
+ delete_cds = true;
+ continue;
+ }
+
+ bool match = false;
+ for (int j = 0; j < cdnskeys->count; j++) {
+ knot_rdata_t *cdnskey = knot_rdataset_at(cdnskeys, j);
+
+ dnssec_key_t *key;
+ int ret = dnssec_key_from_rdata(&key, data->zone->apex->owner,
+ cdnskey->data, cdnskey->len);
+ if (ret != KNOT_EOK) {
+ continue;
+ }
+
+ dnssec_binary_t cds_calc = { 0 };
+ dnssec_binary_t cds_orig = { .size = cds->len, .data = cds->data };
+ ret = dnssec_key_create_ds(key, digest_type, &cds_calc);
+ if (ret != KNOT_EOK) {
+ dnssec_key_free(key);
+ return ret;
+ }
+
+ ret = dnssec_binary_cmp(&cds_orig, &cds_calc);
+ dnssec_binary_free(&cds_calc);
+ dnssec_key_free(key);
+ if (ret == 0) {
+ match = true;
+ break;
+ }
+ }
+ if (!match) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDS_NOT_MATCH, NULL);
+ }
+ }
+
+ // check delete-dnssec records
+ if ((delete_cds && (!delete_cdnskey || cdss->count > 1)) ||
+ (delete_cdnskey && (!delete_cds || cdnskeys->count > 1))) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDNSKEY_INVALID_DELETE, NULL);
+ }
+
+ // check orphaned CDS
+ if (cdss->count < cdnskeys->count) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CDNSKEY_NO_CDS, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_ds(const zone_node_t *node, semchecks_data_t *data)
+{
+ const knot_rdataset_t *dss = node_rdataset(node, KNOT_RRTYPE_DS);
+ if (dss == NULL) {
+ return KNOT_EOK;
+ }
+
+ for (int i = 0; i < dss->count; i++) {
+ knot_rdata_t *ds = knot_rdataset_at(dss, i);
+ uint16_t keytag = knot_ds_key_tag(ds);
+ uint8_t digest_type = knot_ds_digest_type(ds);
+
+ char info[64] = "";
+ (void)snprintf(info, sizeof(info), "(keytag %d)", keytag);
+
+ if (!dnssec_algorithm_digest_support(digest_type)) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DS_RDATA_ALG, info);
+ } else {
+ // Sizes for different digest algorithms.
+ const uint16_t digest_sizes [] = { 0, 20, 32, 32, 48};
+
+ uint16_t digest_size = knot_ds_digest_len(ds);
+
+ if (digest_sizes[digest_type] != digest_size) {
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DS_RDATA_DIGLEN, info);
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_soa(const zone_node_t *node, semchecks_data_t *data)
+{
+ if (data->zone->apex != node) {
+ return KNOT_EOK;
+ }
+
+ const knot_rdataset_t *soa_rrs = node_rdataset(node, KNOT_RRTYPE_SOA);
+ if (soa_rrs == NULL) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_SOA_NONE, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_cname(const zone_node_t *node, semchecks_data_t *data)
+{
+ const knot_rdataset_t *cname_rrs = node_rdataset(node, KNOT_RRTYPE_CNAME);
+ if (cname_rrs == NULL) {
+ return KNOT_EOK;
+ }
+
+ unsigned rrset_limit = 1;
+ /* With DNSSEC node can contain RRSIGs or NSEC */
+ if (node_rrtype_exists(node, KNOT_RRTYPE_NSEC)) {
+ rrset_limit += 1;
+ }
+ if (node_rrtype_exists(node, KNOT_RRTYPE_RRSIG)) {
+ rrset_limit += 1;
+ }
+
+ if (node->rrset_count > rrset_limit) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CNAME_EXTRA_RECORDS, NULL);
+ }
+ if (cname_rrs->count != 1) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_CNAME_MULTIPLE, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_dname(const zone_node_t *node, semchecks_data_t *data)
+{
+ const knot_rdataset_t *dname_rrs = node_rdataset(node, KNOT_RRTYPE_DNAME);
+ if (dname_rrs == NULL) {
+ return KNOT_EOK;
+ }
+
+ /* RFC 6672 Section 2.3 Paragraph 3 */
+ bool is_apex = (node->flags & NODE_FLAGS_APEX);
+ if (!is_apex && node_rrtype_exists(node, KNOT_RRTYPE_NS)) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DNAME_EXTRA_NS, NULL);
+ }
+ /* RFC 6672 Section 2.4 Paragraph 1 */
+ /* If the NSEC3 node of the apex is present, it is counted as apex's child. */
+ unsigned allowed_children = (is_apex && node_nsec3_get(node) != NULL) ? 1 : 0;
+ if (node->children > allowed_children) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DNAME_CHILDREN, NULL);
+ }
+ /* RFC 6672 Section 2.4 Paragraph 2 */
+ if (dname_rrs->count != 1) {
+ data->handler->error = true;
+ data->handler->cb(data->handler, data->zone, node->owner,
+ SEM_ERR_DNAME_MULTIPLE, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_nsec3param(const zone_node_t *node, semchecks_data_t *data)
+{
+ if (data->zone->apex != node) {
+ return KNOT_EOK;
+ }
+
+ const knot_rdataset_t *nsec3param_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC3PARAM);
+ if (nsec3param_rrs == NULL) {
+ return KNOT_EOK;
+ }
+
+ uint8_t param = knot_nsec3param_flags(nsec3param_rrs->rdata);
+ if ((param & ~1) != 0) {
+ data->handler->cb(data->handler, data->zone, data->zone->apex->owner,
+ SEM_ERR_NSEC3PARAM_RDATA_FLAGS, NULL);
+ }
+
+ param = knot_nsec3param_alg(nsec3param_rrs->rdata);
+ if (param != DNSSEC_NSEC3_ALGORITHM_SHA1) {
+ data->handler->cb(data->handler, data->zone, data->zone->apex->owner,
+ SEM_ERR_NSEC3PARAM_RDATA_ALG, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int do_checks_in_tree(zone_node_t *node, void *data)
+{
+ semchecks_data_t *s_data = (semchecks_data_t *)data;
+
+ int ret = KNOT_EOK;
+
+ for (int i = 0; ret == KNOT_EOK && i < CHECK_FUNCTIONS_LEN; ++i) {
+ if (CHECK_FUNCTIONS[i].level & s_data->level) {
+ ret = CHECK_FUNCTIONS[i].function(node, s_data);
+ if (s_data->handler->fatal_error &&
+ (CHECK_FUNCTIONS[i].level & SOFT) &&
+ (s_data->level & SOFT)) {
+ s_data->handler->fatal_error = false;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static sem_error_t err_dnssec2sem(int ret, uint16_t rrtype, char *info, size_t len)
+{
+ char type_str[16];
+
+ switch (ret) {
+ case KNOT_DNSSEC_ENOSIG:
+ if (knot_rrtype_to_string(rrtype, type_str, sizeof(type_str)) > 0) {
+ (void)snprintf(info, len, "(record type %s)", type_str);
+ }
+ return SEM_ERR_RRSIG_UNVERIFIABLE;
+ case KNOT_DNSSEC_ENONSEC:
+ return SEM_ERR_NSEC_NONE;
+ case KNOT_DNSSEC_ENSEC_BITMAP:
+ return SEM_ERR_NSEC_RDATA_BITMAP;
+ case KNOT_DNSSEC_ENSEC_CHAIN:
+ return SEM_ERR_NSEC_RDATA_CHAIN;
+ case KNOT_DNSSEC_ENSEC3_OPTOUT:
+ return SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT;
+ default:
+ return SEM_ERR_UNKNOWN;
+ }
+}
+
+static int verify_dnssec(zone_contents_t *zone, sem_handler_t *handler, time_t time)
+{
+ zone_update_t fake_up = { .new_cont = zone, };
+ int ret = knot_dnssec_validate_zone(&fake_up, NULL, time, false);
+ if (fake_up.validation_hint.node != NULL) { // validation found an issue
+ char info[64] = "";
+ sem_error_t err = err_dnssec2sem(ret, fake_up.validation_hint.rrtype, info, sizeof(info));
+ handler->cb(handler, zone, fake_up.validation_hint.node, err, info);
+ return KNOT_EOK;
+ } else if (ret == KNOT_INVALID_PUBLIC_KEY) { // validation failed due to invalid DNSKEY
+ handler->cb(handler, zone, zone->apex->owner, SEM_ERR_DNSKEY_INVALID, NULL);
+ return KNOT_EOK;
+ } else { // validation failed by itself
+ return ret;
+ }
+}
+
+int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_handler_t *handler,
+ time_t time)
+{
+ if (handler == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ semchecks_data_t data = {
+ .handler = handler,
+ .zone = zone,
+ .level = MANDATORY,
+ .time = time,
+ };
+
+ switch (optional) {
+ case SEMCHECK_MANDATORY_SOFT:
+ data.level |= SOFT;
+ data.handler->soft_check = true;
+ break;
+ case SEMCHECK_DNSSEC_AUTO:
+ data.level |= OPTIONAL;
+ if (zone->dnssec) {
+ data.level |= DNSSEC;
+ }
+ break;
+ case SEMCHECK_DNSSEC_ON:
+ data.level |= OPTIONAL;
+ data.level |= DNSSEC;
+ break;
+ case SEMCHECK_DNSSEC_OFF:
+ data.level |= OPTIONAL;
+ break;
+ default:
+ break;
+ }
+
+ int ret = zone_contents_apply(zone, do_checks_in_tree, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (data.handler->fatal_error) {
+ return KNOT_ESEMCHECK;
+ }
+
+ if (data.level & DNSSEC) {
+ ret = verify_dnssec(zone, handler, time);
+ }
+
+ return ret;
+}
diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h
new file mode 100644
index 0000000..0318fc0
--- /dev/null
+++ b/src/knot/zone/semantic-check.h
@@ -0,0 +1,116 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <time.h>
+
+#include "knot/conf/schema.h"
+#include "knot/zone/contents.h"
+
+typedef enum {
+ SEMCHECK_MANDATORY_ONLY = SEMCHECKS_OFF,
+ SEMCHECK_DNSSEC_AUTO = SEMCHECKS_ON,
+ SEMCHECK_MANDATORY_SOFT = SEMCHECKS_SOFT,
+ SEMCHECK_DNSSEC_OFF,
+ SEMCHECK_DNSSEC_ON,
+} semcheck_optional_t;
+
+/*!
+ *\brief Internal error constants.
+ */
+typedef enum {
+ // Mandatory checks.
+ SEM_ERR_SOA_NONE,
+
+ SEM_ERR_CNAME_EXTRA_RECORDS,
+ SEM_ERR_CNAME_MULTIPLE,
+
+ SEM_ERR_DNAME_CHILDREN,
+ SEM_ERR_DNAME_MULTIPLE,
+ SEM_ERR_DNAME_EXTRA_NS,
+
+ // Optional checks.
+ SEM_ERR_NS_APEX,
+ SEM_ERR_NS_GLUE,
+
+ // DNSSEC checks.
+ SEM_ERR_RRSIG_UNVERIFIABLE,
+
+ SEM_ERR_NSEC_NONE,
+ SEM_ERR_NSEC_RDATA_BITMAP,
+ SEM_ERR_NSEC_RDATA_CHAIN,
+ SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT,
+
+ SEM_ERR_NSEC3PARAM_RDATA_FLAGS,
+ SEM_ERR_NSEC3PARAM_RDATA_ALG,
+
+ SEM_ERR_DS_RDATA_ALG,
+ SEM_ERR_DS_RDATA_DIGLEN,
+
+ SEM_ERR_DNSKEY_NONE,
+ SEM_ERR_DNSKEY_INVALID,
+
+ SEM_ERR_CDS_NONE,
+ SEM_ERR_CDS_NOT_MATCH,
+
+ SEM_ERR_CDNSKEY_NONE,
+ SEM_ERR_CDNSKEY_NO_DNSKEY,
+ SEM_ERR_CDNSKEY_NO_CDS,
+ SEM_ERR_CDNSKEY_INVALID_DELETE,
+
+ // General error!
+ SEM_ERR_UNKNOWN
+} sem_error_t;
+
+const char *sem_error_msg(sem_error_t code);
+
+/*!
+ * \brief Structure for handling semantic errors.
+ */
+typedef struct sem_handler sem_handler_t;
+
+/*!
+ * \brief Callback for handle error.
+ */
+typedef void (*sem_callback) (sem_handler_t *ctx, const zone_contents_t *zone,
+ const knot_dname_t *node, sem_error_t error, const char *data);
+
+struct sem_handler {
+ sem_callback cb;
+ bool soft_check;
+ bool error; /* An error in the current check. */
+ bool fatal_error; /* The checks detected at least one error. */
+ bool warning; /* The checks detected at least one warning. */
+};
+
+/*!
+ * \brief Check zone for semantic errors.
+ *
+ * Errors are logged in error handler.
+ *
+ * \param zone Zone to be searched / checked.
+ * \param optional To do also optional check.
+ * \param handler Semantic error handler.
+ * \param time Check zone at given time (rrsig expiration).
+ *
+ * \retval KNOT_EOK no error found
+ * \retval KNOT_ESEMCHECK found semantic error
+ * \retval KNOT_EEMPTYZONE the zone is empty
+ * \retval KNOT_EINVAL another error
+ */
+int sem_checks_process(zone_contents_t *zone, semcheck_optional_t optional, sem_handler_t *handler,
+ time_t time);
diff --git a/src/knot/zone/serial.c b/src/knot/zone/serial.c
new file mode 100644
index 0000000..0be5cbe
--- /dev/null
+++ b/src/knot/zone/serial.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <time.h>
+
+#include "knot/conf/conf.h"
+#include "knot/zone/serial.h"
+
+static const serial_cmp_result_t diffbrief2result[4] = {
+ [0] = SERIAL_EQUAL,
+ [1] = SERIAL_GREATER,
+ [2] = SERIAL_INCOMPARABLE,
+ [3] = SERIAL_LOWER,
+};
+
+serial_cmp_result_t serial_compare(uint32_t s1, uint32_t s2)
+{
+ uint64_t diff = ((uint64_t)s1 + ((uint64_t)1 << 32) - s2) & 0xffffffff;
+ int diffbrief = (diff >> 31 << 1) | ((diff & 0x7fffffff) ? 1 : 0);
+ assert(diffbrief > -1 && diffbrief < 4);
+ return diffbrief2result[diffbrief];
+}
+
+static uint32_t serial_dateserial(uint32_t current)
+{
+ struct tm now;
+ time_t current_time = time(NULL);
+ struct tm *gmtime_result = gmtime_r(&current_time, &now);
+ if (gmtime_result == NULL) {
+ return current;
+ }
+ return (1900 + now.tm_year) * 1000000 +
+ ( 1 + now.tm_mon ) * 10000 +
+ ( now.tm_mday) * 100;
+}
+
+uint32_t serial_next(uint32_t current, int policy, uint32_t must_increment)
+{
+ uint32_t minimum;
+ switch (policy) {
+ case SERIAL_POLICY_INCREMENT:
+ minimum = current;
+ break;
+ case SERIAL_POLICY_UNIXTIME:
+ minimum = time(NULL);
+ break;
+ case SERIAL_POLICY_DATESERIAL:
+ minimum = serial_dateserial(current);
+ break;
+ default:
+ assert(0);
+ return 0;
+ }
+ if (serial_compare(minimum, current) != SERIAL_GREATER) {
+ return current + must_increment;
+ } else {
+ return minimum;
+ }
+}
+
+serial_cmp_result_t kserial_cmp(kserial_t a, kserial_t b)
+{
+ return ((a.valid && b.valid) ? serial_compare(a.serial, b.serial) : SERIAL_INCOMPARABLE);
+}
diff --git a/src/knot/zone/serial.h b/src/knot/zone/serial.h
new file mode 100644
index 0000000..effb1c6
--- /dev/null
+++ b/src/knot/zone/serial.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define SERIAL_MAX_INCREMENT 2147483647
+
+/*!
+ * \brief result of serial comparison. LOWER means that the first serial is lower that the second.
+ *
+ * Example: (serial_compare(a, b) & SERIAL_MASK_LEQ) means "a <= b".
+ */
+typedef enum {
+ SERIAL_INCOMPARABLE = 0x0,
+ SERIAL_LOWER = 0x1,
+ SERIAL_GREATER = 0x2,
+ SERIAL_EQUAL = 0x3,
+ SERIAL_MASK_LEQ = SERIAL_LOWER,
+ SERIAL_MASK_GEQ = SERIAL_GREATER,
+} serial_cmp_result_t;
+
+/*!
+ * \brief Compares two zone serials.
+ */
+serial_cmp_result_t serial_compare(uint32_t s1, uint32_t s2);
+
+inline static bool serial_equal(uint32_t a, uint32_t b)
+{
+ return serial_compare(a, b) == SERIAL_EQUAL;
+}
+
+/*!
+ * \brief Get (next) serial for given serial update policy.
+ *
+ * \param current Current SOA serial.
+ * \param policy SERIAL_POLICY_INCREMENT, SERIAL_POLICY_UNIXTIME or
+ * SERIAL_POLICY_DATESERIAL.
+ * \param must_increment The minimum difference to the current value.
+ * 0 only ensures policy; 1 also increments.
+ *
+ * \return New serial.
+ */
+uint32_t serial_next(uint32_t current, int policy, uint32_t must_increment);
+
+typedef struct {
+ uint32_t serial;
+ bool valid;
+} kserial_t;
+
+/*!
+ * \brief Compares two kserials.
+ *
+ * If any of them is invalid, they are INCOMPARABLE.
+ */
+serial_cmp_result_t kserial_cmp(kserial_t a, kserial_t b);
+
+inline static bool kserial_equal(kserial_t a, kserial_t b)
+{
+ return kserial_cmp(a, b) == SERIAL_EQUAL;
+}
diff --git a/src/knot/zone/timers.c b/src/knot/zone/timers.c
new file mode 100644
index 0000000..32c22b3
--- /dev/null
+++ b/src/knot/zone/timers.c
@@ -0,0 +1,228 @@
+/* Copyright (C) 2022 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 "knot/zone/timers.h"
+
+#include "contrib/wire_ctx.h"
+#include "knot/zone/zonedb.h"
+
+/*
+ * # Timer database
+ *
+ * Timer database stores timestamps of events which need to be retained
+ * across server restarts. The key in the database is the zone name in
+ * wire format. The value contains serialized timers.
+ *
+ * # Serialization format
+ *
+ * The value is a sequence of timers. Each timer consists of the timer
+ * identifier (1 byte, unsigned integer) and timer value (8 bytes, unsigned
+ * integer, network order).
+ *
+ * For example, the following byte sequence:
+ *
+ * 81 00 00 00 00 57 e3 e8 0a 82 00 00 00 00 57 e3 e9 a1
+ *
+ * Encodes the following timers:
+ *
+ * last_flush = 1474553866
+ * last_refresh = 1474554273
+ */
+
+/*!
+ * \brief Timer database fields identifiers.
+ *
+ * Valid ID starts with '1' in MSB to avoid conflicts with "old timers".
+ */
+enum timer_id {
+ TIMER_INVALID = 0,
+ TIMER_SOA_EXPIRE = 0x80, // DEPRECATED
+ TIMER_LAST_FLUSH = 0x81,
+ TIMER_LAST_REFRESH = 0x82, // DEPRECATED
+ TIMER_NEXT_REFRESH = 0x83,
+ TIMER_NEXT_DS_CHECK = 0x85,
+ TIMER_NEXT_DS_PUSH = 0x86,
+ TIMER_CATALOG_MEMBER = 0x87,
+ TIMER_LAST_NOTIFIED = 0x88,
+ TIMER_LAST_REFR_OK = 0x89,
+ TIMER_NEXT_EXPIRE = 0x8a,
+};
+
+#define TIMER_SIZE (sizeof(uint8_t) + sizeof(uint64_t))
+
+/*!
+ * \brief Deserialize timers from a binary buffer.
+ *
+ * \note Unknown timers are ignored.
+ */
+static int deserialize_timers(zone_timers_t *timers_ptr,
+ const uint8_t *data, size_t size)
+{
+ if (!timers_ptr || !data) {
+ return KNOT_EINVAL;
+ }
+
+ zone_timers_t timers = { 0 };
+
+ wire_ctx_t wire = wire_ctx_init_const(data, size);
+ while (wire_ctx_available(&wire) >= TIMER_SIZE) {
+ uint8_t id = wire_ctx_read_u8(&wire);
+ uint64_t value = wire_ctx_read_u64(&wire);
+ switch (id) {
+ case TIMER_SOA_EXPIRE: timers.soa_expire = value; break;
+ case TIMER_LAST_FLUSH: timers.last_flush = value; break;
+ case TIMER_LAST_REFRESH: timers.last_refresh = value; break;
+ case TIMER_NEXT_REFRESH: timers.next_refresh = value; break;
+ case TIMER_LAST_REFR_OK: timers.last_refresh_ok = value; break;
+ case TIMER_LAST_NOTIFIED: timers.last_notified_serial = value; break;
+ case TIMER_NEXT_DS_CHECK: timers.next_ds_check = value; break;
+ case TIMER_NEXT_DS_PUSH: timers.next_ds_push = value; break;
+ case TIMER_CATALOG_MEMBER: timers.catalog_member = value; break;
+ case TIMER_NEXT_EXPIRE: timers.next_expire = value; break;
+ default: break; // ignore
+ }
+ }
+
+ if (wire_ctx_available(&wire) != 0) {
+ return KNOT_EMALF;
+ }
+
+ assert(wire.error == KNOT_EOK);
+
+ *timers_ptr = timers;
+ return KNOT_EOK;
+}
+
+static void txn_write_timers(knot_lmdb_txn_t *txn, const knot_dname_t *zone,
+ const zone_timers_t *timers)
+{
+ MDB_val k = { knot_dname_size(zone), (void *)zone };
+ MDB_val v = knot_lmdb_make_key("BLBLBLBLBLBLBLBL",
+ TIMER_LAST_FLUSH, (uint64_t)timers->last_flush,
+ TIMER_NEXT_REFRESH, (uint64_t)timers->next_refresh,
+ TIMER_LAST_REFR_OK, (uint64_t)timers->last_refresh_ok,
+ TIMER_LAST_NOTIFIED, timers->last_notified_serial,
+ TIMER_NEXT_DS_CHECK, (uint64_t)timers->next_ds_check,
+ TIMER_NEXT_DS_PUSH, (uint64_t)timers->next_ds_push,
+ TIMER_CATALOG_MEMBER,(uint64_t)timers->catalog_member,
+ TIMER_NEXT_EXPIRE, (uint64_t)timers->next_expire);
+ knot_lmdb_insert(txn, &k, &v);
+ free(v.mv_data);
+}
+
+
+int zone_timers_open(const char *path, knot_db_t **db, size_t mapsize)
+{
+ if (path == NULL || db == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER;
+ opts.mapsize = mapsize;
+ opts.path = path;
+
+ return knot_db_lmdb_api()->init(db, NULL, &opts);
+}
+
+void zone_timers_close(knot_db_t *db)
+{
+ if (db == NULL) {
+ return;
+ }
+
+ knot_db_lmdb_api()->deinit(db);
+}
+
+int zone_timers_read(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ zone_timers_t *timers)
+{
+ if (knot_lmdb_exists(db) == KNOT_ENODB) {
+ return KNOT_ENODB;
+ }
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ MDB_val k = { knot_dname_size(zone), (void *)zone };
+ if (knot_lmdb_find(&txn, &k, KNOT_LMDB_EXACT | KNOT_LMDB_FORCE)) {
+ deserialize_timers(timers, txn.cur_val.mv_data, txn.cur_val.mv_size);
+ }
+ knot_lmdb_abort(&txn);
+
+ // backward compatibility
+ // For catalog zones, next_expire is cleaned up later by zone_timers_sanitize().
+ if (timers->next_expire == 0 && timers->last_refresh > 0) {
+ timers->next_expire = timers->last_refresh + timers->soa_expire;
+ }
+
+ return txn.ret;
+}
+
+int zone_timers_write(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ const zone_timers_t *timers)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ txn_write_timers(&txn, zone, timers);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+static void txn_zone_write(zone_t *z, knot_lmdb_txn_t *txn)
+{
+ txn_write_timers(txn, z->name, &z->timers);
+}
+
+int zone_timers_write_all(knot_lmdb_db_t *db, knot_zonedb_t *zonedb)
+{
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_zonedb_foreach(zonedb, txn_zone_write, &txn);
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+int zone_timers_sweep(knot_lmdb_db_t *db, sweep_cb keep_zone, void *cb_data)
+{
+ if (knot_lmdb_exists(db) == KNOT_ENODB) {
+ return KNOT_EOK;
+ }
+ int ret = knot_lmdb_open(db);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, true);
+ knot_lmdb_forwhole(&txn) {
+ if (!keep_zone((const knot_dname_t *)txn.cur_key.mv_data, cb_data)) {
+ knot_lmdb_del_cur(&txn);
+ }
+ }
+ knot_lmdb_commit(&txn);
+ return txn.ret;
+}
+
+bool zone_timers_serial_notified(const zone_timers_t *timers, uint32_t serial)
+{
+ return (timers->last_notified_serial & LAST_NOTIFIED_SERIAL_VALID) &&
+ ((uint32_t)timers->last_notified_serial == serial);
+}
diff --git a/src/knot/zone/timers.h b/src/knot/zone/timers.h
new file mode 100644
index 0000000..d7bb05c
--- /dev/null
+++ b/src/knot/zone/timers.h
@@ -0,0 +1,99 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <time.h>
+
+#include "libknot/dname.h"
+#include "knot/journal/knot_lmdb.h"
+
+#define LAST_NOTIFIED_SERIAL_VALID (1LLU << 32)
+
+/*!
+ * \brief Persistent zone timers.
+ */
+struct zone_timers {
+ uint32_t soa_expire; //!< SOA expire value. DEPRECATED
+ time_t last_flush; //!< Last zone file synchronization.
+ time_t last_refresh; //!< Last successful zone refresh attempt. DEPRECATED
+ time_t next_refresh; //!< Next zone refresh attempt.
+ bool last_refresh_ok; //!< Last zone refresh attempt was successful.
+ uint64_t last_notified_serial; //!< SOA serial of last successful NOTIFY; (1<<32) if none.
+ time_t next_ds_check; //!< Next parent DS check.
+ time_t next_ds_push; //!< Next DDNS to parent zone with updated DS record.
+ time_t catalog_member; //!< This catalog member zone created.
+ time_t next_expire; //!< Timestamp of the zone to expire.
+};
+
+typedef struct zone_timers zone_timers_t;
+
+/*!
+ * \brief From zonedb.h
+ */
+typedef struct knot_zonedb knot_zonedb_t;
+
+/*!
+ * \brief Load timers for one zone.
+ *
+ * \param[in] db Timer database.
+ * \param[in] zone Zone name.
+ * \param[out] timers Loaded timers
+ *
+ * \return KNOT_E*
+ * \retval KNOT_ENOENT Zone not found in the database.
+ */
+int zone_timers_read(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ zone_timers_t *timers);
+
+/*!
+ * \brief Write timers for one zone.
+ *
+ * \param db Timer database.
+ * \param zone Zone name.
+ * \param timers Loaded timers
+ *
+ * \return KNOT_E*
+ */
+int zone_timers_write(knot_lmdb_db_t *db, const knot_dname_t *zone,
+ const zone_timers_t *timers);
+
+/*!
+ * \brief Write timers for all zones.
+ *
+ * \param db Timer database.
+ * \param zonedb Zones database.
+ *
+ * \return KNOT_E*
+ */
+int zone_timers_write_all(knot_lmdb_db_t *db, knot_zonedb_t *zonedb);
+
+/*!
+ * \brief Selectively delete zones from the database.
+ *
+ * \param db Timer database.
+ * \param keep_zone Filtering callback.
+ * \param cb_data Data passed to callback function.
+ *
+ * \return KNOT_E*
+ */
+int zone_timers_sweep(knot_lmdb_db_t *db, sweep_cb keep_zone, void *cb_data);
+
+/*!
+ * \brief Tell if the specified serial has already been notified according to timers.
+ */
+bool zone_timers_serial_notified(const zone_timers_t *timers, uint32_t serial);
diff --git a/src/knot/zone/zone-diff.c b/src/knot/zone/zone-diff.c
new file mode 100644
index 0000000..9e6ecc6
--- /dev/null
+++ b/src/knot/zone/zone-diff.c
@@ -0,0 +1,402 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <stdlib.h>
+#include <inttypes.h>
+
+#include "libknot/libknot.h"
+#include "knot/zone/zone-diff.h"
+#include "knot/zone/serial.h"
+
+struct zone_diff_param {
+ zone_tree_t *nodes;
+ changeset_t *changeset;
+ bool ignore_dnssec;
+ bool ignore_zonemd;
+};
+
+static bool rrset_is_dnssec(const knot_rrset_t *rrset)
+{
+ switch (rrset->type) {
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_NSEC3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2,
+ changeset_t *changeset)
+{
+ assert(zone1);
+ assert(zone2);
+ assert(changeset);
+
+ const zone_node_t *apex1 = zone1->apex;
+ const zone_node_t *apex2 = zone2->apex;
+ if (apex1 == NULL || apex2 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t soa_rrset1 = node_rrset(apex1, KNOT_RRTYPE_SOA);
+ knot_rrset_t soa_rrset2 = node_rrset(apex2, KNOT_RRTYPE_SOA);
+ if (knot_rrset_empty(&soa_rrset1) || knot_rrset_empty(&soa_rrset2)) {
+ return KNOT_EINVAL;
+ }
+
+ if (soa_rrset1.rrs.count == 0 ||
+ soa_rrset2.rrs.count == 0) {
+ return KNOT_EINVAL;
+ }
+
+ uint32_t soa_serial1 = knot_soa_serial(soa_rrset1.rrs.rdata);
+ uint32_t soa_serial2 = knot_soa_serial(soa_rrset2.rrs.rdata);
+
+ if (serial_compare(soa_serial1, soa_serial2) == SERIAL_EQUAL) {
+ return KNOT_ENODIFF;
+ }
+
+ if (serial_compare(soa_serial1, soa_serial2) != SERIAL_LOWER) {
+ return KNOT_ERANGE;
+ }
+
+ changeset->soa_from = knot_rrset_copy(&soa_rrset1, NULL);
+ if (changeset->soa_from == NULL) {
+ return KNOT_ENOMEM;
+ }
+ changeset->soa_to = knot_rrset_copy(&soa_rrset2, NULL);
+ if (changeset->soa_to == NULL) {
+ knot_rrset_free(changeset->soa_from, NULL);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+static int add_node(const zone_node_t *node, changeset_t *changeset,
+ bool ignore_dnssec, bool ignore_zonemd)
+{
+ /* Add all rrsets from node. */
+ for (unsigned i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+
+ if ((ignore_dnssec && rrset_is_dnssec(&rrset)) ||
+ (ignore_zonemd && rrset.type == KNOT_RRTYPE_ZONEMD)) {
+ continue;
+ }
+
+ int ret = changeset_add_addition(changeset, &rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int remove_node(const zone_node_t *node, changeset_t *changeset,
+ bool ignore_dnssec, bool ignore_zonemd)
+{
+ /* Remove all the RRSets of the node. */
+ for (unsigned i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+
+ if ((ignore_dnssec && rrset_is_dnssec(&rrset)) ||
+ (ignore_zonemd && rrset.type == KNOT_RRTYPE_ZONEMD)) {
+ continue;
+ }
+
+ int ret = changeset_add_removal(changeset, &rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int rdata_return_changes(const knot_rrset_t *rrset1,
+ const knot_rrset_t *rrset2,
+ knot_rrset_t *changes)
+{
+ if (rrset1 == NULL || rrset2 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Create fake RRSet, it will be easier to handle. */
+ knot_rrset_init(changes, rrset1->owner, rrset1->type, rrset1->rclass, rrset1->ttl);
+
+ /*
+ * Take one rdata from first list and search through the second list
+ * looking for an exact match. If no match occurs, it means that this
+ * particular RR has changed.
+ * After the list has been traversed, we have a list of
+ * changed/removed rdatas. This has awful computation time.
+ */
+ bool ttl_differ = rrset1->ttl != rrset2->ttl && rrset1->type != KNOT_RRTYPE_RRSIG;
+ knot_rdata_t *rr1 = rrset1->rrs.rdata;
+ for (uint16_t i = 0; i < rrset1->rrs.count; ++i) {
+ if (ttl_differ || !knot_rdataset_member(&rrset2->rrs, rr1)) {
+ /*
+ * No such RR is present in 'rrset2'. We'll copy
+ * index 'i' into 'changes' RRSet.
+ */
+ int ret = knot_rdataset_add(&changes->rrs, rr1, NULL);
+ if (ret != KNOT_EOK) {
+ knot_rdataset_clear(&changes->rrs, NULL);
+ return ret;
+ }
+ }
+ rr1 = knot_rdataset_next(rr1);
+ }
+
+ return KNOT_EOK;
+}
+
+static int diff_rrsets(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2,
+ changeset_t *changeset)
+{
+ if (changeset == NULL || (rrset1 == NULL && rrset2 == NULL)) {
+ return KNOT_EINVAL;
+ }
+ /*
+ * The easiest solution is to remove all the RRs that had no match and
+ * to add all RRs that had no match, but those from second RRSet. */
+
+ /* Get RRs to add to zone and to remove from zone. */
+ knot_rrset_t to_remove = { 0 };
+ knot_rrset_t to_add = { 0 };
+ if (rrset1 != NULL && rrset2 != NULL) {
+ int ret = rdata_return_changes(rrset1, rrset2, &to_remove);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = rdata_return_changes(rrset2, rrset1, &to_add);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ if (!knot_rrset_empty(&to_remove)) {
+ int ret = changeset_add_removal(changeset, &to_remove, 0);
+ knot_rdataset_clear(&to_remove.rrs, NULL);
+ if (ret != KNOT_EOK) {
+ knot_rdataset_clear(&to_add.rrs, NULL);
+ return ret;
+ }
+ }
+
+ if (!knot_rrset_empty(&to_add)) {
+ int ret = changeset_add_addition(changeset, &to_add, 0);
+ knot_rdataset_clear(&to_add.rrs, NULL);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+/*!< \todo this could be generic function for adding / removing. */
+static int knot_zone_diff_node(zone_node_t *node, void *data)
+{
+ if (node == NULL || data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct zone_diff_param *param = (struct zone_diff_param *)data;
+ if (param->changeset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /*
+ * First, we have to search the second tree to see if there's according
+ * node, if not, the whole node has been removed.
+ */
+ zone_node_t *node_in_second_tree = zone_tree_get(param->nodes, node->owner);
+ if (node_in_second_tree == NULL) {
+ return remove_node(node, param->changeset, param->ignore_dnssec,
+ param->ignore_zonemd);
+ }
+
+ assert(node_in_second_tree != node);
+
+ /* The nodes are in both trees, we have to diff each RRSet. */
+ if (node->rrset_count == 0) {
+ /*
+ * If there are no RRs in the first tree, all of the RRs
+ * in the second tree will have to be inserted to ADD section.
+ */
+ return add_node(node_in_second_tree, param->changeset,
+ param->ignore_dnssec, param->ignore_zonemd);
+ }
+
+ for (unsigned i = 0; i < node->rrset_count; i++) {
+ /* Search for the RRSet in the node from the second tree. */
+ knot_rrset_t rrset = node_rrset_at(node, i);
+
+ /* SOAs are handled explicitly. */
+ if (rrset.type == KNOT_RRTYPE_SOA) {
+ continue;
+ }
+
+ if ((param->ignore_dnssec && rrset_is_dnssec(&rrset)) ||
+ (param->ignore_zonemd && rrset.type == KNOT_RRTYPE_ZONEMD)) {
+ continue;
+ }
+
+ knot_rrset_t rrset_from_second_node =
+ node_rrset(node_in_second_tree, rrset.type);
+ if (knot_rrset_empty(&rrset_from_second_node)) {
+ /* RRSet has been removed. Make a copy and remove. */
+ int ret = changeset_add_removal(
+ param->changeset, &rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ /* Diff RRSets. */
+ int ret = diff_rrsets(&rrset, &rrset_from_second_node,
+ param->changeset);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ for (unsigned i = 0; i < node_in_second_tree->rrset_count; i++) {
+ /* Search for the RRSet in the node from the second tree. */
+ knot_rrset_t rrset = node_rrset_at(node_in_second_tree, i);
+
+ /* SOAs are handled explicitly. */
+ if (rrset.type == KNOT_RRTYPE_SOA) {
+ continue;
+ }
+
+ if ((param->ignore_dnssec && rrset_is_dnssec(&rrset)) ||
+ (param->ignore_zonemd && rrset.type == KNOT_RRTYPE_ZONEMD)) {
+ continue;
+ }
+
+ knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type);
+ if (knot_rrset_empty(&rrset_from_first_node)) {
+ /* RRSet has been added. Make a copy and add. */
+ int ret = changeset_add_addition(
+ param->changeset, &rrset, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*!< \todo possibly not needed! */
+static int add_new_nodes(zone_node_t *node, void *data)
+{
+ if (node == NULL || data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct zone_diff_param *param = (struct zone_diff_param *)data;
+ if (param->changeset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /*
+ * If a node is not present in the second zone, it is a new node
+ * and has to be added to changeset. Differences on the RRSet level are
+ * already handled.
+ */
+ zone_node_t *new_node = zone_tree_get(param->nodes, node->owner);
+ if (new_node == NULL) {
+ assert(node);
+ return add_node(node, param->changeset, param->ignore_dnssec,
+ param->ignore_zonemd);
+ }
+
+ return KNOT_EOK;
+}
+
+static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2,
+ changeset_t *changeset, bool ignore_dnssec, bool ignore_zonemd)
+{
+ assert(changeset);
+
+ struct zone_diff_param param = {
+ .changeset = changeset,
+ .ignore_dnssec = ignore_dnssec,
+ .ignore_zonemd = ignore_zonemd,
+ };
+
+ // Traverse one tree, compare every node, each RRSet with its rdata.
+ param.nodes = nodes2;
+ int ret = zone_tree_apply(nodes1, knot_zone_diff_node, &param);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Some nodes may have been added. Add missing nodes to changeset.
+ param.nodes = nodes1;
+ return zone_tree_apply(nodes2, add_new_nodes, &param);
+}
+
+int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2,
+ changeset_t *changeset, bool ignore_dnssec, bool ignore_zonemd)
+{
+ if (changeset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone1 == NULL || zone2 == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ int ret_soa = load_soas(zone1, zone2, changeset);
+ if (ret_soa != KNOT_EOK && ret_soa != KNOT_ENODIFF) {
+ return ret_soa;
+ }
+
+ int ret = load_trees(zone1->nodes, zone2->nodes, changeset,
+ ignore_dnssec, ignore_zonemd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset,
+ ignore_dnssec, ignore_zonemd);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (ret_soa == KNOT_ENODIFF && !changeset_empty(changeset)) {
+ return KNOT_ESEMCHECK;
+ }
+
+ return ret_soa;
+}
+
+int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset)
+{
+ if (changeset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return load_trees(t1, t2, changeset, false, false);
+}
diff --git a/src/knot/zone/zone-diff.h b/src/knot/zone/zone-diff.h
new file mode 100644
index 0000000..f31e214
--- /dev/null
+++ b/src/knot/zone/zone-diff.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/contents.h"
+#include "knot/updates/changesets.h"
+
+/*!
+ * \brief Create diff between two zone trees.
+ * */
+int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2,
+ changeset_t *changeset, bool ignore_dnssec, bool ignore_zonemd);
+
+/*!
+ * \brief Add diff between two zone trees into the changeset.
+ */
+int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset);
diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c
new file mode 100644
index 0000000..41ec925
--- /dev/null
+++ b/src/knot/zone/zone-dump.c
@@ -0,0 +1,236 @@
+/* Copyright (C) 2022 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 <inttypes.h>
+
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/zone/zone-dump.h"
+#include "libknot/libknot.h"
+
+/*! \brief Size of auxiliary buffer. */
+#define DUMP_BUF_LEN (70 * 1024)
+
+/*! \brief Dump parameters. */
+typedef struct {
+ FILE *file;
+ char *buf;
+ size_t buflen;
+ uint64_t rr_count;
+ bool dump_rrsig;
+ bool dump_nsec;
+ const knot_dname_t *origin;
+ const knot_dump_style_t *style;
+ const char *first_comment;
+} dump_params_t;
+
+static int apex_node_dump_text(zone_node_t *node, dump_params_t *params)
+{
+ knot_rrset_t soa = node_rrset(node, KNOT_RRTYPE_SOA);
+
+ // Dump SOA record as a first.
+ if (!params->dump_nsec) {
+ int ret = knot_rrset_txt_dump(&soa, &params->buf, &params->buflen,
+ params->style);
+ if (ret < 0) {
+ return ret;
+ }
+ params->rr_count += soa.rrs.count;
+ fprintf(params->file, "%s", params->buf);
+ params->buf[0] = '\0';
+ }
+
+ // Dump other records.
+ for (uint16_t i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ switch (rrset.type) {
+ case KNOT_RRTYPE_NSEC:
+ continue;
+ case KNOT_RRTYPE_RRSIG:
+ continue;
+ case KNOT_RRTYPE_SOA:
+ continue;
+ default:
+ break;
+ }
+
+ int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
+ params->style);
+ if (ret < 0) {
+ return ret;
+ }
+ params->rr_count += rrset.rrs.count;
+ fprintf(params->file, "%s", params->buf);
+ params->buf[0] = '\0';
+ }
+
+ return KNOT_EOK;
+}
+
+static int node_dump_text(zone_node_t *node, void *data)
+{
+ dump_params_t *params = (dump_params_t *)data;
+
+ // Zone apex rrsets.
+ if (node->owner == params->origin && !params->dump_rrsig &&
+ !params->dump_nsec) {
+ apex_node_dump_text(node, params);
+ return KNOT_EOK;
+ }
+
+ // Dump non-apex rrsets.
+ for (uint16_t i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ switch (rrset.type) {
+ case KNOT_RRTYPE_RRSIG:
+ if (params->dump_rrsig) {
+ break;
+ }
+ continue;
+ case KNOT_RRTYPE_NSEC:
+ if (params->dump_nsec) {
+ break;
+ }
+ continue;
+ case KNOT_RRTYPE_NSEC3:
+ if (params->dump_nsec) {
+ break;
+ }
+ continue;
+ default:
+ if (params->dump_nsec || params->dump_rrsig) {
+ continue;
+ }
+ break;
+ }
+
+ // Dump block comment if available.
+ if (params->first_comment != NULL) {
+ fprintf(params->file, "%s", params->first_comment);
+ params->first_comment = NULL;
+ }
+
+ int ret = knot_rrset_txt_dump(&rrset, &params->buf, &params->buflen,
+ params->style);
+ if (ret < 0) {
+ return ret;
+ }
+ params->rr_count += rrset.rrs.count;
+ fprintf(params->file, "%s", params->buf);
+ params->buf[0] = '\0';
+ }
+
+ return KNOT_EOK;
+}
+
+int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments, const char *color)
+{
+ if (file == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ // Allocate auxiliary buffer for dumping operations.
+ char *buf = malloc(DUMP_BUF_LEN);
+ if (buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (comments) {
+ fprintf(file, ";; Zone dump (Knot DNS %s)\n", PACKAGE_VERSION);
+ }
+
+ // Set structure with parameters.
+ knot_dump_style_t style = KNOT_DUMP_STYLE_DEFAULT;
+ style.color = color;
+ style.now = knot_time();
+ dump_params_t params = {
+ .file = file,
+ .buf = buf,
+ .buflen = DUMP_BUF_LEN,
+ .rr_count = 0,
+ .origin = zone->apex->owner,
+ .style = &style,
+ .dump_rrsig = false,
+ .dump_nsec = false
+ };
+
+ // Dump standard zone records without RRSIGS.
+ int ret = zone_contents_apply(zone, node_dump_text, &params);
+ if (ret != KNOT_EOK) {
+ free(params.buf);
+ return ret;
+ }
+
+ // Dump RRSIG records if available.
+ params.dump_rrsig = true;
+ params.dump_nsec = false;
+ params.first_comment = comments ? ";; DNSSEC signatures\n" : NULL;
+ ret = zone_contents_apply(zone, node_dump_text, &params);
+ if (ret != KNOT_EOK) {
+ free(params.buf);
+ return ret;
+ }
+
+ // Dump NSEC chain if available.
+ params.dump_rrsig = false;
+ params.dump_nsec = true;
+ params.first_comment = comments ? ";; DNSSEC NSEC chain\n" : NULL;
+ ret = zone_contents_apply(zone, node_dump_text, &params);
+ if (ret != KNOT_EOK) {
+ free(params.buf);
+ return ret;
+ }
+
+ // Dump NSEC3 chain if available.
+ params.dump_rrsig = false;
+ params.dump_nsec = true;
+ params.first_comment = comments ? ";; DNSSEC NSEC3 chain\n" : NULL;
+ ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
+ if (ret != KNOT_EOK) {
+ free(params.buf);
+ return ret;
+ }
+
+ params.dump_rrsig = true;
+ params.dump_nsec = false;
+ params.first_comment = comments ? ";; DNSSEC NSEC3 signatures\n" : NULL;
+ ret = zone_contents_nsec3_apply(zone, node_dump_text, &params);
+ if (ret != KNOT_EOK) {
+ free(params.buf);
+ return ret;
+ }
+
+ if (comments) {
+ // Create formatted date-time string.
+ time_t now = time(NULL);
+ struct tm tm;
+ localtime_r(&now, &tm);
+ char date[64];
+ strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);
+
+ // Dump trailing statistics.
+ fprintf(file, ";; Written %"PRIu64" records\n"
+ ";; Time %s\n",
+ params.rr_count, date);
+ }
+
+ free(params.buf); // params.buf may be != buf because of knot_rrset_txt_dump_dynamic()
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/zone/zone-dump.h b/src/knot/zone/zone-dump.h
new file mode 100644
index 0000000..a0290ef
--- /dev/null
+++ b/src/knot/zone/zone-dump.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/zone.h"
+
+/*!
+ * \brief Dumps given zone to text file.
+ *
+ * \param zone Zone to be saved.
+ * \param file File to write to.
+ * \param comments Add separating comments indicator.
+ * \param color Optional color control sequence.
+ *
+ * \retval KNOT_EOK on success.
+ * \retval < 0 if error.
+ */
+int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments, const char *color);
diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c
new file mode 100644
index 0000000..11cba83
--- /dev/null
+++ b/src/knot/zone/zone-load.c
@@ -0,0 +1,173 @@
+/* Copyright (C) 2023 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 "knot/common/log.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/journal_read.h"
+#include "knot/zone/zone-diff.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zonefile.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/zone-events.h"
+#include "libknot/libknot.h"
+
+int zone_load_contents(conf_t *conf, const knot_dname_t *zone_name,
+ zone_contents_t **contents, semcheck_optional_t semcheck_mode,
+ bool fail_on_warning)
+{
+ if (conf == NULL || zone_name == NULL || contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ char *zonefile = conf_zonefile(conf, zone_name);
+
+ zloader_t zl;
+ int ret = zonefile_open(&zl, zonefile, zone_name, semcheck_mode, time(NULL));
+ free(zonefile);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ sem_handler_t handler = {
+ .cb = err_handler_logger
+ };
+
+ zl.err_handler = &handler;
+ zl.creator->master = !zone_load_can_bootstrap(conf, zone_name);
+
+ *contents = zonefile_load(&zl);
+ zonefile_close(&zl);
+ if (*contents == NULL) {
+ return KNOT_ERROR;
+ }
+ if (handler.warning && fail_on_warning) {
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ return KNOT_ESEMCHECK;
+ }
+
+ return KNOT_EOK;
+}
+
+static int apply_one_cb(bool remove, const knot_rrset_t *rr, void *ctx)
+{
+ zone_node_t *unused = NULL;
+ zone_contents_t *contents = ctx;
+ int ret = remove ? zone_contents_remove_rr(contents, rr, &unused)
+ : zone_contents_add_rr(contents, rr, &unused);
+ if (ret == KNOT_ENOENT && remove && knot_rrtype_is_dnssec(rr->type)) {
+ // Compatibility with imperfect journal contents (versions < 2.9) if
+ // 'zonefile-load: difference' and 'dnssec-signing: on`.
+ // Journal history can contain a changeset with removed DNSSEC records
+ // which are not present in the zonefile.
+ return KNOT_EOK;
+ } else {
+ return ret;
+ }
+}
+
+int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Check if journal is used (later in zone_changes_load() and zone is not empty.
+ if (zone_contents_is_empty(contents)) {
+ return KNOT_EOK;
+ }
+ uint32_t serial = zone_contents_serial(contents);
+
+ journal_read_t *read = NULL;
+ int ret = journal_read_begin(zone_journal(zone), false, serial, &read);
+ switch (ret) {
+ case KNOT_EOK:
+ break;
+ case KNOT_ENOENT:
+ return KNOT_EOK;
+ default:
+ return ret;
+ }
+
+ ret = journal_read_rrsets(read, apply_one_cb, contents);
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "changes from journal applied, serial %u -> %u",
+ serial, zone_contents_serial(contents));
+ } else {
+ log_zone_error(zone->name, "failed to apply journal changes, serial %u -> %u (%s)",
+ serial, zone_contents_serial(contents),
+ knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+int zone_load_from_journal(conf_t *conf, zone_t *zone, zone_contents_t **contents)
+{
+ if (conf == NULL || zone == NULL || contents == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ *contents = zone_contents_new(zone->name, true);
+ if (*contents == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ journal_read_t *read = NULL;
+ int ret = journal_read_begin(zone_journal(zone), true, 0, &read);
+ if (ret == KNOT_ENOENT) {
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ return ret;
+ }
+
+ knot_rrset_t rr = { 0 };
+ while (ret == KNOT_EOK && journal_read_rrset(read, &rr, false)) {
+ zone_node_t *unused = NULL;
+ ret = zone_contents_add_rr(*contents, &rr, &unused);
+ journal_read_clear_rrset(&rr);
+ }
+
+ if (ret == KNOT_EOK) {
+ ret = journal_read_rrsets(read, apply_one_cb, *contents);
+ } else {
+ journal_read_end(read);
+ }
+
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "zone loaded from journal, serial %u",
+ zone_contents_serial(*contents));
+ } else {
+ log_zone_error(zone->name, "failed to load zone from journal, serial %u (%s)",
+ zone_contents_serial(*contents), knot_strerror(ret));
+ zone_contents_deep_free(*contents);
+ *contents = NULL;
+ }
+
+ return ret;
+}
+
+bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name)
+{
+ if (conf == NULL || zone_name == NULL) {
+ return false;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_MASTER, zone_name);
+ size_t count = conf_val_count(&val);
+
+ return count > 0;
+}
diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h
new file mode 100644
index 0000000..c438903
--- /dev/null
+++ b/src/knot/zone/zone-load.h
@@ -0,0 +1,68 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/zone/semantic-check.h"
+#include "knot/zone/zone.h"
+
+/*!
+ * \brief Load zone contents according to the configuration.
+ *
+ * \param conf
+ * \param zone_name
+ * \param contents
+ * \param semcheck_mode
+ * \param fail_on_warning
+ *
+ * \retval KNOT_EOK if success.
+ * \retval KNOT_ESEMCHECK if any semantic check warning.
+ * \retval KNOT_E* if error.
+ */
+int zone_load_contents(conf_t *conf, const knot_dname_t *zone_name,
+ zone_contents_t **contents, semcheck_optional_t semcheck_mode,
+ bool fail_on_warning);
+
+/*!
+ * \brief Update zone contents from the journal.
+ *
+ * \warning If error, the zone is in inconsistent state and should be freed.
+ *
+ * \param conf
+ * \param zone
+ * \param contents
+ * \return KNOT_EOK or an error
+ */
+int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents);
+
+/*!
+ * \brief Load zone contents from journal (headless).
+ *
+ * \param conf
+ * \param zone
+ * \param contents
+ * \return KNOT_EOK or an error
+ */
+int zone_load_from_journal(conf_t *conf, zone_t *zone, zone_contents_t **contents);
+
+/*!
+ * \brief Check if zone can be bootstrapped.
+ *
+ * \param conf
+ * \param zone_name
+ */
+bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name);
diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c
new file mode 100644
index 0000000..87dde18
--- /dev/null
+++ b/src/knot/zone/zone-tree.c
@@ -0,0 +1,512 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdlib.h>
+
+#include "knot/zone/zone-tree.h"
+#include "libknot/consts.h"
+#include "libknot/errcode.h"
+#include "libknot/packet/wire.h"
+
+typedef struct {
+ zone_tree_apply_cb_t func;
+ void *data;
+ int binode_second;
+} zone_tree_func_t;
+
+static int tree_apply_cb(trie_val_t *node, void *data)
+{
+ zone_tree_func_t *f = (zone_tree_func_t *)data;
+ zone_node_t *n = (zone_node_t *)(*node) + f->binode_second;
+ assert(!f->binode_second || (n->flags & NODE_FLAGS_SECOND));
+ return f->func(n, f->data);
+}
+
+zone_tree_t *zone_tree_create(bool use_binodes)
+{
+ zone_tree_t *t = calloc(1, sizeof(*t));
+ if (t != NULL) {
+ if (use_binodes) {
+ t->flags = ZONE_TREE_USE_BINODES;
+ }
+ t->trie = trie_create(NULL);
+ if (t->trie == NULL) {
+ free(t);
+ t = NULL;
+ }
+ }
+ return t;
+}
+
+zone_tree_t *zone_tree_cow(zone_tree_t *from)
+{
+ zone_tree_t *to = calloc(1, sizeof(*to));
+ if (to == NULL) {
+ return to;
+ }
+ to->flags = from->flags ^ ZONE_TREE_BINO_SECOND;
+ from->cow = trie_cow(from->trie, NULL, NULL);
+ to->cow = from->cow;
+ to->trie = trie_cow_new(to->cow);
+ if (to->trie == NULL) {
+ free(to);
+ to = NULL;
+ }
+ return to;
+}
+
+static trie_val_t nocopy(const trie_val_t val, _unused_ knot_mm_t *mm)
+{
+ return val;
+}
+
+zone_tree_t *zone_tree_shallow_copy(zone_tree_t *from)
+{
+ zone_tree_t *to = calloc(1, sizeof(*to));
+ if (to == NULL) {
+ return to;
+ }
+ to->flags = from->flags;
+ to->trie = trie_dup(from->trie, nocopy, NULL);
+ if (to->trie == NULL) {
+ free(to);
+ to = NULL;
+ }
+ return to;
+}
+
+int zone_tree_insert(zone_tree_t *tree, zone_node_t **node)
+{
+ if (tree == NULL || node == NULL || *node == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert((*node)->owner);
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf((*node)->owner, lf_storage);
+ assert(lf);
+
+ if (tree->cow != NULL) {
+ *trie_get_cow(tree->cow, lf + 1, *lf) = binode_first(*node);
+ } else {
+ *trie_get_ins(tree->trie, lf + 1, *lf) = binode_first(*node);
+ }
+
+ *node = zone_tree_fix_get(*node, tree);
+
+ return KNOT_EOK;
+}
+
+int zone_tree_insert_with_parents(zone_tree_t *tree, zone_node_t *node, bool without_parents)
+{
+ int ret = KNOT_EOK;
+ do {
+ ret = zone_tree_insert(tree, &node);
+ node = node->parent;
+ } while (node != NULL && ret == KNOT_EOK && !without_parents);
+ return ret;
+}
+
+zone_node_t *zone_tree_get(zone_tree_t *tree, const knot_dname_t *owner)
+{
+ if (owner == NULL) {
+ return NULL;
+ }
+
+ if (zone_tree_is_empty(tree)) {
+ return NULL;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(owner, lf_storage);
+ assert(lf);
+
+ trie_val_t *val = trie_get_try(tree->trie, lf + 1, *lf);
+ if (val == NULL) {
+ return NULL;
+ }
+
+ return zone_tree_fix_get(*val, tree);
+}
+
+int zone_tree_get_less_or_equal(zone_tree_t *tree,
+ const knot_dname_t *owner,
+ zone_node_t **found,
+ zone_node_t **previous)
+{
+ if (owner == NULL || found == NULL || previous == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone_tree_is_empty(tree)) {
+ return KNOT_ENONODE;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(owner, lf_storage);
+ assert(lf);
+
+ trie_val_t *fval = NULL;
+ int ret = trie_get_leq(tree->trie, lf + 1, *lf, &fval);
+ if (fval != NULL) {
+ *found = zone_tree_fix_get(*fval, tree);
+ }
+
+ int exact_match = 0;
+ if (ret == KNOT_EOK) {
+ if (fval != NULL) {
+ *previous = node_prev(*found);
+ }
+ exact_match = 1;
+ } else if (ret == 1) {
+ *previous = *found;
+ *found = NULL;
+ } else {
+ /* Previous should be the rightmost node.
+ * For regular zone it is the node left of apex, but for some
+ * cases like NSEC3, there is no such sort of thing (name wise).
+ */
+ /*! \todo We could store rightmost node in zonetree probably. */
+ zone_tree_it_t it = { 0 };
+ ret = zone_tree_it_begin(tree, &it);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *previous = zone_tree_it_val(&it); /* leftmost */
+ assert(*previous != NULL); // cppcheck
+ *previous = zone_tree_fix_get(*previous, tree);
+ *previous = node_prev(*previous); /* rightmost */
+ *found = NULL;
+ zone_tree_it_free(&it);
+ }
+
+ return exact_match;
+}
+
+/*! \brief Removes node with the given owner from the zone tree. */
+void zone_tree_remove_node(zone_tree_t *tree, const knot_dname_t *owner)
+{
+ if (zone_tree_is_empty(tree) || owner == NULL) {
+ return;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(owner, lf_storage);
+ assert(lf);
+
+ trie_val_t *rval = trie_get_try(tree->trie, lf + 1, *lf);
+ if (rval != NULL) {
+ if (tree->cow != NULL) {
+ trie_del_cow(tree->cow, lf + 1, *lf, NULL);
+ } else {
+ trie_del(tree->trie, lf + 1, *lf, NULL);
+ }
+ }
+}
+
+int zone_tree_add_node(zone_tree_t *tree, zone_node_t *apex, const knot_dname_t *dname,
+ zone_tree_new_node_cb_t new_cb, void *new_cb_ctx, zone_node_t **new_node)
+{
+ int in_bailiwick = knot_dname_in_bailiwick(dname, apex->owner);
+ if (in_bailiwick == 0) {
+ *new_node = apex;
+ return KNOT_EOK;
+ } else if (in_bailiwick < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+
+ *new_node = zone_tree_get(tree, dname);
+ if (*new_node == NULL) {
+ *new_node = new_cb(dname, new_cb_ctx);
+ if (*new_node == NULL) {
+ return KNOT_ENOMEM;
+ }
+ int ret = zone_tree_insert(tree, new_node);
+ assert(!((*new_node)->flags & NODE_FLAGS_DELETED));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ zone_node_t *parent = NULL;
+ ret = zone_tree_add_node(tree, apex, knot_wire_next_label(dname, NULL), new_cb, new_cb_ctx, &parent);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ (*new_node)->parent = parent;
+ if (parent != NULL) {
+ parent->children++;
+ if (knot_dname_is_wildcard(dname)) {
+ parent->flags |= NODE_FLAGS_WILDCARD_CHILD;
+ }
+ }
+ }
+ return KNOT_EOK;
+}
+
+int zone_tree_del_node(zone_tree_t *tree, zone_node_t *node, bool free_deleted)
+{
+ zone_node_t *parent = node_parent(node);
+ bool wildcard = knot_dname_is_wildcard(node->owner);
+
+ node->parent = NULL;
+ node->flags |= NODE_FLAGS_DELETED;
+ zone_tree_remove_node(tree, node->owner);
+
+ if (free_deleted) {
+ node_free(node, NULL);
+ }
+
+ int ret = KNOT_EOK;
+ if (ret == KNOT_EOK && parent != NULL) {
+ parent->children--;
+ if (wildcard) {
+ parent->flags &= ~NODE_FLAGS_WILDCARD_CHILD;
+ }
+ if (parent->children == 0 && parent->rrset_count == 0 &&
+ !(parent->flags & NODE_FLAGS_APEX)) {
+ ret = zone_tree_del_node(tree, parent, free_deleted);
+ }
+ }
+ return ret;
+}
+
+int zone_tree_apply(zone_tree_t *tree, zone_tree_apply_cb_t function, void *data)
+{
+ if (function == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone_tree_is_empty(tree)) {
+ return KNOT_EOK;
+ }
+
+ zone_tree_func_t f = {
+ .func = function,
+ .data = data,
+ .binode_second = ((tree->flags & ZONE_TREE_BINO_SECOND) ? 1 : 0),
+ };
+
+ return trie_apply(tree->trie, tree_apply_cb, &f);
+}
+
+int zone_tree_sub_apply(zone_tree_t *tree, const knot_dname_t *sub_root,
+ bool excl_root, zone_tree_apply_cb_t function, void *data)
+{
+ zone_tree_it_t it = { 0 };
+ int ret = zone_tree_it_sub_begin(tree, sub_root, &it);
+ if (excl_root && ret == KNOT_EOK && !zone_tree_it_finished(&it)) {
+ zone_tree_it_next(&it);
+ }
+ while (ret == KNOT_EOK && !zone_tree_it_finished(&it)) {
+ ret = function(zone_tree_it_val(&it), data);
+ zone_tree_it_next(&it);
+ }
+ zone_tree_it_free(&it);
+ return ret;
+}
+
+int zone_tree_it_begin(zone_tree_t *tree, zone_tree_it_t *it)
+{
+ return zone_tree_it_double_begin(tree, NULL, it);
+}
+
+int zone_tree_it_sub_begin(zone_tree_t *tree, const knot_dname_t *sub_root,
+ zone_tree_it_t *it)
+{
+ if (tree == NULL || sub_root == NULL) {
+ return KNOT_EINVAL;
+ }
+ int ret = zone_tree_it_begin(tree, it);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ it->sub_root = knot_dname_copy(sub_root, NULL);
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(sub_root, lf_storage);
+ ret = trie_it_get_leq(it->it, lf + 1, *lf);
+ if ((ret != KNOT_EOK && ret != KNOT_ENOENT) || it->sub_root == NULL) {
+ zone_tree_it_free(it);
+ return ret == KNOT_EOK ? KNOT_ENOMEM : ret;
+ }
+ return KNOT_EOK;
+}
+
+int zone_tree_it_double_begin(zone_tree_t *first, zone_tree_t *second, zone_tree_it_t *it)
+{
+ if (it->tree == NULL) {
+ it->it = trie_it_begin(first->trie);
+ if (it->it == NULL) {
+ return KNOT_ENOMEM;
+ }
+ if (trie_it_finished(it->it) && second != NULL) { // first tree is empty
+ trie_it_free(it->it);
+ it->it = trie_it_begin(second->trie);
+ it->tree = second;
+ it->next_tree = NULL;
+ } else {
+ it->tree = first;
+ it->next_tree = second;
+ }
+ it->binode_second = ((it->tree->flags & ZONE_TREE_BINO_SECOND) ? 1 : 0);
+ }
+ return KNOT_EOK;
+}
+
+static bool sub_done(zone_tree_it_t *it)
+{
+ return it->sub_root != NULL &&
+ knot_dname_in_bailiwick(zone_tree_it_val(it)->owner, it->sub_root) < 0;
+}
+
+bool zone_tree_it_finished(zone_tree_it_t *it)
+{
+ return it->it == NULL || it->tree == NULL || trie_it_finished(it->it) || sub_done(it);
+}
+
+zone_node_t *zone_tree_it_val(zone_tree_it_t *it)
+{
+ zone_node_t *node = (zone_node_t *)(*trie_it_val(it->it)) + it->binode_second;
+ assert(!it->binode_second || (node->flags & NODE_FLAGS_SECOND));
+ return node;
+}
+
+void zone_tree_it_del(zone_tree_it_t *it)
+{
+ trie_it_del(it->it);
+}
+
+void zone_tree_it_next(zone_tree_it_t *it)
+{
+ trie_it_next(it->it);
+ if (it->next_tree != NULL && trie_it_finished(it->it)) {
+ trie_it_free(it->it);
+ it->tree = it->next_tree;
+ it->binode_second = ((it->tree->flags & ZONE_TREE_BINO_SECOND) ? 1 : 0);
+ it->next_tree = NULL;
+ it->it = trie_it_begin(it->tree->trie);
+ assert(it->sub_root == NULL);
+ }
+}
+
+void zone_tree_it_free(zone_tree_it_t *it)
+{
+ trie_it_free(it->it);
+ knot_dname_free(it->sub_root, NULL);
+ memset(it, 0, sizeof(*it));
+}
+
+int zone_tree_delsafe_it_begin(zone_tree_t *tree, zone_tree_delsafe_it_t *it, bool include_deleted)
+{
+ it->incl_del = include_deleted;
+ it->total = zone_tree_count(tree);
+ if (it->total == 0) {
+ it->current = 0;
+ it->nodes = NULL;
+ return KNOT_EOK;
+ }
+ it->nodes = malloc(it->total * sizeof(*it->nodes));
+ if (it->nodes == NULL) {
+ return KNOT_ENOMEM;
+ }
+ it->current = 0;
+
+ zone_tree_it_t tmp = { 0 };
+ int ret = zone_tree_it_begin(tree, &tmp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ while (!zone_tree_it_finished(&tmp)) {
+ it->nodes[it->current++] = zone_tree_it_val(&tmp);
+ zone_tree_it_next(&tmp);
+ }
+ zone_tree_it_free(&tmp);
+ assert(it->total == it->current);
+
+ zone_tree_delsafe_it_restart(it);
+
+ return KNOT_EOK;
+}
+
+bool zone_tree_delsafe_it_finished(zone_tree_delsafe_it_t *it)
+{
+ return (it->current >= it->total);
+}
+
+void zone_tree_delsafe_it_restart(zone_tree_delsafe_it_t *it)
+{
+ it->current = 0;
+
+ while (!it->incl_del && !zone_tree_delsafe_it_finished(it) &&
+ (zone_tree_delsafe_it_val(it)->flags & NODE_FLAGS_DELETED)) {
+ it->current++;
+ }
+}
+
+zone_node_t *zone_tree_delsafe_it_val(zone_tree_delsafe_it_t *it)
+{
+ return it->nodes[it->current];
+}
+
+void zone_tree_delsafe_it_next(zone_tree_delsafe_it_t *it)
+{
+ do {
+ it->current++;
+ } while (!it->incl_del && !zone_tree_delsafe_it_finished(it) &&
+ (zone_tree_delsafe_it_val(it)->flags & NODE_FLAGS_DELETED));
+}
+
+void zone_tree_delsafe_it_free(zone_tree_delsafe_it_t *it)
+{
+ free(it->nodes);
+ memset(it, 0, sizeof(*it));
+}
+
+static int merge_cb(zone_node_t *node, void *ctx)
+{
+ return zone_tree_insert(ctx, &node);
+}
+
+int zone_tree_merge(zone_tree_t *into, zone_tree_t *what)
+{
+ return zone_tree_apply(what, merge_cb, into);
+}
+
+static int binode_unify_cb(zone_node_t *node, void *ctx)
+{
+ binode_unify(node, *(bool *)ctx, NULL);
+ return KNOT_EOK;
+}
+
+void zone_trees_unify_binodes(zone_tree_t *nodes, zone_tree_t *nsec3_nodes, bool free_deleted)
+{
+ if (nodes != NULL) {
+ zone_tree_apply(nodes, binode_unify_cb, &free_deleted);
+ }
+ if (nsec3_nodes != NULL) {
+ zone_tree_apply(nsec3_nodes, binode_unify_cb, &free_deleted);
+ }
+}
+
+void zone_tree_free(zone_tree_t **tree)
+{
+ if (tree == NULL || *tree == NULL) {
+ return;
+ }
+
+ trie_free((*tree)->trie);
+ free(*tree);
+ *tree = NULL;
+}
diff --git a/src/knot/zone/zone-tree.h b/src/knot/zone/zone-tree.h
new file mode 100644
index 0000000..384e87e
--- /dev/null
+++ b/src/knot/zone/zone-tree.h
@@ -0,0 +1,337 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/ucw/lists.h"
+#include "knot/zone/node.h"
+
+enum {
+ /*! Indication of a zone tree with bi-nodes (two zone_node_t structures allocated for one node). */
+ ZONE_TREE_USE_BINODES = (1 << 0),
+ /*! If set, from each bi-node in the zone tree, the second zone_node_t is valid. */
+ ZONE_TREE_BINO_SECOND = (1 << 1),
+};
+
+typedef struct {
+ trie_t *trie;
+ trie_cow_t *cow; // non-NULL only during zone update
+ uint16_t flags;
+} zone_tree_t;
+
+/*!
+ * \brief Signature of callback for zone apply functions.
+ */
+typedef int (*zone_tree_apply_cb_t)(zone_node_t *node, void *data);
+
+typedef zone_node_t *(*zone_tree_new_node_cb_t)(const knot_dname_t *dname, void *ctx);
+
+/*!
+ * \brief Zone tree iteration context.
+ */
+typedef struct {
+ zone_tree_t *tree;
+ trie_it_t *it;
+ int binode_second;
+
+ zone_tree_t *next_tree;
+ knot_dname_t *sub_root;
+} zone_tree_it_t;
+
+typedef struct {
+ zone_node_t **nodes;
+ size_t total;
+ size_t current;
+ bool incl_del;
+} zone_tree_delsafe_it_t;
+
+/*!
+ * \brief Creates the zone tree.
+ *
+ * \return created zone tree structure.
+ */
+zone_tree_t *zone_tree_create(bool use_binodes);
+
+zone_tree_t *zone_tree_cow(zone_tree_t *from);
+
+/*!
+ * \brief Create a clone of existing zone_tree.
+ *
+ * \note Copies only the trie, not individual nodes.
+ *
+ * \warning Don't use COW in the duplicate.
+ */
+zone_tree_t *zone_tree_shallow_copy(zone_tree_t *from);
+
+/*!
+ * \brief Return number of nodes in the zone tree.
+ *
+ * \param tree Zone tree.
+ *
+ * \return number of nodes in tree.
+ */
+inline static size_t zone_tree_count(const zone_tree_t *tree)
+{
+ if (tree == NULL || tree->trie == NULL) {
+ return 0;
+ }
+
+ return trie_weight(tree->trie);
+}
+
+/*!
+ * \brief Checks if the zone tree is empty.
+ *
+ * \param tree Zone tree to check.
+ *
+ * \return Nonzero if the zone tree is empty.
+ */
+inline static bool zone_tree_is_empty(const zone_tree_t *tree)
+{
+ return zone_tree_count(tree) == 0;
+}
+
+inline static zone_node_t *zone_tree_fix_get(zone_node_t *node, const zone_tree_t *tree)
+{
+ assert(((node->flags & NODE_FLAGS_BINODE) ? 1 : 0) == ((tree->flags & ZONE_TREE_USE_BINODES) ? 1 : 0));
+ assert((tree->flags & ZONE_TREE_USE_BINODES) || !(tree->flags & ZONE_TREE_BINO_SECOND));
+ return binode_node(node, (tree->flags & ZONE_TREE_BINO_SECOND));
+}
+
+inline static zone_node_t *node_new_for_tree(const knot_dname_t *owner, const zone_tree_t *tree, knot_mm_t *mm)
+{
+ assert((tree->flags & ZONE_TREE_USE_BINODES) || !(tree->flags & ZONE_TREE_BINO_SECOND));
+ return node_new(owner, (tree->flags & ZONE_TREE_USE_BINODES), (tree->flags & ZONE_TREE_BINO_SECOND), mm);
+}
+
+/*!
+ * \brief Inserts the given node into the zone tree.
+ *
+ * \param tree Zone tree to insert the node into.
+ * \param node Node to insert. If it's binode, the pointer will be adjusted to correct node.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ */
+int zone_tree_insert(zone_tree_t *tree, zone_node_t **node);
+
+/*!
+ * \brief Insert a node together with its parents (iteratively node->parent).
+ *
+ * \param tree Zone tree to insert into.
+ * \param node Node to be inserted with parents.
+ * \param without_parents Actually, insert it without parents.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_insert_with_parents(zone_tree_t *tree, zone_node_t *node, bool without_parents);
+
+/*!
+ * \brief Finds node with the given owner in the zone tree.
+ *
+ * \param tree Zone tree to search in.
+ * \param owner Owner of the node to find.
+ *
+ * \retval Found node or NULL.
+ */
+zone_node_t *zone_tree_get(zone_tree_t *tree, const knot_dname_t *owner);
+
+/*!
+ * \brief Tries to find the given domain name in the zone tree and returns the
+ * associated node and previous node in canonical order.
+ *
+ * \param tree Zone to search in.
+ * \param owner Owner of the node to find.
+ * \param found Found node.
+ * \param previous Previous node in canonical order (i.e. the one directly
+ * preceding \a owner in canonical order, regardless if the name
+ * is in the zone or not).
+ *
+ * \retval > 0 if the domain name was found. In such case \a found holds the
+ * zone node with \a owner as its owner.
+ * \a previous is set properly.
+ * \retval 0 if the domain name was not found. \a found may hold any (or none)
+ * node. \a previous is set properly.
+ * \retval KNOT_EINVAL
+ * \retval KNOT_ENOMEM
+ */
+int zone_tree_get_less_or_equal(zone_tree_t *tree,
+ const knot_dname_t *owner,
+ zone_node_t **found,
+ zone_node_t **previous);
+
+/*!
+ * \brief Remove a node from a tree with no checks.
+ *
+ * \param tree The tree to remove from.
+ * \param owner The node to remove.
+ */
+void zone_tree_remove_node(zone_tree_t *tree, const knot_dname_t *owner);
+
+/*!
+ * \brief Create a node in zone tree if not already exists, and also all parent nodes.
+ *
+ * \param tree Zone tree to insert into.
+ * \param apex Zone contents apex node.
+ * \param dname Name of the node to be added.
+ * \param new_cb Callback for allocating new node.
+ * \param new_cb_ctx Context to be passed to allocating callback.
+ * \param new_node Output: pointer on added (or existing) node with specified dname.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_add_node(zone_tree_t *tree, zone_node_t *apex, const knot_dname_t *dname,
+ zone_tree_new_node_cb_t new_cb, void *new_cb_ctx, zone_node_t **new_node);
+
+/*!
+ * \brief Remove a node in zone tree, removing also empty parents.
+ *
+ * \param tree Zone tree to remove from.
+ * \param node Node to be removed.
+ * \param free_deleted Indication to free node.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_del_node(zone_tree_t *tree, zone_node_t *node, bool free_deleted);
+
+/*!
+ * \brief Applies the given function to each node in the zone in order.
+ *
+ * \param tree Zone tree to apply the function to.
+ * \param function Function to be applied to each node of the zone.
+ * \param data Arbitrary data to be passed to the function.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int zone_tree_apply(zone_tree_t *tree, zone_tree_apply_cb_t function, void *data);
+
+/*!
+ * \brief Applies given function to each node in a subtree.
+ *
+ * \param tree Zone tree.
+ * \param sub_root Name denoting the subtree.
+ * \param excl_root Exclude the subtree root.
+ * \param function Callback to be applied.
+ * \param data Callback context.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_sub_apply(zone_tree_t *tree, const knot_dname_t *sub_root,
+ bool excl_root, zone_tree_apply_cb_t function, void *data);
+
+/*!
+ * \brief Start zone tree iteration.
+ *
+ * \param tree Zone tree to iterate over.
+ * \param it Out: iteration context. It shall be zeroed before.
+ *
+ * \return KNOT_OK, KNOT_ENOMEM
+ */
+int zone_tree_it_begin(zone_tree_t *tree, zone_tree_it_t *it);
+
+/*!
+ * \brief Start iteration over a subtree.
+ *
+ * \param tree Zone tree to iterate in.
+ * \param sub_root Iterate over node of this name and all children.
+ * \param it Out: iteration context, shall be zeroed before.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_it_sub_begin(zone_tree_t *tree, const knot_dname_t *sub_root,
+ zone_tree_it_t *it);
+
+/*!
+ * \brief Start iteration of two zone trees.
+ *
+ * This is useful e.g. for iteration over normal and NSEC3 nodes.
+ *
+ * \param first First tree to be iterated over.
+ * \param second Second tree to be iterated over.
+ * \param it Out: iteration context. It shall be zeroed before.
+ *
+ * \return KNOT_OK, KNOT_ENOMEM
+ */
+int zone_tree_it_double_begin(zone_tree_t *first, zone_tree_t *second, zone_tree_it_t *it);
+
+/*!
+ * \brief Return true iff iteration is finished.
+ *
+ * \note The iteration context needs to be freed afterwards nevertheless.
+ */
+bool zone_tree_it_finished(zone_tree_it_t *it);
+
+/*!
+ * \brief Return the node, zone iteration is currently pointing at.
+ *
+ * \note Don't call this when zone_tree_it_finished.
+ */
+zone_node_t *zone_tree_it_val(zone_tree_it_t *it);
+
+/*!
+ * \brief Remove from zone tree the node that iteration is pointing at.
+ *
+ * \note This doesn't free the node.
+ */
+void zone_tree_it_del(zone_tree_it_t *it);
+
+/*!
+ * \brief Move the iteration to next node.
+ */
+void zone_tree_it_next(zone_tree_it_t *it);
+
+/*!
+ * \brief Free zone iteration context.
+ */
+void zone_tree_it_free(zone_tree_it_t *it);
+
+/*!
+ * \brief Zone tree iteration allowing tree changes.
+ *
+ * The semantics is the same like for normal iteration.
+ * The set of iterated nodes is according to zone tree state on the beginning.
+ */
+int zone_tree_delsafe_it_begin(zone_tree_t *tree, zone_tree_delsafe_it_t *it, bool include_deleted);
+bool zone_tree_delsafe_it_finished(zone_tree_delsafe_it_t *it);
+void zone_tree_delsafe_it_restart(zone_tree_delsafe_it_t *it);
+zone_node_t *zone_tree_delsafe_it_val(zone_tree_delsafe_it_t *it);
+void zone_tree_delsafe_it_next(zone_tree_delsafe_it_t *it);
+void zone_tree_delsafe_it_free(zone_tree_delsafe_it_t *it);
+
+/*!
+ * \brief Merge all nodes from 'what' to 'into'.
+ *
+ * \param into Zone tree to be inserted into..
+ * \param what ...all nodes from this one.
+ *
+ * \return KNOT_E*
+ */
+int zone_tree_merge(zone_tree_t *into, zone_tree_t *what);
+
+/*!
+ * \brief Unify all bi-nodes in specified trees.
+ */
+void zone_trees_unify_binodes(zone_tree_t *nodes, zone_tree_t *nsec3_nodes, bool free_deleted);
+
+/*!
+ * \brief Destroys the zone tree, not touching the saved data.
+ *
+ * \param tree Zone tree to be destroyed.
+ */
+void zone_tree_free(zone_tree_t **tree);
diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c
new file mode 100644
index 0000000..15a9c54
--- /dev/null
+++ b/src/knot/zone/zone.c
@@ -0,0 +1,792 @@
+/* Copyright (C) 2022 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 <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <urcu.h>
+
+#include "knot/common/log.h"
+#include "knot/conf/module.h"
+#include "knot/dnssec/kasp/kasp_db.h"
+#include "knot/events/replan.h"
+#include "knot/journal/journal_read.h"
+#include "knot/journal/journal_write.h"
+#include "knot/nameserver/process_query.h"
+#include "knot/query/requestor.h"
+#include "knot/updates/zone-update.h"
+#include "knot/server/server.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/serial.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zonefile.h"
+#include "libknot/libknot.h"
+#include "contrib/sockaddr.h"
+#include "contrib/mempattern.h"
+#include "contrib/ucw/lists.h"
+#include "contrib/ucw/mempool.h"
+
+#define JOURNAL_LOCK_MUTEX (&zone->journal_lock)
+#define JOURNAL_LOCK_RW pthread_mutex_lock(JOURNAL_LOCK_MUTEX);
+#define JOURNAL_UNLOCK_RW pthread_mutex_unlock(JOURNAL_LOCK_MUTEX);
+
+knot_dynarray_define(notifailed_rmt, notifailed_rmt_hash, DYNARRAY_VISIBILITY_NORMAL);
+
+static void free_ddns_queue(zone_t *zone)
+{
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, zone->ddns_queue) {
+ knot_request_free(node->d, NULL);
+ }
+ ptrlist_free(&zone->ddns_queue, NULL);
+}
+
+/*!
+ * \param allow_empty_zone useful when need to flush journal but zone is not yet loaded
+ * ...in this case we actually don't have to do anything because the zonefile is current,
+ * but we must mark the journal as flushed
+ */
+static int flush_journal(conf_t *conf, zone_t *zone, bool allow_empty_zone, bool verbose)
+{
+ /*! @note Function expects nobody will change zone contents meanwhile. */
+
+ assert(zone);
+
+ int ret = KNOT_EOK;
+ zone_journal_t j = zone_journal(zone);
+
+ bool force = zone_get_flag(zone, ZONE_FORCE_FLUSH, true);
+ bool user_flush = zone_get_flag(zone, ZONE_USER_FLUSH, true);
+
+ conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name);
+ int64_t sync_timeout = conf_int(&val);
+
+ if (zone_contents_is_empty(zone->contents)) {
+ if (allow_empty_zone && journal_is_existing(j)) {
+ ret = journal_set_flushed(j);
+ } else {
+ ret = KNOT_EEMPTYZONE;
+ }
+ goto flush_journal_replan;
+ }
+
+ /* Check for disabled zonefile synchronization. */
+ if (sync_timeout < 0 && !force) {
+ if (verbose) {
+ log_zone_warning(zone->name, "zonefile synchronization disabled, "
+ "use force command to override it");
+ }
+ return KNOT_EOK;
+ }
+
+ /* Check for updated zone. */
+ zone_contents_t *contents = zone->contents;
+ uint32_t serial_to = zone_contents_serial(contents);
+ if (!force && !user_flush &&
+ zone->zonefile.exists && zone->zonefile.serial == serial_to &&
+ !zone->zonefile.retransfer && !zone->zonefile.resigned) {
+ ret = KNOT_EOK; /* No differences. */
+ goto flush_journal_replan;
+ }
+
+ char *zonefile = conf_zonefile(conf, zone->name);
+
+ /* Synchronize journal. */
+ ret = zonefile_write(zonefile, contents);
+ if (ret != KNOT_EOK) {
+ log_zone_warning(zone->name, "failed to update zone file (%s)",
+ knot_strerror(ret));
+ free(zonefile);
+ goto flush_journal_replan;
+ }
+
+ if (zone->zonefile.exists) {
+ log_zone_info(zone->name, "zone file updated, serial %u -> %u",
+ zone->zonefile.serial, serial_to);
+ } else {
+ log_zone_info(zone->name, "zone file updated, serial %u",
+ serial_to);
+ }
+
+ /* Update zone version. */
+ struct stat st;
+ if (stat(zonefile, &st) < 0) {
+ log_zone_warning(zone->name, "failed to update zone file (%s)",
+ knot_strerror(knot_map_errno()));
+ free(zonefile);
+ ret = KNOT_EACCES;
+ goto flush_journal_replan;
+ }
+
+ free(zonefile);
+
+ /* Update zone file attributes. */
+ zone->zonefile.exists = true;
+ zone->zonefile.mtime = st.st_mtim;
+ zone->zonefile.serial = serial_to;
+ zone->zonefile.resigned = false;
+ zone->zonefile.retransfer = false;
+
+ /* Flush journal. */
+ if (journal_is_existing(j)) {
+ ret = journal_set_flushed(j);
+ }
+
+flush_journal_replan:
+ /* Plan next journal flush after proper period. */
+ zone->timers.last_flush = time(NULL);
+ if (sync_timeout > 0) {
+ time_t next_flush = zone->timers.last_flush + sync_timeout;
+ zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, 0,
+ ZONE_EVENT_FLUSH, next_flush);
+ }
+
+ return ret;
+}
+
+zone_t* zone_new(const knot_dname_t *name)
+{
+ zone_t *zone = malloc(sizeof(zone_t));
+ if (zone == NULL) {
+ return NULL;
+ }
+ memset(zone, 0, sizeof(zone_t));
+
+ zone->name = knot_dname_copy(name, NULL);
+ if (zone->name == NULL) {
+ free(zone);
+ return NULL;
+ }
+
+ // DDNS
+ pthread_mutex_init(&zone->ddns_lock, NULL);
+ zone->ddns_queue_size = 0;
+ init_list(&zone->ddns_queue);
+
+ knot_sem_init(&zone->cow_lock, 1);
+
+ // Preferred master lock
+ pthread_mutex_init(&zone->preferred_lock, NULL);
+
+ // Initialize events
+ zone_events_init(zone);
+
+ // Initialize query modules list.
+ init_list(&zone->query_modules);
+
+ return zone;
+}
+
+void zone_control_clear(zone_t *zone)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ zone_update_clear(zone->control_update);
+ free(zone->control_update);
+ zone->control_update = NULL;
+}
+
+void zone_free(zone_t **zone_ptr)
+{
+ if (zone_ptr == NULL || *zone_ptr == NULL) {
+ return;
+ }
+
+ zone_t *zone = *zone_ptr;
+
+ zone_events_deinit(zone);
+
+ knot_dname_free(zone->name, NULL);
+
+ free_ddns_queue(zone);
+ pthread_mutex_destroy(&zone->ddns_lock);
+
+ knot_sem_destroy(&zone->cow_lock);
+
+ /* Control update. */
+ zone_control_clear(zone);
+
+ free(zone->catalog_gen);
+ catalog_update_free(zone->cat_members);
+
+ /* Free preferred master. */
+ pthread_mutex_destroy(&zone->preferred_lock);
+ free(zone->preferred_master);
+
+ /* Free zone contents. */
+ zone_contents_deep_free(zone->contents);
+
+ conf_deactivate_modules(&zone->query_modules, &zone->query_plan);
+
+ free(zone);
+ *zone_ptr = NULL;
+}
+
+void zone_reset(conf_t *conf, zone_t *zone)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ zone_contents_t *old_contents = zone_switch_contents(zone, NULL);
+ conf_reset_modules(conf, &zone->query_modules, &zone->query_plan); // includes synchronize_rcu()
+ zone_contents_deep_free(old_contents);
+ if (zone_expired(zone)) {
+ replan_from_timers(conf, zone);
+ } else {
+ zone_events_schedule_now(zone, ZONE_EVENT_LOAD);
+ }
+}
+
+#define RETURN_IF_FAILED(str, exception) \
+{ \
+ if (ret != KNOT_EOK && ret != (exception)) { \
+ errors = true; \
+ log_zone_error(zone->name, \
+ "failed to purge %s (%s)", (str), knot_strerror(ret)); \
+ if (exit_immediately) { \
+ return ret; \
+ } \
+ } \
+}
+
+int selective_zone_purge(conf_t *conf, zone_t *zone, purge_flag_t params)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+ bool errors = false;
+ bool exit_immediately = !(params & PURGE_ZONE_BEST);
+
+ // Purge the zone timers.
+ if (params & PURGE_ZONE_TIMERS) {
+ zone->timers = (zone_timers_t) {
+ .catalog_member = zone->timers.catalog_member
+ };
+ zone->zonefile.bootstrap_cnt = 0;
+ ret = zone_timers_sweep(&zone->server->timerdb,
+ (sweep_cb)knot_dname_cmp, zone->name);
+ RETURN_IF_FAILED("timers", KNOT_ENOENT);
+ }
+
+ // Purge the zone file.
+ if (params & PURGE_ZONE_ZONEFILE) {
+ conf_val_t sync;
+ if ((params & PURGE_ZONE_NOSYNC) ||
+ (sync = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name),
+ conf_int(&sync) > -1)) {
+ char *zonefile = conf_zonefile(conf, zone->name);
+ ret = (unlink(zonefile) == -1 ? knot_map_errno() : KNOT_EOK);
+ free(zonefile);
+ RETURN_IF_FAILED("zone file", KNOT_ENOENT);
+ }
+ }
+
+ // Purge the zone journal.
+ if (params & PURGE_ZONE_JOURNAL) {
+ ret = journal_scrape_with_md(zone_journal(zone), true);
+ RETURN_IF_FAILED("journal", KNOT_ENOENT);
+ }
+
+ // Purge KASP DB.
+ if (params & PURGE_ZONE_KASPDB) {
+ ret = knot_lmdb_open(zone_kaspdb(zone));
+ if (ret == KNOT_EOK) {
+ ret = kasp_db_delete_all(zone_kaspdb(zone), zone->name);
+ }
+ RETURN_IF_FAILED("KASP DB", KNOT_ENOENT);
+ }
+
+ // Purge Catalog.
+ if (params & PURGE_ZONE_CATALOG) {
+ zone->timers.catalog_member = 0;
+ ret = catalog_zone_purge(zone->server, conf, zone->name);
+ RETURN_IF_FAILED("catalog", KNOT_EOK);
+ }
+
+ if (errors) {
+ return KNOT_ERROR;
+ }
+
+ if ((params & PURGE_ZONE_LOG) ||
+ (params & PURGE_ZONE_DATA) == PURGE_ZONE_DATA) {
+ log_zone_notice(zone->name, "zone purged");
+ }
+
+ return KNOT_EOK;
+}
+
+knot_lmdb_db_t *zone_journaldb(const zone_t *zone)
+{
+ return &zone->server->journaldb;
+}
+
+knot_lmdb_db_t *zone_kaspdb(const zone_t *zone)
+{
+ return &zone->server->kaspdb;
+}
+
+catalog_t *zone_catalog(const zone_t *zone)
+{
+ return &zone->server->catalog;
+}
+
+catalog_update_t *zone_catalog_upd(const zone_t *zone)
+{
+ return &zone->server->catalog_upd;
+}
+
+int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset_t *extra)
+{
+ if (conf == NULL || zone == NULL || change == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ zone_journal_t j = { zone_journaldb(zone), zone->name, conf };
+
+ int ret = journal_insert(j, change, extra, NULL);
+ if (ret == KNOT_EBUSY) {
+ log_zone_notice(zone->name, "journal is full, flushing");
+
+ /* Transaction rolled back, journal released, we may flush. */
+ ret = flush_journal(conf, zone, true, false);
+ if (ret == KNOT_EOK) {
+ ret = journal_insert(j, change, extra, NULL);
+ }
+ }
+
+ return ret;
+}
+
+int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff)
+{
+ if (conf == NULL || zone == NULL || diff == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ zone_journal_t j = { zone_journaldb(zone), zone->name, conf };
+
+ int ret = journal_insert(j, NULL, NULL, diff);
+ if (ret == KNOT_EBUSY) {
+ log_zone_notice(zone->name, "journal is full, flushing");
+
+ /* Transaction rolled back, journal released, we may flush. */
+ ret = flush_journal(conf, zone, true, false);
+ if (ret == KNOT_EOK) {
+ ret = journal_insert(j, NULL, NULL, diff);
+ }
+ }
+
+ return ret;
+}
+
+int zone_changes_clear(conf_t *conf, zone_t *zone)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return journal_scrape_with_md(zone_journal(zone), true);
+}
+
+int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (new_contents == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ zone_journal_t j = { zone_journaldb(zone), zone->name, conf };
+
+ int ret = journal_insert_zone(j, new_contents);
+ if (ret == KNOT_EOK) {
+ log_zone_info(zone->name, "zone stored to journal, serial %u",
+ zone_contents_serial(new_contents));
+ }
+
+ return ret;
+}
+
+int zone_flush_journal(conf_t *conf, zone_t *zone, bool verbose)
+{
+ if (conf == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return flush_journal(conf, zone, false, verbose);
+}
+
+bool zone_journal_has_zij(zone_t *zone)
+{
+ bool exists = false, zij = false;
+ (void)journal_info(zone_journal(zone), &exists, NULL, &zij, NULL, NULL, NULL, NULL, NULL);
+ return exists && zij;
+}
+
+void zone_notifailed_clear(zone_t *zone)
+{
+ pthread_mutex_lock(&zone->preferred_lock);
+ notifailed_rmt_dynarray_free(&zone->notifailed);
+ pthread_mutex_unlock(&zone->preferred_lock);
+}
+
+void zone_schedule_notify(zone_t *zone, time_t delay)
+{
+ zone_notifailed_clear(zone);
+ zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + delay);
+}
+
+zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents)
+{
+ if (zone == NULL) {
+ return NULL;
+ }
+
+ zone_contents_t *old_contents;
+ zone_contents_t **current_contents = &zone->contents;
+ old_contents = rcu_xchg_pointer(current_contents, new_contents);
+
+ return old_contents;
+}
+
+bool zone_is_slave(conf_t *conf, const zone_t *zone)
+{
+ if (conf == NULL || zone == NULL) {
+ return false;
+ }
+
+ conf_val_t val = conf_zone_get(conf, C_MASTER, zone->name);
+ return conf_val_count(&val) > 0 ? true : false;
+}
+
+void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr)
+{
+ if (zone == NULL || addr == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&zone->preferred_lock);
+ free(zone->preferred_master);
+ zone->preferred_master = malloc(sizeof(struct sockaddr_storage));
+ *zone->preferred_master = *addr;
+ pthread_mutex_unlock(&zone->preferred_lock);
+}
+
+void zone_clear_preferred_master(zone_t *zone)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&zone->preferred_lock);
+ free(zone->preferred_master);
+ zone->preferred_master = NULL;
+ pthread_mutex_unlock(&zone->preferred_lock);
+}
+
+static void set_flag(zone_t *zone, zone_flag_t flag, bool remove)
+{
+ if (zone == NULL) {
+ return;
+ }
+
+ pthread_mutex_lock(&zone->preferred_lock); // this mutex seems OK to be reused for this
+ zone->flags = remove ? (zone->flags & ~flag) : (zone->flags | flag);
+ pthread_mutex_unlock(&zone->preferred_lock);
+
+ if (flag & ZONE_IS_CATALOG) {
+ zone->is_catalog_flag = !remove;
+ }
+}
+
+void zone_set_flag(zone_t *zone, zone_flag_t flag)
+{
+ return set_flag(zone, flag, false);
+}
+
+void zone_unset_flag(zone_t *zone, zone_flag_t flag)
+{
+ return set_flag(zone, flag, true);
+}
+
+zone_flag_t zone_get_flag(zone_t *zone, zone_flag_t flag, bool clear)
+{
+ if (zone == NULL) {
+ return 0;
+ }
+
+ pthread_mutex_lock(&zone->preferred_lock);
+ zone_flag_t res = (zone->flags & flag);
+ if (clear && res) {
+ zone->flags &= ~flag;
+ }
+ assert(((bool)(zone->flags & ZONE_IS_CATALOG)) == zone->is_catalog_flag);
+ pthread_mutex_unlock(&zone->preferred_lock);
+
+ return res;
+}
+
+const knot_rdataset_t *zone_soa(const zone_t *zone)
+{
+ if (!zone || zone_contents_is_empty(zone->contents)) {
+ return NULL;
+ }
+
+ return node_rdataset(zone->contents->apex, KNOT_RRTYPE_SOA);
+}
+
+uint32_t zone_soa_expire(const zone_t *zone)
+{
+ const knot_rdataset_t *soa = zone_soa(zone);
+ return soa == NULL ? 0 : knot_soa_expire(soa->rdata);
+}
+
+bool zone_expired(const zone_t *zone)
+{
+ if (!zone) {
+ return false;
+ }
+
+ const zone_timers_t *timers = &zone->timers;
+
+ return timers->next_expire > 0 && timers->next_expire <= time(NULL);
+}
+
+static void time_set_default(time_t *time, time_t value)
+{
+ assert(time);
+
+ if (*time == 0) {
+ *time = value;
+ }
+}
+
+void zone_timers_sanitize(conf_t *conf, zone_t *zone)
+{
+ assert(conf);
+ assert(zone);
+
+ time_t now = time(NULL);
+
+ // assume now if we don't know when we flushed
+ time_set_default(&zone->timers.last_flush, now);
+
+ if (zone_is_slave(conf, zone)) {
+ // assume now if we don't know
+ time_set_default(&zone->timers.next_refresh, now);
+ if (zone->is_catalog_flag) {
+ zone->timers.next_expire = 0;
+ }
+ } else {
+ // invalidate if we don't have a master
+ zone->timers.last_refresh = 0;
+ zone->timers.next_refresh = 0;
+ zone->timers.last_refresh_ok = false;
+ zone->timers.next_expire = 0;
+ }
+}
+
+/*!
+ * \brief Get preferred zone master while checking its existence.
+ */
+int static preferred_master(conf_t *conf, zone_t *zone, conf_remote_t *master)
+{
+ pthread_mutex_lock(&zone->preferred_lock);
+
+ if (zone->preferred_master == NULL) {
+ pthread_mutex_unlock(&zone->preferred_lock);
+ return KNOT_ENOENT;
+ }
+
+ conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name);
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &masters, &iter);
+ while (iter.id->code == KNOT_EOK) {
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ size_t addr_count = conf_val_count(&addr);
+
+ for (size_t i = 0; i < addr_count; i++) {
+ conf_remote_t remote = conf_remote(conf, iter.id, i);
+ if (sockaddr_net_match(&remote.addr, zone->preferred_master, -1)) {
+ *master = remote;
+ pthread_mutex_unlock(&zone->preferred_lock);
+ return KNOT_EOK;
+ }
+ }
+
+ conf_mix_iter_next(&iter);
+ }
+
+ pthread_mutex_unlock(&zone->preferred_lock);
+
+ return KNOT_ENOENT;
+}
+
+static void log_try_addr_error(const zone_t *zone, const char *remote_name,
+ const struct sockaddr_storage *remote_addr,
+ const char *err_str, int ret)
+{
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), remote_addr);
+ log_zone_info(zone->name, "%s%s%s, address %s, failed (%s)", err_str,
+ (remote_name != NULL ? ", remote " : ""),
+ (remote_name != NULL ? remote_name : ""),
+ addr_str, knot_strerror(ret));
+}
+
+int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback,
+ void *callback_data, const char *err_str)
+{
+ if (conf == NULL || zone == NULL || callback == NULL || err_str == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ zone_master_fallback_t fallback = { true, true };
+
+ /* Try the preferred server. */
+
+ conf_remote_t preferred = { { AF_UNSPEC } };
+ if (preferred_master(conf, zone, &preferred) == KNOT_EOK) {
+ int ret = callback(conf, zone, &preferred, callback_data, &fallback);
+ if (ret == KNOT_EOK) {
+ return ret;
+ } else if (!fallback.remote) {
+ return ret; // Local error.
+ }
+
+ log_try_addr_error(zone, NULL, &preferred.addr, err_str, ret);
+
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), &preferred.addr);
+ log_zone_warning(zone->name, "%s, address %s not usable",
+ err_str, addr_str);
+ }
+
+ /* Try all the other servers. */
+
+ bool success = false;
+
+ conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name);
+ conf_mix_iter_t iter;
+ conf_mix_iter_init(conf, &masters, &iter);
+ while (iter.id->code == KNOT_EOK && fallback.remote) {
+ conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, iter.id);
+ size_t addr_count = conf_val_count(&addr);
+
+ bool tried = false;
+ fallback.address = true;
+ for (size_t i = 0; i < addr_count && fallback.address; i++) {
+ conf_remote_t master = conf_remote(conf, iter.id, i);
+ if (preferred.addr.ss_family != AF_UNSPEC &&
+ sockaddr_net_match(&master.addr, &preferred.addr, -1)) {
+ preferred.addr.ss_family = AF_UNSPEC;
+ continue;
+ }
+
+ tried = true;
+ int ret = callback(conf, zone, &master, callback_data, &fallback);
+ if (ret == KNOT_EOK) {
+ success = true;
+ break;
+ } else if (!fallback.remote) {
+ return ret; // Local error.
+ }
+
+ log_try_addr_error(zone, conf_str(iter.id), &master.addr,
+ err_str, ret);
+ }
+
+ if (!success && tried) {
+ log_zone_warning(zone->name, "%s, remote %s not usable",
+ err_str, conf_str(iter.id));
+ }
+
+ conf_mix_iter_next(&iter);
+ }
+
+ return success ? KNOT_EOK : KNOT_ENOMASTER;
+}
+
+int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir)
+{
+ if (zone == NULL || dir == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t dir_len = strlen(dir);
+ if (dir_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ char *zonefile = conf_zonefile(conf, zone->name);
+ char *zonefile_basename = strrchr(zonefile, '/');
+ if (zonefile_basename == NULL) {
+ zonefile_basename = zonefile;
+ }
+
+ size_t target_length = strlen(zonefile_basename) + dir_len + 2;
+ char target[target_length];
+ (void)snprintf(target, target_length, "%s/%s", dir, zonefile_basename);
+ if (strcmp(target, zonefile) == 0) {
+ free(zonefile);
+ return KNOT_EDENIED;
+ }
+ free(zonefile);
+
+ return zonefile_write(target, zone->contents);
+}
+
+int zone_set_master_serial(zone_t *zone, uint32_t serial)
+{
+ return kasp_db_store_serial(zone_kaspdb(zone), zone->name, KASPDB_SERIAL_MASTER, serial);
+}
+
+int zone_get_master_serial(zone_t *zone, uint32_t *serial)
+{
+ return kasp_db_load_serial(zone_kaspdb(zone), zone->name, KASPDB_SERIAL_MASTER, serial);
+}
+
+int zone_set_lastsigned_serial(zone_t *zone, uint32_t serial)
+{
+ return kasp_db_store_serial(zone_kaspdb(zone), zone->name, KASPDB_SERIAL_LASTSIGNED, serial);
+}
+
+int zone_get_lastsigned_serial(zone_t *zone, uint32_t *serial)
+{
+ return kasp_db_load_serial(zone_kaspdb(zone), zone->name, KASPDB_SERIAL_LASTSIGNED, serial);
+}
+
+int slave_zone_serial(zone_t *zone, conf_t *conf, uint32_t *serial)
+{
+ int ret = KNOT_EOK;
+ assert(zone->contents != NULL);
+ *serial = zone_contents_serial(zone->contents);
+
+ conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name);
+ if (conf_bool(&val)) {
+ ret = zone_get_master_serial(zone, serial);
+ }
+
+ return ret;
+}
diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h
new file mode 100644
index 0000000..ae8991e
--- /dev/null
+++ b/src/knot/zone/zone.h
@@ -0,0 +1,290 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "contrib/semaphore.h"
+#include "knot/catalog/catalog_update.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/confio.h"
+#include "knot/journal/journal_basic.h"
+#include "knot/journal/serialization.h"
+#include "knot/events/events.h"
+#include "knot/updates/changesets.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/timers.h"
+#include "libknot/dname.h"
+#include "libknot/dynarray.h"
+#include "libknot/packet/pkt.h"
+
+struct zone_update;
+struct zone_backup_ctx;
+
+/*!
+ * \brief Zone flags.
+ *
+ * When updating check create_zone_reload() if the flag mask is ok.
+ */
+typedef enum {
+ ZONE_FORCE_AXFR = 1 << 0, /*!< Force AXFR as next transfer. */
+ ZONE_FORCE_RESIGN = 1 << 1, /*!< Force zone re-sign. */
+ ZONE_FORCE_FLUSH = 1 << 2, /*!< Force zone flush. */
+ ZONE_FORCE_KSK_ROLL = 1 << 3, /*!< Force KSK/CSK rollover. */
+ ZONE_FORCE_ZSK_ROLL = 1 << 4, /*!< Force ZSK rollover. */
+ ZONE_IS_CATALOG = 1 << 5, /*!< This is a catalog. */
+ ZONE_IS_CAT_MEMBER = 1 << 6, /*!< This zone exists according to a catalog. */
+ ZONE_XFR_FROZEN = 1 << 7, /*!< Outgoing AXFR/IXFR temporarily disabled. */
+ ZONE_USER_FLUSH = 1 << 8, /*!< User-triggered flush. */
+} zone_flag_t;
+
+/*!
+ * \brief Track unsuccessful NOTIFY targets.
+ */
+typedef uint64_t notifailed_rmt_hash;
+knot_dynarray_declare(notifailed_rmt, notifailed_rmt_hash, DYNARRAY_VISIBILITY_NORMAL, 4);
+
+/*!
+ * \brief Zone purging parameter flags.
+ */
+typedef enum {
+ PURGE_ZONE_BEST = 1 << 0, /*!< Best effort -- continue on failures. */
+ PURGE_ZONE_LOG = 1 << 1, /*!< Log a purged zone even if requested less. */
+ PURGE_ZONE_NOSYNC = 1 << 2, /*!< Remove even zone files with disabled syncing. */
+ PURGE_ZONE_TIMERS = 1 << 3, /*!< Purge the zone timers. */
+ PURGE_ZONE_ZONEFILE = 1 << 4, /*!< Purge the zone file. */
+ PURGE_ZONE_JOURNAL = 1 << 5, /*!< Purge the zone journal. */
+ PURGE_ZONE_KASPDB = 1 << 6, /*!< Purge KASP DB. */
+ PURGE_ZONE_CATALOG = 1 << 7, /*!< Purge the catalog. */
+} purge_flag_t;
+
+#define PURGE_ZONE_FULL ~0U /*!< Purge everything possible. */
+ /*!< Standard purge (respect C_ZONEFILE_SYNC param). */
+#define PURGE_ZONE_ALL (PURGE_ZONE_FULL ^ PURGE_ZONE_NOSYNC)
+ /*!< All data. */
+#define PURGE_ZONE_DATA (PURGE_ZONE_TIMERS | PURGE_ZONE_ZONEFILE | PURGE_ZONE_JOURNAL | \
+ PURGE_ZONE_KASPDB | PURGE_ZONE_CATALOG)
+
+/*!
+ * \brief Structure for holding DNS zone.
+ */
+typedef struct zone
+{
+ knot_dname_t *name;
+ zone_contents_t *contents;
+ zone_flag_t flags;
+ bool is_catalog_flag; //!< Lock-less indication of ZONE_IS_CATALOG flag.
+
+ /*! \brief Dynamic configuration zone change type. */
+ conf_io_type_t change_type;
+
+ /*! \brief Zonefile parameters. */
+ struct {
+ struct timespec mtime;
+ uint32_t serial;
+ bool exists;
+ bool resigned;
+ bool retransfer;
+ uint8_t bootstrap_cnt; //!< Rebootstrap count (not related to zonefile).
+ } zonefile;
+
+ /*! \brief Zone events. */
+ zone_timers_t timers; //!< Persistent zone timers.
+ zone_events_t events; //!< Zone events timers.
+
+ /*! \brief Track unsuccessful NOTIFY targets. */
+ notifailed_rmt_dynarray_t notifailed;
+
+ /*! \brief DDNS queue and lock. */
+ pthread_mutex_t ddns_lock;
+ size_t ddns_queue_size;
+ list_t ddns_queue;
+
+ /*! \brief Control update context. */
+ struct zone_update *control_update;
+
+ /*! \brief Ensue one COW transaction on zone's trees at a time. */
+ knot_sem_t cow_lock;
+
+ /*! \brief Pointer on running server with e.g. KASP db, journal DB, catalog... */
+ struct server *server;
+
+ /*! \brief Zone backup context (NULL unless backup pending). */
+ struct zone_backup_ctx *backup_ctx;
+
+ /*! \brief Catalog-generate feature. */
+ knot_dname_t *catalog_gen;
+ catalog_update_t *cat_members;
+ const char *catalog_group;
+
+ /*! \brief Preferred master lock. Also used for flags access. */
+ pthread_mutex_t preferred_lock;
+ /*! \brief Preferred master for remote operation. */
+ struct sockaddr_storage *preferred_master;
+
+ /*! \brief Query modules. */
+ list_t query_modules;
+ struct query_plan *query_plan;
+} zone_t;
+
+/*!
+ * \brief Creates new zone with empty zone content.
+ *
+ * \param name Zone name.
+ *
+ * \return The initialized zone structure or NULL if an error occurred.
+ */
+zone_t* zone_new(const knot_dname_t *name);
+
+/*!
+ * \brief Deallocates the zone structure.
+ *
+ * \note The function also deallocates all bound structures (contents, etc.).
+ *
+ * \param zone_ptr Zone to be freed.
+ */
+void zone_free(zone_t **zone_ptr);
+
+/*!
+ * \brief Clear zone contents (->SERVFAIL), reset modules, plan LOAD.
+ *
+ * \param conf Current configuration.
+ * \param zone Zone to be re-set.
+ */
+void zone_reset(conf_t *conf, zone_t *zone);
+
+/*!
+ * \brief Purges selected zone components.
+ *
+ * \param conf Current configuration.
+ * \param zone Zone to be purged.
+ * \param params Zone components to be purged and the purging mode
+ * (with PURGE_ZONE_BEST try to purge everything requested,
+ * otherwise exit on the first failure).
+ *
+ * \return KNOT_E*
+ */
+int selective_zone_purge(conf_t *conf, zone_t *zone, purge_flag_t params);
+
+/*!
+ * \brief Clears possible control update transaction.
+ *
+ * \param zone Zone to be cleared.
+ */
+void zone_control_clear(zone_t *zone);
+
+/*!
+ * \brief Common database getters.
+ */
+knot_lmdb_db_t *zone_journaldb(const zone_t *zone);
+knot_lmdb_db_t *zone_kaspdb(const zone_t *zone);
+catalog_t *zone_catalog(const zone_t *zone);
+catalog_update_t *zone_catalog_upd(const zone_t *zone);
+
+/*!
+ * \brief Only for RO journal operations.
+ */
+inline static zone_journal_t zone_journal(zone_t *zone)
+{
+ zone_journal_t j = { zone_journaldb(zone), zone->name, NULL };
+ return j;
+}
+
+int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change, changeset_t *extra);
+int zone_diff_store(conf_t *conf, zone_t *zone, const zone_diff_t *diff);
+int zone_changes_clear(conf_t *conf, zone_t *zone);
+int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents);
+
+/*! \brief Synchronize zone file with journal. */
+int zone_flush_journal(conf_t *conf, zone_t *zone, bool verbose);
+
+bool zone_journal_has_zij(zone_t *zone);
+
+/*!
+ * \brief Clear failed_notify list before planning new NOTIFY.
+ */
+void zone_notifailed_clear(zone_t *zone);
+void zone_schedule_notify(zone_t *zone, time_t delay);
+
+/*!
+ * \brief Atomically switch the content of the zone.
+ */
+zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents);
+
+/*! \brief Checks if the zone is slave. */
+bool zone_is_slave(conf_t *conf, const zone_t *zone);
+
+/*! \brief Sets the address as a preferred master address. */
+void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr);
+
+/*! \brief Clears the current preferred master address. */
+void zone_clear_preferred_master(zone_t *zone);
+
+/*! \brief Sets a zone flag. */
+void zone_set_flag(zone_t *zone, zone_flag_t flag);
+
+/*! \brief Unsets a zone flag. */
+void zone_unset_flag(zone_t *zone, zone_flag_t flag);
+
+/*! \brief Returns if a flag is set (and optionally clears it). */
+zone_flag_t zone_get_flag(zone_t *zone, zone_flag_t flag, bool clear);
+
+/*! \brief Get zone SOA RR. */
+const knot_rdataset_t *zone_soa(const zone_t *zone);
+
+/*! \brief Get zone SOA EXPIRE field, or 0 if empty zone. */
+uint32_t zone_soa_expire(const zone_t *zone);
+
+/*! \brief Check if zone is expired according to timers. */
+bool zone_expired(const zone_t *zone);
+
+/*!
+ * \brief Set default timers for new zones or invalidate if not valid.
+ */
+void zone_timers_sanitize(conf_t *conf, zone_t *zone);
+
+typedef struct {
+ bool address; //!< Fallback to next remote address is required.
+ bool remote; //!< Fallback to next remote server is required.
+} zone_master_fallback_t;
+
+typedef int (*zone_master_cb)(conf_t *conf, zone_t *zone, const conf_remote_t *remote,
+ void *data, zone_master_fallback_t *fallback);
+
+/*!
+ * \brief Perform an action with all configured master servers.
+ *
+ * The function iterates over available masters. For each master, the callback
+ * function is called once for its every adresses until the callback function
+ * succeeds (\ref KNOT_EOK is returned) and then the iteration continues with
+ * the next master.
+ *
+ * \return Error code from the last callback or KNOT_ENOMASTER.
+ */
+int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback,
+ void *callback_data, const char *err_str);
+
+/*! \brief Write zone contents to zonefile, but into different directory. */
+int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir);
+
+int zone_set_master_serial(zone_t *zone, uint32_t serial);
+
+int zone_get_master_serial(zone_t *zone, uint32_t *serial);
+
+int zone_set_lastsigned_serial(zone_t *zone, uint32_t serial);
+
+int zone_get_lastsigned_serial(zone_t *zone, uint32_t *serial);
+
+int slave_zone_serial(zone_t *zone, conf_t *conf, uint32_t *serial);
diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c
new file mode 100644
index 0000000..58b17ab
--- /dev/null
+++ b/src/knot/zone/zonedb-load.c
@@ -0,0 +1,643 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include "knot/catalog/generate.h"
+#include "knot/common/log.h"
+#include "knot/conf/module.h"
+#include "knot/events/replan.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/zone/digest.h"
+#include "knot/zone/timers.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zonedb-load.h"
+#include "knot/zone/zonedb.h"
+#include "knot/zone/zonefile.h"
+#include "libknot/libknot.h"
+
+static bool zone_file_updated(conf_t *conf, const zone_t *old_zone,
+ const knot_dname_t *zone_name)
+{
+ assert(conf);
+ assert(zone_name);
+
+ if (old_zone == NULL) {
+ return false;
+ }
+
+ char *zonefile = conf_zonefile(conf, zone_name);
+ struct timespec mtime;
+ int ret = zonefile_exists(zonefile, &mtime);
+ free(zonefile);
+
+ if (ret == KNOT_EOK) {
+ return !(old_zone->zonefile.exists &&
+ old_zone->zonefile.mtime.tv_sec == mtime.tv_sec &&
+ old_zone->zonefile.mtime.tv_nsec == mtime.tv_nsec);
+ } else {
+ return old_zone->zonefile.exists;
+ }
+}
+
+static void zone_get_catalog_group(conf_t *conf, zone_t *zone)
+{
+ conf_val_t val = conf_zone_get(conf, C_CATALOG_GROUP, zone->name);
+ if (val.code == KNOT_EOK) {
+ zone->catalog_group = conf_str(&val);
+ }
+}
+
+static zone_t *create_zone_from(const knot_dname_t *name, server_t *server)
+{
+ zone_t *zone = zone_new(name);
+ if (!zone) {
+ return NULL;
+ }
+
+ zone->server = server;
+
+ int result = zone_events_setup(zone, server->workers, &server->sched);
+ if (result != KNOT_EOK) {
+ zone_free(&zone);
+ return NULL;
+ }
+
+ return zone;
+}
+
+static void replan_events(conf_t *conf, zone_t *zone, zone_t *old_zone)
+{
+ bool conf_updated = (old_zone->change_type & CONF_IO_TRELOAD);
+
+ conf_val_t digest = conf_zone_get(conf, C_ZONEMD_GENERATE, zone->name);
+ if (zone->contents != NULL && !zone_contents_digest_exists(zone->contents, conf_opt(&digest), true)) {
+ conf_updated = true;
+ }
+
+ zone->events.ufrozen = old_zone->events.ufrozen;
+ if ((zone_file_updated(conf, old_zone, zone->name) || conf_updated) && !zone_expired(zone)) {
+ replan_load_updated(zone, old_zone);
+ } else {
+ zone->zonefile = old_zone->zonefile;
+ memcpy(&zone->notifailed, &old_zone->notifailed, sizeof(zone->notifailed));
+ memset(&old_zone->notifailed, 0, sizeof(zone->notifailed));
+ replan_load_current(conf, zone, old_zone);
+ }
+}
+
+static zone_t *create_zone_reload(conf_t *conf, const knot_dname_t *name,
+ server_t *server, zone_t *old_zone)
+{
+ zone_t *zone = create_zone_from(name, server);
+ if (!zone) {
+ return NULL;
+ }
+
+ zone->contents = old_zone->contents;
+ zone_set_flag(zone, zone_get_flag(old_zone, ~0, false));
+
+ zone->timers = old_zone->timers;
+ zone_timers_sanitize(conf, zone);
+
+ if (old_zone->control_update != NULL) {
+ log_zone_warning(old_zone->name, "control transaction aborted");
+ zone_control_clear(old_zone);
+ }
+
+ zone->cat_members = old_zone->cat_members;
+ old_zone->cat_members = NULL;
+
+ zone->catalog_gen = old_zone->catalog_gen;
+ old_zone->catalog_gen = NULL;
+
+ return zone;
+}
+
+static zone_t *create_zone_new(conf_t *conf, const knot_dname_t *name,
+ server_t *server)
+{
+ zone_t *zone = create_zone_from(name, server);
+ if (!zone) {
+ return NULL;
+ }
+
+ int ret = zone_timers_read(&server->timerdb, name, &zone->timers);
+ if (ret != KNOT_EOK && ret != KNOT_ENODB && ret != KNOT_ENOENT) {
+ log_zone_error(zone->name, "failed to load persistent timers (%s)",
+ knot_strerror(ret));
+ zone_free(&zone);
+ return NULL;
+ }
+
+ zone_timers_sanitize(conf, zone);
+
+ conf_val_t role_val = conf_zone_get(conf, C_CATALOG_ROLE, name);
+ unsigned role = conf_opt(&role_val);
+ if (role == CATALOG_ROLE_MEMBER) {
+ conf_val_t catz = conf_zone_get(conf, C_CATALOG_ZONE, name);
+ assert(catz.code == KNOT_EOK); // conf consistency checked in conf/tools.c
+ zone->catalog_gen = knot_dname_copy(conf_dname(&catz), NULL);
+ if (zone->timers.catalog_member == 0) {
+ zone->timers.catalog_member = time(NULL);
+ }
+ if (zone->catalog_gen == NULL) {
+ log_zone_error(zone->name, "failed to initialize catalog member zone (%s)",
+ knot_strerror(KNOT_ENOMEM));
+ zone_free(&zone);
+ return NULL;
+ }
+ } else if (role == CATALOG_ROLE_GENERATE) {
+ zone->cat_members = catalog_update_new();
+ if (zone->cat_members == NULL) {
+ log_zone_error(zone->name, "failed to initialize catalog zone (%s)",
+ knot_strerror(KNOT_ENOMEM));
+ zone_free(&zone);
+ return NULL;
+ }
+ zone_set_flag(zone, ZONE_IS_CATALOG);
+ } else if (role == CATALOG_ROLE_INTERPRET) {
+ ret = catalog_open(&server->catalog);
+ if (ret != KNOT_EOK) {
+ log_error("failed to open catalog database (%s)", knot_strerror(ret));
+ }
+ zone_set_flag(zone, ZONE_IS_CATALOG);
+ }
+
+ if (zone_expired(zone)) {
+ // expired => force bootstrap, no load attempt
+ log_zone_info(zone->name, "zone will be bootstrapped");
+ assert(zone_is_slave(conf, zone));
+ replan_load_bootstrap(conf, zone);
+ } else {
+ log_zone_info(zone->name, "zone will be loaded");
+ // if load fails, fallback to bootstrap
+ replan_load_new(zone, role == CATALOG_ROLE_GENERATE);
+ }
+
+ return zone;
+}
+
+/*!
+ * \brief Load or reload the zone.
+ *
+ * \param conf Configuration.
+ * \param server Server.
+ * \param old_zone Already loaded zone (can be NULL).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+static zone_t *create_zone(conf_t *conf, const knot_dname_t *name, server_t *server,
+ zone_t *old_zone)
+{
+ assert(conf);
+ assert(name);
+ assert(server);
+
+ zone_t *z;
+
+ if (old_zone) {
+ z = create_zone_reload(conf, name, server, old_zone);
+ } else {
+ z = create_zone_new(conf, name, server);
+ }
+
+ if (z != NULL) {
+ zone_get_catalog_group(conf, z);
+ }
+
+ return z;
+}
+
+static void mark_changed_zones(knot_zonedb_t *zonedb, trie_t *changed)
+{
+ if (changed == NULL) {
+ return;
+ }
+
+ trie_it_t *it = trie_it_begin(changed);
+ for (; !trie_it_finished(it); trie_it_next(it)) {
+ const knot_dname_t *name =
+ (const knot_dname_t *)trie_it_key(it, NULL);
+
+ zone_t *zone = knot_zonedb_find(zonedb, name);
+ if (zone != NULL) {
+ conf_io_type_t type = conf_io_trie_val(it);
+ assert(!(type & CONF_IO_TSET));
+ zone->change_type = type;
+ }
+ }
+ trie_it_free(it);
+}
+
+static void zone_purge(conf_t *conf, zone_t *zone)
+{
+ (void)selective_zone_purge(conf, zone, PURGE_ZONE_ALL);
+}
+
+static zone_contents_t *zone_expire(zone_t *zone)
+{
+ zone->timers.next_expire = time(NULL);
+ zone->timers.next_refresh = zone->timers.next_expire;
+ return zone_switch_contents(zone, NULL);
+}
+
+static bool check_open_catalog(catalog_t *cat)
+{
+ int ret = knot_lmdb_exists(&cat->db);
+ switch (ret) {
+ case KNOT_ENODB:
+ return false;
+ case KNOT_EOK:
+ ret = catalog_open(cat);
+ if (ret == KNOT_EOK) {
+ return true;
+ }
+ // FALLTHROUGH
+ default:
+ log_error("failed to open persistent zone catalog");
+ }
+ return false;
+}
+
+static zone_t *reuse_member_zone(zone_t *zone, server_t *server, conf_t *conf,
+ reload_t mode, list_t *expired_contents)
+{
+ if (!zone_get_flag(zone, ZONE_IS_CAT_MEMBER, false)) {
+ return NULL;
+ }
+
+ catalog_upd_val_t *upd = catalog_update_get(&server->catalog_upd, zone->name);
+ if (upd != NULL) {
+ switch (upd->type) {
+ case CAT_UPD_UNIQ:
+ zone_purge(conf, zone);
+ knot_sem_wait(&zone->cow_lock);
+ ptrlist_add(expired_contents, zone_expire(zone), NULL);
+ knot_sem_post(&zone->cow_lock);
+ // FALLTHROUGH
+ case CAT_UPD_PROP:
+ zone->change_type = CONF_IO_TRELOAD;
+ break; // reload the member zone
+ case CAT_UPD_INVALID:
+ case CAT_UPD_MINOR:
+ return zone; // reuse the member zone
+ case CAT_UPD_REM:
+ return NULL; // remove the member zone
+ case CAT_UPD_ADD: // cannot add existing member
+ default:
+ assert(0);
+ return NULL;
+ }
+ } else if (mode & (RELOAD_COMMIT | RELOAD_CATALOG)) {
+ return zone; // reuse the member zone
+ }
+
+ zone_t *newzone = create_zone(conf, zone->name, server, zone);
+ if (newzone == NULL) {
+ log_zone_error(zone->name, "zone cannot be created");
+ } else {
+ assert(zone_get_flag(newzone, ZONE_IS_CAT_MEMBER, false));
+ conf_activate_modules(conf, server, newzone->name, &newzone->query_modules,
+ &newzone->query_plan);
+ }
+
+ return newzone;
+}
+
+// cold start of knot: add unchanged member zone to zonedb
+static zone_t *reuse_cold_zone(const knot_dname_t *zname, server_t *server, conf_t *conf)
+{
+ catalog_upd_val_t *upd = catalog_update_get(&server->catalog_upd, zname);
+ if (upd != NULL && upd->type == CAT_UPD_REM) {
+ return NULL; // zone will be removed immediately
+ }
+
+ zone_t *zone = create_zone(conf, zname, server, NULL);
+ if (zone == NULL) {
+ log_zone_error(zname, "zone cannot be created");
+ } else {
+ zone_set_flag(zone, ZONE_IS_CAT_MEMBER);
+ conf_activate_modules(conf, server, zone->name, &zone->query_modules,
+ &zone->query_plan);
+ }
+ return zone;
+}
+
+typedef struct {
+ knot_zonedb_t *zonedb;
+ server_t *server;
+ conf_t *conf;
+} reuse_cold_zone_ctx_t;
+
+static int reuse_cold_zone_cb(const knot_dname_t *member, _unused_ const knot_dname_t *owner,
+ const knot_dname_t *catz, _unused_ const char *group,
+ void *ctx)
+{
+ reuse_cold_zone_ctx_t *rcz = ctx;
+
+ zone_t *catz_z = knot_zonedb_find(rcz->zonedb, catz);
+ if (catz_z == NULL || !(catz_z->flags & ZONE_IS_CATALOG)) {
+ log_zone_warning(member, "orphaned catalog member zone, ignoring");
+ return KNOT_EOK;
+ }
+
+ zone_t *zone = reuse_cold_zone(member, rcz->server, rcz->conf);
+ if (zone == NULL) {
+ return KNOT_ENOMEM;
+ }
+ return knot_zonedb_insert(rcz->zonedb, zone);
+}
+
+static zone_t *add_member_zone(catalog_upd_val_t *val, knot_zonedb_t *check,
+ server_t *server, conf_t *conf)
+{
+ if (val->type != CAT_UPD_ADD) {
+ return NULL;
+ }
+
+ if (knot_zonedb_find(check, val->member) != NULL) {
+ log_zone_error(val->member, "zone already configured, ignoring");
+ return NULL;
+ }
+
+ zone_t *zone = create_zone(conf, val->member, server, NULL);
+ if (zone == NULL) {
+ log_zone_error(val->member, "zone cannot be created");
+ } else {
+ zone_set_flag(zone, ZONE_IS_CAT_MEMBER);
+ conf_activate_modules(conf, server, zone->name, &zone->query_modules,
+ &zone->query_plan);
+ log_zone_info(val->member, "zone added from catalog");
+ }
+ return zone;
+}
+
+/*!
+ * \brief Create new zone database.
+ *
+ * Zones that should be retained are just added from the old database to the
+ * new. New zones are loaded.
+ *
+ * \param conf New server configuration.
+ * \param server Server instance.
+ * \param mode Reload mode.
+ * \param expired_contents Out: ptrlist of zone_contents_t to be deep freed after sync RCU.
+ *
+ * \return New zone database.
+ */
+static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server, reload_t mode,
+ list_t *expired_contents)
+{
+ assert(conf);
+ assert(server);
+
+ knot_zonedb_t *db_old = server->zone_db;
+ knot_zonedb_t *db_new = knot_zonedb_new();
+ if (!db_new) {
+ return NULL;
+ }
+
+ /* Mark changed zones during dynamic configuration. */
+ if (mode == RELOAD_COMMIT) {
+ mark_changed_zones(db_old, conf->io.zones);
+ }
+
+ /* Process regular zones from the configuration. */
+ for (conf_iter_t iter = conf_iter(conf, C_ZONE); iter.code == KNOT_EOK;
+ conf_iter_next(conf, &iter)) {
+ conf_val_t id = conf_iter_id(conf, &iter);
+ const knot_dname_t *name = conf_dname(&id);
+
+ zone_t *old_zone = knot_zonedb_find(db_old, name);
+ if (old_zone != NULL && (mode & (RELOAD_COMMIT | RELOAD_CATALOG))) {
+ /* Reuse unchanged zone. */
+ if (!(old_zone->change_type & CONF_IO_TRELOAD)) {
+ knot_zonedb_insert(db_new, old_zone);
+ continue;
+ }
+ }
+
+ zone_t *zone = create_zone(conf, name, server, old_zone);
+ if (zone == NULL) {
+ log_zone_error(name, "zone cannot be created");
+ continue;
+ }
+
+ conf_activate_modules(conf, server, zone->name, &zone->query_modules,
+ &zone->query_plan);
+
+ knot_zonedb_insert(db_new, zone);
+ }
+
+ /* Purge decataloged zones before catalog removals are commited. */
+ catalog_it_t *cat_it = catalog_it_begin(&server->catalog_upd);
+ while (!catalog_it_finished(cat_it)) {
+ catalog_upd_val_t *upd = catalog_it_val(cat_it);
+ if (upd->type == CAT_UPD_REM) {
+ zone_t *zone = knot_zonedb_find(db_old, upd->member);
+ if (zone != NULL) {
+ zone->change_type = CONF_IO_TUNSET;
+ zone_purge(conf, zone);
+ }
+ }
+ catalog_it_next(cat_it);
+ }
+ catalog_it_free(cat_it);
+
+ int ret = catalog_update_commit(&server->catalog_upd, &server->catalog);
+ if (ret != KNOT_EOK) {
+ log_error("catalog, failed to apply changes (%s)", knot_strerror(ret));
+ return db_new;
+ }
+
+ /* Process existing catalog member zones. */
+ if (db_old != NULL) {
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old);
+ while (!knot_zonedb_iter_finished(it)) {
+ zone_t *newzone = reuse_member_zone(knot_zonedb_iter_val(it),
+ server, conf, mode,
+ expired_contents);
+ if (newzone != NULL) {
+ knot_zonedb_insert(db_new, newzone);
+ }
+ knot_zonedb_iter_next(it);
+ }
+ knot_zonedb_iter_free(it);
+ } else if (check_open_catalog(&server->catalog)) {
+ reuse_cold_zone_ctx_t rcz = { db_new, server, conf };
+ ret = catalog_apply(&server->catalog, NULL, reuse_cold_zone_cb, &rcz, false);
+ if (ret != KNOT_EOK) {
+ log_error("catalog, failed to load member zones (%s)", knot_strerror(ret));
+ }
+ }
+
+ /* Process new catalog member zones. */
+ catalog_it_t *it = catalog_it_begin(&server->catalog_upd);
+ while (!catalog_it_finished(it)) {
+ catalog_upd_val_t *val = catalog_it_val(it);
+ zone_t *zone = add_member_zone(val, db_new, server, conf);
+ if (zone != NULL) {
+ knot_zonedb_insert(db_new, zone);
+ }
+ catalog_it_next(it);
+ }
+ catalog_it_free(it);
+
+ return db_new;
+}
+
+/*!
+ * \brief Schedule deletion of old zones, and free the zone db structure.
+ *
+ * \note Zone content may be preserved in the new zone database, in this case
+ * new and old zone share the contents. Shared content is not freed.
+ *
+ * \param conf New server configuration.
+ * \param db_old Old zone database to remove.
+ * \param server Server context.
+ */
+static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old,
+ server_t *server, reload_t mode)
+{
+ catalog_commit_cleanup(&server->catalog);
+
+ knot_zonedb_t *db_new = server->zone_db;
+
+ if (db_old == NULL) {
+ goto catalog_only;
+ }
+
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old);
+ while (!knot_zonedb_iter_finished(it)) {
+ zone_t *zone = knot_zonedb_iter_val(it);
+ if (mode & (RELOAD_FULL | RELOAD_ZONES)) {
+ /* Check if reloaded (reused contents). */
+ zone_t *new_zone = knot_zonedb_find(db_new, zone->name);
+ if (new_zone != NULL) {
+ replan_events(conf, new_zone, zone);
+ zone->contents = NULL;
+ }
+ /* Completely new zone. */
+ } else {
+ /* Check if reloaded (reused contents). */
+ if (zone->change_type & CONF_IO_TRELOAD) {
+ zone_t *new_zone = knot_zonedb_find(db_new, zone->name);
+ assert(new_zone);
+ replan_events(conf, new_zone, zone);
+ zone->contents = NULL;
+ zone_free(&zone);
+ /* Check if removed (drop also contents). */
+ } else if (zone->change_type & CONF_IO_TUNSET) {
+ zone_free(&zone);
+ }
+ /* Completely reused zone. */
+ }
+ knot_zonedb_iter_next(it);
+ }
+ knot_zonedb_iter_free(it);
+
+catalog_only:
+
+ /* Clear catalog changes. No need to use mutex as this is done from main
+ * thread while all zone events are paused. */
+ catalog_update_clear(&server->catalog_upd);
+
+ if (mode & (RELOAD_FULL | RELOAD_ZONES)) {
+ knot_zonedb_deep_free(&db_old, false);
+ } else {
+ knot_zonedb_free(&db_old);
+ }
+}
+
+void zonedb_reload(conf_t *conf, server_t *server, reload_t mode)
+{
+ if (conf == NULL || server == NULL) {
+ return;
+ }
+
+ if (mode == RELOAD_COMMIT) {
+ assert(conf->io.flags & CONF_IO_FACTIVE);
+ if (conf->io.flags & CONF_IO_FRLD_ZONES) {
+ mode = RELOAD_ZONES;
+ }
+ }
+
+ list_t contents_tofree;
+ init_list(&contents_tofree);
+
+ catalog_update_finalize(&server->catalog_upd, &server->catalog, conf);
+ size_t cat_upd_size = trie_weight(server->catalog_upd.upd);
+ if (cat_upd_size > 0) {
+ log_info("catalog, updating, %zu changes", cat_upd_size);
+ }
+
+ /* Insert all required zones to the new zone DB. */
+ knot_zonedb_t *db_new = create_zonedb(conf, server, mode, &contents_tofree);
+ if (db_new == NULL) {
+ log_error("failed to create new zone database");
+ return;
+ }
+
+ catalogs_generate(db_new, server->zone_db);
+
+ /* Switch the databases. */
+ knot_zonedb_t **db_current = &server->zone_db;
+ knot_zonedb_t *db_old = rcu_xchg_pointer(db_current, db_new);
+
+ /* Wait for readers to finish reading old zone database. */
+ synchronize_rcu();
+
+ ptrlist_free_custom(&contents_tofree, NULL, (ptrlist_free_cb)zone_contents_deep_free);
+
+ /* Remove old zone DB. */
+ remove_old_zonedb(conf, db_old, server, mode);
+}
+
+int zone_reload_modules(conf_t *conf, server_t *server, const knot_dname_t *zone_name)
+{
+ zone_t **zone = knot_zonedb_find_ptr(server->zone_db, zone_name);
+ if (zone == NULL) {
+ return KNOT_ENOENT;
+ }
+ assert(knot_dname_is_equal((*zone)->name, zone_name));
+
+ zone_events_freeze_blocking(*zone);
+ knot_sem_wait(&(*zone)->cow_lock);
+
+ zone_t *newzone = create_zone(conf, zone_name, server, *zone);
+ if (newzone == NULL) {
+ return KNOT_ENOMEM;
+ }
+ conf_activate_modules(conf, server, newzone->name, &newzone->query_modules,
+ &newzone->query_plan);
+
+ zone_t *oldzone = rcu_xchg_pointer(zone, newzone);
+ synchronize_rcu();
+
+ replan_events(conf, newzone, oldzone);
+
+ assert(newzone->contents == oldzone->contents);
+ oldzone->contents = NULL; // contents have been re-used by newzone
+
+ knot_sem_post(&oldzone->cow_lock);
+ zone_free(&oldzone);
+
+ return KNOT_EOK;
+}
diff --git a/src/knot/zone/zonedb-load.h b/src/knot/zone/zonedb-load.h
new file mode 100644
index 0000000..c69b831
--- /dev/null
+++ b/src/knot/zone/zonedb-load.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "knot/server/server.h"
+
+/*!
+ * \brief Update zone database according to configuration.
+ *
+ * \param conf Configuration.
+ * \param server Server instance.
+ * \param mode Reload mode.
+ */
+void zonedb_reload(conf_t *conf, server_t *server, reload_t mode);
+
+/*!
+ * \brief Re-create zone_t struct in zoneDB so that the zone is reloaded incl modules.
+ *
+ * \param conf Configuration.
+ * \param server Server instance.
+ * \param zone_name Name of zone to be reloaded.
+ *
+ * \return KNOT_E*
+ */
+int zone_reload_modules(conf_t *conf, server_t *server, const knot_dname_t *zone_name);
diff --git a/src/knot/zone/zonedb.c b/src/knot/zone/zonedb.c
new file mode 100644
index 0000000..98cade5
--- /dev/null
+++ b/src/knot/zone/zonedb.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdlib.h>
+
+#include "knot/journal/journal_metadata.h"
+#include "knot/zone/zonedb.h"
+#include "libknot/packet/wire.h"
+#include "contrib/mempattern.h"
+#include "contrib/ucw/mempool.h"
+
+/*! \brief Discard zone in zone database. */
+static void discard_zone(zone_t *zone, bool abort_txn)
+{
+ // Don't flush if removed zone (no previous configuration available).
+ if (conf_rawid_exists(conf(), C_ZONE, zone->name, knot_dname_size(zone->name)) ||
+ catalog_has_member(conf()->catalog, zone->name)) {
+ uint32_t journal_serial, zone_serial = zone_contents_serial(zone->contents);
+ bool exists;
+
+ // Flush if bootstrapped or if the journal doesn't exist.
+ if (!zone->zonefile.exists || journal_info(
+ zone_journal(zone), &exists, NULL, NULL, &journal_serial, NULL, NULL, NULL, NULL
+ ) != KNOT_EOK || !exists || journal_serial != zone_serial) {
+ zone_flush_journal(conf(), zone, false);
+ }
+ }
+
+ if (abort_txn) {
+ zone_control_clear(zone);
+ }
+ zone_free(&zone);
+}
+
+knot_zonedb_t *knot_zonedb_new(void)
+{
+ knot_zonedb_t *db = calloc(1, sizeof(knot_zonedb_t));
+ if (db == NULL) {
+ return NULL;
+ }
+
+ mm_ctx_mempool(&db->mm, MM_DEFAULT_BLKSIZE);
+
+ db->trie = trie_create(&db->mm);
+ if (db->trie == NULL) {
+ mp_delete(db->mm.ctx);
+ free(db);
+ return NULL;
+ }
+
+ return db;
+}
+
+int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone)
+{
+ if (db == NULL || zone == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(zone->name);
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(zone->name, lf_storage);
+ assert(lf);
+
+ *trie_get_ins(db->trie, lf + 1, *lf) = zone;
+
+ return KNOT_EOK;
+}
+
+int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name)
+{
+ if (db == NULL || zone_name == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(zone_name, lf_storage);
+ assert(lf);
+
+ trie_val_t *rval = trie_get_try(db->trie, lf + 1, *lf);
+ if (rval == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ return trie_del(db->trie, lf + 1, *lf, NULL);
+}
+
+zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name)
+{
+ if (db == NULL) {
+ return NULL;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(zone_name, lf_storage);
+ assert(lf);
+
+ trie_val_t *val = trie_get_try(db->trie, lf + 1, *lf);
+ if (val == NULL) {
+ return NULL;
+ }
+
+ return *val;
+}
+
+zone_t **knot_zonedb_find_ptr(knot_zonedb_t *db, const knot_dname_t *zone_name)
+{
+ if (db == NULL) {
+ return NULL;
+ }
+
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(zone_name, lf_storage);
+ assert(lf);
+
+ trie_val_t *val = trie_get_try(db->trie, lf + 1, *lf);
+ if (val == NULL) {
+ return NULL;
+ }
+
+ return (zone_t **)val;
+}
+
+zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name)
+{
+ if (db == NULL || zone_name == NULL) {
+ return NULL;
+ }
+
+ while (true) {
+ knot_dname_storage_t lf_storage;
+ uint8_t *lf = knot_dname_lf(zone_name, lf_storage);
+ assert(lf);
+
+ trie_val_t *val = trie_get_try(db->trie, lf + 1, *lf);
+ if (val != NULL) {
+ return *val;
+ } else if (zone_name[0] == 0) {
+ return NULL;
+ }
+
+ zone_name = knot_wire_next_label(zone_name, NULL);
+ }
+}
+
+size_t knot_zonedb_size(const knot_zonedb_t *db)
+{
+ if (db == NULL) {
+ return 0;
+ }
+
+ return trie_weight(db->trie);
+}
+
+void knot_zonedb_free(knot_zonedb_t **db)
+{
+ if (db == NULL || *db == NULL) {
+ return;
+ }
+
+ mp_delete((*db)->mm.ctx);
+ free(*db);
+ *db = NULL;
+}
+
+void knot_zonedb_deep_free(knot_zonedb_t **db, bool abort_txn)
+{
+ if (db == NULL || *db == NULL) {
+ return;
+ }
+
+ knot_zonedb_foreach(*db, discard_zone, abort_txn);
+ knot_zonedb_free(db);
+}
diff --git a/src/knot/zone/zonedb.h b/src/knot/zone/zonedb.h
new file mode 100644
index 0000000..de934d5
--- /dev/null
+++ b/src/knot/zone/zonedb.h
@@ -0,0 +1,135 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Zone database represents a list of managed zones.
+ */
+
+#pragma once
+
+#include "knot/zone/zone.h"
+#include "libknot/dname.h"
+#include "contrib/qp-trie/trie.h"
+
+struct knot_zonedb {
+ trie_t *trie;
+ knot_mm_t mm;
+};
+
+/*
+ * Mapping of iterators to internal data structure.
+ */
+typedef trie_it_t knot_zonedb_iter_t;
+#define knot_zonedb_iter_begin(db) trie_it_begin((db)->trie)
+#define knot_zonedb_iter_finished(it) trie_it_finished(it)
+#define knot_zonedb_iter_next(it) trie_it_next(it)
+#define knot_zonedb_iter_free(it) trie_it_free(it)
+#define knot_zonedb_iter_val(it) *trie_it_val(it)
+
+/*
+ * Simple foreach() access with callback and variable number of callback params.
+ */
+#define knot_zonedb_foreach(db, callback, ...) \
+{ \
+ knot_zonedb_iter_t *it = knot_zonedb_iter_begin((db)); \
+ while(!knot_zonedb_iter_finished(it)) { \
+ callback((zone_t *)knot_zonedb_iter_val(it), ##__VA_ARGS__); \
+ knot_zonedb_iter_next(it); \
+ } \
+ knot_zonedb_iter_free(it); \
+}
+
+/*!
+ * \brief Allocates and initializes the zone database structure.
+ *
+ * \return Pointer to the created zone database structure or NULL if an error
+ * occurred.
+ */
+knot_zonedb_t *knot_zonedb_new(void);
+
+/*!
+ * \brief Adds new zone to the database.
+ *
+ * \param db Zone database to store the zone.
+ * \param zone Parsed zone.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EZONEIN
+ */
+int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone);
+
+/*!
+ * \brief Removes the given zone from the database if it exists.
+ *
+ * \param db Zone database to remove from.
+ * \param zone_name Name of the zone to be removed.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOZONE
+ */
+int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name);
+
+/*!
+ * \brief Finds zone exactly matching the given zone name.
+ *
+ * \param db Zone database to search in.
+ * \param zone_name Domain name representing the zone name.
+ *
+ * \return Zone with \a zone_name being the owner of the zone apex or NULL if
+ * not found.
+ */
+zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name);
+
+/*!
+ * \brief Finds pointer to zone exactly matching the given zone name.
+ *
+ * \param db Zone database to search in.
+ * \param zone_name Domain name representing the zone name.
+ *
+ * \return Pointer in zoneDB pointing at the zone structure, or NULL.
+ */
+zone_t **knot_zonedb_find_ptr(knot_zonedb_t *db, const knot_dname_t *zone_name);
+
+/*!
+ * \brief Finds zone the given domain name should belong to.
+ *
+ * \param db Zone database to search in.
+ * \param zone_name Domain name to find zone for.
+ *
+ * \retval Zone in which the domain name should be present or NULL if no such
+ * zone is found.
+ */
+zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name);
+
+size_t knot_zonedb_size(const knot_zonedb_t *db);
+
+/*!
+ * \brief Destroys and deallocates the zone database structure (but not the
+ * zones within).
+ *
+ * \param db Zone database to be destroyed.
+ */
+void knot_zonedb_free(knot_zonedb_t **db);
+
+/*!
+ * \brief Destroys and deallocates the whole zone database including the zones.
+ *
+ * \param db Zone database to be destroyed.
+ * \param abort_txn Indication that possible zone transactions are aborted.
+ */
+void knot_zonedb_deep_free(knot_zonedb_t **db, bool abort_txn);
diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c
new file mode 100644
index 0000000..e545497
--- /dev/null
+++ b/src/knot/zone/zonefile.c
@@ -0,0 +1,371 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "libknot/libknot.h"
+#include "contrib/files.h"
+#include "knot/common/log.h"
+#include "knot/dnssec/zone-nsec.h"
+#include "knot/zone/semantic-check.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/contents.h"
+#include "knot/zone/zonefile.h"
+#include "knot/zone/zone-dump.h"
+
+#define ERROR(zone, fmt, ...) log_zone_error(zone, "zone loader, " fmt, ##__VA_ARGS__)
+#define WARNING(zone, fmt, ...) log_zone_warning(zone, "zone loader, " fmt, ##__VA_ARGS__)
+#define NOTICE(zone, fmt, ...) log_zone_notice(zone, "zone loader, " fmt, ##__VA_ARGS__)
+
+static void process_error(zs_scanner_t *s)
+{
+ zcreator_t *zc = s->process.data;
+ const knot_dname_t *zname = zc->z->apex->owner;
+
+ ERROR(zname, "%s in zone, file '%s', line %"PRIu64" (%s)",
+ s->error.fatal ? "fatal error" : "error",
+ s->file.name, s->line_counter,
+ zs_strerror(s->error.code));
+}
+
+static bool handle_err(zcreator_t *zc, const knot_rrset_t *rr, int ret, bool master)
+{
+ const knot_dname_t *zname = zc->z->apex->owner;
+
+ knot_dname_txt_storage_t buff;
+ char *owner = knot_dname_to_str(buff, rr->owner, sizeof(buff));
+ if (owner == NULL) {
+ owner = "";
+ }
+
+ if (ret == KNOT_EOUTOFZONE) {
+ WARNING(zname, "ignoring out-of-zone data, owner %s", owner);
+ return true;
+ } else if (ret == KNOT_ETTL) {
+ char type[16] = "";
+ knot_rrtype_to_string(rr->type, type, sizeof(type));
+ NOTICE(zname, "TTL mismatch, owner %s, type %s, TTL set to %u",
+ owner, type, rr->ttl);
+ return true;
+ } else {
+ ERROR(zname, "failed to process record, owner %s", owner);
+ return false;
+ }
+}
+
+int zcreator_step(zcreator_t *zc, const knot_rrset_t *rr)
+{
+ if (zc == NULL || rr == NULL || rr->rrs.count != 1) {
+ return KNOT_EINVAL;
+ }
+
+ if (rr->type == KNOT_RRTYPE_SOA &&
+ node_rrtype_exists(zc->z->apex, KNOT_RRTYPE_SOA)) {
+ // Ignore extra SOA
+ return KNOT_EOK;
+ }
+
+ zone_node_t *node = NULL;
+ int ret = zone_contents_add_rr(zc->z, rr, &node);
+ if (ret != KNOT_EOK) {
+ if (!handle_err(zc, rr, ret, zc->master)) {
+ // Fatal error
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Creates RR from parser input, passes it to handling function. */
+static void process_data(zs_scanner_t *scanner)
+{
+ zcreator_t *zc = scanner->process.data;
+ if (zc->ret != KNOT_EOK) {
+ scanner->state = ZS_STATE_STOP;
+ return;
+ }
+
+ knot_dname_t *owner = knot_dname_copy(scanner->r_owner, NULL);
+ if (owner == NULL) {
+ zc->ret = KNOT_ENOMEM;
+ return;
+ }
+
+ knot_rrset_t rr;
+ knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class, scanner->r_ttl);
+
+ int ret = knot_rrset_add_rdata(&rr, scanner->r_data, scanner->r_data_length, NULL);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ /* Convert RDATA dnames to lowercase before adding to zone. */
+ ret = knot_rrset_rr_to_canonical(&rr);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ zc->ret = zcreator_step(zc, &rr);
+ knot_rrset_clear(&rr, NULL);
+}
+
+int zonefile_open(zloader_t *loader, const char *source,
+ const knot_dname_t *origin, semcheck_optional_t semantic_checks, time_t time)
+{
+ if (!loader) {
+ return KNOT_EINVAL;
+ }
+
+ memset(loader, 0, sizeof(zloader_t));
+
+ /* Check zone file. */
+ if (access(source, F_OK | R_OK) != 0) {
+ return knot_map_errno();
+ }
+
+ /* Create context. */
+ zcreator_t *zc = malloc(sizeof(zcreator_t));
+ if (zc == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(zc, 0, sizeof(zcreator_t));
+
+ zc->z = zone_contents_new(origin, true);
+ if (zc->z == NULL) {
+ free(zc);
+ return KNOT_ENOMEM;
+ }
+
+ /* Prepare textual owner for zone scanner. */
+ char *origin_str = knot_dname_to_str_alloc(origin);
+ if (origin_str == NULL) {
+ zone_contents_deep_free(zc->z);
+ free(zc);
+ return KNOT_ENOMEM;
+ }
+
+ if (zs_init(&loader->scanner, origin_str, KNOT_CLASS_IN, 3600) != 0 ||
+ zs_set_input_file(&loader->scanner, source) != 0 ||
+ zs_set_processing(&loader->scanner, process_data, process_error, zc) != 0) {
+ zs_deinit(&loader->scanner);
+ free(origin_str);
+ zone_contents_deep_free(zc->z);
+ free(zc);
+ return KNOT_EFILE;
+ }
+ free(origin_str);
+
+ loader->source = strdup(source);
+ loader->creator = zc;
+ loader->semantic_checks = semantic_checks;
+ loader->time = time;
+
+ return KNOT_EOK;
+}
+
+zone_contents_t *zonefile_load(zloader_t *loader)
+{
+ if (!loader) {
+ return NULL;
+ }
+
+ zcreator_t *zc = loader->creator;
+ const knot_dname_t *zname = zc->z->apex->owner;
+
+ assert(zc);
+ int ret = zs_parse_all(&loader->scanner);
+ if (ret != 0 && loader->scanner.error.counter == 0) {
+ ERROR(zname, "failed to load zone, file '%s' (%s)",
+ loader->source, zs_strerror(loader->scanner.error.code));
+ goto fail;
+ }
+
+ if (zc->ret != KNOT_EOK) {
+ ERROR(zname, "failed to load zone, file '%s' (%s)",
+ loader->source, knot_strerror(zc->ret));
+ goto fail;
+ }
+
+ if (loader->scanner.error.counter > 0) {
+ ERROR(zname, "failed to load zone, file '%s', %"PRIu64" errors",
+ loader->source, loader->scanner.error.counter);
+ goto fail;
+ }
+
+ if (!node_rrtype_exists(loader->creator->z->apex, KNOT_RRTYPE_SOA)) {
+ loader->err_handler->error = true;
+ loader->err_handler->cb(loader->err_handler, zc->z, NULL,
+ SEM_ERR_SOA_NONE, NULL);
+ goto fail;
+ }
+
+ ret = zone_adjust_contents(zc->z, adjust_cb_flags_and_nsec3, adjust_cb_nsec3_flags,
+ true, true, 1, NULL);
+ if (ret != KNOT_EOK) {
+ ERROR(zname, "failed to finalize zone contents (%s)",
+ knot_strerror(ret));
+ goto fail;
+ }
+
+ ret = sem_checks_process(zc->z, loader->semantic_checks,
+ loader->err_handler, loader->time);
+
+ if (ret != KNOT_EOK) {
+ ERROR(zname, "failed to load zone, file '%s' (%s)",
+ loader->source, knot_strerror(ret));
+ goto fail;
+ }
+
+ /* The contents will now change possibly messing up NSEC3 tree, it will
+ be adjusted again at zone_update_commit. */
+ ret = zone_adjust_contents(zc->z, unadjust_cb_point_to_nsec3, NULL,
+ false, false, 1, NULL);
+ if (ret != KNOT_EOK) {
+ ERROR(zname, "failed to finalize zone contents (%s)",
+ knot_strerror(ret));
+ goto fail;
+ }
+
+ return zc->z;
+
+fail:
+ zone_contents_deep_free(zc->z);
+ return NULL;
+}
+
+int zonefile_exists(const char *path, struct timespec *mtime)
+{
+ if (path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct stat zonefile_st = { 0 };
+ if (stat(path, &zonefile_st) < 0) {
+ return knot_map_errno();
+ }
+
+ if (mtime != NULL) {
+ *mtime = zonefile_st.st_mtim;
+ }
+
+ return KNOT_EOK;
+}
+
+int zonefile_write(const char *path, zone_contents_t *zone)
+{
+ if (path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (zone == NULL) {
+ return KNOT_EEMPTYZONE;
+ }
+
+ int ret = make_path(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ FILE *file = NULL;
+ char *tmp_name = NULL;
+ ret = open_tmp_file(path, &tmp_name, &file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = zone_dump_text(zone, file, true, NULL);
+ fclose(file);
+ if (ret != KNOT_EOK) {
+ unlink(tmp_name);
+ free(tmp_name);
+ return ret;
+ }
+
+ /* Swap temporary zonefile and new zonefile. */
+ ret = rename(tmp_name, path);
+ if (ret != 0) {
+ ret = knot_map_errno();
+ unlink(tmp_name);
+ free(tmp_name);
+ return ret;
+ }
+
+ free(tmp_name);
+
+ return KNOT_EOK;
+}
+
+void zonefile_close(zloader_t *loader)
+{
+ if (!loader) {
+ return;
+ }
+
+ zs_deinit(&loader->scanner);
+ free(loader->source);
+ free(loader->creator);
+}
+
+void err_handler_logger(sem_handler_t *handler, const zone_contents_t *zone,
+ const knot_dname_t *node, sem_error_t error, const char *data)
+{
+ assert(handler != NULL);
+ assert(zone != NULL);
+
+ if (handler->error) {
+ handler->fatal_error = true;
+ } else {
+ handler->warning = true;
+ }
+
+ knot_dname_txt_storage_t owner;
+ if (node != NULL) {
+ if (knot_dname_to_str(owner, node, sizeof(owner)) == NULL) {
+ owner[0] = '\0';
+ }
+ }
+
+ int level = handler->soft_check ? LOG_NOTICE :
+ (handler->error ? LOG_ERR : LOG_WARNING);
+
+ log_fmt_zone(level, LOG_SOURCE_ZONE, zone->apex->owner, NULL,
+ "check%s%s, %s%s%s",
+ (node != NULL ? ", node " : ""),
+ (node != NULL ? owner : ""),
+ sem_error_msg(error),
+ (data != NULL ? " " : ""),
+ (data != NULL ? data : ""));
+
+ handler->error = false;
+}
+
+#undef ERROR
+#undef WARNING
+#undef NOTICE
diff --git a/src/knot/zone/zonefile.h b/src/knot/zone/zonefile.h
new file mode 100644
index 0000000..c8dbfad
--- /dev/null
+++ b/src/knot/zone/zonefile.h
@@ -0,0 +1,104 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "knot/zone/zone.h"
+#include "knot/zone/semantic-check.h"
+#include "libzscanner/scanner.h"
+/*!
+ * \brief Zone creator structure.
+ */
+typedef struct zcreator {
+ zone_contents_t *z; /*!< Created zone. */
+ bool master; /*!< True if server is a primary master for the zone. */
+ int ret; /*!< Return value. */
+} zcreator_t;
+
+/*!
+ * \brief Zone loader structure.
+ */
+typedef struct {
+ char *source; /*!< Zone source file. */
+ semcheck_optional_t semantic_checks; /*!< Do semantic checks. */
+ sem_handler_t *err_handler; /*!< Semantic checks error handler. */
+ zcreator_t *creator; /*!< Loader context. */
+ zs_scanner_t scanner; /*!< Zone scanner. */
+ time_t time; /*!< time for zone check. */
+} zloader_t;
+
+void err_handler_logger(sem_handler_t *handler, const zone_contents_t *zone,
+ const knot_dname_t *node, sem_error_t error, const char *data);
+
+/*!
+ * \brief Open zone file for loading.
+ *
+ * \param loader Output zone loader.
+ * \param source Source file name.
+ * \param origin Zone origin.
+ * \param semantic_checks Perform semantic checks.
+ * \param time Time for semantic check.
+ *
+ * \retval Initialized loader on success.
+ * \retval NULL on error.
+ */
+int zonefile_open(zloader_t *loader, const char *source,
+ const knot_dname_t *origin, semcheck_optional_t semantic_checks, time_t time);
+
+/*!
+ * \brief Loads zone from a zone file.
+ *
+ * \param loader Zone loader instance.
+ *
+ * \retval Loaded zone contents on success.
+ * \retval NULL otherwise.
+ */
+zone_contents_t *zonefile_load(zloader_t *loader);
+
+/*!
+ * \brief Checks if zonefile exists.
+ *
+ * \param path Zonefile path.
+ * \param mtime Zonefile mtime if exists (can be NULL).
+ *
+ * \return KNOT_E*
+ */
+int zonefile_exists(const char *path, struct timespec *mtime);
+
+/*!
+ * \brief Write zone contents to zone file.
+ */
+int zonefile_write(const char *path, zone_contents_t *zone);
+
+/*!
+ * \brief Close zone file loader.
+ *
+ * \param loader Zone loader instance.
+ */
+void zonefile_close(zloader_t *loader);
+
+/*!
+ * \brief Adds one RR into zone.
+ *
+ * \param zl Zone loader.
+ * \param rr RR to add.
+ *
+ * \return KNOT_E*
+ */
+int zcreator_step(zcreator_t *zl, const knot_rrset_t *rr);
diff --git a/src/knotd.pc.in b/src/knotd.pc.in
new file mode 100644
index 0000000..6db74ca
--- /dev/null
+++ b/src/knotd.pc.in
@@ -0,0 +1,9 @@
+prefix=@prefix@
+exec_prefix=@prefix@
+libdir=@libdir@
+module_instdir=@module_instdir@
+
+Name: knotd
+Description: Knot DNS daemon
+URL: https://www.knot-dns.cz
+Version: @PACKAGE_VERSION@
diff --git a/src/libdnssec.pc.in b/src/libdnssec.pc.in
new file mode 100644
index 0000000..6c0aa5c
--- /dev/null
+++ b/src/libdnssec.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+soname=@libdnssec_SONAME@
+
+Name: libdnssec
+Description: Knot DNS DNSSEC library
+URL: https://www.knot-dns.cz
+Version: @PACKAGE_VERSION@
+Requires.private: gnutls >= 3.3
+Libs: -L${libdir} -ldnssec
+Cflags: -I${includedir}
diff --git a/src/libdnssec/Makefile.inc b/src/libdnssec/Makefile.inc
new file mode 100644
index 0000000..981d841
--- /dev/null
+++ b/src/libdnssec/Makefile.inc
@@ -0,0 +1,70 @@
+lib_LTLIBRARIES += libdnssec.la
+pkgconfig_DATA += libdnssec.pc
+
+libdnssec_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS)
+libdnssec_la_LDFLAGS = $(AM_LDFLAGS) $(libdnssec_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS)
+libdnssec_la_LIBADD = $(libcontrib_LIBS) $(gnutls_LIBS)
+if ENABLE_PKCS11
+libdnssec_la_LIBADD += $(pthread_LIBS)
+endif
+
+include_libdnssecdir = $(includedir)/libdnssec
+include_libdnssec_HEADERS = \
+ libdnssec/binary.h \
+ libdnssec/crypto.h \
+ libdnssec/digest.h \
+ libdnssec/dnssec.h \
+ libdnssec/error.h \
+ libdnssec/key.h \
+ libdnssec/keyid.h \
+ libdnssec/keystore.h \
+ libdnssec/keytag.h \
+ libdnssec/nsec.h \
+ libdnssec/pem.h \
+ libdnssec/random.h \
+ libdnssec/sign.h \
+ libdnssec/tsig.h \
+ libdnssec/version.h
+
+libdnssec_la_SOURCES = \
+ libdnssec/binary.c \
+ libdnssec/crypto.c \
+ libdnssec/digest.c \
+ libdnssec/error.c \
+ libdnssec/key/algorithm.c \
+ libdnssec/key/algorithm.h \
+ libdnssec/key/convert.c \
+ libdnssec/key/convert.h \
+ libdnssec/key/dnskey.c \
+ libdnssec/key/dnskey.h \
+ libdnssec/key/ds.c \
+ libdnssec/key/internal.h \
+ libdnssec/key/key.c \
+ libdnssec/key/keytag.c \
+ libdnssec/key/privkey.c \
+ libdnssec/key/privkey.h \
+ libdnssec/key/simple.c \
+ libdnssec/keyid.c \
+ libdnssec/keystore/internal.h \
+ libdnssec/keystore/keystore.c \
+ libdnssec/keystore/pkcs11.c \
+ libdnssec/keystore/pkcs8.c \
+ libdnssec/nsec/bitmap.c \
+ libdnssec/nsec/hash.c \
+ libdnssec/nsec/nsec.c \
+ libdnssec/p11/p11.c \
+ libdnssec/p11/p11.h \
+ libdnssec/pem.c \
+ libdnssec/random.c \
+ libdnssec/shared/bignum.c \
+ libdnssec/shared/bignum.h \
+ libdnssec/shared/binary_wire.h \
+ libdnssec/shared/dname.c \
+ libdnssec/shared/dname.h \
+ libdnssec/shared/keyid_gnutls.c \
+ libdnssec/shared/keyid_gnutls.h \
+ libdnssec/shared/shared.h \
+ libdnssec/sign/der.c \
+ libdnssec/sign/der.h \
+ libdnssec/sign/sign.c \
+ libdnssec/tsig.c
diff --git a/src/libdnssec/binary.c b/src/libdnssec/binary.c
new file mode 100644
index 0000000..59153f3
--- /dev/null
+++ b/src/libdnssec/binary.c
@@ -0,0 +1,163 @@
+/* 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 <assert.h>
+#include <string.h>
+
+#include "contrib/base64.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/shared/shared.h"
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_binary_alloc(dnssec_binary_t *data, size_t size)
+{
+ if (!data || size == 0) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *new_data = calloc(1, size);
+ if (!new_data) {
+ return DNSSEC_ENOMEM;
+ }
+
+ data->data = new_data;
+ data->size = size;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+void dnssec_binary_free(dnssec_binary_t *binary)
+{
+ if (!binary) {
+ return;
+ }
+
+ free(binary->data);
+ clear_struct(binary);
+}
+
+_public_
+int dnssec_binary_dup(const dnssec_binary_t *from, dnssec_binary_t *to)
+{
+ if (!from || !to) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *copy = malloc(from->size);
+ if (copy == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ memmove(copy, from->data, from->size);
+
+ to->size = from->size;
+ to->data = copy;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_binary_resize(dnssec_binary_t *data, size_t new_size)
+{
+ if (!data) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *new_data = realloc(data->data, new_size);
+ if (new_size > 0 && new_data == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ data->data = new_data;
+ data->size = new_size;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two)
+{
+ if (one == two) {
+ return 0;
+ }
+
+ uint8_t *data_one = one && one->size > 0 ? one->data : NULL;
+ uint8_t *data_two = two && two->size > 0 ? two->data : NULL;
+
+ if (data_one == data_two) {
+ return 0;
+ } else if (data_one == NULL) {
+ return -1;
+ } else if (data_two == NULL) {
+ return +1;
+ }
+
+ size_t min_size = one->size <= two->size ? one->size : two->size;
+ int cmp = memcmp(data_one, data_two, min_size);
+ if (cmp != 0) {
+ return cmp;
+ } else if (one->size == two->size) {
+ return 0;
+ } else if (one->size < two->size) {
+ return -1;
+ } else {
+ return +1;
+ }
+}
+
+_public_
+int dnssec_binary_from_base64(const dnssec_binary_t *base64,
+ dnssec_binary_t *binary)
+{
+ if (!base64 || !binary) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *data;
+ int32_t size = knot_base64_decode_alloc(base64->data, base64->size, &data);
+ if (size < 0) {
+ return DNSSEC_EINVAL;
+ }
+
+ binary->data = data;
+ binary->size = size;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_binary_to_base64(const dnssec_binary_t *binary,
+ dnssec_binary_t *base64)
+{
+ if (!binary || !base64) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *data;
+ int32_t size = knot_base64_encode_alloc(binary->data, binary->size, &data);
+ if (size < 0) {
+ return DNSSEC_EINVAL;
+ }
+
+ base64->data = data;
+ base64->size = size;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/binary.h b/src/libdnssec/binary.h
new file mode 100644
index 0000000..8ff4174
--- /dev/null
+++ b/src/libdnssec/binary.h
@@ -0,0 +1,116 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup binary
+ *
+ * \brief Universal binary data container.
+ *
+ * The module provides universal binary data container extensively used by
+ * a lot of functions provided by the library.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*!
+ * Universal structure to hold binary data.
+ */
+typedef struct dnssec_binary {
+ size_t size; /*!< Size of the binary data. */
+ uint8_t *data; /*!< Stored data. */
+} dnssec_binary_t;
+
+/*!
+ * Allocate new binary data structure.
+ *
+ * \param[out] data Binary to be allocated.
+ * \param[in] size Requested size of the binary.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_binary_alloc(dnssec_binary_t *data, size_t size);
+
+/*!
+ * Free content of binary structure.
+ *
+ * \param binary Binary structure to be freed.
+ */
+void dnssec_binary_free(dnssec_binary_t *binary);
+
+/*!
+ * Create a copy of a binary structure.
+ *
+ * \param[in] from Source of the copy.
+ * \param[out] to Target of the copy.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_binary_dup(const dnssec_binary_t *from, dnssec_binary_t *to);
+
+/*!
+ * Resize binary structure to a new size.
+ *
+ * Internally uses realloc, which means that this function can be also used
+ * as a malloc or free.
+ *
+ * \param data Binary to be resized.
+ * \param new_size New size.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_binary_resize(dnssec_binary_t *data, size_t new_size);
+
+/*!
+ * Compare two binary structures (equivalent of memcmp).
+ *
+ * \note NULL sorts before data.
+ *
+ * \param one First binary.
+ * \param two Second binary.
+ *
+ * \return 0 if one equals two, <0 if one sorts before two, >0 otherwise.
+ */
+int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two);
+
+/*!
+ * Allocate binary from Base64 encoded string.
+ *
+ * \param[in] base64 Base64 encoded data.
+ * \param[out] binary Decoded binary data.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_binary_from_base64(const dnssec_binary_t *base64,
+ dnssec_binary_t *binary);
+
+/*!
+ * Create Base64 encoded string from binary data.
+ *
+ * \param[in] binary Binary data.
+ * \param[out] base64 Base64 encode data.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_binary_to_base64(const dnssec_binary_t *binary,
+ dnssec_binary_t *base64);
+/*! @} */
diff --git a/src/libdnssec/crypto.c b/src/libdnssec/crypto.c
new file mode 100644
index 0000000..c42bed5
--- /dev/null
+++ b/src/libdnssec/crypto.c
@@ -0,0 +1,42 @@
+/* Copyright (C) 2018 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 <gnutls/gnutls.h>
+#include <gnutls/pkcs11.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/p11/p11.h"
+#include "libdnssec/shared/shared.h"
+
+_public_
+void dnssec_crypto_init(void)
+{
+ p11_init();
+ gnutls_global_init();
+}
+
+_public_
+void dnssec_crypto_cleanup(void)
+{
+ gnutls_global_deinit();
+ p11_cleanup();
+}
+
+_public_
+void dnssec_crypto_reinit(void)
+{
+ p11_reinit();
+}
diff --git a/src/libdnssec/crypto.h b/src/libdnssec/crypto.h
new file mode 100644
index 0000000..9129084
--- /dev/null
+++ b/src/libdnssec/crypto.h
@@ -0,0 +1,52 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup crypto
+ *
+ * \brief Cryptographic backend initialization.
+ *
+ * For most cryptographic operations, the library requires global
+ * initialization. Also, if the application creates a subprocess, the
+ * library has to be reinitialized in the child process after \c fork().
+ *
+ * @{
+ */
+
+#pragma once
+
+/*!
+ * Initialize cryptographic backend.
+ */
+void dnssec_crypto_init(void);
+
+/*!
+ * Reinitialize cryptographic backend.
+ *
+ * Must be called after fork() by the child.
+ */
+void dnssec_crypto_reinit(void);
+
+/*!
+ * Deinitialize cryptographic backend.
+ *
+ * Should be called when terminating the application.
+ */
+void dnssec_crypto_cleanup(void);
+
+/*! @} */
diff --git a/src/libdnssec/digest.c b/src/libdnssec/digest.c
new file mode 100644
index 0000000..83a4fcb
--- /dev/null
+++ b/src/libdnssec/digest.c
@@ -0,0 +1,105 @@
+/* Copyright (C) 2021 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 "libdnssec/digest.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#include "libdnssec/shared/shared.h"
+
+struct dnssec_digest_ctx {
+ gnutls_hash_hd_t gtctx;
+ unsigned size;
+};
+
+static gnutls_digest_algorithm_t lookup_algorithm(dnssec_digest_t algorithm)
+{
+ switch (algorithm) {
+ case DNSSEC_DIGEST_SHA384: return GNUTLS_DIG_SHA384;
+ case DNSSEC_DIGEST_SHA512: return GNUTLS_DIG_SHA512;
+ default:
+ return GNUTLS_DIG_UNKNOWN;
+ };
+}
+
+_public_
+int dnssec_digest_init(dnssec_digest_t algorithm, dnssec_digest_ctx_t **out_ctx)
+{
+ if (out_ctx == NULL) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_digest_algorithm_t gtalg = lookup_algorithm(algorithm);
+ if (gtalg == GNUTLS_DIG_UNKNOWN) {
+ return DNSSEC_INVALID_DIGEST_ALGORITHM;
+ }
+
+ dnssec_digest_ctx_t *res = malloc(sizeof(*res));
+ if (res == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ res->size = gnutls_hash_get_len(gtalg);
+ if (res->size == 0 || gnutls_hash_init(&res->gtctx, gtalg) < 0) {
+ free(res);
+ return DNSSEC_DIGEST_ERROR;
+ }
+
+ *out_ctx = res;
+ return DNSSEC_EOK;
+}
+
+static void digest_ctx_free(dnssec_digest_ctx_t *ctx)
+{
+ free_gnutls_hash_ptr(&ctx->gtctx);
+ free(ctx);
+}
+
+_public_
+int dnssec_digest(dnssec_digest_ctx_t *ctx, dnssec_binary_t *data)
+{
+ if (ctx == NULL || data == NULL) {
+ return DNSSEC_EINVAL;
+ }
+
+ int r = gnutls_hash(ctx->gtctx, data->data, data->size);
+ if (r != 0) {
+ digest_ctx_free(ctx);
+ return DNSSEC_DIGEST_ERROR;
+ }
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_digest_finish(dnssec_digest_ctx_t *ctx, dnssec_binary_t *out)
+{
+ if (ctx == NULL || out == NULL) {
+ return DNSSEC_EINVAL;
+ }
+
+ int r = dnssec_binary_resize(out, ctx->size);
+ if (r < 0) {
+ dnssec_binary_free(out);
+ digest_ctx_free(ctx);
+ return r;
+ }
+
+ gnutls_hash_output(ctx->gtctx, out->data);
+
+ digest_ctx_free(ctx);
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/digest.h b/src/libdnssec/digest.h
new file mode 100644
index 0000000..76709aa
--- /dev/null
+++ b/src/libdnssec/digest.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup digest
+ *
+ * \brief Data hashing operations.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+
+typedef enum {
+ DNSSEC_DIGEST_INVALID = 0,
+ DNSSEC_DIGEST_SHA384 = 1,
+ DNSSEC_DIGEST_SHA512 = 2,
+} dnssec_digest_t;
+
+struct dnssec_digest_ctx;
+typedef struct dnssec_digest_ctx dnssec_digest_ctx_t;
+
+/*!
+ * \brief Initialize digest context.
+ *
+ * \param algorithm Hasing algorithm to be used.
+ * \param out_ctx Output: context structure to be initialized.
+ *
+ * \return DNSSEC_E*
+ */
+int dnssec_digest_init(dnssec_digest_t algorithm, dnssec_digest_ctx_t **out_ctx);
+
+/*!
+ * \brief Digest data.
+ *
+ * \param ctx Digest context.
+ * \param data Data to be hashed.
+ *
+ * \note This function may be invoked repeatedly for single digest context,
+ * hashing all data as concatenated.
+ *
+ * \return DNSSEC_E*
+ *
+ * \note If error is returned, the digest context is automatically disposed.
+ */
+int dnssec_digest(dnssec_digest_ctx_t *ctx, dnssec_binary_t *data);
+
+/*!
+ * \brief Finalize digest, dispose digest context and return the hash.
+ *
+ * \param ctx Digest context.
+ * \param out Output: computed hash.
+ *
+ * \return DNSSEC_E*
+ */
+int dnssec_digest_finish(dnssec_digest_ctx_t *ctx, dnssec_binary_t *out);
+
+/*! @} */
diff --git a/src/libdnssec/dnssec.h b/src/libdnssec/dnssec.h
new file mode 100644
index 0000000..ebef953
--- /dev/null
+++ b/src/libdnssec/dnssec.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * Convenient header to include all library modules.
+ */
+
+#pragma once
+
+#include <libdnssec/binary.h>
+#include <libdnssec/crypto.h>
+#include <libdnssec/error.h>
+#include <libdnssec/key.h>
+#include <libdnssec/keyid.h>
+#include <libdnssec/keystore.h>
+#include <libdnssec/keytag.h>
+#include <libdnssec/nsec.h>
+#include <libdnssec/pem.h>
+#include <libdnssec/random.h>
+#include <libdnssec/sign.h>
+#include <libdnssec/tsig.h>
diff --git a/src/libdnssec/error.c b/src/libdnssec/error.c
new file mode 100644
index 0000000..d4f9a81
--- /dev/null
+++ b/src/libdnssec/error.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 2021 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 <string.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/shared/shared.h"
+
+typedef struct error_message_t {
+ int code;
+ const char *text;
+} error_message_t;
+
+static const error_message_t ERROR_MESSAGES[] = {
+ { DNSSEC_EOK, "no error" },
+
+ { DNSSEC_ENOMEM, "not enough memory" },
+ { DNSSEC_EINVAL, "invalid argument" },
+ { DNSSEC_ENOENT, "no such file or directory" },
+
+ { DNSSEC_ERROR, "unspecified error" },
+ { DNSSEC_NOT_IMPLEMENTED_ERROR, "not implemented" },
+ { DNSSEC_MALFORMED_DATA, "malformed data" },
+ { DNSSEC_NOT_FOUND, "not found" },
+
+ { DNSSEC_PKCS8_IMPORT_ERROR, "PKCS #8 import error" },
+ { DNSSEC_KEY_EXPORT_ERROR, "key export error" },
+ { DNSSEC_KEY_IMPORT_ERROR, "key import error" },
+ { DNSSEC_KEY_GENERATE_ERROR, "key generation error" },
+
+ { DNSSEC_INVALID_PUBLIC_KEY, "invalid public key" },
+ { DNSSEC_INVALID_PRIVATE_KEY, "invalid private key" },
+ { DNSSEC_INVALID_KEY_ALGORITHM, "invalid key algorithm" },
+ { DNSSEC_INVALID_KEY_SIZE, "invalid key size" },
+ { DNSSEC_INVALID_KEY_ID, "invalid key ID" },
+ { DNSSEC_INVALID_KEY_NAME, "invalid key name" },
+
+ { DNSSEC_NO_PUBLIC_KEY, "no public key" },
+ { DNSSEC_NO_PRIVATE_KEY, "no private key" },
+ { DNSSEC_KEY_ALREADY_PRESENT, "key already present" },
+
+ { DNSSEC_SIGN_INIT_ERROR, "signing initialization error" },
+ { DNSSEC_SIGN_ERROR, "signing error" },
+ { DNSSEC_INVALID_SIGNATURE, "invalid signature" },
+
+ { DNSSEC_INVALID_NSEC3_ALGORITHM, "invalid NSEC3 algorithm" },
+ { DNSSEC_NSEC3_HASHING_ERROR, "NSEC3 hashing error" },
+
+ { DNSSEC_INVALID_DS_ALGORITHM, "invalid DS algorithm" },
+ { DNSSEC_DS_HASHING_ERROR, "DS hashing error" },
+
+ { DNSSEC_KEYSTORE_INVALID_CONFIG, "invalid KASP keystore configuration" },
+
+ { DNSSEC_P11_FAILED_TO_LOAD_MODULE, "failed to load PKCS #11 module" },
+ { DNSSEC_P11_TOO_MANY_MODULES, "too many PKCS #11 modules loaded" },
+ { DNSSEC_P11_TOKEN_NOT_AVAILABLE, "PKCS #11 token not available" },
+
+ { DNSSEC_INVALID_DIGEST_ALGORITHM, "invalid digest algorithm" },
+ { DNSSEC_DIGEST_ERROR, "digest error" },
+
+ { 0 }
+};
+
+_public_
+const char *dnssec_strerror(int error)
+{
+ for (const error_message_t *m = ERROR_MESSAGES; m->text; m++) {
+ if (m->code == error) {
+ return m->text;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/libdnssec/error.h b/src/libdnssec/error.h
new file mode 100644
index 0000000..aee87cb
--- /dev/null
+++ b/src/libdnssec/error.h
@@ -0,0 +1,106 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup error
+ *
+ * \brief Error codes and error reporting.
+ *
+ * The module defines all error codes used in the library, and functions
+ * to convert the error codes to sensible error strings.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <errno.h>
+
+/*!
+ * Library error codes.
+ */
+enum dnssec_error {
+ DNSSEC_EOK = 0,
+
+ DNSSEC_ENOMEM = -ENOMEM,
+ DNSSEC_EINVAL = -EINVAL,
+ DNSSEC_ENOENT = -ENOENT,
+
+ DNSSEC_ERROR_MIN = -1500,
+
+ DNSSEC_ERROR = DNSSEC_ERROR_MIN,
+ DNSSEC_NOT_IMPLEMENTED_ERROR,
+ DNSSEC_MALFORMED_DATA,
+ DNSSEC_NOT_FOUND,
+
+ DNSSEC_PKCS8_IMPORT_ERROR,
+ DNSSEC_KEY_EXPORT_ERROR,
+ DNSSEC_KEY_IMPORT_ERROR,
+ DNSSEC_KEY_GENERATE_ERROR,
+
+ DNSSEC_INVALID_PUBLIC_KEY,
+ DNSSEC_INVALID_PRIVATE_KEY,
+ DNSSEC_INVALID_KEY_ALGORITHM,
+ DNSSEC_INVALID_KEY_SIZE,
+ DNSSEC_INVALID_KEY_ID,
+ DNSSEC_INVALID_KEY_NAME,
+
+ DNSSEC_NO_PUBLIC_KEY,
+ DNSSEC_NO_PRIVATE_KEY,
+ DNSSEC_KEY_ALREADY_PRESENT,
+
+ DNSSEC_SIGN_INIT_ERROR,
+ DNSSEC_SIGN_ERROR,
+ DNSSEC_INVALID_SIGNATURE,
+
+ DNSSEC_INVALID_NSEC3_ALGORITHM,
+ DNSSEC_NSEC3_HASHING_ERROR,
+
+ DNSSEC_INVALID_DS_ALGORITHM,
+ DNSSEC_DS_HASHING_ERROR,
+
+ DNSSEC_KEYSTORE_INVALID_CONFIG,
+
+ DNSSEC_P11_FAILED_TO_LOAD_MODULE,
+ DNSSEC_P11_TOO_MANY_MODULES,
+ DNSSEC_P11_TOKEN_NOT_AVAILABLE,
+
+ DNSSEC_INVALID_DIGEST_ALGORITHM,
+ DNSSEC_DIGEST_ERROR,
+
+ DNSSEC_ERROR_MAX = -1001
+};
+
+/*!
+ * Translate error code to error message.
+ *
+ * \param error Error code.
+ *
+ * \return Statically allocated error message string or NULL if unknown.
+ */
+const char *dnssec_strerror(int error);
+
+/*!
+ * Convert errno value to DNSSEC error code.
+ */
+static inline int dnssec_errno_to_error(int ecode)
+{
+ return -ecode;
+}
+
+/*! @} */
diff --git a/src/libdnssec/key.h b/src/libdnssec/key.h
new file mode 100644
index 0000000..2a69d37
--- /dev/null
+++ b/src/libdnssec/key.h
@@ -0,0 +1,309 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup key
+ *
+ * \brief DNSSEC public and private key manipulation.
+ *
+ * The dnssec_key_t is an abstraction for a DNSSEC key pair. If the key
+ * key is initialized with a public key data only, it can be used only for
+ * signature verification. In order to use the key for signing, private key
+ * has to be loaded. If only a private key is loaded into the structure,
+ * the public key is automatically constructed.
+ *
+ * The module interface provides various functions to retrieve information
+ * about the key. But the key is mostly used by other modules of the library.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <libdnssec/binary.h>
+
+/*!
+ * DNSKEY algorithm numbers.
+ *
+ * \see https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
+ */
+typedef enum dnssec_key_algorithm {
+ DNSSEC_KEY_ALGORITHM_DELETE = 0,
+ DNSSEC_KEY_ALGORITHM_RSA_MD5 = 1, /*!< Unsupported */
+ DNSSEC_KEY_ALGORITHM_DH = 2, /*!< Unsupported */
+ DNSSEC_KEY_ALGORITHM_DSA = 3, /*!< Unsupported */
+
+ DNSSEC_KEY_ALGORITHM_RSA_SHA1 = 5,
+ DNSSEC_KEY_ALGORITHM_DSA_NSEC3_SHA1 = 6, /*!< Unsupported */
+ DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3 = 7,
+ DNSSEC_KEY_ALGORITHM_RSA_SHA256 = 8,
+ DNSSEC_KEY_ALGORITHM_RSA_SHA512 = 10,
+ DNSSEC_KEY_ALGORITHM_ECC_GOST = 12, /*!< Unsupported */
+ DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256 = 13,
+ DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384 = 14,
+ DNSSEC_KEY_ALGORITHM_ED25519 = 15,
+ DNSSEC_KEY_ALGORITHM_ED448 = 16,
+
+ DNSSEC_KEY_ALGORITHM_INDIRECT = 252,
+ DNSSEC_KEY_ALGORITHM_PRIVATEDNS = 253,
+ DNSSEC_KEY_ALGORITHM_PRIVATEOID = 254,
+} dnssec_key_algorithm_t;
+
+/*!
+ * DS algorithm numbers.
+ *
+ * \see https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
+ */
+typedef enum dnssec_key_digest {
+ DNSSEC_KEY_DIGEST_INVALID = 0,
+ DNSSEC_KEY_DIGEST_SHA1 = 1,
+ DNSSEC_KEY_DIGEST_SHA256 = 2,
+ DNSSEC_KEY_DIGEST_SHA384 = 4,
+} dnssec_key_digest_t;
+
+/*!
+ * DS algorithm digest lengths in bytes.
+ */
+typedef enum dnssec_key_digest_len {
+ DNSSEC_KEY_DIGEST_LEN_SHA1 = 20, /*!< RFC 3658 */
+ DNSSEC_KEY_DIGEST_LEN_SHA256 = 32, /*!< RFC 4509 */
+ DNSSEC_KEY_DIGEST_LEN_SHA384 = 48, /*!< RFC 6605 */
+} dnssec_key_digest_len_t;
+
+struct dnssec_key;
+
+/*!
+ * DNSSEC key.
+ */
+typedef struct dnssec_key dnssec_key_t;
+
+/*!
+ * Check whether a DNSKEY algorithm is supported.
+ *
+ * @note: less secure algorithms may go unsupported on purpose.
+ * - if IETF RFCs deprecate algorithm even for validation (see RFC 8624)
+ * - if (local) GnuTLS policy considers an algorithm insecure
+ */
+bool dnssec_algorithm_key_support(dnssec_key_algorithm_t algorithm);
+
+/*!
+ * Check if the algorithm allows deterministic signing.
+ */
+bool dnssec_algorithm_reproducible(dnssec_key_algorithm_t algorithm, bool enabled);
+
+/*!
+ * Allocate new DNSSEC key.
+ *
+ * The protocol field of the key is set to 3 (DNSSEC).
+ * The flags field of the key is set to 256 (zone key, no SEP).
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_key_new(dnssec_key_t **key);
+
+/*!
+ * Clear the DNSSEC key.
+ *
+ * Has the same effect as calling \ref dnssec_key_free and \ref dnssec_key_new.
+ */
+void dnssec_key_clear(dnssec_key_t *key);
+
+/*!
+ * Free the key allocated by \ref dnssec_key_new.
+ */
+void dnssec_key_free(dnssec_key_t *key);
+
+/*!
+ * Create a copy of a DNSSEC key.
+ *
+ * Only a public part of the key is copied.
+ */
+dnssec_key_t *dnssec_key_dup(const dnssec_key_t *key);
+
+/*!
+ * Get the key tag of the DNSSEC key.
+ */
+uint16_t dnssec_key_get_keytag(const dnssec_key_t *key);
+
+/*!
+ * Get the domain name of the DNSSEC key.
+ */
+const uint8_t *dnssec_key_get_dname(const dnssec_key_t *key);
+
+/*!
+ * Set the domain name of the DNSSEC key.
+ */
+int dnssec_key_set_dname(dnssec_key_t *key, const uint8_t *dname);
+
+/*!
+ * Get the flags field of the DNSSEC key.
+ */
+uint16_t dnssec_key_get_flags(const dnssec_key_t *key);
+
+/*!
+ * Set the flags field of the DNSSEC key.
+ */
+int dnssec_key_set_flags(dnssec_key_t *key, uint16_t flags);
+
+/*!
+ * Get the protocol field of the DNSSEC key.
+ */
+uint8_t dnssec_key_get_protocol(const dnssec_key_t *key);
+
+/*!
+ * Get the protocol field of the DNSSEC key.
+ */
+int dnssec_key_set_protocol(dnssec_key_t *key, uint8_t protocol);
+
+/*!
+ * Get the algorithm field of the DNSSEC key.
+ */
+uint8_t dnssec_key_get_algorithm(const dnssec_key_t *key);
+
+/*!
+ * Set the algorithm field of the DNSSEC key.
+ *
+ * The function will fail if the algorithm is incompatible with the
+ * loaded key. This means, that the function can be used to set the initial
+ * algorithm and later, only the hashing algorithm can be changed.
+ */
+int dnssec_key_set_algorithm(dnssec_key_t *key, uint8_t algorithm);
+
+/*!
+ * Get the public key field of the DNSSEC key.
+ *
+ * The returned content must not be modified by the caller. A reference
+ * to internally allocated structure is returned.
+ */
+int dnssec_key_get_pubkey(const dnssec_key_t *key, dnssec_binary_t *pubkey);
+
+/*!
+ * Set the public key field of the DNSSEC key.
+ *
+ * A valid algorithm has to be set prior to calling this function.
+ *
+ * The function will fail if the key is already loaded in the structure.
+ */
+int dnssec_key_set_pubkey(dnssec_key_t *key, const dnssec_binary_t *pubkey);
+
+/*!
+ * Get the bit size of the cryptographic key used with the DNSSEC key.
+ */
+unsigned dnssec_key_get_size(const dnssec_key_t *key);
+
+/*!
+ * \brief Compute key ID from public key.
+ *
+ * \param key Key structure holding the public key.
+ * \param id Output: key ID in hex.
+ *
+ * \return DNSSEC_E*
+ */
+int dnssec_key_get_keyid(const dnssec_key_t *key, char **id);
+
+/*!
+ * Get the RDATA of the DNSSEC key.
+ *
+ * The returned content must not be modified by the caller. A reference
+ * to internally allocated structure is returned.
+ */
+int dnssec_key_get_rdata(const dnssec_key_t *key, dnssec_binary_t *rdata);
+
+/*!
+ * Set the RDATA of the DNSSEC key.
+ *
+ * Calling this function has the same effect as setting the individual
+ * fields of the key step-by-step. The same limitations apply.
+ */
+int dnssec_key_set_rdata(dnssec_key_t *key, const dnssec_binary_t *rdata);
+
+/*!
+ * Load PKCS #8 private key in the unencrypted PEM format.
+ *
+ * At least an algorithm must be set prior to calling this function.
+ *
+ * The function will create public key, unless it was already set (using
+ * \ref dnssec_key_set_pubkey or \ref dnssec_key_set_rdata). If the public key
+ * was set, the function will prevent loading of non-matching private key.
+ */
+int dnssec_key_load_pkcs8(dnssec_key_t *key, const dnssec_binary_t *pem);
+
+/*!
+ * Check if the key can be used for signing.
+ */
+bool dnssec_key_can_sign(const dnssec_key_t *key);
+
+/*!
+ * Check if the key can be used for verification.
+ */
+bool dnssec_key_can_verify(const dnssec_key_t *key);
+
+/*!
+ * Get private key size range for a DNSSEC algorithm.
+ *
+ * \param[in] algorithm DNSKEY algorithm.
+ * \param[out] min Minimal size of the private key (can be NULL).
+ * \param[out] max Maximal size of the private key (can be NULL).
+ *
+ * \return DNSSEC_EOK for valid parameters.
+ */
+int dnssec_algorithm_key_size_range(dnssec_key_algorithm_t algorithm,
+ unsigned *min, unsigned *max);
+
+/*!
+ * Check if the private key size matches DNSKEY constraints.
+ *
+ * \param algorithm DNSKEY algorithm.
+ * \param bits Private key size.
+ *
+ * \return DNSKEY algorithm matches the key size constraints.
+ */
+bool dnssec_algorithm_key_size_check(dnssec_key_algorithm_t algorithm,
+ unsigned bits);
+
+/*!
+ * Get default key size for given algorithm.
+ *
+ * The default size is balance between security and response lengths with
+ * respect to use in DNS.
+ */
+int dnssec_algorithm_key_size_default(dnssec_key_algorithm_t algorithm);
+
+/*!
+ * Check whether a DS algorithm is supported.
+ *
+ * @note: see note at dnssec_algorithm_key_support().
+ */
+bool dnssec_algorithm_digest_support(dnssec_key_digest_t algorithm);
+
+/*!
+ * Create DS (Delegation Signer) RDATA from DNSSEC key.
+ *
+ * \param[in] key DNSSEC key.
+ * \param[in] digest Digest algorithm to be used.
+ * \param[out] rdata Allocated DS RDATA.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_key_create_ds(const dnssec_key_t *key, dnssec_key_digest_t digest,
+ dnssec_binary_t *rdata);
+
+/*! @} */
diff --git a/src/libdnssec/key/algorithm.c b/src/libdnssec/key/algorithm.c
new file mode 100644
index 0000000..a9bc3ee
--- /dev/null
+++ b/src/libdnssec/key/algorithm.c
@@ -0,0 +1,180 @@
+/* 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 <gnutls/gnutls.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/algorithm.h"
+#include "libdnssec/shared/shared.h"
+
+/* -- internal ------------------------------------------------------------- */
+
+struct limits {
+ unsigned min;
+ unsigned max;
+ unsigned def;
+ bool (*validate)(unsigned bits);
+};
+
+static const struct limits *get_limits(dnssec_key_algorithm_t algorithm)
+{
+ static const struct limits RSA = {
+ .min = 1024,
+ .max = 4096,
+ .def = 2048,
+ };
+
+ static const struct limits EC256 = {
+ .min = 256,
+ .max = 256,
+ .def = 256,
+ };
+
+ static const struct limits EC384 = {
+ .min = 384,
+ .max = 384,
+ .def = 384,
+ };
+
+ static const struct limits ED25519 = {
+ .min = 256,
+ .max = 256,
+ .def = 256,
+ };
+
+ static const struct limits ED448 = {
+ .min = 456,
+ .max = 456,
+ .def = 456,
+ };
+
+ switch (algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return &RSA;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ return &EC256;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return &EC384;
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ return &ED25519;
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return &ED448;
+ default:
+ return NULL;
+ }
+}
+
+/* -- internal API --------------------------------------------------------- */
+
+gnutls_pk_algorithm_t algorithm_to_gnutls(dnssec_key_algorithm_t dnssec)
+{
+ switch (dnssec) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return GNUTLS_PK_RSA;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return GNUTLS_PK_EC;
+#ifdef HAVE_ED25519
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ return GNUTLS_PK_EDDSA_ED25519;
+#endif
+#ifdef HAVE_ED448
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return GNUTLS_PK_EDDSA_ED448;
+#endif
+ default:
+ return GNUTLS_PK_UNKNOWN;
+ }
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+bool dnssec_algorithm_reproducible(dnssec_key_algorithm_t algorithm, bool enabled)
+{
+ (void)enabled;
+ switch (algorithm) {
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return true; // those are always reproducible
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+#ifdef HAVE_GNUTLS_REPRODUCIBLE
+ return enabled; // Reproducible only if GnuTLS supports && enabled
+#else
+ return false;
+#endif
+ default:
+ return false;
+ }
+}
+
+_public_
+int dnssec_algorithm_key_size_range(dnssec_key_algorithm_t algorithm,
+ unsigned *min_ptr, unsigned *max_ptr)
+{
+ if (!min_ptr && !max_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ const struct limits *limits = get_limits(algorithm);
+ if (!limits) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ if (min_ptr) {
+ *min_ptr = limits->min;
+ }
+ if (max_ptr) {
+ *max_ptr = limits->max;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+bool dnssec_algorithm_key_size_check(dnssec_key_algorithm_t algorithm,
+ unsigned bits)
+{
+ const struct limits *limits = get_limits(algorithm);
+ if (!limits) {
+ return false;
+ }
+
+ if (bits < limits->min || bits > limits->max) {
+ return false;
+ }
+
+ if (limits->validate && !limits->validate(bits)) {
+ return false;
+ }
+
+ return true;
+}
+
+_public_
+int dnssec_algorithm_key_size_default(dnssec_key_algorithm_t algorithm)
+{
+ const struct limits *limits = get_limits(algorithm);
+ return limits ? limits->def : 0;
+}
diff --git a/src/libdnssec/key/algorithm.h b/src/libdnssec/key/algorithm.h
new file mode 100644
index 0000000..4906675
--- /dev/null
+++ b/src/libdnssec/key/algorithm.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/gnutls.h>
+
+#include "libdnssec/key.h"
+
+/*!
+ * Convert DNSKEY algorithm identifier to GnuTLS identifier.
+ *
+ * \param dnssec DNSSEC DNSKEY algorithm identifier.
+ *
+ * \return GnuTLS private key algorithm identifier, GNUTLS_PK_UNKNOWN on error.
+ */
+gnutls_pk_algorithm_t algorithm_to_gnutls(dnssec_key_algorithm_t dnssec);
diff --git a/src/libdnssec/key/convert.c b/src/libdnssec/key/convert.c
new file mode 100644
index 0000000..56168f7
--- /dev/null
+++ b/src/libdnssec/key/convert.c
@@ -0,0 +1,375 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libdnssec/shared/bignum.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/algorithm.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+
+/* -- wrappers for GnuTLS types -------------------------------------------- */
+
+static size_t bignum_size_u_datum(const gnutls_datum_t *_bignum)
+{
+ const dnssec_binary_t bignum = binary_from_datum(_bignum);
+ return bignum_size_u(&bignum);
+}
+
+static void wire_write_bignum_datum(wire_ctx_t *ctx, size_t width,
+ const gnutls_datum_t *_bignum)
+{
+ const dnssec_binary_t bignum = binary_from_datum(_bignum);
+ bignum_write(ctx, width, &bignum);
+}
+
+static gnutls_datum_t wire_take_datum(wire_ctx_t *ctx, size_t count)
+{
+ gnutls_datum_t result = { .data = ctx->position, .size = count };
+ ctx->position += count;
+
+ return result;
+}
+
+/* -- DNSSEC to crypto ------------------------------------------------------*/
+
+/*!
+ * Convert RSA public key to DNSSEC format.
+ */
+static int rsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
+{
+ assert(key);
+ assert(rdata);
+
+ _cleanup_datum_ gnutls_datum_t modulus = { 0 };
+ _cleanup_datum_ gnutls_datum_t exponent = { 0 };
+
+ int result = gnutls_pubkey_get_pk_rsa_raw(key, &modulus, &exponent);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ size_t exponent_size = bignum_size_u_datum(&exponent);
+ if (exponent_size > UINT8_MAX) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ size_t modulus_size = bignum_size_u_datum(&modulus);
+
+ result = dnssec_binary_alloc(rdata, 1 + exponent_size + modulus_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ wire_ctx_t wire = binary_init(rdata);
+ wire_ctx_write_u8(&wire, exponent_size);
+ wire_write_bignum_datum(&wire, exponent_size, &exponent);
+ wire_write_bignum_datum(&wire, modulus_size, &modulus);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Get point size for an ECDSA curve.
+ */
+static size_t ecdsa_curve_point_size(gnutls_ecc_curve_t curve)
+{
+ switch (curve) {
+ case GNUTLS_ECC_CURVE_SECP256R1: return 32;
+ case GNUTLS_ECC_CURVE_SECP384R1: return 48;
+ default: return 0;
+ }
+}
+
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+static size_t eddsa_curve_point_size(gnutls_ecc_curve_t curve)
+{
+ switch (curve) {
+#ifdef HAVE_ED25519
+ case GNUTLS_ECC_CURVE_ED25519: return 32;
+#endif
+#ifdef HAVE_ED448
+ case GNUTLS_ECC_CURVE_ED448: return 57;
+#endif
+ default: return 0;
+ }
+}
+#endif
+
+/*!
+ * Convert ECDSA public key to DNSSEC format.
+ */
+static int ecdsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
+{
+ assert(key);
+ assert(rdata);
+
+ _cleanup_datum_ gnutls_datum_t point_x = { 0 };
+ _cleanup_datum_ gnutls_datum_t point_y = { 0 };
+ gnutls_ecc_curve_t curve = { 0 };
+
+ int result = gnutls_pubkey_get_pk_ecc_raw(key, &curve, &point_x, &point_y);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ size_t point_size = ecdsa_curve_point_size(curve);
+ if (point_size == 0) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ result = dnssec_binary_alloc(rdata, 2 * point_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ wire_ctx_t wire = binary_init(rdata);
+ wire_write_bignum_datum(&wire, point_size, &point_x);
+ wire_write_bignum_datum(&wire, point_size, &point_y);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Convert EDDSA public key to DNSSEC format.
+ */
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+static int eddsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata)
+{
+ assert(key);
+ assert(rdata);
+
+ _cleanup_datum_ gnutls_datum_t point_x = { 0 };
+ gnutls_ecc_curve_t curve = { 0 };
+
+ int result = gnutls_pubkey_get_pk_ecc_raw(key, &curve, &point_x, NULL);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ size_t point_size = eddsa_curve_point_size(curve);
+ if (point_size == 0) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ result = dnssec_binary_alloc(rdata, point_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ wire_ctx_t wire = binary_init(rdata);
+ wire_write_bignum_datum(&wire, point_size, &point_x);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ return DNSSEC_EOK;
+}
+#endif
+
+/* -- crypto to DNSSEC ------------------------------------------------------*/
+
+/*!
+ * Convert RSA key in DNSSEC format to crypto key.
+ */
+static int rsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key)
+{
+ assert(rdata);
+ assert(key);
+
+ if (rdata->size == 0) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ wire_ctx_t ctx = binary_init(rdata);
+
+ // parse public exponent
+
+ uint8_t exponent_size = wire_ctx_read_u8(&ctx);
+ if (exponent_size == 0 || wire_ctx_available(&ctx) < exponent_size) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ gnutls_datum_t exponent = wire_take_datum(&ctx, exponent_size);
+
+ // parse modulus
+
+ size_t modulus_size = wire_ctx_available(&ctx);
+ if (modulus_size == 0) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ gnutls_datum_t modulus = wire_take_datum(&ctx, modulus_size);
+
+ assert(wire_ctx_offset(&ctx) == rdata->size);
+
+ int result = gnutls_pubkey_import_rsa_raw(key, &modulus, &exponent);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Get ECDSA curve based on DNSKEY RDATA size.
+ */
+static gnutls_ecc_curve_t ecdsa_curve_from_rdata_size(size_t rdata_size)
+{
+ switch (rdata_size) {
+ case 64: return GNUTLS_ECC_CURVE_SECP256R1;
+ case 96: return GNUTLS_ECC_CURVE_SECP384R1;
+ default: return GNUTLS_ECC_CURVE_INVALID;
+ }
+}
+
+/*!
+ * Get EDDSA curve based on DNSKEY RDATA size.
+ */
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+static gnutls_ecc_curve_t eddsa_curve_from_rdata_size(size_t rdata_size)
+{
+ switch (rdata_size) {
+#ifdef HAVE_ED25519
+ case 32: return GNUTLS_ECC_CURVE_ED25519;
+#endif
+#ifdef HAVE_ED448
+ case 57: return GNUTLS_ECC_CURVE_ED448;
+#endif
+ default: return GNUTLS_ECC_CURVE_INVALID;
+ }
+}
+#endif
+
+/*!
+ * Convert ECDSA key in DNSSEC format to crypto key.
+ */
+static int ecdsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key)
+{
+ assert(rdata);
+ assert(key);
+
+ gnutls_ecc_curve_t curve = ecdsa_curve_from_rdata_size(rdata->size);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ // parse points
+
+ wire_ctx_t ctx = binary_init(rdata);
+
+ size_t point_size = wire_ctx_available(&ctx) / 2;
+ gnutls_datum_t point_x = wire_take_datum(&ctx, point_size);
+ gnutls_datum_t point_y = wire_take_datum(&ctx, point_size);
+ assert(wire_ctx_offset(&ctx) == rdata->size);
+
+ int result = gnutls_pubkey_import_ecc_raw(key, curve, &point_x, &point_y);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Convert EDDSA key in DNSSEC format to crypto key.
+ */
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+static int eddsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key)
+{
+ assert(rdata);
+ assert(key);
+
+ gnutls_ecc_curve_t curve = eddsa_curve_from_rdata_size(rdata->size);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ wire_ctx_t ctx = binary_init(rdata);
+
+ size_t point_size = wire_ctx_available(&ctx);
+ gnutls_datum_t point_x = wire_take_datum(&ctx, point_size);
+ assert(wire_ctx_offset(&ctx) == rdata->size);
+
+ int result = gnutls_pubkey_import_ecc_raw(key, curve, &point_x, NULL);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+#endif
+
+/* -- internal API --------------------------------------------------------- */
+
+/*!
+ * Encode public key to the format used in DNSKEY RDATA.
+ */
+int convert_pubkey_to_dnskey(gnutls_pubkey_t key, dnssec_binary_t *rdata)
+{
+ assert(key);
+ assert(rdata);
+
+ int algorithm = gnutls_pubkey_get_pk_algorithm(key, NULL);
+ if (algorithm < 0) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ switch ((gnutls_pk_algorithm_t)algorithm) {
+ case GNUTLS_PK_RSA: return rsa_pubkey_to_rdata(key, rdata);
+ case GNUTLS_PK_EC: return ecdsa_pubkey_to_rdata(key, rdata);
+#ifdef HAVE_ED25519
+ case GNUTLS_PK_EDDSA_ED25519: return eddsa_pubkey_to_rdata(key, rdata);
+#endif
+#ifdef HAVE_ED448
+ case GNUTLS_PK_EDDSA_ED448: return eddsa_pubkey_to_rdata(key, rdata);
+#endif
+ default: return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+}
+
+/*!
+ * Create public key from the format encoded in DNSKEY RDATA.
+ */
+int convert_dnskey_to_pubkey(uint8_t algorithm, const dnssec_binary_t *rdata,
+ gnutls_pubkey_t key)
+{
+ assert(rdata);
+ assert(key);
+
+ gnutls_pk_algorithm_t gnutls_alg = algorithm_to_gnutls(algorithm);
+
+ switch(gnutls_alg) {
+ case GNUTLS_PK_RSA: return rsa_rdata_to_pubkey(rdata, key);
+ case GNUTLS_PK_EC: return ecdsa_rdata_to_pubkey(rdata, key);
+#ifdef HAVE_ED25519
+ case GNUTLS_PK_EDDSA_ED25519: return eddsa_rdata_to_pubkey(rdata, key);
+#endif
+#ifdef HAVE_ED448
+ case GNUTLS_PK_EDDSA_ED448: return eddsa_rdata_to_pubkey(rdata, key);
+#endif
+ default: return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+}
diff --git a/src/libdnssec/key/convert.h b/src/libdnssec/key/convert.h
new file mode 100644
index 0000000..079a5f6
--- /dev/null
+++ b/src/libdnssec/key/convert.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/abstract.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/key.h"
+
+/*!
+ * Encode public key into the format used in DNSKEY RDATA.
+ *
+ * \param[in] key Public key to be encoded.
+ * \param[out] rdata Encoded key (allocated).
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int convert_pubkey_to_dnskey(gnutls_pubkey_t key, dnssec_binary_t *rdata);
+
+/*!
+ * Create public key from the format encoded in DNSKEY RDATA.
+ *
+ * \param[in] algorithm DNSSEC algorithm identification.
+ * \param[in] rdata Public key in DNSKEY RDATA format.
+ * \param[out] key GnuTLS public key (initialized).
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int convert_dnskey_to_pubkey(uint8_t algorithm, const dnssec_binary_t *rdata,
+ gnutls_pubkey_t key);
diff --git a/src/libdnssec/key/dnskey.c b/src/libdnssec/key/dnskey.c
new file mode 100644
index 0000000..6360700
--- /dev/null
+++ b/src/libdnssec/key/dnskey.c
@@ -0,0 +1,91 @@
+/* Copyright (C) 2022 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 "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/key/convert.h"
+#include "libdnssec/shared/binary_wire.h"
+
+/* -- internal API --------------------------------------------------------- */
+
+/*!
+ * Update 'Public key' field of DNSKEY RDATA.
+ */
+int dnskey_rdata_set_pubkey(dnssec_binary_t *rdata, const dnssec_binary_t *pubkey)
+{
+ assert(rdata);
+ assert(pubkey);
+
+ size_t new_size = DNSKEY_RDATA_OFFSET_PUBKEY + pubkey->size;
+ int result = dnssec_binary_resize(rdata, new_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ wire_ctx_t wire = binary_init(rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY);
+ binary_write(&wire, pubkey);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Create a GnuTLS public key from DNSKEY RDATA.
+ *
+ * \param rdata DNSKEY RDATA.
+ * \param key_ptr Resulting public key.
+ */
+int dnskey_rdata_to_crypto_key(const dnssec_binary_t *rdata, gnutls_pubkey_t *key_ptr)
+{
+ assert(rdata);
+ assert(key_ptr);
+
+ uint8_t algorithm = 0, protocol = 0, flags_hi = 0;
+ dnssec_binary_t rdata_pubkey = { 0 };
+
+ wire_ctx_t wire = binary_init(rdata);
+
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_FLAGS);
+ flags_hi = wire_ctx_read_u8(&wire);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PROTOCOL);
+ protocol = wire_ctx_read_u8(&wire);
+ if (flags_hi != 0x1 || protocol != 0x3) {
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM);
+ algorithm = wire_ctx_read_u8(&wire);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY);
+ binary_available(&wire, &rdata_pubkey);
+
+ gnutls_pubkey_t key = NULL;
+ int result = gnutls_pubkey_init(&key);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ result = convert_dnskey_to_pubkey(algorithm, &rdata_pubkey, key);
+ if (result != DNSSEC_EOK) {
+ gnutls_pubkey_deinit(key);
+ return result;
+ }
+
+ *key_ptr = key;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/key/dnskey.h b/src/libdnssec/key/dnskey.h
new file mode 100644
index 0000000..e765046
--- /dev/null
+++ b/src/libdnssec/key/dnskey.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/abstract.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+
+/*!
+ * DNSKEY RDATA fields offsets.
+ *
+ * \see RFC 4034 (section 2.1)
+ */
+enum dnskey_rdata_offsets {
+ DNSKEY_RDATA_OFFSET_FLAGS = 0,
+ DNSKEY_RDATA_OFFSET_PROTOCOL = 2,
+ DNSKEY_RDATA_OFFSET_ALGORITHM = 3,
+ DNSKEY_RDATA_OFFSET_PUBKEY = 4,
+};
+
+/*!
+ * Update 'Public key' field of DNSKEY RDATA.
+ */
+int dnskey_rdata_set_pubkey(dnssec_binary_t *rdata,
+ const dnssec_binary_t *pubkey);
+
+/*!
+ * Create a GnuTLS public key from DNSKEY RDATA.
+ */
+int dnskey_rdata_to_crypto_key(const dnssec_binary_t *rdata,
+ gnutls_pubkey_t *key_ptr);
diff --git a/src/libdnssec/key/ds.c b/src/libdnssec/key/ds.c
new file mode 100644
index 0000000..ad580ad
--- /dev/null
+++ b/src/libdnssec/key/ds.c
@@ -0,0 +1,128 @@
+/* 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 "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/shared/dname.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+/*!
+ * Convert DNSSEC DS digest algorithm to GnuTLS digest algorithm.
+ */
+static gnutls_digest_algorithm_t lookup_algorithm(dnssec_key_digest_t algorithm)
+{
+ switch (algorithm) {
+ case DNSSEC_KEY_DIGEST_SHA1: return GNUTLS_DIG_SHA1;
+ case DNSSEC_KEY_DIGEST_SHA256: return GNUTLS_DIG_SHA256;
+ case DNSSEC_KEY_DIGEST_SHA384: return GNUTLS_DIG_SHA384;
+ default:
+ return GNUTLS_DIG_UNKNOWN;
+ };
+}
+
+_public_
+bool dnssec_algorithm_digest_support(dnssec_key_digest_t algorithm)
+{
+ /* GnuTLS docs:
+ * > It is not possible to query for insecure hash algorithms directly
+ * > (only indirectly through the signature API).
+ * So let's query combining the hash with RSA.
+ */
+ gnutls_sign_algorithm_t rsa;
+ switch (algorithm) {
+ case DNSSEC_KEY_DIGEST_SHA1: rsa = GNUTLS_SIGN_RSA_SHA1; break;
+ case DNSSEC_KEY_DIGEST_SHA256: rsa = GNUTLS_SIGN_RSA_SHA256; break;
+ case DNSSEC_KEY_DIGEST_SHA384: rsa = GNUTLS_SIGN_RSA_SHA384; break;
+ default:
+ return false;
+ };
+ return gnutls_sign_is_secure(rsa);
+}
+
+static void wire_write_digest(wire_ctx_t *wire,
+ gnutls_hash_hd_t digest, int digest_size)
+{
+ assert(wire_ctx_available(wire) >= digest_size);
+ gnutls_hash_output(digest, wire->position);
+ wire->position += digest_size;
+}
+
+_public_
+int dnssec_key_create_ds(const dnssec_key_t *key,
+ dnssec_key_digest_t ds_algorithm,
+ dnssec_binary_t *rdata_ptr)
+{
+ if (!key || !rdata_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (!key->dname) {
+ return DNSSEC_INVALID_KEY_NAME;
+ }
+
+ if (!key->public_key){
+ return DNSSEC_INVALID_PUBLIC_KEY;
+ }
+
+ gnutls_digest_algorithm_t algorithm = lookup_algorithm(ds_algorithm);
+ if (algorithm == GNUTLS_DIG_UNKNOWN) {
+ return DNSSEC_INVALID_DS_ALGORITHM;
+ }
+
+ // compute DS hash
+
+ _cleanup_hash_ gnutls_hash_hd_t digest = NULL;
+ int r = gnutls_hash_init(&digest, algorithm);
+ if (r < 0) {
+ return DNSSEC_DS_HASHING_ERROR;
+ }
+
+ if (gnutls_hash(digest, key->dname, dname_length(key->dname)) != 0 ||
+ gnutls_hash(digest, key->rdata.data, key->rdata.size) != 0
+ ) {
+ return DNSSEC_DS_HASHING_ERROR;
+ }
+
+ // build DS RDATA
+
+ int digest_size = gnutls_hash_get_len(algorithm);
+ if (digest_size == 0) {
+ return DNSSEC_DS_HASHING_ERROR;
+ }
+
+ dnssec_binary_t rdata = { 0 };
+ r = dnssec_binary_alloc(&rdata, 4 + digest_size);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ wire_ctx_t wire = binary_init(&rdata);
+ wire_ctx_write_u16(&wire, dnssec_key_get_keytag(key));
+ wire_ctx_write_u8(&wire, dnssec_key_get_algorithm(key));
+ wire_ctx_write_u8(&wire, ds_algorithm);
+ wire_write_digest(&wire, digest, digest_size);
+ assert(wire_ctx_offset(&wire) == wire.size);
+
+ *rdata_ptr = rdata;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/key/internal.h b/src/libdnssec/key/internal.h
new file mode 100644
index 0000000..550e454
--- /dev/null
+++ b/src/libdnssec/key/internal.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/abstract.h>
+#include <stdint.h>
+
+#include "libdnssec/key.h"
+#include "libdnssec/shared/dname.h"
+
+/*!
+ * DNSSEC key.
+ */
+struct dnssec_key {
+ uint8_t *dname;
+ dnssec_binary_t rdata;
+
+ gnutls_pubkey_t public_key;
+ gnutls_privkey_t private_key;
+ unsigned bits;
+};
diff --git a/src/libdnssec/key/key.c b/src/libdnssec/key/key.c
new file mode 100644
index 0000000..f363167
--- /dev/null
+++ b/src/libdnssec/key/key.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <gnutls/abstract.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/algorithm.h"
+#include "libdnssec/key/convert.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/shared/keyid_gnutls.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/keytag.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+#include "contrib/wire_ctx.h"
+
+/*!
+ * Minimal size of DNSKEY RDATA.
+ */
+#define DNSKEY_RDATA_MIN_SIZE DNSKEY_RDATA_OFFSET_PUBKEY
+
+/*!
+ * RDATA template for newly allocated keys.
+ */
+static const dnssec_binary_t DNSKEY_RDATA_TEMPLATE = {
+ .size = 4,
+ .data = (uint8_t []) { 0x01, 0x00, 0x03, 0x00 }
+};
+
+/* -- key allocation ------------------------------------------------------- */
+
+_public_
+int dnssec_key_new(dnssec_key_t **key_ptr)
+{
+ if (!key_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_key_t *key = calloc(1, sizeof(*key));
+ if (!key) {
+ return DNSSEC_ENOMEM;
+ }
+
+ int r = dnssec_binary_dup(&DNSKEY_RDATA_TEMPLATE, &key->rdata);
+ if (r != DNSSEC_EOK) {
+ free(key);
+ return DNSSEC_ENOMEM;
+ }
+
+ *key_ptr = key;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Clear allocated fields inside the key structure, except RDATA.
+ */
+static void key_free_internals(dnssec_key_t *key)
+{
+ assert(key);
+
+ free(key->dname);
+ key->dname = NULL;
+
+ gnutls_privkey_deinit(key->private_key);
+ key->private_key = NULL;
+
+ gnutls_pubkey_deinit(key->public_key);
+ key->public_key = NULL;
+}
+
+_public_
+void dnssec_key_clear(dnssec_key_t *key)
+{
+ if (!key) {
+ return;
+ }
+
+ // reuse RDATA
+ dnssec_binary_t rdata = key->rdata;
+
+ // clear the structure
+ key_free_internals(key);
+ clear_struct(key);
+
+ // restore template RDATA (downsize, no need to realloc)
+ assert(rdata.size >= DNSKEY_RDATA_MIN_SIZE);
+ rdata.size = DNSKEY_RDATA_MIN_SIZE;
+ memmove(rdata.data, DNSKEY_RDATA_TEMPLATE.data, rdata.size);
+
+ key->rdata = rdata;
+}
+
+_public_
+void dnssec_key_free(dnssec_key_t *key)
+{
+ if (!key) {
+ return;
+ }
+
+ key_free_internals(key);
+ dnssec_binary_free(&key->rdata);
+
+ free(key);
+}
+
+_public_
+dnssec_key_t *dnssec_key_dup(const dnssec_key_t *key)
+{
+ if (!key) {
+ return NULL;
+ }
+
+ dnssec_key_t *dup = NULL;
+
+ if (dnssec_key_new(&dup) != DNSSEC_EOK ||
+ dnssec_key_set_dname(dup, key->dname) != DNSSEC_EOK ||
+ dnssec_key_set_rdata(dup, &key->rdata) != DNSSEC_EOK
+ ) {
+ dnssec_key_free(dup);
+ return NULL;
+ }
+
+ return dup;
+}
+
+/* -- freely modifiable attributes ----------------------------------------- */
+
+_public_
+const uint8_t *dnssec_key_get_dname(const dnssec_key_t *key)
+{
+ if (!key) {
+ return NULL;
+ }
+
+ return key->dname;
+}
+
+_public_
+int dnssec_key_set_dname(dnssec_key_t *key, const uint8_t *dname)
+{
+ if (!key) {
+ return DNSSEC_EINVAL;
+ }
+
+ uint8_t *copy = NULL;
+ if (dname) {
+ copy = dname_copy(dname);
+ if (!copy) {
+ return DNSSEC_ENOMEM;
+ }
+
+ dname_normalize(copy);
+ }
+
+ free(key->dname);
+ key->dname = copy;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+uint16_t dnssec_key_get_flags(const dnssec_key_t *key)
+{
+ if (!key) {
+ return 0;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_FLAGS);
+ return wire_ctx_read_u16(&wire);
+}
+
+_public_
+int dnssec_key_set_flags(dnssec_key_t *key, uint16_t flags)
+{
+ if (!key) {
+ return DNSSEC_EINVAL;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_FLAGS);
+ wire_ctx_write_u16(&wire, flags);
+
+ return DNSSEC_EOK;
+}
+
+_public_
+uint8_t dnssec_key_get_protocol(const dnssec_key_t *key)
+{
+ if (!key) {
+ return 0;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PROTOCOL);
+ return wire_ctx_read_u8(&wire);
+}
+
+_public_
+int dnssec_key_set_protocol(dnssec_key_t *key, uint8_t protocol)
+{
+ if (!key) {
+ return DNSSEC_EINVAL;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PROTOCOL);
+ wire_ctx_write_u8(&wire, protocol);
+
+ return DNSSEC_EOK;
+}
+
+/* -- restricted attributes ------------------------------------------------ */
+
+_public_
+uint16_t dnssec_key_get_keytag(const dnssec_key_t *key)
+{
+ uint16_t keytag = 0;
+ if (dnssec_key_can_verify(key)) {
+ dnssec_keytag(&key->rdata, &keytag);
+ }
+
+ return keytag;
+}
+
+/*!
+ * Check if current public key algorithm matches with the new algorithm.
+ */
+static bool can_change_algorithm(dnssec_key_t *key, uint8_t algorithm)
+{
+ assert(key);
+
+ if (!key->public_key) {
+ return true;
+ }
+
+ gnutls_pk_algorithm_t update = algorithm_to_gnutls(algorithm);
+ if (update == GNUTLS_PK_UNKNOWN) {
+ return false;
+ }
+
+ int current = gnutls_pubkey_get_pk_algorithm(key->public_key, NULL);
+ assert(current >= 0);
+
+ return current == update;
+}
+
+_public_
+uint8_t dnssec_key_get_algorithm(const dnssec_key_t *key)
+{
+ if (!key) {
+ return 0;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM);
+ return wire_ctx_read_u8(&wire);
+}
+
+_public_
+int dnssec_key_set_algorithm(dnssec_key_t *key, uint8_t algorithm)
+{
+ if (!key) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (!can_change_algorithm(key, algorithm)) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM);
+ wire_ctx_write_u8(&wire, algorithm);
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_key_get_pubkey(const dnssec_key_t *key, dnssec_binary_t *pubkey)
+{
+ if (!key || !pubkey) {
+ return DNSSEC_EINVAL;
+ }
+
+ wire_ctx_t wire = binary_init(&key->rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY);
+ binary_available(&wire, pubkey);
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_key_set_pubkey(dnssec_key_t *key, const dnssec_binary_t *pubkey)
+{
+ if (!key || !pubkey || !pubkey->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (key->public_key) {
+ return DNSSEC_KEY_ALREADY_PRESENT;
+ }
+
+ if (dnssec_key_get_algorithm(key) == 0) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ int result = dnskey_rdata_set_pubkey(&key->rdata, pubkey);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ result = dnskey_rdata_to_crypto_key(&key->rdata, &key->public_key);
+ if (result != DNSSEC_EOK) {
+ key->rdata.size = DNSKEY_RDATA_OFFSET_PUBKEY; // downsize
+ return result;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+unsigned dnssec_key_get_size(const dnssec_key_t *key)
+{
+ if (!key || !key->public_key) {
+ return 0;
+ }
+
+ unsigned bits = 0;
+ uint8_t algorithm = dnssec_key_get_algorithm(key);
+ switch (algorithm) {
+ case 13:
+ bits = 256;
+ break;
+ case 14:
+ bits = 384;
+ break;
+ case 15:
+ bits = 256;
+ break;
+ case 16:
+ bits = 456;
+ break;
+ default:
+ gnutls_pubkey_get_pk_algorithm(key->public_key, &bits);
+ }
+
+ return bits;
+}
+
+_public_
+int dnssec_key_get_keyid(const dnssec_key_t *key, char **id)
+{
+ if (!key || !id) {
+ return DNSSEC_EINVAL;
+ }
+
+ return keyid_pubkey_hex(key->public_key, id);
+}
+
+_public_
+int dnssec_key_get_rdata(const dnssec_key_t *key, dnssec_binary_t *rdata)
+{
+ if (!key || !rdata) {
+ return DNSSEC_EINVAL;
+ }
+
+ *rdata = key->rdata;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_key_set_rdata(dnssec_key_t *key, const dnssec_binary_t *rdata)
+{
+ if (!key || !rdata || !rdata->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (rdata->size < DNSKEY_RDATA_MIN_SIZE) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ if (key->public_key) {
+ return DNSSEC_KEY_ALREADY_PRESENT;
+ }
+
+ gnutls_pubkey_t new_pubkey = NULL;
+ int result = dnskey_rdata_to_crypto_key(rdata, &new_pubkey);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ result = dnssec_binary_resize(&key->rdata, rdata->size);
+ if (result != DNSSEC_EOK) {
+ gnutls_pubkey_deinit(new_pubkey);
+ return result;
+ }
+
+ // commit result
+ memmove(key->rdata.data, rdata->data, rdata->size);
+ key->public_key = new_pubkey;
+
+ return DNSSEC_EOK;
+}
+
+/* -- key presence checking ------------------------------------------------ */
+
+_public_
+bool dnssec_key_can_sign(const dnssec_key_t *key)
+{
+ return key && key->private_key;
+}
+
+_public_
+bool dnssec_key_can_verify(const dnssec_key_t *key)
+{
+ return key && key->public_key;
+}
diff --git a/src/libdnssec/key/keytag.c b/src/libdnssec/key/keytag.c
new file mode 100644
index 0000000..a14353e
--- /dev/null
+++ b/src/libdnssec/key/keytag.c
@@ -0,0 +1,88 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/shared/shared.h"
+
+/*!
+ * Compute keytag for RSA/MD5 key.
+ *
+ * \see RFC 2537 (section 2), RFC 4034 (appendix B.1)
+ */
+static uint16_t keytag_compat(const dnssec_binary_t *rdata)
+{
+ assert(rdata);
+ assert(rdata->data);
+
+ if (rdata->size < 9) { // in fact, the condition could be stricter
+ return 0;
+ }
+
+ uint8_t msb = rdata->data[rdata->size - 3];
+ uint8_t lsb = rdata->data[rdata->size - 2];
+
+ return (msb << 8) + lsb;
+}
+
+/*!
+ * Compute keytag for other than RSA/MD5 key.
+ *
+ * \see RFC 4034 (appendix B)
+ */
+static uint16_t keytag_current(const dnssec_binary_t *rdata)
+{
+ assert(rdata);
+ assert(rdata->data);
+
+ uint32_t ac = 0;
+ for (int i = 0; i < rdata->size; i++) {
+ ac += (i & 1) ? rdata->data[i] : rdata->data[i] << 8;
+ }
+
+ return (ac >> 16) + ac;
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+/*!
+ * Compute keytag for a DNSSEC key.
+ */
+_public_
+int dnssec_keytag(const dnssec_binary_t *rdata, uint16_t *keytag)
+{
+ if (!rdata || !keytag) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (!rdata->data || rdata->size < DNSKEY_RDATA_OFFSET_PUBKEY) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ uint8_t algorithm = rdata->data[DNSKEY_RDATA_OFFSET_ALGORITHM];
+ if (algorithm == 1) {
+ *keytag = keytag_compat(rdata);
+ } else {
+ *keytag = keytag_current(rdata);
+ }
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/key/privkey.c b/src/libdnssec/key/privkey.c
new file mode 100644
index 0000000..abe968a
--- /dev/null
+++ b/src/libdnssec/key/privkey.c
@@ -0,0 +1,140 @@
+/* Copyright (C) 2018 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 <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key/algorithm.h"
+#include "libdnssec/key/convert.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/key/privkey.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+
+/* -- internal functions --------------------------------------------------- */
+
+/*!
+ * Check if the algorithm number is valid for given DNSKEY.
+ */
+static bool valid_algorithm(dnssec_key_t *key, gnutls_privkey_t privkey)
+{
+ uint8_t current = dnssec_key_get_algorithm(key);
+ int gnu_algorithm = gnutls_privkey_get_pk_algorithm(privkey, NULL);
+
+ return (gnu_algorithm == algorithm_to_gnutls(current));
+}
+
+/*!
+ * Create GnuTLS public key from private key.
+ */
+static int public_from_private(gnutls_privkey_t privkey, gnutls_pubkey_t *pubkey)
+{
+ assert(privkey);
+ assert(pubkey);
+
+ gnutls_pubkey_t new_key = NULL;
+ int result = gnutls_pubkey_init(&new_key);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ result = gnutls_pubkey_import_privkey(new_key, privkey, 0, 0);
+ if (result != GNUTLS_E_SUCCESS) {
+ gnutls_pubkey_deinit(new_key);
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ *pubkey = new_key;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Create public key (GnuTLS and DNSKEY RDATA) from a private key.
+ */
+static int create_public_key(gnutls_privkey_t privkey,
+ gnutls_pubkey_t *pubkey_ptr,
+ dnssec_binary_t *rdata)
+{
+ assert(privkey);
+ assert(pubkey_ptr);
+ assert(rdata);
+
+ // crypto public key
+
+ gnutls_pubkey_t pubkey = NULL;
+ int result = public_from_private(privkey, &pubkey);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ // dnssec public key
+
+ _cleanup_binary_ dnssec_binary_t rdata_pubkey = { 0 };
+ result = convert_pubkey_to_dnskey(pubkey, &rdata_pubkey);
+ if (result != DNSSEC_EOK) {
+ gnutls_pubkey_deinit(pubkey);
+ return result;
+ }
+
+ size_t rdata_size = DNSKEY_RDATA_OFFSET_PUBKEY + rdata_pubkey.size;
+ result = dnssec_binary_resize(rdata, rdata_size);
+ if (result != DNSSEC_EOK) {
+ gnutls_pubkey_deinit(pubkey);
+ return result;
+ }
+
+ // updated RDATA
+
+ wire_ctx_t wire = binary_init(rdata);
+ wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY);
+ binary_write(&wire, &rdata_pubkey);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ *pubkey_ptr = pubkey;
+
+ return DNSSEC_EOK;
+}
+
+/* -- internal API --------------------------------------------------------- */
+
+/*!
+ * Load a private key into a DNSSEC key, create a public part if necessary.
+ */
+int key_set_private_key(dnssec_key_t *key, gnutls_privkey_t privkey)
+{
+ assert(key);
+ assert(privkey);
+ assert(key->private_key == NULL);
+
+ if (!valid_algorithm(key, privkey)) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ if (!key->public_key) {
+ int r = create_public_key(privkey, &key->public_key, &key->rdata);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+ }
+
+ key->private_key = privkey;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/key/privkey.h b/src/libdnssec/key/privkey.h
new file mode 100644
index 0000000..8afe7c9
--- /dev/null
+++ b/src/libdnssec/key/privkey.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/abstract.h>
+
+#include "libdnssec/key.h"
+
+/*!
+ * Load a private key into a DNSSEC key, create a public part if necessary.
+ *
+ * If the public key is not loaded, at least an algorithm must be set.
+ *
+ * Updates private key, public key, RDATA, and key identifiers.
+ *
+ * \param key DNSSEC key to be updated.
+ * \param privkey Private key to be set.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int key_set_private_key(dnssec_key_t *key, gnutls_privkey_t privkey);
diff --git a/src/libdnssec/key/simple.c b/src/libdnssec/key/simple.c
new file mode 100644
index 0000000..10126cc
--- /dev/null
+++ b/src/libdnssec/key/simple.c
@@ -0,0 +1,55 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/key/privkey.h"
+#include "libdnssec/pem.h"
+#include "libdnssec/shared/shared.h"
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_key_load_pkcs8(dnssec_key_t *key, const dnssec_binary_t *pem)
+{
+ if (!key || !pem || !pem->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (dnssec_key_get_algorithm(key) == 0) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ gnutls_privkey_t privkey = NULL;
+ int r = dnssec_pem_to_privkey(pem, &privkey);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = key_set_private_key(key, privkey);
+ if (r != DNSSEC_EOK) {
+ gnutls_privkey_deinit(privkey);
+ return r;
+ }
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/keyid.c b/src/libdnssec/keyid.c
new file mode 100644
index 0000000..5a6f29d
--- /dev/null
+++ b/src/libdnssec/keyid.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/keyid.h"
+#include "libdnssec/shared/shared.h"
+
+#include "contrib/ctype.h"
+#include "contrib/tolower.h"
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+bool dnssec_keyid_is_valid(const char *id)
+{
+ if (!id) {
+ return false;
+ }
+
+ if (strlen(id) % 2 != 0) {
+ return false;
+ }
+
+ for (int i = 0; id[i] != '\0'; i++) {
+ if (!is_xdigit(id[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+_public_
+void dnssec_keyid_normalize(char *id)
+{
+ if (!id) {
+ return;
+ }
+
+ for (size_t i = 0; id[i] != '\0'; i++) {
+ assert(id[i] != '\0' && is_xdigit(id[i]));
+ id[i] = knot_tolower(id[i]);
+ }
+}
+
+_public_
+char *dnssec_keyid_copy(const char *id)
+{
+ if (!id) {
+ return NULL;
+ }
+
+ char *copy = strdup(id);
+ if (!copy) {
+ return NULL;
+ }
+
+ dnssec_keyid_normalize(copy);
+
+ return copy;
+}
+
+_public_
+bool dnssec_keyid_equal(const char *one, const char *two)
+{
+ if (!one || !two) {
+ return NULL;
+ }
+
+ return (strcasecmp(one, two) == 0);
+}
diff --git a/src/libdnssec/keyid.h b/src/libdnssec/keyid.h
new file mode 100644
index 0000000..c90bb1c
--- /dev/null
+++ b/src/libdnssec/keyid.h
@@ -0,0 +1,64 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup keyid
+ *
+ * \brief DNSSEC key ID manipulation.
+ *
+ * The module contains auxiliary functions for manipulation with key IDs.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/*!
+ * Length of the key ID in presentation form (ASCII).
+ */
+#define DNSSEC_KEYID_SIZE 40
+
+/*!
+ * Length of the key ID in internal form (binary).
+ */
+#define DNSSEC_KEYID_BINARY_SIZE 20
+
+/*!
+ * Check if a provided string is a valid key ID string.
+ */
+bool dnssec_keyid_is_valid(const char *id);
+
+/*!
+ * Normalize the key ID string.
+ */
+void dnssec_keyid_normalize(char *id);
+
+/*!
+ * Create a normalized copy if the key ID.
+ */
+char *dnssec_keyid_copy(const char *id);
+
+/*!
+ * Check if two key IDs are equal.
+ */
+bool dnssec_keyid_equal(const char *one, const char *two);
+
+/*! @} */
diff --git a/src/libdnssec/keystore.h b/src/libdnssec/keystore.h
new file mode 100644
index 0000000..8697935
--- /dev/null
+++ b/src/libdnssec/keystore.h
@@ -0,0 +1,155 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup keystore
+ *
+ * \brief Private key store access.
+ *
+ * The module provides abstraction for private key store. Basically, PKCS #8
+ * and PKCS #11 interfaces are supported.
+ *
+ * PKCS #8 uses unencrypted PEM.
+ *
+ * PKCS #11 provides access Hardware Security Modules.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <libdnssec/binary.h>
+#include <libdnssec/key.h>
+
+struct dnssec_keystore;
+
+/*!
+ * DNSSEC private keys store.
+ */
+typedef struct dnssec_keystore dnssec_keystore_t;
+
+/*!
+ * Create default PKCS #8 private key store context.
+ *
+ * The default store maintains the private keys in one directory on the file
+ * system. The private keys are stored in unencrypted PEM format, named
+ * key-id.pem. The configuration string is a path to the directory.
+ *
+ * \param[out] store Opened key store.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_init_pkcs8(dnssec_keystore_t **store);
+
+/*!
+ * Crate new PKCS #11 private key store context.
+ *
+ * \param[out] store Opened key store.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store);
+
+/*!
+ * Deinitialize private key store context.
+ *
+ * \param store Key store to be deinitialized.
+ */
+int dnssec_keystore_deinit(dnssec_keystore_t *store);
+
+/*!
+ * Initialize new private key store.
+ */
+int dnssec_keystore_init(dnssec_keystore_t *store, const char *config);
+
+/*!
+ * Open private key store.
+ */
+int dnssec_keystore_open(dnssec_keystore_t *store, const char *config);
+
+/*!
+ * Close private key store.
+ *
+ * \param store Key store to be closed.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_close(dnssec_keystore_t *store);
+
+/*!
+ * Generate a new key in the key store.
+ *
+ * \param[in] store Key store.
+ * \param[in] algorithm Algorithm.
+ * \param[in] bits Bit length of the key to be generated.
+ * \param[in] label Optional key label for PKCS #11.
+ * \param[out] id_ptr ID of the generated key. Must be freed by the caller.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_generate(dnssec_keystore_t *store,
+ dnssec_key_algorithm_t algorithm,
+ unsigned bits, const char *label, char **id_ptr);
+
+/*!
+ * Import an existing key into the key store.
+ *
+ * \param[in] store Key store.
+ * \param[in] pem Private key material in PEM format.
+ * \param[out] id_ptr ID of the imported key. Must be freed by the caller.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_import(dnssec_keystore_t *store, const dnssec_binary_t *pem,
+ char **id_ptr);
+
+/*!
+ * Remove a private key from the key store.
+ *
+ * \param store Key store.
+ * \param id ID of the private key to be deleted.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_remove(dnssec_keystore_t *store, const char *id);
+
+/*!
+ * Export private key from the key store into a DNSSEC key.
+ *
+ * The key algorithm has to be set before calling this function.
+ *
+ * \param store Private key store.
+ * \param id ID of the key.
+ * \param key DNSSEC key to be initialized.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_get_private(dnssec_keystore_t *store, const char *id,
+ dnssec_key_t *key);
+
+/*!
+ * Import a DNSSEC private key into key store.
+ *
+ * \param store Key store.
+ * \param key DNSSEC key with a private key.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_keystore_set_private(dnssec_keystore_t *store, dnssec_key_t *key);
+
+/*! @} */
diff --git a/src/libdnssec/keystore/internal.h b/src/libdnssec/keystore/internal.h
new file mode 100644
index 0000000..5afa8ce
--- /dev/null
+++ b/src/libdnssec/keystore/internal.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/key.h"
+#include "libdnssec/keystore.h"
+
+typedef struct keystore_functions {
+ // construction of internal context
+ int (*ctx_new)(void **ctx_ptr);
+ void (*ctx_free)(void *ctx);
+ // keystore init/open/close
+ int (*init)(void *ctx, const char *config);
+ int (*open)(void *ctx, const char *config);
+ int (*close)(void *ctx);
+ // keystore access
+ int (*generate_key)(void *ctx, gnutls_pk_algorithm_t algorithm,
+ unsigned bits, const char *label, char **id_ptr);
+ int (*import_key)(void *ctx, const dnssec_binary_t *pem, char **id_ptr);
+ int (*remove_key)(void *ctx, const char *id);
+ // private key access
+ int (*get_private)(void *ctx, const char *id, gnutls_privkey_t *key_ptr);
+ int (*set_private)(void *ctx, gnutls_privkey_t key);
+} keystore_functions_t;
+
+struct dnssec_keystore {
+ const keystore_functions_t *functions;
+ void *ctx;
+};
+
+int keystore_create(dnssec_keystore_t **store_ptr,
+ const keystore_functions_t *functions);
diff --git a/src/libdnssec/keystore/keystore.c b/src/libdnssec/keystore/keystore.c
new file mode 100644
index 0000000..f88dd14
--- /dev/null
+++ b/src/libdnssec/keystore/keystore.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdlib.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/algorithm.h"
+#include "libdnssec/key/dnskey.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/key/privkey.h"
+#include "libdnssec/keyid.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/keystore/internal.h"
+#include "libdnssec/shared/shared.h"
+
+/* -- internal API --------------------------------------------------------- */
+
+int keystore_create(dnssec_keystore_t **store_ptr,
+ const keystore_functions_t *functions)
+{
+ assert(store_ptr);
+ assert(functions);
+
+ dnssec_keystore_t *store = calloc(1, sizeof(*store));
+ if (!store) {
+ return DNSSEC_ENOMEM;
+ }
+
+ store->functions = functions;
+
+ int result = functions->ctx_new(&store->ctx);
+ if (result != DNSSEC_EOK) {
+ free(store);
+ return DNSSEC_ENOMEM;
+ }
+
+ *store_ptr = store;
+ return DNSSEC_EOK;
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_keystore_deinit(dnssec_keystore_t *store)
+{
+ if (!store) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_keystore_close(store);
+ store->functions->ctx_free(store->ctx);
+
+ free(store);
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_keystore_init(dnssec_keystore_t *store, const char *config)
+{
+ if (!store) {
+ return DNSSEC_EINVAL;
+ }
+
+ return store->functions->init(store->ctx, config);
+}
+
+_public_
+int dnssec_keystore_open(dnssec_keystore_t *store, const char *config)
+{
+ if (!store) {
+ return DNSSEC_EINVAL;
+ }
+
+ return store->functions->open(store->ctx, config);
+}
+
+_public_
+int dnssec_keystore_close(dnssec_keystore_t *store)
+{
+ if (!store) {
+ return DNSSEC_EINVAL;
+ }
+
+ return store->functions->close(store->ctx);
+}
+
+_public_
+int dnssec_keystore_generate(dnssec_keystore_t *store,
+ dnssec_key_algorithm_t _algorithm,
+ unsigned bits, const char *label, char **id_ptr)
+{
+ if (!store || !_algorithm || !id_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ // prepare parameters
+
+ gnutls_pk_algorithm_t algorithm = algorithm_to_gnutls(_algorithm);
+ if (algorithm == GNUTLS_PK_UNKNOWN) {
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ if (!dnssec_algorithm_key_size_check(_algorithm, bits)) {
+ return DNSSEC_INVALID_KEY_SIZE;
+ }
+
+ return store->functions->generate_key(store->ctx, algorithm, bits, label, id_ptr);
+}
+
+_public_
+int dnssec_keystore_import(dnssec_keystore_t *store, const dnssec_binary_t *pem,
+ char **id_ptr)
+{
+ if (!store || !pem || !id_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ return store->functions->import_key(store->ctx, pem, id_ptr);
+}
+
+_public_
+int dnssec_keystore_remove(dnssec_keystore_t *store, const char *id)
+{
+ if (!store || !id) {
+ return DNSSEC_EINVAL;
+ }
+
+ return store->functions->remove_key(store->ctx, id);
+}
+
+_public_
+int dnssec_keystore_get_private(dnssec_keystore_t *store, const char *id,
+ dnssec_key_t *key)
+{
+ if (!store || !id || dnssec_key_get_algorithm(key) == 0 || !key) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (key->private_key) {
+ return DNSSEC_KEY_ALREADY_PRESENT;
+ }
+
+ gnutls_privkey_t privkey = NULL;
+ int r = store->functions->get_private(store->ctx, id, &privkey);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = key_set_private_key(key, privkey);
+ if (r != DNSSEC_EOK) {
+ gnutls_privkey_deinit(privkey);
+ return r;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_keystore_set_private(dnssec_keystore_t *store, dnssec_key_t *key)
+{
+ if (!store || !key) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (!key->private_key) {
+ return DNSSEC_NO_PRIVATE_KEY;
+ }
+
+ return store->functions->set_private(store->ctx, key->private_key);
+}
diff --git a/src/libdnssec/keystore/pkcs11.c b/src/libdnssec/keystore/pkcs11.c
new file mode 100644
index 0000000..2b8b6d5
--- /dev/null
+++ b/src/libdnssec/keystore/pkcs11.c
@@ -0,0 +1,397 @@
+/* Copyright (C) 2022 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 <gnutls/gnutls.h>
+#include <pthread.h>
+
+#include "contrib/string.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keyid.h"
+#include "libdnssec/shared/keyid_gnutls.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/keystore/internal.h"
+#include "libdnssec/p11/p11.h"
+#include "libdnssec/pem.h"
+#include "libdnssec/shared/shared.h"
+
+#ifdef ENABLE_PKCS11
+
+struct pkcs11_ctx {
+ char *url;
+};
+
+typedef struct pkcs11_ctx pkcs11_ctx_t;
+
+/*!
+ * Flags used when generating/import key into the token.
+ */
+static const int TOKEN_ADD_FLAGS = GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE
+ | GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE;
+
+static int key_url(const char *token_uri, const char *key_id, char **url_ptr)
+{
+ assert(token_uri);
+ assert(key_id);
+ assert(url_ptr);
+
+ if (!dnssec_keyid_is_valid(key_id)) {
+ return DNSSEC_INVALID_KEY_ID;
+ }
+
+ size_t token_len = strlen(token_uri);
+ size_t id_len = strlen(key_id);
+
+ // url: <token-url>;id=%aa%bb%cc..
+
+ size_t len = token_len + 4 + (id_len / 2 * 3);
+ char *url = malloc(len + 1);
+ if (!url) {
+ return DNSSEC_ENOMEM;
+ }
+
+ size_t prefix = snprintf(url, len, "%s;id=", token_uri);
+ if (prefix != token_len + 4) {
+ free(url);
+ return DNSSEC_ENOMEM;
+ }
+
+ assert(id_len % 2 == 0);
+ char *pos = url + prefix;
+ for (int i = 0; i < id_len; i += 2, pos += 3) {
+ pos[0] = '%';
+ pos[1] = key_id[i];
+ pos[2] = key_id[i+1];
+ }
+ assert(url + len == pos);
+ url[len] = '\0';
+
+ *url_ptr = url;
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Parse configuration string. Accepted format: "<pkcs11-url> <module-path>"
+ */
+static int parse_config(const char *config, char **uri_ptr, char **module_ptr)
+{
+ const char *space = strchr(config, ' ');
+ if (!space) {
+ return DNSSEC_KEYSTORE_INVALID_CONFIG;
+ }
+
+ char *url = strndup(config, space - config);
+ char *module = strdup(space + 1);
+
+ if (!url || !module) {
+ free(url);
+ free(module);
+ return DNSSEC_ENOMEM;
+ }
+
+ *uri_ptr = url;
+ *module_ptr = module;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Load PKCS #11 module and check if the token is available.
+ */
+static int safe_open(const char *config, char **url_ptr)
+{
+ char *url = NULL;
+ char *module = NULL;
+
+ int r = parse_config(config, &url, &module);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = p11_load_module(module);
+ free(module);
+ if (r != GNUTLS_E_SUCCESS) {
+ free(url);
+ return DNSSEC_P11_FAILED_TO_LOAD_MODULE;
+ }
+
+ unsigned int flags = 0;
+ r = gnutls_pkcs11_token_get_flags(url, &flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ free(url);
+ return DNSSEC_P11_TOKEN_NOT_AVAILABLE;
+ }
+
+ *url_ptr = url;
+
+ return DNSSEC_EOK;
+}
+
+/* -- internal API --------------------------------------------------------- */
+
+static void disable_pkcs11_callbacks(void)
+{
+ gnutls_pkcs11_set_pin_function(NULL, NULL);
+ gnutls_pkcs11_set_token_function(NULL, NULL);
+}
+
+static int pkcs11_ctx_new(void **ctx_ptr)
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, disable_pkcs11_callbacks);
+
+ pkcs11_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ return DNSSEC_ENOMEM;
+ }
+
+ *ctx_ptr = ctx;
+
+ return DNSSEC_EOK;
+}
+
+static void pkcs11_ctx_free(void *ctx)
+{
+ free(ctx);
+}
+
+static int pkcs11_init(void *ctx, const char *config)
+{
+ /*
+ * Current keystore initialization is idempotent. We don't really
+ * initialize the token because don't want to wipe the data. We just
+ * check that the token is available the same way pkcs11_open() does.
+ */
+
+ _cleanup_free_ char *url = NULL;
+
+ return safe_open(config, &url);
+}
+
+static int pkcs11_open(void *_ctx, const char *config)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ return safe_open(config, &ctx->url);
+}
+
+static int pkcs11_close(void *_ctx)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ free(ctx->url);
+ clear_struct(ctx);
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs11_generate_key(void *_ctx, gnutls_pk_algorithm_t algorithm,
+ unsigned bits, const char *label, char **id_ptr)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ uint8_t buf[20] = { 0 };
+ gnutls_rnd(GNUTLS_RND_RANDOM, buf, sizeof(buf));
+ dnssec_binary_t cka_id = { .data = buf, .size = sizeof(buf) };
+
+ int flags = TOKEN_ADD_FLAGS | GNUTLS_PKCS11_OBJ_FLAG_LOGIN;
+ gnutls_datum_t gt_cka_id = binary_to_datum(&cka_id);
+ int r = gnutls_pkcs11_privkey_generate3(ctx->url, algorithm, bits, label,
+ &gt_cka_id, 0, NULL, 0, flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_GENERATE_ERROR;
+ }
+
+ char *id = bin_to_hex(cka_id.data, cka_id.size, false);
+ if (id == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ *id_ptr = id;
+
+ return DNSSEC_EOK;
+}
+
+static int import_pem(const dnssec_binary_t *pem,
+ gnutls_x509_privkey_t *key_ptr,
+ gnutls_pubkey_t *pubkey_ptr)
+{
+ gnutls_x509_privkey_t x509_key = NULL;
+ gnutls_privkey_t key = NULL;
+ gnutls_pubkey_t pubkey = NULL;
+
+ int r = dnssec_pem_to_x509(pem, &x509_key);
+ if (r != DNSSEC_EOK) {
+ goto fail;
+ }
+
+ if (gnutls_privkey_init(&key) != GNUTLS_E_SUCCESS ||
+ gnutls_pubkey_init(&pubkey) != GNUTLS_E_SUCCESS
+ ) {
+ r = DNSSEC_ENOMEM;
+ goto fail;
+ }
+
+ if (gnutls_privkey_import_x509(key, x509_key, 0) != GNUTLS_E_SUCCESS ||
+ gnutls_pubkey_import_privkey(pubkey, key, 0, 0) != GNUTLS_E_SUCCESS
+ ) {
+ r = DNSSEC_KEY_IMPORT_ERROR;
+ goto fail;
+ }
+
+fail:
+ gnutls_privkey_deinit(key);
+
+ if (r == DNSSEC_EOK) {
+ *key_ptr = x509_key;
+ *pubkey_ptr = pubkey;
+ } else {
+ gnutls_x509_privkey_deinit(x509_key);
+ gnutls_pubkey_deinit(pubkey);
+ }
+
+ return r;
+}
+
+static int pkcs11_import_key(void *_ctx, const dnssec_binary_t *pem, char **id_ptr)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ _cleanup_pubkey_ gnutls_pubkey_t pubkey = NULL;
+
+ int r = import_pem(pem, &key, &pubkey);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ _cleanup_binary_ dnssec_binary_t id = { 0 };
+ r = keyid_x509(key, &id);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ int flags = TOKEN_ADD_FLAGS | GNUTLS_PKCS11_OBJ_FLAG_LOGIN;
+ gnutls_datum_t gid = binary_to_datum(&id);
+
+ r = gnutls_pkcs11_copy_x509_privkey2(ctx->url, key, NULL, &gid, 0, flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ r = gnutls_pkcs11_copy_pubkey(ctx->url, pubkey, NULL, &gid, 0, flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ // note, we result with dangling private key in the token
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ *id_ptr = bin_to_hex(id.data, id.size, false);
+ if (*id_ptr == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs11_remove_key(void *_ctx, const char *id)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ _cleanup_free_ char *url = NULL;
+ int r = key_url(ctx->url, id, &url);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = gnutls_pkcs11_delete_url(url, GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
+ if (r < 0) {
+ return DNSSEC_ERROR;
+ } else if (r == 0) {
+ return DNSSEC_ENOENT;
+ }
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs11_get_private(void *_ctx, const char *id, gnutls_privkey_t *key_ptr)
+{
+ pkcs11_ctx_t *ctx = _ctx;
+
+ _cleanup_free_ char *url = NULL;
+ int r = key_url(ctx->url, id, &url);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ gnutls_privkey_t key = NULL;
+ r = gnutls_privkey_init(&key);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ r = gnutls_privkey_import_pkcs11_url(key, url);
+ if (r != GNUTLS_E_SUCCESS) {
+ gnutls_privkey_deinit(key);
+ return DNSSEC_NOT_FOUND;
+ }
+
+ *key_ptr = key;
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs11_set_private(void *ctx, gnutls_privkey_t key)
+{
+ _cleanup_binary_ dnssec_binary_t pem = { 0 };
+ int r = dnssec_pem_from_privkey(key, &pem);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ _cleanup_free_ char *keyid = NULL;
+
+ return pkcs11_import_key(ctx, &pem, &keyid);
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store_ptr)
+{
+ static const keystore_functions_t IMPLEMENTATION = {
+ .ctx_new = pkcs11_ctx_new,
+ .ctx_free = pkcs11_ctx_free,
+ .init = pkcs11_init,
+ .open = pkcs11_open,
+ .close = pkcs11_close,
+ .generate_key = pkcs11_generate_key,
+ .import_key = pkcs11_import_key,
+ .remove_key = pkcs11_remove_key,
+ .get_private = pkcs11_get_private,
+ .set_private = pkcs11_set_private,
+ };
+
+ return keystore_create(store_ptr, &IMPLEMENTATION);
+}
+
+#else // !ENABLE_PKCS11
+
+_public_
+int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store_ptr)
+{
+ return DNSSEC_NOT_IMPLEMENTED_ERROR;
+}
+
+#endif
diff --git a/src/libdnssec/keystore/pkcs8.c b/src/libdnssec/keystore/pkcs8.c
new file mode 100644
index 0000000..047bde8
--- /dev/null
+++ b/src/libdnssec/keystore/pkcs8.c
@@ -0,0 +1,497 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "contrib/files.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/keystore/internal.h"
+#include "libdnssec/pem.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/keyid_gnutls.h"
+
+#define DIR_INIT_MODE 0750
+
+/*!
+ * Context for PKCS #8 key directory.
+ */
+typedef struct {
+ char *dir_name;
+} pkcs8_dir_handle_t;
+
+/* -- internal functions --------------------------------------------------- */
+
+/*!
+ * Get path to a private key in PKCS #8 PEM format.
+ */
+static char *key_path(const char *dir, const char *id)
+{
+ char *strp = NULL;
+
+ int ret = asprintf(&strp, "%s/%s.pem", dir, id);
+ if (ret < 0) {
+ return NULL;
+ }
+ return strp;
+}
+
+/*!
+ * Get size of the file and reset the position to the beginning of the file.
+ */
+static int file_size(int fd, size_t *size)
+{
+ off_t offset = lseek(fd, 0, SEEK_END);
+ if (offset == -1) {
+ return dnssec_errno_to_error(errno);
+ }
+
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ return dnssec_errno_to_error(errno);
+ }
+
+ assert(offset >= 0);
+ *size = offset;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Open a key file and get the descriptor.
+ */
+static int key_open(const char *dir_name, const char *id, int flags,
+ mode_t mode, int *fd_ptr)
+{
+ assert(dir_name);
+ assert(id);
+ assert(fd_ptr);
+
+ _cleanup_free_ char *filename = key_path(dir_name, id);
+ if (!filename) {
+ return DNSSEC_ENOMEM;
+ }
+
+ int fd = open(filename, flags, mode);
+ if (fd == -1) {
+ return dnssec_errno_to_error(errno);
+ }
+
+ *fd_ptr = fd;
+
+ return DNSSEC_EOK;
+}
+
+static int key_open_read(const char *dir_name, const char *id, int *fd_ptr)
+{
+ return key_open(dir_name, id, O_RDONLY, 0, fd_ptr);
+}
+
+static int key_open_write(const char *dir_name, const char *id, int *fd_ptr)
+{
+ return key_open(dir_name, id, O_WRONLY|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR|S_IRGRP, fd_ptr);
+}
+
+static int pkcs8_dir_read(pkcs8_dir_handle_t *handle, const char *id, dnssec_binary_t *pem)
+{
+ if (!handle || !id || !pem) {
+ return DNSSEC_EINVAL;
+ }
+
+ // open file and get it's size
+
+ _cleanup_close_ int file = -1;
+ int result = key_open_read(handle->dir_name, id, &file);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ size_t size = 0;
+ result = file_size(file, &size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ if (size == 0) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ // read the stored data
+
+ dnssec_binary_t read_pem = { 0 };
+ result = dnssec_binary_alloc(&read_pem, size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ ssize_t read_count = read(file, read_pem.data, read_pem.size);
+ if (read_count == -1) {
+ dnssec_binary_free(&read_pem);
+ return dnssec_errno_to_error(errno);
+ }
+
+ assert(read_count == read_pem.size);
+ *pem = read_pem;
+
+ return DNSSEC_EOK;
+}
+
+static bool key_is_duplicate(int open_error, pkcs8_dir_handle_t *handle,
+ const char *id, const dnssec_binary_t *pem)
+{
+ assert(handle);
+ assert(id);
+ assert(pem);
+
+ if (open_error != dnssec_errno_to_error(EEXIST)) {
+ return false;
+ }
+
+ _cleanup_binary_ dnssec_binary_t old = { 0 };
+ int r = pkcs8_dir_read(handle, id, &old);
+ if (r != DNSSEC_EOK) {
+ return false;
+ }
+
+ return dnssec_binary_cmp(&old, pem) == 0;
+}
+
+static int pem_generate(gnutls_pk_algorithm_t algorithm, unsigned bits,
+ dnssec_binary_t *pem, char **id)
+{
+ assert(pem);
+ assert(id);
+
+ // generate key
+
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ int r = gnutls_x509_privkey_init(&key);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ r = gnutls_x509_privkey_generate(key, algorithm, bits, 0);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_GENERATE_ERROR;
+ }
+
+ // convert to PEM and export the ID
+
+ dnssec_binary_t _pem = { 0 };
+ r = dnssec_pem_from_x509(key, &_pem);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ // export key ID
+
+ char *_id = NULL;
+ r = keyid_x509_hex(key, &_id);
+ if (r != DNSSEC_EOK) {
+ dnssec_binary_free(&_pem);
+ return r;
+ }
+
+ *id = _id;
+ *pem = _pem;
+
+ return DNSSEC_EOK;
+}
+
+/* -- internal API --------------------------------------------------------- */
+
+static int pkcs8_ctx_new(void **ctx_ptr)
+{
+ if (!ctx_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ return DNSSEC_ENOMEM;
+ }
+
+ *ctx_ptr = ctx;
+
+ return DNSSEC_EOK;
+}
+
+static void pkcs8_ctx_free(void *ctx)
+{
+ free(ctx);
+}
+
+static int pkcs8_init(void *ctx, const char *config)
+{
+ if (!ctx || !config) {
+ return DNSSEC_EINVAL;
+ }
+
+ return make_dir(config, DIR_INIT_MODE, true);
+}
+
+static int pkcs8_open(void *ctx, const char *config)
+{
+ if (!ctx || !config) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ char *path = realpath(config, NULL);
+ if (!path) {
+ return DNSSEC_NOT_FOUND;
+ }
+
+ handle->dir_name = path;
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_close(void *ctx)
+{
+ if (!ctx) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ free(handle->dir_name);
+ memset(handle, 0, sizeof(*handle));
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_generate_key(void *ctx, gnutls_pk_algorithm_t algorithm,
+ unsigned bits, const char *label, char **id_ptr)
+{
+ if (!ctx || !id_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ (void)label;
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ // generate key
+
+ char *id = NULL;
+ _cleanup_binary_ dnssec_binary_t pem = { 0 };
+ int r = pem_generate(algorithm, bits, &pem, &id);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ // create the file
+
+ _cleanup_close_ int file = -1;
+ r = key_open_write(handle->dir_name, id, &file);
+ if (r != DNSSEC_EOK) {
+ if (key_is_duplicate(r, handle, id, &pem)) {
+ return DNSSEC_EOK;
+ }
+ return r;
+ }
+
+ // write the data
+
+ ssize_t wrote_count = write(file, pem.data, pem.size);
+ if (wrote_count == -1) {
+ return dnssec_errno_to_error(errno);
+ }
+
+ assert(wrote_count == pem.size);
+
+ // finish
+
+ *id_ptr = id;
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_import_key(void *ctx, const dnssec_binary_t *pem, char **id_ptr)
+{
+ if (!ctx || !pem || !id_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ // retrieve key ID
+
+ char *id = NULL;
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ int r = dnssec_pem_to_x509(pem, &key);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = keyid_x509_hex(key, &id);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ // create the file
+
+ _cleanup_close_ int file = -1;
+ r = key_open_write(handle->dir_name, id, &file);
+ if (r != DNSSEC_EOK) {
+ if (key_is_duplicate(r, handle, id, pem)) {
+ *id_ptr = id;
+ return DNSSEC_EOK;
+ }
+ free(id);
+ return r;
+ }
+
+ // write the data
+
+ ssize_t wrote_count = write(file, pem->data, pem->size);
+ if (wrote_count == -1) {
+ free(id);
+ return dnssec_errno_to_error(errno);
+ }
+
+ assert(wrote_count == pem->size);
+
+ // finish
+
+ *id_ptr = id;
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_remove_key(void *ctx, const char *id)
+{
+ if (!ctx || !id) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ _cleanup_free_ char *filename = key_path(handle->dir_name, id);
+ if (!filename) {
+ return DNSSEC_ENOMEM;
+ }
+
+ if (unlink(filename) == -1) {
+ return dnssec_errno_to_error(errno);
+ }
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_get_private(void *ctx, const char *id, gnutls_privkey_t *key_ptr)
+{
+ if (!ctx || !id || !key_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ pkcs8_dir_handle_t *handle = ctx;
+
+ // load private key data
+
+ _cleanup_close_ int file = -1;
+ int r = key_open_read(handle->dir_name, id, &file);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ size_t size = 0;
+ r = file_size(file, &size);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ if (size == 0) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ // read the stored data
+
+ _cleanup_binary_ dnssec_binary_t pem = { 0 };
+ r = dnssec_binary_alloc(&pem, size);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ ssize_t read_count = read(file, pem.data, pem.size);
+ if (read_count == -1) {
+ dnssec_binary_free(&pem);
+ return dnssec_errno_to_error(errno);
+ }
+
+ assert(read_count == pem.size);
+
+ // construct the key
+
+ gnutls_privkey_t key = NULL;
+ r = dnssec_pem_to_privkey(&pem, &key);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ // finish
+
+ *key_ptr = key;
+
+ return DNSSEC_EOK;
+}
+
+static int pkcs8_set_private(void *ctx, gnutls_privkey_t key)
+{
+ if (!ctx) {
+ return DNSSEC_EINVAL;
+ }
+
+ _cleanup_binary_ dnssec_binary_t pem = { 0 };
+ int r = dnssec_pem_from_privkey(key, &pem);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ _cleanup_free_ char *keyid = NULL;
+
+ return pkcs8_import_key(ctx, &pem, &keyid);
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_keystore_init_pkcs8(dnssec_keystore_t **store_ptr)
+{
+ static const keystore_functions_t IMPLEMENTATION = {
+ .ctx_new = pkcs8_ctx_new,
+ .ctx_free = pkcs8_ctx_free,
+ .init = pkcs8_init,
+ .open = pkcs8_open,
+ .close = pkcs8_close,
+ .generate_key = pkcs8_generate_key,
+ .import_key = pkcs8_import_key,
+ .remove_key = pkcs8_remove_key,
+ .get_private = pkcs8_get_private,
+ .set_private = pkcs8_set_private,
+ };
+
+ return keystore_create(store_ptr, &IMPLEMENTATION);
+}
diff --git a/src/libdnssec/keytag.h b/src/libdnssec/keytag.h
new file mode 100644
index 0000000..7915970
--- /dev/null
+++ b/src/libdnssec/keytag.h
@@ -0,0 +1,44 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup keytag
+ *
+ * \brief Low-level key tag computation API.
+ *
+ * The module provides simple interface for DNSKEY key id computation.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <libdnssec/binary.h>
+
+/*!
+ * Compute a key tag for a DNSSEC key.
+ *
+ * \param[in] rdata DNSKEY RDATA.
+ * \param[out] keytag Computed keytag.
+ *
+ * \return Error code, DNSSEC_EOK of successful.
+ */
+int dnssec_keytag(const dnssec_binary_t *rdata, uint16_t *keytag);
+
+/*! @} */
diff --git a/src/libdnssec/nsec.h b/src/libdnssec/nsec.h
new file mode 100644
index 0000000..19808b0
--- /dev/null
+++ b/src/libdnssec/nsec.h
@@ -0,0 +1,155 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup nsec
+ *
+ * \brief NSEC bitmap and NSEC3 hash computation API.
+ *
+ * The module provides interface for computation of NSEC3 hashes and for
+ * construction of bit maps used in NSEC and NSEC3 records.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <libdnssec/binary.h>
+
+/*!
+ * DNSSEC NSEC3 algorithm numbers.
+ */
+typedef enum dnssec_nsec_algorithm {
+ DNSSEC_NSEC3_ALGORITHM_UNKNOWN = 0,
+ DNSSEC_NSEC3_ALGORITHM_SHA1 = 1,
+} dnssec_nsec3_algorithm_t;
+
+/*!
+ * DNSSEC NSEC3 parameters.
+ */
+typedef struct dnssec_nsec3_params {
+ dnssec_nsec3_algorithm_t algorithm; /*!< NSEC3 algorithm. */
+ uint8_t flags; /*!< NSEC3 flags. */
+ uint16_t iterations; /*!< NSEC3 iterations count. */
+ dnssec_binary_t salt; /*!< NSEC3 salt. */
+} dnssec_nsec3_params_t;
+
+/*!
+ * Free NSEC3 parameters.
+ */
+void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params);
+
+/*!
+ * Parse NSEC3 parameters from NSEC3PARAM RDATA.
+ *
+ * \param params Output parameters.
+ * \param rdata NSEC3PARAM RDATA.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
+ const dnssec_binary_t *rdata);
+
+/*!
+ * Check if NSEC3 parameters match.
+ *
+ * \param params1 NSEC3 parameters 1.
+ * \param params2 NSEC3 parameters 2.
+ *
+ * \return True if match or if both NULL.
+ */
+bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1,
+ const dnssec_nsec3_params_t *params2);
+
+/*!
+ * Check whether a given NSEC bitmap contains a given RR type.
+ *
+ * \param bitmap Bitmap of an NSEC record.
+ * \param size Size of the bitmap.
+ * \param type RR type to check for.
+ *
+ * \return true if bitmap contains type, false otherwise.
+ */
+bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type);
+
+/*!
+ * Compute NSEC3 hash for given data.
+ *
+ * \todo Input data must be converted to lowercase!
+ *
+ * \param[in] data Data to be hashed (usually domain name).
+ * \param[in] params NSEC3 parameters.
+ * \param[out] hash Computed hash (will be allocated or resized).
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_nsec3_hash(const dnssec_binary_t *data,
+ const dnssec_nsec3_params_t *params,
+ dnssec_binary_t *hash);
+
+/*!
+ * Get length of raw NSEC3 hash for a given algorithm.
+ *
+ * \param algorithm NSEC3 algorithm number.
+ *
+ * \return Length of raw NSEC3 hash, zero on error.
+ */
+size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm);
+
+struct dnssec_nsec_bitmap;
+
+/*!
+ * Context for encoding of RR types bitmap used in NSEC/NSEC3.
+ */
+typedef struct dnssec_nsec_bitmap dnssec_nsec_bitmap_t;
+
+/*!
+ * Allocate new bit map encoding context.
+ */
+dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void);
+
+/*!
+ * Clear existing bit map encoding context.
+ */
+void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Free bit map encoding context.
+ */
+void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Add one RR type into the bitmap.
+ */
+void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type);
+
+/*!
+ * Compute the size of the encoded bitmap.
+ */
+size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap);
+
+/*!
+ * Write encoded bitmap into the given buffer.
+ */
+void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output);
+
+/*! @} */
diff --git a/src/libdnssec/nsec/bitmap.c b/src/libdnssec/nsec/bitmap.c
new file mode 100644
index 0000000..05f8cfa
--- /dev/null
+++ b/src/libdnssec/nsec/bitmap.c
@@ -0,0 +1,142 @@
+/* Copyright (C) 2018 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 <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+
+#define BITMAP_WINDOW_SIZE 256
+#define BITMAP_WINDOW_BYTES (BITMAP_WINDOW_SIZE/CHAR_BIT)
+#define BITMAP_WINDOW_COUNT 256
+
+/*!
+ * One window of an NSEC bitmap.
+ */
+typedef struct window {
+ uint8_t used;
+ uint8_t data[BITMAP_WINDOW_BYTES];
+} window_t;
+
+struct dnssec_nsec_bitmap {
+ int used;
+ window_t windows[BITMAP_WINDOW_COUNT];
+};
+
+/* -- public API ----------------------------------------------------------- */
+
+/*!
+ * Allocate new bit map encoding context.
+ */
+_public_
+dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void)
+{
+ dnssec_nsec_bitmap_t *bitmap = malloc(sizeof(*bitmap));
+ if (!bitmap) {
+ return NULL;
+ }
+
+ dnssec_nsec_bitmap_clear(bitmap);
+
+ return bitmap;
+}
+
+/*!
+ * Clear existing bit map encoding context.
+ */
+_public_
+void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap)
+{
+ clear_struct(bitmap);
+}
+
+/*!
+ * Free bit map encoding context.
+ */
+_public_
+void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap)
+{
+ free(bitmap);
+}
+
+/*!
+ * Add one RR type into the bitmap.
+ */
+_public_
+void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type)
+{
+ int win = type / BITMAP_WINDOW_SIZE;
+ int bit = type % BITMAP_WINDOW_SIZE;
+
+ if (bitmap->used <= win) {
+ bitmap->used = win + 1;
+ }
+
+ int win_byte = bit / CHAR_BIT;
+ int win_bit = bit % CHAR_BIT;
+
+ window_t *window = &bitmap->windows[win];
+ window->data[win_byte] |= 0x80 >> win_bit;
+ if (window->used <= win_byte) {
+ window->used = win_byte + 1;
+ }
+}
+
+/*!
+ * Compute the size of the encoded bitmap.
+ */
+_public_
+size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap)
+{
+ size_t result = 0;
+
+ for (int i = 0; i < bitmap->used; i++) {
+ int used = bitmap->windows[i].used;
+ if (used == 0) {
+ continue;
+ }
+
+ result += 2 + used; // windows number, window size, data
+ }
+
+ return result;
+}
+
+/*!
+ * Write encoded bitmap into the given buffer.
+ */
+_public_
+void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output)
+{
+ uint8_t *write_ptr = output;
+ for (int win = 0; win < bitmap->used; win++) {
+ int used = bitmap->windows[win].used;
+ if (used == 0) {
+ continue;
+ }
+
+ *write_ptr = (uint8_t)win;
+ write_ptr += 1;
+
+ *write_ptr = (uint8_t)used;
+ write_ptr += 1;
+
+ memmove(write_ptr, bitmap->windows[win].data, used);
+ write_ptr += used;
+ }
+}
diff --git a/src/libdnssec/nsec/hash.c b/src/libdnssec/nsec/hash.c
new file mode 100644
index 0000000..b5d46ab
--- /dev/null
+++ b/src/libdnssec/nsec/hash.c
@@ -0,0 +1,125 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <string.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+
+/*!
+ * Compute NSEC3 hash for given data and algorithm.
+ *
+ * \see RFC 5155
+ *
+ * \todo Input data should be converted to lowercase.
+ */
+static int nsec3_hash(gnutls_digest_algorithm_t algorithm, int iterations,
+ const dnssec_binary_t *salt, const dnssec_binary_t *data,
+ dnssec_binary_t *hash)
+{
+ assert(salt);
+ assert(data);
+ assert(hash);
+
+ int hash_size = gnutls_hash_get_len(algorithm);
+ if (hash_size <= 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ int result = dnssec_binary_resize(hash, hash_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ _cleanup_hash_ gnutls_hash_hd_t digest = NULL;
+ result = gnutls_hash_init(&digest, algorithm);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ const uint8_t *in = data->data;
+ size_t in_size = data->size;
+
+ for (int i = 0; i <= iterations; i++) {
+ result = gnutls_hash(digest, in, in_size);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ result = gnutls_hash(digest, salt->data, salt->size);
+ if (result < 0) {
+ return DNSSEC_NSEC3_HASHING_ERROR;
+ }
+
+ gnutls_hash_output(digest, hash->data);
+
+ in = hash->data;
+ in_size = hash->size;
+ }
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Get GnuTLS digest algorithm from DNSSEC algorithm number.
+ */
+static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec)
+{
+ switch (dnssec) {
+ case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1;
+ default: return GNUTLS_DIG_UNKNOWN;
+ }
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+/*!
+ * Compute NSEC3 hash for given data.
+ */
+_public_
+int dnssec_nsec3_hash(const dnssec_binary_t *data,
+ const dnssec_nsec3_params_t *params,
+ dnssec_binary_t *hash)
+{
+ if (!data || !params || !hash) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm);
+ if (algorithm == GNUTLS_DIG_UNKNOWN) {
+ return DNSSEC_INVALID_NSEC3_ALGORITHM;
+ }
+
+ return nsec3_hash(algorithm, params->iterations, &params->salt, data, hash);
+}
+
+/*!
+ * Get length of raw NSEC3 hash for a given algorithm.
+ */
+_public_
+size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm)
+{
+ gnutls_digest_algorithm_t gnutls = algorithm_d2g(algorithm);
+ if (gnutls == GNUTLS_DIG_UNKNOWN) {
+ return 0;
+ }
+
+ return gnutls_hash_get_len(gnutls);
+}
diff --git a/src/libdnssec/nsec/nsec.c b/src/libdnssec/nsec/nsec.c
new file mode 100644
index 0000000..2e71598
--- /dev/null
+++ b/src/libdnssec/nsec/nsec.c
@@ -0,0 +1,130 @@
+/* Copyright (C) 2018 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 "libdnssec/nsec.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/shared/binary_wire.h"
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+
+/*!
+ * Free NSEC3 parameters.
+ */
+_public_
+void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params)
+{
+ if (!params) {
+ return;
+ }
+
+ dnssec_binary_free(&params->salt);
+ clear_struct(params);
+}
+
+/*!
+ * Parse NSEC3 parameters from NSEC3PARAM RDATA.
+ *
+ * \see RFC 5155 (section 4.2)
+ */
+_public_
+int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params,
+ const dnssec_binary_t *rdata)
+{
+ if (!params || !rdata || !rdata->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_nsec3_params_t new_params = { 0 };
+
+ wire_ctx_t wire = binary_init(rdata);
+
+ if (wire_ctx_available(&wire) < 5) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ new_params.algorithm = wire_ctx_read_u8(&wire);
+ new_params.flags = wire_ctx_read_u8(&wire);
+ new_params.iterations = wire_ctx_read_u16(&wire);
+ new_params.salt.size = wire_ctx_read_u8(&wire);
+
+ if (wire_ctx_available(&wire) != new_params.salt.size) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ new_params.salt.data = malloc(new_params.salt.size);
+ if (new_params.salt.data == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ binary_read(&wire, &new_params.salt);
+ assert(wire_ctx_offset(&wire) == rdata->size);
+
+ *params = new_params;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type)
+{
+ if (!bitmap || size == 0) {
+ return false;
+ }
+
+ const uint8_t type_hi = (type >> 8); // Which window block contains type.
+ const uint8_t type_lo = (type & 0xff);
+ const uint8_t bitmap_idx = (type_lo >> 3); // Which byte in the window block contains type.
+ const uint8_t bit_mask = 1 << (7 - (type_lo & 0x07)); // Which bit in the byte represents type.
+
+ size_t bitmap_pos = 0;
+ while (bitmap_pos + 3 <= size) {
+ uint8_t block_idx = bitmap[bitmap_pos++]; // Skip window block No.
+ uint8_t block_size = bitmap[bitmap_pos++]; // Skip window block size.
+
+ // Size checks.
+ if (block_size == 0 || bitmap_pos + block_size > size) {
+ return false;
+ }
+
+ // Check whether we found the correct window block.
+ if (block_idx == type_hi) {
+ if (bitmap_idx < block_size) {
+ // Check if the bit for type is set.
+ return bitmap[bitmap_pos + bitmap_idx] & bit_mask;
+ }
+ return false;
+ } else {
+ bitmap_pos += block_size;
+ }
+ }
+
+ return false;
+}
+
+_public_
+bool dnssec_nsec3_params_match(const dnssec_nsec3_params_t *params1,
+ const dnssec_nsec3_params_t *params2)
+{
+ if (params1 != NULL && params2 != NULL) {
+ return (params1->algorithm == params2->algorithm &&
+ params1->flags == params2->flags &&
+ params1->iterations == params2->iterations &&
+ dnssec_binary_cmp(&params1->salt, &params2->salt) == 0);
+ } else {
+ return (params1 == NULL && params2 == NULL);
+ }
+}
diff --git a/src/libdnssec/p11/p11.c b/src/libdnssec/p11/p11.c
new file mode 100644
index 0000000..c3af544
--- /dev/null
+++ b/src/libdnssec/p11/p11.c
@@ -0,0 +1,113 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <gnutls/pkcs11.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libdnssec/p11/p11.h"
+#include "libdnssec/error.h"
+
+#ifdef ENABLE_PKCS11
+
+#define PKCS11_MODULES_MAX 16
+
+static char *pkcs11_modules[PKCS11_MODULES_MAX] = { 0 };
+static int pkcs11_modules_count = 0;
+
+static int map_result(int gnutls_result)
+{
+ return gnutls_result == GNUTLS_E_SUCCESS ? DNSSEC_EOK : DNSSEC_ERROR;
+}
+
+int p11_init(void)
+{
+ int r = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+ return map_result(r);
+}
+
+int p11_reinit(void)
+{
+ int r = gnutls_pkcs11_reinit();
+ return map_result(r);
+}
+
+int p11_load_module(const char *module)
+{
+ for (int i = 0; i < pkcs11_modules_count; i++) {
+ if (strcmp(pkcs11_modules[i], module) == 0) {
+ return DNSSEC_EOK;
+ }
+ }
+
+ assert(pkcs11_modules_count <= PKCS11_MODULES_MAX);
+ if (pkcs11_modules_count == PKCS11_MODULES_MAX) {
+ return DNSSEC_P11_TOO_MANY_MODULES;
+ }
+
+ char *copy = strdup(module);
+ if (!copy) {
+ return DNSSEC_ENOMEM;
+ }
+
+ int r = gnutls_pkcs11_add_provider(module, NULL);
+ if (r != GNUTLS_E_SUCCESS) {
+ free(copy);
+ return DNSSEC_P11_FAILED_TO_LOAD_MODULE;
+ }
+
+ pkcs11_modules[pkcs11_modules_count] = copy;
+ pkcs11_modules_count += 1;
+
+ return DNSSEC_EOK;
+}
+
+void p11_cleanup(void)
+{
+ for (int i = 0; i < pkcs11_modules_count; i++) {
+ free(pkcs11_modules[i]);
+ pkcs11_modules[i] = NULL;
+ }
+
+ pkcs11_modules_count = 0;
+
+ gnutls_pkcs11_deinit();
+}
+
+#else
+
+int p11_init(void)
+{
+ return DNSSEC_EOK;
+}
+
+int p11_reinit(void)
+{
+ return DNSSEC_EOK;
+}
+
+int p11_load_module(const char *module)
+{
+ return DNSSEC_NOT_IMPLEMENTED_ERROR;
+}
+
+void p11_cleanup(void)
+{
+ // this function intentionally left blank
+}
+
+#endif
diff --git a/src/libdnssec/p11/p11.h b/src/libdnssec/p11/p11.h
new file mode 100644
index 0000000..5642a55
--- /dev/null
+++ b/src/libdnssec/p11/p11.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+/*!
+ * Initialize PKCS11 global context.
+ */
+int p11_init(void);
+
+/*!
+ * Reinitialize PKCS11 global context after fork().
+ */
+int p11_reinit(void);
+
+/*!
+ * Load PKCS11 module unless the module was already loaded.
+ *
+ * Duplicates are detected based on the module path.
+ */
+int p11_load_module(const char *name);
+
+/*!
+ * Cleanup PKCS11 global context.
+ *
+ * Should be called when the library is deinitialized to prevent memory leaks.
+ */
+void p11_cleanup(void);
diff --git a/src/libdnssec/pem.c b/src/libdnssec/pem.c
new file mode 100644
index 0000000..fa463f6
--- /dev/null
+++ b/src/libdnssec/pem.c
@@ -0,0 +1,182 @@
+/* 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 <assert.h>
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/pem.h"
+#include "libdnssec/shared/shared.h"
+
+_public_
+int dnssec_pem_to_x509(const dnssec_binary_t *pem, gnutls_x509_privkey_t *key)
+{
+ if (!pem || !key) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_datum_t data = binary_to_datum(pem);
+
+ gnutls_x509_privkey_t _key = NULL;
+ int r = gnutls_x509_privkey_init(&_key);
+ if (r != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ int format = GNUTLS_X509_FMT_PEM;
+ char *password = NULL;
+ int flags = GNUTLS_PKCS_PLAIN;
+ r = gnutls_x509_privkey_import_pkcs8(_key, &data, format, password, flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(_key);
+ return DNSSEC_PKCS8_IMPORT_ERROR;
+ }
+
+ *key = _key;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_pem_to_privkey(const dnssec_binary_t *pem, gnutls_privkey_t *key)
+{
+ if (!pem || !key) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_x509_privkey_t key_x509 = NULL;
+ int r = dnssec_pem_to_x509(pem, &key_x509);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ gnutls_privkey_t key_abs = NULL;
+ r = gnutls_privkey_init(&key_abs);
+ if (r != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(key_x509);
+ return DNSSEC_ENOMEM;
+ }
+
+ int flags = GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE;
+ r = gnutls_privkey_import_x509(key_abs, key_x509, flags);
+ if (r != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(key_x509);
+ gnutls_privkey_deinit(key_abs);
+ return DNSSEC_ENOMEM;
+ }
+
+ *key = key_abs;
+
+ return DNSSEC_EOK;
+}
+
+static int try_export_pem(gnutls_x509_privkey_t key, dnssec_binary_t *pem)
+{
+ assert(key);
+
+ gnutls_x509_crt_fmt_t format = GNUTLS_X509_FMT_PEM;
+ char *password = NULL;
+ int flags = GNUTLS_PKCS_PLAIN;
+
+ return gnutls_x509_privkey_export_pkcs8(key, format, password, flags,
+ pem->data, &pem->size);
+}
+
+_public_
+int dnssec_pem_from_x509(gnutls_x509_privkey_t key, dnssec_binary_t *pem)
+{
+ if (!key || !pem) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_binary_t _pem = { 0 };
+ int r = try_export_pem(key, &_pem);
+ if (r != GNUTLS_E_SHORT_MEMORY_BUFFER || _pem.size == 0) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ r = dnssec_binary_alloc(&_pem, _pem.size);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ r = try_export_pem(key, &_pem);
+ if (r != GNUTLS_E_SUCCESS) {
+ dnssec_binary_free(&_pem);
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ *pem = _pem;
+
+ return DNSSEC_EOK;
+}
+
+static int privkey_export_x509(gnutls_privkey_t key, gnutls_x509_privkey_t *_key)
+{
+#ifdef HAVE_EXPORT_X509
+ if (gnutls_privkey_export_x509(key, _key) != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+#else // Needed for GnuTLS < 3.4.0 (CentOS 7)
+ struct privkey { // Extracted needed items only!
+ gnutls_privkey_type_t type;
+ gnutls_pk_algorithm_t pk_algorithm;
+ gnutls_x509_privkey_t x509;
+ };
+ struct privkey *pkey = (struct privkey *)key;
+
+ assert(pkey->type == GNUTLS_PRIVKEY_X509);
+
+ if (gnutls_x509_privkey_init(_key) != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+
+ if (gnutls_x509_privkey_cpy(*_key, pkey->x509) != GNUTLS_E_SUCCESS) {
+ gnutls_x509_privkey_deinit(*_key);
+ return DNSSEC_KEY_EXPORT_ERROR;
+ }
+#endif
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_pem_from_privkey(gnutls_privkey_t key, dnssec_binary_t *pem)
+{
+ if (!key || !pem) {
+ return DNSSEC_EINVAL;
+ }
+
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t _key = NULL;
+
+ int r = privkey_export_x509(key, &_key);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ dnssec_binary_t _pem = { 0 };
+ r = dnssec_pem_from_x509(_key, &_pem);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ *pem = _pem;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/pem.h b/src/libdnssec/pem.h
new file mode 100644
index 0000000..c84d87d
--- /dev/null
+++ b/src/libdnssec/pem.h
@@ -0,0 +1,73 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup pem
+ *
+ * \brief PEM key format operations.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <gnutls/gnutls.h>
+
+#include <libdnssec/binary.h>
+
+/*!
+ * Create GnuTLS X.509 private key from unencrypted PEM data.
+ *
+ * \param[in] pem PEM binary data.
+ * \param[out] key Resulting private key.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_pem_to_x509(const dnssec_binary_t *pem, gnutls_x509_privkey_t *key);
+
+/*!
+ * Create GnuTLS private key from unencrypted PEM data.
+ *
+ * \param[in] pem PEM binary data.
+ * \param[out] key Resulting private key.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_pem_to_privkey(const dnssec_binary_t *pem, gnutls_privkey_t *key);
+
+/*!
+ * Export GnuTLS X.509 private key to PEM binary.
+ *
+ * \param[in] key Key to be exported.
+ * \param[out] pem Generated key in unencrypted PEM format.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_pem_from_x509(gnutls_x509_privkey_t key, dnssec_binary_t *pem);
+
+/*!
+ * Export GnuTLS private key to PEM binary.
+ *
+ * \param[in] key Key to be exported.
+ * \param[out] pem Generated key in unencrypted PEM format.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_pem_from_privkey(gnutls_privkey_t key, dnssec_binary_t *pem);
+
+/*! @} */
diff --git a/src/libdnssec/random.c b/src/libdnssec/random.c
new file mode 100644
index 0000000..5d6be5d
--- /dev/null
+++ b/src/libdnssec/random.c
@@ -0,0 +1,53 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+#include "libdnssec/shared/shared.h"
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+int dnssec_random_buffer(uint8_t *data, size_t size)
+{
+ if (!data) {
+ return DNSSEC_EINVAL;
+ }
+
+ int result = gnutls_rnd(GNUTLS_RND_RANDOM, data, size);
+ if (result != 0) {
+ assert_unreachable();
+ return DNSSEC_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_random_binary(dnssec_binary_t *binary)
+{
+ if (!binary) {
+ return DNSSEC_EINVAL;
+ }
+
+ return dnssec_random_buffer(binary->data, binary->size);
+}
diff --git a/src/libdnssec/random.h b/src/libdnssec/random.h
new file mode 100644
index 0000000..8d4784f
--- /dev/null
+++ b/src/libdnssec/random.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup random
+ *
+ * \brief Pseudo-random number generating API.
+ *
+ * The module provides generating of pseudo-random numbers and buffers.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <libdnssec/binary.h>
+
+/*!
+ * Fill a buffer with pseudo-random data.
+ *
+ * \param data Pointer to the output buffer.
+ * \param size Size of the output buffer.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_random_buffer(uint8_t *data, size_t size);
+
+/*!
+ * Fill a binary structure with random data.
+ *
+ * \param data Preallocated binary structure to be filled.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_random_binary(dnssec_binary_t *data);
+
+/*!
+ * Declare function dnssec_random_<type>().
+ */
+#define dnssec_register_random_type(type) \
+ static inline type dnssec_random_##type(void) { \
+ type value; \
+ dnssec_random_buffer((uint8_t *)&value, sizeof(value)); \
+ return value; \
+ }
+
+/*!
+ * Generate pseudo-random 16-bit number.
+ */
+static inline uint16_t dnssec_random_uint16_t(void);
+
+/*!
+ * Generate pseudo-random 32-bit number.
+ */
+static inline uint32_t dnssec_random_uint32_t(void);
+
+/*! \cond */
+dnssec_register_random_type(uint16_t);
+dnssec_register_random_type(uint32_t);
+dnssec_register_random_type(uint64_t);
+/*! \endcond */
+
+/*! @} */
diff --git a/src/libdnssec/shared/bignum.c b/src/libdnssec/shared/bignum.c
new file mode 100644
index 0000000..3b347a6
--- /dev/null
+++ b/src/libdnssec/shared/bignum.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <string.h>
+
+#include "libdnssec/shared/bignum.h"
+
+static void skip_leading_zeroes(dnssec_binary_t *value)
+{
+ while (value->size > 0 && value->data[0] == 0) {
+ value->data += 1;
+ value->size -= 1;
+ }
+}
+
+size_t bignum_size_u(const dnssec_binary_t *_value)
+{
+ dnssec_binary_t value = *_value;
+ skip_leading_zeroes(&value);
+
+ if (value.size == 0) {
+ return value.size + 1;
+ } else {
+ return value.size;
+ }
+}
+
+size_t bignum_size_s(const dnssec_binary_t *_value)
+{
+ dnssec_binary_t value = *_value;
+ skip_leading_zeroes(&value);
+
+ if (value.size == 0 || value.data[0] & 0x80) {
+ return value.size + 1;
+ } else {
+ return value.size;
+ }
+}
+
+void bignum_write(wire_ctx_t *ctx, size_t width, const dnssec_binary_t *_value)
+{
+ dnssec_binary_t value = *_value;
+ skip_leading_zeroes(&value);
+
+ size_t padding_len = width - value.size;
+ if (padding_len > 0) {
+ wire_ctx_clear(ctx, padding_len);
+ }
+ wire_ctx_write(ctx, value.data, value.size);
+}
diff --git a/src/libdnssec/shared/bignum.h b/src/libdnssec/shared/bignum.h
new file mode 100644
index 0000000..e4ddede
--- /dev/null
+++ b/src/libdnssec/shared/bignum.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include "libdnssec/binary.h"
+#include "contrib/wire_ctx.h"
+
+/*!
+ * Size needed to write unsigned number in unsigned encoding.
+ */
+size_t bignum_size_u(const dnssec_binary_t *value);
+
+/*!
+ * Size needed to write unsigned number in signed encoding.
+ *
+ * Signed encoding expects the MSB to be zero.
+ */
+size_t bignum_size_s(const dnssec_binary_t *value);
+
+/*!
+ * Write unsigned number on a fixed width in a big-endian byte order.
+ *
+ * The destination size has to be set properly to accommodate used encoding.
+ */
+void bignum_write(wire_ctx_t *ctx, size_t width, const dnssec_binary_t *value);
diff --git a/src/libdnssec/shared/binary_wire.h b/src/libdnssec/shared/binary_wire.h
new file mode 100644
index 0000000..807cfc6
--- /dev/null
+++ b/src/libdnssec/shared/binary_wire.h
@@ -0,0 +1,53 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+#include "contrib/wire_ctx.h"
+#include "libdnssec/binary.h"
+
+static inline wire_ctx_t binary_init(const dnssec_binary_t *binary)
+{
+ assert(binary);
+
+ return wire_ctx_init(binary->data, binary->size);
+}
+
+static inline void binary_read(wire_ctx_t *ctx, dnssec_binary_t *data)
+{
+ assert(data);
+
+ wire_ctx_read(ctx, data->data, data->size);
+}
+
+static inline void binary_available(wire_ctx_t *ctx, dnssec_binary_t *data)
+{
+ assert(ctx);
+ assert(data);
+
+ data->data = ctx->position;
+ data->size = wire_ctx_available(ctx);
+}
+
+static inline void binary_write(wire_ctx_t *ctx, const dnssec_binary_t *data)
+{
+ assert(ctx);
+ assert(data);
+
+ wire_ctx_write(ctx, data->data, data->size);
+}
diff --git a/src/libdnssec/shared/dname.c b/src/libdnssec/shared/dname.c
new file mode 100644
index 0000000..61a5034
--- /dev/null
+++ b/src/libdnssec/shared/dname.c
@@ -0,0 +1,165 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libdnssec/shared/dname.h"
+#include "libdnssec/shared/shared.h"
+#include "contrib/tolower.h"
+
+/*!
+ * Get length of a domain name in wire format.
+ */
+size_t dname_length(const uint8_t *dname)
+{
+ if (!dname) {
+ return 0;
+ }
+
+ const uint8_t *scan = dname;
+ uint8_t label_len;
+ do {
+ label_len = *scan;
+ scan += 1 + label_len;
+ } while (label_len > 0);
+ assert(scan > dname);
+
+ size_t length = scan - dname;
+ if (length > DNAME_MAX_LENGTH) {
+ return 0;
+ }
+
+ return length;
+}
+
+/*!
+ * Copy domain name in wire format.
+ */
+uint8_t *dname_copy(const uint8_t *dname)
+{
+ if (!dname) {
+ return NULL;
+ }
+
+ size_t length = dname_length(dname);
+ if (length == 0) {
+ return NULL;
+ }
+
+ uint8_t *copy = malloc(length);
+ if (!copy) {
+ return NULL;
+ }
+
+ memmove(copy, dname, length);
+ return copy;
+}
+
+/*!
+ * Normalize dname label in-place.
+ *
+ * \return Number of processed bytes, 0 if we encounter the last label.
+ */
+static uint8_t normalize_label(uint8_t *label)
+{
+ assert(label);
+
+ uint8_t len = *label;
+ if (len == 0 || len > DNAME_MAX_LABEL_LENGTH) {
+ return 0;
+ }
+
+ for (uint8_t *scan = label + 1, *end = scan + len; scan < end; scan++) {
+ *scan = knot_tolower(*scan);
+ }
+
+ return len + 1;
+}
+
+/*!
+ * Normalize domain name in wire format.
+ */
+void dname_normalize(uint8_t *dname)
+{
+ if (!dname) {
+ return;
+ }
+
+ uint8_t read, *scan = dname;
+ do {
+ read = normalize_label(scan);
+ scan += read;
+ } while (read > 0);
+}
+
+/*!
+ * Compare dname labels case insensitively.
+ */
+static int label_casecmp(const uint8_t *a, const uint8_t *b, uint8_t len)
+{
+ assert(a);
+ assert(b);
+
+ for (const uint8_t *a_end = a + len; a < a_end; a++, b++) {
+ if (knot_tolower(*a) != knot_tolower(*b)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ * Check if two dnames are equal.
+ */
+bool dname_equal(const uint8_t *one, const uint8_t *two)
+{
+ if (!one || !two) {
+ return false;
+ }
+
+ const uint8_t *scan_one = one;
+ const uint8_t *scan_two = two;
+
+ for (;;) {
+ if (*scan_one != *scan_two) {
+ return false;
+ }
+
+ uint8_t len = *scan_one;
+ if (len == 0) {
+ return true;
+ } else if (len > DNAME_MAX_LABEL_LENGTH) {
+ return false;
+ }
+
+ scan_one += 1;
+ scan_two += 1;
+
+ if (!label_casecmp(scan_one, scan_two, len)) {
+ return false;
+ }
+
+ scan_one += len;
+ scan_two += len;
+ }
+
+ return true;
+}
diff --git a/src/libdnssec/shared/dname.h b/src/libdnssec/shared/dname.h
new file mode 100644
index 0000000..15e4e2a
--- /dev/null
+++ b/src/libdnssec/shared/dname.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/*!
+ * Maximal length of domain name including labels and length bytes.
+ * \see RFC 1035
+ */
+#define DNAME_MAX_LENGTH 255
+
+/*!
+ * Maximal length of the domain name label, excluding the label size.
+ * \see RFC 1035
+ */
+#define DNAME_MAX_LABEL_LENGTH 63
+
+/*!
+ * Get length of a domain name in wire format.
+ */
+size_t dname_length(const uint8_t *dname);
+
+/*!
+ * Copy domain name in wire format.
+ */
+uint8_t *dname_copy(const uint8_t *dname);
+
+/*!
+ * Normalize domain name in wire format.
+ *
+ * Currently converts all letters to lowercase.
+ */
+void dname_normalize(uint8_t *dname);
+
+/*!
+ * Check if two dnames are equal.
+ *
+ * Case insensitive.
+ */
+bool dname_equal(const uint8_t *one, const uint8_t *two);
diff --git a/src/libdnssec/shared/keyid_gnutls.c b/src/libdnssec/shared/keyid_gnutls.c
new file mode 100644
index 0000000..eee27d3
--- /dev/null
+++ b/src/libdnssec/shared/keyid_gnutls.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <string.h>
+
+#include "contrib/string.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keyid.h"
+#include "libdnssec/shared/keyid_gnutls.h"
+#include "libdnssec/shared/shared.h"
+
+/*!
+ * Get binary key ID from a key (public or private).
+ */
+static int keyid_bin(gnutls_x509_privkey_t key, gnutls_pubkey_t pubkey, dnssec_binary_t *id)
+{
+ assert(key || pubkey);
+ assert(id);
+
+ // Flags can be used to enable SHA-2 since GnuTLS 3.4.7.
+
+ int flags = 0;
+ uint8_t buffer[DNSSEC_KEYID_BINARY_SIZE];
+ size_t size = DNSSEC_KEYID_SIZE;
+
+ int r = key ? gnutls_x509_privkey_get_key_id(key, flags, buffer, &size)
+ : gnutls_pubkey_get_key_id(pubkey, flags, buffer, &size);
+
+ if (r != GNUTLS_E_SUCCESS || size != DNSSEC_KEYID_BINARY_SIZE) {
+ return DNSSEC_INVALID_KEY_ID;
+ }
+
+ assert(size == DNSSEC_KEYID_BINARY_SIZE);
+ r = dnssec_binary_resize(id, size);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ memcpy(id->data, buffer, size);
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Get hexadecimal key ID from a key (public or private).
+ */
+static int keyid_hex(gnutls_x509_privkey_t key, gnutls_pubkey_t pubkey, char **id)
+{
+ _cleanup_binary_ dnssec_binary_t bin = { 0 };
+ int r = keyid_bin(key, pubkey, &bin);
+ if (r != DNSSEC_EOK) {
+ return r;
+ }
+
+ *id = bin_to_hex(bin.data, bin.size, false);
+ if (*id == NULL) {
+ return DNSSEC_ENOMEM;
+ }
+
+ return DNSSEC_EOK;
+}
+
+int keyid_x509(gnutls_x509_privkey_t key, dnssec_binary_t *id)
+{
+ return keyid_bin(key, NULL, id);
+}
+
+int keyid_x509_hex(gnutls_x509_privkey_t key, char **id)
+{
+ return keyid_hex(key, NULL, id);
+}
+
+int keyid_pubkey(gnutls_pubkey_t pubkey, dnssec_binary_t *id)
+{
+ return keyid_bin(NULL, pubkey, id);
+}
+
+int keyid_pubkey_hex(gnutls_pubkey_t pubkey, char **id)
+{
+ return keyid_hex(NULL, pubkey, id);
+}
diff --git a/src/libdnssec/shared/keyid_gnutls.h b/src/libdnssec/shared/keyid_gnutls.h
new file mode 100644
index 0000000..356e62e
--- /dev/null
+++ b/src/libdnssec/shared/keyid_gnutls.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+
+#include "libdnssec/binary.h"
+
+int keyid_x509(gnutls_x509_privkey_t key, dnssec_binary_t *id);
+
+int keyid_x509_hex(gnutls_x509_privkey_t key, char **id);
+
+int keyid_pubkey(gnutls_pubkey_t pubkey, dnssec_binary_t *id);
+
+int keyid_pubkey_hex(gnutls_pubkey_t pubkey, char **id);
diff --git a/src/libdnssec/shared/shared.h b/src/libdnssec/shared/shared.h
new file mode 100644
index 0000000..1cde2d1
--- /dev/null
+++ b/src/libdnssec/shared/shared.h
@@ -0,0 +1,121 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+#include "libdnssec/binary.h"
+#include "libknot/attribute.h"
+
+/*!
+ * Macro to clear a structure of known size.
+ *
+ * \param pointer Pointer to the structure.
+ */
+#define clear_struct(pointer) memset((pointer), '\0', sizeof(*(pointer)))
+
+/* -- cleanup macros ------------------------------------------------------- */
+
+static inline void free_ptr(void *ptr)
+{
+ free(*(void **)ptr);
+}
+
+static inline void close_ptr(int *ptr)
+{
+ if (*ptr != -1) {
+ close(*ptr);
+ }
+}
+
+static inline void fclose_ptr(FILE **ptr)
+{
+ if (*ptr) {
+ fclose(*ptr);
+ }
+}
+
+static inline void closedir_ptr(DIR **ptr)
+{
+ if (*ptr) {
+ closedir(*ptr);
+ }
+}
+
+static inline void free_gnutls_datum_ptr(gnutls_datum_t *ptr)
+{
+ gnutls_free(ptr->data);
+}
+
+static inline void free_x509_privkey_ptr(gnutls_x509_privkey_t *ptr)
+{
+ if (*ptr) {
+ gnutls_x509_privkey_deinit(*ptr);
+ }
+}
+
+static inline void free_pubkey_ptr(gnutls_pubkey_t *ptr)
+{
+ if (*ptr) {
+ gnutls_pubkey_deinit(*ptr);
+ }
+}
+
+static inline void free_gnutls_hash_ptr(gnutls_hash_hd_t *ptr)
+{
+ if (*ptr) {
+ gnutls_hash_deinit(*ptr, NULL);
+ }
+}
+
+#define _cleanup_free_ _cleanup_(free_ptr)
+#define _cleanup_close_ _cleanup_(close_ptr)
+#define _cleanup_fclose_ _cleanup_(fclose_ptr)
+#define _cleanup_closedir_ _cleanup_(closedir_ptr)
+#define _cleanup_binary_ _cleanup_(dnssec_binary_free)
+#define _cleanup_datum_ _cleanup_(free_gnutls_datum_ptr)
+#define _cleanup_x509_privkey_ _cleanup_(free_x509_privkey_ptr)
+#define _cleanup_pubkey_ _cleanup_(free_pubkey_ptr)
+#define _cleanup_hash_ _cleanup_(free_gnutls_hash_ptr)
+
+/* -- assertions ----------------------------------------------------------- */
+
+#define assert_unreachable() assert(0)
+
+/* -- crypto helpers ------------------------------------------------------- */
+
+static inline gnutls_datum_t binary_to_datum(const dnssec_binary_t *from)
+{
+ gnutls_datum_t to = { .size = from->size, .data = from->data };
+ return to;
+}
+
+static inline dnssec_binary_t binary_from_datum(const gnutls_datum_t *from)
+{
+ dnssec_binary_t to = { .size = from->size, .data = from->data };
+ return to;
+}
diff --git a/src/libdnssec/sign.h b/src/libdnssec/sign.h
new file mode 100644
index 0000000..0311b52
--- /dev/null
+++ b/src/libdnssec/sign.h
@@ -0,0 +1,114 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup sign
+ *
+ * \brief DNSSEC signing API
+ *
+ * The module provides the low level DNSSEC signing and verification.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <libdnssec/binary.h>
+#include <libdnssec/key.h>
+
+struct dnssec_sign_ctx;
+
+typedef enum {
+ DNSSEC_SIGN_NORMAL = 0,
+ DNSSEC_SIGN_REPRODUCIBLE = (1 << 0),
+} dnssec_sign_flags_t;
+
+/*!
+ * DNSSEC signing context.
+ */
+typedef struct dnssec_sign_ctx dnssec_sign_ctx_t;
+
+/*!
+ * Create new DNSSEC signing context.
+ *
+ * \note \ref dnssec_sign_init is called as a part of this function.
+ *
+ * \param ctx_ptr Pointer to context to be allocated.
+ * \param key DNSSEC key to be used.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_sign_new(dnssec_sign_ctx_t **ctx_ptr, const dnssec_key_t *key);
+
+/*!
+ * Free DNSSEC signing context.
+ *
+ * \param ctx Signing context to be freed.
+ */
+void dnssec_sign_free(dnssec_sign_ctx_t *ctx);
+
+/*!
+ * Reinitialize DNSSEC signing context to start a new operation.
+ *
+ * \param ctx Signing context.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_sign_init(dnssec_sign_ctx_t *ctx);
+
+/*!
+ * Add data to be covered by DNSSEC signature.
+ *
+ * \param ctx Signing context.
+ * \param data Data to be signed.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_sign_add(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *data);
+
+/*!
+ * Write down the DNSSEC signature.
+ *
+ * \param ctx Signing context.
+ * \param flags Additional flags to be used for signing.
+ * \param signature Signature to be allocated and written.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_sign_flags_t flags,
+ dnssec_binary_t *signature);
+
+/*!
+ * Verify DNSSEC signature.
+ *
+ * \param ctx Signing context.
+ * \param sign_cmp Verify by signing and comparing signatures.
+ * Not possible for non-deterministic algorithms!
+ * \param signature Signature to be verified.
+ *
+ * \return Error code.
+ * \retval DNSSEC_EOK Validation successful, valid signature.
+ * \retval DNSSEC_INVALID_SIGNATURE Validation successful, invalid signature.
+ */
+int dnssec_sign_verify(dnssec_sign_ctx_t *ctx, bool sign_cmp,
+ const dnssec_binary_t *signature);
+
+/*! @} */
diff --git a/src/libdnssec/sign/der.c b/src/libdnssec/sign/der.c
new file mode 100644
index 0000000..cd6102d
--- /dev/null
+++ b/src/libdnssec/sign/der.c
@@ -0,0 +1,229 @@
+/* Copyright (C) 2018 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 <stdbool.h>
+
+#include "libdnssec/shared/bignum.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/sign/der.h"
+#include "libdnssec/shared/binary_wire.h"
+
+/*
+ * In fact, this is a very tiny subset of ASN.1 encoding format implementation,
+ * which is necessary for the purpose of DNSSEC.
+ *
+ * References: RFC 3279 (X.509 PKI), X.690, RFC 6605 (ECDSA), RFC8080 (EDDSA)
+ *
+ * Dss-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
+ */
+
+#define ASN1_TYPE_SEQUENCE 0x30
+#define ASN1_TYPE_INTEGER 0x02
+
+#define ASN1_MAX_SIZE 127
+
+/*!
+ * Check if the next object has a given type.
+ */
+static bool asn1_expect_type(wire_ctx_t *wire, uint8_t type)
+{
+ assert(wire);
+ return (wire_ctx_available(wire) >= 1 && wire_ctx_read_u8(wire) == type);
+}
+
+/*!
+ * Decode the size of the object (only short format is supported).
+ */
+static int asn1_decode_size(wire_ctx_t *wire, size_t *size)
+{
+ assert(wire);
+ assert(size);
+
+ if (wire_ctx_available(wire) < 1) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ uint8_t byte = wire_ctx_read_u8(wire);
+ if (byte & 0x80) {
+ // long form, we do not need it for DNSSEC
+ return DNSSEC_NOT_IMPLEMENTED_ERROR;
+ }
+
+ *size = byte;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Decode an unsigned integer object.
+ */
+static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *_value)
+{
+ assert(wire);
+ assert(_value);
+
+ if (!asn1_expect_type(wire, ASN1_TYPE_INTEGER)) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ size_t size;
+ int result = asn1_decode_size(wire, &size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ if (size == 0 || size > wire_ctx_available(wire)) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ dnssec_binary_t value = { .data = wire->position, .size = size };
+ wire->position += size;
+
+ // skip leading zeroes (unless equal to zero)
+ while (value.size > 1 && value.data[0] == 0) {
+ value.data += 1;
+ value.size -= 1;
+ }
+
+ *_value = value;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Encode object header (type and length).
+ */
+static void asn1_write_header(wire_ctx_t *wire, uint8_t type, size_t length)
+{
+ assert(wire);
+ assert(length < ASN1_MAX_SIZE);
+
+ wire_ctx_write_u8(wire, type);
+ wire_ctx_write_u8(wire, length);
+}
+
+/*!
+ * Encode unsigned integer object.
+ */
+static void asn1_write_integer(wire_ctx_t *wire, size_t integer_size,
+ const dnssec_binary_t *integer)
+{
+ assert(wire);
+ assert(integer);
+ assert(integer->data);
+
+ asn1_write_header(wire, ASN1_TYPE_INTEGER, integer_size);
+ bignum_write(wire, integer_size, integer);
+}
+
+/*!
+ * Decode signature parameters from X.509 ECDSA signature.
+ */
+int dss_sig_value_decode(const dnssec_binary_t *der,
+ dnssec_binary_t *r, dnssec_binary_t *s)
+{
+ if (!der || !der->data || !r || !s) {
+ return DNSSEC_EINVAL;
+ }
+
+ wire_ctx_t wire = binary_init(der);
+
+ size_t size;
+ int result;
+
+ // decode the sequence
+
+ if (!asn1_expect_type(&wire, ASN1_TYPE_SEQUENCE)) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ result = asn1_decode_size(&wire, &size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ if (size != wire_ctx_available(&wire)) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ // decode the 'r' and 's' values
+
+ dnssec_binary_t der_r;
+ result = asn1_decode_integer(&wire, &der_r);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ dnssec_binary_t der_s;
+ result = asn1_decode_integer(&wire, &der_s);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ if (wire_ctx_available(&wire) != 0) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ *r = der_r;
+ *s = der_s;
+
+ return DNSSEC_EOK;
+}
+
+/*!
+ * Encode signature parameters from X.509 ECDSA signature.
+ */
+int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s,
+ dnssec_binary_t *der)
+{
+ if (!r || !r->data || !s || !s->data || !der) {
+ return DNSSEC_EINVAL;
+ }
+
+ size_t r_size = bignum_size_s(r);
+ size_t s_size = bignum_size_s(s);
+
+ // check supported inputs range
+
+ if (r_size > ASN1_MAX_SIZE || s_size > ASN1_MAX_SIZE) {
+ return DNSSEC_NOT_IMPLEMENTED_ERROR;
+ }
+
+ size_t seq_size = 2 + r_size + 2 + s_size;
+ if (seq_size > ASN1_MAX_SIZE) {
+ return DNSSEC_NOT_IMPLEMENTED_ERROR;
+ }
+
+ // encode result
+
+ size_t total_size = 2 + seq_size;
+
+ dnssec_binary_t _der = { 0 };
+ if (dnssec_binary_alloc(&_der, total_size)) {
+ return DNSSEC_ENOMEM;
+ }
+
+ wire_ctx_t wire = binary_init(&_der);
+ asn1_write_header(&wire, ASN1_TYPE_SEQUENCE, seq_size);
+ asn1_write_integer(&wire, r_size, r);
+ asn1_write_integer(&wire, s_size, s);
+ assert(wire_ctx_available(&wire) == 0);
+
+ *der = _der;
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/sign/der.h b/src/libdnssec/sign/der.h
new file mode 100644
index 0000000..687b061
--- /dev/null
+++ b/src/libdnssec/sign/der.h
@@ -0,0 +1,56 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libdnssec/binary.h"
+
+/*
+ * The ECDSA signatures in DNSSEC are encoded differently than in X.509
+ * (PKCS #1). The cryptographic libraries usually produce the signatures in
+ * X.509 format, which uses Dss-Sig-Value to encapsulate 'r' and 's' values
+ * of the signature.
+ *
+ * This module provides decoding and encoding of this format.
+ *
+ * The 'r' and 's' values are treated as unsigned values: The leading zeroes
+ * are stripped on decoding; an extra leading zero is added on encoding in case
+ * the value starts with a set bit.
+ */
+
+/*!
+ * Decode signature parameters from X.509 ECDSA signature.
+ *
+ * \param[in] der X.509 encoded signature.
+ * \param[out] s Value 's' of the signature, will point to the data in DER.
+ * \param[out] r Value 'r' of the signature, will point to the data in DER.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dss_sig_value_decode(const dnssec_binary_t *der,
+ dnssec_binary_t *r, dnssec_binary_t *s);
+
+/*!
+ * Encode signature parameters from X.509 ECDSA signature.
+ *
+ * \param[in] s Value 's' of the signature.
+ * \param[in] r Value 'r' of the signature.
+ * \param[out] der X.509 signature, the content will be allocated.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s,
+ dnssec_binary_t *der);
diff --git a/src/libdnssec/sign/sign.c b/src/libdnssec/sign/sign.c
new file mode 100644
index 0000000..3a7bcba
--- /dev/null
+++ b/src/libdnssec/sign/sign.c
@@ -0,0 +1,433 @@
+/* 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#include "contrib/macros.h"
+#include "contrib/vpool/vpool.h"
+#include "libdnssec/shared/bignum.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "libdnssec/key/internal.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/sign.h"
+#include "libdnssec/sign/der.h"
+#include "libdnssec/shared/binary_wire.h"
+
+/*!
+ * Signature format conversion callback.
+ *
+ * \param ctx DNSSEC signing context.
+ * \param from Data in source format.
+ * \param to Allocated data in target format.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+typedef int (*signature_convert_cb)(dnssec_sign_ctx_t *ctx,
+ const dnssec_binary_t *from,
+ dnssec_binary_t *to);
+
+/*!
+ * Algorithm specific callbacks.
+ */
+typedef struct algorithm_functions {
+ //! Convert X.509 signature to DNSSEC format.
+ signature_convert_cb x509_to_dnssec;
+ //! Convert DNSSEC signature to X.509 format.
+ signature_convert_cb dnssec_to_x509;
+} algorithm_functions_t;
+
+typedef struct dnssec_buffer {
+ uint8_t *allocd; //!< Pointer to allocated data.
+ uint8_t *data; //!< API: pointer to data to copy from.
+ size_t max_length;
+ size_t length; //!< API: current length.
+} dnssec_buffer_t;
+
+/*!
+ * DNSSEC signing context.
+ */
+struct dnssec_sign_ctx {
+ const dnssec_key_t *key; //!< Signing key.
+ const algorithm_functions_t *functions; //!< Implementation specific.
+
+ gnutls_sign_algorithm_t sign_algorithm; //!< Used algorithm for signing.
+ struct vpool buffer; //!< Buffer for the data to be signed.
+};
+
+/* -- signature format conversions ----------------------------------------- */
+
+/*!
+ * Conversion of RSA signature between X.509 and DNSSEC format is a NOOP.
+ *
+ * \note Described in RFC 3110.
+ */
+static int rsa_copy_signature(dnssec_sign_ctx_t *ctx,
+ const dnssec_binary_t *from,
+ dnssec_binary_t *to)
+{
+ assert(ctx);
+ assert(from);
+ assert(to);
+
+ return dnssec_binary_dup(from, to);
+}
+
+static const algorithm_functions_t rsa_functions = {
+ .x509_to_dnssec = rsa_copy_signature,
+ .dnssec_to_x509 = rsa_copy_signature,
+};
+
+static size_t ecdsa_sign_integer_size(dnssec_sign_ctx_t *ctx)
+{
+ assert(ctx);
+
+ switch (ctx->sign_algorithm) {
+ case GNUTLS_SIGN_ECDSA_SHA256: return 32;
+ case GNUTLS_SIGN_ECDSA_SHA384: return 48;
+ default: return 0;
+ };
+}
+
+/*!
+ * Convert ECDSA signature to DNSSEC format.
+ *
+ * \note Described in RFC 6605.
+ */
+static int ecdsa_x509_to_dnssec(dnssec_sign_ctx_t *ctx,
+ const dnssec_binary_t *x509,
+ dnssec_binary_t *dnssec)
+{
+ assert(ctx);
+ assert(x509);
+ assert(dnssec);
+
+ dnssec_binary_t value_r = { 0 };
+ dnssec_binary_t value_s = { 0 };
+
+ int result = dss_sig_value_decode(x509, &value_r, &value_s);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ size_t int_size = ecdsa_sign_integer_size(ctx);
+ size_t r_size = bignum_size_u(&value_r);
+ size_t s_size = bignum_size_u(&value_s);
+
+ if (r_size > int_size || s_size > int_size) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ result = dnssec_binary_alloc(dnssec, 2 * int_size);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ wire_ctx_t wire = binary_init(dnssec);
+ bignum_write(&wire, int_size, &value_r);
+ bignum_write(&wire, int_size, &value_s);
+ assert(wire_ctx_offset(&wire) == dnssec->size);
+
+ return DNSSEC_EOK;
+}
+
+static int ecdsa_dnssec_to_x509(dnssec_sign_ctx_t *ctx,
+ const dnssec_binary_t *dnssec,
+ dnssec_binary_t *x509)
+{
+ assert(ctx);
+ assert(x509);
+ assert(dnssec);
+
+ size_t int_size = ecdsa_sign_integer_size(ctx);
+
+ if (dnssec->size != 2 * int_size) {
+ return DNSSEC_INVALID_SIGNATURE;
+ }
+
+ const dnssec_binary_t value_r = { .size = int_size, .data = dnssec->data };
+ const dnssec_binary_t value_s = { .size = int_size, .data = dnssec->data + int_size };
+
+ return dss_sig_value_encode(&value_r, &value_s, x509);
+}
+
+static const algorithm_functions_t ecdsa_functions = {
+ .x509_to_dnssec = ecdsa_x509_to_dnssec,
+ .dnssec_to_x509 = ecdsa_dnssec_to_x509,
+};
+
+#define eddsa_copy_signature rsa_copy_signature
+static const algorithm_functions_t eddsa_functions = {
+ .x509_to_dnssec = eddsa_copy_signature,
+ .dnssec_to_x509 = eddsa_copy_signature,
+};
+
+/* -- crypto helper functions --------------------------------------------- */
+
+static const algorithm_functions_t *get_functions(const dnssec_key_t *key)
+{
+ uint8_t algorithm = dnssec_key_get_algorithm(key);
+
+ switch ((dnssec_key_algorithm_t)algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return &rsa_functions;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return &ecdsa_functions;
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return &eddsa_functions;
+ default:
+ return NULL;
+ }
+}
+
+#ifndef HAVE_SIGN_DATA2
+/*!
+ * Get digest algorithm used with a given key.
+ */
+static gnutls_digest_algorithm_t get_digest_algorithm(const dnssec_key_t *key)
+{
+ uint8_t algorithm = dnssec_key_get_algorithm(key);
+
+ switch ((dnssec_key_algorithm_t)algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ return GNUTLS_DIG_SHA1;
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ return GNUTLS_DIG_SHA256;
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return GNUTLS_DIG_SHA512;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return GNUTLS_DIG_SHA384;
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return GNUTLS_DIG_SHA512;
+ default:
+ return GNUTLS_DIG_UNKNOWN;
+ }
+}
+#endif
+
+static gnutls_sign_algorithm_t algo_dnssec2gnutls(dnssec_key_algorithm_t algorithm)
+{
+ switch (algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ return GNUTLS_SIGN_RSA_SHA1;
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ return GNUTLS_SIGN_RSA_SHA256;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ return GNUTLS_SIGN_ECDSA_SHA256;
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return GNUTLS_SIGN_RSA_SHA512;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return GNUTLS_SIGN_ECDSA_SHA384;
+#ifdef HAVE_ED25519
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ return GNUTLS_SIGN_EDDSA_ED25519;
+#endif
+#ifdef HAVE_ED448
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ return GNUTLS_SIGN_EDDSA_ED448;
+#endif
+ default:
+ return GNUTLS_SIGN_UNKNOWN;
+ }
+}
+
+/* -- public API ---------------------------------------------------------- */
+
+_public_
+bool dnssec_algorithm_key_support(dnssec_key_algorithm_t algorithm)
+{
+ gnutls_sign_algorithm_t a = algo_dnssec2gnutls(algorithm);
+ return a != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(a);
+}
+
+_public_
+int dnssec_sign_new(dnssec_sign_ctx_t **ctx_ptr, const dnssec_key_t *key)
+{
+ if (!ctx_ptr) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_sign_ctx_t *ctx = calloc(1, sizeof(*ctx));
+
+ ctx->key = key;
+
+ ctx->functions = get_functions(key);
+ if (ctx->functions == NULL) {
+ free(ctx);
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ const uint8_t algo_raw = dnssec_key_get_algorithm(key);
+ ctx->sign_algorithm = algo_dnssec2gnutls((dnssec_key_algorithm_t)algo_raw);
+ int result = dnssec_sign_init(ctx);
+ if (result != DNSSEC_EOK) {
+ free(ctx);
+ return result;
+ }
+
+ *ctx_ptr = ctx;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+void dnssec_sign_free(dnssec_sign_ctx_t *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ vpool_reset(&ctx->buffer);
+
+ free(ctx);
+}
+
+_public_
+int dnssec_sign_init(dnssec_sign_ctx_t *ctx)
+{
+ if (!ctx) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (vpool_get_buf(&ctx->buffer) != NULL) {
+ vpool_wipe(&ctx->buffer);
+ } else {
+ vpool_init(&ctx->buffer, 1024, 0);
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_sign_add(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *data)
+{
+ if (!ctx || !data || !data->data) {
+ return DNSSEC_EINVAL;
+ }
+
+ void *result = vpool_insert(&ctx->buffer, vpool_get_length(&ctx->buffer), data->data, data->size);
+ if (result == NULL) {
+ return DNSSEC_SIGN_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_sign_flags_t flags, dnssec_binary_t *signature)
+{
+ if (!ctx || !signature) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (!dnssec_key_can_sign(ctx->key)) {
+ return DNSSEC_NO_PRIVATE_KEY;
+ }
+
+ gnutls_datum_t data = {
+ .data = vpool_get_buf(&ctx->buffer),
+ .size = vpool_get_length(&ctx->buffer)
+ };
+
+ unsigned gnutls_flags = 0;
+#ifdef HAVE_GNUTLS_REPRODUCIBLE
+ if (flags & DNSSEC_SIGN_REPRODUCIBLE) {
+ gnutls_flags |= GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE;
+ }
+#endif
+
+ assert(ctx->key->private_key);
+ _cleanup_datum_ gnutls_datum_t raw = { 0 };
+#ifdef HAVE_SIGN_DATA2
+ int result = gnutls_privkey_sign_data2(ctx->key->private_key,
+ ctx->sign_algorithm,
+ gnutls_flags, &data, &raw);
+#else
+ gnutls_digest_algorithm_t digest_algorithm = get_digest_algorithm(ctx->key);
+ int result = gnutls_privkey_sign_data(ctx->key->private_key,
+ digest_algorithm,
+ gnutls_flags, &data, &raw);
+#endif
+ if (result < 0) {
+ return DNSSEC_SIGN_ERROR;
+ }
+
+ dnssec_binary_t bin_raw = binary_from_datum(&raw);
+
+ return ctx->functions->x509_to_dnssec(ctx, &bin_raw, signature);
+}
+
+_public_
+int dnssec_sign_verify(dnssec_sign_ctx_t *ctx, bool sign_cmp, const dnssec_binary_t *signature)
+{
+ if (!ctx || !signature) {
+ return DNSSEC_EINVAL;
+ }
+
+ if (sign_cmp && dnssec_key_can_sign(ctx->key)) {
+ dnssec_binary_t sign = { 0 };
+ int ret = dnssec_sign_write(ctx, DNSSEC_SIGN_REPRODUCIBLE, &sign);
+ if (ret == KNOT_EOK) {
+ ret = dnssec_binary_cmp(&sign, signature)
+ ? DNSSEC_INVALID_SIGNATURE
+ : DNSSEC_EOK;
+ }
+ dnssec_binary_free(&sign);
+ return ret;
+ }
+
+ if (!dnssec_key_can_verify(ctx->key)) {
+ return DNSSEC_NO_PUBLIC_KEY;
+ }
+
+ gnutls_datum_t data = {
+ .data = vpool_get_buf(&ctx->buffer),
+ .size = vpool_get_length(&ctx->buffer)
+ };
+
+ _cleanup_binary_ dnssec_binary_t bin_raw = { 0 };
+ int result = ctx->functions->dnssec_to_x509(ctx, signature, &bin_raw);
+ if (result != DNSSEC_EOK) {
+ return result;
+ }
+
+ gnutls_datum_t raw = binary_to_datum(&bin_raw);
+
+ assert(ctx->key->public_key);
+ result = gnutls_pubkey_verify_data2(ctx->key->public_key,
+ ctx->sign_algorithm,
+ 0, &data, &raw);
+ if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ return DNSSEC_INVALID_SIGNATURE;
+ } else if (result < 0) {
+ return DNSSEC_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/tsig.c b/src/libdnssec/tsig.c
new file mode 100644
index 0000000..b7bd980
--- /dev/null
+++ b/src/libdnssec/tsig.c
@@ -0,0 +1,242 @@
+/* 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 <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libdnssec/shared/dname.h"
+#include "libdnssec/error.h"
+#include "libdnssec/shared/shared.h"
+#include "libdnssec/tsig.h"
+
+struct dnssec_tsig_ctx {
+ gnutls_mac_algorithm_t algorithm;
+ gnutls_hmac_hd_t hash;
+};
+
+/*!
+ * TSIG algorithm identifiers.
+ */
+typedef struct {
+ dnssec_tsig_algorithm_t id;
+ gnutls_mac_algorithm_t gnutls_id;
+ const char *name;
+ const char *dname;
+} algorithm_id_t;
+
+/*!
+ * DNAME to algorithm conversion table.
+ */
+static const algorithm_id_t ALGORITHM_ID_TABLE[] = {
+ // RFC 4635
+ { DNSSEC_TSIG_HMAC_SHA1, GNUTLS_MAC_SHA1, "hmac-sha1", "\x9hmac-sha1" },
+ { DNSSEC_TSIG_HMAC_SHA224, GNUTLS_MAC_SHA224, "hmac-sha224", "\xbhmac-sha224" },
+ { DNSSEC_TSIG_HMAC_SHA256, GNUTLS_MAC_SHA256, "hmac-sha256", "\xbhmac-sha256" },
+ { DNSSEC_TSIG_HMAC_SHA384, GNUTLS_MAC_SHA384, "hmac-sha384", "\xbhmac-sha384" },
+ { DNSSEC_TSIG_HMAC_SHA512, GNUTLS_MAC_SHA512, "hmac-sha512", "\xbhmac-sha512" },
+ // RFC 2845
+ { DNSSEC_TSIG_HMAC_MD5, GNUTLS_MAC_MD5, "hmac-md5", "\x8hmac-md5\x7sig-alg\x3reg\x3int" },
+ { 0 }
+};
+
+/*!
+ * Algorithm match callback prototype.
+ */
+typedef bool (*algorithm_match_cb)(const algorithm_id_t *m, const void *data);
+
+/*!
+ * Lookup an algorithm in the algorithm table.
+ */
+static const algorithm_id_t *lookup_algorithm(algorithm_match_cb match,
+ const void *data)
+{
+ assert(match);
+
+ for (const algorithm_id_t *a = ALGORITHM_ID_TABLE; a->id; a++) {
+ if (match(a, data)) {
+ return a;
+ }
+ }
+
+ return NULL;
+}
+
+static bool match_dname(const algorithm_id_t *algorithm, const void *data)
+{
+ const uint8_t *search = data;
+ return dname_equal(search, (uint8_t *)algorithm->dname);
+}
+
+static bool match_name(const algorithm_id_t *algorithm, const void *data)
+{
+ const char *search = data;
+ return strcasecmp(search, algorithm->name) == 0;
+}
+
+static bool match_id(const algorithm_id_t *algorithm, const void *data)
+{
+ dnssec_tsig_algorithm_t search = *((dnssec_tsig_algorithm_t *)data);
+ return algorithm->id == search;
+}
+
+/*!
+ * Convert TSIG algorithm identifier to GnuTLS identifier.
+ */
+static gnutls_mac_algorithm_t algorithm_to_gnutls(dnssec_tsig_algorithm_t algorithm)
+{
+ const algorithm_id_t *found = lookup_algorithm(match_id, &algorithm);
+ return (found ? found->gnutls_id : GNUTLS_MAC_UNKNOWN);
+}
+
+/* -- public API ----------------------------------------------------------- */
+
+_public_
+dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_dname(const uint8_t *dname)
+{
+ if (!dname) {
+ return DNSSEC_TSIG_UNKNOWN;
+ }
+
+ const algorithm_id_t *found = lookup_algorithm(match_dname, dname);
+ return (found ? found->id : DNSSEC_TSIG_UNKNOWN);
+}
+
+_public_
+const uint8_t *dnssec_tsig_algorithm_to_dname(dnssec_tsig_algorithm_t algorithm)
+{
+ const algorithm_id_t *found = lookup_algorithm(match_id, &algorithm);
+ return (found ? (uint8_t *)found->dname : NULL);
+}
+
+_public_
+dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_name(const char *name)
+{
+ if (!name) {
+ return DNSSEC_TSIG_UNKNOWN;
+ }
+
+ const algorithm_id_t *found = lookup_algorithm(match_name, name);
+ return (found ? found->id : DNSSEC_TSIG_UNKNOWN);
+}
+
+_public_
+const char *dnssec_tsig_algorithm_to_name(dnssec_tsig_algorithm_t algorithm)
+{
+ const algorithm_id_t *found = lookup_algorithm(match_id, &algorithm);
+ return (found ? found->name : NULL);
+}
+
+_public_
+int dnssec_tsig_optimal_key_size(dnssec_tsig_algorithm_t tsig)
+{
+ gnutls_mac_algorithm_t mac = algorithm_to_gnutls(tsig);
+ if (mac == GNUTLS_MAC_UNKNOWN) {
+ return 0;
+ }
+
+ return gnutls_mac_get_key_size(mac) * CHAR_BIT;
+}
+
+_public_
+int dnssec_tsig_new(dnssec_tsig_ctx_t **ctx_ptr,
+ dnssec_tsig_algorithm_t algorithm,
+ const dnssec_binary_t *key)
+{
+ if (!ctx_ptr || !key) {
+ return DNSSEC_EINVAL;
+ }
+
+ dnssec_tsig_ctx_t *ctx = calloc(1, sizeof(*ctx));
+ if (!ctx) {
+ return DNSSEC_ENOMEM;
+ }
+
+ ctx->algorithm = algorithm_to_gnutls(algorithm);
+ if (ctx->algorithm == GNUTLS_MAC_UNKNOWN) {
+ free(ctx);
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+
+ int result = gnutls_hmac_init(&ctx->hash, ctx->algorithm, key->data, key->size);
+ if (result != 0) {
+ free(ctx);
+ return DNSSEC_SIGN_INIT_ERROR;
+ }
+
+ *ctx_ptr = ctx;
+
+ return DNSSEC_EOK;
+}
+
+_public_
+void dnssec_tsig_free(dnssec_tsig_ctx_t *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ gnutls_hmac_deinit(ctx->hash, NULL);
+ free(ctx);
+}
+
+_public_
+int dnssec_tsig_add(dnssec_tsig_ctx_t *ctx, const dnssec_binary_t *data)
+{
+ if (!ctx || !data) {
+ return DNSSEC_EINVAL;
+ }
+
+ int result = gnutls_hmac(ctx->hash, data->data, data->size);
+ if (result != 0) {
+ return DNSSEC_SIGN_ERROR;
+ }
+
+ return DNSSEC_EOK;
+}
+
+_public_
+size_t dnssec_tsig_size(dnssec_tsig_ctx_t *ctx)
+{
+ if (!ctx) {
+ return 0;
+ }
+
+ return gnutls_hmac_get_len(ctx->algorithm);
+}
+
+_public_
+size_t dnssec_tsig_algorithm_size(dnssec_tsig_algorithm_t algorithm)
+{
+ int gnutls_algorithm = algorithm_to_gnutls(algorithm);
+ return gnutls_hmac_get_len(gnutls_algorithm);
+}
+
+_public_
+int dnssec_tsig_write(dnssec_tsig_ctx_t *ctx, uint8_t *mac)
+{
+ if (!ctx || !mac) {
+ return DNSSEC_EINVAL;
+ }
+
+ gnutls_hmac_output(ctx->hash, mac);
+
+ return DNSSEC_EOK;
+}
diff --git a/src/libdnssec/tsig.h b/src/libdnssec/tsig.h
new file mode 100644
index 0000000..31f15c5
--- /dev/null
+++ b/src/libdnssec/tsig.h
@@ -0,0 +1,155 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup tsig
+ *
+ * \brief Low-level TSIG signing API.
+ *
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <libdnssec/binary.h>
+
+/*!
+ * TSIG algorithms.
+ *
+ * \note The numeric values are library specific.
+ */
+typedef enum dnssec_tsig_algorithm {
+ DNSSEC_TSIG_UNKNOWN = 0,
+ DNSSEC_TSIG_HMAC_MD5,
+ DNSSEC_TSIG_HMAC_SHA1,
+ DNSSEC_TSIG_HMAC_SHA224,
+ DNSSEC_TSIG_HMAC_SHA256,
+ DNSSEC_TSIG_HMAC_SHA384,
+ DNSSEC_TSIG_HMAC_SHA512
+} dnssec_tsig_algorithm_t;
+
+/*!
+ * Get TSIG algorithm number from domain name.
+ *
+ * \see https://www.iana.org/assignments/tsig-algorithm-names/tsig-algorithm-names.xhtml
+ *
+ * \param dname Domain name of the algorithm (e.g., 0x0b hmac-sha256).
+ *
+ * \return TSIG algorithm.
+ */
+dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_dname(const uint8_t *dname);
+
+/*!
+ * Get a domain name of the TSIG algorithm.
+ *
+ * \param algorithm TSIG algorithm.
+ *
+ * \return Domain name of the TSIG algorithm.
+ */
+const uint8_t *dnssec_tsig_algorithm_to_dname(dnssec_tsig_algorithm_t algorithm);
+
+/*!
+ * Get TSIG algorithm from a MAC name.
+ *
+ * \param name MAC name (e.g., hmac-sha256).
+ *
+ * \return TSIG algorithm.
+ */
+dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_name(const char *name);
+
+/*!
+ * Get MAC name from a TSIG algorithm.
+ *
+ * \param algorithm TSIG algorithm.
+ *
+ * \return MAC name of the TSIG algorithm.
+ */
+const char *dnssec_tsig_algorithm_to_name(dnssec_tsig_algorithm_t algorithm);
+
+/*!
+ * Get optimal size of a TSIG algorithm.
+ */
+int dnssec_tsig_optimal_key_size(dnssec_tsig_algorithm_t algorithm);
+
+struct dnssec_tsig_ctx;
+
+/*!
+ * TSIG signing context.
+ */
+typedef struct dnssec_tsig_ctx dnssec_tsig_ctx_t;
+
+/*!
+ * Create new TSIG signing context.
+ *
+ * \param[out] ctx Resulting TSIG context.
+ * \param[in] algorithm TSIG algorithm.
+ * \param[in] key Shared key to be used for signing.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_tsig_new(dnssec_tsig_ctx_t **ctx, dnssec_tsig_algorithm_t algorithm,
+ const dnssec_binary_t *key);
+
+/*!
+ * Free the TSIG signing context.
+ *
+ * \param ctx TSIG signing context to be freed.
+ */
+void dnssec_tsig_free(dnssec_tsig_ctx_t *ctx);
+
+/*!
+ * Add data to be signed by TSIG.
+ *
+ * \param ctx TSIG signing context.
+ * \param data Data to be signed.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_tsig_add(dnssec_tsig_ctx_t *ctx, const dnssec_binary_t *data);
+
+/*!
+ * Get size of the TSIG signature for given signing context.
+ *
+ * \param ctx TSIG signing context.
+ *
+ * \return The size of the TSIG signature.
+ */
+size_t dnssec_tsig_size(dnssec_tsig_ctx_t *ctx);
+
+/*!
+ * Get size of the TSIG signature for given algorithm.
+ *
+ * \param algorithm TSIG algorithm.
+ *
+ * \return The size of the TSIG signature.
+ */
+size_t dnssec_tsig_algorithm_size(dnssec_tsig_algorithm_t algorithm);
+
+/*!
+ * Write TSIG signature.
+ *
+ * \param[in] ctx TSIG signing context.
+ * \param[out] mac Resulting TSIG signature.
+ *
+ * \return Error code, DNSSEC_EOK if successful.
+ */
+int dnssec_tsig_write(dnssec_tsig_ctx_t *ctx, uint8_t *mac);
+
+/*! @} */
diff --git a/src/libdnssec/version.h b/src/libdnssec/version.h
new file mode 100644
index 0000000..90f90be
--- /dev/null
+++ b/src/libdnssec/version.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define DNSSEC_VERSION_MAJOR 3
+#define DNSSEC_VERSION_MINOR 2
+#define DNSSEC_VERSION_PATCH 0x06
+
+#define DNSSEC_VERSION_HEX ((DNSSEC_VERSION_MAJOR << 16) | \
+ (DNSSEC_VERSION_MINOR << 8) | \
+ (DNSSEC_VERSION_PATCH))
diff --git a/src/libdnssec/version.h.in b/src/libdnssec/version.h.in
new file mode 100644
index 0000000..0ac1c4e
--- /dev/null
+++ b/src/libdnssec/version.h.in
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define DNSSEC_VERSION_MAJOR @KNOT_VERSION_MAJOR@
+#define DNSSEC_VERSION_MINOR @KNOT_VERSION_MINOR@
+#define DNSSEC_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@
+
+#define DNSSEC_VERSION_HEX ((DNSSEC_VERSION_MAJOR << 16) | \
+ (DNSSEC_VERSION_MINOR << 8) | \
+ (DNSSEC_VERSION_PATCH))
diff --git a/src/libknot.pc.in b/src/libknot.pc.in
new file mode 100644
index 0000000..9ce7802
--- /dev/null
+++ b/src/libknot.pc.in
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+soname=@libknot_SONAME@
+
+Name: libknot
+Description: Knot DNS library
+URL: https://www.knot-dns.cz
+Version: @PACKAGE_VERSION@
+Requires.private: libdnssec = @PACKAGE_VERSION@
+Libs: -L${libdir} -lknot
+Libs.private: -lm @lmdb_LIBS@
+Cflags: -I${includedir}
diff --git a/src/libknot/Makefile.inc b/src/libknot/Makefile.inc
new file mode 100755
index 0000000..73a4d6e
--- /dev/null
+++ b/src/libknot/Makefile.inc
@@ -0,0 +1,136 @@
+lib_LTLIBRARIES += libknot.la
+pkgconfig_DATA += libknot.pc
+
+libknot_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(lmdb_CFLAGS) \
+ ${fuzzer_CFLAGS}
+libknot_la_LDFLAGS = $(AM_LDFLAGS) $(libknot_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS) \
+ ${fuzzer_LDFLAGS}
+libknot_la_LIBADD = libdnssec.la $(libcontrib_LIBS) $(lmdb_LIBS) $(math_LIBS)
+
+if EMBEDDED_LIBNGTCP2
+libknot_la_LIBADD += $(libembngtcp2_LIBS)
+endif EMBEDDED_LIBNGTCP2
+
+include_libknotdir = $(includedir)
+nobase_include_libknot_HEADERS = \
+ libknot/attribute.h \
+ libknot/codes.h \
+ libknot/consts.h \
+ libknot/control/control.h \
+ libknot/cookies.h \
+ libknot/descriptor.h \
+ libknot/dname.h \
+ libknot/dynarray.h \
+ libknot/endian.h \
+ libknot/errcode.h \
+ libknot/error.h \
+ libknot/libknot.h \
+ libknot/lookup.h \
+ libknot/mm_ctx.h \
+ libknot/db/db.h \
+ libknot/db/db_lmdb.h \
+ libknot/db/db_trie.h \
+ libknot/packet/compr.h \
+ libknot/packet/pkt.h \
+ libknot/packet/rrset-wire.h \
+ libknot/packet/wire.h \
+ libknot/probe/data.h \
+ libknot/probe/probe.h \
+ libknot/rdata.h \
+ libknot/rdataset.h \
+ libknot/rrset-dump.h \
+ libknot/rrset.h \
+ libknot/rrtype/dnskey.h \
+ libknot/rrtype/ds.h \
+ libknot/rrtype/naptr.h \
+ libknot/rrtype/nsec.h \
+ libknot/rrtype/nsec3.h \
+ libknot/rrtype/nsec3param.h \
+ libknot/rrtype/opt.h \
+ libknot/rrtype/rdname.h \
+ libknot/rrtype/rrsig.h \
+ libknot/rrtype/soa.h \
+ libknot/rrtype/svcb.h \
+ libknot/rrtype/tsig.h \
+ libknot/rrtype/zonemd.h \
+ libknot/tsig-op.h \
+ libknot/tsig.h \
+ libknot/wire.h \
+ libknot/yparser/yparser.h \
+ libknot/yparser/ypformat.h \
+ libknot/yparser/ypschema.h \
+ libknot/yparser/yptrafo.h \
+ libknot/version.h \
+ libknot/xdp/tcp_iobuf.h \
+ libknot/xdp.h
+
+libknot_la_SOURCES = \
+ libknot/codes.c \
+ libknot/control/control.c \
+ libknot/cookies.c \
+ libknot/descriptor.c \
+ libknot/dname.c \
+ libknot/error.c \
+ libknot/db/db_lmdb.c \
+ libknot/db/db_trie.c \
+ libknot/packet/pkt.c \
+ libknot/packet/rrset-wire.c \
+ libknot/probe/data.c \
+ libknot/probe/probe.c \
+ libknot/rdataset.c \
+ libknot/rrset-dump.c \
+ libknot/rrset.c \
+ libknot/rrtype/naptr.c \
+ libknot/rrtype/opt.c \
+ libknot/rrtype/tsig.c \
+ libknot/tsig-op.c \
+ libknot/tsig.c \
+ libknot/yparser/yparser.c \
+ libknot/yparser/ypbody.c \
+ libknot/yparser/ypformat.c \
+ libknot/yparser/ypschema.c \
+ libknot/yparser/yptrafo.c \
+ libknot/xdp/tcp_iobuf.c
+
+if EMBEDDED_LIBBPF
+libknot_la_LIBADD += $(libembbpf_LIBS)
+endif EMBEDDED_LIBBPF
+if ENABLE_XDP
+libknot_la_CPPFLAGS += $(libbpf_CFLAGS)
+libknot_la_LIBADD += $(libbpf_LIBS)
+
+nobase_include_libknot_HEADERS += \
+ libknot/xdp/bpf-consts.h \
+ libknot/xdp/eth.h \
+ libknot/xdp/msg.h \
+ libknot/xdp/tcp.h \
+ libknot/xdp/xdp.h
+
+libknot_la_SOURCES += \
+ libknot/xdp/bpf-kernel-obj.c \
+ libknot/xdp/bpf-kernel-obj.h \
+ libknot/xdp/bpf-user.c \
+ libknot/xdp/bpf-user.h \
+ libknot/xdp/eth.c \
+ libknot/xdp/msg_init.h \
+ libknot/xdp/protocols.h \
+ libknot/xdp/tcp.c \
+ libknot/xdp/xdp.c
+
+if ENABLE_QUIC
+
+libknot_la_CPPFLAGS += $(libngtcp2_CFLAGS) $(gnutls_CFLAGS)
+libknot_la_LIBADD += $(libngtcp2_LIBS) $(gnutls_LIBS)
+
+nobase_include_libknot_HEADERS += \
+ libknot/xdp/quic.h \
+ libknot/xdp/quic_conn.h
+
+libknot_la_SOURCES += \
+ libknot/xdp/quic.c \
+ libknot/xdp/quic_conn.c
+
+endif ENABLE_QUIC
+endif ENABLE_XDP
+
+DIST_SUBDIRS = libknot/xdp
diff --git a/src/libknot/attribute.h b/src/libknot/attribute.h
new file mode 100644
index 0000000..525aef3
--- /dev/null
+++ b/src/libknot/attribute.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Function and variable attributes.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+/*! \brief Library visibility macros. */
+#define _public_ __attribute__((visibility("default")))
+#define _hidden_ __attribute__((visibility("hidden")))
+
+#define _unused_ __attribute__((unused))
+
+#define _cleanup_(var) __attribute__((cleanup(var)))
+
+/*! \brief GNU C function attributes. */
+#if __GNUC__ >= 3
+#define _pure_ __attribute__ ((pure))
+#define _const_ __attribute__ ((const))
+#define _noreturn_ __attribute__ ((noreturn))
+#define _malloc_ __attribute__ ((malloc))
+#define _mustcheck_ __attribute__ ((warn_unused_result))
+#else
+#define _pure_
+#define _const_
+#define _noreturn_
+#define _malloc_
+#define _mustcheck_
+#endif
+
+/*! @} */
diff --git a/src/libknot/codes.c b/src/libknot/codes.c
new file mode 100644
index 0000000..e6b38cf
--- /dev/null
+++ b/src/libknot/codes.c
@@ -0,0 +1,129 @@
+/* Copyright (C) 2023 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 "libknot/attribute.h"
+#include "libknot/codes.h"
+#include "libknot/consts.h"
+#include "libdnssec/key.h"
+
+_public_
+const knot_lookup_t knot_opcode_names[] = {
+ { KNOT_OPCODE_QUERY, "QUERY" },
+ { KNOT_OPCODE_IQUERY, "IQUERY" },
+ { KNOT_OPCODE_STATUS, "STATUS" },
+ { KNOT_OPCODE_NOTIFY, "NOTIFY" },
+ { KNOT_OPCODE_UPDATE, "UPDATE" },
+ { 0, NULL }
+};
+
+_public_
+const knot_lookup_t knot_rcode_names[] = {
+ { KNOT_RCODE_NOERROR, "NOERROR" },
+ { KNOT_RCODE_FORMERR, "FORMERR" },
+ { KNOT_RCODE_SERVFAIL, "SERVFAIL" },
+ { KNOT_RCODE_NXDOMAIN, "NXDOMAIN" },
+ { KNOT_RCODE_NOTIMPL, "NOTIMPL" },
+ { KNOT_RCODE_REFUSED, "REFUSED" },
+ { KNOT_RCODE_YXDOMAIN, "YXDOMAIN" },
+ { KNOT_RCODE_YXRRSET, "YXRRSET" },
+ { KNOT_RCODE_NXRRSET, "NXRRSET" },
+ { KNOT_RCODE_NOTAUTH, "NOTAUTH" },
+ { KNOT_RCODE_NOTZONE, "NOTZONE" },
+ { KNOT_RCODE_BADVERS, "BADVERS" },
+ { KNOT_RCODE_BADKEY, "BADKEY" },
+ { KNOT_RCODE_BADTIME, "BADTIME" },
+ { KNOT_RCODE_BADMODE, "BADMODE" },
+ { KNOT_RCODE_BADNAME, "BADNAME" },
+ { KNOT_RCODE_BADALG, "BADALG" },
+ { KNOT_RCODE_BADTRUNC, "BADTRUNC" },
+ { KNOT_RCODE_BADCOOKIE, "BADCOOKIE" },
+ { 0, NULL }
+};
+
+_public_
+const knot_lookup_t knot_tsig_rcode_names[] = {
+ { KNOT_RCODE_BADSIG, "BADSIG" },
+ { 0, NULL }
+};
+
+_public_
+const knot_lookup_t knot_edns_ede_names[] = {
+ { KNOT_EDNS_EDE_OTHER, "Other" },
+ { KNOT_EDNS_EDE_DNSKEY_ALG, "Unsupported DNSKEY Algorithm" },
+ { KNOT_EDNS_EDE_DS_DIGEST, "Unsupported DS Digest Type" },
+ { KNOT_EDNS_EDE_STALE, "Stale Answer" },
+ { KNOT_EDNS_EDE_FORGED, "Forged Answer" },
+ { KNOT_EDNS_EDE_INDETERMINATE, "DNSSEC Indeterminate" },
+ { KNOT_EDNS_EDE_BOGUS, "DNSSEC Bogus" },
+ { KNOT_EDNS_EDE_SIG_EXPIRED, "Signature Expired" },
+ { KNOT_EDNS_EDE_SIG_NOTYET, "Signature Not Yet Valid" },
+ { KNOT_EDNS_EDE_DNSKEY_MISS, "DNSKEY Missing" },
+ { KNOT_EDNS_EDE_RRSIG_MISS, "RRSIGs Missing" },
+ { KNOT_EDNS_EDE_DNSKEY_BIT, "No Zone Key Bit Set" },
+ { KNOT_EDNS_EDE_NSEC_MISS, "NSEC Missing" },
+ { KNOT_EDNS_EDE_CACHED_ERR, "Cached Error" },
+ { KNOT_EDNS_EDE_NOT_READY, "Not Ready" },
+ { KNOT_EDNS_EDE_BLOCKED, "Blocked" },
+ { KNOT_EDNS_EDE_CENSORED, "Censored" },
+ { KNOT_EDNS_EDE_FILTERED, "Filtered" },
+ { KNOT_EDNS_EDE_PROHIBITED, "Prohibited" },
+ { KNOT_EDNS_EDE_STALE_NXD, "Stale NXDOMAIN Answer" },
+ { KNOT_EDNS_EDE_NOTAUTH, "Not Authoritative" },
+ { KNOT_EDNS_EDE_NOTSUP, "Not Supported" },
+ { KNOT_EDNS_EDE_NREACH_AUTH, "No Reachable Authority" },
+ { KNOT_EDNS_EDE_NETWORK, "Network Error" },
+ { KNOT_EDNS_EDE_INV_DATA, "Invalid Data" },
+ { KNOT_EDNS_EDE_EXPIRED_INV, "Signature Expired before Valid" },
+ { KNOT_EDNS_EDE_TOO_EARLY, "Too Early" },
+ { KNOT_EDNS_EDE_NSEC3_ITERS, "Unsupported NSEC3 Iterations Value" },
+ { KNOT_EDNS_EDE_NONCONF_POLICY, "Unable to conform to policy" },
+ { KNOT_EDNS_EDE_SYNTHESIZED, "Synthesized" },
+ { 0, NULL }
+};
+
+_public_
+const knot_lookup_t knot_dnssec_alg_names[] = {
+ { DNSSEC_KEY_ALGORITHM_DELETE, "DELETE" },
+ { DNSSEC_KEY_ALGORITHM_RSA_MD5, "RSAMD5" },
+ { DNSSEC_KEY_ALGORITHM_DH, "DH" },
+ { DNSSEC_KEY_ALGORITHM_DSA, "DSA" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA1, "RSASHA1" },
+ { DNSSEC_KEY_ALGORITHM_DSA_NSEC3_SHA1, "DSA_NSEC3_SHA1" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3, "RSASHA1_NSEC3_SHA1" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA256, "RSASHA256" },
+ { DNSSEC_KEY_ALGORITHM_RSA_SHA512, "RSASHA512" },
+ { DNSSEC_KEY_ALGORITHM_ECC_GOST, "ECC_GOST" },
+ { DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256, "ECDSAP256SHA256" },
+ { DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, "ECDSAP384SHA384" },
+ { DNSSEC_KEY_ALGORITHM_ED25519, "ED25519" },
+ { DNSSEC_KEY_ALGORITHM_ED448, "ED448" },
+ { DNSSEC_KEY_ALGORITHM_INDIRECT, "INDIRECT" },
+ { DNSSEC_KEY_ALGORITHM_PRIVATEDNS, "PRIVATEDNS" },
+ { DNSSEC_KEY_ALGORITHM_PRIVATEOID, "PRIVATEOID" },
+ { 0, NULL }
+};
+
+_public_
+const knot_lookup_t knot_svcb_param_names[] = {
+ { KNOT_SVCB_PARAM_MANDATORY, "mandatory" },
+ { KNOT_SVCB_PARAM_ALPN, "alpn" },
+ { KNOT_SVCB_PARAM_NDALPN, "no-default-alpn" },
+ { KNOT_SVCB_PARAM_PORT, "port" },
+ { KNOT_SVCB_PARAM_IPV4HINT, "ipv4hint" },
+ { KNOT_SVCB_PARAM_ECH, "ech" },
+ { KNOT_SVCB_PARAM_IPV6HINT, "ipv6hint" },
+ { 0, NULL }
+};
diff --git a/src/libknot/codes.h b/src/libknot/codes.h
new file mode 100644
index 0000000..2d3a548
--- /dev/null
+++ b/src/libknot/codes.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Some DNS-related code names.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/lookup.h"
+
+/*!
+ * \brief DNS operation code names.
+ */
+extern const knot_lookup_t knot_opcode_names[];
+
+/*!
+ * \brief DNS reply code names.
+ */
+extern const knot_lookup_t knot_rcode_names[];
+
+/*!
+ * \brief TSIG exceptions to reply code names.
+ */
+extern const knot_lookup_t knot_tsig_rcode_names[];
+
+/*!
+ * \brief EDNS EDE names.
+ */
+extern const knot_lookup_t knot_edns_ede_names[];
+
+/*!
+ * \brief DNSSEC algorithm names.
+ */
+extern const knot_lookup_t knot_dnssec_alg_names[];
+
+/*!
+ * \brief Service binding (SVCB) param types.
+ */
+extern const knot_lookup_t knot_svcb_param_names[];
+
+/*! @} */
diff --git a/src/libknot/consts.h b/src/libknot/consts.h
new file mode 100644
index 0000000..e6041b7
--- /dev/null
+++ b/src/libknot/consts.h
@@ -0,0 +1,163 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Some DNS-related constants.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+/*!
+ * \brief Basic limits for domain names (RFC 1035).
+ */
+#define KNOT_DNAME_MAXLEN 255 /*!< 1-byte maximum. */
+#define KNOT_DNAME_MAXLABELS 127 /*!< 1-char labels. */
+#define KNOT_DNAME_MAXLABELLEN 63 /*!< 2^6 - 1 */
+
+/*!
+ * \brief The longest textual dname representation.
+ *
+ * Binary: 3 x (0x3F + maximum_label) + (0x3D + rest_label) + (0x00)
+ * Textual: 3 x (maximum_label + '.') + (rest_label + '.')
+ *
+ * Each dname label byte takes 4 characters (\\DDD).
+ *
+ * KNOT_DNAME_TXT_MAXLEN = 3 x (63 x 4 + 1) + (61 x 4 + 1)
+ */
+#define KNOT_DNAME_TXT_MAXLEN 1004
+
+/*!
+ * \brief Address family numbers.
+ *
+ * https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml
+ */
+typedef enum {
+ KNOT_ADDR_FAMILY_IPV4 = 1, /*!< IP version 4. */
+ KNOT_ADDR_FAMILY_IPV6 = 2 /*!< IP version 6. */
+} knot_addr_family_t;
+
+/*!
+ * \brief DNS operation codes (OPCODEs).
+ *
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
+ */
+typedef enum {
+ KNOT_OPCODE_QUERY = 0, /*!< Standard query. */
+ KNOT_OPCODE_IQUERY = 1, /*!< Inverse query. */
+ KNOT_OPCODE_STATUS = 2, /*!< Server status request. */
+ KNOT_OPCODE_NOTIFY = 4, /*!< Notify message. */
+ KNOT_OPCODE_UPDATE = 5 /*!< Dynamic update. */
+} knot_opcode_t;
+
+/*!
+ * \brief DNS reply codes (RCODEs).
+ *
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
+ */
+typedef enum {
+ KNOT_RCODE_NOERROR = 0, /*!< No error. */
+ KNOT_RCODE_FORMERR = 1, /*!< Format error. */
+ KNOT_RCODE_SERVFAIL = 2, /*!< Server failure. */
+ KNOT_RCODE_NXDOMAIN = 3, /*!< Non-existent domain. */
+ KNOT_RCODE_NOTIMPL = 4, /*!< Not implemented. */
+ KNOT_RCODE_REFUSED = 5, /*!< Refused. */
+ KNOT_RCODE_YXDOMAIN = 6, /*!< Name should not exist. */
+ KNOT_RCODE_YXRRSET = 7, /*!< RR set should not exist. */
+ KNOT_RCODE_NXRRSET = 8, /*!< RR set does not exist. */
+ KNOT_RCODE_NOTAUTH = 9, /*!< Server not authoritative. / Query not authorized. */
+ KNOT_RCODE_NOTZONE = 10, /*!< Name is not inside zone. */
+ KNOT_RCODE_BADVERS = 16, /*!< Bad OPT Version. */
+ KNOT_RCODE_BADSIG = 16, /*!< (TSIG) Signature failure. */
+ KNOT_RCODE_BADKEY = 17, /*!< (TSIG) Key is not supported. */
+ KNOT_RCODE_BADTIME = 18, /*!< (TSIG) Signature out of time window. */
+ KNOT_RCODE_BADMODE = 19, /*!< (TKEY) Bad mode. */
+ KNOT_RCODE_BADNAME = 20, /*!< (TKEY) Duplicate key name. */
+ KNOT_RCODE_BADALG = 21, /*!< (TKEY) Algorithm not supported. */
+ KNOT_RCODE_BADTRUNC = 22, /*!< (TSIG) Bad truncation. */
+ KNOT_RCODE_BADCOOKIE = 23 /*!< Bad/missing server cookie. */
+} knot_rcode_t;
+
+/*!
+ * \brief Extended error codes as in EDNS option #15.
+ *
+ * \note The default -1 value must be filtered out before storing to uint16_t!
+ *
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml#extended-dns-error-codes
+ */
+typedef enum {
+ KNOT_EDNS_EDE_NONE = -1,
+ KNOT_EDNS_EDE_OTHER = 0,
+ KNOT_EDNS_EDE_DNSKEY_ALG = 1,
+ KNOT_EDNS_EDE_DS_DIGEST = 2,
+ KNOT_EDNS_EDE_STALE = 3,
+ KNOT_EDNS_EDE_FORGED = 4,
+ KNOT_EDNS_EDE_INDETERMINATE = 5,
+ KNOT_EDNS_EDE_BOGUS = 6,
+ KNOT_EDNS_EDE_SIG_EXPIRED = 7,
+ KNOT_EDNS_EDE_SIG_NOTYET = 8,
+ KNOT_EDNS_EDE_DNSKEY_MISS = 9,
+ KNOT_EDNS_EDE_RRSIG_MISS = 10,
+ KNOT_EDNS_EDE_DNSKEY_BIT = 11,
+ KNOT_EDNS_EDE_NSEC_MISS = 12,
+ KNOT_EDNS_EDE_CACHED_ERR = 13,
+ KNOT_EDNS_EDE_NOT_READY = 14,
+ KNOT_EDNS_EDE_BLOCKED = 15,
+ KNOT_EDNS_EDE_CENSORED = 16,
+ KNOT_EDNS_EDE_FILTERED = 17,
+ KNOT_EDNS_EDE_PROHIBITED = 18,
+ KNOT_EDNS_EDE_STALE_NXD = 19,
+ KNOT_EDNS_EDE_NOTAUTH = 20,
+ KNOT_EDNS_EDE_NOTSUP = 21,
+ KNOT_EDNS_EDE_NREACH_AUTH = 22,
+ KNOT_EDNS_EDE_NETWORK = 23,
+ KNOT_EDNS_EDE_INV_DATA = 24,
+ KNOT_EDNS_EDE_EXPIRED_INV = 25,
+ KNOT_EDNS_EDE_TOO_EARLY = 26,
+ KNOT_EDNS_EDE_NSEC3_ITERS = 27,
+ KNOT_EDNS_EDE_NONCONF_POLICY = 28,
+ KNOT_EDNS_EDE_SYNTHESIZED = 29,
+} knot_edns_ede_t;
+
+/*!
+ * \brief DNS packet section identifiers.
+ */
+typedef enum {
+ KNOT_ANSWER = 0,
+ KNOT_AUTHORITY = 1,
+ KNOT_ADDITIONAL = 2
+} knot_section_t;
+
+/*!
+ * \brief Service Binding (SVCB) Parameter Registry
+ *
+ * https://tools.ietf.org/html/draft-ietf-dnsop-svcb-https-05 // FIXME
+ */
+typedef enum {
+ KNOT_SVCB_PARAM_MANDATORY = 0,
+ KNOT_SVCB_PARAM_ALPN = 1,
+ KNOT_SVCB_PARAM_NDALPN = 2,
+ KNOT_SVCB_PARAM_PORT = 3,
+ KNOT_SVCB_PARAM_IPV4HINT = 4,
+ KNOT_SVCB_PARAM_ECH = 5,
+ KNOT_SVCB_PARAM_IPV6HINT = 6,
+} knot_svcb_param_t;
+
+/*! @} */
diff --git a/src/libknot/control/control.c b/src/libknot/control/control.c
new file mode 100644
index 0000000..8656057
--- /dev/null
+++ b/src/libknot/control/control.c
@@ -0,0 +1,568 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/control/control.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+#include "contrib/mempattern.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "contrib/ucw/mempool.h"
+#include "contrib/wire_ctx.h"
+
+/*! Size of the input and output buffers. */
+#ifndef CTL_BUFF_SIZE
+#define CTL_BUFF_SIZE (256 * 1024)
+#endif
+
+/*! Listen backlog size. */
+#define LISTEN_BACKLOG 5
+
+/*! Default socket operations timeout in milliseconds. */
+#define DEFAULT_TIMEOUT (30 * 1000)
+
+/*! Accept poll timeout in milliseconds. */
+#define ACCEPT_TIMEOUT (5 * 1000)
+
+/*! The first data item code. */
+#define DATA_CODE_OFFSET 16
+
+/*! Control context structure. */
+struct knot_ctl {
+ /*! Memory pool context. */
+ knot_mm_t mm;
+ /*! Network operations timeout. */
+ int timeout;
+ /*! Server listening socket. */
+ int listen_sock;
+ /*! Remote server/client socket. */
+ int sock;
+
+ /*! The latter read data. */
+ knot_ctl_data_t data;
+
+ /*! Write wire context. */
+ wire_ctx_t wire_out;
+ /*! Read wire context. */
+ wire_ctx_t wire_in;
+
+ /*! Write buffer. */
+ uint8_t buff_out[CTL_BUFF_SIZE];
+ /*! Read buffer. */
+ uint8_t buff_in[CTL_BUFF_SIZE];
+};
+
+static int type_to_code(knot_ctl_type_t type)
+{
+ switch (type) {
+ case KNOT_CTL_TYPE_END: return 0;
+ case KNOT_CTL_TYPE_DATA: return 1;
+ case KNOT_CTL_TYPE_EXTRA: return 2;
+ case KNOT_CTL_TYPE_BLOCK: return 3;
+ default: return -1;
+ }
+}
+
+static int code_to_type(uint8_t code)
+{
+ switch (code) {
+ case 0: return KNOT_CTL_TYPE_END;
+ case 1: return KNOT_CTL_TYPE_DATA;
+ case 2: return KNOT_CTL_TYPE_EXTRA;
+ case 3: return KNOT_CTL_TYPE_BLOCK;
+ default: return -1;
+ }
+}
+
+static bool is_data_type(knot_ctl_type_t type)
+{
+ switch (type) {
+ case KNOT_CTL_TYPE_DATA:
+ case KNOT_CTL_TYPE_EXTRA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int idx_to_code(knot_ctl_idx_t idx)
+{
+ if (idx >= KNOT_CTL_IDX__COUNT) {
+ return -1;
+ }
+
+ return DATA_CODE_OFFSET + idx;
+}
+
+static int code_to_idx(uint8_t code)
+{
+ if (code < DATA_CODE_OFFSET ||
+ code >= DATA_CODE_OFFSET + KNOT_CTL_IDX__COUNT) {
+ return -1;
+ }
+
+ return code - DATA_CODE_OFFSET;
+}
+
+static void reset_buffers(knot_ctl_t *ctx)
+{
+ ctx->wire_out = wire_ctx_init(ctx->buff_out, CTL_BUFF_SIZE);
+ ctx->wire_in = wire_ctx_init(ctx->buff_in, 0);
+}
+
+static void clean_data(knot_ctl_t *ctx)
+{
+ mp_flush(ctx->mm.ctx);
+ memzero(ctx->data, sizeof(ctx->data));
+}
+
+static void close_sock(int *sock)
+{
+ if (*sock < 0) {
+ return;
+ }
+
+ close(*sock);
+ *sock = -1;
+}
+
+_public_
+knot_ctl_t* knot_ctl_alloc(void)
+{
+ knot_ctl_t *ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ mm_ctx_mempool(&ctx->mm, MM_DEFAULT_BLKSIZE);
+ ctx->timeout = DEFAULT_TIMEOUT;
+ ctx->listen_sock = -1;
+ ctx->sock = -1;
+
+ reset_buffers(ctx);
+
+ return ctx;
+}
+
+_public_
+void knot_ctl_free(knot_ctl_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ close_sock(&ctx->listen_sock);
+ close_sock(&ctx->sock);
+
+ clean_data(ctx);
+
+ mp_delete(ctx->mm.ctx);
+
+ memzero(ctx, sizeof(*ctx));
+ free(ctx);
+}
+
+_public_
+void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ ctx->timeout = (timeout_ms > 0) ? timeout_ms : -1;
+}
+
+_public_
+int knot_ctl_bind(knot_ctl_t *ctx, const char *path)
+{
+ if (ctx == NULL || path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Prepare socket address.
+ struct sockaddr_storage addr;
+ int ret = sockaddr_set(&addr, AF_UNIX, path, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Bind the socket.
+ mode_t mode = S_IWUSR | S_IWGRP;
+ ctx->listen_sock = net_bound_socket(SOCK_STREAM, &addr, 0, mode);
+ if (ctx->listen_sock < 0) {
+ return ctx->listen_sock;
+ }
+
+ // Start listening.
+ if (listen(ctx->listen_sock, LISTEN_BACKLOG) != 0) {
+ close_sock(&ctx->listen_sock);
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_ctl_unbind(knot_ctl_t *ctx)
+{
+ if (ctx == NULL || ctx->listen_sock < 0) {
+ return;
+ }
+
+ // Remove the control socket file.
+ struct sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+ if (getsockname(ctx->listen_sock, (struct sockaddr *)&addr, &addr_len) == 0) {
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ if (sockaddr_tostr(addr_str, sizeof(addr_str), &addr) > 0) {
+ (void)unlink(addr_str);
+ }
+ }
+
+ // Close the listening socket.
+ close_sock(&ctx->listen_sock);
+}
+
+_public_
+int knot_ctl_accept(knot_ctl_t *ctx)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_ctl_close(ctx);
+
+ // Control interface.
+ struct pollfd pfd = { .fd = ctx->listen_sock, .events = POLLIN };
+ int ret = poll(&pfd, 1, ACCEPT_TIMEOUT);
+ if (ret <= 0) {
+ return (ret == 0) ? KNOT_ETIMEOUT : knot_map_errno();
+ }
+
+ int client = net_accept(ctx->listen_sock, NULL);
+ if (client < 0) {
+ return client;
+ }
+
+ ctx->sock = client;
+
+ reset_buffers(ctx);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_ctl_connect(knot_ctl_t *ctx, const char *path)
+{
+ if (ctx == NULL || path == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Prepare socket address.
+ struct sockaddr_storage addr;
+ int ret = sockaddr_set(&addr, AF_UNIX, path, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Connect to socket.
+ ctx->sock = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ if (ctx->sock < 0) {
+ return ctx->sock;
+ }
+
+ reset_buffers(ctx);
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_ctl_close(knot_ctl_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ close_sock(&ctx->sock);
+}
+
+static int ensure_output(knot_ctl_t *ctx, uint16_t len)
+{
+ wire_ctx_t *w = &ctx->wire_out;
+
+ // Check for enough available room in the output buffer.
+ size_t available = wire_ctx_available(w);
+ if (available >= len) {
+ return KNOT_EOK;
+ }
+
+ // Flush the buffer.
+ int ret = net_stream_send(ctx->sock, w->wire, wire_ctx_offset(w),
+ ctx->timeout);
+ if (ret < 0) {
+ return ret;
+ }
+
+ *w = wire_ctx_init(w->wire, CTL_BUFF_SIZE);
+
+ return KNOT_EOK;
+}
+
+static int send_item(knot_ctl_t *ctx, uint8_t code, const char *data, bool flush)
+{
+ wire_ctx_t *w = &ctx->wire_out;
+
+ // Write the control block code.
+ int ret = ensure_output(ctx, sizeof(uint8_t));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ wire_ctx_write_u8(w, code);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+
+ // Control block data is optional.
+ if (data != NULL) {
+ // Get the data length.
+ size_t data_len = strlen(data);
+ if (data_len > UINT16_MAX) {
+ return KNOT_ERANGE;
+ }
+
+ // Write the data length.
+ ret = ensure_output(ctx, sizeof(uint16_t));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ wire_ctx_write_u16(w, data_len);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+
+ // Write the data.
+ ret = ensure_output(ctx, data_len);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ wire_ctx_write(w, (uint8_t *)data, data_len);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+ }
+
+ // Send finalized buffer.
+ if (flush && wire_ctx_offset(w) > 0) {
+ ret = net_stream_send(ctx->sock, w->wire, wire_ctx_offset(w),
+ ctx->timeout);
+ if (ret < 0) {
+ return ret;
+ }
+
+ *w = wire_ctx_init(w->wire, CTL_BUFF_SIZE);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_ctl_send(knot_ctl_t *ctx, knot_ctl_type_t type, knot_ctl_data_t *data)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Get the type code.
+ int code = type_to_code(type);
+ if (code == -1) {
+ return KNOT_EINVAL;
+ }
+
+ // Send unit type.
+ int ret = send_item(ctx, code, NULL, !is_data_type(type));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Send unit data.
+ if (is_data_type(type) && data != NULL) {
+ // Send all non-empty data items.
+ for (knot_ctl_idx_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
+ const char *value = (*data)[i];
+ if (value == NULL) {
+ continue;
+ }
+
+ ret = send_item(ctx, idx_to_code(i), value, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int ensure_input(knot_ctl_t *ctx, uint16_t len)
+{
+ wire_ctx_t *w = &ctx->wire_in;
+
+ // Check for enough available room in the input buffer.
+ size_t available = wire_ctx_available(w);
+ if (available >= len) {
+ return KNOT_EOK;
+ }
+
+ // Move unprocessed data to the beginning of the buffer.
+ memmove(w->wire, w->wire + wire_ctx_offset(w), available);
+
+ // Receive enough data.
+ while (available < len) {
+ int ret = net_stream_recv(ctx->sock, w->wire + available,
+ CTL_BUFF_SIZE - available,
+ ctx->timeout);
+ if (ret < 0) {
+ return ret;
+ }
+ assert(ret > 0);
+ available += ret;
+ }
+
+ ctx->wire_in = wire_ctx_init(w->wire, available);
+
+ return KNOT_EOK;
+}
+
+static int receive_item_code(knot_ctl_t *ctx, uint8_t *code)
+{
+ wire_ctx_t *w = &ctx->wire_in;
+
+ // Read the type.
+ int ret = ensure_input(ctx, sizeof(uint8_t));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *code = wire_ctx_read_u8(w);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+
+ return KNOT_EOK;
+}
+
+static int receive_item_value(knot_ctl_t *ctx, char **value)
+{
+ wire_ctx_t *w = &ctx->wire_in;
+
+ // Read value length.
+ int ret = ensure_input(ctx, sizeof(uint16_t));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ uint16_t data_len = wire_ctx_read_u16(w);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+
+ // Read the value.
+ ret = ensure_input(ctx, data_len);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *value = mm_alloc(&ctx->mm, data_len + 1);
+ if (*value == NULL) {
+ return KNOT_ENOMEM;
+ }
+ wire_ctx_read(w, *value, data_len);
+ if (w->error != KNOT_EOK) {
+ return w->error;
+ }
+ (*value)[data_len] = '\0';
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_ctl_receive(knot_ctl_t *ctx, knot_ctl_type_t *type, knot_ctl_data_t *data)
+{
+ if (ctx == NULL || type == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t *w = &ctx->wire_in;
+
+ // Reset output variables.
+ *type = KNOT_CTL_TYPE_END;
+ clean_data(ctx);
+
+ // Read data units until end of message.
+ bool have_type = false;
+ while (true) {
+ uint8_t code;
+ int ret = receive_item_code(ctx, &code);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Process unit type.
+ int current_type = code_to_type(code);
+ if (current_type != -1) {
+ if (have_type) {
+ // Revert parsed type.
+ wire_ctx_skip(w, -sizeof(uint8_t));
+ assert(w->error == KNOT_EOK);
+ break;
+ }
+
+ // Set the unit type.
+ *type = current_type;
+
+ if (is_data_type(current_type)) {
+ have_type = true;
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ // Check for data item code.
+ int idx = code_to_idx(code);
+ if (idx == -1) {
+ return KNOT_EINVAL;
+ }
+
+ // Store the item data value.
+ ret = receive_item_value(ctx, (char **)&ctx->data[idx]);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Set the output data.
+ if (data != NULL) {
+ memcpy(*data, ctx->data, sizeof(*data));
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/control/control.h b/src/libknot/control/control.h
new file mode 100644
index 0000000..1d3dcd1
--- /dev/null
+++ b/src/libknot/control/control.h
@@ -0,0 +1,161 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief A server control interface.
+ *
+ * \addtogroup ctl
+ * @{
+ */
+
+#pragma once
+
+/*! Control data item indexes. */
+typedef enum {
+ KNOT_CTL_IDX_CMD = 0, /*!< Control command name. */
+ KNOT_CTL_IDX_FLAGS, /*!< Control command flags. */
+ KNOT_CTL_IDX_ERROR, /*!< Error message. */
+ KNOT_CTL_IDX_SECTION, /*!< Configuration section name. */
+ KNOT_CTL_IDX_ITEM, /*!< Configuration item name. */
+ KNOT_CTL_IDX_ID, /*!< Configuration item identifier. */
+ KNOT_CTL_IDX_ZONE, /*!< Zone name. */
+ KNOT_CTL_IDX_OWNER, /*!< Zone record owner */
+ KNOT_CTL_IDX_TTL, /*!< Zone record TTL. */
+ KNOT_CTL_IDX_TYPE, /*!< Zone record type name. */
+ KNOT_CTL_IDX_DATA, /*!< Configuration item/zone record data. */
+ KNOT_CTL_IDX_FILTER, /*!< An option or a filter for output data processing. */
+ KNOT_CTL_IDX__COUNT, /*!< The number of data items. */
+} knot_ctl_idx_t;
+
+/*! Control unit types. */
+typedef enum {
+ KNOT_CTL_TYPE_END, /*!< End of message, cache flushed. */
+ KNOT_CTL_TYPE_DATA, /*!< Data unit, cached. */
+ KNOT_CTL_TYPE_EXTRA, /*!< Extra value data unit, cached. */
+ KNOT_CTL_TYPE_BLOCK, /*!< End of data block, cache flushed. */
+} knot_ctl_type_t;
+
+/*! Control input/output string data. */
+typedef const char* knot_ctl_data_t[KNOT_CTL_IDX__COUNT];
+
+/*! A control context. */
+struct knot_ctl;
+typedef struct knot_ctl knot_ctl_t;
+
+/*!
+ * Allocates a control context.
+ *
+ * \return Control context.
+ */
+knot_ctl_t* knot_ctl_alloc(void);
+
+/*!
+ * Deallocates a control context.
+ *
+ * \param[in] ctx Control context.
+ */
+void knot_ctl_free(knot_ctl_t *ctx);
+
+/*!
+ * Sets the timeout for socket operations.
+ *
+ * Default value is 30 seconds.
+ *
+ * \param[in] ctx Control context.
+ * \param[in] timeout_ms Timeout in milliseconds (0 for infinity).
+ */
+void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms);
+
+/*!
+ * Binds a specified UNIX socket path.
+ *
+ * \note Server operation.
+ *
+ * \param[in] ctx Control context.
+ * \param[in] path Control UNIX socket path.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_ctl_bind(knot_ctl_t *ctx, const char *path);
+
+/*!
+ * Unbinds a control socket.
+ *
+ * \note Server operation.
+ *
+ * \param[in] ctx Control context.
+ */
+void knot_ctl_unbind(knot_ctl_t *ctx);
+
+/*!
+ * Connects to a specified UNIX socket path.
+ *
+ * \note Client operation.
+ *
+ * \param[in] ctx Control context.
+ * \param[in] path Control UNIX socket path.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_ctl_connect(knot_ctl_t *ctx, const char *path);
+
+/*!
+ * Waits for an incoming connection.
+ *
+ * \note Server operation.
+ *
+ * \param[in] ctx Control context.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval KNOT_ETIMEOUT if no connection accepted during ACCEPT_TIMEOUT interval.
+ * \retval KNOT_E* if error.
+ */
+int knot_ctl_accept(knot_ctl_t *ctx);
+
+/*!
+ * Closes the remote connections.
+ *
+ * \note Applies to both server and client.
+ *
+ * \param[in] ctx Control context.
+ */
+void knot_ctl_close(knot_ctl_t *ctx);
+
+/*!
+ * Sends one control unit.
+ *
+ * \param[in] ctx Control context.
+ * \param[in] type Unit type to send.
+ * \param[in] data Data unit to send (optional, ignored if non-data type).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_ctl_send(knot_ctl_t *ctx, knot_ctl_type_t type, knot_ctl_data_t *data);
+
+/*!
+ * Receives one control unit.
+ *
+ * \param[in] ctx Control context.
+ * \param[out] type Received unit type.
+ * \param[out] data Received data unit (optional).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_ctl_receive(knot_ctl_t *ctx, knot_ctl_type_t *type, knot_ctl_data_t *data);
+
+/*! @} */
diff --git a/src/libknot/cookies.c b/src/libknot/cookies.c
new file mode 100644
index 0000000..e7b3354
--- /dev/null
+++ b/src/libknot/cookies.c
@@ -0,0 +1,170 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "libknot/attribute.h"
+#include "libknot/cookies.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "contrib/string.h"
+#include "contrib/sockaddr.h"
+#include "contrib/openbsd/siphash.h"
+
+_public_
+int knot_edns_cookie_client_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_params_t *params)
+{
+ if (out == NULL || params == NULL || params->server_addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ SIPHASH_CTX ctx;
+ assert(sizeof(params->secret) == sizeof(SIPHASH_KEY));
+ SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret);
+
+ size_t addr_len = 0;
+ void *addr = sockaddr_raw(params->server_addr, &addr_len);
+ assert(addr);
+ SipHash24_Update(&ctx, addr, addr_len);
+
+ uint64_t hash = SipHash24_End(&ctx);
+ memcpy(out->data, &hash, sizeof(hash));
+ out->len = sizeof(hash);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params)
+{
+ if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ return KNOT_EINVAL;
+ }
+
+ knot_edns_cookie_t ref;
+ int ret = knot_edns_cookie_client_generate(&ref, params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ assert(ref.len == KNOT_EDNS_COOKIE_CLNT_SIZE);
+
+ ret = const_time_memcmp(cc->data, ref.data, KNOT_EDNS_COOKIE_CLNT_SIZE);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int cookie_server_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params)
+{
+ assert(out && params);
+
+ if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE ||
+ params->client_addr == NULL) {
+ return KNOT_EINVAL;
+ } else if (out->data[0] != KNOT_EDNS_COOKIE_VERSION) {
+ return KNOT_ENOTSUP;
+ }
+
+ SIPHASH_CTX ctx;
+ assert(sizeof(params->secret) == sizeof(SIPHASH_KEY));
+ SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret);
+
+ SipHash24_Update(&ctx, cc->data, cc->len);
+ SipHash24_Update(&ctx, out->data, out->len);
+
+ size_t addr_len = 0;
+ void *addr = sockaddr_raw(params->client_addr, &addr_len);
+ assert(addr);
+ SipHash24_Update(&ctx, addr, addr_len);
+
+ uint64_t hash = SipHash24_End(&ctx);
+ memcpy(out->data + out->len, &hash, sizeof(hash));
+ out->len += sizeof(hash);
+
+ return KNOT_EOK;
+
+}
+
+_public_
+int knot_edns_cookie_server_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params)
+{
+ if (out == NULL || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ out->data[0] = params->version;
+ out->data[1] = 0; /* reserved */
+ out->data[2] = 0; /* reserved */
+ out->data[3] = 0; /* reserved */
+ out->len = 4;
+
+ uint32_t now = htobe32(params->timestamp);
+ memcpy(&out->data[out->len], &now, sizeof(now));
+ out->len += sizeof(now);
+
+ return cookie_server_generate(out, cc, params);
+}
+
+_public_
+int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params)
+{
+ if (sc == NULL || sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint32_t cookie_time;
+ memcpy(&cookie_time, &sc->data[4], sizeof(cookie_time));
+ cookie_time = be32toh(cookie_time);
+
+ uint32_t min_time = params->timestamp - params->lifetime_before;
+ uint32_t max_time = params->timestamp + params->lifetime_after;
+ if (cookie_time < min_time || cookie_time > max_time) {
+ return KNOT_ERANGE;
+ }
+
+ const int fixed_len = 8;
+ knot_edns_cookie_t ref;
+ memcpy(ref.data, sc->data, fixed_len);
+ ref.len = fixed_len;
+
+ int ret = cookie_server_generate(&ref, cc, params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (sc->len != ref.len) {
+ return KNOT_EINVAL;
+ }
+
+ ret = const_time_memcmp(sc->data + fixed_len, ref.data + fixed_len,
+ sc->len - fixed_len);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/cookies.h b/src/libknot/cookies.h
new file mode 100644
index 0000000..b39ca93
--- /dev/null
+++ b/src/libknot/cookies.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief DNS cookies processing.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+#include "libknot/rrtype/opt.h"
+
+#define KNOT_EDNS_COOKIE_SECRET_SIZE 16
+
+/*!
+ * \brief DNS Cookie parameters needed to generate/check the cookie value.
+ *
+ * \note Client address is not used for the client cookie generation/check.
+ * \note Server address is not used for the server cookie generation/check.
+ */
+typedef struct {
+ uint8_t version; /*!< Server cookie version to generate. */
+ uint32_t timestamp; /*!< [s] Server cookie generate or check time. */
+ uint32_t lifetime_before; /*!< [s] Server cookie lifetime in the past. */
+ uint32_t lifetime_after; /*!< [s] Server cookie lifetime in the future. */
+ const struct sockaddr_storage *client_addr; /*!< Client socket address. */
+ const struct sockaddr_storage *server_addr; /*!< Server socket address. */
+ uint8_t secret[KNOT_EDNS_COOKIE_SECRET_SIZE]; /*!< Cookie secret data. */
+} knot_edns_cookie_params_t;
+
+/*!
+ * \brief Generate a client cookie using given parameters.
+ *
+ * \param out Generated client cookie.
+ * \param params Client cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_client_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Check whether client cookie was generated using given parameters.
+ *
+ * \param cc Client cookie that should be checked.
+ * \param params Client cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Generate a server cookie using given parameters.
+ *
+ * \param out Generated server cookie.
+ * \param cc Client cookie parameter.
+ * \param params Server cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_server_generate(knot_edns_cookie_t *out,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params);
+
+/*!
+ * \brief Check whether server cookie was generated using given parameters.
+ *
+ * \param sc Server cookie that should be checked.
+ * \param cc Client cookie parameter.
+ * \param params Server cookie parameters.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_EINVAL
+ */
+int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_params_t *params);
+
+/*! @} */
diff --git a/src/libknot/db/db.h b/src/libknot/db/db.h
new file mode 100644
index 0000000..044a2b9
--- /dev/null
+++ b/src/libknot/db/db.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Structures for binary data handling.
+ *
+ * \addtogroup db
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/mm_ctx.h"
+
+enum {
+ /* Database flags */
+
+ KNOT_DB_RDONLY = 1 << 0, /*!< Read only. */
+ KNOT_DB_SORTED = 1 << 1, /*!< Sorted output. */
+
+ /* Operations */
+
+ KNOT_DB_NOOP = 1 << 2, /*!< No operation. */
+ KNOT_DB_FIRST = 1 << 3, /*!< First entry. */
+ KNOT_DB_LAST = 1 << 4, /*!< Last entry. */
+ KNOT_DB_NEXT = 1 << 5, /*!< Next entry. */
+ KNOT_DB_PREV = 1 << 6, /*!< Previous entry. */
+ KNOT_DB_LEQ = 1 << 7, /*!< Lesser or equal. */
+ KNOT_DB_GEQ = 1 << 8 /*!< Greater or equal. */
+};
+
+typedef void knot_db_t;
+typedef void knot_db_iter_t;
+
+typedef struct knot_db_val {
+ void *data;
+ size_t len;
+} knot_db_val_t;
+
+typedef struct knot_db_txn {
+ knot_db_t *db;
+ void *txn;
+} knot_db_txn_t;
+
+typedef struct knot_db_api {
+ const char *name;
+
+ /* Context operations */
+
+ int (*init)(knot_db_t **db, knot_mm_t *mm, void *opts);
+ void (*deinit)(knot_db_t *db);
+
+ /* Transactions */
+
+ int (*txn_begin)(knot_db_t *db, knot_db_txn_t *txn, unsigned flags);
+ int (*txn_commit)(knot_db_txn_t *txn);
+ void (*txn_abort)(knot_db_txn_t *txn);
+
+ /* Data access */
+
+ int (*count)(knot_db_txn_t *txn);
+ int (*clear)(knot_db_txn_t *txn);
+ int (*find)(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags);
+ int (*insert)(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags);
+ int (*del)(knot_db_txn_t *txn, knot_db_val_t *key);
+
+ /* Iteration */
+
+ knot_db_iter_t *(*iter_begin)(knot_db_txn_t *txn, unsigned flags);
+ knot_db_iter_t *(*iter_seek)(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags);
+ knot_db_iter_t *(*iter_next)(knot_db_iter_t *iter);
+ int (*iter_key)(knot_db_iter_t *iter, knot_db_val_t *key);
+ int (*iter_val)(knot_db_iter_t *iter, knot_db_val_t *val);
+ void (*iter_finish)(knot_db_iter_t *iter);
+} knot_db_api_t;
+
+/*! @} */
diff --git a/src/libknot/db/db_lmdb.c b/src/libknot/db/db_lmdb.c
new file mode 100644
index 0000000..b5eae09
--- /dev/null
+++ b/src/libknot/db/db_lmdb.c
@@ -0,0 +1,578 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/db/db_lmdb.h"
+#include "contrib/files.h"
+#include "contrib/mempattern.h"
+
+#include <lmdb.h>
+
+/* Defines */
+#define LMDB_DIR_MODE 0770
+#define LMDB_FILE_MODE 0660
+
+_public_ const unsigned KNOT_DB_LMDB_NOTLS = MDB_NOTLS;
+_public_ const unsigned KNOT_DB_LMDB_RDONLY = MDB_RDONLY;
+_public_ const unsigned KNOT_DB_LMDB_INTEGERKEY = MDB_INTEGERKEY;
+_public_ const unsigned KNOT_DB_LMDB_NOSYNC = MDB_NOSYNC;
+_public_ const unsigned KNOT_DB_LMDB_WRITEMAP = MDB_WRITEMAP;
+_public_ const unsigned KNOT_DB_LMDB_MAPASYNC = MDB_MAPASYNC;
+_public_ const unsigned KNOT_DB_LMDB_DUPSORT = MDB_DUPSORT;
+
+struct lmdb_env
+{
+ bool shared;
+ MDB_dbi dbi;
+ MDB_env *env;
+ knot_mm_t *pool;
+};
+
+/*!
+ * \brief Convert error code returned by LMDB to Knot DNS error code.
+ *
+ * LMDB defines own error codes but uses additional ones from libc:
+ * - LMDB errors do not conflict with Knot DNS ones.
+ * - Significant LMDB errors are mapped to Knot DNS ones.
+ * - Standard errors are converted to negative value to match Knot DNS mapping.
+ */
+static int lmdb_error_to_knot(int error)
+{
+ if (error == MDB_SUCCESS) {
+ return KNOT_EOK;
+ }
+
+ if (error == MDB_NOTFOUND) {
+ return KNOT_ENOENT;
+ }
+
+ if (error == MDB_TXN_FULL) {
+ return KNOT_ELIMIT;
+ }
+
+ if (error == MDB_MAP_FULL || error == ENOSPC) {
+ return KNOT_ESPACE;
+ }
+
+ return -abs(error);
+}
+
+/*! \brief Set the environment map size.
+ * \note This also sets the maximum database size, see mdb_env_set_mapsize
+ */
+static int set_mapsize(MDB_env *env, size_t map_size)
+{
+ long page_size = sysconf(_SC_PAGESIZE);
+ if (page_size <= 0) {
+ return KNOT_ERROR;
+ }
+
+ /* Round to page size. */
+ map_size = (map_size / page_size) * page_size;
+ int ret = mdb_env_set_mapsize(env, map_size);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Close the database. */
+static void dbase_close(struct lmdb_env *env)
+{
+ mdb_dbi_close(env->env, env->dbi);
+ if (!env->shared) {
+ mdb_env_close(env->env);
+ }
+}
+
+/*! \brief Open database environment. */
+static int dbase_open_env(struct lmdb_env *env, struct knot_db_lmdb_opts *opts)
+{
+ MDB_env *mdb_env = NULL;
+ int ret = mdb_env_create(&mdb_env);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ ret = make_dir(opts->path, LMDB_DIR_MODE, true);
+ if (ret != KNOT_EOK) {
+ mdb_env_close(mdb_env);
+ return ret;
+ }
+
+ ret = set_mapsize(mdb_env, opts->mapsize);
+ if (ret != KNOT_EOK) {
+ mdb_env_close(mdb_env);
+ return ret;
+ }
+
+ ret = mdb_env_set_maxdbs(mdb_env, opts->maxdbs);
+ if (ret != MDB_SUCCESS) {
+ mdb_env_close(mdb_env);
+ return lmdb_error_to_knot(ret);
+ }
+
+ ret = mdb_env_set_maxreaders(mdb_env, opts->maxreaders);
+ if (ret != MDB_SUCCESS) {
+ mdb_env_close(mdb_env);
+ return lmdb_error_to_knot(ret);
+ }
+
+#ifdef __OpenBSD__
+ /*
+ * Enforce that MDB_WRITEMAP is set.
+ *
+ * MDB assumes a unified buffer cache.
+ *
+ * See https://www.openldap.org/pub/hyc/mdm-paper.pdf section 3.1,
+ * references 17, 18, and 19.
+ *
+ * From Howard Chu: "This requirement can be relaxed in the
+ * current version of the library. If you create the environment
+ * with the MDB_WRITEMAP option then all reads and writes are
+ * performed using mmap, so the file buffer cache is irrelevant.
+ * Of course then you lose the protection that the read-only
+ * map offers."
+ */
+ opts->flags.env |= MDB_WRITEMAP;
+#endif
+
+ ret = mdb_env_open(mdb_env, opts->path, opts->flags.env, LMDB_FILE_MODE);
+ if (ret != MDB_SUCCESS) {
+ mdb_env_close(mdb_env);
+ return lmdb_error_to_knot(ret);
+ }
+
+ /* Keep the environment pointer. */
+ env->env = mdb_env;
+
+ return KNOT_EOK;
+}
+
+static int dbase_open(struct lmdb_env *env, struct knot_db_lmdb_opts *opts)
+{
+ unsigned flags = 0;
+ if (opts->flags.env & KNOT_DB_LMDB_RDONLY) {
+ flags = MDB_RDONLY;
+ }
+
+ /* Open the database. */
+ MDB_txn *txn = NULL;
+ int ret = mdb_txn_begin(env->env, NULL, flags, &txn);
+ if (ret == MDB_READERS_FULL) {
+ int cleared = 0;
+ ret = mdb_reader_check(env->env, &cleared);
+ if (ret == MDB_SUCCESS) {
+ ret = mdb_txn_begin(env->env, NULL, flags, &txn);
+ }
+ }
+ if (ret != MDB_SUCCESS) {
+ mdb_env_close(env->env);
+ return lmdb_error_to_knot(ret);
+ }
+
+ ret = mdb_dbi_open(txn, opts->dbname, opts->flags.db | MDB_CREATE, &env->dbi);
+ if (ret != MDB_SUCCESS) {
+ mdb_txn_abort(txn);
+ mdb_env_close(env->env);
+ return lmdb_error_to_knot(ret);
+ }
+
+ ret = mdb_txn_commit(txn);
+ if (ret != MDB_SUCCESS) {
+ mdb_env_close(env->env);
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+static int init(knot_db_t **db_ptr, knot_mm_t *mm, void *arg)
+{
+ if (db_ptr == NULL || arg == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct lmdb_env *env = mm_alloc(mm, sizeof(struct lmdb_env));
+ if (env == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(env, 0, sizeof(struct lmdb_env));
+ env->pool = mm;
+
+ /* Open new environment. */
+ struct lmdb_env *old_env = *db_ptr;
+ if (old_env == NULL) {
+ int ret = dbase_open_env(env, (struct knot_db_lmdb_opts *)arg);
+ if (ret != KNOT_EOK) {
+ mm_free(mm, env);
+ return ret;
+ }
+ } else {
+ /* Shared environment, this instance just owns the DBI. */
+ env->env = old_env->env;
+ env->shared = true;
+ }
+
+ /* Open the database. */
+ int ret = dbase_open(env, (struct knot_db_lmdb_opts *)arg);
+ if (ret != KNOT_EOK) {
+ mm_free(mm, env);
+ return ret;
+ }
+
+ /* Store the new environment. */
+ *db_ptr = env;
+
+ return KNOT_EOK;
+}
+
+static void deinit(knot_db_t *db)
+{
+ if (db) {
+ struct lmdb_env *env = db;
+
+ dbase_close(env);
+ mm_free(env->pool, env);
+ }
+}
+
+_public_
+int knot_db_lmdb_txn_begin(knot_db_t *db, knot_db_txn_t *txn, knot_db_txn_t *parent,
+ unsigned flags)
+{
+ txn->db = db;
+ txn->txn = NULL;
+
+ unsigned txn_flags = 0;
+ if (flags & KNOT_DB_RDONLY) {
+ txn_flags |= MDB_RDONLY;
+ }
+
+ MDB_txn *parent_txn = (parent != NULL) ? (MDB_txn *)parent->txn : NULL;
+
+ struct lmdb_env *env = db;
+ int ret = mdb_txn_begin(env->env, parent_txn, txn_flags, (MDB_txn **)&txn->txn);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags)
+{
+ return knot_db_lmdb_txn_begin(db, txn, NULL, flags);
+}
+
+static int txn_commit(knot_db_txn_t *txn)
+{
+ int ret = mdb_txn_commit((MDB_txn *)txn->txn);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+static void txn_abort(knot_db_txn_t *txn)
+{
+ mdb_txn_abort((MDB_txn *)txn->txn);
+}
+
+static int count(knot_db_txn_t *txn)
+{
+ struct lmdb_env *env = txn->db;
+
+ MDB_stat stat;
+ int ret = mdb_stat(txn->txn, env->dbi, &stat);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return stat.ms_entries;
+}
+
+static int clear(knot_db_txn_t *txn)
+{
+ struct lmdb_env *env = txn->db;
+
+ int ret = mdb_drop(txn->txn, env->dbi, 0);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+static knot_db_iter_t *iter_set(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags)
+{
+ MDB_cursor *cursor = iter;
+
+ MDB_cursor_op op = MDB_SET;
+ switch(flags) {
+ case KNOT_DB_NOOP: return cursor;
+ case KNOT_DB_FIRST: op = MDB_FIRST; break;
+ case KNOT_DB_LAST: op = MDB_LAST; break;
+ case KNOT_DB_NEXT: op = MDB_NEXT; break;
+ case KNOT_DB_PREV: op = MDB_PREV; break;
+ case KNOT_DB_LEQ:
+ case KNOT_DB_GEQ: op = MDB_SET_RANGE; break;
+ default: break;
+ }
+
+ MDB_val db_key = { 0, NULL };
+ if (key) {
+ db_key.mv_data = key->data;
+ db_key.mv_size = key->len;
+ }
+ MDB_val unused_key = { 0, NULL }, unused_val = { 0, NULL };
+
+ int ret = mdb_cursor_get(cursor, key ? &db_key : &unused_key, &unused_val, op);
+
+ /* LEQ is not supported in LMDB, workaround using GEQ. */
+ if (flags == KNOT_DB_LEQ && key) {
+ /* Searched key is after the last key. */
+ if (ret != MDB_SUCCESS) {
+ return iter_set(iter, NULL, KNOT_DB_LAST);
+ }
+ /* If the searched key != matched, get previous. */
+ if ((key->len != db_key.mv_size) ||
+ (memcmp(key->data, db_key.mv_data, key->len) != 0)) {
+ return iter_set(iter, NULL, KNOT_DB_PREV);
+ }
+ }
+
+ if (ret != MDB_SUCCESS) {
+ mdb_cursor_close(cursor);
+ return NULL;
+ }
+
+ return cursor;
+}
+
+static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags)
+{
+ struct lmdb_env *env = txn->db;
+ MDB_cursor *cursor = NULL;
+
+ int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor);
+ if (ret != MDB_SUCCESS) {
+ return NULL;
+ }
+
+ /* Clear sorted flag, as it's always sorted. */
+ flags &= ~KNOT_DB_SORTED;
+
+ return iter_set(cursor, NULL, (flags == 0) ? KNOT_DB_FIRST : flags);
+}
+
+static knot_db_iter_t *iter_next(knot_db_iter_t *iter)
+{
+ return iter_set(iter, NULL, KNOT_DB_NEXT);
+}
+
+_public_
+int knot_db_lmdb_iter_del(knot_db_iter_t *iter)
+{
+ MDB_cursor *cursor = iter;
+
+ int ret = mdb_cursor_del(cursor, 0);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+static int iter_key(knot_db_iter_t *iter, knot_db_val_t *key)
+{
+ MDB_cursor *cursor = iter;
+
+ MDB_val mdb_key, mdb_val;
+ int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ key->data = mdb_key.mv_data;
+ key->len = mdb_key.mv_size;
+ return KNOT_EOK;
+}
+
+static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val)
+{
+ MDB_cursor *cursor = iter;
+
+ MDB_val mdb_key, mdb_val;
+ int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ val->data = mdb_val.mv_data;
+ val->len = mdb_val.mv_size;
+ return KNOT_EOK;
+}
+
+static void iter_finish(knot_db_iter_t *iter)
+{
+ if (iter == NULL) {
+ return;
+ }
+
+ MDB_cursor *cursor = iter;
+ mdb_cursor_close(cursor);
+}
+
+static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
+{
+ knot_db_iter_t *iter = iter_begin(txn, KNOT_DB_NOOP);
+ if (iter == NULL) {
+ return KNOT_ERROR;
+ }
+
+ int ret = KNOT_EOK;
+ if (iter_set(iter, key, flags) == NULL) {
+ return KNOT_ENOENT;
+ } else {
+ ret = iter_val(iter, val);
+ }
+
+ iter_finish(iter);
+ return ret;
+}
+
+static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
+{
+ struct lmdb_env *env = txn->db;
+
+ MDB_val db_key = { key->len, key->data };
+ MDB_val data = { val->len, val->data };
+
+ /* Reserve if only size is declared. */
+ unsigned mdb_flags = 0;
+ if (val->len > 0 && val->data == NULL) {
+ mdb_flags |= MDB_RESERVE;
+ }
+
+ int ret = mdb_put(txn->txn, env->dbi, &db_key, &data, mdb_flags);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ /* Update the result. */
+ val->data = data.mv_data;
+ val->len = data.mv_size;
+
+ return KNOT_EOK;
+}
+
+static int del(knot_db_txn_t *txn, knot_db_val_t *key)
+{
+ struct lmdb_env *env = txn->db;
+ MDB_val db_key = { key->len, key->data };
+
+ int ret = mdb_del(txn->txn, env->dbi, &db_key, NULL);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_db_lmdb_del_exact(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val)
+{
+ struct lmdb_env *env = txn->db;
+ MDB_val db_key = { key->len, key->data };
+ MDB_val data = { val->len, val->data };
+
+ int ret = mdb_del(txn->txn, env->dbi, &db_key, &data);
+ if (ret != MDB_SUCCESS) {
+ return lmdb_error_to_knot(ret);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+size_t knot_db_lmdb_get_mapsize(knot_db_t *db)
+{
+ struct lmdb_env *env = db;
+ MDB_envinfo info;
+ if (mdb_env_info(env->env, &info) != MDB_SUCCESS) {
+ return 0;
+ }
+
+ return info.me_mapsize;
+}
+
+// you should SUM all the usages of DBs sharing one mapsize
+_public_
+size_t knot_db_lmdb_get_usage(knot_db_t *db)
+{
+ struct lmdb_env *env = db;
+ knot_db_txn_t txn;
+ knot_db_lmdb_txn_begin(db, &txn, NULL, KNOT_DB_RDONLY);
+ MDB_stat st;
+ if (mdb_stat(txn.txn, env->dbi, &st) != MDB_SUCCESS) {
+ txn_abort(&txn);
+ return 0;
+ }
+ txn_abort(&txn);
+
+ size_t pgs_used = st.ms_branch_pages + st.ms_leaf_pages + st.ms_overflow_pages;
+
+ return (pgs_used * st.ms_psize);
+}
+
+_public_
+const char *knot_db_lmdb_get_path(knot_db_t *db)
+{
+ struct lmdb_env *env = db;
+
+ static const char *path;
+ if (mdb_env_get_path(env->env, &path) == 0) {
+ return path;
+ } else {
+ return "";
+ }
+}
+
+_public_
+const knot_db_api_t *knot_db_lmdb_api(void)
+{
+ static const knot_db_api_t api = {
+ "lmdb",
+ init, deinit,
+ txn_begin, txn_commit, txn_abort,
+ count, clear, find, insert, del,
+ iter_begin, iter_set, iter_next, iter_key, iter_val, iter_finish
+ };
+
+ return &api;
+}
diff --git a/src/libknot/db/db_lmdb.h b/src/libknot/db/db_lmdb.h
new file mode 100644
index 0000000..ef8e664
--- /dev/null
+++ b/src/libknot/db/db_lmdb.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup db
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/db/db.h"
+
+/* Defines. */
+#define KNOT_DB_LMDB_MAPSIZE (100 * 1024 * 1024)
+
+/* LMDB specific flags. */
+extern const unsigned KNOT_DB_LMDB_NOTLS;
+extern const unsigned KNOT_DB_LMDB_RDONLY;
+extern const unsigned KNOT_DB_LMDB_INTEGERKEY;
+extern const unsigned KNOT_DB_LMDB_NOSYNC;
+extern const unsigned KNOT_DB_LMDB_WRITEMAP;
+extern const unsigned KNOT_DB_LMDB_MAPASYNC;
+extern const unsigned KNOT_DB_LMDB_DUPSORT;
+
+/* Native options. */
+struct knot_db_lmdb_opts {
+ const char *path; /*!< Database environment path. */
+ const char *dbname; /*!< Database name (or NULL). */
+ size_t mapsize; /*!< Environment map size. */
+ unsigned maxdbs; /*!< Maximum number of databases in the env. */
+ unsigned maxreaders; /*!< Maximum number of concurrent readers */
+ struct {
+ unsigned env; /*!< Environment flags. */
+ unsigned db; /*!< Database flags. */
+ } flags;
+};
+
+/* Default options. */
+#define KNOT_DB_LMDB_OPTS_INITIALIZER { \
+ NULL, NULL, \
+ KNOT_DB_LMDB_MAPSIZE, \
+ 0, \
+ 126, /* = contrib/lmdb/mdb.c DEFAULT_READERS */ \
+ { 0, 0 } \
+}
+
+const knot_db_api_t *knot_db_lmdb_api(void);
+
+/* LMDB specific operations. */
+int knot_db_lmdb_del_exact(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val);
+int knot_db_lmdb_txn_begin(knot_db_t *db, knot_db_txn_t *txn, knot_db_txn_t *parent,
+ unsigned flags);
+int knot_db_lmdb_iter_del(knot_db_iter_t *iter);
+size_t knot_db_lmdb_get_mapsize(knot_db_t *db);
+size_t knot_db_lmdb_get_usage(knot_db_t *db);
+const char *knot_db_lmdb_get_path(knot_db_t *db);
+
+/*! @} */
diff --git a/src/libknot/db/db_trie.c b/src/libknot/db/db_trie.c
new file mode 100644
index 0000000..1bdba8b
--- /dev/null
+++ b/src/libknot/db/db_trie.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/db/db_trie.h"
+#include "contrib/qp-trie/trie.h"
+#include "contrib/mempattern.h"
+
+static int init(knot_db_t **db, knot_mm_t *mm, void *arg)
+{
+ if (db == NULL || arg == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ _unused_ struct knot_db_trie_opts *opts = arg;
+ trie_t *trie = trie_create(mm);
+ if (!trie) {
+ return KNOT_ENOMEM;
+ }
+
+ *db = trie;
+
+ return KNOT_EOK;
+}
+
+static void deinit(knot_db_t *db)
+{
+ trie_free((trie_t *)db);
+}
+
+static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags)
+{
+ txn->txn = (void *)(size_t)flags;
+ txn->db = db;
+ return KNOT_EOK; /* N/A */
+}
+
+static int txn_commit(knot_db_txn_t *txn)
+{
+ return KNOT_EOK;
+}
+
+static void txn_abort(knot_db_txn_t *txn)
+{
+}
+
+static int count(knot_db_txn_t *txn)
+{
+ return trie_weight((trie_t *)txn->db);
+}
+
+static int clear(knot_db_txn_t *txn)
+{
+ trie_clear((trie_t *)txn->db);
+
+ return KNOT_EOK;
+}
+
+static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
+{
+ trie_val_t *ret = trie_get_try((trie_t *)txn->db, key->data, key->len);
+ if (ret == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ val->data = *ret;
+ val->len = sizeof(trie_val_t); /* Trie doesn't support storing length. */
+ return KNOT_EOK;
+}
+
+static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags)
+{
+ /* No flags supported. */
+ if (flags != 0) {
+ return KNOT_ENOTSUP;
+ }
+
+ trie_val_t *ret = trie_get_ins((trie_t *)txn->db, key->data, key->len);
+ if (ret == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ *ret = val->data;
+ return KNOT_EOK;
+}
+
+static int del(knot_db_txn_t *txn, knot_db_val_t *key)
+{
+ return trie_del((trie_t *)txn->db, key->data, key->len, NULL);
+}
+
+static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags)
+{
+ flags &= ~KNOT_DB_SORTED;
+
+ /* No operations other than begin are supported right now. */
+ if (flags != 0) {
+ return NULL;
+ }
+
+ return trie_it_begin((trie_t *)txn->db);
+}
+
+static knot_db_iter_t *iter_seek(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags)
+{
+ assert(0);
+ return NULL; /* ENOTSUP */
+}
+
+static knot_db_iter_t *iter_next(knot_db_iter_t *iter)
+{
+ trie_it_next((trie_it_t *)iter);
+ if (trie_it_finished((trie_it_t *)iter)) {
+ trie_it_free((trie_it_t *)iter);
+ return NULL;
+ }
+
+ return iter;
+}
+
+static int iter_key(knot_db_iter_t *iter, knot_db_val_t *val)
+{
+ val->data = (void *)trie_it_key((trie_it_t *)iter, &val->len);
+ if (val->data == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ return KNOT_EOK;
+}
+
+static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val)
+{
+ trie_val_t *ret = trie_it_val((trie_it_t *)iter);
+ if (ret == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ val->data = *ret;
+ val->len = sizeof(trie_val_t);
+ return KNOT_EOK;
+}
+
+static void iter_finish(knot_db_iter_t *iter)
+{
+ trie_it_free((trie_it_t *)iter);
+}
+
+_public_
+const knot_db_api_t *knot_db_trie_api(void)
+{
+ static const knot_db_api_t api = {
+ "trie",
+ init, deinit,
+ txn_begin, txn_commit, txn_abort,
+ count, clear, find, insert, del,
+ iter_begin, iter_seek, iter_next, iter_key, iter_val, iter_finish
+ };
+
+ return &api;
+}
diff --git a/src/libknot/db/db_trie.h b/src/libknot/db/db_trie.h
new file mode 100644
index 0000000..fee4722
--- /dev/null
+++ b/src/libknot/db/db_trie.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup db
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/db/db.h"
+
+/* Native options. */
+struct knot_db_trie_opts {
+ unsigned unused;
+};
+
+/* Default options. */
+#define KNOT_DB_TRIE_OPTS_INITIALIZER { \
+ 0 \
+}
+
+const knot_db_api_t *knot_db_trie_api(void);
+
+/*! @} */
diff --git a/src/libknot/descriptor.c b/src/libknot/descriptor.c
new file mode 100644
index 0000000..5ff2714
--- /dev/null
+++ b/src/libknot/descriptor.c
@@ -0,0 +1,425 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "libknot/attribute.h"
+#include "libknot/descriptor.h"
+
+/*!
+ * \brief Table with DNS classes.
+ */
+static const char* dns_classes[] = {
+ [KNOT_CLASS_IN] = "IN",
+ [KNOT_CLASS_CH] = "CH",
+ [KNOT_CLASS_NONE] = "NONE",
+ [KNOT_CLASS_ANY] = "ANY"
+};
+
+/*!
+ * \brief RR type descriptors.
+ */
+static const knot_rdata_descriptor_t rdata_descriptors[] = {
+ [0] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, NULL },
+ [KNOT_RRTYPE_A] = { { 4, KNOT_RDATA_WF_END }, "A" },
+ [KNOT_RRTYPE_NS] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "NS" },
+ [KNOT_RRTYPE_CNAME] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "CNAME" },
+ [KNOT_RRTYPE_SOA] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ 20, KNOT_RDATA_WF_END }, "SOA" },
+ [KNOT_RRTYPE_NULL] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "NULL" },
+ [KNOT_RRTYPE_PTR] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "PTR" },
+ [KNOT_RRTYPE_HINFO] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "HINFO" },
+ [KNOT_RRTYPE_MINFO] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MINFO" },
+ [KNOT_RRTYPE_MX] = { { 2, KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MX" },
+ [KNOT_RRTYPE_TXT] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "TXT" },
+ [KNOT_RRTYPE_RP] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "RP" },
+ [KNOT_RRTYPE_AFSDB] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "AFSDB" },
+ [KNOT_RRTYPE_RT] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "RT" },
+ [KNOT_RRTYPE_SIG] = { { 18, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "SIG" },
+ [KNOT_RRTYPE_KEY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "KEY" },
+ [KNOT_RRTYPE_AAAA] = { { 16, KNOT_RDATA_WF_END }, "AAAA" },
+ [KNOT_RRTYPE_LOC] = { { 16, KNOT_RDATA_WF_END }, "LOC" },
+ [KNOT_RRTYPE_SRV] = { { 6, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "SRV" },
+ [KNOT_RRTYPE_NAPTR] = { { KNOT_RDATA_WF_NAPTR_HEADER,
+ KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "NAPTR" },
+ [KNOT_RRTYPE_KX] = { { 2, KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_END }, "KX" },
+ [KNOT_RRTYPE_CERT] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "CERT" },
+ [KNOT_RRTYPE_DNAME] = { { KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_END }, "DNAME" },
+ [KNOT_RRTYPE_OPT] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "OPT" },
+ [KNOT_RRTYPE_APL] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "APL" },
+ [KNOT_RRTYPE_DS] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "DS" },
+ [KNOT_RRTYPE_SSHFP] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "SSHFP" },
+ [KNOT_RRTYPE_IPSECKEY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "IPSECKEY" },
+ [KNOT_RRTYPE_RRSIG] = { { 18, KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "RRSIG" },
+ [KNOT_RRTYPE_NSEC] = { { KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "NSEC" },
+ [KNOT_RRTYPE_DNSKEY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "DNSKEY" },
+ [KNOT_RRTYPE_DHCID] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "DHCID" },
+ [KNOT_RRTYPE_NSEC3] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "NSEC3" },
+ [KNOT_RRTYPE_NSEC3PARAM] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "NSEC3PARAM" },
+ [KNOT_RRTYPE_TLSA] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "TLSA" },
+ [KNOT_RRTYPE_SMIMEA] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "SMIMEA" },
+ [KNOT_RRTYPE_CDS] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "CDS" },
+ [KNOT_RRTYPE_CDNSKEY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "CDNSKEY" },
+ [KNOT_RRTYPE_OPENPGPKEY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "OPENPGPKEY" },
+ [KNOT_RRTYPE_CSYNC] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "CSYNC" },
+ [KNOT_RRTYPE_ZONEMD] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "ZONEMD" },
+ [KNOT_RRTYPE_SVCB] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "SVCB" },
+ [KNOT_RRTYPE_HTTPS] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "HTTPS" },
+ [KNOT_RRTYPE_SPF] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "SPF" },
+ [KNOT_RRTYPE_NID] = { { 10, KNOT_RDATA_WF_END }, "NID" },
+ [KNOT_RRTYPE_L32] = { { 6, KNOT_RDATA_WF_END }, "L32" },
+ [KNOT_RRTYPE_L64] = { { 10, KNOT_RDATA_WF_END }, "L64" },
+ [KNOT_RRTYPE_LP] = { { 2, KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_END }, "LP" },
+ [KNOT_RRTYPE_EUI48] = { { 6, KNOT_RDATA_WF_END }, "EUI48" },
+ [KNOT_RRTYPE_EUI64] = { { 8, KNOT_RDATA_WF_END }, "EUI64" },
+ [KNOT_RRTYPE_TKEY] = { { KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "TKEY" },
+ [KNOT_RRTYPE_TSIG] = { { KNOT_RDATA_WF_FIXED_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "TSIG" },
+ [KNOT_RRTYPE_IXFR] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "IXFR" },
+ [KNOT_RRTYPE_AXFR] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "AXFR" },
+ [KNOT_RRTYPE_ANY] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "ANY" },
+ [KNOT_RRTYPE_URI] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "URI" },
+ [KNOT_RRTYPE_CAA] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "CAA" },
+};
+
+#define MAX_RRTYPE sizeof(rdata_descriptors) / sizeof(knot_rdata_descriptor_t) - 1
+
+/*!
+ * \brief Some (OBSOLETE) RR type descriptors.
+ */
+static const knot_rdata_descriptor_t obsolete_rdata_descriptors[] = {
+ [0] = { { KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, NULL },
+ [KNOT_RRTYPE_MD] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MD" },
+ [KNOT_RRTYPE_MF] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MF" },
+ [KNOT_RRTYPE_MB] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MB" },
+ [KNOT_RRTYPE_MG] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MG" },
+ [KNOT_RRTYPE_MR] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "MR" },
+ [KNOT_RRTYPE_PX] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_END }, "PX" },
+ [KNOT_RRTYPE_NXT] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ KNOT_RDATA_WF_REMAINDER,
+ KNOT_RDATA_WF_END }, "NXT" },
+};
+
+_public_
+const knot_rdata_descriptor_t *knot_get_rdata_descriptor(const uint16_t type)
+{
+ if (type <= MAX_RRTYPE && rdata_descriptors[type].type_name != NULL) {
+ return &rdata_descriptors[type];
+ } else {
+ return &rdata_descriptors[0];
+ }
+}
+
+_public_
+const knot_rdata_descriptor_t *knot_get_obsolete_rdata_descriptor(const uint16_t type)
+{
+ if (type <= KNOT_RRTYPE_NXT &&
+ obsolete_rdata_descriptors[type].type_name != NULL) {
+ return &obsolete_rdata_descriptors[type];
+ } else {
+ return &obsolete_rdata_descriptors[0];
+ }
+}
+
+_public_
+int knot_rrtype_to_string(const uint16_t rrtype,
+ char *out,
+ const size_t out_len)
+{
+ if (out == NULL) {
+ return -1;
+ }
+
+ int ret;
+
+ const knot_rdata_descriptor_t *descr = knot_get_rdata_descriptor(rrtype);
+
+ if (descr->type_name != NULL) {
+ ret = snprintf(out, out_len, "%s", descr->type_name);
+ } else {
+ ret = snprintf(out, out_len, "TYPE%u", rrtype);
+ }
+
+ if (ret <= 0 || (size_t)ret >= out_len) {
+ return -1;
+ } else {
+ return ret;
+ }
+}
+
+_public_
+int knot_rrtype_from_string(const char *name, uint16_t *num)
+{
+ if (name == NULL || num == NULL) {
+ return -1;
+ }
+
+ int i;
+ char *end;
+ unsigned long n;
+
+ // Try to find name in descriptors table.
+ for (i = 0; i <= MAX_RRTYPE; i++) {
+ if (rdata_descriptors[i].type_name != NULL &&
+ strcasecmp(rdata_descriptors[i].type_name, name) == 0) {
+ *num = i;
+ return 0;
+ }
+ }
+
+ // Type name must begin with TYPE.
+ if (strncasecmp(name, "TYPE", 4) != 0) {
+ return -1;
+ } else {
+ name += 4;
+ }
+
+ // The rest must be a number.
+ n = strtoul(name, &end, 10);
+ if (end == name || *end != '\0' || n > UINT16_MAX) {
+ return -1;
+ }
+
+ *num = n;
+ return 0;
+}
+
+_public_
+int knot_rrclass_to_string(const uint16_t rrclass,
+ char *out,
+ const size_t out_len)
+{
+ if (out == NULL) {
+ return -1;
+ }
+
+ int ret;
+
+ if (rrclass <= KNOT_CLASS_ANY && dns_classes[rrclass] != NULL) {
+ ret = snprintf(out, out_len, "%s", dns_classes[rrclass]);
+ } else {
+ ret = snprintf(out, out_len, "CLASS%u", rrclass);
+ }
+
+ if (ret <= 0 || (size_t)ret >= out_len) {
+ return -1;
+ } else {
+ return ret;
+ }
+}
+
+_public_
+int knot_rrclass_from_string(const char *name, uint16_t *num)
+{
+ if (name == NULL || num == NULL) {
+ return -1;
+ }
+
+ int i;
+ char *end;
+ unsigned long n;
+
+ // Try to find the name in classes table.
+ for (i = 0; i <= KNOT_CLASS_ANY; i++) {
+ if (dns_classes[i] != NULL &&
+ strcasecmp(dns_classes[i], name) == 0) {
+ *num = i;
+ return 0;
+ }
+ }
+
+ // Class name must begin with CLASS.
+ if (strncasecmp(name, "CLASS", 5) != 0) {
+ return -1;
+ } else {
+ name += 5;
+ }
+
+ // The rest must be a number.
+ n = strtoul(name, &end, 10);
+ if (end == name || *end != '\0' || n > UINT16_MAX) {
+ return -1;
+ }
+
+ *num = n;
+ return 0;
+}
+
+_public_
+int knot_rrtype_is_metatype(const uint16_t type)
+{
+ return type == KNOT_RRTYPE_SIG ||
+ type == KNOT_RRTYPE_OPT ||
+ type == KNOT_RRTYPE_TKEY ||
+ type == KNOT_RRTYPE_TSIG ||
+ type == KNOT_RRTYPE_IXFR ||
+ type == KNOT_RRTYPE_AXFR ||
+ type == KNOT_RRTYPE_ANY;
+}
+
+_public_
+int knot_rrtype_is_dnssec(const uint16_t type)
+{
+ return type == KNOT_RRTYPE_DNSKEY ||
+ type == KNOT_RRTYPE_RRSIG ||
+ type == KNOT_RRTYPE_NSEC ||
+ type == KNOT_RRTYPE_NSEC3 ||
+ type == KNOT_RRTYPE_NSEC3PARAM ||
+ type == KNOT_RRTYPE_CDNSKEY ||
+ type == KNOT_RRTYPE_CDS;
+}
+
+_public_
+int knot_rrtype_additional_needed(const uint16_t type)
+{
+ return type == KNOT_RRTYPE_NS ||
+ type == KNOT_RRTYPE_MX ||
+ type == KNOT_RRTYPE_SRV ||
+ type == KNOT_RRTYPE_SVCB ||
+ type == KNOT_RRTYPE_HTTPS;
+}
+
+_public_
+bool knot_rrtype_should_be_lowercased(const uint16_t type)
+{
+ return type == KNOT_RRTYPE_NS ||
+ type == KNOT_RRTYPE_MD ||
+ type == KNOT_RRTYPE_MF ||
+ type == KNOT_RRTYPE_CNAME ||
+ type == KNOT_RRTYPE_SOA ||
+ type == KNOT_RRTYPE_MB ||
+ type == KNOT_RRTYPE_MG ||
+ type == KNOT_RRTYPE_MR ||
+ type == KNOT_RRTYPE_PTR ||
+ type == KNOT_RRTYPE_MINFO ||
+ type == KNOT_RRTYPE_MX ||
+ type == KNOT_RRTYPE_RP ||
+ type == KNOT_RRTYPE_AFSDB ||
+ type == KNOT_RRTYPE_RT ||
+ type == KNOT_RRTYPE_SIG ||
+ type == KNOT_RRTYPE_PX ||
+ type == KNOT_RRTYPE_NXT ||
+ type == KNOT_RRTYPE_NAPTR ||
+ type == KNOT_RRTYPE_KX ||
+ type == KNOT_RRTYPE_SRV ||
+ type == KNOT_RRTYPE_DNAME ||
+ type == KNOT_RRTYPE_RRSIG;
+}
+
+_public_
+int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len)
+{
+ if (out == NULL) {
+ return -1;
+ }
+
+ const char *name = NULL;
+
+ switch (code) {
+ case 1: name = "LLQ"; break;
+ case 2: name = "UL"; break;
+ case 3: name = "NSID"; break;
+ case 5: name = "DAU"; break;
+ case 6: name = "DHU"; break;
+ case 7: name = "N3U"; break;
+ case 8: name = "EDNS-CLIENT-SUBNET"; break;
+ case 9: name = "EDNS-EXPIRE"; break;
+ case 10: name = "COOKIE"; break;
+ case 11: name = "EDNS-TCP-KEEPALIVE"; break;
+ case 12: name = "PADDING"; break;
+ case 13: name = "CHAIN"; break;
+ case 14: name = "EDNS-KEY-TAG"; break;
+ }
+
+ int ret;
+
+ if (name != NULL) {
+ ret = snprintf(out, out_len, "%s", name);
+ } else {
+ ret = snprintf(out, out_len, "CODE%u", code);
+ }
+
+ if (ret <= 0 || (size_t)ret >= out_len) {
+ return -1;
+ } else {
+ return ret;
+ }
+}
diff --git a/src/libknot/descriptor.h b/src/libknot/descriptor.h
new file mode 100644
index 0000000..14cd3b3
--- /dev/null
+++ b/src/libknot/descriptor.h
@@ -0,0 +1,307 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief DNS resource record descriptions.
+ *
+ * \addtogroup rr
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#define KNOT_MAX_RDATA_BLOCKS 8
+#define KNOT_MAX_RDATA_DNAMES 2 // Update this when defining new RR types!
+
+/*!
+ * \brief Resource record class codes.
+ *
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
+ */
+enum knot_rr_class {
+ KNOT_CLASS_IN = 1,
+ KNOT_CLASS_CH = 3,
+ KNOT_CLASS_NONE = 254,
+ KNOT_CLASS_ANY = 255
+};
+
+/*!
+ * \brief Resource record type constants.
+ *
+ * References:
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
+ * RFC 3597#4
+ *
+ * METATYPE: Contains DNS data that can't be in a zone file.
+ * QTYPE: Specifies DNS query type; can't be in a zone file.
+ */
+enum knot_rr_type {
+ KNOT_RRTYPE_A = 1, /*!< An IPv4 host address. */
+ KNOT_RRTYPE_NS = 2, /*!< An authoritative name server. */
+
+ KNOT_RRTYPE_CNAME = 5, /*!< The canonical name for an alias. */
+ KNOT_RRTYPE_SOA = 6, /*!< The start of a zone of authority. */
+
+ KNOT_RRTYPE_NULL = 10, /*!< METATYPE. Used in RFC 8145. */
+
+ KNOT_RRTYPE_PTR = 12, /*!< A domain name pointer. */
+ KNOT_RRTYPE_HINFO = 13, /*!< A host information. */
+ KNOT_RRTYPE_MINFO = 14, /*!< A mailbox information. */
+ KNOT_RRTYPE_MX = 15, /*!< Mail exchange. */
+ KNOT_RRTYPE_TXT = 16, /*!< Text strings. */
+ KNOT_RRTYPE_RP = 17, /*!< For responsible person. */
+ KNOT_RRTYPE_AFSDB = 18, /*!< For AFS Data Base location. */
+
+ KNOT_RRTYPE_RT = 21, /*!< For route through. */
+
+ KNOT_RRTYPE_SIG = 24, /*!< METATYPE. Transaction signature. */
+ KNOT_RRTYPE_KEY = 25, /*!< For security key. */
+
+ KNOT_RRTYPE_AAAA = 28, /*!< IPv6 address. */
+ KNOT_RRTYPE_LOC = 29, /*!< Location information. */
+
+ KNOT_RRTYPE_SRV = 33, /*!< Server selection. */
+
+ KNOT_RRTYPE_NAPTR = 35, /*!< Naming authority pointer . */
+ KNOT_RRTYPE_KX = 36, /*!< Key exchanger. */
+ KNOT_RRTYPE_CERT = 37, /*!< Certificate record. */
+
+ KNOT_RRTYPE_DNAME = 39, /*!< Delegation name. */
+
+ KNOT_RRTYPE_OPT = 41, /*!< METATYPE. Option for EDNS. */
+ KNOT_RRTYPE_APL = 42, /*!< Address prefix list. */
+ KNOT_RRTYPE_DS = 43, /*!< Delegation signer. */
+ KNOT_RRTYPE_SSHFP = 44, /*!< SSH public key fingerprint. */
+ KNOT_RRTYPE_IPSECKEY = 45, /*!< IPSEC key. */
+ KNOT_RRTYPE_RRSIG = 46, /*!< DNSSEC signature. */
+ KNOT_RRTYPE_NSEC = 47, /*!< Next-secure record. */
+ KNOT_RRTYPE_DNSKEY = 48, /*!< DNS key. */
+ KNOT_RRTYPE_DHCID = 49, /*!< DHCP identifier. */
+ KNOT_RRTYPE_NSEC3 = 50, /*!< NSEC version 3. */
+ KNOT_RRTYPE_NSEC3PARAM = 51, /*!< NSEC3 parameters. */
+ KNOT_RRTYPE_TLSA = 52, /*!< DANE record. */
+ KNOT_RRTYPE_SMIMEA = 53, /*!< S/MIME cert association. */
+
+ KNOT_RRTYPE_CDS = 59, /*!< Child delegation signer. */
+ KNOT_RRTYPE_CDNSKEY = 60, /*!< Child DNS key. */
+ KNOT_RRTYPE_OPENPGPKEY = 61, /*!< OpenPGP Key. */
+ KNOT_RRTYPE_CSYNC = 62, /*!< Child-To-Parent synchronization. */
+ KNOT_RRTYPE_ZONEMD = 63, /*!< Message digest for DNS zone. */
+ KNOT_RRTYPE_SVCB = 64, /*!< Service Binding. */
+ KNOT_RRTYPE_HTTPS = 65, /*!< HTTPS Binding. */
+
+ KNOT_RRTYPE_SPF = 99, /*!< Sender policy framework. */
+
+ KNOT_RRTYPE_NID = 104, /*!< Node identifier. */
+ KNOT_RRTYPE_L32 = 105, /*!< 32-bit network locator. */
+ KNOT_RRTYPE_L64 = 106, /*!< 64-bit network locator. */
+ KNOT_RRTYPE_LP = 107, /*!< Subnetwork name. */
+ KNOT_RRTYPE_EUI48 = 108, /*!< 48-bit extended unique identifier. */
+ KNOT_RRTYPE_EUI64 = 109, /*!< 64-bit extended unique identifier. */
+
+ KNOT_RRTYPE_TKEY = 249, /*!< METATYPE. Transaction key. */
+ KNOT_RRTYPE_TSIG = 250, /*!< METATYPE. Transaction signature. */
+ KNOT_RRTYPE_IXFR = 251, /*!< QTYPE. Incremental zone transfer. */
+ KNOT_RRTYPE_AXFR = 252, /*!< QTYPE. Authoritative zone transfer. */
+
+ KNOT_RRTYPE_ANY = 255, /*!< QTYPE. Any record. */
+ KNOT_RRTYPE_URI = 256, /*!< Uniform resource identifier. */
+ KNOT_RRTYPE_CAA = 257, /*!< Certification authority restriction. */
+};
+
+/*!
+ * \brief Some (OBSOLETE) resource record type constants.
+ *
+ * References:
+ * https://www.iana.org/assignments/dns-parameters/dns-parameters.xml
+ * RFC 3597#4
+ *
+ * \note These records can contain compressed domain name in rdata so
+ * it is important to know the position of them during transfers.
+ */
+enum knot_obsolete_rr_type {
+ KNOT_RRTYPE_MD = 3,
+ KNOT_RRTYPE_MF = 4,
+ KNOT_RRTYPE_MB = 7,
+ KNOT_RRTYPE_MG = 8,
+ KNOT_RRTYPE_MR = 9,
+ KNOT_RRTYPE_PX = 26,
+ KNOT_RRTYPE_NXT = 30
+};
+
+/*!
+ * \brief Constants characterising the wire format of RDATA items.
+ */
+enum knot_rdata_wireformat {
+ /*!< Dname must not be compressed. */
+ KNOT_RDATA_WF_FIXED_DNAME = -10,
+ /*!< Dname can be both compressed and decompressed. */
+ KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ /*!< Dname can be decompressed. */
+ KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ /*!< Initial part of NAPTR record before dname. */
+ KNOT_RDATA_WF_NAPTR_HEADER,
+ /*!< Final part of a record. */
+ KNOT_RDATA_WF_REMAINDER,
+ /*!< The last descriptor in array. */
+ KNOT_RDATA_WF_END = 0
+};
+
+/*!
+ * \brief Structure describing rdata.
+ */
+typedef struct {
+ /*!< Item types describing rdata. */
+ const int block_types[KNOT_MAX_RDATA_BLOCKS];
+ /*!< RR type name. */
+ const char *type_name;
+} knot_rdata_descriptor_t;
+
+/*!
+ * \brief Gets rdata descriptor for given RR name.
+ *
+ * \param type Mnemonic of RR type whose descriptor should be returned.
+ *
+ * \retval RR descriptor for given name, NULL descriptor if
+ * unknown type.
+ */
+const knot_rdata_descriptor_t *knot_get_rdata_descriptor(const uint16_t type);
+
+/*!
+ * \brief Gets rdata descriptor for given RR name (obsolete version).
+ *
+ * \param type Mnemonic of RR type whose descriptor should be returned.
+ *
+ * \retval RR descriptor for given name, NULL descriptor if
+ * unknown type.
+ */
+const knot_rdata_descriptor_t *knot_get_obsolete_rdata_descriptor(const uint16_t type);
+
+/*!
+ * \brief Converts numeric type representation to mnemonic string.
+ *
+ * \param rrtype Type RR type code to be converted.
+ * \param out Output buffer.
+ * \param out_len Length of the output buffer.
+ *
+ * \retval Length of output string.
+ * \retval -1 if error.
+ */
+int knot_rrtype_to_string(const uint16_t rrtype,
+ char *out,
+ const size_t out_len);
+
+/*!
+ * \brief Converts mnemonic string representation of a type to numeric one.
+ *
+ * \param name Mnemonic string to be converted.
+ * \param num Output variable.
+ *
+ * \retval 0 if OK.
+ * \retval -1 if error.
+ */
+int knot_rrtype_from_string(const char *name, uint16_t *num);
+
+/*!
+ * \brief Converts numeric class representation to the string one.
+ *
+ * \param rrclass Class code to be converted.
+ * \param out Output buffer.
+ * \param out_len Length of the output buffer.
+ *
+ * \retval Length of output string.
+ * \retval -1 if error.
+ */
+int knot_rrclass_to_string(const uint16_t rrclass,
+ char *out,
+ const size_t out_len);
+
+/*!
+ * \brief Converts string representation of a class to numeric one.
+ *
+ * \param name Mnemonic string to be converted.
+ * \param num Output variable.
+ *
+ * \retval 0 if OK.
+ * \retval -1 if error.
+ */
+int knot_rrclass_from_string(const char *name, uint16_t *num);
+
+/*!
+ * \brief Checks if given item is one of metatypes or qtypes.
+ *
+ * \param type Item value.
+ *
+ * \retval > 0 if YES.
+ * \retval 0 if NO.
+ */
+int knot_rrtype_is_metatype(const uint16_t type);
+
+/*!
+ * \brief Checks if given item is one of the DNSSEC types.
+ *
+ * \param type Item value.
+ *
+ * \retval > 0 if YES.
+ * \retval 0 if NO.
+ */
+int knot_rrtype_is_dnssec(const uint16_t type);
+
+/*!
+ * \brief Checks whether the given type requires additional processing.
+ *
+ * Only MX, NS and SRV types require additional processing.
+ *
+ * \param type Type to check.
+ *
+ * \retval <> 0 if additional processing is needed for \a qtype.
+ * \retval 0 otherwise.
+ */
+int knot_rrtype_additional_needed(const uint16_t type);
+
+/*!
+ * \brief Checks whether the RDATA domain names should be lowercased in
+ * canonical format of RRSet of the given type.
+ *
+ * Types that should be lowercased are according to RFC 4034, Section 6.2,
+ * except for NSEC (updated by RFC 6840, Section 5.1) and A6 (not supported).
+ *
+ * \param type RRSet type to check.
+ *
+ * \retval true If RDATA dnames for type should be lowercased in canonical format.
+ * \retval false Otherwise.
+ */
+bool knot_rrtype_should_be_lowercased(const uint16_t type);
+
+/*!
+ * \brief Translates option code to string.
+ *
+ * \param code Code of the option to translate.
+ * \param out Buffer for the output string.
+ * \param out_len The available size of the buffer.
+ *
+ * \retval Length of output string.
+ * \retval -1 if error.
+ */
+int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len);
+
+/*! @} */
diff --git a/src/libknot/dname.c b/src/libknot/dname.c
new file mode 100644
index 0000000..31b8a5f
--- /dev/null
+++ b/src/libknot/dname.c
@@ -0,0 +1,801 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+#include "libknot/packet/wire.h"
+#include "contrib/ctype.h"
+#include "contrib/mempattern.h"
+#include "contrib/tolower.h"
+
+static bool label_is_equal(const uint8_t *lb1, const uint8_t *lb2, bool no_case)
+{
+ if (*lb1 != *lb2) {
+ return false;
+ }
+
+ if (no_case) {
+ uint8_t len = *lb1;
+ for (uint8_t i = 1; i <= len; i++) {
+ if (knot_tolower(lb1[i]) != knot_tolower(lb2[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ } else {
+ return memcmp(lb1 + 1, lb2 + 1, *lb1) == 0;
+ }
+}
+
+/*!
+ * \brief Align name end-to-end and return number of common suffix labels.
+ *
+ * \param d1 Domain name1.
+ * \param d1_labels Number of labels in d1.
+ * \param d2 Domain name2.
+ * \param d2_labels Number of labels in d2.
+ */
+static int dname_align(const uint8_t **d1, uint8_t d1_labels,
+ const uint8_t **d2, uint8_t d2_labels)
+{
+ assert(d1 && d2);
+
+ for (unsigned j = d1_labels; j < d2_labels; ++j) {
+ *d2 = knot_wire_next_label(*d2, NULL);
+ }
+
+ for (unsigned j = d2_labels; j < d1_labels; ++j) {
+ *d1 = knot_wire_next_label(*d1, NULL);
+ }
+
+ return (d1_labels < d2_labels) ? d1_labels : d2_labels;
+}
+
+_public_
+int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp,
+ const uint8_t *pkt)
+{
+ if (name == NULL || name == endp) {
+ return KNOT_EINVAL;
+ }
+
+ int wire_len = 0;
+ int name_len = 1; /* Keep \x00 terminal label in advance. */
+ bool is_compressed = false;
+
+ while (*name != '\0') {
+ /* Check bounds (must have at least 2 octets remaining). */
+ if (name + 2 > endp) {
+ return KNOT_EMALF;
+ }
+
+ if (knot_wire_is_pointer(name)) {
+ /* Check that the pointer points backwards
+ * otherwise it could result in infinite loop
+ */
+ if (pkt == NULL) {
+ return KNOT_EINVAL;
+ }
+ uint16_t ptr = knot_wire_get_pointer(name);
+ if (ptr >= (name - pkt)) {
+ return KNOT_EMALF;
+ }
+
+ name = pkt + ptr; /* Hop to compressed label */
+ if (!is_compressed) { /* Measure compressed size only */
+ wire_len += sizeof(uint16_t);
+ is_compressed = true;
+ }
+ } else {
+ /* Check label length. */
+ if (*name > KNOT_DNAME_MAXLABELLEN) {
+ return KNOT_EMALF;
+ }
+ /* Check if there's enough space. */
+ int lblen = *name + 1;
+ if (name_len + lblen > KNOT_DNAME_MAXLEN) {
+ return KNOT_EMALF;
+ }
+ /* Update wire size only for uncompressed part. */
+ name_len += lblen;
+ if (!is_compressed) {
+ wire_len += lblen;
+ }
+ /* Hop to next label. */
+ name += lblen;
+ }
+
+ /* Check bounds (must have at least 1 octet). */
+ if (name + 1 > endp) {
+ return KNOT_EMALF;
+ }
+ }
+
+ if (!is_compressed) { /* Terminal label. */
+ wire_len += 1;
+ }
+
+ return wire_len;
+}
+
+_public_
+size_t knot_dname_store(knot_dname_storage_t dst, const knot_dname_t *name)
+{
+ if (dst == NULL || name == NULL) {
+ return 0;
+ }
+
+ size_t len = knot_dname_size(name);
+ assert(len <= KNOT_DNAME_MAXLEN);
+ memcpy(dst, name, len);
+
+ return len;
+}
+
+_public_
+knot_dname_t *knot_dname_copy(const knot_dname_t *name, knot_mm_t *mm)
+{
+ if (name == NULL) {
+ return NULL;
+ }
+
+ size_t len = knot_dname_size(name);
+ knot_dname_t *dst = mm_alloc(mm, len);
+ if (dst == NULL) {
+ return NULL;
+ }
+ memcpy(dst, name, len);
+
+ return dst;
+}
+
+_public_
+int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t len = knot_dname_size(src);
+ if (len > maxlen) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst, src, len);
+
+ return len;
+}
+
+_public_
+int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src,
+ size_t maxlen, const uint8_t *pkt)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Seek first real label occurrence. */
+ src = knot_wire_seek_label(src, pkt);
+
+ /* Unpack rest of the labels. */
+ int len = 0;
+ while (*src != '\0') {
+ uint8_t lblen = *src + 1;
+ if (len + lblen > maxlen) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst + len, src, lblen);
+ len += lblen;
+ src = knot_wire_next_label(src, pkt);
+ }
+
+ /* Terminal label */
+ if (len + 1 > maxlen) {
+ return KNOT_EINVAL;
+ }
+
+ *(dst + len) = '\0';
+ return len + 1;
+}
+
+_public_
+char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen)
+{
+ if (name == NULL) {
+ return NULL;
+ }
+
+ size_t dname_size = knot_dname_size(name);
+
+ /* Check the size for len(dname) + 1 char termination. */
+ size_t alloc_size = (dst == NULL) ? dname_size + 1 : maxlen;
+ if (alloc_size < dname_size + 1) {
+ return NULL;
+ }
+
+ char *res = (dst == NULL) ? malloc(alloc_size) : dst;
+ if (res == NULL) {
+ return NULL;
+ }
+
+ uint8_t label_len = 0;
+ size_t str_len = 0;
+
+ for (unsigned i = 0; i < dname_size; i++) {
+ uint8_t c = name[i];
+
+ /* Read next label size. */
+ if (label_len == 0) {
+ label_len = c;
+
+ /* Write label separation. */
+ if (str_len > 0 || dname_size == 1) {
+ if (alloc_size <= str_len + 1) {
+ goto dname_to_str_failed;
+ }
+ res[str_len++] = '.';
+ }
+
+ continue;
+ }
+
+ if (is_alnum(c) || c == '-' || c == '_' || c == '*' ||
+ c == '/') {
+ if (alloc_size <= str_len + 1) {
+ goto dname_to_str_failed;
+ }
+ res[str_len++] = c;
+ } else if (is_punct(c) && c != '#') {
+ /* Exclusion of '#' character is to avoid possible
+ * collision with rdata hex notation '\#'. So it is
+ * encoded in \ddd notation.
+ */
+
+ if (dst != NULL) {
+ if (maxlen <= str_len + 2) {
+ goto dname_to_str_failed;
+ }
+ } else {
+ /* Extend output buffer for \x format. */
+ alloc_size += 1;
+ char *extended = realloc(res, alloc_size);
+ if (extended == NULL) {
+ goto dname_to_str_failed;
+ }
+ res = extended;
+ }
+
+ /* Write encoded character. */
+ res[str_len++] = '\\';
+ res[str_len++] = c;
+ } else {
+ if (dst != NULL) {
+ if (maxlen <= str_len + 4) {
+ goto dname_to_str_failed;
+ }
+ } else {
+ /* Extend output buffer for \DDD format. */
+ alloc_size += 3;
+ char *extended = realloc(res, alloc_size);
+ if (extended == NULL) {
+ goto dname_to_str_failed;
+ }
+ res = extended;
+ }
+
+ /* Write encoded character. */
+ int ret = snprintf(res + str_len, alloc_size - str_len,
+ "\\%03u", c);
+ if (ret <= 0 || ret >= alloc_size - str_len) {
+ goto dname_to_str_failed;
+ }
+
+ str_len += ret;
+ }
+
+ label_len--;
+ }
+
+ /* String_termination. */
+ assert(str_len < alloc_size);
+ res[str_len] = 0;
+
+ return res;
+
+dname_to_str_failed:
+
+ if (dst == NULL) {
+ free(res);
+ }
+
+ return NULL;
+}
+
+_public_
+knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen)
+{
+ if (name == NULL) {
+ return NULL;
+ }
+
+ size_t name_len = strlen(name);
+ if (name_len == 0) {
+ return NULL;
+ }
+
+ /* Wire size estimation. */
+ size_t alloc_size = maxlen;
+ if (dst == NULL) {
+ /* Check for the root label. */
+ if (name[0] == '.') {
+ /* Just the root dname can begin with a dot. */
+ if (name_len > 1) {
+ return NULL;
+ }
+ name_len = 0; /* Skip the following parsing. */
+ alloc_size = 1;
+ } else if (name[name_len - 1] != '.') { /* Check for non-FQDN. */
+ alloc_size = 1 + name_len + 1;
+ } else {
+ alloc_size = 1 + name_len ; /* + 1 ~ first label length. */
+ }
+ }
+
+ /* The minimal (root) dname takes 1 byte. */
+ if (alloc_size == 0) {
+ return NULL;
+ }
+
+ /* Check the maximal wire size. */
+ if (alloc_size > KNOT_DNAME_MAXLEN) {
+ alloc_size = KNOT_DNAME_MAXLEN;
+ }
+
+ /* Prepare output buffer. */
+ uint8_t *wire = (dst == NULL) ? malloc(alloc_size) : dst;
+ if (wire == NULL) {
+ return NULL;
+ }
+
+ uint8_t *label = wire;
+ uint8_t *wire_pos = wire + 1;
+ uint8_t *wire_end = wire + alloc_size;
+
+ /* Initialize the first label (root label). */
+ *label = 0;
+
+ const uint8_t *ch = (const uint8_t *)name;
+ const uint8_t *end = ch + name_len;
+
+ while (ch < end) {
+ /* Check the output buffer for enough space. */
+ if (wire_pos >= wire_end) {
+ goto dname_from_str_failed;
+ }
+
+ switch (*ch) {
+ case '.':
+ /* Check for invalid zero-length label. */
+ if (*label == 0 && name_len > 1) {
+ goto dname_from_str_failed;
+ }
+ label = wire_pos++;
+ *label = 0;
+ break;
+ case '\\':
+ ch++;
+
+ /* At least one more character is required OR
+ * check for maximal label length.
+ */
+ if (ch == end || ++(*label) > KNOT_DNAME_MAXLABELLEN) {
+ goto dname_from_str_failed;
+ }
+
+ /* Check for \DDD notation. */
+ if (is_digit(*ch)) {
+ /* Check for next two digits. */
+ if (ch + 2 >= end ||
+ !is_digit(*(ch + 1)) ||
+ !is_digit(*(ch + 2))) {
+ goto dname_from_str_failed;
+ }
+
+ uint32_t num = (*(ch + 0) - '0') * 100 +
+ (*(ch + 1) - '0') * 10 +
+ (*(ch + 2) - '0') * 1;
+ if (num > UINT8_MAX) {
+ goto dname_from_str_failed;
+ }
+ *(wire_pos++) = num;
+ ch +=2;
+ } else {
+ *(wire_pos++) = *ch;
+ }
+ break;
+ default:
+ /* Check for maximal label length. */
+ if (++(*label) > KNOT_DNAME_MAXLABELLEN) {
+ goto dname_from_str_failed;
+ }
+ *(wire_pos++) = *ch;
+ }
+ ch++;
+ }
+
+ /* Check for non-FQDN name. */
+ if (*label > 0) {
+ if (wire_pos >= wire_end) {
+ goto dname_from_str_failed;
+ }
+ *(wire_pos++) = 0;
+ }
+
+ /* Reduce output buffer if the size is overestimated. */
+ if (wire_pos < wire_end && dst == NULL) {
+ uint8_t *reduced = realloc(wire, wire_pos - wire);
+ if (reduced == NULL) {
+ goto dname_from_str_failed;
+ }
+ wire = reduced;
+ }
+
+ return wire;
+
+dname_from_str_failed:
+
+ if (dst == NULL) {
+ free(wire);
+ }
+
+ return NULL;
+}
+
+_public_
+void knot_dname_to_lower(knot_dname_t *name)
+{
+ if (name == NULL) {
+ return;
+ }
+
+ while (*name != '\0') {
+ uint8_t len = *name;
+ for (uint8_t i = 1; i <= len; ++i) {
+ name[i] = knot_tolower(name[i]);
+ }
+ name += 1 + len;
+ }
+}
+
+_public_
+void knot_dname_copy_lower(knot_dname_t *dst, const knot_dname_t *name)
+{
+ if (dst == NULL || name == NULL) {
+ return;
+ }
+
+ while (*name != '\0') {
+ uint8_t len = *name;
+ *dst = len;
+ for (uint8_t i = 1; i <= len; ++i) {
+ dst[i] = knot_tolower(name[i]);
+ }
+ dst += 1 + len;
+ name += 1 + len;
+ }
+ *dst = '\0';
+}
+
+_public_
+size_t knot_dname_size(const knot_dname_t *name)
+{
+ if (name == NULL) {
+ return 0;
+ }
+
+ /* Count name size without terminal label. */
+ size_t len = 0;
+ while (*name != '\0' && !knot_wire_is_pointer(name)) {
+ uint8_t lblen = *name + 1;
+ len += lblen;
+ name += lblen;
+ }
+
+ if (knot_wire_is_pointer(name)) {
+ /* Add 2-octet compression pointer. */
+ return len + 2;
+ } else {
+ /* Add 1-octet terminal label. */
+ return len + 1;
+ }
+}
+
+_public_
+size_t knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt)
+{
+ if (name == NULL) {
+ return 0;
+ }
+
+ /* Seek first real label occurrence. */
+ name = knot_wire_seek_label(name, pkt);
+
+ size_t len = 0;
+ while (*name != '\0') {
+ len += *name + 1;
+ name = knot_wire_next_label(name, pkt);
+ }
+
+ /* Add 1-octet terminal label. */
+ return len + 1;
+}
+
+_public_
+size_t knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ /* Count labels. */
+ size_t l1 = knot_dname_labels(d1, NULL);
+ size_t l2 = knot_dname_labels(d2, NULL);
+ if (l1 == 0 || l2 == 0) {
+ return 0;
+ }
+
+ /* Align end-to-end to common suffix. */
+ int common = dname_align(&d1, l1, &d2, l2);
+
+ /* Count longest chain leading to root label. */
+ size_t matched = 0;
+ while (common > 0) {
+ if (label_is_equal(d1, d2, false)) {
+ ++matched;
+ } else {
+ matched = 0; /* Broken chain. */
+ }
+
+ /* Next label. */
+ d1 = knot_wire_next_label(d1, NULL);
+ d2 = knot_wire_next_label(d2, NULL);
+ --common;
+ }
+
+ return matched;
+}
+
+_public_
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels,
+ const knot_dname_t *suffix, knot_mm_t *mm)
+{
+ if (name == NULL) {
+ return NULL;
+ }
+
+ /* Calculate prefix and suffix lengths. */
+ size_t dname_lbs = knot_dname_labels(name, NULL);
+ if (dname_lbs < labels) {
+ return NULL;
+ }
+ size_t prefix_lbs = dname_lbs - labels;
+
+ size_t prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL);
+ size_t suffix_len = knot_dname_size(suffix);
+ if (prefix_len == 0 || suffix_len == 0) {
+ return NULL;
+ }
+
+ /* Create target name. */
+ size_t new_len = prefix_len + suffix_len;
+ knot_dname_t *out = mm_alloc(mm, new_len);
+ if (out == NULL) {
+ return NULL;
+ }
+
+ /* Copy prefix. */
+ uint8_t *dst = out;
+ while (prefix_lbs > 0) {
+ memcpy(dst, name, *name + 1);
+ dst += *name + 1;
+ name = knot_wire_next_label(name, NULL);
+ --prefix_lbs;
+ }
+
+ /* Copy suffix. */
+ while (*suffix != '\0') {
+ memcpy(dst, suffix, *suffix + 1);
+ dst += *suffix + 1;
+ suffix = knot_wire_next_label(suffix, NULL);
+ }
+
+ *dst = '\0';
+ return out;
+}
+
+_public_
+void knot_dname_free(knot_dname_t *name, knot_mm_t *mm)
+{
+ if (name == NULL) {
+ return;
+ }
+
+ mm_free(mm, name);
+}
+
+_public_
+int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ if (d1 == NULL) {
+ return -1;
+ } else if (d2 == NULL) {
+ return 1;
+ }
+
+ /* Convert to lookup format. */
+ knot_dname_storage_t lf1_storage;
+ knot_dname_storage_t lf2_storage;
+
+ uint8_t *lf1 = knot_dname_lf(d1, lf1_storage);
+ uint8_t *lf2 = knot_dname_lf(d2, lf2_storage);
+ assert(lf1 && lf2);
+
+ /* Compare common part. */
+ uint8_t common = lf1[0];
+ if (common > lf2[0]) {
+ common = lf2[0];
+ }
+ int ret = memcmp(lf1 + 1, lf2 + 1, common);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* If they match, compare lengths. */
+ if (lf1[0] < lf2[0]) {
+ return -1;
+ } else if (lf1[0] > lf2[0]) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+inline static bool dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2, bool no_case)
+{
+ if (d1 == NULL || d2 == NULL) {
+ return false;
+ }
+
+ while (*d1 != '\0' || *d2 != '\0') {
+ if (label_is_equal(d1, d2, no_case)) {
+ d1 = knot_wire_next_label(d1, NULL);
+ d2 = knot_wire_next_label(d2, NULL);
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+_public_
+bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return dname_is_equal(d1, d2, false);
+}
+
+_public_
+bool knot_dname_is_case_equal(const knot_dname_t *d1, const knot_dname_t *d2)
+{
+ return dname_is_equal(d1, d2, true);
+}
+
+_public_
+size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt)
+{
+ if (name == NULL) {
+ return 0;
+ }
+
+ /* Zero labels means no prefix. */
+ if (nlabels == 0) {
+ return 0;
+ }
+
+ /* Seek first real label occurrence. */
+ name = knot_wire_seek_label(name, pkt);
+
+ size_t len = 0;
+ while (*name != '\0') {
+ len += *name + 1;
+ name = knot_wire_next_label(name, pkt);
+ if (--nlabels == 0) { /* Count N first labels only. */
+ break;
+ }
+ }
+
+ return len;
+}
+
+_public_
+size_t knot_dname_labels(const uint8_t *name, const uint8_t *pkt)
+{
+ if (name == NULL) {
+ return 0;
+ }
+
+ size_t count = 0;
+ while (*name != '\0') {
+ ++count;
+ name = knot_wire_next_label(name, pkt);
+ if (name == NULL) {
+ return 0;
+ }
+ }
+
+ return count;
+}
+
+_public_
+uint8_t *knot_dname_lf(const knot_dname_t *src, knot_dname_storage_t storage)
+{
+ if (src == NULL || storage == NULL) {
+ return NULL;
+ }
+
+ uint8_t *dst = storage + KNOT_DNAME_MAXLEN - 1;
+
+ while (*src != 0) {
+ uint8_t len = *src++;
+ *dst = '\0';
+ dst -= len;
+ assert(dst >= storage);
+
+ if (len == 1) {
+ *dst-- = *src++;
+ } else {
+ memcpy(dst--, src, len);
+ src += len;
+ }
+ }
+
+ *dst = storage + KNOT_DNAME_MAXLEN - 1 - dst;
+ assert(dst >= storage);
+
+ return dst;
+}
+
+_public_
+int knot_dname_in_bailiwick(const knot_dname_t *name, const knot_dname_t *bailiwick)
+{
+ if (name == NULL || bailiwick == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int label_diff = knot_dname_labels(name, NULL) - knot_dname_labels(bailiwick, NULL);
+ if (label_diff < 0) {
+ return KNOT_EOUTOFZONE;
+ }
+
+ for (int i = 0; i < label_diff; ++i) {
+ name = knot_wire_next_label(name, NULL);
+ }
+
+ return knot_dname_is_equal(name, bailiwick) ? label_diff : KNOT_EOUTOFZONE;
+}
diff --git a/src/libknot/dname.h b/src/libknot/dname.h
new file mode 100644
index 0000000..5733de9
--- /dev/null
+++ b/src/libknot/dname.h
@@ -0,0 +1,354 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Domain name structure and API for manipulating it.
+ *
+ * \addtogroup dname
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/mm_ctx.h"
+
+/*! \brief Type representing a domain name in wire format. */
+typedef uint8_t knot_dname_t;
+
+/*! \brief Local domain name storage. */
+typedef uint8_t knot_dname_storage_t[KNOT_DNAME_MAXLEN];
+
+/*! \brief Local textual domain name storage. */
+typedef char knot_dname_txt_storage_t[KNOT_DNAME_TXT_MAXLEN + 1];
+
+/*!
+ * \brief Check dname on the wire for constraints.
+ *
+ * If the name passes such checks, it is safe to be used in rest of the functions.
+ *
+ * \param name Name on the wire.
+ * \param endp Name boundary.
+ * \param pkt Wire.
+ *
+ * \retval (compressed) size of the domain name.
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EMALF
+ */
+_pure_ _mustcheck_
+int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp,
+ const uint8_t *pkt);
+
+/*!
+ * \brief Duplicates the given domain name to a local storage.
+ *
+ * \param dst Destination storage.
+ * \param name Domain name to be copied.
+ *
+ * \retval size of the domain name.
+ * \retval 0 if invalid argument.
+ */
+_mustcheck_
+size_t knot_dname_store(knot_dname_storage_t dst, const knot_dname_t *name);
+
+/*!
+ * \brief Duplicates the given domain name.
+ *
+ * \param name Domain name to be copied.
+ * \param mm Memory context.
+ *
+ * \return New domain name which is an exact copy of \a name.
+ */
+_mustcheck_
+knot_dname_t *knot_dname_copy(const knot_dname_t *name, knot_mm_t *mm);
+
+/*!
+ * \brief Copy name to wire as is, no compression pointer expansion will be done.
+ *
+ * \param dst Destination wire.
+ * \param src Source name.
+ * \param maxlen Maximum wire length.
+ *
+ * \return the number of bytes written or negative error code
+ */
+int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen);
+
+/*!
+ * \brief Write unpacked name (i.e. compression pointers expanded)
+ *
+ * \note The function is very similar to the knot_dname_to_wire(), except
+ * it expands compression pointers. E.g. you want to use knot_dname_unpack()
+ * if you copy a dname from incoming packet to some persistent storage.
+ * And you want to use knot_dname_to_wire() if you know the name is not
+ * compressed or you want to copy it 1:1.
+ *
+ * \param dst Destination wire.
+ * \param src Source name.
+ * \param maxlen Maximum destination wire size.
+ * \param pkt Name packet wire (for compression pointers).
+ *
+ * \return number of bytes written
+ */
+int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src,
+ size_t maxlen, const uint8_t *pkt);
+
+/*!
+ * \brief Converts the given domain name to its string representation.
+ *
+ * \note Output buffer is allocated automatically if dst is NULL.
+ *
+ * \param dst Output buffer.
+ * \param name Domain name to be converted.
+ * \param maxlen Output buffer length.
+ *
+ * \return 0-terminated string if successful, NULL if error.
+ */
+char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen);
+
+/*!
+ * \brief This function is a shortcut for \ref knot_dname_to_str with
+ * no output buffer parameters.
+ */
+_mustcheck_
+static inline char *knot_dname_to_str_alloc(const knot_dname_t *name)
+{
+ return knot_dname_to_str(NULL, name, 0);
+}
+
+/*!
+ * \brief Creates a dname structure from domain name given in presentation
+ * format.
+ *
+ * \note The resulting FQDN is stored in the wire format.
+ * \note Output buffer is allocated automatically if dst is NULL.
+ *
+ * \param dst Output buffer.
+ * \param name Domain name in presentation format (labels separated by dots,
+ * '\0' terminated).
+ * \param maxlen Output buffer length.
+ *
+ * \return New dname if successful, NULL if error.
+ */
+knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen);
+
+/*!
+ * \brief This function is a shortcut for \ref knot_dname_from_str with
+ * no output buffer parameters.
+ */
+_mustcheck_
+static inline knot_dname_t *knot_dname_from_str_alloc(const char *name)
+{
+ return knot_dname_from_str(NULL, name, 0);
+}
+
+/*!
+ * \brief Convert domain name to lowercase.
+ *
+ * \param name Domain name to be converted.
+ */
+void knot_dname_to_lower(knot_dname_t *name);
+
+/*!
+ * \brief Copy lowercased domain name.
+ *
+ * \note The output buffer isn't checked if it's big enough!
+ *
+ * \param dst Destination buffer.
+ * \param name Source domain name to be converted.
+ */
+void knot_dname_copy_lower(knot_dname_t *dst, const knot_dname_t *name);
+
+/*!
+ * \brief Returns size of the given domain name.
+ *
+ * \note If the domain name is compressed, the length of not compressed part
+ * is returned.
+ *
+ * \param name Domain name to get the size of.
+ *
+ * \retval size of the domain name.
+ * \retval 0 if invalid argument.
+ */
+_pure_
+size_t knot_dname_size(const knot_dname_t *name);
+
+/*!
+ * \brief Returns full size of the given domain name (expanded compression ptrs).
+ *
+ * \param name Domain name to get the size of.
+ * \param pkt Related packet (or NULL if unpacked)
+ *
+ * \retval size of the domain name.
+ * \retval 0 if invalid argument.
+ */
+_pure_
+size_t knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt);
+
+/*!
+ * \brief Checks if the domain name is a wildcard.
+ *
+ * \param name Domain name to check.
+ *
+ * \retval true if \a dname is a wildcard domain name.
+ * \retval false otherwise.
+ */
+static inline
+bool knot_dname_is_wildcard(const knot_dname_t *name)
+{
+ return name != NULL && name[0] == 1 && name[1] == '*';
+}
+
+/*!
+ * \brief Returns the number of labels common for the two domain names (counted
+ * from the rightmost label.
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \return Number of labels common for the two domain names.
+ */
+_pure_
+size_t knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Replaces the suffix of given size in one domain name with other domain
+ * name.
+ *
+ * \param name Domain name where to replace the suffix.
+ * \param labels Size of the suffix to be replaced.
+ * \param suffix New suffix to be used as a replacement.
+ * \param mm Memory context.
+ *
+ * \return New domain name created by replacing suffix of \a dname of size
+ * \a size with \a suffix.
+ */
+_mustcheck_
+knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels,
+ const knot_dname_t *suffix, knot_mm_t *mm);
+
+/*!
+ * \brief Destroys the given domain name.
+ *
+ * \param name Domain name to be destroyed.
+ * \param mm Memory context.
+ */
+void knot_dname_free(knot_dname_t *name, knot_mm_t *mm);
+
+/*!
+ * \brief Compares two domain names by labels (case sensitive).
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \retval < 0 if \a d1 goes before \a d2 in canonical order.
+ * \retval > 0 if \a d1 goes after \a d2 in canonical order.
+ * \retval 0 if the domain names are identical.
+ */
+_pure_
+int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Compares two domain names (case sensitive).
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \retval true if the domain names are identical
+ * \retval false if the domain names are NOT identical
+ */
+_pure_
+bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Compares two domain names (case insensitive).
+ *
+ * \param d1 First domain name.
+ * \param d2 Second domain name.
+ *
+ * \retval true if the domain names are equal
+ * \retval false if the domain names are NOT equal
+ */
+_pure_
+bool knot_dname_is_case_equal(const knot_dname_t *d1, const knot_dname_t *d2);
+
+/*!
+ * \brief Count length of the N first labels.
+ *
+ * \param name Domain name.
+ * \param nlabels First N labels.
+ * \param pkt Related packet (or NULL if not compressed).
+ *
+ * \return Length of the prefix.
+ */
+_pure_
+size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt);
+
+/*!
+ * \brief Return number of labels in the domain name.
+ *
+ * Terminal nullbyte is not counted.
+ *
+ * \param name Domain name.
+ * \param pkt Related packet (or NULL if not compressed).
+ *
+ * \return Number of labels.
+ */
+_pure_
+size_t knot_dname_labels(const uint8_t *name, const uint8_t *pkt);
+
+/*!
+ * \brief Convert domain name from wire to the lookup format.
+ *
+ * Formats names from rightmost label to the leftmost, separated by the lowest
+ * possible character (\\x00). Sorting such formatted names also gives
+ * correct canonical order (for NSEC/NSEC3). The first byte of the output
+ * contains length of the remaining output.
+ *
+ * Examples:
+ * Name: lake.example.com.
+ * Wire: \\x04lake\\x07example\\x03com\\x00
+ * Lookup: \\x11com\\x00example\\x00lake\\x00
+ *
+ * Name: .
+ * Wire: \\x00
+ * Lookup: \\x00
+ *
+ * \param src Source domain name.
+ * \param storage Memory to store converted name into. Don't use directly!
+ *
+ * \retval Lookup format if successful (pointer into the storage).
+ * \retval NULL on invalid parameters.
+ */
+uint8_t *knot_dname_lf(const knot_dname_t *src, knot_dname_storage_t storage);
+
+/*!
+ * \brief Check whether a domain name is under another one and how deep.
+ *
+ * \param name The longer name to check.
+ * \param bailiwick The shorter name to check.
+ *
+ * \retval >=0 a subdomain nested this many labels.
+ * \retval <0 not a subdomain (KNOT_EOUTOFZONE) or another error (KNOT_EINVAL).
+ */
+int knot_dname_in_bailiwick(const knot_dname_t *name, const knot_dname_t *bailiwick);
+
+/*! @} */
diff --git a/src/libknot/dynarray.h b/src/libknot/dynarray.h
new file mode 100644
index 0000000..7ea66f9
--- /dev/null
+++ b/src/libknot/dynarray.h
@@ -0,0 +1,188 @@
+/* Copyright (C) 2022 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 Simple write-once allocation-optimal dynamic array.
+ *
+ * Include it into your .c file
+ *
+ * prefix - identifier prefix, e.g. ptr -> struct ptr_dynarray, ptr_dynarray_add(), ...
+ * ntype - data type to be stored. Let it be a number, pointer or small struct
+ * initial_capacity - how many data items will be allocated on stac and copied with assignment
+ *
+ * prefix_dynarray_add() - add a data item
+ * prefix_dynarray_fix() - call EVERYTIME the array is copied from some already invalid stack
+ * prefix_dynarray_free() - call EVERYTIME you dismiss all copies of the array
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "libknot/attribute.h"
+
+#pragma once
+
+#define DYNARRAY_VISIBILITY_NORMAL
+#define DYNARRAY_VISIBILITY_STATIC static
+#define DYNARRAY_VISIBILITY_PUBLIC _public_
+
+#define knot_dynarray_declare(prefix, ntype, visibility, initial_capacity) \
+ typedef struct prefix ## _dynarray { \
+ ssize_t capacity; \
+ ssize_t size; \
+ ntype *(*arr)(struct prefix ## _dynarray *dynarray); \
+ ntype init[initial_capacity]; \
+ ntype *_arr; \
+ } prefix ## _dynarray_t; \
+ \
+ visibility ntype *prefix ## _dynarray_arr(prefix ## _dynarray_t *dynarray); \
+ visibility ntype *prefix ## _dynarray_add(prefix ## _dynarray_t *dynarray, \
+ ntype const *to_add); \
+ visibility void prefix ## _dynarray_remove(prefix ## _dynarray_t *dynarray, \
+ ntype const *to_remove); \
+ visibility void prefix ## _dynarray_sort(prefix ## _dynarray_t *dynarray); \
+ visibility ntype *prefix ## _dynarray_bsearch(prefix ## _dynarray_t *dynarray, \
+ const ntype *bskey); \
+ visibility void prefix ## _dynarray_sort_dedup(prefix ## _dynarray_t *dynarray); \
+ visibility void prefix ## _dynarray_free(prefix ## _dynarray_t *dynarray);
+
+#define knot_dynarray_foreach(prefix, ntype, ptr, array) \
+ for (ntype *ptr = prefix ## _dynarray_arr(&(array)); \
+ ptr < prefix ## _dynarray_arr(&(array)) + (array).size; ptr++)
+
+#define knot_dynarray_define(prefix, ntype, visibility) \
+ \
+ static void prefix ## _dynarray_free__(struct prefix ## _dynarray *dynarray) \
+ { \
+ if (dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)) { \
+ free(dynarray->_arr); \
+ } \
+ } \
+ \
+ _unused_ \
+ visibility ntype *prefix ## _dynarray_arr(struct prefix ## _dynarray *dynarray) \
+ { \
+ assert(dynarray->size <= dynarray->capacity); \
+ return (dynarray->capacity <= sizeof(dynarray->init) / sizeof(*dynarray->init) ? \
+ dynarray->init : dynarray->_arr); \
+ } \
+ \
+ static ntype *prefix ## _dynarray_arr_init__(struct prefix ## _dynarray *dynarray) \
+ { \
+ assert(dynarray->capacity == sizeof(dynarray->init) / sizeof(*dynarray->init)); \
+ return dynarray->init; \
+ } \
+ \
+ static ntype *prefix ## _dynarray_arr_arr__(struct prefix ## _dynarray *dynarray) \
+ { \
+ assert(dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)); \
+ return dynarray->_arr; \
+ } \
+ \
+ _unused_ \
+ visibility ntype *prefix ## _dynarray_add(struct prefix ## _dynarray *dynarray, \
+ ntype const *to_add) \
+ { \
+ if (dynarray->capacity < 0) { \
+ return NULL; \
+ } \
+ if (dynarray->capacity == 0) { \
+ dynarray->capacity = sizeof(dynarray->init) / sizeof(*dynarray->init); \
+ dynarray->arr = prefix ## _dynarray_arr_init__; \
+ } \
+ if (dynarray->size >= dynarray->capacity) { \
+ ssize_t new_capacity = dynarray->capacity * 2 + 1; \
+ ntype *new_arr = calloc(new_capacity, sizeof(ntype)); \
+ if (new_arr == NULL) { \
+ prefix ## _dynarray_free__(dynarray); \
+ dynarray->capacity = dynarray->size = -1; \
+ return NULL; \
+ } \
+ if (dynarray->capacity > 0) { \
+ memcpy(new_arr, prefix ## _dynarray_arr(dynarray), \
+ dynarray->capacity * sizeof(ntype)); \
+ } \
+ prefix ## _dynarray_free__(dynarray); \
+ dynarray->_arr = new_arr; \
+ dynarray->capacity = new_capacity; \
+ dynarray->arr = prefix ## _dynarray_arr_arr__; \
+ } \
+ ntype *add_to = &prefix ## _dynarray_arr(dynarray)[dynarray->size++]; \
+ *add_to = *to_add; \
+ return add_to; \
+ } \
+ \
+ _unused_ \
+ visibility void prefix ## _dynarray_remove(struct prefix ## _dynarray *dynarray, \
+ ntype const *to_remove) \
+ { \
+ ntype *orig_arr = prefix ## _dynarray_arr(dynarray); \
+ knot_dynarray_foreach(prefix, ntype, removable, *dynarray) { \
+ if (memcmp(removable, to_remove, sizeof(*to_remove)) == 0) { \
+ if (removable != orig_arr + --dynarray->size) { \
+ *(removable--) = orig_arr[dynarray->size]; \
+ } \
+ } \
+ } /* TODO enable lowering capacity, take care of capacity going back to initial! */ \
+ } \
+ \
+ static int prefix ## _dynarray_memb_cmp(const void *a, const void *b) \
+ { \
+ return memcmp(a, b, sizeof(ntype)); \
+ } \
+ \
+ _unused_ \
+ visibility void prefix ## _dynarray_sort(struct prefix ## _dynarray *dynarray) \
+ { \
+ ntype *arr = prefix ## _dynarray_arr(dynarray); \
+ qsort(arr, dynarray->size, sizeof(*arr), prefix ## _dynarray_memb_cmp); \
+ } \
+ \
+ _unused_ \
+ visibility ntype *prefix ## _dynarray_bsearch(struct prefix ## _dynarray *dynarray, const ntype *bskey) \
+ { \
+ ntype *arr = prefix ## _dynarray_arr(dynarray); \
+ return bsearch(bskey, arr, dynarray->size, sizeof(*arr), prefix ## _dynarray_memb_cmp); \
+ } \
+ \
+ _unused_ \
+ visibility void prefix ## _dynarray_sort_dedup(struct prefix ## _dynarray *dynarray) \
+ { \
+ if (dynarray->size > 1) { \
+ prefix ## _dynarray_sort(dynarray); \
+ ntype *arr = prefix ## _dynarray_arr(dynarray); \
+ ntype *rd = arr + 1; \
+ ntype *wr = arr + 1; \
+ ntype *end = arr + dynarray->size; \
+ while (rd != end) { \
+ if (memcmp(rd - 1, rd, sizeof(*rd)) != 0) { \
+ if (wr != rd) { \
+ *wr = *rd; \
+ } \
+ wr++; \
+ } \
+ rd++; \
+ } \
+ dynarray->size = wr - arr; \
+ } \
+ } \
+ _unused_ \
+ visibility void prefix ## _dynarray_free(struct prefix ## _dynarray *dynarray) \
+ { \
+ prefix ## _dynarray_free__(dynarray); \
+ memset(dynarray, 0, sizeof(*dynarray)); \
+ }
diff --git a/src/libknot/endian.h b/src/libknot/endian.h
new file mode 100644
index 0000000..037ed68
--- /dev/null
+++ b/src/libknot/endian.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Endian dependent integer operations.
+ *
+ * \addtogroup wire
+ * @{
+ */
+
+#pragma once
+
+#if defined(__linux__) || defined(__gnu_hurd__) || \
+ (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
+# include <endian.h>
+#elif defined(__FreeBSD__) || defined(__NetBSD__)
+# include <sys/endian.h>
+#elif defined(__OpenBSD__) || defined(__sun) || defined(__CYGWIN__)
+# include <endian.h>
+#elif defined(__APPLE__)
+# include <libkern/OSByteOrder.h>
+# define be16toh(x) OSSwapBigToHostInt16(x)
+# define be32toh(x) OSSwapBigToHostInt32(x)
+# define be64toh(x) OSSwapBigToHostInt64(x)
+# define htobe16(x) OSSwapHostToBigInt16(x)
+# define htobe32(x) OSSwapHostToBigInt32(x)
+# define htobe64(x) OSSwapHostToBigInt64(x)
+# define le16toh(x) OSSwapLittleToHostInt16(x)
+# define le32toh(x) OSSwapLittleToHostInt32(x)
+# define le64toh(x) OSSwapLittleToHostInt64(x)
+# define htole16(x) OSSwapHostToLittleInt16(x)
+# define htole32(x) OSSwapHostToLittleInt32(x)
+# define htole64(x) OSSwapHostToLittleInt64(x)
+#endif
+
+/*! @} */
diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h
new file mode 100644
index 0000000..648ca66
--- /dev/null
+++ b/src/libknot/errcode.h
@@ -0,0 +1,272 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Knot error codes.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include <errno.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_ENOTSUP = -ENOTSUP,
+ KNOT_EBUSY = -EBUSY,
+ KNOT_EAGAIN = -EAGAIN,
+ KNOT_ENOBUFS = -ENOBUFS,
+ KNOT_EMFILE = -EMFILE,
+ KNOT_ENFILE = -ENFILE,
+ KNOT_EACCES = -EACCES,
+ KNOT_EISCONN = -EISCONN,
+ KNOT_ECONNREFUSED = -ECONNREFUSED,
+ KNOT_EALREADY = -EALREADY,
+ KNOT_ECONNRESET = -ECONNRESET,
+ KNOT_ECONNABORTED = -ECONNABORTED,
+ KNOT_ENETRESET = -ENETRESET,
+ KNOT_EHOSTUNREACH = -EHOSTUNREACH,
+ KNOT_ENETUNREACH = -ENETUNREACH,
+ KNOT_EHOSTDOWN = -EHOSTDOWN,
+ KNOT_ENETDOWN = -ENETDOWN,
+ KNOT_EADDRINUSE = -EADDRINUSE,
+ KNOT_ENOENT = -ENOENT,
+ KNOT_EEXIST = -EEXIST,
+ KNOT_ERANGE = -ERANGE,
+ KNOT_EADDRNOTAVAIL = -EADDRNOTAVAIL,
+ KNOT_ENOTDIR = -ENOTDIR,
+
+ KNOT_ERRNO_ERROR = -500,
+
+ KNOT_ERROR_MIN = -1000,
+
+ /* General errors. */
+ KNOT_ERROR = KNOT_ERROR_MIN,
+ KNOT_EPARSEFAIL,
+ KNOT_ESEMCHECK,
+ KNOT_EUPTODATE,
+ KNOT_EFEWDATA,
+ KNOT_ESPACE,
+ KNOT_EMALF,
+ KNOT_ENSEC3PAR,
+ KNOT_ENSEC3CHAIN,
+ KNOT_EOUTOFZONE,
+ KNOT_EZONEINVAL,
+ KNOT_ENOZONE,
+ KNOT_ENONODE,
+ KNOT_ENORECORD,
+ KNOT_EISRECORD,
+ KNOT_ENOMASTER,
+ KNOT_EPREREQ,
+ KNOT_ETTL,
+ KNOT_ENOTTL,
+ KNOT_ENOXFR,
+ KNOT_EDENIED,
+ KNOT_ECONN,
+ KNOT_ETIMEOUT,
+ KNOT_ENODIFF,
+ KNOT_ENOTSIG,
+ KNOT_ELIMIT,
+ KNOT_EZONESIZE,
+ KNOT_EOF,
+ KNOT_ESYSTEM,
+ KNOT_EFILE,
+ KNOT_ESOAINVAL,
+ KNOT_ETRAIL,
+ KNOT_EPROCESSING,
+ KNOT_EPROGRESS,
+ KNOT_ELOOP,
+ KNOT_EPROGRAM,
+ KNOT_EFD,
+ KNOT_ENOPARAM,
+ KNOT_EXPARAM,
+ KNOT_EEMPTYZONE,
+ KNOT_ENODB,
+ KNOT_EUNREACH,
+
+ KNOT_GENERAL_ERROR = -900,
+
+ /* Control states. */
+ KNOT_CTL_ESTOP,
+ KNOT_CTL_EZONE,
+
+ /* Network errors. */
+ KNOT_NET_EADDR,
+ KNOT_NET_ESOCKET,
+ KNOT_NET_ECONNECT,
+ KNOT_NET_ESEND,
+ KNOT_NET_ERECV,
+ KNOT_NET_ETIMEOUT,
+
+ /* Encoding errors. */
+ KNOT_BASE64_ESIZE,
+ KNOT_BASE64_ECHAR,
+ KNOT_BASE32HEX_ESIZE,
+ KNOT_BASE32HEX_ECHAR,
+
+ /* TSIG errors. */
+ KNOT_TSIG_EBADSIG,
+ KNOT_TSIG_EBADKEY,
+ KNOT_TSIG_EBADTIME,
+ KNOT_TSIG_EBADTRUNC,
+
+ /* DNSSEC errors. */
+ KNOT_DNSSEC_EMISSINGKEYTYPE,
+ KNOT_DNSSEC_ENOKEY,
+ KNOT_DNSSEC_ENOSIG,
+ KNOT_DNSSEC_ENONSEC,
+ KNOT_DNSSEC_ENSEC_BITMAP,
+ KNOT_DNSSEC_ENSEC_CHAIN,
+ KNOT_DNSSEC_ENSEC3_OPTOUT,
+
+ /* Yparser errors. */
+ KNOT_YP_ECHAR_TAB,
+ KNOT_YP_EINVAL_ITEM,
+ KNOT_YP_EINVAL_ID,
+ KNOT_YP_EINVAL_DATA,
+ KNOT_YP_EINVAL_INDENT,
+ KNOT_YP_ENOTSUP_DATA,
+ KNOT_YP_ENOTSUP_ID,
+ KNOT_YP_ENODATA,
+ KNOT_YP_ENOID,
+
+ /* Configuration errors. */
+ KNOT_CONF_ENOTINIT,
+ KNOT_CONF_EVERSION,
+ KNOT_CONF_EREDEFINE,
+
+ /* Transaction errors. */
+ KNOT_TXN_EEXISTS,
+ KNOT_TXN_ENOTEXISTS,
+
+ /* DNSSEC errors. */
+ KNOT_INVALID_PUBLIC_KEY,
+ KNOT_INVALID_PRIVATE_KEY,
+ KNOT_INVALID_KEY_ALGORITHM,
+ KNOT_INVALID_KEY_SIZE,
+ KNOT_INVALID_KEY_ID,
+ KNOT_INVALID_KEY_NAME,
+ KNOT_NO_PUBLIC_KEY,
+ KNOT_NO_PRIVATE_KEY,
+ KNOT_NO_READY_KEY,
+
+ KNOT_ERROR_MAX = -501
+};
+
+/*!
+ * \brief Map POSIX errno code to Knot error code.
+ *
+ * \param code Errno code to transform (set -1 to use the current errno).
+ * \param dflt_error Default return value, if code is unknown.
+ *
+ * \return Mapped errno or the value of dflt_error if unknown.
+ */
+inline static int knot_map_errno_code_def(int code, int dflt_error)
+{
+ if (code < 0) {
+ code = errno;
+ }
+
+ typedef struct {
+ int errno_code;
+ int libknot_code;
+ } err_table_t;
+
+ #define ERR_ITEM(name) { name, KNOT_##name }
+ static const err_table_t errno_to_errcode[] = {
+ ERR_ITEM(ENOMEM),
+ ERR_ITEM(EINVAL),
+ ERR_ITEM(ENOTSUP),
+ ERR_ITEM(EBUSY),
+ ERR_ITEM(EAGAIN),
+ ERR_ITEM(ENOBUFS),
+ ERR_ITEM(EMFILE),
+ ERR_ITEM(ENFILE),
+ ERR_ITEM(EACCES),
+ ERR_ITEM(EISCONN),
+ ERR_ITEM(ECONNREFUSED),
+ ERR_ITEM(EALREADY),
+ ERR_ITEM(ECONNRESET),
+ ERR_ITEM(ECONNABORTED),
+ ERR_ITEM(ENETRESET),
+ ERR_ITEM(EHOSTUNREACH),
+ ERR_ITEM(ENETUNREACH),
+ ERR_ITEM(EHOSTDOWN),
+ ERR_ITEM(ENETDOWN),
+ ERR_ITEM(EADDRINUSE),
+ ERR_ITEM(ENOENT),
+ ERR_ITEM(EEXIST),
+ ERR_ITEM(ERANGE),
+ ERR_ITEM(EADDRNOTAVAIL),
+
+ /* Terminator - the value isn't used. */
+ { 0, KNOT_ERRNO_ERROR }
+ };
+ #undef ERR_ITEM
+
+ const err_table_t *err = errno_to_errcode;
+
+ while (err->errno_code != 0 && err->errno_code != code) {
+ err++;
+ }
+
+ return err->errno_code != 0 ? err->libknot_code : dflt_error;
+}
+
+/*!
+ * \brief Map POSIX errno code to Knot error code.
+ *
+ * \param code Errno code to transform (set -1 to use the current errno).
+ *
+ * \return Mapped errno or KNOT_ERRNO_ERROR if unknown.
+ */
+inline static int knot_map_errno_code(int code)
+{
+ return knot_map_errno_code_def(code, KNOT_ERRNO_ERROR);
+}
+
+/*!
+ * \brief Get a POSIX errno mapped to Knot error code.
+ *
+ * \param dflt_error Default return value, if code is unknown.
+ *
+ * \return Mapped errno.
+ */
+inline static int knot_map_errno_def(int dflt_error)
+{
+ return knot_map_errno_code_def(-1, dflt_error);
+}
+
+/*!
+ * \brief Get a POSIX errno mapped to Knot error code.
+ *
+ * \return Mapped errno or KNOT_ERRNO_ERROR if unknown.
+ */
+inline static int knot_map_errno(void)
+{
+ return knot_map_errno_code_def(-1, KNOT_ERRNO_ERROR);
+}
+
+/*! @} */
diff --git a/src/libknot/error.c b/src/libknot/error.c
new file mode 100644
index 0000000..352acd3
--- /dev/null
+++ b/src/libknot/error.c
@@ -0,0 +1,239 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <lmdb.h>
+
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+#include "libdnssec/error.h"
+
+struct error {
+ int code;
+ const char *message;
+};
+
+static const struct error errors[] = {
+ { KNOT_EOK, "OK" },
+
+ /* Directly mapped error codes. */
+ { KNOT_ENOMEM, "not enough memory" },
+ { KNOT_EINVAL, "invalid parameter" },
+ { KNOT_ENOTSUP, "operation not supported" },
+ { KNOT_EBUSY, "requested resource is busy" },
+ { KNOT_EAGAIN, "OS lacked necessary resources" },
+ { KNOT_ENOBUFS, "no buffers" },
+ { KNOT_EMFILE, "too many open files" },
+ { KNOT_ENFILE, "too many open files in system" },
+ { KNOT_EACCES, "operation not permitted" },
+ { KNOT_EISCONN, "already connected" },
+ { KNOT_ECONNREFUSED, "connection refused" },
+ { KNOT_EALREADY, "operation already in progress" },
+ { KNOT_ECONNRESET, "connection reset by peer" },
+ { KNOT_ECONNABORTED, "connection aborted" },
+ { KNOT_ENETRESET, "connection aborted by network" },
+ { KNOT_EHOSTUNREACH, "host is unreachable" },
+ { KNOT_ENETUNREACH, "network is unreachable" },
+ { KNOT_EHOSTDOWN, "host is down" },
+ { KNOT_ENETDOWN, "network is down" },
+ { KNOT_EADDRINUSE, "address already in use" },
+ { KNOT_ENOENT, "not exists" },
+ { KNOT_EEXIST, "already exists" },
+ { KNOT_ERANGE, "value is out of range" },
+ { KNOT_EADDRNOTAVAIL, "address is not available" },
+ { KNOT_ENOTDIR, "not a directory" },
+
+ { KNOT_ERRNO_ERROR, "unknown system error" },
+
+ /* General errors. */
+ { KNOT_ERROR, "failed" },
+ { KNOT_EPARSEFAIL, "parser failed" },
+ { KNOT_ESEMCHECK, "semantic check" },
+ { KNOT_EUPTODATE, "zone is up-to-date" },
+ { KNOT_EFEWDATA, "not enough data to parse" },
+ { KNOT_ESPACE, "not enough space provided" },
+ { KNOT_EMALF, "malformed data" },
+ { KNOT_ENSEC3PAR, "missing or wrong NSEC3PARAM record" },
+ { KNOT_ENSEC3CHAIN, "missing or wrong NSEC3 chain in the zone" },
+ { KNOT_EOUTOFZONE, "name does not belong to the zone" },
+ { KNOT_EZONEINVAL, "invalid zone file" },
+ { KNOT_ENOZONE, "no such zone found" },
+ { KNOT_ENONODE, "no such node in zone found" },
+ { KNOT_ENORECORD, "no such record in zone found" },
+ { KNOT_EISRECORD, "such record already exists in zone" },
+ { KNOT_ENOMASTER, "no usable master" },
+ { KNOT_EPREREQ, "UPDATE prerequisite not met" },
+ { KNOT_ETTL, "TTL mismatch" },
+ { KNOT_ENOTTL, "no TTL specified" },
+ { KNOT_ENOXFR, "transfer was not sent" },
+ { KNOT_EDENIED, "not allowed" },
+ { KNOT_ECONN, "connection reset" },
+ { KNOT_ETIMEOUT, "connection timeout" },
+ { KNOT_ENODIFF, "cannot create zone diff" },
+ { KNOT_ENOTSIG, "expected a TSIG or SIG(0)" },
+ { KNOT_ELIMIT, "exceeded limit" },
+ { KNOT_EZONESIZE, "zone size exceeded" },
+ { KNOT_EOF, "end of file" },
+ { KNOT_ESYSTEM, "system error" },
+ { KNOT_EFILE, "file error" },
+ { KNOT_ESOAINVAL, "SOA mismatch" },
+ { KNOT_ETRAIL, "trailing data" },
+ { KNOT_EPROCESSING, "processing error" },
+ { KNOT_EPROGRESS, "in progress" },
+ { KNOT_ELOOP, "loop detected" },
+ { KNOT_EPROGRAM, "program not loaded" },
+ { KNOT_EFD, "file descriptor error" },
+ { KNOT_ENOPARAM, "missing parameter" },
+ { KNOT_EXPARAM, "parameter conflict" },
+ { KNOT_EEMPTYZONE, "zone is empty" },
+ { KNOT_ENODB, "database does not exist" },
+ { KNOT_EUNREACH, "remote known to be unreachable" },
+
+ { KNOT_GENERAL_ERROR, "unknown general error" },
+
+ /* Control states. */
+ { KNOT_CTL_ESTOP, "stopping server" },
+ { KNOT_CTL_EZONE, "operation failed for some zones" },
+
+ /* Network errors. */
+ { KNOT_NET_EADDR, "bad address or host name" },
+ { KNOT_NET_ESOCKET, "can't create socket" },
+ { KNOT_NET_ECONNECT, "can't connect" },
+ { KNOT_NET_ESEND, "can't send data" },
+ { KNOT_NET_ERECV, "can't receive data" },
+ { KNOT_NET_ETIMEOUT, "network timeout" },
+
+ /* Encoding errors. */
+ { KNOT_BASE64_ESIZE, "invalid base64 string length" },
+ { KNOT_BASE64_ECHAR, "invalid base64 character" },
+ { KNOT_BASE32HEX_ESIZE, "invalid base32hex string length" },
+ { KNOT_BASE32HEX_ECHAR, "invalid base32hex character" },
+
+ /* TSIG errors. */
+ { KNOT_TSIG_EBADSIG, "failed to verify TSIG" },
+ { KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid" },
+ { KNOT_TSIG_EBADTIME, "TSIG out of time window" },
+ { KNOT_TSIG_EBADTRUNC, "TSIG bad truncation" },
+
+ /* DNSSEC errors. */
+ { KNOT_DNSSEC_ENOKEY, "no keys for signing" },
+ { KNOT_DNSSEC_EMISSINGKEYTYPE, "missing active KSK or ZSK" },
+ { KNOT_DNSSEC_ENOSIG, "no valid signature for a record" },
+ { KNOT_DNSSEC_ENONSEC, "missing NSEC(3) record" },
+ { KNOT_DNSSEC_ENSEC_BITMAP, "wrong NSEC(3) bitmap" },
+ { KNOT_DNSSEC_ENSEC_CHAIN, "inconsistent NSEC(3) chain" },
+ { KNOT_DNSSEC_ENSEC3_OPTOUT, "wrong NSEC3 opt-out" },
+
+ /* Yparser errors. */
+ { KNOT_YP_ECHAR_TAB, "tabulator character is not allowed" },
+ { KNOT_YP_EINVAL_ITEM, "invalid item" },
+ { KNOT_YP_EINVAL_ID, "invalid identifier" },
+ { KNOT_YP_EINVAL_DATA, "invalid value" },
+ { KNOT_YP_EINVAL_INDENT, "invalid indentation" },
+ { KNOT_YP_ENOTSUP_DATA, "value not supported" },
+ { KNOT_YP_ENOTSUP_ID, "identifier not supported" },
+ { KNOT_YP_ENODATA, "missing value" },
+ { KNOT_YP_ENOID, "missing identifier" },
+
+ /* Configuration errors. */
+ { KNOT_CONF_ENOTINIT, "config DB not initialized" },
+ { KNOT_CONF_EVERSION, "invalid config DB version" },
+ { KNOT_CONF_EREDEFINE, "duplicate identifier" },
+
+ /* Transaction errors. */
+ { KNOT_TXN_EEXISTS, "too many transactions" },
+ { KNOT_TXN_ENOTEXISTS, "no active transaction" },
+
+ /* DNSSEC errors. */
+ { KNOT_INVALID_PUBLIC_KEY, "invalid public key" },
+ { KNOT_INVALID_PRIVATE_KEY, "invalid private key" },
+ { KNOT_INVALID_KEY_ALGORITHM, "invalid key algorithm" },
+ { KNOT_INVALID_KEY_SIZE, "invalid key size" },
+ { KNOT_INVALID_KEY_ID, "invalid key ID" },
+ { KNOT_INVALID_KEY_NAME, "invalid key name" },
+ { KNOT_NO_PUBLIC_KEY, "no public key" },
+ { KNOT_NO_PRIVATE_KEY, "no private key" },
+ { KNOT_NO_READY_KEY, "no key ready for submission" },
+
+ /* Terminator */
+ { KNOT_ERROR, NULL }
+};
+
+/*!
+ * \brief Lookup error message by error code.
+ */
+static const char *lookup_message(int code)
+{
+ for (const struct error *e = errors; e->message; e++) {
+ if (e->code == code) {
+ return e->message;
+ }
+ }
+
+ return NULL;
+}
+
+_public_
+int knot_error_from_libdnssec(int libdnssec_errcode)
+{
+ switch (libdnssec_errcode) {
+ case DNSSEC_ERROR:
+ return KNOT_ERROR;
+ case DNSSEC_MALFORMED_DATA:
+ return KNOT_EMALF;
+ case DNSSEC_NOT_FOUND:
+ return KNOT_ENOENT;
+ case DNSSEC_NO_PUBLIC_KEY:
+ case DNSSEC_NO_PRIVATE_KEY:
+ return KNOT_DNSSEC_ENOKEY;
+ // EOK, EINVAL, ENOMEM and ENOENT are identical, no need to translate
+ case DNSSEC_INVALID_PUBLIC_KEY ... DNSSEC_INVALID_KEY_NAME:
+ return libdnssec_errcode
+ - DNSSEC_INVALID_PUBLIC_KEY + KNOT_INVALID_PUBLIC_KEY;
+ default:
+ return libdnssec_errcode;
+ }
+}
+
+_public_
+const char *knot_strerror(int code)
+{
+ const char *msg;
+
+ switch (code) {
+ case INT_MIN: // Cannot convert to a positive value.
+ code = KNOT_ERROR;
+ // FALLTHROUGH
+ case KNOT_ERROR_MIN ... KNOT_EOK:
+ msg = lookup_message(code); break;
+ case DNSSEC_ERROR_MIN ... DNSSEC_ERROR_MAX:
+ msg = dnssec_strerror(code); break;
+ case MDB_KEYEXIST ... MDB_LAST_ERRCODE:
+ msg = mdb_strerror(code); break;
+ default:
+ msg = NULL;
+ }
+
+ if (msg != NULL) {
+ return msg;
+ } else {
+ // strerror_r would be better but it requires thread local storage.
+ return strerror(abs(code));
+ }
+}
diff --git a/src/libknot/error.h b/src/libknot/error.h
new file mode 100644
index 0000000..4403497
--- /dev/null
+++ b/src/libknot/error.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2016 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/>.
+ */
+
+/*!
+* \file
+*
+* \brief Error codes and function for getting error message.
+*
+* \addtogroup libknot
+* @{
+*/
+
+#pragma once
+
+#include "libknot/errcode.h"
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+const char *knot_strerror(int code);
+
+/*!
+ * \brief Translates error code from libdnssec into libknot.
+ *
+ * This is just temporary until everything from libdnssec moved to libknot.
+ *
+ * \param libdnssec_errcode Error code from libdnssec
+ *
+ * \return Error code.
+ */
+int knot_error_from_libdnssec(int libdnssec_errcode);
+
+/*! @} */
diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h
new file mode 100644
index 0000000..f62c2c7
--- /dev/null
+++ b/src/libknot/libknot.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Convenience header for including whole library.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/version.h"
+#include "libknot/attribute.h"
+#include "libknot/cookies.h"
+#include "libknot/codes.h"
+#include "libknot/consts.h"
+#include "libknot/descriptor.h"
+#include "libknot/dname.h"
+#include "libknot/dynarray.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "libknot/error.h"
+#include "libknot/lookup.h"
+#include "libknot/mm_ctx.h"
+#include "libknot/rdata.h"
+#include "libknot/rdataset.h"
+#include "libknot/rrset-dump.h"
+#include "libknot/rrset.h"
+#include "libknot/tsig-op.h"
+#include "libknot/tsig.h"
+#include "libknot/control/control.h"
+#include "libknot/db/db.h"
+#include "libknot/db/db_lmdb.h"
+#include "libknot/db/db_trie.h"
+#include "libknot/packet/compr.h"
+#include "libknot/packet/pkt.h"
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/packet/wire.h"
+#include "libknot/probe/data.h"
+#include "libknot/probe/probe.h"
+#include "libknot/rrtype/dnskey.h"
+#include "libknot/rrtype/ds.h"
+#include "libknot/rrtype/naptr.h"
+#include "libknot/rrtype/nsec.h"
+#include "libknot/rrtype/nsec3.h"
+#include "libknot/rrtype/nsec3param.h"
+#include "libknot/rrtype/opt.h"
+#include "libknot/rrtype/rdname.h"
+#include "libknot/rrtype/rrsig.h"
+#include "libknot/rrtype/soa.h"
+#include "libknot/rrtype/svcb.h"
+#include "libknot/rrtype/tsig.h"
+#include "libknot/rrtype/zonemd.h"
+#include "libknot/wire.h"
+
+/*! @} */
diff --git a/src/libknot/lookup.h b/src/libknot/lookup.h
new file mode 100644
index 0000000..8599aa3
--- /dev/null
+++ b/src/libknot/lookup.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief A general purpose lookup table.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include <string.h>
+#include <strings.h>
+
+/*!
+ * \brief A general purpose lookup table.
+ */
+typedef struct knot_lookup {
+ int id;
+ const char *name;
+} knot_lookup_t;
+
+/*!
+ * \brief Looks up the given name in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param name Name to look up.
+ *
+ * \return Item in the lookup table with the given name or NULL if no such is
+ * present.
+ */
+inline static const knot_lookup_t *knot_lookup_by_name(const knot_lookup_t *table, const char *name)
+{
+ if (table == NULL || name == NULL) {
+ return NULL;
+ }
+
+ while (table->name != NULL) {
+ if (strcasecmp(name, table->name) == 0) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Looks up the given id in the lookup table.
+ *
+ * \param table Lookup table.
+ * \param id ID to look up.
+ *
+ * \return Item in the lookup table with the given id or NULL if no such is
+ * present.
+ */
+inline static const knot_lookup_t *knot_lookup_by_id(const knot_lookup_t *table, int id)
+{
+ if (table == NULL) {
+ return NULL;
+ }
+
+ while (table->name != NULL) {
+ if (table->id == id) {
+ return table;
+ }
+ table++;
+ }
+
+ return NULL;
+}
+
+/*! @} */
diff --git a/src/libknot/mm_ctx.h b/src/libknot/mm_ctx.h
new file mode 100644
index 0000000..8f0544c
--- /dev/null
+++ b/src/libknot/mm_ctx.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2011 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Memory allocation context.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+
+/* 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;
+
+/*! @} */
diff --git a/src/libknot/packet/compr.h b/src/libknot/packet/compr.h
new file mode 100644
index 0000000..ea14d64
--- /dev/null
+++ b/src/libknot/packet/compr.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Name compression API.
+ *
+ * \addtogroup pkt
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/packet/wire.h"
+
+/*! \brief Compression hint type. */
+enum knot_compr_hint {
+ KNOT_COMPR_HINT_NONE = 0, /* No hint. */
+ KNOT_COMPR_HINT_NOCOMP = 1, /* Don't compress. */
+ KNOT_COMPR_HINT_QNAME = KNOT_WIRE_HEADER_SIZE /* Name is QNAME. */
+};
+
+/*! \brief Compression hint array offsets. */
+enum knot_compr_offset {
+ KNOT_COMPR_HINT_OWNER = 0, /* First element in the array is RR owner. */
+ KNOT_COMPR_HINT_RDATA = 1, /* First name in RDATA is at offset 1. */
+ KNOT_COMPR_HINT_COUNT = 16 /* Maximum number of stored hints per-RR. */
+};
+
+/*
+ * \note A little bit about how compression hints work.
+ *
+ * We're storing a RRSet say 'abcd. CNAME [0]net. [1]com.' (owner=abcd. 2 RRs).
+ * The owner 'abcd.' is same for both RRs, we put it at the offset 0 in rrinfo.compress_ptr
+ * The names 'net.' and 'com.' are in the RDATA, therefore go to offsets 1 and 2.
+ * Now this is useful when solving additionals for example, because we can scan
+ * rrinfo for this RRSet and we know that 'net.' name is at the hint 1 and that leads
+ * to packet position N. With that, we just put the pointer in without any calculation.
+ * This is also useful for positive answers, where we know the RRSet owner is always QNAME.
+ * All in all, we just remember the positions of written domain names.
+ */
+
+/*! \brief Additional information about RRSet position and compression hints. */
+typedef struct {
+ uint16_t pos; /* RRSet position in the packet. */
+ uint16_t flags; /* RRSet flags. */
+ uint16_t compress_ptr[KNOT_COMPR_HINT_COUNT]; /* Array of compr. ptr hints. */
+} knot_rrinfo_t;
+
+/*!
+ * \brief Name compression context.
+ */
+typedef struct knot_compr {
+ uint8_t *wire; /* Packet wireformat. */
+ knot_rrinfo_t *rrinfo; /* Hints for current RRSet. */
+ struct {
+ uint16_t pos; /* Position of current suffix. */
+ uint8_t labels; /* Label count of the suffix. */
+ } suffix;
+} knot_compr_t;
+
+/*!
+ * \brief Retrieve compression hint from given offset.
+ */
+static inline uint16_t knot_compr_hint(const knot_rrinfo_t *info, uint16_t hint_id)
+{
+ if (hint_id < KNOT_COMPR_HINT_COUNT) {
+ return info->compress_ptr[hint_id];
+ } else {
+ return KNOT_COMPR_HINT_NONE;
+ }
+}
+
+/*!
+ * \brief Store compression hint for given offset.
+ */
+static inline void knot_compr_hint_set(knot_rrinfo_t *info, uint16_t hint_id,
+ uint16_t val, uint16_t len)
+{
+ if ((hint_id < KNOT_COMPR_HINT_COUNT) && (val + len < KNOT_WIRE_PTR_MAX)) {
+ info->compress_ptr[hint_id] = val;
+ }
+}
+
+/*! @} */
diff --git a/src/libknot/packet/pkt.c b/src/libknot/packet/pkt.c
new file mode 100644
index 0000000..728bb3e
--- /dev/null
+++ b/src/libknot/packet/pkt.c
@@ -0,0 +1,837 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "libknot/attribute.h"
+#include "libknot/packet/pkt.h"
+#include "libknot/codes.h"
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+#include "libknot/rrtype/tsig.h"
+#include "libknot/tsig-op.h"
+#include "libknot/packet/wire.h"
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/wire.h"
+#include "contrib/mempattern.h"
+#include "contrib/wire_ctx.h"
+
+/*! \brief Packet RR array growth step. */
+#define NEXT_RR_ALIGN 16
+#define NEXT_RR_COUNT(count) (((count) / NEXT_RR_ALIGN + 1) * NEXT_RR_ALIGN)
+
+/*! \brief Scan packet for RRSet existence. */
+static bool pkt_contains(const knot_pkt_t *packet, const knot_rrset_t *rrset)
+{
+ assert(packet);
+ assert(rrset);
+
+ for (int i = 0; i < packet->rrset_count; ++i) {
+ const uint16_t type = packet->rr[i].type;
+ const knot_rdata_t *data = packet->rr[i].rrs.rdata;
+ if (type == rrset->type && data == rrset->rrs.rdata) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*! \brief Free all RRSets and reset RRSet count. */
+static void pkt_free_data(knot_pkt_t *pkt)
+{
+ assert(pkt);
+
+ /* Free RRSets if applicable. */
+ for (uint16_t i = 0; i < pkt->rrset_count; ++i) {
+ if (pkt->rr_info[i].flags & KNOT_PF_FREE) {
+ knot_rrset_clear(&pkt->rr[i], &pkt->mm);
+ }
+ }
+ pkt->rrset_count = 0;
+
+ /* Free EDNS option positions. */
+ mm_free(&pkt->mm, pkt->edns_opts);
+ pkt->edns_opts = 0;
+}
+
+/*! \brief Allocate new wireformat of given length, assuming *pkt is zeroed. */
+static int pkt_wire_alloc(knot_pkt_t *pkt, uint16_t len)
+{
+ assert(pkt);
+
+ if (len < KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_ERANGE;
+ }
+
+ pkt->wire = mm_alloc(&pkt->mm, len);
+ if (pkt->wire == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ pkt->flags |= KNOT_PF_FREE;
+ pkt->max_size = len;
+
+ /* Reset to header size. */
+ pkt->size = KNOT_WIRE_HEADER_SIZE;
+ memset(pkt->wire, 0, pkt->size);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Set packet wireformat to an existing memory. */
+static void pkt_wire_set(knot_pkt_t *pkt, void *wire, uint16_t len)
+{
+ assert(pkt);
+
+ pkt->wire = wire;
+ pkt->size = pkt->max_size = len;
+ pkt->parsed = 0;
+}
+
+/*! \brief Calculate remaining size in the packet. */
+static uint16_t pkt_remaining(knot_pkt_t *pkt)
+{
+ assert(pkt);
+
+ return pkt->max_size - pkt->size - pkt->reserved;
+}
+
+/*! \brief Return RR count for given section (from wire xxCOUNT in header). */
+static uint16_t pkt_rr_wirecount(knot_pkt_t *pkt, knot_section_t section_id)
+{
+ assert(pkt);
+ switch (section_id) {
+ case KNOT_ANSWER: return knot_wire_get_ancount(pkt->wire);
+ case KNOT_AUTHORITY: return knot_wire_get_nscount(pkt->wire);
+ case KNOT_ADDITIONAL: return knot_wire_get_arcount(pkt->wire);
+ default: assert(0); return 0;
+ }
+}
+
+/*! \brief Update RR count for given section (wire xxCOUNT in header). */
+static void pkt_rr_wirecount_add(knot_pkt_t *pkt, knot_section_t section_id,
+ int16_t val)
+{
+ assert(pkt);
+ switch (section_id) {
+ case KNOT_ANSWER: knot_wire_add_ancount(pkt->wire, val); break;
+ case KNOT_AUTHORITY: knot_wire_add_nscount(pkt->wire, val); break;
+ case KNOT_ADDITIONAL: knot_wire_add_arcount(pkt->wire, val); break;
+ }
+}
+
+/*! \brief Reserve enough space in the RR arrays. */
+static int pkt_rr_array_alloc(knot_pkt_t *pkt, uint16_t count)
+{
+ /* Enough space. */
+ if (pkt->rrset_allocd >= count) {
+ return KNOT_EOK;
+ }
+
+ /* Allocate rr_info and rr fields to next size. */
+ size_t next_size = NEXT_RR_COUNT(count);
+ knot_rrinfo_t *rr_info = mm_alloc(&pkt->mm, sizeof(knot_rrinfo_t) * next_size);
+ if (rr_info == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_t *rr = mm_alloc(&pkt->mm, sizeof(knot_rrset_t) * next_size);
+ if (rr == NULL) {
+ mm_free(&pkt->mm, rr_info);
+ return KNOT_ENOMEM;
+ }
+
+ /* Copy and free the old data, if any. */
+ if (pkt->rrset_allocd > 0) {
+ memcpy(rr_info, pkt->rr_info, pkt->rrset_allocd * sizeof(knot_rrinfo_t));
+ memcpy(rr, pkt->rr, pkt->rrset_allocd * sizeof(knot_rrset_t));
+ mm_free(&pkt->mm, pkt->rr);
+ mm_free(&pkt->mm, pkt->rr_info);
+ }
+ pkt->rr = rr;
+ pkt->rr_info = rr_info;
+ pkt->rrset_allocd = next_size;
+
+ return KNOT_EOK;
+}
+
+static void compr_clear(knot_compr_t *compr)
+{
+ compr->rrinfo = NULL;
+ compr->suffix.pos = 0;
+ compr->suffix.labels = 0;
+}
+
+/*! \brief Clear the packet and switch wireformat pointers (possibly allocate new). */
+static int pkt_init(knot_pkt_t *pkt, void *wire, uint16_t len, knot_mm_t *mm)
+{
+ assert(pkt);
+
+ memset(pkt, 0, offsetof(knot_pkt_t, lower_qname));
+ pkt->lower_qname[0] = '\0';
+
+ /* No data to free, set memory context. */
+ memcpy(&pkt->mm, mm, sizeof(knot_mm_t));
+
+ /* Initialize wire. */
+ int ret = KNOT_EOK;
+ if (wire == NULL) {
+ ret = pkt_wire_alloc(pkt, len);
+ } else {
+ pkt_wire_set(pkt, wire, len);
+ }
+
+ /* Initialize compression context (zeroed above). */
+ pkt->compr.wire = pkt->wire;
+
+ return ret;
+}
+
+/*! \brief Reset packet parse state. */
+static void sections_reset(knot_pkt_t *pkt)
+{
+ pkt->current = KNOT_ANSWER;
+ memset(pkt->sections, 0, sizeof(pkt->sections));
+ (void)knot_pkt_begin(pkt, KNOT_ANSWER);
+}
+
+/*! \brief Allocate new packet using memory context. */
+static knot_pkt_t *pkt_new_mm(void *wire, uint16_t len, knot_mm_t *mm)
+{
+ assert(mm);
+
+ knot_pkt_t *pkt = mm_alloc(mm, sizeof(knot_pkt_t));
+ if (pkt == NULL) {
+ return NULL;
+ }
+
+ if (pkt_init(pkt, wire, len, mm) != KNOT_EOK) {
+ mm_free(mm, pkt);
+ return NULL;
+ }
+
+ return pkt;
+}
+
+_public_
+knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm)
+{
+ /* Default memory allocator if NULL. */
+ knot_mm_t _mm;
+ if (mm == NULL) {
+ mm_ctx_init(&_mm);
+ mm = &_mm;
+ }
+
+ return pkt_new_mm(wire, len, mm);
+}
+
+static int append_tsig(knot_pkt_t *dst, const knot_pkt_t *src)
+{
+ /* Check if a wire TSIG is available. */
+ if (src->tsig_wire.pos != NULL) {
+ if (dst->max_size < src->size + src->tsig_wire.len) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst->wire + dst->size, src->tsig_wire.pos,
+ src->tsig_wire.len);
+ dst->size += src->tsig_wire.len;
+
+ /* Increment arcount. */
+ knot_wire_set_arcount(dst->wire,
+ knot_wire_get_arcount(dst->wire) + 1);
+ } else {
+ return knot_tsig_append(dst->wire, &dst->size, dst->max_size,
+ src->tsig_rr);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (dst->max_size < src->size) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst->wire, src->wire, src->size);
+ dst->size = src->size;
+
+ /* Append TSIG record. */
+ if (src->tsig_rr) {
+ int ret = append_tsig(dst, src);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ /* Invalidate arrays. */
+ dst->rr = NULL;
+ dst->rr_info = NULL;
+ dst->rrset_count = 0;
+ dst->rrset_allocd = 0;
+
+ /* @note This could be done more effectively if needed. */
+ return knot_pkt_parse(dst, 0);
+}
+
+static void payload_clear(knot_pkt_t *pkt)
+{
+ assert(pkt);
+
+ /* Keep question. */
+ pkt->parsed = 0;
+ pkt->reserved = 0;
+
+ /* Free RRSets if applicable. */
+ pkt_free_data(pkt);
+
+ /* Reset sections. */
+ sections_reset(pkt);
+
+ /* Reset special types. */
+ pkt->opt_rr = NULL;
+ pkt->tsig_rr = NULL;
+
+ /* Reset TSIG wire reference. */
+ pkt->tsig_wire.pos = NULL;
+ pkt->tsig_wire.len = 0;
+}
+
+_public_
+int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query)
+{
+ if (pkt == NULL || query == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Header + question size. */
+ size_t base_size = KNOT_WIRE_HEADER_SIZE + knot_pkt_question_size(query);
+ if (base_size > pkt->max_size) {
+ return KNOT_ESPACE;
+ }
+
+ pkt->size = base_size;
+ memcpy(pkt->wire, query->wire, base_size);
+
+ /* Copy lowercased QNAME. */
+ pkt->qname_size = query->qname_size;
+ if (query->qname_size == 0) {
+ /* Reset question count if malformed. */
+ knot_wire_set_qdcount(pkt->wire, 0);
+ }
+ memcpy(pkt->lower_qname, query->lower_qname, pkt->qname_size);
+
+ /* Update flags and section counters. */
+ knot_wire_set_ancount(pkt->wire, 0);
+ knot_wire_set_nscount(pkt->wire, 0);
+ knot_wire_set_arcount(pkt->wire, 0);
+
+ knot_wire_set_qr(pkt->wire);
+ knot_wire_clear_tc(pkt->wire);
+ knot_wire_clear_ad(pkt->wire);
+ knot_wire_clear_ra(pkt->wire);
+ knot_wire_clear_aa(pkt->wire);
+ knot_wire_clear_z(pkt->wire);
+
+ /* Clear payload. */
+ payload_clear(pkt);
+
+ /* Clear compression context. */
+ compr_clear(&pkt->compr);
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_pkt_clear(knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ /* Reset to header size. */
+ pkt->size = KNOT_WIRE_HEADER_SIZE;
+ memset(pkt->wire, 0, pkt->size);
+
+ /* Clear payload. */
+ payload_clear(pkt);
+
+ /* Clear compression context. */
+ compr_clear(&pkt->compr);
+
+ /* Initialize lowercased QNAME. */
+ pkt->lower_qname[0] = '\0';
+}
+
+_public_
+void knot_pkt_free(knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ /* Free temporary RRSets. */
+ pkt_free_data(pkt);
+
+ /* Free RR/RR info arrays. */
+ mm_free(&pkt->mm, pkt->rr);
+ mm_free(&pkt->mm, pkt->rr_info);
+
+ /* Free the space for wireformat. */
+ if (pkt->flags & KNOT_PF_FREE) {
+ mm_free(&pkt->mm, pkt->wire);
+ }
+
+ mm_free(&pkt->mm, pkt);
+}
+
+_public_
+int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size)
+{
+ if (pkt == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Reserve extra space (if possible). */
+ if (pkt_remaining(pkt) >= size) {
+ pkt->reserved += size;
+ return KNOT_EOK;
+ } else {
+ return KNOT_ERANGE;
+ }
+}
+
+_public_
+int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size)
+{
+ if (pkt == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (pkt->reserved >= size) {
+ pkt->reserved -= size;
+ return KNOT_EOK;
+ } else {
+ return KNOT_ERANGE;
+ }
+}
+
+_public_
+int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id)
+{
+ if (pkt == NULL || section_id < pkt->current) {
+ return KNOT_EINVAL;
+ }
+
+ /* Remember watermark but not on repeated calls. */
+ pkt->sections[section_id].pkt = pkt;
+ if (section_id > pkt->current) {
+ pkt->sections[section_id].pos = pkt->rrset_count;
+ }
+
+ pkt->current = section_id;
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype)
+{
+ if (pkt == NULL || qname == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ assert(pkt->size == KNOT_WIRE_HEADER_SIZE);
+ assert(pkt->rrset_count == 0);
+
+ /* Copy name into wire format buffer. */
+ wire_ctx_t wire = wire_ctx_init(pkt->wire, pkt->max_size);
+ wire_ctx_set_offset(&wire, KNOT_WIRE_HEADER_SIZE);
+
+ int qname_len = knot_dname_to_wire(wire.position,
+ qname, wire_ctx_available(&wire));
+ if (qname_len < 0) {
+ return qname_len;
+ }
+ wire_ctx_skip(&wire, qname_len);
+
+ /* Copy QNAME and canonicalize to lowercase. */
+ knot_dname_copy_lower(pkt->lower_qname, qname);
+
+ /* Copy QTYPE & QCLASS */
+ wire_ctx_write_u16(&wire, qtype);
+ wire_ctx_write_u16(&wire, qclass);
+
+ /* Check errors. */
+ if (wire.error != KNOT_EOK) {
+ return wire.error;
+ }
+
+ /* Update question count and sizes. */
+ knot_wire_set_qdcount(pkt->wire, 1);
+ pkt->size = wire_ctx_offset(&wire);
+ pkt->qname_size = qname_len;
+
+ /* Start writing ANSWER. */
+ return knot_pkt_begin(pkt, KNOT_ANSWER);
+}
+
+_public_
+int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
+ uint16_t rotate, uint16_t flags)
+{
+ if (pkt == NULL || rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Reserve memory for RR descriptors. */
+ int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check for double insertion. */
+ if ((flags & KNOT_PF_CHECKDUP) && pkt_contains(pkt, rr)) {
+ return KNOT_EOK;
+ }
+
+ knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count];
+ memset(rrinfo, 0, sizeof(knot_rrinfo_t));
+ rrinfo->pos = pkt->size;
+ rrinfo->flags = flags;
+ rrinfo->compress_ptr[0] = compr_hint;
+ memcpy(pkt->rr + pkt->rrset_count, rr, sizeof(knot_rrset_t));
+
+ /* Disable compression if no QNAME is available. */
+ knot_compr_t *compr = NULL;
+ if (knot_pkt_qname(pkt) != NULL) {
+ /* Initialize compression context if it did not happen yet. */
+ pkt->compr.rrinfo = rrinfo;
+ if (pkt->compr.suffix.pos == 0) {
+ pkt->compr.suffix.pos = KNOT_WIRE_HEADER_SIZE;
+ pkt->compr.suffix.labels =
+ knot_dname_labels(pkt->compr.wire + pkt->compr.suffix.pos,
+ pkt->compr.wire);
+ }
+
+ compr = &pkt->compr;
+ }
+
+ uint8_t *pos = pkt->wire + pkt->size;
+ size_t maxlen = pkt_remaining(pkt);
+
+ /* Write RRSet to wireformat. */
+ ret = knot_rrset_to_wire_extra(rr, pos, maxlen, rotate, compr, flags);
+ if (ret < 0) {
+ /* Truncate packet if required. */
+ if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) {
+ knot_wire_set_tc(pkt->wire);
+ }
+ return ret;
+ }
+
+ size_t len = ret;
+ uint16_t rr_added = rr->rrs.count;
+
+ /* Keep reference to special types. */
+ if (rr->type == KNOT_RRTYPE_OPT) {
+ pkt->opt_rr = &pkt->rr[pkt->rrset_count];
+ }
+
+ if (rr_added > 0) {
+ pkt->rrset_count += 1;
+ pkt->sections[pkt->current].count += 1;
+ pkt->size += len;
+ pkt_rr_wirecount_add(pkt, pkt->current, rr_added);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_pkt_parse_question(knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Check at least header size. */
+ if (pkt->size < KNOT_WIRE_HEADER_SIZE) {
+ return KNOT_EMALF;
+ }
+
+ /* We have at least some DNS header. */
+ pkt->parsed = KNOT_WIRE_HEADER_SIZE;
+
+ /* Check QD count. */
+ uint16_t qd = knot_wire_get_qdcount(pkt->wire);
+ if (qd > 1) {
+ return KNOT_EMALF;
+ }
+
+ /* No question. */
+ if (qd == 0) {
+ pkt->qname_size = 0;
+ return KNOT_EOK;
+ }
+
+ /* Process question. */
+ int len = knot_dname_wire_check(pkt->wire + pkt->parsed,
+ pkt->wire + pkt->size,
+ NULL /* No compression in QNAME. */);
+ if (len <= 0) {
+ return KNOT_EMALF;
+ }
+
+ /* Check QCLASS/QTYPE size. */
+ uint16_t question_size = len + 2 * sizeof(uint16_t); /* QCLASS + QTYPE */
+ if (pkt->parsed + question_size > pkt->size) {
+ return KNOT_EMALF;
+ }
+
+ pkt->parsed += question_size;
+ pkt->qname_size = len;
+
+ /* Copy QNAME and canonicalize to lowercase. */
+ knot_dname_copy_lower(pkt->lower_qname, pkt->wire + KNOT_WIRE_HEADER_SIZE);
+
+ return KNOT_EOK;
+}
+
+/*! \brief Check constraints (position, uniqueness, validity) for special types
+ * (TSIG, OPT).
+ */
+static int check_rr_constraints(knot_pkt_t *pkt, knot_rrset_t *rr, size_t rr_size,
+ unsigned flags)
+{
+ switch (rr->type) {
+ case KNOT_RRTYPE_TSIG:
+ if (pkt->current != KNOT_ADDITIONAL || pkt->tsig_rr != NULL ||
+ !knot_tsig_rdata_is_ok(rr)) {
+ return KNOT_EMALF;
+ }
+
+ /* Strip TSIG RR from wireformat and decrease ARCOUNT. */
+ if (!(flags & KNOT_PF_KEEPWIRE)) {
+ pkt->parsed -= rr_size;
+ pkt->size -= rr_size;
+ pkt->tsig_wire.pos = pkt->wire + pkt->parsed;
+ pkt->tsig_wire.len = rr_size;
+ knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1);
+ }
+
+ pkt->tsig_rr = rr;
+ break;
+ case KNOT_RRTYPE_OPT:
+ if (pkt->current != KNOT_ADDITIONAL || pkt->opt_rr != NULL ||
+ knot_edns_get_options(rr, &pkt->edns_opts, &pkt->mm) != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ pkt->opt_rr = rr;
+ break;
+ default:
+ break;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_rr(knot_pkt_t *pkt, unsigned flags)
+{
+ assert(pkt);
+
+ if (pkt->parsed >= pkt->size) {
+ return KNOT_EFEWDATA;
+ }
+
+ /* Reserve memory for RR descriptors. */
+ int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Initialize RR info. */
+ memset(&pkt->rr_info[pkt->rrset_count], 0, sizeof(knot_rrinfo_t));
+ pkt->rr_info[pkt->rrset_count].pos = pkt->parsed;
+ pkt->rr_info[pkt->rrset_count].flags = KNOT_PF_FREE;
+
+ /* Parse wire format. */
+ size_t rr_size = pkt->parsed;
+ knot_rrset_t *rr = &pkt->rr[pkt->rrset_count];
+ ret = knot_rrset_rr_from_wire(pkt->wire, &pkt->parsed, pkt->size,
+ rr, &pkt->mm, !(flags & KNOT_PF_NOCANON));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Calculate parsed RR size from before/after parsing. */
+ rr_size = (pkt->parsed - rr_size);
+
+ /* Update packet RRSet count. */
+ ++pkt->rrset_count;
+ ++pkt->sections[pkt->current].count;
+
+ /* Check special RRs (OPT and TSIG). */
+ return check_rr_constraints(pkt, rr, rr_size, flags);
+}
+
+static int parse_section(knot_pkt_t *pkt, unsigned flags)
+{
+ assert(pkt);
+
+ uint16_t rr_parsed = 0;
+ uint16_t rr_count = pkt_rr_wirecount(pkt, pkt->current);
+
+ /* Parse all RRs belonging to the section. */
+ for (rr_parsed = 0; rr_parsed < rr_count; ++rr_parsed) {
+ int ret = parse_rr(pkt, flags);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_payload(knot_pkt_t *pkt, unsigned flags)
+{
+ assert(pkt);
+ assert(pkt->wire);
+ assert(pkt->size > 0);
+
+ /* Reserve memory in advance to avoid resizing. */
+ size_t rr_count = knot_wire_get_ancount(pkt->wire) +
+ knot_wire_get_nscount(pkt->wire) +
+ knot_wire_get_arcount(pkt->wire);
+
+ if (rr_count > pkt->size / KNOT_WIRE_RR_MIN_SIZE) {
+ return KNOT_EMALF;
+ }
+
+ int ret = pkt_rr_array_alloc(pkt, rr_count);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) {
+ ret = knot_pkt_begin(pkt, i);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ret = parse_section(pkt, flags);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ /* TSIG must be last record of AR if present. */
+ const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL);
+ if (pkt->tsig_rr != NULL) {
+ const knot_rrset_t *last_rr = knot_pkt_rr(ar, ar->count - 1);
+ if (ar->count > 0 && pkt->tsig_rr->rrs.rdata != last_rr->rrs.rdata) {
+ return KNOT_EMALF;
+ }
+ }
+
+ /* Check for trailing garbage. */
+ if (pkt->parsed < pkt->size) {
+ return KNOT_ETRAIL;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags)
+{
+ if (pkt == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Reset parse state. */
+ sections_reset(pkt);
+
+ int ret = knot_pkt_parse_question(pkt);
+ if (ret == KNOT_EOK) {
+ ret = parse_payload(pkt, flags);
+ }
+
+ return ret;
+}
+
+_public_
+uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return 0;
+ }
+
+ /* Get header RCODE. */
+ uint16_t rcode = knot_wire_get_rcode(pkt->wire);
+
+ /* Update to extended RCODE if EDNS is available. */
+ if (pkt->opt_rr != NULL) {
+ uint8_t opt_rcode = knot_edns_get_ext_rcode(pkt->opt_rr);
+ rcode = knot_edns_whole_rcode(opt_rcode, rcode);
+ }
+
+ /* Return if not NOTAUTH. */
+ if (rcode != KNOT_RCODE_NOTAUTH) {
+ return rcode;
+ }
+
+ /* Get TSIG RCODE. */
+ uint16_t tsig_rcode = KNOT_RCODE_NOERROR;
+ if (pkt->tsig_rr != NULL) {
+ tsig_rcode = knot_tsig_rdata_error(pkt->tsig_rr);
+ }
+
+ /* Return proper RCODE. */
+ if (tsig_rcode != KNOT_RCODE_NOERROR) {
+ return tsig_rcode;
+ } else {
+ return rcode;
+ }
+}
+
+_public_
+const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL) {
+ return "";
+ }
+
+ uint16_t rcode = knot_pkt_ext_rcode(pkt);
+
+ const knot_lookup_t *item = NULL;
+ if (pkt->tsig_rr != NULL) {
+ item = knot_lookup_by_id(knot_tsig_rcode_names, rcode);
+ }
+ if (item == NULL) {
+ item = knot_lookup_by_id(knot_rcode_names, rcode);
+ }
+
+ return (item != NULL) ? item->name : "";
+}
diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h
new file mode 100644
index 0000000..f5e218f
--- /dev/null
+++ b/src/libknot/packet/pkt.h
@@ -0,0 +1,416 @@
+/* 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Structure for holding DNS packet data and metadata.
+ *
+ * \addtogroup pkt
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/mm_ctx.h"
+#include "libknot/rrset.h"
+#include "libknot/rrtype/opt.h"
+#include "libknot/packet/wire.h"
+#include "libknot/packet/compr.h"
+#include "libknot/wire.h"
+
+/* Number of packet sections (ANSWER, AUTHORITY, ADDITIONAL). */
+#define KNOT_PKT_SECTIONS 3
+
+/*!
+ * \brief Packet flags.
+ */
+enum {
+ KNOT_PF_NULL = 0 << 0, /*!< No flags. */
+ KNOT_PF_FREE = 1 << 1, /*!< Free with packet. */
+ KNOT_PF_NOTRUNC = 1 << 2, /*!< Don't truncate. */
+ KNOT_PF_CHECKDUP = 1 << 3, /*!< Check for duplicates. */
+ KNOT_PF_KEEPWIRE = 1 << 4, /*!< Keep wireformat untouched when parsing. */
+ KNOT_PF_NOCANON = 1 << 5, /*!< Don't canonicalize rrsets during parsing. */
+ KNOT_PF_ORIGTTL = 1 << 6, /*!< Write RRSIGs with their original TTL. */
+ KNOT_PF_SOAMINTTL = 1 << 7, /*!< Write SOA with its minimum-ttl as TTL. */
+};
+
+typedef struct knot_pkt knot_pkt_t;
+
+/*!
+ * \brief Packet section.
+ * Points to RRSet and RRSet info arrays in the packet.
+ * This structure is required for random access to packet sections.
+ */
+typedef struct {
+ knot_pkt_t *pkt; /*!< Owner. */
+ uint16_t pos; /*!< Position in the rr/rrinfo fields in packet. */
+ uint16_t count; /*!< Number of RRSets in this section. */
+} knot_pktsection_t;
+
+/*!
+ * \brief Structure representing a DNS packet.
+ */
+struct knot_pkt {
+ uint8_t *wire; /*!< Wire format of the packet. */
+ size_t size; /*!< Current wire size of the packet. */
+ size_t max_size; /*!< Maximum allowed size of the packet. */
+ size_t parsed; /*!< Parsed size. */
+ uint16_t reserved; /*!< Reserved space. */
+ uint16_t qname_size; /*!< QNAME size. */
+ uint16_t rrset_count; /*!< Packet RRSet count. */
+ uint16_t flags; /*!< Packet flags. */
+
+ knot_rrset_t *opt_rr; /*!< OPT RR included in the packet. */
+ knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */
+
+ /*! EDNS option positions in the wire (if parsed from wire). */
+ knot_edns_options_t *edns_opts;
+
+ /*! TSIG RR position in the wire (if parsed from wire). */
+ struct {
+ uint8_t *pos;
+ size_t len;
+ } tsig_wire;
+
+ /* Packet sections. */
+ knot_section_t current;
+ knot_pktsection_t sections[KNOT_PKT_SECTIONS];
+
+ /* Packet RRSet (meta)data. */
+ size_t rrset_allocd;
+ knot_rrinfo_t *rr_info;
+ knot_rrset_t *rr;
+
+ knot_mm_t mm; /*!< Memory allocation context. */
+
+ knot_compr_t compr; /*!< Compression context. */
+
+ /*! Lowercased QNAME. MUST BE LAST ITEM! */
+ knot_dname_storage_t lower_qname;
+};
+
+/*!
+ * \brief Create new packet over existing memory, or allocate new from memory context.
+ *
+ * \note Packet is allocated from given memory context.
+ *
+ * \param wire If NULL, memory of 'len' size shall be allocated.
+ * Otherwise pointer is used for the wire format of the packet.
+ * \param len Wire format length.
+ * \param mm Memory context (NULL for default).
+ * \return New packet or NULL.
+ */
+knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm);
+
+/*!
+ * \brief Copy packet.
+ *
+ * \note Current implementation is not very efficient, as it re-parses the wire.
+ *
+ * \param dst Target packet.
+ * \param src Source packet.
+ *
+ * \return new packet or NULL
+ */
+int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src);
+
+/*!
+ * \brief Initialized response from query packet.
+ *
+ * \note Question is not checked, it is expected to be checked already.
+ *
+ * \param pkt Given packet.
+ * \param query Query.
+ * \return KNOT_EOK, KNOT_EINVAL, KNOT_ESPACE
+ */
+int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query);
+
+/*! \brief Reinitialize packet for another use. */
+void knot_pkt_clear(knot_pkt_t *pkt);
+
+/*! \brief Begone you foul creature of the underworld. */
+void knot_pkt_free(knot_pkt_t *pkt);
+
+/*!
+ * \brief Reserve an arbitrary amount of space in the packet.
+ *
+ * \return KNOT_EOK
+ * \return KNOT_ERANGE if size can't be reserved
+ */
+int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size);
+
+/*!
+ * \brief Reclaim reserved size.
+ *
+ * \return KNOT_EOK
+ * \return KNOT_ERANGE if size can't be reclaimed
+ */
+int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size);
+
+/*
+ * Packet QUESTION accessors.
+ */
+static inline uint16_t knot_pkt_question_size(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL || pkt->qname_size == 0) {
+ return 0;
+ }
+
+ return pkt->qname_size + 2 * sizeof(uint16_t);
+}
+
+static inline const knot_dname_t *knot_pkt_qname(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL || pkt->qname_size == 0) {
+ return NULL;
+ }
+
+ return pkt->lower_qname;
+}
+
+static inline const knot_dname_t *knot_pkt_wire_qname(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL || pkt->qname_size == 0) {
+ return NULL;
+ }
+
+ return pkt->wire + KNOT_WIRE_HEADER_SIZE;
+}
+
+static inline uint16_t knot_pkt_qtype(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL || pkt->qname_size == 0) {
+ return 0;
+ }
+
+ unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size;
+ return knot_wire_read_u16(pkt->wire + off);
+}
+
+static inline uint16_t knot_pkt_qclass(const knot_pkt_t *pkt)
+{
+ if (pkt == NULL || pkt->qname_size == 0) {
+ return 0;
+ }
+
+ unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size + sizeof(uint16_t);
+ return knot_wire_read_u16(pkt->wire + off);
+}
+
+/*
+ * Packet writing API.
+ */
+
+/*!
+ * \brief Begin reading/writing packet section.
+ *
+ * \note You must proceed in the natural order (ANSWER, AUTHORITY, ADDITIONAL).
+ *
+ * \param pkt
+ * \param section_id
+ * \return KNOT_EOK or KNOT_EINVAL
+ */
+int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id);
+
+/*!
+ * \brief Put QUESTION in the packet.
+ *
+ * \note Since we support QD=1 only, QUESTION is a special type of packet section.
+ * \note Must not be used after putting RRsets into the packet.
+ *
+ * \param pkt
+ * \param qname
+ * \param qclass
+ * \param qtype
+ * \return KNOT_EOK or various errors
+ */
+int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname,
+ uint16_t qclass, uint16_t qtype);
+
+/*!
+ * \brief Put RRSet into packet.
+ *
+ * \note See compr.h for description on how compression hints work.
+ * \note Available flags: PF_FREE, KNOT_PF_CHECKDUP, KNOT_PF_NOTRUNC
+ *
+ * \param pkt
+ * \param compr_hint Compression hint, see enum knot_compr_hint or absolute
+ * position.
+ * \param rr Given RRSet.
+ * \param rotate Rotate the RRSet order by this count.
+ * \param flags RRSet flags (set PF_FREE if you want RRSet to be freed
+ * with the packet).
+ *
+ * \return KNOT_EOK, KNOT_ESPACE, various errors
+ */
+int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr,
+ uint16_t rotate, uint16_t flags);
+
+/*! \brief Same as knot_pkt_put_rotate but without rrset rotation. */
+static inline int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint,
+ const knot_rrset_t *rr, uint16_t flags)
+{
+ return knot_pkt_put_rotate(pkt, compr_hint, rr, 0, flags);
+}
+
+/*! \brief Get description of the given packet section. */
+static inline const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt,
+ knot_section_t section_id)
+{
+ assert(pkt);
+ return &pkt->sections[section_id];
+}
+
+/*! \brief Get RRSet from the packet section. */
+static inline const knot_rrset_t *knot_pkt_rr(const knot_pktsection_t *section,
+ uint16_t i)
+{
+ assert(section);
+ return section->pkt->rr + section->pos + i;
+}
+
+/*! \brief Get RRSet offset in the packet wire. */
+static inline uint16_t knot_pkt_rr_offset(const knot_pktsection_t *section,
+ uint16_t i)
+{
+ assert(section);
+ return section->pkt->rr_info[section->pos + i].pos;
+}
+
+/*
+ * Packet parsing API.
+ */
+
+/*!
+ * \brief Parse both packet question and payload.
+ *
+ * Parses both QUESTION and all packet sections,
+ * includes semantic checks over specific RRs (TSIG, OPT).
+ *
+ * \note If KNOT_PF_KEEPWIRE is set, TSIG RR is not stripped from the wire
+ * and is processed as any other RR.
+ *
+ * \param pkt Given packet.
+ * \param flags Parsing flags (allowed KNOT_PF_KEEPWIRE)
+ *
+ * \retval KNOT_EOK if success.
+ * \retval KNOT_ETRAIL if success but with some trailing data.
+ * \retval KNOT_EMALF and other errors.
+ */
+int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags);
+
+/*!
+ * \brief Parse packet header and a QUESTION section.
+ */
+int knot_pkt_parse_question(knot_pkt_t *pkt);
+
+/*!
+ * \brief Get packet extended RCODE.
+ *
+ * Extended RCODE is created by considering TSIG RCODE, EDNS RCODE and
+ * DNS Header RCODE. (See RFC 6895, Section 2.3).
+ *
+ * \param pkt Packet to get the response code from.
+ *
+ * \return Whole extended RCODE (0 if pkt == NULL).
+ */
+uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt);
+
+/*!
+ * \brief Get packet extended RCODE name.
+ *
+ * The packet parameter is important as the name depends on TSIG.
+ *
+ * \param pkt Packet to get the response code from.
+ *
+ * \return RCODE name (or empty string if not known).
+ */
+const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt);
+
+/*!
+ * \brief Checks if there is an OPT RR in the packet.
+ */
+static inline bool knot_pkt_has_edns(const knot_pkt_t *pkt)
+{
+ assert(pkt);
+ return pkt->opt_rr != NULL;
+}
+
+/*!
+ * \brief Checks if TSIG is present.
+ */
+static inline bool knot_pkt_has_tsig(const knot_pkt_t *pkt)
+{
+ assert(pkt);
+ return pkt->tsig_rr != NULL;
+}
+
+/*!
+ * \brief Checks if DO bit is set in the packet's OPT RR.
+ */
+static inline bool knot_pkt_has_dnssec(const knot_pkt_t *pkt)
+{
+ assert(pkt);
+ return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr);
+}
+
+/*!
+ * \brief Get specific EDNS option from a parsed packet.
+ */
+static inline uint8_t *knot_pkt_edns_option(const knot_pkt_t *pkt, uint16_t code)
+{
+ assert(pkt);
+ if (pkt->edns_opts != NULL && code <= KNOT_EDNS_MAX_OPTION_CODE) {
+ return pkt->edns_opts->ptr[code];
+ } else {
+ return NULL;
+ }
+}
+
+/*!
+ * \brief Computes a reasonable Padding data length for a given packet and opt RR.
+ *
+ * \param pkt DNS Packet prepared and otherwise ready to go, no OPT yet added.
+ * \param opt_rr OPT RR, not yet including padding.
+ *
+ * \return Required padding length or -1 if padding not required.
+ */
+static inline int knot_pkt_default_padding_size(const knot_pkt_t *pkt,
+ const knot_rrset_t *opt_rr)
+{
+ if (knot_wire_get_qr(pkt->wire)) {
+ return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr),
+ KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT);
+ } else {
+ return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr),
+ KNOT_EDNS_ALIGNMENT_QUERY_DEFAULT);
+ }
+}
+
+static inline size_t knot_pkt_size(const knot_pkt_t *pkt)
+{
+ assert(pkt);
+ return pkt->size + (knot_pkt_has_tsig(pkt) ? pkt->tsig_wire.len : 0);
+}
+
+/*! @} */
diff --git a/src/libknot/packet/rrset-wire.c b/src/libknot/packet/rrset-wire.c
new file mode 100644
index 0000000..2b02969
--- /dev/null
+++ b/src/libknot/packet/rrset-wire.c
@@ -0,0 +1,727 @@
+/* Copyright (C) 2021 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 <assert.h>
+
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/descriptor.h"
+#include "libknot/packet/pkt.h"
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/rrtype/naptr.h"
+#include "libknot/rrtype/rrsig.h"
+#include "libknot/rrtype/soa.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "contrib/tolower.h"
+#include "contrib/wire_ctx.h"
+
+/*!
+ * \brief Get maximal size of a domain name in a wire with given capacity.
+ */
+static uint16_t dname_max(size_t wire_avail)
+{
+ return MIN(wire_avail, KNOT_DNAME_MAXLEN);
+}
+
+/*!
+ * \brief Compares two domain name labels.
+ *
+ * \param label1 First label.
+ * \param label2 Second label (may be in upper-case).
+ *
+ * \retval true if the labels are identical
+ * \retval false if the labels are NOT identical
+ */
+static bool label_is_equal(const uint8_t *label1, const uint8_t *label2)
+{
+ assert(label1 && label2);
+
+ if (*label1 != *label2) {
+ return false;
+ }
+
+ uint8_t len = *label1;
+ for (uint8_t i = 1; i <= len; i++) {
+ if (label1[i] != knot_tolower(label2[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*!
+ * Case insensitive comparison of two dnames in wire format.
+ * The second name may be compressed in a supplied wire.
+ */
+static bool dname_equal_wire(const knot_dname_t *d1, const knot_dname_t *d2,
+ const uint8_t *wire)
+{
+ assert(d1);
+ assert(d2);
+
+ d2 = knot_wire_seek_label(d2, wire);
+
+ while (*d1 != '\0' || *d2 != '\0') {
+ if (!label_is_equal(d1, d2)) {
+ return false;
+ }
+ d1 = knot_wire_next_label(d1, NULL);
+ d2 = knot_wire_next_label(d2, wire);
+ }
+
+ return true;
+}
+
+static uint16_t compr_get_ptr(knot_compr_t *compr, uint16_t hint)
+{
+ if (compr == NULL) {
+ return 0;
+ }
+
+ return knot_compr_hint(compr->rrinfo, hint);
+}
+
+static void compr_set_ptr(knot_compr_t *compr, uint16_t hint,
+ const uint8_t *written_at, uint16_t written_size)
+{
+ if (compr == NULL) {
+ return;
+ }
+
+ assert(written_at >= compr->wire);
+
+ uint16_t offset = written_at - compr->wire;
+
+ if (knot_wire_is_pointer(written_at)) {
+ offset = knot_wire_get_pointer(written_at);
+ }
+
+ knot_compr_hint_set(compr->rrinfo, hint, offset, written_size);
+}
+
+static int write_rdata_fixed(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail, size_t size)
+{
+ assert(src && *src);
+ assert(src_avail);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ // Check input/output buffer boundaries.
+ if (size > *src_avail) {
+ return KNOT_EMALF;
+ }
+
+ if (size > *dst_avail) {
+ return KNOT_ESPACE;
+ }
+
+ // Data binary copy.
+ memcpy(*dst, *src, size);
+
+ // Update buffers.
+ *src += size;
+ *src_avail -= size;
+
+ *dst += size;
+ *dst_avail -= size;
+
+ return KNOT_EOK;
+}
+
+static int write_rdata_naptr_header(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail)
+{
+ assert(src && *src);
+ assert(src_avail);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ int ret = knot_naptr_header_size(*src, *src + *src_avail);
+ if (ret < 0) {
+ return ret;
+ }
+
+ // Copy the data.
+ return write_rdata_fixed(src, src_avail, dst, dst_avail, ret);
+}
+
+/*! \brief Helper for \ref compr_put_dname, writes label(s) with size checks. */
+#define WRITE_LABEL(dst, written, label, max, len) \
+ if ((written) + (len) > (max)) { \
+ return KNOT_ESPACE; \
+ } else { \
+ memcpy((dst) + (written), (label), (len)); \
+ written += (len); \
+ }
+
+/*!
+ * \brief Write compressed domain name to the destination wire.
+ *
+ * \param dname Name to be written.
+ * \param dst Destination wire.
+ * \param max Maximum number of bytes available.
+ * \param compr Compression context (NULL for no compression)
+ * \return Number of written bytes or an error.
+ */
+static int compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max,
+ knot_compr_t *compr)
+{
+ assert(dname && dst);
+
+ // Write uncompressible names directly (zero label dname).
+ if (compr == NULL || *dname == '\0') {
+ return knot_dname_to_wire(dst, dname, max);
+ }
+
+ // Get number of labels (should not be a zero label dname).
+ size_t name_labels = knot_dname_labels(dname, NULL);
+ assert(name_labels > 0);
+
+ // Suffix must not be longer than whole name.
+ const knot_dname_t *suffix = compr->wire + compr->suffix.pos;
+ int suffix_labels = compr->suffix.labels;
+ while (suffix_labels > name_labels) {
+ suffix = knot_wire_next_label(suffix, compr->wire);
+ --suffix_labels;
+ }
+
+ // Suffix is shorter than name, write labels until aligned.
+ uint8_t orig_labels = name_labels;
+ uint16_t written = 0;
+ while (name_labels > suffix_labels) {
+ WRITE_LABEL(dst, written, dname, max, (*dname + 1));
+ dname = knot_wire_next_label(dname, NULL);
+ --name_labels;
+ }
+
+ // Label count is now equal.
+ assert(name_labels == suffix_labels);
+ const knot_dname_t *match_begin = dname;
+ const knot_dname_t *compr_ptr = suffix;
+ while (dname[0] != '\0') {
+ // Next labels.
+ const knot_dname_t *next_dname = knot_wire_next_label(dname, NULL);
+ const knot_dname_t *next_suffix = knot_wire_next_label(suffix, compr->wire);
+
+ // Two labels match, extend suffix length.
+ if (!label_is_equal(dname, suffix)) {
+ // If they don't match, write unmatched labels.
+ uint16_t mismatch_len = (dname - match_begin) + (*dname + 1);
+ WRITE_LABEL(dst, written, match_begin, max, mismatch_len);
+ // Start new potential match.
+ match_begin = next_dname;
+ compr_ptr = next_suffix;
+ }
+
+ // Jump to next labels.
+ dname = next_dname;
+ suffix = next_suffix;
+ }
+
+ // If match begins at the end of the name, write '\0' label.
+ if (match_begin == dname) {
+ WRITE_LABEL(dst, written, dname, max, 1);
+ } else {
+ // Match covers >0 labels, write out compression pointer.
+ if (written + sizeof(uint16_t) > max) {
+ return KNOT_ESPACE;
+ }
+ knot_wire_put_pointer(dst + written, compr_ptr - compr->wire);
+ written += sizeof(uint16_t);
+ }
+
+ assert(dst >= compr->wire);
+ size_t wire_pos = dst - compr->wire;
+ assert(wire_pos < KNOT_WIRE_MAX_PKTSIZE);
+
+ // Heuristics - expect similar names are grouped together.
+ if (written > sizeof(uint16_t) && wire_pos + written < KNOT_WIRE_PTR_MAX) {
+ compr->suffix.pos = wire_pos;
+ compr->suffix.labels = orig_labels;
+ }
+
+ return written;
+}
+
+#define WRITE_OWNER_CHECK(size, dst_avail) \
+ if ((size) > *(dst_avail)) { \
+ return KNOT_ESPACE; \
+ }
+
+#define WRITE_OWNER_INCR(dst, dst_avail, size) \
+ *(dst) += (size); \
+ *(dst_avail) -= (size);
+
+static int write_owner(const knot_rrset_t *rrset, uint8_t **dst, size_t *dst_avail,
+ knot_compr_t *compr)
+{
+ assert(rrset);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ // Check for zero label owner (don't compress).
+ uint16_t owner_pointer = 0;
+ if (*rrset->owner != '\0') {
+ owner_pointer = compr_get_ptr(compr, KNOT_COMPR_HINT_OWNER);
+ }
+
+ // Write result.
+ if (owner_pointer > 0) {
+ WRITE_OWNER_CHECK(sizeof(uint16_t), dst_avail);
+ knot_wire_put_pointer(*dst, owner_pointer);
+ WRITE_OWNER_INCR(dst, dst_avail, sizeof(uint16_t));
+ // Check for coincidence with previous RR set.
+ } else if (compr != NULL && compr->suffix.pos != 0 && *rrset->owner != '\0' &&
+ dname_equal_wire(rrset->owner, compr->wire + compr->suffix.pos,
+ compr->wire)) {
+ WRITE_OWNER_CHECK(sizeof(uint16_t), dst_avail);
+ knot_wire_put_pointer(*dst, compr->suffix.pos);
+ compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER,
+ compr->wire + compr->suffix.pos,
+ knot_dname_size(rrset->owner));
+ WRITE_OWNER_INCR(dst, dst_avail, sizeof(uint16_t));
+ } else {
+ if (compr != NULL) {
+ compr->suffix.pos = KNOT_WIRE_HEADER_SIZE;
+ compr->suffix.labels =
+ knot_dname_labels(compr->wire + compr->suffix.pos,
+ compr->wire);
+ }
+ // WRITE_OWNER_CHECK not needed, compr_put_dname has a check.
+ int written = compr_put_dname(rrset->owner, *dst,
+ dname_max(*dst_avail), compr);
+ if (written < 0) {
+ return written;
+ }
+
+ compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER, *dst, written);
+ WRITE_OWNER_INCR(dst, dst_avail, written);
+ }
+
+ return KNOT_EOK;
+}
+
+static int write_fixed_header(const knot_rrset_t *rrset, uint16_t rrset_index,
+ uint8_t **dst, size_t *dst_avail, uint16_t flags)
+{
+ assert(rrset);
+ assert(rrset_index < rrset->rrs.count);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ // Write header.
+ wire_ctx_t write = wire_ctx_init(*dst, *dst_avail);
+
+ wire_ctx_write_u16(&write, rrset->type);
+ wire_ctx_write_u16(&write, rrset->rclass);
+
+ if ((flags & KNOT_PF_ORIGTTL) && rrset->type == KNOT_RRTYPE_RRSIG) {
+ const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index);
+ wire_ctx_write_u32(&write, knot_rrsig_original_ttl(rdata));
+ } else if ((flags & KNOT_PF_SOAMINTTL) && rrset->type == KNOT_RRTYPE_SOA) {
+ const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index);
+ wire_ctx_write_u32(&write, MIN(knot_soa_minimum(rdata), rrset->ttl));
+ } else {
+ wire_ctx_write_u32(&write, rrset->ttl);
+ }
+
+ // Check write.
+ if (write.error != KNOT_EOK) {
+ return write.error;
+ }
+
+ // Update buffer.
+ *dst = write.position;
+ *dst_avail = wire_ctx_available(&write);
+
+ return KNOT_EOK;
+}
+
+static int compress_rdata_dname(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail,
+ knot_compr_t *put_compr, knot_compr_t *compr,
+ uint16_t hint)
+{
+ assert(src && *src);
+ assert(src_avail);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ // Source domain name.
+ const knot_dname_t *dname = *src;
+ size_t dname_size = knot_dname_size(dname);
+
+ // Output domain name.
+ int written = compr_put_dname(dname, *dst, dname_max(*dst_avail), put_compr);
+ if (written < 0) {
+ return written;
+ }
+
+ // Update compression hints.
+ if (compr_get_ptr(compr, hint) == 0) {
+ compr_set_ptr(compr, hint, *dst, written);
+ }
+
+ // Update buffers.
+ *dst += written;
+ *dst_avail -= written;
+
+ *src += dname_size;
+ *src_avail -= dname_size;
+
+ return KNOT_EOK;
+}
+
+static int rdata_traverse_write(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail,
+ const knot_rdata_descriptor_t *desc,
+ knot_compr_t *compr, uint16_t hint)
+{
+ for (const int *type = desc->block_types; *type != KNOT_RDATA_WF_END; type++) {
+ int ret;
+ knot_compr_t *put_compr = NULL;
+ switch (*type) {
+ case KNOT_RDATA_WF_COMPRESSIBLE_DNAME:
+ put_compr = compr;
+ // FALLTHROUGH
+ case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME:
+ case KNOT_RDATA_WF_FIXED_DNAME:
+ ret = compress_rdata_dname(src, src_avail, dst, dst_avail,
+ put_compr, compr, hint);
+ break;
+ case KNOT_RDATA_WF_NAPTR_HEADER:
+ ret = write_rdata_naptr_header(src, src_avail, dst, dst_avail);
+ break;
+ case KNOT_RDATA_WF_REMAINDER:
+ ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *src_avail);
+ break;
+ default:
+ // Fixed size block.
+ assert(*type > 0);
+ ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *type);
+ break;
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int write_rdata(const knot_rrset_t *rrset, uint16_t rrset_index,
+ uint8_t **dst, size_t *dst_avail, knot_compr_t *compr)
+{
+ assert(rrset);
+ assert(rrset_index < rrset->rrs.count);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index);
+
+ // Reserve space for RDLENGTH.
+ if (sizeof(uint16_t) > *dst_avail) {
+ return KNOT_ESPACE;
+ }
+
+ uint8_t *wire_rdlength = *dst;
+ *dst += sizeof(uint16_t);
+ *dst_avail -= sizeof(uint16_t);
+ uint8_t *wire_rdata_begin = *dst;
+
+ // Write RDATA.
+ const uint8_t *src = rdata->data;
+ size_t src_avail = rdata->len;
+ if (src_avail > 0) {
+ // Only write non-empty data.
+ const knot_rdata_descriptor_t *desc =
+ knot_get_rdata_descriptor(rrset->type);
+ int ret = rdata_traverse_write(&src, &src_avail, dst, dst_avail,
+ desc, compr, KNOT_COMPR_HINT_RDATA + rrset_index);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Check for trailing data in the message.
+ if (src_avail > 0) {
+ return KNOT_EMALF;
+ }
+
+ // Write final RDLENGTH.
+ size_t rdlength = *dst - wire_rdata_begin;
+ knot_wire_write_u16(wire_rdlength, rdlength);
+
+ return KNOT_EOK;
+}
+
+static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index, uint8_t **dst,
+ size_t *dst_avail, knot_compr_t *compr, uint16_t flags)
+{
+ int ret = write_owner(rrset, dst, dst_avail, compr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = write_fixed_header(rrset, rrset_index, dst, dst_avail, flags);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return write_rdata(rrset, rrset_index, dst, dst_avail, compr);
+}
+
+_public_
+int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
+ uint16_t max_size, uint16_t rotate,
+ knot_compr_t *compr, uint16_t flags)
+{
+ if (rrset == NULL || wire == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (rrset->rrs.count == 0) {
+ return 0;
+ }
+ if (rotate != 0) {
+ rotate %= rrset->rrs.count;
+ }
+
+ uint8_t *write = wire;
+ size_t capacity = max_size;
+
+ uint16_t count = rrset->rrs.count;
+ for (int i = rotate; i < count + rotate; i++) {
+ uint16_t pos = (i < count) ? i : (i - count);
+ int ret = write_rr(rrset, pos, &write, &capacity, compr, flags);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return write - wire;
+}
+
+static int parse_header(const uint8_t *wire, size_t *pos, size_t pkt_size,
+ knot_mm_t *mm, knot_rrset_t *rrset, uint16_t *rdlen)
+{
+ assert(wire);
+ assert(pos);
+ assert(rrset);
+ assert(rdlen);
+
+ wire_ctx_t src = wire_ctx_init_const(wire, pkt_size);
+ wire_ctx_set_offset(&src, *pos);
+
+ int compr_size = knot_dname_wire_check(src.position, wire + pkt_size, wire);
+ if (compr_size <= 0) {
+ return KNOT_EMALF;
+ }
+
+ knot_dname_storage_t buff;
+ int decompr_size = knot_dname_unpack(buff, src.position, sizeof(buff), wire);
+ if (decompr_size <= 0) {
+ return KNOT_EMALF;
+ }
+
+ knot_dname_t *owner = mm_alloc(mm, decompr_size);
+ if (owner == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(owner, buff, decompr_size);
+ wire_ctx_skip(&src, compr_size);
+
+ uint16_t type = wire_ctx_read_u16(&src);
+ uint16_t rclass = wire_ctx_read_u16(&src);
+ uint32_t ttl = wire_ctx_read_u32(&src);
+ *rdlen = wire_ctx_read_u16(&src);
+
+ if (src.error != KNOT_EOK) {
+ knot_dname_free(owner, mm);
+ return KNOT_EMALF;
+ }
+
+ if (wire_ctx_available(&src) < *rdlen) {
+ knot_dname_free(owner, mm);
+ return KNOT_EMALF;
+ }
+
+ *pos = wire_ctx_offset(&src);
+
+ knot_rrset_init(rrset, owner, type, rclass, ttl);
+
+ return KNOT_EOK;
+}
+
+static int decompress_rdata_dname(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail,
+ const uint8_t *pkt_wire)
+{
+ assert(src && *src);
+ assert(src_avail);
+ assert(dst && *dst);
+ assert(dst_avail);
+
+ int compr_size = knot_dname_wire_check(*src, *src + *src_avail, pkt_wire);
+ if (compr_size <= 0) {
+ return compr_size;
+ }
+
+ int decompr_size = knot_dname_unpack(*dst, *src, *dst_avail, pkt_wire);
+ if (decompr_size <= 0) {
+ return decompr_size;
+ }
+
+ // Update buffers.
+ *dst += decompr_size;
+ *dst_avail -= decompr_size;
+
+ *src += compr_size;
+ *src_avail -= compr_size;
+
+ return KNOT_EOK;
+}
+
+static int rdata_traverse_parse(const uint8_t **src, size_t *src_avail,
+ uint8_t **dst, size_t *dst_avail,
+ const knot_rdata_descriptor_t *desc,
+ const uint8_t *pkt_wire)
+{
+ for (const int *type = desc->block_types; *type != KNOT_RDATA_WF_END; type++) {
+ int ret;
+ switch (*type) {
+ case KNOT_RDATA_WF_COMPRESSIBLE_DNAME:
+ case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME:
+ case KNOT_RDATA_WF_FIXED_DNAME:
+ ret = decompress_rdata_dname(src, src_avail, dst, dst_avail,
+ pkt_wire);
+ break;
+ case KNOT_RDATA_WF_NAPTR_HEADER:
+ ret = write_rdata_naptr_header(src, src_avail, dst, dst_avail);
+ break;
+ case KNOT_RDATA_WF_REMAINDER:
+ ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *src_avail);
+ break;
+ default:
+ /* Fixed size block */
+ assert(*type > 0);
+ ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *type);
+ break;
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static bool allow_zero_rdata(const knot_rrset_t *rr,
+ const knot_rdata_descriptor_t *desc)
+{
+ return rr->rclass != KNOT_CLASS_IN || // NONE and ANY for DDNS
+ rr->type == KNOT_RRTYPE_APL || // APL RR type
+ desc->type_name == NULL; // Unknown RR type
+}
+
+static int parse_rdata(const uint8_t *pkt_wire, size_t *pos, size_t pkt_size,
+ knot_mm_t *mm, uint16_t rdlength, knot_rrset_t *rrset)
+{
+ assert(pkt_wire);
+ assert(pos);
+ assert(rrset);
+
+ const knot_rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type);
+ if (desc->type_name == NULL) {
+ desc = knot_get_obsolete_rdata_descriptor(rrset->type);
+ }
+
+ if (rdlength == 0) {
+ if (allow_zero_rdata(rrset, desc)) {
+ return knot_rrset_add_rdata(rrset, NULL, 0, mm);
+ } else {
+ return KNOT_EMALF;
+ }
+ } else if (pkt_size - *pos < rdlength) {
+ return KNOT_EMALF;
+ }
+
+ // Buffer for parsed rdata (decompression extends rdata length).
+ const size_t max_rdata_len = UINT16_MAX;
+ uint8_t buf[knot_rdata_size(max_rdata_len)];
+ knot_rdata_t *rdata = (knot_rdata_t *)buf;
+
+ const uint8_t *src = pkt_wire + *pos;
+ size_t src_avail = rdlength;
+ uint8_t *dst = rdata->data;
+ size_t dst_avail = max_rdata_len;
+
+ // Parse RDATA.
+ int ret = rdata_traverse_parse(&src, &src_avail, &dst, &dst_avail, desc, pkt_wire);
+ if (ret != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ // Check for trailing data.
+ size_t real_len = max_rdata_len - dst_avail;
+ if (real_len < rdlength) {
+ return KNOT_EMALF;
+ }
+ rdata->len = real_len;
+
+ ret = knot_rdataset_add(&rrset->rrs, rdata, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Update position pointer.
+ *pos += rdlength;
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_rrset_rr_from_wire(const uint8_t *wire, size_t *pos, size_t max_size,
+ knot_rrset_t *rrset, knot_mm_t *mm, bool canonical)
+{
+ if (wire == NULL || pos == NULL || *pos > max_size || rrset == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint16_t rdlen = 0;
+ int ret = parse_header(wire, pos, max_size, mm, rrset, &rdlen);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = parse_rdata(wire, pos, max_size, mm, rdlen, rrset);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(rrset, mm);
+ return ret;
+ }
+
+ // Convert RR to the canonical format.
+ if (canonical) {
+ ret = knot_rrset_rr_to_canonical(rrset);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(rrset, mm);
+ }
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/packet/rrset-wire.h b/src/libknot/packet/rrset-wire.h
new file mode 100644
index 0000000..3be0cba
--- /dev/null
+++ b/src/libknot/packet/rrset-wire.h
@@ -0,0 +1,69 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief RRSet from/to wire conversion functions.
+ *
+ * \addtogroup wire
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rrset.h"
+#include "libknot/packet/compr.h"
+
+/*!
+ * \brief Write RR Set content to a wire.
+ *
+ * \param rrset RRSet to be converted.
+ * \param wire Output wire buffer.
+ * \param max_size Capacity of wire buffer.
+ * \param rotate Rotate the RR order by this count.
+ * \param compr Compression context.
+ * \param flags Flags; currently only KNOT_PF_TTL_ORIG is accepted.
+ *
+ * \return Output size, negative number on error (KNOT_E*).
+ */
+int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
+ uint16_t max_size, uint16_t rotate,
+ knot_compr_t *compr, uint16_t flags);
+
+/*! \brief Same as knot_rrset_to_wire_extra but without rrset rotation and flags. */
+static inline int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire,
+ uint16_t max_size, knot_compr_t *compr)
+{
+ return knot_rrset_to_wire_extra(rrset, wire, max_size, 0, compr, 0);
+}
+
+/*!
+* \brief Creates one RR from wire, stores it into \a rrset.
+*
+* \param wire Source wire (the whole packet).
+* \param pos Position in \a wire where to start parsing.
+* \param max_size Total size of data in \a wire (size of the packet).
+* \param rrset Destination RRSet.
+* \param mm Memory context.
+* \param canonical Convert rrset to canonical format indication.
+*
+* \return KNOT_E*
+*/
+int knot_rrset_rr_from_wire(const uint8_t *wire, size_t *pos, size_t max_size,
+ knot_rrset_t *rrset, knot_mm_t *mm, bool canonical);
+
+/*! @} */
diff --git a/src/libknot/packet/wire.h b/src/libknot/packet/wire.h
new file mode 100644
index 0000000..698ac3d
--- /dev/null
+++ b/src/libknot/packet/wire.h
@@ -0,0 +1,1045 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Functions for manipulating and parsing raw data in DNS packets.
+ *
+ * \addtogroup wire
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "libknot/attribute.h"
+#include "libknot/wire.h"
+
+/*! \brief Offset of DNS header fields in wireformat. */
+enum knot_wire_offsets {
+ KNOT_WIRE_OFFSET_ID = 0,
+ KNOT_WIRE_OFFSET_FLAGS1 = 2,
+ KNOT_WIRE_OFFSET_FLAGS2 = 3,
+ KNOT_WIRE_OFFSET_QDCOUNT = 4,
+ KNOT_WIRE_OFFSET_ANCOUNT = 6,
+ KNOT_WIRE_OFFSET_NSCOUNT = 8,
+ KNOT_WIRE_OFFSET_ARCOUNT = 10
+};
+
+/*! \brief Minimum size for some parts of the DNS packet. */
+enum knot_wire_sizes {
+ KNOT_WIRE_HEADER_SIZE = 12,
+ KNOT_WIRE_QUESTION_MIN_SIZE = 5,
+ KNOT_WIRE_RR_MIN_SIZE = 11,
+ KNOT_WIRE_MIN_PKTSIZE = 512,
+ KNOT_WIRE_MAX_PKTSIZE = 65535,
+ KNOT_WIRE_MAX_PAYLOAD = KNOT_WIRE_MAX_PKTSIZE
+ - KNOT_WIRE_HEADER_SIZE
+ - KNOT_WIRE_QUESTION_MIN_SIZE
+};
+
+/*
+ * Packet header manipulation functions.
+ */
+
+/*!
+ * \brief Returns the ID from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return DNS packet ID.
+ */
+static inline uint16_t knot_wire_get_id(const uint8_t *packet)
+{
+ assert(packet);
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ID);
+}
+
+/*!
+ * \brief Sets the ID to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param id DNS packet ID.
+ */
+static inline void knot_wire_set_id(uint8_t *packet, uint16_t id)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ID, id);
+}
+
+/*!
+ * \brief Returns the first byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return First byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags1(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1);
+}
+
+/*!
+ * \brief Sets the first byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags1 First byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags1(uint8_t *packet, uint8_t flags1)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) = flags1;
+}
+
+/*!
+ * \brief Returns the second byte of flags from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Second byte of DNS flags.
+ */
+static inline uint8_t knot_wire_get_flags2(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2);
+}
+
+/*!
+ * \brief Sets the second byte of flags to the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param flags2 Second byte of the DNS flags.
+ */
+static inline uint8_t knot_wire_set_flags2(uint8_t *packet, uint8_t flags2)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) = flags2;
+}
+
+/*!
+ * \brief Returns the QDCOUNT (count of Question entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return QDCOUNT (count of Question entries in the packet).
+ */
+static inline uint16_t knot_wire_get_qdcount(const uint8_t *packet)
+{
+ assert(packet);
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT);
+}
+
+/*!
+ * \brief Sets the QDCOUNT (count of Question entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param qdcount QDCOUNT (count of Question entries in the packet).
+ */
+static inline void knot_wire_set_qdcount(uint8_t *packet, uint16_t qdcount)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, qdcount);
+}
+
+/*!
+ * \brief Adds to QDCOUNT.
+ */
+static inline void knot_wire_add_qdcount(uint8_t *packet, int16_t n)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT,
+ knot_wire_get_qdcount(packet) + n);
+}
+
+/*!
+ * \brief Returns the ANCOUNT (count of Answer entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ANCOUNT (count of Answer entries in the packet).
+ */
+static inline uint16_t knot_wire_get_ancount(const uint8_t *packet)
+{
+ assert(packet);
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT);
+}
+
+/*!
+ * \brief Sets the ANCOUNT (count of Answer entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param ancount ANCOUNT (count of Answer entries in the packet).
+ */
+static inline void knot_wire_set_ancount(uint8_t *packet, uint16_t ancount)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, ancount);
+}
+
+/*!
+ * \brief Adds to ANCOUNT.
+ */
+static inline void knot_wire_add_ancount(uint8_t *packet, int16_t n)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT,
+ knot_wire_get_ancount(packet) + n);
+}
+
+/*!
+ * \brief Returns the NSCOUNT (count of Authority entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return NSCOUNT (count of Authority entries in the packet).
+ */
+static inline uint16_t knot_wire_get_nscount(const uint8_t *packet)
+{
+ assert(packet);
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT);
+}
+
+/*!
+ * \brief Sets the NSCOUNT (count of Authority entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param nscount NSCOUNT (count of Authority entries in the packet).
+ */
+static inline void knot_wire_set_nscount(uint8_t *packet, uint16_t nscount)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, nscount);
+}
+
+/*!
+ * \brief Adds to NSCOUNT.
+ */
+static inline void knot_wire_add_nscount(uint8_t *packet, int16_t n)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT,
+ knot_wire_get_nscount(packet) + n);
+}
+
+/*!
+ * \brief Returns the ARCOUNT (count of Additional entries) from wire format of
+ * the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return ARCOUNT (count of Additional entries in the packet).
+ */
+static inline uint16_t knot_wire_get_arcount(const uint8_t *packet)
+{
+ assert(packet);
+ return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT);
+}
+
+/*!
+ * \brief Sets the ARCOUNT (count of Additional entries) to wire format of the
+ * packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param arcount ARCOUNT (count of Additional entries in the packet).
+ */
+static inline void knot_wire_set_arcount(uint8_t *packet, uint16_t arcount)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, arcount);
+}
+
+/*!
+ * \brief Adds to ARCOUNT.
+ */
+static inline void knot_wire_add_arcount(uint8_t *packet, int16_t n)
+{
+ assert(packet);
+ knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT,
+ knot_wire_get_arcount(packet) + n);
+}
+
+/*
+ * Packet header flags manipulation functions.
+ */
+/*! \brief Constants for DNS header flags in the first flags byte. */
+enum knot_wire_flags1_consts {
+ KNOT_WIRE_RD_MASK = (uint8_t)0x01U, /*!< RD bit mask. */
+ KNOT_WIRE_RD_SHIFT = 0, /*!< RD bit shift. */
+ KNOT_WIRE_TC_MASK = (uint8_t)0x02U, /*!< TC bit mask. */
+ KNOT_WIRE_TC_SHIFT = 1, /*!< TC bit shift. */
+ KNOT_WIRE_AA_MASK = (uint8_t)0x04U, /*!< AA bit mask. */
+ KNOT_WIRE_AA_SHIFT = 2, /*!< AA bit shift. */
+ KNOT_WIRE_OPCODE_MASK = (uint8_t)0x78U, /*!< OPCODE mask. */
+ KNOT_WIRE_OPCODE_SHIFT = 3, /*!< OPCODE shift. */
+ KNOT_WIRE_QR_MASK = (uint8_t)0x80U, /*!< QR bit mask. */
+ KNOT_WIRE_QR_SHIFT = 7 /*!< QR bit shift. */
+};
+
+/*! \brief Constants for DNS header flags in the second flags byte. */
+enum knot_wire_flags2_consts {
+ KNOT_WIRE_RCODE_MASK = (uint8_t)0x0fU, /*!< RCODE mask. */
+ KNOT_WIRE_RCODE_SHIFT = 0, /*!< RCODE shift. */
+ KNOT_WIRE_CD_MASK = (uint8_t)0x10U, /*!< CD bit mask. */
+ KNOT_WIRE_CD_SHIFT = 4, /*!< CD bit shift. */
+ KNOT_WIRE_AD_MASK = (uint8_t)0x20U, /*!< AD bit mask. */
+ KNOT_WIRE_AD_SHIFT = 5, /*!< AD bit shift. */
+ KNOT_WIRE_Z_MASK = (uint8_t)0x40U, /*!< Zero bit mask. */
+ KNOT_WIRE_Z_SHIFT = 6, /*!< Zero bit shift. */
+ KNOT_WIRE_RA_MASK = (uint8_t)0x80U, /*!< RA bit mask. */
+ KNOT_WIRE_RA_SHIFT = 7 /*!< RA bit shift. */
+};
+
+/*
+ * Functions for getting / setting / clearing flags and codes directly in packet
+ */
+
+/*!
+ * \brief Returns the RD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_rd(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_rd(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_rd(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the TC bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_tc(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_tc(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_tc(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_aa(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_aa(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_aa(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return OPCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_opcode(const uint8_t *packet)
+{
+ assert(packet);
+ return (*(packet + KNOT_WIRE_OFFSET_FLAGS1)
+ & KNOT_WIRE_OPCODE_MASK) >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_set_opcode(uint8_t *packet, short opcode)
+{
+ assert(packet);
+ uint8_t *flags1 = packet + KNOT_WIRE_OFFSET_FLAGS1;
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Nonzero for responses and zero for queries.
+ */
+static inline uint8_t knot_wire_get_qr(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_qr(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_qr(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return RCODE of the packet.
+ */
+static inline uint8_t knot_wire_get_rcode(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2)
+ & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_set_rcode(uint8_t *packet, short rcode)
+{
+ assert(packet);
+ uint8_t *flags2 = packet + KNOT_WIRE_OFFSET_FLAGS2;
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the CD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_cd(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_cd(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_cd(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the AD bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ad(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ad(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ad(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the Zero bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_z(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_z(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_z(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ *
+ * \return Flags with only the RA bit according to its setting in the packet.
+ */
+static inline uint8_t knot_wire_get_ra(const uint8_t *packet)
+{
+ assert(packet);
+ return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_set_ra(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the wire format of the packet.
+ *
+ * \param packet Wire format of the packet.
+ */
+static inline void knot_wire_clear_ra(uint8_t *packet)
+{
+ assert(packet);
+ *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Functions for getting / setting / clearing flags in flags variable
+ */
+
+/*!
+ * \brief Returns the RD bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the RD bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_rd(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Sets the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_rd(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 |= KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Clears the RD bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_rd(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 &= ~KNOT_WIRE_RD_MASK;
+}
+
+/*!
+ * \brief Returns the TC bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the TC bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_tc(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Sets the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_tc(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 |= KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Clears the TC bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_tc(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 &= ~KNOT_WIRE_TC_MASK;
+}
+
+/*!
+ * \brief Returns the AA bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the AA bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_aa(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Sets the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_aa(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 |= KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Clears the AA bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_aa(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 &= ~KNOT_WIRE_AA_MASK;
+}
+
+/*!
+ * \brief Returns the OPCODE from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return OPCODE
+ */
+static inline uint8_t knot_wire_flags_get_opcode(uint8_t flags1)
+{
+ return (flags1 & KNOT_WIRE_OPCODE_MASK)
+ >> KNOT_WIRE_OPCODE_SHIFT;
+}
+
+/*!
+ * \brief Sets the OPCODE in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ * \param opcode OPCODE to set.
+ */
+static inline void knot_wire_flags_set_opcode(uint8_t *flags1, short opcode)
+{
+ assert(flags1);
+ *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK)
+ | ((opcode) << KNOT_WIRE_OPCODE_SHIFT);
+}
+
+/*!
+ * \brief Returns the QR bit from the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ *
+ * \return Flags byte with only the QR bit according to its setting in
+ * \a flags1.
+ */
+static inline uint8_t knot_wire_flags_get_qr(uint8_t flags1)
+{
+ return flags1 & KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Sets the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_qr(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 |= KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Clears the QR bit in the first byte of flags.
+ *
+ * \param flags1 First byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_qr(uint8_t *flags1)
+{
+ assert(flags1);
+ *flags1 &= ~KNOT_WIRE_QR_MASK;
+}
+
+/*!
+ * \brief Returns the RCODE from the second byte of flags.
+ *
+ * \param flags2 First byte of DNS header flags.
+ *
+ * \return RCODE
+ */
+static inline uint8_t knot_wire_flags_get_rcode(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RCODE_MASK;
+}
+
+/*!
+ * \brief Sets the RCODE in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ * \param rcode RCODE to set.
+ */
+static inline void knot_wire_flags_set_rcode(uint8_t *flags2, short rcode)
+{
+ assert(flags2);
+ *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode);
+}
+
+/*!
+ * \brief Returns the CD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the CD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_cd(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Sets the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_cd(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 |= KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Clears the CD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_cd(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 &= ~KNOT_WIRE_CD_MASK;
+}
+
+/*!
+ * \brief Returns the AD bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the AD bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ad(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Sets the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ad(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 |= KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Clears the AD bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ad(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 &= ~KNOT_WIRE_AD_MASK;
+}
+
+/*!
+ * \brief Returns the Zero bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the Zero bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_z(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Sets the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_z(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 |= KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Clears the Zero bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_z(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 &= ~KNOT_WIRE_Z_MASK;
+}
+
+/*!
+ * \brief Returns the RA bit from the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ *
+ * \return Flags byte with only the RA bit according to its setting in
+ * \a flags2.
+ */
+static inline uint8_t knot_wire_flags_get_ra(uint8_t flags2)
+{
+ return flags2 & KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Sets the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_set_ra(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 |= KNOT_WIRE_RA_MASK;
+}
+
+/*!
+ * \brief Clears the RA bit in the second byte of flags.
+ *
+ * \param flags2 Second byte of DNS header flags.
+ */
+static inline void knot_wire_flags_clear_ra(uint8_t *flags2)
+{
+ assert(flags2);
+ *flags2 &= ~KNOT_WIRE_RA_MASK;
+}
+
+/*
+ * Pointer manipulation
+ */
+
+enum knot_wire_pointer_consts {
+ /*! \brief DNS packet pointer designation (first two bits set to 1). */
+ KNOT_WIRE_PTR = (uint8_t)0xC0,
+ /*! \brief DNS packet minimal pointer (KNOT_WIRE_PTR + 1 zero byte). */
+ KNOT_WIRE_PTR_BASE = (uint16_t)0xC000,
+ /*! \brief DNS packet maximal offset (KNOT_WIRE_BASE complement). */
+ KNOT_WIRE_PTR_MAX = (uint16_t)0x3FFF
+};
+
+static inline int knot_wire_is_pointer(const uint8_t *pos)
+{
+ return pos && ((pos[0] & KNOT_WIRE_PTR) == KNOT_WIRE_PTR);
+}
+
+/*!
+ * \brief Creates a DNS packet pointer and stores it in wire format.
+ *
+ * \param pos Position where tu put the pointer.
+ * \param ptr Relative position of the item to which the pointer should point in
+ * the wire format of the packet.
+ */
+static inline void knot_wire_put_pointer(uint8_t *pos, uint16_t ptr)
+{
+ knot_wire_write_u16(pos, ptr); // Write pointer offset.
+ assert((pos[0] & KNOT_WIRE_PTR) == 0); // Check for maximal offset.
+ pos[0] |= KNOT_WIRE_PTR; // Add pointer mark.
+}
+
+static inline uint16_t knot_wire_get_pointer(const uint8_t *pos)
+{
+ assert(knot_wire_is_pointer(pos)); // Check pointer.
+ return (knot_wire_read_u16(pos) - KNOT_WIRE_PTR_BASE); // Return offset.
+}
+
+_pure_ _mustcheck_
+static inline const uint8_t *knot_wire_seek_label(const uint8_t *lp, const uint8_t *wire)
+{
+ while (knot_wire_is_pointer(lp)) {
+ if (!wire)
+ return NULL;
+ lp = wire + knot_wire_get_pointer(lp);
+ }
+ return lp;
+}
+
+_pure_ _mustcheck_
+static inline const uint8_t *knot_wire_next_label(const uint8_t *lp, const uint8_t *wire)
+{
+ if (!lp || !lp[0]) /* No label after final label. */
+ return NULL;
+ return knot_wire_seek_label(lp + (lp[0] + sizeof(uint8_t)), wire);
+}
+
+/*! @} */
diff --git a/src/libknot/probe/data.c b/src/libknot/probe/data.c
new file mode 100644
index 0000000..5d831b2
--- /dev/null
+++ b/src/libknot/probe/data.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2023 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 <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "libknot/attribute.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "libknot/probe/probe.h"
+#include "contrib/macros.h"
+
+_public_
+int knot_probe_data_set(knot_probe_data_t *data, knot_probe_proto_t proto,
+ const struct sockaddr_storage *local_addr,
+ const struct sockaddr_storage *remote_addr,
+ const knot_pkt_t *query, const knot_pkt_t *reply,
+ uint16_t rcode)
+{
+ if (data == NULL || remote_addr == NULL || query == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ data->proto = proto;
+
+ if (remote_addr->ss_family == AF_INET) {
+ const struct sockaddr_in *sa = (struct sockaddr_in *)remote_addr;
+ const struct sockaddr_in *da = (struct sockaddr_in *)local_addr;
+
+ memcpy(data->remote.addr, &sa->sin_addr, sizeof(sa->sin_addr));
+ memset(data->remote.addr + sizeof(sa->sin_addr), 0,
+ sizeof(data->remote.addr) - sizeof(sa->sin_addr));
+ data->remote.port = be16toh(sa->sin_port);
+
+ if (da != NULL) {
+ memcpy(data->local.addr, &da->sin_addr, sizeof(da->sin_addr));
+ memset(data->local.addr + sizeof(da->sin_addr), 0,
+ sizeof(data->local.addr) - sizeof(da->sin_addr));
+ data->local.port = be16toh(da->sin_port);
+ } else {
+ memset(&data->local, 0, sizeof(data->local));
+ }
+
+ data->ip = 4;
+ } else if (remote_addr->ss_family == AF_INET6) {
+ const struct sockaddr_in6 *sa = (struct sockaddr_in6 *)remote_addr;
+ const struct sockaddr_in6 *da = (struct sockaddr_in6 *)local_addr;
+
+ memcpy(data->remote.addr, &sa->sin6_addr, sizeof(sa->sin6_addr));
+ data->remote.port = be16toh(sa->sin6_port);
+
+ if (da != NULL) {
+ memcpy(data->local.addr, &da->sin6_addr, sizeof(da->sin6_addr));
+ data->local.port = be16toh(da->sin6_port);
+ } else {
+ memset(&data->local, 0, sizeof(data->local));
+ }
+
+ data->ip = 6;
+ } else {
+ memset(&data->remote, 0, sizeof(data->remote));
+ memset(&data->local, 0, sizeof(data->local));
+
+ data->ip = 0;
+ }
+
+ if (reply != NULL) {
+ memcpy(&data->reply.hdr, reply->wire, sizeof(data->reply.hdr));
+ data->reply.size = knot_pkt_size(reply);
+ data->reply.rcode = rcode;
+ } else {
+ memset(&data->reply, 0, sizeof(data->reply));
+ }
+ data->reply.ede = KNOT_PROBE_DATA_EDE_NONE;
+
+ data->tcp_rtt = 0;
+
+ if (query->opt_rr != NULL) {
+ data->query_edns.options = 0;
+ data->query_edns.payload = knot_edns_get_payload(query->opt_rr);
+ data->query_edns.version = knot_edns_get_version(query->opt_rr);
+ data->query_edns.present = 1;
+ data->query_edns.flag_do = knot_edns_do(query->opt_rr);
+ if (query->edns_opts != NULL) {
+ for (int i = 0; i <= KNOT_EDNS_MAX_OPTION_CODE; i++) {
+ if (query->edns_opts->ptr[i] != NULL) {
+ data->query_edns.options |= (1 << i);
+ }
+ }
+ }
+ data->query_edns.reserved = 0;
+ } else {
+ memset(&data->query_edns, 0, sizeof(data->query_edns));
+ }
+
+ memcpy(&data->query.hdr, query->wire, sizeof(data->query.hdr));
+ data->query.size = knot_pkt_size(query);
+ data->query.qclass = knot_pkt_qclass(query);
+ data->query.qtype = knot_pkt_qtype(query);
+ data->query.qname_len = knot_dname_size(knot_pkt_qname(query));
+ memcpy(data->query.qname, knot_pkt_qname(query), data->query.qname_len);
+ memset(data->query.qname + data->query.qname_len, 0,
+ MIN(8, sizeof(data->query.qname) - data->query.qname_len));
+
+ return KNOT_EOK;
+}
+
+_public_
+uint32_t knot_probe_tcp_rtt(int sockfd)
+{
+#if defined(__linux__)
+ struct tcp_info info = { 0 };
+ socklen_t info_length = sizeof(info);
+ if (getsockopt(sockfd, SOL_TCP, TCP_INFO, &info, &info_length) == 0) {
+ return info.tcpi_rtt;
+ }
+#endif
+
+ return 0;
+}
diff --git a/src/libknot/probe/data.h b/src/libknot/probe/data.h
new file mode 100644
index 0000000..efd5135
--- /dev/null
+++ b/src/libknot/probe/data.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief A DNS traffic probe data structure.
+ *
+ * \addtogroup probe
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/consts.h"
+#include "libknot/packet/pkt.h"
+
+/*! EDE absence indication. */
+#define KNOT_PROBE_DATA_EDE_NONE 0xFFFF
+
+/*! Data transport protocol types. */
+typedef enum {
+ KNOT_PROBE_PROTO_UDP = 0,
+ KNOT_PROBE_PROTO_TCP,
+ KNOT_PROBE_PROTO_QUIC,
+ KNOT_PROBE_PROTO_TLS,
+ KNOT_PROBE_PROTO_HTTPS,
+} knot_probe_proto_t;
+
+/*! DNS message header in wire format (network byte order!). */
+typedef struct {
+ uint16_t id;
+ uint8_t byte3; /*!< QR, OPCODE, AA, TC, RD. */
+ uint8_t byte4; /*!< RA, Z, AD, CD, RCODE. */
+ uint16_t questions;
+ uint16_t answers;
+ uint16_t authorities;
+ uint16_t additionals;
+} knot_probe_data_wire_hdr_t;
+
+/*! Probe data unit. */
+typedef struct {
+ uint8_t ip; /*!< IP protocol: 4 or 6. */
+ uint8_t proto; /*!< Transport protocol \ref knot_probe_proto_t. */
+
+ struct {
+ uint8_t addr[16]; /*!< Query destination address. */
+ uint16_t port; /*!< Query destination port. */
+ } local;
+
+ struct {
+ uint8_t addr[16]; /*!< Query source address. */
+ uint16_t port; /*!< Query source port. */
+ } remote;
+
+ struct {
+ knot_probe_data_wire_hdr_t hdr; /*!< DNS reply header. */
+ uint16_t size; /*!< DNS reply size (0 if no reply). */
+ uint16_t rcode; /*!< Final RCODE (header + EDNS + TSIG). */
+ uint16_t ede; /*!< EDE code if present. */
+ } reply;
+
+ uint32_t tcp_rtt; /*!< Average TCP RTT in microseconds. */
+
+ struct {
+ uint32_t options; /*!< EDNS options bit map (e.g. NSID ~ 1 << 3). */
+ uint16_t payload; /*!< EDNS payload size. */
+ uint8_t version; /*!< EDNS version. */
+ uint8_t present : 1; /*!< EDNS presence indication. */
+ uint8_t flag_do : 1; /*!< DO flag indication. */
+ uint8_t reserved : 6; /*!< Unused. */
+ } query_edns;
+
+ struct {
+ knot_probe_data_wire_hdr_t hdr; /*!< DNS query header. */
+ uint16_t size; /*!< DNS query size. */
+ uint16_t qclass; /*!< QCLASS. */
+ uint16_t qtype; /*!< QTYPE. */
+ uint8_t qname_len; /*!< QNAME length. */
+ uint8_t qname[KNOT_DNAME_MAXLEN]; /*!< QNAME. */
+ } query;
+} knot_probe_data_t;
+
+/*!
+ * \brief Initializes a probe data unit.
+ *
+ * \note 'reply.ede' and 'tcp.rtt' are zeroed only and require further setting.
+ *
+ * \param data Output probe data unit.
+ * \param proto Transport protocol \ref knot_probe_proto_t.
+ * \param local_addr Query destination address (optional).
+ * \param remote_addr Query source address.
+ * \param query Query packet.
+ * \param reply Reply packet (optional).
+ * \param rcode Extended rcode (combination of RCODE, EDNS, TSIG).
+ *
+ * \retval KNOT_EOK Success.
+ * \return KNOT_E* If error.
+ */
+int knot_probe_data_set(knot_probe_data_t *data, knot_probe_proto_t proto,
+ const struct sockaddr_storage *local_addr,
+ const struct sockaddr_storage *remote_addr,
+ const knot_pkt_t *query, const knot_pkt_t *reply,
+ uint16_t rcode);
+
+/*!
+ * \brief Gets averate TCP RRT for a given socket descriptor.
+ *
+ * \note Implemented on Linux only!
+ *
+ * \param sockfd Socket descriptor of a TCP connection.
+ *
+ * \return Average TCP RTT in microseconds.
+ */
+uint32_t knot_probe_tcp_rtt(int sockfd);
+
+/*! @} */
diff --git a/src/libknot/probe/probe.c b/src/libknot/probe/probe.c
new file mode 100644
index 0000000..341c48b
--- /dev/null
+++ b/src/libknot/probe/probe.c
@@ -0,0 +1,228 @@
+/* Copyright (C) 2023 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 <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/probe/probe.h"
+#include "contrib/time.h"
+
+struct knot_probe {
+ struct sockaddr_un path;
+ uint32_t last_unconn_time;
+ bool consumer;
+ int fd;
+};
+
+_public_
+knot_probe_t *knot_probe_alloc(void)
+{
+ knot_probe_t *probe = calloc(1, sizeof(*probe));
+ if (probe == NULL) {
+ return NULL;
+ }
+
+ probe->fd = -1;
+
+ return probe;
+}
+
+_public_
+void knot_probe_free(knot_probe_t *probe)
+{
+ if (probe == NULL) {
+ return;
+ }
+
+ close(probe->fd);
+ if (probe->consumer) {
+ (void)unlink(probe->path.sun_path);
+ }
+ free(probe);
+}
+
+static int probe_connect(knot_probe_t *probe)
+{
+ return connect(probe->fd, (const struct sockaddr *)(&probe->path),
+ sizeof(probe->path));
+}
+
+static int probe_init(knot_probe_t *probe, const char *dir, uint16_t idx)
+{
+ if (probe == NULL || dir == NULL || idx == 0) {
+ return KNOT_EINVAL;
+ }
+
+ probe->path.sun_family = AF_UNIX;
+ int ret = snprintf(probe->path.sun_path, sizeof(probe->path.sun_path),
+ "%s/probe%02u.sock", dir, idx);
+ if (ret < 0 || ret >= sizeof(probe->path.sun_path)) {
+ return KNOT_ERANGE;
+ }
+
+ probe->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (probe->fd < 0) {
+ return knot_map_errno();
+ }
+
+ if (fcntl(probe->fd, F_SETFL, O_NONBLOCK) == -1) {
+ close(probe->fd);
+ probe->fd = -1;
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_probe_set_producer(knot_probe_t *probe, const char *dir, uint16_t idx)
+{
+ int ret = probe_init(probe, dir, idx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = probe_connect(probe);
+ if (ret != 0) {
+ return KNOT_ECONN;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_probe_set_consumer(knot_probe_t *probe, const char *dir, uint16_t idx)
+{
+ int ret = probe_init(probe, dir, idx);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ probe->consumer = true;
+
+ (void)unlink(probe->path.sun_path);
+
+ ret = bind(probe->fd, (const struct sockaddr *)(&probe->path),
+ sizeof(probe->path));
+ if (ret != 0) {
+ return knot_map_errno();
+ }
+
+ if (chmod(probe->path.sun_path, S_IWUSR | S_IWGRP | S_IWOTH) != 0) {
+ close(probe->fd);
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_probe_fd(knot_probe_t *probe)
+{
+ if (probe == NULL) {
+ return -1;
+ }
+
+ return probe->fd;
+}
+
+_public_
+int knot_probe_produce(knot_probe_t *probe, const knot_probe_data_t *data, uint8_t count)
+{
+ if (probe == NULL || data == NULL || count != 1) {
+ return KNOT_EINVAL;
+ }
+
+ size_t used_len = sizeof(*data) - KNOT_DNAME_MAXLEN + data->query.qname_len;
+ if (send(probe->fd, data, used_len, 0) == -1) {
+ struct timespec now = time_now();
+ if (now.tv_sec - probe->last_unconn_time > 2) {
+ probe->last_unconn_time = now.tv_sec;
+ if ((errno == ENOTCONN || errno == ECONNREFUSED) &&
+ probe_connect(probe) == 0 &&
+ send(probe->fd, data, used_len, 0) > 0) {
+ return KNOT_EOK;
+ }
+ }
+ return knot_map_errno();
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_probe_consume(knot_probe_t *probe, knot_probe_data_t *data, uint8_t count,
+ int timeout_ms)
+{
+ if (probe == NULL || data == NULL || count == 0) {
+ return KNOT_EINVAL;
+ }
+
+#ifdef ENABLE_RECVMMSG
+ struct mmsghdr msgs[count];
+ struct iovec iovecs[count];
+
+ memset(msgs, 0, sizeof(msgs));
+ for (int i = 0; i < count; i++) {
+ iovecs[i].iov_base = &(data[i]);
+ iovecs[i].iov_len = sizeof(*data);
+ msgs[i].msg_hdr.msg_iov = &iovecs[i];
+ msgs[i].msg_hdr.msg_iovlen = 1;
+ }
+#else
+ struct iovec iov = {
+ .iov_base = data,
+ .iov_len = sizeof(*data)
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1
+ };
+#endif
+
+ struct pollfd pfd = { .fd = probe->fd, .events = POLLIN };
+ int ret = poll(&pfd, 1, timeout_ms);
+ if (ret == -1) {
+ return knot_map_errno();
+ } else if ((pfd.revents & POLLIN) == 0) {
+ return 0;
+ }
+
+#ifdef ENABLE_RECVMMSG
+ ret = recvmmsg(probe->fd, msgs, count, 0, NULL);
+#else
+ ret = recvmsg(probe->fd, &msg, 0);
+#endif
+ if (ret == -1) {
+ return knot_map_errno();
+ }
+
+#ifdef ENABLE_RECVMMSG
+ return ret;
+#else
+ return (ret > 0 ? 1 : 0);
+#endif
+}
diff --git a/src/libknot/probe/probe.h b/src/libknot/probe/probe.h
new file mode 100644
index 0000000..24c8067
--- /dev/null
+++ b/src/libknot/probe/probe.h
@@ -0,0 +1,116 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief A DNS traffic probe interface.
+ *
+ * \addtogroup probe
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/probe/data.h"
+
+/*! A probe context. */
+struct knot_probe;
+typedef struct knot_probe knot_probe_t;
+
+/*!
+ * Allocates a probe context.
+ *
+ * \return Probe context.
+ */
+knot_probe_t *knot_probe_alloc(void);
+
+/*!
+ * \brief Deallocates a probe.
+ *
+ * \param probe Probe context.
+ */
+void knot_probe_free(knot_probe_t *probe);
+
+/*!
+ * \brief Initializes one probe producer.
+ *
+ * \param probe Probe context.
+ * \param dir Unix socket directory.
+ * \param idx Probe ID (counted from 1).
+ *
+ * \retval KNOT_EOK Success.
+ * \retval KNOT_ECONN Initial connection failed.
+ * \return KNOT_E* If error.
+ */
+int knot_probe_set_producer(knot_probe_t *probe, const char *dir, uint16_t idx);
+
+/*!
+ * \brief Initializes one probe consumer.
+ *
+ * \note The socket permissions are set to 0222!
+ *
+ * \param probe Probe context.
+ * \param dir Unix socket directory.
+ * \param idx Probe ID (counted from 1).
+ *
+ * \retval KNOT_EOK Success.
+ * \return KNOT_E* If error.
+ */
+int knot_probe_set_consumer(knot_probe_t *probe, const char *dir, uint16_t idx);
+
+/*!
+ * \brief Returns file descriptor of the probe.
+ *
+ * \param probe Probe context.
+ */
+int knot_probe_fd(knot_probe_t *probe);
+
+/*!
+ * \brief Sends data units to a probe.
+ *
+ * \note Data arrays of length > 1 are not supported yet.
+ *
+ * If send fails due to unconnected socket anf if not connected for at least
+ * 2 seconds, reconnection is attempted and if successful, the send operation
+ * is repeated.
+ *
+ * \param probe Probe context.
+ * \param data Array of data units.
+ * \param count Length of data unit array.
+ *
+ * \retval KNOT_EOK Success.
+ * \return KNOT_E* If error.
+ */
+int knot_probe_produce(knot_probe_t *probe, const knot_probe_data_t *data, uint8_t count);
+
+/*!
+ * \brief Receives data units from a probe.
+ *
+ * This function blocks on poll until a data unit is received or timeout is hit.
+ *
+ * \param probe Probe context.
+ * \param data Array of data units.
+ * \param count Length of data unit array.
+ * \param timeout_ms Poll timeout in milliseconds (-1 means infinity).
+ *
+ * \retval >= 0 Number of data units received.
+ * \return KNOT_E* If error.
+ */
+int knot_probe_consume(knot_probe_t *probe, knot_probe_data_t *data, uint8_t count,
+ int timeout_ms);
+
+/*! @} */
diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h
new file mode 100644
index 0000000..1891f91
--- /dev/null
+++ b/src/libknot/rdata.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief API for manipulating rdata.
+ *
+ * \addtogroup rr
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+/*!< \brief Maximum rdata length. */
+#define KNOT_RDATA_MAXLEN 65535
+
+/*!
+ * \brief Structure holding single rdata payload.
+ */
+typedef struct {
+ uint16_t len;
+ uint8_t data[];
+} knot_rdata_t;
+
+/*!
+ * \brief Inits rdata structure.
+ *
+ * \param rdata Rdata structure to be initialized. At least knot_rdata_size bytes
+ * must fit into it!
+ * \param len Rdata length.
+ * \param data Rdata itself.
+ */
+inline static void knot_rdata_init(knot_rdata_t *rdata, uint16_t len, const uint8_t *data)
+{
+ assert(rdata);
+ rdata->len = len;
+ if (rdata->len > 0) {
+ assert(data);
+ memcpy(rdata->data, data, len);
+ if (len & 1) { // Initialize possible padding to mute analytical tools.
+ rdata->data[len] = 0;
+ }
+ }
+}
+
+/*!
+ * \brief Returns actual size of the rdata structure for given rdata length.
+ *
+ * \param len Rdata length.
+ *
+ * \return Actual structure size.
+ */
+inline static size_t knot_rdata_size(uint16_t len)
+{
+ return sizeof(uint16_t) + len + (len & 1);
+}
+
+/*!
+ * \brief Canonical comparison of two rdata structures.
+ *
+ * \param rdata1 First rdata to compare.
+ * \param rdata2 Second rdata to compare.
+ *
+ * \retval = 0 if rdata1 == rdata2.
+ * \retval < 0 if rdata1 < rdata2.
+ * \retval > 0 if rdata1 > rdata2.
+ */
+inline static int knot_rdata_cmp(const knot_rdata_t *rdata1, const knot_rdata_t *rdata2)
+{
+ assert(rdata1);
+ assert(rdata2);
+
+ size_t common_len = (rdata1->len <= rdata2->len) ? rdata1->len : rdata2->len;
+
+ int cmp = memcmp(rdata1->data, rdata2->data, common_len);
+ if (cmp == 0 && rdata1->len != rdata2->len) {
+ cmp = rdata1->len < rdata2->len ? -1 : 1;
+ }
+ return cmp;
+}
+
+/*! @} */
diff --git a/src/libknot/rdataset.c b/src/libknot/rdataset.c
new file mode 100644
index 0000000..03e0784
--- /dev/null
+++ b/src/libknot/rdataset.c
@@ -0,0 +1,371 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libknot/attribute.h"
+#include "libknot/rdataset.h"
+#include "contrib/mempattern.h"
+
+static knot_rdata_t *rr_seek(const knot_rdataset_t *rrs, uint16_t pos)
+{
+ assert(rrs);
+ assert(0 < rrs->count);
+ assert(pos < rrs->count);
+
+ uint8_t *raw = (uint8_t *)(rrs->rdata);
+ for (uint16_t i = 0; i < pos; ++i) {
+ raw += knot_rdata_size(((knot_rdata_t *)raw)->len);
+ }
+
+ return (knot_rdata_t *)raw;
+}
+
+static int find_rr_pos(const knot_rdataset_t *rrs, const knot_rdata_t *rr)
+{
+ knot_rdata_t *search_rr = rrs->rdata;
+ for (uint16_t i = 0; i < rrs->count; ++i) {
+ if (knot_rdata_cmp(rr, search_rr) == 0) {
+ return i;
+ }
+ search_rr = knot_rdataset_next(search_rr);
+ }
+
+ return KNOT_ENOENT;
+}
+
+static int add_rr_at(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_rdata_t *ins_pos,
+ knot_mm_t *mm)
+{
+ assert(rrs);
+ assert(rr);
+ const size_t ins_offset = (uint8_t *)ins_pos - (uint8_t *)rrs->rdata;
+ assert(ins_offset <= rrs->size);
+
+ if (rrs->count == UINT16_MAX) {
+ return KNOT_ESPACE;
+ } else if (rrs->size > UINT32_MAX - knot_rdata_size(UINT16_MAX)) {
+ return KNOT_ESPACE;
+ }
+
+ const size_t rr_size = knot_rdata_size(rr->len);
+
+ // Realloc RDATA.
+ knot_rdata_t *tmp = mm_realloc(mm, rrs->rdata, rrs->size + rr_size,
+ rrs->size);
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ } else {
+ rrs->rdata = tmp;
+ }
+
+ uint8_t *ins_pos_raw = (uint8_t *)rrs->rdata + ins_offset;
+ // RDATA may have to be rearanged. Moving zero-length region is OK.
+ memmove(ins_pos_raw + rr_size, ins_pos_raw, rrs->size - ins_offset);
+
+ // Set new RDATA.
+ knot_rdata_init((knot_rdata_t *)ins_pos_raw, rr->len, rr->data);
+ rrs->count++;
+ rrs->size += rr_size;
+
+ return KNOT_EOK;
+}
+
+static int remove_rr_at(knot_rdataset_t *rrs, uint16_t pos, knot_mm_t *mm)
+{
+ assert(rrs);
+ assert(0 < rrs->count);
+ assert(pos < rrs->count);
+
+ knot_rdata_t *old_rr = rr_seek(rrs, pos);
+ knot_rdata_t *last_rr = rr_seek(rrs, rrs->count - 1);
+
+ size_t old_size = knot_rdata_size(old_rr->len);
+
+ // Move RDATA.
+ uint8_t *old_threshold = (uint8_t *)old_rr + old_size;
+ uint8_t *last_threshold = (uint8_t *)last_rr + knot_rdata_size(last_rr->len);
+ assert(old_threshold <= last_threshold);
+ memmove(old_rr, old_threshold, last_threshold - old_threshold);
+
+ if (rrs->count > 1) {
+ // Realloc RDATA.
+ knot_rdata_t *tmp = mm_realloc(mm, rrs->rdata, rrs->size - old_size,
+ rrs->size);
+ if (tmp == NULL) {
+ return KNOT_ENOMEM;
+ } else {
+ rrs->rdata = tmp;
+ }
+ } else {
+ // Free RDATA.
+ mm_free(mm, rrs->rdata);
+ rrs->rdata = NULL;
+ }
+ rrs->count--;
+ rrs->size -= old_size;
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_rdataset_clear(knot_rdataset_t *rrs, knot_mm_t *mm)
+{
+ if (rrs == NULL) {
+ return;
+ }
+
+ mm_free(mm, rrs->rdata);
+ knot_rdataset_init(rrs);
+}
+
+_public_
+int knot_rdataset_copy(knot_rdataset_t *dst, const knot_rdataset_t *src, knot_mm_t *mm)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ dst->count = src->count;
+ dst->size = src->size;
+
+ if (src->count > 0) {
+ assert(src->rdata != NULL);
+ dst->rdata = mm_alloc(mm, src->size);
+ if (dst->rdata == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(dst->rdata, src->rdata, src->size);
+ } else {
+ assert(src->size == 0);
+ dst->rdata = NULL;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+knot_rdata_t *knot_rdataset_at(const knot_rdataset_t *rrs, uint16_t pos)
+{
+ if (rrs == NULL || rrs->count == 0 || pos >= rrs->count) {
+ return NULL;
+ }
+
+ return rr_seek(rrs, pos);
+}
+
+_public_
+int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm)
+{
+ if (rrs == NULL || rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Optimize a little for insertion at the end, for larger RRsets.
+ if (rrs->count > 4) {
+ knot_rdata_t *last = rr_seek(rrs, rrs->count - 1);
+ if (knot_rdata_cmp(last, rr) < 0) {
+ return add_rr_at(rrs, rr, knot_rdataset_next(last), mm);
+ }
+ }
+
+ // Look for the right place to insert.
+ knot_rdata_t *ins_pos = rrs->rdata;
+ for (int i = 0; i < rrs->count; ++i, ins_pos = knot_rdataset_next(ins_pos)) {
+ int cmp = knot_rdata_cmp(ins_pos, rr);
+ if (cmp == 0) {
+ // Duplicate - no need to add this RR.
+ return KNOT_EOK;
+ } else if (cmp > 0) {
+ // Found position to insert.
+ return add_rr_at(rrs, rr, ins_pos, mm);
+ }
+ }
+
+ assert(rrs->rdata == NULL || (uint8_t *)rrs->rdata + rrs->size == (uint8_t *)ins_pos);
+
+ // If flow gets here, it means that we should insert at the current position (append).
+ return add_rr_at(rrs, rr, ins_pos, mm);
+}
+
+_public_
+bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2)
+{
+ if (rrs1 == NULL || rrs2 == NULL || rrs1->count != rrs2->count) {
+ return false;
+ }
+
+ knot_rdata_t *rr1 = rrs1->rdata;
+ knot_rdata_t *rr2 = rrs2->rdata;
+ for (uint16_t i = 0; i < rrs1->count; ++i) {
+ if (knot_rdata_cmp(rr1, rr2) != 0) {
+ return false;
+ }
+ rr1 = knot_rdataset_next(rr1);
+ rr2 = knot_rdataset_next(rr2);
+ }
+
+ return true;
+}
+
+_public_
+bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr)
+{
+ if (rrs == NULL) {
+ return false;
+ }
+
+ knot_rdata_t *cmp_rr = rrs->rdata;
+ for (uint16_t i = 0; i < rrs->count; ++i) {
+ int cmp = knot_rdata_cmp(cmp_rr, rr);
+ if (cmp == 0) {
+ // Match.
+ return true;
+ } else if (cmp > 0) {
+ // 'Greater' RR present, no need to continue.
+ return false;
+ }
+ cmp_rr = knot_rdataset_next(cmp_rr);
+ }
+
+ return false;
+}
+
+_public_
+bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of)
+{
+ if (subset == NULL || (of != NULL && subset->rdata == of->rdata)) {
+ return true;
+ }
+
+ knot_rdata_t *rd = subset->rdata;
+ for (uint16_t i = 0; i < subset->count; ++i) {
+ if (!knot_rdataset_member(of, rd)) {
+ return false;
+ }
+ rd = knot_rdataset_next(rd);
+ }
+
+ return true;
+}
+
+_public_
+int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2,
+ knot_mm_t *mm)
+{
+ if (rrs1 == NULL || rrs2 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rr2 = rrs2->rdata;
+ for (uint16_t i = 0; i < rrs2->count; ++i) {
+ int ret = knot_rdataset_add(rrs1, rr2, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ rr2 = knot_rdataset_next(rr2);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_rdataset_intersect(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2,
+ knot_rdataset_t *out, knot_mm_t *mm)
+{
+ if (rrs1 == NULL || rrs2 == NULL || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdataset_init(out);
+ knot_rdata_t *rr1 = rrs1->rdata;
+ for (uint16_t i = 0; i < rrs1->count; ++i) {
+ if (knot_rdataset_member(rrs2, rr1)) {
+ // Add RR into output intersection RRSet.
+ int ret = knot_rdataset_add(out, rr1, mm);
+ if (ret != KNOT_EOK) {
+ knot_rdataset_clear(out, mm);
+ return ret;
+ }
+ }
+ rr1 = knot_rdataset_next(rr1);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_rdataset_intersect2(knot_rdataset_t *from, const knot_rdataset_t *what,
+ knot_mm_t *mm)
+{
+ if (from == NULL || what == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (from->rdata == what->rdata) {
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *rr1 = from->rdata;
+ for (uint16_t i = 0; i < from->count; ) {
+ if (!knot_rdataset_member(what, rr1)) {
+ int ret = remove_rr_at(from, i, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (i < from->count) {
+ // Just to make sure rr1 remains valid if re-allocated.
+ rr1 = rr_seek(from, i);
+ }
+ } else {
+ i++;
+ rr1 = knot_rdataset_next(rr1);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_rdataset_subtract(knot_rdataset_t *from, const knot_rdataset_t *what,
+ knot_mm_t *mm)
+{
+ if (from == NULL || what == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (from->rdata == what->rdata) {
+ knot_rdataset_clear(from, mm);
+ knot_rdataset_init((knot_rdataset_t *) what);
+ return KNOT_EOK;
+ }
+
+ knot_rdata_t *to_remove = what->rdata;
+ for (uint16_t i = 0; i < what->count; ++i) {
+ int pos_to_remove = find_rr_pos(from, to_remove);
+ if (pos_to_remove >= 0) {
+ int ret = remove_rr_at(from, pos_to_remove, mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ to_remove = knot_rdataset_next(to_remove);
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/rdataset.h b/src/libknot/rdataset.h
new file mode 100644
index 0000000..6d98086
--- /dev/null
+++ b/src/libknot/rdataset.h
@@ -0,0 +1,218 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief API for manipulating RR arrays.
+ *
+ * \addtogroup rr
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/errcode.h"
+#include "libknot/mm_ctx.h"
+#include "libknot/rdata.h"
+
+/*!< \brief Set of RRs. */
+typedef struct {
+ uint16_t count; /*!< \brief Count of RRs stored in the structure. */
+ uint32_t size; /*!< \brief Size of the rdata array. */
+ knot_rdata_t *rdata; /*!< \brief Serialized rdata, canonically sorted. */
+} knot_rdataset_t;
+
+/*!
+ * \brief Initializes RRS structure.
+ *
+ * \param rrs Structure to be initialized.
+ */
+inline static void knot_rdataset_init(knot_rdataset_t *rrs)
+{
+ if (rrs != NULL) {
+ rrs->count = 0;
+ rrs->size = 0;
+ rrs->rdata = NULL;
+ }
+}
+
+/*!
+ * \brief Advance to the next rdata in a rdataset.
+ *
+ * Useful for iteration.
+ *
+ * \note Ensure that this operation makes sense!
+ *
+ * \param rr Current RR.
+ *
+ * \return Next RR.
+ */
+static inline knot_rdata_t *knot_rdataset_next(knot_rdata_t *rr)
+{
+ assert(rr);
+ return (knot_rdata_t *)((uint8_t *)rr + knot_rdata_size(rr->len));
+}
+
+/*!
+ * \brief Frees data initialized by RRS structure, but not the structure itself.
+ *
+ * \param rrs Structure to be cleared.
+ * \param mm Memory context used to create allocations.
+ */
+void knot_rdataset_clear(knot_rdataset_t *rrs, knot_mm_t *mm);
+
+/*!
+ * \brief Deep copies RRS structure. All data are duplicated.
+ *
+ * \param dst Copy destination.
+ * \param src Copy source.
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_copy(knot_rdataset_t *dst, const knot_rdataset_t *src, knot_mm_t *mm);
+
+/*!
+ * \brief Gets RR from RRS structure, using given position.
+ *
+ * \param rrs RRS structure to get RR from.
+ * \param pos Position to use (counted from 0).
+ *
+ * \return Pointer to RR at \a pos position.
+ */
+knot_rdata_t *knot_rdataset_at(const knot_rdataset_t *rrs, uint16_t pos);
+
+/*!
+ * \brief Adds single RR into RRS structure. All data are copied.
+ *
+ * \param rrs RRS structure to add RR into.
+ * \param rr RR to add.
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm);
+
+/*!
+ * \brief RRS equality check.
+ *
+ * \param rrs1 First RRS to be compared.
+ * \param rrs2 Second RRS to be compared.
+ *
+ * \retval true if rrs1 == rrs2.
+ * \retval false if rrs1 != rrs2.
+ */
+bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2);
+
+/*!
+ * \brief Returns true if \a rr is present in \a rrs, false otherwise.
+ *
+ * \param rrs RRS to search in.
+ * \param rr RR to compare with.
+ *
+ * \retval true if \a rr is present in \a rrs.
+ * \retval false if \a rr is not present in \a rrs.
+ */
+bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr);
+
+/*!
+ * \brief Returns true if \a subset is a sub-set of \a of, false otherwise.
+ *
+ * \param subset RRS to check.
+ * \param of RRS to search in.
+ *
+ * \retval true if \a subset is a sub-set of \a of.
+ * \retval false if \a subset is not a sub-set of \a of.
+ */
+bool knot_rdataset_subset(const knot_rdataset_t *subset, const knot_rdataset_t *of);
+
+/*!
+ * \brief Merges two RRS into the first one. Second RRS is left intact.
+ * Canonical order is preserved.
+ *
+ * \param rrs1 Destination RRS (merge here).
+ * \param rrs2 RRS to be merged (merge from).
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2,
+ knot_mm_t *mm);
+
+/*!
+ * \brief RRS set-like intersection. Full compare is done.
+ *
+ * \param rrs1 First RRS to intersect.
+ * \param rrs2 Second RRS to intersect.
+ * \param out Output RRS with intersection, RDATA are created anew.
+ * \param mm Memory context. Will be used to create new RDATA.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_intersect(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2,
+ knot_rdataset_t *out, knot_mm_t *mm);
+
+/*!
+ * \brief Does set-like RRS intersection. \a from RRS is changed.
+ *
+ * \param from RRS to be modified by intersection.
+ * \param what RRS to intersect.
+ * \param mm Memory context use to reallocated \a from data.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_intersect2(knot_rdataset_t *from, const knot_rdataset_t *what,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Does set-like RRS subtraction. \a from RRS is changed.
+ *
+ * \param from RRS to subtract from.
+ * \param what RRS to subtract.
+ * \param mm Memory context use to reallocated \a from data.
+ *
+ * \return KNOT_E*
+ */
+int knot_rdataset_subtract(knot_rdataset_t *from, const knot_rdataset_t *what,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Removes single RR from RRS structure.
+ *
+ * \param rrs RRS structure to remove RR from.
+ * \param rr RR to remove.
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+inline static int knot_rdataset_remove(knot_rdataset_t *rrs, const knot_rdata_t *rr,
+ knot_mm_t *mm)
+{
+ if (rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdataset_t rrs_rm = { 1, knot_rdata_size(rr->len), (knot_rdata_t *)rr };
+ return knot_rdataset_subtract(rrs, &rrs_rm, mm);
+}
+
+/*! @} */
diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c
new file mode 100644
index 0000000..dd814de
--- /dev/null
+++ b/src/libknot/rrset-dump.c
@@ -0,0 +1,2292 @@
+/* Copyright (C) 2023 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 <arpa/inet.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/key.h"
+#include "libdnssec/keytag.h"
+#include "libknot/attribute.h"
+#include "libknot/rrset-dump.h"
+#include "libknot/codes.h"
+#include "libknot/consts.h"
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+#include "libknot/lookup.h"
+#include "libknot/rrtype/rrsig.h"
+#include "libknot/wire.h"
+#include "contrib/base32hex.h"
+#include "contrib/base64.h"
+#include "contrib/color.h"
+#include "contrib/ctype.h"
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/time.h"
+#include "contrib/wire_ctx.h"
+
+#define RRSET_DUMP_LIMIT (2 * 1024 * 1024)
+
+#define TAB_WIDTH 8
+#define BLOCK_WIDTH 40
+#define BLOCK_INDENT "\n\t\t\t\t"
+
+#define LOC_ZERO 2147483648 // 2^31
+
+/*! \brief macros with repetitive (mostly error-checking) code of methods from first section of this file */
+#define CHECK_PRET if (p->ret < 0) return;
+#define CHECK_INMAX(mininmax) if (p->in_max < (mininmax)) { p->ret = -1; return; }
+#define CHECK_RET_OUTMAX_SNPRINTF if (ret <= 0 || (size_t)ret >= p->out_max) { p->ret = -1; return; }
+#define STRING_TERMINATION if (p->out_max > 0) { *p->out = '\0'; } else { p->ret = -1; return; }
+#define FILL_IN_INPUT(pdata) if (memcpy(&(pdata), p->in, in_len) == NULL) { p->ret = -1; return; }
+#define CHECK_RET_POSITIVE if (ret <= 0) { p->ret = -1; return; }
+
+#define SNPRINTF_CHECK(ret, max_len) \
+ if ((ret) < 0 || (size_t)(ret) >= (max_len)) { \
+ return KNOT_ESPACE; \
+ }
+
+typedef struct {
+ const knot_dump_style_t *style;
+ const uint8_t *in;
+ size_t in_max;
+ char *out;
+ size_t out_max;
+ size_t total;
+ int ret;
+} rrset_dump_params_t;
+
+_public_
+const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT = {
+ .wrap = false,
+ .show_class = false,
+ .show_ttl = true,
+ .verbose = false,
+ .original_ttl = true,
+ .empty_ttl = false,
+ .human_ttl = false,
+ .human_timestamp = true,
+ .hide_crypto = false,
+ .ascii_to_idn = NULL,
+ .color = NULL,
+ .now = 0,
+};
+
+static void dump_string(rrset_dump_params_t *p, const char *str)
+{
+ CHECK_PRET
+
+ size_t in_len = strlen(str);
+
+ // Check input size (+ 1 termination).
+ if (in_len >= p->out_max) {
+ p->ret = -1;
+ return;
+ }
+
+ // Copy string including termination '\0'!
+ if (memcpy(p->out, str, in_len + 1) == NULL) {
+ p->ret = -1;
+ return;
+ }
+
+ // Fill in output.
+ p->out += in_len;
+ p->out_max -= in_len;
+ p->total += in_len;
+}
+
+static void wire_num8_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint8_t data = *(p->in);
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ // Write number.
+ int ret = snprintf(p->out, p->out_max, "%u", data);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_num16_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint16_t data;
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ // Fill in input data.
+ data = knot_wire_read_u16(p->in);
+
+ // Write number.
+ int ret = snprintf(p->out, p->out_max, "%u", data);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_num32_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint32_t data;
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ // Fill in input data.
+ data = knot_wire_read_u32(p->in);
+
+ // Write number.
+ int ret = snprintf(p->out, p->out_max, "%u", data);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_num48_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint64_t data;
+ size_t in_len = 6;
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ // Fill in input data.
+ data = knot_wire_read_u48(p->in);
+
+ // Write number.
+ int ret = snprintf(p->out, p->out_max, "%"PRIu64"", data);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_ipv4_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ struct in_addr addr4;
+ size_t in_len = sizeof(addr4.s_addr);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ FILL_IN_INPUT(addr4.s_addr)
+
+ // Write address.
+ if (knot_inet_ntop(AF_INET, &addr4, p->out, p->out_max) == NULL) {
+ p->ret = -1;
+ return;
+ }
+ out_len = strlen(p->out);
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_ipv6_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ struct in6_addr addr6;
+ size_t in_len = sizeof(addr6.s6_addr);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ FILL_IN_INPUT(addr6.s6_addr)
+
+ // Write address.
+ if (knot_inet_ntop(AF_INET6, &addr6, p->out, p->out_max) == NULL) {
+ p->ret = -1;
+ return;
+ }
+ out_len = strlen(p->out);
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_type_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ char type[32];
+ uint16_t data;
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ FILL_IN_INPUT(data)
+
+ // Get record type name string.
+ int ret = knot_rrtype_to_string(ntohs(data), type, sizeof(type));
+ CHECK_RET_POSITIVE
+
+ // Write string.
+ ret = snprintf(p->out, p->out_max, "%s", type);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static int hex_encode(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t *out,
+ const uint32_t out_len)
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+ if (out_len < 2 * in_len) {
+ return -1;
+ }
+
+ for (uint32_t i = 0; i < in_len; i++) {
+ out[2 * i] = hex[in[i] / 16];
+ out[2 * i + 1] = hex[in[i] % 16];
+ }
+
+ return 2 * in_len;
+}
+
+static int hex_encode_alloc(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t **out)
+{
+ uint32_t out_len = 2 * in_len;
+
+ // Allocating output buffer.
+ *out = malloc(out_len);
+
+ if (*out == NULL) {
+ return -1;
+ }
+
+ // Encoding data.
+ return hex_encode(in, in_len, *out, out_len);
+}
+
+static int num48_encode(const uint8_t *in,
+ const uint32_t in_len,
+ uint8_t *out,
+ const uint32_t out_len)
+{
+ if (in_len != 6) {
+ return -1;
+ }
+
+ uint64_t data = knot_wire_read_u48(in);
+
+ int ret = snprintf((char *)out, out_len, "%"PRIu64"", data);
+ if (ret <= 0 || (size_t)ret >= out_len) {
+ return -1;
+ }
+
+ return ret;
+}
+
+typedef int (*encode_t)(const uint8_t *in, const uint32_t in_len,
+ uint8_t *out, const uint32_t out_len);
+
+typedef int (*encode_alloc_t)(const uint8_t *in, const uint32_t in_len,
+ uint8_t **out);
+
+static void wire_data_encode_to_str(rrset_dump_params_t *p,
+ encode_t enc, encode_alloc_t enc_alloc)
+{
+ CHECK_PRET
+
+ int ret;
+ size_t in_len = p->in_max;
+
+ // One-line vs multi-line mode.
+ if (p->style->wrap == false) {
+ // Encode data directly to the output.
+ ret = enc(p->in, in_len, (uint8_t *)(p->out), p->out_max);
+ CHECK_RET_POSITIVE
+ size_t out_len = ret;
+
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+ } else {
+ int src_begin;
+ uint8_t *buf;
+
+ // Encode data to the temporary buffer.
+ ret = enc_alloc(p->in, in_len, &buf);
+ CHECK_RET_POSITIVE
+
+ // Loop which wraps base64 block in more lines.
+ for (src_begin = 0; src_begin < ret; src_begin += BLOCK_WIDTH) {
+ if (src_begin > 0) {
+ // Write indent block.
+ dump_string(p, BLOCK_INDENT);
+ if (p->ret < 0) {
+ free(buf);
+ return;
+ }
+ }
+
+ // Compute block length (the last one can be shorter).
+ int src_len = (ret - src_begin) < BLOCK_WIDTH ?
+ (ret - src_begin) : BLOCK_WIDTH;
+
+ if ((size_t)src_len > p->out_max) {
+ free(buf);
+ p->ret = -1;
+ return;
+ }
+
+ // Write data block.
+ memcpy(p->out, buf + src_begin, src_len);
+
+ p->out += src_len;
+ p->out_max -= src_len;
+ p->total += src_len;
+ }
+
+ // Destroy temporary buffer.
+ free(buf);
+ }
+
+ STRING_TERMINATION
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+}
+
+static void wire_len_data_encode_to_str(rrset_dump_params_t *p,
+ encode_t enc,
+ const size_t len_len,
+ const bool print_len,
+ const char *empty_str)
+{
+ CHECK_PRET
+
+ size_t in_len;
+
+ // First len_len bytes are data length.
+ CHECK_INMAX(len_len)
+
+ // Read data length.
+ switch (len_len) {
+ case 1:
+ in_len = *(p->in);
+ break;
+ case 2:
+ in_len = knot_wire_read_u16(p->in);
+ break;
+ case 4:
+ in_len = knot_wire_read_u32(p->in);
+ break;
+ default:
+ p->ret = -1;
+ return;
+ }
+
+ // If required print data length.
+ if (print_len == true) {
+ switch (len_len) {
+ case 1:
+ wire_num8_to_str(p);
+ break;
+ case 2:
+ wire_num16_to_str(p);
+ break;
+ case 4:
+ wire_num32_to_str(p);
+ break;
+ }
+
+ CHECK_PRET
+
+ // If something follows, print one space character.
+ if (in_len > 0 || *empty_str != '\0') {
+ dump_string(p, " ");
+ CHECK_PRET
+ }
+ } else {
+ p->in += len_len;
+ p->in_max -= len_len;
+ }
+
+ if (in_len > 0) {
+ // Encode data directly to the output.
+ int ret = enc(p->in, in_len, (uint8_t *)(p->out), p->out_max);
+ CHECK_RET_POSITIVE
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+
+ STRING_TERMINATION
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ } else if (*empty_str != '\0') {
+ dump_string(p, empty_str);
+ CHECK_PRET
+ }
+}
+
+static void wire_data_omit(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ const char *omit_message = "[omitted]";
+ const size_t omlen = strlen(omit_message);
+
+ if (p->out_max < omlen) {
+ p->ret = -1;
+ return;
+ }
+
+ memcpy(p->out, omit_message, omlen);
+ p->out += omlen;
+ p->out_max -= omlen;
+ p->total += omlen;
+
+ STRING_TERMINATION
+
+ p->in += p->in_max;
+ p->in_max = 0;
+}
+
+static void wire_dnskey_to_tag(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ int key_pos = -4; // we expect that key flags, 3 and algorithm
+ // have been already dumped
+
+ uint16_t key_tag = 0;
+ const dnssec_binary_t rdata_bin = {
+ .data = (uint8_t *)(p->in + key_pos),
+ .size = p->in_max - key_pos
+ };
+ dnssec_keytag(&rdata_bin, &key_tag);
+
+ int ret = snprintf(p->out, p->out_max, "[id = %hu]", key_tag);
+ CHECK_RET_OUTMAX_SNPRINTF
+
+ p->in += p->in_max;
+ p->in_max = 0;
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+}
+
+static void wire_unknown_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ int ret;
+ size_t in_len = p->in_max;
+ size_t out_len = 0;
+
+ // Write unknown length header.
+ if (in_len > 0) {
+ ret = snprintf(p->out, p->out_max, "\\# %zu ", in_len);
+ } else {
+ ret = snprintf(p->out, p->out_max, "\\# 0");
+ }
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+
+ // Fill in output.
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+
+ // Write hex data if any.
+ if (in_len > 0) {
+ // If wrap mode wrap line.
+ if (p->style->wrap) {
+ dump_string(p, BLOCK_INDENT);
+ CHECK_PRET
+ }
+
+ wire_data_encode_to_str(p, &hex_encode, &hex_encode_alloc);
+ CHECK_PRET
+ }
+}
+
+static void wire_text_to_str(rrset_dump_params_t *p, bool quote,
+ unsigned with_header_len, bool alpn_mode)
+{
+ CHECK_PRET
+
+ size_t in_len = 0;
+
+ CHECK_INMAX(with_header_len)
+ switch (with_header_len) {
+ case 0:
+ in_len = p->in_max;
+ break;
+ case 1:
+ in_len = *(p->in);
+ break;
+ case 2:
+ in_len = knot_wire_read_u16(p->in);
+ break;
+ default:
+ assert(0);
+ }
+ p->in += with_header_len;
+ p->in_max -= with_header_len;
+ CHECK_INMAX(in_len)
+
+ // Check if quotation can ever be disabled (parser protection fallback).
+ if (!quote) {
+ for (size_t i = 0; i < in_len; i++) {
+ if (p->in[i] == ' ') { // Other WS characters are encoded.
+ quote = true;
+ break;
+ }
+ }
+ }
+
+ // Opening quotation.
+ if (quote) {
+ dump_string(p, "\"");
+ CHECK_PRET
+ }
+
+ // Loop over all characters.
+ for (size_t i = 0; i < in_len; i++) {
+ uint8_t ch = p->in[i];
+
+ if (is_print(ch)) {
+ // For special character print leading slash.
+ if (ch == '\\' || ch == '"') {
+ dump_string(p, "\\");
+ CHECK_PRET
+ }
+ if (alpn_mode && (ch == ',' || ch == '\\')) {
+ dump_string(p, "\\\\");
+ CHECK_PRET
+ }
+
+ // Print text character.
+ if (p->out_max == 0) {
+ p->ret = -1;
+ return;
+ }
+
+ *p->out = ch;
+ p->out++;
+ p->out_max--;
+ p->total++;
+ } else {
+ // Unprintable character encode via \ddd notation.
+ int ret = snprintf(p->out, p->out_max,"\\%03u", ch);
+ CHECK_RET_OUTMAX_SNPRINTF
+
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+ }
+ }
+
+ // Closing quotation.
+ if (quote) {
+ dump_string(p, "\"");
+ CHECK_PRET
+ }
+
+ STRING_TERMINATION
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+}
+
+static void wire_timestamp_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint32_t data;
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+ int ret;
+
+ CHECK_INMAX(in_len)
+
+ FILL_IN_INPUT(data)
+
+ time_t timestamp = ntohl(data);
+ if (sizeof(time_t) > 4) {
+ timestamp = knot_time_from_u32(timestamp, p->style->now);
+ }
+
+ if (p->style->human_timestamp) {
+ struct tm result;
+ // Write timestamp in YYYYMMDDhhmmss format.
+ ret = strftime(p->out, p->out_max, "%Y%m%d%H%M%S",
+ gmtime_r(&timestamp, &result));
+ CHECK_RET_POSITIVE
+ } else {
+ // Write timestamp only.
+ ret = snprintf(p->out, p->out_max, "%u", ntohl(data));
+ CHECK_RET_OUTMAX_SNPRINTF
+ }
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static uint32_t wire_time_to_val(rrset_dump_params_t *p)
+{
+ uint32_t data;
+ size_t in_len = sizeof(data);
+
+ if (p->ret < 0 || p->in_max < in_len ||
+ memcpy(&data, p->in, in_len) == NULL) {
+ p->ret = -1;
+ return 0;
+ }
+
+ return ntohl(data);
+}
+
+static void wire_ttl_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint32_t data;
+ size_t in_len = sizeof(data);
+ size_t out_len = 0;
+ int ret;
+
+ CHECK_INMAX(in_len)
+
+ FILL_IN_INPUT(data)
+
+ if (p->style->human_ttl) {
+ // Write time in human readable format.
+ ret = knot_time_print_human(ntohl(data), p->out, p->out_max, true);
+ CHECK_RET_POSITIVE
+ } else {
+ // Write timestamp only.
+ ret = snprintf(p->out, p->out_max, "%u", ntohl(data));
+ CHECK_RET_OUTMAX_SNPRINTF
+ }
+ out_len = ret;
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_bitmap_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ int ret;
+ char type[32];
+ size_t i = 0;
+ size_t in_len = p->in_max;
+ size_t out_len = 0;
+
+ // Loop over bitmap window array (can be empty).
+ while (i < in_len) {
+ // First byte is window number.
+ uint8_t win = p->in[i++];
+
+ // Check window length (length must follow).
+ if (i >= in_len) {
+ p->ret = -1;
+ return;
+ }
+
+ // Second byte is window length.
+ uint8_t bitmap_len = p->in[i++];
+
+ // Check window length (len bytes must follow).
+ if (i + bitmap_len > in_len) {
+ p->ret = -1;
+ return;
+ }
+
+ // Bitmap processing.
+ for (size_t j = 0; j < (bitmap_len * 8); j++) {
+ if ((p->in[i + j / 8] & (128 >> (j % 8))) != 0) {
+ uint16_t type_num = win * 256 + j;
+
+ ret = knot_rrtype_to_string(type_num, type, sizeof(type));
+ CHECK_RET_POSITIVE
+
+ // Print type name to type list.
+ if (out_len > 0) {
+ ret = snprintf(p->out, p->out_max,
+ " %s", type);
+ } else {
+ ret = snprintf(p->out, p->out_max,
+ "%s", type);
+ }
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len += ret;
+ p->out += ret;
+ p->out_max -= ret;
+ }
+ }
+
+ i += bitmap_len;
+ }
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->total += out_len;
+}
+
+static void wire_dname_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ size_t in_len = knot_dname_size(p->in);
+ size_t out_len = 0;
+
+ CHECK_INMAX(in_len)
+
+ // Write dname string.
+ if (p->style->ascii_to_idn == NULL) {
+ char *dname_str = knot_dname_to_str(p->out, p->in, p->out_max);
+ if (dname_str == NULL) {
+ p->ret = -1;
+ return;
+ }
+ out_len = strlen(dname_str);
+ } else {
+ char *dname_str = knot_dname_to_str_alloc(p->in);
+ p->style->ascii_to_idn(&dname_str);
+
+ int ret = snprintf(p->out, p->out_max, "%s", dname_str);
+ free(dname_str);
+ CHECK_RET_OUTMAX_SNPRINTF
+ out_len = ret;
+ }
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+}
+
+static void wire_apl_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ int ret;
+ size_t out_len = 0;
+
+ // Input check: family(2B) + prefix(1B) + afdlen(1B).
+ CHECK_INMAX(4)
+
+ // Read fixed size values.
+ uint16_t family = knot_wire_read_u16(p->in);
+ uint8_t prefix = *(p->in + 2);
+ uint8_t negation = *(p->in + 3) >> 7;
+ uint8_t afdlen = *(p->in + 3) & 0x7F;
+ p->in += 4;
+ p->in_max -= 4;
+
+ // Write negation mark.
+ if (negation != 0) {
+ dump_string(p, "!");
+ CHECK_PRET
+ }
+
+ // Write address family with colon.
+ ret = snprintf(p->out, p->out_max, "%u:", family);
+ CHECK_RET_OUTMAX_SNPRINTF
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+
+ // Write address.
+ switch (family) {
+ case 1:
+ memset(&addr4, 0, sizeof(addr4));
+
+ if (afdlen > sizeof(addr4.s_addr) || afdlen > p->in_max) {
+ p->ret = -1;
+ return;
+ }
+
+ if (memcpy(&(addr4.s_addr), p->in, afdlen) == NULL) {
+ p->ret = -1;
+ return;
+ }
+
+ // Write address.
+ if (knot_inet_ntop(AF_INET, &addr4, p->out, p->out_max) == NULL) {
+ p->ret = -1;
+ return;
+ }
+ out_len = strlen(p->out);
+
+ break;
+ case 2:
+ memset(&addr6, 0, sizeof(addr6));
+
+ if (afdlen > sizeof(addr6.s6_addr) || afdlen > p->in_max) {
+ p->ret = -1;
+ return;
+ }
+
+ if (memcpy(&(addr6.s6_addr), p->in, afdlen) == NULL) {
+ p->ret = -1;
+ return;
+ }
+
+ // Write address.
+ if (knot_inet_ntop(AF_INET6, &addr6, p->out, p->out_max) == NULL) {
+ p->ret = -1;
+ return;
+ }
+ out_len = strlen(p->out);
+
+ break;
+ default:
+ p->ret = -1;
+ return;
+ }
+ p->in += afdlen;
+ p->in_max -= afdlen;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+
+ // Write prefix length with forward slash.
+ ret = snprintf(p->out, p->out_max, "/%u", prefix);
+ CHECK_RET_OUTMAX_SNPRINTF
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+}
+
+static void wire_loc_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ // Read values.
+ wire_ctx_t wire = wire_ctx_init_const(p->in, p->in_max);
+ uint8_t version = wire_ctx_read_u8(&wire);
+
+ // Version check.
+ if (version != 0) {
+ wire_unknown_to_str(p);
+ p->ret = -1;
+ return;
+ }
+
+ // Continue to read values.
+ uint8_t size_w = wire_ctx_read_u8(&wire);
+ uint8_t hpre_w = wire_ctx_read_u8(&wire);
+ uint8_t vpre_w = wire_ctx_read_u8(&wire);
+ uint32_t lat_w = wire_ctx_read_u32(&wire);
+ uint32_t lon_w = wire_ctx_read_u32(&wire);
+ uint32_t alt_w = wire_ctx_read_u32(&wire);
+
+ // Check if all reads are correct.
+ if (wire.error != KNOT_EOK) {
+ p->ret = -1;
+ return;
+ }
+
+ p->in += wire_ctx_offset(&wire);
+ p->in_max = wire_ctx_available(&wire);
+
+ // Latitude calculation.
+ char lat_mark;
+ uint32_t lat;
+ if (lat_w >= LOC_ZERO) {
+ lat_mark = 'N';
+ lat = lat_w - LOC_ZERO;
+ } else {
+ lat_mark = 'S';
+ lat = LOC_ZERO - lat_w;
+ }
+
+ uint32_t d1 = lat / 3600000;
+ uint32_t m1 = (lat - 3600000 * d1) / 60000;
+ double s1 = 0.001 * (lat - 3600000 * d1 - 60000 * m1);
+
+ // Longitude calculation.
+ char lon_mark;
+ uint32_t lon;
+ if (lon_w >= LOC_ZERO) {
+ lon_mark = 'E';
+ lon = lon_w - LOC_ZERO;
+ } else {
+ lon_mark = 'W';
+ lon = LOC_ZERO - lon_w;
+ }
+
+ uint32_t d2 = lon / 3600000;
+ uint32_t m2 = (lon - 3600000 * d2) / 60000;
+ double s2 = 0.001 * (lon - 3600000 * d2 - 60000 * m2);
+
+ // Write latitude and longitude.
+ int ret = snprintf(p->out, p->out_max, "%u %u %.*f %c %u %u %.*f %c",
+ d1, m1, (uint32_t)s1 != s1 ? 3 : 0, s1, lat_mark,
+ d2, m2, (uint32_t)s2 != s2 ? 3 : 0, s2, lon_mark);
+ CHECK_RET_OUTMAX_SNPRINTF
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+
+ // Altitude calculation.
+ double alt = 0.01 * alt_w - 100000.0;
+
+ // Compute mantissa and exponent for each size.
+ uint8_t size_m = size_w >> 4;
+ uint8_t size_e = size_w & 0xF;
+ uint8_t hpre_m = hpre_w >> 4;
+ uint8_t hpre_e = hpre_w & 0xF;
+ uint8_t vpre_m = vpre_w >> 4;
+ uint8_t vpre_e = vpre_w & 0xF;
+
+ // Sizes check.
+ if (size_m > 9 || size_e > 9 || hpre_m > 9 || hpre_e > 9 ||
+ vpre_m > 9 || vpre_e > 9) {
+ p->ret = -1;
+ return;
+ }
+
+ // Size and precisions calculation.
+ double size = 0.01 * size_m * pow(10, size_e);
+ double hpre = 0.01 * hpre_m * pow(10, hpre_e);
+ double vpre = 0.01 * vpre_m * pow(10, vpre_e);
+
+ // Write altitude and precisions.
+ ret = snprintf(p->out, p->out_max, " %.*fm %.*fm %.*fm %.*fm",
+ (int32_t)alt != alt ? 2 : 0, alt,
+ (uint32_t)size != size ? 2 : 0, size,
+ (uint32_t)hpre != hpre ? 2 : 0, hpre,
+ (uint32_t)vpre != vpre ? 2 : 0, vpre);
+ CHECK_RET_OUTMAX_SNPRINTF
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+}
+
+static void wire_gateway_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ // Input check: type(1B) + algo(1B).
+ CHECK_INMAX(2)
+
+ uint8_t type = *p->in;
+ uint8_t alg = *(p->in + 1);
+
+ // Write gateway type.
+ wire_num8_to_str(p);
+ CHECK_PRET
+
+ // Write space.
+ dump_string(p, " ");
+ CHECK_PRET
+
+ // Write algorithm number.
+ wire_num8_to_str(p);
+ CHECK_PRET
+
+ // Write space.
+ dump_string(p, " ");
+ CHECK_PRET
+
+ // Write appropriate gateway.
+ switch (type) {
+ case 0:
+ dump_string(p, ".");
+ break;
+ case 1:
+ wire_ipv4_to_str(p);
+ break;
+ case 2:
+ wire_ipv6_to_str(p);
+ break;
+ case 3:
+ wire_dname_to_str(p);
+ break;
+ default:
+ p->ret = -1;
+ }
+ CHECK_PRET
+
+ if (alg > 0) {
+ // If wrap mode wrap line.
+ if (p->style->wrap) {
+ dump_string(p, BLOCK_INDENT);
+ } else {
+ dump_string(p, " ");
+ }
+ CHECK_PRET
+
+ // Write ipsec key.
+ wire_data_encode_to_str(p, &knot_base64_encode, &knot_base64_encode_alloc);
+ CHECK_PRET
+ }
+}
+
+static void wire_l64_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ // Check input size (64-bit identifier).
+ if (p->in_max != 8) {
+ p->ret = -1;
+ return;
+ }
+
+ // Write identifier (2-byte) labels separated with a colon.
+ while (p->in_max > 0) {
+ int ret = hex_encode(p->in, 2, (uint8_t *)(p->out), p->out_max);
+ CHECK_RET_POSITIVE
+ p->in += 2;
+ p->in_max -= 2;
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+
+ // Write separation character.
+ if (p->in_max > 0) {
+ dump_string(p, ":");
+ CHECK_PRET
+ }
+ }
+}
+
+static void wire_eui_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ CHECK_INMAX(2)
+
+ // Write EUI hexadecimal pairs.
+ while (p->in_max > 0) {
+ int ret = hex_encode(p->in, 1, (uint8_t *)(p->out), p->out_max);
+ CHECK_RET_POSITIVE
+ p->in++;
+ p->in_max--;
+ p->out += ret;
+ p->out_max -= ret;
+ p->total += ret;
+
+ // Write separation character.
+ if (p->in_max > 0) {
+ dump_string(p, "-");
+ CHECK_PRET
+ }
+ }
+}
+
+static void wire_tsig_rcode_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ uint16_t data;
+ size_t in_len = sizeof(data);
+ const char *rcode_str = "Unknown";
+
+ CHECK_INMAX(in_len)
+
+ // Fill in input data.
+ data = knot_wire_read_u16(p->in);
+
+ // Find RCODE name.
+ const knot_lookup_t *rcode = NULL;
+ rcode = knot_lookup_by_id(knot_tsig_rcode_names, data);
+ if (rcode == NULL) {
+ rcode = knot_lookup_by_id(knot_rcode_names, data);
+ }
+ if (rcode != NULL) {
+ rcode_str = rcode->name;
+ }
+
+ // Dump RCODE name.
+ dump_string(p, rcode_str);
+ CHECK_PRET
+
+ // Fill in output.
+ p->in += in_len;
+ p->in_max -= in_len;
+}
+
+static void wire_svcb_paramkey_to_str(rrset_dump_params_t *p)
+{
+ uint16_t param_key = knot_wire_read_u16(p->in);
+ const knot_lookup_t *type = knot_lookup_by_id(knot_svcb_param_names, param_key);
+
+ if (type != NULL) {
+ dump_string(p, type->name);
+ CHECK_PRET
+ p->in += sizeof(param_key);
+ p->in_max -= sizeof(param_key);
+ } else {
+ dump_string(p, "key");
+ CHECK_PRET
+ wire_num16_to_str(p);
+ CHECK_PRET
+ }
+}
+
+static void wire_value_list_to_str(rrset_dump_params_t *p,
+ void (*list_item_dump_fcn)(rrset_dump_params_t *p),
+ const uint8_t *expect_end)
+{
+ bool first = true;
+
+ while (expect_end > p->in) {
+ if (first) {
+ first = false;
+ } else {
+ dump_string(p, ",");
+ CHECK_PRET
+ }
+
+ list_item_dump_fcn(p);
+ CHECK_PRET
+ }
+ if (expect_end != p->in) {
+ p->ret = -1;
+ }
+}
+
+static void wire_text_to_str1(rrset_dump_params_t *p)
+{
+ wire_text_to_str(p, false, 1, true);
+}
+
+static void wire_ech_to_base64(rrset_dump_params_t *p, unsigned ech_len)
+{
+ CHECK_INMAX(ech_len)
+
+ int ret = knot_base64_encode(p->in, ech_len, (uint8_t *)(p->out), p->out_max);
+ CHECK_RET_POSITIVE
+ size_t out_len = ret;
+
+ p->in += ech_len;
+ p->in_max -= ech_len;
+ p->out += out_len;
+ p->out_max -= out_len;
+ p->total += out_len;
+
+ STRING_TERMINATION
+}
+
+static void wire_svcparam_to_str(rrset_dump_params_t *p)
+{
+ CHECK_PRET
+
+ CHECK_INMAX(4)
+
+ // Pre-fetch key and length for later use.
+ uint16_t key_type = knot_wire_read_u16(p->in);
+ uint16_t val_len = knot_wire_read_u16(p->in + sizeof(key_type));
+
+ wire_svcb_paramkey_to_str(p);
+
+ p->in += sizeof(val_len);
+ p->in_max -= sizeof(val_len);
+ CHECK_INMAX(val_len)
+
+ if (val_len > 0) {
+ dump_string(p, "=");
+ CHECK_PRET
+
+ switch (key_type) {
+ case KNOT_SVCB_PARAM_MANDATORY:
+ wire_value_list_to_str(p, wire_svcb_paramkey_to_str, p->in + val_len);
+ break;
+ case KNOT_SVCB_PARAM_ALPN:
+ wire_value_list_to_str(p, wire_text_to_str1, p->in + val_len);
+ break;
+ case KNOT_SVCB_PARAM_NDALPN:
+ p->ret = -1; // must not have value
+ break;
+ case KNOT_SVCB_PARAM_PORT:
+ if (val_len != sizeof(uint16_t)) {
+ p->ret = -1;
+ } else {
+ wire_num16_to_str(p);
+ }
+ break;
+ case KNOT_SVCB_PARAM_IPV4HINT:
+ wire_value_list_to_str(p, wire_ipv4_to_str, p->in + val_len);
+ break;
+ case KNOT_SVCB_PARAM_ECH:
+ wire_ech_to_base64(p, val_len);
+ break;
+ case KNOT_SVCB_PARAM_IPV6HINT:
+ wire_value_list_to_str(p, wire_ipv6_to_str, p->in + val_len);
+ break;
+ default:
+ p->in -= sizeof(val_len); // Rollback to where the string length resides.
+ p->in_max += sizeof(val_len);
+ wire_text_to_str(p, true, sizeof(val_len), false);
+ }
+ }
+}
+
+static size_t dnskey_len(const uint8_t *rdata,
+ const size_t rdata_len)
+{
+ // Check for empty rdata and empty key.
+ if (rdata_len <= 4) {
+ return 0;
+ }
+
+ const uint8_t *key = rdata + 4;
+ const size_t len = rdata_len - 4;
+
+ switch (rdata[3]) {
+ case DNSSEC_KEY_ALGORITHM_DSA:
+ case DNSSEC_KEY_ALGORITHM_DSA_NSEC3_SHA1:
+ // RFC 2536, key size ~ bit-length of 'modulus' P.
+ return (64 + 8 * key[0]) * 8;
+ case DNSSEC_KEY_ALGORITHM_RSA_MD5:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ // RFC 3110, key size ~ bit-length of 'modulus'.
+ if (key[0] == 0) {
+ if (len < 3) {
+ return 0;
+ }
+ uint16_t exp;
+ memcpy(&exp, key + 1, sizeof(uint16_t));
+ return (len - 3 - ntohs(exp)) * 8;
+ } else {
+ return (len - 1 - key[0]) * 8;
+ }
+ case DNSSEC_KEY_ALGORITHM_ECC_GOST:
+ // RFC 5933, key size of GOST public keys MUST be 512 bits.
+ return 512;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ // RFC 6605.
+ return 256;
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ // RFC 6605.
+ return 384;
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+ // RFC 8080.
+ return 256;
+ case DNSSEC_KEY_ALGORITHM_ED448:
+ // RFC 8080.
+ return 456;
+ default:
+ return 0;
+ }
+}
+
+static int ber_to_oid(char *dst,
+ size_t dst_len,
+ const uint8_t *src,
+ const size_t src_len)
+{
+ assert(dst);
+ assert(src);
+
+ static const uint8_t longer_mask = (1 << 7);
+
+ size_t len = src[0];
+ if (len == 0 || len >= src_len || dst_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ uint64_t node = 0UL;
+ for (int i = 1; i <= len; ++i) {
+ uint8_t longer_node = (src[i] & longer_mask);
+ node <<= 7;
+ node += (longer_node ^ src[i]);
+ if (!longer_node) {
+ int ret = snprintf(dst, dst_len, "%"PRIu64".", node);
+ SNPRINTF_CHECK(ret, dst_len);
+ dst += ret;
+ dst_len -= ret;
+ node = 0UL;
+ }
+ }
+ *(dst - 1) = '\0';
+
+ return KNOT_EOK;
+}
+
+static void dnskey_info(const uint8_t *rdata,
+ const size_t rdata_len,
+ char *out,
+ const size_t out_len)
+{
+ if (rdata_len < 5) {
+ return;
+ }
+
+ const uint8_t sep = *(rdata + 1) & 0x01;
+ uint16_t key_tag = 0;
+ const size_t key_len = dnskey_len(rdata, rdata_len);
+ const uint8_t alg_id = rdata[3];
+ char alg_info[512] = "";
+
+ const dnssec_binary_t rdata_bin = { .data = (uint8_t *)rdata,
+ .size = rdata_len };
+ dnssec_keytag(&rdata_bin, &key_tag);
+
+ const knot_lookup_t *alg = knot_lookup_by_id(knot_dnssec_alg_names, alg_id);
+
+ switch (alg_id) {
+ case DNSSEC_KEY_ALGORITHM_DELETE:
+ case DNSSEC_KEY_ALGORITHM_INDIRECT:
+ break;
+ case DNSSEC_KEY_ALGORITHM_PRIVATEOID:
+ ; char oid_str[sizeof(alg_info) - 3];
+ if (ber_to_oid(oid_str, sizeof(oid_str), rdata + 4, rdata_len - 4) != KNOT_EOK ||
+ snprintf(alg_info, sizeof(alg_info), " (%s)", oid_str) <= 0) {
+ alg_info[0] = '\0';
+ }
+ break;
+ case DNSSEC_KEY_ALGORITHM_PRIVATEDNS:
+ ; knot_dname_txt_storage_t alg_str;
+ if (knot_dname_wire_check(rdata + 4, rdata + rdata_len, NULL) <= 0 ||
+ knot_dname_to_str(alg_str, rdata + 4, sizeof(alg_str)) == NULL ||
+ snprintf(alg_info, sizeof(alg_info), " (%s)", alg_str) <= 0) {
+ alg_info[0] = '\0';
+ }
+ break;
+ default:
+ if (snprintf(alg_info, sizeof(alg_info), " (%zub)", key_len) <= 0) {
+ alg_info[0] = '\0';
+ }
+ break;
+ }
+
+ int ret = snprintf(out, out_len, "%s, %s%s, id = %u",
+ sep ? "KSK" : "ZSK",
+ alg ? alg->name : "UNKNOWN",
+ alg_info,
+ key_tag);
+ if (ret <= 0) { // Truncated return is acceptable. Just check for errors.
+ out[0] = '\0';
+ }
+}
+
+#define DUMP_PARAMS rrset_dump_params_t *const p
+#define DUMP_END return (p->in_max == 0 ? (int)p->total : KNOT_EPARSEFAIL);
+
+#define CHECK_RET(p) if (p->ret < 0) return p->ret;
+
+#define WRAP_INIT dump_string(p, "(" BLOCK_INDENT); CHECK_RET(p);
+#define WRAP_END dump_string(p, BLOCK_INDENT ")"); CHECK_RET(p);
+#define WRAP_LINE dump_string(p, BLOCK_INDENT); CHECK_RET(p);
+
+#define COMMENT(s) if (p->style->verbose) { \
+ dump_string(p, " ; "); CHECK_RET(p); \
+ dump_string(p, s); CHECK_RET(p); \
+ }
+
+#define STORE_TIME if (p->style->verbose) { \
+ time = wire_time_to_val(p); CHECK_RET(p); \
+ }
+#define COMMENT_TIME(s) if (p->style->verbose) { \
+ char buf[80]; \
+ dump_string(p, " ; "); CHECK_RET(p); \
+ dump_string(p, s); CHECK_RET(p); \
+ if (knot_time_print_human(time, buf, sizeof(buf), false) > 0) { \
+ dump_string(p, " ("); CHECK_RET(p); \
+ dump_string(p, buf); CHECK_RET(p); \
+ dump_string(p, ")"); CHECK_RET(p); \
+ } \
+ }
+
+#define DUMP_SPACE dump_string(p, " "); CHECK_RET(p);
+#define DUMP_NUM8 wire_num8_to_str(p); CHECK_RET(p);
+#define DUMP_NUM16 wire_num16_to_str(p); CHECK_RET(p);
+#define DUMP_NUM32 wire_num32_to_str(p); CHECK_RET(p);
+#define DUMP_NUM48 wire_num48_to_str(p); CHECK_RET(p);
+#define DUMP_DNAME wire_dname_to_str(p); CHECK_RET(p);
+#define DUMP_TIME wire_ttl_to_str(p); CHECK_RET(p);
+#define DUMP_TIMESTAMP wire_timestamp_to_str(p); CHECK_RET(p);
+#define DUMP_IPV4 wire_ipv4_to_str(p); CHECK_RET(p);
+#define DUMP_IPV6 wire_ipv6_to_str(p); CHECK_RET(p);
+#define DUMP_TYPE wire_type_to_str(p); CHECK_RET(p);
+#define DUMP_HEX wire_data_encode_to_str(p, &hex_encode, \
+ &hex_encode_alloc); CHECK_RET(p);
+#define DUMP_BASE64 wire_data_encode_to_str(p, &knot_base64_encode, \
+ &knot_base64_encode_alloc); CHECK_RET(p);
+#define DUMP_HASH wire_len_data_encode_to_str(p, &knot_base32hex_encode, \
+ 1, false, ""); CHECK_RET(p);
+#define DUMP_SALT wire_len_data_encode_to_str(p, &hex_encode, \
+ 1, false, "-"); CHECK_RET(p);
+#define DUMP_TSIG_DGST wire_len_data_encode_to_str(p, &knot_base64_encode, \
+ 2, true, ""); CHECK_RET(p);
+#define DUMP_TSIG_DATA wire_len_data_encode_to_str(p, &num48_encode, \
+ 2, true, ""); CHECK_RET(p);
+#define DUMP_OMIT wire_data_omit(p); CHECK_RET(p);
+#define DUMP_KEY_OMIT wire_dnskey_to_tag(p); CHECK_RET(p);
+#define DUMP_TEXT wire_text_to_str(p, true, 1, false); CHECK_RET(p);
+#define DUMP_LONG_TEXT wire_text_to_str(p, true, 0, false); CHECK_RET(p);
+#define DUMP_UNQUOTED wire_text_to_str(p, false, 1, false); CHECK_RET(p);
+#define DUMP_BITMAP wire_bitmap_to_str(p); CHECK_RET(p);
+#define DUMP_APL wire_apl_to_str(p); CHECK_RET(p);
+#define DUMP_LOC wire_loc_to_str(p); CHECK_RET(p);
+#define DUMP_GATEWAY wire_gateway_to_str(p); CHECK_RET(p);
+#define DUMP_L64 wire_l64_to_str(p); CHECK_RET(p);
+#define DUMP_EUI wire_eui_to_str(p); CHECK_RET(p);
+#define DUMP_TSIG_RCODE wire_tsig_rcode_to_str(p); CHECK_RET(p);
+#define DUMP_SVCPARAM wire_svcparam_to_str(p); CHECK_RET(p);
+#define DUMP_UNKNOWN wire_unknown_to_str(p); CHECK_RET(p);
+
+static int dump_a(DUMP_PARAMS)
+{
+ DUMP_IPV4;
+
+ DUMP_END;
+}
+
+static int dump_ns(DUMP_PARAMS)
+{
+ DUMP_DNAME;
+
+ DUMP_END;
+}
+
+static int dump_soa(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ uint32_t time = 0;
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_DNAME; DUMP_SPACE; WRAP_INIT;
+ DUMP_NUM32; COMMENT("serial"); WRAP_LINE;
+ STORE_TIME; DUMP_TIME; COMMENT_TIME("refresh"); WRAP_LINE;
+ STORE_TIME; DUMP_TIME; COMMENT_TIME("retry"); WRAP_LINE;
+ STORE_TIME; DUMP_TIME; COMMENT_TIME("expire"); WRAP_LINE;
+ STORE_TIME; DUMP_TIME; COMMENT_TIME("minimum"); WRAP_END;
+ } else {
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_TIME; DUMP_SPACE;
+ DUMP_TIME; DUMP_SPACE;
+ DUMP_TIME; DUMP_SPACE;
+ DUMP_TIME;
+ }
+
+ DUMP_END;
+}
+
+static int dump_hinfo(DUMP_PARAMS)
+{
+ DUMP_TEXT; DUMP_SPACE;
+ DUMP_TEXT;
+
+ DUMP_END;
+}
+
+static int dump_minfo(DUMP_PARAMS)
+{
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_DNAME;
+
+ DUMP_END;
+}
+
+static int dump_mx(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_DNAME;
+
+ DUMP_END;
+}
+
+static int dump_txt(DUMP_PARAMS)
+{
+ // First text string.
+ DUMP_TEXT;
+
+ // Other text strings if any.
+ while (p->in_max > 0) {
+ DUMP_SPACE; DUMP_TEXT;
+ }
+
+ DUMP_END;
+}
+
+static int dump_dnskey(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ char info[512] = "";
+ dnskey_info(p->in, p->in_max, info, sizeof(info));
+
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ if (p->style->hide_crypto) {
+ DUMP_OMIT;
+ WRAP_LINE;
+ } else {
+ WRAP_INIT;
+ DUMP_BASE64;
+ WRAP_END;
+ }
+ COMMENT(info);
+ } else {
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ if (p->style->hide_crypto) {
+ DUMP_KEY_OMIT;
+ } else {
+ DUMP_BASE64;
+ }
+ }
+
+ DUMP_END;
+}
+
+static int dump_aaaa(DUMP_PARAMS)
+{
+ DUMP_IPV6;
+
+ DUMP_END;
+}
+
+static int dump_loc(DUMP_PARAMS)
+{
+ DUMP_LOC;
+
+ DUMP_END;
+}
+
+static int dump_srv(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_DNAME;
+
+ DUMP_END;
+}
+
+static int dump_naptr(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_TEXT; DUMP_SPACE;
+ DUMP_TEXT; DUMP_SPACE;
+ DUMP_TEXT; DUMP_SPACE;
+ DUMP_DNAME;
+
+ DUMP_END;
+}
+
+static int dump_cert(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_BASE64;
+ WRAP_END;
+ } else {
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_BASE64;
+ }
+
+ DUMP_END;
+}
+
+static int dump_apl(DUMP_PARAMS)
+{
+ // Print list of APLs (empty list is allowed).
+ while (p->in_max > 0) {
+ if (p->total > 0) {
+ DUMP_SPACE;
+ }
+ DUMP_APL;
+ }
+
+ DUMP_END;
+}
+
+static int dump_ds(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_HEX;
+ WRAP_END;
+ } else {
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX;
+ }
+
+ DUMP_END;
+}
+
+static int dump_sshfp(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_HEX;
+ WRAP_END;
+ } else {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX;
+ }
+
+ DUMP_END;
+}
+
+static int dump_ipseckey(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_GATEWAY;
+ WRAP_END;
+ } else {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_GATEWAY;
+ }
+
+ DUMP_END;
+}
+
+static int dump_rrsig(DUMP_PARAMS)
+{
+ DUMP_TYPE; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_TIMESTAMP; DUMP_SPACE;
+ if (p->style->wrap) {
+ WRAP_INIT;
+ }
+ DUMP_TIMESTAMP; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_DNAME;
+ if (p->style->wrap) {
+ WRAP_LINE;
+ } else {
+ DUMP_SPACE;
+ }
+ if (p->style->hide_crypto) {
+ DUMP_OMIT;
+ } else {
+ DUMP_BASE64;
+ }
+ if (p->style->wrap) {
+ WRAP_END;
+ }
+ DUMP_END;
+}
+
+static int dump_nsec(DUMP_PARAMS)
+{
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_BITMAP;
+
+ DUMP_END;
+}
+
+static int dump_dhcid(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ WRAP_INIT;
+ DUMP_BASE64;
+ WRAP_END;
+ } else {
+ DUMP_BASE64;
+ }
+
+ DUMP_END;
+}
+
+static int dump_nsec3(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_SALT; DUMP_SPACE; WRAP_INIT;
+ DUMP_HASH; WRAP_LINE;
+ DUMP_BITMAP;
+ WRAP_END;
+ } else {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_SALT; DUMP_SPACE;
+ DUMP_HASH; DUMP_SPACE;
+ DUMP_BITMAP;
+ }
+
+ DUMP_END;
+}
+
+static int dump_nsec3param(DUMP_PARAMS)
+{
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_SALT;
+
+ DUMP_END;
+}
+
+static int dump_tlsa(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_HEX;
+ WRAP_END;
+ } else {
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX;
+ }
+
+ DUMP_END;
+}
+
+static int dump_csync(DUMP_PARAMS)
+{
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_BITMAP;
+
+ DUMP_END;
+}
+
+static int dump_zonemd(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE; WRAP_INIT;
+ DUMP_HEX;
+ WRAP_END;
+ } else {
+ DUMP_NUM32; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_HEX;
+ }
+
+ DUMP_END;
+}
+
+static int dump_l64(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_L64;
+
+ DUMP_END;
+}
+
+static int dump_l32(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_IPV4;
+
+ DUMP_END;
+}
+
+static int dump_eui(DUMP_PARAMS)
+{
+ DUMP_EUI;
+
+ DUMP_END;
+}
+
+static int dump_tsig(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_NUM48; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE; WRAP_INIT;
+ DUMP_TSIG_DGST; WRAP_LINE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_TSIG_RCODE; DUMP_SPACE;
+ DUMP_TSIG_DATA;
+ WRAP_END;
+ } else {
+ DUMP_DNAME; DUMP_SPACE;
+ DUMP_NUM48; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_TSIG_DGST; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_TSIG_RCODE; DUMP_SPACE;
+ DUMP_TSIG_DATA;
+ }
+
+ DUMP_END;
+}
+
+static int dump_uri(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_LONG_TEXT; DUMP_SPACE;
+
+ DUMP_END;
+}
+
+static int dump_caa(DUMP_PARAMS)
+{
+ DUMP_NUM8; DUMP_SPACE;
+ DUMP_UNQUOTED; DUMP_SPACE;
+ DUMP_LONG_TEXT; DUMP_SPACE;
+
+ DUMP_END;
+}
+
+static int dump_svcb(DUMP_PARAMS)
+{
+ DUMP_NUM16; DUMP_SPACE;
+ DUMP_DNAME;
+ if (p->style->wrap) {
+ if (p->in_max > 0) {
+ DUMP_SPACE;
+ WRAP_INIT;
+ DUMP_SVCPARAM;
+ while (p->in_max > 0) {
+ WRAP_LINE; DUMP_SVCPARAM;
+ }
+ WRAP_END;
+ }
+ } else {
+ while (p->in_max > 0) {
+ DUMP_SPACE;
+ DUMP_SVCPARAM;
+ }
+ }
+
+ DUMP_END;
+}
+
+static int dump_unknown(DUMP_PARAMS)
+{
+ if (p->style->wrap) {
+ WRAP_INIT;
+ DUMP_UNKNOWN;
+ WRAP_END;
+ } else {
+ DUMP_UNKNOWN;
+ }
+
+ DUMP_END;
+}
+
+static int txt_dump_data(rrset_dump_params_t *p, uint16_t type)
+{
+ switch (type) {
+ case KNOT_RRTYPE_A:
+ return dump_a(p);
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ return dump_ns(p);
+ case KNOT_RRTYPE_SOA:
+ return dump_soa(p);
+ case KNOT_RRTYPE_HINFO:
+ return dump_hinfo(p);
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ return dump_minfo(p);
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ return dump_mx(p);
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ return dump_txt(p);
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ return dump_dnskey(p);
+ case KNOT_RRTYPE_AAAA:
+ return dump_aaaa(p);
+ case KNOT_RRTYPE_LOC:
+ return dump_loc(p);
+ case KNOT_RRTYPE_SRV:
+ return dump_srv(p);
+ case KNOT_RRTYPE_NAPTR:
+ return dump_naptr(p);
+ case KNOT_RRTYPE_CERT:
+ return dump_cert(p);
+ case KNOT_RRTYPE_APL:
+ return dump_apl(p);
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ return dump_ds(p);
+ case KNOT_RRTYPE_SSHFP:
+ return dump_sshfp(p);
+ case KNOT_RRTYPE_IPSECKEY:
+ return dump_ipseckey(p);
+ case KNOT_RRTYPE_RRSIG:
+ return dump_rrsig(p);
+ case KNOT_RRTYPE_NSEC:
+ return dump_nsec(p);
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ return dump_dhcid(p);
+ case KNOT_RRTYPE_NSEC3:
+ return dump_nsec3(p);
+ case KNOT_RRTYPE_NSEC3PARAM:
+ return dump_nsec3param(p);
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ return dump_tlsa(p);
+ case KNOT_RRTYPE_CSYNC:
+ return dump_csync(p);
+ case KNOT_RRTYPE_ZONEMD:
+ return dump_zonemd(p);
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ return dump_l64(p);
+ case KNOT_RRTYPE_L32:
+ return dump_l32(p);
+ case KNOT_RRTYPE_EUI48:
+ case KNOT_RRTYPE_EUI64:
+ return dump_eui(p);
+ case KNOT_RRTYPE_TSIG:
+ return dump_tsig(p);
+ case KNOT_RRTYPE_URI:
+ return dump_uri(p);
+ case KNOT_RRTYPE_CAA:
+ return dump_caa(p);
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ return dump_svcb(p);
+ default:
+ return dump_unknown(p);
+ }
+}
+
+_public_
+int knot_rrset_txt_dump_data(const knot_rrset_t *rrset,
+ const size_t pos,
+ char *dst,
+ const size_t maxlen,
+ const knot_dump_style_t *style)
+{
+ if (rrset == NULL || dst == NULL || style == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, pos);
+ if (rr_data == NULL) {
+ return KNOT_EINVAL; /* bad pos or rrset->rrs */
+ }
+
+ uint8_t *data = rr_data->data;
+ uint16_t data_len = rr_data->len;
+
+ rrset_dump_params_t p = {
+ .style = style,
+ .in = data,
+ .in_max = data_len,
+ .out = dst,
+ .out_max = maxlen,
+ .total = 0,
+ .ret = 0
+ };
+
+ int ret;
+
+ // Allow empty rdata with the CH class (knsupdate).
+ if (data_len == 0 && rrset->rclass != KNOT_CLASS_IN) {
+ ret = 0;
+ } else if (style->generic) {
+ ret = dump_unknown(&p);
+ } else {
+ ret = txt_dump_data(&p, rrset->type);
+ }
+
+ // Terminate the string just in case.
+ if (ret < 0 || ret >= maxlen) {
+ return KNOT_ESPACE;
+ }
+ dst[ret] = '\0';
+
+ return ret;
+}
+
+_public_
+int knot_rrset_txt_dump_header(const knot_rrset_t *rrset,
+ const uint32_t ttl,
+ char *dst,
+ const size_t maxlen,
+ const knot_dump_style_t *style)
+{
+ if (rrset == NULL || dst == NULL || style == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t len = 0;
+ char buf[32];
+ int ret;
+
+ // Dump rrset owner.
+ char *name = knot_dname_to_str_alloc(rrset->owner);
+ if (style->ascii_to_idn != NULL) {
+ style->ascii_to_idn(&name);
+ }
+ char sep = strlen(name) < 4 * TAB_WIDTH ? '\t' : ' ';
+ ret = snprintf(dst + len, maxlen - len, "%-20s%c", name, sep);
+ free(name);
+ SNPRINTF_CHECK(ret, maxlen - len);
+ len += ret;
+
+ // Set white space separation character.
+ sep = style->wrap ? ' ' : '\t';
+
+ // Dump rrset ttl.
+ if (style->show_ttl) {
+ if (style->empty_ttl) {
+ ret = snprintf(dst + len, maxlen - len, "%c", sep);
+ } else if (style->human_ttl) {
+ // Create human readable ttl string.
+ if (knot_time_print_human(ttl, buf, sizeof(buf), true) < 0) {
+ return KNOT_ESPACE;
+ }
+ ret = snprintf(dst + len, maxlen - len, "%s%c",
+ buf, sep);
+ } else {
+ ret = snprintf(dst + len, maxlen - len, "%u%c", ttl, sep);
+ }
+ SNPRINTF_CHECK(ret, maxlen - len);
+ len += ret;
+ }
+
+ // Dump rrset class.
+ if (style->show_class) {
+ if (knot_rrclass_to_string(rrset->rclass, buf, sizeof(buf)) < 0) {
+ return KNOT_ESPACE;
+ }
+ ret = snprintf(dst + len, maxlen - len, "%-2s%c", buf, sep);
+ SNPRINTF_CHECK(ret, maxlen - len);
+ len += ret;
+ }
+
+ // Dump rrset type.
+ if (style->generic) {
+ if (snprintf(buf, sizeof(buf), "TYPE%u", rrset->type) < 0) {
+ return KNOT_ESPACE;
+ }
+ } else if (knot_rrtype_to_string(rrset->type, buf, sizeof(buf)) < 0) {
+ return KNOT_ESPACE;
+ }
+ if (rrset->rrs.count > 0) {
+ ret = snprintf(dst + len, maxlen - len, "%s%c", buf, sep);
+ } else {
+ ret = snprintf(dst + len, maxlen - len, "%s", buf);
+ }
+ SNPRINTF_CHECK(ret, maxlen - len);
+ len += ret;
+
+ return len;
+}
+
+static int rrset_txt_dump(const knot_rrset_t *rrset,
+ char *dst,
+ const size_t maxlen,
+ const knot_dump_style_t *style)
+{
+ if (rrset == NULL || dst == NULL || style == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t len = 0;
+ size_t color_len = (style->color != NULL ? strlen(style->color) : 0);
+ size_t reset_len = (color_len > 0 ? strlen(COL_RST(true)) : 0);
+
+ dst[0] = '\0';
+
+ // Loop over rdata in rrset.
+ uint16_t rr_count = rrset->rrs.count;
+ knot_rdata_t *rr = rrset->rrs.rdata;
+ for (uint16_t i = 0; i < rr_count; i++) {
+ // Put color prefix before every record.
+ if (color_len > 0) {
+ if (len >= maxlen - color_len) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst + len, style->color, color_len);
+ len += color_len;
+ }
+
+ // Dump rdata owner, class, ttl and type.
+ uint32_t ttl = ((style->original_ttl && rrset->type == KNOT_RRTYPE_RRSIG) ?
+ knot_rrsig_original_ttl(rr) : rrset->ttl);
+
+ int ret = knot_rrset_txt_dump_header(rrset, ttl, dst + len,
+ maxlen - len, style);
+ if (ret < 0) {
+ return KNOT_ESPACE;
+ }
+ len += ret;
+
+ // Dump rdata as such.
+ ret = knot_rrset_txt_dump_data(rrset, i, dst + len,
+ maxlen - len, style);
+ if (ret < 0) {
+ return KNOT_ESPACE;
+ }
+ len += ret;
+
+ // Reset the color.
+ if (reset_len > 0) {
+ if (len >= maxlen - reset_len) {
+ return KNOT_ESPACE;
+ }
+ memcpy(dst + len, COL_RST(true), reset_len);
+ len += reset_len;
+ }
+
+ // Terminate line.
+ if (len >= maxlen - 1) {
+ return KNOT_ESPACE;
+ }
+ dst[len++] = '\n';
+ dst[len] = '\0';
+
+ rr = knot_rdataset_next(rr);
+ }
+
+ return len;
+}
+
+_public_
+int knot_rrset_txt_dump(const knot_rrset_t *rrset,
+ char **dst,
+ size_t *dst_size,
+ const knot_dump_style_t *style)
+{
+ if (dst == NULL || dst_size == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ while (1) {
+ int ret = rrset_txt_dump(rrset, *dst, *dst_size, style);
+ if (ret != KNOT_ESPACE) {
+ return ret;
+ }
+
+ size_t new_dst_size = 2 * (*dst_size);
+ if (new_dst_size > RRSET_DUMP_LIMIT) {
+ return KNOT_ESPACE;
+ }
+
+ char * new_dst = malloc(new_dst_size);
+ if (new_dst == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ free(*dst);
+ *dst = new_dst;
+ *dst_size = new_dst_size;
+ }
+}
diff --git a/src/libknot/rrset-dump.h b/src/libknot/rrset-dump.h
new file mode 100644
index 0000000..4631531
--- /dev/null
+++ b/src/libknot/rrset-dump.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief RRset text dump facility.
+ *
+ * \addtogroup rr
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "libknot/rrset.h"
+
+/*! \brief Text output settings. */
+typedef struct {
+ /*!< Wrap long records. */
+ bool wrap;
+ /*!< Show class. */
+ bool show_class;
+ /*!< Show TTL. */
+ bool show_ttl;
+ /*!< Print extra information. */
+ bool verbose;
+ /*!< Print RRSIG original TTL instead of rrset TTL. */
+ bool original_ttl;
+ /*!< Show empty TTL value (keep indentation). */
+ bool empty_ttl;
+ /*!< Format TTL as DHMS. */
+ bool human_ttl;
+ /*!< Format timestamp as YYYYMMDDHHmmSS. */
+ bool human_timestamp;
+ /*!< Force generic data representation. */
+ bool generic;
+ /*!< Hide binary parts of RRSIGs and DNSKEYs. */
+ bool hide_crypto;
+ /*!< ASCII string to IDN string transformation callback. */
+ void (*ascii_to_idn)(char **name);
+ /*!< Optional color control sequence which is put before every output line.
+ * Not compatible with wrap. */
+ const char *color;
+ /*!< Time context for correct > 32 bit timestamp conversion. */
+ uint64_t now;
+} knot_dump_style_t;
+
+/*! \brief Default dump style. */
+extern const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT;
+
+/*!
+ * \brief Dumps rrset header.
+ *
+ * \param rrset RRset to dump.
+ * \param ttl TTL to dump.
+ * \param dst Output buffer.
+ * \param maxlen Output buffer size.
+ * \param style Output style.
+ *
+ * \retval output length if success.
+ * \retval < 0 if error.
+ */
+int knot_rrset_txt_dump_header(const knot_rrset_t *rrset,
+ const uint32_t ttl,
+ char *dst,
+ const size_t maxlen,
+ const knot_dump_style_t *style);
+
+/*!
+ * \brief Dumps rrset data.
+ *
+ * \param rrset RRset to dump.
+ * \param pos Position of the record to dump.
+ * \param dst Output buffer.
+ * \param maxlen Output buffer size.
+ * \param style Output style.
+ *
+ * \retval output length if success.
+ * \retval < 0 if error.
+ */
+int knot_rrset_txt_dump_data(const knot_rrset_t *rrset,
+ const size_t pos,
+ char *dst,
+ const size_t maxlen,
+ const knot_dump_style_t *style);
+
+/*!
+ * \brief Dumps rrset, re-allocates dst to double (4x, 8x, ...) if too small.
+ *
+ * \param rrset RRset to dump.
+ * \param dst Output buffer.
+ * \param dst_size Output buffer size (changed if *dst re-allocated).
+ * \param style Output style.
+ *
+ * \retval output length if success.
+ * \retval < 0 if error.
+ */
+int knot_rrset_txt_dump(const knot_rrset_t *rrset,
+ char **dst,
+ size_t *dst_size,
+ const knot_dump_style_t *style);
+
+/*! @} */
diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c
new file mode 100644
index 0000000..110422f
--- /dev/null
+++ b/src/libknot/rrset.c
@@ -0,0 +1,217 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/rrset.h"
+#include "libknot/rrtype/naptr.h"
+#include "libknot/rrtype/rrsig.h"
+#include "contrib/mempattern.h"
+
+_public_
+knot_rrset_t *knot_rrset_new(const knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl, knot_mm_t *mm)
+{
+ knot_dname_t *owner_cpy = knot_dname_copy(owner, mm);
+ if (owner_cpy == NULL) {
+ return NULL;
+ }
+
+ knot_rrset_t *ret = mm_alloc(mm, sizeof(knot_rrset_t));
+ if (ret == NULL) {
+ knot_dname_free(owner_cpy, mm);
+ return NULL;
+ }
+
+ knot_rrset_init(ret, owner_cpy, type, rclass, ttl);
+
+ return ret;
+}
+
+_public_
+knot_rrset_t *knot_rrset_copy(const knot_rrset_t *src, knot_mm_t *mm)
+{
+ if (src == NULL) {
+ return NULL;
+ }
+
+ knot_rrset_t *rrset = knot_rrset_new(src->owner, src->type,
+ src->rclass, src->ttl, mm);
+ if (rrset == NULL) {
+ return NULL;
+ }
+
+ int ret = knot_rdataset_copy(&rrset->rrs, &src->rrs, mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(rrset, mm);
+ return NULL;
+ }
+
+ return rrset;
+}
+
+_public_
+void knot_rrset_free(knot_rrset_t *rrset, knot_mm_t *mm)
+{
+ if (rrset == NULL) {
+ return;
+ }
+
+ knot_rrset_clear(rrset, mm);
+ mm_free(mm, rrset);
+}
+
+_public_
+void knot_rrset_clear(knot_rrset_t *rrset, knot_mm_t *mm)
+{
+ if (rrset == NULL) {
+ return;
+ }
+
+ knot_rdataset_clear(&rrset->rrs, mm);
+ knot_dname_free(rrset->owner, mm);
+ rrset->owner = NULL;
+}
+
+_public_
+int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *data, uint16_t len,
+ knot_mm_t *mm)
+{
+ if (rrset == NULL || (data == NULL && len > 0)) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t buf[knot_rdata_size(len)];
+ knot_rdata_t *rdata = (knot_rdata_t *)buf;
+ knot_rdata_init(rdata, len, data);
+
+ return knot_rdataset_add(&rrset->rrs, rdata, mm);
+}
+
+_public_
+bool knot_rrset_equal(const knot_rrset_t *r1,
+ const knot_rrset_t *r2,
+ bool incl_ttl)
+{
+ if (r1->type != r2->type ||
+ (incl_ttl && r1->ttl != r2->ttl)) {
+ return false;
+ }
+
+ if ((r1->owner != NULL || r2->owner != NULL) &&
+ !knot_dname_is_equal(r1->owner, r2->owner)) {
+ return false;
+ }
+
+ return knot_rdataset_eq(&r1->rrs, &r2->rrs);
+}
+
+_public_
+bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr)
+{
+ if (rr == NULL) {
+ return false;
+ }
+
+ /* Is NSEC3 or non-empty RRSIG covering NSEC3. */
+ return ((rr->type == KNOT_RRTYPE_NSEC3) ||
+ (rr->type == KNOT_RRTYPE_RRSIG
+ && knot_rrsig_type_covered(rr->rrs.rdata) == KNOT_RRTYPE_NSEC3));
+}
+
+_public_
+int knot_rrset_rr_to_canonical(knot_rrset_t *rrset)
+{
+ if (rrset == NULL || rrset->rrs.count != 1) {
+ return KNOT_EINVAL;
+ }
+
+ /* Convert owner for all RRSets. */
+ knot_dname_to_lower(rrset->owner);
+
+ /* Convert DNAMEs in RDATA only for RFC4034 types. */
+ if (!knot_rrtype_should_be_lowercased(rrset->type)) {
+ return KNOT_EOK;
+ }
+
+ const knot_rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type);
+ if (desc->type_name == NULL) {
+ desc = knot_get_obsolete_rdata_descriptor(rrset->type);
+ }
+
+ uint16_t rdlen = rrset->rrs.rdata->len;
+ uint8_t *pos = rrset->rrs.rdata->data;
+ uint8_t *endpos = pos + rdlen;
+
+ /* No RDATA */
+ if (rdlen == 0) {
+ return KNOT_EOK;
+ }
+
+ /* Otherwise, whole and not malformed RDATA are expected. */
+ for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) {
+ int type = desc->block_types[i];
+ switch (type) {
+ case KNOT_RDATA_WF_COMPRESSIBLE_DNAME:
+ case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME:
+ case KNOT_RDATA_WF_FIXED_DNAME:
+ knot_dname_to_lower(pos);
+ pos += knot_dname_size(pos);
+ break;
+ case KNOT_RDATA_WF_NAPTR_HEADER:
+ ; int ret = knot_naptr_header_size(pos, endpos);
+ if (ret < 0) {
+ return ret;
+ }
+
+ pos += ret;
+ break;
+ case KNOT_RDATA_WF_REMAINDER:
+ break;
+ default:
+ /* Fixed size block */
+ assert(type > 0);
+ pos += type;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+size_t knot_rrset_size(const knot_rrset_t *rrset)
+{
+ if (rrset == NULL) {
+ return 0;
+ }
+
+ uint16_t rr_count = rrset->rrs.count;
+
+ size_t total_size = knot_dname_size(rrset->owner) * rr_count;
+
+ knot_rdata_t *rr = rrset->rrs.rdata;
+ for (size_t i = 0; i < rr_count; ++i) {
+ /* 10B = TYPE + CLASS + TTL + RDLENGTH */
+ total_size += rr->len + 10;
+ rr = knot_rdataset_next(rr);
+ }
+
+ return total_size;
+}
diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h
new file mode 100644
index 0000000..fdc5719
--- /dev/null
+++ b/src/libknot/rrset.h
@@ -0,0 +1,194 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief RRSet structure and API for manipulating it.
+ *
+ * \addtogroup rr
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "libknot/dname.h"
+#include "libknot/descriptor.h"
+#include "libknot/mm_ctx.h"
+#include "libknot/rdataset.h"
+
+/*!
+ * \brief Structure for representing RRSet.
+ *
+ * For RRSet definition see RFC2181, Section 5.
+ */
+typedef struct {
+ knot_dname_t *owner; /*!< Domain name being the owner of the RRSet. */
+ uint32_t ttl; /*!< TTL of the RRset. */
+ uint16_t type; /*!< TYPE of the RRset. */
+ uint16_t rclass; /*!< CLASS of the RRSet. */
+ knot_rdataset_t rrs; /*!< RRSet's RRs */
+ /* Optional fields. */
+ void *additional; /*!< Additional records. */
+} knot_rrset_t;
+
+/*!
+ * \brief Creates a new RRSet with the given properties.
+ *
+ * The created RRSet contains no RDATAs (i.e. is actually empty).
+ *
+ * \param owner OWNER of the RRSet.
+ * \param type TYPE of the RRSet.
+ * \param rclass CLASS of the RRSet.
+ * \param ttl TTL of the RRSet.
+ * \param mm Memory context.
+ *
+ * \return New RRSet structure or NULL if an error occurred.
+ */
+knot_rrset_t *knot_rrset_new(const knot_dname_t *owner, uint16_t type,
+ uint16_t rclass, uint32_t ttl, knot_mm_t *mm);
+
+/*!
+ * \brief Initializes RRSet structure with given data.
+ *
+ * \param rrset RRSet to init.
+ * \param owner RRSet owner to use.
+ * \param type RR type to use.
+ * \param rclass Class to use.
+ * \param ttl TTL to use.
+ */
+inline static void knot_rrset_init(knot_rrset_t *rrset, knot_dname_t *owner,
+ uint16_t type, uint16_t rclass, uint32_t ttl)
+{
+ if (rrset != NULL) {
+ rrset->owner = owner;
+ rrset->type = type;
+ rrset->rclass = rclass;
+ rrset->ttl = ttl;
+ knot_rdataset_init(&rrset->rrs);
+ rrset->additional = NULL;
+ }
+}
+
+/*!
+ * \brief Initializes given RRSet structure.
+ *
+ * \param rrset RRSet to init.
+ */
+inline static void knot_rrset_init_empty(knot_rrset_t *rrset)
+{
+ knot_rrset_init(rrset, NULL, 0, KNOT_CLASS_IN, 0);
+}
+
+/*!
+ * \brief Creates new RRSet from \a src RRSet.
+ *
+ * \param src Source RRSet.
+ * \param mm Memory context.
+ *
+ * \retval Pointer to new RRSet if all went OK.
+ * \retval NULL on error.
+ */
+knot_rrset_t *knot_rrset_copy(const knot_rrset_t *src, knot_mm_t *mm);
+
+/*!
+ * \brief Destroys the RRSet structure and all its substructures.
+ *
+ * \param rrset RRset to be destroyed.
+ * \param mm Memory context.
+ */
+void knot_rrset_free(knot_rrset_t *rrset, knot_mm_t *mm);
+
+/*!
+ * \brief Frees structures inside RRSet, but not the RRSet itself.
+ *
+ * \param rrset RRSet to be cleared.
+ * \param mm Memory context used for allocations.
+ */
+void knot_rrset_clear(knot_rrset_t *rrset, knot_mm_t *mm);
+
+/*!
+ * \brief Adds the given RDATA to the RRSet.
+ *
+ * \param rrset RRSet to add the RDATA to.
+ * \param data RDATA to add to the RRSet.
+ * \param len Length of RDATA.
+ * \param mm Memory context.
+ *
+ * \return KNOT_E*
+ */
+int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *data, uint16_t len,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Compares two RRSets for equality.
+ *
+ * \param r1 First RRSet.
+ * \param r2 Second RRSet.
+ * \param incl_ttl Compare also TTLs for equality.
+ *
+ * \retval True if RRSets are equal.
+ * \retval False if RRSets are not equal.
+ */
+bool knot_rrset_equal(const knot_rrset_t *r1, const knot_rrset_t *r2,
+ bool incl_ttl);
+
+/*!
+ * \brief Checks whether RRSet is empty.
+ *
+ * \param rrset RRSet to check.
+ *
+ * \retval True if RRSet is empty.
+ * \retval False if RRSet is not empty.
+ */
+inline static bool knot_rrset_empty(const knot_rrset_t *rrset)
+{
+ return rrset == NULL || rrset->rrs.count == 0;
+}
+
+/*!
+ * \brief Return whether the RR type is NSEC3 related (NSEC3 or RRSIG).
+ */
+bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr);
+
+/*!
+ * \brief Convert one RR into canonical format.
+ *
+ * Owner is always converted to lowercase. RDATA domain names are converted only
+ * for types listed in RFC 4034, Section 6.2, except for NSEC (updated by
+ * RFC 6840, Section 5.1) and A6 (not supported).
+ *
+ * \note If RRSet with more RRs is given to this function, only the first RR
+ * will be converted.
+ * \warning This function expects either empty RDATA or full, not malformed
+ * RDATA. If malformed RRSet is passed to this function, memory errors
+ * may occur.
+ *
+ * \param rrset RR to convert.
+ */
+int knot_rrset_rr_to_canonical(knot_rrset_t *rrset);
+
+/*!
+ * \brief Size of rrset in wire format.
+ *
+ * \retval size in bytes
+ */
+size_t knot_rrset_size(const knot_rrset_t *rrset);
+
+/*! @} */
diff --git a/src/libknot/rrtype/dnskey.h b/src/libknot/rrtype/dnskey.h
new file mode 100644
index 0000000..229b353
--- /dev/null
+++ b/src/libknot/rrtype/dnskey.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+/*! See https://www.iana.org/assignments/dnskey-flags */
+/*! /brief "Secure entry point" marks KSK and CSK in practice. */
+#define KNOT_DNSKEY_FLAG_SEP 1
+/*! /brief The key is ALLOWED to be used for zone contents signing. */
+#define KNOT_DNSKEY_FLAG_ZONE 256
+/*! /brief The key MUST NOT be used for validation. */
+#define KNOT_DNSKEY_FLAG_REVOKE 128
+
+static inline
+uint16_t knot_dnskey_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_dnskey_proto(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_dnskey_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint16_t knot_dnskey_key_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 4;
+}
+
+static inline
+const uint8_t *knot_dnskey_key(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 4;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/ds.h b/src/libknot/rrtype/ds.h
new file mode 100644
index 0000000..cb82e08
--- /dev/null
+++ b/src/libknot/rrtype/ds.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint16_t knot_ds_key_tag(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_ds_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_ds_digest_type(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint16_t knot_ds_digest_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 4;
+}
+
+static inline
+const uint8_t *knot_ds_digest(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 4;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/naptr.c b/src/libknot/rrtype/naptr.c
new file mode 100644
index 0000000..9307816
--- /dev/null
+++ b/src/libknot/rrtype/naptr.c
@@ -0,0 +1,47 @@
+/* Copyright (C) 2015 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 <assert.h>
+#include <stdint.h>
+
+#include "libknot/attribute.h"
+#include "libknot/rrtype/naptr.h"
+#include "contrib/wire_ctx.h"
+
+_public_
+int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp)
+{
+ if (!naptr || !maxp || naptr >= maxp) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(naptr, maxp - naptr);
+
+ /* Fixed fields size (order, preference) */
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+
+ /* Variable fields size (flags, services, regexp) */
+ for (int i = 0; i < 3; i++) {
+ uint8_t size = wire_ctx_read_u8(&wire);
+ wire_ctx_skip(&wire, size);
+ }
+
+ if (wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ return wire_ctx_offset(&wire);
+}
diff --git a/src/libknot/rrtype/naptr.h b/src/libknot/rrtype/naptr.h
new file mode 100644
index 0000000..ab14a9f
--- /dev/null
+++ b/src/libknot/rrtype/naptr.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Counts the size of the NAPTR RDATA before the Replacement domain name.
+ *
+ * See RFC 2915.
+ *
+ * \param naptr Wire format of NAPTR record.
+ * \param maxp Limit of the wire format.
+ *
+ * \retval KNOT_EMALF if the record is malformed.
+ * \retval Size of the RDATA before the Replacement domain name.
+ */
+int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp);
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec.h b/src/libknot/rrtype/nsec.h
new file mode 100644
index 0000000..be1ce31
--- /dev/null
+++ b/src/libknot/rrtype/nsec.h
@@ -0,0 +1,50 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+
+static inline
+const knot_dname_t *knot_nsec_next(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+uint16_t knot_nsec_bitmap_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - knot_dname_size(knot_nsec_next(rdata));
+}
+
+static inline
+const uint8_t *knot_nsec_bitmap(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + knot_dname_size(knot_nsec_next(rdata));
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec3.h b/src/libknot/rrtype/nsec3.h
new file mode 100644
index 0000000..5bb94bd
--- /dev/null
+++ b/src/libknot/rrtype/nsec3.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+/*!
+ * \brief NSEC3 rdata constants.
+ */
+#define KNOT_NSEC3_ALGORITHM_SHA1 1
+#define KNOT_NSEC3_FLAG_OPT_OUT 1
+
+static inline
+uint8_t knot_nsec3_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data);
+}
+
+static inline
+uint8_t knot_nsec3_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 1);
+}
+
+static inline
+uint16_t knot_nsec3_iters(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_nsec3_salt_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+const uint8_t *knot_nsec3_salt(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 5;
+}
+
+static inline
+uint8_t knot_nsec3_next_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 5 + knot_nsec3_salt_len(rdata));
+}
+
+static inline
+const uint8_t *knot_nsec3_next(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6 + knot_nsec3_salt_len(rdata);
+}
+
+static inline
+uint16_t knot_nsec3_bitmap_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 6 - knot_nsec3_salt_len(rdata) - knot_nsec3_next_len(rdata);
+}
+
+static inline
+const uint8_t *knot_nsec3_bitmap(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6 + knot_nsec3_salt_len(rdata) + knot_nsec3_next_len(rdata);
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/nsec3param.h b/src/libknot/rrtype/nsec3param.h
new file mode 100644
index 0000000..43bce18
--- /dev/null
+++ b/src/libknot/rrtype/nsec3param.h
@@ -0,0 +1,64 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint8_t knot_nsec3param_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data);
+}
+
+static inline
+uint8_t knot_nsec3param_flags(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 1);
+}
+
+static inline
+uint16_t knot_nsec3param_iters(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_nsec3param_salt_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+const uint8_t *knot_nsec3param_salt(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 5;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/opt.c b/src/libknot/rrtype/opt.c
new file mode 100644
index 0000000..294689c
--- /dev/null
+++ b/src/libknot/rrtype/opt.c
@@ -0,0 +1,687 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/descriptor.h"
+#include "libknot/lookup.h"
+#include "libknot/packet/pkt.h"
+#include "libknot/rrtype/opt.h"
+#include "contrib/mempattern.h"
+#include "contrib/wire_ctx.h"
+
+/*! \brief Some implementation-related constants. */
+enum {
+ /*! \brief Byte offset of the extended RCODE field in TTL. */
+ EDNS_OFFSET_RCODE = 0,
+ /*! \brief Byte offset of the version field in TTL. */
+ EDNS_OFFSET_VERSION = 1,
+
+ /*! \brief Byte offset of the family field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_FAMILY = 0,
+ /*! \brief Byte offset of the source mask field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_SRC_MASK = 2,
+ /*! \brief Byte offset of the destination mask field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_DST_MASK = 3,
+ /*! \brief Byte offset of the address field in option data. */
+ EDNS_OFFSET_CLIENT_SUBNET_ADDR = 4,
+};
+
+_public_
+int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld,
+ uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm)
+{
+ if (opt_rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Initialize RRSet. */
+ knot_dname_t *owner = knot_dname_copy((const uint8_t *)"", mm);
+ if (owner == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_rrset_init(opt_rr, owner, KNOT_RRTYPE_OPT, max_pld, 0);
+
+ /* Create empty RDATA */
+ int ret = knot_rrset_add_rdata(opt_rr, NULL, 0, mm);
+ if (ret == KNOT_EOK) {
+ knot_edns_set_ext_rcode(opt_rr, ext_rcode);
+ knot_edns_set_version(opt_rr, ver);
+ }
+
+ return ret;
+}
+
+_public_
+uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ wire_ctx_set_offset(&w, EDNS_OFFSET_RCODE);
+ return wire_ctx_read_u8(&w);
+}
+
+static void set_value_to_ttl(knot_rrset_t *opt_rr, size_t offset, uint8_t value)
+{
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ // Set the Extended RCODE in the converted TTL
+ wire_ctx_set_offset(&w, offset);
+ wire_ctx_write_u8(&w, value);
+ // Convert it back to machine byte order
+ wire_ctx_set_offset(&w, 0);
+ uint32_t ttl_local = wire_ctx_read_u32(&w);
+ // Store the TTL to the RDATA
+ opt_rr->ttl = ttl_local;
+}
+
+_public_
+void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ set_value_to_ttl(opt_rr, EDNS_OFFSET_RCODE, ext_rcode);
+}
+
+_public_
+uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ uint32_t ttl = 0;
+ wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl));
+ // TTL is stored in machine byte order. Convert it to wire order first.
+ wire_ctx_write_u32(&w, opt_rr->ttl);
+ wire_ctx_set_offset(&w, EDNS_OFFSET_VERSION);
+ return wire_ctx_read_u8(&w);
+}
+
+_public_
+void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version)
+{
+ assert(opt_rr != NULL);
+ set_value_to_ttl(opt_rr, EDNS_OFFSET_VERSION, version);
+}
+
+/*!
+ * \brief Add new EDNS option by replacing RDATA of OPT RR.
+ *
+ * \param opt OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param size Option data length in bytes.
+ * \param mm Memory context.
+ *
+ * \return Pointer to uninitialized option data.
+ */
+static uint8_t *edns_add(knot_rrset_t *opt, uint16_t code, uint16_t size,
+ knot_mm_t *mm)
+{
+ assert(opt->rrs.count == 1);
+
+ // extract old RDATA
+
+ uint8_t *old_data = opt->rrs.rdata->data;
+ uint16_t old_data_len = opt->rrs.rdata->len;
+
+ // construct new RDATA
+
+ uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + size;
+ uint8_t new_data[new_data_len];
+
+ wire_ctx_t wire = wire_ctx_init(new_data, new_data_len);
+ wire_ctx_write(&wire, old_data, old_data_len);
+ wire_ctx_write_u16(&wire, code);
+ wire_ctx_write_u16(&wire, size);
+
+ // prepare EDNS option data
+
+ size_t offset = wire_ctx_offset(&wire);
+ wire_ctx_clear(&wire, size);
+
+ assert(wire_ctx_available(&wire) == 0);
+ assert(wire.error == KNOT_EOK);
+
+ // replace RDATA
+
+ knot_rdataset_clear(&opt->rrs, mm);
+ if (knot_rrset_add_rdata(opt, new_data, new_data_len, mm) != KNOT_EOK) {
+ return NULL;
+ }
+
+ return opt->rrs.rdata->data + offset;
+}
+
+_public_
+int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm)
+{
+ if (!opt_rr) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = edns_add(opt_rr, code, size, mm);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ if (wire_ptr) {
+ *wire_ptr = wire;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, const uint8_t *data, knot_mm_t *mm)
+{
+ if (!opt_rr || (size > 0 && !data)) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = edns_add(opt_rr, code, size, mm);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ if (size > 0) {
+ memcpy(wire, data, size);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code,
+ const uint8_t *previous)
+{
+ if (opt_rr == NULL) {
+ return NULL;
+ }
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ if (rdata == NULL || rdata->len == 0) {
+ return NULL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+ if (previous != NULL) {
+ if (previous < wire.wire) {
+ return NULL;
+ }
+ wire_ctx_set_offset(&wire, previous - wire.wire + 2);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ }
+
+ while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) {
+ uint8_t *pos = wire.position;
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ if (wire.error == KNOT_EOK && opt_code == code) {
+ return pos;
+ }
+ }
+
+ return NULL;
+}
+
+_public_
+int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out,
+ knot_mm_t *mm)
+{
+ if (opt_rr == NULL || opt_rr->rrs.count > 1 || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ if (rdata == NULL || rdata->len == 0) {
+ return KNOT_EOK;
+ }
+
+ knot_edns_options_t *options = mm_calloc(mm, 1, sizeof(*options));
+
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+
+ while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) {
+ uint8_t *pos = wire.position;
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ wire_ctx_skip(&wire, opt_len);
+ if (wire.error == KNOT_EOK && opt_code <= KNOT_EDNS_MAX_OPTION_CODE) {
+ options->ptr[opt_code] = pos;
+ }
+ }
+
+ if (wire.error != KNOT_EOK) {
+ mm_free(mm, options);
+ return wire.error;
+ }
+
+ *out = options;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_alignment_size(size_t current_pkt_size,
+ size_t current_opt_size,
+ size_t block_size)
+{
+ if (current_opt_size == 0 || block_size == 0) {
+ return -1;
+ }
+
+ size_t current_size = current_pkt_size + current_opt_size;
+ if (current_size % block_size == 0) {
+ return -1;
+ }
+
+ size_t modulo = (current_size + KNOT_EDNS_OPTION_HDRLEN) % block_size;
+
+ return (modulo == 0) ? 0 : block_size - modulo;
+}
+
+/*!
+ * \brief EDNS Client Subnet family data.
+ */
+typedef struct {
+ int platform; //!< Platform family identifier.
+ uint16_t iana; //!< IANA family identifier.
+ size_t offset; //!< Socket address offset.
+ size_t size; //!< Socket address size.
+} ecs_family_t;
+
+#define ECS_INIT(platform, iana, type, member) \
+ { platform, iana, offsetof(type, member), sizeof(((type *)0)->member) }
+
+/*!
+ * \brief Supported EDNS Client Subnet families.
+ *
+ * https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml
+ */
+static const ecs_family_t ECS_FAMILIES[] = {
+ ECS_INIT(AF_INET, KNOT_ADDR_FAMILY_IPV4, struct sockaddr_in, sin_addr),
+ ECS_INIT(AF_INET6, KNOT_ADDR_FAMILY_IPV6, struct sockaddr_in6, sin6_addr),
+ { 0 }
+};
+
+/*!
+ * \brief Lookup ECS family by platform identifier.
+ */
+static const ecs_family_t *ecs_family_by_platform(int family)
+{
+ for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) {
+ if (f->platform == family) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Lookup ECS family by IANA identifier.
+ */
+static const ecs_family_t *ecs_family_by_iana(uint16_t family)
+{
+ for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) {
+ if (f->iana == family) {
+ return f;
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * \brief Get ECS address prefix size in bytes.
+ */
+static uint16_t ecs_prefix_size(uint8_t prefix)
+{
+ return (prefix + 7) / 8;
+}
+
+static uint8_t ecs_prefix_lsb_mask(uint8_t prefix)
+{
+ int modulo = prefix % 8;
+ if (modulo == 0) {
+ return 0xff;
+ } else {
+ return 0xff << (8 - modulo);
+ }
+}
+
+/*!
+ * \brief Write raw network address prefix and clear the rest of the buffer.
+ */
+static void ecs_write_address(wire_ctx_t *dst, wire_ctx_t *src, int8_t prefix)
+{
+ size_t count = ecs_prefix_size(prefix);
+ uint8_t lsb_mask = ecs_prefix_lsb_mask(prefix);
+
+ if (count > 0) {
+ wire_ctx_copy(dst, src, count);
+ if (dst->error != KNOT_EOK) {
+ return;
+ }
+ dst->position[-1] &= lsb_mask;
+ }
+
+ size_t blank = wire_ctx_available(dst);
+ wire_ctx_clear(dst, blank);
+}
+
+/*!
+ * \brief Check if ECS parameters are valid.
+ */
+static bool ecs_is_valid(const knot_edns_client_subnet_t *ecs)
+{
+ if (ecs == NULL) {
+ return false;
+ }
+
+ const ecs_family_t *f = ecs_family_by_iana(ecs->family);
+
+ return f != NULL && // known family check
+ (ecs->source_len <= f->size * 8) && // family address maximum check
+ (ecs->scope_len <= f->size * 8); // family address maximum check
+}
+
+_public_
+uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs)
+{
+ if (!ecs_is_valid(ecs)) {
+ return 0;
+ }
+
+ return sizeof(ecs->family) +
+ sizeof(ecs->source_len) +
+ sizeof(ecs->scope_len) +
+ ecs_prefix_size(ecs->source_len);
+}
+
+_public_
+int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_client_subnet_t *ecs)
+{
+ if (option == NULL || ecs == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (!ecs_is_valid(ecs)) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_t addr = wire_ctx_init_const(ecs->address, sizeof(ecs->address));
+
+ wire_ctx_write_u16(&wire, ecs->family);
+ wire_ctx_write_u8(&wire, ecs->source_len);
+ wire_ctx_write_u8(&wire, ecs->scope_len);
+ ecs_write_address(&wire, &addr, ecs->source_len);
+
+ if (wire.error != KNOT_EOK) {
+ return wire.error;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs,
+ const uint8_t *option, uint16_t option_len)
+{
+ if (ecs == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_edns_client_subnet_t result = { 0 };
+
+ wire_ctx_t wire = wire_ctx_init_const(option, option_len);
+ wire_ctx_t addr = wire_ctx_init(result.address, sizeof(result.address));
+
+ result.family = wire_ctx_read_u16(&wire);
+ result.source_len = wire_ctx_read_u8(&wire);
+ result.scope_len = wire_ctx_read_u8(&wire);
+ ecs_write_address(&addr, &wire, result.source_len);
+
+ if (addr.error != KNOT_EOK || wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+
+ if (!ecs_is_valid(&result)) {
+ return KNOT_EMALF;
+ }
+
+ *ecs = result;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs,
+ const struct sockaddr_storage *addr)
+{
+ if (ecs == NULL || addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const ecs_family_t *f = ecs_family_by_platform(addr->ss_family);
+ if (f == NULL) {
+ return KNOT_ENOTSUP;
+ }
+
+ ecs->family = f->iana;
+ ecs->source_len = f->size * 8;
+ ecs->scope_len = 0;
+
+ wire_ctx_t dst = wire_ctx_init(ecs->address, sizeof(ecs->address));
+ wire_ctx_t src = wire_ctx_init_const((uint8_t *)addr + f->offset, f->size);
+ ecs_write_address(&dst, &src, ecs->source_len);
+
+ assert(dst.error == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr,
+ const knot_edns_client_subnet_t *ecs)
+{
+ if (addr == NULL || ecs == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const ecs_family_t *f = ecs_family_by_iana(ecs->family);
+ if (f == NULL) {
+ return KNOT_ENOTSUP;
+ }
+
+ addr->ss_family = f->platform;
+
+ wire_ctx_t dst = wire_ctx_init((uint8_t *)addr + f->offset, f->size);
+ wire_ctx_t src = wire_ctx_init_const(ecs->address, sizeof(ecs->address));
+ ecs_write_address(&dst, &src, ecs->source_len);
+
+ assert(dst.error == KNOT_EOK);
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_keepalive_size(uint16_t timeout)
+{
+ return (timeout > 0) ? sizeof(uint16_t) : 0;
+}
+
+_public_
+int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout)
+{
+ if (option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (timeout == 0) {
+ return KNOT_EOK;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write_u16(&wire, timeout);
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option,
+ uint16_t option_len)
+{
+ if (timeout == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ *timeout = 0;
+
+ if (option_len > 0) {
+ wire_ctx_t wire = wire_ctx_init_const(option, option_len);
+ *timeout = wire_ctx_read_u16(&wire);
+
+ if (wire.error != KNOT_EOK) {
+ return KNOT_EMALF;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_chain_size(const knot_dname_t *point)
+{
+ return knot_dname_size(point);
+}
+
+_public_
+int knot_edns_chain_write(uint8_t *option, uint16_t option_len,
+ const knot_dname_t *point)
+{
+ if (option == NULL || point == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write(&wire, point, knot_dname_size(point));
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
+ uint16_t option_len, knot_mm_t *mm)
+{
+ if (point == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = knot_dname_wire_check(option, option + option_len, NULL);
+ if (ret <= 0) {
+ return KNOT_EMALF;
+ }
+
+ *point = knot_dname_copy(option, mm);
+ if (*point == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc)
+{
+ if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ return 0;
+ } else if (sc == NULL || sc->len == 0) {
+ return KNOT_EDNS_COOKIE_CLNT_SIZE;
+ } else if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+ return 0;
+ } else {
+ return cc->len + sc->len;
+ }
+}
+
+_public_
+int knot_edns_cookie_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc)
+{
+ if (option == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init(option, option_len);
+ wire_ctx_write(&wire, cc->data, cc->len);
+
+ if (sc != NULL && sc->len > 0) {
+ if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_write(&wire, sc->data, sc->len);
+ }
+
+ return wire.error;
+}
+
+_public_
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+ const uint8_t *option, uint16_t option_len)
+{
+ if (cc == NULL || sc == NULL || option == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (option_len != KNOT_EDNS_COOKIE_CLNT_SIZE &&
+ (option_len < KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE ||
+ option_len > KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE)) {
+ return KNOT_EMALF;
+ }
+ assert(option_len >= KNOT_EDNS_COOKIE_CLNT_SIZE);
+
+ memcpy(cc->data, option, KNOT_EDNS_COOKIE_CLNT_SIZE);
+ cc->len = KNOT_EDNS_COOKIE_CLNT_SIZE;
+
+ size_t sc_len = option_len - KNOT_EDNS_COOKIE_CLNT_SIZE;
+ if (sc_len == 0) {
+ sc->len = 0;
+ } else {
+ memcpy(sc->data, option + KNOT_EDNS_COOKIE_CLNT_SIZE, sc_len);
+ sc->len = sc_len;
+ }
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h
new file mode 100644
index 0000000..bce87c2
--- /dev/null
+++ b/src/libknot/rrtype/opt.h
@@ -0,0 +1,587 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Functions for manipulating the EDNS OPT pseudo-RR.
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <sys/socket.h>
+
+#include "libknot/consts.h"
+#include "libknot/rrset.h"
+#include "libknot/wire.h"
+
+/*! \brief Constants related to EDNS. */
+enum {
+ /*! \brief Supported EDNS version. */
+ KNOT_EDNS_VERSION = 0,
+
+ /*! \brief Bit mask for DO bit. */
+ KNOT_EDNS_DO_MASK = (uint32_t)(1 << 15),
+
+ /*! \brief Minimal UDP payload with EDNS enabled. */
+ KNOT_EDNS_MIN_UDP_PAYLOAD = 512,
+ /*! \brief Minimal payload when using DNSSEC (RFC4035/sec.3). */
+ KNOT_EDNS_MIN_DNSSEC_PAYLOAD = 1220,
+ /*! \brief Maximal UDP payload with EDNS enabled. */
+ KNOT_EDNS_MAX_UDP_PAYLOAD = 4096,
+
+ /*! \brief Minimum size of EDNS OPT RR in wire format. */
+ KNOT_EDNS_MIN_SIZE = 11,
+ /*! \brief Position of the Ext RCODE field in wire format of OPT RR. */
+ KNOT_EDNS_EXT_RCODE_POS = 5,
+ /*! \brief EDNS OPTION header size. */
+ KNOT_EDNS_OPTION_HDRLEN = 4,
+
+ /*! \brief Maximal size of EDNS client subnet address in bytes (IPv6). */
+ KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN = 16,
+
+ /*! \brief Default EDNS alignment size for a query. */
+ KNOT_EDNS_ALIGNMENT_QUERY_DEFAULT = 128,
+ /*! \brief Default EDNS alignment size for a response. */
+ KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT = 468,
+
+ /*! \brief Current EDNS cookie version. */
+ KNOT_EDNS_COOKIE_VERSION = 1,
+ /*! \brief EDNS client cookie size. */
+ KNOT_EDNS_COOKIE_CLNT_SIZE = 8,
+ /*! \brief EDNS minimum server cookie size. */
+ KNOT_EDNS_COOKIE_SRVR_MIN_SIZE = 8,
+ /*! \brief EDNS maximum server cookie size. */
+ KNOT_EDNS_COOKIE_SRVR_MAX_SIZE = 32,
+
+ /*! \brief NSID option code. */
+ KNOT_EDNS_OPTION_NSID = 3,
+ /*! \brief EDNS Client subnet option code. */
+ KNOT_EDNS_OPTION_CLIENT_SUBNET = 8,
+ /*! \brief EDNS Expire option code. */
+ KNOT_EDNS_OPTION_EXPIRE = 9,
+ /*! \brief EDNS DNS Cookie option code. */
+ KNOT_EDNS_OPTION_COOKIE = 10,
+ /*! \brief EDNS TCP Keepalive option code. */
+ KNOT_EDNS_OPTION_TCP_KEEPALIVE = 11,
+ /*! \brief EDNS Padding option code. */
+ KNOT_EDNS_OPTION_PADDING = 12,
+ /*! \brief EDNS Chain query option code. */
+ KNOT_EDNS_OPTION_CHAIN = 13,
+
+ /*! \brief EDNS Extended error code. */
+ KNOT_EDNS_OPTION_EDE = 15,
+ /*! \brief The minimal length for EDE option including option header. */
+ KNOT_EDNS_EDE_MIN_LENGTH = 6,
+
+ /*! \brief Maximal currently known option code. */
+ KNOT_EDNS_MAX_OPTION_CODE = 17,
+};
+
+/* Helpers for splitting extended RCODE. */
+#define KNOT_EDNS_RCODE_HI(rc) ((rc >> 4) & 0x00ff)
+#define KNOT_EDNS_RCODE_LO(rc) (rc & 0x000f)
+
+/*!
+ * \brief Initialize OPT RR.
+ *
+ * \param opt_rr OPT RR to initialize.
+ * \param max_pld Max UDP payload.
+ * \param ext_rcode Extended RCODE.
+ * \param ver Version.
+ * \param mm Memory context.
+ *
+ * \return KNOT_EOK or an error
+ */
+int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld,
+ uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm);
+
+/*!
+ * \brief Returns size of the OPT RR in wire format.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to count the wire size of.
+ *
+ * \return Size of the OPT RR in bytes.
+ */
+static inline
+size_t knot_edns_wire_size(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return KNOT_EDNS_MIN_SIZE + opt_rr->rrs.rdata->len;
+}
+
+/*!
+ * \brief Returns the Max UDP payload value stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to get the value from.
+ *
+ * \return Max UDP payload in bytes.
+ */
+static inline
+uint16_t knot_edns_get_payload(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->rclass;
+}
+
+/*!
+ * \brief Sets the Max UDP payload field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to set the value to.
+ * \param payload UDP payload in bytes.
+ */
+static inline
+void knot_edns_set_payload(knot_rrset_t *opt_rr, uint16_t payload)
+{
+ assert(opt_rr != NULL);
+ opt_rr->rclass = payload;
+}
+
+/*!
+ * \brief Returns the Extended RCODE stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to get the Extended RCODE from.
+ *
+ * \return Extended RCODE.
+ */
+uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr);
+
+/*!
+ * \brief Concatenates OPT RR Extended RCODE field and normal RCODE to get the
+ * whole Extended RCODE.
+ *
+ * Extended RCODE is created by using the Extended RCODE field from OPT RR as
+ * higher 8 bits and the RCODE from DNS Header as the lower 4 bits, resulting
+ * in a 12-bit unsigned integer. (See RFC 6891, Section 6.1.3).
+ *
+ * \param ext_rcode Extended RCODE field from OPT RR.
+ * \param rcode RCODE from DNS Header.
+ *
+ * \return 12-bit Extended RCODE.
+ */
+static inline
+uint16_t knot_edns_whole_rcode(uint8_t ext_rcode, uint8_t rcode)
+{
+ uint16_t high = ext_rcode;
+ return (high << 4) | rcode;
+}
+
+/*!
+ * \brief Sets the Extended RCODE field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to set the Extended RCODE to.
+ * \param ext_rcode Extended RCODE to set.
+ */
+void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode);
+
+/*!
+ * \brief Sets the Extended RCODE field in OPT RR wire.
+ *
+ * \param opt_rr Position of the OPT RR in packet.
+ * \param ext_rcode Higher 8 bits of Extended RCODE.
+ */
+static inline
+void knot_edns_set_ext_rcode_wire(uint8_t *opt_rr, uint8_t ext_rcode)
+{
+ assert(opt_rr != NULL);
+ *(opt_rr + KNOT_EDNS_EXT_RCODE_POS) = ext_rcode;
+}
+
+/*!
+ * \brief Returns the EDNS version stored in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to get the EDNS version from.
+ *
+ * \return EDNS version.
+ */
+uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr);
+
+/*!
+ * \brief Sets the EDNS version field in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ * \note There is an assert() for debug checking of the parameter.
+ *
+ * \param opt_rr OPT RR to set the EDNS version to.
+ * \param version EDNS version to set.
+ */
+void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version);
+
+/*!
+ * \brief Returns the state of the DO bit in the OPT RR flags.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to get the DO bit from.
+ *
+ * \return true if the DO bit is set.
+ * \return false if the DO bit is not set.
+ */
+static inline
+bool knot_edns_do(const knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ return opt_rr->ttl & KNOT_EDNS_DO_MASK;
+}
+
+/*!
+ * \brief Sets the DO bit in the OPT RR.
+ *
+ * \warning This function does not check the parameter, so ensure to check it
+ * before calling the function. It must not be NULL.
+ *
+ * \param opt_rr OPT RR to set the DO bit in.
+ */
+static inline
+void knot_edns_set_do(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+ opt_rr->ttl |= KNOT_EDNS_DO_MASK;
+}
+
+/*!
+ * \brief Add EDNS option into the package with empty (zeroed) content.
+ *
+ * \param[in] opt_rr OPT RR structure to reserve the option in.
+ * \param[in] code Option code.
+ * \param[in] size Desired option size.
+ * \param[out] wire_ptr Pointer to reserved option data (can be NULL).
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm);
+
+/*!
+ * \brief Adds EDNS Option to the OPT RR.
+ *
+ * \note The function now supports adding empty OPTION (just having its code).
+ *
+ * \param opt_rr OPT RR structure to add the Option to.
+ * \param code Option code.
+ * \param size Option data length in bytes.
+ * \param data Option data.
+ * \param mm Memory context.
+ *
+ * \retval KNOT_EOK
+ * \retval KNOT_ENOMEM
+ */
+int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code,
+ uint16_t size, const uint8_t *data, knot_mm_t *mm);
+
+/*!
+ * \brief Searches the OPT RR for option with the specified code.
+ *
+ * \param opt_rr OPT RR structure to search in.
+ * \param code Option code to search for.
+ * \param previous (Optional) Previously returned option to start searching from,
+ * to be able to return another option with the same code.
+ *
+ * \retval pointer to option if found
+ * \retval NULL otherwise.
+ */
+uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code,
+ const uint8_t *previous);
+
+/*!
+ * \brief Pointers to every option in the OPT RR wire.
+ */
+typedef struct {
+ uint8_t *ptr[KNOT_EDNS_MAX_OPTION_CODE + 1];
+} knot_edns_options_t;
+
+/*!
+ * \brief Initializes pointers to options in a given OPT RR.
+ *
+ * \note If the OPT RR has no options, the output is NULL.
+ *
+ * \param opt_rr OPT RR structure to be used.
+ * \param out Structure to be initialized.
+ * \param mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out,
+ knot_mm_t *mm);
+
+/*!
+ * \brief Returns the option code.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \return EDNS option code
+ */
+static inline uint16_t knot_edns_opt_get_code(const uint8_t *opt)
+{
+ assert(opt != NULL);
+ return knot_wire_read_u16(opt);
+}
+
+/*!
+ * \brief Returns the option data length.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \return EDNS option length
+ */
+static inline uint16_t knot_edns_opt_get_length(const uint8_t *opt)
+{
+ assert(opt != NULL);
+ return knot_wire_read_u16(opt + sizeof(uint16_t));
+}
+
+/*!
+ * \brief Returns pointer to option data.
+ *
+ * \warning No safety checks are performed on the supplied data.
+ *
+ * \param opt EDNS option (including code, length and data portion).
+ *
+ * \retval pointer to place where ENDS option data would reside
+ */
+static inline uint8_t *knot_edns_opt_get_data(uint8_t *opt)
+{
+ return opt + KNOT_EDNS_OPTION_HDRLEN;
+}
+
+/*!
+ * \brief Computes additional Padding data length for required packet alignment.
+ *
+ * \param current_pkt_size Current packet size.
+ * \param current_opt_size Current OPT rrset size (OPT must be used).
+ * \param block_size Required packet block length (must be non-zero).
+ *
+ * \return Required padding length or -1 if padding not required.
+ */
+int knot_edns_alignment_size(size_t current_pkt_size,
+ size_t current_opt_size,
+ size_t block_size);
+
+/*!
+ * \brief EDNS Client Subnet content.
+ *
+ * \see draft-ietf-dnsop-edns-client-subnet
+ */
+typedef struct {
+ /*! \brief FAMILY */
+ uint16_t family;
+ /*! \brief SOURCE PREFIX-LENGTH */
+ uint8_t source_len;
+ /*! \brief SCOPE PREFIX-LENGTH */
+ uint8_t scope_len;
+ /*! \brief ADDRESS */
+ uint8_t address[KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN];
+} knot_edns_client_subnet_t;
+
+/*!
+ * \brief Get the wire size of the EDNS Client Subnet option.
+ *
+ * \param ecs EDNS Client Subnet data.
+ *
+ * \return Size of the EDNS option data.
+ */
+uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Write EDNS Client Subnet data from the ECS structure to wire.
+ *
+ * \param option EDNS option data buffer.
+ * \param option_len EDNS option data buffer size.
+ * \param ecs EDNS Client Subnet data.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Parse EDNS Client Subnet data from wire to the ECS structure.
+ *
+ * \param[out] ecs EDNS Client Subnet data.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs,
+ const uint8_t *option, uint16_t option_len);
+
+/*!
+ * \brief Set address to the ECS structure.
+ *
+ * \note It also resets the lengths.
+ *
+ * \param ecs ECS structure to set address into.
+ * \param addr Address to be set.
+ *
+ * \return Error code. KNOT_EOK if successful.
+ */
+int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs,
+ const struct sockaddr_storage *addr);
+
+/*!
+ * \brief Get address from the ECS structure.
+ *
+ * Only the family and raw address is set in the structure. The bits not
+ * covered by the prefix length are cleared.
+ *
+ * \param addr Address to be set.
+ * \param ecs ECS structure to retrieve address from.
+ */
+int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr,
+ const knot_edns_client_subnet_t *ecs);
+
+/*!
+ * \brief Get size of the EDNS Keepalive option wire size.
+ *
+ * \param[in] timeout EDNS TCP Keepalive timeout.
+ *
+ * \return Size of the EDNS option data.
+ */
+uint16_t knot_edns_keepalive_size(uint16_t timeout);
+
+/*!
+ * \brief Writes EDNS TCP Keepalive wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] timeout EDNS TCP Keepalive timeout.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout);
+
+/*!
+ * \brief Parses EDNS TCP Keepalive wire data.
+ *
+ * \param[out] timeout EDNS TCP Keepalive timeout.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option,
+ uint16_t option_len);
+
+/*!
+ * \brief Get size of the EDNS Chain option wire size.
+ *
+ * \param[in] point EDNS Chain closest trusted point.
+ *
+ * \return Size of the EDNS option data or 0 if invalid input.
+ */
+uint16_t knot_edns_chain_size(const knot_dname_t *point);
+
+/*!
+ * \brief Writes EDNS Chain wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] point EDNS Chain closest trusted point.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_chain_write(uint8_t *option, uint16_t option_len,
+ const knot_dname_t *point);
+
+/*!
+ * \brief Parses EDNS Chain wire data.
+ *
+ * \param[out] point EDNS Chain closest trusted point.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ * \param[in] mm Memory context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option,
+ uint16_t option_len, knot_mm_t *mm);
+
+/*!
+ * \brief DNS Cookie content.
+ */
+typedef struct {
+ uint8_t data[KNOT_EDNS_COOKIE_SRVR_MAX_SIZE]; /*!< Cookie data. */
+ uint16_t len; /*!< Cookie length. */
+} knot_edns_cookie_t;
+
+/*!
+ * \brief Get size of the EDNS Cookie option wire size.
+ *
+ * \param[in] cc Client cookie.
+ * \param[in] sc Server cookie (can be NULL).
+ *
+ * \return Size of the EDNS option data or 0 if invalid input.
+ */
+uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Writes EDNS cookie wire data.
+ *
+ * \param[out] option EDNS option data buffer.
+ * \param[in] option_len EDNS option data buffer size.
+ * \param[in] cc EDNS client cookie.
+ * \param[in] sc EDNS server cookie (can be NULL).
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_write(uint8_t *option, uint16_t option_len,
+ const knot_edns_cookie_t *cc,
+ const knot_edns_cookie_t *sc);
+
+/*!
+ * \brief Parses EDNS Cookie wire data.
+ *
+ * \param[out] cc EDNS client cookie.
+ * \param[out] sc EDNS server cookie.
+ * \param[in] option EDNS option data.
+ * \param[in] option_len EDNS option size.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc,
+ const uint8_t *option, uint16_t option_len);
+
+/*! @} */
diff --git a/src/libknot/rrtype/rdname.h b/src/libknot/rrtype/rdname.h
new file mode 100644
index 0000000..d2be870
--- /dev/null
+++ b/src/libknot/rrtype/rdname.h
@@ -0,0 +1,98 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/descriptor.h"
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/rrtype/svcb.h"
+
+static inline
+const knot_dname_t *knot_cname_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_dname_target(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_ns_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_ptr_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_mx_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 2;
+}
+
+static inline
+const knot_dname_t *knot_srv_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6;
+}
+
+static inline
+const knot_dname_t *knot_rdata_name(const knot_rdata_t *rdata, uint16_t type)
+{
+ assert(rdata);
+ switch (type) {
+ case KNOT_RRTYPE_NS:
+ return knot_ns_name(rdata);
+ case KNOT_RRTYPE_PTR:
+ return knot_ptr_name(rdata);
+ case KNOT_RRTYPE_MX:
+ return knot_mx_name(rdata);
+ case KNOT_RRTYPE_SRV:
+ return knot_srv_name(rdata);
+ case KNOT_RRTYPE_CNAME:
+ return knot_cname_name(rdata);
+ case KNOT_RRTYPE_DNAME:
+ return knot_dname_target(rdata);
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ return knot_svcb_target(rdata);
+ }
+
+ return NULL;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/rrsig.h b/src/libknot/rrtype/rrsig.h
new file mode 100644
index 0000000..5a3643d
--- /dev/null
+++ b/src/libknot/rrtype/rrsig.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint16_t knot_rrsig_type_covered(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+uint8_t knot_rrsig_alg(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 2);
+}
+
+static inline
+uint8_t knot_rrsig_labels(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 3);
+}
+
+static inline
+uint32_t knot_rrsig_original_ttl(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 4);
+}
+
+static inline
+uint32_t knot_rrsig_sig_expiration(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 8);
+}
+
+static inline
+uint32_t knot_rrsig_sig_inception(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + 12);
+}
+
+static inline
+uint16_t knot_rrsig_key_tag(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data + 16);
+}
+
+static inline
+const knot_dname_t *knot_rrsig_signer_name(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 18;
+}
+
+static inline
+uint16_t knot_rrsig_signature_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->len - 18 - knot_dname_size(knot_rrsig_signer_name(rdata));
+}
+
+static inline
+const uint8_t *knot_rrsig_signature(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 18 + knot_dname_size(knot_rrsig_signer_name(rdata));
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/soa.h b/src/libknot/rrtype/soa.h
new file mode 100644
index 0000000..9449f00
--- /dev/null
+++ b/src/libknot/rrtype/soa.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+const knot_dname_t *knot_soa_primary(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data;
+}
+
+static inline
+const knot_dname_t *knot_soa_mailbox(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + knot_dname_size(knot_soa_primary(rdata));
+}
+
+static inline
+size_t knot_soa_names_len(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_dname_size(knot_soa_primary(rdata)) +
+ knot_dname_size(knot_soa_mailbox(rdata));
+}
+
+static inline
+uint32_t knot_soa_serial(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata));
+}
+
+static inline
+void knot_soa_serial_set(knot_rdata_t *rdata, uint32_t serial)
+{
+ assert(rdata);
+ knot_wire_write_u32(rdata->data + knot_soa_names_len(rdata), serial);
+}
+
+static inline
+uint32_t knot_soa_refresh(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 4);
+}
+
+static inline
+uint32_t knot_soa_retry(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 8);
+}
+
+static inline
+uint32_t knot_soa_expire(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 12);
+}
+
+static inline
+uint32_t knot_soa_minimum(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 16);
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/svcb.h b/src/libknot/rrtype/svcb.h
new file mode 100644
index 0000000..abee597
--- /dev/null
+++ b/src/libknot/rrtype/svcb.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+static inline
+uint32_t knot_svcb_priority(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u16(rdata->data);
+}
+
+static inline
+const knot_dname_t *knot_svcb_target(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 2;
+}
+
+/*! @} */
diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c
new file mode 100644
index 0000000..83f8436
--- /dev/null
+++ b/src/libknot/rrtype/tsig.c
@@ -0,0 +1,409 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "libdnssec/tsig.h"
+#include "libknot/attribute.h"
+#include "libknot/rrtype/tsig.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+#include "libknot/rrset.h"
+#include "libknot/wire.h"
+#include "contrib/wire_ctx.h"
+
+/*! \brief TSIG field offsets. */
+typedef enum tsig_off_t {
+ TSIG_ALGNAME_O = 0,
+ TSIG_TSIGNED_O,
+ TSIG_FUDGE_O,
+ TSIG_MACLEN_O,
+ TSIG_MAC_O,
+ TSIG_ORIGID_O,
+ TSIG_ERROR_O,
+ TSIG_OLEN_O,
+ TSIG_OTHER_O
+} tsig_off_t;
+
+/* Helpers for RDATA offset calculation. */
+#define TSIG_OFF_MACLEN (4 * sizeof(uint16_t))
+#define TSIG_FIXED_RDLEN (8 * sizeof(uint16_t))
+#define TSIG_OTHER_MAXLEN (3 * sizeof(uint16_t))
+
+/*!
+ * \brief Seek offset of a TSIG RR field.
+ *
+ * \param rr TSIG RR.
+ * \param id Field index.
+ * \param nb Required number of bytes after the offset (for boundaries check).
+ * \return pointer to field on wire or NULL.
+ */
+static uint8_t* rdata_seek(const knot_rrset_t *rr, tsig_off_t id, size_t nb)
+{
+ const knot_rdata_t *rr_data = knot_rdataset_at(&rr->rrs, 0);
+ if (!rr_data || rr_data->len == 0) {
+ return NULL;
+ }
+
+ wire_ctx_t wire = wire_ctx_init_const(rr_data->data, rr_data->len);
+
+ /* TSIG RR names should be already sanitized on parse. */
+ size_t alg_len = knot_dname_size(wire.wire);
+
+ /* Not pretty, but fast. */
+ switch (id) {
+ case TSIG_ALGNAME_O: break;
+ case TSIG_TSIGNED_O:
+ wire_ctx_skip(&wire, alg_len); break;
+ case TSIG_FUDGE_O:
+ wire_ctx_skip(&wire, alg_len + 3 * sizeof(uint16_t));
+ break;
+ case TSIG_MACLEN_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ break;
+ case TSIG_MAC_O:
+ wire_ctx_skip(&wire, alg_len + 5 * sizeof(uint16_t));
+ break;
+ case TSIG_ORIGID_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ break;
+ case TSIG_ERROR_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, sizeof(uint16_t));
+ break;
+ case TSIG_OLEN_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+ break;
+ case TSIG_OTHER_O:
+ wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t));
+ wire_ctx_skip(&wire, wire_ctx_read_u16(&wire));
+ wire_ctx_skip(&wire, 2 * sizeof(uint16_t));
+ assert(nb == 0);
+ nb = wire_ctx_read_u16(&wire);
+ if (wire_ctx_available(&wire) != nb) {
+ return NULL;
+ }
+ break;
+ }
+
+ if (wire.error != KNOT_EOK) {
+ return NULL;
+ }
+
+ /* Check remaining bytes. */
+
+ if (wire_ctx_available(&wire) < nb){
+ return NULL;
+ }
+
+ return wire.position;
+}
+
+static int rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u16(rd, tsig_error);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg,
+ uint16_t maclen, uint16_t tsig_err)
+{
+ if (rr == NULL || alg == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t alg_len = knot_dname_size(alg);
+ size_t rdlen = alg_len + TSIG_FIXED_RDLEN + maclen;
+ if (tsig_err == KNOT_RCODE_BADTIME) {
+ rdlen += TSIG_OTHER_MAXLEN;
+ }
+ uint8_t rd[rdlen];
+ memset(rd, 0, rdlen);
+
+ /* Copy alg name. */
+ knot_dname_to_wire(rd, alg, rdlen);
+
+ /* Set MAC variable length in advance. */
+ size_t offset = alg_len + TSIG_OFF_MACLEN;
+ knot_wire_write_u16(rd + offset, maclen);
+
+ int ret = knot_rrset_add_rdata(rr, rd, rdlen, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Set error. */
+ rdata_set_tsig_error(rr, tsig_err);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u48(rd, time);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ knot_wire_write_u16(rd, fudge);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, length);
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /*! \note Cannot change length, as rdata is already preallocd. */
+
+ /* Copy the actual MAC. */
+ memcpy(rd, mac, length);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /* Write the length - 2. */
+ knot_wire_write_u16(rd, id);
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t len,
+ const uint8_t *other_data)
+{
+ if (len > TSIG_OTHER_MAXLEN) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, len + sizeof(uint16_t));
+ if (!rd) {
+ return KNOT_ERROR;
+ }
+
+ /* Write the length. */
+ knot_wire_write_u16(rd, len);
+
+ /* Copy the actual data. */
+ if (len > 0) {
+ memcpy(rd + sizeof(uint16_t), other_data, len);
+ }
+ return KNOT_EOK;
+}
+
+_public_
+const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig)
+{
+ return knot_rdataset_at(&tsig->rrs, 0)->data;
+}
+
+_public_
+dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig)
+{
+ /* Get the algorithm name. */
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return DNSSEC_TSIG_UNKNOWN;
+ }
+
+ return dnssec_tsig_algorithm_from_dname(alg_name);
+}
+
+_public_
+uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig)
+{
+ /*! \todo How to return invalid value? */
+ uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u48(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, 0);
+ if (!rd) {
+ return NULL;
+ }
+ return rd;
+}
+
+_public_
+size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_MACLEN_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_OTHER_O, 0);
+ if (!rd) {
+ return NULL;
+ }
+ return rd;
+}
+
+_public_
+uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig)
+{
+ uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, sizeof(uint16_t));
+ if (!rd) {
+ return 0;
+ }
+ return knot_wire_read_u16(rd);
+}
+
+_public_
+size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig)
+{
+ if (tsig == NULL) {
+ return 0;
+ }
+ /* Key name, Algorithm name and Other data have variable lengths. */
+ const knot_dname_t *key_name = tsig->owner;
+ if (!key_name) {
+ return 0;
+ }
+
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig);
+ if (!alg_name) {
+ return 0;
+ }
+
+ uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig);
+
+ return knot_dname_size(key_name) + knot_dname_size(alg_name) +
+ other_data_length + KNOT_TSIG_VARIABLES_LENGTH;
+}
+
+_public_
+size_t knot_tsig_rdata_tsig_timers_length(void)
+{
+ /*! \todo Cleanup */
+ return KNOT_TSIG_TIMERS_LENGTH;
+}
+
+_public_
+size_t knot_tsig_wire_size(const knot_tsig_key_t *key)
+{
+ if (key == NULL || key->name == NULL) {
+ return 0;
+ }
+
+ return knot_dname_size(key->name) + TSIG_FIXED_RDLEN +
+ sizeof(uint16_t) + /* TYPE */
+ sizeof(uint16_t) + /* CLASS */
+ sizeof(uint32_t) + /* TTL */
+ sizeof(uint16_t) + /* RDATA length. */
+ knot_dname_size(dnssec_tsig_algorithm_to_dname(key->algorithm)) +
+ dnssec_tsig_algorithm_size(key->algorithm); /* MAC length. */
+}
+
+_public_
+size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key)
+{
+ size_t size = knot_tsig_wire_size(key);
+ if (size == 0) {
+ return 0;
+ }
+
+ /* In case of BADTIME other data. */
+ return size + TSIG_OTHER_MAXLEN;
+}
+
+_public_
+bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig)
+{
+ return (tsig != NULL
+ && knot_rdataset_at(&tsig->rrs, 0) != NULL
+ && rdata_seek(tsig, TSIG_OTHER_O, 0) != NULL
+ && knot_tsig_rdata_alg_name(tsig) != NULL);
+}
diff --git a/src/libknot/rrtype/tsig.h b/src/libknot/rrtype/tsig.h
new file mode 100644
index 0000000..083a063
--- /dev/null
+++ b/src/libknot/rrtype/tsig.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TSIG manipulation.
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libdnssec/binary.h"
+#include "libdnssec/tsig.h"
+#include "libknot/consts.h"
+#include "libknot/rrset.h"
+#include "libknot/tsig.h"
+
+enum tsig_consts {
+ KNOT_TSIG_ITEM_COUNT = 7,
+ KNOT_TSIG_VARIABLES_LENGTH = sizeof(uint16_t) // class
+ + sizeof(uint32_t) // ttl
+ + 6 // time signed
+ + sizeof(uint16_t) // fudge
+ + sizeof(uint16_t) // error
+ + sizeof(uint16_t),// other data length
+ KNOT_TSIG_TIMERS_LENGTH = sizeof(uint16_t) //fudge
+ + 6 // time signed
+};
+
+/*!
+ * \brief Create TSIG RDATA.
+ *
+ * \param rr TSIG RR to contain created data.
+ * \param alg Algorithm name.
+ * \param maclen Algorithm MAC len (may be set to 0 for empty MAC).
+ * \param tsig_err TSIG error code.
+ *
+ * \retval KNOT_EINVAL
+ * \retval KNOT_EOK
+ */
+int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg,
+ uint16_t maclen, uint16_t tsig_err);
+
+int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time);
+
+int knot_tsig_rdata_store_current_time(knot_rrset_t *tsig);
+
+int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge);
+
+int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac);
+
+int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id);
+
+int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length,
+ const uint8_t *other_data);
+
+const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig);
+
+dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig);
+
+uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig);
+
+const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig);
+
+const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig);
+
+uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig);
+
+size_t knot_tsig_rdata_tsig_timers_length(void);
+
+/*!
+ * \brief Return standard TSIG RRSET wire size for given algorithm.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t knot_tsig_wire_size(const knot_tsig_key_t *key);
+
+/*!
+ * \brief Return TSIG RRSET maximum wire size for given algorithm.
+ *
+ * This size is reached if error reply with BADTIME.
+ *
+ * \param key Signing key descriptor.
+ *
+ * \return RRSET wire size.
+ */
+size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key);
+
+/*! \todo Documentation. */
+bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig);
+
+/*! @} */
diff --git a/src/libknot/rrtype/zonemd.h b/src/libknot/rrtype/zonemd.h
new file mode 100644
index 0000000..a4d734b
--- /dev/null
+++ b/src/libknot/rrtype/zonemd.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \addtogroup rrtype
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/rdata.h"
+#include "libknot/wire.h"
+
+#define KNOT_ZONEMD_SCHEME_SIMPLE 1
+#define KNOT_ZONEMD_ALGORITHM_SHA384 1
+#define KNOT_ZONEMD_ALGORITHM_SHA512 2
+
+static inline
+uint32_t knot_zonemd_soa_serial(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return knot_wire_read_u32(rdata->data);
+}
+
+static inline
+uint8_t knot_zonemd_scheme(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 4);
+}
+
+static inline
+uint8_t knot_zonemd_algorithm(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return *(rdata->data + 5);
+}
+
+static inline
+size_t knot_zonemd_digest_size(const knot_rdata_t *rdata)
+{
+ switch (knot_zonemd_algorithm(rdata)) {
+ case KNOT_ZONEMD_ALGORITHM_SHA384: return 48;
+ case KNOT_ZONEMD_ALGORITHM_SHA512: return 64;
+ default: return 0;
+ }
+}
+
+static inline
+const uint8_t *knot_zonemd_digest(const knot_rdata_t *rdata)
+{
+ assert(rdata);
+ return rdata->data + 6;
+}
+
+/*! @} */
diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c
new file mode 100644
index 0000000..7913859
--- /dev/null
+++ b/src/libknot/tsig-op.c
@@ -0,0 +1,683 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <inttypes.h>
+#include <time.h>
+#include <stdint.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/tsig.h"
+#include "libknot/attribute.h"
+#include "libknot/tsig-op.h"
+#include "libknot/errcode.h"
+#include "libknot/descriptor.h"
+#include "libknot/rrtype/tsig.h"
+#include "libknot/packet/wire.h"
+#include "libknot/consts.h"
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/wire.h"
+#include "contrib/string.h"
+
+const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest
+const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300; // default Fudge value
+
+static int check_algorithm(const knot_rrset_t *tsig_rr)
+{
+ if (tsig_rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ return KNOT_EMALF;
+ }
+
+ dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name);
+ if (alg == DNSSEC_TSIG_UNKNOWN) {
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_key(const knot_rrset_t *tsig_rr, const knot_tsig_key_t *tsig_key)
+{
+ if (tsig_rr == NULL || tsig_key == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const knot_dname_t *tsig_name = tsig_rr->owner;
+ if (!tsig_name) {
+ return KNOT_EMALF;
+ }
+
+ if (!knot_dname_is_equal(tsig_name, tsig_key->name)) {
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ return KNOT_EOK;
+}
+
+static int compute_digest(const uint8_t *wire, size_t wire_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_tsig_key_t *key)
+{
+ if (!wire || !digest || !digest_len || !key) {
+ return KNOT_EINVAL;
+ }
+
+ if (!key->name) {
+ return KNOT_EMALF;
+ }
+
+ dnssec_tsig_ctx_t *ctx = NULL;
+ int result = dnssec_tsig_new(&ctx, key->algorithm, &key->secret);
+ if (result != DNSSEC_EOK) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ dnssec_binary_t cover = { .data = (uint8_t *)wire, .size = wire_len };
+ dnssec_tsig_add(ctx, &cover);
+
+ *digest_len = dnssec_tsig_size(ctx);
+ dnssec_tsig_write(ctx, digest);
+ dnssec_tsig_free(ctx);
+
+ return KNOT_EOK;
+}
+
+static int check_time_signed(const knot_rrset_t *tsig_rr, uint64_t prev_time_signed)
+{
+ if (!tsig_rr) {
+ return KNOT_EINVAL;
+ }
+
+ /* Get the time signed and fudge values. */
+ uint64_t time_signed = knot_tsig_rdata_time_signed(tsig_rr);
+ if (time_signed == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+ uint16_t fudge = knot_tsig_rdata_fudge(tsig_rr);
+ if (fudge == 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ /* Get the current time. */
+ time_t curr_time = time(NULL);
+
+ /*!< \todo bleeding eyes. */
+ double diff = difftime(curr_time, (time_t)time_signed);
+
+ if (diff > fudge || diff < -fudge) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ diff = difftime((time_t)time_signed, prev_time_signed);
+
+ if (diff < 0) {
+ return KNOT_TSIG_EBADTIME;
+ }
+
+ return KNOT_EOK;
+}
+
+static int write_tsig_variables(uint8_t *wire, const knot_rrset_t *tsig_rr)
+{
+ if (wire == NULL || tsig_rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Copy TSIG variables - starting with key name. */
+ const knot_dname_t *tsig_owner = tsig_rr->owner;
+ if (!tsig_owner) {
+ return KNOT_EINVAL;
+ }
+
+ int offset = 0;
+
+ offset += knot_dname_to_wire(wire + offset, tsig_owner, KNOT_DNAME_MAXLEN);
+
+ /*!< \todo which order? */
+
+ /* Copy class. */
+ knot_wire_write_u16(wire + offset, tsig_rr->rclass);
+ offset += sizeof(uint16_t);
+
+ /* Copy TTL - always 0. */
+ knot_wire_write_u32(wire + offset, tsig_rr->ttl);
+ offset += sizeof(uint32_t);
+
+ /* Copy alg name. */
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
+ if (!alg_name) {
+ return KNOT_EINVAL;
+ }
+
+ /* Te algorithm name must be in canonical form, i.e. in lowercase. */
+ uint8_t *alg_name_wire = wire + offset;
+ offset += knot_dname_to_wire(alg_name_wire, alg_name, KNOT_DNAME_MAXLEN);
+ knot_dname_to_lower(alg_name_wire);
+
+ /* Following data are written in network order. */
+ /* Time signed. */
+ knot_wire_write_u48(wire + offset, knot_tsig_rdata_time_signed(tsig_rr));
+ offset += 6;
+ /* Fudge. */
+ knot_wire_write_u16(wire + offset, knot_tsig_rdata_fudge(tsig_rr));
+ offset += sizeof(uint16_t);
+ /* TSIG error. */
+ knot_wire_write_u16(wire + offset, knot_tsig_rdata_error(tsig_rr));
+ offset += sizeof(uint16_t);
+ /* Get other data length. */
+ uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig_rr);
+ /* Get other data. */
+ const uint8_t *other_data = knot_tsig_rdata_other_data(tsig_rr);
+ if (!other_data) {
+ return KNOT_EINVAL;
+ }
+
+ /*
+ * We cannot write the whole other_data, as it contains its length in
+ * machine order.
+ */
+ knot_wire_write_u16(wire + offset, other_data_length);
+ offset += sizeof(uint16_t);
+
+ /* Skip the length. */
+ memcpy(wire + offset, other_data, other_data_length);
+
+ return KNOT_EOK;
+}
+
+static int wire_write_timers(uint8_t *wire, const knot_rrset_t *tsig_rr)
+{
+ if (wire == NULL || tsig_rr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ //write time signed
+ knot_wire_write_u48(wire, knot_tsig_rdata_time_signed(tsig_rr));
+ //write fudge
+ knot_wire_write_u16(wire + 6, knot_tsig_rdata_fudge(tsig_rr));
+
+ return KNOT_EOK;
+}
+
+static int create_sign_wire(const uint8_t *msg, size_t msg_len,
+ const uint8_t *request_mac, size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_tsig_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig variables.
+ */
+ size_t wire_len = msg_len + request_mac_len + (request_mac_len > 0 ? 2 : 0) +
+ knot_tsig_rdata_tsig_variables_length(tmp_tsig);
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ uint8_t *pos = wire;
+
+ /* Copy the request MAC - should work even if NULL. */
+ if (request_mac_len > 0) {
+ knot_wire_write_u16(pos, request_mac_len);
+ pos += 2;
+ memcpy(pos, request_mac, request_mac_len);
+ }
+ pos += request_mac_len;
+ /* Copy the original message. */
+ memcpy(pos, msg, msg_len);
+ pos += msg_len;
+ /* Copy TSIG variables. */
+ ret = write_tsig_variables(pos, tmp_tsig);
+ if (ret != KNOT_EOK) {
+ free(wire);
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = compute_digest(wire, wire_len, digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ free(wire);
+ return ret;
+ }
+
+ free(wire);
+
+ return KNOT_EOK;
+}
+
+static int create_sign_wire_next(const uint8_t *msg, size_t msg_len,
+ const uint8_t *prev_mac, size_t prev_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_rrset_t *tmp_tsig,
+ const knot_tsig_key_t *key)
+{
+ if (!msg || !key || digest_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Create tmp TSIG. */
+ int ret = KNOT_EOK;
+
+ /*
+ * Create tmp wire, it should contain message
+ * plus request mac plus tsig variables.
+ */
+ size_t wire_len = msg_len + prev_mac_len + knot_tsig_rdata_tsig_timers_length() + 2;
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ return KNOT_ENOMEM;
+ }
+
+ memset(wire, 0, wire_len);
+
+ /* Copy the request MAC - should work even if NULL. */
+ knot_wire_write_u16(wire, prev_mac_len);
+ memcpy(wire + 2, prev_mac, prev_mac_len);
+ /* Copy the original message. */
+ memcpy(wire + prev_mac_len + 2, msg, msg_len);
+ /* Copy TSIG variables. */
+
+ ret = wire_write_timers(wire + prev_mac_len + msg_len + 2, tmp_tsig);
+ if (ret != KNOT_EOK) {
+ free(wire);
+ return ret;
+ }
+
+ /* Compute digest. */
+ ret = compute_digest(wire, wire_len, digest, digest_len, key);
+ if (ret != KNOT_EOK) {
+ *digest_len = 0;
+ free(wire);
+ return ret;
+ }
+
+ free(wire);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *request_mac, size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_tsig_key_t *key, uint16_t tsig_rcode,
+ uint64_t request_time_signed)
+{
+ if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG,
+ KNOT_CLASS_ANY, 0, NULL);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ uint16_t rdata_rcode = KNOT_RCODE_NOERROR;
+ if (tsig_rcode == KNOT_RCODE_BADTIME) {
+ rdata_rcode = tsig_rcode;
+ }
+
+ const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm);
+ size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm);
+ knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, rdata_rcode);
+
+ /* Distinguish BADTIME response. */
+ if (tsig_rcode == KNOT_RCODE_BADTIME) {
+ /* Set client's time signed into the time signed field. */
+ knot_tsig_rdata_set_time_signed(tmp_tsig, request_time_signed);
+
+ /* Store current time into Other data. */
+ uint8_t time_signed[6];
+ time_t now = time(NULL);
+ knot_wire_write_u48(time_signed, now);
+
+ knot_tsig_rdata_set_other_data(tmp_tsig, 6, time_signed);
+ } else {
+ knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL));
+
+ /* Set other len. */
+ knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+ }
+
+ knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
+
+ /* Set original ID */
+ knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+
+ int ret = create_sign_wire(msg, *msg_len, /*msg_max_len,*/
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len, tmp_tsig, key);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(tmp_tsig, NULL);
+ return ret;
+ }
+
+ /* Set the digest. */
+ knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ /* Write RRSet to wire */
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ msg_max_len - *msg_len, NULL);
+ if (ret < 0) {
+ *digest_len = 0;
+ knot_rrset_free(tmp_tsig, NULL);
+ return ret;
+ }
+
+ size_t tsig_wire_len = ret;
+
+ knot_rrset_free(tmp_tsig, NULL);
+
+ *msg_len += tsig_wire_len;
+
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ /* everything went ok, save the digest to the output parameter */
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_tsig_key_t *key, uint8_t *to_sign,
+ size_t to_sign_len)
+{
+ if (!msg || !msg_len || !key || !digest || !digest_len) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+ knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG,
+ KNOT_CLASS_ANY, 0, NULL);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ /* Create rdata for TSIG RR. */
+ const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm);
+ size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm);
+ knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, 0);
+ knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL));
+ knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
+
+ /* Create wire to be signed. */
+ size_t wire_len = prev_digest_len + to_sign_len + KNOT_TSIG_TIMERS_LENGTH + 2;
+ uint8_t *wire = malloc(wire_len);
+ if (!wire) {
+ knot_rrset_free(tmp_tsig, NULL);
+ return KNOT_ENOMEM;
+ }
+ memset(wire, 0, wire_len);
+
+ /* Write previous digest length. */
+ knot_wire_write_u16(wire, prev_digest_len);
+ /* Write previous digest. */
+ memcpy(wire + 2, prev_digest, prev_digest_len);
+ /* Write original message. */
+ memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len);
+ /* Write timers. */
+ wire_write_timers(wire + prev_digest_len + to_sign_len + 2, tmp_tsig);
+
+ int ret = compute_digest(wire, wire_len, digest_tmp, &digest_tmp_len, key);
+ free(wire);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(tmp_tsig, NULL);
+ *digest_len = 0;
+ return ret;
+ }
+
+ if (digest_tmp_len > *digest_len) {
+ knot_rrset_free(tmp_tsig, NULL);
+ *digest_len = 0;
+ return KNOT_ESPACE;
+ }
+
+ /* Set the MAC. */
+ knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp);
+
+ /* Set original id. */
+ knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set other data. */
+ knot_tsig_rdata_set_other_data(tmp_tsig, 0, NULL);
+
+ ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len,
+ msg_max_len - *msg_len, NULL);
+ if (ret < 0) {
+ knot_rrset_free(tmp_tsig, NULL);
+ *digest_len = 0;
+ return ret;
+ }
+
+ size_t tsig_wire_size = ret;
+
+ knot_rrset_free(tmp_tsig, NULL);
+
+ *msg_len += tsig_wire_size;
+ uint16_t arcount = knot_wire_get_arcount(msg);
+ knot_wire_set_arcount(msg, ++arcount);
+
+ memcpy(digest, digest_tmp, digest_tmp_len);
+ *digest_len = digest_tmp_len;
+
+ return KNOT_EOK;
+}
+
+static int check_digest(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_tsig_key_t *tsig_key,
+ uint64_t prev_time_signed, int use_times)
+{
+ if (!wire || !tsig_key) {
+ return KNOT_EINVAL;
+ }
+
+ /* No TSIG record means verification failure. */
+ if (tsig_rr == NULL) {
+ return KNOT_TSIG_EBADKEY;
+ }
+
+ /* Check that libknot knows the algorithm. */
+ int ret = check_algorithm(tsig_rr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check that key is valid, ie. the same as given in args. */
+ ret = check_key(tsig_rr, tsig_key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint8_t *wire_to_sign = malloc(size);
+ if (!wire_to_sign) {
+ return KNOT_ENOMEM;
+ }
+
+ memcpy(wire_to_sign, wire, size);
+
+ // restore message ID to which the signature had been created with
+ knot_wire_set_id(wire_to_sign, knot_tsig_rdata_orig_id(tsig_rr));
+
+ uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE];
+ size_t digest_tmp_len = 0;
+ assert(tsig_rr->rrs.count > 0);
+
+ if (use_times) {
+ /* Wire is not a single packet, TSIG RRs must be stripped already. */
+ ret = create_sign_wire_next(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ } else {
+ ret = create_sign_wire(wire_to_sign, size,
+ request_mac, request_mac_len,
+ digest_tmp, &digest_tmp_len,
+ tsig_rr, tsig_key);
+ }
+
+ assert(tsig_rr->rrs.count > 0);
+ free(wire_to_sign);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Compare MAC from TSIG RR RDATA with just computed digest. */
+
+ /*!< \todo move to function. */
+ const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr);
+ dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name);
+
+ /*! \todo [TSIG] TRUNCATION */
+ uint16_t mac_length = knot_tsig_rdata_mac_length(tsig_rr);
+ const uint8_t *tsig_mac = knot_tsig_rdata_mac(tsig_rr);
+
+ if (mac_length != dnssec_tsig_algorithm_size(alg)) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ if (const_time_memcmp(tsig_mac, digest_tmp, mac_length) != 0) {
+ return KNOT_TSIG_EBADSIG;
+ }
+
+ /* Check TSIG validity period, must be after the signature check! */
+ ret = check_time_signed(tsig_rr, prev_time_signed);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_tsig_key_t *tsig_key)
+{
+ return check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0, 0);
+}
+
+_public_
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_tsig_key_t *tsig_key,
+ uint64_t prev_time_signed)
+{
+ return check_digest(tsig_rr, wire, size, request_mac, request_mac_len,
+ tsig_key, prev_time_signed, 0);
+}
+
+_public_
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_tsig_key_t *tsig_key,
+ uint64_t prev_time_signed)
+{
+ return check_digest(tsig_rr, wire, size, prev_digest,
+ prev_digest_len, tsig_key, prev_time_signed, 1);
+}
+
+_public_
+int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ uint16_t tsig_rcode, const knot_rrset_t *tsig_rr)
+{
+ /*! \todo Revise!! */
+
+ if (!msg || !msg_len || !tsig_rr) {
+ return KNOT_EINVAL;
+ }
+
+ /*! \todo What key to use, when we do not sign? Does this even work? */
+ knot_rrset_t *tmp_tsig = knot_rrset_new(tsig_rr->owner, KNOT_RRTYPE_TSIG,
+ KNOT_CLASS_ANY, 0, NULL);
+ if (!tmp_tsig) {
+ return KNOT_ENOMEM;
+ }
+
+ assert(tsig_rcode != KNOT_RCODE_BADTIME);
+ knot_tsig_create_rdata(tmp_tsig, knot_tsig_rdata_alg_name(tsig_rr), 0, tsig_rcode);
+ knot_tsig_rdata_set_time_signed(tmp_tsig, knot_tsig_rdata_time_signed(tsig_rr));
+
+ /* Comparing to BIND it was found out that the Fudge should always be
+ * set to the server's value.
+ */
+ knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT);
+
+ /* Set original ID */
+ knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg));
+
+ /* Set other len. */
+ knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0);
+
+ /* Append TSIG RR. */
+ int ret = knot_tsig_append(msg, msg_len, msg_max_len, tmp_tsig);
+
+ /* key_name already referenced in RRSet, no need to free separately. */
+ knot_rrset_free(tmp_tsig, NULL);
+
+ return ret;
+}
+
+_public_
+int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const knot_rrset_t *tsig_rr)
+{
+ /* Write RRSet to wire */
+ int ret = knot_rrset_to_wire(tsig_rr, msg + *msg_len,
+ msg_max_len - *msg_len, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ *msg_len += ret;
+
+ knot_wire_set_arcount(msg, knot_wire_get_arcount(msg) + 1);
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h
new file mode 100644
index 0000000..03e744d
--- /dev/null
+++ b/src/libknot/tsig-op.h
@@ -0,0 +1,187 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TSIG signing and validating.
+ *
+ * \addtogroup knot-tsig
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libknot/rrtype/tsig.h"
+#include "libknot/rrset.h"
+
+/*!
+ * \brief Generate TSIG signature of a message.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows sending the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param key TSIG used for signing.
+ * \param tsig_rcode RCODE of the TSIG.
+ * \param request_time_signed Clients time signed.
+ *
+ * \retval KNOT_EOK if everything went OK.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *request_mac, size_t request_mac_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_tsig_key_t *key, uint16_t tsig_rcode,
+ uint64_t request_time_signed);
+
+/*!
+ * \brief Generate TSIG signature of a 2nd or later message in a TCP session.
+ *
+ * This function generates TSIG digest of the given message prepended with the
+ * given Request MAC (if any) and appended with TSIG Variables. It also appends
+ * the resulting TSIG RR to the message wire format and accordingly adjusts
+ * the message size.
+ *
+ * \note This function does not save the new digest to the 'digest' parameter
+ * unless everything went OK. This allows sending the same buffer to
+ * the 'request_mac' and 'digest' parameters.
+ *
+ * \param msg Message to be signed.
+ * \param msg_len Size of the message in bytes.
+ * \param msg_max_len Maximum size of the message in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ * \param digest Buffer to save the digest in.
+ * \param digest_len In: size of the buffer. Out: real size of the digest saved.
+ * \param key TSIG key for signing.
+ * \param to_sign Data being signed.
+ * \param to_sign_len Size of the data being signed.
+ *
+ * \retval KNOT_EOK if successful.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const uint8_t *prev_digest, size_t prev_digest_len,
+ uint8_t *digest, size_t *digest_len,
+ const knot_tsig_key_t *key, uint8_t *to_sign,
+ size_t to_sign_len);
+
+/*!
+ * \brief Checks incoming request.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param tsig_key TSIG key.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_server_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const knot_tsig_key_t *tsig_key);
+
+/*!
+ * \brief Checks incoming response.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param request_mac Request MAC. (may be NULL).
+ * \param request_mac_len Size of the request MAC in bytes.
+ * \param key TSIG key.
+ * \param prev_time_signed Time for TSIG period validity.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *request_mac, size_t request_mac_len,
+ const knot_tsig_key_t *key,
+ uint64_t prev_time_signed);
+
+/*!
+ * \brief Checks signature of 2nd or next packet in a TCP session.
+ *
+ * \param tsig_rr TSIG extracted from the packet.
+ * \param wire Wire format of the packet (including the TSIG RR).
+ * \param size Size of the wire format of packet in bytes.
+ * \param prev_digest Previous digest sent by the server in the session.
+ * \param prev_digest_len Size of the previous digest in bytes.
+ * \param key TSIG key.
+ * \param prev_time_signed Time for TSIG period validity.
+ *
+ * \retval KNOT_EOK If the signature is valid.
+ * \retval TODO
+ *
+ * \todo This function should return TSIG errors by their codes which are
+ * positive values - this will be recognized by the caller.
+ */
+int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr,
+ const uint8_t *wire, size_t size,
+ const uint8_t *prev_digest,
+ size_t prev_digest_len,
+ const knot_tsig_key_t *key,
+ uint64_t prev_time_signed);
+
+/*!
+ * \todo Documentation!
+ */
+int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ uint16_t tsig_rcode, const knot_rrset_t *tsig_rr);
+
+/*! \brief Append TSIG RR to message.
+ * \todo Proper documentation.
+ */
+int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len,
+ const knot_rrset_t *tsig_rr);
+
+/*! \brief Return true if the TSIG RCODE allows signing the packet.
+ * \todo Proper documentation.
+ */
+static inline bool knot_tsig_can_sign(uint16_t tsig_rcode) {
+ return tsig_rcode == KNOT_RCODE_NOERROR || tsig_rcode == KNOT_RCODE_BADTIME;
+}
+
+/*! @} */
diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c
new file mode 100644
index 0000000..35311a2
--- /dev/null
+++ b/src/libknot/tsig.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <string.h>
+
+#include "contrib/getline.h"
+#include "contrib/string.h"
+#include "libdnssec/error.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "libknot/tsig.h"
+
+_public_
+void knot_tsig_key_deinit(knot_tsig_key_t *key)
+{
+ if (!key) {
+ return;
+ }
+
+ knot_dname_free(key->name, NULL);
+
+ if (key->secret.data) {
+ memzero(key->secret.data, key->secret.size);
+ }
+ dnssec_binary_free(&key->secret);
+
+ memzero(key, sizeof(*key));
+}
+
+_public_
+int knot_tsig_key_init(knot_tsig_key_t *key, const char *algorithm_name,
+ const char *name, const char *secret_b64)
+{
+ if (!name || !secret_b64 || !key) {
+ return KNOT_EINVAL;
+ }
+
+ dnssec_tsig_algorithm_t algorithm = DNSSEC_TSIG_HMAC_SHA256;
+ if (algorithm_name != NULL) {
+ algorithm = dnssec_tsig_algorithm_from_name(algorithm_name);
+ if (algorithm == DNSSEC_TSIG_UNKNOWN) {
+ return KNOT_EMALF;
+ }
+ }
+
+ knot_dname_t *dname = knot_dname_from_str_alloc(name);
+ if (!dname) {
+ return KNOT_ENOMEM;
+ }
+ knot_dname_to_lower(dname);
+
+ dnssec_binary_t b64secret = { 0 };
+ b64secret.data = (uint8_t *)secret_b64;
+ b64secret.size = strlen(secret_b64);
+
+ dnssec_binary_t secret = { 0 };
+ int result = dnssec_binary_from_base64(&b64secret, &secret);
+ if (result != KNOT_EOK) {
+ knot_dname_free(dname, NULL);
+ return result;
+ }
+
+ key->name = dname;
+ key->algorithm = algorithm;
+ key->secret = secret;
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tsig_key_init_str(knot_tsig_key_t *key, const char *params)
+{
+ if (!params) {
+ return KNOT_EINVAL;
+ }
+
+ char *copy = strstrip(params);
+ if (!copy) {
+ return KNOT_ENOMEM;
+ }
+
+ size_t copy_size = strlen(copy) + 1;
+
+ // format [algorithm:]name:secret
+
+ char *algorithm = NULL;
+ char *name = NULL;
+ char *secret = NULL;
+
+ // find secret
+
+ char *pos = strrchr(copy, ':');
+ if (pos) {
+ *pos = '\0';
+ secret = pos + 1;
+ } else {
+ memzero(copy, copy_size);
+ free(copy);
+ return KNOT_EMALF;
+ }
+
+ // find name and optionally algorithm
+
+ pos = strchr(copy, ':');
+ if (pos) {
+ *pos = '\0';
+ algorithm = copy;
+ name = pos + 1;
+ } else {
+ name = copy;
+ }
+
+ int result = knot_tsig_key_init(key, algorithm, name, secret);
+
+ memzero(copy, copy_size);
+ free(copy);
+
+ return result;
+}
+
+_public_
+int knot_tsig_key_init_file(knot_tsig_key_t *key, const char *filename)
+{
+ if (!filename) {
+ return KNOT_EINVAL;
+ }
+
+ FILE *file = fopen(filename, "r");
+ if (!file) {
+ return KNOT_EACCES;
+ }
+
+ char *line = NULL;
+ size_t line_size = 0;
+ ssize_t read = knot_getline(&line, &line_size, file);
+
+ fclose(file);
+
+ if (read == -1) {
+ return KNOT_EMALF;
+ }
+
+ int result = knot_tsig_key_init_str(key, line);
+
+ memzero(line, line_size);
+ free(line);
+
+ return result;
+}
+
+_public_
+int knot_tsig_key_copy(knot_tsig_key_t *dst, const knot_tsig_key_t *src)
+{
+ if (!src || !dst) {
+ return KNOT_EINVAL;
+ }
+
+ knot_tsig_key_t copy = { 0 };
+ copy.algorithm = src->algorithm;
+
+ copy.name = knot_dname_copy(src->name, NULL);
+ if (!copy.name) {
+ return KNOT_ENOMEM;
+ }
+
+ if (dnssec_binary_dup(&src->secret, &copy.secret) != DNSSEC_EOK) {
+ knot_tsig_key_deinit(&copy);
+ return KNOT_ENOMEM;
+ }
+
+ *dst = copy;
+
+ return KNOT_EOK;
+}
diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h
new file mode 100644
index 0000000..abbdde2
--- /dev/null
+++ b/src/libknot/tsig.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TSIG operations
+ *
+ * \addtogroup knot-tsig
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/dname.h"
+#include "libdnssec/tsig.h"
+
+/*!
+ * \brief TSIG key.
+ */
+typedef struct {
+ dnssec_tsig_algorithm_t algorithm;
+ knot_dname_t *name;
+ dnssec_binary_t secret;
+} knot_tsig_key_t;
+
+/*!
+ * \brief Packet signing context.
+ */
+typedef struct {
+ knot_tsig_key_t tsig_key;
+ uint8_t *tsig_digest;
+ size_t tsig_digestlen;
+ uint64_t tsig_time_signed;
+ size_t pkt_count;
+} knot_sign_context_t;
+
+/*!
+ * \brief Initialize a new TSIG key from individual key parameters.
+ *
+ * \param[out] key Key to be initialized.
+ * \param[in] algorithm Algorithm name. NULL for default (hmac-md5).
+ * \param[in] name Key name (domain name in presentation format).
+ * \param[in] secret_b64 Secret encoded using Base 64.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int knot_tsig_key_init(knot_tsig_key_t *key, const char *algorithm,
+ const char *name, const char *secret_b64);
+
+/*!
+ * \brief Create a new TSIG key from a string encoding all parameters.
+ *
+ * \param[out] key Key to be initialized.
+ * \param[in] params Parameters in a form \a [algorithm:]name:base64_secret
+ */
+int knot_tsig_key_init_str(knot_tsig_key_t *key, const char *params);
+
+/*!
+ * \brief Create a new TSIG key by reading the parameters from a file.
+ *
+ * The file content is parsed by \a tsig_key_create_str.
+ */
+int knot_tsig_key_init_file(knot_tsig_key_t *key, const char *filename);
+
+/*!
+ * \brief Deinitialize TSIG key.
+ */
+void knot_tsig_key_deinit(knot_tsig_key_t *key);
+
+/*!
+ * \brief Duplicate a TSIG key.
+ */
+int knot_tsig_key_copy(knot_tsig_key_t *dst, const knot_tsig_key_t *src);
+
+/*! @} */
diff --git a/src/libknot/version.h b/src/libknot/version.h
new file mode 100644
index 0000000..49aee67
--- /dev/null
+++ b/src/libknot/version.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define KNOT_VERSION_MAJOR 3
+#define KNOT_VERSION_MINOR 2
+#define KNOT_VERSION_PATCH 0x06
+
+#define KNOT_VERSION_HEX ((KNOT_VERSION_MAJOR << 16) | \
+ (KNOT_VERSION_MINOR << 8) | \
+ (KNOT_VERSION_PATCH))
diff --git a/src/libknot/version.h.in b/src/libknot/version.h.in
new file mode 100644
index 0000000..37c02c6
--- /dev/null
+++ b/src/libknot/version.h.in
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define KNOT_VERSION_MAJOR @KNOT_VERSION_MAJOR@
+#define KNOT_VERSION_MINOR @KNOT_VERSION_MINOR@
+#define KNOT_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@
+
+#define KNOT_VERSION_HEX ((KNOT_VERSION_MAJOR << 16) | \
+ (KNOT_VERSION_MINOR << 8) | \
+ (KNOT_VERSION_PATCH))
diff --git a/src/libknot/wire.h b/src/libknot/wire.h
new file mode 100644
index 0000000..2f264c7
--- /dev/null
+++ b/src/libknot/wire.h
@@ -0,0 +1,154 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Wire integer operations.
+ *
+ * \addtogroup wire
+ * @{
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "libknot/endian.h"
+
+/*!
+ * \brief Reads 2 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 2 bytes from.
+ *
+ * \return The 2 bytes read, in host byte order.
+ */
+inline static uint16_t knot_wire_read_u16(const uint8_t *pos)
+{
+ assert(pos);
+ uint16_t result;
+ memcpy(&result, pos, sizeof(result));
+ return be16toh(result);
+}
+
+/*!
+ * \brief Reads 4 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 4 bytes from.
+ *
+ * \return The 4 bytes read, in host byte order.
+ */
+inline static uint32_t knot_wire_read_u32(const uint8_t *pos)
+{
+ assert(pos);
+ uint32_t result;
+ memcpy(&result, pos, sizeof(result));
+ return be32toh(result);
+}
+
+/*!
+ * \brief Reads 6 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 6 bytes from.
+ *
+ * \return The 6 bytes read, in host byte order.
+ */
+inline static uint64_t knot_wire_read_u48(const uint8_t *pos)
+{
+ assert(pos);
+ uint64_t input = 0;
+ memcpy((uint8_t *)&input + 1, pos, 6);
+ return be64toh(input) >> 8;
+}
+
+/*!
+ * \brief Read 8 bytes from the wireformat data.
+ *
+ * \param pos Data to read the 8 bytes from.
+ *
+ * \return The 8 bytes read, in host byte order.
+ */
+inline static uint64_t knot_wire_read_u64(const uint8_t *pos)
+{
+ assert(pos);
+ uint64_t result;
+ memcpy(&result, pos, sizeof(result));
+ return be64toh(result);
+}
+
+/*!
+ * \brief Writes 2 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 2 bytes.
+ * \param data Data to put.
+ */
+inline static void knot_wire_write_u16(uint8_t *pos, uint16_t data)
+{
+ assert(pos);
+ uint16_t beval = htobe16(data);
+ memcpy(pos, &beval, sizeof(beval));
+}
+
+/*!
+ * \brief Writes 4 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+inline static void knot_wire_write_u32(uint8_t *pos, uint32_t data)
+{
+ assert(pos);
+ uint32_t beval = htobe32(data);
+ memcpy(pos, &beval, sizeof(beval));
+}
+
+/*!
+ * \brief Writes 6 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 4 bytes.
+ * \param data Data to put.
+ */
+inline static void knot_wire_write_u48(uint8_t *pos, uint64_t data)
+{
+ assert(pos);
+ uint64_t swapped = htobe64(data << 8);
+ memcpy(pos, (uint8_t *)&swapped + 1, 6);
+}
+
+/*!
+ * \brief Writes 8 bytes in wireformat.
+ *
+ * The data are stored in network byte order (big endian).
+ *
+ * \param pos Position where to put the 8 bytes.
+ * \param data Data to put.
+ */
+inline static void knot_wire_write_u64(uint8_t *pos, uint64_t data)
+{
+ assert(pos);
+ uint64_t beval = htobe64(data);
+ memcpy(pos, &beval, sizeof(beval));
+}
+
+/*! @} */
diff --git a/src/libknot/xdp.h b/src/libknot/xdp.h
new file mode 100644
index 0000000..db4460c
--- /dev/null
+++ b/src/libknot/xdp.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Convenience header for including XDP-related stuff.
+ *
+ * \addtogroup libknot
+ * @{
+ */
+
+#pragma once
+
+#if ENABLE_XDP
+#include "libknot/xdp/xdp.h"
+#include "libknot/xdp/bpf-consts.h"
+#include "libknot/xdp/eth.h"
+#include "libknot/xdp/tcp.h"
+#endif
+
+/*! @} */
diff --git a/src/libknot/xdp/Makefile.am b/src/libknot/xdp/Makefile.am
new file mode 100644
index 0000000..cb58438
--- /dev/null
+++ b/src/libknot/xdp/Makefile.am
@@ -0,0 +1,20 @@
+# Useful commands:
+# make filter
+# ip link show $eth
+# sudo ip link set dev $eth xdp off
+# sudo ip link set dev $eth xdp obj ./bpf-kernel.o
+#
+# Built using LLVM/CLANG 14.
+#
+# When updating check using `llvm-objdump -h bpf-kernel.o` if .BTF and .BTF.ext
+# sections are present.
+
+EXTRA_DIST = bpf-kernel-obj.c bpf-kernel.c
+
+.PHONY: filter
+
+filter:
+ rm -f bpf-kernel.o bpf-kernel-obj.c
+ clang -target bpf -Wall -O2 -g -DNDEBUG -c -o bpf-kernel.o -I/usr/include/x86_64-linux-gnu -include ../../config.h bpf-kernel.c
+ llvm-strip -S bpf-kernel.o
+ xxd -i bpf-kernel.o > bpf-kernel-obj.c
diff --git a/src/libknot/xdp/Makefile.in b/src/libknot/xdp/Makefile.in
new file mode 100644
index 0000000..b56c39d
--- /dev/null
+++ b/src/libknot/xdp/Makefile.in
@@ -0,0 +1,545 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+
+# Useful commands:
+# make filter
+# ip link show $eth
+# sudo ip link set dev $eth xdp off
+# sudo ip link set dev $eth xdp obj ./bpf-kernel.o
+#
+# Built using LLVM/CLANG 14.
+#
+# When updating check using `llvm-objdump -h bpf-kernel.o` if .BTF and .BTF.ext
+# sections are present.
+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/libknot/xdp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = bpf-kernel-obj.c bpf-kernel.c
+all: all-am
+
+.SUFFIXES:
+$(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/libknot/xdp/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/libknot/xdp/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
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+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
+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:
+
+clean-generic:
+
+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."
+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:
+
+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: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am 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 \
+ tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+.PHONY: filter
+
+filter:
+ rm -f bpf-kernel.o bpf-kernel-obj.c
+ clang -target bpf -Wall -O2 -g -DNDEBUG -c -o bpf-kernel.o -I/usr/include/x86_64-linux-gnu -include ../../config.h bpf-kernel.c
+ llvm-strip -S bpf-kernel.o
+ xxd -i bpf-kernel.o > bpf-kernel-obj.c
+
+# 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/src/libknot/xdp/bpf-consts.h b/src/libknot/xdp/bpf-consts.h
new file mode 100644
index 0000000..3b92cbb
--- /dev/null
+++ b/src/libknot/xdp/bpf-consts.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief XDP filter configuration constants.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <linux/types.h>
+
+#define KNOT_XDP_PKT_ALIGNMENT 2 /*!< Fix for misaligned access to packet structures. */
+
+/*! \brief XDP filter configuration flags. */
+typedef enum {
+ KNOT_XDP_FILTER_ON = 1 << 0, /*!< Filter enabled. */
+ KNOT_XDP_FILTER_UDP = 1 << 1, /*!< Apply filter to UDP. */
+ KNOT_XDP_FILTER_TCP = 1 << 2, /*!< Apply filter to TCP. */
+ KNOT_XDP_FILTER_QUIC = 1 << 3, /*!< Apply filter to QUIC/UDP. */
+ KNOT_XDP_FILTER_PASS = 1 << 4, /*!< Pass incoming messages to ports >= port value. */
+ KNOT_XDP_FILTER_DROP = 1 << 5, /*!< Drop incoming messages to ports >= port value. */
+ KNOT_XDP_FILTER_ROUTE = 1 << 6, /*!< Consider routing information from kernel. */
+} knot_xdp_filter_flag_t;
+
+/*! \brief XDP map item for the filter configuration. */
+typedef struct knot_xdp_opts knot_xdp_opts_t;
+struct knot_xdp_opts {
+ __u16 flags; /*!< XDP filter flags \a knot_xdp_filter_flag_t. */
+ __u16 udp_port; /*!< UDP/TCP port to listen on. */
+ __u16 quic_port; /*!< QUIC/UDP port to listen on. */
+} __attribute__((packed));
+
+/*! \brief Additional information from the filter. */
+typedef struct knot_xdp_info knot_xdp_info_t;
+struct knot_xdp_info {
+ __u16 out_if_index; /*!< Index of the output interface (if routing enabled). */
+};
+
+/*! @} */
diff --git a/src/libknot/xdp/bpf-kernel-obj.c b/src/libknot/xdp/bpf-kernel-obj.c
new file mode 100644
index 0000000..9f1ee71
--- /dev/null
+++ b/src/libknot/xdp/bpf-kernel-obj.c
@@ -0,0 +1,941 @@
+unsigned char bpf_kernel_o[] = {
+ 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb8, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x0d, 0x00, 0x01, 0x00, 0xbf, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x1a, 0xfc, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x02, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0x18, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x71, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x71, 0x19, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x67, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x29, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x57, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x15, 0x02, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x71, 0x12, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xa0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x71, 0x12, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x2a, 0x98, 0xff, 0x00, 0x00, 0x00, 0x00, 0x71, 0x18, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x71, 0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x1a, 0xa8, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x61, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00,
+ 0x36, 0x00, 0x00, 0x00, 0x61, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x72, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x3a, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x21, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x71, 0x74, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x73, 0x0d, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x4f, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x0b, 0x00,
+ 0x81, 0x00, 0x00, 0x00, 0xbf, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x01, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x21, 0xe7, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x79, 0xa3, 0xb0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x71, 0x74, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x73, 0x11, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x4f, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x15, 0x03, 0x1e, 0x00, 0x86, 0xdd, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x03, 0xdc, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x23, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x71, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x34, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x57, 0x04, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x04, 0xd3, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0xbf, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x15, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xdc, 0x05, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6d, 0x45, 0xcd, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x57, 0x05, 0x00, 0x00, 0xbf, 0xff, 0x00, 0x00, 0xb7, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x4a, 0x90, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x05, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x67, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00,
+ 0x3c, 0x00, 0x00, 0x00, 0xbf, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0f, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x10, 0x09, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x2d, 0x25, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x55, 0x03, 0xb8, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0xbf, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x14, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xdc, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x07, 0x04, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x34, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7b, 0x3a, 0x90, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x71, 0x10, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x08, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xbf, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x2d, 0x25, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x71, 0x10, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x7b, 0x3a, 0x90, 0xff, 0x00, 0x00, 0x00, 0x00, 0x67, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x79, 0xa3, 0xa8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x4f, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x17, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x55, 0x03, 0x9d, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0xbf, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x03, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x23, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x02, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x52, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xbf, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x1d, 0x32, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x03, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x2d, 0x28, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x28, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2d, 0x23, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1f, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x53, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xdc, 0x03, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6d, 0x23, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x52, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xdc, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xbf, 0x93, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x83, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
+ 0x1d, 0x32, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x93, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x15, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x08, 0x00, 0x00,
+ 0xff, 0xff, 0x00, 0x00, 0x3d, 0x82, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x15, 0x03, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0xa3, 0xa0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x79, 0xa5, 0x98, 0xff, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x53, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x57, 0x03, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x1d, 0x32, 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xbf, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x57, 0x03, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x15, 0x03, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x57, 0x05, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x2d, 0x25, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x55, 0x02, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x04, 0x00, 0x00,
+ 0xff, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x55, 0x04, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x09, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x15, 0x09, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xc0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x2a, 0xe8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xe0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x2a, 0xd0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xc8, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xb8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x63, 0x2a, 0xc0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x79, 0xa2, 0x90, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x55, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x73, 0x2a, 0xb8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x2a, 0xc8, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x61, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x63, 0x1a, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x73, 0x2a, 0xb8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x61, 0x12, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x61, 0x13, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x32, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xc8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x61, 0x13, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x4f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x2a, 0xd0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x61, 0x12, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x67, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x61, 0x13, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7b, 0x2a, 0xe0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x61, 0x12, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x61, 0x11, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x67, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x4f, 0x21, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x7b, 0x1a, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00,
+ 0xb8, 0xff, 0xff, 0xff, 0xbf, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xb7, 0x04, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00,
+ 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x15, 0x01, 0x24, 0x00, 0x07, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x15, 0x01, 0x21, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x55, 0x01, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x69, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0xa2, 0xec, 0xff, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x21, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0xa2, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x21, 0x19, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0x71, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0xa2, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x21, 0x16, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x79, 0xa1, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x15, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xa1, 0xc0, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x79, 0xa2, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x6b, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xa1, 0xf6, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x73, 0x17, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x77, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x73, 0x17, 0x0b, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x69, 0xa1, 0xf4, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x73, 0x17, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x73, 0x17, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0xa1, 0xf2, 0xff, 0x00, 0x00, 0x00, 0x00, 0x73, 0x17, 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x73, 0x17, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x62, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0xfb, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x50, 0x4c, 0x00,
+ 0x9f, 0xeb, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7c, 0x02, 0x00, 0x00, 0x7c, 0x02, 0x00, 0x00, 0xce, 0x0a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x33, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x0f, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x12, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04,
+ 0x18, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
+ 0xa0, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x14, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x0d, 0x02, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00,
+ 0x11, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0c,
+ 0x15, 0x00, 0x00, 0x00, 0xb2, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb7, 0x0a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0e, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xc0, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0xc6, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x69, 0x6e, 0x74, 0x00, 0x5f, 0x5f, 0x41, 0x52, 0x52, 0x41, 0x59,
+ 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x5f,
+ 0x00, 0x74, 0x79, 0x70, 0x65, 0x00, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x6e,
+ 0x74, 0x72, 0x69, 0x65, 0x73, 0x00, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69,
+ 0x7a, 0x65, 0x00, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x73, 0x69, 0x7a,
+ 0x65, 0x00, 0x6f, 0x70, 0x74, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x00, 0x78,
+ 0x73, 0x6b, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x00, 0x78, 0x64, 0x70, 0x5f,
+ 0x6d, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x64, 0x61, 0x74, 0x61,
+ 0x5f, 0x65, 0x6e, 0x64, 0x00, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65,
+ 0x74, 0x61, 0x00, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69,
+ 0x66, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x72, 0x78, 0x5f, 0x71, 0x75,
+ 0x65, 0x75, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x00, 0x65, 0x67,
+ 0x72, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x66, 0x69, 0x6e, 0x64, 0x65, 0x78,
+ 0x00, 0x5f, 0x5f, 0x75, 0x33, 0x32, 0x00, 0x75, 0x6e, 0x73, 0x69, 0x67,
+ 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x63, 0x74, 0x78, 0x00,
+ 0x78, 0x64, 0x70, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
+ 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x00, 0x78, 0x64,
+ 0x70, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x64, 0x73, 0x61, 0x6c,
+ 0x7a, 0x6d, 0x61, 0x6e, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x32, 0x2f, 0x73,
+ 0x72, 0x63, 0x2f, 0x6c, 0x69, 0x62, 0x6b, 0x6e, 0x6f, 0x74, 0x2f, 0x78,
+ 0x64, 0x70, 0x2f, 0x62, 0x70, 0x66, 0x2d, 0x6b, 0x65, 0x72, 0x6e, 0x65,
+ 0x6c, 0x2e, 0x63, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x64, 0x70, 0x5f,
+ 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6e, 0x73,
+ 0x5f, 0x66, 0x75, 0x6e, 0x63, 0x28, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74,
+ 0x20, 0x78, 0x64, 0x70, 0x5f, 0x6d, 0x64, 0x20, 0x2a, 0x63, 0x74, 0x78,
+ 0x29, 0x00, 0x09, 0x5f, 0x5f, 0x75, 0x33, 0x32, 0x20, 0x69, 0x6e, 0x64,
+ 0x65, 0x78, 0x20, 0x3d, 0x20, 0x63, 0x74, 0x78, 0x2d, 0x3e, 0x72, 0x78,
+ 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
+ 0x3b, 0x00, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x6b, 0x6e,
+ 0x6f, 0x74, 0x5f, 0x78, 0x64, 0x70, 0x5f, 0x6f, 0x70, 0x74, 0x73, 0x20,
+ 0x2a, 0x6f, 0x70, 0x74, 0x73, 0x5f, 0x70, 0x74, 0x72, 0x20, 0x3d, 0x20,
+ 0x62, 0x70, 0x66, 0x5f, 0x6d, 0x61, 0x70, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b,
+ 0x75, 0x70, 0x5f, 0x65, 0x6c, 0x65, 0x6d, 0x28, 0x26, 0x6f, 0x70, 0x74,
+ 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2c, 0x20, 0x26, 0x69, 0x6e, 0x64, 0x65,
+ 0x78, 0x29, 0x3b, 0x00, 0x09, 0x69, 0x66, 0x20, 0x28, 0x21, 0x6f, 0x70,
+ 0x74, 0x73, 0x5f, 0x70, 0x74, 0x72, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x69,
+ 0x66, 0x20, 0x28, 0x21, 0x28, 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x66, 0x6c,
+ 0x61, 0x67, 0x73, 0x20, 0x26, 0x20, 0x4b, 0x4e, 0x4f, 0x54, 0x5f, 0x58,
+ 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x4f, 0x4e,
+ 0x29, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x28, 0x76, 0x6f, 0x69, 0x64, 0x29,
+ 0x62, 0x70, 0x66, 0x5f, 0x78, 0x64, 0x70, 0x5f, 0x61, 0x64, 0x6a, 0x75,
+ 0x73, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x28, 0x63, 0x74, 0x78, 0x2c,
+ 0x20, 0x2d, 0x20, 0x28, 0x69, 0x6e, 0x74, 0x29, 0x73, 0x69, 0x7a, 0x65,
+ 0x6f, 0x66, 0x28, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x6b, 0x6e,
+ 0x6f, 0x74, 0x5f, 0x78, 0x64, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x29,
+ 0x00, 0x09, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x64, 0x61, 0x74, 0x61,
+ 0x20, 0x3d, 0x20, 0x28, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x28,
+ 0x6c, 0x6f, 0x6e, 0x67, 0x29, 0x63, 0x74, 0x78, 0x2d, 0x3e, 0x64, 0x61,
+ 0x74, 0x61, 0x3b, 0x00, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20,
+ 0x6b, 0x6e, 0x6f, 0x74, 0x5f, 0x78, 0x64, 0x70, 0x5f, 0x69, 0x6e, 0x66,
+ 0x6f, 0x20, 0x2a, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x3d, 0x20, 0x28, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x28, 0x6c, 0x6f, 0x6e, 0x67, 0x29,
+ 0x63, 0x74, 0x78, 0x2d, 0x3e, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65,
+ 0x74, 0x61, 0x3b, 0x00, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x76, 0x6f,
+ 0x69, 0x64, 0x20, 0x2a, 0x29, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x2b, 0x20,
+ 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a, 0x6d, 0x65, 0x74, 0x61,
+ 0x29, 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x7b, 0x00,
+ 0x09, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20,
+ 0x2a, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x20, 0x3d, 0x20,
+ 0x28, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x28, 0x6c, 0x6f, 0x6e,
+ 0x67, 0x29, 0x63, 0x74, 0x78, 0x2d, 0x3e, 0x64, 0x61, 0x74, 0x61, 0x5f,
+ 0x65, 0x6e, 0x64, 0x3b, 0x00, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x76,
+ 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x65, 0x74, 0x68, 0x5f, 0x68, 0x64,
+ 0x72, 0x20, 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a,
+ 0x65, 0x74, 0x68, 0x5f, 0x68, 0x64, 0x72, 0x29, 0x20, 0x3e, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x29, 0x20, 0x7b, 0x00, 0x09,
+ 0x69, 0x66, 0x20, 0x28, 0x65, 0x74, 0x68, 0x5f, 0x68, 0x64, 0x72, 0x2d,
+ 0x3e, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x3d, 0x20,
+ 0x5f, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x68,
+ 0x74, 0x6f, 0x6e, 0x73, 0x28, 0x45, 0x54, 0x48, 0x5f, 0x50, 0x5f, 0x38,
+ 0x30, 0x32, 0x31, 0x51, 0x29, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69,
+ 0x66, 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x20, 0x2b, 0x20, 0x73, 0x69,
+ 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x5f, 0x5f, 0x75, 0x31, 0x36, 0x29, 0x20,
+ 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x65, 0x74, 0x68,
+ 0x5f, 0x74, 0x79, 0x70, 0x65, 0x29, 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74,
+ 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x7d,
+ 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x65,
+ 0x74, 0x61, 0x20, 0x3d, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x00, 0x09,
+ 0x09, 0x5f, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x5f, 0x6d,
+ 0x65, 0x6d, 0x63, 0x70, 0x79, 0x28, 0x26, 0x65, 0x74, 0x68, 0x5f, 0x74,
+ 0x79, 0x70, 0x65, 0x2c, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x2b, 0x20,
+ 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x5f, 0x5f, 0x75, 0x31, 0x36,
+ 0x29, 0x2c, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x65, 0x74,
+ 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x29, 0x29, 0x3b, 0x00, 0x09, 0x73,
+ 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28, 0x65, 0x74, 0x68, 0x5f, 0x74,
+ 0x79, 0x70, 0x65, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20,
+ 0x28, 0x28, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x69, 0x70, 0x34,
+ 0x20, 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a, 0x69,
+ 0x70, 0x34, 0x29, 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65,
+ 0x6e, 0x64, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28,
+ 0x69, 0x70, 0x34, 0x2d, 0x3e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x21, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69,
+ 0x66, 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x20,
+ 0x2d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3c, 0x20, 0x5f, 0x5f, 0x62,
+ 0x70, 0x66, 0x5f, 0x6e, 0x74, 0x6f, 0x68, 0x73, 0x28, 0x69, 0x70, 0x34,
+ 0x2d, 0x3e, 0x74, 0x6f, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x29, 0x29, 0x20,
+ 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x70, 0x34, 0x2d,
+ 0x3e, 0x66, 0x72, 0x61, 0x67, 0x5f, 0x6f, 0x66, 0x66, 0x20, 0x21, 0x3d,
+ 0x20, 0x30, 0x20, 0x26, 0x26, 0x00, 0x09, 0x09, 0x6c, 0x34, 0x5f, 0x68,
+ 0x64, 0x72, 0x20, 0x3d, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x2b, 0x20,
+ 0x69, 0x70, 0x34, 0x2d, 0x3e, 0x69, 0x68, 0x6c, 0x20, 0x2a, 0x20, 0x34,
+ 0x3b, 0x00, 0x09, 0x09, 0x69, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x20, 0x3d, 0x20, 0x69, 0x70, 0x34, 0x2d, 0x3e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x63, 0x6f, 0x6c, 0x3b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28,
+ 0x28, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x2a, 0x29, 0x69, 0x70, 0x36, 0x20,
+ 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a, 0x69, 0x70,
+ 0x36, 0x29, 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e,
+ 0x64, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x69,
+ 0x70, 0x36, 0x2d, 0x3e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
+ 0x21, 0x3d, 0x20, 0x36, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66,
+ 0x20, 0x28, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x20, 0x2d,
+ 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x3c, 0x20, 0x5f, 0x5f, 0x62, 0x70,
+ 0x66, 0x5f, 0x6e, 0x74, 0x6f, 0x68, 0x73, 0x28, 0x69, 0x70, 0x36, 0x2d,
+ 0x3e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6c, 0x65, 0x6e,
+ 0x29, 0x20, 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a,
+ 0x69, 0x70, 0x36, 0x29, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x70,
+ 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x69, 0x70, 0x36,
+ 0x2d, 0x3e, 0x6e, 0x65, 0x78, 0x74, 0x68, 0x64, 0x72, 0x3b, 0x00, 0x09,
+ 0x09, 0x69, 0x66, 0x20, 0x28, 0x69, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x20, 0x3d, 0x3d, 0x20, 0x49, 0x50, 0x50, 0x52, 0x4f, 0x54, 0x4f,
+ 0x5f, 0x46, 0x52, 0x41, 0x47, 0x4d, 0x45, 0x4e, 0x54, 0x29, 0x20, 0x7b,
+ 0x00, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x76, 0x6f, 0x69,
+ 0x64, 0x20, 0x2a, 0x29, 0x66, 0x72, 0x61, 0x67, 0x20, 0x2b, 0x20, 0x73,
+ 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a, 0x66, 0x72, 0x61, 0x67, 0x29,
+ 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x29,
+ 0x20, 0x7b, 0x00, 0x09, 0x09, 0x09, 0x69, 0x70, 0x5f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x2d, 0x3e, 0x6e,
+ 0x65, 0x78, 0x74, 0x68, 0x64, 0x72, 0x3b, 0x00, 0x09, 0x73, 0x77, 0x69,
+ 0x74, 0x63, 0x68, 0x20, 0x28, 0x69, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6c,
+ 0x34, 0x5f, 0x68, 0x64, 0x72, 0x20, 0x2b, 0x20, 0x73, 0x69, 0x7a, 0x65,
+ 0x6f, 0x66, 0x28, 0x2a, 0x74, 0x63, 0x70, 0x29, 0x20, 0x3e, 0x20, 0x64,
+ 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x29, 0x20, 0x7b, 0x00, 0x09,
+ 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x66,
+ 0x6c, 0x61, 0x67, 0x73, 0x20, 0x26, 0x20, 0x4b, 0x4e, 0x4f, 0x54, 0x5f,
+ 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x54,
+ 0x43, 0x50, 0x29, 0x20, 0x26, 0x26, 0x00, 0x09, 0x09, 0x70, 0x6f, 0x72,
+ 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x20, 0x5f, 0x5f, 0x62,
+ 0x70, 0x66, 0x5f, 0x6e, 0x74, 0x6f, 0x68, 0x73, 0x28, 0x74, 0x63, 0x70,
+ 0x2d, 0x3e, 0x64, 0x65, 0x73, 0x74, 0x29, 0x3b, 0x00, 0x09, 0x09, 0x20,
+ 0x20, 0x20, 0x20, 0x28, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x73,
+ 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x75, 0x64,
+ 0x70, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x7c, 0x7c, 0x00, 0x09, 0x09,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x28, 0x28, 0x6f, 0x70, 0x74, 0x73, 0x2e,
+ 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x26, 0x20, 0x28, 0x4b, 0x4e, 0x4f,
+ 0x54, 0x5f, 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52,
+ 0x5f, 0x50, 0x41, 0x53, 0x53, 0x20, 0x7c, 0x20, 0x4b, 0x4e, 0x4f, 0x54,
+ 0x5f, 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f,
+ 0x44, 0x52, 0x4f, 0x50, 0x29, 0x29, 0x20, 0x26, 0x26, 0x00, 0x09, 0x09,
+ 0x69, 0x66, 0x20, 0x28, 0x6c, 0x34, 0x5f, 0x68, 0x64, 0x72, 0x20, 0x2b,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x2a, 0x75, 0x64, 0x70,
+ 0x29, 0x20, 0x3e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64,
+ 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x64, 0x61,
+ 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x20, 0x2d, 0x20, 0x28, 0x76, 0x6f,
+ 0x69, 0x64, 0x20, 0x2a, 0x29, 0x75, 0x64, 0x70, 0x20, 0x3c, 0x20, 0x5f,
+ 0x5f, 0x62, 0x70, 0x66, 0x5f, 0x6e, 0x74, 0x6f, 0x68, 0x73, 0x28, 0x75,
+ 0x64, 0x70, 0x2d, 0x3e, 0x6c, 0x65, 0x6e, 0x29, 0x29, 0x20, 0x7b, 0x00,
+ 0x09, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x20,
+ 0x3d, 0x20, 0x5f, 0x5f, 0x62, 0x70, 0x66, 0x5f, 0x6e, 0x74, 0x6f, 0x68,
+ 0x73, 0x28, 0x75, 0x64, 0x70, 0x2d, 0x3e, 0x64, 0x65, 0x73, 0x74, 0x29,
+ 0x3b, 0x00, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x28, 0x6f, 0x70, 0x74,
+ 0x73, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x26, 0x20, 0x4b, 0x4e,
+ 0x4f, 0x54, 0x5f, 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45,
+ 0x52, 0x5f, 0x55, 0x44, 0x50, 0x29, 0x20, 0x26, 0x26, 0x00, 0x09, 0x09,
+ 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20, 0x69, 0x66, 0x20, 0x28, 0x28,
+ 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x26,
+ 0x20, 0x4b, 0x4e, 0x4f, 0x54, 0x5f, 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49,
+ 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x51, 0x55, 0x49, 0x43, 0x29, 0x20, 0x26,
+ 0x26, 0x00, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x28, 0x70, 0x6f, 0x72,
+ 0x74, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x20, 0x3d, 0x3d, 0x20, 0x6f, 0x70,
+ 0x74, 0x73, 0x2e, 0x71, 0x75, 0x69, 0x63, 0x5f, 0x70, 0x6f, 0x72, 0x74,
+ 0x20, 0x7c, 0x7c, 0x00, 0x09, 0x7d, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x20,
+ 0x69, 0x66, 0x20, 0x28, 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x66, 0x6c, 0x61,
+ 0x67, 0x73, 0x20, 0x26, 0x20, 0x4b, 0x4e, 0x4f, 0x54, 0x5f, 0x58, 0x44,
+ 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x5f, 0x44, 0x52, 0x4f,
+ 0x50, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6f, 0x70,
+ 0x74, 0x73, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x20, 0x26, 0x20, 0x4b,
+ 0x4e, 0x4f, 0x54, 0x5f, 0x58, 0x44, 0x50, 0x5f, 0x46, 0x49, 0x4c, 0x54,
+ 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x55, 0x54, 0x45, 0x29, 0x20, 0x7b, 0x00,
+ 0x09, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x62, 0x70, 0x66,
+ 0x5f, 0x66, 0x69, 0x62, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x20,
+ 0x66, 0x69, 0x62, 0x20, 0x3d, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x69, 0x66,
+ 0x20, 0x28, 0x69, 0x70, 0x76, 0x34, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09,
+ 0x09, 0x66, 0x69, 0x62, 0x2e, 0x66, 0x61, 0x6d, 0x69, 0x6c, 0x79, 0x20,
+ 0x20, 0x20, 0x3d, 0x20, 0x41, 0x46, 0x5f, 0x49, 0x4e, 0x45, 0x54, 0x3b,
+ 0x00, 0x09, 0x09, 0x09, 0x66, 0x69, 0x62, 0x2e, 0x69, 0x70, 0x76, 0x34,
+ 0x5f, 0x73, 0x72, 0x63, 0x20, 0x3d, 0x20, 0x69, 0x70, 0x34, 0x2d, 0x3e,
+ 0x64, 0x61, 0x64, 0x64, 0x72, 0x3b, 0x00, 0x09, 0x09, 0x09, 0x66, 0x69,
+ 0x62, 0x2e, 0x69, 0x70, 0x76, 0x34, 0x5f, 0x64, 0x73, 0x74, 0x20, 0x3d,
+ 0x20, 0x69, 0x70, 0x34, 0x2d, 0x3e, 0x73, 0x61, 0x64, 0x64, 0x72, 0x3b,
+ 0x00, 0x09, 0x09, 0x09, 0x66, 0x69, 0x62, 0x2e, 0x66, 0x61, 0x6d, 0x69,
+ 0x6c, 0x79, 0x20, 0x3d, 0x20, 0x41, 0x46, 0x5f, 0x49, 0x4e, 0x45, 0x54,
+ 0x36, 0x3b, 0x00, 0x09, 0x09, 0x09, 0x2a, 0x69, 0x70, 0x76, 0x36, 0x5f,
+ 0x73, 0x72, 0x63, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x70, 0x36, 0x2d, 0x3e,
+ 0x64, 0x61, 0x64, 0x64, 0x72, 0x3b, 0x00, 0x09, 0x09, 0x09, 0x2a, 0x69,
+ 0x70, 0x76, 0x36, 0x5f, 0x64, 0x73, 0x74, 0x20, 0x20, 0x3d, 0x20, 0x69,
+ 0x70, 0x36, 0x2d, 0x3e, 0x73, 0x61, 0x64, 0x64, 0x72, 0x3b, 0x00, 0x09,
+ 0x09, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x74, 0x20, 0x3d, 0x20, 0x62,
+ 0x70, 0x66, 0x5f, 0x66, 0x69, 0x62, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75,
+ 0x70, 0x28, 0x63, 0x74, 0x78, 0x2c, 0x20, 0x26, 0x66, 0x69, 0x62, 0x2c,
+ 0x20, 0x73, 0x69, 0x7a, 0x65, 0x6f, 0x66, 0x28, 0x66, 0x69, 0x62, 0x29,
+ 0x2c, 0x20, 0x42, 0x50, 0x46, 0x5f, 0x46, 0x49, 0x42, 0x5f, 0x4c, 0x4f,
+ 0x4f, 0x4b, 0x55, 0x50, 0x5f, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x29,
+ 0x3b, 0x00, 0x09, 0x09, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, 0x28,
+ 0x72, 0x65, 0x74, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x09, 0x69, 0x66,
+ 0x20, 0x28, 0x6d, 0x61, 0x63, 0x5f, 0x69, 0x6e, 0x5b, 0x30, 0x5d, 0x20,
+ 0x21, 0x3d, 0x20, 0x6d, 0x61, 0x63, 0x5f, 0x6f, 0x75, 0x74, 0x5b, 0x30,
+ 0x5d, 0x20, 0x7c, 0x7c, 0x00, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20,
+ 0x6d, 0x61, 0x63, 0x5f, 0x69, 0x6e, 0x5b, 0x31, 0x5d, 0x20, 0x21, 0x3d,
+ 0x20, 0x6d, 0x61, 0x63, 0x5f, 0x6f, 0x75, 0x74, 0x5b, 0x31, 0x5d, 0x20,
+ 0x7c, 0x7c, 0x00, 0x09, 0x09, 0x09, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61,
+ 0x63, 0x5f, 0x69, 0x6e, 0x5b, 0x32, 0x5d, 0x20, 0x21, 0x3d, 0x20, 0x6d,
+ 0x61, 0x63, 0x5f, 0x6f, 0x75, 0x74, 0x5b, 0x32, 0x5d, 0x29, 0x20, 0x7b,
+ 0x00, 0x09, 0x09, 0x09, 0x69, 0x66, 0x20, 0x28, 0x6d, 0x65, 0x74, 0x61,
+ 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x7b, 0x00, 0x09, 0x09, 0x09,
+ 0x09, 0x6d, 0x65, 0x74, 0x61, 0x2d, 0x3e, 0x6f, 0x75, 0x74, 0x5f, 0x69,
+ 0x66, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x66, 0x69,
+ 0x62, 0x2e, 0x69, 0x66, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x00, 0x09,
+ 0x09, 0x09, 0x5f, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x69, 0x6e, 0x5f,
+ 0x6d, 0x65, 0x6d, 0x63, 0x70, 0x79, 0x28, 0x65, 0x74, 0x68, 0x5f, 0x68,
+ 0x64, 0x72, 0x2d, 0x3e, 0x68, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x2c, 0x20, 0x66, 0x69, 0x62, 0x2e, 0x64, 0x6d, 0x61, 0x63, 0x2c, 0x20,
+ 0x45, 0x54, 0x48, 0x5f, 0x41, 0x4c, 0x45, 0x4e, 0x29, 0x3b, 0x00, 0x09,
+ 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x62, 0x70, 0x66, 0x5f, 0x72,
+ 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x28,
+ 0x26, 0x78, 0x73, 0x6b, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x2c, 0x20, 0x63,
+ 0x74, 0x78, 0x2d, 0x3e, 0x72, 0x78, 0x5f, 0x71, 0x75, 0x65, 0x75, 0x65,
+ 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x2c, 0x20, 0x30, 0x29, 0x3b, 0x00,
+ 0x7d, 0x00, 0x63, 0x68, 0x61, 0x72, 0x00, 0x5f, 0x6c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x00, 0x2e, 0x6d, 0x61, 0x70, 0x73, 0x00, 0x6c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00, 0x00, 0x00, 0x9f, 0xeb, 0x01, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x2c, 0x07, 0x00, 0x00, 0x40, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0xf0, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x2e, 0x01, 0x00, 0x00, 0x15, 0xfc, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x2e, 0x01, 0x00, 0x00, 0x08, 0xfc, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x52, 0x01, 0x00, 0x00, 0x23, 0x00, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x06, 0x04, 0x01, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xae, 0x01, 0x00, 0x00,
+ 0x08, 0x1c, 0x01, 0x00, 0x78, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xae, 0x01, 0x00, 0x00, 0x13, 0x1c, 0x01, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xae, 0x01, 0x00, 0x00, 0x06, 0x1c, 0x01, 0x00,
+ 0x98, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xd9, 0x01, 0x00, 0x00, 0x08, 0x30, 0x01, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x1d, 0x02, 0x00, 0x00, 0x22, 0x3c, 0x01, 0x00,
+ 0xf8, 0x00, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00,
+ 0x32, 0x44, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x80, 0x02, 0x00, 0x00, 0x13, 0x50, 0x01, 0x00, 0x18, 0x01, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x06, 0x50, 0x01, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xac, 0x02, 0x00, 0x00,
+ 0x2c, 0x40, 0x01, 0x00, 0x38, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xe1, 0x02, 0x00, 0x00, 0x16, 0x8c, 0x01, 0x00, 0x50, 0x01, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xe1, 0x02, 0x00, 0x00, 0x06, 0x8c, 0x01, 0x00,
+ 0x58, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x17, 0x03, 0x00, 0x00,
+ 0x0f, 0xa4, 0x01, 0x00, 0x78, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x17, 0x03, 0x00, 0x00, 0x06, 0xa4, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, 0x1c, 0xa8, 0x01, 0x00,
+ 0x98, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00,
+ 0x07, 0xa8, 0x01, 0x00, 0xa8, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x8d, 0x03, 0x00, 0x00, 0x0e, 0xb0, 0x01, 0x00, 0xb8, 0x01, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xa7, 0x03, 0x00, 0x00, 0x03, 0xbc, 0x01, 0x00,
+ 0xd8, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xee, 0x03, 0x00, 0x00,
+ 0x02, 0xe0, 0x01, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x03, 0x04, 0x00, 0x00, 0x13, 0xec, 0x01, 0x00, 0x10, 0x02, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x07, 0xec, 0x01, 0x00,
+ 0x18, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x32, 0x04, 0x00, 0x00,
+ 0x0c, 0xf8, 0x01, 0x00, 0x20, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x32, 0x04, 0x00, 0x00, 0x14, 0xf8, 0x01, 0x00, 0x38, 0x02, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x32, 0x04, 0x00, 0x00, 0x07, 0xf8, 0x01, 0x00,
+ 0x40, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x4d, 0x04, 0x00, 0x00,
+ 0x10, 0x10, 0x02, 0x00, 0x50, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x4d, 0x04, 0x00, 0x00, 0x19, 0x10, 0x02, 0x00, 0x68, 0x02, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x4d, 0x04, 0x00, 0x00, 0x07, 0x10, 0x02, 0x00,
+ 0x70, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x82, 0x04, 0x00, 0x00,
+ 0x0c, 0x20, 0x02, 0x00, 0x78, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x82, 0x04, 0x00, 0x00, 0x1a, 0x20, 0x02, 0x00, 0xa8, 0x02, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x9e, 0x04, 0x00, 0x00, 0x1c, 0x34, 0x02, 0x00,
+ 0xb8, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x9e, 0x04, 0x00, 0x00,
+ 0x11, 0x34, 0x02, 0x00, 0xc8, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xbe, 0x04, 0x00, 0x00, 0x13, 0x30, 0x02, 0x00, 0xd8, 0x02, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xda, 0x04, 0x00, 0x00, 0x13, 0x48, 0x02, 0x00,
+ 0xf0, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xda, 0x04, 0x00, 0x00,
+ 0x07, 0x48, 0x02, 0x00, 0xf8, 0x02, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x09, 0x05, 0x00, 0x00, 0x0c, 0x54, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00, 0x14, 0x54, 0x02, 0x00,
+ 0x10, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x09, 0x05, 0x00, 0x00,
+ 0x07, 0x54, 0x02, 0x00, 0x18, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x24, 0x05, 0x00, 0x00, 0x10, 0x6c, 0x02, 0x00, 0x28, 0x03, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00, 0x19, 0x6c, 0x02, 0x00,
+ 0x38, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x24, 0x05, 0x00, 0x00,
+ 0x37, 0x6c, 0x02, 0x00, 0x48, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x24, 0x05, 0x00, 0x00, 0x07, 0x6c, 0x02, 0x00, 0x68, 0x03, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x6c, 0x05, 0x00, 0x00, 0x13, 0x7c, 0x02, 0x00,
+ 0x70, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x87, 0x05, 0x00, 0x00,
+ 0x07, 0x84, 0x02, 0x00, 0x80, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xad, 0x05, 0x00, 0x00, 0x15, 0x90, 0x02, 0x00, 0x90, 0x03, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xad, 0x05, 0x00, 0x00, 0x08, 0x90, 0x02, 0x00,
+ 0xa0, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xdf, 0x05, 0x00, 0x00,
+ 0x15, 0x9c, 0x02, 0x00, 0xb8, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x03, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xfc, 0x05, 0x00, 0x00, 0x02, 0xe0, 0x02, 0x00,
+ 0xf0, 0x03, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x11, 0x06, 0x00, 0x00,
+ 0x0e, 0xf0, 0x02, 0x00, 0x08, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x11, 0x06, 0x00, 0x00, 0x07, 0xf0, 0x02, 0x00, 0x10, 0x04, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x3b, 0x06, 0x00, 0x00, 0x13, 0x08, 0x03, 0x00,
+ 0x28, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x3b, 0x06, 0x00, 0x00,
+ 0x2a, 0x08, 0x03, 0x00, 0x30, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x67, 0x06, 0x00, 0x00, 0x0f, 0x00, 0x03, 0x00, 0x40, 0x04, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x8d, 0x06, 0x00, 0x00, 0x12, 0x0c, 0x03, 0x00,
+ 0x50, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x8d, 0x06, 0x00, 0x00,
+ 0x23, 0x0c, 0x03, 0x00, 0x58, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xb2, 0x06, 0x00, 0x00, 0x15, 0x10, 0x03, 0x00, 0x70, 0x04, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xb2, 0x06, 0x00, 0x00, 0x46, 0x10, 0x03, 0x00,
+ 0x98, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xfa, 0x06, 0x00, 0x00,
+ 0x0e, 0x30, 0x03, 0x00, 0xb0, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xfa, 0x06, 0x00, 0x00, 0x07, 0x30, 0x03, 0x00, 0xb8, 0x04, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00, 0x10, 0x44, 0x03, 0x00,
+ 0xc0, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x24, 0x07, 0x00, 0x00,
+ 0x20, 0x44, 0x03, 0x00, 0xd8, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x24, 0x07, 0x00, 0x00, 0x07, 0x44, 0x03, 0x00, 0xe0, 0x04, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x5c, 0x07, 0x00, 0x00, 0x0f, 0x54, 0x03, 0x00,
+ 0xf0, 0x04, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x82, 0x07, 0x00, 0x00,
+ 0x13, 0x5c, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x82, 0x07, 0x00, 0x00, 0x2a, 0x5c, 0x03, 0x00, 0x08, 0x05, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x8d, 0x06, 0x00, 0x00, 0x12, 0x60, 0x03, 0x00,
+ 0x18, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x8d, 0x06, 0x00, 0x00,
+ 0x23, 0x60, 0x03, 0x00, 0x20, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xb2, 0x06, 0x00, 0x00, 0x15, 0x64, 0x03, 0x00, 0x30, 0x05, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xb2, 0x06, 0x00, 0x00, 0x46, 0x64, 0x03, 0x00,
+ 0x48, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xae, 0x07, 0x00, 0x00,
+ 0x1a, 0x70, 0x03, 0x00, 0x60, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xae, 0x07, 0x00, 0x00, 0x32, 0x70, 0x03, 0x00, 0x70, 0x05, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x90, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xe2, 0x07, 0x00, 0x00,
+ 0x12, 0x74, 0x03, 0x00, 0x98, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xe2, 0x07, 0x00, 0x00, 0x24, 0x74, 0x03, 0x00, 0xa0, 0x05, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xb2, 0x06, 0x00, 0x00, 0x15, 0x78, 0x03, 0x00,
+ 0xb8, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xb2, 0x06, 0x00, 0x00,
+ 0x46, 0x78, 0x03, 0x00, 0xd8, 0x05, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x00, 0x00, 0x18, 0xac, 0x03, 0x00, 0xf0, 0x05, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x0d, 0xac, 0x03, 0x00,
+ 0x10, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x39, 0x08, 0x00, 0x00,
+ 0x11, 0xd0, 0x03, 0x00, 0x18, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x39, 0x08, 0x00, 0x00, 0x06, 0xd0, 0x03, 0x00, 0x28, 0x06, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x64, 0x08, 0x00, 0x00, 0x19, 0xd4, 0x03, 0x00,
+ 0x78, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x84, 0x08, 0x00, 0x00,
+ 0x07, 0xe0, 0x03, 0x00, 0x90, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x92, 0x08, 0x00, 0x00, 0x11, 0xe4, 0x03, 0x00, 0x98, 0x06, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xad, 0x08, 0x00, 0x00, 0x18, 0xe8, 0x03, 0x00,
+ 0xa0, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xad, 0x08, 0x00, 0x00,
+ 0x11, 0xe8, 0x03, 0x00, 0xa8, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xcb, 0x08, 0x00, 0x00, 0x18, 0xec, 0x03, 0x00, 0xb0, 0x06, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xcb, 0x08, 0x00, 0x00, 0x11, 0xec, 0x03, 0x00,
+ 0xc8, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xe9, 0x08, 0x00, 0x00,
+ 0x0f, 0xfc, 0x03, 0x00, 0xd0, 0x06, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x03, 0x09, 0x00, 0x00, 0x16, 0x00, 0x04, 0x00, 0x20, 0x07, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x1f, 0x09, 0x00, 0x00, 0x16, 0x04, 0x04, 0x00,
+ 0x78, 0x07, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x3b, 0x09, 0x00, 0x00, 0x0d, 0x18, 0x04, 0x00, 0xb8, 0x07, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x86, 0x09, 0x00, 0x00, 0x03, 0x1c, 0x04, 0x00,
+ 0xe0, 0x07, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x97, 0x09, 0x00, 0x00,
+ 0x08, 0x28, 0x04, 0x00, 0xe8, 0x07, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x97, 0x09, 0x00, 0x00, 0x15, 0x28, 0x04, 0x00, 0xf0, 0x07, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x97, 0x09, 0x00, 0x00, 0x20, 0x28, 0x04, 0x00,
+ 0xf8, 0x07, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xb9, 0x09, 0x00, 0x00,
+ 0x08, 0x2c, 0x04, 0x00, 0x00, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xb9, 0x09, 0x00, 0x00, 0x15, 0x2c, 0x04, 0x00, 0x08, 0x08, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xb9, 0x09, 0x00, 0x00, 0x20, 0x2c, 0x04, 0x00,
+ 0x10, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xdb, 0x09, 0x00, 0x00,
+ 0x08, 0x30, 0x04, 0x00, 0x18, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0xdb, 0x09, 0x00, 0x00, 0x15, 0x30, 0x04, 0x00, 0x20, 0x08, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x97, 0x09, 0x00, 0x00, 0x08, 0x28, 0x04, 0x00,
+ 0x28, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0xfd, 0x09, 0x00, 0x00,
+ 0x08, 0x44, 0x04, 0x00, 0x38, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x11, 0x0a, 0x00, 0x00, 0x1e, 0x48, 0x04, 0x00, 0x40, 0x08, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x18, 0x48, 0x04, 0x00,
+ 0x50, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00, 0x37, 0x0a, 0x00, 0x00,
+ 0x04, 0x58, 0x04, 0x00, 0xb0, 0x08, 0x00, 0x00, 0xce, 0x00, 0x00, 0x00,
+ 0x73, 0x0a, 0x00, 0x00, 0x09, 0x88, 0x04, 0x00, 0xd8, 0x08, 0x00, 0x00,
+ 0xce, 0x00, 0x00, 0x00, 0xb0, 0x0a, 0x00, 0x00, 0x01, 0x8c, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xd8, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x28, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xd8, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xd8, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xa8, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xb8, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x98, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xd8, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x48, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xb0, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xc0, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x70, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xe0, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0xf0, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x4b, 0x00, 0x00, 0x00, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x11, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2b, 0x00, 0x00, 0x00, 0x11, 0x00, 0x05, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x42, 0x00, 0x00, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00, 0xb8, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x74, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x13, 0x00, 0x00, 0x00, 0x8c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x80, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xe0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xf0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x70, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xa0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xb0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x30, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x60, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x70, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x90, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xb0, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xc0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xd0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xf0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x20, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x30, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x50, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x60, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x70, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x80, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x90, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xb0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xe0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xf0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x10, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x20, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x30, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x40, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x70, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x80, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x90, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xa0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xb0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0xd0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xe0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xf0, 0x06, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x30, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x1a, 0x1b, 0x1c, 0x1d, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e,
+ 0x72, 0x65, 0x6c, 0x2e, 0x42, 0x54, 0x46, 0x2e, 0x65, 0x78, 0x74, 0x00,
+ 0x2e, 0x6d, 0x61, 0x70, 0x73, 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x78, 0x64,
+ 0x70, 0x00, 0x6f, 0x70, 0x74, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x00, 0x78,
+ 0x73, 0x6b, 0x73, 0x5f, 0x6d, 0x61, 0x70, 0x00, 0x2e, 0x6c, 0x6c, 0x76,
+ 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x69, 0x67, 0x00, 0x5f, 0x6c,
+ 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00, 0x78, 0x64, 0x70, 0x5f, 0x72,
+ 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6e, 0x73, 0x5f,
+ 0x66, 0x75, 0x6e, 0x63, 0x00, 0x2e, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62,
+ 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x72, 0x65,
+ 0x6c, 0x2e, 0x42, 0x54, 0x46, 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x39,
+ 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x35, 0x38, 0x00, 0x4c, 0x42, 0x42,
+ 0x30, 0x5f, 0x35, 0x37, 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x31, 0x37,
+ 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x35, 0x36, 0x00, 0x4c, 0x42, 0x42,
+ 0x30, 0x5f, 0x34, 0x36, 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x33, 0x36,
+ 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x31, 0x36, 0x00, 0x4c, 0x42, 0x42,
+ 0x30, 0x5f, 0x35, 0x35, 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x34, 0x35,
+ 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x34, 0x00, 0x4c, 0x42, 0x42, 0x30,
+ 0x5f, 0x35, 0x34, 0x00, 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x32, 0x33, 0x00,
+ 0x4c, 0x42, 0x42, 0x30, 0x5f, 0x34, 0x30, 0x00, 0x4c, 0x42, 0x42, 0x30,
+ 0x5f, 0x33, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc4, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x40, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x84, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x0d, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x16, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x60, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x90, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x03, 0x4c, 0xff, 0x6f,
+ 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xc0, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x1e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int bpf_kernel_o_len = 11256;
diff --git a/src/libknot/xdp/bpf-kernel-obj.h b/src/libknot/xdp/bpf-kernel-obj.h
new file mode 100644
index 0000000..7c3d759
--- /dev/null
+++ b/src/libknot/xdp/bpf-kernel-obj.h
@@ -0,0 +1,2 @@
+extern unsigned char bpf_kernel_o[];
+extern unsigned int bpf_kernel_o_len;
diff --git a/src/libknot/xdp/bpf-kernel.c b/src/libknot/xdp/bpf-kernel.c
new file mode 100644
index 0000000..8bffd7f
--- /dev/null
+++ b/src/libknot/xdp/bpf-kernel.c
@@ -0,0 +1,293 @@
+/* Copyright (C) 2022 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 <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+
+#include "bpf-consts.h"
+#include "../../contrib/libbpf/include/uapi/linux/bpf.h"
+#include "../../contrib/libbpf/bpf/bpf_endian.h"
+#include "../../contrib/libbpf/bpf/bpf_helpers.h"
+
+/* Don't fragment flag. */
+#define IP_DF 0x4000
+
+#define AF_INET 2
+#define AF_INET6 10
+
+/* Define maximum reasonable number of NIC queues supported. */
+#define QUEUE_MAX 256
+
+/* A map of configuration options. */
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, QUEUE_MAX);
+ __uint(key_size, sizeof(__u32)); /* Must be 4 bytes. */
+ __uint(value_size, sizeof(knot_xdp_opts_t));
+} opts_map SEC(".maps");
+
+/* A map of AF_XDP sockets. */
+struct {
+ __uint(type, BPF_MAP_TYPE_XSKMAP);
+ __uint(max_entries, QUEUE_MAX);
+ __uint(key_size, sizeof(__u32)); /* Must be 4 bytes. */
+ __uint(value_size, sizeof(int));
+} xsks_map SEC(".maps");
+
+struct ipv6_frag_hdr {
+ unsigned char nexthdr;
+ unsigned char whatever[7];
+} __attribute__((packed));
+
+SEC("xdp")
+int xdp_redirect_dns_func(struct xdp_md *ctx)
+{
+ /* Get the queue options. */
+ __u32 index = ctx->rx_queue_index;
+ struct knot_xdp_opts *opts_ptr = bpf_map_lookup_elem(&opts_map, &index);
+ if (!opts_ptr) {
+ return XDP_ABORTED;
+ }
+ knot_xdp_opts_t opts = *opts_ptr;
+
+ /* Check if the filter is disabled. */
+ if (!(opts.flags & KNOT_XDP_FILTER_ON)) {
+ return XDP_PASS;
+ }
+
+ /* Try to reserve space in front of the packet for additional (VLAN) data. */
+ (void)bpf_xdp_adjust_meta(ctx, - (int)sizeof(struct knot_xdp_info)
+ - KNOT_XDP_PKT_ALIGNMENT);
+
+ void *data = (void *)(long)ctx->data;
+ const void *data_end = (void *)(long)ctx->data_end;
+ struct knot_xdp_info *meta = (void *)(long)ctx->data_meta;
+
+ /* Check if the meta data pointer is usable (e.g. not `tap` interface). */
+ if ((void *)meta + sizeof(*meta) > data) {
+ meta = 0;
+ }
+
+ struct ethhdr *eth_hdr = data;
+ const void *ip_hdr;
+ const struct iphdr *ip4;
+ const struct ipv6hdr *ip6;
+ const void *l4_hdr;
+ __u8 ipv4;
+ __u8 ip_proto;
+ __u8 fragmented = 0;
+ __u16 eth_type; /* In big endian. */
+
+ /* Parse Ethernet header. */
+ if ((void *)eth_hdr + sizeof(*eth_hdr) > data_end) {
+ return XDP_DROP;
+ }
+ data += sizeof(*eth_hdr);
+
+ /* Parse possible VLAN (802.1Q) header. */
+ if (eth_hdr->h_proto == __constant_htons(ETH_P_8021Q)) {
+ if (data + sizeof(__u16) + sizeof(eth_type) > data_end) {
+ return XDP_DROP;
+ } else if (meta == 0) { /* VLAN not supported. */
+ return XDP_PASS;
+ }
+ __builtin_memcpy(&eth_type, data + sizeof(__u16), sizeof(eth_type));
+ data += sizeof(__u16) + sizeof(eth_type);
+ } else {
+ eth_type = eth_hdr->h_proto;
+ }
+
+ ip_hdr = data;
+
+ /* Parse IPv4 or IPv6 header. */
+ switch (eth_type) {
+ case __constant_htons(ETH_P_IP):
+ ip4 = ip_hdr;
+ if ((void *)ip4 + sizeof(*ip4) > data_end) {
+ return XDP_DROP;
+ }
+ if (ip4->version != 4) {
+ return XDP_DROP;
+ }
+
+ /* Check the IP length. Cannot use strict equality due to
+ * Ethernet padding applied to frames shorter than 64 octects. */
+ if (data_end - data < __bpf_ntohs(ip4->tot_len)) {
+ return XDP_DROP;
+ }
+
+ if (ip4->frag_off != 0 &&
+ ip4->frag_off != __constant_htons(IP_DF)) {
+ fragmented = 1;
+ }
+ ip_proto = ip4->protocol;
+ l4_hdr = data + ip4->ihl * 4;
+ ipv4 = 1;
+ break;
+ case __constant_htons(ETH_P_IPV6):
+ ip6 = ip_hdr;
+ if ((void *)ip6 + sizeof(*ip6) > data_end) {
+ return XDP_DROP;
+ }
+ if (ip6->version != 6) {
+ return XDP_DROP;
+ }
+
+ /* Check the IP length. Cannot use strict equality due to
+ * Ethernet padding applied to frames shorter than 64 octects. */
+ if (data_end - data < __bpf_ntohs(ip6->payload_len) + sizeof(*ip6)) {
+ return XDP_DROP;
+ }
+
+ ip_proto = ip6->nexthdr;
+ data += sizeof(*ip6);
+ if (ip_proto == IPPROTO_FRAGMENT) {
+ fragmented = 1;
+ const struct ipv6_frag_hdr *frag = data;
+ if ((void *)frag + sizeof(*frag) > data_end) {
+ return XDP_DROP;
+ }
+ ip_proto = frag->nexthdr;
+ data += sizeof(*frag);
+ }
+ l4_hdr = data;
+ ipv4 = 0;
+ break;
+ default:
+ /* Pass packets of possible other protocols. */
+ return XDP_PASS;
+ }
+
+ const struct tcphdr *tcp;
+ const struct udphdr *udp;
+ __u16 port_dest;
+ __u8 match = 0;
+
+ /* Check the transport protocol. */
+ switch (ip_proto) {
+ case IPPROTO_TCP:
+ /* Parse TCP header. */
+ tcp = l4_hdr;
+ if (l4_hdr + sizeof(*tcp) > data_end) {
+ return XDP_DROP;
+ }
+
+ port_dest = __bpf_ntohs(tcp->dest);
+
+ if ((opts.flags & KNOT_XDP_FILTER_TCP) &&
+ (port_dest == opts.udp_port ||
+ ((opts.flags & (KNOT_XDP_FILTER_PASS | KNOT_XDP_FILTER_DROP)) &&
+ port_dest >= opts.udp_port))) {
+ match = 1;
+ }
+ break;
+ case IPPROTO_UDP:
+ /* Parse UDP header. */
+ udp = l4_hdr;
+ if (l4_hdr + sizeof(*udp) > data_end) {
+ return XDP_DROP;
+ }
+
+ /* Check the UDP length. */
+ if (data_end - (void *)udp < __bpf_ntohs(udp->len)) {
+ return XDP_DROP;
+ }
+
+ port_dest = __bpf_ntohs(udp->dest);
+
+ if ((opts.flags & KNOT_XDP_FILTER_UDP) &&
+ (port_dest == opts.udp_port ||
+ ((opts.flags & (KNOT_XDP_FILTER_PASS | KNOT_XDP_FILTER_DROP)) &&
+ port_dest >= opts.udp_port))) {
+ match = 1;
+ } else if ((opts.flags & KNOT_XDP_FILTER_QUIC) &&
+ (port_dest == opts.quic_port ||
+ ((opts.flags & (KNOT_XDP_FILTER_PASS | KNOT_XDP_FILTER_DROP)) &&
+ port_dest >= opts.quic_port))) {
+ match = 1;
+ }
+ break;
+ default:
+ /* Pass packets of possible other protocols. */
+ return XDP_PASS;
+ }
+
+ if (!match) {
+ /* Pass non-matching packet. */
+ return XDP_PASS;
+ } else if (opts.flags & KNOT_XDP_FILTER_DROP) {
+ /* Drop matching packet if requested. */
+ return XDP_DROP;
+ } else if (fragmented) {
+ /* Drop fragmented packet. */
+ return XDP_DROP;
+ }
+
+ /* Take into account routing information. */
+ if (opts.flags & KNOT_XDP_FILTER_ROUTE) {
+ struct bpf_fib_lookup fib = {
+ .ifindex = 1 /* Loopback. */
+ };
+ if (ipv4) {
+ fib.family = AF_INET;
+ fib.ipv4_src = ip4->daddr;
+ fib.ipv4_dst = ip4->saddr;
+ } else {
+ struct in6_addr *ipv6_src = (struct in6_addr *)fib.ipv6_src;
+ struct in6_addr *ipv6_dst = (struct in6_addr *)fib.ipv6_dst;
+ fib.family = AF_INET6;
+ *ipv6_src = ip6->daddr;
+ *ipv6_dst = ip6->saddr;
+ }
+
+ const __u16 *mac_in = (const __u16 *)eth_hdr->h_dest;
+ const __u16 *mac_out = (const __u16 *)fib.smac;
+ int ret = bpf_fib_lookup(ctx, &fib, sizeof(fib), BPF_FIB_LOOKUP_DIRECT);
+ switch (ret) {
+ case BPF_FIB_LKUP_RET_SUCCESS:
+ /* Cross-interface answers are handled through normal stack. */
+ if (mac_in[0] != mac_out[0] ||
+ mac_in[1] != mac_out[1] ||
+ mac_in[2] != mac_out[2]) {
+ return XDP_PASS;
+ }
+
+ /* Store output interface index for later use with VLAN in user space. */
+ if (meta != 0) {
+ meta->out_if_index = fib.ifindex;
+ }
+
+ /* Update destination MAC for responding. */
+ __builtin_memcpy(eth_hdr->h_source, fib.dmac, ETH_ALEN);
+ break;
+ case BPF_FIB_LKUP_RET_FWD_DISABLED: /* Disabled forwarding on loopback. */
+ return XDP_ABORTED;
+ case BPF_FIB_LKUP_RET_NO_NEIGH: /* Use normal stack to obtain MAC. */
+ return XDP_PASS;
+ default:
+ return XDP_DROP;
+ }
+ }
+
+ /* Forward the packet to user space. */
+ return bpf_redirect_map(&xsks_map, ctx->rx_queue_index, 0);
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/src/libknot/xdp/bpf-user.c b/src/libknot/xdp/bpf-user.c
new file mode 100644
index 0000000..a0c8bae
--- /dev/null
+++ b/src/libknot/xdp/bpf-user.c
@@ -0,0 +1,313 @@
+/* Copyright (C) 2022 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 <bpf/bpf.h>
+#include <linux/if_link.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libknot/endian.h"
+#include "libknot/error.h"
+#include "libknot/xdp/bpf-kernel-obj.h"
+#include "libknot/xdp/bpf-user.h"
+#include "libknot/xdp/eth.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define NO_BPF_MAPS 2
+
+static inline bool IS_ERR_OR_NULL(const void *ptr)
+{
+ return (ptr == NULL) || (unsigned long)ptr >= (unsigned long)-4095;
+}
+
+static int prog_load(struct bpf_object **pobj, int *prog_fd)
+{
+ struct bpf_program *prog, *first_prog = NULL;
+ struct bpf_object *obj;
+
+ obj = bpf_object__open_mem(bpf_kernel_o, bpf_kernel_o_len, NULL);
+ if (IS_ERR_OR_NULL(obj)) {
+ return KNOT_ENOENT;
+ }
+
+ bpf_object__for_each_program(prog, obj) {
+ bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
+ if (first_prog == NULL) {
+ first_prog = prog;
+ }
+ }
+
+ if (first_prog == NULL) {
+ bpf_object__close(obj);
+ return KNOT_ENOENT;
+ }
+
+ int ret = bpf_object__load(obj);
+ if (ret != 0) {
+ bpf_object__close(obj);
+ return KNOT_EINVAL;
+ }
+
+ *pobj = obj;
+ *prog_fd = bpf_program__fd(first_prog);
+
+ return KNOT_EOK;
+}
+
+static int ensure_prog(struct kxsk_iface *iface, bool overwrite)
+{
+ if (bpf_kernel_o_len < 2) {
+ return KNOT_ENOTSUP;
+ }
+
+ /* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
+ * loading this into the kernel via bpf-syscall. */
+ int prog_fd;
+ int ret = prog_load(&iface->prog_obj, &prog_fd);
+ if (ret != KNOT_EOK) {
+ return KNOT_EPROGRAM;
+ }
+
+#if USE_LIBXDP
+ ret = bpf_xdp_attach(iface->if_index, prog_fd,
+ overwrite ? 0 : XDP_FLAGS_UPDATE_IF_NOEXIST, NULL);
+#else
+ ret = bpf_set_link_xdp_fd(iface->if_index, prog_fd,
+ overwrite ? 0 : XDP_FLAGS_UPDATE_IF_NOEXIST);
+#endif
+ if (ret != 0) {
+ close(prog_fd);
+ }
+ if (ret == -EBUSY && !overwrite) { // We try accepting the present program.
+ uint32_t prog_id = 0;
+#if USE_LIBXDP
+ ret = bpf_xdp_query_id(iface->if_index, 0, &prog_id);
+#else
+ ret = bpf_get_link_xdp_id(iface->if_index, &prog_id, 0);
+#endif
+ if (ret == 0 && prog_id != 0) {
+ ret = prog_fd = bpf_prog_get_fd_by_id(prog_id);
+ }
+ }
+ if (ret < 0) {
+ return KNOT_EFD;
+ } else {
+ return prog_fd;
+ }
+}
+
+static void unget_bpf_maps(struct kxsk_iface *iface)
+{
+ if (iface->opts_map_fd >= 0) {
+ close(iface->opts_map_fd);
+ }
+ if (iface->xsks_map_fd >= 0) {
+ close(iface->xsks_map_fd);
+ }
+ iface->opts_map_fd = iface->xsks_map_fd = -1;
+}
+
+/*!
+ * /brief Get FDs for the two maps and assign them into xsk_info-> fields.
+ *
+ * Inspired by xsk_lookup_bpf_maps() from libbpf before qidconf_map elimination.
+ */
+static int get_bpf_maps(int prog_fd, struct kxsk_iface *iface)
+{
+ uint32_t *map_ids = calloc(NO_BPF_MAPS, sizeof(*map_ids));
+ if (map_ids == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ struct bpf_prog_info prog_info = {
+ .nr_map_ids = NO_BPF_MAPS,
+ .map_ids = (__u64)(unsigned long)map_ids,
+ };
+
+ uint32_t prog_len = sizeof(struct bpf_prog_info);
+ int ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_len);
+ if (ret != 0) {
+ free(map_ids);
+ return ret;
+ }
+
+ for (int i = 0; i < NO_BPF_MAPS; ++i) {
+ int fd = bpf_map_get_fd_by_id(map_ids[i]);
+ if (fd < 0) {
+ continue;
+ }
+
+ struct bpf_map_info map_info = { 0 };
+ uint32_t map_len = sizeof(struct bpf_map_info);
+ ret = bpf_obj_get_info_by_fd(fd, &map_info, &map_len);
+ if (ret != 0) {
+ close(fd);
+ continue;
+ }
+
+ if (strcmp(map_info.name, "opts_map") == 0) {
+ iface->opts_map_fd = fd;
+ continue;
+ }
+
+ if (strcmp(map_info.name, "xsks_map") == 0) {
+ iface->xsks_map_fd = fd;
+ continue;
+ }
+
+ close(fd);
+ }
+
+ if (iface->opts_map_fd < 0 || iface->xsks_map_fd < 0) {
+ unget_bpf_maps(iface);
+ free(map_ids);
+ return KNOT_ENOENT;
+ }
+
+ free(map_ids);
+ return KNOT_EOK;
+}
+
+int kxsk_socket_start(const struct kxsk_iface *iface, knot_xdp_filter_flag_t flags,
+ uint16_t udp_port, uint16_t quic_port, struct xsk_socket *xsk)
+{
+ if (iface == NULL || xsk == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int fd = xsk_socket__fd(xsk);
+ int ret = bpf_map_update_elem(iface->xsks_map_fd, &iface->if_queue, &fd, 0);
+ if (ret != 0) {
+ return ret;
+ }
+
+ knot_xdp_opts_t opts = {
+ .flags = flags | KNOT_XDP_FILTER_ON,
+ .udp_port = udp_port,
+ .quic_port = quic_port,
+ };
+
+ ret = bpf_map_update_elem(iface->opts_map_fd, &iface->if_queue, &opts, 0);
+ if (ret != 0) {
+ (void)bpf_map_delete_elem(iface->xsks_map_fd, &iface->if_queue);
+ }
+
+ return ret;
+}
+
+void kxsk_socket_stop(const struct kxsk_iface *iface)
+{
+ if (iface == NULL) {
+ return;
+ }
+
+ knot_xdp_opts_t opts = { 0 };
+
+ (void)bpf_map_update_elem(iface->opts_map_fd, &iface->if_queue, &opts, 0);
+ (void)bpf_map_delete_elem(iface->xsks_map_fd, &iface->if_queue);
+}
+
+int kxsk_iface_new(const char *if_name, unsigned if_queue, knot_xdp_load_bpf_t load_bpf,
+ struct kxsk_iface **out_iface)
+{
+ if (if_name == NULL || out_iface == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct kxsk_iface *iface = calloc(1, sizeof(*iface) + IFNAMSIZ);
+ if (iface == NULL) {
+ return KNOT_ENOMEM;
+ }
+ iface->if_name = (char *)(iface + 1);
+ strlcpy((char *)iface->if_name, if_name, IFNAMSIZ);
+ iface->if_index = if_nametoindex(if_name);
+ if (iface->if_index == 0) {
+ free(iface);
+ return KNOT_EINVAL;
+ }
+ iface->if_queue = if_queue;
+ iface->opts_map_fd = iface->xsks_map_fd = -1;
+
+ int ret;
+ switch (load_bpf) {
+ case KNOT_XDP_LOAD_BPF_NEVER:
+ (void)0;
+ uint32_t prog_id = 0;
+#if USE_LIBXDP
+ ret = bpf_xdp_query_id(iface->if_index, 0, &prog_id);
+#else
+ ret = bpf_get_link_xdp_id(iface->if_index, &prog_id, 0);
+#endif
+ if (ret == 0) {
+ if (prog_id == 0) {
+ ret = KNOT_EPROGRAM;
+ } else {
+ ret = bpf_prog_get_fd_by_id(prog_id);
+ }
+ }
+ break;
+ case KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD:
+#if USE_LIBXDP
+ (void)bpf_xdp_detach(iface->if_index, 0, NULL);
+#else
+ (void)bpf_set_link_xdp_fd(iface->if_index, -1, 0);
+#endif
+ sleep(1);
+ // FALLTHROUGH
+ case KNOT_XDP_LOAD_BPF_ALWAYS:
+ ret = ensure_prog(iface, true);
+ break;
+ case KNOT_XDP_LOAD_BPF_MAYBE:
+ ret = ensure_prog(iface, false);
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ if (ret >= 0) {
+ ret = get_bpf_maps(ret, iface);
+ }
+ if (ret < 0) {
+ free(iface);
+ return ret;
+ }
+
+ knot_xdp_mode_t mode = knot_eth_xdp_mode(iface->if_index);
+ if (mode == KNOT_XDP_MODE_NONE) {
+ free(iface);
+ return KNOT_ENOTSUP;
+ }
+
+ *out_iface = iface;
+ return KNOT_EOK;
+}
+
+void kxsk_iface_free(struct kxsk_iface *iface)
+{
+ if (iface == NULL) {
+ return;
+ }
+
+ unget_bpf_maps(iface);
+
+ if (iface->prog_obj != NULL) {
+ bpf_object__close(iface->prog_obj);
+ }
+
+ free(iface);
+}
diff --git a/src/libknot/xdp/bpf-user.h b/src/libknot/xdp/bpf-user.h
new file mode 100644
index 0000000..14c54c5
--- /dev/null
+++ b/src/libknot/xdp/bpf-user.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief XDP socket interface.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#if USE_LIBXDP
+ #include <xdp/xsk.h>
+#else
+ #include <bpf/xsk.h>
+#endif
+
+#include "libknot/xdp/xdp.h"
+
+struct kxsk_iface {
+ /*! Interface name. */
+ const char *if_name;
+ /*! Interface name index (derived from ifname). */
+ int if_index;
+ /*! Network card queue id. */
+ unsigned if_queue;
+
+ /*! Configuration BPF map file descriptor. */
+ int opts_map_fd;
+ /*! XSK BPF map file descriptor. */
+ int xsks_map_fd;
+
+ /*! BPF program object. */
+ struct bpf_object *prog_obj;
+};
+
+struct kxsk_umem {
+ /*! Fill queue: passing memory frames to kernel - ready to receive. */
+ struct xsk_ring_prod fq;
+ /*! Completion queue: passing memory frames from kernel - after send finishes. */
+ struct xsk_ring_cons cq;
+ /*! Handle internal to libbpf. */
+ struct xsk_umem *umem;
+
+ /*! The memory frames. */
+ struct umem_frame *frames;
+ /*! The number of free frames (for TX). */
+ uint32_t tx_free_count;
+ /*! Stack of indices of the free frames (for TX). */
+ uint16_t tx_free_indices[];
+};
+
+struct knot_xdp_socket {
+ /*! Receive queue: passing arrived packets from kernel. */
+ struct xsk_ring_cons rx;
+ /*! Transmit queue: passing packets to kernel for sending. */
+ struct xsk_ring_prod tx;
+ /*! Information about memory frames for all the passed packets. */
+ struct kxsk_umem *umem;
+ /*! Handle internal to libbpf. */
+ struct xsk_socket *xsk;
+
+ /*! Interface context. */
+ const struct kxsk_iface *iface;
+
+ /*! If non-NULL, it's a mocked socket with this send function. */
+ int (*send_mock)(struct knot_xdp_socket *, const knot_xdp_msg_t[], uint32_t, uint32_t *);
+
+ /*! The kernel has to be woken up by a syscall indication. */
+ bool kernel_needs_wakeup;
+
+ /*! The limit of frame size. */
+ unsigned frame_limit;
+
+ /*! Mapping of interface indices to VLAN tags. */
+ uint16_t *vlan_map;
+ uint16_t vlan_map_max;
+};
+
+/*!
+ * \brief Set up BPF program and map for one XDP socket.
+ *
+ * \param if_name Name of the net iface (e.g. eth0).
+ * \param if_queue Network card queue id.
+ * \param load_bpf Insert BPF program into packet processing.
+ * \param out_iface Output: created interface context.
+ *
+ * \return KNOT_E* or -errno
+ */
+int kxsk_iface_new(const char *if_name, unsigned if_queue, knot_xdp_load_bpf_t load_bpf,
+ struct kxsk_iface **out_iface);
+
+/*!
+ * \brief Unload BPF maps for a socket.
+ *
+ * \note This keeps the loaded BPF program. We don't care.
+ *
+ * \param iface Interface context to be freed.
+ */
+void kxsk_iface_free(struct kxsk_iface *iface);
+
+/*!
+ * \brief Activate this AF_XDP socket through the BPF maps.
+ *
+ * \param iface Interface context.
+ * \param flags XDP filter configuration flags.
+ * \param udp_port UDP and/or TCP port to listen on if enabled via \a opts.
+ * \param quic_port QUIC/UDP port to listen on if enabled via \a opts.
+ * \param xsk Socket ctx.
+ *
+ * \return KNOT_E* or -errno
+ */
+int kxsk_socket_start(const struct kxsk_iface *iface, knot_xdp_filter_flag_t flags,
+ uint16_t udp_port, uint16_t quic_port, struct xsk_socket *xsk);
+
+/*!
+ * \brief Deactivate this AF_XDP socket through the BPF maps.
+ *
+ * \param iface Interface context.
+ */
+void kxsk_socket_stop(const struct kxsk_iface *iface);
+
+/*! @} */
diff --git a/src/libknot/xdp/eth.c b/src/libknot/xdp/eth.c
new file mode 100644
index 0000000..608eb20
--- /dev/null
+++ b/src/libknot/xdp/eth.c
@@ -0,0 +1,312 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <linux/ethtool.h>
+#include <linux/if_link.h>
+#include <linux/if_vlan.h>
+#include <linux/sockios.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/sockaddr.h"
+#include "libknot/attribute.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "libknot/xdp/eth.h"
+
+_public_
+int knot_eth_queues(const char *devname)
+{
+ if (devname == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return knot_map_errno();
+ }
+
+ struct ethtool_channels ch = {
+ .cmd = ETHTOOL_GCHANNELS
+ };
+ struct ifreq ifr = {
+ .ifr_data = (char *)&ch
+ };
+ strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
+
+ int ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret != 0) {
+ if (errno == EOPNOTSUPP) {
+ ret = 1;
+ } else {
+ ret = knot_map_errno();
+ }
+ } else {
+ if (ch.combined_count == 0) {
+ ret = 1;
+ } else {
+ ret = ch.combined_count;
+ }
+ }
+
+ close(fd);
+ return ret;
+}
+
+_public_
+int knot_eth_rss(const char *devname, knot_eth_rss_conf_t **rss_conf)
+{
+ if (devname == NULL || rss_conf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct ethtool_rxfh *ctx = NULL;
+ knot_eth_rss_conf_t *out = NULL;
+ int ret = KNOT_ERROR;
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return knot_map_errno();
+ }
+
+ struct ethtool_rxfh sizes = {
+ .cmd = ETHTOOL_GRSSH
+ };
+ struct ifreq ifr = {
+ .ifr_data = (char *)&sizes
+ };
+ strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
+
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret != 0) {
+ ret = knot_map_errno();
+ goto finish;
+ }
+
+ const unsigned data_size = sizes.indir_size * sizeof(sizes.rss_config[0]) +
+ sizes.key_size;
+
+ ctx = calloc(1, sizeof(*ctx) + data_size);
+ if (ctx == NULL) {
+ ret = KNOT_ENOMEM;
+ goto finish;
+ }
+ ctx->cmd = ETHTOOL_GRSSH;
+ ctx->indir_size = sizes.indir_size;
+ ctx->key_size = sizes.key_size;
+ ifr.ifr_data = (char *)ctx;
+
+ ret = ioctl(fd, SIOCETHTOOL, &ifr);
+ if (ret != 0) {
+ ret = knot_map_errno();
+ goto finish;
+ }
+
+ out = calloc(1, sizeof(*out) + data_size);
+ if (out == NULL) {
+ ret = KNOT_ENOMEM;
+ goto finish;
+ }
+
+ out->table_size = sizes.indir_size;
+ out->key_size = sizes.key_size;
+ memcpy(out->data, ctx->rss_config, data_size);
+ out->mask = out->table_size - 1;
+finish:
+ *rss_conf = out;
+
+ free(ctx);
+ close(fd);
+ return ret;
+}
+
+_public_
+int knot_eth_mtu(const char *devname)
+{
+ if (devname == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return knot_map_errno();
+ }
+
+ struct ifreq ifr = { 0 };
+ strlcpy(ifr.ifr_name, devname, IFNAMSIZ);
+
+ int ret = ioctl(fd, SIOCGIFMTU, &ifr);
+ if (ret != 0) {
+ if (errno == EOPNOTSUPP) {
+ ret = KNOT_ENOTSUP;
+ } else {
+ ret = knot_map_errno();
+ }
+ } else {
+ ret = ifr.ifr_mtu;
+ }
+
+ close(fd);
+ return ret;
+}
+
+_public_
+int knot_eth_name_from_addr(const struct sockaddr_storage *addr, char *out,
+ size_t out_len)
+{
+ if (addr == NULL || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct ifaddrs *ifaces = NULL;
+ if (getifaddrs(&ifaces) != 0) {
+ return -errno;
+ }
+
+ size_t matches = 0;
+ char *match_name = NULL;
+
+ for (struct ifaddrs *ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) {
+ const struct sockaddr_storage *ifss = (struct sockaddr_storage *)ifa->ifa_addr;
+ if (ifss == NULL) { // Observed on interfaces without any address.
+ continue;
+ }
+
+ if ((ifss->ss_family == addr->ss_family && sockaddr_is_any(addr)) ||
+ sockaddr_cmp(ifss, addr, true) == 0) {
+ matches++;
+ match_name = ifa->ifa_name;
+ }
+ }
+
+ if (matches == 1) {
+ size_t len = strlcpy(out, match_name, out_len);
+ freeifaddrs(ifaces);
+ return (len >= out_len) ? KNOT_ESPACE : KNOT_EOK;
+ }
+
+ freeifaddrs(ifaces);
+ return matches == 0 ? KNOT_EADDRNOTAVAIL : KNOT_ELIMIT;
+}
+
+_public_
+int knot_eth_vlans(uint16_t *vlan_map[], uint16_t *vlan_map_max)
+{
+ if (vlan_map == NULL || vlan_map_max == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct ifaddrs *ifaces = NULL;
+ if (getifaddrs(&ifaces) != 0) {
+ return knot_map_errno();
+ }
+
+ unsigned map_size = 0;
+ for (struct ifaddrs *ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_PACKET) {
+ continue;
+ }
+ map_size++;
+ }
+
+ uint16_t *map = calloc(sizeof(uint16_t), 1 + map_size); // Indexed from 1.
+ if (map == NULL) {
+ freeifaddrs(ifaces);
+ return KNOT_ENOMEM;
+ }
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ free(map);
+ freeifaddrs(ifaces);
+ return knot_map_errno();
+ }
+
+ for (struct ifaddrs *ifa = ifaces; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_PACKET) {
+ continue;
+ }
+
+ unsigned if_index = if_nametoindex(ifa->ifa_name);
+ if (if_index == 0) {
+ close(fd);
+ free(map);
+ freeifaddrs(ifaces);
+ return knot_map_errno();
+ }
+
+ struct vlan_ioctl_args ifv = {
+ .cmd = GET_VLAN_REALDEV_NAME_CMD
+ };
+ strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+
+ if (ioctl(fd, SIOCGIFVLAN, &ifv) >= 0) {
+ memset(&ifv, 0, sizeof(ifv));
+ ifv.cmd = GET_VLAN_VID_CMD;
+ strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
+
+ if (ioctl(fd, SIOCGIFVLAN, &ifv) < 0) {
+ close(fd);
+ free(map);
+ freeifaddrs(ifaces);
+ return knot_map_errno();
+ }
+
+ map[if_index] = htobe16(ifv.u.VID);
+ }
+ }
+
+ close(fd);
+ freeifaddrs(ifaces);
+
+ *vlan_map = map;
+ *vlan_map_max = map_size;
+
+ return KNOT_EOK;
+}
+
+_public_
+knot_xdp_mode_t knot_eth_xdp_mode(int if_index)
+{
+#if USE_LIBXDP
+ struct bpf_xdp_query_opts info = { .sz = sizeof(info) };
+ int ret = bpf_xdp_query(if_index, 0, &info);
+#else
+ struct xdp_link_info info;
+ int ret = bpf_get_link_xdp_info(if_index, &info, sizeof(info), 0);
+#endif
+ if (ret != 0) {
+ return KNOT_XDP_MODE_NONE;
+ }
+
+ switch (info.attach_mode) {
+ case XDP_ATTACHED_DRV:
+ case XDP_ATTACHED_HW:
+ return KNOT_XDP_MODE_FULL;
+ case XDP_ATTACHED_SKB:
+ return KNOT_XDP_MODE_EMUL;
+ default:
+ return KNOT_XDP_MODE_NONE;
+ }
+}
diff --git a/src/libknot/xdp/eth.h b/src/libknot/xdp/eth.h
new file mode 100644
index 0000000..8c5b901
--- /dev/null
+++ b/src/libknot/xdp/eth.h
@@ -0,0 +1,111 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Ethernet device info interface.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define KNOT_XDP_MAX_MTU 1790
+
+/*!
+ * \brief Get number of combined queues of a network interface.
+ *
+ * \param devname Name of the ethdev (e.g. eth1).
+ *
+ * \retval < 0 KNOT_E* if error.
+ * \retval 1 Default no of queues if the dev does not support.
+ * \return > 0 Number of queues.
+ */
+int knot_eth_queues(const char *devname);
+
+/*!
+ * \brief Network card RSS configuration.
+ */
+typedef struct {
+ size_t table_size; /*!< Size of indirection table in four-bytes. */
+ size_t key_size; /*!< Size of the RSS key in bytes. */
+ uint32_t mask; /*!< Input mask for accessing the table. */
+ uint32_t data[]; /*!< Serialized key and table. */
+} knot_eth_rss_conf_t;
+
+/*!
+ * \brief Get RSS configuration of a network interface.
+ *
+ * \param devname Name of the ethdev (e.g. eth1).
+ * \param rss_conf Output RSS configuration. Must be freed explicitly.
+ *
+ * \return KNOT_E*
+ */
+int knot_eth_rss(const char *devname, knot_eth_rss_conf_t **rss_conf);
+
+/*!
+ * \brief Get value of MTU setup on a network interface.
+ *
+ * \param devname Name of the ethdev (e.g. eth1).
+ *
+ * \retval < 0 KNOT_E* if error.
+ * \return >= 0 Interface MTU.
+ */
+int knot_eth_mtu(const char *devname);
+
+/*!
+ * \brief Get the corresponding network interface name for the address.
+ *
+ * \param addr Address of the interface.
+ * \param out Output buffer for the interface name.
+ * \param out_len Size of the output buffer.
+ *
+ * \return KNOT_E*
+ */
+int knot_eth_name_from_addr(const struct sockaddr_storage *addr, char *out,
+ size_t out_len);
+
+/*!
+ * \brief Get the mapping of interface index to VLAN tags.
+ *
+ * \param vlan_map Output array of the mappings.
+ * \param vlan_map_max Maximum interface index allowed.
+ *
+ * \return KNOT_E*
+ */
+int knot_eth_vlans(uint16_t *vlan_map[], uint16_t *vlan_map_max);
+
+typedef enum {
+ KNOT_XDP_MODE_NONE, /*!< XDP not available, BPF not loaded, or error. */
+ KNOT_XDP_MODE_FULL, /*!< Full XDP support in driver or HW. */
+ KNOT_XDP_MODE_EMUL, /*!< Emulated XDP support. */
+} knot_xdp_mode_t;
+
+/*!
+ * \brief Return the current XDP mode of a network interface.
+ *
+ * \param if_index Index of the interface, output from if_nametoindex().
+ *
+ * \return Current XDP mode.
+ */
+knot_xdp_mode_t knot_eth_xdp_mode(int if_index);
+
+/*! @} */
diff --git a/src/libknot/xdp/msg.h b/src/libknot/xdp/msg.h
new file mode 100644
index 0000000..f567921
--- /dev/null
+++ b/src/libknot/xdp/msg.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief XDP message description.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <linux/if_ether.h>
+#include <netinet/in.h>
+#include <sys/uio.h>
+
+/*! \brief Message flags. */
+typedef enum {
+ KNOT_XDP_MSG_IPV6 = (1 << 0), /*!< This packet is a IPv6 (IPv4 otherwise). */
+ KNOT_XDP_MSG_TCP = (1 << 1), /*!< This packet is a TCP (UDP otherwise). */
+ KNOT_XDP_MSG_SYN = (1 << 2), /*!< SYN flag set (TCP only). */
+ KNOT_XDP_MSG_ACK = (1 << 3), /*!< ACK flag set (TCP only). */
+ KNOT_XDP_MSG_FIN = (1 << 4), /*!< FIN flag set (TCP only). */
+ KNOT_XDP_MSG_RST = (1 << 5), /*!< RST flag set (TCP only). */
+ KNOT_XDP_MSG_MSS = (1 << 6), /*!< MSS option in TCP header (TCP only). */
+ KNOT_XDP_MSG_WSC = (1 << 7), /*!< Window Scale option in TCP header. */
+ KNOT_XDP_MSG_VLAN = (1 << 8), /*!< This packet will contain VLAN header. */
+} knot_xdp_msg_flag_t;
+
+/*! \brief Packet description with src & dst MAC & IP addrs + DNS payload. */
+typedef struct knot_xdp_msg {
+ struct sockaddr_in6 ip_from;
+ struct sockaddr_in6 ip_to;
+ uint8_t eth_from[ETH_ALEN];
+ uint8_t eth_to[ETH_ALEN];
+ knot_xdp_msg_flag_t flags;
+ struct iovec payload;
+ uint32_t seqno;
+ uint32_t ackno;
+ uint16_t mss;
+ uint16_t win;
+ uint8_t win_scale;
+ uint16_t vlan_tci;
+} knot_xdp_msg_t;
+
+/*! @} */
diff --git a/src/libknot/xdp/msg_init.h b/src/libknot/xdp/msg_init.h
new file mode 100644
index 0000000..8b96129
--- /dev/null
+++ b/src/libknot/xdp/msg_init.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <string.h>
+
+#include "libknot/xdp/msg.h"
+#include "libknot/xdp/tcp.h"
+#include "libdnssec/random.h"
+
+inline static bool empty_msg(const knot_xdp_msg_t *msg)
+{
+ const unsigned tcp_flags = KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK |
+ KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_RST;
+
+ return (msg->payload.iov_len == 0 && !(msg->flags & tcp_flags));
+}
+
+inline static void msg_init_base(knot_xdp_msg_t *msg, knot_xdp_msg_flag_t flags)
+{
+ memset(msg, 0, sizeof(*msg));
+
+ msg->flags = flags;
+}
+
+inline static void msg_init(knot_xdp_msg_t *msg, knot_xdp_msg_flag_t flags)
+{
+ msg_init_base(msg, flags);
+
+ if (flags & KNOT_XDP_MSG_TCP) {
+ msg->ackno = 0;
+ msg->seqno = dnssec_random_uint32_t();
+ if (flags & KNOT_XDP_MSG_SYN) {
+ msg->flags |= KNOT_XDP_MSG_MSS | KNOT_XDP_MSG_WSC;
+ }
+ }
+}
+
+inline static void msg_init_reply(knot_xdp_msg_t *msg, const knot_xdp_msg_t *query)
+{
+ msg_init_base(msg, query->flags & (KNOT_XDP_MSG_IPV6 | KNOT_XDP_MSG_TCP |
+ KNOT_XDP_MSG_MSS | KNOT_XDP_MSG_WSC));
+
+ memcpy(msg->eth_from, query->eth_to, ETH_ALEN);
+ memcpy(msg->eth_to, query->eth_from, ETH_ALEN);
+
+ memcpy(&msg->ip_from, &query->ip_to, sizeof(msg->ip_from));
+ memcpy(&msg->ip_to, &query->ip_from, sizeof(msg->ip_to));
+
+ msg->vlan_tci = query->vlan_tci;
+
+ if (msg->flags & KNOT_XDP_MSG_TCP) {
+ msg->ackno = knot_tcp_next_seqno(query);
+ msg->seqno = query->ackno;
+ if (msg->seqno == 0) {
+ msg->seqno = dnssec_random_uint32_t();
+ }
+ }
+}
diff --git a/src/libknot/xdp/protocols.h b/src/libknot/xdp/protocols.h
new file mode 100644
index 0000000..ab1cff4
--- /dev/null
+++ b/src/libknot/xdp/protocols.h
@@ -0,0 +1,446 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <assert.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <string.h>
+
+#include "libknot/endian.h"
+#include "libknot/xdp/bpf-consts.h"
+#include "libknot/xdp/msg.h"
+
+/* Don't fragment flag. */
+#define IP_DF 0x4000
+
+#define HDR_8021Q_LEN 4;
+
+/*
+ * Following prot_read_*() functions do not check sanity of parsed packet.
+ * Broken packets have to be dropped by BPF filter prior getting here.
+ */
+
+inline static void *prot_read_udp(void *data, uint16_t *src_port, uint16_t *dst_port)
+{
+ const struct udphdr *udp = data;
+
+ *src_port = udp->source;
+ *dst_port = udp->dest;
+
+ return data + sizeof(*udp);
+}
+
+enum {
+ PROT_TCP_OPT_ENDOP = 0,
+ PROT_TCP_OPT_NOOP = 1,
+ PROT_TCP_OPT_MSS = 2,
+ PROT_TCP_OPT_WSC = 3, // window scale
+
+ PROT_TCP_OPT_LEN_MSS = 4,
+ PROT_TCP_OPT_LEN_WSC = 3,
+};
+
+inline static void *prot_read_tcp(void *data, knot_xdp_msg_t *msg, uint16_t *src_port, uint16_t *dst_port)
+{
+ const struct tcphdr *tcp = data;
+
+ msg->flags |= KNOT_XDP_MSG_TCP;
+
+ if (tcp->syn) {
+ msg->flags |= KNOT_XDP_MSG_SYN;
+ }
+ if (tcp->ack) {
+ msg->flags |= KNOT_XDP_MSG_ACK;
+ }
+ if (tcp->fin) {
+ msg->flags |= KNOT_XDP_MSG_FIN;
+ }
+ if (tcp->rst) {
+ msg->flags |= KNOT_XDP_MSG_RST;
+ }
+
+ msg->seqno = be32toh(tcp->seq);
+ msg->ackno = be32toh(tcp->ack_seq);
+ msg->win = be16toh(tcp->window);
+
+ *src_port = tcp->source;
+ *dst_port = tcp->dest;
+
+ uint8_t *opts = data + sizeof(*tcp), *hdr_end = data + tcp->doff * 4;
+ while (opts < hdr_end) {
+ if (opts[0] == PROT_TCP_OPT_ENDOP || opts[0] == PROT_TCP_OPT_NOOP) {
+ opts++;
+ continue;
+ }
+
+ if (opts + 1 > hdr_end || opts + opts[1] > hdr_end) {
+ // Malformed option.
+ break;
+ }
+
+ if (opts[0] == PROT_TCP_OPT_MSS && opts[1] == PROT_TCP_OPT_LEN_MSS) {
+ msg->flags |= KNOT_XDP_MSG_MSS;
+ memcpy(&msg->mss, &opts[2], sizeof(msg->mss));
+ msg->mss = be16toh(msg->mss);
+ }
+
+ if (opts[0] == PROT_TCP_OPT_WSC && opts[1] == PROT_TCP_OPT_LEN_WSC) {
+ msg->flags |= KNOT_XDP_MSG_WSC;
+ msg->win_scale = opts[2];
+ }
+
+ opts += opts[1];
+ }
+
+ return hdr_end;
+}
+
+inline static void *prot_read_ipv4(void *data, knot_xdp_msg_t *msg, void **data_end)
+{
+ const struct iphdr *ip4 = data;
+
+ // Conditions ensured by the BPF filter.
+ assert(ip4->version == 4);
+ assert(ip4->frag_off == 0 || ip4->frag_off == __constant_htons(IP_DF));
+ // IPv4 header checksum is not verified!
+
+ struct sockaddr_in *src = (struct sockaddr_in *)&msg->ip_from;
+ struct sockaddr_in *dst = (struct sockaddr_in *)&msg->ip_to;
+ memcpy(&src->sin_addr, &ip4->saddr, sizeof(src->sin_addr));
+ memcpy(&dst->sin_addr, &ip4->daddr, sizeof(dst->sin_addr));
+ src->sin_family = AF_INET;
+ dst->sin_family = AF_INET;
+
+ *data_end = data + be16toh(ip4->tot_len);
+ data += ip4->ihl * 4;
+
+ if (ip4->protocol == IPPROTO_TCP) {
+ return prot_read_tcp(data, msg, &src->sin_port, &dst->sin_port);
+ } else {
+ assert(ip4->protocol == IPPROTO_UDP);
+ return prot_read_udp(data, &src->sin_port, &dst->sin_port);
+ }
+}
+
+inline static void *prot_read_ipv6(void *data, knot_xdp_msg_t *msg, void **data_end)
+{
+ const struct ipv6hdr *ip6 = data;
+
+ msg->flags |= KNOT_XDP_MSG_IPV6;
+
+ // Conditions ensured by the BPF filter.
+ assert(ip6->version == 6);
+
+ struct sockaddr_in6 *src = (struct sockaddr_in6 *)&msg->ip_from;
+ struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&msg->ip_to;
+ memcpy(&src->sin6_addr, &ip6->saddr, sizeof(src->sin6_addr));
+ memcpy(&dst->sin6_addr, &ip6->daddr, sizeof(dst->sin6_addr));
+ src->sin6_family = AF_INET6;
+ dst->sin6_family = AF_INET6;
+ src->sin6_flowinfo = 0;
+ dst->sin6_flowinfo = 0;
+ // Scope ID is ignored.
+
+ data += sizeof(*ip6);
+ *data_end = data + be16toh(ip6->payload_len);
+
+ if (ip6->nexthdr == IPPROTO_TCP) {
+ return prot_read_tcp(data, msg, &src->sin6_port, &dst->sin6_port);
+ } else {
+ assert(ip6->nexthdr == IPPROTO_UDP);
+ return prot_read_udp(data, &src->sin6_port, &dst->sin6_port);
+ }
+}
+
+inline static void *prot_read_eth(void *data, knot_xdp_msg_t *msg, void **data_end,
+ const uint16_t *vlan_map, unsigned vlan_map_max)
+{
+ const struct ethhdr *eth = data;
+ knot_xdp_info_t *info = data - KNOT_XDP_PKT_ALIGNMENT - sizeof(*info);
+
+ memcpy(msg->eth_from, eth->h_source, ETH_ALEN);
+ memcpy(msg->eth_to, eth->h_dest, ETH_ALEN);
+ msg->flags = 0;
+
+ if (eth->h_proto == __constant_htons(ETH_P_8021Q)) {
+ if (info->out_if_index > 0 && info->out_if_index <= vlan_map_max) {
+ assert(vlan_map);
+ msg->vlan_tci = vlan_map[info->out_if_index];
+ } else {
+ memcpy(&msg->vlan_tci, data + sizeof(*eth), sizeof(msg->vlan_tci));
+ }
+ data += HDR_8021Q_LEN;
+ eth = data;
+ }
+
+ data += sizeof(*eth);
+
+ if (eth->h_proto == __constant_htons(ETH_P_IPV6)) {
+ return prot_read_ipv6(data, msg, data_end);
+ } else {
+ assert(eth->h_proto == __constant_htons(ETH_P_IP));
+ return prot_read_ipv4(data, msg, data_end);
+ }
+}
+
+inline static size_t prot_write_hdrs_len(const knot_xdp_msg_t *msg)
+{
+ size_t res = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr);
+
+ if (msg->vlan_tci != 0 || msg->flags & KNOT_XDP_MSG_VLAN) {
+ res += HDR_8021Q_LEN;
+ }
+
+ if (msg->flags & KNOT_XDP_MSG_IPV6) {
+ res += sizeof(struct ipv6hdr) - sizeof(struct iphdr);
+ }
+
+ if (msg->flags & KNOT_XDP_MSG_TCP) {
+ res += sizeof(struct tcphdr) - sizeof(struct udphdr);
+
+ if (msg->flags & KNOT_XDP_MSG_MSS) {
+ res += PROT_TCP_OPT_LEN_MSS;
+ }
+ if (msg->flags & KNOT_XDP_MSG_WSC) {
+ res += PROT_TCP_OPT_LEN_WSC + 1; // 1 == align
+ }
+ }
+
+ return res;
+}
+
+/* Checksum endianness implementation notes for ipv4_checksum() and checksum().
+ *
+ * The basis for checksum is addition on big-endian 16-bit words, with bit 16 carrying
+ * over to bit 0. That can be viewed as first byte carrying to the second and the
+ * second one carrying back to the first one, i.e. a symmetrical situation.
+ * Therefore the result is the same even when arithmetics is done on little-endian (!)
+ */
+
+inline static void checksum(uint32_t *result, const void *_data, uint32_t _data_len)
+{
+ assert(!(_data_len & 1));
+ const uint16_t *data = _data;
+ uint32_t len = _data_len / 2;
+ while (len-- > 0) {
+ *result += *data++;
+ }
+}
+
+inline static void checksum_uint16(uint32_t *result, uint16_t x)
+{
+ checksum(result, &x, sizeof(x));
+}
+
+inline static void checksum_payload(uint32_t *result, void *payload, size_t pay_len)
+{
+ if (pay_len & 1) {
+ ((uint8_t *)payload)[pay_len++] = 0;
+ }
+ checksum(result, payload, pay_len);
+}
+
+inline static uint16_t checksum_finish(uint32_t result, bool nonzero)
+{
+ while (result > 0xffff) {
+ result = (result & 0xffff) + (result >> 16);
+ }
+ if (!nonzero || result != 0xffff) {
+ result = ~result;
+ }
+ return result;
+}
+
+inline static void prot_write_udp(void *data, const knot_xdp_msg_t *msg, void *data_end,
+ uint16_t src_port, uint16_t dst_port, uint32_t chksum)
+{
+ struct udphdr *udp = data;
+
+ udp->len = htobe16(data_end - data);
+ udp->source = src_port;
+ udp->dest = dst_port;
+
+ if (msg->flags & KNOT_XDP_MSG_IPV6) {
+ udp->check = 0;
+ checksum(&chksum, &udp->len, sizeof(udp->len));
+ checksum_uint16(&chksum, htobe16(IPPROTO_UDP));
+ checksum_payload(&chksum, data, data_end - data);
+ udp->check = checksum_finish(chksum, true);
+ } else {
+ udp->check = 0; // UDP over IPv4 doesn't require checksum.
+ }
+
+ assert(data + sizeof(*udp) == msg->payload.iov_base);
+}
+
+inline static void prot_write_tcp(void *data, const knot_xdp_msg_t *msg, void *data_end,
+ uint16_t src_port, uint16_t dst_port, uint32_t chksum,
+ uint16_t mss)
+{
+ struct tcphdr *tcp = data;
+
+ tcp->source = src_port;
+ tcp->dest = dst_port;
+ tcp->seq = htobe32(msg->seqno);
+ tcp->ack_seq = htobe32(msg->ackno);
+ tcp->window = htobe16(msg->win);
+ tcp->check = 0; // Temporarily initialize before checksum calculation.
+
+ tcp->syn = ((msg->flags & KNOT_XDP_MSG_SYN) ? 1 : 0);
+ tcp->ack = ((msg->flags & KNOT_XDP_MSG_ACK) ? 1 : 0);
+ tcp->fin = ((msg->flags & KNOT_XDP_MSG_FIN) ? 1 : 0);
+ tcp->rst = ((msg->flags & KNOT_XDP_MSG_RST) ? 1 : 0);
+
+ uint8_t *hdr_end = data + sizeof(*tcp);
+ if (msg->flags & KNOT_XDP_MSG_WSC) {
+ hdr_end[0] = PROT_TCP_OPT_WSC;
+ hdr_end[1] = PROT_TCP_OPT_LEN_WSC;
+ hdr_end[2] = msg->win_scale;
+ hdr_end += PROT_TCP_OPT_LEN_WSC;
+ *hdr_end++ = PROT_TCP_OPT_NOOP; // align
+ }
+ if (msg->flags & KNOT_XDP_MSG_MSS) {
+ mss = htobe16(mss);
+ hdr_end[0] = PROT_TCP_OPT_MSS;
+ hdr_end[1] = PROT_TCP_OPT_LEN_MSS;
+ memcpy(&hdr_end[2], &mss, sizeof(mss));
+ hdr_end += PROT_TCP_OPT_LEN_MSS;
+ }
+
+ tcp->psh = ((data_end - (void *)hdr_end > 0) ? 1 : 0);
+ tcp->doff = (hdr_end - (uint8_t *)tcp) / 4;
+ assert((hdr_end - (uint8_t *)tcp) % 4 == 0);
+
+ checksum_uint16(&chksum, htobe16(IPPROTO_TCP));
+ checksum_uint16(&chksum, htobe16(data_end - data));
+ checksum_payload(&chksum, data, data_end - data);
+ tcp->check = checksum_finish(chksum, false);
+
+ assert(hdr_end == msg->payload.iov_base);
+}
+
+inline static uint16_t from32to16(uint32_t sum)
+{
+ sum = (sum & 0xffff) + (sum >> 16);
+ sum = (sum & 0xffff) + (sum >> 16);
+ return sum;
+}
+
+inline static uint16_t ipv4_checksum(const uint16_t *ipv4_hdr)
+{
+ uint32_t sum32 = 0;
+ for (int i = 0; i < 10; ++i) {
+ if (i != 5) {
+ sum32 += ipv4_hdr[i];
+ }
+ }
+ return ~from32to16(sum32);
+}
+
+inline static void prot_write_ipv4(void *data, const knot_xdp_msg_t *msg,
+ void *data_end, uint16_t tcp_mss)
+{
+ struct iphdr *ip4 = data;
+
+ ip4->version = 4;
+ ip4->ihl = sizeof(*ip4) / 4;
+ ip4->tos = 0;
+ ip4->tot_len = htobe16(data_end - data);
+ ip4->id = 0;
+ ip4->frag_off = 0;
+ ip4->ttl = IPDEFTTL;
+ ip4->protocol = ((msg->flags & KNOT_XDP_MSG_TCP) ? IPPROTO_TCP : IPPROTO_UDP);
+
+ const struct sockaddr_in *src = (const struct sockaddr_in *)&msg->ip_from;
+ const struct sockaddr_in *dst = (const struct sockaddr_in *)&msg->ip_to;
+ memcpy(&ip4->saddr, &src->sin_addr, sizeof(src->sin_addr));
+ memcpy(&ip4->daddr, &dst->sin_addr, sizeof(dst->sin_addr));
+
+ ip4->check = ipv4_checksum(data);
+
+ data += sizeof(*ip4);
+
+ if (msg->flags & KNOT_XDP_MSG_TCP) {
+ uint32_t chk = 0;
+ checksum(&chk, &src->sin_addr, sizeof(src->sin_addr));
+ checksum(&chk, &dst->sin_addr, sizeof(dst->sin_addr));
+
+ prot_write_tcp(data, msg, data_end, src->sin_port, dst->sin_port, chk, tcp_mss);
+ } else {
+ prot_write_udp(data, msg, data_end, src->sin_port, dst->sin_port, 0); // IPv4/UDP requires no checksum
+ }
+}
+
+inline static void prot_write_ipv6(void *data, const knot_xdp_msg_t *msg,
+ void *data_end, uint16_t tcp_mss)
+{
+ struct ipv6hdr *ip6 = data;
+
+ ip6->version = 6;
+ ip6->priority = 0;
+ ip6->payload_len = htobe16(data_end - data - sizeof(*ip6));
+ ip6->nexthdr = ((msg->flags & KNOT_XDP_MSG_TCP) ? IPPROTO_TCP : IPPROTO_UDP);
+ ip6->hop_limit = IPDEFTTL;
+
+ memset(ip6->flow_lbl, 0, sizeof(ip6->flow_lbl));
+
+ const struct sockaddr_in6 *src = (const struct sockaddr_in6 *)&msg->ip_from;
+ const struct sockaddr_in6 *dst = (const struct sockaddr_in6 *)&msg->ip_to;
+ memcpy(&ip6->saddr, &src->sin6_addr, sizeof(src->sin6_addr));
+ memcpy(&ip6->daddr, &dst->sin6_addr, sizeof(dst->sin6_addr));
+
+ data += sizeof(*ip6);
+
+ uint32_t chk = 0;
+ checksum(&chk, &src->sin6_addr, sizeof(src->sin6_addr));
+ checksum(&chk, &dst->sin6_addr, sizeof(dst->sin6_addr));
+
+ if (msg->flags & KNOT_XDP_MSG_TCP) {
+ prot_write_tcp(data, msg, data_end, src->sin6_port, dst->sin6_port, chk, tcp_mss);
+ } else {
+ prot_write_udp(data, msg, data_end, src->sin6_port, dst->sin6_port, chk);
+ }
+}
+
+inline static void prot_write_eth(void *data, const knot_xdp_msg_t *msg,
+ void *data_end, uint16_t tcp_mss)
+{
+ struct ethhdr *eth = data;
+
+ memcpy(eth->h_source, msg->eth_from, ETH_ALEN);
+ memcpy(eth->h_dest, msg->eth_to, ETH_ALEN);
+
+ if (msg->vlan_tci != 0) {
+ eth->h_proto = __constant_htons(ETH_P_8021Q);
+ memcpy(data + sizeof(*eth), &msg->vlan_tci, sizeof(msg->vlan_tci));
+ data += HDR_8021Q_LEN;
+ eth = data;
+ }
+
+ data += sizeof(*eth);
+
+ if (msg->flags & KNOT_XDP_MSG_IPV6) {
+ eth->h_proto = __constant_htons(ETH_P_IPV6);
+ prot_write_ipv6(data, msg, data_end, tcp_mss);
+ } else {
+ eth->h_proto = __constant_htons(ETH_P_IP);
+ prot_write_ipv4(data, msg, data_end, tcp_mss);
+ }
+}
diff --git a/src/libknot/xdp/quic.c b/src/libknot/xdp/quic.c
new file mode 100644
index 0000000..f12a47c
--- /dev/null
+++ b/src/libknot/xdp/quic.c
@@ -0,0 +1,1028 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/x509.h>
+#include <ngtcp2/ngtcp2.h>
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "libknot/xdp/quic.h"
+
+#include "contrib/macros.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/lists.h"
+#include "libknot/endian.h"
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+#include "libknot/attribute.h"
+#include "libknot/endian.h"
+#include "libknot/error.h"
+#include "libknot/wire.h"
+
+#define SERVER_DEFAULT_SCIDLEN 18
+
+#define QUIC_DEFAULT_VERSION "-VERS-ALL:+VERS-TLS1.3"
+#define QUIC_DEFAULT_CIPHERS "-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:+CHACHA20-POLY1305:+AES-128-CCM"
+#define QUIC_DEFAULT_GROUPS "-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1"
+#define QUIC_PRIORITIES "%DISABLE_TLS13_COMPAT_MODE:NORMAL:"QUIC_DEFAULT_VERSION":"QUIC_DEFAULT_CIPHERS":"QUIC_DEFAULT_GROUPS
+
+#define XQUIC_SEND_VERSION_NEGOTIATION NGTCP2_ERR_VERSION_NEGOTIATION
+#define XQUIC_SEND_RETRY NGTCP2_ERR_RETRY
+#define XQUIC_SEND_STATELESS_RESET (-NGTCP2_STATELESS_RESET_TOKENLEN)
+
+#define TLS_CALLBACK_ERR (-1)
+
+typedef struct knot_quic_creds {
+ gnutls_certificate_credentials_t tls_cert;
+ gnutls_anti_replay_t tls_anti_replay;
+ gnutls_datum_t tls_ticket_key;
+ bool is_clone;
+} knot_xquic_creds_t;
+
+typedef struct knot_quic_session {
+ node_t n;
+ gnutls_datum_t tls_session;
+ ngtcp2_transport_params quic_params;
+} knot_xquic_session_t;
+
+static unsigned addr_len(const struct sockaddr_in6 *ss)
+{
+ return (ss->sin6_family == AF_INET6 ?
+ sizeof(struct sockaddr_in6) : sizeof(struct sockaddr));
+}
+
+_public_
+struct knot_quic_session *knot_xquic_session_save(knot_xquic_conn_t *conn)
+{
+ const ngtcp2_transport_params *tmp = ngtcp2_conn_get_remote_transport_params(conn->conn);
+ if (tmp == NULL) {
+ return NULL;
+ }
+
+ knot_xquic_session_t *session = calloc(1, sizeof(*session));
+ if (session == NULL) {
+ return NULL;
+ }
+
+ int ret = gnutls_session_get_data2(conn->tls_session, &session->tls_session);
+ if (ret != GNUTLS_E_SUCCESS) {
+ free(session);
+ return NULL;
+ }
+
+ memcpy(&session->quic_params, tmp, sizeof(session->quic_params));
+
+ return session;
+}
+
+_public_
+int knot_xquic_session_load(knot_xquic_conn_t *conn, struct knot_quic_session *session)
+{
+ if (session == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ if (conn == NULL) {
+ goto session_free;
+ }
+
+ ret = gnutls_session_set_data(conn->tls_session, session->tls_session.data, session->tls_session.size);
+ if (ret != KNOT_EOK) {
+ goto session_free;
+ }
+
+ ngtcp2_conn_set_early_remote_transport_params(conn->conn, &session->quic_params);
+
+session_free:
+ gnutls_free(session->tls_session.data);
+ free(session);
+ return ret;
+}
+
+static int tls_anti_replay_db_add_func(void *dbf, time_t exp_time,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *data)
+{
+ return 0;
+}
+
+static void tls_session_ticket_key_free(gnutls_datum_t *ticket)
+{
+ gnutls_memset(ticket->data, 0, ticket->size);
+ gnutls_free(ticket->data);
+}
+
+static int self_signed_cert(gnutls_certificate_credentials_t tls_cert)
+{
+ gnutls_x509_privkey_t privkey = NULL;
+ gnutls_x509_crt_t cert = NULL;
+ char *hostname = sockaddr_hostname();
+ if (hostname == NULL) {
+ return -ENOMEM;
+ }
+
+ int ret = gnutls_x509_privkey_init(&privkey);
+ if (ret < 0) {
+ free(hostname);
+ return ret;
+ }
+
+ uint8_t serial[16];
+ gnutls_rnd(GNUTLS_RND_NONCE, serial, sizeof(serial));
+ /* clear the left-most bit to avoid signedness confusion: */
+ serial[0] &= 0x8f;
+
+#define CHK(cmd) \
+ ret = (cmd); \
+ if (ret < 0) { goto finish; }
+
+#define now_years(years) (time(NULL) + 365 * 24 * 3600 * (years))
+
+ CHK(gnutls_x509_privkey_generate(privkey, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1), 0));
+
+ CHK(gnutls_x509_crt_init(&cert));
+ //CHK(gnutls_x509_crt_set_ca_status(cert, 0)); // TODO needed ?
+ CHK(gnutls_x509_crt_set_activation_time(cert, now_years(-1)));
+ CHK(gnutls_x509_crt_set_expiration_time(cert, now_years(20)));
+ CHK(gnutls_x509_crt_set_dn(cert, "CN=DoQ Self-Signed Server Certificate", NULL));
+ CHK(gnutls_x509_crt_set_key(cert, privkey));
+
+ CHK(gnutls_x509_crt_set_serial(cert, serial, sizeof(serial)));
+ CHK(gnutls_x509_crt_set_subject_alt_name(cert, GNUTLS_SAN_DNSNAME, hostname, strlen(hostname), GNUTLS_FSAN_SET));
+ CHK(gnutls_x509_crt_set_version(cert, 3));
+ CHK(gnutls_x509_crt_sign2(cert, cert, privkey, GNUTLS_DIG_SHA256, 0));
+
+ ret = gnutls_certificate_set_x509_key(tls_cert, &cert, 1, privkey);
+
+finish:
+ free(hostname);
+ gnutls_x509_privkey_deinit(privkey);
+ gnutls_x509_crt_deinit(cert);
+
+ return ret;
+}
+
+_public_
+struct knot_quic_creds *knot_xquic_init_creds(bool server, const char *tls_cert,
+ const char *tls_key)
+{
+ knot_xquic_creds_t *creds = calloc(1, sizeof(*creds));
+ if (creds == NULL) {
+ return NULL;
+ }
+
+ int ret = gnutls_anti_replay_init(&creds->tls_anti_replay);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail;
+ }
+ gnutls_anti_replay_set_add_function(creds->tls_anti_replay, tls_anti_replay_db_add_func);
+ gnutls_anti_replay_set_ptr(creds->tls_anti_replay, NULL);
+
+ ret = gnutls_certificate_allocate_credentials(&creds->tls_cert);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_anti_replay_deinit(creds->tls_anti_replay);
+ goto fail;
+ }
+
+ ret = gnutls_certificate_set_x509_system_trust(creds->tls_cert);
+ if (ret < 0) {
+ goto fail2;
+ }
+
+ if ((bool)(tls_cert == NULL) != (bool)(tls_key == NULL) ||
+ (tls_cert != NULL && !server)) {
+ goto fail2;
+ }
+ if (tls_cert != NULL) {
+ ret = gnutls_certificate_set_x509_key_file(creds->tls_cert, tls_cert, tls_key, GNUTLS_X509_FMT_PEM);
+ } else if (server) {
+ ret = self_signed_cert(creds->tls_cert);
+ }
+ if (ret < 0) {
+ goto fail2;
+ }
+
+ ret = gnutls_session_ticket_key_generate(&creds->tls_ticket_key);
+ if (ret != GNUTLS_E_SUCCESS) {
+ goto fail2;
+ }
+
+ return creds;
+
+fail:
+ free(creds);
+ return NULL;
+fail2:
+ knot_xquic_free_creds(creds);
+ return NULL;
+}
+
+_public_
+void knot_xquic_free_creds(struct knot_quic_creds *creds)
+{
+ if (creds == NULL) {
+ return;
+ }
+
+ if (!creds->is_clone) {
+ gnutls_certificate_free_credentials(creds->tls_cert);
+ if (creds->tls_ticket_key.data != NULL) {
+ tls_session_ticket_key_free(&creds->tls_ticket_key);
+ }
+ }
+ gnutls_anti_replay_deinit(creds->tls_anti_replay);
+ free(creds);
+}
+
+#define ALPN "\03""doq"
+#define ALPN_TMP1 "\07""doq-i11"
+#define ALPN_TMP2 "\07""doq-i03"
+
+static int tls_client_hello_cb(gnutls_session_t session, unsigned int htype,
+ unsigned when, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ assert(htype == GNUTLS_HANDSHAKE_CLIENT_HELLO);
+ assert(when == GNUTLS_HOOK_POST);
+
+ if (!incoming) {
+ return 0;
+ }
+
+ gnutls_datum_t alpn;
+ int ret = gnutls_alpn_get_selected_protocol(session, &alpn);
+ if (ret != 0) {
+ return ret;
+ }
+
+ const char *dq = (const char *)&ALPN[1];
+ if (((unsigned int)ALPN[0] != alpn.size ||
+ memcmp(dq, alpn.data, alpn.size) != 0) &&
+ ((unsigned int)ALPN_TMP1[0] != alpn.size ||
+ memcmp((const char *)&ALPN_TMP1[1], alpn.data, alpn.size) != 0) &&
+ ((unsigned int)ALPN_TMP1[0] != alpn.size ||
+ memcmp((const char *)&ALPN_TMP2[1], alpn.data, alpn.size) != 0)) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ return 0;
+}
+
+static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
+{
+ return ((knot_xquic_conn_t *)conn_ref->user_data)->conn;
+}
+
+static int tls_init_conn_session(knot_xquic_conn_t *conn, bool server)
+{
+ if (gnutls_init(&conn->tls_session, (server ? GNUTLS_SERVER : GNUTLS_CLIENT) |
+ GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_AUTO_SEND_TICKET |
+ GNUTLS_NO_END_OF_EARLY_DATA) != GNUTLS_E_SUCCESS) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ if (gnutls_priority_set_direct(conn->tls_session, QUIC_PRIORITIES,
+ NULL) != GNUTLS_E_SUCCESS) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ if (server && gnutls_session_ticket_enable_server(conn->tls_session,
+ &conn->xquic_table->creds->tls_ticket_key) != GNUTLS_E_SUCCESS) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ gnutls_handshake_set_hook_function(conn->tls_session,
+ GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST, tls_client_hello_cb);
+ int ret = ngtcp2_crypto_gnutls_configure_server_session(conn->tls_session);
+ if (ret != 0) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ gnutls_record_set_max_early_data_size(conn->tls_session, 0xffffffffu);
+
+ conn->conn_ref = (nc_conn_ref_placeholder_t) {
+ .get_conn = get_conn,
+ .user_data = conn
+ };
+
+ _Static_assert(sizeof(nc_conn_ref_placeholder_t) == sizeof(ngtcp2_crypto_conn_ref), "invalid placeholder for conn_ref");
+ gnutls_session_set_ptr(conn->tls_session, &conn->conn_ref);
+
+ if (server) {
+ gnutls_anti_replay_enable(conn->tls_session, conn->xquic_table->creds->tls_anti_replay);
+
+ }
+ if (gnutls_credentials_set(conn->tls_session, GNUTLS_CRD_CERTIFICATE,
+ conn->xquic_table->creds->tls_cert) != GNUTLS_E_SUCCESS) {
+ return TLS_CALLBACK_ERR;
+ }
+
+
+ gnutls_datum_t alpn[3] = {
+ {
+ .data = (uint8_t *)(&ALPN[1]),
+ .size = ALPN[0],
+ },
+ {
+ .data = (uint8_t *)(&ALPN_TMP1[1]),
+ .size = ALPN_TMP1[0],
+ },
+ {
+ .data = (uint8_t *)(&ALPN_TMP2[1]),
+ .size = ALPN_TMP2[0],
+ }
+ };
+ gnutls_alpn_set_protocols(conn->tls_session, alpn, 3, 0);
+
+ ngtcp2_conn_set_tls_native_handle(conn->conn, conn->tls_session);
+
+ return KNOT_EOK;
+}
+
+static uint64_t get_timestamp(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+ assert(0);
+ }
+
+ return (uint64_t)ts.tv_sec * NGTCP2_SECONDS + (uint64_t)ts.tv_nsec;
+}
+
+uint64_t xquic_conn_get_timeout(knot_xquic_conn_t *conn)
+{
+ // This effectively obtains the locally configured conn timeout.
+ // It would be possible to obey negotitated idle timeout by employing remote params,
+ // but this would differ per-connection and the whole idea of maintaining
+ // to-be-timeouted connections in simple linear list requires that
+ // the idle timeout is homogeneous among conns.
+ // Anyway, we also violate RFC9000/10.1 (Probe Timeout) for the same reason.
+ // TODO for the future: refactor conn table to use some tree/heap
+ // for to-be-timeouted conns, and use ngtcp2_conn_get_expiry() and
+ // ngtcp2_conn_handle_expiry() appropriately.
+ const ngtcp2_transport_params *params = ngtcp2_conn_get_local_transport_params(conn->conn);
+
+ return conn->last_ts + params->max_idle_timeout;
+}
+
+bool xquic_conn_timeout(knot_xquic_conn_t *conn, uint64_t *now)
+{
+ if (*now == 0) {
+ *now = get_timestamp();
+ }
+ return *now > xquic_conn_get_timeout(conn);
+}
+
+_public_
+uint32_t knot_xquic_conn_rtt(knot_xquic_conn_t *conn)
+{
+ ngtcp2_conn_stat stat = { 0 };
+ ngtcp2_conn_get_conn_stat(conn->conn, &stat);
+ return stat.smoothed_rtt / 1000; // nanosec --> usec
+}
+
+static void knot_quic_rand_cb(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx)
+{
+ (void)rand_ctx;
+ dnssec_random_buffer(dest, destlen);
+}
+
+static void init_random_cid(ngtcp2_cid *cid, size_t len)
+{
+ if (len == 0) {
+ len = SERVER_DEFAULT_SCIDLEN;
+ }
+
+ if (dnssec_random_buffer(cid->data, len) != DNSSEC_EOK) {
+ cid->datalen = 0;
+ } else {
+ cid->datalen = len;
+ }
+}
+
+static bool init_unique_cid(ngtcp2_cid *cid, size_t len, knot_xquic_table_t *table)
+{
+ do {
+ if (init_random_cid(cid, len), cid->datalen == 0) {
+ return false;
+ }
+ } while (xquic_table_lookup(cid, table) != NULL);
+ return true;
+}
+
+static int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ if (!init_unique_cid(cid, cidlen, ctx->xquic_table)) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ knot_xquic_cid_t **addto = xquic_table_insert(ctx, cid, ctx->xquic_table);
+ (void)addto;
+
+ if (token != NULL &&
+ ngtcp2_crypto_generate_stateless_reset_token(
+ token, (uint8_t *)ctx->xquic_table->hash_secret,
+ sizeof(ctx->xquic_table->hash_secret), cid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int remove_connection_id(ngtcp2_conn *conn, const ngtcp2_cid *cid,
+ void *user_data)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ knot_xquic_cid_t **torem = xquic_table_lookup2(cid, ctx->xquic_table);
+ if (torem != NULL) {
+ assert((*torem)->conn == ctx);
+ xquic_table_rem2(torem, ctx->xquic_table);
+ }
+
+ return 0;
+}
+
+static int handshake_completed_cb(ngtcp2_conn *conn, void *user_data)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ assert(!ctx->handshake_done);
+ ctx->handshake_done = true;
+
+ if (!ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ gnutls_datum_t alpn;
+ if (gnutls_alpn_get_selected_protocol(ctx->tls_session, &alpn) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ char alpn_str[alpn.size + 1];
+ alpn_str[alpn.size] = '\0';
+ memcpy(alpn_str, alpn.data, alpn.size);
+
+ if (gnutls_session_ticket_send(ctx->tls_session, 1, 0) != GNUTLS_E_SUCCESS) {
+ return TLS_CALLBACK_ERR;
+ }
+
+ uint8_t token[NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN];
+ ngtcp2_path path = *ngtcp2_conn_get_path(ctx->conn);
+ uint64_t ts = get_timestamp();
+ ngtcp2_ssize tokenlen = ngtcp2_crypto_generate_regular_token(token,
+ (uint8_t *)ctx->xquic_table->hash_secret,
+ sizeof(ctx->xquic_table->hash_secret),
+ path.remote.addr, path.remote.addrlen, ts);
+ if (tokenlen < 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (ngtcp2_conn_submit_new_token(ctx->conn, token, tokenlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int recv_stream_data(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data, void *stream_user_data)
+{
+ (void)(stream_user_data); // always NULL
+ (void)(offset); // QUIC shall ensure that data arrive in-order
+
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ int ret = knot_xquic_stream_recv_data(ctx, stream_id, data, datalen, (flags & NGTCP2_STREAM_DATA_FLAG_FIN));
+
+ return ret == KNOT_EOK ? 0 : NGTCP2_ERR_CALLBACK_FAILURE;
+}
+
+static int acked_stream_data_offset_cb(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t offset, uint64_t datalen,
+ void *user_data, void *stream_user_data)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+
+ bool keep = !ngtcp2_conn_is_server(conn); // kxdpgun: await incomming reply after query sent&acked
+
+ knot_xquic_stream_ack_data(ctx, stream_id, offset + datalen, keep);
+
+ return 0;
+}
+
+static int stream_closed(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
+ uint64_t app_error_code, void *user_data, void *stream_user_data)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ // NOTE possible error is stored in (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)
+
+ bool keep = !ngtcp2_conn_is_server(conn); // kxdpgun: process incomming reply after recvd&closed
+ if (!keep) {
+ xquic_stream_free(ctx, stream_id);
+ }
+ return 0;
+}
+
+static int recv_stateless_rst(ngtcp2_conn *conn, const ngtcp2_pkt_stateless_reset *sr, void *user_data)
+{
+ // NOTE server can't receive stateless resets, only client
+
+ // ngtcp2 verified stateless reset token already
+ (void)(sr);
+
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ assert(ctx->conn == conn);
+
+ knot_xquic_table_rem(ctx, ctx->xquic_table);
+
+ return 0;
+}
+
+static int recv_stream_rst(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
+ uint64_t app_error_code, void *user_data, void *stream_user_data)
+{
+ (void)final_size;
+ return stream_closed(conn, NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET,
+ stream_id, app_error_code, user_data, stream_user_data);
+}
+
+static void user_printf(void *user_data, const char *format, ...)
+{
+ knot_xquic_conn_t *ctx = (knot_xquic_conn_t *)user_data;
+ if (ctx->xquic_table->log_cb != NULL) {
+ char buf[256];
+ va_list args;
+ va_start(args, format);
+ vsnprintf(buf, sizeof(buf), format, args);
+ va_end(args);
+ ctx->xquic_table->log_cb(buf);
+ }
+}
+
+static int conn_new(ngtcp2_conn **pconn, const ngtcp2_path *path, const ngtcp2_cid *scid,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *odcid, uint32_t version,
+ uint64_t now, size_t udp_pl, uint64_t idle_timeout_ns,
+ void *user_data, bool server, bool retry_sent)
+{
+ // I. CALLBACKS
+ const ngtcp2_callbacks callbacks = {
+ ngtcp2_crypto_client_initial_cb,
+ ngtcp2_crypto_recv_client_initial_cb,
+ ngtcp2_crypto_recv_crypto_data_cb,
+ handshake_completed_cb,
+ NULL, // recv_version_negotiation not needed on server, nor kxdpgun
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ recv_stream_data,
+ acked_stream_data_offset_cb,
+ NULL, // stream_opened
+ stream_closed,
+ recv_stateless_rst,
+ ngtcp2_crypto_recv_retry_cb,
+ NULL, // extend_max_streams_bidi
+ NULL, // extend_max_streams_uni
+ knot_quic_rand_cb,
+ get_new_connection_id,
+ remove_connection_id,
+ ngtcp2_crypto_update_key_cb,
+ NULL, // path_validation,
+ NULL, // select_preferred_addr
+ recv_stream_rst,
+ NULL, // extend_max_remote_streams_bidi, might be useful to some allocation optimizations?
+ NULL, // extend_max_remote_streams_uni
+ NULL, // extend_max_stream_data,
+ NULL, // dcid_status
+ NULL, // handshake_confirmed
+ NULL, // recv_new_token
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ NULL, // recv_datagram
+ NULL, // ack_datagram
+ NULL, // lost_datagram
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ NULL, // stream_stop_sending
+ ngtcp2_crypto_version_negotiation_cb,
+ NULL, // recv_rx_key
+ NULL // recv_tx_key
+ };
+
+ // II. SETTINGS
+ ngtcp2_settings settings;
+ ngtcp2_settings_default(&settings);
+ settings.initial_ts = now;
+ settings.log_printf = user_printf;
+ settings.max_tx_udp_payload_size = udp_pl;
+ settings.qlog.odcid = *odcid;
+ settings.handshake_timeout = idle_timeout_ns; // NOTE setting handshake timeout to idle_timeout for simplicity
+ settings.no_pmtud = true;
+
+ // III. PARAMS
+ ngtcp2_transport_params params;
+ ngtcp2_transport_params_default(&params);
+
+ params.initial_max_data = 786432;
+ params.initial_max_stream_data_bidi_local = 524288;
+ params.initial_max_stream_data_bidi_remote = 524288;
+ params.initial_max_stream_data_uni = 524288;
+
+ // params.initial_max_stream_data_bidi_local = config.max_stream_data_bidi_local;
+ // params.initial_max_stream_data_bidi_remote = config.max_stream_data_bidi_remote;
+ // params.initial_max_stream_data_uni = config.max_stream_data_uni;
+ // params.initial_max_data = config.max_data;
+ params.initial_max_streams_bidi = 100;
+ params.initial_max_streams_uni = 3;
+ params.max_idle_timeout = idle_timeout_ns;
+ // params.stateless_reset_token_present = 1;
+ // params.active_connection_id_limit = 7;
+ if (odcid) {
+ params.original_dcid = *odcid;
+ } else {
+ params.original_dcid = *scid;
+ }
+ if (retry_sent) {
+ params.retry_scid_present = 1;
+ params.retry_scid = *scid;
+ }
+ if (dnssec_random_buffer(params.stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN) != DNSSEC_EOK) {
+ return KNOT_ERROR;
+ }
+
+ if (server) {
+ return ngtcp2_conn_server_new(pconn, dcid, scid, path, version, &callbacks, &settings, &params, NULL, user_data);
+ } else {
+ return ngtcp2_conn_client_new(pconn, dcid, scid, path, version, &callbacks, &settings, &params, NULL, user_data);
+ }
+}
+
+_public_
+int knot_xquic_client(knot_xquic_table_t *table, struct sockaddr_in6 *dest,
+ struct sockaddr_in6 *via, knot_xquic_conn_t **out_conn)
+{
+ ngtcp2_cid scid = { 0 }, dcid = { 0 };
+ uint64_t now = get_timestamp();
+
+ init_random_cid(&scid, 0);
+ init_random_cid(&dcid, 0);
+
+ knot_xquic_conn_t *xconn = xquic_table_add(NULL, &dcid, table);
+ if (xconn == NULL) {
+ return ENOMEM;
+ }
+ xquic_conn_mark_used(xconn, table, now);
+
+ ngtcp2_path path;
+ path.remote.addr = (struct sockaddr *)dest;
+ path.remote.addrlen = addr_len((const struct sockaddr_in6 *)dest);
+ path.local.addr = (struct sockaddr *)via;
+ path.local.addrlen = addr_len((const struct sockaddr_in6 *)via);
+
+ int ret = conn_new(&xconn->conn, &path, &dcid, &scid, &dcid, NGTCP2_PROTO_VER_V1, now,
+ table->udp_payload_limit, 5000000000L, xconn, false, false);
+ if (ret == KNOT_EOK) {
+ ret = tls_init_conn_session(xconn, false);
+ }
+ if (ret == KNOT_EOK) {
+ char *hostname = sockaddr_hostname();
+ if (hostname == NULL) {
+ ret = KNOT_ENOMEM;
+ } else {
+ ret = gnutls_server_name_set(xconn->tls_session, GNUTLS_NAME_DNS,
+ hostname, strlen(hostname));
+ free(hostname);
+ }
+ }
+ if (ret != KNOT_EOK) {
+ knot_xquic_table_rem(xconn, table);
+ return ret;
+ }
+
+ *out_conn = xconn;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg, uint64_t idle_timeout, knot_xquic_conn_t **out_conn)
+{
+ *out_conn = NULL;
+
+ ngtcp2_version_cid decoded_cids = { 0 };
+ ngtcp2_cid scid = { 0 }, dcid = { 0 }, odcid = { 0 };
+ uint64_t now = get_timestamp();
+ int ret = ngtcp2_pkt_decode_version_cid(&decoded_cids,
+ msg->payload.iov_base,
+ msg->payload.iov_len,
+ SERVER_DEFAULT_SCIDLEN);
+ if (ret == NGTCP2_ERR_VERSION_NEGOTIATION) {
+ return -XQUIC_SEND_VERSION_NEGOTIATION;
+ } else if (ret != NGTCP2_NO_ERROR) {
+ return ret;
+ }
+ ngtcp2_cid_init(&dcid, decoded_cids.dcid, decoded_cids.dcidlen);
+ ngtcp2_cid_init(&scid, decoded_cids.scid, decoded_cids.scidlen);
+
+ knot_xquic_conn_t *xconn = xquic_table_lookup(&dcid, table);
+
+ if (decoded_cids.version == 0 /* short header */ && xconn == NULL) {
+ return KNOT_EOK; // NOOP
+ }
+
+ ngtcp2_path path;
+ path.remote.addr = (struct sockaddr *)&msg->ip_from;
+ path.remote.addrlen = addr_len(&msg->ip_from);
+ path.local.addr = (struct sockaddr *)&msg->ip_to;
+ path.local.addrlen = addr_len(&msg->ip_to);
+
+ if (xconn == NULL) {
+ // new conn
+
+ ngtcp2_pkt_hd header = { 0 };
+ ret = ngtcp2_accept(&header, msg->payload.iov_base, msg->payload.iov_len);
+ if (ret == NGTCP2_ERR_RETRY) {
+ return -XQUIC_SEND_RETRY;
+ } else if (ret != NGTCP2_NO_ERROR) { // discard packet
+ return KNOT_EOK;
+ }
+
+ assert(header.type == NGTCP2_PKT_INITIAL);
+ if (header.tokenlen == 0 && xquic_require_retry(table)) {
+ return -XQUIC_SEND_RETRY;
+ }
+
+ if (header.tokenlen > 0) {
+ ret = ngtcp2_crypto_verify_retry_token(
+ &odcid, header.token, header.tokenlen,
+ (const uint8_t *)table->hash_secret, sizeof(table->hash_secret), header.version,
+ (const struct sockaddr *)&msg->ip_from, addr_len(&msg->ip_from),
+ &dcid, idle_timeout, now // NOTE setting retry token validity to idle_timeout for simplicity
+ );
+ if (ret != 0) {
+ return KNOT_EOK;
+ }
+ } else {
+ memcpy(&odcid, &dcid, sizeof(odcid));
+ }
+
+ // server chooses his CID to his liking
+ if (!init_unique_cid(&dcid, 0, table)) {
+ return KNOT_ERROR;
+ }
+
+ xconn = xquic_table_add(NULL, &dcid, table);
+ if (xconn == NULL) {
+ return ENOMEM;
+ }
+ xquic_conn_mark_used(xconn, table, now);
+
+ ret = conn_new(&xconn->conn, &path, &dcid, &scid, &odcid, decoded_cids.version, now,
+ table->udp_payload_limit, idle_timeout, xconn, true, header.tokenlen > 0);
+ if (ret >= 0) {
+ ret = tls_init_conn_session(xconn, true);
+ }
+ if (ret < 0) {
+ knot_xquic_table_rem(xconn, table);
+ return ret;
+ }
+ }
+
+ ngtcp2_pkt_info pi = { .ecn = NGTCP2_ECN_NOT_ECT, };
+
+ ret = ngtcp2_conn_read_pkt(xconn->conn, &path, &pi, msg->payload.iov_base, msg->payload.iov_len, now);
+
+ *out_conn = xconn;
+ if (ret == NGTCP2_ERR_DRAINING) { // received CONNECTION_CLOSE from the counterpart
+ knot_xquic_table_rem(xconn, table);
+ return KNOT_EOK;
+ } else if(ngtcp2_err_is_fatal(ret)) { // connection doomed
+ knot_xquic_table_rem(xconn, table);
+ return KNOT_ECONN;
+ } else if (ret != NGTCP2_NO_ERROR) { // non-fatal error, discard packet
+ return KNOT_EOK;
+ }
+
+ xquic_conn_mark_used(xconn, table, now);
+
+ return KNOT_EOK;
+}
+
+static bool stream_exists(knot_xquic_conn_t *xconn, int64_t stream_id)
+{
+ // TRICK, we never use stream_user_data
+ return (ngtcp2_conn_set_stream_user_data(xconn->conn, stream_id, NULL) == NGTCP2_NO_ERROR);
+}
+
+static int send_stream(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock,
+ knot_xdp_msg_t *in_msg, knot_xquic_conn_t *relay, int64_t stream_id,
+ uint8_t *data, size_t len, bool fin, ngtcp2_ssize *sent)
+{
+ (void)quic_table;
+ assert(stream_id >= 0 || (data == NULL && len == 0));
+
+ while (stream_id >= 0 && !stream_exists(relay, stream_id)) {
+ int64_t opened = 0;
+ int ret = ngtcp2_conn_open_bidi_stream(relay->conn, &opened, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ assert((bool)(opened == stream_id) == stream_exists(relay, stream_id));
+ }
+
+ uint32_t xdp_sent = 0;
+ knot_xdp_msg_t out_msg = { 0 };
+ int ret = knot_xdp_reply_alloc(sock, in_msg, &out_msg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint32_t fl = ((stream_id >= 0 && fin) ? NGTCP2_WRITE_STREAM_FLAG_FIN : NGTCP2_WRITE_STREAM_FLAG_NONE);
+ ngtcp2_vec vec = { .base = data, .len = len };
+
+ ret = ngtcp2_conn_writev_stream(relay->conn, NULL, NULL, out_msg.payload.iov_base, out_msg.payload.iov_len,
+ sent, fl, stream_id, &vec, (stream_id >= 0 ? 1 : 0), get_timestamp());
+ if (ret <= 0) {
+ knot_xdp_send_free(sock, &out_msg, 1);
+ return ret;
+ }
+ if (*sent < 0) {
+ *sent = 0;
+ }
+
+ out_msg.payload.iov_len = ret;
+ ret = knot_xdp_send(sock, &out_msg, 1, &xdp_sent);
+ if (ret == KNOT_EOK) {
+ assert(xdp_sent == 1);
+ return 1;
+ }
+ return ret;
+}
+
+static int send_special(knot_xquic_table_t *quic_table, knot_xdp_socket_t *sock,
+ knot_xdp_msg_t *in_msg, int handle_ret)
+{
+ knot_xdp_msg_t out_msg;
+ int ret = knot_xdp_reply_alloc(sock, in_msg, &out_msg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint64_t now = get_timestamp();
+ ngtcp2_version_cid decoded_cids = { 0 };
+ ngtcp2_cid scid = { 0 }, dcid = { 0 };
+
+ int dvc_ret = ngtcp2_pkt_decode_version_cid(&decoded_cids,
+ in_msg->payload.iov_base,
+ in_msg->payload.iov_len,
+ SERVER_DEFAULT_SCIDLEN);
+
+ uint8_t rnd = 0;
+ dnssec_random_buffer(&rnd, sizeof(rnd));
+ uint32_t supported_quic[1] = { NGTCP2_PROTO_VER_V1 };
+ ngtcp2_cid new_dcid;
+ uint8_t retry_token[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
+ uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
+ uint8_t sreset_rand[NGTCP2_MIN_STATELESS_RESET_RANDLEN];
+ dnssec_random_buffer(sreset_rand, sizeof(sreset_rand));
+
+ switch (handle_ret) {
+ case -XQUIC_SEND_VERSION_NEGOTIATION:
+ if (dvc_ret != NGTCP2_ERR_VERSION_NEGOTIATION) {
+ return KNOT_ERROR;
+ }
+ ret = ngtcp2_pkt_write_version_negotiation(
+ out_msg.payload.iov_base, out_msg.payload.iov_len,
+ rnd, decoded_cids.scid, decoded_cids.scidlen, decoded_cids.dcid,
+ decoded_cids.dcidlen, supported_quic,
+ sizeof(supported_quic) / sizeof(*supported_quic)
+ );
+ break;
+ case -XQUIC_SEND_RETRY:
+ ngtcp2_cid_init(&dcid, decoded_cids.dcid, decoded_cids.dcidlen);
+ ngtcp2_cid_init(&scid, decoded_cids.scid, decoded_cids.scidlen);
+
+ init_random_cid(&new_dcid, 0);
+
+ ret = ngtcp2_crypto_generate_retry_token(
+ retry_token, (const uint8_t *)quic_table->hash_secret, sizeof(quic_table->hash_secret), decoded_cids.version,
+ (const struct sockaddr *)&in_msg->ip_from, sockaddr_len((const struct sockaddr_storage *)&in_msg->ip_from),
+ &new_dcid, &dcid, now
+ );
+
+ if (ret >= 0) {
+ ret = ngtcp2_crypto_write_retry(
+ out_msg.payload.iov_base, out_msg.payload.iov_len,
+ decoded_cids.version, &scid, &new_dcid, &dcid, retry_token, ret
+ );
+ }
+ break;
+ case -XQUIC_SEND_STATELESS_RESET:
+ ret = ngtcp2_pkt_write_stateless_reset(
+ out_msg.payload.iov_base, out_msg.payload.iov_len,
+ stateless_reset_token, sreset_rand, sizeof(sreset_rand)
+ );
+ break;
+ default:
+ ret = KNOT_EINVAL;
+ break;
+ }
+
+ if (ret < 0) {
+ knot_xdp_send_free(sock, &out_msg, 1);
+ } else {
+ uint32_t sent;
+ out_msg.payload.iov_len = ret;
+ ret = knot_xdp_send(sock, &out_msg, 1, &sent);
+ }
+ return ret;
+}
+
+_public_
+int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay,
+ knot_xdp_socket_t *sock, knot_xdp_msg_t *in_msg,
+ int handle_ret, unsigned max_msgs, bool ignore_lastbyte)
+{
+ if (handle_ret < 0) {
+ return handle_ret;
+ } else if (handle_ret > 0) {
+ return send_special(quic_table, sock, in_msg, handle_ret);
+ } else if (relay == NULL) {
+ return KNOT_EINVAL;
+ } else if (relay->conn == NULL) {
+ return KNOT_EOK;
+ }
+
+ unsigned sent_msgs = 0, stream_msgs = 0;
+ int ret = 1;
+ for (int64_t si = 0; si < relay->streams_count && sent_msgs < max_msgs; /* NO INCREMENT */) {
+ int64_t stream_id = 4 * (relay->streams_first + si);
+
+ ngtcp2_ssize sent = 0;
+ size_t uf = relay->streams[si].unsent_offset;
+ knot_xquic_obuf_t *uo = relay->streams[si].unsent_obuf;
+ if (uo == NULL) {
+ si++;
+ continue;
+ }
+
+ bool fin = (((node_t *)uo->node.next)->next == NULL) && !ignore_lastbyte;
+ ret = send_stream(quic_table, sock, in_msg, relay, stream_id,
+ uo->buf + uf, uo->len - uf - (ignore_lastbyte ? 1 : 0),
+ fin, &sent);
+ if (ret < 0) {
+ return ret;
+ }
+
+ sent_msgs++;
+ stream_msgs++;
+ if (sent > 0 && ignore_lastbyte) {
+ sent++;
+ }
+ if (sent > 0) {
+ knot_xquic_stream_mark_sent(relay, stream_id, sent);
+ }
+
+ if (stream_msgs >= max_msgs / relay->streams_count) {
+ stream_msgs = 0;
+ si++; // if this stream is sending too much, give chance to other streams
+ }
+ }
+
+ while (ret == 1) {
+ ngtcp2_ssize unused = 0;
+ ret = send_stream(quic_table, sock, in_msg, relay, -1, NULL, 0, false, &unused);
+ }
+
+ return ret;
+}
diff --git a/src/libknot/xdp/quic.h b/src/libknot/xdp/quic.h
new file mode 100644
index 0000000..943a2f3
--- /dev/null
+++ b/src/libknot/xdp/quic.h
@@ -0,0 +1,134 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief General QUIC functionality.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/xdp/quic_conn.h"
+#include "libknot/xdp/xdp.h"
+
+struct knot_quic_creds;
+struct knot_quic_session;
+
+/*!
+ * \brief Gets data needed for session resumption.
+ *
+ * \param conn QUIC connection.
+ *
+ * \return QUIC session context.
+ */
+struct knot_quic_session *knot_xquic_session_save(knot_xquic_conn_t *conn);
+
+/*!
+ * \brief Loads data needed for session resumption.
+ *
+ * \param conn QUIC connection.
+ * \param session QUIC session context.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_session_load(knot_xquic_conn_t *conn, struct knot_quic_session *session);
+
+/*!
+ * \brief Init server TLS certificate for DoQ.
+ *
+ * \param server Initializing for server-side (client otherwise).
+ * \param tls_cert X509 certificate PEM file path/name.
+ * \param tls_key Key PEM file path/name.
+ *
+ * \return Initialized creds.
+ */
+struct knot_quic_creds *knot_xquic_init_creds(bool server, const char *tls_cert,
+ const char *tls_key);
+
+/*!
+ * \brief Init server TLS certificate for DoQ.
+ */
+void knot_xquic_free_creds(struct knot_quic_creds *creds);
+
+/*!
+ * \brief Returns timeout value for the connection.
+ */
+uint64_t xquic_conn_get_timeout(knot_xquic_conn_t *conn);
+
+/*!
+ * \brief Check if connection timed out due to inactivity.
+ *
+ * \param conn QUIC connection.
+ * \param now In/out: current monotonic time. Use zero first and reuse for
+ * next calls for optimization.
+ *
+ * \return True if the connection timed out idle.
+ */
+bool xquic_conn_timeout(knot_xquic_conn_t *conn, uint64_t *now);
+
+/*!
+ * \brief Returns measured connection RTT in usecs.
+ */
+uint32_t knot_xquic_conn_rtt(knot_xquic_conn_t *conn);
+
+/*!
+ * \brief Create new outgoing QUIC connection.
+ *
+ * \param table QUIC connections table to be added to.
+ * \param dest Destination IP address.
+ * \param via Source IP address.
+ * \param out_conn Out: new connection.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_client(knot_xquic_table_t *table, struct sockaddr_in6 *dest,
+ struct sockaddr_in6 *via, knot_xquic_conn_t **out_conn);
+
+/*!
+ * \brief Handle incoming QUIC packet.
+ *
+ * \param table QUIC connectoins table-
+ * \param msg Incoming XDP packet.
+ * \param idle_timeout Configured idle timeout for connections (in nanoseconds).
+ * \param out_conn Out: QUIC connection that this packet belongs to.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_handle(knot_xquic_table_t *table, knot_xdp_msg_t *msg,
+ uint64_t idle_timeout, knot_xquic_conn_t **out_conn);
+
+/*!
+ * \brief Send outgoing QUIC packet(s) for a connection.
+ *
+ * \param quic_table QUIC connection table.
+ * \param relay QUIC connection.
+ * \param sock XDP socket.
+ * \param in_msg Previous incomming packet for this connection.
+ * \param handle_ret Error returned from knot_xquic_handle() for incoming packet.
+ * \param max_msgs Maxmimum packets to be sent.
+ * \param ignore_lastbyte Cut off last byte of QUIC paylod.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_send(knot_xquic_table_t *quic_table, knot_xquic_conn_t *relay,
+ knot_xdp_socket_t *sock, knot_xdp_msg_t *in_msg,
+ int handle_ret, unsigned max_msgs, bool ignore_lastbyte);
+
+/*! @} */
diff --git a/src/libknot/xdp/quic_conn.c b/src/libknot/xdp/quic_conn.c
new file mode 100644
index 0000000..40a5d5b
--- /dev/null
+++ b/src/libknot/xdp/quic_conn.c
@@ -0,0 +1,506 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <gnutls/gnutls.h>
+#include <ngtcp2/ngtcp2.h>
+#include <string.h>
+
+#include "libknot/xdp/quic_conn.h"
+
+#include "contrib/macros.h"
+#include "contrib/openbsd/siphash.h"
+#include "contrib/ucw/lists.h"
+#include "libdnssec/random.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+#include "libknot/xdp/quic.h"
+#include "libknot/xdp/tcp_iobuf.h"
+#include "libknot/wire.h"
+
+#define STREAM_INCR 4 // DoQ only uses client-initiated bi-directional streams, so stream IDs increment by four
+#define BUCKETS_PER_CONNS 8 // Each connecion has several dCIDs, and each CID takes one hash table bucket.
+
+_public_
+knot_xquic_table_t *knot_xquic_table_new(size_t max_conns, size_t max_ibufs, size_t max_obufs,
+ size_t udp_payload, struct knot_quic_creds *creds)
+{
+ size_t table_size = max_conns * BUCKETS_PER_CONNS;
+
+ knot_xquic_table_t *res = calloc(1, sizeof(*res) + table_size * sizeof(res->conns[0]));
+ if (res == NULL) {
+ return NULL;
+ }
+
+ res->size = table_size;
+ res->max_conns = max_conns;
+ res->ibufs_max = max_ibufs;
+ res->obufs_max = max_obufs;
+ res->udp_payload_limit = udp_payload;
+ init_list((list_t *)&res->timeout);
+
+ res->creds = creds;
+
+ res->hash_secret[0] = dnssec_random_uint64_t();
+ res->hash_secret[1] = dnssec_random_uint64_t();
+ res->hash_secret[2] = dnssec_random_uint64_t();
+ res->hash_secret[3] = dnssec_random_uint64_t();
+
+ return res;
+}
+
+_public_
+void knot_xquic_table_free(knot_xquic_table_t *table)
+{
+ if (table != NULL) {
+ knot_xquic_conn_t *c, *next;
+ list_t *tto = (list_t *)&table->timeout;
+ WALK_LIST_DELSAFE(c, next, *tto) {
+ knot_xquic_table_rem(c, table);
+ knot_xquic_cleanup(&c, 1);
+ }
+ assert(table->usage == 0);
+ assert(table->pointers == 0);
+ assert(table->obufs_size == 0);
+
+ free(table);
+ }
+}
+
+_public_
+int knot_xquic_table_sweep(knot_xquic_table_t *table, struct knot_sweep_stats *stats)
+{
+ uint64_t now = 0;
+ knot_xquic_conn_t *c, *next;
+ list_t *tto = (list_t *)&table->timeout;
+ WALK_LIST_DELSAFE(c, next, *tto) {
+ if (xquic_conn_timeout(c, &now)) {
+ knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_TIMEOUT);
+ knot_xquic_table_rem(c, table);
+ } else if (table->usage > table->max_conns) {
+ knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_LIMIT_CONN);
+ knot_xquic_table_rem(c, table);
+ // NOTE here it would be correct to send Immediate close
+ // with DoQ errcode DOQ_EXCESSIVE_LOAD
+ // nowever, we don't do this for the sake of simplicty
+ // it would be possible to send by using ngtcp2_conn_get_path()...
+ // (also applies to below case)
+ } else if (table->obufs_size > table->obufs_max) {
+ if (c->obufs_size > 0) {
+ knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_LIMIT_OBUF);
+ knot_xquic_table_rem(c, table);
+ }
+ } else if (table->ibufs_size > table->ibufs_max) {
+ if (c->ibufs_size > 0) {
+ knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_LIMIT_IBUF);
+ knot_xquic_table_rem(c, table);
+ }
+ } else {
+ break;
+ }
+ knot_xquic_cleanup(&c, 1);
+ }
+ return KNOT_EOK;
+}
+
+static uint64_t cid2hash(const ngtcp2_cid *cid, knot_xquic_table_t *table)
+{
+ SIPHASH_CTX ctx;
+ SipHash24_Init(&ctx, (const SIPHASH_KEY *)(table->hash_secret));
+ SipHash24_Update(&ctx, cid->data, MIN(cid->datalen, 8));
+ uint64_t ret = SipHash24_End(&ctx);
+ return ret;
+}
+
+knot_xquic_cid_t **xquic_table_insert(knot_xquic_conn_t *xconn, const ngtcp2_cid *cid,
+ knot_xquic_table_t *table)
+{
+ uint64_t hash = cid2hash(cid, table);
+
+ knot_xquic_cid_t *cidobj = malloc(sizeof(*cidobj));
+ if (cidobj == NULL) {
+ return NULL;
+ }
+ _Static_assert(sizeof(*cid) <= sizeof(cidobj->cid_placeholder), "insufficient placeholder for CID struct");
+ memcpy(cidobj->cid_placeholder, cid, sizeof(*cid));
+ cidobj->conn = xconn;
+
+ knot_xquic_cid_t **addto = table->conns + (hash % table->size);
+
+ cidobj->next = *addto;
+ *addto = cidobj;
+ table->pointers++;
+
+ return addto;
+}
+
+knot_xquic_conn_t *xquic_table_add(ngtcp2_conn *conn, const ngtcp2_cid *cid,
+ knot_xquic_table_t *table)
+{
+ knot_xquic_conn_t *xconn = calloc(1, sizeof(*xconn));
+ if (xconn == NULL) {
+ return NULL;
+ }
+
+ xconn->conn = conn;
+ xconn->xquic_table = table;
+ xconn->stream_inprocess = -1;
+
+ knot_xquic_cid_t **addto = xquic_table_insert(xconn, cid, table);
+ if (addto == NULL) {
+ free(xconn);
+ return NULL;
+ }
+ table->usage++;
+
+ return xconn;
+}
+
+knot_xquic_cid_t **xquic_table_lookup2(const ngtcp2_cid *cid, knot_xquic_table_t *table)
+{
+ uint64_t hash = cid2hash(cid, table);
+
+ knot_xquic_cid_t **res = table->conns + (hash % table->size);
+ while (*res != NULL && !ngtcp2_cid_eq(cid, (const ngtcp2_cid *)(*res)->cid_placeholder)) {
+ res = &(*res)->next;
+ }
+ return res;
+}
+
+knot_xquic_conn_t *xquic_table_lookup(const ngtcp2_cid *cid, knot_xquic_table_t *table)
+{
+ knot_xquic_cid_t **pcid = xquic_table_lookup2(cid, table);
+ assert(pcid != NULL);
+ return *pcid == NULL ? NULL : (*pcid)->conn;
+}
+
+void xquic_conn_mark_used(knot_xquic_conn_t *conn, knot_xquic_table_t *table,
+ uint64_t now)
+{
+ node_t *n = (node_t *)&conn->timeout;
+ list_t *l = (list_t *)&table->timeout;
+ if (n->next != NULL) {
+ rem_node(n);
+ }
+ add_tail(l, n);
+ conn->last_ts = now;
+}
+
+void xquic_table_rem2(knot_xquic_cid_t **pcid, knot_xquic_table_t *table)
+{
+ knot_xquic_cid_t *cid = *pcid;
+ *pcid = cid->next;
+ free(cid);
+ table->pointers--;
+}
+
+void xquic_stream_free(knot_xquic_conn_t *xconn, int64_t stream_id)
+{
+ knot_xquic_stream_ack_data(xconn, stream_id, SIZE_MAX, false);
+}
+
+_public_
+void knot_xquic_table_rem(knot_xquic_conn_t *conn, knot_xquic_table_t *table)
+{
+ if (conn->streams_count == -1) { // kxdpgun special
+ conn->streams_count = 1;
+ }
+ for (ssize_t i = conn->streams_count - 1; i >= 0; i--) {
+ xquic_stream_free(conn, (i + conn->streams_first) * 4);
+ }
+ assert(conn->streams_count <= 0);
+ assert(conn->obufs_size == 0);
+
+ size_t num_scid = ngtcp2_conn_get_num_scid(conn->conn);
+ ngtcp2_cid *scids = calloc(num_scid, sizeof(*scids));
+ ngtcp2_conn_get_scid(conn->conn, scids);
+
+ for (size_t i = 0; i < num_scid; i++) {
+ knot_xquic_cid_t **pcid = xquic_table_lookup2(&scids[i], table);
+ assert(pcid != NULL);
+ if (*pcid == NULL) {
+ continue;
+ }
+ assert((*pcid)->conn == conn);
+ xquic_table_rem2(pcid, table);
+ }
+
+ rem_node((node_t *)&conn->timeout);
+
+ free(scids);
+
+ gnutls_deinit(conn->tls_session);
+ ngtcp2_conn_del(conn->conn);
+ conn->conn = NULL;
+
+ table->usage--;
+}
+
+_public_
+knot_xquic_stream_t *knot_xquic_conn_get_stream(knot_xquic_conn_t *xconn,
+ int64_t stream_id, bool create)
+{
+ if (stream_id % 4 != 0) {
+ return NULL;
+ }
+ stream_id /= 4;
+
+ if (xconn->streams_first > stream_id) {
+ return NULL;
+ }
+ if (xconn->streams_count > stream_id - xconn->streams_first) {
+ return &xconn->streams[stream_id - xconn->streams_first];
+ }
+
+ if (create) {
+ size_t new_streams_count;
+ knot_xquic_stream_t *new_streams;
+
+ if (xconn->streams_count == 0) {
+ new_streams = malloc(sizeof(new_streams[0]));
+ if (new_streams == NULL) {
+ return NULL;
+ }
+ new_streams_count = 1;
+ xconn->streams_first = stream_id;
+ } else {
+ new_streams_count = stream_id + 1 - xconn->streams_first;
+ if (new_streams_count > MAX_STREAMS_PER_CONN) {
+ return NULL;
+ }
+ new_streams = realloc(xconn->streams, new_streams_count * sizeof(*new_streams));
+ if (new_streams == NULL) {
+ return NULL;
+ }
+ }
+
+ for (knot_xquic_stream_t *si = new_streams;
+ si < new_streams + xconn->streams_count; si++) {
+ if (si->obufs_size == 0) {
+ init_list((list_t *)&si->outbufs);
+ } else {
+ fix_list((list_t *)&si->outbufs);
+ }
+ }
+
+ for (knot_xquic_stream_t *si = new_streams + xconn->streams_count;
+ si < new_streams + new_streams_count; si++) {
+ memset(si, 0, sizeof(*si));
+ init_list((list_t *)&si->outbufs);
+ }
+ xconn->streams = new_streams;
+ xconn->streams_count = new_streams_count;
+
+ return &xconn->streams[stream_id - xconn->streams_first];
+ }
+ return NULL;
+}
+
+static void stream_inprocess(knot_xquic_conn_t *xconn, knot_xquic_stream_t *stream)
+{
+ int16_t idx = stream - xconn->streams;
+ assert(idx >= 0);
+ assert(idx < xconn->streams_count);
+ if (xconn->stream_inprocess < 0 || xconn->stream_inprocess > idx) {
+ xconn->stream_inprocess = idx;
+ }
+}
+
+static void stream_outprocess(knot_xquic_conn_t *xconn, knot_xquic_stream_t *stream)
+{
+ if (stream != &xconn->streams[xconn->stream_inprocess]) {
+ return;
+ }
+
+ for (int16_t idx = xconn->stream_inprocess + 1; idx < xconn->streams_count; idx++) {
+ stream = &xconn->streams[idx];
+ if (stream->inbuf_fin != NULL) {
+ xconn->stream_inprocess = stream - xconn->streams;
+ return;
+ }
+ }
+ xconn->stream_inprocess = -1;
+}
+
+int knot_xquic_stream_recv_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ const uint8_t *data, size_t len, bool fin)
+{
+ if (len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ knot_xquic_stream_t *stream = knot_xquic_conn_get_stream(xconn, stream_id, true);
+ if (stream == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ struct iovec in = { (void *)data, len }, *outs;
+ size_t outs_count;
+ int ret = knot_tcp_inbuf_update(&stream->inbuf, in, &outs, &outs_count,
+ &xconn->ibufs_size);
+ if (ret != KNOT_EOK || (outs_count == 0 && !fin)) {
+ return ret;
+ }
+ if (outs_count != 1 || !fin) {
+ free(outs);
+ return KNOT_ESEMCHECK;
+ }
+
+ stream->inbuf_fin = outs;
+ stream_inprocess(xconn, stream);
+ return KNOT_EOK;
+}
+
+_public_
+knot_xquic_stream_t *knot_xquic_stream_get_process(knot_xquic_conn_t *xconn,
+ int64_t *stream_id)
+{
+ if (xconn->stream_inprocess < 0) {
+ return NULL;
+ }
+
+ knot_xquic_stream_t *stream = &xconn->streams[xconn->stream_inprocess];
+ *stream_id = (xconn->streams_first + xconn->stream_inprocess) * 4;
+ stream_outprocess(xconn, stream);
+ return stream;
+}
+
+_public_
+uint8_t *knot_xquic_stream_add_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ uint8_t *data, size_t len)
+{
+ knot_xquic_stream_t *s = knot_xquic_conn_get_stream(xconn, stream_id, true);
+ if (s == NULL) {
+ return NULL;
+ }
+
+ size_t prefix = sizeof(uint16_t);
+
+ knot_xquic_obuf_t *obuf = malloc(sizeof(*obuf) + prefix + len);
+ if (obuf == NULL) {
+ return NULL;
+ }
+
+ obuf->len = len + prefix;
+ knot_wire_write_u16(obuf->buf, len);
+ if (data != NULL) {
+ memcpy(obuf->buf + prefix, data, len);
+ }
+
+ list_t *list = (list_t *)&s->outbufs;
+ if (EMPTY_LIST(*list)) {
+ s->unsent_obuf = obuf;
+ }
+ add_tail((list_t *)&s->outbufs, (node_t *)obuf);
+ s->obufs_size += obuf->len;
+ xconn->obufs_size += obuf->len;
+ xconn->xquic_table->obufs_size += obuf->len;
+
+ return obuf->buf + prefix;
+}
+
+void knot_xquic_stream_ack_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ size_t end_acked, bool keep_stream)
+{
+ knot_xquic_stream_t *s = knot_xquic_conn_get_stream(xconn, stream_id, false);
+ if (s == NULL) {
+ return;
+ }
+
+ list_t *obs = (list_t *)&s->outbufs;
+
+ knot_xquic_obuf_t *first;
+ while (!EMPTY_LIST(*obs) && end_acked >= (first = HEAD(*obs))->len + s->first_offset) {
+ rem_node((node_t *)first);
+ s->obufs_size -= first->len;
+ xconn->obufs_size -= first->len;
+ xconn->xquic_table->obufs_size -= first->len;
+ s->first_offset += first->len;
+ free(first);
+ if (s->unsent_obuf == first) {
+ s->unsent_obuf = EMPTY_LIST(*obs) ? NULL : HEAD(*obs);
+ s->unsent_offset = 0;
+ }
+ }
+
+ if (EMPTY_LIST(*obs) && !keep_stream) {
+ stream_outprocess(xconn, s);
+ memset(s, 0, sizeof(*s));
+ while (s = &xconn->streams[0], s->inbuf.iov_len == 0 && s->inbuf_fin == NULL && s->obufs_size == 0) {
+ assert(xconn->streams_count > 0);
+ xconn->streams_count--;
+
+ if (xconn->streams_count == 0) {
+ free(xconn->streams);
+ xconn->streams = 0;
+ xconn->streams_first = 0;
+ break;
+ } else {
+ xconn->streams_first++;
+ xconn->stream_inprocess--;
+ memmove(s, s + 1, sizeof(*s) * xconn->streams_count);
+ // possible realloc to shrink allocated space, but probably useless
+ for (knot_xquic_stream_t *si = s; si < s + xconn->streams_count; si++) {
+ if (si->obufs_size == 0) {
+ init_list((list_t *)&si->outbufs);
+ } else {
+ fix_list((list_t *)&si->outbufs);
+ }
+ }
+ }
+ }
+ }
+}
+
+void knot_xquic_stream_mark_sent(knot_xquic_conn_t *xconn, int64_t stream_id,
+ size_t amount_sent)
+{
+ knot_xquic_stream_t *s = knot_xquic_conn_get_stream(xconn, stream_id, false);
+ if (s == NULL) {
+ return;
+ }
+
+ s->unsent_offset += amount_sent;
+ assert(s->unsent_offset <= s->unsent_obuf->len);
+ if (s->unsent_offset == s->unsent_obuf->len) {
+ s->unsent_offset = 0;
+ s->unsent_obuf = (knot_xquic_obuf_t *)s->unsent_obuf->node.next;
+ if (s->unsent_obuf->node.next == NULL) { // already behind the tail of list
+ s->unsent_obuf = NULL;
+ }
+ }
+}
+
+_public_
+void knot_xquic_cleanup(knot_xquic_conn_t *conns[], size_t n_conns)
+{
+ for (size_t i = 0; i < n_conns; i++) {
+ if (conns[i] != NULL && conns[i]->conn == NULL) {
+ free(conns[i]);
+ for (size_t j = i + 1; j < n_conns; j++) {
+ if (conns[j] == conns[i]) {
+ conns[j] = NULL;
+ }
+ }
+ }
+ }
+}
+
+bool xquic_require_retry(knot_xquic_table_t *table)
+{
+ (void)table;
+ return false;
+}
diff --git a/src/libknot/xdp/quic_conn.h b/src/libknot/xdp/quic_conn.h
new file mode 100644
index 0000000..20ee176
--- /dev/null
+++ b/src/libknot/xdp/quic_conn.h
@@ -0,0 +1,314 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief QUIC connection management.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <linux/if_ether.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/uio.h>
+
+#define MAX_STREAMS_PER_CONN 10
+
+struct ngtcp2_cid; // declaration taken from wherever in ngtcp2
+struct knot_quic_creds;
+struct knot_sweep_stats;
+
+// those are equivalent to contrib/ucw/lists.h , just must not be included.
+typedef struct knot_xquic_ucw_node {
+ struct knot_xquic_ucw_node *next, *prev;
+} knot_xquic_ucw_node_t;
+typedef struct knot_xquic_ucw_list {
+ knot_xquic_ucw_node_t head, tail;
+} knot_xquic_ucw_list_t;
+
+typedef struct {
+ void *get_conn;
+ void *user_data;
+} nc_conn_ref_placeholder_t;
+
+typedef struct {
+ knot_xquic_ucw_node_t node;
+ size_t len;
+ uint8_t buf[];
+} knot_xquic_obuf_t;
+
+typedef struct {
+ struct iovec inbuf;
+ struct iovec *inbuf_fin;
+ knot_xquic_ucw_list_t outbufs;
+ size_t obufs_size;
+
+ knot_xquic_obuf_t *unsent_obuf;
+ size_t first_offset;
+ size_t unsent_offset;
+} knot_xquic_stream_t;
+
+typedef struct knot_xquic_conn {
+ knot_xquic_ucw_node_t timeout; // MUST be first field of the struct
+ uint64_t last_ts;
+
+ nc_conn_ref_placeholder_t conn_ref; // placeholder for internal struct ngtcp2_crypto_conn_ref
+
+ struct ngtcp2_conn *conn;
+
+ struct gnutls_session_int *tls_session;
+
+ knot_xquic_stream_t *streams;
+ int16_t streams_count; // number of allocated streams structures
+ int16_t stream_inprocess; // index of first stream that has complete incomming data to be processed (aka inbuf_fin)
+ bool handshake_done;
+ bool session_taken; // TODO ... ?
+ int64_t streams_first; // stream_id/4 of first allocated stream
+ size_t ibufs_size;
+ size_t obufs_size;
+
+ struct knot_xquic_table *xquic_table;
+
+ struct knot_xquic_conn *next;
+} knot_xquic_conn_t;
+
+typedef struct knot_xquic_cid {
+ uint8_t cid_placeholder[32];
+ knot_xquic_conn_t *conn;
+ struct knot_xquic_cid *next;
+} knot_xquic_cid_t;
+
+typedef struct knot_xquic_table {
+ size_t size;
+ size_t usage;
+ size_t pointers;
+ size_t max_conns;
+ size_t ibufs_max;
+ size_t obufs_max;
+ size_t ibufs_size;
+ size_t obufs_size;
+ size_t udp_payload_limit; // for simplicity not distinguishing IPv4/6
+ void (*log_cb)(const char *);
+ uint64_t hash_secret[4];
+ struct knot_quic_creds *creds;
+ knot_xquic_ucw_list_t timeout;
+ knot_xquic_cid_t *conns[];
+} knot_xquic_table_t;
+
+/*!
+ * \brief Allocate QUIC connections hash table.
+ *
+ * \param max_conns Maximum nuber of connections.
+ * \param max_ibufs Maximum size of buffers for fragmented incomming DNS msgs.
+ * \param max_obufs Maximum size of buffers for un-ACKed outgoing data.
+ * \param udp_pl Maximum UDP payload size (both IPv4 and 6).
+ * \param creds QUIC crypto context..
+ *
+ * \return Allocated table, or NULL.
+ */
+knot_xquic_table_t *knot_xquic_table_new(size_t max_conns, size_t max_ibufs, size_t max_obufs,
+ size_t udp_payload, struct knot_quic_creds *creds);
+
+/*!
+ * \brief Free QUIC table including its contents.
+ *
+ * \param table Table to be freed.
+ */
+void knot_xquic_table_free(knot_xquic_table_t *table);
+
+/*!
+ * \brief Close timed out connections and some oldest ones if table full.
+ *
+ * \param table QUIC table to be cleaned up.
+ * \param stats Out: sweep statistics.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_table_sweep(knot_xquic_table_t *table, struct knot_sweep_stats *stats);
+
+/*!
+ * \brief Add new connection/CID link to table.
+ *
+ * \param xconn QUIC connection linked.
+ * \param cid New CID to be added.
+ * \param table QUIC table to be modified.
+ *
+ * \return Pointer on the CID reference in table, or NULL.
+ */
+knot_xquic_cid_t **xquic_table_insert(knot_xquic_conn_t *xconn,
+ const struct ngtcp2_cid *cid,
+ knot_xquic_table_t *table);
+
+/*!
+ * \brief Add new connection to the table, allocating conn struct.
+ *
+ * \param conn Ngtcp2 conn struct.
+ * \param cid CID to be linked (usually oscid for server).
+ * \param table QUIC table to be modified.
+ *
+ * \return Allocated (and linked) Knot conn struct, or NULL.
+ */
+knot_xquic_conn_t *xquic_table_add(struct ngtcp2_conn *conn,
+ const struct ngtcp2_cid *cid,
+ knot_xquic_table_t *table);
+
+/*!
+ * \brief Lookup connection/CID link in table.
+ *
+ * \param cid CID to be searched for.
+ * \param table QUIC table.
+ *
+ * \return Pointer on the CID reference in table, or NULL.
+ */
+knot_xquic_cid_t **xquic_table_lookup2(const struct ngtcp2_cid *cid,
+ knot_xquic_table_t *table);
+
+/*!
+ * \brief Lookup QUIC connection in table.
+ *
+ * \param cid CID to be searched for.
+ * \param table QUIC table.
+ *
+ * \return Connection that the CID belongs to, or NULL.
+ */
+knot_xquic_conn_t *xquic_table_lookup(const struct ngtcp2_cid *cid,
+ knot_xquic_table_t *table);
+
+/*!
+ * \brief Put the connection on the end of timeout queue.
+ */
+void xquic_conn_mark_used(knot_xquic_conn_t *conn, knot_xquic_table_t *table,
+ uint64_t now);
+
+/*!
+ * \brief Remove connection/CID link from table.
+ *
+ * \param pcid CID to be removed.
+ * \param table QUIC table.
+ */
+void xquic_table_rem2(knot_xquic_cid_t **pcid, knot_xquic_table_t *table);
+
+/*!
+ * \brief Remove specified stream from QUIC connection, freeing all buffers.
+ *
+ * \param xconn QUIC connection to remove from.
+ * \param stream_id Stream QUIC ID.
+ */
+void xquic_stream_free(knot_xquic_conn_t *xconn, int64_t stream_id);
+
+/*!
+ * \brief Remove and deinitialize connection completely.
+ *
+ * \param conn Connection to be removed.
+ * \param table Table to remove from.
+ */
+void knot_xquic_table_rem(knot_xquic_conn_t *conn, knot_xquic_table_t *table);
+
+/*!
+ * \brief Fetch or initialize a QUIC stream.
+ *
+ * \param xconn QUIC connection.
+ * \param stream_id Stream QUIC ID.
+ * \param create Trigger stream creation if not exists.
+ *
+ * \return Stream or NULL.
+ */
+knot_xquic_stream_t *knot_xquic_conn_get_stream(knot_xquic_conn_t *xconn,
+ int64_t stream_id, bool create);
+
+/*!
+ * \brief Process incomming stream data to stream structure.
+ *
+ * \param xconn QUIC connection that has received data.
+ * \param stream_id Stream QUIC ID of the incomming data.
+ * \param data Incomming payload data.
+ * \param len Incomming payload data length.
+ * \param fin FIN flag set for incomming data.
+ *
+ * \return KNOT_E*
+ */
+int knot_xquic_stream_recv_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ const uint8_t *data, size_t len, bool fin);
+
+/*!
+ * \brief Get next stream which has pending incomming data to be processed.
+ *
+ * \param xconn QUIC connection.
+ * \param stream_id Out: strem QUIC ID of the returned stream.
+ *
+ * \return Stream with incomming data.
+ */
+knot_xquic_stream_t *knot_xquic_stream_get_process(knot_xquic_conn_t *xconn,
+ int64_t *stream_id);
+
+/*!
+ * \brief Add outgiong data to the stream for sending.
+ *
+ * \param xconn QUIC connection that shall send data.
+ * \param stream_id Stream ID for outgoing data.
+ * \param data Data payload.
+ * \param len Data payload length.
+ *
+ * \return NULL if error, or pinter at the data in outgiong buffer.
+ */
+uint8_t *knot_xquic_stream_add_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ uint8_t *data, size_t len);
+
+/*!
+ * \brief Mark outgiong data as acknowledged after ACK received.
+ *
+ * \param xconn QUIC connection that received ACK.
+ * \param stream_id Stream ID of ACKed data.
+ * \param end_acked Offset of ACKed data + ACKed length.
+ * \param keep_stream Don't free the stream even when ACKed all outgoing data.
+ */
+void knot_xquic_stream_ack_data(knot_xquic_conn_t *xconn, int64_t stream_id,
+ size_t end_acked, bool keep_stream);
+
+/*!
+ * \brief Mark outgoing data as sent.
+ *
+ * \param xconn QUIC connection that sent data.
+ * \param stream_id Stream ID of sent data.
+ * \param amount_sent Length of sent data.
+ */
+void knot_xquic_stream_mark_sent(knot_xquic_conn_t *xconn, int64_t stream_id,
+ size_t amount_sent);
+
+/*!
+ * \brief Free rest of resources of closed conns.
+ *
+ * \param conns Array with recently used conns (possibly NULLs).
+ * \param n_conns Size of the array.
+ */
+void knot_xquic_cleanup(knot_xquic_conn_t *conns[], size_t n_conns);
+
+/*!
+ * \brief Toggle sending Retry packet as a reaction to Initial packet of new connection.
+ *
+ * \param table Connection table.
+ *
+ * \return True if instead of continuing handshake, Retry packet shall be sent
+ * to verify counterpart's address.
+ */
+bool xquic_require_retry(knot_xquic_table_t *table);
+
+/*! @} */
diff --git a/src/libknot/xdp/tcp.c b/src/libknot/xdp/tcp.c
new file mode 100644
index 0000000..752e9ef
--- /dev/null
+++ b/src/libknot/xdp/tcp.c
@@ -0,0 +1,729 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libknot/xdp/tcp.h"
+#include "libknot/xdp/tcp_iobuf.h"
+#include "libknot/attribute.h"
+#include "libknot/error.h"
+#include "libdnssec/random.h"
+#include "contrib/macros.h"
+#include "contrib/openbsd/siphash.h"
+#include "contrib/ucw/lists.h"
+
+static uint32_t get_timestamp(void)
+{
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ uint64_t res = (uint64_t)t.tv_sec * 1000000;
+ res += (uint64_t)t.tv_nsec / 1000;
+ return res & 0xffffffff; // overflow does not matter since we are working with differences
+}
+
+static size_t sockaddr_data_len(const struct sockaddr_in6 *rem, const struct sockaddr_in6 *loc)
+{
+ assert(rem->sin6_family == loc->sin6_family);
+ if (rem->sin6_family == AF_INET) {
+ return offsetof(struct sockaddr_in, sin_zero);
+ } else {
+ assert(rem->sin6_family == AF_INET6);
+ return offsetof(struct sockaddr_in6, sin6_scope_id);
+ }
+}
+
+static uint64_t hash_four_tuple(const struct sockaddr_in6 *rem, const struct sockaddr_in6 *loc,
+ knot_tcp_table_t *table)
+{
+ size_t socka_data_len = sockaddr_data_len(rem, loc);
+ SIPHASH_CTX ctx;
+ SipHash24_Init(&ctx, (const SIPHASH_KEY *)(table->hash_secret));
+ SipHash24_Update(&ctx, rem, socka_data_len);
+ SipHash24_Update(&ctx, loc, socka_data_len);
+ return SipHash24_End(&ctx);
+}
+
+static list_t *tcp_table_timeout(knot_tcp_table_t *table)
+{
+ return (list_t *)&table->conns[table->size];
+}
+
+static node_t *tcp_conn_node(knot_tcp_conn_t *conn)
+{
+ return (node_t *)&conn->list_node_placeholder;
+}
+
+static void next_node_ptr(knot_tcp_conn_t **ptr)
+{
+ if (*ptr != NULL) {
+ *ptr = (*ptr)->list_node_placeholder.list_node_next;
+ if ((*ptr)->list_node_placeholder.list_node_next == NULL) { // detected tail of list
+ *ptr = NULL;
+ }
+ }
+}
+
+static void next_ptr_ibuf(knot_tcp_conn_t **ptr)
+{
+ do {
+ next_node_ptr(ptr);
+ } while (*ptr != NULL && (*ptr)->inbuf.iov_len == 0);
+}
+
+static void next_ptr_obuf(knot_tcp_conn_t **ptr)
+{
+ do {
+ next_node_ptr(ptr);
+ } while (*ptr != NULL && knot_tcp_outbufs_usage((*ptr)->outbufs) == 0);
+}
+
+_public_
+knot_tcp_table_t *knot_tcp_table_new(size_t size, knot_tcp_table_t *secret_share)
+{
+ knot_tcp_table_t *table = calloc(1, sizeof(*table) + sizeof(list_t) +
+ size * sizeof(table->conns[0]));
+ if (table == NULL) {
+ return table;
+ }
+
+ table->size = size;
+ init_list(tcp_table_timeout(table));
+
+ assert(sizeof(table->hash_secret) == sizeof(SIPHASH_KEY));
+ if (secret_share == NULL) {
+ table->hash_secret[0] = dnssec_random_uint64_t();
+ table->hash_secret[1] = dnssec_random_uint64_t();
+ } else {
+ table->hash_secret[0] = secret_share->hash_secret[0];
+ table->hash_secret[1] = secret_share->hash_secret[1];
+ }
+
+ return table;
+}
+
+static void del_conn(knot_tcp_conn_t *conn)
+{
+ if (conn != NULL) {
+ free(conn->inbuf.iov_base);
+ while (conn->outbufs != NULL) {
+ struct knot_tcp_outbuf *next = conn->outbufs->next;
+ free(conn->outbufs);
+ conn->outbufs = next;
+ }
+ free(conn);
+ }
+}
+
+_public_
+void knot_tcp_table_free(knot_tcp_table_t *table)
+{
+ if (table != NULL) {
+ knot_tcp_conn_t *conn, *next;
+ WALK_LIST_DELSAFE(conn, next, *tcp_table_timeout(table)) {
+ del_conn(conn);
+ }
+ free(table);
+ }
+}
+
+static knot_tcp_conn_t **tcp_table_lookup(const struct sockaddr_in6 *rem,
+ const struct sockaddr_in6 *loc,
+ uint64_t *hash, knot_tcp_table_t *table)
+{
+ if (*hash == 0) {
+ *hash = hash_four_tuple(rem, loc, table);
+ }
+ size_t sdl = sockaddr_data_len(rem, loc);
+ knot_tcp_conn_t **res = table->conns + (*hash % table->size);
+ while (*res != NULL) {
+ if (memcmp(&(*res)->ip_rem, rem, sdl) == 0 &&
+ memcmp(&(*res)->ip_loc, loc, sdl) == 0) {
+ break;
+ }
+ res = &(*res)->next;
+ }
+ return res;
+}
+
+static knot_tcp_conn_t **tcp_table_re_lookup(knot_tcp_conn_t *conn,
+ knot_tcp_table_t *table)
+{
+ uint64_t unused_hash = 0;
+ knot_tcp_conn_t **res = tcp_table_lookup(&conn->ip_rem, &conn->ip_loc,
+ &unused_hash, table);
+ assert(*res == conn);
+ return res;
+}
+
+static void rem_align_pointers(knot_tcp_conn_t *to_rem, knot_tcp_table_t *table)
+{
+ if (to_rem == table->next_close) {
+ next_node_ptr(&table->next_close);
+ }
+ if (to_rem == table->next_ibuf) {
+ next_ptr_ibuf(&table->next_ibuf);
+ }
+ if (to_rem == table->next_obuf) {
+ next_ptr_obuf(&table->next_obuf);
+ }
+ if (to_rem == table->next_resend) {
+ next_ptr_obuf(&table->next_resend);
+ }
+}
+
+static void tcp_table_remove_conn(knot_tcp_conn_t **todel)
+{
+ rem_node(tcp_conn_node(*todel)); // remove from timeout double-linked list
+ *todel = (*todel)->next; // remove from conn-table linked list
+}
+
+static void tcp_table_remove(knot_tcp_conn_t **todel, knot_tcp_table_t *table)
+{
+ assert(table->usage > 0);
+ rem_align_pointers(*todel, table);
+ table->inbufs_total -= (*todel)->inbuf.iov_len;
+ table->outbufs_total -= knot_tcp_outbufs_usage((*todel)->outbufs);
+ tcp_table_remove_conn(todel);
+ table->usage--;
+}
+
+static void conn_init_from_msg(knot_tcp_conn_t *conn, knot_xdp_msg_t *msg)
+{
+ memcpy(&conn->ip_rem, &msg->ip_from, sizeof(conn->ip_rem));
+ memcpy(&conn->ip_loc, &msg->ip_to, sizeof(conn->ip_loc));
+
+ memcpy(&conn->last_eth_rem, &msg->eth_from, sizeof(conn->last_eth_rem));
+ memcpy(&conn->last_eth_loc, &msg->eth_to, sizeof(conn->last_eth_loc));
+
+ conn->seqno = msg->seqno;
+ conn->ackno = msg->ackno;
+ conn->acked = msg->ackno;
+
+ conn->last_active = get_timestamp();
+ conn->state = XDP_TCP_NORMAL;
+ conn->establish_rtt = 0;
+
+ memset(&conn->inbuf, 0, sizeof(conn->inbuf));
+ memset(&conn->outbufs, 0, sizeof(conn->outbufs));
+}
+
+static void tcp_table_insert(knot_tcp_conn_t *conn, uint64_t hash,
+ knot_tcp_table_t *table)
+{
+ knot_tcp_conn_t **addto = table->conns + (hash % table->size);
+ add_tail(tcp_table_timeout(table), tcp_conn_node(conn));
+ if (table->next_close == NULL) {
+ table->next_close = conn;
+ }
+ conn->next = *addto;
+ *addto = conn;
+ table->usage++;
+}
+
+// WARNING you shall ensure that it's not in the table already!
+static int tcp_table_add(knot_xdp_msg_t *msg, uint64_t hash, knot_tcp_table_t *table,
+ knot_tcp_conn_t **res)
+{
+ knot_tcp_conn_t *c = malloc(sizeof(*c));
+ if (c == NULL) {
+ return KNOT_ENOMEM;
+ }
+ conn_init_from_msg(c, msg);
+ tcp_table_insert(c, hash, table);
+ *res = c;
+ return KNOT_EOK;
+}
+
+static bool check_seq_ack(const knot_xdp_msg_t *msg, const knot_tcp_conn_t *conn)
+{
+ if (conn == NULL || conn->seqno != msg->seqno) {
+ return false;
+ }
+
+ if (conn->acked <= conn->ackno) { // ackno does not wrap around uint32
+ return (msg->ackno >= conn->acked && msg->ackno <= conn->ackno);
+ } else { // this is more tricky
+ return (msg->ackno >= conn->acked || msg->ackno <= conn->ackno);
+ }
+}
+
+static void conn_update(knot_tcp_conn_t *conn, const knot_xdp_msg_t *msg)
+{
+ conn->seqno = knot_tcp_next_seqno(msg);
+ memcpy(conn->last_eth_rem, msg->eth_from, sizeof(conn->last_eth_rem));
+ memcpy(conn->last_eth_loc, msg->eth_to, sizeof(conn->last_eth_loc));
+ conn->window_size = (uint32_t)msg->win * (1LU << conn->window_scale);
+
+ uint32_t now = get_timestamp();
+ if (conn->establish_rtt == 0 && conn->last_active != 0) {
+ conn->establish_rtt = now - conn->last_active;
+ }
+ conn->last_active = now;
+}
+
+_public_
+int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t msgs[], uint32_t msg_count,
+ knot_tcp_table_t *tcp_table, knot_tcp_table_t *syn_table,
+ knot_tcp_ignore_t ignore)
+{
+ if (msg_count == 0) {
+ return KNOT_EOK;
+ }
+ if (relays == NULL || msgs == NULL || tcp_table == NULL) {
+ return KNOT_EINVAL;
+ }
+ memset(relays, 0, msg_count * sizeof(*relays));
+
+ knot_tcp_relay_t *relay = relays;
+ int ret = KNOT_EOK;
+
+ for (knot_xdp_msg_t *msg = msgs; msg != msgs + msg_count && ret == KNOT_EOK; msg++) {
+ if (!(msg->flags & KNOT_XDP_MSG_TCP)) {
+ continue;
+ }
+
+ uint64_t conn_hash = 0;
+ knot_tcp_conn_t **pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to,
+ &conn_hash, tcp_table);
+ knot_tcp_conn_t *conn = *pconn;
+ bool seq_ack_match = check_seq_ack(msg, conn);
+ if (seq_ack_match) {
+ assert(conn->mss != 0);
+ conn_update(conn, msg);
+
+ rem_align_pointers(conn, tcp_table);
+ rem_node(tcp_conn_node(conn));
+ add_tail(tcp_table_timeout(tcp_table), tcp_conn_node(conn));
+
+ if (msg->flags & KNOT_XDP_MSG_ACK) {
+ conn->acked = msg->ackno;
+ knot_tcp_outbufs_ack(&conn->outbufs, msg->ackno, &tcp_table->outbufs_total);
+ }
+ }
+
+ relay->msg = msg;
+ relay->conn = conn;
+
+ // process incoming data
+ if (seq_ack_match && (msg->flags & KNOT_XDP_MSG_ACK) && msg->payload.iov_len > 0) {
+ if (!(ignore & XDP_TCP_IGNORE_DATA_ACK)) {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ }
+ ret = knot_tcp_inbuf_update(&conn->inbuf, msg->payload, &relay->inbufs,
+ &relay->inbufs_count, &tcp_table->inbufs_total);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ if (conn->inbuf.iov_len > 0 && tcp_table->next_ibuf == NULL) {
+ tcp_table->next_ibuf = conn;
+ }
+ }
+
+ // process TCP connection state
+ switch (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK |
+ KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_RST)) {
+ case KNOT_XDP_MSG_SYN:
+ case (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK):
+ if (conn == NULL) {
+ bool synack = (msg->flags & KNOT_XDP_MSG_ACK);
+
+ knot_tcp_table_t *add_table = tcp_table;
+ if (syn_table != NULL && !synack) {
+ add_table = syn_table;
+ if (*tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table) != NULL) {
+ break;
+ }
+ }
+
+ ret = tcp_table_add(msg, conn_hash, add_table, &relay->conn);
+ if (ret == KNOT_EOK) {
+ relay->action = synack ? XDP_TCP_ESTABLISH : XDP_TCP_SYN;
+ if (!(ignore & XDP_TCP_IGNORE_ESTABLISH)) {
+ relay->auto_answer = synack ? KNOT_XDP_MSG_ACK : (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK);
+ }
+
+ conn = relay->conn;
+ conn->state = synack ? XDP_TCP_NORMAL: XDP_TCP_ESTABLISHING;
+ conn->mss = MAX(msg->mss, 536); // minimal MSS, most importantly not zero!
+ conn->window_scale = msg->win_scale;
+ conn_update(conn, msg);
+ if (!synack) {
+ conn->acked = dnssec_random_uint32_t();
+ conn->ackno = conn->acked;
+ }
+ }
+ } else {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ }
+ break;
+ case KNOT_XDP_MSG_ACK:
+ if (!seq_ack_match) {
+ if (syn_table != NULL && msg->payload.iov_len == 0 &&
+ (pconn = tcp_table_lookup(&msg->ip_from, &msg->ip_to, &conn_hash, syn_table)) != NULL &&
+ (conn = *pconn) != NULL && check_seq_ack(msg, conn)) {
+ // move conn from syn_table to tcp_table
+ tcp_table_remove(pconn, syn_table);
+ tcp_table_insert(conn, conn_hash, tcp_table);
+ relay->conn = conn;
+ relay->action = XDP_TCP_ESTABLISH;
+ conn->state = XDP_TCP_NORMAL;
+ conn_update(conn, msg);
+ }
+ } else {
+ switch (conn->state) {
+ case XDP_TCP_NORMAL:
+ case XDP_TCP_CLOSING1: // just a mess, ignore
+ break;
+ case XDP_TCP_ESTABLISHING:
+ conn->state = XDP_TCP_NORMAL;
+ relay->action = XDP_TCP_ESTABLISH;
+ break;
+ case XDP_TCP_CLOSING2:
+ if (msg->payload.iov_len == 0) { // otherwise ignore close
+ tcp_table_remove(pconn, tcp_table);
+ relay->answer = XDP_TCP_FREE;
+ }
+ break;
+ }
+ }
+ break;
+ case (KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK):
+ if (ignore & XDP_TCP_IGNORE_FIN) {
+ break;
+ }
+ if (!seq_ack_match) {
+ if (conn != NULL) {
+ relay->auto_answer = KNOT_XDP_MSG_RST;
+ relay->auto_seqno = msg->ackno;
+ } // else ignore. It would be better and possible, but no big value for the price of CPU.
+ } else {
+ if (conn->state == XDP_TCP_CLOSING1) {
+ relay->action = XDP_TCP_CLOSE;
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ relay->answer = XDP_TCP_FREE;
+ tcp_table_remove(pconn, tcp_table);
+ } else if (msg->payload.iov_len == 0) { // otherwise ignore FIN
+ relay->action = XDP_TCP_CLOSE;
+ relay->auto_answer = KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK;
+ conn->state = XDP_TCP_CLOSING2;
+ }
+ }
+ break;
+ case KNOT_XDP_MSG_RST:
+ if (conn != NULL && msg->seqno == conn->seqno) {
+ relay->action = XDP_TCP_RESET;
+ tcp_table_remove(pconn, tcp_table);
+ relay->answer = XDP_TCP_FREE;
+ } else if (conn != NULL) {
+ relay->auto_answer = KNOT_XDP_MSG_ACK;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!knot_tcp_relay_empty(relay)) {
+ relay++;
+ }
+ }
+
+ return ret;
+}
+
+_public_
+int knot_tcp_reply_data(knot_tcp_relay_t *relay, knot_tcp_table_t *tcp_table,
+ bool ignore_lastbyte, uint8_t *data, uint32_t len)
+{
+ if (relay == NULL || tcp_table == NULL || relay->conn == NULL) {
+ return KNOT_EINVAL;
+ }
+ int ret = knot_tcp_outbufs_add(&relay->conn->outbufs, data, len, ignore_lastbyte,
+ relay->conn->mss, &tcp_table->outbufs_total);
+
+ if (tcp_table->next_obuf == NULL && knot_tcp_outbufs_usage(relay->conn->outbufs) > 0) {
+ tcp_table->next_obuf = relay->conn;
+ }
+ if (tcp_table->next_resend == NULL && knot_tcp_outbufs_usage(relay->conn->outbufs) > 0) {
+ tcp_table->next_resend = relay->conn;
+ }
+ return ret;
+}
+
+static knot_xdp_msg_t *first_msg(knot_xdp_msg_t *msgs, uint32_t n_msgs)
+{
+ memset(msgs, 0, n_msgs * sizeof(*msgs));
+ return msgs - 1; // will be incremented just before first use
+}
+
+static int send_msgs(knot_xdp_msg_t *msgs, uint32_t n_msgs, knot_xdp_socket_t *socket)
+{
+ assert(socket);
+ assert(msgs);
+
+ if (n_msgs > 0) {
+ uint32_t unused;
+ return knot_xdp_send(socket, msgs, n_msgs, &unused);
+ }
+
+ return KNOT_EOK;
+}
+
+static void msg_init_from_conn(knot_xdp_msg_t *msg, knot_tcp_conn_t *conn)
+{
+ memcpy( msg->eth_from, conn->last_eth_loc, sizeof(msg->eth_from));
+ memcpy( msg->eth_to, conn->last_eth_rem, sizeof(msg->eth_to));
+ memcpy(&msg->ip_from, &conn->ip_loc, sizeof(msg->ip_from));
+ memcpy(&msg->ip_to, &conn->ip_rem, sizeof(msg->ip_to));
+
+ msg->ackno = conn->seqno;
+ msg->seqno = conn->ackno;
+
+ msg->payload.iov_len = 0;
+
+ msg->win_scale = 14; // maximum possible
+ msg->win = 0xffff;
+}
+
+static int next_msg(knot_xdp_msg_t *msgs, uint32_t n_msgs, knot_xdp_msg_t **cur,
+ knot_xdp_socket_t *socket, knot_tcp_relay_t *rl)
+{
+ (*cur)++;
+ if (*cur - msgs >= n_msgs) {
+ int ret = send_msgs(msgs, n_msgs, socket);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ *cur = first_msg(msgs, n_msgs);
+ (*cur)++;
+ }
+
+ knot_xdp_msg_t *msg = *cur;
+
+ knot_xdp_msg_flag_t fl = KNOT_XDP_MSG_TCP;
+ if (rl->conn->ip_loc.sin6_family == AF_INET6) {
+ fl |= KNOT_XDP_MSG_IPV6;
+ }
+ if (rl->conn->state == XDP_TCP_ESTABLISHING) {
+ fl |= KNOT_XDP_MSG_MSS | KNOT_XDP_MSG_WSC;
+ }
+
+ int ret = knot_xdp_send_alloc(socket, fl, msg);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ msg_init_from_conn(msg, rl->conn);
+
+ return ret;
+}
+
+_public_
+int knot_tcp_send(knot_xdp_socket_t *socket, knot_tcp_relay_t relays[],
+ uint32_t relay_count, uint32_t max_at_once)
+{
+ if (relay_count == 0) {
+ return KNOT_EOK;
+ }
+ if (socket == NULL || relays == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_xdp_msg_t msgs[max_at_once], *first = first_msg(msgs, max_at_once), *msg = first;
+
+ for (uint32_t i = 0; i < relay_count; i++) {
+ knot_tcp_relay_t *rl = &relays[i];
+
+#define NEXT_MSG { \
+ int ret = next_msg(msgs, max_at_once, &msg, socket, rl); \
+ if (ret != KNOT_EOK) { return ret; } \
+}
+
+ if (rl->auto_answer != 0) {
+ NEXT_MSG
+ msg->flags |= rl->auto_answer;
+ if (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_FIN)) {
+ rl->conn->ackno++;
+ }
+ if (rl->auto_answer == KNOT_XDP_MSG_RST) {
+ msg->seqno = rl->auto_seqno;
+ }
+ }
+
+ switch (rl->answer & 0x0f) {
+ case XDP_TCP_ESTABLISH:
+ NEXT_MSG
+ msg->flags |= KNOT_XDP_MSG_SYN;
+ rl->conn->ackno++;
+ break;
+ case XDP_TCP_CLOSE:
+ NEXT_MSG
+ msg->flags |= (KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK);
+ rl->conn->ackno++;
+ rl->conn->state = XDP_TCP_CLOSING1;
+ break;
+ case XDP_TCP_RESET:
+ NEXT_MSG
+ msg->flags |= KNOT_XDP_MSG_RST;
+ break;
+ case XDP_TCP_NOOP:
+ default:
+ break;
+ }
+
+ size_t can_data = 0;
+ knot_tcp_outbuf_t *ob;
+ if (rl->conn != NULL) {
+ knot_tcp_outbufs_can_send(rl->conn->outbufs, rl->conn->window_size,
+ rl->answer == XDP_TCP_RESEND, &ob, &can_data);
+ }
+ while (can_data > 0) {
+ NEXT_MSG
+ msg->flags |= KNOT_XDP_MSG_ACK;
+ msg->payload.iov_len = ob->len;
+ memcpy(msg->payload.iov_base, ob->bytes, ob->len);
+
+ if (!ob->sent) {
+ assert(rl->conn->ackno == msg->seqno);
+ rl->conn->ackno += msg->payload.iov_len;
+ } else {
+ msg->seqno = ob->seqno;
+ }
+
+ ob->sent = true;
+ ob->seqno = msg->seqno;
+
+ can_data--;
+ ob = ob->next;
+ }
+#undef NEXT_MSG
+ }
+
+ return send_msgs(msgs, msg - first, socket);
+}
+
+static void sweep_reset(knot_tcp_table_t *tcp_table, knot_tcp_relay_t *rl,
+ ssize_t *free_conns, ssize_t *free_inbuf, ssize_t *free_outbuf,
+ knot_sweep_stats_t *stats, knot_sweep_counter_t counter)
+{
+ rl->answer = XDP_TCP_RESET | XDP_TCP_FREE;
+ tcp_table_remove(tcp_table_re_lookup(rl->conn, tcp_table), tcp_table); // also updates tcp_table->next_*
+
+ *free_conns -= 1;
+ *free_inbuf -= rl->conn->inbuf.iov_len;
+ *free_outbuf -= knot_tcp_outbufs_usage(rl->conn->outbufs);
+
+ knot_sweep_stats_incr(stats, counter);
+}
+
+_public_
+int knot_tcp_sweep(knot_tcp_table_t *tcp_table,
+ uint32_t close_timeout, uint32_t reset_timeout,
+ uint32_t resend_timeout, uint32_t limit_conn_count,
+ size_t limit_ibuf_size, size_t limit_obuf_size,
+ knot_tcp_relay_t *relays, uint32_t max_relays,
+ struct knot_sweep_stats *stats)
+{
+ if (tcp_table == NULL || relays == NULL || max_relays < 1) {
+ return KNOT_EINVAL;
+ }
+
+ uint32_t now = get_timestamp();
+ memset(relays, 0, max_relays * sizeof(*relays));
+ knot_tcp_relay_t *rl = relays, *rl_max = rl + max_relays;
+
+ ssize_t free_conns = (ssize_t)(tcp_table->usage - limit_conn_count);
+ ssize_t free_inbuf = (ssize_t)(tcp_table->inbufs_total - MIN(limit_ibuf_size, SSIZE_MAX));
+ ssize_t free_outbuf = (ssize_t)(tcp_table->outbufs_total - MIN(limit_obuf_size, SSIZE_MAX));
+
+ // reset connections to free ibufs
+ while (free_inbuf > 0 && rl != rl_max) {
+ if (tcp_table->next_ibuf->inbuf.iov_len == 0) { // this conn might have get rid of ibuf in the meantime
+ next_ptr_ibuf(&tcp_table->next_ibuf);
+ }
+ assert(tcp_table->next_ibuf != NULL);
+ rl->conn = tcp_table->next_ibuf;
+ sweep_reset(tcp_table, rl, &free_conns, &free_inbuf, &free_outbuf,
+ stats, KNOT_SWEEP_CTR_LIMIT_IBUF);
+ rl++;
+ }
+
+ // reset connections to free obufs
+ while (free_outbuf > 0 && rl != rl_max) {
+ if (knot_tcp_outbufs_usage(tcp_table->next_obuf->outbufs) == 0) {
+ next_ptr_obuf(&tcp_table->next_obuf);
+ }
+ assert(tcp_table->next_obuf != NULL);
+ rl->conn = tcp_table->next_obuf;
+ sweep_reset(tcp_table, rl, &free_conns, &free_inbuf, &free_outbuf,
+ stats, KNOT_SWEEP_CTR_LIMIT_OBUF);
+ rl++;
+ }
+
+ // reset connections to free their count, and old ones
+ knot_tcp_conn_t *conn, *next;
+ WALK_LIST_DELSAFE(conn, next, *tcp_table_timeout(tcp_table)) {
+ if ((free_conns <= 0 && now - conn->last_active < reset_timeout) || rl == rl_max) {
+ break;
+ }
+
+ rl->conn = conn;
+ sweep_reset(tcp_table, rl, &free_conns, &free_inbuf, &free_outbuf,
+ stats, KNOT_SWEEP_CTR_LIMIT_CONN);
+ rl++;
+ }
+
+ // close old connections
+ while (tcp_table->next_close != NULL &&
+ now - tcp_table->next_close->last_active >= close_timeout &&
+ rl != rl_max) {
+ if (tcp_table->next_close->state != XDP_TCP_CLOSING1) {
+ rl->conn = tcp_table->next_close;
+ rl->answer = XDP_TCP_CLOSE;
+ knot_sweep_stats_incr(stats, KNOT_SWEEP_CTR_TIMEOUT);
+ rl++;
+ }
+ next_node_ptr(&tcp_table->next_close);
+ }
+
+ // resend unACKed data
+ while (tcp_table->next_resend != NULL &&
+ now - tcp_table->next_resend->last_active >= resend_timeout &&
+ rl != rl_max) {
+ rl->conn = tcp_table->next_resend;
+ rl->answer = XDP_TCP_RESEND;
+ rl++;
+ next_ptr_obuf(&tcp_table->next_resend);
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_tcp_cleanup(knot_tcp_table_t *tcp_table, knot_tcp_relay_t relays[],
+ uint32_t relay_count)
+{
+ (void)tcp_table;
+ for (uint32_t i = 0; i < relay_count; i++) {
+ if (relays[i].answer & XDP_TCP_FREE) {
+ del_conn(relays[i].conn);
+ }
+ free(relays[i].inbufs);
+ }
+}
diff --git a/src/libknot/xdp/tcp.h b/src/libknot/xdp/tcp.h
new file mode 100644
index 0000000..da0f1a4
--- /dev/null
+++ b/src/libknot/xdp/tcp.h
@@ -0,0 +1,227 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TCP over XDP IO interface.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/xdp/msg.h"
+#include "libknot/xdp/xdp.h"
+
+struct knot_sweep_stats;
+
+typedef enum {
+ XDP_TCP_NOOP = 0,
+ XDP_TCP_SYN = 1,
+ XDP_TCP_ESTABLISH = 2,
+ XDP_TCP_CLOSE = 3,
+ XDP_TCP_RESET = 4,
+ XDP_TCP_RESEND = 5,
+
+ XDP_TCP_FREE = 0x10,
+} knot_tcp_action_t;
+
+typedef enum {
+ XDP_TCP_NORMAL,
+ XDP_TCP_ESTABLISHING,
+ XDP_TCP_CLOSING1, // FIN+ACK sent
+ XDP_TCP_CLOSING2, // FIN+ACK received and sent
+} knot_tcp_state_t;
+
+typedef enum {
+ XDP_TCP_FREE_NONE,
+ XDP_TCP_FREE_DATA,
+ XDP_TCP_FREE_PREFIX,
+} knot_tcp_relay_free_t;
+
+typedef enum {
+ XDP_TCP_IGNORE_NONE = 0,
+ XDP_TCP_IGNORE_ESTABLISH = (1 << 0),
+ XDP_TCP_IGNORE_DATA_ACK = (1 << 1),
+ XDP_TCP_IGNORE_FIN = (1 << 2),
+} knot_tcp_ignore_t;
+
+typedef struct knot_tcp_conn {
+ struct {
+ struct knot_tcp_conn *list_node_next;
+ struct knot_tcp_conn *list_node_prev;
+ } list_node_placeholder;
+ struct sockaddr_in6 ip_rem;
+ struct sockaddr_in6 ip_loc;
+ uint8_t last_eth_rem[ETH_ALEN];
+ uint8_t last_eth_loc[ETH_ALEN];
+ uint16_t mss;
+ uint8_t window_scale;
+ uint32_t seqno;
+ uint32_t ackno;
+ uint32_t acked;
+ uint32_t window_size;
+ uint32_t last_active;
+ uint32_t establish_rtt; // in microseconds
+ knot_tcp_state_t state;
+ struct iovec inbuf;
+ struct knot_tcp_outbuf *outbufs;
+ struct knot_tcp_conn *next;
+} knot_tcp_conn_t;
+
+typedef struct {
+ size_t size;
+ size_t usage;
+ size_t inbufs_total;
+ size_t outbufs_total;
+ uint64_t hash_secret[2];
+ knot_tcp_conn_t *next_close;
+ knot_tcp_conn_t *next_ibuf;
+ knot_tcp_conn_t *next_obuf;
+ knot_tcp_conn_t *next_resend;
+ knot_tcp_conn_t *conns[];
+} knot_tcp_table_t;
+
+typedef struct {
+ const knot_xdp_msg_t *msg;
+ knot_tcp_action_t action;
+ knot_xdp_msg_flag_t auto_answer;
+ uint32_t auto_seqno;
+ knot_tcp_action_t answer;
+ struct iovec *inbufs;
+ size_t inbufs_count;
+ knot_tcp_conn_t *conn;
+} knot_tcp_relay_t;
+
+/*!
+ * \brief Return next TCP sequence number.
+ */
+inline static uint32_t knot_tcp_next_seqno(const knot_xdp_msg_t *msg)
+{
+ uint32_t res = msg->seqno + msg->payload.iov_len;
+ if (msg->flags & (KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_FIN)) {
+ res++;
+ }
+ return res;
+}
+
+/*!
+ * \brief Check if the relay is empty.
+ */
+inline static bool knot_tcp_relay_empty(const knot_tcp_relay_t *relay)
+{
+ return relay->action == XDP_TCP_NOOP && relay->answer == XDP_TCP_NOOP &&
+ relay->auto_answer == 0 && relay->inbufs_count == 0;
+}
+
+/*!
+ * \brief Allocate TCP connection-handling hash table.
+ *
+ * \param size Number of records for the hash table.
+ * \param secret_share Optional: share the hashing secret with another table.
+ *
+ * \note Hashing conflicts are solved by single-linked-lists in each record.
+ *
+ * \return The table, or NULL.
+ */
+knot_tcp_table_t *knot_tcp_table_new(size_t size, knot_tcp_table_t *secret_share);
+
+/*!
+ * \brief Free TCP connection hash table including all connection records.
+ *
+ * \note The freed connections are not closed nor reset.
+ */
+void knot_tcp_table_free(knot_tcp_table_t *table);
+
+/*!
+ * \brief Process received packets, prepare automatic responses (e.g. ACK), pick incoming data.
+ *
+ * \param relays Out: relays to be filled with message/connection details.
+ * \param msgs Packets received by knot_xdp_recv().
+ * \param msg_count Number of received packets.
+ * \param tcp_table Table of TCP connections.
+ * \param syn_table Optional: extra table for handling partially established connections.
+ * \param ignore Ignore specific TCP packets indication.
+ *
+ * \return KNOT_E*
+ */
+int knot_tcp_recv(knot_tcp_relay_t *relays, knot_xdp_msg_t msgs[], uint32_t msg_count,
+ knot_tcp_table_t *tcp_table, knot_tcp_table_t *syn_table,
+ knot_tcp_ignore_t ignore);
+
+/*!
+ * \brief Prepare data (payload) to be sent as a response on specific relay.
+ *
+ * \param relay Relay with active connection.
+ * \param tcp_table TCP table.
+ * \param ignore_lastbyte Evil mode: drop last byte of the payload.
+ * \param data Data payload, possibly > MSS and > window.
+ * \param len Payload length, < 64k.
+ *
+ * \return KNOT_E*
+ */
+int knot_tcp_reply_data(knot_tcp_relay_t *relay, knot_tcp_table_t *tcp_table,
+ bool ignore_lastbyte, uint8_t *data, uint32_t len);
+
+/*!
+ * \brief Send TCP packets.
+ *
+ * \param socket XDP socket to send through.
+ * \param relays Connection changes and data.
+ * \param relay_count Number of connection changes and data.
+ * \param max_at_once Limit of packet batch sent by knot_xdp_send().
+ *
+ * \return KNOT_E*
+ */
+int knot_tcp_send(knot_xdp_socket_t *socket, knot_tcp_relay_t relays[],
+ uint32_t relay_count, uint32_t max_at_once);
+
+/*!
+ * \brief Cleanup old TCP connections, perform timeout checks.
+ *
+ * \param tcp_table TCP connection table to clean up.
+ * \param close_timeout Gracefully close connections older than this (usecs).
+ * \param reset_timeout Reset connections older than this (usecs).
+ * \param resend_timeout Resend unAcked data older than this (usecs).
+ * \param limit_conn_count Limit of active connections in TCP table, reset if more.
+ * \param limit_ibuf_size Limit of memory usage by input buffers, reset if exceeded.
+ * \param limit_obuf_size Limit of memory usage by output buffers, reset if exceeded.
+ * \param relays Out: relays to be filled with close/reset instructions for knot_tcp_send().
+ * \param max_relays Maximum relays to be used.
+ * \param stats Out: sweeped out connection statistics.
+ *
+ * \return KNOT_E*
+ */
+int knot_tcp_sweep(knot_tcp_table_t *tcp_table,
+ uint32_t close_timeout, uint32_t reset_timeout,
+ uint32_t resend_timeout, uint32_t limit_conn_count,
+ size_t limit_ibuf_size, size_t limit_obuf_size,
+ knot_tcp_relay_t *relays, uint32_t max_relays,
+ struct knot_sweep_stats *stats);
+
+/*!
+ * \brief Free resources of closed/reset connections.
+ *
+ * \param tcp_table TCP table with connections.
+ * \param relays Relays with closed/reset (or other, ignored) connections.
+ * \param relay_count Number of relays.
+ */
+void knot_tcp_cleanup(knot_tcp_table_t *tcp_table, knot_tcp_relay_t relays[],
+ uint32_t relay_count);
+
+/*! @} */
diff --git a/src/libknot/xdp/tcp_iobuf.c b/src/libknot/xdp/tcp_iobuf.c
new file mode 100644
index 0000000..88ebcc3
--- /dev/null
+++ b/src/libknot/xdp/tcp_iobuf.c
@@ -0,0 +1,266 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/xdp/tcp_iobuf.h"
+
+#include "contrib/macros.h"
+#include "libknot/attribute.h"
+#include "libknot/endian.h"
+#include "libknot/error.h"
+
+static void iov_clear(struct iovec *iov)
+{
+ free(iov->iov_base);
+ memset(iov, 0, sizeof(*iov));
+}
+
+static void iov_inc(struct iovec *iov, size_t shift)
+{
+ assert(shift <= iov->iov_len);
+ iov->iov_base += shift;
+ iov->iov_len -= shift;
+}
+
+/*! \brief Strip 2-byte length prefix from a payload. */
+static void iov_inc2(struct iovec *iov)
+{
+ iov_inc(iov, sizeof(uint16_t));
+}
+
+static size_t tcp_payload_len(const struct iovec *payload)
+{
+ assert(payload->iov_len >= 2);
+ uint16_t val;
+ memcpy(&val, payload->iov_base, sizeof(val));
+ return be16toh(val) + sizeof(val);
+}
+
+static bool iov_inc_pf(struct iovec *iov)
+{
+ size_t shift = tcp_payload_len(iov);
+ if (iov->iov_len >= shift) {
+ iov_inc(iov, shift);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static size_t iov_count(const struct iovec *iov)
+{
+ size_t res = 0;
+ struct iovec tmp = *iov;
+ while (tmp.iov_len >= sizeof(uint16_t) && iov_inc_pf(&tmp)) {
+ res++;
+ }
+ return res;
+}
+
+static void iov_append(struct iovec *what, const struct iovec *with)
+{
+ // NOTE: what->iov_base must be pre-allocated large enough
+ memcpy(what->iov_base + what->iov_len, with->iov_base, with->iov_len);
+ what->iov_len += with->iov_len;
+}
+
+_public_
+int knot_tcp_inbuf_update(struct iovec *buffer, struct iovec data,
+ struct iovec **inbufs, size_t *inbufs_count,
+ size_t *buffers_total)
+{
+ size_t res_count = 0;
+ struct iovec *res = NULL, *cur = NULL;
+
+ *inbufs = NULL;
+ *inbufs_count = 0;
+
+ if (data.iov_len < 1) {
+ return KNOT_EOK;
+ }
+ if (buffer->iov_len == 1) {
+ ((uint8_t *)buffer->iov_base)[1] = ((uint8_t *)data.iov_base)[0];
+ buffer->iov_len++;
+ iov_inc(&data, 1);
+ if (data.iov_len < 1) {
+ return KNOT_EOK;
+ }
+ }
+ if (buffer->iov_len > 0) {
+ size_t buffer_req = tcp_payload_len(buffer);
+ assert(buffer_req > buffer->iov_len);
+ struct iovec data_use = { data.iov_base, buffer_req - buffer->iov_len };
+ if (data_use.iov_len <= data.iov_len) { // usable payload combined from buffer and data ---> res[0] allocated tohether with res
+ iov_inc(&data, data_use.iov_len);
+
+ res_count = 1 + iov_count(&data);
+ res = malloc(res_count * sizeof(*res) + buffer_req);
+ if (res == NULL) {
+ return KNOT_ENOMEM;
+ }
+ res[0].iov_base = (void *)(res + res_count);
+ res[0].iov_len = 0;
+ iov_append(&res[0], buffer);
+ iov_append(&res[0], &data_use);
+ assert(res[0].iov_len == buffer_req);
+ iov_inc2(&res[0]);
+
+ cur = &res[1];
+ *buffers_total -= buffer->iov_len;
+ iov_clear(buffer);
+ } else { // just extend the buffer with data
+ void *bufnew = realloc(buffer->iov_base, buffer->iov_len + data.iov_len);
+ if (bufnew == NULL) {
+ return KNOT_ENOMEM;
+ }
+ buffer->iov_base = bufnew;
+ iov_append(buffer, &data);
+ *buffers_total += data.iov_len;
+ return KNOT_EOK;
+ }
+ } else { // just allocate res
+ res_count = iov_count(&data);
+ if (res_count > 0) {
+ res = malloc(res_count * sizeof(*res));
+ if (res == NULL) {
+ return KNOT_ENOMEM;
+ }
+ cur = &res[0];
+ }
+ }
+
+ void *last;
+ while (data.iov_len > 1) {
+ last = data.iov_base;
+ if (!iov_inc_pf(&data)) {
+ break;
+ }
+ assert(cur);
+ cur->iov_base = last;
+ cur->iov_len = data.iov_base - last;
+ iov_inc2(cur);
+ cur++;
+ }
+ assert(cur == ((res_count) ? res + res_count : res));
+
+ // store the final incomplete payload to buffer
+ if (data.iov_len > 0) {
+ assert(buffer->iov_base == NULL);
+ buffer->iov_base = malloc(MAX(data.iov_len, 2));
+ if (buffer->iov_base == NULL) {
+ free(res);
+ return KNOT_ENOMEM;
+ }
+ *buffers_total += MAX(data.iov_len, 2);
+ buffer->iov_len = 0;
+ iov_append(buffer, &data);
+ }
+
+ *inbufs = res;
+ *inbufs_count = res_count;
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_tcp_outbufs_add(knot_tcp_outbuf_t **bufs, uint8_t *data, size_t len,
+ bool ignore_lastbyte, uint32_t mss, size_t *outbufs_total)
+{
+ if (len > UINT16_MAX) {
+ return KNOT_ELIMIT;
+ }
+ knot_tcp_outbuf_t **end = bufs;
+ while (*end != NULL) { // NOTE: this can be optimized by adding "end" pointer for the price of larger knot_tcp_conn_t struct
+ end = &(*end)->next;
+ }
+ uint16_t prefix = htobe16(len), prefix_len = sizeof(prefix);
+ while (len > 0) {
+ uint16_t newlen = MIN(len + prefix_len, mss);
+ knot_tcp_outbuf_t *newob = calloc(1, sizeof(*newob) + newlen);
+ if (newob == NULL) {
+ return KNOT_ENOMEM;
+ }
+ *outbufs_total += sizeof(*newob) + newlen;
+ newob->len = newlen;
+ if (ignore_lastbyte) {
+ newob->len--;
+ }
+ memcpy(newob->bytes, &prefix, prefix_len);
+ memcpy(newob->bytes + prefix_len, data, newlen - prefix_len);
+
+ *end = newob;
+ end = &newob->next;
+
+ data += newlen - prefix_len;
+ len -= newlen - prefix_len;
+
+ prefix_len = 0;
+ }
+ return KNOT_EOK;
+}
+
+static bool seqno_lower(uint32_t seqno, uint32_t ackno, uint32_t ackno_min)
+{
+ if (ackno_min <= ackno) {
+ return (seqno >= ackno_min && seqno <= ackno);
+ } else {
+ return (seqno >= ackno_min || seqno <= ackno);
+ }
+}
+
+_public_
+void knot_tcp_outbufs_ack(knot_tcp_outbuf_t **bufs, uint32_t ackno, size_t *outbufs_total)
+{
+ uint32_t ackno_min = ackno - (UINT32_MAX / 2); // FIXME better?
+ while (*bufs != NULL && (*bufs)->sent && seqno_lower((*bufs)->seqno + (*bufs)->len, ackno, ackno_min)) {
+ knot_tcp_outbuf_t *tofree = *bufs;
+ *bufs = tofree->next;
+ *outbufs_total -= tofree->len + sizeof(*tofree);
+ free(tofree);
+ }
+}
+
+_public_
+void knot_tcp_outbufs_can_send(knot_tcp_outbuf_t *bufs, ssize_t window_size, bool resend,
+ knot_tcp_outbuf_t **send_start, size_t *send_count)
+{
+ *send_count = 0;
+ *send_start = bufs;
+ while (*send_start != NULL && (*send_start)->sent && !resend) {
+ window_size -= (*send_start)->len;
+ *send_start = (*send_start)->next;
+ }
+
+ knot_tcp_outbuf_t *can_send = *send_start;
+ while (can_send != NULL && window_size >= can_send->len) {
+ (*send_count)++;
+ window_size -= can_send->len;
+ can_send = can_send->next;
+ }
+}
+
+_public_
+size_t knot_tcp_outbufs_usage(knot_tcp_outbuf_t *bufs)
+{
+ size_t res = 0;
+ for (knot_tcp_outbuf_t *i = bufs; i != NULL; i = i->next) {
+ res += i->len + sizeof(*i);
+ }
+ return res;
+}
diff --git a/src/libknot/xdp/tcp_iobuf.h b/src/libknot/xdp/tcp_iobuf.h
new file mode 100644
index 0000000..a27eeab
--- /dev/null
+++ b/src/libknot/xdp/tcp_iobuf.h
@@ -0,0 +1,120 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief TCP buffer helpers.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/uio.h>
+
+typedef struct knot_tcp_outbuf {
+ struct knot_tcp_outbuf *next;
+ uint32_t len;
+ uint32_t seqno;
+ bool sent;
+ uint8_t bytes[];
+} knot_tcp_outbuf_t;
+
+typedef enum {
+ KNOT_SWEEP_CTR_TIMEOUT = 0,
+ KNOT_SWEEP_CTR_LIMIT_CONN = 1,
+ KNOT_SWEEP_CTR_LIMIT_IBUF = 2,
+ KNOT_SWEEP_CTR_LIMIT_OBUF = 3,
+} knot_sweep_counter_t;
+
+typedef struct knot_sweep_stats {
+ uint32_t total;
+ uint32_t counters[4];
+} knot_sweep_stats_t;
+
+inline static void knot_sweep_stats_incr(knot_sweep_stats_t *stats, knot_sweep_counter_t counter)
+{
+ (stats->counters[counter])++;
+ (stats->total)++;
+}
+
+inline static void knot_sweep_stats_reset(knot_sweep_stats_t *stats)
+{
+ memset(stats, 0, sizeof(*stats));
+}
+
+/*!
+ * \brief Handle DNS-over-TCP payloads in buffer and message.
+ *
+ * \param buffer In/out: persistent buffer to store incomplete DNS payloads between receiving packets.
+ * \param data In: momental DNS payloads in incoming packet.
+ * \param inbufs Out: list of incoming DNS messages.
+ * \param inbufs_count Out: number of inbufs.
+ * \param buffers_total In/Out: total size of buffers (will be increased or decreased).
+ *
+ * \return KNOT_EOK, KNOT_ENOMEM
+ */
+int knot_tcp_inbuf_update(struct iovec *buffer, struct iovec data,
+ struct iovec **inbufs, size_t *inbufs_count,
+ size_t *buffers_total);
+
+/*!
+ * \brief Add payload to be sent by TCP, to output buffers.
+ *
+ * \param bufs Output buffers to be updated.
+ * \param data Payload to be sent.
+ * \param len Payload length.
+ * \param ignore_lastbyte Evil mode: drop last byte of the payload.
+ * \param mss Connection outgoing MSS.
+ * \param outbufs_total In/out: total outbuf statistic to be updated.
+ *
+ * \return KNOT_E*
+ */
+int knot_tcp_outbufs_add(knot_tcp_outbuf_t **bufs, uint8_t *data, size_t len,
+ bool ignore_lastbyte, uint32_t mss, size_t *outbufs_total);
+
+/*!
+ * \brief Remove+free acked data from output buffers.
+ *
+ * \param bufs Output buffers to be updated.
+ * \param ackno Ackno of received ACK.
+ * \param outbufs_total In/out: total outbuf statistic to be updated.
+ */
+void knot_tcp_outbufs_ack(knot_tcp_outbuf_t **bufs, uint32_t ackno, size_t *outbufs_total);
+
+/*!
+ * \brief Prepare output buffers to be sent now.
+ *
+ * \param bufs Output buffers to be updated.
+ * \param window_size Connection outgoing window size.
+ * \param resend Send also possibly already sent data.
+ * \param send_start Out: first output buffer to be sent.
+ * \param send_count Out: number of output buffers to be sent.
+ */
+void knot_tcp_outbufs_can_send(knot_tcp_outbuf_t *bufs, ssize_t window_size, bool resend,
+ knot_tcp_outbuf_t **send_start, size_t *send_count);
+
+/*!
+ * \brief Compute allocated size of output buffers.
+ */
+size_t knot_tcp_outbufs_usage(knot_tcp_outbuf_t *bufs);
+
+/*! @} */
diff --git a/src/libknot/xdp/xdp.c b/src/libknot/xdp/xdp.c
new file mode 100644
index 0000000..975276d
--- /dev/null
+++ b/src/libknot/xdp/xdp.c
@@ -0,0 +1,568 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <linux/if_ether.h>
+#include <linux/udp.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "libknot/attribute.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "libknot/xdp/bpf-consts.h"
+#include "libknot/xdp/bpf-user.h"
+#include "libknot/xdp/eth.h"
+#include "libknot/xdp/msg_init.h"
+#include "libknot/xdp/protocols.h"
+#include "libknot/xdp/xdp.h"
+#include "contrib/macros.h"
+#include "contrib/net.h"
+
+#define FRAME_SIZE 2048
+
+#define FRAME_COUNT_TX 2048
+#define FRAME_COUNT_RX 2048
+#define FRAME_COUNT (FRAME_COUNT_TX + FRAME_COUNT_RX)
+
+#define RING_LEN_TX FRAME_COUNT_TX
+#define RING_LEN_CQ FRAME_COUNT_TX
+#define RING_LEN_RX FRAME_COUNT_RX
+/* It's recommended that the FQ ring size >= HW RX ring size + AF_XDP RX ring size. */
+#define RING_LEN_FQ 8192
+
+#define ALLOC_RETRY_NUM 15
+#define ALLOC_RETRY_DELAY 20 // In nanoseconds.
+
+/* With recent compilers we statically check #defines for settings that
+ * get refused by AF_XDP drivers (in current versions, at least). */
+#if (__STDC_VERSION__ >= 201112L)
+#define IS_POWER_OF_2(n) (((n) & (n - 1)) == 0)
+_Static_assert((FRAME_SIZE == 4096 || FRAME_SIZE == 2048)
+ && IS_POWER_OF_2(RING_LEN_TX) && IS_POWER_OF_2(RING_LEN_RX)
+ && IS_POWER_OF_2(RING_LEN_CQ) && IS_POWER_OF_2(RING_LEN_FQ)
+ && FRAME_COUNT_TX <= (1 << 16) /* see tx_free_indices */
+ , "Incorrect #define combination for AF_XDP.");
+#endif
+
+struct umem_frame {
+ uint8_t bytes[FRAME_SIZE];
+};
+
+static int configure_xsk_umem(struct kxsk_umem **out_umem)
+{
+ /* Allocate memory and call driver to create the UMEM. */
+ struct kxsk_umem *umem = calloc(1,
+ offsetof(struct kxsk_umem, tx_free_indices)
+ + sizeof(umem->tx_free_indices[0]) * FRAME_COUNT_TX);
+ if (umem == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = posix_memalign((void **)&umem->frames, getpagesize(),
+ FRAME_SIZE * FRAME_COUNT);
+ if (ret != 0) {
+ free(umem);
+ return KNOT_ENOMEM;
+ }
+
+ const struct xsk_umem_config config = {
+ .fill_size = RING_LEN_FQ,
+ .comp_size = RING_LEN_CQ,
+ .frame_size = FRAME_SIZE,
+ .frame_headroom = KNOT_XDP_PKT_ALIGNMENT,
+ };
+
+ ret = xsk_umem__create(&umem->umem, umem->frames, FRAME_SIZE * FRAME_COUNT,
+ &umem->fq, &umem->cq, &config);
+ if (ret != KNOT_EOK) {
+ free(umem->frames);
+ free(umem);
+ return ret;
+ }
+ *out_umem = umem;
+
+ /* Designate the starting chunk of buffers for TX, and put them onto the stack. */
+ umem->tx_free_count = FRAME_COUNT_TX;
+ for (uint32_t i = 0; i < FRAME_COUNT_TX; ++i) {
+ umem->tx_free_indices[i] = i;
+ }
+
+ /* Designate the rest of buffers for RX, and pass them to the driver. */
+ uint32_t idx = 0;
+ ret = xsk_ring_prod__reserve(&umem->fq, FRAME_COUNT_RX, &idx);
+ if (ret != FRAME_COUNT_RX) {
+ assert(0);
+ return KNOT_ERROR;
+ }
+ assert(idx == 0);
+ assert(FRAME_COUNT == FRAME_COUNT_TX + FRAME_COUNT_RX);
+ for (uint32_t i = FRAME_COUNT_TX; i < FRAME_COUNT; ++i) {
+ *xsk_ring_prod__fill_addr(&umem->fq, idx++) = i * FRAME_SIZE;
+ }
+ xsk_ring_prod__submit(&umem->fq, FRAME_COUNT_RX);
+
+ return KNOT_EOK;
+}
+
+static void deconfigure_xsk_umem(struct kxsk_umem *umem)
+{
+ (void)xsk_umem__delete(umem->umem);
+ free(umem->frames);
+ free(umem);
+}
+
+static int configure_xsk_socket(struct kxsk_umem *umem,
+ const struct kxsk_iface *iface,
+ knot_xdp_socket_t **out_sock)
+{
+ knot_xdp_socket_t *xsk_info = calloc(1, sizeof(*xsk_info));
+ if (xsk_info == NULL) {
+ return KNOT_ENOMEM;
+ }
+ xsk_info->iface = iface;
+ xsk_info->umem = umem;
+
+ const struct xsk_socket_config sock_conf = {
+ .tx_size = RING_LEN_TX,
+ .rx_size = RING_LEN_RX,
+ .libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD,
+ };
+
+ int ret = xsk_socket__create(&xsk_info->xsk, iface->if_name,
+ iface->if_queue, umem->umem,
+ &xsk_info->rx, &xsk_info->tx, &sock_conf);
+ if (ret != 0) {
+ free(xsk_info);
+ return ret;
+ }
+
+ *out_sock = xsk_info;
+ return KNOT_EOK;
+}
+
+_public_
+int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue,
+ knot_xdp_filter_flag_t flags, uint16_t udp_port, uint16_t quic_port,
+ knot_xdp_load_bpf_t load_bpf, const void *xdp_config)
+{
+ if (socket == NULL || if_name == NULL ||
+ (udp_port == quic_port && (flags & KNOT_XDP_FILTER_UDP) && (flags & KNOT_XDP_FILTER_QUIC)) ||
+ (flags & (KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_TCP | KNOT_XDP_FILTER_QUIC)) == 0) {
+ return KNOT_EINVAL;
+ }
+
+ struct kxsk_iface *iface;
+ int ret = kxsk_iface_new(if_name, if_queue, load_bpf, &iface);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Initialize shared packet_buffer for umem usage. */
+ struct kxsk_umem *umem = NULL;
+ ret = configure_xsk_umem(&umem);
+ if (ret != KNOT_EOK) {
+ kxsk_iface_free(iface);
+ return ret;
+ }
+
+ ret = configure_xsk_socket(umem, iface, socket);
+ if (ret != KNOT_EOK) {
+ deconfigure_xsk_umem(umem);
+ kxsk_iface_free(iface);
+ return ret;
+ }
+
+ (*socket)->frame_limit = FRAME_SIZE;
+ ret = knot_eth_mtu(if_name);
+ if (ret > 0) {
+ (*socket)->frame_limit = MIN((unsigned)ret, (*socket)->frame_limit);
+ }
+
+ if (flags & KNOT_XDP_FILTER_ROUTE) {
+ ret = knot_eth_vlans(&(*socket)->vlan_map, &(*socket)->vlan_map_max);
+ if (ret != KNOT_EOK) {
+ xsk_socket__delete((*socket)->xsk);
+ deconfigure_xsk_umem(umem);
+ kxsk_iface_free(iface);
+ free(*socket);
+ *socket = NULL;
+ return ret;
+ }
+ }
+
+ ret = kxsk_socket_start(iface, flags, udp_port, quic_port, (*socket)->xsk);
+ if (ret != KNOT_EOK) {
+ free((*socket)->vlan_map);
+ xsk_socket__delete((*socket)->xsk);
+ deconfigure_xsk_umem(umem);
+ kxsk_iface_free(iface);
+ free(*socket);
+ *socket = NULL;
+ return ret;
+ }
+
+ return ret;
+}
+
+_public_
+void knot_xdp_deinit(knot_xdp_socket_t *socket)
+{
+ if (socket == NULL) {
+ return;
+ }
+ if (unlikely(socket->send_mock != NULL)) {
+ free(socket);
+ return;
+ }
+
+ kxsk_socket_stop(socket->iface);
+ xsk_socket__delete(socket->xsk);
+ deconfigure_xsk_umem(socket->umem);
+
+ kxsk_iface_free((struct kxsk_iface *)/*const-cast*/socket->iface);
+ free(socket->vlan_map);
+ free(socket);
+}
+
+_public_
+int knot_xdp_socket_fd(knot_xdp_socket_t *socket)
+{
+ if (socket == NULL) {
+ return 0;
+ }
+
+ return xsk_socket__fd(socket->xsk);
+}
+
+static void tx_free_relative(struct kxsk_umem *umem, uint64_t addr_relative)
+{
+ /* The address may not point to *start* of buffer, but `/` solves that. */
+ uint64_t index = addr_relative / FRAME_SIZE;
+ assert(index < FRAME_COUNT);
+ umem->tx_free_indices[umem->tx_free_count++] = index;
+}
+
+_public_
+void knot_xdp_send_prepare(knot_xdp_socket_t *socket)
+{
+ if (socket == NULL || unlikely(socket->send_mock != NULL)) {
+ return;
+ }
+
+ struct kxsk_umem *const umem = socket->umem;
+ struct xsk_ring_cons *const cq = &umem->cq;
+
+ uint32_t idx = 0;
+ const uint32_t completed = xsk_ring_cons__peek(cq, UINT32_MAX, &idx);
+ if (completed == 0) {
+ return;
+ }
+ assert(umem->tx_free_count + completed <= FRAME_COUNT_TX);
+
+ for (uint32_t i = 0; i < completed; ++i) {
+ uint64_t addr_relative = *xsk_ring_cons__comp_addr(cq, idx++);
+ tx_free_relative(umem, addr_relative);
+ }
+
+ xsk_ring_cons__release(cq, completed);
+}
+
+static struct umem_frame *alloc_tx_frame(knot_xdp_socket_t *socket)
+{
+ if (unlikely(socket->send_mock != NULL)) {
+ return malloc(sizeof(struct umem_frame));
+ }
+
+ const struct timespec delay = { .tv_nsec = ALLOC_RETRY_DELAY };
+ struct kxsk_umem *umem = socket->umem;
+
+ for (int i = 0; unlikely(umem->tx_free_count == 0); i++) {
+ if (i == ALLOC_RETRY_NUM) {
+ return NULL;
+ }
+ nanosleep(&delay, NULL);
+ knot_xdp_send_prepare(socket);
+ }
+
+ uint32_t index = umem->tx_free_indices[--umem->tx_free_count];
+ return umem->frames + index;
+}
+
+static void prepare_payload(knot_xdp_msg_t *msg, void *uframe)
+{
+ size_t hdr_len = prot_write_hdrs_len(msg);
+ msg->payload.iov_base = uframe + hdr_len + KNOT_XDP_PKT_ALIGNMENT;
+ msg->payload.iov_len = FRAME_SIZE - hdr_len - KNOT_XDP_PKT_ALIGNMENT;
+}
+
+_public_
+int knot_xdp_send_alloc(knot_xdp_socket_t *socket, knot_xdp_msg_flag_t flags,
+ knot_xdp_msg_t *out)
+{
+ if (socket == NULL || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct umem_frame *uframe = alloc_tx_frame(socket);
+ if (uframe == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ msg_init(out, flags);
+ prepare_payload(out, uframe);
+
+ return KNOT_EOK;
+}
+
+_public_
+int knot_xdp_reply_alloc(knot_xdp_socket_t *socket, const knot_xdp_msg_t *query,
+ knot_xdp_msg_t *out)
+{
+ if (socket == NULL || query == NULL || out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct umem_frame *uframe = alloc_tx_frame(socket);
+ if (uframe == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ msg_init_reply(out, query);
+ prepare_payload(out, uframe);
+
+ return KNOT_EOK;
+}
+
+static void free_unsent(knot_xdp_socket_t *socket, const knot_xdp_msg_t *msg)
+{
+ if (unlikely(socket->send_mock != NULL)) {
+ free(msg->payload.iov_base - prot_write_hdrs_len(msg) - KNOT_XDP_PKT_ALIGNMENT);
+ return;
+ }
+ uint64_t addr_relative = (uint8_t *)msg->payload.iov_base
+ - socket->umem->frames->bytes;
+ tx_free_relative(socket->umem, addr_relative);
+}
+
+_public_
+int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count, uint32_t *sent)
+{
+ if (socket == NULL || msgs == NULL || sent == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (unlikely(socket->send_mock != NULL)) {
+ int ret = socket->send_mock(socket, msgs, count, sent);
+ for (uint32_t i = 0; i < count; ++i) {
+ free_unsent(socket, &msgs[i]);
+ }
+ return ret;
+ }
+
+ /* Now we want to do something close to
+ * xsk_ring_prod__reserve(&socket->tx, count, *idx)
+ * but we don't know in advance if we utilize *whole* `count`,
+ * and the API doesn't allow "cancelling reservations".
+ * Therefore we handle `socket->tx.cached_prod` by hand.
+ */
+ if (xsk_prod_nb_free(&socket->tx, count) < count) {
+ /* This situation was sometimes observed in the emulated XDP mode. */
+ for (uint32_t i = 0; i < count; ++i) {
+ free_unsent(socket, &msgs[i]);
+ }
+ return KNOT_ENOBUFS;
+ }
+ uint32_t idx = socket->tx.cached_prod;
+
+ for (uint32_t i = 0; i < count; ++i) {
+ const knot_xdp_msg_t *msg = &msgs[i];
+
+ if (empty_msg(msg)) {
+ free_unsent(socket, msg);
+ } else {
+ size_t hdr_len = prot_write_hdrs_len(msg);
+ size_t tot_len = hdr_len + msg->payload.iov_len;
+ uint8_t *msg_beg = msg->payload.iov_base - hdr_len;
+ uint16_t mss = MIN(socket->frame_limit - hdr_len, KNOT_TCP_MSS);
+ prot_write_eth(msg_beg, msg, msg_beg + tot_len, mss);
+
+ *xsk_ring_prod__tx_desc(&socket->tx, idx++) = (struct xdp_desc) {
+ .addr = msg_beg - socket->umem->frames->bytes,
+ .len = tot_len,
+ };
+ }
+ }
+
+ *sent = idx - socket->tx.cached_prod;
+ assert(*sent <= count);
+ socket->tx.cached_prod = idx;
+ xsk_ring_prod__submit(&socket->tx, *sent);
+ socket->kernel_needs_wakeup = true;
+
+ return KNOT_EOK;
+}
+
+_public_
+void knot_xdp_send_free(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count)
+{
+ for (uint32_t i = 0; i < count; i++) {
+ free_unsent(socket, &msgs[i]);
+ }
+}
+
+_public_
+int knot_xdp_send_finish(knot_xdp_socket_t *socket)
+{
+ if (socket == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ /* Trigger sending queued packets. */
+ if (!socket->kernel_needs_wakeup) {
+ return KNOT_EOK;
+ }
+
+ int ret = sendto(xsk_socket__fd(socket->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0);
+ const bool is_ok = (ret >= 0);
+ // List of "safe" errors taken from
+ // https://github.com/torvalds/linux/blame/master/samples/bpf/xdpsock_user.c
+ const bool is_again = !is_ok && (errno == ENOBUFS || errno == EAGAIN
+ || errno == EBUSY || errno == ENETDOWN);
+ // Some of the !is_ok cases are a little unclear - what to do about the syscall,
+ // including how caller of _sendmsg_finish() should react.
+ if (is_ok || !is_again) {
+ socket->kernel_needs_wakeup = false;
+ }
+ if (is_again) {
+ return KNOT_EAGAIN;
+ } else if (is_ok) {
+ return KNOT_EOK;
+ } else {
+ return -errno;
+ }
+ /* This syscall might be avoided with a newer kernel feature (>= 5.4):
+ https://www.kernel.org/doc/html/latest/networking/af_xdp.html#xdp-use-need-wakeup-bind-flag
+ Unfortunately it's not easy to continue supporting older kernels
+ when using this feature on newer ones.
+ */
+}
+
+_public_
+int knot_xdp_recv(knot_xdp_socket_t *socket, knot_xdp_msg_t msgs[],
+ uint32_t max_count, uint32_t *count, size_t *wire_size)
+{
+ if (socket == NULL || msgs == NULL || count == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint32_t idx = 0;
+ const uint32_t available = xsk_ring_cons__peek(&socket->rx, max_count, &idx);
+ if (available == 0) {
+ *count = 0;
+ return KNOT_EOK;
+ }
+ assert(available <= max_count);
+
+ for (uint32_t i = 0; i < available; ++i) {
+ knot_xdp_msg_t *msg = &msgs[i];
+ const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&socket->rx, idx++);
+ uint8_t *uframe_p = (uint8_t *)socket->umem->frames + desc->addr;
+
+ void *payl_end;
+ void *payl_start = prot_read_eth(uframe_p, msg, &payl_end,
+ socket->vlan_map, socket->vlan_map_max);
+
+ msg->payload.iov_base = payl_start;
+ msg->payload.iov_len = payl_end - payl_start;
+ msg->mss = MIN(msg->mss, FRAME_SIZE - (payl_start - (void *)uframe_p));
+
+ if (wire_size != NULL) {
+ (*wire_size) += desc->len;
+ }
+ }
+
+ xsk_ring_cons__release(&socket->rx, available);
+ *count = available;
+
+ return KNOT_EOK;
+}
+
+static uint8_t *msg_uframe_ptr(const knot_xdp_msg_t *msg)
+{
+ return NULL + ((msg->payload.iov_base - NULL) & ~(FRAME_SIZE - 1));
+}
+
+_public_
+void knot_xdp_recv_finish(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count)
+{
+ if (socket == NULL || msgs == NULL) {
+ return;
+ }
+
+ struct kxsk_umem *const umem = socket->umem;
+ struct xsk_ring_prod *const fq = &umem->fq;
+
+ uint32_t idx = 0;
+ const uint32_t reserved = xsk_ring_prod__reserve(fq, count, &idx);
+ assert(reserved == count);
+
+ for (uint32_t i = 0; i < reserved; ++i) {
+ uint8_t *uframe_p = msg_uframe_ptr(&msgs[i]);
+ uint64_t offset = uframe_p - umem->frames->bytes;
+ *xsk_ring_prod__fill_addr(fq, idx++) = offset;
+ }
+
+ xsk_ring_prod__submit(fq, reserved);
+}
+
+_public_
+void knot_xdp_info(const knot_xdp_socket_t *socket, FILE *file)
+{
+ if (socket == NULL || file == NULL) {
+ return;
+ }
+
+ // The number of busy frames
+ #define RING_BUSY(ring) \
+ ((*(ring)->producer - *(ring)->consumer) & (ring)->mask)
+
+ #define RING_PRINFO(name, ring) \
+ fprintf(file, "Ring %s: size %4d, busy %4d (prod %4d, cons %4d)\n", \
+ name, (unsigned)(ring)->size, \
+ (unsigned)RING_BUSY((ring)), \
+ (unsigned)*(ring)->producer, (unsigned)*(ring)->consumer)
+
+ const int rx_busyf = RING_BUSY(&socket->umem->fq) + RING_BUSY(&socket->rx);
+ fprintf(file, "\nLOST RX frames: %4d", (int)(FRAME_COUNT_RX - rx_busyf));
+
+ const int tx_busyf = RING_BUSY(&socket->umem->cq) + RING_BUSY(&socket->tx);
+ const int tx_freef = socket->umem->tx_free_count;
+ fprintf(file, "\nLOST TX frames: %4d\n", (int)(FRAME_COUNT_TX - tx_busyf - tx_freef));
+
+ RING_PRINFO("FQ", &socket->umem->fq);
+ RING_PRINFO("RX", &socket->rx);
+ RING_PRINFO("TX", &socket->tx);
+ RING_PRINFO("CQ", &socket->umem->cq);
+ fprintf(file, "TX free frames: %4d\n", tx_freef);
+}
diff --git a/src/libknot/xdp/xdp.h b/src/libknot/xdp/xdp.h
new file mode 100644
index 0000000..a7606fe
--- /dev/null
+++ b/src/libknot/xdp/xdp.h
@@ -0,0 +1,189 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief XDP IO interface.
+ *
+ * \addtogroup xdp
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include "libknot/xdp/bpf-consts.h"
+#include "libknot/xdp/msg.h"
+
+/*!
+ * \brief Styles of loading BPF program.
+ *
+ * \note In *all* the cases loading can only succeed if at the end
+ * a compatible BPF program is loaded on the interface.
+ */
+typedef enum {
+ KNOT_XDP_LOAD_BPF_NEVER, /*!< Do not load; error out if not loaded already. */
+ KNOT_XDP_LOAD_BPF_ALWAYS, /*!< Always load a program (overwrite it). */
+ KNOT_XDP_LOAD_BPF_ALWAYS_UNLOAD, /*!< KNOT_XDP_LOAD_BPF_ALWAYS + unload previous. */
+ KNOT_XDP_LOAD_BPF_MAYBE, /*!< Try with present program or load if none. */
+ /* Implementation caveat: when re-using program in _MAYBE case, we get a message:
+ * libbpf: Kernel error message: XDP program already attached */
+} knot_xdp_load_bpf_t;
+
+/*! \brief Context structure for one XDP socket. */
+typedef struct knot_xdp_socket knot_xdp_socket_t;
+
+/*!
+ * \brief Initialize XDP socket.
+ *
+ * \param socket XDP socket.
+ * \param if_name Name of the net iface (e.g. eth0).
+ * \param if_queue Network card queue to be used (normally 1 socket per each queue).
+ * \param flags XDP filter configuration flags.
+ * \param udp_port UDP and/or TCP port to listen on if enabled via \a opts.
+ * \param quic_port QUIC/UDP port to listen on if enabled via \a opts.
+ * \param load_bpf Insert BPF program into packet processing.
+ * \param xdp_config Optional XDP socket configuration.
+ *
+ * \return KNOT_E* or -errno
+ */
+int knot_xdp_init(knot_xdp_socket_t **socket, const char *if_name, int if_queue,
+ knot_xdp_filter_flag_t flags, uint16_t udp_port, uint16_t quic_port,
+ knot_xdp_load_bpf_t load_bpf, const void *xdp_config);
+
+/*!
+ * \brief De-init XDP socket.
+ *
+ * \param socket XDP socket.
+ */
+void knot_xdp_deinit(knot_xdp_socket_t *socket);
+
+/*!
+ * \brief Return a file descriptor to be polled on for incoming packets.
+ *
+ * \param socket XDP socket.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_socket_fd(knot_xdp_socket_t *socket);
+
+/*!
+ * \brief Collect completed TX buffers, so they can be used by knot_xdp_send_alloc().
+ *
+ * \param socket XDP socket.
+ */
+void knot_xdp_send_prepare(knot_xdp_socket_t *socket);
+
+/*!
+ * \brief Allocate one buffer for an outgoing packet.
+ *
+ * \param socket XDP socket.
+ * \param flags Flags for new message.
+ * \param out Out: the allocated packet buffer.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_send_alloc(knot_xdp_socket_t *socket, knot_xdp_msg_flag_t flags,
+ knot_xdp_msg_t *out);
+
+/*!
+ * \brief Allocate one buffer for a reply packet.
+ *
+ * \param socket XDP socket.
+ * \param query The packet to be replied to.
+ * \param out Out: the allocated packet buffer.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_reply_alloc(knot_xdp_socket_t *socket, const knot_xdp_msg_t *query,
+ knot_xdp_msg_t *out);
+
+/*!
+ * \brief Send multiple packets thru XDP.
+ *
+ * \note The packets all must have been allocated by knot_xdp_send_alloc()!
+ * \note Do not free the packet payloads afterwards.
+ * \note Packets with zero length will be skipped.
+ *
+ * \param socket XDP socket.
+ * \param msgs Packets to be sent.
+ * \param count Number of packets.
+ * \param sent Out: number of packet successfully sent.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_send(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count, uint32_t *sent);
+
+/*!
+ * \brief Cleanup messages that have not been knot_xdp_send().
+ *
+ * ...possibly due to some error.
+ *
+ * \param socket XDP socket.
+ * \param msgs Messages to be freed.
+ * \param count Number of messages.
+ */
+void knot_xdp_send_free(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count);
+
+/*!
+ * \brief Syscall to kernel to wake up the network card driver after knot_xdp_send().
+ *
+ * \param socket XDP socket.
+ *
+ * \return KNOT_E* or -errno
+ */
+int knot_xdp_send_finish(knot_xdp_socket_t *socket);
+
+/*!
+ * \brief Receive multiple packets thru XDP.
+ *
+ * \param socket XDP socket.
+ * \param msgs Out: buffers to be filled in with incoming packets.
+ * \param max_count Limit for number of packets received at once.
+ * \param count Out: real number of received packets.
+ * \param wire_size Out: (optional) total wire size of received packets.
+ *
+ * \return KNOT_E*
+ */
+int knot_xdp_recv(knot_xdp_socket_t *socket, knot_xdp_msg_t msgs[],
+ uint32_t max_count, uint32_t *count, size_t *wire_size);
+
+/*!
+ * \brief Free buffers with received packets.
+ *
+ * \param socket XDP socket.
+ * \param msgs Buffers with received packets.
+ * \param count Number of received packets to free.
+ */
+void knot_xdp_recv_finish(knot_xdp_socket_t *socket, const knot_xdp_msg_t msgs[],
+ uint32_t count);
+
+/*!
+ * \brief Print some info about the XDP socket.
+ *
+ * \param socket XDP socket.
+ * \param file Output file.
+ */
+void knot_xdp_info(const knot_xdp_socket_t *socket, FILE *file);
+
+/*! @} */
diff --git a/src/libknot/yparser/yparser.c b/src/libknot/yparser/yparser.c
new file mode 100644
index 0000000..3004761
--- /dev/null
+++ b/src/libknot/yparser/yparser.c
@@ -0,0 +1,176 @@
+/* Copyright (C) 2021 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 <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+extern int _yp_start_state;
+extern int _yp_parse(yp_parser_t *parser);
+
+_public_
+void yp_init(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return;
+ }
+
+ memset(parser, 0, sizeof(*parser));
+
+ parser->cs = _yp_start_state;
+ parser->file.descriptor = -1;
+ parser->line_count = 1;
+}
+
+_public_
+void yp_deinit(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return;
+ }
+
+ if (parser->file.descriptor != -1) {
+ munmap((void *)parser->input.start,
+ parser->input.end - parser->input.start);
+ close(parser->file.descriptor);
+ free(parser->file.name);
+ }
+}
+
+_public_
+int yp_set_input_string(
+ yp_parser_t *parser,
+ const char *input,
+ size_t size)
+{
+ if (parser == NULL || input == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Reinitialize the parser.
+ yp_deinit(parser);
+ yp_init(parser);
+
+ // Set the parser input limits.
+ parser->input.start = input;
+ parser->input.current = input;
+ parser->input.end = input + size;
+ parser->input.eof = false;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_set_input_file(
+ yp_parser_t *parser,
+ const char *file_name)
+{
+ if (parser == NULL || file_name == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Reinitialize the parser.
+ yp_deinit(parser);
+ yp_init(parser);
+
+ // Try to open the file.
+ parser->file.descriptor = open(file_name, O_RDONLY);
+ if (parser->file.descriptor == -1) {
+ return knot_map_errno();
+ }
+
+ // Check for regular file input.
+ struct stat file_stat;
+ if (fstat(parser->file.descriptor, &file_stat) == -1) {
+ close(parser->file.descriptor);
+ return knot_map_errno();
+ } else if (!S_ISREG(file_stat.st_mode)) {
+ close(parser->file.descriptor);
+ return KNOT_EFILE;
+ }
+
+ // Check for empty file (cannot mmap).
+ if (file_stat.st_size > 0) {
+ // Map the file to the memory.
+ char *start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED,
+ parser->file.descriptor, 0);
+ if (start == MAP_FAILED) {
+ close(parser->file.descriptor);
+ return KNOT_ENOMEM;
+ }
+
+ // Try to set the mapped memory advise to sequential.
+#if defined(MADV_SEQUENTIAL) && !defined(__sun)
+ (void)madvise(start, file_stat.st_size, MADV_SEQUENTIAL);
+#else
+#ifdef POSIX_MADV_SEQUENTIAL
+ (void)posix_madvise(start, file_stat.st_size, POSIX_MADV_SEQUENTIAL);
+#endif /* POSIX_MADV_SEQUENTIAL */
+#endif /* MADV_SEQUENTIAL && !__sun */
+
+ // Set the parser input limits.
+ parser->input.start = start;
+ parser->input.current = start;
+ parser->input.end = start + file_stat.st_size;
+
+ parser->input.eof = false;
+ } else {
+ parser->input.eof = true;
+ }
+
+ parser->file.name = strdup(file_name);
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_parse(
+ yp_parser_t *parser)
+{
+ if (parser == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EPARSEFAIL;
+
+ // Run the parser until found new item, error or end of input.
+ do {
+ // Check for the end of the input.
+ if (parser->input.current == parser->input.end) {
+ if (parser->input.eof) {
+ // End of parsing.
+ return KNOT_EOF;
+ } else {
+ // Set the parser to final parsing.
+ parser->input.eof = true;
+ }
+ }
+
+ // Parse the next item.
+ ret = _yp_parse(parser);
+ } while (ret == KNOT_EFEWDATA);
+
+ return ret;
+}
diff --git a/src/libknot/yparser/yparser.h b/src/libknot/yparser/yparser.h
new file mode 100644
index 0000000..88e0e00
--- /dev/null
+++ b/src/libknot/yparser/yparser.h
@@ -0,0 +1,149 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Simple parser (Yparser) of a YAML-inspired data format.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/*! Maximal length of textual key value. */
+#define YP_MAX_TXT_KEY_LEN 127
+/*! Maximal length of textual data value. */
+#define YP_MAX_TXT_DATA_LEN 32767
+
+/*! Parser events indicating type of lastly parsed item. */
+typedef enum {
+ YP_ENULL = 0, /*!< No valid data. */
+ YP_EKEY0, /*!< First level item. */
+ YP_EKEY1, /*!< Second level item. */
+ YP_EID, /*!< Second level identifier. */
+} yp_event_t;
+
+/*! Context structure of yparser. */
+typedef struct {
+ /*! Current parser state (Ragel internals). */
+ int cs;
+ /*! Indication if the current item was already processed. */
+ bool processed;
+ /*! Current block indentation. */
+ size_t indent;
+ /*! Last id dash position. */
+ size_t id_pos;
+
+ /*! Input parameters. */
+ struct {
+ /*! Start of the block. */
+ const char *start;
+ /*! Current parser position. */
+ const char *current;
+ /*! End of the block. */
+ const char *end;
+ /*! Indication for the final block parsing. */
+ bool eof;
+ } input;
+
+ /*! File input parameters. */
+ struct {
+ /*! File name. */
+ char *name;
+ /*! File descriptor. */
+ int descriptor;
+ } file;
+
+ /*! [out] Current line number (error location). */
+ size_t line_count;
+ /*! [out] Current event. */
+ yp_event_t event;
+ /*! [out] Parsed key (zero terminated string). */
+ char key[YP_MAX_TXT_KEY_LEN + 1];
+ /*! [out] Key length. */
+ size_t key_len;
+ /*! [out] Parsed data (zero terminated string). */
+ char data[YP_MAX_TXT_DATA_LEN + 1];
+ /*! [out] Data length. */
+ size_t data_len;
+} yp_parser_t;
+
+/*!
+ * Initializes the parser.
+ *
+ * \param[in] parser Parser context.
+ */
+void yp_init(
+ yp_parser_t *parser
+);
+
+/*!
+ * Deinitializes the parser.
+ *
+ * \param[in] parser Parser context.
+ */
+void yp_deinit(
+ yp_parser_t *parser
+);
+
+/*!
+ * Sets the parser to parse given string.
+ *
+ * \param[in] parser Parser context.
+ * \param[in] input The string to parse.
+ * \param[in] size Length of the string.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_set_input_string(
+ yp_parser_t *parser,
+ const char *input,
+ size_t size
+);
+
+/*!
+ * Sets the parser to parse given file.
+ *
+ * \param[in] parser Parser context.
+ * \param[in] file_name The filename to parse.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_set_input_file(
+ yp_parser_t *parser,
+ const char *file_name
+);
+
+/*!
+ * Parses one item from the input.
+ *
+ * If the item has more values, this function returns for each value. The item
+ * can also have no value.
+ *
+ * \param[in] parser Parser context.
+ *
+ * \return Error code, KNOT_EOK if success, KNOT_EOF if end of data.
+ */
+int yp_parse(
+ yp_parser_t *parser
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/ypbody.c b/src/libknot/yparser/ypbody.c
new file mode 100644
index 0000000..ad9cf1f
--- /dev/null
+++ b/src/libknot/yparser/ypbody.c
@@ -0,0 +1,460 @@
+
+/* Copyright (C) 2021 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 <string.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/errcode.h"
+
+
+
+
+// Include parser static data (Ragel internals).
+
+static const char _yparser_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2, 1,
+ 3, 1, 4, 1, 6, 1, 8, 1,
+ 9, 1, 10, 1, 11, 1, 12, 1,
+ 15, 2, 1, 0, 2, 1, 2, 2,
+ 1, 15, 2, 2, 0, 2, 3, 4,
+ 2, 5, 4, 2, 6, 0, 2, 7,
+ 8, 2, 12, 13, 2, 14, 12, 3,
+ 1, 2, 0, 3, 1, 7, 8, 3,
+ 1, 12, 13, 3, 1, 14, 12, 3,
+ 2, 7, 8, 3, 2, 12, 13, 3,
+ 2, 14, 12, 4, 1, 2, 7, 8,
+ 4, 1, 2, 12, 13, 4, 1, 2,
+ 14, 12
+};
+
+static const unsigned char _yparser_key_offsets[] = {
+ 0, 0, 14, 16, 27, 29, 32, 43,
+ 44, 54, 65, 67, 68, 78, 90, 94,
+ 96, 99, 104, 107, 110, 122, 132, 142,
+ 145, 148, 151, 156, 159, 162, 176, 190,
+ 204
+};
+
+static const char _yparser_trans_keys[] = {
+ 10, 13, 32, 35, 42, 45, 92, 95,
+ 46, 57, 65, 90, 97, 122, 10, 13,
+ 32, 42, 58, 92, 95, 45, 57, 65,
+ 90, 97, 122, 32, 58, 10, 13, 32,
+ 32, 42, 58, 92, 95, 45, 57, 65,
+ 90, 97, 122, 32, 32, 42, 92, 95,
+ 46, 57, 65, 90, 97, 122, 32, 42,
+ 58, 92, 95, 45, 57, 65, 90, 97,
+ 122, 32, 58, 32, 32, 34, 35, 44,
+ 92, 127, 0, 31, 91, 93, 10, 13,
+ 32, 44, 92, 127, 0, 31, 34, 35,
+ 91, 93, 10, 13, 32, 35, 10, 13,
+ 34, 32, 126, 34, 92, 127, 0, 31,
+ 10, 13, 32, 34, 32, 126, 10, 13,
+ 32, 34, 35, 44, 91, 92, 93, 127,
+ 0, 31, 32, 34, 35, 44, 92, 127,
+ 0, 31, 91, 93, 32, 44, 91, 92,
+ 93, 127, 0, 31, 34, 35, 32, 44,
+ 93, 10, 13, 32, 34, 32, 126, 34,
+ 92, 127, 0, 31, 32, 44, 93, 34,
+ 32, 126, 10, 13, 32, 35, 42, 45,
+ 92, 95, 46, 57, 65, 90, 97, 122,
+ 10, 13, 32, 35, 42, 45, 92, 95,
+ 46, 57, 65, 90, 97, 122, 10, 13,
+ 32, 35, 42, 45, 92, 95, 46, 57,
+ 65, 90, 97, 122, 10, 13, 32, 35,
+ 42, 45, 92, 95, 46, 57, 65, 90,
+ 97, 122, 0
+};
+
+static const char _yparser_single_lengths[] = {
+ 0, 8, 2, 5, 2, 3, 5, 1,
+ 4, 5, 2, 1, 6, 6, 4, 2,
+ 1, 3, 3, 1, 10, 6, 6, 3,
+ 3, 1, 3, 3, 1, 8, 8, 8,
+ 8
+};
+
+static const char _yparser_range_lengths[] = {
+ 0, 3, 0, 3, 0, 0, 3, 0,
+ 3, 3, 0, 0, 2, 3, 0, 0,
+ 1, 1, 0, 1, 1, 2, 2, 0,
+ 0, 1, 1, 0, 1, 3, 3, 3,
+ 3
+};
+
+static const unsigned char _yparser_index_offsets[] = {
+ 0, 0, 12, 15, 24, 27, 31, 40,
+ 42, 50, 59, 62, 64, 73, 83, 88,
+ 91, 94, 99, 103, 106, 118, 127, 136,
+ 140, 144, 147, 152, 156, 159, 171, 183,
+ 195
+};
+
+static const char _yparser_indicies[] = {
+ 1, 2, 3, 4, 5, 6, 5, 5,
+ 5, 5, 5, 0, 1, 2, 4, 7,
+ 8, 9, 8, 8, 8, 8, 8, 0,
+ 10, 11, 0, 12, 13, 14, 0, 15,
+ 16, 17, 16, 16, 16, 16, 16, 0,
+ 18, 0, 18, 19, 19, 19, 19, 19,
+ 19, 0, 20, 21, 22, 21, 21, 21,
+ 21, 21, 0, 23, 24, 0, 25, 0,
+ 25, 27, 0, 0, 28, 0, 0, 0,
+ 26, 30, 31, 32, 0, 33, 0, 0,
+ 0, 0, 29, 12, 13, 34, 35, 0,
+ 12, 13, 35, 36, 29, 0, 38, 39,
+ 0, 0, 37, 30, 31, 32, 0, 40,
+ 37, 0, 12, 13, 14, 27, 35, 0,
+ 41, 28, 0, 0, 0, 26, 41, 43,
+ 0, 0, 44, 0, 0, 0, 42, 46,
+ 47, 0, 48, 49, 0, 0, 0, 45,
+ 50, 41, 51, 0, 12, 13, 34, 0,
+ 52, 45, 0, 54, 55, 0, 0, 53,
+ 46, 47, 49, 0, 56, 53, 0, 1,
+ 2, 3, 4, 57, 6, 57, 57, 57,
+ 57, 57, 0, 59, 60, 61, 62, 63,
+ 64, 63, 63, 63, 63, 63, 58, 65,
+ 66, 67, 68, 69, 70, 69, 69, 69,
+ 69, 69, 0, 71, 72, 73, 74, 75,
+ 76, 75, 75, 75, 75, 75, 58, 0
+};
+
+static const char _yparser_trans_targs[] = {
+ 0, 30, 31, 1, 2, 3, 7, 4,
+ 3, 5, 4, 5, 32, 29, 20, 4,
+ 6, 5, 8, 9, 10, 9, 11, 10,
+ 11, 12, 13, 17, 16, 13, 32, 29,
+ 14, 16, 14, 15, 13, 17, 18, 19,
+ 17, 21, 22, 26, 25, 22, 23, 21,
+ 25, 24, 23, 24, 22, 26, 27, 28,
+ 26, 6, 0, 30, 31, 1, 2, 6,
+ 7, 30, 31, 1, 2, 6, 7, 30,
+ 31, 1, 2, 6, 7
+};
+
+static const char _yparser_trans_actions[] = {
+ 23, 1, 0, 49, 0, 46, 52, 17,
+ 13, 17, 0, 0, 1, 0, 0, 15,
+ 13, 15, 21, 46, 19, 13, 19, 0,
+ 0, 0, 37, 7, 37, 9, 43, 11,
+ 11, 9, 0, 0, 40, 9, 0, 9,
+ 40, 0, 37, 7, 37, 9, 11, 11,
+ 9, 11, 0, 0, 40, 9, 0, 9,
+ 40, 46, 31, 55, 28, 88, 28, 83,
+ 93, 34, 5, 75, 5, 71, 79, 25,
+ 3, 63, 3, 59, 67
+};
+
+static const char _yparser_eof_actions[] = {
+ 0, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 0, 28, 5,
+ 3
+};
+
+
+
+
+
+int _yp_start_state = 29;
+
+int _yp_parse(
+ yp_parser_t *parser)
+{
+ // Parser input limits (Ragel internals).
+ const char *p, *pe, *eof;
+
+ // Current item indent.
+ size_t indent = 0;
+ // Current id dash position.
+ size_t id_pos = 0;
+ // Indicates if the current parsing step contains an item.
+ bool found = false;
+
+ if (!parser->input.eof) { // Restore parser input limits.
+ p = parser->input.current;
+ pe = parser->input.end;
+ eof = NULL;
+ } else { // Set the last artificial block with just one new line char.
+ p = "\n";
+ pe = p + 1;
+ eof = pe;
+ }
+
+ // Include parser body.
+
+ {
+ int _klen;
+ unsigned int _trans;
+ const char *_acts;
+ unsigned int _nacts;
+ const char *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( parser->cs == 0 )
+ goto _out;
+_resume:
+ _keys = _yparser_trans_keys + _yparser_key_offsets[ parser->cs];
+ _trans = _yparser_index_offsets[ parser->cs];
+
+ _klen = _yparser_single_lengths[ parser->cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( (*p) < *_mid )
+ _upper = _mid - 1;
+ else if ( (*p) > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (unsigned int)(_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _yparser_range_lengths[ parser->cs];
+ if ( _klen > 0 ) {
+ const char *_lower = _keys;
+ const char *_mid;
+ const char *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( (*p) < _mid[0] )
+ _upper = _mid - 2;
+ else if ( (*p) > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += (unsigned int)((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _yparser_indicies[_trans];
+ parser->cs = _yparser_trans_targs[_trans];
+
+ if ( _yparser_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _yparser_actions + _yparser_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+ {
+ // Return if key without value.
+ if (parser->event != YP_ENULL && !parser->processed) {
+ parser->processed = true;
+ found = true;
+ {p++; goto _out; }
+ }
+ }
+ break;
+ case 1:
+ {
+ parser->line_count++;
+ parser->event = YP_ENULL;
+ parser->processed = false;
+ }
+ break;
+ case 2:
+ {
+ indent = 0;
+ id_pos = 0;
+ }
+ break;
+ case 3:
+ {
+ parser->data_len = 0;
+ }
+ break;
+ case 4:
+ {
+ if (parser->data_len >= sizeof(parser->data) - 1) {
+ return KNOT_ESPACE;
+ }
+ parser->data[parser->data_len++] = (*p);
+ }
+ break;
+ case 5:
+ {
+ parser->data_len--;
+ }
+ break;
+ case 6:
+ {
+ // Return if a value parsed.
+ parser->data[parser->data_len] = '\0';
+ parser->processed = true;
+ found = true;
+ {p++; goto _out; }
+ }
+ break;
+ case 7:
+ {
+ if (indent > 0 && parser->indent > 0 &&
+ indent != parser->indent) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+ parser->processed = false;
+ parser->key_len = 0;
+ parser->data_len = 0;
+ parser->event = YP_ENULL;
+ }
+ break;
+ case 8:
+ {
+ if (parser->key_len >= sizeof(parser->key) - 1) {
+ return KNOT_ESPACE;
+ }
+ parser->key[parser->key_len++] = (*p);
+ }
+ break;
+ case 9:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = 0;
+ parser->id_pos = 0;
+ parser->event = YP_EKEY0;
+ }
+ break;
+ case 10:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = indent;
+ parser->event = YP_EKEY1;
+ }
+ break;
+ case 11:
+ {
+ parser->key[parser->key_len] = '\0';
+ parser->indent = indent;
+ parser->id_pos = id_pos;
+ parser->event = YP_EID;
+ }
+ break;
+ case 12:
+ {
+ indent++;
+ }
+ break;
+ case 13:
+ {
+ id_pos++;
+ }
+ break;
+ case 14:
+ {
+ if (id_pos > 0 && parser->id_pos > 0 &&
+ id_pos != parser->id_pos) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+ parser->indent = 0;
+ }
+ break;
+ case 15:
+ {
+ switch ((*p)) {
+ case '\t':
+ return KNOT_YP_ECHAR_TAB;
+ default:
+ return KNOT_EPARSEFAIL;
+ }
+ }
+ break;
+ }
+ }
+
+_again:
+ if ( parser->cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const char *__acts = _yparser_actions + _yparser_eof_actions[ parser->cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 1:
+ {
+ parser->line_count++;
+ parser->event = YP_ENULL;
+ parser->processed = false;
+ }
+ break;
+ case 2:
+ {
+ indent = 0;
+ id_pos = 0;
+ }
+ break;
+ case 15:
+ {
+ switch ((*p)) {
+ case '\t':
+ return KNOT_YP_ECHAR_TAB;
+ default:
+ return KNOT_EPARSEFAIL;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ _out: {}
+ }
+
+
+ // Store the current parser position.
+ if (!parser->input.eof) {
+ parser->input.current = p;
+ } else {
+ parser->input.current = parser->input.end;
+ }
+
+ // Check for general parser error.
+ if (parser->cs == 0) {
+ return KNOT_EPARSEFAIL;
+ }
+
+ // Check if parsed an item.
+ if (found) {
+ return KNOT_EOK;
+ } else {
+ return KNOT_EFEWDATA;
+ }
+}
diff --git a/src/libknot/yparser/ypformat.c b/src/libknot/yparser/ypformat.c
new file mode 100644
index 0000000..3284924
--- /dev/null
+++ b/src/libknot/yparser/ypformat.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 2017 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 <stdio.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+static int format_item(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ const char *prefix,
+ bool first_value,
+ bool last_value)
+{
+ if (item == NULL || out == NULL || prefix == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Format key part.
+ int ret = snprintf(out, out_len, "%s%s%s%s",
+ first_value ? prefix : "",
+ first_value ? item->name + 1 : "",
+ first_value ? ":" : "",
+ item->type == YP_TGRP ?
+ "\n" : (first_value && !last_value ? " [ " : " "));
+ if (ret < 0 || ret >= out_len) {
+ return KNOT_ESPACE;
+ }
+ out += ret;
+ out_len -= ret;
+
+ // Finish if group.
+ if (item->type == YP_TGRP) {
+ return KNOT_EOK;
+ }
+
+ // Format data part.
+ size_t aux_len = out_len;
+ ret = yp_item_to_txt(item, data, data_len, out, &aux_len, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ out += aux_len;
+ out_len -= aux_len;
+
+ // Format data end.
+ ret = snprintf(out, out_len, "%s%s",
+ last_value && !first_value ? " ]" : "",
+ last_value ? "\n" : ",");
+ if (ret < 0 || ret >= out_len) {
+ return KNOT_ESPACE;
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_format_key0(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value)
+{
+ return format_item(item, data, data_len, out, out_len, style, "",
+ first_value, last_value);
+}
+
+_public_
+int yp_format_id(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style)
+{
+ if (data == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return format_item(item, data, data_len, out, out_len, style, " - ",
+ true, true);
+}
+
+_public_
+int yp_format_key1(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value)
+{
+ return format_item(item, data, data_len, out, out_len, style, " ",
+ first_value, last_value);
+}
diff --git a/src/libknot/yparser/ypformat.h b/src/libknot/yparser/ypformat.h
new file mode 100644
index 0000000..c1d21bd
--- /dev/null
+++ b/src/libknot/yparser/ypformat.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2017 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Tools for Yparser format creation.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/yparser/ypschema.h"
+
+/*!
+ * Formats key0 item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ * \param[in] first_value First value indication (multivalued support).
+ * \param[in] last_value Last value indication (multivalued support).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_key0(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value
+);
+
+/*!
+ * Formats identifier item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_id(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style
+);
+
+/*!
+ * Formats key1 item.
+ *
+ * \param[in] item Schema item to format.
+ * \param[in] data Data to format.
+ * \param[in] data_len Data length.
+ * \param[out] out Output buffer.
+ * \param[in, out] out_len Output buffer length, output length.
+ * \param[in] style Value style.
+ * \param[in] first_value First value indication (multivalued support).
+ * \param[in] last_value Last value indication (multivalued support).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_format_key1(
+ const yp_item_t *item,
+ const uint8_t *data,
+ size_t data_len,
+ char *out,
+ size_t out_len,
+ yp_style_t style,
+ bool first_value,
+ bool last_value
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/ypschema.c b/src/libknot/yparser/ypschema.c
new file mode 100644
index 0000000..0ac5b57
--- /dev/null
+++ b/src/libknot/yparser/ypschema.c
@@ -0,0 +1,598 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+
+static size_t schema_count(
+ const yp_item_t *src)
+{
+ size_t count = 0;
+ for (const yp_item_t *item = src; item->name != NULL; item++) {
+ count++;
+ }
+
+ return count;
+}
+
+/*! Initializes the referenced item. */
+static int set_ref_item(
+ yp_item_t *dst,
+ const yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Get referenced section.
+ const yp_name_t *ref_name = dst->var.r.ref_name;
+ const yp_item_t *ref = yp_schema_find(ref_name, NULL, schema);
+ if (ref == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+ dst->var.r.ref = ref;
+
+ // Get referenced group if supported.
+ const yp_name_t *grp_ref_name = dst->var.r.grp_ref_name;
+ if (grp_ref_name != NULL) {
+ const yp_item_t *grp_ref = yp_schema_find(grp_ref_name, NULL, schema);
+ if (grp_ref == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+ dst->var.r.grp_ref = grp_ref;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! Copies the sub_items list and initializes pointer to the identifier item. */
+static int set_grp_item(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Count subitems.
+ size_t count = schema_count(src->var.g.sub_items);
+
+ // Allocate space for subitems + terminal zero item.
+ size_t memsize = (count + 1) * sizeof(yp_item_t);
+ dst->sub_items = malloc(memsize);
+ if (dst->sub_items == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(dst->sub_items, 0, memsize);
+
+ // Copy subitems.
+ for (size_t i = 0; i < count; i++) {
+ // The first item is an identifier if multi group.
+ if (i == 0 && (dst->flags & YP_FMULTI)) {
+ dst->var.g.id = &dst->sub_items[0];
+ }
+
+ // Copy sub-item.
+ dst->sub_items[i] = src->var.g.sub_items[i];
+
+ // Initialize sub-item.
+ int ret = KNOT_EOK;
+ switch (dst->sub_items[i].type) {
+ case YP_TREF:
+ ret = set_ref_item(dst->sub_items + i, schema);
+ break;
+ case YP_TGRP: // Deeper hierarchy is not supported.
+ ret = KNOT_ENOTSUP;
+ break;
+ default:
+ break;
+ }
+
+ // Set the parent item.
+ dst->sub_items[i].parent = dst;
+
+ if (ret != KNOT_EOK) {
+ free(dst->sub_items);
+ dst->sub_items = NULL;
+ return ret;
+ }
+ }
+
+ if (src->flags & YP_FALLOC) {
+ dst->var.g.sub_items = malloc(memsize);
+ if (dst->var.g.sub_items == NULL) {
+ free(dst->sub_items);
+ dst->sub_items = NULL;
+ return KNOT_ENOMEM;
+ }
+ memcpy((void *)dst->var.g.sub_items, src->var.g.sub_items, memsize);
+ }
+
+ return KNOT_EOK;
+}
+
+static int set_item(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Check maximal item name length.
+ if ((uint8_t)src->name[0] > YP_MAX_ITEM_NAME_LEN) {
+ return KNOT_ERANGE;
+ }
+
+ // Copy the static data.
+ *dst = *src;
+
+ // Copy item name into dynamic memory.
+ if (src->flags & YP_FALLOC) {
+ dst->name = malloc(src->name[0] + 2);
+ if (dst->name == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy((void *)dst->name, src->name, src->name[0] + 2);
+ }
+
+ int ret;
+
+ // Item type specific preparation.
+ switch (src->type) {
+ case YP_TREF:
+ ret = set_ref_item(dst, schema);
+ break;
+ case YP_TGRP:
+ ret = set_grp_item(dst, src, schema);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ if (ret != KNOT_EOK && src->flags & YP_FALLOC) {
+ free((void *)dst->name);
+ }
+
+ return ret;
+}
+
+static void unset_item(
+ yp_item_t *item)
+{
+ if (item->flags & YP_FALLOC) {
+ free((void *)item->name);
+ }
+ if (item->type & YP_TGRP) {
+ free(item->sub_items);
+ if (item->flags & YP_FALLOC) {
+ free((void *)item->var.g.sub_items);
+ }
+ }
+
+ memset(item, 0, sizeof(yp_item_t));
+}
+
+static int schema_copy(
+ yp_item_t *dst,
+ const yp_item_t *src,
+ const yp_item_t *schema)
+{
+ // Copy the schema.
+ for (int i = 0; src[i].name != NULL; i++) {
+ int ret = set_item(&dst[i], &src[i], schema);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_schema_copy(
+ yp_item_t **dst,
+ const yp_item_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Allocate space for new schema (+ terminal NULL item).
+ size_t size = (schema_count(src) + 1) * sizeof(yp_item_t);
+ yp_item_t *out = malloc(size);
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, size);
+
+ // Copy the schema.
+ int ret = schema_copy(out, src, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ *dst = out;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_schema_merge(
+ yp_item_t **dst,
+ const yp_item_t *src1,
+ const yp_item_t *src2)
+{
+ if (dst == NULL || src1 == NULL || src2 == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t count1 = schema_count(src1);
+ size_t count2 = schema_count(src2);
+
+ // Allocate space for new schema (+ terminal NULL item).
+ size_t size = (count1 + count2 + 1) * sizeof(yp_item_t);
+ yp_item_t *out = malloc(size);
+ if (out == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memset(out, 0, size);
+
+ // Copy the first schema.
+ int ret = schema_copy(out, src1, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ // Copy the second schema.
+ ret = schema_copy(out + count1, src2, out);
+ if (ret != KNOT_EOK) {
+ free(out);
+ return ret;
+ }
+
+ *dst = out;
+
+ return KNOT_EOK;
+}
+
+_public_
+void yp_schema_purge_dynamic(
+ yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return;
+ }
+
+ for (yp_item_t *item = schema; item->name != NULL; item++) {
+ if (item->flags & YP_FALLOC) {
+ unset_item(item);
+ }
+ }
+}
+
+_public_
+void yp_schema_free(
+ yp_item_t *schema)
+{
+ if (schema == NULL) {
+ return;
+ }
+
+ for (yp_item_t *item = schema; item->name != NULL; item++) {
+ unset_item(item);
+ }
+ free(schema);
+}
+
+/*! Search the schema for an item with the given name. */
+static const yp_item_t* find_item(
+ const char *name,
+ size_t name_len,
+ const yp_item_t *schema)
+{
+ if (name == NULL || schema == NULL) {
+ return NULL;
+ }
+
+ for (const yp_item_t *item = schema; item->name != NULL; item++) {
+ if (item->name[0] != name_len) {
+ continue;
+ }
+ if (memcmp(item->name + 1, name, name_len) == 0) {
+ return item;
+ }
+ }
+
+ return NULL;
+}
+
+_public_
+const yp_item_t* yp_schema_find(
+ const yp_name_t *name,
+ const yp_name_t *parent_name,
+ const yp_item_t *schema)
+{
+ if (name == NULL || schema == NULL) {
+ return NULL;
+ }
+
+ if (parent_name == NULL) {
+ return find_item(name + 1, name[0], schema);
+ } else {
+ const yp_item_t *parent = find_item(parent_name + 1,
+ parent_name[0], schema);
+ if (parent == NULL) {
+ return NULL;
+ }
+ return find_item(name + 1, name[0], parent->sub_items);
+ }
+}
+
+_public_
+yp_check_ctx_t* yp_schema_check_init(
+ yp_item_t **schema)
+{
+ if (schema == NULL) {
+ return NULL;
+ }
+
+ yp_check_ctx_t *ctx = malloc(sizeof(yp_check_ctx_t));
+ if (ctx == NULL) {
+ return NULL;
+ }
+ memset(ctx, 0, sizeof(yp_check_ctx_t));
+
+ ctx->schema = schema;
+
+ return ctx;
+}
+
+static void reset_ctx(
+ yp_check_ctx_t *ctx,
+ size_t index)
+{
+ assert(index < YP_MAX_NODE_DEPTH);
+
+ yp_node_t *node = &ctx->nodes[index];
+
+ node->parent = (index > 0) ? &ctx->nodes[index - 1] : NULL;
+ node->item = NULL;
+ node->id_len = 0;
+ node->data_len = 0;
+
+ ctx->current = index;
+}
+
+static int check_item(
+ const char *key,
+ size_t key_len,
+ const char *data,
+ size_t data_len,
+ yp_check_ctx_t *ctx,
+ bool allow_key1_without_id)
+{
+ yp_node_t *node = &ctx->nodes[ctx->current];
+ yp_node_t *parent = node->parent;
+ bool is_id = false;
+
+ if (parent != NULL) {
+ // Check for invalid indentation.
+ if (parent->item == NULL) {
+ return KNOT_YP_EINVAL_INDENT;
+ }
+
+ // Check if valid group parent.
+ if (parent->item->type != YP_TGRP) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Check if valid subitem.
+ node->item = find_item(key, key_len, parent->item->sub_items);
+ } else {
+ node->item = find_item(key, key_len, *ctx->schema);
+ }
+ if (node->item == NULL) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Check if the parent requires id specification.
+ if (parent != NULL && parent->item->var.g.id != NULL) {
+ // Check if id.
+ if (node->item == parent->item->var.g.id) {
+ is_id = true;
+ // Move current to the parent.
+ --(ctx->current);
+ // Check for missing id.
+ } else if (parent->id_len == 0 && !allow_key1_without_id) {
+ return KNOT_YP_ENOID;
+ }
+ }
+
+ // Return if no data provided.
+ if (data == NULL) {
+ return KNOT_EOK;
+ }
+
+ // Group cannot have data.
+ if (data_len != 0 && node->item->type == YP_TGRP) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+
+ // Convert item data to binary format.
+ const yp_item_t *item = (node->item->type != YP_TREF) ?
+ node->item : node->item->var.r.ref->var.g.id;
+ if (is_id) {
+ // Textual id must not be empty.
+ if (data_len == 0) {
+ return KNOT_YP_ENODATA;
+ }
+
+ parent->id_len = sizeof(((yp_node_t *)NULL)->id);
+ int ret = yp_item_to_bin(item, data, data_len, parent->id,
+ &parent->id_len);
+
+ // Binary id must not be empty.
+ if (ret == KNOT_EOK && parent->id_len == 0) {
+ return KNOT_YP_EINVAL_DATA;
+ }
+
+ return ret;
+ } else {
+ node->data_len = sizeof(((yp_node_t *)NULL)->data);
+ int ret = yp_item_to_bin(item, data, data_len, node->data,
+ &node->data_len);
+ return ret;
+ }
+}
+
+_public_
+int yp_schema_check_parser(
+ yp_check_ctx_t *ctx,
+ const yp_parser_t *parser)
+{
+ if (ctx == NULL || parser == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret;
+
+ switch (parser->event) {
+ case YP_EKEY0:
+ reset_ctx(ctx, 0);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ break;
+ case YP_EKEY1:
+ reset_ctx(ctx, 1);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ // Check for KEY1 event with id item.
+ if (ctx->current != 1) {
+ return KNOT_YP_ENOTSUP_ID;
+ }
+
+ break;
+ case YP_EID:
+ reset_ctx(ctx, 1);
+ ret = check_item(parser->key, parser->key_len, parser->data,
+ parser->data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+
+ // Check for ID event with nonid item.
+ if (ctx->current != 0) {
+ return KNOT_YP_EINVAL_ID;
+ }
+
+ break;
+ default:
+ ret = KNOT_EPARSEFAIL;
+ break;
+ }
+
+ return ret;
+}
+
+_public_
+int yp_schema_check_str(
+ yp_check_ctx_t *ctx,
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t key0_len = (key0 != NULL) ? strlen(key0) : 0;
+ size_t key1_len = (key1 != NULL) ? strlen(key1) : 0;
+ size_t id_len = (id != NULL) ? strlen(id) : 0;
+ size_t data_len = (data != NULL) ? strlen(data) : 0;
+
+ // Key0 must always be non-empty.
+ if (key0_len == 0) {
+ return KNOT_YP_EINVAL_ITEM;
+ }
+
+ // Process key0.
+ reset_ctx(ctx, 0);
+ if (key1_len == 0) {
+ int ret = check_item(key0, key0_len, data, data_len, ctx, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ int ret = check_item(key0, key0_len, NULL, 0, ctx, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Process id.
+ if (id_len != 0) {
+ if (ctx->nodes[0].item->type != YP_TGRP ||
+ ctx->nodes[0].item->var.g.id == NULL) {
+ return KNOT_YP_ENOTSUP_ID;
+ }
+ const yp_name_t *name = ctx->nodes[0].item->var.g.id->name;
+
+ reset_ctx(ctx, 1);
+ int ret = check_item(name + 1, name[0], id, id_len, ctx, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for non-id item (should not happen).
+ assert(ctx->current == 0);
+
+ // Check for group id with data.
+ if (key1_len == 0 && data != NULL) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+ }
+
+ // Process key1.
+ if (key1_len != 0) {
+ reset_ctx(ctx, 1);
+ int ret = check_item(key1, key1_len, data, data_len, ctx, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for id in key1 with extra data.
+ if (ctx->current != 1 && id_len != 0 && data != NULL) {
+ return KNOT_YP_ENOTSUP_DATA;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+_public_
+void yp_schema_check_deinit(
+ yp_check_ctx_t* ctx)
+{
+ free(ctx);
+}
diff --git a/src/libknot/yparser/ypschema.h b/src/libknot/yparser/ypschema.h
new file mode 100644
index 0000000..91e5165
--- /dev/null
+++ b/src/libknot/yparser/ypschema.h
@@ -0,0 +1,352 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Schema layer for Yparser.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/lookup.h"
+
+struct wire_ctx;
+
+/*! Maximal length of item name. */
+#define YP_MAX_ITEM_NAME_LEN 64
+/*! Maximal length of binary identifier name (maximal dname length). */
+#define YP_MAX_ID_LEN 255
+/*! Maximal length of binary data (rough limit). */
+#define YP_MAX_DATA_LEN 32768
+/*! Integer item nil definition. */
+#define YP_NIL INT64_MIN
+/*! Maximal number of miscellaneous callbacks/pointers. */
+#define YP_MAX_MISC_COUNT 4
+/*! Maximal node stack depth. */
+#define YP_MAX_NODE_DEPTH 2
+
+#define YP_TXT_BIN_PARAMS struct wire_ctx *in, struct wire_ctx *out, const uint8_t *stop
+#define YP_BIN_TXT_PARAMS struct wire_ctx *in, struct wire_ctx *out
+
+/*! Helper macros for item variables definition. */
+#define YP_VNONE .var.i = { 0 }
+#define YP_VINT .var.i
+#define YP_VBOOL .var.b
+#define YP_VOPT .var.o
+#define YP_VSTR .var.s
+#define YP_VADDR .var.a
+#define YP_VNET .var.d
+#define YP_VDNAME .var.d
+#define YP_VHEX .var.d
+#define YP_VB64 .var.d
+#define YP_VDATA .var.d
+#define YP_VREF .var.r
+#define YP_VGRP .var.g
+
+/*! Schema item name is a char string with a leading byte (string length). */
+typedef char yp_name_t;
+
+/*! Schema item type. */
+typedef enum {
+ YP_TNONE = 0, /*!< Unspecified. */
+ YP_TINT, /*!< Integer. */
+ YP_TBOOL, /*!< Boolean. */
+ YP_TOPT, /*!< Option from the list. */
+ YP_TSTR, /*!< String. */
+ YP_THEX, /*!< String or hexadecimal string if "0x" prefix. */
+ YP_TADDR, /*!< Address (address[\@port] or UNIX socket path). */
+ YP_TNET, /*!< Network address range (address[/mask] or address-address). */
+ YP_TDNAME, /*!< Domain name. */
+ YP_TB64, /*!< Base64 encoded string. */
+ YP_TDATA, /*!< Customized data. */
+ YP_TREF, /*!< Reference to another item. */
+ YP_TGRP, /*!< Group of sub-items. */
+} yp_type_t;
+
+/*! Schema item flags. */
+typedef enum {
+ YP_FNONE = 0, /*!< Unspecified. */
+ YP_FMULTI = 1 << 0, /*!< Multivalued item. */
+ YP_FALLOC = 1 << 1, /*!< Allocated item. */
+ YP_FUSR1 = 1 << 5, /*!< User-defined flag1. */
+ YP_FUSR2 = 1 << 6, /*!< User-defined flag2. */
+ YP_FUSR3 = 1 << 7, /*!< User-defined flag3. */
+ YP_FUSR4 = 1 << 8, /*!< User-defined flag4. */
+ YP_FUSR5 = 1 << 9, /*!< User-defined flag5. */
+ YP_FUSR6 = 1 << 10, /*!< User-defined flag6. */
+ YP_FUSR7 = 1 << 11, /*!< User-defined flag7. */
+ YP_FUSR8 = 1 << 12, /*!< User-defined flag8. */
+ YP_FUSR9 = 1 << 13, /*!< User-defined flag9. */
+ YP_FUSR10 = 1 << 14, /*!< User-defined flag10. */
+ YP_FUSR11 = 1 << 15, /*!< User-defined flag11. */
+ YP_FUSR12 = 1 << 16, /*!< User-defined flag12. */
+ YP_FUSR13 = 1 << 17, /*!< User-defined flag13. */
+ YP_FUSR14 = 1 << 18, /*!< User-defined flag14. */
+ YP_FUSR15 = 1 << 19, /*!< User-defined flag15. */
+ YP_FUSR16 = 1 << 20, /*!< User-defined flag16. */
+} yp_flag_t;
+
+/*! Schema item style. */
+typedef enum {
+ YP_SNONE = 0, /*!< Unspecified. */
+ YP_SSIZE = 1 << 0, /*!< Size unit (B, K, M, G) (in, out). */
+ YP_STIME = 1 << 1, /*!< Time unit (s, m, h, d) (in, out). */
+ YP_SUNIT = YP_SSIZE | YP_STIME, /*!< Unit (in, out). */
+ YP_SNOQUOTE = 1 << 2 /*!< Unquoted value (out). */
+} yp_style_t;
+
+typedef struct yp_item yp_item_t;
+
+/*! Schema item variables (type dependent). */
+typedef union {
+ /*! Integer variables. */
+ struct {
+ /*! Minimal value. */
+ int64_t min;
+ /*! Maximal value. */
+ int64_t max;
+ /*! Default value. */
+ int64_t dflt;
+ /*! Possible unit type. */
+ yp_style_t unit;
+ } i;
+ /*! Boolean variables. */
+ struct {
+ /*! Default value. */
+ bool dflt;
+ } b;
+ /*! Option variables. */
+ struct {
+ /*! List of options (maximal value is 255). */
+ struct knot_lookup const *opts;
+ /*! Default value. */
+ unsigned dflt;
+ } o;
+ /*! String variables. */
+ struct {
+ /*! Default value. */
+ char const *dflt;
+ } s;
+ /*! Address variables. */
+ struct {
+ /*! Default port. */
+ uint16_t dflt_port;
+ /*! Default socket. */
+ char const *dflt_socket;
+ } a;
+ /*! Customized data variables. */
+ struct {
+ /*! Length of default data. */
+ size_t dflt_len;
+ /*! Default data. */
+ uint8_t const *dflt;
+ /*! Text to binary transformation function. */
+ int (*to_bin)(YP_TXT_BIN_PARAMS);
+ /*! Binary to text transformation function. */
+ int (*to_txt)(YP_BIN_TXT_PARAMS);
+ } d;
+ /*! Reference variables. */
+ struct {
+ /*! Referenced section name. */
+ yp_name_t const *ref_name;
+ /*! Referenced group section name (optional). */
+ yp_name_t const *grp_ref_name;
+ /*! Referenced section item (dynamic value). */
+ yp_item_t const *ref;
+ /*! Referenced group section item (dynamic value). */
+ yp_item_t const *grp_ref;
+ } r;
+ /*! Group variables. */
+ struct {
+ /*! List of sub-items. */
+ yp_item_t const *sub_items;
+ /*! ID item of sub-items (dynamic value). */
+ yp_item_t const *id;
+ } g;
+} yp_var_t;
+
+/*! Schema item specification. */
+struct yp_item {
+ /*! Item name. */
+ const yp_name_t *name;
+ /*! Item type. */
+ yp_type_t type;
+ /*! Item parameters. */
+ yp_var_t var;
+ /*! Item flags. */
+ yp_flag_t flags;
+ /*! Arbitrary data/callbacks. */
+ const void *misc[YP_MAX_MISC_COUNT];
+ /*! Parent item. */
+ yp_item_t *parent;
+ /*! Item group subitems (name=NULL terminated array). */
+ yp_item_t *sub_items;
+};
+
+typedef struct yp_node yp_node_t;
+struct yp_node {
+ /*! Parent node. */
+ yp_node_t *parent;
+ /*! Node item descriptor. */
+ const yp_item_t *item;
+ /*! Current binary id length. */
+ size_t id_len;
+ /*! Current binary id. */
+ uint8_t id[YP_MAX_ID_LEN];
+ /*! Current item data length. */
+ size_t data_len;
+ /*! Current item data. */
+ uint8_t data[YP_MAX_DATA_LEN];
+};
+
+/*! Context parameters for check operations. */
+typedef struct {
+ /*! Used schema. */
+ yp_item_t **schema;
+ /*! Index of the current node. */
+ size_t current;
+ /*! Node stack. */
+ yp_node_t nodes[YP_MAX_NODE_DEPTH];
+} yp_check_ctx_t;
+
+/*!
+ * Copies the schema and reinitializes dynamic parameters.
+ *
+ * \param[out] dst New copy of the schema.
+ * \param[in] src Source schema.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_copy(
+ yp_item_t **dst,
+ const yp_item_t *src
+);
+
+/*!
+ * Merges two schemas.
+ *
+ * \param[out] dst Merged schema.
+ * \param[in] src1 Source schema1.
+ * \param[in] src2 Source schema2.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_merge(
+ yp_item_t **dst,
+ const yp_item_t *src1,
+ const yp_item_t *src2
+);
+
+/*!
+ * Purges dynamic items from the schema.
+ *
+ * \param[in] schema Schema to purge.
+ */
+void yp_schema_purge_dynamic(
+ yp_item_t *schema
+);
+
+/*!
+ * Deallocates the schema.
+ *
+ * \param[in] schema A schema returned by #yp_schema_copy().
+ */
+void yp_schema_free(
+ yp_item_t *schema
+);
+
+/*!
+ * Tries to find given parent_name/name in the schema.
+ *
+ * \param[in] name Name of the item.
+ * \param[in] parent_name Name of the parent item (NULL if no parent).
+ * \param[in] schema Schema.
+ *
+ * \return Item, NULL if not found or error.
+ */
+const yp_item_t* yp_schema_find(
+ const yp_name_t *name,
+ const yp_name_t *parent_name,
+ const yp_item_t *schema
+);
+
+/*!
+ * Prepares a context for item check against the schema.
+ *
+ * \param[in] schema Schema.
+ *
+ * \return Context, NULL if error.
+ */
+yp_check_ctx_t* yp_schema_check_init(
+ yp_item_t **schema
+);
+
+/*!
+ * Checks the current parser output against the schema.
+ *
+ * If the item is correct, context also contains binary value of the item.
+ *
+ * \param[in,out] ctx Check context.
+ * \param[in] parser Parser context.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_check_parser(
+ yp_check_ctx_t *ctx,
+ const yp_parser_t *parser
+);
+
+/*!
+ * Checks the string data against the schema.
+ *
+ * Description: key0[id].key1 data
+ *
+ * If the item is correct, context also contains binary value of the item.
+ *
+ * \param[in,out] ctx Check context.
+ * \param[in] key0 Key0 item name.
+ * \param[in] key1 Key1 item name.
+ * \param[in] id Item identifier.
+ * \param[in] data Item data (NULL means no data provided).
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_schema_check_str(
+ yp_check_ctx_t *ctx,
+ const char *key0,
+ const char *key1,
+ const char *id,
+ const char *data
+);
+
+/*!
+ * Deallocates the context.
+ *
+ * \param[in,out] ctx Check context.
+ */
+void yp_schema_check_deinit(
+ yp_check_ctx_t *ctx
+);
+
+/*! @} */
diff --git a/src/libknot/yparser/yptrafo.c b/src/libknot/yparser/yptrafo.c
new file mode 100644
index 0000000..7b040d9
--- /dev/null
+++ b/src/libknot/yparser/yptrafo.c
@@ -0,0 +1,1099 @@
+/* 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 <arpa/inet.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/attribute.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "contrib/base64.h"
+#include "contrib/ctype.h"
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/sockaddr.h"
+#include "contrib/wire_ctx.h"
+
+enum {
+ UNIT_BYTE = 'B',
+ UNIT_KILO = 'K',
+ UNIT_MEGA = 'M',
+ UNIT_GIGA = 'G',
+ UNIT_SEC = 's',
+ UNIT_MIN = 'm',
+ UNIT_HOUR = 'h',
+ UNIT_DAY = 'd'
+};
+
+enum {
+ MULTI_BYTE = 1,
+ MULTI_KILO = 1024,
+ MULTI_MEGA = 1024 * 1024,
+ MULTI_GIGA = 1024 * 1024 * 1024,
+ MULTI_SEC = 1,
+ MULTI_MIN = 60,
+ MULTI_HOUR = 3600,
+ MULTI_DAY = 24 * 3600
+};
+
+static wire_ctx_t copy_in(
+ wire_ctx_t *in,
+ size_t in_len,
+ char *buf,
+ size_t buf_len)
+{
+ wire_ctx_t ctx = wire_ctx_init((uint8_t *)buf, buf_len);
+ wire_ctx_write(&ctx, in->position, in_len);
+ wire_ctx_skip(in, in_len);
+ // Write the terminator.
+ wire_ctx_write_u8(&ctx, '\0');
+ wire_ctx_skip(&ctx, -1);
+ return ctx;
+}
+
+_public_
+int yp_str_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ wire_ctx_write(out, in->position, YP_LEN);
+ wire_ctx_skip(in, YP_LEN);
+ // Write string terminator.
+ wire_ctx_write_u8(out, '\0');
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_str_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ size_t len = strlen((char *)in->position) + 1;
+
+ wire_ctx_write(out, in->position, len);
+ wire_ctx_skip(in, len);
+ // Set the terminator as a current position.
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_bool_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ if (strncasecmp((char *)in->position, "on", YP_LEN) == 0 ||
+ strncasecmp((char *)in->position, "true", YP_LEN) == 0) {
+ wire_ctx_write_u8(out, 1);
+ } else if (strncasecmp((char *)in->position, "off", YP_LEN) == 0 ||
+ strncasecmp((char *)in->position, "false", YP_LEN) == 0) {
+ wire_ctx_write_u8(out, 0);
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_skip(in, YP_LEN);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_bool_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ const char *value;
+
+ switch (wire_ctx_read_u8(in)) {
+ case 0:
+ value = "off";
+ break;
+ case 1:
+ value = "on";
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ int ret = snprintf((char *)out->position, wire_ctx_available(out), "%s",
+ value);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+static int remove_unit(
+ int64_t *number,
+ char unit,
+ yp_style_t style)
+{
+ int64_t multiplier = 1;
+
+ // Get the multiplier for the unit.
+ if (style & YP_SSIZE) {
+ switch (unit) {
+ case UNIT_BYTE:
+ multiplier = MULTI_BYTE;
+ break;
+ case UNIT_KILO:
+ multiplier = MULTI_KILO;
+ break;
+ case UNIT_MEGA:
+ multiplier = MULTI_MEGA;
+ break;
+ case UNIT_GIGA:
+ multiplier = MULTI_GIGA;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ } else if (style & YP_STIME) {
+ switch (unit) {
+ case UNIT_SEC:
+ multiplier = MULTI_SEC;
+ break;
+ case UNIT_MIN:
+ multiplier = MULTI_MIN;
+ break;
+ case UNIT_HOUR:
+ multiplier = MULTI_HOUR;
+ break;
+ case UNIT_DAY:
+ multiplier = MULTI_DAY;
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ // Check for possible number overflow.
+ if (INT64_MAX / multiplier < (*number >= 0 ? *number : -*number)) {
+ return KNOT_ERANGE;
+ }
+
+ *number *= multiplier;
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_int_to_bin(
+ YP_TXT_BIN_PARAMS,
+ int64_t min,
+ int64_t max,
+ yp_style_t style)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Copy input string to the buffer to limit strtoll overread.
+ char buf[32];
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+
+ // Parse the number.
+ char *end;
+ errno = 0;
+ int64_t number = strtoll(buf, &end, 10);
+
+ // Check for number overflow.
+ if (errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN)) {
+ return KNOT_ERANGE;
+ }
+ // Check if the whole string is invalid.
+ if ((errno != 0 && number == 0) || end == buf) {
+ return KNOT_EINVAL;
+ }
+ // Check the rest of the string for a unit.
+ if (*end != '\0') {
+ // Check just for one-char rest.
+ if (*(end + 1) != '\0') {
+ return KNOT_EINVAL;
+ }
+
+ // Try to apply a unit on the number.
+ int ret = remove_unit(&number, *end, style);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ // Check for the result number overflow.
+ if (number < min || number > max) {
+ return KNOT_ERANGE;
+ }
+
+ // Write the result.
+ wire_ctx_write_u64(out, number);
+
+ YP_CHECK_RET;
+}
+
+static void add_unit(
+ int64_t *number,
+ char *unit,
+ yp_style_t style)
+{
+ int64_t multiplier = 1;
+ char basic_unit = '\0';
+ char new_unit = '\0';
+
+ // Get the multiplier for the unit.
+ if (style & YP_SSIZE) {
+ basic_unit = UNIT_BYTE;
+
+ if (*number < MULTI_KILO) {
+ multiplier = MULTI_BYTE;
+ new_unit = UNIT_BYTE;
+ } else if (*number < MULTI_MEGA) {
+ multiplier = MULTI_KILO;
+ new_unit = UNIT_KILO;
+ } else if (*number < MULTI_GIGA) {
+ multiplier = MULTI_MEGA;
+ new_unit = UNIT_MEGA;
+ } else {
+ multiplier = MULTI_GIGA;
+ new_unit = UNIT_GIGA;
+ }
+ } else if (style & YP_STIME) {
+ basic_unit = UNIT_SEC;
+
+ if (*number < MULTI_MIN) {
+ multiplier = MULTI_SEC;
+ new_unit = UNIT_SEC;
+ } else if (*number < MULTI_HOUR) {
+ multiplier = MULTI_MIN;
+ new_unit = UNIT_MIN;
+ } else if (*number < MULTI_DAY) {
+ multiplier = MULTI_HOUR;
+ new_unit = UNIT_HOUR;
+ } else {
+ multiplier = MULTI_DAY;
+ new_unit = UNIT_DAY;
+ }
+ }
+
+ // Check for unit application without any remainder.
+ if ((*number % multiplier) == 0) {
+ *number /= multiplier;
+ *unit = new_unit;
+ } else {
+ *unit = basic_unit;
+ }
+}
+
+_public_
+int yp_int_to_txt(
+ YP_BIN_TXT_PARAMS,
+ yp_style_t style)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ char unit[2] = { '\0' };
+ int64_t number = wire_ctx_read_u64(in);
+ add_unit(&number, unit, style);
+
+ int ret = snprintf((char *)out->position, wire_ctx_available(out),
+ "%"PRId64"%s", number, unit);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+static uint8_t sock_type_guess(
+ const uint8_t *str,
+ size_t len)
+{
+ size_t dots = 0;
+ size_t semicolons = 0;
+ size_t digits = 0;
+
+ // Analyze the string.
+ for (size_t i = 0; i < len; i++) {
+ if (str[i] == '.') dots++;
+ else if (str[i] == ':') semicolons++;
+ else if (is_digit(str[i])) digits++;
+ }
+
+ // Guess socket type.
+ if (semicolons >= 1) {
+ return 6;
+ } else if (semicolons == 0 && dots == 3 && digits >= 3) {
+ return 4;
+ } else {
+ return 0;
+ }
+}
+
+_public_
+int yp_addr_noport_to_bin(
+ YP_TXT_BIN_PARAMS,
+ bool allow_unix)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ uint8_t type = sock_type_guess(in->position, YP_LEN);
+
+ // Copy address to the buffer to limit inet_pton overread.
+ char buf[INET6_ADDRSTRLEN];
+ if (type == 4 || type == 6) {
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+ }
+
+ // Write address type.
+ wire_ctx_write_u8(out, type);
+
+ // Write address as such.
+ if (type == 4 && inet_pton(AF_INET, buf, &addr4) == 1) {
+ wire_ctx_write(out, (uint8_t *)&(addr4.s_addr),
+ sizeof(addr4.s_addr));
+ } else if (type == 6 && inet_pton(AF_INET6, buf, &addr6) == 1) {
+ wire_ctx_write(out, (uint8_t *)&(addr6.s6_addr),
+ sizeof(addr6.s6_addr));
+ } else if (type == 0 && allow_unix) {
+ int ret = yp_str_to_bin(in, out, stop);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_noport_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ struct in_addr addr4;
+ struct in6_addr addr6;
+
+ int ret;
+
+ switch (wire_ctx_read_u8(in)) {
+ case 0:
+ ret = yp_str_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 4:
+ wire_ctx_read(in, &(addr4.s_addr), sizeof(addr4.s_addr));
+ if (knot_inet_ntop(AF_INET, &addr4, (char *)out->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_skip(out, strlen((char *)out->position));
+ break;
+ case 6:
+ wire_ctx_read(in, &(addr6.s6_addr), sizeof(addr6.s6_addr));
+ if (knot_inet_ntop(AF_INET6, &addr6, (char *)out->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+ wire_ctx_skip(out, strlen((char *)out->position));
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Check for address@port separator.
+ const uint8_t *pos = (uint8_t *)strrchr((char *)in->position, '@');
+ // Ignore out-of-bounds result.
+ if (pos >= stop) {
+ pos = NULL;
+ }
+
+ // Store address type position.
+ uint8_t *type = out->position;
+
+ // Write the address without a port.
+ int ret = yp_addr_noport_to_bin(in, out, pos, true);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (pos != NULL) {
+ if (*type == 0) {
+ // Rewrite string terminator.
+ wire_ctx_skip(out, -1);
+ // Append the rest (separator and port) as a string.
+ ret = yp_str_to_bin(in, out, stop);
+ } else {
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Write the port as a number.
+ ret = yp_int_to_bin(in, out, stop, 0, UINT16_MAX, YP_SNONE);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else if (*type == 4 || *type == 6) {
+ wire_ctx_write_u64(out, (uint64_t)-1);
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Store address type position.
+ uint8_t *type = in->position;
+
+ // Write address.
+ int ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Write port.
+ if (*type == 4 || *type == 6) {
+ int64_t port = wire_ctx_read_u64(in);
+
+ if (port >= 0) {
+ // Write separator.
+ wire_ctx_write_u8(out, '@');
+
+ // Write port.
+ wire_ctx_skip(in, -sizeof(uint64_t));
+ ret = yp_int_to_txt(in, out, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_range_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Format: 0 - single address, 1 - address prefix, 2 - address range.
+ uint8_t format = 0;
+
+ // Check for the "addr/mask" format.
+ const uint8_t *pos = (uint8_t *)strchr((char *)in->position, '/');
+ if (pos >= stop) {
+ pos = NULL;
+ }
+
+ if (pos != NULL) {
+ format = 1;
+ } else {
+ // Check for the "addr1-addr2" format.
+ pos = (uint8_t *)strchr((char *)in->position, '-');
+ if (pos >= stop) {
+ pos = NULL;
+ }
+ if (pos != NULL) {
+ format = 2;
+ }
+ }
+
+ // Store address1 type position.
+ uint8_t *type1 = out->position;
+
+ // Write the first address.
+ int ret = yp_addr_noport_to_bin(in, out, pos, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ wire_ctx_write_u8(out, format);
+
+ switch (format) {
+ case 1:
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Write the prefix length.
+ ret = yp_int_to_bin(in, out, stop, 0, (*type1 == 4) ? 32 : 128,
+ YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 2:
+ // Skip the separator.
+ wire_ctx_skip(in, sizeof(uint8_t));
+
+ // Store address2 type position.
+ uint8_t *type2 = out->position;
+
+ // Write the second address.
+ ret = yp_addr_noport_to_bin(in, out, stop, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Check for address mismatch.
+ if (*type1 != *type2) {
+ return KNOT_EINVAL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_addr_range_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Write the first address.
+ int ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ uint8_t format = wire_ctx_read_u8(in);
+
+ switch (format) {
+ case 1:
+ // Write the separator.
+ wire_ctx_write_u8(out, '/');
+
+ // Write the prefix length.
+ ret = yp_int_to_txt(in, out, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ case 2:
+ // Write the separator.
+ wire_ctx_write_u8(out, '-');
+
+ // Write the second address.
+ ret = yp_addr_noport_to_txt(in, out);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_option_to_bin(
+ YP_TXT_BIN_PARAMS,
+ const knot_lookup_t *opts)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ while (opts->name != NULL) {
+ if (YP_LEN == strlen(opts->name) &&
+ strncasecmp((char *)in->position, opts->name, YP_LEN) == 0) {
+ wire_ctx_write_u8(out, opts->id);
+ wire_ctx_skip(in, YP_LEN);
+ YP_CHECK_RET;
+ }
+ opts++;
+ }
+
+ return KNOT_EINVAL;
+}
+
+_public_
+int yp_option_to_txt(
+ YP_BIN_TXT_PARAMS,
+ const knot_lookup_t *opts)
+{
+ uint8_t id = wire_ctx_read_u8(in);
+
+ while (opts->name != NULL) {
+ if (id == opts->id) {
+ int ret = snprintf((char *)out->position,
+ wire_ctx_available(out), "%s",
+ opts->name);
+ if (ret <= 0 || ret >= wire_ctx_available(out)) {
+ return KNOT_ESPACE;
+ }
+ wire_ctx_skip(out, ret);
+ YP_CHECK_RET;
+ }
+ opts++;
+ }
+
+ return KNOT_EINVAL;
+}
+
+_public_
+int yp_dname_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Copy dname string to the buffer to limit dname_from_str overread.
+ knot_dname_txt_storage_t buf;
+ wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf));
+ if (buf_ctx.error != KNOT_EOK) {
+ return buf_ctx.error;
+ }
+
+ // Convert the dname.
+ if (knot_dname_from_str(out->position, buf, wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Check the result and count the length.
+ int ret = knot_dname_wire_check(out->position,
+ out->position + wire_ctx_available(out),
+ NULL);
+ if (ret <= 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Convert the result to lower case.
+ knot_dname_to_lower(out->position);
+
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_dname_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ if (knot_dname_to_str((char *)out->position, in->position,
+ wire_ctx_available(out)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_skip(out, strlen((char *)out->position));
+
+ YP_CHECK_RET;
+}
+
+static int hex_to_num(char hex) {
+ if (hex >= '0' && hex <= '9') return hex - '0';
+ if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10;
+ if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10;
+ return -1;
+}
+
+_public_
+int yp_hex_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Check for hex notation (leading "0x").
+ if (wire_ctx_available(in) >= 2 &&
+ in->position[0] == '0' && in->position[1] == 'x') {
+ wire_ctx_skip(in, 2);
+
+ if (YP_LEN % 2 != 0) {
+ return KNOT_EINVAL;
+ }
+
+ // Write data length.
+ wire_ctx_write_u16(out, YP_LEN / 2);
+
+ // Decode hex string.
+ while (YP_LEN > 0) {
+ uint8_t buf[2] = { 0 };
+ wire_ctx_read(in, buf, sizeof(buf));
+
+ if (!is_xdigit(buf[0]) ||
+ !is_xdigit(buf[1])) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_write_u8(out, 16 * hex_to_num(buf[0]) +
+ hex_to_num(buf[1]));
+ }
+ } else {
+ // Write data length.
+ wire_ctx_write_u16(out, YP_LEN);
+
+ // Write textual string (without terminator).
+ wire_ctx_write(out, in->position, YP_LEN);
+ wire_ctx_skip(in, YP_LEN);
+ }
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_hex_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ size_t len = wire_ctx_read_u16(in);
+
+ bool printable = true;
+
+ // Check for printable string.
+ for (size_t i = 0; i < len; i++) {
+ if (!is_print(in->position[i])) {
+ printable = false;
+ break;
+ }
+ }
+
+ if (printable) {
+ wire_ctx_write(out, in->position, len);
+ wire_ctx_skip(in, len);
+ } else {
+ const char *prefix = "0x";
+ const char *hex = "0123456789ABCDEF";
+
+ // Write hex prefix.
+ wire_ctx_write(out, (uint8_t *)prefix, strlen(prefix));
+
+ // Encode data to hex.
+ for (size_t i = 0; i < len; i++) {
+ uint8_t bin = wire_ctx_read_u8(in);
+ wire_ctx_write_u8(out, hex[bin / 16]);
+ wire_ctx_write_u8(out, hex[bin % 16]);
+ }
+ }
+
+ // Write the terminator.
+ wire_ctx_write_u8(out, '\0');
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_base64_to_bin(
+ YP_TXT_BIN_PARAMS)
+{
+ YP_CHECK_PARAMS_BIN;
+
+ // Reserve some space for data length.
+ wire_ctx_skip(out, sizeof(uint16_t));
+
+ int ret = knot_base64_decode(in->position, YP_LEN, out->position,
+ wire_ctx_available(out));
+ if (ret < 0) {
+ return ret;
+ }
+ wire_ctx_skip(in, YP_LEN);
+
+ // Write the data length.
+ wire_ctx_skip(out, -sizeof(uint16_t));
+ wire_ctx_write_u16(out, ret);
+ wire_ctx_skip(out, ret);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_base64_to_txt(
+ YP_BIN_TXT_PARAMS)
+{
+ YP_CHECK_PARAMS_TXT;
+
+ // Read the data length.
+ uint16_t len = wire_ctx_read_u16(in);
+
+ int ret = knot_base64_encode(in->position, len, out->position,
+ wire_ctx_available(out));
+ if (ret < 0) {
+ return ret;
+ }
+ wire_ctx_skip(out, ret);
+
+ // Write the terminator.
+ wire_ctx_write_u8(out, '\0');
+ wire_ctx_skip(out, -1);
+
+ YP_CHECK_RET;
+}
+
+_public_
+int yp_item_to_bin(
+ const yp_item_t *item,
+ const char *txt,
+ size_t txt_len,
+ uint8_t *bin,
+ size_t *bin_len)
+{
+ if (item == NULL || txt == NULL || bin == NULL || bin_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t in = wire_ctx_init_const((const uint8_t *)txt, txt_len);
+ wire_ctx_t out = wire_ctx_init(bin, *bin_len);
+
+ int ret;
+ size_t ref_len;
+
+ switch (item->type) {
+ case YP_TINT:
+ ret = yp_int_to_bin(&in, &out, NULL, item->var.i.min,
+ item->var.i.max, item->var.i.unit);
+ break;
+ case YP_TBOOL:
+ ret = yp_bool_to_bin(&in, &out, NULL);
+ break;
+ case YP_TOPT:
+ ret = yp_option_to_bin(&in, &out, NULL, item->var.o.opts);
+ break;
+ case YP_TSTR:
+ ret = yp_str_to_bin(&in, &out, NULL);
+ break;
+ case YP_TADDR:
+ ret = yp_addr_to_bin(&in, &out, NULL);
+ break;
+ case YP_TNET:
+ ret = yp_addr_range_to_bin(&in, &out, NULL);
+ break;
+ case YP_TDNAME:
+ ret = yp_dname_to_bin(&in, &out, NULL);
+ break;
+ case YP_THEX:
+ ret = yp_hex_to_bin(&in, &out, NULL);
+ break;
+ case YP_TB64:
+ ret = yp_base64_to_bin(&in, &out, NULL);
+ break;
+ case YP_TDATA:
+ ret = item->var.d.to_bin(&in, &out, NULL);
+ break;
+ case YP_TREF:
+ ref_len = wire_ctx_available(&out);
+ ret = yp_item_to_bin(item->var.r.ref->var.g.id,
+ (char *)in.position, wire_ctx_available(&in),
+ out.position, &ref_len);
+ wire_ctx_skip(&out, ref_len);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (in.error != KNOT_EOK) {
+ return in.error;
+ } else if (out.error != KNOT_EOK) {
+ return out.error;
+ }
+
+ *bin_len = wire_ctx_offset(&out);
+
+ return KNOT_EOK;
+}
+
+_public_
+int yp_item_to_txt(
+ const yp_item_t *item,
+ const uint8_t *bin,
+ size_t bin_len,
+ char *txt,
+ size_t *txt_len,
+ yp_style_t style)
+{
+ if (item == NULL || bin == NULL || txt == NULL || txt_len == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ wire_ctx_t in = wire_ctx_init_const(bin, bin_len);
+ wire_ctx_t out = wire_ctx_init((uint8_t *)txt, *txt_len);
+
+ // Write leading quote.
+ if (!(style & YP_SNOQUOTE)) {
+ wire_ctx_write_u8(&out, '"');
+ }
+
+ int ret;
+ size_t ref_len;
+
+ switch (item->type) {
+ case YP_TINT:
+ ret = yp_int_to_txt(&in, &out, item->var.i.unit & style);
+ break;
+ case YP_TBOOL:
+ ret = yp_bool_to_txt(&in, &out);
+ break;
+ case YP_TOPT:
+ ret = yp_option_to_txt(&in, &out, item->var.o.opts);
+ break;
+ case YP_TSTR:
+ ret = yp_str_to_txt(&in, &out);
+ break;
+ case YP_TADDR:
+ ret = yp_addr_to_txt(&in, &out);
+ break;
+ case YP_TNET:
+ ret = yp_addr_range_to_txt(&in, &out);
+ break;
+ case YP_TDNAME:
+ ret = yp_dname_to_txt(&in, &out);
+ break;
+ case YP_THEX:
+ ret = yp_hex_to_txt(&in, &out);
+ break;
+ case YP_TB64:
+ ret = yp_base64_to_txt(&in, &out);
+ break;
+ case YP_TDATA:
+ ret = item->var.d.to_txt(&in, &out);
+ break;
+ case YP_TREF:
+ ref_len = wire_ctx_available(&out);
+ ret = yp_item_to_txt(item->var.r.ref->var.g.id,
+ in.position, wire_ctx_available(&in),
+ (char *)out.position,
+ &ref_len, style | YP_SNOQUOTE);
+ wire_ctx_skip(&out, ref_len);
+ break;
+ default:
+ ret = KNOT_EOK;
+ }
+
+ // Write trailing quote.
+ if (!(style & YP_SNOQUOTE)) {
+ wire_ctx_write_u8(&out, '"');
+ }
+
+ // Write string terminator.
+ wire_ctx_write_u8(&out, '\0');
+ wire_ctx_skip(&out, -1);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ } else if (in.error != KNOT_EOK) {
+ return in.error;
+ } else if (out.error != KNOT_EOK) {
+ return out.error;
+ }
+
+ *txt_len = wire_ctx_offset(&out);
+
+ return KNOT_EOK;
+}
+
+_public_
+struct sockaddr_storage yp_addr_noport(
+ const uint8_t *data)
+{
+ struct sockaddr_storage ss = { AF_UNSPEC };
+
+ // Read address type.
+ uint8_t type = *data;
+ data += sizeof(type);
+
+ // Set address.
+ switch (type) {
+ case 0:
+ sockaddr_set(&ss, AF_UNIX, (char *)data, 0);
+ break;
+ case 4:
+ sockaddr_set_raw(&ss, AF_INET, data,
+ sizeof(((struct in_addr *)NULL)->s_addr));
+ break;
+ case 6:
+ sockaddr_set_raw(&ss, AF_INET6, data,
+ sizeof(((struct in6_addr *)NULL)->s6_addr));
+ break;
+ }
+
+ return ss;
+}
+
+_public_
+struct sockaddr_storage yp_addr(
+ const uint8_t *data,
+ bool *no_port)
+{
+ struct sockaddr_storage ss = yp_addr_noport(data);
+
+ size_t addr_len;
+
+ // Get binary address length.
+ switch (ss.ss_family) {
+ case AF_INET:
+ addr_len = sizeof(((struct in_addr *)NULL)->s_addr);
+ break;
+ case AF_INET6:
+ addr_len = sizeof(((struct in6_addr *)NULL)->s6_addr);
+ break;
+ default:
+ addr_len = 0;
+ *no_port = true;
+ }
+
+ if (addr_len > 0) {
+ int64_t port = knot_wire_read_u64(data + sizeof(uint8_t) + addr_len);
+ if (port >= 0) {
+ sockaddr_port_set(&ss, port);
+ *no_port = false;
+ } else {
+ *no_port = true;
+ }
+ }
+
+ return ss;
+}
diff --git a/src/libknot/yparser/yptrafo.h b/src/libknot/yparser/yptrafo.h
new file mode 100644
index 0000000..285ab98
--- /dev/null
+++ b/src/libknot/yparser/yptrafo.h
@@ -0,0 +1,311 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Value transformations for Yparser.
+ *
+ * \addtogroup yparser
+ * @{
+ */
+
+#pragma once
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/lookup.h"
+#include "libknot/wire.h"
+
+/*!
+ * Transforms textual item value to binary form.
+ *
+ * \param[in] item Schema item to transform.
+ * \param[in] txt Value to transform.
+ * \param[in] txt_len Value length.
+ * \param[out] bin Output buffer.
+ * \param[in, out] bin_len Output buffer length, output length.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_item_to_bin(
+ const yp_item_t *item,
+ const char *txt,
+ size_t txt_len,
+ uint8_t *bin,
+ size_t *bin_len
+);
+
+/*!
+ * Transforms binary item value to textual form.
+ *
+ * \param[in] item Schema item to transform.
+ * \param[in] bin Value to transform.
+ * \param[in] bin_len Value length.
+ * \param[out] txt Output buffer.
+ * \param[in, out] txt_len Output buffer length, output length.
+ * \param[in] style Value style.
+ *
+ * \return Error code, KNOT_EOK if success.
+ */
+int yp_item_to_txt(
+ const yp_item_t *item,
+ const uint8_t *bin,
+ size_t bin_len,
+ char *txt,
+ size_t *txt_len,
+ yp_style_t style
+);
+
+/*!
+ * Converts binary value to string pointer.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return String pointer.
+ */
+inline static const char* yp_str(
+ const uint8_t *data)
+{
+ return (const char *)data;
+}
+
+/*!
+ * Converts binary value to boolean value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Boolean value.
+ */
+inline static bool yp_bool(
+ const uint8_t *data)
+{
+ return (data[0] == 0) ? false : true;
+}
+
+/*!
+ * Converts binary value to integer value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Integer value.
+ */
+inline static int64_t yp_int(
+ const uint8_t *data)
+{
+ return (int64_t)knot_wire_read_u64(data);
+}
+
+/*!
+ * Converts binary value to address value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Address value.
+ */
+struct sockaddr_storage yp_addr_noport(
+ const uint8_t *data
+);
+
+/*!
+ * Converts binary value to address value with an optional port.
+ *
+ * \param[in] data Binary value to transform.
+ * \param[out] no_port No port indicator.
+ *
+ * \return Address value.
+ */
+struct sockaddr_storage yp_addr(
+ const uint8_t *data,
+ bool *no_port
+);
+
+/*!
+ * Converts binary value to option value.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Unsigned value.
+ */
+inline static unsigned yp_opt(
+ const uint8_t *data)
+{
+ return (unsigned)data[0];
+}
+
+/*!
+ * Converts binary value to dname pointer.
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Dname pointer.
+ */
+inline static const uint8_t* yp_dname(
+ const uint8_t *data)
+{
+ return data;
+}
+
+/*!
+ * Converts binary value to data pointer.
+ *
+ * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64).
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Data pointer.
+ */
+inline static const uint8_t* yp_bin(
+ const uint8_t *data)
+{
+ return data + 2;
+}
+
+/*!
+ * Gets binary value length.
+ *
+ * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64).
+ *
+ * \param[in] data Binary value to transform.
+ *
+ * \return Data length.
+ */
+inline static size_t yp_bin_len(
+ const uint8_t *data)
+{
+ return knot_wire_read_u16(data);
+}
+
+/*!
+ * \brief Helper macros for conversion functions.
+ */
+
+#define YP_CHECK_CTX \
+ if (in->error != KNOT_EOK) { \
+ return in->error; \
+ } else if (out->error != KNOT_EOK) { \
+ return out->error; \
+ } \
+
+#define YP_CHECK_STOP \
+ if (stop != NULL) { \
+ assert(stop <= in->position + wire_ctx_available(in)); \
+ } else { \
+ stop = in->position + wire_ctx_available(in); \
+ }
+
+#define YP_LEN (stop - in->position)
+
+#define YP_CHECK_PARAMS_BIN \
+ YP_CHECK_CTX YP_CHECK_STOP
+
+#define YP_CHECK_PARAMS_TXT \
+ YP_CHECK_CTX
+
+#define YP_CHECK_RET \
+ YP_CHECK_CTX return KNOT_EOK;
+
+/*!
+ * \brief Conversion functions for basic types.
+ */
+
+int yp_str_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_str_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_bool_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_bool_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_int_to_bin(
+ YP_TXT_BIN_PARAMS,
+ int64_t min,
+ int64_t max,
+ yp_style_t style
+);
+
+int yp_int_to_txt(
+ YP_BIN_TXT_PARAMS,
+ yp_style_t style
+);
+
+int yp_addr_noport_to_bin(
+ YP_TXT_BIN_PARAMS,
+ bool allow_unix
+);
+
+int yp_addr_noport_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_addr_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_addr_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_addr_range_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_addr_range_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_option_to_bin(
+ YP_TXT_BIN_PARAMS,
+ const struct knot_lookup *opts
+);
+
+int yp_option_to_txt(
+ YP_BIN_TXT_PARAMS,
+ const struct knot_lookup *opts
+);
+
+int yp_dname_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_dname_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_hex_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_hex_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+int yp_base64_to_bin(
+ YP_TXT_BIN_PARAMS
+);
+
+int yp_base64_to_txt(
+ YP_BIN_TXT_PARAMS
+);
+
+/*! @} */
diff --git a/src/libzscanner.pc.in b/src/libzscanner.pc.in
new file mode 100644
index 0000000..35a8dab
--- /dev/null
+++ b/src/libzscanner.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+soname=@libzscanner_SONAME@
+
+Name: libzscanner
+Description: Knot DNS Zone Parsing library
+URL: https://www.knot-dns.cz
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lzscanner
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/src/libzscanner/Makefile.inc b/src/libzscanner/Makefile.inc
new file mode 100644
index 0000000..36fb795
--- /dev/null
+++ b/src/libzscanner/Makefile.inc
@@ -0,0 +1,40 @@
+lib_LTLIBRARIES += libzscanner.la
+pkgconfig_DATA += libzscanner.pc
+
+libzscanner_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) ${fuzzer_CFLAGS}
+libzscanner_la_LDFLAGS = $(AM_LDFLAGS) $(libzscanner_VERSION_INFO) \
+ $(LDFLAG_EXCLUDE_LIBS) ${fuzzer_LDFLAGS}
+libzscanner_la_LIBADD = $(math_LIBS)
+
+EXTRA_DIST += \
+ libzscanner/scanner.rl \
+ libzscanner/scanner_body.rl \
+ libzscanner/scanner.c.g2 \
+ libzscanner/scanner.c.t0
+include_libzscannerdir = $(includedir)/libzscanner
+
+include_libzscanner_HEADERS = \
+ libzscanner/error.h \
+ libzscanner/scanner.h \
+ libzscanner/version.h
+
+libzscanner_la_SOURCES = \
+ libzscanner/error.c \
+ libzscanner/functions.h \
+ libzscanner/functions.c \
+ $(include_libzscanner_HEADERS)
+
+BUILT_SOURCES += libzscanner/scanner.c
+CLEANFILES += libzscanner/scanner.c
+
+nodist_libzscanner_la_SOURCES = \
+ libzscanner/scanner.c
+
+if FAST_PARSER
+libzscanner/scanner.c: libzscanner/scanner.c.g2
+ @cp $(srcdir)/$@.g2 $@
+ @echo "NOTE: Compilation of scanner.c can take several minutes!"
+else
+libzscanner/scanner.c: libzscanner/scanner.c.t0
+ @cp $(srcdir)/$@.t0 $@
+endif
diff --git a/src/libzscanner/error.c b/src/libzscanner/error.c
new file mode 100644
index 0000000..8e571f9
--- /dev/null
+++ b/src/libzscanner/error.c
@@ -0,0 +1,202 @@
+/* Copyright (C) 2022 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 <stdlib.h>
+
+#include "libzscanner/error.h"
+
+typedef struct {
+ int code;
+ const char *text;
+ const char *code_name;
+} err_table_t;
+
+#define ERR_ITEM(code, text) { code, text, #code }
+
+static const err_table_t err_msgs[] = {
+ ERR_ITEM( ZS_OK,
+ "ok" ),
+ ERR_ITEM( ZS_EINVAL,
+ "invalid parameter" ),
+ ERR_ITEM( ZS_ENOMEM,
+ "not enough memory" ),
+ ERR_ITEM( ZS_FILE_OPEN,
+ "file open error" ),
+ ERR_ITEM( ZS_FILE_INVALID,
+ "invalid file" ),
+ ERR_ITEM( ZS_DOS_NEWLINE,
+ "unsupported CRLF newline, remove CR bytes" ),
+ ERR_ITEM( ZS_UNCOVERED_STATE,
+ "general scanner error" ),
+ ERR_ITEM( ZS_UNCLOSED_MULTILINE,
+ "unclosed last multiline block" ),
+ ERR_ITEM( ZS_LEFT_PARENTHESIS,
+ "too many left parentheses" ),
+ ERR_ITEM( ZS_RIGHT_PARENTHESIS,
+ "too many right parentheses" ),
+ ERR_ITEM( ZS_UNSUPPORTED_TYPE,
+ "unsupported record type" ),
+ ERR_ITEM( ZS_BAD_PREVIOUS_OWNER,
+ "previous owner is invalid" ),
+ ERR_ITEM( ZS_BAD_DNAME_CHAR,
+ "invalid domain name character" ),
+ ERR_ITEM( ZS_BAD_OWNER,
+ "owner is invalid" ),
+ ERR_ITEM( ZS_LABEL_OVERFLOW,
+ "maximal domain name label length has exceeded" ),
+ ERR_ITEM( ZS_DNAME_OVERFLOW,
+ "maximal domain name length has exceeded" ),
+ ERR_ITEM( ZS_BAD_NUMBER,
+ "invalid number" ),
+ ERR_ITEM( ZS_NUMBER64_OVERFLOW,
+ "number is too big" ),
+ ERR_ITEM( ZS_NUMBER32_OVERFLOW,
+ "number is bigger than 32 bits" ),
+ ERR_ITEM( ZS_NUMBER16_OVERFLOW,
+ "number is bigger than 16 bits" ),
+ ERR_ITEM( ZS_NUMBER8_OVERFLOW,
+ "number is bigger than 8 bits" ),
+ ERR_ITEM( ZS_FLOAT_OVERFLOW,
+ "float number overflow" ),
+ ERR_ITEM( ZS_RDATA_OVERFLOW,
+ "maximal record data length has exceeded" ),
+ ERR_ITEM( ZS_ITEM_OVERFLOW,
+ "maximal item length has exceeded" ),
+ ERR_ITEM( ZS_BAD_ADDRESS_CHAR,
+ "invalid address character" ),
+ ERR_ITEM( ZS_BAD_IPV4,
+ "invalid IPv4 address" ),
+ ERR_ITEM( ZS_BAD_IPV6,
+ "invalid IPv6 address" ),
+ ERR_ITEM( ZS_BAD_GATEWAY,
+ "invalid gateway" ),
+ ERR_ITEM( ZS_BAD_GATEWAY_KEY,
+ "invalid gateway key" ),
+ ERR_ITEM( ZS_BAD_APL,
+ "invalid address prefix list" ),
+ ERR_ITEM( ZS_BAD_RDATA,
+ "invalid record data" ),
+ ERR_ITEM( ZS_BAD_HEX_RDATA,
+ "invalid record data in hex format" ),
+ ERR_ITEM( ZS_BAD_HEX_CHAR,
+ "invalid hexadecimal character" ),
+ ERR_ITEM( ZS_BAD_BASE64_CHAR,
+ "invalid Base64 character" ),
+ ERR_ITEM( ZS_BAD_BASE32HEX_CHAR,
+ "invalid Base32hex character" ),
+ ERR_ITEM( ZS_BAD_REST,
+ "unexpected data" ),
+ ERR_ITEM( ZS_BAD_TIMESTAMP_CHAR,
+ "invalid timestamp character" ),
+ ERR_ITEM( ZS_BAD_TIMESTAMP_LENGTH,
+ "invalid timestamp length" ),
+ ERR_ITEM( ZS_BAD_TIMESTAMP,
+ "invalid timestamp" ),
+ ERR_ITEM( ZS_BAD_DATE,
+ "invalid date" ),
+ ERR_ITEM( ZS_BAD_TIME,
+ "invalid time" ),
+ ERR_ITEM( ZS_BAD_TIME_UNIT,
+ "invalid time unit" ),
+ ERR_ITEM( ZS_BAD_BITMAP,
+ "invalid bitmap" ),
+ ERR_ITEM( ZS_TEXT_OVERFLOW,
+ "text is too long" ),
+ ERR_ITEM( ZS_BAD_TEXT_CHAR,
+ "invalid text character" ),
+ ERR_ITEM( ZS_BAD_TEXT,
+ "invalid text string" ),
+ ERR_ITEM( ZS_BAD_DIRECTIVE,
+ "invalid directive" ),
+ ERR_ITEM( ZS_BAD_TTL,
+ "invalid zone TTL" ),
+ ERR_ITEM( ZS_BAD_ORIGIN,
+ "invalid FQDN zone origin" ),
+ ERR_ITEM( ZS_BAD_INCLUDE_FILENAME,
+ "invalid filename in include directive" ),
+ ERR_ITEM( ZS_BAD_INCLUDE_ORIGIN,
+ "invalid origin in include directive" ),
+ ERR_ITEM( ZS_UNPROCESSED_INCLUDE,
+ "include file processing error" ),
+ ERR_ITEM( ZS_UNOPENED_INCLUDE,
+ "include file opening error" ),
+ ERR_ITEM( ZS_BAD_RDATA_LENGTH,
+ "the rdata length statement is incorrect" ),
+ ERR_ITEM( ZS_CANNOT_TEXT_DATA,
+ "unable to process text form for this type" ),
+ ERR_ITEM( ZS_BAD_LOC_DATA,
+ "invalid zone location data" ),
+ ERR_ITEM( ZS_UNKNOWN_BLOCK,
+ "unknown rdata block" ),
+ ERR_ITEM( ZS_BAD_ALGORITHM,
+ "invalid algorithm" ),
+ ERR_ITEM( ZS_BAD_CERT_TYPE,
+ "invalid certificate type" ),
+ ERR_ITEM( ZS_BAD_EUI_LENGTH,
+ "invalid EUI length" ),
+ ERR_ITEM( ZS_BAD_L64_LENGTH,
+ "invalid 64-bit locator" ),
+ ERR_ITEM( ZS_BAD_CHAR_COLON,
+ "missing colon character" ),
+ ERR_ITEM( ZS_BAD_CHAR_DASH,
+ "missing dash character" ),
+ ERR_ITEM( ZS_DUPLICATE_SVCB_KEY,
+ "duplicate parameter name" ),
+ ERR_ITEM( ZS_BAD_SVCB_PARAM,
+ "invalid parameter" ),
+ ERR_ITEM( ZS_BAD_SVCB_MANDATORY,
+ "invalid mandatory value" ),
+ ERR_ITEM( ZS_DUPLICATE_SVCB_MANDATORY,
+ "duplicate mandatory value" ),
+ ERR_ITEM( ZS_MISSING_SVCB_MANDATORY,
+ "missing mandatory parameter" ),
+ ERR_ITEM( ZS_EMPTY_LIST_ITEM,
+ "empty comma-separated list item" ),
+ ERR_ITEM( ZS_FILE_ACCESS,
+ "permission denied" ),
+
+ ERR_ITEM( 0, NULL ) // Terminator
+};
+
+__attribute__((visibility("default")))
+const char* zs_strerror(const int code)
+{
+ const err_table_t *err = err_msgs;
+
+ while (err->text != NULL) {
+ if (err->code == code) {
+ return err->text;
+ }
+ err++;
+ }
+
+ return NULL;
+}
+
+__attribute__((visibility("default")))
+const char* zs_errorname(const int code)
+{
+ const err_table_t *err = err_msgs;
+
+ while (err->text != NULL) {
+ if (err->code == code) {
+ return err->code_name;
+ }
+ err++;
+ }
+
+ return NULL;
+}
diff --git a/src/libzscanner/error.h b/src/libzscanner/error.h
new file mode 100644
index 0000000..f54a750
--- /dev/null
+++ b/src/libzscanner/error.h
@@ -0,0 +1,119 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Error codes and handling.
+ *
+ * \addtogroup zone_scanner
+ * @{
+ */
+
+#pragma once
+
+enum err_codes {
+ ZS_OK = 0,
+ ZS_EINVAL = -1000,
+ ZS_ENOMEM,
+ ZS_FILE_OPEN,
+ ZS_FILE_INVALID,
+ ZS_DOS_NEWLINE,
+ ZS_UNCOVERED_STATE,
+ ZS_UNCLOSED_MULTILINE,
+ ZS_LEFT_PARENTHESIS,
+ ZS_RIGHT_PARENTHESIS,
+ ZS_UNSUPPORTED_TYPE,
+ ZS_BAD_PREVIOUS_OWNER,
+ ZS_BAD_DNAME_CHAR,
+ ZS_BAD_OWNER,
+ ZS_LABEL_OVERFLOW,
+ ZS_DNAME_OVERFLOW,
+ ZS_BAD_NUMBER,
+ ZS_NUMBER64_OVERFLOW,
+ ZS_NUMBER32_OVERFLOW,
+ ZS_NUMBER16_OVERFLOW,
+ ZS_NUMBER8_OVERFLOW,
+ ZS_FLOAT_OVERFLOW,
+ ZS_RDATA_OVERFLOW,
+ ZS_ITEM_OVERFLOW,
+ ZS_BAD_ADDRESS_CHAR,
+ ZS_BAD_IPV4,
+ ZS_BAD_IPV6,
+ ZS_BAD_GATEWAY,
+ ZS_BAD_GATEWAY_KEY,
+ ZS_BAD_APL,
+ ZS_BAD_RDATA,
+ ZS_BAD_HEX_RDATA,
+ ZS_BAD_HEX_CHAR,
+ ZS_BAD_BASE64_CHAR,
+ ZS_BAD_BASE32HEX_CHAR,
+ ZS_BAD_REST,
+ ZS_BAD_TIMESTAMP_CHAR,
+ ZS_BAD_TIMESTAMP_LENGTH,
+ ZS_BAD_TIMESTAMP,
+ ZS_BAD_DATE,
+ ZS_BAD_TIME,
+ ZS_BAD_TIME_UNIT,
+ ZS_BAD_BITMAP,
+ ZS_TEXT_OVERFLOW,
+ ZS_BAD_TEXT_CHAR,
+ ZS_BAD_TEXT,
+ ZS_BAD_DIRECTIVE,
+ ZS_BAD_TTL,
+ ZS_BAD_ORIGIN,
+ ZS_BAD_INCLUDE_FILENAME,
+ ZS_BAD_INCLUDE_ORIGIN,
+ ZS_UNPROCESSED_INCLUDE,
+ ZS_UNOPENED_INCLUDE,
+ ZS_BAD_RDATA_LENGTH,
+ ZS_CANNOT_TEXT_DATA,
+ ZS_BAD_LOC_DATA,
+ ZS_UNKNOWN_BLOCK,
+ ZS_BAD_ALGORITHM,
+ ZS_BAD_CERT_TYPE,
+ ZS_BAD_EUI_LENGTH,
+ ZS_BAD_L64_LENGTH,
+ ZS_BAD_CHAR_COLON,
+ ZS_BAD_CHAR_DASH,
+ ZS_DUPLICATE_SVCB_KEY,
+ ZS_BAD_SVCB_PARAM,
+ ZS_BAD_SVCB_MANDATORY,
+ ZS_DUPLICATE_SVCB_MANDATORY,
+ ZS_MISSING_SVCB_MANDATORY,
+ ZS_EMPTY_LIST_ITEM,
+ ZS_FILE_ACCESS,
+};
+
+/*!
+ * \brief Returns error message for the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error message.
+ */
+const char* zs_strerror(const int code);
+
+/*!
+ * \brief Returns error code name of the given error code.
+ *
+ * \param code Error code.
+ *
+ * \return String containing the error code name.
+ */
+const char* zs_errorname(const int code);
+
+/*! @} */
diff --git a/src/libzscanner/functions.c b/src/libzscanner/functions.c
new file mode 100644
index 0000000..f6a1c44
--- /dev/null
+++ b/src/libzscanner/functions.c
@@ -0,0 +1,980 @@
+/* Copyright (C) 2022 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libzscanner/error.h"
+#include "libzscanner/functions.h"
+#include "libknot/endian.h"
+
+const uint8_t digit_to_num[] = {
+ ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
+ ['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
+};
+
+/*
+ * Hex transformation:
+ * 1 2
+ * 12345678 12345678
+ * in: AAAA BBBB
+ * out: AAAABBBB
+ */
+
+const uint8_t first_hex_to_num[] = {
+ ['0'] = ( 0 * 16), ['6'] = ( 6 * 16), ['C'] = (12 * 16), ['b'] = (11 * 16),
+ ['1'] = ( 1 * 16), ['7'] = ( 7 * 16), ['D'] = (13 * 16), ['c'] = (12 * 16),
+ ['2'] = ( 2 * 16), ['8'] = ( 8 * 16), ['E'] = (14 * 16), ['d'] = (13 * 16),
+ ['3'] = ( 3 * 16), ['9'] = ( 9 * 16), ['F'] = (15 * 16), ['e'] = (14 * 16),
+ ['4'] = ( 4 * 16), ['A'] = (10 * 16), ['a'] = (10 * 16), ['f'] = (15 * 16),
+ ['5'] = ( 5 * 16), ['B'] = (11 * 16),
+};
+
+const uint8_t second_hex_to_num[] = {
+ ['0'] = 0, ['4'] = 4, ['8'] = 8, ['C'] = 12, ['a'] = 10, ['d'] = 13,
+ ['1'] = 1, ['5'] = 5, ['9'] = 9, ['D'] = 13, ['b'] = 11, ['e'] = 14,
+ ['2'] = 2, ['6'] = 6, ['A'] = 10, ['E'] = 14, ['c'] = 12, ['f'] = 15,
+ ['3'] = 3, ['7'] = 7, ['B'] = 11, ['F'] = 15,
+};
+
+/*
+ * Base64 transformation:
+ * 1 2 3 4
+ * 12345678 12345678 12345678 12345678
+ * in: 00AAAAAA 00BBBBBB 00CCCCCC 00DDDDDD
+ * out: AAAAAABB BBBBCCCC CCDDDDDD
+ */
+
+// 0x3F = 00111111
+const uint8_t first_base64_to_num[] = {
+ ['A'] = (( 0 & 0x3F) << 2), ['g'] = ((32 & 0x3F) << 2),
+ ['B'] = (( 1 & 0x3F) << 2), ['h'] = ((33 & 0x3F) << 2),
+ ['C'] = (( 2 & 0x3F) << 2), ['i'] = ((34 & 0x3F) << 2),
+ ['D'] = (( 3 & 0x3F) << 2), ['j'] = ((35 & 0x3F) << 2),
+ ['E'] = (( 4 & 0x3F) << 2), ['k'] = ((36 & 0x3F) << 2),
+ ['F'] = (( 5 & 0x3F) << 2), ['l'] = ((37 & 0x3F) << 2),
+ ['G'] = (( 6 & 0x3F) << 2), ['m'] = ((38 & 0x3F) << 2),
+ ['H'] = (( 7 & 0x3F) << 2), ['n'] = ((39 & 0x3F) << 2),
+ ['I'] = (( 8 & 0x3F) << 2), ['o'] = ((40 & 0x3F) << 2),
+ ['J'] = (( 9 & 0x3F) << 2), ['p'] = ((41 & 0x3F) << 2),
+ ['K'] = ((10 & 0x3F) << 2), ['q'] = ((42 & 0x3F) << 2),
+ ['L'] = ((11 & 0x3F) << 2), ['r'] = ((43 & 0x3F) << 2),
+ ['M'] = ((12 & 0x3F) << 2), ['s'] = ((44 & 0x3F) << 2),
+ ['N'] = ((13 & 0x3F) << 2), ['t'] = ((45 & 0x3F) << 2),
+ ['O'] = ((14 & 0x3F) << 2), ['u'] = ((46 & 0x3F) << 2),
+ ['P'] = ((15 & 0x3F) << 2), ['v'] = ((47 & 0x3F) << 2),
+ ['Q'] = ((16 & 0x3F) << 2), ['w'] = ((48 & 0x3F) << 2),
+ ['R'] = ((17 & 0x3F) << 2), ['x'] = ((49 & 0x3F) << 2),
+ ['S'] = ((18 & 0x3F) << 2), ['y'] = ((50 & 0x3F) << 2),
+ ['T'] = ((19 & 0x3F) << 2), ['z'] = ((51 & 0x3F) << 2),
+ ['U'] = ((20 & 0x3F) << 2), ['0'] = ((52 & 0x3F) << 2),
+ ['V'] = ((21 & 0x3F) << 2), ['1'] = ((53 & 0x3F) << 2),
+ ['W'] = ((22 & 0x3F) << 2), ['2'] = ((54 & 0x3F) << 2),
+ ['X'] = ((23 & 0x3F) << 2), ['3'] = ((55 & 0x3F) << 2),
+ ['Y'] = ((24 & 0x3F) << 2), ['4'] = ((56 & 0x3F) << 2),
+ ['Z'] = ((25 & 0x3F) << 2), ['5'] = ((57 & 0x3F) << 2),
+ ['a'] = ((26 & 0x3F) << 2), ['6'] = ((58 & 0x3F) << 2),
+ ['b'] = ((27 & 0x3F) << 2), ['7'] = ((59 & 0x3F) << 2),
+ ['c'] = ((28 & 0x3F) << 2), ['8'] = ((60 & 0x3F) << 2),
+ ['d'] = ((29 & 0x3F) << 2), ['9'] = ((61 & 0x3F) << 2),
+ ['e'] = ((30 & 0x3F) << 2), ['+'] = ((62 & 0x3F) << 2),
+ ['f'] = ((31 & 0x3F) << 2), ['/'] = ((63 & 0x3F) << 2),
+};
+
+// 0x30 = 00110000
+const uint8_t second_left_base64_to_num[] = {
+ ['A'] = (( 0 & 0x30) >> 4), ['g'] = ((32 & 0x30) >> 4),
+ ['B'] = (( 1 & 0x30) >> 4), ['h'] = ((33 & 0x30) >> 4),
+ ['C'] = (( 2 & 0x30) >> 4), ['i'] = ((34 & 0x30) >> 4),
+ ['D'] = (( 3 & 0x30) >> 4), ['j'] = ((35 & 0x30) >> 4),
+ ['E'] = (( 4 & 0x30) >> 4), ['k'] = ((36 & 0x30) >> 4),
+ ['F'] = (( 5 & 0x30) >> 4), ['l'] = ((37 & 0x30) >> 4),
+ ['G'] = (( 6 & 0x30) >> 4), ['m'] = ((38 & 0x30) >> 4),
+ ['H'] = (( 7 & 0x30) >> 4), ['n'] = ((39 & 0x30) >> 4),
+ ['I'] = (( 8 & 0x30) >> 4), ['o'] = ((40 & 0x30) >> 4),
+ ['J'] = (( 9 & 0x30) >> 4), ['p'] = ((41 & 0x30) >> 4),
+ ['K'] = ((10 & 0x30) >> 4), ['q'] = ((42 & 0x30) >> 4),
+ ['L'] = ((11 & 0x30) >> 4), ['r'] = ((43 & 0x30) >> 4),
+ ['M'] = ((12 & 0x30) >> 4), ['s'] = ((44 & 0x30) >> 4),
+ ['N'] = ((13 & 0x30) >> 4), ['t'] = ((45 & 0x30) >> 4),
+ ['O'] = ((14 & 0x30) >> 4), ['u'] = ((46 & 0x30) >> 4),
+ ['P'] = ((15 & 0x30) >> 4), ['v'] = ((47 & 0x30) >> 4),
+ ['Q'] = ((16 & 0x30) >> 4), ['w'] = ((48 & 0x30) >> 4),
+ ['R'] = ((17 & 0x30) >> 4), ['x'] = ((49 & 0x30) >> 4),
+ ['S'] = ((18 & 0x30) >> 4), ['y'] = ((50 & 0x30) >> 4),
+ ['T'] = ((19 & 0x30) >> 4), ['z'] = ((51 & 0x30) >> 4),
+ ['U'] = ((20 & 0x30) >> 4), ['0'] = ((52 & 0x30) >> 4),
+ ['V'] = ((21 & 0x30) >> 4), ['1'] = ((53 & 0x30) >> 4),
+ ['W'] = ((22 & 0x30) >> 4), ['2'] = ((54 & 0x30) >> 4),
+ ['X'] = ((23 & 0x30) >> 4), ['3'] = ((55 & 0x30) >> 4),
+ ['Y'] = ((24 & 0x30) >> 4), ['4'] = ((56 & 0x30) >> 4),
+ ['Z'] = ((25 & 0x30) >> 4), ['5'] = ((57 & 0x30) >> 4),
+ ['a'] = ((26 & 0x30) >> 4), ['6'] = ((58 & 0x30) >> 4),
+ ['b'] = ((27 & 0x30) >> 4), ['7'] = ((59 & 0x30) >> 4),
+ ['c'] = ((28 & 0x30) >> 4), ['8'] = ((60 & 0x30) >> 4),
+ ['d'] = ((29 & 0x30) >> 4), ['9'] = ((61 & 0x30) >> 4),
+ ['e'] = ((30 & 0x30) >> 4), ['+'] = ((62 & 0x30) >> 4),
+ ['f'] = ((31 & 0x30) >> 4), ['/'] = ((63 & 0x30) >> 4),
+};
+
+// 0x0F = 00001111
+const uint8_t second_right_base64_to_num[] = {
+ ['A'] = (( 0 & 0x0F) << 4), ['g'] = ((32 & 0x0F) << 4),
+ ['B'] = (( 1 & 0x0F) << 4), ['h'] = ((33 & 0x0F) << 4),
+ ['C'] = (( 2 & 0x0F) << 4), ['i'] = ((34 & 0x0F) << 4),
+ ['D'] = (( 3 & 0x0F) << 4), ['j'] = ((35 & 0x0F) << 4),
+ ['E'] = (( 4 & 0x0F) << 4), ['k'] = ((36 & 0x0F) << 4),
+ ['F'] = (( 5 & 0x0F) << 4), ['l'] = ((37 & 0x0F) << 4),
+ ['G'] = (( 6 & 0x0F) << 4), ['m'] = ((38 & 0x0F) << 4),
+ ['H'] = (( 7 & 0x0F) << 4), ['n'] = ((39 & 0x0F) << 4),
+ ['I'] = (( 8 & 0x0F) << 4), ['o'] = ((40 & 0x0F) << 4),
+ ['J'] = (( 9 & 0x0F) << 4), ['p'] = ((41 & 0x0F) << 4),
+ ['K'] = ((10 & 0x0F) << 4), ['q'] = ((42 & 0x0F) << 4),
+ ['L'] = ((11 & 0x0F) << 4), ['r'] = ((43 & 0x0F) << 4),
+ ['M'] = ((12 & 0x0F) << 4), ['s'] = ((44 & 0x0F) << 4),
+ ['N'] = ((13 & 0x0F) << 4), ['t'] = ((45 & 0x0F) << 4),
+ ['O'] = ((14 & 0x0F) << 4), ['u'] = ((46 & 0x0F) << 4),
+ ['P'] = ((15 & 0x0F) << 4), ['v'] = ((47 & 0x0F) << 4),
+ ['Q'] = ((16 & 0x0F) << 4), ['w'] = ((48 & 0x0F) << 4),
+ ['R'] = ((17 & 0x0F) << 4), ['x'] = ((49 & 0x0F) << 4),
+ ['S'] = ((18 & 0x0F) << 4), ['y'] = ((50 & 0x0F) << 4),
+ ['T'] = ((19 & 0x0F) << 4), ['z'] = ((51 & 0x0F) << 4),
+ ['U'] = ((20 & 0x0F) << 4), ['0'] = ((52 & 0x0F) << 4),
+ ['V'] = ((21 & 0x0F) << 4), ['1'] = ((53 & 0x0F) << 4),
+ ['W'] = ((22 & 0x0F) << 4), ['2'] = ((54 & 0x0F) << 4),
+ ['X'] = ((23 & 0x0F) << 4), ['3'] = ((55 & 0x0F) << 4),
+ ['Y'] = ((24 & 0x0F) << 4), ['4'] = ((56 & 0x0F) << 4),
+ ['Z'] = ((25 & 0x0F) << 4), ['5'] = ((57 & 0x0F) << 4),
+ ['a'] = ((26 & 0x0F) << 4), ['6'] = ((58 & 0x0F) << 4),
+ ['b'] = ((27 & 0x0F) << 4), ['7'] = ((59 & 0x0F) << 4),
+ ['c'] = ((28 & 0x0F) << 4), ['8'] = ((60 & 0x0F) << 4),
+ ['d'] = ((29 & 0x0F) << 4), ['9'] = ((61 & 0x0F) << 4),
+ ['e'] = ((30 & 0x0F) << 4), ['+'] = ((62 & 0x0F) << 4),
+ ['f'] = ((31 & 0x0F) << 4), ['/'] = ((63 & 0x0F) << 4),
+};
+
+// 0x3C = 00111100
+const uint8_t third_left_base64_to_num[] = {
+ ['A'] = (( 0 & 0x3C) >> 2), ['g'] = ((32 & 0x3C) >> 2),
+ ['B'] = (( 1 & 0x3C) >> 2), ['h'] = ((33 & 0x3C) >> 2),
+ ['C'] = (( 2 & 0x3C) >> 2), ['i'] = ((34 & 0x3C) >> 2),
+ ['D'] = (( 3 & 0x3C) >> 2), ['j'] = ((35 & 0x3C) >> 2),
+ ['E'] = (( 4 & 0x3C) >> 2), ['k'] = ((36 & 0x3C) >> 2),
+ ['F'] = (( 5 & 0x3C) >> 2), ['l'] = ((37 & 0x3C) >> 2),
+ ['G'] = (( 6 & 0x3C) >> 2), ['m'] = ((38 & 0x3C) >> 2),
+ ['H'] = (( 7 & 0x3C) >> 2), ['n'] = ((39 & 0x3C) >> 2),
+ ['I'] = (( 8 & 0x3C) >> 2), ['o'] = ((40 & 0x3C) >> 2),
+ ['J'] = (( 9 & 0x3C) >> 2), ['p'] = ((41 & 0x3C) >> 2),
+ ['K'] = ((10 & 0x3C) >> 2), ['q'] = ((42 & 0x3C) >> 2),
+ ['L'] = ((11 & 0x3C) >> 2), ['r'] = ((43 & 0x3C) >> 2),
+ ['M'] = ((12 & 0x3C) >> 2), ['s'] = ((44 & 0x3C) >> 2),
+ ['N'] = ((13 & 0x3C) >> 2), ['t'] = ((45 & 0x3C) >> 2),
+ ['O'] = ((14 & 0x3C) >> 2), ['u'] = ((46 & 0x3C) >> 2),
+ ['P'] = ((15 & 0x3C) >> 2), ['v'] = ((47 & 0x3C) >> 2),
+ ['Q'] = ((16 & 0x3C) >> 2), ['w'] = ((48 & 0x3C) >> 2),
+ ['R'] = ((17 & 0x3C) >> 2), ['x'] = ((49 & 0x3C) >> 2),
+ ['S'] = ((18 & 0x3C) >> 2), ['y'] = ((50 & 0x3C) >> 2),
+ ['T'] = ((19 & 0x3C) >> 2), ['z'] = ((51 & 0x3C) >> 2),
+ ['U'] = ((20 & 0x3C) >> 2), ['0'] = ((52 & 0x3C) >> 2),
+ ['V'] = ((21 & 0x3C) >> 2), ['1'] = ((53 & 0x3C) >> 2),
+ ['W'] = ((22 & 0x3C) >> 2), ['2'] = ((54 & 0x3C) >> 2),
+ ['X'] = ((23 & 0x3C) >> 2), ['3'] = ((55 & 0x3C) >> 2),
+ ['Y'] = ((24 & 0x3C) >> 2), ['4'] = ((56 & 0x3C) >> 2),
+ ['Z'] = ((25 & 0x3C) >> 2), ['5'] = ((57 & 0x3C) >> 2),
+ ['a'] = ((26 & 0x3C) >> 2), ['6'] = ((58 & 0x3C) >> 2),
+ ['b'] = ((27 & 0x3C) >> 2), ['7'] = ((59 & 0x3C) >> 2),
+ ['c'] = ((28 & 0x3C) >> 2), ['8'] = ((60 & 0x3C) >> 2),
+ ['d'] = ((29 & 0x3C) >> 2), ['9'] = ((61 & 0x3C) >> 2),
+ ['e'] = ((30 & 0x3C) >> 2), ['+'] = ((62 & 0x3C) >> 2),
+ ['f'] = ((31 & 0x3C) >> 2), ['/'] = ((63 & 0x3C) >> 2),
+};
+
+// 0x03 = 00000011
+const uint8_t third_right_base64_to_num[] = {
+ ['A'] = (( 0 & 0x03) << 6), ['g'] = ((32 & 0x03) << 6),
+ ['B'] = (( 1 & 0x03) << 6), ['h'] = ((33 & 0x03) << 6),
+ ['C'] = (( 2 & 0x03) << 6), ['i'] = ((34 & 0x03) << 6),
+ ['D'] = (( 3 & 0x03) << 6), ['j'] = ((35 & 0x03) << 6),
+ ['E'] = (( 4 & 0x03) << 6), ['k'] = ((36 & 0x03) << 6),
+ ['F'] = (( 5 & 0x03) << 6), ['l'] = ((37 & 0x03) << 6),
+ ['G'] = (( 6 & 0x03) << 6), ['m'] = ((38 & 0x03) << 6),
+ ['H'] = (( 7 & 0x03) << 6), ['n'] = ((39 & 0x03) << 6),
+ ['I'] = (( 8 & 0x03) << 6), ['o'] = ((40 & 0x03) << 6),
+ ['J'] = (( 9 & 0x03) << 6), ['p'] = ((41 & 0x03) << 6),
+ ['K'] = ((10 & 0x03) << 6), ['q'] = ((42 & 0x03) << 6),
+ ['L'] = ((11 & 0x03) << 6), ['r'] = ((43 & 0x03) << 6),
+ ['M'] = ((12 & 0x03) << 6), ['s'] = ((44 & 0x03) << 6),
+ ['N'] = ((13 & 0x03) << 6), ['t'] = ((45 & 0x03) << 6),
+ ['O'] = ((14 & 0x03) << 6), ['u'] = ((46 & 0x03) << 6),
+ ['P'] = ((15 & 0x03) << 6), ['v'] = ((47 & 0x03) << 6),
+ ['Q'] = ((16 & 0x03) << 6), ['w'] = ((48 & 0x03) << 6),
+ ['R'] = ((17 & 0x03) << 6), ['x'] = ((49 & 0x03) << 6),
+ ['S'] = ((18 & 0x03) << 6), ['y'] = ((50 & 0x03) << 6),
+ ['T'] = ((19 & 0x03) << 6), ['z'] = ((51 & 0x03) << 6),
+ ['U'] = ((20 & 0x03) << 6), ['0'] = ((52 & 0x03) << 6),
+ ['V'] = ((21 & 0x03) << 6), ['1'] = ((53 & 0x03) << 6),
+ ['W'] = ((22 & 0x03) << 6), ['2'] = ((54 & 0x03) << 6),
+ ['X'] = ((23 & 0x03) << 6), ['3'] = ((55 & 0x03) << 6),
+ ['Y'] = ((24 & 0x03) << 6), ['4'] = ((56 & 0x03) << 6),
+ ['Z'] = ((25 & 0x03) << 6), ['5'] = ((57 & 0x03) << 6),
+ ['a'] = ((26 & 0x03) << 6), ['6'] = ((58 & 0x03) << 6),
+ ['b'] = ((27 & 0x03) << 6), ['7'] = ((59 & 0x03) << 6),
+ ['c'] = ((28 & 0x03) << 6), ['8'] = ((60 & 0x03) << 6),
+ ['d'] = ((29 & 0x03) << 6), ['9'] = ((61 & 0x03) << 6),
+ ['e'] = ((30 & 0x03) << 6), ['+'] = ((62 & 0x03) << 6),
+ ['f'] = ((31 & 0x03) << 6), ['/'] = ((63 & 0x03) << 6),
+};
+
+// 0x3F = 00111111
+const uint8_t fourth_base64_to_num[] = {
+ ['A'] = (( 0 & 0x3F) << 0), ['g'] = ((32 & 0x3F) << 0),
+ ['B'] = (( 1 & 0x3F) << 0), ['h'] = ((33 & 0x3F) << 0),
+ ['C'] = (( 2 & 0x3F) << 0), ['i'] = ((34 & 0x3F) << 0),
+ ['D'] = (( 3 & 0x3F) << 0), ['j'] = ((35 & 0x3F) << 0),
+ ['E'] = (( 4 & 0x3F) << 0), ['k'] = ((36 & 0x3F) << 0),
+ ['F'] = (( 5 & 0x3F) << 0), ['l'] = ((37 & 0x3F) << 0),
+ ['G'] = (( 6 & 0x3F) << 0), ['m'] = ((38 & 0x3F) << 0),
+ ['H'] = (( 7 & 0x3F) << 0), ['n'] = ((39 & 0x3F) << 0),
+ ['I'] = (( 8 & 0x3F) << 0), ['o'] = ((40 & 0x3F) << 0),
+ ['J'] = (( 9 & 0x3F) << 0), ['p'] = ((41 & 0x3F) << 0),
+ ['K'] = ((10 & 0x3F) << 0), ['q'] = ((42 & 0x3F) << 0),
+ ['L'] = ((11 & 0x3F) << 0), ['r'] = ((43 & 0x3F) << 0),
+ ['M'] = ((12 & 0x3F) << 0), ['s'] = ((44 & 0x3F) << 0),
+ ['N'] = ((13 & 0x3F) << 0), ['t'] = ((45 & 0x3F) << 0),
+ ['O'] = ((14 & 0x3F) << 0), ['u'] = ((46 & 0x3F) << 0),
+ ['P'] = ((15 & 0x3F) << 0), ['v'] = ((47 & 0x3F) << 0),
+ ['Q'] = ((16 & 0x3F) << 0), ['w'] = ((48 & 0x3F) << 0),
+ ['R'] = ((17 & 0x3F) << 0), ['x'] = ((49 & 0x3F) << 0),
+ ['S'] = ((18 & 0x3F) << 0), ['y'] = ((50 & 0x3F) << 0),
+ ['T'] = ((19 & 0x3F) << 0), ['z'] = ((51 & 0x3F) << 0),
+ ['U'] = ((20 & 0x3F) << 0), ['0'] = ((52 & 0x3F) << 0),
+ ['V'] = ((21 & 0x3F) << 0), ['1'] = ((53 & 0x3F) << 0),
+ ['W'] = ((22 & 0x3F) << 0), ['2'] = ((54 & 0x3F) << 0),
+ ['X'] = ((23 & 0x3F) << 0), ['3'] = ((55 & 0x3F) << 0),
+ ['Y'] = ((24 & 0x3F) << 0), ['4'] = ((56 & 0x3F) << 0),
+ ['Z'] = ((25 & 0x3F) << 0), ['5'] = ((57 & 0x3F) << 0),
+ ['a'] = ((26 & 0x3F) << 0), ['6'] = ((58 & 0x3F) << 0),
+ ['b'] = ((27 & 0x3F) << 0), ['7'] = ((59 & 0x3F) << 0),
+ ['c'] = ((28 & 0x3F) << 0), ['8'] = ((60 & 0x3F) << 0),
+ ['d'] = ((29 & 0x3F) << 0), ['9'] = ((61 & 0x3F) << 0),
+ ['e'] = ((30 & 0x3F) << 0), ['+'] = ((62 & 0x3F) << 0),
+ ['f'] = ((31 & 0x3F) << 0), ['/'] = ((63 & 0x3F) << 0),
+};
+
+/*
+ * Base32hex transformation (with lower-case):
+ * 1 2 3 4 5 6 7 8
+ * 12345678 12345678 12345678 12345678 12345678 12345678 12345678 12345678
+ * in: 000AAAAA 000BBBBB 000CCCCC 000DDDDD 000EEEEE 000FFFFF 000GGGGG 000HHHHH
+ * out AAAAABBB BBCCCCCD DDDDEEEE EFFFFFGG GGGHHHHH
+ */
+
+// 0x1F = 00011111
+const uint8_t first_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1F) << 3), ['R'] = ((27 & 0x1F) << 3),
+ ['1'] = (( 1 & 0x1F) << 3), ['S'] = ((28 & 0x1F) << 3),
+ ['2'] = (( 2 & 0x1F) << 3), ['T'] = ((29 & 0x1F) << 3),
+ ['3'] = (( 3 & 0x1F) << 3), ['U'] = ((30 & 0x1F) << 3),
+ ['4'] = (( 4 & 0x1F) << 3), ['V'] = ((31 & 0x1F) << 3),
+ ['5'] = (( 5 & 0x1F) << 3), ['a'] = ((10 & 0x1F) << 3),
+ ['6'] = (( 6 & 0x1F) << 3), ['b'] = ((11 & 0x1F) << 3),
+ ['7'] = (( 7 & 0x1F) << 3), ['c'] = ((12 & 0x1F) << 3),
+ ['8'] = (( 8 & 0x1F) << 3), ['d'] = ((13 & 0x1F) << 3),
+ ['9'] = (( 9 & 0x1F) << 3), ['e'] = ((14 & 0x1F) << 3),
+ ['A'] = ((10 & 0x1F) << 3), ['f'] = ((15 & 0x1F) << 3),
+ ['B'] = ((11 & 0x1F) << 3), ['g'] = ((16 & 0x1F) << 3),
+ ['C'] = ((12 & 0x1F) << 3), ['h'] = ((17 & 0x1F) << 3),
+ ['D'] = ((13 & 0x1F) << 3), ['i'] = ((18 & 0x1F) << 3),
+ ['E'] = ((14 & 0x1F) << 3), ['j'] = ((19 & 0x1F) << 3),
+ ['F'] = ((15 & 0x1F) << 3), ['k'] = ((20 & 0x1F) << 3),
+ ['G'] = ((16 & 0x1F) << 3), ['l'] = ((21 & 0x1F) << 3),
+ ['H'] = ((17 & 0x1F) << 3), ['m'] = ((22 & 0x1F) << 3),
+ ['I'] = ((18 & 0x1F) << 3), ['n'] = ((23 & 0x1F) << 3),
+ ['J'] = ((19 & 0x1F) << 3), ['o'] = ((24 & 0x1F) << 3),
+ ['K'] = ((20 & 0x1F) << 3), ['p'] = ((25 & 0x1F) << 3),
+ ['L'] = ((21 & 0x1F) << 3), ['q'] = ((26 & 0x1F) << 3),
+ ['M'] = ((22 & 0x1F) << 3), ['r'] = ((27 & 0x1F) << 3),
+ ['N'] = ((23 & 0x1F) << 3), ['s'] = ((28 & 0x1F) << 3),
+ ['O'] = ((24 & 0x1F) << 3), ['t'] = ((29 & 0x1F) << 3),
+ ['P'] = ((25 & 0x1F) << 3), ['u'] = ((30 & 0x1F) << 3),
+ ['Q'] = ((26 & 0x1F) << 3), ['v'] = ((31 & 0x1F) << 3),
+};
+
+// 0x1C = 00011100
+const uint8_t second_left_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1C) >> 2), ['R'] = ((27 & 0x1C) >> 2),
+ ['1'] = (( 1 & 0x1C) >> 2), ['S'] = ((28 & 0x1C) >> 2),
+ ['2'] = (( 2 & 0x1C) >> 2), ['T'] = ((29 & 0x1C) >> 2),
+ ['3'] = (( 3 & 0x1C) >> 2), ['U'] = ((30 & 0x1C) >> 2),
+ ['4'] = (( 4 & 0x1C) >> 2), ['V'] = ((31 & 0x1C) >> 2),
+ ['5'] = (( 5 & 0x1C) >> 2), ['a'] = ((10 & 0x1C) >> 2),
+ ['6'] = (( 6 & 0x1C) >> 2), ['b'] = ((11 & 0x1C) >> 2),
+ ['7'] = (( 7 & 0x1C) >> 2), ['c'] = ((12 & 0x1C) >> 2),
+ ['8'] = (( 8 & 0x1C) >> 2), ['d'] = ((13 & 0x1C) >> 2),
+ ['9'] = (( 9 & 0x1C) >> 2), ['e'] = ((14 & 0x1C) >> 2),
+ ['A'] = ((10 & 0x1C) >> 2), ['f'] = ((15 & 0x1C) >> 2),
+ ['B'] = ((11 & 0x1C) >> 2), ['g'] = ((16 & 0x1C) >> 2),
+ ['C'] = ((12 & 0x1C) >> 2), ['h'] = ((17 & 0x1C) >> 2),
+ ['D'] = ((13 & 0x1C) >> 2), ['i'] = ((18 & 0x1C) >> 2),
+ ['E'] = ((14 & 0x1C) >> 2), ['j'] = ((19 & 0x1C) >> 2),
+ ['F'] = ((15 & 0x1C) >> 2), ['k'] = ((20 & 0x1C) >> 2),
+ ['G'] = ((16 & 0x1C) >> 2), ['l'] = ((21 & 0x1C) >> 2),
+ ['H'] = ((17 & 0x1C) >> 2), ['m'] = ((22 & 0x1C) >> 2),
+ ['I'] = ((18 & 0x1C) >> 2), ['n'] = ((23 & 0x1C) >> 2),
+ ['J'] = ((19 & 0x1C) >> 2), ['o'] = ((24 & 0x1C) >> 2),
+ ['K'] = ((20 & 0x1C) >> 2), ['p'] = ((25 & 0x1C) >> 2),
+ ['L'] = ((21 & 0x1C) >> 2), ['q'] = ((26 & 0x1C) >> 2),
+ ['M'] = ((22 & 0x1C) >> 2), ['r'] = ((27 & 0x1C) >> 2),
+ ['N'] = ((23 & 0x1C) >> 2), ['s'] = ((28 & 0x1C) >> 2),
+ ['O'] = ((24 & 0x1C) >> 2), ['t'] = ((29 & 0x1C) >> 2),
+ ['P'] = ((25 & 0x1C) >> 2), ['u'] = ((30 & 0x1C) >> 2),
+ ['Q'] = ((26 & 0x1C) >> 2), ['v'] = ((31 & 0x1C) >> 2),
+};
+
+// 0x03 = 00000011
+const uint8_t second_right_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x03) << 6), ['R'] = ((27 & 0x03) << 6),
+ ['1'] = (( 1 & 0x03) << 6), ['S'] = ((28 & 0x03) << 6),
+ ['2'] = (( 2 & 0x03) << 6), ['T'] = ((29 & 0x03) << 6),
+ ['3'] = (( 3 & 0x03) << 6), ['U'] = ((30 & 0x03) << 6),
+ ['4'] = (( 4 & 0x03) << 6), ['V'] = ((31 & 0x03) << 6),
+ ['5'] = (( 5 & 0x03) << 6), ['a'] = ((10 & 0x03) << 6),
+ ['6'] = (( 6 & 0x03) << 6), ['b'] = ((11 & 0x03) << 6),
+ ['7'] = (( 7 & 0x03) << 6), ['c'] = ((12 & 0x03) << 6),
+ ['8'] = (( 8 & 0x03) << 6), ['d'] = ((13 & 0x03) << 6),
+ ['9'] = (( 9 & 0x03) << 6), ['e'] = ((14 & 0x03) << 6),
+ ['A'] = ((10 & 0x03) << 6), ['f'] = ((15 & 0x03) << 6),
+ ['B'] = ((11 & 0x03) << 6), ['g'] = ((16 & 0x03) << 6),
+ ['C'] = ((12 & 0x03) << 6), ['h'] = ((17 & 0x03) << 6),
+ ['D'] = ((13 & 0x03) << 6), ['i'] = ((18 & 0x03) << 6),
+ ['E'] = ((14 & 0x03) << 6), ['j'] = ((19 & 0x03) << 6),
+ ['F'] = ((15 & 0x03) << 6), ['k'] = ((20 & 0x03) << 6),
+ ['G'] = ((16 & 0x03) << 6), ['l'] = ((21 & 0x03) << 6),
+ ['H'] = ((17 & 0x03) << 6), ['m'] = ((22 & 0x03) << 6),
+ ['I'] = ((18 & 0x03) << 6), ['n'] = ((23 & 0x03) << 6),
+ ['J'] = ((19 & 0x03) << 6), ['o'] = ((24 & 0x03) << 6),
+ ['K'] = ((20 & 0x03) << 6), ['p'] = ((25 & 0x03) << 6),
+ ['L'] = ((21 & 0x03) << 6), ['q'] = ((26 & 0x03) << 6),
+ ['M'] = ((22 & 0x03) << 6), ['r'] = ((27 & 0x03) << 6),
+ ['N'] = ((23 & 0x03) << 6), ['s'] = ((28 & 0x03) << 6),
+ ['O'] = ((24 & 0x03) << 6), ['t'] = ((29 & 0x03) << 6),
+ ['P'] = ((25 & 0x03) << 6), ['u'] = ((30 & 0x03) << 6),
+ ['Q'] = ((26 & 0x03) << 6), ['v'] = ((31 & 0x03) << 6),
+};
+
+// 0x1F = 00011111
+const uint8_t third_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1F) << 1), ['R'] = ((27 & 0x1F) << 1),
+ ['1'] = (( 1 & 0x1F) << 1), ['S'] = ((28 & 0x1F) << 1),
+ ['2'] = (( 2 & 0x1F) << 1), ['T'] = ((29 & 0x1F) << 1),
+ ['3'] = (( 3 & 0x1F) << 1), ['U'] = ((30 & 0x1F) << 1),
+ ['4'] = (( 4 & 0x1F) << 1), ['V'] = ((31 & 0x1F) << 1),
+ ['5'] = (( 5 & 0x1F) << 1), ['a'] = ((10 & 0x1F) << 1),
+ ['6'] = (( 6 & 0x1F) << 1), ['b'] = ((11 & 0x1F) << 1),
+ ['7'] = (( 7 & 0x1F) << 1), ['c'] = ((12 & 0x1F) << 1),
+ ['8'] = (( 8 & 0x1F) << 1), ['d'] = ((13 & 0x1F) << 1),
+ ['9'] = (( 9 & 0x1F) << 1), ['e'] = ((14 & 0x1F) << 1),
+ ['A'] = ((10 & 0x1F) << 1), ['f'] = ((15 & 0x1F) << 1),
+ ['B'] = ((11 & 0x1F) << 1), ['g'] = ((16 & 0x1F) << 1),
+ ['C'] = ((12 & 0x1F) << 1), ['h'] = ((17 & 0x1F) << 1),
+ ['D'] = ((13 & 0x1F) << 1), ['i'] = ((18 & 0x1F) << 1),
+ ['E'] = ((14 & 0x1F) << 1), ['j'] = ((19 & 0x1F) << 1),
+ ['F'] = ((15 & 0x1F) << 1), ['k'] = ((20 & 0x1F) << 1),
+ ['G'] = ((16 & 0x1F) << 1), ['l'] = ((21 & 0x1F) << 1),
+ ['H'] = ((17 & 0x1F) << 1), ['m'] = ((22 & 0x1F) << 1),
+ ['I'] = ((18 & 0x1F) << 1), ['n'] = ((23 & 0x1F) << 1),
+ ['J'] = ((19 & 0x1F) << 1), ['o'] = ((24 & 0x1F) << 1),
+ ['K'] = ((20 & 0x1F) << 1), ['p'] = ((25 & 0x1F) << 1),
+ ['L'] = ((21 & 0x1F) << 1), ['q'] = ((26 & 0x1F) << 1),
+ ['M'] = ((22 & 0x1F) << 1), ['r'] = ((27 & 0x1F) << 1),
+ ['N'] = ((23 & 0x1F) << 1), ['s'] = ((28 & 0x1F) << 1),
+ ['O'] = ((24 & 0x1F) << 1), ['t'] = ((29 & 0x1F) << 1),
+ ['P'] = ((25 & 0x1F) << 1), ['u'] = ((30 & 0x1F) << 1),
+ ['Q'] = ((26 & 0x1F) << 1), ['v'] = ((31 & 0x1F) << 1),
+};
+
+// 0x10 = 00010000
+const uint8_t fourth_left_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x10) >> 4), ['R'] = ((27 & 0x10) >> 4),
+ ['1'] = (( 1 & 0x10) >> 4), ['S'] = ((28 & 0x10) >> 4),
+ ['2'] = (( 2 & 0x10) >> 4), ['T'] = ((29 & 0x10) >> 4),
+ ['3'] = (( 3 & 0x10) >> 4), ['U'] = ((30 & 0x10) >> 4),
+ ['4'] = (( 4 & 0x10) >> 4), ['V'] = ((31 & 0x10) >> 4),
+ ['5'] = (( 5 & 0x10) >> 4), ['a'] = ((10 & 0x10) >> 4),
+ ['6'] = (( 6 & 0x10) >> 4), ['b'] = ((11 & 0x10) >> 4),
+ ['7'] = (( 7 & 0x10) >> 4), ['c'] = ((12 & 0x10) >> 4),
+ ['8'] = (( 8 & 0x10) >> 4), ['d'] = ((13 & 0x10) >> 4),
+ ['9'] = (( 9 & 0x10) >> 4), ['e'] = ((14 & 0x10) >> 4),
+ ['A'] = ((10 & 0x10) >> 4), ['f'] = ((15 & 0x10) >> 4),
+ ['B'] = ((11 & 0x10) >> 4), ['g'] = ((16 & 0x10) >> 4),
+ ['C'] = ((12 & 0x10) >> 4), ['h'] = ((17 & 0x10) >> 4),
+ ['D'] = ((13 & 0x10) >> 4), ['i'] = ((18 & 0x10) >> 4),
+ ['E'] = ((14 & 0x10) >> 4), ['j'] = ((19 & 0x10) >> 4),
+ ['F'] = ((15 & 0x10) >> 4), ['k'] = ((20 & 0x10) >> 4),
+ ['G'] = ((16 & 0x10) >> 4), ['l'] = ((21 & 0x10) >> 4),
+ ['H'] = ((17 & 0x10) >> 4), ['m'] = ((22 & 0x10) >> 4),
+ ['I'] = ((18 & 0x10) >> 4), ['n'] = ((23 & 0x10) >> 4),
+ ['J'] = ((19 & 0x10) >> 4), ['o'] = ((24 & 0x10) >> 4),
+ ['K'] = ((20 & 0x10) >> 4), ['p'] = ((25 & 0x10) >> 4),
+ ['L'] = ((21 & 0x10) >> 4), ['q'] = ((26 & 0x10) >> 4),
+ ['M'] = ((22 & 0x10) >> 4), ['r'] = ((27 & 0x10) >> 4),
+ ['N'] = ((23 & 0x10) >> 4), ['s'] = ((28 & 0x10) >> 4),
+ ['O'] = ((24 & 0x10) >> 4), ['t'] = ((29 & 0x10) >> 4),
+ ['P'] = ((25 & 0x10) >> 4), ['u'] = ((30 & 0x10) >> 4),
+ ['Q'] = ((26 & 0x10) >> 4), ['v'] = ((31 & 0x10) >> 4),
+};
+
+// 0x0F = 00001111
+const uint8_t fourth_right_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x0F) << 4), ['R'] = ((27 & 0x0F) << 4),
+ ['1'] = (( 1 & 0x0F) << 4), ['S'] = ((28 & 0x0F) << 4),
+ ['2'] = (( 2 & 0x0F) << 4), ['T'] = ((29 & 0x0F) << 4),
+ ['3'] = (( 3 & 0x0F) << 4), ['U'] = ((30 & 0x0F) << 4),
+ ['4'] = (( 4 & 0x0F) << 4), ['V'] = ((31 & 0x0F) << 4),
+ ['5'] = (( 5 & 0x0F) << 4), ['a'] = ((10 & 0x0F) << 4),
+ ['6'] = (( 6 & 0x0F) << 4), ['b'] = ((11 & 0x0F) << 4),
+ ['7'] = (( 7 & 0x0F) << 4), ['c'] = ((12 & 0x0F) << 4),
+ ['8'] = (( 8 & 0x0F) << 4), ['d'] = ((13 & 0x0F) << 4),
+ ['9'] = (( 9 & 0x0F) << 4), ['e'] = ((14 & 0x0F) << 4),
+ ['A'] = ((10 & 0x0F) << 4), ['f'] = ((15 & 0x0F) << 4),
+ ['B'] = ((11 & 0x0F) << 4), ['g'] = ((16 & 0x0F) << 4),
+ ['C'] = ((12 & 0x0F) << 4), ['h'] = ((17 & 0x0F) << 4),
+ ['D'] = ((13 & 0x0F) << 4), ['i'] = ((18 & 0x0F) << 4),
+ ['E'] = ((14 & 0x0F) << 4), ['j'] = ((19 & 0x0F) << 4),
+ ['F'] = ((15 & 0x0F) << 4), ['k'] = ((20 & 0x0F) << 4),
+ ['G'] = ((16 & 0x0F) << 4), ['l'] = ((21 & 0x0F) << 4),
+ ['H'] = ((17 & 0x0F) << 4), ['m'] = ((22 & 0x0F) << 4),
+ ['I'] = ((18 & 0x0F) << 4), ['n'] = ((23 & 0x0F) << 4),
+ ['J'] = ((19 & 0x0F) << 4), ['o'] = ((24 & 0x0F) << 4),
+ ['K'] = ((20 & 0x0F) << 4), ['p'] = ((25 & 0x0F) << 4),
+ ['L'] = ((21 & 0x0F) << 4), ['q'] = ((26 & 0x0F) << 4),
+ ['M'] = ((22 & 0x0F) << 4), ['r'] = ((27 & 0x0F) << 4),
+ ['N'] = ((23 & 0x0F) << 4), ['s'] = ((28 & 0x0F) << 4),
+ ['O'] = ((24 & 0x0F) << 4), ['t'] = ((29 & 0x0F) << 4),
+ ['P'] = ((25 & 0x0F) << 4), ['u'] = ((30 & 0x0F) << 4),
+ ['Q'] = ((26 & 0x0F) << 4), ['v'] = ((31 & 0x0F) << 4),
+};
+
+// 0x1E = 00011110
+const uint8_t fifth_left_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1E) >> 1), ['R'] = ((27 & 0x1E) >> 1),
+ ['1'] = (( 1 & 0x1E) >> 1), ['S'] = ((28 & 0x1E) >> 1),
+ ['2'] = (( 2 & 0x1E) >> 1), ['T'] = ((29 & 0x1E) >> 1),
+ ['3'] = (( 3 & 0x1E) >> 1), ['U'] = ((30 & 0x1E) >> 1),
+ ['4'] = (( 4 & 0x1E) >> 1), ['V'] = ((31 & 0x1E) >> 1),
+ ['5'] = (( 5 & 0x1E) >> 1), ['a'] = ((10 & 0x1E) >> 1),
+ ['6'] = (( 6 & 0x1E) >> 1), ['b'] = ((11 & 0x1E) >> 1),
+ ['7'] = (( 7 & 0x1E) >> 1), ['c'] = ((12 & 0x1E) >> 1),
+ ['8'] = (( 8 & 0x1E) >> 1), ['d'] = ((13 & 0x1E) >> 1),
+ ['9'] = (( 9 & 0x1E) >> 1), ['e'] = ((14 & 0x1E) >> 1),
+ ['A'] = ((10 & 0x1E) >> 1), ['f'] = ((15 & 0x1E) >> 1),
+ ['B'] = ((11 & 0x1E) >> 1), ['g'] = ((16 & 0x1E) >> 1),
+ ['C'] = ((12 & 0x1E) >> 1), ['h'] = ((17 & 0x1E) >> 1),
+ ['D'] = ((13 & 0x1E) >> 1), ['i'] = ((18 & 0x1E) >> 1),
+ ['E'] = ((14 & 0x1E) >> 1), ['j'] = ((19 & 0x1E) >> 1),
+ ['F'] = ((15 & 0x1E) >> 1), ['k'] = ((20 & 0x1E) >> 1),
+ ['G'] = ((16 & 0x1E) >> 1), ['l'] = ((21 & 0x1E) >> 1),
+ ['H'] = ((17 & 0x1E) >> 1), ['m'] = ((22 & 0x1E) >> 1),
+ ['I'] = ((18 & 0x1E) >> 1), ['n'] = ((23 & 0x1E) >> 1),
+ ['J'] = ((19 & 0x1E) >> 1), ['o'] = ((24 & 0x1E) >> 1),
+ ['K'] = ((20 & 0x1E) >> 1), ['p'] = ((25 & 0x1E) >> 1),
+ ['L'] = ((21 & 0x1E) >> 1), ['q'] = ((26 & 0x1E) >> 1),
+ ['M'] = ((22 & 0x1E) >> 1), ['r'] = ((27 & 0x1E) >> 1),
+ ['N'] = ((23 & 0x1E) >> 1), ['s'] = ((28 & 0x1E) >> 1),
+ ['O'] = ((24 & 0x1E) >> 1), ['t'] = ((29 & 0x1E) >> 1),
+ ['P'] = ((25 & 0x1E) >> 1), ['u'] = ((30 & 0x1E) >> 1),
+ ['Q'] = ((26 & 0x1E) >> 1), ['v'] = ((31 & 0x1E) >> 1),
+};
+
+// 0x01 = 00000001
+const uint8_t fifth_right_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x01) << 7), ['R'] = ((27 & 0x01) << 7),
+ ['1'] = (( 1 & 0x01) << 7), ['S'] = ((28 & 0x01) << 7),
+ ['2'] = (( 2 & 0x01) << 7), ['T'] = ((29 & 0x01) << 7),
+ ['3'] = (( 3 & 0x01) << 7), ['U'] = ((30 & 0x01) << 7),
+ ['4'] = (( 4 & 0x01) << 7), ['V'] = ((31 & 0x01) << 7),
+ ['5'] = (( 5 & 0x01) << 7), ['a'] = ((10 & 0x01) << 7),
+ ['6'] = (( 6 & 0x01) << 7), ['b'] = ((11 & 0x01) << 7),
+ ['7'] = (( 7 & 0x01) << 7), ['c'] = ((12 & 0x01) << 7),
+ ['8'] = (( 8 & 0x01) << 7), ['d'] = ((13 & 0x01) << 7),
+ ['9'] = (( 9 & 0x01) << 7), ['e'] = ((14 & 0x01) << 7),
+ ['A'] = ((10 & 0x01) << 7), ['f'] = ((15 & 0x01) << 7),
+ ['B'] = ((11 & 0x01) << 7), ['g'] = ((16 & 0x01) << 7),
+ ['C'] = ((12 & 0x01) << 7), ['h'] = ((17 & 0x01) << 7),
+ ['D'] = ((13 & 0x01) << 7), ['i'] = ((18 & 0x01) << 7),
+ ['E'] = ((14 & 0x01) << 7), ['j'] = ((19 & 0x01) << 7),
+ ['F'] = ((15 & 0x01) << 7), ['k'] = ((20 & 0x01) << 7),
+ ['G'] = ((16 & 0x01) << 7), ['l'] = ((21 & 0x01) << 7),
+ ['H'] = ((17 & 0x01) << 7), ['m'] = ((22 & 0x01) << 7),
+ ['I'] = ((18 & 0x01) << 7), ['n'] = ((23 & 0x01) << 7),
+ ['J'] = ((19 & 0x01) << 7), ['o'] = ((24 & 0x01) << 7),
+ ['K'] = ((20 & 0x01) << 7), ['p'] = ((25 & 0x01) << 7),
+ ['L'] = ((21 & 0x01) << 7), ['q'] = ((26 & 0x01) << 7),
+ ['M'] = ((22 & 0x01) << 7), ['r'] = ((27 & 0x01) << 7),
+ ['N'] = ((23 & 0x01) << 7), ['s'] = ((28 & 0x01) << 7),
+ ['O'] = ((24 & 0x01) << 7), ['t'] = ((29 & 0x01) << 7),
+ ['P'] = ((25 & 0x01) << 7), ['u'] = ((30 & 0x01) << 7),
+ ['Q'] = ((26 & 0x01) << 7), ['v'] = ((31 & 0x01) << 7),
+};
+
+// 0x1F = 00011111
+const uint8_t sixth_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1F) << 2), ['R'] = ((27 & 0x1F) << 2),
+ ['1'] = (( 1 & 0x1F) << 2), ['S'] = ((28 & 0x1F) << 2),
+ ['2'] = (( 2 & 0x1F) << 2), ['T'] = ((29 & 0x1F) << 2),
+ ['3'] = (( 3 & 0x1F) << 2), ['U'] = ((30 & 0x1F) << 2),
+ ['4'] = (( 4 & 0x1F) << 2), ['V'] = ((31 & 0x1F) << 2),
+ ['5'] = (( 5 & 0x1F) << 2), ['a'] = ((10 & 0x1F) << 2),
+ ['6'] = (( 6 & 0x1F) << 2), ['b'] = ((11 & 0x1F) << 2),
+ ['7'] = (( 7 & 0x1F) << 2), ['c'] = ((12 & 0x1F) << 2),
+ ['8'] = (( 8 & 0x1F) << 2), ['d'] = ((13 & 0x1F) << 2),
+ ['9'] = (( 9 & 0x1F) << 2), ['e'] = ((14 & 0x1F) << 2),
+ ['A'] = ((10 & 0x1F) << 2), ['f'] = ((15 & 0x1F) << 2),
+ ['B'] = ((11 & 0x1F) << 2), ['g'] = ((16 & 0x1F) << 2),
+ ['C'] = ((12 & 0x1F) << 2), ['h'] = ((17 & 0x1F) << 2),
+ ['D'] = ((13 & 0x1F) << 2), ['i'] = ((18 & 0x1F) << 2),
+ ['E'] = ((14 & 0x1F) << 2), ['j'] = ((19 & 0x1F) << 2),
+ ['F'] = ((15 & 0x1F) << 2), ['k'] = ((20 & 0x1F) << 2),
+ ['G'] = ((16 & 0x1F) << 2), ['l'] = ((21 & 0x1F) << 2),
+ ['H'] = ((17 & 0x1F) << 2), ['m'] = ((22 & 0x1F) << 2),
+ ['I'] = ((18 & 0x1F) << 2), ['n'] = ((23 & 0x1F) << 2),
+ ['J'] = ((19 & 0x1F) << 2), ['o'] = ((24 & 0x1F) << 2),
+ ['K'] = ((20 & 0x1F) << 2), ['p'] = ((25 & 0x1F) << 2),
+ ['L'] = ((21 & 0x1F) << 2), ['q'] = ((26 & 0x1F) << 2),
+ ['M'] = ((22 & 0x1F) << 2), ['r'] = ((27 & 0x1F) << 2),
+ ['N'] = ((23 & 0x1F) << 2), ['s'] = ((28 & 0x1F) << 2),
+ ['O'] = ((24 & 0x1F) << 2), ['t'] = ((29 & 0x1F) << 2),
+ ['P'] = ((25 & 0x1F) << 2), ['u'] = ((30 & 0x1F) << 2),
+ ['Q'] = ((26 & 0x1F) << 2), ['v'] = ((31 & 0x1F) << 2),
+};
+
+// 0x18 = 00011000
+const uint8_t seventh_left_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x18) >> 3), ['R'] = ((27 & 0x18) >> 3),
+ ['1'] = (( 1 & 0x18) >> 3), ['S'] = ((28 & 0x18) >> 3),
+ ['2'] = (( 2 & 0x18) >> 3), ['T'] = ((29 & 0x18) >> 3),
+ ['3'] = (( 3 & 0x18) >> 3), ['U'] = ((30 & 0x18) >> 3),
+ ['4'] = (( 4 & 0x18) >> 3), ['V'] = ((31 & 0x18) >> 3),
+ ['5'] = (( 5 & 0x18) >> 3), ['a'] = ((10 & 0x18) >> 3),
+ ['6'] = (( 6 & 0x18) >> 3), ['b'] = ((11 & 0x18) >> 3),
+ ['7'] = (( 7 & 0x18) >> 3), ['c'] = ((12 & 0x18) >> 3),
+ ['8'] = (( 8 & 0x18) >> 3), ['d'] = ((13 & 0x18) >> 3),
+ ['9'] = (( 9 & 0x18) >> 3), ['e'] = ((14 & 0x18) >> 3),
+ ['A'] = ((10 & 0x18) >> 3), ['f'] = ((15 & 0x18) >> 3),
+ ['B'] = ((11 & 0x18) >> 3), ['g'] = ((16 & 0x18) >> 3),
+ ['C'] = ((12 & 0x18) >> 3), ['h'] = ((17 & 0x18) >> 3),
+ ['D'] = ((13 & 0x18) >> 3), ['i'] = ((18 & 0x18) >> 3),
+ ['E'] = ((14 & 0x18) >> 3), ['j'] = ((19 & 0x18) >> 3),
+ ['F'] = ((15 & 0x18) >> 3), ['k'] = ((20 & 0x18) >> 3),
+ ['G'] = ((16 & 0x18) >> 3), ['l'] = ((21 & 0x18) >> 3),
+ ['H'] = ((17 & 0x18) >> 3), ['m'] = ((22 & 0x18) >> 3),
+ ['I'] = ((18 & 0x18) >> 3), ['n'] = ((23 & 0x18) >> 3),
+ ['J'] = ((19 & 0x18) >> 3), ['o'] = ((24 & 0x18) >> 3),
+ ['K'] = ((20 & 0x18) >> 3), ['p'] = ((25 & 0x18) >> 3),
+ ['L'] = ((21 & 0x18) >> 3), ['q'] = ((26 & 0x18) >> 3),
+ ['M'] = ((22 & 0x18) >> 3), ['r'] = ((27 & 0x18) >> 3),
+ ['N'] = ((23 & 0x18) >> 3), ['s'] = ((28 & 0x18) >> 3),
+ ['O'] = ((24 & 0x18) >> 3), ['t'] = ((29 & 0x18) >> 3),
+ ['P'] = ((25 & 0x18) >> 3), ['u'] = ((30 & 0x18) >> 3),
+ ['Q'] = ((26 & 0x18) >> 3), ['v'] = ((31 & 0x18) >> 3),
+};
+
+// 0x07 = 00000111
+const uint8_t seventh_right_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x07) << 5), ['R'] = ((27 & 0x07) << 5),
+ ['1'] = (( 1 & 0x07) << 5), ['S'] = ((28 & 0x07) << 5),
+ ['2'] = (( 2 & 0x07) << 5), ['T'] = ((29 & 0x07) << 5),
+ ['3'] = (( 3 & 0x07) << 5), ['U'] = ((30 & 0x07) << 5),
+ ['4'] = (( 4 & 0x07) << 5), ['V'] = ((31 & 0x07) << 5),
+ ['5'] = (( 5 & 0x07) << 5), ['a'] = ((10 & 0x07) << 5),
+ ['6'] = (( 6 & 0x07) << 5), ['b'] = ((11 & 0x07) << 5),
+ ['7'] = (( 7 & 0x07) << 5), ['c'] = ((12 & 0x07) << 5),
+ ['8'] = (( 8 & 0x07) << 5), ['d'] = ((13 & 0x07) << 5),
+ ['9'] = (( 9 & 0x07) << 5), ['e'] = ((14 & 0x07) << 5),
+ ['A'] = ((10 & 0x07) << 5), ['f'] = ((15 & 0x07) << 5),
+ ['B'] = ((11 & 0x07) << 5), ['g'] = ((16 & 0x07) << 5),
+ ['C'] = ((12 & 0x07) << 5), ['h'] = ((17 & 0x07) << 5),
+ ['D'] = ((13 & 0x07) << 5), ['i'] = ((18 & 0x07) << 5),
+ ['E'] = ((14 & 0x07) << 5), ['j'] = ((19 & 0x07) << 5),
+ ['F'] = ((15 & 0x07) << 5), ['k'] = ((20 & 0x07) << 5),
+ ['G'] = ((16 & 0x07) << 5), ['l'] = ((21 & 0x07) << 5),
+ ['H'] = ((17 & 0x07) << 5), ['m'] = ((22 & 0x07) << 5),
+ ['I'] = ((18 & 0x07) << 5), ['n'] = ((23 & 0x07) << 5),
+ ['J'] = ((19 & 0x07) << 5), ['o'] = ((24 & 0x07) << 5),
+ ['K'] = ((20 & 0x07) << 5), ['p'] = ((25 & 0x07) << 5),
+ ['L'] = ((21 & 0x07) << 5), ['q'] = ((26 & 0x07) << 5),
+ ['M'] = ((22 & 0x07) << 5), ['r'] = ((27 & 0x07) << 5),
+ ['N'] = ((23 & 0x07) << 5), ['s'] = ((28 & 0x07) << 5),
+ ['O'] = ((24 & 0x07) << 5), ['t'] = ((29 & 0x07) << 5),
+ ['P'] = ((25 & 0x07) << 5), ['u'] = ((30 & 0x07) << 5),
+ ['Q'] = ((26 & 0x07) << 5), ['v'] = ((31 & 0x07) << 5),
+};
+
+// 0x1F = 00011111
+const uint8_t eighth_base32hex_to_num[] = {
+ ['0'] = (( 0 & 0x1F) << 0), ['R'] = ((27 & 0x1F) << 0),
+ ['1'] = (( 1 & 0x1F) << 0), ['S'] = ((28 & 0x1F) << 0),
+ ['2'] = (( 2 & 0x1F) << 0), ['T'] = ((29 & 0x1F) << 0),
+ ['3'] = (( 3 & 0x1F) << 0), ['U'] = ((30 & 0x1F) << 0),
+ ['4'] = (( 4 & 0x1F) << 0), ['V'] = ((31 & 0x1F) << 0),
+ ['5'] = (( 5 & 0x1F) << 0), ['a'] = ((10 & 0x1F) << 0),
+ ['6'] = (( 6 & 0x1F) << 0), ['b'] = ((11 & 0x1F) << 0),
+ ['7'] = (( 7 & 0x1F) << 0), ['c'] = ((12 & 0x1F) << 0),
+ ['8'] = (( 8 & 0x1F) << 0), ['d'] = ((13 & 0x1F) << 0),
+ ['9'] = (( 9 & 0x1F) << 0), ['e'] = ((14 & 0x1F) << 0),
+ ['A'] = ((10 & 0x1F) << 0), ['f'] = ((15 & 0x1F) << 0),
+ ['B'] = ((11 & 0x1F) << 0), ['g'] = ((16 & 0x1F) << 0),
+ ['C'] = ((12 & 0x1F) << 0), ['h'] = ((17 & 0x1F) << 0),
+ ['D'] = ((13 & 0x1F) << 0), ['i'] = ((18 & 0x1F) << 0),
+ ['E'] = ((14 & 0x1F) << 0), ['j'] = ((19 & 0x1F) << 0),
+ ['F'] = ((15 & 0x1F) << 0), ['k'] = ((20 & 0x1F) << 0),
+ ['G'] = ((16 & 0x1F) << 0), ['l'] = ((21 & 0x1F) << 0),
+ ['H'] = ((17 & 0x1F) << 0), ['m'] = ((22 & 0x1F) << 0),
+ ['I'] = ((18 & 0x1F) << 0), ['n'] = ((23 & 0x1F) << 0),
+ ['J'] = ((19 & 0x1F) << 0), ['o'] = ((24 & 0x1F) << 0),
+ ['K'] = ((20 & 0x1F) << 0), ['p'] = ((25 & 0x1F) << 0),
+ ['L'] = ((21 & 0x1F) << 0), ['q'] = ((26 & 0x1F) << 0),
+ ['M'] = ((22 & 0x1F) << 0), ['r'] = ((27 & 0x1F) << 0),
+ ['N'] = ((23 & 0x1F) << 0), ['s'] = ((28 & 0x1F) << 0),
+ ['O'] = ((24 & 0x1F) << 0), ['t'] = ((29 & 0x1F) << 0),
+ ['P'] = ((25 & 0x1F) << 0), ['u'] = ((30 & 0x1F) << 0),
+ ['Q'] = ((26 & 0x1F) << 0), ['v'] = ((31 & 0x1F) << 0),
+};
+
+// Without leap day 29. 2.
+static const uint8_t days_in_months[] = {
+ [ 1] = 31, [ 2] = 28, [ 3] = 31, [ 4] = 30, [ 5] = 31, [ 6] = 30,
+ [ 7] = 31, [ 8] = 31, [ 9] = 30, [10] = 31, [11] = 30, [12] = 31,
+};
+
+// Without leap day 29. 2.
+static const uint16_t days_across_months[] = {
+ [ 1] = 0, [ 2] = 31, [ 3] = 59, [ 4] = 90, [ 5] = 120, [ 6] = 151,
+ [ 7] = 181, [ 8] = 212, [ 9] = 243, [10] = 273, [11] = 304, [12] = 334,
+};
+
+// 0 ~ 1970 ... 135 ~ 2105 ... 255 ~ 2225
+static const uint8_t is_leap_year[] = {
+ [ 1] = 0, [ 2] = 1, [ 3] = 0, [ 4] = 0, [ 5] = 0,
+ [ 6] = 1, [ 7] = 0, [ 8] = 0, [ 9] = 0, [ 10] = 1,
+ [ 11] = 0, [ 12] = 0, [ 13] = 0, [ 14] = 1, [ 15] = 0,
+ [ 16] = 0, [ 17] = 0, [ 18] = 1, [ 19] = 0, [ 20] = 0,
+ [ 21] = 0, [ 22] = 1, [ 23] = 0, [ 24] = 0, [ 25] = 0,
+ [ 26] = 1, [ 27] = 0, [ 28] = 0, [ 29] = 0, [ 30] = 1,
+ [ 31] = 0, [ 32] = 0, [ 33] = 0, [ 34] = 1, [ 35] = 0,
+ [ 36] = 0, [ 37] = 0, [ 38] = 1, [ 39] = 0, [ 40] = 0,
+ [ 41] = 0, [ 42] = 1, [ 43] = 0, [ 44] = 0, [ 45] = 0,
+ [ 46] = 1, [ 47] = 0, [ 48] = 0, [ 49] = 0, [ 50] = 1,
+ [ 51] = 0, [ 52] = 0, [ 53] = 0, [ 54] = 1, [ 55] = 0,
+ [ 56] = 0, [ 57] = 0, [ 58] = 1, [ 59] = 0, [ 60] = 0,
+ [ 61] = 0, [ 62] = 1, [ 63] = 0, [ 64] = 0, [ 65] = 0,
+ [ 66] = 1, [ 67] = 0, [ 68] = 0, [ 69] = 0, [ 70] = 1,
+ [ 71] = 0, [ 72] = 0, [ 73] = 0, [ 74] = 1, [ 75] = 0,
+ [ 76] = 0, [ 77] = 0, [ 78] = 1, [ 79] = 0, [ 80] = 0,
+ [ 81] = 0, [ 82] = 1, [ 83] = 0, [ 84] = 0, [ 85] = 0,
+ [ 86] = 1, [ 87] = 0, [ 88] = 0, [ 89] = 0, [ 90] = 1,
+ [ 91] = 0, [ 92] = 0, [ 93] = 0, [ 94] = 1, [ 95] = 0,
+ [ 96] = 0, [ 97] = 0, [ 98] = 1, [ 99] = 0, [100] = 0,
+ [101] = 0, [102] = 1, [103] = 0, [104] = 0, [105] = 0,
+ [106] = 1, [107] = 0, [108] = 0, [109] = 0, [110] = 1,
+ [111] = 0, [112] = 0, [113] = 0, [114] = 1, [115] = 0,
+ [116] = 0, [117] = 0, [118] = 1, [119] = 0, [120] = 0,
+ [121] = 0, [122] = 1, [123] = 0, [124] = 0, [125] = 0,
+ [126] = 1, [127] = 0, [128] = 0, [129] = 0, [130] = 0,
+ [131] = 0, [132] = 0, [133] = 0, [134] = 1, [135] = 0,
+ [136] = 0, [137] = 0, [138] = 1, [139] = 0, [140] = 0,
+ [141] = 0, [142] = 1, [143] = 0, [144] = 0, [145] = 0,
+ [146] = 1, [147] = 0, [148] = 0, [149] = 0, [150] = 1,
+ [151] = 0, [152] = 0, [153] = 0, [154] = 1, [155] = 0,
+ [156] = 0, [157] = 0, [158] = 1, [159] = 0, [160] = 0,
+ [161] = 0, [162] = 1, [163] = 0, [164] = 0, [165] = 0,
+ [166] = 1, [167] = 0, [168] = 0, [169] = 0, [170] = 1,
+ [171] = 0, [172] = 0, [173] = 0, [174] = 1, [175] = 0,
+ [176] = 0, [177] = 0, [178] = 1, [179] = 0, [180] = 0,
+ [181] = 0, [182] = 1, [183] = 0, [184] = 0, [185] = 0,
+ [186] = 1, [187] = 0, [188] = 0, [189] = 0, [190] = 1,
+ [191] = 0, [192] = 0, [193] = 0, [194] = 1, [195] = 0,
+ [196] = 0, [197] = 0, [198] = 1, [199] = 0, [200] = 0,
+ [201] = 0, [202] = 1, [203] = 0, [204] = 0, [205] = 0,
+ [206] = 1, [207] = 0, [208] = 0, [209] = 0, [210] = 1,
+ [211] = 0, [212] = 0, [213] = 0, [214] = 1, [215] = 0,
+ [216] = 0, [217] = 0, [218] = 1, [219] = 0, [220] = 0,
+ [221] = 0, [222] = 1, [223] = 0, [224] = 0, [225] = 0,
+ [226] = 1, [227] = 0, [228] = 0, [229] = 0, [230] = 0,
+ [231] = 0, [232] = 0, [233] = 0, [234] = 1, [235] = 0,
+ [236] = 0, [237] = 0, [238] = 1, [239] = 0, [240] = 0,
+ [241] = 0, [242] = 1, [243] = 0, [244] = 0, [245] = 0,
+ [246] = 1, [247] = 0, [248] = 0, [249] = 0, [250] = 1,
+ [251] = 0, [252] = 0, [253] = 0, [254] = 1, [255] = 0,
+};
+
+// 0 ~ 1970 ... 135 ~ 2105 ... 255 ~ 2225
+static const uint32_t days_across_years[] = {
+ [ 1] = 365, [ 2] = 730, [ 3] = 1096, [ 4] = 1461, [ 5] = 1826,
+ [ 6] = 2191, [ 7] = 2557, [ 8] = 2922, [ 9] = 3287, [ 10] = 3652,
+ [ 11] = 4018, [ 12] = 4383, [ 13] = 4748, [ 14] = 5113, [ 15] = 5479,
+ [ 16] = 5844, [ 17] = 6209, [ 18] = 6574, [ 19] = 6940, [ 20] = 7305,
+ [ 21] = 7670, [ 22] = 8035, [ 23] = 8401, [ 24] = 8766, [ 25] = 9131,
+ [ 26] = 9496, [ 27] = 9862, [ 28] = 10227, [ 29] = 10592, [ 30] = 10957,
+ [ 31] = 11323, [ 32] = 11688, [ 33] = 12053, [ 34] = 12418, [ 35] = 12784,
+ [ 36] = 13149, [ 37] = 13514, [ 38] = 13879, [ 39] = 14245, [ 40] = 14610,
+ [ 41] = 14975, [ 42] = 15340, [ 43] = 15706, [ 44] = 16071, [ 45] = 16436,
+ [ 46] = 16801, [ 47] = 17167, [ 48] = 17532, [ 49] = 17897, [ 50] = 18262,
+ [ 51] = 18628, [ 52] = 18993, [ 53] = 19358, [ 54] = 19723, [ 55] = 20089,
+ [ 56] = 20454, [ 57] = 20819, [ 58] = 21184, [ 59] = 21550, [ 60] = 21915,
+ [ 61] = 22280, [ 62] = 22645, [ 63] = 23011, [ 64] = 23376, [ 65] = 23741,
+ [ 66] = 24106, [ 67] = 24472, [ 68] = 24837, [ 69] = 25202, [ 70] = 25567,
+ [ 71] = 25933, [ 72] = 26298, [ 73] = 26663, [ 74] = 27028, [ 75] = 27394,
+ [ 76] = 27759, [ 77] = 28124, [ 78] = 28489, [ 79] = 28855, [ 80] = 29220,
+ [ 81] = 29585, [ 82] = 29950, [ 83] = 30316, [ 84] = 30681, [ 85] = 31046,
+ [ 86] = 31411, [ 87] = 31777, [ 88] = 32142, [ 89] = 32507, [ 90] = 32872,
+ [ 91] = 33238, [ 92] = 33603, [ 93] = 33968, [ 94] = 34333, [ 95] = 34699,
+ [ 96] = 35064, [ 97] = 35429, [ 98] = 35794, [ 99] = 36160, [100] = 36525,
+ [101] = 36890, [102] = 37255, [103] = 37621, [104] = 37986, [105] = 38351,
+ [106] = 38716, [107] = 39082, [108] = 39447, [109] = 39812, [110] = 40177,
+ [111] = 40543, [112] = 40908, [113] = 41273, [114] = 41638, [115] = 42004,
+ [116] = 42369, [117] = 42734, [118] = 43099, [119] = 43465, [120] = 43830,
+ [121] = 44195, [122] = 44560, [123] = 44926, [124] = 45291, [125] = 45656,
+ [126] = 46021, [127] = 46387, [128] = 46752, [129] = 47117, [130] = 47482,
+ [131] = 47847, [132] = 48212, [133] = 48577, [134] = 48942, [135] = 49308,
+ [136] = 49673, [137] = 50038, [138] = 50403, [139] = 50769, [140] = 51134,
+ [141] = 51499, [142] = 51864, [143] = 52230, [144] = 52595, [145] = 52960,
+ [146] = 53325, [147] = 53691, [148] = 54056, [149] = 54421, [150] = 54786,
+ [151] = 55152, [152] = 55517, [153] = 55882, [154] = 56247, [155] = 56613,
+ [156] = 56978, [157] = 57343, [158] = 57708, [159] = 58074, [160] = 58439,
+ [161] = 58804, [162] = 59169, [163] = 59535, [164] = 59900, [165] = 60265,
+ [166] = 60630, [167] = 60996, [168] = 61361, [169] = 61726, [170] = 62091,
+ [171] = 62457, [172] = 62822, [173] = 63187, [174] = 63552, [175] = 63918,
+ [176] = 64283, [177] = 64648, [178] = 65013, [179] = 65379, [180] = 65744,
+ [181] = 66109, [182] = 66474, [183] = 66840, [184] = 67205, [185] = 67570,
+ [186] = 67935, [187] = 68301, [188] = 68666, [189] = 69031, [190] = 69396,
+ [191] = 69762, [192] = 70127, [193] = 70492, [194] = 70857, [195] = 71223,
+ [196] = 71588, [197] = 71953, [198] = 72318, [199] = 72684, [200] = 73049,
+ [201] = 73414, [202] = 73779, [203] = 74145, [204] = 74510, [205] = 74875,
+ [206] = 75240, [207] = 75606, [208] = 75971, [209] = 76336, [210] = 76701,
+ [211] = 77067, [212] = 77432, [213] = 77797, [214] = 78162, [215] = 78528,
+ [216] = 78893, [217] = 79258, [218] = 79623, [219] = 79989, [220] = 80354,
+ [221] = 80719, [222] = 81084, [223] = 81450, [224] = 81815, [225] = 82180,
+ [226] = 82545, [227] = 82911, [228] = 83276, [229] = 83641, [230] = 84006,
+ [231] = 84371, [232] = 84736, [233] = 85101, [234] = 85466, [235] = 85832,
+ [236] = 86197, [237] = 86562, [238] = 86927, [239] = 87293, [240] = 87658,
+ [241] = 88023, [242] = 88388, [243] = 88754, [244] = 89119, [245] = 89484,
+ [246] = 89849, [247] = 90215, [248] = 90580, [249] = 90945, [250] = 91310,
+ [251] = 91676, [252] = 92041, [253] = 92406, [254] = 92771, [255] = 93137,
+};
+
+int date_to_timestamp(uint8_t *buff, uint32_t *timestamp)
+{
+ uint32_t year, month, day, hour, minute, second;
+ uint32_t leap_day = 0;
+
+ year = 1000 * (buff[ 0] - '0') + 100 * (buff[ 1] - '0') +
+ 10 * (buff[ 2] - '0') + (buff[ 3] - '0');
+ month = 10 * (buff[ 4] - '0') + (buff[ 5] - '0');
+ day = 10 * (buff[ 6] - '0') + (buff[ 7] - '0');
+ hour = 10 * (buff[ 8] - '0') + (buff[ 9] - '0');
+ minute = 10 * (buff[10] - '0') + (buff[11] - '0');
+ second = 10 * (buff[12] - '0') + (buff[13] - '0');
+
+ if (year < 1970 || year > 2225 || month < 1 || month > 12 || day < 1) {
+ return ZS_BAD_DATE;
+ } else {
+ year -= 1970;
+ }
+
+ if (is_leap_year[year]) {
+ if (month > 2) {
+ leap_day = 1; // Add one day in case of leap year.
+ } else if (month == 2 &&
+ day > (uint32_t)(days_in_months[month] + 1)) {
+ return ZS_BAD_DATE;
+ }
+ } else if (day > days_in_months[month]){
+ return ZS_BAD_DATE;
+ }
+
+ if (hour > 23 || minute > 59 || second > 59) {
+ return ZS_BAD_TIME;
+ }
+
+ *timestamp = hour * 3600 + minute * 60 + second +
+ (days_across_years[year] +
+ days_across_months[month] +
+ day - 1 + leap_day) * 86400;
+
+ return ZS_OK;
+}
+
+void wire_dname_to_str(const uint8_t *data,
+ const uint32_t data_len,
+ char *text)
+{
+ uint32_t i = 0, text_len = 0;
+
+ if (data == NULL || data_len == 0 || text == NULL) {
+ return;
+ }
+
+ uint8_t label_len = data[0];
+
+ // Loop over data characters.
+ for (i = 1; i < data_len; i++) {
+ // Replace label length with dot.
+ if (label_len == 0) {
+ label_len = data[i];
+ text[text_len++] = '.';
+ continue;
+ }
+
+ // Just in case use \123 notation.
+ text[text_len++] = '\\';
+ text[text_len++] = (data[i] / 100) + '0';
+ text[text_len++] = (data[i] / 10) % 10 + '0';
+ text[text_len++] = (data[i] ) % 10 + '0';
+
+ label_len--;
+ }
+
+ // Add trailing dot for root domain.
+ if (data_len == 1 && label_len == 0) {
+ text[text_len++] = '.';
+ }
+
+ // Ending text string.
+ text[text_len] = 0;
+}
+
+uint8_t loc64to8(uint64_t number)
+{
+ uint8_t exponent = 0;
+
+ while (number > 9) {
+ number /= 10;
+ exponent++;
+ }
+ // First 4 bits are mantisa, second 4 bits are exponent.
+ return ((uint8_t)number << 4) + (exponent & 15);
+}
+
+static int mandatory_cmp(const void *p1, const void *p2)
+{
+ uint16_t val1, val2;
+ memcpy(&val1, p1, sizeof(val1));
+ memcpy(&val2, p2, sizeof(val2));
+
+ if (be16toh(val1) < be16toh(val2)) {
+ return -1;
+ } else if (val1 == val2) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+void svcb_mandatory_sort(uint8_t *list_begin, uint8_t *list_end)
+{
+ uint32_t count = (list_end - list_begin) / sizeof(uint16_t);
+ qsort(list_begin, count, sizeof(uint16_t), mandatory_cmp);
+}
+
+int svcb_sort(zs_scanner_t *scanner, uint8_t *rdata_end)
+{
+ zs_svcb_t *svcb = &(scanner->svcb);
+
+ uint8_t *curr_pos = svcb->param_position;
+
+ uint16_t curr_key;
+ memcpy(&curr_key, curr_pos, sizeof(uint16_t));
+ curr_key = be16toh(curr_key);
+
+ if (curr_key > svcb->last_key) {
+ // Already sorted.
+ svcb->last_key = curr_key;
+ return ZS_OK;
+ }
+
+ uint8_t *param_pos = svcb->params_position;
+ while (param_pos < curr_pos) {
+ uint16_t param_key, param_len;
+ memcpy(&param_key, param_pos, 2);
+ param_key = be16toh(param_key);
+ memcpy(&param_len, param_pos + sizeof(uint16_t), 2);
+ param_len = be16toh(param_len);
+
+ uint32_t param_full_len = 2 * sizeof(uint16_t) + param_len;
+
+ if (curr_key < param_key) {
+ uint32_t curr_full_len = rdata_end - curr_pos;
+ memcpy(scanner->buffer, curr_pos, curr_full_len);
+ memmove(param_pos + curr_full_len, param_pos,
+ curr_pos - param_pos);
+ memcpy(param_pos, scanner->buffer, curr_full_len);
+ break;
+ } else if (curr_key == param_key) {
+ return ZS_DUPLICATE_SVCB_KEY;
+ }
+
+ param_pos += param_full_len;
+ }
+
+ return ZS_OK;
+}
+
+int svcb_check(zs_scanner_t *scanner, uint8_t *rdata_end)
+{
+ zs_svcb_t *svcb = &(scanner->svcb);
+
+ if (svcb->params_position == rdata_end ||
+ svcb->params_position[0] != 0 || svcb->params_position[1] != 0) {
+ return ZS_OK; // No parameters or no mandatory parameter available.
+ }
+
+ uint16_t mandat_size;
+ memcpy(&mandat_size, svcb->params_position + sizeof(uint16_t), sizeof(uint16_t));
+ mandat_size = be16toh(mandat_size);
+
+ uint16_t mandat_count = mandat_size / sizeof(uint16_t);
+
+ uint8_t *param_pos = svcb->params_position + 2 * sizeof(uint16_t) + mandat_size;
+ const uint8_t *mandats = svcb->params_position + 2 * sizeof(uint16_t);
+ for (int i = 0; i < mandat_count; i++) {
+ // Check for duplicates.
+ if (i > 0 && memcmp(mandats + (i - 1) * sizeof(uint16_t),
+ mandats + i * sizeof(uint16_t), 2) == 0) {
+ return ZS_DUPLICATE_SVCB_MANDATORY;
+ }
+
+ bool found = false;
+ while (param_pos < rdata_end && !found) {
+ uint16_t param_key, param_len;
+ memcpy(&param_key, param_pos, 2);
+ memcpy(&param_len, param_pos + sizeof(uint16_t), 2);
+ param_len = be16toh(param_len);
+
+ // Compare keys in big endian.
+ if (memcmp(mandats + i * sizeof(uint16_t), param_pos, 2) == 0) {
+ found = true;
+ }
+ param_pos += 2 * sizeof(uint16_t) + param_len;
+ }
+ if (!found) {
+ return ZS_MISSING_SVCB_MANDATORY;
+ }
+ }
+
+ return ZS_OK;
+}
diff --git a/src/libzscanner/functions.h b/src/libzscanner/functions.h
new file mode 100644
index 0000000..5f996fc
--- /dev/null
+++ b/src/libzscanner/functions.h
@@ -0,0 +1,141 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Zone scanner auxiliary functions.
+ *
+ * \addtogroup zone_scanner
+ * @{
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "libzscanner/scanner.h"
+
+/*! \brief Transforms digit char to number. */
+extern const uint8_t digit_to_num[];
+
+/*! \brief Transforms first hex char to the part of the total number. */
+extern const uint8_t first_hex_to_num[];
+/*! \brief Transforms second hex char to the part of the total number. */
+extern const uint8_t second_hex_to_num[];
+
+/*! \brief Transforms first Base64 char. */
+extern const uint8_t first_base64_to_num[];
+/*! \brief Transforms left part of the second Base64 char. */
+extern const uint8_t second_left_base64_to_num[];
+/*! \brief Transforms left part of the second Base64 char. */
+extern const uint8_t second_right_base64_to_num[];
+/*! \brief Transforms left part of the third Base64 char. */
+extern const uint8_t third_left_base64_to_num[];
+/*! \brief Transforms left part of the third Base64 char. */
+extern const uint8_t third_right_base64_to_num[];
+/*! \brief Transforms fourth Base64 char. */
+extern const uint8_t fourth_base64_to_num[];
+
+/*! \brief Transforms first Base32hex char. */
+extern const uint8_t first_base32hex_to_num[];
+/*! \brief Transforms left part of the second Base32hex char. */
+extern const uint8_t second_left_base32hex_to_num[];
+/*! \brief Transforms right part of the second Base32hex char. */
+extern const uint8_t second_right_base32hex_to_num[];
+/*! \brief Transforms third Base32hex char. */
+extern const uint8_t third_base32hex_to_num[];
+/*! \brief Transforms left part of the fourth Base32hex char. */
+extern const uint8_t fourth_left_base32hex_to_num[];
+/*! \brief Transforms right part of the fourth Base32hex char. */
+extern const uint8_t fourth_right_base32hex_to_num[];
+/*! \brief Transforms left part of the fifth Base32hex char. */
+extern const uint8_t fifth_left_base32hex_to_num[];
+/*! \brief Transforms right part of the fifth Base32hex char. */
+extern const uint8_t fifth_right_base32hex_to_num[];
+/*! \brief Transforms sixth Base32hex char. */
+extern const uint8_t sixth_base32hex_to_num[];
+/*! \brief Transforms left part of the seventh Base32hex char. */
+extern const uint8_t seventh_left_base32hex_to_num[];
+/*! \brief Transforms right part of the seventh Base32hex char. */
+extern const uint8_t seventh_right_base32hex_to_num[];
+/*! \brief Transforms eighth Base32hex char. */
+extern const uint8_t eighth_base32hex_to_num[];
+
+/*!
+ * \brief Converts YYYYMMDDHHMMSS time string to unsigned 32-bit timestamp.
+ *
+ * \param buff Buffer containing time string.
+ * \param timestamp Computed timestamp.
+ *
+ * \retval KNOT_EOK if success.
+ * \retval error_code if error.
+ */
+int date_to_timestamp(uint8_t *buff, uint32_t *timestamp);
+
+/*!
+ * \brief Converts wire-format dname to text dname.
+ *
+ * \param data Buffer containing wire-format dname.
+ * \param data_len Length of the buffer.
+ * \param text Text output.
+ */
+void wire_dname_to_str(const uint8_t *data,
+ const uint32_t data_len,
+ char *text);
+
+/*!
+ * \brief Converts unsigned integer to mantissa*10^(exponent).
+ *
+ * Given number is encoded as two 4-bit numbers. First part is mantissa [0-9],
+ * second part is decimal exponent [0-15]. Result is concatenation of these
+ * two blocks.
+ *
+ * \param number Number to convert.
+ *
+ * \retval number encoded number.
+ */
+uint8_t loc64to8(uint64_t number);
+
+/*!
+ * \brief Sorts mandatory parameter values of the SVCB record.
+ *
+ * \param list_begin Start of the parameter list.
+ * \param list_end End of the parameter list.
+ */
+void svcb_mandatory_sort(uint8_t *list_begin, uint8_t *list_end);
+
+/*!
+ * \brief Sorts parameters of the SVCB record.
+ *
+ * \param scanner Scanner context.
+ * \param rdata_end Current end of the output data.
+ *
+ * \return ZS_*.
+ */
+int svcb_sort(zs_scanner_t *scanner, uint8_t *rdata_end);
+
+/*!
+ * \brief Final check of a sorted SVCB record.
+ *
+ * \param scanner Scanner context.
+ * \param rdata_end Current end of the output data.
+ *
+ * \return ZS_*.
+ */
+int svcb_check(zs_scanner_t *scanner, uint8_t *rdata_end);
+
+/*! @} */
diff --git a/src/libzscanner/scanner.c.g2 b/src/libzscanner/scanner.c.g2
new file mode 100644
index 0000000..66f6b1a
--- /dev/null
+++ b/src/libzscanner/scanner.c.g2
@@ -0,0 +1,102263 @@
+
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libzscanner/scanner.h"
+#include "libzscanner/functions.h"
+#include "libknot/descriptor.h"
+
+/*! \brief Maximal length of rdata item. */
+#define MAX_ITEM_LENGTH 255
+#define MAX_ITEM_LENGTH2 65535
+
+/*! \brief Latitude value for equator (2^31). */
+#define LOC_LAT_ZERO (uint32_t)2147483648
+/*! \brief Longitude value for meridian (2^31). */
+#define LOC_LONG_ZERO (uint32_t)2147483648
+/*! \brief Zero level altitude value. */
+#define LOC_ALT_ZERO (uint32_t)10000000
+
+/*! \brief Shorthand for setting warning data. */
+#define WARN(err_code) { s->error.code = err_code; }
+/*! \brief Shorthand for setting error data. */
+#define ERR(err_code) { WARN(err_code); s->error.fatal = true; }
+/*! \brief Shorthand for error reset. */
+#define NOERR { WARN(ZS_OK); s->error.fatal = false; }
+
+/*!
+ * \brief Writes record type number to r_data.
+ *
+ * \param type Type number.
+ * \param rdata_tail Position where to write type number to.
+ */
+static inline void type_num(const uint16_t type, uint8_t **rdata_tail)
+{
+ *((uint16_t *)*rdata_tail) = htons(type);
+ *rdata_tail += 2;
+}
+
+/*!
+ * \brief Sets bit to bitmap window.
+ *
+ * \param type Type number.
+ * \param s Scanner context.
+ */
+static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) {
+ uint8_t win = type / 256;
+ uint8_t bit_pos = type % 256;
+ uint8_t byte_pos = bit_pos / 8;
+
+ ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8);
+
+ if ((s->windows[win]).length < byte_pos + 1) {
+ (s->windows[win]).length = byte_pos + 1;
+ }
+
+ if (s->last_window < win) {
+ s->last_window = win;
+ }
+}
+
+// Include scanner file (in Ragel).
+
+
+
+
+
+
+__attribute__((visibility("default")))
+int zs_init(
+ zs_scanner_t *s,
+ const char *origin,
+ const uint16_t rclass,
+ const uint32_t ttl)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ memset(s, 0, sizeof(*s));
+
+ // Nonzero initial scanner state.
+ s->cs = 1396;
+
+ // Reset the file descriptor.
+ s->file.descriptor = -1;
+
+ // Use the root zone as origin if not specified.
+ if (origin == NULL || strlen(origin) == 0) {
+ origin = ".";
+ }
+ size_t origin_len = strlen(origin);
+
+ // Prepare a zone settings header.
+ const char *format;
+ if (origin[origin_len - 1] != '.') {
+ format = "$ORIGIN %s.\n";
+ } else {
+ format = "$ORIGIN %s\n";
+ }
+
+ char settings[1024];
+ int ret = snprintf(settings, sizeof(settings), format, origin);
+ if (ret <= 0 || ret >= sizeof(settings)) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+
+ // Parse the settings to set up the scanner origin.
+ if (zs_set_input_string(s, settings, ret) != 0 ||
+ zs_parse_all(s) != 0) {
+ return -1;
+ }
+
+ // Set scanner defaults.
+ s->path = strdup(".");
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+ s->default_class = rclass;
+ s->default_ttl = ttl;
+ s->line_counter = 1;
+
+ s->state = ZS_STATE_NONE;
+ s->process.automatic = false;
+
+ return 0;
+}
+
+static void input_deinit(
+ zs_scanner_t *s,
+ bool keep_filename)
+{
+ // Deinit the file input.
+ if (s->file.descriptor != -1) {
+ // Unmap the file content.
+ if (s->input.start != NULL) {
+ if (s->input.mmaped) {
+ munmap((void *)s->input.start,
+ s->input.end - s->input.start);
+ } else {
+ free((void *)s->input.start);
+ }
+ }
+
+ // Close the opened file.
+ close(s->file.descriptor);
+ s->file.descriptor = -1;
+ }
+
+ // Keep file name for possible trailing error report.
+ if (!keep_filename) {
+ free(s->file.name);
+ s->file.name = NULL;
+ }
+
+ // Unset the input limits.
+ s->input.start = NULL;
+ s->input.current = NULL;
+ s->input.end = NULL;
+ s->input.eof = false;
+}
+
+__attribute__((visibility("default")))
+void zs_deinit(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return;
+ }
+
+ input_deinit(s, false);
+ free(s->path);
+}
+
+static int set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size,
+ bool final_block)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (input == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, final_block);
+
+ // Set the scanner input limits.
+ s->input.start = input;
+ s->input.current = input;
+ s->input.end = input + size;
+ s->input.eof = final_block;
+
+ return 0;
+}
+
+static char *read_file_to_buf(
+ int fd,
+ size_t *bufsize)
+{
+ size_t bufs = 0, newbufs = 8192;
+ char *buf = malloc(bufs + newbufs);
+ int ret = 0;
+
+ while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) {
+ bufs += newbufs;
+ newbufs = bufs;
+ char *newbuf = realloc(buf, bufs + newbufs);
+ if (newbuf == NULL) {
+ free(buf);
+ }
+ buf = newbuf;
+ }
+ if (ret < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ *bufsize = bufs + ret;
+ return buf;
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size)
+{
+ s->state = ZS_STATE_NONE;
+
+ return set_input_string(s, input, size, false);
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_file(
+ zs_scanner_t *s,
+ const char *file_name)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (file_name == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, false);
+
+ // Try to open the file.
+ s->file.descriptor = open(file_name, O_RDONLY);
+ if (s->file.descriptor == -1) {
+ ERR(errno == EACCES ? ZS_FILE_ACCESS : ZS_FILE_OPEN);
+ return -1;
+ }
+
+ char *start = NULL;
+ size_t size = 0;
+
+ // Check the input.
+ struct stat file_stat;
+ if (fstat(s->file.descriptor, &file_stat) == -1) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (S_ISCHR(file_stat.st_mode) ||
+ S_ISBLK(file_stat.st_mode) ||
+ S_ISFIFO(file_stat.st_mode)) {
+ // Workaround if cannot mmap, read to memory.
+ start = read_file_to_buf(s->file.descriptor, &size);
+ if (start == NULL) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+ } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file.
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (file_stat.st_size > 0) { // Mmap non-empty file.
+ start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED,
+ s->file.descriptor, 0);
+ if (start == MAP_FAILED) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ size = file_stat.st_size;
+ s->input.mmaped = true;
+
+ // Try to set the mapped memory advise to sequential.
+#if defined(MADV_SEQUENTIAL) && !defined(__sun)
+ (void)madvise(start, size, MADV_SEQUENTIAL);
+#else
+#ifdef POSIX_MADV_SEQUENTIAL
+ (void)posix_madvise(start, size, POSIX_MADV_SEQUENTIAL);
+#endif /* POSIX_MADV_SEQUENTIAL */
+#endif /* MADV_SEQUENTIAL && !__sun */
+ }
+
+ // Set the scanner input limits.
+ s->input.start = start;
+ s->input.current = start;
+ s->input.end = (start != NULL) ? start + size : start;
+
+ // Get absolute path of the zone file if possible.
+ char *full_name = realpath(file_name, NULL);
+ if (full_name != NULL) {
+ free(s->path);
+ s->path = strdup(dirname(full_name));
+ free(full_name);
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+ }
+
+ s->file.name = strdup(file_name);
+ if (s->file.name == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ s->state = ZS_STATE_NONE;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing(
+ zs_scanner_t *s,
+ void (*process_record)(zs_scanner_t *),
+ void (*process_error)(zs_scanner_t *),
+ void *data)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.record = process_record;
+ s->process.error = process_error;
+ s->process.data = data;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing_comment(
+ zs_scanner_t *s,
+ void (*process_comment)(zs_scanner_t *))
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.comment = process_comment;
+
+ return 0;
+}
+
+typedef enum {
+ WRAP_NONE, // Initial state.
+ WRAP_DETECTED, // Input block end is a first '\' in rdata.
+ WRAP_PROCESS // Parsing of auxiliary block = "\".
+} wrap_t;
+
+static void parse(
+ zs_scanner_t *s,
+ wrap_t *wrap)
+{
+ // Restore scanner input limits (Ragel internals).
+ const char *p = s->input.current;
+ const char *pe = s->input.end;
+ const char *eof = s->input.eof ? pe : NULL;
+
+ // Restore state variables (Ragel internals).
+ int cs = s->cs;
+ int top = s->top;
+ int stack[ZS_RAGEL_STACK_SIZE];
+ memcpy(stack, s->stack, sizeof(stack));
+
+ // Next 2 variables are for better performance.
+ // Restoring r_data pointer to next free space.
+ uint8_t *rdata_tail = s->r_data + s->r_data_tail;
+ // Initialization of the last r_data byte.
+ uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1;
+
+ // Write scanner body (in C).
+
+ {
+ short _widec;
+ if ( p == pe )
+ goto _test_eof;
+ goto _resume;
+
+_again:
+ switch ( cs ) {
+ case 1396: goto st1396;
+ case 0: goto st0;
+ case 1: goto st1;
+ case 2: goto st2;
+ case 3: goto st3;
+ case 4: goto st4;
+ case 5: goto st5;
+ case 6: goto st6;
+ case 7: goto st7;
+ case 8: goto st8;
+ case 9: goto st9;
+ case 10: goto st10;
+ case 11: goto st11;
+ case 12: goto st12;
+ case 13: goto st13;
+ case 1397: goto st1397;
+ case 14: goto st14;
+ case 15: goto st15;
+ case 16: goto st16;
+ case 17: goto st17;
+ case 18: goto st18;
+ case 19: goto st19;
+ case 20: goto st20;
+ case 21: goto st21;
+ case 22: goto st22;
+ case 23: goto st23;
+ case 24: goto st24;
+ case 25: goto st25;
+ case 26: goto st26;
+ case 27: goto st27;
+ case 28: goto st28;
+ case 29: goto st29;
+ case 30: goto st30;
+ case 31: goto st31;
+ case 32: goto st32;
+ case 33: goto st33;
+ case 34: goto st34;
+ case 35: goto st35;
+ case 36: goto st36;
+ case 37: goto st37;
+ case 38: goto st38;
+ case 39: goto st39;
+ case 40: goto st40;
+ case 41: goto st41;
+ case 42: goto st42;
+ case 43: goto st43;
+ case 44: goto st44;
+ case 45: goto st45;
+ case 46: goto st46;
+ case 47: goto st47;
+ case 48: goto st48;
+ case 49: goto st49;
+ case 50: goto st50;
+ case 51: goto st51;
+ case 52: goto st52;
+ case 53: goto st53;
+ case 54: goto st54;
+ case 55: goto st55;
+ case 56: goto st56;
+ case 57: goto st57;
+ case 58: goto st58;
+ case 59: goto st59;
+ case 60: goto st60;
+ case 61: goto st61;
+ case 62: goto st62;
+ case 63: goto st63;
+ case 64: goto st64;
+ case 65: goto st65;
+ case 66: goto st66;
+ case 67: goto st67;
+ case 68: goto st68;
+ case 69: goto st69;
+ case 70: goto st70;
+ case 71: goto st71;
+ case 72: goto st72;
+ case 73: goto st73;
+ case 74: goto st74;
+ case 75: goto st75;
+ case 76: goto st76;
+ case 77: goto st77;
+ case 78: goto st78;
+ case 79: goto st79;
+ case 80: goto st80;
+ case 81: goto st81;
+ case 82: goto st82;
+ case 83: goto st83;
+ case 84: goto st84;
+ case 85: goto st85;
+ case 86: goto st86;
+ case 87: goto st87;
+ case 88: goto st88;
+ case 89: goto st89;
+ case 90: goto st90;
+ case 91: goto st91;
+ case 92: goto st92;
+ case 93: goto st93;
+ case 94: goto st94;
+ case 95: goto st95;
+ case 96: goto st96;
+ case 97: goto st97;
+ case 98: goto st98;
+ case 99: goto st99;
+ case 100: goto st100;
+ case 101: goto st101;
+ case 102: goto st102;
+ case 103: goto st103;
+ case 104: goto st104;
+ case 105: goto st105;
+ case 106: goto st106;
+ case 107: goto st107;
+ case 108: goto st108;
+ case 109: goto st109;
+ case 110: goto st110;
+ case 111: goto st111;
+ case 112: goto st112;
+ case 113: goto st113;
+ case 114: goto st114;
+ case 115: goto st115;
+ case 116: goto st116;
+ case 117: goto st117;
+ case 118: goto st118;
+ case 119: goto st119;
+ case 120: goto st120;
+ case 121: goto st121;
+ case 122: goto st122;
+ case 123: goto st123;
+ case 124: goto st124;
+ case 125: goto st125;
+ case 126: goto st126;
+ case 127: goto st127;
+ case 128: goto st128;
+ case 129: goto st129;
+ case 130: goto st130;
+ case 131: goto st131;
+ case 132: goto st132;
+ case 133: goto st133;
+ case 134: goto st134;
+ case 135: goto st135;
+ case 136: goto st136;
+ case 137: goto st137;
+ case 138: goto st138;
+ case 139: goto st139;
+ case 140: goto st140;
+ case 141: goto st141;
+ case 142: goto st142;
+ case 143: goto st143;
+ case 144: goto st144;
+ case 145: goto st145;
+ case 146: goto st146;
+ case 147: goto st147;
+ case 148: goto st148;
+ case 149: goto st149;
+ case 150: goto st150;
+ case 151: goto st151;
+ case 152: goto st152;
+ case 153: goto st153;
+ case 154: goto st154;
+ case 155: goto st155;
+ case 156: goto st156;
+ case 157: goto st157;
+ case 158: goto st158;
+ case 159: goto st159;
+ case 160: goto st160;
+ case 161: goto st161;
+ case 162: goto st162;
+ case 163: goto st163;
+ case 164: goto st164;
+ case 165: goto st165;
+ case 166: goto st166;
+ case 167: goto st167;
+ case 168: goto st168;
+ case 169: goto st169;
+ case 170: goto st170;
+ case 171: goto st171;
+ case 172: goto st172;
+ case 173: goto st173;
+ case 1398: goto st1398;
+ case 174: goto st174;
+ case 175: goto st175;
+ case 176: goto st176;
+ case 177: goto st177;
+ case 178: goto st178;
+ case 179: goto st179;
+ case 180: goto st180;
+ case 181: goto st181;
+ case 182: goto st182;
+ case 183: goto st183;
+ case 1399: goto st1399;
+ case 184: goto st184;
+ case 185: goto st185;
+ case 186: goto st186;
+ case 187: goto st187;
+ case 188: goto st188;
+ case 189: goto st189;
+ case 190: goto st190;
+ case 191: goto st191;
+ case 1400: goto st1400;
+ case 192: goto st192;
+ case 193: goto st193;
+ case 194: goto st194;
+ case 1401: goto st1401;
+ case 195: goto st195;
+ case 196: goto st196;
+ case 197: goto st197;
+ case 198: goto st198;
+ case 199: goto st199;
+ case 200: goto st200;
+ case 201: goto st201;
+ case 202: goto st202;
+ case 203: goto st203;
+ case 204: goto st204;
+ case 205: goto st205;
+ case 1402: goto st1402;
+ case 206: goto st206;
+ case 207: goto st207;
+ case 208: goto st208;
+ case 209: goto st209;
+ case 1403: goto st1403;
+ case 210: goto st210;
+ case 211: goto st211;
+ case 212: goto st212;
+ case 213: goto st213;
+ case 214: goto st214;
+ case 215: goto st215;
+ case 216: goto st216;
+ case 217: goto st217;
+ case 218: goto st218;
+ case 219: goto st219;
+ case 220: goto st220;
+ case 221: goto st221;
+ case 222: goto st222;
+ case 223: goto st223;
+ case 224: goto st224;
+ case 225: goto st225;
+ case 226: goto st226;
+ case 227: goto st227;
+ case 1404: goto st1404;
+ case 228: goto st228;
+ case 229: goto st229;
+ case 230: goto st230;
+ case 231: goto st231;
+ case 232: goto st232;
+ case 233: goto st233;
+ case 234: goto st234;
+ case 235: goto st235;
+ case 236: goto st236;
+ case 237: goto st237;
+ case 238: goto st238;
+ case 239: goto st239;
+ case 240: goto st240;
+ case 241: goto st241;
+ case 242: goto st242;
+ case 243: goto st243;
+ case 1405: goto st1405;
+ case 244: goto st244;
+ case 245: goto st245;
+ case 246: goto st246;
+ case 247: goto st247;
+ case 248: goto st248;
+ case 249: goto st249;
+ case 250: goto st250;
+ case 251: goto st251;
+ case 252: goto st252;
+ case 253: goto st253;
+ case 254: goto st254;
+ case 255: goto st255;
+ case 256: goto st256;
+ case 257: goto st257;
+ case 258: goto st258;
+ case 259: goto st259;
+ case 260: goto st260;
+ case 261: goto st261;
+ case 262: goto st262;
+ case 263: goto st263;
+ case 264: goto st264;
+ case 265: goto st265;
+ case 266: goto st266;
+ case 267: goto st267;
+ case 268: goto st268;
+ case 269: goto st269;
+ case 270: goto st270;
+ case 271: goto st271;
+ case 272: goto st272;
+ case 273: goto st273;
+ case 274: goto st274;
+ case 275: goto st275;
+ case 276: goto st276;
+ case 277: goto st277;
+ case 278: goto st278;
+ case 279: goto st279;
+ case 280: goto st280;
+ case 281: goto st281;
+ case 282: goto st282;
+ case 283: goto st283;
+ case 284: goto st284;
+ case 285: goto st285;
+ case 286: goto st286;
+ case 287: goto st287;
+ case 288: goto st288;
+ case 289: goto st289;
+ case 290: goto st290;
+ case 291: goto st291;
+ case 292: goto st292;
+ case 293: goto st293;
+ case 294: goto st294;
+ case 295: goto st295;
+ case 296: goto st296;
+ case 297: goto st297;
+ case 298: goto st298;
+ case 299: goto st299;
+ case 300: goto st300;
+ case 301: goto st301;
+ case 302: goto st302;
+ case 303: goto st303;
+ case 304: goto st304;
+ case 305: goto st305;
+ case 306: goto st306;
+ case 1406: goto st1406;
+ case 307: goto st307;
+ case 308: goto st308;
+ case 1407: goto st1407;
+ case 309: goto st309;
+ case 310: goto st310;
+ case 1408: goto st1408;
+ case 311: goto st311;
+ case 312: goto st312;
+ case 313: goto st313;
+ case 314: goto st314;
+ case 315: goto st315;
+ case 316: goto st316;
+ case 317: goto st317;
+ case 318: goto st318;
+ case 319: goto st319;
+ case 1409: goto st1409;
+ case 1410: goto st1410;
+ case 320: goto st320;
+ case 321: goto st321;
+ case 322: goto st322;
+ case 323: goto st323;
+ case 324: goto st324;
+ case 325: goto st325;
+ case 326: goto st326;
+ case 327: goto st327;
+ case 328: goto st328;
+ case 329: goto st329;
+ case 330: goto st330;
+ case 331: goto st331;
+ case 332: goto st332;
+ case 333: goto st333;
+ case 334: goto st334;
+ case 1411: goto st1411;
+ case 335: goto st335;
+ case 336: goto st336;
+ case 337: goto st337;
+ case 338: goto st338;
+ case 339: goto st339;
+ case 340: goto st340;
+ case 341: goto st341;
+ case 342: goto st342;
+ case 343: goto st343;
+ case 344: goto st344;
+ case 1412: goto st1412;
+ case 345: goto st345;
+ case 346: goto st346;
+ case 347: goto st347;
+ case 348: goto st348;
+ case 349: goto st349;
+ case 350: goto st350;
+ case 351: goto st351;
+ case 352: goto st352;
+ case 353: goto st353;
+ case 354: goto st354;
+ case 355: goto st355;
+ case 356: goto st356;
+ case 357: goto st357;
+ case 358: goto st358;
+ case 1413: goto st1413;
+ case 359: goto st359;
+ case 360: goto st360;
+ case 361: goto st361;
+ case 362: goto st362;
+ case 363: goto st363;
+ case 364: goto st364;
+ case 365: goto st365;
+ case 1414: goto st1414;
+ case 366: goto st366;
+ case 367: goto st367;
+ case 368: goto st368;
+ case 369: goto st369;
+ case 370: goto st370;
+ case 371: goto st371;
+ case 372: goto st372;
+ case 373: goto st373;
+ case 374: goto st374;
+ case 1415: goto st1415;
+ case 1416: goto st1416;
+ case 1417: goto st1417;
+ case 375: goto st375;
+ case 376: goto st376;
+ case 377: goto st377;
+ case 378: goto st378;
+ case 379: goto st379;
+ case 380: goto st380;
+ case 381: goto st381;
+ case 382: goto st382;
+ case 1418: goto st1418;
+ case 1419: goto st1419;
+ case 383: goto st383;
+ case 384: goto st384;
+ case 385: goto st385;
+ case 1420: goto st1420;
+ case 386: goto st386;
+ case 387: goto st387;
+ case 388: goto st388;
+ case 389: goto st389;
+ case 390: goto st390;
+ case 391: goto st391;
+ case 392: goto st392;
+ case 393: goto st393;
+ case 394: goto st394;
+ case 395: goto st395;
+ case 396: goto st396;
+ case 397: goto st397;
+ case 398: goto st398;
+ case 399: goto st399;
+ case 400: goto st400;
+ case 401: goto st401;
+ case 402: goto st402;
+ case 403: goto st403;
+ case 404: goto st404;
+ case 405: goto st405;
+ case 406: goto st406;
+ case 407: goto st407;
+ case 408: goto st408;
+ case 409: goto st409;
+ case 410: goto st410;
+ case 411: goto st411;
+ case 412: goto st412;
+ case 413: goto st413;
+ case 414: goto st414;
+ case 415: goto st415;
+ case 416: goto st416;
+ case 417: goto st417;
+ case 418: goto st418;
+ case 419: goto st419;
+ case 420: goto st420;
+ case 421: goto st421;
+ case 422: goto st422;
+ case 423: goto st423;
+ case 424: goto st424;
+ case 425: goto st425;
+ case 426: goto st426;
+ case 427: goto st427;
+ case 428: goto st428;
+ case 429: goto st429;
+ case 430: goto st430;
+ case 431: goto st431;
+ case 432: goto st432;
+ case 433: goto st433;
+ case 434: goto st434;
+ case 435: goto st435;
+ case 436: goto st436;
+ case 437: goto st437;
+ case 438: goto st438;
+ case 439: goto st439;
+ case 440: goto st440;
+ case 441: goto st441;
+ case 442: goto st442;
+ case 443: goto st443;
+ case 444: goto st444;
+ case 445: goto st445;
+ case 446: goto st446;
+ case 447: goto st447;
+ case 448: goto st448;
+ case 449: goto st449;
+ case 450: goto st450;
+ case 451: goto st451;
+ case 452: goto st452;
+ case 453: goto st453;
+ case 454: goto st454;
+ case 455: goto st455;
+ case 456: goto st456;
+ case 457: goto st457;
+ case 458: goto st458;
+ case 459: goto st459;
+ case 460: goto st460;
+ case 461: goto st461;
+ case 462: goto st462;
+ case 463: goto st463;
+ case 464: goto st464;
+ case 465: goto st465;
+ case 466: goto st466;
+ case 467: goto st467;
+ case 468: goto st468;
+ case 469: goto st469;
+ case 470: goto st470;
+ case 471: goto st471;
+ case 472: goto st472;
+ case 473: goto st473;
+ case 474: goto st474;
+ case 475: goto st475;
+ case 476: goto st476;
+ case 477: goto st477;
+ case 478: goto st478;
+ case 479: goto st479;
+ case 480: goto st480;
+ case 481: goto st481;
+ case 482: goto st482;
+ case 483: goto st483;
+ case 484: goto st484;
+ case 485: goto st485;
+ case 486: goto st486;
+ case 487: goto st487;
+ case 488: goto st488;
+ case 489: goto st489;
+ case 490: goto st490;
+ case 491: goto st491;
+ case 492: goto st492;
+ case 493: goto st493;
+ case 494: goto st494;
+ case 495: goto st495;
+ case 496: goto st496;
+ case 497: goto st497;
+ case 498: goto st498;
+ case 499: goto st499;
+ case 500: goto st500;
+ case 501: goto st501;
+ case 502: goto st502;
+ case 503: goto st503;
+ case 504: goto st504;
+ case 505: goto st505;
+ case 506: goto st506;
+ case 507: goto st507;
+ case 508: goto st508;
+ case 509: goto st509;
+ case 510: goto st510;
+ case 511: goto st511;
+ case 512: goto st512;
+ case 513: goto st513;
+ case 514: goto st514;
+ case 515: goto st515;
+ case 516: goto st516;
+ case 517: goto st517;
+ case 518: goto st518;
+ case 519: goto st519;
+ case 520: goto st520;
+ case 521: goto st521;
+ case 522: goto st522;
+ case 523: goto st523;
+ case 524: goto st524;
+ case 525: goto st525;
+ case 526: goto st526;
+ case 527: goto st527;
+ case 528: goto st528;
+ case 529: goto st529;
+ case 530: goto st530;
+ case 531: goto st531;
+ case 532: goto st532;
+ case 533: goto st533;
+ case 534: goto st534;
+ case 535: goto st535;
+ case 536: goto st536;
+ case 537: goto st537;
+ case 538: goto st538;
+ case 539: goto st539;
+ case 540: goto st540;
+ case 541: goto st541;
+ case 542: goto st542;
+ case 543: goto st543;
+ case 544: goto st544;
+ case 545: goto st545;
+ case 546: goto st546;
+ case 1421: goto st1421;
+ case 1422: goto st1422;
+ case 1423: goto st1423;
+ case 547: goto st547;
+ case 548: goto st548;
+ case 549: goto st549;
+ case 550: goto st550;
+ case 551: goto st551;
+ case 1424: goto st1424;
+ case 552: goto st552;
+ case 553: goto st553;
+ case 554: goto st554;
+ case 555: goto st555;
+ case 1425: goto st1425;
+ case 1426: goto st1426;
+ case 1427: goto st1427;
+ case 556: goto st556;
+ case 557: goto st557;
+ case 1428: goto st1428;
+ case 558: goto st558;
+ case 559: goto st559;
+ case 560: goto st560;
+ case 561: goto st561;
+ case 562: goto st562;
+ case 563: goto st563;
+ case 1429: goto st1429;
+ case 564: goto st564;
+ case 565: goto st565;
+ case 566: goto st566;
+ case 567: goto st567;
+ case 568: goto st568;
+ case 569: goto st569;
+ case 570: goto st570;
+ case 571: goto st571;
+ case 572: goto st572;
+ case 573: goto st573;
+ case 574: goto st574;
+ case 575: goto st575;
+ case 576: goto st576;
+ case 577: goto st577;
+ case 578: goto st578;
+ case 579: goto st579;
+ case 580: goto st580;
+ case 581: goto st581;
+ case 582: goto st582;
+ case 583: goto st583;
+ case 584: goto st584;
+ case 585: goto st585;
+ case 586: goto st586;
+ case 587: goto st587;
+ case 588: goto st588;
+ case 589: goto st589;
+ case 590: goto st590;
+ case 591: goto st591;
+ case 592: goto st592;
+ case 593: goto st593;
+ case 594: goto st594;
+ case 595: goto st595;
+ case 596: goto st596;
+ case 597: goto st597;
+ case 598: goto st598;
+ case 599: goto st599;
+ case 600: goto st600;
+ case 601: goto st601;
+ case 602: goto st602;
+ case 603: goto st603;
+ case 604: goto st604;
+ case 605: goto st605;
+ case 606: goto st606;
+ case 607: goto st607;
+ case 608: goto st608;
+ case 609: goto st609;
+ case 610: goto st610;
+ case 611: goto st611;
+ case 1430: goto st1430;
+ case 1431: goto st1431;
+ case 612: goto st612;
+ case 613: goto st613;
+ case 614: goto st614;
+ case 615: goto st615;
+ case 616: goto st616;
+ case 617: goto st617;
+ case 618: goto st618;
+ case 619: goto st619;
+ case 620: goto st620;
+ case 621: goto st621;
+ case 1432: goto st1432;
+ case 622: goto st622;
+ case 623: goto st623;
+ case 624: goto st624;
+ case 625: goto st625;
+ case 626: goto st626;
+ case 627: goto st627;
+ case 628: goto st628;
+ case 629: goto st629;
+ case 630: goto st630;
+ case 631: goto st631;
+ case 632: goto st632;
+ case 633: goto st633;
+ case 634: goto st634;
+ case 635: goto st635;
+ case 636: goto st636;
+ case 637: goto st637;
+ case 638: goto st638;
+ case 639: goto st639;
+ case 640: goto st640;
+ case 641: goto st641;
+ case 642: goto st642;
+ case 643: goto st643;
+ case 644: goto st644;
+ case 645: goto st645;
+ case 646: goto st646;
+ case 647: goto st647;
+ case 648: goto st648;
+ case 649: goto st649;
+ case 650: goto st650;
+ case 651: goto st651;
+ case 652: goto st652;
+ case 653: goto st653;
+ case 654: goto st654;
+ case 655: goto st655;
+ case 656: goto st656;
+ case 657: goto st657;
+ case 658: goto st658;
+ case 659: goto st659;
+ case 660: goto st660;
+ case 661: goto st661;
+ case 662: goto st662;
+ case 663: goto st663;
+ case 664: goto st664;
+ case 665: goto st665;
+ case 666: goto st666;
+ case 667: goto st667;
+ case 668: goto st668;
+ case 669: goto st669;
+ case 670: goto st670;
+ case 671: goto st671;
+ case 672: goto st672;
+ case 673: goto st673;
+ case 674: goto st674;
+ case 675: goto st675;
+ case 676: goto st676;
+ case 677: goto st677;
+ case 678: goto st678;
+ case 679: goto st679;
+ case 680: goto st680;
+ case 681: goto st681;
+ case 682: goto st682;
+ case 683: goto st683;
+ case 684: goto st684;
+ case 685: goto st685;
+ case 686: goto st686;
+ case 687: goto st687;
+ case 688: goto st688;
+ case 689: goto st689;
+ case 690: goto st690;
+ case 691: goto st691;
+ case 692: goto st692;
+ case 693: goto st693;
+ case 694: goto st694;
+ case 695: goto st695;
+ case 696: goto st696;
+ case 697: goto st697;
+ case 698: goto st698;
+ case 699: goto st699;
+ case 700: goto st700;
+ case 1433: goto st1433;
+ case 701: goto st701;
+ case 702: goto st702;
+ case 703: goto st703;
+ case 704: goto st704;
+ case 705: goto st705;
+ case 706: goto st706;
+ case 707: goto st707;
+ case 708: goto st708;
+ case 709: goto st709;
+ case 710: goto st710;
+ case 711: goto st711;
+ case 712: goto st712;
+ case 713: goto st713;
+ case 714: goto st714;
+ case 715: goto st715;
+ case 716: goto st716;
+ case 717: goto st717;
+ case 718: goto st718;
+ case 719: goto st719;
+ case 720: goto st720;
+ case 721: goto st721;
+ case 722: goto st722;
+ case 723: goto st723;
+ case 724: goto st724;
+ case 725: goto st725;
+ case 726: goto st726;
+ case 727: goto st727;
+ case 728: goto st728;
+ case 729: goto st729;
+ case 730: goto st730;
+ case 731: goto st731;
+ case 732: goto st732;
+ case 733: goto st733;
+ case 734: goto st734;
+ case 735: goto st735;
+ case 736: goto st736;
+ case 737: goto st737;
+ case 738: goto st738;
+ case 739: goto st739;
+ case 740: goto st740;
+ case 741: goto st741;
+ case 742: goto st742;
+ case 743: goto st743;
+ case 744: goto st744;
+ case 745: goto st745;
+ case 746: goto st746;
+ case 747: goto st747;
+ case 748: goto st748;
+ case 749: goto st749;
+ case 750: goto st750;
+ case 751: goto st751;
+ case 752: goto st752;
+ case 753: goto st753;
+ case 754: goto st754;
+ case 755: goto st755;
+ case 756: goto st756;
+ case 757: goto st757;
+ case 758: goto st758;
+ case 759: goto st759;
+ case 760: goto st760;
+ case 761: goto st761;
+ case 762: goto st762;
+ case 763: goto st763;
+ case 764: goto st764;
+ case 765: goto st765;
+ case 766: goto st766;
+ case 767: goto st767;
+ case 768: goto st768;
+ case 769: goto st769;
+ case 770: goto st770;
+ case 771: goto st771;
+ case 772: goto st772;
+ case 773: goto st773;
+ case 774: goto st774;
+ case 775: goto st775;
+ case 776: goto st776;
+ case 777: goto st777;
+ case 778: goto st778;
+ case 779: goto st779;
+ case 780: goto st780;
+ case 781: goto st781;
+ case 782: goto st782;
+ case 783: goto st783;
+ case 784: goto st784;
+ case 785: goto st785;
+ case 786: goto st786;
+ case 787: goto st787;
+ case 788: goto st788;
+ case 789: goto st789;
+ case 790: goto st790;
+ case 791: goto st791;
+ case 792: goto st792;
+ case 793: goto st793;
+ case 794: goto st794;
+ case 795: goto st795;
+ case 796: goto st796;
+ case 797: goto st797;
+ case 798: goto st798;
+ case 799: goto st799;
+ case 800: goto st800;
+ case 801: goto st801;
+ case 802: goto st802;
+ case 803: goto st803;
+ case 804: goto st804;
+ case 1434: goto st1434;
+ case 805: goto st805;
+ case 806: goto st806;
+ case 807: goto st807;
+ case 808: goto st808;
+ case 809: goto st809;
+ case 810: goto st810;
+ case 811: goto st811;
+ case 812: goto st812;
+ case 813: goto st813;
+ case 814: goto st814;
+ case 815: goto st815;
+ case 816: goto st816;
+ case 817: goto st817;
+ case 818: goto st818;
+ case 819: goto st819;
+ case 820: goto st820;
+ case 821: goto st821;
+ case 822: goto st822;
+ case 823: goto st823;
+ case 824: goto st824;
+ case 825: goto st825;
+ case 826: goto st826;
+ case 827: goto st827;
+ case 828: goto st828;
+ case 829: goto st829;
+ case 830: goto st830;
+ case 831: goto st831;
+ case 832: goto st832;
+ case 833: goto st833;
+ case 834: goto st834;
+ case 835: goto st835;
+ case 836: goto st836;
+ case 837: goto st837;
+ case 838: goto st838;
+ case 839: goto st839;
+ case 840: goto st840;
+ case 841: goto st841;
+ case 842: goto st842;
+ case 843: goto st843;
+ case 844: goto st844;
+ case 845: goto st845;
+ case 1435: goto st1435;
+ case 846: goto st846;
+ case 847: goto st847;
+ case 1436: goto st1436;
+ case 848: goto st848;
+ case 849: goto st849;
+ case 850: goto st850;
+ case 851: goto st851;
+ case 852: goto st852;
+ case 853: goto st853;
+ case 854: goto st854;
+ case 855: goto st855;
+ case 856: goto st856;
+ case 857: goto st857;
+ case 858: goto st858;
+ case 859: goto st859;
+ case 860: goto st860;
+ case 861: goto st861;
+ case 1437: goto st1437;
+ case 862: goto st862;
+ case 863: goto st863;
+ case 864: goto st864;
+ case 865: goto st865;
+ case 866: goto st866;
+ case 867: goto st867;
+ case 868: goto st868;
+ case 869: goto st869;
+ case 870: goto st870;
+ case 871: goto st871;
+ case 872: goto st872;
+ case 873: goto st873;
+ case 874: goto st874;
+ case 875: goto st875;
+ case 876: goto st876;
+ case 877: goto st877;
+ case 878: goto st878;
+ case 879: goto st879;
+ case 880: goto st880;
+ case 881: goto st881;
+ case 882: goto st882;
+ case 883: goto st883;
+ case 1438: goto st1438;
+ case 884: goto st884;
+ case 885: goto st885;
+ case 886: goto st886;
+ case 887: goto st887;
+ case 888: goto st888;
+ case 1439: goto st1439;
+ case 889: goto st889;
+ case 890: goto st890;
+ case 891: goto st891;
+ case 892: goto st892;
+ case 893: goto st893;
+ case 1440: goto st1440;
+ case 894: goto st894;
+ case 895: goto st895;
+ case 896: goto st896;
+ case 897: goto st897;
+ case 898: goto st898;
+ case 1441: goto st1441;
+ case 1442: goto st1442;
+ case 1443: goto st1443;
+ case 899: goto st899;
+ case 900: goto st900;
+ case 1444: goto st1444;
+ case 901: goto st901;
+ case 902: goto st902;
+ case 903: goto st903;
+ case 904: goto st904;
+ case 905: goto st905;
+ case 906: goto st906;
+ case 907: goto st907;
+ case 908: goto st908;
+ case 909: goto st909;
+ case 910: goto st910;
+ case 911: goto st911;
+ case 912: goto st912;
+ case 913: goto st913;
+ case 914: goto st914;
+ case 915: goto st915;
+ case 916: goto st916;
+ case 917: goto st917;
+ case 918: goto st918;
+ case 919: goto st919;
+ case 920: goto st920;
+ case 921: goto st921;
+ case 922: goto st922;
+ case 923: goto st923;
+ case 924: goto st924;
+ case 925: goto st925;
+ case 926: goto st926;
+ case 927: goto st927;
+ case 1445: goto st1445;
+ case 1446: goto st1446;
+ case 1447: goto st1447;
+ case 928: goto st928;
+ case 929: goto st929;
+ case 930: goto st930;
+ case 1448: goto st1448;
+ case 1449: goto st1449;
+ case 931: goto st931;
+ case 932: goto st932;
+ case 933: goto st933;
+ case 934: goto st934;
+ case 1450: goto st1450;
+ case 1451: goto st1451;
+ case 935: goto st935;
+ case 936: goto st936;
+ case 937: goto st937;
+ case 938: goto st938;
+ case 1452: goto st1452;
+ case 1453: goto st1453;
+ case 939: goto st939;
+ case 940: goto st940;
+ case 941: goto st941;
+ case 942: goto st942;
+ case 943: goto st943;
+ case 944: goto st944;
+ case 945: goto st945;
+ case 946: goto st946;
+ case 947: goto st947;
+ case 948: goto st948;
+ case 949: goto st949;
+ case 950: goto st950;
+ case 951: goto st951;
+ case 952: goto st952;
+ case 953: goto st953;
+ case 954: goto st954;
+ case 955: goto st955;
+ case 956: goto st956;
+ case 957: goto st957;
+ case 958: goto st958;
+ case 959: goto st959;
+ case 960: goto st960;
+ case 961: goto st961;
+ case 962: goto st962;
+ case 963: goto st963;
+ case 1454: goto st1454;
+ case 964: goto st964;
+ case 965: goto st965;
+ case 966: goto st966;
+ case 967: goto st967;
+ case 968: goto st968;
+ case 969: goto st969;
+ case 970: goto st970;
+ case 971: goto st971;
+ case 972: goto st972;
+ case 973: goto st973;
+ case 974: goto st974;
+ case 975: goto st975;
+ case 976: goto st976;
+ case 977: goto st977;
+ case 978: goto st978;
+ case 1455: goto st1455;
+ case 979: goto st979;
+ case 980: goto st980;
+ case 981: goto st981;
+ case 982: goto st982;
+ case 983: goto st983;
+ case 984: goto st984;
+ case 985: goto st985;
+ case 986: goto st986;
+ case 987: goto st987;
+ case 988: goto st988;
+ case 989: goto st989;
+ case 990: goto st990;
+ case 991: goto st991;
+ case 1456: goto st1456;
+ case 992: goto st992;
+ case 993: goto st993;
+ case 994: goto st994;
+ case 995: goto st995;
+ case 996: goto st996;
+ case 997: goto st997;
+ case 998: goto st998;
+ case 999: goto st999;
+ case 1000: goto st1000;
+ case 1001: goto st1001;
+ case 1002: goto st1002;
+ case 1457: goto st1457;
+ case 1458: goto st1458;
+ case 1003: goto st1003;
+ case 1004: goto st1004;
+ case 1005: goto st1005;
+ case 1459: goto st1459;
+ case 1006: goto st1006;
+ case 1007: goto st1007;
+ case 1008: goto st1008;
+ case 1009: goto st1009;
+ case 1010: goto st1010;
+ case 1011: goto st1011;
+ case 1012: goto st1012;
+ case 1013: goto st1013;
+ case 1014: goto st1014;
+ case 1015: goto st1015;
+ case 1460: goto st1460;
+ case 1461: goto st1461;
+ case 1462: goto st1462;
+ case 1016: goto st1016;
+ case 1017: goto st1017;
+ case 1018: goto st1018;
+ case 1019: goto st1019;
+ case 1020: goto st1020;
+ case 1021: goto st1021;
+ case 1022: goto st1022;
+ case 1023: goto st1023;
+ case 1024: goto st1024;
+ case 1025: goto st1025;
+ case 1026: goto st1026;
+ case 1463: goto st1463;
+ case 1464: goto st1464;
+ case 1465: goto st1465;
+ case 1027: goto st1027;
+ case 1028: goto st1028;
+ case 1029: goto st1029;
+ case 1030: goto st1030;
+ case 1031: goto st1031;
+ case 1032: goto st1032;
+ case 1033: goto st1033;
+ case 1034: goto st1034;
+ case 1035: goto st1035;
+ case 1036: goto st1036;
+ case 1037: goto st1037;
+ case 1038: goto st1038;
+ case 1466: goto st1466;
+ case 1039: goto st1039;
+ case 1040: goto st1040;
+ case 1041: goto st1041;
+ case 1467: goto st1467;
+ case 1468: goto st1468;
+ case 1042: goto st1042;
+ case 1469: goto st1469;
+ case 1470: goto st1470;
+ case 1043: goto st1043;
+ case 1471: goto st1471;
+ case 1472: goto st1472;
+ case 1044: goto st1044;
+ case 1045: goto st1045;
+ case 1046: goto st1046;
+ case 1047: goto st1047;
+ case 1048: goto st1048;
+ case 1049: goto st1049;
+ case 1050: goto st1050;
+ case 1051: goto st1051;
+ case 1052: goto st1052;
+ case 1053: goto st1053;
+ case 1054: goto st1054;
+ case 1055: goto st1055;
+ case 1056: goto st1056;
+ case 1057: goto st1057;
+ case 1058: goto st1058;
+ case 1059: goto st1059;
+ case 1060: goto st1060;
+ case 1061: goto st1061;
+ case 1062: goto st1062;
+ case 1063: goto st1063;
+ case 1064: goto st1064;
+ case 1065: goto st1065;
+ case 1066: goto st1066;
+ case 1067: goto st1067;
+ case 1068: goto st1068;
+ case 1069: goto st1069;
+ case 1070: goto st1070;
+ case 1071: goto st1071;
+ case 1072: goto st1072;
+ case 1073: goto st1073;
+ case 1074: goto st1074;
+ case 1075: goto st1075;
+ case 1076: goto st1076;
+ case 1077: goto st1077;
+ case 1078: goto st1078;
+ case 1079: goto st1079;
+ case 1080: goto st1080;
+ case 1081: goto st1081;
+ case 1082: goto st1082;
+ case 1083: goto st1083;
+ case 1084: goto st1084;
+ case 1085: goto st1085;
+ case 1473: goto st1473;
+ case 1086: goto st1086;
+ case 1087: goto st1087;
+ case 1088: goto st1088;
+ case 1089: goto st1089;
+ case 1090: goto st1090;
+ case 1091: goto st1091;
+ case 1092: goto st1092;
+ case 1093: goto st1093;
+ case 1094: goto st1094;
+ case 1095: goto st1095;
+ case 1096: goto st1096;
+ case 1097: goto st1097;
+ case 1098: goto st1098;
+ case 1099: goto st1099;
+ case 1100: goto st1100;
+ case 1101: goto st1101;
+ case 1102: goto st1102;
+ case 1103: goto st1103;
+ case 1104: goto st1104;
+ case 1105: goto st1105;
+ case 1106: goto st1106;
+ case 1107: goto st1107;
+ case 1108: goto st1108;
+ case 1109: goto st1109;
+ case 1110: goto st1110;
+ case 1111: goto st1111;
+ case 1112: goto st1112;
+ case 1113: goto st1113;
+ case 1114: goto st1114;
+ case 1115: goto st1115;
+ case 1116: goto st1116;
+ case 1117: goto st1117;
+ case 1118: goto st1118;
+ case 1119: goto st1119;
+ case 1120: goto st1120;
+ case 1121: goto st1121;
+ case 1122: goto st1122;
+ case 1123: goto st1123;
+ case 1124: goto st1124;
+ case 1125: goto st1125;
+ case 1126: goto st1126;
+ case 1127: goto st1127;
+ case 1128: goto st1128;
+ case 1129: goto st1129;
+ case 1130: goto st1130;
+ case 1131: goto st1131;
+ case 1132: goto st1132;
+ case 1133: goto st1133;
+ case 1134: goto st1134;
+ case 1135: goto st1135;
+ case 1136: goto st1136;
+ case 1137: goto st1137;
+ case 1138: goto st1138;
+ case 1139: goto st1139;
+ case 1140: goto st1140;
+ case 1141: goto st1141;
+ case 1142: goto st1142;
+ case 1143: goto st1143;
+ case 1144: goto st1144;
+ case 1145: goto st1145;
+ case 1146: goto st1146;
+ case 1147: goto st1147;
+ case 1148: goto st1148;
+ case 1149: goto st1149;
+ case 1150: goto st1150;
+ case 1151: goto st1151;
+ case 1152: goto st1152;
+ case 1153: goto st1153;
+ case 1154: goto st1154;
+ case 1155: goto st1155;
+ case 1156: goto st1156;
+ case 1157: goto st1157;
+ case 1158: goto st1158;
+ case 1159: goto st1159;
+ case 1160: goto st1160;
+ case 1161: goto st1161;
+ case 1162: goto st1162;
+ case 1163: goto st1163;
+ case 1164: goto st1164;
+ case 1165: goto st1165;
+ case 1166: goto st1166;
+ case 1167: goto st1167;
+ case 1168: goto st1168;
+ case 1169: goto st1169;
+ case 1170: goto st1170;
+ case 1171: goto st1171;
+ case 1172: goto st1172;
+ case 1173: goto st1173;
+ case 1174: goto st1174;
+ case 1175: goto st1175;
+ case 1176: goto st1176;
+ case 1177: goto st1177;
+ case 1178: goto st1178;
+ case 1179: goto st1179;
+ case 1180: goto st1180;
+ case 1181: goto st1181;
+ case 1182: goto st1182;
+ case 1183: goto st1183;
+ case 1184: goto st1184;
+ case 1185: goto st1185;
+ case 1186: goto st1186;
+ case 1187: goto st1187;
+ case 1188: goto st1188;
+ case 1189: goto st1189;
+ case 1190: goto st1190;
+ case 1191: goto st1191;
+ case 1192: goto st1192;
+ case 1193: goto st1193;
+ case 1194: goto st1194;
+ case 1195: goto st1195;
+ case 1196: goto st1196;
+ case 1197: goto st1197;
+ case 1198: goto st1198;
+ case 1199: goto st1199;
+ case 1200: goto st1200;
+ case 1201: goto st1201;
+ case 1202: goto st1202;
+ case 1203: goto st1203;
+ case 1204: goto st1204;
+ case 1205: goto st1205;
+ case 1206: goto st1206;
+ case 1207: goto st1207;
+ case 1208: goto st1208;
+ case 1209: goto st1209;
+ case 1210: goto st1210;
+ case 1211: goto st1211;
+ case 1212: goto st1212;
+ case 1213: goto st1213;
+ case 1214: goto st1214;
+ case 1215: goto st1215;
+ case 1216: goto st1216;
+ case 1217: goto st1217;
+ case 1218: goto st1218;
+ case 1219: goto st1219;
+ case 1220: goto st1220;
+ case 1221: goto st1221;
+ case 1222: goto st1222;
+ case 1223: goto st1223;
+ case 1224: goto st1224;
+ case 1225: goto st1225;
+ case 1226: goto st1226;
+ case 1227: goto st1227;
+ case 1228: goto st1228;
+ case 1229: goto st1229;
+ case 1230: goto st1230;
+ case 1231: goto st1231;
+ case 1232: goto st1232;
+ case 1233: goto st1233;
+ case 1234: goto st1234;
+ case 1235: goto st1235;
+ case 1236: goto st1236;
+ case 1237: goto st1237;
+ case 1238: goto st1238;
+ case 1239: goto st1239;
+ case 1240: goto st1240;
+ case 1241: goto st1241;
+ case 1242: goto st1242;
+ case 1243: goto st1243;
+ case 1244: goto st1244;
+ case 1245: goto st1245;
+ case 1246: goto st1246;
+ case 1247: goto st1247;
+ case 1248: goto st1248;
+ case 1249: goto st1249;
+ case 1250: goto st1250;
+ case 1251: goto st1251;
+ case 1252: goto st1252;
+ case 1253: goto st1253;
+ case 1254: goto st1254;
+ case 1255: goto st1255;
+ case 1256: goto st1256;
+ case 1474: goto st1474;
+ case 1257: goto st1257;
+ case 1258: goto st1258;
+ case 1259: goto st1259;
+ case 1260: goto st1260;
+ case 1261: goto st1261;
+ case 1262: goto st1262;
+ case 1263: goto st1263;
+ case 1264: goto st1264;
+ case 1475: goto st1475;
+ case 1265: goto st1265;
+ case 1266: goto st1266;
+ case 1267: goto st1267;
+ case 1268: goto st1268;
+ case 1269: goto st1269;
+ case 1476: goto st1476;
+ case 1270: goto st1270;
+ case 1271: goto st1271;
+ case 1272: goto st1272;
+ case 1273: goto st1273;
+ case 1274: goto st1274;
+ case 1275: goto st1275;
+ case 1276: goto st1276;
+ case 1277: goto st1277;
+ case 1278: goto st1278;
+ case 1279: goto st1279;
+ case 1280: goto st1280;
+ case 1281: goto st1281;
+ case 1282: goto st1282;
+ case 1283: goto st1283;
+ case 1284: goto st1284;
+ case 1285: goto st1285;
+ case 1286: goto st1286;
+ case 1287: goto st1287;
+ case 1477: goto st1477;
+ case 1288: goto st1288;
+ case 1289: goto st1289;
+ case 1290: goto st1290;
+ case 1291: goto st1291;
+ case 1292: goto st1292;
+ case 1293: goto st1293;
+ case 1294: goto st1294;
+ case 1295: goto st1295;
+ case 1296: goto st1296;
+ case 1297: goto st1297;
+ case 1298: goto st1298;
+ case 1299: goto st1299;
+ case 1300: goto st1300;
+ case 1301: goto st1301;
+ case 1302: goto st1302;
+ case 1303: goto st1303;
+ case 1304: goto st1304;
+ case 1305: goto st1305;
+ case 1306: goto st1306;
+ case 1478: goto st1478;
+ case 1307: goto st1307;
+ case 1308: goto st1308;
+ case 1309: goto st1309;
+ case 1310: goto st1310;
+ case 1311: goto st1311;
+ case 1312: goto st1312;
+ case 1313: goto st1313;
+ case 1314: goto st1314;
+ case 1315: goto st1315;
+ case 1316: goto st1316;
+ case 1317: goto st1317;
+ case 1318: goto st1318;
+ case 1319: goto st1319;
+ case 1320: goto st1320;
+ case 1321: goto st1321;
+ case 1479: goto st1479;
+ case 1480: goto st1480;
+ case 1481: goto st1481;
+ case 1322: goto st1322;
+ case 1323: goto st1323;
+ case 1324: goto st1324;
+ case 1325: goto st1325;
+ case 1326: goto st1326;
+ case 1327: goto st1327;
+ case 1328: goto st1328;
+ case 1329: goto st1329;
+ case 1482: goto st1482;
+ case 1330: goto st1330;
+ case 1331: goto st1331;
+ case 1332: goto st1332;
+ case 1333: goto st1333;
+ case 1334: goto st1334;
+ case 1335: goto st1335;
+ case 1336: goto st1336;
+ case 1337: goto st1337;
+ case 1338: goto st1338;
+ case 1339: goto st1339;
+ case 1340: goto st1340;
+ case 1483: goto st1483;
+ case 1484: goto st1484;
+ case 1485: goto st1485;
+ case 1341: goto st1341;
+ case 1342: goto st1342;
+ case 1343: goto st1343;
+ case 1344: goto st1344;
+ case 1345: goto st1345;
+ case 1346: goto st1346;
+ case 1347: goto st1347;
+ case 1486: goto st1486;
+ case 1348: goto st1348;
+ case 1349: goto st1349;
+ case 1350: goto st1350;
+ case 1351: goto st1351;
+ case 1352: goto st1352;
+ case 1353: goto st1353;
+ case 1354: goto st1354;
+ case 1355: goto st1355;
+ case 1356: goto st1356;
+ case 1357: goto st1357;
+ case 1358: goto st1358;
+ case 1359: goto st1359;
+ case 1360: goto st1360;
+ case 1487: goto st1487;
+ case 1361: goto st1361;
+ case 1362: goto st1362;
+ case 1363: goto st1363;
+ case 1364: goto st1364;
+ case 1365: goto st1365;
+ case 1366: goto st1366;
+ case 1367: goto st1367;
+ case 1488: goto st1488;
+ case 1368: goto st1368;
+ case 1369: goto st1369;
+ case 1370: goto st1370;
+ case 1371: goto st1371;
+ case 1372: goto st1372;
+ case 1373: goto st1373;
+ case 1489: goto st1489;
+ case 1374: goto st1374;
+ case 1375: goto st1375;
+ case 1376: goto st1376;
+ case 1377: goto st1377;
+ case 1378: goto st1378;
+ case 1379: goto st1379;
+ case 1490: goto st1490;
+ case 1380: goto st1380;
+ case 1381: goto st1381;
+ case 1382: goto st1382;
+ case 1383: goto st1383;
+ case 1384: goto st1384;
+ case 1385: goto st1385;
+ case 1386: goto st1386;
+ case 1387: goto st1387;
+ case 1491: goto st1491;
+ case 1388: goto st1388;
+ case 1389: goto st1389;
+ case 1390: goto st1390;
+ case 1391: goto st1391;
+ case 1392: goto st1392;
+ case 1393: goto st1393;
+ case 1394: goto st1394;
+ case 1492: goto st1492;
+ case 1395: goto st1395;
+ default: break;
+ }
+
+ if ( ++p == pe )
+ goto _test_eof;
+_resume:
+ switch ( cs )
+ {
+tr22:
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr87:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr93:
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr96:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr754:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr848:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr966:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ }
+ goto st1396;
+tr970:
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1396; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1396; goto _out;}
+ }
+ }
+ }
+ goto st1396;
+tr4211:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+tr4275:
+ {
+ NOERR;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1396;
+st1396:
+ if ( ++p == pe )
+ goto _test_eof1396;
+case 1396:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4206;
+ case 32: goto tr4206;
+ case 36: goto st184;
+ case 40: goto tr4208;
+ case 41: goto tr4209;
+ case 42: goto tr4210;
+ case 92: goto tr4210;
+ case 95: goto tr4210;
+ case 1802: goto tr4211;
+ case 1851: goto tr4212;
+ case 2058: goto tr4213;
+ case 2107: goto tr4214;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4210;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr4210;
+ } else
+ goto tr4210;
+ goto tr4205;
+tr0:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr26:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr38:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr61:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr75:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr83:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr89:
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr118:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr145:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr151:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr656:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr658:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr755:
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr766:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr780:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr808:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr823:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr861:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr871:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr877:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr890:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr904:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr996:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1011:
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1024:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1032:
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1040:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1056:
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1065:
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1071:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1073:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1085:
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1094:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1109:
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1121:
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1137:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1151:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1152:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1159:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1169:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1186:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1203:
+ {
+ WARN(ZS_BAD_BASE64_CHAR);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1219:
+ {
+ WARN(ZS_BAD_BITMAP);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1792:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1798:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1806:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1868:
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1898:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr1921:
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2005:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2049:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2129:
+ {
+ WARN(ZS_BAD_ALGORITHM);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2251:
+ {
+ WARN(ZS_BAD_CERT_TYPE);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2304:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2327:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2346:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2512:
+ {
+ s->long_string = false;
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2535:
+ {
+ WARN(ZS_BAD_LOC_DATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2938:
+ {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2953:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr2998:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3069:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3084:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3098:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3121:
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3145:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3152:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3271:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3327:
+ {
+ WARN(ZS_BAD_TIMESTAMP_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3756:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr3867:
+ {
+ WARN(ZS_BAD_BASE32HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4106:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4112:
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4118:
+ {
+ WARN(ZS_BAD_CHAR_DASH);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4205:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4227:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4250:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4269:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+tr4293:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; {goto st307;}
+ }
+ goto st0;
+st0:
+cs = 0;
+ goto _out;
+tr2:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1;
+tr3:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1;
+tr4206:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4208:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4209:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4279:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4280:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4270:
+ {
+ NOERR;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4272:
+ {
+ NOERR;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+tr4273:
+ {
+ NOERR;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1;
+st1:
+ if ( ++p == pe )
+ goto _test_eof1;
+case 1:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1;
+ case 32: goto st1;
+ case 40: goto tr2;
+ case 41: goto tr3;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 1802: goto tr22;
+ case 1851: goto tr23;
+ case 2058: goto tr24;
+ case 2107: goto tr25;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr0;
+tr4:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st2;
+tr30:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st2;
+st2:
+ if ( ++p == pe )
+ goto _test_eof2;
+case 2:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr27;
+ case 32: goto tr27;
+ case 40: goto tr28;
+ case 41: goto tr29;
+ case 68: goto tr31;
+ case 72: goto tr32;
+ case 77: goto tr33;
+ case 83: goto st198;
+ case 87: goto tr35;
+ case 100: goto tr31;
+ case 104: goto tr32;
+ case 109: goto tr33;
+ case 115: goto st198;
+ case 119: goto tr35;
+ case 2058: goto tr36;
+ case 2107: goto tr37;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr30;
+ goto tr26;
+tr40:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st3;
+tr41:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st3;
+tr59:
+ {
+ s->line_counter++;
+ }
+ goto st3;
+tr27:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st3;
+tr28:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st3;
+tr29:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st3;
+tr36:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st3;
+tr807:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 3; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st3;
+tr800:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st3;
+tr801:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st3;
+tr802:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st3;
+tr804:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st3;
+st3:
+ if ( ++p == pe )
+ goto _test_eof3;
+case 3:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st3;
+ case 32: goto st3;
+ case 40: goto tr40;
+ case 41: goto tr41;
+ case 65: goto tr42;
+ case 67: goto tr43;
+ case 68: goto tr44;
+ case 69: goto tr45;
+ case 72: goto tr46;
+ case 73: goto tr47;
+ case 75: goto tr48;
+ case 76: goto tr49;
+ case 77: goto tr50;
+ case 78: goto tr51;
+ case 79: goto tr52;
+ case 80: goto tr53;
+ case 82: goto tr54;
+ case 83: goto tr55;
+ case 84: goto tr56;
+ case 85: goto tr57;
+ case 90: goto tr58;
+ case 97: goto tr42;
+ case 99: goto tr43;
+ case 100: goto tr44;
+ case 101: goto tr45;
+ case 104: goto tr46;
+ case 105: goto tr47;
+ case 107: goto tr48;
+ case 108: goto tr49;
+ case 109: goto tr50;
+ case 110: goto tr51;
+ case 111: goto tr52;
+ case 112: goto tr53;
+ case 114: goto tr54;
+ case 115: goto tr55;
+ case 116: goto tr56;
+ case 117: goto tr57;
+ case 122: goto tr58;
+ case 2058: goto tr59;
+ case 2107: goto tr60;
+ }
+ goto tr38;
+tr5:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st4;
+tr42:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st4;
+tr708:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st4;
+st4:
+ if ( ++p == pe )
+ goto _test_eof4;
+case 4:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr62;
+ case 32: goto tr62;
+ case 40: goto tr63;
+ case 41: goto tr64;
+ case 65: goto st266;
+ case 70: goto st269;
+ case 80: goto st273;
+ case 97: goto st266;
+ case 102: goto st269;
+ case 112: goto st273;
+ case 3082: goto tr68;
+ case 3131: goto tr69;
+ case 3338: goto tr70;
+ case 3387: goto tr70;
+ case 3594: goto tr71;
+ case 3643: goto tr72;
+ }
+ goto tr61;
+tr76:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr77:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr79:
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr62:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr63:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr64:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr68:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr100:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 5; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr163:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr164:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr165:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr166:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr185:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr186:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr187:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr188:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr193:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr194:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr195:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr196:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr203:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr204:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr205:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr206:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr214:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr215:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr216:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr217:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr225:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr226:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr227:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr228:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr239:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr240:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr241:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr242:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr251:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr252:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr253:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr254:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr262:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr263:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr264:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr265:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr270:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr271:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr272:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr273:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr283:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr284:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr285:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr286:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr292:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr293:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr294:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr295:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr305:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr306:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr307:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr308:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr316:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr317:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr318:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr319:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr359:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr360:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr361:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr362:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr370:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr371:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr372:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr373:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr378:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr379:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr380:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr381:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr391:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr392:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr393:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr394:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr400:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr401:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr402:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr403:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr409:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr410:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr411:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr412:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr417:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr418:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr419:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr420:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr430:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr431:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr432:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr433:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr438:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr439:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr440:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr441:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr452:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr453:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr454:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr455:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr461:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr462:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr463:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr464:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr469:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr470:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr471:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr473:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr479:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr480:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr481:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr483:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr488:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr489:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr490:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr492:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr501:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr502:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr503:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr504:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr518:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr519:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr520:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr521:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr528:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr529:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr530:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr531:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr539:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr540:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr541:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr542:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr550:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr551:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr552:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr553:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr558:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr559:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr560:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr561:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr576:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr577:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr578:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr579:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr585:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr586:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr587:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr588:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr594:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr595:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr596:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr597:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr603:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr604:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr605:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr606:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr614:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr615:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr616:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr617:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr624:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr625:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr626:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr627:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr637:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr638:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr639:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr640:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr646:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr647:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr648:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr649:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr659:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr660:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr661:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr663:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr670:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr671:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr672:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr673:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr683:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr684:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr685:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr686:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr933:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr934:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr935:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr936:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr944:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr945:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr946:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr947:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+tr953:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st5;
+tr954:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st5;
+tr955:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st5;
+tr956:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st5;
+st5:
+ if ( ++p == pe )
+ goto _test_eof5;
+case 5:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st5;
+ case 32: goto st5;
+ case 40: goto tr76;
+ case 41: goto tr77;
+ case 92: goto tr78;
+ case 3082: goto tr79;
+ case 3131: goto tr80;
+ case 3338: goto tr73;
+ case 3387: goto tr73;
+ case 3594: goto tr81;
+ case 3643: goto tr82;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr75;
+tr70:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr73:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr97:
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr98:
+ {
+ switch (s->r_type) {
+ // Next types must not have empty rdata.
+ case KNOT_RRTYPE_A:
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ case KNOT_RRTYPE_SOA:
+ case KNOT_RRTYPE_HINFO:
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ case KNOT_RRTYPE_RP:
+ case KNOT_RRTYPE_AAAA:
+ case KNOT_RRTYPE_LOC:
+ case KNOT_RRTYPE_SRV:
+ case KNOT_RRTYPE_NAPTR:
+ case KNOT_RRTYPE_CERT:
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_SSHFP:
+ case KNOT_RRTYPE_IPSECKEY:
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_NSEC3:
+ case KNOT_RRTYPE_NSEC3PARAM:
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ case KNOT_RRTYPE_CDS:
+ case KNOT_RRTYPE_CDNSKEY:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ case KNOT_RRTYPE_CSYNC:
+ case KNOT_RRTYPE_ZONEMD:
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L32:
+ case KNOT_RRTYPE_L64:
+ case KNOT_RRTYPE_LP:
+ case KNOT_RRTYPE_EUI48:
+ case KNOT_RRTYPE_EUI64:
+ case KNOT_RRTYPE_URI:
+ case KNOT_RRTYPE_CAA:
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st540;}
+ // Next types can have empty rdata.
+ case KNOT_RRTYPE_APL:
+ default:
+ {stack[top++] = 6;goto st549;}
+ }
+ }
+ goto st6;
+tr168:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr190:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr198:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr208:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr219:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr230:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr244:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr256:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr267:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr275:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr288:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr297:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr310:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr321:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr364:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr375:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr383:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr396:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr405:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr414:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr422:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr435:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr443:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr457:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr466:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr475:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr485:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr494:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr506:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr523:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr533:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr544:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr555:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr563:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr581:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr590:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr599:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr608:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr619:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr629:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr642:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr651:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr665:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr675:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr688:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr938:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr949:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+tr958:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 6;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 6;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 6;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 6;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 6;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 6;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 6;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 6;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 6;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 6;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 6;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 6;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 6;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 6;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 6;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 6;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 6;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 6;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 6;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 6;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 6;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 6;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 6;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 6;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 6;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 6;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 6;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 6;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 6;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 6;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 6;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 6;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st6;
+st6:
+ if ( ++p == pe )
+ goto _test_eof6;
+case 6:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr83;
+tr91:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st7;
+tr92:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st7;
+tr84:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st7;
+tr85:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st7;
+tr86:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st7;
+st7:
+ if ( ++p == pe )
+ goto _test_eof7;
+case 7:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st7;
+ case 32: goto st7;
+ case 40: goto tr91;
+ case 41: goto tr92;
+ case 1802: goto tr93;
+ case 1851: goto tr94;
+ case 2058: goto tr93;
+ case 2107: goto tr94;
+ }
+ goto tr89;
+tr94:
+ {
+ s->buffer_length = 0;
+ }
+ goto st8;
+tr88:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st8;
+tr95:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st8;
+tr849:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st8;
+tr853:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st8;
+st8:
+ if ( ++p == pe )
+ goto _test_eof8;
+case 8:
+ if ( (*p) == 10 )
+ goto tr96;
+ goto tr95;
+tr78:
+ {
+ if (pe - p == 1) {
+ *wrap = WRAP_DETECTED;
+ }
+ }
+ goto st9;
+st9:
+ if ( ++p == pe )
+ goto _test_eof9;
+case 9:
+ if ( (*p) == 35 )
+ goto tr98;
+ goto tr97;
+tr80:
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr69:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr99:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st10;
+tr167:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr189:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr197:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr207:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr218:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr229:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr243:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr255:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr266:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr274:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr287:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr296:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr309:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr320:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr363:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr374:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr382:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr395:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr404:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr413:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr421:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr434:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr442:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr456:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr465:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr474:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr484:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr493:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr505:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr522:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr532:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr543:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr554:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr562:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr580:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr589:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr598:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr607:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr618:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr628:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr641:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr650:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr664:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr674:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr687:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr937:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr948:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+tr957:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st10;
+st10:
+ if ( ++p == pe )
+ goto _test_eof10;
+case 10:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr100;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr99;
+ goto tr75;
+tr71:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr81:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr169:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr191:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr199:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr209:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr220:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr231:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr245:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr257:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr268:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr276:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr289:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr298:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr311:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr322:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr365:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr376:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr384:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr397:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr406:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr415:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr423:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr436:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr444:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr458:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr467:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr476:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr486:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr495:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr507:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr524:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr534:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr545:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr556:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr564:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr582:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr591:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr600:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr609:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr620:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr630:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr643:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr652:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr666:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr676:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr689:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr939:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr950:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+tr959:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 11;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 11;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 11;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 11;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 11;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 11;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 11;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 11;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 11;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 11;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 11;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 11;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 11;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 11;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 11;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 11;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 11;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 11;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 11;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 11;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 11;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 11;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 11;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 11;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 11;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 11;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 11;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 11;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 11;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 11;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 11;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 11;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st11;
+st11:
+ if ( ++p == pe )
+ goto _test_eof11;
+case 11:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr101;
+ case 32: goto tr101;
+ case 40: goto tr102;
+ case 41: goto tr103;
+ case 92: goto tr78;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr87;
+ case 3131: goto tr104;
+ case 3338: goto tr105;
+ case 3387: goto tr106;
+ case 3594: goto tr87;
+ case 3643: goto tr107;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr83;
+tr109:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st12;
+tr110:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st12;
+tr101:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st12;
+tr102:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st12;
+tr103:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st12;
+st12:
+ if ( ++p == pe )
+ goto _test_eof12;
+case 12:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st12;
+ case 32: goto st12;
+ case 40: goto tr109;
+ case 41: goto tr110;
+ case 92: goto tr78;
+ case 2826: goto tr93;
+ case 2875: goto tr94;
+ case 3082: goto tr93;
+ case 3131: goto tr111;
+ case 3338: goto tr112;
+ case 3387: goto tr113;
+ case 3594: goto tr93;
+ case 3643: goto tr114;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr83;
+tr111:
+ {
+ s->buffer_length = 0;
+ }
+ goto st13;
+tr115:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st13;
+tr104:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st13;
+tr174:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st13;
+tr178:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st13;
+tr828:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st13;
+st13:
+ if ( ++p == pe )
+ goto _test_eof13;
+case 13:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr96;
+ case 2058: goto tr116;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr115;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+tr116:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1397; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1397; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1397; goto _out;}
+ }
+ }
+ goto st1397;
+tr836:
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1397; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1397; goto _out;}
+ }
+ }
+ goto st1397;
+tr827:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1397; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1397; goto _out;}
+ }
+ }
+ goto st1397;
+st1397:
+ if ( ++p == pe )
+ goto _test_eof1397;
+case 1397:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr4215;
+ case 32: goto tr4215;
+ case 36: goto tr4216;
+ case 40: goto tr4217;
+ case 41: goto tr4218;
+ case 42: goto tr4219;
+ case 58: goto tr73;
+ case 92: goto tr4220;
+ case 95: goto tr4219;
+ case 2826: goto tr4211;
+ case 2875: goto tr4212;
+ case 3082: goto tr4221;
+ case 3131: goto tr4222;
+ case 3338: goto tr4223;
+ case 3387: goto tr4224;
+ case 3594: goto tr4225;
+ case 3643: goto tr4226;
+ }
+ if ( _widec < 60 ) {
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 44 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else if ( _widec > 63 ) {
+ if ( _widec < 91 ) {
+ if ( 64 <= _widec && _widec <= 90 )
+ goto tr4219;
+ } else if ( _widec > 96 ) {
+ if ( _widec > 122 ) {
+ if ( 123 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 97 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr871;
+tr119:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st14;
+tr120:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st14;
+tr4215:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st14;
+tr4217:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st14;
+tr4218:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st14;
+st14:
+ if ( ++p == pe )
+ goto _test_eof14;
+case 14:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st14;
+ case 32: goto st14;
+ case 40: goto tr119;
+ case 41: goto tr120;
+ case 58: goto tr73;
+ case 65: goto tr122;
+ case 67: goto tr123;
+ case 68: goto tr124;
+ case 69: goto tr125;
+ case 72: goto tr126;
+ case 73: goto tr127;
+ case 75: goto tr128;
+ case 76: goto tr129;
+ case 77: goto tr130;
+ case 78: goto tr131;
+ case 79: goto tr132;
+ case 80: goto tr133;
+ case 82: goto tr134;
+ case 83: goto tr135;
+ case 84: goto tr136;
+ case 85: goto tr137;
+ case 90: goto tr138;
+ case 92: goto tr78;
+ case 97: goto tr122;
+ case 99: goto tr123;
+ case 100: goto tr124;
+ case 101: goto tr125;
+ case 104: goto tr126;
+ case 105: goto tr127;
+ case 107: goto tr128;
+ case 108: goto tr129;
+ case 109: goto tr130;
+ case 110: goto tr131;
+ case 111: goto tr132;
+ case 112: goto tr133;
+ case 114: goto tr134;
+ case 115: goto tr135;
+ case 116: goto tr136;
+ case 117: goto tr137;
+ case 122: goto tr138;
+ case 2826: goto tr22;
+ case 2875: goto tr23;
+ case 3082: goto tr139;
+ case 3131: goto tr140;
+ case 3338: goto tr141;
+ case 3387: goto tr142;
+ case 3594: goto tr143;
+ case 3643: goto tr144;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 47 ) {
+ if ( _widec > 57 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 48 )
+ goto tr121;
+ } else
+ goto tr73;
+ goto tr118;
+tr121:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 15;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 15;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 15;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 15;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 15;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 15;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 15;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 15;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 15;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 15;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 15;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 15;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 15;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 15;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 15;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 15;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 15;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 15;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 15;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 15;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 15;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 15;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 15;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 15;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 15;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 15;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 15;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 15;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 15;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 15;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 15;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 15;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st15;
+st15:
+ if ( ++p == pe )
+ goto _test_eof15;
+case 15:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr146;
+ case 32: goto tr146;
+ case 40: goto tr147;
+ case 41: goto tr148;
+ case 68: goto tr31;
+ case 72: goto tr32;
+ case 77: goto tr33;
+ case 83: goto st198;
+ case 87: goto tr35;
+ case 100: goto tr31;
+ case 104: goto tr32;
+ case 109: goto tr33;
+ case 115: goto st198;
+ case 119: goto tr35;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr149;
+ case 2107: goto tr150;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr30;
+ goto tr145;
+tr153:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st16;
+tr154:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st16;
+tr146:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st16;
+tr147:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st16;
+tr148:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st16;
+st16:
+ if ( ++p == pe )
+ goto _test_eof16;
+case 16:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st16;
+ case 32: goto st16;
+ case 40: goto tr153;
+ case 41: goto tr154;
+ case 65: goto tr42;
+ case 67: goto tr43;
+ case 68: goto tr44;
+ case 69: goto tr45;
+ case 72: goto tr46;
+ case 73: goto tr47;
+ case 75: goto tr48;
+ case 76: goto tr49;
+ case 77: goto tr50;
+ case 78: goto tr51;
+ case 79: goto tr52;
+ case 80: goto tr53;
+ case 82: goto tr54;
+ case 83: goto tr55;
+ case 84: goto tr56;
+ case 85: goto tr57;
+ case 90: goto tr58;
+ case 97: goto tr42;
+ case 99: goto tr43;
+ case 100: goto tr44;
+ case 101: goto tr45;
+ case 104: goto tr46;
+ case 105: goto tr47;
+ case 107: goto tr48;
+ case 108: goto tr49;
+ case 109: goto tr50;
+ case 110: goto tr51;
+ case 111: goto tr52;
+ case 112: goto tr53;
+ case 114: goto tr54;
+ case 115: goto tr55;
+ case 116: goto tr56;
+ case 117: goto tr57;
+ case 122: goto tr58;
+ case 1802: goto tr93;
+ case 1851: goto tr94;
+ case 2058: goto tr155;
+ case 2107: goto tr156;
+ }
+ goto tr151;
+tr6:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st17;
+tr43:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st17;
+tr709:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st17;
+st17:
+ if ( ++p == pe )
+ goto _test_eof17;
+case 17:
+ switch( (*p) ) {
+ case 65: goto st18;
+ case 68: goto st22;
+ case 69: goto st29;
+ case 78: goto st32;
+ case 83: goto st36;
+ case 97: goto st18;
+ case 100: goto st22;
+ case 101: goto st29;
+ case 110: goto st32;
+ case 115: goto st36;
+ }
+ goto tr38;
+st18:
+ if ( ++p == pe )
+ goto _test_eof18;
+case 18:
+ switch( (*p) ) {
+ case 65: goto st19;
+ case 97: goto st19;
+ }
+ goto tr38;
+st19:
+ if ( ++p == pe )
+ goto _test_eof19;
+case 19:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr163;
+ case 32: goto tr163;
+ case 40: goto tr164;
+ case 41: goto tr165;
+ case 3082: goto tr166;
+ case 3131: goto tr167;
+ case 3338: goto tr168;
+ case 3387: goto tr168;
+ case 3594: goto tr169;
+ case 3643: goto tr170;
+ }
+ goto tr61;
+tr72:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr82:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr170:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr192:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr200:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr210:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr221:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr232:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr246:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr258:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr269:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr277:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr290:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr299:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr312:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr323:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr366:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr377:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr385:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr398:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr407:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr416:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr424:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr437:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr445:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr459:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr468:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr477:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr487:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr496:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr508:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr525:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr535:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr546:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr557:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr565:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr583:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr592:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr601:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr610:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr621:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr631:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr644:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr653:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr667:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr677:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr690:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr940:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr951:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+tr960:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 20;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 20;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 20;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 20;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 20;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 20;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 20;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 20;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 20;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 20;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 20;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 20;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 20;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 20;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 20;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 20;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 20;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 20;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 20;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 20;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 20;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 20;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 20;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 20;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 20;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 20;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 20;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 20;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 20;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 20;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 20;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 20;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st20;
+st20:
+ if ( ++p == pe )
+ goto _test_eof20;
+case 20:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr84;
+ case 1802: goto tr87;
+ case 1824: goto tr84;
+ case 1832: goto tr85;
+ case 1833: goto tr86;
+ case 1851: goto tr88;
+ case 2057: goto tr171;
+ case 2058: goto tr87;
+ case 2080: goto tr171;
+ case 2088: goto tr172;
+ case 2089: goto tr173;
+ case 2107: goto tr174;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr99;
+ goto tr83;
+tr175:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st21;
+tr171:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st21;
+tr172:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st21;
+tr173:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st21;
+tr176:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st21;
+tr177:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st21;
+st21:
+ if ( ++p == pe )
+ goto _test_eof21;
+case 21:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto st7;
+ case 1802: goto tr93;
+ case 1824: goto st7;
+ case 1832: goto tr91;
+ case 1833: goto tr92;
+ case 1851: goto tr94;
+ case 2057: goto tr175;
+ case 2058: goto tr93;
+ case 2080: goto tr175;
+ case 2088: goto tr176;
+ case 2089: goto tr177;
+ case 2107: goto tr178;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr99;
+ goto tr83;
+st22:
+ if ( ++p == pe )
+ goto _test_eof22;
+case 22:
+ switch( (*p) ) {
+ case 78: goto st23;
+ case 83: goto st28;
+ case 110: goto st23;
+ case 115: goto st28;
+ }
+ goto tr38;
+st23:
+ if ( ++p == pe )
+ goto _test_eof23;
+case 23:
+ switch( (*p) ) {
+ case 83: goto st24;
+ case 115: goto st24;
+ }
+ goto tr38;
+st24:
+ if ( ++p == pe )
+ goto _test_eof24;
+case 24:
+ switch( (*p) ) {
+ case 75: goto st25;
+ case 107: goto st25;
+ }
+ goto tr38;
+st25:
+ if ( ++p == pe )
+ goto _test_eof25;
+case 25:
+ switch( (*p) ) {
+ case 69: goto st26;
+ case 101: goto st26;
+ }
+ goto tr38;
+st26:
+ if ( ++p == pe )
+ goto _test_eof26;
+case 26:
+ switch( (*p) ) {
+ case 89: goto st27;
+ case 121: goto st27;
+ }
+ goto tr38;
+st27:
+ if ( ++p == pe )
+ goto _test_eof27;
+case 27:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr185;
+ case 32: goto tr185;
+ case 40: goto tr186;
+ case 41: goto tr187;
+ case 3082: goto tr188;
+ case 3131: goto tr189;
+ case 3338: goto tr190;
+ case 3387: goto tr190;
+ case 3594: goto tr191;
+ case 3643: goto tr192;
+ }
+ goto tr61;
+st28:
+ if ( ++p == pe )
+ goto _test_eof28;
+case 28:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr193;
+ case 32: goto tr193;
+ case 40: goto tr194;
+ case 41: goto tr195;
+ case 3082: goto tr196;
+ case 3131: goto tr197;
+ case 3338: goto tr198;
+ case 3387: goto tr198;
+ case 3594: goto tr199;
+ case 3643: goto tr200;
+ }
+ goto tr61;
+st29:
+ if ( ++p == pe )
+ goto _test_eof29;
+case 29:
+ switch( (*p) ) {
+ case 82: goto st30;
+ case 114: goto st30;
+ }
+ goto tr38;
+st30:
+ if ( ++p == pe )
+ goto _test_eof30;
+case 30:
+ switch( (*p) ) {
+ case 84: goto st31;
+ case 116: goto st31;
+ }
+ goto tr38;
+st31:
+ if ( ++p == pe )
+ goto _test_eof31;
+case 31:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr203;
+ case 32: goto tr203;
+ case 40: goto tr204;
+ case 41: goto tr205;
+ case 3082: goto tr206;
+ case 3131: goto tr207;
+ case 3338: goto tr208;
+ case 3387: goto tr208;
+ case 3594: goto tr209;
+ case 3643: goto tr210;
+ }
+ goto tr61;
+st32:
+ if ( ++p == pe )
+ goto _test_eof32;
+case 32:
+ switch( (*p) ) {
+ case 65: goto st33;
+ case 97: goto st33;
+ }
+ goto tr38;
+st33:
+ if ( ++p == pe )
+ goto _test_eof33;
+case 33:
+ switch( (*p) ) {
+ case 77: goto st34;
+ case 109: goto st34;
+ }
+ goto tr38;
+st34:
+ if ( ++p == pe )
+ goto _test_eof34;
+case 34:
+ switch( (*p) ) {
+ case 69: goto st35;
+ case 101: goto st35;
+ }
+ goto tr38;
+st35:
+ if ( ++p == pe )
+ goto _test_eof35;
+case 35:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr214;
+ case 32: goto tr214;
+ case 40: goto tr215;
+ case 41: goto tr216;
+ case 3082: goto tr217;
+ case 3131: goto tr218;
+ case 3338: goto tr219;
+ case 3387: goto tr219;
+ case 3594: goto tr220;
+ case 3643: goto tr221;
+ }
+ goto tr61;
+st36:
+ if ( ++p == pe )
+ goto _test_eof36;
+case 36:
+ switch( (*p) ) {
+ case 89: goto st37;
+ case 121: goto st37;
+ }
+ goto tr38;
+st37:
+ if ( ++p == pe )
+ goto _test_eof37;
+case 37:
+ switch( (*p) ) {
+ case 78: goto st38;
+ case 110: goto st38;
+ }
+ goto tr38;
+st38:
+ if ( ++p == pe )
+ goto _test_eof38;
+case 38:
+ switch( (*p) ) {
+ case 67: goto st39;
+ case 99: goto st39;
+ }
+ goto tr38;
+st39:
+ if ( ++p == pe )
+ goto _test_eof39;
+case 39:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr225;
+ case 32: goto tr225;
+ case 40: goto tr226;
+ case 41: goto tr227;
+ case 3082: goto tr228;
+ case 3131: goto tr229;
+ case 3338: goto tr230;
+ case 3387: goto tr230;
+ case 3594: goto tr231;
+ case 3643: goto tr232;
+ }
+ goto tr61;
+tr7:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st40;
+tr44:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st40;
+tr710:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st40;
+st40:
+ if ( ++p == pe )
+ goto _test_eof40;
+case 40:
+ switch( (*p) ) {
+ case 72: goto st41;
+ case 78: goto st45;
+ case 83: goto st53;
+ case 104: goto st41;
+ case 110: goto st45;
+ case 115: goto st53;
+ }
+ goto tr38;
+st41:
+ if ( ++p == pe )
+ goto _test_eof41;
+case 41:
+ switch( (*p) ) {
+ case 67: goto st42;
+ case 99: goto st42;
+ }
+ goto tr38;
+st42:
+ if ( ++p == pe )
+ goto _test_eof42;
+case 42:
+ switch( (*p) ) {
+ case 73: goto st43;
+ case 105: goto st43;
+ }
+ goto tr38;
+st43:
+ if ( ++p == pe )
+ goto _test_eof43;
+case 43:
+ switch( (*p) ) {
+ case 68: goto st44;
+ case 100: goto st44;
+ }
+ goto tr38;
+st44:
+ if ( ++p == pe )
+ goto _test_eof44;
+case 44:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr239;
+ case 32: goto tr239;
+ case 40: goto tr240;
+ case 41: goto tr241;
+ case 3082: goto tr242;
+ case 3131: goto tr243;
+ case 3338: goto tr244;
+ case 3387: goto tr244;
+ case 3594: goto tr245;
+ case 3643: goto tr246;
+ }
+ goto tr61;
+st45:
+ if ( ++p == pe )
+ goto _test_eof45;
+case 45:
+ switch( (*p) ) {
+ case 65: goto st46;
+ case 83: goto st49;
+ case 97: goto st46;
+ case 115: goto st49;
+ }
+ goto tr38;
+st46:
+ if ( ++p == pe )
+ goto _test_eof46;
+case 46:
+ switch( (*p) ) {
+ case 77: goto st47;
+ case 109: goto st47;
+ }
+ goto tr38;
+st47:
+ if ( ++p == pe )
+ goto _test_eof47;
+case 47:
+ switch( (*p) ) {
+ case 69: goto st48;
+ case 101: goto st48;
+ }
+ goto tr38;
+st48:
+ if ( ++p == pe )
+ goto _test_eof48;
+case 48:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr251;
+ case 32: goto tr251;
+ case 40: goto tr252;
+ case 41: goto tr253;
+ case 3082: goto tr254;
+ case 3131: goto tr255;
+ case 3338: goto tr256;
+ case 3387: goto tr256;
+ case 3594: goto tr257;
+ case 3643: goto tr258;
+ }
+ goto tr61;
+st49:
+ if ( ++p == pe )
+ goto _test_eof49;
+case 49:
+ switch( (*p) ) {
+ case 75: goto st50;
+ case 107: goto st50;
+ }
+ goto tr38;
+st50:
+ if ( ++p == pe )
+ goto _test_eof50;
+case 50:
+ switch( (*p) ) {
+ case 69: goto st51;
+ case 101: goto st51;
+ }
+ goto tr38;
+st51:
+ if ( ++p == pe )
+ goto _test_eof51;
+case 51:
+ switch( (*p) ) {
+ case 89: goto st52;
+ case 121: goto st52;
+ }
+ goto tr38;
+st52:
+ if ( ++p == pe )
+ goto _test_eof52;
+case 52:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr262;
+ case 32: goto tr262;
+ case 40: goto tr263;
+ case 41: goto tr264;
+ case 3082: goto tr265;
+ case 3131: goto tr266;
+ case 3338: goto tr267;
+ case 3387: goto tr267;
+ case 3594: goto tr268;
+ case 3643: goto tr269;
+ }
+ goto tr61;
+st53:
+ if ( ++p == pe )
+ goto _test_eof53;
+case 53:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr270;
+ case 32: goto tr270;
+ case 40: goto tr271;
+ case 41: goto tr272;
+ case 3082: goto tr273;
+ case 3131: goto tr274;
+ case 3338: goto tr275;
+ case 3387: goto tr275;
+ case 3594: goto tr276;
+ case 3643: goto tr277;
+ }
+ goto tr61;
+tr8:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st54;
+tr45:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st54;
+tr711:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st54;
+st54:
+ if ( ++p == pe )
+ goto _test_eof54;
+case 54:
+ switch( (*p) ) {
+ case 85: goto st55;
+ case 117: goto st55;
+ }
+ goto tr38;
+st55:
+ if ( ++p == pe )
+ goto _test_eof55;
+case 55:
+ switch( (*p) ) {
+ case 73: goto st56;
+ case 105: goto st56;
+ }
+ goto tr38;
+st56:
+ if ( ++p == pe )
+ goto _test_eof56;
+case 56:
+ switch( (*p) ) {
+ case 52: goto st57;
+ case 54: goto st59;
+ }
+ goto tr38;
+st57:
+ if ( ++p == pe )
+ goto _test_eof57;
+case 57:
+ if ( (*p) == 56 )
+ goto st58;
+ goto tr38;
+st58:
+ if ( ++p == pe )
+ goto _test_eof58;
+case 58:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr283;
+ case 32: goto tr283;
+ case 40: goto tr284;
+ case 41: goto tr285;
+ case 3082: goto tr286;
+ case 3131: goto tr287;
+ case 3338: goto tr288;
+ case 3387: goto tr288;
+ case 3594: goto tr289;
+ case 3643: goto tr290;
+ }
+ goto tr61;
+st59:
+ if ( ++p == pe )
+ goto _test_eof59;
+case 59:
+ if ( (*p) == 52 )
+ goto st60;
+ goto tr38;
+st60:
+ if ( ++p == pe )
+ goto _test_eof60;
+case 60:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr292;
+ case 32: goto tr292;
+ case 40: goto tr293;
+ case 41: goto tr294;
+ case 3082: goto tr295;
+ case 3131: goto tr296;
+ case 3338: goto tr297;
+ case 3387: goto tr297;
+ case 3594: goto tr298;
+ case 3643: goto tr299;
+ }
+ goto tr61;
+tr9:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st61;
+tr46:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st61;
+tr712:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st61;
+st61:
+ if ( ++p == pe )
+ goto _test_eof61;
+case 61:
+ switch( (*p) ) {
+ case 73: goto st62;
+ case 84: goto st66;
+ case 105: goto st62;
+ case 116: goto st66;
+ }
+ goto tr38;
+st62:
+ if ( ++p == pe )
+ goto _test_eof62;
+case 62:
+ switch( (*p) ) {
+ case 78: goto st63;
+ case 110: goto st63;
+ }
+ goto tr38;
+st63:
+ if ( ++p == pe )
+ goto _test_eof63;
+case 63:
+ switch( (*p) ) {
+ case 70: goto st64;
+ case 102: goto st64;
+ }
+ goto tr38;
+st64:
+ if ( ++p == pe )
+ goto _test_eof64;
+case 64:
+ switch( (*p) ) {
+ case 79: goto st65;
+ case 111: goto st65;
+ }
+ goto tr38;
+st65:
+ if ( ++p == pe )
+ goto _test_eof65;
+case 65:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr305;
+ case 32: goto tr305;
+ case 40: goto tr306;
+ case 41: goto tr307;
+ case 3082: goto tr308;
+ case 3131: goto tr309;
+ case 3338: goto tr310;
+ case 3387: goto tr310;
+ case 3594: goto tr311;
+ case 3643: goto tr312;
+ }
+ goto tr61;
+st66:
+ if ( ++p == pe )
+ goto _test_eof66;
+case 66:
+ switch( (*p) ) {
+ case 84: goto st67;
+ case 116: goto st67;
+ }
+ goto tr38;
+st67:
+ if ( ++p == pe )
+ goto _test_eof67;
+case 67:
+ switch( (*p) ) {
+ case 80: goto st68;
+ case 112: goto st68;
+ }
+ goto tr38;
+st68:
+ if ( ++p == pe )
+ goto _test_eof68;
+case 68:
+ switch( (*p) ) {
+ case 83: goto st69;
+ case 115: goto st69;
+ }
+ goto tr38;
+st69:
+ if ( ++p == pe )
+ goto _test_eof69;
+case 69:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr316;
+ case 32: goto tr316;
+ case 40: goto tr317;
+ case 41: goto tr318;
+ case 3082: goto tr319;
+ case 3131: goto tr320;
+ case 3338: goto tr321;
+ case 3387: goto tr321;
+ case 3594: goto tr322;
+ case 3643: goto tr323;
+ }
+ goto tr61;
+tr47:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st70;
+st70:
+ if ( ++p == pe )
+ goto _test_eof70;
+case 70:
+ switch( (*p) ) {
+ case 78: goto st71;
+ case 80: goto st74;
+ case 110: goto st71;
+ case 112: goto st74;
+ }
+ goto tr38;
+st71:
+ if ( ++p == pe )
+ goto _test_eof71;
+case 71:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr326;
+ case 32: goto tr326;
+ case 40: goto tr327;
+ case 41: goto tr328;
+ case 2058: goto tr329;
+ case 2107: goto tr330;
+ }
+ goto tr38;
+tr332:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st72;
+tr333:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st72;
+tr351:
+ {
+ s->line_counter++;
+ }
+ goto st72;
+tr727:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st72;
+tr728:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st72;
+tr729:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st72;
+tr736:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st72;
+tr692:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 72; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st72;
+tr326:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ goto st72;
+tr327:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st72;
+tr328:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st72;
+tr329:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st72;
+tr745:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st72;
+tr746:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st72;
+tr747:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st72;
+tr749:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st72;
+st72:
+ if ( ++p == pe )
+ goto _test_eof72;
+case 72:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st72;
+ case 32: goto st72;
+ case 40: goto tr332;
+ case 41: goto tr333;
+ case 65: goto st4;
+ case 67: goto st17;
+ case 68: goto st40;
+ case 69: goto st54;
+ case 72: goto st61;
+ case 73: goto st73;
+ case 75: goto st81;
+ case 76: goto st85;
+ case 77: goto st93;
+ case 78: goto st99;
+ case 79: goto st115;
+ case 80: goto st125;
+ case 82: goto st128;
+ case 83: goto st135;
+ case 84: goto st154;
+ case 85: goto st164;
+ case 90: goto st167;
+ case 97: goto st4;
+ case 99: goto st17;
+ case 100: goto st40;
+ case 101: goto st54;
+ case 104: goto st61;
+ case 105: goto st73;
+ case 107: goto st81;
+ case 108: goto st85;
+ case 109: goto st93;
+ case 110: goto st99;
+ case 111: goto st115;
+ case 112: goto st125;
+ case 114: goto st128;
+ case 115: goto st135;
+ case 116: goto st154;
+ case 117: goto st164;
+ case 122: goto st167;
+ case 2058: goto tr351;
+ case 2107: goto tr352;
+ }
+ goto tr38;
+tr713:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st73;
+st73:
+ if ( ++p == pe )
+ goto _test_eof73;
+case 73:
+ switch( (*p) ) {
+ case 80: goto st74;
+ case 112: goto st74;
+ }
+ goto tr38;
+st74:
+ if ( ++p == pe )
+ goto _test_eof74;
+case 74:
+ switch( (*p) ) {
+ case 83: goto st75;
+ case 115: goto st75;
+ }
+ goto tr38;
+st75:
+ if ( ++p == pe )
+ goto _test_eof75;
+case 75:
+ switch( (*p) ) {
+ case 69: goto st76;
+ case 101: goto st76;
+ }
+ goto tr38;
+st76:
+ if ( ++p == pe )
+ goto _test_eof76;
+case 76:
+ switch( (*p) ) {
+ case 67: goto st77;
+ case 99: goto st77;
+ }
+ goto tr38;
+st77:
+ if ( ++p == pe )
+ goto _test_eof77;
+case 77:
+ switch( (*p) ) {
+ case 75: goto st78;
+ case 107: goto st78;
+ }
+ goto tr38;
+st78:
+ if ( ++p == pe )
+ goto _test_eof78;
+case 78:
+ switch( (*p) ) {
+ case 69: goto st79;
+ case 101: goto st79;
+ }
+ goto tr38;
+st79:
+ if ( ++p == pe )
+ goto _test_eof79;
+case 79:
+ switch( (*p) ) {
+ case 89: goto st80;
+ case 121: goto st80;
+ }
+ goto tr38;
+st80:
+ if ( ++p == pe )
+ goto _test_eof80;
+case 80:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr359;
+ case 32: goto tr359;
+ case 40: goto tr360;
+ case 41: goto tr361;
+ case 3082: goto tr362;
+ case 3131: goto tr363;
+ case 3338: goto tr364;
+ case 3387: goto tr364;
+ case 3594: goto tr365;
+ case 3643: goto tr366;
+ }
+ goto tr61;
+tr11:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st81;
+tr48:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st81;
+tr714:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st81;
+st81:
+ if ( ++p == pe )
+ goto _test_eof81;
+case 81:
+ switch( (*p) ) {
+ case 69: goto st82;
+ case 88: goto st84;
+ case 101: goto st82;
+ case 120: goto st84;
+ }
+ goto tr38;
+st82:
+ if ( ++p == pe )
+ goto _test_eof82;
+case 82:
+ switch( (*p) ) {
+ case 89: goto st83;
+ case 121: goto st83;
+ }
+ goto tr38;
+st83:
+ if ( ++p == pe )
+ goto _test_eof83;
+case 83:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr370;
+ case 32: goto tr370;
+ case 40: goto tr371;
+ case 41: goto tr372;
+ case 3082: goto tr373;
+ case 3131: goto tr374;
+ case 3338: goto tr375;
+ case 3387: goto tr375;
+ case 3594: goto tr376;
+ case 3643: goto tr377;
+ }
+ goto tr61;
+st84:
+ if ( ++p == pe )
+ goto _test_eof84;
+case 84:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr378;
+ case 32: goto tr378;
+ case 40: goto tr379;
+ case 41: goto tr380;
+ case 3082: goto tr381;
+ case 3131: goto tr382;
+ case 3338: goto tr383;
+ case 3387: goto tr383;
+ case 3594: goto tr384;
+ case 3643: goto tr385;
+ }
+ goto tr61;
+tr12:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st85;
+tr49:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st85;
+tr715:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st85;
+st85:
+ if ( ++p == pe )
+ goto _test_eof85;
+case 85:
+ switch( (*p) ) {
+ case 51: goto st86;
+ case 54: goto st88;
+ case 79: goto st90;
+ case 80: goto st92;
+ case 111: goto st90;
+ case 112: goto st92;
+ }
+ goto tr38;
+st86:
+ if ( ++p == pe )
+ goto _test_eof86;
+case 86:
+ if ( (*p) == 50 )
+ goto st87;
+ goto tr38;
+st87:
+ if ( ++p == pe )
+ goto _test_eof87;
+case 87:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr391;
+ case 32: goto tr391;
+ case 40: goto tr392;
+ case 41: goto tr393;
+ case 3082: goto tr394;
+ case 3131: goto tr395;
+ case 3338: goto tr396;
+ case 3387: goto tr396;
+ case 3594: goto tr397;
+ case 3643: goto tr398;
+ }
+ goto tr61;
+st88:
+ if ( ++p == pe )
+ goto _test_eof88;
+case 88:
+ if ( (*p) == 52 )
+ goto st89;
+ goto tr38;
+st89:
+ if ( ++p == pe )
+ goto _test_eof89;
+case 89:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr400;
+ case 32: goto tr400;
+ case 40: goto tr401;
+ case 41: goto tr402;
+ case 3082: goto tr403;
+ case 3131: goto tr404;
+ case 3338: goto tr405;
+ case 3387: goto tr405;
+ case 3594: goto tr406;
+ case 3643: goto tr407;
+ }
+ goto tr61;
+st90:
+ if ( ++p == pe )
+ goto _test_eof90;
+case 90:
+ switch( (*p) ) {
+ case 67: goto st91;
+ case 99: goto st91;
+ }
+ goto tr38;
+st91:
+ if ( ++p == pe )
+ goto _test_eof91;
+case 91:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr409;
+ case 32: goto tr409;
+ case 40: goto tr410;
+ case 41: goto tr411;
+ case 3082: goto tr412;
+ case 3131: goto tr413;
+ case 3338: goto tr414;
+ case 3387: goto tr414;
+ case 3594: goto tr415;
+ case 3643: goto tr416;
+ }
+ goto tr61;
+st92:
+ if ( ++p == pe )
+ goto _test_eof92;
+case 92:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr417;
+ case 32: goto tr417;
+ case 40: goto tr418;
+ case 41: goto tr419;
+ case 3082: goto tr420;
+ case 3131: goto tr421;
+ case 3338: goto tr422;
+ case 3387: goto tr422;
+ case 3594: goto tr423;
+ case 3643: goto tr424;
+ }
+ goto tr61;
+tr13:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st93;
+tr50:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st93;
+tr716:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st93;
+st93:
+ if ( ++p == pe )
+ goto _test_eof93;
+case 93:
+ switch( (*p) ) {
+ case 73: goto st94;
+ case 88: goto st98;
+ case 105: goto st94;
+ case 120: goto st98;
+ }
+ goto tr38;
+st94:
+ if ( ++p == pe )
+ goto _test_eof94;
+case 94:
+ switch( (*p) ) {
+ case 78: goto st95;
+ case 110: goto st95;
+ }
+ goto tr38;
+st95:
+ if ( ++p == pe )
+ goto _test_eof95;
+case 95:
+ switch( (*p) ) {
+ case 70: goto st96;
+ case 102: goto st96;
+ }
+ goto tr38;
+st96:
+ if ( ++p == pe )
+ goto _test_eof96;
+case 96:
+ switch( (*p) ) {
+ case 79: goto st97;
+ case 111: goto st97;
+ }
+ goto tr38;
+st97:
+ if ( ++p == pe )
+ goto _test_eof97;
+case 97:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr430;
+ case 32: goto tr430;
+ case 40: goto tr431;
+ case 41: goto tr432;
+ case 3082: goto tr433;
+ case 3131: goto tr434;
+ case 3338: goto tr435;
+ case 3387: goto tr435;
+ case 3594: goto tr436;
+ case 3643: goto tr437;
+ }
+ goto tr61;
+st98:
+ if ( ++p == pe )
+ goto _test_eof98;
+case 98:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr438;
+ case 32: goto tr438;
+ case 40: goto tr439;
+ case 41: goto tr440;
+ case 3082: goto tr441;
+ case 3131: goto tr442;
+ case 3338: goto tr443;
+ case 3387: goto tr443;
+ case 3594: goto tr444;
+ case 3643: goto tr445;
+ }
+ goto tr61;
+tr14:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st99;
+tr51:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st99;
+tr717:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st99;
+st99:
+ if ( ++p == pe )
+ goto _test_eof99;
+case 99:
+ switch( (*p) ) {
+ case 65: goto st100;
+ case 73: goto st104;
+ case 83: goto st106;
+ case 97: goto st100;
+ case 105: goto st104;
+ case 115: goto st106;
+ }
+ goto tr38;
+st100:
+ if ( ++p == pe )
+ goto _test_eof100;
+case 100:
+ switch( (*p) ) {
+ case 80: goto st101;
+ case 112: goto st101;
+ }
+ goto tr38;
+st101:
+ if ( ++p == pe )
+ goto _test_eof101;
+case 101:
+ switch( (*p) ) {
+ case 84: goto st102;
+ case 116: goto st102;
+ }
+ goto tr38;
+st102:
+ if ( ++p == pe )
+ goto _test_eof102;
+case 102:
+ switch( (*p) ) {
+ case 82: goto st103;
+ case 114: goto st103;
+ }
+ goto tr38;
+st103:
+ if ( ++p == pe )
+ goto _test_eof103;
+case 103:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr452;
+ case 32: goto tr452;
+ case 40: goto tr453;
+ case 41: goto tr454;
+ case 3082: goto tr455;
+ case 3131: goto tr456;
+ case 3338: goto tr457;
+ case 3387: goto tr457;
+ case 3594: goto tr458;
+ case 3643: goto tr459;
+ }
+ goto tr61;
+st104:
+ if ( ++p == pe )
+ goto _test_eof104;
+case 104:
+ switch( (*p) ) {
+ case 68: goto st105;
+ case 100: goto st105;
+ }
+ goto tr38;
+st105:
+ if ( ++p == pe )
+ goto _test_eof105;
+case 105:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr461;
+ case 32: goto tr461;
+ case 40: goto tr462;
+ case 41: goto tr463;
+ case 3082: goto tr464;
+ case 3131: goto tr465;
+ case 3338: goto tr466;
+ case 3387: goto tr466;
+ case 3594: goto tr467;
+ case 3643: goto tr468;
+ }
+ goto tr61;
+st106:
+ if ( ++p == pe )
+ goto _test_eof106;
+case 106:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr469;
+ case 32: goto tr469;
+ case 40: goto tr470;
+ case 41: goto tr471;
+ case 69: goto st107;
+ case 101: goto st107;
+ case 3082: goto tr473;
+ case 3131: goto tr474;
+ case 3338: goto tr475;
+ case 3387: goto tr475;
+ case 3594: goto tr476;
+ case 3643: goto tr477;
+ }
+ goto tr61;
+st107:
+ if ( ++p == pe )
+ goto _test_eof107;
+case 107:
+ switch( (*p) ) {
+ case 67: goto st108;
+ case 99: goto st108;
+ }
+ goto tr38;
+st108:
+ if ( ++p == pe )
+ goto _test_eof108;
+case 108:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr479;
+ case 32: goto tr479;
+ case 40: goto tr480;
+ case 41: goto tr481;
+ case 51: goto st109;
+ case 3082: goto tr483;
+ case 3131: goto tr484;
+ case 3338: goto tr485;
+ case 3387: goto tr485;
+ case 3594: goto tr486;
+ case 3643: goto tr487;
+ }
+ goto tr61;
+st109:
+ if ( ++p == pe )
+ goto _test_eof109;
+case 109:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr488;
+ case 32: goto tr488;
+ case 40: goto tr489;
+ case 41: goto tr490;
+ case 80: goto st110;
+ case 112: goto st110;
+ case 3082: goto tr492;
+ case 3131: goto tr493;
+ case 3338: goto tr494;
+ case 3387: goto tr494;
+ case 3594: goto tr495;
+ case 3643: goto tr496;
+ }
+ goto tr61;
+st110:
+ if ( ++p == pe )
+ goto _test_eof110;
+case 110:
+ switch( (*p) ) {
+ case 65: goto st111;
+ case 97: goto st111;
+ }
+ goto tr38;
+st111:
+ if ( ++p == pe )
+ goto _test_eof111;
+case 111:
+ switch( (*p) ) {
+ case 82: goto st112;
+ case 114: goto st112;
+ }
+ goto tr38;
+st112:
+ if ( ++p == pe )
+ goto _test_eof112;
+case 112:
+ switch( (*p) ) {
+ case 65: goto st113;
+ case 97: goto st113;
+ }
+ goto tr38;
+st113:
+ if ( ++p == pe )
+ goto _test_eof113;
+case 113:
+ switch( (*p) ) {
+ case 77: goto st114;
+ case 109: goto st114;
+ }
+ goto tr38;
+st114:
+ if ( ++p == pe )
+ goto _test_eof114;
+case 114:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr501;
+ case 32: goto tr501;
+ case 40: goto tr502;
+ case 41: goto tr503;
+ case 3082: goto tr504;
+ case 3131: goto tr505;
+ case 3338: goto tr506;
+ case 3387: goto tr506;
+ case 3594: goto tr507;
+ case 3643: goto tr508;
+ }
+ goto tr61;
+tr15:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st115;
+tr52:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st115;
+tr718:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st115;
+st115:
+ if ( ++p == pe )
+ goto _test_eof115;
+case 115:
+ switch( (*p) ) {
+ case 80: goto st116;
+ case 112: goto st116;
+ }
+ goto tr38;
+st116:
+ if ( ++p == pe )
+ goto _test_eof116;
+case 116:
+ switch( (*p) ) {
+ case 69: goto st117;
+ case 101: goto st117;
+ }
+ goto tr38;
+st117:
+ if ( ++p == pe )
+ goto _test_eof117;
+case 117:
+ switch( (*p) ) {
+ case 78: goto st118;
+ case 110: goto st118;
+ }
+ goto tr38;
+st118:
+ if ( ++p == pe )
+ goto _test_eof118;
+case 118:
+ switch( (*p) ) {
+ case 80: goto st119;
+ case 112: goto st119;
+ }
+ goto tr38;
+st119:
+ if ( ++p == pe )
+ goto _test_eof119;
+case 119:
+ switch( (*p) ) {
+ case 71: goto st120;
+ case 103: goto st120;
+ }
+ goto tr38;
+st120:
+ if ( ++p == pe )
+ goto _test_eof120;
+case 120:
+ switch( (*p) ) {
+ case 80: goto st121;
+ case 112: goto st121;
+ }
+ goto tr38;
+st121:
+ if ( ++p == pe )
+ goto _test_eof121;
+case 121:
+ switch( (*p) ) {
+ case 75: goto st122;
+ case 107: goto st122;
+ }
+ goto tr38;
+st122:
+ if ( ++p == pe )
+ goto _test_eof122;
+case 122:
+ switch( (*p) ) {
+ case 69: goto st123;
+ case 101: goto st123;
+ }
+ goto tr38;
+st123:
+ if ( ++p == pe )
+ goto _test_eof123;
+case 123:
+ switch( (*p) ) {
+ case 89: goto st124;
+ case 121: goto st124;
+ }
+ goto tr38;
+st124:
+ if ( ++p == pe )
+ goto _test_eof124;
+case 124:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr518;
+ case 32: goto tr518;
+ case 40: goto tr519;
+ case 41: goto tr520;
+ case 3082: goto tr521;
+ case 3131: goto tr522;
+ case 3338: goto tr523;
+ case 3387: goto tr523;
+ case 3594: goto tr524;
+ case 3643: goto tr525;
+ }
+ goto tr61;
+tr16:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st125;
+tr53:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st125;
+tr719:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st125;
+st125:
+ if ( ++p == pe )
+ goto _test_eof125;
+case 125:
+ switch( (*p) ) {
+ case 84: goto st126;
+ case 116: goto st126;
+ }
+ goto tr38;
+st126:
+ if ( ++p == pe )
+ goto _test_eof126;
+case 126:
+ switch( (*p) ) {
+ case 82: goto st127;
+ case 114: goto st127;
+ }
+ goto tr38;
+st127:
+ if ( ++p == pe )
+ goto _test_eof127;
+case 127:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr528;
+ case 32: goto tr528;
+ case 40: goto tr529;
+ case 41: goto tr530;
+ case 3082: goto tr531;
+ case 3131: goto tr532;
+ case 3338: goto tr533;
+ case 3387: goto tr533;
+ case 3594: goto tr534;
+ case 3643: goto tr535;
+ }
+ goto tr61;
+tr17:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st128;
+tr54:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st128;
+tr720:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st128;
+st128:
+ if ( ++p == pe )
+ goto _test_eof128;
+case 128:
+ switch( (*p) ) {
+ case 80: goto st129;
+ case 82: goto st130;
+ case 84: goto st134;
+ case 112: goto st129;
+ case 114: goto st130;
+ case 116: goto st134;
+ }
+ goto tr38;
+st129:
+ if ( ++p == pe )
+ goto _test_eof129;
+case 129:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr539;
+ case 32: goto tr539;
+ case 40: goto tr540;
+ case 41: goto tr541;
+ case 3082: goto tr542;
+ case 3131: goto tr543;
+ case 3338: goto tr544;
+ case 3387: goto tr544;
+ case 3594: goto tr545;
+ case 3643: goto tr546;
+ }
+ goto tr61;
+st130:
+ if ( ++p == pe )
+ goto _test_eof130;
+case 130:
+ switch( (*p) ) {
+ case 83: goto st131;
+ case 115: goto st131;
+ }
+ goto tr38;
+st131:
+ if ( ++p == pe )
+ goto _test_eof131;
+case 131:
+ switch( (*p) ) {
+ case 73: goto st132;
+ case 105: goto st132;
+ }
+ goto tr38;
+st132:
+ if ( ++p == pe )
+ goto _test_eof132;
+case 132:
+ switch( (*p) ) {
+ case 71: goto st133;
+ case 103: goto st133;
+ }
+ goto tr38;
+st133:
+ if ( ++p == pe )
+ goto _test_eof133;
+case 133:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr550;
+ case 32: goto tr550;
+ case 40: goto tr551;
+ case 41: goto tr552;
+ case 3082: goto tr553;
+ case 3131: goto tr554;
+ case 3338: goto tr555;
+ case 3387: goto tr555;
+ case 3594: goto tr556;
+ case 3643: goto tr557;
+ }
+ goto tr61;
+st134:
+ if ( ++p == pe )
+ goto _test_eof134;
+case 134:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr558;
+ case 32: goto tr558;
+ case 40: goto tr559;
+ case 41: goto tr560;
+ case 3082: goto tr561;
+ case 3131: goto tr562;
+ case 3338: goto tr563;
+ case 3387: goto tr563;
+ case 3594: goto tr564;
+ case 3643: goto tr565;
+ }
+ goto tr61;
+tr18:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st135;
+tr55:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st135;
+tr721:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st135;
+st135:
+ if ( ++p == pe )
+ goto _test_eof135;
+case 135:
+ switch( (*p) ) {
+ case 77: goto st136;
+ case 79: goto st141;
+ case 80: goto st143;
+ case 82: goto st145;
+ case 83: goto st147;
+ case 86: goto st151;
+ case 109: goto st136;
+ case 111: goto st141;
+ case 112: goto st143;
+ case 114: goto st145;
+ case 115: goto st147;
+ case 118: goto st151;
+ }
+ goto tr38;
+st136:
+ if ( ++p == pe )
+ goto _test_eof136;
+case 136:
+ switch( (*p) ) {
+ case 73: goto st137;
+ case 105: goto st137;
+ }
+ goto tr38;
+st137:
+ if ( ++p == pe )
+ goto _test_eof137;
+case 137:
+ switch( (*p) ) {
+ case 77: goto st138;
+ case 109: goto st138;
+ }
+ goto tr38;
+st138:
+ if ( ++p == pe )
+ goto _test_eof138;
+case 138:
+ switch( (*p) ) {
+ case 69: goto st139;
+ case 101: goto st139;
+ }
+ goto tr38;
+st139:
+ if ( ++p == pe )
+ goto _test_eof139;
+case 139:
+ switch( (*p) ) {
+ case 65: goto st140;
+ case 97: goto st140;
+ }
+ goto tr38;
+st140:
+ if ( ++p == pe )
+ goto _test_eof140;
+case 140:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr576;
+ case 32: goto tr576;
+ case 40: goto tr577;
+ case 41: goto tr578;
+ case 3082: goto tr579;
+ case 3131: goto tr580;
+ case 3338: goto tr581;
+ case 3387: goto tr581;
+ case 3594: goto tr582;
+ case 3643: goto tr583;
+ }
+ goto tr61;
+st141:
+ if ( ++p == pe )
+ goto _test_eof141;
+case 141:
+ switch( (*p) ) {
+ case 65: goto st142;
+ case 97: goto st142;
+ }
+ goto tr38;
+st142:
+ if ( ++p == pe )
+ goto _test_eof142;
+case 142:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr585;
+ case 32: goto tr585;
+ case 40: goto tr586;
+ case 41: goto tr587;
+ case 3082: goto tr588;
+ case 3131: goto tr589;
+ case 3338: goto tr590;
+ case 3387: goto tr590;
+ case 3594: goto tr591;
+ case 3643: goto tr592;
+ }
+ goto tr61;
+st143:
+ if ( ++p == pe )
+ goto _test_eof143;
+case 143:
+ switch( (*p) ) {
+ case 70: goto st144;
+ case 102: goto st144;
+ }
+ goto tr38;
+st144:
+ if ( ++p == pe )
+ goto _test_eof144;
+case 144:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr594;
+ case 32: goto tr594;
+ case 40: goto tr595;
+ case 41: goto tr596;
+ case 3082: goto tr597;
+ case 3131: goto tr598;
+ case 3338: goto tr599;
+ case 3387: goto tr599;
+ case 3594: goto tr600;
+ case 3643: goto tr601;
+ }
+ goto tr61;
+st145:
+ if ( ++p == pe )
+ goto _test_eof145;
+case 145:
+ switch( (*p) ) {
+ case 86: goto st146;
+ case 118: goto st146;
+ }
+ goto tr38;
+st146:
+ if ( ++p == pe )
+ goto _test_eof146;
+case 146:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr603;
+ case 32: goto tr603;
+ case 40: goto tr604;
+ case 41: goto tr605;
+ case 3082: goto tr606;
+ case 3131: goto tr607;
+ case 3338: goto tr608;
+ case 3387: goto tr608;
+ case 3594: goto tr609;
+ case 3643: goto tr610;
+ }
+ goto tr61;
+st147:
+ if ( ++p == pe )
+ goto _test_eof147;
+case 147:
+ switch( (*p) ) {
+ case 72: goto st148;
+ case 104: goto st148;
+ }
+ goto tr38;
+st148:
+ if ( ++p == pe )
+ goto _test_eof148;
+case 148:
+ switch( (*p) ) {
+ case 70: goto st149;
+ case 102: goto st149;
+ }
+ goto tr38;
+st149:
+ if ( ++p == pe )
+ goto _test_eof149;
+case 149:
+ switch( (*p) ) {
+ case 80: goto st150;
+ case 112: goto st150;
+ }
+ goto tr38;
+st150:
+ if ( ++p == pe )
+ goto _test_eof150;
+case 150:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr614;
+ case 32: goto tr614;
+ case 40: goto tr615;
+ case 41: goto tr616;
+ case 3082: goto tr617;
+ case 3131: goto tr618;
+ case 3338: goto tr619;
+ case 3387: goto tr619;
+ case 3594: goto tr620;
+ case 3643: goto tr621;
+ }
+ goto tr61;
+st151:
+ if ( ++p == pe )
+ goto _test_eof151;
+case 151:
+ switch( (*p) ) {
+ case 67: goto st152;
+ case 99: goto st152;
+ }
+ goto tr38;
+st152:
+ if ( ++p == pe )
+ goto _test_eof152;
+case 152:
+ switch( (*p) ) {
+ case 66: goto st153;
+ case 98: goto st153;
+ }
+ goto tr38;
+st153:
+ if ( ++p == pe )
+ goto _test_eof153;
+case 153:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr624;
+ case 32: goto tr624;
+ case 40: goto tr625;
+ case 41: goto tr626;
+ case 3082: goto tr627;
+ case 3131: goto tr628;
+ case 3338: goto tr629;
+ case 3387: goto tr629;
+ case 3594: goto tr630;
+ case 3643: goto tr631;
+ }
+ goto tr61;
+tr19:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st154;
+tr56:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st154;
+tr722:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st154;
+st154:
+ if ( ++p == pe )
+ goto _test_eof154;
+case 154:
+ switch( (*p) ) {
+ case 76: goto st155;
+ case 88: goto st158;
+ case 89: goto st160;
+ case 108: goto st155;
+ case 120: goto st158;
+ case 121: goto st160;
+ }
+ goto tr38;
+st155:
+ if ( ++p == pe )
+ goto _test_eof155;
+case 155:
+ switch( (*p) ) {
+ case 83: goto st156;
+ case 115: goto st156;
+ }
+ goto tr38;
+st156:
+ if ( ++p == pe )
+ goto _test_eof156;
+case 156:
+ switch( (*p) ) {
+ case 65: goto st157;
+ case 97: goto st157;
+ }
+ goto tr38;
+st157:
+ if ( ++p == pe )
+ goto _test_eof157;
+case 157:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr637;
+ case 32: goto tr637;
+ case 40: goto tr638;
+ case 41: goto tr639;
+ case 3082: goto tr640;
+ case 3131: goto tr641;
+ case 3338: goto tr642;
+ case 3387: goto tr642;
+ case 3594: goto tr643;
+ case 3643: goto tr644;
+ }
+ goto tr61;
+st158:
+ if ( ++p == pe )
+ goto _test_eof158;
+case 158:
+ switch( (*p) ) {
+ case 84: goto st159;
+ case 116: goto st159;
+ }
+ goto tr38;
+st159:
+ if ( ++p == pe )
+ goto _test_eof159;
+case 159:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr646;
+ case 32: goto tr646;
+ case 40: goto tr647;
+ case 41: goto tr648;
+ case 3082: goto tr649;
+ case 3131: goto tr650;
+ case 3338: goto tr651;
+ case 3387: goto tr651;
+ case 3594: goto tr652;
+ case 3643: goto tr653;
+ }
+ goto tr61;
+st160:
+ if ( ++p == pe )
+ goto _test_eof160;
+case 160:
+ switch( (*p) ) {
+ case 80: goto st161;
+ case 112: goto st161;
+ }
+ goto tr38;
+st161:
+ if ( ++p == pe )
+ goto _test_eof161;
+case 161:
+ switch( (*p) ) {
+ case 69: goto st162;
+ case 101: goto st162;
+ }
+ goto tr38;
+st162:
+ if ( ++p == pe )
+ goto _test_eof162;
+case 162:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr657;
+ goto tr656;
+tr657:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st163;
+tr662:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st163;
+st163:
+ if ( ++p == pe )
+ goto _test_eof163;
+case 163:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr659;
+ case 32: goto tr659;
+ case 40: goto tr660;
+ case 41: goto tr661;
+ case 3082: goto tr663;
+ case 3131: goto tr664;
+ case 3338: goto tr665;
+ case 3387: goto tr665;
+ case 3594: goto tr666;
+ case 3643: goto tr667;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr662;
+ goto tr658;
+tr20:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st164;
+tr57:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st164;
+tr723:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st164;
+st164:
+ if ( ++p == pe )
+ goto _test_eof164;
+case 164:
+ switch( (*p) ) {
+ case 82: goto st165;
+ case 114: goto st165;
+ }
+ goto tr38;
+st165:
+ if ( ++p == pe )
+ goto _test_eof165;
+case 165:
+ switch( (*p) ) {
+ case 73: goto st166;
+ case 105: goto st166;
+ }
+ goto tr38;
+st166:
+ if ( ++p == pe )
+ goto _test_eof166;
+case 166:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr670;
+ case 32: goto tr670;
+ case 40: goto tr671;
+ case 41: goto tr672;
+ case 3082: goto tr673;
+ case 3131: goto tr674;
+ case 3338: goto tr675;
+ case 3387: goto tr675;
+ case 3594: goto tr676;
+ case 3643: goto tr677;
+ }
+ goto tr61;
+tr21:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st167;
+tr58:
+ {
+ s->r_class = s->default_class;
+ }
+ goto st167;
+tr724:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st167;
+st167:
+ if ( ++p == pe )
+ goto _test_eof167;
+case 167:
+ switch( (*p) ) {
+ case 79: goto st168;
+ case 111: goto st168;
+ }
+ goto tr38;
+st168:
+ if ( ++p == pe )
+ goto _test_eof168;
+case 168:
+ switch( (*p) ) {
+ case 78: goto st169;
+ case 110: goto st169;
+ }
+ goto tr38;
+st169:
+ if ( ++p == pe )
+ goto _test_eof169;
+case 169:
+ switch( (*p) ) {
+ case 69: goto st170;
+ case 101: goto st170;
+ }
+ goto tr38;
+st170:
+ if ( ++p == pe )
+ goto _test_eof170;
+case 170:
+ switch( (*p) ) {
+ case 77: goto st171;
+ case 109: goto st171;
+ }
+ goto tr38;
+st171:
+ if ( ++p == pe )
+ goto _test_eof171;
+case 171:
+ switch( (*p) ) {
+ case 68: goto st172;
+ case 100: goto st172;
+ }
+ goto tr38;
+st172:
+ if ( ++p == pe )
+ goto _test_eof172;
+case 172:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr683;
+ case 32: goto tr683;
+ case 40: goto tr684;
+ case 41: goto tr685;
+ case 3082: goto tr686;
+ case 3131: goto tr687;
+ case 3338: goto tr688;
+ case 3387: goto tr688;
+ case 3594: goto tr689;
+ case 3643: goto tr690;
+ }
+ goto tr61;
+tr352:
+ {
+ s->buffer_length = 0;
+ }
+ goto st173;
+tr737:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st173;
+tr691:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st173;
+tr330:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st173;
+tr750:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st173;
+st173:
+ if ( ++p == pe )
+ goto _test_eof173;
+case 173:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr692;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr691;
+ goto tr38;
+tr1002:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1398; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1398; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1398; goto _out;}
+ }
+ }
+ goto st1398;
+tr149:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1398; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1398; goto _out;}
+ }
+ }
+ goto st1398;
+tr155:
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1398; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1398; goto _out;}
+ }
+ }
+ goto st1398;
+st1398:
+ if ( ++p == pe )
+ goto _test_eof1398;
+case 1398:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4228;
+ case 32: goto tr4228;
+ case 36: goto st184;
+ case 40: goto tr4229;
+ case 41: goto tr4230;
+ case 42: goto tr4210;
+ case 65: goto tr4231;
+ case 67: goto tr4232;
+ case 68: goto tr4233;
+ case 69: goto tr4234;
+ case 72: goto tr4235;
+ case 73: goto tr4236;
+ case 75: goto tr4237;
+ case 76: goto tr4238;
+ case 77: goto tr4239;
+ case 78: goto tr4240;
+ case 79: goto tr4241;
+ case 80: goto tr4242;
+ case 82: goto tr4243;
+ case 83: goto tr4244;
+ case 84: goto tr4245;
+ case 85: goto tr4246;
+ case 90: goto tr4247;
+ case 92: goto tr4210;
+ case 95: goto tr4210;
+ case 97: goto tr4231;
+ case 99: goto tr4232;
+ case 100: goto tr4233;
+ case 101: goto tr4234;
+ case 104: goto tr4235;
+ case 105: goto tr4236;
+ case 107: goto tr4237;
+ case 108: goto tr4238;
+ case 109: goto tr4239;
+ case 110: goto tr4240;
+ case 111: goto tr4241;
+ case 112: goto tr4242;
+ case 114: goto tr4243;
+ case 115: goto tr4244;
+ case 116: goto tr4245;
+ case 117: goto tr4246;
+ case 122: goto tr4247;
+ case 1802: goto tr4211;
+ case 1851: goto tr4212;
+ case 2058: goto tr4248;
+ case 2107: goto tr4249;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4210;
+ } else if ( _widec > 89 ) {
+ if ( 98 <= _widec && _widec <= 121 )
+ goto tr4210;
+ } else
+ goto tr4210;
+ goto tr4227;
+tr694:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st174;
+tr695:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st174;
+tr4228:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st174;
+tr4229:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st174;
+tr4230:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st174;
+st174:
+ if ( ++p == pe )
+ goto _test_eof174;
+case 174:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st174;
+ case 32: goto st174;
+ case 40: goto tr694;
+ case 41: goto tr695;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 1802: goto tr22;
+ case 1851: goto tr23;
+ case 2058: goto tr696;
+ case 2107: goto tr697;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr0;
+tr10:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ goto st175;
+st175:
+ if ( ++p == pe )
+ goto _test_eof175;
+case 175:
+ switch( (*p) ) {
+ case 78: goto st176;
+ case 80: goto st74;
+ case 110: goto st176;
+ case 112: goto st74;
+ }
+ goto tr38;
+st176:
+ if ( ++p == pe )
+ goto _test_eof176;
+case 176:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr699;
+ case 32: goto tr699;
+ case 40: goto tr700;
+ case 41: goto tr701;
+ case 2058: goto tr702;
+ case 2107: goto tr703;
+ }
+ goto tr38;
+tr705:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st177;
+tr706:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st177;
+tr725:
+ {
+ s->line_counter++;
+ }
+ goto st177;
+tr752:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 177; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st177;
+tr699:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ goto st177;
+tr700:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st177;
+tr701:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st177;
+tr702:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st177;
+st177:
+ if ( ++p == pe )
+ goto _test_eof177;
+case 177:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st177;
+ case 32: goto st177;
+ case 40: goto tr705;
+ case 41: goto tr706;
+ case 65: goto tr708;
+ case 67: goto tr709;
+ case 68: goto tr710;
+ case 69: goto tr711;
+ case 72: goto tr712;
+ case 73: goto tr713;
+ case 75: goto tr714;
+ case 76: goto tr715;
+ case 77: goto tr716;
+ case 78: goto tr717;
+ case 79: goto tr718;
+ case 80: goto tr719;
+ case 82: goto tr720;
+ case 83: goto tr721;
+ case 84: goto tr722;
+ case 85: goto tr723;
+ case 90: goto tr724;
+ case 97: goto tr708;
+ case 99: goto tr709;
+ case 100: goto tr710;
+ case 101: goto tr711;
+ case 104: goto tr712;
+ case 105: goto tr713;
+ case 107: goto tr714;
+ case 108: goto tr715;
+ case 109: goto tr716;
+ case 110: goto tr717;
+ case 111: goto tr718;
+ case 112: goto tr719;
+ case 114: goto tr720;
+ case 115: goto tr721;
+ case 116: goto tr722;
+ case 117: goto tr723;
+ case 122: goto tr724;
+ case 2058: goto tr725;
+ case 2107: goto tr726;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr707;
+ goto tr656;
+tr707:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st178;
+tr730:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st178;
+st178:
+ if ( ++p == pe )
+ goto _test_eof178;
+case 178:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr727;
+ case 32: goto tr727;
+ case 40: goto tr728;
+ case 41: goto tr729;
+ case 68: goto tr731;
+ case 72: goto tr732;
+ case 77: goto tr733;
+ case 83: goto st179;
+ case 87: goto tr735;
+ case 100: goto tr731;
+ case 104: goto tr732;
+ case 109: goto tr733;
+ case 115: goto st179;
+ case 119: goto tr735;
+ case 2058: goto tr736;
+ case 2107: goto tr737;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr730;
+ goto tr26;
+tr731:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st179;
+tr732:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st179;
+tr733:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st179;
+tr735:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st179;
+st179:
+ if ( ++p == pe )
+ goto _test_eof179;
+case 179:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr727;
+ case 32: goto tr727;
+ case 40: goto tr728;
+ case 41: goto tr729;
+ case 2058: goto tr736;
+ case 2107: goto tr737;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr738;
+ goto tr26;
+tr739:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st180;
+tr738:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st180;
+tr748:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st180;
+st180:
+ if ( ++p == pe )
+ goto _test_eof180;
+case 180:
+ switch( (*p) ) {
+ case 68: goto tr740;
+ case 72: goto tr741;
+ case 77: goto tr742;
+ case 83: goto st181;
+ case 87: goto tr744;
+ case 100: goto tr740;
+ case 104: goto tr741;
+ case 109: goto tr742;
+ case 115: goto st181;
+ case 119: goto tr744;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr739;
+ goto tr26;
+tr740:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st181;
+tr741:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st181;
+tr742:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st181;
+tr744:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st181;
+st181:
+ if ( ++p == pe )
+ goto _test_eof181;
+case 181:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr745;
+ case 32: goto tr745;
+ case 40: goto tr746;
+ case 41: goto tr747;
+ case 2058: goto tr749;
+ case 2107: goto tr750;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr748;
+ goto tr26;
+tr726:
+ {
+ s->buffer_length = 0;
+ }
+ goto st182;
+tr751:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st182;
+tr703:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st182;
+st182:
+ if ( ++p == pe )
+ goto _test_eof182;
+case 182:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr752;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr751;
+ goto tr38;
+tr23:
+ {
+ s->buffer_length = 0;
+ }
+ goto st183;
+tr4212:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st183;
+tr753:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st183;
+tr4276:
+ {
+ NOERR;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st183;
+st183:
+ if ( ++p == pe )
+ goto _test_eof183;
+case 183:
+ if ( (*p) == 10 )
+ goto tr754;
+ goto tr753;
+tr696:
+ {
+ s->line_counter++;
+ }
+ goto st1399;
+tr1000:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1399; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1399;
+tr889:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1399; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1399; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1399; goto _out;}
+ }
+ }
+ goto st1399;
+tr886:
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1399; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1399; goto _out;}
+ }
+ }
+ goto st1399;
+tr881:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1399; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1399; goto _out;}
+ }
+ }
+ goto st1399;
+tr4248:
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1399;
+st1399:
+ if ( ++p == pe )
+ goto _test_eof1399;
+case 1399:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4228;
+ case 32: goto tr4228;
+ case 36: goto st184;
+ case 40: goto tr4229;
+ case 41: goto tr4230;
+ case 42: goto tr4210;
+ case 65: goto tr4252;
+ case 67: goto tr4253;
+ case 68: goto tr4254;
+ case 69: goto tr4255;
+ case 72: goto tr4256;
+ case 73: goto tr4257;
+ case 75: goto tr4258;
+ case 76: goto tr4259;
+ case 77: goto tr4260;
+ case 78: goto tr4261;
+ case 79: goto tr4262;
+ case 80: goto tr4263;
+ case 82: goto tr4264;
+ case 83: goto tr4265;
+ case 84: goto tr4266;
+ case 85: goto tr4267;
+ case 90: goto tr4268;
+ case 92: goto tr4210;
+ case 95: goto tr4210;
+ case 97: goto tr4252;
+ case 99: goto tr4253;
+ case 100: goto tr4254;
+ case 101: goto tr4255;
+ case 104: goto tr4256;
+ case 105: goto tr4257;
+ case 107: goto tr4258;
+ case 108: goto tr4259;
+ case 109: goto tr4260;
+ case 110: goto tr4261;
+ case 111: goto tr4262;
+ case 112: goto tr4263;
+ case 114: goto tr4264;
+ case 115: goto tr4265;
+ case 116: goto tr4266;
+ case 117: goto tr4267;
+ case 122: goto tr4268;
+ case 1802: goto tr4211;
+ case 1851: goto tr4212;
+ case 2058: goto tr4248;
+ case 2107: goto tr4249;
+ }
+ if ( _widec < 48 ) {
+ if ( 45 <= _widec && _widec <= 47 )
+ goto tr4210;
+ } else if ( _widec > 57 ) {
+ if ( _widec > 89 ) {
+ if ( 98 <= _widec && _widec <= 121 )
+ goto tr4210;
+ } else if ( _widec >= 64 )
+ goto tr4210;
+ } else
+ goto tr4251;
+ goto tr4250;
+tr4271:
+ {
+ NOERR;
+ }
+ goto st184;
+st184:
+ if ( ++p == pe )
+ goto _test_eof184;
+case 184:
+ switch( (*p) ) {
+ case 73: goto tr756;
+ case 79: goto tr757;
+ case 84: goto tr758;
+ case 105: goto tr756;
+ case 111: goto tr757;
+ case 116: goto tr758;
+ }
+ goto tr755;
+tr756:
+ {
+ ERR(ZS_OK);
+ }
+ goto st185;
+st185:
+ if ( ++p == pe )
+ goto _test_eof185;
+case 185:
+ switch( (*p) ) {
+ case 78: goto st186;
+ case 110: goto st186;
+ }
+ goto tr755;
+st186:
+ if ( ++p == pe )
+ goto _test_eof186;
+case 186:
+ switch( (*p) ) {
+ case 67: goto st187;
+ case 99: goto st187;
+ }
+ goto tr755;
+st187:
+ if ( ++p == pe )
+ goto _test_eof187;
+case 187:
+ switch( (*p) ) {
+ case 76: goto st188;
+ case 108: goto st188;
+ }
+ goto tr755;
+st188:
+ if ( ++p == pe )
+ goto _test_eof188;
+case 188:
+ switch( (*p) ) {
+ case 85: goto st189;
+ case 117: goto st189;
+ }
+ goto tr755;
+st189:
+ if ( ++p == pe )
+ goto _test_eof189;
+case 189:
+ switch( (*p) ) {
+ case 68: goto st190;
+ case 100: goto st190;
+ }
+ goto tr755;
+st190:
+ if ( ++p == pe )
+ goto _test_eof190;
+case 190:
+ switch( (*p) ) {
+ case 69: goto st191;
+ case 101: goto st191;
+ }
+ goto tr755;
+st191:
+ if ( ++p == pe )
+ goto _test_eof191;
+case 191:
+ switch( (*p) ) {
+ case 32: goto tr765;
+ case 59: goto tr765;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr765;
+ } else if ( (*p) >= 9 )
+ goto tr765;
+ goto tr755;
+tr765:
+ { p--; {stack[top++] = 1400;goto st352;} }
+ goto st1400;
+tr867:
+ { p--; {stack[top++] = 1400;goto st340;} }
+ goto st1400;
+tr870:
+ { p--; {stack[top++] = 1400;goto st331;} }
+ goto st1400;
+st1400:
+ if ( ++p == pe )
+ goto _test_eof1400;
+case 1400:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4270;
+ case 32: goto tr4270;
+ case 36: goto tr4271;
+ case 40: goto tr4272;
+ case 41: goto tr4273;
+ case 42: goto tr4274;
+ case 92: goto tr4274;
+ case 95: goto tr4274;
+ case 1802: goto tr4275;
+ case 1851: goto tr4276;
+ case 2058: goto tr4277;
+ case 2107: goto tr4278;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4274;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr4274;
+ } else
+ goto tr4274;
+ goto tr4269;
+tr4210:
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 192;goto st309;} }
+ goto st192;
+tr4274:
+ {
+ NOERR;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 192;goto st309;} }
+ goto st192;
+st192:
+ if ( ++p == pe )
+ goto _test_eof192;
+case 192:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr766;
+tr773:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st193;
+tr774:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st193;
+tr775:
+ {
+ s->line_counter++;
+ }
+ goto st193;
+tr779:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 193; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st193;
+tr767:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ goto st193;
+tr768:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st193;
+tr769:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st193;
+tr770:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st193;
+st193:
+ if ( ++p == pe )
+ goto _test_eof193;
+case 193:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st193;
+ case 32: goto st193;
+ case 40: goto tr773;
+ case 41: goto tr774;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 2058: goto tr775;
+ case 2107: goto tr776;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr656;
+tr776:
+ {
+ s->buffer_length = 0;
+ }
+ goto st194;
+tr777:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st194;
+tr771:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st194;
+st194:
+ if ( ++p == pe )
+ goto _test_eof194;
+case 194:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr779;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr777;
+ goto st0;
+tr24:
+ {
+ s->line_counter++;
+ }
+ goto st1401;
+tr998:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1401;
+tr844:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ goto st1401;
+tr841:
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ goto st1401;
+tr875:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ goto st1401;
+tr912:
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ goto st1401;
+tr915:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ goto st1401;
+tr4213:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1401;
+tr4281:
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1401;
+tr4277:
+ {
+ NOERR;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1401;
+tr4286:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st1401;
+tr4312:
+ {
+ s->line_counter++;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1401; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1401; goto _out;}
+ }
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st1401;
+st1401:
+ if ( ++p == pe )
+ goto _test_eof1401;
+case 1401:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4206;
+ case 32: goto tr4206;
+ case 36: goto st184;
+ case 40: goto tr4279;
+ case 41: goto tr4280;
+ case 42: goto tr4210;
+ case 65: goto tr4252;
+ case 67: goto tr4253;
+ case 68: goto tr4254;
+ case 69: goto tr4255;
+ case 72: goto tr4256;
+ case 73: goto tr4257;
+ case 75: goto tr4258;
+ case 76: goto tr4259;
+ case 77: goto tr4260;
+ case 78: goto tr4261;
+ case 79: goto tr4262;
+ case 80: goto tr4263;
+ case 82: goto tr4264;
+ case 83: goto tr4265;
+ case 84: goto tr4266;
+ case 85: goto tr4267;
+ case 90: goto tr4268;
+ case 92: goto tr4210;
+ case 95: goto tr4210;
+ case 97: goto tr4252;
+ case 99: goto tr4253;
+ case 100: goto tr4254;
+ case 101: goto tr4255;
+ case 104: goto tr4256;
+ case 105: goto tr4257;
+ case 107: goto tr4258;
+ case 108: goto tr4259;
+ case 109: goto tr4260;
+ case 110: goto tr4261;
+ case 111: goto tr4262;
+ case 112: goto tr4263;
+ case 114: goto tr4264;
+ case 115: goto tr4265;
+ case 116: goto tr4266;
+ case 117: goto tr4267;
+ case 122: goto tr4268;
+ case 1802: goto tr4211;
+ case 1851: goto tr4212;
+ case 2058: goto tr4281;
+ case 2107: goto tr4282;
+ }
+ if ( _widec < 48 ) {
+ if ( 45 <= _widec && _widec <= 47 )
+ goto tr4210;
+ } else if ( _widec > 57 ) {
+ if ( _widec > 89 ) {
+ if ( 98 <= _widec && _widec <= 121 )
+ goto tr4210;
+ } else if ( _widec >= 64 )
+ goto tr4210;
+ } else
+ goto tr4251;
+ goto tr4250;
+tr4251:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 195;goto st309;} }
+ goto st195;
+st195:
+ if ( ++p == pe )
+ goto _test_eof195;
+case 195:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr781;
+ case 32: goto tr781;
+ case 40: goto tr782;
+ case 41: goto tr783;
+ case 68: goto tr31;
+ case 72: goto tr32;
+ case 77: goto tr33;
+ case 83: goto st198;
+ case 87: goto tr35;
+ case 100: goto tr31;
+ case 104: goto tr32;
+ case 109: goto tr33;
+ case 115: goto st198;
+ case 119: goto tr35;
+ case 2058: goto tr784;
+ case 2107: goto tr785;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr30;
+ goto tr780;
+tr787:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st196;
+tr788:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st196;
+tr789:
+ {
+ s->line_counter++;
+ }
+ goto st196;
+tr792:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 196; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st196;
+tr781:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st196;
+tr782:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st196;
+tr783:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st196;
+tr784:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st196;
+st196:
+ if ( ++p == pe )
+ goto _test_eof196;
+case 196:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st196;
+ case 32: goto st196;
+ case 40: goto tr787;
+ case 41: goto tr788;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 2058: goto tr789;
+ case 2107: goto tr790;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr656;
+tr790:
+ {
+ s->buffer_length = 0;
+ }
+ goto st197;
+tr791:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st197;
+tr785:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st197;
+st197:
+ if ( ++p == pe )
+ goto _test_eof197;
+case 197:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr792;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr791;
+ goto tr38;
+tr31:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st198;
+tr32:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st198;
+tr33:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st198;
+tr35:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st198;
+st198:
+ if ( ++p == pe )
+ goto _test_eof198;
+case 198:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr27;
+ case 32: goto tr27;
+ case 40: goto tr28;
+ case 41: goto tr29;
+ case 2058: goto tr36;
+ case 2107: goto tr37;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr793;
+ goto tr26;
+tr794:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st199;
+tr793:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st199;
+tr803:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st199;
+st199:
+ if ( ++p == pe )
+ goto _test_eof199;
+case 199:
+ switch( (*p) ) {
+ case 68: goto tr795;
+ case 72: goto tr796;
+ case 77: goto tr797;
+ case 83: goto st200;
+ case 87: goto tr799;
+ case 100: goto tr795;
+ case 104: goto tr796;
+ case 109: goto tr797;
+ case 115: goto st200;
+ case 119: goto tr799;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr794;
+ goto tr26;
+tr795:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st200;
+tr796:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st200;
+tr797:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st200;
+tr799:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st200;
+st200:
+ if ( ++p == pe )
+ goto _test_eof200;
+case 200:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr800;
+ case 32: goto tr800;
+ case 40: goto tr801;
+ case 41: goto tr802;
+ case 2058: goto tr804;
+ case 2107: goto tr805;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr803;
+ goto tr26;
+tr60:
+ {
+ s->buffer_length = 0;
+ }
+ goto st201;
+tr37:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st201;
+tr806:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st201;
+tr805:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st201;
+st201:
+ if ( ++p == pe )
+ goto _test_eof201;
+case 201:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr807;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr806;
+ goto tr38;
+tr4231:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 202;goto st309;} }
+ goto st202;
+tr4252:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 202;goto st309;} }
+ goto st202;
+st202:
+ if ( ++p == pe )
+ goto _test_eof202;
+case 202:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr809;
+ case 32: goto tr809;
+ case 40: goto tr810;
+ case 41: goto tr811;
+ case 65: goto st266;
+ case 70: goto st269;
+ case 80: goto st273;
+ case 97: goto st266;
+ case 102: goto st269;
+ case 112: goto st273;
+ case 3082: goto tr812;
+ case 3131: goto tr813;
+ case 3338: goto tr70;
+ case 3387: goto tr70;
+ case 3594: goto tr814;
+ case 3643: goto tr815;
+ }
+ goto tr808;
+tr817:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st203;
+tr818:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st203;
+tr819:
+ {
+ s->line_counter++;
+ }
+ goto st203;
+tr984:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 203; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st203;
+tr809:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st203;
+tr810:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st203;
+tr811:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st203;
+tr812:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st203;
+st203:
+ if ( ++p == pe )
+ goto _test_eof203;
+case 203:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st203;
+ case 32: goto st203;
+ case 40: goto tr817;
+ case 41: goto tr818;
+ case 58: goto tr73;
+ case 65: goto tr122;
+ case 67: goto tr123;
+ case 68: goto tr124;
+ case 69: goto tr125;
+ case 72: goto tr126;
+ case 73: goto tr127;
+ case 75: goto tr128;
+ case 76: goto tr129;
+ case 77: goto tr130;
+ case 78: goto tr131;
+ case 79: goto tr132;
+ case 80: goto tr133;
+ case 82: goto tr134;
+ case 83: goto tr135;
+ case 84: goto tr136;
+ case 85: goto tr137;
+ case 90: goto tr138;
+ case 92: goto tr78;
+ case 97: goto tr122;
+ case 99: goto tr123;
+ case 100: goto tr124;
+ case 101: goto tr125;
+ case 104: goto tr126;
+ case 105: goto tr127;
+ case 107: goto tr128;
+ case 108: goto tr129;
+ case 109: goto tr130;
+ case 110: goto tr131;
+ case 111: goto tr132;
+ case 112: goto tr133;
+ case 114: goto tr134;
+ case 115: goto tr135;
+ case 116: goto tr136;
+ case 117: goto tr137;
+ case 122: goto tr138;
+ case 3082: goto tr819;
+ case 3131: goto tr820;
+ case 3338: goto tr73;
+ case 3387: goto tr73;
+ case 3594: goto tr821;
+ case 3643: goto tr822;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 47 ) {
+ if ( _widec > 57 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 48 )
+ goto tr121;
+ } else
+ goto tr73;
+ goto tr658;
+tr122:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 204;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 204;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 204;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 204;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 204;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 204;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 204;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 204;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 204;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 204;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 204;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 204;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 204;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 204;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 204;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 204;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 204;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 204;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 204;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 204;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 204;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 204;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 204;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 204;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 204;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 204;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 204;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 204;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 204;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 204;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 204;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 204;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st204;
+st204:
+ if ( ++p == pe )
+ goto _test_eof204;
+case 204:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr824;
+ case 32: goto tr824;
+ case 40: goto tr825;
+ case 41: goto tr826;
+ case 65: goto st266;
+ case 70: goto st269;
+ case 80: goto st273;
+ case 97: goto st266;
+ case 102: goto st269;
+ case 112: goto st273;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr827;
+ case 3131: goto tr828;
+ case 3338: goto tr829;
+ case 3387: goto tr830;
+ case 3594: goto tr831;
+ case 3643: goto tr832;
+ }
+ goto tr823;
+tr834:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st205;
+tr835:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st205;
+tr824:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st205;
+tr825:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st205;
+tr826:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st205;
+st205:
+ if ( ++p == pe )
+ goto _test_eof205;
+case 205:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st205;
+ case 32: goto st205;
+ case 40: goto tr834;
+ case 41: goto tr835;
+ case 92: goto tr78;
+ case 2826: goto tr93;
+ case 2875: goto tr94;
+ case 3082: goto tr836;
+ case 3131: goto tr111;
+ case 3338: goto tr112;
+ case 3387: goto tr113;
+ case 3594: goto tr837;
+ case 3643: goto tr114;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr83;
+tr105:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1402;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1402;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1402;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1402;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1402;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1402;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1402;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1402;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1402;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1402;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1402;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1402;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1402;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1402;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1402;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1402;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1402;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1402;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1402;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1402;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1402;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1402;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1402;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1402;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1402;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1402;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1402;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1402;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1402;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1402;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1402;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1402;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1402; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1402; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1402;
+tr112:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1402;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1402;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1402;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1402;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1402;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1402;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1402;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1402;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1402;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1402;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1402;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1402;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1402;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1402;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1402;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1402;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1402;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1402;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1402;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1402;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1402;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1402;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1402;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1402;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1402;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1402;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1402;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1402;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1402;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1402;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1402;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1402;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1402; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1402; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1402;
+tr141:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1402;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1402;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1402;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1402;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1402;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1402;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1402;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1402;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1402;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1402;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1402;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1402;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1402;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1402;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1402;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1402;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1402;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1402;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1402;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1402;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1402;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1402;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1402;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1402;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1402;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1402;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1402;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1402;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1402;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1402;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1402;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1402;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1402;
+tr829:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1402;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1402;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1402;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1402;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1402;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1402;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1402;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1402;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1402;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1402;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1402;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1402;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1402;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1402;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1402;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1402;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1402;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1402;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1402;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1402;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1402;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1402;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1402;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1402;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1402;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1402;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1402;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1402;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1402;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1402;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1402;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1402;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1402; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1402; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1402;
+tr4223:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1402;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1402;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1402;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1402;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1402;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1402;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1402;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1402;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1402;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1402;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1402;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1402;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1402;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1402;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1402;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1402;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1402;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1402;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1402;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1402;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1402;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1402;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1402;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1402;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1402;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1402;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1402;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1402;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1402;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1402;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1402;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1402;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1402;
+st1402:
+ if ( ++p == pe )
+ goto _test_eof1402;
+case 1402:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4283;
+ case 32: goto tr4283;
+ case 36: goto st184;
+ case 40: goto tr4284;
+ case 41: goto tr4285;
+ case 42: goto tr4210;
+ case 92: goto tr4210;
+ case 95: goto tr4210;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr4286;
+ case 2107: goto tr4287;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4210;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr4210;
+ } else
+ goto tr4210;
+ goto tr871;
+tr839:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st206;
+tr840:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st206;
+tr909:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr910:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr911:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr872:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr873:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr874:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st206;
+tr4283:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st206;
+tr4284:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st206;
+tr4285:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st206;
+st206:
+ if ( ++p == pe )
+ goto _test_eof206;
+case 206:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st206;
+ case 32: goto st206;
+ case 40: goto tr839;
+ case 41: goto tr840;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 1802: goto tr93;
+ case 1851: goto tr94;
+ case 2058: goto tr841;
+ case 2107: goto tr842;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr0;
+tr842:
+ {
+ s->buffer_length = 0;
+ }
+ goto st207;
+tr843:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st207;
+tr913:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st207;
+tr918:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st207;
+tr922:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st207;
+tr876:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st207;
+tr4287:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st207;
+st207:
+ if ( ++p == pe )
+ goto _test_eof207;
+case 207:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr96;
+ case 2058: goto tr844;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr843;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr89;
+tr106:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 208;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 208;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 208;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 208;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 208;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 208;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 208;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 208;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 208;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 208;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 208;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 208;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 208;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 208;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 208;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 208;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 208;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 208;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 208;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 208;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 208;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 208;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 208;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 208;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 208;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 208;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 208;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 208;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 208;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 208;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 208;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 208;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st208;
+tr113:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 208;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 208;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 208;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 208;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 208;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 208;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 208;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 208;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 208;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 208;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 208;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 208;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 208;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 208;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 208;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 208;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 208;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 208;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 208;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 208;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 208;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 208;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 208;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 208;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 208;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 208;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 208;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 208;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 208;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 208;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 208;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 208;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st208;
+tr830:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 208;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 208;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 208;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 208;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 208;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 208;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 208;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 208;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 208;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 208;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 208;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 208;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 208;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 208;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 208;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 208;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 208;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 208;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 208;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 208;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 208;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 208;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 208;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 208;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 208;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 208;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 208;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 208;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 208;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 208;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 208;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 208;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st208;
+st208:
+ if ( ++p == pe )
+ goto _test_eof208;
+case 208:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr845;
+ case 32: goto tr845;
+ case 40: goto tr846;
+ case 41: goto tr847;
+ case 1802: goto tr848;
+ case 1851: goto tr849;
+ case 2058: goto tr848;
+ case 2107: goto tr849;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr95;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr95;
+ } else
+ goto tr95;
+ goto tr83;
+tr850:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+tr845:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+tr846:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+tr847:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+tr851:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+tr852:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st209;
+st209:
+ if ( ++p == pe )
+ goto _test_eof209;
+case 209:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr850;
+ case 32: goto tr850;
+ case 40: goto tr851;
+ case 41: goto tr852;
+ case 1802: goto tr96;
+ case 1851: goto tr853;
+ case 2058: goto tr96;
+ case 2107: goto tr853;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr95;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr95;
+ } else
+ goto tr95;
+ goto tr89;
+tr831:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1403;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1403;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1403;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1403;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1403;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1403;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1403;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1403;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1403;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1403;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1403;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1403;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1403;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1403;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1403;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1403;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1403;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1403;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1403;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1403;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1403;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1403;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1403;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1403;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1403;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1403;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1403;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1403;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1403;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1403;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1403;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1403;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1403; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1403; goto _out;}
+ }
+ }
+ goto st1403;
+tr837:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1403;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1403;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1403;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1403;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1403;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1403;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1403;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1403;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1403;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1403;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1403;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1403;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1403;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1403;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1403;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1403;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1403;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1403;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1403;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1403;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1403;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1403;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1403;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1403;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1403;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1403;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1403;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1403;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1403;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1403;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1403;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1403;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1403; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1403; goto _out;}
+ }
+ }
+ goto st1403;
+st1403:
+ if ( ++p == pe )
+ goto _test_eof1403;
+case 1403:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr4288;
+ case 32: goto tr4288;
+ case 36: goto tr4216;
+ case 40: goto tr4289;
+ case 41: goto tr4290;
+ case 42: goto tr4219;
+ case 58: goto tr73;
+ case 92: goto tr4220;
+ case 95: goto tr4219;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr4286;
+ case 3131: goto tr4291;
+ case 3338: goto tr105;
+ case 3387: goto tr106;
+ case 3594: goto tr4286;
+ case 3643: goto tr4292;
+ }
+ if ( _widec < 60 ) {
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 44 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else if ( _widec > 63 ) {
+ if ( _widec < 91 ) {
+ if ( 64 <= _widec && _widec <= 90 )
+ goto tr4219;
+ } else if ( _widec > 96 ) {
+ if ( _widec > 122 ) {
+ if ( 123 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 97 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr871;
+tr855:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st210;
+tr856:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st210;
+tr985:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st210;
+tr986:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st210;
+tr987:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st210;
+tr4288:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st210;
+tr4289:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st210;
+tr4290:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st210;
+st210:
+ if ( ++p == pe )
+ goto _test_eof210;
+case 210:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st210;
+ case 32: goto st210;
+ case 40: goto tr855;
+ case 41: goto tr856;
+ case 58: goto tr73;
+ case 65: goto tr122;
+ case 67: goto tr123;
+ case 68: goto tr124;
+ case 69: goto tr125;
+ case 72: goto tr126;
+ case 73: goto tr127;
+ case 75: goto tr128;
+ case 76: goto tr129;
+ case 77: goto tr130;
+ case 78: goto tr131;
+ case 79: goto tr132;
+ case 80: goto tr133;
+ case 82: goto tr134;
+ case 83: goto tr135;
+ case 84: goto tr136;
+ case 85: goto tr137;
+ case 90: goto tr138;
+ case 92: goto tr78;
+ case 97: goto tr122;
+ case 99: goto tr123;
+ case 100: goto tr124;
+ case 101: goto tr125;
+ case 104: goto tr126;
+ case 105: goto tr127;
+ case 107: goto tr128;
+ case 108: goto tr129;
+ case 109: goto tr130;
+ case 110: goto tr131;
+ case 111: goto tr132;
+ case 112: goto tr133;
+ case 114: goto tr134;
+ case 115: goto tr135;
+ case 116: goto tr136;
+ case 117: goto tr137;
+ case 122: goto tr138;
+ case 2826: goto tr93;
+ case 2875: goto tr94;
+ case 3082: goto tr841;
+ case 3131: goto tr857;
+ case 3338: goto tr112;
+ case 3387: goto tr113;
+ case 3594: goto tr841;
+ case 3643: goto tr858;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 47 ) {
+ if ( _widec > 57 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 48 )
+ goto tr121;
+ } else
+ goto tr73;
+ goto tr118;
+tr123:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 211;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 211;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 211;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 211;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 211;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 211;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 211;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 211;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 211;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 211;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 211;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 211;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 211;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 211;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 211;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 211;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 211;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 211;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 211;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 211;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 211;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 211;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 211;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 211;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 211;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 211;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 211;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 211;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 211;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 211;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 211;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 211;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st211;
+st211:
+ if ( ++p == pe )
+ goto _test_eof211;
+case 211:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 65: goto st18;
+ case 68: goto st22;
+ case 69: goto st29;
+ case 78: goto st32;
+ case 83: goto st36;
+ case 97: goto st18;
+ case 100: goto st22;
+ case 101: goto st29;
+ case 110: goto st32;
+ case 115: goto st36;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr124:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 212;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 212;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 212;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 212;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 212;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 212;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 212;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 212;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 212;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 212;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 212;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 212;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 212;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 212;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 212;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 212;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 212;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 212;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 212;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 212;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 212;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 212;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 212;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 212;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 212;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 212;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 212;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 212;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 212;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 212;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 212;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 212;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st212;
+st212:
+ if ( ++p == pe )
+ goto _test_eof212;
+case 212:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 72: goto st41;
+ case 78: goto st45;
+ case 83: goto st53;
+ case 104: goto st41;
+ case 110: goto st45;
+ case 115: goto st53;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr125:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 213;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 213;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 213;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 213;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 213;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 213;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 213;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 213;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 213;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 213;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 213;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 213;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 213;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 213;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 213;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 213;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 213;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 213;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 213;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 213;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 213;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 213;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 213;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 213;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 213;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 213;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 213;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 213;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 213;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 213;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 213;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 213;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st213;
+st213:
+ if ( ++p == pe )
+ goto _test_eof213;
+case 213:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 85: goto st55;
+ case 117: goto st55;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr126:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 214;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 214;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 214;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 214;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 214;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 214;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 214;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 214;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 214;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 214;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 214;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 214;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 214;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 214;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 214;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 214;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 214;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 214;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 214;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 214;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 214;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 214;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 214;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 214;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 214;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 214;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 214;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 214;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 214;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 214;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 214;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 214;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st214;
+st214:
+ if ( ++p == pe )
+ goto _test_eof214;
+case 214:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 73: goto st62;
+ case 84: goto st66;
+ case 105: goto st62;
+ case 116: goto st66;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr127:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 215;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 215;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 215;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 215;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 215;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 215;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 215;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 215;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 215;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 215;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 215;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 215;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 215;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 215;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 215;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 215;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 215;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 215;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 215;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 215;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 215;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 215;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 215;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 215;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 215;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 215;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 215;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 215;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 215;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 215;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 215;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 215;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st215;
+st215:
+ if ( ++p == pe )
+ goto _test_eof215;
+case 215:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 78: goto st176;
+ case 80: goto st74;
+ case 110: goto st176;
+ case 112: goto st74;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr128:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 216;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 216;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 216;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 216;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 216;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 216;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 216;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 216;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 216;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 216;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 216;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 216;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 216;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 216;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 216;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 216;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 216;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 216;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 216;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 216;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 216;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 216;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 216;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 216;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 216;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 216;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 216;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 216;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 216;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 216;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 216;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 216;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st216;
+st216:
+ if ( ++p == pe )
+ goto _test_eof216;
+case 216:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 69: goto st82;
+ case 88: goto st84;
+ case 101: goto st82;
+ case 120: goto st84;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr129:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 217;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 217;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 217;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 217;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 217;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 217;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 217;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 217;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 217;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 217;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 217;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 217;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 217;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 217;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 217;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 217;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 217;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 217;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 217;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 217;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 217;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 217;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 217;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 217;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 217;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 217;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 217;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 217;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 217;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 217;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 217;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 217;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st217;
+st217:
+ if ( ++p == pe )
+ goto _test_eof217;
+case 217:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 51: goto st86;
+ case 54: goto st88;
+ case 79: goto st90;
+ case 80: goto st92;
+ case 111: goto st90;
+ case 112: goto st92;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr130:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 218;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 218;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 218;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 218;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 218;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 218;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 218;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 218;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 218;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 218;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 218;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 218;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 218;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 218;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 218;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 218;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 218;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 218;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 218;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 218;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 218;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 218;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 218;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 218;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 218;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 218;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 218;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 218;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 218;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 218;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 218;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 218;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st218;
+st218:
+ if ( ++p == pe )
+ goto _test_eof218;
+case 218:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 73: goto st94;
+ case 88: goto st98;
+ case 105: goto st94;
+ case 120: goto st98;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr131:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 219;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 219;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 219;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 219;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 219;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 219;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 219;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 219;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 219;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 219;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 219;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 219;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 219;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 219;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 219;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 219;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 219;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 219;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 219;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 219;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 219;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 219;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 219;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 219;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 219;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 219;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 219;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 219;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 219;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 219;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 219;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 219;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st219;
+st219:
+ if ( ++p == pe )
+ goto _test_eof219;
+case 219:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 65: goto st100;
+ case 73: goto st104;
+ case 83: goto st106;
+ case 97: goto st100;
+ case 105: goto st104;
+ case 115: goto st106;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr132:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 220;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 220;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 220;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 220;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 220;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 220;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 220;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 220;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 220;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 220;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 220;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 220;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 220;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 220;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 220;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 220;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 220;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 220;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 220;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 220;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 220;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 220;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 220;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 220;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 220;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 220;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 220;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 220;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 220;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 220;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 220;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 220;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st220;
+st220:
+ if ( ++p == pe )
+ goto _test_eof220;
+case 220:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 80: goto st116;
+ case 112: goto st116;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr133:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 221;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 221;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 221;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 221;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 221;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 221;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 221;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 221;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 221;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 221;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 221;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 221;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 221;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 221;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 221;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 221;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 221;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 221;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 221;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 221;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 221;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 221;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 221;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 221;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 221;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 221;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 221;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 221;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 221;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 221;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 221;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 221;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st221;
+st221:
+ if ( ++p == pe )
+ goto _test_eof221;
+case 221:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 84: goto st126;
+ case 116: goto st126;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr134:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 222;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 222;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 222;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 222;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 222;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 222;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 222;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 222;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 222;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 222;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 222;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 222;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 222;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 222;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 222;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 222;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 222;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 222;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 222;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 222;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 222;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 222;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 222;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 222;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 222;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 222;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 222;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 222;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 222;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 222;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 222;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 222;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st222;
+st222:
+ if ( ++p == pe )
+ goto _test_eof222;
+case 222:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 80: goto st129;
+ case 82: goto st130;
+ case 84: goto st134;
+ case 112: goto st129;
+ case 114: goto st130;
+ case 116: goto st134;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr135:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 223;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 223;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 223;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 223;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 223;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 223;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 223;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 223;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 223;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 223;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 223;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 223;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 223;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 223;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 223;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 223;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 223;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 223;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 223;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 223;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 223;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 223;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 223;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 223;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 223;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 223;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 223;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 223;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 223;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 223;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 223;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 223;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st223;
+st223:
+ if ( ++p == pe )
+ goto _test_eof223;
+case 223:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 77: goto st136;
+ case 79: goto st141;
+ case 80: goto st143;
+ case 82: goto st145;
+ case 83: goto st147;
+ case 86: goto st151;
+ case 109: goto st136;
+ case 111: goto st141;
+ case 112: goto st143;
+ case 114: goto st145;
+ case 115: goto st147;
+ case 118: goto st151;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr136:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 224;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 224;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 224;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 224;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 224;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 224;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 224;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 224;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 224;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 224;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 224;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 224;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 224;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 224;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 224;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 224;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 224;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 224;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 224;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 224;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 224;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 224;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 224;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 224;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 224;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 224;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 224;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 224;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 224;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 224;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 224;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 224;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st224;
+st224:
+ if ( ++p == pe )
+ goto _test_eof224;
+case 224:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 76: goto st155;
+ case 88: goto st158;
+ case 89: goto st160;
+ case 108: goto st155;
+ case 120: goto st158;
+ case 121: goto st160;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr137:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 225;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 225;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 225;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 225;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 225;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 225;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 225;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 225;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 225;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 225;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 225;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 225;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 225;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 225;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 225;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 225;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 225;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 225;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 225;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 225;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 225;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 225;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 225;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 225;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 225;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 225;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 225;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 225;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 225;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 225;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 225;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 225;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st225;
+st225:
+ if ( ++p == pe )
+ goto _test_eof225;
+case 225:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 82: goto st165;
+ case 114: goto st165;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr138:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 226;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 226;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 226;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 226;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 226;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 226;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 226;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 226;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 226;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 226;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 226;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 226;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 226;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 226;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 226;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 226;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 226;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 226;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 226;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 226;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 226;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 226;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 226;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 226;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 226;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 226;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 226;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 226;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 226;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 226;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 226;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 226;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st226;
+st226:
+ if ( ++p == pe )
+ goto _test_eof226;
+case 226:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 79: goto st168;
+ case 111: goto st168;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr823;
+tr857:
+ {
+ s->buffer_length = 0;
+ }
+ goto st227;
+tr859:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st227;
+tr988:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st227;
+tr926:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st227;
+tr930:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st227;
+tr895:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st227;
+tr4291:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st227;
+st227:
+ if ( ++p == pe )
+ goto _test_eof227;
+case 227:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr96;
+ case 2058: goto tr860;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr859;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+tr139:
+ {
+ s->line_counter++;
+ }
+ goto st1404;
+tr962:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1404; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1404;
+tr860:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1404; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1404; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1404; goto _out;}
+ }
+ }
+ goto st1404;
+tr901:
+ {
+ s->line_counter++;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1404; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1404; goto _out;}
+ }
+ }
+ goto st1404;
+tr894:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1404; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1404; goto _out;}
+ }
+ }
+ goto st1404;
+tr4221:
+ {
+ s->line_counter++;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1404;
+st1404:
+ if ( ++p == pe )
+ goto _test_eof1404;
+case 1404:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr4215;
+ case 32: goto tr4215;
+ case 36: goto tr4216;
+ case 40: goto tr4217;
+ case 41: goto tr4218;
+ case 42: goto tr4219;
+ case 58: goto tr73;
+ case 65: goto tr4295;
+ case 67: goto tr4296;
+ case 68: goto tr4297;
+ case 69: goto tr4298;
+ case 72: goto tr4299;
+ case 73: goto tr4300;
+ case 75: goto tr4301;
+ case 76: goto tr4302;
+ case 77: goto tr4303;
+ case 78: goto tr4304;
+ case 79: goto tr4305;
+ case 80: goto tr4306;
+ case 82: goto tr4307;
+ case 83: goto tr4308;
+ case 84: goto tr4309;
+ case 85: goto tr4310;
+ case 90: goto tr4311;
+ case 92: goto tr4220;
+ case 95: goto tr4219;
+ case 97: goto tr4295;
+ case 99: goto tr4296;
+ case 100: goto tr4297;
+ case 101: goto tr4298;
+ case 104: goto tr4299;
+ case 105: goto tr4300;
+ case 107: goto tr4301;
+ case 108: goto tr4302;
+ case 109: goto tr4303;
+ case 110: goto tr4304;
+ case 111: goto tr4305;
+ case 112: goto tr4306;
+ case 114: goto tr4307;
+ case 115: goto tr4308;
+ case 116: goto tr4309;
+ case 117: goto tr4310;
+ case 122: goto tr4311;
+ case 2826: goto tr4211;
+ case 2875: goto tr4212;
+ case 3082: goto tr4221;
+ case 3131: goto tr4222;
+ case 3338: goto tr4223;
+ case 3387: goto tr4224;
+ case 3594: goto tr4225;
+ case 3643: goto tr4226;
+ }
+ if ( _widec < 60 ) {
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 44 ) {
+ if ( _widec > 47 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4294;
+ } else if ( _widec >= 45 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else if ( _widec > 63 ) {
+ if ( _widec < 91 ) {
+ if ( 64 <= _widec && _widec <= 89 )
+ goto tr4219;
+ } else if ( _widec > 96 ) {
+ if ( _widec > 121 ) {
+ if ( 123 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 98 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr4293;
+tr4216:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 228;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 228;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 228;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 228;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 228;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 228;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 228;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 228;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 228;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 228;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 228;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 228;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 228;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 228;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 228;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 228;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 228;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 228;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 228;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 228;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 228;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 228;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 228;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 228;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 228;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 228;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 228;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 228;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 228;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 228;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 228;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 228;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st228;
+st228:
+ if ( ++p == pe )
+ goto _test_eof228;
+case 228:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr84;
+ case 32: goto tr84;
+ case 40: goto tr85;
+ case 41: goto tr86;
+ case 73: goto tr756;
+ case 79: goto tr757;
+ case 84: goto tr758;
+ case 105: goto tr756;
+ case 111: goto tr757;
+ case 116: goto tr758;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr87;
+ case 2107: goto tr88;
+ }
+ goto tr861;
+tr757:
+ {
+ ERR(ZS_OK);
+ }
+ goto st229;
+st229:
+ if ( ++p == pe )
+ goto _test_eof229;
+case 229:
+ switch( (*p) ) {
+ case 82: goto st230;
+ case 114: goto st230;
+ }
+ goto tr755;
+st230:
+ if ( ++p == pe )
+ goto _test_eof230;
+case 230:
+ switch( (*p) ) {
+ case 73: goto st231;
+ case 105: goto st231;
+ }
+ goto tr755;
+st231:
+ if ( ++p == pe )
+ goto _test_eof231;
+case 231:
+ switch( (*p) ) {
+ case 71: goto st232;
+ case 103: goto st232;
+ }
+ goto tr755;
+st232:
+ if ( ++p == pe )
+ goto _test_eof232;
+case 232:
+ switch( (*p) ) {
+ case 73: goto st233;
+ case 105: goto st233;
+ }
+ goto tr755;
+st233:
+ if ( ++p == pe )
+ goto _test_eof233;
+case 233:
+ switch( (*p) ) {
+ case 78: goto st234;
+ case 110: goto st234;
+ }
+ goto tr755;
+st234:
+ if ( ++p == pe )
+ goto _test_eof234;
+case 234:
+ switch( (*p) ) {
+ case 32: goto tr867;
+ case 59: goto tr867;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr867;
+ } else if ( (*p) >= 9 )
+ goto tr867;
+ goto tr755;
+tr758:
+ {
+ ERR(ZS_OK);
+ }
+ goto st235;
+st235:
+ if ( ++p == pe )
+ goto _test_eof235;
+case 235:
+ switch( (*p) ) {
+ case 84: goto st236;
+ case 116: goto st236;
+ }
+ goto tr755;
+st236:
+ if ( ++p == pe )
+ goto _test_eof236;
+case 236:
+ switch( (*p) ) {
+ case 76: goto st237;
+ case 108: goto st237;
+ }
+ goto tr755;
+st237:
+ if ( ++p == pe )
+ goto _test_eof237;
+case 237:
+ switch( (*p) ) {
+ case 32: goto tr870;
+ case 59: goto tr870;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr870;
+ } else if ( (*p) >= 9 )
+ goto tr870;
+ goto tr755;
+tr4219:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 238;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 238;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 238;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 238;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 238;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 238;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 238;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 238;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 238;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 238;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 238;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 238;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 238;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 238;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 238;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 238;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 238;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 238;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 238;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 238;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 238;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 238;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 238;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 238;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 238;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 238;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 238;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 238;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 238;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 238;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 238;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 238;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 238;goto st309;} }
+ goto st238;
+st238:
+ if ( ++p == pe )
+ goto _test_eof238;
+case 238:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr871;
+tr4294:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 239;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 239;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 239;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 239;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 239;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 239;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 239;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 239;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 239;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 239;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 239;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 239;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 239;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 239;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 239;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 239;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 239;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 239;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 239;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 239;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 239;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 239;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 239;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 239;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 239;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 239;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 239;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 239;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 239;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 239;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 239;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 239;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 239;goto st309;} }
+ goto st239;
+st239:
+ if ( ++p == pe )
+ goto _test_eof239;
+case 239:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr878;
+ case 32: goto tr878;
+ case 40: goto tr879;
+ case 41: goto tr880;
+ case 68: goto tr31;
+ case 72: goto tr32;
+ case 77: goto tr33;
+ case 83: goto st198;
+ case 87: goto tr35;
+ case 100: goto tr31;
+ case 104: goto tr32;
+ case 109: goto tr33;
+ case 115: goto st198;
+ case 119: goto tr35;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr881;
+ case 2107: goto tr882;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr30;
+ goto tr877;
+tr884:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st240;
+tr885:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st240;
+tr878:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st240;
+tr879:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st240;
+tr880:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st240;
+st240:
+ if ( ++p == pe )
+ goto _test_eof240;
+case 240:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st240;
+ case 32: goto st240;
+ case 40: goto tr884;
+ case 41: goto tr885;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 1802: goto tr93;
+ case 1851: goto tr94;
+ case 2058: goto tr886;
+ case 2107: goto tr887;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr0;
+tr887:
+ {
+ s->buffer_length = 0;
+ }
+ goto st241;
+tr888:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st241;
+tr882:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st241;
+st241:
+ if ( ++p == pe )
+ goto _test_eof241;
+case 241:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr96;
+ case 2058: goto tr889;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr888;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr151;
+tr4295:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 242;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 242;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 242;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 242;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 242;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 242;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 242;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 242;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 242;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 242;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 242;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 242;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 242;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 242;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 242;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 242;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 242;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 242;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 242;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 242;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 242;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 242;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 242;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 242;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 242;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 242;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 242;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 242;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 242;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 242;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 242;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 242;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 242;goto st309;} }
+ goto st242;
+st242:
+ if ( ++p == pe )
+ goto _test_eof242;
+case 242:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr891;
+ case 32: goto tr891;
+ case 40: goto tr892;
+ case 41: goto tr893;
+ case 65: goto st266;
+ case 70: goto st269;
+ case 80: goto st273;
+ case 97: goto st266;
+ case 102: goto st269;
+ case 112: goto st273;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr894;
+ case 3131: goto tr895;
+ case 3338: goto tr829;
+ case 3387: goto tr830;
+ case 3594: goto tr896;
+ case 3643: goto tr897;
+ }
+ goto tr890;
+tr899:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st243;
+tr900:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st243;
+tr891:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st243;
+tr892:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st243;
+tr893:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st243;
+st243:
+ if ( ++p == pe )
+ goto _test_eof243;
+case 243:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st243;
+ case 32: goto st243;
+ case 40: goto tr899;
+ case 41: goto tr900;
+ case 58: goto tr73;
+ case 65: goto tr122;
+ case 67: goto tr123;
+ case 68: goto tr124;
+ case 69: goto tr125;
+ case 72: goto tr126;
+ case 73: goto tr127;
+ case 75: goto tr128;
+ case 76: goto tr129;
+ case 77: goto tr130;
+ case 78: goto tr131;
+ case 79: goto tr132;
+ case 80: goto tr133;
+ case 82: goto tr134;
+ case 83: goto tr135;
+ case 84: goto tr136;
+ case 85: goto tr137;
+ case 90: goto tr138;
+ case 92: goto tr78;
+ case 97: goto tr122;
+ case 99: goto tr123;
+ case 100: goto tr124;
+ case 101: goto tr125;
+ case 104: goto tr126;
+ case 105: goto tr127;
+ case 107: goto tr128;
+ case 108: goto tr129;
+ case 109: goto tr130;
+ case 110: goto tr131;
+ case 111: goto tr132;
+ case 112: goto tr133;
+ case 114: goto tr134;
+ case 115: goto tr135;
+ case 116: goto tr136;
+ case 117: goto tr137;
+ case 122: goto tr138;
+ case 2826: goto tr93;
+ case 2875: goto tr94;
+ case 3082: goto tr901;
+ case 3131: goto tr857;
+ case 3338: goto tr112;
+ case 3387: goto tr113;
+ case 3594: goto tr902;
+ case 3643: goto tr858;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 47 ) {
+ if ( _widec > 57 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 48 )
+ goto tr121;
+ } else
+ goto tr73;
+ goto tr118;
+tr143:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1405;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1405;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1405;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1405;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1405;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1405;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1405;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1405;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1405;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1405;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1405;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1405;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1405;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1405;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1405;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1405;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1405;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1405;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1405;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1405;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1405;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1405;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1405;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1405;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1405;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1405;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1405;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1405;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1405;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1405;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1405;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1405;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st1405;
+tr902:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1405;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1405;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1405;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1405;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1405;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1405;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1405;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1405;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1405;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1405;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1405;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1405;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1405;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1405;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1405;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1405;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1405;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1405;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1405;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1405;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1405;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1405;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1405;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1405;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1405;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1405;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1405;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1405;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1405;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1405;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1405;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1405;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1405; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1405; goto _out;}
+ }
+ }
+ goto st1405;
+tr896:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1405;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1405;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1405;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1405;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1405;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1405;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1405;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1405;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1405;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1405;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1405;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1405;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1405;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1405;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1405;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1405;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1405;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1405;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1405;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1405;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1405;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1405;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1405;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1405;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1405;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1405;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1405;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1405;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1405;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1405;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1405;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1405;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1405; goto _out;}
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; cs = 1405; goto _out;}
+ }
+ }
+ goto st1405;
+tr4225:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 1405;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 1405;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 1405;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 1405;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 1405;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 1405;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 1405;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 1405;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 1405;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 1405;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 1405;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 1405;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 1405;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 1405;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 1405;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 1405;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 1405;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 1405;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 1405;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 1405;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 1405;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 1405;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 1405;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 1405;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 1405;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 1405;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 1405;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 1405;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 1405;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 1405;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 1405;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 1405;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st1405;
+st1405:
+ if ( ++p == pe )
+ goto _test_eof1405;
+case 1405:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr4288;
+ case 32: goto tr4288;
+ case 36: goto tr4216;
+ case 40: goto tr4289;
+ case 41: goto tr4290;
+ case 42: goto tr4219;
+ case 58: goto tr73;
+ case 65: goto tr4295;
+ case 67: goto tr4296;
+ case 68: goto tr4297;
+ case 69: goto tr4298;
+ case 72: goto tr4299;
+ case 73: goto tr4300;
+ case 75: goto tr4301;
+ case 76: goto tr4302;
+ case 77: goto tr4303;
+ case 78: goto tr4304;
+ case 79: goto tr4305;
+ case 80: goto tr4306;
+ case 82: goto tr4307;
+ case 83: goto tr4308;
+ case 84: goto tr4309;
+ case 85: goto tr4310;
+ case 90: goto tr4311;
+ case 92: goto tr4220;
+ case 95: goto tr4219;
+ case 97: goto tr4295;
+ case 99: goto tr4296;
+ case 100: goto tr4297;
+ case 101: goto tr4298;
+ case 104: goto tr4299;
+ case 105: goto tr4300;
+ case 107: goto tr4301;
+ case 108: goto tr4302;
+ case 109: goto tr4303;
+ case 110: goto tr4304;
+ case 111: goto tr4305;
+ case 112: goto tr4306;
+ case 114: goto tr4307;
+ case 115: goto tr4308;
+ case 116: goto tr4309;
+ case 117: goto tr4310;
+ case 122: goto tr4311;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr4312;
+ case 3131: goto tr4291;
+ case 3338: goto tr105;
+ case 3387: goto tr106;
+ case 3594: goto tr4312;
+ case 3643: goto tr4292;
+ }
+ if ( _widec < 60 ) {
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 44 ) {
+ if ( _widec > 47 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4294;
+ } else if ( _widec >= 45 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else if ( _widec > 63 ) {
+ if ( _widec < 91 ) {
+ if ( 64 <= _widec && _widec <= 89 )
+ goto tr4219;
+ } else if ( _widec > 96 ) {
+ if ( _widec > 121 ) {
+ if ( 123 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 98 )
+ goto tr4219;
+ } else
+ goto tr73;
+ } else
+ goto tr73;
+ goto tr4293;
+tr4296:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 244;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 244;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 244;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 244;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 244;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 244;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 244;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 244;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 244;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 244;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 244;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 244;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 244;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 244;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 244;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 244;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 244;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 244;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 244;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 244;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 244;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 244;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 244;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 244;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 244;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 244;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 244;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 244;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 244;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 244;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 244;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 244;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 244;goto st309;} }
+ goto st244;
+st244:
+ if ( ++p == pe )
+ goto _test_eof244;
+case 244:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 65: goto st18;
+ case 68: goto st22;
+ case 69: goto st29;
+ case 78: goto st32;
+ case 83: goto st36;
+ case 97: goto st18;
+ case 100: goto st22;
+ case 101: goto st29;
+ case 110: goto st32;
+ case 115: goto st36;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4297:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 245;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 245;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 245;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 245;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 245;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 245;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 245;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 245;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 245;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 245;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 245;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 245;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 245;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 245;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 245;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 245;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 245;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 245;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 245;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 245;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 245;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 245;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 245;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 245;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 245;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 245;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 245;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 245;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 245;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 245;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 245;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 245;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 245;goto st309;} }
+ goto st245;
+st245:
+ if ( ++p == pe )
+ goto _test_eof245;
+case 245:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 72: goto st41;
+ case 78: goto st45;
+ case 83: goto st53;
+ case 104: goto st41;
+ case 110: goto st45;
+ case 115: goto st53;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4298:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 246;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 246;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 246;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 246;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 246;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 246;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 246;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 246;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 246;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 246;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 246;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 246;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 246;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 246;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 246;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 246;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 246;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 246;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 246;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 246;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 246;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 246;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 246;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 246;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 246;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 246;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 246;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 246;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 246;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 246;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 246;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 246;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 246;goto st309;} }
+ goto st246;
+st246:
+ if ( ++p == pe )
+ goto _test_eof246;
+case 246:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 85: goto st55;
+ case 117: goto st55;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4299:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 247;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 247;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 247;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 247;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 247;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 247;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 247;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 247;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 247;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 247;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 247;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 247;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 247;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 247;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 247;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 247;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 247;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 247;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 247;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 247;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 247;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 247;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 247;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 247;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 247;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 247;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 247;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 247;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 247;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 247;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 247;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 247;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 247;goto st309;} }
+ goto st247;
+st247:
+ if ( ++p == pe )
+ goto _test_eof247;
+case 247:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 73: goto st62;
+ case 84: goto st66;
+ case 105: goto st62;
+ case 116: goto st66;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4300:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 248;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 248;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 248;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 248;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 248;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 248;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 248;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 248;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 248;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 248;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 248;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 248;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 248;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 248;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 248;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 248;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 248;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 248;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 248;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 248;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 248;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 248;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 248;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 248;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 248;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 248;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 248;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 248;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 248;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 248;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 248;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 248;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 248;goto st309;} }
+ goto st248;
+st248:
+ if ( ++p == pe )
+ goto _test_eof248;
+case 248:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 78: goto st176;
+ case 80: goto st74;
+ case 110: goto st176;
+ case 112: goto st74;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4301:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 249;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 249;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 249;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 249;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 249;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 249;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 249;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 249;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 249;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 249;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 249;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 249;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 249;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 249;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 249;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 249;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 249;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 249;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 249;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 249;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 249;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 249;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 249;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 249;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 249;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 249;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 249;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 249;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 249;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 249;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 249;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 249;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 249;goto st309;} }
+ goto st249;
+st249:
+ if ( ++p == pe )
+ goto _test_eof249;
+case 249:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 69: goto st82;
+ case 88: goto st84;
+ case 101: goto st82;
+ case 120: goto st84;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4302:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 250;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 250;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 250;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 250;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 250;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 250;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 250;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 250;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 250;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 250;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 250;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 250;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 250;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 250;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 250;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 250;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 250;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 250;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 250;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 250;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 250;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 250;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 250;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 250;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 250;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 250;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 250;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 250;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 250;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 250;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 250;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 250;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 250;goto st309;} }
+ goto st250;
+st250:
+ if ( ++p == pe )
+ goto _test_eof250;
+case 250:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 51: goto st86;
+ case 54: goto st88;
+ case 79: goto st90;
+ case 80: goto st92;
+ case 111: goto st90;
+ case 112: goto st92;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4303:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 251;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 251;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 251;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 251;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 251;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 251;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 251;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 251;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 251;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 251;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 251;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 251;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 251;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 251;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 251;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 251;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 251;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 251;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 251;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 251;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 251;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 251;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 251;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 251;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 251;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 251;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 251;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 251;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 251;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 251;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 251;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 251;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 251;goto st309;} }
+ goto st251;
+st251:
+ if ( ++p == pe )
+ goto _test_eof251;
+case 251:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 73: goto st94;
+ case 88: goto st98;
+ case 105: goto st94;
+ case 120: goto st98;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4304:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 252;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 252;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 252;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 252;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 252;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 252;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 252;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 252;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 252;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 252;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 252;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 252;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 252;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 252;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 252;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 252;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 252;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 252;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 252;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 252;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 252;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 252;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 252;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 252;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 252;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 252;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 252;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 252;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 252;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 252;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 252;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 252;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 252;goto st309;} }
+ goto st252;
+st252:
+ if ( ++p == pe )
+ goto _test_eof252;
+case 252:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 65: goto st100;
+ case 73: goto st104;
+ case 83: goto st106;
+ case 97: goto st100;
+ case 105: goto st104;
+ case 115: goto st106;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4305:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 253;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 253;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 253;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 253;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 253;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 253;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 253;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 253;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 253;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 253;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 253;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 253;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 253;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 253;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 253;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 253;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 253;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 253;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 253;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 253;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 253;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 253;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 253;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 253;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 253;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 253;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 253;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 253;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 253;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 253;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 253;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 253;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 253;goto st309;} }
+ goto st253;
+st253:
+ if ( ++p == pe )
+ goto _test_eof253;
+case 253:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 80: goto st116;
+ case 112: goto st116;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4306:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 254;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 254;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 254;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 254;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 254;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 254;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 254;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 254;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 254;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 254;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 254;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 254;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 254;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 254;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 254;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 254;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 254;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 254;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 254;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 254;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 254;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 254;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 254;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 254;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 254;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 254;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 254;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 254;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 254;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 254;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 254;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 254;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 254;goto st309;} }
+ goto st254;
+st254:
+ if ( ++p == pe )
+ goto _test_eof254;
+case 254:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 84: goto st126;
+ case 116: goto st126;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4307:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 255;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 255;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 255;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 255;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 255;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 255;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 255;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 255;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 255;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 255;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 255;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 255;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 255;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 255;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 255;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 255;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 255;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 255;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 255;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 255;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 255;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 255;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 255;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 255;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 255;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 255;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 255;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 255;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 255;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 255;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 255;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 255;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 255;goto st309;} }
+ goto st255;
+st255:
+ if ( ++p == pe )
+ goto _test_eof255;
+case 255:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 80: goto st129;
+ case 82: goto st130;
+ case 84: goto st134;
+ case 112: goto st129;
+ case 114: goto st130;
+ case 116: goto st134;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4308:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 256;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 256;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 256;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 256;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 256;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 256;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 256;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 256;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 256;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 256;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 256;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 256;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 256;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 256;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 256;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 256;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 256;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 256;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 256;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 256;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 256;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 256;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 256;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 256;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 256;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 256;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 256;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 256;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 256;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 256;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 256;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 256;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 256;goto st309;} }
+ goto st256;
+st256:
+ if ( ++p == pe )
+ goto _test_eof256;
+case 256:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 77: goto st136;
+ case 79: goto st141;
+ case 80: goto st143;
+ case 82: goto st145;
+ case 83: goto st147;
+ case 86: goto st151;
+ case 109: goto st136;
+ case 111: goto st141;
+ case 112: goto st143;
+ case 114: goto st145;
+ case 115: goto st147;
+ case 118: goto st151;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4309:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 257;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 257;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 257;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 257;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 257;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 257;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 257;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 257;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 257;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 257;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 257;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 257;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 257;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 257;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 257;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 257;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 257;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 257;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 257;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 257;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 257;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 257;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 257;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 257;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 257;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 257;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 257;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 257;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 257;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 257;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 257;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 257;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 257;goto st309;} }
+ goto st257;
+st257:
+ if ( ++p == pe )
+ goto _test_eof257;
+case 257:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 76: goto st155;
+ case 88: goto st158;
+ case 89: goto st160;
+ case 108: goto st155;
+ case 120: goto st158;
+ case 121: goto st160;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4310:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 258;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 258;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 258;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 258;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 258;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 258;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 258;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 258;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 258;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 258;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 258;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 258;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 258;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 258;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 258;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 258;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 258;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 258;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 258;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 258;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 258;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 258;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 258;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 258;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 258;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 258;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 258;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 258;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 258;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 258;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 258;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 258;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 258;goto st309;} }
+ goto st258;
+st258:
+ if ( ++p == pe )
+ goto _test_eof258;
+case 258:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 82: goto st165;
+ case 114: goto st165;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4311:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 259;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 259;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 259;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 259;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 259;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 259;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 259;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 259;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 259;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 259;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 259;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 259;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 259;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 259;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 259;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 259;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 259;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 259;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 259;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 259;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 259;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 259;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 259;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 259;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 259;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 259;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 259;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 259;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 259;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 259;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 259;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 259;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 259;goto st309;} }
+ goto st259;
+st259:
+ if ( ++p == pe )
+ goto _test_eof259;
+case 259:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr872;
+ case 32: goto tr872;
+ case 40: goto tr873;
+ case 41: goto tr874;
+ case 79: goto st168;
+ case 111: goto st168;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr875;
+ case 2107: goto tr876;
+ }
+ goto tr890;
+tr4220:
+ {
+ if (pe - p == 1) {
+ *wrap = WRAP_DETECTED;
+ }
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 260;goto st309;} }
+ goto st260;
+st260:
+ if ( ++p == pe )
+ goto _test_eof260;
+case 260:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr903;
+ case 32: goto tr903;
+ case 35: goto tr98;
+ case 40: goto tr905;
+ case 41: goto tr906;
+ case 1802: goto tr97;
+ case 1851: goto tr97;
+ case 2058: goto tr907;
+ case 2107: goto tr908;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr97;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr97;
+ } else
+ goto tr97;
+ goto tr904;
+tr903:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 261;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 261;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 261;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 261;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 261;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 261;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 261;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 261;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 261;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 261;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 261;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 261;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 261;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 261;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 261;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 261;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 261;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 261;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 261;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 261;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 261;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 261;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 261;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 261;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 261;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 261;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 261;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 261;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 261;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 261;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 261;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 261;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st261;
+tr905:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 261;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 261;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 261;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 261;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 261;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 261;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 261;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 261;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 261;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 261;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 261;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 261;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 261;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 261;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 261;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 261;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 261;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 261;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 261;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 261;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 261;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 261;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 261;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 261;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 261;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 261;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 261;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 261;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 261;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 261;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 261;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 261;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st261;
+tr906:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 261;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 261;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 261;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 261;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 261;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 261;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 261;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 261;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 261;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 261;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 261;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 261;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 261;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 261;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 261;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 261;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 261;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 261;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 261;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 261;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 261;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 261;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 261;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 261;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 261;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 261;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 261;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 261;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 261;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 261;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 261;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 261;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st261;
+tr907:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 261;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 261;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 261;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 261;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 261;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 261;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 261;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 261;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 261;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 261;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 261;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 261;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 261;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 261;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 261;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 261;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 261;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 261;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 261;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 261;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 261;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 261;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 261;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 261;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 261;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 261;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 261;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 261;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 261;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 261;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 261;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 261;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st261;
+st261:
+ if ( ++p == pe )
+ goto _test_eof261;
+case 261:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr909;
+ case 32: goto tr909;
+ case 40: goto tr910;
+ case 41: goto tr911;
+ case 65: goto tr5;
+ case 67: goto tr6;
+ case 68: goto tr7;
+ case 69: goto tr8;
+ case 72: goto tr9;
+ case 73: goto tr10;
+ case 75: goto tr11;
+ case 76: goto tr12;
+ case 77: goto tr13;
+ case 78: goto tr14;
+ case 79: goto tr15;
+ case 80: goto tr16;
+ case 82: goto tr17;
+ case 83: goto tr18;
+ case 84: goto tr19;
+ case 85: goto tr20;
+ case 90: goto tr21;
+ case 97: goto tr5;
+ case 99: goto tr6;
+ case 100: goto tr7;
+ case 101: goto tr8;
+ case 104: goto tr9;
+ case 105: goto tr10;
+ case 107: goto tr11;
+ case 108: goto tr12;
+ case 109: goto tr13;
+ case 110: goto tr14;
+ case 111: goto tr15;
+ case 112: goto tr16;
+ case 114: goto tr17;
+ case 115: goto tr18;
+ case 116: goto tr19;
+ case 117: goto tr20;
+ case 122: goto tr21;
+ case 1802: goto tr87;
+ case 1851: goto tr88;
+ case 2058: goto tr912;
+ case 2107: goto tr913;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4;
+ goto tr118;
+tr908:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 262;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 262;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 262;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 262;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 262;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 262;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 262;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 262;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 262;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 262;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 262;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 262;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 262;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 262;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 262;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 262;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 262;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 262;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 262;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 262;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 262;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 262;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 262;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 262;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 262;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 262;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 262;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 262;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 262;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 262;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 262;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 262;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st262;
+st262:
+ if ( ++p == pe )
+ goto _test_eof262;
+case 262:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr84;
+ case 1802: goto tr87;
+ case 1824: goto tr84;
+ case 1832: goto tr85;
+ case 1833: goto tr86;
+ case 1851: goto tr88;
+ case 2057: goto tr914;
+ case 2058: goto tr915;
+ case 2080: goto tr914;
+ case 2088: goto tr916;
+ case 2089: goto tr917;
+ case 2107: goto tr918;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr777;
+ goto tr83;
+tr919:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st263;
+tr914:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st263;
+tr916:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st263;
+tr917:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st263;
+tr920:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st263;
+tr921:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st263;
+st263:
+ if ( ++p == pe )
+ goto _test_eof263;
+case 263:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto st7;
+ case 1802: goto tr93;
+ case 1824: goto st7;
+ case 1832: goto tr91;
+ case 1833: goto tr92;
+ case 1851: goto tr94;
+ case 2057: goto tr919;
+ case 2058: goto tr844;
+ case 2080: goto tr919;
+ case 2088: goto tr920;
+ case 2089: goto tr921;
+ case 2107: goto tr922;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr777;
+ goto tr89;
+tr858:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 264;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 264;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 264;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 264;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 264;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 264;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 264;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 264;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 264;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 264;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 264;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 264;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 264;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 264;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 264;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 264;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 264;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 264;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 264;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 264;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 264;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 264;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 264;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 264;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 264;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 264;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 264;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 264;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 264;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 264;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 264;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 264;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st264;
+tr989:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 264;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 264;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 264;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 264;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 264;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 264;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 264;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 264;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 264;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 264;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 264;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 264;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 264;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 264;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 264;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 264;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 264;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 264;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 264;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 264;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 264;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 264;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 264;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 264;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 264;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 264;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 264;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 264;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 264;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 264;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 264;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 264;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st264;
+tr897:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 264;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 264;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 264;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 264;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 264;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 264;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 264;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 264;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 264;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 264;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 264;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 264;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 264;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 264;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 264;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 264;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 264;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 264;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 264;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 264;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 264;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 264;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 264;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 264;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 264;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 264;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 264;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 264;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 264;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 264;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 264;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 264;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st264;
+tr4292:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 264;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 264;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 264;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 264;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 264;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 264;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 264;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 264;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 264;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 264;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 264;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 264;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 264;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 264;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 264;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 264;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 264;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 264;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 264;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 264;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 264;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 264;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 264;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 264;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 264;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 264;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 264;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 264;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 264;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 264;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 264;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 264;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ goto st264;
+st264:
+ if ( ++p == pe )
+ goto _test_eof264;
+case 264:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr845;
+ case 1802: goto tr848;
+ case 1824: goto tr845;
+ case 1832: goto tr846;
+ case 1833: goto tr847;
+ case 1851: goto tr849;
+ case 2057: goto tr923;
+ case 2058: goto tr915;
+ case 2080: goto tr923;
+ case 2088: goto tr924;
+ case 2089: goto tr925;
+ case 2107: goto tr926;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr859;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+tr927:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st265;
+tr923:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st265;
+tr924:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st265;
+tr925:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st265;
+tr928:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st265;
+tr929:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st265;
+st265:
+ if ( ++p == pe )
+ goto _test_eof265;
+case 265:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr850;
+ case 1802: goto tr96;
+ case 1824: goto tr850;
+ case 1832: goto tr851;
+ case 1833: goto tr852;
+ case 1851: goto tr853;
+ case 2057: goto tr927;
+ case 2058: goto tr844;
+ case 2080: goto tr927;
+ case 2088: goto tr928;
+ case 2089: goto tr929;
+ case 2107: goto tr930;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr859;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+st266:
+ if ( ++p == pe )
+ goto _test_eof266;
+case 266:
+ switch( (*p) ) {
+ case 65: goto st267;
+ case 97: goto st267;
+ }
+ goto tr38;
+st267:
+ if ( ++p == pe )
+ goto _test_eof267;
+case 267:
+ switch( (*p) ) {
+ case 65: goto st268;
+ case 97: goto st268;
+ }
+ goto tr38;
+st268:
+ if ( ++p == pe )
+ goto _test_eof268;
+case 268:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr933;
+ case 32: goto tr933;
+ case 40: goto tr934;
+ case 41: goto tr935;
+ case 3082: goto tr936;
+ case 3131: goto tr937;
+ case 3338: goto tr938;
+ case 3387: goto tr938;
+ case 3594: goto tr939;
+ case 3643: goto tr940;
+ }
+ goto tr61;
+st269:
+ if ( ++p == pe )
+ goto _test_eof269;
+case 269:
+ switch( (*p) ) {
+ case 83: goto st270;
+ case 115: goto st270;
+ }
+ goto tr38;
+st270:
+ if ( ++p == pe )
+ goto _test_eof270;
+case 270:
+ switch( (*p) ) {
+ case 68: goto st271;
+ case 100: goto st271;
+ }
+ goto tr38;
+st271:
+ if ( ++p == pe )
+ goto _test_eof271;
+case 271:
+ switch( (*p) ) {
+ case 66: goto st272;
+ case 98: goto st272;
+ }
+ goto tr38;
+st272:
+ if ( ++p == pe )
+ goto _test_eof272;
+case 272:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr944;
+ case 32: goto tr944;
+ case 40: goto tr945;
+ case 41: goto tr946;
+ case 3082: goto tr947;
+ case 3131: goto tr948;
+ case 3338: goto tr949;
+ case 3387: goto tr949;
+ case 3594: goto tr950;
+ case 3643: goto tr951;
+ }
+ goto tr61;
+st273:
+ if ( ++p == pe )
+ goto _test_eof273;
+case 273:
+ switch( (*p) ) {
+ case 76: goto st274;
+ case 108: goto st274;
+ }
+ goto tr38;
+st274:
+ if ( ++p == pe )
+ goto _test_eof274;
+case 274:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr953;
+ case 32: goto tr953;
+ case 40: goto tr954;
+ case 41: goto tr955;
+ case 3082: goto tr956;
+ case 3131: goto tr957;
+ case 3338: goto tr958;
+ case 3387: goto tr958;
+ case 3594: goto tr959;
+ case 3643: goto tr960;
+ }
+ goto tr61;
+tr140:
+ {
+ s->buffer_length = 0;
+ }
+ goto st275;
+tr961:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st275;
+tr4222:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st275;
+st275:
+ if ( ++p == pe )
+ goto _test_eof275;
+case 275:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr754;
+ case 2058: goto tr962;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr961;
+ } else if ( _widec >= 1664 )
+ goto tr753;
+ goto tr83;
+tr4224:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 276;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 276;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 276;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 276;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 276;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 276;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 276;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 276;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 276;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 276;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 276;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 276;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 276;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 276;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 276;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 276;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 276;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 276;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 276;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 276;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 276;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 276;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 276;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 276;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 276;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 276;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 276;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 276;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 276;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 276;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 276;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 276;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st276;
+tr142:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 276;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 276;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 276;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 276;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 276;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 276;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 276;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 276;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 276;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 276;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 276;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 276;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 276;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 276;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 276;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 276;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 276;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 276;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 276;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 276;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 276;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 276;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 276;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 276;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 276;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 276;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 276;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 276;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 276;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 276;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 276;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 276;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st276;
+st276:
+ if ( ++p == pe )
+ goto _test_eof276;
+case 276:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr963;
+ case 32: goto tr963;
+ case 40: goto tr964;
+ case 41: goto tr965;
+ case 1802: goto tr966;
+ case 1851: goto tr849;
+ case 2058: goto tr966;
+ case 2107: goto tr849;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr753;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr753;
+ } else
+ goto tr753;
+ goto tr83;
+tr967:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+tr963:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+tr964:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+tr965:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+tr968:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+tr969:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st277;
+st277:
+ if ( ++p == pe )
+ goto _test_eof277;
+case 277:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr967;
+ case 32: goto tr967;
+ case 40: goto tr968;
+ case 41: goto tr969;
+ case 1802: goto tr970;
+ case 1851: goto tr853;
+ case 2058: goto tr970;
+ case 2107: goto tr853;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr753;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr753;
+ } else
+ goto tr753;
+ goto tr89;
+tr144:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 278;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 278;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 278;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 278;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 278;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 278;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 278;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 278;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 278;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 278;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 278;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 278;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 278;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 278;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 278;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 278;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 278;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 278;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 278;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 278;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 278;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 278;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 278;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 278;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 278;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 278;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 278;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 278;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 278;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 278;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 278;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 278;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st278;
+tr4226:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 278;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 278;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 278;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 278;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 278;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 278;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 278;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 278;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 278;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 278;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 278;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 278;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 278;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 278;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 278;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 278;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 278;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 278;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 278;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 278;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 278;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 278;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 278;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 278;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 278;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 278;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 278;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 278;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 278;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 278;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 278;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 278;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st278;
+st278:
+ if ( ++p == pe )
+ goto _test_eof278;
+case 278:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr963;
+ case 1802: goto tr966;
+ case 1824: goto tr963;
+ case 1832: goto tr964;
+ case 1833: goto tr965;
+ case 1851: goto tr849;
+ case 2057: goto tr971;
+ case 2058: goto tr915;
+ case 2080: goto tr971;
+ case 2088: goto tr972;
+ case 2089: goto tr973;
+ case 2107: goto tr926;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr961;
+ } else if ( _widec >= 1664 )
+ goto tr753;
+ goto tr83;
+tr974:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st279;
+tr971:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st279;
+tr972:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st279;
+tr973:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st279;
+tr975:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st279;
+tr976:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st279;
+st279:
+ if ( ++p == pe )
+ goto _test_eof279;
+case 279:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr967;
+ case 1802: goto tr970;
+ case 1824: goto tr967;
+ case 1832: goto tr968;
+ case 1833: goto tr969;
+ case 1851: goto tr853;
+ case 2057: goto tr974;
+ case 2058: goto tr844;
+ case 2080: goto tr974;
+ case 2088: goto tr975;
+ case 2089: goto tr976;
+ case 2107: goto tr930;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr961;
+ } else if ( _widec >= 1664 )
+ goto tr753;
+ goto tr83;
+tr114:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 280;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 280;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 280;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 280;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 280;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 280;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 280;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 280;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 280;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 280;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 280;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 280;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 280;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 280;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 280;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 280;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 280;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 280;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 280;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 280;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 280;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 280;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 280;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 280;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 280;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 280;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 280;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 280;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 280;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 280;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 280;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 280;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st280;
+tr107:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 280;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 280;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 280;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 280;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 280;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 280;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 280;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 280;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 280;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 280;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 280;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 280;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 280;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 280;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 280;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 280;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 280;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 280;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 280;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 280;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 280;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 280;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 280;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 280;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 280;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 280;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 280;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 280;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 280;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 280;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 280;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 280;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st280;
+tr832:
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 280;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 280;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 280;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 280;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 280;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 280;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 280;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 280;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 280;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 280;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 280;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 280;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 280;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 280;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 280;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 280;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 280;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 280;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 280;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 280;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 280;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 280;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 280;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 280;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 280;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 280;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 280;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 280;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 280;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 280;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 280;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 280;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st280;
+st280:
+ if ( ++p == pe )
+ goto _test_eof280;
+case 280:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr845;
+ case 1802: goto tr848;
+ case 1824: goto tr845;
+ case 1832: goto tr846;
+ case 1833: goto tr847;
+ case 1851: goto tr849;
+ case 2057: goto tr977;
+ case 2058: goto tr848;
+ case 2080: goto tr977;
+ case 2088: goto tr978;
+ case 2089: goto tr979;
+ case 2107: goto tr174;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr115;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+tr980:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st281;
+tr977:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st281;
+tr978:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st281;
+tr979:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st281;
+tr981:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st281;
+tr982:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st281;
+st281:
+ if ( ++p == pe )
+ goto _test_eof281;
+case 281:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr850;
+ case 1802: goto tr96;
+ case 1824: goto tr850;
+ case 1832: goto tr851;
+ case 1833: goto tr852;
+ case 1851: goto tr853;
+ case 2057: goto tr980;
+ case 2058: goto tr96;
+ case 2080: goto tr980;
+ case 2088: goto tr981;
+ case 2089: goto tr982;
+ case 2107: goto tr178;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr115;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr83;
+tr820:
+ {
+ s->buffer_length = 0;
+ }
+ goto st282;
+tr983:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st282;
+tr813:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ goto st282;
+st282:
+ if ( ++p == pe )
+ goto _test_eof282;
+case 282:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr984;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr983;
+ goto tr75;
+tr821:
+ {
+ s->line_counter++;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 283;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 283;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 283;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 283;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 283;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 283;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 283;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 283;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 283;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 283;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 283;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 283;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 283;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 283;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 283;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 283;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 283;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 283;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 283;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 283;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 283;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 283;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 283;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 283;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 283;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 283;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 283;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 283;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 283;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 283;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 283;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 283;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st283;
+tr814:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 283;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 283;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 283;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 283;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 283;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 283;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 283;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 283;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 283;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 283;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 283;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 283;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 283;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 283;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 283;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 283;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 283;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 283;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 283;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 283;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 283;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 283;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 283;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 283;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 283;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 283;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 283;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 283;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 283;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 283;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 283;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 283;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st283;
+st283:
+ if ( ++p == pe )
+ goto _test_eof283;
+case 283:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr985;
+ case 32: goto tr985;
+ case 40: goto tr986;
+ case 41: goto tr987;
+ case 58: goto tr73;
+ case 65: goto tr122;
+ case 67: goto tr123;
+ case 68: goto tr124;
+ case 69: goto tr125;
+ case 72: goto tr126;
+ case 73: goto tr127;
+ case 75: goto tr128;
+ case 76: goto tr129;
+ case 77: goto tr130;
+ case 78: goto tr131;
+ case 79: goto tr132;
+ case 80: goto tr133;
+ case 82: goto tr134;
+ case 83: goto tr135;
+ case 84: goto tr136;
+ case 85: goto tr137;
+ case 90: goto tr138;
+ case 92: goto tr78;
+ case 97: goto tr122;
+ case 99: goto tr123;
+ case 100: goto tr124;
+ case 101: goto tr125;
+ case 104: goto tr126;
+ case 105: goto tr127;
+ case 107: goto tr128;
+ case 108: goto tr129;
+ case 109: goto tr130;
+ case 110: goto tr131;
+ case 111: goto tr132;
+ case 112: goto tr133;
+ case 114: goto tr134;
+ case 115: goto tr135;
+ case 116: goto tr136;
+ case 117: goto tr137;
+ case 122: goto tr138;
+ case 2826: goto tr87;
+ case 2875: goto tr88;
+ case 3082: goto tr912;
+ case 3131: goto tr988;
+ case 3338: goto tr105;
+ case 3387: goto tr106;
+ case 3594: goto tr912;
+ case 3643: goto tr989;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr73;
+ } else if ( _widec > 47 ) {
+ if ( _widec > 57 ) {
+ if ( 60 <= _widec )
+ goto tr73;
+ } else if ( _widec >= 48 )
+ goto tr121;
+ } else
+ goto tr73;
+ goto tr118;
+tr822:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 284;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 284;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 284;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 284;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 284;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 284;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 284;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 284;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 284;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 284;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 284;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 284;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 284;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 284;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 284;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 284;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 284;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 284;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 284;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 284;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 284;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 284;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 284;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 284;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 284;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 284;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 284;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 284;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 284;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 284;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 284;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 284;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st284;
+tr815:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ { s->r_type = KNOT_RRTYPE_A; }
+ {
+ rdata_tail = s->r_data;
+ }
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = 284;goto st844;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = 284;goto st846;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = 284;goto st848;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = 284;goto st880;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = 284;goto st885;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = 284;goto st890;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = 284;goto st895;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = 284;goto st899;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = 284;goto st901;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = 284;goto st956;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = 284;goto st967;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = 284;goto st984;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = 284;goto st995;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = 284;goto st1006;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = 284;goto st1019;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = 284;goto st1029;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = 284;goto st1068;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = 284;goto st1254;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = 284;goto st1257;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = 284;goto st1268;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = 284;goto st1270;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = 284;goto st1299;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = 284;goto st1312;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = 284;goto st1325;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = 284;goto st1331;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = 284;goto st1349;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = 284;goto st1344;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = 284;goto st1362;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = 284;goto st1368;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = 284;goto st1374;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = 284;goto st1382;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = 284;goto st1390;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {goto st307;}
+ }
+ }
+ goto st284;
+st284:
+ if ( ++p == pe )
+ goto _test_eof284;
+case 284:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto tr84;
+ case 1802: goto tr87;
+ case 1824: goto tr84;
+ case 1832: goto tr85;
+ case 1833: goto tr86;
+ case 1851: goto tr88;
+ case 2057: goto tr990;
+ case 2058: goto tr915;
+ case 2080: goto tr990;
+ case 2088: goto tr991;
+ case 2089: goto tr992;
+ case 2107: goto tr926;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr983;
+ goto tr83;
+tr993:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st285;
+tr990:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st285;
+tr991:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st285;
+tr992:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st285;
+tr994:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st285;
+tr995:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st285;
+st285:
+ if ( ++p == pe )
+ goto _test_eof285;
+case 285:
+ _widec = (*p);
+ if ( (*p) < 11 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 59 ) {
+ if ( 60 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1801: goto st7;
+ case 1802: goto tr93;
+ case 1824: goto st7;
+ case 1832: goto tr91;
+ case 1833: goto tr92;
+ case 1851: goto tr94;
+ case 2057: goto tr993;
+ case 2058: goto tr844;
+ case 2080: goto tr993;
+ case 2088: goto tr994;
+ case 2089: goto tr995;
+ case 2107: goto tr930;
+ }
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr983;
+ goto tr83;
+tr4232:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 286;goto st309;} }
+ goto st286;
+tr4253:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 286;goto st309;} }
+ goto st286;
+st286:
+ if ( ++p == pe )
+ goto _test_eof286;
+case 286:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 65: goto st18;
+ case 68: goto st22;
+ case 69: goto st29;
+ case 78: goto st32;
+ case 83: goto st36;
+ case 97: goto st18;
+ case 100: goto st22;
+ case 101: goto st29;
+ case 110: goto st32;
+ case 115: goto st36;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4233:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 287;goto st309;} }
+ goto st287;
+tr4254:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 287;goto st309;} }
+ goto st287;
+st287:
+ if ( ++p == pe )
+ goto _test_eof287;
+case 287:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 72: goto st41;
+ case 78: goto st45;
+ case 83: goto st53;
+ case 104: goto st41;
+ case 110: goto st45;
+ case 115: goto st53;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4234:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 288;goto st309;} }
+ goto st288;
+tr4255:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 288;goto st309;} }
+ goto st288;
+st288:
+ if ( ++p == pe )
+ goto _test_eof288;
+case 288:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 85: goto st55;
+ case 117: goto st55;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4235:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 289;goto st309;} }
+ goto st289;
+tr4256:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 289;goto st309;} }
+ goto st289;
+st289:
+ if ( ++p == pe )
+ goto _test_eof289;
+case 289:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 73: goto st62;
+ case 84: goto st66;
+ case 105: goto st62;
+ case 116: goto st66;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4257:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 290;goto st309;} }
+ goto st290;
+st290:
+ if ( ++p == pe )
+ goto _test_eof290;
+case 290:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 78: goto st176;
+ case 80: goto st74;
+ case 110: goto st176;
+ case 112: goto st74;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4237:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 291;goto st309;} }
+ goto st291;
+tr4258:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 291;goto st309;} }
+ goto st291;
+st291:
+ if ( ++p == pe )
+ goto _test_eof291;
+case 291:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 69: goto st82;
+ case 88: goto st84;
+ case 101: goto st82;
+ case 120: goto st84;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4238:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 292;goto st309;} }
+ goto st292;
+tr4259:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 292;goto st309;} }
+ goto st292;
+st292:
+ if ( ++p == pe )
+ goto _test_eof292;
+case 292:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 51: goto st86;
+ case 54: goto st88;
+ case 79: goto st90;
+ case 80: goto st92;
+ case 111: goto st90;
+ case 112: goto st92;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4239:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 293;goto st309;} }
+ goto st293;
+tr4260:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 293;goto st309;} }
+ goto st293;
+st293:
+ if ( ++p == pe )
+ goto _test_eof293;
+case 293:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 73: goto st94;
+ case 88: goto st98;
+ case 105: goto st94;
+ case 120: goto st98;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4240:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 294;goto st309;} }
+ goto st294;
+tr4261:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 294;goto st309;} }
+ goto st294;
+st294:
+ if ( ++p == pe )
+ goto _test_eof294;
+case 294:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 65: goto st100;
+ case 73: goto st104;
+ case 83: goto st106;
+ case 97: goto st100;
+ case 105: goto st104;
+ case 115: goto st106;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4241:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 295;goto st309;} }
+ goto st295;
+tr4262:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 295;goto st309;} }
+ goto st295;
+st295:
+ if ( ++p == pe )
+ goto _test_eof295;
+case 295:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 80: goto st116;
+ case 112: goto st116;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4242:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 296;goto st309;} }
+ goto st296;
+tr4263:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 296;goto st309;} }
+ goto st296;
+st296:
+ if ( ++p == pe )
+ goto _test_eof296;
+case 296:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 84: goto st126;
+ case 116: goto st126;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4243:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 297;goto st309;} }
+ goto st297;
+tr4264:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 297;goto st309;} }
+ goto st297;
+st297:
+ if ( ++p == pe )
+ goto _test_eof297;
+case 297:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 80: goto st129;
+ case 82: goto st130;
+ case 84: goto st134;
+ case 112: goto st129;
+ case 114: goto st130;
+ case 116: goto st134;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4244:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 298;goto st309;} }
+ goto st298;
+tr4265:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 298;goto st309;} }
+ goto st298;
+st298:
+ if ( ++p == pe )
+ goto _test_eof298;
+case 298:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 77: goto st136;
+ case 79: goto st141;
+ case 80: goto st143;
+ case 82: goto st145;
+ case 83: goto st147;
+ case 86: goto st151;
+ case 109: goto st136;
+ case 111: goto st141;
+ case 112: goto st143;
+ case 114: goto st145;
+ case 115: goto st147;
+ case 118: goto st151;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4245:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 299;goto st309;} }
+ goto st299;
+tr4266:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 299;goto st309;} }
+ goto st299;
+st299:
+ if ( ++p == pe )
+ goto _test_eof299;
+case 299:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 76: goto st155;
+ case 88: goto st158;
+ case 89: goto st160;
+ case 108: goto st155;
+ case 120: goto st158;
+ case 121: goto st160;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4246:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 300;goto st309;} }
+ goto st300;
+tr4267:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 300;goto st309;} }
+ goto st300;
+st300:
+ if ( ++p == pe )
+ goto _test_eof300;
+case 300:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 82: goto st165;
+ case 114: goto st165;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr4247:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 301;goto st309;} }
+ goto st301;
+tr4268:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 301;goto st309;} }
+ goto st301;
+st301:
+ if ( ++p == pe )
+ goto _test_eof301;
+case 301:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 79: goto st168;
+ case 111: goto st168;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr25:
+ {
+ s->buffer_length = 0;
+ }
+ goto st302;
+tr997:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st302;
+tr4214:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st302;
+tr4282:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st302;
+tr4278:
+ {
+ NOERR;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st302;
+st302:
+ if ( ++p == pe )
+ goto _test_eof302;
+case 302:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr754;
+ case 2058: goto tr998;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr997;
+ } else if ( _widec >= 1664 )
+ goto tr753;
+ goto tr89;
+tr697:
+ {
+ s->buffer_length = 0;
+ }
+ goto st303;
+tr999:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st303;
+tr4249:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st303;
+st303:
+ if ( ++p == pe )
+ goto _test_eof303;
+case 303:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr754;
+ case 2058: goto tr1000;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr999;
+ } else if ( _widec >= 1664 )
+ goto tr753;
+ goto tr151;
+tr4236:
+ {
+ s->r_class = s->default_class;
+ }
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ { p--; {stack[top++] = 304;goto st309;} }
+ goto st304;
+st304:
+ if ( ++p == pe )
+ goto _test_eof304;
+case 304:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr767;
+ case 32: goto tr767;
+ case 40: goto tr768;
+ case 41: goto tr769;
+ case 78: goto st71;
+ case 80: goto st74;
+ case 110: goto st71;
+ case 112: goto st74;
+ case 2058: goto tr770;
+ case 2107: goto tr771;
+ }
+ goto tr996;
+tr156:
+ {
+ s->buffer_length = 0;
+ }
+ goto st305;
+tr1001:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st305;
+tr150:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st305;
+st305:
+ if ( ++p == pe )
+ goto _test_eof305;
+case 305:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr96;
+ case 2058: goto tr1002;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1001;
+ } else if ( _widec >= 1664 )
+ goto tr95;
+ goto tr151;
+tr1006:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st306;
+tr1005:
+ {
+ s->line_counter++;
+ }
+ goto st306;
+st306:
+ if ( ++p == pe )
+ goto _test_eof306;
+case 306:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 41 <= (*p) && (*p) <= 41 ) {
+ _widec = (short)(640 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(128 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 266: goto tr1004;
+ case 522: goto tr1005;
+ case 1065: goto tr1006;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 9 )
+ goto st306;
+ } else if ( _widec > 40 ) {
+ if ( 42 <= _widec )
+ goto st306;
+ } else
+ goto st306;
+ goto st0;
+tr1004:
+ { p--; {stack[top++] = 1406;goto st1396;} }
+ {
+ s->line_counter++;
+ }
+ goto st1406;
+st1406:
+ if ( ++p == pe )
+ goto _test_eof1406;
+case 1406:
+ goto st0;
+st307:
+ if ( ++p == pe )
+ goto _test_eof307;
+case 307:
+ if ( (*p) == 10 )
+ goto tr1008;
+ goto tr1007;
+tr1007:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if ((*p) == '\r') {
+ ERR(ZS_DOS_NEWLINE);
+ }
+
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st308;
+tr1009:
+ {
+ if ((*p) == '\r') {
+ ERR(ZS_DOS_NEWLINE);
+ }
+
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st308;
+st308:
+ if ( ++p == pe )
+ goto _test_eof308;
+case 308:
+ if ( (*p) == 10 )
+ goto tr1010;
+ goto tr1009;
+tr1008:
+ cs = 1407;
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Terminate the error context string.
+ s->buffer[s->buffer_length++] = 0;
+
+ // Error counter incrementation.
+ s->error.counter++;
+
+ // Initialize the fcall stack.
+ top = 0;
+
+ // Reset per-record contexts.
+ s->long_string = false;
+ s->comma_list = false;
+
+ s->state = ZS_STATE_ERROR;
+
+ // Execute the error callback.
+ if (s->process.automatic) {
+ p--;
+ if (s->process.error != NULL) {
+ s->process.error(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out;}
+ }
+ }
+
+ // Stop the scanner if fatal error.
+ if (s->error.fatal) {
+ {p++; goto _out;}
+ }
+ {goto st306;}
+ } else {
+ // Return if external processing.
+ p--; cs = 306; {p++; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+tr1010:
+ cs = 1407;
+ {
+ // Terminate the error context string.
+ s->buffer[s->buffer_length++] = 0;
+
+ // Error counter incrementation.
+ s->error.counter++;
+
+ // Initialize the fcall stack.
+ top = 0;
+
+ // Reset per-record contexts.
+ s->long_string = false;
+ s->comma_list = false;
+
+ s->state = ZS_STATE_ERROR;
+
+ // Execute the error callback.
+ if (s->process.automatic) {
+ p--;
+ if (s->process.error != NULL) {
+ s->process.error(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out;}
+ }
+ }
+
+ // Stop the scanner if fatal error.
+ if (s->error.fatal) {
+ {p++; goto _out;}
+ }
+ {goto st306;}
+ } else {
+ // Return if external processing.
+ p--; cs = 306; {p++; goto _out;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+st1407:
+ if ( ++p == pe )
+ goto _test_eof1407;
+case 1407:
+ goto st0;
+st309:
+ if ( ++p == pe )
+ goto _test_eof309;
+case 309:
+ switch( (*p) ) {
+ case 42: goto tr1012;
+ case 46: goto tr1013;
+ case 64: goto st317;
+ case 92: goto tr1015;
+ case 95: goto tr1012;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr1012;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1012;
+ } else
+ goto tr1012;
+ goto tr1011;
+tr1012:
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st310;
+tr1017:
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st310;
+tr1021:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st310;
+tr1028:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st310;
+st310:
+ if ( ++p == pe )
+ goto _test_eof310;
+case 310:
+ switch( (*p) ) {
+ case 32: goto tr1016;
+ case 42: goto tr1017;
+ case 46: goto tr1018;
+ case 59: goto tr1016;
+ case 92: goto st312;
+ case 95: goto tr1017;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1016;
+ } else if ( (*p) >= 9 )
+ goto tr1016;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1017;
+ } else if ( (*p) >= 65 )
+ goto tr1017;
+ } else
+ goto tr1017;
+ goto tr1011;
+tr1016:
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Check for (relative + origin) dname length overflow.
+ if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) {
+ memcpy(s->dname + s->dname_tmp_length,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length += s->zone_origin_length;
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1408;
+tr1020:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1408;
+tr1027:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Check for (relative + origin) dname length overflow.
+ if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) {
+ memcpy(s->dname + s->dname_tmp_length,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length += s->zone_origin_length;
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1408;
+tr1031:
+ {
+ // Copy already verified zone origin.
+ memcpy(s->dname,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length = s->zone_origin_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1408;
+st1408:
+ if ( ++p == pe )
+ goto _test_eof1408;
+case 1408:
+ goto st0;
+tr1018:
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st311;
+tr1029:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st311;
+st311:
+ if ( ++p == pe )
+ goto _test_eof311;
+case 311:
+ switch( (*p) ) {
+ case 32: goto tr1020;
+ case 42: goto tr1021;
+ case 45: goto tr1021;
+ case 59: goto tr1020;
+ case 92: goto tr1022;
+ case 95: goto tr1021;
+ }
+ if ( (*p) < 47 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1020;
+ } else if ( (*p) >= 9 )
+ goto tr1020;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1021;
+ } else if ( (*p) >= 65 )
+ goto tr1021;
+ } else
+ goto tr1021;
+ goto tr1011;
+tr1015:
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st312;
+tr1022:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st312;
+tr1030:
+ {
+ s->dname_tmp_length++;
+ }
+ goto st312;
+st312:
+ if ( ++p == pe )
+ goto _test_eof312;
+case 312:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1023;
+ goto tr1017;
+tr1023:
+ {
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length] = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st313;
+st313:
+ if ( ++p == pe )
+ goto _test_eof313;
+case 313:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1025;
+ goto tr1024;
+tr1025:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st314;
+st314:
+ if ( ++p == pe )
+ goto _test_eof314;
+case 314:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1026;
+ goto tr1024;
+tr1026:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st315;
+st315:
+ if ( ++p == pe )
+ goto _test_eof315;
+case 315:
+ switch( (*p) ) {
+ case 32: goto tr1027;
+ case 42: goto tr1028;
+ case 46: goto tr1029;
+ case 59: goto tr1027;
+ case 92: goto tr1030;
+ case 95: goto tr1028;
+ }
+ if ( (*p) < 45 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1027;
+ } else if ( (*p) >= 9 )
+ goto tr1027;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1028;
+ } else if ( (*p) >= 65 )
+ goto tr1028;
+ } else
+ goto tr1028;
+ goto tr1024;
+tr1013:
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ goto st316;
+st316:
+ if ( ++p == pe )
+ goto _test_eof316;
+case 316:
+ switch( (*p) ) {
+ case 32: goto tr1020;
+ case 59: goto tr1020;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1020;
+ } else if ( (*p) >= 9 )
+ goto tr1020;
+ goto tr1011;
+st317:
+ if ( ++p == pe )
+ goto _test_eof317;
+case 317:
+ switch( (*p) ) {
+ case 32: goto tr1031;
+ case 59: goto tr1031;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1031;
+ } else if ( (*p) >= 9 )
+ goto tr1031;
+ goto tr1011;
+st318:
+ if ( ++p == pe )
+ goto _test_eof318;
+case 318:
+ switch( (*p) ) {
+ case 34: goto st324;
+ case 92: goto st320;
+ }
+ if ( (*p) > 58 ) {
+ if ( 60 <= (*p) && (*p) <= 126 )
+ goto tr1033;
+ } else if ( (*p) >= 33 )
+ goto tr1033;
+ goto tr1032;
+tr1033:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st319;
+tr1037:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st319;
+tr1045:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st319;
+st319:
+ if ( ++p == pe )
+ goto _test_eof319;
+case 319:
+ switch( (*p) ) {
+ case 32: goto tr1036;
+ case 33: goto tr1037;
+ case 59: goto tr1036;
+ case 92: goto tr1039;
+ }
+ if ( (*p) < 35 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr1036;
+ } else if ( (*p) > 39 ) {
+ if ( (*p) > 41 ) {
+ if ( 42 <= (*p) && (*p) <= 126 )
+ goto tr1037;
+ } else if ( (*p) >= 40 )
+ goto tr1038;
+ } else
+ goto tr1037;
+ goto tr1032;
+tr1036:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1409;
+tr1044:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1409;
+tr1057:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1409;
+st1409:
+ if ( ++p == pe )
+ goto _test_eof1409;
+case 1409:
+ goto st0;
+tr1038:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1410;
+tr1046:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1410;
+st1410:
+ if ( ++p == pe )
+ goto _test_eof1410;
+case 1410:
+ switch( (*p) ) {
+ case 32: goto tr1036;
+ case 33: goto tr1037;
+ case 59: goto tr1036;
+ case 92: goto tr1039;
+ }
+ if ( (*p) < 35 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr1036;
+ } else if ( (*p) > 39 ) {
+ if ( (*p) > 41 ) {
+ if ( 42 <= (*p) && (*p) <= 126 )
+ goto tr1037;
+ } else if ( (*p) >= 40 )
+ goto tr1038;
+ } else
+ goto tr1037;
+ goto tr1032;
+tr1039:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st320;
+tr1047:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st320;
+st320:
+ if ( ++p == pe )
+ goto _test_eof320;
+case 320:
+ if ( (*p) < 48 ) {
+ if ( 32 <= (*p) && (*p) <= 47 )
+ goto tr1033;
+ } else if ( (*p) > 57 ) {
+ if ( 58 <= (*p) && (*p) <= 126 )
+ goto tr1033;
+ } else
+ goto tr1041;
+ goto tr1040;
+tr1041:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *rdata_tail = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st321;
+st321:
+ if ( ++p == pe )
+ goto _test_eof321;
+case 321:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1042;
+ goto tr1040;
+tr1042:
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st322;
+st322:
+ if ( ++p == pe )
+ goto _test_eof322;
+case 322:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1043;
+ goto tr1040;
+tr1043:
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st323;
+st323:
+ if ( ++p == pe )
+ goto _test_eof323;
+case 323:
+ switch( (*p) ) {
+ case 32: goto tr1044;
+ case 33: goto tr1045;
+ case 59: goto tr1044;
+ case 92: goto tr1047;
+ }
+ if ( (*p) < 35 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr1044;
+ } else if ( (*p) > 39 ) {
+ if ( (*p) > 41 ) {
+ if ( 42 <= (*p) && (*p) <= 126 )
+ goto tr1045;
+ } else if ( (*p) >= 40 )
+ goto tr1046;
+ } else
+ goto tr1045;
+ goto tr1040;
+tr1048:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st324;
+tr1052:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st324;
+tr1061:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st324;
+st324:
+ if ( ++p == pe )
+ goto _test_eof324;
+case 324:
+ _widec = (*p);
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1152 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1048;
+ case 32: goto tr1048;
+ case 34: goto st326;
+ case 59: goto tr1048;
+ case 92: goto st327;
+ case 1546: goto tr1048;
+ }
+ if ( 33 <= _widec && _widec <= 126 )
+ goto tr1049;
+ goto tr1032;
+tr1049:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st325;
+tr1053:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st325;
+tr1062:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st325;
+st325:
+ if ( ++p == pe )
+ goto _test_eof325;
+case 325:
+ _widec = (*p);
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1152 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1052;
+ case 32: goto tr1052;
+ case 34: goto tr1054;
+ case 59: goto tr1052;
+ case 92: goto tr1055;
+ case 1546: goto tr1052;
+ }
+ if ( 33 <= _widec && _widec <= 126 )
+ goto tr1053;
+ goto tr1032;
+tr1054:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st326;
+tr1063:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st326;
+st326:
+ if ( ++p == pe )
+ goto _test_eof326;
+case 326:
+ switch( (*p) ) {
+ case 32: goto tr1057;
+ case 59: goto tr1057;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1057;
+ } else if ( (*p) >= 9 )
+ goto tr1057;
+ goto tr1056;
+tr1055:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st327;
+tr1064:
+ {
+ rdata_tail++;
+ }
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ goto st327;
+st327:
+ if ( ++p == pe )
+ goto _test_eof327;
+case 327:
+ if ( (*p) < 48 ) {
+ if ( 32 <= (*p) && (*p) <= 47 )
+ goto tr1049;
+ } else if ( (*p) > 57 ) {
+ if ( 58 <= (*p) && (*p) <= 126 )
+ goto tr1049;
+ } else
+ goto tr1058;
+ goto tr1040;
+tr1058:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+
+ *rdata_tail = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st328;
+st328:
+ if ( ++p == pe )
+ goto _test_eof328;
+case 328:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1059;
+ goto tr1040;
+tr1059:
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st329;
+st329:
+ if ( ++p == pe )
+ goto _test_eof329;
+case 329:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1060;
+ goto tr1040;
+tr1060:
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st330;
+st330:
+ if ( ++p == pe )
+ goto _test_eof330;
+case 330:
+ _widec = (*p);
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(1152 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1061;
+ case 32: goto tr1061;
+ case 34: goto tr1063;
+ case 59: goto tr1061;
+ case 92: goto tr1064;
+ case 1546: goto tr1061;
+ }
+ if ( 33 <= _widec && _widec <= 126 )
+ goto tr1062;
+ goto tr1040;
+st331:
+ if ( ++p == pe )
+ goto _test_eof331;
+case 331:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st332;
+ case 32: goto st332;
+ case 40: goto tr1067;
+ case 41: goto tr1068;
+ case 2058: goto tr1069;
+ case 2107: goto tr1070;
+ }
+ goto tr1065;
+tr1067:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st332;
+tr1068:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st332;
+tr1069:
+ {
+ s->line_counter++;
+ }
+ goto st332;
+tr1108:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 332; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st332;
+st332:
+ if ( ++p == pe )
+ goto _test_eof332;
+case 332:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st332;
+ case 32: goto st332;
+ case 40: goto tr1067;
+ case 41: goto tr1068;
+ case 2058: goto tr1069;
+ case 2107: goto tr1070;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1072;
+ goto tr1071;
+tr1072:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st333;
+tr1077:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st333;
+st333:
+ if ( ++p == pe )
+ goto _test_eof333;
+case 333:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1074;
+ case 32: goto tr1074;
+ case 40: goto tr1075;
+ case 41: goto tr1076;
+ case 68: goto tr1078;
+ case 72: goto tr1079;
+ case 77: goto tr1080;
+ case 83: goto st336;
+ case 87: goto tr1082;
+ case 100: goto tr1078;
+ case 104: goto tr1079;
+ case 109: goto tr1080;
+ case 115: goto st336;
+ case 119: goto tr1082;
+ case 1802: goto tr1083;
+ case 1851: goto tr1084;
+ case 2058: goto tr1083;
+ case 2107: goto tr1084;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1077;
+ goto tr1073;
+tr1087:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st334;
+tr1088:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st334;
+tr1074:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st334;
+tr1075:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st334;
+tr1076:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st334;
+tr1101:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st334;
+tr1102:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st334;
+tr1103:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st334;
+st334:
+ if ( ++p == pe )
+ goto _test_eof334;
+case 334:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st334;
+ case 32: goto st334;
+ case 40: goto tr1087;
+ case 41: goto tr1088;
+ case 1802: goto tr1089;
+ case 1851: goto tr1090;
+ case 2058: goto tr1089;
+ case 2107: goto tr1090;
+ }
+ goto tr1085;
+tr1083:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1411;
+tr1089:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1411;
+tr1092:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1411; goto _out;}
+ }
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1411;
+tr1105:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1411;
+st1411:
+ if ( ++p == pe )
+ goto _test_eof1411;
+case 1411:
+ goto st0;
+tr1090:
+ {
+ s->buffer_length = 0;
+ }
+ goto st335;
+tr1084:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st335;
+tr1091:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st335;
+tr1106:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st335;
+st335:
+ if ( ++p == pe )
+ goto _test_eof335;
+case 335:
+ if ( (*p) == 10 )
+ goto tr1092;
+ goto tr1091;
+tr1078:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st336;
+tr1079:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st336;
+tr1080:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st336;
+tr1082:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st336;
+st336:
+ if ( ++p == pe )
+ goto _test_eof336;
+case 336:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1074;
+ case 32: goto tr1074;
+ case 40: goto tr1075;
+ case 41: goto tr1076;
+ case 1802: goto tr1083;
+ case 1851: goto tr1084;
+ case 2058: goto tr1083;
+ case 2107: goto tr1084;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1093;
+ goto tr1073;
+tr1095:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st337;
+tr1093:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st337;
+tr1104:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st337;
+st337:
+ if ( ++p == pe )
+ goto _test_eof337;
+case 337:
+ switch( (*p) ) {
+ case 68: goto tr1096;
+ case 72: goto tr1097;
+ case 77: goto tr1098;
+ case 83: goto st338;
+ case 87: goto tr1100;
+ case 100: goto tr1096;
+ case 104: goto tr1097;
+ case 109: goto tr1098;
+ case 115: goto st338;
+ case 119: goto tr1100;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1095;
+ goto tr1094;
+tr1096:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st338;
+tr1097:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st338;
+tr1098:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st338;
+tr1100:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st338;
+st338:
+ if ( ++p == pe )
+ goto _test_eof338;
+case 338:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1101;
+ case 32: goto tr1101;
+ case 40: goto tr1102;
+ case 41: goto tr1103;
+ case 1802: goto tr1105;
+ case 1851: goto tr1106;
+ case 2058: goto tr1105;
+ case 2107: goto tr1106;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1104;
+ goto tr1073;
+tr1070:
+ {
+ s->buffer_length = 0;
+ }
+ goto st339;
+tr1107:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st339;
+st339:
+ if ( ++p == pe )
+ goto _test_eof339;
+case 339:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1108;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1107;
+ goto tr1065;
+st340:
+ if ( ++p == pe )
+ goto _test_eof340;
+case 340:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st341;
+ case 32: goto st341;
+ case 40: goto tr1111;
+ case 41: goto tr1112;
+ case 2058: goto tr1113;
+ case 2107: goto tr1114;
+ }
+ goto tr1109;
+tr1111:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st341;
+tr1112:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st341;
+tr1113:
+ {
+ s->line_counter++;
+ }
+ goto st341;
+tr1144:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 341; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st341;
+st341:
+ if ( ++p == pe )
+ goto _test_eof341;
+case 341:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st341;
+ case 32: goto st341;
+ case 40: goto tr1111;
+ case 41: goto tr1112;
+ case 42: goto tr1115;
+ case 46: goto tr1116;
+ case 92: goto tr1117;
+ case 95: goto tr1115;
+ case 2058: goto tr1113;
+ case 2107: goto tr1114;
+ }
+ if ( _widec < 65 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr1115;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1115;
+ } else
+ goto tr1115;
+ goto tr1109;
+tr1118:
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st342;
+tr1125:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st342;
+tr1140:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st342;
+tr1115:
+ {
+ s->dname = s->zone_origin;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st342;
+st342:
+ if ( ++p == pe )
+ goto _test_eof342;
+case 342:
+ switch( (*p) ) {
+ case 42: goto tr1118;
+ case 46: goto tr1119;
+ case 92: goto st346;
+ case 95: goto tr1118;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr1118;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1118;
+ } else
+ goto tr1118;
+ goto tr1109;
+tr1119:
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st343;
+tr1141:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st343;
+st343:
+ if ( ++p == pe )
+ goto _test_eof343;
+case 343:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1122;
+ case 32: goto tr1122;
+ case 40: goto tr1123;
+ case 41: goto tr1124;
+ case 42: goto tr1125;
+ case 45: goto tr1125;
+ case 92: goto tr1126;
+ case 95: goto tr1125;
+ case 1802: goto tr1127;
+ case 1851: goto tr1128;
+ case 2058: goto tr1127;
+ case 2107: goto tr1128;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr1125;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1125;
+ } else
+ goto tr1125;
+ goto tr1121;
+tr1130:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st344;
+tr1131:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st344;
+tr1122:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st344;
+tr1123:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st344;
+tr1124:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st344;
+st344:
+ if ( ++p == pe )
+ goto _test_eof344;
+case 344:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st344;
+ case 32: goto st344;
+ case 40: goto tr1130;
+ case 41: goto tr1131;
+ case 1802: goto tr1132;
+ case 1851: goto tr1133;
+ case 2058: goto tr1132;
+ case 2107: goto tr1133;
+ }
+ goto tr1121;
+tr1127:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->zone_origin_length = s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1412;
+tr1132:
+ {
+ s->zone_origin_length = s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1412;
+tr1135:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1412; goto _out;}
+ }
+ }
+ }
+ {
+ s->zone_origin_length = s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1412;
+st1412:
+ if ( ++p == pe )
+ goto _test_eof1412;
+case 1412:
+ goto st0;
+tr1133:
+ {
+ s->buffer_length = 0;
+ }
+ goto st345;
+tr1134:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st345;
+tr1128:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st345;
+st345:
+ if ( ++p == pe )
+ goto _test_eof345;
+case 345:
+ if ( (*p) == 10 )
+ goto tr1135;
+ goto tr1134;
+tr1126:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st346;
+tr1142:
+ {
+ s->dname_tmp_length++;
+ }
+ goto st346;
+tr1117:
+ {
+ s->dname = s->zone_origin;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st346;
+st346:
+ if ( ++p == pe )
+ goto _test_eof346;
+case 346:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1136;
+ goto tr1118;
+tr1136:
+ {
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length] = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st347;
+st347:
+ if ( ++p == pe )
+ goto _test_eof347;
+case 347:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1138;
+ goto tr1137;
+tr1138:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st348;
+st348:
+ if ( ++p == pe )
+ goto _test_eof348;
+case 348:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1139;
+ goto tr1137;
+tr1139:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st349;
+st349:
+ if ( ++p == pe )
+ goto _test_eof349;
+case 349:
+ switch( (*p) ) {
+ case 42: goto tr1140;
+ case 46: goto tr1141;
+ case 92: goto tr1142;
+ case 95: goto tr1140;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr1140;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1140;
+ } else
+ goto tr1140;
+ goto tr1137;
+tr1116:
+ {
+ s->dname = s->zone_origin;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ goto st350;
+st350:
+ if ( ++p == pe )
+ goto _test_eof350;
+case 350:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1122;
+ case 32: goto tr1122;
+ case 40: goto tr1123;
+ case 41: goto tr1124;
+ case 1802: goto tr1127;
+ case 1851: goto tr1128;
+ case 2058: goto tr1127;
+ case 2107: goto tr1128;
+ }
+ goto tr1121;
+tr1114:
+ {
+ s->buffer_length = 0;
+ }
+ goto st351;
+tr1143:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st351;
+st351:
+ if ( ++p == pe )
+ goto _test_eof351;
+case 351:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1144;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1143;
+ goto tr1109;
+st352:
+ if ( ++p == pe )
+ goto _test_eof352;
+case 352:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st353;
+ case 32: goto st353;
+ case 40: goto tr1146;
+ case 41: goto tr1147;
+ case 2058: goto tr1148;
+ case 2107: goto tr1149;
+ }
+ goto st0;
+tr1146:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st353;
+tr1147:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st353;
+tr1148:
+ {
+ s->line_counter++;
+ }
+ goto st353;
+tr1202:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 353; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st353;
+st353:
+ if ( ++p == pe )
+ goto _test_eof353;
+case 353:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st353;
+ case 32: goto st353;
+ case 40: goto tr1146;
+ case 41: goto tr1147;
+ case 2058: goto tr1148;
+ case 2107: goto tr1149;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr1150;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr1150;
+ } else
+ goto tr1150;
+ goto tr1151;
+tr1150:
+ {
+ rdata_tail = s->r_data;
+ }
+ { p--; {stack[top++] = 354;goto st318;} }
+ goto st354;
+st354:
+ if ( ++p == pe )
+ goto _test_eof354;
+case 354:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1153;
+ case 32: goto tr1153;
+ case 40: goto tr1154;
+ case 41: goto tr1155;
+ case 1802: goto tr1156;
+ case 1851: goto tr1157;
+ case 2058: goto tr1156;
+ case 2107: goto tr1158;
+ }
+ goto tr1152;
+tr1161:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st355;
+tr1162:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st355;
+tr1153:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st355;
+tr1154:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st355;
+tr1155:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st355;
+st355:
+ if ( ++p == pe )
+ goto _test_eof355;
+case 355:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st355;
+ case 32: goto st355;
+ case 40: goto tr1161;
+ case 41: goto tr1162;
+ case 42: goto tr1163;
+ case 46: goto tr1164;
+ case 92: goto tr1165;
+ case 95: goto tr1163;
+ case 1802: goto tr1166;
+ case 1851: goto tr1167;
+ case 2058: goto tr1166;
+ case 2107: goto tr1168;
+ }
+ if ( _widec < 65 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr1163;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1163;
+ } else
+ goto tr1163;
+ goto tr1159;
+tr1170:
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st356;
+tr1176:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st356;
+tr1189:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st356;
+tr1163:
+ {
+ s->dname = s->r_data;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st356;
+st356:
+ if ( ++p == pe )
+ goto _test_eof356;
+case 356:
+ switch( (*p) ) {
+ case 42: goto tr1170;
+ case 46: goto tr1171;
+ case 92: goto st360;
+ case 95: goto tr1170;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr1170;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1170;
+ } else
+ goto tr1170;
+ goto tr1169;
+tr1171:
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st357;
+tr1190:
+ {
+ s->dname_tmp_length++;
+ }
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st357;
+st357:
+ if ( ++p == pe )
+ goto _test_eof357;
+case 357:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1173;
+ case 32: goto tr1173;
+ case 40: goto tr1174;
+ case 41: goto tr1175;
+ case 42: goto tr1176;
+ case 45: goto tr1176;
+ case 92: goto tr1177;
+ case 95: goto tr1176;
+ case 1802: goto tr1178;
+ case 1851: goto tr1179;
+ case 2058: goto tr1178;
+ case 2107: goto tr1179;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr1176;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1176;
+ } else
+ goto tr1176;
+ goto tr1159;
+tr1181:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st358;
+tr1182:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st358;
+tr1173:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st358;
+tr1174:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st358;
+tr1175:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st358;
+st358:
+ if ( ++p == pe )
+ goto _test_eof358;
+case 358:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st358;
+ case 32: goto st358;
+ case 40: goto tr1181;
+ case 41: goto tr1182;
+ case 1802: goto tr1166;
+ case 1851: goto tr1167;
+ case 2058: goto tr1166;
+ case 2107: goto tr1167;
+ }
+ goto tr89;
+tr1156:
+ cs = 1413;
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {goto st307;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {goto st307;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+tr1166:
+ cs = 1413;
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {goto st307;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {goto st307;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+tr1178:
+ cs = 1413;
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {goto st307;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {goto st307;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+tr1184:
+ cs = 1413;
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out;}
+ }
+ }
+ }
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {goto st307;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {goto st307;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ s->line_counter++;
+ }
+ goto _again;
+st1413:
+ if ( ++p == pe )
+ goto _test_eof1413;
+case 1413:
+ goto st0;
+tr1167:
+ {
+ s->buffer_length = 0;
+ }
+ goto st359;
+tr1183:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st359;
+tr1157:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st359;
+tr1179:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st359;
+st359:
+ if ( ++p == pe )
+ goto _test_eof359;
+case 359:
+ if ( (*p) == 10 )
+ goto tr1184;
+ goto tr1183;
+tr1177:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st360;
+tr1191:
+ {
+ s->dname_tmp_length++;
+ }
+ goto st360;
+tr1165:
+ {
+ s->dname = s->r_data;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ goto st360;
+st360:
+ if ( ++p == pe )
+ goto _test_eof360;
+case 360:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1185;
+ goto tr1170;
+tr1185:
+ {
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length] = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st361;
+st361:
+ if ( ++p == pe )
+ goto _test_eof361;
+case 361:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1187;
+ goto tr1186;
+tr1187:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st362;
+st362:
+ if ( ++p == pe )
+ goto _test_eof362;
+case 362:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1188;
+ goto tr1186;
+tr1188:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ goto st363;
+st363:
+ if ( ++p == pe )
+ goto _test_eof363;
+case 363:
+ switch( (*p) ) {
+ case 42: goto tr1189;
+ case 46: goto tr1190;
+ case 92: goto tr1191;
+ case 95: goto tr1189;
+ }
+ if ( (*p) < 65 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr1189;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1189;
+ } else
+ goto tr1189;
+ goto tr1186;
+tr1164:
+ {
+ s->dname = s->r_data;
+ }
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ goto st364;
+st364:
+ if ( ++p == pe )
+ goto _test_eof364;
+case 364:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1173;
+ case 32: goto tr1173;
+ case 40: goto tr1174;
+ case 41: goto tr1175;
+ case 1802: goto tr1178;
+ case 1851: goto tr1179;
+ case 2058: goto tr1178;
+ case 2107: goto tr1179;
+ }
+ goto tr1159;
+tr1168:
+ {
+ s->buffer_length = 0;
+ }
+ goto st365;
+tr1192:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st365;
+tr1158:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ goto st365;
+st365:
+ if ( ++p == pe )
+ goto _test_eof365;
+case 365:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 1802: goto tr1184;
+ case 2058: goto tr1193;
+ }
+ if ( _widec > 1919 ) {
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1192;
+ } else if ( _widec >= 1664 )
+ goto tr1183;
+ goto tr89;
+tr1193:
+ cs = 1414;
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {goto st307;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {goto st307;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {goto st307;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto _again;
+st1414:
+ if ( ++p == pe )
+ goto _test_eof1414;
+case 1414:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st366;
+ case 32: goto st366;
+ case 40: goto tr1195;
+ case 41: goto tr1196;
+ case 42: goto tr1163;
+ case 46: goto tr1164;
+ case 92: goto tr1165;
+ case 95: goto tr1163;
+ case 2058: goto tr1197;
+ case 2107: goto tr1198;
+ }
+ if ( _widec < 65 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr1163;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1163;
+ } else
+ goto tr1163;
+ goto tr1169;
+tr1195:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st366;
+tr1196:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st366;
+tr1197:
+ {
+ s->line_counter++;
+ }
+ goto st366;
+tr1200:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 366; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st366;
+st366:
+ if ( ++p == pe )
+ goto _test_eof366;
+case 366:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st366;
+ case 32: goto st366;
+ case 40: goto tr1195;
+ case 41: goto tr1196;
+ case 42: goto tr1163;
+ case 46: goto tr1164;
+ case 92: goto tr1165;
+ case 95: goto tr1163;
+ case 2058: goto tr1197;
+ case 2107: goto tr1198;
+ }
+ if ( _widec < 65 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr1163;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1163;
+ } else
+ goto tr1163;
+ goto tr1169;
+tr1198:
+ {
+ s->buffer_length = 0;
+ }
+ goto st367;
+tr1199:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st367;
+st367:
+ if ( ++p == pe )
+ goto _test_eof367;
+case 367:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1200;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1199;
+ goto st0;
+tr1149:
+ {
+ s->buffer_length = 0;
+ }
+ goto st368;
+tr1201:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st368;
+st368:
+ if ( ++p == pe )
+ goto _test_eof368;
+case 368:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1202;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1201;
+ goto st0;
+st369:
+ if ( ++p == pe )
+ goto _test_eof369;
+case 369:
+ if ( (*p) == 43 )
+ goto tr1204;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1204;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1204;
+ } else
+ goto tr1204;
+ goto tr1203;
+tr1204:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st370;
+st370:
+ if ( ++p == pe )
+ goto _test_eof370;
+case 370:
+ if ( (*p) == 43 )
+ goto tr1205;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1205;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1205;
+ } else
+ goto tr1205;
+ goto tr1203;
+tr1205:
+ {
+ *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st371;
+st371:
+ if ( ++p == pe )
+ goto _test_eof371;
+case 371:
+ switch( (*p) ) {
+ case 43: goto tr1206;
+ case 61: goto st375;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1206;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1206;
+ } else
+ goto tr1206;
+ goto tr1203;
+tr1206:
+ {
+ *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st372;
+st372:
+ if ( ++p == pe )
+ goto _test_eof372;
+case 372:
+ switch( (*p) ) {
+ case 43: goto tr1208;
+ case 61: goto st373;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1208;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1208;
+ } else
+ goto tr1208;
+ goto tr1203;
+tr1210:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st373;
+tr1211:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st373;
+tr1212:
+ {
+ s->line_counter++;
+ }
+ goto st373;
+tr1218:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 373; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st373;
+tr1208:
+ {
+ *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)];
+ }
+ goto st373;
+st373:
+ if ( ++p == pe )
+ goto _test_eof373;
+case 373:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st373;
+ case 32: goto st373;
+ case 40: goto tr1210;
+ case 41: goto tr1211;
+ case 43: goto tr1204;
+ case 3082: goto tr1212;
+ case 3131: goto tr1213;
+ case 3338: goto tr1214;
+ case 3387: goto tr1214;
+ case 3594: goto tr1215;
+ case 3643: goto tr1216;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr1204;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1204;
+ } else
+ goto tr1204;
+ goto tr1203;
+tr1213:
+ {
+ s->buffer_length = 0;
+ }
+ goto st374;
+tr1217:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st374;
+st374:
+ if ( ++p == pe )
+ goto _test_eof374;
+case 374:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1218;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1217;
+ goto tr1203;
+tr1214:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1415;
+st1415:
+ if ( ++p == pe )
+ goto _test_eof1415;
+case 1415:
+ goto st0;
+tr1215:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1416;
+st1416:
+ if ( ++p == pe )
+ goto _test_eof1416;
+case 1416:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st373;
+ case 32: goto st373;
+ case 40: goto tr1210;
+ case 41: goto tr1211;
+ case 43: goto tr1204;
+ case 3082: goto tr1212;
+ case 3131: goto tr1213;
+ case 3338: goto tr1214;
+ case 3387: goto tr1214;
+ case 3594: goto tr1215;
+ case 3643: goto tr1216;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr1204;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1204;
+ } else
+ goto tr1204;
+ goto tr1203;
+tr1216:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1417;
+st1417:
+ if ( ++p == pe )
+ goto _test_eof1417;
+case 1417:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1218;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1217;
+ goto tr1203;
+st375:
+ if ( ++p == pe )
+ goto _test_eof375;
+case 375:
+ if ( (*p) == 61 )
+ goto st373;
+ goto tr1203;
+st376:
+ if ( ++p == pe )
+ goto _test_eof376;
+case 376:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1220;
+ case 32: goto tr1220;
+ case 40: goto tr1221;
+ case 41: goto tr1222;
+ case 3082: goto tr1223;
+ case 3131: goto tr1224;
+ case 3338: goto tr1225;
+ case 3387: goto tr1225;
+ case 3594: goto tr1226;
+ case 3643: goto tr1227;
+ }
+ goto tr1219;
+tr1229:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1230:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1248:
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1275:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 377; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1220:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ goto st377;
+tr1221:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1222:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1223:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1253:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ goto st377;
+tr1254:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1255:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1259:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1266:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ goto st377;
+tr1267:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1268:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1269:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1282:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ goto st377;
+tr1283:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1284:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1285:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1296:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ goto st377;
+tr1297:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1298:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1299:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1304:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ goto st377;
+tr1305:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1306:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1307:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1314:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ goto st377;
+tr1315:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1316:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1317:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1325:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ goto st377;
+tr1326:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1327:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1328:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1336:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ goto st377;
+tr1337:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1338:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1339:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1350:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ goto st377;
+tr1351:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1352:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1353:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1362:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ goto st377;
+tr1363:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1364:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1365:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1373:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ goto st377;
+tr1374:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1375:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1376:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1381:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ goto st377;
+tr1382:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1383:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1384:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1394:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ goto st377;
+tr1395:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1396:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1397:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1403:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ goto st377;
+tr1404:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1405:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1406:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1416:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ goto st377;
+tr1417:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1418:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1419:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1427:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ goto st377;
+tr1428:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1429:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1430:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1442:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ goto st377;
+tr1443:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1444:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1445:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1453:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ goto st377;
+tr1454:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1455:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1456:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1461:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ goto st377;
+tr1462:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1463:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1464:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1474:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ goto st377;
+tr1475:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1476:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1477:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1483:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ goto st377;
+tr1484:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1485:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1486:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1492:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ goto st377;
+tr1493:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1494:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1495:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1500:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ goto st377;
+tr1501:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1502:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1503:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1513:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ goto st377;
+tr1514:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1515:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1516:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1521:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ goto st377;
+tr1522:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1523:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1524:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1535:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ goto st377;
+tr1536:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1537:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1538:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1544:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ goto st377;
+tr1545:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1546:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1547:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1552:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ goto st377;
+tr1553:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1554:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1556:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1562:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ goto st377;
+tr1563:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1564:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1566:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1571:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ goto st377;
+tr1572:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1573:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1575:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1584:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ goto st377;
+tr1585:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1586:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1587:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1601:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ goto st377;
+tr1602:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1603:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1604:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1611:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ goto st377;
+tr1612:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1613:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1614:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1622:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ goto st377;
+tr1623:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1624:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1625:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1633:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ goto st377;
+tr1634:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1635:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1636:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1641:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ goto st377;
+tr1642:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1643:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1644:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1659:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ goto st377;
+tr1660:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1661:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1662:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1668:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ goto st377;
+tr1669:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1670:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1671:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1677:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ goto st377;
+tr1678:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1679:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1680:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1686:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ goto st377;
+tr1687:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1688:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1689:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1697:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ goto st377;
+tr1698:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1699:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1700:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1707:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ goto st377;
+tr1708:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1709:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1710:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1720:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ goto st377;
+tr1721:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1722:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1723:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1729:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ goto st377;
+tr1730:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1731:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1732:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1740:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st377;
+tr1741:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1742:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1744:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1751:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ goto st377;
+tr1752:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1753:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1754:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1764:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ goto st377;
+tr1765:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1766:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1767:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1775:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ goto st377;
+tr1776:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1777:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1778:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+tr1784:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ goto st377;
+tr1785:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st377;
+tr1786:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st377;
+tr1787:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ s->line_counter++;
+ }
+ goto st377;
+st377:
+ if ( ++p == pe )
+ goto _test_eof377;
+case 377:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st377;
+ case 32: goto st377;
+ case 40: goto tr1229;
+ case 41: goto tr1230;
+ case 65: goto st378;
+ case 67: goto st383;
+ case 68: goto st404;
+ case 69: goto st418;
+ case 72: goto st425;
+ case 73: goto st434;
+ case 75: goto st442;
+ case 76: goto st446;
+ case 77: goto st454;
+ case 78: goto st460;
+ case 79: goto st476;
+ case 80: goto st486;
+ case 82: goto st489;
+ case 83: goto st496;
+ case 84: goto st515;
+ case 85: goto st525;
+ case 90: goto st528;
+ case 97: goto st378;
+ case 99: goto st383;
+ case 100: goto st404;
+ case 101: goto st418;
+ case 104: goto st425;
+ case 105: goto st434;
+ case 107: goto st442;
+ case 108: goto st446;
+ case 109: goto st454;
+ case 110: goto st460;
+ case 111: goto st476;
+ case 112: goto st486;
+ case 114: goto st489;
+ case 115: goto st496;
+ case 116: goto st515;
+ case 117: goto st525;
+ case 122: goto st528;
+ case 3082: goto tr1248;
+ case 3131: goto tr1249;
+ case 3338: goto tr1250;
+ case 3387: goto tr1250;
+ case 3594: goto tr1251;
+ case 3643: goto tr1252;
+ }
+ goto tr1219;
+st378:
+ if ( ++p == pe )
+ goto _test_eof378;
+case 378:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1253;
+ case 32: goto tr1253;
+ case 40: goto tr1254;
+ case 41: goto tr1255;
+ case 65: goto st379;
+ case 70: goto st534;
+ case 80: goto st538;
+ case 97: goto st379;
+ case 102: goto st534;
+ case 112: goto st538;
+ case 3082: goto tr1259;
+ case 3131: goto tr1260;
+ case 3338: goto tr1261;
+ case 3387: goto tr1261;
+ case 3594: goto tr1262;
+ case 3643: goto tr1263;
+ }
+ goto tr1219;
+st379:
+ if ( ++p == pe )
+ goto _test_eof379;
+case 379:
+ switch( (*p) ) {
+ case 65: goto st380;
+ case 97: goto st380;
+ }
+ goto tr1219;
+st380:
+ if ( ++p == pe )
+ goto _test_eof380;
+case 380:
+ switch( (*p) ) {
+ case 65: goto st381;
+ case 97: goto st381;
+ }
+ goto tr1219;
+st381:
+ if ( ++p == pe )
+ goto _test_eof381;
+case 381:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1266;
+ case 32: goto tr1266;
+ case 40: goto tr1267;
+ case 41: goto tr1268;
+ case 3082: goto tr1269;
+ case 3131: goto tr1270;
+ case 3338: goto tr1271;
+ case 3387: goto tr1271;
+ case 3594: goto tr1272;
+ case 3643: goto tr1273;
+ }
+ goto tr1219;
+tr1249:
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1274:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st382;
+tr1224:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1260:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1270:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1286:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1300:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1308:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1318:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1329:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1340:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1354:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1366:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1377:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1385:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1398:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1407:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1420:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1431:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1446:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1457:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1465:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1478:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1487:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1496:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1504:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1517:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1525:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1539:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1548:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1557:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1567:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1576:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1588:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1605:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1615:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1626:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1637:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1645:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1663:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1672:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1681:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1690:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1701:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1711:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1724:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1733:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1745:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1755:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1768:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1779:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+tr1788:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st382;
+st382:
+ if ( ++p == pe )
+ goto _test_eof382;
+case 382:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1275;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1274;
+ goto tr1219;
+tr1225:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1250:
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1261:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1271:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1287:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1301:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1309:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1319:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1330:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1341:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1355:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1367:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1378:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1386:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1399:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1408:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1421:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1432:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1447:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1458:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1466:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1479:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1488:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1497:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1505:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1518:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1526:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1540:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1549:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1558:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1568:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1577:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1589:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1606:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1616:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1627:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1638:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1646:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1664:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1673:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1682:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1691:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1702:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1712:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1725:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1734:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1746:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1756:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1769:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1780:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+tr1789:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1418;
+st1418:
+ if ( ++p == pe )
+ goto _test_eof1418;
+case 1418:
+ goto st0;
+tr1226:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1251:
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1262:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1272:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1288:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1302:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1310:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1320:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1331:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1342:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1356:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1368:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1379:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1387:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1400:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1409:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1422:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1433:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1448:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1459:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1467:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1480:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1489:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1498:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1506:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1519:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1527:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1541:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1550:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1559:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1569:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1578:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1590:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1607:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1617:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1628:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1639:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1647:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1665:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1674:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1683:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1692:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1703:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1713:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1726:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1735:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1747:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1757:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1770:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1781:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+tr1790:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ s->line_counter++;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1419;
+st1419:
+ if ( ++p == pe )
+ goto _test_eof1419;
+case 1419:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st377;
+ case 32: goto st377;
+ case 40: goto tr1229;
+ case 41: goto tr1230;
+ case 65: goto st378;
+ case 67: goto st383;
+ case 68: goto st404;
+ case 69: goto st418;
+ case 72: goto st425;
+ case 73: goto st434;
+ case 75: goto st442;
+ case 76: goto st446;
+ case 77: goto st454;
+ case 78: goto st460;
+ case 79: goto st476;
+ case 80: goto st486;
+ case 82: goto st489;
+ case 83: goto st496;
+ case 84: goto st515;
+ case 85: goto st525;
+ case 90: goto st528;
+ case 97: goto st378;
+ case 99: goto st383;
+ case 100: goto st404;
+ case 101: goto st418;
+ case 104: goto st425;
+ case 105: goto st434;
+ case 107: goto st442;
+ case 108: goto st446;
+ case 109: goto st454;
+ case 110: goto st460;
+ case 111: goto st476;
+ case 112: goto st486;
+ case 114: goto st489;
+ case 115: goto st496;
+ case 116: goto st515;
+ case 117: goto st525;
+ case 122: goto st528;
+ case 3082: goto tr1248;
+ case 3131: goto tr1249;
+ case 3338: goto tr1250;
+ case 3387: goto tr1250;
+ case 3594: goto tr1251;
+ case 3643: goto tr1252;
+ }
+ goto tr1219;
+st383:
+ if ( ++p == pe )
+ goto _test_eof383;
+case 383:
+ switch( (*p) ) {
+ case 65: goto st384;
+ case 68: goto st386;
+ case 69: goto st393;
+ case 78: goto st396;
+ case 83: goto st400;
+ case 97: goto st384;
+ case 100: goto st386;
+ case 101: goto st393;
+ case 110: goto st396;
+ case 115: goto st400;
+ }
+ goto tr1219;
+st384:
+ if ( ++p == pe )
+ goto _test_eof384;
+case 384:
+ switch( (*p) ) {
+ case 65: goto st385;
+ case 97: goto st385;
+ }
+ goto tr1219;
+st385:
+ if ( ++p == pe )
+ goto _test_eof385;
+case 385:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1282;
+ case 32: goto tr1282;
+ case 40: goto tr1283;
+ case 41: goto tr1284;
+ case 3082: goto tr1285;
+ case 3131: goto tr1286;
+ case 3338: goto tr1287;
+ case 3387: goto tr1287;
+ case 3594: goto tr1288;
+ case 3643: goto tr1289;
+ }
+ goto tr1219;
+tr1227:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1252:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1263:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1273:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1289:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1303:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1311:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1321:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1332:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1343:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1357:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1369:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1380:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1388:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1401:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1410:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1423:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1434:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1449:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1460:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1468:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1481:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1490:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1499:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1507:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1520:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1528:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1542:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1551:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1560:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1570:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1579:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1591:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1608:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1618:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1629:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1640:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1648:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1666:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1675:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1684:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1693:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1704:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1714:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1727:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1736:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1748:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1758:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1771:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1782:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+tr1791:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1420;
+st1420:
+ if ( ++p == pe )
+ goto _test_eof1420;
+case 1420:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1275;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1274;
+ goto tr1219;
+st386:
+ if ( ++p == pe )
+ goto _test_eof386;
+case 386:
+ switch( (*p) ) {
+ case 78: goto st387;
+ case 83: goto st392;
+ case 110: goto st387;
+ case 115: goto st392;
+ }
+ goto tr1219;
+st387:
+ if ( ++p == pe )
+ goto _test_eof387;
+case 387:
+ switch( (*p) ) {
+ case 83: goto st388;
+ case 115: goto st388;
+ }
+ goto tr1219;
+st388:
+ if ( ++p == pe )
+ goto _test_eof388;
+case 388:
+ switch( (*p) ) {
+ case 75: goto st389;
+ case 107: goto st389;
+ }
+ goto tr1219;
+st389:
+ if ( ++p == pe )
+ goto _test_eof389;
+case 389:
+ switch( (*p) ) {
+ case 69: goto st390;
+ case 101: goto st390;
+ }
+ goto tr1219;
+st390:
+ if ( ++p == pe )
+ goto _test_eof390;
+case 390:
+ switch( (*p) ) {
+ case 89: goto st391;
+ case 121: goto st391;
+ }
+ goto tr1219;
+st391:
+ if ( ++p == pe )
+ goto _test_eof391;
+case 391:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1296;
+ case 32: goto tr1296;
+ case 40: goto tr1297;
+ case 41: goto tr1298;
+ case 3082: goto tr1299;
+ case 3131: goto tr1300;
+ case 3338: goto tr1301;
+ case 3387: goto tr1301;
+ case 3594: goto tr1302;
+ case 3643: goto tr1303;
+ }
+ goto tr1219;
+st392:
+ if ( ++p == pe )
+ goto _test_eof392;
+case 392:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1304;
+ case 32: goto tr1304;
+ case 40: goto tr1305;
+ case 41: goto tr1306;
+ case 3082: goto tr1307;
+ case 3131: goto tr1308;
+ case 3338: goto tr1309;
+ case 3387: goto tr1309;
+ case 3594: goto tr1310;
+ case 3643: goto tr1311;
+ }
+ goto tr1219;
+st393:
+ if ( ++p == pe )
+ goto _test_eof393;
+case 393:
+ switch( (*p) ) {
+ case 82: goto st394;
+ case 114: goto st394;
+ }
+ goto tr1219;
+st394:
+ if ( ++p == pe )
+ goto _test_eof394;
+case 394:
+ switch( (*p) ) {
+ case 84: goto st395;
+ case 116: goto st395;
+ }
+ goto tr1219;
+st395:
+ if ( ++p == pe )
+ goto _test_eof395;
+case 395:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1314;
+ case 32: goto tr1314;
+ case 40: goto tr1315;
+ case 41: goto tr1316;
+ case 3082: goto tr1317;
+ case 3131: goto tr1318;
+ case 3338: goto tr1319;
+ case 3387: goto tr1319;
+ case 3594: goto tr1320;
+ case 3643: goto tr1321;
+ }
+ goto tr1219;
+st396:
+ if ( ++p == pe )
+ goto _test_eof396;
+case 396:
+ switch( (*p) ) {
+ case 65: goto st397;
+ case 97: goto st397;
+ }
+ goto tr1219;
+st397:
+ if ( ++p == pe )
+ goto _test_eof397;
+case 397:
+ switch( (*p) ) {
+ case 77: goto st398;
+ case 109: goto st398;
+ }
+ goto tr1219;
+st398:
+ if ( ++p == pe )
+ goto _test_eof398;
+case 398:
+ switch( (*p) ) {
+ case 69: goto st399;
+ case 101: goto st399;
+ }
+ goto tr1219;
+st399:
+ if ( ++p == pe )
+ goto _test_eof399;
+case 399:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1325;
+ case 32: goto tr1325;
+ case 40: goto tr1326;
+ case 41: goto tr1327;
+ case 3082: goto tr1328;
+ case 3131: goto tr1329;
+ case 3338: goto tr1330;
+ case 3387: goto tr1330;
+ case 3594: goto tr1331;
+ case 3643: goto tr1332;
+ }
+ goto tr1219;
+st400:
+ if ( ++p == pe )
+ goto _test_eof400;
+case 400:
+ switch( (*p) ) {
+ case 89: goto st401;
+ case 121: goto st401;
+ }
+ goto tr1219;
+st401:
+ if ( ++p == pe )
+ goto _test_eof401;
+case 401:
+ switch( (*p) ) {
+ case 78: goto st402;
+ case 110: goto st402;
+ }
+ goto tr1219;
+st402:
+ if ( ++p == pe )
+ goto _test_eof402;
+case 402:
+ switch( (*p) ) {
+ case 67: goto st403;
+ case 99: goto st403;
+ }
+ goto tr1219;
+st403:
+ if ( ++p == pe )
+ goto _test_eof403;
+case 403:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1336;
+ case 32: goto tr1336;
+ case 40: goto tr1337;
+ case 41: goto tr1338;
+ case 3082: goto tr1339;
+ case 3131: goto tr1340;
+ case 3338: goto tr1341;
+ case 3387: goto tr1341;
+ case 3594: goto tr1342;
+ case 3643: goto tr1343;
+ }
+ goto tr1219;
+st404:
+ if ( ++p == pe )
+ goto _test_eof404;
+case 404:
+ switch( (*p) ) {
+ case 72: goto st405;
+ case 78: goto st409;
+ case 83: goto st417;
+ case 104: goto st405;
+ case 110: goto st409;
+ case 115: goto st417;
+ }
+ goto tr1219;
+st405:
+ if ( ++p == pe )
+ goto _test_eof405;
+case 405:
+ switch( (*p) ) {
+ case 67: goto st406;
+ case 99: goto st406;
+ }
+ goto tr1219;
+st406:
+ if ( ++p == pe )
+ goto _test_eof406;
+case 406:
+ switch( (*p) ) {
+ case 73: goto st407;
+ case 105: goto st407;
+ }
+ goto tr1219;
+st407:
+ if ( ++p == pe )
+ goto _test_eof407;
+case 407:
+ switch( (*p) ) {
+ case 68: goto st408;
+ case 100: goto st408;
+ }
+ goto tr1219;
+st408:
+ if ( ++p == pe )
+ goto _test_eof408;
+case 408:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1350;
+ case 32: goto tr1350;
+ case 40: goto tr1351;
+ case 41: goto tr1352;
+ case 3082: goto tr1353;
+ case 3131: goto tr1354;
+ case 3338: goto tr1355;
+ case 3387: goto tr1355;
+ case 3594: goto tr1356;
+ case 3643: goto tr1357;
+ }
+ goto tr1219;
+st409:
+ if ( ++p == pe )
+ goto _test_eof409;
+case 409:
+ switch( (*p) ) {
+ case 65: goto st410;
+ case 83: goto st413;
+ case 97: goto st410;
+ case 115: goto st413;
+ }
+ goto tr1219;
+st410:
+ if ( ++p == pe )
+ goto _test_eof410;
+case 410:
+ switch( (*p) ) {
+ case 77: goto st411;
+ case 109: goto st411;
+ }
+ goto tr1219;
+st411:
+ if ( ++p == pe )
+ goto _test_eof411;
+case 411:
+ switch( (*p) ) {
+ case 69: goto st412;
+ case 101: goto st412;
+ }
+ goto tr1219;
+st412:
+ if ( ++p == pe )
+ goto _test_eof412;
+case 412:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1362;
+ case 32: goto tr1362;
+ case 40: goto tr1363;
+ case 41: goto tr1364;
+ case 3082: goto tr1365;
+ case 3131: goto tr1366;
+ case 3338: goto tr1367;
+ case 3387: goto tr1367;
+ case 3594: goto tr1368;
+ case 3643: goto tr1369;
+ }
+ goto tr1219;
+st413:
+ if ( ++p == pe )
+ goto _test_eof413;
+case 413:
+ switch( (*p) ) {
+ case 75: goto st414;
+ case 107: goto st414;
+ }
+ goto tr1219;
+st414:
+ if ( ++p == pe )
+ goto _test_eof414;
+case 414:
+ switch( (*p) ) {
+ case 69: goto st415;
+ case 101: goto st415;
+ }
+ goto tr1219;
+st415:
+ if ( ++p == pe )
+ goto _test_eof415;
+case 415:
+ switch( (*p) ) {
+ case 89: goto st416;
+ case 121: goto st416;
+ }
+ goto tr1219;
+st416:
+ if ( ++p == pe )
+ goto _test_eof416;
+case 416:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1373;
+ case 32: goto tr1373;
+ case 40: goto tr1374;
+ case 41: goto tr1375;
+ case 3082: goto tr1376;
+ case 3131: goto tr1377;
+ case 3338: goto tr1378;
+ case 3387: goto tr1378;
+ case 3594: goto tr1379;
+ case 3643: goto tr1380;
+ }
+ goto tr1219;
+st417:
+ if ( ++p == pe )
+ goto _test_eof417;
+case 417:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1381;
+ case 32: goto tr1381;
+ case 40: goto tr1382;
+ case 41: goto tr1383;
+ case 3082: goto tr1384;
+ case 3131: goto tr1385;
+ case 3338: goto tr1386;
+ case 3387: goto tr1386;
+ case 3594: goto tr1387;
+ case 3643: goto tr1388;
+ }
+ goto tr1219;
+st418:
+ if ( ++p == pe )
+ goto _test_eof418;
+case 418:
+ switch( (*p) ) {
+ case 85: goto st419;
+ case 117: goto st419;
+ }
+ goto tr1219;
+st419:
+ if ( ++p == pe )
+ goto _test_eof419;
+case 419:
+ switch( (*p) ) {
+ case 73: goto st420;
+ case 105: goto st420;
+ }
+ goto tr1219;
+st420:
+ if ( ++p == pe )
+ goto _test_eof420;
+case 420:
+ switch( (*p) ) {
+ case 52: goto st421;
+ case 54: goto st423;
+ }
+ goto tr1219;
+st421:
+ if ( ++p == pe )
+ goto _test_eof421;
+case 421:
+ if ( (*p) == 56 )
+ goto st422;
+ goto tr1219;
+st422:
+ if ( ++p == pe )
+ goto _test_eof422;
+case 422:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1394;
+ case 32: goto tr1394;
+ case 40: goto tr1395;
+ case 41: goto tr1396;
+ case 3082: goto tr1397;
+ case 3131: goto tr1398;
+ case 3338: goto tr1399;
+ case 3387: goto tr1399;
+ case 3594: goto tr1400;
+ case 3643: goto tr1401;
+ }
+ goto tr1219;
+st423:
+ if ( ++p == pe )
+ goto _test_eof423;
+case 423:
+ if ( (*p) == 52 )
+ goto st424;
+ goto tr1219;
+st424:
+ if ( ++p == pe )
+ goto _test_eof424;
+case 424:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1403;
+ case 32: goto tr1403;
+ case 40: goto tr1404;
+ case 41: goto tr1405;
+ case 3082: goto tr1406;
+ case 3131: goto tr1407;
+ case 3338: goto tr1408;
+ case 3387: goto tr1408;
+ case 3594: goto tr1409;
+ case 3643: goto tr1410;
+ }
+ goto tr1219;
+st425:
+ if ( ++p == pe )
+ goto _test_eof425;
+case 425:
+ switch( (*p) ) {
+ case 73: goto st426;
+ case 84: goto st430;
+ case 105: goto st426;
+ case 116: goto st430;
+ }
+ goto tr1219;
+st426:
+ if ( ++p == pe )
+ goto _test_eof426;
+case 426:
+ switch( (*p) ) {
+ case 78: goto st427;
+ case 110: goto st427;
+ }
+ goto tr1219;
+st427:
+ if ( ++p == pe )
+ goto _test_eof427;
+case 427:
+ switch( (*p) ) {
+ case 70: goto st428;
+ case 102: goto st428;
+ }
+ goto tr1219;
+st428:
+ if ( ++p == pe )
+ goto _test_eof428;
+case 428:
+ switch( (*p) ) {
+ case 79: goto st429;
+ case 111: goto st429;
+ }
+ goto tr1219;
+st429:
+ if ( ++p == pe )
+ goto _test_eof429;
+case 429:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1416;
+ case 32: goto tr1416;
+ case 40: goto tr1417;
+ case 41: goto tr1418;
+ case 3082: goto tr1419;
+ case 3131: goto tr1420;
+ case 3338: goto tr1421;
+ case 3387: goto tr1421;
+ case 3594: goto tr1422;
+ case 3643: goto tr1423;
+ }
+ goto tr1219;
+st430:
+ if ( ++p == pe )
+ goto _test_eof430;
+case 430:
+ switch( (*p) ) {
+ case 84: goto st431;
+ case 116: goto st431;
+ }
+ goto tr1219;
+st431:
+ if ( ++p == pe )
+ goto _test_eof431;
+case 431:
+ switch( (*p) ) {
+ case 80: goto st432;
+ case 112: goto st432;
+ }
+ goto tr1219;
+st432:
+ if ( ++p == pe )
+ goto _test_eof432;
+case 432:
+ switch( (*p) ) {
+ case 83: goto st433;
+ case 115: goto st433;
+ }
+ goto tr1219;
+st433:
+ if ( ++p == pe )
+ goto _test_eof433;
+case 433:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1427;
+ case 32: goto tr1427;
+ case 40: goto tr1428;
+ case 41: goto tr1429;
+ case 3082: goto tr1430;
+ case 3131: goto tr1431;
+ case 3338: goto tr1432;
+ case 3387: goto tr1432;
+ case 3594: goto tr1433;
+ case 3643: goto tr1434;
+ }
+ goto tr1219;
+st434:
+ if ( ++p == pe )
+ goto _test_eof434;
+case 434:
+ switch( (*p) ) {
+ case 80: goto st435;
+ case 112: goto st435;
+ }
+ goto tr1219;
+st435:
+ if ( ++p == pe )
+ goto _test_eof435;
+case 435:
+ switch( (*p) ) {
+ case 83: goto st436;
+ case 115: goto st436;
+ }
+ goto tr1219;
+st436:
+ if ( ++p == pe )
+ goto _test_eof436;
+case 436:
+ switch( (*p) ) {
+ case 69: goto st437;
+ case 101: goto st437;
+ }
+ goto tr1219;
+st437:
+ if ( ++p == pe )
+ goto _test_eof437;
+case 437:
+ switch( (*p) ) {
+ case 67: goto st438;
+ case 99: goto st438;
+ }
+ goto tr1219;
+st438:
+ if ( ++p == pe )
+ goto _test_eof438;
+case 438:
+ switch( (*p) ) {
+ case 75: goto st439;
+ case 107: goto st439;
+ }
+ goto tr1219;
+st439:
+ if ( ++p == pe )
+ goto _test_eof439;
+case 439:
+ switch( (*p) ) {
+ case 69: goto st440;
+ case 101: goto st440;
+ }
+ goto tr1219;
+st440:
+ if ( ++p == pe )
+ goto _test_eof440;
+case 440:
+ switch( (*p) ) {
+ case 89: goto st441;
+ case 121: goto st441;
+ }
+ goto tr1219;
+st441:
+ if ( ++p == pe )
+ goto _test_eof441;
+case 441:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1442;
+ case 32: goto tr1442;
+ case 40: goto tr1443;
+ case 41: goto tr1444;
+ case 3082: goto tr1445;
+ case 3131: goto tr1446;
+ case 3338: goto tr1447;
+ case 3387: goto tr1447;
+ case 3594: goto tr1448;
+ case 3643: goto tr1449;
+ }
+ goto tr1219;
+st442:
+ if ( ++p == pe )
+ goto _test_eof442;
+case 442:
+ switch( (*p) ) {
+ case 69: goto st443;
+ case 88: goto st445;
+ case 101: goto st443;
+ case 120: goto st445;
+ }
+ goto tr1219;
+st443:
+ if ( ++p == pe )
+ goto _test_eof443;
+case 443:
+ switch( (*p) ) {
+ case 89: goto st444;
+ case 121: goto st444;
+ }
+ goto tr1219;
+st444:
+ if ( ++p == pe )
+ goto _test_eof444;
+case 444:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1453;
+ case 32: goto tr1453;
+ case 40: goto tr1454;
+ case 41: goto tr1455;
+ case 3082: goto tr1456;
+ case 3131: goto tr1457;
+ case 3338: goto tr1458;
+ case 3387: goto tr1458;
+ case 3594: goto tr1459;
+ case 3643: goto tr1460;
+ }
+ goto tr1219;
+st445:
+ if ( ++p == pe )
+ goto _test_eof445;
+case 445:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1461;
+ case 32: goto tr1461;
+ case 40: goto tr1462;
+ case 41: goto tr1463;
+ case 3082: goto tr1464;
+ case 3131: goto tr1465;
+ case 3338: goto tr1466;
+ case 3387: goto tr1466;
+ case 3594: goto tr1467;
+ case 3643: goto tr1468;
+ }
+ goto tr1219;
+st446:
+ if ( ++p == pe )
+ goto _test_eof446;
+case 446:
+ switch( (*p) ) {
+ case 51: goto st447;
+ case 54: goto st449;
+ case 79: goto st451;
+ case 80: goto st453;
+ case 111: goto st451;
+ case 112: goto st453;
+ }
+ goto tr1219;
+st447:
+ if ( ++p == pe )
+ goto _test_eof447;
+case 447:
+ if ( (*p) == 50 )
+ goto st448;
+ goto tr1219;
+st448:
+ if ( ++p == pe )
+ goto _test_eof448;
+case 448:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1474;
+ case 32: goto tr1474;
+ case 40: goto tr1475;
+ case 41: goto tr1476;
+ case 3082: goto tr1477;
+ case 3131: goto tr1478;
+ case 3338: goto tr1479;
+ case 3387: goto tr1479;
+ case 3594: goto tr1480;
+ case 3643: goto tr1481;
+ }
+ goto tr1219;
+st449:
+ if ( ++p == pe )
+ goto _test_eof449;
+case 449:
+ if ( (*p) == 52 )
+ goto st450;
+ goto tr1219;
+st450:
+ if ( ++p == pe )
+ goto _test_eof450;
+case 450:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1483;
+ case 32: goto tr1483;
+ case 40: goto tr1484;
+ case 41: goto tr1485;
+ case 3082: goto tr1486;
+ case 3131: goto tr1487;
+ case 3338: goto tr1488;
+ case 3387: goto tr1488;
+ case 3594: goto tr1489;
+ case 3643: goto tr1490;
+ }
+ goto tr1219;
+st451:
+ if ( ++p == pe )
+ goto _test_eof451;
+case 451:
+ switch( (*p) ) {
+ case 67: goto st452;
+ case 99: goto st452;
+ }
+ goto tr1219;
+st452:
+ if ( ++p == pe )
+ goto _test_eof452;
+case 452:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1492;
+ case 32: goto tr1492;
+ case 40: goto tr1493;
+ case 41: goto tr1494;
+ case 3082: goto tr1495;
+ case 3131: goto tr1496;
+ case 3338: goto tr1497;
+ case 3387: goto tr1497;
+ case 3594: goto tr1498;
+ case 3643: goto tr1499;
+ }
+ goto tr1219;
+st453:
+ if ( ++p == pe )
+ goto _test_eof453;
+case 453:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1500;
+ case 32: goto tr1500;
+ case 40: goto tr1501;
+ case 41: goto tr1502;
+ case 3082: goto tr1503;
+ case 3131: goto tr1504;
+ case 3338: goto tr1505;
+ case 3387: goto tr1505;
+ case 3594: goto tr1506;
+ case 3643: goto tr1507;
+ }
+ goto tr1219;
+st454:
+ if ( ++p == pe )
+ goto _test_eof454;
+case 454:
+ switch( (*p) ) {
+ case 73: goto st455;
+ case 88: goto st459;
+ case 105: goto st455;
+ case 120: goto st459;
+ }
+ goto tr1219;
+st455:
+ if ( ++p == pe )
+ goto _test_eof455;
+case 455:
+ switch( (*p) ) {
+ case 78: goto st456;
+ case 110: goto st456;
+ }
+ goto tr1219;
+st456:
+ if ( ++p == pe )
+ goto _test_eof456;
+case 456:
+ switch( (*p) ) {
+ case 70: goto st457;
+ case 102: goto st457;
+ }
+ goto tr1219;
+st457:
+ if ( ++p == pe )
+ goto _test_eof457;
+case 457:
+ switch( (*p) ) {
+ case 79: goto st458;
+ case 111: goto st458;
+ }
+ goto tr1219;
+st458:
+ if ( ++p == pe )
+ goto _test_eof458;
+case 458:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1513;
+ case 32: goto tr1513;
+ case 40: goto tr1514;
+ case 41: goto tr1515;
+ case 3082: goto tr1516;
+ case 3131: goto tr1517;
+ case 3338: goto tr1518;
+ case 3387: goto tr1518;
+ case 3594: goto tr1519;
+ case 3643: goto tr1520;
+ }
+ goto tr1219;
+st459:
+ if ( ++p == pe )
+ goto _test_eof459;
+case 459:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1521;
+ case 32: goto tr1521;
+ case 40: goto tr1522;
+ case 41: goto tr1523;
+ case 3082: goto tr1524;
+ case 3131: goto tr1525;
+ case 3338: goto tr1526;
+ case 3387: goto tr1526;
+ case 3594: goto tr1527;
+ case 3643: goto tr1528;
+ }
+ goto tr1219;
+st460:
+ if ( ++p == pe )
+ goto _test_eof460;
+case 460:
+ switch( (*p) ) {
+ case 65: goto st461;
+ case 73: goto st465;
+ case 83: goto st467;
+ case 97: goto st461;
+ case 105: goto st465;
+ case 115: goto st467;
+ }
+ goto tr1219;
+st461:
+ if ( ++p == pe )
+ goto _test_eof461;
+case 461:
+ switch( (*p) ) {
+ case 80: goto st462;
+ case 112: goto st462;
+ }
+ goto tr1219;
+st462:
+ if ( ++p == pe )
+ goto _test_eof462;
+case 462:
+ switch( (*p) ) {
+ case 84: goto st463;
+ case 116: goto st463;
+ }
+ goto tr1219;
+st463:
+ if ( ++p == pe )
+ goto _test_eof463;
+case 463:
+ switch( (*p) ) {
+ case 82: goto st464;
+ case 114: goto st464;
+ }
+ goto tr1219;
+st464:
+ if ( ++p == pe )
+ goto _test_eof464;
+case 464:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1535;
+ case 32: goto tr1535;
+ case 40: goto tr1536;
+ case 41: goto tr1537;
+ case 3082: goto tr1538;
+ case 3131: goto tr1539;
+ case 3338: goto tr1540;
+ case 3387: goto tr1540;
+ case 3594: goto tr1541;
+ case 3643: goto tr1542;
+ }
+ goto tr1219;
+st465:
+ if ( ++p == pe )
+ goto _test_eof465;
+case 465:
+ switch( (*p) ) {
+ case 68: goto st466;
+ case 100: goto st466;
+ }
+ goto tr1219;
+st466:
+ if ( ++p == pe )
+ goto _test_eof466;
+case 466:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1544;
+ case 32: goto tr1544;
+ case 40: goto tr1545;
+ case 41: goto tr1546;
+ case 3082: goto tr1547;
+ case 3131: goto tr1548;
+ case 3338: goto tr1549;
+ case 3387: goto tr1549;
+ case 3594: goto tr1550;
+ case 3643: goto tr1551;
+ }
+ goto tr1219;
+st467:
+ if ( ++p == pe )
+ goto _test_eof467;
+case 467:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1552;
+ case 32: goto tr1552;
+ case 40: goto tr1553;
+ case 41: goto tr1554;
+ case 69: goto st468;
+ case 101: goto st468;
+ case 3082: goto tr1556;
+ case 3131: goto tr1557;
+ case 3338: goto tr1558;
+ case 3387: goto tr1558;
+ case 3594: goto tr1559;
+ case 3643: goto tr1560;
+ }
+ goto tr1219;
+st468:
+ if ( ++p == pe )
+ goto _test_eof468;
+case 468:
+ switch( (*p) ) {
+ case 67: goto st469;
+ case 99: goto st469;
+ }
+ goto tr1219;
+st469:
+ if ( ++p == pe )
+ goto _test_eof469;
+case 469:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1562;
+ case 32: goto tr1562;
+ case 40: goto tr1563;
+ case 41: goto tr1564;
+ case 51: goto st470;
+ case 3082: goto tr1566;
+ case 3131: goto tr1567;
+ case 3338: goto tr1568;
+ case 3387: goto tr1568;
+ case 3594: goto tr1569;
+ case 3643: goto tr1570;
+ }
+ goto tr1219;
+st470:
+ if ( ++p == pe )
+ goto _test_eof470;
+case 470:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1571;
+ case 32: goto tr1571;
+ case 40: goto tr1572;
+ case 41: goto tr1573;
+ case 80: goto st471;
+ case 112: goto st471;
+ case 3082: goto tr1575;
+ case 3131: goto tr1576;
+ case 3338: goto tr1577;
+ case 3387: goto tr1577;
+ case 3594: goto tr1578;
+ case 3643: goto tr1579;
+ }
+ goto tr1219;
+st471:
+ if ( ++p == pe )
+ goto _test_eof471;
+case 471:
+ switch( (*p) ) {
+ case 65: goto st472;
+ case 97: goto st472;
+ }
+ goto tr1219;
+st472:
+ if ( ++p == pe )
+ goto _test_eof472;
+case 472:
+ switch( (*p) ) {
+ case 82: goto st473;
+ case 114: goto st473;
+ }
+ goto tr1219;
+st473:
+ if ( ++p == pe )
+ goto _test_eof473;
+case 473:
+ switch( (*p) ) {
+ case 65: goto st474;
+ case 97: goto st474;
+ }
+ goto tr1219;
+st474:
+ if ( ++p == pe )
+ goto _test_eof474;
+case 474:
+ switch( (*p) ) {
+ case 77: goto st475;
+ case 109: goto st475;
+ }
+ goto tr1219;
+st475:
+ if ( ++p == pe )
+ goto _test_eof475;
+case 475:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1584;
+ case 32: goto tr1584;
+ case 40: goto tr1585;
+ case 41: goto tr1586;
+ case 3082: goto tr1587;
+ case 3131: goto tr1588;
+ case 3338: goto tr1589;
+ case 3387: goto tr1589;
+ case 3594: goto tr1590;
+ case 3643: goto tr1591;
+ }
+ goto tr1219;
+st476:
+ if ( ++p == pe )
+ goto _test_eof476;
+case 476:
+ switch( (*p) ) {
+ case 80: goto st477;
+ case 112: goto st477;
+ }
+ goto tr1219;
+st477:
+ if ( ++p == pe )
+ goto _test_eof477;
+case 477:
+ switch( (*p) ) {
+ case 69: goto st478;
+ case 101: goto st478;
+ }
+ goto tr1219;
+st478:
+ if ( ++p == pe )
+ goto _test_eof478;
+case 478:
+ switch( (*p) ) {
+ case 78: goto st479;
+ case 110: goto st479;
+ }
+ goto tr1219;
+st479:
+ if ( ++p == pe )
+ goto _test_eof479;
+case 479:
+ switch( (*p) ) {
+ case 80: goto st480;
+ case 112: goto st480;
+ }
+ goto tr1219;
+st480:
+ if ( ++p == pe )
+ goto _test_eof480;
+case 480:
+ switch( (*p) ) {
+ case 71: goto st481;
+ case 103: goto st481;
+ }
+ goto tr1219;
+st481:
+ if ( ++p == pe )
+ goto _test_eof481;
+case 481:
+ switch( (*p) ) {
+ case 80: goto st482;
+ case 112: goto st482;
+ }
+ goto tr1219;
+st482:
+ if ( ++p == pe )
+ goto _test_eof482;
+case 482:
+ switch( (*p) ) {
+ case 75: goto st483;
+ case 107: goto st483;
+ }
+ goto tr1219;
+st483:
+ if ( ++p == pe )
+ goto _test_eof483;
+case 483:
+ switch( (*p) ) {
+ case 69: goto st484;
+ case 101: goto st484;
+ }
+ goto tr1219;
+st484:
+ if ( ++p == pe )
+ goto _test_eof484;
+case 484:
+ switch( (*p) ) {
+ case 89: goto st485;
+ case 121: goto st485;
+ }
+ goto tr1219;
+st485:
+ if ( ++p == pe )
+ goto _test_eof485;
+case 485:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1601;
+ case 32: goto tr1601;
+ case 40: goto tr1602;
+ case 41: goto tr1603;
+ case 3082: goto tr1604;
+ case 3131: goto tr1605;
+ case 3338: goto tr1606;
+ case 3387: goto tr1606;
+ case 3594: goto tr1607;
+ case 3643: goto tr1608;
+ }
+ goto tr1219;
+st486:
+ if ( ++p == pe )
+ goto _test_eof486;
+case 486:
+ switch( (*p) ) {
+ case 84: goto st487;
+ case 116: goto st487;
+ }
+ goto tr1219;
+st487:
+ if ( ++p == pe )
+ goto _test_eof487;
+case 487:
+ switch( (*p) ) {
+ case 82: goto st488;
+ case 114: goto st488;
+ }
+ goto tr1219;
+st488:
+ if ( ++p == pe )
+ goto _test_eof488;
+case 488:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1611;
+ case 32: goto tr1611;
+ case 40: goto tr1612;
+ case 41: goto tr1613;
+ case 3082: goto tr1614;
+ case 3131: goto tr1615;
+ case 3338: goto tr1616;
+ case 3387: goto tr1616;
+ case 3594: goto tr1617;
+ case 3643: goto tr1618;
+ }
+ goto tr1219;
+st489:
+ if ( ++p == pe )
+ goto _test_eof489;
+case 489:
+ switch( (*p) ) {
+ case 80: goto st490;
+ case 82: goto st491;
+ case 84: goto st495;
+ case 112: goto st490;
+ case 114: goto st491;
+ case 116: goto st495;
+ }
+ goto tr1219;
+st490:
+ if ( ++p == pe )
+ goto _test_eof490;
+case 490:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1622;
+ case 32: goto tr1622;
+ case 40: goto tr1623;
+ case 41: goto tr1624;
+ case 3082: goto tr1625;
+ case 3131: goto tr1626;
+ case 3338: goto tr1627;
+ case 3387: goto tr1627;
+ case 3594: goto tr1628;
+ case 3643: goto tr1629;
+ }
+ goto tr1219;
+st491:
+ if ( ++p == pe )
+ goto _test_eof491;
+case 491:
+ switch( (*p) ) {
+ case 83: goto st492;
+ case 115: goto st492;
+ }
+ goto tr1219;
+st492:
+ if ( ++p == pe )
+ goto _test_eof492;
+case 492:
+ switch( (*p) ) {
+ case 73: goto st493;
+ case 105: goto st493;
+ }
+ goto tr1219;
+st493:
+ if ( ++p == pe )
+ goto _test_eof493;
+case 493:
+ switch( (*p) ) {
+ case 71: goto st494;
+ case 103: goto st494;
+ }
+ goto tr1219;
+st494:
+ if ( ++p == pe )
+ goto _test_eof494;
+case 494:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1633;
+ case 32: goto tr1633;
+ case 40: goto tr1634;
+ case 41: goto tr1635;
+ case 3082: goto tr1636;
+ case 3131: goto tr1637;
+ case 3338: goto tr1638;
+ case 3387: goto tr1638;
+ case 3594: goto tr1639;
+ case 3643: goto tr1640;
+ }
+ goto tr1219;
+st495:
+ if ( ++p == pe )
+ goto _test_eof495;
+case 495:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1641;
+ case 32: goto tr1641;
+ case 40: goto tr1642;
+ case 41: goto tr1643;
+ case 3082: goto tr1644;
+ case 3131: goto tr1645;
+ case 3338: goto tr1646;
+ case 3387: goto tr1646;
+ case 3594: goto tr1647;
+ case 3643: goto tr1648;
+ }
+ goto tr1219;
+st496:
+ if ( ++p == pe )
+ goto _test_eof496;
+case 496:
+ switch( (*p) ) {
+ case 77: goto st497;
+ case 79: goto st502;
+ case 80: goto st504;
+ case 82: goto st506;
+ case 83: goto st508;
+ case 86: goto st512;
+ case 109: goto st497;
+ case 111: goto st502;
+ case 112: goto st504;
+ case 114: goto st506;
+ case 115: goto st508;
+ case 118: goto st512;
+ }
+ goto tr1219;
+st497:
+ if ( ++p == pe )
+ goto _test_eof497;
+case 497:
+ switch( (*p) ) {
+ case 73: goto st498;
+ case 105: goto st498;
+ }
+ goto tr1219;
+st498:
+ if ( ++p == pe )
+ goto _test_eof498;
+case 498:
+ switch( (*p) ) {
+ case 77: goto st499;
+ case 109: goto st499;
+ }
+ goto tr1219;
+st499:
+ if ( ++p == pe )
+ goto _test_eof499;
+case 499:
+ switch( (*p) ) {
+ case 69: goto st500;
+ case 101: goto st500;
+ }
+ goto tr1219;
+st500:
+ if ( ++p == pe )
+ goto _test_eof500;
+case 500:
+ switch( (*p) ) {
+ case 65: goto st501;
+ case 97: goto st501;
+ }
+ goto tr1219;
+st501:
+ if ( ++p == pe )
+ goto _test_eof501;
+case 501:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1659;
+ case 32: goto tr1659;
+ case 40: goto tr1660;
+ case 41: goto tr1661;
+ case 3082: goto tr1662;
+ case 3131: goto tr1663;
+ case 3338: goto tr1664;
+ case 3387: goto tr1664;
+ case 3594: goto tr1665;
+ case 3643: goto tr1666;
+ }
+ goto tr1219;
+st502:
+ if ( ++p == pe )
+ goto _test_eof502;
+case 502:
+ switch( (*p) ) {
+ case 65: goto st503;
+ case 97: goto st503;
+ }
+ goto tr1219;
+st503:
+ if ( ++p == pe )
+ goto _test_eof503;
+case 503:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1668;
+ case 32: goto tr1668;
+ case 40: goto tr1669;
+ case 41: goto tr1670;
+ case 3082: goto tr1671;
+ case 3131: goto tr1672;
+ case 3338: goto tr1673;
+ case 3387: goto tr1673;
+ case 3594: goto tr1674;
+ case 3643: goto tr1675;
+ }
+ goto tr1219;
+st504:
+ if ( ++p == pe )
+ goto _test_eof504;
+case 504:
+ switch( (*p) ) {
+ case 70: goto st505;
+ case 102: goto st505;
+ }
+ goto tr1219;
+st505:
+ if ( ++p == pe )
+ goto _test_eof505;
+case 505:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1677;
+ case 32: goto tr1677;
+ case 40: goto tr1678;
+ case 41: goto tr1679;
+ case 3082: goto tr1680;
+ case 3131: goto tr1681;
+ case 3338: goto tr1682;
+ case 3387: goto tr1682;
+ case 3594: goto tr1683;
+ case 3643: goto tr1684;
+ }
+ goto tr1219;
+st506:
+ if ( ++p == pe )
+ goto _test_eof506;
+case 506:
+ switch( (*p) ) {
+ case 86: goto st507;
+ case 118: goto st507;
+ }
+ goto tr1219;
+st507:
+ if ( ++p == pe )
+ goto _test_eof507;
+case 507:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1686;
+ case 32: goto tr1686;
+ case 40: goto tr1687;
+ case 41: goto tr1688;
+ case 3082: goto tr1689;
+ case 3131: goto tr1690;
+ case 3338: goto tr1691;
+ case 3387: goto tr1691;
+ case 3594: goto tr1692;
+ case 3643: goto tr1693;
+ }
+ goto tr1219;
+st508:
+ if ( ++p == pe )
+ goto _test_eof508;
+case 508:
+ switch( (*p) ) {
+ case 72: goto st509;
+ case 104: goto st509;
+ }
+ goto tr1219;
+st509:
+ if ( ++p == pe )
+ goto _test_eof509;
+case 509:
+ switch( (*p) ) {
+ case 70: goto st510;
+ case 102: goto st510;
+ }
+ goto tr1219;
+st510:
+ if ( ++p == pe )
+ goto _test_eof510;
+case 510:
+ switch( (*p) ) {
+ case 80: goto st511;
+ case 112: goto st511;
+ }
+ goto tr1219;
+st511:
+ if ( ++p == pe )
+ goto _test_eof511;
+case 511:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1697;
+ case 32: goto tr1697;
+ case 40: goto tr1698;
+ case 41: goto tr1699;
+ case 3082: goto tr1700;
+ case 3131: goto tr1701;
+ case 3338: goto tr1702;
+ case 3387: goto tr1702;
+ case 3594: goto tr1703;
+ case 3643: goto tr1704;
+ }
+ goto tr1219;
+st512:
+ if ( ++p == pe )
+ goto _test_eof512;
+case 512:
+ switch( (*p) ) {
+ case 67: goto st513;
+ case 99: goto st513;
+ }
+ goto tr1219;
+st513:
+ if ( ++p == pe )
+ goto _test_eof513;
+case 513:
+ switch( (*p) ) {
+ case 66: goto st514;
+ case 98: goto st514;
+ }
+ goto tr1219;
+st514:
+ if ( ++p == pe )
+ goto _test_eof514;
+case 514:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1707;
+ case 32: goto tr1707;
+ case 40: goto tr1708;
+ case 41: goto tr1709;
+ case 3082: goto tr1710;
+ case 3131: goto tr1711;
+ case 3338: goto tr1712;
+ case 3387: goto tr1712;
+ case 3594: goto tr1713;
+ case 3643: goto tr1714;
+ }
+ goto tr1219;
+st515:
+ if ( ++p == pe )
+ goto _test_eof515;
+case 515:
+ switch( (*p) ) {
+ case 76: goto st516;
+ case 88: goto st519;
+ case 89: goto st521;
+ case 108: goto st516;
+ case 120: goto st519;
+ case 121: goto st521;
+ }
+ goto tr1219;
+st516:
+ if ( ++p == pe )
+ goto _test_eof516;
+case 516:
+ switch( (*p) ) {
+ case 83: goto st517;
+ case 115: goto st517;
+ }
+ goto tr1219;
+st517:
+ if ( ++p == pe )
+ goto _test_eof517;
+case 517:
+ switch( (*p) ) {
+ case 65: goto st518;
+ case 97: goto st518;
+ }
+ goto tr1219;
+st518:
+ if ( ++p == pe )
+ goto _test_eof518;
+case 518:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1720;
+ case 32: goto tr1720;
+ case 40: goto tr1721;
+ case 41: goto tr1722;
+ case 3082: goto tr1723;
+ case 3131: goto tr1724;
+ case 3338: goto tr1725;
+ case 3387: goto tr1725;
+ case 3594: goto tr1726;
+ case 3643: goto tr1727;
+ }
+ goto tr1219;
+st519:
+ if ( ++p == pe )
+ goto _test_eof519;
+case 519:
+ switch( (*p) ) {
+ case 84: goto st520;
+ case 116: goto st520;
+ }
+ goto tr1219;
+st520:
+ if ( ++p == pe )
+ goto _test_eof520;
+case 520:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1729;
+ case 32: goto tr1729;
+ case 40: goto tr1730;
+ case 41: goto tr1731;
+ case 3082: goto tr1732;
+ case 3131: goto tr1733;
+ case 3338: goto tr1734;
+ case 3387: goto tr1734;
+ case 3594: goto tr1735;
+ case 3643: goto tr1736;
+ }
+ goto tr1219;
+st521:
+ if ( ++p == pe )
+ goto _test_eof521;
+case 521:
+ switch( (*p) ) {
+ case 80: goto st522;
+ case 112: goto st522;
+ }
+ goto tr1219;
+st522:
+ if ( ++p == pe )
+ goto _test_eof522;
+case 522:
+ switch( (*p) ) {
+ case 69: goto st523;
+ case 101: goto st523;
+ }
+ goto tr1219;
+st523:
+ if ( ++p == pe )
+ goto _test_eof523;
+case 523:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1739;
+ goto tr1219;
+tr1739:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st524;
+tr1743:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st524;
+st524:
+ if ( ++p == pe )
+ goto _test_eof524;
+case 524:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1740;
+ case 32: goto tr1740;
+ case 40: goto tr1741;
+ case 41: goto tr1742;
+ case 3082: goto tr1744;
+ case 3131: goto tr1745;
+ case 3338: goto tr1746;
+ case 3387: goto tr1746;
+ case 3594: goto tr1747;
+ case 3643: goto tr1748;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1743;
+ goto tr1219;
+st525:
+ if ( ++p == pe )
+ goto _test_eof525;
+case 525:
+ switch( (*p) ) {
+ case 82: goto st526;
+ case 114: goto st526;
+ }
+ goto tr1219;
+st526:
+ if ( ++p == pe )
+ goto _test_eof526;
+case 526:
+ switch( (*p) ) {
+ case 73: goto st527;
+ case 105: goto st527;
+ }
+ goto tr1219;
+st527:
+ if ( ++p == pe )
+ goto _test_eof527;
+case 527:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1751;
+ case 32: goto tr1751;
+ case 40: goto tr1752;
+ case 41: goto tr1753;
+ case 3082: goto tr1754;
+ case 3131: goto tr1755;
+ case 3338: goto tr1756;
+ case 3387: goto tr1756;
+ case 3594: goto tr1757;
+ case 3643: goto tr1758;
+ }
+ goto tr1219;
+st528:
+ if ( ++p == pe )
+ goto _test_eof528;
+case 528:
+ switch( (*p) ) {
+ case 79: goto st529;
+ case 111: goto st529;
+ }
+ goto tr1219;
+st529:
+ if ( ++p == pe )
+ goto _test_eof529;
+case 529:
+ switch( (*p) ) {
+ case 78: goto st530;
+ case 110: goto st530;
+ }
+ goto tr1219;
+st530:
+ if ( ++p == pe )
+ goto _test_eof530;
+case 530:
+ switch( (*p) ) {
+ case 69: goto st531;
+ case 101: goto st531;
+ }
+ goto tr1219;
+st531:
+ if ( ++p == pe )
+ goto _test_eof531;
+case 531:
+ switch( (*p) ) {
+ case 77: goto st532;
+ case 109: goto st532;
+ }
+ goto tr1219;
+st532:
+ if ( ++p == pe )
+ goto _test_eof532;
+case 532:
+ switch( (*p) ) {
+ case 68: goto st533;
+ case 100: goto st533;
+ }
+ goto tr1219;
+st533:
+ if ( ++p == pe )
+ goto _test_eof533;
+case 533:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1764;
+ case 32: goto tr1764;
+ case 40: goto tr1765;
+ case 41: goto tr1766;
+ case 3082: goto tr1767;
+ case 3131: goto tr1768;
+ case 3338: goto tr1769;
+ case 3387: goto tr1769;
+ case 3594: goto tr1770;
+ case 3643: goto tr1771;
+ }
+ goto tr1219;
+st534:
+ if ( ++p == pe )
+ goto _test_eof534;
+case 534:
+ switch( (*p) ) {
+ case 83: goto st535;
+ case 115: goto st535;
+ }
+ goto tr1219;
+st535:
+ if ( ++p == pe )
+ goto _test_eof535;
+case 535:
+ switch( (*p) ) {
+ case 68: goto st536;
+ case 100: goto st536;
+ }
+ goto tr1219;
+st536:
+ if ( ++p == pe )
+ goto _test_eof536;
+case 536:
+ switch( (*p) ) {
+ case 66: goto st537;
+ case 98: goto st537;
+ }
+ goto tr1219;
+st537:
+ if ( ++p == pe )
+ goto _test_eof537;
+case 537:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1775;
+ case 32: goto tr1775;
+ case 40: goto tr1776;
+ case 41: goto tr1777;
+ case 3082: goto tr1778;
+ case 3131: goto tr1779;
+ case 3338: goto tr1780;
+ case 3387: goto tr1780;
+ case 3594: goto tr1781;
+ case 3643: goto tr1782;
+ }
+ goto tr1219;
+st538:
+ if ( ++p == pe )
+ goto _test_eof538;
+case 538:
+ switch( (*p) ) {
+ case 76: goto st539;
+ case 108: goto st539;
+ }
+ goto tr1219;
+st539:
+ if ( ++p == pe )
+ goto _test_eof539;
+case 539:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1784;
+ case 32: goto tr1784;
+ case 40: goto tr1785;
+ case 41: goto tr1786;
+ case 3082: goto tr1787;
+ case 3131: goto tr1788;
+ case 3338: goto tr1789;
+ case 3387: goto tr1789;
+ case 3594: goto tr1790;
+ case 3643: goto tr1791;
+ }
+ goto tr1219;
+st540:
+ if ( ++p == pe )
+ goto _test_eof540;
+case 540:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st541;
+ case 32: goto st541;
+ case 40: goto tr1794;
+ case 41: goto tr1795;
+ case 2058: goto tr1796;
+ case 2107: goto tr1797;
+ }
+ goto tr1792;
+tr1794:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st541;
+tr1795:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st541;
+tr1796:
+ {
+ s->line_counter++;
+ }
+ goto st541;
+tr1827:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 541; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st541;
+st541:
+ if ( ++p == pe )
+ goto _test_eof541;
+case 541:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st541;
+ case 32: goto st541;
+ case 40: goto tr1794;
+ case 41: goto tr1795;
+ case 2058: goto tr1796;
+ case 2107: goto tr1797;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1799;
+ goto tr1798;
+tr1799:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st542;
+tr1803:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st542;
+st542:
+ if ( ++p == pe )
+ goto _test_eof542;
+case 542:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1800;
+ case 32: goto tr1800;
+ case 40: goto tr1801;
+ case 41: goto tr1802;
+ case 2058: goto tr1804;
+ case 2107: goto tr1805;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1803;
+ goto tr1798;
+tr1808:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st543;
+tr1809:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st543;
+tr1811:
+ {
+ s->line_counter++;
+ }
+ goto st543;
+tr1825:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 543; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st543;
+tr1800:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st543;
+tr1801:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st543;
+tr1802:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st543;
+tr1804:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st543;
+st543:
+ if ( ++p == pe )
+ goto _test_eof543;
+case 543:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st543;
+ case 32: goto st543;
+ case 40: goto tr1808;
+ case 41: goto tr1809;
+ case 2058: goto tr1811;
+ case 2107: goto tr1812;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1810;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1810;
+ } else
+ goto tr1810;
+ goto tr1806;
+tr1810:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st544;
+st544:
+ if ( ++p == pe )
+ goto _test_eof544;
+case 544:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1813;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr1813;
+ } else
+ goto tr1813;
+ goto tr1806;
+tr1815:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st545;
+tr1816:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st545;
+tr1817:
+ {
+ s->line_counter++;
+ }
+ goto st545;
+tr1823:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 545; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st545;
+tr1813:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st545;
+st545:
+ if ( ++p == pe )
+ goto _test_eof545;
+case 545:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st545;
+ case 32: goto st545;
+ case 40: goto tr1815;
+ case 41: goto tr1816;
+ case 3082: goto tr1817;
+ case 3131: goto tr1818;
+ case 3338: goto tr1819;
+ case 3387: goto tr1819;
+ case 3594: goto tr1820;
+ case 3643: goto tr1821;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1810;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1810;
+ } else
+ goto tr1810;
+ goto tr1806;
+tr1818:
+ {
+ s->buffer_length = 0;
+ }
+ goto st546;
+tr1822:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st546;
+st546:
+ if ( ++p == pe )
+ goto _test_eof546;
+case 546:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1823;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1822;
+ goto tr1806;
+tr1819:
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1421;
+st1421:
+ if ( ++p == pe )
+ goto _test_eof1421;
+case 1421:
+ goto st0;
+tr1820:
+ {
+ s->line_counter++;
+ }
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1422;
+st1422:
+ if ( ++p == pe )
+ goto _test_eof1422;
+case 1422:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st545;
+ case 32: goto st545;
+ case 40: goto tr1815;
+ case 41: goto tr1816;
+ case 3082: goto tr1817;
+ case 3131: goto tr1818;
+ case 3338: goto tr1819;
+ case 3387: goto tr1819;
+ case 3594: goto tr1820;
+ case 3643: goto tr1821;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1810;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1810;
+ } else
+ goto tr1810;
+ goto tr1806;
+tr1821:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1423;
+st1423:
+ if ( ++p == pe )
+ goto _test_eof1423;
+case 1423:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1823;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1822;
+ goto tr1806;
+tr1812:
+ {
+ s->buffer_length = 0;
+ }
+ goto st547;
+tr1824:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st547;
+tr1805:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st547;
+st547:
+ if ( ++p == pe )
+ goto _test_eof547;
+case 547:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1825;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1824;
+ goto tr1792;
+tr1797:
+ {
+ s->buffer_length = 0;
+ }
+ goto st548;
+tr1826:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st548;
+st548:
+ if ( ++p == pe )
+ goto _test_eof548;
+case 548:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1827;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1826;
+ goto tr1792;
+st549:
+ if ( ++p == pe )
+ goto _test_eof549;
+case 549:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st550;
+ case 32: goto st550;
+ case 40: goto tr1829;
+ case 41: goto tr1830;
+ case 2058: goto tr1831;
+ case 2107: goto tr1832;
+ }
+ goto tr1792;
+tr1829:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st550;
+tr1830:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st550;
+tr1831:
+ {
+ s->line_counter++;
+ }
+ goto st550;
+tr1867:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 550; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st550;
+st550:
+ if ( ++p == pe )
+ goto _test_eof550;
+case 550:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st550;
+ case 32: goto st550;
+ case 40: goto tr1829;
+ case 41: goto tr1830;
+ case 48: goto tr1833;
+ case 2058: goto tr1831;
+ case 2107: goto tr1832;
+ }
+ if ( 49 <= _widec && _widec <= 57 )
+ goto tr1834;
+ goto tr1798;
+tr1833:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st551;
+st551:
+ if ( ++p == pe )
+ goto _test_eof551;
+case 551:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1835;
+ case 32: goto tr1835;
+ case 40: goto tr1836;
+ case 41: goto tr1837;
+ case 1802: goto tr1839;
+ case 1851: goto tr1839;
+ case 2058: goto tr1840;
+ case 2107: goto tr1841;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1838;
+ goto tr1798;
+tr1835:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1424;
+tr1836:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1424;
+tr1837:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1424;
+tr1840:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1424;
+st1424:
+ if ( ++p == pe )
+ goto _test_eof1424;
+case 1424:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st552;
+ case 32: goto st552;
+ case 40: goto tr1843;
+ case 41: goto tr1844;
+ case 2058: goto tr1846;
+ case 2107: goto tr1847;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1845;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1845;
+ } else
+ goto tr1845;
+ goto tr1806;
+tr1843:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st552;
+tr1844:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st552;
+tr1846:
+ {
+ s->line_counter++;
+ }
+ goto st552;
+tr1860:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 552; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st552;
+tr1861:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st552;
+tr1862:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st552;
+tr1863:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st552;
+tr1864:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st552;
+st552:
+ if ( ++p == pe )
+ goto _test_eof552;
+case 552:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st552;
+ case 32: goto st552;
+ case 40: goto tr1843;
+ case 41: goto tr1844;
+ case 2058: goto tr1846;
+ case 2107: goto tr1847;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1845;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1845;
+ } else
+ goto tr1845;
+ goto tr1806;
+tr1845:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st553;
+st553:
+ if ( ++p == pe )
+ goto _test_eof553;
+case 553:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1848;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr1848;
+ } else
+ goto tr1848;
+ goto tr1806;
+tr1850:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st554;
+tr1851:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st554;
+tr1852:
+ {
+ s->line_counter++;
+ }
+ goto st554;
+tr1858:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 554; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st554;
+tr1848:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st554;
+st554:
+ if ( ++p == pe )
+ goto _test_eof554;
+case 554:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st554;
+ case 32: goto st554;
+ case 40: goto tr1850;
+ case 41: goto tr1851;
+ case 3082: goto tr1852;
+ case 3131: goto tr1853;
+ case 3338: goto tr1854;
+ case 3387: goto tr1854;
+ case 3594: goto tr1855;
+ case 3643: goto tr1856;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1845;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1845;
+ } else
+ goto tr1845;
+ goto tr1806;
+tr1853:
+ {
+ s->buffer_length = 0;
+ }
+ goto st555;
+tr1857:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st555;
+st555:
+ if ( ++p == pe )
+ goto _test_eof555;
+case 555:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1858;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1857;
+ goto tr1806;
+tr1839:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1425;
+tr1854:
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1425;
+st1425:
+ if ( ++p == pe )
+ goto _test_eof1425;
+case 1425:
+ goto tr1792;
+tr1855:
+ {
+ s->line_counter++;
+ }
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1426;
+st1426:
+ if ( ++p == pe )
+ goto _test_eof1426;
+case 1426:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st554;
+ case 32: goto st554;
+ case 40: goto tr1850;
+ case 41: goto tr1851;
+ case 3082: goto tr1852;
+ case 3131: goto tr1853;
+ case 3338: goto tr1854;
+ case 3387: goto tr1854;
+ case 3594: goto tr1855;
+ case 3643: goto tr1856;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1845;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr1845;
+ } else
+ goto tr1845;
+ goto tr1806;
+tr1856:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1427;
+st1427:
+ if ( ++p == pe )
+ goto _test_eof1427;
+case 1427:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1858;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1857;
+ goto tr1806;
+tr1847:
+ {
+ s->buffer_length = 0;
+ }
+ goto st556;
+tr1859:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st556;
+tr1865:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st556;
+st556:
+ if ( ++p == pe )
+ goto _test_eof556;
+case 556:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1860;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1859;
+ goto tr1792;
+tr1834:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st557;
+tr1838:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st557;
+st557:
+ if ( ++p == pe )
+ goto _test_eof557;
+case 557:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr1861;
+ case 32: goto tr1861;
+ case 40: goto tr1862;
+ case 41: goto tr1863;
+ case 2058: goto tr1864;
+ case 2107: goto tr1865;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr1838;
+ goto tr1798;
+tr1841:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1428;
+st1428:
+ if ( ++p == pe )
+ goto _test_eof1428;
+case 1428:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1860;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1859;
+ goto tr1792;
+tr1832:
+ {
+ s->buffer_length = 0;
+ }
+ goto st558;
+tr1866:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st558;
+st558:
+ if ( ++p == pe )
+ goto _test_eof558;
+case 558:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1867;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1866;
+ goto tr1792;
+st559:
+ if ( ++p == pe )
+ goto _test_eof559;
+case 559:
+ switch( (*p) ) {
+ case 97: goto tr1869;
+ case 101: goto tr1870;
+ case 105: goto tr1871;
+ case 107: goto tr1872;
+ case 110: goto tr1873;
+ case 112: goto tr1874;
+ }
+ goto tr1868;
+tr1869:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st560;
+st560:
+ if ( ++p == pe )
+ goto _test_eof560;
+case 560:
+ if ( (*p) == 108 )
+ goto st561;
+ goto tr1868;
+st561:
+ if ( ++p == pe )
+ goto _test_eof561;
+case 561:
+ if ( (*p) == 112 )
+ goto st562;
+ goto tr1868;
+st562:
+ if ( ++p == pe )
+ goto _test_eof562;
+case 562:
+ if ( (*p) == 110 )
+ goto st563;
+ goto tr1868;
+st563:
+ if ( ++p == pe )
+ goto _test_eof563;
+case 563:
+ switch( (*p) ) {
+ case 32: goto tr1878;
+ case 34: goto tr1878;
+ case 44: goto tr1878;
+ case 59: goto tr1878;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1878;
+ } else if ( (*p) >= 9 )
+ goto tr1878;
+ goto tr1868;
+tr1878:
+ {
+ uint16_t val = htons(1);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1881:
+ {
+ uint16_t val = htons(5);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1890:
+ {
+ uint16_t val = htons(4);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1895:
+ {
+ uint16_t val = htons(6);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1900:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1916:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+tr1920:
+ {
+ uint16_t val = htons(3);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1429;
+st1429:
+ if ( ++p == pe )
+ goto _test_eof1429;
+case 1429:
+ goto st0;
+tr1870:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st564;
+st564:
+ if ( ++p == pe )
+ goto _test_eof564;
+case 564:
+ if ( (*p) == 99 )
+ goto st565;
+ goto tr1868;
+st565:
+ if ( ++p == pe )
+ goto _test_eof565;
+case 565:
+ if ( (*p) == 104 )
+ goto st566;
+ goto tr1868;
+st566:
+ if ( ++p == pe )
+ goto _test_eof566;
+case 566:
+ switch( (*p) ) {
+ case 32: goto tr1881;
+ case 34: goto tr1881;
+ case 44: goto tr1881;
+ case 59: goto tr1881;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1881;
+ } else if ( (*p) >= 9 )
+ goto tr1881;
+ goto tr1868;
+tr1871:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st567;
+st567:
+ if ( ++p == pe )
+ goto _test_eof567;
+case 567:
+ if ( (*p) == 112 )
+ goto st568;
+ goto tr1868;
+st568:
+ if ( ++p == pe )
+ goto _test_eof568;
+case 568:
+ if ( (*p) == 118 )
+ goto st569;
+ goto tr1868;
+st569:
+ if ( ++p == pe )
+ goto _test_eof569;
+case 569:
+ switch( (*p) ) {
+ case 52: goto st570;
+ case 54: goto st575;
+ }
+ goto tr1868;
+st570:
+ if ( ++p == pe )
+ goto _test_eof570;
+case 570:
+ if ( (*p) == 104 )
+ goto st571;
+ goto tr1868;
+st571:
+ if ( ++p == pe )
+ goto _test_eof571;
+case 571:
+ if ( (*p) == 105 )
+ goto st572;
+ goto tr1868;
+st572:
+ if ( ++p == pe )
+ goto _test_eof572;
+case 572:
+ if ( (*p) == 110 )
+ goto st573;
+ goto tr1868;
+st573:
+ if ( ++p == pe )
+ goto _test_eof573;
+case 573:
+ if ( (*p) == 116 )
+ goto st574;
+ goto tr1868;
+st574:
+ if ( ++p == pe )
+ goto _test_eof574;
+case 574:
+ switch( (*p) ) {
+ case 32: goto tr1890;
+ case 34: goto tr1890;
+ case 44: goto tr1890;
+ case 59: goto tr1890;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1890;
+ } else if ( (*p) >= 9 )
+ goto tr1890;
+ goto tr1868;
+st575:
+ if ( ++p == pe )
+ goto _test_eof575;
+case 575:
+ if ( (*p) == 104 )
+ goto st576;
+ goto tr1868;
+st576:
+ if ( ++p == pe )
+ goto _test_eof576;
+case 576:
+ if ( (*p) == 105 )
+ goto st577;
+ goto tr1868;
+st577:
+ if ( ++p == pe )
+ goto _test_eof577;
+case 577:
+ if ( (*p) == 110 )
+ goto st578;
+ goto tr1868;
+st578:
+ if ( ++p == pe )
+ goto _test_eof578;
+case 578:
+ if ( (*p) == 116 )
+ goto st579;
+ goto tr1868;
+st579:
+ if ( ++p == pe )
+ goto _test_eof579;
+case 579:
+ switch( (*p) ) {
+ case 32: goto tr1895;
+ case 34: goto tr1895;
+ case 44: goto tr1895;
+ case 59: goto tr1895;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1895;
+ } else if ( (*p) >= 9 )
+ goto tr1895;
+ goto tr1868;
+tr1872:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st580;
+st580:
+ if ( ++p == pe )
+ goto _test_eof580;
+case 580:
+ if ( (*p) == 101 )
+ goto st581;
+ goto tr1868;
+st581:
+ if ( ++p == pe )
+ goto _test_eof581;
+case 581:
+ if ( (*p) == 121 )
+ goto st582;
+ goto tr1868;
+st582:
+ if ( ++p == pe )
+ goto _test_eof582;
+case 582:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1899;
+ goto tr1898;
+tr1899:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st583;
+tr1901:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st583;
+st583:
+ if ( ++p == pe )
+ goto _test_eof583;
+case 583:
+ switch( (*p) ) {
+ case 32: goto tr1900;
+ case 34: goto tr1900;
+ case 44: goto tr1900;
+ case 59: goto tr1900;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr1900;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr1901;
+ } else
+ goto tr1900;
+ goto tr1898;
+tr1873:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st584;
+st584:
+ if ( ++p == pe )
+ goto _test_eof584;
+case 584:
+ if ( (*p) == 111 )
+ goto st585;
+ goto tr1868;
+st585:
+ if ( ++p == pe )
+ goto _test_eof585;
+case 585:
+ if ( (*p) == 45 )
+ goto st586;
+ goto tr1868;
+st586:
+ if ( ++p == pe )
+ goto _test_eof586;
+case 586:
+ if ( (*p) == 100 )
+ goto st587;
+ goto tr1868;
+st587:
+ if ( ++p == pe )
+ goto _test_eof587;
+case 587:
+ if ( (*p) == 101 )
+ goto st588;
+ goto tr1868;
+st588:
+ if ( ++p == pe )
+ goto _test_eof588;
+case 588:
+ if ( (*p) == 102 )
+ goto st589;
+ goto tr1868;
+st589:
+ if ( ++p == pe )
+ goto _test_eof589;
+case 589:
+ if ( (*p) == 97 )
+ goto st590;
+ goto tr1868;
+st590:
+ if ( ++p == pe )
+ goto _test_eof590;
+case 590:
+ if ( (*p) == 117 )
+ goto st591;
+ goto tr1868;
+st591:
+ if ( ++p == pe )
+ goto _test_eof591;
+case 591:
+ if ( (*p) == 108 )
+ goto st592;
+ goto tr1868;
+st592:
+ if ( ++p == pe )
+ goto _test_eof592;
+case 592:
+ if ( (*p) == 116 )
+ goto st593;
+ goto tr1868;
+st593:
+ if ( ++p == pe )
+ goto _test_eof593;
+case 593:
+ if ( (*p) == 45 )
+ goto st594;
+ goto tr1868;
+st594:
+ if ( ++p == pe )
+ goto _test_eof594;
+case 594:
+ if ( (*p) == 97 )
+ goto st595;
+ goto tr1868;
+st595:
+ if ( ++p == pe )
+ goto _test_eof595;
+case 595:
+ if ( (*p) == 108 )
+ goto st596;
+ goto tr1868;
+st596:
+ if ( ++p == pe )
+ goto _test_eof596;
+case 596:
+ if ( (*p) == 112 )
+ goto st597;
+ goto tr1868;
+st597:
+ if ( ++p == pe )
+ goto _test_eof597;
+case 597:
+ if ( (*p) == 110 )
+ goto st598;
+ goto tr1868;
+st598:
+ if ( ++p == pe )
+ goto _test_eof598;
+case 598:
+ switch( (*p) ) {
+ case 32: goto tr1916;
+ case 34: goto tr1916;
+ case 44: goto tr1916;
+ case 59: goto tr1916;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1916;
+ } else if ( (*p) >= 9 )
+ goto tr1916;
+ goto tr1868;
+tr1874:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st599;
+st599:
+ if ( ++p == pe )
+ goto _test_eof599;
+case 599:
+ if ( (*p) == 111 )
+ goto st600;
+ goto tr1868;
+st600:
+ if ( ++p == pe )
+ goto _test_eof600;
+case 600:
+ if ( (*p) == 114 )
+ goto st601;
+ goto tr1868;
+st601:
+ if ( ++p == pe )
+ goto _test_eof601;
+case 601:
+ if ( (*p) == 116 )
+ goto st602;
+ goto tr1868;
+st602:
+ if ( ++p == pe )
+ goto _test_eof602;
+case 602:
+ switch( (*p) ) {
+ case 32: goto tr1920;
+ case 34: goto tr1920;
+ case 44: goto tr1920;
+ case 59: goto tr1920;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1920;
+ } else if ( (*p) >= 9 )
+ goto tr1920;
+ goto tr1868;
+st603:
+ if ( ++p == pe )
+ goto _test_eof603;
+case 603:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1922;
+ case 32: goto tr1922;
+ case 40: goto tr1923;
+ case 41: goto tr1924;
+ case 3082: goto tr1925;
+ case 3131: goto tr1926;
+ case 3338: goto tr1927;
+ case 3387: goto tr1927;
+ case 3594: goto tr1928;
+ case 3643: goto tr1929;
+ }
+ goto tr1921;
+tr1931:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr1932:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr1940:
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr1960:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 604; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr1922:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ goto st604;
+tr1923:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr1924:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr1925:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr1951:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr1952:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr1953:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr1954:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr1974:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr1975:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr1976:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr1977:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr1987:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr1988:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr1989:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr1991:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2013:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2014:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2015:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2018:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2036:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2037:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2038:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2041:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2051:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2052:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2053:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2056:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2077:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2078:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2079:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2081:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2101:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2102:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2103:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2104:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+tr2118:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ goto st604;
+tr2119:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st604;
+tr2120:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st604;
+tr2122:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st604;
+st604:
+ if ( ++p == pe )
+ goto _test_eof604;
+case 604:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st604;
+ case 32: goto st604;
+ case 40: goto tr1931;
+ case 41: goto tr1932;
+ case 97: goto tr1933;
+ case 101: goto tr1934;
+ case 105: goto tr1935;
+ case 107: goto tr1936;
+ case 109: goto tr1937;
+ case 110: goto tr1938;
+ case 112: goto tr1939;
+ case 3082: goto tr1940;
+ case 3131: goto tr1941;
+ case 3338: goto tr1942;
+ case 3387: goto tr1942;
+ case 3594: goto tr1943;
+ case 3643: goto tr1944;
+ }
+ goto tr1921;
+tr1933:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st605;
+st605:
+ if ( ++p == pe )
+ goto _test_eof605;
+case 605:
+ if ( (*p) == 108 )
+ goto st606;
+ goto tr1921;
+st606:
+ if ( ++p == pe )
+ goto _test_eof606;
+case 606:
+ if ( (*p) == 112 )
+ goto st607;
+ goto tr1921;
+st607:
+ if ( ++p == pe )
+ goto _test_eof607;
+case 607:
+ if ( (*p) == 110 )
+ goto st608;
+ goto tr1921;
+st608:
+ if ( ++p == pe )
+ goto _test_eof608;
+case 608:
+ if ( (*p) == 61 )
+ goto tr1948;
+ goto tr1921;
+tr1948:
+ {
+ uint16_t val = htons(1);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st609;
+st609:
+ if ( ++p == pe )
+ goto _test_eof609;
+case 609:
+ switch( (*p) ) {
+ case 32: goto tr1921;
+ case 34: goto tr1950;
+ case 59: goto tr1921;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1921;
+ } else if ( (*p) >= 9 )
+ goto tr1921;
+ goto tr1949;
+tr1949:
+ {
+ s->comma_list = true;
+ }
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 610;goto st318;} }
+ goto st610;
+st610:
+ if ( ++p == pe )
+ goto _test_eof610;
+case 610:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1951;
+ case 32: goto tr1951;
+ case 40: goto tr1952;
+ case 41: goto tr1953;
+ case 3082: goto tr1954;
+ case 3131: goto tr1955;
+ case 3338: goto tr1956;
+ case 3387: goto tr1956;
+ case 3594: goto tr1957;
+ case 3643: goto tr1958;
+ }
+ goto tr1921;
+tr1941:
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr1959:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st611;
+tr1926:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr1955:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr1978:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr1992:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2019:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2042:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2057:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2082:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2105:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+tr2123:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st611;
+st611:
+ if ( ++p == pe )
+ goto _test_eof611;
+case 611:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1960;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1959;
+ goto tr1921;
+tr1927:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr1942:
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr1956:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr1979:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr1993:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2020:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2043:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2058:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2083:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2106:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+tr2124:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1430;
+st1430:
+ if ( ++p == pe )
+ goto _test_eof1430;
+case 1430:
+ goto st0;
+tr1928:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr1943:
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr1957:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr1980:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr1994:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2021:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2044:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2059:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2084:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2107:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+tr2125:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1431;
+st1431:
+ if ( ++p == pe )
+ goto _test_eof1431;
+case 1431:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st604;
+ case 32: goto st604;
+ case 40: goto tr1931;
+ case 41: goto tr1932;
+ case 97: goto tr1933;
+ case 101: goto tr1934;
+ case 105: goto tr1935;
+ case 107: goto tr1936;
+ case 109: goto tr1937;
+ case 110: goto tr1938;
+ case 112: goto tr1939;
+ case 3082: goto tr1940;
+ case 3131: goto tr1941;
+ case 3338: goto tr1942;
+ case 3387: goto tr1942;
+ case 3594: goto tr1943;
+ case 3643: goto tr1944;
+ }
+ goto tr1921;
+tr1934:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st612;
+st612:
+ if ( ++p == pe )
+ goto _test_eof612;
+case 612:
+ if ( (*p) == 99 )
+ goto st613;
+ goto tr1921;
+st613:
+ if ( ++p == pe )
+ goto _test_eof613;
+case 613:
+ if ( (*p) == 104 )
+ goto st614;
+ goto tr1921;
+st614:
+ if ( ++p == pe )
+ goto _test_eof614;
+case 614:
+ if ( (*p) == 61 )
+ goto tr1963;
+ goto tr1921;
+tr1963:
+ {
+ uint16_t val = htons(5);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st615;
+st615:
+ if ( ++p == pe )
+ goto _test_eof615;
+case 615:
+ switch( (*p) ) {
+ case 34: goto st616;
+ case 43: goto tr1965;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1965;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1965;
+ } else
+ goto tr1965;
+ goto tr1921;
+st616:
+ if ( ++p == pe )
+ goto _test_eof616;
+case 616:
+ if ( (*p) == 43 )
+ goto tr1966;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1966;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1966;
+ } else
+ goto tr1966;
+ goto tr1921;
+tr1973:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st617;
+tr1966:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st617;
+st617:
+ if ( ++p == pe )
+ goto _test_eof617;
+case 617:
+ if ( (*p) == 43 )
+ goto tr1967;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1967;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1967;
+ } else
+ goto tr1967;
+ goto tr1921;
+tr1967:
+ {
+ *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st618;
+st618:
+ if ( ++p == pe )
+ goto _test_eof618;
+case 618:
+ switch( (*p) ) {
+ case 43: goto tr1968;
+ case 61: goto st622;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1968;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1968;
+ } else
+ goto tr1968;
+ goto tr1921;
+tr1968:
+ {
+ *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st619;
+st619:
+ if ( ++p == pe )
+ goto _test_eof619;
+case 619:
+ switch( (*p) ) {
+ case 43: goto tr1970;
+ case 61: goto st620;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1970;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1970;
+ } else
+ goto tr1970;
+ goto tr1921;
+tr1970:
+ {
+ *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)];
+ }
+ goto st620;
+st620:
+ if ( ++p == pe )
+ goto _test_eof620;
+case 620:
+ switch( (*p) ) {
+ case 34: goto tr1972;
+ case 43: goto tr1973;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1973;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1973;
+ } else
+ goto tr1973;
+ goto tr1921;
+tr1972:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st621;
+tr2009:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st621;
+tr2032:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st621;
+tr2116:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st621;
+tr2074:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ goto st621;
+tr2128:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st621;
+st621:
+ if ( ++p == pe )
+ goto _test_eof621;
+case 621:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1974;
+ case 32: goto tr1974;
+ case 40: goto tr1975;
+ case 41: goto tr1976;
+ case 3082: goto tr1977;
+ case 3131: goto tr1978;
+ case 3338: goto tr1979;
+ case 3387: goto tr1979;
+ case 3594: goto tr1980;
+ case 3643: goto tr1981;
+ }
+ goto tr1921;
+tr1929:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr1944:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr1958:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->comma_list = false;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr1981:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr1995:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2022:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2045:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2060:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2085:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2108:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+tr2126:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1432;
+st1432:
+ if ( ++p == pe )
+ goto _test_eof1432;
+case 1432:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr1960;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr1959;
+ goto tr1921;
+st622:
+ if ( ++p == pe )
+ goto _test_eof622;
+case 622:
+ if ( (*p) == 61 )
+ goto st620;
+ goto tr1921;
+tr1990:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st623;
+tr1965:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st623;
+st623:
+ if ( ++p == pe )
+ goto _test_eof623;
+case 623:
+ if ( (*p) == 43 )
+ goto tr1982;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1982;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1982;
+ } else
+ goto tr1982;
+ goto tr1921;
+tr1982:
+ {
+ *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st624;
+st624:
+ if ( ++p == pe )
+ goto _test_eof624;
+case 624:
+ switch( (*p) ) {
+ case 43: goto tr1983;
+ case 61: goto st627;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1983;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1983;
+ } else
+ goto tr1983;
+ goto tr1921;
+tr1983:
+ {
+ *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st625;
+st625:
+ if ( ++p == pe )
+ goto _test_eof625;
+case 625:
+ switch( (*p) ) {
+ case 43: goto tr1985;
+ case 61: goto st626;
+ }
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr1985;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr1985;
+ } else
+ goto tr1985;
+ goto tr1921;
+tr1985:
+ {
+ *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)];
+ }
+ goto st626;
+st626:
+ if ( ++p == pe )
+ goto _test_eof626;
+case 626:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1987;
+ case 32: goto tr1987;
+ case 40: goto tr1988;
+ case 41: goto tr1989;
+ case 43: goto tr1990;
+ case 3082: goto tr1991;
+ case 3131: goto tr1992;
+ case 3338: goto tr1993;
+ case 3387: goto tr1993;
+ case 3594: goto tr1994;
+ case 3643: goto tr1995;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr1990;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr1990;
+ } else
+ goto tr1990;
+ goto tr1921;
+st627:
+ if ( ++p == pe )
+ goto _test_eof627;
+case 627:
+ if ( (*p) == 61 )
+ goto st626;
+ goto tr1921;
+tr1935:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st628;
+st628:
+ if ( ++p == pe )
+ goto _test_eof628;
+case 628:
+ if ( (*p) == 112 )
+ goto st629;
+ goto tr1921;
+st629:
+ if ( ++p == pe )
+ goto _test_eof629;
+case 629:
+ if ( (*p) == 118 )
+ goto st630;
+ goto tr1921;
+st630:
+ if ( ++p == pe )
+ goto _test_eof630;
+case 630:
+ switch( (*p) ) {
+ case 52: goto st631;
+ case 54: goto st642;
+ }
+ goto tr1921;
+st631:
+ if ( ++p == pe )
+ goto _test_eof631;
+case 631:
+ if ( (*p) == 104 )
+ goto st632;
+ goto tr1921;
+st632:
+ if ( ++p == pe )
+ goto _test_eof632;
+case 632:
+ if ( (*p) == 105 )
+ goto st633;
+ goto tr1921;
+st633:
+ if ( ++p == pe )
+ goto _test_eof633;
+case 633:
+ if ( (*p) == 110 )
+ goto st634;
+ goto tr1921;
+st634:
+ if ( ++p == pe )
+ goto _test_eof634;
+case 634:
+ if ( (*p) == 116 )
+ goto st635;
+ goto tr1921;
+st635:
+ if ( ++p == pe )
+ goto _test_eof635;
+case 635:
+ if ( (*p) == 61 )
+ goto tr2004;
+ goto tr1921;
+tr2004:
+ {
+ uint16_t val = htons(4);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st636;
+st636:
+ if ( ++p == pe )
+ goto _test_eof636;
+case 636:
+ switch( (*p) ) {
+ case 34: goto st637;
+ case 46: goto tr2007;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2007;
+ goto tr2005;
+st637:
+ if ( ++p == pe )
+ goto _test_eof637;
+case 637:
+ if ( (*p) == 46 )
+ goto tr2008;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2008;
+ goto tr2005;
+tr2008:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st638;
+tr2011:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st638;
+tr2012:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st638;
+st638:
+ if ( ++p == pe )
+ goto _test_eof638;
+case 638:
+ switch( (*p) ) {
+ case 34: goto tr2009;
+ case 44: goto tr2010;
+ case 46: goto tr2011;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2011;
+ goto tr2005;
+tr2010:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ goto st639;
+st639:
+ if ( ++p == pe )
+ goto _test_eof639;
+case 639:
+ if ( (*p) == 46 )
+ goto tr2012;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2012;
+ goto tr2005;
+tr2007:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st640;
+tr2017:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st640;
+tr2023:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st640;
+st640:
+ if ( ++p == pe )
+ goto _test_eof640;
+case 640:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2013;
+ case 32: goto tr2013;
+ case 40: goto tr2014;
+ case 41: goto tr2015;
+ case 44: goto tr2016;
+ case 46: goto tr2017;
+ case 3082: goto tr2018;
+ case 3131: goto tr2019;
+ case 3338: goto tr2020;
+ case 3387: goto tr2020;
+ case 3594: goto tr2021;
+ case 3643: goto tr2022;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2017;
+ goto tr2005;
+tr2016:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ goto st641;
+st641:
+ if ( ++p == pe )
+ goto _test_eof641;
+case 641:
+ if ( (*p) == 46 )
+ goto tr2023;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2023;
+ goto tr2005;
+st642:
+ if ( ++p == pe )
+ goto _test_eof642;
+case 642:
+ if ( (*p) == 104 )
+ goto st643;
+ goto tr1921;
+st643:
+ if ( ++p == pe )
+ goto _test_eof643;
+case 643:
+ if ( (*p) == 105 )
+ goto st644;
+ goto tr1921;
+st644:
+ if ( ++p == pe )
+ goto _test_eof644;
+case 644:
+ if ( (*p) == 110 )
+ goto st645;
+ goto tr1921;
+st645:
+ if ( ++p == pe )
+ goto _test_eof645;
+case 645:
+ if ( (*p) == 116 )
+ goto st646;
+ goto tr1921;
+st646:
+ if ( ++p == pe )
+ goto _test_eof646;
+case 646:
+ if ( (*p) == 61 )
+ goto tr2028;
+ goto tr1921;
+tr2028:
+ {
+ uint16_t val = htons(6);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st647;
+st647:
+ if ( ++p == pe )
+ goto _test_eof647;
+case 647:
+ switch( (*p) ) {
+ case 34: goto st648;
+ case 46: goto tr2030;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2030;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2030;
+ } else
+ goto tr2030;
+ goto tr2005;
+st648:
+ if ( ++p == pe )
+ goto _test_eof648;
+case 648:
+ if ( (*p) == 46 )
+ goto tr2031;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2031;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2031;
+ } else
+ goto tr2031;
+ goto tr2005;
+tr2031:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st649;
+tr2034:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st649;
+tr2035:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st649;
+st649:
+ if ( ++p == pe )
+ goto _test_eof649;
+case 649:
+ switch( (*p) ) {
+ case 34: goto tr2032;
+ case 44: goto tr2033;
+ case 46: goto tr2034;
+ }
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2034;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2034;
+ } else
+ goto tr2034;
+ goto tr2005;
+tr2033:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ goto st650;
+st650:
+ if ( ++p == pe )
+ goto _test_eof650;
+case 650:
+ if ( (*p) == 46 )
+ goto tr2035;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2035;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2035;
+ } else
+ goto tr2035;
+ goto tr2005;
+tr2030:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st651;
+tr2040:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st651;
+tr2046:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st651;
+st651:
+ if ( ++p == pe )
+ goto _test_eof651;
+case 651:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2036;
+ case 32: goto tr2036;
+ case 40: goto tr2037;
+ case 41: goto tr2038;
+ case 44: goto tr2039;
+ case 46: goto tr2040;
+ case 3082: goto tr2041;
+ case 3131: goto tr2042;
+ case 3338: goto tr2043;
+ case 3387: goto tr2043;
+ case 3594: goto tr2044;
+ case 3643: goto tr2045;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 58 )
+ goto tr2040;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr2040;
+ } else
+ goto tr2040;
+ goto tr2005;
+tr2039:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ goto st652;
+st652:
+ if ( ++p == pe )
+ goto _test_eof652;
+case 652:
+ if ( (*p) == 46 )
+ goto tr2046;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2046;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2046;
+ } else
+ goto tr2046;
+ goto tr2005;
+tr1936:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st653;
+st653:
+ if ( ++p == pe )
+ goto _test_eof653;
+case 653:
+ if ( (*p) == 101 )
+ goto st654;
+ goto tr1921;
+st654:
+ if ( ++p == pe )
+ goto _test_eof654;
+case 654:
+ if ( (*p) == 121 )
+ goto st655;
+ goto tr1921;
+st655:
+ if ( ++p == pe )
+ goto _test_eof655;
+case 655:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2050;
+ goto tr2049;
+tr2050:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st656;
+tr2054:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st656;
+st656:
+ if ( ++p == pe )
+ goto _test_eof656;
+case 656:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2051;
+ case 32: goto tr2051;
+ case 40: goto tr2052;
+ case 41: goto tr2053;
+ case 61: goto tr2055;
+ case 3082: goto tr2056;
+ case 3131: goto tr2057;
+ case 3338: goto tr2058;
+ case 3387: goto tr2058;
+ case 3594: goto tr2059;
+ case 3643: goto tr2060;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2054;
+ goto tr2049;
+tr2055:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st657;
+st657:
+ if ( ++p == pe )
+ goto _test_eof657;
+case 657:
+ switch( (*p) ) {
+ case 32: goto tr1921;
+ case 59: goto tr1921;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr1921;
+ } else if ( (*p) >= 9 )
+ goto tr1921;
+ goto tr2061;
+tr2061:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 658;goto st318;} }
+ goto st658;
+st658:
+ if ( ++p == pe )
+ goto _test_eof658;
+case 658:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1987;
+ case 32: goto tr1987;
+ case 40: goto tr1988;
+ case 41: goto tr1989;
+ case 3082: goto tr1991;
+ case 3131: goto tr1992;
+ case 3338: goto tr1993;
+ case 3387: goto tr1993;
+ case 3594: goto tr1994;
+ case 3643: goto tr1995;
+ }
+ goto tr1921;
+tr1937:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st659;
+st659:
+ if ( ++p == pe )
+ goto _test_eof659;
+case 659:
+ if ( (*p) == 97 )
+ goto st660;
+ goto tr1921;
+st660:
+ if ( ++p == pe )
+ goto _test_eof660;
+case 660:
+ if ( (*p) == 110 )
+ goto st661;
+ goto tr1921;
+st661:
+ if ( ++p == pe )
+ goto _test_eof661;
+case 661:
+ if ( (*p) == 100 )
+ goto st662;
+ goto tr1921;
+st662:
+ if ( ++p == pe )
+ goto _test_eof662;
+case 662:
+ if ( (*p) == 97 )
+ goto st663;
+ goto tr1921;
+st663:
+ if ( ++p == pe )
+ goto _test_eof663;
+case 663:
+ if ( (*p) == 116 )
+ goto st664;
+ goto tr1921;
+st664:
+ if ( ++p == pe )
+ goto _test_eof664;
+case 664:
+ if ( (*p) == 111 )
+ goto st665;
+ goto tr1921;
+st665:
+ if ( ++p == pe )
+ goto _test_eof665;
+case 665:
+ if ( (*p) == 114 )
+ goto st666;
+ goto tr1921;
+st666:
+ if ( ++p == pe )
+ goto _test_eof666;
+case 666:
+ if ( (*p) == 121 )
+ goto st667;
+ goto tr1921;
+st667:
+ if ( ++p == pe )
+ goto _test_eof667;
+case 667:
+ if ( (*p) == 61 )
+ goto tr2070;
+ goto tr1921;
+tr2070:
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st668;
+st668:
+ if ( ++p == pe )
+ goto _test_eof668;
+case 668:
+ if ( (*p) == 34 )
+ goto st669;
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2072;
+ } else if ( (*p) >= 65 )
+ goto tr2072;
+ goto tr1921;
+st669:
+ if ( ++p == pe )
+ goto _test_eof669;
+case 669:
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2073;
+ } else if ( (*p) >= 65 )
+ goto tr2073;
+ goto tr1921;
+tr2073:
+ {
+ s->svcb.mandatory_position = rdata_tail + 2; // Skip 2-B prefix.
+ }
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 670;goto st559;} }
+ goto st670;
+tr2076:
+ { p--; {stack[top++] = 670;goto st559;} }
+ goto st670;
+st670:
+ if ( ++p == pe )
+ goto _test_eof670;
+case 670:
+ switch( (*p) ) {
+ case 34: goto tr2074;
+ case 44: goto st671;
+ }
+ goto tr1921;
+st671:
+ if ( ++p == pe )
+ goto _test_eof671;
+case 671:
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2076;
+ } else if ( (*p) >= 65 )
+ goto tr2076;
+ goto tr1921;
+tr2072:
+ {
+ s->svcb.mandatory_position = rdata_tail + 2; // Skip 2-B prefix.
+ }
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 672;goto st559;} }
+ goto st672;
+tr2086:
+ { p--; {stack[top++] = 672;goto st559;} }
+ goto st672;
+st672:
+ if ( ++p == pe )
+ goto _test_eof672;
+case 672:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2077;
+ case 32: goto tr2077;
+ case 40: goto tr2078;
+ case 41: goto tr2079;
+ case 44: goto st673;
+ case 3082: goto tr2081;
+ case 3131: goto tr2082;
+ case 3338: goto tr2083;
+ case 3387: goto tr2083;
+ case 3594: goto tr2084;
+ case 3643: goto tr2085;
+ }
+ goto tr1921;
+st673:
+ if ( ++p == pe )
+ goto _test_eof673;
+case 673:
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2086;
+ } else if ( (*p) >= 65 )
+ goto tr2086;
+ goto tr1921;
+tr1938:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st674;
+st674:
+ if ( ++p == pe )
+ goto _test_eof674;
+case 674:
+ if ( (*p) == 111 )
+ goto st675;
+ goto tr1921;
+st675:
+ if ( ++p == pe )
+ goto _test_eof675;
+case 675:
+ if ( (*p) == 45 )
+ goto st676;
+ goto tr1921;
+st676:
+ if ( ++p == pe )
+ goto _test_eof676;
+case 676:
+ if ( (*p) == 100 )
+ goto st677;
+ goto tr1921;
+st677:
+ if ( ++p == pe )
+ goto _test_eof677;
+case 677:
+ if ( (*p) == 101 )
+ goto st678;
+ goto tr1921;
+st678:
+ if ( ++p == pe )
+ goto _test_eof678;
+case 678:
+ if ( (*p) == 102 )
+ goto st679;
+ goto tr1921;
+st679:
+ if ( ++p == pe )
+ goto _test_eof679;
+case 679:
+ if ( (*p) == 97 )
+ goto st680;
+ goto tr1921;
+st680:
+ if ( ++p == pe )
+ goto _test_eof680;
+case 680:
+ if ( (*p) == 117 )
+ goto st681;
+ goto tr1921;
+st681:
+ if ( ++p == pe )
+ goto _test_eof681;
+case 681:
+ if ( (*p) == 108 )
+ goto st682;
+ goto tr1921;
+st682:
+ if ( ++p == pe )
+ goto _test_eof682;
+case 682:
+ if ( (*p) == 116 )
+ goto st683;
+ goto tr1921;
+st683:
+ if ( ++p == pe )
+ goto _test_eof683;
+case 683:
+ if ( (*p) == 45 )
+ goto st684;
+ goto tr1921;
+st684:
+ if ( ++p == pe )
+ goto _test_eof684;
+case 684:
+ if ( (*p) == 97 )
+ goto st685;
+ goto tr1921;
+st685:
+ if ( ++p == pe )
+ goto _test_eof685;
+case 685:
+ if ( (*p) == 108 )
+ goto st686;
+ goto tr1921;
+st686:
+ if ( ++p == pe )
+ goto _test_eof686;
+case 686:
+ if ( (*p) == 112 )
+ goto st687;
+ goto tr1921;
+st687:
+ if ( ++p == pe )
+ goto _test_eof687;
+case 687:
+ if ( (*p) == 110 )
+ goto st688;
+ goto tr1921;
+st688:
+ if ( ++p == pe )
+ goto _test_eof688;
+case 688:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2101;
+ case 32: goto tr2101;
+ case 40: goto tr2102;
+ case 41: goto tr2103;
+ case 3082: goto tr2104;
+ case 3131: goto tr2105;
+ case 3338: goto tr2106;
+ case 3387: goto tr2106;
+ case 3594: goto tr2107;
+ case 3643: goto tr2108;
+ }
+ goto tr1921;
+tr1939:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ goto st689;
+st689:
+ if ( ++p == pe )
+ goto _test_eof689;
+case 689:
+ if ( (*p) == 111 )
+ goto st690;
+ goto tr1921;
+st690:
+ if ( ++p == pe )
+ goto _test_eof690;
+case 690:
+ if ( (*p) == 114 )
+ goto st691;
+ goto tr1921;
+st691:
+ if ( ++p == pe )
+ goto _test_eof691;
+case 691:
+ if ( (*p) == 116 )
+ goto st692;
+ goto tr1921;
+st692:
+ if ( ++p == pe )
+ goto _test_eof692;
+case 692:
+ if ( (*p) == 61 )
+ goto tr2112;
+ goto tr1921;
+tr2112:
+ {
+ uint16_t val = htons(3);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ goto st693;
+st693:
+ if ( ++p == pe )
+ goto _test_eof693;
+case 693:
+ if ( (*p) == 34 )
+ goto st694;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2114;
+ goto tr2049;
+st694:
+ if ( ++p == pe )
+ goto _test_eof694;
+case 694:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2115;
+ goto tr2049;
+tr2117:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st695;
+tr2115:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st695;
+st695:
+ if ( ++p == pe )
+ goto _test_eof695;
+case 695:
+ if ( (*p) == 34 )
+ goto tr2116;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2117;
+ goto tr2049;
+tr2121:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st696;
+tr2114:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st696;
+st696:
+ if ( ++p == pe )
+ goto _test_eof696;
+case 696:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2118;
+ case 32: goto tr2118;
+ case 40: goto tr2119;
+ case 41: goto tr2120;
+ case 3082: goto tr2122;
+ case 3131: goto tr2123;
+ case 3338: goto tr2124;
+ case 3387: goto tr2124;
+ case 3594: goto tr2125;
+ case 3643: goto tr2126;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2121;
+ goto tr2049;
+tr1950:
+ {
+ s->comma_list = true;
+ }
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 697;goto st318;} }
+ goto st697;
+st697:
+ if ( ++p == pe )
+ goto _test_eof697;
+case 697:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr1951;
+ case 32: goto tr1951;
+ case 40: goto tr1952;
+ case 41: goto tr1953;
+ case 3082: goto tr1954;
+ case 3131: goto tr1955;
+ case 3338: goto tr1956;
+ case 3387: goto tr1956;
+ case 3594: goto tr1957;
+ case 3643: goto tr1958;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2127;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2127;
+ } else
+ goto tr2127;
+ goto tr1921;
+tr2127:
+ {
+ s->comma_list = true;
+ }
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 698;goto st318;} }
+ goto st698;
+st698:
+ if ( ++p == pe )
+ goto _test_eof698;
+case 698:
+ if ( (*p) == 34 )
+ goto tr2128;
+ goto tr1921;
+st699:
+ if ( ++p == pe )
+ goto _test_eof699;
+case 699:
+ switch( (*p) ) {
+ case 68: goto st701;
+ case 69: goto st716;
+ case 73: goto st755;
+ case 80: goto st763;
+ case 82: goto st776;
+ case 100: goto st701;
+ case 101: goto st716;
+ case 105: goto st755;
+ case 112: goto st763;
+ case 114: goto st776;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2130;
+ goto tr2129;
+tr2130:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st700;
+tr2137:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st700;
+st700:
+ if ( ++p == pe )
+ goto _test_eof700;
+case 700:
+ switch( (*p) ) {
+ case 32: goto tr2136;
+ case 59: goto tr2136;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2136;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2137;
+ } else
+ goto tr2136;
+ goto tr2129;
+tr2136:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2140:
+ {
+ *(rdata_tail++) = 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2142:
+ {
+ *(rdata_tail++) = 3;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2154:
+ {
+ *(rdata_tail++) = 6;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2164:
+ {
+ *(rdata_tail++) = 12;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2178:
+ {
+ *(rdata_tail++) = 13;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2187:
+ {
+ *(rdata_tail++) = 14;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2194:
+ {
+ *(rdata_tail++) = 15;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2197:
+ {
+ *(rdata_tail++) = 16;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2205:
+ {
+ *(rdata_tail++) = 252;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2216:
+ {
+ *(rdata_tail++) = 253;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2219:
+ {
+ *(rdata_tail++) = 254;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2226:
+ {
+ *(rdata_tail++) = 1;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2232:
+ {
+ *(rdata_tail++) = 5;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2244:
+ {
+ *(rdata_tail++) = 7;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2247:
+ {
+ *(rdata_tail++) = 8;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+tr2250:
+ {
+ *(rdata_tail++) = 10;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1433;
+st1433:
+ if ( ++p == pe )
+ goto _test_eof1433;
+case 1433:
+ goto st0;
+st701:
+ if ( ++p == pe )
+ goto _test_eof701;
+case 701:
+ switch( (*p) ) {
+ case 72: goto st702;
+ case 83: goto st703;
+ case 104: goto st702;
+ case 115: goto st703;
+ }
+ goto tr2129;
+st702:
+ if ( ++p == pe )
+ goto _test_eof702;
+case 702:
+ switch( (*p) ) {
+ case 32: goto tr2140;
+ case 59: goto tr2140;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2140;
+ } else if ( (*p) >= 9 )
+ goto tr2140;
+ goto tr2129;
+st703:
+ if ( ++p == pe )
+ goto _test_eof703;
+case 703:
+ switch( (*p) ) {
+ case 65: goto st704;
+ case 97: goto st704;
+ }
+ goto tr2129;
+st704:
+ if ( ++p == pe )
+ goto _test_eof704;
+case 704:
+ switch( (*p) ) {
+ case 32: goto tr2142;
+ case 45: goto st705;
+ case 59: goto tr2142;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2142;
+ } else if ( (*p) >= 9 )
+ goto tr2142;
+ goto tr2129;
+st705:
+ if ( ++p == pe )
+ goto _test_eof705;
+case 705:
+ switch( (*p) ) {
+ case 78: goto st706;
+ case 110: goto st706;
+ }
+ goto tr2129;
+st706:
+ if ( ++p == pe )
+ goto _test_eof706;
+case 706:
+ switch( (*p) ) {
+ case 83: goto st707;
+ case 115: goto st707;
+ }
+ goto tr2129;
+st707:
+ if ( ++p == pe )
+ goto _test_eof707;
+case 707:
+ switch( (*p) ) {
+ case 69: goto st708;
+ case 101: goto st708;
+ }
+ goto tr2129;
+st708:
+ if ( ++p == pe )
+ goto _test_eof708;
+case 708:
+ switch( (*p) ) {
+ case 67: goto st709;
+ case 99: goto st709;
+ }
+ goto tr2129;
+st709:
+ if ( ++p == pe )
+ goto _test_eof709;
+case 709:
+ if ( (*p) == 51 )
+ goto st710;
+ goto tr2129;
+st710:
+ if ( ++p == pe )
+ goto _test_eof710;
+case 710:
+ if ( (*p) == 45 )
+ goto st711;
+ goto tr2129;
+st711:
+ if ( ++p == pe )
+ goto _test_eof711;
+case 711:
+ switch( (*p) ) {
+ case 83: goto st712;
+ case 115: goto st712;
+ }
+ goto tr2129;
+st712:
+ if ( ++p == pe )
+ goto _test_eof712;
+case 712:
+ switch( (*p) ) {
+ case 72: goto st713;
+ case 104: goto st713;
+ }
+ goto tr2129;
+st713:
+ if ( ++p == pe )
+ goto _test_eof713;
+case 713:
+ switch( (*p) ) {
+ case 65: goto st714;
+ case 97: goto st714;
+ }
+ goto tr2129;
+st714:
+ if ( ++p == pe )
+ goto _test_eof714;
+case 714:
+ if ( (*p) == 49 )
+ goto st715;
+ goto tr2129;
+st715:
+ if ( ++p == pe )
+ goto _test_eof715;
+case 715:
+ switch( (*p) ) {
+ case 32: goto tr2154;
+ case 59: goto tr2154;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2154;
+ } else if ( (*p) >= 9 )
+ goto tr2154;
+ goto tr2129;
+st716:
+ if ( ++p == pe )
+ goto _test_eof716;
+case 716:
+ switch( (*p) ) {
+ case 67: goto st717;
+ case 68: goto st746;
+ case 99: goto st717;
+ case 100: goto st746;
+ }
+ goto tr2129;
+st717:
+ if ( ++p == pe )
+ goto _test_eof717;
+case 717:
+ switch( (*p) ) {
+ case 67: goto st718;
+ case 68: goto st724;
+ case 99: goto st718;
+ case 100: goto st724;
+ }
+ goto tr2129;
+st718:
+ if ( ++p == pe )
+ goto _test_eof718;
+case 718:
+ if ( (*p) == 45 )
+ goto st719;
+ goto tr2129;
+st719:
+ if ( ++p == pe )
+ goto _test_eof719;
+case 719:
+ switch( (*p) ) {
+ case 71: goto st720;
+ case 103: goto st720;
+ }
+ goto tr2129;
+st720:
+ if ( ++p == pe )
+ goto _test_eof720;
+case 720:
+ switch( (*p) ) {
+ case 79: goto st721;
+ case 111: goto st721;
+ }
+ goto tr2129;
+st721:
+ if ( ++p == pe )
+ goto _test_eof721;
+case 721:
+ switch( (*p) ) {
+ case 83: goto st722;
+ case 115: goto st722;
+ }
+ goto tr2129;
+st722:
+ if ( ++p == pe )
+ goto _test_eof722;
+case 722:
+ switch( (*p) ) {
+ case 84: goto st723;
+ case 116: goto st723;
+ }
+ goto tr2129;
+st723:
+ if ( ++p == pe )
+ goto _test_eof723;
+case 723:
+ switch( (*p) ) {
+ case 32: goto tr2164;
+ case 59: goto tr2164;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2164;
+ } else if ( (*p) >= 9 )
+ goto tr2164;
+ goto tr2129;
+st724:
+ if ( ++p == pe )
+ goto _test_eof724;
+case 724:
+ switch( (*p) ) {
+ case 83: goto st725;
+ case 115: goto st725;
+ }
+ goto tr2129;
+st725:
+ if ( ++p == pe )
+ goto _test_eof725;
+case 725:
+ switch( (*p) ) {
+ case 65: goto st726;
+ case 97: goto st726;
+ }
+ goto tr2129;
+st726:
+ if ( ++p == pe )
+ goto _test_eof726;
+case 726:
+ switch( (*p) ) {
+ case 80: goto st727;
+ case 112: goto st727;
+ }
+ goto tr2129;
+st727:
+ if ( ++p == pe )
+ goto _test_eof727;
+case 727:
+ switch( (*p) ) {
+ case 50: goto st728;
+ case 51: goto st737;
+ }
+ goto tr2129;
+st728:
+ if ( ++p == pe )
+ goto _test_eof728;
+case 728:
+ if ( (*p) == 53 )
+ goto st729;
+ goto tr2129;
+st729:
+ if ( ++p == pe )
+ goto _test_eof729;
+case 729:
+ if ( (*p) == 54 )
+ goto st730;
+ goto tr2129;
+st730:
+ if ( ++p == pe )
+ goto _test_eof730;
+case 730:
+ switch( (*p) ) {
+ case 83: goto st731;
+ case 115: goto st731;
+ }
+ goto tr2129;
+st731:
+ if ( ++p == pe )
+ goto _test_eof731;
+case 731:
+ switch( (*p) ) {
+ case 72: goto st732;
+ case 104: goto st732;
+ }
+ goto tr2129;
+st732:
+ if ( ++p == pe )
+ goto _test_eof732;
+case 732:
+ switch( (*p) ) {
+ case 65: goto st733;
+ case 97: goto st733;
+ }
+ goto tr2129;
+st733:
+ if ( ++p == pe )
+ goto _test_eof733;
+case 733:
+ if ( (*p) == 50 )
+ goto st734;
+ goto tr2129;
+st734:
+ if ( ++p == pe )
+ goto _test_eof734;
+case 734:
+ if ( (*p) == 53 )
+ goto st735;
+ goto tr2129;
+st735:
+ if ( ++p == pe )
+ goto _test_eof735;
+case 735:
+ if ( (*p) == 54 )
+ goto st736;
+ goto tr2129;
+st736:
+ if ( ++p == pe )
+ goto _test_eof736;
+case 736:
+ switch( (*p) ) {
+ case 32: goto tr2178;
+ case 59: goto tr2178;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2178;
+ } else if ( (*p) >= 9 )
+ goto tr2178;
+ goto tr2129;
+st737:
+ if ( ++p == pe )
+ goto _test_eof737;
+case 737:
+ if ( (*p) == 56 )
+ goto st738;
+ goto tr2129;
+st738:
+ if ( ++p == pe )
+ goto _test_eof738;
+case 738:
+ if ( (*p) == 52 )
+ goto st739;
+ goto tr2129;
+st739:
+ if ( ++p == pe )
+ goto _test_eof739;
+case 739:
+ switch( (*p) ) {
+ case 83: goto st740;
+ case 115: goto st740;
+ }
+ goto tr2129;
+st740:
+ if ( ++p == pe )
+ goto _test_eof740;
+case 740:
+ switch( (*p) ) {
+ case 72: goto st741;
+ case 104: goto st741;
+ }
+ goto tr2129;
+st741:
+ if ( ++p == pe )
+ goto _test_eof741;
+case 741:
+ switch( (*p) ) {
+ case 65: goto st742;
+ case 97: goto st742;
+ }
+ goto tr2129;
+st742:
+ if ( ++p == pe )
+ goto _test_eof742;
+case 742:
+ if ( (*p) == 51 )
+ goto st743;
+ goto tr2129;
+st743:
+ if ( ++p == pe )
+ goto _test_eof743;
+case 743:
+ if ( (*p) == 56 )
+ goto st744;
+ goto tr2129;
+st744:
+ if ( ++p == pe )
+ goto _test_eof744;
+case 744:
+ if ( (*p) == 52 )
+ goto st745;
+ goto tr2129;
+st745:
+ if ( ++p == pe )
+ goto _test_eof745;
+case 745:
+ switch( (*p) ) {
+ case 32: goto tr2187;
+ case 59: goto tr2187;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2187;
+ } else if ( (*p) >= 9 )
+ goto tr2187;
+ goto tr2129;
+st746:
+ if ( ++p == pe )
+ goto _test_eof746;
+case 746:
+ switch( (*p) ) {
+ case 50: goto st747;
+ case 52: goto st752;
+ }
+ goto tr2129;
+st747:
+ if ( ++p == pe )
+ goto _test_eof747;
+case 747:
+ if ( (*p) == 53 )
+ goto st748;
+ goto tr2129;
+st748:
+ if ( ++p == pe )
+ goto _test_eof748;
+case 748:
+ if ( (*p) == 53 )
+ goto st749;
+ goto tr2129;
+st749:
+ if ( ++p == pe )
+ goto _test_eof749;
+case 749:
+ if ( (*p) == 49 )
+ goto st750;
+ goto tr2129;
+st750:
+ if ( ++p == pe )
+ goto _test_eof750;
+case 750:
+ if ( (*p) == 57 )
+ goto st751;
+ goto tr2129;
+st751:
+ if ( ++p == pe )
+ goto _test_eof751;
+case 751:
+ switch( (*p) ) {
+ case 32: goto tr2194;
+ case 59: goto tr2194;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2194;
+ } else if ( (*p) >= 9 )
+ goto tr2194;
+ goto tr2129;
+st752:
+ if ( ++p == pe )
+ goto _test_eof752;
+case 752:
+ if ( (*p) == 52 )
+ goto st753;
+ goto tr2129;
+st753:
+ if ( ++p == pe )
+ goto _test_eof753;
+case 753:
+ if ( (*p) == 56 )
+ goto st754;
+ goto tr2129;
+st754:
+ if ( ++p == pe )
+ goto _test_eof754;
+case 754:
+ switch( (*p) ) {
+ case 32: goto tr2197;
+ case 59: goto tr2197;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2197;
+ } else if ( (*p) >= 9 )
+ goto tr2197;
+ goto tr2129;
+st755:
+ if ( ++p == pe )
+ goto _test_eof755;
+case 755:
+ switch( (*p) ) {
+ case 78: goto st756;
+ case 110: goto st756;
+ }
+ goto tr2129;
+st756:
+ if ( ++p == pe )
+ goto _test_eof756;
+case 756:
+ switch( (*p) ) {
+ case 68: goto st757;
+ case 100: goto st757;
+ }
+ goto tr2129;
+st757:
+ if ( ++p == pe )
+ goto _test_eof757;
+case 757:
+ switch( (*p) ) {
+ case 73: goto st758;
+ case 105: goto st758;
+ }
+ goto tr2129;
+st758:
+ if ( ++p == pe )
+ goto _test_eof758;
+case 758:
+ switch( (*p) ) {
+ case 82: goto st759;
+ case 114: goto st759;
+ }
+ goto tr2129;
+st759:
+ if ( ++p == pe )
+ goto _test_eof759;
+case 759:
+ switch( (*p) ) {
+ case 69: goto st760;
+ case 101: goto st760;
+ }
+ goto tr2129;
+st760:
+ if ( ++p == pe )
+ goto _test_eof760;
+case 760:
+ switch( (*p) ) {
+ case 67: goto st761;
+ case 99: goto st761;
+ }
+ goto tr2129;
+st761:
+ if ( ++p == pe )
+ goto _test_eof761;
+case 761:
+ switch( (*p) ) {
+ case 84: goto st762;
+ case 116: goto st762;
+ }
+ goto tr2129;
+st762:
+ if ( ++p == pe )
+ goto _test_eof762;
+case 762:
+ switch( (*p) ) {
+ case 32: goto tr2205;
+ case 59: goto tr2205;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2205;
+ } else if ( (*p) >= 9 )
+ goto tr2205;
+ goto tr2129;
+st763:
+ if ( ++p == pe )
+ goto _test_eof763;
+case 763:
+ switch( (*p) ) {
+ case 82: goto st764;
+ case 114: goto st764;
+ }
+ goto tr2129;
+st764:
+ if ( ++p == pe )
+ goto _test_eof764;
+case 764:
+ switch( (*p) ) {
+ case 73: goto st765;
+ case 105: goto st765;
+ }
+ goto tr2129;
+st765:
+ if ( ++p == pe )
+ goto _test_eof765;
+case 765:
+ switch( (*p) ) {
+ case 86: goto st766;
+ case 118: goto st766;
+ }
+ goto tr2129;
+st766:
+ if ( ++p == pe )
+ goto _test_eof766;
+case 766:
+ switch( (*p) ) {
+ case 65: goto st767;
+ case 97: goto st767;
+ }
+ goto tr2129;
+st767:
+ if ( ++p == pe )
+ goto _test_eof767;
+case 767:
+ switch( (*p) ) {
+ case 84: goto st768;
+ case 116: goto st768;
+ }
+ goto tr2129;
+st768:
+ if ( ++p == pe )
+ goto _test_eof768;
+case 768:
+ switch( (*p) ) {
+ case 69: goto st769;
+ case 101: goto st769;
+ }
+ goto tr2129;
+st769:
+ if ( ++p == pe )
+ goto _test_eof769;
+case 769:
+ switch( (*p) ) {
+ case 68: goto st770;
+ case 79: goto st773;
+ case 100: goto st770;
+ case 111: goto st773;
+ }
+ goto tr2129;
+st770:
+ if ( ++p == pe )
+ goto _test_eof770;
+case 770:
+ switch( (*p) ) {
+ case 78: goto st771;
+ case 110: goto st771;
+ }
+ goto tr2129;
+st771:
+ if ( ++p == pe )
+ goto _test_eof771;
+case 771:
+ switch( (*p) ) {
+ case 83: goto st772;
+ case 115: goto st772;
+ }
+ goto tr2129;
+st772:
+ if ( ++p == pe )
+ goto _test_eof772;
+case 772:
+ switch( (*p) ) {
+ case 32: goto tr2216;
+ case 59: goto tr2216;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2216;
+ } else if ( (*p) >= 9 )
+ goto tr2216;
+ goto tr2129;
+st773:
+ if ( ++p == pe )
+ goto _test_eof773;
+case 773:
+ switch( (*p) ) {
+ case 73: goto st774;
+ case 105: goto st774;
+ }
+ goto tr2129;
+st774:
+ if ( ++p == pe )
+ goto _test_eof774;
+case 774:
+ switch( (*p) ) {
+ case 68: goto st775;
+ case 100: goto st775;
+ }
+ goto tr2129;
+st775:
+ if ( ++p == pe )
+ goto _test_eof775;
+case 775:
+ switch( (*p) ) {
+ case 32: goto tr2219;
+ case 59: goto tr2219;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2219;
+ } else if ( (*p) >= 9 )
+ goto tr2219;
+ goto tr2129;
+st776:
+ if ( ++p == pe )
+ goto _test_eof776;
+case 776:
+ switch( (*p) ) {
+ case 83: goto st777;
+ case 115: goto st777;
+ }
+ goto tr2129;
+st777:
+ if ( ++p == pe )
+ goto _test_eof777;
+case 777:
+ switch( (*p) ) {
+ case 65: goto st778;
+ case 97: goto st778;
+ }
+ goto tr2129;
+st778:
+ if ( ++p == pe )
+ goto _test_eof778;
+case 778:
+ switch( (*p) ) {
+ case 77: goto st779;
+ case 83: goto st782;
+ case 109: goto st779;
+ case 115: goto st782;
+ }
+ goto tr2129;
+st779:
+ if ( ++p == pe )
+ goto _test_eof779;
+case 779:
+ switch( (*p) ) {
+ case 68: goto st780;
+ case 100: goto st780;
+ }
+ goto tr2129;
+st780:
+ if ( ++p == pe )
+ goto _test_eof780;
+case 780:
+ if ( (*p) == 53 )
+ goto st781;
+ goto tr2129;
+st781:
+ if ( ++p == pe )
+ goto _test_eof781;
+case 781:
+ switch( (*p) ) {
+ case 32: goto tr2226;
+ case 59: goto tr2226;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2226;
+ } else if ( (*p) >= 9 )
+ goto tr2226;
+ goto tr2129;
+st782:
+ if ( ++p == pe )
+ goto _test_eof782;
+case 782:
+ switch( (*p) ) {
+ case 72: goto st783;
+ case 104: goto st783;
+ }
+ goto tr2129;
+st783:
+ if ( ++p == pe )
+ goto _test_eof783;
+case 783:
+ switch( (*p) ) {
+ case 65: goto st784;
+ case 97: goto st784;
+ }
+ goto tr2129;
+st784:
+ if ( ++p == pe )
+ goto _test_eof784;
+case 784:
+ switch( (*p) ) {
+ case 49: goto st785;
+ case 50: goto st797;
+ case 53: goto st800;
+ }
+ goto tr2129;
+st785:
+ if ( ++p == pe )
+ goto _test_eof785;
+case 785:
+ switch( (*p) ) {
+ case 32: goto tr2232;
+ case 45: goto st786;
+ case 59: goto tr2232;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2232;
+ } else if ( (*p) >= 9 )
+ goto tr2232;
+ goto tr2129;
+st786:
+ if ( ++p == pe )
+ goto _test_eof786;
+case 786:
+ switch( (*p) ) {
+ case 78: goto st787;
+ case 110: goto st787;
+ }
+ goto tr2129;
+st787:
+ if ( ++p == pe )
+ goto _test_eof787;
+case 787:
+ switch( (*p) ) {
+ case 83: goto st788;
+ case 115: goto st788;
+ }
+ goto tr2129;
+st788:
+ if ( ++p == pe )
+ goto _test_eof788;
+case 788:
+ switch( (*p) ) {
+ case 69: goto st789;
+ case 101: goto st789;
+ }
+ goto tr2129;
+st789:
+ if ( ++p == pe )
+ goto _test_eof789;
+case 789:
+ switch( (*p) ) {
+ case 67: goto st790;
+ case 99: goto st790;
+ }
+ goto tr2129;
+st790:
+ if ( ++p == pe )
+ goto _test_eof790;
+case 790:
+ if ( (*p) == 51 )
+ goto st791;
+ goto tr2129;
+st791:
+ if ( ++p == pe )
+ goto _test_eof791;
+case 791:
+ if ( (*p) == 45 )
+ goto st792;
+ goto tr2129;
+st792:
+ if ( ++p == pe )
+ goto _test_eof792;
+case 792:
+ switch( (*p) ) {
+ case 83: goto st793;
+ case 115: goto st793;
+ }
+ goto tr2129;
+st793:
+ if ( ++p == pe )
+ goto _test_eof793;
+case 793:
+ switch( (*p) ) {
+ case 72: goto st794;
+ case 104: goto st794;
+ }
+ goto tr2129;
+st794:
+ if ( ++p == pe )
+ goto _test_eof794;
+case 794:
+ switch( (*p) ) {
+ case 65: goto st795;
+ case 97: goto st795;
+ }
+ goto tr2129;
+st795:
+ if ( ++p == pe )
+ goto _test_eof795;
+case 795:
+ if ( (*p) == 49 )
+ goto st796;
+ goto tr2129;
+st796:
+ if ( ++p == pe )
+ goto _test_eof796;
+case 796:
+ switch( (*p) ) {
+ case 32: goto tr2244;
+ case 59: goto tr2244;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2244;
+ } else if ( (*p) >= 9 )
+ goto tr2244;
+ goto tr2129;
+st797:
+ if ( ++p == pe )
+ goto _test_eof797;
+case 797:
+ if ( (*p) == 53 )
+ goto st798;
+ goto tr2129;
+st798:
+ if ( ++p == pe )
+ goto _test_eof798;
+case 798:
+ if ( (*p) == 54 )
+ goto st799;
+ goto tr2129;
+st799:
+ if ( ++p == pe )
+ goto _test_eof799;
+case 799:
+ switch( (*p) ) {
+ case 32: goto tr2247;
+ case 59: goto tr2247;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2247;
+ } else if ( (*p) >= 9 )
+ goto tr2247;
+ goto tr2129;
+st800:
+ if ( ++p == pe )
+ goto _test_eof800;
+case 800:
+ if ( (*p) == 49 )
+ goto st801;
+ goto tr2129;
+st801:
+ if ( ++p == pe )
+ goto _test_eof801;
+case 801:
+ if ( (*p) == 50 )
+ goto st802;
+ goto tr2129;
+st802:
+ if ( ++p == pe )
+ goto _test_eof802;
+case 802:
+ switch( (*p) ) {
+ case 32: goto tr2250;
+ case 59: goto tr2250;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2250;
+ } else if ( (*p) >= 9 )
+ goto tr2250;
+ goto tr2129;
+st803:
+ if ( ++p == pe )
+ goto _test_eof803;
+case 803:
+ switch( (*p) ) {
+ case 65: goto st805;
+ case 73: goto st811;
+ case 79: goto st828;
+ case 80: goto st831;
+ case 83: goto st837;
+ case 85: goto st841;
+ case 97: goto st805;
+ case 105: goto st811;
+ case 111: goto st828;
+ case 112: goto st831;
+ case 115: goto st837;
+ case 117: goto st841;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2252;
+ goto tr2251;
+tr2252:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st804;
+tr2260:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st804;
+st804:
+ if ( ++p == pe )
+ goto _test_eof804;
+case 804:
+ switch( (*p) ) {
+ case 32: goto tr2259;
+ case 59: goto tr2259;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2259;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2260;
+ } else
+ goto tr2259;
+ goto tr2251;
+tr2296:
+ {
+ uint16_t val = htons(1);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2286:
+ {
+ uint16_t val = htons(5);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2282:
+ {
+ uint16_t val = htons(4);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2279:
+ {
+ uint16_t val = htons(6);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2259:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2300:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2293:
+ {
+ uint16_t val = htons(3);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2266:
+ {
+ uint16_t val = htons(7);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2275:
+ {
+ uint16_t val = htons(8);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2289:
+ {
+ uint16_t val = htons(254);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+tr2303:
+ {
+ uint16_t val = htons(253);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1434;
+st1434:
+ if ( ++p == pe )
+ goto _test_eof1434;
+case 1434:
+ goto st0;
+st805:
+ if ( ++p == pe )
+ goto _test_eof805;
+case 805:
+ switch( (*p) ) {
+ case 67: goto st806;
+ case 99: goto st806;
+ }
+ goto tr2251;
+st806:
+ if ( ++p == pe )
+ goto _test_eof806;
+case 806:
+ switch( (*p) ) {
+ case 80: goto st807;
+ case 112: goto st807;
+ }
+ goto tr2251;
+st807:
+ if ( ++p == pe )
+ goto _test_eof807;
+case 807:
+ switch( (*p) ) {
+ case 75: goto st808;
+ case 107: goto st808;
+ }
+ goto tr2251;
+st808:
+ if ( ++p == pe )
+ goto _test_eof808;
+case 808:
+ switch( (*p) ) {
+ case 73: goto st809;
+ case 105: goto st809;
+ }
+ goto tr2251;
+st809:
+ if ( ++p == pe )
+ goto _test_eof809;
+case 809:
+ switch( (*p) ) {
+ case 88: goto st810;
+ case 120: goto st810;
+ }
+ goto tr2251;
+st810:
+ if ( ++p == pe )
+ goto _test_eof810;
+case 810:
+ switch( (*p) ) {
+ case 32: goto tr2266;
+ case 59: goto tr2266;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2266;
+ } else if ( (*p) >= 9 )
+ goto tr2266;
+ goto tr2251;
+st811:
+ if ( ++p == pe )
+ goto _test_eof811;
+case 811:
+ switch( (*p) ) {
+ case 65: goto st812;
+ case 80: goto st818;
+ case 83: goto st824;
+ case 97: goto st812;
+ case 112: goto st818;
+ case 115: goto st824;
+ }
+ goto tr2251;
+st812:
+ if ( ++p == pe )
+ goto _test_eof812;
+case 812:
+ switch( (*p) ) {
+ case 67: goto st813;
+ case 99: goto st813;
+ }
+ goto tr2251;
+st813:
+ if ( ++p == pe )
+ goto _test_eof813;
+case 813:
+ switch( (*p) ) {
+ case 80: goto st814;
+ case 112: goto st814;
+ }
+ goto tr2251;
+st814:
+ if ( ++p == pe )
+ goto _test_eof814;
+case 814:
+ switch( (*p) ) {
+ case 75: goto st815;
+ case 107: goto st815;
+ }
+ goto tr2251;
+st815:
+ if ( ++p == pe )
+ goto _test_eof815;
+case 815:
+ switch( (*p) ) {
+ case 73: goto st816;
+ case 105: goto st816;
+ }
+ goto tr2251;
+st816:
+ if ( ++p == pe )
+ goto _test_eof816;
+case 816:
+ switch( (*p) ) {
+ case 88: goto st817;
+ case 120: goto st817;
+ }
+ goto tr2251;
+st817:
+ if ( ++p == pe )
+ goto _test_eof817;
+case 817:
+ switch( (*p) ) {
+ case 32: goto tr2275;
+ case 59: goto tr2275;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2275;
+ } else if ( (*p) >= 9 )
+ goto tr2275;
+ goto tr2251;
+st818:
+ if ( ++p == pe )
+ goto _test_eof818;
+case 818:
+ switch( (*p) ) {
+ case 71: goto st819;
+ case 75: goto st821;
+ case 103: goto st819;
+ case 107: goto st821;
+ }
+ goto tr2251;
+st819:
+ if ( ++p == pe )
+ goto _test_eof819;
+case 819:
+ switch( (*p) ) {
+ case 80: goto st820;
+ case 112: goto st820;
+ }
+ goto tr2251;
+st820:
+ if ( ++p == pe )
+ goto _test_eof820;
+case 820:
+ switch( (*p) ) {
+ case 32: goto tr2279;
+ case 59: goto tr2279;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2279;
+ } else if ( (*p) >= 9 )
+ goto tr2279;
+ goto tr2251;
+st821:
+ if ( ++p == pe )
+ goto _test_eof821;
+case 821:
+ switch( (*p) ) {
+ case 73: goto st822;
+ case 105: goto st822;
+ }
+ goto tr2251;
+st822:
+ if ( ++p == pe )
+ goto _test_eof822;
+case 822:
+ switch( (*p) ) {
+ case 88: goto st823;
+ case 120: goto st823;
+ }
+ goto tr2251;
+st823:
+ if ( ++p == pe )
+ goto _test_eof823;
+case 823:
+ switch( (*p) ) {
+ case 32: goto tr2282;
+ case 59: goto tr2282;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2282;
+ } else if ( (*p) >= 9 )
+ goto tr2282;
+ goto tr2251;
+st824:
+ if ( ++p == pe )
+ goto _test_eof824;
+case 824:
+ switch( (*p) ) {
+ case 80: goto st825;
+ case 112: goto st825;
+ }
+ goto tr2251;
+st825:
+ if ( ++p == pe )
+ goto _test_eof825;
+case 825:
+ switch( (*p) ) {
+ case 75: goto st826;
+ case 107: goto st826;
+ }
+ goto tr2251;
+st826:
+ if ( ++p == pe )
+ goto _test_eof826;
+case 826:
+ switch( (*p) ) {
+ case 73: goto st827;
+ case 105: goto st827;
+ }
+ goto tr2251;
+st827:
+ if ( ++p == pe )
+ goto _test_eof827;
+case 827:
+ switch( (*p) ) {
+ case 32: goto tr2286;
+ case 59: goto tr2286;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2286;
+ } else if ( (*p) >= 9 )
+ goto tr2286;
+ goto tr2251;
+st828:
+ if ( ++p == pe )
+ goto _test_eof828;
+case 828:
+ switch( (*p) ) {
+ case 73: goto st829;
+ case 105: goto st829;
+ }
+ goto tr2251;
+st829:
+ if ( ++p == pe )
+ goto _test_eof829;
+case 829:
+ switch( (*p) ) {
+ case 68: goto st830;
+ case 100: goto st830;
+ }
+ goto tr2251;
+st830:
+ if ( ++p == pe )
+ goto _test_eof830;
+case 830:
+ switch( (*p) ) {
+ case 32: goto tr2289;
+ case 59: goto tr2289;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2289;
+ } else if ( (*p) >= 9 )
+ goto tr2289;
+ goto tr2251;
+st831:
+ if ( ++p == pe )
+ goto _test_eof831;
+case 831:
+ switch( (*p) ) {
+ case 71: goto st832;
+ case 75: goto st834;
+ case 103: goto st832;
+ case 107: goto st834;
+ }
+ goto tr2251;
+st832:
+ if ( ++p == pe )
+ goto _test_eof832;
+case 832:
+ switch( (*p) ) {
+ case 80: goto st833;
+ case 112: goto st833;
+ }
+ goto tr2251;
+st833:
+ if ( ++p == pe )
+ goto _test_eof833;
+case 833:
+ switch( (*p) ) {
+ case 32: goto tr2293;
+ case 59: goto tr2293;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2293;
+ } else if ( (*p) >= 9 )
+ goto tr2293;
+ goto tr2251;
+st834:
+ if ( ++p == pe )
+ goto _test_eof834;
+case 834:
+ switch( (*p) ) {
+ case 73: goto st835;
+ case 105: goto st835;
+ }
+ goto tr2251;
+st835:
+ if ( ++p == pe )
+ goto _test_eof835;
+case 835:
+ switch( (*p) ) {
+ case 88: goto st836;
+ case 120: goto st836;
+ }
+ goto tr2251;
+st836:
+ if ( ++p == pe )
+ goto _test_eof836;
+case 836:
+ switch( (*p) ) {
+ case 32: goto tr2296;
+ case 59: goto tr2296;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2296;
+ } else if ( (*p) >= 9 )
+ goto tr2296;
+ goto tr2251;
+st837:
+ if ( ++p == pe )
+ goto _test_eof837;
+case 837:
+ switch( (*p) ) {
+ case 80: goto st838;
+ case 112: goto st838;
+ }
+ goto tr2251;
+st838:
+ if ( ++p == pe )
+ goto _test_eof838;
+case 838:
+ switch( (*p) ) {
+ case 75: goto st839;
+ case 107: goto st839;
+ }
+ goto tr2251;
+st839:
+ if ( ++p == pe )
+ goto _test_eof839;
+case 839:
+ switch( (*p) ) {
+ case 73: goto st840;
+ case 105: goto st840;
+ }
+ goto tr2251;
+st840:
+ if ( ++p == pe )
+ goto _test_eof840;
+case 840:
+ switch( (*p) ) {
+ case 32: goto tr2300;
+ case 59: goto tr2300;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2300;
+ } else if ( (*p) >= 9 )
+ goto tr2300;
+ goto tr2251;
+st841:
+ if ( ++p == pe )
+ goto _test_eof841;
+case 841:
+ switch( (*p) ) {
+ case 82: goto st842;
+ case 114: goto st842;
+ }
+ goto tr2251;
+st842:
+ if ( ++p == pe )
+ goto _test_eof842;
+case 842:
+ switch( (*p) ) {
+ case 73: goto st843;
+ case 105: goto st843;
+ }
+ goto tr2251;
+st843:
+ if ( ++p == pe )
+ goto _test_eof843;
+case 843:
+ switch( (*p) ) {
+ case 32: goto tr2303;
+ case 59: goto tr2303;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2303;
+ } else if ( (*p) >= 9 )
+ goto tr2303;
+ goto tr2251;
+st844:
+ if ( ++p == pe )
+ goto _test_eof844;
+case 844:
+ if ( (*p) == 46 )
+ goto tr2305;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2305;
+ goto tr2304;
+tr2307:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st845;
+tr2305:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st845;
+st845:
+ if ( ++p == pe )
+ goto _test_eof845;
+case 845:
+ switch( (*p) ) {
+ case 32: goto tr2306;
+ case 46: goto tr2307;
+ case 59: goto tr2306;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2306;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2307;
+ } else
+ goto tr2306;
+ goto tr2304;
+tr2306:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1435;
+st1435:
+ if ( ++p == pe )
+ goto _test_eof1435;
+case 1435:
+ goto st0;
+st846:
+ if ( ++p == pe )
+ goto _test_eof846;
+case 846:
+ switch( (*p) ) {
+ case 42: goto tr2308;
+ case 92: goto tr2308;
+ case 95: goto tr2308;
+ }
+ if ( (*p) < 64 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr2308;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2308;
+ } else
+ goto tr2308;
+ goto tr75;
+tr2308:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 847;goto st309;} }
+ goto st847;
+st847:
+ if ( ++p == pe )
+ goto _test_eof847;
+case 847:
+ switch( (*p) ) {
+ case 32: goto tr2309;
+ case 59: goto tr2309;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2309;
+ } else if ( (*p) >= 9 )
+ goto tr2309;
+ goto tr75;
+tr2309:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1436;
+st1436:
+ if ( ++p == pe )
+ goto _test_eof1436;
+case 1436:
+ goto st0;
+st848:
+ if ( ++p == pe )
+ goto _test_eof848;
+case 848:
+ switch( (*p) ) {
+ case 42: goto tr2310;
+ case 92: goto tr2310;
+ case 95: goto tr2310;
+ }
+ if ( (*p) < 64 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr2310;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2310;
+ } else
+ goto tr2310;
+ goto tr75;
+tr2310:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 849;goto st309;} }
+ goto st849;
+st849:
+ if ( ++p == pe )
+ goto _test_eof849;
+case 849:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2311;
+ case 32: goto tr2311;
+ case 40: goto tr2312;
+ case 41: goto tr2313;
+ case 2058: goto tr2314;
+ case 2107: goto tr2315;
+ }
+ goto tr75;
+tr2317:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st850;
+tr2318:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st850;
+tr2320:
+ {
+ s->line_counter++;
+ }
+ goto st850;
+tr2464:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 850; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st850;
+tr2311:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st850;
+tr2312:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st850;
+tr2313:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st850;
+tr2314:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st850;
+st850:
+ if ( ++p == pe )
+ goto _test_eof850;
+case 850:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st850;
+ case 32: goto st850;
+ case 40: goto tr2317;
+ case 41: goto tr2318;
+ case 42: goto tr2319;
+ case 92: goto tr2319;
+ case 95: goto tr2319;
+ case 2058: goto tr2320;
+ case 2107: goto tr2321;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr2319;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2319;
+ } else
+ goto tr2319;
+ goto tr75;
+tr2319:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 851;goto st309;} }
+ goto st851;
+st851:
+ if ( ++p == pe )
+ goto _test_eof851;
+case 851:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2322;
+ case 32: goto tr2322;
+ case 40: goto tr2323;
+ case 41: goto tr2324;
+ case 2058: goto tr2325;
+ case 2107: goto tr2326;
+ }
+ goto tr75;
+tr2329:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st852;
+tr2330:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st852;
+tr2332:
+ {
+ s->line_counter++;
+ }
+ goto st852;
+tr2462:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 852; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st852;
+tr2322:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st852;
+tr2323:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st852;
+tr2324:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st852;
+tr2325:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st852;
+st852:
+ if ( ++p == pe )
+ goto _test_eof852;
+case 852:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st852;
+ case 32: goto st852;
+ case 40: goto tr2329;
+ case 41: goto tr2330;
+ case 2058: goto tr2332;
+ case 2107: goto tr2333;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2331;
+ goto tr2327;
+tr2331:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st853;
+tr2337:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st853;
+st853:
+ if ( ++p == pe )
+ goto _test_eof853;
+case 853:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2334;
+ case 32: goto tr2334;
+ case 40: goto tr2335;
+ case 41: goto tr2336;
+ case 2058: goto tr2338;
+ case 2107: goto tr2339;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2337;
+ goto tr2327;
+tr2341:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st854;
+tr2342:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st854;
+tr2344:
+ {
+ s->line_counter++;
+ }
+ goto st854;
+tr2460:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 854; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st854;
+tr2334:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st854;
+tr2335:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st854;
+tr2336:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st854;
+tr2338:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st854;
+st854:
+ if ( ++p == pe )
+ goto _test_eof854;
+case 854:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st854;
+ case 32: goto st854;
+ case 40: goto tr2341;
+ case 41: goto tr2342;
+ case 2058: goto tr2344;
+ case 2107: goto tr2345;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2343;
+ goto tr2327;
+tr2343:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st855;
+tr2350:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st855;
+st855:
+ if ( ++p == pe )
+ goto _test_eof855;
+case 855:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2347;
+ case 32: goto tr2347;
+ case 40: goto tr2348;
+ case 41: goto tr2349;
+ case 68: goto tr2351;
+ case 72: goto tr2352;
+ case 77: goto tr2353;
+ case 83: goto st874;
+ case 87: goto tr2355;
+ case 100: goto tr2351;
+ case 104: goto tr2352;
+ case 109: goto tr2353;
+ case 115: goto st874;
+ case 119: goto tr2355;
+ case 2058: goto tr2356;
+ case 2107: goto tr2357;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2350;
+ goto tr2346;
+tr2359:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st856;
+tr2360:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st856;
+tr2362:
+ {
+ s->line_counter++;
+ }
+ goto st856;
+tr2445:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 856; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st856;
+tr2347:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st856;
+tr2348:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st856;
+tr2349:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st856;
+tr2356:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st856;
+tr2453:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st856;
+tr2454:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st856;
+tr2455:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st856;
+tr2457:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st856;
+st856:
+ if ( ++p == pe )
+ goto _test_eof856;
+case 856:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st856;
+ case 32: goto st856;
+ case 40: goto tr2359;
+ case 41: goto tr2360;
+ case 2058: goto tr2362;
+ case 2107: goto tr2363;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2361;
+ goto tr2327;
+tr2361:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st857;
+tr2367:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st857;
+st857:
+ if ( ++p == pe )
+ goto _test_eof857;
+case 857:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2364;
+ case 32: goto tr2364;
+ case 40: goto tr2365;
+ case 41: goto tr2366;
+ case 68: goto tr2368;
+ case 72: goto tr2369;
+ case 77: goto tr2370;
+ case 83: goto st870;
+ case 87: goto tr2372;
+ case 100: goto tr2368;
+ case 104: goto tr2369;
+ case 109: goto tr2370;
+ case 115: goto st870;
+ case 119: goto tr2372;
+ case 2058: goto tr2373;
+ case 2107: goto tr2374;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2367;
+ goto tr2346;
+tr2376:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st858;
+tr2377:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st858;
+tr2379:
+ {
+ s->line_counter++;
+ }
+ goto st858;
+tr2430:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 858; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st858;
+tr2364:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st858;
+tr2365:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st858;
+tr2366:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st858;
+tr2373:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st858;
+tr2438:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st858;
+tr2439:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st858;
+tr2440:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st858;
+tr2442:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st858;
+st858:
+ if ( ++p == pe )
+ goto _test_eof858;
+case 858:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st858;
+ case 32: goto st858;
+ case 40: goto tr2376;
+ case 41: goto tr2377;
+ case 2058: goto tr2379;
+ case 2107: goto tr2380;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2378;
+ goto tr2327;
+tr2378:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st859;
+tr2384:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st859;
+st859:
+ if ( ++p == pe )
+ goto _test_eof859;
+case 859:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2381;
+ case 32: goto tr2381;
+ case 40: goto tr2382;
+ case 41: goto tr2383;
+ case 68: goto tr2385;
+ case 72: goto tr2386;
+ case 77: goto tr2387;
+ case 83: goto st866;
+ case 87: goto tr2389;
+ case 100: goto tr2385;
+ case 104: goto tr2386;
+ case 109: goto tr2387;
+ case 115: goto st866;
+ case 119: goto tr2389;
+ case 2058: goto tr2390;
+ case 2107: goto tr2391;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2384;
+ goto tr2346;
+tr2393:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st860;
+tr2394:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st860;
+tr2396:
+ {
+ s->line_counter++;
+ }
+ goto st860;
+tr2415:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 860; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st860;
+tr2381:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st860;
+tr2382:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st860;
+tr2383:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st860;
+tr2390:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st860;
+tr2423:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st860;
+tr2424:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st860;
+tr2425:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st860;
+tr2427:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st860;
+st860:
+ if ( ++p == pe )
+ goto _test_eof860;
+case 860:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st860;
+ case 32: goto st860;
+ case 40: goto tr2393;
+ case 41: goto tr2394;
+ case 2058: goto tr2396;
+ case 2107: goto tr2397;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2395;
+ goto tr2327;
+tr2395:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st861;
+tr2399:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st861;
+st861:
+ if ( ++p == pe )
+ goto _test_eof861;
+case 861:
+ switch( (*p) ) {
+ case 32: goto tr2398;
+ case 59: goto tr2398;
+ case 68: goto tr2400;
+ case 72: goto tr2401;
+ case 77: goto tr2402;
+ case 83: goto st862;
+ case 87: goto tr2404;
+ case 100: goto tr2400;
+ case 104: goto tr2401;
+ case 109: goto tr2402;
+ case 115: goto st862;
+ case 119: goto tr2404;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2398;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2399;
+ } else
+ goto tr2398;
+ goto tr2346;
+tr2398:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1437;
+tr2412:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1437;
+st1437:
+ if ( ++p == pe )
+ goto _test_eof1437;
+case 1437:
+ goto st0;
+tr2400:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st862;
+tr2401:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st862;
+tr2402:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st862;
+tr2404:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st862;
+st862:
+ if ( ++p == pe )
+ goto _test_eof862;
+case 862:
+ switch( (*p) ) {
+ case 32: goto tr2398;
+ case 59: goto tr2398;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2398;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2405;
+ } else
+ goto tr2398;
+ goto tr2346;
+tr2406:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st863;
+tr2405:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st863;
+tr2413:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st863;
+st863:
+ if ( ++p == pe )
+ goto _test_eof863;
+case 863:
+ switch( (*p) ) {
+ case 68: goto tr2407;
+ case 72: goto tr2408;
+ case 77: goto tr2409;
+ case 83: goto st864;
+ case 87: goto tr2411;
+ case 100: goto tr2407;
+ case 104: goto tr2408;
+ case 109: goto tr2409;
+ case 115: goto st864;
+ case 119: goto tr2411;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2406;
+ goto tr2346;
+tr2407:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st864;
+tr2408:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st864;
+tr2409:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st864;
+tr2411:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st864;
+st864:
+ if ( ++p == pe )
+ goto _test_eof864;
+case 864:
+ switch( (*p) ) {
+ case 32: goto tr2412;
+ case 59: goto tr2412;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr2412;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2413;
+ } else
+ goto tr2412;
+ goto tr2346;
+tr2397:
+ {
+ s->buffer_length = 0;
+ }
+ goto st865;
+tr2414:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st865;
+tr2391:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st865;
+tr2428:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st865;
+st865:
+ if ( ++p == pe )
+ goto _test_eof865;
+case 865:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2415;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2414;
+ goto tr75;
+tr2385:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st866;
+tr2386:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st866;
+tr2387:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st866;
+tr2389:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st866;
+st866:
+ if ( ++p == pe )
+ goto _test_eof866;
+case 866:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2381;
+ case 32: goto tr2381;
+ case 40: goto tr2382;
+ case 41: goto tr2383;
+ case 2058: goto tr2390;
+ case 2107: goto tr2391;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2416;
+ goto tr2346;
+tr2417:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st867;
+tr2416:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st867;
+tr2426:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st867;
+st867:
+ if ( ++p == pe )
+ goto _test_eof867;
+case 867:
+ switch( (*p) ) {
+ case 68: goto tr2418;
+ case 72: goto tr2419;
+ case 77: goto tr2420;
+ case 83: goto st868;
+ case 87: goto tr2422;
+ case 100: goto tr2418;
+ case 104: goto tr2419;
+ case 109: goto tr2420;
+ case 115: goto st868;
+ case 119: goto tr2422;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2417;
+ goto tr2346;
+tr2418:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st868;
+tr2419:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st868;
+tr2420:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st868;
+tr2422:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st868;
+st868:
+ if ( ++p == pe )
+ goto _test_eof868;
+case 868:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2423;
+ case 32: goto tr2423;
+ case 40: goto tr2424;
+ case 41: goto tr2425;
+ case 2058: goto tr2427;
+ case 2107: goto tr2428;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2426;
+ goto tr2346;
+tr2380:
+ {
+ s->buffer_length = 0;
+ }
+ goto st869;
+tr2429:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st869;
+tr2374:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st869;
+tr2443:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st869;
+st869:
+ if ( ++p == pe )
+ goto _test_eof869;
+case 869:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2430;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2429;
+ goto tr75;
+tr2368:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st870;
+tr2369:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st870;
+tr2370:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st870;
+tr2372:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st870;
+st870:
+ if ( ++p == pe )
+ goto _test_eof870;
+case 870:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2364;
+ case 32: goto tr2364;
+ case 40: goto tr2365;
+ case 41: goto tr2366;
+ case 2058: goto tr2373;
+ case 2107: goto tr2374;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2431;
+ goto tr2346;
+tr2432:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st871;
+tr2431:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st871;
+tr2441:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st871;
+st871:
+ if ( ++p == pe )
+ goto _test_eof871;
+case 871:
+ switch( (*p) ) {
+ case 68: goto tr2433;
+ case 72: goto tr2434;
+ case 77: goto tr2435;
+ case 83: goto st872;
+ case 87: goto tr2437;
+ case 100: goto tr2433;
+ case 104: goto tr2434;
+ case 109: goto tr2435;
+ case 115: goto st872;
+ case 119: goto tr2437;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2432;
+ goto tr2346;
+tr2433:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st872;
+tr2434:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st872;
+tr2435:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st872;
+tr2437:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st872;
+st872:
+ if ( ++p == pe )
+ goto _test_eof872;
+case 872:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2438;
+ case 32: goto tr2438;
+ case 40: goto tr2439;
+ case 41: goto tr2440;
+ case 2058: goto tr2442;
+ case 2107: goto tr2443;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2441;
+ goto tr2346;
+tr2363:
+ {
+ s->buffer_length = 0;
+ }
+ goto st873;
+tr2444:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st873;
+tr2357:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st873;
+tr2458:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st873;
+st873:
+ if ( ++p == pe )
+ goto _test_eof873;
+case 873:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2445;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2444;
+ goto tr75;
+tr2351:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st874;
+tr2352:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st874;
+tr2353:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st874;
+tr2355:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st874;
+st874:
+ if ( ++p == pe )
+ goto _test_eof874;
+case 874:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2347;
+ case 32: goto tr2347;
+ case 40: goto tr2348;
+ case 41: goto tr2349;
+ case 2058: goto tr2356;
+ case 2107: goto tr2357;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2446;
+ goto tr2346;
+tr2447:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st875;
+tr2446:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st875;
+tr2456:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st875;
+st875:
+ if ( ++p == pe )
+ goto _test_eof875;
+case 875:
+ switch( (*p) ) {
+ case 68: goto tr2448;
+ case 72: goto tr2449;
+ case 77: goto tr2450;
+ case 83: goto st876;
+ case 87: goto tr2452;
+ case 100: goto tr2448;
+ case 104: goto tr2449;
+ case 109: goto tr2450;
+ case 115: goto st876;
+ case 119: goto tr2452;
+ }
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2447;
+ goto tr2346;
+tr2448:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st876;
+tr2449:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st876;
+tr2450:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st876;
+tr2452:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st876;
+st876:
+ if ( ++p == pe )
+ goto _test_eof876;
+case 876:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2453;
+ case 32: goto tr2453;
+ case 40: goto tr2454;
+ case 41: goto tr2455;
+ case 2058: goto tr2457;
+ case 2107: goto tr2458;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2456;
+ goto tr2346;
+tr2345:
+ {
+ s->buffer_length = 0;
+ }
+ goto st877;
+tr2459:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st877;
+tr2339:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st877;
+st877:
+ if ( ++p == pe )
+ goto _test_eof877;
+case 877:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2460;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2459;
+ goto tr75;
+tr2333:
+ {
+ s->buffer_length = 0;
+ }
+ goto st878;
+tr2461:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st878;
+tr2326:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st878;
+st878:
+ if ( ++p == pe )
+ goto _test_eof878;
+case 878:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2462;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2461;
+ goto tr75;
+tr2321:
+ {
+ s->buffer_length = 0;
+ }
+ goto st879;
+tr2463:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st879;
+tr2315:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st879;
+st879:
+ if ( ++p == pe )
+ goto _test_eof879;
+case 879:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2464;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2463;
+ goto tr75;
+st880:
+ if ( ++p == pe )
+ goto _test_eof880;
+case 880:
+ switch( (*p) ) {
+ case 32: goto tr75;
+ case 59: goto tr75;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr75;
+ } else if ( (*p) >= 9 )
+ goto tr75;
+ goto tr2465;
+tr2465:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 881;goto st318;} }
+ goto st881;
+st881:
+ if ( ++p == pe )
+ goto _test_eof881;
+case 881:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2466;
+ case 32: goto tr2466;
+ case 40: goto tr2467;
+ case 41: goto tr2468;
+ case 2058: goto tr2469;
+ case 2107: goto tr2470;
+ }
+ goto tr75;
+tr2473:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st882;
+tr2474:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st882;
+tr2475:
+ {
+ s->line_counter++;
+ }
+ goto st882;
+tr2479:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 882; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st882;
+tr2466:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st882;
+tr2467:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st882;
+tr2468:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st882;
+tr2469:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st882;
+st882:
+ if ( ++p == pe )
+ goto _test_eof882;
+case 882:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st882;
+ case 32: goto st882;
+ case 40: goto tr2473;
+ case 41: goto tr2474;
+ case 2058: goto tr2475;
+ case 2107: goto tr2476;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2471;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2471;
+ } else
+ goto tr2471;
+ goto tr75;
+tr2471:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 883;goto st318;} }
+ goto st883;
+st883:
+ if ( ++p == pe )
+ goto _test_eof883;
+case 883:
+ switch( (*p) ) {
+ case 32: goto tr2477;
+ case 59: goto tr2477;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2477;
+ } else if ( (*p) >= 9 )
+ goto tr2477;
+ goto tr75;
+tr2477:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1438;
+st1438:
+ if ( ++p == pe )
+ goto _test_eof1438;
+case 1438:
+ goto st0;
+tr2476:
+ {
+ s->buffer_length = 0;
+ }
+ goto st884;
+tr2478:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st884;
+tr2470:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st884;
+st884:
+ if ( ++p == pe )
+ goto _test_eof884;
+case 884:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2479;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2478;
+ goto tr75;
+st885:
+ if ( ++p == pe )
+ goto _test_eof885;
+case 885:
+ switch( (*p) ) {
+ case 42: goto tr2480;
+ case 92: goto tr2480;
+ case 95: goto tr2480;
+ }
+ if ( (*p) < 64 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr2480;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2480;
+ } else
+ goto tr2480;
+ goto tr75;
+tr2480:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 886;goto st309;} }
+ goto st886;
+st886:
+ if ( ++p == pe )
+ goto _test_eof886;
+case 886:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2481;
+ case 32: goto tr2481;
+ case 40: goto tr2482;
+ case 41: goto tr2483;
+ case 2058: goto tr2484;
+ case 2107: goto tr2485;
+ }
+ goto tr75;
+tr2487:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st887;
+tr2488:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st887;
+tr2490:
+ {
+ s->line_counter++;
+ }
+ goto st887;
+tr2494:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 887; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st887;
+tr2481:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st887;
+tr2482:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st887;
+tr2483:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st887;
+tr2484:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st887;
+st887:
+ if ( ++p == pe )
+ goto _test_eof887;
+case 887:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st887;
+ case 32: goto st887;
+ case 40: goto tr2487;
+ case 41: goto tr2488;
+ case 42: goto tr2489;
+ case 92: goto tr2489;
+ case 95: goto tr2489;
+ case 2058: goto tr2490;
+ case 2107: goto tr2491;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr2489;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2489;
+ } else
+ goto tr2489;
+ goto tr75;
+tr2489:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 888;goto st309;} }
+ goto st888;
+st888:
+ if ( ++p == pe )
+ goto _test_eof888;
+case 888:
+ switch( (*p) ) {
+ case 32: goto tr2492;
+ case 59: goto tr2492;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2492;
+ } else if ( (*p) >= 9 )
+ goto tr2492;
+ goto tr75;
+tr2492:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1439;
+st1439:
+ if ( ++p == pe )
+ goto _test_eof1439;
+case 1439:
+ goto st0;
+tr2491:
+ {
+ s->buffer_length = 0;
+ }
+ goto st889;
+tr2493:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st889;
+tr2485:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st889;
+st889:
+ if ( ++p == pe )
+ goto _test_eof889;
+case 889:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2494;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2493;
+ goto tr75;
+st890:
+ if ( ++p == pe )
+ goto _test_eof890;
+case 890:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2495;
+ goto tr2327;
+tr2495:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st891;
+tr2499:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st891;
+st891:
+ if ( ++p == pe )
+ goto _test_eof891;
+case 891:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2496;
+ case 32: goto tr2496;
+ case 40: goto tr2497;
+ case 41: goto tr2498;
+ case 2058: goto tr2500;
+ case 2107: goto tr2501;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2499;
+ goto tr2327;
+tr2503:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st892;
+tr2504:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st892;
+tr2506:
+ {
+ s->line_counter++;
+ }
+ goto st892;
+tr2510:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 892; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st892;
+tr2496:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st892;
+tr2497:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st892;
+tr2498:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st892;
+tr2500:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st892;
+st892:
+ if ( ++p == pe )
+ goto _test_eof892;
+case 892:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st892;
+ case 32: goto st892;
+ case 40: goto tr2503;
+ case 41: goto tr2504;
+ case 42: goto tr2505;
+ case 92: goto tr2505;
+ case 95: goto tr2505;
+ case 2058: goto tr2506;
+ case 2107: goto tr2507;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr2505;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2505;
+ } else
+ goto tr2505;
+ goto tr75;
+tr2505:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 893;goto st309;} }
+ goto st893;
+st893:
+ if ( ++p == pe )
+ goto _test_eof893;
+case 893:
+ switch( (*p) ) {
+ case 32: goto tr2508;
+ case 59: goto tr2508;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2508;
+ } else if ( (*p) >= 9 )
+ goto tr2508;
+ goto tr75;
+tr2508:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1440;
+st1440:
+ if ( ++p == pe )
+ goto _test_eof1440;
+case 1440:
+ goto st0;
+tr2507:
+ {
+ s->buffer_length = 0;
+ }
+ goto st894;
+tr2509:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st894;
+tr2501:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st894;
+st894:
+ if ( ++p == pe )
+ goto _test_eof894;
+case 894:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2510;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2509;
+ goto tr75;
+st895:
+ if ( ++p == pe )
+ goto _test_eof895;
+case 895:
+ switch( (*p) ) {
+ case 32: goto tr2512;
+ case 59: goto tr2512;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2512;
+ } else if ( (*p) >= 9 )
+ goto tr2512;
+ goto tr2511;
+tr2521:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 896;goto st318;} }
+ goto st896;
+tr2511:
+ {
+ s->long_string = true;
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 896;goto st318;} }
+ goto st896;
+st896:
+ if ( ++p == pe )
+ goto _test_eof896;
+case 896:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2513;
+ case 32: goto tr2513;
+ case 40: goto tr2514;
+ case 41: goto tr2515;
+ case 3082: goto tr2516;
+ case 3131: goto tr2517;
+ case 3338: goto tr2518;
+ case 3387: goto tr2518;
+ case 3594: goto tr2519;
+ case 3643: goto tr2520;
+ }
+ goto tr2512;
+tr2523:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st897;
+tr2524:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st897;
+tr2525:
+ {
+ s->line_counter++;
+ }
+ goto st897;
+tr2531:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 897; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st897;
+tr2513:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st897;
+tr2514:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st897;
+tr2515:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st897;
+tr2516:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st897;
+st897:
+ if ( ++p == pe )
+ goto _test_eof897;
+case 897:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st897;
+ case 32: goto st897;
+ case 40: goto tr2523;
+ case 41: goto tr2524;
+ case 3082: goto tr2525;
+ case 3131: goto tr2526;
+ case 3338: goto tr2527;
+ case 3387: goto tr2527;
+ case 3594: goto tr2528;
+ case 3643: goto tr2529;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2521;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2521;
+ } else
+ goto tr2521;
+ goto tr2512;
+tr2526:
+ {
+ s->buffer_length = 0;
+ }
+ goto st898;
+tr2530:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st898;
+tr2517:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st898;
+st898:
+ if ( ++p == pe )
+ goto _test_eof898;
+case 898:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2531;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2530;
+ goto tr2512;
+tr2518:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1441;
+tr2527:
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1441;
+st1441:
+ if ( ++p == pe )
+ goto _test_eof1441;
+case 1441:
+ goto st0;
+tr2519:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1442;
+tr2528:
+ {
+ s->line_counter++;
+ }
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1442;
+st1442:
+ if ( ++p == pe )
+ goto _test_eof1442;
+case 1442:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st897;
+ case 32: goto st897;
+ case 40: goto tr2523;
+ case 41: goto tr2524;
+ case 3082: goto tr2525;
+ case 3131: goto tr2526;
+ case 3338: goto tr2527;
+ case 3387: goto tr2527;
+ case 3594: goto tr2528;
+ case 3643: goto tr2529;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2521;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2521;
+ } else
+ goto tr2521;
+ goto tr2512;
+tr2520:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1443;
+tr2529:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ s->long_string = false;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1443;
+st1443:
+ if ( ++p == pe )
+ goto _test_eof1443;
+case 1443:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2531;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2530;
+ goto tr2512;
+st899:
+ if ( ++p == pe )
+ goto _test_eof899;
+case 899:
+ if ( (*p) == 46 )
+ goto tr2532;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2532;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2532;
+ } else
+ goto tr2532;
+ goto tr2304;
+tr2534:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st900;
+tr2532:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st900;
+st900:
+ if ( ++p == pe )
+ goto _test_eof900;
+case 900:
+ switch( (*p) ) {
+ case 32: goto tr2533;
+ case 46: goto tr2534;
+ case 59: goto tr2533;
+ }
+ if ( (*p) < 48 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2533;
+ } else if ( (*p) >= 9 )
+ goto tr2533;
+ } else if ( (*p) > 58 ) {
+ if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2534;
+ } else if ( (*p) >= 65 )
+ goto tr2534;
+ } else
+ goto tr2534;
+ goto tr2304;
+tr2533:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1444;
+st1444:
+ if ( ++p == pe )
+ goto _test_eof1444;
+case 1444:
+ goto st0;
+st901:
+ if ( ++p == pe )
+ goto _test_eof901;
+case 901:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2536;
+ goto tr2535;
+tr2540:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st902;
+tr2536:
+ {
+ memset(&(s->loc), 0, sizeof(s->loc));
+ // Defaults.
+ s->loc.siz = 100;
+ s->loc.vp = 1000;
+ s->loc.hp = 1000000;
+ s->loc.lat_sign = 1;
+ s->loc.long_sign = 1;
+ s->loc.alt_sign = 1;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st902;
+st902:
+ if ( ++p == pe )
+ goto _test_eof902;
+case 902:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2537;
+ case 32: goto tr2537;
+ case 40: goto tr2538;
+ case 41: goto tr2539;
+ case 2058: goto tr2541;
+ case 2107: goto tr2542;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2540;
+ goto tr2535;
+tr2544:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st903;
+tr2545:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st903;
+tr2549:
+ {
+ s->line_counter++;
+ }
+ goto st903;
+tr2792:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 903; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st903;
+tr2537:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st903;
+tr2538:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st903;
+tr2539:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st903;
+tr2541:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st903;
+st903:
+ if ( ++p == pe )
+ goto _test_eof903;
+case 903:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st903;
+ case 32: goto st903;
+ case 40: goto tr2544;
+ case 41: goto tr2545;
+ case 78: goto st908;
+ case 83: goto st950;
+ case 2058: goto tr2549;
+ case 2107: goto tr2550;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2546;
+ goto tr2535;
+tr2546:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st904;
+tr2554:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st904;
+st904:
+ if ( ++p == pe )
+ goto _test_eof904;
+case 904:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2551;
+ case 32: goto tr2551;
+ case 40: goto tr2552;
+ case 41: goto tr2553;
+ case 2058: goto tr2555;
+ case 2107: goto tr2556;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2554;
+ goto tr2535;
+tr2558:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st905;
+tr2559:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st905;
+tr2561:
+ {
+ s->line_counter++;
+ }
+ goto st905;
+tr2790:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 905; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st905;
+tr2551:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st905;
+tr2552:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st905;
+tr2553:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st905;
+tr2555:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st905;
+st905:
+ if ( ++p == pe )
+ goto _test_eof905;
+case 905:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st905;
+ case 32: goto st905;
+ case 40: goto tr2558;
+ case 41: goto tr2559;
+ case 78: goto st908;
+ case 83: goto st950;
+ case 2058: goto tr2561;
+ case 2107: goto tr2562;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2560;
+ goto tr2535;
+tr2567:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st906;
+tr2560:
+ {
+ s->decimals = 3;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st906;
+st906:
+ if ( ++p == pe )
+ goto _test_eof906;
+case 906:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2563;
+ case 32: goto tr2563;
+ case 40: goto tr2564;
+ case 41: goto tr2565;
+ case 46: goto st952;
+ case 2058: goto tr2568;
+ case 2107: goto tr2569;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2567;
+ goto tr2535;
+tr2571:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st907;
+tr2572:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st907;
+tr2573:
+ {
+ s->line_counter++;
+ }
+ goto st907;
+tr2781:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 907; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st907;
+tr2563:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st907;
+tr2564:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st907;
+tr2565:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st907;
+tr2568:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st907;
+tr2782:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st907;
+tr2783:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st907;
+tr2784:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st907;
+tr2786:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st907;
+st907:
+ if ( ++p == pe )
+ goto _test_eof907;
+case 907:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st907;
+ case 32: goto st907;
+ case 40: goto tr2571;
+ case 41: goto tr2572;
+ case 78: goto st908;
+ case 83: goto st950;
+ case 2058: goto tr2573;
+ case 2107: goto tr2574;
+ }
+ goto tr2535;
+st908:
+ if ( ++p == pe )
+ goto _test_eof908;
+case 908:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st909;
+ case 32: goto st909;
+ case 40: goto tr2576;
+ case 41: goto tr2577;
+ case 2058: goto tr2578;
+ case 2107: goto tr2579;
+ }
+ goto tr2535;
+tr2576:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st909;
+tr2577:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st909;
+tr2578:
+ {
+ s->line_counter++;
+ }
+ goto st909;
+tr2774:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 909; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st909;
+tr2775:
+ {
+ s->loc.lat_sign = -1;
+ }
+ goto st909;
+tr2776:
+ {
+ s->loc.lat_sign = -1;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st909;
+tr2777:
+ {
+ s->loc.lat_sign = -1;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st909;
+tr2778:
+ {
+ s->loc.lat_sign = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st909;
+st909:
+ if ( ++p == pe )
+ goto _test_eof909;
+case 909:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st909;
+ case 32: goto st909;
+ case 40: goto tr2576;
+ case 41: goto tr2577;
+ case 2058: goto tr2578;
+ case 2107: goto tr2579;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2580;
+ goto tr2535;
+tr2580:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st910;
+tr2584:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st910;
+st910:
+ if ( ++p == pe )
+ goto _test_eof910;
+case 910:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2581;
+ case 32: goto tr2581;
+ case 40: goto tr2582;
+ case 41: goto tr2583;
+ case 2058: goto tr2585;
+ case 2107: goto tr2586;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2584;
+ goto tr2535;
+tr2588:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st911;
+tr2589:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st911;
+tr2593:
+ {
+ s->line_counter++;
+ }
+ goto st911;
+tr2772:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 911; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st911;
+tr2581:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st911;
+tr2582:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st911;
+tr2583:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st911;
+tr2585:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st911;
+st911:
+ if ( ++p == pe )
+ goto _test_eof911;
+case 911:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st911;
+ case 32: goto st911;
+ case 40: goto tr2588;
+ case 41: goto tr2589;
+ case 69: goto st916;
+ case 87: goto st943;
+ case 2058: goto tr2593;
+ case 2107: goto tr2594;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2590;
+ goto tr2535;
+tr2590:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st912;
+tr2598:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st912;
+st912:
+ if ( ++p == pe )
+ goto _test_eof912;
+case 912:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2595;
+ case 32: goto tr2595;
+ case 40: goto tr2596;
+ case 41: goto tr2597;
+ case 2058: goto tr2599;
+ case 2107: goto tr2600;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2598;
+ goto tr2535;
+tr2602:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st913;
+tr2603:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st913;
+tr2605:
+ {
+ s->line_counter++;
+ }
+ goto st913;
+tr2770:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 913; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st913;
+tr2595:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st913;
+tr2596:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st913;
+tr2597:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st913;
+tr2599:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st913;
+st913:
+ if ( ++p == pe )
+ goto _test_eof913;
+case 913:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st913;
+ case 32: goto st913;
+ case 40: goto tr2602;
+ case 41: goto tr2603;
+ case 69: goto st916;
+ case 87: goto st943;
+ case 2058: goto tr2605;
+ case 2107: goto tr2606;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2604;
+ goto tr2535;
+tr2611:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st914;
+tr2604:
+ {
+ s->decimals = 3;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st914;
+st914:
+ if ( ++p == pe )
+ goto _test_eof914;
+case 914:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2607;
+ case 32: goto tr2607;
+ case 40: goto tr2608;
+ case 41: goto tr2609;
+ case 46: goto st945;
+ case 2058: goto tr2612;
+ case 2107: goto tr2613;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2611;
+ goto tr2535;
+tr2615:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st915;
+tr2616:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st915;
+tr2617:
+ {
+ s->line_counter++;
+ }
+ goto st915;
+tr2761:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 915; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st915;
+tr2607:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st915;
+tr2608:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st915;
+tr2609:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st915;
+tr2612:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st915;
+tr2762:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st915;
+tr2763:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st915;
+tr2764:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st915;
+tr2766:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st915;
+st915:
+ if ( ++p == pe )
+ goto _test_eof915;
+case 915:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st915;
+ case 32: goto st915;
+ case 40: goto tr2615;
+ case 41: goto tr2616;
+ case 69: goto st916;
+ case 87: goto st943;
+ case 2058: goto tr2617;
+ case 2107: goto tr2618;
+ }
+ goto tr2535;
+st916:
+ if ( ++p == pe )
+ goto _test_eof916;
+case 916:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st917;
+ case 32: goto st917;
+ case 40: goto tr2620;
+ case 41: goto tr2621;
+ case 2058: goto tr2622;
+ case 2107: goto tr2623;
+ }
+ goto tr2535;
+tr2620:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st917;
+tr2621:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st917;
+tr2622:
+ {
+ s->line_counter++;
+ }
+ goto st917;
+tr2754:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 917; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st917;
+tr2755:
+ {
+ s->loc.long_sign = -1;
+ }
+ goto st917;
+tr2756:
+ {
+ s->loc.long_sign = -1;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st917;
+tr2757:
+ {
+ s->loc.long_sign = -1;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st917;
+tr2758:
+ {
+ s->loc.long_sign = -1;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st917;
+st917:
+ if ( ++p == pe )
+ goto _test_eof917;
+case 917:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st917;
+ case 32: goto st917;
+ case 40: goto tr2620;
+ case 41: goto tr2621;
+ case 45: goto st918;
+ case 2058: goto tr2622;
+ case 2107: goto tr2623;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2625;
+ goto tr2535;
+st918:
+ if ( ++p == pe )
+ goto _test_eof918;
+case 918:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2626;
+ goto tr2535;
+tr2631:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st919;
+tr2625:
+ {
+ s->decimals = 2;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st919;
+tr2626:
+ {
+ s->loc.alt_sign = -1;
+ }
+ {
+ s->decimals = 2;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st919;
+st919:
+ if ( ++p == pe )
+ goto _test_eof919;
+case 919:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2627;
+ case 32: goto tr2627;
+ case 40: goto tr2628;
+ case 41: goto tr2629;
+ case 46: goto st939;
+ case 109: goto tr2632;
+ case 3082: goto tr2633;
+ case 3131: goto tr2634;
+ case 3338: goto tr2635;
+ case 3387: goto tr2635;
+ case 3594: goto tr2636;
+ case 3643: goto tr2637;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2631;
+ goto tr2535;
+tr2639:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st920;
+tr2640:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st920;
+tr2642:
+ {
+ s->line_counter++;
+ }
+ goto st920;
+tr2741:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 920; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st920;
+tr2627:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st920;
+tr2628:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st920;
+tr2629:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st920;
+tr2633:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st920;
+tr2742:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st920;
+tr2743:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st920;
+tr2744:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st920;
+tr2747:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st920;
+st920:
+ if ( ++p == pe )
+ goto _test_eof920;
+case 920:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st920;
+ case 32: goto st920;
+ case 40: goto tr2639;
+ case 41: goto tr2640;
+ case 3082: goto tr2642;
+ case 3131: goto tr2643;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2645;
+ case 3643: goto tr2646;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2641;
+ goto tr2535;
+tr2651:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st921;
+tr2641:
+ {
+ s->decimals = 2;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st921;
+st921:
+ if ( ++p == pe )
+ goto _test_eof921;
+case 921:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2647;
+ case 32: goto tr2647;
+ case 40: goto tr2648;
+ case 41: goto tr2649;
+ case 46: goto st935;
+ case 109: goto tr2652;
+ case 3082: goto tr2653;
+ case 3131: goto tr2654;
+ case 3338: goto tr2655;
+ case 3387: goto tr2655;
+ case 3594: goto tr2656;
+ case 3643: goto tr2657;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2651;
+ goto tr2535;
+tr2659:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st922;
+tr2660:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st922;
+tr2662:
+ {
+ s->line_counter++;
+ }
+ goto st922;
+tr2728:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 922; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st922;
+tr2647:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st922;
+tr2648:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st922;
+tr2649:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st922;
+tr2653:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st922;
+tr2729:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st922;
+tr2730:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st922;
+tr2731:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st922;
+tr2734:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st922;
+st922:
+ if ( ++p == pe )
+ goto _test_eof922;
+case 922:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st922;
+ case 32: goto st922;
+ case 40: goto tr2659;
+ case 41: goto tr2660;
+ case 3082: goto tr2662;
+ case 3131: goto tr2663;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2664;
+ case 3643: goto tr2665;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2661;
+ goto tr2535;
+tr2670:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st923;
+tr2661:
+ {
+ s->decimals = 2;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st923;
+st923:
+ if ( ++p == pe )
+ goto _test_eof923;
+case 923:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2666;
+ case 32: goto tr2666;
+ case 40: goto tr2667;
+ case 41: goto tr2668;
+ case 46: goto st931;
+ case 109: goto tr2671;
+ case 3082: goto tr2672;
+ case 3131: goto tr2673;
+ case 3338: goto tr2674;
+ case 3387: goto tr2674;
+ case 3594: goto tr2675;
+ case 3643: goto tr2676;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2670;
+ goto tr2535;
+tr2678:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st924;
+tr2679:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st924;
+tr2681:
+ {
+ s->line_counter++;
+ }
+ goto st924;
+tr2715:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 924; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st924;
+tr2666:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st924;
+tr2667:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st924;
+tr2668:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st924;
+tr2672:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st924;
+tr2716:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st924;
+tr2717:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st924;
+tr2718:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st924;
+tr2721:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st924;
+st924:
+ if ( ++p == pe )
+ goto _test_eof924;
+case 924:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st924;
+ case 32: goto st924;
+ case 40: goto tr2678;
+ case 41: goto tr2679;
+ case 3082: goto tr2681;
+ case 3131: goto tr2682;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2683;
+ case 3643: goto tr2684;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2680;
+ goto tr2535;
+tr2689:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st925;
+tr2680:
+ {
+ s->decimals = 2;
+ }
+ {
+ s->decimal_counter = 0;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st925;
+st925:
+ if ( ++p == pe )
+ goto _test_eof925;
+case 925:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2685;
+ case 32: goto tr2685;
+ case 40: goto tr2686;
+ case 41: goto tr2687;
+ case 46: goto st928;
+ case 109: goto tr2685;
+ case 3082: goto tr2690;
+ case 3131: goto tr2691;
+ case 3338: goto tr2692;
+ case 3387: goto tr2692;
+ case 3594: goto tr2693;
+ case 3643: goto tr2694;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2689;
+ goto tr2535;
+tr2696:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st926;
+tr2697:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st926;
+tr2698:
+ {
+ s->line_counter++;
+ }
+ goto st926;
+tr2703:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 926; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st926;
+tr2685:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st926;
+tr2686:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st926;
+tr2687:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st926;
+tr2690:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st926;
+tr2704:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st926;
+tr2705:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st926;
+tr2706:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st926;
+tr2708:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st926;
+st926:
+ if ( ++p == pe )
+ goto _test_eof926;
+case 926:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st926;
+ case 32: goto st926;
+ case 40: goto tr2696;
+ case 41: goto tr2697;
+ case 3082: goto tr2698;
+ case 3131: goto tr2699;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2700;
+ case 3643: goto tr2701;
+ }
+ goto tr2535;
+tr2699:
+ {
+ s->buffer_length = 0;
+ }
+ goto st927;
+tr2702:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st927;
+tr2691:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st927;
+tr2709:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st927;
+st927:
+ if ( ++p == pe )
+ goto _test_eof927;
+case 927:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2703;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2702;
+ goto tr2535;
+tr2635:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2644:
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2655:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2674:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2692:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2710:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2723:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2736:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+tr2749:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1445;
+st1445:
+ if ( ++p == pe )
+ goto _test_eof1445;
+case 1445:
+ goto st0;
+tr2700:
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1446;
+tr2693:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1446;
+tr2711:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1446;
+st1446:
+ if ( ++p == pe )
+ goto _test_eof1446;
+case 1446:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st926;
+ case 32: goto st926;
+ case 40: goto tr2696;
+ case 41: goto tr2697;
+ case 3082: goto tr2698;
+ case 3131: goto tr2699;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2700;
+ case 3643: goto tr2701;
+ }
+ goto tr2535;
+tr2701:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1447;
+tr2694:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1447;
+tr2712:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1447;
+st1447:
+ if ( ++p == pe )
+ goto _test_eof1447;
+case 1447:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2703;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2702;
+ goto tr2535;
+st928:
+ if ( ++p == pe )
+ goto _test_eof928;
+case 928:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2704;
+ case 32: goto tr2704;
+ case 40: goto tr2705;
+ case 41: goto tr2706;
+ case 109: goto tr2704;
+ case 3082: goto tr2708;
+ case 3131: goto tr2709;
+ case 3338: goto tr2710;
+ case 3387: goto tr2710;
+ case 3594: goto tr2711;
+ case 3643: goto tr2712;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2707;
+ goto tr2535;
+tr2707:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st929;
+tr2713:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st929;
+st929:
+ if ( ++p == pe )
+ goto _test_eof929;
+case 929:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2685;
+ case 32: goto tr2685;
+ case 40: goto tr2686;
+ case 41: goto tr2687;
+ case 109: goto tr2685;
+ case 3082: goto tr2690;
+ case 3131: goto tr2691;
+ case 3338: goto tr2692;
+ case 3387: goto tr2692;
+ case 3594: goto tr2693;
+ case 3643: goto tr2694;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2713;
+ goto tr2535;
+tr2682:
+ {
+ s->buffer_length = 0;
+ }
+ goto st930;
+tr2714:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st930;
+tr2673:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st930;
+tr2722:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st930;
+st930:
+ if ( ++p == pe )
+ goto _test_eof930;
+case 930:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2715;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2714;
+ goto tr2535;
+tr2683:
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1448;
+tr2675:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1448;
+tr2724:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1448;
+st1448:
+ if ( ++p == pe )
+ goto _test_eof1448;
+case 1448:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st924;
+ case 32: goto st924;
+ case 40: goto tr2678;
+ case 41: goto tr2679;
+ case 3082: goto tr2681;
+ case 3131: goto tr2682;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2683;
+ case 3643: goto tr2684;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2680;
+ goto tr2535;
+tr2684:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1449;
+tr2676:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1449;
+tr2725:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1449;
+st1449:
+ if ( ++p == pe )
+ goto _test_eof1449;
+case 1449:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2715;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2714;
+ goto tr2535;
+st931:
+ if ( ++p == pe )
+ goto _test_eof931;
+case 931:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2716;
+ case 32: goto tr2716;
+ case 40: goto tr2717;
+ case 41: goto tr2718;
+ case 109: goto tr2720;
+ case 3082: goto tr2721;
+ case 3131: goto tr2722;
+ case 3338: goto tr2723;
+ case 3387: goto tr2723;
+ case 3594: goto tr2724;
+ case 3643: goto tr2725;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2719;
+ goto tr2535;
+tr2719:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st932;
+tr2726:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st932;
+st932:
+ if ( ++p == pe )
+ goto _test_eof932;
+case 932:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2666;
+ case 32: goto tr2666;
+ case 40: goto tr2667;
+ case 41: goto tr2668;
+ case 109: goto tr2671;
+ case 3082: goto tr2672;
+ case 3131: goto tr2673;
+ case 3338: goto tr2674;
+ case 3387: goto tr2674;
+ case 3594: goto tr2675;
+ case 3643: goto tr2676;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2726;
+ goto tr2535;
+tr2671:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st933;
+tr2720:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st933;
+st933:
+ if ( ++p == pe )
+ goto _test_eof933;
+case 933:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st924;
+ case 32: goto st924;
+ case 40: goto tr2678;
+ case 41: goto tr2679;
+ case 3082: goto tr2681;
+ case 3131: goto tr2682;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2683;
+ case 3643: goto tr2684;
+ }
+ goto tr2535;
+tr2663:
+ {
+ s->buffer_length = 0;
+ }
+ goto st934;
+tr2727:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st934;
+tr2654:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st934;
+tr2735:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st934;
+st934:
+ if ( ++p == pe )
+ goto _test_eof934;
+case 934:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2728;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2727;
+ goto tr2535;
+tr2664:
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1450;
+tr2656:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1450;
+tr2737:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1450;
+st1450:
+ if ( ++p == pe )
+ goto _test_eof1450;
+case 1450:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st922;
+ case 32: goto st922;
+ case 40: goto tr2659;
+ case 41: goto tr2660;
+ case 3082: goto tr2662;
+ case 3131: goto tr2663;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2664;
+ case 3643: goto tr2665;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2661;
+ goto tr2535;
+tr2665:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1451;
+tr2657:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1451;
+tr2738:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1451;
+st1451:
+ if ( ++p == pe )
+ goto _test_eof1451;
+case 1451:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2728;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2727;
+ goto tr2535;
+st935:
+ if ( ++p == pe )
+ goto _test_eof935;
+case 935:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2729;
+ case 32: goto tr2729;
+ case 40: goto tr2730;
+ case 41: goto tr2731;
+ case 109: goto tr2733;
+ case 3082: goto tr2734;
+ case 3131: goto tr2735;
+ case 3338: goto tr2736;
+ case 3387: goto tr2736;
+ case 3594: goto tr2737;
+ case 3643: goto tr2738;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2732;
+ goto tr2535;
+tr2732:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st936;
+tr2739:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st936;
+st936:
+ if ( ++p == pe )
+ goto _test_eof936;
+case 936:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2647;
+ case 32: goto tr2647;
+ case 40: goto tr2648;
+ case 41: goto tr2649;
+ case 109: goto tr2652;
+ case 3082: goto tr2653;
+ case 3131: goto tr2654;
+ case 3338: goto tr2655;
+ case 3387: goto tr2655;
+ case 3594: goto tr2656;
+ case 3643: goto tr2657;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2739;
+ goto tr2535;
+tr2652:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st937;
+tr2733:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st937;
+st937:
+ if ( ++p == pe )
+ goto _test_eof937;
+case 937:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st922;
+ case 32: goto st922;
+ case 40: goto tr2659;
+ case 41: goto tr2660;
+ case 3082: goto tr2662;
+ case 3131: goto tr2663;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2664;
+ case 3643: goto tr2665;
+ }
+ goto tr2535;
+tr2643:
+ {
+ s->buffer_length = 0;
+ }
+ goto st938;
+tr2740:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st938;
+tr2634:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st938;
+tr2748:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st938;
+st938:
+ if ( ++p == pe )
+ goto _test_eof938;
+case 938:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2741;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2740;
+ goto tr2535;
+tr2636:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1452;
+tr2645:
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1452;
+tr2750:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1452;
+st1452:
+ if ( ++p == pe )
+ goto _test_eof1452;
+case 1452:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st920;
+ case 32: goto st920;
+ case 40: goto tr2639;
+ case 41: goto tr2640;
+ case 3082: goto tr2642;
+ case 3131: goto tr2643;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2645;
+ case 3643: goto tr2646;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2641;
+ goto tr2535;
+tr2637:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1453;
+tr2646:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1453;
+tr2751:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1453;
+st1453:
+ if ( ++p == pe )
+ goto _test_eof1453;
+case 1453:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2741;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2740;
+ goto tr2535;
+st939:
+ if ( ++p == pe )
+ goto _test_eof939;
+case 939:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2742;
+ case 32: goto tr2742;
+ case 40: goto tr2743;
+ case 41: goto tr2744;
+ case 109: goto tr2746;
+ case 3082: goto tr2747;
+ case 3131: goto tr2748;
+ case 3338: goto tr2749;
+ case 3387: goto tr2749;
+ case 3594: goto tr2750;
+ case 3643: goto tr2751;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2745;
+ goto tr2535;
+tr2745:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st940;
+tr2752:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st940;
+st940:
+ if ( ++p == pe )
+ goto _test_eof940;
+case 940:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2627;
+ case 32: goto tr2627;
+ case 40: goto tr2628;
+ case 41: goto tr2629;
+ case 109: goto tr2632;
+ case 3082: goto tr2633;
+ case 3131: goto tr2634;
+ case 3338: goto tr2635;
+ case 3387: goto tr2635;
+ case 3594: goto tr2636;
+ case 3643: goto tr2637;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2752;
+ goto tr2535;
+tr2632:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st941;
+tr2746:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ goto st941;
+st941:
+ if ( ++p == pe )
+ goto _test_eof941;
+case 941:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st920;
+ case 32: goto st920;
+ case 40: goto tr2639;
+ case 41: goto tr2640;
+ case 3082: goto tr2642;
+ case 3131: goto tr2643;
+ case 3338: goto tr2644;
+ case 3387: goto tr2644;
+ case 3594: goto tr2645;
+ case 3643: goto tr2646;
+ }
+ goto tr2535;
+tr2623:
+ {
+ s->buffer_length = 0;
+ }
+ goto st942;
+tr2753:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st942;
+tr2759:
+ {
+ s->loc.long_sign = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st942;
+st942:
+ if ( ++p == pe )
+ goto _test_eof942;
+case 942:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2754;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2753;
+ goto tr2535;
+st943:
+ if ( ++p == pe )
+ goto _test_eof943;
+case 943:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2755;
+ case 32: goto tr2755;
+ case 40: goto tr2756;
+ case 41: goto tr2757;
+ case 2058: goto tr2758;
+ case 2107: goto tr2759;
+ }
+ goto tr2535;
+tr2618:
+ {
+ s->buffer_length = 0;
+ }
+ goto st944;
+tr2760:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st944;
+tr2613:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st944;
+tr2767:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st944;
+st944:
+ if ( ++p == pe )
+ goto _test_eof944;
+case 944:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2761;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2760;
+ goto tr2535;
+st945:
+ if ( ++p == pe )
+ goto _test_eof945;
+case 945:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2762;
+ case 32: goto tr2762;
+ case 40: goto tr2763;
+ case 41: goto tr2764;
+ case 2058: goto tr2766;
+ case 2107: goto tr2767;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2765;
+ goto tr2535;
+tr2765:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st946;
+tr2768:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st946;
+st946:
+ if ( ++p == pe )
+ goto _test_eof946;
+case 946:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2607;
+ case 32: goto tr2607;
+ case 40: goto tr2608;
+ case 41: goto tr2609;
+ case 2058: goto tr2612;
+ case 2107: goto tr2613;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2768;
+ goto tr2535;
+tr2606:
+ {
+ s->buffer_length = 0;
+ }
+ goto st947;
+tr2769:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st947;
+tr2600:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st947;
+st947:
+ if ( ++p == pe )
+ goto _test_eof947;
+case 947:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2770;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2769;
+ goto tr2535;
+tr2594:
+ {
+ s->buffer_length = 0;
+ }
+ goto st948;
+tr2771:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st948;
+tr2586:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st948;
+st948:
+ if ( ++p == pe )
+ goto _test_eof948;
+case 948:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2772;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2771;
+ goto tr2535;
+tr2579:
+ {
+ s->buffer_length = 0;
+ }
+ goto st949;
+tr2773:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st949;
+tr2779:
+ {
+ s->loc.lat_sign = -1;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st949;
+st949:
+ if ( ++p == pe )
+ goto _test_eof949;
+case 949:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2774;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2773;
+ goto tr2535;
+st950:
+ if ( ++p == pe )
+ goto _test_eof950;
+case 950:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2775;
+ case 32: goto tr2775;
+ case 40: goto tr2776;
+ case 41: goto tr2777;
+ case 2058: goto tr2778;
+ case 2107: goto tr2779;
+ }
+ goto tr2535;
+tr2574:
+ {
+ s->buffer_length = 0;
+ }
+ goto st951;
+tr2780:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st951;
+tr2569:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st951;
+tr2787:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st951;
+st951:
+ if ( ++p == pe )
+ goto _test_eof951;
+case 951:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2781;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2780;
+ goto tr2535;
+st952:
+ if ( ++p == pe )
+ goto _test_eof952;
+case 952:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2782;
+ case 32: goto tr2782;
+ case 40: goto tr2783;
+ case 41: goto tr2784;
+ case 2058: goto tr2786;
+ case 2107: goto tr2787;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2785;
+ goto tr2535;
+tr2785:
+ {
+ s->number64_tmp = s->number64;
+ }
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st953;
+tr2788:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->decimal_counter++;
+ }
+ goto st953;
+st953:
+ if ( ++p == pe )
+ goto _test_eof953;
+case 953:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2563;
+ case 32: goto tr2563;
+ case 40: goto tr2564;
+ case 41: goto tr2565;
+ case 2058: goto tr2568;
+ case 2107: goto tr2569;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2788;
+ goto tr2535;
+tr2562:
+ {
+ s->buffer_length = 0;
+ }
+ goto st954;
+tr2789:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st954;
+tr2556:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st954;
+st954:
+ if ( ++p == pe )
+ goto _test_eof954;
+case 954:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2790;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2789;
+ goto tr2535;
+tr2550:
+ {
+ s->buffer_length = 0;
+ }
+ goto st955;
+tr2791:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st955;
+tr2542:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st955;
+st955:
+ if ( ++p == pe )
+ goto _test_eof955;
+case 955:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2792;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2791;
+ goto tr2535;
+st956:
+ if ( ++p == pe )
+ goto _test_eof956;
+case 956:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2793;
+ goto tr2327;
+tr2793:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st957;
+tr2797:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st957;
+st957:
+ if ( ++p == pe )
+ goto _test_eof957;
+case 957:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2794;
+ case 32: goto tr2794;
+ case 40: goto tr2795;
+ case 41: goto tr2796;
+ case 2058: goto tr2798;
+ case 2107: goto tr2799;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2797;
+ goto tr2327;
+tr2801:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st958;
+tr2802:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st958;
+tr2804:
+ {
+ s->line_counter++;
+ }
+ goto st958;
+tr2836:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 958; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st958;
+tr2794:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st958;
+tr2795:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st958;
+tr2796:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st958;
+tr2798:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st958;
+st958:
+ if ( ++p == pe )
+ goto _test_eof958;
+case 958:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st958;
+ case 32: goto st958;
+ case 40: goto tr2801;
+ case 41: goto tr2802;
+ case 2058: goto tr2804;
+ case 2107: goto tr2805;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2803;
+ goto tr2327;
+tr2803:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st959;
+tr2809:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st959;
+st959:
+ if ( ++p == pe )
+ goto _test_eof959;
+case 959:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2806;
+ case 32: goto tr2806;
+ case 40: goto tr2807;
+ case 41: goto tr2808;
+ case 2058: goto tr2810;
+ case 2107: goto tr2811;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2809;
+ goto tr2327;
+tr2813:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st960;
+tr2814:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st960;
+tr2816:
+ {
+ s->line_counter++;
+ }
+ goto st960;
+tr2834:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 960; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st960;
+tr2806:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st960;
+tr2807:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st960;
+tr2808:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st960;
+tr2810:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st960;
+st960:
+ if ( ++p == pe )
+ goto _test_eof960;
+case 960:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st960;
+ case 32: goto st960;
+ case 40: goto tr2813;
+ case 41: goto tr2814;
+ case 2058: goto tr2816;
+ case 2107: goto tr2817;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2815;
+ goto tr2327;
+tr2815:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st961;
+tr2821:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st961;
+st961:
+ if ( ++p == pe )
+ goto _test_eof961;
+case 961:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2818;
+ case 32: goto tr2818;
+ case 40: goto tr2819;
+ case 41: goto tr2820;
+ case 2058: goto tr2822;
+ case 2107: goto tr2823;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2821;
+ goto tr2327;
+tr2825:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st962;
+tr2826:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st962;
+tr2828:
+ {
+ s->line_counter++;
+ }
+ goto st962;
+tr2832:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 962; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st962;
+tr2818:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st962;
+tr2819:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st962;
+tr2820:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st962;
+tr2822:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st962;
+st962:
+ if ( ++p == pe )
+ goto _test_eof962;
+case 962:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st962;
+ case 32: goto st962;
+ case 40: goto tr2825;
+ case 41: goto tr2826;
+ case 42: goto tr2827;
+ case 92: goto tr2827;
+ case 95: goto tr2827;
+ case 2058: goto tr2828;
+ case 2107: goto tr2829;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr2827;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2827;
+ } else
+ goto tr2827;
+ goto tr75;
+tr2827:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 963;goto st309;} }
+ goto st963;
+st963:
+ if ( ++p == pe )
+ goto _test_eof963;
+case 963:
+ switch( (*p) ) {
+ case 32: goto tr2830;
+ case 59: goto tr2830;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2830;
+ } else if ( (*p) >= 9 )
+ goto tr2830;
+ goto tr75;
+tr2830:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1454;
+st1454:
+ if ( ++p == pe )
+ goto _test_eof1454;
+case 1454:
+ goto st0;
+tr2829:
+ {
+ s->buffer_length = 0;
+ }
+ goto st964;
+tr2831:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st964;
+tr2823:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st964;
+st964:
+ if ( ++p == pe )
+ goto _test_eof964;
+case 964:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2832;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2831;
+ goto tr75;
+tr2817:
+ {
+ s->buffer_length = 0;
+ }
+ goto st965;
+tr2833:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st965;
+tr2811:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st965;
+st965:
+ if ( ++p == pe )
+ goto _test_eof965;
+case 965:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2834;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2833;
+ goto tr75;
+tr2805:
+ {
+ s->buffer_length = 0;
+ }
+ goto st966;
+tr2835:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st966;
+tr2799:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st966;
+st966:
+ if ( ++p == pe )
+ goto _test_eof966;
+case 966:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2836;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2835;
+ goto tr75;
+st967:
+ if ( ++p == pe )
+ goto _test_eof967;
+case 967:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2837;
+ goto tr2327;
+tr2837:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st968;
+tr2841:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st968;
+st968:
+ if ( ++p == pe )
+ goto _test_eof968;
+case 968:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2838;
+ case 32: goto tr2838;
+ case 40: goto tr2839;
+ case 41: goto tr2840;
+ case 2058: goto tr2842;
+ case 2107: goto tr2843;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2841;
+ goto tr2327;
+tr2845:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st969;
+tr2846:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st969;
+tr2848:
+ {
+ s->line_counter++;
+ }
+ goto st969;
+tr2905:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 969; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st969;
+tr2838:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st969;
+tr2839:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st969;
+tr2840:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st969;
+tr2842:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st969;
+st969:
+ if ( ++p == pe )
+ goto _test_eof969;
+case 969:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st969;
+ case 32: goto st969;
+ case 40: goto tr2845;
+ case 41: goto tr2846;
+ case 2058: goto tr2848;
+ case 2107: goto tr2849;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2847;
+ goto tr2327;
+tr2847:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st970;
+tr2853:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st970;
+st970:
+ if ( ++p == pe )
+ goto _test_eof970;
+case 970:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2850;
+ case 32: goto tr2850;
+ case 40: goto tr2851;
+ case 41: goto tr2852;
+ case 2058: goto tr2854;
+ case 2107: goto tr2855;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2853;
+ goto tr2327;
+tr2858:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st971;
+tr2859:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st971;
+tr2860:
+ {
+ s->line_counter++;
+ }
+ goto st971;
+tr2903:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 971; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st971;
+tr2850:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st971;
+tr2851:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st971;
+tr2852:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st971;
+tr2854:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st971;
+st971:
+ if ( ++p == pe )
+ goto _test_eof971;
+case 971:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st971;
+ case 32: goto st971;
+ case 40: goto tr2858;
+ case 41: goto tr2859;
+ case 2058: goto tr2860;
+ case 2107: goto tr2861;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2856;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2856;
+ } else
+ goto tr2856;
+ goto tr75;
+tr2856:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 972;goto st318;} }
+ goto st972;
+st972:
+ if ( ++p == pe )
+ goto _test_eof972;
+case 972:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2862;
+ case 32: goto tr2862;
+ case 40: goto tr2863;
+ case 41: goto tr2864;
+ case 2058: goto tr2865;
+ case 2107: goto tr2866;
+ }
+ goto tr75;
+tr2869:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st973;
+tr2870:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st973;
+tr2871:
+ {
+ s->line_counter++;
+ }
+ goto st973;
+tr2901:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 973; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st973;
+tr2862:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st973;
+tr2863:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st973;
+tr2864:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st973;
+tr2865:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st973;
+st973:
+ if ( ++p == pe )
+ goto _test_eof973;
+case 973:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st973;
+ case 32: goto st973;
+ case 40: goto tr2869;
+ case 41: goto tr2870;
+ case 2058: goto tr2871;
+ case 2107: goto tr2872;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2867;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2867;
+ } else
+ goto tr2867;
+ goto tr75;
+tr2867:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 974;goto st318;} }
+ goto st974;
+st974:
+ if ( ++p == pe )
+ goto _test_eof974;
+case 974:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2873;
+ case 32: goto tr2873;
+ case 40: goto tr2874;
+ case 41: goto tr2875;
+ case 2058: goto tr2876;
+ case 2107: goto tr2877;
+ }
+ goto tr75;
+tr2880:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st975;
+tr2881:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st975;
+tr2882:
+ {
+ s->line_counter++;
+ }
+ goto st975;
+tr2899:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 975; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st975;
+tr2873:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st975;
+tr2874:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st975;
+tr2875:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st975;
+tr2876:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st975;
+st975:
+ if ( ++p == pe )
+ goto _test_eof975;
+case 975:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st975;
+ case 32: goto st975;
+ case 40: goto tr2880;
+ case 41: goto tr2881;
+ case 2058: goto tr2882;
+ case 2107: goto tr2883;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr2878;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr2878;
+ } else
+ goto tr2878;
+ goto tr75;
+tr2878:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 976;goto st318;} }
+ goto st976;
+st976:
+ if ( ++p == pe )
+ goto _test_eof976;
+case 976:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2884;
+ case 32: goto tr2884;
+ case 40: goto tr2885;
+ case 41: goto tr2886;
+ case 2058: goto tr2887;
+ case 2107: goto tr2888;
+ }
+ goto tr75;
+tr2890:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st977;
+tr2891:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st977;
+tr2893:
+ {
+ s->line_counter++;
+ }
+ goto st977;
+tr2897:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 977; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st977;
+tr2884:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st977;
+tr2885:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st977;
+tr2886:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st977;
+tr2887:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st977;
+st977:
+ if ( ++p == pe )
+ goto _test_eof977;
+case 977:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st977;
+ case 32: goto st977;
+ case 40: goto tr2890;
+ case 41: goto tr2891;
+ case 42: goto tr2892;
+ case 92: goto tr2892;
+ case 95: goto tr2892;
+ case 2058: goto tr2893;
+ case 2107: goto tr2894;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr2892;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2892;
+ } else
+ goto tr2892;
+ goto tr75;
+tr2892:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 978;goto st309;} }
+ goto st978;
+st978:
+ if ( ++p == pe )
+ goto _test_eof978;
+case 978:
+ switch( (*p) ) {
+ case 32: goto tr2895;
+ case 59: goto tr2895;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr2895;
+ } else if ( (*p) >= 9 )
+ goto tr2895;
+ goto tr75;
+tr2895:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1455;
+st1455:
+ if ( ++p == pe )
+ goto _test_eof1455;
+case 1455:
+ goto st0;
+tr2894:
+ {
+ s->buffer_length = 0;
+ }
+ goto st979;
+tr2896:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st979;
+tr2888:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st979;
+st979:
+ if ( ++p == pe )
+ goto _test_eof979;
+case 979:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2897;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2896;
+ goto tr75;
+tr2883:
+ {
+ s->buffer_length = 0;
+ }
+ goto st980;
+tr2898:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st980;
+tr2877:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st980;
+st980:
+ if ( ++p == pe )
+ goto _test_eof980;
+case 980:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2899;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2898;
+ goto tr75;
+tr2872:
+ {
+ s->buffer_length = 0;
+ }
+ goto st981;
+tr2900:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st981;
+tr2866:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st981;
+st981:
+ if ( ++p == pe )
+ goto _test_eof981;
+case 981:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2901;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2900;
+ goto tr75;
+tr2861:
+ {
+ s->buffer_length = 0;
+ }
+ goto st982;
+tr2902:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st982;
+tr2855:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st982;
+st982:
+ if ( ++p == pe )
+ goto _test_eof982;
+case 982:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2903;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2902;
+ goto tr75;
+tr2849:
+ {
+ s->buffer_length = 0;
+ }
+ goto st983;
+tr2904:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st983;
+tr2843:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st983;
+st983:
+ if ( ++p == pe )
+ goto _test_eof983;
+case 983:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2905;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2904;
+ goto tr75;
+st984:
+ if ( ++p == pe )
+ goto _test_eof984;
+case 984:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2906;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr2906;
+ } else
+ goto tr2906;
+ goto tr75;
+tr2906:
+ { p--; {stack[top++] = 985;goto st803;} }
+ goto st985;
+st985:
+ if ( ++p == pe )
+ goto _test_eof985;
+case 985:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st986;
+ case 32: goto st986;
+ case 40: goto tr2908;
+ case 41: goto tr2909;
+ case 2058: goto tr2910;
+ case 2107: goto tr2911;
+ }
+ goto tr75;
+tr2908:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st986;
+tr2909:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st986;
+tr2910:
+ {
+ s->line_counter++;
+ }
+ goto st986;
+tr2937:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 986; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st986;
+st986:
+ if ( ++p == pe )
+ goto _test_eof986;
+case 986:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st986;
+ case 32: goto st986;
+ case 40: goto tr2908;
+ case 41: goto tr2909;
+ case 2058: goto tr2910;
+ case 2107: goto tr2911;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2912;
+ goto tr2327;
+tr2912:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st987;
+tr2916:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st987;
+st987:
+ if ( ++p == pe )
+ goto _test_eof987;
+case 987:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2913;
+ case 32: goto tr2913;
+ case 40: goto tr2914;
+ case 41: goto tr2915;
+ case 2058: goto tr2917;
+ case 2107: goto tr2918;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2916;
+ goto tr2327;
+tr2920:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st988;
+tr2921:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st988;
+tr2923:
+ {
+ s->line_counter++;
+ }
+ goto st988;
+tr2935:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 988; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st988;
+tr2913:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st988;
+tr2914:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st988;
+tr2915:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st988;
+tr2917:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st988;
+st988:
+ if ( ++p == pe )
+ goto _test_eof988;
+case 988:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st988;
+ case 32: goto st988;
+ case 40: goto tr2920;
+ case 41: goto tr2921;
+ case 2058: goto tr2923;
+ case 2107: goto tr2924;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2922;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2922;
+ } else
+ goto tr2922;
+ goto tr75;
+tr2922:
+ { p--; {stack[top++] = 989;goto st699;} }
+ goto st989;
+st989:
+ if ( ++p == pe )
+ goto _test_eof989;
+case 989:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st990;
+ case 32: goto st990;
+ case 40: goto tr2926;
+ case 41: goto tr2927;
+ case 2058: goto tr2928;
+ case 2107: goto tr2929;
+ }
+ goto tr75;
+tr2926:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st990;
+tr2927:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st990;
+tr2928:
+ {
+ s->line_counter++;
+ }
+ goto st990;
+tr2933:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 990; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st990;
+st990:
+ if ( ++p == pe )
+ goto _test_eof990;
+case 990:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st990;
+ case 32: goto st990;
+ case 40: goto tr2926;
+ case 41: goto tr2927;
+ case 43: goto tr2930;
+ case 2058: goto tr2928;
+ case 2107: goto tr2929;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr2930;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2930;
+ } else
+ goto tr2930;
+ goto tr75;
+tr2930:
+ { p--; {stack[top++] = 991;goto st369;} }
+ goto st991;
+st991:
+ if ( ++p == pe )
+ goto _test_eof991;
+case 991:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 2570: goto tr2931;
+ case 2619: goto tr2931;
+ }
+ goto tr75;
+tr2931:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1456;
+st1456:
+ if ( ++p == pe )
+ goto _test_eof1456;
+case 1456:
+ goto st0;
+tr2929:
+ {
+ s->buffer_length = 0;
+ }
+ goto st992;
+tr2932:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st992;
+st992:
+ if ( ++p == pe )
+ goto _test_eof992;
+case 992:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2933;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2932;
+ goto tr75;
+tr2924:
+ {
+ s->buffer_length = 0;
+ }
+ goto st993;
+tr2934:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st993;
+tr2918:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st993;
+st993:
+ if ( ++p == pe )
+ goto _test_eof993;
+case 993:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2935;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2934;
+ goto tr75;
+tr2911:
+ {
+ s->buffer_length = 0;
+ }
+ goto st994;
+tr2936:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st994;
+st994:
+ if ( ++p == pe )
+ goto _test_eof994;
+case 994:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2937;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2936;
+ goto tr75;
+tr2941:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st995;
+tr2942:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st995;
+tr2945:
+ {
+ s->line_counter++;
+ }
+ goto st995;
+tr2968:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 995; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st995;
+tr2958:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ goto st995;
+tr2959:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st995;
+tr2960:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st995;
+tr2962:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st995;
+st995:
+ if ( ++p == pe )
+ goto _test_eof995;
+case 995:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st995;
+ case 32: goto st995;
+ case 33: goto tr2940;
+ case 40: goto tr2941;
+ case 41: goto tr2942;
+ case 49: goto tr2943;
+ case 50: goto tr2944;
+ case 3082: goto tr2945;
+ case 3131: goto tr2946;
+ case 3338: goto tr2947;
+ case 3387: goto tr2947;
+ case 3594: goto tr2948;
+ case 3643: goto tr2949;
+ }
+ goto tr2938;
+tr2940:
+ {
+ memset(&(s->apl), 0, sizeof(s->apl));
+ }
+ {
+ s->apl.excl_flag = 128; // dec 128 = bin 10000000.
+ }
+ goto st996;
+st996:
+ if ( ++p == pe )
+ goto _test_eof996;
+case 996:
+ switch( (*p) ) {
+ case 49: goto tr2950;
+ case 50: goto tr2951;
+ }
+ goto tr2938;
+tr2943:
+ {
+ memset(&(s->apl), 0, sizeof(s->apl));
+ }
+ {
+ s->apl.addr_family = 1;
+ }
+ goto st997;
+tr2950:
+ {
+ s->apl.addr_family = 1;
+ }
+ goto st997;
+st997:
+ if ( ++p == pe )
+ goto _test_eof997;
+case 997:
+ if ( (*p) == 58 )
+ goto st998;
+ goto tr2938;
+st998:
+ if ( ++p == pe )
+ goto _test_eof998;
+case 998:
+ if ( (*p) == 46 )
+ goto tr2954;
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2954;
+ goto tr2953;
+tr2955:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st999;
+tr2954:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st999;
+st999:
+ if ( ++p == pe )
+ goto _test_eof999;
+case 999:
+ if ( (*p) == 47 )
+ goto tr2956;
+ if ( 46 <= (*p) && (*p) <= 57 )
+ goto tr2955;
+ goto tr2953;
+tr2956:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ goto st1000;
+tr2972:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ goto st1000;
+st1000:
+ if ( ++p == pe )
+ goto _test_eof1000;
+case 1000:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2957;
+ goto tr2938;
+tr2957:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1001;
+tr2961:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1001;
+st1001:
+ if ( ++p == pe )
+ goto _test_eof1001;
+case 1001:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto tr2958;
+ case 32: goto tr2958;
+ case 40: goto tr2959;
+ case 41: goto tr2960;
+ case 3082: goto tr2962;
+ case 3131: goto tr2963;
+ case 3338: goto tr2964;
+ case 3387: goto tr2964;
+ case 3594: goto tr2965;
+ case 3643: goto tr2966;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2961;
+ goto tr2938;
+tr2946:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1002;
+tr2967:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1002;
+tr2963:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1002;
+st1002:
+ if ( ++p == pe )
+ goto _test_eof1002;
+case 1002:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2968;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2967;
+ goto tr75;
+tr2947:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1457;
+tr2964:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1457;
+st1457:
+ if ( ++p == pe )
+ goto _test_eof1457;
+case 1457:
+ goto st0;
+tr2948:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1458;
+tr2965:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1458;
+st1458:
+ if ( ++p == pe )
+ goto _test_eof1458;
+case 1458:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st995;
+ case 32: goto st995;
+ case 33: goto tr2940;
+ case 40: goto tr2941;
+ case 41: goto tr2942;
+ case 49: goto tr2943;
+ case 50: goto tr2944;
+ case 3082: goto tr2945;
+ case 3131: goto tr2946;
+ case 3338: goto tr2947;
+ case 3387: goto tr2947;
+ case 3594: goto tr2948;
+ case 3643: goto tr2949;
+ }
+ goto tr2938;
+tr2944:
+ {
+ memset(&(s->apl), 0, sizeof(s->apl));
+ }
+ {
+ s->apl.addr_family = 2;
+ }
+ goto st1003;
+tr2951:
+ {
+ s->apl.addr_family = 2;
+ }
+ goto st1003;
+st1003:
+ if ( ++p == pe )
+ goto _test_eof1003;
+case 1003:
+ if ( (*p) == 58 )
+ goto st1004;
+ goto tr2938;
+st1004:
+ if ( ++p == pe )
+ goto _test_eof1004;
+case 1004:
+ if ( (*p) == 46 )
+ goto tr2970;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 58 )
+ goto tr2970;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2970;
+ } else
+ goto tr2970;
+ goto tr2953;
+tr2971:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1005;
+tr2970:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1005;
+st1005:
+ if ( ++p == pe )
+ goto _test_eof1005;
+case 1005:
+ if ( (*p) == 47 )
+ goto tr2972;
+ if ( (*p) < 65 ) {
+ if ( 46 <= (*p) && (*p) <= 58 )
+ goto tr2971;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr2971;
+ } else
+ goto tr2971;
+ goto tr2953;
+tr2949:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1459;
+tr2966:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ }
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {goto st307;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1459;
+st1459:
+ if ( ++p == pe )
+ goto _test_eof1459;
+case 1459:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr2968;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr2967;
+ goto tr75;
+st1006:
+ if ( ++p == pe )
+ goto _test_eof1006;
+case 1006:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr2973;
+ goto tr2327;
+tr2973:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1007;
+tr2977:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1007;
+st1007:
+ if ( ++p == pe )
+ goto _test_eof1007;
+case 1007:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2974;
+ case 32: goto tr2974;
+ case 40: goto tr2975;
+ case 41: goto tr2976;
+ case 2058: goto tr2978;
+ case 2107: goto tr2979;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2977;
+ goto tr2327;
+tr2981:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1008;
+tr2982:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1008;
+tr2984:
+ {
+ s->line_counter++;
+ }
+ goto st1008;
+tr3021:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1008; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1008;
+tr2974:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1008;
+tr2975:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1008;
+tr2976:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1008;
+tr2978:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1008;
+st1008:
+ if ( ++p == pe )
+ goto _test_eof1008;
+case 1008:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1008;
+ case 32: goto st1008;
+ case 40: goto tr2981;
+ case 41: goto tr2982;
+ case 2058: goto tr2984;
+ case 2107: goto tr2985;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2983;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr2983;
+ } else
+ goto tr2983;
+ goto tr75;
+tr2983:
+ { p--; {stack[top++] = 1009;goto st699;} }
+ goto st1009;
+st1009:
+ if ( ++p == pe )
+ goto _test_eof1009;
+case 1009:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1010;
+ case 32: goto st1010;
+ case 40: goto tr2987;
+ case 41: goto tr2988;
+ case 2058: goto tr2989;
+ case 2107: goto tr2990;
+ }
+ goto tr75;
+tr2987:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1010;
+tr2988:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1010;
+tr2989:
+ {
+ s->line_counter++;
+ }
+ goto st1010;
+tr3019:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1010; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1010;
+st1010:
+ if ( ++p == pe )
+ goto _test_eof1010;
+case 1010:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1010;
+ case 32: goto st1010;
+ case 40: goto tr2987;
+ case 41: goto tr2988;
+ case 2058: goto tr2989;
+ case 2107: goto tr2990;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2991;
+ goto tr2327;
+tr2991:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1011;
+tr2995:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1011;
+st1011:
+ if ( ++p == pe )
+ goto _test_eof1011;
+case 1011:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr2992;
+ case 32: goto tr2992;
+ case 40: goto tr2993;
+ case 41: goto tr2994;
+ case 2058: goto tr2996;
+ case 2107: goto tr2997;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr2995;
+ goto tr2327;
+tr3000:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1012;
+tr3001:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1012;
+tr3003:
+ {
+ s->line_counter++;
+ }
+ goto st1012;
+tr3017:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1012; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1012;
+tr2992:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1012;
+tr2993:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1012;
+tr2994:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1012;
+tr2996:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1012;
+st1012:
+ if ( ++p == pe )
+ goto _test_eof1012;
+case 1012:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1012;
+ case 32: goto st1012;
+ case 40: goto tr3000;
+ case 41: goto tr3001;
+ case 2058: goto tr3003;
+ case 2107: goto tr3004;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3002;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3002;
+ } else
+ goto tr3002;
+ goto tr2998;
+tr3002:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1013;
+st1013:
+ if ( ++p == pe )
+ goto _test_eof1013;
+case 1013:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3005;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3005;
+ } else
+ goto tr3005;
+ goto tr2998;
+tr3007:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1014;
+tr3008:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1014;
+tr3009:
+ {
+ s->line_counter++;
+ }
+ goto st1014;
+tr3015:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1014; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1014;
+tr3005:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1014;
+st1014:
+ if ( ++p == pe )
+ goto _test_eof1014;
+case 1014:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1014;
+ case 32: goto st1014;
+ case 40: goto tr3007;
+ case 41: goto tr3008;
+ case 3082: goto tr3009;
+ case 3131: goto tr3010;
+ case 3338: goto tr3011;
+ case 3387: goto tr3011;
+ case 3594: goto tr3012;
+ case 3643: goto tr3013;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3002;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3002;
+ } else
+ goto tr3002;
+ goto tr2998;
+tr3010:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1015;
+tr3014:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1015;
+st1015:
+ if ( ++p == pe )
+ goto _test_eof1015;
+case 1015:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3015;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3014;
+ goto tr2998;
+tr3011:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1460;
+st1460:
+ if ( ++p == pe )
+ goto _test_eof1460;
+case 1460:
+ goto st0;
+tr3012:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1461;
+st1461:
+ if ( ++p == pe )
+ goto _test_eof1461;
+case 1461:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1014;
+ case 32: goto st1014;
+ case 40: goto tr3007;
+ case 41: goto tr3008;
+ case 3082: goto tr3009;
+ case 3131: goto tr3010;
+ case 3338: goto tr3011;
+ case 3387: goto tr3011;
+ case 3594: goto tr3012;
+ case 3643: goto tr3013;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3002;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3002;
+ } else
+ goto tr3002;
+ goto tr2998;
+tr3013:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1462;
+st1462:
+ if ( ++p == pe )
+ goto _test_eof1462;
+case 1462:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3015;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3014;
+ goto tr2998;
+tr3004:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1016;
+tr3016:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1016;
+tr2997:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1016;
+st1016:
+ if ( ++p == pe )
+ goto _test_eof1016;
+case 1016:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3017;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3016;
+ goto tr75;
+tr2990:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1017;
+tr3018:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1017;
+st1017:
+ if ( ++p == pe )
+ goto _test_eof1017;
+case 1017:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3019;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3018;
+ goto tr75;
+tr2985:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1018;
+tr3020:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1018;
+tr2979:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1018;
+st1018:
+ if ( ++p == pe )
+ goto _test_eof1018;
+case 1018:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3021;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3020;
+ goto tr75;
+st1019:
+ if ( ++p == pe )
+ goto _test_eof1019;
+case 1019:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3022;
+ goto tr2327;
+tr3022:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1020;
+tr3026:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1020;
+st1020:
+ if ( ++p == pe )
+ goto _test_eof1020;
+case 1020:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3023;
+ case 32: goto tr3023;
+ case 40: goto tr3024;
+ case 41: goto tr3025;
+ case 2058: goto tr3027;
+ case 2107: goto tr3028;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3026;
+ goto tr2327;
+tr3030:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1021;
+tr3031:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1021;
+tr3033:
+ {
+ s->line_counter++;
+ }
+ goto st1021;
+tr3061:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1021; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1021;
+tr3023:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1021;
+tr3024:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1021;
+tr3025:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1021;
+tr3027:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1021;
+st1021:
+ if ( ++p == pe )
+ goto _test_eof1021;
+case 1021:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1021;
+ case 32: goto st1021;
+ case 40: goto tr3030;
+ case 41: goto tr3031;
+ case 2058: goto tr3033;
+ case 2107: goto tr3034;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3032;
+ goto tr2327;
+tr3032:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1022;
+tr3038:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1022;
+st1022:
+ if ( ++p == pe )
+ goto _test_eof1022;
+case 1022:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3035;
+ case 32: goto tr3035;
+ case 40: goto tr3036;
+ case 41: goto tr3037;
+ case 2058: goto tr3039;
+ case 2107: goto tr3040;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3038;
+ goto tr2327;
+tr3042:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1023;
+tr3043:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1023;
+tr3045:
+ {
+ s->line_counter++;
+ }
+ goto st1023;
+tr3059:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1023; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1023;
+tr3035:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1023;
+tr3036:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1023;
+tr3037:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1023;
+tr3039:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1023;
+st1023:
+ if ( ++p == pe )
+ goto _test_eof1023;
+case 1023:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1023;
+ case 32: goto st1023;
+ case 40: goto tr3042;
+ case 41: goto tr3043;
+ case 2058: goto tr3045;
+ case 2107: goto tr3046;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3044;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3044;
+ } else
+ goto tr3044;
+ goto tr2998;
+tr3044:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1024;
+st1024:
+ if ( ++p == pe )
+ goto _test_eof1024;
+case 1024:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3047;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3047;
+ } else
+ goto tr3047;
+ goto tr2998;
+tr3049:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1025;
+tr3050:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1025;
+tr3051:
+ {
+ s->line_counter++;
+ }
+ goto st1025;
+tr3057:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1025; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1025;
+tr3047:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1025;
+st1025:
+ if ( ++p == pe )
+ goto _test_eof1025;
+case 1025:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1025;
+ case 32: goto st1025;
+ case 40: goto tr3049;
+ case 41: goto tr3050;
+ case 3082: goto tr3051;
+ case 3131: goto tr3052;
+ case 3338: goto tr3053;
+ case 3387: goto tr3053;
+ case 3594: goto tr3054;
+ case 3643: goto tr3055;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3044;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3044;
+ } else
+ goto tr3044;
+ goto tr2998;
+tr3052:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1026;
+tr3056:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1026;
+st1026:
+ if ( ++p == pe )
+ goto _test_eof1026;
+case 1026:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3057;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3056;
+ goto tr2998;
+tr3053:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1463;
+st1463:
+ if ( ++p == pe )
+ goto _test_eof1463;
+case 1463:
+ goto st0;
+tr3054:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1464;
+st1464:
+ if ( ++p == pe )
+ goto _test_eof1464;
+case 1464:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1025;
+ case 32: goto st1025;
+ case 40: goto tr3049;
+ case 41: goto tr3050;
+ case 3082: goto tr3051;
+ case 3131: goto tr3052;
+ case 3338: goto tr3053;
+ case 3387: goto tr3053;
+ case 3594: goto tr3054;
+ case 3643: goto tr3055;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3044;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3044;
+ } else
+ goto tr3044;
+ goto tr2998;
+tr3055:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1465;
+st1465:
+ if ( ++p == pe )
+ goto _test_eof1465;
+case 1465:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3057;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3056;
+ goto tr2998;
+tr3046:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1027;
+tr3058:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1027;
+tr3040:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1027;
+st1027:
+ if ( ++p == pe )
+ goto _test_eof1027;
+case 1027:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3059;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3058;
+ goto tr75;
+tr3034:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1028;
+tr3060:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1028;
+tr3028:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1028;
+st1028:
+ if ( ++p == pe )
+ goto _test_eof1028;
+case 1028:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3061;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3060;
+ goto tr75;
+st1029:
+ if ( ++p == pe )
+ goto _test_eof1029;
+case 1029:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3062;
+ goto tr2327;
+tr3062:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1030;
+tr3066:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1030;
+st1030:
+ if ( ++p == pe )
+ goto _test_eof1030;
+case 1030:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3063;
+ case 32: goto tr3063;
+ case 40: goto tr3064;
+ case 41: goto tr3065;
+ case 2058: goto tr3067;
+ case 2107: goto tr3068;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3066;
+ goto tr2327;
+tr3071:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1031;
+tr3072:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1031;
+tr3077:
+ {
+ s->line_counter++;
+ }
+ goto st1031;
+tr3270:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1031; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1031;
+tr3063:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1031;
+tr3064:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1031;
+tr3065:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1031;
+tr3067:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1031;
+st1031:
+ if ( ++p == pe )
+ goto _test_eof1031;
+case 1031:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1031;
+ case 32: goto st1031;
+ case 40: goto tr3071;
+ case 41: goto tr3072;
+ case 48: goto tr3073;
+ case 49: goto tr3074;
+ case 50: goto tr3075;
+ case 51: goto tr3076;
+ case 2058: goto tr3077;
+ case 2107: goto tr3078;
+ }
+ goto tr3069;
+tr3073:
+ {
+ *(rdata_tail++) = 0;
+ }
+ goto st1032;
+st1032:
+ if ( ++p == pe )
+ goto _test_eof1032;
+case 1032:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1033;
+ case 32: goto st1033;
+ case 40: goto tr3080;
+ case 41: goto tr3081;
+ case 2058: goto tr3082;
+ case 2107: goto tr3083;
+ }
+ goto tr3069;
+tr3080:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1033;
+tr3081:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1033;
+tr3082:
+ {
+ s->line_counter++;
+ }
+ goto st1033;
+tr3132:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1033; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1033;
+st1033:
+ if ( ++p == pe )
+ goto _test_eof1033;
+case 1033:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1033;
+ case 32: goto st1033;
+ case 40: goto tr3080;
+ case 41: goto tr3081;
+ case 2058: goto tr3082;
+ case 2107: goto tr3083;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3085;
+ goto tr3084;
+tr3085:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1034;
+tr3089:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1034;
+st1034:
+ if ( ++p == pe )
+ goto _test_eof1034;
+case 1034:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3086;
+ case 32: goto tr3086;
+ case 40: goto tr3087;
+ case 41: goto tr3088;
+ case 2058: goto tr3090;
+ case 2107: goto tr3091;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3089;
+ goto tr3084;
+tr3093:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1035;
+tr3094:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1035;
+tr3096:
+ {
+ s->line_counter++;
+ }
+ goto st1035;
+tr3130:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1035; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1035;
+tr3086:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1035;
+tr3087:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1035;
+tr3088:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1035;
+tr3090:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1035;
+st1035:
+ if ( ++p == pe )
+ goto _test_eof1035;
+case 1035:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1035;
+ case 32: goto st1035;
+ case 40: goto tr3093;
+ case 41: goto tr3094;
+ case 46: goto st1036;
+ case 2058: goto tr3096;
+ case 2107: goto tr3097;
+ }
+ goto tr3069;
+st1036:
+ if ( ++p == pe )
+ goto _test_eof1036;
+case 1036:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 7177: goto st1037;
+ case 7200: goto st1037;
+ case 7208: goto tr3100;
+ case 7209: goto tr3101;
+ case 7433: goto st1040;
+ case 7456: goto st1040;
+ case 7464: goto tr3103;
+ case 7465: goto tr3104;
+ case 7689: goto st1042;
+ case 7712: goto st1042;
+ case 7720: goto tr3106;
+ case 7721: goto tr3107;
+ case 10506: goto tr3108;
+ case 10555: goto tr3108;
+ case 10762: goto tr3108;
+ case 10811: goto tr3108;
+ case 11274: goto tr3109;
+ case 11323: goto tr3110;
+ case 11530: goto tr3108;
+ case 11579: goto tr3108;
+ case 11786: goto tr3111;
+ case 11835: goto tr3112;
+ case 12298: goto tr3113;
+ case 12347: goto tr3114;
+ case 12554: goto tr3108;
+ case 12603: goto tr3108;
+ case 12810: goto tr3115;
+ case 12859: goto tr3116;
+ case 13322: goto tr3117;
+ case 13371: goto tr3118;
+ case 13578: goto tr3108;
+ case 13627: goto tr3108;
+ case 13834: goto tr3119;
+ case 13883: goto tr3120;
+ }
+ goto tr3098;
+tr3100:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1037;
+tr3101:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1037;
+tr3109:
+ {
+ s->line_counter++;
+ }
+ goto st1037;
+tr3124:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1037; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1037;
+tr3154:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ goto st1037;
+tr3199:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ goto st1037;
+tr3243:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st1037;
+tr3244:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1037;
+tr3245:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1037;
+tr3253:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1037;
+tr3155:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1037;
+tr3156:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1037;
+tr3164:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1037;
+tr3200:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1037;
+tr3201:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1037;
+tr3209:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1037;
+st1037:
+ if ( ++p == pe )
+ goto _test_eof1037;
+case 1037:
+ _widec = (*p);
+ if ( (*p) < 43 ) {
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) > 40 ) {
+ if ( 41 <= (*p) && (*p) <= 41 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 40 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 43 ) {
+ if ( (*p) < 59 ) {
+ if ( (*p) > 47 ) {
+ if ( 48 <= (*p) && (*p) <= 57 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 47 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 59 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 65 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 4105: goto st1037;
+ case 4128: goto st1037;
+ case 4136: goto tr3100;
+ case 4137: goto tr3101;
+ case 4139: goto tr3122;
+ case 5130: goto tr3109;
+ case 5179: goto tr3110;
+ }
+ if ( _widec < 4161 ) {
+ if ( 4143 <= _widec && _widec <= 4153 )
+ goto tr3122;
+ } else if ( _widec > 4186 ) {
+ if ( 4193 <= _widec && _widec <= 4218 )
+ goto tr3122;
+ } else
+ goto tr3122;
+ goto tr3121;
+tr3122:
+ { p--; {stack[top++] = 1038;goto st369;} }
+ goto st1038;
+st1038:
+ if ( ++p == pe )
+ goto _test_eof1038;
+case 1038:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 2570: goto tr3108;
+ case 2619: goto tr3108;
+ }
+ goto tr3121;
+tr3108:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1466;
+tr3163:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1466;
+tr3252:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1466;
+tr3208:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1466;
+st1466:
+ if ( ++p == pe )
+ goto _test_eof1466;
+case 1466:
+ goto st0;
+tr3110:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1039;
+tr3123:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1039;
+tr3254:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1039;
+tr3165:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1039;
+tr3210:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1039;
+st1039:
+ if ( ++p == pe )
+ goto _test_eof1039;
+case 1039:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ if ( _widec == 5130 )
+ goto tr3124;
+ if ( 4992 <= _widec && _widec <= 5247 )
+ goto tr3123;
+ goto tr3121;
+tr3103:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1040;
+tr3104:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1040;
+tr3113:
+ {
+ s->line_counter++;
+ }
+ goto st1040;
+tr3126:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1040; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1040;
+tr3157:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ goto st1040;
+tr3202:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ goto st1040;
+tr3246:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st1040;
+tr3247:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1040;
+tr3248:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1040;
+tr3257:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1040;
+tr3158:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1040;
+tr3159:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1040;
+tr3168:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1040;
+tr3203:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1040;
+tr3204:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1040;
+tr3213:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1040;
+st1040:
+ if ( ++p == pe )
+ goto _test_eof1040;
+case 1040:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(13952 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(13952 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 5641: goto st1040;
+ case 5664: goto st1040;
+ case 5672: goto tr3103;
+ case 5673: goto tr3104;
+ case 14602: goto tr3108;
+ case 14651: goto tr3108;
+ case 14858: goto tr3108;
+ case 14907: goto tr3108;
+ case 15370: goto tr3113;
+ case 15419: goto tr3114;
+ case 15626: goto tr3108;
+ case 15675: goto tr3108;
+ case 15882: goto tr3115;
+ case 15931: goto tr3116;
+ }
+ goto tr3121;
+tr3114:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1041;
+tr3125:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1041;
+tr3258:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1041;
+tr3169:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1041;
+tr3214:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1041;
+st1041:
+ if ( ++p == pe )
+ goto _test_eof1041;
+case 1041:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ if ( _widec == 6666 )
+ goto tr3126;
+ if ( 6528 <= _widec && _widec <= 6783 )
+ goto tr3125;
+ goto tr3121;
+tr3115:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1467;
+tr3170:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1467;
+tr3215:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1467;
+tr3259:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1467;
+st1467:
+ if ( ++p == pe )
+ goto _test_eof1467;
+case 1467:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(13952 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(13952 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 5641: goto st1040;
+ case 5664: goto st1040;
+ case 5672: goto tr3103;
+ case 5673: goto tr3104;
+ case 14602: goto tr3108;
+ case 14651: goto tr3108;
+ case 14858: goto tr3108;
+ case 14907: goto tr3108;
+ case 15370: goto tr3113;
+ case 15419: goto tr3114;
+ case 15626: goto tr3108;
+ case 15675: goto tr3108;
+ case 15882: goto tr3115;
+ case 15931: goto tr3116;
+ }
+ goto tr3121;
+tr3116:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1468;
+tr3171:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1468;
+tr3216:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1468;
+tr3260:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1468;
+st1468:
+ if ( ++p == pe )
+ goto _test_eof1468;
+case 1468:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ if ( _widec == 6666 )
+ goto tr3126;
+ if ( 6528 <= _widec && _widec <= 6783 )
+ goto tr3125;
+ goto tr3121;
+tr3106:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1042;
+tr3107:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1042;
+tr3117:
+ {
+ s->line_counter++;
+ }
+ goto st1042;
+tr3128:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1042; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1042;
+tr3160:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ goto st1042;
+tr3205:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ goto st1042;
+tr3249:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st1042;
+tr3250:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1042;
+tr3251:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1042;
+tr3261:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1042;
+tr3161:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1042;
+tr3162:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1042;
+tr3172:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1042;
+tr3206:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1042;
+tr3207:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1042;
+tr3217:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1042;
+st1042:
+ if ( ++p == pe )
+ goto _test_eof1042;
+case 1042:
+ _widec = (*p);
+ if ( (*p) < 43 ) {
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) > 40 ) {
+ if ( 41 <= (*p) && (*p) <= 41 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) >= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 43 ) {
+ if ( (*p) < 59 ) {
+ if ( (*p) > 47 ) {
+ if ( 48 <= (*p) && (*p) <= 57 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 47 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 59 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 65 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 4139: goto tr3122;
+ case 7177: goto st1037;
+ case 7200: goto st1037;
+ case 7208: goto tr3100;
+ case 7209: goto tr3101;
+ case 7433: goto st1040;
+ case 7456: goto st1040;
+ case 7464: goto tr3103;
+ case 7465: goto tr3104;
+ case 7689: goto st1042;
+ case 7712: goto st1042;
+ case 7720: goto tr3106;
+ case 7721: goto tr3107;
+ case 10506: goto tr3108;
+ case 10555: goto tr3108;
+ case 10762: goto tr3108;
+ case 10811: goto tr3108;
+ case 11274: goto tr3109;
+ case 11323: goto tr3110;
+ case 11530: goto tr3108;
+ case 11579: goto tr3108;
+ case 11786: goto tr3111;
+ case 11835: goto tr3112;
+ case 12298: goto tr3113;
+ case 12347: goto tr3114;
+ case 12554: goto tr3108;
+ case 12603: goto tr3108;
+ case 12810: goto tr3115;
+ case 12859: goto tr3116;
+ case 13322: goto tr3117;
+ case 13371: goto tr3118;
+ case 13578: goto tr3108;
+ case 13627: goto tr3108;
+ case 13834: goto tr3119;
+ case 13883: goto tr3120;
+ }
+ if ( _widec < 4161 ) {
+ if ( 4143 <= _widec && _widec <= 4153 )
+ goto tr3122;
+ } else if ( _widec > 4186 ) {
+ if ( 4193 <= _widec && _widec <= 4218 )
+ goto tr3122;
+ } else
+ goto tr3122;
+ goto tr3121;
+tr3111:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1469;
+tr3166:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1469;
+tr3211:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1469;
+tr3255:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1469;
+st1469:
+ if ( ++p == pe )
+ goto _test_eof1469;
+case 1469:
+ _widec = (*p);
+ if ( (*p) < 43 ) {
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) > 40 ) {
+ if ( 41 <= (*p) && (*p) <= 41 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 40 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 43 ) {
+ if ( (*p) < 59 ) {
+ if ( (*p) > 47 ) {
+ if ( 48 <= (*p) && (*p) <= 57 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 47 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 59 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 65 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 4105: goto st1037;
+ case 4128: goto st1037;
+ case 4136: goto tr3100;
+ case 4137: goto tr3101;
+ case 4139: goto tr3122;
+ case 5130: goto tr3109;
+ case 5179: goto tr3110;
+ }
+ if ( _widec < 4161 ) {
+ if ( 4143 <= _widec && _widec <= 4153 )
+ goto tr3122;
+ } else if ( _widec > 4186 ) {
+ if ( 4193 <= _widec && _widec <= 4218 )
+ goto tr3122;
+ } else
+ goto tr3122;
+ goto tr3121;
+tr3112:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1470;
+tr3167:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1470;
+tr3212:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1470;
+tr3256:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1470;
+st1470:
+ if ( ++p == pe )
+ goto _test_eof1470;
+case 1470:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ }
+ if ( _widec == 5130 )
+ goto tr3124;
+ if ( 4992 <= _widec && _widec <= 5247 )
+ goto tr3123;
+ goto tr3121;
+tr3118:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1043;
+tr3127:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1043;
+tr3262:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1043;
+tr3173:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1043;
+tr3218:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1043;
+st1043:
+ if ( ++p == pe )
+ goto _test_eof1043;
+case 1043:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else {
+ _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ switch( _widec ) {
+ case 8714: goto tr3124;
+ case 9226: goto tr3126;
+ case 9738: goto tr3128;
+ }
+ if ( _widec < 9088 ) {
+ if ( 8576 <= _widec && _widec <= 8831 )
+ goto tr3123;
+ } else if ( _widec > 9343 ) {
+ if ( 9600 <= _widec && _widec <= 9855 )
+ goto tr3127;
+ } else
+ goto tr3125;
+ goto tr3121;
+tr3119:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1471;
+tr3174:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1471;
+tr3219:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1471;
+tr3263:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1471;
+st1471:
+ if ( ++p == pe )
+ goto _test_eof1471;
+case 1471:
+ _widec = (*p);
+ if ( (*p) < 43 ) {
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) > 40 ) {
+ if ( 41 <= (*p) && (*p) <= 41 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) >= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 43 ) {
+ if ( (*p) < 59 ) {
+ if ( (*p) > 47 ) {
+ if ( 48 <= (*p) && (*p) <= 57 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 47 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) > 59 ) {
+ if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else if ( (*p) >= 65 ) {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 4139: goto tr3122;
+ case 7177: goto st1037;
+ case 7200: goto st1037;
+ case 7208: goto tr3100;
+ case 7209: goto tr3101;
+ case 7433: goto st1040;
+ case 7456: goto st1040;
+ case 7464: goto tr3103;
+ case 7465: goto tr3104;
+ case 7689: goto st1042;
+ case 7712: goto st1042;
+ case 7720: goto tr3106;
+ case 7721: goto tr3107;
+ case 10506: goto tr3108;
+ case 10555: goto tr3108;
+ case 10762: goto tr3108;
+ case 10811: goto tr3108;
+ case 11274: goto tr3109;
+ case 11323: goto tr3110;
+ case 11530: goto tr3108;
+ case 11579: goto tr3108;
+ case 11786: goto tr3111;
+ case 11835: goto tr3112;
+ case 12298: goto tr3113;
+ case 12347: goto tr3114;
+ case 12554: goto tr3108;
+ case 12603: goto tr3108;
+ case 12810: goto tr3115;
+ case 12859: goto tr3116;
+ case 13322: goto tr3117;
+ case 13371: goto tr3118;
+ case 13578: goto tr3108;
+ case 13627: goto tr3108;
+ case 13834: goto tr3119;
+ case 13883: goto tr3120;
+ }
+ if ( _widec < 4161 ) {
+ if ( 4143 <= _widec && _widec <= 4153 )
+ goto tr3122;
+ } else if ( _widec > 4186 ) {
+ if ( 4193 <= _widec && _widec <= 4218 )
+ goto tr3122;
+ } else
+ goto tr3122;
+ goto tr3121;
+tr3120:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1472;
+tr3175:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1472;
+tr3220:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1472;
+tr3264:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1472;
+st1472:
+ if ( ++p == pe )
+ goto _test_eof1472;
+case 1472:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ } else {
+ _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ }
+ switch( _widec ) {
+ case 8714: goto tr3124;
+ case 9226: goto tr3126;
+ case 9738: goto tr3128;
+ }
+ if ( _widec < 9088 ) {
+ if ( 8576 <= _widec && _widec <= 8831 )
+ goto tr3123;
+ } else if ( _widec > 9343 ) {
+ if ( 9600 <= _widec && _widec <= 9855 )
+ goto tr3127;
+ } else
+ goto tr3125;
+ goto tr3121;
+tr3097:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1044;
+tr3129:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1044;
+tr3091:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1044;
+st1044:
+ if ( ++p == pe )
+ goto _test_eof1044;
+case 1044:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3130;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3129;
+ goto tr3069;
+tr3083:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1045;
+tr3131:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1045;
+st1045:
+ if ( ++p == pe )
+ goto _test_eof1045;
+case 1045:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3132;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3131;
+ goto tr3069;
+tr3074:
+ {
+ *(rdata_tail++) = 1;
+ }
+ goto st1046;
+st1046:
+ if ( ++p == pe )
+ goto _test_eof1046;
+case 1046:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1047;
+ case 32: goto st1047;
+ case 40: goto tr3134;
+ case 41: goto tr3135;
+ case 2058: goto tr3136;
+ case 2107: goto tr3137;
+ }
+ goto tr3069;
+tr3134:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1047;
+tr3135:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1047;
+tr3136:
+ {
+ s->line_counter++;
+ }
+ goto st1047;
+tr3179:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1047; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1047;
+st1047:
+ if ( ++p == pe )
+ goto _test_eof1047;
+case 1047:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1047;
+ case 32: goto st1047;
+ case 40: goto tr3134;
+ case 41: goto tr3135;
+ case 2058: goto tr3136;
+ case 2107: goto tr3137;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3138;
+ goto tr3084;
+tr3138:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1048;
+tr3142:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1048;
+st1048:
+ if ( ++p == pe )
+ goto _test_eof1048;
+case 1048:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3139;
+ case 32: goto tr3139;
+ case 40: goto tr3140;
+ case 41: goto tr3141;
+ case 2058: goto tr3143;
+ case 2107: goto tr3144;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3142;
+ goto tr3084;
+tr3147:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1049;
+tr3148:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1049;
+tr3150:
+ {
+ s->line_counter++;
+ }
+ goto st1049;
+tr3177:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1049; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1049;
+tr3139:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1049;
+tr3140:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1049;
+tr3141:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1049;
+tr3143:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1049;
+st1049:
+ if ( ++p == pe )
+ goto _test_eof1049;
+case 1049:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1049;
+ case 32: goto st1049;
+ case 40: goto tr3147;
+ case 41: goto tr3148;
+ case 46: goto tr3149;
+ case 2058: goto tr3150;
+ case 2107: goto tr3151;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3149;
+ goto tr3145;
+tr3153:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1050;
+tr3149:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1050;
+st1050:
+ if ( ++p == pe )
+ goto _test_eof1050;
+case 1050:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 46: goto tr3153;
+ case 7177: goto tr3154;
+ case 7200: goto tr3154;
+ case 7208: goto tr3155;
+ case 7209: goto tr3156;
+ case 7433: goto tr3157;
+ case 7456: goto tr3157;
+ case 7464: goto tr3158;
+ case 7465: goto tr3159;
+ case 7689: goto tr3160;
+ case 7712: goto tr3160;
+ case 7720: goto tr3161;
+ case 7721: goto tr3162;
+ case 10506: goto tr3163;
+ case 10555: goto tr3163;
+ case 10762: goto tr3163;
+ case 10811: goto tr3163;
+ case 11274: goto tr3164;
+ case 11323: goto tr3165;
+ case 11530: goto tr3163;
+ case 11579: goto tr3163;
+ case 11786: goto tr3166;
+ case 11835: goto tr3167;
+ case 12298: goto tr3168;
+ case 12347: goto tr3169;
+ case 12554: goto tr3163;
+ case 12603: goto tr3163;
+ case 12810: goto tr3170;
+ case 12859: goto tr3171;
+ case 13322: goto tr3172;
+ case 13371: goto tr3173;
+ case 13578: goto tr3163;
+ case 13627: goto tr3163;
+ case 13834: goto tr3174;
+ case 13883: goto tr3175;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3153;
+ goto tr3152;
+tr3151:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1051;
+tr3176:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1051;
+tr3144:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1051;
+st1051:
+ if ( ++p == pe )
+ goto _test_eof1051;
+case 1051:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3177;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3176;
+ goto tr3069;
+tr3137:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1052;
+tr3178:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1052;
+st1052:
+ if ( ++p == pe )
+ goto _test_eof1052;
+case 1052:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3179;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3178;
+ goto tr3069;
+tr3075:
+ {
+ *(rdata_tail++) = 2;
+ }
+ goto st1053;
+st1053:
+ if ( ++p == pe )
+ goto _test_eof1053;
+case 1053:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1054;
+ case 32: goto st1054;
+ case 40: goto tr3181;
+ case 41: goto tr3182;
+ case 2058: goto tr3183;
+ case 2107: goto tr3184;
+ }
+ goto tr3069;
+tr3181:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1054;
+tr3182:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1054;
+tr3183:
+ {
+ s->line_counter++;
+ }
+ goto st1054;
+tr3224:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1054; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1054;
+st1054:
+ if ( ++p == pe )
+ goto _test_eof1054;
+case 1054:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1054;
+ case 32: goto st1054;
+ case 40: goto tr3181;
+ case 41: goto tr3182;
+ case 2058: goto tr3183;
+ case 2107: goto tr3184;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3185;
+ goto tr3084;
+tr3185:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1055;
+tr3189:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1055;
+st1055:
+ if ( ++p == pe )
+ goto _test_eof1055;
+case 1055:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3186;
+ case 32: goto tr3186;
+ case 40: goto tr3187;
+ case 41: goto tr3188;
+ case 2058: goto tr3190;
+ case 2107: goto tr3191;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3189;
+ goto tr3084;
+tr3193:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1056;
+tr3194:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1056;
+tr3196:
+ {
+ s->line_counter++;
+ }
+ goto st1056;
+tr3222:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1056; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1056;
+tr3186:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1056;
+tr3187:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1056;
+tr3188:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1056;
+tr3190:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1056;
+st1056:
+ if ( ++p == pe )
+ goto _test_eof1056;
+case 1056:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1056;
+ case 32: goto st1056;
+ case 40: goto tr3193;
+ case 41: goto tr3194;
+ case 46: goto tr3195;
+ case 2058: goto tr3196;
+ case 2107: goto tr3197;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 58 )
+ goto tr3195;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3195;
+ } else
+ goto tr3195;
+ goto tr3145;
+tr3198:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1057;
+tr3195:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1057;
+st1057:
+ if ( ++p == pe )
+ goto _test_eof1057;
+case 1057:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 46: goto tr3198;
+ case 7177: goto tr3199;
+ case 7200: goto tr3199;
+ case 7208: goto tr3200;
+ case 7209: goto tr3201;
+ case 7433: goto tr3202;
+ case 7456: goto tr3202;
+ case 7464: goto tr3203;
+ case 7465: goto tr3204;
+ case 7689: goto tr3205;
+ case 7712: goto tr3205;
+ case 7720: goto tr3206;
+ case 7721: goto tr3207;
+ case 10506: goto tr3208;
+ case 10555: goto tr3208;
+ case 10762: goto tr3208;
+ case 10811: goto tr3208;
+ case 11274: goto tr3209;
+ case 11323: goto tr3210;
+ case 11530: goto tr3208;
+ case 11579: goto tr3208;
+ case 11786: goto tr3211;
+ case 11835: goto tr3212;
+ case 12298: goto tr3213;
+ case 12347: goto tr3214;
+ case 12554: goto tr3208;
+ case 12603: goto tr3208;
+ case 12810: goto tr3215;
+ case 12859: goto tr3216;
+ case 13322: goto tr3217;
+ case 13371: goto tr3218;
+ case 13578: goto tr3208;
+ case 13627: goto tr3208;
+ case 13834: goto tr3219;
+ case 13883: goto tr3220;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 58 )
+ goto tr3198;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3198;
+ } else
+ goto tr3198;
+ goto tr3152;
+tr3197:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1058;
+tr3221:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1058;
+tr3191:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1058;
+st1058:
+ if ( ++p == pe )
+ goto _test_eof1058;
+case 1058:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3222;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3221;
+ goto tr3069;
+tr3184:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1059;
+tr3223:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1059;
+st1059:
+ if ( ++p == pe )
+ goto _test_eof1059;
+case 1059:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3224;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3223;
+ goto tr3069;
+tr3076:
+ {
+ *(rdata_tail++) = 3;
+ }
+ goto st1060;
+st1060:
+ if ( ++p == pe )
+ goto _test_eof1060;
+case 1060:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1061;
+ case 32: goto st1061;
+ case 40: goto tr3226;
+ case 41: goto tr3227;
+ case 2058: goto tr3228;
+ case 2107: goto tr3229;
+ }
+ goto tr3069;
+tr3226:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1061;
+tr3227:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1061;
+tr3228:
+ {
+ s->line_counter++;
+ }
+ goto st1061;
+tr3268:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1061; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1061;
+st1061:
+ if ( ++p == pe )
+ goto _test_eof1061;
+case 1061:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1061;
+ case 32: goto st1061;
+ case 40: goto tr3226;
+ case 41: goto tr3227;
+ case 2058: goto tr3228;
+ case 2107: goto tr3229;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3230;
+ goto tr3084;
+tr3230:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1062;
+tr3234:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1062;
+st1062:
+ if ( ++p == pe )
+ goto _test_eof1062;
+case 1062:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3231;
+ case 32: goto tr3231;
+ case 40: goto tr3232;
+ case 41: goto tr3233;
+ case 2058: goto tr3235;
+ case 2107: goto tr3236;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3234;
+ goto tr3084;
+tr3238:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1063;
+tr3239:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1063;
+tr3241:
+ {
+ s->line_counter++;
+ }
+ goto st1063;
+tr3266:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1063; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1063;
+tr3231:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1063;
+tr3232:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1063;
+tr3233:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1063;
+tr3235:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1063;
+st1063:
+ if ( ++p == pe )
+ goto _test_eof1063;
+case 1063:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1063;
+ case 32: goto st1063;
+ case 40: goto tr3238;
+ case 41: goto tr3239;
+ case 42: goto tr3240;
+ case 92: goto tr3240;
+ case 95: goto tr3240;
+ case 2058: goto tr3241;
+ case 2107: goto tr3242;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr3240;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3240;
+ } else
+ goto tr3240;
+ goto tr3069;
+tr3240:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 1064;goto st309;} }
+ goto st1064;
+st1064:
+ if ( ++p == pe )
+ goto _test_eof1064;
+case 1064:
+ _widec = (*p);
+ if ( (*p) < 32 ) {
+ if ( (*p) > 9 ) {
+ if ( 10 <= (*p) && (*p) <= 10 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else if ( (*p) >= 9 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 32 ) {
+ if ( (*p) < 41 ) {
+ if ( 40 <= (*p) && (*p) <= 40 ) {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else if ( (*p) > 41 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ } else {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 7177: goto tr3243;
+ case 7200: goto tr3243;
+ case 7208: goto tr3244;
+ case 7209: goto tr3245;
+ case 7433: goto tr3246;
+ case 7456: goto tr3246;
+ case 7464: goto tr3247;
+ case 7465: goto tr3248;
+ case 7689: goto tr3249;
+ case 7712: goto tr3249;
+ case 7720: goto tr3250;
+ case 7721: goto tr3251;
+ case 10506: goto tr3252;
+ case 10555: goto tr3252;
+ case 10762: goto tr3252;
+ case 10811: goto tr3252;
+ case 11274: goto tr3253;
+ case 11323: goto tr3254;
+ case 11530: goto tr3252;
+ case 11579: goto tr3252;
+ case 11786: goto tr3255;
+ case 11835: goto tr3256;
+ case 12298: goto tr3257;
+ case 12347: goto tr3258;
+ case 12554: goto tr3252;
+ case 12603: goto tr3252;
+ case 12810: goto tr3259;
+ case 12859: goto tr3260;
+ case 13322: goto tr3261;
+ case 13371: goto tr3262;
+ case 13578: goto tr3252;
+ case 13627: goto tr3252;
+ case 13834: goto tr3263;
+ case 13883: goto tr3264;
+ }
+ goto tr3098;
+tr3242:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1065;
+tr3265:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1065;
+tr3236:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1065;
+st1065:
+ if ( ++p == pe )
+ goto _test_eof1065;
+case 1065:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3266;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3265;
+ goto tr3069;
+tr3229:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1066;
+tr3267:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1066;
+st1066:
+ if ( ++p == pe )
+ goto _test_eof1066;
+case 1066:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3268;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3267;
+ goto tr3069;
+tr3078:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1067;
+tr3269:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1067;
+tr3068:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1067;
+st1067:
+ if ( ++p == pe )
+ goto _test_eof1067;
+case 1067:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3270;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3269;
+ goto tr75;
+st1068:
+ if ( ++p == pe )
+ goto _test_eof1068;
+case 1068:
+ switch( (*p) ) {
+ case 65: goto st1069;
+ case 67: goto st1103;
+ case 68: goto st1124;
+ case 69: goto st1138;
+ case 72: goto st1145;
+ case 73: goto st1154;
+ case 75: goto st1162;
+ case 76: goto st1166;
+ case 77: goto st1174;
+ case 78: goto st1180;
+ case 79: goto st1196;
+ case 80: goto st1206;
+ case 82: goto st1209;
+ case 83: goto st1216;
+ case 84: goto st1235;
+ case 85: goto st1245;
+ case 90: goto st1248;
+ case 97: goto st1069;
+ case 99: goto st1103;
+ case 100: goto st1124;
+ case 101: goto st1138;
+ case 104: goto st1145;
+ case 105: goto st1154;
+ case 107: goto st1162;
+ case 108: goto st1166;
+ case 109: goto st1174;
+ case 110: goto st1180;
+ case 111: goto st1196;
+ case 112: goto st1206;
+ case 114: goto st1209;
+ case 115: goto st1216;
+ case 116: goto st1235;
+ case 117: goto st1245;
+ case 122: goto st1248;
+ }
+ goto tr3271;
+st1069:
+ if ( ++p == pe )
+ goto _test_eof1069;
+case 1069:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3289;
+ case 32: goto tr3289;
+ case 40: goto tr3290;
+ case 41: goto tr3291;
+ case 65: goto st1094;
+ case 70: goto st1097;
+ case 80: goto st1101;
+ case 97: goto st1094;
+ case 102: goto st1097;
+ case 112: goto st1101;
+ case 2058: goto tr3295;
+ case 2107: goto tr3296;
+ }
+ goto tr3271;
+tr3298:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3299:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3301:
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3397:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1070; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3758:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1070;
+tr3759:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3760:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3762:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3289:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ goto st1070;
+tr3290:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3291:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3295:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3400:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ goto st1070;
+tr3401:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3402:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3403:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3408:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ goto st1070;
+tr3409:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3410:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3411:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3414:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ goto st1070;
+tr3415:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3416:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3417:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3425:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ goto st1070;
+tr3426:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3427:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3428:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3436:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ goto st1070;
+tr3437:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3438:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3439:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3441:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ goto st1070;
+tr3442:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3443:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3444:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3448:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ goto st1070;
+tr3449:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3450:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3451:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3456:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ goto st1070;
+tr3457:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3458:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3459:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3464:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ goto st1070;
+tr3465:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3466:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3467:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3475:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ goto st1070;
+tr3476:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3477:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3478:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3484:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ goto st1070;
+tr3485:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3486:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3487:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3492:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ goto st1070;
+tr3493:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3494:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3495:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3497:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ goto st1070;
+tr3498:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3499:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3500:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3507:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ goto st1070;
+tr3508:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3509:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3510:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3513:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ goto st1070;
+tr3514:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3515:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3516:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3523:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ goto st1070;
+tr3524:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3525:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3526:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3531:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ goto st1070;
+tr3532:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3533:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3534:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3543:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ goto st1070;
+tr3544:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3545:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3546:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3551:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ goto st1070;
+tr3552:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3553:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3554:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3556:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ goto st1070;
+tr3557:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3558:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3559:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3566:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ goto st1070;
+tr3567:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3568:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3569:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3572:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ goto st1070;
+tr3573:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3574:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3575:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3578:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ goto st1070;
+tr3579:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3580:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3581:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3583:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ goto st1070;
+tr3584:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3585:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3586:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3593:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ goto st1070;
+tr3594:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3595:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3596:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3598:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ goto st1070;
+tr3599:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3600:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3601:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3609:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ goto st1070;
+tr3610:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3611:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3612:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3615:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ goto st1070;
+tr3616:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3617:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3618:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3620:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ goto st1070;
+tr3621:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3622:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3624:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3627:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ goto st1070;
+tr3628:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3629:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3631:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3633:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ goto st1070;
+tr3634:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3635:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3637:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3643:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ goto st1070;
+tr3644:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3645:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3646:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3657:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ goto st1070;
+tr3658:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3659:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3660:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3664:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ goto st1070;
+tr3665:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3666:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3667:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3672:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ goto st1070;
+tr3673:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3674:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3675:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3680:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ goto st1070;
+tr3681:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3682:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3683:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3685:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ goto st1070;
+tr3686:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3687:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3688:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3700:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ goto st1070;
+tr3701:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3702:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3703:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3706:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ goto st1070;
+tr3707:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3708:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3709:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3712:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ goto st1070;
+tr3713:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3714:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3715:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3718:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ goto st1070;
+tr3719:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3720:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3721:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3726:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ goto st1070;
+tr3727:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3728:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3729:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3733:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ goto st1070;
+tr3734:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3735:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3736:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3743:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ goto st1070;
+tr3744:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3745:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3746:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3749:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ goto st1070;
+tr3750:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3751:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3752:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3766:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ goto st1070;
+tr3767:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3768:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3769:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+tr3776:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ goto st1070;
+tr3777:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1070;
+tr3778:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1070;
+tr3779:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ {
+ s->line_counter++;
+ }
+ goto st1070;
+st1070:
+ if ( ++p == pe )
+ goto _test_eof1070;
+case 1070:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1070;
+ case 32: goto st1070;
+ case 40: goto tr3298;
+ case 41: goto tr3299;
+ case 2058: goto tr3301;
+ case 2107: goto tr3302;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3300;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3300;
+ } else
+ goto tr3300;
+ goto tr75;
+tr3300:
+ { p--; {stack[top++] = 1071;goto st699;} }
+ goto st1071;
+st1071:
+ if ( ++p == pe )
+ goto _test_eof1071;
+case 1071:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1072;
+ case 32: goto st1072;
+ case 40: goto tr3304;
+ case 41: goto tr3305;
+ case 2058: goto tr3306;
+ case 2107: goto tr3307;
+ }
+ goto tr75;
+tr3304:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1072;
+tr3305:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1072;
+tr3306:
+ {
+ s->line_counter++;
+ }
+ goto st1072;
+tr3395:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1072; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1072;
+st1072:
+ if ( ++p == pe )
+ goto _test_eof1072;
+case 1072:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1072;
+ case 32: goto st1072;
+ case 40: goto tr3304;
+ case 41: goto tr3305;
+ case 2058: goto tr3306;
+ case 2107: goto tr3307;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3308;
+ goto tr2327;
+tr3308:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1073;
+tr3312:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1073;
+st1073:
+ if ( ++p == pe )
+ goto _test_eof1073;
+case 1073:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3309;
+ case 32: goto tr3309;
+ case 40: goto tr3310;
+ case 41: goto tr3311;
+ case 2058: goto tr3313;
+ case 2107: goto tr3314;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3312;
+ goto tr2327;
+tr3316:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1074;
+tr3317:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1074;
+tr3319:
+ {
+ s->line_counter++;
+ }
+ goto st1074;
+tr3393:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1074; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1074;
+tr3309:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1074;
+tr3310:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1074;
+tr3311:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1074;
+tr3313:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1074;
+st1074:
+ if ( ++p == pe )
+ goto _test_eof1074;
+case 1074:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1074;
+ case 32: goto st1074;
+ case 40: goto tr3316;
+ case 41: goto tr3317;
+ case 2058: goto tr3319;
+ case 2107: goto tr3320;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3318;
+ goto tr2327;
+tr3318:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1075;
+tr3324:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1075;
+st1075:
+ if ( ++p == pe )
+ goto _test_eof1075;
+case 1075:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3321;
+ case 32: goto tr3321;
+ case 40: goto tr3322;
+ case 41: goto tr3323;
+ case 2058: goto tr3325;
+ case 2107: goto tr3326;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3324;
+ goto tr2327;
+tr3329:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1076;
+tr3330:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1076;
+tr3332:
+ {
+ s->line_counter++;
+ }
+ goto st1076;
+tr3391:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1076; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1076;
+tr3321:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1076;
+tr3322:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1076;
+tr3323:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1076;
+tr3325:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1076;
+st1076:
+ if ( ++p == pe )
+ goto _test_eof1076;
+case 1076:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1076;
+ case 32: goto st1076;
+ case 40: goto tr3329;
+ case 41: goto tr3330;
+ case 2058: goto tr3332;
+ case 2107: goto tr3333;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3331;
+ goto tr3327;
+tr3331:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1077;
+tr3337:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1077;
+st1077:
+ if ( ++p == pe )
+ goto _test_eof1077;
+case 1077:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3334;
+ case 32: goto tr3334;
+ case 40: goto tr3335;
+ case 41: goto tr3336;
+ case 2058: goto tr3338;
+ case 2107: goto tr3339;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3337;
+ goto tr3327;
+tr3341:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1078;
+tr3342:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1078;
+tr3344:
+ {
+ s->line_counter++;
+ }
+ goto st1078;
+tr3389:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1078; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1078;
+tr3334:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ goto st1078;
+tr3335:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1078;
+tr3336:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1078;
+tr3338:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1078;
+st1078:
+ if ( ++p == pe )
+ goto _test_eof1078;
+case 1078:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1078;
+ case 32: goto st1078;
+ case 40: goto tr3341;
+ case 41: goto tr3342;
+ case 2058: goto tr3344;
+ case 2107: goto tr3345;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3343;
+ goto tr3327;
+tr3343:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1079;
+tr3349:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1079;
+st1079:
+ if ( ++p == pe )
+ goto _test_eof1079;
+case 1079:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3346;
+ case 32: goto tr3346;
+ case 40: goto tr3347;
+ case 41: goto tr3348;
+ case 2058: goto tr3350;
+ case 2107: goto tr3351;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3349;
+ goto tr3327;
+tr3353:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1080;
+tr3354:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1080;
+tr3356:
+ {
+ s->line_counter++;
+ }
+ goto st1080;
+tr3387:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1080; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1080;
+tr3346:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ goto st1080;
+tr3347:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1080;
+tr3348:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1080;
+tr3350:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1080;
+st1080:
+ if ( ++p == pe )
+ goto _test_eof1080;
+case 1080:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1080;
+ case 32: goto st1080;
+ case 40: goto tr3353;
+ case 41: goto tr3354;
+ case 2058: goto tr3356;
+ case 2107: goto tr3357;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3355;
+ goto tr2327;
+tr3355:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1081;
+tr3361:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1081;
+st1081:
+ if ( ++p == pe )
+ goto _test_eof1081;
+case 1081:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3358;
+ case 32: goto tr3358;
+ case 40: goto tr3359;
+ case 41: goto tr3360;
+ case 2058: goto tr3362;
+ case 2107: goto tr3363;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3361;
+ goto tr2327;
+tr3365:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1082;
+tr3366:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1082;
+tr3368:
+ {
+ s->line_counter++;
+ }
+ goto st1082;
+tr3385:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1082; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1082;
+tr3358:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1082;
+tr3359:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1082;
+tr3360:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1082;
+tr3362:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1082;
+st1082:
+ if ( ++p == pe )
+ goto _test_eof1082;
+case 1082:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1082;
+ case 32: goto st1082;
+ case 40: goto tr3365;
+ case 41: goto tr3366;
+ case 42: goto tr3367;
+ case 92: goto tr3367;
+ case 95: goto tr3367;
+ case 2058: goto tr3368;
+ case 2107: goto tr3369;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr3367;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3367;
+ } else
+ goto tr3367;
+ goto tr75;
+tr3367:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 1083;goto st309;} }
+ goto st1083;
+st1083:
+ if ( ++p == pe )
+ goto _test_eof1083;
+case 1083:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3370;
+ case 32: goto tr3370;
+ case 40: goto tr3371;
+ case 41: goto tr3372;
+ case 2058: goto tr3373;
+ case 2107: goto tr3374;
+ }
+ goto tr75;
+tr3376:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1084;
+tr3377:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1084;
+tr3379:
+ {
+ s->line_counter++;
+ }
+ goto st1084;
+tr3383:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1084; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1084;
+tr3370:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ goto st1084;
+tr3371:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1084;
+tr3372:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1084;
+tr3373:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1084;
+st1084:
+ if ( ++p == pe )
+ goto _test_eof1084;
+case 1084:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1084;
+ case 32: goto st1084;
+ case 40: goto tr3376;
+ case 41: goto tr3377;
+ case 43: goto tr3378;
+ case 2058: goto tr3379;
+ case 2107: goto tr3380;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr3378;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3378;
+ } else
+ goto tr3378;
+ goto tr75;
+tr3378:
+ { p--; {stack[top++] = 1085;goto st369;} }
+ goto st1085;
+st1085:
+ if ( ++p == pe )
+ goto _test_eof1085;
+case 1085:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 2570: goto tr3381;
+ case 2619: goto tr3381;
+ }
+ goto tr75;
+tr3381:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1473;
+st1473:
+ if ( ++p == pe )
+ goto _test_eof1473;
+case 1473:
+ goto st0;
+tr3380:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1086;
+tr3382:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1086;
+tr3374:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1086;
+st1086:
+ if ( ++p == pe )
+ goto _test_eof1086;
+case 1086:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3383;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3382;
+ goto tr75;
+tr3369:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1087;
+tr3384:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1087;
+tr3363:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1087;
+st1087:
+ if ( ++p == pe )
+ goto _test_eof1087;
+case 1087:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3385;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3384;
+ goto tr75;
+tr3357:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1088;
+tr3386:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1088;
+tr3351:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1088;
+st1088:
+ if ( ++p == pe )
+ goto _test_eof1088;
+case 1088:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3387;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3386;
+ goto tr75;
+tr3345:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1089;
+tr3388:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1089;
+tr3339:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {goto st307;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {goto st307;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1089;
+st1089:
+ if ( ++p == pe )
+ goto _test_eof1089;
+case 1089:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3389;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3388;
+ goto tr75;
+tr3333:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1090;
+tr3390:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1090;
+tr3326:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1090;
+st1090:
+ if ( ++p == pe )
+ goto _test_eof1090;
+case 1090:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3391;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3390;
+ goto tr75;
+tr3320:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1091;
+tr3392:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1091;
+tr3314:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1091;
+st1091:
+ if ( ++p == pe )
+ goto _test_eof1091;
+case 1091:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3393;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3392;
+ goto tr75;
+tr3307:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1092;
+tr3394:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1092;
+st1092:
+ if ( ++p == pe )
+ goto _test_eof1092;
+case 1092:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3395;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3394;
+ goto tr75;
+tr3302:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3396:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1093;
+tr3763:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3296:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3404:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3412:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3418:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3429:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3440:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3445:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3452:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3460:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3468:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3479:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3488:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3496:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3501:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3511:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3517:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3527:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3535:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3547:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3555:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3560:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3570:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3576:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3582:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3587:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3597:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3602:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3613:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3619:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3625:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3632:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3638:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3647:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3661:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3668:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3676:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3684:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3689:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3704:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3710:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3716:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3722:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3730:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3737:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3747:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3753:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3770:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+tr3780:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1093;
+st1093:
+ if ( ++p == pe )
+ goto _test_eof1093;
+case 1093:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3397;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3396;
+ goto tr75;
+st1094:
+ if ( ++p == pe )
+ goto _test_eof1094;
+case 1094:
+ switch( (*p) ) {
+ case 65: goto st1095;
+ case 97: goto st1095;
+ }
+ goto tr3271;
+st1095:
+ if ( ++p == pe )
+ goto _test_eof1095;
+case 1095:
+ switch( (*p) ) {
+ case 65: goto st1096;
+ case 97: goto st1096;
+ }
+ goto tr3271;
+st1096:
+ if ( ++p == pe )
+ goto _test_eof1096;
+case 1096:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3400;
+ case 32: goto tr3400;
+ case 40: goto tr3401;
+ case 41: goto tr3402;
+ case 2058: goto tr3403;
+ case 2107: goto tr3404;
+ }
+ goto tr3271;
+st1097:
+ if ( ++p == pe )
+ goto _test_eof1097;
+case 1097:
+ switch( (*p) ) {
+ case 83: goto st1098;
+ case 115: goto st1098;
+ }
+ goto tr3271;
+st1098:
+ if ( ++p == pe )
+ goto _test_eof1098;
+case 1098:
+ switch( (*p) ) {
+ case 68: goto st1099;
+ case 100: goto st1099;
+ }
+ goto tr3271;
+st1099:
+ if ( ++p == pe )
+ goto _test_eof1099;
+case 1099:
+ switch( (*p) ) {
+ case 66: goto st1100;
+ case 98: goto st1100;
+ }
+ goto tr3271;
+st1100:
+ if ( ++p == pe )
+ goto _test_eof1100;
+case 1100:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3408;
+ case 32: goto tr3408;
+ case 40: goto tr3409;
+ case 41: goto tr3410;
+ case 2058: goto tr3411;
+ case 2107: goto tr3412;
+ }
+ goto tr3271;
+st1101:
+ if ( ++p == pe )
+ goto _test_eof1101;
+case 1101:
+ switch( (*p) ) {
+ case 76: goto st1102;
+ case 108: goto st1102;
+ }
+ goto tr3271;
+st1102:
+ if ( ++p == pe )
+ goto _test_eof1102;
+case 1102:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3414;
+ case 32: goto tr3414;
+ case 40: goto tr3415;
+ case 41: goto tr3416;
+ case 2058: goto tr3417;
+ case 2107: goto tr3418;
+ }
+ goto tr3271;
+st1103:
+ if ( ++p == pe )
+ goto _test_eof1103;
+case 1103:
+ switch( (*p) ) {
+ case 65: goto st1104;
+ case 68: goto st1106;
+ case 69: goto st1113;
+ case 78: goto st1116;
+ case 83: goto st1120;
+ case 97: goto st1104;
+ case 100: goto st1106;
+ case 101: goto st1113;
+ case 110: goto st1116;
+ case 115: goto st1120;
+ }
+ goto tr3271;
+st1104:
+ if ( ++p == pe )
+ goto _test_eof1104;
+case 1104:
+ switch( (*p) ) {
+ case 65: goto st1105;
+ case 97: goto st1105;
+ }
+ goto tr3271;
+st1105:
+ if ( ++p == pe )
+ goto _test_eof1105;
+case 1105:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3425;
+ case 32: goto tr3425;
+ case 40: goto tr3426;
+ case 41: goto tr3427;
+ case 2058: goto tr3428;
+ case 2107: goto tr3429;
+ }
+ goto tr3271;
+st1106:
+ if ( ++p == pe )
+ goto _test_eof1106;
+case 1106:
+ switch( (*p) ) {
+ case 78: goto st1107;
+ case 83: goto st1112;
+ case 110: goto st1107;
+ case 115: goto st1112;
+ }
+ goto tr3271;
+st1107:
+ if ( ++p == pe )
+ goto _test_eof1107;
+case 1107:
+ switch( (*p) ) {
+ case 83: goto st1108;
+ case 115: goto st1108;
+ }
+ goto tr3271;
+st1108:
+ if ( ++p == pe )
+ goto _test_eof1108;
+case 1108:
+ switch( (*p) ) {
+ case 75: goto st1109;
+ case 107: goto st1109;
+ }
+ goto tr3271;
+st1109:
+ if ( ++p == pe )
+ goto _test_eof1109;
+case 1109:
+ switch( (*p) ) {
+ case 69: goto st1110;
+ case 101: goto st1110;
+ }
+ goto tr3271;
+st1110:
+ if ( ++p == pe )
+ goto _test_eof1110;
+case 1110:
+ switch( (*p) ) {
+ case 89: goto st1111;
+ case 121: goto st1111;
+ }
+ goto tr3271;
+st1111:
+ if ( ++p == pe )
+ goto _test_eof1111;
+case 1111:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3436;
+ case 32: goto tr3436;
+ case 40: goto tr3437;
+ case 41: goto tr3438;
+ case 2058: goto tr3439;
+ case 2107: goto tr3440;
+ }
+ goto tr3271;
+st1112:
+ if ( ++p == pe )
+ goto _test_eof1112;
+case 1112:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3441;
+ case 32: goto tr3441;
+ case 40: goto tr3442;
+ case 41: goto tr3443;
+ case 2058: goto tr3444;
+ case 2107: goto tr3445;
+ }
+ goto tr3271;
+st1113:
+ if ( ++p == pe )
+ goto _test_eof1113;
+case 1113:
+ switch( (*p) ) {
+ case 82: goto st1114;
+ case 114: goto st1114;
+ }
+ goto tr3271;
+st1114:
+ if ( ++p == pe )
+ goto _test_eof1114;
+case 1114:
+ switch( (*p) ) {
+ case 84: goto st1115;
+ case 116: goto st1115;
+ }
+ goto tr3271;
+st1115:
+ if ( ++p == pe )
+ goto _test_eof1115;
+case 1115:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3448;
+ case 32: goto tr3448;
+ case 40: goto tr3449;
+ case 41: goto tr3450;
+ case 2058: goto tr3451;
+ case 2107: goto tr3452;
+ }
+ goto tr3271;
+st1116:
+ if ( ++p == pe )
+ goto _test_eof1116;
+case 1116:
+ switch( (*p) ) {
+ case 65: goto st1117;
+ case 97: goto st1117;
+ }
+ goto tr3271;
+st1117:
+ if ( ++p == pe )
+ goto _test_eof1117;
+case 1117:
+ switch( (*p) ) {
+ case 77: goto st1118;
+ case 109: goto st1118;
+ }
+ goto tr3271;
+st1118:
+ if ( ++p == pe )
+ goto _test_eof1118;
+case 1118:
+ switch( (*p) ) {
+ case 69: goto st1119;
+ case 101: goto st1119;
+ }
+ goto tr3271;
+st1119:
+ if ( ++p == pe )
+ goto _test_eof1119;
+case 1119:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3456;
+ case 32: goto tr3456;
+ case 40: goto tr3457;
+ case 41: goto tr3458;
+ case 2058: goto tr3459;
+ case 2107: goto tr3460;
+ }
+ goto tr3271;
+st1120:
+ if ( ++p == pe )
+ goto _test_eof1120;
+case 1120:
+ switch( (*p) ) {
+ case 89: goto st1121;
+ case 121: goto st1121;
+ }
+ goto tr3271;
+st1121:
+ if ( ++p == pe )
+ goto _test_eof1121;
+case 1121:
+ switch( (*p) ) {
+ case 78: goto st1122;
+ case 110: goto st1122;
+ }
+ goto tr3271;
+st1122:
+ if ( ++p == pe )
+ goto _test_eof1122;
+case 1122:
+ switch( (*p) ) {
+ case 67: goto st1123;
+ case 99: goto st1123;
+ }
+ goto tr3271;
+st1123:
+ if ( ++p == pe )
+ goto _test_eof1123;
+case 1123:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3464;
+ case 32: goto tr3464;
+ case 40: goto tr3465;
+ case 41: goto tr3466;
+ case 2058: goto tr3467;
+ case 2107: goto tr3468;
+ }
+ goto tr3271;
+st1124:
+ if ( ++p == pe )
+ goto _test_eof1124;
+case 1124:
+ switch( (*p) ) {
+ case 72: goto st1125;
+ case 78: goto st1129;
+ case 83: goto st1137;
+ case 104: goto st1125;
+ case 110: goto st1129;
+ case 115: goto st1137;
+ }
+ goto tr3271;
+st1125:
+ if ( ++p == pe )
+ goto _test_eof1125;
+case 1125:
+ switch( (*p) ) {
+ case 67: goto st1126;
+ case 99: goto st1126;
+ }
+ goto tr3271;
+st1126:
+ if ( ++p == pe )
+ goto _test_eof1126;
+case 1126:
+ switch( (*p) ) {
+ case 73: goto st1127;
+ case 105: goto st1127;
+ }
+ goto tr3271;
+st1127:
+ if ( ++p == pe )
+ goto _test_eof1127;
+case 1127:
+ switch( (*p) ) {
+ case 68: goto st1128;
+ case 100: goto st1128;
+ }
+ goto tr3271;
+st1128:
+ if ( ++p == pe )
+ goto _test_eof1128;
+case 1128:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3475;
+ case 32: goto tr3475;
+ case 40: goto tr3476;
+ case 41: goto tr3477;
+ case 2058: goto tr3478;
+ case 2107: goto tr3479;
+ }
+ goto tr3271;
+st1129:
+ if ( ++p == pe )
+ goto _test_eof1129;
+case 1129:
+ switch( (*p) ) {
+ case 65: goto st1130;
+ case 83: goto st1133;
+ case 97: goto st1130;
+ case 115: goto st1133;
+ }
+ goto tr3271;
+st1130:
+ if ( ++p == pe )
+ goto _test_eof1130;
+case 1130:
+ switch( (*p) ) {
+ case 77: goto st1131;
+ case 109: goto st1131;
+ }
+ goto tr3271;
+st1131:
+ if ( ++p == pe )
+ goto _test_eof1131;
+case 1131:
+ switch( (*p) ) {
+ case 69: goto st1132;
+ case 101: goto st1132;
+ }
+ goto tr3271;
+st1132:
+ if ( ++p == pe )
+ goto _test_eof1132;
+case 1132:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3484;
+ case 32: goto tr3484;
+ case 40: goto tr3485;
+ case 41: goto tr3486;
+ case 2058: goto tr3487;
+ case 2107: goto tr3488;
+ }
+ goto tr3271;
+st1133:
+ if ( ++p == pe )
+ goto _test_eof1133;
+case 1133:
+ switch( (*p) ) {
+ case 75: goto st1134;
+ case 107: goto st1134;
+ }
+ goto tr3271;
+st1134:
+ if ( ++p == pe )
+ goto _test_eof1134;
+case 1134:
+ switch( (*p) ) {
+ case 69: goto st1135;
+ case 101: goto st1135;
+ }
+ goto tr3271;
+st1135:
+ if ( ++p == pe )
+ goto _test_eof1135;
+case 1135:
+ switch( (*p) ) {
+ case 89: goto st1136;
+ case 121: goto st1136;
+ }
+ goto tr3271;
+st1136:
+ if ( ++p == pe )
+ goto _test_eof1136;
+case 1136:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3492;
+ case 32: goto tr3492;
+ case 40: goto tr3493;
+ case 41: goto tr3494;
+ case 2058: goto tr3495;
+ case 2107: goto tr3496;
+ }
+ goto tr3271;
+st1137:
+ if ( ++p == pe )
+ goto _test_eof1137;
+case 1137:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3497;
+ case 32: goto tr3497;
+ case 40: goto tr3498;
+ case 41: goto tr3499;
+ case 2058: goto tr3500;
+ case 2107: goto tr3501;
+ }
+ goto tr3271;
+st1138:
+ if ( ++p == pe )
+ goto _test_eof1138;
+case 1138:
+ switch( (*p) ) {
+ case 85: goto st1139;
+ case 117: goto st1139;
+ }
+ goto tr3271;
+st1139:
+ if ( ++p == pe )
+ goto _test_eof1139;
+case 1139:
+ switch( (*p) ) {
+ case 73: goto st1140;
+ case 105: goto st1140;
+ }
+ goto tr3271;
+st1140:
+ if ( ++p == pe )
+ goto _test_eof1140;
+case 1140:
+ switch( (*p) ) {
+ case 52: goto st1141;
+ case 54: goto st1143;
+ }
+ goto tr3271;
+st1141:
+ if ( ++p == pe )
+ goto _test_eof1141;
+case 1141:
+ if ( (*p) == 56 )
+ goto st1142;
+ goto tr3271;
+st1142:
+ if ( ++p == pe )
+ goto _test_eof1142;
+case 1142:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3507;
+ case 32: goto tr3507;
+ case 40: goto tr3508;
+ case 41: goto tr3509;
+ case 2058: goto tr3510;
+ case 2107: goto tr3511;
+ }
+ goto tr3271;
+st1143:
+ if ( ++p == pe )
+ goto _test_eof1143;
+case 1143:
+ if ( (*p) == 52 )
+ goto st1144;
+ goto tr3271;
+st1144:
+ if ( ++p == pe )
+ goto _test_eof1144;
+case 1144:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3513;
+ case 32: goto tr3513;
+ case 40: goto tr3514;
+ case 41: goto tr3515;
+ case 2058: goto tr3516;
+ case 2107: goto tr3517;
+ }
+ goto tr3271;
+st1145:
+ if ( ++p == pe )
+ goto _test_eof1145;
+case 1145:
+ switch( (*p) ) {
+ case 73: goto st1146;
+ case 84: goto st1150;
+ case 105: goto st1146;
+ case 116: goto st1150;
+ }
+ goto tr3271;
+st1146:
+ if ( ++p == pe )
+ goto _test_eof1146;
+case 1146:
+ switch( (*p) ) {
+ case 78: goto st1147;
+ case 110: goto st1147;
+ }
+ goto tr3271;
+st1147:
+ if ( ++p == pe )
+ goto _test_eof1147;
+case 1147:
+ switch( (*p) ) {
+ case 70: goto st1148;
+ case 102: goto st1148;
+ }
+ goto tr3271;
+st1148:
+ if ( ++p == pe )
+ goto _test_eof1148;
+case 1148:
+ switch( (*p) ) {
+ case 79: goto st1149;
+ case 111: goto st1149;
+ }
+ goto tr3271;
+st1149:
+ if ( ++p == pe )
+ goto _test_eof1149;
+case 1149:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3523;
+ case 32: goto tr3523;
+ case 40: goto tr3524;
+ case 41: goto tr3525;
+ case 2058: goto tr3526;
+ case 2107: goto tr3527;
+ }
+ goto tr3271;
+st1150:
+ if ( ++p == pe )
+ goto _test_eof1150;
+case 1150:
+ switch( (*p) ) {
+ case 84: goto st1151;
+ case 116: goto st1151;
+ }
+ goto tr3271;
+st1151:
+ if ( ++p == pe )
+ goto _test_eof1151;
+case 1151:
+ switch( (*p) ) {
+ case 80: goto st1152;
+ case 112: goto st1152;
+ }
+ goto tr3271;
+st1152:
+ if ( ++p == pe )
+ goto _test_eof1152;
+case 1152:
+ switch( (*p) ) {
+ case 83: goto st1153;
+ case 115: goto st1153;
+ }
+ goto tr3271;
+st1153:
+ if ( ++p == pe )
+ goto _test_eof1153;
+case 1153:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3531;
+ case 32: goto tr3531;
+ case 40: goto tr3532;
+ case 41: goto tr3533;
+ case 2058: goto tr3534;
+ case 2107: goto tr3535;
+ }
+ goto tr3271;
+st1154:
+ if ( ++p == pe )
+ goto _test_eof1154;
+case 1154:
+ switch( (*p) ) {
+ case 80: goto st1155;
+ case 112: goto st1155;
+ }
+ goto tr3271;
+st1155:
+ if ( ++p == pe )
+ goto _test_eof1155;
+case 1155:
+ switch( (*p) ) {
+ case 83: goto st1156;
+ case 115: goto st1156;
+ }
+ goto tr3271;
+st1156:
+ if ( ++p == pe )
+ goto _test_eof1156;
+case 1156:
+ switch( (*p) ) {
+ case 69: goto st1157;
+ case 101: goto st1157;
+ }
+ goto tr3271;
+st1157:
+ if ( ++p == pe )
+ goto _test_eof1157;
+case 1157:
+ switch( (*p) ) {
+ case 67: goto st1158;
+ case 99: goto st1158;
+ }
+ goto tr3271;
+st1158:
+ if ( ++p == pe )
+ goto _test_eof1158;
+case 1158:
+ switch( (*p) ) {
+ case 75: goto st1159;
+ case 107: goto st1159;
+ }
+ goto tr3271;
+st1159:
+ if ( ++p == pe )
+ goto _test_eof1159;
+case 1159:
+ switch( (*p) ) {
+ case 69: goto st1160;
+ case 101: goto st1160;
+ }
+ goto tr3271;
+st1160:
+ if ( ++p == pe )
+ goto _test_eof1160;
+case 1160:
+ switch( (*p) ) {
+ case 89: goto st1161;
+ case 121: goto st1161;
+ }
+ goto tr3271;
+st1161:
+ if ( ++p == pe )
+ goto _test_eof1161;
+case 1161:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3543;
+ case 32: goto tr3543;
+ case 40: goto tr3544;
+ case 41: goto tr3545;
+ case 2058: goto tr3546;
+ case 2107: goto tr3547;
+ }
+ goto tr3271;
+st1162:
+ if ( ++p == pe )
+ goto _test_eof1162;
+case 1162:
+ switch( (*p) ) {
+ case 69: goto st1163;
+ case 88: goto st1165;
+ case 101: goto st1163;
+ case 120: goto st1165;
+ }
+ goto tr3271;
+st1163:
+ if ( ++p == pe )
+ goto _test_eof1163;
+case 1163:
+ switch( (*p) ) {
+ case 89: goto st1164;
+ case 121: goto st1164;
+ }
+ goto tr3271;
+st1164:
+ if ( ++p == pe )
+ goto _test_eof1164;
+case 1164:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3551;
+ case 32: goto tr3551;
+ case 40: goto tr3552;
+ case 41: goto tr3553;
+ case 2058: goto tr3554;
+ case 2107: goto tr3555;
+ }
+ goto tr3271;
+st1165:
+ if ( ++p == pe )
+ goto _test_eof1165;
+case 1165:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3556;
+ case 32: goto tr3556;
+ case 40: goto tr3557;
+ case 41: goto tr3558;
+ case 2058: goto tr3559;
+ case 2107: goto tr3560;
+ }
+ goto tr3271;
+st1166:
+ if ( ++p == pe )
+ goto _test_eof1166;
+case 1166:
+ switch( (*p) ) {
+ case 51: goto st1167;
+ case 54: goto st1169;
+ case 79: goto st1171;
+ case 80: goto st1173;
+ case 111: goto st1171;
+ case 112: goto st1173;
+ }
+ goto tr3271;
+st1167:
+ if ( ++p == pe )
+ goto _test_eof1167;
+case 1167:
+ if ( (*p) == 50 )
+ goto st1168;
+ goto tr3271;
+st1168:
+ if ( ++p == pe )
+ goto _test_eof1168;
+case 1168:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3566;
+ case 32: goto tr3566;
+ case 40: goto tr3567;
+ case 41: goto tr3568;
+ case 2058: goto tr3569;
+ case 2107: goto tr3570;
+ }
+ goto tr3271;
+st1169:
+ if ( ++p == pe )
+ goto _test_eof1169;
+case 1169:
+ if ( (*p) == 52 )
+ goto st1170;
+ goto tr3271;
+st1170:
+ if ( ++p == pe )
+ goto _test_eof1170;
+case 1170:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3572;
+ case 32: goto tr3572;
+ case 40: goto tr3573;
+ case 41: goto tr3574;
+ case 2058: goto tr3575;
+ case 2107: goto tr3576;
+ }
+ goto tr3271;
+st1171:
+ if ( ++p == pe )
+ goto _test_eof1171;
+case 1171:
+ switch( (*p) ) {
+ case 67: goto st1172;
+ case 99: goto st1172;
+ }
+ goto tr3271;
+st1172:
+ if ( ++p == pe )
+ goto _test_eof1172;
+case 1172:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3578;
+ case 32: goto tr3578;
+ case 40: goto tr3579;
+ case 41: goto tr3580;
+ case 2058: goto tr3581;
+ case 2107: goto tr3582;
+ }
+ goto tr3271;
+st1173:
+ if ( ++p == pe )
+ goto _test_eof1173;
+case 1173:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3583;
+ case 32: goto tr3583;
+ case 40: goto tr3584;
+ case 41: goto tr3585;
+ case 2058: goto tr3586;
+ case 2107: goto tr3587;
+ }
+ goto tr3271;
+st1174:
+ if ( ++p == pe )
+ goto _test_eof1174;
+case 1174:
+ switch( (*p) ) {
+ case 73: goto st1175;
+ case 88: goto st1179;
+ case 105: goto st1175;
+ case 120: goto st1179;
+ }
+ goto tr3271;
+st1175:
+ if ( ++p == pe )
+ goto _test_eof1175;
+case 1175:
+ switch( (*p) ) {
+ case 78: goto st1176;
+ case 110: goto st1176;
+ }
+ goto tr3271;
+st1176:
+ if ( ++p == pe )
+ goto _test_eof1176;
+case 1176:
+ switch( (*p) ) {
+ case 70: goto st1177;
+ case 102: goto st1177;
+ }
+ goto tr3271;
+st1177:
+ if ( ++p == pe )
+ goto _test_eof1177;
+case 1177:
+ switch( (*p) ) {
+ case 79: goto st1178;
+ case 111: goto st1178;
+ }
+ goto tr3271;
+st1178:
+ if ( ++p == pe )
+ goto _test_eof1178;
+case 1178:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3593;
+ case 32: goto tr3593;
+ case 40: goto tr3594;
+ case 41: goto tr3595;
+ case 2058: goto tr3596;
+ case 2107: goto tr3597;
+ }
+ goto tr3271;
+st1179:
+ if ( ++p == pe )
+ goto _test_eof1179;
+case 1179:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3598;
+ case 32: goto tr3598;
+ case 40: goto tr3599;
+ case 41: goto tr3600;
+ case 2058: goto tr3601;
+ case 2107: goto tr3602;
+ }
+ goto tr3271;
+st1180:
+ if ( ++p == pe )
+ goto _test_eof1180;
+case 1180:
+ switch( (*p) ) {
+ case 65: goto st1181;
+ case 73: goto st1185;
+ case 83: goto st1187;
+ case 97: goto st1181;
+ case 105: goto st1185;
+ case 115: goto st1187;
+ }
+ goto tr3271;
+st1181:
+ if ( ++p == pe )
+ goto _test_eof1181;
+case 1181:
+ switch( (*p) ) {
+ case 80: goto st1182;
+ case 112: goto st1182;
+ }
+ goto tr3271;
+st1182:
+ if ( ++p == pe )
+ goto _test_eof1182;
+case 1182:
+ switch( (*p) ) {
+ case 84: goto st1183;
+ case 116: goto st1183;
+ }
+ goto tr3271;
+st1183:
+ if ( ++p == pe )
+ goto _test_eof1183;
+case 1183:
+ switch( (*p) ) {
+ case 82: goto st1184;
+ case 114: goto st1184;
+ }
+ goto tr3271;
+st1184:
+ if ( ++p == pe )
+ goto _test_eof1184;
+case 1184:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3609;
+ case 32: goto tr3609;
+ case 40: goto tr3610;
+ case 41: goto tr3611;
+ case 2058: goto tr3612;
+ case 2107: goto tr3613;
+ }
+ goto tr3271;
+st1185:
+ if ( ++p == pe )
+ goto _test_eof1185;
+case 1185:
+ switch( (*p) ) {
+ case 68: goto st1186;
+ case 100: goto st1186;
+ }
+ goto tr3271;
+st1186:
+ if ( ++p == pe )
+ goto _test_eof1186;
+case 1186:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3615;
+ case 32: goto tr3615;
+ case 40: goto tr3616;
+ case 41: goto tr3617;
+ case 2058: goto tr3618;
+ case 2107: goto tr3619;
+ }
+ goto tr3271;
+st1187:
+ if ( ++p == pe )
+ goto _test_eof1187;
+case 1187:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3620;
+ case 32: goto tr3620;
+ case 40: goto tr3621;
+ case 41: goto tr3622;
+ case 69: goto st1188;
+ case 101: goto st1188;
+ case 2058: goto tr3624;
+ case 2107: goto tr3625;
+ }
+ goto tr3271;
+st1188:
+ if ( ++p == pe )
+ goto _test_eof1188;
+case 1188:
+ switch( (*p) ) {
+ case 67: goto st1189;
+ case 99: goto st1189;
+ }
+ goto tr3271;
+st1189:
+ if ( ++p == pe )
+ goto _test_eof1189;
+case 1189:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3627;
+ case 32: goto tr3627;
+ case 40: goto tr3628;
+ case 41: goto tr3629;
+ case 51: goto st1190;
+ case 2058: goto tr3631;
+ case 2107: goto tr3632;
+ }
+ goto tr3271;
+st1190:
+ if ( ++p == pe )
+ goto _test_eof1190;
+case 1190:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3633;
+ case 32: goto tr3633;
+ case 40: goto tr3634;
+ case 41: goto tr3635;
+ case 80: goto st1191;
+ case 112: goto st1191;
+ case 2058: goto tr3637;
+ case 2107: goto tr3638;
+ }
+ goto tr3271;
+st1191:
+ if ( ++p == pe )
+ goto _test_eof1191;
+case 1191:
+ switch( (*p) ) {
+ case 65: goto st1192;
+ case 97: goto st1192;
+ }
+ goto tr3271;
+st1192:
+ if ( ++p == pe )
+ goto _test_eof1192;
+case 1192:
+ switch( (*p) ) {
+ case 82: goto st1193;
+ case 114: goto st1193;
+ }
+ goto tr3271;
+st1193:
+ if ( ++p == pe )
+ goto _test_eof1193;
+case 1193:
+ switch( (*p) ) {
+ case 65: goto st1194;
+ case 97: goto st1194;
+ }
+ goto tr3271;
+st1194:
+ if ( ++p == pe )
+ goto _test_eof1194;
+case 1194:
+ switch( (*p) ) {
+ case 77: goto st1195;
+ case 109: goto st1195;
+ }
+ goto tr3271;
+st1195:
+ if ( ++p == pe )
+ goto _test_eof1195;
+case 1195:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3643;
+ case 32: goto tr3643;
+ case 40: goto tr3644;
+ case 41: goto tr3645;
+ case 2058: goto tr3646;
+ case 2107: goto tr3647;
+ }
+ goto tr3271;
+st1196:
+ if ( ++p == pe )
+ goto _test_eof1196;
+case 1196:
+ switch( (*p) ) {
+ case 80: goto st1197;
+ case 112: goto st1197;
+ }
+ goto tr3271;
+st1197:
+ if ( ++p == pe )
+ goto _test_eof1197;
+case 1197:
+ switch( (*p) ) {
+ case 69: goto st1198;
+ case 101: goto st1198;
+ }
+ goto tr3271;
+st1198:
+ if ( ++p == pe )
+ goto _test_eof1198;
+case 1198:
+ switch( (*p) ) {
+ case 78: goto st1199;
+ case 110: goto st1199;
+ }
+ goto tr3271;
+st1199:
+ if ( ++p == pe )
+ goto _test_eof1199;
+case 1199:
+ switch( (*p) ) {
+ case 80: goto st1200;
+ case 112: goto st1200;
+ }
+ goto tr3271;
+st1200:
+ if ( ++p == pe )
+ goto _test_eof1200;
+case 1200:
+ switch( (*p) ) {
+ case 71: goto st1201;
+ case 103: goto st1201;
+ }
+ goto tr3271;
+st1201:
+ if ( ++p == pe )
+ goto _test_eof1201;
+case 1201:
+ switch( (*p) ) {
+ case 80: goto st1202;
+ case 112: goto st1202;
+ }
+ goto tr3271;
+st1202:
+ if ( ++p == pe )
+ goto _test_eof1202;
+case 1202:
+ switch( (*p) ) {
+ case 75: goto st1203;
+ case 107: goto st1203;
+ }
+ goto tr3271;
+st1203:
+ if ( ++p == pe )
+ goto _test_eof1203;
+case 1203:
+ switch( (*p) ) {
+ case 69: goto st1204;
+ case 101: goto st1204;
+ }
+ goto tr3271;
+st1204:
+ if ( ++p == pe )
+ goto _test_eof1204;
+case 1204:
+ switch( (*p) ) {
+ case 89: goto st1205;
+ case 121: goto st1205;
+ }
+ goto tr3271;
+st1205:
+ if ( ++p == pe )
+ goto _test_eof1205;
+case 1205:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3657;
+ case 32: goto tr3657;
+ case 40: goto tr3658;
+ case 41: goto tr3659;
+ case 2058: goto tr3660;
+ case 2107: goto tr3661;
+ }
+ goto tr3271;
+st1206:
+ if ( ++p == pe )
+ goto _test_eof1206;
+case 1206:
+ switch( (*p) ) {
+ case 84: goto st1207;
+ case 116: goto st1207;
+ }
+ goto tr3271;
+st1207:
+ if ( ++p == pe )
+ goto _test_eof1207;
+case 1207:
+ switch( (*p) ) {
+ case 82: goto st1208;
+ case 114: goto st1208;
+ }
+ goto tr3271;
+st1208:
+ if ( ++p == pe )
+ goto _test_eof1208;
+case 1208:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3664;
+ case 32: goto tr3664;
+ case 40: goto tr3665;
+ case 41: goto tr3666;
+ case 2058: goto tr3667;
+ case 2107: goto tr3668;
+ }
+ goto tr3271;
+st1209:
+ if ( ++p == pe )
+ goto _test_eof1209;
+case 1209:
+ switch( (*p) ) {
+ case 80: goto st1210;
+ case 82: goto st1211;
+ case 84: goto st1215;
+ case 112: goto st1210;
+ case 114: goto st1211;
+ case 116: goto st1215;
+ }
+ goto tr3271;
+st1210:
+ if ( ++p == pe )
+ goto _test_eof1210;
+case 1210:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3672;
+ case 32: goto tr3672;
+ case 40: goto tr3673;
+ case 41: goto tr3674;
+ case 2058: goto tr3675;
+ case 2107: goto tr3676;
+ }
+ goto tr3271;
+st1211:
+ if ( ++p == pe )
+ goto _test_eof1211;
+case 1211:
+ switch( (*p) ) {
+ case 83: goto st1212;
+ case 115: goto st1212;
+ }
+ goto tr3271;
+st1212:
+ if ( ++p == pe )
+ goto _test_eof1212;
+case 1212:
+ switch( (*p) ) {
+ case 73: goto st1213;
+ case 105: goto st1213;
+ }
+ goto tr3271;
+st1213:
+ if ( ++p == pe )
+ goto _test_eof1213;
+case 1213:
+ switch( (*p) ) {
+ case 71: goto st1214;
+ case 103: goto st1214;
+ }
+ goto tr3271;
+st1214:
+ if ( ++p == pe )
+ goto _test_eof1214;
+case 1214:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3680;
+ case 32: goto tr3680;
+ case 40: goto tr3681;
+ case 41: goto tr3682;
+ case 2058: goto tr3683;
+ case 2107: goto tr3684;
+ }
+ goto tr3271;
+st1215:
+ if ( ++p == pe )
+ goto _test_eof1215;
+case 1215:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3685;
+ case 32: goto tr3685;
+ case 40: goto tr3686;
+ case 41: goto tr3687;
+ case 2058: goto tr3688;
+ case 2107: goto tr3689;
+ }
+ goto tr3271;
+st1216:
+ if ( ++p == pe )
+ goto _test_eof1216;
+case 1216:
+ switch( (*p) ) {
+ case 77: goto st1217;
+ case 79: goto st1222;
+ case 80: goto st1224;
+ case 82: goto st1226;
+ case 83: goto st1228;
+ case 86: goto st1232;
+ case 109: goto st1217;
+ case 111: goto st1222;
+ case 112: goto st1224;
+ case 114: goto st1226;
+ case 115: goto st1228;
+ case 118: goto st1232;
+ }
+ goto tr3271;
+st1217:
+ if ( ++p == pe )
+ goto _test_eof1217;
+case 1217:
+ switch( (*p) ) {
+ case 73: goto st1218;
+ case 105: goto st1218;
+ }
+ goto tr3271;
+st1218:
+ if ( ++p == pe )
+ goto _test_eof1218;
+case 1218:
+ switch( (*p) ) {
+ case 77: goto st1219;
+ case 109: goto st1219;
+ }
+ goto tr3271;
+st1219:
+ if ( ++p == pe )
+ goto _test_eof1219;
+case 1219:
+ switch( (*p) ) {
+ case 69: goto st1220;
+ case 101: goto st1220;
+ }
+ goto tr3271;
+st1220:
+ if ( ++p == pe )
+ goto _test_eof1220;
+case 1220:
+ switch( (*p) ) {
+ case 65: goto st1221;
+ case 97: goto st1221;
+ }
+ goto tr3271;
+st1221:
+ if ( ++p == pe )
+ goto _test_eof1221;
+case 1221:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3700;
+ case 32: goto tr3700;
+ case 40: goto tr3701;
+ case 41: goto tr3702;
+ case 2058: goto tr3703;
+ case 2107: goto tr3704;
+ }
+ goto tr3271;
+st1222:
+ if ( ++p == pe )
+ goto _test_eof1222;
+case 1222:
+ switch( (*p) ) {
+ case 65: goto st1223;
+ case 97: goto st1223;
+ }
+ goto tr3271;
+st1223:
+ if ( ++p == pe )
+ goto _test_eof1223;
+case 1223:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3706;
+ case 32: goto tr3706;
+ case 40: goto tr3707;
+ case 41: goto tr3708;
+ case 2058: goto tr3709;
+ case 2107: goto tr3710;
+ }
+ goto tr3271;
+st1224:
+ if ( ++p == pe )
+ goto _test_eof1224;
+case 1224:
+ switch( (*p) ) {
+ case 70: goto st1225;
+ case 102: goto st1225;
+ }
+ goto tr3271;
+st1225:
+ if ( ++p == pe )
+ goto _test_eof1225;
+case 1225:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3712;
+ case 32: goto tr3712;
+ case 40: goto tr3713;
+ case 41: goto tr3714;
+ case 2058: goto tr3715;
+ case 2107: goto tr3716;
+ }
+ goto tr3271;
+st1226:
+ if ( ++p == pe )
+ goto _test_eof1226;
+case 1226:
+ switch( (*p) ) {
+ case 86: goto st1227;
+ case 118: goto st1227;
+ }
+ goto tr3271;
+st1227:
+ if ( ++p == pe )
+ goto _test_eof1227;
+case 1227:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3718;
+ case 32: goto tr3718;
+ case 40: goto tr3719;
+ case 41: goto tr3720;
+ case 2058: goto tr3721;
+ case 2107: goto tr3722;
+ }
+ goto tr3271;
+st1228:
+ if ( ++p == pe )
+ goto _test_eof1228;
+case 1228:
+ switch( (*p) ) {
+ case 72: goto st1229;
+ case 104: goto st1229;
+ }
+ goto tr3271;
+st1229:
+ if ( ++p == pe )
+ goto _test_eof1229;
+case 1229:
+ switch( (*p) ) {
+ case 70: goto st1230;
+ case 102: goto st1230;
+ }
+ goto tr3271;
+st1230:
+ if ( ++p == pe )
+ goto _test_eof1230;
+case 1230:
+ switch( (*p) ) {
+ case 80: goto st1231;
+ case 112: goto st1231;
+ }
+ goto tr3271;
+st1231:
+ if ( ++p == pe )
+ goto _test_eof1231;
+case 1231:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3726;
+ case 32: goto tr3726;
+ case 40: goto tr3727;
+ case 41: goto tr3728;
+ case 2058: goto tr3729;
+ case 2107: goto tr3730;
+ }
+ goto tr3271;
+st1232:
+ if ( ++p == pe )
+ goto _test_eof1232;
+case 1232:
+ switch( (*p) ) {
+ case 67: goto st1233;
+ case 99: goto st1233;
+ }
+ goto tr3271;
+st1233:
+ if ( ++p == pe )
+ goto _test_eof1233;
+case 1233:
+ switch( (*p) ) {
+ case 66: goto st1234;
+ case 98: goto st1234;
+ }
+ goto tr3271;
+st1234:
+ if ( ++p == pe )
+ goto _test_eof1234;
+case 1234:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3733;
+ case 32: goto tr3733;
+ case 40: goto tr3734;
+ case 41: goto tr3735;
+ case 2058: goto tr3736;
+ case 2107: goto tr3737;
+ }
+ goto tr3271;
+st1235:
+ if ( ++p == pe )
+ goto _test_eof1235;
+case 1235:
+ switch( (*p) ) {
+ case 76: goto st1236;
+ case 88: goto st1239;
+ case 89: goto st1241;
+ case 108: goto st1236;
+ case 120: goto st1239;
+ case 121: goto st1241;
+ }
+ goto tr3271;
+st1236:
+ if ( ++p == pe )
+ goto _test_eof1236;
+case 1236:
+ switch( (*p) ) {
+ case 83: goto st1237;
+ case 115: goto st1237;
+ }
+ goto tr3271;
+st1237:
+ if ( ++p == pe )
+ goto _test_eof1237;
+case 1237:
+ switch( (*p) ) {
+ case 65: goto st1238;
+ case 97: goto st1238;
+ }
+ goto tr3271;
+st1238:
+ if ( ++p == pe )
+ goto _test_eof1238;
+case 1238:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3743;
+ case 32: goto tr3743;
+ case 40: goto tr3744;
+ case 41: goto tr3745;
+ case 2058: goto tr3746;
+ case 2107: goto tr3747;
+ }
+ goto tr3271;
+st1239:
+ if ( ++p == pe )
+ goto _test_eof1239;
+case 1239:
+ switch( (*p) ) {
+ case 84: goto st1240;
+ case 116: goto st1240;
+ }
+ goto tr3271;
+st1240:
+ if ( ++p == pe )
+ goto _test_eof1240;
+case 1240:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3749;
+ case 32: goto tr3749;
+ case 40: goto tr3750;
+ case 41: goto tr3751;
+ case 2058: goto tr3752;
+ case 2107: goto tr3753;
+ }
+ goto tr3271;
+st1241:
+ if ( ++p == pe )
+ goto _test_eof1241;
+case 1241:
+ switch( (*p) ) {
+ case 80: goto st1242;
+ case 112: goto st1242;
+ }
+ goto tr3271;
+st1242:
+ if ( ++p == pe )
+ goto _test_eof1242;
+case 1242:
+ switch( (*p) ) {
+ case 69: goto st1243;
+ case 101: goto st1243;
+ }
+ goto tr3271;
+st1243:
+ if ( ++p == pe )
+ goto _test_eof1243;
+case 1243:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3757;
+ goto tr3756;
+tr3757:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1244;
+tr3761:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1244;
+st1244:
+ if ( ++p == pe )
+ goto _test_eof1244;
+case 1244:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3758;
+ case 32: goto tr3758;
+ case 40: goto tr3759;
+ case 41: goto tr3760;
+ case 2058: goto tr3762;
+ case 2107: goto tr3763;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3761;
+ goto tr3756;
+st1245:
+ if ( ++p == pe )
+ goto _test_eof1245;
+case 1245:
+ switch( (*p) ) {
+ case 82: goto st1246;
+ case 114: goto st1246;
+ }
+ goto tr3271;
+st1246:
+ if ( ++p == pe )
+ goto _test_eof1246;
+case 1246:
+ switch( (*p) ) {
+ case 73: goto st1247;
+ case 105: goto st1247;
+ }
+ goto tr3271;
+st1247:
+ if ( ++p == pe )
+ goto _test_eof1247;
+case 1247:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3766;
+ case 32: goto tr3766;
+ case 40: goto tr3767;
+ case 41: goto tr3768;
+ case 2058: goto tr3769;
+ case 2107: goto tr3770;
+ }
+ goto tr3271;
+st1248:
+ if ( ++p == pe )
+ goto _test_eof1248;
+case 1248:
+ switch( (*p) ) {
+ case 79: goto st1249;
+ case 111: goto st1249;
+ }
+ goto tr3271;
+st1249:
+ if ( ++p == pe )
+ goto _test_eof1249;
+case 1249:
+ switch( (*p) ) {
+ case 78: goto st1250;
+ case 110: goto st1250;
+ }
+ goto tr3271;
+st1250:
+ if ( ++p == pe )
+ goto _test_eof1250;
+case 1250:
+ switch( (*p) ) {
+ case 69: goto st1251;
+ case 101: goto st1251;
+ }
+ goto tr3271;
+st1251:
+ if ( ++p == pe )
+ goto _test_eof1251;
+case 1251:
+ switch( (*p) ) {
+ case 77: goto st1252;
+ case 109: goto st1252;
+ }
+ goto tr3271;
+st1252:
+ if ( ++p == pe )
+ goto _test_eof1252;
+case 1252:
+ switch( (*p) ) {
+ case 68: goto st1253;
+ case 100: goto st1253;
+ }
+ goto tr3271;
+st1253:
+ if ( ++p == pe )
+ goto _test_eof1253;
+case 1253:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3776;
+ case 32: goto tr3776;
+ case 40: goto tr3777;
+ case 41: goto tr3778;
+ case 2058: goto tr3779;
+ case 2107: goto tr3780;
+ }
+ goto tr3271;
+st1254:
+ if ( ++p == pe )
+ goto _test_eof1254;
+case 1254:
+ switch( (*p) ) {
+ case 42: goto tr3781;
+ case 92: goto tr3781;
+ case 95: goto tr3781;
+ }
+ if ( (*p) < 64 ) {
+ if ( 45 <= (*p) && (*p) <= 57 )
+ goto tr3781;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr3781;
+ } else
+ goto tr3781;
+ goto tr75;
+tr3781:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 1255;goto st309;} }
+ goto st1255;
+st1255:
+ if ( ++p == pe )
+ goto _test_eof1255;
+case 1255:
+ switch( (*p) ) {
+ case 32: goto tr3782;
+ case 59: goto tr3782;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3782;
+ } else if ( (*p) >= 9 )
+ goto tr3782;
+ goto tr75;
+tr3782:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ { p--; {stack[top++] = 1256;goto st376;} }
+ goto st1256;
+st1256:
+ if ( ++p == pe )
+ goto _test_eof1256;
+case 1256:
+ switch( (*p) ) {
+ case 32: goto tr3783;
+ case 59: goto tr3783;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3783;
+ } else if ( (*p) >= 9 )
+ goto tr3783;
+ goto tr75;
+tr3783:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1474;
+st1474:
+ if ( ++p == pe )
+ goto _test_eof1474;
+case 1474:
+ goto st0;
+st1257:
+ if ( ++p == pe )
+ goto _test_eof1257;
+case 1257:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3784;
+ goto tr2327;
+tr3784:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1258;
+tr3788:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1258;
+st1258:
+ if ( ++p == pe )
+ goto _test_eof1258;
+case 1258:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3785;
+ case 32: goto tr3785;
+ case 40: goto tr3786;
+ case 41: goto tr3787;
+ case 2058: goto tr3789;
+ case 2107: goto tr3790;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3788;
+ goto tr2327;
+tr3792:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1259;
+tr3793:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1259;
+tr3795:
+ {
+ s->line_counter++;
+ }
+ goto st1259;
+tr3821:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1259; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1259;
+tr3785:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1259;
+tr3786:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1259;
+tr3787:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1259;
+tr3789:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1259;
+st1259:
+ if ( ++p == pe )
+ goto _test_eof1259;
+case 1259:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1259;
+ case 32: goto st1259;
+ case 40: goto tr3792;
+ case 41: goto tr3793;
+ case 2058: goto tr3795;
+ case 2107: goto tr3796;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3794;
+ goto tr2327;
+tr3794:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1260;
+tr3800:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1260;
+st1260:
+ if ( ++p == pe )
+ goto _test_eof1260;
+case 1260:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3797;
+ case 32: goto tr3797;
+ case 40: goto tr3798;
+ case 41: goto tr3799;
+ case 2058: goto tr3801;
+ case 2107: goto tr3802;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3800;
+ goto tr2327;
+tr3804:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1261;
+tr3805:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1261;
+tr3807:
+ {
+ s->line_counter++;
+ }
+ goto st1261;
+tr3819:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1261; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1261;
+tr3797:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1261;
+tr3798:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1261;
+tr3799:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1261;
+tr3801:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1261;
+st1261:
+ if ( ++p == pe )
+ goto _test_eof1261;
+case 1261:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1261;
+ case 32: goto st1261;
+ case 40: goto tr3804;
+ case 41: goto tr3805;
+ case 2058: goto tr3807;
+ case 2107: goto tr3808;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3806;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3806;
+ } else
+ goto tr3806;
+ goto tr75;
+tr3806:
+ { p--; {stack[top++] = 1262;goto st699;} }
+ goto st1262;
+st1262:
+ if ( ++p == pe )
+ goto _test_eof1262;
+case 1262:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1263;
+ case 32: goto st1263;
+ case 40: goto tr3810;
+ case 41: goto tr3811;
+ case 2058: goto tr3812;
+ case 2107: goto tr3813;
+ }
+ goto tr75;
+tr3810:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1263;
+tr3811:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1263;
+tr3812:
+ {
+ s->line_counter++;
+ }
+ goto st1263;
+tr3817:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1263; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1263;
+st1263:
+ if ( ++p == pe )
+ goto _test_eof1263;
+case 1263:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1263;
+ case 32: goto st1263;
+ case 40: goto tr3810;
+ case 41: goto tr3811;
+ case 43: goto tr3814;
+ case 2058: goto tr3812;
+ case 2107: goto tr3813;
+ }
+ if ( _widec < 65 ) {
+ if ( 47 <= _widec && _widec <= 57 )
+ goto tr3814;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr3814;
+ } else
+ goto tr3814;
+ goto tr75;
+tr3814:
+ { p--; {stack[top++] = 1264;goto st369;} }
+ goto st1264;
+st1264:
+ if ( ++p == pe )
+ goto _test_eof1264;
+case 1264:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 2570: goto tr3815;
+ case 2619: goto tr3815;
+ }
+ goto tr75;
+tr3815:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1475;
+st1475:
+ if ( ++p == pe )
+ goto _test_eof1475;
+case 1475:
+ goto st0;
+tr3813:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1265;
+tr3816:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1265;
+st1265:
+ if ( ++p == pe )
+ goto _test_eof1265;
+case 1265:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3817;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3816;
+ goto tr75;
+tr3808:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1266;
+tr3818:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1266;
+tr3802:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1266;
+st1266:
+ if ( ++p == pe )
+ goto _test_eof1266;
+case 1266:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3819;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3818;
+ goto tr75;
+tr3796:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1267;
+tr3820:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1267;
+tr3790:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1267;
+st1267:
+ if ( ++p == pe )
+ goto _test_eof1267;
+case 1267:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3821;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3820;
+ goto tr75;
+st1268:
+ if ( ++p == pe )
+ goto _test_eof1268;
+case 1268:
+ if ( (*p) == 43 )
+ goto tr3822;
+ if ( (*p) < 65 ) {
+ if ( 47 <= (*p) && (*p) <= 57 )
+ goto tr3822;
+ } else if ( (*p) > 90 ) {
+ if ( 97 <= (*p) && (*p) <= 122 )
+ goto tr3822;
+ } else
+ goto tr3822;
+ goto tr75;
+tr3822:
+ { p--; {stack[top++] = 1269;goto st369;} }
+ goto st1269;
+st1269:
+ if ( ++p == pe )
+ goto _test_eof1269;
+case 1269:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 2570: goto tr3823;
+ case 2619: goto tr3823;
+ }
+ goto tr75;
+tr3823:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1476;
+st1476:
+ if ( ++p == pe )
+ goto _test_eof1476;
+case 1476:
+ goto st0;
+st1270:
+ if ( ++p == pe )
+ goto _test_eof1270;
+case 1270:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3824;
+ goto tr2327;
+tr3824:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1271;
+tr3828:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1271;
+st1271:
+ if ( ++p == pe )
+ goto _test_eof1271;
+case 1271:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3825;
+ case 32: goto tr3825;
+ case 40: goto tr3826;
+ case 41: goto tr3827;
+ case 2058: goto tr3829;
+ case 2107: goto tr3830;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3828;
+ goto tr2327;
+tr3832:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1272;
+tr3833:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1272;
+tr3835:
+ {
+ s->line_counter++;
+ }
+ goto st1272;
+tr3899:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1272; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1272;
+tr3825:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1272;
+tr3826:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1272;
+tr3827:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1272;
+tr3829:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1272;
+st1272:
+ if ( ++p == pe )
+ goto _test_eof1272;
+case 1272:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1272;
+ case 32: goto st1272;
+ case 40: goto tr3832;
+ case 41: goto tr3833;
+ case 2058: goto tr3835;
+ case 2107: goto tr3836;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3834;
+ goto tr2327;
+tr3834:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1273;
+tr3840:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1273;
+st1273:
+ if ( ++p == pe )
+ goto _test_eof1273;
+case 1273:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3837;
+ case 32: goto tr3837;
+ case 40: goto tr3838;
+ case 41: goto tr3839;
+ case 2058: goto tr3841;
+ case 2107: goto tr3842;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3840;
+ goto tr2327;
+tr3844:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1274;
+tr3845:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1274;
+tr3847:
+ {
+ s->line_counter++;
+ }
+ goto st1274;
+tr3897:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1274; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1274;
+tr3837:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1274;
+tr3838:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1274;
+tr3839:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1274;
+tr3841:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1274;
+st1274:
+ if ( ++p == pe )
+ goto _test_eof1274;
+case 1274:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1274;
+ case 32: goto st1274;
+ case 40: goto tr3844;
+ case 41: goto tr3845;
+ case 2058: goto tr3847;
+ case 2107: goto tr3848;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3846;
+ goto tr2327;
+tr3846:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1275;
+tr3852:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1275;
+st1275:
+ if ( ++p == pe )
+ goto _test_eof1275;
+case 1275:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3849;
+ case 32: goto tr3849;
+ case 40: goto tr3850;
+ case 41: goto tr3851;
+ case 2058: goto tr3853;
+ case 2107: goto tr3854;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3852;
+ goto tr2327;
+tr3856:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1276;
+tr3857:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1276;
+tr3860:
+ {
+ s->line_counter++;
+ }
+ goto st1276;
+tr3895:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1276; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1276;
+tr3849:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1276;
+tr3850:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1276;
+tr3851:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1276;
+tr3853:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1276;
+st1276:
+ if ( ++p == pe )
+ goto _test_eof1276;
+case 1276:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1276;
+ case 32: goto st1276;
+ case 40: goto tr3856;
+ case 41: goto tr3857;
+ case 45: goto tr3858;
+ case 2058: goto tr3860;
+ case 2107: goto tr3861;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3859;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3859;
+ } else
+ goto tr3859;
+ goto tr2998;
+tr3858:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1277;
+st1277:
+ if ( ++p == pe )
+ goto _test_eof1277;
+case 1277:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3862;
+ case 32: goto tr3862;
+ case 40: goto tr3863;
+ case 41: goto tr3864;
+ case 2058: goto tr3865;
+ case 2107: goto tr3866;
+ }
+ goto tr2998;
+tr3869:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1278;
+tr3870:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1278;
+tr3872:
+ {
+ s->line_counter++;
+ }
+ goto st1278;
+tr3891:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1278; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1278;
+tr3862:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1278;
+tr3863:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1278;
+tr3864:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1278;
+tr3865:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1278;
+st1278:
+ if ( ++p == pe )
+ goto _test_eof1278;
+case 1278:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1278;
+ case 32: goto st1278;
+ case 40: goto tr3869;
+ case 41: goto tr3870;
+ case 2058: goto tr3872;
+ case 2107: goto tr3873;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3871;
+ } else if ( _widec > 86 ) {
+ if ( 97 <= _widec && _widec <= 118 )
+ goto tr3871;
+ } else
+ goto tr3871;
+ goto tr3867;
+tr3871:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1279;
+tr3886:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1279;
+st1279:
+ if ( ++p == pe )
+ goto _test_eof1279;
+case 1279:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3874;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3874;
+ } else
+ goto tr3874;
+ goto tr3867;
+tr3874:
+ {
+ *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1280;
+st1280:
+ if ( ++p == pe )
+ goto _test_eof1280;
+case 1280:
+ if ( (*p) == 61 )
+ goto st1291;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3875;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3875;
+ } else
+ goto tr3875;
+ goto tr3867;
+tr3875:
+ {
+ *rdata_tail += third_base32hex_to_num[(uint8_t)(*p)];
+ }
+ goto st1281;
+st1281:
+ if ( ++p == pe )
+ goto _test_eof1281;
+case 1281:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3877;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3877;
+ } else
+ goto tr3877;
+ goto tr3867;
+tr3877:
+ {
+ *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1282;
+st1282:
+ if ( ++p == pe )
+ goto _test_eof1282;
+case 1282:
+ if ( (*p) == 61 )
+ goto st1290;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3878;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3878;
+ } else
+ goto tr3878;
+ goto tr3867;
+tr3878:
+ {
+ *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1283;
+st1283:
+ if ( ++p == pe )
+ goto _test_eof1283;
+case 1283:
+ if ( (*p) == 61 )
+ goto st1288;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3880;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3880;
+ } else
+ goto tr3880;
+ goto tr3867;
+tr3880:
+ {
+ *rdata_tail += sixth_base32hex_to_num[(uint8_t)(*p)];
+ }
+ goto st1284;
+st1284:
+ if ( ++p == pe )
+ goto _test_eof1284;
+case 1284:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3882;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3882;
+ } else
+ goto tr3882;
+ goto tr3867;
+tr3882:
+ {
+ *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1285;
+st1285:
+ if ( ++p == pe )
+ goto _test_eof1285;
+case 1285:
+ if ( (*p) == 61 )
+ goto st1286;
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3883;
+ } else if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3883;
+ } else
+ goto tr3883;
+ goto tr3867;
+tr3883:
+ {
+ *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)(*p)];
+ }
+ goto st1286;
+st1286:
+ if ( ++p == pe )
+ goto _test_eof1286;
+case 1286:
+ switch( (*p) ) {
+ case 32: goto tr3885;
+ case 59: goto tr3885;
+ }
+ if ( (*p) < 48 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3885;
+ } else if ( (*p) >= 9 )
+ goto tr3885;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 86 ) {
+ if ( 97 <= (*p) && (*p) <= 118 )
+ goto tr3886;
+ } else if ( (*p) >= 65 )
+ goto tr3886;
+ } else
+ goto tr3886;
+ goto tr3867;
+tr3885:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 1287;goto st376;} }
+ goto st1287;
+st1287:
+ if ( ++p == pe )
+ goto _test_eof1287;
+case 1287:
+ switch( (*p) ) {
+ case 32: goto tr3887;
+ case 59: goto tr3887;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3887;
+ } else if ( (*p) >= 9 )
+ goto tr3887;
+ goto tr75;
+tr3887:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1477;
+st1477:
+ if ( ++p == pe )
+ goto _test_eof1477;
+case 1477:
+ goto st0;
+st1288:
+ if ( ++p == pe )
+ goto _test_eof1288;
+case 1288:
+ if ( (*p) == 61 )
+ goto st1289;
+ goto tr3867;
+st1289:
+ if ( ++p == pe )
+ goto _test_eof1289;
+case 1289:
+ if ( (*p) == 61 )
+ goto st1286;
+ goto tr3867;
+st1290:
+ if ( ++p == pe )
+ goto _test_eof1290;
+case 1290:
+ if ( (*p) == 61 )
+ goto st1288;
+ goto tr3867;
+st1291:
+ if ( ++p == pe )
+ goto _test_eof1291;
+case 1291:
+ if ( (*p) == 61 )
+ goto st1292;
+ goto tr3867;
+st1292:
+ if ( ++p == pe )
+ goto _test_eof1292;
+case 1292:
+ if ( (*p) == 61 )
+ goto st1290;
+ goto tr3867;
+tr3873:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1293;
+tr3890:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1293;
+tr3866:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1293;
+st1293:
+ if ( ++p == pe )
+ goto _test_eof1293;
+case 1293:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3891;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3890;
+ goto tr75;
+tr3893:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1294;
+tr3859:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1294;
+st1294:
+ if ( ++p == pe )
+ goto _test_eof1294;
+case 1294:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3892;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3892;
+ } else
+ goto tr3892;
+ goto tr2998;
+tr3892:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1295;
+st1295:
+ if ( ++p == pe )
+ goto _test_eof1295;
+case 1295:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3862;
+ case 32: goto tr3862;
+ case 40: goto tr3863;
+ case 41: goto tr3864;
+ case 2058: goto tr3865;
+ case 2107: goto tr3866;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3893;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3893;
+ } else
+ goto tr3893;
+ goto tr2998;
+tr3861:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1296;
+tr3894:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1296;
+tr3854:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1296;
+st1296:
+ if ( ++p == pe )
+ goto _test_eof1296;
+case 1296:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3895;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3894;
+ goto tr75;
+tr3848:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1297;
+tr3896:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1297;
+tr3842:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1297;
+st1297:
+ if ( ++p == pe )
+ goto _test_eof1297;
+case 1297:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3897;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3896;
+ goto tr75;
+tr3836:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1298;
+tr3898:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1298;
+tr3830:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1298;
+st1298:
+ if ( ++p == pe )
+ goto _test_eof1298;
+case 1298:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3899;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3898;
+ goto tr75;
+st1299:
+ if ( ++p == pe )
+ goto _test_eof1299;
+case 1299:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3900;
+ goto tr2327;
+tr3900:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1300;
+tr3904:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1300;
+st1300:
+ if ( ++p == pe )
+ goto _test_eof1300;
+case 1300:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3901;
+ case 32: goto tr3901;
+ case 40: goto tr3902;
+ case 41: goto tr3903;
+ case 2058: goto tr3905;
+ case 2107: goto tr3906;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3904;
+ goto tr2327;
+tr3908:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1301;
+tr3909:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1301;
+tr3911:
+ {
+ s->line_counter++;
+ }
+ goto st1301;
+tr3946:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1301; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1301;
+tr3901:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1301;
+tr3902:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1301;
+tr3903:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1301;
+tr3905:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1301;
+st1301:
+ if ( ++p == pe )
+ goto _test_eof1301;
+case 1301:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1301;
+ case 32: goto st1301;
+ case 40: goto tr3908;
+ case 41: goto tr3909;
+ case 2058: goto tr3911;
+ case 2107: goto tr3912;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3910;
+ goto tr2327;
+tr3910:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1302;
+tr3916:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1302;
+st1302:
+ if ( ++p == pe )
+ goto _test_eof1302;
+case 1302:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3913;
+ case 32: goto tr3913;
+ case 40: goto tr3914;
+ case 41: goto tr3915;
+ case 2058: goto tr3917;
+ case 2107: goto tr3918;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3916;
+ goto tr2327;
+tr3920:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1303;
+tr3921:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1303;
+tr3923:
+ {
+ s->line_counter++;
+ }
+ goto st1303;
+tr3944:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1303; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1303;
+tr3913:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1303;
+tr3914:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1303;
+tr3915:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1303;
+tr3917:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1303;
+st1303:
+ if ( ++p == pe )
+ goto _test_eof1303;
+case 1303:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1303;
+ case 32: goto st1303;
+ case 40: goto tr3920;
+ case 41: goto tr3921;
+ case 2058: goto tr3923;
+ case 2107: goto tr3924;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3922;
+ goto tr2327;
+tr3922:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1304;
+tr3928:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1304;
+st1304:
+ if ( ++p == pe )
+ goto _test_eof1304;
+case 1304:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3925;
+ case 32: goto tr3925;
+ case 40: goto tr3926;
+ case 41: goto tr3927;
+ case 2058: goto tr3929;
+ case 2107: goto tr3930;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3928;
+ goto tr2327;
+tr3932:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1305;
+tr3933:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1305;
+tr3936:
+ {
+ s->line_counter++;
+ }
+ goto st1305;
+tr3942:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1305; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1305;
+tr3925:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1305;
+tr3926:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1305;
+tr3927:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1305;
+tr3929:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1305;
+st1305:
+ if ( ++p == pe )
+ goto _test_eof1305;
+case 1305:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1305;
+ case 32: goto st1305;
+ case 40: goto tr3932;
+ case 41: goto tr3933;
+ case 45: goto tr3934;
+ case 2058: goto tr3936;
+ case 2107: goto tr3937;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3935;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3935;
+ } else
+ goto tr3935;
+ goto tr2998;
+tr3934:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1306;
+st1306:
+ if ( ++p == pe )
+ goto _test_eof1306;
+case 1306:
+ switch( (*p) ) {
+ case 32: goto tr3938;
+ case 59: goto tr3938;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3938;
+ } else if ( (*p) >= 9 )
+ goto tr3938;
+ goto tr2998;
+tr3938:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1478;
+st1478:
+ if ( ++p == pe )
+ goto _test_eof1478;
+case 1478:
+ goto st0;
+tr3940:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1307;
+tr3935:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1307;
+st1307:
+ if ( ++p == pe )
+ goto _test_eof1307;
+case 1307:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3939;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3939;
+ } else
+ goto tr3939;
+ goto tr2998;
+tr3939:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1308;
+st1308:
+ if ( ++p == pe )
+ goto _test_eof1308;
+case 1308:
+ switch( (*p) ) {
+ case 32: goto tr3938;
+ case 59: goto tr3938;
+ }
+ if ( (*p) < 48 ) {
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr3938;
+ } else if ( (*p) >= 9 )
+ goto tr3938;
+ } else if ( (*p) > 57 ) {
+ if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3940;
+ } else if ( (*p) >= 65 )
+ goto tr3940;
+ } else
+ goto tr3940;
+ goto tr2998;
+tr3937:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1309;
+tr3941:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1309;
+tr3930:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1309;
+st1309:
+ if ( ++p == pe )
+ goto _test_eof1309;
+case 1309:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3942;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3941;
+ goto tr75;
+tr3924:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1310;
+tr3943:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1310;
+tr3918:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1310;
+st1310:
+ if ( ++p == pe )
+ goto _test_eof1310;
+case 1310:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3944;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3943;
+ goto tr75;
+tr3912:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1311;
+tr3945:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1311;
+tr3906:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1311;
+st1311:
+ if ( ++p == pe )
+ goto _test_eof1311;
+case 1311:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3946;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3945;
+ goto tr75;
+st1312:
+ if ( ++p == pe )
+ goto _test_eof1312;
+case 1312:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3947;
+ goto tr2327;
+tr3947:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1313;
+tr3951:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1313;
+st1313:
+ if ( ++p == pe )
+ goto _test_eof1313;
+case 1313:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3948;
+ case 32: goto tr3948;
+ case 40: goto tr3949;
+ case 41: goto tr3950;
+ case 2058: goto tr3952;
+ case 2107: goto tr3953;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3951;
+ goto tr2327;
+tr3955:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1314;
+tr3956:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1314;
+tr3958:
+ {
+ s->line_counter++;
+ }
+ goto st1314;
+tr4000:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1314; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1314;
+tr3948:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1314;
+tr3949:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1314;
+tr3950:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1314;
+tr3952:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1314;
+st1314:
+ if ( ++p == pe )
+ goto _test_eof1314;
+case 1314:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1314;
+ case 32: goto st1314;
+ case 40: goto tr3955;
+ case 41: goto tr3956;
+ case 2058: goto tr3958;
+ case 2107: goto tr3959;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3957;
+ goto tr2327;
+tr3957:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1315;
+tr3963:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1315;
+st1315:
+ if ( ++p == pe )
+ goto _test_eof1315;
+case 1315:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3960;
+ case 32: goto tr3960;
+ case 40: goto tr3961;
+ case 41: goto tr3962;
+ case 2058: goto tr3964;
+ case 2107: goto tr3965;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3963;
+ goto tr2327;
+tr3967:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1316;
+tr3968:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1316;
+tr3970:
+ {
+ s->line_counter++;
+ }
+ goto st1316;
+tr3998:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1316; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1316;
+tr3960:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1316;
+tr3961:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1316;
+tr3962:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1316;
+tr3964:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1316;
+st1316:
+ if ( ++p == pe )
+ goto _test_eof1316;
+case 1316:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1316;
+ case 32: goto st1316;
+ case 40: goto tr3967;
+ case 41: goto tr3968;
+ case 2058: goto tr3970;
+ case 2107: goto tr3971;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3969;
+ goto tr2327;
+tr3969:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1317;
+tr3975:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1317;
+st1317:
+ if ( ++p == pe )
+ goto _test_eof1317;
+case 1317:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr3972;
+ case 32: goto tr3972;
+ case 40: goto tr3973;
+ case 41: goto tr3974;
+ case 2058: goto tr3976;
+ case 2107: goto tr3977;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3975;
+ goto tr2327;
+tr3979:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1318;
+tr3980:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1318;
+tr3982:
+ {
+ s->line_counter++;
+ }
+ goto st1318;
+tr3996:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1318; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1318;
+tr3972:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1318;
+tr3973:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1318;
+tr3974:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1318;
+tr3976:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1318;
+st1318:
+ if ( ++p == pe )
+ goto _test_eof1318;
+case 1318:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1318;
+ case 32: goto st1318;
+ case 40: goto tr3979;
+ case 41: goto tr3980;
+ case 2058: goto tr3982;
+ case 2107: goto tr3983;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3981;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3981;
+ } else
+ goto tr3981;
+ goto tr2998;
+tr3981:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1319;
+st1319:
+ if ( ++p == pe )
+ goto _test_eof1319;
+case 1319:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr3984;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr3984;
+ } else
+ goto tr3984;
+ goto tr2998;
+tr3986:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1320;
+tr3987:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1320;
+tr3988:
+ {
+ s->line_counter++;
+ }
+ goto st1320;
+tr3994:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1320; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1320;
+tr3984:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1320;
+st1320:
+ if ( ++p == pe )
+ goto _test_eof1320;
+case 1320:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1320;
+ case 32: goto st1320;
+ case 40: goto tr3986;
+ case 41: goto tr3987;
+ case 3082: goto tr3988;
+ case 3131: goto tr3989;
+ case 3338: goto tr3990;
+ case 3387: goto tr3990;
+ case 3594: goto tr3991;
+ case 3643: goto tr3992;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3981;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3981;
+ } else
+ goto tr3981;
+ goto tr2998;
+tr3989:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1321;
+tr3993:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1321;
+st1321:
+ if ( ++p == pe )
+ goto _test_eof1321;
+case 1321:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3994;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3993;
+ goto tr2998;
+tr3990:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1479;
+st1479:
+ if ( ++p == pe )
+ goto _test_eof1479;
+case 1479:
+ goto st0;
+tr3991:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1480;
+st1480:
+ if ( ++p == pe )
+ goto _test_eof1480;
+case 1480:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1320;
+ case 32: goto st1320;
+ case 40: goto tr3986;
+ case 41: goto tr3987;
+ case 3082: goto tr3988;
+ case 3131: goto tr3989;
+ case 3338: goto tr3990;
+ case 3387: goto tr3990;
+ case 3594: goto tr3991;
+ case 3643: goto tr3992;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr3981;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr3981;
+ } else
+ goto tr3981;
+ goto tr2998;
+tr3992:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1481;
+st1481:
+ if ( ++p == pe )
+ goto _test_eof1481;
+case 1481:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3994;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3993;
+ goto tr2998;
+tr3983:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1322;
+tr3995:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1322;
+tr3977:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1322;
+st1322:
+ if ( ++p == pe )
+ goto _test_eof1322;
+case 1322:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3996;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3995;
+ goto tr75;
+tr3971:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1323;
+tr3997:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1323;
+tr3965:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1323;
+st1323:
+ if ( ++p == pe )
+ goto _test_eof1323;
+case 1323:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr3998;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3997;
+ goto tr75;
+tr3959:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1324;
+tr3999:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1324;
+tr3953:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1324;
+st1324:
+ if ( ++p == pe )
+ goto _test_eof1324;
+case 1324:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4000;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr3999;
+ goto tr75;
+st1325:
+ if ( ++p == pe )
+ goto _test_eof1325;
+case 1325:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4001;
+ goto tr2327;
+tr4001:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1326;
+tr4005:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1326;
+st1326:
+ if ( ++p == pe )
+ goto _test_eof1326;
+case 1326:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4002;
+ case 32: goto tr4002;
+ case 40: goto tr4003;
+ case 41: goto tr4004;
+ case 2058: goto tr4006;
+ case 2107: goto tr4007;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4005;
+ goto tr2327;
+tr4009:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1327;
+tr4010:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1327;
+tr4012:
+ {
+ s->line_counter++;
+ }
+ goto st1327;
+tr4018:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1327; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1327;
+tr4002:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1327;
+tr4003:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1327;
+tr4004:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1327;
+tr4006:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1327;
+st1327:
+ if ( ++p == pe )
+ goto _test_eof1327;
+case 1327:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1327;
+ case 32: goto st1327;
+ case 40: goto tr4009;
+ case 41: goto tr4010;
+ case 2058: goto tr4012;
+ case 2107: goto tr4013;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4011;
+ goto tr2327;
+tr4011:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1328;
+tr4015:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1328;
+st1328:
+ if ( ++p == pe )
+ goto _test_eof1328;
+case 1328:
+ switch( (*p) ) {
+ case 32: goto tr4014;
+ case 59: goto tr4014;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr4014;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4015;
+ } else
+ goto tr4014;
+ goto tr2327;
+tr4014:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 1329;goto st376;} }
+ goto st1329;
+st1329:
+ if ( ++p == pe )
+ goto _test_eof1329;
+case 1329:
+ switch( (*p) ) {
+ case 32: goto tr4016;
+ case 59: goto tr4016;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4016;
+ } else if ( (*p) >= 9 )
+ goto tr4016;
+ goto tr75;
+tr4016:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1482;
+st1482:
+ if ( ++p == pe )
+ goto _test_eof1482;
+case 1482:
+ goto st0;
+tr4013:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1330;
+tr4017:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1330;
+tr4007:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1330;
+st1330:
+ if ( ++p == pe )
+ goto _test_eof1330;
+case 1330:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4018;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4017;
+ goto tr75;
+st1331:
+ if ( ++p == pe )
+ goto _test_eof1331;
+case 1331:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4019;
+ goto tr2327;
+tr4019:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1332;
+tr4023:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1332;
+st1332:
+ if ( ++p == pe )
+ goto _test_eof1332;
+case 1332:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4020;
+ case 32: goto tr4020;
+ case 40: goto tr4021;
+ case 41: goto tr4022;
+ case 2058: goto tr4024;
+ case 2107: goto tr4025;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4023;
+ goto tr2327;
+tr4027:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1333;
+tr4028:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1333;
+tr4030:
+ {
+ s->line_counter++;
+ }
+ goto st1333;
+tr4072:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1333; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1333;
+tr4020:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1333;
+tr4021:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1333;
+tr4022:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1333;
+tr4024:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1333;
+st1333:
+ if ( ++p == pe )
+ goto _test_eof1333;
+case 1333:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1333;
+ case 32: goto st1333;
+ case 40: goto tr4027;
+ case 41: goto tr4028;
+ case 2058: goto tr4030;
+ case 2107: goto tr4031;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4029;
+ goto tr2327;
+tr4029:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1334;
+tr4035:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1334;
+st1334:
+ if ( ++p == pe )
+ goto _test_eof1334;
+case 1334:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4032;
+ case 32: goto tr4032;
+ case 40: goto tr4033;
+ case 41: goto tr4034;
+ case 2058: goto tr4036;
+ case 2107: goto tr4037;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4035;
+ goto tr2327;
+tr4039:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1335;
+tr4040:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1335;
+tr4042:
+ {
+ s->line_counter++;
+ }
+ goto st1335;
+tr4070:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1335; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1335;
+tr4032:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1335;
+tr4033:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1335;
+tr4034:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1335;
+tr4036:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1335;
+st1335:
+ if ( ++p == pe )
+ goto _test_eof1335;
+case 1335:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1335;
+ case 32: goto st1335;
+ case 40: goto tr4039;
+ case 41: goto tr4040;
+ case 2058: goto tr4042;
+ case 2107: goto tr4043;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4041;
+ goto tr2327;
+tr4041:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1336;
+tr4047:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1336;
+st1336:
+ if ( ++p == pe )
+ goto _test_eof1336;
+case 1336:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4044;
+ case 32: goto tr4044;
+ case 40: goto tr4045;
+ case 41: goto tr4046;
+ case 2058: goto tr4048;
+ case 2107: goto tr4049;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4047;
+ goto tr2327;
+tr4051:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1337;
+tr4052:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1337;
+tr4054:
+ {
+ s->line_counter++;
+ }
+ goto st1337;
+tr4068:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1337; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1337;
+tr4044:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1337;
+tr4045:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1337;
+tr4046:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1337;
+tr4048:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1337;
+st1337:
+ if ( ++p == pe )
+ goto _test_eof1337;
+case 1337:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1337;
+ case 32: goto st1337;
+ case 40: goto tr4051;
+ case 41: goto tr4052;
+ case 2058: goto tr4054;
+ case 2107: goto tr4055;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4053;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr4053;
+ } else
+ goto tr4053;
+ goto tr2998;
+tr4053:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1338;
+st1338:
+ if ( ++p == pe )
+ goto _test_eof1338;
+case 1338:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4056;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4056;
+ } else
+ goto tr4056;
+ goto tr2998;
+tr4058:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1339;
+tr4059:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1339;
+tr4060:
+ {
+ s->line_counter++;
+ }
+ goto st1339;
+tr4066:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1339; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1339;
+tr4056:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1339;
+st1339:
+ if ( ++p == pe )
+ goto _test_eof1339;
+case 1339:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1339;
+ case 32: goto st1339;
+ case 40: goto tr4058;
+ case 41: goto tr4059;
+ case 3082: goto tr4060;
+ case 3131: goto tr4061;
+ case 3338: goto tr4062;
+ case 3387: goto tr4062;
+ case 3594: goto tr4063;
+ case 3643: goto tr4064;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4053;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr4053;
+ } else
+ goto tr4053;
+ goto tr2998;
+tr4061:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1340;
+tr4065:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1340;
+st1340:
+ if ( ++p == pe )
+ goto _test_eof1340;
+case 1340:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4066;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4065;
+ goto tr2998;
+tr4062:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1483;
+st1483:
+ if ( ++p == pe )
+ goto _test_eof1483;
+case 1483:
+ goto st0;
+tr4063:
+ {
+ s->line_counter++;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1484;
+st1484:
+ if ( ++p == pe )
+ goto _test_eof1484;
+case 1484:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ }
+ switch( _widec ) {
+ case 9: goto st1339;
+ case 32: goto st1339;
+ case 40: goto tr4058;
+ case 41: goto tr4059;
+ case 3082: goto tr4060;
+ case 3131: goto tr4061;
+ case 3338: goto tr4062;
+ case 3387: goto tr4062;
+ case 3594: goto tr4063;
+ case 3643: goto tr4064;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4053;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr4053;
+ } else
+ goto tr4053;
+ goto tr2998;
+tr4064:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1485;
+st1485:
+ if ( ++p == pe )
+ goto _test_eof1485;
+case 1485:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4066;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4065;
+ goto tr2998;
+tr4055:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1341;
+tr4067:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1341;
+tr4049:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1341;
+st1341:
+ if ( ++p == pe )
+ goto _test_eof1341;
+case 1341:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4068;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4067;
+ goto tr75;
+tr4043:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1342;
+tr4069:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1342;
+tr4037:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1342;
+st1342:
+ if ( ++p == pe )
+ goto _test_eof1342;
+case 1342:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4070;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4069;
+ goto tr75;
+tr4031:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1343;
+tr4071:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1343;
+tr4025:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1343;
+st1343:
+ if ( ++p == pe )
+ goto _test_eof1343;
+case 1343:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4072;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4071;
+ goto tr75;
+st1344:
+ if ( ++p == pe )
+ goto _test_eof1344;
+case 1344:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4073;
+ goto tr2327;
+tr4073:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1345;
+tr4077:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1345;
+st1345:
+ if ( ++p == pe )
+ goto _test_eof1345;
+case 1345:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4074;
+ case 32: goto tr4074;
+ case 40: goto tr4075;
+ case 41: goto tr4076;
+ case 2058: goto tr4078;
+ case 2107: goto tr4079;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4077;
+ goto tr2327;
+tr4081:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1346;
+tr4082:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1346;
+tr4084:
+ {
+ s->line_counter++;
+ }
+ goto st1346;
+tr4089:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1346; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1346;
+tr4074:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1346;
+tr4075:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1346;
+tr4076:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1346;
+tr4078:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1346;
+st1346:
+ if ( ++p == pe )
+ goto _test_eof1346;
+case 1346:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1346;
+ case 32: goto st1346;
+ case 40: goto tr4081;
+ case 41: goto tr4082;
+ case 46: goto tr4083;
+ case 2058: goto tr4084;
+ case 2107: goto tr4085;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4083;
+ goto tr2304;
+tr4087:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1347;
+tr4083:
+ {
+ s->buffer_length = 0;
+ }
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1347;
+st1347:
+ if ( ++p == pe )
+ goto _test_eof1347;
+case 1347:
+ switch( (*p) ) {
+ case 32: goto tr4086;
+ case 46: goto tr4087;
+ case 59: goto tr4086;
+ }
+ if ( (*p) < 40 ) {
+ if ( 9 <= (*p) && (*p) <= 10 )
+ goto tr4086;
+ } else if ( (*p) > 41 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4087;
+ } else
+ goto tr4086;
+ goto tr2304;
+tr4086:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1486;
+st1486:
+ if ( ++p == pe )
+ goto _test_eof1486;
+case 1486:
+ goto st0;
+tr4085:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1348;
+tr4088:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1348;
+tr4079:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1348;
+st1348:
+ if ( ++p == pe )
+ goto _test_eof1348;
+case 1348:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4089;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4088;
+ goto tr75;
+st1349:
+ if ( ++p == pe )
+ goto _test_eof1349;
+case 1349:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4090;
+ goto tr2327;
+tr4090:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1350;
+tr4094:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1350;
+st1350:
+ if ( ++p == pe )
+ goto _test_eof1350;
+case 1350:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4091;
+ case 32: goto tr4091;
+ case 40: goto tr4092;
+ case 41: goto tr4093;
+ case 2058: goto tr4095;
+ case 2107: goto tr4096;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4094;
+ goto tr2327;
+tr4098:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1351;
+tr4099:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1351;
+tr4101:
+ {
+ s->line_counter++;
+ }
+ goto st1351;
+tr4115:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1351; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1351;
+tr4091:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1351;
+tr4092:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1351;
+tr4093:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1351;
+tr4095:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1351;
+st1351:
+ if ( ++p == pe )
+ goto _test_eof1351;
+case 1351:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1351;
+ case 32: goto st1351;
+ case 40: goto tr4098;
+ case 41: goto tr4099;
+ case 2058: goto tr4101;
+ case 2107: goto tr4102;
+ }
+ if ( _widec < 65 ) {
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4100;
+ } else if ( _widec > 70 ) {
+ if ( 97 <= _widec && _widec <= 102 )
+ goto tr4100;
+ } else
+ goto tr4100;
+ goto tr2998;
+tr4100:
+ {
+ s->item_length = 0;
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1352;
+st1352:
+ if ( ++p == pe )
+ goto _test_eof1352;
+case 1352:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4103;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4103;
+ } else
+ goto tr4103;
+ goto tr2998;
+tr4103:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1353;
+st1353:
+ if ( ++p == pe )
+ goto _test_eof1353;
+case 1353:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4104;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4104;
+ } else
+ goto tr4104;
+ goto tr2998;
+tr4104:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1354;
+st1354:
+ if ( ++p == pe )
+ goto _test_eof1354;
+case 1354:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4105;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4105;
+ } else
+ goto tr4105;
+ goto tr2998;
+tr4105:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1355;
+st1355:
+ if ( ++p == pe )
+ goto _test_eof1355;
+case 1355:
+ if ( (*p) == 58 )
+ goto tr4107;
+ goto tr4106;
+tr4107:
+ {
+ s->item_length++;
+ }
+ goto st1356;
+st1356:
+ if ( ++p == pe )
+ goto _test_eof1356;
+case 1356:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4108;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4108;
+ } else
+ goto tr4108;
+ goto tr2998;
+tr4108:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1357;
+st1357:
+ if ( ++p == pe )
+ goto _test_eof1357;
+case 1357:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4109;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4109;
+ } else
+ goto tr4109;
+ goto tr2998;
+tr4109:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1358;
+st1358:
+ if ( ++p == pe )
+ goto _test_eof1358;
+case 1358:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4110;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4110;
+ } else
+ goto tr4110;
+ goto tr2998;
+tr4110:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1359;
+st1359:
+ if ( ++p == pe )
+ goto _test_eof1359;
+case 1359:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4111;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4111;
+ } else
+ goto tr4111;
+ goto tr2998;
+tr4111:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1360;
+st1360:
+ if ( ++p == pe )
+ goto _test_eof1360;
+case 1360:
+ switch( (*p) ) {
+ case 32: goto tr4113;
+ case 58: goto tr4107;
+ case 59: goto tr4113;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4113;
+ } else if ( (*p) >= 9 )
+ goto tr4113;
+ goto tr4112;
+tr4113:
+ {
+ s->item_length++;
+ }
+ {
+ if (s->item_length != 4) {
+ WARN(ZS_BAD_L64_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1487;
+st1487:
+ if ( ++p == pe )
+ goto _test_eof1487;
+case 1487:
+ goto st0;
+tr4102:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1361;
+tr4114:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1361;
+tr4096:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1361;
+st1361:
+ if ( ++p == pe )
+ goto _test_eof1361;
+case 1361:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4115;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4114;
+ goto tr75;
+st1362:
+ if ( ++p == pe )
+ goto _test_eof1362;
+case 1362:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4116;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4116;
+ } else
+ goto tr4116;
+ goto tr2998;
+tr4116:
+ {
+ s->item_length = 0;
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1363;
+st1363:
+ if ( ++p == pe )
+ goto _test_eof1363;
+case 1363:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4117;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4117;
+ } else
+ goto tr4117;
+ goto tr2998;
+tr4117:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1364;
+st1364:
+ if ( ++p == pe )
+ goto _test_eof1364;
+case 1364:
+ if ( (*p) == 45 )
+ goto tr4119;
+ goto tr4118;
+tr4119:
+ {
+ s->item_length++;
+ }
+ goto st1365;
+st1365:
+ if ( ++p == pe )
+ goto _test_eof1365;
+case 1365:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4120;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4120;
+ } else
+ goto tr4120;
+ goto tr2998;
+tr4120:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1366;
+st1366:
+ if ( ++p == pe )
+ goto _test_eof1366;
+case 1366:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4121;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4121;
+ } else
+ goto tr4121;
+ goto tr2998;
+tr4121:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1367;
+st1367:
+ if ( ++p == pe )
+ goto _test_eof1367;
+case 1367:
+ switch( (*p) ) {
+ case 32: goto tr4122;
+ case 45: goto tr4119;
+ case 59: goto tr4122;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4122;
+ } else if ( (*p) >= 9 )
+ goto tr4122;
+ goto tr4118;
+tr4122:
+ {
+ s->item_length++;
+ }
+ {
+ if (s->item_length != 6) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1488;
+st1488:
+ if ( ++p == pe )
+ goto _test_eof1488;
+case 1488:
+ goto st0;
+st1368:
+ if ( ++p == pe )
+ goto _test_eof1368;
+case 1368:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4123;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4123;
+ } else
+ goto tr4123;
+ goto tr2998;
+tr4123:
+ {
+ s->item_length = 0;
+ }
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1369;
+st1369:
+ if ( ++p == pe )
+ goto _test_eof1369;
+case 1369:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4124;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4124;
+ } else
+ goto tr4124;
+ goto tr2998;
+tr4124:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1370;
+st1370:
+ if ( ++p == pe )
+ goto _test_eof1370;
+case 1370:
+ if ( (*p) == 45 )
+ goto tr4125;
+ goto tr4118;
+tr4125:
+ {
+ s->item_length++;
+ }
+ goto st1371;
+st1371:
+ if ( ++p == pe )
+ goto _test_eof1371;
+case 1371:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4126;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4126;
+ } else
+ goto tr4126;
+ goto tr2998;
+tr4126:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1372;
+st1372:
+ if ( ++p == pe )
+ goto _test_eof1372;
+case 1372:
+ if ( (*p) < 65 ) {
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4127;
+ } else if ( (*p) > 70 ) {
+ if ( 97 <= (*p) && (*p) <= 102 )
+ goto tr4127;
+ } else
+ goto tr4127;
+ goto tr2998;
+tr4127:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ goto st1373;
+st1373:
+ if ( ++p == pe )
+ goto _test_eof1373;
+case 1373:
+ switch( (*p) ) {
+ case 32: goto tr4128;
+ case 45: goto tr4125;
+ case 59: goto tr4128;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4128;
+ } else if ( (*p) >= 9 )
+ goto tr4128;
+ goto tr4118;
+tr4128:
+ {
+ s->item_length++;
+ }
+ {
+ if (s->item_length != 8) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ p--; {goto st307;}
+ }
+ }
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1489;
+st1489:
+ if ( ++p == pe )
+ goto _test_eof1489;
+case 1489:
+ goto st0;
+st1374:
+ if ( ++p == pe )
+ goto _test_eof1374;
+case 1374:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4129;
+ goto tr2327;
+tr4129:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1375;
+tr4133:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1375;
+st1375:
+ if ( ++p == pe )
+ goto _test_eof1375;
+case 1375:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4130;
+ case 32: goto tr4130;
+ case 40: goto tr4131;
+ case 41: goto tr4132;
+ case 2058: goto tr4134;
+ case 2107: goto tr4135;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4133;
+ goto tr2327;
+tr4137:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1376;
+tr4138:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1376;
+tr4140:
+ {
+ s->line_counter++;
+ }
+ goto st1376;
+tr4158:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1376; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1376;
+tr4130:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1376;
+tr4131:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1376;
+tr4132:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1376;
+tr4134:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1376;
+st1376:
+ if ( ++p == pe )
+ goto _test_eof1376;
+case 1376:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1376;
+ case 32: goto st1376;
+ case 40: goto tr4137;
+ case 41: goto tr4138;
+ case 2058: goto tr4140;
+ case 2107: goto tr4141;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4139;
+ goto tr2327;
+tr4139:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1377;
+tr4145:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1377;
+st1377:
+ if ( ++p == pe )
+ goto _test_eof1377;
+case 1377:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4142;
+ case 32: goto tr4142;
+ case 40: goto tr4143;
+ case 41: goto tr4144;
+ case 2058: goto tr4146;
+ case 2107: goto tr4147;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4145;
+ goto tr2327;
+tr4150:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1378;
+tr4151:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1378;
+tr4152:
+ {
+ s->line_counter++;
+ }
+ goto st1378;
+tr4156:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1378; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1378;
+tr4142:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1378;
+tr4143:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1378;
+tr4144:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1378;
+tr4146:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1378;
+st1378:
+ if ( ++p == pe )
+ goto _test_eof1378;
+case 1378:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1378;
+ case 32: goto st1378;
+ case 40: goto tr4150;
+ case 41: goto tr4151;
+ case 2058: goto tr4152;
+ case 2107: goto tr4153;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr4148;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr4148;
+ } else
+ goto tr4148;
+ goto tr75;
+tr4148:
+ { p--; {stack[top++] = 1379;goto st318;} }
+ goto st1379;
+st1379:
+ if ( ++p == pe )
+ goto _test_eof1379;
+case 1379:
+ switch( (*p) ) {
+ case 32: goto tr4154;
+ case 59: goto tr4154;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4154;
+ } else if ( (*p) >= 9 )
+ goto tr4154;
+ goto tr75;
+tr4154:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1490;
+st1490:
+ if ( ++p == pe )
+ goto _test_eof1490;
+case 1490:
+ goto st0;
+tr4153:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1380;
+tr4155:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1380;
+tr4147:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1380;
+st1380:
+ if ( ++p == pe )
+ goto _test_eof1380;
+case 1380:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4156;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4155;
+ goto tr75;
+tr4141:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1381;
+tr4157:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1381;
+tr4135:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1381;
+st1381:
+ if ( ++p == pe )
+ goto _test_eof1381;
+case 1381:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4158;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4157;
+ goto tr75;
+st1382:
+ if ( ++p == pe )
+ goto _test_eof1382;
+case 1382:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4159;
+ goto tr2327;
+tr4159:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1383;
+tr4163:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1383;
+st1383:
+ if ( ++p == pe )
+ goto _test_eof1383;
+case 1383:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4160;
+ case 32: goto tr4160;
+ case 40: goto tr4161;
+ case 41: goto tr4162;
+ case 2058: goto tr4164;
+ case 2107: goto tr4165;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4163;
+ goto tr2327;
+tr4168:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1384;
+tr4169:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1384;
+tr4170:
+ {
+ s->line_counter++;
+ }
+ goto st1384;
+tr4187:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1384; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1384;
+tr4160:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1384;
+tr4161:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1384;
+tr4162:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1384;
+tr4164:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1384;
+st1384:
+ if ( ++p == pe )
+ goto _test_eof1384;
+case 1384:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1384;
+ case 32: goto st1384;
+ case 40: goto tr4168;
+ case 41: goto tr4169;
+ case 2058: goto tr4170;
+ case 2107: goto tr4171;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr4166;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr4166;
+ } else
+ goto tr4166;
+ goto tr75;
+tr4166:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ { p--; {stack[top++] = 1385;goto st318;} }
+ goto st1385;
+st1385:
+ if ( ++p == pe )
+ goto _test_eof1385;
+case 1385:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4172;
+ case 32: goto tr4172;
+ case 40: goto tr4173;
+ case 41: goto tr4174;
+ case 2058: goto tr4175;
+ case 2107: goto tr4176;
+ }
+ goto tr75;
+tr4179:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1386;
+tr4180:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1386;
+tr4181:
+ {
+ s->line_counter++;
+ }
+ goto st1386;
+tr4185:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1386; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1386;
+tr4172:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1386;
+tr4173:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1386;
+tr4174:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1386;
+tr4175:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1386;
+st1386:
+ if ( ++p == pe )
+ goto _test_eof1386;
+case 1386:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1386;
+ case 32: goto st1386;
+ case 40: goto tr4179;
+ case 41: goto tr4180;
+ case 2058: goto tr4181;
+ case 2107: goto tr4182;
+ }
+ if ( _widec < 11 ) {
+ if ( _widec <= 8 )
+ goto tr4177;
+ } else if ( _widec > 58 ) {
+ if ( 60 <= _widec )
+ goto tr4177;
+ } else
+ goto tr4177;
+ goto tr75;
+tr4177:
+ { p--; {stack[top++] = 1387;goto st318;} }
+ goto st1387;
+st1387:
+ if ( ++p == pe )
+ goto _test_eof1387;
+case 1387:
+ switch( (*p) ) {
+ case 32: goto tr4183;
+ case 59: goto tr4183;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4183;
+ } else if ( (*p) >= 9 )
+ goto tr4183;
+ goto tr75;
+tr4183:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1491;
+st1491:
+ if ( ++p == pe )
+ goto _test_eof1491;
+case 1491:
+ goto st0;
+tr4182:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1388;
+tr4184:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1388;
+tr4176:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {goto st307;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1388;
+st1388:
+ if ( ++p == pe )
+ goto _test_eof1388;
+case 1388:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4185;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4184;
+ goto tr75;
+tr4171:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1389;
+tr4186:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1389;
+tr4165:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1389;
+st1389:
+ if ( ++p == pe )
+ goto _test_eof1389;
+case 1389:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4187;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4186;
+ goto tr75;
+st1390:
+ if ( ++p == pe )
+ goto _test_eof1390;
+case 1390:
+ if ( 48 <= (*p) && (*p) <= 57 )
+ goto tr4188;
+ goto tr2327;
+tr4188:
+ {
+ s->number64 = 0;
+ }
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1391;
+tr4192:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1391;
+st1391:
+ if ( ++p == pe )
+ goto _test_eof1391;
+case 1391:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto tr4189;
+ case 32: goto tr4189;
+ case 40: goto tr4190;
+ case 41: goto tr4191;
+ case 2058: goto tr4193;
+ case 2107: goto tr4194;
+ }
+ if ( 48 <= _widec && _widec <= 57 )
+ goto tr4192;
+ goto tr2327;
+tr4196:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1392;
+tr4197:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1392;
+tr4199:
+ {
+ s->line_counter++;
+ }
+ goto st1392;
+tr4204:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; cs = 1392; goto _out;}
+ }
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1392;
+tr4189:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ goto st1392;
+tr4190:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = true;
+ }
+ goto st1392;
+tr4191:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {goto st307;}
+ }
+ s->multiline = false;
+ }
+ goto st1392;
+tr4193:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->line_counter++;
+ }
+ goto st1392;
+st1392:
+ if ( ++p == pe )
+ goto _test_eof1392;
+case 1392:
+ _widec = (*p);
+ if ( (*p) > 10 ) {
+ if ( 59 <= (*p) && (*p) <= 59 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) >= 10 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ switch( _widec ) {
+ case 9: goto st1392;
+ case 32: goto st1392;
+ case 40: goto tr4196;
+ case 41: goto tr4197;
+ case 42: goto tr4198;
+ case 92: goto tr4198;
+ case 95: goto tr4198;
+ case 2058: goto tr4199;
+ case 2107: goto tr4200;
+ }
+ if ( _widec < 64 ) {
+ if ( 45 <= _widec && _widec <= 57 )
+ goto tr4198;
+ } else if ( _widec > 90 ) {
+ if ( 97 <= _widec && _widec <= 122 )
+ goto tr4198;
+ } else
+ goto tr4198;
+ goto tr75;
+tr4198:
+ {
+ s->dname = rdata_tail;
+ }
+ { p--; {stack[top++] = 1393;goto st309;} }
+ goto st1393;
+st1393:
+ if ( ++p == pe )
+ goto _test_eof1393;
+case 1393:
+ switch( (*p) ) {
+ case 32: goto tr4201;
+ case 59: goto tr4201;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4201;
+ } else if ( (*p) >= 9 )
+ goto tr4201;
+ goto tr75;
+tr4201:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ { p--; {stack[top++] = 1394;goto st603;} }
+ goto st1394;
+st1394:
+ if ( ++p == pe )
+ goto _test_eof1394;
+case 1394:
+ switch( (*p) ) {
+ case 32: goto tr4202;
+ case 59: goto tr4202;
+ }
+ if ( (*p) > 10 ) {
+ if ( 40 <= (*p) && (*p) <= 41 )
+ goto tr4202;
+ } else if ( (*p) >= 9 )
+ goto tr4202;
+ goto tr75;
+tr4202:
+ {
+ p--; {cs = stack[--top];goto _again;}
+ }
+ goto st1492;
+st1492:
+ if ( ++p == pe )
+ goto _test_eof1492;
+case 1492:
+ goto st0;
+tr4200:
+ {
+ s->buffer_length = 0;
+ }
+ goto st1395;
+tr4203:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ goto st1395;
+tr4194:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {goto st307;}
+ }
+ }
+ {
+ s->buffer_length = 0;
+ }
+ goto st1395;
+st1395:
+ if ( ++p == pe )
+ goto _test_eof1395;
+case 1395:
+ _widec = (*p);
+ if ( (*p) < 10 ) {
+ if ( (*p) <= 9 ) {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else if ( (*p) > 10 ) {
+ if ( 11 <= (*p) )
+ { _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ } else {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ }
+ if ( _widec == 2058 )
+ goto tr4204;
+ if ( 1920 <= _widec && _widec <= 2175 )
+ goto tr4203;
+ goto tr75;
+ }
+ _test_eof1396: cs = 1396; goto _test_eof;
+ _test_eof1: cs = 1; goto _test_eof;
+ _test_eof2: cs = 2; goto _test_eof;
+ _test_eof3: cs = 3; goto _test_eof;
+ _test_eof4: cs = 4; goto _test_eof;
+ _test_eof5: cs = 5; goto _test_eof;
+ _test_eof6: cs = 6; goto _test_eof;
+ _test_eof7: cs = 7; goto _test_eof;
+ _test_eof8: cs = 8; goto _test_eof;
+ _test_eof9: cs = 9; goto _test_eof;
+ _test_eof10: cs = 10; goto _test_eof;
+ _test_eof11: cs = 11; goto _test_eof;
+ _test_eof12: cs = 12; goto _test_eof;
+ _test_eof13: cs = 13; goto _test_eof;
+ _test_eof1397: cs = 1397; goto _test_eof;
+ _test_eof14: cs = 14; goto _test_eof;
+ _test_eof15: cs = 15; goto _test_eof;
+ _test_eof16: cs = 16; goto _test_eof;
+ _test_eof17: cs = 17; goto _test_eof;
+ _test_eof18: cs = 18; goto _test_eof;
+ _test_eof19: cs = 19; goto _test_eof;
+ _test_eof20: cs = 20; goto _test_eof;
+ _test_eof21: cs = 21; goto _test_eof;
+ _test_eof22: cs = 22; goto _test_eof;
+ _test_eof23: cs = 23; goto _test_eof;
+ _test_eof24: cs = 24; goto _test_eof;
+ _test_eof25: cs = 25; goto _test_eof;
+ _test_eof26: cs = 26; goto _test_eof;
+ _test_eof27: cs = 27; goto _test_eof;
+ _test_eof28: cs = 28; goto _test_eof;
+ _test_eof29: cs = 29; goto _test_eof;
+ _test_eof30: cs = 30; goto _test_eof;
+ _test_eof31: cs = 31; goto _test_eof;
+ _test_eof32: cs = 32; goto _test_eof;
+ _test_eof33: cs = 33; goto _test_eof;
+ _test_eof34: cs = 34; goto _test_eof;
+ _test_eof35: cs = 35; goto _test_eof;
+ _test_eof36: cs = 36; goto _test_eof;
+ _test_eof37: cs = 37; goto _test_eof;
+ _test_eof38: cs = 38; goto _test_eof;
+ _test_eof39: cs = 39; goto _test_eof;
+ _test_eof40: cs = 40; goto _test_eof;
+ _test_eof41: cs = 41; goto _test_eof;
+ _test_eof42: cs = 42; goto _test_eof;
+ _test_eof43: cs = 43; goto _test_eof;
+ _test_eof44: cs = 44; goto _test_eof;
+ _test_eof45: cs = 45; goto _test_eof;
+ _test_eof46: cs = 46; goto _test_eof;
+ _test_eof47: cs = 47; goto _test_eof;
+ _test_eof48: cs = 48; goto _test_eof;
+ _test_eof49: cs = 49; goto _test_eof;
+ _test_eof50: cs = 50; goto _test_eof;
+ _test_eof51: cs = 51; goto _test_eof;
+ _test_eof52: cs = 52; goto _test_eof;
+ _test_eof53: cs = 53; goto _test_eof;
+ _test_eof54: cs = 54; goto _test_eof;
+ _test_eof55: cs = 55; goto _test_eof;
+ _test_eof56: cs = 56; goto _test_eof;
+ _test_eof57: cs = 57; goto _test_eof;
+ _test_eof58: cs = 58; goto _test_eof;
+ _test_eof59: cs = 59; goto _test_eof;
+ _test_eof60: cs = 60; goto _test_eof;
+ _test_eof61: cs = 61; goto _test_eof;
+ _test_eof62: cs = 62; goto _test_eof;
+ _test_eof63: cs = 63; goto _test_eof;
+ _test_eof64: cs = 64; goto _test_eof;
+ _test_eof65: cs = 65; goto _test_eof;
+ _test_eof66: cs = 66; goto _test_eof;
+ _test_eof67: cs = 67; goto _test_eof;
+ _test_eof68: cs = 68; goto _test_eof;
+ _test_eof69: cs = 69; goto _test_eof;
+ _test_eof70: cs = 70; goto _test_eof;
+ _test_eof71: cs = 71; goto _test_eof;
+ _test_eof72: cs = 72; goto _test_eof;
+ _test_eof73: cs = 73; goto _test_eof;
+ _test_eof74: cs = 74; goto _test_eof;
+ _test_eof75: cs = 75; goto _test_eof;
+ _test_eof76: cs = 76; goto _test_eof;
+ _test_eof77: cs = 77; goto _test_eof;
+ _test_eof78: cs = 78; goto _test_eof;
+ _test_eof79: cs = 79; goto _test_eof;
+ _test_eof80: cs = 80; goto _test_eof;
+ _test_eof81: cs = 81; goto _test_eof;
+ _test_eof82: cs = 82; goto _test_eof;
+ _test_eof83: cs = 83; goto _test_eof;
+ _test_eof84: cs = 84; goto _test_eof;
+ _test_eof85: cs = 85; goto _test_eof;
+ _test_eof86: cs = 86; goto _test_eof;
+ _test_eof87: cs = 87; goto _test_eof;
+ _test_eof88: cs = 88; goto _test_eof;
+ _test_eof89: cs = 89; goto _test_eof;
+ _test_eof90: cs = 90; goto _test_eof;
+ _test_eof91: cs = 91; goto _test_eof;
+ _test_eof92: cs = 92; goto _test_eof;
+ _test_eof93: cs = 93; goto _test_eof;
+ _test_eof94: cs = 94; goto _test_eof;
+ _test_eof95: cs = 95; goto _test_eof;
+ _test_eof96: cs = 96; goto _test_eof;
+ _test_eof97: cs = 97; goto _test_eof;
+ _test_eof98: cs = 98; goto _test_eof;
+ _test_eof99: cs = 99; goto _test_eof;
+ _test_eof100: cs = 100; goto _test_eof;
+ _test_eof101: cs = 101; goto _test_eof;
+ _test_eof102: cs = 102; goto _test_eof;
+ _test_eof103: cs = 103; goto _test_eof;
+ _test_eof104: cs = 104; goto _test_eof;
+ _test_eof105: cs = 105; goto _test_eof;
+ _test_eof106: cs = 106; goto _test_eof;
+ _test_eof107: cs = 107; goto _test_eof;
+ _test_eof108: cs = 108; goto _test_eof;
+ _test_eof109: cs = 109; goto _test_eof;
+ _test_eof110: cs = 110; goto _test_eof;
+ _test_eof111: cs = 111; goto _test_eof;
+ _test_eof112: cs = 112; goto _test_eof;
+ _test_eof113: cs = 113; goto _test_eof;
+ _test_eof114: cs = 114; goto _test_eof;
+ _test_eof115: cs = 115; goto _test_eof;
+ _test_eof116: cs = 116; goto _test_eof;
+ _test_eof117: cs = 117; goto _test_eof;
+ _test_eof118: cs = 118; goto _test_eof;
+ _test_eof119: cs = 119; goto _test_eof;
+ _test_eof120: cs = 120; goto _test_eof;
+ _test_eof121: cs = 121; goto _test_eof;
+ _test_eof122: cs = 122; goto _test_eof;
+ _test_eof123: cs = 123; goto _test_eof;
+ _test_eof124: cs = 124; goto _test_eof;
+ _test_eof125: cs = 125; goto _test_eof;
+ _test_eof126: cs = 126; goto _test_eof;
+ _test_eof127: cs = 127; goto _test_eof;
+ _test_eof128: cs = 128; goto _test_eof;
+ _test_eof129: cs = 129; goto _test_eof;
+ _test_eof130: cs = 130; goto _test_eof;
+ _test_eof131: cs = 131; goto _test_eof;
+ _test_eof132: cs = 132; goto _test_eof;
+ _test_eof133: cs = 133; goto _test_eof;
+ _test_eof134: cs = 134; goto _test_eof;
+ _test_eof135: cs = 135; goto _test_eof;
+ _test_eof136: cs = 136; goto _test_eof;
+ _test_eof137: cs = 137; goto _test_eof;
+ _test_eof138: cs = 138; goto _test_eof;
+ _test_eof139: cs = 139; goto _test_eof;
+ _test_eof140: cs = 140; goto _test_eof;
+ _test_eof141: cs = 141; goto _test_eof;
+ _test_eof142: cs = 142; goto _test_eof;
+ _test_eof143: cs = 143; goto _test_eof;
+ _test_eof144: cs = 144; goto _test_eof;
+ _test_eof145: cs = 145; goto _test_eof;
+ _test_eof146: cs = 146; goto _test_eof;
+ _test_eof147: cs = 147; goto _test_eof;
+ _test_eof148: cs = 148; goto _test_eof;
+ _test_eof149: cs = 149; goto _test_eof;
+ _test_eof150: cs = 150; goto _test_eof;
+ _test_eof151: cs = 151; goto _test_eof;
+ _test_eof152: cs = 152; goto _test_eof;
+ _test_eof153: cs = 153; goto _test_eof;
+ _test_eof154: cs = 154; goto _test_eof;
+ _test_eof155: cs = 155; goto _test_eof;
+ _test_eof156: cs = 156; goto _test_eof;
+ _test_eof157: cs = 157; goto _test_eof;
+ _test_eof158: cs = 158; goto _test_eof;
+ _test_eof159: cs = 159; goto _test_eof;
+ _test_eof160: cs = 160; goto _test_eof;
+ _test_eof161: cs = 161; goto _test_eof;
+ _test_eof162: cs = 162; goto _test_eof;
+ _test_eof163: cs = 163; goto _test_eof;
+ _test_eof164: cs = 164; goto _test_eof;
+ _test_eof165: cs = 165; goto _test_eof;
+ _test_eof166: cs = 166; goto _test_eof;
+ _test_eof167: cs = 167; goto _test_eof;
+ _test_eof168: cs = 168; goto _test_eof;
+ _test_eof169: cs = 169; goto _test_eof;
+ _test_eof170: cs = 170; goto _test_eof;
+ _test_eof171: cs = 171; goto _test_eof;
+ _test_eof172: cs = 172; goto _test_eof;
+ _test_eof173: cs = 173; goto _test_eof;
+ _test_eof1398: cs = 1398; goto _test_eof;
+ _test_eof174: cs = 174; goto _test_eof;
+ _test_eof175: cs = 175; goto _test_eof;
+ _test_eof176: cs = 176; goto _test_eof;
+ _test_eof177: cs = 177; goto _test_eof;
+ _test_eof178: cs = 178; goto _test_eof;
+ _test_eof179: cs = 179; goto _test_eof;
+ _test_eof180: cs = 180; goto _test_eof;
+ _test_eof181: cs = 181; goto _test_eof;
+ _test_eof182: cs = 182; goto _test_eof;
+ _test_eof183: cs = 183; goto _test_eof;
+ _test_eof1399: cs = 1399; goto _test_eof;
+ _test_eof184: cs = 184; goto _test_eof;
+ _test_eof185: cs = 185; goto _test_eof;
+ _test_eof186: cs = 186; goto _test_eof;
+ _test_eof187: cs = 187; goto _test_eof;
+ _test_eof188: cs = 188; goto _test_eof;
+ _test_eof189: cs = 189; goto _test_eof;
+ _test_eof190: cs = 190; goto _test_eof;
+ _test_eof191: cs = 191; goto _test_eof;
+ _test_eof1400: cs = 1400; goto _test_eof;
+ _test_eof192: cs = 192; goto _test_eof;
+ _test_eof193: cs = 193; goto _test_eof;
+ _test_eof194: cs = 194; goto _test_eof;
+ _test_eof1401: cs = 1401; goto _test_eof;
+ _test_eof195: cs = 195; goto _test_eof;
+ _test_eof196: cs = 196; goto _test_eof;
+ _test_eof197: cs = 197; goto _test_eof;
+ _test_eof198: cs = 198; goto _test_eof;
+ _test_eof199: cs = 199; goto _test_eof;
+ _test_eof200: cs = 200; goto _test_eof;
+ _test_eof201: cs = 201; goto _test_eof;
+ _test_eof202: cs = 202; goto _test_eof;
+ _test_eof203: cs = 203; goto _test_eof;
+ _test_eof204: cs = 204; goto _test_eof;
+ _test_eof205: cs = 205; goto _test_eof;
+ _test_eof1402: cs = 1402; goto _test_eof;
+ _test_eof206: cs = 206; goto _test_eof;
+ _test_eof207: cs = 207; goto _test_eof;
+ _test_eof208: cs = 208; goto _test_eof;
+ _test_eof209: cs = 209; goto _test_eof;
+ _test_eof1403: cs = 1403; goto _test_eof;
+ _test_eof210: cs = 210; goto _test_eof;
+ _test_eof211: cs = 211; goto _test_eof;
+ _test_eof212: cs = 212; goto _test_eof;
+ _test_eof213: cs = 213; goto _test_eof;
+ _test_eof214: cs = 214; goto _test_eof;
+ _test_eof215: cs = 215; goto _test_eof;
+ _test_eof216: cs = 216; goto _test_eof;
+ _test_eof217: cs = 217; goto _test_eof;
+ _test_eof218: cs = 218; goto _test_eof;
+ _test_eof219: cs = 219; goto _test_eof;
+ _test_eof220: cs = 220; goto _test_eof;
+ _test_eof221: cs = 221; goto _test_eof;
+ _test_eof222: cs = 222; goto _test_eof;
+ _test_eof223: cs = 223; goto _test_eof;
+ _test_eof224: cs = 224; goto _test_eof;
+ _test_eof225: cs = 225; goto _test_eof;
+ _test_eof226: cs = 226; goto _test_eof;
+ _test_eof227: cs = 227; goto _test_eof;
+ _test_eof1404: cs = 1404; goto _test_eof;
+ _test_eof228: cs = 228; goto _test_eof;
+ _test_eof229: cs = 229; goto _test_eof;
+ _test_eof230: cs = 230; goto _test_eof;
+ _test_eof231: cs = 231; goto _test_eof;
+ _test_eof232: cs = 232; goto _test_eof;
+ _test_eof233: cs = 233; goto _test_eof;
+ _test_eof234: cs = 234; goto _test_eof;
+ _test_eof235: cs = 235; goto _test_eof;
+ _test_eof236: cs = 236; goto _test_eof;
+ _test_eof237: cs = 237; goto _test_eof;
+ _test_eof238: cs = 238; goto _test_eof;
+ _test_eof239: cs = 239; goto _test_eof;
+ _test_eof240: cs = 240; goto _test_eof;
+ _test_eof241: cs = 241; goto _test_eof;
+ _test_eof242: cs = 242; goto _test_eof;
+ _test_eof243: cs = 243; goto _test_eof;
+ _test_eof1405: cs = 1405; goto _test_eof;
+ _test_eof244: cs = 244; goto _test_eof;
+ _test_eof245: cs = 245; goto _test_eof;
+ _test_eof246: cs = 246; goto _test_eof;
+ _test_eof247: cs = 247; goto _test_eof;
+ _test_eof248: cs = 248; goto _test_eof;
+ _test_eof249: cs = 249; goto _test_eof;
+ _test_eof250: cs = 250; goto _test_eof;
+ _test_eof251: cs = 251; goto _test_eof;
+ _test_eof252: cs = 252; goto _test_eof;
+ _test_eof253: cs = 253; goto _test_eof;
+ _test_eof254: cs = 254; goto _test_eof;
+ _test_eof255: cs = 255; goto _test_eof;
+ _test_eof256: cs = 256; goto _test_eof;
+ _test_eof257: cs = 257; goto _test_eof;
+ _test_eof258: cs = 258; goto _test_eof;
+ _test_eof259: cs = 259; goto _test_eof;
+ _test_eof260: cs = 260; goto _test_eof;
+ _test_eof261: cs = 261; goto _test_eof;
+ _test_eof262: cs = 262; goto _test_eof;
+ _test_eof263: cs = 263; goto _test_eof;
+ _test_eof264: cs = 264; goto _test_eof;
+ _test_eof265: cs = 265; goto _test_eof;
+ _test_eof266: cs = 266; goto _test_eof;
+ _test_eof267: cs = 267; goto _test_eof;
+ _test_eof268: cs = 268; goto _test_eof;
+ _test_eof269: cs = 269; goto _test_eof;
+ _test_eof270: cs = 270; goto _test_eof;
+ _test_eof271: cs = 271; goto _test_eof;
+ _test_eof272: cs = 272; goto _test_eof;
+ _test_eof273: cs = 273; goto _test_eof;
+ _test_eof274: cs = 274; goto _test_eof;
+ _test_eof275: cs = 275; goto _test_eof;
+ _test_eof276: cs = 276; goto _test_eof;
+ _test_eof277: cs = 277; goto _test_eof;
+ _test_eof278: cs = 278; goto _test_eof;
+ _test_eof279: cs = 279; goto _test_eof;
+ _test_eof280: cs = 280; goto _test_eof;
+ _test_eof281: cs = 281; goto _test_eof;
+ _test_eof282: cs = 282; goto _test_eof;
+ _test_eof283: cs = 283; goto _test_eof;
+ _test_eof284: cs = 284; goto _test_eof;
+ _test_eof285: cs = 285; goto _test_eof;
+ _test_eof286: cs = 286; goto _test_eof;
+ _test_eof287: cs = 287; goto _test_eof;
+ _test_eof288: cs = 288; goto _test_eof;
+ _test_eof289: cs = 289; goto _test_eof;
+ _test_eof290: cs = 290; goto _test_eof;
+ _test_eof291: cs = 291; goto _test_eof;
+ _test_eof292: cs = 292; goto _test_eof;
+ _test_eof293: cs = 293; goto _test_eof;
+ _test_eof294: cs = 294; goto _test_eof;
+ _test_eof295: cs = 295; goto _test_eof;
+ _test_eof296: cs = 296; goto _test_eof;
+ _test_eof297: cs = 297; goto _test_eof;
+ _test_eof298: cs = 298; goto _test_eof;
+ _test_eof299: cs = 299; goto _test_eof;
+ _test_eof300: cs = 300; goto _test_eof;
+ _test_eof301: cs = 301; goto _test_eof;
+ _test_eof302: cs = 302; goto _test_eof;
+ _test_eof303: cs = 303; goto _test_eof;
+ _test_eof304: cs = 304; goto _test_eof;
+ _test_eof305: cs = 305; goto _test_eof;
+ _test_eof306: cs = 306; goto _test_eof;
+ _test_eof1406: cs = 1406; goto _test_eof;
+ _test_eof307: cs = 307; goto _test_eof;
+ _test_eof308: cs = 308; goto _test_eof;
+ _test_eof1407: cs = 1407; goto _test_eof;
+ _test_eof309: cs = 309; goto _test_eof;
+ _test_eof310: cs = 310; goto _test_eof;
+ _test_eof1408: cs = 1408; goto _test_eof;
+ _test_eof311: cs = 311; goto _test_eof;
+ _test_eof312: cs = 312; goto _test_eof;
+ _test_eof313: cs = 313; goto _test_eof;
+ _test_eof314: cs = 314; goto _test_eof;
+ _test_eof315: cs = 315; goto _test_eof;
+ _test_eof316: cs = 316; goto _test_eof;
+ _test_eof317: cs = 317; goto _test_eof;
+ _test_eof318: cs = 318; goto _test_eof;
+ _test_eof319: cs = 319; goto _test_eof;
+ _test_eof1409: cs = 1409; goto _test_eof;
+ _test_eof1410: cs = 1410; goto _test_eof;
+ _test_eof320: cs = 320; goto _test_eof;
+ _test_eof321: cs = 321; goto _test_eof;
+ _test_eof322: cs = 322; goto _test_eof;
+ _test_eof323: cs = 323; goto _test_eof;
+ _test_eof324: cs = 324; goto _test_eof;
+ _test_eof325: cs = 325; goto _test_eof;
+ _test_eof326: cs = 326; goto _test_eof;
+ _test_eof327: cs = 327; goto _test_eof;
+ _test_eof328: cs = 328; goto _test_eof;
+ _test_eof329: cs = 329; goto _test_eof;
+ _test_eof330: cs = 330; goto _test_eof;
+ _test_eof331: cs = 331; goto _test_eof;
+ _test_eof332: cs = 332; goto _test_eof;
+ _test_eof333: cs = 333; goto _test_eof;
+ _test_eof334: cs = 334; goto _test_eof;
+ _test_eof1411: cs = 1411; goto _test_eof;
+ _test_eof335: cs = 335; goto _test_eof;
+ _test_eof336: cs = 336; goto _test_eof;
+ _test_eof337: cs = 337; goto _test_eof;
+ _test_eof338: cs = 338; goto _test_eof;
+ _test_eof339: cs = 339; goto _test_eof;
+ _test_eof340: cs = 340; goto _test_eof;
+ _test_eof341: cs = 341; goto _test_eof;
+ _test_eof342: cs = 342; goto _test_eof;
+ _test_eof343: cs = 343; goto _test_eof;
+ _test_eof344: cs = 344; goto _test_eof;
+ _test_eof1412: cs = 1412; goto _test_eof;
+ _test_eof345: cs = 345; goto _test_eof;
+ _test_eof346: cs = 346; goto _test_eof;
+ _test_eof347: cs = 347; goto _test_eof;
+ _test_eof348: cs = 348; goto _test_eof;
+ _test_eof349: cs = 349; goto _test_eof;
+ _test_eof350: cs = 350; goto _test_eof;
+ _test_eof351: cs = 351; goto _test_eof;
+ _test_eof352: cs = 352; goto _test_eof;
+ _test_eof353: cs = 353; goto _test_eof;
+ _test_eof354: cs = 354; goto _test_eof;
+ _test_eof355: cs = 355; goto _test_eof;
+ _test_eof356: cs = 356; goto _test_eof;
+ _test_eof357: cs = 357; goto _test_eof;
+ _test_eof358: cs = 358; goto _test_eof;
+ _test_eof1413: cs = 1413; goto _test_eof;
+ _test_eof359: cs = 359; goto _test_eof;
+ _test_eof360: cs = 360; goto _test_eof;
+ _test_eof361: cs = 361; goto _test_eof;
+ _test_eof362: cs = 362; goto _test_eof;
+ _test_eof363: cs = 363; goto _test_eof;
+ _test_eof364: cs = 364; goto _test_eof;
+ _test_eof365: cs = 365; goto _test_eof;
+ _test_eof1414: cs = 1414; goto _test_eof;
+ _test_eof366: cs = 366; goto _test_eof;
+ _test_eof367: cs = 367; goto _test_eof;
+ _test_eof368: cs = 368; goto _test_eof;
+ _test_eof369: cs = 369; goto _test_eof;
+ _test_eof370: cs = 370; goto _test_eof;
+ _test_eof371: cs = 371; goto _test_eof;
+ _test_eof372: cs = 372; goto _test_eof;
+ _test_eof373: cs = 373; goto _test_eof;
+ _test_eof374: cs = 374; goto _test_eof;
+ _test_eof1415: cs = 1415; goto _test_eof;
+ _test_eof1416: cs = 1416; goto _test_eof;
+ _test_eof1417: cs = 1417; goto _test_eof;
+ _test_eof375: cs = 375; goto _test_eof;
+ _test_eof376: cs = 376; goto _test_eof;
+ _test_eof377: cs = 377; goto _test_eof;
+ _test_eof378: cs = 378; goto _test_eof;
+ _test_eof379: cs = 379; goto _test_eof;
+ _test_eof380: cs = 380; goto _test_eof;
+ _test_eof381: cs = 381; goto _test_eof;
+ _test_eof382: cs = 382; goto _test_eof;
+ _test_eof1418: cs = 1418; goto _test_eof;
+ _test_eof1419: cs = 1419; goto _test_eof;
+ _test_eof383: cs = 383; goto _test_eof;
+ _test_eof384: cs = 384; goto _test_eof;
+ _test_eof385: cs = 385; goto _test_eof;
+ _test_eof1420: cs = 1420; goto _test_eof;
+ _test_eof386: cs = 386; goto _test_eof;
+ _test_eof387: cs = 387; goto _test_eof;
+ _test_eof388: cs = 388; goto _test_eof;
+ _test_eof389: cs = 389; goto _test_eof;
+ _test_eof390: cs = 390; goto _test_eof;
+ _test_eof391: cs = 391; goto _test_eof;
+ _test_eof392: cs = 392; goto _test_eof;
+ _test_eof393: cs = 393; goto _test_eof;
+ _test_eof394: cs = 394; goto _test_eof;
+ _test_eof395: cs = 395; goto _test_eof;
+ _test_eof396: cs = 396; goto _test_eof;
+ _test_eof397: cs = 397; goto _test_eof;
+ _test_eof398: cs = 398; goto _test_eof;
+ _test_eof399: cs = 399; goto _test_eof;
+ _test_eof400: cs = 400; goto _test_eof;
+ _test_eof401: cs = 401; goto _test_eof;
+ _test_eof402: cs = 402; goto _test_eof;
+ _test_eof403: cs = 403; goto _test_eof;
+ _test_eof404: cs = 404; goto _test_eof;
+ _test_eof405: cs = 405; goto _test_eof;
+ _test_eof406: cs = 406; goto _test_eof;
+ _test_eof407: cs = 407; goto _test_eof;
+ _test_eof408: cs = 408; goto _test_eof;
+ _test_eof409: cs = 409; goto _test_eof;
+ _test_eof410: cs = 410; goto _test_eof;
+ _test_eof411: cs = 411; goto _test_eof;
+ _test_eof412: cs = 412; goto _test_eof;
+ _test_eof413: cs = 413; goto _test_eof;
+ _test_eof414: cs = 414; goto _test_eof;
+ _test_eof415: cs = 415; goto _test_eof;
+ _test_eof416: cs = 416; goto _test_eof;
+ _test_eof417: cs = 417; goto _test_eof;
+ _test_eof418: cs = 418; goto _test_eof;
+ _test_eof419: cs = 419; goto _test_eof;
+ _test_eof420: cs = 420; goto _test_eof;
+ _test_eof421: cs = 421; goto _test_eof;
+ _test_eof422: cs = 422; goto _test_eof;
+ _test_eof423: cs = 423; goto _test_eof;
+ _test_eof424: cs = 424; goto _test_eof;
+ _test_eof425: cs = 425; goto _test_eof;
+ _test_eof426: cs = 426; goto _test_eof;
+ _test_eof427: cs = 427; goto _test_eof;
+ _test_eof428: cs = 428; goto _test_eof;
+ _test_eof429: cs = 429; goto _test_eof;
+ _test_eof430: cs = 430; goto _test_eof;
+ _test_eof431: cs = 431; goto _test_eof;
+ _test_eof432: cs = 432; goto _test_eof;
+ _test_eof433: cs = 433; goto _test_eof;
+ _test_eof434: cs = 434; goto _test_eof;
+ _test_eof435: cs = 435; goto _test_eof;
+ _test_eof436: cs = 436; goto _test_eof;
+ _test_eof437: cs = 437; goto _test_eof;
+ _test_eof438: cs = 438; goto _test_eof;
+ _test_eof439: cs = 439; goto _test_eof;
+ _test_eof440: cs = 440; goto _test_eof;
+ _test_eof441: cs = 441; goto _test_eof;
+ _test_eof442: cs = 442; goto _test_eof;
+ _test_eof443: cs = 443; goto _test_eof;
+ _test_eof444: cs = 444; goto _test_eof;
+ _test_eof445: cs = 445; goto _test_eof;
+ _test_eof446: cs = 446; goto _test_eof;
+ _test_eof447: cs = 447; goto _test_eof;
+ _test_eof448: cs = 448; goto _test_eof;
+ _test_eof449: cs = 449; goto _test_eof;
+ _test_eof450: cs = 450; goto _test_eof;
+ _test_eof451: cs = 451; goto _test_eof;
+ _test_eof452: cs = 452; goto _test_eof;
+ _test_eof453: cs = 453; goto _test_eof;
+ _test_eof454: cs = 454; goto _test_eof;
+ _test_eof455: cs = 455; goto _test_eof;
+ _test_eof456: cs = 456; goto _test_eof;
+ _test_eof457: cs = 457; goto _test_eof;
+ _test_eof458: cs = 458; goto _test_eof;
+ _test_eof459: cs = 459; goto _test_eof;
+ _test_eof460: cs = 460; goto _test_eof;
+ _test_eof461: cs = 461; goto _test_eof;
+ _test_eof462: cs = 462; goto _test_eof;
+ _test_eof463: cs = 463; goto _test_eof;
+ _test_eof464: cs = 464; goto _test_eof;
+ _test_eof465: cs = 465; goto _test_eof;
+ _test_eof466: cs = 466; goto _test_eof;
+ _test_eof467: cs = 467; goto _test_eof;
+ _test_eof468: cs = 468; goto _test_eof;
+ _test_eof469: cs = 469; goto _test_eof;
+ _test_eof470: cs = 470; goto _test_eof;
+ _test_eof471: cs = 471; goto _test_eof;
+ _test_eof472: cs = 472; goto _test_eof;
+ _test_eof473: cs = 473; goto _test_eof;
+ _test_eof474: cs = 474; goto _test_eof;
+ _test_eof475: cs = 475; goto _test_eof;
+ _test_eof476: cs = 476; goto _test_eof;
+ _test_eof477: cs = 477; goto _test_eof;
+ _test_eof478: cs = 478; goto _test_eof;
+ _test_eof479: cs = 479; goto _test_eof;
+ _test_eof480: cs = 480; goto _test_eof;
+ _test_eof481: cs = 481; goto _test_eof;
+ _test_eof482: cs = 482; goto _test_eof;
+ _test_eof483: cs = 483; goto _test_eof;
+ _test_eof484: cs = 484; goto _test_eof;
+ _test_eof485: cs = 485; goto _test_eof;
+ _test_eof486: cs = 486; goto _test_eof;
+ _test_eof487: cs = 487; goto _test_eof;
+ _test_eof488: cs = 488; goto _test_eof;
+ _test_eof489: cs = 489; goto _test_eof;
+ _test_eof490: cs = 490; goto _test_eof;
+ _test_eof491: cs = 491; goto _test_eof;
+ _test_eof492: cs = 492; goto _test_eof;
+ _test_eof493: cs = 493; goto _test_eof;
+ _test_eof494: cs = 494; goto _test_eof;
+ _test_eof495: cs = 495; goto _test_eof;
+ _test_eof496: cs = 496; goto _test_eof;
+ _test_eof497: cs = 497; goto _test_eof;
+ _test_eof498: cs = 498; goto _test_eof;
+ _test_eof499: cs = 499; goto _test_eof;
+ _test_eof500: cs = 500; goto _test_eof;
+ _test_eof501: cs = 501; goto _test_eof;
+ _test_eof502: cs = 502; goto _test_eof;
+ _test_eof503: cs = 503; goto _test_eof;
+ _test_eof504: cs = 504; goto _test_eof;
+ _test_eof505: cs = 505; goto _test_eof;
+ _test_eof506: cs = 506; goto _test_eof;
+ _test_eof507: cs = 507; goto _test_eof;
+ _test_eof508: cs = 508; goto _test_eof;
+ _test_eof509: cs = 509; goto _test_eof;
+ _test_eof510: cs = 510; goto _test_eof;
+ _test_eof511: cs = 511; goto _test_eof;
+ _test_eof512: cs = 512; goto _test_eof;
+ _test_eof513: cs = 513; goto _test_eof;
+ _test_eof514: cs = 514; goto _test_eof;
+ _test_eof515: cs = 515; goto _test_eof;
+ _test_eof516: cs = 516; goto _test_eof;
+ _test_eof517: cs = 517; goto _test_eof;
+ _test_eof518: cs = 518; goto _test_eof;
+ _test_eof519: cs = 519; goto _test_eof;
+ _test_eof520: cs = 520; goto _test_eof;
+ _test_eof521: cs = 521; goto _test_eof;
+ _test_eof522: cs = 522; goto _test_eof;
+ _test_eof523: cs = 523; goto _test_eof;
+ _test_eof524: cs = 524; goto _test_eof;
+ _test_eof525: cs = 525; goto _test_eof;
+ _test_eof526: cs = 526; goto _test_eof;
+ _test_eof527: cs = 527; goto _test_eof;
+ _test_eof528: cs = 528; goto _test_eof;
+ _test_eof529: cs = 529; goto _test_eof;
+ _test_eof530: cs = 530; goto _test_eof;
+ _test_eof531: cs = 531; goto _test_eof;
+ _test_eof532: cs = 532; goto _test_eof;
+ _test_eof533: cs = 533; goto _test_eof;
+ _test_eof534: cs = 534; goto _test_eof;
+ _test_eof535: cs = 535; goto _test_eof;
+ _test_eof536: cs = 536; goto _test_eof;
+ _test_eof537: cs = 537; goto _test_eof;
+ _test_eof538: cs = 538; goto _test_eof;
+ _test_eof539: cs = 539; goto _test_eof;
+ _test_eof540: cs = 540; goto _test_eof;
+ _test_eof541: cs = 541; goto _test_eof;
+ _test_eof542: cs = 542; goto _test_eof;
+ _test_eof543: cs = 543; goto _test_eof;
+ _test_eof544: cs = 544; goto _test_eof;
+ _test_eof545: cs = 545; goto _test_eof;
+ _test_eof546: cs = 546; goto _test_eof;
+ _test_eof1421: cs = 1421; goto _test_eof;
+ _test_eof1422: cs = 1422; goto _test_eof;
+ _test_eof1423: cs = 1423; goto _test_eof;
+ _test_eof547: cs = 547; goto _test_eof;
+ _test_eof548: cs = 548; goto _test_eof;
+ _test_eof549: cs = 549; goto _test_eof;
+ _test_eof550: cs = 550; goto _test_eof;
+ _test_eof551: cs = 551; goto _test_eof;
+ _test_eof1424: cs = 1424; goto _test_eof;
+ _test_eof552: cs = 552; goto _test_eof;
+ _test_eof553: cs = 553; goto _test_eof;
+ _test_eof554: cs = 554; goto _test_eof;
+ _test_eof555: cs = 555; goto _test_eof;
+ _test_eof1425: cs = 1425; goto _test_eof;
+ _test_eof1426: cs = 1426; goto _test_eof;
+ _test_eof1427: cs = 1427; goto _test_eof;
+ _test_eof556: cs = 556; goto _test_eof;
+ _test_eof557: cs = 557; goto _test_eof;
+ _test_eof1428: cs = 1428; goto _test_eof;
+ _test_eof558: cs = 558; goto _test_eof;
+ _test_eof559: cs = 559; goto _test_eof;
+ _test_eof560: cs = 560; goto _test_eof;
+ _test_eof561: cs = 561; goto _test_eof;
+ _test_eof562: cs = 562; goto _test_eof;
+ _test_eof563: cs = 563; goto _test_eof;
+ _test_eof1429: cs = 1429; goto _test_eof;
+ _test_eof564: cs = 564; goto _test_eof;
+ _test_eof565: cs = 565; goto _test_eof;
+ _test_eof566: cs = 566; goto _test_eof;
+ _test_eof567: cs = 567; goto _test_eof;
+ _test_eof568: cs = 568; goto _test_eof;
+ _test_eof569: cs = 569; goto _test_eof;
+ _test_eof570: cs = 570; goto _test_eof;
+ _test_eof571: cs = 571; goto _test_eof;
+ _test_eof572: cs = 572; goto _test_eof;
+ _test_eof573: cs = 573; goto _test_eof;
+ _test_eof574: cs = 574; goto _test_eof;
+ _test_eof575: cs = 575; goto _test_eof;
+ _test_eof576: cs = 576; goto _test_eof;
+ _test_eof577: cs = 577; goto _test_eof;
+ _test_eof578: cs = 578; goto _test_eof;
+ _test_eof579: cs = 579; goto _test_eof;
+ _test_eof580: cs = 580; goto _test_eof;
+ _test_eof581: cs = 581; goto _test_eof;
+ _test_eof582: cs = 582; goto _test_eof;
+ _test_eof583: cs = 583; goto _test_eof;
+ _test_eof584: cs = 584; goto _test_eof;
+ _test_eof585: cs = 585; goto _test_eof;
+ _test_eof586: cs = 586; goto _test_eof;
+ _test_eof587: cs = 587; goto _test_eof;
+ _test_eof588: cs = 588; goto _test_eof;
+ _test_eof589: cs = 589; goto _test_eof;
+ _test_eof590: cs = 590; goto _test_eof;
+ _test_eof591: cs = 591; goto _test_eof;
+ _test_eof592: cs = 592; goto _test_eof;
+ _test_eof593: cs = 593; goto _test_eof;
+ _test_eof594: cs = 594; goto _test_eof;
+ _test_eof595: cs = 595; goto _test_eof;
+ _test_eof596: cs = 596; goto _test_eof;
+ _test_eof597: cs = 597; goto _test_eof;
+ _test_eof598: cs = 598; goto _test_eof;
+ _test_eof599: cs = 599; goto _test_eof;
+ _test_eof600: cs = 600; goto _test_eof;
+ _test_eof601: cs = 601; goto _test_eof;
+ _test_eof602: cs = 602; goto _test_eof;
+ _test_eof603: cs = 603; goto _test_eof;
+ _test_eof604: cs = 604; goto _test_eof;
+ _test_eof605: cs = 605; goto _test_eof;
+ _test_eof606: cs = 606; goto _test_eof;
+ _test_eof607: cs = 607; goto _test_eof;
+ _test_eof608: cs = 608; goto _test_eof;
+ _test_eof609: cs = 609; goto _test_eof;
+ _test_eof610: cs = 610; goto _test_eof;
+ _test_eof611: cs = 611; goto _test_eof;
+ _test_eof1430: cs = 1430; goto _test_eof;
+ _test_eof1431: cs = 1431; goto _test_eof;
+ _test_eof612: cs = 612; goto _test_eof;
+ _test_eof613: cs = 613; goto _test_eof;
+ _test_eof614: cs = 614; goto _test_eof;
+ _test_eof615: cs = 615; goto _test_eof;
+ _test_eof616: cs = 616; goto _test_eof;
+ _test_eof617: cs = 617; goto _test_eof;
+ _test_eof618: cs = 618; goto _test_eof;
+ _test_eof619: cs = 619; goto _test_eof;
+ _test_eof620: cs = 620; goto _test_eof;
+ _test_eof621: cs = 621; goto _test_eof;
+ _test_eof1432: cs = 1432; goto _test_eof;
+ _test_eof622: cs = 622; goto _test_eof;
+ _test_eof623: cs = 623; goto _test_eof;
+ _test_eof624: cs = 624; goto _test_eof;
+ _test_eof625: cs = 625; goto _test_eof;
+ _test_eof626: cs = 626; goto _test_eof;
+ _test_eof627: cs = 627; goto _test_eof;
+ _test_eof628: cs = 628; goto _test_eof;
+ _test_eof629: cs = 629; goto _test_eof;
+ _test_eof630: cs = 630; goto _test_eof;
+ _test_eof631: cs = 631; goto _test_eof;
+ _test_eof632: cs = 632; goto _test_eof;
+ _test_eof633: cs = 633; goto _test_eof;
+ _test_eof634: cs = 634; goto _test_eof;
+ _test_eof635: cs = 635; goto _test_eof;
+ _test_eof636: cs = 636; goto _test_eof;
+ _test_eof637: cs = 637; goto _test_eof;
+ _test_eof638: cs = 638; goto _test_eof;
+ _test_eof639: cs = 639; goto _test_eof;
+ _test_eof640: cs = 640; goto _test_eof;
+ _test_eof641: cs = 641; goto _test_eof;
+ _test_eof642: cs = 642; goto _test_eof;
+ _test_eof643: cs = 643; goto _test_eof;
+ _test_eof644: cs = 644; goto _test_eof;
+ _test_eof645: cs = 645; goto _test_eof;
+ _test_eof646: cs = 646; goto _test_eof;
+ _test_eof647: cs = 647; goto _test_eof;
+ _test_eof648: cs = 648; goto _test_eof;
+ _test_eof649: cs = 649; goto _test_eof;
+ _test_eof650: cs = 650; goto _test_eof;
+ _test_eof651: cs = 651; goto _test_eof;
+ _test_eof652: cs = 652; goto _test_eof;
+ _test_eof653: cs = 653; goto _test_eof;
+ _test_eof654: cs = 654; goto _test_eof;
+ _test_eof655: cs = 655; goto _test_eof;
+ _test_eof656: cs = 656; goto _test_eof;
+ _test_eof657: cs = 657; goto _test_eof;
+ _test_eof658: cs = 658; goto _test_eof;
+ _test_eof659: cs = 659; goto _test_eof;
+ _test_eof660: cs = 660; goto _test_eof;
+ _test_eof661: cs = 661; goto _test_eof;
+ _test_eof662: cs = 662; goto _test_eof;
+ _test_eof663: cs = 663; goto _test_eof;
+ _test_eof664: cs = 664; goto _test_eof;
+ _test_eof665: cs = 665; goto _test_eof;
+ _test_eof666: cs = 666; goto _test_eof;
+ _test_eof667: cs = 667; goto _test_eof;
+ _test_eof668: cs = 668; goto _test_eof;
+ _test_eof669: cs = 669; goto _test_eof;
+ _test_eof670: cs = 670; goto _test_eof;
+ _test_eof671: cs = 671; goto _test_eof;
+ _test_eof672: cs = 672; goto _test_eof;
+ _test_eof673: cs = 673; goto _test_eof;
+ _test_eof674: cs = 674; goto _test_eof;
+ _test_eof675: cs = 675; goto _test_eof;
+ _test_eof676: cs = 676; goto _test_eof;
+ _test_eof677: cs = 677; goto _test_eof;
+ _test_eof678: cs = 678; goto _test_eof;
+ _test_eof679: cs = 679; goto _test_eof;
+ _test_eof680: cs = 680; goto _test_eof;
+ _test_eof681: cs = 681; goto _test_eof;
+ _test_eof682: cs = 682; goto _test_eof;
+ _test_eof683: cs = 683; goto _test_eof;
+ _test_eof684: cs = 684; goto _test_eof;
+ _test_eof685: cs = 685; goto _test_eof;
+ _test_eof686: cs = 686; goto _test_eof;
+ _test_eof687: cs = 687; goto _test_eof;
+ _test_eof688: cs = 688; goto _test_eof;
+ _test_eof689: cs = 689; goto _test_eof;
+ _test_eof690: cs = 690; goto _test_eof;
+ _test_eof691: cs = 691; goto _test_eof;
+ _test_eof692: cs = 692; goto _test_eof;
+ _test_eof693: cs = 693; goto _test_eof;
+ _test_eof694: cs = 694; goto _test_eof;
+ _test_eof695: cs = 695; goto _test_eof;
+ _test_eof696: cs = 696; goto _test_eof;
+ _test_eof697: cs = 697; goto _test_eof;
+ _test_eof698: cs = 698; goto _test_eof;
+ _test_eof699: cs = 699; goto _test_eof;
+ _test_eof700: cs = 700; goto _test_eof;
+ _test_eof1433: cs = 1433; goto _test_eof;
+ _test_eof701: cs = 701; goto _test_eof;
+ _test_eof702: cs = 702; goto _test_eof;
+ _test_eof703: cs = 703; goto _test_eof;
+ _test_eof704: cs = 704; goto _test_eof;
+ _test_eof705: cs = 705; goto _test_eof;
+ _test_eof706: cs = 706; goto _test_eof;
+ _test_eof707: cs = 707; goto _test_eof;
+ _test_eof708: cs = 708; goto _test_eof;
+ _test_eof709: cs = 709; goto _test_eof;
+ _test_eof710: cs = 710; goto _test_eof;
+ _test_eof711: cs = 711; goto _test_eof;
+ _test_eof712: cs = 712; goto _test_eof;
+ _test_eof713: cs = 713; goto _test_eof;
+ _test_eof714: cs = 714; goto _test_eof;
+ _test_eof715: cs = 715; goto _test_eof;
+ _test_eof716: cs = 716; goto _test_eof;
+ _test_eof717: cs = 717; goto _test_eof;
+ _test_eof718: cs = 718; goto _test_eof;
+ _test_eof719: cs = 719; goto _test_eof;
+ _test_eof720: cs = 720; goto _test_eof;
+ _test_eof721: cs = 721; goto _test_eof;
+ _test_eof722: cs = 722; goto _test_eof;
+ _test_eof723: cs = 723; goto _test_eof;
+ _test_eof724: cs = 724; goto _test_eof;
+ _test_eof725: cs = 725; goto _test_eof;
+ _test_eof726: cs = 726; goto _test_eof;
+ _test_eof727: cs = 727; goto _test_eof;
+ _test_eof728: cs = 728; goto _test_eof;
+ _test_eof729: cs = 729; goto _test_eof;
+ _test_eof730: cs = 730; goto _test_eof;
+ _test_eof731: cs = 731; goto _test_eof;
+ _test_eof732: cs = 732; goto _test_eof;
+ _test_eof733: cs = 733; goto _test_eof;
+ _test_eof734: cs = 734; goto _test_eof;
+ _test_eof735: cs = 735; goto _test_eof;
+ _test_eof736: cs = 736; goto _test_eof;
+ _test_eof737: cs = 737; goto _test_eof;
+ _test_eof738: cs = 738; goto _test_eof;
+ _test_eof739: cs = 739; goto _test_eof;
+ _test_eof740: cs = 740; goto _test_eof;
+ _test_eof741: cs = 741; goto _test_eof;
+ _test_eof742: cs = 742; goto _test_eof;
+ _test_eof743: cs = 743; goto _test_eof;
+ _test_eof744: cs = 744; goto _test_eof;
+ _test_eof745: cs = 745; goto _test_eof;
+ _test_eof746: cs = 746; goto _test_eof;
+ _test_eof747: cs = 747; goto _test_eof;
+ _test_eof748: cs = 748; goto _test_eof;
+ _test_eof749: cs = 749; goto _test_eof;
+ _test_eof750: cs = 750; goto _test_eof;
+ _test_eof751: cs = 751; goto _test_eof;
+ _test_eof752: cs = 752; goto _test_eof;
+ _test_eof753: cs = 753; goto _test_eof;
+ _test_eof754: cs = 754; goto _test_eof;
+ _test_eof755: cs = 755; goto _test_eof;
+ _test_eof756: cs = 756; goto _test_eof;
+ _test_eof757: cs = 757; goto _test_eof;
+ _test_eof758: cs = 758; goto _test_eof;
+ _test_eof759: cs = 759; goto _test_eof;
+ _test_eof760: cs = 760; goto _test_eof;
+ _test_eof761: cs = 761; goto _test_eof;
+ _test_eof762: cs = 762; goto _test_eof;
+ _test_eof763: cs = 763; goto _test_eof;
+ _test_eof764: cs = 764; goto _test_eof;
+ _test_eof765: cs = 765; goto _test_eof;
+ _test_eof766: cs = 766; goto _test_eof;
+ _test_eof767: cs = 767; goto _test_eof;
+ _test_eof768: cs = 768; goto _test_eof;
+ _test_eof769: cs = 769; goto _test_eof;
+ _test_eof770: cs = 770; goto _test_eof;
+ _test_eof771: cs = 771; goto _test_eof;
+ _test_eof772: cs = 772; goto _test_eof;
+ _test_eof773: cs = 773; goto _test_eof;
+ _test_eof774: cs = 774; goto _test_eof;
+ _test_eof775: cs = 775; goto _test_eof;
+ _test_eof776: cs = 776; goto _test_eof;
+ _test_eof777: cs = 777; goto _test_eof;
+ _test_eof778: cs = 778; goto _test_eof;
+ _test_eof779: cs = 779; goto _test_eof;
+ _test_eof780: cs = 780; goto _test_eof;
+ _test_eof781: cs = 781; goto _test_eof;
+ _test_eof782: cs = 782; goto _test_eof;
+ _test_eof783: cs = 783; goto _test_eof;
+ _test_eof784: cs = 784; goto _test_eof;
+ _test_eof785: cs = 785; goto _test_eof;
+ _test_eof786: cs = 786; goto _test_eof;
+ _test_eof787: cs = 787; goto _test_eof;
+ _test_eof788: cs = 788; goto _test_eof;
+ _test_eof789: cs = 789; goto _test_eof;
+ _test_eof790: cs = 790; goto _test_eof;
+ _test_eof791: cs = 791; goto _test_eof;
+ _test_eof792: cs = 792; goto _test_eof;
+ _test_eof793: cs = 793; goto _test_eof;
+ _test_eof794: cs = 794; goto _test_eof;
+ _test_eof795: cs = 795; goto _test_eof;
+ _test_eof796: cs = 796; goto _test_eof;
+ _test_eof797: cs = 797; goto _test_eof;
+ _test_eof798: cs = 798; goto _test_eof;
+ _test_eof799: cs = 799; goto _test_eof;
+ _test_eof800: cs = 800; goto _test_eof;
+ _test_eof801: cs = 801; goto _test_eof;
+ _test_eof802: cs = 802; goto _test_eof;
+ _test_eof803: cs = 803; goto _test_eof;
+ _test_eof804: cs = 804; goto _test_eof;
+ _test_eof1434: cs = 1434; goto _test_eof;
+ _test_eof805: cs = 805; goto _test_eof;
+ _test_eof806: cs = 806; goto _test_eof;
+ _test_eof807: cs = 807; goto _test_eof;
+ _test_eof808: cs = 808; goto _test_eof;
+ _test_eof809: cs = 809; goto _test_eof;
+ _test_eof810: cs = 810; goto _test_eof;
+ _test_eof811: cs = 811; goto _test_eof;
+ _test_eof812: cs = 812; goto _test_eof;
+ _test_eof813: cs = 813; goto _test_eof;
+ _test_eof814: cs = 814; goto _test_eof;
+ _test_eof815: cs = 815; goto _test_eof;
+ _test_eof816: cs = 816; goto _test_eof;
+ _test_eof817: cs = 817; goto _test_eof;
+ _test_eof818: cs = 818; goto _test_eof;
+ _test_eof819: cs = 819; goto _test_eof;
+ _test_eof820: cs = 820; goto _test_eof;
+ _test_eof821: cs = 821; goto _test_eof;
+ _test_eof822: cs = 822; goto _test_eof;
+ _test_eof823: cs = 823; goto _test_eof;
+ _test_eof824: cs = 824; goto _test_eof;
+ _test_eof825: cs = 825; goto _test_eof;
+ _test_eof826: cs = 826; goto _test_eof;
+ _test_eof827: cs = 827; goto _test_eof;
+ _test_eof828: cs = 828; goto _test_eof;
+ _test_eof829: cs = 829; goto _test_eof;
+ _test_eof830: cs = 830; goto _test_eof;
+ _test_eof831: cs = 831; goto _test_eof;
+ _test_eof832: cs = 832; goto _test_eof;
+ _test_eof833: cs = 833; goto _test_eof;
+ _test_eof834: cs = 834; goto _test_eof;
+ _test_eof835: cs = 835; goto _test_eof;
+ _test_eof836: cs = 836; goto _test_eof;
+ _test_eof837: cs = 837; goto _test_eof;
+ _test_eof838: cs = 838; goto _test_eof;
+ _test_eof839: cs = 839; goto _test_eof;
+ _test_eof840: cs = 840; goto _test_eof;
+ _test_eof841: cs = 841; goto _test_eof;
+ _test_eof842: cs = 842; goto _test_eof;
+ _test_eof843: cs = 843; goto _test_eof;
+ _test_eof844: cs = 844; goto _test_eof;
+ _test_eof845: cs = 845; goto _test_eof;
+ _test_eof1435: cs = 1435; goto _test_eof;
+ _test_eof846: cs = 846; goto _test_eof;
+ _test_eof847: cs = 847; goto _test_eof;
+ _test_eof1436: cs = 1436; goto _test_eof;
+ _test_eof848: cs = 848; goto _test_eof;
+ _test_eof849: cs = 849; goto _test_eof;
+ _test_eof850: cs = 850; goto _test_eof;
+ _test_eof851: cs = 851; goto _test_eof;
+ _test_eof852: cs = 852; goto _test_eof;
+ _test_eof853: cs = 853; goto _test_eof;
+ _test_eof854: cs = 854; goto _test_eof;
+ _test_eof855: cs = 855; goto _test_eof;
+ _test_eof856: cs = 856; goto _test_eof;
+ _test_eof857: cs = 857; goto _test_eof;
+ _test_eof858: cs = 858; goto _test_eof;
+ _test_eof859: cs = 859; goto _test_eof;
+ _test_eof860: cs = 860; goto _test_eof;
+ _test_eof861: cs = 861; goto _test_eof;
+ _test_eof1437: cs = 1437; goto _test_eof;
+ _test_eof862: cs = 862; goto _test_eof;
+ _test_eof863: cs = 863; goto _test_eof;
+ _test_eof864: cs = 864; goto _test_eof;
+ _test_eof865: cs = 865; goto _test_eof;
+ _test_eof866: cs = 866; goto _test_eof;
+ _test_eof867: cs = 867; goto _test_eof;
+ _test_eof868: cs = 868; goto _test_eof;
+ _test_eof869: cs = 869; goto _test_eof;
+ _test_eof870: cs = 870; goto _test_eof;
+ _test_eof871: cs = 871; goto _test_eof;
+ _test_eof872: cs = 872; goto _test_eof;
+ _test_eof873: cs = 873; goto _test_eof;
+ _test_eof874: cs = 874; goto _test_eof;
+ _test_eof875: cs = 875; goto _test_eof;
+ _test_eof876: cs = 876; goto _test_eof;
+ _test_eof877: cs = 877; goto _test_eof;
+ _test_eof878: cs = 878; goto _test_eof;
+ _test_eof879: cs = 879; goto _test_eof;
+ _test_eof880: cs = 880; goto _test_eof;
+ _test_eof881: cs = 881; goto _test_eof;
+ _test_eof882: cs = 882; goto _test_eof;
+ _test_eof883: cs = 883; goto _test_eof;
+ _test_eof1438: cs = 1438; goto _test_eof;
+ _test_eof884: cs = 884; goto _test_eof;
+ _test_eof885: cs = 885; goto _test_eof;
+ _test_eof886: cs = 886; goto _test_eof;
+ _test_eof887: cs = 887; goto _test_eof;
+ _test_eof888: cs = 888; goto _test_eof;
+ _test_eof1439: cs = 1439; goto _test_eof;
+ _test_eof889: cs = 889; goto _test_eof;
+ _test_eof890: cs = 890; goto _test_eof;
+ _test_eof891: cs = 891; goto _test_eof;
+ _test_eof892: cs = 892; goto _test_eof;
+ _test_eof893: cs = 893; goto _test_eof;
+ _test_eof1440: cs = 1440; goto _test_eof;
+ _test_eof894: cs = 894; goto _test_eof;
+ _test_eof895: cs = 895; goto _test_eof;
+ _test_eof896: cs = 896; goto _test_eof;
+ _test_eof897: cs = 897; goto _test_eof;
+ _test_eof898: cs = 898; goto _test_eof;
+ _test_eof1441: cs = 1441; goto _test_eof;
+ _test_eof1442: cs = 1442; goto _test_eof;
+ _test_eof1443: cs = 1443; goto _test_eof;
+ _test_eof899: cs = 899; goto _test_eof;
+ _test_eof900: cs = 900; goto _test_eof;
+ _test_eof1444: cs = 1444; goto _test_eof;
+ _test_eof901: cs = 901; goto _test_eof;
+ _test_eof902: cs = 902; goto _test_eof;
+ _test_eof903: cs = 903; goto _test_eof;
+ _test_eof904: cs = 904; goto _test_eof;
+ _test_eof905: cs = 905; goto _test_eof;
+ _test_eof906: cs = 906; goto _test_eof;
+ _test_eof907: cs = 907; goto _test_eof;
+ _test_eof908: cs = 908; goto _test_eof;
+ _test_eof909: cs = 909; goto _test_eof;
+ _test_eof910: cs = 910; goto _test_eof;
+ _test_eof911: cs = 911; goto _test_eof;
+ _test_eof912: cs = 912; goto _test_eof;
+ _test_eof913: cs = 913; goto _test_eof;
+ _test_eof914: cs = 914; goto _test_eof;
+ _test_eof915: cs = 915; goto _test_eof;
+ _test_eof916: cs = 916; goto _test_eof;
+ _test_eof917: cs = 917; goto _test_eof;
+ _test_eof918: cs = 918; goto _test_eof;
+ _test_eof919: cs = 919; goto _test_eof;
+ _test_eof920: cs = 920; goto _test_eof;
+ _test_eof921: cs = 921; goto _test_eof;
+ _test_eof922: cs = 922; goto _test_eof;
+ _test_eof923: cs = 923; goto _test_eof;
+ _test_eof924: cs = 924; goto _test_eof;
+ _test_eof925: cs = 925; goto _test_eof;
+ _test_eof926: cs = 926; goto _test_eof;
+ _test_eof927: cs = 927; goto _test_eof;
+ _test_eof1445: cs = 1445; goto _test_eof;
+ _test_eof1446: cs = 1446; goto _test_eof;
+ _test_eof1447: cs = 1447; goto _test_eof;
+ _test_eof928: cs = 928; goto _test_eof;
+ _test_eof929: cs = 929; goto _test_eof;
+ _test_eof930: cs = 930; goto _test_eof;
+ _test_eof1448: cs = 1448; goto _test_eof;
+ _test_eof1449: cs = 1449; goto _test_eof;
+ _test_eof931: cs = 931; goto _test_eof;
+ _test_eof932: cs = 932; goto _test_eof;
+ _test_eof933: cs = 933; goto _test_eof;
+ _test_eof934: cs = 934; goto _test_eof;
+ _test_eof1450: cs = 1450; goto _test_eof;
+ _test_eof1451: cs = 1451; goto _test_eof;
+ _test_eof935: cs = 935; goto _test_eof;
+ _test_eof936: cs = 936; goto _test_eof;
+ _test_eof937: cs = 937; goto _test_eof;
+ _test_eof938: cs = 938; goto _test_eof;
+ _test_eof1452: cs = 1452; goto _test_eof;
+ _test_eof1453: cs = 1453; goto _test_eof;
+ _test_eof939: cs = 939; goto _test_eof;
+ _test_eof940: cs = 940; goto _test_eof;
+ _test_eof941: cs = 941; goto _test_eof;
+ _test_eof942: cs = 942; goto _test_eof;
+ _test_eof943: cs = 943; goto _test_eof;
+ _test_eof944: cs = 944; goto _test_eof;
+ _test_eof945: cs = 945; goto _test_eof;
+ _test_eof946: cs = 946; goto _test_eof;
+ _test_eof947: cs = 947; goto _test_eof;
+ _test_eof948: cs = 948; goto _test_eof;
+ _test_eof949: cs = 949; goto _test_eof;
+ _test_eof950: cs = 950; goto _test_eof;
+ _test_eof951: cs = 951; goto _test_eof;
+ _test_eof952: cs = 952; goto _test_eof;
+ _test_eof953: cs = 953; goto _test_eof;
+ _test_eof954: cs = 954; goto _test_eof;
+ _test_eof955: cs = 955; goto _test_eof;
+ _test_eof956: cs = 956; goto _test_eof;
+ _test_eof957: cs = 957; goto _test_eof;
+ _test_eof958: cs = 958; goto _test_eof;
+ _test_eof959: cs = 959; goto _test_eof;
+ _test_eof960: cs = 960; goto _test_eof;
+ _test_eof961: cs = 961; goto _test_eof;
+ _test_eof962: cs = 962; goto _test_eof;
+ _test_eof963: cs = 963; goto _test_eof;
+ _test_eof1454: cs = 1454; goto _test_eof;
+ _test_eof964: cs = 964; goto _test_eof;
+ _test_eof965: cs = 965; goto _test_eof;
+ _test_eof966: cs = 966; goto _test_eof;
+ _test_eof967: cs = 967; goto _test_eof;
+ _test_eof968: cs = 968; goto _test_eof;
+ _test_eof969: cs = 969; goto _test_eof;
+ _test_eof970: cs = 970; goto _test_eof;
+ _test_eof971: cs = 971; goto _test_eof;
+ _test_eof972: cs = 972; goto _test_eof;
+ _test_eof973: cs = 973; goto _test_eof;
+ _test_eof974: cs = 974; goto _test_eof;
+ _test_eof975: cs = 975; goto _test_eof;
+ _test_eof976: cs = 976; goto _test_eof;
+ _test_eof977: cs = 977; goto _test_eof;
+ _test_eof978: cs = 978; goto _test_eof;
+ _test_eof1455: cs = 1455; goto _test_eof;
+ _test_eof979: cs = 979; goto _test_eof;
+ _test_eof980: cs = 980; goto _test_eof;
+ _test_eof981: cs = 981; goto _test_eof;
+ _test_eof982: cs = 982; goto _test_eof;
+ _test_eof983: cs = 983; goto _test_eof;
+ _test_eof984: cs = 984; goto _test_eof;
+ _test_eof985: cs = 985; goto _test_eof;
+ _test_eof986: cs = 986; goto _test_eof;
+ _test_eof987: cs = 987; goto _test_eof;
+ _test_eof988: cs = 988; goto _test_eof;
+ _test_eof989: cs = 989; goto _test_eof;
+ _test_eof990: cs = 990; goto _test_eof;
+ _test_eof991: cs = 991; goto _test_eof;
+ _test_eof1456: cs = 1456; goto _test_eof;
+ _test_eof992: cs = 992; goto _test_eof;
+ _test_eof993: cs = 993; goto _test_eof;
+ _test_eof994: cs = 994; goto _test_eof;
+ _test_eof995: cs = 995; goto _test_eof;
+ _test_eof996: cs = 996; goto _test_eof;
+ _test_eof997: cs = 997; goto _test_eof;
+ _test_eof998: cs = 998; goto _test_eof;
+ _test_eof999: cs = 999; goto _test_eof;
+ _test_eof1000: cs = 1000; goto _test_eof;
+ _test_eof1001: cs = 1001; goto _test_eof;
+ _test_eof1002: cs = 1002; goto _test_eof;
+ _test_eof1457: cs = 1457; goto _test_eof;
+ _test_eof1458: cs = 1458; goto _test_eof;
+ _test_eof1003: cs = 1003; goto _test_eof;
+ _test_eof1004: cs = 1004; goto _test_eof;
+ _test_eof1005: cs = 1005; goto _test_eof;
+ _test_eof1459: cs = 1459; goto _test_eof;
+ _test_eof1006: cs = 1006; goto _test_eof;
+ _test_eof1007: cs = 1007; goto _test_eof;
+ _test_eof1008: cs = 1008; goto _test_eof;
+ _test_eof1009: cs = 1009; goto _test_eof;
+ _test_eof1010: cs = 1010; goto _test_eof;
+ _test_eof1011: cs = 1011; goto _test_eof;
+ _test_eof1012: cs = 1012; goto _test_eof;
+ _test_eof1013: cs = 1013; goto _test_eof;
+ _test_eof1014: cs = 1014; goto _test_eof;
+ _test_eof1015: cs = 1015; goto _test_eof;
+ _test_eof1460: cs = 1460; goto _test_eof;
+ _test_eof1461: cs = 1461; goto _test_eof;
+ _test_eof1462: cs = 1462; goto _test_eof;
+ _test_eof1016: cs = 1016; goto _test_eof;
+ _test_eof1017: cs = 1017; goto _test_eof;
+ _test_eof1018: cs = 1018; goto _test_eof;
+ _test_eof1019: cs = 1019; goto _test_eof;
+ _test_eof1020: cs = 1020; goto _test_eof;
+ _test_eof1021: cs = 1021; goto _test_eof;
+ _test_eof1022: cs = 1022; goto _test_eof;
+ _test_eof1023: cs = 1023; goto _test_eof;
+ _test_eof1024: cs = 1024; goto _test_eof;
+ _test_eof1025: cs = 1025; goto _test_eof;
+ _test_eof1026: cs = 1026; goto _test_eof;
+ _test_eof1463: cs = 1463; goto _test_eof;
+ _test_eof1464: cs = 1464; goto _test_eof;
+ _test_eof1465: cs = 1465; goto _test_eof;
+ _test_eof1027: cs = 1027; goto _test_eof;
+ _test_eof1028: cs = 1028; goto _test_eof;
+ _test_eof1029: cs = 1029; goto _test_eof;
+ _test_eof1030: cs = 1030; goto _test_eof;
+ _test_eof1031: cs = 1031; goto _test_eof;
+ _test_eof1032: cs = 1032; goto _test_eof;
+ _test_eof1033: cs = 1033; goto _test_eof;
+ _test_eof1034: cs = 1034; goto _test_eof;
+ _test_eof1035: cs = 1035; goto _test_eof;
+ _test_eof1036: cs = 1036; goto _test_eof;
+ _test_eof1037: cs = 1037; goto _test_eof;
+ _test_eof1038: cs = 1038; goto _test_eof;
+ _test_eof1466: cs = 1466; goto _test_eof;
+ _test_eof1039: cs = 1039; goto _test_eof;
+ _test_eof1040: cs = 1040; goto _test_eof;
+ _test_eof1041: cs = 1041; goto _test_eof;
+ _test_eof1467: cs = 1467; goto _test_eof;
+ _test_eof1468: cs = 1468; goto _test_eof;
+ _test_eof1042: cs = 1042; goto _test_eof;
+ _test_eof1469: cs = 1469; goto _test_eof;
+ _test_eof1470: cs = 1470; goto _test_eof;
+ _test_eof1043: cs = 1043; goto _test_eof;
+ _test_eof1471: cs = 1471; goto _test_eof;
+ _test_eof1472: cs = 1472; goto _test_eof;
+ _test_eof1044: cs = 1044; goto _test_eof;
+ _test_eof1045: cs = 1045; goto _test_eof;
+ _test_eof1046: cs = 1046; goto _test_eof;
+ _test_eof1047: cs = 1047; goto _test_eof;
+ _test_eof1048: cs = 1048; goto _test_eof;
+ _test_eof1049: cs = 1049; goto _test_eof;
+ _test_eof1050: cs = 1050; goto _test_eof;
+ _test_eof1051: cs = 1051; goto _test_eof;
+ _test_eof1052: cs = 1052; goto _test_eof;
+ _test_eof1053: cs = 1053; goto _test_eof;
+ _test_eof1054: cs = 1054; goto _test_eof;
+ _test_eof1055: cs = 1055; goto _test_eof;
+ _test_eof1056: cs = 1056; goto _test_eof;
+ _test_eof1057: cs = 1057; goto _test_eof;
+ _test_eof1058: cs = 1058; goto _test_eof;
+ _test_eof1059: cs = 1059; goto _test_eof;
+ _test_eof1060: cs = 1060; goto _test_eof;
+ _test_eof1061: cs = 1061; goto _test_eof;
+ _test_eof1062: cs = 1062; goto _test_eof;
+ _test_eof1063: cs = 1063; goto _test_eof;
+ _test_eof1064: cs = 1064; goto _test_eof;
+ _test_eof1065: cs = 1065; goto _test_eof;
+ _test_eof1066: cs = 1066; goto _test_eof;
+ _test_eof1067: cs = 1067; goto _test_eof;
+ _test_eof1068: cs = 1068; goto _test_eof;
+ _test_eof1069: cs = 1069; goto _test_eof;
+ _test_eof1070: cs = 1070; goto _test_eof;
+ _test_eof1071: cs = 1071; goto _test_eof;
+ _test_eof1072: cs = 1072; goto _test_eof;
+ _test_eof1073: cs = 1073; goto _test_eof;
+ _test_eof1074: cs = 1074; goto _test_eof;
+ _test_eof1075: cs = 1075; goto _test_eof;
+ _test_eof1076: cs = 1076; goto _test_eof;
+ _test_eof1077: cs = 1077; goto _test_eof;
+ _test_eof1078: cs = 1078; goto _test_eof;
+ _test_eof1079: cs = 1079; goto _test_eof;
+ _test_eof1080: cs = 1080; goto _test_eof;
+ _test_eof1081: cs = 1081; goto _test_eof;
+ _test_eof1082: cs = 1082; goto _test_eof;
+ _test_eof1083: cs = 1083; goto _test_eof;
+ _test_eof1084: cs = 1084; goto _test_eof;
+ _test_eof1085: cs = 1085; goto _test_eof;
+ _test_eof1473: cs = 1473; goto _test_eof;
+ _test_eof1086: cs = 1086; goto _test_eof;
+ _test_eof1087: cs = 1087; goto _test_eof;
+ _test_eof1088: cs = 1088; goto _test_eof;
+ _test_eof1089: cs = 1089; goto _test_eof;
+ _test_eof1090: cs = 1090; goto _test_eof;
+ _test_eof1091: cs = 1091; goto _test_eof;
+ _test_eof1092: cs = 1092; goto _test_eof;
+ _test_eof1093: cs = 1093; goto _test_eof;
+ _test_eof1094: cs = 1094; goto _test_eof;
+ _test_eof1095: cs = 1095; goto _test_eof;
+ _test_eof1096: cs = 1096; goto _test_eof;
+ _test_eof1097: cs = 1097; goto _test_eof;
+ _test_eof1098: cs = 1098; goto _test_eof;
+ _test_eof1099: cs = 1099; goto _test_eof;
+ _test_eof1100: cs = 1100; goto _test_eof;
+ _test_eof1101: cs = 1101; goto _test_eof;
+ _test_eof1102: cs = 1102; goto _test_eof;
+ _test_eof1103: cs = 1103; goto _test_eof;
+ _test_eof1104: cs = 1104; goto _test_eof;
+ _test_eof1105: cs = 1105; goto _test_eof;
+ _test_eof1106: cs = 1106; goto _test_eof;
+ _test_eof1107: cs = 1107; goto _test_eof;
+ _test_eof1108: cs = 1108; goto _test_eof;
+ _test_eof1109: cs = 1109; goto _test_eof;
+ _test_eof1110: cs = 1110; goto _test_eof;
+ _test_eof1111: cs = 1111; goto _test_eof;
+ _test_eof1112: cs = 1112; goto _test_eof;
+ _test_eof1113: cs = 1113; goto _test_eof;
+ _test_eof1114: cs = 1114; goto _test_eof;
+ _test_eof1115: cs = 1115; goto _test_eof;
+ _test_eof1116: cs = 1116; goto _test_eof;
+ _test_eof1117: cs = 1117; goto _test_eof;
+ _test_eof1118: cs = 1118; goto _test_eof;
+ _test_eof1119: cs = 1119; goto _test_eof;
+ _test_eof1120: cs = 1120; goto _test_eof;
+ _test_eof1121: cs = 1121; goto _test_eof;
+ _test_eof1122: cs = 1122; goto _test_eof;
+ _test_eof1123: cs = 1123; goto _test_eof;
+ _test_eof1124: cs = 1124; goto _test_eof;
+ _test_eof1125: cs = 1125; goto _test_eof;
+ _test_eof1126: cs = 1126; goto _test_eof;
+ _test_eof1127: cs = 1127; goto _test_eof;
+ _test_eof1128: cs = 1128; goto _test_eof;
+ _test_eof1129: cs = 1129; goto _test_eof;
+ _test_eof1130: cs = 1130; goto _test_eof;
+ _test_eof1131: cs = 1131; goto _test_eof;
+ _test_eof1132: cs = 1132; goto _test_eof;
+ _test_eof1133: cs = 1133; goto _test_eof;
+ _test_eof1134: cs = 1134; goto _test_eof;
+ _test_eof1135: cs = 1135; goto _test_eof;
+ _test_eof1136: cs = 1136; goto _test_eof;
+ _test_eof1137: cs = 1137; goto _test_eof;
+ _test_eof1138: cs = 1138; goto _test_eof;
+ _test_eof1139: cs = 1139; goto _test_eof;
+ _test_eof1140: cs = 1140; goto _test_eof;
+ _test_eof1141: cs = 1141; goto _test_eof;
+ _test_eof1142: cs = 1142; goto _test_eof;
+ _test_eof1143: cs = 1143; goto _test_eof;
+ _test_eof1144: cs = 1144; goto _test_eof;
+ _test_eof1145: cs = 1145; goto _test_eof;
+ _test_eof1146: cs = 1146; goto _test_eof;
+ _test_eof1147: cs = 1147; goto _test_eof;
+ _test_eof1148: cs = 1148; goto _test_eof;
+ _test_eof1149: cs = 1149; goto _test_eof;
+ _test_eof1150: cs = 1150; goto _test_eof;
+ _test_eof1151: cs = 1151; goto _test_eof;
+ _test_eof1152: cs = 1152; goto _test_eof;
+ _test_eof1153: cs = 1153; goto _test_eof;
+ _test_eof1154: cs = 1154; goto _test_eof;
+ _test_eof1155: cs = 1155; goto _test_eof;
+ _test_eof1156: cs = 1156; goto _test_eof;
+ _test_eof1157: cs = 1157; goto _test_eof;
+ _test_eof1158: cs = 1158; goto _test_eof;
+ _test_eof1159: cs = 1159; goto _test_eof;
+ _test_eof1160: cs = 1160; goto _test_eof;
+ _test_eof1161: cs = 1161; goto _test_eof;
+ _test_eof1162: cs = 1162; goto _test_eof;
+ _test_eof1163: cs = 1163; goto _test_eof;
+ _test_eof1164: cs = 1164; goto _test_eof;
+ _test_eof1165: cs = 1165; goto _test_eof;
+ _test_eof1166: cs = 1166; goto _test_eof;
+ _test_eof1167: cs = 1167; goto _test_eof;
+ _test_eof1168: cs = 1168; goto _test_eof;
+ _test_eof1169: cs = 1169; goto _test_eof;
+ _test_eof1170: cs = 1170; goto _test_eof;
+ _test_eof1171: cs = 1171; goto _test_eof;
+ _test_eof1172: cs = 1172; goto _test_eof;
+ _test_eof1173: cs = 1173; goto _test_eof;
+ _test_eof1174: cs = 1174; goto _test_eof;
+ _test_eof1175: cs = 1175; goto _test_eof;
+ _test_eof1176: cs = 1176; goto _test_eof;
+ _test_eof1177: cs = 1177; goto _test_eof;
+ _test_eof1178: cs = 1178; goto _test_eof;
+ _test_eof1179: cs = 1179; goto _test_eof;
+ _test_eof1180: cs = 1180; goto _test_eof;
+ _test_eof1181: cs = 1181; goto _test_eof;
+ _test_eof1182: cs = 1182; goto _test_eof;
+ _test_eof1183: cs = 1183; goto _test_eof;
+ _test_eof1184: cs = 1184; goto _test_eof;
+ _test_eof1185: cs = 1185; goto _test_eof;
+ _test_eof1186: cs = 1186; goto _test_eof;
+ _test_eof1187: cs = 1187; goto _test_eof;
+ _test_eof1188: cs = 1188; goto _test_eof;
+ _test_eof1189: cs = 1189; goto _test_eof;
+ _test_eof1190: cs = 1190; goto _test_eof;
+ _test_eof1191: cs = 1191; goto _test_eof;
+ _test_eof1192: cs = 1192; goto _test_eof;
+ _test_eof1193: cs = 1193; goto _test_eof;
+ _test_eof1194: cs = 1194; goto _test_eof;
+ _test_eof1195: cs = 1195; goto _test_eof;
+ _test_eof1196: cs = 1196; goto _test_eof;
+ _test_eof1197: cs = 1197; goto _test_eof;
+ _test_eof1198: cs = 1198; goto _test_eof;
+ _test_eof1199: cs = 1199; goto _test_eof;
+ _test_eof1200: cs = 1200; goto _test_eof;
+ _test_eof1201: cs = 1201; goto _test_eof;
+ _test_eof1202: cs = 1202; goto _test_eof;
+ _test_eof1203: cs = 1203; goto _test_eof;
+ _test_eof1204: cs = 1204; goto _test_eof;
+ _test_eof1205: cs = 1205; goto _test_eof;
+ _test_eof1206: cs = 1206; goto _test_eof;
+ _test_eof1207: cs = 1207; goto _test_eof;
+ _test_eof1208: cs = 1208; goto _test_eof;
+ _test_eof1209: cs = 1209; goto _test_eof;
+ _test_eof1210: cs = 1210; goto _test_eof;
+ _test_eof1211: cs = 1211; goto _test_eof;
+ _test_eof1212: cs = 1212; goto _test_eof;
+ _test_eof1213: cs = 1213; goto _test_eof;
+ _test_eof1214: cs = 1214; goto _test_eof;
+ _test_eof1215: cs = 1215; goto _test_eof;
+ _test_eof1216: cs = 1216; goto _test_eof;
+ _test_eof1217: cs = 1217; goto _test_eof;
+ _test_eof1218: cs = 1218; goto _test_eof;
+ _test_eof1219: cs = 1219; goto _test_eof;
+ _test_eof1220: cs = 1220; goto _test_eof;
+ _test_eof1221: cs = 1221; goto _test_eof;
+ _test_eof1222: cs = 1222; goto _test_eof;
+ _test_eof1223: cs = 1223; goto _test_eof;
+ _test_eof1224: cs = 1224; goto _test_eof;
+ _test_eof1225: cs = 1225; goto _test_eof;
+ _test_eof1226: cs = 1226; goto _test_eof;
+ _test_eof1227: cs = 1227; goto _test_eof;
+ _test_eof1228: cs = 1228; goto _test_eof;
+ _test_eof1229: cs = 1229; goto _test_eof;
+ _test_eof1230: cs = 1230; goto _test_eof;
+ _test_eof1231: cs = 1231; goto _test_eof;
+ _test_eof1232: cs = 1232; goto _test_eof;
+ _test_eof1233: cs = 1233; goto _test_eof;
+ _test_eof1234: cs = 1234; goto _test_eof;
+ _test_eof1235: cs = 1235; goto _test_eof;
+ _test_eof1236: cs = 1236; goto _test_eof;
+ _test_eof1237: cs = 1237; goto _test_eof;
+ _test_eof1238: cs = 1238; goto _test_eof;
+ _test_eof1239: cs = 1239; goto _test_eof;
+ _test_eof1240: cs = 1240; goto _test_eof;
+ _test_eof1241: cs = 1241; goto _test_eof;
+ _test_eof1242: cs = 1242; goto _test_eof;
+ _test_eof1243: cs = 1243; goto _test_eof;
+ _test_eof1244: cs = 1244; goto _test_eof;
+ _test_eof1245: cs = 1245; goto _test_eof;
+ _test_eof1246: cs = 1246; goto _test_eof;
+ _test_eof1247: cs = 1247; goto _test_eof;
+ _test_eof1248: cs = 1248; goto _test_eof;
+ _test_eof1249: cs = 1249; goto _test_eof;
+ _test_eof1250: cs = 1250; goto _test_eof;
+ _test_eof1251: cs = 1251; goto _test_eof;
+ _test_eof1252: cs = 1252; goto _test_eof;
+ _test_eof1253: cs = 1253; goto _test_eof;
+ _test_eof1254: cs = 1254; goto _test_eof;
+ _test_eof1255: cs = 1255; goto _test_eof;
+ _test_eof1256: cs = 1256; goto _test_eof;
+ _test_eof1474: cs = 1474; goto _test_eof;
+ _test_eof1257: cs = 1257; goto _test_eof;
+ _test_eof1258: cs = 1258; goto _test_eof;
+ _test_eof1259: cs = 1259; goto _test_eof;
+ _test_eof1260: cs = 1260; goto _test_eof;
+ _test_eof1261: cs = 1261; goto _test_eof;
+ _test_eof1262: cs = 1262; goto _test_eof;
+ _test_eof1263: cs = 1263; goto _test_eof;
+ _test_eof1264: cs = 1264; goto _test_eof;
+ _test_eof1475: cs = 1475; goto _test_eof;
+ _test_eof1265: cs = 1265; goto _test_eof;
+ _test_eof1266: cs = 1266; goto _test_eof;
+ _test_eof1267: cs = 1267; goto _test_eof;
+ _test_eof1268: cs = 1268; goto _test_eof;
+ _test_eof1269: cs = 1269; goto _test_eof;
+ _test_eof1476: cs = 1476; goto _test_eof;
+ _test_eof1270: cs = 1270; goto _test_eof;
+ _test_eof1271: cs = 1271; goto _test_eof;
+ _test_eof1272: cs = 1272; goto _test_eof;
+ _test_eof1273: cs = 1273; goto _test_eof;
+ _test_eof1274: cs = 1274; goto _test_eof;
+ _test_eof1275: cs = 1275; goto _test_eof;
+ _test_eof1276: cs = 1276; goto _test_eof;
+ _test_eof1277: cs = 1277; goto _test_eof;
+ _test_eof1278: cs = 1278; goto _test_eof;
+ _test_eof1279: cs = 1279; goto _test_eof;
+ _test_eof1280: cs = 1280; goto _test_eof;
+ _test_eof1281: cs = 1281; goto _test_eof;
+ _test_eof1282: cs = 1282; goto _test_eof;
+ _test_eof1283: cs = 1283; goto _test_eof;
+ _test_eof1284: cs = 1284; goto _test_eof;
+ _test_eof1285: cs = 1285; goto _test_eof;
+ _test_eof1286: cs = 1286; goto _test_eof;
+ _test_eof1287: cs = 1287; goto _test_eof;
+ _test_eof1477: cs = 1477; goto _test_eof;
+ _test_eof1288: cs = 1288; goto _test_eof;
+ _test_eof1289: cs = 1289; goto _test_eof;
+ _test_eof1290: cs = 1290; goto _test_eof;
+ _test_eof1291: cs = 1291; goto _test_eof;
+ _test_eof1292: cs = 1292; goto _test_eof;
+ _test_eof1293: cs = 1293; goto _test_eof;
+ _test_eof1294: cs = 1294; goto _test_eof;
+ _test_eof1295: cs = 1295; goto _test_eof;
+ _test_eof1296: cs = 1296; goto _test_eof;
+ _test_eof1297: cs = 1297; goto _test_eof;
+ _test_eof1298: cs = 1298; goto _test_eof;
+ _test_eof1299: cs = 1299; goto _test_eof;
+ _test_eof1300: cs = 1300; goto _test_eof;
+ _test_eof1301: cs = 1301; goto _test_eof;
+ _test_eof1302: cs = 1302; goto _test_eof;
+ _test_eof1303: cs = 1303; goto _test_eof;
+ _test_eof1304: cs = 1304; goto _test_eof;
+ _test_eof1305: cs = 1305; goto _test_eof;
+ _test_eof1306: cs = 1306; goto _test_eof;
+ _test_eof1478: cs = 1478; goto _test_eof;
+ _test_eof1307: cs = 1307; goto _test_eof;
+ _test_eof1308: cs = 1308; goto _test_eof;
+ _test_eof1309: cs = 1309; goto _test_eof;
+ _test_eof1310: cs = 1310; goto _test_eof;
+ _test_eof1311: cs = 1311; goto _test_eof;
+ _test_eof1312: cs = 1312; goto _test_eof;
+ _test_eof1313: cs = 1313; goto _test_eof;
+ _test_eof1314: cs = 1314; goto _test_eof;
+ _test_eof1315: cs = 1315; goto _test_eof;
+ _test_eof1316: cs = 1316; goto _test_eof;
+ _test_eof1317: cs = 1317; goto _test_eof;
+ _test_eof1318: cs = 1318; goto _test_eof;
+ _test_eof1319: cs = 1319; goto _test_eof;
+ _test_eof1320: cs = 1320; goto _test_eof;
+ _test_eof1321: cs = 1321; goto _test_eof;
+ _test_eof1479: cs = 1479; goto _test_eof;
+ _test_eof1480: cs = 1480; goto _test_eof;
+ _test_eof1481: cs = 1481; goto _test_eof;
+ _test_eof1322: cs = 1322; goto _test_eof;
+ _test_eof1323: cs = 1323; goto _test_eof;
+ _test_eof1324: cs = 1324; goto _test_eof;
+ _test_eof1325: cs = 1325; goto _test_eof;
+ _test_eof1326: cs = 1326; goto _test_eof;
+ _test_eof1327: cs = 1327; goto _test_eof;
+ _test_eof1328: cs = 1328; goto _test_eof;
+ _test_eof1329: cs = 1329; goto _test_eof;
+ _test_eof1482: cs = 1482; goto _test_eof;
+ _test_eof1330: cs = 1330; goto _test_eof;
+ _test_eof1331: cs = 1331; goto _test_eof;
+ _test_eof1332: cs = 1332; goto _test_eof;
+ _test_eof1333: cs = 1333; goto _test_eof;
+ _test_eof1334: cs = 1334; goto _test_eof;
+ _test_eof1335: cs = 1335; goto _test_eof;
+ _test_eof1336: cs = 1336; goto _test_eof;
+ _test_eof1337: cs = 1337; goto _test_eof;
+ _test_eof1338: cs = 1338; goto _test_eof;
+ _test_eof1339: cs = 1339; goto _test_eof;
+ _test_eof1340: cs = 1340; goto _test_eof;
+ _test_eof1483: cs = 1483; goto _test_eof;
+ _test_eof1484: cs = 1484; goto _test_eof;
+ _test_eof1485: cs = 1485; goto _test_eof;
+ _test_eof1341: cs = 1341; goto _test_eof;
+ _test_eof1342: cs = 1342; goto _test_eof;
+ _test_eof1343: cs = 1343; goto _test_eof;
+ _test_eof1344: cs = 1344; goto _test_eof;
+ _test_eof1345: cs = 1345; goto _test_eof;
+ _test_eof1346: cs = 1346; goto _test_eof;
+ _test_eof1347: cs = 1347; goto _test_eof;
+ _test_eof1486: cs = 1486; goto _test_eof;
+ _test_eof1348: cs = 1348; goto _test_eof;
+ _test_eof1349: cs = 1349; goto _test_eof;
+ _test_eof1350: cs = 1350; goto _test_eof;
+ _test_eof1351: cs = 1351; goto _test_eof;
+ _test_eof1352: cs = 1352; goto _test_eof;
+ _test_eof1353: cs = 1353; goto _test_eof;
+ _test_eof1354: cs = 1354; goto _test_eof;
+ _test_eof1355: cs = 1355; goto _test_eof;
+ _test_eof1356: cs = 1356; goto _test_eof;
+ _test_eof1357: cs = 1357; goto _test_eof;
+ _test_eof1358: cs = 1358; goto _test_eof;
+ _test_eof1359: cs = 1359; goto _test_eof;
+ _test_eof1360: cs = 1360; goto _test_eof;
+ _test_eof1487: cs = 1487; goto _test_eof;
+ _test_eof1361: cs = 1361; goto _test_eof;
+ _test_eof1362: cs = 1362; goto _test_eof;
+ _test_eof1363: cs = 1363; goto _test_eof;
+ _test_eof1364: cs = 1364; goto _test_eof;
+ _test_eof1365: cs = 1365; goto _test_eof;
+ _test_eof1366: cs = 1366; goto _test_eof;
+ _test_eof1367: cs = 1367; goto _test_eof;
+ _test_eof1488: cs = 1488; goto _test_eof;
+ _test_eof1368: cs = 1368; goto _test_eof;
+ _test_eof1369: cs = 1369; goto _test_eof;
+ _test_eof1370: cs = 1370; goto _test_eof;
+ _test_eof1371: cs = 1371; goto _test_eof;
+ _test_eof1372: cs = 1372; goto _test_eof;
+ _test_eof1373: cs = 1373; goto _test_eof;
+ _test_eof1489: cs = 1489; goto _test_eof;
+ _test_eof1374: cs = 1374; goto _test_eof;
+ _test_eof1375: cs = 1375; goto _test_eof;
+ _test_eof1376: cs = 1376; goto _test_eof;
+ _test_eof1377: cs = 1377; goto _test_eof;
+ _test_eof1378: cs = 1378; goto _test_eof;
+ _test_eof1379: cs = 1379; goto _test_eof;
+ _test_eof1490: cs = 1490; goto _test_eof;
+ _test_eof1380: cs = 1380; goto _test_eof;
+ _test_eof1381: cs = 1381; goto _test_eof;
+ _test_eof1382: cs = 1382; goto _test_eof;
+ _test_eof1383: cs = 1383; goto _test_eof;
+ _test_eof1384: cs = 1384; goto _test_eof;
+ _test_eof1385: cs = 1385; goto _test_eof;
+ _test_eof1386: cs = 1386; goto _test_eof;
+ _test_eof1387: cs = 1387; goto _test_eof;
+ _test_eof1491: cs = 1491; goto _test_eof;
+ _test_eof1388: cs = 1388; goto _test_eof;
+ _test_eof1389: cs = 1389; goto _test_eof;
+ _test_eof1390: cs = 1390; goto _test_eof;
+ _test_eof1391: cs = 1391; goto _test_eof;
+ _test_eof1392: cs = 1392; goto _test_eof;
+ _test_eof1393: cs = 1393; goto _test_eof;
+ _test_eof1394: cs = 1394; goto _test_eof;
+ _test_eof1492: cs = 1492; goto _test_eof;
+ _test_eof1395: cs = 1395; goto _test_eof;
+
+ _test_eof: {}
+ if ( p == eof )
+ {
+ switch ( cs ) {
+ case 7:
+ case 8:
+ case 183:
+ case 207:
+ case 209:
+ case 263:
+ case 277:
+ case 302:
+ case 358:
+ case 359:
+ case 365:
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 309:
+ case 310:
+ case 311:
+ case 316:
+ case 317:
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 192:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 326:
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 331:
+ case 339:
+ {
+ ERR(ZS_BAD_TTL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 340:
+ case 341:
+ case 342:
+ case 351:
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 353:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 356:
+ case 366:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1400:
+ {
+ NOERR;
+ }
+ break;
+ case 184:
+ case 185:
+ case 186:
+ case 187:
+ case 188:
+ case 189:
+ case 190:
+ case 191:
+ case 229:
+ case 230:
+ case 231:
+ case 232:
+ case 233:
+ case 234:
+ case 235:
+ case 236:
+ case 237:
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 369:
+ case 370:
+ case 371:
+ case 372:
+ case 373:
+ case 374:
+ case 375:
+ {
+ WARN(ZS_BAD_BASE64_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 376:
+ case 377:
+ case 378:
+ case 379:
+ case 380:
+ case 381:
+ case 382:
+ case 383:
+ case 384:
+ case 385:
+ case 386:
+ case 387:
+ case 388:
+ case 389:
+ case 390:
+ case 391:
+ case 392:
+ case 393:
+ case 394:
+ case 395:
+ case 396:
+ case 397:
+ case 398:
+ case 399:
+ case 400:
+ case 401:
+ case 402:
+ case 403:
+ case 404:
+ case 405:
+ case 406:
+ case 407:
+ case 408:
+ case 409:
+ case 410:
+ case 411:
+ case 412:
+ case 413:
+ case 414:
+ case 415:
+ case 416:
+ case 417:
+ case 418:
+ case 419:
+ case 420:
+ case 421:
+ case 422:
+ case 423:
+ case 424:
+ case 425:
+ case 426:
+ case 427:
+ case 428:
+ case 429:
+ case 430:
+ case 431:
+ case 432:
+ case 433:
+ case 434:
+ case 435:
+ case 436:
+ case 437:
+ case 438:
+ case 439:
+ case 440:
+ case 441:
+ case 442:
+ case 443:
+ case 444:
+ case 445:
+ case 446:
+ case 447:
+ case 448:
+ case 449:
+ case 450:
+ case 451:
+ case 452:
+ case 453:
+ case 454:
+ case 455:
+ case 456:
+ case 457:
+ case 458:
+ case 459:
+ case 460:
+ case 461:
+ case 462:
+ case 463:
+ case 464:
+ case 465:
+ case 466:
+ case 467:
+ case 468:
+ case 469:
+ case 470:
+ case 471:
+ case 472:
+ case 473:
+ case 474:
+ case 475:
+ case 476:
+ case 477:
+ case 478:
+ case 479:
+ case 480:
+ case 481:
+ case 482:
+ case 483:
+ case 484:
+ case 485:
+ case 486:
+ case 487:
+ case 488:
+ case 489:
+ case 490:
+ case 491:
+ case 492:
+ case 493:
+ case 494:
+ case 495:
+ case 496:
+ case 497:
+ case 498:
+ case 499:
+ case 500:
+ case 501:
+ case 502:
+ case 503:
+ case 504:
+ case 505:
+ case 506:
+ case 507:
+ case 508:
+ case 509:
+ case 510:
+ case 511:
+ case 512:
+ case 513:
+ case 514:
+ case 515:
+ case 516:
+ case 517:
+ case 518:
+ case 519:
+ case 520:
+ case 521:
+ case 522:
+ case 523:
+ case 524:
+ case 525:
+ case 526:
+ case 527:
+ case 528:
+ case 529:
+ case 530:
+ case 531:
+ case 532:
+ case 533:
+ case 534:
+ case 535:
+ case 536:
+ case 537:
+ case 538:
+ case 539:
+ {
+ WARN(ZS_BAD_BITMAP);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 540:
+ case 547:
+ case 548:
+ case 549:
+ case 556:
+ case 558:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 603:
+ case 604:
+ case 605:
+ case 606:
+ case 607:
+ case 608:
+ case 609:
+ case 610:
+ case 611:
+ case 612:
+ case 613:
+ case 614:
+ case 615:
+ case 616:
+ case 617:
+ case 618:
+ case 619:
+ case 620:
+ case 621:
+ case 622:
+ case 623:
+ case 624:
+ case 625:
+ case 626:
+ case 627:
+ case 628:
+ case 629:
+ case 630:
+ case 631:
+ case 632:
+ case 633:
+ case 634:
+ case 635:
+ case 642:
+ case 643:
+ case 644:
+ case 645:
+ case 646:
+ case 653:
+ case 654:
+ case 657:
+ case 658:
+ case 659:
+ case 660:
+ case 661:
+ case 662:
+ case 663:
+ case 664:
+ case 665:
+ case 666:
+ case 667:
+ case 668:
+ case 669:
+ case 670:
+ case 671:
+ case 672:
+ case 673:
+ case 674:
+ case 675:
+ case 676:
+ case 677:
+ case 678:
+ case 679:
+ case 680:
+ case 681:
+ case 682:
+ case 683:
+ case 684:
+ case 685:
+ case 686:
+ case 687:
+ case 688:
+ case 689:
+ case 690:
+ case 691:
+ case 692:
+ case 697:
+ case 698:
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 559:
+ case 560:
+ case 561:
+ case 562:
+ case 563:
+ case 564:
+ case 565:
+ case 566:
+ case 567:
+ case 568:
+ case 569:
+ case 570:
+ case 571:
+ case 572:
+ case 573:
+ case 574:
+ case 575:
+ case 576:
+ case 577:
+ case 578:
+ case 579:
+ case 580:
+ case 581:
+ case 584:
+ case 585:
+ case 586:
+ case 587:
+ case 588:
+ case 589:
+ case 590:
+ case 591:
+ case 592:
+ case 593:
+ case 594:
+ case 595:
+ case 596:
+ case 597:
+ case 598:
+ case 599:
+ case 600:
+ case 601:
+ case 602:
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 699:
+ case 700:
+ case 701:
+ case 702:
+ case 703:
+ case 704:
+ case 705:
+ case 706:
+ case 707:
+ case 708:
+ case 709:
+ case 710:
+ case 711:
+ case 712:
+ case 713:
+ case 714:
+ case 715:
+ case 716:
+ case 717:
+ case 718:
+ case 719:
+ case 720:
+ case 721:
+ case 722:
+ case 723:
+ case 724:
+ case 725:
+ case 726:
+ case 727:
+ case 728:
+ case 729:
+ case 730:
+ case 731:
+ case 732:
+ case 733:
+ case 734:
+ case 735:
+ case 736:
+ case 737:
+ case 738:
+ case 739:
+ case 740:
+ case 741:
+ case 742:
+ case 743:
+ case 744:
+ case 745:
+ case 746:
+ case 747:
+ case 748:
+ case 749:
+ case 750:
+ case 751:
+ case 752:
+ case 753:
+ case 754:
+ case 755:
+ case 756:
+ case 757:
+ case 758:
+ case 759:
+ case 760:
+ case 761:
+ case 762:
+ case 763:
+ case 764:
+ case 765:
+ case 766:
+ case 767:
+ case 768:
+ case 769:
+ case 770:
+ case 771:
+ case 772:
+ case 773:
+ case 774:
+ case 775:
+ case 776:
+ case 777:
+ case 778:
+ case 779:
+ case 780:
+ case 781:
+ case 782:
+ case 783:
+ case 784:
+ case 785:
+ case 786:
+ case 787:
+ case 788:
+ case 789:
+ case 790:
+ case 791:
+ case 792:
+ case 793:
+ case 794:
+ case 795:
+ case 796:
+ case 797:
+ case 798:
+ case 799:
+ case 800:
+ case 801:
+ case 802:
+ {
+ WARN(ZS_BAD_ALGORITHM);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 803:
+ case 804:
+ case 805:
+ case 806:
+ case 807:
+ case 808:
+ case 809:
+ case 810:
+ case 811:
+ case 812:
+ case 813:
+ case 814:
+ case 815:
+ case 816:
+ case 817:
+ case 818:
+ case 819:
+ case 820:
+ case 821:
+ case 822:
+ case 823:
+ case 824:
+ case 825:
+ case 826:
+ case 827:
+ case 828:
+ case 829:
+ case 830:
+ case 831:
+ case 832:
+ case 833:
+ case 834:
+ case 835:
+ case 836:
+ case 837:
+ case 838:
+ case 839:
+ case 840:
+ case 841:
+ case 842:
+ case 843:
+ {
+ WARN(ZS_BAD_CERT_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 5:
+ case 9:
+ case 10:
+ case 282:
+ case 846:
+ case 847:
+ case 848:
+ case 849:
+ case 850:
+ case 851:
+ case 865:
+ case 869:
+ case 873:
+ case 877:
+ case 878:
+ case 879:
+ case 880:
+ case 881:
+ case 882:
+ case 883:
+ case 884:
+ case 885:
+ case 886:
+ case 887:
+ case 888:
+ case 889:
+ case 892:
+ case 893:
+ case 894:
+ case 962:
+ case 963:
+ case 964:
+ case 965:
+ case 966:
+ case 971:
+ case 972:
+ case 973:
+ case 974:
+ case 975:
+ case 976:
+ case 977:
+ case 978:
+ case 979:
+ case 980:
+ case 981:
+ case 982:
+ case 983:
+ case 984:
+ case 985:
+ case 988:
+ case 989:
+ case 990:
+ case 991:
+ case 992:
+ case 993:
+ case 994:
+ case 1002:
+ case 1008:
+ case 1009:
+ case 1016:
+ case 1017:
+ case 1018:
+ case 1027:
+ case 1028:
+ case 1067:
+ case 1070:
+ case 1071:
+ case 1082:
+ case 1083:
+ case 1084:
+ case 1085:
+ case 1086:
+ case 1087:
+ case 1088:
+ case 1089:
+ case 1090:
+ case 1091:
+ case 1092:
+ case 1093:
+ case 1254:
+ case 1255:
+ case 1256:
+ case 1261:
+ case 1262:
+ case 1263:
+ case 1264:
+ case 1265:
+ case 1266:
+ case 1267:
+ case 1268:
+ case 1269:
+ case 1287:
+ case 1293:
+ case 1296:
+ case 1297:
+ case 1298:
+ case 1309:
+ case 1310:
+ case 1311:
+ case 1322:
+ case 1323:
+ case 1324:
+ case 1329:
+ case 1330:
+ case 1341:
+ case 1342:
+ case 1343:
+ case 1348:
+ case 1361:
+ case 1378:
+ case 1379:
+ case 1380:
+ case 1381:
+ case 1384:
+ case 1385:
+ case 1386:
+ case 1387:
+ case 1388:
+ case 1389:
+ case 1392:
+ case 1393:
+ case 1394:
+ case 1395:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 3:
+ case 17:
+ case 18:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 26:
+ case 29:
+ case 30:
+ case 32:
+ case 33:
+ case 34:
+ case 36:
+ case 37:
+ case 38:
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 45:
+ case 46:
+ case 47:
+ case 49:
+ case 50:
+ case 51:
+ case 54:
+ case 55:
+ case 56:
+ case 57:
+ case 59:
+ case 61:
+ case 62:
+ case 63:
+ case 64:
+ case 66:
+ case 67:
+ case 68:
+ case 70:
+ case 71:
+ case 72:
+ case 73:
+ case 74:
+ case 75:
+ case 76:
+ case 77:
+ case 78:
+ case 79:
+ case 81:
+ case 82:
+ case 85:
+ case 86:
+ case 88:
+ case 90:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 99:
+ case 100:
+ case 101:
+ case 102:
+ case 104:
+ case 107:
+ case 110:
+ case 111:
+ case 112:
+ case 113:
+ case 115:
+ case 116:
+ case 117:
+ case 118:
+ case 119:
+ case 120:
+ case 121:
+ case 122:
+ case 123:
+ case 125:
+ case 126:
+ case 128:
+ case 130:
+ case 131:
+ case 132:
+ case 135:
+ case 136:
+ case 137:
+ case 138:
+ case 139:
+ case 141:
+ case 143:
+ case 145:
+ case 147:
+ case 148:
+ case 149:
+ case 151:
+ case 152:
+ case 154:
+ case 155:
+ case 156:
+ case 158:
+ case 160:
+ case 161:
+ case 164:
+ case 165:
+ case 167:
+ case 168:
+ case 169:
+ case 170:
+ case 171:
+ case 173:
+ case 175:
+ case 176:
+ case 182:
+ case 197:
+ case 201:
+ case 266:
+ case 267:
+ case 269:
+ case 270:
+ case 271:
+ case 273:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 334:
+ case 335:
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 343:
+ case 344:
+ case 345:
+ case 350:
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 312:
+ case 313:
+ case 314:
+ case 315:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 346:
+ case 347:
+ case 348:
+ case 349:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 360:
+ case 361:
+ case 362:
+ case 363:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 260:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 286:
+ case 287:
+ case 288:
+ case 289:
+ case 290:
+ case 291:
+ case 292:
+ case 293:
+ case 294:
+ case 295:
+ case 296:
+ case 297:
+ case 298:
+ case 299:
+ case 300:
+ case 301:
+ case 304:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 332:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 541:
+ case 542:
+ case 550:
+ case 551:
+ case 557:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 655:
+ case 656:
+ case 693:
+ case 694:
+ case 695:
+ case 696:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 582:
+ case 583:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 852:
+ case 853:
+ case 854:
+ case 856:
+ case 858:
+ case 860:
+ case 890:
+ case 891:
+ case 956:
+ case 957:
+ case 958:
+ case 959:
+ case 960:
+ case 961:
+ case 967:
+ case 968:
+ case 969:
+ case 970:
+ case 986:
+ case 987:
+ case 1006:
+ case 1007:
+ case 1010:
+ case 1011:
+ case 1019:
+ case 1020:
+ case 1021:
+ case 1022:
+ case 1029:
+ case 1030:
+ case 1072:
+ case 1073:
+ case 1074:
+ case 1075:
+ case 1080:
+ case 1081:
+ case 1257:
+ case 1258:
+ case 1259:
+ case 1260:
+ case 1270:
+ case 1271:
+ case 1272:
+ case 1273:
+ case 1274:
+ case 1275:
+ case 1299:
+ case 1300:
+ case 1301:
+ case 1302:
+ case 1303:
+ case 1304:
+ case 1312:
+ case 1313:
+ case 1314:
+ case 1315:
+ case 1316:
+ case 1317:
+ case 1325:
+ case 1326:
+ case 1327:
+ case 1328:
+ case 1331:
+ case 1332:
+ case 1333:
+ case 1334:
+ case 1335:
+ case 1336:
+ case 1344:
+ case 1345:
+ case 1349:
+ case 1350:
+ case 1374:
+ case 1375:
+ case 1376:
+ case 1377:
+ case 1382:
+ case 1383:
+ case 1390:
+ case 1391:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 162:
+ case 177:
+ case 193:
+ case 196:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1076:
+ case 1077:
+ case 1078:
+ case 1079:
+ {
+ WARN(ZS_BAD_TIMESTAMP_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 318:
+ case 319:
+ case 324:
+ case 325:
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 895:
+ case 896:
+ case 897:
+ case 898:
+ {
+ s->long_string = false;
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 354:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 355:
+ case 357:
+ case 364:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 636:
+ case 637:
+ case 638:
+ case 639:
+ case 640:
+ case 641:
+ case 647:
+ case 648:
+ case 649:
+ case 650:
+ case 651:
+ case 652:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 844:
+ case 845:
+ case 899:
+ case 900:
+ case 1346:
+ case 1347:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 995:
+ case 996:
+ case 997:
+ case 1000:
+ case 1001:
+ case 1003:
+ {
+ WARN(ZS_BAD_APL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1012:
+ case 1013:
+ case 1014:
+ case 1015:
+ case 1023:
+ case 1024:
+ case 1025:
+ case 1026:
+ case 1276:
+ case 1277:
+ case 1294:
+ case 1295:
+ case 1305:
+ case 1306:
+ case 1307:
+ case 1308:
+ case 1318:
+ case 1319:
+ case 1320:
+ case 1321:
+ case 1337:
+ case 1338:
+ case 1339:
+ case 1340:
+ case 1351:
+ case 1352:
+ case 1353:
+ case 1354:
+ case 1356:
+ case 1357:
+ case 1358:
+ case 1359:
+ case 1362:
+ case 1363:
+ case 1365:
+ case 1366:
+ case 1368:
+ case 1369:
+ case 1371:
+ case 1372:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1278:
+ case 1279:
+ case 1280:
+ case 1281:
+ case 1282:
+ case 1283:
+ case 1284:
+ case 1285:
+ case 1286:
+ case 1288:
+ case 1289:
+ case 1290:
+ case 1291:
+ case 1292:
+ {
+ WARN(ZS_BAD_BASE32HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1031:
+ case 1032:
+ case 1035:
+ case 1044:
+ case 1045:
+ case 1046:
+ case 1051:
+ case 1052:
+ case 1053:
+ case 1058:
+ case 1059:
+ case 1060:
+ case 1063:
+ case 1065:
+ case 1066:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1037:
+ case 1038:
+ case 1039:
+ case 1040:
+ case 1041:
+ case 1042:
+ case 1043:
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1068:
+ case 1069:
+ case 1094:
+ case 1095:
+ case 1096:
+ case 1097:
+ case 1098:
+ case 1099:
+ case 1100:
+ case 1101:
+ case 1102:
+ case 1103:
+ case 1104:
+ case 1105:
+ case 1106:
+ case 1107:
+ case 1108:
+ case 1109:
+ case 1110:
+ case 1111:
+ case 1112:
+ case 1113:
+ case 1114:
+ case 1115:
+ case 1116:
+ case 1117:
+ case 1118:
+ case 1119:
+ case 1120:
+ case 1121:
+ case 1122:
+ case 1123:
+ case 1124:
+ case 1125:
+ case 1126:
+ case 1127:
+ case 1128:
+ case 1129:
+ case 1130:
+ case 1131:
+ case 1132:
+ case 1133:
+ case 1134:
+ case 1135:
+ case 1136:
+ case 1137:
+ case 1138:
+ case 1139:
+ case 1140:
+ case 1141:
+ case 1142:
+ case 1143:
+ case 1144:
+ case 1145:
+ case 1146:
+ case 1147:
+ case 1148:
+ case 1149:
+ case 1150:
+ case 1151:
+ case 1152:
+ case 1153:
+ case 1154:
+ case 1155:
+ case 1156:
+ case 1157:
+ case 1158:
+ case 1159:
+ case 1160:
+ case 1161:
+ case 1162:
+ case 1163:
+ case 1164:
+ case 1165:
+ case 1166:
+ case 1167:
+ case 1168:
+ case 1169:
+ case 1170:
+ case 1171:
+ case 1172:
+ case 1173:
+ case 1174:
+ case 1175:
+ case 1176:
+ case 1177:
+ case 1178:
+ case 1179:
+ case 1180:
+ case 1181:
+ case 1182:
+ case 1183:
+ case 1184:
+ case 1185:
+ case 1186:
+ case 1187:
+ case 1188:
+ case 1189:
+ case 1190:
+ case 1191:
+ case 1192:
+ case 1193:
+ case 1194:
+ case 1195:
+ case 1196:
+ case 1197:
+ case 1198:
+ case 1199:
+ case 1200:
+ case 1201:
+ case 1202:
+ case 1203:
+ case 1204:
+ case 1205:
+ case 1206:
+ case 1207:
+ case 1208:
+ case 1209:
+ case 1210:
+ case 1211:
+ case 1212:
+ case 1213:
+ case 1214:
+ case 1215:
+ case 1216:
+ case 1217:
+ case 1218:
+ case 1219:
+ case 1220:
+ case 1221:
+ case 1222:
+ case 1223:
+ case 1224:
+ case 1225:
+ case 1226:
+ case 1227:
+ case 1228:
+ case 1229:
+ case 1230:
+ case 1231:
+ case 1232:
+ case 1233:
+ case 1234:
+ case 1235:
+ case 1236:
+ case 1237:
+ case 1238:
+ case 1239:
+ case 1240:
+ case 1241:
+ case 1242:
+ case 1245:
+ case 1246:
+ case 1247:
+ case 1248:
+ case 1249:
+ case 1250:
+ case 1251:
+ case 1252:
+ case 1253:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 901:
+ case 902:
+ case 903:
+ case 904:
+ case 905:
+ case 906:
+ case 907:
+ case 908:
+ case 909:
+ case 910:
+ case 911:
+ case 912:
+ case 913:
+ case 914:
+ case 915:
+ case 916:
+ case 917:
+ case 918:
+ case 919:
+ case 920:
+ case 921:
+ case 922:
+ case 923:
+ case 924:
+ case 925:
+ case 926:
+ case 927:
+ case 928:
+ case 929:
+ case 930:
+ case 931:
+ case 932:
+ case 933:
+ case 934:
+ case 935:
+ case 936:
+ case 937:
+ case 938:
+ case 939:
+ case 940:
+ case 941:
+ case 942:
+ case 943:
+ case 944:
+ case 945:
+ case 946:
+ case 947:
+ case 948:
+ case 949:
+ case 950:
+ case 951:
+ case 952:
+ case 953:
+ case 954:
+ case 955:
+ {
+ WARN(ZS_BAD_LOC_DATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 6:
+ case 11:
+ case 12:
+ case 13:
+ case 20:
+ case 21:
+ case 205:
+ case 208:
+ case 227:
+ case 262:
+ case 264:
+ case 265:
+ case 275:
+ case 276:
+ case 278:
+ case 279:
+ case 280:
+ case 281:
+ case 284:
+ case 285:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 16:
+ case 241:
+ case 303:
+ case 305:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 4:
+ case 19:
+ case 27:
+ case 28:
+ case 31:
+ case 35:
+ case 39:
+ case 44:
+ case 48:
+ case 52:
+ case 53:
+ case 58:
+ case 60:
+ case 65:
+ case 69:
+ case 80:
+ case 83:
+ case 84:
+ case 87:
+ case 89:
+ case 91:
+ case 92:
+ case 97:
+ case 98:
+ case 103:
+ case 105:
+ case 106:
+ case 108:
+ case 109:
+ case 114:
+ case 124:
+ case 127:
+ case 129:
+ case 133:
+ case 134:
+ case 140:
+ case 142:
+ case 144:
+ case 146:
+ case 150:
+ case 153:
+ case 157:
+ case 159:
+ case 166:
+ case 172:
+ case 268:
+ case 272:
+ case 274:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 238:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 202:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1033:
+ case 1034:
+ case 1047:
+ case 1048:
+ case 1054:
+ case 1055:
+ case 1061:
+ case 1062:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1243:
+ case 1244:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1:
+ case 174:
+ case 206:
+ case 240:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 163:
+ case 203:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 337:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 855:
+ case 857:
+ case 859:
+ case 861:
+ case 862:
+ case 863:
+ case 864:
+ case 866:
+ case 867:
+ case 868:
+ case 870:
+ case 871:
+ case 872:
+ case 874:
+ case 875:
+ case 876:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 2:
+ case 178:
+ case 179:
+ case 180:
+ case 181:
+ case 198:
+ case 199:
+ case 200:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 320:
+ case 321:
+ case 322:
+ case 323:
+ case 327:
+ case 328:
+ case 329:
+ case 330:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 998:
+ case 999:
+ case 1004:
+ case 1005:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_APL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1049:
+ case 1056:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 543:
+ case 544:
+ case 545:
+ case 546:
+ case 552:
+ case 553:
+ case 554:
+ case 555:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1355:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1036:
+ case 1064:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1364:
+ case 1367:
+ case 1370:
+ case 1373:
+ {
+ WARN(ZS_BAD_CHAR_DASH);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1360:
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 228:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 204:
+ case 211:
+ case 212:
+ case 213:
+ case 214:
+ case 215:
+ case 216:
+ case 217:
+ case 218:
+ case 219:
+ case 220:
+ case 221:
+ case 222:
+ case 223:
+ case 224:
+ case 225:
+ case 226:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 195:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 242:
+ case 244:
+ case 245:
+ case 246:
+ case 247:
+ case 248:
+ case 249:
+ case 250:
+ case 251:
+ case 252:
+ case 253:
+ case 254:
+ case 255:
+ case 256:
+ case 257:
+ case 258:
+ case 259:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 14:
+ case 210:
+ case 243:
+ case 261:
+ case 283:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 333:
+ case 336:
+ case 338:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ ERR(ZS_BAD_TTL);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 1050:
+ case 1057:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 15:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ case 239:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ {
+ WARN(ZS_BAD_REST);
+ p--; { if ( p == pe )
+ goto _test_eof307;
+goto st307;}
+ }
+ break;
+ }
+ }
+
+ _out: {}
+ }
+
+
+ // Check if the scanner state machine is in an uncovered state.
+ bool extra_error = false;
+ if (cs == 0) {
+ ERR(ZS_UNCOVERED_STATE);
+ extra_error = true;
+ // Check for an unclosed multiline record.
+ } else if (s->input.eof && s->multiline) {
+ ERR(ZS_UNCLOSED_MULTILINE);
+ extra_error = true;
+ s->line_counter--;
+ }
+
+ // Treat the extra error.
+ if (extra_error) {
+ s->error.counter++;
+ s->state = ZS_STATE_ERROR;
+
+ // Copy the error context just for the part of the current line.
+ s->buffer_length = 0;
+ while (p < pe && *p != '\n' && s->buffer_length < 50) {
+ s->buffer[s->buffer_length++] = *p++;
+ }
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the error callback.
+ if (s->process.automatic && s->process.error != NULL) {
+ s->process.error(s);
+ }
+
+ return;
+ }
+
+ // Storing scanner states.
+ s->cs = cs;
+ s->top = top;
+ memcpy(s->stack, stack, sizeof(stack));
+
+ // Store the current parser position.
+ s->input.current = p;
+
+ // Storing r_data pointer.
+ s->r_data_tail = rdata_tail - s->r_data;
+
+ if (*wrap == WRAP_DETECTED) {
+ if (set_input_string(s, "\\", 1, true) != 0) {
+ return;
+ }
+
+ *wrap = WRAP_PROCESS;
+ parse(s, wrap);
+ } else {
+ *wrap = WRAP_NONE;
+ }
+}
+
+__attribute__((visibility("default")))
+int zs_parse_record(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ // Check if parsing is possible.
+ switch (s->state) {
+ case ZS_STATE_NONE:
+ case ZS_STATE_DATA:
+ case ZS_STATE_INCLUDE:
+ break;
+ case ZS_STATE_ERROR:
+ if (s->error.fatal) {
+ return -1;
+ }
+ break;
+ default:
+ // Return if stop or end of file.
+ return 0;
+ }
+
+ // Check for the end of the input.
+ if (s->input.current != s->input.end) {
+ // Try to parse another item.
+ s->state = ZS_STATE_NONE;
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Finish if nothing was parsed.
+ if (s->state == ZS_STATE_NONE) {
+ // Parse the final block.
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ if (s->state == ZS_STATE_NONE) {
+ s->state = ZS_STATE_EOF;
+ }
+ }
+ } else {
+ s->state = ZS_STATE_EOF;
+ }
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_parse_all(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.automatic = true;
+
+ // Parse input block.
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Parse trailing newline-char block if it makes sense.
+ if (s->state != ZS_STATE_STOP && !s->error.fatal) {
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ }
+
+ // Check if any errors have occurred.
+ if (s->error.counter > 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/libzscanner/scanner.c.t0 b/src/libzscanner/scanner.c.t0
new file mode 100644
index 0000000..0909496
--- /dev/null
+++ b/src/libzscanner/scanner.c.t0
@@ -0,0 +1,9692 @@
+
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libzscanner/scanner.h"
+#include "libzscanner/functions.h"
+#include "libknot/descriptor.h"
+
+/*! \brief Maximal length of rdata item. */
+#define MAX_ITEM_LENGTH 255
+#define MAX_ITEM_LENGTH2 65535
+
+/*! \brief Latitude value for equator (2^31). */
+#define LOC_LAT_ZERO (uint32_t)2147483648
+/*! \brief Longitude value for meridian (2^31). */
+#define LOC_LONG_ZERO (uint32_t)2147483648
+/*! \brief Zero level altitude value. */
+#define LOC_ALT_ZERO (uint32_t)10000000
+
+/*! \brief Shorthand for setting warning data. */
+#define WARN(err_code) { s->error.code = err_code; }
+/*! \brief Shorthand for setting error data. */
+#define ERR(err_code) { WARN(err_code); s->error.fatal = true; }
+/*! \brief Shorthand for error reset. */
+#define NOERR { WARN(ZS_OK); s->error.fatal = false; }
+
+/*!
+ * \brief Writes record type number to r_data.
+ *
+ * \param type Type number.
+ * \param rdata_tail Position where to write type number to.
+ */
+static inline void type_num(const uint16_t type, uint8_t **rdata_tail)
+{
+ *((uint16_t *)*rdata_tail) = htons(type);
+ *rdata_tail += 2;
+}
+
+/*!
+ * \brief Sets bit to bitmap window.
+ *
+ * \param type Type number.
+ * \param s Scanner context.
+ */
+static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) {
+ uint8_t win = type / 256;
+ uint8_t bit_pos = type % 256;
+ uint8_t byte_pos = bit_pos / 8;
+
+ ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8);
+
+ if ((s->windows[win]).length < byte_pos + 1) {
+ (s->windows[win]).length = byte_pos + 1;
+ }
+
+ if (s->last_window < win) {
+ s->last_window = win;
+ }
+}
+
+// Include scanner file (in Ragel).
+
+static const short _zone_scanner_actions[] = {
+ 0, 1, 0, 1, 1, 1, 2, 1,
+ 3, 1, 4, 1, 5, 1, 7, 1,
+ 8, 1, 10, 1, 13, 1, 14, 1,
+ 15, 1, 17, 1, 18, 1, 23, 1,
+ 24, 1, 26, 1, 27, 1, 29, 1,
+ 31, 1, 33, 1, 35, 1, 36, 1,
+ 45, 1, 46, 1, 47, 1, 49, 1,
+ 51, 1, 52, 1, 53, 1, 54, 1,
+ 58, 1, 59, 1, 61, 1, 63, 1,
+ 65, 1, 68, 1, 69, 1, 73, 1,
+ 74, 1, 77, 1, 78, 1, 81, 1,
+ 84, 1, 86, 1, 87, 1, 88, 1,
+ 89, 1, 90, 1, 91, 1, 92, 1,
+ 93, 1, 95, 1, 97, 1, 99, 1,
+ 103, 1, 104, 1, 108, 1, 109, 1,
+ 113, 1, 114, 1, 115, 1, 116, 1,
+ 117, 1, 118, 1, 119, 1, 120, 1,
+ 121, 1, 122, 1, 123, 1, 124, 1,
+ 125, 1, 126, 1, 128, 1, 129, 1,
+ 130, 1, 131, 1, 145, 1, 146, 1,
+ 148, 1, 149, 1, 150, 1, 151, 1,
+ 159, 1, 160, 1, 161, 1, 162, 1,
+ 163, 1, 164, 1, 165, 1, 166, 1,
+ 167, 1, 168, 1, 169, 1, 170, 1,
+ 171, 1, 172, 1, 173, 1, 174, 1,
+ 175, 1, 176, 1, 177, 1, 178, 1,
+ 179, 1, 180, 1, 181, 1, 182, 1,
+ 183, 1, 184, 1, 185, 1, 186, 1,
+ 187, 1, 188, 1, 189, 1, 190, 1,
+ 191, 1, 192, 1, 193, 1, 194, 1,
+ 195, 1, 196, 1, 197, 1, 198, 1,
+ 199, 1, 200, 1, 201, 1, 202, 1,
+ 203, 1, 204, 1, 205, 1, 206, 1,
+ 207, 1, 208, 1, 209, 1, 210, 1,
+ 211, 1, 212, 1, 213, 1, 214, 1,
+ 215, 1, 216, 1, 217, 1, 218, 1,
+ 219, 1, 220, 1, 221, 1, 222, 1,
+ 223, 1, 224, 1, 225, 1, 226, 1,
+ 227, 1, 228, 1, 229, 1, 230, 1,
+ 231, 1, 232, 1, 233, 1, 234, 1,
+ 235, 1, 236, 1, 237, 1, 238, 1,
+ 239, 1, 240, 1, 241, 1, 242, 1,
+ 243, 1, 244, 1, 245, 1, 246, 1,
+ 247, 1, 248, 1, 249, 1, 250, 1,
+ 251, 1, 252, 1, 253, 1, 254, 1,
+ 255, 1, 256, 1, 258, 1, 260, 1,
+ 261, 1, 262, 1, 263, 1, 270, 1,
+ 271, 1, 276, 1, 278, 1, 283, 1,
+ 286, 1, 288, 1, 289, 1, 290, 1,
+ 291, 1, 296, 1, 297, 1, 299, 1,
+ 300, 1, 301, 1, 302, 1, 304, 1,
+ 305, 1, 306, 1, 307, 1, 309, 2,
+ 0, 49, 2, 1, 0, 2, 1, 305,
+ 2, 1, 358, 2, 2, 5, 2, 2,
+ 7, 2, 3, 5, 2, 3, 7, 2,
+ 4, 0, 2, 4, 5, 2, 4, 7,
+ 2, 4, 305, 2, 5, 2, 2, 5,
+ 3, 2, 5, 4, 2, 5, 7, 2,
+ 6, 1, 2, 7, 1, 2, 7, 2,
+ 2, 7, 3, 2, 7, 4, 2, 7,
+ 5, 2, 7, 32, 2, 8, 73, 2,
+ 8, 77, 2, 9, 10, 2, 11, 1,
+ 2, 12, 1, 2, 13, 14, 2, 16,
+ 17, 2, 18, 14, 2, 18, 15, 2,
+ 19, 24, 2, 19, 77, 2, 19, 84,
+ 2, 20, 0, 2, 20, 7, 2, 22,
+ 0, 2, 23, 13, 2, 26, 69, 2,
+ 26, 108, 2, 26, 119, 2, 27, 0,
+ 2, 27, 1, 2, 27, 2, 2, 27,
+ 3, 2, 27, 4, 2, 27, 259, 2,
+ 28, 69, 2, 28, 113, 2, 29, 291,
+ 2, 29, 295, 2, 30, 25, 2, 31,
+ 1, 2, 31, 2, 2, 31, 3, 2,
+ 31, 4, 2, 31, 7, 2, 31, 93,
+ 2, 32, 7, 2, 33, 8, 2, 33,
+ 304, 2, 33, 309, 2, 34, 25, 2,
+ 35, 0, 2, 35, 1, 2, 35, 2,
+ 2, 35, 3, 2, 35, 4, 2, 35,
+ 259, 2, 35, 298, 2, 36, 41, 2,
+ 37, 36, 2, 38, 73, 2, 38, 276,
+ 2, 38, 288, 2, 38, 289, 2, 38,
+ 304, 2, 38, 309, 2, 42, 264, 2,
+ 42, 265, 2, 42, 266, 2, 42, 267,
+ 2, 42, 268, 2, 42, 269, 2, 45,
+ 0, 2, 45, 1, 2, 45, 2, 2,
+ 45, 3, 2, 45, 4, 2, 46, 0,
+ 2, 46, 1, 2, 46, 2, 2, 46,
+ 3, 2, 46, 4, 2, 46, 259, 2,
+ 46, 291, 2, 47, 0, 2, 47, 1,
+ 2, 47, 2, 2, 47, 3, 2, 47,
+ 4, 2, 48, 303, 2, 49, 1, 2,
+ 49, 2, 2, 49, 3, 2, 49, 4,
+ 2, 56, 7, 2, 56, 47, 2, 56,
+ 93, 2, 57, 58, 2, 59, 1, 2,
+ 59, 2, 2, 59, 3, 2, 59, 4,
+ 2, 60, 304, 2, 62, 63, 2, 64,
+ 65, 2, 66, 68, 2, 68, 0, 2,
+ 68, 61, 2, 71, 0, 2, 71, 304,
+ 2, 75, 23, 2, 79, 69, 2, 80,
+ 7, 2, 81, 8, 2, 82, 23, 2,
+ 84, 8, 2, 90, 91, 2, 92, 1,
+ 2, 92, 2, 2, 92, 3, 2, 92,
+ 4, 2, 93, 1, 2, 93, 2, 2,
+ 93, 3, 2, 93, 4, 2, 93, 7,
+ 2, 94, 95, 2, 96, 288, 2, 96,
+ 304, 2, 97, 98, 2, 99, 100, 2,
+ 101, 102, 2, 101, 103, 2, 101, 104,
+ 2, 105, 106, 2, 107, 304, 2, 110,
+ 304, 2, 111, 0, 2, 127, 304, 2,
+ 129, 0, 2, 130, 0, 2, 131, 0,
+ 2, 132, 0, 2, 133, 0, 2, 134,
+ 0, 2, 135, 0, 2, 136, 0, 2,
+ 137, 0, 2, 138, 0, 2, 139, 0,
+ 2, 140, 0, 2, 141, 0, 2, 142,
+ 0, 2, 143, 0, 2, 144, 0, 2,
+ 146, 0, 2, 147, 0, 2, 148, 0,
+ 2, 149, 0, 2, 150, 0, 2, 151,
+ 0, 2, 152, 0, 2, 153, 0, 2,
+ 154, 0, 2, 155, 0, 2, 156, 304,
+ 2, 157, 304, 2, 158, 304, 2, 159,
+ 1, 2, 159, 2, 2, 159, 3, 2,
+ 159, 4, 2, 160, 1, 2, 160, 2,
+ 2, 160, 3, 2, 160, 4, 2, 161,
+ 1, 2, 161, 2, 2, 161, 3, 2,
+ 161, 4, 2, 162, 1, 2, 162, 2,
+ 2, 162, 3, 2, 162, 4, 2, 163,
+ 1, 2, 163, 2, 2, 163, 3, 2,
+ 163, 4, 2, 164, 1, 2, 164, 2,
+ 2, 164, 3, 2, 164, 4, 2, 165,
+ 1, 2, 165, 2, 2, 165, 3, 2,
+ 165, 4, 2, 166, 1, 2, 166, 2,
+ 2, 166, 3, 2, 166, 4, 2, 167,
+ 1, 2, 167, 2, 2, 167, 3, 2,
+ 167, 4, 2, 168, 1, 2, 168, 2,
+ 2, 168, 3, 2, 168, 4, 2, 169,
+ 1, 2, 169, 2, 2, 169, 3, 2,
+ 169, 4, 2, 170, 1, 2, 170, 2,
+ 2, 170, 3, 2, 170, 4, 2, 171,
+ 1, 2, 171, 2, 2, 171, 3, 2,
+ 171, 4, 2, 172, 1, 2, 172, 2,
+ 2, 172, 3, 2, 172, 4, 2, 173,
+ 1, 2, 173, 2, 2, 173, 3, 2,
+ 173, 4, 2, 174, 1, 2, 174, 2,
+ 2, 174, 3, 2, 174, 4, 2, 175,
+ 1, 2, 175, 2, 2, 175, 3, 2,
+ 175, 4, 2, 176, 1, 2, 176, 2,
+ 2, 176, 3, 2, 176, 4, 2, 177,
+ 1, 2, 177, 2, 2, 177, 3, 2,
+ 177, 4, 2, 178, 1, 2, 178, 2,
+ 2, 178, 3, 2, 178, 4, 2, 179,
+ 1, 2, 179, 2, 2, 179, 3, 2,
+ 179, 4, 2, 180, 1, 2, 180, 2,
+ 2, 180, 3, 2, 180, 4, 2, 181,
+ 1, 2, 181, 2, 2, 181, 3, 2,
+ 181, 4, 2, 182, 1, 2, 182, 2,
+ 2, 182, 3, 2, 182, 4, 2, 183,
+ 1, 2, 183, 2, 2, 183, 3, 2,
+ 183, 4, 2, 184, 1, 2, 184, 2,
+ 2, 184, 3, 2, 184, 4, 2, 185,
+ 1, 2, 185, 2, 2, 185, 3, 2,
+ 185, 4, 2, 186, 1, 2, 186, 2,
+ 2, 186, 3, 2, 186, 4, 2, 187,
+ 1, 2, 187, 2, 2, 187, 3, 2,
+ 187, 4, 2, 188, 1, 2, 188, 2,
+ 2, 188, 3, 2, 188, 4, 2, 189,
+ 1, 2, 189, 2, 2, 189, 3, 2,
+ 189, 4, 2, 190, 1, 2, 190, 2,
+ 2, 190, 3, 2, 190, 4, 2, 191,
+ 1, 2, 191, 2, 2, 191, 3, 2,
+ 191, 4, 2, 192, 1, 2, 192, 2,
+ 2, 192, 3, 2, 192, 4, 2, 193,
+ 1, 2, 193, 2, 2, 193, 3, 2,
+ 193, 4, 2, 194, 1, 2, 194, 2,
+ 2, 194, 3, 2, 194, 4, 2, 195,
+ 1, 2, 195, 2, 2, 195, 3, 2,
+ 195, 4, 2, 196, 1, 2, 196, 2,
+ 2, 196, 3, 2, 196, 4, 2, 197,
+ 1, 2, 197, 2, 2, 197, 3, 2,
+ 197, 4, 2, 198, 1, 2, 198, 2,
+ 2, 198, 3, 2, 198, 4, 2, 199,
+ 1, 2, 199, 2, 2, 199, 3, 2,
+ 199, 4, 2, 200, 1, 2, 200, 2,
+ 2, 200, 3, 2, 200, 4, 2, 201,
+ 1, 2, 201, 2, 2, 201, 3, 2,
+ 201, 4, 2, 202, 1, 2, 202, 2,
+ 2, 202, 3, 2, 202, 4, 2, 203,
+ 1, 2, 203, 2, 2, 203, 3, 2,
+ 203, 4, 2, 204, 1, 2, 204, 2,
+ 2, 204, 3, 2, 204, 4, 2, 205,
+ 1, 2, 205, 2, 2, 205, 3, 2,
+ 205, 4, 2, 206, 1, 2, 206, 2,
+ 2, 206, 3, 2, 206, 4, 2, 207,
+ 1, 2, 207, 2, 2, 207, 3, 2,
+ 207, 4, 2, 208, 1, 2, 208, 2,
+ 2, 208, 3, 2, 208, 4, 2, 209,
+ 1, 2, 209, 2, 2, 209, 3, 2,
+ 209, 4, 2, 210, 1, 2, 210, 2,
+ 2, 210, 3, 2, 210, 4, 2, 211,
+ 1, 2, 211, 2, 2, 211, 3, 2,
+ 211, 4, 2, 212, 1, 2, 212, 2,
+ 2, 212, 3, 2, 212, 4, 2, 213,
+ 1, 2, 213, 2, 2, 213, 3, 2,
+ 213, 4, 2, 214, 1, 2, 214, 2,
+ 2, 214, 3, 2, 214, 4, 2, 215,
+ 1, 2, 215, 2, 2, 215, 3, 2,
+ 215, 4, 2, 216, 1, 2, 216, 2,
+ 2, 216, 3, 2, 216, 4, 2, 217,
+ 1, 2, 217, 2, 2, 217, 3, 2,
+ 217, 4, 2, 218, 1, 2, 218, 2,
+ 2, 218, 3, 2, 218, 4, 2, 219,
+ 1, 2, 219, 2, 2, 219, 3, 2,
+ 219, 4, 2, 220, 1, 2, 220, 2,
+ 2, 220, 3, 2, 220, 4, 2, 221,
+ 1, 2, 221, 2, 2, 221, 3, 2,
+ 221, 4, 2, 222, 1, 2, 222, 2,
+ 2, 222, 3, 2, 222, 4, 2, 223,
+ 1, 2, 223, 2, 2, 223, 3, 2,
+ 223, 4, 2, 224, 1, 2, 224, 2,
+ 2, 224, 3, 2, 224, 4, 2, 225,
+ 1, 2, 225, 2, 2, 225, 3, 2,
+ 225, 4, 2, 226, 1, 2, 226, 2,
+ 2, 226, 3, 2, 226, 4, 2, 227,
+ 1, 2, 227, 2, 2, 227, 3, 2,
+ 227, 4, 2, 228, 1, 2, 228, 2,
+ 2, 228, 3, 2, 228, 4, 2, 229,
+ 1, 2, 229, 2, 2, 229, 3, 2,
+ 229, 4, 2, 230, 1, 2, 230, 2,
+ 2, 230, 3, 2, 230, 4, 2, 231,
+ 1, 2, 231, 2, 2, 231, 3, 2,
+ 231, 4, 2, 232, 1, 2, 232, 2,
+ 2, 232, 3, 2, 232, 4, 2, 233,
+ 1, 2, 233, 2, 2, 233, 3, 2,
+ 233, 4, 2, 234, 1, 2, 234, 2,
+ 2, 234, 3, 2, 234, 4, 2, 235,
+ 1, 2, 235, 2, 2, 235, 3, 2,
+ 235, 4, 2, 236, 1, 2, 236, 2,
+ 2, 236, 3, 2, 236, 4, 2, 237,
+ 1, 2, 237, 2, 2, 237, 3, 2,
+ 237, 4, 2, 238, 1, 2, 238, 2,
+ 2, 238, 3, 2, 238, 4, 2, 239,
+ 1, 2, 239, 2, 2, 239, 3, 2,
+ 239, 4, 2, 240, 1, 2, 240, 2,
+ 2, 240, 3, 2, 240, 4, 2, 241,
+ 1, 2, 241, 2, 2, 241, 3, 2,
+ 241, 4, 2, 242, 1, 2, 242, 2,
+ 2, 242, 3, 2, 242, 4, 2, 243,
+ 1, 2, 243, 2, 2, 243, 3, 2,
+ 243, 4, 2, 244, 1, 2, 244, 2,
+ 2, 244, 3, 2, 244, 4, 2, 245,
+ 1, 2, 245, 2, 2, 245, 3, 2,
+ 245, 4, 2, 246, 1, 2, 246, 2,
+ 2, 246, 3, 2, 246, 4, 2, 247,
+ 1, 2, 247, 2, 2, 247, 3, 2,
+ 247, 4, 2, 248, 1, 2, 248, 2,
+ 2, 248, 3, 2, 248, 4, 2, 249,
+ 1, 2, 249, 2, 2, 249, 3, 2,
+ 249, 4, 2, 250, 1, 2, 250, 2,
+ 2, 250, 3, 2, 250, 4, 2, 251,
+ 1, 2, 251, 2, 2, 251, 3, 2,
+ 251, 4, 2, 252, 1, 2, 252, 2,
+ 2, 252, 3, 2, 252, 4, 2, 253,
+ 1, 2, 253, 2, 2, 253, 3, 2,
+ 253, 4, 2, 254, 1, 2, 254, 2,
+ 2, 254, 3, 2, 254, 4, 2, 255,
+ 1, 2, 255, 2, 2, 255, 3, 2,
+ 255, 4, 2, 256, 1, 2, 256, 2,
+ 2, 256, 3, 2, 256, 4, 2, 257,
+ 0, 2, 260, 1, 2, 260, 2, 2,
+ 260, 3, 2, 260, 4, 2, 261, 1,
+ 2, 261, 2, 2, 261, 3, 2, 261,
+ 4, 2, 262, 1, 2, 262, 2, 2,
+ 262, 3, 2, 262, 4, 2, 263, 1,
+ 2, 263, 2, 2, 263, 3, 2, 263,
+ 4, 2, 270, 1, 2, 270, 2, 2,
+ 270, 3, 2, 270, 4, 2, 271, 1,
+ 2, 271, 2, 2, 271, 3, 2, 271,
+ 4, 2, 274, 0, 2, 275, 304, 2,
+ 277, 108, 2, 282, 108, 2, 286, 1,
+ 2, 286, 2, 2, 286, 3, 2, 286,
+ 4, 2, 287, 0, 2, 291, 1, 2,
+ 291, 2, 2, 291, 3, 2, 291, 4,
+ 2, 304, 8, 2, 305, 1, 2, 305,
+ 4, 2, 308, 305, 2, 309, 8, 2,
+ 309, 304, 2, 310, 303, 2, 311, 303,
+ 2, 312, 303, 2, 313, 303, 2, 314,
+ 303, 2, 315, 303, 2, 316, 303, 2,
+ 317, 303, 2, 318, 303, 2, 319, 303,
+ 2, 320, 303, 2, 321, 303, 2, 322,
+ 303, 2, 323, 303, 2, 324, 303, 2,
+ 325, 303, 2, 326, 303, 2, 327, 303,
+ 2, 328, 303, 2, 329, 303, 2, 330,
+ 303, 2, 331, 303, 2, 332, 303, 2,
+ 333, 303, 2, 334, 303, 2, 335, 303,
+ 2, 336, 303, 2, 337, 303, 2, 338,
+ 303, 2, 339, 303, 2, 340, 303, 2,
+ 341, 303, 2, 342, 303, 2, 343, 303,
+ 2, 344, 303, 2, 345, 303, 2, 346,
+ 303, 2, 347, 303, 2, 348, 303, 2,
+ 349, 303, 2, 350, 303, 2, 351, 303,
+ 2, 352, 303, 2, 353, 303, 2, 354,
+ 303, 2, 355, 303, 2, 356, 303, 2,
+ 357, 303, 2, 358, 1, 3, 0, 49,
+ 1, 3, 0, 49, 2, 3, 0, 49,
+ 3, 3, 0, 49, 4, 3, 1, 7,
+ 358, 3, 1, 32, 7, 3, 1, 71,
+ 0, 3, 1, 111, 0, 3, 1, 257,
+ 0, 3, 1, 274, 0, 3, 1, 287,
+ 0, 3, 1, 305, 358, 3, 2, 7,
+ 32, 3, 2, 32, 7, 3, 3, 7,
+ 32, 3, 3, 32, 7, 3, 4, 7,
+ 32, 3, 4, 32, 7, 3, 4, 71,
+ 0, 3, 4, 111, 0, 3, 4, 257,
+ 0, 3, 4, 274, 0, 3, 4, 287,
+ 0, 3, 4, 305, 7, 3, 5, 7,
+ 2, 3, 5, 7, 3, 3, 5, 7,
+ 4, 3, 6, 1, 358, 3, 6, 358,
+ 1, 3, 7, 2, 5, 3, 7, 2,
+ 32, 3, 7, 3, 5, 3, 7, 3,
+ 32, 3, 7, 4, 5, 3, 7, 4,
+ 32, 3, 7, 358, 1, 3, 9, 11,
+ 1, 3, 15, 21, 0, 3, 20, 7,
+ 2, 3, 20, 7, 3, 3, 20, 7,
+ 4, 3, 20, 83, 7, 3, 23, 13,
+ 14, 3, 27, 71, 0, 3, 27, 293,
+ 29, 3, 28, 94, 95, 3, 29, 291,
+ 1, 3, 29, 291, 2, 3, 29, 291,
+ 3, 3, 29, 291, 4, 3, 29, 295,
+ 291, 3, 31, 1, 93, 3, 31, 2,
+ 7, 3, 31, 2, 93, 3, 31, 3,
+ 7, 3, 31, 3, 93, 3, 31, 4,
+ 7, 3, 31, 4, 93, 3, 31, 93,
+ 7, 3, 31, 308, 305, 3, 31, 310,
+ 303, 3, 32, 1, 7, 3, 32, 2,
+ 7, 3, 32, 3, 7, 3, 32, 4,
+ 7, 3, 33, 89, 8, 3, 33, 304,
+ 8, 3, 33, 309, 8, 3, 33, 309,
+ 304, 3, 35, 1, 0, 3, 35, 4,
+ 0, 3, 37, 36, 305, 3, 38, 156,
+ 304, 3, 38, 158, 304, 3, 38, 309,
+ 8, 3, 38, 309, 304, 3, 40, 42,
+ 264, 3, 40, 42, 265, 3, 40, 42,
+ 266, 3, 40, 42, 267, 3, 40, 42,
+ 268, 3, 40, 42, 269, 3, 42, 264,
+ 1, 3, 42, 264, 2, 3, 42, 264,
+ 3, 3, 42, 264, 4, 3, 42, 265,
+ 1, 3, 42, 265, 2, 3, 42, 265,
+ 3, 3, 42, 265, 4, 3, 42, 266,
+ 1, 3, 42, 266, 2, 3, 42, 266,
+ 3, 3, 42, 266, 4, 3, 42, 267,
+ 1, 3, 42, 267, 2, 3, 42, 267,
+ 3, 3, 42, 267, 4, 3, 42, 268,
+ 1, 3, 42, 268, 2, 3, 42, 268,
+ 3, 3, 42, 268, 4, 3, 42, 269,
+ 1, 3, 42, 269, 2, 3, 42, 269,
+ 3, 3, 42, 269, 4, 3, 46, 145,
+ 291, 3, 46, 291, 1, 3, 46, 291,
+ 2, 3, 46, 291, 3, 3, 46, 291,
+ 4, 3, 48, 303, 1, 3, 48, 303,
+ 2, 3, 48, 303, 3, 3, 48, 303,
+ 4, 3, 48, 303, 305, 3, 50, 38,
+ 73, 3, 50, 38, 304, 3, 50, 38,
+ 309, 3, 55, 37, 36, 3, 56, 7,
+ 2, 3, 56, 7, 3, 3, 56, 7,
+ 4, 3, 56, 47, 0, 3, 56, 47,
+ 1, 3, 56, 47, 2, 3, 56, 47,
+ 3, 3, 56, 47, 4, 3, 56, 93,
+ 1, 3, 56, 93, 2, 3, 56, 93,
+ 3, 3, 56, 93, 4, 3, 66, 68,
+ 0, 3, 66, 68, 61, 3, 67, 62,
+ 63, 3, 68, 61, 0, 3, 70, 26,
+ 69, 3, 72, 0, 1, 3, 75, 23,
+ 13, 3, 76, 0, 1, 3, 80, 2,
+ 7, 3, 80, 3, 7, 3, 80, 4,
+ 7, 3, 80, 7, 4, 3, 82, 23,
+ 13, 3, 85, 0, 1, 3, 88, 7,
+ 1, 3, 88, 7, 4, 3, 88, 30,
+ 25, 3, 88, 32, 7, 3, 90, 30,
+ 25, 3, 90, 91, 305, 3, 93, 2,
+ 7, 3, 93, 3, 7, 3, 93, 4,
+ 7, 3, 96, 107, 304, 3, 96, 156,
+ 304, 3, 97, 98, 0, 3, 97, 98,
+ 1, 3, 97, 98, 2, 3, 97, 98,
+ 3, 3, 97, 98, 4, 3, 97, 98,
+ 29, 3, 99, 100, 0, 3, 99, 100,
+ 1, 3, 99, 100, 2, 3, 99, 100,
+ 3, 3, 99, 100, 4, 3, 99, 100,
+ 29, 3, 105, 106, 0, 3, 105, 106,
+ 1, 3, 105, 106, 2, 3, 105, 106,
+ 3, 3, 105, 106, 4, 3, 110, 112,
+ 276, 3, 110, 285, 304, 3, 147, 145,
+ 291, 3, 156, 157, 304, 3, 207, 257,
+ 0, 3, 208, 257, 0, 3, 209, 257,
+ 0, 3, 210, 257, 0, 3, 211, 257,
+ 0, 3, 212, 257, 0, 3, 213, 257,
+ 0, 3, 214, 257, 0, 3, 215, 257,
+ 0, 3, 216, 257, 0, 3, 217, 257,
+ 0, 3, 218, 257, 0, 3, 219, 257,
+ 0, 3, 220, 257, 0, 3, 221, 257,
+ 0, 3, 222, 257, 0, 3, 223, 257,
+ 0, 3, 224, 257, 0, 3, 225, 257,
+ 0, 3, 226, 257, 0, 3, 227, 257,
+ 0, 3, 228, 257, 0, 3, 229, 257,
+ 0, 3, 230, 257, 0, 3, 231, 257,
+ 0, 3, 232, 257, 0, 3, 233, 257,
+ 0, 3, 234, 257, 0, 3, 235, 257,
+ 0, 3, 236, 257, 0, 3, 237, 257,
+ 0, 3, 238, 257, 0, 3, 239, 257,
+ 0, 3, 240, 257, 0, 3, 241, 257,
+ 0, 3, 242, 257, 0, 3, 243, 257,
+ 0, 3, 244, 257, 0, 3, 245, 257,
+ 0, 3, 246, 257, 0, 3, 247, 257,
+ 0, 3, 248, 257, 0, 3, 249, 257,
+ 0, 3, 250, 257, 0, 3, 251, 257,
+ 0, 3, 252, 257, 0, 3, 253, 257,
+ 0, 3, 254, 257, 0, 3, 255, 257,
+ 0, 3, 256, 257, 0, 3, 273, 37,
+ 36, 3, 278, 279, 0, 3, 278, 280,
+ 0, 3, 281, 110, 304, 3, 283, 284,
+ 0, 3, 285, 110, 304, 3, 286, 287,
+ 0, 3, 291, 287, 0, 3, 294, 28,
+ 297, 3, 304, 8, 89, 3, 305, 7,
+ 1, 3, 305, 7, 4, 3, 305, 30,
+ 25, 3, 305, 358, 1, 3, 307, 30,
+ 25, 3, 309, 304, 8, 3, 310, 303,
+ 1, 3, 310, 303, 2, 3, 310, 303,
+ 3, 3, 310, 303, 4, 3, 310, 303,
+ 7, 3, 310, 303, 305, 3, 311, 303,
+ 1, 3, 311, 303, 2, 3, 311, 303,
+ 3, 3, 311, 303, 4, 3, 311, 303,
+ 305, 3, 312, 303, 1, 3, 312, 303,
+ 2, 3, 312, 303, 3, 3, 312, 303,
+ 4, 3, 312, 303, 305, 3, 313, 303,
+ 1, 3, 313, 303, 2, 3, 313, 303,
+ 3, 3, 313, 303, 4, 3, 313, 303,
+ 305, 3, 314, 303, 1, 3, 314, 303,
+ 2, 3, 314, 303, 3, 3, 314, 303,
+ 4, 3, 314, 303, 305, 3, 315, 303,
+ 1, 3, 315, 303, 2, 3, 315, 303,
+ 3, 3, 315, 303, 4, 3, 315, 303,
+ 305, 3, 316, 303, 1, 3, 316, 303,
+ 2, 3, 316, 303, 3, 3, 316, 303,
+ 4, 3, 316, 303, 305, 3, 317, 303,
+ 1, 3, 317, 303, 2, 3, 317, 303,
+ 3, 3, 317, 303, 4, 3, 317, 303,
+ 305, 3, 318, 303, 1, 3, 318, 303,
+ 2, 3, 318, 303, 3, 3, 318, 303,
+ 4, 3, 318, 303, 305, 3, 319, 303,
+ 1, 3, 319, 303, 2, 3, 319, 303,
+ 3, 3, 319, 303, 4, 3, 319, 303,
+ 305, 3, 320, 303, 1, 3, 320, 303,
+ 2, 3, 320, 303, 3, 3, 320, 303,
+ 4, 3, 320, 303, 305, 3, 321, 303,
+ 1, 3, 321, 303, 2, 3, 321, 303,
+ 3, 3, 321, 303, 4, 3, 321, 303,
+ 305, 3, 322, 303, 1, 3, 322, 303,
+ 2, 3, 322, 303, 3, 3, 322, 303,
+ 4, 3, 322, 303, 305, 3, 323, 303,
+ 1, 3, 323, 303, 2, 3, 323, 303,
+ 3, 3, 323, 303, 4, 3, 323, 303,
+ 305, 3, 324, 303, 1, 3, 324, 303,
+ 2, 3, 324, 303, 3, 3, 324, 303,
+ 4, 3, 324, 303, 305, 3, 325, 303,
+ 1, 3, 325, 303, 2, 3, 325, 303,
+ 3, 3, 325, 303, 4, 3, 325, 303,
+ 305, 3, 326, 303, 1, 3, 326, 303,
+ 2, 3, 326, 303, 3, 3, 326, 303,
+ 4, 3, 326, 303, 305, 3, 327, 303,
+ 1, 3, 327, 303, 2, 3, 327, 303,
+ 3, 3, 327, 303, 4, 3, 327, 303,
+ 305, 3, 328, 303, 1, 3, 328, 303,
+ 2, 3, 328, 303, 3, 3, 328, 303,
+ 4, 3, 328, 303, 305, 3, 329, 303,
+ 1, 3, 329, 303, 2, 3, 329, 303,
+ 3, 3, 329, 303, 4, 3, 329, 303,
+ 305, 3, 330, 303, 1, 3, 330, 303,
+ 2, 3, 330, 303, 3, 3, 330, 303,
+ 4, 3, 330, 303, 305, 3, 331, 303,
+ 1, 3, 331, 303, 2, 3, 331, 303,
+ 3, 3, 331, 303, 4, 3, 331, 303,
+ 305, 3, 332, 303, 1, 3, 332, 303,
+ 2, 3, 332, 303, 3, 3, 332, 303,
+ 4, 3, 332, 303, 305, 3, 333, 303,
+ 1, 3, 333, 303, 2, 3, 333, 303,
+ 3, 3, 333, 303, 4, 3, 333, 303,
+ 305, 3, 334, 303, 1, 3, 334, 303,
+ 2, 3, 334, 303, 3, 3, 334, 303,
+ 4, 3, 334, 303, 305, 3, 335, 303,
+ 1, 3, 335, 303, 2, 3, 335, 303,
+ 3, 3, 335, 303, 4, 3, 335, 303,
+ 305, 3, 336, 303, 1, 3, 336, 303,
+ 2, 3, 336, 303, 3, 3, 336, 303,
+ 4, 3, 336, 303, 305, 3, 337, 303,
+ 1, 3, 337, 303, 2, 3, 337, 303,
+ 3, 3, 337, 303, 4, 3, 337, 303,
+ 305, 3, 338, 303, 1, 3, 338, 303,
+ 2, 3, 338, 303, 3, 3, 338, 303,
+ 4, 3, 338, 303, 305, 3, 339, 303,
+ 1, 3, 339, 303, 2, 3, 339, 303,
+ 3, 3, 339, 303, 4, 3, 339, 303,
+ 305, 3, 340, 303, 1, 3, 340, 303,
+ 2, 3, 340, 303, 3, 3, 340, 303,
+ 4, 3, 340, 303, 305, 3, 341, 303,
+ 1, 3, 341, 303, 2, 3, 341, 303,
+ 3, 3, 341, 303, 4, 3, 341, 303,
+ 305, 3, 342, 303, 1, 3, 342, 303,
+ 2, 3, 342, 303, 3, 3, 342, 303,
+ 4, 3, 342, 303, 305, 3, 343, 303,
+ 1, 3, 343, 303, 2, 3, 343, 303,
+ 3, 3, 343, 303, 4, 3, 343, 303,
+ 305, 3, 344, 303, 1, 3, 344, 303,
+ 2, 3, 344, 303, 3, 3, 344, 303,
+ 4, 3, 344, 303, 305, 3, 345, 303,
+ 1, 3, 345, 303, 2, 3, 345, 303,
+ 3, 3, 345, 303, 4, 3, 345, 303,
+ 305, 3, 346, 303, 1, 3, 346, 303,
+ 2, 3, 346, 303, 3, 3, 346, 303,
+ 4, 3, 346, 303, 305, 3, 347, 303,
+ 1, 3, 347, 303, 2, 3, 347, 303,
+ 3, 3, 347, 303, 4, 3, 347, 303,
+ 305, 3, 348, 303, 1, 3, 348, 303,
+ 2, 3, 348, 303, 3, 3, 348, 303,
+ 4, 3, 348, 303, 305, 3, 349, 303,
+ 1, 3, 349, 303, 2, 3, 349, 303,
+ 3, 3, 349, 303, 4, 3, 349, 303,
+ 305, 3, 350, 303, 1, 3, 350, 303,
+ 2, 3, 350, 303, 3, 3, 350, 303,
+ 4, 3, 350, 303, 305, 3, 351, 303,
+ 1, 3, 351, 303, 2, 3, 351, 303,
+ 3, 3, 351, 303, 4, 3, 351, 303,
+ 305, 3, 352, 303, 1, 3, 352, 303,
+ 2, 3, 352, 303, 3, 3, 352, 303,
+ 4, 3, 352, 303, 305, 3, 353, 303,
+ 1, 3, 353, 303, 2, 3, 353, 303,
+ 3, 3, 353, 303, 4, 3, 353, 303,
+ 305, 3, 354, 303, 1, 3, 354, 303,
+ 2, 3, 354, 303, 3, 3, 354, 303,
+ 4, 3, 354, 303, 305, 3, 355, 303,
+ 1, 3, 355, 303, 2, 3, 355, 303,
+ 3, 3, 355, 303, 4, 3, 355, 303,
+ 305, 3, 356, 303, 1, 3, 356, 303,
+ 2, 3, 356, 303, 3, 3, 356, 303,
+ 4, 3, 356, 303, 305, 3, 357, 303,
+ 1, 3, 357, 303, 2, 3, 357, 303,
+ 3, 3, 357, 303, 4, 3, 357, 303,
+ 305, 3, 358, 1, 6, 4, 1, 7,
+ 358, 32, 4, 1, 305, 32, 7, 4,
+ 4, 305, 7, 32, 4, 4, 305, 32,
+ 7, 4, 6, 1, 7, 358, 4, 6,
+ 1, 85, 0, 4, 6, 72, 0, 1,
+ 4, 6, 76, 0, 1, 4, 6, 85,
+ 0, 1, 4, 7, 6, 358, 1, 4,
+ 7, 72, 0, 1, 4, 7, 358, 1,
+ 6, 4, 7, 358, 1, 32, 4, 18,
+ 15, 21, 0, 4, 20, 83, 7, 2,
+ 4, 20, 83, 7, 3, 4, 20, 83,
+ 7, 4, 4, 27, 1, 71, 0, 4,
+ 27, 4, 71, 0, 4, 27, 293, 29,
+ 291, 4, 29, 291, 287, 0, 4, 29,
+ 295, 291, 1, 4, 29, 295, 291, 2,
+ 4, 29, 295, 291, 3, 4, 29, 295,
+ 291, 4, 4, 31, 1, 7, 358, 4,
+ 31, 1, 308, 305, 4, 31, 1, 310,
+ 303, 4, 31, 2, 93, 7, 4, 31,
+ 2, 308, 305, 4, 31, 2, 310, 303,
+ 4, 31, 3, 93, 7, 4, 31, 3,
+ 308, 305, 4, 31, 3, 310, 303, 4,
+ 31, 4, 93, 7, 4, 31, 4, 308,
+ 305, 4, 31, 4, 310, 303, 4, 31,
+ 310, 303, 7, 4, 33, 38, 309, 8,
+ 4, 33, 50, 38, 309, 4, 33, 309,
+ 304, 8, 4, 37, 36, 30, 25, 4,
+ 38, 309, 304, 8, 4, 40, 37, 36,
+ 41, 4, 40, 42, 264, 1, 4, 40,
+ 42, 264, 2, 4, 40, 42, 264, 3,
+ 4, 40, 42, 264, 4, 4, 40, 42,
+ 265, 1, 4, 40, 42, 265, 2, 4,
+ 40, 42, 265, 3, 4, 40, 42, 265,
+ 4, 4, 40, 42, 266, 1, 4, 40,
+ 42, 266, 2, 4, 40, 42, 266, 3,
+ 4, 40, 42, 266, 4, 4, 40, 42,
+ 267, 1, 4, 40, 42, 267, 2, 4,
+ 40, 42, 267, 3, 4, 40, 42, 267,
+ 4, 4, 40, 42, 268, 1, 4, 40,
+ 42, 268, 2, 4, 40, 42, 268, 3,
+ 4, 40, 42, 268, 4, 4, 40, 42,
+ 269, 1, 4, 40, 42, 269, 2, 4,
+ 40, 42, 269, 3, 4, 40, 42, 269,
+ 4, 4, 42, 266, 274, 0, 4, 42,
+ 267, 274, 0, 4, 42, 268, 274, 0,
+ 4, 42, 269, 274, 0, 4, 43, 39,
+ 37, 36, 4, 44, 39, 37, 36, 4,
+ 46, 145, 291, 1, 4, 46, 145, 291,
+ 2, 4, 46, 145, 291, 3, 4, 46,
+ 145, 291, 4, 4, 46, 291, 287, 0,
+ 4, 48, 303, 1, 305, 4, 48, 303,
+ 4, 305, 4, 50, 38, 8, 73, 4,
+ 56, 55, 37, 36, 4, 66, 68, 61,
+ 0, 4, 75, 23, 13, 14, 4, 82,
+ 23, 13, 14, 4, 88, 32, 1, 7,
+ 4, 88, 32, 2, 7, 4, 88, 32,
+ 3, 7, 4, 88, 32, 4, 7, 4,
+ 90, 91, 30, 25, 4, 93, 1, 7,
+ 358, 4, 96, 156, 157, 304, 4, 97,
+ 98, 1, 0, 4, 97, 98, 4, 0,
+ 4, 97, 98, 29, 291, 4, 99, 100,
+ 1, 0, 4, 99, 100, 4, 0, 4,
+ 99, 100, 29, 291, 4, 105, 106, 1,
+ 0, 4, 105, 106, 4, 0, 4, 147,
+ 145, 291, 1, 4, 147, 145, 291, 2,
+ 4, 147, 145, 291, 3, 4, 147, 145,
+ 291, 4, 4, 147, 296, 37, 36, 4,
+ 207, 1, 257, 0, 4, 207, 4, 257,
+ 0, 4, 208, 1, 257, 0, 4, 208,
+ 4, 257, 0, 4, 209, 1, 257, 0,
+ 4, 209, 4, 257, 0, 4, 210, 1,
+ 257, 0, 4, 210, 4, 257, 0, 4,
+ 211, 1, 257, 0, 4, 211, 4, 257,
+ 0, 4, 212, 1, 257, 0, 4, 212,
+ 4, 257, 0, 4, 213, 1, 257, 0,
+ 4, 213, 4, 257, 0, 4, 214, 1,
+ 257, 0, 4, 214, 4, 257, 0, 4,
+ 215, 1, 257, 0, 4, 215, 4, 257,
+ 0, 4, 216, 1, 257, 0, 4, 216,
+ 4, 257, 0, 4, 217, 1, 257, 0,
+ 4, 217, 4, 257, 0, 4, 218, 1,
+ 257, 0, 4, 218, 4, 257, 0, 4,
+ 219, 1, 257, 0, 4, 219, 4, 257,
+ 0, 4, 220, 1, 257, 0, 4, 220,
+ 4, 257, 0, 4, 221, 1, 257, 0,
+ 4, 221, 4, 257, 0, 4, 222, 1,
+ 257, 0, 4, 222, 4, 257, 0, 4,
+ 223, 1, 257, 0, 4, 223, 4, 257,
+ 0, 4, 224, 1, 257, 0, 4, 224,
+ 4, 257, 0, 4, 225, 1, 257, 0,
+ 4, 225, 4, 257, 0, 4, 226, 1,
+ 257, 0, 4, 226, 4, 257, 0, 4,
+ 227, 1, 257, 0, 4, 227, 4, 257,
+ 0, 4, 228, 1, 257, 0, 4, 228,
+ 4, 257, 0, 4, 229, 1, 257, 0,
+ 4, 229, 4, 257, 0, 4, 230, 1,
+ 257, 0, 4, 230, 4, 257, 0, 4,
+ 231, 1, 257, 0, 4, 231, 4, 257,
+ 0, 4, 232, 1, 257, 0, 4, 232,
+ 4, 257, 0, 4, 233, 1, 257, 0,
+ 4, 233, 4, 257, 0, 4, 234, 1,
+ 257, 0, 4, 234, 4, 257, 0, 4,
+ 235, 1, 257, 0, 4, 235, 4, 257,
+ 0, 4, 236, 1, 257, 0, 4, 236,
+ 4, 257, 0, 4, 237, 1, 257, 0,
+ 4, 237, 4, 257, 0, 4, 238, 1,
+ 257, 0, 4, 238, 4, 257, 0, 4,
+ 239, 1, 257, 0, 4, 239, 4, 257,
+ 0, 4, 240, 1, 257, 0, 4, 240,
+ 4, 257, 0, 4, 241, 1, 257, 0,
+ 4, 241, 4, 257, 0, 4, 242, 1,
+ 257, 0, 4, 242, 4, 257, 0, 4,
+ 243, 1, 257, 0, 4, 243, 4, 257,
+ 0, 4, 244, 1, 257, 0, 4, 244,
+ 4, 257, 0, 4, 245, 1, 257, 0,
+ 4, 245, 4, 257, 0, 4, 246, 1,
+ 257, 0, 4, 246, 4, 257, 0, 4,
+ 247, 1, 257, 0, 4, 247, 4, 257,
+ 0, 4, 248, 1, 257, 0, 4, 248,
+ 4, 257, 0, 4, 249, 1, 257, 0,
+ 4, 249, 4, 257, 0, 4, 250, 1,
+ 257, 0, 4, 250, 4, 257, 0, 4,
+ 251, 1, 257, 0, 4, 251, 4, 257,
+ 0, 4, 252, 1, 257, 0, 4, 252,
+ 4, 257, 0, 4, 253, 1, 257, 0,
+ 4, 253, 4, 257, 0, 4, 254, 1,
+ 257, 0, 4, 254, 4, 257, 0, 4,
+ 255, 1, 257, 0, 4, 255, 4, 257,
+ 0, 4, 256, 1, 257, 0, 4, 256,
+ 4, 257, 0, 4, 286, 1, 287, 0,
+ 4, 286, 4, 287, 0, 4, 291, 1,
+ 287, 0, 4, 291, 4, 287, 0, 4,
+ 292, 28, 26, 69, 4, 305, 7, 358,
+ 1, 4, 310, 303, 1, 305, 4, 310,
+ 303, 2, 7, 4, 310, 303, 3, 7,
+ 4, 310, 303, 4, 7, 4, 310, 303,
+ 4, 305, 4, 311, 303, 1, 305, 4,
+ 311, 303, 4, 305, 4, 312, 303, 1,
+ 305, 4, 312, 303, 4, 305, 4, 313,
+ 303, 1, 305, 4, 313, 303, 4, 305,
+ 4, 314, 303, 1, 305, 4, 314, 303,
+ 4, 305, 4, 315, 303, 1, 305, 4,
+ 315, 303, 4, 305, 4, 316, 303, 1,
+ 305, 4, 316, 303, 4, 305, 4, 317,
+ 303, 1, 305, 4, 317, 303, 4, 305,
+ 4, 318, 303, 1, 305, 4, 318, 303,
+ 4, 305, 4, 319, 303, 1, 305, 4,
+ 319, 303, 4, 305, 4, 320, 303, 1,
+ 305, 4, 320, 303, 4, 305, 4, 321,
+ 303, 1, 305, 4, 321, 303, 4, 305,
+ 4, 322, 303, 1, 305, 4, 322, 303,
+ 4, 305, 4, 323, 303, 1, 305, 4,
+ 323, 303, 4, 305, 4, 324, 303, 1,
+ 305, 4, 324, 303, 4, 305, 4, 325,
+ 303, 1, 305, 4, 325, 303, 4, 305,
+ 4, 326, 303, 1, 305, 4, 326, 303,
+ 4, 305, 4, 327, 303, 1, 305, 4,
+ 327, 303, 4, 305, 4, 328, 303, 1,
+ 305, 4, 328, 303, 4, 305, 4, 329,
+ 303, 1, 305, 4, 329, 303, 4, 305,
+ 4, 330, 303, 1, 305, 4, 330, 303,
+ 4, 305, 4, 331, 303, 1, 305, 4,
+ 331, 303, 4, 305, 4, 332, 303, 1,
+ 305, 4, 332, 303, 4, 305, 4, 333,
+ 303, 1, 305, 4, 333, 303, 4, 305,
+ 4, 334, 303, 1, 305, 4, 334, 303,
+ 4, 305, 4, 335, 303, 1, 305, 4,
+ 335, 303, 4, 305, 4, 336, 303, 1,
+ 305, 4, 336, 303, 4, 305, 4, 337,
+ 303, 1, 305, 4, 337, 303, 4, 305,
+ 4, 338, 303, 1, 305, 4, 338, 303,
+ 4, 305, 4, 339, 303, 1, 305, 4,
+ 339, 303, 4, 305, 4, 340, 303, 1,
+ 305, 4, 340, 303, 4, 305, 4, 341,
+ 303, 1, 305, 4, 341, 303, 4, 305,
+ 4, 342, 303, 1, 305, 4, 342, 303,
+ 4, 305, 4, 343, 303, 1, 305, 4,
+ 343, 303, 4, 305, 4, 344, 303, 1,
+ 305, 4, 344, 303, 4, 305, 4, 345,
+ 303, 1, 305, 4, 345, 303, 4, 305,
+ 4, 346, 303, 1, 305, 4, 346, 303,
+ 4, 305, 4, 347, 303, 1, 305, 4,
+ 347, 303, 4, 305, 4, 348, 303, 1,
+ 305, 4, 348, 303, 4, 305, 4, 349,
+ 303, 1, 305, 4, 349, 303, 4, 305,
+ 4, 350, 303, 1, 305, 4, 350, 303,
+ 4, 305, 4, 351, 303, 1, 305, 4,
+ 351, 303, 4, 305, 4, 352, 303, 1,
+ 305, 4, 352, 303, 4, 305, 4, 353,
+ 303, 1, 305, 4, 353, 303, 4, 305,
+ 4, 354, 303, 1, 305, 4, 354, 303,
+ 4, 305, 4, 355, 303, 1, 305, 4,
+ 355, 303, 4, 305, 4, 356, 303, 1,
+ 305, 4, 356, 303, 4, 305, 4, 357,
+ 303, 1, 305, 4, 357, 303, 4, 305,
+ 5, 20, 7, 76, 0, 1, 5, 27,
+ 293, 29, 291, 1, 5, 27, 293, 29,
+ 291, 2, 5, 27, 293, 29, 291, 3,
+ 5, 27, 293, 29, 291, 4, 5, 29,
+ 291, 1, 287, 0, 5, 29, 291, 4,
+ 287, 0, 5, 29, 295, 291, 287, 0,
+ 5, 31, 1, 93, 7, 358, 5, 31,
+ 1, 310, 303, 305, 5, 31, 2, 310,
+ 303, 7, 5, 31, 3, 310, 303, 7,
+ 5, 31, 4, 310, 303, 7, 5, 31,
+ 4, 310, 303, 305, 5, 33, 38, 309,
+ 304, 8, 5, 37, 36, 305, 30, 25,
+ 5, 40, 42, 266, 274, 0, 5, 40,
+ 42, 267, 274, 0, 5, 40, 42, 268,
+ 274, 0, 5, 40, 42, 269, 274, 0,
+ 5, 42, 266, 1, 274, 0, 5, 42,
+ 266, 4, 274, 0, 5, 42, 267, 1,
+ 274, 0, 5, 42, 267, 4, 274, 0,
+ 5, 42, 268, 1, 274, 0, 5, 42,
+ 268, 4, 274, 0, 5, 42, 269, 1,
+ 274, 0, 5, 42, 269, 4, 274, 0,
+ 5, 46, 145, 291, 287, 0, 5, 46,
+ 291, 1, 287, 0, 5, 46, 291, 4,
+ 287, 0, 5, 50, 38, 309, 304, 8,
+ 5, 56, 7, 72, 0, 1, 5, 80,
+ 7, 85, 0, 1, 5, 90, 91, 305,
+ 30, 25, 5, 97, 98, 29, 291, 1,
+ 5, 97, 98, 29, 291, 2, 5, 97,
+ 98, 29, 291, 3, 5, 97, 98, 29,
+ 291, 4, 5, 99, 100, 29, 291, 1,
+ 5, 99, 100, 29, 291, 2, 5, 99,
+ 100, 29, 291, 3, 5, 99, 100, 29,
+ 291, 4, 5, 147, 145, 291, 287, 0,
+ 5, 272, 43, 39, 37, 36, 5, 310,
+ 303, 1, 7, 358, 5, 310, 303, 4,
+ 305, 7, 5, 310, 303, 305, 7, 4,
+ 6, 20, 83, 7, 85, 0, 1, 6,
+ 27, 293, 29, 291, 287, 0, 6, 29,
+ 295, 291, 1, 287, 0, 6, 29, 295,
+ 291, 4, 287, 0, 6, 31, 1, 310,
+ 303, 7, 358, 6, 31, 4, 310, 303,
+ 305, 7, 6, 33, 50, 38, 309, 304,
+ 8, 6, 40, 42, 266, 1, 274, 0,
+ 6, 40, 42, 266, 4, 274, 0, 6,
+ 40, 42, 267, 1, 274, 0, 6, 40,
+ 42, 267, 4, 274, 0, 6, 40, 42,
+ 268, 1, 274, 0, 6, 40, 42, 268,
+ 4, 274, 0, 6, 40, 42, 269, 1,
+ 274, 0, 6, 40, 42, 269, 4, 274,
+ 0, 6, 46, 145, 291, 1, 287, 0,
+ 6, 46, 145, 291, 4, 287, 0, 6,
+ 97, 98, 29, 291, 287, 0, 6, 99,
+ 100, 29, 291, 287, 0, 6, 147, 145,
+ 291, 1, 287, 0, 6, 147, 145, 291,
+ 4, 287, 0, 6, 310, 303, 1, 305,
+ 7, 358, 6, 310, 303, 305, 7, 358,
+ 1, 7, 27, 293, 29, 291, 1, 287,
+ 0, 7, 27, 293, 29, 291, 4, 287,
+ 0, 7, 31, 1, 310, 303, 305, 7,
+ 358, 7, 97, 98, 29, 291, 1, 287,
+ 0, 7, 97, 98, 29, 291, 4, 287,
+ 0, 7, 99, 100, 29, 291, 1, 287,
+ 0, 7, 99, 100, 29, 291, 4, 287,
+ 0
+};
+
+static const short _zone_scanner_cond_offsets[] = {
+ 0, 0, 2, 4, 6, 8, 10, 12,
+ 14, 14, 14, 17, 19, 21, 24, 26,
+ 28, 30, 30, 30, 32, 37, 42, 42,
+ 42, 42, 42, 42, 44, 46, 46, 46,
+ 48, 48, 48, 48, 50, 50, 50, 50,
+ 52, 52, 52, 52, 52, 54, 54, 54,
+ 54, 56, 56, 56, 56, 58, 60, 60,
+ 60, 60, 60, 62, 62, 64, 64, 64,
+ 64, 64, 66, 66, 66, 66, 68, 68,
+ 70, 72, 72, 72, 72, 72, 72, 72,
+ 72, 74, 74, 74, 76, 78, 78, 78,
+ 80, 80, 82, 82, 84, 86, 86, 86,
+ 86, 86, 88, 90, 90, 90, 90, 90,
+ 92, 92, 94, 96, 96, 98, 100, 100,
+ 100, 100, 100, 102, 102, 102, 102, 102,
+ 102, 102, 102, 102, 102, 104, 104, 104,
+ 106, 106, 108, 108, 108, 108, 110, 112,
+ 112, 112, 112, 112, 112, 114, 114, 116,
+ 116, 118, 118, 120, 120, 120, 120, 122,
+ 122, 122, 124, 124, 124, 124, 126, 126,
+ 128, 128, 128, 128, 130, 130, 130, 132,
+ 132, 132, 132, 132, 132, 134, 137, 139,
+ 139, 141, 143, 145, 147, 147, 149, 152,
+ 152, 152, 152, 152, 152, 152, 152, 152,
+ 152, 154, 156, 159, 161, 163, 166, 168,
+ 168, 170, 173, 175, 177, 179, 181, 183,
+ 186, 188, 190, 192, 194, 196, 198, 200,
+ 202, 204, 206, 208, 210, 212, 214, 216,
+ 218, 220, 222, 224, 227, 229, 229, 229,
+ 229, 229, 229, 229, 229, 229, 229, 231,
+ 233, 235, 238, 240, 242, 244, 246, 248,
+ 250, 252, 254, 256, 258, 260, 262, 264,
+ 266, 268, 270, 272, 274, 276, 278, 283,
+ 288, 293, 298, 298, 298, 300, 300, 300,
+ 300, 302, 302, 304, 307, 309, 311, 316,
+ 321, 326, 331, 334, 336, 341, 346, 348,
+ 350, 352, 354, 356, 358, 360, 362, 364,
+ 366, 368, 370, 372, 374, 376, 378, 381,
+ 384, 386, 389, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 391, 391, 391,
+ 391, 391, 391, 391, 391, 392, 393, 393,
+ 393, 393, 393, 394, 396, 398, 400, 402,
+ 402, 404, 404, 406, 409, 411, 413, 413,
+ 415, 417, 417, 417, 417, 417, 417, 419,
+ 422, 424, 426, 428, 430, 430, 432, 434,
+ 434, 434, 434, 434, 434, 436, 439, 441,
+ 444, 447, 447, 447, 447, 447, 449, 452,
+ 452, 454, 456, 458, 458, 458, 460, 463,
+ 463, 463, 465, 465, 465, 465, 465, 465,
+ 467, 469, 469, 469, 471, 471, 471, 471,
+ 473, 473, 473, 473, 475, 475, 475, 475,
+ 475, 477, 477, 477, 477, 479, 479, 479,
+ 479, 481, 483, 483, 483, 483, 483, 485,
+ 485, 487, 487, 487, 487, 487, 489, 489,
+ 489, 489, 491, 491, 491, 491, 491, 491,
+ 491, 491, 493, 493, 493, 495, 497, 497,
+ 497, 499, 499, 501, 501, 503, 505, 505,
+ 505, 505, 505, 507, 509, 509, 509, 509,
+ 509, 511, 511, 513, 515, 515, 517, 519,
+ 519, 519, 519, 519, 521, 521, 521, 521,
+ 521, 521, 521, 521, 521, 521, 523, 523,
+ 523, 525, 525, 527, 527, 527, 527, 529,
+ 531, 531, 531, 531, 531, 531, 533, 533,
+ 535, 535, 537, 537, 539, 539, 539, 539,
+ 541, 541, 541, 543, 543, 543, 543, 545,
+ 545, 547, 547, 547, 547, 549, 549, 549,
+ 551, 551, 551, 551, 551, 551, 553, 553,
+ 553, 553, 555, 555, 557, 559, 561, 563,
+ 565, 565, 567, 570, 573, 576, 578, 580,
+ 582, 584, 584, 586, 589, 592, 594, 597,
+ 597, 597, 597, 597, 597, 597, 597, 597,
+ 597, 597, 597, 597, 597, 597, 597, 597,
+ 597, 597, 597, 597, 597, 597, 597, 597,
+ 597, 597, 597, 597, 597, 597, 597, 597,
+ 597, 597, 597, 597, 597, 597, 597, 597,
+ 597, 597, 597, 597, 599, 601, 601, 601,
+ 601, 601, 601, 603, 606, 606, 606, 606,
+ 606, 606, 606, 606, 606, 606, 608, 608,
+ 608, 608, 608, 610, 610, 610, 610, 610,
+ 610, 610, 610, 610, 610, 610, 610, 610,
+ 610, 612, 612, 612, 612, 612, 612, 612,
+ 612, 612, 612, 612, 614, 614, 614, 614,
+ 614, 616, 616, 618, 618, 618, 618, 618,
+ 618, 618, 618, 618, 618, 618, 618, 618,
+ 618, 620, 620, 620, 620, 620, 620, 620,
+ 620, 620, 620, 620, 620, 620, 620, 620,
+ 620, 622, 622, 622, 622, 622, 622, 622,
+ 622, 624, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 626, 626, 626, 626, 626, 626,
+ 626, 626, 628, 630, 632, 634, 636, 638,
+ 640, 642, 644, 646, 648, 650, 650, 650,
+ 650, 650, 653, 655, 655, 657, 660, 662,
+ 662, 664, 667, 669, 669, 671, 674, 677,
+ 680, 680, 682, 684, 684, 687, 687, 689,
+ 691, 691, 694, 694, 696, 698, 698, 701,
+ 701, 703, 705, 708, 708, 708, 708, 710,
+ 712, 714, 716, 718, 720, 722, 724, 726,
+ 728, 730, 732, 734, 736, 738, 740, 740,
+ 742, 744, 746, 748, 750, 752, 754, 756,
+ 759, 761, 763, 766, 768, 770, 772, 775,
+ 777, 779, 781, 784, 786, 788, 790, 793,
+ 795, 798, 800, 802, 805, 808, 811, 813,
+ 816, 818, 820, 823, 826, 826, 828, 830,
+ 832, 834, 836, 838, 838, 841, 844, 847,
+ 847, 849, 851, 853, 855, 857, 859, 861,
+ 863, 865, 867, 867, 870, 873, 876, 879,
+ 882, 882, 884, 886, 888, 890, 892, 894,
+ 896, 899, 902, 905, 907, 907, 907, 907,
+ 907, 907, 909, 912, 912, 912, 912, 912,
+ 914, 916, 918, 920, 922, 924, 924, 926,
+ 929, 932, 935, 938, 938, 940, 942, 944,
+ 946, 946, 948, 951, 954, 957, 957, 959,
+ 961, 963, 965, 967, 969, 975, 986, 988,
+ 991, 997, 1000, 1011, 1014, 1017, 1020, 1022,
+ 1024, 1026, 1028, 1034, 1037, 1040, 1042, 1044,
+ 1046, 1048, 1054, 1057, 1060, 1062, 1064, 1066,
+ 1068, 1074, 1077, 1080, 1083, 1083, 1085, 1087,
+ 1089, 1091, 1093, 1095, 1097, 1099, 1101, 1103,
+ 1105, 1107, 1109, 1111, 1113, 1115, 1117, 1120,
+ 1123, 1126, 1129, 1132, 1135, 1138, 1141, 1141,
+ 1141, 1143, 1143, 1143, 1143, 1145, 1145, 1147,
+ 1147, 1147, 1149, 1149, 1149, 1149, 1149, 1149,
+ 1151, 1153, 1153, 1153, 1155, 1155, 1155, 1155,
+ 1157, 1157, 1157, 1157, 1159, 1159, 1159, 1159,
+ 1159, 1161, 1161, 1161, 1161, 1163, 1163, 1163,
+ 1163, 1165, 1167, 1167, 1167, 1167, 1167, 1169,
+ 1169, 1171, 1171, 1171, 1171, 1171, 1173, 1173,
+ 1173, 1173, 1175, 1175, 1175, 1175, 1175, 1175,
+ 1175, 1175, 1177, 1177, 1177, 1179, 1181, 1181,
+ 1181, 1183, 1183, 1185, 1185, 1187, 1189, 1189,
+ 1189, 1189, 1189, 1191, 1193, 1193, 1193, 1193,
+ 1193, 1195, 1195, 1197, 1199, 1199, 1201, 1203,
+ 1203, 1203, 1203, 1203, 1205, 1205, 1205, 1205,
+ 1205, 1205, 1205, 1205, 1205, 1205, 1207, 1207,
+ 1207, 1209, 1209, 1211, 1211, 1211, 1211, 1213,
+ 1215, 1215, 1215, 1215, 1215, 1215, 1217, 1217,
+ 1219, 1219, 1221, 1221, 1223, 1223, 1223, 1223,
+ 1225, 1225, 1225, 1227, 1227, 1227, 1227, 1229,
+ 1229, 1231, 1231, 1231, 1231, 1233, 1233, 1233,
+ 1235, 1235, 1235, 1235, 1235, 1235, 1237, 1237,
+ 1237, 1237, 1237, 1239, 1241, 1243, 1245, 1247,
+ 1249, 1251, 1254, 1257, 1260, 1260, 1262, 1262,
+ 1264, 1266, 1268, 1270, 1272, 1274, 1276, 1278,
+ 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278,
+ 1278, 1278, 1278, 1278, 1278, 1278, 1281, 1281,
+ 1283, 1286, 1289, 1292, 1292, 1294, 1296, 1298,
+ 1300, 1302, 1304, 1304, 1304, 1304, 1307, 1310,
+ 1313, 1313, 1315, 1317, 1319, 1321, 1323, 1325,
+ 1325, 1327, 1330, 1333, 1336, 1339, 1339, 1341,
+ 1343, 1343, 1343, 1346, 1346, 1348, 1350, 1352,
+ 1354, 1356, 1358, 1358, 1360, 1363, 1366, 1369,
+ 1372, 1372, 1374, 1376, 1376, 1379, 1379, 1381,
+ 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383,
+ 1383, 1383, 1386, 1386, 1386, 1386, 1386, 1386,
+ 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386,
+ 1388, 1390, 1392, 1394, 1394, 1397, 1400, 1400,
+ 1402, 1404, 1406, 1408, 1408, 1411, 1414, 1414,
+ 1416, 1418, 1418, 1418, 1421, 1423, 1425, 1427,
+ 1429, 1431, 1433, 1435, 1437, 1439, 1441, 1441,
+ 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1443,
+ 1443, 1445, 1448, 1448, 1450, 1453, 1453, 1455,
+ 1458, 1460, 1460, 1462, 1465, 1468, 1468, 1468,
+ 1470, 1473, 1473, 1473, 1473, 1473, 1473, 1473,
+ 1473, 1473, 1473, 1475, 1478, 1478, 1478, 1480,
+ 1483, 1485, 1488, 1490, 1493, 1495, 1498, 1498,
+ 1498, 1498, 1498, 1500, 1503, 1503, 1505, 1508,
+ 1508, 1510, 1513, 1513, 1519, 1522, 1533, 1536,
+ 1547, 1550, 1550, 1550, 1550, 1550, 1550, 1550,
+ 1550, 1552, 1555, 1555, 1555, 1557, 1560, 1560,
+ 1560, 1560, 1560, 1560, 1560
+};
+
+static const char _zone_scanner_cond_lengths[] = {
+ 0, 2, 2, 2, 2, 2, 2, 2,
+ 0, 0, 3, 2, 2, 3, 2, 2,
+ 2, 0, 0, 2, 5, 5, 0, 0,
+ 0, 0, 0, 2, 2, 0, 0, 2,
+ 0, 0, 0, 2, 0, 0, 0, 2,
+ 0, 0, 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 0, 2, 2, 0, 0,
+ 0, 0, 2, 0, 2, 0, 0, 0,
+ 0, 2, 0, 0, 0, 2, 0, 2,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 2, 2, 0, 0, 2,
+ 0, 2, 0, 2, 2, 0, 0, 0,
+ 0, 2, 2, 0, 0, 0, 0, 2,
+ 0, 2, 2, 0, 2, 2, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0, 0, 2,
+ 0, 2, 0, 0, 0, 2, 2, 0,
+ 0, 0, 0, 0, 2, 0, 2, 0,
+ 2, 0, 2, 0, 0, 0, 2, 0,
+ 0, 2, 0, 0, 0, 2, 0, 2,
+ 0, 0, 0, 2, 0, 0, 2, 0,
+ 0, 0, 0, 0, 2, 3, 2, 0,
+ 2, 2, 2, 2, 0, 2, 3, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 3, 2, 2, 3, 2, 0,
+ 2, 3, 2, 2, 2, 2, 2, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 3, 2, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 2, 3, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 5, 5,
+ 5, 5, 0, 0, 2, 0, 0, 0,
+ 2, 0, 2, 3, 2, 2, 5, 5,
+ 5, 5, 3, 2, 5, 5, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3,
+ 2, 3, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 0, 0,
+ 0, 0, 1, 2, 2, 2, 2, 0,
+ 2, 0, 2, 3, 2, 2, 0, 2,
+ 2, 0, 0, 0, 0, 0, 2, 3,
+ 2, 2, 2, 2, 0, 2, 2, 0,
+ 0, 0, 0, 0, 2, 3, 2, 3,
+ 3, 0, 0, 0, 0, 2, 3, 0,
+ 2, 2, 2, 0, 0, 2, 3, 0,
+ 0, 2, 0, 0, 0, 0, 0, 2,
+ 2, 0, 0, 2, 0, 0, 0, 2,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 2, 0, 0, 0, 2, 0, 0, 0,
+ 2, 2, 0, 0, 0, 0, 2, 0,
+ 2, 0, 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 2, 2, 0, 0,
+ 2, 0, 2, 0, 2, 2, 0, 0,
+ 0, 0, 2, 2, 0, 0, 0, 0,
+ 2, 0, 2, 2, 0, 2, 2, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 2, 0, 2, 0, 0, 0, 2, 2,
+ 0, 0, 0, 0, 0, 2, 0, 2,
+ 0, 2, 0, 2, 0, 0, 0, 2,
+ 0, 0, 2, 0, 0, 0, 2, 0,
+ 2, 0, 0, 0, 2, 0, 0, 2,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 2, 2, 2, 2, 2,
+ 0, 2, 3, 3, 3, 2, 2, 2,
+ 2, 0, 2, 3, 3, 2, 3, 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, 2, 2, 0, 0, 0,
+ 0, 0, 2, 3, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 2, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 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, 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, 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, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 0, 0, 0,
+ 0, 3, 2, 0, 2, 3, 2, 0,
+ 2, 3, 2, 0, 2, 3, 3, 3,
+ 0, 2, 2, 0, 3, 0, 2, 2,
+ 0, 3, 0, 2, 2, 0, 3, 0,
+ 2, 2, 3, 0, 0, 0, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3,
+ 2, 2, 3, 2, 2, 2, 3, 2,
+ 2, 2, 3, 2, 2, 2, 3, 2,
+ 3, 2, 2, 3, 3, 3, 2, 3,
+ 2, 2, 3, 3, 0, 2, 2, 2,
+ 2, 2, 2, 0, 3, 3, 3, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 0, 3, 3, 3, 3, 3,
+ 0, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 2, 0, 0, 0, 0,
+ 0, 2, 3, 0, 0, 0, 0, 2,
+ 2, 2, 2, 2, 2, 0, 2, 3,
+ 3, 3, 3, 0, 2, 2, 2, 2,
+ 0, 2, 3, 3, 3, 0, 2, 2,
+ 2, 2, 2, 2, 6, 11, 2, 3,
+ 6, 3, 11, 3, 3, 3, 2, 2,
+ 2, 2, 6, 3, 3, 2, 2, 2,
+ 2, 6, 3, 3, 2, 2, 2, 2,
+ 6, 3, 3, 3, 0, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 3, 3, 3, 0, 0,
+ 2, 0, 0, 0, 2, 0, 2, 0,
+ 0, 2, 0, 0, 0, 0, 0, 2,
+ 2, 0, 0, 2, 0, 0, 0, 2,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 2, 0, 0, 0, 2, 0, 0, 0,
+ 2, 2, 0, 0, 0, 0, 2, 0,
+ 2, 0, 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 2, 2, 0, 0,
+ 2, 0, 2, 0, 2, 2, 0, 0,
+ 0, 0, 2, 2, 0, 0, 0, 0,
+ 2, 0, 2, 2, 0, 2, 2, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 2, 0, 2, 0, 0, 0, 2, 2,
+ 0, 0, 0, 0, 0, 2, 0, 2,
+ 0, 2, 0, 2, 0, 0, 0, 2,
+ 0, 0, 2, 0, 0, 0, 2, 0,
+ 2, 0, 0, 0, 2, 0, 0, 2,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2,
+ 2, 3, 3, 3, 0, 2, 0, 2,
+ 2, 2, 2, 2, 2, 2, 2, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3, 0, 2,
+ 3, 3, 3, 0, 2, 2, 2, 2,
+ 2, 2, 0, 0, 0, 3, 3, 3,
+ 0, 2, 2, 2, 2, 2, 2, 0,
+ 2, 3, 3, 3, 3, 0, 2, 2,
+ 0, 0, 3, 0, 2, 2, 2, 2,
+ 2, 2, 0, 2, 3, 3, 3, 3,
+ 0, 2, 2, 0, 3, 0, 2, 2,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 3, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 2, 2, 2, 0, 3, 3, 0, 2,
+ 2, 2, 2, 0, 3, 3, 0, 2,
+ 2, 0, 0, 3, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 0,
+ 2, 3, 0, 2, 3, 0, 2, 3,
+ 2, 0, 2, 3, 3, 0, 0, 2,
+ 3, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 3, 0, 0, 2, 3,
+ 2, 3, 2, 3, 2, 3, 0, 0,
+ 0, 0, 2, 3, 0, 2, 3, 0,
+ 2, 3, 0, 6, 3, 11, 3, 11,
+ 3, 0, 0, 0, 0, 0, 0, 0,
+ 2, 3, 0, 0, 2, 3, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static const short _zone_scanner_cond_keys[] = {
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 58, 59, 59,
+ 60, 127, -128, 9, 10, 10, 11, 58,
+ 59, 59, 60, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 58, 59, 59, 60, 127, -128, 9,
+ 10, 10, 11, 58, 59, 59, 60, 127,
+ -128, 9, 10, 10, 11, 58, 59, 59,
+ 60, 127, -128, 9, 10, 10, 11, 58,
+ 59, 59, 60, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 58, 59, 59, 60, 127,
+ -128, 9, 10, 10, 11, 58, 59, 59,
+ 60, 127, -128, 9, 10, 10, 11, 58,
+ 59, 59, 60, 127, -128, 9, 10, 10,
+ 11, 58, 59, 59, 60, 127, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 58, 59, 59,
+ 60, 127, -128, 9, 10, 10, 11, 58,
+ 59, 59, 60, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 41, 41, 10, 10,
+ 10, 10, 10, 10, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 9, 9, 10, 10, 32, 32,
+ 40, 40, 41, 41, 59, 59, 9, 9,
+ 10, 10, 32, 32, 40, 40, 41, 41,
+ 43, 43, 47, 47, 48, 57, 59, 59,
+ 65, 90, 97, 122, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 9, 9,
+ 10, 10, 32, 32, 40, 40, 41, 41,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 9, 9, 10, 10, 32, 32, 40, 40,
+ 41, 41, 43, 43, 47, 47, 48, 57,
+ 59, 59, 65, 90, 97, 122, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 9, 9, 10, 10, 32, 32, 40, 40,
+ 41, 41, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 9, 9, 10, 10, 32, 32, 40, 40,
+ 41, 41, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 9, 9, 10, 10, 32, 32, 40, 40,
+ 41, 41, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ 10, 10, 59, 59, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, 10, 10, 59, 59, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 10, 10, 59, 59, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 10, 10, 59, 59, -128, 9, 10, 10,
+ 11, 127, 9, 9, 10, 10, 32, 32,
+ 40, 40, 41, 41, 59, 59, -128, 9,
+ 10, 10, 11, 127, 9, 9, 10, 10,
+ 32, 32, 40, 40, 41, 41, 43, 43,
+ 47, 47, 48, 57, 59, 59, 65, 90,
+ 97, 122, -128, 9, 10, 10, 11, 127,
+ 9, 9, 10, 10, 32, 32, 40, 40,
+ 41, 41, 43, 43, 47, 47, 48, 57,
+ 59, 59, 65, 90, 97, 122, -128, 9,
+ 10, 10, 11, 127, 10, 10, 59, 59,
+ -128, 9, 10, 10, 11, 127, 10, 10,
+ 59, 59, -128, 9, 10, 10, 11, 127,
+ 0
+};
+
+static const char _zone_scanner_cond_spaces[] = {
+ 0, 0, 0, 0, 0, 0, 7, 7,
+ 7, 7, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 0, 0, 0,
+ 7, 7, 0, 0, 0, 0, 7, 7,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 0, 0, 0, 0,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 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, 7, 7, 7,
+ 7, 7, 7, 7, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 7, 7,
+ 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, 7, 7,
+ 7, 7, 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, 7, 7, 7, 7, 7, 7,
+ 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, 7, 7,
+ 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, 2, 3, 4,
+ 4, 4, 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, 7,
+ 7, 0, 0, 0, 7, 7, 7, 7,
+ 7, 7, 7, 7, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 7, 7, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 7, 7, 0, 0, 0, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 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, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 7,
+ 7, 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, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 0, 0, 0, 7,
+ 7, 7, 7, 0, 0, 0, 7, 7,
+ 7, 7, 7, 7, 0, 0, 0, 7,
+ 7, 7, 7, 7, 7, 0, 0, 0,
+ 7, 7, 7, 7, 7, 7, 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, 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, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 7, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 7, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 10, 13, 10, 10, 10, 13, 5,
+ 8, 5, 5, 5, 5, 5, 5, 8,
+ 5, 5, 1, 1, 8, 8, 8, 6,
+ 11, 6, 6, 6, 11, 9, 9, 9,
+ 10, 13, 10, 10, 10, 5, 5, 5,
+ 13, 5, 5, 12, 12, 12, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 10, 13, 10, 10,
+ 10, 13, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 13, 10, 10, 10, 13, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 10, 13, 10, 10,
+ 10, 13, 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, 1, 1, 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, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 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, 0, 0,
+ 0, 0, 0, 0, 0, 7, 7, 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, 7, 7,
+ 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,
+ 0, 0, 0, 0, 0, 0, 0, 7,
+ 7, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 7, 7, 7, 7, 7,
+ 7, 0, 0, 7, 7, 0, 0, 0,
+ 7, 7, 0, 0, 0, 7, 7, 0,
+ 0, 0, 0, 0, 7, 7, 0, 0,
+ 0, 0, 0, 0, 7, 7, 0, 0,
+ 0, 7, 7, 0, 0, 0, 7, 7,
+ 0, 0, 0, 7, 7, 0, 0, 0,
+ 7, 7, 0, 0, 0, 7, 7, 0,
+ 0, 0, 7, 7, 0, 0, 0, 7,
+ 7, 0, 0, 0, 7, 7, 0, 0,
+ 0, 6, 11, 6, 6, 6, 11, 9,
+ 9, 9, 5, 8, 5, 5, 5, 5,
+ 5, 5, 8, 5, 5, 8, 8, 8,
+ 10, 13, 10, 10, 10, 5, 5, 5,
+ 13, 5, 5, 12, 12, 12, 7, 7,
+ 0, 0, 0, 7, 7, 0, 0, 0,
+ 0
+};
+
+static const short _zone_scanner_key_offsets[] = {
+ 0, 0, 44, 62, 102, 118, 135, 143,
+ 151, 152, 153, 156, 175, 194, 200, 256,
+ 276, 318, 328, 330, 340, 354, 368, 372,
+ 374, 376, 378, 380, 390, 400, 402, 404,
+ 414, 416, 418, 420, 430, 432, 434, 436,
+ 446, 452, 454, 456, 458, 468, 472, 474,
+ 476, 486, 488, 490, 492, 502, 512, 514,
+ 516, 518, 519, 529, 530, 540, 544, 546,
+ 548, 550, 560, 562, 564, 566, 576, 580,
+ 586, 626, 628, 630, 632, 634, 636, 638,
+ 640, 650, 654, 656, 666, 676, 682, 683,
+ 693, 694, 704, 706, 716, 726, 730, 732,
+ 734, 736, 746, 756, 762, 764, 766, 768,
+ 778, 780, 790, 802, 804, 815, 827, 829,
+ 831, 833, 835, 845, 847, 849, 851, 853,
+ 855, 857, 859, 861, 863, 873, 875, 877,
+ 887, 893, 903, 905, 907, 909, 919, 929,
+ 941, 943, 945, 947, 949, 959, 961, 971,
+ 973, 983, 985, 995, 997, 999, 1001, 1011,
+ 1013, 1015, 1025, 1031, 1033, 1035, 1045, 1047,
+ 1057, 1059, 1061, 1063, 1075, 1077, 1079, 1089,
+ 1091, 1093, 1095, 1097, 1099, 1109, 1112, 1156,
+ 1160, 1166, 1208, 1226, 1234, 1246, 1254, 1257,
+ 1258, 1264, 1266, 1268, 1270, 1272, 1274, 1276,
+ 1282, 1288, 1330, 1333, 1351, 1393, 1396, 1404,
+ 1416, 1424, 1427, 1443, 1497, 1515, 1534, 1578,
+ 1584, 1598, 1612, 1668, 1686, 1700, 1710, 1722,
+ 1734, 1746, 1760, 1772, 1786, 1796, 1806, 1820,
+ 1840, 1854, 1864, 1874, 1880, 1894, 1896, 1898,
+ 1900, 1902, 1904, 1910, 1912, 1914, 1920, 1928,
+ 1948, 1992, 1998, 2016, 2072, 2090, 2104, 2114,
+ 2126, 2138, 2150, 2164, 2176, 2190, 2200, 2210,
+ 2224, 2244, 2258, 2268, 2278, 2293, 2337, 2351,
+ 2365, 2381, 2397, 2399, 2401, 2411, 2413, 2415,
+ 2417, 2427, 2429, 2439, 2445, 2459, 2473, 2489,
+ 2505, 2521, 2537, 2540, 2596, 2610, 2624, 2640,
+ 2652, 2660, 2670, 2680, 2690, 2702, 2712, 2724,
+ 2732, 2740, 2752, 2770, 2782, 2790, 2798, 2804,
+ 2810, 2820, 2826, 2835, 2836, 2837, 2848, 2864,
+ 2880, 2882, 2884, 2886, 2902, 2908, 2914, 2920,
+ 2932, 2938, 2940, 2942, 2954, 2962, 2970, 2976,
+ 2982, 2984, 2986, 2994, 3000, 3008, 3028, 3036,
+ 3037, 3047, 3059, 3069, 3072, 3078, 3094, 3104,
+ 3122, 3130, 3131, 3133, 3135, 3137, 3147, 3155,
+ 3158, 3164, 3176, 3184, 3202, 3212, 3230, 3238,
+ 3239, 3241, 3243, 3245, 3255, 3263, 3269, 3285,
+ 3288, 3291, 3298, 3305, 3313, 3321, 3338, 3341,
+ 3342, 3352, 3396, 3412, 3414, 3416, 3426, 3429,
+ 3439, 3441, 3451, 3455, 3457, 3459, 3461, 3463,
+ 3473, 3483, 3485, 3487, 3497, 3499, 3501, 3503,
+ 3513, 3515, 3517, 3519, 3529, 3535, 3537, 3539,
+ 3541, 3551, 3555, 3557, 3559, 3569, 3571, 3573,
+ 3575, 3585, 3595, 3597, 3599, 3601, 3602, 3612,
+ 3613, 3623, 3627, 3629, 3631, 3633, 3643, 3645,
+ 3647, 3649, 3659, 3661, 3663, 3665, 3667, 3669,
+ 3671, 3673, 3683, 3687, 3689, 3699, 3709, 3715,
+ 3716, 3726, 3727, 3737, 3739, 3749, 3759, 3763,
+ 3765, 3767, 3769, 3779, 3789, 3795, 3797, 3799,
+ 3801, 3811, 3813, 3823, 3835, 3837, 3848, 3860,
+ 3862, 3864, 3866, 3868, 3878, 3880, 3882, 3884,
+ 3886, 3888, 3890, 3892, 3894, 3896, 3906, 3908,
+ 3910, 3920, 3926, 3936, 3938, 3940, 3942, 3952,
+ 3962, 3974, 3976, 3978, 3980, 3982, 3992, 3994,
+ 4004, 4006, 4016, 4018, 4028, 4030, 4032, 4034,
+ 4044, 4046, 4048, 4058, 4064, 4066, 4068, 4078,
+ 4080, 4090, 4092, 4094, 4096, 4108, 4110, 4112,
+ 4122, 4124, 4126, 4128, 4130, 4132, 4142, 4144,
+ 4146, 4148, 4158, 4160, 4170, 4176, 4184, 4192,
+ 4204, 4210, 4226, 4229, 4232, 4235, 4241, 4250,
+ 4260, 4272, 4278, 4294, 4297, 4300, 4308, 4311,
+ 4317, 4318, 4319, 4320, 4328, 4329, 4330, 4338,
+ 4339, 4340, 4342, 4343, 4344, 4345, 4346, 4354,
+ 4355, 4356, 4357, 4358, 4366, 4367, 4368, 4370,
+ 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387,
+ 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4402,
+ 4403, 4404, 4405, 4413, 4423, 4440, 4441, 4442,
+ 4443, 4444, 4451, 4461, 4464, 4465, 4466, 4467,
+ 4475, 4482, 4489, 4497, 4505, 4513, 4523, 4524,
+ 4531, 4539, 4547, 4564, 4565, 4566, 4567, 4569,
+ 4570, 4571, 4572, 4573, 4574, 4578, 4581, 4586,
+ 4589, 4603, 4606, 4607, 4608, 4609, 4610, 4611,
+ 4619, 4626, 4635, 4642, 4660, 4667, 4668, 4669,
+ 4671, 4684, 4690, 4700, 4701, 4702, 4703, 4704,
+ 4705, 4706, 4707, 4708, 4709, 4714, 4718, 4720,
+ 4724, 4735, 4739, 4740, 4741, 4742, 4743, 4744,
+ 4745, 4746, 4747, 4748, 4749, 4750, 4751, 4752,
+ 4753, 4763, 4764, 4765, 4766, 4767, 4770, 4772,
+ 4775, 4787, 4803, 4804, 4816, 4824, 4828, 4834,
+ 4836, 4843, 4845, 4847, 4849, 4851, 4852, 4853,
+ 4855, 4857, 4859, 4860, 4866, 4870, 4874, 4875,
+ 4877, 4879, 4881, 4883, 4889, 4891, 4893, 4895,
+ 4897, 4898, 4899, 4901, 4903, 4905, 4906, 4907,
+ 4908, 4914, 4915, 4916, 4918, 4920, 4922, 4923,
+ 4924, 4925, 4931, 4933, 4934, 4935, 4936, 4937,
+ 4943, 4944, 4945, 4951, 4953, 4955, 4957, 4959,
+ 4961, 4963, 4965, 4971, 4973, 4975, 4977, 4979,
+ 4981, 4983, 4987, 4989, 4991, 4997, 4999, 5001,
+ 5007, 5009, 5011, 5015, 5017, 5018, 5024, 5026,
+ 5028, 5031, 5038, 5040, 5042, 5044, 5046, 5047,
+ 5048, 5050, 5052, 5054, 5055, 5061, 5062, 5063,
+ 5069, 5070, 5071, 5077, 5091, 5099, 5101, 5103,
+ 5105, 5107, 5109, 5115, 5121, 5123, 5125, 5127,
+ 5129, 5131, 5137, 5141, 5143, 5149, 5151, 5153,
+ 5159, 5161, 5163, 5165, 5171, 5173, 5175, 5181,
+ 5185, 5187, 5193, 5195, 5197, 5203, 5205, 5207,
+ 5209, 5215, 5217, 5219, 5225, 5228, 5237, 5246,
+ 5252, 5261, 5267, 5282, 5288, 5296, 5304, 5312,
+ 5330, 5338, 5356, 5364, 5382, 5390, 5408, 5416,
+ 5428, 5436, 5439, 5447, 5459, 5467, 5470, 5478,
+ 5490, 5498, 5501, 5509, 5521, 5529, 5532, 5535,
+ 5538, 5544, 5550, 5562, 5568, 5571, 5580, 5586,
+ 5601, 5607, 5610, 5612, 5620, 5635, 5641, 5644,
+ 5650, 5660, 5676, 5679, 5686, 5699, 5701, 5709,
+ 5719, 5727, 5737, 5746, 5754, 5760, 5768, 5776,
+ 5786, 5794, 5804, 5813, 5821, 5827, 5836, 5838,
+ 5852, 5864, 5878, 5890, 5904, 5916, 5930, 5940,
+ 5943, 5956, 5969, 5972, 5985, 5998, 6008, 6011,
+ 6024, 6037, 6047, 6050, 6063, 6076, 6086, 6089,
+ 6095, 6098, 6106, 6114, 6117, 6120, 6123, 6129,
+ 6132, 6140, 6148, 6151, 6154, 6156, 6164, 6172,
+ 6180, 6188, 6196, 6211, 6217, 6220, 6223, 6226,
+ 6228, 6236, 6244, 6252, 6264, 6270, 6282, 6288,
+ 6300, 6306, 6321, 6327, 6330, 6333, 6336, 6339,
+ 6342, 6348, 6354, 6362, 6370, 6382, 6388, 6401,
+ 6403, 6406, 6409, 6412, 6425, 6427, 6428, 6431,
+ 6434, 6436, 6448, 6451, 6452, 6459, 6466, 6468,
+ 6476, 6488, 6494, 6502, 6510, 6522, 6528, 6544,
+ 6547, 6550, 6553, 6556, 6558, 6566, 6574, 6582,
+ 6594, 6600, 6616, 6619, 6622, 6625, 6627, 6635,
+ 6645, 6651, 6659, 6667, 6674, 6708, 6721, 6723,
+ 6726, 6740, 6743, 6784, 6793, 6796, 6799, 6805,
+ 6813, 6821, 6830, 6867, 6870, 6873, 6879, 6887,
+ 6895, 6908, 6949, 6952, 6955, 6961, 6969, 6977,
+ 6992, 7026, 7029, 7032, 7035, 7069, 7081, 7093,
+ 7099, 7107, 7115, 7123, 7131, 7139, 7147, 7155,
+ 7163, 7171, 7179, 7194, 7200, 7213, 7215, 7218,
+ 7221, 7224, 7227, 7230, 7233, 7236, 7239, 7241,
+ 7243, 7249, 7251, 7253, 7255, 7261, 7263, 7269,
+ 7279, 7281, 7287, 7291, 7293, 7295, 7297, 7299,
+ 7305, 7311, 7313, 7315, 7321, 7323, 7325, 7327,
+ 7333, 7335, 7337, 7339, 7345, 7351, 7353, 7355,
+ 7357, 7363, 7367, 7369, 7371, 7377, 7379, 7381,
+ 7383, 7389, 7395, 7397, 7399, 7401, 7402, 7408,
+ 7409, 7415, 7419, 7421, 7423, 7425, 7431, 7433,
+ 7435, 7437, 7443, 7445, 7447, 7449, 7451, 7453,
+ 7455, 7457, 7463, 7467, 7469, 7475, 7481, 7487,
+ 7488, 7494, 7495, 7501, 7503, 7509, 7515, 7519,
+ 7521, 7523, 7525, 7531, 7537, 7543, 7545, 7547,
+ 7549, 7555, 7557, 7563, 7571, 7573, 7580, 7588,
+ 7590, 7592, 7594, 7596, 7602, 7604, 7606, 7608,
+ 7610, 7612, 7614, 7616, 7618, 7620, 7626, 7628,
+ 7630, 7636, 7642, 7648, 7650, 7652, 7654, 7660,
+ 7666, 7678, 7680, 7682, 7684, 7686, 7692, 7694,
+ 7700, 7702, 7708, 7710, 7716, 7718, 7720, 7722,
+ 7728, 7730, 7732, 7738, 7744, 7746, 7748, 7754,
+ 7756, 7762, 7764, 7766, 7768, 7776, 7778, 7780,
+ 7786, 7788, 7790, 7792, 7794, 7796, 7802, 7811,
+ 7817, 7823, 7825, 7833, 7841, 7849, 7861, 7867,
+ 7880, 7882, 7885, 7888, 7891, 7898, 7900, 7902,
+ 7910, 7918, 7926, 7934, 7942, 7955, 7961, 7973,
+ 7979, 7986, 7992, 7999, 8006, 8012, 8019, 8031,
+ 8037, 8038, 8039, 8040, 8041, 8042, 8045, 8051,
+ 8063, 8066, 8069, 8072, 8074, 8082, 8090, 8098,
+ 8106, 8114, 8127, 8133, 8139, 8151, 8154, 8157,
+ 8160, 8162, 8170, 8178, 8186, 8194, 8202, 8214,
+ 8220, 8236, 8239, 8242, 8245, 8248, 8250, 8258,
+ 8266, 8274, 8280, 8283, 8285, 8293, 8301, 8309,
+ 8317, 8325, 8337, 8343, 8359, 8362, 8365, 8368,
+ 8371, 8373, 8381, 8390, 8399, 8402, 8404, 8412,
+ 8424, 8430, 8436, 8442, 8443, 8449, 8455, 8461,
+ 8467, 8474, 8477, 8483, 8489, 8490, 8496, 8502,
+ 8509, 8515, 8521, 8522, 8528, 8534, 8541, 8543,
+ 8551, 8559, 8567, 8579, 8585, 8588, 8591, 8593,
+ 8601, 8613, 8619, 8631, 8637, 8640, 8643, 8645,
+ 8653, 8668, 8674, 8680, 8683, 8701, 8734, 8786,
+ 8840, 8858, 8912, 8930, 8963, 9032, 9101, 9101,
+ 9101, 9101, 9101, 9113, 9113, 9113, 9113, 9129,
+ 9129, 9146, 9149, 9149, 9193, 9196, 9196, 9212,
+ 9215, 9227, 9227, 9243, 9246, 9249, 9249, 9249,
+ 9266, 9269, 9269, 9269, 9269, 9269, 9269, 9269,
+ 9269, 9269, 9269, 9285, 9288, 9288, 9288, 9298,
+ 9301, 9313, 9316, 9328, 9331, 9343, 9346, 9346,
+ 9346, 9346, 9346, 9359, 9362, 9362, 9378, 9381,
+ 9381, 9397, 9400, 9400, 9414, 9417, 9430, 9433,
+ 9474, 9483, 9483, 9483, 9483, 9483, 9483, 9483,
+ 9483, 9499, 9502, 9502, 9502, 9518, 9521, 9521,
+ 9521, 9521, 9521, 9521, 9521
+};
+
+static const short _zone_scanner_trans_keys[] = {
+ 9, 32, 40, 41, 65, 67, 68, 69,
+ 72, 73, 75, 76, 77, 78, 79, 80,
+ 82, 83, 84, 85, 90, 97, 99, 100,
+ 101, 104, 105, 107, 108, 109, 110, 111,
+ 112, 114, 115, 116, 117, 122, 1802, 1851,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 68, 72, 77, 83, 87, 100, 104, 109,
+ 115, 119, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 65, 67, 68, 69, 72, 73,
+ 75, 76, 77, 78, 79, 80, 82, 83,
+ 84, 85, 90, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 2058, 2107, 9, 32,
+ 40, 41, 65, 70, 80, 97, 102, 112,
+ 3082, 3131, 3338, 3387, 3594, 3643, 9, 32,
+ 40, 41, 92, 3082, 3131, 3338, 3387, 3594,
+ 3643, -128, 8, 11, 58, 60, 127, 9,
+ 32, 40, 41, 1802, 1851, 2058, 2107, 9,
+ 32, 40, 41, 1802, 1851, 2058, 2107, 10,
+ 35, 2058, 1920, 2175, 9, 32, 40, 41,
+ 92, 2826, 2875, 3082, 3131, 3338, 3387, 3594,
+ 3643, -128, 8, 11, 58, 60, 127, 9,
+ 32, 40, 41, 92, 2826, 2875, 3082, 3131,
+ 3338, 3387, 3594, 3643, -128, 8, 11, 58,
+ 60, 127, 1802, 2058, 1664, 1919, 1920, 2175,
+ 9, 32, 40, 41, 58, 65, 67, 68,
+ 69, 72, 73, 75, 76, 77, 78, 79,
+ 80, 82, 83, 84, 85, 90, 92, 97,
+ 99, 100, 101, 104, 105, 107, 108, 109,
+ 110, 111, 112, 114, 115, 116, 117, 122,
+ 2826, 2875, 3082, 3131, 3338, 3387, 3594, 3643,
+ -128, 8, 11, 47, 48, 57, 60, 127,
+ 9, 32, 40, 41, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 1802, 1851,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 65, 67, 68, 69, 72, 73, 75, 76,
+ 77, 78, 79, 80, 82, 83, 84, 85,
+ 90, 97, 99, 100, 101, 104, 105, 107,
+ 108, 109, 110, 111, 112, 114, 115, 116,
+ 117, 122, 1802, 1851, 2058, 2107, 65, 68,
+ 69, 78, 83, 97, 100, 101, 110, 115,
+ 65, 97, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 1801, 1802, 1824, 1832,
+ 1833, 1851, 2057, 2058, 2080, 2088, 2089, 2107,
+ 1920, 2175, 1801, 1802, 1824, 1832, 1833, 1851,
+ 2057, 2058, 2080, 2088, 2089, 2107, 1920, 2175,
+ 78, 83, 110, 115, 83, 115, 75, 107,
+ 69, 101, 89, 121, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 82, 114, 84, 116, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 65, 97,
+ 77, 109, 69, 101, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 89, 121,
+ 78, 110, 67, 99, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 72, 78,
+ 83, 104, 110, 115, 67, 99, 73, 105,
+ 68, 100, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 65, 83, 97, 115,
+ 77, 109, 69, 101, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 75, 107,
+ 69, 101, 89, 121, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 85, 117, 73, 105, 52, 54, 56, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 52, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 73, 84, 105, 116,
+ 78, 110, 70, 102, 79, 111, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 84, 116, 80, 112, 83, 115, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 78, 80, 110, 112, 9, 32, 40, 41,
+ 2058, 2107, 9, 32, 40, 41, 65, 67,
+ 68, 69, 72, 73, 75, 76, 77, 78,
+ 79, 80, 82, 83, 84, 85, 90, 97,
+ 99, 100, 101, 104, 105, 107, 108, 109,
+ 110, 111, 112, 114, 115, 116, 117, 122,
+ 2058, 2107, 80, 112, 83, 115, 69, 101,
+ 67, 99, 75, 107, 69, 101, 89, 121,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 69, 88, 101, 120, 89, 121,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 51, 54, 79, 80,
+ 111, 112, 50, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 52, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 67, 99, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 73, 88,
+ 105, 120, 78, 110, 70, 102, 79, 111,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 65, 73, 83, 97,
+ 105, 115, 80, 112, 84, 116, 82, 114,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 68, 100, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 9, 32,
+ 40, 41, 69, 101, 3082, 3131, 3338, 3387,
+ 3594, 3643, 67, 99, 9, 32, 40, 41,
+ 51, 3082, 3131, 3338, 3387, 3594, 3643, 9,
+ 32, 40, 41, 80, 112, 3082, 3131, 3338,
+ 3387, 3594, 3643, 65, 97, 82, 114, 65,
+ 97, 77, 109, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 80, 112, 69,
+ 101, 78, 110, 80, 112, 71, 103, 80,
+ 112, 75, 107, 69, 101, 89, 121, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 84, 116, 82, 114, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 80,
+ 82, 84, 112, 114, 116, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 83,
+ 115, 73, 105, 71, 103, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 77, 79, 80, 82, 83, 86, 109,
+ 111, 112, 114, 115, 118, 73, 105, 77,
+ 109, 69, 101, 65, 97, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 65,
+ 97, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 70, 102, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 86,
+ 118, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 72, 104, 70, 102, 80,
+ 112, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 67, 99, 66, 98, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 76, 88, 89, 108, 120, 121, 83,
+ 115, 65, 97, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 84, 116, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 80, 112, 69, 101, 48, 57, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 48, 57, 82, 114, 73, 105, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 79, 111, 78, 110, 69, 101, 77,
+ 109, 68, 100, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 2058, 1920, 2175,
+ 9, 32, 40, 41, 65, 67, 68, 69,
+ 72, 73, 75, 76, 77, 78, 79, 80,
+ 82, 83, 84, 85, 90, 97, 99, 100,
+ 101, 104, 105, 107, 108, 109, 110, 111,
+ 112, 114, 115, 116, 117, 122, 1802, 1851,
+ 2058, 2107, 48, 57, 78, 80, 110, 112,
+ 9, 32, 40, 41, 2058, 2107, 9, 32,
+ 40, 41, 65, 67, 68, 69, 72, 73,
+ 75, 76, 77, 78, 79, 80, 82, 83,
+ 84, 85, 90, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 68, 72, 77, 83, 87, 100,
+ 104, 109, 115, 119, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 2058, 1920,
+ 2175, 10, 73, 79, 84, 105, 111, 116,
+ 78, 110, 67, 99, 76, 108, 85, 117,
+ 68, 100, 69, 101, 32, 59, 9, 10,
+ 40, 41, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 65, 67, 68, 69,
+ 72, 73, 75, 76, 77, 78, 79, 80,
+ 82, 83, 84, 85, 90, 97, 99, 100,
+ 101, 104, 105, 107, 108, 109, 110, 111,
+ 112, 114, 115, 116, 117, 122, 2058, 2107,
+ 48, 57, 2058, 1920, 2175, 9, 32, 40,
+ 41, 68, 72, 77, 83, 87, 100, 104,
+ 109, 115, 119, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 65, 67, 68, 69, 72,
+ 73, 75, 76, 77, 78, 79, 80, 82,
+ 83, 84, 85, 90, 97, 99, 100, 101,
+ 104, 105, 107, 108, 109, 110, 111, 112,
+ 114, 115, 116, 117, 122, 2058, 2107, 48,
+ 57, 2058, 1920, 2175, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 2058, 1920, 2175, 9, 32, 40, 41, 65,
+ 70, 80, 97, 102, 112, 3082, 3131, 3338,
+ 3387, 3594, 3643, 9, 32, 40, 41, 58,
+ 65, 67, 68, 69, 72, 73, 75, 76,
+ 77, 78, 79, 80, 82, 83, 84, 85,
+ 90, 92, 97, 99, 100, 101, 104, 105,
+ 107, 108, 109, 110, 111, 112, 114, 115,
+ 116, 117, 122, 3082, 3131, 3338, 3387, 3594,
+ 3643, -128, 8, 11, 47, 48, 57, 60,
+ 127, 9, 32, 40, 41, 65, 70, 80,
+ 97, 102, 112, 2826, 2875, 3082, 3131, 3338,
+ 3387, 3594, 3643, 9, 32, 40, 41, 92,
+ 2826, 2875, 3082, 3131, 3338, 3387, 3594, 3643,
+ -128, 8, 11, 58, 60, 127, 9, 32,
+ 40, 41, 65, 67, 68, 69, 72, 73,
+ 75, 76, 77, 78, 79, 80, 82, 83,
+ 84, 85, 90, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 1802, 1851, 2058, 2107,
+ 48, 57, 1802, 2058, 1664, 1919, 1920, 2175,
+ 9, 32, 40, 41, 1802, 1851, 2058, 2107,
+ -128, 8, 11, 58, 60, 127, 9, 32,
+ 40, 41, 1802, 1851, 2058, 2107, -128, 8,
+ 11, 58, 60, 127, 9, 32, 40, 41,
+ 58, 65, 67, 68, 69, 72, 73, 75,
+ 76, 77, 78, 79, 80, 82, 83, 84,
+ 85, 90, 92, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 2826, 2875, 3082, 3131,
+ 3338, 3387, 3594, 3643, -128, 8, 11, 47,
+ 48, 57, 60, 127, 9, 32, 40, 41,
+ 65, 68, 69, 78, 83, 97, 100, 101,
+ 110, 115, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 72, 78, 83, 104, 110, 115,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 85, 117, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 73, 84, 105, 116, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 78, 80,
+ 110, 112, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 69, 88, 101, 120, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 51, 54,
+ 79, 80, 111, 112, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 73, 88, 105, 120,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 65, 73, 83, 97, 105, 115, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 80, 112,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 84, 116, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 80, 82, 84, 112, 114, 116,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 77, 79, 80, 82, 83, 86, 109, 111,
+ 112, 114, 115, 118, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 76, 88, 89, 108,
+ 120, 121, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 82, 114, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 79, 111, 1802, 1851,
+ 2058, 2107, 1802, 2058, 1664, 1919, 1920, 2175,
+ 9, 32, 40, 41, 73, 79, 84, 105,
+ 111, 116, 1802, 1851, 2058, 2107, 82, 114,
+ 73, 105, 71, 103, 73, 105, 78, 110,
+ 32, 59, 9, 10, 40, 41, 84, 116,
+ 76, 108, 32, 59, 9, 10, 40, 41,
+ 9, 32, 40, 41, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 1802, 1851,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 65, 67, 68, 69, 72, 73, 75, 76,
+ 77, 78, 79, 80, 82, 83, 84, 85,
+ 90, 97, 99, 100, 101, 104, 105, 107,
+ 108, 109, 110, 111, 112, 114, 115, 116,
+ 117, 122, 1802, 1851, 2058, 2107, 48, 57,
+ 1802, 2058, 1664, 1919, 1920, 2175, 9, 32,
+ 40, 41, 65, 70, 80, 97, 102, 112,
+ 2826, 2875, 3082, 3131, 3338, 3387, 3594, 3643,
+ 9, 32, 40, 41, 58, 65, 67, 68,
+ 69, 72, 73, 75, 76, 77, 78, 79,
+ 80, 82, 83, 84, 85, 90, 92, 97,
+ 99, 100, 101, 104, 105, 107, 108, 109,
+ 110, 111, 112, 114, 115, 116, 117, 122,
+ 2826, 2875, 3082, 3131, 3338, 3387, 3594, 3643,
+ -128, 8, 11, 47, 48, 57, 60, 127,
+ 9, 32, 40, 41, 65, 68, 69, 78,
+ 83, 97, 100, 101, 110, 115, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 72, 78,
+ 83, 104, 110, 115, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 85, 117, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 73, 84,
+ 105, 116, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 78, 80, 110, 112, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 69, 88,
+ 101, 120, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 51, 54, 79, 80, 111, 112,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 73, 88, 105, 120, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 65, 73, 83, 97,
+ 105, 115, 1802, 1851, 2058, 2107, 9, 32,
+ 40, 41, 80, 112, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 84, 116, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 80, 82,
+ 84, 112, 114, 116, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 77, 79, 80, 82,
+ 83, 86, 109, 111, 112, 114, 115, 118,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 76, 88, 89, 108, 120, 121, 1802, 1851,
+ 2058, 2107, 9, 32, 40, 41, 82, 114,
+ 1802, 1851, 2058, 2107, 9, 32, 40, 41,
+ 79, 111, 1802, 1851, 2058, 2107, 9, 32,
+ 35, 40, 41, 1802, 1851, 2058, 2107, -128,
+ 8, 11, 58, 60, 127, 9, 32, 40,
+ 41, 65, 67, 68, 69, 72, 73, 75,
+ 76, 77, 78, 79, 80, 82, 83, 84,
+ 85, 90, 97, 99, 100, 101, 104, 105,
+ 107, 108, 109, 110, 111, 112, 114, 115,
+ 116, 117, 122, 1802, 1851, 2058, 2107, 48,
+ 57, 1801, 1802, 1824, 1832, 1833, 1851, 2057,
+ 2058, 2080, 2088, 2089, 2107, 1920, 2175, 1801,
+ 1802, 1824, 1832, 1833, 1851, 2057, 2058, 2080,
+ 2088, 2089, 2107, 1920, 2175, 1801, 1802, 1824,
+ 1832, 1833, 1851, 2057, 2058, 2080, 2088, 2089,
+ 2107, 1664, 1919, 1920, 2175, 1801, 1802, 1824,
+ 1832, 1833, 1851, 2057, 2058, 2080, 2088, 2089,
+ 2107, 1664, 1919, 1920, 2175, 65, 97, 65,
+ 97, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 83, 115, 68, 100, 66,
+ 98, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 76, 108, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 1802,
+ 2058, 1664, 1919, 1920, 2175, 9, 32, 40,
+ 41, 1802, 1851, 2058, 2107, -128, 8, 11,
+ 58, 60, 127, 9, 32, 40, 41, 1802,
+ 1851, 2058, 2107, -128, 8, 11, 58, 60,
+ 127, 1801, 1802, 1824, 1832, 1833, 1851, 2057,
+ 2058, 2080, 2088, 2089, 2107, 1664, 1919, 1920,
+ 2175, 1801, 1802, 1824, 1832, 1833, 1851, 2057,
+ 2058, 2080, 2088, 2089, 2107, 1664, 1919, 1920,
+ 2175, 1801, 1802, 1824, 1832, 1833, 1851, 2057,
+ 2058, 2080, 2088, 2089, 2107, 1664, 1919, 1920,
+ 2175, 1801, 1802, 1824, 1832, 1833, 1851, 2057,
+ 2058, 2080, 2088, 2089, 2107, 1664, 1919, 1920,
+ 2175, 2058, 1920, 2175, 9, 32, 40, 41,
+ 58, 65, 67, 68, 69, 72, 73, 75,
+ 76, 77, 78, 79, 80, 82, 83, 84,
+ 85, 90, 92, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 2826, 2875, 3082, 3131,
+ 3338, 3387, 3594, 3643, -128, 8, 11, 47,
+ 48, 57, 60, 127, 1801, 1802, 1824, 1832,
+ 1833, 1851, 2057, 2058, 2080, 2088, 2089, 2107,
+ 1920, 2175, 1801, 1802, 1824, 1832, 1833, 1851,
+ 2057, 2058, 2080, 2088, 2089, 2107, 1920, 2175,
+ 9, 32, 40, 41, 65, 68, 69, 78,
+ 83, 97, 100, 101, 110, 115, 2058, 2107,
+ 9, 32, 40, 41, 72, 78, 83, 104,
+ 110, 115, 2058, 2107, 9, 32, 40, 41,
+ 85, 117, 2058, 2107, 9, 32, 40, 41,
+ 73, 84, 105, 116, 2058, 2107, 9, 32,
+ 40, 41, 78, 80, 110, 112, 2058, 2107,
+ 9, 32, 40, 41, 69, 88, 101, 120,
+ 2058, 2107, 9, 32, 40, 41, 51, 54,
+ 79, 80, 111, 112, 2058, 2107, 9, 32,
+ 40, 41, 73, 88, 105, 120, 2058, 2107,
+ 9, 32, 40, 41, 65, 73, 83, 97,
+ 105, 115, 2058, 2107, 9, 32, 40, 41,
+ 80, 112, 2058, 2107, 9, 32, 40, 41,
+ 84, 116, 2058, 2107, 9, 32, 40, 41,
+ 80, 82, 84, 112, 114, 116, 2058, 2107,
+ 9, 32, 40, 41, 77, 79, 80, 82,
+ 83, 86, 109, 111, 112, 114, 115, 118,
+ 2058, 2107, 9, 32, 40, 41, 76, 88,
+ 89, 108, 120, 121, 2058, 2107, 9, 32,
+ 40, 41, 82, 114, 2058, 2107, 9, 32,
+ 40, 41, 79, 111, 2058, 2107, 1802, 2058,
+ 1664, 1919, 1920, 2175, 1802, 2058, 1664, 1919,
+ 1920, 2175, 9, 32, 40, 41, 78, 80,
+ 110, 112, 2058, 2107, 1802, 2058, 1664, 1919,
+ 1920, 2175, 266, 522, 1065, -128, 9, 11,
+ 40, 42, 127, 10, 10, 42, 46, 64,
+ 92, 95, 45, 57, 65, 90, 97, 122,
+ 32, 42, 46, 59, 92, 95, 9, 10,
+ 40, 41, 45, 57, 65, 90, 97, 122,
+ 32, 42, 45, 59, 92, 95, 9, 10,
+ 40, 41, 47, 57, 65, 90, 97, 122,
+ 48, 57, 48, 57, 48, 57, 32, 42,
+ 46, 59, 92, 95, 9, 10, 40, 41,
+ 45, 57, 65, 90, 97, 122, 32, 59,
+ 9, 10, 40, 41, 32, 59, 9, 10,
+ 40, 41, 34, 92, 33, 58, 60, 126,
+ 32, 33, 59, 92, 9, 10, 35, 39,
+ 40, 41, 42, 126, 32, 47, 48, 57,
+ 58, 126, 48, 57, 48, 57, 32, 33,
+ 59, 92, 9, 10, 35, 39, 40, 41,
+ 42, 126, 9, 32, 34, 59, 92, 1546,
+ 33, 126, 9, 32, 34, 59, 92, 1546,
+ 33, 126, 32, 59, 9, 10, 40, 41,
+ 32, 47, 48, 57, 58, 126, 48, 57,
+ 48, 57, 9, 32, 34, 59, 92, 1546,
+ 33, 126, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 1802, 1851,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 1802, 1851, 2058, 2107, 10, 9, 32, 40,
+ 41, 1802, 1851, 2058, 2107, 48, 57, 68,
+ 72, 77, 83, 87, 100, 104, 109, 115,
+ 119, 48, 57, 9, 32, 40, 41, 1802,
+ 1851, 2058, 2107, 48, 57, 2058, 1920, 2175,
+ 9, 32, 40, 41, 2058, 2107, 9, 32,
+ 40, 41, 42, 46, 92, 95, 2058, 2107,
+ 45, 57, 65, 90, 97, 122, 42, 46,
+ 92, 95, 45, 57, 65, 90, 97, 122,
+ 9, 32, 40, 41, 42, 45, 92, 95,
+ 1802, 1851, 2058, 2107, 47, 57, 65, 90,
+ 97, 122, 9, 32, 40, 41, 1802, 1851,
+ 2058, 2107, 10, 48, 57, 48, 57, 48,
+ 57, 42, 46, 92, 95, 45, 57, 65,
+ 90, 97, 122, 9, 32, 40, 41, 1802,
+ 1851, 2058, 2107, 2058, 1920, 2175, 9, 32,
+ 40, 41, 2058, 2107, 9, 32, 40, 41,
+ 2058, 2107, -128, 8, 11, 58, 60, 127,
+ 9, 32, 40, 41, 1802, 1851, 2058, 2107,
+ 9, 32, 40, 41, 42, 46, 92, 95,
+ 1802, 1851, 2058, 2107, 45, 57, 65, 90,
+ 97, 122, 42, 46, 92, 95, 45, 57,
+ 65, 90, 97, 122, 9, 32, 40, 41,
+ 42, 45, 92, 95, 1802, 1851, 2058, 2107,
+ 47, 57, 65, 90, 97, 122, 9, 32,
+ 40, 41, 1802, 1851, 2058, 2107, 10, 48,
+ 57, 48, 57, 48, 57, 42, 46, 92,
+ 95, 45, 57, 65, 90, 97, 122, 9,
+ 32, 40, 41, 1802, 1851, 2058, 2107, 1802,
+ 2058, 1664, 1919, 1920, 2175, 9, 32, 40,
+ 41, 42, 46, 92, 95, 2058, 2107, 45,
+ 57, 65, 90, 97, 122, 2058, 1920, 2175,
+ 2058, 1920, 2175, 43, 47, 57, 65, 90,
+ 97, 122, 43, 47, 57, 65, 90, 97,
+ 122, 43, 61, 47, 57, 65, 90, 97,
+ 122, 43, 61, 47, 57, 65, 90, 97,
+ 122, 9, 32, 40, 41, 43, 3082, 3131,
+ 3338, 3387, 3594, 3643, 47, 57, 65, 90,
+ 97, 122, 2058, 1920, 2175, 61, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 9, 32, 40, 41, 65, 67, 68, 69,
+ 72, 73, 75, 76, 77, 78, 79, 80,
+ 82, 83, 84, 85, 90, 97, 99, 100,
+ 101, 104, 105, 107, 108, 109, 110, 111,
+ 112, 114, 115, 116, 117, 122, 3082, 3131,
+ 3338, 3387, 3594, 3643, 9, 32, 40, 41,
+ 65, 70, 80, 97, 102, 112, 3082, 3131,
+ 3338, 3387, 3594, 3643, 65, 97, 65, 97,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 2058, 1920, 2175, 65, 68, 69,
+ 78, 83, 97, 100, 101, 110, 115, 65,
+ 97, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 78, 83, 110, 115, 83,
+ 115, 75, 107, 69, 101, 89, 121, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 82, 114, 84, 116, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 65, 97, 77, 109, 69, 101, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 89, 121, 78, 110, 67, 99, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 72, 78, 83, 104, 110, 115, 67,
+ 99, 73, 105, 68, 100, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 65,
+ 83, 97, 115, 77, 109, 69, 101, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 75, 107, 69, 101, 89, 121, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 85, 117, 73, 105, 52,
+ 54, 56, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 52, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 73,
+ 84, 105, 116, 78, 110, 70, 102, 79,
+ 111, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 84, 116, 80, 112, 83,
+ 115, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 80, 112, 83, 115, 69,
+ 101, 67, 99, 75, 107, 69, 101, 89,
+ 121, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 69, 88, 101, 120, 89,
+ 121, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 51, 54, 79,
+ 80, 111, 112, 50, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 52, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 67, 99, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 73,
+ 88, 105, 120, 78, 110, 70, 102, 79,
+ 111, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 65, 73, 83,
+ 97, 105, 115, 80, 112, 84, 116, 82,
+ 114, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 68, 100, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 9,
+ 32, 40, 41, 69, 101, 3082, 3131, 3338,
+ 3387, 3594, 3643, 67, 99, 9, 32, 40,
+ 41, 51, 3082, 3131, 3338, 3387, 3594, 3643,
+ 9, 32, 40, 41, 80, 112, 3082, 3131,
+ 3338, 3387, 3594, 3643, 65, 97, 82, 114,
+ 65, 97, 77, 109, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 80, 112,
+ 69, 101, 78, 110, 80, 112, 71, 103,
+ 80, 112, 75, 107, 69, 101, 89, 121,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 84, 116, 82, 114, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 80, 82, 84, 112, 114, 116, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 83, 115, 73, 105, 71, 103, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 77, 79, 80, 82, 83, 86,
+ 109, 111, 112, 114, 115, 118, 73, 105,
+ 77, 109, 69, 101, 65, 97, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 65, 97, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 70, 102, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 86, 118, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 72, 104, 70, 102,
+ 80, 112, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 67, 99, 66, 98,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 76, 88, 89, 108, 120, 121,
+ 83, 115, 65, 97, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 84, 116,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 80, 112, 69, 101, 48, 57,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 82, 114, 73, 105,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 79, 111, 78, 110, 69, 101,
+ 77, 109, 68, 100, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 83, 115,
+ 68, 100, 66, 98, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 76, 108,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 65, 70, 97, 102, 48, 57, 65, 70,
+ 97, 102, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 48, 57, 65, 70,
+ 97, 102, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 9, 32, 40, 41, 2058,
+ 2107, 9, 32, 40, 41, 48, 2058, 2107,
+ 49, 57, 9, 32, 40, 41, 1802, 1851,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 65, 70, 97, 102,
+ 48, 57, 65, 70, 97, 102, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 48, 57, 65, 70, 97, 102, 2058, 1920,
+ 2175, 2058, 1920, 2175, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 2058, 1920, 2175, 97,
+ 101, 105, 107, 110, 112, 108, 112, 110,
+ 32, 34, 44, 59, 9, 10, 40, 41,
+ 99, 104, 32, 34, 44, 59, 9, 10,
+ 40, 41, 112, 118, 52, 54, 104, 105,
+ 110, 116, 32, 34, 44, 59, 9, 10,
+ 40, 41, 104, 105, 110, 116, 32, 34,
+ 44, 59, 9, 10, 40, 41, 101, 121,
+ 48, 57, 32, 34, 44, 59, 9, 10,
+ 40, 41, 48, 57, 111, 45, 100, 101,
+ 102, 97, 117, 108, 116, 45, 97, 108,
+ 112, 110, 32, 34, 44, 59, 9, 10,
+ 40, 41, 111, 114, 116, 32, 34, 44,
+ 59, 9, 10, 40, 41, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 9,
+ 32, 40, 41, 97, 101, 105, 107, 109,
+ 110, 112, 3082, 3131, 3338, 3387, 3594, 3643,
+ 108, 112, 110, 61, 32, 34, 59, 9,
+ 10, 40, 41, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 2058, 1920, 2175,
+ 99, 104, 61, 34, 43, 47, 57, 65,
+ 90, 97, 122, 43, 47, 57, 65, 90,
+ 97, 122, 43, 47, 57, 65, 90, 97,
+ 122, 43, 61, 47, 57, 65, 90, 97,
+ 122, 43, 61, 47, 57, 65, 90, 97,
+ 122, 34, 43, 47, 57, 65, 90, 97,
+ 122, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 61, 43, 47, 57, 65,
+ 90, 97, 122, 43, 61, 47, 57, 65,
+ 90, 97, 122, 43, 61, 47, 57, 65,
+ 90, 97, 122, 9, 32, 40, 41, 43,
+ 3082, 3131, 3338, 3387, 3594, 3643, 47, 57,
+ 65, 90, 97, 122, 61, 112, 118, 52,
+ 54, 104, 105, 110, 116, 61, 34, 46,
+ 48, 57, 46, 48, 57, 34, 44, 46,
+ 48, 57, 46, 48, 57, 9, 32, 40,
+ 41, 44, 46, 3082, 3131, 3338, 3387, 3594,
+ 3643, 48, 57, 46, 48, 57, 104, 105,
+ 110, 116, 61, 34, 46, 48, 58, 65,
+ 70, 97, 102, 46, 48, 58, 65, 70,
+ 97, 102, 34, 44, 46, 48, 58, 65,
+ 70, 97, 102, 46, 48, 58, 65, 70,
+ 97, 102, 9, 32, 40, 41, 44, 46,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 58,
+ 65, 70, 97, 102, 46, 48, 58, 65,
+ 70, 97, 102, 101, 121, 48, 57, 9,
+ 32, 40, 41, 61, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 32, 59, 9, 10,
+ 40, 41, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 97, 110, 100, 97,
+ 116, 111, 114, 121, 61, 34, 65, 90,
+ 97, 122, 65, 90, 97, 122, 34, 44,
+ 65, 90, 97, 122, 9, 32, 40, 41,
+ 44, 3082, 3131, 3338, 3387, 3594, 3643, 65,
+ 90, 97, 122, 111, 45, 100, 101, 102,
+ 97, 117, 108, 116, 45, 97, 108, 112,
+ 110, 9, 32, 40, 41, 3082, 3131, 3338,
+ 3387, 3594, 3643, 111, 114, 116, 61, 34,
+ 48, 57, 48, 57, 34, 48, 57, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 48, 57, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, -128, 8, 11,
+ 58, 60, 127, 34, 68, 69, 73, 80,
+ 82, 100, 101, 105, 112, 114, 48, 57,
+ 32, 59, 9, 10, 40, 41, 48, 57,
+ 72, 83, 104, 115, 32, 59, 9, 10,
+ 40, 41, 65, 97, 32, 45, 59, 9,
+ 10, 40, 41, 78, 110, 83, 115, 69,
+ 101, 67, 99, 51, 45, 83, 115, 72,
+ 104, 65, 97, 49, 32, 59, 9, 10,
+ 40, 41, 67, 68, 99, 100, 67, 68,
+ 99, 100, 45, 71, 103, 79, 111, 83,
+ 115, 84, 116, 32, 59, 9, 10, 40,
+ 41, 83, 115, 65, 97, 80, 112, 50,
+ 51, 53, 54, 83, 115, 72, 104, 65,
+ 97, 50, 53, 54, 32, 59, 9, 10,
+ 40, 41, 56, 52, 83, 115, 72, 104,
+ 65, 97, 51, 56, 52, 32, 59, 9,
+ 10, 40, 41, 50, 52, 53, 53, 49,
+ 57, 32, 59, 9, 10, 40, 41, 52,
+ 56, 32, 59, 9, 10, 40, 41, 78,
+ 110, 68, 100, 73, 105, 82, 114, 69,
+ 101, 67, 99, 84, 116, 32, 59, 9,
+ 10, 40, 41, 82, 114, 73, 105, 86,
+ 118, 65, 97, 84, 116, 69, 101, 68,
+ 79, 100, 111, 78, 110, 83, 115, 32,
+ 59, 9, 10, 40, 41, 73, 105, 68,
+ 100, 32, 59, 9, 10, 40, 41, 83,
+ 115, 65, 97, 77, 83, 109, 115, 68,
+ 100, 53, 32, 59, 9, 10, 40, 41,
+ 72, 104, 65, 97, 49, 50, 53, 32,
+ 45, 59, 9, 10, 40, 41, 78, 110,
+ 83, 115, 69, 101, 67, 99, 51, 45,
+ 83, 115, 72, 104, 65, 97, 49, 32,
+ 59, 9, 10, 40, 41, 53, 54, 32,
+ 59, 9, 10, 40, 41, 49, 50, 32,
+ 59, 9, 10, 40, 41, 65, 73, 79,
+ 80, 83, 85, 97, 105, 111, 112, 115,
+ 117, 48, 57, 32, 59, 9, 10, 40,
+ 41, 48, 57, 67, 99, 80, 112, 75,
+ 107, 73, 105, 88, 120, 32, 59, 9,
+ 10, 40, 41, 65, 80, 83, 97, 112,
+ 115, 67, 99, 80, 112, 75, 107, 73,
+ 105, 88, 120, 32, 59, 9, 10, 40,
+ 41, 71, 75, 103, 107, 80, 112, 32,
+ 59, 9, 10, 40, 41, 73, 105, 88,
+ 120, 32, 59, 9, 10, 40, 41, 80,
+ 112, 75, 107, 73, 105, 32, 59, 9,
+ 10, 40, 41, 73, 105, 68, 100, 32,
+ 59, 9, 10, 40, 41, 71, 75, 103,
+ 107, 80, 112, 32, 59, 9, 10, 40,
+ 41, 73, 105, 88, 120, 32, 59, 9,
+ 10, 40, 41, 80, 112, 75, 107, 73,
+ 105, 32, 59, 9, 10, 40, 41, 82,
+ 114, 73, 105, 32, 59, 9, 10, 40,
+ 41, 46, 48, 57, 32, 46, 59, 9,
+ 10, 40, 41, 48, 57, 42, 92, 95,
+ 45, 57, 64, 90, 97, 122, 32, 59,
+ 9, 10, 40, 41, 42, 92, 95, 45,
+ 57, 64, 90, 97, 122, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 42,
+ 92, 95, 2058, 2107, 45, 57, 64, 90,
+ 97, 122, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 68, 72, 77, 83,
+ 87, 100, 104, 109, 115, 119, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 68, 72,
+ 77, 83, 87, 100, 104, 109, 115, 119,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 68, 72, 77, 83, 87, 100, 104, 109,
+ 115, 119, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 32, 59,
+ 68, 72, 77, 83, 87, 100, 104, 109,
+ 115, 119, 9, 10, 40, 41, 48, 57,
+ 32, 59, 9, 10, 40, 41, 48, 57,
+ 68, 72, 77, 83, 87, 100, 104, 109,
+ 115, 119, 48, 57, 32, 59, 9, 10,
+ 40, 41, 48, 57, 2058, 1920, 2175, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 68,
+ 72, 77, 83, 87, 100, 104, 109, 115,
+ 119, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 2058, 1920, 2175, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 68, 72,
+ 77, 83, 87, 100, 104, 109, 115, 119,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 2058, 1920, 2175, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 68, 72, 77,
+ 83, 87, 100, 104, 109, 115, 119, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 2058, 1920, 2175, 2058, 1920, 2175, 2058,
+ 1920, 2175, 32, 59, 9, 10, 40, 41,
+ 9, 32, 40, 41, 2058, 2107, 9, 32,
+ 40, 41, 2058, 2107, -128, 8, 11, 58,
+ 60, 127, 32, 59, 9, 10, 40, 41,
+ 2058, 1920, 2175, 42, 92, 95, 45, 57,
+ 64, 90, 97, 122, 9, 32, 40, 41,
+ 2058, 2107, 9, 32, 40, 41, 42, 92,
+ 95, 2058, 2107, 45, 57, 64, 90, 97,
+ 122, 32, 59, 9, 10, 40, 41, 2058,
+ 1920, 2175, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 42, 92, 95, 2058, 2107, 45, 57, 64,
+ 90, 97, 122, 32, 59, 9, 10, 40,
+ 41, 2058, 1920, 2175, 32, 59, 9, 10,
+ 40, 41, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, -128, 8,
+ 11, 58, 60, 127, 2058, 1920, 2175, 46,
+ 48, 58, 65, 70, 97, 102, 32, 46,
+ 59, 9, 10, 40, 41, 48, 58, 65,
+ 70, 97, 102, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 78, 83, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 78, 83, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 46, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 78, 83,
+ 2058, 2107, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 9, 32, 40, 41, 69, 87, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 69, 87,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 46, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 69, 87, 2058, 2107, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 45,
+ 2058, 2107, 48, 57, 48, 57, 9, 32,
+ 40, 41, 46, 109, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 9, 32, 40, 41, 46, 109, 3082, 3131,
+ 3338, 3387, 3594, 3643, 48, 57, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 48, 57, 9, 32, 40, 41, 46, 109,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 9, 32, 40, 41,
+ 46, 109, 3082, 3131, 3338, 3387, 3594, 3643,
+ 48, 57, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 2058, 1920, 2175, 9,
+ 32, 40, 41, 109, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 9, 32, 40, 41,
+ 109, 3082, 3131, 3338, 3387, 3594, 3643, 48,
+ 57, 2058, 1920, 2175, 9, 32, 40, 41,
+ 109, 3082, 3131, 3338, 3387, 3594, 3643, 48,
+ 57, 9, 32, 40, 41, 109, 3082, 3131,
+ 3338, 3387, 3594, 3643, 48, 57, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 2058, 1920, 2175, 9, 32, 40, 41, 109,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 9, 32, 40, 41, 109, 3082, 3131, 3338,
+ 3387, 3594, 3643, 48, 57, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 2058,
+ 1920, 2175, 9, 32, 40, 41, 109, 3082,
+ 3131, 3338, 3387, 3594, 3643, 48, 57, 9,
+ 32, 40, 41, 109, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 2058, 1920,
+ 2175, 9, 32, 40, 41, 2058, 2107, 2058,
+ 1920, 2175, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 9, 32, 40, 41, 2058,
+ 2107, 2058, 1920, 2175, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 2058, 1920, 2175, 2058,
+ 1920, 2175, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 42, 92, 95, 2058, 2107, 45, 57, 64,
+ 90, 97, 122, 32, 59, 9, 10, 40,
+ 41, 2058, 1920, 2175, 2058, 1920, 2175, 2058,
+ 1920, 2175, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, -128, 8, 11, 58, 60, 127,
+ 9, 32, 40, 41, 2058, 2107, 9, 32,
+ 40, 41, 2058, 2107, -128, 8, 11, 58,
+ 60, 127, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 2058, 2107, -128, 8,
+ 11, 58, 60, 127, 9, 32, 40, 41,
+ 2058, 2107, 9, 32, 40, 41, 42, 92,
+ 95, 2058, 2107, 45, 57, 64, 90, 97,
+ 122, 32, 59, 9, 10, 40, 41, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 2058, 1920, 2175, 48, 57,
+ 65, 90, 97, 122, 9, 32, 40, 41,
+ 2058, 2107, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 65, 90, 97, 122, 9, 32,
+ 40, 41, 2058, 2107, 9, 32, 40, 41,
+ 43, 2058, 2107, 47, 57, 65, 90, 97,
+ 122, 2570, 2619, 2058, 1920, 2175, 2058, 1920,
+ 2175, 2058, 1920, 2175, 9, 32, 33, 40,
+ 41, 49, 50, 3082, 3131, 3338, 3387, 3594,
+ 3643, 49, 50, 58, 46, 48, 57, 47,
+ 46, 57, 48, 57, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 2058, 1920, 2175, 58, 46, 48, 58, 65,
+ 70, 97, 102, 47, 46, 58, 65, 70,
+ 97, 102, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 65, 90, 97, 122,
+ 9, 32, 40, 41, 2058, 2107, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 65, 70,
+ 97, 102, 48, 57, 65, 70, 97, 102,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 65, 70, 97, 102,
+ 2058, 1920, 2175, 2058, 1920, 2175, 2058, 1920,
+ 2175, 2058, 1920, 2175, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 65, 70,
+ 97, 102, 48, 57, 65, 70, 97, 102,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 48, 57, 65, 70, 97, 102,
+ 2058, 1920, 2175, 2058, 1920, 2175, 2058, 1920,
+ 2175, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 48,
+ 49, 50, 51, 2058, 2107, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 46,
+ 2058, 2107, 7177, 7200, 7208, 7209, 7433, 7456,
+ 7464, 7465, 7689, 7712, 7720, 7721, 10506, 10555,
+ 10762, 10811, 11274, 11323, 11530, 11579, 11786, 11835,
+ 12298, 12347, 12554, 12603, 12810, 12859, 13322, 13371,
+ 13578, 13627, 13834, 13883, 4105, 4128, 4136, 4137,
+ 4139, 5130, 5179, 4143, 4153, 4161, 4186, 4193,
+ 4218, 2570, 2619, 5130, 4992, 5247, 5641, 5664,
+ 5672, 5673, 14602, 14651, 14858, 14907, 15370, 15419,
+ 15626, 15675, 15882, 15931, 6666, 6528, 6783, 4139,
+ 7177, 7200, 7208, 7209, 7433, 7456, 7464, 7465,
+ 7689, 7712, 7720, 7721, 10506, 10555, 10762, 10811,
+ 11274, 11323, 11530, 11579, 11786, 11835, 12298, 12347,
+ 12554, 12603, 12810, 12859, 13322, 13371, 13578, 13627,
+ 13834, 13883, 4143, 4153, 4161, 4186, 4193, 4218,
+ 8714, 9226, 9738, 8576, 8831, 9088, 9343, 9600,
+ 9855, 2058, 1920, 2175, 2058, 1920, 2175, 9,
+ 32, 40, 41, 2058, 2107, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 46, 2058, 2107, 48, 57, 46, 7177,
+ 7200, 7208, 7209, 7433, 7456, 7464, 7465, 7689,
+ 7712, 7720, 7721, 10506, 10555, 10762, 10811, 11274,
+ 11323, 11530, 11579, 11786, 11835, 12298, 12347, 12554,
+ 12603, 12810, 12859, 13322, 13371, 13578, 13627, 13834,
+ 13883, 48, 57, 2058, 1920, 2175, 2058, 1920,
+ 2175, 9, 32, 40, 41, 2058, 2107, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 46, 2058, 2107, 48, 58,
+ 65, 70, 97, 102, 46, 7177, 7200, 7208,
+ 7209, 7433, 7456, 7464, 7465, 7689, 7712, 7720,
+ 7721, 10506, 10555, 10762, 10811, 11274, 11323, 11530,
+ 11579, 11786, 11835, 12298, 12347, 12554, 12603, 12810,
+ 12859, 13322, 13371, 13578, 13627, 13834, 13883, 48,
+ 58, 65, 70, 97, 102, 2058, 1920, 2175,
+ 2058, 1920, 2175, 9, 32, 40, 41, 2058,
+ 2107, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 42, 92, 95,
+ 2058, 2107, 45, 57, 64, 90, 97, 122,
+ 7177, 7200, 7208, 7209, 7433, 7456, 7464, 7465,
+ 7689, 7712, 7720, 7721, 10506, 10555, 10762, 10811,
+ 11274, 11323, 11530, 11579, 11786, 11835, 12298, 12347,
+ 12554, 12603, 12810, 12859, 13322, 13371, 13578, 13627,
+ 13834, 13883, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 65, 67, 68, 69, 72,
+ 73, 75, 76, 77, 78, 79, 80, 82,
+ 83, 84, 85, 90, 97, 99, 100, 101,
+ 104, 105, 107, 108, 109, 110, 111, 112,
+ 114, 115, 116, 117, 122, 9, 32, 40,
+ 41, 65, 70, 80, 97, 102, 112, 2058,
+ 2107, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 65, 90, 97, 122, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 9, 32, 40, 41, 42,
+ 92, 95, 2058, 2107, 45, 57, 64, 90,
+ 97, 122, 9, 32, 40, 41, 2058, 2107,
+ 9, 32, 40, 41, 43, 2058, 2107, 47,
+ 57, 65, 90, 97, 122, 2570, 2619, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 2058, 1920, 2175, 2058, 1920,
+ 2175, 2058, 1920, 2175, 2058, 1920, 2175, 65,
+ 97, 65, 97, 9, 32, 40, 41, 2058,
+ 2107, 83, 115, 68, 100, 66, 98, 9,
+ 32, 40, 41, 2058, 2107, 76, 108, 9,
+ 32, 40, 41, 2058, 2107, 65, 68, 69,
+ 78, 83, 97, 100, 101, 110, 115, 65,
+ 97, 9, 32, 40, 41, 2058, 2107, 78,
+ 83, 110, 115, 83, 115, 75, 107, 69,
+ 101, 89, 121, 9, 32, 40, 41, 2058,
+ 2107, 9, 32, 40, 41, 2058, 2107, 82,
+ 114, 84, 116, 9, 32, 40, 41, 2058,
+ 2107, 65, 97, 77, 109, 69, 101, 9,
+ 32, 40, 41, 2058, 2107, 89, 121, 78,
+ 110, 67, 99, 9, 32, 40, 41, 2058,
+ 2107, 72, 78, 83, 104, 110, 115, 67,
+ 99, 73, 105, 68, 100, 9, 32, 40,
+ 41, 2058, 2107, 65, 83, 97, 115, 77,
+ 109, 69, 101, 9, 32, 40, 41, 2058,
+ 2107, 75, 107, 69, 101, 89, 121, 9,
+ 32, 40, 41, 2058, 2107, 9, 32, 40,
+ 41, 2058, 2107, 85, 117, 73, 105, 52,
+ 54, 56, 9, 32, 40, 41, 2058, 2107,
+ 52, 9, 32, 40, 41, 2058, 2107, 73,
+ 84, 105, 116, 78, 110, 70, 102, 79,
+ 111, 9, 32, 40, 41, 2058, 2107, 84,
+ 116, 80, 112, 83, 115, 9, 32, 40,
+ 41, 2058, 2107, 80, 112, 83, 115, 69,
+ 101, 67, 99, 75, 107, 69, 101, 89,
+ 121, 9, 32, 40, 41, 2058, 2107, 69,
+ 88, 101, 120, 89, 121, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 2058,
+ 2107, 51, 54, 79, 80, 111, 112, 50,
+ 9, 32, 40, 41, 2058, 2107, 52, 9,
+ 32, 40, 41, 2058, 2107, 67, 99, 9,
+ 32, 40, 41, 2058, 2107, 9, 32, 40,
+ 41, 2058, 2107, 73, 88, 105, 120, 78,
+ 110, 70, 102, 79, 111, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 2058,
+ 2107, 65, 73, 83, 97, 105, 115, 80,
+ 112, 84, 116, 82, 114, 9, 32, 40,
+ 41, 2058, 2107, 68, 100, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 69,
+ 101, 2058, 2107, 67, 99, 9, 32, 40,
+ 41, 51, 2058, 2107, 9, 32, 40, 41,
+ 80, 112, 2058, 2107, 65, 97, 82, 114,
+ 65, 97, 77, 109, 9, 32, 40, 41,
+ 2058, 2107, 80, 112, 69, 101, 78, 110,
+ 80, 112, 71, 103, 80, 112, 75, 107,
+ 69, 101, 89, 121, 9, 32, 40, 41,
+ 2058, 2107, 84, 116, 82, 114, 9, 32,
+ 40, 41, 2058, 2107, 80, 82, 84, 112,
+ 114, 116, 9, 32, 40, 41, 2058, 2107,
+ 83, 115, 73, 105, 71, 103, 9, 32,
+ 40, 41, 2058, 2107, 9, 32, 40, 41,
+ 2058, 2107, 77, 79, 80, 82, 83, 86,
+ 109, 111, 112, 114, 115, 118, 73, 105,
+ 77, 109, 69, 101, 65, 97, 9, 32,
+ 40, 41, 2058, 2107, 65, 97, 9, 32,
+ 40, 41, 2058, 2107, 70, 102, 9, 32,
+ 40, 41, 2058, 2107, 86, 118, 9, 32,
+ 40, 41, 2058, 2107, 72, 104, 70, 102,
+ 80, 112, 9, 32, 40, 41, 2058, 2107,
+ 67, 99, 66, 98, 9, 32, 40, 41,
+ 2058, 2107, 76, 88, 89, 108, 120, 121,
+ 83, 115, 65, 97, 9, 32, 40, 41,
+ 2058, 2107, 84, 116, 9, 32, 40, 41,
+ 2058, 2107, 80, 112, 69, 101, 48, 57,
+ 9, 32, 40, 41, 2058, 2107, 48, 57,
+ 82, 114, 73, 105, 9, 32, 40, 41,
+ 2058, 2107, 79, 111, 78, 110, 69, 101,
+ 77, 109, 68, 100, 9, 32, 40, 41,
+ 2058, 2107, 42, 92, 95, 45, 57, 64,
+ 90, 97, 122, 32, 59, 9, 10, 40,
+ 41, 32, 59, 9, 10, 40, 41, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 65, 90, 97, 122, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 43,
+ 2058, 2107, 47, 57, 65, 90, 97, 122,
+ 2570, 2619, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 43, 47, 57, 65, 90,
+ 97, 122, 2570, 2619, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 2058, 2107, 48, 57, 9, 32,
+ 40, 41, 45, 2058, 2107, 48, 57, 65,
+ 70, 97, 102, 9, 32, 40, 41, 2058,
+ 2107, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 65, 86, 97, 118, 48, 57, 65,
+ 86, 97, 118, 61, 48, 57, 65, 86,
+ 97, 118, 48, 57, 65, 86, 97, 118,
+ 61, 48, 57, 65, 86, 97, 118, 61,
+ 48, 57, 65, 86, 97, 118, 48, 57,
+ 65, 86, 97, 118, 61, 48, 57, 65,
+ 86, 97, 118, 32, 59, 9, 10, 40,
+ 41, 48, 57, 65, 86, 97, 118, 32,
+ 59, 9, 10, 40, 41, 61, 61, 61,
+ 61, 61, 2058, 1920, 2175, 48, 57, 65,
+ 70, 97, 102, 9, 32, 40, 41, 2058,
+ 2107, 48, 57, 65, 70, 97, 102, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 45, 2058,
+ 2107, 48, 57, 65, 70, 97, 102, 32,
+ 59, 9, 10, 40, 41, 48, 57, 65,
+ 70, 97, 102, 32, 59, 9, 10, 40,
+ 41, 48, 57, 65, 70, 97, 102, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 65, 70, 97, 102, 48, 57,
+ 65, 70, 97, 102, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 65, 70, 97, 102, 2058, 1920, 2175, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 9, 32, 40, 41, 2058, 2107,
+ 48, 57, 32, 59, 9, 10, 40, 41,
+ 48, 57, 32, 59, 9, 10, 40, 41,
+ 2058, 1920, 2175, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 65, 70, 97,
+ 102, 48, 57, 65, 70, 97, 102, 9,
+ 32, 40, 41, 3082, 3131, 3338, 3387, 3594,
+ 3643, 48, 57, 65, 70, 97, 102, 2058,
+ 1920, 2175, 2058, 1920, 2175, 2058, 1920, 2175,
+ 2058, 1920, 2175, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 46, 2058, 2107, 48, 57, 32, 46,
+ 59, 9, 10, 40, 41, 48, 57, 2058,
+ 1920, 2175, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 9, 32, 40, 41,
+ 2058, 2107, 48, 57, 65, 70, 97, 102,
+ 48, 57, 65, 70, 97, 102, 48, 57,
+ 65, 70, 97, 102, 48, 57, 65, 70,
+ 97, 102, 58, 48, 57, 65, 70, 97,
+ 102, 48, 57, 65, 70, 97, 102, 48,
+ 57, 65, 70, 97, 102, 48, 57, 65,
+ 70, 97, 102, 32, 58, 59, 9, 10,
+ 40, 41, 2058, 1920, 2175, 48, 57, 65,
+ 70, 97, 102, 48, 57, 65, 70, 97,
+ 102, 45, 48, 57, 65, 70, 97, 102,
+ 48, 57, 65, 70, 97, 102, 32, 45,
+ 59, 9, 10, 40, 41, 48, 57, 65,
+ 70, 97, 102, 48, 57, 65, 70, 97,
+ 102, 45, 48, 57, 65, 70, 97, 102,
+ 48, 57, 65, 70, 97, 102, 32, 45,
+ 59, 9, 10, 40, 41, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 9,
+ 32, 40, 41, 2058, 2107, -128, 8, 11,
+ 58, 60, 127, 32, 59, 9, 10, 40,
+ 41, 2058, 1920, 2175, 2058, 1920, 2175, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, 48,
+ 57, 9, 32, 40, 41, 2058, 2107, -128,
+ 8, 11, 58, 60, 127, 9, 32, 40,
+ 41, 2058, 2107, 9, 32, 40, 41, 2058,
+ 2107, -128, 8, 11, 58, 60, 127, 32,
+ 59, 9, 10, 40, 41, 2058, 1920, 2175,
+ 2058, 1920, 2175, 48, 57, 9, 32, 40,
+ 41, 2058, 2107, 48, 57, 9, 32, 40,
+ 41, 42, 92, 95, 2058, 2107, 45, 57,
+ 64, 90, 97, 122, 32, 59, 9, 10,
+ 40, 41, 32, 59, 9, 10, 40, 41,
+ 2058, 1920, 2175, 9, 32, 36, 40, 41,
+ 42, 92, 95, 1802, 1851, 2058, 2107, 45,
+ 57, 64, 90, 97, 122, 9, 32, 36,
+ 40, 41, 42, 58, 92, 95, 2826, 2875,
+ 3082, 3131, 3338, 3387, 3594, 3643, -128, 8,
+ 11, 44, 45, 57, 60, 63, 64, 90,
+ 91, 96, 97, 122, 123, 127, 9, 32,
+ 36, 40, 41, 42, 65, 67, 68, 69,
+ 72, 73, 75, 76, 77, 78, 79, 80,
+ 82, 83, 84, 85, 90, 92, 95, 97,
+ 99, 100, 101, 104, 105, 107, 108, 109,
+ 110, 111, 112, 114, 115, 116, 117, 122,
+ 1802, 1851, 2058, 2107, 45, 57, 64, 89,
+ 98, 121, 9, 32, 36, 40, 41, 42,
+ 65, 67, 68, 69, 72, 73, 75, 76,
+ 77, 78, 79, 80, 82, 83, 84, 85,
+ 90, 92, 95, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 1802, 1851, 2058, 2107,
+ 45, 47, 48, 57, 64, 89, 98, 121,
+ 9, 32, 36, 40, 41, 42, 92, 95,
+ 1802, 1851, 2058, 2107, 45, 57, 64, 90,
+ 97, 122, 9, 32, 36, 40, 41, 42,
+ 65, 67, 68, 69, 72, 73, 75, 76,
+ 77, 78, 79, 80, 82, 83, 84, 85,
+ 90, 92, 95, 97, 99, 100, 101, 104,
+ 105, 107, 108, 109, 110, 111, 112, 114,
+ 115, 116, 117, 122, 1802, 1851, 2058, 2107,
+ 45, 47, 48, 57, 64, 89, 98, 121,
+ 9, 32, 36, 40, 41, 42, 92, 95,
+ 1802, 1851, 2058, 2107, 45, 57, 64, 90,
+ 97, 122, 9, 32, 36, 40, 41, 42,
+ 58, 92, 95, 2826, 2875, 3082, 3131, 3338,
+ 3387, 3594, 3643, -128, 8, 11, 44, 45,
+ 57, 60, 63, 64, 90, 91, 96, 97,
+ 122, 123, 127, 9, 32, 36, 40, 41,
+ 42, 58, 65, 67, 68, 69, 72, 73,
+ 75, 76, 77, 78, 79, 80, 82, 83,
+ 84, 85, 90, 92, 95, 97, 99, 100,
+ 101, 104, 105, 107, 108, 109, 110, 111,
+ 112, 114, 115, 116, 117, 122, 2826, 2875,
+ 3082, 3131, 3338, 3387, 3594, 3643, -128, 8,
+ 11, 44, 45, 47, 48, 57, 60, 63,
+ 64, 89, 91, 96, 98, 121, 123, 127,
+ 9, 32, 36, 40, 41, 42, 58, 65,
+ 67, 68, 69, 72, 73, 75, 76, 77,
+ 78, 79, 80, 82, 83, 84, 85, 90,
+ 92, 95, 97, 99, 100, 101, 104, 105,
+ 107, 108, 109, 110, 111, 112, 114, 115,
+ 116, 117, 122, 2826, 2875, 3082, 3131, 3338,
+ 3387, 3594, 3643, -128, 8, 11, 44, 45,
+ 47, 48, 57, 60, 63, 64, 89, 91,
+ 96, 98, 121, 123, 127, 32, 33, 59,
+ 92, 9, 10, 35, 39, 40, 41, 42,
+ 126, 9, 32, 40, 41, 42, 46, 92,
+ 95, 2058, 2107, 45, 57, 65, 90, 97,
+ 122, 9, 32, 40, 41, 43, 3082, 3131,
+ 3338, 3387, 3594, 3643, 47, 57, 65, 90,
+ 97, 122, 2058, 1920, 2175, 9, 32, 40,
+ 41, 65, 67, 68, 69, 72, 73, 75,
+ 76, 77, 78, 79, 80, 82, 83, 84,
+ 85, 90, 97, 99, 100, 101, 104, 105,
+ 107, 108, 109, 110, 111, 112, 114, 115,
+ 116, 117, 122, 3082, 3131, 3338, 3387, 3594,
+ 3643, 2058, 1920, 2175, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 65, 70, 97, 102, 2058, 1920, 2175, 9,
+ 32, 40, 41, 2058, 2107, 48, 57, 65,
+ 70, 97, 102, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 48, 57, 65,
+ 70, 97, 102, 2058, 1920, 2175, 2058, 1920,
+ 2175, 9, 32, 40, 41, 97, 101, 105,
+ 107, 109, 110, 112, 3082, 3131, 3338, 3387,
+ 3594, 3643, 2058, 1920, 2175, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, -128,
+ 8, 11, 58, 60, 127, 2058, 1920, 2175,
+ 9, 32, 40, 41, 3082, 3131, 3338, 3387,
+ 3594, 3643, 2058, 1920, 2175, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 48,
+ 57, 2058, 1920, 2175, 9, 32, 40, 41,
+ 3082, 3131, 3338, 3387, 3594, 3643, 48, 57,
+ 2058, 1920, 2175, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 48, 57, 2058,
+ 1920, 2175, 9, 32, 33, 40, 41, 49,
+ 50, 3082, 3131, 3338, 3387, 3594, 3643, 2058,
+ 1920, 2175, 9, 32, 40, 41, 3082, 3131,
+ 3338, 3387, 3594, 3643, 48, 57, 65, 70,
+ 97, 102, 2058, 1920, 2175, 9, 32, 40,
+ 41, 3082, 3131, 3338, 3387, 3594, 3643, 48,
+ 57, 65, 70, 97, 102, 2058, 1920, 2175,
+ 5641, 5664, 5672, 5673, 14602, 14651, 14858, 14907,
+ 15370, 15419, 15626, 15675, 15882, 15931, 6666, 6528,
+ 6783, 4105, 4128, 4136, 4137, 4139, 5130, 5179,
+ 4143, 4153, 4161, 4186, 4193, 4218, 5130, 4992,
+ 5247, 4139, 7177, 7200, 7208, 7209, 7433, 7456,
+ 7464, 7465, 7689, 7712, 7720, 7721, 10506, 10555,
+ 10762, 10811, 11274, 11323, 11530, 11579, 11786, 11835,
+ 12298, 12347, 12554, 12603, 12810, 12859, 13322, 13371,
+ 13578, 13627, 13834, 13883, 4143, 4153, 4161, 4186,
+ 4193, 4218, 8714, 9226, 9738, 8576, 8831, 9088,
+ 9343, 9600, 9855, 9, 32, 40, 41, 3082,
+ 3131, 3338, 3387, 3594, 3643, 48, 57, 65,
+ 70, 97, 102, 2058, 1920, 2175, 9, 32,
+ 40, 41, 3082, 3131, 3338, 3387, 3594, 3643,
+ 48, 57, 65, 70, 97, 102, 2058, 1920,
+ 2175, 0
+};
+
+static const char _zone_scanner_single_lengths[] = {
+ 0, 42, 16, 40, 16, 11, 8, 8,
+ 1, 1, 1, 13, 13, 2, 48, 18,
+ 42, 10, 2, 10, 12, 12, 4, 2,
+ 2, 2, 2, 10, 10, 2, 2, 10,
+ 2, 2, 2, 10, 2, 2, 2, 10,
+ 6, 2, 2, 2, 10, 4, 2, 2,
+ 10, 2, 2, 2, 10, 10, 2, 2,
+ 2, 1, 10, 1, 10, 4, 2, 2,
+ 2, 10, 2, 2, 2, 10, 4, 6,
+ 40, 2, 2, 2, 2, 2, 2, 2,
+ 10, 4, 2, 10, 10, 6, 1, 10,
+ 1, 10, 2, 10, 10, 4, 2, 2,
+ 2, 10, 10, 6, 2, 2, 2, 10,
+ 2, 10, 12, 2, 11, 12, 2, 2,
+ 2, 2, 10, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 10, 2, 2, 10,
+ 6, 10, 2, 2, 2, 10, 10, 12,
+ 2, 2, 2, 2, 10, 2, 10, 2,
+ 10, 2, 10, 2, 2, 2, 10, 2,
+ 2, 10, 6, 2, 2, 10, 2, 10,
+ 2, 2, 0, 10, 2, 2, 10, 2,
+ 2, 2, 2, 2, 10, 1, 42, 4,
+ 6, 40, 16, 6, 10, 6, 1, 1,
+ 6, 2, 2, 2, 2, 2, 2, 2,
+ 6, 40, 1, 16, 40, 1, 6, 10,
+ 6, 1, 16, 46, 18, 13, 42, 2,
+ 8, 8, 48, 18, 14, 10, 12, 12,
+ 12, 14, 12, 14, 10, 10, 14, 20,
+ 14, 10, 10, 2, 14, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 8, 18,
+ 42, 2, 18, 48, 18, 14, 10, 12,
+ 12, 12, 14, 12, 14, 10, 10, 14,
+ 20, 14, 10, 10, 9, 42, 12, 12,
+ 12, 12, 2, 2, 10, 2, 2, 2,
+ 10, 2, 10, 2, 8, 8, 12, 12,
+ 12, 12, 1, 48, 12, 12, 16, 12,
+ 8, 10, 10, 10, 12, 10, 12, 8,
+ 8, 12, 18, 12, 8, 8, 2, 2,
+ 10, 2, 3, 1, 1, 5, 6, 6,
+ 0, 0, 0, 6, 2, 2, 2, 4,
+ 0, 0, 0, 4, 6, 6, 2, 0,
+ 0, 0, 6, 6, 6, 18, 8, 1,
+ 8, 10, 8, 1, 6, 10, 4, 12,
+ 8, 1, 0, 0, 0, 4, 8, 1,
+ 6, 6, 8, 12, 4, 12, 8, 1,
+ 0, 0, 0, 4, 8, 2, 10, 1,
+ 1, 1, 1, 2, 2, 11, 1, 1,
+ 10, 44, 16, 2, 2, 10, 1, 10,
+ 2, 10, 4, 2, 2, 2, 2, 10,
+ 10, 2, 2, 10, 2, 2, 2, 10,
+ 2, 2, 2, 10, 6, 2, 2, 2,
+ 10, 4, 2, 2, 10, 2, 2, 2,
+ 10, 10, 2, 2, 2, 1, 10, 1,
+ 10, 4, 2, 2, 2, 10, 2, 2,
+ 2, 10, 2, 2, 2, 2, 2, 2,
+ 2, 10, 4, 2, 10, 10, 6, 1,
+ 10, 1, 10, 2, 10, 10, 4, 2,
+ 2, 2, 10, 10, 6, 2, 2, 2,
+ 10, 2, 10, 12, 2, 11, 12, 2,
+ 2, 2, 2, 10, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 10, 2, 2,
+ 10, 6, 10, 2, 2, 2, 10, 10,
+ 12, 2, 2, 2, 2, 10, 2, 10,
+ 2, 10, 2, 10, 2, 2, 2, 10,
+ 2, 2, 10, 6, 2, 2, 10, 2,
+ 10, 2, 2, 0, 10, 2, 2, 10,
+ 2, 2, 2, 2, 2, 10, 2, 2,
+ 2, 10, 2, 10, 6, 6, 6, 6,
+ 0, 10, 1, 1, 1, 6, 7, 8,
+ 6, 0, 10, 1, 1, 6, 1, 6,
+ 1, 1, 1, 4, 1, 1, 4, 1,
+ 1, 2, 1, 1, 1, 1, 4, 1,
+ 1, 1, 1, 4, 1, 1, 0, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 4, 1,
+ 1, 1, 4, 10, 17, 1, 1, 1,
+ 1, 3, 10, 1, 1, 1, 1, 2,
+ 1, 1, 2, 2, 2, 10, 1, 1,
+ 2, 2, 11, 1, 1, 1, 2, 1,
+ 1, 1, 1, 1, 2, 1, 3, 1,
+ 12, 1, 1, 1, 1, 1, 1, 2,
+ 1, 3, 1, 12, 1, 1, 1, 0,
+ 11, 2, 10, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 2, 0,
+ 11, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 10, 1, 1, 1, 1, 1, 0, 1,
+ 10, 10, 1, 10, 2, 4, 2, 2,
+ 3, 2, 2, 2, 2, 1, 1, 2,
+ 2, 2, 1, 2, 4, 4, 1, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 2, 2, 2, 1, 1, 1,
+ 2, 1, 1, 2, 2, 2, 1, 1,
+ 1, 2, 2, 1, 1, 1, 1, 2,
+ 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 4, 2, 2, 2, 2, 2, 2,
+ 2, 2, 4, 2, 1, 2, 2, 2,
+ 3, 3, 2, 2, 2, 2, 1, 1,
+ 2, 2, 2, 1, 2, 1, 1, 2,
+ 1, 1, 2, 12, 2, 2, 2, 2,
+ 2, 2, 2, 6, 2, 2, 2, 2,
+ 2, 2, 4, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 4,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 1, 3, 3, 2,
+ 3, 6, 9, 6, 6, 6, 6, 16,
+ 6, 16, 6, 16, 6, 12, 2, 10,
+ 2, 1, 6, 10, 6, 1, 6, 10,
+ 6, 1, 6, 10, 6, 1, 1, 1,
+ 2, 6, 6, 2, 1, 3, 6, 9,
+ 2, 1, 0, 6, 9, 2, 1, 2,
+ 10, 10, 1, 1, 3, 0, 6, 8,
+ 6, 8, 7, 8, 6, 6, 6, 8,
+ 6, 8, 7, 8, 6, 7, 0, 12,
+ 10, 12, 10, 12, 10, 12, 10, 1,
+ 11, 11, 1, 11, 11, 10, 1, 11,
+ 11, 10, 1, 11, 11, 10, 1, 6,
+ 1, 6, 6, 1, 1, 1, 6, 1,
+ 6, 6, 1, 1, 0, 6, 6, 6,
+ 6, 6, 9, 2, 1, 1, 1, 0,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 9, 2, 1, 1, 1, 1, 1,
+ 0, 6, 6, 6, 6, 6, 7, 2,
+ 1, 1, 1, 13, 2, 1, 1, 1,
+ 0, 10, 1, 1, 1, 1, 0, 6,
+ 6, 6, 6, 6, 6, 0, 10, 1,
+ 1, 1, 1, 0, 6, 6, 6, 6,
+ 0, 10, 1, 1, 1, 0, 6, 10,
+ 6, 6, 6, 7, 34, 7, 2, 1,
+ 14, 1, 35, 3, 1, 1, 6, 6,
+ 6, 7, 35, 1, 1, 6, 6, 6,
+ 7, 35, 1, 1, 6, 6, 6, 9,
+ 34, 1, 1, 1, 34, 12, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 9, 6, 7, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 2, 2,
+ 6, 2, 2, 2, 6, 2, 6, 10,
+ 2, 6, 4, 2, 2, 2, 2, 6,
+ 6, 2, 2, 6, 2, 2, 2, 6,
+ 2, 2, 2, 6, 6, 2, 2, 2,
+ 6, 4, 2, 2, 6, 2, 2, 2,
+ 6, 6, 2, 2, 2, 1, 6, 1,
+ 6, 4, 2, 2, 2, 6, 2, 2,
+ 2, 6, 2, 2, 2, 2, 2, 2,
+ 2, 6, 4, 2, 6, 6, 6, 1,
+ 6, 1, 6, 2, 6, 6, 4, 2,
+ 2, 2, 6, 6, 6, 2, 2, 2,
+ 6, 2, 6, 8, 2, 7, 8, 2,
+ 2, 2, 2, 6, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 6, 2, 2,
+ 6, 6, 6, 2, 2, 2, 6, 6,
+ 12, 2, 2, 2, 2, 6, 2, 6,
+ 2, 6, 2, 6, 2, 2, 2, 6,
+ 2, 2, 6, 6, 2, 2, 6, 2,
+ 6, 2, 2, 0, 6, 2, 2, 6,
+ 2, 2, 2, 2, 2, 6, 3, 2,
+ 2, 0, 6, 6, 6, 6, 6, 7,
+ 2, 1, 1, 1, 1, 2, 0, 6,
+ 6, 6, 6, 6, 7, 6, 6, 0,
+ 1, 0, 1, 1, 0, 1, 2, 2,
+ 1, 1, 1, 1, 1, 1, 0, 6,
+ 1, 1, 1, 0, 6, 6, 6, 6,
+ 6, 7, 2, 0, 2, 1, 1, 1,
+ 0, 6, 6, 6, 6, 6, 6, 0,
+ 10, 1, 1, 1, 1, 0, 6, 6,
+ 2, 2, 1, 0, 6, 6, 6, 6,
+ 6, 6, 0, 10, 1, 1, 1, 1,
+ 0, 6, 7, 3, 1, 0, 6, 6,
+ 0, 0, 0, 1, 0, 0, 0, 0,
+ 3, 1, 0, 0, 1, 0, 0, 3,
+ 0, 0, 1, 0, 0, 3, 0, 6,
+ 6, 6, 6, 2, 1, 1, 0, 6,
+ 6, 6, 6, 2, 1, 1, 0, 6,
+ 9, 2, 2, 1, 12, 17, 46, 46,
+ 12, 46, 12, 17, 51, 51, 0, 0,
+ 0, 0, 4, 0, 0, 0, 10, 0,
+ 11, 1, 0, 44, 1, 0, 10, 1,
+ 6, 0, 10, 1, 1, 0, 0, 17,
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 10, 1, 0, 0, 10, 1,
+ 10, 1, 10, 1, 10, 1, 0, 0,
+ 0, 0, 13, 1, 0, 10, 1, 0,
+ 10, 1, 0, 14, 1, 7, 1, 35,
+ 3, 0, 0, 0, 0, 0, 0, 0,
+ 10, 1, 0, 0, 10, 1, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static const char _zone_scanner_range_lengths[] = {
+ 0, 1, 1, 0, 0, 3, 0, 0,
+ 0, 0, 1, 3, 3, 2, 4, 1,
+ 0, 0, 0, 0, 1, 1, 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, 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, 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, 1, 1, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 0,
+ 0, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 2,
+ 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0, 4, 0, 3, 1, 2,
+ 3, 3, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 2, 0, 1,
+ 1, 2, 0, 4, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 3, 1, 1, 1,
+ 2, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 3, 3, 2, 2,
+ 2, 2, 1, 4, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 2,
+ 0, 2, 3, 0, 0, 3, 5, 5,
+ 1, 1, 1, 5, 2, 2, 2, 4,
+ 3, 1, 1, 4, 1, 1, 2, 3,
+ 1, 1, 1, 0, 1, 1, 0, 0,
+ 1, 1, 1, 1, 0, 3, 3, 3,
+ 0, 0, 1, 1, 1, 3, 0, 1,
+ 0, 3, 0, 3, 3, 3, 0, 0,
+ 1, 1, 1, 3, 0, 2, 3, 1,
+ 1, 3, 3, 3, 3, 3, 1, 0,
+ 0, 0, 0, 0, 0, 0, 1, 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, 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, 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, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 3,
+ 3, 3, 1, 1, 1, 0, 1, 1,
+ 3, 3, 3, 1, 1, 1, 1, 0,
+ 0, 0, 0, 2, 0, 0, 2, 0,
+ 0, 0, 0, 0, 0, 0, 2, 0,
+ 0, 0, 0, 2, 0, 0, 1, 3,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 2, 0, 1, 0, 0, 0, 3,
+ 3, 3, 3, 3, 3, 0, 0, 3,
+ 3, 3, 3, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0, 0, 3,
+ 3, 3, 3, 3, 3, 0, 0, 1,
+ 1, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 2, 0, 2,
+ 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 1, 1, 1,
+ 1, 3, 0, 1, 3, 0, 2, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 2, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 0, 0, 0, 2,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0, 0, 2,
+ 0, 0, 0, 0, 0, 2, 0, 0,
+ 0, 2, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 2, 0, 0, 2,
+ 0, 0, 2, 1, 3, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0,
+ 0, 2, 0, 0, 2, 0, 0, 2,
+ 0, 0, 0, 2, 0, 0, 2, 0,
+ 0, 2, 0, 0, 2, 0, 0, 0,
+ 2, 0, 0, 2, 1, 3, 3, 2,
+ 3, 0, 3, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 3, 1,
+ 3, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 0, 3, 2, 1, 3, 0, 3,
+ 2, 1, 1, 1, 3, 2, 1, 2,
+ 0, 3, 1, 3, 5, 1, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 1, 1, 1, 1, 1, 0, 1, 1,
+ 1, 0, 1, 1, 1, 0, 1, 0,
+ 1, 1, 1, 1, 1, 1, 0, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 2, 1, 1, 1, 1,
+ 1, 1, 1, 3, 0, 3, 0, 3,
+ 0, 3, 2, 1, 1, 1, 1, 1,
+ 3, 0, 1, 1, 3, 0, 3, 0,
+ 1, 1, 1, 0, 0, 0, 1, 1,
+ 1, 1, 1, 0, 3, 3, 1, 1,
+ 3, 0, 1, 1, 3, 3, 3, 1,
+ 1, 1, 1, 1, 1, 1, 1, 3,
+ 3, 3, 1, 1, 1, 1, 1, 0,
+ 0, 1, 1, 0, 0, 3, 0, 1,
+ 0, 1, 3, 3, 1, 1, 0, 1,
+ 1, 1, 1, 1, 1, 0, 1, 1,
+ 3, 3, 1, 1, 0, 1, 1, 3,
+ 0, 1, 1, 1, 0, 0, 3, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 0, 3, 0, 1, 1,
+ 1, 1, 1, 1, 1, 1, 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, 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, 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, 1, 1, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 3, 2,
+ 2, 1, 1, 1, 1, 3, 0, 3,
+ 0, 1, 1, 1, 3, 0, 1, 1,
+ 1, 1, 1, 1, 3, 0, 3, 3,
+ 3, 3, 3, 3, 3, 3, 5, 2,
+ 0, 0, 0, 0, 0, 1, 3, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 2, 3, 5, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 3, 3,
+ 3, 1, 1, 1, 1, 1, 1, 1,
+ 3, 2, 1, 1, 1, 1, 1, 1,
+ 1, 3, 3, 3, 1, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3,
+ 2, 1, 3, 3, 0, 3, 3, 2,
+ 3, 3, 0, 3, 3, 2, 1, 1,
+ 1, 1, 3, 2, 1, 1, 1, 1,
+ 3, 0, 3, 2, 1, 1, 1, 1,
+ 3, 2, 2, 1, 3, 8, 3, 4,
+ 3, 4, 3, 8, 9, 9, 0, 0,
+ 0, 0, 4, 0, 0, 0, 3, 0,
+ 3, 1, 0, 0, 1, 0, 3, 1,
+ 3, 0, 3, 1, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 1, 0, 0, 0, 1,
+ 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 1, 0, 3, 1, 0,
+ 3, 1, 0, 0, 1, 3, 1, 3,
+ 3, 0, 0, 0, 0, 0, 0, 0,
+ 3, 1, 0, 0, 3, 1, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+static const short _zone_scanner_index_offsets[] = {
+ 0, 0, 44, 62, 103, 120, 135, 144,
+ 153, 155, 157, 160, 177, 194, 199, 252,
+ 272, 315, 326, 329, 340, 354, 368, 373,
+ 376, 379, 382, 385, 396, 407, 410, 413,
+ 424, 427, 430, 433, 444, 447, 450, 453,
+ 464, 471, 474, 477, 480, 491, 496, 499,
+ 502, 513, 516, 519, 522, 533, 544, 547,
+ 550, 553, 555, 566, 568, 579, 584, 587,
+ 590, 593, 604, 607, 610, 613, 624, 629,
+ 636, 677, 680, 683, 686, 689, 692, 695,
+ 698, 709, 714, 717, 728, 739, 746, 748,
+ 759, 761, 772, 775, 786, 797, 802, 805,
+ 808, 811, 822, 833, 840, 843, 846, 849,
+ 860, 863, 874, 887, 890, 902, 915, 918,
+ 921, 924, 927, 938, 941, 944, 947, 950,
+ 953, 956, 959, 962, 965, 976, 979, 982,
+ 993, 1000, 1011, 1014, 1017, 1020, 1031, 1042,
+ 1055, 1058, 1061, 1064, 1067, 1078, 1081, 1092,
+ 1095, 1106, 1109, 1120, 1123, 1126, 1129, 1140,
+ 1143, 1146, 1157, 1164, 1167, 1170, 1181, 1184,
+ 1195, 1198, 1201, 1203, 1215, 1218, 1221, 1232,
+ 1235, 1238, 1241, 1244, 1247, 1258, 1261, 1305,
+ 1310, 1317, 1359, 1377, 1385, 1397, 1405, 1408,
+ 1410, 1417, 1420, 1423, 1426, 1429, 1432, 1435,
+ 1440, 1447, 1489, 1492, 1510, 1552, 1555, 1563,
+ 1575, 1583, 1586, 1603, 1654, 1673, 1690, 1734,
+ 1739, 1751, 1763, 1816, 1835, 1850, 1861, 1874,
+ 1887, 1900, 1915, 1928, 1943, 1954, 1965, 1980,
+ 2001, 2016, 2027, 2038, 2043, 2058, 2061, 2064,
+ 2067, 2070, 2073, 2078, 2081, 2084, 2089, 2098,
+ 2118, 2162, 2167, 2186, 2239, 2258, 2273, 2284,
+ 2297, 2310, 2323, 2338, 2351, 2366, 2377, 2388,
+ 2403, 2424, 2439, 2450, 2461, 2474, 2518, 2532,
+ 2546, 2561, 2576, 2579, 2582, 2593, 2596, 2599,
+ 2602, 2613, 2616, 2627, 2632, 2644, 2656, 2671,
+ 2686, 2701, 2716, 2719, 2772, 2786, 2800, 2817,
+ 2830, 2839, 2850, 2861, 2872, 2885, 2896, 2909,
+ 2918, 2927, 2940, 2959, 2972, 2981, 2990, 2995,
+ 3000, 3011, 3016, 3023, 3025, 3027, 3036, 3048,
+ 3060, 3062, 3064, 3066, 3078, 3083, 3088, 3093,
+ 3102, 3106, 3108, 3110, 3119, 3127, 3135, 3140,
+ 3144, 3146, 3148, 3156, 3163, 3171, 3191, 3200,
+ 3202, 3212, 3224, 3234, 3237, 3244, 3258, 3266,
+ 3282, 3291, 3293, 3295, 3297, 3299, 3307, 3316,
+ 3319, 3326, 3336, 3345, 3361, 3369, 3385, 3394,
+ 3396, 3398, 3400, 3402, 3410, 3419, 3424, 3438,
+ 3441, 3444, 3449, 3454, 3460, 3466, 3481, 3484,
+ 3486, 3497, 3542, 3559, 3562, 3565, 3576, 3579,
+ 3590, 3593, 3604, 3609, 3612, 3615, 3618, 3621,
+ 3632, 3643, 3646, 3649, 3660, 3663, 3666, 3669,
+ 3680, 3683, 3686, 3689, 3700, 3707, 3710, 3713,
+ 3716, 3727, 3732, 3735, 3738, 3749, 3752, 3755,
+ 3758, 3769, 3780, 3783, 3786, 3789, 3791, 3802,
+ 3804, 3815, 3820, 3823, 3826, 3829, 3840, 3843,
+ 3846, 3849, 3860, 3863, 3866, 3869, 3872, 3875,
+ 3878, 3881, 3892, 3897, 3900, 3911, 3922, 3929,
+ 3931, 3942, 3944, 3955, 3958, 3969, 3980, 3985,
+ 3988, 3991, 3994, 4005, 4016, 4023, 4026, 4029,
+ 4032, 4043, 4046, 4057, 4070, 4073, 4085, 4098,
+ 4101, 4104, 4107, 4110, 4121, 4124, 4127, 4130,
+ 4133, 4136, 4139, 4142, 4145, 4148, 4159, 4162,
+ 4165, 4176, 4183, 4194, 4197, 4200, 4203, 4214,
+ 4225, 4238, 4241, 4244, 4247, 4250, 4261, 4264,
+ 4275, 4278, 4289, 4292, 4303, 4306, 4309, 4312,
+ 4323, 4326, 4329, 4340, 4347, 4350, 4353, 4364,
+ 4367, 4378, 4381, 4384, 4386, 4398, 4401, 4404,
+ 4415, 4418, 4421, 4424, 4427, 4430, 4441, 4444,
+ 4447, 4450, 4461, 4464, 4475, 4482, 4490, 4498,
+ 4508, 4512, 4526, 4529, 4532, 4535, 4542, 4551,
+ 4561, 4571, 4575, 4589, 4592, 4595, 4603, 4606,
+ 4613, 4615, 4617, 4619, 4626, 4628, 4630, 4637,
+ 4639, 4641, 4644, 4646, 4648, 4650, 4652, 4659,
+ 4661, 4663, 4665, 4667, 4674, 4676, 4678, 4680,
+ 4688, 4690, 4692, 4694, 4696, 4698, 4700, 4702,
+ 4704, 4706, 4708, 4710, 4712, 4714, 4716, 4723,
+ 4725, 4727, 4729, 4736, 4747, 4765, 4767, 4769,
+ 4771, 4773, 4779, 4790, 4793, 4795, 4797, 4799,
+ 4805, 4810, 4815, 4821, 4827, 4833, 4844, 4846,
+ 4851, 4857, 4863, 4878, 4880, 4882, 4884, 4887,
+ 4889, 4891, 4893, 4895, 4897, 4901, 4904, 4909,
+ 4912, 4926, 4929, 4931, 4933, 4935, 4937, 4939,
+ 4945, 4950, 4957, 4962, 4978, 4983, 4985, 4987,
+ 4989, 5002, 5007, 5018, 5020, 5022, 5024, 5026,
+ 5028, 5030, 5032, 5034, 5036, 5040, 5043, 5046,
+ 5049, 5061, 5064, 5066, 5068, 5070, 5072, 5074,
+ 5076, 5078, 5080, 5082, 5084, 5086, 5088, 5090,
+ 5092, 5103, 5105, 5107, 5109, 5111, 5114, 5116,
+ 5119, 5131, 5145, 5147, 5159, 5165, 5170, 5175,
+ 5178, 5184, 5187, 5190, 5193, 5196, 5198, 5200,
+ 5203, 5206, 5209, 5211, 5216, 5221, 5226, 5228,
+ 5231, 5234, 5237, 5240, 5245, 5248, 5251, 5254,
+ 5257, 5259, 5261, 5264, 5267, 5270, 5272, 5274,
+ 5276, 5281, 5283, 5285, 5288, 5291, 5294, 5296,
+ 5298, 5300, 5305, 5308, 5310, 5312, 5314, 5316,
+ 5321, 5323, 5325, 5330, 5333, 5336, 5339, 5342,
+ 5345, 5348, 5351, 5356, 5359, 5362, 5365, 5368,
+ 5371, 5374, 5379, 5382, 5385, 5390, 5393, 5396,
+ 5401, 5404, 5407, 5412, 5415, 5417, 5422, 5425,
+ 5428, 5432, 5438, 5441, 5444, 5447, 5450, 5452,
+ 5454, 5457, 5460, 5463, 5465, 5470, 5472, 5474,
+ 5479, 5481, 5483, 5488, 5502, 5508, 5511, 5514,
+ 5517, 5520, 5523, 5528, 5535, 5538, 5541, 5544,
+ 5547, 5550, 5555, 5560, 5563, 5568, 5571, 5574,
+ 5579, 5582, 5585, 5588, 5593, 5596, 5599, 5604,
+ 5609, 5612, 5617, 5620, 5623, 5628, 5631, 5634,
+ 5637, 5642, 5645, 5648, 5653, 5656, 5663, 5670,
+ 5675, 5682, 5689, 5702, 5709, 5717, 5725, 5733,
+ 5751, 5759, 5777, 5785, 5803, 5811, 5827, 5833,
+ 5845, 5851, 5854, 5862, 5874, 5882, 5885, 5893,
+ 5905, 5913, 5916, 5924, 5936, 5944, 5947, 5950,
+ 5953, 5958, 5965, 5975, 5980, 5983, 5990, 5997,
+ 6010, 6015, 6018, 6020, 6028, 6041, 6046, 6049,
+ 6054, 6065, 6079, 6082, 6087, 6096, 6098, 6106,
+ 6116, 6124, 6134, 6143, 6152, 6159, 6167, 6175,
+ 6185, 6193, 6203, 6212, 6221, 6228, 6237, 6239,
+ 6253, 6265, 6279, 6291, 6305, 6317, 6331, 6342,
+ 6345, 6358, 6371, 6374, 6387, 6400, 6411, 6414,
+ 6427, 6440, 6451, 6454, 6467, 6480, 6491, 6494,
+ 6501, 6504, 6512, 6520, 6523, 6526, 6529, 6536,
+ 6539, 6547, 6555, 6558, 6561, 6563, 6571, 6579,
+ 6587, 6595, 6603, 6616, 6621, 6624, 6627, 6630,
+ 6632, 6640, 6648, 6656, 6666, 6673, 6683, 6690,
+ 6700, 6707, 6720, 6725, 6728, 6731, 6734, 6737,
+ 6740, 6744, 6751, 6759, 6767, 6777, 6784, 6795,
+ 6798, 6801, 6804, 6807, 6821, 6824, 6826, 6829,
+ 6832, 6834, 6846, 6849, 6851, 6856, 6861, 6863,
+ 6871, 6881, 6888, 6896, 6904, 6914, 6918, 6932,
+ 6935, 6938, 6941, 6944, 6946, 6954, 6962, 6970,
+ 6980, 6984, 6998, 7001, 7004, 7007, 7009, 7017,
+ 7028, 7035, 7043, 7051, 7059, 7094, 7105, 7108,
+ 7111, 7126, 7129, 7168, 7175, 7178, 7181, 7188,
+ 7196, 7204, 7213, 7250, 7253, 7256, 7263, 7271,
+ 7279, 7290, 7329, 7332, 7335, 7342, 7350, 7358,
+ 7371, 7406, 7409, 7412, 7415, 7450, 7463, 7473,
+ 7480, 7488, 7496, 7504, 7512, 7520, 7528, 7536,
+ 7544, 7552, 7560, 7573, 7580, 7591, 7594, 7597,
+ 7600, 7603, 7606, 7609, 7612, 7615, 7618, 7621,
+ 7624, 7631, 7634, 7637, 7640, 7647, 7650, 7657,
+ 7668, 7671, 7678, 7683, 7686, 7689, 7692, 7695,
+ 7702, 7709, 7712, 7715, 7722, 7725, 7728, 7731,
+ 7738, 7741, 7744, 7747, 7754, 7761, 7764, 7767,
+ 7770, 7777, 7782, 7785, 7788, 7795, 7798, 7801,
+ 7804, 7811, 7818, 7821, 7824, 7827, 7829, 7836,
+ 7838, 7845, 7850, 7853, 7856, 7859, 7866, 7869,
+ 7872, 7875, 7882, 7885, 7888, 7891, 7894, 7897,
+ 7900, 7903, 7910, 7915, 7918, 7925, 7932, 7939,
+ 7941, 7948, 7950, 7957, 7960, 7967, 7974, 7979,
+ 7982, 7985, 7988, 7995, 8002, 8009, 8012, 8015,
+ 8018, 8025, 8028, 8035, 8044, 8047, 8055, 8064,
+ 8067, 8070, 8073, 8076, 8083, 8086, 8089, 8092,
+ 8095, 8098, 8101, 8104, 8107, 8110, 8117, 8120,
+ 8123, 8130, 8137, 8144, 8147, 8150, 8153, 8160,
+ 8167, 8180, 8183, 8186, 8189, 8192, 8199, 8202,
+ 8209, 8212, 8219, 8222, 8229, 8232, 8235, 8238,
+ 8245, 8248, 8251, 8258, 8265, 8268, 8271, 8278,
+ 8281, 8288, 8291, 8294, 8296, 8304, 8307, 8310,
+ 8317, 8320, 8323, 8326, 8329, 8332, 8339, 8346,
+ 8351, 8356, 8358, 8366, 8374, 8382, 8392, 8399,
+ 8410, 8413, 8416, 8419, 8422, 8427, 8430, 8432,
+ 8440, 8448, 8456, 8464, 8472, 8483, 8490, 8500,
+ 8504, 8509, 8513, 8518, 8523, 8527, 8532, 8540,
+ 8545, 8547, 8549, 8551, 8553, 8555, 8558, 8562,
+ 8572, 8575, 8578, 8581, 8583, 8591, 8599, 8607,
+ 8615, 8623, 8634, 8639, 8643, 8651, 8654, 8657,
+ 8660, 8662, 8670, 8678, 8686, 8694, 8702, 8712,
+ 8716, 8730, 8733, 8736, 8739, 8742, 8744, 8752,
+ 8760, 8766, 8771, 8774, 8776, 8784, 8792, 8800,
+ 8808, 8816, 8826, 8830, 8844, 8847, 8850, 8853,
+ 8856, 8858, 8866, 8875, 8882, 8885, 8887, 8895,
+ 8905, 8909, 8913, 8917, 8919, 8923, 8927, 8931,
+ 8935, 8941, 8944, 8948, 8952, 8954, 8958, 8962,
+ 8968, 8972, 8976, 8978, 8982, 8986, 8992, 8994,
+ 9002, 9010, 9018, 9028, 9033, 9036, 9039, 9041,
+ 9049, 9059, 9066, 9076, 9081, 9084, 9087, 9089,
+ 9097, 9110, 9115, 9120, 9123, 9139, 9165, 9215,
+ 9266, 9282, 9333, 9349, 9375, 9436, 9497, 9498,
+ 9499, 9500, 9501, 9510, 9511, 9512, 9513, 9527,
+ 9528, 9543, 9546, 9547, 9592, 9595, 9596, 9610,
+ 9613, 9623, 9624, 9638, 9641, 9644, 9645, 9646,
+ 9664, 9667, 9668, 9669, 9670, 9671, 9672, 9673,
+ 9674, 9675, 9676, 9690, 9693, 9694, 9695, 9706,
+ 9709, 9721, 9724, 9736, 9739, 9751, 9754, 9755,
+ 9756, 9757, 9758, 9772, 9775, 9776, 9790, 9793,
+ 9794, 9808, 9811, 9812, 9827, 9830, 9841, 9844,
+ 9883, 9890, 9891, 9892, 9893, 9894, 9895, 9896,
+ 9897, 9911, 9914, 9915, 9916, 9930, 9933, 9934,
+ 9935, 9936, 9937, 9938, 9939
+};
+
+static const short _zone_scanner_indicies[] = {
+ 1, 1, 2, 3, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 4, 0, 27, 27, 28, 29,
+ 31, 32, 33, 34, 35, 31, 32, 33,
+ 34, 35, 36, 37, 30, 26, 39, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 38, 62,
+ 62, 63, 64, 65, 66, 67, 65, 66,
+ 67, 68, 69, 70, 70, 71, 72, 61,
+ 74, 74, 76, 77, 78, 79, 80, 73,
+ 73, 81, 82, 73, 73, 73, 75, 84,
+ 84, 85, 86, 87, 88, 87, 88, 83,
+ 90, 90, 91, 92, 93, 94, 93, 94,
+ 89, 96, 95, 98, 97, 100, 99, 75,
+ 101, 101, 102, 103, 78, 87, 88, 87,
+ 104, 105, 106, 87, 107, 73, 73, 73,
+ 83, 108, 108, 109, 110, 78, 93, 94,
+ 93, 111, 112, 113, 93, 114, 73, 73,
+ 73, 83, 96, 116, 95, 115, 83, 117,
+ 117, 119, 120, 73, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 78, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 22,
+ 23, 139, 140, 141, 142, 143, 144, 73,
+ 73, 121, 73, 118, 146, 146, 147, 148,
+ 31, 32, 33, 34, 35, 31, 32, 33,
+ 34, 35, 87, 88, 149, 150, 30, 145,
+ 152, 152, 153, 154, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 93, 94,
+ 155, 156, 151, 157, 158, 159, 160, 161,
+ 157, 158, 159, 160, 161, 38, 162, 162,
+ 38, 163, 163, 164, 165, 166, 167, 168,
+ 168, 169, 170, 61, 84, 87, 84, 85,
+ 86, 88, 171, 87, 171, 172, 173, 174,
+ 99, 83, 90, 93, 90, 91, 92, 94,
+ 175, 93, 175, 176, 177, 178, 99, 83,
+ 179, 180, 179, 180, 38, 181, 181, 38,
+ 182, 182, 38, 183, 183, 38, 184, 184,
+ 38, 185, 185, 186, 187, 188, 189, 190,
+ 190, 191, 192, 61, 193, 193, 194, 195,
+ 196, 197, 198, 198, 199, 200, 61, 201,
+ 201, 38, 202, 202, 38, 203, 203, 204,
+ 205, 206, 207, 208, 208, 209, 210, 61,
+ 211, 211, 38, 212, 212, 38, 213, 213,
+ 38, 214, 214, 215, 216, 217, 218, 219,
+ 219, 220, 221, 61, 222, 222, 38, 223,
+ 223, 38, 224, 224, 38, 225, 225, 226,
+ 227, 228, 229, 230, 230, 231, 232, 61,
+ 233, 234, 235, 233, 234, 235, 38, 236,
+ 236, 38, 237, 237, 38, 238, 238, 38,
+ 239, 239, 240, 241, 242, 243, 244, 244,
+ 245, 246, 61, 247, 248, 247, 248, 38,
+ 249, 249, 38, 250, 250, 38, 251, 251,
+ 252, 253, 254, 255, 256, 256, 257, 258,
+ 61, 259, 259, 38, 260, 260, 38, 261,
+ 261, 38, 262, 262, 263, 264, 265, 266,
+ 267, 267, 268, 269, 61, 270, 270, 271,
+ 272, 273, 274, 275, 275, 276, 277, 61,
+ 278, 278, 38, 279, 279, 38, 280, 281,
+ 38, 282, 38, 283, 283, 284, 285, 286,
+ 287, 288, 288, 289, 290, 61, 291, 38,
+ 292, 292, 293, 294, 295, 296, 297, 297,
+ 298, 299, 61, 300, 301, 300, 301, 38,
+ 302, 302, 38, 303, 303, 38, 304, 304,
+ 38, 305, 305, 306, 307, 308, 309, 310,
+ 310, 311, 312, 61, 313, 313, 38, 314,
+ 314, 38, 315, 315, 38, 316, 316, 317,
+ 318, 319, 320, 321, 321, 322, 323, 61,
+ 324, 325, 324, 325, 38, 326, 326, 327,
+ 328, 329, 330, 38, 331, 331, 332, 333,
+ 334, 335, 336, 337, 338, 339, 340, 341,
+ 342, 343, 344, 345, 346, 347, 348, 349,
+ 350, 334, 335, 336, 337, 338, 339, 340,
+ 341, 342, 343, 344, 345, 346, 347, 348,
+ 349, 350, 351, 352, 38, 325, 325, 38,
+ 353, 353, 38, 354, 354, 38, 355, 355,
+ 38, 356, 356, 38, 357, 357, 38, 358,
+ 358, 38, 359, 359, 360, 361, 362, 363,
+ 364, 364, 365, 366, 61, 367, 368, 367,
+ 368, 38, 369, 369, 38, 370, 370, 371,
+ 372, 373, 374, 375, 375, 376, 377, 61,
+ 378, 378, 379, 380, 381, 382, 383, 383,
+ 384, 385, 61, 386, 387, 388, 389, 388,
+ 389, 38, 390, 38, 391, 391, 392, 393,
+ 394, 395, 396, 396, 397, 398, 61, 399,
+ 38, 400, 400, 401, 402, 403, 404, 405,
+ 405, 406, 407, 61, 408, 408, 38, 409,
+ 409, 410, 411, 412, 413, 414, 414, 415,
+ 416, 61, 417, 417, 418, 419, 420, 421,
+ 422, 422, 423, 424, 61, 425, 426, 425,
+ 426, 38, 427, 427, 38, 428, 428, 38,
+ 429, 429, 38, 430, 430, 431, 432, 433,
+ 434, 435, 435, 436, 437, 61, 438, 438,
+ 439, 440, 441, 442, 443, 443, 444, 445,
+ 61, 446, 447, 448, 446, 447, 448, 38,
+ 449, 449, 38, 450, 450, 38, 451, 451,
+ 38, 452, 452, 453, 454, 455, 456, 457,
+ 457, 458, 459, 61, 460, 460, 38, 461,
+ 461, 462, 463, 464, 465, 466, 466, 467,
+ 468, 61, 469, 469, 470, 471, 472, 472,
+ 473, 474, 475, 475, 476, 477, 61, 478,
+ 478, 38, 479, 479, 480, 481, 482, 483,
+ 484, 485, 485, 486, 487, 61, 488, 488,
+ 489, 490, 491, 491, 492, 493, 494, 494,
+ 495, 496, 61, 497, 497, 38, 498, 498,
+ 38, 499, 499, 38, 500, 500, 38, 501,
+ 501, 502, 503, 504, 505, 506, 506, 507,
+ 508, 61, 509, 509, 38, 510, 510, 38,
+ 511, 511, 38, 512, 512, 38, 513, 513,
+ 38, 514, 514, 38, 515, 515, 38, 516,
+ 516, 38, 517, 517, 38, 518, 518, 519,
+ 520, 521, 522, 523, 523, 524, 525, 61,
+ 526, 526, 38, 527, 527, 38, 528, 528,
+ 529, 530, 531, 532, 533, 533, 534, 535,
+ 61, 536, 537, 538, 536, 537, 538, 38,
+ 539, 539, 540, 541, 542, 543, 544, 544,
+ 545, 546, 61, 547, 547, 38, 548, 548,
+ 38, 549, 549, 38, 550, 550, 551, 552,
+ 553, 554, 555, 555, 556, 557, 61, 558,
+ 558, 559, 560, 561, 562, 563, 563, 564,
+ 565, 61, 566, 567, 568, 569, 570, 571,
+ 566, 567, 568, 569, 570, 571, 38, 572,
+ 572, 38, 573, 573, 38, 574, 574, 38,
+ 575, 575, 38, 576, 576, 577, 578, 579,
+ 580, 581, 581, 582, 583, 61, 584, 584,
+ 38, 585, 585, 586, 587, 588, 589, 590,
+ 590, 591, 592, 61, 593, 593, 38, 594,
+ 594, 595, 596, 597, 598, 599, 599, 600,
+ 601, 61, 602, 602, 38, 603, 603, 604,
+ 605, 606, 607, 608, 608, 609, 610, 61,
+ 611, 611, 38, 612, 612, 38, 613, 613,
+ 38, 614, 614, 615, 616, 617, 618, 619,
+ 619, 620, 621, 61, 622, 622, 38, 623,
+ 623, 38, 624, 624, 625, 626, 627, 628,
+ 629, 629, 630, 631, 61, 632, 633, 634,
+ 632, 633, 634, 38, 635, 635, 38, 636,
+ 636, 38, 637, 637, 638, 639, 640, 641,
+ 642, 642, 643, 644, 61, 645, 645, 38,
+ 646, 646, 647, 648, 649, 650, 651, 651,
+ 652, 653, 61, 654, 654, 38, 655, 655,
+ 38, 657, 656, 659, 659, 660, 661, 663,
+ 664, 665, 665, 666, 667, 662, 658, 668,
+ 668, 38, 669, 669, 38, 670, 670, 671,
+ 672, 673, 674, 675, 675, 676, 677, 61,
+ 678, 678, 38, 679, 679, 38, 680, 680,
+ 38, 681, 681, 38, 682, 682, 38, 683,
+ 683, 684, 685, 686, 687, 688, 688, 689,
+ 690, 61, 692, 691, 38, 693, 693, 694,
+ 695, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 696, 697, 4,
+ 0, 698, 325, 698, 325, 38, 699, 699,
+ 700, 701, 702, 703, 38, 704, 704, 705,
+ 706, 708, 709, 710, 711, 712, 713, 714,
+ 715, 716, 717, 718, 719, 720, 721, 722,
+ 723, 724, 708, 709, 710, 711, 712, 713,
+ 714, 715, 716, 717, 718, 719, 720, 721,
+ 722, 723, 724, 725, 726, 707, 656, 727,
+ 727, 728, 729, 731, 732, 733, 734, 735,
+ 731, 732, 733, 734, 735, 736, 737, 730,
+ 26, 727, 727, 728, 729, 736, 737, 738,
+ 26, 740, 741, 742, 743, 744, 740, 741,
+ 742, 743, 744, 739, 26, 745, 745, 746,
+ 747, 749, 750, 748, 26, 752, 751, 38,
+ 754, 753, 756, 757, 758, 756, 757, 758,
+ 755, 759, 759, 755, 760, 760, 755, 761,
+ 761, 755, 762, 762, 755, 763, 763, 755,
+ 764, 764, 755, 765, 765, 765, 765, 755,
+ 767, 767, 768, 769, 770, 771, 766, 772,
+ 772, 773, 774, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 775, 776, 4,
+ 656, 779, 777, 778, 781, 781, 782, 783,
+ 31, 32, 33, 34, 35, 31, 32, 33,
+ 34, 35, 784, 785, 30, 780, 786, 786,
+ 787, 788, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 789, 790, 4, 656,
+ 792, 791, 38, 27, 27, 28, 29, 36,
+ 37, 793, 26, 795, 796, 797, 798, 799,
+ 795, 796, 797, 798, 799, 794, 26, 800,
+ 800, 801, 802, 804, 805, 803, 26, 807,
+ 806, 38, 809, 809, 810, 811, 65, 66,
+ 67, 65, 66, 67, 812, 813, 70, 70,
+ 814, 815, 808, 816, 816, 817, 818, 73,
+ 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 78, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 819, 820, 73, 73, 821,
+ 822, 73, 73, 121, 73, 658, 824, 824,
+ 825, 826, 65, 66, 67, 65, 66, 67,
+ 87, 88, 827, 828, 829, 830, 831, 832,
+ 823, 833, 833, 834, 835, 78, 93, 94,
+ 836, 111, 112, 113, 837, 114, 73, 73,
+ 73, 83, 838, 838, 839, 840, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21,
+ 93, 94, 841, 842, 4, 0, 96, 844,
+ 95, 843, 89, 845, 845, 846, 847, 848,
+ 849, 848, 849, 95, 95, 95, 83, 850,
+ 850, 851, 852, 96, 853, 96, 853, 95,
+ 95, 95, 89, 854, 854, 855, 856, 73,
+ 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 134, 135, 136, 137,
+ 138, 78, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 93, 94, 841, 857, 112,
+ 113, 841, 858, 73, 73, 121, 73, 118,
+ 84, 84, 85, 86, 157, 158, 159, 160,
+ 161, 157, 158, 159, 160, 161, 87, 88,
+ 87, 88, 823, 84, 84, 85, 86, 233,
+ 234, 235, 233, 234, 235, 87, 88, 87,
+ 88, 823, 84, 84, 85, 86, 278, 278,
+ 87, 88, 87, 88, 823, 84, 84, 85,
+ 86, 300, 301, 300, 301, 87, 88, 87,
+ 88, 823, 84, 84, 85, 86, 698, 325,
+ 698, 325, 87, 88, 87, 88, 823, 84,
+ 84, 85, 86, 367, 368, 367, 368, 87,
+ 88, 87, 88, 823, 84, 84, 85, 86,
+ 386, 387, 388, 389, 388, 389, 87, 88,
+ 87, 88, 823, 84, 84, 85, 86, 425,
+ 426, 425, 426, 87, 88, 87, 88, 823,
+ 84, 84, 85, 86, 446, 447, 448, 446,
+ 447, 448, 87, 88, 87, 88, 823, 84,
+ 84, 85, 86, 509, 509, 87, 88, 87,
+ 88, 823, 84, 84, 85, 86, 526, 526,
+ 87, 88, 87, 88, 823, 84, 84, 85,
+ 86, 536, 537, 538, 536, 537, 538, 87,
+ 88, 87, 88, 823, 84, 84, 85, 86,
+ 566, 567, 568, 569, 570, 571, 566, 567,
+ 568, 569, 570, 571, 87, 88, 87, 88,
+ 823, 84, 84, 85, 86, 632, 633, 634,
+ 632, 633, 634, 87, 88, 87, 88, 823,
+ 84, 84, 85, 86, 668, 668, 87, 88,
+ 87, 88, 823, 84, 84, 85, 86, 678,
+ 678, 87, 88, 87, 88, 823, 96, 860,
+ 95, 859, 83, 84, 84, 85, 86, 756,
+ 757, 758, 756, 757, 758, 87, 88, 87,
+ 88, 861, 862, 862, 755, 863, 863, 755,
+ 864, 864, 755, 865, 865, 755, 866, 866,
+ 755, 867, 867, 867, 867, 755, 868, 868,
+ 755, 869, 869, 755, 870, 870, 870, 870,
+ 755, 872, 872, 873, 874, 87, 88, 875,
+ 876, 871, 878, 878, 879, 880, 31, 32,
+ 33, 34, 35, 31, 32, 33, 34, 35,
+ 87, 88, 881, 882, 30, 877, 883, 883,
+ 884, 885, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 93, 94, 886, 887,
+ 4, 0, 96, 889, 95, 888, 151, 891,
+ 891, 892, 893, 65, 66, 67, 65, 66,
+ 67, 87, 88, 894, 895, 829, 830, 896,
+ 897, 890, 898, 898, 899, 900, 73, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 134, 135, 136, 137, 138,
+ 78, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 93, 94, 901, 857, 112, 113,
+ 902, 858, 73, 73, 121, 73, 118, 872,
+ 872, 873, 874, 157, 158, 159, 160, 161,
+ 157, 158, 159, 160, 161, 87, 88, 875,
+ 876, 890, 872, 872, 873, 874, 233, 234,
+ 235, 233, 234, 235, 87, 88, 875, 876,
+ 890, 872, 872, 873, 874, 278, 278, 87,
+ 88, 875, 876, 890, 872, 872, 873, 874,
+ 300, 301, 300, 301, 87, 88, 875, 876,
+ 890, 872, 872, 873, 874, 698, 325, 698,
+ 325, 87, 88, 875, 876, 890, 872, 872,
+ 873, 874, 367, 368, 367, 368, 87, 88,
+ 875, 876, 890, 872, 872, 873, 874, 386,
+ 387, 388, 389, 388, 389, 87, 88, 875,
+ 876, 890, 872, 872, 873, 874, 425, 426,
+ 425, 426, 87, 88, 875, 876, 890, 872,
+ 872, 873, 874, 446, 447, 448, 446, 447,
+ 448, 87, 88, 875, 876, 890, 872, 872,
+ 873, 874, 509, 509, 87, 88, 875, 876,
+ 890, 872, 872, 873, 874, 526, 526, 87,
+ 88, 875, 876, 890, 872, 872, 873, 874,
+ 536, 537, 538, 536, 537, 538, 87, 88,
+ 875, 876, 890, 872, 872, 873, 874, 566,
+ 567, 568, 569, 570, 571, 566, 567, 568,
+ 569, 570, 571, 87, 88, 875, 876, 890,
+ 872, 872, 873, 874, 632, 633, 634, 632,
+ 633, 634, 87, 88, 875, 876, 890, 872,
+ 872, 873, 874, 668, 668, 87, 88, 875,
+ 876, 890, 872, 872, 873, 874, 678, 678,
+ 87, 88, 875, 876, 890, 903, 903, 98,
+ 905, 906, 97, 97, 907, 908, 97, 97,
+ 97, 904, 909, 909, 910, 911, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21,
+ 87, 88, 912, 913, 4, 118, 84, 87,
+ 84, 85, 86, 88, 914, 915, 914, 916,
+ 917, 918, 777, 83, 90, 93, 90, 91,
+ 92, 94, 919, 844, 919, 920, 921, 922,
+ 777, 89, 845, 848, 845, 846, 847, 849,
+ 923, 915, 923, 924, 925, 926, 95, 859,
+ 83, 850, 96, 850, 851, 852, 853, 927,
+ 844, 927, 928, 929, 930, 95, 859, 83,
+ 931, 931, 38, 932, 932, 38, 933, 933,
+ 934, 935, 936, 937, 938, 938, 939, 940,
+ 61, 941, 941, 38, 942, 942, 38, 943,
+ 943, 38, 944, 944, 945, 946, 947, 948,
+ 949, 949, 950, 951, 61, 952, 952, 38,
+ 953, 953, 954, 955, 956, 957, 958, 958,
+ 959, 960, 61, 754, 962, 753, 961, 83,
+ 963, 963, 964, 965, 966, 849, 966, 849,
+ 753, 753, 753, 83, 967, 967, 968, 969,
+ 970, 853, 970, 853, 753, 753, 753, 89,
+ 963, 966, 963, 964, 965, 849, 971, 915,
+ 971, 972, 973, 926, 753, 961, 83, 967,
+ 970, 967, 968, 969, 853, 974, 844, 974,
+ 975, 976, 930, 753, 961, 83, 845, 848,
+ 845, 846, 847, 849, 977, 848, 977, 978,
+ 979, 174, 95, 115, 83, 850, 96, 850,
+ 851, 852, 853, 980, 96, 980, 981, 982,
+ 178, 95, 115, 83, 984, 983, 75, 985,
+ 985, 986, 987, 73, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133,
+ 134, 135, 136, 137, 138, 78, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131,
+ 132, 133, 134, 135, 136, 137, 138, 87,
+ 88, 912, 988, 105, 106, 912, 989, 73,
+ 73, 121, 73, 118, 84, 87, 84, 85,
+ 86, 88, 990, 915, 990, 991, 992, 926,
+ 983, 83, 90, 93, 90, 91, 92, 94,
+ 993, 844, 993, 994, 995, 930, 983, 83,
+ 767, 767, 768, 769, 157, 158, 159, 160,
+ 161, 157, 158, 159, 160, 161, 770, 771,
+ 996, 767, 767, 768, 769, 233, 234, 235,
+ 233, 234, 235, 770, 771, 996, 767, 767,
+ 768, 769, 278, 278, 770, 771, 996, 767,
+ 767, 768, 769, 300, 301, 300, 301, 770,
+ 771, 996, 767, 767, 768, 769, 698, 325,
+ 698, 325, 770, 771, 996, 767, 767, 768,
+ 769, 367, 368, 367, 368, 770, 771, 996,
+ 767, 767, 768, 769, 386, 387, 388, 389,
+ 388, 389, 770, 771, 996, 767, 767, 768,
+ 769, 425, 426, 425, 426, 770, 771, 996,
+ 767, 767, 768, 769, 446, 447, 448, 446,
+ 447, 448, 770, 771, 996, 767, 767, 768,
+ 769, 509, 509, 770, 771, 996, 767, 767,
+ 768, 769, 526, 526, 770, 771, 996, 767,
+ 767, 768, 769, 536, 537, 538, 536, 537,
+ 538, 770, 771, 996, 767, 767, 768, 769,
+ 566, 567, 568, 569, 570, 571, 566, 567,
+ 568, 569, 570, 571, 770, 771, 996, 767,
+ 767, 768, 769, 632, 633, 634, 632, 633,
+ 634, 770, 771, 996, 767, 767, 768, 769,
+ 668, 668, 770, 771, 996, 767, 767, 768,
+ 769, 678, 678, 770, 771, 996, 754, 998,
+ 753, 997, 89, 754, 1000, 753, 999, 151,
+ 767, 767, 768, 769, 324, 325, 324, 325,
+ 770, 771, 996, 96, 1002, 95, 1001, 151,
+ 1004, 1005, 1006, 1003, 1003, 1003, 778, 1008,
+ 1007, 1010, 1009, 1012, 1013, 1014, 1015, 1012,
+ 1012, 1012, 1012, 1011, 1016, 1017, 1018, 1016,
+ 1019, 1017, 1016, 1016, 1017, 1017, 1017, 1011,
+ 1020, 1021, 1021, 1020, 1022, 1021, 1020, 1020,
+ 1021, 1021, 1021, 1011, 1023, 1017, 1025, 1024,
+ 1026, 1024, 1027, 1028, 1029, 1027, 1030, 1028,
+ 1027, 1027, 1028, 1028, 1028, 1024, 1020, 1020,
+ 1020, 1020, 1011, 1031, 1031, 1031, 1031, 1011,
+ 1034, 1035, 1033, 1033, 1032, 1036, 1037, 1036,
+ 1039, 1036, 1037, 1038, 1037, 1032, 1033, 1041,
+ 1033, 1040, 1042, 1040, 1043, 1040, 1044, 1045,
+ 1044, 1047, 1044, 1045, 1046, 1045, 1040, 1048,
+ 1048, 1050, 1048, 1051, 1048, 1049, 1032, 1052,
+ 1052, 1054, 1052, 1055, 1052, 1053, 1032, 1057,
+ 1057, 1057, 1057, 1056, 1049, 1058, 1049, 1040,
+ 1059, 1040, 1060, 1040, 1061, 1061, 1063, 1061,
+ 1064, 1061, 1062, 1040, 1066, 1066, 1067, 1068,
+ 1069, 1070, 1065, 1066, 1066, 1067, 1068, 1069,
+ 1070, 1072, 1071, 1074, 1074, 1075, 1076, 1078,
+ 1079, 1080, 1081, 1082, 1078, 1079, 1080, 1081,
+ 1082, 1083, 1084, 1083, 1084, 1077, 1073, 1086,
+ 1086, 1087, 1088, 1089, 1090, 1089, 1090, 1085,
+ 1092, 1091, 1074, 1074, 1075, 1076, 1083, 1084,
+ 1083, 1084, 1093, 1073, 1096, 1097, 1098, 1099,
+ 1100, 1096, 1097, 1098, 1099, 1100, 1095, 1094,
+ 1101, 1101, 1102, 1103, 1105, 1106, 1105, 1106,
+ 1104, 1073, 1108, 1107, 1065, 1110, 1110, 1111,
+ 1112, 1113, 1114, 1109, 1110, 1110, 1111, 1112,
+ 1115, 1116, 1117, 1115, 1113, 1114, 1115, 1115,
+ 1115, 1109, 1118, 1119, 1120, 1118, 1118, 1118,
+ 1118, 1109, 1122, 1122, 1123, 1124, 1125, 1125,
+ 1126, 1125, 1127, 1128, 1127, 1128, 1125, 1125,
+ 1125, 1121, 1129, 1129, 1130, 1131, 1132, 1133,
+ 1132, 1133, 1121, 1135, 1134, 1136, 1118, 1138,
+ 1137, 1139, 1137, 1140, 1141, 1142, 1140, 1140,
+ 1140, 1140, 1137, 1122, 1122, 1123, 1124, 1127,
+ 1128, 1127, 1128, 1121, 1144, 1143, 1109, 1145,
+ 1145, 1146, 1147, 1148, 1149, 778, 1145, 1145,
+ 1146, 1147, 1148, 1149, 1150, 1150, 1150, 1151,
+ 1153, 1153, 1154, 1155, 1156, 1157, 1156, 1158,
+ 1152, 1160, 1160, 1161, 1162, 1163, 1164, 1165,
+ 1163, 1166, 1167, 1166, 1168, 1163, 1163, 1163,
+ 1159, 1170, 1171, 1172, 1170, 1170, 1170, 1170,
+ 1169, 1173, 1173, 1174, 1175, 1176, 1176, 1177,
+ 1176, 1178, 1179, 1178, 1179, 1176, 1176, 1176,
+ 1159, 1180, 1180, 1181, 1182, 1166, 1167, 1166,
+ 1167, 89, 1184, 1183, 1185, 1170, 1187, 1186,
+ 1188, 1186, 1189, 1190, 1191, 1189, 1189, 1189,
+ 1189, 1186, 1173, 1173, 1174, 1175, 1178, 1179,
+ 1178, 1179, 1159, 1184, 1193, 1183, 1192, 89,
+ 1194, 1194, 1195, 1196, 1163, 1164, 1165, 1163,
+ 1197, 1198, 1163, 1163, 1163, 1169, 1200, 1199,
+ 778, 1202, 1201, 778, 1204, 1204, 1204, 1204,
+ 1203, 1205, 1205, 1205, 1205, 1203, 1206, 1207,
+ 1206, 1206, 1206, 1203, 1208, 1209, 1208, 1208,
+ 1208, 1203, 1209, 1209, 1210, 1211, 1204, 1212,
+ 1213, 1214, 1214, 1215, 1216, 1204, 1204, 1204,
+ 1203, 1218, 1217, 1203, 1209, 1203, 1220, 1220,
+ 1221, 1222, 1223, 1224, 1225, 1225, 1226, 1227,
+ 1219, 1228, 1228, 1229, 1230, 1231, 1232, 1233,
+ 1234, 1235, 1236, 1237, 1238, 1239, 1240, 1241,
+ 1242, 1243, 1244, 1245, 1246, 1247, 1231, 1232,
+ 1233, 1234, 1235, 1236, 1237, 1238, 1239, 1240,
+ 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248,
+ 1249, 1250, 1250, 1251, 1252, 1219, 1253, 1253,
+ 1254, 1255, 1256, 1257, 1258, 1256, 1257, 1258,
+ 1259, 1260, 1261, 1261, 1262, 1263, 1219, 1264,
+ 1264, 1219, 1265, 1265, 1219, 1266, 1266, 1267,
+ 1268, 1269, 1270, 1271, 1271, 1272, 1273, 1219,
+ 1275, 1274, 1219, 1276, 1277, 1278, 1279, 1280,
+ 1276, 1277, 1278, 1279, 1280, 1219, 1281, 1281,
+ 1219, 1282, 1282, 1283, 1284, 1285, 1286, 1287,
+ 1287, 1288, 1289, 1219, 1290, 1291, 1290, 1291,
+ 1219, 1292, 1292, 1219, 1293, 1293, 1219, 1294,
+ 1294, 1219, 1295, 1295, 1219, 1296, 1296, 1297,
+ 1298, 1299, 1300, 1301, 1301, 1302, 1303, 1219,
+ 1304, 1304, 1305, 1306, 1307, 1308, 1309, 1309,
+ 1310, 1311, 1219, 1312, 1312, 1219, 1313, 1313,
+ 1219, 1314, 1314, 1315, 1316, 1317, 1318, 1319,
+ 1319, 1320, 1321, 1219, 1322, 1322, 1219, 1323,
+ 1323, 1219, 1324, 1324, 1219, 1325, 1325, 1326,
+ 1327, 1328, 1329, 1330, 1330, 1331, 1332, 1219,
+ 1333, 1333, 1219, 1334, 1334, 1219, 1335, 1335,
+ 1219, 1336, 1336, 1337, 1338, 1339, 1340, 1341,
+ 1341, 1342, 1343, 1219, 1344, 1345, 1346, 1344,
+ 1345, 1346, 1219, 1347, 1347, 1219, 1348, 1348,
+ 1219, 1349, 1349, 1219, 1350, 1350, 1351, 1352,
+ 1353, 1354, 1355, 1355, 1356, 1357, 1219, 1358,
+ 1359, 1358, 1359, 1219, 1360, 1360, 1219, 1361,
+ 1361, 1219, 1362, 1362, 1363, 1364, 1365, 1366,
+ 1367, 1367, 1368, 1369, 1219, 1370, 1370, 1219,
+ 1371, 1371, 1219, 1372, 1372, 1219, 1373, 1373,
+ 1374, 1375, 1376, 1377, 1378, 1378, 1379, 1380,
+ 1219, 1381, 1381, 1382, 1383, 1384, 1385, 1386,
+ 1386, 1387, 1388, 1219, 1389, 1389, 1219, 1390,
+ 1390, 1219, 1391, 1392, 1219, 1393, 1219, 1394,
+ 1394, 1395, 1396, 1397, 1398, 1399, 1399, 1400,
+ 1401, 1219, 1402, 1219, 1403, 1403, 1404, 1405,
+ 1406, 1407, 1408, 1408, 1409, 1410, 1219, 1411,
+ 1412, 1411, 1412, 1219, 1413, 1413, 1219, 1414,
+ 1414, 1219, 1415, 1415, 1219, 1416, 1416, 1417,
+ 1418, 1419, 1420, 1421, 1421, 1422, 1423, 1219,
+ 1424, 1424, 1219, 1425, 1425, 1219, 1426, 1426,
+ 1219, 1427, 1427, 1428, 1429, 1430, 1431, 1432,
+ 1432, 1433, 1434, 1219, 1435, 1435, 1219, 1436,
+ 1436, 1219, 1437, 1437, 1219, 1438, 1438, 1219,
+ 1439, 1439, 1219, 1440, 1440, 1219, 1441, 1441,
+ 1219, 1442, 1442, 1443, 1444, 1445, 1446, 1447,
+ 1447, 1448, 1449, 1219, 1450, 1451, 1450, 1451,
+ 1219, 1452, 1452, 1219, 1453, 1453, 1454, 1455,
+ 1456, 1457, 1458, 1458, 1459, 1460, 1219, 1461,
+ 1461, 1462, 1463, 1464, 1465, 1466, 1466, 1467,
+ 1468, 1219, 1469, 1470, 1471, 1472, 1471, 1472,
+ 1219, 1473, 1219, 1474, 1474, 1475, 1476, 1477,
+ 1478, 1479, 1479, 1480, 1481, 1219, 1482, 1219,
+ 1483, 1483, 1484, 1485, 1486, 1487, 1488, 1488,
+ 1489, 1490, 1219, 1491, 1491, 1219, 1492, 1492,
+ 1493, 1494, 1495, 1496, 1497, 1497, 1498, 1499,
+ 1219, 1500, 1500, 1501, 1502, 1503, 1504, 1505,
+ 1505, 1506, 1507, 1219, 1508, 1509, 1508, 1509,
+ 1219, 1510, 1510, 1219, 1511, 1511, 1219, 1512,
+ 1512, 1219, 1513, 1513, 1514, 1515, 1516, 1517,
+ 1518, 1518, 1519, 1520, 1219, 1521, 1521, 1522,
+ 1523, 1524, 1525, 1526, 1526, 1527, 1528, 1219,
+ 1529, 1530, 1531, 1529, 1530, 1531, 1219, 1532,
+ 1532, 1219, 1533, 1533, 1219, 1534, 1534, 1219,
+ 1535, 1535, 1536, 1537, 1538, 1539, 1540, 1540,
+ 1541, 1542, 1219, 1543, 1543, 1219, 1544, 1544,
+ 1545, 1546, 1547, 1548, 1549, 1549, 1550, 1551,
+ 1219, 1552, 1552, 1553, 1554, 1555, 1555, 1556,
+ 1557, 1558, 1558, 1559, 1560, 1219, 1561, 1561,
+ 1219, 1562, 1562, 1563, 1564, 1565, 1566, 1567,
+ 1568, 1568, 1569, 1570, 1219, 1571, 1571, 1572,
+ 1573, 1574, 1574, 1575, 1576, 1577, 1577, 1578,
+ 1579, 1219, 1580, 1580, 1219, 1581, 1581, 1219,
+ 1582, 1582, 1219, 1583, 1583, 1219, 1584, 1584,
+ 1585, 1586, 1587, 1588, 1589, 1589, 1590, 1591,
+ 1219, 1592, 1592, 1219, 1593, 1593, 1219, 1594,
+ 1594, 1219, 1595, 1595, 1219, 1596, 1596, 1219,
+ 1597, 1597, 1219, 1598, 1598, 1219, 1599, 1599,
+ 1219, 1600, 1600, 1219, 1601, 1601, 1602, 1603,
+ 1604, 1605, 1606, 1606, 1607, 1608, 1219, 1609,
+ 1609, 1219, 1610, 1610, 1219, 1611, 1611, 1612,
+ 1613, 1614, 1615, 1616, 1616, 1617, 1618, 1219,
+ 1619, 1620, 1621, 1619, 1620, 1621, 1219, 1622,
+ 1622, 1623, 1624, 1625, 1626, 1627, 1627, 1628,
+ 1629, 1219, 1630, 1630, 1219, 1631, 1631, 1219,
+ 1632, 1632, 1219, 1633, 1633, 1634, 1635, 1636,
+ 1637, 1638, 1638, 1639, 1640, 1219, 1641, 1641,
+ 1642, 1643, 1644, 1645, 1646, 1646, 1647, 1648,
+ 1219, 1649, 1650, 1651, 1652, 1653, 1654, 1649,
+ 1650, 1651, 1652, 1653, 1654, 1219, 1655, 1655,
+ 1219, 1656, 1656, 1219, 1657, 1657, 1219, 1658,
+ 1658, 1219, 1659, 1659, 1660, 1661, 1662, 1663,
+ 1664, 1664, 1665, 1666, 1219, 1667, 1667, 1219,
+ 1668, 1668, 1669, 1670, 1671, 1672, 1673, 1673,
+ 1674, 1675, 1219, 1676, 1676, 1219, 1677, 1677,
+ 1678, 1679, 1680, 1681, 1682, 1682, 1683, 1684,
+ 1219, 1685, 1685, 1219, 1686, 1686, 1687, 1688,
+ 1689, 1690, 1691, 1691, 1692, 1693, 1219, 1694,
+ 1694, 1219, 1695, 1695, 1219, 1696, 1696, 1219,
+ 1697, 1697, 1698, 1699, 1700, 1701, 1702, 1702,
+ 1703, 1704, 1219, 1705, 1705, 1219, 1706, 1706,
+ 1219, 1707, 1707, 1708, 1709, 1710, 1711, 1712,
+ 1712, 1713, 1714, 1219, 1715, 1716, 1717, 1715,
+ 1716, 1717, 1219, 1718, 1718, 1219, 1719, 1719,
+ 1219, 1720, 1720, 1721, 1722, 1723, 1724, 1725,
+ 1725, 1726, 1727, 1219, 1728, 1728, 1219, 1729,
+ 1729, 1730, 1731, 1732, 1733, 1734, 1734, 1735,
+ 1736, 1219, 1737, 1737, 1219, 1738, 1738, 1219,
+ 1739, 1219, 1740, 1740, 1741, 1742, 1744, 1745,
+ 1746, 1746, 1747, 1748, 1743, 1219, 1749, 1749,
+ 1219, 1750, 1750, 1219, 1751, 1751, 1752, 1753,
+ 1754, 1755, 1756, 1756, 1757, 1758, 1219, 1759,
+ 1759, 1219, 1760, 1760, 1219, 1761, 1761, 1219,
+ 1762, 1762, 1219, 1763, 1763, 1219, 1764, 1764,
+ 1765, 1766, 1767, 1768, 1769, 1769, 1770, 1771,
+ 1219, 1772, 1772, 1219, 1773, 1773, 1219, 1774,
+ 1774, 1219, 1775, 1775, 1776, 1777, 1778, 1779,
+ 1780, 1780, 1781, 1782, 1219, 1783, 1783, 1219,
+ 1784, 1784, 1785, 1786, 1787, 1788, 1789, 1789,
+ 1790, 1791, 1219, 1793, 1793, 1794, 1795, 1796,
+ 1797, 1792, 1793, 1793, 1794, 1795, 1796, 1797,
+ 1799, 1798, 1800, 1800, 1801, 1802, 1804, 1805,
+ 1803, 1798, 1807, 1807, 1808, 1809, 1811, 1812,
+ 1810, 1810, 1810, 1806, 1813, 1813, 1813, 1806,
+ 1814, 1814, 1815, 1816, 1817, 1818, 1819, 1819,
+ 1820, 1821, 1810, 1810, 1810, 1806, 1823, 1822,
+ 1806, 1825, 1824, 1792, 1827, 1826, 1792, 1828,
+ 1828, 1829, 1830, 1831, 1832, 1792, 1828, 1828,
+ 1829, 1830, 1833, 1831, 1832, 1834, 1798, 1835,
+ 1835, 1836, 1837, 1839, 1839, 1840, 1841, 1838,
+ 1798, 1842, 1842, 1843, 1844, 1846, 1847, 1845,
+ 1845, 1845, 1806, 1848, 1848, 1848, 1806, 1849,
+ 1849, 1850, 1851, 1852, 1853, 1854, 1854, 1855,
+ 1856, 1845, 1845, 1845, 1806, 1858, 1857, 1806,
+ 1860, 1859, 1792, 1861, 1861, 1862, 1863, 1864,
+ 1865, 1838, 1798, 1867, 1866, 1792, 1869, 1870,
+ 1871, 1872, 1873, 1874, 1868, 1875, 1868, 1876,
+ 1868, 1877, 1868, 1878, 1878, 1878, 1878, 1878,
+ 1878, 1868, 1879, 1868, 1880, 1868, 1881, 1881,
+ 1881, 1881, 1881, 1881, 1868, 1882, 1868, 1883,
+ 1868, 1884, 1885, 1868, 1886, 1868, 1887, 1868,
+ 1888, 1868, 1889, 1868, 1890, 1890, 1890, 1890,
+ 1890, 1890, 1868, 1891, 1868, 1892, 1868, 1893,
+ 1868, 1894, 1868, 1895, 1895, 1895, 1895, 1895,
+ 1895, 1868, 1896, 1868, 1897, 1868, 1899, 1898,
+ 1900, 1900, 1900, 1900, 1900, 1900, 1901, 1898,
+ 1902, 1868, 1903, 1868, 1904, 1868, 1905, 1868,
+ 1906, 1868, 1907, 1868, 1908, 1868, 1909, 1868,
+ 1910, 1868, 1911, 1868, 1912, 1868, 1913, 1868,
+ 1914, 1868, 1915, 1868, 1916, 1916, 1916, 1916,
+ 1916, 1916, 1868, 1917, 1868, 1918, 1868, 1919,
+ 1868, 1920, 1920, 1920, 1920, 1920, 1920, 1868,
+ 1922, 1922, 1923, 1924, 1925, 1926, 1927, 1927,
+ 1928, 1929, 1921, 1930, 1930, 1931, 1932, 1933,
+ 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941,
+ 1942, 1942, 1943, 1944, 1921, 1945, 1921, 1946,
+ 1921, 1947, 1921, 1948, 1921, 1921, 1950, 1921,
+ 1921, 1921, 1949, 1951, 1951, 1952, 1953, 1954,
+ 1955, 1956, 1956, 1957, 1958, 1921, 1960, 1959,
+ 1921, 1961, 1921, 1962, 1921, 1963, 1921, 1964,
+ 1965, 1965, 1965, 1965, 1921, 1966, 1966, 1966,
+ 1966, 1921, 1967, 1967, 1967, 1967, 1921, 1968,
+ 1969, 1968, 1968, 1968, 1921, 1970, 1971, 1970,
+ 1970, 1970, 1921, 1972, 1973, 1973, 1973, 1973,
+ 1921, 1974, 1974, 1975, 1976, 1977, 1978, 1979,
+ 1979, 1980, 1981, 1921, 1971, 1921, 1982, 1982,
+ 1982, 1982, 1921, 1983, 1984, 1983, 1983, 1983,
+ 1921, 1985, 1986, 1985, 1985, 1985, 1921, 1987,
+ 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1993,
+ 1994, 1995, 1990, 1990, 1990, 1921, 1986, 1921,
+ 1996, 1921, 1997, 1921, 1998, 1999, 1921, 2000,
+ 1921, 2001, 1921, 2002, 1921, 2003, 1921, 2004,
+ 1921, 2006, 2007, 2007, 2005, 2008, 2008, 2005,
+ 2009, 2010, 2011, 2011, 2005, 2012, 2012, 2005,
+ 2013, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
+ 2020, 2020, 2021, 2022, 2017, 2005, 2023, 2023,
+ 2005, 2024, 1921, 2025, 1921, 2026, 1921, 2027,
+ 1921, 2028, 1921, 2029, 2030, 2030, 2030, 2030,
+ 2005, 2031, 2031, 2031, 2031, 2005, 2032, 2033,
+ 2034, 2034, 2034, 2034, 2005, 2035, 2035, 2035,
+ 2035, 2005, 2036, 2036, 2037, 2038, 2039, 2040,
+ 2041, 2042, 2043, 2043, 2044, 2045, 2040, 2040,
+ 2040, 2005, 2046, 2046, 2046, 2046, 2005, 2047,
+ 1921, 2048, 1921, 2050, 2049, 2051, 2051, 2052,
+ 2053, 2055, 2056, 2057, 2058, 2058, 2059, 2060,
+ 2054, 2049, 1921, 1921, 1921, 1921, 2061, 1987,
+ 1987, 1988, 1989, 1991, 1992, 1993, 1993, 1994,
+ 1995, 1921, 2062, 1921, 2063, 1921, 2064, 1921,
+ 2065, 1921, 2066, 1921, 2067, 1921, 2068, 1921,
+ 2069, 1921, 2070, 1921, 2071, 2072, 2072, 1921,
+ 2073, 2073, 1921, 2074, 2075, 1921, 2076, 2076,
+ 1921, 2077, 2077, 2078, 2079, 2080, 2081, 2082,
+ 2083, 2083, 2084, 2085, 1921, 2086, 2086, 1921,
+ 2087, 1921, 2088, 1921, 2089, 1921, 2090, 1921,
+ 2091, 1921, 2092, 1921, 2093, 1921, 2094, 1921,
+ 2095, 1921, 2096, 1921, 2097, 1921, 2098, 1921,
+ 2099, 1921, 2100, 1921, 2101, 2101, 2102, 2103,
+ 2104, 2105, 2106, 2106, 2107, 2108, 1921, 2109,
+ 1921, 2110, 1921, 2111, 1921, 2112, 1921, 2113,
+ 2114, 2049, 2115, 2049, 2116, 2117, 2049, 2118,
+ 2118, 2119, 2120, 2122, 2123, 2124, 2124, 2125,
+ 2126, 2121, 2049, 1951, 1951, 1952, 1953, 1954,
+ 1955, 1956, 1956, 1957, 1958, 2127, 2127, 2127,
+ 1921, 2128, 1921, 2131, 2132, 2133, 2134, 2135,
+ 2131, 2132, 2133, 2134, 2135, 2130, 2129, 2136,
+ 2136, 2136, 2136, 2137, 2129, 2138, 2139, 2138,
+ 2139, 2129, 2140, 2140, 2140, 2140, 2129, 2141,
+ 2141, 2129, 2142, 2143, 2142, 2142, 2142, 2129,
+ 2144, 2144, 2129, 2145, 2145, 2129, 2146, 2146,
+ 2129, 2147, 2147, 2129, 2148, 2129, 2149, 2129,
+ 2150, 2150, 2129, 2151, 2151, 2129, 2152, 2152,
+ 2129, 2153, 2129, 2154, 2154, 2154, 2154, 2129,
+ 2155, 2156, 2155, 2156, 2129, 2157, 2158, 2157,
+ 2158, 2129, 2159, 2129, 2160, 2160, 2129, 2161,
+ 2161, 2129, 2162, 2162, 2129, 2163, 2163, 2129,
+ 2164, 2164, 2164, 2164, 2129, 2165, 2165, 2129,
+ 2166, 2166, 2129, 2167, 2167, 2129, 2168, 2169,
+ 2129, 2170, 2129, 2171, 2129, 2172, 2172, 2129,
+ 2173, 2173, 2129, 2174, 2174, 2129, 2175, 2129,
+ 2176, 2129, 2177, 2129, 2178, 2178, 2178, 2178,
+ 2129, 2179, 2129, 2180, 2129, 2181, 2181, 2129,
+ 2182, 2182, 2129, 2183, 2183, 2129, 2184, 2129,
+ 2185, 2129, 2186, 2129, 2187, 2187, 2187, 2187,
+ 2129, 2188, 2189, 2129, 2190, 2129, 2191, 2129,
+ 2192, 2129, 2193, 2129, 2194, 2194, 2194, 2194,
+ 2129, 2195, 2129, 2196, 2129, 2197, 2197, 2197,
+ 2197, 2129, 2198, 2198, 2129, 2199, 2199, 2129,
+ 2200, 2200, 2129, 2201, 2201, 2129, 2202, 2202,
+ 2129, 2203, 2203, 2129, 2204, 2204, 2129, 2205,
+ 2205, 2205, 2205, 2129, 2206, 2206, 2129, 2207,
+ 2207, 2129, 2208, 2208, 2129, 2209, 2209, 2129,
+ 2210, 2210, 2129, 2211, 2211, 2129, 2212, 2213,
+ 2212, 2213, 2129, 2214, 2214, 2129, 2215, 2215,
+ 2129, 2216, 2216, 2216, 2216, 2129, 2217, 2217,
+ 2129, 2218, 2218, 2129, 2219, 2219, 2219, 2219,
+ 2129, 2220, 2220, 2129, 2221, 2221, 2129, 2222,
+ 2223, 2222, 2223, 2129, 2224, 2224, 2129, 2225,
+ 2129, 2226, 2226, 2226, 2226, 2129, 2227, 2227,
+ 2129, 2228, 2228, 2129, 2229, 2230, 2231, 2129,
+ 2232, 2233, 2232, 2232, 2232, 2129, 2234, 2234,
+ 2129, 2235, 2235, 2129, 2236, 2236, 2129, 2237,
+ 2237, 2129, 2238, 2129, 2239, 2129, 2240, 2240,
+ 2129, 2241, 2241, 2129, 2242, 2242, 2129, 2243,
+ 2129, 2244, 2244, 2244, 2244, 2129, 2245, 2129,
+ 2246, 2129, 2247, 2247, 2247, 2247, 2129, 2248,
+ 2129, 2249, 2129, 2250, 2250, 2250, 2250, 2129,
+ 2253, 2254, 2255, 2256, 2257, 2258, 2253, 2254,
+ 2255, 2256, 2257, 2258, 2252, 2251, 2259, 2259,
+ 2259, 2259, 2260, 2251, 2261, 2261, 2251, 2262,
+ 2262, 2251, 2263, 2263, 2251, 2264, 2264, 2251,
+ 2265, 2265, 2251, 2266, 2266, 2266, 2266, 2251,
+ 2267, 2268, 2269, 2267, 2268, 2269, 2251, 2270,
+ 2270, 2251, 2271, 2271, 2251, 2272, 2272, 2251,
+ 2273, 2273, 2251, 2274, 2274, 2251, 2275, 2275,
+ 2275, 2275, 2251, 2276, 2277, 2276, 2277, 2251,
+ 2278, 2278, 2251, 2279, 2279, 2279, 2279, 2251,
+ 2280, 2280, 2251, 2281, 2281, 2251, 2282, 2282,
+ 2282, 2282, 2251, 2283, 2283, 2251, 2284, 2284,
+ 2251, 2285, 2285, 2251, 2286, 2286, 2286, 2286,
+ 2251, 2287, 2287, 2251, 2288, 2288, 2251, 2289,
+ 2289, 2289, 2289, 2251, 2290, 2291, 2290, 2291,
+ 2251, 2292, 2292, 2251, 2293, 2293, 2293, 2293,
+ 2251, 2294, 2294, 2251, 2295, 2295, 2251, 2296,
+ 2296, 2296, 2296, 2251, 2297, 2297, 2251, 2298,
+ 2298, 2251, 2299, 2299, 2251, 2300, 2300, 2300,
+ 2300, 2251, 2301, 2301, 2251, 2302, 2302, 2251,
+ 2303, 2303, 2303, 2303, 2251, 2305, 2305, 2304,
+ 2306, 2307, 2306, 2306, 2306, 2307, 2304, 2308,
+ 2308, 2308, 2308, 2308, 2308, 75, 2309, 2309,
+ 2309, 2309, 75, 2310, 2310, 2310, 2310, 2310,
+ 2310, 75, 2311, 2311, 2312, 2313, 2314, 2315,
+ 75, 2316, 2316, 2317, 2318, 2319, 2319, 2319,
+ 2320, 2321, 2319, 2319, 2319, 75, 2322, 2322,
+ 2323, 2324, 2325, 2326, 75, 2328, 2328, 2329,
+ 2330, 2332, 2333, 2331, 2327, 2334, 2334, 2335,
+ 2336, 2338, 2339, 2337, 2327, 2340, 2340, 2341,
+ 2342, 2344, 2345, 2343, 2327, 2347, 2347, 2348,
+ 2349, 2351, 2352, 2353, 2354, 2355, 2351, 2352,
+ 2353, 2354, 2355, 2356, 2357, 2350, 2346, 2358,
+ 2358, 2359, 2360, 2362, 2363, 2361, 2327, 2364,
+ 2364, 2365, 2366, 2368, 2369, 2370, 2371, 2372,
+ 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2367,
+ 2346, 2375, 2375, 2376, 2377, 2379, 2380, 2378,
+ 2327, 2381, 2381, 2382, 2383, 2385, 2386, 2387,
+ 2388, 2389, 2385, 2386, 2387, 2388, 2389, 2390,
+ 2391, 2384, 2346, 2392, 2392, 2393, 2394, 2396,
+ 2397, 2395, 2327, 2398, 2398, 2400, 2401, 2402,
+ 2403, 2404, 2400, 2401, 2402, 2403, 2404, 2398,
+ 2398, 2399, 2346, 2398, 2398, 2398, 2398, 2405,
+ 2346, 2407, 2408, 2409, 2410, 2411, 2407, 2408,
+ 2409, 2410, 2411, 2406, 2346, 2412, 2412, 2412,
+ 2412, 2413, 2346, 2415, 2414, 75, 2381, 2381,
+ 2382, 2383, 2390, 2391, 2416, 2346, 2418, 2419,
+ 2420, 2421, 2422, 2418, 2419, 2420, 2421, 2422,
+ 2417, 2346, 2423, 2423, 2424, 2425, 2427, 2428,
+ 2426, 2346, 2430, 2429, 75, 2364, 2364, 2365,
+ 2366, 2373, 2374, 2431, 2346, 2433, 2434, 2435,
+ 2436, 2437, 2433, 2434, 2435, 2436, 2437, 2432,
+ 2346, 2438, 2438, 2439, 2440, 2442, 2443, 2441,
+ 2346, 2445, 2444, 75, 2347, 2347, 2348, 2349,
+ 2356, 2357, 2446, 2346, 2448, 2449, 2450, 2451,
+ 2452, 2448, 2449, 2450, 2451, 2452, 2447, 2346,
+ 2453, 2453, 2454, 2455, 2457, 2458, 2456, 2346,
+ 2460, 2459, 75, 2462, 2461, 75, 2464, 2463,
+ 75, 75, 75, 75, 75, 2465, 2466, 2466,
+ 2467, 2468, 2469, 2470, 75, 2472, 2472, 2473,
+ 2474, 2475, 2476, 2471, 2471, 2471, 75, 2477,
+ 2477, 2477, 2477, 75, 2479, 2478, 75, 2480,
+ 2480, 2480, 2480, 2480, 2480, 75, 2481, 2481,
+ 2482, 2483, 2484, 2485, 75, 2486, 2486, 2487,
+ 2488, 2489, 2489, 2489, 2490, 2491, 2489, 2489,
+ 2489, 75, 2492, 2492, 2492, 2492, 75, 2494,
+ 2493, 75, 2495, 2327, 2496, 2496, 2497, 2498,
+ 2500, 2501, 2499, 2327, 2502, 2502, 2503, 2504,
+ 2505, 2505, 2505, 2506, 2507, 2505, 2505, 2505,
+ 75, 2508, 2508, 2508, 2508, 75, 2510, 2509,
+ 75, 2512, 2512, 2512, 2512, 2511, 2513, 2513,
+ 2514, 2515, 2516, 2517, 2518, 2518, 2519, 2520,
+ 2512, 2522, 2522, 2523, 2524, 2525, 2526, 2527,
+ 2527, 2528, 2529, 2521, 2521, 2521, 2512, 2531,
+ 2530, 2512, 2532, 2532, 2532, 2532, 2304, 2533,
+ 2534, 2533, 2533, 2533, 2534, 2534, 2534, 2304,
+ 2536, 2535, 2537, 2537, 2538, 2539, 2541, 2542,
+ 2540, 2535, 2543, 2543, 2544, 2545, 2547, 2548,
+ 2549, 2550, 2546, 2535, 2551, 2551, 2552, 2553,
+ 2555, 2556, 2554, 2535, 2557, 2557, 2558, 2559,
+ 2547, 2548, 2561, 2562, 2560, 2535, 2563, 2563,
+ 2564, 2565, 2566, 2568, 2569, 2567, 2535, 2570,
+ 2570, 2571, 2572, 2547, 2548, 2573, 2574, 2535,
+ 2575, 2575, 2576, 2577, 2578, 2579, 2535, 2575,
+ 2575, 2576, 2577, 2578, 2579, 2580, 2535, 2581,
+ 2581, 2582, 2583, 2585, 2586, 2584, 2535, 2587,
+ 2587, 2588, 2589, 2591, 2592, 2593, 2594, 2590,
+ 2535, 2595, 2595, 2596, 2597, 2599, 2600, 2598,
+ 2535, 2601, 2601, 2602, 2603, 2591, 2592, 2605,
+ 2606, 2604, 2535, 2607, 2607, 2608, 2609, 2610,
+ 2612, 2613, 2611, 2535, 2614, 2614, 2615, 2616,
+ 2591, 2592, 2617, 2618, 2535, 2619, 2619, 2620,
+ 2621, 2622, 2623, 2535, 2619, 2619, 2620, 2621,
+ 2624, 2622, 2623, 2625, 2535, 2626, 2535, 2627,
+ 2627, 2628, 2629, 2630, 2632, 2633, 2634, 2635,
+ 2635, 2636, 2637, 2631, 2535, 2638, 2638, 2639,
+ 2640, 2642, 2643, 2644, 2644, 2645, 2646, 2641,
+ 2535, 2647, 2647, 2648, 2649, 2650, 2652, 2653,
+ 2654, 2655, 2655, 2656, 2657, 2651, 2535, 2658,
+ 2658, 2659, 2660, 2662, 2663, 2644, 2644, 2664,
+ 2665, 2661, 2535, 2666, 2666, 2667, 2668, 2669,
+ 2671, 2672, 2673, 2674, 2674, 2675, 2676, 2670,
+ 2535, 2677, 2677, 2678, 2679, 2681, 2682, 2644,
+ 2644, 2683, 2684, 2680, 2535, 2685, 2685, 2686,
+ 2687, 2688, 2685, 2690, 2691, 2692, 2692, 2693,
+ 2694, 2689, 2535, 2695, 2695, 2696, 2697, 2698,
+ 2699, 2644, 2644, 2700, 2701, 2535, 2703, 2702,
+ 2535, 2704, 2704, 2705, 2706, 2704, 2708, 2709,
+ 2710, 2710, 2711, 2712, 2707, 2535, 2685, 2685,
+ 2686, 2687, 2685, 2690, 2691, 2692, 2692, 2693,
+ 2694, 2713, 2535, 2715, 2714, 2535, 2716, 2716,
+ 2717, 2718, 2720, 2721, 2722, 2723, 2723, 2724,
+ 2725, 2719, 2535, 2666, 2666, 2667, 2668, 2671,
+ 2672, 2673, 2674, 2674, 2675, 2676, 2726, 2535,
+ 2677, 2677, 2678, 2679, 2681, 2682, 2644, 2644,
+ 2683, 2684, 2535, 2728, 2727, 2535, 2729, 2729,
+ 2730, 2731, 2733, 2734, 2735, 2736, 2736, 2737,
+ 2738, 2732, 2535, 2647, 2647, 2648, 2649, 2652,
+ 2653, 2654, 2655, 2655, 2656, 2657, 2739, 2535,
+ 2658, 2658, 2659, 2660, 2662, 2663, 2644, 2644,
+ 2664, 2665, 2535, 2741, 2740, 2535, 2742, 2742,
+ 2743, 2744, 2746, 2747, 2748, 2749, 2749, 2750,
+ 2751, 2745, 2535, 2627, 2627, 2628, 2629, 2632,
+ 2633, 2634, 2635, 2635, 2636, 2637, 2752, 2535,
+ 2638, 2638, 2639, 2640, 2642, 2643, 2644, 2644,
+ 2645, 2646, 2535, 2754, 2753, 2535, 2755, 2755,
+ 2756, 2757, 2758, 2759, 2535, 2761, 2760, 2535,
+ 2762, 2762, 2763, 2764, 2766, 2767, 2765, 2535,
+ 2607, 2607, 2608, 2609, 2612, 2613, 2768, 2535,
+ 2770, 2769, 2535, 2772, 2771, 2535, 2774, 2773,
+ 2535, 2775, 2775, 2776, 2777, 2778, 2779, 2535,
+ 2781, 2780, 2535, 2782, 2782, 2783, 2784, 2786,
+ 2787, 2785, 2535, 2563, 2563, 2564, 2565, 2568,
+ 2569, 2788, 2535, 2790, 2789, 2535, 2792, 2791,
+ 2535, 2793, 2327, 2794, 2794, 2795, 2796, 2798,
+ 2799, 2797, 2327, 2800, 2800, 2801, 2802, 2804,
+ 2805, 2803, 2327, 2806, 2806, 2807, 2808, 2810,
+ 2811, 2809, 2327, 2812, 2812, 2813, 2814, 2816,
+ 2817, 2815, 2327, 2818, 2818, 2819, 2820, 2822,
+ 2823, 2821, 2327, 2824, 2824, 2825, 2826, 2827,
+ 2827, 2827, 2828, 2829, 2827, 2827, 2827, 75,
+ 2830, 2830, 2830, 2830, 75, 2832, 2831, 75,
+ 2834, 2833, 75, 2836, 2835, 75, 2837, 2327,
+ 2838, 2838, 2839, 2840, 2842, 2843, 2841, 2327,
+ 2844, 2844, 2845, 2846, 2848, 2849, 2847, 2327,
+ 2850, 2850, 2851, 2852, 2854, 2855, 2853, 2327,
+ 2857, 2857, 2858, 2859, 2860, 2861, 2856, 2856,
+ 2856, 75, 2862, 2862, 2863, 2864, 2865, 2866,
+ 75, 2868, 2868, 2869, 2870, 2871, 2872, 2867,
+ 2867, 2867, 75, 2873, 2873, 2874, 2875, 2876,
+ 2877, 75, 2879, 2879, 2880, 2881, 2882, 2883,
+ 2878, 2878, 2878, 75, 2884, 2884, 2885, 2886,
+ 2887, 2888, 75, 2889, 2889, 2890, 2891, 2892,
+ 2892, 2892, 2893, 2894, 2892, 2892, 2892, 75,
+ 2895, 2895, 2895, 2895, 75, 2897, 2896, 75,
+ 2899, 2898, 75, 2901, 2900, 75, 2903, 2902,
+ 75, 2905, 2904, 75, 2906, 2906, 2906, 75,
+ 2907, 2907, 2908, 2909, 2910, 2911, 75, 2907,
+ 2907, 2908, 2909, 2910, 2911, 2912, 2327, 2913,
+ 2913, 2914, 2915, 2917, 2918, 2916, 2327, 2919,
+ 2919, 2920, 2921, 2923, 2924, 2922, 2922, 2922,
+ 75, 2925, 2925, 2926, 2927, 2928, 2929, 75,
+ 2925, 2925, 2926, 2927, 2930, 2928, 2929, 2930,
+ 2930, 2930, 75, 2931, 2931, 75, 2933, 2932,
+ 75, 2935, 2934, 75, 2937, 2936, 75, 2939,
+ 2939, 2940, 2941, 2942, 2943, 2944, 2945, 2946,
+ 2947, 2947, 2948, 2949, 2938, 2950, 2951, 2938,
+ 2952, 2938, 2954, 2954, 2953, 2956, 2955, 2953,
+ 2957, 2938, 2958, 2958, 2959, 2960, 2962, 2963,
+ 2964, 2964, 2965, 2966, 2961, 2938, 2968, 2967,
+ 75, 2969, 2938, 2970, 2970, 2970, 2970, 2953,
+ 2972, 2971, 2971, 2971, 2953, 2973, 2327, 2974,
+ 2974, 2975, 2976, 2978, 2979, 2977, 2327, 2980,
+ 2980, 2981, 2982, 2984, 2985, 2983, 2983, 2983,
+ 75, 2986, 2986, 2987, 2988, 2989, 2990, 75,
+ 2986, 2986, 2987, 2988, 2989, 2990, 2991, 2327,
+ 2992, 2992, 2993, 2994, 2996, 2997, 2995, 2327,
+ 2999, 2999, 3000, 3001, 3003, 3004, 3002, 3002,
+ 3002, 2998, 3005, 3005, 3005, 2998, 3006, 3006,
+ 3007, 3008, 3009, 3010, 3011, 3011, 3012, 3013,
+ 3002, 3002, 3002, 2998, 3015, 3014, 2998, 3017,
+ 3016, 75, 3019, 3018, 75, 3021, 3020, 75,
+ 3022, 2327, 3023, 3023, 3024, 3025, 3027, 3028,
+ 3026, 2327, 3029, 3029, 3030, 3031, 3033, 3034,
+ 3032, 2327, 3035, 3035, 3036, 3037, 3039, 3040,
+ 3038, 2327, 3041, 3041, 3042, 3043, 3045, 3046,
+ 3044, 3044, 3044, 2998, 3047, 3047, 3047, 2998,
+ 3048, 3048, 3049, 3050, 3051, 3052, 3053, 3053,
+ 3054, 3055, 3044, 3044, 3044, 2998, 3057, 3056,
+ 2998, 3059, 3058, 75, 3061, 3060, 75, 3062,
+ 2327, 3063, 3063, 3064, 3065, 3067, 3068, 3066,
+ 2327, 3070, 3070, 3071, 3072, 3073, 3074, 3075,
+ 3076, 3077, 3078, 3069, 3079, 3079, 3080, 3081,
+ 3082, 3083, 3069, 3079, 3079, 3080, 3081, 3082,
+ 3083, 3085, 3084, 3086, 3086, 3087, 3088, 3090,
+ 3091, 3089, 3084, 3092, 3092, 3093, 3094, 3095,
+ 3096, 3097, 3069, 3099, 3099, 3100, 3101, 3102,
+ 3102, 3103, 3104, 3105, 3105, 3106, 3107, 3108,
+ 3108, 3108, 3108, 3109, 3110, 3108, 3108, 3111,
+ 3112, 3113, 3114, 3108, 3108, 3115, 3116, 3117,
+ 3118, 3108, 3108, 3119, 3120, 3098, 3099, 3099,
+ 3100, 3101, 3122, 3109, 3110, 3122, 3122, 3122,
+ 3121, 3108, 3108, 3121, 3124, 3123, 3121, 3102,
+ 3102, 3103, 3104, 3108, 3108, 3108, 3108, 3113,
+ 3114, 3108, 3108, 3115, 3116, 3121, 3126, 3125,
+ 3121, 3122, 3099, 3099, 3100, 3101, 3102, 3102,
+ 3103, 3104, 3105, 3105, 3106, 3107, 3108, 3108,
+ 3108, 3108, 3109, 3110, 3108, 3108, 3111, 3112,
+ 3113, 3114, 3108, 3108, 3115, 3116, 3117, 3118,
+ 3108, 3108, 3119, 3120, 3122, 3122, 3122, 3121,
+ 3124, 3126, 3128, 3123, 3125, 3127, 3121, 3130,
+ 3129, 3069, 3132, 3131, 3069, 3133, 3133, 3134,
+ 3135, 3136, 3137, 3069, 3133, 3133, 3134, 3135,
+ 3136, 3137, 3138, 3084, 3139, 3139, 3140, 3141,
+ 3143, 3144, 3142, 3084, 3146, 3146, 3147, 3148,
+ 3149, 3150, 3151, 3149, 3145, 3153, 3154, 3154,
+ 3155, 3156, 3157, 3157, 3158, 3159, 3160, 3160,
+ 3161, 3162, 3163, 3163, 3163, 3163, 3164, 3165,
+ 3163, 3163, 3166, 3167, 3168, 3169, 3163, 3163,
+ 3170, 3171, 3172, 3173, 3163, 3163, 3174, 3175,
+ 3153, 3152, 3177, 3176, 3069, 3179, 3178, 3069,
+ 3180, 3180, 3181, 3182, 3183, 3184, 3069, 3180,
+ 3180, 3181, 3182, 3183, 3184, 3185, 3084, 3186,
+ 3186, 3187, 3188, 3190, 3191, 3189, 3084, 3192,
+ 3192, 3193, 3194, 3195, 3196, 3197, 3195, 3195,
+ 3195, 3145, 3198, 3199, 3199, 3200, 3201, 3202,
+ 3202, 3203, 3204, 3205, 3205, 3206, 3207, 3208,
+ 3208, 3208, 3208, 3209, 3210, 3208, 3208, 3211,
+ 3212, 3213, 3214, 3208, 3208, 3215, 3216, 3217,
+ 3218, 3208, 3208, 3219, 3220, 3198, 3198, 3198,
+ 3152, 3222, 3221, 3069, 3224, 3223, 3069, 3225,
+ 3225, 3226, 3227, 3228, 3229, 3069, 3225, 3225,
+ 3226, 3227, 3228, 3229, 3230, 3084, 3231, 3231,
+ 3232, 3233, 3235, 3236, 3234, 3084, 3237, 3237,
+ 3238, 3239, 3240, 3240, 3240, 3241, 3242, 3240,
+ 3240, 3240, 3069, 3243, 3243, 3244, 3245, 3246,
+ 3246, 3247, 3248, 3249, 3249, 3250, 3251, 3252,
+ 3252, 3252, 3252, 3253, 3254, 3252, 3252, 3255,
+ 3256, 3257, 3258, 3252, 3252, 3259, 3260, 3261,
+ 3262, 3252, 3252, 3263, 3264, 3098, 3266, 3265,
+ 3069, 3268, 3267, 3069, 3270, 3269, 75, 3272,
+ 3273, 3274, 3275, 3276, 3277, 3278, 3279, 3280,
+ 3281, 3282, 3283, 3284, 3285, 3286, 3287, 3288,
+ 3272, 3273, 3274, 3275, 3276, 3277, 3278, 3279,
+ 3280, 3281, 3282, 3283, 3284, 3285, 3286, 3287,
+ 3288, 3271, 3289, 3289, 3290, 3291, 3292, 3293,
+ 3294, 3292, 3293, 3294, 3295, 3296, 3271, 3297,
+ 3297, 3298, 3299, 3301, 3302, 3300, 3300, 3300,
+ 75, 3303, 3303, 3304, 3305, 3306, 3307, 75,
+ 3303, 3303, 3304, 3305, 3306, 3307, 3308, 2327,
+ 3309, 3309, 3310, 3311, 3313, 3314, 3312, 2327,
+ 3315, 3315, 3316, 3317, 3319, 3320, 3318, 2327,
+ 3321, 3321, 3322, 3323, 3325, 3326, 3324, 2327,
+ 3328, 3328, 3329, 3330, 3332, 3333, 3331, 3327,
+ 3334, 3334, 3335, 3336, 3338, 3339, 3337, 3327,
+ 3340, 3340, 3341, 3342, 3344, 3345, 3343, 3327,
+ 3346, 3346, 3347, 3348, 3350, 3351, 3349, 3327,
+ 3352, 3352, 3353, 3354, 3356, 3357, 3355, 2327,
+ 3358, 3358, 3359, 3360, 3362, 3363, 3361, 2327,
+ 3364, 3364, 3365, 3366, 3367, 3367, 3367, 3368,
+ 3369, 3367, 3367, 3367, 75, 3370, 3370, 3371,
+ 3372, 3373, 3374, 75, 3375, 3375, 3376, 3377,
+ 3378, 3379, 3380, 3378, 3378, 3378, 75, 3381,
+ 3381, 75, 3383, 3382, 75, 3385, 3384, 75,
+ 3387, 3386, 75, 3389, 3388, 75, 3391, 3390,
+ 75, 3393, 3392, 75, 3395, 3394, 75, 3397,
+ 3396, 75, 3398, 3398, 3271, 3399, 3399, 3271,
+ 3400, 3400, 3401, 3402, 3403, 3404, 3271, 3405,
+ 3405, 3271, 3406, 3406, 3271, 3407, 3407, 3271,
+ 3408, 3408, 3409, 3410, 3411, 3412, 3271, 3413,
+ 3413, 3271, 3414, 3414, 3415, 3416, 3417, 3418,
+ 3271, 3419, 3420, 3421, 3422, 3423, 3419, 3420,
+ 3421, 3422, 3423, 3271, 3424, 3424, 3271, 3425,
+ 3425, 3426, 3427, 3428, 3429, 3271, 3430, 3431,
+ 3430, 3431, 3271, 3432, 3432, 3271, 3433, 3433,
+ 3271, 3434, 3434, 3271, 3435, 3435, 3271, 3436,
+ 3436, 3437, 3438, 3439, 3440, 3271, 3441, 3441,
+ 3442, 3443, 3444, 3445, 3271, 3446, 3446, 3271,
+ 3447, 3447, 3271, 3448, 3448, 3449, 3450, 3451,
+ 3452, 3271, 3453, 3453, 3271, 3454, 3454, 3271,
+ 3455, 3455, 3271, 3456, 3456, 3457, 3458, 3459,
+ 3460, 3271, 3461, 3461, 3271, 3462, 3462, 3271,
+ 3463, 3463, 3271, 3464, 3464, 3465, 3466, 3467,
+ 3468, 3271, 3469, 3470, 3471, 3469, 3470, 3471,
+ 3271, 3472, 3472, 3271, 3473, 3473, 3271, 3474,
+ 3474, 3271, 3475, 3475, 3476, 3477, 3478, 3479,
+ 3271, 3480, 3481, 3480, 3481, 3271, 3482, 3482,
+ 3271, 3483, 3483, 3271, 3484, 3484, 3485, 3486,
+ 3487, 3488, 3271, 3489, 3489, 3271, 3490, 3490,
+ 3271, 3491, 3491, 3271, 3492, 3492, 3493, 3494,
+ 3495, 3496, 3271, 3497, 3497, 3498, 3499, 3500,
+ 3501, 3271, 3502, 3502, 3271, 3503, 3503, 3271,
+ 3504, 3505, 3271, 3506, 3271, 3507, 3507, 3508,
+ 3509, 3510, 3511, 3271, 3512, 3271, 3513, 3513,
+ 3514, 3515, 3516, 3517, 3271, 3518, 3519, 3518,
+ 3519, 3271, 3520, 3520, 3271, 3521, 3521, 3271,
+ 3522, 3522, 3271, 3523, 3523, 3524, 3525, 3526,
+ 3527, 3271, 3528, 3528, 3271, 3529, 3529, 3271,
+ 3530, 3530, 3271, 3531, 3531, 3532, 3533, 3534,
+ 3535, 3271, 3536, 3536, 3271, 3537, 3537, 3271,
+ 3538, 3538, 3271, 3539, 3539, 3271, 3540, 3540,
+ 3271, 3541, 3541, 3271, 3542, 3542, 3271, 3543,
+ 3543, 3544, 3545, 3546, 3547, 3271, 3548, 3549,
+ 3548, 3549, 3271, 3550, 3550, 3271, 3551, 3551,
+ 3552, 3553, 3554, 3555, 3271, 3556, 3556, 3557,
+ 3558, 3559, 3560, 3271, 3561, 3562, 3563, 3564,
+ 3563, 3564, 3271, 3565, 3271, 3566, 3566, 3567,
+ 3568, 3569, 3570, 3271, 3571, 3271, 3572, 3572,
+ 3573, 3574, 3575, 3576, 3271, 3577, 3577, 3271,
+ 3578, 3578, 3579, 3580, 3581, 3582, 3271, 3583,
+ 3583, 3584, 3585, 3586, 3587, 3271, 3588, 3589,
+ 3588, 3589, 3271, 3590, 3590, 3271, 3591, 3591,
+ 3271, 3592, 3592, 3271, 3593, 3593, 3594, 3595,
+ 3596, 3597, 3271, 3598, 3598, 3599, 3600, 3601,
+ 3602, 3271, 3603, 3604, 3605, 3603, 3604, 3605,
+ 3271, 3606, 3606, 3271, 3607, 3607, 3271, 3608,
+ 3608, 3271, 3609, 3609, 3610, 3611, 3612, 3613,
+ 3271, 3614, 3614, 3271, 3615, 3615, 3616, 3617,
+ 3618, 3619, 3271, 3620, 3620, 3621, 3622, 3623,
+ 3623, 3624, 3625, 3271, 3626, 3626, 3271, 3627,
+ 3627, 3628, 3629, 3630, 3631, 3632, 3271, 3633,
+ 3633, 3634, 3635, 3636, 3636, 3637, 3638, 3271,
+ 3639, 3639, 3271, 3640, 3640, 3271, 3641, 3641,
+ 3271, 3642, 3642, 3271, 3643, 3643, 3644, 3645,
+ 3646, 3647, 3271, 3648, 3648, 3271, 3649, 3649,
+ 3271, 3650, 3650, 3271, 3651, 3651, 3271, 3652,
+ 3652, 3271, 3653, 3653, 3271, 3654, 3654, 3271,
+ 3655, 3655, 3271, 3656, 3656, 3271, 3657, 3657,
+ 3658, 3659, 3660, 3661, 3271, 3662, 3662, 3271,
+ 3663, 3663, 3271, 3664, 3664, 3665, 3666, 3667,
+ 3668, 3271, 3669, 3670, 3671, 3669, 3670, 3671,
+ 3271, 3672, 3672, 3673, 3674, 3675, 3676, 3271,
+ 3677, 3677, 3271, 3678, 3678, 3271, 3679, 3679,
+ 3271, 3680, 3680, 3681, 3682, 3683, 3684, 3271,
+ 3685, 3685, 3686, 3687, 3688, 3689, 3271, 3690,
+ 3691, 3692, 3693, 3694, 3695, 3690, 3691, 3692,
+ 3693, 3694, 3695, 3271, 3696, 3696, 3271, 3697,
+ 3697, 3271, 3698, 3698, 3271, 3699, 3699, 3271,
+ 3700, 3700, 3701, 3702, 3703, 3704, 3271, 3705,
+ 3705, 3271, 3706, 3706, 3707, 3708, 3709, 3710,
+ 3271, 3711, 3711, 3271, 3712, 3712, 3713, 3714,
+ 3715, 3716, 3271, 3717, 3717, 3271, 3718, 3718,
+ 3719, 3720, 3721, 3722, 3271, 3723, 3723, 3271,
+ 3724, 3724, 3271, 3725, 3725, 3271, 3726, 3726,
+ 3727, 3728, 3729, 3730, 3271, 3731, 3731, 3271,
+ 3732, 3732, 3271, 3733, 3733, 3734, 3735, 3736,
+ 3737, 3271, 3738, 3739, 3740, 3738, 3739, 3740,
+ 3271, 3741, 3741, 3271, 3742, 3742, 3271, 3743,
+ 3743, 3744, 3745, 3746, 3747, 3271, 3748, 3748,
+ 3271, 3749, 3749, 3750, 3751, 3752, 3753, 3271,
+ 3754, 3754, 3271, 3755, 3755, 3271, 3757, 3756,
+ 3758, 3758, 3759, 3760, 3762, 3763, 3761, 3756,
+ 3764, 3764, 3271, 3765, 3765, 3271, 3766, 3766,
+ 3767, 3768, 3769, 3770, 3271, 3771, 3771, 3271,
+ 3772, 3772, 3271, 3773, 3773, 3271, 3774, 3774,
+ 3271, 3775, 3775, 3271, 3776, 3776, 3777, 3778,
+ 3779, 3780, 3271, 3781, 3781, 3781, 3781, 3781,
+ 3781, 75, 3782, 3782, 3782, 3782, 75, 3783,
+ 3783, 3783, 3783, 75, 3784, 2327, 3785, 3785,
+ 3786, 3787, 3789, 3790, 3788, 2327, 3791, 3791,
+ 3792, 3793, 3795, 3796, 3794, 2327, 3797, 3797,
+ 3798, 3799, 3801, 3802, 3800, 2327, 3803, 3803,
+ 3804, 3805, 3807, 3808, 3806, 3806, 3806, 75,
+ 3809, 3809, 3810, 3811, 3812, 3813, 75, 3809,
+ 3809, 3810, 3811, 3814, 3812, 3813, 3814, 3814,
+ 3814, 75, 3815, 3815, 75, 3817, 3816, 75,
+ 3819, 3818, 75, 3821, 3820, 75, 3822, 3822,
+ 3822, 3822, 75, 3823, 3823, 75, 3824, 2327,
+ 3825, 3825, 3826, 3827, 3829, 3830, 3828, 2327,
+ 3831, 3831, 3832, 3833, 3835, 3836, 3834, 2327,
+ 3837, 3837, 3838, 3839, 3841, 3842, 3840, 2327,
+ 3843, 3843, 3844, 3845, 3847, 3848, 3846, 2327,
+ 3849, 3849, 3850, 3851, 3853, 3854, 3852, 2327,
+ 3855, 3855, 3856, 3857, 3858, 3860, 3861, 3859,
+ 3859, 3859, 2998, 3862, 3862, 3863, 3864, 3865,
+ 3866, 2998, 3868, 3868, 3869, 3870, 3872, 3873,
+ 3871, 3871, 3871, 3867, 3874, 3874, 3874, 3867,
+ 3876, 3875, 3875, 3875, 3867, 3877, 3877, 3877,
+ 3867, 3879, 3878, 3878, 3878, 3867, 3881, 3880,
+ 3880, 3880, 3867, 3882, 3882, 3882, 3867, 3884,
+ 3883, 3883, 3883, 3867, 3885, 3885, 3885, 3885,
+ 3886, 3886, 3886, 3867, 3887, 3887, 3887, 3887,
+ 75, 3888, 3867, 3884, 3867, 3881, 3867, 3889,
+ 3867, 3879, 3867, 3891, 3890, 75, 3892, 3892,
+ 3892, 2998, 3862, 3862, 3863, 3864, 3865, 3866,
+ 3893, 3893, 3893, 2998, 3895, 3894, 75, 3897,
+ 3896, 75, 3899, 3898, 75, 3900, 2327, 3901,
+ 3901, 3902, 3903, 3905, 3906, 3904, 2327, 3907,
+ 3907, 3908, 3909, 3911, 3912, 3910, 2327, 3913,
+ 3913, 3914, 3915, 3917, 3918, 3916, 2327, 3919,
+ 3919, 3920, 3921, 3923, 3924, 3922, 2327, 3925,
+ 3925, 3926, 3927, 3929, 3930, 3928, 2327, 3931,
+ 3931, 3932, 3933, 3934, 3936, 3937, 3935, 3935,
+ 3935, 2998, 3938, 3938, 3938, 3938, 2998, 3939,
+ 3939, 3939, 2998, 3938, 3938, 3938, 3938, 3940,
+ 3940, 3940, 2998, 3942, 3941, 75, 3944, 3943,
+ 75, 3946, 3945, 75, 3947, 2327, 3948, 3948,
+ 3949, 3950, 3952, 3953, 3951, 2327, 3954, 3954,
+ 3955, 3956, 3958, 3959, 3957, 2327, 3960, 3960,
+ 3961, 3962, 3964, 3965, 3963, 2327, 3966, 3966,
+ 3967, 3968, 3970, 3971, 3969, 2327, 3972, 3972,
+ 3973, 3974, 3976, 3977, 3975, 2327, 3978, 3978,
+ 3979, 3980, 3982, 3983, 3981, 3981, 3981, 2998,
+ 3984, 3984, 3984, 2998, 3985, 3985, 3986, 3987,
+ 3988, 3989, 3990, 3990, 3991, 3992, 3981, 3981,
+ 3981, 2998, 3994, 3993, 2998, 3996, 3995, 75,
+ 3998, 3997, 75, 4000, 3999, 75, 4001, 2327,
+ 4002, 4002, 4003, 4004, 4006, 4007, 4005, 2327,
+ 4008, 4008, 4009, 4010, 4012, 4013, 4011, 2327,
+ 4014, 4014, 4014, 4014, 4015, 2327, 4016, 4016,
+ 4016, 4016, 75, 4018, 4017, 75, 4019, 2327,
+ 4020, 4020, 4021, 4022, 4024, 4025, 4023, 2327,
+ 4026, 4026, 4027, 4028, 4030, 4031, 4029, 2327,
+ 4032, 4032, 4033, 4034, 4036, 4037, 4035, 2327,
+ 4038, 4038, 4039, 4040, 4042, 4043, 4041, 2327,
+ 4044, 4044, 4045, 4046, 4048, 4049, 4047, 2327,
+ 4050, 4050, 4051, 4052, 4054, 4055, 4053, 4053,
+ 4053, 2998, 4056, 4056, 4056, 2998, 4057, 4057,
+ 4058, 4059, 4060, 4061, 4062, 4062, 4063, 4064,
+ 4053, 4053, 4053, 2998, 4066, 4065, 2998, 4068,
+ 4067, 75, 4070, 4069, 75, 4072, 4071, 75,
+ 4073, 2327, 4074, 4074, 4075, 4076, 4078, 4079,
+ 4077, 2327, 4080, 4080, 4081, 4082, 4083, 4084,
+ 4085, 4083, 2304, 4086, 4087, 4086, 4086, 4086,
+ 4087, 2304, 4089, 4088, 75, 4090, 2327, 4091,
+ 4091, 4092, 4093, 4095, 4096, 4094, 2327, 4097,
+ 4097, 4098, 4099, 4101, 4102, 4100, 4100, 4100,
+ 2998, 4103, 4103, 4103, 2998, 4104, 4104, 4104,
+ 2998, 4105, 4105, 4105, 2998, 4107, 4106, 4108,
+ 4108, 4108, 2998, 4109, 4109, 4109, 2998, 4110,
+ 4110, 4110, 2998, 4111, 4111, 4111, 2998, 4113,
+ 4107, 4113, 4113, 4113, 4112, 4115, 4114, 75,
+ 4116, 4116, 4116, 2998, 4117, 4117, 4117, 2998,
+ 4119, 4118, 4120, 4120, 4120, 2998, 4121, 4121,
+ 4121, 2998, 4122, 4119, 4122, 4122, 4122, 4118,
+ 4123, 4123, 4123, 2998, 4124, 4124, 4124, 2998,
+ 4125, 4118, 4126, 4126, 4126, 2998, 4127, 4127,
+ 4127, 2998, 4128, 4125, 4128, 4128, 4128, 4118,
+ 4129, 2327, 4130, 4130, 4131, 4132, 4134, 4135,
+ 4133, 2327, 4136, 4136, 4137, 4138, 4140, 4141,
+ 4139, 2327, 4142, 4142, 4143, 4144, 4146, 4147,
+ 4145, 2327, 4149, 4149, 4150, 4151, 4152, 4153,
+ 4148, 4148, 4148, 75, 4154, 4154, 4154, 4154,
+ 75, 4156, 4155, 75, 4158, 4157, 75, 4159,
+ 2327, 4160, 4160, 4161, 4162, 4164, 4165, 4163,
+ 2327, 4167, 4167, 4168, 4169, 4170, 4171, 4166,
+ 4166, 4166, 75, 4172, 4172, 4173, 4174, 4175,
+ 4176, 75, 4178, 4178, 4179, 4180, 4181, 4182,
+ 4177, 4177, 4177, 75, 4183, 4183, 4183, 4183,
+ 75, 4185, 4184, 75, 4187, 4186, 75, 4188,
+ 2327, 4189, 4189, 4190, 4191, 4193, 4194, 4192,
+ 2327, 4195, 4195, 4196, 4197, 4198, 4198, 4198,
+ 4199, 4200, 4198, 4198, 4198, 75, 4201, 4201,
+ 4201, 4201, 75, 4202, 4202, 4202, 4202, 75,
+ 4204, 4203, 75, 4206, 4206, 4207, 4208, 4209,
+ 4210, 4210, 4210, 4211, 4212, 4213, 4214, 4210,
+ 4210, 4210, 4205, 4215, 4215, 4216, 4217, 4218,
+ 4219, 73, 4220, 4219, 4211, 4212, 4221, 4222,
+ 4223, 4224, 4225, 4226, 73, 73, 4219, 73,
+ 4219, 73, 4219, 73, 871, 4228, 4228, 4207,
+ 4229, 4230, 4210, 4231, 4232, 4233, 4234, 4235,
+ 4236, 4237, 4238, 4239, 4240, 4241, 4242, 4243,
+ 4244, 4245, 4246, 4247, 4210, 4210, 4231, 4232,
+ 4233, 4234, 4235, 4236, 4237, 4238, 4239, 4240,
+ 4241, 4242, 4243, 4244, 4245, 4246, 4247, 4211,
+ 4212, 4248, 4249, 4210, 4210, 4210, 4227, 4228,
+ 4228, 4207, 4229, 4230, 4210, 4252, 4253, 4254,
+ 4255, 4256, 4257, 4258, 4259, 4260, 4261, 4262,
+ 4263, 4264, 4265, 4266, 4267, 4268, 4210, 4210,
+ 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259,
+ 4260, 4261, 4262, 4263, 4264, 4265, 4266, 4267,
+ 4268, 4211, 4212, 4248, 4249, 4210, 4251, 4210,
+ 4210, 4250, 4270, 4270, 4271, 4272, 4273, 4274,
+ 4274, 4274, 4275, 4276, 4277, 4278, 4274, 4274,
+ 4274, 4269, 4206, 4206, 4207, 4279, 4280, 4210,
+ 4252, 4253, 4254, 4255, 4256, 4257, 4258, 4259,
+ 4260, 4261, 4262, 4263, 4264, 4265, 4266, 4267,
+ 4268, 4210, 4210, 4252, 4253, 4254, 4255, 4256,
+ 4257, 4258, 4259, 4260, 4261, 4262, 4263, 4264,
+ 4265, 4266, 4267, 4268, 4211, 4212, 4281, 4282,
+ 4210, 4251, 4210, 4210, 4250, 4283, 4283, 4207,
+ 4284, 4285, 4210, 4210, 4210, 87, 88, 4286,
+ 4287, 4210, 4210, 4210, 871, 4288, 4288, 4216,
+ 4289, 4290, 4219, 73, 4220, 4219, 87, 88,
+ 4286, 4291, 105, 106, 4286, 4292, 73, 73,
+ 4219, 73, 4219, 73, 4219, 73, 871, 4215,
+ 4215, 4216, 4217, 4218, 4219, 73, 4295, 4296,
+ 4297, 4298, 4299, 4300, 4301, 4302, 4303, 4304,
+ 4305, 4306, 4307, 4308, 4309, 4310, 4311, 4220,
+ 4219, 4295, 4296, 4297, 4298, 4299, 4300, 4301,
+ 4302, 4303, 4304, 4305, 4306, 4307, 4308, 4309,
+ 4310, 4311, 4211, 4212, 4221, 4222, 4223, 4224,
+ 4225, 4226, 73, 73, 4219, 4294, 73, 4219,
+ 73, 4219, 73, 4293, 4288, 4288, 4216, 4289,
+ 4290, 4219, 73, 4295, 4296, 4297, 4298, 4299,
+ 4300, 4301, 4302, 4303, 4304, 4305, 4306, 4307,
+ 4308, 4309, 4310, 4311, 4220, 4219, 4295, 4296,
+ 4297, 4298, 4299, 4300, 4301, 4302, 4303, 4304,
+ 4305, 4306, 4307, 4308, 4309, 4310, 4311, 87,
+ 88, 4312, 4291, 105, 106, 4312, 4292, 73,
+ 73, 4219, 4294, 73, 4219, 73, 4219, 73,
+ 4293, 778, 778, 778, 778, 1036, 1037, 1036,
+ 1039, 1036, 1037, 1038, 1037, 1032, 778, 778,
+ 778, 1194, 1194, 1195, 1196, 1163, 1164, 1165,
+ 1163, 1197, 1198, 1163, 1163, 1163, 1169, 778,
+ 1209, 1209, 1210, 1211, 1204, 1212, 1213, 1214,
+ 1214, 1215, 1216, 1204, 1204, 1204, 1203, 1218,
+ 1217, 1203, 778, 1228, 1228, 1229, 1230, 1231,
+ 1232, 1233, 1234, 1235, 1236, 1237, 1238, 1239,
+ 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247,
+ 1231, 1232, 1233, 1234, 1235, 1236, 1237, 1238,
+ 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246,
+ 1247, 1248, 1249, 1250, 1250, 1251, 1252, 1219,
+ 1275, 1274, 1219, 778, 1814, 1814, 1815, 1816,
+ 1817, 1818, 1819, 1819, 1820, 1821, 1810, 1810,
+ 1810, 1806, 1823, 1822, 1806, 1842, 1842, 1843,
+ 1844, 1846, 1847, 1845, 1845, 1845, 1806, 1792,
+ 1849, 1849, 1850, 1851, 1852, 1853, 1854, 1854,
+ 1855, 1856, 1845, 1845, 1845, 1806, 1858, 1857,
+ 1806, 1860, 1859, 1792, 778, 778, 1930, 1930,
+ 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938,
+ 1939, 1940, 1941, 1942, 1942, 1943, 1944, 1921,
+ 1960, 1959, 1921, 778, 778, 778, 778, 778,
+ 778, 778, 778, 778, 2522, 2522, 2523, 2524,
+ 2525, 2526, 2527, 2527, 2528, 2529, 2521, 2521,
+ 2521, 2512, 2531, 2530, 2512, 778, 778, 2695,
+ 2695, 2696, 2697, 2698, 2699, 2644, 2644, 2700,
+ 2701, 2535, 2703, 2702, 2535, 2677, 2677, 2678,
+ 2679, 2681, 2682, 2644, 2644, 2683, 2684, 2680,
+ 2535, 2715, 2714, 2535, 2658, 2658, 2659, 2660,
+ 2662, 2663, 2644, 2644, 2664, 2665, 2661, 2535,
+ 2728, 2727, 2535, 2638, 2638, 2639, 2640, 2642,
+ 2643, 2644, 2644, 2645, 2646, 2641, 2535, 2741,
+ 2740, 2535, 778, 778, 778, 778, 2939, 2939,
+ 2940, 2941, 2942, 2943, 2944, 2945, 2946, 2947,
+ 2947, 2948, 2949, 2938, 2968, 2967, 75, 778,
+ 3006, 3006, 3007, 3008, 3009, 3010, 3011, 3011,
+ 3012, 3013, 3002, 3002, 3002, 2998, 3015, 3014,
+ 2998, 778, 3048, 3048, 3049, 3050, 3051, 3052,
+ 3053, 3053, 3054, 3055, 3044, 3044, 3044, 2998,
+ 3057, 3056, 2998, 778, 3102, 3102, 3103, 3104,
+ 3108, 3108, 3108, 3108, 3113, 3114, 3108, 3108,
+ 3115, 3116, 3121, 3126, 3125, 3121, 3099, 3099,
+ 3100, 3101, 3122, 3109, 3110, 3122, 3122, 3122,
+ 3121, 3124, 3123, 3121, 3122, 3099, 3099, 3100,
+ 3101, 3102, 3102, 3103, 3104, 3105, 3105, 3106,
+ 3107, 3108, 3108, 3108, 3108, 3109, 3110, 3108,
+ 3108, 3111, 3112, 3113, 3114, 3108, 3108, 3115,
+ 3116, 3117, 3118, 3108, 3108, 3119, 3120, 3122,
+ 3122, 3122, 3121, 3124, 3126, 3128, 3123, 3125,
+ 3127, 3121, 778, 778, 778, 778, 778, 778,
+ 778, 3985, 3985, 3986, 3987, 3988, 3989, 3990,
+ 3990, 3991, 3992, 3981, 3981, 3981, 2998, 3994,
+ 3993, 2998, 778, 778, 4057, 4057, 4058, 4059,
+ 4060, 4061, 4062, 4062, 4063, 4064, 4053, 4053,
+ 4053, 2998, 4066, 4065, 2998, 778, 778, 778,
+ 778, 778, 778, 778, 0
+};
+
+static const short _zone_scanner_trans_targs[] = {
+ 0, 1, 1, 1, 2, 4, 17, 40,
+ 54, 61, 175, 81, 85, 93, 99, 115,
+ 125, 128, 135, 154, 164, 167, 1396, 183,
+ 1401, 302, 0, 3, 3, 3, 2, 198,
+ 198, 198, 198, 198, 3, 201, 0, 3,
+ 3, 3, 4, 17, 40, 54, 61, 70,
+ 81, 85, 93, 99, 115, 125, 128, 135,
+ 154, 164, 167, 3, 201, 0, 5, 5,
+ 5, 266, 269, 273, 5, 10, 6, 11,
+ 20, 6, 5, 0, 5, 5, 9, 5,
+ 10, 11, 20, 0, 7, 7, 7, 1396,
+ 8, 0, 7, 7, 7, 1396, 8, 8,
+ 1396, 6, 6, 10, 5, 12, 12, 12,
+ 13, 1402, 208, 280, 12, 12, 12, 13,
+ 1402, 208, 280, 13, 1397, 14, 0, 14,
+ 14, 15, 204, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 1404, 275, 1402, 276, 1405,
+ 278, 0, 16, 16, 16, 1398, 305, 0,
+ 16, 16, 16, 1398, 305, 18, 22, 29,
+ 32, 36, 19, 5, 5, 5, 5, 10,
+ 6, 11, 20, 21, 21, 21, 13, 21,
+ 21, 21, 13, 23, 28, 24, 25, 26,
+ 27, 5, 5, 5, 5, 10, 6, 11,
+ 20, 5, 5, 5, 5, 10, 6, 11,
+ 20, 30, 31, 5, 5, 5, 5, 10,
+ 6, 11, 20, 33, 34, 35, 5, 5,
+ 5, 5, 10, 6, 11, 20, 37, 38,
+ 39, 5, 5, 5, 5, 10, 6, 11,
+ 20, 41, 45, 53, 42, 43, 44, 5,
+ 5, 5, 5, 10, 6, 11, 20, 46,
+ 49, 47, 48, 5, 5, 5, 5, 10,
+ 6, 11, 20, 50, 51, 52, 5, 5,
+ 5, 5, 10, 6, 11, 20, 5, 5,
+ 5, 5, 10, 6, 11, 20, 55, 56,
+ 57, 59, 58, 5, 5, 5, 5, 10,
+ 6, 11, 20, 60, 5, 5, 5, 5,
+ 10, 6, 11, 20, 62, 66, 63, 64,
+ 65, 5, 5, 5, 5, 10, 6, 11,
+ 20, 67, 68, 69, 5, 5, 5, 5,
+ 10, 6, 11, 20, 71, 74, 72, 72,
+ 72, 72, 173, 72, 72, 72, 4, 17,
+ 40, 54, 61, 73, 81, 85, 93, 99,
+ 115, 125, 128, 135, 154, 164, 167, 72,
+ 173, 75, 76, 77, 78, 79, 80, 5,
+ 5, 5, 5, 10, 6, 11, 20, 82,
+ 84, 83, 5, 5, 5, 5, 10, 6,
+ 11, 20, 5, 5, 5, 5, 10, 6,
+ 11, 20, 86, 88, 90, 92, 87, 5,
+ 5, 5, 5, 10, 6, 11, 20, 89,
+ 5, 5, 5, 5, 10, 6, 11, 20,
+ 91, 5, 5, 5, 5, 10, 6, 11,
+ 20, 5, 5, 5, 5, 10, 6, 11,
+ 20, 94, 98, 95, 96, 97, 5, 5,
+ 5, 5, 10, 6, 11, 20, 5, 5,
+ 5, 5, 10, 6, 11, 20, 100, 104,
+ 106, 101, 102, 103, 5, 5, 5, 5,
+ 10, 6, 11, 20, 105, 5, 5, 5,
+ 5, 10, 6, 11, 20, 5, 5, 5,
+ 107, 5, 10, 6, 11, 20, 108, 5,
+ 5, 5, 109, 5, 10, 6, 11, 20,
+ 5, 5, 5, 110, 5, 10, 6, 11,
+ 20, 111, 112, 113, 114, 5, 5, 5,
+ 5, 10, 6, 11, 20, 116, 117, 118,
+ 119, 120, 121, 122, 123, 124, 5, 5,
+ 5, 5, 10, 6, 11, 20, 126, 127,
+ 5, 5, 5, 5, 10, 6, 11, 20,
+ 129, 130, 134, 5, 5, 5, 5, 10,
+ 6, 11, 20, 131, 132, 133, 5, 5,
+ 5, 5, 10, 6, 11, 20, 5, 5,
+ 5, 5, 10, 6, 11, 20, 136, 141,
+ 143, 145, 147, 151, 137, 138, 139, 140,
+ 5, 5, 5, 5, 10, 6, 11, 20,
+ 142, 5, 5, 5, 5, 10, 6, 11,
+ 20, 144, 5, 5, 5, 5, 10, 6,
+ 11, 20, 146, 5, 5, 5, 5, 10,
+ 6, 11, 20, 148, 149, 150, 5, 5,
+ 5, 5, 10, 6, 11, 20, 152, 153,
+ 5, 5, 5, 5, 10, 6, 11, 20,
+ 155, 158, 160, 156, 157, 5, 5, 5,
+ 5, 10, 6, 11, 20, 159, 5, 5,
+ 5, 5, 10, 6, 11, 20, 161, 162,
+ 0, 163, 0, 5, 5, 5, 163, 5,
+ 10, 6, 11, 20, 165, 166, 5, 5,
+ 5, 5, 10, 6, 11, 20, 168, 169,
+ 170, 171, 172, 5, 5, 5, 5, 10,
+ 6, 11, 20, 173, 72, 174, 174, 174,
+ 1399, 303, 176, 177, 177, 177, 177, 182,
+ 177, 177, 177, 178, 4, 17, 40, 54,
+ 61, 73, 81, 85, 93, 99, 115, 125,
+ 128, 135, 154, 164, 167, 177, 182, 72,
+ 72, 72, 178, 179, 179, 179, 179, 179,
+ 72, 173, 180, 180, 181, 181, 181, 181,
+ 181, 72, 72, 72, 180, 72, 173, 182,
+ 177, 183, 1396, 0, 185, 229, 235, 186,
+ 187, 188, 189, 190, 191, 1400, 0, 193,
+ 193, 193, 193, 194, 193, 193, 193, 193,
+ 194, 194, 0, 193, 0, 196, 196, 196,
+ 196, 197, 196, 196, 196, 196, 197, 197,
+ 196, 199, 199, 200, 200, 200, 200, 200,
+ 3, 3, 3, 199, 3, 201, 201, 3,
+ 0, 203, 203, 203, 203, 282, 283, 284,
+ 203, 203, 203, 203, 282, 283, 284, 0,
+ 205, 205, 205, 1397, 13, 1402, 208, 1403,
+ 280, 205, 205, 205, 1397, 1403, 206, 206,
+ 206, 1401, 207, 207, 1401, 209, 209, 209,
+ 1396, 8, 209, 209, 209, 8, 210, 210,
+ 210, 227, 264, 227, 1404, 0, 230, 231,
+ 232, 233, 234, 1400, 236, 237, 1400, 0,
+ 206, 206, 206, 1401, 207, 0, 240, 240,
+ 240, 1399, 241, 240, 240, 240, 1399, 241,
+ 241, 1399, 0, 243, 243, 243, 1404, 227,
+ 1405, 264, 243, 243, 243, 1404, 1405, 261,
+ 0, 261, 261, 261, 262, 206, 206, 206,
+ 1401, 207, 263, 1401, 263, 263, 207, 263,
+ 263, 263, 207, 265, 265, 265, 227, 265,
+ 265, 265, 227, 267, 268, 5, 5, 5,
+ 5, 10, 6, 11, 20, 270, 271, 272,
+ 5, 5, 5, 5, 10, 6, 11, 20,
+ 274, 5, 5, 5, 5, 10, 6, 11,
+ 20, 275, 1404, 277, 277, 277, 1396, 277,
+ 277, 277, 1396, 279, 279, 279, 279, 279,
+ 279, 281, 281, 281, 281, 281, 281, 282,
+ 203, 210, 210, 210, 227, 264, 285, 285,
+ 285, 285, 285, 285, 0, 302, 1401, 303,
+ 1399, 305, 1398, 306, 1406, 306, 306, 308,
+ 1407, 308, 1407, 0, 310, 316, 317, 312,
+ 1408, 310, 311, 312, 1408, 310, 312, 313,
+ 0, 314, 315, 1408, 310, 311, 312, 1408,
+ 0, 319, 324, 320, 1409, 319, 1410, 320,
+ 0, 321, 322, 323, 1409, 319, 1410, 320,
+ 324, 325, 326, 327, 324, 325, 326, 327,
+ 0, 1409, 328, 329, 330, 324, 325, 326,
+ 327, 0, 332, 332, 332, 332, 339, 0,
+ 333, 0, 334, 334, 334, 333, 336, 336,
+ 336, 336, 336, 1411, 335, 0, 334, 334,
+ 334, 1411, 335, 335, 1411, 337, 0, 337,
+ 338, 338, 338, 338, 338, 334, 334, 334,
+ 337, 1411, 335, 339, 332, 0, 341, 341,
+ 341, 341, 351, 342, 350, 346, 342, 343,
+ 346, 0, 344, 344, 344, 342, 346, 1412,
+ 345, 344, 344, 344, 1412, 345, 345, 1412,
+ 347, 0, 348, 349, 342, 343, 346, 351,
+ 341, 353, 353, 353, 353, 368, 354, 0,
+ 0, 355, 355, 355, 1413, 359, 365, 0,
+ 355, 355, 355, 356, 364, 360, 1413, 359,
+ 365, 0, 356, 357, 360, 358, 358, 358,
+ 356, 360, 1413, 359, 358, 358, 358, 359,
+ 1413, 361, 0, 362, 363, 356, 357, 360,
+ 365, 1414, 366, 366, 366, 366, 367, 367,
+ 366, 368, 353, 0, 370, 371, 372, 375,
+ 373, 373, 373, 373, 373, 374, 1415, 1416,
+ 1417, 374, 373, 0, 377, 377, 377, 377,
+ 382, 1418, 1419, 1420, 377, 377, 377, 378,
+ 383, 404, 418, 425, 434, 442, 446, 454,
+ 460, 476, 486, 489, 496, 515, 525, 528,
+ 377, 382, 1418, 1419, 1420, 377, 377, 377,
+ 379, 534, 538, 377, 382, 1418, 1419, 1420,
+ 380, 381, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 382, 377, 384, 386, 393, 396,
+ 400, 385, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 387, 392, 388, 389, 390, 391,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 394, 395, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 397, 398, 399, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 401, 402, 403,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 405, 409, 417, 406, 407, 408, 377, 377,
+ 377, 377, 382, 1418, 1419, 1420, 410, 413,
+ 411, 412, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 414, 415, 416, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 419, 420, 421,
+ 423, 422, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 424, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 426, 430, 427, 428, 429,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 431, 432, 433, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 435, 436, 437, 438, 439,
+ 440, 441, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 443, 445, 444, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 447, 449, 451,
+ 453, 448, 377, 377, 377, 377, 382, 1418,
+ 1419, 1420, 450, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 452, 377, 377, 377, 377,
+ 382, 1418, 1419, 1420, 377, 377, 377, 377,
+ 382, 1418, 1419, 1420, 455, 459, 456, 457,
+ 458, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 461, 465, 467, 462, 463, 464, 377,
+ 377, 377, 377, 382, 1418, 1419, 1420, 466,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 377, 377, 377, 468, 377, 382, 1418, 1419,
+ 1420, 469, 377, 377, 377, 470, 377, 382,
+ 1418, 1419, 1420, 377, 377, 377, 471, 377,
+ 382, 1418, 1419, 1420, 472, 473, 474, 475,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 477, 478, 479, 480, 481, 482, 483, 484,
+ 485, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 487, 488, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 490, 491, 495, 377, 377,
+ 377, 377, 382, 1418, 1419, 1420, 492, 493,
+ 494, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 497, 502, 504, 506, 508, 512, 498,
+ 499, 500, 501, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 503, 377, 377, 377, 377,
+ 382, 1418, 1419, 1420, 505, 377, 377, 377,
+ 377, 382, 1418, 1419, 1420, 507, 377, 377,
+ 377, 377, 382, 1418, 1419, 1420, 509, 510,
+ 511, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 513, 514, 377, 377, 377, 377, 382,
+ 1418, 1419, 1420, 516, 519, 521, 517, 518,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 520, 377, 377, 377, 377, 382, 1418, 1419,
+ 1420, 522, 523, 524, 377, 377, 377, 524,
+ 377, 382, 1418, 1419, 1420, 526, 527, 377,
+ 377, 377, 377, 382, 1418, 1419, 1420, 529,
+ 530, 531, 532, 533, 377, 377, 377, 377,
+ 382, 1418, 1419, 1420, 535, 536, 537, 377,
+ 377, 377, 377, 382, 1418, 1419, 1420, 539,
+ 377, 377, 377, 377, 382, 1418, 1419, 1420,
+ 0, 541, 541, 541, 541, 548, 0, 542,
+ 543, 543, 543, 542, 543, 547, 0, 543,
+ 543, 543, 544, 543, 547, 545, 545, 545,
+ 545, 545, 546, 1421, 1422, 1423, 546, 545,
+ 547, 543, 548, 541, 550, 550, 550, 550,
+ 558, 551, 557, 1424, 1424, 1424, 557, 1425,
+ 1424, 1428, 552, 552, 552, 553, 552, 556,
+ 554, 554, 554, 554, 554, 555, 1425, 1426,
+ 1427, 555, 554, 556, 552, 552, 552, 552,
+ 552, 556, 558, 550, 0, 560, 564, 567,
+ 580, 584, 599, 561, 562, 563, 1429, 565,
+ 566, 1429, 568, 569, 570, 575, 571, 572,
+ 573, 574, 1429, 576, 577, 578, 579, 1429,
+ 581, 582, 0, 583, 1429, 583, 585, 586,
+ 587, 588, 589, 590, 591, 592, 593, 594,
+ 595, 596, 597, 598, 1429, 600, 601, 602,
+ 1429, 0, 604, 604, 604, 604, 611, 1430,
+ 1431, 1432, 604, 604, 604, 605, 612, 628,
+ 653, 659, 674, 689, 604, 611, 1430, 1431,
+ 1432, 606, 607, 608, 609, 610, 697, 604,
+ 604, 604, 604, 611, 1430, 1431, 1432, 611,
+ 604, 613, 614, 615, 616, 623, 617, 618,
+ 619, 622, 620, 620, 621, 617, 604, 604,
+ 604, 604, 611, 1430, 1431, 1432, 624, 625,
+ 627, 626, 626, 604, 604, 604, 623, 604,
+ 611, 1430, 1431, 1432, 629, 630, 631, 642,
+ 632, 633, 634, 635, 636, 0, 637, 640,
+ 638, 621, 639, 638, 638, 604, 604, 604,
+ 641, 640, 604, 611, 1430, 1431, 1432, 640,
+ 643, 644, 645, 646, 647, 648, 651, 649,
+ 621, 650, 649, 649, 604, 604, 604, 652,
+ 651, 604, 611, 1430, 1431, 1432, 651, 654,
+ 655, 0, 656, 604, 604, 604, 656, 657,
+ 604, 611, 1430, 1431, 1432, 658, 660, 661,
+ 662, 663, 664, 665, 666, 667, 668, 669,
+ 672, 670, 621, 671, 670, 604, 604, 604,
+ 673, 604, 611, 1430, 1431, 1432, 672, 675,
+ 676, 677, 678, 679, 680, 681, 682, 683,
+ 684, 685, 686, 687, 688, 604, 604, 604,
+ 604, 611, 1430, 1431, 1432, 690, 691, 692,
+ 693, 694, 696, 695, 621, 695, 604, 604,
+ 604, 696, 604, 611, 1430, 1431, 1432, 698,
+ 621, 0, 700, 701, 716, 755, 763, 776,
+ 1433, 700, 702, 703, 1433, 704, 1433, 705,
+ 706, 707, 708, 709, 710, 711, 712, 713,
+ 714, 715, 1433, 717, 746, 718, 724, 719,
+ 720, 721, 722, 723, 1433, 725, 726, 727,
+ 728, 737, 729, 730, 731, 732, 733, 734,
+ 735, 736, 1433, 738, 739, 740, 741, 742,
+ 743, 744, 745, 1433, 747, 752, 748, 749,
+ 750, 751, 1433, 753, 754, 1433, 756, 757,
+ 758, 759, 760, 761, 762, 1433, 764, 765,
+ 766, 767, 768, 769, 770, 773, 771, 772,
+ 1433, 774, 775, 1433, 777, 778, 779, 782,
+ 780, 781, 1433, 783, 784, 785, 797, 800,
+ 1433, 786, 787, 788, 789, 790, 791, 792,
+ 793, 794, 795, 796, 1433, 798, 799, 1433,
+ 801, 802, 1433, 0, 804, 805, 811, 828,
+ 831, 837, 841, 1434, 804, 806, 807, 808,
+ 809, 810, 1434, 812, 818, 824, 813, 814,
+ 815, 816, 817, 1434, 819, 821, 820, 1434,
+ 822, 823, 1434, 825, 826, 827, 1434, 829,
+ 830, 1434, 832, 834, 833, 1434, 835, 836,
+ 1434, 838, 839, 840, 1434, 842, 843, 1434,
+ 0, 845, 1435, 845, 847, 1436, 849, 850,
+ 850, 850, 850, 879, 850, 850, 850, 851,
+ 850, 879, 852, 852, 852, 852, 878, 0,
+ 852, 852, 852, 853, 852, 878, 854, 854,
+ 854, 853, 854, 877, 854, 854, 854, 855,
+ 854, 877, 0, 856, 856, 856, 855, 874,
+ 874, 874, 874, 874, 856, 873, 856, 856,
+ 856, 857, 856, 873, 858, 858, 858, 857,
+ 870, 870, 870, 870, 870, 858, 869, 858,
+ 858, 858, 859, 858, 869, 860, 860, 860,
+ 859, 866, 866, 866, 866, 866, 860, 865,
+ 860, 860, 860, 861, 860, 865, 1437, 861,
+ 862, 862, 862, 862, 862, 863, 863, 864,
+ 864, 864, 864, 864, 1437, 863, 865, 860,
+ 867, 867, 868, 868, 868, 868, 868, 860,
+ 860, 860, 867, 860, 865, 869, 858, 871,
+ 871, 872, 872, 872, 872, 872, 858, 858,
+ 858, 871, 858, 869, 873, 856, 875, 875,
+ 876, 876, 876, 876, 876, 856, 856, 856,
+ 875, 856, 873, 877, 854, 878, 852, 879,
+ 850, 881, 882, 882, 882, 882, 884, 883,
+ 882, 882, 882, 882, 884, 1438, 884, 882,
+ 886, 887, 887, 887, 887, 889, 887, 887,
+ 887, 888, 887, 889, 1439, 889, 887, 891,
+ 892, 892, 892, 891, 892, 894, 892, 892,
+ 892, 893, 892, 894, 1440, 894, 892, 896,
+ 0, 897, 897, 897, 897, 898, 1441, 1442,
+ 1443, 896, 897, 897, 897, 897, 898, 1441,
+ 1442, 1443, 898, 897, 900, 1444, 900, 0,
+ 902, 903, 903, 903, 902, 903, 955, 903,
+ 903, 903, 904, 908, 950, 903, 955, 905,
+ 905, 905, 904, 905, 954, 905, 905, 905,
+ 906, 905, 954, 907, 907, 907, 952, 906,
+ 907, 951, 907, 907, 907, 907, 951, 909,
+ 909, 909, 909, 949, 910, 911, 911, 911,
+ 910, 911, 948, 911, 911, 911, 912, 916,
+ 943, 911, 948, 913, 913, 913, 912, 913,
+ 947, 913, 913, 913, 914, 913, 947, 915,
+ 915, 915, 945, 914, 915, 944, 915, 915,
+ 915, 915, 944, 917, 917, 917, 917, 942,
+ 918, 919, 919, 920, 920, 920, 939, 919,
+ 941, 920, 938, 1445, 1452, 1453, 920, 920,
+ 920, 921, 920, 938, 1445, 1452, 1453, 922,
+ 922, 922, 935, 921, 937, 922, 934, 1445,
+ 1450, 1451, 922, 922, 922, 923, 922, 934,
+ 1450, 1451, 924, 924, 924, 931, 923, 933,
+ 924, 930, 1445, 1448, 1449, 924, 924, 924,
+ 925, 924, 930, 1448, 1449, 926, 926, 926,
+ 928, 925, 926, 927, 1445, 1446, 1447, 926,
+ 926, 926, 926, 927, 1446, 1447, 927, 926,
+ 926, 926, 926, 929, 926, 927, 1445, 1446,
+ 1447, 929, 930, 924, 924, 924, 924, 932,
+ 933, 924, 930, 1445, 1448, 1449, 932, 934,
+ 922, 922, 922, 922, 936, 937, 922, 934,
+ 1445, 1450, 1451, 936, 938, 920, 920, 920,
+ 920, 940, 941, 920, 938, 1445, 1452, 1453,
+ 940, 942, 917, 917, 917, 917, 917, 942,
+ 944, 915, 915, 915, 915, 946, 915, 944,
+ 946, 947, 913, 948, 911, 949, 909, 909,
+ 909, 909, 909, 949, 951, 907, 907, 907,
+ 907, 953, 907, 951, 953, 954, 905, 955,
+ 903, 957, 958, 958, 958, 957, 958, 966,
+ 958, 958, 958, 959, 958, 966, 960, 960,
+ 960, 959, 960, 965, 960, 960, 960, 961,
+ 960, 965, 962, 962, 962, 961, 962, 964,
+ 962, 962, 962, 963, 962, 964, 1454, 964,
+ 962, 965, 960, 966, 958, 968, 969, 969,
+ 969, 968, 969, 983, 969, 969, 969, 970,
+ 969, 983, 971, 971, 971, 970, 971, 982,
+ 972, 971, 971, 971, 971, 982, 973, 973,
+ 973, 973, 981, 974, 973, 973, 973, 973,
+ 981, 975, 975, 975, 975, 980, 976, 975,
+ 975, 975, 975, 980, 977, 977, 977, 977,
+ 979, 977, 977, 977, 978, 977, 979, 1455,
+ 979, 977, 980, 975, 981, 973, 982, 971,
+ 983, 969, 985, 986, 986, 986, 986, 994,
+ 987, 988, 988, 988, 987, 988, 993, 988,
+ 988, 988, 989, 988, 993, 990, 990, 990,
+ 990, 992, 991, 1456, 992, 990, 993, 988,
+ 994, 986, 0, 995, 996, 995, 995, 997,
+ 1003, 995, 1002, 1457, 1458, 1459, 997, 1003,
+ 998, 0, 999, 999, 1000, 1001, 995, 995,
+ 995, 1001, 995, 1002, 1457, 1458, 1459, 1002,
+ 995, 1004, 1005, 1005, 1000, 1007, 1008, 1008,
+ 1008, 1007, 1008, 1018, 1008, 1008, 1008, 1009,
+ 1008, 1018, 1010, 1010, 1010, 1010, 1017, 1011,
+ 1012, 1012, 1012, 1011, 1012, 1016, 0, 1012,
+ 1012, 1012, 1013, 1012, 1016, 1014, 1014, 1014,
+ 1014, 1014, 1015, 1460, 1461, 1462, 1015, 1014,
+ 1016, 1012, 1017, 1010, 1018, 1008, 1020, 1021,
+ 1021, 1021, 1020, 1021, 1028, 1021, 1021, 1021,
+ 1022, 1021, 1028, 1023, 1023, 1023, 1022, 1023,
+ 1027, 1023, 1023, 1023, 1024, 1023, 1027, 1025,
+ 1025, 1025, 1025, 1025, 1026, 1463, 1464, 1465,
+ 1026, 1025, 1027, 1023, 1028, 1021, 1030, 1031,
+ 1031, 1031, 1030, 1031, 1067, 0, 1031, 1031,
+ 1031, 1032, 1046, 1053, 1060, 1031, 1067, 1033,
+ 1033, 1033, 1033, 1045, 0, 1034, 1035, 1035,
+ 1035, 1034, 1035, 1044, 1035, 1035, 1035, 1036,
+ 1035, 1044, 0, 1037, 1037, 1037, 1040, 1040,
+ 1040, 1042, 1042, 1042, 1466, 1037, 1039, 1469,
+ 1470, 1040, 1041, 1467, 1468, 1042, 1043, 1471,
+ 1472, 0, 1038, 1039, 1037, 1041, 1040, 1043,
+ 1042, 1044, 1035, 1045, 1033, 1047, 1047, 1047,
+ 1047, 1052, 1048, 1049, 1049, 1049, 1048, 1049,
+ 1051, 0, 1049, 1049, 1049, 1050, 1049, 1051,
+ 0, 1050, 1037, 1037, 1037, 1040, 1040, 1040,
+ 1042, 1042, 1042, 1466, 1037, 1039, 1469, 1470,
+ 1040, 1041, 1467, 1468, 1042, 1043, 1471, 1472,
+ 1051, 1049, 1052, 1047, 1054, 1054, 1054, 1054,
+ 1059, 1055, 1056, 1056, 1056, 1055, 1056, 1058,
+ 1056, 1056, 1056, 1057, 1056, 1058, 1057, 1037,
+ 1037, 1037, 1040, 1040, 1040, 1042, 1042, 1042,
+ 1466, 1037, 1039, 1469, 1470, 1040, 1041, 1467,
+ 1468, 1042, 1043, 1471, 1472, 1058, 1056, 1059,
+ 1054, 1061, 1061, 1061, 1061, 1066, 1062, 1063,
+ 1063, 1063, 1062, 1063, 1065, 1063, 1063, 1063,
+ 1064, 1063, 1065, 1037, 1037, 1037, 1040, 1040,
+ 1040, 1042, 1042, 1042, 1466, 1037, 1039, 1469,
+ 1470, 1040, 1041, 1467, 1468, 1042, 1043, 1471,
+ 1472, 1065, 1063, 1066, 1061, 1067, 1031, 0,
+ 1069, 1103, 1124, 1138, 1145, 1154, 1162, 1166,
+ 1174, 1180, 1196, 1206, 1209, 1216, 1235, 1245,
+ 1248, 1070, 1070, 1070, 1094, 1097, 1101, 1070,
+ 1093, 1070, 1070, 1070, 1071, 1070, 1093, 1072,
+ 1072, 1072, 1072, 1092, 1073, 1074, 1074, 1074,
+ 1073, 1074, 1091, 1074, 1074, 1074, 1075, 1074,
+ 1091, 1076, 1076, 1076, 1075, 1076, 1090, 0,
+ 1076, 1076, 1076, 1077, 1076, 1090, 1078, 1078,
+ 1078, 1077, 1078, 1089, 1078, 1078, 1078, 1079,
+ 1078, 1089, 1080, 1080, 1080, 1079, 1080, 1088,
+ 1080, 1080, 1080, 1081, 1080, 1088, 1082, 1082,
+ 1082, 1081, 1082, 1087, 1082, 1082, 1082, 1083,
+ 1082, 1087, 1084, 1084, 1084, 1084, 1086, 1084,
+ 1084, 1084, 1085, 1084, 1086, 1473, 1086, 1084,
+ 1087, 1082, 1088, 1080, 1089, 1078, 1090, 1076,
+ 1091, 1074, 1092, 1072, 1093, 1070, 1095, 1096,
+ 1070, 1070, 1070, 1070, 1093, 1098, 1099, 1100,
+ 1070, 1070, 1070, 1070, 1093, 1102, 1070, 1070,
+ 1070, 1070, 1093, 1104, 1106, 1113, 1116, 1120,
+ 1105, 1070, 1070, 1070, 1070, 1093, 1107, 1112,
+ 1108, 1109, 1110, 1111, 1070, 1070, 1070, 1070,
+ 1093, 1070, 1070, 1070, 1070, 1093, 1114, 1115,
+ 1070, 1070, 1070, 1070, 1093, 1117, 1118, 1119,
+ 1070, 1070, 1070, 1070, 1093, 1121, 1122, 1123,
+ 1070, 1070, 1070, 1070, 1093, 1125, 1129, 1137,
+ 1126, 1127, 1128, 1070, 1070, 1070, 1070, 1093,
+ 1130, 1133, 1131, 1132, 1070, 1070, 1070, 1070,
+ 1093, 1134, 1135, 1136, 1070, 1070, 1070, 1070,
+ 1093, 1070, 1070, 1070, 1070, 1093, 1139, 1140,
+ 1141, 1143, 1142, 1070, 1070, 1070, 1070, 1093,
+ 1144, 1070, 1070, 1070, 1070, 1093, 1146, 1150,
+ 1147, 1148, 1149, 1070, 1070, 1070, 1070, 1093,
+ 1151, 1152, 1153, 1070, 1070, 1070, 1070, 1093,
+ 1155, 1156, 1157, 1158, 1159, 1160, 1161, 1070,
+ 1070, 1070, 1070, 1093, 1163, 1165, 1164, 1070,
+ 1070, 1070, 1070, 1093, 1070, 1070, 1070, 1070,
+ 1093, 1167, 1169, 1171, 1173, 1168, 1070, 1070,
+ 1070, 1070, 1093, 1170, 1070, 1070, 1070, 1070,
+ 1093, 1172, 1070, 1070, 1070, 1070, 1093, 1070,
+ 1070, 1070, 1070, 1093, 1175, 1179, 1176, 1177,
+ 1178, 1070, 1070, 1070, 1070, 1093, 1070, 1070,
+ 1070, 1070, 1093, 1181, 1185, 1187, 1182, 1183,
+ 1184, 1070, 1070, 1070, 1070, 1093, 1186, 1070,
+ 1070, 1070, 1070, 1093, 1070, 1070, 1070, 1188,
+ 1070, 1093, 1189, 1070, 1070, 1070, 1190, 1070,
+ 1093, 1070, 1070, 1070, 1191, 1070, 1093, 1192,
+ 1193, 1194, 1195, 1070, 1070, 1070, 1070, 1093,
+ 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204,
+ 1205, 1070, 1070, 1070, 1070, 1093, 1207, 1208,
+ 1070, 1070, 1070, 1070, 1093, 1210, 1211, 1215,
+ 1070, 1070, 1070, 1070, 1093, 1212, 1213, 1214,
+ 1070, 1070, 1070, 1070, 1093, 1070, 1070, 1070,
+ 1070, 1093, 1217, 1222, 1224, 1226, 1228, 1232,
+ 1218, 1219, 1220, 1221, 1070, 1070, 1070, 1070,
+ 1093, 1223, 1070, 1070, 1070, 1070, 1093, 1225,
+ 1070, 1070, 1070, 1070, 1093, 1227, 1070, 1070,
+ 1070, 1070, 1093, 1229, 1230, 1231, 1070, 1070,
+ 1070, 1070, 1093, 1233, 1234, 1070, 1070, 1070,
+ 1070, 1093, 1236, 1239, 1241, 1237, 1238, 1070,
+ 1070, 1070, 1070, 1093, 1240, 1070, 1070, 1070,
+ 1070, 1093, 1242, 1243, 0, 1244, 1070, 1070,
+ 1070, 1244, 1070, 1093, 1246, 1247, 1070, 1070,
+ 1070, 1070, 1093, 1249, 1250, 1251, 1252, 1253,
+ 1070, 1070, 1070, 1070, 1093, 1255, 1256, 1474,
+ 1258, 1259, 1259, 1259, 1258, 1259, 1267, 1259,
+ 1259, 1259, 1260, 1259, 1267, 1261, 1261, 1261,
+ 1260, 1261, 1266, 1261, 1261, 1261, 1262, 1261,
+ 1266, 1263, 1263, 1263, 1263, 1265, 1264, 1475,
+ 1265, 1263, 1266, 1261, 1267, 1259, 1269, 1476,
+ 1271, 1272, 1272, 1272, 1271, 1272, 1298, 1272,
+ 1272, 1272, 1273, 1272, 1298, 1274, 1274, 1274,
+ 1273, 1274, 1297, 1274, 1274, 1274, 1275, 1274,
+ 1297, 1276, 1276, 1276, 1275, 1276, 1296, 1276,
+ 1276, 1276, 1277, 1294, 1276, 1296, 1278, 1278,
+ 1278, 1278, 1293, 0, 1278, 1278, 1278, 1279,
+ 1278, 1293, 1280, 1281, 1291, 1282, 1283, 1290,
+ 1284, 1288, 1285, 1286, 1286, 1287, 1279, 1477,
+ 1289, 1292, 1293, 1278, 1295, 1294, 1296, 1276,
+ 1297, 1274, 1298, 1272, 1300, 1301, 1301, 1301,
+ 1300, 1301, 1311, 1301, 1301, 1301, 1302, 1301,
+ 1311, 1303, 1303, 1303, 1302, 1303, 1310, 1303,
+ 1303, 1303, 1304, 1303, 1310, 1305, 1305, 1305,
+ 1304, 1305, 1309, 1305, 1305, 1305, 1306, 1307,
+ 1305, 1309, 1478, 1308, 1307, 1309, 1305, 1310,
+ 1303, 1311, 1301, 1313, 1314, 1314, 1314, 1313,
+ 1314, 1324, 1314, 1314, 1314, 1315, 1314, 1324,
+ 1316, 1316, 1316, 1315, 1316, 1323, 1316, 1316,
+ 1316, 1317, 1316, 1323, 1318, 1318, 1318, 1317,
+ 1318, 1322, 1318, 1318, 1318, 1319, 1318, 1322,
+ 1320, 1320, 1320, 1320, 1320, 1321, 1479, 1480,
+ 1481, 1321, 1320, 1322, 1318, 1323, 1316, 1324,
+ 1314, 1326, 1327, 1327, 1327, 1326, 1327, 1330,
+ 1327, 1327, 1327, 1328, 1327, 1330, 1329, 1328,
+ 1482, 1330, 1327, 1332, 1333, 1333, 1333, 1332,
+ 1333, 1343, 1333, 1333, 1333, 1334, 1333, 1343,
+ 1335, 1335, 1335, 1334, 1335, 1342, 1335, 1335,
+ 1335, 1336, 1335, 1342, 1337, 1337, 1337, 1336,
+ 1337, 1341, 1337, 1337, 1337, 1338, 1337, 1341,
+ 1339, 1339, 1339, 1339, 1339, 1340, 1483, 1484,
+ 1485, 1340, 1339, 1341, 1337, 1342, 1335, 1343,
+ 1333, 1345, 1346, 1346, 1346, 1345, 1346, 1348,
+ 1346, 1346, 1346, 1347, 1346, 1348, 1486, 1347,
+ 1348, 1346, 1350, 1351, 1351, 1351, 1350, 1351,
+ 1361, 1351, 1351, 1351, 1352, 1351, 1361, 1353,
+ 1354, 1355, 0, 1356, 1357, 1358, 1359, 1360,
+ 0, 1487, 1361, 1351, 1363, 1364, 0, 1365,
+ 1366, 1367, 1488, 1369, 1370, 1371, 1372, 1373,
+ 1489, 1375, 1376, 1376, 1376, 1375, 1376, 1381,
+ 1376, 1376, 1376, 1377, 1376, 1381, 1378, 1378,
+ 1378, 1377, 1378, 1380, 1379, 1378, 1378, 1378,
+ 1378, 1380, 1490, 1380, 1378, 1381, 1376, 1383,
+ 1384, 1384, 1384, 1383, 1384, 1389, 1385, 1384,
+ 1384, 1384, 1384, 1389, 1386, 1386, 1386, 1386,
+ 1388, 1387, 1386, 1386, 1386, 1386, 1388, 1491,
+ 1388, 1386, 1389, 1384, 1391, 1392, 1392, 1392,
+ 1391, 1392, 1395, 1392, 1392, 1392, 1393, 1392,
+ 1395, 1394, 1492, 1395, 1392, 0, 1, 184,
+ 1, 1, 192, 1396, 183, 1401, 302, 14,
+ 228, 14, 14, 238, 260, 1404, 275, 1402,
+ 276, 1405, 278, 0, 174, 174, 174, 202,
+ 286, 287, 288, 289, 304, 291, 292, 293,
+ 294, 295, 296, 297, 298, 299, 300, 301,
+ 1399, 303, 0, 195, 202, 286, 287, 288,
+ 289, 290, 291, 292, 293, 294, 295, 296,
+ 297, 298, 299, 300, 301, 0, 1, 184,
+ 1, 1, 192, 1396, 183, 1401, 302, 1,
+ 1, 1401, 302, 206, 206, 206, 1401, 207,
+ 210, 210, 210, 227, 264, 0, 239, 242,
+ 244, 245, 246, 247, 248, 249, 250, 251,
+ 252, 253, 254, 255, 256, 257, 258, 259,
+ 1401
+};
+
+static const short _zone_scanner_trans_actions[] = {
+ 2709, 0, 5, 7, 631, 802, 802, 802,
+ 802, 802, 802, 802, 802, 802, 802, 802,
+ 802, 802, 802, 802, 802, 802, 3, 9,
+ 3, 9, 2885, 103, 820, 823, 45, 59,
+ 57, 55, 0, 61, 817, 826, 413, 0,
+ 5, 7, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 3, 9, 2263, 2266, 3393,
+ 3397, 0, 0, 0, 3389, 3401, 3409, 5417,
+ 5437, 407, 0, 405, 5, 7, 411, 3,
+ 9, 421, 448, 2248, 13, 469, 472, 2553,
+ 475, 15, 0, 5, 7, 2410, 9, 11,
+ 2525, 2257, 409, 11, 463, 13, 430, 436,
+ 445, 5412, 3369, 2505, 0, 5, 7, 9,
+ 3377, 2254, 448, 11, 2521, 0, 4567, 5,
+ 7, 2697, 3017, 3017, 3017, 3017, 3017, 3017,
+ 3017, 3017, 3017, 3017, 3017, 3017, 3017, 3017,
+ 3017, 3017, 3017, 3, 9, 2251, 2254, 421,
+ 448, 6098, 829, 3021, 3025, 4812, 3029, 2260,
+ 0, 5, 7, 424, 9, 0, 0, 0,
+ 0, 0, 0, 2401, 4297, 4301, 4293, 4305,
+ 4309, 5882, 5887, 460, 2509, 2513, 2517, 11,
+ 451, 454, 457, 0, 0, 0, 0, 0,
+ 0, 2365, 4057, 4061, 4053, 4065, 4069, 5762,
+ 5767, 2362, 4037, 4041, 4033, 4045, 4049, 5752,
+ 5757, 0, 0, 2320, 3757, 3761, 3753, 3765,
+ 3769, 5612, 5617, 0, 0, 0, 2272, 3437,
+ 3441, 3433, 3445, 3449, 5452, 5457, 0, 0,
+ 0, 2371, 4097, 4101, 4093, 4105, 4109, 5782,
+ 5787, 0, 0, 0, 0, 0, 0, 2347,
+ 3937, 3941, 3933, 3945, 3949, 5702, 5707, 0,
+ 0, 0, 0, 2323, 3777, 3781, 3773, 3785,
+ 3789, 5622, 5627, 0, 0, 0, 2344, 3917,
+ 3921, 3913, 3925, 3929, 5692, 5697, 2329, 3817,
+ 3821, 3813, 3825, 3829, 5642, 5647, 0, 0,
+ 0, 0, 0, 2392, 4237, 4241, 4233, 4245,
+ 4249, 5852, 5857, 0, 2395, 4257, 4261, 4253,
+ 4265, 4269, 5862, 5867, 0, 0, 0, 0,
+ 0, 2281, 3497, 3501, 3493, 3505, 3509, 5482,
+ 5487, 0, 0, 0, 2407, 4337, 4341, 4333,
+ 4345, 4349, 5902, 5907, 0, 0, 101, 808,
+ 811, 805, 814, 0, 5, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 3,
+ 9, 0, 0, 0, 0, 0, 0, 2335,
+ 3857, 3861, 3853, 3865, 3869, 5662, 5667, 0,
+ 0, 0, 2302, 3637, 3641, 3633, 3645, 3649,
+ 5552, 5557, 2317, 3737, 3741, 3733, 3745, 3749,
+ 5602, 5607, 0, 0, 0, 0, 0, 2383,
+ 4177, 4181, 4173, 4185, 4189, 5822, 5827, 0,
+ 2386, 4197, 4201, 4193, 4205, 4209, 5832, 5837,
+ 0, 2308, 3677, 3681, 3673, 3685, 3689, 5572,
+ 5577, 2389, 4217, 4221, 4213, 4225, 4229, 5842,
+ 5847, 0, 0, 0, 0, 0, 2284, 3517,
+ 3521, 3513, 3525, 3529, 5492, 5497, 2287, 3537,
+ 3541, 3533, 3545, 3549, 5502, 5507, 0, 0,
+ 0, 0, 0, 0, 2314, 3717, 3721, 3713,
+ 3725, 3729, 5592, 5597, 0, 2380, 4157, 4161,
+ 4153, 4165, 4169, 5812, 5817, 2269, 3417, 3421,
+ 0, 3413, 3425, 3429, 5442, 5447, 0, 2341,
+ 3897, 3901, 0, 3893, 3905, 3909, 5682, 5687,
+ 2350, 3957, 3961, 0, 3953, 3965, 3969, 5712,
+ 5717, 0, 0, 0, 0, 2353, 3977, 3981,
+ 3973, 3985, 3989, 5722, 5727, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 2368, 4077,
+ 4081, 4073, 4085, 4089, 5772, 5777, 0, 0,
+ 2278, 3477, 3481, 3473, 3485, 3489, 5472, 5477,
+ 0, 0, 0, 2293, 3577, 3581, 3573, 3585,
+ 3589, 5522, 5527, 0, 0, 0, 2338, 3877,
+ 3881, 3873, 3885, 3889, 5672, 5677, 2299, 3617,
+ 3621, 3613, 3625, 3629, 5542, 5547, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2359, 4017, 4021, 4013, 4025, 4029, 5742, 5747,
+ 0, 2275, 3457, 3461, 3453, 3465, 3469, 5462,
+ 5467, 0, 2377, 4137, 4141, 4133, 4145, 4149,
+ 5802, 5807, 0, 2311, 3697, 3701, 3693, 3705,
+ 3709, 5582, 5587, 0, 0, 0, 2332, 3837,
+ 3841, 3833, 3845, 3849, 5652, 5657, 0, 0,
+ 2404, 4317, 4321, 4313, 4325, 4329, 5892, 5897,
+ 0, 0, 0, 0, 0, 2356, 3997, 4001,
+ 3993, 4005, 4009, 5732, 5737, 0, 2290, 3557,
+ 3561, 3553, 3565, 3569, 5512, 5517, 0, 0,
+ 649, 631, 2713, 721, 2861, 2865, 45, 2857,
+ 2869, 2873, 4752, 4757, 0, 0, 2398, 4277,
+ 4281, 4273, 4285, 4289, 5872, 5877, 0, 0,
+ 0, 0, 0, 2374, 4117, 4121, 4113, 4125,
+ 4129, 5792, 5797, 11, 463, 0, 5, 7,
+ 3, 9, 0, 101, 808, 811, 805, 814,
+ 0, 5, 7, 631, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 99, 99, 3, 9, 103,
+ 820, 823, 45, 59, 57, 55, 0, 61,
+ 817, 826, 2889, 45, 59, 57, 55, 0,
+ 61, 742, 2929, 2933, 4767, 2925, 2937, 11,
+ 463, 11, 463, 95, 91, 91, 91, 0,
+ 0, 0, 0, 0, 0, 89, 41, 39,
+ 577, 580, 574, 583, 0, 5, 7, 3,
+ 9, 11, 0, 463, 4552, 589, 2625, 2633,
+ 2617, 2641, 0, 5, 7, 3, 9, 11,
+ 463, 2889, 45, 59, 57, 55, 0, 61,
+ 742, 2929, 2933, 4767, 2925, 2937, 11, 463,
+ 2685, 2653, 4507, 4522, 4492, 4537, 5966, 5990,
+ 0, 5, 7, 3, 9, 421, 448, 3385,
+ 3405, 5422, 5427, 6182, 5432, 6354, 6194, 6347,
+ 6188, 0, 5, 7, 424, 2457, 0, 5,
+ 7, 424, 9, 11, 2521, 478, 2529, 2537,
+ 4402, 2545, 11, 427, 433, 442, 0, 5,
+ 7, 9, 448, 11, 2521, 3361, 0, 0,
+ 0, 0, 0, 83, 0, 0, 79, 2677,
+ 586, 2621, 2629, 4482, 2637, 6242, 2645, 4497,
+ 4512, 5960, 4527, 0, 5, 7, 424, 9,
+ 11, 2521, 4557, 4542, 5972, 5978, 6228, 5984,
+ 6377, 6235, 0, 5, 7, 424, 2457, 2649,
+ 598, 4502, 4517, 4487, 4532, 13, 430, 436,
+ 2429, 445, 460, 4377, 2509, 2513, 2517, 11,
+ 451, 454, 457, 460, 2509, 2513, 2517, 11,
+ 451, 454, 457, 0, 0, 2305, 3657, 3661,
+ 3653, 3665, 3669, 5562, 5567, 0, 0, 0,
+ 2296, 3597, 3601, 3593, 3605, 3609, 5532, 5537,
+ 0, 2326, 3797, 3801, 3793, 3805, 3809, 5632,
+ 5637, 11, 463, 478, 2529, 2537, 4412, 11,
+ 427, 433, 4353, 460, 2509, 2513, 11, 451,
+ 454, 460, 2509, 2513, 11, 451, 454, 11,
+ 463, 13, 430, 436, 445, 2505, 460, 2509,
+ 2513, 11, 451, 454, 601, 11, 463, 11,
+ 463, 11, 2521, 0, 496, 3, 7, 490,
+ 2557, 17, 493, 31, 2581, 29, 0, 529,
+ 2561, 21, 23, 0, 520, 499, 19, 502,
+ 511, 25, 25, 4422, 505, 508, 27, 526,
+ 763, 67, 0, 0, 772, 775, 2953, 73,
+ 2949, 766, 71, 71, 2941, 2945, 4772, 769,
+ 67, 67, 0, 0, 775, 775, 73, 73,
+ 69, 1, 766, 71, 71, 2945, 2945, 769,
+ 769, 77, 0, 5, 7, 3, 9, 634,
+ 631, 4762, 13, 469, 472, 45, 59, 57,
+ 55, 0, 61, 4407, 475, 484, 0, 5,
+ 7, 2961, 9, 11, 4387, 2889, 2877, 45,
+ 59, 57, 55, 0, 61, 736, 2893, 2897,
+ 4767, 6104, 2901, 11, 463, 81, 0, 5,
+ 7, 3, 9, 4777, 784, 2965, 21, 23,
+ 0, 487, 523, 2565, 2569, 499, 19, 5912,
+ 2573, 0, 5, 7, 2969, 9, 11, 4392,
+ 502, 514, 25, 25, 505, 508, 27, 11,
+ 463, 0, 5, 7, 3, 9, 787, 85,
+ 793, 790, 2973, 2977, 6110, 2985, 2981, 799,
+ 0, 5, 7, 4782, 796, 2989, 2993, 9,
+ 9, 87, 21, 23, 0, 2577, 4427, 4432,
+ 499, 19, 6200, 4437, 0, 5, 7, 11,
+ 4397, 502, 517, 25, 25, 505, 508, 27,
+ 11, 4382, 0, 5, 7, 3, 9, 11,
+ 463, 11, 463, 127, 119, 121, 123, 0,
+ 125, 0, 5, 7, 3, 9, 1, 418,
+ 439, 11, 463, 363, 361, 2125, 2128, 2122,
+ 2131, 3321, 5377, 5382, 0, 5, 7, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 3, 9, 2134, 2445, 2493, 265, 1549, 1552,
+ 0, 0, 0, 1546, 1555, 3129, 4897, 4902,
+ 0, 0, 291, 1705, 1708, 1702, 1711, 3181,
+ 5027, 5032, 11, 463, 0, 0, 0, 0,
+ 0, 0, 355, 2089, 2092, 2086, 2095, 3309,
+ 5347, 5352, 0, 0, 0, 0, 0, 0,
+ 331, 1945, 1948, 1942, 1951, 3261, 5227, 5232,
+ 329, 1933, 1936, 1930, 1939, 3257, 5217, 5222,
+ 0, 0, 301, 1765, 1768, 1762, 1771, 3201,
+ 5077, 5082, 0, 0, 0, 269, 1573, 1576,
+ 1570, 1579, 3137, 4917, 4922, 0, 0, 0,
+ 335, 1969, 1972, 1966, 1975, 3269, 5247, 5252,
+ 0, 0, 0, 0, 0, 0, 319, 1873,
+ 1876, 1870, 1879, 3237, 5167, 5172, 0, 0,
+ 0, 0, 303, 1777, 1780, 1774, 1783, 3205,
+ 5087, 5092, 0, 0, 0, 317, 1861, 1864,
+ 1858, 1867, 3233, 5157, 5162, 307, 1801, 1804,
+ 1798, 1807, 3213, 5107, 5112, 0, 0, 0,
+ 0, 0, 349, 2053, 2056, 2050, 2059, 3297,
+ 5317, 5322, 0, 351, 2065, 2068, 2062, 2071,
+ 3301, 5327, 5332, 0, 0, 0, 0, 0,
+ 275, 1609, 1612, 1606, 1615, 3149, 4947, 4952,
+ 0, 0, 0, 359, 2113, 2116, 2110, 2119,
+ 3317, 5367, 5372, 0, 0, 0, 0, 0,
+ 0, 0, 311, 1825, 1828, 1822, 1831, 3221,
+ 5127, 5132, 0, 0, 0, 289, 1693, 1696,
+ 1690, 1699, 3177, 5017, 5022, 299, 1753, 1756,
+ 1750, 1759, 3197, 5067, 5072, 0, 0, 0,
+ 0, 0, 343, 2017, 2020, 2014, 2023, 3285,
+ 5287, 5292, 0, 345, 2029, 2032, 2026, 2035,
+ 3289, 5297, 5302, 0, 293, 1717, 1720, 1714,
+ 1723, 3185, 5037, 5042, 347, 2041, 2044, 2038,
+ 2047, 3293, 5307, 5312, 0, 0, 0, 0,
+ 0, 277, 1621, 1624, 1618, 1627, 3153, 4957,
+ 4962, 279, 1633, 1636, 1630, 1639, 3157, 4967,
+ 4972, 0, 0, 0, 0, 0, 0, 297,
+ 1741, 1744, 1738, 1747, 3193, 5057, 5062, 0,
+ 341, 2005, 2008, 2002, 2011, 3281, 5277, 5282,
+ 267, 1561, 1564, 0, 1558, 1567, 3133, 4907,
+ 4912, 0, 315, 1849, 1852, 0, 1846, 1855,
+ 3229, 5147, 5152, 321, 1885, 1888, 0, 1882,
+ 1891, 3241, 5177, 5182, 0, 0, 0, 0,
+ 323, 1897, 1900, 1894, 1903, 3245, 5187, 5192,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 333, 1957, 1960, 1954, 1963, 3265, 5237,
+ 5242, 0, 0, 273, 1597, 1600, 1594, 1603,
+ 3145, 4937, 4942, 0, 0, 0, 283, 1657,
+ 1660, 1654, 1663, 3165, 4987, 4992, 0, 0,
+ 0, 313, 1837, 1840, 1834, 1843, 3225, 5137,
+ 5142, 287, 1681, 1684, 1678, 1687, 3173, 5007,
+ 5012, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 327, 1921, 1924, 1918, 1927,
+ 3253, 5207, 5212, 0, 271, 1585, 1588, 1582,
+ 1591, 3141, 4927, 4932, 0, 339, 1993, 1996,
+ 1990, 1999, 3277, 5267, 5272, 0, 295, 1729,
+ 1732, 1726, 1735, 3189, 5047, 5052, 0, 0,
+ 0, 309, 1813, 1816, 1810, 1819, 3217, 5117,
+ 5122, 0, 0, 357, 2101, 2104, 2098, 2107,
+ 3313, 5357, 5362, 0, 0, 0, 0, 0,
+ 325, 1909, 1912, 1906, 1915, 3249, 5197, 5202,
+ 0, 281, 1645, 1648, 1642, 1651, 3161, 4977,
+ 4982, 0, 0, 631, 263, 1537, 1540, 45,
+ 1534, 1543, 3125, 4887, 4892, 0, 0, 353,
+ 2077, 2080, 2074, 2083, 3305, 5337, 5342, 0,
+ 0, 0, 0, 0, 337, 1981, 1984, 1978,
+ 1987, 3273, 5257, 5262, 0, 0, 0, 285,
+ 1669, 1672, 1666, 1675, 3169, 4997, 5002, 0,
+ 305, 1789, 1792, 1786, 1795, 3209, 5097, 5102,
+ 377, 0, 5, 7, 3, 9, 637, 631,
+ 53, 727, 730, 45, 724, 733, 3109, 0,
+ 5, 7, 115, 3, 9, 117, 0, 5,
+ 7, 3, 9, 865, 2441, 2489, 11, 463,
+ 11, 463, 11, 463, 0, 5, 7, 3,
+ 9, 631, 631, 415, 2417, 2421, 45, 1,
+ 2413, 2425, 0, 5, 7, 115, 3, 9,
+ 117, 0, 5, 7, 3, 9, 865, 2441,
+ 2489, 11, 463, 11, 463, 53, 727, 730,
+ 724, 733, 11, 463, 387, 393, 393, 393,
+ 393, 393, 393, 0, 0, 0, 919, 0,
+ 0, 931, 0, 0, 0, 0, 0, 0,
+ 0, 0, 928, 0, 0, 0, 0, 934,
+ 0, 0, 643, 631, 685, 45, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 922, 0, 0, 0,
+ 925, 385, 383, 2224, 2227, 2221, 2230, 3349,
+ 5387, 5392, 0, 5, 7, 389, 389, 389,
+ 389, 389, 389, 389, 3, 9, 2233, 2453,
+ 2501, 0, 0, 0, 157, 5407, 5407, 4452,
+ 5924, 5930, 5918, 5936, 6207, 6361, 6369, 11,
+ 463, 0, 0, 163, 0, 562, 562, 121,
+ 123, 0, 125, 0, 37, 119, 391, 2239,
+ 2242, 2236, 2245, 3353, 5397, 5402, 121, 123,
+ 0, 125, 0, 565, 2601, 2605, 119, 2597,
+ 2609, 4457, 5942, 5948, 0, 0, 0, 0,
+ 0, 0, 0, 0, 161, 835, 0, 2593,
+ 2593, 3061, 841, 105, 832, 4832, 6128, 6134,
+ 841, 105, 6122, 6140, 6319, 6385, 6393, 832,
+ 0, 0, 0, 0, 165, 0, 2593, 2593,
+ 3085, 844, 105, 832, 4847, 6152, 6158, 844,
+ 105, 6146, 6164, 6326, 6401, 6409, 832, 0,
+ 0, 640, 631, 2837, 4732, 4737, 45, 49,
+ 4727, 4742, 6080, 6305, 6312, 559, 0, 0,
+ 0, 0, 0, 0, 0, 0, 155, 0,
+ 3357, 3357, 568, 0, 395, 2613, 4467, 4472,
+ 0, 4462, 4477, 5954, 6214, 6221, 395, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 3117, 4867, 4872,
+ 4862, 4877, 6170, 6333, 6340, 0, 0, 0,
+ 159, 0, 4882, 4882, 49, 45, 703, 2845,
+ 2849, 45, 2841, 2853, 4747, 6086, 6092, 5407,
+ 2589, 397, 631, 0, 0, 0, 0, 0,
+ 670, 45, 0, 0, 874, 0, 877, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 883, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 895, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 898, 0, 0, 0, 0, 0,
+ 0, 0, 0, 901, 0, 0, 0, 0,
+ 0, 0, 904, 0, 0, 907, 0, 0,
+ 0, 0, 0, 0, 0, 910, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 913, 0, 0, 916, 0, 0, 0, 0,
+ 0, 0, 871, 0, 0, 0, 0, 0,
+ 880, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 886, 0, 0, 889,
+ 0, 0, 892, 399, 631, 0, 0, 0,
+ 0, 0, 0, 685, 45, 0, 0, 0,
+ 0, 0, 937, 0, 0, 0, 0, 0,
+ 0, 0, 0, 940, 0, 0, 0, 934,
+ 0, 0, 928, 0, 0, 0, 931, 0,
+ 0, 946, 0, 0, 0, 925, 0, 0,
+ 919, 0, 0, 0, 922, 0, 0, 943,
+ 838, 832, 3041, 105, 604, 607, 604, 43,
+ 613, 616, 610, 619, 0, 5, 7, 604,
+ 3, 9, 43, 613, 616, 610, 619, 646,
+ 0, 5, 7, 631, 3, 9, 51, 712,
+ 715, 45, 709, 718, 0, 5, 7, 631,
+ 3, 9, 2881, 51, 712, 715, 45, 59,
+ 57, 55, 0, 61, 709, 718, 0, 5,
+ 7, 631, 3, 9, 51, 712, 715, 45,
+ 59, 57, 55, 0, 61, 709, 718, 0,
+ 5, 7, 631, 3, 9, 51, 712, 715,
+ 45, 59, 57, 55, 0, 61, 709, 718,
+ 0, 5, 7, 631, 3, 9, 706, 45,
+ 59, 57, 55, 0, 61, 2889, 45, 59,
+ 57, 55, 0, 61, 2905, 4767, 11, 463,
+ 2889, 45, 59, 57, 55, 0, 61, 739,
+ 2913, 2917, 4767, 2909, 2921, 11, 463, 2889,
+ 45, 59, 57, 55, 0, 61, 739, 2913,
+ 2917, 4767, 2909, 2921, 11, 463, 2889, 45,
+ 59, 57, 55, 0, 61, 739, 2913, 2917,
+ 4767, 2909, 2921, 11, 463, 11, 463, 11,
+ 463, 532, 35, 547, 550, 544, 553, 532,
+ 0, 5, 7, 3, 9, 541, 11, 463,
+ 604, 43, 613, 616, 610, 619, 0, 5,
+ 7, 604, 3, 9, 607, 11, 463, 631,
+ 49, 691, 694, 45, 688, 697, 0, 5,
+ 7, 604, 3, 9, 607, 11, 463, 2957,
+ 781, 35, 547, 550, 544, 553, 2585, 4442,
+ 4447, 532, 0, 5, 7, 3, 9, 778,
+ 2437, 2485, 11, 463, 832, 3065, 105, 2212,
+ 3325, 365, 2140, 2143, 45, 2137, 2146, 0,
+ 5, 7, 631, 0, 0, 3, 9, 369,
+ 2164, 2167, 45, 2161, 2170, 0, 5, 7,
+ 4722, 3, 9, 652, 2745, 2749, 0, 45,
+ 2741, 2753, 0, 5, 7, 3, 9, 0,
+ 5, 7, 3, 9, 631, 367, 2152, 2155,
+ 45, 2149, 2158, 0, 5, 7, 631, 0,
+ 0, 3, 9, 371, 2176, 2179, 45, 2173,
+ 2182, 0, 5, 7, 4722, 3, 9, 655,
+ 2761, 2765, 0, 45, 2757, 2769, 0, 5,
+ 7, 3, 9, 0, 5, 7, 3, 9,
+ 0, 4717, 6176, 658, 2777, 2781, 0, 45,
+ 658, 2773, 2785, 4697, 6032, 6038, 0, 5,
+ 7, 4717, 3, 9, 2209, 2449, 2497, 661,
+ 2793, 2797, 0, 45, 661, 2789, 2801, 4702,
+ 6044, 6050, 0, 5, 7, 4717, 3, 9,
+ 2449, 2497, 664, 2809, 2813, 0, 45, 664,
+ 2805, 2817, 4707, 6056, 6062, 0, 5, 7,
+ 4717, 3, 9, 2449, 2497, 667, 2825, 2829,
+ 0, 45, 2821, 2833, 4712, 6068, 6074, 0,
+ 5, 7, 3, 9, 2449, 2497, 11, 463,
+ 2737, 4682, 4687, 4572, 4677, 4692, 6026, 6291,
+ 6298, 628, 11, 463, 2733, 4662, 4667, 4572,
+ 2733, 4657, 4672, 6020, 6277, 6284, 628, 11,
+ 463, 2729, 4642, 4647, 4572, 2729, 4637, 4652,
+ 6014, 6263, 6270, 628, 11, 463, 2725, 4622,
+ 4627, 4572, 2725, 4617, 4632, 6008, 6249, 6256,
+ 628, 11, 463, 375, 2200, 2203, 2197, 2206,
+ 11, 463, 2721, 4602, 4607, 4572, 4597, 4612,
+ 628, 11, 463, 11, 463, 11, 463, 373,
+ 2188, 2191, 2185, 2194, 11, 463, 2717, 4582,
+ 4587, 4572, 4577, 4592, 628, 11, 463, 11,
+ 463, 631, 49, 691, 694, 45, 688, 697,
+ 0, 5, 7, 631, 3, 9, 49, 691,
+ 694, 45, 688, 697, 0, 5, 7, 631,
+ 3, 9, 49, 691, 694, 45, 688, 697,
+ 0, 5, 7, 604, 3, 9, 607, 11,
+ 463, 11, 463, 11, 463, 631, 49, 691,
+ 694, 45, 688, 697, 0, 5, 7, 631,
+ 3, 9, 49, 691, 694, 45, 688, 697,
+ 532, 0, 5, 7, 3, 9, 35, 547,
+ 550, 544, 553, 532, 0, 5, 7, 3,
+ 9, 35, 547, 550, 544, 553, 532, 0,
+ 5, 7, 3, 9, 35, 547, 550, 544,
+ 553, 0, 5, 7, 604, 3, 9, 607,
+ 11, 463, 11, 463, 11, 463, 11, 463,
+ 11, 463, 403, 0, 5, 7, 3, 9,
+ 631, 49, 691, 694, 45, 688, 697, 0,
+ 5, 7, 401, 3, 9, 0, 5, 7,
+ 3, 9, 129, 1, 11, 463, 11, 463,
+ 11, 463, 859, 0, 847, 5, 7, 850,
+ 853, 3, 9, 1, 418, 439, 111, 113,
+ 0, 3033, 832, 105, 107, 631, 856, 3097,
+ 3101, 45, 3093, 3105, 3089, 4852, 4857, 11,
+ 463, 0, 832, 105, 109, 631, 49, 691,
+ 694, 45, 688, 697, 0, 5, 7, 401,
+ 3, 9, 0, 5, 7, 3, 9, 631,
+ 47, 676, 679, 45, 673, 682, 862, 0,
+ 5, 7, 115, 3, 9, 117, 0, 5,
+ 7, 3, 9, 1, 418, 439, 11, 463,
+ 11, 463, 11, 463, 11, 463, 631, 47,
+ 676, 679, 45, 673, 682, 0, 5, 7,
+ 631, 3, 9, 47, 676, 679, 45, 673,
+ 682, 0, 5, 7, 115, 3, 9, 117,
+ 0, 5, 7, 3, 9, 1, 418, 439,
+ 11, 463, 11, 463, 11, 463, 631, 47,
+ 676, 679, 45, 673, 682, 949, 0, 5,
+ 7, 147, 149, 151, 153, 3, 9, 0,
+ 5, 7, 3, 9, 2701, 631, 47, 676,
+ 679, 45, 673, 682, 0, 5, 7, 0,
+ 3, 9, 3121, 0, 5, 7, 0, 5,
+ 7, 0, 5, 7, 1, 3, 9, 418,
+ 439, 3, 9, 418, 439, 3, 9, 418,
+ 439, 952, 129, 11, 463, 11, 463, 11,
+ 463, 11, 463, 11, 463, 0, 5, 7,
+ 3, 9, 631, 47, 676, 679, 45, 673,
+ 682, 3037, 0, 5, 7, 832, 3, 9,
+ 4817, 105, 841, 3049, 3053, 841, 3049, 3053,
+ 841, 3049, 3053, 3041, 3045, 3057, 4822, 4827,
+ 3045, 3057, 4822, 4827, 3045, 3057, 4822, 4827,
+ 11, 463, 11, 463, 0, 5, 7, 3,
+ 9, 631, 47, 676, 679, 45, 673, 682,
+ 0, 5, 7, 832, 3, 9, 105, 844,
+ 3073, 3077, 844, 3073, 3077, 844, 3073, 3077,
+ 3065, 3069, 3081, 4837, 4842, 3069, 3081, 4837,
+ 4842, 3069, 3081, 4837, 4842, 11, 463, 11,
+ 463, 0, 5, 7, 3, 9, 631, 47,
+ 676, 679, 45, 673, 682, 0, 5, 7,
+ 604, 3, 9, 43, 613, 616, 43, 613,
+ 616, 43, 613, 616, 607, 610, 619, 2689,
+ 2693, 610, 619, 2689, 2693, 610, 619, 2689,
+ 2693, 11, 463, 11, 463, 11, 463, 955,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 167, 961, 964, 0, 0, 0, 958,
+ 967, 0, 5, 7, 401, 3, 9, 0,
+ 5, 7, 3, 9, 631, 47, 676, 679,
+ 45, 673, 682, 0, 5, 7, 631, 3,
+ 9, 51, 712, 715, 45, 709, 718, 760,
+ 0, 5, 7, 745, 3, 9, 65, 751,
+ 754, 63, 748, 757, 0, 5, 7, 745,
+ 3, 9, 65, 751, 754, 63, 748, 757,
+ 0, 5, 7, 631, 3, 9, 49, 691,
+ 694, 45, 688, 697, 0, 5, 7, 604,
+ 3, 9, 43, 613, 616, 610, 619, 0,
+ 5, 7, 129, 3, 9, 1, 11, 463,
+ 11, 463, 11, 463, 11, 463, 11, 463,
+ 11, 463, 11, 463, 11, 463, 0, 0,
+ 193, 1117, 1120, 1114, 1123, 0, 0, 0,
+ 187, 1081, 1084, 1078, 1087, 0, 207, 1201,
+ 1204, 1198, 1207, 0, 0, 0, 0, 0,
+ 0, 257, 1501, 1504, 1498, 1507, 0, 0,
+ 0, 0, 0, 0, 233, 1357, 1360, 1354,
+ 1363, 231, 1345, 1348, 1342, 1351, 0, 0,
+ 203, 1177, 1180, 1174, 1183, 0, 0, 0,
+ 171, 985, 988, 982, 991, 0, 0, 0,
+ 237, 1381, 1384, 1378, 1387, 0, 0, 0,
+ 0, 0, 0, 221, 1285, 1288, 1282, 1291,
+ 0, 0, 0, 0, 205, 1189, 1192, 1186,
+ 1195, 0, 0, 0, 219, 1273, 1276, 1270,
+ 1279, 209, 1213, 1216, 1210, 1219, 0, 0,
+ 0, 0, 0, 251, 1465, 1468, 1462, 1471,
+ 0, 253, 1477, 1480, 1474, 1483, 0, 0,
+ 0, 0, 0, 177, 1021, 1024, 1018, 1027,
+ 0, 0, 0, 261, 1525, 1528, 1522, 1531,
+ 0, 0, 0, 0, 0, 0, 0, 213,
+ 1237, 1240, 1234, 1243, 0, 0, 0, 191,
+ 1105, 1108, 1102, 1111, 201, 1165, 1168, 1162,
+ 1171, 0, 0, 0, 0, 0, 245, 1429,
+ 1432, 1426, 1435, 0, 247, 1441, 1444, 1438,
+ 1447, 0, 195, 1129, 1132, 1126, 1135, 249,
+ 1453, 1456, 1450, 1459, 0, 0, 0, 0,
+ 0, 179, 1033, 1036, 1030, 1039, 181, 1045,
+ 1048, 1042, 1051, 0, 0, 0, 0, 0,
+ 0, 199, 1153, 1156, 1150, 1159, 0, 243,
+ 1417, 1420, 1414, 1423, 169, 973, 976, 0,
+ 970, 979, 0, 217, 1261, 1264, 0, 1258,
+ 1267, 223, 1297, 1300, 0, 1294, 1303, 0,
+ 0, 0, 0, 225, 1309, 1312, 1306, 1315,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 235, 1369, 1372, 1366, 1375, 0, 0,
+ 175, 1009, 1012, 1006, 1015, 0, 0, 0,
+ 185, 1069, 1072, 1066, 1075, 0, 0, 0,
+ 215, 1249, 1252, 1246, 1255, 189, 1093, 1096,
+ 1090, 1099, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 229, 1333, 1336, 1330,
+ 1339, 0, 173, 997, 1000, 994, 1003, 0,
+ 241, 1405, 1408, 1402, 1411, 0, 197, 1141,
+ 1144, 1138, 1147, 0, 0, 0, 211, 1225,
+ 1228, 1222, 1231, 0, 0, 259, 1513, 1516,
+ 1510, 1519, 0, 0, 0, 0, 0, 227,
+ 1321, 1324, 1318, 1327, 0, 183, 1057, 1060,
+ 1054, 1063, 0, 0, 2705, 631, 49, 691,
+ 694, 45, 688, 697, 0, 0, 255, 1489,
+ 1492, 1486, 1495, 0, 0, 0, 0, 0,
+ 239, 1393, 1396, 1390, 1399, 604, 622, 1,
+ 631, 49, 691, 694, 45, 688, 697, 0,
+ 5, 7, 631, 3, 9, 47, 676, 679,
+ 45, 673, 682, 0, 5, 7, 401, 3,
+ 9, 0, 5, 7, 3, 9, 129, 1,
+ 11, 463, 11, 463, 11, 463, 129, 1,
+ 631, 47, 676, 679, 45, 673, 682, 0,
+ 5, 7, 631, 3, 9, 47, 676, 679,
+ 45, 673, 682, 0, 5, 7, 631, 3,
+ 9, 49, 691, 694, 45, 688, 697, 0,
+ 5, 7, 33, 535, 3, 9, 35, 547,
+ 550, 544, 553, 868, 0, 5, 7, 538,
+ 3, 9, 133, 135, 0, 137, 139, 0,
+ 141, 0, 143, 145, 0, 556, 131, 1,
+ 0, 0, 11, 463, 117, 115, 11, 463,
+ 11, 463, 11, 463, 631, 47, 676, 679,
+ 45, 673, 682, 0, 5, 7, 631, 3,
+ 9, 47, 676, 679, 45, 673, 682, 0,
+ 5, 7, 631, 3, 9, 49, 691, 694,
+ 45, 688, 697, 0, 5, 7, 33, 535,
+ 3, 9, 541, 117, 115, 11, 463, 11,
+ 463, 11, 463, 631, 47, 676, 679, 45,
+ 673, 682, 0, 5, 7, 631, 3, 9,
+ 47, 676, 679, 45, 673, 682, 0, 5,
+ 7, 631, 3, 9, 47, 676, 679, 45,
+ 673, 682, 0, 5, 7, 115, 3, 9,
+ 117, 0, 5, 7, 3, 9, 1, 418,
+ 439, 11, 463, 11, 463, 11, 463, 11,
+ 463, 631, 51, 712, 715, 45, 709, 718,
+ 0, 5, 7, 631, 3, 9, 700, 45,
+ 1, 11, 463, 631, 51, 712, 715, 45,
+ 709, 718, 0, 5, 7, 631, 3, 9,
+ 47, 676, 679, 45, 673, 682, 0, 5,
+ 7, 631, 3, 9, 47, 676, 679, 45,
+ 673, 682, 0, 5, 7, 115, 3, 9,
+ 117, 0, 5, 7, 3, 9, 1, 418,
+ 439, 11, 463, 11, 463, 11, 463, 11,
+ 463, 631, 49, 691, 694, 45, 688, 697,
+ 0, 5, 7, 832, 3, 9, 3041, 105,
+ 11, 463, 631, 49, 691, 694, 45, 688,
+ 697, 0, 5, 7, 2218, 3, 9, 117,
+ 115, 117, 3113, 381, 115, 117, 115, 117,
+ 3345, 3341, 11, 463, 2215, 117, 3337, 379,
+ 115, 117, 3329, 2215, 117, 379, 115, 117,
+ 3333, 631, 49, 691, 694, 45, 688, 697,
+ 0, 5, 7, 631, 3, 9, 49, 691,
+ 694, 45, 688, 697, 75, 0, 5, 7,
+ 3, 9, 1, 11, 463, 11, 463, 631,
+ 47, 676, 679, 45, 673, 682, 532, 0,
+ 5, 7, 3, 9, 35, 547, 550, 544,
+ 553, 75, 0, 5, 7, 3, 9, 1,
+ 11, 463, 11, 463, 631, 49, 691, 694,
+ 45, 688, 697, 0, 5, 7, 604, 3,
+ 9, 625, 1, 11, 463, 595, 592, 0,
+ 2661, 2665, 571, 466, 475, 2657, 2669, 592,
+ 407, 2465, 2473, 3373, 3381, 2433, 2481, 3365,
+ 3369, 4362, 4372, 2681, 592, 2465, 2473, 3013,
+ 3013, 3013, 3013, 3013, 3013, 3013, 3013, 3013,
+ 3013, 3013, 3013, 3013, 3013, 3013, 3013, 3013,
+ 2433, 2481, 4547, 4562, 4807, 4807, 4807, 4807,
+ 4807, 4807, 4807, 4807, 4807, 4807, 4807, 4807,
+ 4807, 4807, 4807, 4807, 4807, 2673, 3009, 93,
+ 4792, 4797, 3005, 2997, 3001, 4787, 4802, 2465,
+ 2473, 2433, 2481, 481, 2533, 2541, 4417, 2549,
+ 481, 2461, 2469, 2477, 4367, 5996, 6002, 6116,
+ 6116, 6116, 6116, 6116, 6116, 6116, 6116, 6116,
+ 6116, 6116, 6116, 6116, 6116, 6116, 6116, 6116,
+ 4357
+};
+
+static const short _zone_scanner_eof_actions[] = {
+ 0, 2709, 2885, 413, 2263, 405, 2248, 15,
+ 15, 405, 405, 2248, 2248, 2248, 4567, 6098,
+ 2260, 413, 413, 2263, 2248, 2248, 413, 413,
+ 413, 413, 413, 2263, 2263, 413, 413, 2263,
+ 413, 413, 413, 2263, 413, 413, 413, 2263,
+ 413, 413, 413, 413, 2263, 413, 413, 413,
+ 2263, 413, 413, 413, 2263, 2263, 413, 413,
+ 413, 413, 2263, 413, 2263, 413, 413, 413,
+ 413, 2263, 413, 413, 413, 2263, 413, 413,
+ 413, 413, 413, 413, 413, 413, 413, 413,
+ 2263, 413, 413, 2263, 2263, 413, 413, 2263,
+ 413, 2263, 413, 2263, 2263, 413, 413, 413,
+ 413, 2263, 2263, 413, 413, 413, 413, 2263,
+ 413, 2263, 2263, 413, 2263, 2263, 413, 413,
+ 413, 413, 2263, 413, 413, 413, 413, 413,
+ 413, 413, 413, 413, 2263, 413, 413, 2263,
+ 413, 2263, 413, 413, 413, 2263, 2263, 413,
+ 413, 413, 413, 413, 2263, 413, 2263, 413,
+ 2263, 413, 2263, 413, 413, 413, 2263, 413,
+ 413, 2263, 413, 413, 413, 2263, 413, 2263,
+ 413, 413, 649, 2713, 413, 413, 2263, 413,
+ 413, 413, 413, 413, 2263, 413, 2709, 413,
+ 413, 649, 2885, 2885, 2885, 2885, 413, 15,
+ 95, 95, 95, 95, 95, 95, 95, 95,
+ 41, 649, 0, 4552, 649, 413, 2885, 2885,
+ 2885, 413, 2685, 2713, 3385, 2248, 2709, 15,
+ 2248, 15, 4567, 3385, 3385, 3385, 3385, 3385,
+ 3385, 3385, 3385, 3385, 3385, 3385, 3385, 3385,
+ 3385, 3385, 3385, 2248, 3361, 95, 95, 95,
+ 95, 95, 95, 95, 95, 95, 2677, 6242,
+ 2709, 2260, 4557, 4567, 4557, 4557, 4557, 4557,
+ 4557, 4557, 4557, 4557, 4557, 4557, 4557, 4557,
+ 4557, 4557, 4557, 4557, 598, 4567, 2248, 15,
+ 2248, 2248, 413, 413, 2263, 413, 413, 413,
+ 2263, 413, 2263, 2248, 2248, 15, 2248, 2248,
+ 2248, 2248, 405, 4567, 2248, 2248, 601, 601,
+ 601, 601, 601, 601, 601, 601, 601, 601,
+ 601, 601, 601, 601, 601, 601, 15, 2260,
+ 601, 2260, 0, 0, 0, 31, 31, 31,
+ 511, 511, 511, 511, 31, 31, 763, 763,
+ 2949, 2949, 2949, 2949, 763, 763, 69, 2949,
+ 2949, 2949, 2949, 77, 634, 4762, 484, 484,
+ 4762, 2877, 4762, 77, 81, 81, 81, 487,
+ 487, 487, 514, 514, 514, 514, 487, 81,
+ 0, 85, 793, 799, 87, 799, 15, 15,
+ 517, 517, 517, 517, 799, 15, 87, 0,
+ 0, 127, 127, 127, 127, 127, 127, 127,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 377, 637, 637, 3109,
+ 3109, 3109, 3109, 377, 377, 377, 637, 637,
+ 3109, 3109, 3109, 3109, 377, 637, 377, 387,
+ 387, 387, 387, 387, 387, 387, 387, 387,
+ 387, 387, 387, 387, 387, 387, 387, 387,
+ 387, 387, 387, 387, 387, 387, 643, 643,
+ 387, 387, 387, 387, 387, 387, 387, 387,
+ 387, 387, 387, 387, 387, 387, 387, 387,
+ 387, 387, 387, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 835, 835, 835, 835,
+ 835, 835, 385, 385, 385, 385, 385, 835,
+ 835, 835, 835, 835, 835, 385, 385, 640,
+ 640, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 385, 385, 385,
+ 385, 385, 385, 385, 385, 640, 640, 640,
+ 640, 385, 385, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 397, 397, 397, 397, 397,
+ 397, 397, 397, 399, 399, 399, 399, 399,
+ 399, 399, 399, 399, 399, 399, 399, 399,
+ 399, 399, 399, 399, 399, 399, 399, 399,
+ 399, 399, 399, 399, 399, 399, 399, 399,
+ 399, 399, 399, 399, 399, 399, 399, 399,
+ 399, 399, 399, 399, 838, 838, 405, 405,
+ 405, 405, 405, 405, 646, 646, 646, 2881,
+ 646, 2881, 646, 2881, 646, 2881, 2881, 2881,
+ 2881, 405, 2881, 2881, 2881, 405, 2881, 2881,
+ 2881, 405, 2881, 2881, 2881, 405, 405, 405,
+ 405, 405, 405, 405, 405, 405, 405, 405,
+ 405, 405, 646, 646, 405, 405, 405, 781,
+ 781, 781, 781, 838, 838, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212,
+ 2212, 2212, 2212, 2212, 646, 646, 646, 646,
+ 646, 646, 405, 405, 405, 405, 405, 646,
+ 646, 646, 646, 405, 405, 405, 405, 405,
+ 405, 405, 405, 405, 405, 405, 405, 405,
+ 405, 405, 646, 646, 405, 405, 405, 405,
+ 405, 405, 405, 859, 859, 859, 3033, 3033,
+ 859, 859, 405, 859, 3033, 3033, 646, 646,
+ 405, 405, 646, 646, 862, 862, 862, 862,
+ 405, 405, 405, 646, 646, 646, 646, 862,
+ 862, 862, 862, 405, 405, 646, 646, 949,
+ 949, 2701, 2701, 949, 3121, 952, 952, 952,
+ 952, 952, 952, 952, 949, 949, 949, 2701,
+ 2701, 3037, 4817, 949, 949, 949, 2701, 2701,
+ 3037, 4817, 949, 949, 949, 2701, 2701, 949,
+ 3121, 949, 949, 405, 955, 955, 405, 405,
+ 646, 646, 646, 646, 760, 760, 760, 760,
+ 646, 646, 405, 405, 405, 405, 405, 405,
+ 405, 405, 405, 405, 405, 405, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 955, 955,
+ 955, 955, 955, 2705, 2705, 955, 955, 955,
+ 955, 955, 955, 955, 955, 955, 405, 405,
+ 405, 646, 646, 646, 646, 405, 405, 405,
+ 405, 405, 405, 405, 405, 405, 646, 646,
+ 646, 646, 646, 646, 862, 862, 868, 868,
+ 868, 868, 868, 868, 868, 868, 868, 405,
+ 868, 868, 868, 868, 868, 405, 862, 862,
+ 405, 405, 405, 646, 646, 646, 646, 646,
+ 646, 862, 862, 862, 862, 405, 405, 405,
+ 646, 646, 646, 646, 646, 646, 862, 862,
+ 862, 862, 405, 405, 405, 646, 646, 646,
+ 646, 405, 405, 646, 646, 646, 646, 646,
+ 646, 862, 862, 862, 862, 405, 405, 405,
+ 646, 646, 838, 838, 405, 646, 646, 862,
+ 862, 862, 862, 3113, 862, 862, 862, 862,
+ 3345, 405, 862, 862, 3337, 862, 862, 3337,
+ 862, 862, 3337, 862, 862, 3337, 646, 646,
+ 646, 646, 405, 405, 405, 405, 646, 646,
+ 405, 405, 405, 405, 405, 405, 646, 646,
+ 405, 405, 405, 405, 0, 0, 0, 0,
+ 93, 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, 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
+};
+
+
+
+
+
+
+__attribute__((visibility("default")))
+int zs_init(
+ zs_scanner_t *s,
+ const char *origin,
+ const uint16_t rclass,
+ const uint32_t ttl)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ memset(s, 0, sizeof(*s));
+
+ // Nonzero initial scanner state.
+ s->cs = 1396;
+
+ // Reset the file descriptor.
+ s->file.descriptor = -1;
+
+ // Use the root zone as origin if not specified.
+ if (origin == NULL || strlen(origin) == 0) {
+ origin = ".";
+ }
+ size_t origin_len = strlen(origin);
+
+ // Prepare a zone settings header.
+ const char *format;
+ if (origin[origin_len - 1] != '.') {
+ format = "$ORIGIN %s.\n";
+ } else {
+ format = "$ORIGIN %s\n";
+ }
+
+ char settings[1024];
+ int ret = snprintf(settings, sizeof(settings), format, origin);
+ if (ret <= 0 || ret >= sizeof(settings)) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+
+ // Parse the settings to set up the scanner origin.
+ if (zs_set_input_string(s, settings, ret) != 0 ||
+ zs_parse_all(s) != 0) {
+ return -1;
+ }
+
+ // Set scanner defaults.
+ s->path = strdup(".");
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+ s->default_class = rclass;
+ s->default_ttl = ttl;
+ s->line_counter = 1;
+
+ s->state = ZS_STATE_NONE;
+ s->process.automatic = false;
+
+ return 0;
+}
+
+static void input_deinit(
+ zs_scanner_t *s,
+ bool keep_filename)
+{
+ // Deinit the file input.
+ if (s->file.descriptor != -1) {
+ // Unmap the file content.
+ if (s->input.start != NULL) {
+ if (s->input.mmaped) {
+ munmap((void *)s->input.start,
+ s->input.end - s->input.start);
+ } else {
+ free((void *)s->input.start);
+ }
+ }
+
+ // Close the opened file.
+ close(s->file.descriptor);
+ s->file.descriptor = -1;
+ }
+
+ // Keep file name for possible trailing error report.
+ if (!keep_filename) {
+ free(s->file.name);
+ s->file.name = NULL;
+ }
+
+ // Unset the input limits.
+ s->input.start = NULL;
+ s->input.current = NULL;
+ s->input.end = NULL;
+ s->input.eof = false;
+}
+
+__attribute__((visibility("default")))
+void zs_deinit(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return;
+ }
+
+ input_deinit(s, false);
+ free(s->path);
+}
+
+static int set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size,
+ bool final_block)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (input == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, final_block);
+
+ // Set the scanner input limits.
+ s->input.start = input;
+ s->input.current = input;
+ s->input.end = input + size;
+ s->input.eof = final_block;
+
+ return 0;
+}
+
+static char *read_file_to_buf(
+ int fd,
+ size_t *bufsize)
+{
+ size_t bufs = 0, newbufs = 8192;
+ char *buf = malloc(bufs + newbufs);
+ int ret = 0;
+
+ while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) {
+ bufs += newbufs;
+ newbufs = bufs;
+ char *newbuf = realloc(buf, bufs + newbufs);
+ if (newbuf == NULL) {
+ free(buf);
+ }
+ buf = newbuf;
+ }
+ if (ret < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ *bufsize = bufs + ret;
+ return buf;
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size)
+{
+ s->state = ZS_STATE_NONE;
+
+ return set_input_string(s, input, size, false);
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_file(
+ zs_scanner_t *s,
+ const char *file_name)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (file_name == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, false);
+
+ // Try to open the file.
+ s->file.descriptor = open(file_name, O_RDONLY);
+ if (s->file.descriptor == -1) {
+ ERR(errno == EACCES ? ZS_FILE_ACCESS : ZS_FILE_OPEN);
+ return -1;
+ }
+
+ char *start = NULL;
+ size_t size = 0;
+
+ // Check the input.
+ struct stat file_stat;
+ if (fstat(s->file.descriptor, &file_stat) == -1) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (S_ISCHR(file_stat.st_mode) ||
+ S_ISBLK(file_stat.st_mode) ||
+ S_ISFIFO(file_stat.st_mode)) {
+ // Workaround if cannot mmap, read to memory.
+ start = read_file_to_buf(s->file.descriptor, &size);
+ if (start == NULL) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+ } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file.
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (file_stat.st_size > 0) { // Mmap non-empty file.
+ start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED,
+ s->file.descriptor, 0);
+ if (start == MAP_FAILED) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ size = file_stat.st_size;
+ s->input.mmaped = true;
+
+ // Try to set the mapped memory advise to sequential.
+#if defined(MADV_SEQUENTIAL) && !defined(__sun)
+ (void)madvise(start, size, MADV_SEQUENTIAL);
+#else
+#ifdef POSIX_MADV_SEQUENTIAL
+ (void)posix_madvise(start, size, POSIX_MADV_SEQUENTIAL);
+#endif /* POSIX_MADV_SEQUENTIAL */
+#endif /* MADV_SEQUENTIAL && !__sun */
+ }
+
+ // Set the scanner input limits.
+ s->input.start = start;
+ s->input.current = start;
+ s->input.end = (start != NULL) ? start + size : start;
+
+ // Get absolute path of the zone file if possible.
+ char *full_name = realpath(file_name, NULL);
+ if (full_name != NULL) {
+ free(s->path);
+ s->path = strdup(dirname(full_name));
+ free(full_name);
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+ }
+
+ s->file.name = strdup(file_name);
+ if (s->file.name == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ s->state = ZS_STATE_NONE;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing(
+ zs_scanner_t *s,
+ void (*process_record)(zs_scanner_t *),
+ void (*process_error)(zs_scanner_t *),
+ void *data)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.record = process_record;
+ s->process.error = process_error;
+ s->process.data = data;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing_comment(
+ zs_scanner_t *s,
+ void (*process_comment)(zs_scanner_t *))
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.comment = process_comment;
+
+ return 0;
+}
+
+typedef enum {
+ WRAP_NONE, // Initial state.
+ WRAP_DETECTED, // Input block end is a first '\' in rdata.
+ WRAP_PROCESS // Parsing of auxiliary block = "\".
+} wrap_t;
+
+static void parse(
+ zs_scanner_t *s,
+ wrap_t *wrap)
+{
+ // Restore scanner input limits (Ragel internals).
+ const char *p = s->input.current;
+ const char *pe = s->input.end;
+ const char *eof = s->input.eof ? pe : NULL;
+
+ // Restore state variables (Ragel internals).
+ int cs = s->cs;
+ int top = s->top;
+ int stack[ZS_RAGEL_STACK_SIZE];
+ memcpy(stack, s->stack, sizeof(stack));
+
+ // Next 2 variables are for better performance.
+ // Restoring r_data pointer to next free space.
+ uint8_t *rdata_tail = s->r_data + s->r_data_tail;
+ // Initialization of the last r_data byte.
+ uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1;
+
+ // Write scanner body (in C).
+
+ {
+ int _klen;
+ unsigned int _trans;
+ short _widec;
+ const short *_acts;
+ unsigned int _nacts;
+ const short *_keys;
+
+ if ( p == pe )
+ goto _test_eof;
+ if ( cs == 0 )
+ goto _out;
+_resume:
+ _widec = (*p);
+ _klen = _zone_scanner_cond_lengths[cs];
+ _keys = _zone_scanner_cond_keys + (_zone_scanner_cond_offsets[cs]*2);
+ if ( _klen > 0 ) {
+ const short *_lower = _keys;
+ const short *_mid;
+ const short *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( _widec < _mid[0] )
+ _upper = _mid - 2;
+ else if ( _widec > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ switch ( _zone_scanner_cond_spaces[_zone_scanner_cond_offsets[cs] + ((_mid - _keys)>>1)] ) {
+ case 0: {
+ _widec = (short)(1664 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ break;
+ }
+ case 1: {
+ _widec = (short)(2176 + ((*p) - -128));
+ if (
+ !s->multiline ) _widec += 256;
+ break;
+ }
+ case 2: {
+ _widec = (short)(128 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ break;
+ }
+ case 3: {
+ _widec = (short)(640 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ break;
+ }
+ case 4: {
+ _widec = (short)(1152 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ break;
+ }
+ case 5: {
+ _widec = (short)(3712 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ break;
+ }
+ case 6: {
+ _widec = (short)(5248 + ((*p) - -128));
+ if (
+ s->number64 == 0 ) _widec += 256;
+ break;
+ }
+ case 7: {
+ _widec = (short)(2688 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ break;
+ }
+ case 8: {
+ _widec = (short)(4224 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ break;
+ }
+ case 9: {
+ _widec = (short)(5760 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ break;
+ }
+ case 10: {
+ _widec = (short)(6784 + ((*p) - -128));
+ if (
+ s->number64 != 0 ) _widec += 256;
+ if (
+ s->number64 == 0 ) _widec += 512;
+ break;
+ }
+ case 11: {
+ _widec = (short)(13952 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ break;
+ }
+ case 12: {
+ _widec = (short)(7808 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ s->number64 != 0 ) _widec += 512;
+ if (
+ s->number64 == 0 ) _widec += 1024;
+ break;
+ }
+ case 13: {
+ _widec = (short)(9856 + ((*p) - -128));
+ if (
+ s->multiline ) _widec += 256;
+ if (
+ !s->multiline ) _widec += 512;
+ if (
+ s->number64 != 0 ) _widec += 1024;
+ if (
+ s->number64 == 0 ) _widec += 2048;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ _keys = _zone_scanner_trans_keys + _zone_scanner_key_offsets[cs];
+ _trans = _zone_scanner_index_offsets[cs];
+
+ _klen = _zone_scanner_single_lengths[cs];
+ if ( _klen > 0 ) {
+ const short *_lower = _keys;
+ const short *_mid;
+ const short *_upper = _keys + _klen - 1;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + ((_upper-_lower) >> 1);
+ if ( _widec < *_mid )
+ _upper = _mid - 1;
+ else if ( _widec > *_mid )
+ _lower = _mid + 1;
+ else {
+ _trans += (unsigned int)(_mid - _keys);
+ goto _match;
+ }
+ }
+ _keys += _klen;
+ _trans += _klen;
+ }
+
+ _klen = _zone_scanner_range_lengths[cs];
+ if ( _klen > 0 ) {
+ const short *_lower = _keys;
+ const short *_mid;
+ const short *_upper = _keys + (_klen<<1) - 2;
+ while (1) {
+ if ( _upper < _lower )
+ break;
+
+ _mid = _lower + (((_upper-_lower) >> 1) & ~1);
+ if ( _widec < _mid[0] )
+ _upper = _mid - 2;
+ else if ( _widec > _mid[1] )
+ _lower = _mid + 2;
+ else {
+ _trans += (unsigned int)((_mid - _keys)>>1);
+ goto _match;
+ }
+ }
+ _trans += _klen;
+ }
+
+_match:
+ _trans = _zone_scanner_indicies[_trans];
+ cs = _zone_scanner_trans_targs[_trans];
+
+ if ( _zone_scanner_trans_actions[_trans] == 0 )
+ goto _again;
+
+ _acts = _zone_scanner_actions + _zone_scanner_trans_actions[_trans];
+ _nacts = (unsigned int) *_acts++;
+ while ( _nacts-- > 0 )
+ {
+ switch ( *_acts++ )
+ {
+ case 0:
+ {
+ p--; {cs = stack[--top]; goto _again;}
+ }
+ break;
+ case 1:
+ {
+ s->line_counter++;
+ }
+ break;
+ case 2:
+ {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ p--; {cs = 307;goto _again;}
+ }
+ s->multiline = true;
+ }
+ break;
+ case 3:
+ {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ p--; {cs = 307;goto _again;}
+ }
+ s->multiline = false;
+ }
+ break;
+ case 4:
+ {
+ s->buffer_length = 0;
+ }
+ break;
+ case 5:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ break;
+ case 6:
+ {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out; }
+ }
+ }
+ }
+ break;
+ case 7:
+ {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ break;
+ case 8:
+ {
+ WARN(ZS_BAD_REST);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 9:
+ {
+ s->buffer_length = 0;
+ }
+ break;
+ case 10:
+ {
+ if ((*p) == '\r') {
+ ERR(ZS_DOS_NEWLINE);
+ }
+
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ }
+ }
+ break;
+ case 11:
+ {
+ // Terminate the error context string.
+ s->buffer[s->buffer_length++] = 0;
+
+ // Error counter incrementation.
+ s->error.counter++;
+
+ // Initialize the fcall stack.
+ top = 0;
+
+ // Reset per-record contexts.
+ s->long_string = false;
+ s->comma_list = false;
+
+ s->state = ZS_STATE_ERROR;
+
+ // Execute the error callback.
+ if (s->process.automatic) {
+ p--;
+ if (s->process.error != NULL) {
+ s->process.error(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out; }
+ }
+ }
+
+ // Stop the scanner if fatal error.
+ if (s->error.fatal) {
+ {p++; goto _out; }
+ }
+ {cs = 306;goto _again;}
+ } else {
+ // Return if external processing.
+ p--; cs = 306; {p++; goto _out; }
+ }
+ }
+ break;
+ case 12:
+ { p--; {stack[top++] = cs; cs = 1396;goto _again;} }
+ break;
+ case 13:
+ {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ break;
+ case 14:
+ {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = (*p);
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 15:
+ {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 16:
+ {
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length] = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 17:
+ {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)];
+ }
+ break;
+ case 18:
+ {
+ s->dname_tmp_length++;
+ }
+ break;
+ case 19:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 20:
+ {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ break;
+ case 21:
+ {
+ // Check for (relative + origin) dname length overflow.
+ if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) {
+ memcpy(s->dname + s->dname_tmp_length,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length += s->zone_origin_length;
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 22:
+ {
+ // Copy already verified zone origin.
+ memcpy(s->dname,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length = s->zone_origin_length;
+ }
+ break;
+ case 23:
+ {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ break;
+ case 24:
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 25:
+ { p--; {stack[top++] = cs; cs = 309;goto _again;} }
+ break;
+ case 26:
+ {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 27:
+ {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {cs = 307;goto _again;}
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 28:
+ {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 29:
+ {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 30:
+ {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ break;
+ case 31:
+ {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ break;
+ case 32:
+ {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 33:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 34:
+ {
+ s->dname = rdata_tail;
+ }
+ break;
+ case 35:
+ {
+ rdata_tail += s->dname_tmp_length;
+ }
+ break;
+ case 36:
+ {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 37:
+ {
+ s->number64 = 0;
+ }
+ break;
+ case 38:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 39:
+ {
+ s->decimal_counter = 0;
+ }
+ break;
+ case 40:
+ {
+ s->number64_tmp = s->number64;
+ }
+ break;
+ case 41:
+ {
+ s->decimal_counter++;
+ }
+ break;
+ case 42:
+ {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 43:
+ {
+ s->decimals = 2;
+ }
+ break;
+ case 44:
+ {
+ s->decimals = 3;
+ }
+ break;
+ case 45:
+ {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 46:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 47:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 48:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 49:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 50:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 51:
+ { if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 52:
+ { if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 53:
+ { if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 54:
+ { if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 55:
+ {
+ s->number64_tmp = s->number64;
+ }
+ break;
+ case 56:
+ {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 57:
+ {
+ s->buffer_length = 0;
+ }
+ break;
+ case 58:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 59:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ p--; {cs = 307;goto _again;}
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ p--; {cs = 307;goto _again;}
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 60:
+ {
+ WARN(ZS_BAD_TIMESTAMP_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 61:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+
+ *(rdata_tail++) = (*p);
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 62:
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 63:
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 64:
+ {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+
+ *rdata_tail = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 65:
+ {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ ((*p) <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 66:
+ {
+ rdata_tail++;
+ }
+ break;
+ case 67:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 68:
+ {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ p--; {cs = 307;goto _again;}
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+ break;
+ case 69:
+ { p--; {stack[top++] = cs; cs = 318;goto _again;} }
+ break;
+ case 70:
+ {
+ s->long_string = true;
+ }
+ break;
+ case 71:
+ {
+ s->long_string = false;
+ }
+ break;
+ case 72:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 73:
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 74:
+ { p--; {stack[top++] = cs; cs = 331;goto _again;} }
+ break;
+ case 75:
+ {
+ s->dname = s->zone_origin;
+ }
+ break;
+ case 76:
+ {
+ s->zone_origin_length = s->dname_tmp_length;
+ }
+ break;
+ case 77:
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 78:
+ { p--; {stack[top++] = cs; cs = 340;goto _again;} }
+ break;
+ case 79:
+ {
+ rdata_tail = s->r_data;
+ }
+ break;
+ case 80:
+ {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {cs = 307;goto _again;}
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ break;
+ case 81:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 82:
+ {
+ s->dname = s->r_data;
+ }
+ break;
+ case 83:
+ {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ break;
+ case 84:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 85:
+ {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {cs = 307;goto _again;}
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ p--; {cs = 307;goto _again;}
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ p--; {cs = 307;goto _again;}
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ p--; cs = 1396; {p++; goto _out; }
+ }
+ }
+ break;
+ case 86:
+ { p--; {stack[top++] = cs; cs = 352;goto _again;} }
+ break;
+ case 87:
+ {
+ ERR(ZS_OK);
+ }
+ break;
+ case 88:
+ {
+ NOERR;
+ }
+ break;
+ case 89:
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 90:
+ {
+ s->r_class = s->default_class;
+ }
+ break;
+ case 91:
+ {
+ s->r_ttl = s->default_ttl;
+ }
+ break;
+ case 92:
+ {
+ s->r_class = KNOT_CLASS_IN;
+ }
+ break;
+ case 93:
+ {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 94:
+ {
+ s->buffer_length = 0;
+ }
+ break;
+ case 95:
+ {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = (*p);
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 96:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 97:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 98:
+ {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+ break;
+ case 99:
+ {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 100:
+ {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+ break;
+ case 101:
+ {
+ memset(&(s->apl), 0, sizeof(s->apl));
+ }
+ break;
+ case 102:
+ {
+ s->apl.excl_flag = 128; // dec 128 = bin 10000000.
+ }
+ break;
+ case 103:
+ {
+ s->apl.addr_family = 1;
+ }
+ break;
+ case 104:
+ {
+ s->apl.addr_family = 2;
+ }
+ break;
+ case 105:
+ {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 106:
+ {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ p--; {cs = 307;goto _again;}
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ break;
+ case 107:
+ {
+ WARN(ZS_BAD_APL);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 108:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 109:
+ {
+ *rdata_tail += second_hex_to_num[(uint8_t)(*p)];
+ rdata_tail++;
+ }
+ break;
+ case 110:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 111:
+ {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 112:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 113:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 114:
+ {
+ *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 115:
+ {
+ *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 116:
+ {
+ *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)];
+ }
+ break;
+ case 117:
+ {
+ WARN(ZS_BAD_BASE64_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 118:
+ { p--; {stack[top++] = cs; cs = 369;goto _again;} }
+ break;
+ case 119:
+ {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 120:
+ {
+ *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 121:
+ {
+ *rdata_tail += third_base32hex_to_num[(uint8_t)(*p)];
+ }
+ break;
+ case 122:
+ {
+ *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 123:
+ {
+ *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 124:
+ {
+ *rdata_tail += sixth_base32hex_to_num[(uint8_t)(*p)];
+ }
+ break;
+ case 125:
+ {
+ *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)(*p)];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)(*p)];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 126:
+ {
+ *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)(*p)];
+ }
+ break;
+ case 127:
+ {
+ WARN(ZS_BAD_BASE32HEX_CHAR);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 128:
+ {
+ *(rdata_tail++) = 0;
+ }
+ break;
+ case 129:
+ {
+ *(rdata_tail++) = 1;
+ }
+ break;
+ case 130:
+ {
+ *(rdata_tail++) = 2;
+ }
+ break;
+ case 131:
+ {
+ *(rdata_tail++) = 3;
+ }
+ break;
+ case 132:
+ {
+ *(rdata_tail++) = 5;
+ }
+ break;
+ case 133:
+ {
+ *(rdata_tail++) = 6;
+ }
+ break;
+ case 134:
+ {
+ *(rdata_tail++) = 7;
+ }
+ break;
+ case 135:
+ {
+ *(rdata_tail++) = 8;
+ }
+ break;
+ case 136:
+ {
+ *(rdata_tail++) = 10;
+ }
+ break;
+ case 137:
+ {
+ *(rdata_tail++) = 12;
+ }
+ break;
+ case 138:
+ {
+ *(rdata_tail++) = 13;
+ }
+ break;
+ case 139:
+ {
+ *(rdata_tail++) = 14;
+ }
+ break;
+ case 140:
+ {
+ *(rdata_tail++) = 15;
+ }
+ break;
+ case 141:
+ {
+ *(rdata_tail++) = 16;
+ }
+ break;
+ case 142:
+ {
+ *(rdata_tail++) = 252;
+ }
+ break;
+ case 143:
+ {
+ *(rdata_tail++) = 253;
+ }
+ break;
+ case 144:
+ {
+ *(rdata_tail++) = 254;
+ }
+ break;
+ case 145:
+ {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 146:
+ {
+ uint16_t val = htons(1);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 147:
+ {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 148:
+ {
+ uint16_t val = htons(3);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 149:
+ {
+ uint16_t val = htons(4);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 150:
+ {
+ uint16_t val = htons(5);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 151:
+ {
+ uint16_t val = htons(6);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 152:
+ {
+ uint16_t val = htons(7);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 153:
+ {
+ uint16_t val = htons(8);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 154:
+ {
+ uint16_t val = htons(253);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 155:
+ {
+ uint16_t val = htons(254);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ break;
+ case 156:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 157:
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 158:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 159:
+ { type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ break;
+ case 160:
+ { type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ break;
+ case 161:
+ { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ break;
+ case 162:
+ { type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ break;
+ case 163:
+ { type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ break;
+ case 164:
+ { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ break;
+ case 165:
+ { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ break;
+ case 166:
+ { type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ break;
+ case 167:
+ { type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ break;
+ case 168:
+ { type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ break;
+ case 169:
+ { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ break;
+ case 170:
+ { type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ break;
+ case 171:
+ { type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ break;
+ case 172:
+ { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ break;
+ case 173:
+ { type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ break;
+ case 174:
+ { type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ break;
+ case 175:
+ { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ break;
+ case 176:
+ { type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ break;
+ case 177:
+ { type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ break;
+ case 178:
+ { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ break;
+ case 179:
+ { type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ break;
+ case 180:
+ { type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ break;
+ case 181:
+ { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ break;
+ case 182:
+ { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ break;
+ case 183:
+ { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ break;
+ case 184:
+ { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ break;
+ case 185:
+ { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ break;
+ case 186:
+ { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ break;
+ case 187:
+ { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ break;
+ case 188:
+ { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ break;
+ case 189:
+ { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ break;
+ case 190:
+ { type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ break;
+ case 191:
+ { type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ break;
+ case 192:
+ { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ break;
+ case 193:
+ { type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ break;
+ case 194:
+ { type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ break;
+ case 195:
+ { type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ break;
+ case 196:
+ { type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ break;
+ case 197:
+ { type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ break;
+ case 198:
+ { type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ break;
+ case 199:
+ { type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ break;
+ case 200:
+ { type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ break;
+ case 201:
+ { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ break;
+ case 202:
+ { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ break;
+ case 203:
+ { type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ break;
+ case 204:
+ { type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ break;
+ case 205:
+ { type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ break;
+ case 206:
+ { type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ break;
+ case 207:
+ {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 208:
+ { window_add_bit(KNOT_RRTYPE_A, s); }
+ break;
+ case 209:
+ { window_add_bit(KNOT_RRTYPE_NS, s); }
+ break;
+ case 210:
+ { window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ break;
+ case 211:
+ { window_add_bit(KNOT_RRTYPE_SOA, s); }
+ break;
+ case 212:
+ { window_add_bit(KNOT_RRTYPE_PTR, s); }
+ break;
+ case 213:
+ { window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ break;
+ case 214:
+ { window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ break;
+ case 215:
+ { window_add_bit(KNOT_RRTYPE_MX, s); }
+ break;
+ case 216:
+ { window_add_bit(KNOT_RRTYPE_TXT, s); }
+ break;
+ case 217:
+ { window_add_bit(KNOT_RRTYPE_RP, s); }
+ break;
+ case 218:
+ { window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ break;
+ case 219:
+ { window_add_bit(KNOT_RRTYPE_RT, s); }
+ break;
+ case 220:
+ { window_add_bit(KNOT_RRTYPE_KEY, s); }
+ break;
+ case 221:
+ { window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ break;
+ case 222:
+ { window_add_bit(KNOT_RRTYPE_LOC, s); }
+ break;
+ case 223:
+ { window_add_bit(KNOT_RRTYPE_SRV, s); }
+ break;
+ case 224:
+ { window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ break;
+ case 225:
+ { window_add_bit(KNOT_RRTYPE_KX, s); }
+ break;
+ case 226:
+ { window_add_bit(KNOT_RRTYPE_CERT, s); }
+ break;
+ case 227:
+ { window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ break;
+ case 228:
+ { window_add_bit(KNOT_RRTYPE_APL, s); }
+ break;
+ case 229:
+ { window_add_bit(KNOT_RRTYPE_DS, s); }
+ break;
+ case 230:
+ { window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ break;
+ case 231:
+ { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ break;
+ case 232:
+ { window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ break;
+ case 233:
+ { window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ break;
+ case 234:
+ { window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ break;
+ case 235:
+ { window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ break;
+ case 236:
+ { window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ break;
+ case 237:
+ { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ break;
+ case 238:
+ { window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ break;
+ case 239:
+ { window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ break;
+ case 240:
+ { window_add_bit(KNOT_RRTYPE_CDS, s); }
+ break;
+ case 241:
+ { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ break;
+ case 242:
+ { window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ break;
+ case 243:
+ { window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ break;
+ case 244:
+ { window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ break;
+ case 245:
+ { window_add_bit(KNOT_RRTYPE_SPF, s); }
+ break;
+ case 246:
+ { window_add_bit(KNOT_RRTYPE_NID, s); }
+ break;
+ case 247:
+ { window_add_bit(KNOT_RRTYPE_L32, s); }
+ break;
+ case 248:
+ { window_add_bit(KNOT_RRTYPE_L64, s); }
+ break;
+ case 249:
+ { window_add_bit(KNOT_RRTYPE_LP, s); }
+ break;
+ case 250:
+ { window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ break;
+ case 251:
+ { window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ break;
+ case 252:
+ { window_add_bit(KNOT_RRTYPE_URI, s); }
+ break;
+ case 253:
+ { window_add_bit(KNOT_RRTYPE_CAA, s); }
+ break;
+ case 254:
+ { window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ break;
+ case 255:
+ { window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ break;
+ case 256:
+ {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ break;
+ case 257:
+ {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ }
+ }
+ break;
+ case 258:
+ {
+ WARN(ZS_BAD_BITMAP);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 259:
+ { p--; {stack[top++] = cs; cs = 376;goto _again;} }
+ break;
+ case 260:
+ {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 261:
+ {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 262:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 263:
+ {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 264:
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 265:
+ {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 266:
+ {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 267:
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 268:
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 269:
+ {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 270:
+ {
+ s->loc.lat_sign = -1;
+ }
+ break;
+ case 271:
+ {
+ s->loc.long_sign = -1;
+ }
+ break;
+ case 272:
+ {
+ s->loc.alt_sign = -1;
+ }
+ break;
+ case 273:
+ {
+ memset(&(s->loc), 0, sizeof(s->loc));
+ // Defaults.
+ s->loc.siz = 100;
+ s->loc.vp = 1000;
+ s->loc.hp = 1000000;
+ s->loc.lat_sign = 1;
+ s->loc.long_sign = 1;
+ s->loc.alt_sign = 1;
+ }
+ break;
+ case 274:
+ {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ break;
+ case 275:
+ {
+ WARN(ZS_BAD_LOC_DATA);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 276:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 277:
+ {
+ s->item_length = 0;
+ }
+ break;
+ case 278:
+ {
+ s->item_length++;
+ }
+ break;
+ case 279:
+ {
+ if (s->item_length != 6) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 280:
+ {
+ if (s->item_length != 8) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 281:
+ {
+ WARN(ZS_BAD_CHAR_DASH);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 282:
+ {
+ s->item_length = 0;
+ }
+ break;
+ case 283:
+ {
+ s->item_length++;
+ }
+ break;
+ case 284:
+ {
+ if (s->item_length != 4) {
+ WARN(ZS_BAD_L64_LENGTH);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 285:
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 286:
+ {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ break;
+ case 287:
+ {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 288:
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 289:
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 290:
+ {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ break;
+ case 291:
+ {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 292:
+ {
+ s->comma_list = true;
+ }
+ break;
+ case 293:
+ {
+ s->comma_list = false;
+ }
+ break;
+ case 294:
+ {
+ s->svcb.mandatory_position = rdata_tail + 2; // Skip 2-B prefix.
+ }
+ break;
+ case 295:
+ {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+ break;
+ case 296:
+ {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 297:
+ { p--; {stack[top++] = cs; cs = 559;goto _again;} }
+ break;
+ case 298:
+ { p--; {stack[top++] = cs; cs = 603;goto _again;} }
+ break;
+ case 299:
+ {
+ WARN(ZS_BAD_ALGORITHM);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 300:
+ {
+ WARN(ZS_BAD_CERT_TYPE);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 301:
+ { p--; {stack[top++] = cs; cs = 699;goto _again;} }
+ break;
+ case 302:
+ { p--; {stack[top++] = cs; cs = 803;goto _again;} }
+ break;
+ case 303:
+ {
+ rdata_tail = s->r_data;
+ }
+ break;
+ case 304:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 305:
+ {
+ p--;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ {stack[top++] = cs; cs = 844;goto _again;}
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ {stack[top++] = cs; cs = 846;goto _again;}
+ case KNOT_RRTYPE_SOA:
+ {stack[top++] = cs; cs = 848;goto _again;}
+ case KNOT_RRTYPE_HINFO:
+ {stack[top++] = cs; cs = 880;goto _again;}
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ {stack[top++] = cs; cs = 885;goto _again;}
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ {stack[top++] = cs; cs = 890;goto _again;}
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ {stack[top++] = cs; cs = 895;goto _again;}
+ case KNOT_RRTYPE_AAAA:
+ {stack[top++] = cs; cs = 899;goto _again;}
+ case KNOT_RRTYPE_LOC:
+ {stack[top++] = cs; cs = 901;goto _again;}
+ case KNOT_RRTYPE_SRV:
+ {stack[top++] = cs; cs = 956;goto _again;}
+ case KNOT_RRTYPE_NAPTR:
+ {stack[top++] = cs; cs = 967;goto _again;}
+ case KNOT_RRTYPE_CERT:
+ {stack[top++] = cs; cs = 984;goto _again;}
+ case KNOT_RRTYPE_APL:
+ {stack[top++] = cs; cs = 995;goto _again;}
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ {stack[top++] = cs; cs = 1006;goto _again;}
+ case KNOT_RRTYPE_SSHFP:
+ {stack[top++] = cs; cs = 1019;goto _again;}
+ case KNOT_RRTYPE_IPSECKEY:
+ {stack[top++] = cs; cs = 1029;goto _again;}
+ case KNOT_RRTYPE_RRSIG:
+ {stack[top++] = cs; cs = 1068;goto _again;}
+ case KNOT_RRTYPE_NSEC:
+ {stack[top++] = cs; cs = 1254;goto _again;}
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ {stack[top++] = cs; cs = 1257;goto _again;}
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ {stack[top++] = cs; cs = 1268;goto _again;}
+ case KNOT_RRTYPE_NSEC3:
+ {stack[top++] = cs; cs = 1270;goto _again;}
+ case KNOT_RRTYPE_NSEC3PARAM:
+ {stack[top++] = cs; cs = 1299;goto _again;}
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ {stack[top++] = cs; cs = 1312;goto _again;}
+ case KNOT_RRTYPE_CSYNC:
+ {stack[top++] = cs; cs = 1325;goto _again;}
+ case KNOT_RRTYPE_ZONEMD:
+ {stack[top++] = cs; cs = 1331;goto _again;}
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ {stack[top++] = cs; cs = 1349;goto _again;}
+ case KNOT_RRTYPE_L32:
+ {stack[top++] = cs; cs = 1344;goto _again;}
+ case KNOT_RRTYPE_EUI48:
+ {stack[top++] = cs; cs = 1362;goto _again;}
+ case KNOT_RRTYPE_EUI64:
+ {stack[top++] = cs; cs = 1368;goto _again;}
+ case KNOT_RRTYPE_URI:
+ {stack[top++] = cs; cs = 1374;goto _again;}
+ case KNOT_RRTYPE_CAA:
+ {stack[top++] = cs; cs = 1382;goto _again;}
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = cs; cs = 1390;goto _again;}
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ {cs = 307;goto _again;}
+ }
+ }
+ break;
+ case 306:
+ {
+ switch (s->r_type) {
+ // Next types must not have empty rdata.
+ case KNOT_RRTYPE_A:
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ case KNOT_RRTYPE_SOA:
+ case KNOT_RRTYPE_HINFO:
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ case KNOT_RRTYPE_RP:
+ case KNOT_RRTYPE_AAAA:
+ case KNOT_RRTYPE_LOC:
+ case KNOT_RRTYPE_SRV:
+ case KNOT_RRTYPE_NAPTR:
+ case KNOT_RRTYPE_CERT:
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_SSHFP:
+ case KNOT_RRTYPE_IPSECKEY:
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_NSEC3:
+ case KNOT_RRTYPE_NSEC3PARAM:
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ case KNOT_RRTYPE_CDS:
+ case KNOT_RRTYPE_CDNSKEY:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ case KNOT_RRTYPE_CSYNC:
+ case KNOT_RRTYPE_ZONEMD:
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L32:
+ case KNOT_RRTYPE_L64:
+ case KNOT_RRTYPE_LP:
+ case KNOT_RRTYPE_EUI48:
+ case KNOT_RRTYPE_EUI64:
+ case KNOT_RRTYPE_URI:
+ case KNOT_RRTYPE_CAA:
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ {stack[top++] = cs; cs = 540;goto _again;}
+ // Next types can have empty rdata.
+ case KNOT_RRTYPE_APL:
+ default:
+ {stack[top++] = cs; cs = 549;goto _again;}
+ }
+ }
+ break;
+ case 307:
+ {
+ if (pe - p == 1) {
+ *wrap = WRAP_DETECTED;
+ }
+ }
+ break;
+ case 308:
+ {
+ if (*wrap == WRAP_NONE) {
+ p--;
+ }
+ }
+ break;
+ case 309:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {cs = 307;goto _again;}
+ }
+ break;
+ case 310:
+ { s->r_type = KNOT_RRTYPE_A; }
+ break;
+ case 311:
+ { s->r_type = KNOT_RRTYPE_NS; }
+ break;
+ case 312:
+ { s->r_type = KNOT_RRTYPE_CNAME; }
+ break;
+ case 313:
+ { s->r_type = KNOT_RRTYPE_SOA; }
+ break;
+ case 314:
+ { s->r_type = KNOT_RRTYPE_PTR; }
+ break;
+ case 315:
+ { s->r_type = KNOT_RRTYPE_HINFO; }
+ break;
+ case 316:
+ { s->r_type = KNOT_RRTYPE_MINFO; }
+ break;
+ case 317:
+ { s->r_type = KNOT_RRTYPE_MX; }
+ break;
+ case 318:
+ { s->r_type = KNOT_RRTYPE_TXT; }
+ break;
+ case 319:
+ { s->r_type = KNOT_RRTYPE_RP; }
+ break;
+ case 320:
+ { s->r_type = KNOT_RRTYPE_AFSDB; }
+ break;
+ case 321:
+ { s->r_type = KNOT_RRTYPE_RT; }
+ break;
+ case 322:
+ { s->r_type = KNOT_RRTYPE_KEY; }
+ break;
+ case 323:
+ { s->r_type = KNOT_RRTYPE_AAAA; }
+ break;
+ case 324:
+ { s->r_type = KNOT_RRTYPE_LOC; }
+ break;
+ case 325:
+ { s->r_type = KNOT_RRTYPE_SRV; }
+ break;
+ case 326:
+ { s->r_type = KNOT_RRTYPE_NAPTR; }
+ break;
+ case 327:
+ { s->r_type = KNOT_RRTYPE_KX; }
+ break;
+ case 328:
+ { s->r_type = KNOT_RRTYPE_CERT; }
+ break;
+ case 329:
+ { s->r_type = KNOT_RRTYPE_DNAME; }
+ break;
+ case 330:
+ { s->r_type = KNOT_RRTYPE_APL; }
+ break;
+ case 331:
+ { s->r_type = KNOT_RRTYPE_DS; }
+ break;
+ case 332:
+ { s->r_type = KNOT_RRTYPE_SSHFP; }
+ break;
+ case 333:
+ { s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ break;
+ case 334:
+ { s->r_type = KNOT_RRTYPE_RRSIG; }
+ break;
+ case 335:
+ { s->r_type = KNOT_RRTYPE_NSEC; }
+ break;
+ case 336:
+ { s->r_type = KNOT_RRTYPE_DNSKEY; }
+ break;
+ case 337:
+ { s->r_type = KNOT_RRTYPE_DHCID; }
+ break;
+ case 338:
+ { s->r_type = KNOT_RRTYPE_NSEC3; }
+ break;
+ case 339:
+ { s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ break;
+ case 340:
+ { s->r_type = KNOT_RRTYPE_TLSA; }
+ break;
+ case 341:
+ { s->r_type = KNOT_RRTYPE_SMIMEA; }
+ break;
+ case 342:
+ { s->r_type = KNOT_RRTYPE_CDS; }
+ break;
+ case 343:
+ { s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ break;
+ case 344:
+ { s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ break;
+ case 345:
+ { s->r_type = KNOT_RRTYPE_CSYNC; }
+ break;
+ case 346:
+ { s->r_type = KNOT_RRTYPE_ZONEMD; }
+ break;
+ case 347:
+ { s->r_type = KNOT_RRTYPE_SPF; }
+ break;
+ case 348:
+ { s->r_type = KNOT_RRTYPE_NID; }
+ break;
+ case 349:
+ { s->r_type = KNOT_RRTYPE_L32; }
+ break;
+ case 350:
+ { s->r_type = KNOT_RRTYPE_L64; }
+ break;
+ case 351:
+ { s->r_type = KNOT_RRTYPE_LP; }
+ break;
+ case 352:
+ { s->r_type = KNOT_RRTYPE_EUI48; }
+ break;
+ case 353:
+ { s->r_type = KNOT_RRTYPE_EUI64; }
+ break;
+ case 354:
+ { s->r_type = KNOT_RRTYPE_URI; }
+ break;
+ case 355:
+ { s->r_type = KNOT_RRTYPE_CAA; }
+ break;
+ case 356:
+ { s->r_type = KNOT_RRTYPE_SVCB; }
+ break;
+ case 357:
+ { s->r_type = KNOT_RRTYPE_HTTPS; }
+ break;
+ case 358:
+ {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ p--; {cs = 307;goto _again;}
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ {p++; goto _out; }
+ }
+ }
+ } else {
+ // Return if external processing.
+ p--; {p++; goto _out; }
+ }
+ }
+ break;
+ }
+ }
+
+_again:
+ if ( cs == 0 )
+ goto _out;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const short *__acts = _zone_scanner_actions + _zone_scanner_eof_actions[cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 8:
+ {
+ WARN(ZS_BAD_REST);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 19:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 24:
+ {
+ WARN(ZS_BAD_DNAME_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 33:
+ {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 38:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 50:
+ {
+ WARN(ZS_BAD_TIME_UNIT);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 60:
+ {
+ WARN(ZS_BAD_TIMESTAMP_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 62:
+ {
+ WARN(ZS_BAD_TEXT_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 63:
+ {
+ WARN(ZS_BAD_TEXT);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 67:
+ {
+ WARN(ZS_BAD_NUMBER);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 71:
+ {
+ s->long_string = false;
+ }
+ break;
+ case 73:
+ {
+ ERR(ZS_BAD_TTL);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 77:
+ {
+ ERR(ZS_BAD_ORIGIN);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 81:
+ {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 84:
+ {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 88:
+ {
+ NOERR;
+ }
+ break;
+ case 89:
+ {
+ ERR(ZS_BAD_DIRECTIVE);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 96:
+ {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 107:
+ {
+ WARN(ZS_BAD_APL);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 110:
+ {
+ WARN(ZS_BAD_HEX_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 112:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 117:
+ {
+ WARN(ZS_BAD_BASE64_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 127:
+ {
+ WARN(ZS_BAD_BASE32HEX_CHAR);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 156:
+ {
+ WARN(ZS_BAD_GATEWAY);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 157:
+ {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 158:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 258:
+ {
+ WARN(ZS_BAD_BITMAP);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 275:
+ {
+ WARN(ZS_BAD_LOC_DATA);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 276:
+ {
+ WARN(ZS_BAD_HEX_RDATA);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 281:
+ {
+ WARN(ZS_BAD_CHAR_DASH);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 285:
+ {
+ WARN(ZS_BAD_CHAR_COLON);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 288:
+ {
+ WARN(ZS_BAD_SVCB_PARAM);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 289:
+ {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 299:
+ {
+ WARN(ZS_BAD_ALGORITHM);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 300:
+ {
+ WARN(ZS_BAD_CERT_TYPE);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 304:
+ {
+ WARN(ZS_BAD_RDATA);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ case 309:
+ {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ p--; {cs = 307; if ( p == pe )
+ goto _test_eof;
+goto _again;}
+ }
+ break;
+ }
+ }
+ }
+
+ _out: {}
+ }
+
+
+ // Check if the scanner state machine is in an uncovered state.
+ bool extra_error = false;
+ if (cs == 0) {
+ ERR(ZS_UNCOVERED_STATE);
+ extra_error = true;
+ // Check for an unclosed multiline record.
+ } else if (s->input.eof && s->multiline) {
+ ERR(ZS_UNCLOSED_MULTILINE);
+ extra_error = true;
+ s->line_counter--;
+ }
+
+ // Treat the extra error.
+ if (extra_error) {
+ s->error.counter++;
+ s->state = ZS_STATE_ERROR;
+
+ // Copy the error context just for the part of the current line.
+ s->buffer_length = 0;
+ while (p < pe && *p != '\n' && s->buffer_length < 50) {
+ s->buffer[s->buffer_length++] = *p++;
+ }
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the error callback.
+ if (s->process.automatic && s->process.error != NULL) {
+ s->process.error(s);
+ }
+
+ return;
+ }
+
+ // Storing scanner states.
+ s->cs = cs;
+ s->top = top;
+ memcpy(s->stack, stack, sizeof(stack));
+
+ // Store the current parser position.
+ s->input.current = p;
+
+ // Storing r_data pointer.
+ s->r_data_tail = rdata_tail - s->r_data;
+
+ if (*wrap == WRAP_DETECTED) {
+ if (set_input_string(s, "\\", 1, true) != 0) {
+ return;
+ }
+
+ *wrap = WRAP_PROCESS;
+ parse(s, wrap);
+ } else {
+ *wrap = WRAP_NONE;
+ }
+}
+
+__attribute__((visibility("default")))
+int zs_parse_record(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ // Check if parsing is possible.
+ switch (s->state) {
+ case ZS_STATE_NONE:
+ case ZS_STATE_DATA:
+ case ZS_STATE_INCLUDE:
+ break;
+ case ZS_STATE_ERROR:
+ if (s->error.fatal) {
+ return -1;
+ }
+ break;
+ default:
+ // Return if stop or end of file.
+ return 0;
+ }
+
+ // Check for the end of the input.
+ if (s->input.current != s->input.end) {
+ // Try to parse another item.
+ s->state = ZS_STATE_NONE;
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Finish if nothing was parsed.
+ if (s->state == ZS_STATE_NONE) {
+ // Parse the final block.
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ if (s->state == ZS_STATE_NONE) {
+ s->state = ZS_STATE_EOF;
+ }
+ }
+ } else {
+ s->state = ZS_STATE_EOF;
+ }
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_parse_all(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.automatic = true;
+
+ // Parse input block.
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Parse trailing newline-char block if it makes sense.
+ if (s->state != ZS_STATE_STOP && !s->error.fatal) {
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ }
+
+ // Check if any errors have occurred.
+ if (s->error.counter > 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/libzscanner/scanner.h b/src/libzscanner/scanner.h
new file mode 100644
index 0000000..b45ca48
--- /dev/null
+++ b/src/libzscanner/scanner.h
@@ -0,0 +1,405 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Zone scanner core interface.
+ *
+ * \addtogroup zscanner
+ * @{
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "libzscanner/error.h"
+
+/*! \brief Maximal length of rdata. */
+#define ZS_MAX_RDATA_LENGTH 65535
+/*! \brief Maximal length of domain name. */
+#define ZS_MAX_DNAME_LENGTH 255
+/*! \brief Maximal length of domain name label. */
+#define ZS_MAX_LABEL_LENGTH 63
+
+/*! \brief Length of ipv4 address in the wire format. */
+#define ZS_INET4_ADDR_LENGTH 4
+/*! \brief Length of ipv6 address in the wire format. */
+#define ZS_INET6_ADDR_LENGTH 16
+
+/*! \brief Number of bitmap windows. */
+#define ZS_BITMAP_WINDOWS 256
+
+/*! \brief Ragel call stack size (see Ragel internals). */
+#define ZS_RAGEL_STACK_SIZE 16
+
+/*! \brief Auxiliary structure for storing bitmap window items (see RFC4034). */
+typedef struct {
+ uint8_t bitmap[32];
+ uint8_t length;
+} zs_win_t;
+
+/*! \brief Auxiliary structure for storing one APL record (see RFC3123). */
+typedef struct {
+ uint8_t excl_flag;
+ uint16_t addr_family;
+ uint8_t prefix_length;
+} zs_apl_t;
+
+/*! \brief Auxiliary structure for storing LOC information (see RFC1876). */
+typedef struct {
+ uint32_t d1, d2;
+ uint32_t m1, m2;
+ uint32_t s1, s2;
+ uint32_t alt;
+ uint64_t siz, hp, vp;
+ int8_t lat_sign, long_sign, alt_sign;
+} zs_loc_t;
+
+/*! \brief Auxiliary structure for storing SVCB information. */
+typedef struct {
+ uint8_t *params_position;
+ uint8_t *mandatory_position;
+ uint8_t *param_position;
+ int32_t last_key;
+} zs_svcb_t;
+
+/*! \brief Scanner states describing the result. */
+typedef enum {
+ ZS_STATE_NONE, /*!< Initial state (no data). */
+ ZS_STATE_DATA, /*!< A record parsed. */
+ ZS_STATE_ERROR, /*!< An error occurred. */
+ ZS_STATE_INCLUDE, /*!< An include directive (see include_filename, buffer). */
+ ZS_STATE_EOF, /*!< The end of the current input reached. */
+ ZS_STATE_STOP /*!< Early stop (possibly set from a callback). */
+} zs_state_t;
+
+/*!
+ * \brief Context structure for zone scanner.
+ *
+ * This structure contains following items:
+ * - Copies of Ragel internal variables. The scanner can be called many times
+ * on smaller parts of zone file/memory. So it is necessary to preserve
+ * internal values between subsequent scanner callings.
+ * - Auxiliary variables which are used during processing zone data.
+ * - Pointers to callback functions and pointer to any arbitrary data which
+ * can be used in callback functions.
+ * - Zone file and error information.
+ * - Output variables (r_ prefix) containing all parts of zone record. These
+ * data are useful during processing via callback function.
+ */
+typedef struct zs_scanner zs_scanner_t; // Forward declaration due to arguments.
+struct zs_scanner {
+ /*! Current state (Ragel internals). */
+ int cs;
+ /*! Stack top (Ragel internals). */
+ int top;
+ /*! Call stack (Ragel internals). */
+ int stack[ZS_RAGEL_STACK_SIZE];
+
+ /*! Indicates whether current record is multiline. */
+ bool multiline;
+ /*! Auxiliary number for all numeric operations. */
+ uint64_t number64;
+ /*! Auxiliary variable for time and other numeric operations. */
+ uint64_t number64_tmp;
+ /*! Auxiliary variable for float numeric operations. */
+ uint32_t decimals;
+ /*! Auxiliary variable for float numeric operations. */
+ uint32_t decimal_counter;
+
+ /*! Auxiliary variable for item length (label, base64, ...). */
+ uint32_t item_length;
+ /*! Auxiliary index for item length position in array. */
+ uint32_t item_length_position;
+ /*! Auxiliary pointer to item length. */
+ uint8_t *item_length_location;
+ /*! Auxiliary 2-byte length locator. */
+ uint8_t *item_length2_location;
+ /*! Auxiliary buffer length. Is zero if no comment after a valid record. */
+ uint32_t buffer_length;
+ /*! Auxiliary buffer. Contains a comment after a valid record. */
+ uint8_t buffer[ZS_MAX_RDATA_LENGTH];
+ /*! Auxiliary buffer for current included file name. */
+ char include_filename[ZS_MAX_RDATA_LENGTH];
+ /*! Absolute path for relative includes. */
+ char *path;
+
+ /*! Auxiliary array of bitmap window blocks. */
+ zs_win_t windows[ZS_BITMAP_WINDOWS];
+ /*! Last window block which is used (-1 means no window). */
+ int16_t last_window;
+ /*! Auxiliary apl structure. */
+ zs_apl_t apl;
+ /*! Auxiliary loc structure. */
+ zs_loc_t loc;
+ /*! Auxiliary svcb structure. */
+ zs_svcb_t svcb;
+ /*! Auxiliary IP address storage. */
+ uint8_t addr[ZS_INET6_ADDR_LENGTH];
+ /*! Allow text strings longer than 255 characters. */
+ bool long_string;
+ /*! Comma separated string list indication (svcb parsing). */
+ bool comma_list;
+
+ /*! Pointer to the actual dname storage (origin/owner/rdata). */
+ uint8_t *dname;
+ /*! Pointer to the actual dname length storage. */
+ uint32_t *dname_length;
+ /*!
+ * Temporary dname length which is copied to dname_length after
+ * dname processing.
+ */
+ uint32_t dname_tmp_length;
+ /*! Position of the last free r_data byte. */
+ uint32_t r_data_tail;
+
+ /*! Length of the current origin. */
+ uint32_t zone_origin_length;
+ /*!
+ * Wire format of the current origin (ORIGIN directive sets this).
+ *
+ * \note Maximal dname length check is after each valid label.
+ */
+ uint8_t zone_origin[ZS_MAX_DNAME_LENGTH + ZS_MAX_LABEL_LENGTH];
+ /*! Value of the default class. */
+ uint16_t default_class;
+ /*! Value of the current default ttl (TTL directive sets this). */
+ uint32_t default_ttl;
+
+ /*! The current processing state. */
+ zs_state_t state;
+
+ /*! Processing callbacks and auxiliary data. */
+ struct {
+ /*! Automatic zone processing using record/error callbacks. */
+ bool automatic;
+ /*! Callback function for correct zone record. */
+ void (*record)(zs_scanner_t *);
+ /*! Callback function for wrong situations. */
+ void (*error)(zs_scanner_t *);
+ /*! Callback function for pure comment line. */
+ void (*comment)(zs_scanner_t *);
+ /*! Arbitrary data useful inside callback functions. */
+ void *data;
+ } process;
+
+ /*! Input parameters. */
+ struct {
+ /*! Start of the block. */
+ const char *start;
+ /*! Current parser position. */
+ const char *current;
+ /*! End of the block. */
+ const char *end;
+ /*! Indication for the final block parsing. */
+ bool eof;
+ /*! Indication of being mmap()-ed (malloc()-ed otherwise). */
+ bool mmaped;
+ } input;
+
+ /*! File input parameters. */
+ struct {
+ /*! Zone file name. */
+ char *name;
+ /*!< File descriptor. */
+ int descriptor;
+ } file;
+
+ struct {
+ /*! Last occurred error/warning code. */
+ int code;
+ /*! Error/warning counter. */
+ uint64_t counter;
+ /*! Indicates serious error - parsing cannot continue. */
+ bool fatal;
+ } error;
+
+ /*! Zone data line counter. */
+ uint64_t line_counter;
+
+ /*! Length of the current record owner. */
+ uint32_t r_owner_length;
+ /*!
+ * Owner of the current record.
+ *
+ * \note Maximal dname length check is after each valid label.
+ */
+ uint8_t r_owner[ZS_MAX_DNAME_LENGTH + ZS_MAX_LABEL_LENGTH];
+ /*! Class of the current record. */
+ uint16_t r_class;
+ /*! TTL of the current record. */
+ uint32_t r_ttl;
+ /*! Type of the current record data. */
+ uint16_t r_type;
+ /*! Length of the current rdata. */
+ uint32_t r_data_length;
+ /*! Current rdata. */
+ uint8_t r_data[ZS_MAX_RDATA_LENGTH];
+
+ /*
+ * Example: a. IN 60 MX 1 b. ; A comment
+ *
+ * r_owner_length = 3
+ * r_owner = 016100
+ * r_class = 1
+ * r_ttl = 60
+ * r_type = 15
+ * r_data_length = 5
+ * r_data = 0001016200
+ * buffer_length = 11
+ * buffer = " A comment"
+ */
+};
+
+/*!
+ * \brief Initializes the scanner context.
+ *
+ * \note Error code is stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ * \param origin Initial zone origin.
+ * \param rclass Zone class value.
+ * \param ttl Initial ttl value.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_init(
+ zs_scanner_t *scanner,
+ const char *origin,
+ const uint16_t rclass,
+ const uint32_t ttl
+);
+
+/*!
+ * \brief Deinitializes the scanner context.
+ *
+ * \param scanner Scanner context.
+ */
+void zs_deinit(
+ zs_scanner_t *scanner
+);
+
+/*!
+ * \brief Sets the scanner to parse a zone data string.
+ *
+ * \note Error code is stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ * \param input Input zone data string to parse.
+ * \param size Size of the input string.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_set_input_string(
+ zs_scanner_t *scanner,
+ const char *input,
+ size_t size
+);
+
+/*!
+ * \brief Sets the scanner to parse a zone file.
+ *
+ * \note Error code is stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ * \param file_name Name of the file to parse.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_set_input_file(
+ zs_scanner_t *scanner,
+ const char *file_name
+);
+
+/*!
+ * \brief Sets the scanner processing callbacks for automatic processing.
+ *
+ * \note Error code is stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ * \param process_record Processing callback function (may be NULL).
+ * \param process_error Error callback function (may be NULL).
+ * \param data Arbitrary data useful in callback functions.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_set_processing(
+ zs_scanner_t *scanner,
+ void (*process_record)(zs_scanner_t *),
+ void (*process_error)(zs_scanner_t *),
+ void *data
+);
+
+/*!
+ * \brief Sets the scanner comment processing callback for automatic processing.
+ *
+ * \note If the comment is after a valid resource record, the callback is
+ * executed before a record processing callback!
+ * \note Optional data must be set via zs_set_processing.
+ *
+ * \param scanner Scanner context.
+ * \param process_comment Processing callback function (may be NULL).
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_set_processing_comment(
+ zs_scanner_t *scanner,
+ void (*process_comment)(zs_scanner_t *)
+);
+
+/*!
+ * \brief Parses one record from the input.
+ *
+ * The following processing should be based on the scanner->state.
+ *
+ * \note Error code and other information are stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_parse_record(
+ zs_scanner_t *scanner
+);
+
+/*!
+ * \brief Launches automatic parsing of the whole input.
+ *
+ * For each correctly recognized record, the record callback is executed.
+ * If any syntax error occurs, the error callback is executed.
+ *
+ * \note Error code and other information are stored in the scanner context.
+ *
+ * \param scanner Scanner context.
+ *
+ * \retval 0 if success.
+ * \retval -1 if error.
+ */
+int zs_parse_all(
+ zs_scanner_t *scanner
+);
+
+/*! @} */
diff --git a/src/libzscanner/scanner.rl b/src/libzscanner/scanner.rl
new file mode 100644
index 0000000..2776bad
--- /dev/null
+++ b/src/libzscanner/scanner.rl
@@ -0,0 +1,564 @@
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <math.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libzscanner/scanner.h"
+#include "libzscanner/functions.h"
+#include "libknot/descriptor.h"
+
+/*! \brief Maximal length of rdata item. */
+#define MAX_ITEM_LENGTH 255
+#define MAX_ITEM_LENGTH2 65535
+
+/*! \brief Latitude value for equator (2^31). */
+#define LOC_LAT_ZERO (uint32_t)2147483648
+/*! \brief Longitude value for meridian (2^31). */
+#define LOC_LONG_ZERO (uint32_t)2147483648
+/*! \brief Zero level altitude value. */
+#define LOC_ALT_ZERO (uint32_t)10000000
+
+/*! \brief Shorthand for setting warning data. */
+#define WARN(err_code) { s->error.code = err_code; }
+/*! \brief Shorthand for setting error data. */
+#define ERR(err_code) { WARN(err_code); s->error.fatal = true; }
+/*! \brief Shorthand for error reset. */
+#define NOERR { WARN(ZS_OK); s->error.fatal = false; }
+
+/*!
+ * \brief Writes record type number to r_data.
+ *
+ * \param type Type number.
+ * \param rdata_tail Position where to write type number to.
+ */
+static inline void type_num(const uint16_t type, uint8_t **rdata_tail)
+{
+ *((uint16_t *)*rdata_tail) = htons(type);
+ *rdata_tail += 2;
+}
+
+/*!
+ * \brief Sets bit to bitmap window.
+ *
+ * \param type Type number.
+ * \param s Scanner context.
+ */
+static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) {
+ uint8_t win = type / 256;
+ uint8_t bit_pos = type % 256;
+ uint8_t byte_pos = bit_pos / 8;
+
+ ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8);
+
+ if ((s->windows[win]).length < byte_pos + 1) {
+ (s->windows[win]).length = byte_pos + 1;
+ }
+
+ if (s->last_window < win) {
+ s->last_window = win;
+ }
+}
+
+// Include scanner file (in Ragel).
+%%{
+ machine zone_scanner;
+
+ include "scanner_body.rl";
+
+ write data;
+}%%
+
+__attribute__((visibility("default")))
+int zs_init(
+ zs_scanner_t *s,
+ const char *origin,
+ const uint16_t rclass,
+ const uint32_t ttl)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ memset(s, 0, sizeof(*s));
+
+ // Nonzero initial scanner state.
+ s->cs = %%{ write start; }%%;
+
+ // Reset the file descriptor.
+ s->file.descriptor = -1;
+
+ // Use the root zone as origin if not specified.
+ if (origin == NULL || strlen(origin) == 0) {
+ origin = ".";
+ }
+ size_t origin_len = strlen(origin);
+
+ // Prepare a zone settings header.
+ const char *format;
+ if (origin[origin_len - 1] != '.') {
+ format = "$ORIGIN %s.\n";
+ } else {
+ format = "$ORIGIN %s\n";
+ }
+
+ char settings[1024];
+ int ret = snprintf(settings, sizeof(settings), format, origin);
+ if (ret <= 0 || ret >= sizeof(settings)) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+
+ // Parse the settings to set up the scanner origin.
+ if (zs_set_input_string(s, settings, ret) != 0 ||
+ zs_parse_all(s) != 0) {
+ return -1;
+ }
+
+ // Set scanner defaults.
+ s->path = strdup(".");
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ return -1;
+ }
+ s->default_class = rclass;
+ s->default_ttl = ttl;
+ s->line_counter = 1;
+
+ s->state = ZS_STATE_NONE;
+ s->process.automatic = false;
+
+ return 0;
+}
+
+static void input_deinit(
+ zs_scanner_t *s,
+ bool keep_filename)
+{
+ // Deinit the file input.
+ if (s->file.descriptor != -1) {
+ // Unmap the file content.
+ if (s->input.start != NULL) {
+ if (s->input.mmaped) {
+ munmap((void *)s->input.start,
+ s->input.end - s->input.start);
+ } else {
+ free((void *)s->input.start);
+ }
+ }
+
+ // Close the opened file.
+ close(s->file.descriptor);
+ s->file.descriptor = -1;
+ }
+
+ // Keep file name for possible trailing error report.
+ if (!keep_filename) {
+ free(s->file.name);
+ s->file.name = NULL;
+ }
+
+ // Unset the input limits.
+ s->input.start = NULL;
+ s->input.current = NULL;
+ s->input.end = NULL;
+ s->input.eof = false;
+}
+
+__attribute__((visibility("default")))
+void zs_deinit(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return;
+ }
+
+ input_deinit(s, false);
+ free(s->path);
+}
+
+static int set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size,
+ bool final_block)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (input == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, final_block);
+
+ // Set the scanner input limits.
+ s->input.start = input;
+ s->input.current = input;
+ s->input.end = input + size;
+ s->input.eof = final_block;
+
+ return 0;
+}
+
+static char *read_file_to_buf(
+ int fd,
+ size_t *bufsize)
+{
+ size_t bufs = 0, newbufs = 8192;
+ char *buf = malloc(bufs + newbufs);
+ int ret = 0;
+
+ while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) {
+ bufs += newbufs;
+ newbufs = bufs;
+ char *newbuf = realloc(buf, bufs + newbufs);
+ if (newbuf == NULL) {
+ free(buf);
+ }
+ buf = newbuf;
+ }
+ if (ret < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ *bufsize = bufs + ret;
+ return buf;
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_string(
+ zs_scanner_t *s,
+ const char *input,
+ size_t size)
+{
+ s->state = ZS_STATE_NONE;
+
+ return set_input_string(s, input, size, false);
+}
+
+__attribute__((visibility("default")))
+int zs_set_input_file(
+ zs_scanner_t *s,
+ const char *file_name)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ if (file_name == NULL) {
+ ERR(ZS_EINVAL);
+ return -1;
+ }
+
+ // Deinit possibly opened file.
+ input_deinit(s, false);
+
+ // Try to open the file.
+ s->file.descriptor = open(file_name, O_RDONLY);
+ if (s->file.descriptor == -1) {
+ ERR(errno == EACCES ? ZS_FILE_ACCESS : ZS_FILE_OPEN);
+ return -1;
+ }
+
+ char *start = NULL;
+ size_t size = 0;
+
+ // Check the input.
+ struct stat file_stat;
+ if (fstat(s->file.descriptor, &file_stat) == -1) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (S_ISCHR(file_stat.st_mode) ||
+ S_ISBLK(file_stat.st_mode) ||
+ S_ISFIFO(file_stat.st_mode)) {
+ // Workaround if cannot mmap, read to memory.
+ start = read_file_to_buf(s->file.descriptor, &size);
+ if (start == NULL) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+ } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file.
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ } else if (file_stat.st_size > 0) { // Mmap non-empty file.
+ start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED,
+ s->file.descriptor, 0);
+ if (start == MAP_FAILED) {
+ ERR(ZS_FILE_INVALID);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ size = file_stat.st_size;
+ s->input.mmaped = true;
+
+ // Try to set the mapped memory advise to sequential.
+#if defined(MADV_SEQUENTIAL) && !defined(__sun)
+ (void)madvise(start, size, MADV_SEQUENTIAL);
+#else
+#ifdef POSIX_MADV_SEQUENTIAL
+ (void)posix_madvise(start, size, POSIX_MADV_SEQUENTIAL);
+#endif /* POSIX_MADV_SEQUENTIAL */
+#endif /* MADV_SEQUENTIAL && !__sun */
+ }
+
+ // Set the scanner input limits.
+ s->input.start = start;
+ s->input.current = start;
+ s->input.end = (start != NULL) ? start + size : start;
+
+ // Get absolute path of the zone file if possible.
+ char *full_name = realpath(file_name, NULL);
+ if (full_name != NULL) {
+ free(s->path);
+ s->path = strdup(dirname(full_name));
+ free(full_name);
+ if (s->path == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+ }
+
+ s->file.name = strdup(file_name);
+ if (s->file.name == NULL) {
+ ERR(ZS_ENOMEM);
+ input_deinit(s, false);
+ return -1;
+ }
+
+ s->state = ZS_STATE_NONE;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing(
+ zs_scanner_t *s,
+ void (*process_record)(zs_scanner_t *),
+ void (*process_error)(zs_scanner_t *),
+ void *data)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.record = process_record;
+ s->process.error = process_error;
+ s->process.data = data;
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_set_processing_comment(
+ zs_scanner_t *s,
+ void (*process_comment)(zs_scanner_t *))
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.comment = process_comment;
+
+ return 0;
+}
+
+typedef enum {
+ WRAP_NONE, // Initial state.
+ WRAP_DETECTED, // Input block end is a first '\' in rdata.
+ WRAP_PROCESS // Parsing of auxiliary block = "\".
+} wrap_t;
+
+static void parse(
+ zs_scanner_t *s,
+ wrap_t *wrap)
+{
+ // Restore scanner input limits (Ragel internals).
+ const char *p = s->input.current;
+ const char *pe = s->input.end;
+ const char *eof = s->input.eof ? pe : NULL;
+
+ // Restore state variables (Ragel internals).
+ int cs = s->cs;
+ int top = s->top;
+ int stack[ZS_RAGEL_STACK_SIZE];
+ memcpy(stack, s->stack, sizeof(stack));
+
+ // Next 2 variables are for better performance.
+ // Restoring r_data pointer to next free space.
+ uint8_t *rdata_tail = s->r_data + s->r_data_tail;
+ // Initialization of the last r_data byte.
+ uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1;
+
+ // Write scanner body (in C).
+ %% write exec;
+
+ // Check if the scanner state machine is in an uncovered state.
+ bool extra_error = false;
+ if (cs == %%{ write error; }%%) {
+ ERR(ZS_UNCOVERED_STATE);
+ extra_error = true;
+ // Check for an unclosed multiline record.
+ } else if (s->input.eof && s->multiline) {
+ ERR(ZS_UNCLOSED_MULTILINE);
+ extra_error = true;
+ s->line_counter--;
+ }
+
+ // Treat the extra error.
+ if (extra_error) {
+ s->error.counter++;
+ s->state = ZS_STATE_ERROR;
+
+ // Copy the error context just for the part of the current line.
+ s->buffer_length = 0;
+ while (p < pe && *p != '\n' && s->buffer_length < 50) {
+ s->buffer[s->buffer_length++] = *p++;
+ }
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the error callback.
+ if (s->process.automatic && s->process.error != NULL) {
+ s->process.error(s);
+ }
+
+ return;
+ }
+
+ // Storing scanner states.
+ s->cs = cs;
+ s->top = top;
+ memcpy(s->stack, stack, sizeof(stack));
+
+ // Store the current parser position.
+ s->input.current = p;
+
+ // Storing r_data pointer.
+ s->r_data_tail = rdata_tail - s->r_data;
+
+ if (*wrap == WRAP_DETECTED) {
+ if (set_input_string(s, "\\", 1, true) != 0) {
+ return;
+ }
+
+ *wrap = WRAP_PROCESS;
+ parse(s, wrap);
+ } else {
+ *wrap = WRAP_NONE;
+ }
+}
+
+__attribute__((visibility("default")))
+int zs_parse_record(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ // Check if parsing is possible.
+ switch (s->state) {
+ case ZS_STATE_NONE:
+ case ZS_STATE_DATA:
+ case ZS_STATE_INCLUDE:
+ break;
+ case ZS_STATE_ERROR:
+ if (s->error.fatal) {
+ return -1;
+ }
+ break;
+ default:
+ // Return if stop or end of file.
+ return 0;
+ }
+
+ // Check for the end of the input.
+ if (s->input.current != s->input.end) {
+ // Try to parse another item.
+ s->state = ZS_STATE_NONE;
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Finish if nothing was parsed.
+ if (s->state == ZS_STATE_NONE) {
+ // Parse the final block.
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ if (s->state == ZS_STATE_NONE) {
+ s->state = ZS_STATE_EOF;
+ }
+ }
+ } else {
+ s->state = ZS_STATE_EOF;
+ }
+
+ return 0;
+}
+
+__attribute__((visibility("default")))
+int zs_parse_all(
+ zs_scanner_t *s)
+{
+ if (s == NULL) {
+ return -1;
+ }
+
+ s->process.automatic = true;
+
+ // Parse input block.
+ wrap_t wrap = WRAP_NONE;
+ parse(s, &wrap);
+
+ // Parse trailing newline-char block if it makes sense.
+ if (s->state != ZS_STATE_STOP && !s->error.fatal) {
+ if (set_input_string(s, "\n", 1, true) != 0) {
+ return -1;
+ }
+ parse(s, &wrap);
+ }
+
+ // Check if any errors have occurred.
+ if (s->error.counter > 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/libzscanner/scanner_body.rl b/src/libzscanner/scanner_body.rl
new file mode 100644
index 0000000..34d51cd
--- /dev/null
+++ b/src/libzscanner/scanner_body.rl
@@ -0,0 +1,2326 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+%%{
+ machine zone_scanner;
+
+ # Comeback function to calling state machine.
+ action _ret {
+ fhold; fret;
+ }
+
+ # BEGIN - Blank space processing
+ action _newline {
+ s->line_counter++;
+ }
+
+ action _check_multiline_begin {
+ if (s->multiline == true) {
+ ERR(ZS_LEFT_PARENTHESIS);
+ fhold; fgoto err_line;
+ }
+ s->multiline = true;
+ }
+ action _check_multiline_end {
+ if (s->multiline == false) {
+ ERR(ZS_RIGHT_PARENTHESIS);
+ fhold; fgoto err_line;
+ }
+ s->multiline = false;
+ }
+
+ action _comment_init {
+ s->buffer_length = 0;
+ }
+ action _comment {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = fc;
+ }
+ }
+ action _comment_exit {
+ s->buffer[s->buffer_length++] = 0;
+
+ // Execute the comment callback.
+ if (s->process.automatic && s->process.comment != NULL) {
+ s->process.comment(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ fbreak;
+ }
+ }
+ }
+
+ action _rest_init {
+ s->buffer[0] = 0;
+ s->buffer_length = 0;
+ }
+ action _rest_error {
+ WARN(ZS_BAD_REST);
+ fhold; fgoto err_line;
+ }
+
+ newline = '\n' $_newline;
+ comment = (';' . (^newline)* $_comment) >_comment_init %_comment_exit;
+
+ # White space separation. With respect to parentheses and included comments.
+ sep = ( [ \t] # Blank characters.
+ | (comment? . newline) when { s->multiline } # Comment in multiline.
+ | '(' $_check_multiline_begin # Start of multiline.
+ | ')' $_check_multiline_end # End of multiline.
+ )+; # Apply more times.
+
+ rest = (sep? :> comment?) >_rest_init $!_rest_error; # Comments.
+
+ # Artificial machines which are used for next state transition only!
+ all_wchar = [ \t\n;()];
+ end_wchar = [\n;] when { !s->multiline }; # For noncontinuous ending tokens.
+ # END
+
+ # BEGIN - Error line processing
+ action _err_line_init {
+ s->buffer_length = 0;
+ }
+ action _err_line {
+ if (fc == '\r') {
+ ERR(ZS_DOS_NEWLINE);
+ }
+
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = fc;
+ }
+ }
+ action _err_line_exit {
+ // Terminate the error context string.
+ s->buffer[s->buffer_length++] = 0;
+
+ // Error counter incrementation.
+ s->error.counter++;
+
+ // Initialize the fcall stack.
+ top = 0;
+
+ // Reset per-record contexts.
+ s->long_string = false;
+ s->comma_list = false;
+
+ s->state = ZS_STATE_ERROR;
+
+ // Execute the error callback.
+ if (s->process.automatic) {
+ fhold;
+ if (s->process.error != NULL) {
+ s->process.error(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ fbreak;
+ }
+ }
+
+ // Stop the scanner if fatal error.
+ if (s->error.fatal) {
+ fbreak;
+ }
+ fgoto err_rest;
+ } else {
+ // Return if external processing.
+ fhold; fnext err_rest; fbreak;
+ }
+ }
+
+ # Consume rest lines of defective multiline record.
+ err_rest := ( (any - newline - ')')
+ | newline when { s->multiline }
+ | ')' when { s->multiline } $_check_multiline_end
+ )* %{ fhold; fcall main; } <: newline;
+
+ # Fill rest of the line to buffer and skip to main loop.
+ err_line := (^newline $_err_line)* >_err_line_init
+ %_err_line_exit . newline;
+ # END
+
+ # BEGIN - Domain name labels processing
+ action _label_init {
+ s->item_length = 0;
+ s->item_length_position = s->dname_tmp_length++;
+ }
+ action _label_char {
+ // Check for maximum dname label length.
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length++] = fc;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _label_exit {
+ // Check for maximum dname length overflow after each label.
+ // (at least the next label length must follow).
+ if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) {
+ (s->dname)[s->item_length_position] =
+ (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ action _label_dec_init {
+ if (s->item_length < ZS_MAX_LABEL_LENGTH) {
+ (s->dname)[s->dname_tmp_length] = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_LABEL_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _label_dec {
+ (s->dname)[s->dname_tmp_length] *= 10;
+ (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)fc];
+ }
+ action _label_dec_exit {
+ s->dname_tmp_length++;
+ }
+ action _label_dec_error {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+
+ label_char =
+ ( (alnum | [*\-_/]) $_label_char # One common char.
+ | ('\\' . ^digit) @_label_char # One "\x" char.
+ | ('\\' %_label_dec_init # Initial "\" char.
+ . digit {3} $_label_dec %_label_dec_exit # "DDD" rest.
+ $!_label_dec_error
+ )
+ );
+
+ label = label_char+ >_label_init %_label_exit;
+ labels = (label . '.')* . label;
+ # END
+
+ # BEGIN - Domain name processing.
+ action _absolute_dname_exit {
+ // Enough room for the terminal label is guaranteed (_label_exit).
+ (s->dname)[s->dname_tmp_length++] = 0;
+ }
+ action _relative_dname_exit {
+ // Check for (relative + origin) dname length overflow.
+ if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) {
+ memcpy(s->dname + s->dname_tmp_length,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length += s->zone_origin_length;
+ } else {
+ WARN(ZS_DNAME_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _origin_dname_exit {
+ // Copy already verified zone origin.
+ memcpy(s->dname,
+ s->zone_origin,
+ s->zone_origin_length);
+
+ s->dname_tmp_length = s->zone_origin_length;
+ }
+
+ action _dname_init {
+ s->item_length_position = 0;
+ s->dname_tmp_length = 0;
+ }
+ action _dname_error {
+ WARN(ZS_BAD_DNAME_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ relative_dname = (labels ) >_dname_init %_relative_dname_exit;
+ absolute_dname = (labels? . '.') >_dname_init %_absolute_dname_exit;
+
+ dname_ := ( relative_dname
+ | absolute_dname
+ | '@' %_origin_dname_exit
+ ) $!_dname_error %_ret . all_wchar;
+ dname = (alnum | [\-_/\\] | [*.@]) ${ fhold; fcall dname_; };
+ # END
+
+ # BEGIN - Common r_data item processing
+ action _item_length_init {
+ if (rdata_tail <= rdata_stop) {
+ s->item_length_location = rdata_tail++;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _item_length_exit {
+ s->item_length = rdata_tail - s->item_length_location - 1;
+ if (s->comma_list && s->item_length == 0) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ fhold; fgoto err_line;
+ }
+ if (s->item_length <= MAX_ITEM_LENGTH) {
+ *(s->item_length_location) = (uint8_t)(s->item_length);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _item_length2_init {
+ if (rdata_tail < rdata_stop) {
+ s->item_length2_location = rdata_tail;
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _item_length2_exit {
+ s->item_length = rdata_tail - s->item_length2_location - 2;
+
+ if (s->item_length <= MAX_ITEM_LENGTH2) {
+ uint16_t val = htons((uint16_t)(s->item_length));
+ memcpy(s->item_length2_location, &val, 2);
+ } else {
+ WARN(ZS_ITEM_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ # END
+
+ # BEGIN - Owner processing
+ action _r_owner_init {
+ s->dname = s->r_owner;
+ s->r_owner_length = 0;
+ }
+ action _r_owner_exit {
+ s->r_owner_length = s->dname_tmp_length;
+ }
+ action _r_owner_empty_exit {
+ if (s->r_owner_length == 0) {
+ WARN(ZS_BAD_PREVIOUS_OWNER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _r_owner_error {
+ s->r_owner_length = 0;
+ WARN(ZS_BAD_OWNER);
+ fhold; fgoto err_line;
+ }
+
+ r_owner = ( dname >_r_owner_init %_r_owner_exit
+ | zlen %_r_owner_empty_exit # Empty owner - use the previous one.
+ ) $!_r_owner_error;
+ # END
+
+ # BEGIN - domain name in record data processing
+ action _r_dname_init {
+ s->dname = rdata_tail;
+ }
+ action _r_dname_exit {
+ rdata_tail += s->dname_tmp_length;
+ }
+
+ r_dname = dname >_r_dname_init %_r_dname_exit;
+ # END
+
+ # BEGIN - Number processing
+ action _number_digit {
+ // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX
+ if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check.
+ ((s->number64 == (UINT64_MAX / 10)) && // Marginal case.
+ ((uint8_t)fc <= (UINT64_MAX % 10) + '0')
+ )
+ ) {
+ s->number64 *= 10;
+ s->number64 += digit_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_NUMBER64_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ number_digit = [0-9] $_number_digit;
+
+ action _number_init {
+ s->number64 = 0;
+ }
+ action _number_error {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+
+ # General integer number that cover all necessary integer ranges.
+ number = number_digit+ >_number_init;
+
+ action _float_init {
+ s->decimal_counter = 0;
+ }
+ action _decimal_init {
+ s->number64_tmp = s->number64;
+ }
+ action _decimal_digit {
+ s->decimal_counter++;
+ }
+
+ action _float_exit {
+ if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals);
+ } else if (s->decimal_counter <= s->decimals &&
+ s->number64_tmp < UINT32_MAX) {
+ s->number64 *= pow(10, s->decimals - s->decimal_counter);
+ s->number64 += s->number64_tmp * pow(10, s->decimals);
+ } else {
+ WARN(ZS_FLOAT_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ # Next float can't be used directly (doesn't contain decimals init)!
+ float = (number . ('.' . number? >_decimal_init $_decimal_digit)?)
+ >_float_init %_float_exit;
+
+ action _float2_init {
+ s->decimals = 2;
+ }
+ action _float3_init {
+ s->decimals = 3;
+ }
+
+ # Float number (in hundredths)with 2 possible decimal digits.
+ float2 = float >_float2_init;
+ # Float number (in thousandths) with 3 possible decimal digits.
+ float3 = float >_float3_init;
+
+ action _num8_write {
+ if (s->number64 <= UINT8_MAX) {
+ *rdata_tail = (uint8_t)(s->number64);
+ rdata_tail += 1;
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _num16_write {
+ if (s->number64 <= UINT16_MAX) {
+ uint16_t num16 = htons((uint16_t)s->number64);
+ memcpy(rdata_tail, &num16, 2);
+ rdata_tail += 2;
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _num32_write {
+ if (s->number64 <= UINT32_MAX) {
+ uint32_t num32 = htonl((uint32_t)s->number64);
+ memcpy(rdata_tail, &num32, 4);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ action _type_number_exit {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_type = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ action _length_number_exit {
+ if (s->number64 <= UINT16_MAX) {
+ s->r_data_length = (uint16_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ num8 = number %_num8_write $!_number_error;
+ num16 = number %_num16_write $!_number_error;
+ num32 = number %_num32_write $!_number_error;
+
+ type_number = number %_type_number_exit $!_number_error;
+ length_number = number %_length_number_exit $!_number_error;
+ # END
+
+ # BEGIN - Time processing
+ action _time_unit_error {
+ WARN(ZS_BAD_TIME_UNIT);
+ fhold; fgoto err_line;
+ }
+
+ time_unit =
+ ( 's'i
+ | 'm'i ${ if (s->number64 <= (UINT32_MAX / 60)) {
+ s->number64 *= 60;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ | 'h'i ${ if (s->number64 <= (UINT32_MAX / 3600)) {
+ s->number64 *= 3600;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ | 'd'i ${ if (s->number64 <= (UINT32_MAX / 86400)) {
+ s->number64 *= 86400;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ | 'w'i ${ if (s->number64 <= (UINT32_MAX / 604800)) {
+ s->number64 *= 604800;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ ) $!_time_unit_error;
+
+
+ action _time_block_init {
+ s->number64_tmp = s->number64;
+ }
+ action _time_block_exit {
+ if (s->number64 + s->number64_tmp < UINT32_MAX) {
+ s->number64 += s->number64_tmp;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ time_block = (number . time_unit) >_time_block_init %_time_block_exit;
+
+ # Time is either a number or a sequence of time blocks (1w1h1m).
+ time = (number . (time_unit . (time_block)*)?) $!_number_error;
+
+ time32 = time %_num32_write;
+ # END
+
+ # BEGIN - Timestamp processing
+ action _timestamp_init {
+ s->buffer_length = 0;
+ }
+ action _timestamp {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = fc;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _timestamp_exit {
+ s->buffer[s->buffer_length] = 0;
+
+ if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS").
+ uint32_t timestamp;
+ int ret = date_to_timestamp(s->buffer, &timestamp);
+
+ if (ret == ZS_OK) {
+ *((uint32_t *)rdata_tail) = htonl(timestamp);
+ rdata_tail += 4;
+ } else {
+ WARN(ret);
+ fhold; fgoto err_line;
+ }
+ } else if (s->buffer_length <= 10) { // Timestamp format.
+ char *end;
+
+ s->number64 = strtoull((char *)(s->buffer), &end, 10);
+
+ if (end == (char *)(s->buffer) || *end != '\0') {
+ WARN(ZS_BAD_TIMESTAMP);
+ fhold; fgoto err_line;
+ }
+
+ if (s->number64 <= UINT32_MAX) {
+ *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64);
+ rdata_tail += 4;
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ } else {
+ WARN(ZS_BAD_TIMESTAMP_LENGTH);
+ fhold; fgoto err_line;
+ }
+ }
+ action _timestamp_error {
+ WARN(ZS_BAD_TIMESTAMP_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ timestamp = digit+ >_timestamp_init $_timestamp
+ %_timestamp_exit $!_timestamp_error;
+ # END
+
+ # BEGIN - Text processing
+ action _text_char {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ *(rdata_tail++) = fc;
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _text_char_error {
+ WARN(ZS_BAD_TEXT_CHAR);
+ fhold; fgoto err_line;
+ }
+ action _text_error {
+ WARN(ZS_BAD_TEXT);
+ fhold; fgoto err_line;
+ }
+
+ action _text_dec_init {
+ if (rdata_tail <= rdata_stop) {
+ // Split long string.
+ if (s->long_string &&
+ rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) {
+ // _item_length_exit equivalent.
+ *(s->item_length_location) = MAX_ITEM_LENGTH;
+ // _item_length_init equivalent.
+ s->item_length_location = rdata_tail++;
+
+ if (rdata_tail > rdata_stop) {
+ WARN(ZS_TEXT_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ *rdata_tail = 0;
+ s->item_length++;
+ } else {
+ WARN(ZS_TEXT_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _text_dec {
+ if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check.
+ ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case.
+ (fc <= (UINT8_MAX % 10) + '0')
+ )
+ ) {
+ *rdata_tail *= 10;
+ *rdata_tail += digit_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_NUMBER8_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _text_dec_exit {
+ rdata_tail++;
+ }
+ action _text_dec_error {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+
+ action _comma_list {
+ uint8_t *last_two = rdata_tail - 2;
+ uint16_t current_len = rdata_tail - s->item_length_location - 2;
+ if (s->comma_list) {
+ if (last_two[1] == ',') {
+ if (current_len <= 1) {
+ WARN(ZS_EMPTY_LIST_ITEM);
+ fhold; fgoto err_line;
+ } else if (last_two[0] != '\\') { // Start a new item.
+ *(s->item_length_location) = current_len;
+ s->item_length_location = rdata_tail - 1;
+ } else { // Remove backslash.
+ last_two[0] = ',';
+ rdata_tail--;
+ }
+ } else if (current_len > 1 && last_two[1] == '\\') {
+ if (last_two[0] == '\\') { // Remove backslash.
+ rdata_tail--;
+ }
+ }
+ }
+ }
+
+ text_char =
+ ( (33..126 - [\\;\"]) $_text_char # One printable char.
+ | ('\\' . (32..126 - digit)) @_text_char # One "\x" char.
+ | ('\\' %_text_dec_init # Initial "\" char.
+ . digit {3} $_text_dec %_text_dec_exit # "DDD" rest.
+ $!_text_dec_error
+ )
+ ) %_comma_list $!_text_char_error;
+
+ quoted_text_char =
+ ( text_char
+ | ([ \t;] | [\n] when { s->multiline }) $_text_char
+ ) $!_text_char_error;
+
+ # Text string machine instantiation (for smaller code).
+ text_ := (('\"' . quoted_text_char* . '\"') | text_char+)
+ $!_text_error %_ret . all_wchar;
+ text = ^all_wchar ${ fhold; fcall text_; };
+
+ # Text string with forward 1-byte length.
+ text_string = text >_item_length_init %_item_length_exit;
+
+ action _text_array_init {
+ s->long_string = true;
+ }
+ action _text_array_exit {
+ s->long_string = false;
+ }
+
+ # Text string array as one rdata item.
+ text_array =
+ ( (text_string . (sep . text_string)* . sep?)
+ ) >_text_array_init %_text_array_exit $!_text_array_exit;
+ # END
+
+ # BEGIN - TTL directive processing
+ action _default_ttl_exit {
+ if (s->number64 <= UINT32_MAX) {
+ s->default_ttl = (uint32_t)(s->number64);
+ } else {
+ ERR(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _default_ttl_error {
+ ERR(ZS_BAD_TTL);
+ fhold; fgoto err_line;
+ }
+
+ default_ttl_ := (sep . time . rest) $!_default_ttl_error
+ %_default_ttl_exit %_ret . newline;
+ default_ttl = all_wchar ${ fhold; fcall default_ttl_; };
+ # END
+
+ # BEGIN - ORIGIN directive processing
+ action _zone_origin_init {
+ s->dname = s->zone_origin;
+ }
+ action _zone_origin_exit {
+ s->zone_origin_length = s->dname_tmp_length;
+ }
+ action _zone_origin_error {
+ ERR(ZS_BAD_ORIGIN);
+ fhold; fgoto err_line;
+ }
+
+ zone_origin_ := (sep . absolute_dname >_zone_origin_init . rest)
+ $!_zone_origin_error %_zone_origin_exit %_ret . newline;
+ zone_origin = all_wchar ${ fhold; fcall zone_origin_; };
+ # END
+
+ # BEGIN - INCLUDE directive processing
+ action _incl_filename_init {
+ rdata_tail = s->r_data;
+ }
+ action _incl_filename_exit {
+ size_t len = rdata_tail - s->r_data;
+ if (len >= sizeof(s->include_filename)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ fhold; fgoto err_line;
+ }
+
+ // Store zero terminated include filename.
+ memcpy(s->include_filename, s->r_data, len);
+ s->include_filename[len] = '\0';
+
+ // For detection whether origin is not present.
+ s->dname = NULL;
+ }
+ action _incl_filename_error {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ fhold; fgoto err_line;
+ }
+
+ action _incl_origin_init {
+ s->dname = s->r_data;
+ }
+ action _incl_origin_exit {
+ s->r_data_length = s->dname_tmp_length;
+ }
+ action _incl_origin_error {
+ ERR(ZS_BAD_INCLUDE_ORIGIN);
+ fhold; fgoto err_line;
+ }
+
+ action _include_exit {
+ // Extend relative file path.
+ if (s->include_filename[0] != '/') {
+ int ret = snprintf((char *)(s->buffer), sizeof(s->buffer),
+ "%s/%s", s->path, s->include_filename);
+ if (ret <= 0 || ret >= sizeof(s->buffer)) {
+ ERR(ZS_BAD_INCLUDE_FILENAME);
+ fhold; fgoto err_line;
+ }
+ memcpy(s->include_filename, s->buffer, ret + 1);
+ }
+
+ // Origin conversion from wire to text form in \DDD notation.
+ if (s->dname == NULL) { // Use current origin.
+ wire_dname_to_str(s->zone_origin,
+ s->zone_origin_length,
+ (char *)s->buffer);
+ } else { // Use specified origin.
+ wire_dname_to_str(s->r_data,
+ s->r_data_length,
+ (char *)s->buffer);
+ }
+
+ // Let the caller to solve the include.
+ if (s->process.automatic) {
+ // Create new scanner for included zone file.
+ zs_scanner_t *ss = malloc(sizeof(zs_scanner_t));
+ if (ss == NULL) {
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ fhold; fgoto err_line;
+ }
+
+ // Parse included zone file.
+ if (zs_init(ss, (char *)s->buffer, s->default_class,
+ s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error,
+ s->process.data) != 0 ||
+ zs_parse_all(ss) != 0) {
+ // File internal errors are handled by error callback.
+ if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ ERR(ZS_UNPROCESSED_INCLUDE);
+ // General include file error.
+ } else {
+ ERR(ss->error.code);
+ }
+ zs_deinit(ss);
+ free(ss);
+ fhold; fgoto err_line;
+ }
+ zs_deinit(ss);
+ free(ss);
+ } else {
+ s->state = ZS_STATE_INCLUDE;
+ fhold; fnext main; fbreak;
+ }
+ }
+
+ include_file_ :=
+ (sep . text >_incl_filename_init %_incl_filename_exit
+ $!_incl_filename_error .
+ (sep . absolute_dname >_incl_origin_init %_incl_origin_exit
+ $!_incl_origin_error
+ )? . rest
+ ) %_include_exit %_ret newline;
+ include_file = all_wchar ${ fhold; fcall include_file_; };
+ # END
+
+ # BEGIN - Directive switch
+ # Each error/warning in directive should stop processing.
+ # Some internal errors cause warning only. This causes stop processing.
+ action _directive_init {
+ ERR(ZS_OK);
+ }
+ # Remove stop processing flag.
+ action _directive_exit {
+ NOERR;
+ }
+ action _directive_error {
+ ERR(ZS_BAD_DIRECTIVE);
+ fhold; fgoto err_line;
+ }
+
+ directive = '$' . ( ("TTL"i . default_ttl)
+ | ("ORIGIN"i . zone_origin)
+ | ("INCLUDE"i . include_file)
+ ) >_directive_init %_directive_exit $!_directive_error;
+ # END
+
+ # BEGIN - RRecord class and ttl processing
+ action _default_r_class_exit {
+ s->r_class = s->default_class;
+ }
+
+ action _default_r_ttl_exit {
+ s->r_ttl = s->default_ttl;
+ }
+
+ action _r_class_in_exit {
+ s->r_class = KNOT_CLASS_IN;
+ }
+
+ action _r_ttl_exit {
+ if (s->number64 <= UINT32_MAX) {
+ s->r_ttl = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_NUMBER32_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ r_class = "IN"i %_r_class_in_exit;
+
+ r_ttl = time %_r_ttl_exit;
+ # END
+
+ # BEGIN - IPv4 and IPv6 address processing
+ action _addr_init {
+ s->buffer_length = 0;
+ }
+ action _addr {
+ if (s->buffer_length < sizeof(s->buffer) - 1) {
+ s->buffer[s->buffer_length++] = fc;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _addr_error {
+ WARN(ZS_BAD_ADDRESS_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ action _ipv4_addr_exit {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV4);
+ fhold; fgoto err_line;
+ }
+ }
+ action _ipv4_addr_write {
+ if (rdata_tail + ZS_INET4_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH);
+ rdata_tail += ZS_INET4_ADDR_LENGTH;
+ }
+
+ action _ipv6_addr_exit {
+ s->buffer[s->buffer_length] = 0;
+
+ if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) {
+ WARN(ZS_BAD_IPV6);
+ fhold; fgoto err_line;
+ }
+ }
+ action _ipv6_addr_write {
+ if (rdata_tail + ZS_INET6_ADDR_LENGTH > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH);
+ rdata_tail += ZS_INET6_ADDR_LENGTH;
+ }
+
+ # Address parsers only.
+ ipv4_addr = (digit | '.')+ >_addr_init $_addr %_ipv4_addr_exit
+ $!_addr_error;
+ ipv6_addr = (xdigit | [.:])+ >_addr_init $_addr %_ipv6_addr_exit
+ $!_addr_error;
+
+ # Write parsed address to r_data.
+ ipv4_addr_write = ipv4_addr %_ipv4_addr_write;
+ ipv6_addr_write = ipv6_addr %_ipv6_addr_write;
+ # END
+
+ # BEGIN - apl record processing
+ action _apl_init {
+ memset(&(s->apl), 0, sizeof(s->apl));
+ }
+ action _apl_excl_flag {
+ s->apl.excl_flag = 128; // dec 128 = bin 10000000.
+ }
+ action _apl_addr_1 {
+ s->apl.addr_family = 1;
+ }
+ action _apl_addr_2 {
+ s->apl.addr_family = 2;
+ }
+ action _apl_prefix_length {
+ if ((s->apl.addr_family == 1 && s->number64 <= 32) ||
+ (s->apl.addr_family == 2 && s->number64 <= 128)) {
+ s->apl.prefix_length = (uint8_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_APL);
+ fhold; fgoto err_line;
+ }
+ }
+ action _apl_exit {
+ // Copy address to buffer.
+ uint8_t len;
+ switch (s->apl.addr_family) {
+ case 1:
+ len = ZS_INET4_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ case 2:
+ len = ZS_INET6_ADDR_LENGTH;
+ memcpy(s->buffer, s->addr, len);
+ break;
+ default:
+ WARN(ZS_BAD_APL);
+ fhold; fgoto err_line;
+ }
+ // Find prefix without trailing zeroes.
+ while (len > 0) {
+ if ((s->buffer[len - 1] & 255) != 0) {
+ break;
+ }
+ len--;
+ }
+ // Check for rdata overflow.
+ if (rdata_tail + 4 + len > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ // Write address family.
+ uint16_t af = htons(s->apl.addr_family);
+ memcpy(rdata_tail, &af, sizeof(af));
+ rdata_tail += 2;
+ // Write prefix length in bits.
+ *(rdata_tail) = s->apl.prefix_length;
+ rdata_tail += 1;
+ // Write negation flag + prefix length in bytes.
+ *(rdata_tail) = len + s->apl.excl_flag;
+ rdata_tail += 1;
+ // Write address prefix non-null data.
+ memcpy(rdata_tail, s->buffer, len);
+ rdata_tail += len;
+ }
+ action _apl_error {
+ WARN(ZS_BAD_APL);
+ fhold; fgoto err_line;
+ }
+
+ apl = ('!'? $_apl_excl_flag .
+ ( ('1' $_apl_addr_1 . ':' . ipv4_addr . '/' . number
+ %_apl_prefix_length)
+ | ('2' $_apl_addr_2 . ':' . ipv6_addr . '/' . number
+ %_apl_prefix_length)
+ )
+ ) >_apl_init %_apl_exit $!_apl_error;
+
+ # Array of APL records (can be empty).
+ apl_array = apl? . (sep . apl)* . sep?;
+ # END
+
+ # BEGIN - Hexadecimal string array processing
+ action _first_hex_char {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _second_hex_char {
+ *rdata_tail += second_hex_to_num[(uint8_t)fc];
+ rdata_tail++;
+ }
+ action _hex_char_error {
+ WARN(ZS_BAD_HEX_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ hex_char = (xdigit $_first_hex_char . xdigit $_second_hex_char);
+
+ # Hex array with possibility of inside white spaces and multiline.
+ hex_array = (hex_char+ . sep?)+ $!_hex_char_error;
+
+ # Continuous hex array (or "-") with forward length processing.
+ salt = (hex_char+ | '-') >_item_length_init %_item_length_exit
+ $!_hex_char_error;
+
+ action _type_data_exit {
+ if ((rdata_tail - s->r_data) != s->r_data_length) {
+ WARN(ZS_BAD_RDATA_LENGTH);
+ fhold; fgoto err_line;
+ }
+ }
+
+ action _type_data_error {
+ WARN(ZS_BAD_HEX_RDATA);
+ fhold; fgoto err_line;
+ }
+
+ # Hex array with control to forward length statement.
+ type_data = hex_array %_type_data_exit $!_type_data_error;
+ # END
+
+ # BEGIN - Base64 processing (RFC 4648)
+ action _first_base64_char {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base64_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _second_base64_char {
+ *(rdata_tail++) += second_left_base64_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base64_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _third_base64_char {
+ *(rdata_tail++) += third_left_base64_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = third_right_base64_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _fourth_base64_char {
+ *(rdata_tail++) += fourth_base64_to_num[(uint8_t)fc];
+ }
+
+ action _base64_char_error {
+ WARN(ZS_BAD_BASE64_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ base64_char = alnum | [+/];
+ base64_padd = '=';
+ base64_quartet =
+ ( base64_char $_first_base64_char . # A
+ base64_char $_second_base64_char . # AB
+ ( ( base64_char $_third_base64_char . # ABC
+ ( base64_char $_fourth_base64_char # ABCD
+ | base64_padd{1} # ABC=
+ )
+ )
+ | base64_padd{2} # AB==
+ )
+ );
+
+ # Base64 array with possibility of inside white spaces and multiline.
+ base64_ := (base64_quartet+ . sep?)+ $!_base64_char_error
+ %_ret . end_wchar;
+ base64 = base64_char ${ fhold; fcall base64_; };
+ # END
+
+ # BEGIN - Base32hex processing (RFC 4648)
+ action _first_base32hex_char {
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = first_base32hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _second_base32hex_char {
+ *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = second_right_base32hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _third_base32hex_char {
+ *rdata_tail += third_base32hex_to_num[(uint8_t)fc];
+ }
+ action _fourth_base32hex_char {
+ *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _fifth_base32hex_char {
+ *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _sixth_base32hex_char {
+ *rdata_tail += sixth_base32hex_to_num[(uint8_t)fc];
+ }
+ action _seventh_base32hex_char {
+ *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)fc];
+
+ if (rdata_tail <= rdata_stop) {
+ *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)fc];
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ action _eighth_base32hex_char {
+ *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)fc];
+ }
+
+ action _base32hex_char_error {
+ WARN(ZS_BAD_BASE32HEX_CHAR);
+ fhold; fgoto err_line;
+ }
+
+ base32hex_char = [0-9a-vA-V];
+ base32hex_padd = '=';
+ base32hex_octet =
+ ( base32hex_char $_first_base32hex_char . # A
+ base32hex_char $_second_base32hex_char . # AB
+ ( ( base32hex_char $_third_base32hex_char . # ABC
+ base32hex_char $_fourth_base32hex_char . # ABCD
+ ( ( base32hex_char $_fifth_base32hex_char . # ABCDE
+ ( ( base32hex_char $_sixth_base32hex_char . # ABCDEF
+ base32hex_char $_seventh_base32hex_char . # ABCDEFG
+ ( base32hex_char $_eighth_base32hex_char # ABCDEFGH
+ | base32hex_padd{1} # ABCDEFG=
+ )
+ )
+ | base32hex_padd{3} # ABCDE===
+ )
+ )
+ | base32hex_padd{4} # ABCD====
+ )
+ )
+ | base32hex_padd{6} # AB======
+ )
+ );
+
+ # Continuous base32hex (with padding!) array with forward length processing.
+ hash = base32hex_octet+ >_item_length_init %_item_length_exit
+ $!_base32hex_char_error;
+ # END
+
+ # BEGIN - Simple number write functions.
+ action _write8_0 {
+ *(rdata_tail++) = 0;
+ }
+ action _write8_1 {
+ *(rdata_tail++) = 1;
+ }
+ action _write8_2 {
+ *(rdata_tail++) = 2;
+ }
+ action _write8_3 {
+ *(rdata_tail++) = 3;
+ }
+ action _write8_5 {
+ *(rdata_tail++) = 5;
+ }
+ action _write8_6 {
+ *(rdata_tail++) = 6;
+ }
+ action _write8_7 {
+ *(rdata_tail++) = 7;
+ }
+ action _write8_8 {
+ *(rdata_tail++) = 8;
+ }
+ action _write8_10 {
+ *(rdata_tail++) = 10;
+ }
+ action _write8_12 {
+ *(rdata_tail++) = 12;
+ }
+ action _write8_13 {
+ *(rdata_tail++) = 13;
+ }
+ action _write8_14 {
+ *(rdata_tail++) = 14;
+ }
+ action _write8_15 {
+ *(rdata_tail++) = 15;
+ }
+ action _write8_16 {
+ *(rdata_tail++) = 16;
+ }
+ action _write8_252 {
+ *(rdata_tail++) = 252;
+ }
+ action _write8_253 {
+ *(rdata_tail++) = 253;
+ }
+ action _write8_254 {
+ *(rdata_tail++) = 254;
+ }
+
+ action _write16_0 {
+ uint16_t val = htons(0);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_1 {
+ uint16_t val = htons(1);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_2 {
+ uint16_t val = htons(2);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_3 {
+ uint16_t val = htons(3);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_4 {
+ uint16_t val = htons(4);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_5 {
+ uint16_t val = htons(5);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_6 {
+ uint16_t val = htons(6);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_7 {
+ uint16_t val = htons(7);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_8 {
+ uint16_t val = htons(8);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_253 {
+ uint16_t val = htons(253);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ action _write16_254 {
+ uint16_t val = htons(254);
+ memcpy(rdata_tail, &val, 2);
+ rdata_tail += 2;
+ }
+ # END
+
+ # BEGIN - Gateway
+ action _gateway_error {
+ WARN(ZS_BAD_GATEWAY);
+ fhold; fgoto err_line;
+ }
+ action _gateway_key_error {
+ WARN(ZS_BAD_GATEWAY_KEY);
+ fhold; fgoto err_line;
+ }
+
+ gateway = (( ('0' $_write8_0 . sep . num8 . sep . '.')
+ | ('1' $_write8_1 . sep . num8 . sep . ipv4_addr_write)
+ | ('2' $_write8_2 . sep . num8 . sep . ipv6_addr_write)
+ | ('3' $_write8_3 . sep . num8 . sep . r_dname)
+ ) $!_gateway_error .
+ # If algorithm is 0 then key isn't present and vice versa.
+ ( ((sep . base64) when { s->number64 != 0 })
+ | ((sep?) when { s->number64 == 0 }) # remove blank space
+ ) $!_gateway_key_error
+ );
+ # END
+
+ # BEGIN - Type processing
+ action _type_error {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ fhold; fgoto err_line;
+ }
+
+ type_num =
+ ( "A"i %{ type_num(KNOT_RRTYPE_A, &rdata_tail); }
+ | "NS"i %{ type_num(KNOT_RRTYPE_NS, &rdata_tail); }
+ | "CNAME"i %{ type_num(KNOT_RRTYPE_CNAME, &rdata_tail); }
+ | "SOA"i %{ type_num(KNOT_RRTYPE_SOA, &rdata_tail); }
+ | "PTR"i %{ type_num(KNOT_RRTYPE_PTR, &rdata_tail); }
+ | "HINFO"i %{ type_num(KNOT_RRTYPE_HINFO, &rdata_tail); }
+ | "MINFO"i %{ type_num(KNOT_RRTYPE_MINFO, &rdata_tail); }
+ | "MX"i %{ type_num(KNOT_RRTYPE_MX, &rdata_tail); }
+ | "TXT"i %{ type_num(KNOT_RRTYPE_TXT, &rdata_tail); }
+ | "RP"i %{ type_num(KNOT_RRTYPE_RP, &rdata_tail); }
+ | "AFSDB"i %{ type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); }
+ | "RT"i %{ type_num(KNOT_RRTYPE_RT, &rdata_tail); }
+ | "KEY"i %{ type_num(KNOT_RRTYPE_KEY, &rdata_tail); }
+ | "AAAA"i %{ type_num(KNOT_RRTYPE_AAAA, &rdata_tail); }
+ | "LOC"i %{ type_num(KNOT_RRTYPE_LOC, &rdata_tail); }
+ | "SRV"i %{ type_num(KNOT_RRTYPE_SRV, &rdata_tail); }
+ | "NAPTR"i %{ type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); }
+ | "KX"i %{ type_num(KNOT_RRTYPE_KX, &rdata_tail); }
+ | "CERT"i %{ type_num(KNOT_RRTYPE_CERT, &rdata_tail); }
+ | "DNAME"i %{ type_num(KNOT_RRTYPE_DNAME, &rdata_tail); }
+ | "APL"i %{ type_num(KNOT_RRTYPE_APL, &rdata_tail); }
+ | "DS"i %{ type_num(KNOT_RRTYPE_DS, &rdata_tail); }
+ | "SSHFP"i %{ type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); }
+ | "IPSECKEY"i %{ type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); }
+ | "RRSIG"i %{ type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); }
+ | "NSEC"i %{ type_num(KNOT_RRTYPE_NSEC, &rdata_tail); }
+ | "DNSKEY"i %{ type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); }
+ | "DHCID"i %{ type_num(KNOT_RRTYPE_DHCID, &rdata_tail); }
+ | "NSEC3"i %{ type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); }
+ | "NSEC3PARAM"i %{ type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); }
+ | "TLSA"i %{ type_num(KNOT_RRTYPE_TLSA, &rdata_tail); }
+ | "SMIMEA"i %{ type_num(KNOT_RRTYPE_SMIMEA, &rdata_tail); }
+ | "CDS"i %{ type_num(KNOT_RRTYPE_CDS, &rdata_tail); }
+ | "CDNSKEY"i %{ type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); }
+ | "OPENPGPKEY"i %{ type_num(KNOT_RRTYPE_OPENPGPKEY, &rdata_tail); }
+ | "CSYNC"i %{ type_num(KNOT_RRTYPE_CSYNC, &rdata_tail); }
+ | "ZONEMD"i %{ type_num(KNOT_RRTYPE_ZONEMD, &rdata_tail); }
+ | "SPF"i %{ type_num(KNOT_RRTYPE_SPF, &rdata_tail); }
+ | "NID"i %{ type_num(KNOT_RRTYPE_NID, &rdata_tail); }
+ | "L32"i %{ type_num(KNOT_RRTYPE_L32, &rdata_tail); }
+ | "L64"i %{ type_num(KNOT_RRTYPE_L64, &rdata_tail); }
+ | "LP"i %{ type_num(KNOT_RRTYPE_LP, &rdata_tail); }
+ | "EUI48"i %{ type_num(KNOT_RRTYPE_EUI48, &rdata_tail); }
+ | "EUI64"i %{ type_num(KNOT_RRTYPE_EUI64, &rdata_tail); }
+ | "URI"i %{ type_num(KNOT_RRTYPE_URI, &rdata_tail); }
+ | "CAA"i %{ type_num(KNOT_RRTYPE_CAA, &rdata_tail); }
+ | "SVCB"i %{ type_num(KNOT_RRTYPE_SVCB, &rdata_tail); }
+ | "HTTPS"i %{ type_num(KNOT_RRTYPE_HTTPS, &rdata_tail); }
+ | "TYPE"i . num16 # TYPE0-TYPE65535.
+ ) $!_type_error;
+ # END
+
+ # BEGIN - Bitmap processing
+ action _type_bitmap_exit {
+ if (s->number64 <= UINT16_MAX) {
+ window_add_bit(s->number64, s);
+ } else {
+ WARN(ZS_NUMBER16_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ # TYPE0-TYPE65535.
+ type_bitmap = number %_type_bitmap_exit;
+
+ type_bit =
+ ( "A"i %{ window_add_bit(KNOT_RRTYPE_A, s); }
+ | "NS"i %{ window_add_bit(KNOT_RRTYPE_NS, s); }
+ | "CNAME"i %{ window_add_bit(KNOT_RRTYPE_CNAME, s); }
+ | "SOA"i %{ window_add_bit(KNOT_RRTYPE_SOA, s); }
+ | "PTR"i %{ window_add_bit(KNOT_RRTYPE_PTR, s); }
+ | "HINFO"i %{ window_add_bit(KNOT_RRTYPE_HINFO, s); }
+ | "MINFO"i %{ window_add_bit(KNOT_RRTYPE_MINFO, s); }
+ | "MX"i %{ window_add_bit(KNOT_RRTYPE_MX, s); }
+ | "TXT"i %{ window_add_bit(KNOT_RRTYPE_TXT, s); }
+ | "RP"i %{ window_add_bit(KNOT_RRTYPE_RP, s); }
+ | "AFSDB"i %{ window_add_bit(KNOT_RRTYPE_AFSDB, s); }
+ | "RT"i %{ window_add_bit(KNOT_RRTYPE_RT, s); }
+ | "KEY"i %{ window_add_bit(KNOT_RRTYPE_KEY, s); }
+ | "AAAA"i %{ window_add_bit(KNOT_RRTYPE_AAAA, s); }
+ | "LOC"i %{ window_add_bit(KNOT_RRTYPE_LOC, s); }
+ | "SRV"i %{ window_add_bit(KNOT_RRTYPE_SRV, s); }
+ | "NAPTR"i %{ window_add_bit(KNOT_RRTYPE_NAPTR, s); }
+ | "KX"i %{ window_add_bit(KNOT_RRTYPE_KX, s); }
+ | "CERT"i %{ window_add_bit(KNOT_RRTYPE_CERT, s); }
+ | "DNAME"i %{ window_add_bit(KNOT_RRTYPE_DNAME, s); }
+ | "APL"i %{ window_add_bit(KNOT_RRTYPE_APL, s); }
+ | "DS"i %{ window_add_bit(KNOT_RRTYPE_DS, s); }
+ | "SSHFP"i %{ window_add_bit(KNOT_RRTYPE_SSHFP, s); }
+ | "IPSECKEY"i %{ window_add_bit(KNOT_RRTYPE_IPSECKEY, s); }
+ | "RRSIG"i %{ window_add_bit(KNOT_RRTYPE_RRSIG, s); }
+ | "NSEC"i %{ window_add_bit(KNOT_RRTYPE_NSEC, s); }
+ | "DNSKEY"i %{ window_add_bit(KNOT_RRTYPE_DNSKEY, s); }
+ | "DHCID"i %{ window_add_bit(KNOT_RRTYPE_DHCID, s); }
+ | "NSEC3"i %{ window_add_bit(KNOT_RRTYPE_NSEC3, s); }
+ | "NSEC3PARAM"i %{ window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); }
+ | "TLSA"i %{ window_add_bit(KNOT_RRTYPE_TLSA, s); }
+ | "SMIMEA"i %{ window_add_bit(KNOT_RRTYPE_SMIMEA, s); }
+ | "CDS"i %{ window_add_bit(KNOT_RRTYPE_CDS, s); }
+ | "CDNSKEY"i %{ window_add_bit(KNOT_RRTYPE_CDNSKEY, s); }
+ | "OPENPGPKEY"i %{ window_add_bit(KNOT_RRTYPE_OPENPGPKEY, s); }
+ | "CSYNC"i %{ window_add_bit(KNOT_RRTYPE_CSYNC, s); }
+ | "ZONEMD"i %{ window_add_bit(KNOT_RRTYPE_ZONEMD, s); }
+ | "SPF"i %{ window_add_bit(KNOT_RRTYPE_SPF, s); }
+ | "NID"i %{ window_add_bit(KNOT_RRTYPE_NID, s); }
+ | "L32"i %{ window_add_bit(KNOT_RRTYPE_L32, s); }
+ | "L64"i %{ window_add_bit(KNOT_RRTYPE_L64, s); }
+ | "LP"i %{ window_add_bit(KNOT_RRTYPE_LP, s); }
+ | "EUI48"i %{ window_add_bit(KNOT_RRTYPE_EUI48, s); }
+ | "EUI64"i %{ window_add_bit(KNOT_RRTYPE_EUI64, s); }
+ | "URI"i %{ window_add_bit(KNOT_RRTYPE_URI, s); }
+ | "CAA"i %{ window_add_bit(KNOT_RRTYPE_CAA, s); }
+ | "SVCB"i %{ window_add_bit(KNOT_RRTYPE_SVCB, s); }
+ | "HTTPS"i %{ window_add_bit(KNOT_RRTYPE_HTTPS, s); }
+ | "TYPE"i . type_bitmap # TYPE0-TYPE65535.
+ );
+
+ action _bitmap_init {
+ memset(s->windows, 0, sizeof(s->windows));
+ s->last_window = -1;
+ }
+ action _bitmap_exit {
+ for (uint16_t window = 0; window <= s->last_window; window++) {
+ if ((s->windows[window]).length > 0) {
+ if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop)
+ {
+ // Window number.
+ *rdata_tail = (uint8_t)window;
+ rdata_tail += 1;
+ // Bitmap length.
+ *rdata_tail = (s->windows[window]).length;
+ rdata_tail += 1;
+ // Copying bitmap.
+ memcpy(rdata_tail,
+ (s->windows[window]).bitmap,
+ (s->windows[window]).length);
+ rdata_tail += (s->windows[window]).length;
+ } else {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+ }
+ }
+ action _bitmap_error {
+ WARN(ZS_BAD_BITMAP);
+ fhold; fgoto err_line;
+ }
+
+ # Blank bitmap is allowed too.
+ bitmap_ := ((sep . type_bit)* . sep?) >_bitmap_init
+ %_bitmap_exit %_ret $!_bitmap_error . end_wchar;
+ bitmap = all_wchar ${ fhold; fcall bitmap_; };
+ # END
+
+ # BEGIN - Location processing
+ action _d1_exit {
+ if (s->number64 <= 90) {
+ s->loc.d1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _d2_exit {
+ if (s->number64 <= 180) {
+ s->loc.d2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _m1_exit {
+ if (s->number64 <= 59) {
+ s->loc.m1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _m2_exit {
+ if (s->number64 <= 59) {
+ s->loc.m2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _s1_exit {
+ if (s->number64 <= 59999) {
+ s->loc.s1 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _s2_exit {
+ if (s->number64 <= 59999) {
+ s->loc.s2 = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _alt_exit {
+ if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) ||
+ (s->loc.alt_sign == -1 && s->number64 <= 10000000))
+ {
+ s->loc.alt = (uint32_t)(s->number64);
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _siz_exit {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.siz = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _hp_exit {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.hp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _vp_exit {
+ if (s->number64 <= 9000000000ULL) {
+ s->loc.vp = s->number64;
+ } else {
+ WARN(ZS_BAD_NUMBER);
+ fhold; fgoto err_line;
+ }
+ }
+ action _lat_sign {
+ s->loc.lat_sign = -1;
+ }
+ action _long_sign {
+ s->loc.long_sign = -1;
+ }
+ action _alt_sign {
+ s->loc.alt_sign = -1;
+ }
+
+ d1 = number %_d1_exit;
+ d2 = number %_d2_exit;
+ m1 = number %_m1_exit;
+ m2 = number %_m2_exit;
+ s1 = float3 %_s1_exit;
+ s2 = float3 %_s2_exit;
+ siz = float2 %_siz_exit;
+ hp = float2 %_hp_exit;
+ vp = float2 %_vp_exit;
+ alt = ('-' %_alt_sign)? . float2 %_alt_exit;
+ lat_sign = 'N' | 'S' %_lat_sign;
+ long_sign = 'E' | 'W' %_long_sign;
+
+ action _loc_init {
+ memset(&(s->loc), 0, sizeof(s->loc));
+ // Defaults.
+ s->loc.siz = 100;
+ s->loc.vp = 1000;
+ s->loc.hp = 1000000;
+ s->loc.lat_sign = 1;
+ s->loc.long_sign = 1;
+ s->loc.alt_sign = 1;
+ }
+ action _loc_exit {
+ // Write version.
+ *(rdata_tail) = 0;
+ rdata_tail += 1;
+ // Write size.
+ *(rdata_tail) = loc64to8(s->loc.siz);
+ rdata_tail += 1;
+ // Write horizontal precision.
+ *(rdata_tail) = loc64to8(s->loc.hp);
+ rdata_tail += 1;
+ // Write vertical precision.
+ *(rdata_tail) = loc64to8(s->loc.vp);
+ rdata_tail += 1;
+ // Write latitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign *
+ (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1));
+ rdata_tail += 4;
+ // Write longitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign *
+ (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2));
+ rdata_tail += 4;
+ // Write altitude.
+ *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign *
+ (s->loc.alt));
+ rdata_tail += 4;
+ }
+ action _loc_error {
+ WARN(ZS_BAD_LOC_DATA);
+ fhold; fgoto err_line;
+ }
+
+ loc = (d1 . sep . (m1 . sep . (s1 . sep)?)? . lat_sign . sep .
+ d2 . sep . (m2 . sep . (s2 . sep)?)? . long_sign . sep .
+ alt 'm'? . (sep . siz 'm'? . (sep . hp 'm'? . (sep . vp 'm'?)?)?)? .
+ sep?
+ ) >_loc_init %_loc_exit $!_loc_error;
+ # END
+
+ # BEGIN - Hexadecimal rdata processing
+ action _hex_r_data_error {
+ WARN(ZS_BAD_HEX_RDATA);
+ fhold; fgoto err_line;
+ }
+
+ nonempty_hex_r_data :=
+ (sep . length_number . sep . type_data)
+ $!_hex_r_data_error %_ret . end_wchar;
+
+ hex_r_data :=
+ (sep .
+ ( ('0' %_ret . all_wchar)
+ | (length_number . sep . type_data %_ret . end_wchar)
+ )
+ ) $!_hex_r_data_error;
+ # END
+
+ # BEGIN - EUI processing
+ action _eui_init {
+ s->item_length = 0;
+ }
+ action _eui_count {
+ s->item_length++;
+ }
+ action _eui48_exit {
+ if (s->item_length != 6) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ fhold; fgoto err_line;
+ }
+ }
+ action _eui64_exit {
+ if (s->item_length != 8) {
+ WARN(ZS_BAD_EUI_LENGTH);
+ fhold; fgoto err_line;
+ }
+ }
+ action _eui_sep_error {
+ WARN(ZS_BAD_CHAR_DASH);
+ fhold; fgoto err_line;
+ }
+
+ eui48 = (hex_char %_eui_count .
+ ('-' >!_eui_sep_error . hex_char %_eui_count)+
+ ) $!_hex_char_error >_eui_init %_eui48_exit;
+
+ eui64 = (hex_char %_eui_count .
+ ('-' >!_eui_sep_error . hex_char %_eui_count)+
+ ) $!_hex_char_error >_eui_init %_eui64_exit;
+ # END
+
+ # BEGIN - ILNP processing
+ action _l64_init {
+ s->item_length = 0;
+ }
+ action _l64_count {
+ s->item_length++;
+ }
+ action _l64_exit {
+ if (s->item_length != 4) {
+ WARN(ZS_BAD_L64_LENGTH);
+ fhold; fgoto err_line;
+ }
+ }
+ action _l64_sep_error {
+ WARN(ZS_BAD_CHAR_COLON);
+ fhold; fgoto err_line;
+ }
+
+ l64_label = (hex_char . hex_char) $!_hex_char_error %_l64_count;
+ l64 = (l64_label . (':' >!_l64_sep_error . l64_label)+
+ ) $!_hex_char_error >_l64_init %_l64_exit;
+
+ l32 = ipv4_addr %_ipv4_addr_write;
+ # END
+
+ # BEGIN - SvcParams processing (SVCB/HTTPS records)
+ action _svcb_params_init {
+ s->svcb.params_position = rdata_tail;
+ s->svcb.last_key = -1;
+ }
+ action _svcb_params_exit {
+ int ret = svcb_check(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ fhold; fgoto err_line;
+ }
+ }
+ action _svcb_params_error {
+ WARN(ZS_BAD_SVCB_PARAM);
+ fhold; fgoto err_line;
+ }
+
+ action _mandat_value_error {
+ WARN(ZS_BAD_SVCB_MANDATORY);
+ fhold; fgoto err_line;
+ }
+
+ action _svcb_param_init {
+ if (rdata_tail + 4 > rdata_stop + 1) { // key_len + val_len
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ s->svcb.param_position = rdata_tail;
+ }
+ action _svcb_param_exit {
+ int ret = svcb_sort(s, rdata_tail);
+ if (ret != ZS_OK) {
+ WARN(ret);
+ fhold; fgoto err_line;
+ }
+ }
+
+ action _alpnl_init {
+ s->comma_list = true;
+ }
+ action _alpnl_exit {
+ s->comma_list = false;
+ }
+
+ action _mandatory_init {
+ s->svcb.mandatory_position = rdata_tail + 2; // Skip 2-B prefix.
+ }
+ action _mandatory_exit {
+ svcb_mandatory_sort(s->svcb.mandatory_position, rdata_tail);
+ }
+
+ action _rdata_2B_check {
+ if (rdata_tail + 2 > rdata_stop + 1) {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ }
+
+ svcb_key_generic = ("key" . num16);
+ svcb_key_mandatory = ("mandatory" %_write16_0);
+ svcb_key_alpn = ("alpn" %_write16_1);
+ svcb_key_ndalpn = ("no-default-alpn" %_write16_2);
+ svcb_key_port = ("port" %_write16_3);
+ svcb_key_ipv4hint = ("ipv4hint" %_write16_4);
+ svcb_key_ech = ("ech" %_write16_5);
+ svcb_key_ipv6hint = ("ipv6hint" %_write16_6);
+
+ mandat_value_ :=
+ (svcb_key_generic | svcb_key_alpn | svcb_key_ndalpn | svcb_key_port |
+ svcb_key_ipv4hint | svcb_key_ech | svcb_key_ipv6hint
+ ) >_rdata_2B_check $!_mandat_value_error %_ret . ([,\"] | all_wchar);
+ mandat_value = alpha ${ fhold; fcall mandat_value_; };
+
+ svcb_empty = zlen %_write16_0;
+ svcb_generic_ = (text >_item_length2_init %_item_length2_exit);
+ svcb_generic = ("=" . svcb_generic_) | svcb_empty;
+ svcb_mandat_ = ((mandat_value . ("," . mandat_value)*) >_item_length2_init %_item_length2_exit);
+ svcb_mandat = svcb_mandat_ >_mandatory_init %_mandatory_exit;
+ svcb_alpn = (text_string >_alpnl_init %_alpnl_exit >_item_length2_init %_item_length2_exit);
+ svcb_port = num16 >_write16_2 >_rdata_2B_check;
+ svcb_ipv4 = ((ipv4_addr_write . ("," . ipv4_addr_write)*) >_item_length2_init %_item_length2_exit);
+ svcb_ech = (base64_quartet+ >_item_length2_init %_item_length2_exit);
+ svcb_ipv6 = ((ipv6_addr_write . ("," . ipv6_addr_write)*) >_item_length2_init %_item_length2_exit);
+
+ svcb_param_generic = (svcb_key_generic . svcb_generic);
+ svcb_param_mandatory = (svcb_key_mandatory . "=" . (svcb_mandat | ('\"' . svcb_mandat . '\"')));
+ svcb_param_alpn = (svcb_key_alpn . "=" . (svcb_alpn | ('\"' . svcb_alpn . '\"')));
+ svcb_param_ndalpn = (svcb_key_ndalpn . svcb_empty);
+ svcb_param_port = (svcb_key_port . "=" . (svcb_port | ('\"' . svcb_port . '\"')));
+ svcb_param_ipv4hint = (svcb_key_ipv4hint . "=" . (svcb_ipv4 | ('\"' . svcb_ipv4 . '\"')));
+ svcb_param_ech = (svcb_key_ech . "=" . (svcb_ech | ('\"' . svcb_ech . '\"')));
+ svcb_param_ipv6hint = (svcb_key_ipv6hint . "=" . (svcb_ipv6 | ('\"' . svcb_ipv6 . '\"')));
+
+ svcb_param_any =
+ (svcb_param_generic | svcb_param_mandatory | svcb_param_alpn |
+ svcb_param_ndalpn | svcb_param_port | svcb_param_ipv4hint |
+ svcb_param_ech | svcb_param_ipv6hint
+ ) >_svcb_param_init %_svcb_param_exit;
+ svcb_params_ :=
+ ((sep . svcb_param_any)* . sep?) >_svcb_params_init
+ %_svcb_params_exit $!_svcb_params_error %_ret . end_wchar;
+ svcb_params = all_wchar ${ fhold; fcall svcb_params_; };
+ # END
+
+ # BEGIN - Mnemonic names processing
+ action _dns_alg_error {
+ WARN(ZS_BAD_ALGORITHM);
+ fhold; fgoto err_line;
+ }
+ action _cert_type_error {
+ WARN(ZS_BAD_CERT_TYPE);
+ fhold; fgoto err_line;
+ }
+
+ dns_alg_ :=
+ ( number %_num8_write
+ | "RSAMD5"i %_write8_1
+ | "DH"i %_write8_2
+ | "DSA"i %_write8_3
+ | "RSASHA1"i %_write8_5
+ | "DSA-NSEC3-SHA1"i %_write8_6
+ | "RSASHA1-NSEC3-SHA1"i %_write8_7
+ | "RSASHA256"i %_write8_8
+ | "RSASHA512"i %_write8_10
+ | "ECC-GOST"i %_write8_12
+ | "ECDSAP256SHA256"i %_write8_13
+ | "ECDSAP384SHA384"i %_write8_14
+ | "ED25519"i %_write8_15
+ | "ED448"i %_write8_16
+ | "INDIRECT"i %_write8_252
+ | "PRIVATEDNS"i %_write8_253
+ | "PRIVATEOID"i %_write8_254
+ ) $!_dns_alg_error %_ret . all_wchar;
+ dns_alg = alnum ${ fhold; fcall dns_alg_; };
+
+ cert_type_ :=
+ ( number %_num16_write
+ | "PKIX"i %_write16_1
+ | "SPKI"i %_write16_2
+ | "PGP"i %_write16_3
+ | "IPKIX"i %_write16_4
+ | "ISPKI"i %_write16_5
+ | "IPGP"i %_write16_6
+ | "ACPKIX"i %_write16_7
+ | "IACPKIX"i %_write16_8
+ | "URI"i %_write16_253
+ | "OID"i %_write16_254
+ ) $!_cert_type_error %_ret . all_wchar;
+ cert_type = alnum ${ fhold; fcall cert_type_; };
+ # END
+
+ # BEGIN - Rdata processing
+ action _r_data_init {
+ rdata_tail = s->r_data;
+ }
+ action _r_data_error {
+ WARN(ZS_BAD_RDATA);
+ fhold; fgoto err_line;
+ }
+
+ r_data_a :=
+ (ipv4_addr_write)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_ns :=
+ (r_dname)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_soa :=
+ (r_dname . sep . r_dname . sep . num32 . sep . time32 .
+ sep . time32 . sep . time32 . sep . time32)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_hinfo :=
+ (text_string . sep . text_string)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_minfo :=
+ (r_dname . sep . r_dname)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_mx :=
+ (num16 . sep . r_dname)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_txt :=
+ (text_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_aaaa :=
+ (ipv6_addr_write)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_loc :=
+ (loc)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_srv :=
+ (num16 . sep . num16 . sep . num16 . sep . r_dname)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_naptr :=
+ (num16 . sep . num16 . sep . text_string . sep . text_string .
+ sep . text_string . sep . r_dname)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_cert :=
+ (cert_type . sep . num16 . sep . dns_alg . sep . base64)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_apl :=
+ (apl_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_ds :=
+ (num16 . sep . dns_alg . sep . num8 . sep . hex_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_sshfp :=
+ (num8 . sep . num8 . sep . hex_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_ipseckey :=
+ (num8 . sep . gateway)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_rrsig :=
+ (type_num . sep . dns_alg . sep . num8 . sep . num32 . sep .
+ timestamp . sep . timestamp . sep . num16 . sep . r_dname .
+ sep . base64)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_nsec :=
+ (r_dname . bitmap)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_dnskey :=
+ (num16 . sep . num8 . sep . dns_alg . sep . base64)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_dhcid :=
+ (base64)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_nsec3 :=
+ (num8 . sep . num8 . sep . num16 . sep . salt . sep .
+ hash . bitmap)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_nsec3param :=
+ (num8 . sep . num8 . sep . num16 . sep . salt)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_tlsa :=
+ (num8 . sep . num8 . sep . num8 . sep . hex_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_csync :=
+ (num32 . sep . num16 . bitmap)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_zonemd :=
+ (num32 . sep . num8 . sep . num8 . sep . hex_array)
+ $!_r_data_error %_ret . end_wchar;
+
+ r_data_l32 :=
+ (num16 . sep . l32)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_l64 :=
+ (num16 . sep . l64)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_eui48 :=
+ (eui48)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_eui64 :=
+ (eui64)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_uri :=
+ (num16 . sep . num16 . sep . text)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_caa :=
+ (num8 . sep . text_string . sep . text)
+ $!_r_data_error %_ret . all_wchar;
+
+ r_data_svcb :=
+ (num16 . sep . r_dname . svcb_params)
+ $!_r_data_error %_ret . all_wchar;
+
+ action _text_r_data {
+ fhold;
+ switch (s->r_type) {
+ case KNOT_RRTYPE_A:
+ fcall r_data_a;
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ fcall r_data_ns;
+ case KNOT_RRTYPE_SOA:
+ fcall r_data_soa;
+ case KNOT_RRTYPE_HINFO:
+ fcall r_data_hinfo;
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_RP:
+ fcall r_data_minfo;
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_LP:
+ fcall r_data_mx;
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ fcall r_data_txt;
+ case KNOT_RRTYPE_AAAA:
+ fcall r_data_aaaa;
+ case KNOT_RRTYPE_LOC:
+ fcall r_data_loc;
+ case KNOT_RRTYPE_SRV:
+ fcall r_data_srv;
+ case KNOT_RRTYPE_NAPTR:
+ fcall r_data_naptr;
+ case KNOT_RRTYPE_CERT:
+ fcall r_data_cert;
+ case KNOT_RRTYPE_APL:
+ fcall r_data_apl;
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_CDS:
+ fcall r_data_ds;
+ case KNOT_RRTYPE_SSHFP:
+ fcall r_data_sshfp;
+ case KNOT_RRTYPE_IPSECKEY:
+ fcall r_data_ipseckey;
+ case KNOT_RRTYPE_RRSIG:
+ fcall r_data_rrsig;
+ case KNOT_RRTYPE_NSEC:
+ fcall r_data_nsec;
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_CDNSKEY:
+ fcall r_data_dnskey;
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ fcall r_data_dhcid;
+ case KNOT_RRTYPE_NSEC3:
+ fcall r_data_nsec3;
+ case KNOT_RRTYPE_NSEC3PARAM:
+ fcall r_data_nsec3param;
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ fcall r_data_tlsa;
+ case KNOT_RRTYPE_CSYNC:
+ fcall r_data_csync;
+ case KNOT_RRTYPE_ZONEMD:
+ fcall r_data_zonemd;
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L64:
+ fcall r_data_l64;
+ case KNOT_RRTYPE_L32:
+ fcall r_data_l32;
+ case KNOT_RRTYPE_EUI48:
+ fcall r_data_eui48;
+ case KNOT_RRTYPE_EUI64:
+ fcall r_data_eui64;
+ case KNOT_RRTYPE_URI:
+ fcall r_data_uri;
+ case KNOT_RRTYPE_CAA:
+ fcall r_data_caa;
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ fcall r_data_svcb;
+ default:
+ WARN(ZS_CANNOT_TEXT_DATA);
+ fgoto err_line;
+ }
+ }
+ action _hex_r_data {
+ switch (s->r_type) {
+ // Next types must not have empty rdata.
+ case KNOT_RRTYPE_A:
+ case KNOT_RRTYPE_NS:
+ case KNOT_RRTYPE_CNAME:
+ case KNOT_RRTYPE_PTR:
+ case KNOT_RRTYPE_DNAME:
+ case KNOT_RRTYPE_SOA:
+ case KNOT_RRTYPE_HINFO:
+ case KNOT_RRTYPE_MINFO:
+ case KNOT_RRTYPE_MX:
+ case KNOT_RRTYPE_AFSDB:
+ case KNOT_RRTYPE_RT:
+ case KNOT_RRTYPE_KX:
+ case KNOT_RRTYPE_TXT:
+ case KNOT_RRTYPE_SPF:
+ case KNOT_RRTYPE_RP:
+ case KNOT_RRTYPE_AAAA:
+ case KNOT_RRTYPE_LOC:
+ case KNOT_RRTYPE_SRV:
+ case KNOT_RRTYPE_NAPTR:
+ case KNOT_RRTYPE_CERT:
+ case KNOT_RRTYPE_DS:
+ case KNOT_RRTYPE_SSHFP:
+ case KNOT_RRTYPE_IPSECKEY:
+ case KNOT_RRTYPE_RRSIG:
+ case KNOT_RRTYPE_NSEC:
+ case KNOT_RRTYPE_KEY:
+ case KNOT_RRTYPE_DNSKEY:
+ case KNOT_RRTYPE_DHCID:
+ case KNOT_RRTYPE_NSEC3:
+ case KNOT_RRTYPE_NSEC3PARAM:
+ case KNOT_RRTYPE_TLSA:
+ case KNOT_RRTYPE_SMIMEA:
+ case KNOT_RRTYPE_CDS:
+ case KNOT_RRTYPE_CDNSKEY:
+ case KNOT_RRTYPE_OPENPGPKEY:
+ case KNOT_RRTYPE_CSYNC:
+ case KNOT_RRTYPE_ZONEMD:
+ case KNOT_RRTYPE_NID:
+ case KNOT_RRTYPE_L32:
+ case KNOT_RRTYPE_L64:
+ case KNOT_RRTYPE_LP:
+ case KNOT_RRTYPE_EUI48:
+ case KNOT_RRTYPE_EUI64:
+ case KNOT_RRTYPE_URI:
+ case KNOT_RRTYPE_CAA:
+ case KNOT_RRTYPE_SVCB:
+ case KNOT_RRTYPE_HTTPS:
+ fcall nonempty_hex_r_data;
+ // Next types can have empty rdata.
+ case KNOT_RRTYPE_APL:
+ default:
+ fcall hex_r_data;
+ }
+ }
+
+ # Avoidance of multiple fhold at the input block end.
+ action _wrap_in {
+ if (pe - p == 1) {
+ *wrap = WRAP_DETECTED;
+ }
+ }
+ action _wrap_out {
+ if (*wrap == WRAP_NONE) {
+ fhold;
+ }
+ }
+
+ # rdata can be in text or hex format with leading "\#" string.
+ r_data =
+ ( sep . ^('\\' | all_wchar) $_text_r_data
+ | sep . '\\' $_wrap_in . ^'#' $_wrap_out $_text_r_data
+ | sep . '\\' . '#' $_hex_r_data # Hex format.
+ | sep? . end_wchar $_text_r_data # Empty rdata.
+ ) >_r_data_init $!_r_data_error;
+ # END
+
+ # BEGIN - Record type processing
+ action _r_type_error {
+ WARN(ZS_UNSUPPORTED_TYPE);
+ fhold; fgoto err_line;
+ }
+
+ r_type =
+ ( "A"i %{ s->r_type = KNOT_RRTYPE_A; }
+ | "NS"i %{ s->r_type = KNOT_RRTYPE_NS; }
+ | "CNAME"i %{ s->r_type = KNOT_RRTYPE_CNAME; }
+ | "SOA"i %{ s->r_type = KNOT_RRTYPE_SOA; }
+ | "PTR"i %{ s->r_type = KNOT_RRTYPE_PTR; }
+ | "HINFO"i %{ s->r_type = KNOT_RRTYPE_HINFO; }
+ | "MINFO"i %{ s->r_type = KNOT_RRTYPE_MINFO; }
+ | "MX"i %{ s->r_type = KNOT_RRTYPE_MX; }
+ | "TXT"i %{ s->r_type = KNOT_RRTYPE_TXT; }
+ | "RP"i %{ s->r_type = KNOT_RRTYPE_RP; }
+ | "AFSDB"i %{ s->r_type = KNOT_RRTYPE_AFSDB; }
+ | "RT"i %{ s->r_type = KNOT_RRTYPE_RT; }
+ | "KEY"i %{ s->r_type = KNOT_RRTYPE_KEY; }
+ | "AAAA"i %{ s->r_type = KNOT_RRTYPE_AAAA; }
+ | "LOC"i %{ s->r_type = KNOT_RRTYPE_LOC; }
+ | "SRV"i %{ s->r_type = KNOT_RRTYPE_SRV; }
+ | "NAPTR"i %{ s->r_type = KNOT_RRTYPE_NAPTR; }
+ | "KX"i %{ s->r_type = KNOT_RRTYPE_KX; }
+ | "CERT"i %{ s->r_type = KNOT_RRTYPE_CERT; }
+ | "DNAME"i %{ s->r_type = KNOT_RRTYPE_DNAME; }
+ | "APL"i %{ s->r_type = KNOT_RRTYPE_APL; }
+ | "DS"i %{ s->r_type = KNOT_RRTYPE_DS; }
+ | "SSHFP"i %{ s->r_type = KNOT_RRTYPE_SSHFP; }
+ | "IPSECKEY"i %{ s->r_type = KNOT_RRTYPE_IPSECKEY; }
+ | "RRSIG"i %{ s->r_type = KNOT_RRTYPE_RRSIG; }
+ | "NSEC"i %{ s->r_type = KNOT_RRTYPE_NSEC; }
+ | "DNSKEY"i %{ s->r_type = KNOT_RRTYPE_DNSKEY; }
+ | "DHCID"i %{ s->r_type = KNOT_RRTYPE_DHCID; }
+ | "NSEC3"i %{ s->r_type = KNOT_RRTYPE_NSEC3; }
+ | "NSEC3PARAM"i %{ s->r_type = KNOT_RRTYPE_NSEC3PARAM; }
+ | "TLSA"i %{ s->r_type = KNOT_RRTYPE_TLSA; }
+ | "SMIMEA"i %{ s->r_type = KNOT_RRTYPE_SMIMEA; }
+ | "CDS"i %{ s->r_type = KNOT_RRTYPE_CDS; }
+ | "CDNSKEY"i %{ s->r_type = KNOT_RRTYPE_CDNSKEY; }
+ | "OPENPGPKEY"i %{ s->r_type = KNOT_RRTYPE_OPENPGPKEY; }
+ | "CSYNC"i %{ s->r_type = KNOT_RRTYPE_CSYNC; }
+ | "ZONEMD"i %{ s->r_type = KNOT_RRTYPE_ZONEMD; }
+ | "SPF"i %{ s->r_type = KNOT_RRTYPE_SPF; }
+ | "NID"i %{ s->r_type = KNOT_RRTYPE_NID; }
+ | "L32"i %{ s->r_type = KNOT_RRTYPE_L32; }
+ | "L64"i %{ s->r_type = KNOT_RRTYPE_L64; }
+ | "LP"i %{ s->r_type = KNOT_RRTYPE_LP; }
+ | "EUI48"i %{ s->r_type = KNOT_RRTYPE_EUI48; }
+ | "EUI64"i %{ s->r_type = KNOT_RRTYPE_EUI64; }
+ | "URI"i %{ s->r_type = KNOT_RRTYPE_URI; }
+ | "CAA"i %{ s->r_type = KNOT_RRTYPE_CAA; }
+ | "SVCB"i %{ s->r_type = KNOT_RRTYPE_SVCB; }
+ | "HTTPS"i %{ s->r_type = KNOT_RRTYPE_HTTPS; }
+ | "TYPE"i . type_number
+ ) $!_r_type_error;
+ # END
+
+ # BEGIN - The highest level processing
+ action _record_exit {
+ if (rdata_tail - s->r_data > UINT16_MAX) {
+ WARN(ZS_RDATA_OVERFLOW);
+ fhold; fgoto err_line;
+ }
+ s->r_data_length = rdata_tail - s->r_data;
+
+ s->state = ZS_STATE_DATA;
+
+ // Execute the record callback.
+ if (s->process.automatic) {
+ if (s->process.record != NULL) {
+ s->process.record(s);
+
+ // Stop if required from the callback.
+ if (s->state == ZS_STATE_STOP) {
+ fbreak;
+ }
+ }
+ } else {
+ // Return if external processing.
+ fhold; fbreak;
+ }
+ }
+
+ # Resource record.
+ record =
+ r_owner . sep .
+ ( (r_class . sep . ((r_ttl . sep) | (zlen %_default_r_ttl_exit )))
+ | (r_ttl . sep . ((r_class . sep) | (zlen %_default_r_class_exit)))
+ | zlen %_default_r_class_exit %_default_r_ttl_exit
+ ) $!_r_type_error .
+ r_type . r_data .
+ rest %_record_exit .
+ newline;
+
+ # Blank spaces with comments.
+ blank = rest . newline;
+
+ # Main processing loop.
+ main := (record | directive | blank)*;
+ # END
+}%%
diff --git a/src/libzscanner/version.h b/src/libzscanner/version.h
new file mode 100644
index 0000000..54762bf
--- /dev/null
+++ b/src/libzscanner/version.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define ZSCANNER_VERSION_MAJOR 3
+#define ZSCANNER_VERSION_MINOR 2
+#define ZSCANNER_VERSION_PATCH 0x06
+
+#define ZSCANNER_VERSION_HEX ((ZSCANNER_VERSION_MAJOR << 16) | \
+ (ZSCANNER_VERSION_MINOR << 8) | \
+ (ZSCANNER_VERSION_PATCH))
diff --git a/src/libzscanner/version.h.in b/src/libzscanner/version.h.in
new file mode 100644
index 0000000..72eaf31
--- /dev/null
+++ b/src/libzscanner/version.h.in
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#define ZSCANNER_VERSION_MAJOR @KNOT_VERSION_MAJOR@
+#define ZSCANNER_VERSION_MINOR @KNOT_VERSION_MINOR@
+#define ZSCANNER_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@
+
+#define ZSCANNER_VERSION_HEX ((ZSCANNER_VERSION_MAJOR << 16) | \
+ (ZSCANNER_VERSION_MINOR << 8) | \
+ (ZSCANNER_VERSION_PATCH))
diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc
new file mode 100644
index 0000000..e1fe0e4
--- /dev/null
+++ b/src/utils/Makefile.inc
@@ -0,0 +1,187 @@
+bin_PROGRAMS =
+sbin_PROGRAMS =
+
+if HAVE_LIBUTILS
+noinst_LTLIBRARIES += libknotus.la
+
+libknotus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS) \
+ $(libedit_CFLAGS) $(libidn2_CFLAGS) $(libidn_CFLAGS) \
+ $(libkqueue_CFLAGS) $(libnghttp2_CFLAGS) $(libngtcp2_CFLAGS) \
+ $(lmdb_CFLAGS)
+libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS)
+libknotus_la_LIBADD = $(libidn2_LIBS) $(libidn_LIBS) $(libnghttp2_LIBS) $(libngtcp2_LIBS)
+libknotus_LIBS = libknotus.la libknot.la libdnssec.la $(libcontrib_LIBS) \
+ $(gnutls_LIBS) $(libedit_LIBS)
+
+if EMBEDDED_LIBNGTCP2
+libknotus_la_LIBADD += $(libembngtcp2_LIBS)
+endif EMBEDDED_LIBNGTCP2
+
+libknotus_la_SOURCES = \
+ utils/common/cert.c \
+ utils/common/cert.h \
+ utils/common/exec.c \
+ utils/common/exec.h \
+ utils/common/hex.c \
+ utils/common/hex.h \
+ utils/common/https.c \
+ utils/common/https.h \
+ utils/common/lookup.c \
+ utils/common/lookup.h \
+ utils/common/msg.c \
+ utils/common/msg.h \
+ utils/common/netio.c \
+ utils/common/netio.h \
+ utils/common/params.c \
+ utils/common/params.h \
+ utils/common/quic.c \
+ utils/common/quic.h \
+ utils/common/resolv.c \
+ utils/common/resolv.h \
+ utils/common/sign.c \
+ utils/common/sign.h \
+ utils/common/tls.c \
+ utils/common/tls.h \
+ utils/common/token.c \
+ utils/common/token.h \
+ utils/common/util_conf.c \
+ utils/common/util_conf.h
+endif HAVE_LIBUTILS
+
+if HAVE_UTILS
+bin_PROGRAMS += kdig khost knsec3hash knsupdate
+
+kdig_SOURCES = \
+ utils/kdig/kdig_exec.c \
+ utils/kdig/kdig_exec.h \
+ utils/kdig/kdig_main.c \
+ utils/kdig/kdig_params.c \
+ utils/kdig/kdig_params.h
+
+khost_SOURCES = \
+ utils/kdig/kdig_exec.c \
+ utils/kdig/kdig_exec.h \
+ utils/kdig/kdig_params.c \
+ utils/kdig/kdig_params.h \
+ utils/khost/khost_main.c \
+ utils/khost/khost_params.c \
+ utils/khost/khost_params.h
+
+knsec3hash_SOURCES = \
+ utils/knsec3hash/knsec3hash.c
+
+knsupdate_SOURCES = \
+ utils/knsupdate/knsupdate_exec.c \
+ utils/knsupdate/knsupdate_exec.h \
+ utils/knsupdate/knsupdate_interactive.c \
+ utils/knsupdate/knsupdate_interactive.h \
+ utils/knsupdate/knsupdate_main.c \
+ utils/knsupdate/knsupdate_params.c \
+ utils/knsupdate/knsupdate_params.h
+
+kdig_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+kdig_LDADD = $(libknotus_LIBS)
+khost_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+khost_LDADD = $(libknotus_LIBS)
+knsec3hash_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+knsec3hash_LDADD = libknot.la libdnssec.la $(libcontrib_LIBS)
+knsupdate_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+knsupdate_LDADD = $(libknotus_LIBS) libzscanner.la
+
+if HAVE_DNSTAP
+kdig_CPPFLAGS += $(DNSTAP_CFLAGS)
+kdig_LDADD += $(libdnstap_LIBS)
+khost_CPPFLAGS += $(DNSTAP_CFLAGS)
+khost_LDADD += $(libdnstap_LIBS)
+endif HAVE_DNSTAP
+
+if ENABLE_XDP
+sbin_PROGRAMS += kxdpgun
+kxdpgun_SOURCES = \
+ utils/kxdpgun/ip_route.c \
+ utils/kxdpgun/ip_route.h \
+ utils/kxdpgun/load_queries.c \
+ utils/kxdpgun/load_queries.h \
+ utils/kxdpgun/main.c
+
+kxdpgun_CPPFLAGS = $(libknotus_la_CPPFLAGS) $(libmnl_CFLAGS)
+kxdpgun_LDADD = libknot.la $(libcontrib_LIBS) $(libmnl_LIBS) $(pthread_LIBS)
+if ENABLE_QUIC
+kxdpgun_CPPFLAGS += $(gnutls_CFLAGS)
+kxdpgun_LDADD += $(gnutls_LIBS)
+endif ENABLE_QUIC
+endif ENABLE_XDP
+endif HAVE_UTILS
+
+if HAVE_DAEMON
+# Create storage and run-time directories
+install-data-hook:
+ $(INSTALL) -d $(DESTDIR)/@config_dir@
+ $(INSTALL) -d $(DESTDIR)/@run_dir@
+ $(INSTALL) -d $(DESTDIR)/@storage_dir@
+
+sbin_PROGRAMS += knotc knotd
+
+knotc_SOURCES = \
+ utils/knotc/commands.c \
+ utils/knotc/commands.h \
+ utils/knotc/interactive.c \
+ utils/knotc/interactive.h \
+ utils/knotc/process.c \
+ utils/knotc/process.h \
+ utils/knotc/main.c
+
+knotd_SOURCES = \
+ utils/knotd/main.c
+
+knotc_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+knotc_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+knotc_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+knotd_CPPFLAGS = $(libknotus_la_CPPFLAGS) $(liburcu_CFLAGS) $(systemd_CFLAGS)
+knotd_LDADD = $(malloc_LIBS) $(libknotd_LIBS) $(cap_ng_LIBS)
+knotd_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+
+if HAVE_UTILS
+bin_PROGRAMS += kzonecheck kzonesign
+sbin_PROGRAMS += keymgr kjournalprint kcatalogprint
+
+kzonecheck_SOURCES = \
+ utils/kzonecheck/main.c \
+ utils/kzonecheck/zone_check.c \
+ utils/kzonecheck/zone_check.h
+
+kzonesign_SOURCES = \
+ utils/kzonesign/main.c
+
+keymgr_SOURCES = \
+ utils/keymgr/bind_privkey.c \
+ utils/keymgr/bind_privkey.h \
+ utils/keymgr/functions.c \
+ utils/keymgr/functions.h \
+ utils/keymgr/offline_ksk.c \
+ utils/keymgr/offline_ksk.h \
+ utils/keymgr/main.c
+
+kjournalprint_SOURCES = \
+ utils/kjournalprint/main.c
+
+kcatalogprint_SOURCES = \
+ utils/kcatalogprint/main.c
+
+kzonecheck_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+kzonecheck_LDADD = $(libknotd_LIBS)
+kzonecheck_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+kzonesign_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+kzonesign_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+kzonesign_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+keymgr_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+keymgr_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+keymgr_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+kjournalprint_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+kjournalprint_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+kjournalprint_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+kcatalogprint_CPPFLAGS = $(libknotus_la_CPPFLAGS)
+kcatalogprint_LDADD = $(libknotd_LIBS) $(libknotus_LIBS)
+kcatalogprint_LDFLAGS = $(AM_LDFLAGS) -rdynamic
+endif HAVE_UTILS
+endif HAVE_DAEMON
diff --git a/src/utils/common/cert.c b/src/utils/common/cert.c
new file mode 100644
index 0000000..1b76b23
--- /dev/null
+++ b/src/utils/common/cert.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2016 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 <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+
+#include "utils/common/cert.h"
+#include "libknot/error.h"
+
+static int spki_hash(gnutls_x509_crt_t cert, gnutls_digest_algorithm_t alg,
+ uint8_t *hash, size_t size)
+{
+ if (!cert || !hash || gnutls_hash_get_len(alg) != size) {
+ return KNOT_EINVAL;
+ }
+
+ gnutls_pubkey_t key = { 0 };
+ if (gnutls_pubkey_init(&key) != GNUTLS_E_SUCCESS) {
+ return KNOT_ENOMEM;
+ }
+
+ if (gnutls_pubkey_import_x509(key, cert, 0) != GNUTLS_E_SUCCESS) {
+ gnutls_pubkey_deinit(key);
+ return KNOT_ERROR;
+ }
+
+ gnutls_datum_t der = { 0 };
+ if (gnutls_pubkey_export2(key, GNUTLS_X509_FMT_DER, &der) != GNUTLS_E_SUCCESS) {
+ gnutls_pubkey_deinit(key);
+ return KNOT_ERROR;
+ }
+
+ int ret = gnutls_hash_fast(alg, der.data, der.size, hash);
+
+ gnutls_free(der.data);
+ gnutls_pubkey_deinit(key);
+
+ if (ret != GNUTLS_E_SUCCESS) {
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+int cert_get_pin(gnutls_x509_crt_t cert, uint8_t *pin, size_t size)
+{
+ return spki_hash(cert, GNUTLS_DIG_SHA256, pin, size);
+}
diff --git a/src/utils/common/cert.h b/src/utils/common/cert.h
new file mode 100644
index 0000000..51e3d53
--- /dev/null
+++ b/src/utils/common/cert.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2016 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/>.
+ */
+
+#pragma once
+
+#include <gnutls/x509.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#define CERT_PIN_LEN 32
+
+/*!
+ * \brief Get certificate pin value.
+ *
+ * The pin is a SHA-256 hash of the X.509 SubjectPublicKeyInfo.
+ *
+ * \param[in] crt Certificate.
+ * \param[out] pin Pin.
+ * \param[in] size Length of the pin, must be CERT_PIN_LEN.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int cert_get_pin(gnutls_x509_crt_t crt, uint8_t *pin, size_t size);
diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c
new file mode 100644
index 0000000..dfecd9a
--- /dev/null
+++ b/src/utils/common/exec.c
@@ -0,0 +1,982 @@
+/* Copyright (C) 2023 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 <arpa/inet.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "libdnssec/random.h"
+#include "utils/common/exec.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "utils/common/params.h"
+#include "libknot/libknot.h"
+#include "contrib/ctype.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/ucw/lists.h"
+#include "contrib/wire_ctx.h"
+
+static const char *JSON_INDENT = " ";
+
+static knot_lookup_t rtypes[] = {
+ { KNOT_RRTYPE_A, "has IPv4 address" },
+ { KNOT_RRTYPE_NS, "nameserver is" },
+ { KNOT_RRTYPE_CNAME, "is an alias for" },
+ { KNOT_RRTYPE_SOA, "start of authority is" },
+ { KNOT_RRTYPE_PTR, "points to" },
+ { KNOT_RRTYPE_MX, "mail is handled by" },
+ { KNOT_RRTYPE_TXT, "description is" },
+ { KNOT_RRTYPE_AAAA, "has IPv6 address" },
+ { KNOT_RRTYPE_LOC, "location is" },
+ { KNOT_RRTYPE_DS, "delegation signature is" },
+ { KNOT_RRTYPE_SSHFP, "SSH fingerprint is" },
+ { KNOT_RRTYPE_RRSIG, "RR set signature is" },
+ { KNOT_RRTYPE_DNSKEY, "DNSSEC key is" },
+ { KNOT_RRTYPE_TLSA, "has TLS certificate" },
+ { 0, NULL }
+};
+
+static void print_header(const knot_pkt_t *packet, const style_t *style)
+{
+ char flags[64] = "";
+ char unknown_rcode[64] = "";
+ char unknown_opcode[64] = "";
+
+ const char *rcode_str = NULL;
+ const char *opcode_str = NULL;
+
+ // Get extended RCODE.
+ const char *code_name = knot_pkt_ext_rcode_name(packet);
+ if (code_name[0] != '\0') {
+ rcode_str = code_name;
+ } else {
+ uint16_t code = knot_pkt_ext_rcode(packet);
+ (void)snprintf(unknown_rcode, sizeof(unknown_rcode), "RCODE %d", code);
+ rcode_str = unknown_rcode;
+ }
+
+ // Get OPCODE.
+ uint8_t code = knot_wire_get_opcode(packet->wire);
+ const knot_lookup_t *opcode = knot_lookup_by_id(knot_opcode_names, code);
+ if (opcode != NULL) {
+ opcode_str = opcode->name;
+ } else {
+ (void)snprintf(unknown_opcode, sizeof(unknown_opcode), "OPCODE %d", code);
+ opcode_str = unknown_opcode;
+ }
+
+ // Get flags.
+ size_t flags_rest = sizeof(flags);
+ const size_t flag_len = 4;
+ if (knot_wire_get_qr(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " qr", flags_rest);
+ }
+ if (knot_wire_get_aa(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " aa", flags_rest);
+ }
+ if (knot_wire_get_tc(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " tc", flags_rest);
+ }
+ if (knot_wire_get_rd(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " rd", flags_rest);
+ }
+ if (knot_wire_get_ra(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " ra", flags_rest);
+ }
+ if (knot_wire_get_z(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " z", flags_rest);
+ }
+ if (knot_wire_get_ad(packet->wire) != 0 && flags_rest > flag_len) {
+ flags_rest -= strlcat(flags, " ad", flags_rest);
+ }
+ if (knot_wire_get_cd(packet->wire) != 0 && flags_rest > flag_len) {
+ strlcat(flags, " cd", flags_rest);
+ }
+
+ uint16_t id = knot_wire_get_id(packet->wire);
+ uint16_t qdcount = knot_wire_get_qdcount(packet->wire);
+ uint16_t ancount = knot_wire_get_ancount(packet->wire);
+ uint16_t nscount = knot_wire_get_nscount(packet->wire);
+ uint16_t arcount = knot_wire_get_arcount(packet->wire);
+
+ if (knot_pkt_has_tsig(packet)) {
+ arcount++;
+ }
+
+ // Print formatted info.
+ switch (style->format) {
+ case FORMAT_NSUPDATE:
+ printf(";; ->>HEADER<<- opcode: %s; status: %s; id: %u\n"
+ ";; Flags:%1s; "
+ "ZONE: %u; PREREQ: %u; UPDATE: %u; ADDITIONAL: %u\n",
+ opcode_str, rcode_str, id, flags, qdcount, ancount,
+ nscount, arcount);
+ break;
+ default:
+ printf(";; ->>HEADER<<- opcode: %s; status: %s; id: %u\n"
+ ";; Flags:%1s; "
+ "QUERY: %u; ANSWER: %u; AUTHORITY: %u; ADDITIONAL: %u\n",
+ opcode_str, rcode_str, id, flags, qdcount, ancount,
+ nscount, arcount);
+ break;
+ }
+}
+
+static void print_footer(const size_t total_len,
+ const size_t msg_count,
+ const size_t rr_count,
+ const net_t *net,
+ const float elapsed,
+ time_t exec_time,
+ const bool incoming)
+{
+ struct tm tm;
+ char date[64];
+
+ // Get current timestamp.
+ if (exec_time == 0) {
+ exec_time = time(NULL);
+ }
+
+ // Create formatted date-time string.
+ localtime_r(&exec_time, &tm);
+ strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm);
+
+ // Print messages statistics.
+ if (incoming) {
+ printf(";; Received %zu B", total_len);
+ } else {
+ printf(";; Sent %zu B", total_len);
+ }
+
+ // If multimessage (XFR) print additional statistics.
+ if (msg_count > 0) {
+ printf(" (%zu messages, %zu records)\n", msg_count, rr_count);
+ } else {
+ printf("\n");
+ }
+ // Print date.
+ printf(";; Time %s\n", date);
+
+ // Print connection statistics.
+ if (net != NULL) {
+ if (incoming) {
+ printf(";; From %s", net->remote_str);
+ } else {
+ printf(";; To %s", net->remote_str);
+ }
+
+ if (elapsed >= 0) {
+ printf(" in %.1f ms\n", elapsed);
+ } else {
+ printf("\n");
+ }
+ }
+}
+
+static void print_hex(const uint8_t *data, uint16_t len)
+{
+ for (int i = 0; i < len; i++) {
+ printf("%02X", data[i]);
+ }
+}
+
+static void print_nsid(const uint8_t *data, uint16_t len)
+{
+ if (len == 0) {
+ return;
+ }
+
+ print_hex(data, len);
+
+ // Check if printable string.
+ for (int i = 0; i < len; i++) {
+ if (!is_print(data[i])) {
+ return;
+ }
+ }
+ printf(" \"%.*s\"", len, data);
+}
+
+static bool print_text(const uint8_t *data, uint16_t len)
+{
+ if (len == 0) {
+ return false;
+ }
+
+ // Check if printable string.
+ for (int i = 0; i < len; i++) {
+ if (!is_print(data[i])) {
+ return false;
+ }
+ }
+ printf("%.*s", len, data);
+ return true;
+}
+
+static void print_edns_client_subnet(const uint8_t *data, uint16_t len)
+{
+ knot_edns_client_subnet_t ecs = { 0 };
+ int ret = knot_edns_client_subnet_parse(&ecs, data, len);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ struct sockaddr_storage addr = { 0 };
+ ret = knot_edns_client_subnet_get_addr(&addr, &ecs);
+ assert(ret == KNOT_EOK);
+
+ char addr_str[SOCKADDR_STRLEN] = { 0 };
+ sockaddr_tostr(addr_str, sizeof(addr_str), &addr);
+
+ printf("%s/%u/%u", addr_str, ecs.source_len, ecs.scope_len);
+}
+
+static void print_ede(const uint8_t *data, uint16_t len)
+{
+ if (len < 2) {
+ printf("(malformed)");
+ return;
+ }
+
+
+ uint16_t errcode;
+ memcpy(&errcode, data, sizeof(errcode));
+ errcode = be16toh(errcode);
+
+ const knot_lookup_t *item = knot_lookup_by_id(knot_edns_ede_names, errcode);
+ const char *strerr = (item != NULL) ? item->name : "Unknown code";
+
+ if (len > 2) {
+ printf("%hu (%s): '%.*s'", errcode, strerr, (int)(len - 2), data + 2);
+ } else {
+ printf("%hu (%s)", errcode, strerr);
+ }
+}
+
+static void print_expire(const uint8_t *data, uint16_t len)
+{
+ if (len == 0) {
+ printf("(empty)");
+ } else if (len != sizeof(uint32_t)) {
+ printf("(malformed)");
+ } else {
+ char str[80] = "";
+ uint32_t timer = knot_wire_read_u32(data);
+ if (knot_time_print_human(timer, str, sizeof(str), false) > 0) {
+ printf("%u (%s)", timer, str);
+ } else {
+ printf("%u", timer);
+ }
+ }
+}
+
+static void print_section_opt(const knot_pkt_t *packet, const style_t *style)
+{
+ char unknown_ercode[64] = "";
+ const char *ercode_str = NULL;
+
+ uint16_t ercode = knot_edns_get_ext_rcode(packet->opt_rr);
+ if (ercode > 0) {
+ ercode = knot_edns_whole_rcode(ercode,
+ knot_wire_get_rcode(packet->wire));
+ }
+
+ const knot_lookup_t *item = knot_lookup_by_id(knot_rcode_names, ercode);
+ if (item != NULL) {
+ ercode_str = item->name;
+ } else {
+ (void)snprintf(unknown_ercode, sizeof(unknown_ercode), "RCODE %d", ercode);
+ ercode_str = unknown_ercode;
+ }
+
+ printf("Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n",
+ knot_edns_get_version(packet->opt_rr),
+ (knot_edns_do(packet->opt_rr) != 0) ? "do" : "",
+ knot_edns_get_payload(packet->opt_rr),
+ ercode_str);
+
+ assert(packet->opt_rr->rrs.count > 0);
+ knot_rdata_t *rdata = packet->opt_rr->rrs.rdata;
+ wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len);
+
+ while (wire_ctx_available(&wire) >= KNOT_EDNS_OPTION_HDRLEN) {
+ uint16_t opt_code = wire_ctx_read_u16(&wire);
+ uint16_t opt_len = wire_ctx_read_u16(&wire);
+ uint8_t *opt_data = wire.position;
+
+ if (wire.error != KNOT_EOK) {
+ WARN("invalid OPT record data");
+ return;
+ }
+
+ switch (opt_code) {
+ case KNOT_EDNS_OPTION_NSID:
+ printf(";; NSID: ");
+ print_nsid(opt_data, opt_len);
+ break;
+ case KNOT_EDNS_OPTION_CLIENT_SUBNET:
+ printf(";; CLIENT-SUBNET: ");
+ print_edns_client_subnet(opt_data, opt_len);
+ break;
+ case KNOT_EDNS_OPTION_PADDING:
+ printf(";; PADDING: %u B", opt_len);
+ break;
+ case KNOT_EDNS_OPTION_COOKIE:
+ printf(";; COOKIE: ");
+ print_hex(opt_data, opt_len);
+ break;
+ case KNOT_EDNS_OPTION_EDE:
+ printf(";; EDE: ");
+ print_ede(opt_data, opt_len);
+ break;
+ case KNOT_EDNS_OPTION_EXPIRE:
+ printf(";; EXPIRE: ");
+ print_expire(opt_data, opt_len);
+ break;
+ default:
+ printf(";; Option (%u): ", opt_code);
+ if (style->show_edns_opt_text) {
+ if (!print_text(opt_data, opt_len)) {
+ print_hex(opt_data, opt_len);
+ }
+ } else {
+ print_hex(opt_data, opt_len);
+ }
+ }
+ printf("\n");
+
+ wire_ctx_skip(&wire, opt_len);
+ }
+
+ if (wire_ctx_available(&wire) > 0) {
+ WARN("invalid OPT record data");
+ }
+}
+
+static void print_section_question(const knot_dname_t *owner,
+ const uint16_t qclass,
+ const uint16_t qtype,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ // Don't print zero TTL.
+ knot_dump_style_t qstyle = style->style;
+ qstyle.empty_ttl = true;
+
+ knot_rrset_t *question = knot_rrset_new(owner, qtype, qclass, 0, NULL);
+
+ if (knot_rrset_txt_dump_header(question, 0, buf, buflen, &qstyle) < 0) {
+ WARN("can't print whole question section");
+ }
+
+ printf("%s\n", buf);
+
+ knot_rrset_free(question, NULL);
+ free(buf);
+}
+
+static void print_section_full(const knot_rrset_t *rrsets,
+ const uint16_t count,
+ const style_t *style,
+ const bool no_tsig)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ // Ignore OPT records.
+ if (rrsets[i].type == KNOT_RRTYPE_OPT) {
+ continue;
+ }
+
+ // Exclude TSIG record.
+ if (no_tsig && rrsets[i].type == KNOT_RRTYPE_TSIG) {
+ continue;
+ }
+
+ if (knot_rrset_txt_dump(&rrsets[i], &buf, &buflen,
+ &(style->style)) < 0) {
+ WARN("can't print whole section");
+ break;
+ }
+ printf("%s", buf);
+ }
+
+ free(buf);
+}
+
+static void print_section_dig(const knot_rrset_t *rrsets,
+ const uint16_t count,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ const knot_rrset_t *rrset = &rrsets[i];
+ uint16_t rrset_rdata_count = rrset->rrs.count;
+ for (uint16_t j = 0; j < rrset_rdata_count; j++) {
+ while (knot_rrset_txt_dump_data(rrset, j, buf, buflen,
+ &(style->style)) < 0) {
+ buflen += 4096;
+ // Oversize protection.
+ if (buflen > 100000) {
+ WARN("can't print whole section");
+ break;
+ }
+
+ char *newbuf = realloc(buf, buflen);
+ if (newbuf == NULL) {
+ WARN("can't print whole section");
+ break;
+ }
+ buf = newbuf;
+ }
+ printf("%s\n", buf);
+ }
+ }
+
+ free(buf);
+}
+
+static void print_section_host(const knot_rrset_t *rrsets,
+ const uint16_t count,
+ const style_t *style)
+{
+ size_t buflen = 8192;
+ char *buf = calloc(buflen, 1);
+
+ for (size_t i = 0; i < count; i++) {
+ const knot_rrset_t *rrset = &rrsets[i];
+ const knot_lookup_t *descr;
+ char type[32] = "NULL";
+ char *owner;
+
+ owner = knot_dname_to_str_alloc(rrset->owner);
+ if (style->style.ascii_to_idn != NULL) {
+ style->style.ascii_to_idn(&owner);
+ }
+ descr = knot_lookup_by_id(rtypes, rrset->type);
+
+ uint16_t rrset_rdata_count = rrset->rrs.count;
+ for (uint16_t j = 0; j < rrset_rdata_count; j++) {
+ if (rrset->type == KNOT_RRTYPE_CNAME &&
+ style->hide_cname) {
+ continue;
+ }
+
+ while (knot_rrset_txt_dump_data(rrset, j, buf, buflen,
+ &(style->style)) < 0) {
+ buflen += 4096;
+ // Oversize protection.
+ if (buflen > 100000) {
+ WARN("can't print whole section");
+ break;
+ }
+
+ char *newbuf = realloc(buf, buflen);
+ if (newbuf == NULL) {
+ WARN("can't print whole section");
+ break;
+ }
+ buf = newbuf;
+ }
+
+ if (descr != NULL) {
+ printf("%s %s %s\n", owner, descr->name, buf);
+ } else {
+ knot_rrtype_to_string(rrset->type, type, sizeof(type));
+ printf("%s has %s record %s\n", owner, type, buf);
+ }
+ }
+
+ free(owner);
+ }
+
+ free(buf);
+}
+
+static void print_error_host(const knot_pkt_t *packet, const style_t *style)
+{
+ char type[32] = "Unknown";
+ const char *rcode_str = "Unknown";
+
+ knot_rrtype_to_string(knot_pkt_qtype(packet), type, sizeof(type));
+
+ // Get extended RCODE.
+ const char *code_name = knot_pkt_ext_rcode_name(packet);
+ if (code_name[0] != '\0') {
+ rcode_str = code_name;
+ }
+
+ // Get record owner.
+ char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
+ if (style->style.ascii_to_idn != NULL) {
+ style->style.ascii_to_idn(&owner);
+ }
+
+ if (knot_pkt_ext_rcode(packet) == KNOT_RCODE_NOERROR) {
+ printf("Host %s has no %s record\n", owner, type);
+ } else {
+ printf("Host %s type %s error: %s\n", owner, type, rcode_str);
+ }
+
+ free(owner);
+}
+
+static void json_dname(jsonw_t *w, const char *key, const knot_dname_t *dname)
+{
+ knot_dname_txt_storage_t name;
+ if (knot_dname_to_str(name, dname, sizeof(name)) != NULL) {
+ jsonw_str(w, key, name);
+ }
+}
+
+static void json_rdata(jsonw_t *w, const knot_rrset_t *rrset)
+{
+ char type[16];
+ if (knot_rrtype_to_string(rrset->type, type, sizeof(type)) <= 0 ||
+ strncmp(type, "TYPE", 4) == 0) { // Unknown/hex format.
+ return;
+ }
+
+ char key[32] = "rdata";
+ strlcat(key, type, sizeof(key));
+
+ char data[16384];
+ const knot_dump_style_t *style = &KNOT_DUMP_STYLE_DEFAULT;
+ if (knot_rrset_txt_dump_data(rrset, 0, data, sizeof(data), style) > 0) {
+ jsonw_str(w, key, data);
+ }
+}
+
+static void json_print_section(jsonw_t *w, const char *name,
+ const knot_pktsection_t *section)
+{
+ if (section->count == 0) {
+ return;
+ }
+
+ char str[16];
+
+ jsonw_list(w, name);
+
+ for (int i = 0; i < section->count; i++) {
+ const knot_rrset_t *rr = knot_pkt_rr(section, i);
+ jsonw_object(w, NULL);
+ json_dname(w, "NAME", rr->owner);
+ jsonw_int(w, "TYPE", rr->type);
+ if (knot_rrtype_to_string(rr->type, str, sizeof(str)) > 0) {
+ jsonw_str(w, "TYPEname", str);
+ }
+ jsonw_int(w, "CLASS", rr->rclass);
+ if (rr->type != KNOT_RRTYPE_OPT && // OPT class meaning is different.
+ knot_rrclass_to_string(rr->rclass, str, sizeof(str)) > 0) {
+ jsonw_str(w, "CLASSname", str);
+ }
+ jsonw_int(w, "TTL", rr->ttl);
+ if (rr->type != KNOT_RRTYPE_OPT) { // OPT with HEX rdata.
+ json_rdata(w, rr);
+ }
+ jsonw_int(w, "RDLENGTH", rr->rrs.rdata->len);
+ if (rr->rrs.rdata->len > 0 ) {
+ jsonw_hex(w, "RDATAHEX", rr->rrs.rdata->data, rr->rrs.rdata->len);
+ }
+ jsonw_end(w);
+ }
+
+ jsonw_end(w);
+}
+
+static void print_packet_json(jsonw_t *w, const knot_pkt_t *pkt, time_t time)
+{
+ if (pkt == NULL) {
+ return;
+ }
+
+ char str[16];
+
+ struct tm tm;
+ char date[64];
+ localtime_r(&time, &tm);
+ strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S%z", &tm);
+ jsonw_str(w, "dateString", date);
+ jsonw_ulong(w, "dateSeconds", time);
+
+ jsonw_int(w, "msgLength", pkt->size);
+
+ if (pkt->parsed >= KNOT_WIRE_HEADER_SIZE) {
+ jsonw_int(w, "ID", knot_wire_get_id(pkt->wire));
+ jsonw_int(w, "QR", (bool)knot_wire_get_qr(pkt->wire));
+ jsonw_int(w, "Opcode", knot_wire_get_opcode(pkt->wire));
+ jsonw_int(w, "AA", (bool)knot_wire_get_aa(pkt->wire));
+ jsonw_int(w, "TC", (bool)knot_wire_get_tc(pkt->wire));
+ jsonw_int(w, "RD", (bool)knot_wire_get_rd(pkt->wire));
+ jsonw_int(w, "RA", (bool)knot_wire_get_ra(pkt->wire));
+ jsonw_int(w, "AD", (bool)knot_wire_get_ad(pkt->wire));
+ jsonw_int(w, "CD", (bool)knot_wire_get_cd(pkt->wire));
+ jsonw_int(w, "RCODE", knot_wire_get_rcode(pkt->wire));
+ jsonw_int(w, "QDCOUNT", knot_wire_get_qdcount(pkt->wire));
+ jsonw_int(w, "ANCOUNT", knot_wire_get_ancount(pkt->wire));
+ jsonw_int(w, "NSCOUNT", knot_wire_get_nscount(pkt->wire));
+ jsonw_int(w, "ARCOUNT", knot_wire_get_arcount(pkt->wire));
+ }
+ if (knot_wire_get_qdcount(pkt->wire) == 1) {
+ json_dname(w, "QNAME", knot_pkt_qname(pkt));
+ jsonw_int(w, "QTYPE", knot_pkt_qtype(pkt));
+ if (knot_rrtype_to_string(knot_pkt_qtype(pkt), str, sizeof(str)) > 0) {
+ jsonw_str(w, "QTYPEname", str);
+ }
+ jsonw_int(w, "QCLASS", knot_pkt_qclass(pkt));
+ if (knot_rrclass_to_string(knot_pkt_qclass(pkt), str, sizeof(str)) > 0) {
+ jsonw_str(w, "QCLASSname", str);
+ }
+ }
+ if (pkt->rrset_count) {
+ json_print_section(w, "answerRRs", knot_pkt_section(pkt, KNOT_ANSWER));
+ json_print_section(w, "authorityRRs", knot_pkt_section(pkt, KNOT_AUTHORITY));
+ json_print_section(w, "additionalRRs", knot_pkt_section(pkt, KNOT_ADDITIONAL));
+ }
+ if (pkt->parsed < pkt->size) {
+ jsonw_hex(w, "messageOctetsHEX", pkt->wire, pkt->size);
+ }
+}
+
+knot_pkt_t *create_empty_packet(const uint16_t max_size)
+{
+ // Create packet skeleton.
+ knot_pkt_t *packet = knot_pkt_new(NULL, max_size, NULL);
+ if (packet == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Set random sequence id.
+ knot_wire_set_id(packet->wire, dnssec_random_uint16_t());
+
+ return packet;
+}
+
+jsonw_t *print_header_xfr_json(const knot_pkt_t *query,
+ const time_t exec_time,
+ const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ jsonw_t *w = jsonw_new(stdout, JSON_INDENT);
+ if (w == NULL) {
+ return NULL;
+ }
+
+ if (style->show_query) {
+ jsonw_object(w, NULL);
+ jsonw_object(w, "queryMessage");
+ print_packet_json(w, query, exec_time);
+ jsonw_end(w);
+ jsonw_list(w, "responseMessage");
+ } else {
+ jsonw_list(w, NULL);
+ }
+
+ return w;
+}
+
+void print_data_xfr_json(jsonw_t *w,
+ const knot_pkt_t *reply,
+ const time_t exec_time)
+{
+ if (w == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ jsonw_object(w, NULL);
+ print_packet_json(w, reply, exec_time);
+ jsonw_end(w);
+}
+
+void print_footer_xfr_json(jsonw_t **w,
+ const style_t *style)
+{
+ if (w == NULL || style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ jsonw_end(*w); // list (responseMessage)
+ if (style->show_query) {
+ jsonw_end(*w); // object
+ }
+
+ jsonw_free(w);
+ *w = NULL;
+}
+
+void print_header_xfr(const knot_pkt_t *packet, const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ char xfr[16] = "AXFR";
+
+ switch (knot_pkt_qtype(packet)) {
+ case KNOT_RRTYPE_AXFR:
+ break;
+ case KNOT_RRTYPE_IXFR:
+ xfr[0] = 'I';
+ break;
+ default:
+ return;
+ }
+
+ if (style->show_header) {
+ char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet));
+ if (style->style.ascii_to_idn != NULL) {
+ style->style.ascii_to_idn(&owner);
+ }
+ if (owner != NULL) {
+ printf(";; %s for %s\n", xfr, owner);
+ free(owner);
+ }
+ }
+}
+
+void print_data_xfr(const knot_pkt_t *packet,
+ const style_t *style)
+{
+ if (packet == NULL || style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ const knot_pktsection_t *answers = knot_pkt_section(packet, KNOT_ANSWER);
+
+ switch (style->format) {
+ case FORMAT_DIG:
+ print_section_dig(knot_pkt_rr(answers, 0), answers->count, style);
+ break;
+ case FORMAT_HOST:
+ print_section_host(knot_pkt_rr(answers, 0), answers->count, style);
+ break;
+ case FORMAT_FULL:
+ print_section_full(knot_pkt_rr(answers, 0), answers->count, style, true);
+
+ // Print TSIG record.
+ if (style->show_tsig && knot_pkt_has_tsig(packet)) {
+ print_section_full(packet->tsig_rr, 1, style, false);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void print_footer_xfr(const size_t total_len,
+ const size_t msg_count,
+ const size_t rr_count,
+ const net_t *net,
+ const float elapsed,
+ const time_t exec_time,
+ const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ if (style->show_footer) {
+ print_footer(total_len, msg_count, rr_count, net, elapsed,
+ exec_time, true);
+ }
+}
+
+void print_packets_json(const knot_pkt_t *query,
+ const knot_pkt_t *reply,
+ const net_t *net,
+ const time_t exec_time,
+ const style_t *style)
+{
+ if (style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ jsonw_t *w = jsonw_new(stdout, JSON_INDENT);
+ if (w == NULL) {
+ return;
+ }
+ jsonw_object(w, NULL);
+
+ if (style->show_query) {
+ jsonw_object(w, "queryMessage");
+ print_packet_json(w, query, exec_time);
+ jsonw_end(w);
+ jsonw_object(w, "responseMessage");
+ }
+
+ print_packet_json(w, reply, exec_time);
+
+ if (style->show_query) {
+ jsonw_end(w);
+ }
+
+ jsonw_end(w);
+ jsonw_free(&w);
+}
+
+void print_packet(const knot_pkt_t *packet,
+ const net_t *net,
+ const size_t size,
+ const float elapsed,
+ const time_t exec_time,
+ const bool incoming,
+ const style_t *style)
+{
+ if (packet == NULL || style == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ const knot_pktsection_t *answers = knot_pkt_section(packet,
+ KNOT_ANSWER);
+ const knot_pktsection_t *authority = knot_pkt_section(packet,
+ KNOT_AUTHORITY);
+ const knot_pktsection_t *additional = knot_pkt_section(packet,
+ KNOT_ADDITIONAL);
+
+ uint16_t qdcount = knot_wire_get_qdcount(packet->wire);
+ uint16_t ancount = knot_wire_get_ancount(packet->wire);
+ uint16_t nscount = knot_wire_get_nscount(packet->wire);
+ uint16_t arcount = knot_wire_get_arcount(packet->wire);
+
+ // Disable additionals printing if there are no other records.
+ // OPT record may be placed anywhere within additionals!
+ if (knot_pkt_has_edns(packet) && arcount == 1) {
+ arcount = 0;
+ }
+
+ // Print packet information header.
+ if (style->show_header) {
+ if (net != NULL) {
+#ifdef ENABLE_QUIC
+ if (net->quic.params.enable) {
+ print_quic(&net->quic);
+ } else
+#endif
+ {
+ print_tls(&net->tls);
+#ifdef LIBNGHTTP2
+ print_https(&net->https);
+#endif
+ }
+ }
+ print_header(packet, style);
+ }
+
+ // Print EDNS section.
+ if (style->show_edns && knot_pkt_has_edns(packet)) {
+ printf("%s", style->show_section ? "\n;; EDNS PSEUDOSECTION:\n;; " : ";;");
+ print_section_opt(packet, style);
+ }
+
+ // Print DNS sections.
+ switch (style->format) {
+ case FORMAT_DIG:
+ if (ancount > 0) {
+ print_section_dig(knot_pkt_rr(answers, 0), ancount, style);
+ }
+ break;
+ case FORMAT_HOST:
+ if (ancount > 0) {
+ print_section_host(knot_pkt_rr(answers, 0), ancount, style);
+ } else {
+ print_error_host(packet, style);
+ }
+ break;
+ case FORMAT_NSUPDATE:
+ if (style->show_question && qdcount > 0) {
+ printf("%s", style->show_section ? "\n;; ZONE SECTION:\n;; " : ";;");
+ print_section_question(knot_pkt_qname(packet),
+ knot_pkt_qclass(packet),
+ knot_pkt_qtype(packet),
+ style);
+ }
+
+ if (style->show_answer && ancount > 0) {
+ printf("%s", style->show_section ? "\n;; PREREQUISITE SECTION:\n" : "");
+ print_section_full(knot_pkt_rr(answers, 0), ancount, style, true);
+ }
+
+ if (style->show_authority && nscount > 0) {
+ printf("%s", style->show_section ? "\n;; UPDATE SECTION:\n" : "");
+ print_section_full(knot_pkt_rr(authority, 0), nscount, style, true);
+ }
+
+ if (style->show_additional && arcount > 0) {
+ printf("%s", style->show_section ? "\n;; ADDITIONAL DATA:\n" : "");
+ print_section_full(knot_pkt_rr(additional, 0), arcount, style, true);
+ }
+ break;
+ case FORMAT_FULL:
+ if (style->show_question && qdcount > 0) {
+ printf("%s", style->show_section ? "\n;; QUESTION SECTION:\n;; " : ";;");
+ print_section_question(knot_pkt_wire_qname(packet),
+ knot_pkt_qclass(packet),
+ knot_pkt_qtype(packet),
+ style);
+ }
+
+ if (style->show_answer && ancount > 0) {
+ printf("%s", style->show_section ? "\n;; ANSWER SECTION:\n" : "");
+ print_section_full(knot_pkt_rr(answers, 0), ancount, style, true);
+ }
+
+ if (style->show_authority && nscount > 0) {
+ printf("%s", style->show_section ? "\n;; AUTHORITY SECTION:\n" : "");
+ print_section_full(knot_pkt_rr(authority, 0), nscount, style, true);
+ }
+
+ if (style->show_additional && arcount > 0) {
+ printf("%s", style->show_section ? "\n;; ADDITIONAL SECTION:\n" : "");
+ print_section_full(knot_pkt_rr(additional, 0), arcount, style, true);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Print TSIG section.
+ if (style->show_tsig && knot_pkt_has_tsig(packet)) {
+ printf("%s", style->show_section ? "\n;; TSIG PSEUDOSECTION:\n" : "");
+ print_section_full(packet->tsig_rr, 1, style, false);
+ }
+
+ // Print packet statistics.
+ if (style->show_footer) {
+ printf("\n");
+ print_footer(size, 0, 0, net, elapsed, exec_time, incoming);
+ }
+}
diff --git a/src/utils/common/exec.h b/src/utils/common/exec.h
new file mode 100644
index 0000000..359926c
--- /dev/null
+++ b/src/utils/common/exec.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <time.h>
+
+#include "utils/common/netio.h"
+#include "utils/common/params.h"
+#include "libknot/libknot.h"
+#include "contrib/json.h"
+
+/*!
+ * \brief Allocates empty packet and sets packet size and random id.
+ *
+ * \param max_size Maximal packet size.
+ *
+ * \retval packet if success.
+ * \retval NULL if error.
+ */
+knot_pkt_t *create_empty_packet(const uint16_t max_size);
+
+/*!
+ * \brief Prints information header for transfer.
+ *
+ * \param packet Parsed packet.
+ * \param style Style of the output.
+ */
+void print_header_xfr(const knot_pkt_t *packet, const style_t *style);
+
+/*!
+ * \brief Prints answer section for 1 transfer message.
+ *
+ * \param packet Response packet.
+ * \param style Style of the output.
+ */
+void print_data_xfr(const knot_pkt_t *packet, const style_t *style);
+
+/*!
+ * \brief Prints trailing statistics for transfer.
+ *
+ * \param total_len Total reply size (all messages).
+ * \param msg_count Number of messages.
+ * \param rr_count Total number of answer records.
+ * \param net Connection information.
+ * \param elapsed Total elapsed time.
+ * \param exec_time Time of the packet creation.
+ * \param style Style of the output.
+ */
+void print_footer_xfr(const size_t total_len,
+ const size_t msg_count,
+ const size_t rr_count,
+ const net_t *net,
+ const float elapsed,
+ const time_t exec_time,
+ const style_t *style);
+
+/*!
+ * \brief Prints initial JSON part of XFR output.
+ *
+ * \param query Query packet.
+ * \param exec_time Time of the packet creation.
+ * \param style Style of the output.
+ *
+ * \retval JSON witter if success.
+ * \retval NULL if error.
+ */
+jsonw_t *print_header_xfr_json(const knot_pkt_t *query,
+ const time_t exec_time,
+ const style_t *style);
+
+/*!
+ * \brief Prints one XFR reply packet in JSON.
+ *
+ * \param w JSON writter.
+ * \param reply Reply packet (possibly one of many).
+ * \param exec_time Time of the packet creation.
+ */
+void print_data_xfr_json(jsonw_t *w,
+ const knot_pkt_t *reply,
+ const time_t exec_time);
+
+/*!
+ * \brief Prints trailing JSON part of XFR output.
+ *
+ * \param w JSON writter.
+ * \param style Style of the output.
+ */
+void print_footer_xfr_json(jsonw_t **w,
+ const style_t *style);
+
+/*!
+ * \brief Prints one or query/reply pair of DNS packets in JSON format.
+ *
+ * \param query Query DNS packet.
+ * \param reply Reply DNS packet.
+ * \param net Connection information.
+ * \param exec_time Time of the packet creation.
+ * \param style Style of the output.
+ */
+void print_packets_json(const knot_pkt_t *query,
+ const knot_pkt_t *reply,
+ const net_t *net,
+ const time_t exec_time,
+ const style_t *style);
+
+/*!
+ * \brief Prints one DNS packet.
+ *
+ * \param packet DNS packet.
+ * \param net Connection information.
+ * \param size Original packet wire size.
+ * \param elapsed Total elapsed time.
+ * \param exec_time Time of the packet creation.
+ * \param incoming Indicates if the packet is input.
+ * \param style Style of the output.
+ */
+void print_packet(const knot_pkt_t *packet,
+ const net_t *net,
+ const size_t size,
+ const float elapsed,
+ const time_t exec_time,
+ const bool incoming,
+ const style_t *style);
diff --git a/src/utils/common/hex.c b/src/utils/common/hex.c
new file mode 100644
index 0000000..9683446
--- /dev/null
+++ b/src/utils/common/hex.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2017 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libknot/libknot.h"
+#include "contrib/ctype.h"
+#include "contrib/tolower.h"
+
+/*!
+ * \brief Convert HEX char to byte.
+ * \note Expects valid lowercase letters.
+ */
+static uint8_t hex_to_num(int c)
+{
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else {
+ return c - 'a' + 10;
+ }
+}
+
+/*!
+ * \brief Convert string encoded in hex to bytes.
+ */
+int hex_decode(const char *input, uint8_t **output, size_t *output_size)
+{
+ if (!input || input[0] == '\0' || !output || !output_size) {
+ return KNOT_EINVAL;
+ }
+
+ // input validation (length and content)
+
+ size_t input_size = strlen(input);
+ if (input_size % 2 != 0) {
+ return KNOT_EMALF;
+ }
+
+ for (size_t i = 0; i < input_size; i++) {
+ if (!is_xdigit(input[i])) {
+ return KNOT_EMALF;
+ }
+ }
+
+ // output allocation
+
+ size_t result_size = input_size / 2;
+ assert(result_size > 0);
+ uint8_t *result = malloc(result_size);
+ if (!result) {
+ return KNOT_ENOMEM;
+ }
+
+ // conversion
+
+ for (size_t i = 0; i < result_size; i++) {
+ int high_nib = knot_tolower(input[2 * i]);
+ int low_nib = knot_tolower(input[2 * i + 1]);
+
+ result[i] = hex_to_num(high_nib) << 4 | hex_to_num(low_nib);
+ }
+
+ *output = result;
+ *output_size = result_size;
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/common/hex.h b/src/utils/common/hex.h
new file mode 100644
index 0000000..efe81be
--- /dev/null
+++ b/src/utils/common/hex.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*!
+ * \brief Convert string encoded in hex to bytes.
+ *
+ * \param input Hex encoded input string.
+ * \param output Decoded bytes.
+ * \param output_size Size of the output.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int hex_decode(const char *input, uint8_t **output, size_t *output_size);
diff --git a/src/utils/common/https.c b/src/utils/common/https.c
new file mode 100644
index 0000000..de98586
--- /dev/null
+++ b/src/utils/common/https.c
@@ -0,0 +1,525 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <arpa/inet.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/base64url.h"
+#include "contrib/macros.h"
+#include "contrib/musl/inet_ntop.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/url-parser/url_parser.h"
+#include "libknot/errcode.h"
+#include "utils/common/https.h"
+#include "utils/common/msg.h"
+
+#define is_read(ctx) (ctx->stream == -1)
+
+int https_params_copy(https_params_t *dst, const https_params_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ dst->enable = src->enable;
+ dst->method = src->method;
+ if (src->path != NULL) {
+ dst->path = strdup(src->path);
+ if (dst->path == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+void https_params_clean(https_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ params->enable = false;
+ params->method = GET;
+ free(params->path);
+ params->path = NULL;
+}
+
+#ifdef LIBNGHTTP2
+
+#define HTTP_STATUS_SUCCESS 200
+#define HTTPS_MAX_STREAMS 16
+#define HTTPS_AUTHORITY_LEN (INET6_ADDRSTRLEN + 2)
+
+#define MAKE_NV(K, KS, V, VS) \
+ { (uint8_t *)K, (uint8_t *)V, KS, VS, NGHTTP2_NV_FLAG_NONE }
+
+#define MAKE_STATIC_NV(K, V) \
+ MAKE_NV(K, sizeof(K) - 1, V, sizeof(V) - 1)
+
+static const char default_path[] = "/dns-query";
+static const char default_query[] = "?dns=";
+
+static const nghttp2_settings_entry settings[] = {
+ { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, HTTPS_MAX_STREAMS }
+};
+
+const gnutls_datum_t doh_alpn = {
+ .data = (unsigned char *)"h2",
+ .size = 2
+};
+
+static bool https_status_is_redirect(unsigned long status)
+{
+ switch (status) {
+ case 301UL:
+ case 302UL:
+ case 307UL:
+ case 308UL:
+ return true;
+ }
+ return false;
+}
+
+static ssize_t https_send_callback(nghttp2_session *session, const uint8_t *data,
+ size_t length, int flags, void *user_data)
+{
+ assert(user_data);
+
+ gnutls_session_t tls_session = ((https_ctx_t *)user_data)->tls->session;
+ ssize_t len = 0;
+
+ gnutls_record_cork(tls_session);
+ if ((len = gnutls_record_send(tls_session, data, length)) <= 0) {
+ WARN("TLS, failed to send");
+ return KNOT_NET_ESEND;
+ }
+ return len;
+}
+
+static int https_on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
+ void *user_data)
+{
+ assert(user_data);
+
+ gnutls_session_t tls_session = ((https_ctx_t *)user_data)->tls->session;
+ while (gnutls_record_check_corked(tls_session) > 0) {
+ int ret = gnutls_record_uncork(tls_session, 0);
+ if (ret < 0 && gnutls_error_is_fatal(ret) != 0) {
+ WARN("TLS, failed to send (%s)", gnutls_strerror(ret));
+ return KNOT_NET_ESEND;
+ }
+ }
+ return KNOT_EOK;
+}
+
+static ssize_t https_recv_callback(nghttp2_session *session, uint8_t *data, size_t length,
+ int flags, void *user_data)
+{
+ assert(user_data);
+
+ https_ctx_t *ctx = (https_ctx_t *)user_data;
+ struct pollfd pfd = {
+ .fd = ctx->tls->sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ ssize_t ret = 0;
+ while ((ret = gnutls_record_recv(ctx->tls->session, data, length)) <= 0) {
+ if (is_read(ctx)) { //Unblock `nghttp2_session_recv(nghttp2_session)`
+ return NGHTTP2_ERR_WOULDBLOCK;
+ }
+ if (ret == 0) {
+ WARN("TLS, peer has closed the connection");
+ return KNOT_NET_ERECV;
+ } else if (gnutls_error_is_fatal(ret)) {
+ WARN("TLS, failed to receive reply (%s)",
+ gnutls_strerror(ret));
+ return KNOT_NET_ERECV;
+ } else if (poll(&pfd, 1, 1000 * ctx->tls->wait) != 1) {
+ WARN("TLS, peer took too long to respond");
+ return KNOT_ETIMEOUT;
+ }
+ }
+
+ return ret;
+}
+
+static int https_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id,
+ const uint8_t *data, size_t len, void *user_data)
+{
+ assert(user_data);
+
+ https_ctx_t *ctx = (https_ctx_t *)user_data;
+ if (ctx->stream == stream_id) {
+ int cpy_len = MIN(len, ctx->recv_buflen);
+ memcpy(ctx->recv_buf, data, cpy_len);
+ ctx->recv_buf += cpy_len;
+ ctx->recv_buflen -= cpy_len;
+ }
+ return KNOT_EOK;
+}
+
+static int https_on_stream_close_callback(nghttp2_session *session, int32_t stream_id, uint32_t error_code, void *user_data)
+{
+ assert(user_data);
+
+ https_ctx_t *ctx = (https_ctx_t *)user_data;
+ if (ctx->stream == stream_id) {
+ ctx->stream = -1;
+ }
+ return KNOT_EOK;
+}
+
+static int https_on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
+ const uint8_t *name, size_t namelen,
+ const uint8_t *value, size_t valuelen,
+ uint8_t flags, void *user_data)
+{
+ assert(user_data);
+ https_ctx_t *ctx = (https_ctx_t *)user_data;
+
+ if (!strncasecmp(":status", (const char *)name, namelen)) {
+ char *end;
+ long status;
+ status = strtoul((const char *)value, &end, 10);
+ if (value != (const uint8_t *)end) {
+ ctx->status = status;
+ }
+ }
+ else if (!strncasecmp("location", (const char *)name, namelen) &&
+ https_status_is_redirect(ctx->status)) {
+ struct http_parser_url redirect_url;
+ http_parser_parse_url((const char *)value, valuelen, 0, &redirect_url);
+
+ bool r_auth = redirect_url.field_set & (1 << UF_HOST);
+ bool r_path = redirect_url.field_set & (1 << UF_PATH);
+ char *old_auth = ctx->authority, *old_path = ctx->path;
+
+ if (r_auth) {
+ ctx->authority = strndup((const char *)(value + redirect_url.field_data[UF_HOST].off),
+ redirect_url.field_data[UF_HOST].len);
+ }
+ if (r_path) {
+ ctx->path = strndup((const char *)(value + redirect_url.field_data[UF_PATH].off),
+ redirect_url.field_data[UF_PATH].len);
+ }
+ WARN("HTTP redirect (%s%s)->(%s%s)", old_auth, old_path, ctx->authority, ctx->path);
+ if (r_auth) {
+ free(old_auth);
+ }
+ if (r_path) {
+ free(old_path);
+ }
+ return https_send_dns_query(ctx, ctx->send_buf, ctx->send_buflen);
+ }
+ return KNOT_EOK;
+}
+
+int https_ctx_init(https_ctx_t *ctx, tls_ctx_t *tls_ctx, const https_params_t *params)
+{
+ if (ctx == NULL || tls_ctx == NULL || params == NULL) {
+ return KNOT_EINVAL;
+ }
+ if (ctx->session != NULL) { // Already initialized before
+ return KNOT_EINVAL;
+ }
+ if (!params->enable) {
+ return KNOT_EINVAL;
+ }
+
+ nghttp2_session_callbacks *callbacks;
+ nghttp2_session_callbacks_new(&callbacks);
+ nghttp2_session_callbacks_set_send_callback(callbacks, https_send_callback);
+ nghttp2_session_callbacks_set_on_frame_send_callback(callbacks, https_on_frame_send_callback);
+ nghttp2_session_callbacks_set_recv_callback(callbacks, https_recv_callback);
+ nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, https_on_data_chunk_recv_callback);
+ nghttp2_session_callbacks_set_on_header_callback(callbacks, https_on_header_callback);
+ nghttp2_session_callbacks_set_on_stream_close_callback(callbacks, https_on_stream_close_callback);
+
+ int ret = nghttp2_session_client_new(&(ctx->session), callbacks, ctx);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+
+ nghttp2_session_callbacks_del(callbacks);
+
+ if (pthread_mutex_init(&ctx->recv_mx, NULL) != 0) {
+ return KNOT_EINVAL;
+ }
+
+ ctx->tls = tls_ctx;
+ ctx->params = *params;
+ ctx->authority = (tls_ctx->params->hostname) ? strdup(tls_ctx->params->hostname) : NULL;
+ ctx->path = strdup((ctx->params.path) ? ctx->params.path : (char *)default_path);
+ ctx->stream = -1;
+
+ return KNOT_EOK;
+}
+
+static int sockaddr_to_authority(char *buf, const size_t buf_len, const struct sockaddr_storage *ss)
+{
+ if (buf == NULL || ss == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const char *out = NULL;
+
+ /* Convert IPv6 network address string. */
+ if (ss->ss_family == AF_INET6) {
+ if (buf_len < HTTPS_AUTHORITY_LEN) {
+ return KNOT_EINVAL;
+ }
+
+ const struct sockaddr_in6 *s = (const struct sockaddr_in6 *)ss;
+ buf[0] = '[';
+
+ out = knot_inet_ntop(ss->ss_family, &s->sin6_addr, buf + 1, buf_len - 1);
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ buf += strlen(buf);
+ buf[0] = ']';
+ buf[1] = '\0';
+ /* Convert IPv4 network address string. */
+ } else if (ss->ss_family == AF_INET) {
+ if (buf_len < INET_ADDRSTRLEN) {
+ return KNOT_EINVAL;
+ }
+
+ const struct sockaddr_in *s = (const struct sockaddr_in *)ss;
+
+ out = knot_inet_ntop(ss->ss_family, &s->sin_addr, buf, buf_len);
+ if (out == NULL) {
+ return KNOT_EINVAL;
+ }
+ /* Unknown network address family. */
+ } else {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+int https_ctx_connect(https_ctx_t *ctx, int sockfd, bool fastopen,
+ struct sockaddr_storage *addr)
+{
+ if (ctx == NULL || addr == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Create TLS connection
+ int ret = tls_ctx_connect(ctx->tls, sockfd, fastopen, addr);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Perform HTTP handshake
+ ret = nghttp2_submit_settings(ctx->session, NGHTTP2_FLAG_NONE, settings,
+ sizeof(settings) / sizeof(*settings));
+ if (ret != 0) {
+ return KNOT_NET_ESOCKET;
+ }
+ ret = nghttp2_session_send(ctx->session);
+ if (ret != 0) {
+ return KNOT_NET_ESOCKET;
+ }
+
+ // Save authority server
+ if (ctx->authority == NULL) {
+ ctx->authority = calloc(HTTPS_AUTHORITY_LEN, 1);
+ ret = sockaddr_to_authority(ctx->authority, HTTPS_AUTHORITY_LEN, addr);
+ if (ret != KNOT_EOK) {
+ free(ctx->authority);
+ ctx->authority = NULL;
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int https_send_dns_query_common(https_ctx_t *ctx, nghttp2_nv *hdrs, size_t hdrs_len, nghttp2_data_provider *data_provider)
+{
+ assert(hdrs != NULL && hdrs_len > 0);
+
+ ctx->stream = nghttp2_submit_request(ctx->session, NULL, hdrs, hdrs_len,
+ data_provider, NULL);
+ if (ctx->stream < 0) {
+ return KNOT_NET_ESEND;
+ }
+ int ret = nghttp2_session_send(ctx->session);
+ if (ret != 0) {
+ return KNOT_NET_ESEND;
+ }
+
+ return KNOT_EOK;
+}
+
+static int https_send_dns_query_get(https_ctx_t *ctx)
+{
+ const size_t dns_query_len = strlen(ctx->path) +
+ sizeof(default_query) +
+ (ctx->send_buflen * 4) / 3 + 3;
+ char dns_query[dns_query_len];
+ strlcpy(dns_query, ctx->path, dns_query_len);
+ strlcat(dns_query, default_query, dns_query_len);
+
+ size_t tmp_strlen = strlen(dns_query);
+ int32_t ret = knot_base64url_encode(ctx->send_buf, ctx->send_buflen,
+ (uint8_t *)(dns_query + tmp_strlen), dns_query_len - tmp_strlen - 1);
+ if (ret < 0) {
+ return KNOT_EINVAL;
+ }
+
+ nghttp2_nv hdrs[] = {
+ MAKE_STATIC_NV(":method", "GET"),
+ MAKE_STATIC_NV(":scheme", "https"),
+ MAKE_NV(":authority", 10, ctx->authority, strlen(ctx->authority)),
+ MAKE_NV(":path", 5, dns_query, tmp_strlen + ret),
+ MAKE_STATIC_NV("accept", "application/dns-message"),
+ };
+
+ return https_send_dns_query_common(ctx, hdrs, sizeof(hdrs) / sizeof(*hdrs),
+ NULL);
+}
+
+static ssize_t https_send_data_callback(nghttp2_session *session, int32_t stream_id,
+ uint8_t *buf, size_t length, uint32_t *data_flags,
+ nghttp2_data_source *source, void *user_data)
+{
+ https_data_provider_t *buffer = source->ptr;
+ ssize_t sent = (length < buffer->buf_len) ? length : buffer->buf_len;
+
+ memcpy(buf, buffer->buf, sent);
+ buffer->buf += sent;
+ buffer->buf_len -= sent;
+ if (!buffer->buf_len) {
+ *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+ }
+
+ return sent;
+}
+
+static int https_send_dns_query_post(https_ctx_t *ctx)
+{
+ // size of number in text form (base 10)
+ char content_length[sizeof(size_t) * 3 + 1]; // limit for x->inf: log10(2^(8*sizeof(x))-1)/sizeof(x) = 2,408239965 -> 3
+ int content_length_len = sprintf(content_length, "%zu", ctx->send_buflen);
+
+ nghttp2_nv hdrs[] = {
+ MAKE_STATIC_NV(":method", "POST"),
+ MAKE_STATIC_NV(":scheme", "https"),
+ MAKE_NV(":authority", 10, ctx->authority, strlen(ctx->authority)),
+ MAKE_NV(":path", 5, ctx->path, strlen(ctx->path)),
+ MAKE_STATIC_NV("accept", "application/dns-message"),
+ MAKE_STATIC_NV("content-type", "application/dns-message"),
+ MAKE_NV("content-length", 14, content_length, content_length_len)
+ };
+
+ https_data_provider_t data = {
+ .buf = ctx->send_buf,
+ .buf_len = ctx->send_buflen
+ };
+
+ nghttp2_data_provider data_provider = {
+ .source.ptr = &data,
+ .read_callback = https_send_data_callback
+ };
+
+ return https_send_dns_query_common(ctx, hdrs, sizeof(hdrs) / sizeof(*hdrs),
+ &data_provider);
+}
+
+int https_send_dns_query(https_ctx_t *ctx, const uint8_t *buf, const size_t buf_len)
+{
+ if (ctx == NULL || buf == NULL || buf_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ ctx->send_buf = buf;
+ ctx->send_buflen = buf_len;
+
+ assert(ctx->params.method == POST || ctx->params.method == GET);
+
+ if (ctx->params.method == POST) {
+ return https_send_dns_query_post(ctx);
+ } else {
+ return https_send_dns_query_get(ctx);
+ }
+}
+
+int https_recv_dns_response(https_ctx_t *ctx, uint8_t *buf, const size_t buf_len)
+{
+ if (ctx == NULL || buf == NULL || buf_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ pthread_mutex_lock(&ctx->recv_mx);
+ ctx->recv_buf = buf;
+ ctx->recv_buflen = buf_len;
+
+ int ret = nghttp2_session_recv(ctx->session);
+ if (ret != 0) {
+ pthread_mutex_unlock(&ctx->recv_mx);
+ return KNOT_NET_ERECV;
+ }
+ ctx->recv_buf = NULL;
+
+ pthread_mutex_unlock(&ctx->recv_mx);
+
+ if (ctx->status != HTTP_STATUS_SUCCESS) {
+ print_https(ctx);
+ return KNOT_NET_ERECV;
+ }
+
+ assert(buf_len >= ctx->recv_buflen);
+ return buf_len - ctx->recv_buflen;
+}
+
+void https_ctx_deinit(https_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ nghttp2_session_del(ctx->session);
+ ctx->session = NULL;
+ pthread_mutex_destroy(&ctx->recv_mx);
+ free(ctx->path);
+ ctx->path = NULL;
+ free(ctx->authority);
+ ctx->authority = NULL;
+}
+
+void print_https(const https_ctx_t *ctx)
+{
+ if (!ctx || !ctx->params.enable || !ctx->authority || !ctx->path) {
+ return;
+ }
+
+ printf(";; HTTP session (HTTP/2-%s)-(%s%s)-(status: %lu)\n",
+ ctx->params.method == POST ? "POST" : "GET", ctx->authority,
+ ctx->path, ctx->status);
+}
+
+#endif //LIBNGHTTP2
diff --git a/src/utils/common/https.h b/src/utils/common/https.h
new file mode 100644
index 0000000..aed1cd5
--- /dev/null
+++ b/src/utils/common/https.h
@@ -0,0 +1,150 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+/*! \brief HTTP method to transfer query. */
+typedef enum {
+ POST,
+ GET
+} https_method_t;
+
+/*! \brief HTTPS parameters. */
+typedef struct {
+ /*! Use HTTPS indicator. */
+ bool enable;
+ /*! HTTP method to transfer query. */
+ https_method_t method;
+ /*! Path */
+ char *path;
+} https_params_t;
+
+int https_params_copy(https_params_t *dst, const https_params_t *src);
+void https_params_clean(https_params_t *params);
+
+#ifdef LIBNGHTTP2
+
+#include <netinet/in.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <nghttp2/nghttp2.h>
+
+#include "utils/common/tls.h"
+
+extern const gnutls_datum_t doh_alpn;
+
+/*! \brief Structure that stores data source for DATA frames. */
+typedef struct {
+ const uint8_t *buf;
+ size_t buf_len;
+} https_data_provider_t;
+
+/*! \brief HTTPS context. */
+typedef struct {
+ // Parameters
+ https_params_t params;
+
+ // Contexts
+ nghttp2_session *session;
+ tls_ctx_t *tls;
+ char *authority;
+ char *path;
+
+ // Send destination
+ const uint8_t *send_buf;
+ size_t send_buflen;
+
+ // Recv destination
+ uint8_t *recv_buf;
+ size_t recv_buflen;
+ unsigned long status;
+
+ // Recv locks
+ pthread_mutex_t recv_mx;
+ int32_t stream;
+} https_ctx_t;
+
+/*!
+ * \brief Initialize HTTPS context.
+ *
+ * \param ctx HTTPS context.
+ * \param tls_ctx TLS context.
+ * \param params Parameter table.
+ *
+ * \retval KNOT_EOK When initialized.
+ * \retval KNOT_EINVAL When parameters are invalid.
+ */
+int https_ctx_init(https_ctx_t *ctx, tls_ctx_t *tls_ctx, const https_params_t *params);
+
+/*!
+ * \brief Create TLS connection and perform HTTPS handshake.
+ *
+ * \param ctx HTTPS context.
+ * \param sockfd Socket descriptor.
+ * \param fastopen Use TCP Fast Open indication.
+ * \param addr Socket address storage with address to server side.
+ *
+ * \retval KNOT_EOK When successfully connected.
+ * \retval KNOT_EINVAL When parameters are invalid.
+ * \retval KNOT_NET_ESOCKET When socket is no accessible.
+ * \retval KNOT_NET_ETIMEOUT When server respond takes too long.
+ * \retval KNOT_NET_ECONNECT When unnable to connect to the server.
+ */
+int https_ctx_connect(https_ctx_t *ctx, int sockfd, bool fastopen,
+ struct sockaddr_storage *addr);
+
+/*!
+ * \brief Send buffer as DNS message over HTTPS.
+ *
+ * \param ctx HTTPS context.
+ * \param buf Buffer with DNS message in wire format.
+ * \param buf_len Length of buffer.
+ *
+ * \retval KNOT_EOK When successfully sent.
+ * \retval KNOT_EINVAL When parameters are invalid.
+ * \retval KNOT_NET_ESEND When error occurs while sending a data.
+ */
+int https_send_dns_query(https_ctx_t *ctx, const uint8_t *buf, const size_t buf_len);
+
+/*!
+ * \brief Receive DATA frame as HTTPS packet, and store it into buffer.
+ *
+ * \param ctx HTTPS context.
+ * \param buf Buffer where will be DNS response stored.
+ * \param buf_len Length of buffer.
+ *
+ * \retval >=0 Number of bytes received in DATA frame.
+ * \retval KNOT_NET_ERECV When error while receive.
+ */
+int https_recv_dns_response(https_ctx_t *ctx, uint8_t *buf, const size_t buf_len);
+
+/*!
+ * \brief Deinitialize HTTPS context.
+ *
+ * \param ctx HTTPS context.
+ */
+void https_ctx_deinit(https_ctx_t *ctx);
+
+/*!
+ * \brief Prints information about HTTPS context.
+ *
+ * \param ctx HTTPS context.
+ */
+void print_https(const https_ctx_t *ctx);
+
+#endif //LIBNGHTTP2
diff --git a/src/utils/common/lookup.c b/src/utils/common/lookup.c
new file mode 100644
index 0000000..e7f6084
--- /dev/null
+++ b/src/utils/common/lookup.c
@@ -0,0 +1,295 @@
+/* Copyright (C) 2022 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 <string.h>
+
+#include "utils/common/lookup.h"
+#include "contrib/mempattern.h"
+#include "contrib/ucw/mempool.h"
+#include "libknot/error.h"
+
+int lookup_init(lookup_t *lookup)
+{
+ if (lookup == NULL) {
+ return KNOT_EINVAL;
+ }
+ memset(lookup, 0, sizeof(*lookup));
+
+ mm_ctx_mempool(&lookup->mm, MM_DEFAULT_BLKSIZE);
+ lookup->trie = trie_create(&lookup->mm);
+ if (lookup->trie == NULL) {
+ mp_delete(lookup->mm.ctx);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+static void reset_output(lookup_t *lookup)
+{
+ if (lookup == NULL) {
+ return;
+ }
+
+ mm_free(&lookup->mm, lookup->found.key);
+ lookup->found.key = NULL;
+ lookup->found.data = NULL;
+
+ lookup->iter.count = 0;
+
+ mm_free(&lookup->mm, lookup->iter.first_key);
+ lookup->iter.first_key = NULL;
+
+ trie_it_free(lookup->iter.it);
+ lookup->iter.it = NULL;
+}
+
+void lookup_deinit(lookup_t *lookup)
+{
+ if (lookup == NULL) {
+ return;
+ }
+
+ reset_output(lookup);
+
+ trie_free(lookup->trie);
+ mp_delete(lookup->mm.ctx);
+}
+
+int lookup_insert(lookup_t *lookup, const char *str, void *data)
+{
+ if (lookup == NULL || str == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t str_len = strlen(str);
+ if (str_len == 0) {
+ return KNOT_EINVAL;
+ }
+
+ trie_val_t *val = trie_get_ins(lookup->trie, (const trie_key_t *)str, str_len);
+ if (val == NULL) {
+ return KNOT_ENOMEM;
+ }
+ *val = data;
+
+ return KNOT_EOK;
+}
+
+int lookup_remove(lookup_t *lookup, const char *str)
+{
+ if (lookup == NULL || str == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ size_t str_len = strlen(str);
+ if (str_len > 0) {
+ (void)trie_del(lookup->trie, (const trie_key_t *)str, str_len, NULL);
+ }
+
+ return KNOT_EOK;
+}
+
+static int set_key(lookup_t *lookup, char **dst, const char *key, size_t key_len)
+{
+ if (*dst != NULL) {
+ mm_free(&lookup->mm, *dst);
+ }
+ *dst = mm_alloc(&lookup->mm, key_len + 1);
+ if (*dst == NULL) {
+ return KNOT_ENOMEM;
+ }
+ memcpy(*dst, key, key_len);
+ (*dst)[key_len] = '\0';
+
+ return KNOT_EOK;
+}
+
+int lookup_search(lookup_t *lookup, const char *str, size_t str_len)
+{
+ if (lookup == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Change NULL string to the empty one.
+ if (str == NULL) {
+ str = "";
+ }
+
+ reset_output(lookup);
+
+ size_t new_len = 0;
+ trie_it_t *it = trie_it_begin(lookup->trie);
+ for (; !trie_it_finished(it); trie_it_next(it)) {
+ size_t len;
+ const char *key = (const char *)trie_it_key(it, &len);
+
+ // Compare with a shorter key.
+ if (len < str_len) {
+ int ret = memcmp(str, key, len);
+ if (ret >= 0) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ // Compare with an equal length or longer key.
+ int ret = memcmp(str, key, str_len);
+ if (ret == 0) {
+ lookup->iter.count++;
+
+ // First candidate.
+ if (lookup->iter.count == 1) {
+ ret = set_key(lookup, &lookup->found.key, key, len);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ lookup->found.data = *trie_it_val(it);
+ new_len = len;
+ // Another candidate.
+ } else if (new_len > str_len) {
+ if (new_len > len) {
+ new_len = len;
+ }
+ while (memcmp(lookup->found.key, key, new_len) != 0) {
+ new_len--;
+ }
+ }
+ // Stop if greater than the key, and also than all the following keys.
+ } else if (ret < 0) {
+ break;
+ }
+ }
+ trie_it_free(it);
+
+ switch (lookup->iter.count) {
+ case 0:
+ return KNOT_ENOENT;
+ case 1:
+ return KNOT_EOK;
+ default:
+ // Store full name of the first candidate.
+ if (set_key(lookup, &lookup->iter.first_key, lookup->found.key,
+ strlen(lookup->found.key)) != KNOT_EOK) {
+ return KNOT_ENOMEM;
+ }
+ lookup->found.key[new_len] = '\0';
+ lookup->found.data = NULL;
+
+ return KNOT_EFEWDATA;
+ }
+}
+
+void lookup_list(lookup_t *lookup)
+{
+ if (lookup == NULL || lookup->iter.first_key == NULL) {
+ return;
+ }
+
+ if (lookup->iter.it != NULL) {
+ if (trie_it_finished(lookup->iter.it)) {
+ trie_it_free(lookup->iter.it);
+ lookup->iter.it = NULL;
+ return;
+ }
+
+ trie_it_next(lookup->iter.it);
+
+ size_t len;
+ const char *key = (const char *)trie_it_key(lookup->iter.it, &len);
+
+ int ret = set_key(lookup, &lookup->found.key, key, len);
+ if (ret == KNOT_EOK) {
+ lookup->found.data = *trie_it_val(lookup->iter.it);
+ }
+ return;
+ }
+
+ lookup->iter.it = trie_it_begin(lookup->trie);
+ while (!trie_it_finished(lookup->iter.it)) {
+ size_t len;
+ const char *key = (const char *)trie_it_key(lookup->iter.it, &len);
+
+ if (strncmp(key, lookup->iter.first_key, len) == 0) {
+ int ret = set_key(lookup, &lookup->found.key, key, len);
+ if (ret == KNOT_EOK) {
+ lookup->found.data = *trie_it_val(lookup->iter.it);
+ }
+ break;
+ }
+ trie_it_next(lookup->iter.it);
+ }
+}
+
+static void print_options(lookup_t *lookup, EditLine *el)
+{
+ // Get terminal lines.
+ unsigned lines = 0;
+ if (el_get(el, EL_GETTC, "li", &lines) != 0 || lines < 3) {
+ return;
+ }
+
+ for (size_t i = 1; i <= lookup->iter.count; i++) {
+ lookup_list(lookup);
+ printf("\n%s", lookup->found.key);
+
+ if (i > 1 && i % (lines - 1) == 0 && i < lookup->iter.count) {
+ printf("\n Display next from %zu possibilities? (y or n)",
+ lookup->iter.count);
+ char next;
+ el_getc(el, &next);
+ if (next != 'y') {
+ break;
+ }
+ }
+ }
+
+ printf("\n");
+ fflush(stdout);
+}
+
+int lookup_complete(lookup_t *lookup, const char *str, size_t str_len,
+ EditLine *el, bool add_space)
+{
+ if (lookup == NULL || el == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Try to complete the command name.
+ int ret = lookup_search(lookup, str, str_len);
+ switch (ret) {
+ case KNOT_EOK:
+ el_deletestr(el, str_len);
+ el_insertstr(el, lookup->found.key);
+ if (add_space) {
+ el_insertstr(el, " ");
+ }
+ break;
+ case KNOT_EFEWDATA:
+ if (strlen(lookup->found.key) > str_len) {
+ el_deletestr(el, str_len);
+ el_insertstr(el, lookup->found.key);
+ } else {
+ print_options(lookup, el);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
diff --git a/src/utils/common/lookup.h b/src/utils/common/lookup.h
new file mode 100644
index 0000000..b6dc8ee
--- /dev/null
+++ b/src/utils/common/lookup.h
@@ -0,0 +1,124 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <histedit.h>
+
+#include "libknot/mm_ctx.h"
+#include "contrib/qp-trie/trie.h"
+
+/*! Lookup context. */
+typedef struct {
+ /*! Memory pool context. */
+ knot_mm_t mm;
+ /*! Main trie storage. */
+ trie_t *trie;
+
+ /*! Current (iteration) data context. */
+ struct {
+ /*! Stored key. */
+ char *key;
+ /*! Corresponding key data. */
+ void *data;
+ } found;
+
+ /*! Iteration context. */
+ struct {
+ /*! Total number of possibilities. */
+ size_t count;
+ /*! The first possibility. */
+ char *first_key;
+ /*! Hat-trie iterator. */
+ trie_it_t *it;
+ } iter;
+} lookup_t;
+
+/*!
+ * Initializes the lookup context.
+ *
+ * \param[in] lookup Lookup context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int lookup_init(lookup_t *lookup);
+
+/*!
+ * Deinitializes the lookup context.
+ *
+ * \param[in] lookup Lookup context.
+ */
+void lookup_deinit(lookup_t *lookup);
+
+/*!
+ * Inserts given key and data into the lookup.
+ *
+ * \param[in] lookup Lookup context.
+ * \param[in] str Textual key.
+ * \param[in] data Key textual data.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int lookup_insert(lookup_t *lookup, const char *str, void *data);
+
+/*!
+ * Removes given key from the lookup.
+ *
+ * \param[in] lookup Lookup context.
+ * \param[in] str Textual key.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int lookup_remove(lookup_t *lookup, const char *str);
+
+/*!
+ * Searches the lookup container for the given key.
+ *
+ * \note If one candidate, lookup.found contains the key/data,
+ * if more candidates, lookup.found contains the common key prefix and
+ * lookup.iter.first_key is the first candidate key.
+ *
+ * \param[in] lookup Lookup context.
+ * \param[in] str Textual key.
+ * \param[in] str_len Textual key length.
+ *
+ * \return Error code, KNOT_EOK if 1 candidate, KNOT_ENOENT if no candidate,
+ * and KNOT_EFEWDATA if more candidates are possible.
+ */
+int lookup_search(lookup_t *lookup, const char *str, size_t str_len);
+
+/*!
+ * Moves the lookup iterator to the next key candidate.
+ *
+ * \note lookup.found is updated.
+ *
+ * \param[in] lookup Lookup context.
+ */
+void lookup_list(lookup_t *lookup);
+
+/*!
+ * Completes the string based on the lookup content or prints all candidates.
+ *
+ * \param[in] lookup Lookup context.
+ * \param[in] str Textual key.
+ * \param[in] str_len Textual key length.
+ * \param[in] el Editline context.
+ * \param[in] add_space Add one space after completed string flag.
+ *
+ * \return Error code, same as lookup_search().
+ */
+int lookup_complete(lookup_t *lookup, const char *str, size_t str_len,
+ EditLine *el, bool add_space);
diff --git a/src/utils/common/msg.c b/src/utils/common/msg.c
new file mode 100644
index 0000000..c125297
--- /dev/null
+++ b/src/utils/common/msg.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2011 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils/common/msg.h"
+
+static volatile int MSG_DBG_STATE = 0; /* True if debugging is enabled. */
+
+int msg_enable_debug(int val)
+{
+ return MSG_DBG_STATE = val;
+}
+
+int msg_debug(const char *fmt, ...)
+{
+ int n = 0;
+ if (MSG_DBG_STATE) {
+ va_list ap;
+ va_start(ap, fmt);
+ n = vprintf(fmt, ap);
+ va_end(ap);
+ }
+ return n;
+}
diff --git a/src/utils/common/msg.h b/src/utils/common/msg.h
new file mode 100644
index 0000000..d2ed57e
--- /dev/null
+++ b/src/utils/common/msg.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#define ERROR_ ";; ERROR: "
+#define INFO_ ";; INFO: "
+#define WARNING_ ";; WARNING: "
+#define DEBUG_ ";; DEBUG: "
+
+#define ERR(msg, ...) { fprintf(stderr, ERROR_ msg "\n", ##__VA_ARGS__); fflush(stderr); }
+#define INFO(msg, ...) { fprintf(stdout, INFO_ msg "\n", ##__VA_ARGS__); fflush(stdout); }
+#define WARN(msg, ...) { fprintf(stderr, WARNING_ msg "\n", ##__VA_ARGS__); fflush(stderr); }
+#define DBG(msg, ...) { msg_debug(DEBUG_ msg "\n", ##__VA_ARGS__); fflush(stdout); }
+
+/*! \brief Enable/disable debugging. */
+int msg_enable_debug(int val);
+
+/*! \brief Print debug message. */
+int msg_debug(const char *fmt, ...);
+
+/*! \brief Debug message for null input. */
+#define DBG_NULL DBG("%s: null parameter", __func__)
+
+#define ERR2(msg, ...) { fprintf(stderr, "error: " msg "\n", ##__VA_ARGS__); fflush(stderr); }
+#define WARN2(msg, ...) { fprintf(stderr, "warning: " msg "\n", ##__VA_ARGS__); fflush(stderr); }
+#define INFO2(msg, ...) { fprintf(stdout, msg "\n", ##__VA_ARGS__); fflush(stdout); }
diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c
new file mode 100644
index 0000000..4f31551
--- /dev/null
+++ b/src/utils/common/netio.c
@@ -0,0 +1,896 @@
+/* Copyright (C) 2023 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 <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <sys/types.h> // OpenBSD
+#include <netinet/tcp.h> // TCP_FASTOPEN
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#include "utils/common/netio.h"
+#include "utils/common/msg.h"
+#include "utils/common/tls.h"
+#include "libknot/libknot.h"
+#include "contrib/proxyv2/proxyv2.h"
+#include "contrib/sockaddr.h"
+
+srv_info_t *srv_info_create(const char *name, const char *service)
+{
+ if (name == NULL || service == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Create output structure.
+ srv_info_t *server = calloc(1, sizeof(srv_info_t));
+
+ // Check output.
+ if (server == NULL) {
+ return NULL;
+ }
+
+ // Fill output.
+ server->name = strdup(name);
+ server->service = strdup(service);
+
+ if (server->name == NULL || server->service == NULL) {
+ srv_info_free(server);
+ return NULL;
+ }
+
+ // Return result.
+ return server;
+}
+
+void srv_info_free(srv_info_t *server)
+{
+ if (server == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ free(server->name);
+ free(server->service);
+ free(server);
+}
+
+int get_iptype(const ip_t ip, const srv_info_t *server)
+{
+ bool unix_socket = (server->name[0] == '/');
+
+ switch (ip) {
+ case IP_4:
+ return AF_INET;
+ case IP_6:
+ return AF_INET6;
+ default:
+ return unix_socket ? AF_UNIX : AF_UNSPEC;
+ }
+}
+
+int get_socktype(const protocol_t proto, const uint16_t type)
+{
+ switch (proto) {
+ case PROTO_TCP:
+ return SOCK_STREAM;
+ case PROTO_UDP:
+ return SOCK_DGRAM;
+ default:
+ if (type == KNOT_RRTYPE_AXFR || type == KNOT_RRTYPE_IXFR) {
+ return SOCK_STREAM;
+ } else {
+ return SOCK_DGRAM;
+ }
+ }
+}
+
+const char *get_sockname(const int socktype)
+{
+ switch (socktype) {
+ case SOCK_STREAM:
+ return "TCP";
+ case SOCK_DGRAM:
+ return "UDP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int get_addr(const srv_info_t *server,
+ const int iptype,
+ const int socktype,
+ struct addrinfo **info)
+{
+ struct addrinfo hints;
+
+ // Set connection hints.
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = iptype;
+ hints.ai_socktype = socktype;
+
+ // Get connection parameters.
+ int ret = getaddrinfo(server->name, server->service, &hints, info);
+ switch (ret) {
+ case 0:
+ return 0;
+#ifdef EAI_ADDRFAMILY /* EAI_ADDRFAMILY isn't implemented in FreeBSD/macOS anymore. */
+ case EAI_ADDRFAMILY:
+ break;
+#else /* FreeBSD, macOS, and likely others return EAI_NONAME instead. */
+ case EAI_NONAME:
+ if (iptype != AF_UNSPEC) {
+ break;
+ }
+ /* FALLTHROUGH */
+#endif /* EAI_ADDRFAMILY */
+ default:
+ ERR("%s for %s@%s", gai_strerror(ret), server->name, server->service);
+ }
+ return -1;
+}
+
+void get_addr_str(const struct sockaddr_storage *ss,
+ const int socktype,
+ char **dst)
+{
+ char addr_str[SOCKADDR_STRLEN] = {0};
+
+ // Get network address string and port number.
+ sockaddr_tostr(addr_str, sizeof(addr_str), ss);
+
+ // Calculate needed buffer size
+ const char *sock_name = get_sockname(socktype);
+ size_t buflen = strlen(addr_str) + strlen(sock_name) + 3 /* () */;
+
+ // Free previous string if any and write result
+ free(*dst);
+ *dst = malloc(buflen);
+ if (*dst != NULL) {
+ int ret = snprintf(*dst, buflen, "%s(%s)", addr_str, sock_name);
+ if (ret <= 0 || ret >= buflen) {
+ **dst = '\0';
+ }
+ }
+}
+
+int net_init(const srv_info_t *local,
+ const srv_info_t *remote,
+ const int iptype,
+ const int socktype,
+ const int wait,
+ const net_flags_t flags,
+ const tls_params_t *tls_params,
+ const https_params_t *https_params,
+ const quic_params_t *quic_params,
+ const struct sockaddr *proxy_src,
+ const struct sockaddr *proxy_dst,
+ net_t *net)
+{
+ if (remote == NULL || net == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Clean network structure.
+ memset(net, 0, sizeof(*net));
+ net->sockfd = -1;
+
+ if (iptype == AF_UNIX) {
+ struct addrinfo *info = calloc(1, sizeof(struct addrinfo));
+ info->ai_addr = calloc(1, sizeof(struct sockaddr_storage));
+ info->ai_addrlen = sizeof(struct sockaddr_un);
+ info->ai_socktype = socktype;
+ info->ai_family = iptype;
+ int ret = sockaddr_set_raw((struct sockaddr_storage *)info->ai_addr,
+ AF_UNIX, (const uint8_t *)remote->name,
+ strlen(remote->name));
+ if (ret != KNOT_EOK) {
+ free(info->ai_addr);
+ free(info);
+ return ret;
+ }
+ net->remote_info = info;
+ } else {
+ // Get remote address list.
+ if (get_addr(remote, iptype, socktype, &net->remote_info) != 0) {
+ net_clean(net);
+ return KNOT_NET_EADDR;
+ }
+ }
+
+ // Set current remote address.
+ net->srv = net->remote_info;
+
+ // Get local address if specified.
+ if (local != NULL) {
+ if (get_addr(local, iptype, socktype, &net->local_info) != 0) {
+ net_clean(net);
+ return KNOT_NET_EADDR;
+ }
+ }
+
+ // Store network parameters.
+ net->sockfd = -1;
+ net->iptype = iptype;
+ net->socktype = socktype;
+ net->wait = wait;
+ net->local = local;
+ net->remote = remote;
+ net->flags = flags;
+ net->proxy.src = proxy_src;
+ net->proxy.dst = proxy_dst;
+
+ if ((bool)(proxy_src == NULL) != (bool)(proxy_dst == NULL) ||
+ (proxy_src != NULL && proxy_src->sa_family != proxy_dst->sa_family)) {
+ net_clean(net);
+ return KNOT_EINVAL;
+ }
+
+ // Prepare for TLS.
+ if (tls_params != NULL && tls_params->enable) {
+ int ret = 0;
+#ifdef LIBNGHTTP2
+ // Prepare for HTTPS.
+ if (https_params != NULL && https_params->enable) {
+ ret = tls_ctx_init(&net->tls, tls_params,
+ GNUTLS_NONBLOCK, net->wait);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ ret = https_ctx_init(&net->https, &net->tls, https_params);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ } else
+#endif //LIBNGHTTP2
+#ifdef ENABLE_QUIC
+ if (quic_params != NULL && quic_params->enable) {
+ ret = tls_ctx_init(&net->tls, tls_params,
+ GNUTLS_NONBLOCK | GNUTLS_ENABLE_EARLY_DATA |
+ GNUTLS_NO_END_OF_EARLY_DATA, net->wait);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ ret = quic_ctx_init(&net->quic, &net->tls, quic_params);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ } else
+#endif //ENABLE_QUIC
+ {
+ ret = tls_ctx_init(&net->tls, tls_params,
+ GNUTLS_NONBLOCK, net->wait);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*!
+ * Connect with TCP Fast Open.
+ */
+static int fastopen_connect(int sockfd, const struct addrinfo *srv)
+{
+#if defined( __FreeBSD__)
+ const int enable = 1;
+ return setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN, &enable, sizeof(enable));
+#elif defined(__APPLE__)
+ // connection is performed lazily when first data are sent
+ struct sa_endpoints ep = {0};
+ ep.sae_dstaddr = srv->ai_addr;
+ ep.sae_dstaddrlen = srv->ai_addrlen;
+ int flags = CONNECT_DATA_IDEMPOTENT|CONNECT_RESUME_ON_READ_WRITE;
+
+ return connectx(sockfd, &ep, SAE_ASSOCID_ANY, flags, NULL, 0, NULL, NULL);
+#elif defined(__linux__)
+ // connect() will be called implicitly with sendto(), sendmsg()
+ return 0;
+#else
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+/*!
+ * Sends data with TCP Fast Open.
+ */
+static int fastopen_send(int sockfd, const struct msghdr *msg, int timeout)
+{
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ return sendmsg(sockfd, msg, 0);
+#elif defined(__linux__)
+ int ret = sendmsg(sockfd, msg, MSG_FASTOPEN);
+ if (ret == -1 && errno == EINPROGRESS) {
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLOUT,
+ .revents = 0,
+ };
+ if (poll(&pfd, 1, 1000 * timeout) != 1) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ ret = sendmsg(sockfd, msg, 0);
+ }
+ return ret;
+#else
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
+static char *net_get_remote(const net_t *net)
+{
+ if (net->tls.params->sni != NULL) {
+ return net->tls.params->sni;
+ } else if (net->tls.params->hostname != NULL) {
+ return net->tls.params->hostname;
+ } else if (strchr(net->remote_str, ':') == NULL) {
+ char *at = strchr(net->remote_str, '@');
+ if (at != NULL && strncmp(net->remote->name, net->remote_str,
+ at - net->remote_str)) {
+ return net->remote->name;
+ }
+ }
+ return NULL;
+}
+
+#ifdef ENABLE_QUIC
+static int fd_set_recv_ecn(int fd, int family)
+{
+ unsigned int tos = 1;
+ switch (family) {
+ case AF_INET:
+#ifdef IP_RECVTOS
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &tos, sizeof(tos)) == -1) {
+ return knot_map_errno();
+ }
+#endif
+ break;
+ case AF_INET6:
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &tos, sizeof(tos)) == -1) {
+ return knot_map_errno();
+ }
+ break;
+ default:
+ return KNOT_EINVAL;
+ }
+ return KNOT_EOK;
+}
+#endif
+
+
+int net_connect(net_t *net)
+{
+ if (net == NULL || net->srv == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Set remote information string.
+ get_addr_str((struct sockaddr_storage *)net->srv->ai_addr,
+ net->socktype, &net->remote_str);
+
+ // Create socket.
+ int sockfd = socket(net->srv->ai_family, net->socktype, 0);
+ if (sockfd == -1) {
+ WARN("can't create socket for %s", net->remote_str);
+ return KNOT_NET_ESOCKET;
+ }
+
+ // Initialize poll descriptor structure.
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLOUT,
+ .revents = 0,
+ };
+
+ // Set non-blocking socket.
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
+ WARN("can't set non-blocking socket for %s", net->remote_str);
+ return KNOT_NET_ESOCKET;
+ }
+
+ // Bind address to socket if specified.
+ if (net->local_info != NULL) {
+ if (bind(sockfd, net->local_info->ai_addr,
+ net->local_info->ai_addrlen) == -1) {
+ WARN("can't assign address %s", net->local->name);
+ return KNOT_NET_ESOCKET;
+ }
+ } else {
+ // Ensure source port is always randomized (even for TCP).
+ struct sockaddr_storage local = { .ss_family = net->srv->ai_family };
+ (void)bind(sockfd, (struct sockaddr *)&local, sockaddr_len(&local));
+ }
+
+ int ret = 0;
+ if (net->socktype == SOCK_STREAM) {
+ int cs = 1, err;
+ socklen_t err_len = sizeof(err);
+ bool fastopen = net->flags & NET_FLAGS_FASTOPEN;
+
+#ifdef TCP_NODELAY
+ (void)setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &cs, sizeof(cs));
+#endif
+
+ // Establish a connection.
+ if (net->tls.params == NULL || !fastopen) {
+ if (fastopen) {
+ ret = fastopen_connect(sockfd, net->srv);
+ } else {
+ ret = connect(sockfd, net->srv->ai_addr, net->srv->ai_addrlen);
+ }
+ if (ret != 0 && errno != EINPROGRESS) {
+ WARN("can't connect to %s", net->remote_str);
+ close(sockfd);
+ return KNOT_NET_ECONNECT;
+ }
+
+ // Check for connection timeout.
+ if (!fastopen && poll(&pfd, 1, 1000 * net->wait) != 1) {
+ WARN("connection timeout for %s", net->remote_str);
+ close(sockfd);
+ return KNOT_NET_ECONNECT;
+ }
+
+ // Check if NB socket is writeable.
+ cs = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &err_len);
+ if (cs < 0 || err != 0) {
+ WARN("can't connect to %s", net->remote_str);
+ close(sockfd);
+ return KNOT_NET_ECONNECT;
+ }
+ }
+
+ if (net->tls.params != NULL) {
+#ifdef LIBNGHTTP2
+ if (net->https.params.enable) {
+ // Establish HTTPS connection.
+ char *remote = net_get_remote(net);
+ ret = tls_ctx_setup_remote_endpoint(&net->tls, &doh_alpn, 1, NULL,
+ remote);
+ if (ret != 0) {
+ close(sockfd);
+ return ret;
+ }
+ if (remote && net->https.authority == NULL) {
+ net->https.authority = strdup(remote);
+ }
+ ret = https_ctx_connect(&net->https, sockfd, fastopen,
+ (struct sockaddr_storage *)net->srv->ai_addr);
+ } else
+#endif //LIBNGHTTP2
+ {
+ // Establish TLS connection.
+ ret = tls_ctx_setup_remote_endpoint(&net->tls, &dot_alpn, 1, NULL,
+ net_get_remote(net));
+ if (ret != 0) {
+ close(sockfd);
+ return ret;
+ }
+ ret = tls_ctx_connect(&net->tls, sockfd, fastopen,
+ (struct sockaddr_storage *)net->srv->ai_addr);
+ }
+ if (ret != KNOT_EOK) {
+ close(sockfd);
+ return ret;
+ }
+ }
+ }
+#ifdef ENABLE_QUIC
+ else if (net->socktype == SOCK_DGRAM) {
+ if (net->quic.params.enable) {
+ // Establish QUIC connection.
+ ret = fd_set_recv_ecn(sockfd, net->srv->ai_family);
+ if (ret != KNOT_EOK) {
+ close(sockfd);
+ return ret;
+ }
+ ret = tls_ctx_setup_remote_endpoint(&net->tls,
+ doq_alpn, 4, QUIC_PRIORITY, net_get_remote(net));
+ if (ret != 0) {
+ close(sockfd);
+ return ret;
+ }
+ ret = quic_ctx_connect(&net->quic, sockfd,
+ (struct addrinfo *)net->srv);
+ if (ret != KNOT_EOK) {
+ close(sockfd);
+ return ret;
+ }
+ }
+ }
+#endif
+
+ // Store socket descriptor.
+ net->sockfd = sockfd;
+
+ return KNOT_EOK;
+}
+
+int net_set_local_info(net_t *net)
+{
+ if (net == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ socklen_t local_addr_len = sizeof(struct sockaddr_storage);
+
+ struct addrinfo *new_info = calloc(1, sizeof(*new_info) + local_addr_len);
+ if (new_info == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ new_info->ai_addr = (struct sockaddr *)(new_info + 1);
+ new_info->ai_family = net->srv->ai_family;
+ new_info->ai_socktype = net->srv->ai_socktype;
+ new_info->ai_protocol = net->srv->ai_protocol;
+ new_info->ai_addrlen = local_addr_len;
+
+ if (getsockname(net->sockfd, new_info->ai_addr, &local_addr_len) == -1) {
+ WARN("can't get local address");
+ free(new_info);
+ return KNOT_NET_ESOCKET;
+ }
+
+ if (net->local_info != NULL) {
+ if (net->local == NULL) {
+ free(net->local_info);
+ } else {
+ freeaddrinfo(net->local_info);
+ }
+ }
+
+ net->local_info = new_info;
+
+ get_addr_str((struct sockaddr_storage *)net->local_info->ai_addr,
+ net->socktype, &net->local_str);
+
+ return KNOT_EOK;
+}
+
+int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len)
+{
+ if (net == NULL || buf == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+#ifdef ENABLE_QUIC
+ // Send data over QUIC.
+ if (net->quic.params.enable) {
+ int ret = quic_send_dns_query((quic_ctx_t *)&net->quic,
+ net->sockfd, net->srv, buf, buf_len);
+ if (ret != KNOT_EOK) {
+ WARN("can't send query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+ } else
+#endif
+ // Send data over UDP.
+ if (net->socktype == SOCK_DGRAM) {
+ char proxy_buf[PROXYV2_HEADER_MAXLEN];
+ struct iovec iov[2] = {
+ { .iov_base = proxy_buf, .iov_len = 0 },
+ { .iov_base = (void *)buf, .iov_len = buf_len }
+ };
+
+ struct msghdr msg = {
+ .msg_name = net->srv->ai_addr,
+ .msg_namelen = net->srv->ai_addrlen,
+ .msg_iov = &iov[1],
+ .msg_iovlen = 1
+ };
+
+ if (net->proxy.src != NULL && net->proxy.src->sa_family != 0) {
+ int ret = proxyv2_write_header(proxy_buf, sizeof(proxy_buf),
+ SOCK_DGRAM, net->proxy.src,
+ net->proxy.dst);
+ if (ret < 0) {
+ WARN("can't send proxied query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+ iov[0].iov_len = ret;
+ msg.msg_iov--;
+ msg.msg_iovlen++;
+ }
+
+ ssize_t total = iov[0].iov_len + iov[1].iov_len;
+
+ if (sendmsg(net->sockfd, &msg, 0) != total) {
+ WARN("can't send query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+#ifdef LIBNGHTTP2
+ // Send data over HTTPS
+ } else if (net->https.params.enable) {
+ int ret = https_send_dns_query((https_ctx_t *)&net->https, buf, buf_len);
+ if (ret != KNOT_EOK) {
+ WARN("can't send query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+#endif //LIBNGHTTP2
+ // Send data over TLS.
+ } else if (net->tls.params != NULL) {
+ int ret = tls_ctx_send((tls_ctx_t *)&net->tls, buf, buf_len);
+ if (ret != KNOT_EOK) {
+ WARN("can't send query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+ // Send data over TCP.
+ } else {
+ bool fastopen = net->flags & NET_FLAGS_FASTOPEN;
+
+ char proxy_buf[PROXYV2_HEADER_MAXLEN];
+ uint16_t pktsize = htons(buf_len); // Leading packet length bytes.
+ struct iovec iov[3] = {
+ { .iov_base = proxy_buf, .iov_len = 0 },
+ { .iov_base = &pktsize, .iov_len = sizeof(pktsize) },
+ { .iov_base = (void *)buf, .iov_len = buf_len }
+ };
+
+ struct msghdr msg = {
+ .msg_name = net->srv->ai_addr,
+ .msg_namelen = net->srv->ai_addrlen,
+ .msg_iov = &iov[1],
+ .msg_iovlen = 2
+ };
+
+ if (net->srv->ai_addr->sa_family == AF_UNIX) {
+ msg.msg_name = NULL;
+ }
+
+ if (net->proxy.src != NULL && net->proxy.src->sa_family != 0) {
+ int ret = proxyv2_write_header(proxy_buf, sizeof(proxy_buf),
+ SOCK_STREAM, net->proxy.src,
+ net->proxy.dst);
+ if (ret < 0) {
+ WARN("can't send proxied query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+ iov[0].iov_len = ret;
+ msg.msg_iov--;
+ msg.msg_iovlen++;
+ }
+
+ ssize_t total = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
+
+ int ret = 0;
+ if (fastopen) {
+ ret = fastopen_send(net->sockfd, &msg, net->wait);
+ } else {
+ ret = sendmsg(net->sockfd, &msg, 0);
+ }
+ if (ret != total) {
+ WARN("can't send query to %s", net->remote_str);
+ return KNOT_NET_ESEND;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len)
+{
+ if (net == NULL || buf == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Initialize poll descriptor structure.
+ struct pollfd pfd = {
+ .fd = net->sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+#ifdef ENABLE_QUIC
+ // Receive data over QUIC.
+ if (net->quic.params.enable) {
+ int ret = quic_recv_dns_response((quic_ctx_t *)&net->quic, buf,
+ buf_len, net->srv);
+ if (ret < 0) {
+ WARN("can't receive reply from %s", net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+ return ret;
+ } else
+#endif
+ // Receive data over UDP.
+ if (net->socktype == SOCK_DGRAM) {
+ struct sockaddr_storage from;
+ memset(&from, '\0', sizeof(from));
+
+ // Receive replies unless correct reply or timeout.
+ while (true) {
+ socklen_t from_len = sizeof(from);
+
+ // Wait for datagram data.
+ if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+ WARN("response timeout for %s",
+ net->remote_str);
+ return KNOT_NET_ETIMEOUT;
+ }
+
+ // Receive whole UDP datagram.
+ ssize_t ret = recvfrom(net->sockfd, buf, buf_len, 0,
+ (struct sockaddr *)&from, &from_len);
+ if (ret <= 0) {
+ WARN("can't receive reply from %s",
+ net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+
+ // Compare reply address with the remote one.
+ if (from_len > sizeof(from) ||
+ memcmp(&from, net->srv->ai_addr, from_len) != 0) {
+ char *src = NULL;
+ get_addr_str(&from, net->socktype, &src);
+ WARN("unexpected reply source %s", src);
+ free(src);
+ continue;
+ }
+
+ return ret;
+ }
+#ifdef LIBNGHTTP2
+ // Receive data over HTTPS.
+ } else if (net->https.params.enable) {
+ int ret = https_recv_dns_response((https_ctx_t *)&net->https, buf, buf_len);
+ if (ret < 0) {
+ WARN("can't receive reply from %s", net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+ return ret;
+#endif //LIBNGHTTP2
+ // Receive data over TLS.
+ } else if (net->tls.params != NULL) {
+ int ret = tls_ctx_receive((tls_ctx_t *)&net->tls, buf, buf_len);
+ if (ret < 0) {
+ WARN("can't receive reply from %s", net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+ return ret;
+ // Receive data over TCP.
+ } else {
+ uint32_t total = 0;
+
+ uint16_t msg_len = 0;
+ // Receive TCP message header.
+ while (total < sizeof(msg_len)) {
+ if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+ WARN("response timeout for %s",
+ net->remote_str);
+ return KNOT_NET_ETIMEOUT;
+ }
+
+ // Receive piece of message.
+ ssize_t ret = recv(net->sockfd, (uint8_t *)&msg_len + total,
+ sizeof(msg_len) - total, 0);
+ if (ret <= 0) {
+ WARN("can't receive reply from %s",
+ net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+ total += ret;
+ }
+
+ // Convert number to host format.
+ msg_len = ntohs(msg_len);
+ if (msg_len > buf_len) {
+ return KNOT_ESPACE;
+ }
+
+ total = 0;
+
+ // Receive whole answer message by parts.
+ while (total < msg_len) {
+ if (poll(&pfd, 1, 1000 * net->wait) != 1) {
+ WARN("response timeout for %s",
+ net->remote_str);
+ return KNOT_NET_ETIMEOUT;
+ }
+
+ // Receive piece of message.
+ ssize_t ret = recv(net->sockfd, buf + total, msg_len - total, 0);
+ if (ret <= 0) {
+ WARN("can't receive reply from %s",
+ net->remote_str);
+ return KNOT_NET_ERECV;
+ }
+ total += ret;
+ }
+
+ return total;
+ }
+
+ return KNOT_NET_ERECV;
+}
+
+void net_close(net_t *net)
+{
+ if (net == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+#ifdef ENABLE_QUIC
+ if (net->quic.params.enable) {
+ quic_ctx_close(&net->quic);
+ }
+#endif
+ tls_ctx_close(&net->tls);
+ close(net->sockfd);
+ net->sockfd = -1;
+}
+
+void net_clean(net_t *net)
+{
+ if (net == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ free(net->local_str);
+ free(net->remote_str);
+ net->local_str = NULL;
+ net->remote_str = NULL;
+
+ if (net->local_info != NULL) {
+ if (net->local == NULL) {
+ free(net->local_info);
+ } else {
+ freeaddrinfo(net->local_info);
+ }
+ net->local_info = NULL;
+ }
+
+ if (net->remote_info != NULL) {
+ if (net->remote_info->ai_addr->sa_family == AF_UNIX) {
+ free(net->remote_info->ai_addr);
+ free(net->remote_info);
+ } else {
+ freeaddrinfo(net->remote_info);
+ }
+ net->remote_info = NULL;
+ }
+
+#ifdef LIBNGHTTP2
+ https_ctx_deinit(&net->https);
+#endif
+#ifdef ENABLE_QUIC
+ quic_ctx_deinit(&net->quic);
+#endif
+ tls_ctx_deinit(&net->tls);
+}
diff --git a/src/utils/common/netio.h b/src/utils/common/netio.h
new file mode 100644
index 0000000..824b7a6
--- /dev/null
+++ b/src/utils/common/netio.h
@@ -0,0 +1,239 @@
+/* Copyright (C) 2023 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/>.
+ */
+
+#pragma once
+
+#include <netdb.h>
+#include <stdint.h>
+#include <sys/socket.h>
+
+#include "utils/common/https.h"
+#include "utils/common/params.h"
+#include "utils/common/quic.h"
+#include "utils/common/tls.h"
+
+/*! \brief Structure containing server information. */
+typedef struct {
+ /*! List node (for list container). */
+ node_t n;
+ /*! Name or address of the server. */
+ char *name;
+ /*! Name or number of the service. */
+ char *service;
+} srv_info_t;
+
+typedef enum {
+ NET_FLAGS_NONE = 0,
+ NET_FLAGS_FASTOPEN = 1 << 0,
+} net_flags_t;
+
+typedef struct {
+ /*! Socket descriptor. */
+ int sockfd;
+
+ /*! IP protocol type. */
+ int iptype;
+ /*! Socket type. */
+ int socktype;
+ /*! Timeout for all network operations. */
+ int wait;
+ /*! Connection flags. */
+ net_flags_t flags;
+
+ /*! Local interface parameters. */
+ const srv_info_t *local;
+ /*! Remote server parameters. */
+ const srv_info_t *remote;
+
+ /*! Local description string (used for logging). */
+ char *local_str;
+ /*! Remote description string (used for logging). */
+ char *remote_str;
+
+ /*! Output from getaddrinfo for remote server. If the server is
+ * specified using domain name, this structure may contain more
+ * results.
+ */
+ struct addrinfo *remote_info;
+ /*! Currently used result from remote_info. */
+ struct addrinfo *srv;
+ /*! Output from getaddrinfo for local address. Only first result is
+ * used.
+ */
+ struct addrinfo *local_info;
+
+ /*! TLS context. */
+ tls_ctx_t tls;
+#ifdef LIBNGHTTP2
+ /*! HTTPS context. */
+ https_ctx_t https;
+#endif
+#ifdef ENABLE_QUIC
+ /*! QUIC context. */
+ quic_ctx_t quic;
+#endif
+ struct {
+ const struct sockaddr *src;
+ const struct sockaddr *dst;
+ } proxy;
+} net_t;
+
+/*!
+ * \brief Creates and fills server structure.
+ *
+ * \param name Address or host name.
+ * \param service Port number or service name.
+ *
+ * \retval server if success.
+ * \retval NULL if error.
+ */
+srv_info_t *srv_info_create(const char *name, const char *service);
+
+/*!
+ * \brief Destroys server structure.
+ *
+ * \param server Server structure to destroy.
+ */
+void srv_info_free(srv_info_t *server);
+
+/*!
+ * \brief Translates enum IP version type to int version.
+ *
+ * \param ip IP version to convert.
+ * \param server Server structure.
+ *
+ * \retval AF_INET, AF_INET6, AF_UNIX, or AF_UNSPEC.
+ */
+int get_iptype(const ip_t ip, const srv_info_t *server);
+
+/*!
+ * \brief Translates enum IP protocol type to int version in context to the
+ * current DNS query type.
+ *
+ * \param proto IP protocol type to convert.
+ * \param type DNS query type number.
+ *
+ * \retval SOCK_STREAM or SOCK_DGRAM.
+ */
+int get_socktype(const protocol_t proto, const uint16_t type);
+
+/*!
+ * \brief Translates int socket type to the common string one.
+ *
+ * \param socktype Socket type (SOCK_STREAM or SOCK_DGRAM).
+ *
+ * \retval "TCP" or "UDP".
+ */
+const char *get_sockname(const int socktype);
+
+/*!
+ * \brief Translates int socket type to the common string one.
+ *
+ * \param ss Socket address storage.
+ * \param socktype Socket type (SOCK_STREAM or SOCK_DGRAM).
+ * \param dst Output string.
+ */
+void get_addr_str(const struct sockaddr_storage *ss,
+ const int socktype,
+ char **dst);
+
+/*!
+ * \brief Initializes network structure and resolves local and remote addresses.
+ *
+ * \param local Local address and service description.
+ * \param remote Remote address and service description.
+ * \param iptype IP version.
+ * \param socktype Socket type.
+ * \param wait Network timeout interval.
+ * \param tls_params TLS parameters.
+ * \param https_params HTTPS parameters.
+ * \param flags Connection flags.
+ * \param net Network structure to initialize.
+ *
+ * \retval KNOT_EOK if success.
+ * \retval errcode if error.
+ */
+int net_init(const srv_info_t *local,
+ const srv_info_t *remote,
+ const int iptype,
+ const int socktype,
+ const int wait,
+ const net_flags_t flags,
+ const tls_params_t *tls_params,
+ const https_params_t *https_params,
+ const quic_params_t *quic_params,
+ const struct sockaddr *proxy_src,
+ const struct sockaddr *proxy_dst,
+ net_t *net);
+
+/*!
+ * \brief Creates socket and connects (if TCP) to remote address specified
+ * by net->srv.
+ *
+ * \param net Connection parameters.
+ *
+ * \retval KNOT_EOK if success.
+ * \retval errcode if error.
+ */
+int net_connect(net_t *net);
+
+/*!
+ * \brief Fills in local address information.
+ *
+ * \param net Connection parameters.
+ *
+ * \retval KNOT_EOK if success.
+ * \retval errcode if error.
+ */
+int net_set_local_info(net_t *net);
+
+/*!
+ * \brief Sends data to connected remote server.
+ *
+ * \param net Connection parameters.
+ * \param buf Data to send.
+ * \param buf_len Length of the data to send.
+ *
+ * \retval KNOT_EOK if success.
+ * \retval errcode if error.
+ */
+int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len);
+
+/*!
+ * \brief Receives data from connected remote server.
+ *
+ * \param net Connection parameters.
+ * \param buf Buffer for incoming data.
+ * \param buf_len Length of the buffer.
+ *
+ * \retval >=0 length of successfully received data.
+ * \retval errcode if error.
+ */
+int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len);
+
+/*!
+ * \brief Closes current network connection.
+ *
+ * \param net Connection parameters.
+ */
+void net_close(net_t *net);
+
+/*!
+ * \brief Cleans up network structure.
+ *
+ * \param net Connection parameters.
+ */
+void net_clean(net_t *net);
diff --git a/src/utils/common/params.c b/src/utils/common/params.c
new file mode 100644
index 0000000..4db4b9e
--- /dev/null
+++ b/src/utils/common/params.c
@@ -0,0 +1,343 @@
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#ifdef LIBIDN
+#include LIBIDN_HEADER
+#endif
+
+#include "utils/common/params.h"
+#include "utils/common/msg.h"
+#include "utils/common/resolv.h"
+#include "utils/common/token.h"
+#include "libknot/libknot.h"
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/strtonum.h"
+
+#define IPV4_REVERSE_DOMAIN "in-addr.arpa."
+#define IPV6_REVERSE_DOMAIN "ip6.arpa."
+
+char *name_from_idn(const char *idn_name) {
+#ifdef LIBIDN
+ char *name = NULL;
+
+ int rc = idna_to_ascii_lz(idn_name, &name, 0);
+ if (rc != IDNA_SUCCESS) {
+ ERR("IDNA (%s)", idna_strerror(rc));
+ return NULL;
+ }
+
+ return name;
+#endif
+ return strdup(idn_name);
+}
+
+void name_to_idn(char **name) {
+#ifdef LIBIDN
+ char *idn_name = NULL;
+
+ int rc = idna_to_unicode_8zlz(*name, &idn_name, 0);
+ if (rc != IDNA_SUCCESS) {
+ return;
+ }
+
+ free(*name);
+ *name = idn_name;
+#endif
+ return;
+}
+
+/*!
+ * \brief Checks if string is a prefix of reference string.
+ *
+ * \param pref Prefix string.
+ * \param pref_len Prefix length.
+ * \param str Reference string (must have trailing zero).
+ *
+ * \retval -1 \a pref is not a prefix of \a str.
+ * \retval 0<= number of chars after prefix \a pref in \a str.
+ */
+static int cmp_prefix(const char *pref, const size_t pref_len,
+ const char *str)
+{
+ size_t i = 0;
+ while (1) {
+ // Different characters => NOT prefix.
+ if (pref[i] != str[i]) {
+ return -1;
+ }
+
+ i++;
+
+ // Pref IS a prefix of pref.
+ if (i == pref_len) {
+ size_t rest = 0;
+ while (str[i + rest] != '\0') {
+ rest++;
+ }
+ return rest;
+ // Pref is longer then ref => NOT prefix.
+ } else if (str[i] == '\0') {
+ return -1;
+ }
+ }
+}
+
+int best_param(const char *str, const size_t str_len, const param_t *tbl,
+ bool *unique)
+{
+ if (str == NULL || str_len == 0 || tbl == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ int best_pos = -1;
+ int best_match = INT_MAX;
+ size_t matches = 0;
+ for (int i = 0; tbl[i].name != NULL; i++) {
+ int ret = cmp_prefix(str, str_len, tbl[i].name);
+ switch (ret) {
+ case -1:
+ continue;
+ case 0:
+ *unique = true;
+ return i;
+ default:
+ if (ret < best_match) {
+ best_pos = i;
+ best_match = ret;
+ }
+ matches++;
+ }
+ }
+
+ switch (matches) {
+ case 0:
+ return KNOT_ENOTSUP;
+ case 1:
+ *unique = true;
+ return best_pos;
+ default:
+ *unique = false;
+ return best_pos;
+ }
+}
+
+char *get_reverse_name(const char *name)
+{
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ int ret;
+ char buf[128] = "\0";
+
+ if (name == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Check name for IPv4 address, IPv6 address or other.
+ if (inet_pton(AF_INET, name, &addr4) == 1) {
+ uint32_t num = ntohl(addr4.s_addr);
+
+ // Create IPv4 reverse FQD name.
+ ret = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.%s",
+ (num >> 0) & 0xFF, (num >> 8) & 0xFF,
+ (num >> 16) & 0xFF, (num >> 24) & 0xFF,
+ IPV4_REVERSE_DOMAIN);
+ if (ret < 0 || (size_t)ret >= sizeof(buf)) {
+ return NULL;
+ }
+
+ return strdup(buf);
+ } else if (inet_pton(AF_INET6, name, &addr6) == 1) {
+ char *pos = buf;
+ size_t len = sizeof(buf);
+ uint8_t left, right;
+
+ // Create IPv6 reverse name.
+ for (int i = 15; i >= 0; i--) {
+ left = ((addr6.s6_addr)[i] & 0xF0) >> 4;
+ right = (addr6.s6_addr)[i] & 0x0F;
+
+ ret = snprintf(pos, len, "%x.%x.", right, left);
+ if (ret < 0 || (size_t)ret >= len) {
+ return NULL;
+ }
+
+ pos += ret;
+ len -= ret;
+ }
+
+ // Add IPv6 reverse domain.
+ ret = snprintf(pos, len, "%s", IPV6_REVERSE_DOMAIN);
+ if (ret < 0 || (size_t)ret >= len) {
+ return NULL;
+ }
+
+ return strdup(buf);
+ } else {
+ return NULL;
+ }
+}
+
+char *get_fqd_name(const char *name)
+{
+ char *fqd_name = NULL;
+
+ if (name == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ size_t name_len = strlen(name);
+
+ // If the name is FQDN, make a copy.
+ if (name[name_len - 1] == '.') {
+ fqd_name = strdup(name);
+ // Else make a copy and append a trailing dot.
+ } else {
+ size_t fqd_name_size = name_len + 2;
+ fqd_name = malloc(fqd_name_size);
+ if (fqd_name != NULL) {
+ strlcpy(fqd_name, name, fqd_name_size);
+ fqd_name[name_len] = '.';
+ fqd_name[name_len + 1] = 0;
+ }
+ }
+
+ return fqd_name;
+}
+
+int params_parse_class(const char *value, uint16_t *rclass)
+{
+ if (value == NULL || rclass == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ if (knot_rrclass_from_string(value, rclass) == 0) {
+ return KNOT_EOK;
+ } else {
+ return KNOT_EINVAL;
+ }
+}
+
+int params_parse_type(const char *value, uint16_t *rtype, int64_t *serial,
+ bool *notify)
+{
+ if (value == NULL || rtype == NULL || serial == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Find and parse type name.
+ size_t param_pos = strcspn(value, "=");
+ char *type_char = strndup(value, param_pos);
+
+ if (knot_rrtype_from_string(type_char, rtype) != 0) {
+ size_t cmp_len = MAX(strlen("NOTIFY"), param_pos);
+ if (strncasecmp(type_char, "NOTIFY", cmp_len) == 0) {
+ *rtype = KNOT_RRTYPE_SOA;
+ *notify = true;
+ } else {
+ free(type_char);
+ return KNOT_EINVAL;
+ }
+ } else {
+ *notify = false;
+ }
+
+ free(type_char);
+
+ // Parse additional parameter.
+ if (param_pos == strlen(value)) {
+ // IXFR requires serial parameter.
+ if (*rtype == KNOT_RRTYPE_IXFR) {
+ DBG("SOA serial is required for IXFR query");
+ return KNOT_EINVAL;
+ } else {
+ *serial = -1;
+ }
+ } else {
+ // Additional parameter is accepted for IXFR or NOTIFY.
+ if (*rtype == KNOT_RRTYPE_IXFR || *notify) {
+ const char *param_str = value + 1 + param_pos;
+ char *end;
+
+ // Convert string to serial.
+ unsigned long long num = strtoull(param_str, &end, 10);
+
+ // Check for bad serial string.
+ if (end == param_str || *end != '\0' || num > UINT32_MAX) {
+ DBG("bad SOA serial '%s'", param_str);
+ return KNOT_EINVAL;
+ }
+
+ *serial = num;
+ } else {
+ DBG("unsupported parameter '%s'", value);
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int params_parse_server(const char *value, list_t *servers, const char *def_port)
+{
+ if (value == NULL || servers == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Add specified nameserver.
+ srv_info_t *server = parse_nameserver(value, def_port);
+ if (server == NULL) {
+ return KNOT_EINVAL;
+ }
+ add_tail(servers, (node_t *)server);
+
+ return KNOT_EOK;
+}
+
+int params_parse_wait(const char *value, int32_t *dst)
+{
+ if (value == NULL || dst == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ uint32_t num = 0;
+ int ret = str_to_u32(value, &num);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (num < 1 || num > INT32_MAX / 1000) {
+ num = INT32_MAX / 1000;
+ }
+
+ *dst = num;
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/common/params.h b/src/utils/common/params.h
new file mode 100644
index 0000000..d70d3e0
--- /dev/null
+++ b/src/utils/common/params.h
@@ -0,0 +1,168 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "libknot/libknot.h"
+#include "contrib/ucw/lists.h"
+
+#define DEFAULT_IPV4_NAME "127.0.0.1"
+#define DEFAULT_IPV6_NAME "::1"
+#define DEFAULT_DNS_PORT "53"
+#define DEFAULT_DNS_HTTPS_PORT "443"
+#define DEFAULT_DNS_QUIC_PORT "853"
+#define DEFAULT_DNS_TLS_PORT "853"
+#define DEFAULT_UDP_SIZE 512
+#define DEFAULT_EDNS_SIZE 4096
+#define MAX_PACKET_SIZE 65535
+
+#define SEP_CHARS "\n\t "
+
+/*! \brief Variants of IP protocol. */
+typedef enum {
+ IP_ALL,
+ IP_4,
+ IP_6
+} ip_t;
+
+/*! \brief Variants of transport protocol. */
+typedef enum {
+ PROTO_ALL,
+ PROTO_TCP,
+ PROTO_UDP
+} protocol_t;
+
+/*! \brief Variants of output type. */
+typedef enum {
+ /*!< Verbose output (same for host and dig). */
+ FORMAT_FULL,
+ /*!< Short dig output. */
+ FORMAT_DIG,
+ /*!< Brief host output. */
+ FORMAT_HOST,
+ /*!< Brief nsupdate output. */
+ FORMAT_NSUPDATE,
+ /*!< Machine readable JSON format (RFC 8427). */
+ FORMAT_JSON
+} format_t;
+
+/*! \brief Text output settings. */
+typedef struct {
+ /*!< Output format. */
+ format_t format;
+
+ /*!< Style of rrset dump. */
+ knot_dump_style_t style;
+
+ /*!< Show query packet. */
+ bool show_query;
+ /*!< Show header info. */
+ bool show_header;
+ /*!< Show section name. */
+ bool show_section;
+ /*!< Show EDNS pseudosection. */
+ bool show_edns;
+ /*!< Show unknown EDNS options in printable format. */
+ bool show_edns_opt_text;
+ /*!< Show QUERY/ZONE section. */
+ bool show_question;
+ /*!< Show ANSWER/PREREQ section. */
+ bool show_answer;
+ /*!< Show UPDATE/AUTHORITY section. */
+ bool show_authority;
+ /*!< Show ADDITIONAL section. */
+ bool show_additional;
+ /*!< Show TSIG pseudosection. */
+ bool show_tsig;
+ /*!< Show footer info. */
+ bool show_footer;
+
+ /*!< KHOST - Hide CNAME record in answer (duplicity reduction). */
+ bool hide_cname;
+} style_t;
+
+/*! \brief Parameter handler. */
+typedef int (*param_handle_f)(const char *arg, void *params);
+
+/*! \brief Parameter argument type. */
+typedef enum {
+ ARG_NONE,
+ ARG_REQUIRED,
+ ARG_OPTIONAL
+} arg_t;
+
+/*! \brief Parameter specification. */
+typedef struct {
+ const char *name;
+ arg_t arg;
+ param_handle_f handler;
+} param_t;
+
+inline static void print_version(const char *program_name)
+{
+ printf("%s (Knot DNS), version %s\n", program_name, PACKAGE_VERSION);
+}
+
+/*!
+ * \brief Transforms localized IDN string to ASCII punycode.
+ *
+ * \param idn_name IDN name to transform.
+ *
+ * \retval NULL if transformation fails.
+ * \retval string if ok.
+ */
+char *name_from_idn(const char *idn_name);
+
+/*!
+ * \brief Transforms ASCII punycode to localized IDN string.
+ *
+ * If an error occurs or IDN support is missing, this function does nothing.
+ *
+ * \param name ASCII name to transform and replace with IDN name.
+ */
+void name_to_idn(char **name);
+
+/*!
+ * \brief Find the best parameter match in table based on prefix equality.
+ *
+ * \param str Parameter name to look up.
+ * \param str_len Parameter name length.
+ * \param tbl Parameter table.
+ * \param unique Indication if output is unique result.
+ *
+ * \retval >=0 looked up parameter position in \a tbl.
+ * \retval err if error.
+ */
+int best_param(const char *str, const size_t str_len, const param_t *tbl,
+ bool *unique);
+
+char *get_reverse_name(const char *name);
+
+char *get_fqd_name(const char *name);
+
+int params_parse_class(const char *value, uint16_t *rclass);
+
+int params_parse_type(const char *value, uint16_t *rtype, int64_t *serial,
+ bool *notify);
+
+int params_parse_server(const char *value, list_t *servers, const char *def_port);
+
+int params_parse_wait(const char *value, int32_t *dst);
diff --git a/src/utils/common/quic.c b/src/utils/common/quic.c
new file mode 100644
index 0000000..f73b8c4
--- /dev/null
+++ b/src/utils/common/quic.c
@@ -0,0 +1,887 @@
+/* Copyright (C) 2022 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 <stddef.h>
+
+#include "libknot/errcode.h"
+#include "utils/common/quic.h"
+#include "utils/common/msg.h"
+
+int quic_params_copy(quic_params_t *dst, const quic_params_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ dst->enable = src->enable;
+
+ return KNOT_EOK;
+}
+
+void quic_params_clean(quic_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ params->enable = false;
+}
+
+#ifdef ENABLE_QUIC
+
+#include <assert.h>
+#include <poll.h>
+#include <gnutls/crypto.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+#include "libknot/xdp/tcp_iobuf.h"
+#include "utils/common/params.h"
+
+#define quic_ceil_duration_to_ms(x) (((x) + NGTCP2_MILLISECONDS - 1) / NGTCP2_MILLISECONDS)
+#define quic_get_encryption_level(level) ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(level)
+#define quic_send(ctx, sockfd, family) quic_send_data(ctx, sockfd, family, NULL, 0)
+#define quic_timeout(ts, wait) (((ts) + NGTCP2_SECONDS * (wait)) <= quic_timestamp())
+
+const gnutls_datum_t doq_alpn[] = {
+ {
+ .data = (unsigned char *)"doq",
+ .size = 3
+ },{
+ .data = (unsigned char *)"doq-i12",
+ .size = 7
+ },{
+ .data = (unsigned char *)"doq-i11",
+ .size = 7
+ },{
+ .data = (unsigned char *)"doq-i03",
+ .size = 7
+ }
+};
+
+#define set_application_error(ctx, error_code, reason, reason_len) \
+ ngtcp2_connection_close_error_set_application_error(&(ctx)->last_err, \
+ error_code, reason, reason_len)
+
+#define set_transport_error(ctx, error_code, reason, reason_len) \
+ ngtcp2_connection_close_error_set_transport_error(&(ctx)->last_err, \
+ error_code, reason, reason_len)
+
+static int recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags,
+ int64_t stream_id, uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data, void *stream_user_data)
+{
+ (void)conn;
+ (void)flags;
+ (void)offset;
+ (void)stream_user_data;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+
+ if (stream_id != ctx->stream.id) {
+ return 0;
+ }
+
+ struct iovec in = {
+ .iov_base = (uint8_t *)data,
+ .iov_len = datalen
+ };
+
+ int ret = knot_tcp_inbuf_update(&ctx->stream.in_buffer, in,
+ &ctx->stream.in_parsed, &ctx->stream.in_parsed_size,
+ &ctx->stream.in_parsed_total);
+ if (ret != KNOT_EOK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ ctx->idle_ts = quic_timestamp();
+ ctx->stream.in_parsed_it = 0;
+ return 0;
+}
+
+static int stream_open_cb(ngtcp2_conn *conn, int64_t stream_id,
+ void *user_data)
+{
+ (void)conn;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ set_application_error(ctx, DOQ_PROTOCOL_ERROR, NULL, 0);
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+}
+
+static int acked_stream_data_offset_cb(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t offset, uint64_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ (void)conn;
+ (void)offset;
+ (void)stream_user_data;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ if (ctx->stream.id == stream_id) {
+ ctx->stream.out_ack -= datalen;
+ }
+ return KNOT_EOK;
+}
+
+static int stream_close_cb(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
+ uint64_t app_error_code, void *user_data, void *stream_user_data)
+{
+ (void)conn;
+ (void)flags;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ if (ctx && stream_id == ctx->stream.id) {
+ ctx->stream.id = -1;
+ }
+ return KNOT_EOK;
+}
+
+static int quic_open_bidi_stream(quic_ctx_t *ctx)
+{
+ if (ctx->stream.id != -1) {
+ return KNOT_EOK;
+ }
+
+ int ret = ngtcp2_conn_open_bidi_stream(ctx->conn, &ctx->stream.id, NULL);
+ if (ret) {
+ return KNOT_ERROR;
+ }
+
+ ctx->stream.resets = 3;
+
+ return KNOT_EOK;
+}
+
+static int extend_max_bidi_streams_cb(ngtcp2_conn *conn, uint64_t max_streams,
+ void *user_data)
+{
+ (void)conn;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ if(max_streams > 0) {
+ int ret = quic_open_bidi_stream(ctx);
+ if (ret != KNOT_EOK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ }
+ return 0;
+}
+
+static void rand_cb(uint8_t *dest, size_t destlen,
+ const ngtcp2_rand_ctx *rand_ctx)
+{
+ (void)rand_ctx;
+
+ dnssec_random_buffer(dest, destlen);
+}
+
+static int get_new_connection_id_cb(ngtcp2_conn *conn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen, void *user_data)
+{
+ (void)conn;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+
+ if (dnssec_random_buffer(cid->data, cidlen) != DNSSEC_EOK) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ cid->datalen = cidlen;
+
+ if (ngtcp2_crypto_generate_stateless_reset_token(token, ctx->secret,
+ sizeof(ctx->secret), cid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int stream_reset_cb(ngtcp2_conn *conn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ if (ctx->stream.id == stream_id) {
+ if (--ctx->stream.resets <= 0) {
+ //TODO test
+ set_transport_error(ctx, NGTCP2_PROTOCOL_VIOLATION, NULL, 0);
+ quic_ctx_close(ctx);
+ }
+ }
+
+ return 0;
+}
+
+static int handshake_confirmed_cb(ngtcp2_conn *conn, void *user_data)
+{
+ (void)conn;
+
+ quic_ctx_t *ctx = (quic_ctx_t *)user_data;
+ ctx->state = CONNECTED;
+ return 0;
+}
+
+static const ngtcp2_callbacks quic_client_callbacks = {
+ ngtcp2_crypto_client_initial_cb,
+ NULL, /* recv_client_initial */
+ ngtcp2_crypto_recv_crypto_data_cb,
+ NULL, /* handshake_completed */
+ NULL, /* recv_version_negotiation */
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ recv_stream_data_cb,
+ acked_stream_data_offset_cb,
+ stream_open_cb,
+ stream_close_cb,
+ NULL, /* recv_stateless_reset */
+ ngtcp2_crypto_recv_retry_cb,
+ extend_max_bidi_streams_cb,
+ NULL, /* extend_max_local_streams_uni */
+ rand_cb,
+ get_new_connection_id_cb,
+ NULL, /* remove_connection_id */
+ ngtcp2_crypto_update_key_cb,
+ NULL, /* path_validation */
+ NULL, /* select_preferred_address */
+ stream_reset_cb,
+ NULL, /* extend_max_remote_streams_bidi */
+ NULL, /* extend_max_remote_streams_uni */
+ NULL, /* extend_max_stream_data */
+ NULL, /* dcid_status */
+ handshake_confirmed_cb,
+ NULL, /* recv_new_token */
+ ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ NULL, /* recv_datagram */
+ NULL, /* ack_datagram */
+ NULL, /* lost_datagram */
+ ngtcp2_crypto_get_path_challenge_data_cb,
+ NULL, /* stream_stop_sending */
+ ngtcp2_crypto_version_negotiation_cb,
+ NULL, /* recv_rx_key */
+ NULL /* recv_tx_key */
+};
+
+static int hook_func(gnutls_session_t session, unsigned int htype,
+ unsigned when, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ (void)session;
+ (void)htype;
+ (void)when;
+ (void)incoming;
+ (void)msg;
+
+ return GNUTLS_E_SUCCESS;
+}
+
+static int quic_send_data(quic_ctx_t *ctx, int sockfd, int family,
+ ngtcp2_vec *datav, size_t datavlen)
+{
+ uint8_t enc_buf[MAX_PACKET_SIZE];
+ struct iovec msg_iov = {
+ .iov_base = enc_buf,
+ .iov_len = 0
+ };
+ struct msghdr msg = {
+ .msg_iov = &msg_iov,
+ .msg_iovlen = 1
+ };
+ uint64_t ts = quic_timestamp();
+ size_t tb_send = 0;
+ for (int i = 0; i < datavlen; ++i) {
+ tb_send += datav[i].len;
+ }
+
+ while(1) {
+ int64_t stream = -1;
+ uint32_t flags = NGTCP2_WRITE_STREAM_FLAG_NONE;
+ if (datavlen != 0) {
+ flags = NGTCP2_WRITE_STREAM_FLAG_FIN;
+ stream = ctx->stream.id;
+ }
+ ngtcp2_ssize send_datalen = 0;
+ ngtcp2_ssize nwrite = ngtcp2_conn_writev_stream(ctx->conn,
+ (ngtcp2_path *)ngtcp2_conn_get_path(ctx->conn),
+ &ctx->pi, enc_buf, sizeof(enc_buf),
+ &send_datalen, flags, stream, datav, datavlen,
+ ts);
+ if (send_datalen == tb_send) {
+ ctx->stream.out_ack = send_datalen;
+ datav = NULL;
+ datavlen = 0;
+ }
+ if (nwrite < 0) {
+ switch(nwrite) {
+ case NGTCP2_ERR_WRITE_MORE:
+ assert(0);
+ continue;
+ case NGTCP2_ERR_STREAM_SHUT_WR:
+ ctx->stream.id = -1;
+ // [[ fallthrough ]]
+ default:
+ set_transport_error(ctx,
+ ngtcp2_err_infer_quic_transport_error_code(nwrite),
+ NULL, 0);
+ return KNOT_NET_ESEND;
+ }
+ } else if (nwrite == 0) {
+ ngtcp2_conn_update_pkt_tx_time(ctx->conn, ts);
+ return KNOT_EOK;
+ }
+
+ msg_iov.iov_len = (size_t)nwrite;
+
+ int ret = quic_set_enc(sockfd, family, ctx->pi.ecn);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (sendmsg(sockfd, &msg, 0) == -1) {
+ set_transport_error(ctx, NGTCP2_INTERNAL_ERROR, NULL,
+ 0);
+ return KNOT_NET_ESEND;
+ }
+ }
+ return KNOT_EOK;
+}
+
+static int quic_recv(quic_ctx_t *ctx, int sockfd)
+{
+ uint8_t enc_buf[MAX_PACKET_SIZE];
+ uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint8_t))];
+ struct sockaddr_in6 from = { 0 };
+ struct iovec msg_iov = {
+ .iov_base = enc_buf,
+ .iov_len = sizeof(enc_buf)
+ };
+ struct msghdr msg = {
+ .msg_name = &from,
+ .msg_namelen = sizeof(from),
+ .msg_iov = &msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = msg_ctrl,
+ .msg_controllen = sizeof(msg_ctrl),
+ .msg_flags = 0
+ };
+
+ ssize_t nwrite = recvmsg(sockfd, &msg, 0);
+ if (nwrite <= 0) {
+ return knot_map_errno();
+ }
+ ngtcp2_pkt_info *pi = &ctx->pi;
+ ctx->pi.ecn = quic_get_ecn(&msg, from.sin6_family);
+ if (errno == ENOENT) {
+ pi = NULL;
+ } else if (errno != 0) {
+ return knot_map_errno();
+ }
+
+ int ret = ngtcp2_conn_read_pkt(ctx->conn,
+ ngtcp2_conn_get_path(ctx->conn),
+ pi, enc_buf, nwrite,
+ quic_timestamp());
+ if (ret != 0) {
+ if (ret == NGTCP2_ERR_DROP_CONN) {
+ ctx->state = CLOSED;
+ } else if (ngtcp2_err_is_fatal(ret)) {
+ set_transport_error(ctx,
+ ngtcp2_err_infer_quic_transport_error_code(ret),
+ NULL, 0);
+ }
+ return KNOT_NET_ERECV;
+ }
+ return KNOT_EOK;
+}
+
+static int quic_respcpy(quic_ctx_t *ctx, uint8_t *buf, const size_t buf_len)
+{
+ assert(ctx && buf && buf_len > 0);
+ if (ctx->stream.in_parsed &&
+ ctx->stream.in_parsed_it < ctx->stream.in_parsed_size) {
+ struct iovec *it =
+ &ctx->stream.in_parsed[ctx->stream.in_parsed_it];
+ if (buf_len < it->iov_len) {
+ return KNOT_ENOMEM;
+ }
+ ctx->stream.in_parsed_it++;
+ size_t len = it->iov_len;
+ memcpy(buf, it->iov_base, len);
+ if (ctx->stream.in_parsed_it == ctx->stream.in_parsed_size) {
+ free(ctx->stream.in_parsed);
+ ctx->stream.in_parsed = NULL;
+ ctx->stream.in_parsed_size = 0;
+ }
+ return len;
+ }
+ return 0;
+}
+
+uint64_t quic_timestamp(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+ return 0;
+ }
+
+ return (uint64_t)ts.tv_sec * NGTCP2_SECONDS + (uint64_t)ts.tv_nsec;
+}
+
+int quic_generate_secret(uint8_t *buf, size_t buflen)
+{
+ assert(buf != NULL && buflen > 0 && buflen <= 32);
+ uint8_t rand[16], hash[32];
+ int ret = dnssec_random_buffer(rand, sizeof(rand));
+ if (ret != DNSSEC_EOK) {
+ return KNOT_ERROR;
+ }
+ ret = gnutls_hash_fast(GNUTLS_DIG_SHA256, rand, sizeof(rand), hash);
+ if (ret != 0) {
+ return KNOT_ERROR;
+ }
+ memcpy(buf, hash, buflen);
+ return KNOT_EOK;
+}
+
+int quic_set_enc(int sockfd, int family, uint32_t ecn)
+{
+ switch (family) {
+ case AF_INET:
+ if (setsockopt(sockfd, IPPROTO_IP, IP_TOS, &ecn,
+ (socklen_t)sizeof(ecn)) == -1) {
+ return knot_map_errno();
+ }
+ break;
+ case AF_INET6:
+ if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_TCLASS, &ecn,
+ (socklen_t)sizeof(ecn)) == -1) {
+ return knot_map_errno();
+ }
+ break;
+ default:
+ return KNOT_ENOTSUP;
+ }
+ return KNOT_EOK;
+}
+
+uint32_t quic_get_ecn(struct msghdr *msg, const int family)
+{
+ errno = 0;
+ switch (family) {
+ case AF_INET:
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IP &&
+ cmsg->cmsg_type == IP_TOS && cmsg->cmsg_len) {
+ return *(uint8_t *)CMSG_DATA(cmsg);
+ }
+ }
+ errno = ENOENT;
+ break;
+ case AF_INET6:
+ for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg;
+ cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_IPV6 &&
+ cmsg->cmsg_type == IPV6_TCLASS && cmsg->cmsg_len) {
+ return *(uint8_t *)CMSG_DATA(cmsg);
+ }
+ }
+ errno = ENOENT;
+ break;
+ default:
+ errno = ENOTSUP;
+ }
+
+ return 0;
+}
+
+static int verify_certificate(gnutls_session_t session)
+{
+ quic_ctx_t *ctx = gnutls_session_get_ptr(session);
+ return tls_certificate_verification(ctx->tls);
+}
+
+static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
+{
+ return ((quic_ctx_t *)conn_ref->user_data)->conn;
+}
+
+int quic_ctx_init(quic_ctx_t *ctx, tls_ctx_t *tls_ctx, const quic_params_t *params)
+{
+ if (ctx == NULL || tls_ctx == NULL || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ ctx->conn_ref = (ngtcp2_crypto_conn_ref) {
+ .get_conn = get_conn,
+ .user_data = ctx
+ };
+ ctx->params = *params;
+ ctx->tls = tls_ctx;
+ ctx->state = OPENING;
+ ctx->stream.id = -1;
+ set_application_error(ctx, DOQ_NO_ERROR, NULL, 0);
+ if (quic_generate_secret(ctx->secret, sizeof(ctx->secret)) != KNOT_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return KNOT_ENOMEM;
+ }
+
+ gnutls_certificate_set_verify_function(tls_ctx->credentials,
+ verify_certificate);
+
+ return KNOT_EOK;
+}
+
+int quic_ctx_connect(quic_ctx_t *ctx, int sockfd, struct addrinfo *dst_addr)
+{
+ if (connect(sockfd, (const struct sockaddr *)(dst_addr->ai_addr),
+ dst_addr->ai_addrlen) != 0) {
+ tls_ctx_deinit(ctx->tls);
+ return knot_map_errno();
+ }
+
+ ngtcp2_cid dcid, scid;
+ scid.datalen = NGTCP2_MAX_CIDLEN;
+ int ret = dnssec_random_buffer(scid.data, scid.datalen);
+ if (ret != DNSSEC_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return ret;
+ }
+ dcid.datalen = 18;
+ ret = dnssec_random_buffer(dcid.data, dcid.datalen);
+ if (ret != DNSSEC_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return ret;
+ }
+
+ ctx->idle_ts = quic_timestamp();
+
+ ngtcp2_settings settings;
+ ngtcp2_settings_default(&settings);
+ settings.initial_ts = ctx->idle_ts;
+ settings.handshake_timeout = ctx->tls->wait * NGTCP2_SECONDS;
+
+ ngtcp2_transport_params params;
+ ngtcp2_transport_params_default(&params);
+ params.initial_max_streams_uni = 0;
+ params.initial_max_streams_bidi = 0;
+ params.initial_max_stream_data_bidi_local = NGTCP2_MAX_VARINT;
+ params.initial_max_data = NGTCP2_MAX_VARINT;
+
+ struct sockaddr_in6 src_addr;
+ socklen_t src_addr_len = sizeof(src_addr);
+ ret = getsockname(sockfd, (struct sockaddr *)&src_addr, &src_addr_len);
+ if (ret < 0) {
+ tls_ctx_deinit(ctx->tls);
+ return knot_map_errno();
+ }
+ ngtcp2_path path = {
+ .local = {
+ .addrlen = src_addr_len,
+ .addr = (struct sockaddr *)&src_addr
+ },
+ .remote = {
+ .addrlen = sizeof(*(dst_addr->ai_addr)),
+ .addr = (struct sockaddr *)(dst_addr->ai_addr)
+ },
+ .user_data = NULL
+ };
+
+ if (ngtcp2_conn_client_new(&ctx->conn, &dcid, &scid, &path,
+ NGTCP2_PROTO_VER_V1, &quic_client_callbacks,
+ &settings, &params, NULL, ctx) != 0) {
+ tls_ctx_deinit(ctx->tls);
+ return KNOT_NET_ECONNECT;
+ }
+
+ gnutls_handshake_set_hook_function(ctx->tls->session,
+ GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, hook_func);
+ ret = ngtcp2_crypto_gnutls_configure_client_session(ctx->tls->session);
+ if (ret != KNOT_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return KNOT_NET_ECONNECT;
+ }
+ gnutls_session_set_ptr(ctx->tls->session, ctx);
+ ngtcp2_conn_set_tls_native_handle(ctx->conn, ctx->tls->session);
+
+ // Initialize poll descriptor structure.
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+ ctx->tls->sockfd = sockfd;
+
+ int timeout = ctx->tls->wait * 1000;
+ while(ctx->state != CONNECTED) {
+ if (quic_timeout(ctx->idle_ts, ctx->tls->wait)) {
+ WARN("QUIC, peer took too long to respond");
+ tls_ctx_deinit(ctx->tls);
+ return KNOT_NET_ETIMEOUT;
+ }
+ ret = quic_send(ctx, sockfd, dst_addr->ai_family);
+ if (ret != KNOT_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return ret;
+ }
+
+ ret = poll(&pfd, 1, timeout);
+ if (ret < 0) {
+ tls_ctx_deinit(ctx->tls);
+ return knot_map_errno();
+ } else if (ret == 0) {
+ continue;
+ }
+
+ ret = quic_recv(ctx, sockfd);
+ if (ret != KNOT_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return ret;
+ }
+ const ngtcp2_transport_params *pp =
+ ngtcp2_conn_get_remote_transport_params(ctx->conn);
+ if (pp != NULL) {
+ timeout = quic_ceil_duration_to_ms(pp->max_ack_delay);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int quic_send_dns_query(quic_ctx_t *ctx, int sockfd, struct addrinfo *srv,
+ const uint8_t *buf, const size_t buf_len)
+{
+ if (ctx == NULL || buf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint16_t query_length = htons(buf_len);
+ ngtcp2_vec datav[] = {
+ {
+ .base = (uint8_t *)&query_length,
+ .len = sizeof(uint16_t)
+ },{
+ .base = (uint8_t *)buf,
+ .len = buf_len
+ }
+ };
+ size_t datavlen = sizeof(datav)/sizeof(*datav);
+ ngtcp2_vec *pdatav = datav;
+
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ // Open stream when connection keep-opened
+ if (ctx->stream.id == -1) {
+ quic_open_bidi_stream(ctx);
+ quic_send(ctx, sockfd, srv->ai_family);
+ }
+
+ int timeout = ctx->tls->wait * 1000;
+ while (ctx->stream.out_ack == 0) {
+ if (quic_timeout(ctx->idle_ts, ctx->tls->wait)) {
+ WARN("QUIC, failed to send");
+ set_application_error(ctx, DOQ_REQUEST_CANCELLED,
+ (uint8_t *)"Connection timeout",
+ sizeof("Connection timeout") - 1);
+ return KNOT_NET_ETIMEOUT;
+ }
+ int ret = quic_send_data(ctx, sockfd, srv->ai_family, pdatav,
+ datavlen);
+ if (ret != KNOT_EOK) {
+ WARN("QUIC, failed to send");
+ return ret;
+ }
+ if (ctx->stream.out_ack > 0) {
+ pdatav = NULL;
+ datavlen = 0;
+ }
+
+ const ngtcp2_transport_params *pp =
+ ngtcp2_conn_get_remote_transport_params(ctx->conn);
+ if (pp != NULL) {
+ timeout = quic_ceil_duration_to_ms(pp->max_ack_delay);
+ }
+ ret = poll(&pfd, 1, timeout);
+ if (ret < 0) {
+ WARN("QUIC, failed to send");
+ return knot_map_errno();
+ } else if (ret == 0) {
+ continue;
+ }
+ ret = quic_recv(ctx, sockfd);
+ if (ret != KNOT_EOK) {
+ WARN("QUIC, failed to send");
+ return ret;
+ }
+ if (ctx->stream.in_parsed_size) {
+ return KNOT_EOK;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int quic_recv_dns_response(quic_ctx_t *ctx, uint8_t *buf, const size_t buf_len,
+ struct addrinfo *srv)
+{
+ if (ctx == NULL || ctx->tls == NULL || buf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = quic_respcpy(ctx, buf, buf_len);
+ if (ret != 0) {
+ return ret;
+ } else if (ctx->stream.id < 0) {
+ return KNOT_NET_ERECV;
+ }
+
+ int sockfd = ctx->tls->sockfd;
+
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ int timeout = ctx->tls->wait * 1000;
+ while (!quic_timeout(ctx->idle_ts, ctx->tls->wait)) {
+ const ngtcp2_transport_params *pp =
+ ngtcp2_conn_get_remote_transport_params(ctx->conn);
+ if (pp != NULL) {
+ timeout = quic_ceil_duration_to_ms(pp->max_ack_delay);
+ }
+ ret = poll(&pfd, 1, timeout);
+ if (ret < 0) {
+ WARN("QUIC, failed to receive reply (%s)",
+ knot_strerror(errno));
+ return knot_map_errno();
+ } else if (ret == 0) {
+ goto send;
+ }
+
+ ret = quic_recv(ctx, sockfd);
+ if (ret != KNOT_EOK) {
+ WARN("QUIC, failed to receive reply (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+ ret = quic_respcpy(ctx, buf, buf_len);
+ if (ret != 0) {
+ if (ret < 0) {
+ WARN("QUIC, failed to receive reply (%s)",
+ knot_strerror(ret));
+ }
+ return ret;
+ } else if (ctx->stream.id < 0) {
+ return KNOT_NET_ERECV;
+ }
+
+
+ send: ret = quic_send(ctx, sockfd, srv->ai_family);
+ if (ret != KNOT_EOK) {
+ WARN("QUIC, failed to receive reply (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+ }
+
+ WARN("QUIC, peer took too long to respond");
+ set_application_error(ctx, DOQ_REQUEST_CANCELLED,
+ (uint8_t *)"Connection timeout",
+ sizeof("Connection timeout") - 1);
+ return KNOT_NET_ETIMEOUT;
+}
+
+#define quic_ctx_write_close(ctx, dest, dest_len, ts) \
+ ngtcp2_conn_write_connection_close((ctx)->conn, (ngtcp2_path *)ngtcp2_conn_get_path((ctx)->conn), \
+ &(ctx)->pi, dest, dest_len, &(ctx)->last_err, ts)
+
+void quic_ctx_close(quic_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->state == CLOSED) {
+ return;
+ }
+
+ uint8_t enc_buf[MAX_PACKET_SIZE];
+ struct iovec msg_iov = {
+ .iov_base = enc_buf,
+ .iov_len = 0
+ };
+ struct msghdr msg = {
+ .msg_iov = &msg_iov,
+ .msg_iovlen = 1
+ };
+
+ ngtcp2_ssize nwrite = quic_ctx_write_close(ctx, enc_buf,
+ sizeof(enc_buf), quic_timestamp());
+ if (nwrite <= 0) {
+ return;
+ }
+
+ msg_iov.iov_len = nwrite;
+
+ struct sockaddr_in6 si = { 0 };
+ socklen_t si_len = sizeof(si);
+ if (getsockname(ctx->tls->sockfd, (struct sockaddr *)&si, &si_len) == 0) {
+ quic_set_enc(ctx->tls->sockfd, si.sin6_family, ctx->pi.ecn);
+ }
+
+ (void)sendmsg(ctx->tls->sockfd, &msg, 0);
+ ctx->state = CLOSED;
+}
+
+void quic_ctx_deinit(quic_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->conn) {
+ ngtcp2_conn_del(ctx->conn);
+ ctx->conn = NULL;
+ }
+
+ if (ctx->stream.in_buffer.iov_base != NULL) {
+ free(ctx->stream.in_buffer.iov_base);
+ }
+
+ if (ctx->stream.in_parsed != NULL) {
+ free(ctx->stream.in_parsed);
+ }
+}
+
+void print_quic(const quic_ctx_t *ctx)
+{
+ if (ctx == NULL || !ctx->params.enable || ctx->tls->session == NULL) {
+ return;
+ }
+
+ char *msg = gnutls_session_get_desc(ctx->tls->session);
+ printf(";; QUIC session (QUICv%d)-%s\n", ngtcp2_conn_get_negotiated_version(ctx->conn), msg);
+ gnutls_free(msg);
+}
+
+#endif
diff --git a/src/utils/common/quic.h b/src/utils/common/quic.h
new file mode 100644
index 0000000..74623f1
--- /dev/null
+++ b/src/utils/common/quic.h
@@ -0,0 +1,125 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+/*! \brief QUIC parameters. */
+typedef struct {
+ /*! Use QUIC indicator. */
+ bool enable;
+} quic_params_t;
+
+int quic_params_copy(quic_params_t *dst, const quic_params_t *src);
+
+void quic_params_clean(quic_params_t *params);
+
+#ifdef ENABLE_QUIC
+
+#include <ngtcp2/ngtcp2.h>
+#include <ngtcp2/ngtcp2_crypto.h>
+
+#include "utils/common/tls.h"
+
+#define QUIC_DEFAULT_VERSION "-VERS-ALL:+VERS-TLS1.3"
+#define QUIC_DEFAULT_CIPHERS "-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:+CHACHA20-POLY1305:+AES-128-CCM"
+#define QUIC_DEFAULT_GROUPS "-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1"
+#define QUIC_PRIORITY "%DISABLE_TLS13_COMPAT_MODE:NORMAL:"QUIC_DEFAULT_VERSION":"QUIC_DEFAULT_CIPHERS":"QUIC_DEFAULT_GROUPS
+
+typedef enum {
+ OPENING,
+ CONNECTED,
+ CLOSING,
+ CLOSED
+} quic_state_t;
+
+typedef enum {
+ /*! No error. This is used when the connection or stream needs to be
+ closed, but there is no error to signal. */
+ DOQ_NO_ERROR = 0x0,
+ /*! The DoQ implementation encountered an internal error and is
+ incapable of pursuing the transaction or the connection. */
+ DOQ_INTERNAL_ERROR = 0x1,
+ /*! The DoQ implementation encountered a protocol error and is forcibly
+ aborting the connection. */
+ DOQ_PROTOCOL_ERROR = 0x2,
+ /*! A DoQ client uses this to signal that it wants to cancel an
+ outstanding transaction. */
+ DOQ_REQUEST_CANCELLED = 0x3,
+ /*! A DoQ implementation uses this to signal when closing a connection
+ due to excessive load. */
+ DOQ_EXCESSIVE_LOAD = 0x4,
+ /*! A DoQ implementation uses this in the absence of a more specific
+ error code. */
+ DOQ_UNSPECIFIED_ERROR = 0x5,
+ /*! Alternative error code used for tests. */
+ DOQ_ERROR_RESERVED = 0xd098ea5e
+} quic_doq_error_t;
+
+typedef struct {
+ ngtcp2_crypto_conn_ref conn_ref;
+ // Parameters
+ quic_params_t params;
+
+ // Context
+ ngtcp2_settings settings;
+ struct {
+ int64_t id;
+ uint64_t out_ack;
+ struct iovec in_buffer;
+ struct iovec *in_parsed;
+ size_t in_parsed_size;
+ size_t in_parsed_total;
+ size_t in_parsed_it;
+ int resets;
+ } stream;
+ ngtcp2_connection_close_error last_err;
+ uint8_t secret[32];
+ tls_ctx_t *tls;
+ ngtcp2_conn *conn;
+ ngtcp2_pkt_info pi;
+ quic_state_t state;
+ uint64_t idle_ts;
+} quic_ctx_t;
+
+extern const gnutls_datum_t doq_alpn[];
+
+uint64_t quic_timestamp(void);
+
+int quic_generate_secret(uint8_t *buf, size_t buflen);
+
+uint32_t quic_get_ecn(struct msghdr *msg, const int family);
+
+int quic_set_enc(int sockfd, int family, uint32_t ecn);
+
+int quic_ctx_init(quic_ctx_t *ctx, tls_ctx_t *tls_ctx, const quic_params_t *params);
+
+int quic_ctx_connect(quic_ctx_t *ctx, int sockfd, struct addrinfo *dst_addr);
+
+int quic_send_dns_query(quic_ctx_t *ctx, int sockfd, struct addrinfo *srv,
+ const uint8_t *buf, const size_t buf_len);
+
+int quic_recv_dns_response(quic_ctx_t *ctx, uint8_t *buf, const size_t buf_len,
+ struct addrinfo *srv);
+
+void quic_ctx_close(quic_ctx_t *ctx);
+
+void quic_ctx_deinit(quic_ctx_t *ctx);
+
+void print_quic(const quic_ctx_t *ctx);
+
+#endif //ENABLE_QUIC
diff --git a/src/utils/common/resolv.c b/src/utils/common/resolv.c
new file mode 100644
index 0000000..674a760
--- /dev/null
+++ b/src/utils/common/resolv.c
@@ -0,0 +1,211 @@
+/* Copyright (C) 2023 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/common/resolv.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "libknot/libknot.h"
+#include "contrib/ucw/lists.h"
+
+#define RESOLV_FILE "/etc/resolv.conf"
+
+srv_info_t* parse_nameserver(const char *str, const char *def_port)
+{
+ char *host = NULL, *port = NULL;
+ const char *addr = NULL, *sep = NULL;
+ size_t addr_len = 0;
+ char separator = ':';
+
+ if (str == NULL || def_port == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ const size_t str_len = strlen(str);
+ const char *str_end = str + str_len;
+
+ // UNIX socket path.
+ if (*str == '/') {
+ return srv_info_create(str, "UNIX");
+ // [address]:port notation.
+ } else if (*str == '[') {
+ addr = str + 1;
+ const char *addr_end = strchr(addr, ']');
+ // Missing closing bracket -> stop processing.
+ if (addr_end == NULL) {
+ return NULL;
+ }
+ addr_len = addr_end - addr;
+ str += 1 + addr_len + 1;
+ // Address@port notation.
+ } else if ((sep = strchr(str, '@')) != NULL) {
+ addr = str;
+ addr_len = sep - addr;
+ str += addr_len;
+ separator = '@';
+ // Address#port notation.
+ } else if ((sep = strchr(str, '#')) != NULL) {
+ addr = str;
+ addr_len = sep - addr;
+ str += addr_len;
+ separator = '#';
+ // IPv4:port notation.
+ } else if ((sep = strchr(str, ':')) != NULL) {
+ addr = str;
+ // Not IPv4 address -> no port.
+ if (strchr(sep + 1, ':') != NULL) {
+ addr_len = str_len;
+ str = str_end;
+ } else {
+ addr_len = sep - addr;
+ str += addr_len;
+ }
+ // No port specified.
+ } else {
+ addr = str;
+ addr_len = str_len;
+ str = str_end;
+ }
+
+ // Process port.
+ if (str < str_end) {
+ // Check port separator.
+ if (*str != separator) {
+ return NULL;
+ }
+ str++;
+
+ // Check for missing port.
+ if (str >= str_end) {
+ return NULL;
+ }
+
+ port = strdup(str);
+ } else {
+ port = strdup(def_port);
+ }
+
+ host = strndup(addr, addr_len);
+
+ // Create server structure.
+ srv_info_t *server = srv_info_create(host, port);
+
+ free(host);
+ free(port);
+
+ return server;
+}
+
+static size_t get_resolv_nameservers(list_t *servers, const char *def_port)
+{
+ char line[512];
+
+ // Open config file.
+ FILE *f = fopen(RESOLV_FILE, "r");
+ if (f == NULL) {
+ return 0;
+ }
+
+ // Read lines from config file.
+ while (fgets(line, sizeof(line), f) != NULL) {
+ size_t len;
+ char *pos = line;
+ char *option, *value;
+
+ // Find leading white characters.
+ len = strspn(pos, SEP_CHARS);
+ pos += len;
+
+ // Start of the first token.
+ option = pos;
+
+ // Find length of the token.
+ len = strcspn(pos, SEP_CHARS);
+ pos += len;
+
+ // Check if the token is not empty.
+ if (len == 0) {
+ continue;
+ }
+
+ // Find separating white characters.
+ len = strspn(pos, SEP_CHARS);
+ pos += len;
+
+ // Check if there is a separation between tokens.
+ if (len == 0) {
+ continue;
+ }
+
+ // Copy of the second token.
+ value = strndup(pos, strcspn(pos, SEP_CHARS));
+
+ // Process value with respect to option name.
+ if (strncmp(option, "nameserver", strlen("nameserver")) == 0) {
+ srv_info_t *server;
+
+ server = parse_nameserver(value, def_port);
+
+ // If value is correct, add nameserver to the list.
+ if (server != NULL) {
+ add_tail(servers, (node_t *)server);
+ }
+ }
+
+ // Drop value string.
+ free(value);
+ }
+
+ // Close config file.
+ fclose(f);
+
+ // Return number of servers.
+ return list_size(servers);
+}
+
+void get_nameservers(list_t *servers, const char *def_port)
+{
+ if (servers == NULL || def_port == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Initialize list of servers.
+ init_list(servers);
+
+ // Read nameservers from resolv file or use the default ones.
+ if (get_resolv_nameservers(servers, def_port) == 0) {
+ srv_info_t *server;
+
+ // Add default ipv6 nameservers.
+ server = srv_info_create(DEFAULT_IPV6_NAME, def_port);
+
+ if (server != NULL) {
+ add_tail(servers, (node_t *)server);
+ }
+
+ // Add default ipv4 nameservers.
+ server = srv_info_create(DEFAULT_IPV4_NAME, def_port);
+
+ if (server != NULL) {
+ add_tail(servers, (node_t *)server);
+ }
+ }
+}
diff --git a/src/utils/common/resolv.h b/src/utils/common/resolv.h
new file mode 100644
index 0000000..fb751d1
--- /dev/null
+++ b/src/utils/common/resolv.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "utils/common/netio.h"
+#include "contrib/ucw/lists.h"
+
+srv_info_t* parse_nameserver(const char *str, const char *def_port);
+
+void get_nameservers(list_t *servers, const char *def_port);
diff --git a/src/utils/common/sign.c b/src/utils/common/sign.c
new file mode 100644
index 0000000..84284d3
--- /dev/null
+++ b/src/utils/common/sign.c
@@ -0,0 +1,109 @@
+/* Copyright (C) 2017 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 <string.h>
+
+#include "utils/common/sign.h"
+#include "libknot/errcode.h"
+#include "libknot/tsig-op.h"
+
+int sign_context_init_tsig(sign_context_t *ctx, const knot_tsig_key_t *key)
+{
+ if (!ctx || !key) {
+ return KNOT_EINVAL;
+ }
+
+ size_t digest_size = dnssec_tsig_algorithm_size(key->algorithm);
+ if (digest_size == 0) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *digest = calloc(1, digest_size);
+ if (!digest) {
+ return KNOT_ENOMEM;
+ }
+
+ ctx->digest_size = digest_size;
+ ctx->digest = digest;
+ ctx->tsig_key = key;
+
+ return KNOT_EOK;
+}
+
+void sign_context_deinit(sign_context_t *ctx)
+{
+ if (!ctx) {
+ return;
+ }
+
+ free(ctx->digest);
+
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+int sign_packet(knot_pkt_t *pkt, sign_context_t *sign_ctx)
+{
+ if (pkt == NULL || sign_ctx == NULL || sign_ctx->digest == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *wire = pkt->wire;
+ size_t *wire_size = &pkt->size;
+ size_t max_size = pkt->max_size;
+
+ int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(sign_ctx->tsig_key));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return knot_tsig_sign(wire, wire_size, max_size, NULL, 0,
+ sign_ctx->digest, &sign_ctx->digest_size,
+ sign_ctx->tsig_key, 0, 0);
+}
+
+int verify_packet(const knot_pkt_t *pkt, const sign_context_t *sign_ctx)
+{
+ if (pkt == NULL || sign_ctx == NULL || sign_ctx->digest == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ const uint8_t *wire = pkt->wire;
+ const size_t *wire_size = &pkt->size;
+
+ if (pkt->tsig_rr == NULL) {
+ return KNOT_ENOTSIG;
+ }
+
+ int ret = knot_tsig_client_check(pkt->tsig_rr, wire, *wire_size,
+ sign_ctx->digest, sign_ctx->digest_size,
+ sign_ctx->tsig_key, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ switch (knot_tsig_rdata_error(pkt->tsig_rr)) {
+ case KNOT_RCODE_BADSIG:
+ return KNOT_TSIG_EBADSIG;
+ case KNOT_RCODE_BADKEY:
+ return KNOT_TSIG_EBADKEY;
+ case KNOT_RCODE_BADTIME:
+ return KNOT_TSIG_EBADTIME;
+ case KNOT_RCODE_BADTRUNC:
+ return KNOT_TSIG_EBADTRUNC;
+ default:
+ return KNOT_EOK;
+ }
+}
diff --git a/src/utils/common/sign.h b/src/utils/common/sign.h
new file mode 100644
index 0000000..52f41ef
--- /dev/null
+++ b/src/utils/common/sign.h
@@ -0,0 +1,63 @@
+/* Copyright (C) 2015 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/>.
+ */
+
+#pragma once
+
+#include "libknot/packet/pkt.h"
+#include "libknot/tsig.h"
+
+/*!
+ * \brief Holds data required between signing and signature verification.
+ */
+struct sign_context {
+ size_t digest_size;
+ uint8_t *digest;
+ const knot_tsig_key_t *tsig_key;
+};
+
+typedef struct sign_context sign_context_t;
+
+/*!
+ * \brief Initialize signing context for TSIG.
+ */
+int sign_context_init_tsig(sign_context_t *ctx, const knot_tsig_key_t *key);
+
+/*!
+ * \brief Clean up signing context.
+ *
+ * \param ctx Sign context.
+ */
+void sign_context_deinit(sign_context_t *ctx);
+
+/*!
+ * \brief Signs outgoing DNS packet.
+ *
+ * \param pkt Packet to sign.
+ * \param sign_ctx Signing context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int sign_packet(knot_pkt_t *pkt, sign_context_t *sign_ctx);
+
+/*!
+ * \brief Verifies signature for incoming DNS packet.
+ *
+ * \param pkt Packet verify sign.
+ * \param sign_ctx Signing context.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int verify_packet(const knot_pkt_t *pkt, const sign_context_t *sign_ctx);
diff --git a/src/utils/common/tls.c b/src/utils/common/tls.c
new file mode 100644
index 0000000..c440769
--- /dev/null
+++ b/src/utils/common/tls.c
@@ -0,0 +1,739 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <arpa/inet.h>
+#include <stdbool.h>
+#include <string.h>
+#include <poll.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/ocsp.h>
+#include <gnutls/x509.h>
+#define GNUTLS_VERSION_FASTOPEN_READY 0x030503
+#if GNUTLS_VERSION_NUMBER >= GNUTLS_VERSION_FASTOPEN_READY
+#include <gnutls/socket.h>
+#endif
+
+#include "utils/common/tls.h"
+#include "utils/common/cert.h"
+#include "utils/common/msg.h"
+#include "contrib/base64.h"
+#include "libknot/errcode.h"
+
+const gnutls_datum_t dot_alpn = {
+ (unsigned char *)"dot", 3
+};
+
+void tls_params_init(tls_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ memset(params, 0, sizeof(*params));
+
+ init_list(&params->ca_files);
+ init_list(&params->pins);
+}
+
+int tls_params_copy(tls_params_t *dst, const tls_params_t *src)
+{
+ if (dst == NULL || src == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ tls_params_init(dst);
+
+ dst->enable = src->enable;
+ dst->system_ca = src->system_ca;
+ if (src->hostname != NULL) {
+ dst->hostname = strdup(src->hostname);
+ if (dst->hostname == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ if (src->sni != NULL) {
+ dst->sni = strdup(src->sni);
+ if (dst->sni == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ if (src->keyfile != NULL) {
+ dst->keyfile = strdup(src->keyfile);
+ if (dst->keyfile == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ if (src->certfile != NULL) {
+ dst->certfile = strdup(src->certfile);
+ if (dst->certfile == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ }
+
+ dst->ocsp_stapling = src->ocsp_stapling;
+
+ ptrnode_t *n;
+ WALK_LIST(n, src->ca_files) {
+ char *src_file = (char *)n->d;
+ char *file = strdup(src_file);
+ if (file == NULL || ptrlist_add(&dst->ca_files, file, NULL) == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ }
+ WALK_LIST(n, src->pins) {
+ uint8_t *src_pin = (uint8_t *)n->d;
+ uint8_t *pin = malloc(1 + src_pin[0]);
+ if (pin == NULL || ptrlist_add(&dst->pins, pin, NULL) == NULL) {
+ tls_params_clean(dst);
+ return KNOT_ENOMEM;
+ }
+ memcpy(pin, src_pin, 1 + src_pin[0]);
+ }
+
+ return KNOT_EOK;
+}
+
+void tls_params_clean(tls_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, params->ca_files) {
+ free(node->d);
+ }
+ ptrlist_free(&params->ca_files, NULL);
+
+ WALK_LIST_DELSAFE(node, nxt, params->pins) {
+ free(node->d);
+ }
+ ptrlist_free(&params->pins, NULL);
+
+ free(params->hostname);
+ free(params->sni);
+ free(params->keyfile);
+ free(params->certfile);
+
+ memset(params, 0, sizeof(*params));
+}
+
+static bool check_pin(const uint8_t *cert_pin, size_t cert_pin_len, const list_t *pins)
+{
+ if (EMPTY_LIST(*pins)) {
+ return false;
+ }
+
+ ptrnode_t *n;
+ WALK_LIST(n, *pins) {
+ uint8_t *pin = (uint8_t *)n->d;
+ if (pin[0] == cert_pin_len &&
+ memcmp(cert_pin, &pin[1], cert_pin_len) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool verify_ocsp(gnutls_session_t *session)
+{
+ bool ret = false;
+
+ gnutls_ocsp_resp_t ocsp_resp;
+ bool deinit_ocsp_resp = false;
+
+ gnutls_x509_crt_t server_cert;
+ bool deinit_server_cert = false;
+
+ gnutls_certificate_credentials_t xcred;
+ bool deinit_xcreds = false;
+
+ gnutls_x509_crt_t issuer_cert;
+ bool deinit_issuer_cert = false;
+
+ gnutls_datum_t ocsp_resp_raw;
+ if (gnutls_ocsp_status_request_get(*session, &ocsp_resp_raw) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to retrieve stapled OCSP data");
+ goto cleanup;
+ }
+ if (gnutls_ocsp_resp_init(&ocsp_resp) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to init OCSP data");
+ goto cleanup;
+ }
+ deinit_ocsp_resp = true;
+ if (gnutls_ocsp_resp_import(ocsp_resp, &ocsp_resp_raw) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to import OCSP response");
+ goto cleanup;
+ }
+
+ unsigned int cert_list_size = 0;
+ const gnutls_datum_t *cert_list = gnutls_certificate_get_peers(*session, &cert_list_size);
+ if (cert_list_size == 0) {
+ WARN("TLS, unable to retrieve peer certs when verifying OCSP");
+ goto cleanup;
+ }
+ if (gnutls_x509_crt_init(&server_cert) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to init server cert when verifying OCSP");
+ goto cleanup;
+ }
+ deinit_server_cert = true;
+ if (gnutls_x509_crt_import(server_cert, &cert_list[0], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to import server cert when verifying OCSP");
+ goto cleanup;
+ }
+
+ if (gnutls_certificate_allocate_credentials(&xcred) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to allocate credentials when verifying OCSP");
+ goto cleanup;
+ }
+ deinit_xcreds = true;
+
+ if (gnutls_certificate_get_issuer(xcred, server_cert, &issuer_cert, 0) != GNUTLS_E_SUCCESS) {
+ if (cert_list_size < 2) {
+ WARN("TLS, unable to get issuer (CA) cert when verifying OCSP");
+ goto cleanup;
+ }
+ if (gnutls_x509_crt_init(&issuer_cert) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to init issuer cert when verifying OCSP");
+ goto cleanup;
+ }
+ deinit_issuer_cert = true;
+ if (gnutls_x509_crt_import(issuer_cert, &cert_list[1], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to import issuer cert when verifying OCSP");
+ goto cleanup;
+ }
+ }
+
+ unsigned int status;
+ time_t this_upd, next_upd, now = time(0);
+ if (gnutls_ocsp_resp_check_crt(ocsp_resp, 0, server_cert) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, OCSP response either empty or not for provided server cert");
+ goto cleanup;
+ }
+ if (gnutls_ocsp_resp_verify_direct(ocsp_resp, issuer_cert, &status, 0) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, unable to verify OCSP response against issuer cert");
+ goto cleanup;
+ }
+ if (status != 0) {
+ WARN("TLS, got a non-zero status when verifying OCSP response against issuer cert");
+ goto cleanup;
+ }
+ if (gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, &status,
+ &this_upd, &next_upd, NULL, NULL) != GNUTLS_E_SUCCESS) {
+ WARN("TLS, error reading OCSP response");
+ goto cleanup;
+ }
+ if (status == GNUTLS_OCSP_CERT_REVOKED) {
+ WARN("TLS, OCSP data shows that cert was revoked");
+ goto cleanup;
+ }
+ if (next_upd == -1) {
+ tls_ctx_t *ctx = gnutls_session_get_ptr(*session);
+ assert(now >= this_upd);
+ assert(ctx->params->ocsp_stapling > 0);
+ if (now - this_upd > ctx->params->ocsp_stapling) {
+ WARN("TLS, OCSP response is out of date.");
+ goto cleanup;
+ }
+ } else {
+ if (next_upd < now) {
+ WARN("TLS, a newer OCSP response is available but was not sent");
+ goto cleanup;
+ }
+ }
+
+ // Only if we get here is the ocsp result completely valid.
+ ret = true;
+
+cleanup:
+ if (deinit_issuer_cert) {
+ gnutls_x509_crt_deinit(issuer_cert);
+ }
+ if (deinit_xcreds) {
+ gnutls_certificate_free_credentials(xcred);
+ }
+ if (deinit_server_cert) {
+ gnutls_x509_crt_deinit(server_cert);
+ }
+ if (deinit_ocsp_resp) {
+ gnutls_ocsp_resp_deinit(ocsp_resp);
+ }
+
+ return ret;
+}
+
+static int check_certificates(gnutls_session_t session, const list_t *pins)
+{
+ if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
+ DBG("TLS, invalid certificate type");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ unsigned cert_list_size;
+ const gnutls_datum_t *cert_list =
+ gnutls_certificate_get_peers(session, &cert_list_size);
+ if (cert_list == NULL || cert_list_size == 0) {
+ DBG("TLS, empty certificate list");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ size_t matches = 0;
+
+ DBG("TLS, received certificate hierarchy:");
+ for (int i = 0; i < cert_list_size; i++) {
+ gnutls_x509_crt_t cert;
+ int ret = gnutls_x509_crt_init(&cert);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return ret;
+ }
+
+ ret = gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(cert);
+ return ret;
+ }
+
+ gnutls_datum_t cert_name = { 0 };
+ ret = gnutls_x509_crt_get_dn2(cert, &cert_name);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(cert);
+ return ret;
+ }
+ DBG(" #%i, %s", i + 1, cert_name.data);
+ gnutls_free(cert_name.data);
+
+ uint8_t cert_pin[CERT_PIN_LEN] = { 0 };
+ ret = cert_get_pin(cert, cert_pin, sizeof(cert_pin));
+ if (ret != KNOT_EOK) {
+ gnutls_x509_crt_deinit(cert);
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ // Check if correspond to a specified PIN.
+ bool match = check_pin(cert_pin, sizeof(cert_pin), pins);
+ if (match) {
+ matches++;
+ }
+
+ uint8_t *txt_pin;
+ ret = knot_base64_encode_alloc(cert_pin, sizeof(cert_pin), &txt_pin);
+ if (ret < 0) {
+ gnutls_x509_crt_deinit(cert);
+ return ret;
+ }
+ DBG(" SHA-256 PIN: %.*s%s", ret, txt_pin, match ? ", MATCH" : "");
+ free(txt_pin);
+
+ gnutls_x509_crt_deinit(cert);
+ }
+
+ if (matches > 0) {
+ return GNUTLS_E_SUCCESS;
+ } else if (EMPTY_LIST(*pins)) {
+ DBG("TLS, skipping certificate PIN check");
+ return GNUTLS_E_SUCCESS;
+ } else {
+ DBG("TLS, no certificate PIN match");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+}
+
+static bool do_verification(const tls_params_t *params)
+{
+ return params->hostname != NULL || params->system_ca ||
+ !EMPTY_LIST(params->ca_files) || params->ocsp_stapling > 0;
+}
+
+int tls_certificate_verification(tls_ctx_t *ctx)
+{
+ gnutls_session_t session = ctx->session;
+ // Check for pinned certificates and print certificate hierarchy.
+ int ret = check_certificates(session, &ctx->params->pins);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return ret;
+ }
+
+ if (!do_verification(ctx->params)) {
+ DBG("TLS, skipping certificate verification");
+ return GNUTLS_E_SUCCESS;
+ }
+
+ if (ctx->params->ocsp_stapling > 0 && !verify_ocsp(&session)) {
+ WARN("TLS, failed to validate required OCSP data");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ // Set server certificate check.
+ gnutls_typed_vdata_st data[2] = {
+ { .type = GNUTLS_DT_KEY_PURPOSE_OID,
+ .data = (void *)GNUTLS_KP_TLS_WWW_SERVER },
+ { .type = GNUTLS_DT_DNS_HOSTNAME,
+ .data = (void *)ctx->params->hostname }
+ };
+ size_t data_count = (ctx->params->hostname != NULL) ? 2 : 1;
+ if (data_count == 1) {
+ WARN("TLS, no hostname provided, will not verify certificate owner")
+ }
+
+ unsigned int status;
+ ret = gnutls_certificate_verify_peers(session, data, data_count, &status);
+ if (ret != GNUTLS_E_SUCCESS) {
+ WARN("TLS, failed to verify peer certificate");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ gnutls_datum_t msg;
+ ret = gnutls_certificate_verification_status_print(
+ status, gnutls_certificate_type_get(session), &msg, 0);
+ if (ret == GNUTLS_E_SUCCESS) {
+ DBG("TLS, %s", msg.data);
+ }
+ gnutls_free(msg.data);
+
+ if (status != 0) {
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+static int verify_certificate(gnutls_session_t session)
+{
+ tls_ctx_t *ctx = gnutls_session_get_ptr(session);
+ return tls_certificate_verification(ctx);
+}
+
+int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params,
+ unsigned int flags, int wait)
+
+{
+ if (ctx == NULL || params == NULL || !params->enable) {
+ return KNOT_EINVAL;
+ }
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->params = params;
+ ctx->wait = wait;
+ ctx->sockfd = -1;
+
+ int ret = gnutls_certificate_allocate_credentials(&ctx->credentials);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return KNOT_ENOMEM;
+ }
+
+ // Import system certificates.
+ if (ctx->params->system_ca ||
+ (ctx->params->hostname != NULL && EMPTY_LIST(ctx->params->ca_files))) {
+ ret = gnutls_certificate_set_x509_system_trust(ctx->credentials);
+ if (ret < 0) {
+ WARN("TLS, failed to import system certificates (%s)",
+ gnutls_strerror_name(ret));
+ return KNOT_ERROR;
+ } else {
+ DBG("TLS, imported %i system certificates", ret);
+ }
+ }
+
+ // Import provided certificate files.
+ ptrnode_t *n;
+ WALK_LIST(n, ctx->params->ca_files) {
+ const char *file = (char *)n->d;
+ ret = gnutls_certificate_set_x509_trust_file(ctx->credentials, file,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ WARN("TLS, failed to import certificate file '%s' (%s)",
+ file, gnutls_strerror_name(ret));
+ return KNOT_ERROR;
+ } else {
+ DBG("TLS, imported %i certificates from '%s'", ret, file);
+ }
+ }
+
+ gnutls_certificate_set_verify_function(ctx->credentials, verify_certificate);
+
+ // Setup client keypair if specified. Both key and cert files must be provided.
+ if (params->keyfile != NULL && params->certfile != NULL) {
+ // First, try PEM.
+ ret = gnutls_certificate_set_x509_key_file(ctx->credentials,
+ params->certfile, params->keyfile, GNUTLS_X509_FMT_PEM);
+ if (ret != GNUTLS_E_SUCCESS) {
+ // If PEM didn't work, try DER.
+ ret = gnutls_certificate_set_x509_key_file(ctx->credentials,
+ params->certfile, params->keyfile, GNUTLS_X509_FMT_DER);
+ }
+
+ if (ret != GNUTLS_E_SUCCESS) {
+ WARN("TLS, failed to add client certfile '%s' and keyfile '%s'",
+ params->certfile, params->keyfile);
+ return KNOT_ERROR;
+ } else {
+ DBG("TLS, added client certfile '%s' and keyfile '%s'",
+ params->certfile, params->keyfile);
+ }
+ } else if (params->keyfile != NULL) {
+ WARN("TLS, cannot use client keyfile without a certfile");
+ return KNOT_ERROR;
+ } else if (params->certfile != NULL) {
+ WARN("TLS, cannot use client certfile without a keyfile");
+ return KNOT_ERROR;
+ }
+
+ ret = gnutls_init(&ctx->session, GNUTLS_CLIENT | flags);
+ if (ret != GNUTLS_E_SUCCESS) {
+ return KNOT_ENOMEM;
+ }
+
+ ret = gnutls_credentials_set(ctx->session, GNUTLS_CRD_CERTIFICATE,
+ ctx->credentials);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(ctx->session);
+ return KNOT_ERROR;
+ }
+
+ return KNOT_EOK;
+}
+
+int tls_ctx_setup_remote_endpoint(tls_ctx_t *ctx, const gnutls_datum_t *alpn,
+ size_t alpn_size, const char *priority, const char *remote)
+{
+ if (ctx == NULL || ctx->session == NULL || ctx->credentials == NULL) {
+ return KNOT_EINVAL;
+ }
+ int ret = 0;
+ if (alpn != NULL) {
+ ret = gnutls_alpn_set_protocols(ctx->session, alpn, alpn_size, 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(ctx->session);
+ return KNOT_NET_ECONNECT;
+ }
+ }
+
+ if (priority != NULL) {
+ ret = gnutls_priority_set_direct(ctx->session, priority, NULL);
+ } else {
+ ret = gnutls_set_default_priority(ctx->session);
+ }
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(ctx->session);
+ return KNOT_EINVAL;
+ }
+
+ if (remote != NULL) {
+ ret = gnutls_server_name_set(ctx->session, GNUTLS_NAME_DNS, remote,
+ strlen(remote));
+ if (ret != GNUTLS_E_SUCCESS) {
+ gnutls_deinit(ctx->session);
+ return KNOT_EINVAL;
+ }
+ }
+ return KNOT_EOK;
+}
+
+int tls_ctx_connect(tls_ctx_t *ctx, int sockfd, bool fastopen,
+ struct sockaddr_storage *addr)
+{
+ if (ctx == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = 0;
+ gnutls_session_set_ptr(ctx->session, ctx);
+
+ if (fastopen) {
+#if GNUTLS_VERSION_NUMBER >= GNUTLS_VERSION_FASTOPEN_READY
+ gnutls_transport_set_fastopen(ctx->session, sockfd, (struct sockaddr *)addr,
+ sockaddr_len(addr), 0);
+#else
+ gnutls_deinit(ctx->session);
+ return KNOT_ENOTSUP;
+#endif
+ } else {
+ gnutls_transport_set_int(ctx->session, sockfd);
+ }
+
+ gnutls_handshake_set_timeout(ctx->session, 1000 * ctx->wait);
+
+ // Initialize poll descriptor structure.
+ struct pollfd pfd = {
+ .fd = sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ // Perform the TLS handshake
+ do {
+ ret = gnutls_handshake(ctx->session);
+ if (ret != GNUTLS_E_SUCCESS && gnutls_error_is_fatal(ret) == 0) {
+ if (poll(&pfd, 1, 1000 * ctx->wait) != 1) {
+ WARN("TLS, peer took too long to respond");
+ gnutls_deinit(ctx->session);
+ return KNOT_NET_ETIMEOUT;
+ }
+ }
+ } while (ret != GNUTLS_E_SUCCESS && gnutls_error_is_fatal(ret) == 0);
+ if (ret != GNUTLS_E_SUCCESS) {
+ WARN("TLS, handshake failed (%s)", gnutls_strerror(ret));
+ tls_ctx_close(ctx);
+ return KNOT_NET_ESOCKET;
+ }
+
+ // Save the socket descriptor.
+ ctx->sockfd = sockfd;
+
+ return KNOT_EOK;
+}
+
+int tls_ctx_send(tls_ctx_t *ctx, const uint8_t *buf, const size_t buf_len)
+{
+ if (ctx == NULL || buf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ uint16_t msg_len = htons(buf_len);
+
+ gnutls_record_cork(ctx->session);
+
+ if (gnutls_record_send(ctx->session, &msg_len, sizeof(msg_len)) <= 0) {
+ WARN("TLS, failed to send");
+ return KNOT_NET_ESEND;
+ }
+ if (gnutls_record_send(ctx->session, buf, buf_len) <= 0) {
+ WARN("TLS, failed to send");
+ return KNOT_NET_ESEND;
+ }
+
+ while (gnutls_record_check_corked(ctx->session) > 0) {
+ int ret = gnutls_record_uncork(ctx->session, 0);
+ if (ret < 0 && gnutls_error_is_fatal(ret) != 0) {
+ WARN("TLS, failed to send (%s)", gnutls_strerror(ret));
+ return KNOT_NET_ESEND;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int tls_ctx_receive(tls_ctx_t *ctx, uint8_t *buf, const size_t buf_len)
+{
+ if (ctx == NULL || buf == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Initialize poll descriptor structure.
+ struct pollfd pfd = {
+ .fd = ctx->sockfd,
+ .events = POLLIN,
+ .revents = 0,
+ };
+
+ uint32_t total = 0;
+ uint16_t msg_len = 0;
+
+ // Receive message header.
+ while (total < sizeof(msg_len)) {
+ ssize_t ret = gnutls_record_recv(ctx->session,
+ (uint8_t *)&msg_len + total,
+ sizeof(msg_len) - total);
+ if (ret > 0) {
+ total += ret;
+ } else if (ret == 0) {
+ WARN("TLS, peer has closed the connection");
+ return KNOT_NET_ERECV;
+ } else if (gnutls_error_is_fatal(ret) != 0) {
+ WARN("TLS, failed to receive reply (%s)",
+ gnutls_strerror(ret));
+ return KNOT_NET_ERECV;
+ } else if (poll(&pfd, 1, 1000 * ctx->wait) != 1) {
+ WARN("TLS, peer took too long to respond");
+ return KNOT_NET_ETIMEOUT;
+ }
+ }
+
+ // Convert number to host format.
+ msg_len = ntohs(msg_len);
+ if (msg_len > buf_len) {
+ return KNOT_ESPACE;
+ }
+
+ total = 0;
+
+ // Receive data over TLS
+ while (total < msg_len) {
+ ssize_t ret = gnutls_record_recv(ctx->session, buf + total,
+ msg_len - total);
+ if (ret > 0) {
+ total += ret;
+ } else if (ret == 0) {
+ WARN("TLS, peer has closed the connection");
+ return KNOT_NET_ERECV;
+ } else if (gnutls_error_is_fatal(ret) != 0) {
+ WARN("TLS, failed to receive reply (%s)",
+ gnutls_strerror(ret));
+ return KNOT_NET_ERECV;
+ } else if (poll(&pfd, 1, 1000 * ctx->wait) != 1) {
+ WARN("TLS, peer took too long to respond");
+ return KNOT_NET_ETIMEOUT;
+ }
+ }
+
+ return total;
+}
+
+void tls_ctx_close(tls_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->session == NULL) {
+ return;
+ }
+
+ gnutls_bye(ctx->session, GNUTLS_SHUT_RDWR);
+}
+
+void tls_ctx_deinit(tls_ctx_t *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ if (ctx->credentials != NULL) {
+ gnutls_certificate_free_credentials(ctx->credentials);
+ ctx->credentials = NULL;
+ }
+ if (ctx->session != NULL) {
+ gnutls_deinit(ctx->session);
+ ctx->session = NULL;
+ }
+}
+
+void print_tls(const tls_ctx_t *ctx)
+{
+ if (ctx == NULL || ctx->params == NULL || !ctx->params->enable || ctx->session == NULL) {
+ return;
+ }
+
+ char *msg = gnutls_session_get_desc(ctx->session);
+ printf(";; TLS session %s\n", msg);
+ gnutls_free(msg);
+}
diff --git a/src/utils/common/tls.h b/src/utils/common/tls.h
new file mode 100644
index 0000000..7c25ab7
--- /dev/null
+++ b/src/utils/common/tls.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <netdb.h>
+#include <gnutls/gnutls.h>
+
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/lists.h"
+
+/*! \brief TLS parameters. */
+typedef struct {
+ /*! Use TLS indicator. */
+ bool enable;
+ /*! Import system certificates indicator. */
+ bool system_ca;
+ /*! Certificate files to import. */
+ list_t ca_files;
+ /*! Pinned certificates. */
+ list_t pins;
+ /*! Required server hostname. */
+ char *hostname;
+ /*! Optional server name indicator. */
+ char *sni;
+ /*! Optional client keyfile name. */
+ char *keyfile;
+ /*! Optional client certfile name. */
+ char *certfile;
+ /*! Optional validity of stapled OCSP response for the server cert. */
+ uint32_t ocsp_stapling;
+} tls_params_t;
+
+/*! \brief TLS context. */
+typedef struct {
+ /*! TLS handshake timeout. */
+ int wait;
+ /*! Socket descriptor. */
+ int sockfd;
+ /*! TLS parameters. */
+ const tls_params_t *params;
+ /*! GnuTLS session handle. */
+ gnutls_session_t session;
+ /*! GnuTLS credentials handle. */
+ gnutls_certificate_credentials_t credentials;
+} tls_ctx_t;
+
+extern const gnutls_datum_t dot_alpn;
+
+void tls_params_init(tls_params_t *params);
+int tls_params_copy(tls_params_t *dst, const tls_params_t *src);
+void tls_params_clean(tls_params_t *params);
+
+int tls_certificate_verification(tls_ctx_t *ctx);
+
+int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params,
+ unsigned int flags, int wait);
+int tls_ctx_setup_remote_endpoint(tls_ctx_t *ctx, const gnutls_datum_t *alpn,
+ size_t alpn_size, const char *priority, const char *remote);
+int tls_ctx_connect(tls_ctx_t *ctx, int sockfd,
+ bool fastopen, struct sockaddr_storage *addr);
+
+int tls_ctx_send(tls_ctx_t *ctx, const uint8_t *buf, const size_t buf_len);
+int tls_ctx_receive(tls_ctx_t *ctx, uint8_t *buf, const size_t buf_len);
+void tls_ctx_close(tls_ctx_t *ctx);
+void tls_ctx_deinit(tls_ctx_t *ctx);
+void print_tls(const tls_ctx_t *ctx);
diff --git a/src/utils/common/token.c b/src/utils/common/token.c
new file mode 100644
index 0000000..10e788e
--- /dev/null
+++ b/src/utils/common/token.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/common/token.h"
+#include "utils/common/msg.h"
+#include "libknot/libknot.h"
+#include "contrib/ctype.h"
+
+int tok_scan(const char* lp, const char **tbl, int *lpm)
+{
+ if (lp == NULL || tbl == NULL || *tbl == NULL || lpm == NULL) {
+ DBG_NULL;
+ return -1;
+ }
+
+ const char *prefix = lp; /* Ptr to line start. */
+ int i = 0, pl = 1; /* Match index, prefix length. */
+ unsigned char len = 0; /* Read length. */
+ for(;;) {
+ const char *tok = tbl[i];
+ if (*lp == '\0' || is_space(*lp)) {
+ if (tok && TOK_L(tok) == len) { /* Consumed whole w? */
+ return i; /* Identifier */
+ } else { /* Word is shorter than cmd? */
+ break;
+ }
+ }
+
+ /* Find next prefix match. */
+ ++len;
+ while (tok) {
+ if (TOK_L(tok) >= len) { /* Is prefix of current token */
+ if (*lp < tok[pl]) { /* Terminate early. */
+ tok = NULL;
+ break; /* No match could be found. */
+ }
+ if (*lp == tok[pl]) { /* Match */
+ if(lpm) *lpm = i;
+ ++pl;
+ break;
+ }
+ }
+
+ /* No early cut, no match - seek next. */
+ while ((tok = tbl[++i]) != NULL) {
+ if (TOK_L(tok) >= len &&
+ memcmp(TOK_S(tok), prefix, len) == 0) {
+ break;
+ }
+ }
+ }
+
+ if (tok == NULL) {
+ break; /* All tokens exhausted. */
+ } else {
+ ++lp; /* Next char */
+ }
+ }
+
+ return -1;
+}
+
+int tok_find(const char *lp, const char **tbl)
+{
+ if (lp == NULL || tbl == NULL || *tbl == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ int lpm = -1;
+ int bp = 0;
+ if ((bp = tok_scan(lp, tbl, &lpm)) < 0) {
+ if (lpm > -1) {
+ ERR("unexpected literal: '%s', did you mean '%s' ?",
+ lp, TOK_S(tbl[lpm]));
+ } else {
+ ERR("unexpected literal: '%s'", lp);
+ }
+
+ return KNOT_EPARSEFAIL;
+ }
+
+ return bp;
+}
+
+const char *tok_skipspace(const char *lp)
+{
+ if (lp == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ while (is_space(*lp)) {
+ lp += 1;
+ }
+
+ return lp;
+}
diff --git a/src/utils/common/token.h b/src/utils/common/token.h
new file mode 100644
index 0000000..fab4ea1
--- /dev/null
+++ b/src/utils/common/token.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+/*!
+ * \brief Example of token table:
+ *
+ * \warning Table _must_ be lexicographically ordered.
+ *
+ * const char *tok_tbl[] = {
+ * // LEN STRING
+ * "\x4" "abcd",
+ * "\x5" "class",
+ * NULL // END
+ * }
+ */
+/*! \brief String part of the token. */
+#define TOK_S(x) ((x)+1)
+/*! \brief Len of the token. */
+#define TOK_L(x) ((unsigned char)(x)[0])
+
+/*!
+ * \brief Scan for matching token described by a match table.
+ *
+ * Table consists of strings, prefixed with 1B length.
+ *
+ * \param lp Pointer to current line.
+ * \param tbl Match description table.
+ * \param lpm Pointer to longest prefix match.
+ * \retval index to matching record.
+ * \retval -1 if no match is found, lpm may be set to longest prefix match.
+ */
+int tok_scan(const char* lp, const char **tbl, int *lpm);
+
+/*!
+ * \brief Find token from table in a line buffer.
+ * \param lp Pointer to current line.
+ * \param tbl Match description table.
+ * \retval index to matching record.
+ * \retval error code if no match is found
+ */
+int tok_find(const char *lp, const char **tbl);
+
+/*!
+ * \brief Return pointer to next non-blank character.
+ * \param lp Pointer to current line.
+ * \return ptr to next non-blank character.
+ */
+const char *tok_skipspace(const char *lp);
diff --git a/src/utils/common/util_conf.c b/src/utils/common/util_conf.c
new file mode 100644
index 0000000..231f800
--- /dev/null
+++ b/src/utils/common/util_conf.c
@@ -0,0 +1,139 @@
+/* Copyright (C) 2022 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 <sys/stat.h>
+#include <unistd.h>
+
+#include "utils/common/util_conf.h"
+
+#include "contrib/string.h"
+#include "knot/common/log.h"
+#include "knot/conf/conf.h"
+#include "libknot/attribute.h"
+#include "utils/common/msg.h"
+
+bool util_conf_initialized(void)
+{
+ return (conf() != NULL);
+}
+
+int util_conf_init_confdb(const char *confdb)
+{
+ if (util_conf_initialized()) {
+ ERR2("configuration already initialized");
+ util_conf_deinit();
+ return KNOT_ESEMCHECK;
+ }
+
+ size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024;
+
+ conf_flag_t flags = CONF_FNOHOSTNAME | CONF_FOPTMODULES;
+ if (confdb != NULL) {
+ flags |= CONF_FREADONLY;
+ }
+
+ log_init();
+ log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, 0);
+ log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, LOG_UPTO(LOG_WARNING));
+ log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0);
+ log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO);
+
+ conf_t *new_conf = NULL;
+ int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, flags);
+ if (ret != KNOT_EOK) {
+ ERR2("failed opening configuration database '%s' (%s)",
+ (confdb == NULL ? "" : confdb), knot_strerror(ret));
+ } else {
+ conf_update(new_conf, CONF_UPD_FNONE);
+ }
+ return ret;
+}
+
+int util_conf_init_file(const char *conffile)
+{
+ int ret = util_conf_init_confdb(NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = conf_import(conf(), conffile, true, false);
+ if (ret != KNOT_EOK) {
+ ERR2("failed opening configuration file '%s' (%s)",
+ conffile, knot_strerror(ret));
+ }
+ return ret;
+}
+
+int util_conf_init_justdb(const char *db_type, const char *db_path)
+{
+ int ret = util_conf_init_confdb(NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ char *conf_str = sprintf_alloc("database:\n"
+ " storage: .\n"
+ " %s: \"%s\"\n", db_type, db_path);
+ if (conf_str == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ ret = conf_import(conf(), conf_str, false, false);
+ free(conf_str);
+ if (ret != KNOT_EOK) {
+ ERR2("failed creating temporary configuration (%s)", knot_strerror(ret));
+ }
+ return ret;
+}
+
+int util_conf_init_default(bool allow_db)
+{
+ struct stat st;
+ if (util_conf_initialized()) {
+ return KNOT_EOK;
+ } else if (conf_db_exists(CONF_DEFAULT_DBDIR)) {
+ return util_conf_init_confdb(CONF_DEFAULT_DBDIR);
+ } else if (stat(CONF_DEFAULT_FILE, &st) == 0) {
+ return util_conf_init_file(CONF_DEFAULT_FILE);
+ } else {
+ ERR2("couldn't initialize configuration, please provide %s option",
+ (allow_db ? "-c, -C, or -D" : "-c or -C"));
+ return KNOT_EINVAL;
+ }
+}
+
+void util_update_privileges(void)
+{
+ int uid, gid;
+ if (conf_user(conf(), &uid, &gid) != KNOT_EOK) {
+ return;
+ }
+
+ // Just try to alter process privileges if different from configured.
+ _unused_ int unused;
+ if ((gid_t)gid != getgid()) {
+ unused = setregid(gid, gid);
+ }
+ if ((uid_t)uid != getuid()) {
+ unused = setreuid(uid, uid);
+ }
+}
+
+void util_conf_deinit(void)
+{
+ log_close();
+ conf_update(NULL, CONF_UPD_FNONE);
+}
diff --git a/src/utils/common/util_conf.h b/src/utils/common/util_conf.h
new file mode 100644
index 0000000..a71d886
--- /dev/null
+++ b/src/utils/common/util_conf.h
@@ -0,0 +1,86 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "knot/conf/conf.h"
+
+/*!
+ * General note:
+ *
+ * Those functions operate and manipulate with conf() singleton.
+ * Thus they are not threadsafe etc.
+ * It is expected to use them just inside the main() function.
+ *
+ * Those functions already log any error, while returning an errcode.
+ */
+
+/*!
+ * \brief Return true if conf() for utilities already exists.
+ */
+bool util_conf_initialized(void);
+
+/*!
+ * \brief Initialize conf() for utilities from a configuration database.
+ *
+ * \param confdb Path to configuration database.
+ *
+ * \return KNOT_E*
+ */
+int util_conf_init_confdb(const char *confdb);
+
+/*!
+ * \brief Initialize conf() for utilities from a config file.
+ *
+ * \param conffile Path to Knot configuration file.
+ *
+ * \return KNOT_E*
+ */
+int util_conf_init_file(const char *conffile);
+
+/*!
+ * \brief Initialize basic conf() for utilities just with defaults and some database path.
+ *
+ * \param db_type Type of the database to be configured.
+ * \param db_path Path to that database.
+ *
+ * \return KNOT_E*
+ */
+int util_conf_init_justdb(const char *db_type, const char *db_path);
+
+/*!
+ * \brief Initialize conf() for utilities based on existence of confDB or config
+ * file on default locations.
+ *
+ * \param allow_db Direct path to a database is allowed.
+ *
+ * \return KNOT_E*
+ */
+int util_conf_init_default(bool allow_db);
+
+/*!
+ * \brief Set UID and GID of running utility process to what is configured...
+ *
+ * ...so that e.g. opened files have correct owner.
+ */
+void util_update_privileges(void);
+
+/*!
+ * \brief Deinitialize utility conf() from util_conf_init_*().
+ */
+void util_conf_deinit(void);
diff --git a/src/utils/kcatalogprint/main.c b/src/utils/kcatalogprint/main.c
new file mode 100644
index 0000000..39e53eb
--- /dev/null
+++ b/src/utils/kcatalogprint/main.c
@@ -0,0 +1,178 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "knot/catalog/catalog_db.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/common/util_conf.h"
+
+#define PROGRAM_NAME "kcatalogprint"
+
+static knot_dname_t *filter_member = NULL;
+static knot_dname_t *filter_catalog = NULL;
+
+static void print_help(void)
+{
+ printf("Usage: %s [-c | -C | -D <path>] [parameters]\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> Path to a textual configuration file.\n"
+ " (default %s)\n"
+ " -C, --confdb <dir> Path to a configuration database directory.\n"
+ " (default %s)\n"
+ " -D, --dir <path> Path to a catalog database directory, use default\n"
+ " configuration.\n"
+ " -a, --catalog <name> Filter the output by catalog zone name.\n"
+ " -m, --member <name> Filter the output by member zone name.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n",
+ PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
+}
+
+static void print_dname(const knot_dname_t *d)
+{
+ knot_dname_txt_storage_t tmp;
+ knot_dname_to_str(tmp, d, sizeof(tmp));
+ printf("%s ", tmp);
+}
+
+static int catalog_print_cb(const knot_dname_t *mem, const knot_dname_t *ow,
+ const knot_dname_t *cz, const char *group, void *ctx)
+{
+ if (filter_catalog != NULL && !knot_dname_is_equal(filter_catalog, cz)) {
+ return KNOT_EOK;
+ }
+ print_dname(mem);
+ print_dname(ow);
+ print_dname(cz);
+ printf("%s\n", group);
+ (*(ssize_t *)ctx)++;
+ return KNOT_EOK;
+}
+
+static void catalog_print(catalog_t *cat)
+{
+ ssize_t total = 0;
+
+ printf(";; <member zone> <record owner> <catalog zone> <group>\n");
+
+ if (cat != NULL) {
+ int ret = catalog_open(cat);
+ if (ret == KNOT_EOK) {
+ ret = catalog_apply(cat, filter_member, catalog_print_cb, &total, false);
+ }
+ if (ret != KNOT_EOK) {
+ ERR2("failed to print catalog (%s)", knot_strerror(ret));
+ return;
+ }
+ }
+
+ printf("Total records: %zd\n", total);
+}
+
+static void params_cleanup(void)
+{
+ free(filter_member);
+ free(filter_catalog);
+}
+
+int main(int argc, char *argv[])
+{
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "dir", required_argument, NULL, 'D' },
+ { "catalog", required_argument, NULL, 'a' },
+ { "member", required_argument, NULL, 'm' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "c:C:D:a:m:hV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ if (util_conf_init_file(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'C':
+ if (util_conf_init_confdb(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'D':
+ if (util_conf_init_justdb("catalog-db", optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'a':
+ free(filter_catalog);
+ filter_catalog = knot_dname_from_str_alloc(optarg);
+ knot_dname_to_lower(filter_catalog);
+ break;
+ case 'm':
+ free(filter_member);
+ filter_member = knot_dname_from_str_alloc(optarg);
+ knot_dname_to_lower(filter_member);
+ break;
+ case 'h':
+ print_help();
+ goto success;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ goto success;
+ default:
+ print_help();
+ goto failure;
+ }
+ }
+
+ // Backward compatibility.
+ if (argc - optind > 0) {
+ WARN2("obsolete parameter specified");
+ if (util_conf_init_justdb("catalog-db", argv[optind]) != KNOT_EOK) {
+ goto failure;
+ }
+ optind++;
+ }
+
+ if (util_conf_init_default(true) != KNOT_EOK) {
+ goto failure;
+ }
+
+ catalog_t c = { { 0 } };
+
+ char *db = conf_db(conf(), C_CATALOG_DB);
+ catalog_init(&c, db, 0); // mapsize grows automatically
+ free(db);
+ catalog_print(&c);
+ catalog_deinit(&c);
+
+success:
+ params_cleanup();
+ util_conf_deinit();
+ return EXIT_SUCCESS;
+failure:
+ params_cleanup();
+ util_conf_deinit();
+ return EXIT_FAILURE;
+}
diff --git a/src/utils/kdig/kdig_exec.c b/src/utils/kdig/kdig_exec.c
new file mode 100644
index 0000000..391e674
--- /dev/null
+++ b/src/utils/kdig/kdig_exec.c
@@ -0,0 +1,1309 @@
+/* Copyright (C) 2023 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 <stdlib.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include "utils/kdig/kdig_exec.h"
+#include "utils/common/exec.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "utils/common/sign.h"
+#include "libknot/libknot.h"
+#include "contrib/json.h"
+#include "contrib/sockaddr.h"
+#include "contrib/time.h"
+#include "contrib/ucw/lists.h"
+
+#if USE_DNSTAP
+# include "contrib/dnstap/convert.h"
+# include "contrib/dnstap/message.h"
+# include "contrib/dnstap/writer.h"
+
+static int write_dnstap(dt_writer_t *writer,
+ const bool is_query,
+ const uint8_t *wire,
+ const size_t wire_len,
+ net_t *net,
+ const struct timespec *mtime)
+{
+ Dnstap__Message msg;
+ Dnstap__Message__Type msg_type;
+ int ret;
+ int protocol = 0;
+
+ if (writer == NULL) {
+ return KNOT_EOK;
+ }
+
+ if (net->local == NULL) {
+ net_set_local_info(net);
+ }
+
+ msg_type = is_query ? DNSTAP__MESSAGE__TYPE__TOOL_QUERY :
+ DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE;
+
+ if (net->socktype == SOCK_DGRAM) {
+ protocol = IPPROTO_UDP;
+ } else if (net->socktype == SOCK_STREAM) {
+ protocol = IPPROTO_TCP;
+ }
+
+ ret = dt_message_fill(&msg, msg_type, net->local_info->ai_addr,
+ net->srv->ai_addr, protocol,
+ wire, wire_len, mtime);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ return dt_writer_write(writer, (const ProtobufCMessage *)&msg);
+}
+
+static float get_query_time(const Dnstap__Dnstap *frame)
+{
+ if (!frame->message->has_query_time_sec ||
+ !frame->message->has_query_time_nsec ||
+ !frame->message->has_response_time_sec ||
+ !frame->message->has_response_time_sec) {
+ return 0;
+ }
+
+ struct timespec from = {
+ .tv_sec = frame->message->query_time_sec,
+ .tv_nsec = frame->message->query_time_nsec
+ };
+
+ struct timespec to = {
+ .tv_sec = frame->message->response_time_sec,
+ .tv_nsec = frame->message->response_time_nsec
+ };
+
+ return time_diff_ms(&from, &to);
+}
+
+static void fill_remote_addr(net_t *net, Dnstap__Message *message, bool is_initiator)
+{
+ if (!message->has_socket_family || !message->has_socket_protocol) {
+ return;
+ }
+
+ if ((message->response_address.data == NULL && is_initiator) ||
+ message->query_address.data == NULL) {
+ return;
+ }
+
+ struct sockaddr_storage ss = { 0 };
+ int family = dt_family_decode(message->socket_family);
+ int proto = dt_protocol_decode(message->socket_protocol);
+ int sock_type = 0;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ sock_type = SOCK_STREAM;
+ break;
+ case IPPROTO_UDP:
+ sock_type = SOCK_DGRAM;
+ break;
+ default:
+ break;
+ }
+
+ ProtobufCBinaryData *addr = NULL;
+ uint32_t port = 0;
+ if (is_initiator) {
+ addr = &message->response_address;
+ port = message->response_port;
+ } else {
+ addr = &message->query_address;
+ port = message->query_port;
+ }
+
+ sockaddr_set_raw(&ss, family, addr->data, addr->len);
+ sockaddr_port_set(&ss, port);
+
+ get_addr_str(&ss, sock_type, &net->remote_str);
+}
+
+static int process_dnstap(const query_t *query)
+{
+ dt_reader_t *reader = query->dt_reader;
+
+ if (query->dt_reader == NULL) {
+ return -1;
+ }
+
+ bool first_message = true;
+
+ for (;;) {
+ Dnstap__Dnstap *frame = NULL;
+ Dnstap__Message *message = NULL;
+ ProtobufCBinaryData *wire = NULL;
+ bool is_query;
+ bool is_initiator;
+
+ // Read next message.
+ int ret = dt_reader_read(reader, &frame);
+ if (ret == KNOT_EOF) {
+ break;
+ } else if (ret != KNOT_EOK) {
+ ERR("can't read dnstap message");
+ break;
+ }
+
+ // Check for dnstap message.
+ if (frame->type == DNSTAP__DNSTAP__TYPE__MESSAGE) {
+ message = frame->message;
+ } else {
+ WARN("ignoring non-dnstap message");
+ dt_reader_free_frame(reader, &frame);
+ continue;
+ }
+
+ // Check for the type of dnstap message.
+ if (message->has_response_message) {
+ wire = &message->response_message;
+ is_query = false;
+ } else if (message->has_query_message) {
+ wire = &message->query_message;
+ is_query = true;
+ } else {
+ WARN("dnstap frame contains no message");
+ dt_reader_free_frame(reader, &frame);
+ continue;
+ }
+
+ // Ignore query message if requested.
+ if (is_query && !query->style.show_query) {
+ dt_reader_free_frame(reader, &frame);
+ continue;
+ }
+
+ // Get the message role.
+ is_initiator = dt_message_role_is_initiator(message->type);
+
+ // Create dns packet based on dnstap wire data.
+ knot_pkt_t *pkt = knot_pkt_new(wire->data, wire->len, NULL);
+ if (pkt == NULL) {
+ ERR("can't allocate packet");
+ dt_reader_free_frame(reader, &frame);
+ break;
+ }
+
+ // Parse packet and reconstruct required data.
+ ret = knot_pkt_parse(pkt, KNOT_PF_NOCANON);
+ if (ret == KNOT_EOK || ret == KNOT_ETRAIL) {
+ time_t timestamp = 0;
+ float query_time = 0.0;
+ net_t net_ctx = { 0 };
+
+ if (ret == KNOT_ETRAIL) {
+ WARN("malformed message (%s)", knot_strerror(ret));
+ }
+
+ if (is_query) {
+ if (message->has_query_time_sec) {
+ timestamp = message->query_time_sec;
+ }
+ } else {
+ if (message->has_response_time_sec) {
+ timestamp = message->response_time_sec;
+ }
+ query_time = get_query_time(frame);
+ }
+
+ // Prepare connection information string.
+ fill_remote_addr(&net_ctx, message, is_initiator);
+
+ if (first_message) {
+ first_message = false;
+ } else {
+ printf("\n");
+ }
+
+ print_packet(pkt, &net_ctx, pkt->size, query_time, timestamp,
+ is_query ^ is_initiator, &query->style);
+
+ net_clean(&net_ctx);
+ } else {
+ ERR("can't print dnstap message");
+ }
+
+ knot_pkt_free(pkt);
+ dt_reader_free_frame(reader, &frame);
+ }
+
+ return 0;
+}
+#endif // USE_DNSTAP
+
+static int add_query_edns(knot_pkt_t *packet, const query_t *query, uint16_t max_size)
+{
+ /* Initialize OPT RR. */
+ knot_rrset_t opt_rr;
+ int ret = knot_edns_init(&opt_rr, max_size, 0,
+ query->edns > -1 ? query->edns : 0, &packet->mm);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (query->flags.do_flag) {
+ knot_edns_set_do(&opt_rr);
+ }
+
+ /* Append NSID. */
+ if (query->nsid) {
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID,
+ 0, NULL, &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+ }
+
+ /* Append EDNS-client-subnet. */
+ if (query->subnet.family != AF_UNSPEC) {
+ uint16_t size = knot_edns_client_subnet_size(&query->subnet);
+ uint8_t data[size];
+
+ ret = knot_edns_client_subnet_write(data, size, &query->subnet);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET,
+ size, data, &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+ }
+
+ /* Append a cookie option if present. */
+ if (query->cc.len > 0) {
+ uint16_t size = knot_edns_cookie_size(&query->cc, &query->sc);
+ uint8_t data[size];
+
+ ret = knot_edns_cookie_write(data, size, &query->cc, &query->sc);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_COOKIE,
+ size, data, &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+ }
+
+ /* Append EDNS Padding. */
+ int padding = query->padding;
+ if (padding != -3 && query->alignment > 0) {
+ padding = knot_edns_alignment_size(packet->size,
+ knot_rrset_size(&opt_rr),
+ query->alignment);
+ } else if (query->padding == -2 || (query->padding == -1 && query->tls.enable)) {
+ padding = knot_pkt_default_padding_size(packet, &opt_rr);
+ }
+ if (padding > -1) {
+ uint8_t zeros[padding];
+ memset(zeros, 0, sizeof(zeros));
+
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_PADDING,
+ padding, zeros, &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+ }
+
+ /* Append custom EDNS options. */
+ node_t *node;
+ WALK_LIST(node, query->edns_opts) {
+ ednsopt_t *opt = (ednsopt_t *)node;
+ ret = knot_edns_add_option(&opt_rr, opt->code, opt->length,
+ opt->data, &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ return ret;
+ }
+ }
+
+ /* Add prepared OPT to packet. */
+ ret = knot_pkt_put(packet, KNOT_COMPR_HINT_NONE, &opt_rr, KNOT_PF_FREE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&opt_rr, &packet->mm);
+ }
+
+ return ret;
+}
+
+static bool do_padding(const query_t *query)
+{
+ return (query->padding != -3) && // Disabled padding.
+ (query->padding > -1 || query->alignment > 0 || // Explicit padding.
+ query->padding == -2 || // Default padding.
+ (query->padding == -1 && query->tls.enable)); // TLS automatic.
+}
+
+static bool use_edns(const query_t *query)
+{
+ return query->edns > -1 || query->udp_size > -1 || query->nsid ||
+ query->flags.do_flag || query->subnet.family != AF_UNSPEC ||
+ query->cc.len > 0 || do_padding(query) ||
+ !ednsopt_list_empty(&query->edns_opts);
+}
+
+static knot_pkt_t *create_query_packet(const query_t *query)
+{
+ // Set packet buffer size.
+ uint16_t max_size;
+ if (query->udp_size < 0) {
+ if (use_edns(query)) {
+ max_size = DEFAULT_EDNS_SIZE;
+ } else {
+ max_size = DEFAULT_UDP_SIZE;
+ }
+ } else {
+ max_size = query->udp_size;
+ }
+
+ // Create packet skeleton.
+ knot_pkt_t *packet = create_empty_packet(max_size);
+ if (packet == NULL) {
+ return NULL;
+ }
+
+ // Set ID = 0 for packet send over HTTPS
+ // Due HTTP cache it is convenient to set the query ID to 0 - GET messages has same header then
+#if defined(LIBNGHTTP2) || defined(ENABLE_QUIC)
+ if (query->https.enable || query->quic.enable) {
+ knot_wire_set_id(packet->wire, 0);
+ }
+#endif
+
+ // Set flags to wireformat.
+ if (query->flags.aa_flag) {
+ knot_wire_set_aa(packet->wire);
+ }
+ if (query->flags.tc_flag) {
+ knot_wire_set_tc(packet->wire);
+ }
+ if (query->flags.rd_flag) {
+ knot_wire_set_rd(packet->wire);
+ }
+ if (query->flags.ra_flag) {
+ knot_wire_set_ra(packet->wire);
+ }
+ if (query->flags.z_flag) {
+ knot_wire_set_z(packet->wire);
+ }
+ if (query->flags.ad_flag) {
+ knot_wire_set_ad(packet->wire);
+ }
+ if (query->flags.cd_flag) {
+ knot_wire_set_cd(packet->wire);
+ }
+
+ // Set NOTIFY opcode.
+ if (query->notify) {
+ knot_wire_set_opcode(packet->wire, KNOT_OPCODE_NOTIFY);
+ }
+
+ // Set packet question if available.
+ knot_dname_t *qname = NULL;
+ if (query->owner != NULL) {
+ qname = knot_dname_from_str_alloc(query->owner);
+ if (qname == NULL) {
+ ERR("'%s' is not a valid domain name", query->owner);
+ knot_pkt_free(packet);
+ return NULL;
+ }
+
+ int ret = knot_pkt_put_question(packet, qname, query->class_num,
+ query->type_num);
+ if (ret != KNOT_EOK) {
+ knot_dname_free(qname, NULL);
+ knot_pkt_free(packet);
+ return NULL;
+ }
+ }
+
+ // For IXFR query or NOTIFY query with SOA serial, add a proper section.
+ if (query->serial >= 0) {
+ if (query->notify) {
+ knot_pkt_begin(packet, KNOT_ANSWER);
+ } else {
+ knot_pkt_begin(packet, KNOT_AUTHORITY);
+ }
+
+ // SOA rdata in wireformat.
+ uint8_t wire[22] = { 0x0 };
+
+ // Create rrset with SOA record.
+ knot_rrset_t *soa = knot_rrset_new(qname,
+ KNOT_RRTYPE_SOA,
+ query->class_num,
+ 0,
+ &packet->mm);
+ knot_dname_free(qname, NULL);
+ if (soa == NULL) {
+ knot_pkt_free(packet);
+ return NULL;
+ }
+
+ // Fill in blank SOA rdata to rrset.
+ int ret = knot_rrset_add_rdata(soa, wire, sizeof(wire), &packet->mm);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(soa, &packet->mm);
+ knot_pkt_free(packet);
+ return NULL;
+ }
+
+ // Set SOA serial.
+ knot_soa_serial_set(soa->rrs.rdata, query->serial);
+
+ ret = knot_pkt_put(packet, KNOT_COMPR_HINT_NONE, soa, KNOT_PF_FREE);
+ if (ret != KNOT_EOK) {
+ knot_rrset_free(soa, &packet->mm);
+ knot_pkt_free(packet);
+ return NULL;
+ }
+
+ free(soa);
+ } else {
+ knot_dname_free(qname, NULL);
+ }
+
+ // Begin additional section
+ knot_pkt_begin(packet, KNOT_ADDITIONAL);
+
+ // Create EDNS section if required.
+ if (use_edns(query)) {
+ int ret = add_query_edns(packet, query, max_size);
+ if (ret != KNOT_EOK) {
+ ERR("can't set up EDNS section");
+ knot_pkt_free(packet);
+ return NULL;
+ }
+ }
+
+ return packet;
+}
+
+static bool check_reply_id(const knot_pkt_t *reply,
+ const knot_pkt_t *query)
+{
+ uint16_t query_id = knot_wire_get_id(query->wire);
+ uint16_t reply_id = knot_wire_get_id(reply->wire);
+
+ if (reply_id != query_id) {
+ WARN("reply ID (%u) is different from query ID (%u)",
+ reply_id, query_id);
+ return false;
+ }
+
+ return true;
+}
+
+static void check_reply_qr(const knot_pkt_t *reply)
+{
+ if (!knot_wire_get_qr(reply->wire)) {
+ WARN("response QR bit not set");
+ }
+}
+
+static void check_reply_question(const knot_pkt_t *reply,
+ const knot_pkt_t *query)
+{
+ if (knot_wire_get_qdcount(reply->wire) < 1) {
+ WARN("response doesn't have question section");
+ return;
+ }
+
+ if (!knot_dname_is_equal(knot_pkt_wire_qname(reply), knot_pkt_wire_qname(query)) ||
+ knot_pkt_qclass(reply) != knot_pkt_qclass(query) ||
+ knot_pkt_qtype(reply) != knot_pkt_qtype(query)) {
+ WARN("query/response question sections are different");
+ return;
+ }
+}
+
+static int64_t first_serial_check(const knot_pkt_t *reply, const knot_pkt_t *query)
+{
+ const knot_pktsection_t *answer = knot_pkt_section(reply, KNOT_ANSWER);
+
+ if (answer->count <= 0) {
+ return -1;
+ }
+
+ const knot_rrset_t *first = knot_pkt_rr(answer, 0);
+
+ if (first->type != KNOT_RRTYPE_SOA) {
+ return -1;
+ } else {
+ if (!knot_dname_is_case_equal(first->owner, knot_pkt_qname(query))) {
+ WARN("leading SOA owner not matching the requested zone name");
+ }
+
+ return knot_soa_serial(first->rrs.rdata);
+ }
+}
+
+static bool finished_xfr(const uint32_t serial, const knot_pkt_t *reply,
+ const knot_pkt_t *query, const size_t msg_count, bool is_ixfr)
+{
+ const knot_pktsection_t *answer = knot_pkt_section(reply, KNOT_ANSWER);
+
+ if (answer->count <= 0) {
+ return false;
+ }
+
+ const knot_rrset_t *last = knot_pkt_rr(answer, answer->count - 1);
+
+ if (last->type != KNOT_RRTYPE_SOA) {
+ return false;
+ } else if (answer->count == 1 && msg_count == 1) {
+ return is_ixfr;
+ } else {
+ if (!knot_dname_is_case_equal(last->owner, knot_pkt_qname(query))) {
+ WARN("final SOA owner not matching the requested zone name");
+ }
+
+ return knot_soa_serial(last->rrs.rdata) == serial;
+ }
+}
+
+static int sign_query(knot_pkt_t *pkt, const query_t *query, sign_context_t *ctx)
+{
+ if (query->tsig_key.name == NULL) {
+ return KNOT_EOK;
+ }
+
+ int ret = sign_context_init_tsig(ctx, &query->tsig_key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = sign_packet(pkt, ctx);
+ if (ret != KNOT_EOK) {
+ sign_context_deinit(ctx);
+ return ret;
+ }
+
+ return KNOT_EOK;
+}
+
+static void net_close_keepopen(net_t *net, const query_t *query)
+{
+ if (!query->keepopen) {
+ net_close(net);
+ }
+}
+
+static int process_query_packet(const knot_pkt_t *query,
+ net_t *net,
+ const query_t *query_ctx,
+ const bool ignore_tc,
+ const sign_context_t *sign_ctx,
+ const style_t *style)
+{
+ struct timespec t_start, t_query, t_query_full, t_end, t_end_full;
+ time_t timestamp;
+ knot_pkt_t *reply = NULL;
+ uint8_t in[MAX_PACKET_SIZE];
+ int in_len;
+ int ret;
+
+ // Get start query time.
+ timestamp = time(NULL);
+ t_start = time_now();
+
+ // Connect to the server if not already connected.
+ if (net->sockfd < 0) {
+ ret = net_connect(net);
+ if (ret != KNOT_EOK) {
+ return -1;
+ }
+ }
+
+ // Send query packet.
+ ret = net_send(net, query->wire, query->size);
+ if (ret != KNOT_EOK) {
+ net_close(net);
+ return -1;
+ }
+
+ // Get stop query time and start reply time.
+ t_query = time_now();
+ t_query_full = time_diff(&t_start, &t_query);
+ t_query_full.tv_sec += timestamp;
+
+#if USE_DNSTAP
+ // Make the dnstap copy of the query.
+ write_dnstap(query_ctx->dt_writer, true, query->wire, query->size,
+ net, &t_query_full);
+#endif // USE_DNSTAP
+
+ // Print query packet if required.
+ if (style->show_query && style->format != FORMAT_JSON) {
+ // Create copy of query packet for parsing.
+ knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL);
+ if (q != NULL) {
+ if (knot_pkt_parse(q, KNOT_PF_NOCANON) == KNOT_EOK) {
+ print_packet(q, net, query->size,
+ time_diff_ms(&t_start, &t_query),
+ timestamp, false, style);
+ } else {
+ ERR("can't print query packet");
+ }
+ knot_pkt_free(q);
+ } else {
+ ERR("can't print query packet");
+ }
+
+ printf("\n");
+ }
+
+ // Loop over incoming messages, unless reply id is correct or timeout.
+ while (true) {
+ reply = NULL;
+
+ // Receive a reply message.
+ in_len = net_receive(net, in, sizeof(in));
+ if (in_len <= 0) {
+ goto fail;
+ }
+
+ // Get stop reply time.
+ t_end = time_now();
+ t_end_full = time_diff(&t_start, &t_end);
+ t_end_full.tv_sec += timestamp;
+
+#if USE_DNSTAP
+ // Make the dnstap copy of the response.
+ write_dnstap(query_ctx->dt_writer, false, in, in_len, net,
+ &t_end_full);
+#endif // USE_DNSTAP
+
+ // Create reply packet structure to fill up.
+ reply = knot_pkt_new(in, in_len, NULL);
+ if (reply == NULL) {
+ ERR("internal error (%s)", knot_strerror(KNOT_ENOMEM));
+ goto fail;
+ }
+
+ // Parse reply to the packet structure.
+ ret = knot_pkt_parse(reply, KNOT_PF_NOCANON);
+ if (ret == KNOT_ETRAIL) {
+ WARN("malformed reply packet (%s)", knot_strerror(ret));
+ } else if (ret != KNOT_EOK) {
+ ERR("malformed reply packet from %s", net->remote_str);
+ goto fail;
+ }
+
+ // Compare reply header id.
+ if (check_reply_id(reply, query)) {
+ break;
+ // Check for timeout.
+ } else if (time_diff_ms(&t_query, &t_end) > 1000 * net->wait) {
+ goto fail;
+ }
+
+ knot_pkt_free(reply);
+ }
+
+ // Check for TC bit and repeat query with TCP if required.
+ if (knot_wire_get_tc(reply->wire) != 0 &&
+ ignore_tc == false && net->socktype == SOCK_DGRAM) {
+ printf("\n");
+ WARN("truncated reply from %s, retrying over TCP\n",
+ net->remote_str);
+ knot_pkt_free(reply);
+ net_close_keepopen(net, query_ctx);
+
+ net->socktype = SOCK_STREAM;
+
+ return process_query_packet(query, net, query_ctx, true,
+ sign_ctx, style);
+ }
+
+ // Check for question sections equality.
+ check_reply_question(reply, query);
+
+ // Check QR bit
+ check_reply_qr(reply);
+
+ // Print reply packet.
+ if (style->format != FORMAT_JSON) {
+ print_packet(reply, net, in_len, time_diff_ms(&t_start, &t_end),
+ timestamp, true, style);
+ } else {
+ knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL);
+ (void)knot_pkt_parse(q, KNOT_PF_NOCANON);
+ print_packets_json(q, reply, net, timestamp, style);
+ knot_pkt_free(q);
+ }
+
+ // Verify signature if a key was specified.
+ if (sign_ctx->digest != NULL) {
+ ret = verify_packet(reply, sign_ctx);
+ if (ret != KNOT_EOK) {
+ WARN("reply verification for %s (%s)",
+ net->remote_str, knot_strerror(ret));
+ }
+ }
+
+ // Check for BADCOOKIE RCODE and repeat query with the new cookie if required.
+ if (knot_pkt_ext_rcode(reply) == KNOT_RCODE_BADCOOKIE && query_ctx->badcookie > 0) {
+ printf("\n");
+ WARN("bad cookie from %s, retrying with the received one\n",
+ net->remote_str);
+ net_close_keepopen(net, query_ctx);
+
+ // Prepare new query context.
+ query_t new_ctx = *query_ctx;
+
+ uint8_t *opt = knot_pkt_edns_option(reply, KNOT_EDNS_OPTION_COOKIE);
+ if (opt == NULL) {
+ ERR("bad cookie, missing EDNS section");
+ goto fail;
+ }
+
+ const uint8_t *data = knot_edns_opt_get_data(opt);
+ uint16_t data_len = knot_edns_opt_get_length(opt);
+ ret = knot_edns_cookie_parse(&new_ctx.cc, &new_ctx.sc, data, data_len);
+ if (ret != KNOT_EOK) {
+ ERR("bad cookie, missing EDNS cookie option");
+ goto fail;
+ }
+ knot_pkt_free(reply);
+
+ // Restore the original client cookie.
+ new_ctx.cc = query_ctx->cc;
+
+ new_ctx.badcookie--;
+
+ knot_pkt_t *new_query = create_query_packet(&new_ctx);
+ ret = process_query_packet(new_query, net, &new_ctx, ignore_tc,
+ sign_ctx, style);
+ knot_pkt_free(new_query);
+
+ return ret;
+ }
+
+ knot_pkt_free(reply);
+ net_close_keepopen(net, query_ctx);
+
+ return 0;
+
+fail:
+ if (style->format == FORMAT_JSON) {
+ knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL);
+ (void)knot_pkt_parse(q, KNOT_PF_NOCANON);
+ print_packets_json(q, reply, net, timestamp, style);
+ knot_pkt_free(q);
+ }
+
+ knot_pkt_free(reply);
+ net_close(net);
+
+ return -1;
+}
+
+static int process_query(const query_t *query, net_t *net)
+{
+ node_t *server;
+ knot_pkt_t *out_packet;
+ int ret;
+
+ // Create query packet.
+ out_packet = create_query_packet(query);
+ if (out_packet == NULL) {
+ ERR("can't create query packet");
+ return -1;
+ }
+
+ // Sign the query.
+ sign_context_t sign_ctx = { 0 };
+ ret = sign_query(out_packet, query, &sign_ctx);
+ if (ret != KNOT_EOK) {
+ ERR("can't sign the packet (%s)", knot_strerror(ret));
+ return -1;
+ }
+
+ // Reuse previous connection if available.
+ if (net->sockfd >= 0) {
+ DBG("Querying for owner(%s), class(%u), type(%u), reused connection",
+ query->owner, query->class_num, query->type_num);
+
+ ret = process_query_packet(out_packet, net, query, query->ignore_tc,
+ &sign_ctx, &query->style);
+ goto finish;
+ }
+
+ // Get connection parameters.
+ int socktype = get_socktype(query->protocol, query->type_num);
+ int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE;
+
+ // Loop over server list to process query.
+ WALK_LIST(server, query->servers) {
+ srv_info_t *remote = (srv_info_t *)server;
+ int iptype = get_iptype(query->ip, remote);
+
+ DBG("Querying for owner(%s), class(%u), type(%u), server(%s), "
+ "port(%s), protocol(%s)", query->owner, query->class_num,
+ query->type_num, remote->name, remote->service,
+ get_sockname(socktype));
+
+ // Loop over the number of retries.
+ for (size_t i = 0; i <= query->retries; i++) {
+ // Initialize network structure for current server.
+ ret = net_init(query->local, remote, iptype, socktype,
+ query->wait, flags, &query->tls,
+ &query->https, &query->quic,
+ (struct sockaddr *)&query->proxy.src,
+ (struct sockaddr *)&query->proxy.dst,
+ net);
+ if (ret != KNOT_EOK) {
+ if (ret == KNOT_NET_EADDR) {
+ // Requested address family not available.
+ goto next_server;
+ }
+ continue;
+ }
+
+ // Loop over all resolved addresses for remote.
+ while (net->srv != NULL) {
+ ret = process_query_packet(out_packet, net,
+ query,
+ query->ignore_tc,
+ &sign_ctx,
+ &query->style);
+ // If error try next resolved address.
+ if (ret != 0) {
+ net->srv = net->srv->ai_next;
+ if (net->srv != NULL && query->style.show_query) {
+ printf("\n");
+ }
+
+ continue;
+ }
+
+ break;
+ }
+
+ // Success.
+ if (ret == 0) {
+ goto finish;
+ }
+
+ if (i < query->retries) {
+ DBG("retrying server %s@%s(%s)",
+ remote->name, remote->service,
+ get_sockname(socktype));
+
+ if (query->style.show_query) {
+ printf("\n");
+ }
+ }
+
+ net_clean(net);
+ }
+
+ ERR("failed to query server %s@%s(%s)",
+ remote->name, remote->service, get_sockname(socktype));
+
+ // If not last server, print separation.
+ if (server->next->next && query->style.show_query) {
+ printf("\n");
+ }
+next_server:
+ continue;
+ }
+finish:
+ if (!query->keepopen || net->sockfd < 0) {
+ net_clean(net);
+ }
+ sign_context_deinit(&sign_ctx);
+ knot_pkt_free(out_packet);
+
+ if (ret == KNOT_NET_EADDR) {
+ WARN("no servers to query");
+ }
+
+ return ret;
+}
+
+static int process_xfr_packet(const knot_pkt_t *query,
+ net_t *net,
+ const query_t *query_ctx,
+ const sign_context_t *sign_ctx,
+ const style_t *style)
+{
+ struct timespec t_start, t_query, t_query_full, t_end, t_end_full;
+ time_t timestamp;
+ knot_pkt_t *reply = NULL;
+ uint8_t in[MAX_PACKET_SIZE];
+ int in_len;
+ int ret;
+ int64_t serial = 0;
+ size_t total_len = 0;
+ size_t msg_count = 0;
+ size_t rr_count = 0;
+ jsonw_t *w = NULL;
+
+ // Get start query time.
+ timestamp = time(NULL);
+ t_start = time_now();
+
+ // Connect to the server if not already connected.
+ if (net->sockfd < 0) {
+ ret = net_connect(net);
+ if (ret != KNOT_EOK) {
+ return -1;
+ }
+ }
+
+ // Send query packet.
+ ret = net_send(net, query->wire, query->size);
+ if (ret != KNOT_EOK) {
+ net_close(net);
+ return -1;
+ }
+
+ // Get stop query time and start reply time.
+ t_query = time_now();
+ t_query_full = time_diff(&t_start, &t_query);
+ t_query_full.tv_sec += timestamp;
+
+#if USE_DNSTAP
+ // Make the dnstap copy of the query.
+ write_dnstap(query_ctx->dt_writer, true, query->wire, query->size,
+ net, &t_query_full);
+#endif // USE_DNSTAP
+
+ // Print query packet if required.
+ if (style->show_query && style->format != FORMAT_JSON) {
+ // Create copy of query packet for parsing.
+ knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL);
+ if (q != NULL) {
+ if (knot_pkt_parse(q, KNOT_PF_NOCANON) == KNOT_EOK) {
+ print_packet(q, net, query->size,
+ time_diff_ms(&t_start, &t_query),
+ timestamp, false, style);
+ } else {
+ ERR("can't print query packet");
+ }
+ knot_pkt_free(q);
+ } else {
+ ERR("can't print query packet");
+ }
+
+ printf("\n");
+ }
+
+ // Loop over reply messages unless first and last SOA serials differ.
+ while (true) {
+ reply = NULL;
+
+ // Receive a reply message.
+ in_len = net_receive(net, in, sizeof(in));
+ if (in_len <= 0) {
+ goto fail;
+ }
+
+ // Get stop message time.
+ t_end = time_now();
+ t_end_full = time_diff(&t_start, &t_end);
+ t_end_full.tv_sec += timestamp;
+
+#if USE_DNSTAP
+ // Make the dnstap copy of the response.
+ write_dnstap(query_ctx->dt_writer, false, in, in_len, net,
+ &t_end_full);
+#endif // USE_DNSTAP
+
+ // Create reply packet structure to fill up.
+ reply = knot_pkt_new(in, in_len, NULL);
+ if (reply == NULL) {
+ ERR("internal error (%s)", knot_strerror(KNOT_ENOMEM));
+ goto fail;
+ }
+
+ // Parse reply to the packet structure.
+ ret = knot_pkt_parse(reply, KNOT_PF_NOCANON);
+ if (ret == KNOT_ETRAIL) {
+ WARN("malformed reply packet (%s)", knot_strerror(ret));
+ } else if (ret != KNOT_EOK) {
+ ERR("malformed reply packet from %s", net->remote_str);
+ goto fail;
+ }
+
+ // Compare reply header id.
+ if (check_reply_id(reply, query) == false) {
+ ERR("reply ID mismatch from %s", net->remote_str);
+ goto fail;
+ }
+
+ // Print leading transfer information.
+ if (msg_count == 0) {
+ if (style->format != FORMAT_JSON) {
+ print_header_xfr(query, style);
+ } else {
+ knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL);
+ (void)knot_pkt_parse(q, KNOT_PF_NOCANON);
+ w = print_header_xfr_json(q, timestamp, style);
+ knot_pkt_free(q);
+ }
+ }
+
+ // Check for error reply.
+ if (knot_pkt_ext_rcode(reply) != KNOT_RCODE_NOERROR) {
+ ERR("server replied with error '%s'",
+ knot_pkt_ext_rcode_name(reply));
+ goto fail;
+ }
+
+ // The first message has a special treatment.
+ if (msg_count == 0) {
+ // Verify 1. signature if a key was specified.
+ if (sign_ctx->digest != NULL) {
+ ret = verify_packet(reply, sign_ctx);
+ if (ret != KNOT_EOK) {
+ style_t tsig_style = {
+ .format = style->format,
+ .style = style->style,
+ .show_tsig = true
+ };
+ if (style->format != FORMAT_JSON) {
+ print_data_xfr(reply, &tsig_style);
+ }
+
+ ERR("reply verification for %s (%s)",
+ net->remote_str, knot_strerror(ret));
+ goto fail;
+ }
+ }
+
+ // Read first SOA serial.
+ serial = first_serial_check(reply, query);
+
+ if (serial < 0) {
+ ERR("first answer record from %s isn't SOA",
+ net->remote_str);
+ goto fail;
+ }
+
+ // Check for question sections equality.
+ check_reply_question(reply, query);
+
+ // Check QR bit
+ check_reply_qr(reply);
+ }
+
+ msg_count++;
+ rr_count += knot_wire_get_ancount(reply->wire);
+ total_len += in_len;
+
+ // Print reply packet.
+ if (style->format != FORMAT_JSON) {
+ print_data_xfr(reply, style);
+ } else {
+ print_data_xfr_json(w, reply, timestamp);
+ }
+
+ // Check for finished transfer.
+ if (finished_xfr(serial, reply, query, msg_count, query_ctx->serial != -1)) {
+ knot_pkt_free(reply);
+ break;
+ }
+
+ knot_pkt_free(reply);
+ reply = NULL;
+ }
+
+ // Print full transfer information.
+ t_end = time_now();
+ if (style->format != FORMAT_JSON) {
+ print_footer_xfr(total_len, msg_count, rr_count, net,
+ time_diff_ms(&t_query, &t_end), timestamp, style);
+ } else {
+ print_footer_xfr_json(&w, style);
+ }
+
+ net_close_keepopen(net, query_ctx);
+
+ return 0;
+
+fail:
+ // Print partial transfer information.
+ t_end = time_now();
+ if (style->format != FORMAT_JSON) {
+ print_footer_xfr(total_len, msg_count, rr_count, net,
+ time_diff_ms(&t_query, &t_end), timestamp, style);
+ } else {
+ print_data_xfr_json(w, reply, timestamp);
+ print_footer_xfr_json(&w, style);
+ }
+
+ knot_pkt_free(reply);
+ net_close(net);
+ free(w);
+
+ return -1;
+}
+
+static int process_xfr(const query_t *query, net_t *net)
+{
+ knot_pkt_t *out_packet;
+ int ret;
+
+ // Create query packet.
+ out_packet = create_query_packet(query);
+ if (out_packet == NULL) {
+ ERR("can't create query packet");
+ return -1;
+ }
+
+ // Sign the query.
+ sign_context_t sign_ctx = { 0 };
+ ret = sign_query(out_packet, query, &sign_ctx);
+ if (ret != KNOT_EOK) {
+ ERR("can't sign the packet (%s)", knot_strerror(ret));
+ return -1;
+ }
+
+ // Reuse previous connection if available.
+ if (net->sockfd >= 0) {
+ DBG("Querying for owner(%s), class(%u), type(%u), reused connection",
+ query->owner, query->class_num, query->type_num);
+
+ ret = process_xfr_packet(out_packet, net, query,
+ &sign_ctx, &query->style);
+ goto finish;
+ }
+
+ // Get connection parameters.
+ int socktype = get_socktype(query->protocol, query->type_num);
+ int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE;
+
+ // Use the first nameserver from the list.
+ srv_info_t *remote = HEAD(query->servers);
+ int iptype = get_iptype(query->ip, remote);
+
+ DBG("Querying for owner(%s), class(%u), type(%u), server(%s), "
+ "port(%s), protocol(%s)", query->owner, query->class_num,
+ query->type_num, remote->name, remote->service,
+ get_sockname(socktype));
+
+ // Initialize network structure.
+ ret = net_init(query->local, remote, iptype, socktype, query->wait,
+ flags, &query->tls, &query->https, &query->quic,
+ (struct sockaddr *)&query->proxy.src,
+ (struct sockaddr *)&query->proxy.dst,
+ net);
+ if (ret != KNOT_EOK) {
+ sign_context_deinit(&sign_ctx);
+ knot_pkt_free(out_packet);
+ return -1;
+ }
+
+ // Loop over all resolved addresses for remote.
+ while (net->srv != NULL) {
+ ret = process_xfr_packet(out_packet, net,
+ query,
+ &sign_ctx,
+ &query->style);
+ // If error try next resolved address.
+ if (ret != 0) {
+ net->srv = (net->srv)->ai_next;
+ continue;
+ }
+
+ break;
+ }
+
+ if (ret != 0) {
+ ERR("failed to query server %s@%s(%s)",
+ remote->name, remote->service, get_sockname(socktype));
+ }
+finish:
+ if (!query->keepopen || net->sockfd < 0) {
+ net_clean(net);
+ }
+ sign_context_deinit(&sign_ctx);
+ knot_pkt_free(out_packet);
+
+ return ret;
+}
+
+int kdig_exec(const kdig_params_t *params)
+{
+ node_t *n;
+ net_t net = { .sockfd = -1 };
+
+ if (params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ bool success = true;
+
+ // Loop over query list.
+ WALK_LIST(n, params->queries) {
+ query_t *query = (query_t *)n;
+
+ int ret = -1;
+ switch (query->operation) {
+ case OPERATION_QUERY:
+ ret = process_query(query, &net);
+ break;
+ case OPERATION_XFR:
+ ret = process_xfr(query, &net);
+ break;
+#if USE_DNSTAP
+ case OPERATION_LIST_DNSTAP:
+ ret = process_dnstap(query);
+ break;
+#endif // USE_DNSTAP
+ default:
+ ERR("unsupported operation");
+ break;
+ }
+
+ // All operations must succeed.
+ if (ret != 0) {
+ success = false;
+ }
+
+ // If not last query, print separation.
+ if (n->next->next && params->config->style.format == FORMAT_FULL) {
+ printf("\n");
+ }
+ }
+
+ if (net.sockfd >= 0) {
+ net_close(&net);
+ net_clean(&net);
+ }
+
+ return success ? KNOT_EOK : KNOT_ERROR;
+}
diff --git a/src/utils/kdig/kdig_exec.h b/src/utils/kdig/kdig_exec.h
new file mode 100644
index 0000000..99167ce
--- /dev/null
+++ b/src/utils/kdig/kdig_exec.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "utils/kdig/kdig_params.h"
+
+int kdig_exec(const kdig_params_t *params);
diff --git a/src/utils/kdig/kdig_main.c b/src/utils/kdig/kdig_main.c
new file mode 100644
index 0000000..534d50e
--- /dev/null
+++ b/src/utils/kdig/kdig_main.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 2018 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 <stdlib.h>
+
+#include "libdnssec/crypto.h"
+#include "utils/kdig/kdig_params.h"
+#include "utils/kdig/kdig_exec.h"
+#include "libknot/libknot.h"
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+
+ tzset();
+
+ kdig_params_t params;
+ if (kdig_parse(&params, argc, argv) == KNOT_EOK) {
+ if (!params.stop) {
+ dnssec_crypto_init();
+ if (kdig_exec(&params) != KNOT_EOK) {
+ ret = EXIT_FAILURE;
+ }
+ dnssec_crypto_cleanup();
+ }
+ } else {
+ ret = EXIT_FAILURE;
+ }
+
+ kdig_clean(&params);
+ return ret;
+}
diff --git a/src/utils/kdig/kdig_params.c b/src/utils/kdig/kdig_params.c
new file mode 100644
index 0000000..fce9405
--- /dev/null
+++ b/src/utils/kdig/kdig_params.c
@@ -0,0 +1,2740 @@
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "utils/kdig/kdig_params.h"
+#include "utils/common/cert.h"
+#include "utils/common/hex.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "utils/common/params.h"
+#include "utils/common/resolv.h"
+#include "libknot/descriptor.h"
+#include "libknot/libknot.h"
+#include "contrib/base64.h"
+#include "contrib/sockaddr.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "contrib/time.h"
+#include "contrib/ucw/lists.h"
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+
+#define PROGRAM_NAME "kdig"
+
+#define DEFAULT_RETRIES_DIG 2
+#define DEFAULT_TIMEOUT_DIG 5
+#define DEFAULT_ALIGNMENT_SIZE 128
+#define DEFAULT_TLS_OCSP_STAPLING (7 * 24 * 3600)
+
+#define BADCOOKIE_RETRY_MAX 10
+
+static const flags_t DEFAULT_FLAGS_DIG = {
+ .aa_flag = false,
+ .tc_flag = false,
+ .rd_flag = true,
+ .ra_flag = false,
+ .z_flag = false,
+ .ad_flag = true,
+ .cd_flag = false,
+ .do_flag = false
+};
+
+static const style_t DEFAULT_STYLE_DIG = {
+ .format = FORMAT_FULL,
+ .style = {
+ .wrap = false,
+ .show_class = true,
+ .show_ttl = true,
+ .verbose = false,
+ .original_ttl = false,
+ .empty_ttl = false,
+ .human_ttl = false,
+ .human_timestamp = true,
+ .generic = false,
+ .ascii_to_idn = name_to_idn
+ },
+ .show_query = false,
+ .show_header = true,
+ .show_section = true,
+ .show_edns = true,
+ .show_question = true,
+ .show_answer = true,
+ .show_authority = true,
+ .show_additional = true,
+ .show_tsig = true,
+ .show_footer = true
+};
+
+static int opt_multiline(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.wrap = true;
+ q->style.format = FORMAT_FULL;
+ q->style.show_header = true;
+ q->style.show_edns = true;
+ q->style.show_footer = true;
+ q->style.style.verbose = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nomultiline(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.wrap = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_short(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.format = FORMAT_DIG;
+ q->style.show_header = false;
+ q->style.show_edns = false;
+ q->style.show_footer = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_noshort(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.format = FORMAT_FULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_generic(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.generic = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nogeneric(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.generic = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_aaflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.aa_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noaaflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.aa_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_tcflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.tc_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_notcflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.tc_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_rdflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.rd_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nordflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.rd_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_raflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.ra_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noraflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.ra_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_zflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.z_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nozflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.z_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_adflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.ad_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noadflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.ad_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_cdflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.cd_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nocdflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.cd_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_doflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.do_flag = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nodoflag(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->flags.do_flag = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_all(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_header = true;
+ q->style.show_section = true;
+ q->style.show_edns = true;
+ q->style.show_question = true;
+ q->style.show_answer = true;
+ q->style.show_authority = true;
+ q->style.show_additional = true;
+ q->style.show_tsig = true;
+ q->style.show_footer = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noall(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_header = false;
+ q->style.show_section = false;
+ q->style.show_edns = false;
+ q->style.show_query = false;
+ q->style.show_question = false;
+ q->style.show_answer = false;
+ q->style.show_authority = false;
+ q->style.show_additional = false;
+ q->style.show_tsig = false;
+ q->style.show_footer = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_qr(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_query = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noqr(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_query = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_header(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_header = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noheader(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_header = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_comments(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_section = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nocomments(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_section = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_opt(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_edns = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noopt(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_edns = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_opttext(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_edns_opt_text = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noopttext(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_edns_opt_text = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_question(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_question = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noquestion(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_question = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_answer(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_answer = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noanswer(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_answer = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_authority(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_authority = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noauthority(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_authority = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_additional(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_additional = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noadditional(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_additional = false;
+ q->style.show_edns = false;
+ q->style.show_tsig = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_tsig(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_tsig = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_notsig(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_tsig = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_stats(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_footer = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nostats(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.show_footer = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_class(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.show_class = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noclass(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.show_class = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_ttl(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.show_ttl = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nottl(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.show_ttl = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_ignore(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->ignore_tc = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_noignore(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->ignore_tc = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_crypto(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.hide_crypto = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_nocrypto(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.style.hide_crypto = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_tcp(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->protocol = PROTO_TCP;
+
+ return KNOT_EOK;
+}
+
+static int opt_notcp(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->protocol = PROTO_UDP;
+ return opt_ignore(arg, query);
+}
+
+static int opt_fastopen(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->fastopen = true;
+
+ return opt_tcp(arg, query);
+}
+
+static int opt_nofastopen(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->fastopen = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_keepopen(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->keepopen = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nokeepopen(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->keepopen = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_tls(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->tls.enable = true;
+ return opt_tcp(arg, query);
+}
+
+static int opt_notls(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ tls_params_clean(&q->tls);
+ tls_params_init(&q->tls);
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_ca(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg == NULL) {
+ q->tls.system_ca = true;
+ return opt_tls(arg, query);
+ } else {
+ if (ptrlist_add(&q->tls.ca_files, strdup(arg), NULL) == NULL) {
+ return KNOT_ENOMEM;
+ }
+ return opt_tls(arg, query);
+ }
+}
+
+static int opt_notls_ca(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->tls.system_ca = false;
+
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, q->tls.ca_files) {
+ free(node->d);
+ }
+ ptrlist_free(&q->tls.ca_files, NULL);
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_pin(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ uint8_t pin[64] = { 0 };
+
+ int ret = knot_base64_decode((const uint8_t *)arg, strlen(arg), pin, sizeof(pin));
+ if (ret < 0) {
+ ERR("invalid +tls-pin=%s", arg);
+ return ret;
+ } else if (ret != CERT_PIN_LEN) { // Check for 256-bit value.
+ ERR("invalid sha256 hash length +tls-pin=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ uint8_t *item = malloc(1 + ret); // 1 ~ leading data length.
+ if (item == NULL) {
+ return KNOT_ENOMEM;
+ }
+ item[0] = ret;
+ memcpy(&item[1], pin, ret);
+
+ if (ptrlist_add(&q->tls.pins, item, NULL) == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return opt_tls(arg, query);
+}
+
+static int opt_notls_pin(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ ptrnode_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, q->tls.pins) {
+ free(node->d);
+ }
+ ptrlist_free(&q->tls.pins, NULL);
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_hostname(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.hostname);
+ q->tls.hostname = strdup(arg);
+
+ return opt_tls(arg, query);
+}
+
+static int opt_notls_hostname(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.hostname);
+ q->tls.hostname = NULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_sni(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.sni);
+ q->tls.sni = strdup(arg);
+
+ return opt_tls(arg, query);
+}
+
+static int opt_notls_sni(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.sni);
+ q->tls.sni = NULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_keyfile(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.keyfile);
+ q->tls.keyfile = strdup(arg);
+
+ return opt_tls(arg, query);
+}
+
+static int opt_notls_keyfile(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.keyfile);
+ q->tls.keyfile = NULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_certfile(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.certfile);
+ q->tls.certfile = strdup(arg);
+
+ return opt_tls(arg, query);
+}
+
+static int opt_notls_certfile(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ free(q->tls.certfile);
+ q->tls.certfile = NULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_tls_ocsp_stapling(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg == NULL) {
+ q->tls.ocsp_stapling = DEFAULT_TLS_OCSP_STAPLING;
+ return opt_tls(arg, query);
+ } else {
+ uint32_t num = 0;
+ if (str_to_u32(arg, &num) != KNOT_EOK || num == 0) {
+ ERR("invalid +tls-ocsp-stapling=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ q->tls.ocsp_stapling = 3600 * num;
+ return opt_tls(arg, query);
+ }
+}
+
+static int opt_notls_ocsp_stapling(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->tls.ocsp_stapling = 0;
+
+ return KNOT_EOK;
+}
+
+static int opt_https(const char *arg, void *query)
+{
+#ifdef LIBNGHTTP2
+ query_t *q = query;
+
+ q->https.enable = true;
+
+ if (arg != NULL) {
+ char *resource = strstr(arg, "://");
+ if (resource == NULL) {
+ resource = (char *)arg;
+ } else {
+ resource += 3; // strlen("://")
+ if (*resource == '\0') {
+ ERR("invalid +https=%s", arg);
+ return KNOT_EINVAL;
+ }
+ }
+
+ char *tmp_path = strchr(resource, '/');
+ if (tmp_path) {
+ free(q->https.path);
+ q->https.path = strdup(tmp_path);
+
+ if (tmp_path != resource) {
+ free(q->tls.hostname);
+ q->tls.hostname = strndup(resource, (size_t)(tmp_path - resource));
+ }
+ return opt_tls(arg, query);
+ } else {
+ return opt_tls_hostname(arg, query);
+ }
+
+ }
+
+ return opt_tls(arg, query);
+
+#else
+ return KNOT_ENOTSUP;
+#endif //LIBNGHTTP2
+}
+
+static int opt_nohttps(const char *arg, void *query)
+{
+#ifdef LIBNGHTTP2
+ query_t *q = query;
+
+ https_params_clean(&q->https);
+
+ return opt_notls(arg, query);
+#else
+ return KNOT_ENOTSUP;
+#endif //LIBNGHTTP2
+}
+
+static int opt_https_get(const char *arg, void *query)
+{
+#ifdef LIBNGHTTP2
+ query_t *q = query;
+
+ q->https.method = GET;
+
+ return opt_https(arg, query);
+#else
+ return KNOT_ENOTSUP;
+#endif //LIBNGHTTP2
+}
+
+static int opt_nohttps_get(const char *arg, void *query)
+{
+#ifdef LIBNGHTTP2
+ query_t *q = query;
+
+ q->https.method = POST;
+
+ return KNOT_EOK;
+#else
+ return KNOT_ENOTSUP;
+#endif
+}
+
+static int opt_quic(const char *arg, void *query)
+{
+#ifdef ENABLE_QUIC
+ query_t *q = query;
+
+ q->quic.enable = true;
+
+ opt_tls(arg, query);
+ opt_notcp(arg, query);
+
+ return KNOT_EOK;
+#else
+ return KNOT_ENOTSUP;
+#endif //ENABLE_QUIC
+}
+
+static int opt_noquic(const char *arg, void *query)
+{
+#ifdef ENABLE_QUIC
+ query_t *q = query;
+
+ quic_params_clean(&q->quic);
+
+ return opt_notls(arg, query);
+#else
+ return KNOT_ENOTSUP;
+#endif //ENABLE_QUIC
+}
+
+static int opt_nsid(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->nsid = true;
+
+ return KNOT_EOK;
+}
+
+static int opt_nonsid(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->nsid = false;
+
+ return KNOT_EOK;
+}
+
+static int opt_bufsize(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ uint16_t num = 0;
+ if (str_to_u16(arg, &num) != KNOT_EOK) {
+ ERR("invalid +bufsize=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ // Disable EDNS if zero bufsize.
+ if (num == 0) {
+ q->udp_size = -1;
+ } else if (num < KNOT_WIRE_HEADER_SIZE) {
+ q->udp_size = KNOT_WIRE_HEADER_SIZE;
+ } else {
+ q->udp_size = num;
+ }
+
+ return KNOT_EOK;
+}
+
+static int opt_nobufsize(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->udp_size = -1;
+
+ return KNOT_EOK;
+}
+
+static int opt_cookie(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg != NULL) {
+ uint8_t *input = NULL;
+ size_t input_len;
+
+ int ret = hex_decode(arg, &input, &input_len);
+ if (ret != KNOT_EOK) {
+ ERR("invalid +cookie=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ if (input_len < KNOT_EDNS_COOKIE_CLNT_SIZE) {
+ ERR("too short client +cookie=%s", arg);
+ free(input);
+ return KNOT_EINVAL;
+ }
+ q->cc.len = KNOT_EDNS_COOKIE_CLNT_SIZE;
+ memcpy(q->cc.data, input, q->cc.len);
+
+ input_len -= q->cc.len;
+ if (input_len > 0) {
+ if (input_len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE) {
+ ERR("too short server +cookie=%s", arg);
+ free(input);
+ return KNOT_EINVAL;
+ }
+ if (input_len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) {
+ ERR("too long server +cookie=%s", arg);
+ free(input);
+ return KNOT_EINVAL;
+ }
+ q->sc.len = input_len;
+ memcpy(q->sc.data, input + q->cc.len, q->sc.len);
+ }
+
+ free(input);
+ } else {
+ q->cc.len = KNOT_EDNS_COOKIE_CLNT_SIZE;
+
+ int ret = dnssec_random_buffer(q->cc.data, q->cc.len);
+ if (ret != DNSSEC_EOK) {
+ return knot_error_from_libdnssec(ret);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int opt_nocookie(const char *arg, void *query)
+{
+ query_t *q = query;
+ q->cc.len = 0;
+ q->sc.len = 0;
+ return KNOT_EOK;
+}
+
+static int opt_badcookie(const char *arg, void *query)
+{
+ query_t *q = query;
+ q->badcookie = BADCOOKIE_RETRY_MAX;
+ return KNOT_EOK;
+}
+
+static int opt_nobadcookie(const char *arg, void *query)
+{
+ query_t *q = query;
+ q->badcookie = 0;
+ return KNOT_EOK;
+}
+
+static int opt_padding(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg == NULL) {
+ q->padding = -2;
+ return KNOT_EOK;
+ } else {
+ uint16_t num = 0;
+ if (str_to_u16(arg, &num) != KNOT_EOK) {
+ ERR("invalid +padding=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ q->padding = num;
+ return KNOT_EOK;
+ }
+}
+
+static int opt_nopadding(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->padding = -3;
+
+ return KNOT_EOK;
+}
+
+static int opt_alignment(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg == NULL) {
+ q->alignment = DEFAULT_ALIGNMENT_SIZE;
+ return KNOT_EOK;
+ } else {
+ uint16_t num = 0;
+ if (str_to_u16(arg, &num) != KNOT_EOK || num < 2) {
+ ERR("invalid +alignment=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ q->alignment = num;
+ return KNOT_EOK;
+ }
+}
+
+static int opt_noalignment(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->alignment = 0;
+
+ return KNOT_EOK;
+}
+
+static int opt_subnet(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ char *sep = NULL;
+ const size_t arg_len = strlen(arg);
+ const char *arg_end = arg + arg_len;
+ size_t addr_len = 0;
+
+ // Separate address and network mask.
+ if ((sep = strchr(arg, '/')) != NULL) {
+ addr_len = sep - arg;
+ } else {
+ addr_len = arg_len;
+ }
+
+ // Check IP address.
+
+ struct sockaddr_storage ss = { 0 };
+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST };
+ struct addrinfo *ai = NULL;
+
+ char *addr_str = strndup(arg, addr_len);
+ if (getaddrinfo(addr_str, NULL, &hints, &ai) != 0) {
+ free(addr_str);
+ ERR("invalid address +subnet=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ memcpy(&ss, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ free(addr_str);
+
+ if (knot_edns_client_subnet_set_addr(&q->subnet, &ss) != KNOT_EOK) {
+ ERR("invalid address +subnet=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ // Parse network mask.
+ const char *mask = arg;
+ if (mask + addr_len < arg_end) {
+ mask += addr_len + 1;
+ uint8_t num = 0;
+ if (str_to_u8(mask, &num) != KNOT_EOK || num > q->subnet.source_len) {
+ ERR("invalid network mask +subnet=%s", arg);
+ return KNOT_EINVAL;
+ }
+ q->subnet.source_len = num;
+ }
+
+ return KNOT_EOK;
+}
+
+static int opt_nosubnet(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->subnet.family = AF_UNSPEC;
+
+ return KNOT_EOK;
+}
+
+static int opt_edns(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (arg == NULL) {
+ q->edns = 0;
+ return KNOT_EOK;
+ } else {
+ uint8_t num = 0;
+ if (str_to_u8(arg, &num) != KNOT_EOK) {
+ ERR("invalid +edns=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ q->edns = num;
+ return KNOT_EOK;
+ }
+}
+
+static int opt_noedns(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->edns = -1;
+ opt_nodoflag(arg, query);
+ opt_nonsid(arg, query);
+ opt_nobufsize(arg, query);
+ opt_nocookie(arg, query);
+ opt_nopadding(arg, query);
+ opt_noalignment(arg, query);
+ opt_nosubnet(arg, query);
+
+ return KNOT_EOK;
+}
+
+static int opt_timeout(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (params_parse_wait(arg, &q->wait) != KNOT_EOK) {
+ ERR("invalid +timeout=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int opt_notimeout(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ (void)params_parse_wait("0", &q->wait);
+
+ return KNOT_EOK;
+}
+
+static int opt_retry(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ if (str_to_u32(arg, &q->retries) != KNOT_EOK) {
+ ERR("invalid +retry=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int opt_noretry(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->retries = 0;
+
+ return KNOT_EOK;
+}
+
+static int opt_expire(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ ednsopt_t *opt = ednsopt_create(KNOT_EDNS_OPTION_EXPIRE, 0, NULL);
+ add_tail(&q->edns_opts, &opt->n);
+
+ return KNOT_EOK;
+}
+
+static int opt_noexpire(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ ednsopt_t *node, *nxt;
+ WALK_LIST_DELSAFE(node, nxt, q->edns_opts) {
+ ednsopt_t *opt = node;
+ if (opt->code == KNOT_EDNS_OPTION_EXPIRE) {
+ rem_node(&opt->n);
+ ednsopt_free(opt);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_ednsopt(const char *arg, ednsopt_t **opt_ptr)
+{
+ errno = 0;
+ char *end = NULL;
+ unsigned long code = strtoul(arg, &end, 10);
+ if (errno != 0 || arg == end || code > UINT16_MAX) {
+ return KNOT_EINVAL;
+ }
+
+ size_t length = 0;
+ uint8_t *data = NULL;
+ if (end[0] == ':') {
+ if (end[1] != '\0') {
+ int ret = hex_decode(end + 1, &data, &length);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (length > UINT16_MAX) {
+ free(data);
+ return KNOT_ERANGE;
+ }
+ }
+ } else if (end[0] != '\0') {
+ return KNOT_EINVAL;
+ }
+
+ ednsopt_t *opt = ednsopt_create(code, length, data);
+ if (opt == NULL) {
+ free(data);
+ return KNOT_ENOMEM;
+ }
+
+ *opt_ptr = opt;
+ return KNOT_EOK;
+}
+
+static int opt_ednsopt(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ ednsopt_t *opt = NULL;
+ int ret = parse_ednsopt(arg, &opt);
+ if (ret != KNOT_EOK) {
+ ERR("invalid +ednsopt=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ add_tail(&q->edns_opts, &opt->n);
+
+ return KNOT_EOK;
+}
+
+static int opt_noednsopt(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ ednsopt_list_deinit(&q->edns_opts);
+
+ return KNOT_EOK;
+}
+
+static int opt_noidn(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->idn = false;
+ q->style.style.ascii_to_idn = NULL;
+
+ return KNOT_EOK;
+}
+
+static int opt_json(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.format = FORMAT_JSON;
+
+ return KNOT_EOK;
+}
+
+static int opt_nojson(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->style.format = FORMAT_FULL;
+
+ return KNOT_EOK;
+}
+
+static int parse_addr(struct sockaddr_storage *addr, const char *arg, const char *def_port)
+{
+ srv_info_t *info = parse_nameserver(arg, def_port);
+ if (info == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ struct addrinfo *addr_info = NULL;
+ int ret = getaddrinfo(info->name, info->service, NULL, &addr_info);
+ srv_info_free(info);
+ if (ret != 0) {
+ return KNOT_EINVAL;
+ }
+
+ memcpy(addr, addr_info->ai_addr, addr_info->ai_addrlen);
+ freeaddrinfo(addr_info);
+
+ return KNOT_EOK;
+}
+
+static int opt_proxy(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ const char *sep = strchr(arg, '-');
+ if (sep == NULL || sep == arg || *(sep + 1) == '\0') {
+ ERR("invalid specification +proxy=%s", arg);
+ return KNOT_EINVAL;
+ }
+
+ char *src = strndup(arg, sep - arg);
+ int ret = parse_addr(&q->proxy.src, src, "0");
+ if (ret != KNOT_EOK) {
+ ERR("invalid proxy source address '%s'", src);
+ free(src);
+ return KNOT_EINVAL;
+ }
+
+ const char *dst = sep + 1;
+ ret = parse_addr(&q->proxy.dst, dst, "53");
+ if (ret != KNOT_EOK) {
+ ERR("invalid proxy destination address '%s'", dst);
+ free(src);
+ return KNOT_EINVAL;
+ }
+
+ if (q->proxy.src.ss_family != q->proxy.dst.ss_family) {
+ ERR("proxy address family mismatch '%s' versus '%s'", src, dst);
+ free(src);
+ return KNOT_EINVAL;
+ }
+ free(src);
+
+ return KNOT_EOK;
+}
+
+static int opt_noproxy(const char *arg, void *query)
+{
+ query_t *q = query;
+
+ q->proxy.src.ss_family = 0;
+ q->proxy.dst.ss_family = 0;
+
+ return KNOT_EOK;
+}
+
+static const param_t kdig_opts2[] = {
+ { "multiline", ARG_NONE, opt_multiline },
+ { "nomultiline", ARG_NONE, opt_nomultiline },
+
+ { "short", ARG_NONE, opt_short },
+ { "noshort", ARG_NONE, opt_noshort },
+
+ { "generic", ARG_NONE, opt_generic },
+ { "nogeneric", ARG_NONE, opt_nogeneric },
+
+ { "aaflag", ARG_NONE, opt_aaflag },
+ { "noaaflag", ARG_NONE, opt_noaaflag },
+
+ { "tcflag", ARG_NONE, opt_tcflag },
+ { "notcflag", ARG_NONE, opt_notcflag },
+
+ { "rdflag", ARG_NONE, opt_rdflag },
+ { "nordflag", ARG_NONE, opt_nordflag },
+
+ { "recurse", ARG_NONE, opt_rdflag },
+ { "norecurse", ARG_NONE, opt_nordflag },
+
+ { "raflag", ARG_NONE, opt_raflag },
+ { "noraflag", ARG_NONE, opt_noraflag },
+
+ { "zflag", ARG_NONE, opt_zflag },
+ { "nozflag", ARG_NONE, opt_nozflag },
+
+ { "adflag", ARG_NONE, opt_adflag },
+ { "noadflag", ARG_NONE, opt_noadflag },
+
+ { "cdflag", ARG_NONE, opt_cdflag },
+ { "nocdflag", ARG_NONE, opt_nocdflag },
+
+ { "dnssec", ARG_NONE, opt_doflag },
+ { "nodnssec", ARG_NONE, opt_nodoflag },
+
+ { "all", ARG_NONE, opt_all },
+ { "noall", ARG_NONE, opt_noall },
+
+ { "qr", ARG_NONE, opt_qr },
+ { "noqr", ARG_NONE, opt_noqr },
+
+ { "header", ARG_NONE, opt_header },
+ { "noheader", ARG_NONE, opt_noheader },
+
+ { "comments", ARG_NONE, opt_comments },
+ { "nocomments", ARG_NONE, opt_nocomments },
+
+ { "opt", ARG_NONE, opt_opt },
+ { "noopt", ARG_NONE, opt_noopt },
+
+ { "opttext", ARG_NONE, opt_opttext },
+ { "noopttext", ARG_NONE, opt_noopttext },
+
+ { "question", ARG_NONE, opt_question },
+ { "noquestion", ARG_NONE, opt_noquestion },
+
+ { "answer", ARG_NONE, opt_answer },
+ { "noanswer", ARG_NONE, opt_noanswer },
+
+ { "authority", ARG_NONE, opt_authority },
+ { "noauthority", ARG_NONE, opt_noauthority },
+
+ { "additional", ARG_NONE, opt_additional },
+ { "noadditional", ARG_NONE, opt_noadditional },
+
+ { "tsig", ARG_NONE, opt_tsig },
+ { "notsig", ARG_NONE, opt_notsig },
+
+ { "stats", ARG_NONE, opt_stats },
+ { "nostats", ARG_NONE, opt_nostats },
+
+ { "class", ARG_NONE, opt_class },
+ { "noclass", ARG_NONE, opt_noclass },
+
+ { "ttl", ARG_NONE, opt_ttl },
+ { "nottl", ARG_NONE, opt_nottl },
+
+ { "crypto", ARG_NONE, opt_crypto },
+ { "nocrypto", ARG_NONE, opt_nocrypto },
+
+ { "tcp", ARG_NONE, opt_tcp },
+ { "notcp", ARG_NONE, opt_notcp },
+
+ { "fastopen", ARG_NONE, opt_fastopen },
+ { "nofastopen", ARG_NONE, opt_nofastopen },
+
+ { "ignore", ARG_NONE, opt_ignore },
+ { "noignore", ARG_NONE, opt_noignore },
+
+ { "keepopen", ARG_NONE, opt_keepopen },
+ { "nokeepopen", ARG_NONE, opt_nokeepopen },
+
+ { "tls", ARG_NONE, opt_tls },
+ { "notls", ARG_NONE, opt_notls },
+
+ { "tls-ca", ARG_OPTIONAL, opt_tls_ca },
+ { "notls-ca", ARG_NONE, opt_notls_ca },
+
+ { "tls-pin", ARG_REQUIRED, opt_tls_pin },
+ { "notls-pin", ARG_NONE, opt_notls_pin },
+
+ { "tls-hostname", ARG_REQUIRED, opt_tls_hostname },
+ { "notls-hostname", ARG_NONE, opt_notls_hostname },
+
+ { "tls-sni", ARG_REQUIRED, opt_tls_sni },
+ { "notls-sni", ARG_NONE, opt_notls_sni },
+
+ { "tls-keyfile", ARG_REQUIRED, opt_tls_keyfile },
+ { "notls-keyfile", ARG_NONE, opt_notls_keyfile },
+
+ { "tls-certfile", ARG_REQUIRED, opt_tls_certfile },
+ { "notls-certfile", ARG_NONE, opt_notls_certfile },
+
+ { "tls-ocsp-stapling", ARG_OPTIONAL, opt_tls_ocsp_stapling },
+ { "notls-ocsp-stapling", ARG_NONE, opt_notls_ocsp_stapling },
+
+ { "https", ARG_OPTIONAL, opt_https },
+ { "nohttps", ARG_NONE, opt_nohttps },
+
+ { "https-get", ARG_NONE, opt_https_get },
+ { "nohttps-get", ARG_NONE, opt_nohttps_get },
+
+ { "quic", ARG_NONE, opt_quic },
+ { "noquic", ARG_NONE, opt_noquic },
+
+ { "nsid", ARG_NONE, opt_nsid },
+ { "nonsid", ARG_NONE, opt_nonsid },
+
+ { "bufsize", ARG_REQUIRED, opt_bufsize },
+ { "nobufsize", ARG_NONE, opt_nobufsize },
+
+ { "padding", ARG_OPTIONAL, opt_padding },
+ { "nopadding", ARG_NONE, opt_nopadding },
+
+ { "alignment", ARG_OPTIONAL, opt_alignment },
+ { "noalignment", ARG_NONE, opt_noalignment },
+
+ { "subnet", ARG_REQUIRED, opt_subnet },
+ { "nosubnet", ARG_NONE, opt_nosubnet },
+
+ { "proxy", ARG_REQUIRED, opt_proxy },
+ { "noproxy", ARG_NONE, opt_noproxy },
+
+ // Obsolete aliases.
+ { "client", ARG_REQUIRED, opt_subnet },
+ { "noclient", ARG_NONE, opt_nosubnet },
+
+ { "edns", ARG_OPTIONAL, opt_edns },
+ { "noedns", ARG_NONE, opt_noedns },
+
+ { "timeout", ARG_REQUIRED, opt_timeout },
+ { "notimeout", ARG_NONE, opt_notimeout },
+
+ { "retry", ARG_REQUIRED, opt_retry },
+ { "noretry", ARG_NONE, opt_noretry },
+
+ { "expire", ARG_NONE, opt_expire },
+ { "noexpire", ARG_NONE, opt_noexpire },
+
+ { "cookie", ARG_OPTIONAL, opt_cookie },
+ { "nocookie", ARG_NONE, opt_nocookie },
+
+ { "badcookie", ARG_NONE, opt_badcookie },
+ { "nobadcookie", ARG_NONE, opt_nobadcookie },
+
+ { "ednsopt", ARG_REQUIRED, opt_ednsopt },
+ { "noednsopt", ARG_NONE, opt_noednsopt },
+
+ { "json", ARG_NONE, opt_json },
+ { "nojson", ARG_NONE, opt_nojson },
+
+ /* "idn" doesn't work since it must be called before query creation. */
+ { "noidn", ARG_NONE, opt_noidn },
+
+ { NULL }
+};
+
+query_t *query_create(const char *owner, const query_t *conf)
+{
+ // Create output structure.
+ query_t *query = calloc(1, sizeof(query_t));
+
+ if (query == NULL) {
+ DBG_NULL;
+ return NULL;
+ }
+
+ // Initialization with defaults or with reference query.
+ if (conf == NULL) {
+ query->conf = NULL;
+ query->local = NULL;
+ query->operation = OPERATION_QUERY;
+ query->ip = IP_ALL;
+ query->protocol = PROTO_ALL;
+ query->fastopen = false;
+ query->port = strdup("");
+ query->udp_size = -1;
+ query->retries = DEFAULT_RETRIES_DIG;
+ query->wait = DEFAULT_TIMEOUT_DIG;
+ query->ignore_tc = false;
+ query->class_num = -1;
+ query->type_num = -1;
+ query->serial = -1;
+ query->notify = false;
+ query->flags = DEFAULT_FLAGS_DIG;
+ query->style = DEFAULT_STYLE_DIG;
+ query->style.style.now = knot_time();
+ query->idn = true;
+ query->nsid = false;
+ query->edns = -1;
+ query->cc.len = 0;
+ query->sc.len = 0;
+ query->badcookie = BADCOOKIE_RETRY_MAX;
+ query->padding = -1;
+ query->alignment = 0;
+ tls_params_init(&query->tls);
+ //query->tsig_key
+ query->subnet.family = AF_UNSPEC;
+ ednsopt_list_init(&query->edns_opts);
+#if USE_DNSTAP
+ query->dt_reader = NULL;
+ query->dt_writer = NULL;
+#endif // USE_DNSTAP
+ } else {
+ *query = *conf;
+ query->conf = conf;
+ if (conf->local != NULL) {
+ query->local = srv_info_create(conf->local->name,
+ conf->local->service);
+ if (query->local == NULL) {
+ query_free(query);
+ return NULL;
+ }
+ } else {
+ query->local = NULL;
+ }
+ query->port = strdup(conf->port);
+ tls_params_copy(&query->tls, &conf->tls);
+ https_params_copy(&query->https, &conf->https);
+ quic_params_copy(&query->quic, &conf->quic);
+ if (conf->tsig_key.name != NULL) {
+ int ret = knot_tsig_key_copy(&query->tsig_key,
+ &conf->tsig_key);
+ if (ret != KNOT_EOK) {
+ query_free(query);
+ return NULL;
+ }
+ }
+
+ int ret = ednsopt_list_dup(&query->edns_opts, &conf->edns_opts);
+ if (ret != KNOT_EOK) {
+ query_free(query);
+ return NULL;
+ }
+
+#if USE_DNSTAP
+ query->dt_reader = conf->dt_reader;
+ query->dt_writer = conf->dt_writer;
+#endif // USE_DNSTAP
+ }
+
+ // Initialize list of servers.
+ init_list(&query->servers);
+
+ // Set the query owner if any.
+ if (owner != NULL) {
+ if ((query->owner = strdup(owner)) == NULL) {
+ query_free(query);
+ return NULL;
+ }
+ }
+
+ // Check dynamic allocation.
+ if (query->port == NULL) {
+ query_free(query);
+ return NULL;
+ }
+
+ return query;
+}
+
+void query_free(query_t *query)
+{
+ node_t *n, *nxt;
+
+ if (query == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Cleanup servers.
+ WALK_LIST_DELSAFE(n, nxt, query->servers) {
+ srv_info_free((srv_info_t *)n);
+ }
+
+ // Cleanup local address.
+ if (query->local != NULL) {
+ srv_info_free(query->local);
+ }
+
+ tls_params_clean(&query->tls);
+ https_params_clean(&query->https);
+ quic_params_clean(&query->quic);
+
+ // Cleanup signing key.
+ knot_tsig_key_deinit(&query->tsig_key);
+
+ // Cleanup EDNS options.
+ ednsopt_list_deinit(&query->edns_opts);
+
+#if USE_DNSTAP
+ if (query->dt_reader != NULL) {
+ dt_reader_free(query->dt_reader);
+ }
+ if (query->dt_writer != NULL) {
+ // Global writer can be shared!
+ if (query->conf == NULL ||
+ query->conf->dt_writer != query->dt_writer) {
+ dt_writer_free(query->dt_writer);
+ }
+ }
+#endif // USE_DNSTAP
+
+ free(query->owner);
+ free(query->port);
+ free(query);
+}
+
+ednsopt_t *ednsopt_create(uint16_t code, uint16_t length, uint8_t *data)
+{
+ ednsopt_t *opt = calloc(1, sizeof(*opt));
+ if (opt == NULL) {
+ return NULL;
+ }
+
+ opt->code = code;
+ opt->length = length;
+ opt->data = data;
+
+ return opt;
+}
+
+ednsopt_t *ednsopt_dup(const ednsopt_t *opt)
+{
+ ednsopt_t *dup = calloc(1, sizeof(*opt));
+ if (dup == NULL) {
+ return NULL;
+ }
+
+ dup->code = opt->code;
+ dup->length = opt->length;
+ dup->data = memdup(opt->data, opt->length);
+ if (dup->data == NULL) {
+ free(dup);
+ return NULL;
+ }
+
+ return dup;
+}
+
+void ednsopt_free(ednsopt_t *opt)
+{
+ if (opt == NULL) {
+ return;
+ }
+
+ free(opt->data);
+ free(opt);
+}
+
+void ednsopt_list_init(list_t *list)
+{
+ init_list(list);
+}
+
+void ednsopt_list_deinit(list_t *list)
+{
+ node_t *n, *next;
+ WALK_LIST_DELSAFE(n, next, *list) {
+ ednsopt_t *opt = (ednsopt_t *)n;
+ ednsopt_free(opt);
+ }
+
+ init_list(list);
+}
+
+int ednsopt_list_dup(list_t *dest, const list_t *src)
+{
+ list_t backup = *dest;
+ init_list(dest);
+
+ node_t *n;
+ WALK_LIST(n, *src) {
+ ednsopt_t *opt = (ednsopt_t *)n;
+ ednsopt_t *dup = ednsopt_dup(opt);
+ if (dup == NULL) {
+ ednsopt_list_deinit(dest);
+ *dest = backup;
+ return KNOT_ENOMEM;
+ }
+
+ add_tail(dest, &dup->n);
+ }
+
+ return KNOT_EOK;
+}
+
+bool ednsopt_list_empty(const list_t *list)
+{
+ return EMPTY_LIST(*list);
+}
+
+int kdig_init(kdig_params_t *params)
+{
+ if (params == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ memset(params, 0, sizeof(*params));
+
+ params->stop = false;
+
+ // Initialize list of queries.
+ init_list(&params->queries);
+
+ // Create config query.
+ if ((params->config = query_create(NULL, NULL)) == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+void kdig_clean(kdig_params_t *params)
+{
+ node_t *n, *nxt;
+
+ if (params == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // Clean up queries.
+ WALK_LIST_DELSAFE(n, nxt, params->queries) {
+ query_free((query_t *)n);
+ }
+
+ // Clean up config.
+ query_free(params->config);
+
+ // Clean up the structure.
+ memset(params, 0, sizeof(*params));
+}
+
+static int parse_class(const char *value, query_t *query)
+{
+ uint16_t rclass;
+
+ if (params_parse_class(value, &rclass) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ query->class_num = rclass;
+
+ return KNOT_EOK;
+}
+
+static int parse_keyfile(const char *value, query_t *query)
+{
+ knot_tsig_key_deinit(&query->tsig_key);
+
+ if (knot_tsig_key_init_file(&query->tsig_key, value) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_local(const char *value, query_t *query)
+{
+ srv_info_t *local = parse_nameserver(value, "0");
+ if (local == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (query->local != NULL) {
+ srv_info_free(query->local);
+ }
+
+ query->local = local;
+
+ return KNOT_EOK;
+}
+
+static int parse_name(const char *value, list_t *queries, const query_t *conf)
+{
+ query_t *query = NULL;
+ char *ascii_name = (char *)value;
+ char *fqd_name = NULL;
+
+ if (value != NULL) {
+ if (conf->idn) {
+ ascii_name = name_from_idn(value);
+ if (ascii_name == NULL) {
+ return KNOT_EINVAL;
+ }
+ }
+
+ // If name is not FQDN, append trailing dot.
+ fqd_name = get_fqd_name(ascii_name);
+
+ if (conf->idn) {
+ free(ascii_name);
+ }
+ }
+
+ // Create new query.
+ query = query_create(fqd_name, conf);
+
+ free(fqd_name);
+
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Add new query to the queries.
+ add_tail(queries, (node_t *)query);
+
+ return KNOT_EOK;
+}
+
+static int parse_port(const char *value, query_t *query)
+{
+ char **port;
+
+ // Set current server port (last or query default).
+ if (list_size(&query->servers) > 0) {
+ srv_info_t *server = TAIL(query->servers);
+ port = &(server->service);
+ } else {
+ port = &(query->port);
+ }
+
+ char *new_port = strdup(value);
+
+ if (new_port == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Deallocate old string.
+ free(*port);
+
+ *port = new_port;
+
+ return KNOT_EOK;
+}
+
+static int parse_reverse(const char *value, list_t *queries, const query_t *conf)
+{
+ query_t *query = NULL;
+
+ // Create reverse name.
+ char *reverse = get_reverse_name(value);
+
+ if (reverse == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // Create reverse query for given address.
+ query = query_create(reverse, conf);
+
+ free(reverse);
+
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Set type for reverse query.
+ query->type_num = KNOT_RRTYPE_PTR;
+
+ // Add new query to the queries.
+ add_tail(queries, (node_t *)query);
+
+ return KNOT_EOK;
+}
+
+static int parse_server(const char *value, kdig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ if (params_parse_server(value, &query->servers, query->port) != KNOT_EOK) {
+ ERR("invalid server @%s", value);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_tsig(const char *value, query_t *query)
+{
+ knot_tsig_key_deinit(&query->tsig_key);
+
+ if (knot_tsig_key_init_str(&query->tsig_key, value) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_type(const char *value, query_t *query)
+{
+ uint16_t rtype;
+ int64_t serial;
+ bool notify;
+
+ if (params_parse_type(value, &rtype, &serial, &notify) != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+
+ query->type_num = rtype;
+ query->serial = serial;
+ query->notify = notify;
+
+ // If NOTIFY, reset default RD flag.
+ if (query->notify) {
+ query->flags.rd_flag = false;
+ }
+
+ return KNOT_EOK;
+}
+
+#if USE_DNSTAP
+static int parse_dnstap_output(const char *value, query_t *query)
+{
+ if (query->dt_writer != NULL) {
+ if (query->conf == NULL ||
+ query->conf->dt_writer != query->dt_writer) {
+ dt_writer_free(query->dt_writer);
+ }
+ }
+
+ query->dt_writer = dt_writer_create(value, "kdig " PACKAGE_VERSION);
+ if (query->dt_writer == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_dnstap_input(const char *value, query_t *query)
+{
+ // Just in case, shouldn't happen.
+ if (query->dt_reader != NULL) {
+ dt_reader_free(query->dt_reader);
+ }
+
+ query->dt_reader = dt_reader_create(value);
+ if (query->dt_reader == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+#endif // USE_DNSTAP
+
+static void complete_servers(query_t *query, const query_t *conf)
+{
+ node_t *n;
+ char *def_port;
+
+ // Decide which default port use.
+ if (strlen(query->port) > 0) {
+ def_port = query->port;
+ } else if (strlen(conf->port) > 0) {
+ def_port = conf->port;
+ } else if (query->https.enable) {
+ def_port = DEFAULT_DNS_HTTPS_PORT;
+ } else if (query->quic.enable) {
+ def_port = DEFAULT_DNS_QUIC_PORT;
+ } else if (query->tls.enable) {
+ def_port = DEFAULT_DNS_TLS_PORT;
+ } else {
+ def_port = DEFAULT_DNS_PORT;
+ }
+
+ // Complete specified nameservers if any.
+ if (list_size(&query->servers) > 0) {
+ WALK_LIST(n, query->servers) {
+ srv_info_t *s = (srv_info_t *)n;
+
+ // If the port isn't specified yet use the default one.
+ if (strlen(s->service) == 0) {
+ free(s->service);
+ s->service = strdup(def_port);
+ if (s->service == NULL) {
+ WARN("can't set port %s", def_port);
+ return;
+ }
+ }
+
+ // Use server name as hostname for TLS if necessary.
+ if (query->tls.enable && query->tls.hostname == NULL &&
+ (query->tls.system_ca || !EMPTY_LIST(query->tls.ca_files))) {
+ query->tls.hostname = strdup(s->name);
+ }
+ }
+ // Use servers from config if any.
+ } else if (list_size(&conf->servers) > 0) {
+ WALK_LIST(n, conf->servers) {
+ srv_info_t *s = (srv_info_t *)n;
+ char *port = def_port;
+
+ // If the port is already specified, use it.
+ if (strlen(s->service) > 0) {
+ port = s->service;
+ }
+
+ srv_info_t *server = srv_info_create(s->name, port);
+ if (server == NULL) {
+ WARN("can't set nameserver %s port %s",
+ s->name, s->service);
+ return;
+ }
+ add_tail(&query->servers, (node_t *)server);
+
+ // Use server name as hostname for TLS if necessary.
+ if (query->tls.enable && query->tls.hostname == NULL &&
+ (query->tls.system_ca || !EMPTY_LIST(query->tls.ca_files))) {
+ query->tls.hostname = strdup(s->name);
+ }
+ }
+ // Use system specific.
+ } else {
+ get_nameservers(&query->servers, def_port);
+ }
+}
+
+static bool compare_servers(list_t *s1, list_t *s2)
+{
+ if (list_size(s1) != list_size(s2)) {
+ return false;
+ }
+
+ node_t *n1, *n2 = HEAD(*s2);
+ WALK_LIST(n1, *s1) {
+ srv_info_t *i1 = (srv_info_t *)n1, *i2 = (srv_info_t *)n2;
+ if (strcmp(i1->service, i2->service) != 0 ||
+ strcmp(i1->name, i2->name) != 0)
+ {
+ return false;
+ }
+ n2 = n2->next;
+ }
+ return true;
+}
+
+void complete_queries(list_t *queries, const query_t *conf)
+{
+ node_t *n;
+
+ if (queries == NULL || conf == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ // If there is no query, add default query: NS to ".".
+ if (list_size(queries) == 0) {
+ query_t *q = query_create(".", conf);
+ if (q == NULL) {
+ WARN("can't create query . NS IN");
+ return;
+ }
+ q->class_num = KNOT_CLASS_IN;
+ q->type_num = KNOT_RRTYPE_NS;
+ add_tail(queries, (node_t *)q);
+ }
+
+ WALK_LIST(n, *queries) {
+ query_t *q = (query_t *)n;
+ query_t *q_prev = (HEAD(*queries) != n) ? (query_t *)n->prev : NULL;
+
+ // Fill class number if missing.
+ if (q->class_num < 0) {
+ if (conf->class_num >= 0) {
+ q->class_num = conf->class_num;
+ } else {
+ q->class_num = KNOT_CLASS_IN;
+ }
+ }
+
+ // Fill type number if missing.
+ if (q->type_num < 0) {
+ if (conf->type_num >= 0) {
+ q->type_num = conf->type_num;
+ q->serial = conf->serial;
+ } else {
+ q->type_num = KNOT_RRTYPE_A;
+ }
+ }
+
+ // Set zone transfer if any.
+ if (q->type_num == KNOT_RRTYPE_AXFR ||
+ q->type_num == KNOT_RRTYPE_IXFR) {
+ q->operation = OPERATION_XFR;
+ }
+
+ // No retries for TCP.
+ if (q->protocol == PROTO_TCP) {
+ q->retries = 0;
+ }
+
+ // Complete nameservers list.
+ complete_servers(q, conf);
+
+ // Check if using previous connection makes sense.
+ if (q_prev != NULL && q_prev->keepopen &&
+ (get_socktype(q->protocol, q->type_num) !=
+ get_socktype(q_prev->protocol, q_prev->type_num) ||
+ q->https.enable != q_prev->https.enable ||
+ q->tls.enable != q_prev->tls.enable ||
+ strcmp(q->port, q_prev->port) != 0 ||
+ !compare_servers(&q->servers, &q_prev->servers)))
+ {
+ WARN("connection parameters mismatch for query (%s), "
+ "ignoring keepopen", q->owner);
+ q_prev->keepopen = false;
+ }
+ }
+}
+
+static void print_help(void)
+{
+ printf("Usage: %s [-4] [-6] [-d] [-b address] [-c class] [-p port]\n"
+ " [-q name] [-t type] [-x address] [-k keyfile]\n"
+ " [-y [algo:]keyname:key] [-E tapfile] [-G tapfile]\n"
+ " name [type] [class] [@server]\n"
+ "\n"
+ " +[no]multiline Wrap long records to more lines.\n"
+ " +[no]short Show record data only.\n"
+ " +[no]generic Use generic representation format.\n"
+ " +[no]aaflag Set AA flag.\n"
+ " +[no]tcflag Set TC flag.\n"
+ " +[no]rdflag Set RD flag.\n"
+ " +[no]recurse Same as +[no]rdflag\n"
+ " +[no]raflag Set RA flag.\n"
+ " +[no]zflag Set zero flag bit.\n"
+ " +[no]adflag Set AD flag.\n"
+ " +[no]cdflag Set CD flag.\n"
+ " +[no]dnssec Set DO flag.\n"
+ " +[no]all Show all packet sections.\n"
+ " +[no]qr Show query packet.\n"
+ " +[no]header Show packet header.\n"
+ " +[no]comments Show commented section names.\n"
+ " +[no]opt Show EDNS pseudosection.\n"
+ " +[no]opttext Try to show unknown EDNS options as text.\n"
+ " +[no]question Show question section.\n"
+ " +[no]answer Show answer section.\n"
+ " +[no]authority Show authority section.\n"
+ " +[no]additional Show additional section.\n"
+ " +[no]tsig Show TSIG pseudosection.\n"
+ " +[no]stats Show trailing packet statistics.\n"
+ " +[no]class Show DNS class.\n"
+ " +[no]ttl Show TTL value.\n"
+ " +[no]crypto Show binary parts of RRSIGs and DNSKEYs.\n"
+ " +[no]tcp Use TCP protocol.\n"
+ " +[no]fastopen Use TCP Fast Open.\n"
+ " +[no]ignore Don't use TCP automatically if truncated.\n"
+ " +[no]keepopen Don't close the TCP connection to be reused.\n"
+ " +[no]tls Use TLS with Opportunistic privacy profile.\n"
+ " +[no]tls-ca[=FILE] Use TLS with Out-Of-Band privacy profile.\n"
+ " +[no]tls-pin=BASE64 Use TLS with pinned certificate.\n"
+ " +[no]tls-hostname=STR Use TLS with remote server hostname.\n"
+ " +[no]tls-sni=STR Use TLS with Server Name Indication.\n"
+ " +[no]tls-keyfile=FILE Use TLS with a client keyfile.\n"
+ " +[no]tls-certfile=FILE Use TLS with a client certfile.\n"
+ " +[no]tls-ocsp-stapling[=H] Use TLS with a valid stapled OCSP response for the\n"
+ " server certificate (%u or specify hours).\n"
+#ifdef LIBNGHTTP2
+ " +[no]https[=URL] Use HTTPS protocol. It's also possible to specify\n"
+ " URL as [authority][/path] where query will be sent.\n"
+ " +[no]https-get Use HTTPS protocol with GET method instead of POST.\n"
+#endif
+#ifdef ENABLE_QUIC
+ " +[no]quic Use QUIC protocol.\n"
+#endif
+ " +[no]nsid Request NSID.\n"
+ " +[no]bufsize=B Set EDNS buffer size.\n"
+ " +[no]padding[=N] Pad with EDNS(0) (default or specify size).\n"
+ " +[no]alignment[=N] Pad with EDNS(0) to blocksize (%u or specify size).\n"
+ " +[no]subnet=SUBN Set EDNS(0) client subnet addr/prefix.\n"
+ " +[no]edns[=N] Use EDNS(=version).\n"
+ " +[no]timeout=T Set wait for reply interval in seconds.\n"
+ " +[no]retry=N Set number of retries.\n"
+ " +[no]expire Set the EXPIRE EDNS option.\n"
+ " +[no]cookie[=HEX] Attach EDNS(0) cookie to the query.\n"
+ " +[no]badcookie Repeat a query with the correct cookie.\n"
+ " +[no]ednsopt=CODE[:HEX] Set custom EDNS option.\n"
+ " +[no]proxy=SADDR-DADDR Add PROXYv2 header with src and dest addresses.\n"
+ " +[no]json Use JSON for output encoding (RFC 8427).\n"
+ " +noidn Disable IDN transformation.\n"
+ "\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n",
+ PROGRAM_NAME, DEFAULT_TLS_OCSP_STAPLING / 3600, DEFAULT_ALIGNMENT_SIZE);
+}
+
+static int parse_opt1(const char *opt, const char *value, kdig_params_t *params,
+ int *index)
+{
+ const char *val = value;
+ size_t len = strlen(opt);
+ int add = 1;
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // If there is no space between option and argument.
+ if (len > 1) {
+ val = opt + 1;
+ add = 0;
+ }
+
+ switch (opt[0]) {
+ case '4':
+ if (len > 1) {
+ ERR("invalid option -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ query->ip = IP_4;
+ break;
+ case '6':
+ if (len > 1) {
+ ERR("invalid option -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ query->ip = IP_6;
+ break;
+ case 'b':
+ if (val == NULL) {
+ ERR("missing address");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_local(val, query) != KNOT_EOK) {
+ ERR("bad address %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'd':
+ msg_enable_debug(1);
+ break;
+ case 'h':
+ if (len > 1) {
+ ERR("invalid option -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ print_help();
+ params->stop = true;
+ break;
+ case 'c':
+ if (val == NULL) {
+ ERR("missing class");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_class(val, query) != KNOT_EOK) {
+ ERR("bad class %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'k':
+ if (val == NULL) {
+ ERR("missing filename");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_keyfile(val, query) != KNOT_EOK) {
+ ERR("bad keyfile %s", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'p':
+ if (val == NULL) {
+ ERR("missing port");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_port(val, query) != KNOT_EOK) {
+ ERR("bad port %s", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'q':
+ // Allow empty QNAME.
+ if (parse_name(val, &params->queries, params->config)
+ != KNOT_EOK) {
+ ERR("bad query name %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 't':
+ if (val == NULL) {
+ ERR("missing type");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_type(val, query) != KNOT_EOK) {
+ ERR("bad type %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'V':
+ if (len > 1) {
+ ERR("invalid option -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ print_version(PROGRAM_NAME);
+ params->stop = true;
+ break;
+ case 'x':
+ if (val == NULL) {
+ ERR("missing address");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_reverse(val, &params->queries, params->config)
+ != KNOT_EOK) {
+ ERR("bad reverse name %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'y':
+ if (val == NULL) {
+ ERR("missing key");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_tsig(val, query) != KNOT_EOK) {
+ ERR("bad key %s", value);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+ break;
+ case 'E':
+#if USE_DNSTAP
+ if (val == NULL) {
+ ERR("missing filename");
+ return KNOT_EINVAL;
+ }
+
+ if (parse_dnstap_output(val, query) != KNOT_EOK) {
+ ERR("unable to open dnstap output file %s", val);
+ return KNOT_EINVAL;
+ }
+ *index += add;
+#else
+ ERR("no dnstap support but -E specified");
+ return KNOT_EINVAL;
+#endif // USE_DNSTAP
+ break;
+ case 'G':
+#if USE_DNSTAP
+ if (val == NULL) {
+ ERR("missing filename");
+ return KNOT_EINVAL;
+ }
+
+ query = query_create(NULL, params->config);
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ if (parse_dnstap_input(val, query) != KNOT_EOK) {
+ ERR("unable to open dnstap input file %s", val);
+ query_free(query);
+ return KNOT_EINVAL;
+ }
+
+ query->operation = OPERATION_LIST_DNSTAP;
+ add_tail(&params->queries, (node_t *)query);
+
+ *index += add;
+#else
+ ERR("no dnstap support but -G specified");
+ return KNOT_EINVAL;
+#endif // USE_DNSTAP
+ break;
+ case '-':
+ if (strcmp(opt, "-help") == 0) {
+ print_help();
+ params->stop = true;
+ } else if (strcmp(opt, "-version") == 0) {
+ print_version(PROGRAM_NAME);
+ params->stop = true;
+ } else {
+ ERR("invalid option: -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+ break;
+ default:
+ ERR("invalid option: -%s", opt);
+ return KNOT_ENOTSUP;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_opt2(const char *value, kdig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // Get option name.
+ const char *arg_sep = "=";
+ size_t opt_len = strcspn(value, arg_sep);
+ if (opt_len < 1) {
+ ERR("invalid option: +%s", value);
+ return KNOT_ENOTSUP;
+ }
+
+ // Get option argument if any.
+ const char *arg = NULL;
+ const char *rest = value + opt_len;
+ if (strlen(rest) > 0) {
+ arg = rest + strspn(rest, arg_sep);
+ }
+
+ // Check if the given option is supported.
+ bool unique;
+ int ret = best_param(value, opt_len, kdig_opts2, &unique);
+ if (ret < 0) {
+ ERR("invalid option: +%s", value);
+ return KNOT_ENOTSUP;
+ } else if (!unique) {
+ ERR("ambiguous option: +%s", value);
+ return KNOT_ENOTSUP;
+ }
+
+ // Check argument presence.
+ switch (kdig_opts2[ret].arg) {
+ case ARG_NONE:
+ if (arg != NULL && *arg != '\0') {
+ WARN("superfluous option argument: +%s", value);
+ }
+ break;
+ case ARG_REQUIRED:
+ if (arg == NULL) {
+ ERR("missing argument: +%s", value);
+ return KNOT_EFEWDATA;
+ }
+ // FALLTHROUGH
+ case ARG_OPTIONAL:
+ if (arg != NULL && *arg == '\0') {
+ ERR("empty argument: +%s", value);
+ return KNOT_EFEWDATA;
+ }
+ break;
+ }
+
+ // Call option handler.
+ return kdig_opts2[ret].handler(arg, query);
+}
+
+static int parse_token(const char *value, kdig_params_t *params)
+{
+ query_t *query;
+
+ // Set current query (last or config).
+ if (list_size(&params->queries) > 0) {
+ query = TAIL(params->queries);
+ } else {
+ query = params->config;
+ }
+
+ // Try to guess the meaning of the token.
+ if (strlen(value) == 0) {
+ ERR("invalid empty parameter");
+ } else if (parse_type(value, query) == KNOT_EOK) {
+ return KNOT_EOK;
+ } else if (parse_class(value, query) == KNOT_EOK) {
+ return KNOT_EOK;
+ } else if (parse_name(value, &params->queries, params->config) == KNOT_EOK) {
+ return KNOT_EOK;
+ } else {
+ ERR("invalid parameter: %s", value);
+ }
+
+ return KNOT_EINVAL;
+}
+
+int kdig_parse(kdig_params_t *params, int argc, char *argv[])
+{
+ if (params == NULL || argv == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ // Initialize parameters.
+ if (kdig_init(params) != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+#ifdef LIBIDN
+ // Set up localization.
+ if (setlocale(LC_CTYPE, "") == NULL) {
+ WARN("can't setlocale, disabling IDN");
+ params->config->idn = false;
+ params->config->style.style.ascii_to_idn = NULL;
+ }
+#endif
+
+ // Command line parameters processing.
+ for (int i = 1; i < argc; i++) {
+ int ret = KNOT_ERROR;
+
+ // Process parameter.
+ switch (argv[i][0]) {
+ case '@':
+ ret = parse_server(argv[i] + 1, params);
+ break;
+ case '-':
+ ret = parse_opt1(argv[i] + 1, argv[i + 1], params, &i);
+ break;
+ case '+':
+ ret = parse_opt2(argv[i] + 1, params);
+ break;
+ default:
+ ret = parse_token(argv[i], params);
+ break;
+ }
+
+ // Check return.
+ switch (ret) {
+ case KNOT_EOK:
+ if (params->stop) {
+ return KNOT_EOK;
+ }
+ break;
+ case KNOT_ENOTSUP:
+ print_help();
+ default: // Fall through.
+ return ret;
+ }
+ }
+
+ // Complete missing data in queries based on defaults.
+ complete_queries(&params->queries, params->config);
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/kdig/kdig_params.h b/src/utils/kdig/kdig_params.h
new file mode 100644
index 0000000..03aafe3
--- /dev/null
+++ b/src/utils/kdig/kdig_params.h
@@ -0,0 +1,187 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "utils/common/params.h"
+#include "utils/common/exec.h"
+#include "utils/common/https.h"
+#include "utils/common/quic.h"
+#include "utils/common/sign.h"
+#include "libknot/libknot.h"
+#include "contrib/sockaddr.h"
+
+#if USE_DNSTAP
+# include "contrib/dnstap/reader.h"
+# include "contrib/dnstap/writer.h"
+#endif // USE_DNSTAP
+
+/*! \brief Operation mode of kdig. */
+typedef enum {
+ /*!< Standard 1-message query/reply. */
+ OPERATION_QUERY,
+ /*!< Zone transfer (AXFR or IXFR). */
+ OPERATION_XFR,
+ /*!< Dump dnstap file. */
+ OPERATION_LIST_DNSTAP,
+} operation_t;
+
+/*! \brief DNS header and EDNS flags. */
+typedef struct {
+ /*!< Authoritative answer flag. */
+ bool aa_flag;
+ /*!< Truncated flag. */
+ bool tc_flag;
+ /*!< Recursion desired flag. */
+ bool rd_flag;
+ /*!< Recursion available flag. */
+ bool ra_flag;
+ /*!< Z flag. */
+ bool z_flag;
+ /*!< Authenticated data flag. */
+ bool ad_flag;
+ /*!< Checking disabled flag. */
+ bool cd_flag;
+ /*!< DNSSEC OK flag. */
+ bool do_flag;
+} flags_t;
+
+/*! \brief Basic parameters for DNS query. */
+typedef struct query query_t; // Forward declaration due to configuration.
+struct query {
+ /*!< List node (for list container). */
+ node_t n;
+ /*!< Reference to global config. */
+ const query_t *conf;
+ /*!< Name to query on. */
+ char *owner;
+ /*!< List of nameservers to query to. */
+ list_t servers;
+ /*!< Local interface (optional). */
+ srv_info_t *local;
+ /*!< Operation mode. */
+ operation_t operation;
+ /*!< Version of ip protocol to use. */
+ ip_t ip;
+ /*!< Protocol type (TCP, UDP) to use. */
+ protocol_t protocol;
+ /*!< Use TCP Fast Open. */
+ bool fastopen;
+ /*!< Keep TCP connection open. */
+ bool keepopen;
+ /*!< Port/service to connect to. */
+ char *port;
+ /*!< UDP buffer size (16unsigned + -1 uninitialized). */
+ int32_t udp_size;
+ /*!< Number of UDP retries. */
+ uint32_t retries;
+ /*!< Wait for network response in seconds (-1 means forever). */
+ int32_t wait;
+ /*!< Ignore truncated response. */
+ bool ignore_tc;
+ /*!< Class number (16unsigned + -1 uninitialized). */
+ int32_t class_num;
+ /*!< Type number (16unsigned + -1 uninitialized). */
+ int32_t type_num;
+ /*!< SOA serial for IXFR and NOTIFY (32unsigned + -1 uninitialized). */
+ int64_t serial;
+ /*!< NOTIFY query. */
+ bool notify;
+ /*!< Header flags. */
+ flags_t flags;
+ /*!< Output settings. */
+ style_t style;
+ /*!< IDN conversion. */
+ bool idn;
+ /*!< Query for NSID. */
+ bool nsid;
+ /*!< EDNS version (8unsigned + -1 uninitialized). */
+ int16_t edns;
+ /*!< EDNS client cookie. */
+ knot_edns_cookie_t cc;
+ /*!< EDNS server cookie. */
+ knot_edns_cookie_t sc;
+ /*!< Repeat query after BADCOOKIE. */
+ int badcookie;
+ /*!< EDNS0 padding (16unsigned + -1 ~ uninitialized, -2 ~ default, -3 ~ none). */
+ int32_t padding;
+ /*!< Query alignment with EDNS0 padding (0 ~ uninitialized). */
+ uint16_t alignment;
+ /*!< TLS parameters. */
+ tls_params_t tls;
+ /*!< HTTPS parameters. */
+ https_params_t https;
+ /*!< QUIC parameters. */
+ quic_params_t quic;
+ /*!< Transaction signature. */
+ knot_tsig_key_t tsig_key;
+ /*!< EDNS client subnet. */
+ knot_edns_client_subnet_t subnet;
+ /*!< Lits of custom EDNS options. */
+ list_t edns_opts;
+ /*!< PROXYv2 source and destination address. */
+ struct {
+ struct sockaddr_storage src;
+ struct sockaddr_storage dst;
+ } proxy;
+#if USE_DNSTAP
+ /*!< Context for dnstap reader input. */
+ dt_reader_t *dt_reader;
+ /*!< Context for dnstap writer output. */
+ dt_writer_t *dt_writer;
+#endif // USE_DNSTAP
+};
+
+/*! \brief EDNS option data. */
+typedef struct {
+ /*! List node (for list container). */
+ node_t n;
+ /*!< OPTION-CODE field. */
+ uint16_t code;
+ /*!< OPTION-LENGTH field. */
+ uint16_t length;
+ /*!< OPTION-DATA field. */
+ uint8_t *data;
+} ednsopt_t;
+
+/*! \brief Settings for kdig. */
+typedef struct {
+ /*!< Stop processing - just print help, version,... */
+ bool stop;
+ /*!< List of DNS queries to process. */
+ list_t queries;
+ /*!< Default settings for queries. */
+ query_t *config;
+} kdig_params_t;
+
+query_t *query_create(const char *owner, const query_t *config);
+void query_free(query_t *query);
+void complete_queries(list_t *queries, const query_t *conf);
+
+ednsopt_t *ednsopt_create(uint16_t code, uint16_t length, uint8_t *data);
+ednsopt_t *ednsopt_dup(const ednsopt_t *opt);
+void ednsopt_free(ednsopt_t *opt);
+
+void ednsopt_list_init(list_t *list);
+void ednsopt_list_deinit(list_t *list);
+int ednsopt_list_dup(list_t *dst, const list_t *src);
+bool ednsopt_list_empty(const list_t *list);
+
+int kdig_init(kdig_params_t *params);
+int kdig_parse(kdig_params_t *params, int argc, char *argv[]);
+void kdig_clean(kdig_params_t *params);
diff --git a/src/utils/keymgr/bind_privkey.c b/src/utils/keymgr/bind_privkey.c
new file mode 100644
index 0000000..70f03e3
--- /dev/null
+++ b/src/utils/keymgr/bind_privkey.c
@@ -0,0 +1,411 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include "contrib/ctype.h"
+#include "contrib/strtonum.h"
+#include "libdnssec/binary.h"
+#include "libdnssec/error.h"
+#include "libdnssec/pem.h"
+#include "libdnssec/shared/shared.h"
+#include "utils/keymgr/bind_privkey.h"
+
+/* -- private key params conversion ---------------------------------------- */
+
+/*!
+ * Private key attribute conversion.
+ */
+typedef struct param_t {
+ char *name;
+ size_t offset;
+ int (*parse_cb)(char *string, void *data);
+ void (*free_cb)(void *data);
+} param_t;
+
+static int parse_algorithm(char *string, void *_algorithm);
+static int parse_binary(char *string, void *_binary);
+static int parse_time(char *string, void *_time);
+
+static void binary_free(void *_binary)
+{
+ dnssec_binary_t *binary = _binary;
+ dnssec_binary_free(binary);
+}
+
+/*!
+ * Know attributes in private key file.
+ */
+const param_t PRIVKEY_CONVERSION_TABLE[] = {
+ #define o(field) offsetof(bind_privkey_t, field)
+ { "Algorithm", o(algorithm), parse_algorithm, NULL },
+ { "Modulus", o(modulus), parse_binary, binary_free },
+ { "PublicExponent", o(public_exponent), parse_binary, binary_free },
+ { "PrivateExponent", o(private_exponent), parse_binary, binary_free },
+ { "Prime1", o(prime_one), parse_binary, binary_free },
+ { "Prime2", o(prime_two), parse_binary, binary_free },
+ { "Exponent1", o(exponent_one), parse_binary, binary_free },
+ { "Exponent2", o(exponent_two), parse_binary, binary_free },
+ { "Coefficient", o(coefficient), parse_binary, binary_free },
+ { "PrivateKey", o(private_key), parse_binary, binary_free },
+ { "Created", o(time_created), parse_time, NULL },
+ { "Publish", o(time_publish), parse_time, NULL },
+ { "Activate", o(time_activate), parse_time, NULL },
+ { "Revoke", o(time_revoke), parse_time, NULL },
+ { "Inactive", o(time_inactive), parse_time, NULL },
+ { "Delete", o(time_delete), parse_time, NULL },
+ { NULL }
+ #undef o
+};
+
+/* -- attribute parsing ---------------------------------------------------- */
+
+/*!
+ * Parse key algorithm field.
+ *
+ * Example: 7 (NSEC3RSASHA1)
+ *
+ * Only the numeric value is decoded, the rest of the value is ignored.
+ */
+static int parse_algorithm(char *string, void *_algorithm)
+{
+ char *end = string;
+ while (*end != '\0' && !is_space(*end)) {
+ end += 1;
+ }
+ *end = '\0';
+
+ uint8_t *algorithm = _algorithm;
+ int r = str_to_u8(string, algorithm);
+
+ return (r == KNOT_EOK ? DNSSEC_EOK : DNSSEC_INVALID_KEY_ALGORITHM);
+}
+
+/*!
+ * Parse binary data encoded in Base64.
+ *
+ * Example: AQAB
+ */
+static int parse_binary(char *string, void *_binary)
+{
+ dnssec_binary_t base64 = {
+ .data = (uint8_t *)string,
+ .size = strlen(string)
+ };
+
+ dnssec_binary_t *binary = _binary;
+ return dnssec_binary_from_base64(&base64, binary);
+}
+
+#define LEGACY_DATE_FORMAT "%Y%m%d%H%M%S"
+
+/*!
+ * Parse timestamp in a format in \ref LEGACY_DATE_FORMAT.
+ *
+ * Example: 20140415151855
+ */
+static int parse_time(char *string, void *_time)
+{
+ struct tm tm = { 0 };
+
+ char *end = strptime(string, LEGACY_DATE_FORMAT, &tm);
+ if (end == NULL || *end != '\0') {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ time_t *time = _time;
+ *time = timegm(&tm);
+
+ return DNSSEC_EOK;
+}
+
+/* -- key parsing ---------------------------------------------------------- */
+
+/*!
+ * Strip string value of left and right whitespaces.
+ *
+ * \param[in,out] value Start of the string.
+ * \param[in,out] length Length of the string.
+ *
+ */
+static void strip(char **value, size_t *length)
+{
+ // strip from left
+ while (*length > 0 && is_space(**value)) {
+ *value += 1;
+ *length -= 1;
+ }
+ // strip from right
+ while (*length > 0 && is_space((*value)[*length - 1])) {
+ *length -= 1;
+ }
+}
+
+/*!
+ * Parse one line of the private key file.
+ */
+static int parse_line(bind_privkey_t *params, char *line, size_t length)
+{
+ assert(params);
+ assert(line);
+
+ strip(&line, &length);
+ if (length == 0) {
+ return KNOT_EOK; // blank line
+ }
+
+ char *separator = memchr(line, ':', length);
+ if (!separator) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ char *key = line;
+ size_t key_length = separator - key;
+ strip(&key, &key_length);
+
+ char *value = separator + 1;
+ size_t value_length = (line + length) - value;
+ strip(&value, &value_length);
+
+ if (key_length == 0 || value_length == 0) {
+ return DNSSEC_MALFORMED_DATA;
+ }
+
+ key[key_length] = '\0';
+ value[value_length] = '\0';
+
+ for (const param_t *p = PRIVKEY_CONVERSION_TABLE; p->name != NULL; p++) {
+ size_t name_length = strlen(p->name);
+ if (name_length != key_length) {
+ continue;
+ }
+
+ if (strcasecmp(p->name, key) != 0) {
+ continue;
+ }
+
+ return p->parse_cb(value, (void *)params + p->offset);
+ }
+
+ // ignore unknown attributes
+
+ return DNSSEC_EOK;
+}
+
+int bind_privkey_parse(const char *filename, bind_privkey_t *params_ptr)
+{
+ _cleanup_fclose_ FILE *file = fopen(filename, "r");
+ if (!file) {
+ return DNSSEC_NOT_FOUND;
+ }
+
+ bind_privkey_t params = *params_ptr;
+
+ _cleanup_free_ char *line = NULL;
+ size_t size = 0;
+ ssize_t read = 0;
+ while ((read = getline(&line, &size, file)) != -1) {
+ int r = parse_line(&params, line, read);
+ if (r != DNSSEC_EOK) {
+ bind_privkey_free(&params);
+ return r;
+ }
+ }
+
+ *params_ptr = params;
+
+ return DNSSEC_EOK;
+}
+
+/* -- freeing -------------------------------------------------------------- */
+
+/*!
+ * Free private key parameters.
+ */
+void bind_privkey_free(bind_privkey_t *params)
+{
+ if (!params) {
+ return;
+ }
+
+ for (const param_t *p = PRIVKEY_CONVERSION_TABLE; p->name != NULL; p++) {
+ if (p->free_cb) {
+ p->free_cb((void *)params + p->offset);
+ }
+ }
+
+ clear_struct(params);
+}
+
+/* -- export to PEM -------------------------------------------------------- */
+
+static int rsa_params_to_pem(const bind_privkey_t *params, dnssec_binary_t *pem)
+{
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ int result = gnutls_x509_privkey_init(&key);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ gnutls_datum_t m = binary_to_datum(&params->modulus);
+ gnutls_datum_t e = binary_to_datum(&params->public_exponent);
+ gnutls_datum_t d = binary_to_datum(&params->private_exponent);
+ gnutls_datum_t p = binary_to_datum(&params->prime_one);
+ gnutls_datum_t q = binary_to_datum(&params->prime_two);
+ gnutls_datum_t u = binary_to_datum(&params->coefficient);
+
+ result = gnutls_x509_privkey_import_rsa_raw(key, &m, &e, &d, &p, &q, &u);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ return dnssec_pem_from_x509(key, pem);
+}
+
+/*!
+ * \see lib/key/convert.h
+ */
+static gnutls_ecc_curve_t choose_ecdsa_curve(size_t pubkey_size)
+{
+ switch (pubkey_size) {
+#ifdef HAVE_ED25519
+ case 32: return GNUTLS_ECC_CURVE_ED25519;
+#endif
+#ifdef HAVE_ED448
+ case 57: return GNUTLS_ECC_CURVE_ED448;
+#endif
+ case 64: return GNUTLS_ECC_CURVE_SECP256R1;
+ case 96: return GNUTLS_ECC_CURVE_SECP384R1;
+ default: return GNUTLS_ECC_CURVE_INVALID;
+ }
+}
+
+static void ecdsa_extract_public_params(dnssec_key_t *key, gnutls_ecc_curve_t *curve,
+ gnutls_datum_t *x, gnutls_datum_t *y)
+{
+ dnssec_binary_t pubkey = { 0 };
+ dnssec_key_get_pubkey(key, &pubkey);
+
+ *curve = choose_ecdsa_curve(pubkey.size);
+
+ size_t param_size = pubkey.size / 2;
+ x->data = pubkey.data;
+ x->size = param_size;
+ y->data = pubkey.data + param_size;
+ y->size = param_size;
+}
+
+static int ecdsa_params_to_pem(dnssec_key_t *dnskey, const bind_privkey_t *params,
+ dnssec_binary_t *pem)
+{
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ int result = gnutls_x509_privkey_init(&key);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ gnutls_ecc_curve_t curve = 0;
+ gnutls_datum_t x = { 0 };
+ gnutls_datum_t y = { 0 };
+ ecdsa_extract_public_params(dnskey, &curve, &x, &y);
+
+ gnutls_datum_t k = binary_to_datum(&params->private_key);
+
+ result = gnutls_x509_privkey_import_ecc_raw(key, curve, &x, &y, &k);
+ if (result != DNSSEC_EOK) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ gnutls_x509_privkey_fix(key);
+
+ return dnssec_pem_from_x509(key, pem);
+}
+
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+static void eddsa_extract_public_params(dnssec_key_t *key, gnutls_ecc_curve_t *curve,
+ gnutls_datum_t *x)
+{
+ dnssec_binary_t pubkey = { 0 };
+ dnssec_key_get_pubkey(key, &pubkey);
+
+ *curve = choose_ecdsa_curve(pubkey.size);
+
+ x->data = pubkey.data;
+ x->size = pubkey.size;
+}
+
+static int eddsa_params_to_pem(dnssec_key_t *dnskey, const bind_privkey_t *params,
+ dnssec_binary_t *pem)
+{
+ _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL;
+ int result = gnutls_x509_privkey_init(&key);
+ if (result != GNUTLS_E_SUCCESS) {
+ return DNSSEC_ENOMEM;
+ }
+
+ gnutls_ecc_curve_t curve = 0;
+ gnutls_datum_t x = { 0 };
+ eddsa_extract_public_params(dnskey, &curve, &x);
+
+ gnutls_datum_t k = binary_to_datum(&params->private_key);
+
+ result = gnutls_x509_privkey_import_ecc_raw(key, curve, &x, NULL, &k);
+ if (result != DNSSEC_EOK) {
+ return DNSSEC_KEY_IMPORT_ERROR;
+ }
+
+ gnutls_x509_privkey_fix(key);
+
+ return dnssec_pem_from_x509(key, pem);
+}
+#endif
+
+int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary_t *pem)
+{
+ dnssec_key_algorithm_t algorithm = dnssec_key_get_algorithm(key);
+ switch (algorithm) {
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA256:
+ case DNSSEC_KEY_ALGORITHM_RSA_SHA512:
+ return rsa_params_to_pem(params, pem);
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256:
+ case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384:
+ return ecdsa_params_to_pem(key, params, pem);
+#ifdef HAVE_ED25519
+ case DNSSEC_KEY_ALGORITHM_ED25519:
+#endif
+#ifdef HAVE_ED448
+ case DNSSEC_KEY_ALGORITHM_ED448:
+#endif
+#if defined(HAVE_ED25519) || defined(HAVE_ED448)
+ return eddsa_params_to_pem(key, params, pem);
+#endif
+ default:
+ return DNSSEC_INVALID_KEY_ALGORITHM;
+ }
+}
+
+void bind_privkey_to_timing(bind_privkey_t *params, knot_kasp_key_timing_t *timing)
+{
+ // timing->created remains "now"
+ timing->publish = (knot_time_t)params->time_publish;
+ timing->ready = 0;
+ timing->active = (knot_time_t)params->time_activate;
+ timing->retire = (knot_time_t)params->time_inactive;
+ timing->revoke = (knot_time_t)params->time_revoke;
+ timing->remove = (knot_time_t)params->time_delete;
+}
diff --git a/src/utils/keymgr/bind_privkey.h b/src/utils/keymgr/bind_privkey.h
new file mode 100644
index 0000000..cdb4924
--- /dev/null
+++ b/src/utils/keymgr/bind_privkey.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <time.h>
+
+#include "libdnssec/binary.h"
+#include "knot/dnssec/kasp/policy.h"
+
+/*!
+ * Legacy private key parameters.
+ */
+typedef struct {
+ // key information
+ uint8_t algorithm;
+
+ // RSA
+ dnssec_binary_t modulus;
+ dnssec_binary_t public_exponent;
+ dnssec_binary_t private_exponent;
+ dnssec_binary_t prime_one;
+ dnssec_binary_t prime_two;
+ dnssec_binary_t exponent_one;
+ dnssec_binary_t exponent_two;
+ dnssec_binary_t coefficient;
+
+ // ECDSA
+ dnssec_binary_t private_key;
+
+ // key lifetime
+ time_t time_created;
+ time_t time_publish;
+ time_t time_activate;
+ time_t time_revoke;
+ time_t time_inactive;
+ time_t time_delete;
+} bind_privkey_t;
+
+/*!
+ * Extract parameters from legacy private key file.
+ */
+int bind_privkey_parse(const char *filename, bind_privkey_t *params);
+
+/*!
+ * Free private key parameters.
+ */
+void bind_privkey_free(bind_privkey_t *params);
+
+/*!
+ * Generate PEM from pub&priv key.
+ */
+int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary_t *pem);
+
+/*!
+ * Extract timing info.
+ */
+void bind_privkey_to_timing(bind_privkey_t *params, knot_kasp_key_timing_t *timing);
diff --git a/src/utils/keymgr/functions.c b/src/utils/keymgr/functions.c
new file mode 100644
index 0000000..cd40f7f
--- /dev/null
+++ b/src/utils/keymgr/functions.c
@@ -0,0 +1,1135 @@
+/* Copyright (C) 2022 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 <limits.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <fcntl.h>
+
+#include "utils/keymgr/functions.h"
+
+#include "utils/common/msg.h"
+#include "utils/keymgr/bind_privkey.h"
+#include "contrib/base64.h"
+#include "contrib/color.h"
+#include "contrib/ctype.h"
+#include "contrib/json.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "contrib/tolower.h"
+#include "contrib/wire_ctx.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keyid.h"
+#include "libdnssec/shared/shared.h"
+#include "knot/dnssec/kasp/policy.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/dnssec/zone-sign.h"
+#include "libzscanner/scanner.h"
+
+int parse_timestamp(char *arg, knot_time_t *stamp)
+{
+ int ret = knot_time_parse("YMDhms|'now'+-#u|'t'+-#u|+-#u|'t'+-#|+-#|#",
+ arg, stamp);
+ if (ret < 0) {
+ ERR2("invalid timestamp: %s", arg);
+ return KNOT_EINVAL;
+ }
+ return KNOT_EOK;
+}
+
+static bool init_timestamps(char *arg, knot_kasp_key_timing_t *timing)
+{
+ knot_time_t *dst = NULL;
+
+ if (strncasecmp(arg, "created=", 8) == 0) {
+ dst = &timing->created;
+ } else if (strncasecmp(arg, "publish=", 8) == 0) {
+ dst = &timing->publish;
+ } else if (strncasecmp(arg, "ready=", 6) == 0) {
+ dst = &timing->ready;
+ } else if (strncasecmp(arg, "active=", 7) == 0) {
+ dst = &timing->active;
+ } else if (strncasecmp(arg, "retire=", 7) == 0) {
+ dst = &timing->retire;
+ } else if (strncasecmp(arg, "remove=", 7) == 0) {
+ dst = &timing->remove;
+ } else if (strncasecmp(arg, "pre_active=", 11) == 0) {
+ dst = &timing->pre_active;
+ } else if (strncasecmp(arg, "post_active=", 12) == 0) {
+ dst = &timing->post_active;
+ } else if (strncasecmp(arg, "retire_active=", 14) == 0) {
+ dst = &timing->retire_active;
+ } else if (strncasecmp(arg, "revoke=", 7) == 0) {
+ dst = &timing->revoke;
+ } else {
+ return false;
+ }
+
+ knot_time_t stamp;
+ int ret = parse_timestamp(strchr(arg, '=') + 1, &stamp);
+ if (ret != KNOT_EOK) {
+ return true;
+ }
+
+ *dst = stamp;
+
+ return true;
+}
+
+static bool str2bool(const char *s)
+{
+ switch (knot_tolower(s[0])) {
+ case '1':
+ case 'y':
+ case 't':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void bitmap_set(kdnssec_generate_flags_t *bitmap, int flag, bool onoff)
+{
+ if (onoff) {
+ *bitmap |= flag;
+ } else {
+ *bitmap &= ~flag;
+ }
+}
+
+static bool genkeyargs(int argc, char *argv[], bool just_timing,
+ kdnssec_generate_flags_t *flags, dnssec_key_algorithm_t *algorithm,
+ uint16_t *keysize, knot_kasp_key_timing_t *timing,
+ const char **addtopolicy)
+{
+ // generate algorithms field
+ char *algnames[256] = { 0 };
+ algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA1] = "rsasha1";
+ algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3] = "rsasha1nsec3sha1";
+ algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA256] = "rsasha256";
+ algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA512] = "rsasha512";
+ algnames[DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256] = "ecdsap256sha256";
+ algnames[DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384] = "ecdsap384sha384";
+ algnames[DNSSEC_KEY_ALGORITHM_ED25519] = "ed25519";
+ algnames[DNSSEC_KEY_ALGORITHM_ED448] = "ed448";
+
+ // parse args
+ for (int i = 0; i < argc; i++) {
+ if (!just_timing && strncasecmp(argv[i], "algorithm=", 10) == 0) {
+ int alg = 256; // invalid value
+ (void)str_to_int(argv[i] + 10, &alg, 0, 255);
+ for (int al = 0; al < 256 && alg > 255; al++) {
+ if (algnames[al] != NULL &&
+ strcasecmp(argv[i] + 10, algnames[al]) == 0) {
+ alg = al;
+ }
+ }
+ if (alg > 255) {
+ ERR2("unknown algorithm: %s", argv[i] + 10);
+ return false;
+ }
+ *algorithm = alg;
+ } else if (strncasecmp(argv[i], "ksk=", 4) == 0) {
+ bitmap_set(flags, DNSKEY_GENERATE_KSK, str2bool(argv[i] + 4));
+ } else if (strncasecmp(argv[i], "zsk=", 4) == 0) {
+ bitmap_set(flags, DNSKEY_GENERATE_ZSK, str2bool(argv[i] + 4));
+ } else if (strncasecmp(argv[i], "sep=", 4) == 0) {
+ bitmap_set(flags, DNSKEY_GENERATE_SEP_SPEC, true);
+ bitmap_set(flags, DNSKEY_GENERATE_SEP_ON, str2bool(argv[i] + 4));
+ } else if (!just_timing && strncasecmp(argv[i], "size=", 5) == 0) {
+ if (str_to_u16(argv[i] + 5, keysize) != KNOT_EOK) {
+ ERR2("invalid size: '%s'", argv[i] + 5);
+ return false;
+ }
+ } else if (!just_timing && strncasecmp(argv[i], "addtopolicy=", 12) == 0) {
+ *addtopolicy = argv[i] + 12;
+ } else if (!init_timestamps(argv[i], timing)) {
+ ERR2("invalid parameter: %s", argv[i]);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool _check_lower(knot_time_t a, knot_time_t b,
+ const char *a_name, const char *b_name)
+{
+ if (knot_time_cmp(a, b) > 0) {
+ ERR2("timestamp '%s' must be before '%s'", a_name, b_name);
+ return false;
+ }
+ return true;
+}
+
+#define check_lower(t, a, b) if (!_check_lower(t->a, t->b, #a, #b)) return KNOT_ESEMCHECK
+
+static int check_timers(const knot_kasp_key_timing_t *t)
+{
+ if (t->pre_active != 0) {
+ check_lower(t, pre_active, publish);
+ }
+ check_lower(t, publish, active);
+ check_lower(t, active, retire_active);
+ check_lower(t, active, retire);
+ check_lower(t, active, post_active);
+ if (t->post_active == 0) {
+ check_lower(t, retire, remove);
+ }
+ return KNOT_EOK;
+}
+
+#undef check_lower
+
+// modifies ctx->policy options, so don't do anything afterwards !
+int keymgr_generate_key(kdnssec_ctx_t *ctx, int argc, char *argv[])
+{
+ knot_time_t now = knot_time(), infinity = 0;
+ knot_kasp_key_timing_t gen_timing = { now, infinity, now, infinity, now, infinity, infinity, infinity, infinity };
+ kdnssec_generate_flags_t flags = 0;
+ uint16_t keysize = 0;
+ const char *addtopolicy = NULL;
+ if (!genkeyargs(argc, argv, false, &flags, &ctx->policy->algorithm,
+ &keysize, &gen_timing, &addtopolicy)) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = check_timers(&gen_timing);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if ((flags & DNSKEY_GENERATE_KSK) && gen_timing.ready == infinity) {
+ gen_timing.ready = gen_timing.active;
+ }
+
+ if (keysize == 0) {
+ keysize = dnssec_algorithm_key_size_default(ctx->policy->algorithm);
+ }
+ if ((flags & DNSKEY_GENERATE_KSK)) {
+ ctx->policy->ksk_size = keysize;
+ } else {
+ ctx->policy->zsk_size = keysize;
+ }
+
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *kasp_key = &ctx->zone->keys[i];
+ if ((kasp_key->is_ksk && (flags & DNSKEY_GENERATE_KSK)) &&
+ dnssec_key_get_algorithm(kasp_key->key) != ctx->policy->algorithm) {
+ WARN2("creating key with different algorithm than "
+ "configured in the policy");
+ break;
+ }
+ }
+
+ knot_kasp_key_t *key = NULL;
+ ret = kdnssec_generate_key(ctx, flags, &key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ key->timing = gen_timing;
+
+ if (addtopolicy != NULL) {
+ char *last_policy_last = NULL;
+
+ knot_dname_t *unused = NULL;
+ ret = kasp_db_get_policy_last(ctx->kasp_db, addtopolicy, &unused,
+ &last_policy_last);
+ knot_dname_free(unused, NULL);
+ if (ret != KNOT_EOK && ret != KNOT_ENOENT) {
+ free(last_policy_last);
+ return ret;
+ }
+
+ ret = kasp_db_set_policy_last(ctx->kasp_db, addtopolicy, last_policy_last,
+ ctx->zone->dname, key->id);
+ free(last_policy_last);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ ret = kdnssec_ctx_commit(ctx);
+
+ if (ret == KNOT_EOK) {
+ printf("%s\n", key->id);
+ }
+
+ return ret;
+}
+
+static void parse_record(zs_scanner_t *scanner)
+{
+ dnssec_key_t *key = scanner->process.data;
+
+ if (dnssec_key_get_dname(key) != NULL ||
+ scanner->r_type != KNOT_RRTYPE_DNSKEY) {
+ scanner->state = ZS_STATE_STOP;
+ return;
+ }
+
+ dnssec_binary_t rdata = {
+ .data = scanner->r_data,
+ .size = scanner->r_data_length
+ };
+ dnssec_key_set_dname(key, scanner->dname);
+ dnssec_key_set_rdata(key, &rdata);
+}
+
+int bind_pubkey_parse(const char *filename, dnssec_key_t **key_ptr)
+{
+ dnssec_key_t *key = NULL;
+ int result = dnssec_key_new(&key);
+ if (result != DNSSEC_EOK) {
+ return KNOT_ENOMEM;
+ }
+
+ uint16_t cls = KNOT_CLASS_IN;
+ uint32_t ttl = 0;
+ zs_scanner_t *scanner = malloc(sizeof(zs_scanner_t));
+ if (scanner == NULL) {
+ dnssec_key_free(key);
+ return KNOT_ENOMEM;
+ }
+
+ if (zs_init(scanner, ".", cls, ttl) != 0 ||
+ zs_set_input_file(scanner, filename) != 0 ||
+ zs_set_processing(scanner, parse_record, NULL, key) != 0 ||
+ zs_parse_all(scanner) != 0) {
+ int ret;
+ switch (scanner->error.code) {
+ case ZS_FILE_OPEN:
+ case ZS_FILE_INVALID:
+ ret = KNOT_EFILE;
+ break;
+ default:
+ ret = KNOT_EPARSEFAIL;
+ }
+ zs_deinit(scanner);
+ free(scanner);
+ dnssec_key_free(key);
+ return ret;
+ }
+ zs_deinit(scanner);
+ free(scanner);
+
+ if (dnssec_key_get_dname(key) == NULL) {
+ dnssec_key_free(key);
+ return KNOT_INVALID_PUBLIC_KEY;
+ }
+
+ *key_ptr = key;
+ return KNOT_EOK;
+}
+
+static char *gen_keyfilename(const char *orig, const char *wantsuff, const char *altsuff)
+{
+ assert(orig && wantsuff && altsuff);
+
+ const char *dot = strrchr(orig, '.');
+
+ if (dot != NULL && strcmp(dot, wantsuff) == 0) { // Full match.
+ return strdup(orig);
+ } else if (dot != NULL && strcmp(dot, altsuff) == 0) { // Replace suffix.
+ return sprintf_alloc("%.*s%s", (int)(dot - orig), orig, wantsuff);
+ } else { // Add wanted suffix.
+ return sprintf_alloc("%s%s", orig, wantsuff);
+ }
+}
+
+int keymgr_import_bind(kdnssec_ctx_t *ctx, const char *import_file, bool pub_only)
+{
+ if (ctx == NULL || import_file == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ knot_kasp_key_timing_t timing = { ctx->now, 0 };
+ dnssec_key_t *key = NULL;
+ char *keyid = NULL;
+
+ char *pubname = gen_keyfilename(import_file, ".key", ".private");
+ if (pubname == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = bind_pubkey_parse(pubname, &key);
+ free(pubname);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+
+ if (!pub_only) {
+ bind_privkey_t bpriv = { .time_publish = ctx->now, .time_activate = ctx->now };
+
+ char *privname = gen_keyfilename(import_file, ".private", ".key");
+ if (privname == NULL) {
+ goto fail;
+ }
+
+ ret = bind_privkey_parse(privname, &bpriv);
+ free(privname);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+
+ dnssec_binary_t pem = { 0 };
+ ret = bind_privkey_to_pem(key, &bpriv, &pem);
+ if (ret != DNSSEC_EOK) {
+ bind_privkey_free(&bpriv);
+ goto fail;
+ }
+
+ bind_privkey_to_timing(&bpriv, &timing);
+
+ bind_privkey_free(&bpriv);
+
+ ret = dnssec_keystore_import(ctx->keystore, &pem, &keyid);
+ dnssec_binary_free(&pem);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+ } else {
+ timing.publish = ctx->now;
+
+ ret = dnssec_key_get_keyid(key, &keyid);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+ }
+
+ // allocate kasp key
+ knot_kasp_key_t *kkey = calloc(1, sizeof(*kkey));
+ if (!kkey) {
+ ret = KNOT_ENOMEM;
+ goto fail;
+ }
+
+ kkey->id = keyid;
+ kkey->key = key;
+ kkey->timing = timing;
+ kkey->is_pub_only = pub_only;
+ kkey->is_ksk = (dnssec_key_get_flags(kkey->key) == DNSKEY_FLAGS_KSK);
+ kkey->is_zsk = !kkey->is_ksk;
+
+ // append to zone
+ ret = kasp_zone_append(ctx->zone, kkey);
+ free(kkey);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+ ret = kdnssec_ctx_commit(ctx);
+ if (ret == KNOT_EOK) {
+ printf("%s\n", keyid);
+ return KNOT_EOK;
+ }
+fail:
+ dnssec_key_free(key);
+ free(keyid);
+ return knot_error_from_libdnssec(ret);
+}
+
+static int import_key(kdnssec_ctx_t *ctx, unsigned backend, const char *param,
+ int argc, char *argv[])
+{
+ if (ctx == NULL || param == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ // parse params
+ knot_time_t now = knot_time();
+ knot_kasp_key_timing_t timing = { .publish = now, .active = now };
+ kdnssec_generate_flags_t flags = 0;
+ uint16_t keysize = 0;
+ if (!genkeyargs(argc, argv, false, &flags, &ctx->policy->algorithm,
+ &keysize, &timing, NULL)) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = check_timers(&timing);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ normalize_generate_flags(&flags);
+
+ dnssec_key_t *key = NULL;
+ char *keyid = NULL;
+
+ if (backend == KEYSTORE_BACKEND_PEM) {
+ // open file
+ int fd = open(param, O_RDONLY, 0);
+ if (fd == -1) {
+ return knot_map_errno();
+ }
+
+ // determine size
+ off_t fsize = lseek(fd, 0, SEEK_END);
+ if (fsize == -1) {
+ close(fd);
+ return knot_map_errno();
+ }
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ close(fd);
+ return knot_map_errno();
+ }
+
+ // alloc memory
+ dnssec_binary_t pem = { 0 };
+ ret = dnssec_binary_alloc(&pem, fsize);
+ if (ret != DNSSEC_EOK) {
+ close(fd);
+ goto fail;
+ }
+
+ // read pem
+ ssize_t read_count = read(fd, pem.data, pem.size);
+ close(fd);
+ if (read_count == -1) {
+ dnssec_binary_free(&pem);
+ ret = knot_map_errno();
+ goto fail;
+ }
+
+ // put pem to keystore
+ ret = dnssec_keystore_import(ctx->keystore, &pem, &keyid);
+ dnssec_binary_free(&pem);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+ } else {
+ assert(backend == KEYSTORE_BACKEND_PKCS11);
+ keyid = strdup(param);
+ }
+
+ // create dnssec key
+ ret = dnssec_key_new(&key);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+ ret = dnssec_key_set_dname(key, ctx->zone->dname);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+ dnssec_key_set_flags(key, dnskey_flags(flags & DNSKEY_GENERATE_SEP_ON));
+ dnssec_key_set_algorithm(key, ctx->policy->algorithm);
+
+ // fill key structure from keystore (incl. pubkey from privkey computation)
+ ret = dnssec_keystore_get_private(ctx->keystore, keyid, key);
+ if (ret != DNSSEC_EOK) {
+ goto fail;
+ }
+
+ // allocate kasp key
+ knot_kasp_key_t *kkey = calloc(1, sizeof(*kkey));
+ if (kkey == NULL) {
+ ret = KNOT_ENOMEM;
+ goto fail;
+ }
+ kkey->id = keyid;
+ kkey->key = key;
+ kkey->timing = timing;
+ kkey->is_ksk = (flags & DNSKEY_GENERATE_KSK);
+ kkey->is_zsk = (flags & DNSKEY_GENERATE_ZSK);
+
+ // append to zone
+ ret = kasp_zone_append(ctx->zone, kkey);
+ free(kkey);
+ if (ret != KNOT_EOK) {
+ goto fail;
+ }
+ ret = kdnssec_ctx_commit(ctx);
+ if (ret == KNOT_EOK) {
+ printf("%s\n", keyid);
+ return KNOT_EOK;
+ }
+fail:
+ dnssec_key_free(key);
+ free(keyid);
+ return knot_error_from_libdnssec(ret);
+}
+
+int keymgr_import_pem(kdnssec_ctx_t *ctx, const char *import_file, int argc, char *argv[])
+{
+ return import_key(ctx, KEYSTORE_BACKEND_PEM, import_file, argc, argv);
+}
+
+int keymgr_import_pkcs11(kdnssec_ctx_t *ctx, char *key_id, int argc, char *argv[])
+{
+ if (!dnssec_keyid_is_valid(key_id)) {
+ return DNSSEC_INVALID_KEY_ID;
+ }
+ dnssec_keyid_normalize(key_id);
+ return import_key(ctx, KEYSTORE_BACKEND_PKCS11, key_id, argc, argv);
+}
+
+int keymgr_nsec3_salt_print(kdnssec_ctx_t *ctx)
+{
+ dnssec_binary_t salt_bin;
+ knot_time_t created;
+ int ret = kasp_db_load_nsec3salt(ctx->kasp_db, ctx->zone->dname,
+ &salt_bin, &created);
+ switch (ret) {
+ case KNOT_EOK:
+ printf("Current salt: ");
+ if (salt_bin.size == 0) {
+ printf("-");
+ }
+ for (size_t i = 0; i < salt_bin.size; i++) {
+ printf("%02X", (unsigned)salt_bin.data[i]);
+ }
+ printf("\n");
+ free(salt_bin.data);
+ break;
+ case KNOT_ENOENT:
+ printf("-- no salt --\n");
+ ret = KNOT_EOK;
+ break;
+ }
+ return ret;
+}
+
+int keymgr_nsec3_salt_set(kdnssec_ctx_t *ctx, const char *new_salt)
+{
+ assert(new_salt);
+
+ dnssec_binary_t salt_bin = { 0 };
+ if (strcmp(new_salt, "-") != 0) {
+ salt_bin.data = hex_to_bin(new_salt, &salt_bin.size);
+ if (salt_bin.data == NULL) {
+ return KNOT_EMALF;
+ }
+ }
+ if (salt_bin.size != ctx->policy->nsec3_salt_length) {
+ WARN2("specified salt doesn't match configured salt length (%d)",
+ (int)ctx->policy->nsec3_salt_length);
+ }
+ int ret = kasp_db_store_nsec3salt(ctx->kasp_db, ctx->zone->dname,
+ &salt_bin, knot_time());
+ if (salt_bin.size > 0) {
+ free(salt_bin.data);
+ }
+ return ret;
+}
+
+int keymgr_serial_print(kdnssec_ctx_t *ctx, kaspdb_serial_t type)
+{
+ uint32_t serial = 0;
+ int ret = kasp_db_load_serial(ctx->kasp_db, ctx->zone->dname,
+ type, &serial);
+ switch (ret) {
+ case KNOT_EOK:
+ printf("Current serial: %u\n", serial);
+ break;
+ case KNOT_ENOENT:
+ printf("-- no serial --\n");
+ ret = KNOT_EOK;
+ break;
+ }
+ return ret;
+}
+
+int keymgr_serial_set(kdnssec_ctx_t *ctx, kaspdb_serial_t type, uint32_t new_serial)
+{
+ return kasp_db_store_serial(ctx->kasp_db, ctx->zone->dname,
+ type, new_serial);
+}
+
+static void print_tsig(dnssec_tsig_algorithm_t mac, const char *name,
+ const dnssec_binary_t *secret)
+{
+ assert(name);
+ assert(secret);
+
+ const char *mac_name = dnssec_tsig_algorithm_to_name(mac);
+ assert(mac_name);
+
+ // client format (as a comment)
+ printf("# %s:%s:%.*s\n", mac_name, name, (int)secret->size, secret->data);
+
+ // server format
+ printf("key:\n");
+ printf(" - id: %s\n", name);
+ printf(" algorithm: %s\n", mac_name);
+ printf(" secret: %.*s\n", (int)secret->size, secret->data);
+}
+
+int keymgr_generate_tsig(const char *tsig_name, const char *alg_name, int bits)
+{
+ dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_name(alg_name);
+ if (alg == DNSSEC_TSIG_UNKNOWN) {
+ return KNOT_INVALID_KEY_ALGORITHM;
+ }
+
+ int optimal_bits = dnssec_tsig_optimal_key_size(alg);
+ if (bits == 0) {
+ bits = optimal_bits;
+ }
+
+ // round up bits to bytes
+ bits = (bits + CHAR_BIT - 1) / CHAR_BIT * CHAR_BIT;
+
+ if (bits < optimal_bits) {
+ WARN2("optimal key size for %s is at least %d bits",
+ dnssec_tsig_algorithm_to_name(alg), optimal_bits);
+ }
+ assert(bits % CHAR_BIT == 0);
+
+ _cleanup_binary_ dnssec_binary_t key = { 0 };
+ int r = dnssec_binary_alloc(&key, bits / CHAR_BIT);
+ if (r != DNSSEC_EOK) {
+ ERR2("failed to allocate memory");
+ return knot_error_from_libdnssec(r);
+ }
+
+ r = gnutls_rnd(GNUTLS_RND_KEY, key.data, key.size);
+ if (r != 0) {
+ ERR2("failed to generate secret the key");
+ return knot_error_from_libdnssec(r);
+ }
+
+ _cleanup_binary_ dnssec_binary_t key_b64 = { 0 };
+ r = dnssec_binary_to_base64(&key, &key_b64);
+ if (r != DNSSEC_EOK) {
+ ERR2("failed to convert the key to Base64");
+ return knot_error_from_libdnssec(r);
+ }
+
+ print_tsig(alg, tsig_name, &key_b64);
+
+ return KNOT_EOK;
+}
+
+static bool is_hex(const char *string)
+{
+ for (const char *p = string; *p != '\0'; p++) {
+ if (!is_xdigit(*p)) {
+ return false;
+ }
+ }
+ return (*string != '\0');
+}
+
+int keymgr_get_key(kdnssec_ctx_t *ctx, const char *key_spec, knot_kasp_key_t **key)
+{
+ // Check if type of key spec is prescribed.
+ bool is_keytag = false, is_id = false;
+ if (strncasecmp(key_spec, "keytag=", 7) == 0) {
+ key_spec += 7;
+ is_keytag = true;
+ } else if (strncasecmp(key_spec, "id=", 3) == 0) {
+ key_spec += 3;
+ is_id = true;
+ }
+
+ uint16_t keytag = 0;
+ bool can_be_keytag = (str_to_u16(key_spec, &keytag) == KNOT_EOK);
+ long spec_len = strlen(key_spec);
+
+ // Check if input is a valid key spec.
+ if ((is_keytag && !can_be_keytag) ||
+ (is_id && !is_hex(key_spec)) ||
+ (!can_be_keytag && !is_hex(key_spec))) {
+ ERR2("invalid key specification");
+ return KNOT_EINVAL;
+ }
+
+ *key = NULL;
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *candidate = &ctx->zone->keys[i];
+
+ bool keyid_match = strncmp(candidate->id, key_spec, spec_len) == 0; // May be just a prefix.
+ bool keytag_match = can_be_keytag &&
+ dnssec_key_get_keytag(candidate->key) == keytag;
+
+ // Terminate if found exact key ID match.
+ if (keyid_match && !is_keytag && strlen(candidate->id) == spec_len) {
+ *key = candidate;
+ break;
+ }
+ // Check for key ID prefix or tag match.
+ if ((is_keytag && keytag_match) || // Tag is prescribed.
+ (is_id && keyid_match) || // Key ID is prescribed.
+ ((!is_keytag && !is_id) && (keyid_match || keytag_match))) { // Nothing is prescribed.
+ if (*key == NULL) {
+ *key = candidate;
+ } else {
+ ERR2("key not specified uniquely, please use id=Full_Key_ID");
+ return KNOT_EINVAL;
+ }
+ }
+ }
+ if (*key == NULL) {
+ ERR2("key not found");
+ return KNOT_ENOENT;
+ }
+ return KNOT_EOK;
+}
+
+int keymgr_foreign_key_id(char *argv[], knot_lmdb_db_t *kaspdb, knot_dname_t **key_zone, char **key_id)
+{
+ *key_zone = knot_dname_from_str_alloc(argv[3]);
+ if (*key_zone == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_dname_to_lower(*key_zone);
+
+ kdnssec_ctx_t kctx = { 0 };
+ int ret = kdnssec_ctx_init(conf(), &kctx, *key_zone, kaspdb, NULL);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to initialize zone %s (%s)", argv[0], knot_strerror(ret));
+ free(*key_zone);
+ *key_zone = NULL;
+ return KNOT_ENOZONE;
+ }
+ knot_kasp_key_t *key;
+ ret = keymgr_get_key(&kctx, argv[2], &key);
+ if (ret == KNOT_EOK) {
+ *key_id = strdup(key->id);
+ if (*key_id == NULL) {
+ ret = KNOT_ENOMEM;
+ }
+ }
+ kdnssec_ctx_deinit(&kctx);
+ return ret;
+}
+
+int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[])
+{
+ knot_kasp_key_timing_t temp = key->timing;
+ kdnssec_generate_flags_t flags = ((key->is_ksk ? DNSKEY_GENERATE_KSK : 0) | (key->is_zsk ? DNSKEY_GENERATE_ZSK : 0));
+
+ if (genkeyargs(argc, argv, true, &flags, NULL, NULL, &temp, NULL)) {
+ int ret = check_timers(&temp);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ key->timing = temp;
+ if (key->is_ksk != (bool)(flags & DNSKEY_GENERATE_KSK) ||
+ key->is_zsk != (bool)(flags & DNSKEY_GENERATE_ZSK) ||
+ flags & DNSKEY_GENERATE_SEP_SPEC) {
+ normalize_generate_flags(&flags);
+ key->is_ksk = (flags & DNSKEY_GENERATE_KSK);
+ key->is_zsk = (flags & DNSKEY_GENERATE_ZSK);
+ return dnssec_key_set_flags(key->key, dnskey_flags(flags & DNSKEY_GENERATE_SEP_ON));
+ }
+ return KNOT_EOK;
+ }
+ return KNOT_EINVAL;
+}
+
+typedef struct {
+ const char *name;
+ size_t offset;
+} timer_ctx_t;
+
+static const timer_ctx_t timers[] = {
+ { "pre-active", offsetof(knot_kasp_key_timing_t, pre_active) },
+ { "publish", offsetof(knot_kasp_key_timing_t, publish) },
+ { "ready", offsetof(knot_kasp_key_timing_t, ready) },
+ { "active", offsetof(knot_kasp_key_timing_t, active) },
+ { "retire-active", offsetof(knot_kasp_key_timing_t, retire_active) },
+ { "retire", offsetof(knot_kasp_key_timing_t, retire) },
+ { "post-active", offsetof(knot_kasp_key_timing_t, post_active) },
+ { "revoke", offsetof(knot_kasp_key_timing_t, revoke) },
+ { "remove", offsetof(knot_kasp_key_timing_t, remove) },
+ { NULL }
+};
+
+static void print_key_brief(const knot_kasp_key_t *key, keymgr_list_params_t *params)
+{
+ const bool c = params->color;
+
+ printf("%s %s%5u%s ",
+ key->id, COL_BOLD(c), dnssec_key_get_keytag(key->key), COL_RST(c));
+
+ printf("%s%s%s%s ",
+ COL_BOLD(c),
+ (key->is_ksk ? (key->is_zsk ? COL_YELW(c) : COL_RED(c)) : COL_GRN(c)),
+ (key->is_ksk ? (key->is_zsk ? "CSK" : "KSK") : "ZSK"),
+ COL_RST(c));
+
+ uint8_t alg = dnssec_key_get_algorithm(key->key);
+ const knot_lookup_t *alg_info = knot_lookup_by_id(knot_dnssec_alg_names, alg);
+ if (alg_info != NULL) {
+ printf("%s", alg_info->name);
+ if (alg <= DNSSEC_KEY_ALGORITHM_RSA_SHA512) {
+ printf("%s/%u%s", COL_DIM(c), dnssec_key_get_size(key->key), COL_RST(c));
+ }
+ } else {
+ printf("ALGORITHM_%u", alg);
+ }
+
+ if (key->is_pub_only) {
+ printf(" %s%spublic-only%s", COL_BOLD(c), COL_MGNT(c), COL_RST(c));
+ }
+
+ static char buf[100];
+ knot_time_t now = knot_time();
+ for (const timer_ctx_t *t = &timers[0]; t->name != NULL; t++) {
+ knot_time_t *val = (void *)(&key->timing) + t->offset;
+ if (*val == 0) {
+ continue;
+ }
+ bool past = (knot_time_cmp(*val, now) <= 0);
+ const char *UNDR = past ? COL_UNDR(c) : "";
+ const char *BOLD = past ? "" : COL_BOLD(c);
+ for (const timer_ctx_t *t2 = t + 1; past && t2->name != NULL; t2++) {
+ knot_time_t *val2 = (void *)(&key->timing) + t2->offset;
+ if (knot_time_cmp(*val2, now) <= 0) {
+ UNDR = "";
+ break;
+ }
+ }
+ (void)knot_time_print(params->format, *val, buf, sizeof(buf));
+ printf(" %s%s%s=%s%s%s", UNDR, t->name, COL_RST(c), BOLD, buf, COL_RST(c));
+ }
+ printf("\n");
+}
+
+static void print_key_full(const knot_kasp_key_t *key, knot_time_print_t format)
+{
+ printf("%s ksk=%s zsk=%s tag=%05d algorithm=%-2d size=%-4u public-only=%s", key->id,
+ (key->is_ksk ? "yes" : "no "), (key->is_zsk ? "yes" : "no "),
+ dnssec_key_get_keytag(key->key), (int)dnssec_key_get_algorithm(key->key),
+ dnssec_key_get_size(key->key), (key->is_pub_only ? "yes" : "no "));
+
+ static char buf[100];
+ for (const timer_ctx_t *t = &timers[0]; t->name != NULL; t++) {
+ knot_time_t *val = (void *)(&key->timing) + t->offset;
+ (void)knot_time_print(format, *val, buf, sizeof(buf));
+ printf(" %s=%s", t->name, buf);
+ }
+ printf("\n");
+}
+
+static void print_key_json(const knot_kasp_key_t *key, knot_time_print_t format,
+ jsonw_t *w, const char *zone_name)
+{
+ jsonw_str(w, "zone", zone_name);
+ jsonw_str(w, "id", key->id);
+ jsonw_bool(w, "ksk", key->is_ksk);
+ jsonw_bool(w, "zsk", key->is_zsk);
+ jsonw_int(w, "tag", dnssec_key_get_keytag(key->key));
+ jsonw_ulong(w, "algorithm", dnssec_key_get_algorithm(key->key));
+ jsonw_int(w, "size", dnssec_key_get_size(key->key));
+ jsonw_bool(w, "public-only", key->is_pub_only);
+
+ static char buf[100];
+ for (const timer_ctx_t *t = &timers[0]; t->name != NULL; t++) {
+ knot_time_t *val = (void *)(&key->timing) + t->offset;
+ (void)knot_time_print(format, *val, buf, sizeof(buf));
+
+ if (format == TIME_PRINT_UNIX) {
+ jsonw_int(w, t->name, *val);
+ } else {
+ jsonw_str(w, t->name, buf);
+ }
+ }
+}
+
+typedef struct {
+ knot_time_t val;
+ const knot_kasp_key_t *key;
+} key_sort_item_t;
+
+static int key_sort(const void *a, const void *b)
+{
+ const key_sort_item_t *key_a = a;
+ const key_sort_item_t *key_b = b;
+ return knot_time_cmp(key_a->val, key_b->val);
+}
+
+int keymgr_list_keys(kdnssec_ctx_t *ctx, keymgr_list_params_t *params)
+{
+ if (ctx->zone->num_keys == 0) {
+ return KNOT_EOK;
+ }
+
+ if (params->extended) {
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ print_key_full(key, params->format);
+ }
+ } else if (params->json) {
+ jsonw_t *w = jsonw_new(stdout, " ");
+ if (w == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ knot_dname_txt_storage_t name;
+ (void)knot_dname_to_str(name, ctx->zone->dname, sizeof(name));
+
+ jsonw_list(w, NULL);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ jsonw_object(w, NULL);
+ print_key_json(key, params->format, w, name);
+ jsonw_end(w); // object
+ }
+ jsonw_end(w); // list
+ jsonw_free(&w);
+ } else {
+ key_sort_item_t items[ctx->zone->num_keys];
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ items[i].key = key;
+ if (knot_time_cmp(key->timing.pre_active, key->timing.publish) < 0) {
+ items[i].val = key->timing.pre_active;
+ } else {
+ items[i].val = key->timing.publish;
+ }
+ }
+ qsort(&items, ctx->zone->num_keys, sizeof(items[0]), key_sort);
+ for (size_t i = 0; i < ctx->zone->num_keys; i++) {
+ print_key_brief(items[i].key, params);
+ }
+ }
+ return KNOT_EOK;
+}
+
+static int print_ds(const knot_dname_t *dname, const dnssec_binary_t *rdata)
+{
+ wire_ctx_t ctx = wire_ctx_init(rdata->data, rdata->size);
+ if (wire_ctx_available(&ctx) < 4) {
+ return KNOT_EMALF;
+ }
+
+ char *name = knot_dname_to_str_alloc(dname);
+ if (!name) {
+ return KNOT_ENOMEM;
+ }
+
+ uint16_t keytag = wire_ctx_read_u16(&ctx);
+ uint8_t algorithm = wire_ctx_read_u8(&ctx);
+ uint8_t digest_type = wire_ctx_read_u8(&ctx);
+
+ size_t digest_size = wire_ctx_available(&ctx);
+
+ printf("%s DS %d %d %d ", name, keytag, algorithm, digest_type);
+ for (size_t i = 0; i < digest_size; i++) {
+ printf("%02x", ctx.position[i]);
+ }
+ printf("\n");
+
+ free(name);
+ return KNOT_EOK;
+}
+
+static int create_and_print_ds(const knot_dname_t *zone_name,
+ const dnssec_key_t *key, dnssec_key_digest_t digest)
+{
+ _cleanup_binary_ dnssec_binary_t rdata = { 0 };
+ int r = dnssec_key_create_ds(key, digest, &rdata);
+ if (r != DNSSEC_EOK) {
+ return knot_error_from_libdnssec(r);
+ }
+
+ return print_ds(zone_name, &rdata);
+}
+
+int keymgr_generate_ds(const knot_dname_t *dname, const knot_kasp_key_t *key)
+{
+ static const dnssec_key_digest_t digests[] = {
+ DNSSEC_KEY_DIGEST_SHA256,
+ DNSSEC_KEY_DIGEST_SHA384,
+ 0
+ };
+
+ int ret = KNOT_EOK;
+ for (int i = 0; digests[i] != 0 && ret == KNOT_EOK; i++) {
+ ret = create_and_print_ds(dname, key->key, digests[i]);
+ }
+
+ return ret;
+}
+
+int keymgr_generate_dnskey(const knot_dname_t *dname, const knot_kasp_key_t *key)
+{
+ const dnssec_key_t *dnskey = key->key;
+
+ char *name = knot_dname_to_str_alloc(dname);
+ if (!name) {
+ return KNOT_ENOMEM;
+ }
+
+ uint16_t flags = dnssec_key_get_flags(dnskey);
+ uint8_t algorithm = dnssec_key_get_algorithm(dnskey);
+
+ dnssec_binary_t pubkey = { 0 };
+ int ret = dnssec_key_get_pubkey(dnskey, &pubkey);
+ if (ret != DNSSEC_EOK) {
+ free(name);
+ return knot_error_from_libdnssec(ret);
+ }
+
+ uint8_t *base64_output = NULL;
+ int len = knot_base64_encode_alloc(pubkey.data, pubkey.size, &base64_output);
+ if (len < 0) {
+ free(name);
+ return len;
+ }
+
+ printf("%s DNSKEY %u 3 %u %.*s\n", name, flags, algorithm, len, base64_output);
+
+ free(base64_output);
+ free(name);
+ return KNOT_EOK;
+}
+
+int keymgr_list_zones(knot_lmdb_db_t *kaspdb, bool json)
+{
+ jsonw_t *w;
+ list_t zones;
+ init_list(&zones);
+ int ret = kasp_db_list_zones(kaspdb, &zones);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to initialize KASP (%s)", knot_strerror(ret));
+ return ret;
+ }
+
+ knot_dname_txt_storage_t name;
+ ptrnode_t *node;
+
+ if (json) {
+ w = jsonw_new(stdout, " ");
+ if (w == NULL) {
+ ERR2("failed to allocate memory");
+ ptrlist_deep_free(&zones, NULL);
+ return KNOT_ENOMEM;
+ }
+ jsonw_list(w, NULL);
+ }
+ WALK_LIST(node, zones) {
+ (void)knot_dname_to_str(name, node->d, sizeof(name));
+ if (json) {
+ jsonw_str(w, NULL, name);
+ } else {
+ printf("%s\n", name);
+ }
+ }
+ if (json) {
+ jsonw_end(w); // list
+ jsonw_free(&w);
+ }
+
+ ptrlist_deep_free(&zones, NULL);
+ return KNOT_EOK;
+}
diff --git a/src/utils/keymgr/functions.h b/src/utils/keymgr/functions.h
new file mode 100644
index 0000000..9c16d80
--- /dev/null
+++ b/src/utils/keymgr/functions.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdio.h>
+
+#include "knot/dnssec/context.h"
+
+typedef struct {
+ knot_time_print_t format;
+ bool extended;
+ bool color;
+ bool json;
+} keymgr_list_params_t;
+
+int parse_timestamp(char *arg, knot_time_t *stamp);
+
+int keymgr_generate_key(kdnssec_ctx_t *ctx, int argc, char *argv[]);
+
+int keymgr_import_bind(kdnssec_ctx_t *ctx, const char *import_file, bool pub_only);
+
+int keymgr_import_pem(kdnssec_ctx_t *ctx, const char *import_file, int argc, char *argv[]);
+
+int keymgr_import_pkcs11(kdnssec_ctx_t *ctx, char *key_id, int argc, char *argv[]);
+
+int keymgr_nsec3_salt_print(kdnssec_ctx_t *ctx);
+
+int keymgr_nsec3_salt_set(kdnssec_ctx_t *ctx, const char *new_salt);
+
+int keymgr_serial_print(kdnssec_ctx_t *ctx, kaspdb_serial_t type);
+
+int keymgr_serial_set(kdnssec_ctx_t *ctx, kaspdb_serial_t type, uint32_t new_serial);
+
+int keymgr_generate_tsig(const char *tsig_name, const char *alg_name, int bits);
+
+int keymgr_get_key(kdnssec_ctx_t *ctx, const char *key_spec, knot_kasp_key_t **key);
+
+int keymgr_foreign_key_id(char *argv[], knot_lmdb_db_t *kaspdb, knot_dname_t **key_zone, char **key_id);
+
+int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[]);
+
+int keymgr_list_keys(kdnssec_ctx_t *ctx, keymgr_list_params_t *params);
+
+int keymgr_generate_ds(const knot_dname_t *dname, const knot_kasp_key_t *key);
+
+int keymgr_generate_dnskey(const knot_dname_t *dname, const knot_kasp_key_t *key);
+
+int keymgr_list_zones(knot_lmdb_db_t *kaspdb, bool json);
diff --git a/src/utils/keymgr/main.c b/src/utils/keymgr/main.c
new file mode 100644
index 0000000..cf40c59
--- /dev/null
+++ b/src/utils/keymgr/main.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "contrib/strtonum.h"
+#include "knot/dnssec/zone-keys.h"
+#include "libknot/libknot.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/common/util_conf.h"
+#include "utils/keymgr/functions.h"
+#include "utils/keymgr/offline_ksk.h"
+
+#define PROGRAM_NAME "keymgr"
+
+static void print_help(void)
+{
+ printf("Usage:\n"
+ " %s [-c | -C | -D <path>] <zone_name> <command> [<argument>...]\n"
+ " %s [-c | -C | -D <path>] -l\n"
+ " %s -t <tsig_name> [<algorithm> [<bits>]]\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> Path to a textual configuration file.\n"
+ " (default %s)\n"
+ " -C, --confdb <dir> Path to a configuration database directory.\n"
+ " (default %s)\n"
+ " -D, --dir <path> Path to a KASP database directory, use default configuration.\n"
+ " -t, --tsig <name> [alg] Generate a TSIG key.\n"
+ " -e, --extended Extended output (listing of keys with full description).\n"
+ " -j, --json Print the zones or keys in JSON format.\n"
+ " -l, --list List all zones that have at least one key in KASP database.\n"
+ " -x, --mono Don't color the output.\n"
+ " -X, --color Force output colorization in the normal mode.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n"
+ "\n"
+ "Commands:\n"
+ " list List all zone's DNSSEC keys.\n"
+ " generate Generate new DNSSEC key.\n"
+ " (syntax: generate <attribute_name>=<value>...)\n"
+ " import-bind Import BIND-style key file pair (.key + .private).\n"
+ " (syntax: import-bind <key_file_name>)\n"
+ " import-pub Import public-only key to be published in the zone (in BIND .key format).\n"
+ " (syntax: import-pub <key_file_name>)\n"
+ " import-pem Import key in PEM format. Specify its parameters manually.\n"
+ " (syntax: import-pem <pem_file_path> <attribute_name>=<value>...)\n"
+ " import-pkcs11 Import key stored in PKCS11 storage. Specify its parameters manually.\n"
+ " (syntax: import-pkcs11 <key_id> <attribute_name>=<value>...)\n"
+ " nsec3-salt Print current NSEC3 salt. If a parameter is specified, set new salt.\n"
+ " (syntax: nsec3-salt [<new_salt>])\n"
+ " local-serial Print SOA serial stored in KASP database when using on-slave signing.\n"
+ " If a parameter is specified, set new serial.\n"
+ " (syntax: local-serial <new_serial>)\n"
+ " master-serial Print SOA serial of the remote master stored in KASP database when using on-slave signing.\n"
+ " If a parameter is specified, set new master serial.\n"
+ " (syntax: master-serial <new_serial>)\n"
+ " ds Generate DS record(s) for specified key.\n"
+ " (syntax: ds <key_spec>)\n"
+ " dnskey Generate DNSKEY record for specified key.\n"
+ " (syntax: dnskey <key_spec>)\n"
+ " share Share an existing key of another zone with the specified zone.\n"
+ " (syntax: share <full_key_ID> <zone2share_from>\n"
+ " delete Remove the specified key from zone.\n"
+ " (syntax: delete <key_spec>)\n"
+ " set Set existing key's timing attribute.\n"
+ " (syntax: set <key_spec> <attribute_name>=<value>...)\n"
+ "\n"
+ "Commands related to Offline KSK feature:\n"
+ " pregenerate Pre-generate ZSKs for later rollovers with offline KSK.\n"
+ " (syntax: pregenerate [<from>] <to>)\n"
+ " show-offline Print pre-generated offline key-related records for specified time interval (possibly to infinity).\n"
+ " (syntax: show-offline [<from>] [<to>])\n"
+ " del-offline Delete pre-generated offline key-related records in specified time interval.\n"
+ " (syntax: del-offline <from> <to>)\n"
+ " del-all-old Delete old keys that are in state 'removed'.\n"
+ " generate-ksr Print to stdout KeySigningRequest based on pre-generated ZSKS.\n"
+ " (syntax: generate-ksr [<from>] <to>)\n"
+ " sign-ksr Read KeySigningRequest from a file, sign it and print SignedKeyResponse to stdout.\n"
+ " (syntax: sign-ksr <ksr_file>)\n"
+ " validate-skr Validate RRSIGs in a SignedKeyResponse (if not corrupt).\n"
+ " (syntax: validate-skr <skr_file>)\n"
+ " import-skr Import DNSKEY record signatures from a SignedKeyResponse.\n"
+ " (syntax: import-skr <skr_file>)\n"
+ "\n"
+ "Key specification:\n"
+ " either the key tag (number) or [a prefix of] key ID, with an optional\n"
+ " [id=|keytag=] prefix.\n"
+ "\n"
+ "Key attributes:\n"
+ " algorithm The key cryptographic algorithm: either name (e.g. RSASHA256) or\n"
+ " number.\n"
+ " size The key size in bits.\n"
+ " ksk Whether the generated/imported key shall be Key Signing Key.\n"
+ " created/publish/ready/active/retire/remove The timestamp of the key\n"
+ " lifetime event (e.g. published=+1d active=1499770874)\n",
+ PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
+}
+
+static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kaspdb,
+ keymgr_list_params_t *list_params)
+{
+ if (argc < opt_ind + 2) {
+ ERR2("zone name or command not specified");
+ print_help();
+ return KNOT_EINVAL;
+ }
+ argc -= opt_ind;
+ argv += opt_ind;
+
+ knot_dname_t *zone_name = knot_dname_from_str_alloc(argv[0]);
+ if (zone_name == NULL) {
+ return KNOT_ENOMEM;
+ }
+ knot_dname_to_lower(zone_name);
+
+ kdnssec_ctx_t kctx = { 0 };
+
+ int ret = kdnssec_ctx_init(conf(), &kctx, zone_name, kaspdb, NULL);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to initialize KASP (%s)", knot_strerror(ret));
+ goto main_end;
+ }
+
+#define CHECK_MISSING_ARG(msg) \
+ if (argc < 3) { \
+ ERR2("%s", (msg)); \
+ ret = KNOT_EINVAL; \
+ goto main_end; \
+ }
+
+#define CHECK_MISSING_ARG2(msg) \
+ if (argc < 4) { \
+ ERR2("%s", (msg)); \
+ ret = KNOT_EINVAL; \
+ goto main_end; \
+ }
+
+ bool print_ok_on_succes = true;
+ if (strcmp(argv[1], "generate") == 0) {
+ ret = keymgr_generate_key(&kctx, argc - 2, argv + 2);
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "import-bind") == 0) {
+ CHECK_MISSING_ARG("BIND-style key to import not specified");
+ ret = keymgr_import_bind(&kctx, argv[2], false);
+ } else if (strcmp(argv[1], "import-pub") == 0) {
+ CHECK_MISSING_ARG("BIND-style key to import not specified");
+ ret = keymgr_import_bind(&kctx, argv[2], true);
+ } else if (strcmp(argv[1], "import-pem") == 0) {
+ CHECK_MISSING_ARG("PEM file to import not specified");
+ ret = keymgr_import_pem(&kctx, argv[2], argc - 3, argv + 3);
+ } else if (strcmp(argv[1], "import-pkcs11") == 0) {
+ CHECK_MISSING_ARG("Key ID to import not specified");
+ ret = keymgr_import_pkcs11(&kctx, argv[2], argc - 3, argv + 3);
+ } else if (strcmp(argv[1], "nsec3-salt") == 0) {
+ if (argc > 2) {
+ ret = keymgr_nsec3_salt_set(&kctx, argv[2]);
+ } else {
+ ret = keymgr_nsec3_salt_print(&kctx);
+ print_ok_on_succes = false;
+ }
+ } else if (strcmp(argv[1], "local-serial") == 0 || strcmp(argv[1], "master-serial") == 0 ) {
+ kaspdb_serial_t type = (argv[1][0] == 'm' ? KASPDB_SERIAL_MASTER : KASPDB_SERIAL_LASTSIGNED);
+ if (argc > 2) {
+ uint32_t new_serial = 0;
+ if ((ret = str_to_u32(argv[2], &new_serial)) == KNOT_EOK) {
+ ret = keymgr_serial_set(&kctx, type, new_serial);
+ }
+ } else {
+ ret = keymgr_serial_print(&kctx, type);
+ print_ok_on_succes = false;
+ }
+ } else if (strcmp(argv[1], "set") == 0) {
+ CHECK_MISSING_ARG("Key is not specified");
+ knot_kasp_key_t *key2set;
+ ret = keymgr_get_key(&kctx, argv[2], &key2set);
+ if (ret == KNOT_EOK) {
+ ret = keymgr_set_timing(key2set, argc - 3, argv + 3);
+ if (ret == KNOT_EOK) {
+ ret = kdnssec_ctx_commit(&kctx);
+ }
+ }
+ } else if (strcmp(argv[1], "list") == 0) {
+ list_params->format = TIME_PRINT_UNIX;
+ if (argc > 2 && strcmp(argv[2], "human") == 0) {
+ list_params->format = TIME_PRINT_HUMAN_MIXED;
+ } else if (argc > 2 && strcmp(argv[2], "iso") == 0) {
+ list_params->format = TIME_PRINT_ISO8601;
+ }
+ ret = keymgr_list_keys(&kctx, list_params);
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "ds") == 0 || strcmp(argv[1], "dnskey") == 0) {
+ int (*generate_rr)(const knot_dname_t *, const knot_kasp_key_t *) = keymgr_generate_dnskey;
+ if (strcmp(argv[1], "ds") == 0) {
+ generate_rr = keymgr_generate_ds;
+ }
+ if (argc < 3) {
+ for (int i = 0; i < kctx.zone->num_keys && ret == KNOT_EOK; i++) {
+ if (kctx.zone->keys[i].is_ksk) {
+ ret = generate_rr(zone_name, &kctx.zone->keys[i]);
+ }
+ }
+ } else {
+ knot_kasp_key_t *key2rr;
+ ret = keymgr_get_key(&kctx, argv[2], &key2rr);
+ if (ret == KNOT_EOK) {
+ ret = generate_rr(zone_name, key2rr);
+ }
+ }
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "share") == 0) {
+ CHECK_MISSING_ARG("Key to be shared is not specified");
+ CHECK_MISSING_ARG2("Zone to be shared from not specified");
+ knot_dname_t *other_zone = NULL;
+ char *key_to_share = NULL;
+ ret = keymgr_foreign_key_id(argv, kaspdb, &other_zone, &key_to_share);
+ if (ret == KNOT_EOK) {
+ ret = kasp_db_share_key(kctx.kasp_db, other_zone, kctx.zone->dname, key_to_share);
+ }
+ free(other_zone);
+ free(key_to_share);
+ } else if (strcmp(argv[1], "delete") == 0) {
+ CHECK_MISSING_ARG("Key is not specified");
+ knot_kasp_key_t *key2del;
+ ret = keymgr_get_key(&kctx, argv[2], &key2del);
+ if (ret == KNOT_EOK) {
+ ret = kdnssec_delete_key(&kctx, key2del);
+ }
+ } else if (strcmp(argv[1], "pregenerate") == 0) {
+ CHECK_MISSING_ARG("Timestamp to not specified");
+ ret = keymgr_pregenerate_zsks(&kctx, argc > 3 ? argv[2] : NULL,
+ argc > 3 ? argv[3] : argv[2]);
+ } else if (strcmp(argv[1], "show-offline") == 0) {
+ ret = keymgr_print_offline_records(&kctx, argc > 2 ? argv[2] : NULL,
+ argc > 3 ? argv[3] : NULL);
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "del-offline") == 0) {
+ CHECK_MISSING_ARG2("Timestamps from-to not specified");
+ ret = keymgr_delete_offline_records(&kctx, argv[2], argv[3]);
+ } else if (strcmp(argv[1], "del-all-old") == 0) {
+ ret = keymgr_del_all_old(&kctx);
+ } else if (strcmp(argv[1], "generate-ksr") == 0) {
+ CHECK_MISSING_ARG("Timestamps to not specified");
+ ret = keymgr_print_ksr(&kctx, argc > 3 ? argv[2] : NULL,
+ argc > 3 ? argv[3] : argv[2]);
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "sign-ksr") == 0) {
+ CHECK_MISSING_ARG("Input file not specified");
+ ret = keymgr_sign_ksr(&kctx, argv[2]);
+ print_ok_on_succes = false;
+ } else if (strcmp(argv[1], "validate-skr") == 0) {
+ CHECK_MISSING_ARG("Input file not specified");
+ ret = keymgr_validate_skr(&kctx, argv[2]);
+ } else if (strcmp(argv[1], "import-skr") == 0) {
+ CHECK_MISSING_ARG("Input file not specified");
+ ret = keymgr_import_skr(&kctx, argv[2]);
+ } else {
+ ERR2("invalid command '%s'", argv[1]);
+ goto main_end;
+ }
+
+#undef CHECK_MISSING_ARG
+
+ if (ret == KNOT_EOK) {
+ printf("%s", print_ok_on_succes ? "OK\n" : "");
+ } else {
+ ERR2("%s", knot_strerror(ret));
+ }
+
+main_end:
+ kdnssec_ctx_deinit(&kctx);
+ free(zone_name);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "dir", required_argument, NULL, 'D' },
+ { "tsig", required_argument, NULL, 't' },
+ { "extended", no_argument, NULL, 'e' },
+ { "list", no_argument, NULL, 'l' },
+ { "brief", no_argument, NULL, 'b' }, // Legacy.
+ { "mono", no_argument, NULL, 'x' },
+ { "color", no_argument, NULL, 'X' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "json", no_argument, NULL, 'j' },
+ { NULL }
+ };
+
+ tzset();
+
+ int ret;
+ bool just_list = false;
+ keymgr_list_params_t list_params = { 0 };
+
+ list_params.color = isatty(STDOUT_FILENO);
+
+ int opt = 0, parm = 0;
+ while ((opt = getopt_long(argc, argv, "c:C:D:t:ejlbxXhV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ if (util_conf_init_file(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'C':
+ if (util_conf_init_confdb(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'D':
+ if (util_conf_init_justdb("kasp-db", optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 't':
+ if (argc > optind + 1) {
+ (void)str_to_int(argv[optind + 1], &parm, 0, 65536);
+ }
+ ret = keymgr_generate_tsig(optarg, (argc > optind ? argv[optind] : "hmac-sha256"), parm);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to generate TSIG (%s)", knot_strerror(ret));
+ goto failure;
+ }
+ goto success;
+ case 'e':
+ list_params.extended = true;
+ break;
+ case 'j':
+ list_params.json = true;
+ break;
+ case 'l':
+ just_list = true;
+ break;
+ case 'b':
+ WARN2("option '--brief' is deprecated and enabled by default");
+ break;
+ case 'x':
+ list_params.color = false;
+ break;
+ case 'X':
+ list_params.color = true;
+ break;
+ case 'h':
+ print_help();
+ goto success;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ goto success;
+ default:
+ print_help();
+ goto failure;
+ }
+ }
+
+ if (util_conf_init_default(true) != KNOT_EOK) {
+ goto failure;
+ }
+
+ util_update_privileges();
+
+ knot_lmdb_db_t kaspdb = { 0 };
+ conf_val_t mapsize = conf_db_param(conf(), C_KASP_DB_MAX_SIZE);
+ char *kasp_dir = conf_db(conf(), C_KASP_DB);
+ knot_lmdb_init(&kaspdb, kasp_dir, conf_int(&mapsize), 0, "keys_db");
+ free(kasp_dir);
+
+ if (just_list) {
+ ret = keymgr_list_zones(&kaspdb, list_params.json);
+ } else {
+ ret = key_command(argc, argv, optind, &kaspdb, &list_params);
+ }
+ knot_lmdb_deinit(&kaspdb);
+ if (ret != KNOT_EOK) {
+ goto failure;
+ }
+
+success:
+ util_conf_deinit();
+ return EXIT_SUCCESS;
+failure:
+ util_conf_deinit();
+ return EXIT_FAILURE;
+}
diff --git a/src/utils/keymgr/offline_ksk.c b/src/utils/keymgr/offline_ksk.c
new file mode 100644
index 0000000..1f1110e
--- /dev/null
+++ b/src/utils/keymgr/offline_ksk.c
@@ -0,0 +1,549 @@
+/* Copyright (C) 2022 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 <string.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "utils/keymgr/offline_ksk.h"
+#include "knot/dnssec/kasp/policy.h"
+#include "knot/dnssec/key-events.h"
+#include "knot/dnssec/key_records.h"
+#include "knot/dnssec/rrset-sign.h"
+#include "knot/dnssec/zone-events.h"
+#include "knot/dnssec/zone-keys.h"
+#include "knot/dnssec/zone-sign.h"
+#include "libzscanner/scanner.h"
+#include "utils/common/msg.h"
+#include "utils/keymgr/functions.h"
+
+#define KSR_SKR_VER "1.0"
+
+static int pregenerate_once(kdnssec_ctx_t *ctx, knot_time_t *next)
+{
+ zone_sign_reschedule_t resch = { 0 };
+
+ // generate ZSKs
+ int ret = knot_dnssec_key_rollover(ctx, KEY_ROLL_ALLOW_ZSK_ROLL, &resch);
+ if (ret != KNOT_EOK) {
+ ERR2("key rollover failed");
+ return ret;
+ }
+ // we don't need to do anything explicitly with the generated ZSKs
+ // they're simply stored in KASP db
+
+ *next = resch.next_rollover;
+ return KNOT_EOK;
+}
+
+// please free *_dnskey and keyset even if returned error
+static int load_dnskey_rrset(kdnssec_ctx_t *ctx, knot_rrset_t **_dnskey, zone_keyset_t *keyset)
+{
+ // prepare the DNSKEY rrset to be signed
+ knot_rrset_t *dnskey = knot_rrset_new(ctx->zone->dname, KNOT_RRTYPE_DNSKEY,
+ KNOT_CLASS_IN, ctx->policy->dnskey_ttl, NULL);
+ if (dnskey == NULL) {
+ return KNOT_ENOMEM;
+ }
+ *_dnskey = dnskey;
+
+ int ret = load_zone_keys(ctx, keyset, false);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to load keys");
+ return ret;
+ }
+
+ for (int i = 0; i < keyset->count; i++) {
+ zone_key_t *key = &keyset->keys[i];
+ if (key->is_public) {
+ ret = rrset_add_zone_key(dnskey, key);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to add zone key");
+ return ret;
+ }
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+int keymgr_pregenerate_zsks(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to)
+{
+ knot_time_t from = 0, to;
+ int ret = parse_timestamp(arg_to, &to);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (arg_from != NULL) {
+ ret = parse_timestamp(arg_from, &from);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ knot_time_t next = (from == 0 ? ctx->now : from);
+ ret = KNOT_EOK;
+
+ ctx->keep_deleted_keys = true;
+ ctx->policy->manual = false;
+
+ if (ctx->policy->dnskey_ttl == UINT32_MAX ||
+ ctx->policy->zone_maximal_ttl == UINT32_MAX) {
+ ERR2("dnskey-ttl or zone-max-ttl not configured");
+ return KNOT_ESEMCHECK;
+ }
+
+ while (ret == KNOT_EOK && knot_time_cmp(next, to) <= 0) {
+ ctx->now = next;
+ ret = pregenerate_once(ctx, &next);
+ }
+
+ return ret;
+}
+
+static int dump_rrset_to_buf(const knot_rrset_t *rrset, char **buf, size_t *buf_size)
+{
+ if (*buf == NULL) {
+ *buf = malloc(*buf_size);
+ if (*buf == NULL) {
+ return KNOT_ENOMEM;
+ }
+ }
+
+ knot_dump_style_t style = {
+ .wrap = true,
+ .show_ttl = true,
+ .verbose = true,
+ .original_ttl = true,
+ .human_timestamp = true
+ };
+ return knot_rrset_txt_dump(rrset, buf, buf_size, &style);
+}
+
+static void print_header(const char *of_what, knot_time_t timestamp, const char *contents)
+{
+ char date[64] = { 0 };
+ (void)knot_time_print(TIME_PRINT_ISO8601, timestamp, date, sizeof(date));
+ printf(";; %s %"PRIu64" (%s) =========\n%s", of_what,
+ timestamp, date, contents);
+}
+
+int keymgr_print_offline_records(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to)
+{
+ knot_time_t from = 0, to = 0;
+ if (arg_from != NULL) {
+ int ret = parse_timestamp(arg_from, &from);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ if (arg_to != NULL) {
+ int ret = parse_timestamp(arg_to, &to);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ bool empty = true;
+ char *buf = NULL;
+ size_t buf_size = 512;
+ while (true) {
+ if (arg_to != NULL && knot_time_cmp(from, to) > 0) {
+ break;
+ }
+ knot_time_t next;
+ key_records_t r = { { 0 } };
+ int ret = kasp_db_load_offline_records(ctx->kasp_db, ctx->zone->dname,
+ &from, &next, &r);
+ if (ret == KNOT_ENOENT) {
+ break;
+ } else if (ret != KNOT_EOK) {
+ free(buf);
+ return ret;
+ }
+
+ ret = key_records_dump(&buf, &buf_size, &r, true);
+ key_records_clear(&r);
+ if (ret != KNOT_EOK) {
+ free(buf);
+ return ret;
+ }
+ print_header("Offline records for", from, buf);
+ empty = false;
+
+ if (next == 0) {
+ break;
+ }
+ from = next;
+ }
+ free(buf);
+
+ /* If from is lower than the first record's timestamp, try to start
+ from the first one's instead of empty output. */
+ if (empty && from > 0) {
+ knot_time_t last = 0;
+ int ret = key_records_last_timestamp(ctx, &last);
+ if (ret == KNOT_EOK && knot_time_cmp(last, from) > 0) {
+ return keymgr_print_offline_records(ctx, 0, arg_to);
+ }
+ }
+ return KNOT_EOK;
+}
+
+int keymgr_delete_offline_records(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to)
+{
+ knot_time_t from, to;
+ int ret = parse_timestamp(arg_from, &from);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ ret = parse_timestamp(arg_to, &to);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ return kasp_db_delete_offline_records(ctx->kasp_db, ctx->zone->dname, from, to);
+}
+
+int keymgr_del_all_old(kdnssec_ctx_t *ctx)
+{
+ for (size_t i = 0; i < ctx->zone->num_keys; ) {
+ knot_kasp_key_t *key = &ctx->zone->keys[i];
+ if (knot_time_cmp(key->timing.remove, ctx->now) < 0) {
+ int ret = kdnssec_delete_key(ctx, key);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ } else {
+ i++;
+ }
+ }
+ return kdnssec_ctx_commit(ctx);
+}
+
+static void print_generated_message(void)
+{
+ char buf[64] = { 0 };
+ knot_time_print(TIME_PRINT_ISO8601, knot_time(), buf, sizeof(buf));
+ printf("generated at %s by Knot DNS %s\n", buf, VERSION);
+}
+
+static int ksr_once(kdnssec_ctx_t *ctx, char **buf, size_t *buf_size, knot_time_t *next_ksr)
+{
+ knot_rrset_t *dnskey = NULL;
+ zone_keyset_t keyset = { 0 };
+ int ret = load_dnskey_rrset(ctx, &dnskey, &keyset);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+ ret = dump_rrset_to_buf(dnskey, buf, buf_size);
+ if (ret >= 0) {
+ print_header("KeySigningRequest "KSR_SKR_VER, ctx->now, *buf);
+ ret = KNOT_EOK;
+ }
+
+done:
+ if (ret == KNOT_EOK && next_ksr != NULL) {
+ *next_ksr = knot_get_next_zone_key_event(&keyset);
+ }
+ knot_rrset_free(dnskey, NULL);
+ free_zone_keys(&keyset);
+ return ret;
+}
+
+#define OFFLINE_KSK_CONF_CHECK \
+ if (!ctx->policy->offline_ksk || !ctx->policy->manual) { \
+ ERR2("offline-ksk and manual must be enabled in configuration"); \
+ return KNOT_ESEMCHECK; \
+ }
+
+int keymgr_print_ksr(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to)
+{
+ OFFLINE_KSK_CONF_CHECK
+
+ knot_time_t from, to;
+ int ret = parse_timestamp(arg_to, &to);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ if (arg_from == NULL) {
+ ret = key_records_last_timestamp(ctx, &from);
+ } else {
+ ret = parse_timestamp(arg_from, &from);
+ }
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ char *buf = NULL;
+ size_t buf_size = 4096;
+ while (ret == KNOT_EOK && knot_time_cmp(from, to) < 0) {
+ ctx->now = from;
+ ret = ksr_once(ctx, &buf, &buf_size, &from);
+ }
+ if (ret != KNOT_EOK) {
+ free(buf);
+ return ret;
+ }
+ ctx->now = to;
+ // force end of period as a KSR timestamp
+ ret = ksr_once(ctx, &buf, &buf_size, NULL);
+
+ printf(";; KeySigningRequest %s ", KSR_SKR_VER);
+ print_generated_message();
+
+ free(buf);
+ return ret;
+}
+
+typedef struct {
+ int ret;
+ key_records_t r;
+ knot_time_t timestamp;
+ kdnssec_ctx_t *kctx;
+} ksr_sign_ctx_t;
+
+static int ksr_sign_dnskey(kdnssec_ctx_t *ctx, knot_rrset_t *zsk, knot_time_t now,
+ knot_time_t *next_sign)
+{
+ zone_keyset_t keyset = { 0 };
+ char *buf = NULL;
+ size_t buf_size = 4096;
+ knot_time_t rrsigs_expire = 0;
+
+ ctx->now = now;
+ ctx->policy->dnskey_ttl = zsk->ttl;
+
+ int ret = load_zone_keys(ctx, &keyset, false);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ key_records_t r;
+ key_records_init(ctx, &r);
+
+ ret = knot_zone_sign_add_dnskeys(&keyset, ctx, &r, NULL, NULL);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
+ ret = knot_rdataset_merge(&r.dnskey.rrs, &zsk->rrs, NULL);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+
+ // no check if the KSK used for signing (in keyset) is contained in DNSKEY record being signed (in KSR) !
+ for (int i = 0; i < keyset.count; i++) {
+ ret = key_records_sign(&keyset.keys[i], &r, ctx, &rrsigs_expire);
+ if (ret != KNOT_EOK) {
+ goto done;
+ }
+ }
+ ret = key_records_dump(&buf, &buf_size, &r, true);
+ if (ret == KNOT_EOK) {
+ print_header("SignedKeyResponse "KSR_SKR_VER, ctx->now, buf);
+ *next_sign = knot_time_min(
+ knot_get_next_zone_key_event(&keyset),
+ knot_time_add(rrsigs_expire, -(knot_timediff_t)ctx->policy->rrsig_refresh_before)
+ );
+ }
+
+done:
+ free(buf);
+ key_records_clear(&r);
+ free_zone_keys(&keyset);
+ return ret;
+}
+
+static int process_skr_between_ksrs(ksr_sign_ctx_t *ctx, knot_time_t from, knot_time_t to)
+{
+ for (knot_time_t t = from; t < to /* if (t == infinity) stop */; ) {
+ int ret = ksr_sign_dnskey(ctx->kctx, &ctx->r.dnskey, t, &t);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+ return KNOT_EOK;
+}
+
+static void ksr_sign_header(zs_scanner_t *sc)
+{
+ ksr_sign_ctx_t *ctx = sc->process.data;
+
+ // parse header
+ float header_ver;
+ knot_time_t next_timestamp = 0;
+ if (sc->error.code != 0 || ctx->ret != KNOT_EOK ||
+ sscanf((const char *)sc->buffer, "; KeySigningRequest %f %"PRIu64,
+ &header_ver, &next_timestamp) < 1) {
+ return;
+ }
+ (void)header_ver;
+
+ // sign previous KSR and inbetween KSK changes
+ if (ctx->timestamp > 0) {
+ knot_time_t inbetween_from;
+ ctx->ret = ksr_sign_dnskey(ctx->kctx, &ctx->r.dnskey, ctx->timestamp,
+ &inbetween_from);
+ if (next_timestamp > 0 && ctx->ret == KNOT_EOK) {
+ ctx->ret = process_skr_between_ksrs(ctx, inbetween_from,
+ next_timestamp);
+ }
+ key_records_clear_rdatasets(&ctx->r);
+ }
+
+ // start new KSR
+ ctx->timestamp = next_timestamp;
+}
+
+static void ksr_sign_once(zs_scanner_t *sc)
+{
+ ksr_sign_ctx_t *ctx = sc->process.data;
+ if (sc->error.code == 0 && ctx->ret == KNOT_EOK) {
+ ctx->ret = knot_rrset_add_rdata(&ctx->r.dnskey, sc->r_data, sc->r_data_length, NULL);
+ ctx->r.dnskey.ttl = sc->r_ttl;
+ }
+}
+
+static void skr_import_header(zs_scanner_t *sc)
+{
+ ksr_sign_ctx_t *ctx = sc->process.data;
+
+ // parse header
+ float header_ver;
+ knot_time_t next_timestamp;
+ if (sc->error.code != 0 || ctx->ret != KNOT_EOK ||
+ sscanf((const char *)sc->buffer, "; SignedKeyResponse %f %"PRIu64,
+ &header_ver, &next_timestamp) < 1) {
+ return;
+ }
+ (void)header_ver;
+
+ // delete possibly existing conflicting offline records
+ ctx->ret = kasp_db_delete_offline_records(
+ ctx->kctx->kasp_db, ctx->kctx->zone->dname, next_timestamp, 0
+ );
+
+ // store previous SKR
+ if (ctx->timestamp > 0 && ctx->ret == KNOT_EOK) {
+ ctx->ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp);
+ if (ctx->ret != KNOT_EOK) {
+ return;
+ }
+
+ ctx->ret = kasp_db_store_offline_records(ctx->kctx->kasp_db,
+ ctx->timestamp, &ctx->r);
+ key_records_clear_rdatasets(&ctx->r);
+ }
+
+ // start new SKR
+ ctx->timestamp = next_timestamp;
+}
+
+static void skr_validate_header(zs_scanner_t *sc)
+{
+ ksr_sign_ctx_t *ctx = sc->process.data;
+
+ float header_ver;
+ knot_time_t next_timestamp;
+ if (sc->error.code != 0 || ctx->ret != KNOT_EOK ||
+ sscanf((const char *)sc->buffer, "; SignedKeyResponse %f %"PRIu64,
+ &header_ver, &next_timestamp) < 1) {
+ return;
+ }
+ (void)header_ver;
+
+ if (ctx->timestamp > 0 && ctx->ret == KNOT_EOK) {
+ int ret = key_records_verify(&ctx->r, ctx->kctx, ctx->timestamp);
+ if (ret != KNOT_EOK) { // ctx->ret untouched
+ ERR2("invalid SignedKeyResponse for %"KNOT_TIME_PRINTF" (%s)",
+ ctx->timestamp, knot_strerror(ret));
+ }
+ key_records_clear_rdatasets(&ctx->r);
+ }
+
+ ctx->timestamp = next_timestamp;
+}
+
+static void skr_import_once(zs_scanner_t *sc)
+{
+ ksr_sign_ctx_t *ctx = sc->process.data;
+ if (sc->error.code == 0 && ctx->ret == KNOT_EOK) {
+ ctx->ret = key_records_add_rdata(&ctx->r, sc->r_type, sc->r_data,
+ sc->r_data_length, sc->r_ttl);
+ }
+}
+
+static int read_ksr_skr(kdnssec_ctx_t *ctx, const char *infile,
+ void (*cb_header)(zs_scanner_t *), void (*cb_record)(zs_scanner_t *))
+{
+ zs_scanner_t sc = { 0 };
+ int ret = zs_init(&sc, "", KNOT_CLASS_IN, 0);
+ if (ret < 0) {
+ return KNOT_ERROR;
+ }
+
+ ret = zs_set_input_file(&sc, infile);
+ if (ret < 0) {
+ zs_deinit(&sc);
+ return (sc.error.code == ZS_FILE_ACCESS) ? KNOT_EACCES : KNOT_EFILE;
+ }
+
+ ksr_sign_ctx_t pctx = { 0 };
+ key_records_init(ctx, &pctx.r);
+ pctx.kctx = ctx;
+ ret = zs_set_processing(&sc, cb_record, NULL, &pctx);
+ if (ret < 0) {
+ zs_deinit(&sc);
+ return KNOT_EBUSY;
+ }
+ sc.process.comment = cb_header;
+
+ ret = zs_parse_all(&sc);
+
+ if (sc.error.code != 0) {
+ ret = KNOT_EMALF;
+ } else if (pctx.ret != KNOT_EOK) {
+ ret = pctx.ret;
+ } else if (ret < 0 || pctx.r.dnskey.rrs.count > 0 || pctx.r.cdnskey.rrs.count > 0 ||
+ pctx.r.cds.rrs.count > 0 || pctx.r.rrsig.rrs.count > 0) {
+ ret = KNOT_EMALF;
+ }
+ key_records_clear(&pctx.r);
+ zs_deinit(&sc);
+ return ret;
+}
+
+int keymgr_sign_ksr(kdnssec_ctx_t *ctx, const char *ksr_file)
+{
+ OFFLINE_KSK_CONF_CHECK
+
+ int ret = read_ksr_skr(ctx, ksr_file, ksr_sign_header, ksr_sign_once);
+ printf(";; SignedKeyResponse %s ", KSR_SKR_VER);
+ print_generated_message();
+ return ret;
+}
+
+int keymgr_import_skr(kdnssec_ctx_t *ctx, const char *skr_file)
+{
+ OFFLINE_KSK_CONF_CHECK
+
+ return read_ksr_skr(ctx, skr_file, skr_import_header, skr_import_once);
+}
+
+int keymgr_validate_skr(kdnssec_ctx_t *ctx, const char *skr_file)
+{
+ return read_ksr_skr(ctx, skr_file, skr_validate_header, skr_import_once);
+}
diff --git a/src/utils/keymgr/offline_ksk.h b/src/utils/keymgr/offline_ksk.h
new file mode 100644
index 0000000..bf0e085
--- /dev/null
+++ b/src/utils/keymgr/offline_ksk.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "knot/dnssec/context.h"
+
+int keymgr_pregenerate_zsks(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to);
+
+int keymgr_print_offline_records(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to);
+
+int keymgr_delete_offline_records(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to);
+
+int keymgr_del_all_old(kdnssec_ctx_t *ctx);
+
+int keymgr_print_ksr(kdnssec_ctx_t *ctx, char *arg_from, char *arg_to);
+
+int keymgr_sign_ksr(kdnssec_ctx_t *ctx, const char *ksr_file);
+
+int keymgr_import_skr(kdnssec_ctx_t *ctx, const char *skr_file);
+
+int keymgr_validate_skr(kdnssec_ctx_t *ctx, const char *skr_file);
diff --git a/src/utils/khost/khost_main.c b/src/utils/khost/khost_main.c
new file mode 100644
index 0000000..75b0db7
--- /dev/null
+++ b/src/utils/khost/khost_main.c
@@ -0,0 +1,45 @@
+/* Copyright (C) 2018 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 <stdlib.h>
+
+#include "libdnssec/crypto.h"
+#include "utils/khost/khost_params.h"
+#include "utils/kdig/kdig_exec.h"
+#include "libknot/libknot.h"
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_SUCCESS;
+
+ tzset();
+
+ kdig_params_t params;
+ if (khost_parse(&params, argc, argv) == KNOT_EOK) {
+ if (!params.stop) {
+ dnssec_crypto_init();
+ if (kdig_exec(&params) != KNOT_EOK) {
+ ret = EXIT_FAILURE;
+ }
+ dnssec_crypto_cleanup();
+ }
+ } else {
+ ret = EXIT_FAILURE;
+ }
+
+ khost_clean(&params);
+ return ret;
+}
diff --git a/src/utils/khost/khost_params.c b/src/utils/khost/khost_params.c
new file mode 100644
index 0000000..1423e09
--- /dev/null
+++ b/src/utils/khost/khost_params.c
@@ -0,0 +1,365 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/khost/khost_params.h"
+#include "utils/kdig/kdig_params.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/common/resolv.h"
+#include "libknot/libknot.h"
+#include "contrib/strtonum.h"
+#include "contrib/ucw/lists.h"
+
+#define PROGRAM_NAME "khost"
+
+#define DEFAULT_RETRIES_HOST 1
+#define DEFAULT_TIMEOUT_HOST 2
+
+static const style_t DEFAULT_STYLE_HOST = {
+ .format = FORMAT_HOST,
+ .style = {
+ .wrap = false,
+ .show_class = true,
+ .show_ttl = true,
+ .verbose = false,
+ .original_ttl = false,
+ .empty_ttl = false,
+ .human_ttl = false,
+ .human_timestamp = true,
+ .generic = false,
+ .ascii_to_idn = name_to_idn
+ },
+ .show_query = false,
+ .show_header = false,
+ .show_edns = false,
+ .show_question = true,
+ .show_answer = true,
+ .show_authority = true,
+ .show_additional = true,
+ .show_tsig = false,
+ .show_footer = false
+};
+
+static int khost_init(kdig_params_t *params)
+{
+ // Initialize params with kdig defaults.
+ int ret = kdig_init(params);
+
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Set khost specific defaults.
+ free(params->config->port);
+ params->config->port = strdup(DEFAULT_DNS_PORT);
+ params->config->retries = DEFAULT_RETRIES_HOST;
+ params->config->wait = DEFAULT_TIMEOUT_HOST;
+ params->config->class_num = KNOT_CLASS_IN;
+ params->config->style = DEFAULT_STYLE_HOST;
+ params->config->idn = true;
+
+ // Check port.
+ if (params->config->port == NULL) {
+ query_free(params->config);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+void khost_clean(kdig_params_t *params)
+{
+ if (params == NULL) {
+ DBG_NULL;
+ return;
+ }
+
+ kdig_clean(params);
+}
+
+static int parse_server(const char *value, list_t *servers, const char *def_port)
+{
+ if (params_parse_server(value, servers, def_port) != KNOT_EOK) {
+ ERR("invalid server %s", value);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_name(const char *value, list_t *queries, const query_t *conf)
+{
+ char *reverse = get_reverse_name(value);
+ char *ascii_name = (char *)value;
+ query_t *query;
+
+ if (conf->idn) {
+ ascii_name = name_from_idn(value);
+ if (ascii_name == NULL) {
+ free(reverse);
+ return KNOT_EINVAL;
+ }
+ }
+
+ // If name is not FQDN, append trailing dot.
+ char *fqd_name = get_fqd_name(ascii_name);
+
+ if (conf->idn) {
+ free(ascii_name);
+ }
+
+ // RR type is known.
+ if (conf->type_num >= 0) {
+ if (conf->type_num == KNOT_RRTYPE_PTR) {
+ free(fqd_name);
+
+ // Check for correct address.
+ if (reverse == NULL) {
+ ERR("invalid IPv4/IPv6 address %s", value);
+ return KNOT_EINVAL;
+ }
+
+ // Add reverse query for address.
+ query = query_create(reverse, conf);
+ free(reverse);
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+ add_tail(queries, (node_t *)query);
+ } else {
+ free(reverse);
+
+ // Add query for name and specified type.
+ query = query_create(fqd_name, conf);
+ free(fqd_name);
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+ add_tail(queries, (node_t *)query);
+ }
+ // RR type is unknown, use defaults.
+ } else {
+ if (reverse == NULL) {
+ // Add query for name and type A.
+ query = query_create(fqd_name, conf);
+ if (query == NULL) {
+ free(fqd_name);
+ return KNOT_ENOMEM;
+ }
+ query->type_num = KNOT_RRTYPE_A;
+ add_tail(queries, (node_t *)query);
+
+ // Add query for name and type AAAA.
+ query = query_create(fqd_name, conf);
+ if (query == NULL) {
+ free(fqd_name);
+ return KNOT_ENOMEM;
+ }
+ query->type_num = KNOT_RRTYPE_AAAA;
+ query->style.hide_cname = true;
+ add_tail(queries, (node_t *)query);
+
+ // Add query for name and type MX.
+ query = query_create(fqd_name, conf);
+ if (query == NULL) {
+ free(fqd_name);
+ return KNOT_ENOMEM;
+ }
+ free(fqd_name);
+ query->type_num = KNOT_RRTYPE_MX;
+ query->style.hide_cname = true;
+ add_tail(queries, (node_t *)query);
+ } else {
+ free(fqd_name);
+
+ // Add reverse query for address.
+ query = query_create(reverse, conf);
+ free(reverse);
+ if (query == NULL) {
+ return KNOT_ENOMEM;
+ }
+ query->type_num = KNOT_RRTYPE_PTR;
+ add_tail(queries, (node_t *)query);
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static void print_help(void)
+{
+ printf("Usage: %s [-4] [-6] [-adhrsTvVw] [-c class] [-t type]\n"
+ " [-R retries] [-W time] name [server]\n\n"
+ " -4 Use IPv4 protocol only.\n"
+ " -6 Use IPv6 protocol only.\n"
+ " -a Same as -t ANY -v.\n"
+ " -d Allow debug messages.\n"
+ " -h, --help Print the program help.\n"
+ " -r Disable recursion.\n"
+ " -T Use TCP protocol.\n"
+ " -v Verbose output.\n"
+ " -V, --version Print the program version.\n"
+ " -w Wait forever.\n"
+ " -c Set query class.\n"
+ " -t Set query type.\n"
+ " -R Set number of UDP retries.\n"
+ " -W Set wait interval.\n",
+ PROGRAM_NAME);
+}
+
+int khost_parse(kdig_params_t *params, int argc, char *argv[])
+{
+ if (params == NULL || argv == NULL) {
+ DBG_NULL;
+ return KNOT_EINVAL;
+ }
+
+ if (khost_init(params) != KNOT_EOK) {
+ return KNOT_ERROR;
+ }
+
+#ifdef LIBIDN
+ // Set up localization.
+ if (setlocale(LC_CTYPE, "") == NULL) {
+ params->config->idn = false;
+ }
+#endif
+
+ query_t *conf = params->config;
+ uint16_t rclass, rtype;
+ int64_t serial;
+ bool notify;
+
+ // Long options.
+ struct option opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ // Command line options processing.
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "46adhrsTvVwc:t:R:W:", opts, NULL))
+ != -1) {
+ switch (opt) {
+ case '4':
+ conf->ip = IP_4;
+ break;
+ case '6':
+ conf->ip = IP_6;
+ break;
+ case 'a':
+ conf->type_num = KNOT_RRTYPE_ANY;
+ conf->style.format = FORMAT_FULL;
+ conf->style.show_header = true;
+ conf->style.show_edns = true;
+ conf->style.show_footer = true;
+ break;
+ case 'd':
+ msg_enable_debug(1);
+ break;
+ case 'h':
+ print_help();
+ params->stop = false;
+ return KNOT_EOK;
+ case 'r':
+ conf->flags.rd_flag = false;
+ break;
+ case 'T':
+ conf->protocol = PROTO_TCP;
+ break;
+ case 'v':
+ conf->style.format = FORMAT_FULL;
+ conf->style.show_header = true;
+ conf->style.show_edns = true;
+ conf->style.show_footer = true;
+ break;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ params->stop = false;
+ return KNOT_EOK;
+ case 'w':
+ conf->wait = -1;
+ break;
+ case 'c':
+ if (params_parse_class(optarg, &rclass) != KNOT_EOK) {
+ ERR("invalid class '%s'", optarg);
+ return KNOT_EINVAL;
+ }
+ conf->class_num = rclass;
+ break;
+ case 't':
+ if (params_parse_type(optarg, &rtype, &serial, &notify)
+ != KNOT_EOK) {
+ ERR("invalid type '%s'", optarg);
+ return KNOT_EINVAL;
+ }
+ conf->type_num = rtype;
+ conf->serial = serial;
+ conf->notify = notify;
+
+ // If NOTIFY, reset default RD flag.
+ if (conf->notify) {
+ conf->flags.rd_flag = false;
+ }
+ break;
+ case 'R':
+ if (str_to_u32(optarg, &conf->retries) != KNOT_EOK) {
+ ERR("invalid retries '%s'", optarg);
+ return KNOT_EINVAL;
+ }
+ break;
+ case 'W':
+ if (params_parse_wait(optarg, &conf->wait) != KNOT_EOK) {
+ ERR("invalid wait '%s'", optarg);
+ return KNOT_EINVAL;
+ }
+ break;
+ default:
+ print_help();
+ return KNOT_ENOTSUP;
+ }
+ }
+
+ // Process non-option parameters.
+ switch (argc - optind) {
+ case 2:
+ if (parse_server(argv[optind + 1], &conf->servers, conf->port)
+ != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+ case 1: // Fall through.
+ if (parse_name(argv[optind], &params->queries, conf)
+ != KNOT_EOK) {
+ return KNOT_EINVAL;
+ }
+ break;
+ default:
+ print_help();
+ return KNOT_ENOTSUP;
+ }
+
+ // Complete missing data in queries based on defaults.
+ complete_queries(&params->queries, params->config);
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/khost/khost_params.h b/src/utils/khost/khost_params.h
new file mode 100644
index 0000000..6f019fe
--- /dev/null
+++ b/src/utils/khost/khost_params.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "utils/kdig/kdig_params.h"
+
+int khost_parse(kdig_params_t *params, int argc, char *argv[]);
+void khost_clean(kdig_params_t *params);
diff --git a/src/utils/kjournalprint/main.c b/src/utils/kjournalprint/main.c
new file mode 100644
index 0000000..ed3227c
--- /dev/null
+++ b/src/utils/kjournalprint/main.c
@@ -0,0 +1,466 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libknot/libknot.h"
+#include "knot/journal/journal_basic.h"
+#include "knot/journal/journal_metadata.h"
+#include "knot/journal/journal_read.h"
+#include "knot/journal/serialization.h"
+#include "knot/zone/zone-dump.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/common/util_conf.h"
+#include "contrib/color.h"
+#include "contrib/strtonum.h"
+#include "contrib/string.h"
+
+#define PROGRAM_NAME "kjournalprint"
+
+static void print_help(void)
+{
+ printf("Usage:\n"
+ " %s [-c | -C | -D <path>] [parameters] <zone_name>\n"
+ " %s [-c | -C | -D <path>] -z\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> Path to a textual configuration file.\n"
+ " (default %s)\n"
+ " -C, --confdb <dir> Path to a configuration database directory.\n"
+ " (default %s)\n"
+ " -D, --dir <path> Path to a journal database directory, use default configuration.\n"
+ " -z, --zone-list Instead of reading the journal, display the list\n"
+ " of zones in the DB.\n"
+ " -l, --limit <num> Read only <num> newest changes.\n"
+ " -s, --serial <soa> Start with a specific SOA serial.\n"
+ " -H, --check Additional journal semantic checks.\n"
+ " -d, --debug Debug mode output.\n"
+ " -x, --mono Get output without coloring.\n"
+ " -n, --no-color An alias for -x, deprecated.\n"
+ " -X, --color Force output coloring.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n",
+ PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
+}
+
+typedef struct {
+ bool debug;
+ bool color;
+ bool check;
+ int limit;
+ int counter;
+ uint32_t serial;
+ bool from_serial;
+ size_t changes;
+} print_params_t;
+
+static void print_changeset(const changeset_t *chs, print_params_t *params)
+{
+ static size_t count = 1;
+ if (chs->soa_from == NULL) {
+ printf("%s;; Zone-in-journal, serial: %u, changeset: %zu%s\n",
+ COL_YELW(params->color),
+ knot_soa_serial(chs->soa_to->rrs.rdata),
+ count++,
+ COL_RST(params->color));
+ } else {
+ printf("%s;; Changes between zone versions: %u -> %u, changeset: %zu%s\n",
+ COL_YELW(params->color),
+ knot_soa_serial(chs->soa_from->rrs.rdata),
+ knot_soa_serial(chs->soa_to->rrs.rdata),
+ count++,
+ COL_RST(params->color));
+ }
+ changeset_print(chs, stdout, params->color);
+}
+
+knot_dynarray_declare(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC, 100)
+knot_dynarray_define(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC)
+
+typedef struct {
+ rrtype_dynarray_t *arr;
+ size_t *counter;
+} rrtypelist_ctx_t;
+
+static void rrtypelist_add(rrtype_dynarray_t *arr, uint16_t add_type)
+{
+ bool already_present = false;
+ knot_dynarray_foreach(rrtype, uint16_t, i, *arr) {
+ if (*i == add_type) {
+ already_present = true;
+ break;
+ }
+ }
+ if (!already_present) {
+ rrtype_dynarray_add(arr, &add_type);
+ }
+}
+
+static int rrtypelist_callback(zone_node_t *node, void *data)
+{
+ rrtypelist_ctx_t *ctx = data;
+ for (int i = 0; i < node->rrset_count; i++) {
+ knot_rrset_t rrset = node_rrset_at(node, i);
+ rrtypelist_add(ctx->arr, rrset.type);
+ *ctx->counter += rrset.rrs.count;
+ }
+ return KNOT_EOK;
+}
+
+static void print_changeset_debugmode(const changeset_t *chs)
+{
+ // detect all types
+ rrtype_dynarray_t types = { 0 };
+ size_t count_minus = 1, count_plus = 1; // 1 for SOA which is always present but not iterated
+ rrtypelist_ctx_t ctx_minus = { &types, &count_minus }, ctx_plus = { &types, &count_plus };
+ (void)zone_contents_apply(chs->remove, rrtypelist_callback, &ctx_minus);
+ (void)zone_contents_nsec3_apply(chs->remove, rrtypelist_callback, &ctx_minus);
+ (void)zone_contents_apply(chs->add, rrtypelist_callback, &ctx_plus);
+ (void)zone_contents_nsec3_apply(chs->add, rrtypelist_callback, &ctx_plus);
+
+ if (chs->soa_from == NULL) {
+ printf("Zone-in-journal %u +++: %zu\t size: %zu\t", knot_soa_serial(chs->soa_to->rrs.rdata),
+ count_plus, changeset_serialized_size(chs));
+ } else {
+ printf("%u -> %u ---: %zu\t +++: %zu\t size: %zu\t", knot_soa_serial(chs->soa_from->rrs.rdata),
+ knot_soa_serial(chs->soa_to->rrs.rdata), count_minus, count_plus, changeset_serialized_size(chs));
+ }
+
+ char temp[100];
+ knot_dynarray_foreach(rrtype, uint16_t, i, types) {
+ (void)knot_rrtype_to_string(*i, temp, sizeof(temp));
+ printf(" %s", temp);
+ }
+ printf("\n");
+}
+
+static int count_changeset_cb(_unused_ bool special, const changeset_t *ch, void *ctx)
+{
+ print_params_t *params = ctx;
+ if (ch != NULL) {
+ params->counter++;
+ }
+ return KNOT_EOK;
+}
+
+static int print_changeset_cb(bool special, const changeset_t *ch, void *ctx)
+{
+ print_params_t *params = ctx;
+ if (ch != NULL && params->counter++ >= params->limit) {
+ if (params->debug) {
+ print_changeset_debugmode(ch);
+ params->changes++;
+ } else {
+ print_changeset(ch, params);
+ }
+ if (special && params->debug) {
+ printf("---------------------------------------------\n");
+ }
+ }
+ return KNOT_EOK;
+}
+
+int print_journal(char *path, knot_dname_t *name, print_params_t *params)
+{
+ knot_lmdb_db_t jdb = { 0 };
+ zone_journal_t j = { &jdb, name };
+ bool exists;
+ uint64_t occupied, occupied_all;
+
+ knot_lmdb_init(&jdb, path, 0, journal_env_flags(JOURNAL_MODE_ROBUST, true), NULL);
+ int ret = knot_lmdb_exists(&jdb);
+ if (ret == KNOT_EOK) {
+ ret = knot_lmdb_open(&jdb);
+ }
+ if (ret != KNOT_EOK) {
+ knot_lmdb_deinit(&jdb);
+ return ret;
+ }
+
+ ret = journal_info(j, &exists, NULL, NULL, NULL, NULL, NULL, &occupied, &occupied_all);
+ if (ret != KNOT_EOK || !exists) {
+ ERR2("zone not exists in the journal DB %s", path);
+ knot_lmdb_deinit(&jdb);
+ return ret == KNOT_EOK ? KNOT_ENOENT : ret;
+ }
+
+ if (params->check) {
+ ret = journal_sem_check(j);
+ if (ret > 0) {
+ ERR2("semantic check failed with code %d", ret);
+ } else if (ret != KNOT_EOK) {
+ ERR2("semantic check failed (%s)", knot_strerror(ret));
+ }
+ }
+
+ if (params->limit >= 0 && ret == KNOT_EOK) {
+ if (params->from_serial) {
+ ret = journal_walk_from(j, params->serial, count_changeset_cb, params);
+ } else {
+ ret = journal_walk(j, count_changeset_cb, params);
+ }
+ }
+ if (ret == KNOT_EOK) {
+ if (params->limit < 0 || params->counter <= params->limit) {
+ params->limit = 0;
+ } else {
+ params->limit = params->counter - params->limit;
+ }
+ params->counter = 0;
+ if (params->from_serial) {
+ ret = journal_walk_from(j, params->serial, print_changeset_cb, params);
+ } else {
+ ret = journal_walk(j, print_changeset_cb, params);
+ }
+ }
+
+ if (params->debug && ret == KNOT_EOK) {
+ printf("Total number of changesets: %zu\n", params->changes);
+ printf("Occupied this zone (approx): %"PRIu64" KiB\n", occupied / 1024);
+ printf("Occupied all zones together: %"PRIu64" KiB\n", occupied_all / 1024);
+ }
+
+ knot_lmdb_deinit(&jdb);
+ return ret;
+}
+
+static int add_zone_to_list(const knot_dname_t *zone, void *list)
+{
+ knot_dname_t *copy = knot_dname_copy(zone, NULL);
+ if (copy == NULL) {
+ return KNOT_ENOMEM;
+ }
+ return ptrlist_add(list, copy, NULL) == NULL ? KNOT_ENOMEM : KNOT_EOK;
+}
+
+static int list_zone(const knot_dname_t *zone, bool detailed, knot_lmdb_db_t *jdb, uint64_t *occupied_all)
+{
+ knot_dname_txt_storage_t zone_str;
+ if (knot_dname_to_str(zone_str, zone, sizeof(zone_str)) == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ if (detailed) {
+ zone_journal_t j = { jdb, zone };
+ bool exists;
+ uint64_t occupied;
+
+ int ret = journal_info(j, &exists, NULL, NULL, NULL, NULL, NULL, &occupied, occupied_all);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ assert(exists);
+ printf("%s \t%"PRIu64" KiB\n", zone_str, occupied / 1024);
+ } else {
+ printf("%s\n", zone_str);
+ }
+ return KNOT_EOK;
+}
+
+int list_zones(char *path, bool detailed)
+{
+ knot_lmdb_db_t jdb = { 0 };
+ knot_lmdb_init(&jdb, path, 0, journal_env_flags(JOURNAL_MODE_ROBUST, true), NULL);
+
+ list_t zones;
+ init_list(&zones);
+ ptrnode_t *zone;
+ uint64_t occupied_all = 0;
+
+ int ret = journals_walk(&jdb, add_zone_to_list, &zones);
+ WALK_LIST(zone, zones) {
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ ret = list_zone(zone->d, detailed, &jdb, &occupied_all);
+ }
+
+ knot_lmdb_deinit(&jdb);
+ ptrlist_deep_free(&zones, NULL);
+
+ if (detailed && ret == KNOT_EOK) {
+ printf("Occupied all zones together: %"PRIu64" KiB\n", occupied_all / 1024);
+ }
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ bool justlist = false;
+
+ print_params_t params = {
+ .debug = false,
+ .color = isatty(STDOUT_FILENO),
+ .check = false,
+ .limit = -1,
+ .from_serial = false,
+ };
+
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "dir", required_argument, NULL, 'D' },
+ { "limit", required_argument, NULL, 'l' },
+ { "serial", required_argument, NULL, 's' },
+ { "zone-list", no_argument, NULL, 'z' },
+ { "check", no_argument, NULL, 'H' },
+ { "debug", no_argument, NULL, 'd' },
+ { "no-color", no_argument, NULL, 'n' },
+ { "mono", no_argument, NULL, 'x' },
+ { "color", no_argument, NULL, 'X' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "c:C:D:l:s:zHdnxXhV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ if (util_conf_init_file(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'C':
+ if (util_conf_init_confdb(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'D':
+ if (util_conf_init_justdb("journal-db", optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'l':
+ if (str_to_int(optarg, &params.limit, 0, INT_MAX) != KNOT_EOK) {
+ print_help();
+ goto failure;
+ }
+ break;
+ case 's':
+ if (str_to_u32(optarg, &params.serial) != KNOT_EOK) {
+ print_help();
+ goto failure;
+ }
+ params.from_serial = true;
+ break;
+ case 'z':
+ justlist = true;
+ break;
+ case 'H':
+ params.check = true;
+ break;
+ case 'd':
+ params.debug = true;
+ break;
+ case 'n':
+ case 'x':
+ params.color = false;
+ break;
+ case 'X':
+ params.color = true;
+ break;
+ case 'h':
+ print_help();
+ goto success;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ goto success;
+ default:
+ print_help();
+ goto failure;
+ }
+ }
+
+ // Backward compatibility.
+ if ((justlist && (argc - optind > 0)) || (!justlist && (argc - optind > 1))) {
+ WARN2("obsolete parameter specified");
+ if (util_conf_init_justdb("journal-db", argv[optind]) != KNOT_EOK) {
+ goto failure;
+ }
+ optind++;
+ }
+
+ if (util_conf_init_default(true) != KNOT_EOK) {
+ goto failure;
+ }
+
+ char *db = conf_db(conf(), C_JOURNAL_DB);
+
+ if (justlist) {
+ int ret = list_zones(db, params.debug);
+ free(db);
+ switch (ret) {
+ case KNOT_ENOENT:
+ INFO2("No zones in journal DB");
+ // FALLTHROUGH
+ case KNOT_EOK:
+ goto success;
+ case KNOT_ENODB:
+ ERR2("the journal DB does not exist");
+ goto failure;
+ case KNOT_EMALF:
+ ERR2("the journal DB is broken");
+ goto failure;
+ default:
+ ERR2("failed to load zone list (%s)", knot_strerror(ret));
+ goto failure;
+ }
+ } else {
+ if (argc - optind != 1) {
+ print_help();
+ free(db);
+ goto failure;
+ }
+ knot_dname_t *name = knot_dname_from_str_alloc(argv[optind]);
+ knot_dname_to_lower(name);
+
+ int ret = print_journal(db, name, &params);
+ free(name);
+ free(db);
+ switch (ret) {
+ case KNOT_ENOENT:
+ if (params.from_serial) {
+ INFO2("The journal is empty or the serial not present");
+ } else {
+ INFO2("The journal is empty");
+ }
+ break;
+ case KNOT_ENODB:
+ ERR2("the journal DB does not exist");
+ goto failure;
+ case KNOT_EOUTOFZONE:
+ ERR2("the journal DB does not contain the specified zone");
+ goto failure;
+ case KNOT_EOK:
+ break;
+ default:
+ ERR2("failed to load changesets (%s)", knot_strerror(ret));
+ goto failure;
+ }
+ }
+
+success:
+ util_conf_deinit();
+ return EXIT_SUCCESS;
+failure:
+ util_conf_deinit();
+ return EXIT_FAILURE;
+}
diff --git a/src/utils/knotc/commands.c b/src/utils/knotc/commands.c
new file mode 100644
index 0000000..abfb12b
--- /dev/null
+++ b/src/utils/knotc/commands.c
@@ -0,0 +1,1340 @@
+/* Copyright (C) 2023 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/libknot.h"
+#include "knot/common/log.h"
+#include "knot/ctl/commands.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/confdb.h"
+#include "knot/conf/tools.h"
+#include "knot/zone/zonefile.h"
+#include "knot/zone/zone-load.h"
+#include "contrib/color.h"
+#include "contrib/macros.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "contrib/openbsd/strlcat.h"
+#include "utils/knotc/commands.h"
+
+#define CMD_EXIT "exit"
+
+#define CMD_STATUS "status"
+#define CMD_STOP "stop"
+#define CMD_RELOAD "reload"
+#define CMD_STATS "stats"
+
+#define CMD_ZONE_CHECK "zone-check"
+#define CMD_ZONE_STATUS "zone-status"
+#define CMD_ZONE_RELOAD "zone-reload"
+#define CMD_ZONE_REFRESH "zone-refresh"
+#define CMD_ZONE_RETRANSFER "zone-retransfer"
+#define CMD_ZONE_NOTIFY "zone-notify"
+#define CMD_ZONE_FLUSH "zone-flush"
+#define CMD_ZONE_BACKUP "zone-backup"
+#define CMD_ZONE_RESTORE "zone-restore"
+#define CMD_ZONE_SIGN "zone-sign"
+#define CMD_ZONE_KEYS_LOAD "zone-keys-load"
+#define CMD_ZONE_KEY_ROLL "zone-key-rollover"
+#define CMD_ZONE_KSK_SBM "zone-ksk-submitted"
+#define CMD_ZONE_FREEZE "zone-freeze"
+#define CMD_ZONE_THAW "zone-thaw"
+#define CMD_ZONE_XFR_FREEZE "zone-xfr-freeze"
+#define CMD_ZONE_XFR_THAW "zone-xfr-thaw"
+
+#define CMD_ZONE_READ "zone-read"
+#define CMD_ZONE_BEGIN "zone-begin"
+#define CMD_ZONE_COMMIT "zone-commit"
+#define CMD_ZONE_ABORT "zone-abort"
+#define CMD_ZONE_DIFF "zone-diff"
+#define CMD_ZONE_GET "zone-get"
+#define CMD_ZONE_SET "zone-set"
+#define CMD_ZONE_UNSET "zone-unset"
+#define CMD_ZONE_PURGE "zone-purge"
+#define CMD_ZONE_STATS "zone-stats"
+
+#define CMD_CONF_INIT "conf-init"
+#define CMD_CONF_CHECK "conf-check"
+#define CMD_CONF_IMPORT "conf-import"
+#define CMD_CONF_EXPORT "conf-export"
+#define CMD_CONF_LIST "conf-list"
+#define CMD_CONF_READ "conf-read"
+#define CMD_CONF_BEGIN "conf-begin"
+#define CMD_CONF_COMMIT "conf-commit"
+#define CMD_CONF_ABORT "conf-abort"
+#define CMD_CONF_DIFF "conf-diff"
+#define CMD_CONF_GET "conf-get"
+#define CMD_CONF_SET "conf-set"
+#define CMD_CONF_UNSET "conf-unset"
+
+#define CTL_LOG_STR "failed to control"
+
+#define CTL_SEND(type, data) \
+ ret = knot_ctl_send(args->ctl, (type), (data)); \
+ if (ret != KNOT_EOK) { \
+ log_error(CTL_LOG_STR" (%s)", knot_strerror(ret)); \
+ return ret; \
+ }
+
+#define CTL_SEND_DATA CTL_SEND(KNOT_CTL_TYPE_DATA, &data)
+#define CTL_SEND_BLOCK CTL_SEND(KNOT_CTL_TYPE_BLOCK, NULL)
+
+static int check_args(cmd_args_t *args, int min, int max)
+{
+ if (max == 0 && args->argc > 0) {
+ log_error("command doesn't take arguments");
+ return KNOT_EINVAL;
+ } else if (min == max && args->argc != min) {
+ log_error("command requires %i arguments", min);
+ return KNOT_EINVAL;
+ } else if (args->argc < min) {
+ log_error("command requires at least %i arguments", min);
+ return KNOT_EINVAL;
+ } else if (max > 0 && args->argc > max) {
+ log_error("command takes at most %i arguments", max);
+ return KNOT_EINVAL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int check_conf_args(cmd_args_t *args)
+{
+ // Mask relevant flags.
+ cmd_flag_t flags = args->desc->flags;
+ flags &= CMD_FOPT_ITEM | CMD_FREQ_ITEM | CMD_FOPT_DATA;
+
+ switch (args->argc) {
+ case 0:
+ if (flags == CMD_FNONE || (flags & CMD_FOPT_ITEM)) {
+ return KNOT_EOK;
+ }
+ break;
+ case 1:
+ if (flags & (CMD_FOPT_ITEM | CMD_FREQ_ITEM)) {
+ return KNOT_EOK;
+ }
+ break;
+ default:
+ if (flags != CMD_FNONE) {
+ return KNOT_EOK;
+ }
+ break;
+ }
+
+ log_error("invalid number of arguments");
+
+ return KNOT_EINVAL;
+}
+
+static int get_conf_key(const char *key, knot_ctl_data_t *data)
+{
+ // Get key0.
+ const char *key0 = key;
+
+ // Check for id.
+ char *id = strchr(key, '[');
+ if (id != NULL) {
+ // Separate key0 and id.
+ *id++ = '\0';
+
+ // Check for id end.
+ char *id_end = id;
+ while ((id_end = strchr(id_end, ']')) != NULL) {
+ // Check for escaped character.
+ if (*(id_end - 1) != '\\') {
+ break;
+ }
+ id_end++;
+ }
+
+ // Check for unclosed id.
+ if (id_end == NULL) {
+ log_error("(missing bracket after identifier) %s", id);
+ return KNOT_EINVAL;
+ }
+
+ // Separate id and key1.
+ *id_end = '\0';
+
+ key = id_end + 1;
+
+ // Key1 or nothing must follow.
+ if (*key != '.' && *key != '\0') {
+ log_error("(unexpected token) %s", key);
+ return KNOT_EINVAL;
+ }
+ }
+
+ // Check for key1.
+ char *key1 = strchr(key, '.');
+ if (key1 != NULL) {
+ // Separate key0/id and key1.
+ *key1++ = '\0';
+
+ if (*key1 == '\0') {
+ log_error("(missing item specification)");
+ return KNOT_EINVAL;
+ }
+ }
+
+ (*data)[KNOT_CTL_IDX_SECTION] = key0;
+ (*data)[KNOT_CTL_IDX_ITEM] = key1;
+ (*data)[KNOT_CTL_IDX_ID] = id;
+
+ return KNOT_EOK;
+}
+
+static void format_data(cmd_args_t *args, knot_ctl_type_t data_type,
+ knot_ctl_data_t *data, bool *empty)
+{
+ const char *error = (*data)[KNOT_CTL_IDX_ERROR];
+ const char *flags = (*data)[KNOT_CTL_IDX_FLAGS];
+ const char *key0 = (*data)[KNOT_CTL_IDX_SECTION];
+ const char *key1 = (*data)[KNOT_CTL_IDX_ITEM];
+ const char *id = (*data)[KNOT_CTL_IDX_ID];
+ const char *zone = (*data)[KNOT_CTL_IDX_ZONE];
+ const char *owner = (*data)[KNOT_CTL_IDX_OWNER];
+ const char *ttl = (*data)[KNOT_CTL_IDX_TTL];
+ const char *type = (*data)[KNOT_CTL_IDX_TYPE];
+ const char *value = (*data)[KNOT_CTL_IDX_DATA];
+
+ bool col = false;
+ char status_col[32] = "";
+
+ static bool first_status_item = true;
+
+ const char *sign = NULL;
+ if (ctl_has_flag(flags, CTL_FLAG_DIFF_ADD)) {
+ sign = CTL_FLAG_DIFF_ADD;
+ } else if (ctl_has_flag(flags, CTL_FLAG_DIFF_REM)) {
+ sign = CTL_FLAG_DIFF_REM;
+ }
+
+ switch (args->desc->cmd) {
+ case CTL_STATUS:
+ if (error != NULL) {
+ printf("error: (%s)%s%s", error,
+ (type != NULL) ? " " : "",
+ (type != NULL) ? type : "");
+ } else if (value != NULL) {
+ printf("%s", value);
+ *empty = false;
+ }
+ break;
+ case CTL_STOP:
+ case CTL_RELOAD:
+ case CTL_CONF_BEGIN:
+ case CTL_CONF_ABORT:
+ // Only error message is expected here.
+ if (error != NULL) {
+ printf("error: (%s)", error);
+ }
+ break;
+ case CTL_ZONE_STATUS:
+ if (error == NULL) {
+ col = args->extended ? args->color_force : args->color;
+ }
+ if (!ctl_has_flag(flags, CTL_FLAG_STATUS_EMPTY)) {
+ strlcat(status_col, COL_BOLD(col), sizeof(status_col));
+ }
+ if (ctl_has_flag(flags, CTL_FLAG_STATUS_SLAVE)) {
+ strlcat(status_col, COL_RED(col), sizeof(status_col));
+ } else {
+ strlcat(status_col, COL_GRN(col), sizeof(status_col));
+ }
+ if (ctl_has_flag(flags, CTL_FLAG_STATUS_MEMBER)) {
+ strlcat(status_col, COL_UNDR(col), sizeof(status_col));
+ }
+ // FALLTHROUGH
+ case CTL_ZONE_RELOAD:
+ case CTL_ZONE_REFRESH:
+ case CTL_ZONE_RETRANSFER:
+ case CTL_ZONE_NOTIFY:
+ case CTL_ZONE_FLUSH:
+ case CTL_ZONE_BACKUP:
+ case CTL_ZONE_RESTORE:
+ case CTL_ZONE_SIGN:
+ case CTL_ZONE_KEYS_LOAD:
+ case CTL_ZONE_KEY_ROLL:
+ case CTL_ZONE_KSK_SBM:
+ case CTL_ZONE_FREEZE:
+ case CTL_ZONE_THAW:
+ case CTL_ZONE_BEGIN:
+ case CTL_ZONE_COMMIT:
+ case CTL_ZONE_ABORT:
+ case CTL_ZONE_PURGE:
+ if (data_type == KNOT_CTL_TYPE_DATA) {
+ printf("%s%s%s%s%s%s%s%s%s%s",
+ (!(*empty) ? "\n" : ""),
+ (error != NULL ? "error: " : ""),
+ (zone != NULL ? "[" : ""),
+ (zone != NULL ? status_col : ""),
+ (zone != NULL ? zone : ""),
+ (zone != NULL ? COL_RST(col) : ""),
+ (zone != NULL ? "]" : ""),
+ (error != NULL ? " (" : ""),
+ (error != NULL ? error : ""),
+ (error != NULL ? ")" : ""));
+ *empty = false;
+ }
+ if (args->desc->cmd == CTL_ZONE_STATUS && type != NULL) {
+ if (data_type == KNOT_CTL_TYPE_DATA) {
+ first_status_item = true;
+ }
+ if (!args->extended &&
+ (value == 0 || strcmp(value, STATUS_EMPTY) == 0) &&
+ strcmp(type, "serial") != 0) {
+ return;
+ }
+
+ printf("%s %s: %s%s%s",
+ (first_status_item ? "" : " |"),
+ type, COL_BOLD(col), value, COL_RST(col));
+ first_status_item = false;
+ }
+ break;
+ case CTL_CONF_COMMIT: // Can return a check error context.
+ case CTL_CONF_LIST:
+ case CTL_CONF_READ:
+ case CTL_CONF_DIFF:
+ case CTL_CONF_GET:
+ case CTL_CONF_SET:
+ case CTL_CONF_UNSET:
+ if (data_type == KNOT_CTL_TYPE_DATA) {
+ printf("%s%s%s%s%s%s%s%s%s%s%s%s",
+ (!(*empty) ? "\n" : ""),
+ (error != NULL ? "error: (" : ""),
+ (error != NULL ? error : ""),
+ (error != NULL ? ") " : ""),
+ (sign != NULL ? sign : ""),
+ (key0 != NULL ? key0 : ""),
+ (id != NULL ? "[" : ""),
+ (id != NULL ? id : ""),
+ (id != NULL ? "]" : ""),
+ (key1 != NULL ? "." : ""),
+ (key1 != NULL ? key1 : ""),
+ (value != NULL ? " =" : ""));
+ *empty = false;
+ }
+ if (value != NULL) {
+ printf(" %s", value);
+ }
+ break;
+ case CTL_ZONE_READ:
+ case CTL_ZONE_DIFF:
+ case CTL_ZONE_GET:
+ case CTL_ZONE_SET:
+ case CTL_ZONE_UNSET:
+ printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (!(*empty) ? "\n" : ""),
+ (error != NULL ? "error: (" : ""),
+ (error != NULL ? error : ""),
+ (error != NULL ? ") " : ""),
+ (zone != NULL ? "[" : ""),
+ (zone != NULL ? zone : ""),
+ (zone != NULL ? "] " : ""),
+ (sign != NULL ? sign : ""),
+ (owner != NULL ? owner : ""),
+ (ttl != NULL ? " " : ""),
+ (ttl != NULL ? ttl : ""),
+ (type != NULL ? " " : ""),
+ (type != NULL ? type : ""),
+ (value != NULL ? " " : ""),
+ (value != NULL ? value : ""));
+ *empty = false;
+ break;
+ case CTL_STATS:
+ case CTL_ZONE_STATS:
+ printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ (!(*empty) ? "\n" : ""),
+ (error != NULL ? "error: (" : ""),
+ (error != NULL ? error : ""),
+ (error != NULL ? ") " : ""),
+ (zone != NULL ? "[" : ""),
+ (zone != NULL ? zone : ""),
+ (zone != NULL ? "] " : ""),
+ (key0 != NULL ? key0 : ""),
+ (key1 != NULL ? "." : ""),
+ (key1 != NULL ? key1 : ""),
+ (id != NULL ? "[" : ""),
+ (id != NULL ? id : ""),
+ (id != NULL ? "]" : ""),
+ (value != NULL ? " = " : ""),
+ (value != NULL ? value : ""));
+ *empty = false;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void format_block(ctl_cmd_t cmd, bool failed, bool empty)
+{
+ switch (cmd) {
+ case CTL_STATUS:
+ printf("%s\n", (failed || !empty) ? "" : "Running");
+ break;
+ case CTL_STOP:
+ printf("%s\n", failed ? "" : "Stopped");
+ break;
+ case CTL_RELOAD:
+ printf("%s\n", failed ? "" : "Reloaded");
+ break;
+ case CTL_CONF_BEGIN:
+ case CTL_CONF_COMMIT:
+ case CTL_CONF_ABORT:
+ case CTL_CONF_SET:
+ case CTL_CONF_UNSET:
+ case CTL_ZONE_RELOAD:
+ case CTL_ZONE_REFRESH:
+ case CTL_ZONE_RETRANSFER:
+ case CTL_ZONE_NOTIFY:
+ case CTL_ZONE_FLUSH:
+ case CTL_ZONE_BACKUP:
+ case CTL_ZONE_RESTORE:
+ case CTL_ZONE_SIGN:
+ case CTL_ZONE_KEYS_LOAD:
+ case CTL_ZONE_KEY_ROLL:
+ case CTL_ZONE_KSK_SBM:
+ case CTL_ZONE_FREEZE:
+ case CTL_ZONE_THAW:
+ case CTL_ZONE_XFR_FREEZE:
+ case CTL_ZONE_XFR_THAW:
+ case CTL_ZONE_BEGIN:
+ case CTL_ZONE_COMMIT:
+ case CTL_ZONE_ABORT:
+ case CTL_ZONE_SET:
+ case CTL_ZONE_UNSET:
+ case CTL_ZONE_PURGE:
+ printf("%s\n", failed ? "" : "OK");
+ break;
+ case CTL_ZONE_STATUS:
+ case CTL_ZONE_READ:
+ case CTL_ZONE_DIFF:
+ case CTL_ZONE_GET:
+ case CTL_CONF_LIST:
+ case CTL_CONF_READ:
+ case CTL_CONF_DIFF:
+ case CTL_CONF_GET:
+ case CTL_ZONE_STATS:
+ case CTL_STATS:
+ printf("%s", empty ? "" : "\n");
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static int ctl_receive(cmd_args_t *args)
+{
+ bool failed = false;
+ bool empty = true;
+
+ while (true) {
+ knot_ctl_type_t type;
+ knot_ctl_data_t data;
+
+ int ret = knot_ctl_receive(args->ctl, &type, &data);
+ if (ret != KNOT_EOK) {
+ log_error(CTL_LOG_STR" (%s)", knot_strerror(ret));
+ return ret;
+ }
+
+ switch (type) {
+ case KNOT_CTL_TYPE_END:
+ log_error(CTL_LOG_STR" (%s)", knot_strerror(KNOT_EMALF));
+ return KNOT_EMALF;
+ case KNOT_CTL_TYPE_BLOCK:
+ format_block(args->desc->cmd, failed, empty);
+ return failed ? KNOT_ERROR : KNOT_EOK;
+ case KNOT_CTL_TYPE_DATA:
+ case KNOT_CTL_TYPE_EXTRA:
+ format_data(args, type, &data, &empty);
+ break;
+ default:
+ assert(0);
+ return KNOT_EINVAL;
+ }
+
+ if (data[KNOT_CTL_IDX_ERROR] != NULL) {
+ failed = true;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int cmd_ctl(cmd_args_t *args)
+{
+ int ret = check_args(args, 0, (args->desc->cmd == CTL_STATUS ? 1 : 0));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ [KNOT_CTL_IDX_TYPE] = args->argc > 0 ? args->argv[0] : NULL
+ };
+
+ CTL_SEND_DATA
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+static int set_stats_items(cmd_args_t *args, knot_ctl_data_t *data)
+{
+ int min_args, max_args;
+ switch (args->desc->cmd) {
+ case CTL_STATS: min_args = 0; max_args = 1; break;
+ case CTL_ZONE_STATS: min_args = 1; max_args = 2; break;
+ default:
+ assert(0);
+ return KNOT_EINVAL;
+ }
+
+ // Check the number of arguments.
+ int ret = check_args(args, min_args, max_args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ int idx = 0;
+
+ // Set ZONE name.
+ if (args->argc > idx && args->desc->cmd == CTL_ZONE_STATS) {
+ if (strcmp(args->argv[idx], "--") != 0) {
+ (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx];
+ }
+ idx++;
+ }
+
+ if (args->argc > idx) {
+ (*data)[KNOT_CTL_IDX_SECTION] = args->argv[idx];
+
+ char *item = strchr(args->argv[idx], '.');
+ if (item != NULL) {
+ // Separate section and item.
+ *item++ = '\0';
+ (*data)[KNOT_CTL_IDX_ITEM] = item;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int cmd_stats_ctl(cmd_args_t *args)
+{
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ };
+
+ int ret = set_stats_items(args, &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ CTL_SEND_DATA
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+static int zone_exec(cmd_args_t *args, int (*fcn)(const knot_dname_t *, void *),
+ void *data)
+{
+ bool failed = false;
+
+ // Process specified zones.
+ if (args->argc > 0) {
+ knot_dname_storage_t id;
+
+ for (int i = 0; i < args->argc; i++) {
+ if (knot_dname_from_str(id, args->argv[i], sizeof(id)) == NULL) {
+ log_zone_str_error(args->argv[i], "invalid name");
+ failed = true;
+ continue;
+ }
+ knot_dname_to_lower(id);
+
+ if (!conf_rawid_exists(conf(), C_ZONE, id, knot_dname_size(id))) {
+ log_zone_error(id, "%s", knot_strerror(KNOT_ENOZONE));
+ failed = true;
+ continue;
+ }
+
+ if (fcn(id, data) != KNOT_EOK) {
+ failed = true;
+ }
+ }
+ // Process all configured zones.
+ } else {
+ for (conf_iter_t iter = conf_iter(conf(), C_ZONE);
+ iter.code == KNOT_EOK; conf_iter_next(conf(), &iter)) {
+ conf_val_t val = conf_iter_id(conf(), &iter);
+ const knot_dname_t *id = conf_dname(&val);
+
+ if (fcn(id, data) != KNOT_EOK) {
+ failed = true;
+ }
+ }
+ }
+
+ return failed ? KNOT_ERROR : KNOT_EOK;
+}
+
+static int zone_check(const knot_dname_t *dname, void *data)
+{
+ cmd_args_t *args = data;
+
+ zone_contents_t *contents = NULL;
+ conf_val_t mode = conf_zone_get(conf(), C_SEM_CHECKS, dname);
+ int ret = zone_load_contents(conf(), dname, &contents, conf_opt(&mode), args->force);
+ zone_contents_deep_free(contents);
+ if (ret != KNOT_EOK && ret != KNOT_ESEMCHECK) {
+ knot_dname_txt_storage_t name;
+ (void)knot_dname_to_str(name, dname, sizeof(name));
+ log_error("[%s] failed to check zone file (%s)", name, knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int cmd_zone_check(cmd_args_t *args)
+{
+ return zone_exec(args, zone_check, args);
+}
+
+static int cmd_zone_key_roll_ctl(cmd_args_t *args)
+{
+ int ret = check_args(args, 2, 2);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ [KNOT_CTL_IDX_ZONE] = args->argv[0],
+ [KNOT_CTL_IDX_TYPE] = args->argv[1],
+ };
+
+ CTL_SEND_DATA
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+static int cmd_zone_ctl(cmd_args_t *args)
+{
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ };
+
+ // Check the number of arguments.
+ int ret = check_args(args, (args->desc->flags & CMD_FREQ_ZONE) ? 1 : 0, -1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) {
+ log_error("force option required!");
+ return KNOT_EDENIED;
+ }
+
+ // Ignore all zones argument.
+ if (args->argc == 1 && strcmp(args->argv[0], "--") == 0) {
+ args->argc = 0;
+ }
+
+ if (args->argc == 0) {
+ CTL_SEND_DATA
+ }
+ for (int i = 0; i < args->argc; i++) {
+ data[KNOT_CTL_IDX_ZONE] = args->argv[i];
+
+ CTL_SEND_DATA
+ }
+
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+#define MAX_FILTERS 12
+
+typedef struct {
+ const char *name;
+ char id;
+ bool with_data; // Only ONE filter of each filter_desc_t may have data!
+} filter_desc_t;
+
+const filter_desc_t zone_flush_filters[MAX_FILTERS] = {
+ { "+outdir", CTL_FILTER_FLUSH_OUTDIR, true },
+};
+
+const filter_desc_t zone_backup_filters[MAX_FILTERS] = {
+ { "+backupdir", CTL_FILTER_BACKUP_OUTDIR, true },
+ { "+zonefile", CTL_FILTER_BACKUP_ZONEFILE, false },
+ { "+nozonefile", CTL_FILTER_BACKUP_NOZONEFILE, false },
+ { "+journal", CTL_FILTER_BACKUP_JOURNAL, false },
+ { "+nojournal", CTL_FILTER_BACKUP_NOJOURNAL, false },
+ { "+timers", CTL_FILTER_BACKUP_TIMERS, false },
+ { "+notimers", CTL_FILTER_BACKUP_NOTIMERS, false },
+ { "+kaspdb", CTL_FILTER_BACKUP_KASPDB, false },
+ { "+nokaspdb", CTL_FILTER_BACKUP_NOKASPDB, false },
+ { "+catalog", CTL_FILTER_BACKUP_CATALOG, false },
+ { "+nocatalog", CTL_FILTER_BACKUP_NOCATALOG, false },
+};
+
+const filter_desc_t zone_status_filters[MAX_FILTERS] = {
+ { "+role", CTL_FILTER_STATUS_ROLE },
+ { "+serial", CTL_FILTER_STATUS_SERIAL },
+ { "+transaction", CTL_FILTER_STATUS_TRANSACTION },
+ { "+freeze", CTL_FILTER_STATUS_FREEZE },
+ { "+catalog", CTL_FILTER_STATUS_CATALOG },
+ { "+events", CTL_FILTER_STATUS_EVENTS },
+};
+
+const filter_desc_t zone_purge_filters[MAX_FILTERS] = {
+ { "+expire", CTL_FILTER_PURGE_EXPIRE },
+ { "+zonefile", CTL_FILTER_PURGE_ZONEFILE },
+ { "+journal", CTL_FILTER_PURGE_JOURNAL },
+ { "+timers", CTL_FILTER_PURGE_TIMERS },
+ { "+kaspdb", CTL_FILTER_PURGE_KASPDB },
+ { "+catalog", CTL_FILTER_PURGE_CATALOG },
+ { "+orphan", CTL_FILTER_PURGE_ORPHAN },
+};
+
+const filter_desc_t null_filter = { 0 };
+
+static const filter_desc_t *get_filter(ctl_cmd_t cmd, const char *filter_name)
+{
+ const filter_desc_t *fd = NULL;
+ switch (cmd) {
+ case CTL_ZONE_FLUSH:
+ fd = zone_flush_filters;
+ break;
+ case CTL_ZONE_BACKUP:
+ case CTL_ZONE_RESTORE:
+ fd = zone_backup_filters;
+ break;
+ case CTL_ZONE_STATUS:
+ fd = zone_status_filters;
+ break;
+ case CTL_ZONE_PURGE:
+ fd = zone_purge_filters;
+ break;
+ default:
+ return &null_filter;
+ }
+ for (size_t i = 0; i < MAX_FILTERS && fd[i].name != NULL; i++) {
+ if (strcmp(fd[i].name, filter_name) == 0) {
+ return &fd[i];
+ }
+ }
+ return &null_filter;
+}
+
+static int cmd_zone_filter_ctl(cmd_args_t *args)
+{
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ };
+
+ if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) {
+ log_error("force option required!");
+ return KNOT_EDENIED;
+ }
+
+ char filter_buff[MAX_FILTERS + 1] = { 0 };
+
+ // First, process the filters.
+ for (int i = 0; i < args->argc; i++) {
+ if (args->argv[i][0] == '+') {
+ if (data[KNOT_CTL_IDX_FILTER] == NULL) {
+ data[KNOT_CTL_IDX_FILTER] = filter_buff;
+ }
+ char filter_id[2] = { get_filter(args->desc->cmd, args->argv[i])->id, 0 };
+ if (filter_id[0] == '\0') {
+ log_error("unknown filter: %s", args->argv[i]);
+ return KNOT_EINVAL;
+ }
+ if (strchr(filter_buff, filter_id[0]) == NULL) {
+ assert(strlen(filter_buff) < MAX_FILTERS);
+ strlcat(filter_buff, filter_id, sizeof(filter_buff));
+ }
+ if (get_filter(args->desc->cmd, args->argv[i])->with_data) {
+ data[KNOT_CTL_IDX_DATA] = args->argv[++i];
+ }
+ }
+ }
+
+ // Second, process zones.
+ int ret;
+ int sentzones = 0;
+ bool twodash = false;
+ for (int i = 0; i < args->argc; i++) {
+ // Skip filters.
+ if (args->argv[i][0] == '+') {
+ if (get_filter(args->desc->cmd, args->argv[i])->with_data) {
+ i++;
+ }
+ continue;
+ }
+
+ if (strcmp(args->argv[i], "--") != 0) {
+ data[KNOT_CTL_IDX_ZONE] = args->argv[i];
+ CTL_SEND_DATA
+ sentzones++;
+ } else {
+ twodash = true;
+ }
+ }
+
+ if ((args->desc->flags & CMD_FREQ_ZONE) && sentzones == 0 && !twodash) {
+ log_error("zone must be specified (or -- for all zones)");
+ return KNOT_EDENIED;
+ }
+
+ if (sentzones == 0) {
+ CTL_SEND_DATA
+ }
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+static int set_rdata(cmd_args_t *args, int pos, char *rdata, size_t rdata_len)
+{
+ rdata[0] = '\0';
+
+ for (int i = pos; i < args->argc; i++) {
+ if (i > pos && strlcat(rdata, " ", rdata_len) >= rdata_len) {
+ return KNOT_ESPACE;
+ }
+ if (strlcat(rdata, args->argv[i], rdata_len) >= rdata_len) {
+ return KNOT_ESPACE;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static int set_node_items(cmd_args_t *args, knot_ctl_data_t *data, char *rdata,
+ size_t rdata_len)
+{
+ int min_args, max_args;
+ switch (args->desc->cmd) {
+ case CTL_ZONE_READ:
+ case CTL_ZONE_GET: min_args = 1; max_args = 3; break;
+ case CTL_ZONE_DIFF: min_args = 1; max_args = 1; break;
+ case CTL_ZONE_SET: min_args = 3; max_args = -1; break;
+ case CTL_ZONE_UNSET: min_args = 2; max_args = -1; break;
+ default:
+ assert(0);
+ return KNOT_EINVAL;
+ }
+
+ // Check the number of arguments.
+ int ret = check_args(args, min_args, max_args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ int idx = 0;
+
+ // Set ZONE name.
+ assert(args->argc > idx);
+ if (strcmp(args->argv[idx], "--") != 0) {
+ (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx];
+ }
+ idx++;
+
+ // Set OWNER name if specified.
+ if (args->argc > idx) {
+ (*data)[KNOT_CTL_IDX_OWNER] = args->argv[idx];
+ idx++;
+ }
+
+ // Set TTL only with an editing operation.
+ if (args->argc > idx) {
+ uint32_t num;
+ uint16_t type;
+ if (knot_rrtype_from_string(args->argv[idx], &type) != 0 &&
+ str_to_u32(args->argv[idx], &num) == KNOT_EOK) {
+ switch (args->desc->cmd) {
+ case CTL_ZONE_SET:
+ case CTL_ZONE_UNSET:
+ (*data)[KNOT_CTL_IDX_TTL] = args->argv[idx];
+ idx++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Set record TYPE if specified.
+ if (args->argc > idx) {
+ (*data)[KNOT_CTL_IDX_TYPE] = args->argv[idx];
+ idx++;
+ }
+
+ // Set record DATA if specified.
+ if (args->argc > idx) {
+ ret = set_rdata(args, idx, rdata, rdata_len);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ (*data)[KNOT_CTL_IDX_DATA] = rdata;
+ }
+
+ return KNOT_EOK;
+}
+
+static int cmd_zone_node_ctl(cmd_args_t *args)
+{
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = args->flags,
+ };
+
+ char rdata[65536]; // Maximum item size in libknot control interface.
+
+ int ret = set_node_items(args, &data, rdata, sizeof(rdata));
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ CTL_SEND_DATA
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+static int cmd_conf_init(cmd_args_t *args)
+{
+ int ret = check_args(args, 0, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = conf_db_check(conf(), &conf()->read_txn);
+ if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) {
+ if (ret != KNOT_EOK && !args->force) {
+ log_error("use force option to overwrite the existing "
+ "destination and ensure the server is not running!");
+ return KNOT_EDENIED;
+ }
+
+ ret = conf_import(conf(), "", false, false);
+ }
+
+ if (ret == KNOT_EOK) {
+ log_info("OK");
+ } else {
+ log_error("init (%s)", knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int conf_check_group(const yp_item_t *group, const uint8_t *id, size_t id_len)
+{
+ knotd_conf_check_extra_t extra = {
+ .conf = conf(),
+ .txn = &conf()->read_txn,
+ .check = true
+ };
+ knotd_conf_check_args_t args = {
+ .id = id,
+ .id_len = id_len,
+ .extra = &extra
+ };
+
+ bool non_empty = false;
+ bool error = false;
+
+ // Check the group sub-items.
+ for (yp_item_t *item = group->sub_items; item->name != NULL; item++) {
+ args.item = item;
+
+ conf_val_t bin;
+ conf_db_get(conf(), &conf()->read_txn, group->name, item->name,
+ id, id_len, &bin);
+ if (bin.code == KNOT_ENOENT) {
+ continue;
+ } else if (bin.code != KNOT_EOK) {
+ log_error("failed to read the configuration DB (%s)",
+ knot_strerror(bin.code));
+ return bin.code;
+ }
+
+ non_empty = true;
+
+ // Check the item value(s).
+ size_t values = conf_val_count(&bin);
+ for (size_t i = 1; i <= values; i++) {
+ conf_val(&bin);
+ args.data = bin.data;
+ args.data_len = bin.len;
+
+ int ret = conf_exec_callbacks(&args);
+ if (ret != KNOT_EOK) {
+ log_error("config, item '%s%s%s%s.%s' (%s)",
+ group->name + 1,
+ (id != NULL ? "[" : ""),
+ (id != NULL ? (const char *)id : ""),
+ (id != NULL ? "]" : ""),
+ item->name + 1,
+ args.err_str);
+ error = true;
+ }
+ if (values > 1) {
+ conf_val_next(&bin);
+ }
+ }
+ }
+
+ // Check the group item itself.
+ if (id != NULL || non_empty) {
+ args.item = group;
+ args.data = NULL;
+ args.data_len = 0;
+
+ int ret = conf_exec_callbacks(&args);
+ if (ret != KNOT_EOK) {
+ log_error("config, section '%s%s%s%s' (%s)",
+ group->name + 1,
+ (id != NULL ? "[" : ""),
+ (id != NULL ? (const char *)id : ""),
+ (id != NULL ? "]" : ""),
+ args.err_str);
+ error = true;
+ }
+ }
+
+ return error ? KNOT_ESEMCHECK : KNOT_EOK;
+}
+
+static int cmd_conf_check(cmd_args_t *args) // Similar to conf_io_check().
+{
+ int ret = check_args(args, 0, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (conf()->filename == NULL) { // Config file already checked.
+ for (yp_item_t *item = conf()->schema; item->name != NULL; item++) {
+ // Skip include item.
+ if (item->type != YP_TGRP) {
+ continue;
+ }
+
+ // Group without identifiers.
+ if (!(item->flags & YP_FMULTI)) {
+ ret = conf_check_group(item, NULL, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ continue;
+ }
+
+ conf_iter_t iter;
+ ret = conf_db_iter_begin(conf(), &conf()->read_txn, item->name, &iter);
+ if (ret == KNOT_ENOENT) {
+ continue;
+ } else if (ret != KNOT_EOK) {
+ log_error("failed to read the configuration DB (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ while (ret == KNOT_EOK) {
+ const uint8_t *id;
+ size_t id_len;
+ ret = conf_db_iter_id(conf(), &iter, &id, &id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ log_error("failed to read the configuration DB (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ // Check the group with this identifier.
+ ret = conf_check_group(item, id, id_len);
+ if (ret != KNOT_EOK) {
+ conf_db_iter_finish(conf(), &iter);
+ return ret;
+ }
+
+ ret = conf_db_iter_next(conf(), &iter);
+ }
+ if (ret != KNOT_EOF) {
+ log_error("failed to read the configuration DB (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+ }
+ }
+
+ log_info("Configuration is valid");
+
+ return KNOT_EOK;
+}
+
+static int cmd_conf_import(cmd_args_t *args)
+{
+ int ret = check_args(args, 1, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = conf_db_check(conf(), &conf()->read_txn);
+ if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) {
+ if (ret != KNOT_EOK && !args->force) {
+ log_error("use force option to overwrite the existing "
+ "destination and ensure the server is not running!");
+ return KNOT_EDENIED;
+ }
+
+ log_debug("importing confdb from file '%s'", args->argv[0]);
+
+ ret = conf_import(conf(), args->argv[0], true, false);
+ }
+
+ if (ret == KNOT_EOK) {
+ log_info("OK");
+ } else {
+ log_error("import (%s)", knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int cmd_conf_export(cmd_args_t *args)
+{
+ int ret = check_args(args, 0, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Stdout is the default output file.
+ const char *file_name = NULL;
+ if (args->argc > 0) {
+ file_name = args->argv[0];
+ log_debug("exporting confdb into file '%s'", file_name);
+ }
+
+ ret = conf_export(conf(), file_name, YP_SNONE);
+
+ if (ret == KNOT_EOK) {
+ if (args->argc > 0) {
+ log_info("OK");
+ }
+ } else {
+ log_error("export (%s)", knot_strerror(ret));
+ }
+
+ return ret;
+}
+
+static int cmd_conf_ctl(cmd_args_t *args)
+{
+ // Check the number of arguments.
+ int ret = check_conf_args(args);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ char flags[16] = "";
+ strlcat(flags, args->flags, sizeof(flags));
+ if (args->desc->flags & CMD_FLIST_SCHEMA) {
+ strlcat(flags, CTL_FLAG_LIST_SCHEMA, sizeof(flags));
+ }
+
+ knot_ctl_data_t data = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd),
+ [KNOT_CTL_IDX_FLAGS] = flags,
+ };
+
+ // Send the command without parameters.
+ if (args->argc == 0) {
+ CTL_SEND_DATA
+ // Set the first item argument.
+ } else {
+ ret = get_conf_key(args->argv[0], &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Send if only one argument or item without values.
+ if (args->argc == 1 || !(args->desc->flags & CMD_FOPT_DATA)) {
+ CTL_SEND_DATA
+ }
+ }
+
+ // Send the item values or the other items.
+ for (int i = 1; i < args->argc; i++) {
+ if (args->desc->flags & CMD_FOPT_DATA) {
+ data[KNOT_CTL_IDX_DATA] = args->argv[i];
+ } else {
+ ret = get_conf_key(args->argv[i], &data);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ CTL_SEND_DATA
+ }
+
+ CTL_SEND_BLOCK
+
+ return ctl_receive(args);
+}
+
+const cmd_desc_t cmd_table[] = {
+ { CMD_EXIT, NULL, CTL_NONE },
+
+ { CMD_STATUS, cmd_ctl, CTL_STATUS, CMD_FOPT_DATA},
+ { CMD_STOP, cmd_ctl, CTL_STOP },
+ { CMD_RELOAD, cmd_ctl, CTL_RELOAD },
+ { CMD_STATS, cmd_stats_ctl, CTL_STATS },
+
+ { CMD_ZONE_CHECK, cmd_zone_check, CTL_NONE, CMD_FOPT_ZONE | CMD_FREAD },
+ { CMD_ZONE_STATUS, cmd_zone_filter_ctl, CTL_ZONE_STATUS, CMD_FOPT_ZONE },
+ { CMD_ZONE_RELOAD, cmd_zone_ctl, CTL_ZONE_RELOAD, CMD_FOPT_ZONE },
+ { CMD_ZONE_REFRESH, cmd_zone_ctl, CTL_ZONE_REFRESH, CMD_FOPT_ZONE },
+ { CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER, CMD_FOPT_ZONE },
+ { CMD_ZONE_NOTIFY, cmd_zone_ctl, CTL_ZONE_NOTIFY, CMD_FOPT_ZONE },
+ { CMD_ZONE_FLUSH, cmd_zone_filter_ctl, CTL_ZONE_FLUSH, CMD_FOPT_ZONE },
+ { CMD_ZONE_BACKUP, cmd_zone_filter_ctl, CTL_ZONE_BACKUP, CMD_FOPT_ZONE },
+ { CMD_ZONE_RESTORE, cmd_zone_filter_ctl, CTL_ZONE_RESTORE, CMD_FOPT_ZONE },
+ { CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_FOPT_ZONE },
+ { CMD_ZONE_KEYS_LOAD, cmd_zone_ctl, CTL_ZONE_KEYS_LOAD, CMD_FOPT_ZONE },
+ { CMD_ZONE_KEY_ROLL, cmd_zone_key_roll_ctl, CTL_ZONE_KEY_ROLL, CMD_FREQ_ZONE },
+ { CMD_ZONE_KSK_SBM, cmd_zone_ctl, CTL_ZONE_KSK_SBM, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
+ { CMD_ZONE_FREEZE, cmd_zone_ctl, CTL_ZONE_FREEZE, CMD_FOPT_ZONE },
+ { CMD_ZONE_THAW, cmd_zone_ctl, CTL_ZONE_THAW, CMD_FOPT_ZONE },
+ { CMD_ZONE_XFR_FREEZE, cmd_zone_ctl, CTL_ZONE_XFR_FREEZE, CMD_FOPT_ZONE },
+ { CMD_ZONE_XFR_THAW, cmd_zone_ctl, CTL_ZONE_XFR_THAW, CMD_FOPT_ZONE },
+
+ { CMD_ZONE_READ, cmd_zone_node_ctl, CTL_ZONE_READ, CMD_FREQ_ZONE },
+ { CMD_ZONE_BEGIN, cmd_zone_ctl, CTL_ZONE_BEGIN, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
+ { CMD_ZONE_COMMIT, cmd_zone_ctl, CTL_ZONE_COMMIT, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
+ { CMD_ZONE_ABORT, cmd_zone_ctl, CTL_ZONE_ABORT, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
+ { CMD_ZONE_DIFF, cmd_zone_node_ctl, CTL_ZONE_DIFF, CMD_FREQ_ZONE },
+ { CMD_ZONE_GET, cmd_zone_node_ctl, CTL_ZONE_GET, CMD_FREQ_ZONE },
+ { CMD_ZONE_SET, cmd_zone_node_ctl, CTL_ZONE_SET, CMD_FREQ_ZONE },
+ { CMD_ZONE_UNSET, cmd_zone_node_ctl, CTL_ZONE_UNSET, CMD_FREQ_ZONE },
+ { CMD_ZONE_PURGE, cmd_zone_filter_ctl, CTL_ZONE_PURGE, CMD_FREQ_ZONE | CMD_FOPT_ZONE },
+ { CMD_ZONE_STATS, cmd_stats_ctl, CTL_ZONE_STATS, CMD_FREQ_ZONE },
+
+ { CMD_CONF_INIT, cmd_conf_init, CTL_NONE, CMD_FWRITE },
+ { CMD_CONF_CHECK, cmd_conf_check, CTL_NONE, CMD_FREAD | CMD_FREQ_MOD },
+ { CMD_CONF_IMPORT, cmd_conf_import, CTL_NONE, CMD_FWRITE | CMD_FOPT_MOD },
+ { CMD_CONF_EXPORT, cmd_conf_export, CTL_NONE, CMD_FREAD | CMD_FOPT_MOD },
+ { CMD_CONF_LIST, cmd_conf_ctl, CTL_CONF_LIST, CMD_FOPT_ITEM | CMD_FLIST_SCHEMA },
+ { CMD_CONF_READ, cmd_conf_ctl, CTL_CONF_READ, CMD_FOPT_ITEM },
+ { CMD_CONF_BEGIN, cmd_conf_ctl, CTL_CONF_BEGIN },
+ { CMD_CONF_COMMIT, cmd_conf_ctl, CTL_CONF_COMMIT },
+ { CMD_CONF_ABORT, cmd_conf_ctl, CTL_CONF_ABORT },
+ { CMD_CONF_DIFF, cmd_conf_ctl, CTL_CONF_DIFF, CMD_FOPT_ITEM | CMD_FREQ_TXN },
+ { CMD_CONF_GET, cmd_conf_ctl, CTL_CONF_GET, CMD_FOPT_ITEM | CMD_FREQ_TXN },
+ { CMD_CONF_SET, cmd_conf_ctl, CTL_CONF_SET, CMD_FREQ_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN | CMD_FLIST_SCHEMA},
+ { CMD_CONF_UNSET, cmd_conf_ctl, CTL_CONF_UNSET, CMD_FOPT_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN },
+ { NULL }
+};
+
+static const cmd_help_t cmd_help_table[] = {
+ { CMD_EXIT, "", "Exit interactive mode." },
+ { "", "", "" },
+ { CMD_STATUS, "[<detail>]", "Check if the server is running." },
+ { CMD_STOP, "", "Stop the server if running." },
+ { CMD_RELOAD, "", "Reload the server configuration and modified zones." },
+ { CMD_STATS, "[<module>[.<counter>]]", "Show global statistics counter(s)." },
+ { "", "", "" },
+ { CMD_ZONE_CHECK, "[<zone>...]", "Check if the zone can be loaded. (*)" },
+ { CMD_ZONE_STATUS, "[<zone>...] [<filter>...]", "Show the zone status." },
+ { CMD_ZONE_RELOAD, "[<zone>...]", "Reload a zone from a disk. (#)" },
+ { CMD_ZONE_REFRESH, "[<zone>...]", "Force slave zone refresh. (#)" },
+ { CMD_ZONE_NOTIFY, "[<zone>...]", "Send a NOTIFY message to all configured remotes. (#)" },
+ { CMD_ZONE_RETRANSFER, "[<zone>...]", "Force slave zone retransfer (no serial check). (#)" },
+ { CMD_ZONE_FLUSH, "[<zone>...] [<filter>...]", "Flush zone journal into the zone file. (#)" },
+ { CMD_ZONE_BACKUP, "[<zone>...] [<filter>...] +backupdir <dir>", "Backup zone data and metadata. (#)" },
+ { CMD_ZONE_RESTORE, "[<zone>...] [<filter>...] +backupdir <dir>", "Restore zone data and metadata. (#)" },
+ { CMD_ZONE_SIGN, "[<zone>...]", "Re-sign the automatically signed zone. (#)" },
+ { CMD_ZONE_KEYS_LOAD, "[<zone>...]", "Re-load keys from KASP database, sign the zone. (#)" },
+ { CMD_ZONE_KEY_ROLL, " <zone> ksk|zsk", "Trigger immediate key rollover. (#)" },
+ { CMD_ZONE_KSK_SBM, " <zone>...", "When KSK submission, confirm parent's DS presence. (#)" },
+ { CMD_ZONE_FREEZE, "[<zone>...]", "Temporarily postpone automatic zone-changing events. (#)" },
+ { CMD_ZONE_THAW, "[<zone>...]", "Dismiss zone freeze. (#)" },
+ { CMD_ZONE_XFR_FREEZE, "[<zone>...]", "Temporarily disable outgoing AXFR/IXFR. (#)" },
+ { CMD_ZONE_XFR_THAW, "[<zone>...]", "Dismiss outgoing XFR freeze. (#)" },
+ { "", "", "" },
+ { CMD_ZONE_READ, "<zone> [<owner> [<type>]]", "Get zone data that are currently being presented." },
+ { CMD_ZONE_BEGIN, "<zone>...", "Begin a zone transaction." },
+ { CMD_ZONE_COMMIT, "<zone>...", "Commit the zone transaction." },
+ { CMD_ZONE_ABORT, "<zone>...", "Abort the zone transaction." },
+ { CMD_ZONE_DIFF, "<zone>", "Get zone changes within the transaction." },
+ { CMD_ZONE_GET, "<zone> [<owner> [<type>]]", "Get zone data within the transaction." },
+ { CMD_ZONE_SET, "<zone> <owner> [<ttl>] <type> <rdata>", "Add zone record within the transaction." },
+ { CMD_ZONE_UNSET, "<zone> <owner> [<type> [<rdata>]]", "Remove zone data within the transaction." },
+ { CMD_ZONE_PURGE, "<zone>... [<filter>...]", "Purge zone data, zone file, journal, timers, and KASP data. (#)" },
+ { CMD_ZONE_STATS, "<zone> [<module>[.<counter>]]", "Show zone statistics counter(s)."},
+ { "", "", "" },
+ { CMD_CONF_INIT, "", "Initialize the confdb. (*)" },
+ { CMD_CONF_CHECK, "", "Check the server configuration. (*)" },
+ { CMD_CONF_IMPORT, " <filename>", "Import a config file into the confdb. (*)" },
+ { CMD_CONF_EXPORT, "[<filename>]", "Export the confdb into a config file or stdout. (*)" },
+ { CMD_CONF_LIST, "[<item>...]", "List the confdb sections or section items." },
+ { CMD_CONF_READ, "[<item>...]", "Get the item from the active confdb." },
+ { CMD_CONF_BEGIN, "", "Begin a writing confdb transaction." },
+ { CMD_CONF_COMMIT, "", "Commit the confdb transaction." },
+ { CMD_CONF_ABORT, "", "Rollback the confdb transaction." },
+ { CMD_CONF_DIFF, "[<item>...]", "Get the item difference within the transaction." },
+ { CMD_CONF_GET, "[<item>...]", "Get the item data within the transaction." },
+ { CMD_CONF_SET, " <item> [<data>...]", "Set the item data within the transaction." },
+ { CMD_CONF_UNSET, "[<item>] [<data>...]", "Unset the item data within the transaction." },
+ { NULL }
+};
+
+void print_commands(void)
+{
+ printf("\nActions:\n");
+
+ for (const cmd_help_t *cmd = cmd_help_table; cmd->name != NULL; cmd++) {
+ printf(" %-18s %-38s %s\n", cmd->name, cmd->params, cmd->desc);
+ }
+
+ printf("\n"
+ "Note:\n"
+ " Use @ owner to denote the zone name.\n"
+ " Empty or '--' <zone> parameter means all zones or all zones with a transaction.\n"
+ " Type <item> parameter in the form of <section>[<identifier>].<name>.\n"
+ " (*) indicates a local operation which requires a configuration.\n"
+ " (#) indicates an optionally blocking operation.\n"
+ " The '-b' and '-f' options can be placed right after the command name.\n");
+}
diff --git a/src/utils/knotc/commands.h b/src/utils/knotc/commands.h
new file mode 100644
index 0000000..22c3035
--- /dev/null
+++ b/src/utils/knotc/commands.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "libknot/control/control.h"
+#include "knot/ctl/commands.h"
+
+/*! \brief Command condition flags. */
+typedef enum {
+ CMD_FNONE = 0, /*!< Empty flag. */
+ CMD_FREAD = 1 << 0, /*!< Required read access to config or confdb. */
+ CMD_FWRITE = 1 << 1, /*!< Required write access to confdb. */
+ CMD_FOPT_ITEM = 1 << 2, /*!< Optional item argument. */
+ CMD_FREQ_ITEM = 1 << 3, /*!< Required item argument. */
+ CMD_FOPT_DATA = 1 << 4, /*!< Optional item data argument. */
+ CMD_FOPT_ZONE = 1 << 5, /*!< Optional zone name argument. */
+ CMD_FREQ_ZONE = 1 << 6, /*!< Required zone name argument. */
+ CMD_FREQ_TXN = 1 << 7, /*!< Required open confdb transaction. */
+ CMD_FOPT_MOD = 1 << 8, /*!< Optional configured modules dependency. */
+ CMD_FREQ_MOD = 1 << 9, /*!< Required configured modules dependency. */
+ CMD_FLIST_SCHEMA = 1 << 10, /*!< List schema or possible option values. */
+} cmd_flag_t;
+
+struct cmd_desc;
+typedef struct cmd_desc cmd_desc_t;
+
+/*! \brief Command callback arguments. */
+typedef struct {
+ const cmd_desc_t *desc;
+ knot_ctl_t *ctl;
+ int argc;
+ const char **argv;
+ char flags[4];
+ bool force;
+ bool extended;
+ bool color;
+ bool color_force;
+ bool blocking;
+} cmd_args_t;
+
+/*! \brief Command callback description. */
+struct cmd_desc {
+ const char *name;
+ int (*fcn)(cmd_args_t *);
+ ctl_cmd_t cmd;
+ cmd_flag_t flags;
+};
+
+/*! \brief Command description. */
+typedef struct {
+ const char *name;
+ const char *params;
+ const char *desc;
+} cmd_help_t;
+
+/*! \brief Table of commands. */
+extern const cmd_desc_t cmd_table[];
+
+/*! \brief Prints commands help. */
+void print_commands(void);
diff --git a/src/utils/knotc/interactive.c b/src/utils/knotc/interactive.c
new file mode 100644
index 0000000..a03b8d3
--- /dev/null
+++ b/src/utils/knotc/interactive.c
@@ -0,0 +1,450 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <histedit.h>
+
+#include "knot/common/log.h"
+#include "utils/common/lookup.h"
+#include "utils/knotc/interactive.h"
+#include "utils/knotc/commands.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/string.h"
+
+#define PROGRAM_NAME "knotc"
+#define HISTORY_FILE ".knotc_history"
+
+extern params_t params;
+
+typedef struct {
+ const char **args;
+ int count;
+ bool dname;
+} dup_check_ctx_t;
+
+static void cmds_lookup(EditLine *el, const char *str, size_t str_len)
+{
+ lookup_t lookup;
+ int ret = lookup_init(&lookup);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ // Fill the lookup with command names.
+ for (const cmd_desc_t *desc = cmd_table; desc->name != NULL; desc++) {
+ ret = lookup_insert(&lookup, desc->name, NULL);
+ if (ret != KNOT_EOK) {
+ goto cmds_lookup_finish;
+ }
+ }
+
+ (void)lookup_complete(&lookup, str, str_len, el, true);
+
+cmds_lookup_finish:
+ lookup_deinit(&lookup);
+}
+
+static void remove_duplicates(lookup_t *lookup, dup_check_ctx_t *check_ctx)
+{
+ if (check_ctx == NULL) {
+ return;
+ }
+
+ knot_dname_txt_storage_t dname = "";
+ for (int i = 0; i < check_ctx->count; i++) {
+ const char *arg = (check_ctx->args)[i];
+ size_t len = strlen(arg);
+ if (check_ctx->dname && len > 1 && arg[len - 1] != '.') {
+ strlcat(dname, arg, sizeof(dname));
+ strlcat(dname, ".", sizeof(dname));
+ arg = dname;
+ }
+ (void)lookup_remove(lookup, arg);
+ }
+}
+
+static void local_zones_lookup(EditLine *el, const char *str, size_t str_len,
+ dup_check_ctx_t *check_ctx)
+{
+ lookup_t lookup;
+ int ret = lookup_init(&lookup);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ knot_dname_txt_storage_t buff;
+
+ // Fill the lookup with local zone names.
+ for (conf_iter_t iter = conf_iter(conf(), C_ZONE);
+ iter.code == KNOT_EOK; conf_iter_next(conf(), &iter)) {
+ conf_val_t val = conf_iter_id(conf(), &iter);
+ char *name = knot_dname_to_str(buff, conf_dname(&val), sizeof(buff));
+
+ ret = lookup_insert(&lookup, name, NULL);
+ if (ret != KNOT_EOK) {
+ conf_iter_finish(conf(), &iter);
+ goto local_zones_lookup_finish;
+ }
+ }
+
+ remove_duplicates(&lookup, check_ctx);
+ (void)lookup_complete(&lookup, str, str_len, el, true);
+
+local_zones_lookup_finish:
+ lookup_deinit(&lookup);
+}
+
+static void list_separators(EditLine *el, const char *separators)
+{
+ lookup_t lookup;
+ if (lookup_init(&lookup) != KNOT_EOK) {
+ return;
+ }
+
+ size_t count = strlen(separators);
+ for (int i = 0; i < count; i++) {
+ char sep[2] = { separators[i] };
+ (void)lookup_insert(&lookup, sep, NULL);
+ }
+ (void)lookup_complete(&lookup, "", 0, el, false);
+
+ lookup_deinit(&lookup);
+}
+
+static bool rmt_lookup(EditLine *el, const char *str, size_t str_len,
+ const char *section, const char *item, const char *id,
+ dup_check_ctx_t *check_ctx, bool add_space, const char *flags,
+ knot_ctl_idx_t idx)
+{
+ const cmd_desc_t *desc = cmd_table;
+ while (desc->name != NULL && desc->cmd != CTL_CONF_LIST) {
+ desc++;
+ }
+ assert(desc->name != NULL);
+
+ knot_ctl_data_t query = {
+ [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(desc->cmd),
+ [KNOT_CTL_IDX_SECTION] = section,
+ [KNOT_CTL_IDX_ITEM] = item,
+ [KNOT_CTL_IDX_ID] = id,
+ [KNOT_CTL_IDX_FLAGS] = flags
+ };
+
+ lookup_t lookup;
+ knot_ctl_t *ctl = NULL;
+ bool found = false;
+
+ if (set_ctl(&ctl, params.socket, DEFAULT_CTL_TIMEOUT_MS, desc) != KNOT_EOK ||
+ knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &query) != KNOT_EOK ||
+ knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL) != KNOT_EOK ||
+ lookup_init(&lookup) != KNOT_EOK) {
+ unset_ctl(ctl);
+ return found;
+ }
+
+ while (true) {
+ knot_ctl_type_t type;
+ knot_ctl_data_t reply;
+
+ if (knot_ctl_receive(ctl, &type, &reply) != KNOT_EOK) {
+ goto rmt_lookup_finish;
+ }
+
+ if (type != KNOT_CTL_TYPE_DATA && type != KNOT_CTL_TYPE_EXTRA) {
+ break;
+ }
+
+ const char *error = reply[KNOT_CTL_IDX_ERROR];
+ if (error != NULL) {
+ printf("\nnotice: (%s)\n", error);
+ goto rmt_lookup_finish;
+ }
+
+ // Insert the received name into the lookup.
+ if (lookup_insert(&lookup, reply[idx], NULL) != KNOT_EOK) {
+ goto rmt_lookup_finish;
+ }
+ }
+
+ remove_duplicates(&lookup, check_ctx);
+ if (lookup_complete(&lookup, str, str_len, el, add_space) == KNOT_EOK &&
+ str != NULL && strcmp(lookup.found.key, str) == 0) {
+ found = true;
+ }
+
+rmt_lookup_finish:
+ lookup_deinit(&lookup);
+ unset_ctl(ctl);
+
+ return found;
+}
+
+static bool id_lookup(EditLine *el, const char *str, size_t str_len,
+ const char *section, const cmd_desc_t *cmd_desc,
+ dup_check_ctx_t *ctx, bool add_space, bool zones)
+{
+ char flags[4] = "";
+ if (zones) {
+ strlcat(flags, CTL_FLAG_LIST_ZONES, sizeof(flags));
+ } else if (cmd_desc->flags & CMD_FREQ_TXN) {
+ strlcat(flags, CTL_FLAG_LIST_TXN, sizeof(flags));
+ }
+
+ return rmt_lookup(el, str, str_len, section, NULL, NULL, ctx, add_space,
+ flags, KNOT_CTL_IDX_ID);
+}
+
+static void val_lookup(EditLine *el, const char *str, size_t str_len,
+ const char *section, const char *item, const char *id,
+ dup_check_ctx_t *ctx, bool list_schema)
+{
+ char flags[4] = CTL_FLAG_LIST_TXN;
+ if (list_schema) {
+ strlcat(flags, CTL_FLAG_LIST_SCHEMA, sizeof(flags));
+ }
+
+ (void)rmt_lookup(el, str, str_len, section, item, id, ctx, true,
+ flags, KNOT_CTL_IDX_DATA);
+}
+
+static bool list_lookup(EditLine *el, const char *str, const char *section)
+{
+ const char *flags = CTL_FLAG_LIST_SCHEMA;
+ knot_ctl_idx_t idx = (section == NULL) ? KNOT_CTL_IDX_SECTION : KNOT_CTL_IDX_ITEM;
+
+ return rmt_lookup(el, str, strlen(str), section, NULL, NULL, NULL,
+ section != NULL, flags, idx);
+}
+
+static void item_lookup(EditLine *el, const char *str, const cmd_desc_t *cmd_desc)
+{
+ // List all sections.
+ if (str == NULL) {
+ (void)list_lookup(el, "", NULL);
+ return;
+ }
+
+ // Check for id specification.
+ char *id = (strchr(str, '['));
+ if (id != NULL) {
+ char *section = strndup(str, id - str);
+
+ // Check for completed id specification.
+ char *id_stop = (strchr(id, ']'));
+ if (id_stop != NULL) {
+ // Complete the item name.
+ if (*(id_stop + 1) == '.') {
+ (void)list_lookup(el, id_stop + 2, section);
+ } else {
+ list_separators(el, ".");
+ }
+ } else {
+ // Complete the section id.
+ if (id_lookup(el, id + 1, strlen(id + 1), section, cmd_desc,
+ NULL, false, false)) {
+ list_separators(el, "]");
+ }
+ }
+
+ free(section);
+ } else {
+ // Check for item specification.
+ char *dot = (strchr(str, '.'));
+ if (dot != NULL) {
+ // Complete the item name.
+ char *section = strndup(str, dot - str);
+ (void)list_lookup(el, dot + 1, section);
+ free(section);
+ } else {
+ // Complete the section name.
+ if (list_lookup(el, str, NULL)) {
+ list_separators(el, "[.");
+ }
+ }
+ }
+}
+
+static unsigned char complete(EditLine *el, int ch)
+{
+ int argc, token, pos;
+ const char **argv;
+
+ const LineInfo *li = el_line(el);
+ Tokenizer *tok = tok_init(NULL);
+
+ // Parse the line.
+ int ret = tok_line(tok, li, &argc, &argv, &token, &pos);
+ if (ret != 0) {
+ goto complete_exit;
+ }
+
+ // Show possible commands.
+ if (argc == 0) {
+ print_commands();
+ goto complete_exit;
+ }
+
+ // Complete the command name.
+ if (token == 0) {
+ cmds_lookup(el, argv[0], pos);
+ goto complete_exit;
+ }
+
+ // Find the command descriptor.
+ const cmd_desc_t *desc = cmd_table;
+ while (desc->name != NULL && strcmp(desc->name, argv[0]) != 0) {
+ desc++;
+ }
+ if (desc->name == NULL) {
+ goto complete_exit;
+ }
+
+ // Finish if a command with no or unsupported arguments.
+ if (desc->flags == CMD_FNONE || desc->flags == CMD_FREAD ||
+ desc->flags == CMD_FWRITE) {
+ goto complete_exit;
+ }
+
+ ret = set_config(desc, &params);
+ if (ret != KNOT_EOK) {
+ goto complete_exit;
+ }
+
+ // Complete the zone name.
+ if (desc->flags & (CMD_FREQ_ZONE | CMD_FOPT_ZONE)) {
+ if (token > 1 && !(desc->flags & CMD_FOPT_ZONE)) {
+ goto complete_exit;
+ }
+
+ dup_check_ctx_t ctx = { &argv[1], token - 1, true };
+ if (desc->flags & CMD_FREAD) {
+ local_zones_lookup(el, argv[token], pos, &ctx);
+ } else {
+ id_lookup(el, argv[token], pos, "zone", desc, &ctx, true, true);
+ }
+ goto complete_exit;
+ // Complete the section/id/item name or item value.
+ } else if (desc->flags & (CMD_FOPT_ITEM | CMD_FREQ_ITEM)) {
+ if (token == 1) {
+ item_lookup(el, argv[1], desc);
+ } else if (desc->flags & CMD_FOPT_DATA) {
+ char section[YP_MAX_TXT_KEY_LEN + 1] = "";
+ char item[YP_MAX_TXT_KEY_LEN + 1] = "";
+ char id[KNOT_DNAME_TXT_MAXLEN + 1] = "";
+
+ assert(YP_MAX_TXT_KEY_LEN == 127);
+ assert(KNOT_DNAME_TXT_MAXLEN == 1004);
+ if (sscanf(argv[1], "%127[^[][%1004[^]]].%127s", section, id, item) == 3 ||
+ sscanf(argv[1], "%127[^.].%127s", section, item) == 2) {
+ dup_check_ctx_t ctx = { &argv[2], token - 2 };
+ val_lookup(el, argv[token], pos, section, item, id,
+ &ctx, desc->flags & CMD_FLIST_SCHEMA);
+ }
+ }
+ goto complete_exit;
+ }
+
+complete_exit:
+ conf_update(NULL, CONF_UPD_FNONE);
+ tok_reset(tok);
+ tok_end(tok);
+
+ return CC_REDISPLAY;
+}
+
+static char *prompt(EditLine *el)
+{
+ return PROGRAM_NAME"> ";
+}
+
+int interactive_loop(params_t *process_params)
+{
+ char *hist_file = NULL;
+ const char *home = getenv("HOME");
+ if (home != NULL) {
+ hist_file = sprintf_alloc("%s/%s", home, HISTORY_FILE);
+ }
+ if (hist_file == NULL) {
+ log_notice("failed to get home directory");
+ }
+
+ EditLine *el = el_init(PROGRAM_NAME, stdin, stdout, stderr);
+ if (el == NULL) {
+ log_error("interactive mode not available");
+ free(hist_file);
+ return KNOT_ERROR;
+ }
+
+ History *hist = history_init();
+ if (hist == NULL) {
+ log_error("interactive mode not available");
+ el_end(el);
+ free(hist_file);
+ return KNOT_ERROR;
+ }
+
+ HistEvent hev = { 0 };
+ history(hist, &hev, H_SETSIZE, 1000);
+ history(hist, &hev, H_SETUNIQUE, 1);
+ el_set(el, EL_HIST, history, hist);
+ history(hist, &hev, H_LOAD, hist_file);
+
+ el_set(el, EL_TERMINAL, NULL);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+
+ // Warning: these two el_sets()'s always leak -- in libedit2 library!
+ // For more details see this commit's message.
+ el_set(el, EL_ADDFN, PROGRAM_NAME"-complete",
+ "Perform "PROGRAM_NAME" completion.", complete);
+ el_set(el, EL_BIND, "^I", PROGRAM_NAME"-complete", NULL);
+
+ int count;
+ const char *line;
+ while ((line = el_gets(el, &count)) != NULL && count > 0) {
+ Tokenizer *tok = tok_init(NULL);
+
+ // Tokenize the current line.
+ int argc;
+ const char **argv;
+ const LineInfo *li = el_line(el);
+ int ret = tok_line(tok, li, &argc, &argv, NULL, NULL);
+ if (ret == 0 && argc != 0) {
+ history(hist, &hev, H_ENTER, line);
+ history(hist, &hev, H_SAVE, hist_file);
+
+ // Process the command.
+ ret = process_cmd(argc, argv, process_params);
+ }
+
+ tok_reset(tok);
+ tok_end(tok);
+
+ // Check for the exit command.
+ if (ret == KNOT_CTL_ESTOP) {
+ break;
+ }
+ }
+
+ history_end(hist);
+ free(hist_file);
+
+ el_end(el);
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/knotc/interactive.h b/src/utils/knotc/interactive.h
new file mode 100644
index 0000000..59690c7
--- /dev/null
+++ b/src/utils/knotc/interactive.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "utils/knotc/process.h"
+
+/*!
+ * Executes an interactive processing loop.
+ *
+ * \param[in] params Utility parameters.
+ */
+int interactive_loop(params_t *params);
diff --git a/src/utils/knotc/main.c b/src/utils/knotc/main.c
new file mode 100644
index 0000000..570469b
--- /dev/null
+++ b/src/utils/knotc/main.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "contrib/strtonum.h"
+#include "knot/common/log.h"
+#include "utils/common/params.h"
+#include "utils/knotc/commands.h"
+#include "utils/knotc/interactive.h"
+#include "utils/knotc/process.h"
+
+#define PROGRAM_NAME "knotc"
+#define SPACE " "
+
+static void print_help(void)
+{
+ printf("Usage: %s [parameters] <action> [action_args]\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> "SPACE"Use a textual configuration file.\n"
+ " "SPACE" (default %s)\n"
+ " -C, --confdb <dir> "SPACE"Use a binary configuration database directory.\n"
+ " "SPACE" (default %s)\n"
+ " -m, --max-conf-size <MiB>"SPACE"Set maximum size of the configuration database (max 10000 MiB).\n"
+ " "SPACE" (default %d MiB)\n"
+ " -s, --socket <path> "SPACE"Use a control UNIX socket path.\n"
+ " "SPACE" (default %s)\n"
+ " -t, --timeout <sec> "SPACE"Use a control socket timeout (max 86400 seconds).\n"
+ " "SPACE" (default %u seconds)\n"
+ " -b, --blocking "SPACE"Zone event trigger commands wait until the event is finished.\n"
+ " -e, --extended "SPACE"Show extended output.\n"
+ " -f, --force "SPACE"Forced operation. Overrides some checks.\n"
+ " -x, --mono "SPACE"Don't color the output.\n"
+ " -X, --color "SPACE"Force output colorization.\n"
+ " -v, --verbose "SPACE"Enable debug output.\n"
+ " -h, --help "SPACE"Print the program help.\n"
+ " -V, --version "SPACE"Print the program version.\n",
+ PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR,
+ CONF_MAPSIZE, RUN_DIR "/knot.sock", DEFAULT_CTL_TIMEOUT_MS / 1000);
+
+ print_commands();
+}
+
+params_t params = {
+ .max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024,
+ .timeout = -1
+};
+
+int main(int argc, char **argv)
+{
+ /* Long options. */
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "max-conf-size", required_argument, NULL, 'm' },
+ { "socket", required_argument, NULL, 's' },
+ { "timeout", required_argument, NULL, 't' },
+ { "blocking", no_argument, NULL, 'b' },
+ { "extended", no_argument, NULL, 'e' },
+ { "force", no_argument, NULL, 'f' },
+ { "mono", no_argument, NULL, 'x' },
+ { "color", no_argument, NULL, 'X' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ /* Set the time zone. */
+ tzset();
+
+ params.color = isatty(STDOUT_FILENO);
+ params.color_force = false;
+
+ /* Parse command line arguments */
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "+c:C:m:s:t:befxXvhV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ params.orig_config = optarg;
+ break;
+ case 'C':
+ params.orig_confdb = optarg;
+ break;
+ case 'm':
+ if (str_to_size(optarg, &params.max_conf_size, 1, 10000) != KNOT_EOK) {
+ print_help();
+ return EXIT_FAILURE;
+ }
+ /* Convert to bytes. */
+ params.max_conf_size *= 1024 * 1024;
+ break;
+ case 's':
+ params.socket = optarg;
+ break;
+ case 't':
+ if (str_to_int(optarg, &params.timeout, 0, 86400) != KNOT_EOK) {
+ print_help();
+ return EXIT_FAILURE;
+ }
+ /* Convert to milliseconds. */
+ params.timeout *= 1000;
+ break;
+ case 'b':
+ params.blocking = true;
+ break;
+ case 'e':
+ params.extended = true;
+ break;
+ case 'f':
+ params.force = true;
+ break;
+ case 'v':
+ params.verbose = true;
+ break;
+ case 'x':
+ params.color = false;
+ break;
+ case 'X':
+ params.color = true;
+ params.color_force = true;
+ break;
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ return EXIT_SUCCESS;
+ default:
+ print_help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Set up simplified logging just to stdout/stderr. */
+ log_init();
+ log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY,
+ LOG_MASK(LOG_INFO) | LOG_MASK(LOG_NOTICE));
+ log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, LOG_UPTO(LOG_WARNING));
+ log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0);
+ log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO);
+ if (params.verbose) {
+ log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_MASK(LOG_DEBUG));
+ }
+
+ int ret;
+ if (argc - optind < 1) {
+ ret = interactive_loop(&params);
+ } else {
+ ret = process_cmd(argc - optind, (const char **)argv + optind, &params);
+ }
+
+ log_close();
+
+ return (ret == KNOT_EOK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/utils/knotc/process.c b/src/utils/knotc/process.c
new file mode 100644
index 0000000..0f06891
--- /dev/null
+++ b/src/utils/knotc/process.c
@@ -0,0 +1,291 @@
+/* Copyright (C) 2022 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 <stddef.h>
+#include <sys/stat.h>
+
+#include "contrib/openbsd/strlcat.h"
+#include "knot/conf/conf.h"
+#include "knot/common/log.h"
+#include "utils/knotc/commands.h"
+#include "utils/knotc/process.h"
+
+static const cmd_desc_t *get_cmd_desc(const char *command)
+{
+ /* Find requested command. */
+ const cmd_desc_t *desc = cmd_table;
+ while (desc->name != NULL) {
+ if (strcmp(desc->name, command) == 0) {
+ break;
+ }
+ desc++;
+ }
+ if (desc->name == NULL) {
+ log_error("invalid command '%s'", command);
+ return NULL;
+ }
+
+ return desc;
+}
+
+static bool get_cmd_force_flag(const char *arg)
+{
+ if (strcmp(arg, "-f") == 0 || strcmp(arg, "--force") == 0) {
+ return true;
+ }
+ return false;
+}
+
+static bool get_cmd_blocking_flag(const char *arg)
+{
+ if (strcmp(arg, "-b") == 0 || strcmp(arg, "--blocking") == 0) {
+ return true;
+ }
+ return false;
+}
+
+int set_config(const cmd_desc_t *desc, params_t *params)
+{
+ /* Reset the configuration paths (needed in the interactive mode). */
+ params->config = params->orig_config;
+ params->confdb = params->orig_confdb;
+
+ if (params->config != NULL && params->confdb != NULL) {
+ log_error("ambiguous configuration source");
+ return KNOT_EINVAL;
+ }
+
+ /* Mask relevant flags. */
+ cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE);
+ cmd_flag_t mod_flags = desc->flags & (CMD_FOPT_MOD | CMD_FREQ_MOD);
+
+ /* Choose the optimal config source. */
+ struct stat st;
+ bool import = false;
+ if (flags == CMD_FNONE && params->socket != NULL) {
+ /* Control operation, known socket, skip configuration. */
+ return KNOT_EOK;
+ } else if (params->confdb != NULL) {
+ import = false;
+ } else if (flags == CMD_FWRITE) {
+ import = false;
+ params->confdb = CONF_DEFAULT_DBDIR;
+ } else if (params->config != NULL){
+ import = true;
+ } else if (conf_db_exists(CONF_DEFAULT_DBDIR)) {
+ import = false;
+ params->confdb = CONF_DEFAULT_DBDIR;
+ } else if (stat(CONF_DEFAULT_FILE, &st) == 0) {
+ import = true;
+ params->config = CONF_DEFAULT_FILE;
+ } else if (flags != CMD_FNONE) {
+ log_error("no configuration source available");
+ return KNOT_EINVAL;
+ }
+
+ const char *src = import ? params->config : params->confdb;
+ log_debug("%s '%s'", import ? "config" : "confdb",
+ (src != NULL) ? src : "empty");
+
+ /* Prepare config flags. */
+ conf_flag_t conf_flags = CONF_FNOHOSTNAME;
+ if (params->confdb != NULL && !(flags & CMD_FWRITE)) {
+ conf_flags |= CONF_FREADONLY;
+ }
+ if (import || mod_flags & CMD_FOPT_MOD) {
+ conf_flags |= CONF_FOPTMODULES;
+ } else if (mod_flags & CMD_FREQ_MOD) {
+ conf_flags |= CONF_FREQMODULES;
+ }
+
+ /* Open confdb. */
+ conf_t *new_conf = NULL;
+ int ret = conf_new(&new_conf, conf_schema, params->confdb,
+ params->max_conf_size, conf_flags);
+ if (ret != KNOT_EOK) {
+ log_error("failed to open configuration database '%s' (%s)",
+ (params->confdb != NULL) ? params->confdb : "",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ /* Import the config file. */
+ if (import) {
+ ret = conf_import(new_conf, params->config, true, false);
+ if (ret != KNOT_EOK) {
+ log_error("failed to load configuration file '%s' (%s)",
+ params->config, knot_strerror(ret));
+ conf_free(new_conf);
+ return ret;
+ }
+ }
+
+ /* Update to the new config. */
+ conf_update(new_conf, CONF_UPD_FNONE);
+
+ return KNOT_EOK;
+}
+
+int set_ctl(knot_ctl_t **ctl, const char *socket, int timeout, const cmd_desc_t *desc)
+{
+ if (desc == NULL) {
+ *ctl = NULL;
+ return KNOT_EINVAL;
+ }
+
+ /* Mask relevant flags. */
+ cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE);
+
+ /* Check if control socket is required. */
+ if (flags != CMD_FNONE) {
+ *ctl = NULL;
+ return KNOT_EOK;
+ }
+
+ /* Get control socket path. */
+ char *path = NULL;
+ if (socket != NULL) {
+ path = strdup(socket);
+ } else {
+ conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN);
+ conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&rundir_val, NULL);
+ path = conf_abs_path(&listen_val, rundir);
+ free(rundir);
+ }
+ if (path == NULL) {
+ log_error("empty control socket path");
+ return KNOT_EINVAL;
+ }
+
+ log_debug("socket '%s'", path);
+
+ *ctl = knot_ctl_alloc();
+ if (*ctl == NULL) {
+ free(path);
+ return KNOT_ENOMEM;
+ }
+
+ knot_ctl_set_timeout(*ctl, timeout);
+
+ int ret = knot_ctl_connect(*ctl, path);
+ if (ret != KNOT_EOK) {
+ knot_ctl_free(*ctl);
+ *ctl = NULL;
+ log_error("failed to connect to socket '%s' (%s)", path,
+ knot_strerror(ret));
+ free(path);
+ return ret;
+ }
+
+ free(path);
+
+ return KNOT_EOK;
+}
+
+void unset_ctl(knot_ctl_t *ctl)
+{
+ if (ctl == NULL) {
+ return;
+ }
+
+ int ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ if (ret != KNOT_EOK && ret != KNOT_ECONN) {
+ log_error("failed to finish control (%s)", knot_strerror(ret));
+ }
+
+ knot_ctl_close(ctl);
+ knot_ctl_free(ctl);
+}
+
+int process_cmd(int argc, const char **argv, params_t *params)
+{
+ if (argc == 0) {
+ return KNOT_ENOTSUP;
+ }
+
+ /* Check the command name. */
+ const cmd_desc_t *desc = get_cmd_desc(argv[0]);
+ if (desc == NULL) {
+ return KNOT_ENOENT;
+ }
+
+ /* Check for exit. */
+ if (desc->fcn == NULL) {
+ return KNOT_CTL_ESTOP;
+ }
+
+ /* Set up the configuration. */
+ int ret = set_config(desc, params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Prepare command parameters. */
+ cmd_args_t args = {
+ .desc = desc,
+ .argc = argc - 1,
+ .argv = argv + 1,
+ .force = params->force,
+ .extended = params->extended,
+ .color = params->color,
+ .color_force = params->color_force,
+ .blocking = params->blocking
+ };
+
+ /* Check for special flags after command. */
+ while (args.argc > 0) {
+ if (get_cmd_force_flag(args.argv[0])) {
+ args.force = true;
+ args.argc--;
+ args.argv++;
+ } else if (get_cmd_blocking_flag(args.argv[0])) {
+ args.blocking = true;
+ args.argc--;
+ args.argv++;
+ } else {
+ break;
+ }
+ }
+
+ /* Prepare flags parameter. */
+ if (args.force) {
+ strlcat(args.flags, CTL_FLAG_FORCE, sizeof(args.flags));
+ }
+ if (args.blocking) {
+ strlcat(args.flags, CTL_FLAG_BLOCKING, sizeof(args.flags));
+ }
+
+ /* Set control interface if necessary. */
+ int cmd_timeout = params->timeout != -1 ? params->timeout : DEFAULT_CTL_TIMEOUT_MS;
+ if (args.blocking && params->timeout == -1) {
+ cmd_timeout = 0;
+ }
+ ret = set_ctl(&args.ctl, params->socket, cmd_timeout, desc);
+ if (ret != KNOT_EOK) {
+ conf_update(NULL, CONF_UPD_FNONE);
+ return ret;
+ }
+
+ /* Execute the command. */
+ ret = desc->fcn(&args);
+
+ /* Cleanup */
+ unset_ctl(args.ctl);
+ conf_update(NULL, CONF_UPD_FNONE);
+
+ return ret;
+}
diff --git a/src/utils/knotc/process.h b/src/utils/knotc/process.h
new file mode 100644
index 0000000..3946131
--- /dev/null
+++ b/src/utils/knotc/process.h
@@ -0,0 +1,78 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "utils/knotc/commands.h"
+
+#define DEFAULT_CTL_TIMEOUT_MS (60 * 1000)
+
+/*! Utility command line parameters. */
+typedef struct {
+ const char *orig_config;
+ const char *orig_confdb;
+ const char *config;
+ const char *confdb;
+ size_t max_conf_size;
+ const char *socket;
+ bool verbose;
+ bool extended;
+ bool force;
+ bool blocking;
+ int timeout;
+ bool color;
+ bool color_force;
+} params_t;
+
+/*!
+ * Prepares a proper configuration according to the specified command.
+ *
+ * \param[in] desc Utility command descriptor.
+ * \param[in] params Utility parameters.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int set_config(const cmd_desc_t *desc, params_t *params);
+
+/*!
+ * Establishes a control interface if necessary.
+ *
+ * \param[in] ctl Control context.
+ * \param[in] socket Control socket path.
+ * \param[in] timeout Control socket timeout.
+ * \param[in] desc Utility command descriptor.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int set_ctl(knot_ctl_t **ctl, const char *socket, int timeout, const cmd_desc_t *desc);
+
+/*!
+ * Cleans up the control context.
+ *
+ * \param[in] ctl Control context.
+ */
+void unset_ctl(knot_ctl_t *ctl);
+
+/*!
+ * Processes the given utility command.
+ *
+ * \param[in] argc Number of command arguments.
+ * \param[in] argv Command arguments.
+ * \param[in] params Utility parameters.
+ *
+ * \return Error code, KNOT_EOK if successful.
+ */
+int process_cmd(int argc, const char **argv, params_t *params);
diff --git a/src/utils/knotd/main.c b/src/utils/knotd/main.c
new file mode 100644
index 0000000..3499028
--- /dev/null
+++ b/src/utils/knotd/main.c
@@ -0,0 +1,635 @@
+/* Copyright (C) 2022 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 <dirent.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <urcu.h>
+
+#ifdef ENABLE_CAP_NG
+#include <cap-ng.h>
+#endif
+
+#include "libdnssec/crypto.h"
+#include "libknot/libknot.h"
+#include "contrib/strtonum.h"
+#include "knot/ctl/process.h"
+#include "knot/conf/conf.h"
+#include "knot/conf/migration.h"
+#include "knot/conf/module.h"
+#include "knot/common/log.h"
+#include "knot/common/process.h"
+#include "knot/common/stats.h"
+#include "knot/common/systemd.h"
+#include "knot/server/server.h"
+#include "knot/server/tcp-handler.h"
+
+#define PROGRAM_NAME "knotd"
+
+/* Signal flags. */
+static volatile bool sig_req_stop = false;
+static volatile bool sig_req_reload = false;
+static volatile bool sig_req_zones_reload = false;
+
+static int make_daemon(int nochdir, int noclose)
+{
+ int ret;
+
+ switch (fork()) {
+ case -1:
+ /* Error */
+ return -1;
+ case 0:
+ /* Forked */
+ break;
+ default:
+ /* Exit the main process */
+ _exit(0);
+ }
+
+ if (setsid() == -1) {
+ return -1;
+ }
+
+ if (!nochdir) {
+ ret = chdir("/");
+ if (ret == -1)
+ return errno;
+ }
+
+ if (!noclose) {
+ ret = close(STDIN_FILENO);
+ ret += close(STDOUT_FILENO);
+ ret += close(STDERR_FILENO);
+ if (ret < 0) {
+ return errno;
+ }
+
+ int fd = open("/dev/null", O_RDWR);
+ if (fd == -1) {
+ return errno;
+ }
+
+ if (dup2(fd, STDIN_FILENO) < 0) {
+ close(fd);
+ return errno;
+ }
+ if (dup2(fd, STDOUT_FILENO) < 0) {
+ close(fd);
+ return errno;
+ }
+ if (dup2(fd, STDERR_FILENO) < 0) {
+ close(fd);
+ return errno;
+ }
+ close(fd);
+ }
+
+ return 0;
+}
+
+struct signal {
+ int signum;
+ bool handle;
+};
+
+/*! \brief Signals used by the server. */
+static const struct signal SIGNALS[] = {
+ { SIGHUP, true }, /* Reload server. */
+ { SIGUSR1, true }, /* Reload zones. */
+ { SIGINT, true }, /* Terminate server. */
+ { SIGTERM, true }, /* Terminate server. */
+ { SIGALRM, false }, /* Internal thread synchronization. */
+ { SIGPIPE, false }, /* Ignored. Some I/O errors. */
+ { 0 }
+};
+
+/*! \brief Server signal handler. */
+static void handle_signal(int signum)
+{
+ switch (signum) {
+ case SIGHUP:
+ sig_req_reload = true;
+ break;
+ case SIGUSR1:
+ sig_req_zones_reload = true;
+ break;
+ case SIGINT:
+ case SIGTERM:
+ if (sig_req_stop) {
+ exit(EXIT_FAILURE);
+ }
+ sig_req_stop = true;
+ break;
+ default:
+ /* ignore */
+ break;
+ }
+}
+
+/*! \brief Setup signal handlers and blocking mask. */
+static void setup_signals(void)
+{
+ /* Block all signals. */
+ static sigset_t all;
+ sigfillset(&all);
+ sigdelset(&all, SIGPROF);
+ sigdelset(&all, SIGQUIT);
+ sigdelset(&all, SIGILL);
+ sigdelset(&all, SIGABRT);
+ sigdelset(&all, SIGBUS);
+ sigdelset(&all, SIGFPE);
+ sigdelset(&all, SIGSEGV);
+ pthread_sigmask(SIG_SETMASK, &all, NULL);
+
+ /* Setup handlers. */
+ struct sigaction action = { .sa_handler = handle_signal };
+ for (const struct signal *s = SIGNALS; s->signum > 0; s++) {
+ sigaction(s->signum, &action, NULL);
+ }
+}
+
+/*! \brief Unblock server control signals. */
+static void enable_signals(void)
+{
+ sigset_t mask;
+ sigemptyset(&mask);
+
+ for (const struct signal *s = SIGNALS; s->signum > 0; s++) {
+ if (s->handle) {
+ sigaddset(&mask, s->signum);
+ }
+ }
+
+ pthread_sigmask(SIG_UNBLOCK, &mask, NULL);
+}
+
+/*! \brief Drop POSIX 1003.1e capabilities. */
+static void drop_capabilities(void)
+{
+#ifdef ENABLE_CAP_NG
+ /* Drop all capabilities. */
+ if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
+ capng_clear(CAPNG_SELECT_BOTH);
+
+ /* Apply. */
+ if (capng_apply(CAPNG_SELECT_BOTH) < 0) {
+ log_error("failed to set process capabilities (%s)",
+ strerror(errno));
+ }
+ } else {
+ log_info("process not allowed to set capabilities, skipping");
+ }
+#endif /* ENABLE_CAP_NG */
+}
+
+/*! \brief Event loop listening for signals and remote commands. */
+static void event_loop(server_t *server, const char *socket)
+{
+ knot_ctl_t *ctl = knot_ctl_alloc();
+ if (ctl == NULL) {
+ log_fatal("control, failed to initialize (%s)",
+ knot_strerror(KNOT_ENOMEM));
+ return;
+ }
+
+ // Set control timeout.
+ knot_ctl_set_timeout(ctl, conf()->cache.ctl_timeout);
+
+ /* Get control socket configuration. */
+ char *listen;
+ if (socket == NULL) {
+ conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN);
+ conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR);
+ char *rundir = conf_abs_path(&rundir_val, NULL);
+ listen = conf_abs_path(&listen_val, rundir);
+ free(rundir);
+ } else {
+ listen = strdup(socket);
+ }
+ if (listen == NULL) {
+ knot_ctl_free(ctl);
+ log_fatal("control, empty socket path");
+ return;
+ }
+
+ log_info("control, binding to '%s'", listen);
+
+ /* Bind the control socket. */
+ int ret = knot_ctl_bind(ctl, listen);
+ if (ret != KNOT_EOK) {
+ knot_ctl_free(ctl);
+ log_fatal("control, failed to bind socket '%s' (%s)",
+ listen, knot_strerror(ret));
+ free(listen);
+ return;
+ }
+ free(listen);
+
+ enable_signals();
+
+ /* Notify systemd about successful start. */
+ systemd_ready_notify();
+ if (conf()->cache.srv_dbus_event & DBUS_EVENT_RUNNING) {
+ systemd_emit_running(true);
+ }
+
+ /* Run event loop. */
+ for (;;) {
+ /* Interrupts. */
+ if (sig_req_reload && !sig_req_stop) {
+ sig_req_reload = false;
+ server_reload(server, RELOAD_FULL);
+ }
+ if (sig_req_zones_reload && !sig_req_stop) {
+ sig_req_zones_reload = false;
+ reload_t mode = server->catalog_upd_signal ? RELOAD_CATALOG : RELOAD_ZONES;
+ server->catalog_upd_signal = false;
+ server_update_zones(conf(), server, mode);
+ }
+ if (sig_req_stop) {
+ break;
+ }
+
+ // Update control timeout.
+ knot_ctl_set_timeout(ctl, conf()->cache.ctl_timeout);
+
+ if (sig_req_reload || sig_req_zones_reload) {
+ continue;
+ }
+
+ ret = knot_ctl_accept(ctl);
+ if (ret != KNOT_EOK) {
+ continue;
+ }
+
+ ret = ctl_process(ctl, server);
+ knot_ctl_close(ctl);
+ if (ret == KNOT_CTL_ESTOP) {
+ break;
+ }
+ }
+
+ if (conf()->cache.srv_dbus_event & DBUS_EVENT_RUNNING) {
+ systemd_emit_running(false);
+ }
+
+ /* Unbind the control socket. */
+ knot_ctl_unbind(ctl);
+ knot_ctl_free(ctl);
+}
+
+static void print_help(void)
+{
+ printf("Usage: %s [parameters]\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> Use a textual configuration file.\n"
+ " (default %s)\n"
+ " -C, --confdb <dir> Use a binary configuration database directory.\n"
+ " (default %s)\n"
+ " -m, --max-conf-size <MiB> Set maximum size of the configuration database (max 10000 MiB).\n"
+ " (default %d MiB)\n"
+ " -s, --socket <path> Use a remote control UNIX socket path.\n"
+ " (default %s)\n"
+ " -d, --daemonize=[dir] Run the server as a daemon (with new root directory).\n"
+ " -v, --verbose Enable debug output.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n",
+ PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR,
+ CONF_MAPSIZE, RUN_DIR "/knot.sock");
+}
+
+static void print_version(void)
+{
+ printf("%s (Knot DNS), version %s\n", PROGRAM_NAME, PACKAGE_VERSION);
+}
+
+static int set_config(const char *confdb, const char *config, size_t max_conf_size)
+{
+ if (config != NULL && confdb != NULL) {
+ log_fatal("ambiguous configuration source");
+ return KNOT_EINVAL;
+ }
+
+ /* Choose the optimal config source. */
+ bool import = false;
+ if (confdb != NULL) {
+ import = false;
+ } else if (config != NULL){
+ import = true;
+ } else if (conf_db_exists(CONF_DEFAULT_DBDIR)) {
+ import = false;
+ confdb = CONF_DEFAULT_DBDIR;
+ } else {
+ import = true;
+ config = CONF_DEFAULT_FILE;
+ }
+
+ /* Open confdb. */
+ conf_t *new_conf = NULL;
+ int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, CONF_FREQMODULES);
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to open configuration database '%s' (%s)",
+ (confdb != NULL) ? confdb : "", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Import the config file. */
+ if (import) {
+ ret = conf_import(new_conf, config, true, true);
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to load configuration file '%s' (%s)",
+ config, knot_strerror(ret));
+ conf_free(new_conf);
+ return ret;
+ }
+ }
+
+ // Migrate from old schema.
+ ret = conf_migrate(new_conf);
+ if (ret != KNOT_EOK) {
+ log_error("failed to migrate configuration (%s)", knot_strerror(ret));
+ }
+
+ /* Update to the new config. */
+ conf_update(new_conf, CONF_UPD_FNONE);
+
+ return KNOT_EOK;
+}
+
+int main(int argc, char **argv)
+{
+ bool daemonize = false;
+ const char *config = NULL;
+ const char *confdb = NULL;
+ size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024;
+ const char *daemon_root = "/";
+ char *socket = NULL;
+ bool verbose = false;
+
+ /* Long options. */
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "max-conf-size", required_argument, NULL, 'm' },
+ { "socket", required_argument, NULL, 's' },
+ { "daemonize", optional_argument, NULL, 'd' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ /* Set the time zone. */
+ tzset();
+
+ /* Parse command line arguments. */
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "c:C:m:s:dvhV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ config = optarg;
+ break;
+ case 'C':
+ confdb = optarg;
+ break;
+ case 'm':
+ if (str_to_size(optarg, &max_conf_size, 1, 10000) != KNOT_EOK) {
+ print_help();
+ return EXIT_FAILURE;
+ }
+ /* Convert to bytes. */
+ max_conf_size *= 1024 * 1024;
+ break;
+ case 's':
+ socket = optarg;
+ break;
+ case 'd':
+ daemonize = true;
+ if (optarg) {
+ daemon_root = optarg;
+ }
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'V':
+ print_version();
+ return EXIT_SUCCESS;
+ default:
+ print_help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Check for non-option parameters. */
+ if (argc - optind > 0) {
+ print_help();
+ return EXIT_FAILURE;
+ }
+
+ /* Set file creation mask to remove all permissions for others. */
+ umask(S_IROTH|S_IWOTH|S_IXOTH);
+
+ /* Now check if we want to daemonize. */
+ if (daemonize) {
+ if (make_daemon(1, 0) != 0) {
+ fprintf(stderr, "Daemonization failed, shutting down...\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Setup base signal handling. */
+ setup_signals();
+
+ /* Initialize cryptographic backend. */
+ dnssec_crypto_init();
+
+ /* Initialize pseudorandom number generator. */
+ srand(time(NULL));
+
+ /* Initialize logging subsystem. */
+ log_init();
+ if (verbose) {
+ log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_MASK(LOG_DEBUG));
+ }
+
+ /* Set up the configuration */
+ int ret = set_config(confdb, config, max_conf_size);
+ if (ret != KNOT_EOK) {
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ /* Reconfigure logging. */
+ log_reconfigure(conf());
+
+ /* Initialize server. */
+ server_t server;
+ ret = server_init(&server, conf()->cache.srv_bg_threads);
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to initialize server (%s)", knot_strerror(ret));
+ conf_free(conf());
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ /* Reconfigure server workers, interfaces, and databases.
+ * @note This MUST be done before we drop privileges. */
+ ret = server_reconfigure(conf(), &server);
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to configure server");
+ server_wait(&server);
+ server_deinit(&server);
+ conf_free(conf());
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ if (conf()->cache.srv_dbus_event != DBUS_EVENT_NONE) {
+ ret = systemd_dbus_open();
+ if (ret != KNOT_EOK) {
+ log_error("d-bus: failed to open system bus (%s)",
+ knot_strerror(ret));
+ } else {
+ log_info("d-bus: connected to system bus");
+ }
+ int64_t delay = conf_get_int(conf(), C_SRV, C_DBUS_INIT_DELAY);
+ sleep(delay);
+ }
+
+ /* Alter privileges. */
+ int uid, gid;
+ if (conf_user(conf(), &uid, &gid) != KNOT_EOK ||
+ log_update_privileges(uid, gid) != KNOT_EOK ||
+ proc_update_privileges(uid, gid) != KNOT_EOK) {
+ log_fatal("failed to drop privileges");
+ server_wait(&server);
+ server_deinit(&server);
+ conf_free(conf());
+ systemd_dbus_close();
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ /* Drop POSIX capabilities. */
+ drop_capabilities();
+
+ /* Activate global query modules. */
+ conf_activate_modules(conf(), &server, NULL, conf()->query_modules,
+ &conf()->query_plan);
+
+ /* Check and create PID file. */
+ unsigned long pid = pid_check_and_create();
+ if (pid == 0) {
+ server_wait(&server);
+ server_deinit(&server);
+ conf_free(conf());
+ systemd_dbus_close();
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ if (daemonize) {
+ if (chdir(daemon_root) != 0) {
+ log_warning("failed to change working directory to %s",
+ daemon_root);
+ } else {
+ log_info("changed directory to %s", daemon_root);
+ }
+ }
+
+ /* Now we're going multithreaded. */
+ rcu_register_thread();
+
+ /* Populate zone database. */
+ log_info("loading %zu zones", conf_id_count(conf(), C_ZONE));
+ server_update_zones(conf(), &server, RELOAD_ZONES);
+
+ /* Check number of loaded zones. */
+ if (knot_zonedb_size(server.zone_db) == 0) {
+ log_warning("no zones loaded");
+ }
+
+ stats_reconfigure(conf(), &server);
+
+ /* Start it up. */
+ log_info("starting server");
+ conf_val_t async_val = conf_get(conf(), C_SRV, C_ASYNC_START);
+ ret = server_start(&server, conf_bool(&async_val));
+ if (ret != KNOT_EOK) {
+ log_fatal("failed to start server (%s)", knot_strerror(ret));
+ server_wait(&server);
+ stats_deinit();
+ server_deinit(&server);
+ rcu_unregister_thread();
+ pid_cleanup();
+ conf_free(conf());
+ systemd_dbus_close();
+ log_close();
+ dnssec_crypto_cleanup();
+ return EXIT_FAILURE;
+ }
+
+ if (daemonize) {
+ log_info("server started as a daemon, PID %lu", pid);
+ } else {
+ log_info("server started in the foreground, PID %lu", pid);
+ }
+
+ /* Start the event loop. */
+ event_loop(&server, socket);
+
+ /* Teardown server. */
+ server_stop(&server);
+ server_wait(&server);
+ stats_deinit();
+
+ /* Cleanup PID file. */
+ pid_cleanup();
+
+ /* Free server and configuration. */
+ server_deinit(&server);
+ conf_free(conf());
+
+ /* Unhook from RCU. */
+ rcu_unregister_thread();
+
+ systemd_dbus_close();
+
+ log_info("shutting down");
+ log_close();
+
+ dnssec_crypto_cleanup();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/utils/knsec3hash/knsec3hash.c b/src/utils/knsec3hash/knsec3hash.c
new file mode 100644
index 0000000..a7bac97
--- /dev/null
+++ b/src/utils/knsec3hash/knsec3hash.c
@@ -0,0 +1,187 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "contrib/base32hex.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "libdnssec/error.h"
+#include "libdnssec/nsec.h"
+#include "libknot/libknot.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+
+#define PROGRAM_NAME "knsec3hash"
+
+/*!
+ * \brief Print program help (and example).
+ */
+static void print_help(void)
+{
+ printf("Usage: " PROGRAM_NAME " <salt> <algorithm> <iterations> <domain-name>\n");
+ printf("Example: " PROGRAM_NAME " c01dcafe 1 10 knot-dns.cz\n");
+ printf("Alternative usage: "PROGRAM_NAME " <algorithm> <flags> <iterations> <salt> <domain-name>\n");
+ printf("Example: " PROGRAM_NAME " 1 0 10 c01dcafe knot-dns.cz\n");
+}
+
+/*!
+ * \brief Parse NSEC3 salt.
+ */
+static int str_to_salt(const char *str, dnssec_binary_t *salt)
+{
+ if (strcmp(str, "-") == 0) {
+ salt->size = 0;
+ return DNSSEC_EOK;
+ } else {
+ salt->data = hex_to_bin(str, &salt->size);
+ return (salt->data != NULL ? DNSSEC_EOK : DNSSEC_EINVAL);
+ }
+}
+
+/*!
+ * \brief Parse NSEC3 parameters and fill structure with NSEC3 parameters.
+ */
+static bool parse_nsec3_params(dnssec_nsec3_params_t *params, const char *salt_str,
+ const char *algorithm_str, const char *iterations_str)
+{
+ uint8_t algorithm = 0;
+ int r = str_to_u8(algorithm_str, &algorithm);
+ if (r != KNOT_EOK) {
+ ERR2("invalid algorithm number");
+ return false;
+ }
+
+ uint16_t iterations = 0;
+ r = str_to_u16(iterations_str, &iterations);
+ if (r != KNOT_EOK) {
+ ERR2("invalid iteration count");
+ return false;
+ }
+
+ dnssec_binary_t salt = { 0 };
+ r = str_to_salt(salt_str, &salt);
+ if (r != DNSSEC_EOK) {
+ ERR2("invalid salt (%s)", knot_strerror(r));
+ return false;
+ }
+
+ if (salt.size > UINT8_MAX) {
+ ERR2("invalid salt, maximum length is %d bytes", UINT8_MAX);
+ dnssec_binary_free(&salt);
+ return false;
+ }
+
+ params->algorithm = algorithm;
+ params->iterations = iterations;
+ params->salt = salt;
+ params->flags = 0;
+
+ return true;
+}
+
+/*!
+ * \brief Entry point of 'knsec3hash'.
+ */
+int main(int argc, char *argv[])
+{
+ struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "hV", options, NULL)) != -1) {
+ switch(opt) {
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ return EXIT_SUCCESS;
+ default:
+ print_help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ bool new_params = false;
+ if (argc == 6) {
+ // knsec3hash <algorithm> <flags> <iterations> <salt> <domain>
+ new_params = true;
+ } else if (argc != 5) {
+ // knsec3hash <salt> <algorithm> <iterations> <domain>
+ print_help();
+ return EXIT_FAILURE;
+ }
+
+ int exit_code = EXIT_FAILURE;
+ dnssec_nsec3_params_t nsec3_params = { 0 };
+
+ dnssec_binary_t dname = { 0 };
+ dnssec_binary_t digest = { 0 };
+ dnssec_binary_t digest_print = { 0 };
+
+ if (new_params) {
+ if (!parse_nsec3_params(&nsec3_params, argv[4], argv[1], argv[3])) {
+ goto fail;
+ }
+ } else {
+ if (!parse_nsec3_params(&nsec3_params, argv[1], argv[2], argv[3])) {
+ goto fail;
+ }
+ }
+
+ dname.data = knot_dname_from_str_alloc(argv[new_params ? 5 : 4]);
+ if (dname.data == NULL) {
+ ERR2("cannot parse domain name");
+ goto fail;
+ }
+ knot_dname_to_lower(dname.data);
+ dname.size = knot_dname_size(dname.data);
+
+ int r = dnssec_nsec3_hash(&dname, &nsec3_params, &digest);
+ if (r != DNSSEC_EOK) {
+ ERR2("cannot compute NSEC3 hash (%s)", knot_strerror(r));
+ goto fail;
+ }
+
+ r = knot_base32hex_encode_alloc(digest.data, digest.size, &digest_print.data);
+ if (r < 0) {
+ ERR2("cannot encode computed hash (%s)", knot_strerror(r));
+ goto fail;
+ }
+ digest_print.size = r;
+
+ exit_code = EXIT_SUCCESS;
+
+ printf("%.*s (salt=%s, hash=%d, iterations=%d)\n", (int)digest_print.size,
+ digest_print.data, argv[1], nsec3_params.algorithm,
+ nsec3_params.iterations);
+
+fail:
+ dnssec_nsec3_params_free(&nsec3_params);
+ dnssec_binary_free(&dname);
+ dnssec_binary_free(&digest);
+ dnssec_binary_free(&digest_print);
+
+ return exit_code;
+}
diff --git a/src/utils/knsupdate/knsupdate_exec.c b/src/utils/knsupdate/knsupdate_exec.c
new file mode 100644
index 0000000..f2a432a
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_exec.c
@@ -0,0 +1,1053 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "libdnssec/random.h"
+#include "utils/knsupdate/knsupdate_exec.h"
+#include "utils/knsupdate/knsupdate_interactive.h"
+#include "utils/common/exec.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "utils/common/params.h"
+#include "utils/common/sign.h"
+#include "utils/common/token.h"
+#include "libknot/libknot.h"
+#include "contrib/ctype.h"
+#include "contrib/getline.h"
+#include "contrib/string.h"
+#include "contrib/strtonum.h"
+#include "contrib/openbsd/strlcpy.h"
+
+/* Declarations of cmd parse functions. */
+typedef int (*cmd_handle_f)(const char *lp, knsupdate_params_t *params);
+int cmd_add(const char* lp, knsupdate_params_t *params);
+int cmd_answer(const char* lp, knsupdate_params_t *params);
+int cmd_class(const char* lp, knsupdate_params_t *params);
+int cmd_debug(const char* lp, knsupdate_params_t *params);
+int cmd_del(const char* lp, knsupdate_params_t *params);
+int cmd_gsstsig(const char* lp, knsupdate_params_t *params);
+int cmd_key(const char* lp, knsupdate_params_t *params);
+int cmd_local(const char* lp, knsupdate_params_t *params);
+int cmd_nxdomain(const char *lp, knsupdate_params_t *params);
+int cmd_nxrrset(const char *lp, knsupdate_params_t *params);
+int cmd_oldgsstsig(const char* lp, knsupdate_params_t *params);
+int cmd_origin(const char* lp, knsupdate_params_t *params);
+int cmd_prereq(const char* lp, knsupdate_params_t *params);
+int cmd_exit(const char* lp, knsupdate_params_t *params);
+int cmd_realm(const char* lp, knsupdate_params_t *params);
+int cmd_send(const char* lp, knsupdate_params_t *params);
+int cmd_server(const char* lp, knsupdate_params_t *params);
+int cmd_show(const char* lp, knsupdate_params_t *params);
+int cmd_ttl(const char* lp, knsupdate_params_t *params);
+int cmd_update(const char* lp, knsupdate_params_t *params);
+int cmd_yxdomain(const char *lp, knsupdate_params_t *params);
+int cmd_yxrrset(const char *lp, knsupdate_params_t *params);
+int cmd_zone(const char* lp, knsupdate_params_t *params);
+
+/* Sorted list of commands.
+ * This way we could identify command byte-per-byte and
+ * cancel early if the next is lexicographically greater.
+ */
+const char* knsupdate_cmd_array[] = {
+ "\x3" "add",
+ "\x6" "answer",
+ "\x5" "class", /* {classname} */
+ "\x5" "debug",
+ "\x3" "del",
+ "\x6" "delete",
+ "\x4" "exit",
+ "\x7" "gsstsig",
+ "\x3" "key", /* {[alg:]name} {secret} */
+ "\x5" "local", /* {address} [port] */
+ "\x8" "nxdomain",
+ "\x7" "nxrrset",
+ "\xa" "oldgsstsig",
+ "\x6" "origin", /* {name} */
+ "\x6" "prereq", /* (nx|yx)(domain|rrset) {domain-name} ... */
+ "\x4" "quit",
+ "\x5" "realm", /* {[realm_name]} */
+ "\x4" "send",
+ "\x6" "server", /* {servername} [port] */
+ "\x4" "show",
+ "\x3" "ttl", /* {seconds} */
+ "\x6" "update", /* (add|delete) {domain-name} ... */
+ "\x8" "yxdomain",
+ "\x7" "yxrrset",
+ "\x4" "zone", /* {zonename} */
+ NULL
+};
+
+cmd_handle_f cmd_handle[] = {
+ cmd_add,
+ cmd_answer,
+ cmd_class,
+ cmd_debug,
+ cmd_del,
+ cmd_del, /* delete/del synonyms */
+ cmd_exit,
+ cmd_gsstsig,
+ cmd_key,
+ cmd_local,
+ cmd_nxdomain,
+ cmd_nxrrset,
+ cmd_oldgsstsig,
+ cmd_origin,
+ cmd_prereq,
+ cmd_exit, /* exit/quit synonyms */
+ cmd_realm,
+ cmd_send,
+ cmd_server,
+ cmd_show,
+ cmd_ttl,
+ cmd_update,
+ cmd_yxdomain,
+ cmd_yxrrset,
+ cmd_zone,
+};
+
+/* {prereq} command table. */
+const char* pq_array[] = {
+ "\x8" "nxdomain",
+ "\x7" "nxrrset",
+ "\x8" "yxdomain",
+ "\x7" "yxrrset",
+ NULL
+};
+
+enum {
+ PQ_NXDOMAIN = 0,
+ PQ_NXRRSET,
+ PQ_YXDOMAIN,
+ PQ_YXRRSET
+};
+
+/* RR parser flags */
+enum {
+ PARSE_NODEFAULT = 1 << 0, /* Do not fill defaults. */
+ PARSE_NAMEONLY = 1 << 1, /* Parse only name. */
+ PARSE_NOTTL = 1 << 2 /* Ignore TTL item. */
+};
+
+static bool dname_isvalid(const char *lp)
+{
+ knot_dname_t *dn = knot_dname_from_str_alloc(lp);
+ if (dn == NULL) {
+ return false;
+ }
+ knot_dname_free(dn, NULL);
+ return true;
+}
+
+/* This is probably redundant, but should be a bit faster so let's keep it. */
+static int parse_full_rr(zs_scanner_t *s, const char* lp)
+{
+ if (zs_set_input_string(s, lp, strlen(lp)) != 0 ||
+ zs_parse_all(s) != 0) {
+ ERR("invalid record (%s)", zs_strerror(s->error.code));
+ return KNOT_EPARSEFAIL;
+ }
+
+ /* Class must not differ from specified. */
+ if (s->r_class != s->default_class) {
+ char cls_s[16] = "";
+ knot_rrclass_to_string(s->default_class, cls_s, sizeof(cls_s));
+ ERR("class mismatch '%s'", cls_s);
+ return KNOT_EPARSEFAIL;
+ }
+
+ return KNOT_EOK;
+}
+
+static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags)
+{
+ /* Extract owner. */
+ size_t len = strcspn(lp, SEP_CHARS);
+ char *owner_str = calloc(1, len + 2); // 2 ~ ('.' + '\0')
+ memcpy(owner_str, lp, len);
+
+ /* Make dname FQDN if it isn't. */
+ bool fqdn = true;
+ if (owner_str[len - 1] != '.') {
+ owner_str[len] = '.';
+ fqdn = false;
+ }
+
+ knot_dname_t *owner = knot_dname_from_str_alloc(owner_str);
+ free(owner_str);
+ if (owner == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ s->r_owner_length = knot_dname_size(owner);
+ memcpy(s->r_owner, owner, s->r_owner_length);
+ knot_dname_free(owner, NULL);
+
+ /* Append origin if not FQDN. */
+ if (!fqdn) {
+ s->r_owner_length--;
+ memcpy(s->r_owner + s->r_owner_length, s->zone_origin,
+ s->zone_origin_length);
+ s->r_owner_length += s->zone_origin_length;
+ }
+
+ lp = tok_skipspace(lp + len);
+
+ /* Initialize */
+ s->r_type = KNOT_RRTYPE_ANY;
+ s->r_class = s->default_class;
+ s->r_data_length = 0;
+ if (flags & PARSE_NODEFAULT) {
+ s->r_ttl = 0;
+ } else {
+ s->r_ttl = s->default_ttl;
+ }
+
+ /* Parse only name? */
+ if (flags & PARSE_NAMEONLY) {
+ if (*lp != '\0') {
+ WARN("ignoring input data '%s'", lp);
+ }
+ return KNOT_EOK;
+ }
+
+ /* Now there could be [ttl] [class] [type [data...]]. */
+ char *np = NULL;
+ long ttl = strtol(lp, &np, 10);
+ if (ttl >= 0 && np && (*np == '\0' || is_space(*np))) {
+ DBG("%s: parsed ttl=%lu", __func__, ttl);
+ if (flags & PARSE_NOTTL) {
+ WARN("ignoring TTL value '%ld'", ttl);
+ } else {
+ s->r_ttl = ttl;
+ }
+ lp = tok_skipspace(np);
+ }
+
+ uint16_t num;
+ char *buff = NULL;
+ char *cls = NULL;
+ char *type = NULL;
+
+ /* Try to find class. */
+ len = strcspn(lp, SEP_CHARS);
+ if (len > 0) {
+ buff = strndup(lp, len);
+ }
+
+ if (knot_rrclass_from_string(buff, &num) == 0) {
+ /* Class must not differ from specified. */
+ if (num != s->default_class) {
+ ERR("class mismatch '%s'", buff);
+ free(buff);
+ return KNOT_EPARSEFAIL;
+ }
+ cls = buff;
+ buff = NULL;
+ s->r_class = num;
+ DBG("%s: parsed class=%u '%s'", __func__, s->r_class, cls);
+ lp = tok_skipspace(lp + len);
+ }
+
+ /* Try to parser type. */
+ if (cls != NULL) {
+ len = strcspn(lp, SEP_CHARS);
+ if (len > 0) {
+ buff = strndup(lp, len);
+ }
+ }
+ if (knot_rrtype_from_string(buff, &num) == 0) {
+ type = buff;
+ buff = NULL;
+ s->r_type = num;
+ DBG("%s: parsed type=%u '%s'", __func__, s->r_type, type);
+ lp = tok_skipspace(lp + len);
+ }
+
+ free(buff);
+
+ /* Remainder */
+ if (*lp == '\0') {
+ free(cls);
+ free(type);
+ return KNOT_EOK;
+ }
+
+ /* Need to parse rdata, synthetize input. */
+ char *rr = sprintf_alloc(" %u IN %s %s\n", s->r_ttl, type, lp);
+ free(cls);
+ free(type);
+ if (rr == NULL) {
+ return KNOT_ENOMEM;
+ }
+ if (zs_set_input_string(s, rr, strlen(rr)) != 0 ||
+ zs_parse_all(s) != 0) {
+ ERR("invalid rdata (%s)", zs_strerror(s->error.code));
+ return KNOT_EPARSEFAIL;
+ }
+ free(rr);
+
+ return KNOT_EOK;
+}
+
+static srv_info_t *parse_host(const char *lp, const char* default_port)
+{
+ /* Extract server address. */
+ srv_info_t *srv = NULL;
+ size_t len = strcspn(lp, SEP_CHARS);
+ char *addr = strndup(lp, len);
+ if (!addr) return NULL;
+ DBG("%s: parsed addr: %s", __func__, addr);
+
+ /* Store port/service if present. */
+ lp = tok_skipspace(lp + len);
+ if (*lp == '\0') {
+ srv = srv_info_create(addr, default_port);
+ free(addr);
+ return srv;
+ }
+
+ len = strcspn(lp, SEP_CHARS);
+ char *port = strndup(lp, len);
+ if (!port) {
+ free(addr);
+ return NULL;
+ }
+ DBG("%s: parsed port: %s", __func__, port);
+
+ /* Create server struct. */
+ srv = srv_info_create(addr, port);
+ free(addr);
+ free(port);
+ return srv;
+}
+
+/* Append parsed RRSet to list. */
+static int rr_list_append(zs_scanner_t *s, list_t *target_list, knot_mm_t *mm)
+{
+ knot_rrset_t *rr = knot_rrset_new(s->r_owner, s->r_type, s->r_class,
+ s->r_ttl, NULL);
+ if (!rr) {
+ DBG("%s: failed to create rrset", __func__);
+ return KNOT_ENOMEM;
+ }
+
+ /* Create RDATA. */
+ int ret = knot_rrset_add_rdata(rr, s->r_data, s->r_data_length, NULL);
+ if (ret != KNOT_EOK) {
+ DBG("%s: failed to set rrset from wire (%s)",
+ __func__, knot_strerror(ret));
+ knot_rrset_free(rr, NULL);
+ return ret;
+ }
+
+ if (ptrlist_add(target_list, rr, mm) == NULL) {
+ knot_rrset_free(rr, NULL);
+ return KNOT_ENOMEM;
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Write RRSet list to packet section. */
+static int rr_list_to_packet(knot_pkt_t *dst, list_t *list)
+{
+ assert(dst != NULL);
+ assert(list != NULL);
+
+ ptrnode_t *node;
+ WALK_LIST(node, *list) {
+ int ret = knot_pkt_put(dst, KNOT_COMPR_HINT_NONE,
+ (knot_rrset_t *)node->d, 0);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+/*! \brief Build UPDATE query. */
+static int build_query(knsupdate_params_t *params)
+{
+ /* Clear old query. */
+ knot_pkt_t *query = params->query;
+ knot_pkt_clear(query);
+
+ /* Write question. */
+ knot_wire_set_id(query->wire, dnssec_random_uint16_t());
+ knot_wire_set_opcode(query->wire, KNOT_OPCODE_UPDATE);
+ knot_dname_t *qname = knot_dname_from_str_alloc(params->zone);
+ int ret = knot_pkt_put_question(query, qname, params->class_num,
+ KNOT_RRTYPE_SOA);
+ knot_dname_free(qname, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Now, PREREQ => ANSWER section. */
+ ret = knot_pkt_begin(query, KNOT_ANSWER);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Write PREREQ. */
+ ret = rr_list_to_packet(query, &params->prereq_list);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Now, UPDATE data => AUTHORITY section. */
+ ret = knot_pkt_begin(query, KNOT_AUTHORITY);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Write UPDATE data. */
+ return rr_list_to_packet(query, &params->update_list);
+}
+
+static int pkt_sendrecv(knsupdate_params_t *params)
+{
+ net_t net;
+ int ret;
+
+ ret = net_init(params->srcif,
+ params->server,
+ get_iptype(params->ip, params->server),
+ get_socktype(params->protocol, KNOT_RRTYPE_SOA),
+ params->wait,
+ NET_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &net);
+ if (ret != KNOT_EOK) {
+ return -1;
+ }
+
+ ret = net_connect(&net);
+ DBG("%s: send_msg = %d", __func__, net.sockfd);
+ if (ret != KNOT_EOK) {
+ net_clean(&net);
+ return -1;
+ }
+
+ ret = net_send(&net, params->query->wire, params->query->size);
+ if (ret != KNOT_EOK) {
+ net_close(&net);
+ net_clean(&net);
+ return -1;
+ }
+
+ /* Clear response buffer. */
+ knot_pkt_clear(params->answer);
+
+ /* Wait for reception. */
+ int rb = net_receive(&net, params->answer->wire, params->answer->max_size);
+ DBG("%s: receive_msg = %d", __func__, rb);
+ if (rb <= 0) {
+ net_close(&net);
+ net_clean(&net);
+ return -1;
+ } else {
+ params->answer->size = rb;
+ }
+
+ net_close(&net);
+ net_clean(&net);
+
+ return rb;
+}
+
+int knsupdate_process_line(const char *line, knsupdate_params_t *params)
+{
+ /* Check for empty line or comment. */
+ if (line[0] == '\0' || line[0] == ';') {
+ return KNOT_EOK;
+ }
+
+ int ret = tok_find(line, knsupdate_cmd_array);
+ if (ret < 0) {
+ return ret; /* Syntax error - do nothing. */
+ }
+
+ const char *cmd = knsupdate_cmd_array[ret];
+ const char *val = tok_skipspace(line + TOK_L(cmd));
+ params->parser.error.counter = 0; /* Reset possible previous error. */
+ ret = cmd_handle[ret](val, params);
+ if (ret != KNOT_EOK) {
+ DBG("operation '%s' failed (%s) on line '%s'",
+ TOK_S(cmd), knot_strerror(ret), line);
+ }
+
+ return ret;
+}
+
+static bool is_terminal(FILE *file)
+{
+ int fd = fileno(file);
+ assert(fd >= 0);
+ return isatty(fd);
+}
+
+static int process_lines(knsupdate_params_t *params, FILE *input)
+{
+ char *buf = NULL;
+ size_t buflen = 0;
+ if(is_terminal(input)) {
+ return interactive_loop(params);
+ }
+ int ret = KNOT_EOK;
+
+ /* Process lines. */
+ while (!params->stop && knot_getline(&buf, &buflen, input) != -1) {
+ /* Remove leading and trailing white space. */
+ char *line = strstrip(buf);
+ ret = knsupdate_process_line(line, params);
+ memset(line, 0, strlen(line));
+ free(line);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ if (buf != NULL) {
+ memset(buf, 0, buflen);
+ free(buf);
+ }
+
+ return ret;
+}
+
+int knsupdate_exec(knsupdate_params_t *params)
+{
+ if (!params) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+
+ /* If no file specified, enter the interactive mode. */
+ if (EMPTY_LIST(params->qfiles)) {
+ return process_lines(params, stdin);
+ }
+
+ /* Read from each specified file. */
+ ptrnode_t *n;
+ WALK_LIST(n, params->qfiles) {
+ const char *filename = (const char*)n->d;
+ if (strcmp(filename, "-") == 0) {
+ ret = process_lines(params, stdin);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ continue;
+ }
+
+ FILE *fp = fopen(filename, "r");
+ if (!fp) {
+ ERR("failed to open '%s' (%s)",
+ filename, strerror(errno));
+ ret = KNOT_EFILE;
+ break;
+ }
+ ret = process_lines(params, fp);
+ fclose(fp);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int cmd_update(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* update is optional token, next add|del|delete */
+ int bp = tok_find(lp, knsupdate_cmd_array);
+ if (bp < 0) return bp; /* Syntax error. */
+
+ /* allow only specific tokens */
+ cmd_handle_f *h = cmd_handle;
+ if (h[bp] != cmd_add && h[bp] != cmd_del) {
+ ERR("unexpected token '%s' after 'update', allowed: '%s'",
+ lp, "{add|del|delete}");
+ return KNOT_EPARSEFAIL;
+ }
+
+ return h[bp](tok_skipspace(lp + TOK_L(knsupdate_cmd_array[bp])), params);
+}
+
+int cmd_add(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ if (parse_full_rr(&params->parser, lp) != KNOT_EOK) {
+ return KNOT_EPARSEFAIL;
+ }
+
+ return rr_list_append(&params->parser, &params->update_list, &params->mm);
+}
+
+int cmd_del(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ zs_scanner_t *rrp = &params->parser;
+ int ret = parse_partial_rr(rrp, lp, PARSE_NODEFAULT);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check owner name. */
+ if (rrp->r_owner_length == 0) {
+ ERR("failed to parse owner name '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ rrp->r_ttl = 0; /* Set TTL = 0 when deleting. */
+
+ /* When deleting whole RRSet, use ANY class */
+ if (rrp->r_data_length == 0) {
+ rrp->r_class = KNOT_CLASS_ANY;
+ } else {
+ rrp->r_class = KNOT_CLASS_NONE;
+ }
+
+ return rr_list_append(rrp, &params->update_list, &params->mm);
+}
+
+int cmd_class(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ uint16_t cls;
+
+ if (knot_rrclass_from_string(lp, &cls) != 0) {
+ ERR("failed to parse class '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ params->class_num = cls;
+ params->parser.default_class = params->class_num;
+
+ return KNOT_EOK;
+}
+
+int cmd_ttl(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ uint32_t ttl = 0;
+
+ if (str_to_u32(lp, &ttl) != KNOT_EOK) {
+ ERR("failed to parse ttl '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ return knsupdate_set_ttl(params, ttl);
+}
+
+int cmd_debug(const char* lp, _unused_ knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ msg_enable_debug(1);
+
+ return KNOT_EOK;
+}
+
+int cmd_nxdomain(const char *lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ zs_scanner_t *s = &params->parser;
+ int ret = parse_partial_rr(s, lp, PARSE_NODEFAULT | PARSE_NAMEONLY);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ s->r_ttl = 0;
+ s->r_class = KNOT_CLASS_NONE;
+
+ return rr_list_append(s, &params->prereq_list, &params->mm);
+}
+
+int cmd_yxdomain(const char *lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ zs_scanner_t *s = &params->parser;
+ int ret = parse_partial_rr(s, lp, PARSE_NODEFAULT | PARSE_NAMEONLY);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ s->r_ttl = 0;
+ s->r_class = KNOT_CLASS_ANY;
+
+ return rr_list_append(s, &params->prereq_list, &params->mm);
+}
+
+int cmd_nxrrset(const char *lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ zs_scanner_t *s = &params->parser;
+ int ret = parse_partial_rr(s, lp, PARSE_NOTTL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check owner name. */
+ if (s->r_owner_length == 0) {
+ ERR("failed to parse prereq owner name '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ s->r_ttl = 0;
+ s->r_class = KNOT_CLASS_NONE;
+
+ return rr_list_append(s, &params->prereq_list, &params->mm);
+}
+
+int cmd_yxrrset(const char *lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ zs_scanner_t *s = &params->parser;
+ int ret = parse_partial_rr(s, lp, PARSE_NOTTL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Check owner name. */
+ if (s->r_owner_length == 0) {
+ ERR("failed to parse prereq owner name '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ s->r_ttl = 0;
+ if (s->r_data_length > 0) {
+ s->r_class = KNOT_CLASS_IN;
+ } else {
+ s->r_class = KNOT_CLASS_ANY;
+ }
+
+ return rr_list_append(s, &params->prereq_list, &params->mm);
+}
+
+int cmd_prereq(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Scan prereq specifier ([ny]xrrset|[ny]xdomain) */
+ int prereq_type = tok_find(lp, pq_array);
+ if (prereq_type < 0) {
+ return prereq_type;
+ }
+
+ const char *tok = pq_array[prereq_type];
+ DBG("%s: type %s", __func__, TOK_S(tok));
+ lp = tok_skipspace(lp + TOK_L(tok));
+ if (strlen(lp) == 0) {
+ ERR("missing prerequisite owner name");
+ return KNOT_EINVAL;
+ }
+
+ int ret = KNOT_EOK;
+ switch(prereq_type) {
+ case PQ_NXDOMAIN:
+ ret = cmd_nxdomain(lp, params);
+ break;
+ case PQ_YXDOMAIN:
+ ret = cmd_yxdomain(lp, params);
+ break;
+ case PQ_NXRRSET:
+ ret = cmd_nxrrset(lp, params);
+ break;
+ case PQ_YXRRSET:
+ ret = cmd_yxrrset(lp, params);
+ break;
+ default:
+ ret = KNOT_ERROR;
+ }
+
+ return ret;
+}
+
+int cmd_exit(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ params->stop = true;
+
+ return KNOT_EOK;
+}
+
+int cmd_send(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+ DBG("sending packet");
+
+ if (params->zone == NULL) {
+ ERR("no zone specified");
+ return KNOT_EINVAL;
+ }
+
+ /* Build query packet. */
+ int ret = build_query(params);
+ if (ret != KNOT_EOK) {
+ ERR("failed to build UPDATE message (%s)", knot_strerror(ret));
+ return ret;
+ }
+
+ /* Sign if key specified. */
+ sign_context_t sign_ctx = { 0 };
+ if (params->tsig_key.name) {
+ ret = sign_context_init_tsig(&sign_ctx, &params->tsig_key);
+ if (ret != KNOT_EOK) {
+ ERR("failed to initialize signing context (%s)",
+ knot_strerror(ret));
+ return ret;
+ }
+
+ ret = sign_packet(params->query, &sign_ctx);
+ if (ret != KNOT_EOK) {
+ ERR("failed to sign UPDATE message (%s)",
+ knot_strerror(ret));
+ sign_context_deinit(&sign_ctx);
+ return ret;
+ }
+ }
+
+ int rb = 0;
+ /* Send/recv message (1 try + N retries). */
+ int tries = 1 + params->retries;
+ for (; tries > 0; --tries) {
+ rb = pkt_sendrecv(params);
+ if (rb > 0) {
+ break;
+ }
+ }
+
+ /* Check Send/recv result. */
+ if (rb <= 0) {
+ sign_context_deinit(&sign_ctx);
+ return KNOT_ECONNREFUSED;
+ }
+
+ /* Parse response. */
+ ret = knot_pkt_parse(params->answer, KNOT_PF_NOCANON);
+ if (ret != KNOT_EOK) {
+ ERR("failed to parse response (%s)", knot_strerror(ret));
+ sign_context_deinit(&sign_ctx);
+ return ret;
+ }
+
+ /* Check signature if expected. */
+ if (params->tsig_key.name) {
+ ret = verify_packet(params->answer, &sign_ctx);
+ sign_context_deinit(&sign_ctx);
+ if (ret != KNOT_EOK) {
+ print_packet(params->answer, NULL, 0, -1, 0, true,
+ &params->style);
+ ERR("reply verification (%s)", knot_strerror(ret));
+ return ret;
+ }
+ }
+
+ /* Free RRSet lists. */
+ knsupdate_reset(params);
+
+ /* Check return code. */
+ if (knot_pkt_ext_rcode(params->answer) != KNOT_RCODE_NOERROR) {
+ print_packet(params->answer, NULL, 0, -1, 0, true, &params->style);
+ ERR("update failed with error '%s'",
+ knot_pkt_ext_rcode_name(params->answer));
+ ret = KNOT_ERROR;
+ } else {
+ DBG("update success");
+ }
+
+ return ret;
+}
+
+int cmd_zone(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Check zone name. */
+ if (!dname_isvalid(lp)) {
+ ERR("failed to parse zone '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ free(params->zone);
+ params->zone = strdup(lp);
+
+ return KNOT_EOK;
+}
+
+int cmd_server(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Parse host. */
+ srv_info_t *srv = parse_host(lp, params->server->service);
+ if (!srv) {
+ ERR("failed to parse server '%s'", lp);
+ return KNOT_ENOMEM;
+ }
+
+ srv_info_free(params->server);
+ params->server = srv;
+
+ return KNOT_EOK;
+}
+
+int cmd_local(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Parse host. */
+ srv_info_t *srv = parse_host(lp, "0");
+ if (!srv) {
+ ERR("failed to parse local '%s'", lp);
+ return KNOT_ENOMEM;
+ }
+
+ srv_info_free(params->srcif);
+ params->srcif = srv;
+
+ return KNOT_EOK;
+}
+
+int cmd_show(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ if (!params->query) {
+ return KNOT_EOK;
+ }
+
+ printf("Update query:\n");
+ build_query(params);
+ print_packet(params->query, NULL, 0, -1, 0, false, &params->style);
+ printf("\n");
+
+ return KNOT_EOK;
+}
+
+int cmd_answer(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ if (!params->answer) {
+ return KNOT_EOK;
+ }
+
+ printf("Answer:\n");
+ print_packet(params->answer, NULL, 0, -1, 0, true, &params->style);
+
+ return KNOT_EOK;
+}
+
+int cmd_key(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Convert to default format. */
+ char *kstr = strdup(lp);
+ if (!kstr) {
+ return KNOT_ENOMEM;
+ }
+
+ int ret = KNOT_EOK;
+
+ /* Search for the name secret separation. Allow also alg:name:key form. */
+ char *sep = strchr(kstr, ' ');
+ if (sep != NULL) {
+ /* Replace ' ' with ':'. More spaces are ignored in base64. */
+ *sep = ':';
+ }
+
+ /* Override existing key. */
+ knot_tsig_key_deinit(&params->tsig_key);
+
+ ret = knot_tsig_key_init_str(&params->tsig_key, kstr);
+ if (ret != KNOT_EOK) {
+ ERR("invalid key specification");
+ }
+
+ free(kstr);
+
+ return ret;
+}
+
+int cmd_origin(const char* lp, knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ /* Check zone name. */
+ if (!dname_isvalid(lp)) {
+ ERR("failed to parse zone '%s'", lp);
+ return KNOT_EPARSEFAIL;
+ }
+
+ return knsupdate_set_origin(params, lp);
+}
+
+/*
+ * Not implemented.
+ */
+
+int cmd_gsstsig(const char* lp, _unused_ knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ ERR("gsstsig not supported");
+
+ return KNOT_ENOTSUP;
+}
+
+int cmd_oldgsstsig(const char* lp, _unused_ knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ ERR("oldgsstsig not supported");
+
+ return KNOT_ENOTSUP;
+}
+
+int cmd_realm(const char* lp, _unused_ knsupdate_params_t *params)
+{
+ DBG("%s: lp='%s'", __func__, lp);
+
+ ERR("realm not supported");
+
+ return KNOT_ENOTSUP;
+}
diff --git a/src/utils/knsupdate/knsupdate_exec.h b/src/utils/knsupdate/knsupdate_exec.h
new file mode 100644
index 0000000..36ca43b
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_exec.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "utils/knsupdate/knsupdate_params.h"
+
+extern const char* knsupdate_cmd_array[];
+
+int knsupdate_exec(knsupdate_params_t *params);
+
+int knsupdate_process_line(const char *line, knsupdate_params_t *params);
diff --git a/src/utils/knsupdate/knsupdate_interactive.c b/src/utils/knsupdate/knsupdate_interactive.c
new file mode 100644
index 0000000..0e4ed66
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_interactive.c
@@ -0,0 +1,177 @@
+/* Copyright (C) 2022 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 <histedit.h>
+
+#include "contrib/string.h"
+#include "utils/common/lookup.h"
+#include "utils/common/msg.h"
+#include "utils/knsupdate/knsupdate_exec.h"
+#include "utils/knsupdate/knsupdate_interactive.h"
+
+#define HISTORY_FILE ".knsupdate_history"
+
+static char *prompt(EditLine *el)
+{
+ return PROGRAM_NAME"> ";
+}
+
+static void print_commands(void)
+{
+ printf("\n");
+
+ for (const char **cmd = knsupdate_cmd_array; *cmd != NULL; cmd++) {
+ printf(" %-18s\n", (*cmd) + 1);
+ }
+}
+
+static void cmds_lookup(EditLine *el, const char *str, size_t str_len)
+{
+ lookup_t lookup;
+ int ret = lookup_init(&lookup);
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ // Fill the lookup with command names.
+ for (const char **desc = knsupdate_cmd_array; *desc != NULL; desc++) {
+ ret = lookup_insert(&lookup, (*desc) + 1, NULL);
+ if (ret != KNOT_EOK) {
+ goto cmds_lookup_finish;
+ }
+ }
+
+ (void)lookup_complete(&lookup, str, str_len, el, true);
+
+cmds_lookup_finish:
+ lookup_deinit(&lookup);
+}
+
+static unsigned char complete(EditLine *el, int ch)
+{
+ int argc, token, pos;
+ const char **argv;
+
+ const LineInfo *li = el_line(el);
+ Tokenizer *tok = tok_init(NULL);
+
+ // Parse the line.
+ int ret = tok_line(tok, li, &argc, &argv, &token, &pos);
+ if (ret != 0) {
+ goto complete_exit;
+ }
+
+ // Show possible commands.
+ if (argc == 0) {
+ print_commands();
+ goto complete_exit;
+ }
+
+ // Complete the command name.
+ if (token == 0) {
+ cmds_lookup(el, argv[0], pos);
+ goto complete_exit;
+ }
+
+ // Find the command descriptor.
+ const char **desc = knsupdate_cmd_array;
+ while (*desc != NULL && strcmp((*desc) + 1, argv[0]) != 0) {
+ desc++;
+ }
+ if (*desc == NULL) {
+ goto complete_exit;
+ }
+
+complete_exit:
+ tok_reset(tok);
+ tok_end(tok);
+
+ return CC_REDISPLAY;
+}
+
+int interactive_loop(knsupdate_params_t *params)
+{
+ char *hist_file = NULL;
+ const char *home = getenv("HOME");
+ if (home != NULL) {
+ hist_file = sprintf_alloc("%s/%s", home, HISTORY_FILE);
+ }
+ if (hist_file == NULL) {
+ INFO("failed to get home directory");
+ }
+
+ EditLine *el = el_init(PROGRAM_NAME, stdin, stdout, stderr);
+ if (el == NULL) {
+ ERR("interactive mode not available");
+ free(hist_file);
+ return KNOT_ERROR;
+ }
+
+ History *hist = history_init();
+ if (hist == NULL) {
+ ERR("interactive mode not available");
+ el_end(el);
+ free(hist_file);
+ return KNOT_ERROR;
+ }
+
+ HistEvent hev = { 0 };
+ history(hist, &hev, H_SETSIZE, 1000);
+ history(hist, &hev, H_SETUNIQUE, 1);
+ el_set(el, EL_HIST, history, hist);
+ history(hist, &hev, H_LOAD, hist_file);
+
+ el_set(el, EL_TERMINAL, NULL);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+
+ // Warning: these two el_sets()'s always leak -- in libedit2 library!
+ // For more details see this commit's message.
+ el_set(el, EL_ADDFN, PROGRAM_NAME"-complete",
+ "Perform "PROGRAM_NAME" completion.", complete);
+ el_set(el, EL_BIND, "^I", PROGRAM_NAME"-complete", NULL);
+
+ int count;
+ const char *line;
+ while ((line = el_gets(el, &count)) != NULL && count > 0) {
+ char command[count + 1];
+ memcpy(command, line, count);
+ command[count] = '\0';
+ // Removes trailing newline
+ size_t cmd_len = strcspn(command, "\n");
+ command[cmd_len] = '\0';
+
+ if (cmd_len > 0) {
+ history(hist, &hev, H_ENTER, command);
+ history(hist, &hev, H_SAVE, hist_file);
+ }
+
+ // Process the command.
+ (void)knsupdate_process_line(command, params);
+ if (params->stop) {
+ break;
+ }
+ }
+
+ history_end(hist);
+ free(hist_file);
+
+ el_end(el);
+
+ return KNOT_EOK;
+}
diff --git a/src/utils/knsupdate/knsupdate_interactive.h b/src/utils/knsupdate/knsupdate_interactive.h
new file mode 100644
index 0000000..e2a2428
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_interactive.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "utils/knsupdate/knsupdate_params.h"
+
+/*!
+ * Executes an interactive processing loop.
+ *
+ * \param[in] params Utility parameters.
+ */
+int interactive_loop(knsupdate_params_t *params);
diff --git a/src/utils/knsupdate/knsupdate_main.c b/src/utils/knsupdate/knsupdate_main.c
new file mode 100644
index 0000000..ab65c1e
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_main.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2018 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 <stdlib.h>
+
+#include "libdnssec/crypto.h"
+#include "utils/knsupdate/knsupdate_exec.h"
+#include "utils/knsupdate/knsupdate_params.h"
+#include "libknot/libknot.h"
+
+int main(int argc, char *argv[])
+{
+
+ int ret = EXIT_SUCCESS;
+
+ tzset();
+
+ knsupdate_params_t params;
+ if (knsupdate_parse(&params, argc, argv) == KNOT_EOK) {
+ if (!params.stop) {
+ dnssec_crypto_init();
+ if (knsupdate_exec(&params) != KNOT_EOK) {
+ ret = EXIT_FAILURE;
+ }
+ dnssec_crypto_cleanup();
+ }
+ } else {
+ ret = EXIT_FAILURE;
+ }
+
+ knsupdate_clean(&params);
+ return ret;
+}
diff --git a/src/utils/knsupdate/knsupdate_params.c b/src/utils/knsupdate/knsupdate_params.c
new file mode 100644
index 0000000..f9fa41f
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_params.c
@@ -0,0 +1,304 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "utils/knsupdate/knsupdate_params.h"
+#include "utils/common/msg.h"
+#include "utils/common/netio.h"
+#include "libknot/libknot.h"
+#include "libknot/tsig.h"
+#include "contrib/mempattern.h"
+#include "contrib/strtonum.h"
+#include "contrib/ucw/mempool.h"
+
+#define DEFAULT_RETRIES_NSUPDATE 3
+#define DEFAULT_TIMEOUT_NSUPDATE 12
+
+static const style_t DEFAULT_STYLE_NSUPDATE = {
+ .format = FORMAT_NSUPDATE,
+ .style = {
+ .wrap = false,
+ .show_class = true,
+ .show_ttl = true,
+ .verbose = false,
+ .original_ttl = false,
+ .empty_ttl = false,
+ .human_ttl = false,
+ .human_timestamp = true,
+ .generic = false,
+ .ascii_to_idn = NULL
+ },
+ .show_query = false,
+ .show_header = true,
+ .show_section = true,
+ .show_edns = false,
+ .show_question = true,
+ .show_answer = true,
+ .show_authority = true,
+ .show_additional = true,
+ .show_tsig = true,
+ .show_footer = false
+};
+
+static int parser_set_default(zs_scanner_t *s, const char *fmt, ...)
+{
+ /* Format string. */
+ char buf[512]; /* Must suffice for domain name and TTL. */
+ va_list ap;
+ va_start(ap, fmt);
+ int n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (n < 0 || (size_t)n >= sizeof(buf)) {
+ return ZS_EINVAL;
+ }
+
+ /* Buffer must contain newline */
+ if (zs_set_input_string(s, buf, n) != 0 ||
+ zs_parse_all(s) != 0) {
+ return s->error.code;
+ }
+
+ return KNOT_EOK;
+}
+
+static int knsupdate_init(knsupdate_params_t *params)
+{
+ memset(params, 0, sizeof(knsupdate_params_t));
+
+ /* Initialize lists. */
+ init_list(&params->qfiles);
+ init_list(&params->update_list);
+ init_list(&params->prereq_list);
+
+ /* Initialize memory context. */
+ mm_ctx_mempool(&params->mm, MM_DEFAULT_BLKSIZE);
+
+ /* Default server. */
+ params->server = srv_info_create(DEFAULT_IPV4_NAME, DEFAULT_DNS_PORT);
+ if (!params->server)
+ return KNOT_ENOMEM;
+
+ /* Default settings. */
+ params->ip = IP_ALL;
+ params->protocol = PROTO_ALL;
+ params->class_num = KNOT_CLASS_IN;
+ params->retries = DEFAULT_RETRIES_NSUPDATE;
+ params->wait = DEFAULT_TIMEOUT_NSUPDATE;
+
+ /* Initialize RR parser. */
+ if (zs_init(&params->parser, ".", params->class_num, 3600) != 0 ||
+ zs_set_processing(&params->parser, NULL, NULL, NULL) != 0) {
+ zs_deinit(&params->parser);
+ return KNOT_ENOMEM;
+ }
+
+ /* Default style. */
+ params->style = DEFAULT_STYLE_NSUPDATE;
+
+ /* Create query/answer packets. */
+ params->query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &params->mm);
+ params->answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, &params->mm);
+
+ return KNOT_EOK;
+}
+
+void knsupdate_clean(knsupdate_params_t *params)
+{
+ if (params == NULL) {
+ return;
+ }
+
+ /* Clear current query. */
+ knsupdate_reset(params);
+
+ /* Free qfiles. */
+ ptrlist_free(&params->qfiles, &params->mm);
+
+ srv_info_free(params->server);
+ srv_info_free(params->srcif);
+ free(params->zone);
+ zs_deinit(&params->parser);
+ knot_pkt_free(params->query);
+ knot_pkt_free(params->answer);
+ knot_tsig_key_deinit(&params->tsig_key);
+
+ /* Clean up the structure. */
+ mp_delete(params->mm.ctx);
+ memset(params, 0, sizeof(*params));
+}
+
+/*! \brief Free RRSet list. */
+static void rr_list_free(list_t *list, knot_mm_t *mm)
+{
+ assert(list != NULL);
+ assert(mm != NULL);
+
+ ptrnode_t *node;
+ WALK_LIST(node, *list) {
+ knot_rrset_t *rrset = (knot_rrset_t *)node->d;
+ knot_rrset_free(rrset, NULL);
+ }
+ ptrlist_free(list, mm);
+}
+
+void knsupdate_reset(knsupdate_params_t *params)
+{
+ /* Free ADD/REMOVE RRSets. */
+ rr_list_free(&params->update_list, &params->mm);
+
+ /* Free PREREQ RRSets. */
+ rr_list_free(&params->prereq_list, &params->mm);
+}
+
+static void print_help(void)
+{
+ printf("Usage: %s [-d] [-v] [-k keyfile | -y [hmac:]name:key]\n"
+ " [-p port] [-t timeout] [-r retries] [filename]\n",
+ PROGRAM_NAME);
+}
+
+int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[])
+{
+ if (params == NULL || argv == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ int ret = knsupdate_init(params);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ // Long options.
+ struct option opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ /* Command line options processing. */
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "dhDvVp:t:r:y:k:", opts, NULL))
+ != -1) {
+ switch (opt) {
+ case 'd':
+ case 'D': /* Extra debugging. */
+ msg_enable_debug(1);
+ break;
+ case 'h':
+ print_help();
+ params->stop = true;
+ return KNOT_EOK;
+ case 'v':
+ params->protocol = PROTO_TCP;
+ break;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ params->stop = true;
+ return KNOT_EOK;
+ case 'p':
+ free(params->server->service);
+ params->server->service = strdup(optarg);
+ if (!params->server->service) {
+ ERR("failed to set default port '%s'", optarg);
+ return KNOT_ENOMEM;
+ }
+ break;
+ case 'r':
+ ret = str_to_u32(optarg, &params->retries);
+ if (ret != KNOT_EOK) {
+ ERR("invalid retries '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 't':
+ ret = params_parse_wait(optarg, &params->wait);
+ if (ret != KNOT_EOK) {
+ ERR("invalid timeout '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 'y':
+ knot_tsig_key_deinit(&params->tsig_key);
+ ret = knot_tsig_key_init_str(&params->tsig_key, optarg);
+ if (ret != KNOT_EOK) {
+ ERR("failed to parse key '%s'", optarg);
+ return ret;
+ }
+ break;
+ case 'k':
+ knot_tsig_key_deinit(&params->tsig_key);
+ ret = knot_tsig_key_init_file(&params->tsig_key, optarg);
+ if (ret != KNOT_EOK) {
+ ERR("failed to parse keyfile '%s'", optarg);
+ return ret;
+ }
+ break;
+ default:
+ print_help();
+ return KNOT_ENOTSUP;
+ }
+ }
+
+ /* No retries for TCP. */
+ if (params->protocol == PROTO_TCP) {
+ params->retries = 0;
+ } else {
+ /* If wait/tries < 1 s, set 1 second for each try. */
+ if (params->wait > 0 &&
+ (uint32_t)params->wait < ( 1 + params->retries)) {
+ params->wait = 1;
+ } else {
+ params->wait /= (1 + params->retries);
+ }
+ }
+
+ /* Process non-option parameters. */
+ for (; optind < argc; ++optind) {
+ ptrlist_add(&params->qfiles, argv[optind], &params->mm);
+ }
+
+ return ret;
+}
+
+int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl)
+{
+ int ret = parser_set_default(&params->parser, "$TTL %u\n", ttl);
+ if (ret != KNOT_EOK) {
+ ERR("failed to set default TTL, %s", zs_strerror(ret));
+ }
+ return ret;
+}
+
+int knsupdate_set_origin(knsupdate_params_t *params, const char *origin)
+{
+ char *fqdn = get_fqd_name(origin);
+
+ int ret = parser_set_default(&params->parser, "$ORIGIN %s\n", fqdn);
+
+ free(fqdn);
+
+ if (ret != KNOT_EOK) {
+ ERR("failed to set default origin, %s", zs_strerror(ret));
+ }
+ return ret;
+}
diff --git a/src/utils/knsupdate/knsupdate_params.h b/src/utils/knsupdate/knsupdate_params.h
new file mode 100644
index 0000000..1933244
--- /dev/null
+++ b/src/utils/knsupdate/knsupdate_params.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "utils/common/netio.h"
+#include "utils/common/params.h"
+#include "utils/common/sign.h"
+#include "libknot/libknot.h"
+#include "libzscanner/scanner.h"
+#include "contrib/ucw/lists.h"
+
+#define PROGRAM_NAME "knsupdate"
+
+/*! \brief knsupdate-specific params data. */
+typedef struct {
+ /*!< Stop processing - just print help, version,... */
+ bool stop;
+ /*!< List of files with query data. */
+ list_t qfiles;
+ /*!< List of nameservers to query to. */
+ srv_info_t *server;
+ /*!< Local interface (optional). */
+ srv_info_t *srcif;
+ /*!< Version of ip protocol to use. */
+ ip_t ip;
+ /*!< Type (TCP, UDP) protocol to use. */
+ protocol_t protocol;
+ /*!< Default class number. */
+ uint16_t class_num;
+ /*!< Number of UDP retries. */
+ uint32_t retries;
+ /*!< Wait for network response in seconds (-1 means forever). */
+ int32_t wait;
+ /*!< Current zone. */
+ char *zone;
+ /*!< RR parser. */
+ zs_scanner_t parser;
+ /*!< Current packet. */
+ knot_pkt_t *query;
+ /*!< Current response. */
+ knot_pkt_t *answer;
+ /*< Lists of RRSets. */
+ list_t update_list, prereq_list;
+ /*!< Transaction signature context. */
+ knot_tsig_key_t tsig_key;
+ /*!< Default output settings. */
+ style_t style;
+ /*!< Memory context. */
+ knot_mm_t mm;
+} knsupdate_params_t;
+
+int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[]);
+int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl);
+int knsupdate_set_origin(knsupdate_params_t *params, const char *origin);
+void knsupdate_clean(knsupdate_params_t *params);
+void knsupdate_reset(knsupdate_params_t *params);
diff --git a/src/utils/kxdpgun/ip_route.c b/src/utils/kxdpgun/ip_route.c
new file mode 100644
index 0000000..003c764
--- /dev/null
+++ b/src/utils/kxdpgun/ip_route.c
@@ -0,0 +1,358 @@
+/* Copyright (C) 2022 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/if_ether.h>
+#include <linux/if_link.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+
+#include "utils/kxdpgun/ip_route.h"
+#include "contrib/sockaddr.h"
+
+#define ROUTE_LOOKUP_LOOP_LIMIT 10000
+
+static size_t addr_len(int family)
+{
+ switch (family) {
+ case AF_INET:
+ return sizeof(struct in_addr);
+ case AF_INET6:
+ return sizeof(struct in6_addr);
+ default:
+ return 0;
+ }
+}
+
+static int send_dummy_pkt(const struct sockaddr_storage *ip)
+{
+ static const uint8_t dummy_pkt[] = {
+ // dummy data
+ 0x08, 0x00, 0xec, 0x72, 0x0b, 0x87, 0x00, 0x06,
+
+ //padding
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ int fd = socket(ip->ss_family, SOCK_RAW,
+ ip->ss_family == AF_INET6 ? IPPROTO_ICMPV6 : IPPROTO_ICMP);
+ if (fd < 0) {
+ return -errno;
+ }
+ int ret = sendto(fd, dummy_pkt, sizeof(dummy_pkt), 0, (const struct sockaddr *)ip,
+ ip->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_in));
+ if (ret < 0) {
+ ret = -errno;
+ }
+ close(fd);
+ return ret;
+}
+
+static int netlink_query(int family, uint16_t type, mnl_cb_t cb, void *data,
+ void *qextra, size_t qextra_len, uint16_t qextra_type)
+{
+ // open and bind NETLINK socket
+ struct mnl_socket *nl = mnl_socket_open(NETLINK_ROUTE);
+ if (nl == NULL) {
+ return -errno;
+ }
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
+ mnl_socket_close(nl);
+ return -errno;
+ }
+ unsigned portid = mnl_socket_get_portid(nl);
+ int ret = 0;
+
+ // allocate request
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
+ if (nlh == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ unsigned seq = time(NULL);
+ nlh->nlmsg_seq = seq;
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ struct rtmsg *rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(*rtm));
+ if (rtm == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ if (qextra_len > 0) {
+ nlh->nlmsg_flags = NLM_F_REQUEST;
+ rtm->rtm_dst_len = qextra_len * 8; // 8 bits per byte
+ mnl_attr_put(nlh, qextra_type, qextra_len, qextra);
+ }
+
+ // send request
+ rtm->rtm_family = family;
+ ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
+ if (ret < 0) {
+ ret = -errno;
+ goto end;
+ }
+
+ // collect replies with callback
+ while ((ret = mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, cb, data);
+ if (ret <= MNL_CB_STOP) {
+ break;
+ }
+ if (qextra_len > 0) {
+ break;
+ }
+ }
+ ret = ret < 0 ? -errno : 0;
+
+end:
+ mnl_socket_close(nl);
+ return ret;
+}
+
+typedef struct {
+ const struct sockaddr_storage *ip;
+ struct sockaddr_storage *via;
+ struct sockaddr_storage *src;
+ char *dev;
+ uint64_t priority; // top 32 bits: unmatched address bits; bottom 32 bits: route metric priority
+ unsigned match;
+
+ // intermediate callback data
+ const struct nlattr *tb[RTA_MAX+1];
+} ip_route_get_ctx_t;
+
+static int validate_attr_route(const struct nlattr *attr, void *data)
+{
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, RTA_MAX) < 0) {
+ return MNL_CB_OK;
+ }
+
+ ip_route_get_ctx_t *ctx = data;
+
+ int type = mnl_attr_get_type(attr);
+ switch(type) {
+ case RTA_TABLE:
+ case RTA_OIF:
+ case RTA_FLOW:
+ case RTA_PRIORITY:
+ if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_DST:
+ case RTA_SRC:
+ case RTA_PREFSRC:
+ case RTA_GATEWAY:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, addr_len(ctx->ip->ss_family)) < 0) {
+ return MNL_CB_ERROR;
+ }
+ break;
+ case RTA_METRICS:
+ if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) {
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+ ctx->tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static void attr2addr(const struct nlattr *attr, int family, struct sockaddr_storage *out)
+{
+ if (attr == NULL) {
+ out->ss_family = AF_UNSPEC;
+ return;
+ }
+ out->ss_family = family;
+ if (family == AF_INET6) {
+ struct in6_addr *addr = mnl_attr_get_payload(attr);
+ memcpy(&((struct sockaddr_in6 *)out)->sin6_addr, addr, sizeof(*addr));
+ } else {
+ struct in_addr *addr = mnl_attr_get_payload(attr);
+ memcpy(&((struct sockaddr_in *)out)->sin_addr, addr, sizeof(*addr));
+ }
+}
+
+static void attr2dev(const struct nlattr *attr, char *out) // out must have IFNAMSIZ length
+{
+ *out = '\0';
+
+ if (attr != NULL) {
+ if_indextoname(mnl_attr_get_u32(attr), out);
+ }
+}
+
+static uint32_t attr2prio(const struct nlattr *attr)
+{
+ if (attr == NULL) {
+ return 0; // 0 is the default metric priority in linux
+ }
+ return mnl_attr_get_u32(attr);
+}
+
+static int ip_route_get_cb(const struct nlmsghdr *nlh, void *data)
+{
+ ip_route_get_ctx_t *ctx = data;
+ struct rtmsg *rm = mnl_nlmsg_get_payload(nlh);
+ if (rm->rtm_family != ctx->ip->ss_family) {
+ return MNL_CB_ERROR;
+ }
+
+ mnl_attr_parse(nlh, sizeof(*rm), validate_attr_route, data);
+
+ uint64_t new_metric = addr_len(rm->rtm_family) * 8 - rm->rtm_dst_len;
+ new_metric = (new_metric << 32) + attr2prio(ctx->tb[RTA_PRIORITY]);
+ if (new_metric >= ctx->priority) {
+ return MNL_CB_OK;
+ }
+
+ struct sockaddr_storage dst;
+ attr2addr(ctx->tb[RTA_DST], rm->rtm_family, &dst);
+
+ if (rm->rtm_dst_len == 0 ||
+ sockaddr_net_match(&dst, ctx->ip, rm->rtm_dst_len)) {
+ attr2addr(ctx->tb[RTA_PREFSRC], rm->rtm_family, ctx->src);
+ attr2addr(ctx->tb[RTA_GATEWAY], rm->rtm_family, ctx->via);
+ attr2dev(ctx->tb[RTA_OIF], ctx->dev);
+ ctx->match++;
+ ctx->priority = new_metric;
+ }
+
+ memset(ctx->tb, 0, sizeof(void *) * (RTA_MAX+1));
+ return MNL_CB_OK;
+}
+
+int ip_route_get(const struct sockaddr_storage *ip,
+ struct sockaddr_storage *via,
+ struct sockaddr_storage *src,
+ char *dev)
+{
+ struct sockaddr_storage last_via = { 0 };
+ ip_route_get_ctx_t ctx = { ip, &last_via, src, dev, 0, 0 };
+ do {
+ ctx.priority = UINT64_MAX;
+
+ size_t qextra_len;
+ void *qextra = sockaddr_raw(ip, &qextra_len);
+ int ret = netlink_query(ip->ss_family, RTM_GETROUTE,
+ ip_route_get_cb, &ctx, qextra,
+ qextra_len, IFA_ADDRESS);
+ if (ret != 0) {
+ return ret;
+ }
+ if (last_via.ss_family == ip->ss_family) { // not AF_UNSPEC
+ memcpy(via, &last_via, sizeof(*via));
+ }
+
+ // next loop will search for path to "via"
+ ctx.ip = via;
+ } while (last_via.ss_family != AF_UNSPEC &&
+ ctx.priority != UINT64_MAX && // avoid loop when nothing found
+ ctx.match < ROUTE_LOOKUP_LOOP_LIMIT); // avoid loop when looped route
+
+ return src->ss_family == ip->ss_family ? 0 : -ENOENT;
+}
+
+typedef struct {
+ const struct sockaddr_storage *ip;
+ uint8_t *mac;
+ unsigned match;
+
+ // intermediate callback data
+ const struct nlattr *tb[RTA_MAX+1];
+} ip_neigh_ctx_t;
+
+static int validate_attr_neigh(const struct nlattr *attr, void *data)
+{
+ /* skip unsupported attribute in user-space */
+ if (mnl_attr_type_valid(attr, NDA_MAX) < 0) {
+ return MNL_CB_OK;
+ }
+
+ ip_neigh_ctx_t *ctx = data;
+
+ int type = mnl_attr_get_type(attr);
+ switch (type) {
+ case NDA_DST:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, addr_len(ctx->ip->ss_family)) < 0) {
+ return MNL_CB_ERROR;
+ }
+ break;
+ case NDA_LLADDR:
+ if (mnl_attr_validate2(attr, MNL_TYPE_BINARY, ETH_ALEN) < 0) {
+ return MNL_CB_ERROR;
+ }
+ break;
+ }
+
+ ctx->tb[type] = attr;
+ return MNL_CB_OK;
+}
+
+static int ip_neigh_cb(const struct nlmsghdr *nlh, void *data)
+{
+ ip_neigh_ctx_t *ctx = data;
+ struct rtmsg *rm = mnl_nlmsg_get_payload(nlh);
+ if (rm->rtm_family != ctx->ip->ss_family) {
+ return MNL_CB_ERROR;
+ }
+
+ mnl_attr_parse(nlh, sizeof(*rm), validate_attr_neigh, data);
+
+ struct sockaddr_storage dst;
+ attr2addr(ctx->tb[NDA_DST], rm->rtm_family, &dst);
+
+ if (sockaddr_cmp((struct sockaddr_storage *)&dst, (struct sockaddr_storage *)ctx->ip, true) == 0 &&
+ ctx->tb[NDA_LLADDR] != NULL) {
+ memcpy(ctx->mac, mnl_attr_get_payload(ctx->tb[NDA_LLADDR]), ETH_ALEN);
+ ctx->match++;
+ }
+
+ memset(ctx->tb, 0, sizeof(void *) * (RTA_MAX+1));
+ return MNL_CB_OK;
+}
+
+int ip_neigh_get(const struct sockaddr_storage *ip, bool dummy_sendto, uint8_t *mac)
+{
+ if (dummy_sendto) {
+ int ret = send_dummy_pkt(ip);
+ if (ret < 0) {
+ return ret;
+ }
+ usleep(10000);
+ }
+ ip_neigh_ctx_t ctx = { ip, mac, 0 };
+ int ret = netlink_query(ip->ss_family, RTM_GETNEIGH, ip_neigh_cb, &ctx,
+ NULL, 0, 0);
+ if (ret == 0 && ctx.match == 0) {
+ return -ENOENT;
+ }
+ return ret;
+}
diff --git a/src/utils/kxdpgun/ip_route.h b/src/utils/kxdpgun/ip_route.h
new file mode 100644
index 0000000..c35c25b
--- /dev/null
+++ b/src/utils/kxdpgun/ip_route.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/socket.h>
+
+/*!
+ * \brief Get route to an IPv4/6 from system routing table.
+ *
+ * \param ip IPv4 or IPv6 to search route to.
+ * \param via Out: gateway (first hop) on the route, or AF_UNSPEC if same subnet.
+ * \param src Out: local outgoing IP address on the route.
+ * \param dev Out: local network interface on the route (you must pre-allocate IFNAMSIZ bytes!).
+ *
+ * \return 0 on success, negative errno otherwise.
+ */
+int ip_route_get(const struct sockaddr_storage *ip,
+ struct sockaddr_storage *via,
+ struct sockaddr_storage *src,
+ char *dev);
+
+/*!
+ * \brief Obtain neighbour's MAC addr from system neighbour table.
+ *
+ * \param ip IPv4 or IPv6 of the neighbour in question.
+ * \param dummy_sendto Attempt sendto() to target IP in order to let the system fill the neighbour table.
+ * \param mac Out: MAC address of the neighbour (you must pre-allocate ETH_ALEN bytes!).
+ *
+ * \return 0 on success, -ENOENT if neighbour not found, negative errno otherwise.
+ */
+int ip_neigh_get(const struct sockaddr_storage *ip, bool dummy_sendto, uint8_t *mac);
diff --git a/src/utils/kxdpgun/load_queries.c b/src/utils/kxdpgun/load_queries.c
new file mode 100644
index 0000000..fb421f9
--- /dev/null
+++ b/src/utils/kxdpgun/load_queries.c
@@ -0,0 +1,159 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "load_queries.h"
+#include "libknot/libknot.h"
+#include "utils/common/msg.h"
+
+#define ERR_PREFIX "failed loading queries "
+
+enum qflags {
+ QFLAG_EDNS = 1,
+ QFLAG_DO = 2,
+};
+
+struct pkt_payload *global_payloads = NULL;
+
+void free_global_payloads()
+{
+ struct pkt_payload *g_payloads_p = global_payloads, *tmp;
+ while (g_payloads_p != NULL) {
+ tmp = g_payloads_p;
+ g_payloads_p = tmp->next;
+ free(tmp);
+ }
+ global_payloads = NULL;
+}
+
+bool load_queries(const char *filename, uint16_t edns_size, uint16_t msgid)
+{
+ FILE *f = fopen(filename, "r");
+ if (f == NULL) {
+ ERR2(ERR_PREFIX "file '%s' (%s)", filename, strerror(errno));
+ return false;
+ }
+ struct pkt_payload *g_payloads_top = NULL;
+
+ struct {
+ char line[KNOT_DNAME_TXT_MAXLEN + 256];
+ char dname_txt[KNOT_DNAME_TXT_MAXLEN + 1];
+ uint8_t dname[KNOT_DNAME_MAXLEN];
+ char type_txt[128];
+ char flags_txt[128];
+ } *bufs;
+ bufs = malloc(sizeof(*bufs)); // avoiding too much stuff on stack
+ if (bufs == NULL) {
+ ERR2(ERR_PREFIX "(out of memory)");
+ goto fail;
+ }
+
+ while (fgets(bufs->line, sizeof(bufs->line), f) != NULL) {
+ bufs->flags_txt[0] = '\0';
+ int ret = sscanf(bufs->line, "%s%s%s", bufs->dname_txt, bufs->type_txt, bufs->flags_txt);
+ if (ret < 2) {
+ ERR2(ERR_PREFIX "(faulty line): '%.*s'",
+ (int)strcspn(bufs->line, "\n"), bufs->line);
+ goto fail;
+ }
+
+ void *pret = knot_dname_from_str(bufs->dname, bufs->dname_txt, sizeof(bufs->dname));
+ if (pret == NULL) {
+ ERR2(ERR_PREFIX "(faulty dname): '%s'", bufs->dname_txt);
+ goto fail;
+ }
+
+ uint16_t type;
+ ret = knot_rrtype_from_string(bufs->type_txt, &type);
+ if (ret < 0) {
+ ERR2(ERR_PREFIX "(faulty type): '%s'", bufs->type_txt);
+ goto fail;
+ }
+
+ enum qflags flags = 0;
+ switch (bufs->flags_txt[0]) {
+ case '\0':
+ break;
+ case 'e':
+ case 'E':
+ flags |= QFLAG_EDNS;
+ break;
+ case 'd':
+ case 'D':
+ flags |= QFLAG_EDNS | QFLAG_DO;
+ break;
+ default:
+ ERR2(ERR_PREFIX "(faulty flag): '%s'", bufs->flags_txt);
+ goto fail;
+ }
+
+ size_t dname_len = knot_dname_size(bufs->dname);
+ size_t pkt_len = KNOT_WIRE_HEADER_SIZE + 2 * sizeof(uint16_t) + dname_len;
+ if (flags & QFLAG_EDNS) {
+ pkt_len += KNOT_EDNS_MIN_SIZE;
+ }
+
+ struct pkt_payload *pkt = calloc(1, sizeof(struct pkt_payload) + pkt_len);
+ if (pkt == NULL) {
+ ERR2(ERR_PREFIX "(out of memory)");
+ goto fail;
+ }
+ pkt->len = pkt_len;
+ memcpy(pkt->payload, &msgid, sizeof(msgid));
+ pkt->payload[2] = 0x01; // QR bit
+ pkt->payload[5] = 0x01; // 1 question
+ pkt->payload[11] = (flags & QFLAG_EDNS) ? 0x01 : 0x00;
+ memcpy(pkt->payload + 12, bufs->dname, dname_len);
+ pkt->payload[dname_len + 12] = type >> 8;
+ pkt->payload[dname_len + 13] = type & 0xff;
+ pkt->payload[dname_len + 15] = KNOT_CLASS_IN;
+ if (flags & QFLAG_EDNS) {
+ pkt->payload[dname_len + 18] = KNOT_RRTYPE_OPT;
+ pkt->payload[dname_len + 19] = edns_size >> 8;
+ pkt->payload[dname_len + 20] = edns_size & 0xff;
+ pkt->payload[dname_len + 23] = (flags & QFLAG_DO) ? 0x80 : 0x00;
+ }
+
+ // add pkt to list global_payloads
+ if (g_payloads_top == NULL) {
+ global_payloads = pkt;
+ g_payloads_top = pkt;
+ } else {
+ g_payloads_top->next = pkt;
+ g_payloads_top = pkt;
+ }
+ }
+
+ if (global_payloads == NULL) {
+ ERR2(ERR_PREFIX "(no queries in file)");
+ goto fail;
+ }
+
+ free(bufs);
+ fclose(f);
+ return true;
+
+fail:
+ free_global_payloads();
+ free(bufs);
+ fclose(f);
+ return false;
+}
diff --git a/src/utils/kxdpgun/load_queries.h b/src/utils/kxdpgun/load_queries.h
new file mode 100644
index 0000000..368686b
--- /dev/null
+++ b/src/utils/kxdpgun/load_queries.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct pkt_payload {
+ struct pkt_payload *next;
+ size_t len;
+ uint8_t payload[];
+};
+
+extern struct pkt_payload *global_payloads;
+
+bool load_queries(const char *filename, uint16_t edns_size, uint16_t msgid);
+
+void free_global_payloads(void);
diff --git a/src/utils/kxdpgun/main.c b/src/utils/kxdpgun/main.c
new file mode 100644
index 0000000..2ec0c0c
--- /dev/null
+++ b/src/utils/kxdpgun/main.c
@@ -0,0 +1,1302 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <ifaddrs.h>
+#include <inttypes.h>
+#include <net/if.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include "libknot/libknot.h"
+#include "libknot/xdp.h"
+#include "libknot/xdp/tcp_iobuf.h"
+#ifdef ENABLE_QUIC
+#include <gnutls/gnutls.h>
+#include "libknot/xdp/quic.h"
+#endif // ENABLE_QUIC
+#include "contrib/macros.h"
+#include "contrib/mempattern.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/os.h"
+#include "contrib/sockaddr.h"
+#include "contrib/toeplitz.h"
+#include "contrib/ucw/mempool.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/kxdpgun/ip_route.h"
+#include "utils/kxdpgun/load_queries.h"
+
+#define PROGRAM_NAME "kxdpgun"
+#define SPACE " "
+
+enum {
+ KXDPGUN_WAIT,
+ KXDPGUN_START,
+ KXDPGUN_STOP,
+};
+
+volatile int xdp_trigger = KXDPGUN_WAIT;
+
+volatile unsigned stats_trigger = 0;
+
+unsigned global_cpu_aff_start = 0;
+unsigned global_cpu_aff_step = 1;
+
+#define REMOTE_PORT_DEFAULT 53
+#define REMOTE_PORT_DOQ_DEFAULT 853
+#define LOCAL_PORT_MIN 2000
+#define LOCAL_PORT_MAX 65535
+#define QUIC_THREAD_PORTS 100
+
+#define RCODE_MAX (0x0F + 1)
+
+typedef struct {
+ size_t collected;
+ uint64_t duration;
+ uint64_t qry_sent;
+ uint64_t synack_recv;
+ uint64_t ans_recv;
+ uint64_t finack_recv;
+ uint64_t rst_recv;
+ uint64_t size_recv;
+ uint64_t wire_recv;
+ uint64_t rcodes_recv[RCODE_MAX];
+ pthread_mutex_t mutex;
+} kxdpgun_stats_t;
+
+static kxdpgun_stats_t global_stats = { 0 };
+
+typedef enum {
+ KXDPGUN_IGNORE_NONE = 0,
+ KXDPGUN_IGNORE_QUERY = (1 << 0),
+ KXDPGUN_IGNORE_LASTBYTE = (1 << 1),
+ KXDPGUN_IGNORE_CLOSE = (1 << 2),
+} xdp_gun_ignore_t;
+
+typedef struct {
+ union {
+ struct sockaddr_in local_ip4;
+ struct sockaddr_in6 local_ip;
+ struct sockaddr_storage local_ip_ss;
+ };
+ union {
+ struct sockaddr_in target_ip4;
+ struct sockaddr_in6 target_ip;
+ struct sockaddr_storage target_ip_ss;
+ };
+ char dev[IFNAMSIZ];
+ uint64_t qps, duration;
+ unsigned at_once;
+ uint16_t msgid;
+ uint16_t edns_size;
+ uint16_t vlan_tci;
+ uint8_t local_mac[6], target_mac[6];
+ uint8_t local_ip_range;
+ bool ipv6;
+ bool tcp;
+ bool quic;
+ bool quic_full_handshake;
+ const char *sending_mode;
+ xdp_gun_ignore_t ignore1;
+ knot_tcp_ignore_t ignore2;
+ uint16_t target_port;
+ knot_xdp_filter_flag_t flags;
+ unsigned n_threads, thread_id;
+ knot_eth_rss_conf_t *rss_conf;
+} xdp_gun_ctx_t;
+
+const static xdp_gun_ctx_t ctx_defaults = {
+ .dev[0] = '\0',
+ .edns_size = 1232,
+ .qps = 1000,
+ .duration = 5000000UL, // usecs
+ .at_once = 10,
+ .sending_mode = "",
+ .target_port = 0,
+ .flags = KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_PASS,
+};
+
+static void sigterm_handler(int signo)
+{
+ assert(signo == SIGTERM || signo == SIGINT);
+ xdp_trigger = KXDPGUN_STOP;
+}
+
+static void sigusr_handler(int signo)
+{
+ assert(signo == SIGUSR1);
+ if (global_stats.collected == 0) {
+ stats_trigger++;
+ }
+}
+
+static void clear_stats(kxdpgun_stats_t *st)
+{
+ pthread_mutex_lock(&st->mutex);
+ st->duration = 0;
+ st->qry_sent = 0;
+ st->synack_recv = 0;
+ st->ans_recv = 0;
+ st->finack_recv = 0;
+ st->rst_recv = 0;
+ st->size_recv = 0;
+ st->wire_recv = 0;
+ st->collected = 0;
+ memset(st->rcodes_recv, 0, sizeof(st->rcodes_recv));
+ pthread_mutex_unlock(&st->mutex);
+}
+
+static size_t collect_stats(kxdpgun_stats_t *into, const kxdpgun_stats_t *what)
+{
+ pthread_mutex_lock(&into->mutex);
+ into->duration = MAX(into->duration, what->duration);
+ into->qry_sent += what->qry_sent;
+ into->synack_recv += what->synack_recv;
+ into->ans_recv += what->ans_recv;
+ into->finack_recv += what->finack_recv;
+ into->rst_recv += what->rst_recv;
+ into->size_recv += what->size_recv;
+ into->wire_recv += what->wire_recv;
+ for (int i = 0; i < RCODE_MAX; i++) {
+ into->rcodes_recv[i] += what->rcodes_recv[i];
+ }
+ size_t res = ++into->collected;
+ pthread_mutex_unlock(&into->mutex);
+ return res;
+}
+
+static void print_stats(kxdpgun_stats_t *st, bool tcp, bool quic, bool recv)
+{
+ pthread_mutex_lock(&st->mutex);
+
+#define ps(counter) ((counter) * 1000 / (st->duration / 1000))
+#define pct(counter) ((counter) * 100 / st->qry_sent)
+
+ const char *name = tcp ? "SYNs: " : quic ? "initials:" : "queries: ";
+ printf("total %s %"PRIu64" (%"PRIu64" pps)\n", name,
+ st->qry_sent, ps(st->qry_sent));
+ if (st->qry_sent > 0 && recv) {
+ if (tcp || quic) {
+ name = tcp ? "established:" : "handshakes: ";
+ printf("total %s %"PRIu64" (%"PRIu64" pps) (%"PRIu64"%%)\n", name,
+ st->synack_recv, ps(st->synack_recv), pct(st->synack_recv));
+ }
+ printf("total replies: %"PRIu64" (%"PRIu64" pps) (%"PRIu64"%%)\n",
+ st->ans_recv, ps(st->ans_recv), pct(st->ans_recv));
+ if (tcp) {
+ printf("total closed: %"PRIu64" (%"PRIu64" pps) (%"PRIu64"%%)\n",
+ st->finack_recv, ps(st->finack_recv), pct(st->finack_recv));
+ }
+ if (st->rst_recv > 0) {
+ printf("total reset: %"PRIu64" (%"PRIu64" pps) (%"PRIu64"%%)\n",
+ st->rst_recv, ps(st->rst_recv), pct(st->rst_recv));
+ }
+ printf("average DNS reply size: %"PRIu64" B\n",
+ st->ans_recv > 0 ? st->size_recv / st->ans_recv : 0);
+ printf("average Ethernet reply rate: %"PRIu64" bps (%.2f Mbps)\n",
+ ps(st->wire_recv * 8), ps((float)st->wire_recv * 8 / (1000 * 1000)));
+
+ for (int i = 0; i < RCODE_MAX; i++) {
+ if (st->rcodes_recv[i] > 0) {
+ const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, i);
+ const char *rcname = rcode == NULL ? "unknown" : rcode->name;
+ int space = MAX(9 - strlen(rcname), 0);
+ printf("responded %s: %.*s%"PRIu64"\n",
+ rcname, space, " ", st->rcodes_recv[i]);
+ }
+ }
+ }
+ printf("duration: %"PRIu64" s\n", (st->duration / (1000 * 1000)));
+
+ pthread_mutex_unlock(&st->mutex);
+}
+
+inline static void timer_start(struct timespec *timesp)
+{
+ clock_gettime(CLOCK_MONOTONIC, timesp);
+}
+
+inline static uint64_t timer_end(struct timespec *timesp)
+{
+ struct timespec end;
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ uint64_t res = (end.tv_sec - timesp->tv_sec) * (uint64_t)1000000;
+ res += ((int64_t)end.tv_nsec - timesp->tv_nsec) / 1000;
+ return res;
+}
+
+static unsigned addr_bits(bool ipv6)
+{
+ return ipv6 ? 128 : 32;
+}
+
+static void shuffle_sockaddr4(struct sockaddr_in *dst, struct sockaddr_in *src, uint64_t increment)
+{
+ memcpy(&dst->sin_addr, &src->sin_addr, sizeof(dst->sin_addr));
+ if (increment > 0) {
+ dst->sin_addr.s_addr = htobe32(be32toh(src->sin_addr.s_addr) + increment);
+ }
+}
+
+static void shuffle_sockaddr6(struct sockaddr_in6 *dst, struct sockaddr_in6 *src, uint64_t increment)
+{
+ memcpy(&dst->sin6_addr, &src->sin6_addr, sizeof(dst->sin6_addr));
+ if (increment > 0) {
+ uint64_t *dst_addr = (uint64_t *)&dst->sin6_addr;
+ uint64_t *src_addr = (uint64_t *)&src->sin6_addr;
+ dst_addr[1] = htobe64(be64toh(src_addr[1]) + increment);
+ }
+}
+
+static void shuffle_sockaddr(struct sockaddr_in6 *dst, struct sockaddr_in6 *src,
+ uint16_t port, uint64_t increment)
+{
+ dst->sin6_family = src->sin6_family;
+ dst->sin6_port = htobe16(port);
+ if (src->sin6_family == AF_INET6) {
+ shuffle_sockaddr6(dst, src, increment);
+ } else {
+ shuffle_sockaddr4((struct sockaddr_in *)dst, (struct sockaddr_in *)src, increment);
+ }
+}
+
+static void next_payload(struct pkt_payload **payload, int increment)
+{
+ if (*payload == NULL) {
+ *payload = global_payloads;
+ }
+ for (int i = 0; i < increment; i++) {
+ if ((*payload)->next == NULL) {
+ *payload = global_payloads;
+ } else {
+ *payload = (*payload)->next;
+ }
+ }
+}
+
+static void put_dns_payload(struct iovec *put_into, bool zero_copy, xdp_gun_ctx_t *ctx, struct pkt_payload **payl)
+{
+ if (zero_copy) {
+ put_into->iov_base = (*payl)->payload;
+ } else {
+ memcpy(put_into->iov_base, (*payl)->payload, (*payl)->len);
+ }
+ put_into->iov_len = (*payl)->len;
+ next_payload(payl, ctx->n_threads);
+}
+
+#ifdef ENABLE_QUIC
+static uint16_t get_rss_id(xdp_gun_ctx_t *ctx, uint16_t local_port)
+{
+ assert(ctx->rss_conf);
+
+ const uint8_t *key = (const uint8_t *)&(ctx->rss_conf->data[ctx->rss_conf->table_size]);
+ const size_t key_len = ctx->rss_conf->key_size;
+ uint8_t data[2 * sizeof(struct in6_addr) + 2 * sizeof(uint16_t)];
+
+ size_t addr_len;
+ if (ctx->ipv6) {
+ addr_len = sizeof(struct in6_addr);
+ memcpy(data, &ctx->target_ip.sin6_addr, addr_len);
+ memcpy(data + addr_len, &ctx->local_ip.sin6_addr, addr_len);
+ } else {
+ addr_len = sizeof(struct in_addr);
+ memcpy(data, &ctx->target_ip4.sin_addr, addr_len);
+ memcpy(data + addr_len, &ctx->local_ip4.sin_addr, addr_len);
+ }
+
+ uint16_t src_port = htobe16(ctx->target_port);
+ memcpy(data + 2 * addr_len, &src_port, sizeof(src_port));
+ uint16_t dst_port = htobe16(local_port);
+ memcpy(data + 2 * addr_len + sizeof(uint16_t), &dst_port, sizeof(dst_port));
+
+ size_t data_len = 2 * addr_len + 2 * sizeof(uint16_t);
+ uint16_t hash = toeplitz_hash(key, key_len, data, data_len);
+
+ return ctx->rss_conf->data[hash & ctx->rss_conf->mask];
+}
+
+static uint16_t adjust_port(xdp_gun_ctx_t *ctx, uint16_t local_port)
+{
+ assert(UINT16_MAX == LOCAL_PORT_MAX);
+
+ if (local_port < LOCAL_PORT_MIN) {
+ local_port = LOCAL_PORT_MIN;
+ }
+
+ if (ctx->rss_conf == NULL) {
+ return local_port;
+ }
+
+ for (int i = 0; i < UINT16_MAX; i++) {
+ if (ctx->thread_id == get_rss_id(ctx, local_port)) {
+ break;
+ }
+ local_port++;
+ if (local_port < LOCAL_PORT_MIN) {
+ local_port = LOCAL_PORT_MIN;
+ }
+ }
+
+ return local_port;
+}
+#endif // ENABLE_QUIC
+
+static unsigned alloc_pkts(knot_xdp_msg_t *pkts, struct knot_xdp_socket *xsk,
+ xdp_gun_ctx_t *ctx, uint64_t tick)
+{
+ uint64_t unique = (tick * ctx->n_threads + ctx->thread_id) * ctx->at_once;
+
+ knot_xdp_msg_flag_t flags = ctx->ipv6 ? KNOT_XDP_MSG_IPV6 : 0;
+ if (ctx->tcp) {
+ flags |= (KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_MSS);
+ } else if (ctx->quic) {
+ return ctx->at_once; // NOOP
+ }
+ if (ctx->vlan_tci != 0) {
+ flags |= KNOT_XDP_MSG_VLAN;
+ }
+
+ for (unsigned i = 0; i < ctx->at_once; i++) {
+ int ret = knot_xdp_send_alloc(xsk, flags, &pkts[i]);
+ if (ret != KNOT_EOK) {
+ return i;
+ }
+
+ uint16_t port_range = LOCAL_PORT_MAX - LOCAL_PORT_MIN + 1;
+ uint16_t local_port = LOCAL_PORT_MIN + unique % port_range;
+ uint64_t ip_incr = (unique / port_range) % (1 << (addr_bits(ctx->ipv6) - ctx->local_ip_range));
+ shuffle_sockaddr(&pkts[i].ip_from, &ctx->local_ip, local_port, ip_incr);
+ shuffle_sockaddr(&pkts[i].ip_to, &ctx->target_ip, ctx->target_port, 0);
+
+ memcpy(pkts[i].eth_from, ctx->local_mac, 6);
+ memcpy(pkts[i].eth_to, ctx->target_mac, 6);
+
+ pkts[i].vlan_tci = ctx->vlan_tci;
+
+ unique++;
+ }
+ return ctx->at_once;
+}
+
+inline static bool check_dns_payload(struct iovec *payl, xdp_gun_ctx_t *ctx,
+ kxdpgun_stats_t *st)
+{
+ if (payl->iov_len < KNOT_WIRE_HEADER_SIZE ||
+ memcmp(payl->iov_base, &ctx->msgid, sizeof(ctx->msgid)) != 0) {
+ return false;
+ }
+ st->rcodes_recv[((uint8_t *)payl->iov_base)[3] & 0x0F]++;
+ st->size_recv += payl->iov_len;
+ st->ans_recv++;
+ return true;
+}
+
+void *xdp_gun_thread(void *_ctx)
+{
+ xdp_gun_ctx_t *ctx = _ctx;
+ struct knot_xdp_socket *xsk;
+ struct timespec timer;
+ knot_xdp_msg_t pkts[ctx->at_once];
+ uint64_t errors = 0, lost = 0, duration = 0;
+ kxdpgun_stats_t local_stats = { 0 };
+ unsigned stats_triggered = 0;
+ knot_tcp_table_t *tcp_table = NULL;
+#ifdef ENABLE_QUIC
+ knot_xquic_table_t *quic_table = NULL;
+ struct knot_quic_creds *quic_creds = NULL;
+ knot_xdp_msg_t quic_fake_req = { 0 };
+ list_t quic_sessions;
+ init_list(&quic_sessions);
+#endif // ENABLE_QUIC
+ const uint64_t extra_wait = ctx->quic ? 4000000 : 1000000;
+
+ if (ctx->tcp) {
+ tcp_table = knot_tcp_table_new(ctx->qps, NULL);
+ if (tcp_table == NULL) {
+ ERR2("failed to allocate TCP connection table");
+ return NULL;
+ }
+ }
+ if (ctx->quic) {
+#ifdef ENABLE_QUIC
+ quic_creds = knot_xquic_init_creds(false, NULL, NULL);
+ if (quic_creds == NULL) {
+ ERR2("failed to initialize QUIC context");
+ return NULL;
+ }
+ quic_table = knot_xquic_table_new(ctx->qps * 100, SIZE_MAX, SIZE_MAX, 1232, quic_creds);
+ if (quic_table == NULL) {
+ ERR2("failed to allocate QUIC connection table");
+ return NULL;
+ }
+ ctx->target_ip.sin6_port = htobe16(ctx->target_port);
+
+ memcpy(quic_fake_req.eth_from, ctx->target_mac, sizeof(ctx->target_mac));
+ memcpy(quic_fake_req.eth_to, ctx->local_mac, sizeof(ctx->local_mac));
+ memcpy(&quic_fake_req.ip_from, &ctx->target_ip, sizeof(quic_fake_req.ip_from));
+ memcpy(&quic_fake_req.ip_to, &ctx->local_ip, sizeof(quic_fake_req.ip_to));
+ quic_fake_req.flags = ctx->ipv6 ? KNOT_XDP_MSG_IPV6 : 0;
+#else
+ assert(0);
+#endif // ENABLE_QUIC
+ }
+
+ knot_xdp_load_bpf_t mode = (ctx->thread_id == 0 ?
+ KNOT_XDP_LOAD_BPF_ALWAYS : KNOT_XDP_LOAD_BPF_NEVER);
+ /*
+ * This mutex prevents libbpf from logging:
+ * 'libbpf: can't get link by id (5535): Resource temporarily unavailable'
+ */
+ pthread_mutex_lock(&global_stats.mutex);
+ int ret = knot_xdp_init(&xsk, ctx->dev, ctx->thread_id, ctx->flags,
+ LOCAL_PORT_MIN, LOCAL_PORT_MIN, mode, NULL);
+ pthread_mutex_unlock(&global_stats.mutex);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to initialize XDP socket#%u (%s)",
+ ctx->thread_id, knot_strerror(ret));
+ knot_tcp_table_free(tcp_table);
+ return NULL;
+ }
+
+ if (ctx->thread_id == 0) {
+ INFO2("using interface %s, XDP threads %u, %s%s%s, %s mode",
+ ctx->dev, ctx->n_threads,
+ (ctx->tcp ? "TCP" : ctx->quic ? "QUIC" : "UDP"),
+ (ctx->sending_mode[0] != '\0' ? " mode " : ""),
+ (ctx->sending_mode[0] != '\0' ? ctx->sending_mode : ""),
+ (knot_eth_xdp_mode(if_nametoindex(ctx->dev)) == KNOT_XDP_MODE_FULL ?
+ "native" : "emulated"));
+ }
+
+ struct pollfd pfd = { knot_xdp_socket_fd(xsk), POLLIN, 0 };
+
+ while (xdp_trigger == KXDPGUN_WAIT) {
+ usleep(1000);
+ }
+
+ uint64_t tick = 0;
+ struct pkt_payload *payload_ptr = NULL;
+ next_payload(&payload_ptr, ctx->thread_id);
+
+#ifdef ENABLE_QUIC
+ knot_sweep_stats_t sweep_stats = { 0 };
+ uint16_t local_ports[QUIC_THREAD_PORTS];
+ uint16_t port = LOCAL_PORT_MIN;
+ for (int i = 0; i < QUIC_THREAD_PORTS; ++i) {
+ local_ports[i] = adjust_port(ctx, port);
+ port = local_ports[i] + 1;
+ assert(port >= LOCAL_PORT_MIN);
+ }
+ size_t local_ports_it = 0;
+#endif // ENABLE_QUIC
+
+ timer_start(&timer);
+
+ while (duration < ctx->duration + extra_wait) {
+
+ // sending part
+ if (duration < ctx->duration) {
+ while (1) {
+ knot_xdp_send_prepare(xsk);
+ unsigned alloced = alloc_pkts(pkts, xsk, ctx, tick);
+ if (alloced < ctx->at_once) {
+ lost += ctx->at_once - alloced;
+ if (alloced == 0) {
+ break;
+ }
+ }
+
+ if (ctx->tcp) {
+ for (int i = 0; i < alloced; i++) {
+ pkts[i].payload.iov_len = 0;
+ }
+ } else if (ctx->quic) {
+#ifdef ENABLE_QUIC
+ uint16_t local_port = local_ports[local_ports_it++ % QUIC_THREAD_PORTS];
+ for (unsigned i = 0; i < ctx->at_once; i++) {
+ knot_xquic_conn_t *newconn = NULL;
+ ctx->local_ip.sin6_port = htobe16(local_port);
+ ret = knot_xquic_client(quic_table, &ctx->target_ip, &ctx->local_ip, &newconn);
+ if (ret == KNOT_EOK) {
+ struct iovec tmp = { knot_xquic_stream_add_data(newconn, 0, NULL, payload_ptr->len), 0 };
+ put_dns_payload(&tmp, false, ctx, &payload_ptr);
+ if (EMPTY_LIST(quic_sessions)) {
+ newconn->streams_count = -1;
+ } else {
+ void *session = HEAD(quic_sessions);
+ rem_node(session);
+ (void)knot_xquic_session_load(newconn, session);
+ }
+ quic_fake_req.ip_to.sin6_port = htobe16(local_port);
+ ret = knot_xquic_send(quic_table, newconn, xsk, &quic_fake_req, KNOT_EOK, 1, (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE));
+ }
+ if (ret == KNOT_EOK) {
+ local_stats.qry_sent++;
+ }
+ }
+ (void)knot_xdp_send_finish(xsk);
+#endif // ENABLE_QUIC
+ break;
+ } else {
+ for (int i = 0; i < alloced; i++) {
+ put_dns_payload(&pkts[i].payload, false,
+ ctx, &payload_ptr);
+ }
+ }
+
+ uint32_t really_sent = 0;
+ if (knot_xdp_send(xsk, pkts, alloced, &really_sent) != KNOT_EOK) {
+ lost += alloced;
+ }
+ local_stats.qry_sent += really_sent;
+ (void)knot_xdp_send_finish(xsk);
+
+ break;
+ }
+ }
+
+ // receiving part
+ if (!(ctx->flags & KNOT_XDP_FILTER_DROP)) {
+ while (1) {
+ ret = poll(&pfd, 1, 0);
+ if (ret < 0) {
+ errors++;
+ break;
+ }
+ if (!pfd.revents) {
+ break;
+ }
+
+ uint32_t recvd = 0;
+ size_t wire = 0;
+ (void)knot_xdp_recv(xsk, pkts, ctx->at_once, &recvd, &wire);
+ if (recvd == 0) {
+ break;
+ }
+ if (ctx->tcp) {
+ knot_tcp_relay_t relays[recvd];
+ ret = knot_tcp_recv(relays, pkts, recvd, tcp_table, NULL, ctx->ignore2);
+ if (ret != KNOT_EOK) {
+ errors++;
+ break;
+ }
+
+ for (size_t i = 0; i < recvd; i++) {
+ knot_tcp_relay_t *rl = &relays[i];
+ struct iovec payl;
+ switch (rl->action) {
+ case XDP_TCP_ESTABLISH:
+ local_stats.synack_recv++;
+ if (ctx->ignore1 & KXDPGUN_IGNORE_QUERY) {
+ break;
+ }
+ put_dns_payload(&payl, true, ctx, &payload_ptr);
+ ret = knot_tcp_reply_data(rl, tcp_table,
+ (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE),
+ payl.iov_base, payl.iov_len);
+ if (ret != KNOT_EOK) {
+ errors++;
+ }
+ break;
+ case XDP_TCP_CLOSE:
+ local_stats.finack_recv++;
+ break;
+ case XDP_TCP_RESET:
+ local_stats.rst_recv++;
+ break;
+ default:
+ break;
+ }
+ for (size_t j = 0; j < rl->inbufs_count; j++) {
+ if (check_dns_payload(&rl->inbufs[j], ctx, &local_stats)) {
+ if (!(ctx->ignore1 & KXDPGUN_IGNORE_CLOSE)) {
+ rl->answer = XDP_TCP_CLOSE;
+ }
+ }
+ }
+ }
+
+ ret = knot_tcp_send(xsk, relays, recvd, ctx->at_once);
+ if (ret != KNOT_EOK) {
+ errors++;
+ }
+ (void)knot_xdp_send_finish(xsk);
+
+ knot_tcp_cleanup(tcp_table, relays, recvd);
+ } else if (ctx->quic) {
+#ifdef ENABLE_QUIC
+ knot_xquic_conn_t *relays[recvd];
+ for (size_t i = 0; i < recvd; i++) {
+ ret = knot_xquic_handle(quic_table, &pkts[i], 5000000000L, &relays[i]);
+ if (ret == KNOT_ECONN) {
+ local_stats.rst_recv++;
+ continue;
+ } else if (ret != 0) {
+ errors++;
+ break;
+ }
+
+ knot_xquic_conn_t *rl = relays[i];
+ if (rl == NULL) {
+ continue;
+ }
+
+ bool sess_ticket = (gnutls_session_get_flags(rl->tls_session) & GNUTLS_SFLAGS_SESSION_TICKET);
+
+ if (sess_ticket && !rl->session_taken && !ctx->quic_full_handshake) {
+ rl->session_taken = true;
+ void *session = knot_xquic_session_save(rl);
+ if (session != NULL) {
+ add_tail(&quic_sessions, session);
+ }
+ }
+
+ if (rl->handshake_done && rl->streams_count == -1) {
+ rl->streams_count = 1;
+
+ local_stats.synack_recv++;
+ if ((ctx->ignore1 & KXDPGUN_IGNORE_QUERY)) {
+ knot_xquic_table_rem(relays[i], quic_table);
+ knot_xquic_cleanup(&relays[i], 1);
+ relays[i] = NULL;
+ continue;
+ }
+ }
+ if (!rl->handshake_done && rl->streams_count == -1) {
+ continue;
+ }
+
+ knot_xquic_stream_t *stream0 = knot_xquic_conn_get_stream(rl, 0, false);
+ assert(stream0 != NULL);
+
+ if ((ctx->ignore2 & XDP_TCP_IGNORE_ESTABLISH)) {
+ knot_xquic_table_rem(relays[i], quic_table);
+ knot_xquic_cleanup(&relays[i], 1);
+ relays[i] = NULL;
+ local_stats.synack_recv++;
+ continue;
+ }
+
+ stream0 = knot_xquic_conn_get_stream(rl, 0, false);
+ if (stream0 != NULL && stream0->inbuf_fin != NULL) {
+ check_dns_payload(stream0->inbuf_fin, ctx, &local_stats);
+ free(stream0->inbuf_fin);
+ stream0->inbuf_fin = NULL;
+
+ if ((ctx->ignore2 & XDP_TCP_IGNORE_DATA_ACK)) {
+ knot_xquic_table_rem(relays[i], quic_table);
+ knot_xquic_cleanup(&relays[i], 1);
+ relays[i] = NULL;
+ continue;
+ }
+ }
+ ret = knot_xquic_send(quic_table, rl, xsk, &pkts[i], KNOT_EOK, 4, (ctx->ignore1 & KXDPGUN_IGNORE_LASTBYTE));
+ if (ret != KNOT_EOK) {
+ errors++;
+ }
+ }
+ knot_xquic_cleanup(relays, recvd);
+ (void)knot_xdp_send_finish(xsk);
+#endif // ENABLE_QUIC
+ } else {
+ for (int i = 0; i < recvd; i++) {
+ (void)check_dns_payload(&pkts[i].payload, ctx,
+ &local_stats);
+ }
+ }
+ local_stats.wire_recv += wire;
+ knot_xdp_recv_finish(xsk, pkts, recvd);
+ pfd.revents = 0;
+ }
+ }
+
+#ifdef ENABLE_QUIC
+ if (ctx->quic) {
+ (void)knot_xquic_table_sweep(quic_table, &sweep_stats);
+ }
+#endif // ENABLE_QUIC
+
+ // speed and signal part
+ uint64_t dura_exp = (local_stats.qry_sent * 1000000) / ctx->qps;
+ duration = timer_end(&timer);
+ if (xdp_trigger == KXDPGUN_STOP && ctx->duration > duration) {
+ ctx->duration = duration;
+ }
+ if (stats_trigger > stats_triggered) {
+ assert(stats_trigger == stats_triggered + 1);
+ stats_triggered++;
+
+ local_stats.duration = duration;
+ size_t collected = collect_stats(&global_stats, &local_stats);
+ assert(collected <= ctx->n_threads);
+ if (collected == ctx->n_threads) {
+ print_stats(&global_stats, ctx->tcp, ctx->quic,
+ !(ctx->flags & KNOT_XDP_FILTER_DROP));
+ clear_stats(&global_stats);
+ }
+ }
+ if (dura_exp > duration) {
+ usleep(dura_exp - duration);
+ }
+ if (duration > ctx->duration) {
+ usleep(1000);
+ }
+ tick++;
+ }
+
+ knot_xdp_deinit(xsk);
+
+ knot_tcp_table_free(tcp_table);
+#ifdef ENABLE_QUIC
+ knot_xquic_table_free(quic_table);
+ struct knot_quic_session *n, *nxt;
+ WALK_LIST_DELSAFE(n, nxt, quic_sessions) {
+ knot_xquic_session_load(NULL, n);
+ }
+ knot_xquic_free_creds(quic_creds);
+#endif // ENABLE_QUIC
+
+ char recv_str[40] = "", lost_str[40] = "", err_str[40] = "";
+ if (!(ctx->flags & KNOT_XDP_FILTER_DROP)) {
+ (void)snprintf(recv_str, sizeof(recv_str), ", received %"PRIu64, local_stats.ans_recv);
+ }
+ if (lost > 0) {
+ (void)snprintf(lost_str, sizeof(lost_str), ", lost %"PRIu64, lost);
+ }
+ if (errors > 0) {
+ (void)snprintf(err_str, sizeof(err_str), ", errors %"PRIu64, errors);
+ }
+ INFO2("thread#%02u: sent %"PRIu64"%s%s%s",
+ ctx->thread_id, local_stats.qry_sent, recv_str, lost_str, err_str);
+ local_stats.duration = ctx->duration;
+ collect_stats(&global_stats, &local_stats);
+
+ return NULL;
+}
+
+static int dev2mac(const char *dev, uint8_t *mac)
+{
+ struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return -errno;
+ }
+ strlcpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+ int ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
+ if (ret >= 0) {
+ memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
+ } else {
+ ret = -errno;
+ }
+ close(fd);
+ return ret;
+}
+
+static bool mac_empty(const uint8_t *mac)
+{
+ static const uint8_t unset_mac[6] = { 0 };
+ return (memcmp(mac, unset_mac, sizeof(unset_mac)) == 0);
+}
+
+static int mac_sscan(const char *src, uint8_t *dst)
+{
+ int tmp[6];
+ if (6 != sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], &tmp[5])) {
+ return KNOT_EINVAL;
+ }
+
+ for (int i = 0; i < 6; i++) {
+ dst[i] = (uint8_t)tmp[i];
+ }
+
+ return KNOT_EOK;
+}
+
+static bool configure_target(char *target_str, char *local_ip, xdp_gun_ctx_t *ctx)
+{
+ int val;
+ char *at = strrchr(target_str, '@');
+ if (at != NULL && (val = atoi(at + 1)) > 0 && val <= 0xffff) {
+ ctx->target_port = val;
+ *at = '\0';
+ }
+
+ ctx->ipv6 = false;
+ if (inet_pton(AF_INET, target_str, &ctx->target_ip4.sin_addr) <= 0) {
+ ctx->ipv6 = true;
+ ctx->target_ip.sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, target_str, &ctx->target_ip.sin6_addr) <= 0) {
+ ERR2("invalid target IP");
+ return false;
+ }
+ } else {
+ ctx->target_ip.sin6_family = AF_INET;
+ }
+
+ struct sockaddr_storage via = { 0 };
+ if (local_ip == NULL || ctx->dev[0] == '\0' || mac_empty(ctx->target_mac)) {
+ char auto_dev[IFNAMSIZ];
+ int ret = ip_route_get(&ctx->target_ip_ss,
+ &via,
+ &ctx->local_ip_ss,
+ (ctx->dev[0] == '\0') ? ctx->dev : auto_dev);
+ if (ret < 0) {
+ ERR2("can't find route to '%s' (%s)", target_str, strerror(-ret));
+ return false;
+ }
+ }
+
+ ctx->local_ip_range = addr_bits(ctx->ipv6); // by default use one IP
+ if (local_ip != NULL) {
+ at = strrchr(local_ip, '/');
+ if (at != NULL && (val = atoi(at + 1)) > 0 && val <= ctx->local_ip_range) {
+ ctx->local_ip_range = val;
+ *at = '\0';
+ }
+ if (ctx->ipv6) {
+ if (ctx->local_ip_range < 64 ||
+ inet_pton(AF_INET6, local_ip, &ctx->local_ip.sin6_addr) <= 0) {
+ ERR2("invalid local IPv6 or unsupported prefix length");
+ return false;
+ }
+ } else {
+ if (inet_pton(AF_INET, local_ip, &ctx->local_ip4.sin_addr) <= 0) {
+ ERR2("invalid local IPv4");
+ return false;
+ }
+ }
+ }
+
+ if (mac_empty(ctx->target_mac)) {
+ const struct sockaddr_storage *neigh = (via.ss_family == AF_UNSPEC) ?
+ &ctx->target_ip_ss : &via;
+ int ret = ip_neigh_get(neigh, true, ctx->target_mac);
+ if (ret < 0) {
+ char neigh_str[256] = { 0 };
+ sockaddr_tostr(neigh_str, sizeof(neigh_str), (struct sockaddr_storage *)neigh);
+ ERR2("failed to get remote MAC of target/gateway '%s' (%s)",
+ neigh_str, strerror(-ret));
+ return false;
+ }
+ }
+
+ if (mac_empty(ctx->local_mac)) {
+ int ret = dev2mac(ctx->dev, ctx->local_mac);
+ if (ret < 0) {
+ ERR2("failed to get MAC of device '%s' (%s)", ctx->dev, strerror(-ret));
+ return false;
+ }
+ }
+
+ int ret = knot_eth_queues(ctx->dev);
+ if (ret >= 0) {
+ ctx->n_threads = ret;
+ } else {
+ ERR2("unable to get number of queues for '%s' (%s)", ctx->dev,
+ knot_strerror(ret));
+ return false;
+ }
+
+ if (ctx->n_threads > 1 && ctx->quic) {
+ ret = knot_eth_rss(ctx->dev, &ctx->rss_conf);
+ if (ret != 0) {
+ WARN2("unable to read NIC RSS configuration for '%s' (%s)",
+ ctx->dev, knot_strerror(ret));
+ }
+ }
+
+ return true;
+}
+
+static void print_help(void)
+{
+ printf("Usage: %s [parameters] -i <queries_file> <dest_ip>\n"
+ "\n"
+ "Parameters:\n"
+ " -t, --duration <sec> "SPACE"Duration of traffic generation.\n"
+ " "SPACE" (default is %"PRIu64" seconds)\n"
+ " -T, --tcp[=debug_mode] "SPACE"Send queries over TCP.\n"
+ " -U, --quic[=debug_mode] "SPACE"Send queries over QUIC.\n"
+ " -Q, --qps <qps> "SPACE"Number of queries-per-second (approximately) to be sent.\n"
+ " "SPACE" (default is %"PRIu64" qps)\n"
+ " -b, --batch <size> "SPACE"Send queries in a batch of defined size.\n"
+ " "SPACE" (default is %d for UDP, %d for TCP)\n"
+ " -r, --drop "SPACE"Drop incoming responses (disables response statistics).\n"
+ " -p, --port <port> "SPACE"Remote destination port.\n"
+ " "SPACE" (default is %d for UDP/TCP, %u for QUIC)\n"
+ " -F, --affinity <spec> "SPACE"CPU affinity in the format [<cpu_start>][s<cpu_step>].\n"
+ " "SPACE" (default is %s)\n"
+ " -i, --infile <file> "SPACE"Path to a file with query templates.\n"
+ " -I, --interface <ifname> "SPACE"Override auto-detected interface for outgoing communication.\n"
+ " -l, --local <ip[/prefix]>"SPACE"Override auto-detected source IP address or subnet.\n"
+ " -L, --local-mac <MAC> "SPACE"Override auto-detected local MAC address.\n"
+ " -R, --remote-mac <MAC> "SPACE"Override auto-detected remote MAC address.\n"
+ " -v, --vlan <id> "SPACE"Add VLAN 802.1Q header with the given id.\n"
+ " -h, --help "SPACE"Print the program help.\n"
+ " -V, --version "SPACE"Print the program version.\n"
+ "\n"
+ "Arguments:\n"
+ " <dest_ip> "SPACE"IPv4 or IPv6 address of the remote destination.\n",
+ PROGRAM_NAME, ctx_defaults.duration / 1000000, ctx_defaults.qps,
+ ctx_defaults.at_once, 1, REMOTE_PORT_DEFAULT, REMOTE_PORT_DOQ_DEFAULT, "0s1");
+}
+
+static bool sending_mode(const char *arg, xdp_gun_ctx_t *ctx)
+{
+ if (arg == NULL) {
+ ctx->sending_mode = "";
+ return true;
+ } else if (strlen(arg) != 1) {
+ goto mode_invalid;
+ }
+ ctx->sending_mode = arg;
+
+ switch (ctx->sending_mode[0]) {
+ case '0':
+ if (!ctx->quic) {
+ goto mode_unavailable;
+ }
+ ctx->quic_full_handshake = true;
+ break;
+ case '1':
+ ctx->ignore1 = KXDPGUN_IGNORE_QUERY;
+ ctx->ignore2 = XDP_TCP_IGNORE_ESTABLISH | XDP_TCP_IGNORE_FIN;
+ break;
+ case '2':
+ ctx->ignore1 = KXDPGUN_IGNORE_QUERY;
+ break;
+ case '3':
+ ctx->ignore1 = KXDPGUN_IGNORE_QUERY;
+ ctx->ignore2 = XDP_TCP_IGNORE_FIN;
+ break;
+ case '5':
+ ctx->ignore1 = KXDPGUN_IGNORE_LASTBYTE;
+ ctx->ignore2 = XDP_TCP_IGNORE_FIN;
+ break;
+ case '7':
+ ctx->ignore1 = KXDPGUN_IGNORE_CLOSE;
+ ctx->ignore2 = XDP_TCP_IGNORE_DATA_ACK | XDP_TCP_IGNORE_FIN;
+ break;
+ case '8':
+ if (!ctx->tcp) {
+ goto mode_unavailable;
+ }
+ ctx->ignore1 = KXDPGUN_IGNORE_CLOSE;
+ ctx->ignore2 = XDP_TCP_IGNORE_FIN;
+ break;
+ case '9':
+ if (!ctx->tcp) {
+ goto mode_unavailable;
+ }
+ ctx->ignore2 = XDP_TCP_IGNORE_FIN;
+ break;
+ default:
+ goto mode_invalid;
+ }
+
+ return true;
+mode_unavailable:
+ ERR2("mode '%s' not available", optarg);
+ return false;
+mode_invalid:
+ ERR2("invalid mode '%s'", optarg);
+ return false;
+}
+
+static bool get_opts(int argc, char *argv[], xdp_gun_ctx_t *ctx)
+{
+ struct option opts[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { "duration", required_argument, NULL, 't' },
+ { "qps", required_argument, NULL, 'Q' },
+ { "batch", required_argument, NULL, 'b' },
+ { "drop", no_argument, NULL, 'r' },
+ { "port", required_argument, NULL, 'p' },
+ { "tcp", optional_argument, NULL, 'T' },
+ { "quic", optional_argument, NULL, 'U' },
+ { "affinity", required_argument, NULL, 'F' },
+ { "interface", required_argument, NULL, 'I' },
+ { "local", required_argument, NULL, 'l' },
+ { "infile", required_argument, NULL, 'i' },
+ { "local-mac", required_argument, NULL, 'L' },
+ { "remote-mac", required_argument, NULL, 'R' },
+ { "vlan", required_argument, NULL, 'v' },
+ { NULL }
+ };
+
+ int opt = 0, arg;
+ bool default_at_once = true;
+ double argf;
+ char *argcp, *local_ip = NULL;
+ while ((opt = getopt_long(argc, argv, "hVt:Q:b:rp:T::U::F:I:l:i:L:R:v:", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'h':
+ print_help();
+ exit(EXIT_SUCCESS);
+ case 'V':
+ print_version(PROGRAM_NAME);
+ exit(EXIT_SUCCESS);
+ case 't':
+ assert(optarg);
+ argf = atof(optarg);
+ if (argf > 0) {
+ ctx->duration = argf * 1000000.0;
+ assert(ctx->duration >= 1000);
+ } else {
+ ERR2("invalid duration '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'Q':
+ assert(optarg);
+ arg = atoi(optarg);
+ if (arg > 0) {
+ ctx->qps = arg;
+ } else {
+ ERR2("invalid QPS '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'b':
+ assert(optarg);
+ arg = atoi(optarg);
+ if (arg > 0) {
+ default_at_once = false;
+ ctx->at_once = arg;
+ } else {
+ ERR2("invalid batch size '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'r':
+ ctx->flags &= ~KNOT_XDP_FILTER_PASS;
+ ctx->flags |= KNOT_XDP_FILTER_DROP;
+ break;
+ case 'p':
+ assert(optarg);
+ arg = atoi(optarg);
+ if (arg > 0 && arg <= 0xffff) {
+ ctx->target_port = arg;
+ } else {
+ ERR2("invalid port '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'T':
+ ctx->tcp = true;
+ ctx->quic = false;
+ ctx->flags &= ~(KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_QUIC);
+ ctx->flags |= KNOT_XDP_FILTER_TCP;
+ if (default_at_once) {
+ ctx->at_once = 1;
+ }
+ if (!sending_mode(optarg, ctx)) {
+ return false;
+ }
+ break;
+ case 'U':
+#ifdef ENABLE_QUIC
+ ctx->quic = true;
+ ctx->tcp = false;
+ ctx->flags &= ~(KNOT_XDP_FILTER_UDP | KNOT_XDP_FILTER_TCP);
+ ctx->flags |= KNOT_XDP_FILTER_QUIC;
+ if (ctx->target_port == 0) {
+ ctx->target_port = REMOTE_PORT_DOQ_DEFAULT;
+ }
+ if (default_at_once) {
+ ctx->at_once = 1;
+ }
+ if (!sending_mode(optarg, ctx)) {
+ return false;
+ }
+#else
+ ERR2("QUIC not available");
+ return false;
+#endif // ENABLE_QUIC
+ break;
+ case 'F':
+ assert(optarg);
+ if ((arg = atoi(optarg)) > 0) {
+ global_cpu_aff_start = arg;
+ }
+ argcp = strchr(optarg, 's');
+ if (argcp != NULL && (arg = atoi(argcp + 1)) > 0) {
+ global_cpu_aff_step = arg;
+ }
+ break;
+ case 'I':
+ strlcpy(ctx->dev, optarg, IFNAMSIZ);
+ break;
+ case 'l':
+ local_ip = optarg;
+ break;
+ case 'i':
+ if (!load_queries(optarg, ctx->edns_size, ctx->msgid)) {
+ return false;
+ }
+ break;
+ case 'L':
+ if (mac_sscan(optarg, ctx->local_mac) != KNOT_EOK) {
+ ERR2("invalid local MAC address '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'R':
+ if (mac_sscan(optarg, ctx->target_mac) != KNOT_EOK) {
+ ERR2("invalid remote MAC address '%s'", optarg);
+ return false;
+ }
+ break;
+ case 'v':
+ assert(optarg);
+ arg = atoi(optarg);
+ if (arg > 0 && arg < 4095) {
+ uint16_t id = arg;
+ ctx->vlan_tci = htobe16(id);
+ } else {
+ ERR2("invalid VLAN id '%s'", optarg);
+ return false;
+ }
+ break;
+ default:
+ print_help();
+ return false;
+ }
+ }
+ if (global_payloads == NULL || argc - optind != 1) {
+ print_help();
+ return false;
+ }
+
+ if (ctx->target_port == 0) {
+ ctx->target_port = REMOTE_PORT_DEFAULT;
+ }
+
+ if (!configure_target(argv[optind], local_ip, ctx)) {
+ return false;
+ }
+
+ if (ctx->qps < ctx->n_threads) {
+ WARN2("QPS increased to the number of threads/queues: %u", ctx->n_threads);
+ ctx->qps = ctx->n_threads;
+ }
+ ctx->qps /= ctx->n_threads;
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ xdp_gun_ctx_t ctx = ctx_defaults, *thread_ctxs = NULL;
+ ctx.msgid = time(NULL) % UINT16_MAX;
+ pthread_t *threads = NULL;
+
+ if (!get_opts(argc, argv, &ctx)) {
+ free_global_payloads();
+ return EXIT_FAILURE;
+ }
+
+ thread_ctxs = calloc(ctx.n_threads, sizeof(*thread_ctxs));
+ threads = calloc(ctx.n_threads, sizeof(*threads));
+ if (thread_ctxs == NULL || threads == NULL) {
+ ERR2("out of memory");
+ free(thread_ctxs);
+ free(threads);
+ free_global_payloads();
+ return EXIT_FAILURE;
+ }
+ for (int i = 0; i < ctx.n_threads; i++) {
+ thread_ctxs[i] = ctx;
+ thread_ctxs[i].thread_id = i;
+ }
+
+ if (!linux_at_least(5, 11)) {
+ struct rlimit min_limit = { RLIM_INFINITY, RLIM_INFINITY }, cur_limit = { 0 };
+ if (getrlimit(RLIMIT_MEMLOCK, &cur_limit) != 0 ||
+ cur_limit.rlim_cur != min_limit.rlim_cur ||
+ cur_limit.rlim_max != min_limit.rlim_max) {
+ int ret = setrlimit(RLIMIT_MEMLOCK, &min_limit);
+ if (ret != 0) {
+ WARN2("unable to increase RLIMIT_MEMLOCK: %s",
+ strerror(errno));
+ }
+ }
+ }
+
+ pthread_mutex_init(&global_stats.mutex, NULL);
+
+ struct sigaction stop_action = { .sa_handler = sigterm_handler };
+ struct sigaction stats_action = { .sa_handler = sigusr_handler };
+ sigaction(SIGINT, &stop_action, NULL);
+ sigaction(SIGTERM, &stop_action, NULL);
+ sigaction(SIGUSR1, &stats_action, NULL);
+
+ for (size_t i = 0; i < ctx.n_threads; i++) {
+ unsigned affinity = global_cpu_aff_start + i * global_cpu_aff_step;
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(affinity, &set);
+ (void)pthread_create(&threads[i], NULL, xdp_gun_thread, &thread_ctxs[i]);
+ int ret = pthread_setaffinity_np(threads[i], sizeof(cpu_set_t), &set);
+ if (ret != 0) {
+ WARN2("failed to set affinity of thread#%zu to CPU#%u", i, affinity);
+ }
+ usleep(20000);
+ }
+ usleep(1000000);
+
+ xdp_trigger = KXDPGUN_START;
+ usleep(1000000);
+
+ for (size_t i = 0; i < ctx.n_threads; i++) {
+ pthread_join(threads[i], NULL);
+ }
+ if (global_stats.duration > 0 && global_stats.qry_sent > 0) {
+ print_stats(&global_stats, ctx.tcp, ctx.quic, !(ctx.flags & KNOT_XDP_FILTER_DROP));
+ }
+ pthread_mutex_destroy(&global_stats.mutex);
+
+ free(ctx.rss_conf);
+ free(thread_ctxs);
+ free(threads);
+ free_global_payloads();
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c
new file mode 100644
index 0000000..3b40950
--- /dev/null
+++ b/src/utils/kzonecheck/main.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <libgen.h>
+#include <stdio.h>
+
+#include "contrib/time.h"
+#include "contrib/tolower.h"
+#include "libknot/libknot.h"
+#include "knot/common/log.h"
+#include "knot/zone/semantic-check.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/kzonecheck/zone_check.h"
+
+#define PROGRAM_NAME "kzonecheck"
+
+#define STDIN_SUBST "-"
+#define STDIN_REPL "/dev/stdin"
+
+static void print_help(void)
+{
+ printf("Usage: %s [parameters] <filename>\n"
+ "\n"
+ "Parameters:\n"
+ " -o, --origin <zone_origin> Zone name.\n"
+ " (default filename without .zone)\n"
+ " -d, --dnssec <on|off> Also check DNSSEC-related records.\n"
+ " -t, --time <timestamp> Current time specification.\n"
+ " (default current UNIX time)\n"
+ " -v, --verbose Enable debug output.\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n"
+ "\n",
+ PROGRAM_NAME);
+}
+
+static bool str2bool(const char *s)
+{
+ switch (knot_tolower(s[0])) {
+ case '1':
+ case 'y':
+ case 't':
+ return true;
+ case 'o':
+ return knot_tolower(s[1]) == 'n';
+ default:
+ return false;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ const char *origin = NULL;
+ bool verbose = false;
+ semcheck_optional_t optional = SEMCHECK_DNSSEC_AUTO; // default value for --dnssec
+ knot_time_t check_time = (knot_time_t)time(NULL);
+
+ /* Long options. */
+ struct option opts[] = {
+ { "origin", required_argument, NULL, 'o' },
+ { "time", required_argument, NULL, 't' },
+ { "dnssec", required_argument, NULL, 'd' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ /* Set the time zone. */
+ tzset();
+
+ /* Parse command line arguments */
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "o:t:d:vVh", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'o':
+ origin = optarg;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'h':
+ print_help();
+ return EXIT_SUCCESS;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ return EXIT_SUCCESS;
+ case 'd':
+ optional = str2bool(optarg) ? SEMCHECK_DNSSEC_ON : SEMCHECK_DNSSEC_OFF;
+ break;
+ case 't':
+ if (knot_time_parse("YMDhms|#|+-#U|+-#",
+ optarg, &check_time) != KNOT_EOK) {
+ ERR2("unknown time format");
+ return EXIT_FAILURE;
+ }
+ break;
+ default:
+ print_help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ /* Check if there's at least one remaining non-option. */
+ if (optind >= argc) {
+ ERR2("expected zone file name");
+ print_help();
+ return EXIT_FAILURE;
+ }
+
+ char *filename = argv[optind];
+ if (strncmp(filename, STDIN_SUBST, sizeof(STDIN_SUBST)) == 0) {
+ filename = STDIN_REPL;
+ }
+
+ char *zonename;
+ if (origin == NULL) {
+ /* Get zone name from file name. */
+ const char *ext = ".zone";
+ zonename = basename(filename);
+ if (strcmp(zonename + strlen(zonename) - strlen(ext), ext) == 0) {
+ zonename = strndup(zonename, strlen(zonename) - strlen(ext));
+ } else {
+ zonename = strdup(zonename);
+ }
+ } else {
+ zonename = strdup(origin);
+ }
+
+ log_init();
+ log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, 0);
+ log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, 0);
+ log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0);
+ log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO);
+ if (verbose) {
+ log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_UPTO(LOG_DEBUG));
+ }
+
+ knot_dname_t *dname = knot_dname_from_str_alloc(zonename);
+ knot_dname_to_lower(dname);
+ free(zonename);
+ int ret = zone_check(filename, dname, optional, (time_t)check_time);
+ knot_dname_free(dname, NULL);
+
+ log_close();
+
+ switch (ret) {
+ case KNOT_EOK:
+ if (verbose) {
+ INFO2("No semantic error found");
+ }
+ return EXIT_SUCCESS;
+ case KNOT_EZONEINVAL:
+ INFO2("Serious semantic error detected");
+ // FALLTHROUGH
+ case KNOT_ESEMCHECK:
+ return EXIT_FAILURE;
+ case KNOT_EACCES:
+ case KNOT_EFILE:
+ ERR2("failed to load the zone file");
+ return EXIT_FAILURE;
+ default:
+ ERR2("failed to run semantic checks (%s)", knot_strerror(ret));
+ return EXIT_FAILURE;
+ }
+}
diff --git a/src/utils/kzonecheck/zone_check.c b/src/utils/kzonecheck/zone_check.c
new file mode 100644
index 0000000..55561a5
--- /dev/null
+++ b/src/utils/kzonecheck/zone_check.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 2022 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 <stdio.h>
+#include <assert.h>
+
+#include "utils/kzonecheck/zone_check.h"
+
+#include "knot/zone/contents.h"
+#include "knot/zone/zonefile.h"
+#include "utils/common/msg.h"
+
+typedef struct {
+ sem_handler_t handler;
+ unsigned errors[SEM_ERR_UNKNOWN + 1]; /*!< Counting errors by type. */
+ unsigned error_count; /*!< Total error count. */
+} err_handler_stats_t;
+
+static void err_callback(sem_handler_t *handler, const zone_contents_t *zone,
+ const knot_dname_t *node, sem_error_t error, const char *data)
+{
+ assert(handler != NULL);
+ assert(zone != NULL);
+ err_handler_stats_t *stats = (err_handler_stats_t *)handler;
+
+ knot_dname_txt_storage_t buff;
+ char *owner = knot_dname_to_str(buff, (node != NULL ? node : zone->apex->owner),
+ sizeof(buff));
+ if (owner == NULL) {
+ owner = "";
+ }
+
+ printf("[%s] %s%s%s\n", owner, sem_error_msg(error),
+ (data != NULL ? " " : ""),
+ (data != NULL ? data : ""));
+
+ stats->errors[error]++;
+ stats->error_count++;
+}
+
+static void print_statistics(err_handler_stats_t *stats)
+{
+ printf("\nError summary:\n");
+ for (sem_error_t i = 0; i <= SEM_ERR_UNKNOWN; ++i) {
+ if (stats->errors[i] > 0) {
+ printf("%4u\t%s\n", stats->errors[i], sem_error_msg(i));
+ }
+ }
+}
+
+int zone_check(const char *zone_file, const knot_dname_t *zone_name,
+ semcheck_optional_t optional, time_t time)
+{
+ err_handler_stats_t stats = {
+ .handler = { .cb = err_callback },
+ };
+
+ zloader_t zl;
+ int ret = zonefile_open(&zl, zone_file, zone_name, optional, time);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+ zl.err_handler = (sem_handler_t *)&stats;
+ zl.creator->master = true;
+
+ zone_contents_t *contents = zonefile_load(&zl);
+ zonefile_close(&zl);
+ if (contents == NULL && !stats.handler.error) {
+ return KNOT_ERROR;
+ }
+ zone_contents_deep_free(contents);
+
+ if (stats.error_count > 0) {
+ print_statistics(&stats);
+ return stats.handler.error ? KNOT_EZONEINVAL : KNOT_ESEMCHECK;
+ } else {
+ return KNOT_EOK;
+ }
+}
diff --git a/src/utils/kzonecheck/zone_check.h b/src/utils/kzonecheck/zone_check.h
new file mode 100644
index 0000000..28ebb88
--- /dev/null
+++ b/src/utils/kzonecheck/zone_check.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#pragma once
+
+#include "knot/zone/semantic-check.h"
+#include "libknot/libknot.h"
+
+int zone_check(const char *zone_file, const knot_dname_t *zone_name,
+ semcheck_optional_t optional, time_t time);
diff --git a/src/utils/kzonesign/main.c b/src/utils/kzonesign/main.c
new file mode 100644
index 0000000..66786a0
--- /dev/null
+++ b/src/utils/kzonesign/main.c
@@ -0,0 +1,276 @@
+/* Copyright (C) 2022 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 <getopt.h>
+#include <stdlib.h>
+
+#include "knot/dnssec/zone-events.h"
+#include "knot/updates/zone-update.h"
+#include "knot/server/server.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/zone-load.h"
+#include "knot/zone/zonefile.h"
+#include "utils/common/msg.h"
+#include "utils/common/params.h"
+#include "utils/common/util_conf.h"
+#include "contrib/strtonum.h"
+
+#define PROGRAM_NAME "kzonesign"
+
+static void print_help(void)
+{
+ printf("Usage: %s [-c | -C <path>] [parameters] <zone_name>\n"
+ "\n"
+ "Parameters:\n"
+ " -c, --config <file> Path to a textual configuration file.\n"
+ " (default %s)\n"
+ " -C, --confdb <dir> Path to a configuration database directory.\n"
+ " (default %s)\n"
+ " -o, --outdir <dir_name> Output directory.\n"
+ " -r, --rollover Allow key rollovers and NSEC3 re-salt.\n"
+ " -v, --verify Only verify if zone is signed correctly.\n"
+ " -t, --time <timestamp> Current time specification.\n"
+ " (default current UNIX time)\n"
+ " -h, --help Print the program help.\n"
+ " -V, --version Print the program version.\n"
+ "\n",
+ PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR);
+}
+
+typedef struct {
+ const char *zone_name_str;
+ knot_dname_storage_t zone_name;
+ const char *outdir;
+ zone_sign_roll_flags_t rollover;
+ int64_t timestamp;
+ bool verify;
+} sign_params_t;
+
+static int zonesign(sign_params_t *params)
+{
+ char *zonefile = NULL;
+ zone_contents_t *unsigned_conts = NULL;
+ zone_t *zone_struct = NULL;
+ zone_update_t up = { 0 };
+ server_t fake_server = { 0 };
+ zone_sign_reschedule_t next_sign = { 0 };
+ int ret = KNOT_ERROR;
+
+ conf_val_t val = conf_zone_get(conf(), C_DOMAIN, params->zone_name);
+ if (val.code != KNOT_EOK) {
+ ERR2("zone '%s' not configured", params->zone_name_str);
+ ret = KNOT_ENOENT;
+ goto fail;
+ }
+ val = conf_zone_get(conf(), C_DNSSEC_POLICY, params->zone_name);
+ if (val.code != KNOT_EOK) {
+ WARN2("DNSSEC policy not configured for zone '%s', taking defaults",
+ params->zone_name_str);
+ }
+
+ zone_struct = zone_new(params->zone_name);
+ if (zone_struct == NULL) {
+ ERR2("out of memory");
+ ret = KNOT_ENOMEM;
+ goto fail;
+ }
+
+ ret = zone_load_contents(conf(), params->zone_name, &unsigned_conts,
+ SEMCHECK_MANDATORY_SOFT, false);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to load zone contents (%s)", knot_strerror(ret));
+ goto fail;
+ }
+
+ ret = zone_update_from_contents(&up, zone_struct, unsigned_conts, UPDATE_FULL);
+ if (ret != KNOT_EOK) {
+ ERR2("failed to initialize zone update (%s)", knot_strerror(ret));
+ zone_contents_deep_free(unsigned_conts);
+ goto fail;
+ }
+
+ if (params->verify) {
+ val = conf_zone_get(conf(), C_ADJUST_THR, params->zone_name);
+ ret = zone_adjust_full(up.new_cont, conf_int(&val));
+ if (ret != KNOT_EOK) {
+ ERR2("failed to adjust the zone (%s)", knot_strerror(ret));
+ zone_update_clear(&up);
+ goto fail;
+ }
+
+ ret = knot_dnssec_validate_zone(&up, conf(), params->timestamp, false);
+ if (ret != KNOT_EOK) {
+ ERR2("DNSSEC validation failed (%s)", knot_strerror(ret));
+ char type_str[16];
+ knot_dname_txt_storage_t name_str;
+ if (knot_dname_to_str(name_str, up.validation_hint.node, sizeof(name_str)) != NULL &&
+ knot_rrtype_to_string(up.validation_hint.rrtype, type_str, sizeof(type_str)) >= 0) {
+ ERR2("affected node: '%s' type '%s'", name_str, type_str);
+ }
+ } else {
+ INFO2("DNSSEC validation successful");
+ }
+ zone_update_clear(&up);
+ goto fail;
+ }
+
+ kasp_db_ensure_init(&fake_server.kaspdb, conf());
+ zone_struct->server = &fake_server;
+
+ ret = knot_dnssec_zone_sign(&up, conf(), 0, params->rollover,
+ params->timestamp, &next_sign);
+ if (ret == KNOT_DNSSEC_ENOKEY) { // exception: allow generating initial keys
+ params->rollover = KEY_ROLL_ALLOW_ALL;
+ ret = knot_dnssec_zone_sign(&up, conf(), 0, params->rollover,
+ params->timestamp, &next_sign);
+ }
+ if (ret != KNOT_EOK) {
+ ERR2("failed to sign the zone (%s)", knot_strerror(ret));
+ zone_update_clear(&up);
+ goto fail;
+ }
+
+ if (params->outdir == NULL) {
+ zonefile = conf_zonefile(conf(), params->zone_name);
+ ret = zonefile_write(zonefile, up.new_cont);
+ } else {
+ zone_contents_t *temp = zone_struct->contents;
+ zone_struct->contents = up.new_cont;
+ ret = zone_dump_to_dir(conf(), zone_struct, params->outdir);
+ zone_struct->contents = temp;
+ }
+ zone_update_clear(&up);
+ if (ret != KNOT_EOK) {
+ if (params->outdir == NULL) {
+ ERR2("failed to update zone file '%s' (%s)",
+ zonefile, knot_strerror(ret));
+ } else {
+ ERR2("failed to flush signed zone to '%s' file (%s)",
+ params->outdir, knot_strerror(ret));
+
+ }
+ goto fail;
+ }
+
+ INFO2("Next signing: %"KNOT_TIME_PRINTF, next_sign.next_sign);
+ if (params->rollover) {
+ INFO2("Next roll-over: %"KNOT_TIME_PRINTF, next_sign.next_rollover);
+ if (next_sign.next_nsec3resalt) {
+ INFO2("Next NSEC3 re-salt: %"KNOT_TIME_PRINTF, next_sign.next_nsec3resalt);
+ }
+ if (next_sign.plan_ds_check) {
+ INFO2("KSK submission to parent zone needed");
+ }
+ }
+
+fail:
+ if (fake_server.kaspdb.path != NULL) {
+ knot_lmdb_deinit(&fake_server.kaspdb);
+ }
+ zone_free(&zone_struct);
+ free(zonefile);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ sign_params_t params = { 0 };
+
+ struct option opts[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "confdb", required_argument, NULL, 'C' },
+ { "outdir", required_argument, NULL, 'o' },
+ { "rollover", no_argument, NULL, 'r' },
+ { "verify" , no_argument, NULL, 'v' },
+ { "time", required_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL }
+ };
+
+ tzset();
+
+ int opt = 0;
+ while ((opt = getopt_long(argc, argv, "c:C:o:rvt:hV", opts, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ if (util_conf_init_file(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'C':
+ if (util_conf_init_confdb(optarg) != KNOT_EOK) {
+ goto failure;
+ }
+ break;
+ case 'o':
+ params.outdir = optarg;
+ break;
+ case 'r':
+ params.rollover = KEY_ROLL_ALLOW_ALL;
+ break;
+ case 'v':
+ params.verify = true;
+ break;
+ case 't':
+ ; uint32_t num = 0;
+ if (str_to_u32(optarg, &num) != KNOT_EOK || num == 0) {
+ print_help();
+ goto failure;
+ }
+ params.timestamp = num;
+ break;
+ case 'h':
+ print_help();
+ goto success;
+ case 'V':
+ print_version(PROGRAM_NAME);
+ goto success;
+ default:
+ print_help();
+ goto failure;
+ }
+ }
+ if (argc - optind != 1) {
+ ERR2("missing zone name");
+ print_help();
+ goto failure;
+ }
+ params.zone_name_str = argv[optind];
+ if (knot_dname_from_str(params.zone_name, params.zone_name_str,
+ sizeof(params.zone_name)) == NULL) {
+ ERR2("invalid zone name '%s'", params.zone_name_str);
+ print_help();
+ goto failure;
+ }
+ knot_dname_to_lower(params.zone_name);
+
+ if (util_conf_init_default(false) != KNOT_EOK) {
+ goto failure;
+ }
+
+ if (zonesign(&params) != KNOT_EOK) {
+ goto failure;
+ }
+
+success:
+ util_conf_deinit();
+ return EXIT_SUCCESS;
+failure:
+ util_conf_deinit();
+ return EXIT_FAILURE;
+}
diff --git a/test-driver b/test-driver
new file mode 100755
index 0000000..9759384
--- /dev/null
+++ b/test-driver
@@ -0,0 +1,150 @@
+#! /bin/sh
+# test-driver - basic testsuite driver script.
+
+scriptversion=2018-03-07.03; # UTC
+
+# Copyright (C) 2011-2020 Free Software Foundation, Inc.
+#
+# 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 2, 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/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# This file is maintained in Automake, please report
+# bugs to <bug-automake@gnu.org> or send patches to
+# <automake-patches@gnu.org>.
+
+# Make unconditional expansion of undefined variables an error. This
+# helps a lot in preventing typo-related bugs.
+set -u
+
+usage_error ()
+{
+ echo "$0: $*" >&2
+ print_usage >&2
+ exit 2
+}
+
+print_usage ()
+{
+ cat <<END
+Usage:
+ test-driver --test-name NAME --log-file PATH --trs-file PATH
+ [--expect-failure {yes|no}] [--color-tests {yes|no}]
+ [--enable-hard-errors {yes|no}] [--]
+ TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
+
+The '--test-name', '--log-file' and '--trs-file' options are mandatory.
+See the GNU Automake documentation for information.
+END
+}
+
+test_name= # Used for reporting.
+log_file= # Where to save the output of the test script.
+trs_file= # Where to save the metadata of the test run.
+expect_failure=no
+color_tests=no
+enable_hard_errors=yes
+while test $# -gt 0; do
+ case $1 in
+ --help) print_usage; exit $?;;
+ --version) echo "test-driver $scriptversion"; exit $?;;
+ --test-name) test_name=$2; shift;;
+ --log-file) log_file=$2; shift;;
+ --trs-file) trs_file=$2; shift;;
+ --color-tests) color_tests=$2; shift;;
+ --expect-failure) expect_failure=$2; shift;;
+ --enable-hard-errors) enable_hard_errors=$2; shift;;
+ --) shift; break;;
+ -*) usage_error "invalid option: '$1'";;
+ *) break;;
+ esac
+ shift
+done
+
+missing_opts=
+test x"$test_name" = x && missing_opts="$missing_opts --test-name"
+test x"$log_file" = x && missing_opts="$missing_opts --log-file"
+test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
+if test x"$missing_opts" != x; then
+ usage_error "the following mandatory options are missing:$missing_opts"
+fi
+
+if test $# -eq 0; then
+ usage_error "missing argument"
+fi
+
+if test $color_tests = yes; then
+ # Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
+ red='' # Red.
+ grn='' # Green.
+ lgn='' # Light green.
+ blu='' # Blue.
+ mgn='' # Magenta.
+ std='' # No color.
+else
+ red= grn= lgn= blu= mgn= std=
+fi
+
+do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
+trap "st=129; $do_exit" 1
+trap "st=130; $do_exit" 2
+trap "st=141; $do_exit" 13
+trap "st=143; $do_exit" 15
+
+# Test script is run here.
+"$@" >$log_file 2>&1
+estatus=$?
+
+if test $enable_hard_errors = no && test $estatus -eq 99; then
+ tweaked_estatus=1
+else
+ tweaked_estatus=$estatus
+fi
+
+case $tweaked_estatus:$expect_failure in
+ 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
+ 0:*) col=$grn res=PASS recheck=no gcopy=no;;
+ 77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
+ 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
+ *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
+ *:*) col=$red res=FAIL recheck=yes gcopy=yes;;
+esac
+
+# Report the test outcome and exit status in the logs, so that one can
+# know whether the test passed or failed simply by looking at the '.log'
+# file, without the need of also peaking into the corresponding '.trs'
+# file (automake bug#11814).
+echo "$res $test_name (exit status: $estatus)" >>$log_file
+
+# Report outcome to console.
+echo "${col}${res}${std}: $test_name"
+
+# Register the test result, and other relevant metadata.
+echo ":test-result: $res" > $trs_file
+echo ":global-test-result: $res" >> $trs_file
+echo ":recheck: $recheck" >> $trs_file
+echo ":copy-in-global-log: $gcopy" >> $trs_file
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/tests-fuzz/Makefile.am b/tests-fuzz/Makefile.am
new file mode 100644
index 0000000..dcf18a6
--- /dev/null
+++ b/tests-fuzz/Makefile.am
@@ -0,0 +1,130 @@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src \
+ -DCONFIG_DIR='"${config_dir}"' \
+ -DSTORAGE_DIR='"${storage_dir}"' \
+ -DRUN_DIR='"${run_dir}"' \
+ -DSRCDIR=\"$(abs_srcdir)\"
+
+FUZZERS = \
+ fuzz_packet \
+ fuzz_zscanner \
+ fuzz_dname_to_str \
+ fuzz_dname_from_str
+
+check_PROGRAMS = $(FUZZERS)
+
+if HAVE_DAEMON
+# knotd_stdio (AFL)
+check_PROGRAMS += knotd_stdio
+
+nodist_knotd_stdio_SOURCES = knotd_wrap/main.c
+
+knotd_stdio_SOURCES = \
+ knotd_wrap/afl-loop.h \
+ knotd_wrap/server.c \
+ knotd_wrap/tcp-handler.c \
+ knotd_wrap/udp-handler.c
+
+knotd_stdio_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(libkqueue_CFLAGS) \
+ $(liburcu_CFLAGS) \
+ $(lmdb_CFLAGS) \
+ $(systemd_CFLAGS)
+
+knotd_stdio_LDADD = \
+ $(top_builddir)/src/libknotd.la \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la \
+ $(cap_ng_LIBS) \
+ $(gnutls_LIBS) \
+ $(liburcu_LIBS) \
+ $(lmdb_LIBS) \
+ $(systemd_LIBS)
+
+BUILT_SOURCES = knotd_wrap/main.c
+CLEANFILES = knotd_wrap/main.c
+knotd_wrap/main.c: $(top_builddir)/src/utils/knotd/main.c
+ @echo '#include "knotd_wrap/afl-loop.h"' > $@
+ @$(SED) -e 's/for (;;)/while (__AFL_LOOP(10000))/' $(top_srcdir)/src/utils/knotd/main.c >> $@
+endif HAVE_DAEMON
+
+# Simple function wrappers (AFL, libFuzzer, tests)
+
+fuzz_packet_SOURCES = fuzz_packet.c
+fuzz_packet_CPPFLAGS = $(AM_CPPFLAGS)
+fuzz_packet_LDADD = \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(lmdb_LIBS)
+
+fuzz_zscanner_SOURCES = fuzz_zscanner.c
+fuzz_zscanner_CPPFLAGS = $(AM_CPPFLAGS)
+fuzz_zscanner_LDADD = \
+ $(top_builddir)/src/libzscanner.la
+
+fuzz_dname_to_str_SOURCES = fuzz_dname_to_str.c
+fuzz_dname_to_str_CPPFLAGS = $(AM_CPPFLAGS)
+fuzz_dname_to_str_LDADD = \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(lmdb_LIBS)
+
+fuzz_dname_from_str_SOURCES = fuzz_dname_from_str.c
+fuzz_dname_from_str_CPPFLAGS = $(AM_CPPFLAGS)
+fuzz_dname_from_str_LDADD = \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(lmdb_LIBS)
+
+if OSS_FUZZ
+
+# CFLAGS, LDFLAGS, etc. are defined by the oss-fuzz environment; just link
+# in the fuzzing engine
+
+fuzz_packet_LDADD += ${LIB_FUZZING_ENGINE} -lc++
+fuzz_zscanner_LDADD += ${LIB_FUZZING_ENGINE} -lc++
+fuzz_dname_to_str_LDADD += ${LIB_FUZZING_ENGINE} -lc++
+fuzz_dname_from_str_LDADD += ${LIB_FUZZING_ENGINE} -lc++
+
+else
+
+if FUZZER
+
+fuzz_packet_CPPFLAGS += ${fuzzer_CFLAGS}
+fuzz_packet_LDFLAGS = ${fuzzer_LDFLAGS}
+
+fuzz_zscanner_CPPFLAGS += ${fuzzer_CFLAGS}
+fuzz_zscanner_LDFLAGS = ${fuzzer_LDFLAGS}
+
+fuzz_dname_to_str_CPPFLAGS += ${fuzzer_CFLAGS}
+fuzz_dname_to_str_LDFLAGS = ${fuzzer_LDFLAGS}
+
+fuzz_dname_from_str_CPPFLAGS += ${fuzzer_CFLAGS}
+fuzz_dname_from_str_LDFLAGS = ${fuzzer_LDFLAGS}
+
+# Ensure that fuzzers execute successfully with a benign input
+
+TESTS = check/check_fuzzers.test
+EXTRA_DIST = check/check_fuzzers.test
+
+else
+
+fuzz_packet_SOURCES += main.c
+fuzz_zscanner_SOURCES += main.c
+fuzz_dname_to_str_SOURCES += main.c
+fuzz_dname_from_str_SOURCES += main.c
+
+TESTS = $(FUZZERS)
+
+endif # if FUZZER
+
+endif # if OSS_FUZZ
+
+check-compile: $(check_PROGRAMS)
diff --git a/tests-fuzz/Makefile.in b/tests-fuzz/Makefile.in
new file mode 100644
index 0000000..b49dfdf
--- /dev/null
+++ b/tests-fuzz/Makefile.in
@@ -0,0 +1,1501 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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@
+check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2)
+
+# knotd_stdio (AFL)
+@HAVE_DAEMON_TRUE@am__append_1 = knotd_stdio
+
+# CFLAGS, LDFLAGS, etc. are defined by the oss-fuzz environment; just link
+# in the fuzzing engine
+@OSS_FUZZ_TRUE@am__append_2 = ${LIB_FUZZING_ENGINE} -lc++
+@OSS_FUZZ_TRUE@am__append_3 = ${LIB_FUZZING_ENGINE} -lc++
+@OSS_FUZZ_TRUE@am__append_4 = ${LIB_FUZZING_ENGINE} -lc++
+@OSS_FUZZ_TRUE@am__append_5 = ${LIB_FUZZING_ENGINE} -lc++
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@am__append_6 = ${fuzzer_CFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@am__append_7 = ${fuzzer_CFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@am__append_8 = ${fuzzer_CFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@am__append_9 = ${fuzzer_CFLAGS}
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@TESTS = $(am__EXEEXT_1)
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@TESTS = check/check_fuzzers.test
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__append_10 = main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__append_11 = main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__append_12 = main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__append_13 = main.c
+subdir = tests-fuzz
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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__EXEEXT_1 = fuzz_packet$(EXEEXT) fuzz_zscanner$(EXEEXT) \
+ fuzz_dname_to_str$(EXEEXT) fuzz_dname_from_str$(EXEEXT)
+@HAVE_DAEMON_TRUE@am__EXEEXT_2 = knotd_stdio$(EXEEXT)
+am__fuzz_dname_from_str_SOURCES_DIST = fuzz_dname_from_str.c main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__objects_1 = fuzz_dname_from_str-main.$(OBJEXT)
+am_fuzz_dname_from_str_OBJECTS = \
+ fuzz_dname_from_str-fuzz_dname_from_str.$(OBJEXT) \
+ $(am__objects_1)
+fuzz_dname_from_str_OBJECTS = $(am_fuzz_dname_from_str_OBJECTS)
+am__DEPENDENCIES_1 =
+fuzz_dname_from_str_DEPENDENCIES = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+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 =
+fuzz_dname_from_str_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(fuzz_dname_from_str_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__fuzz_dname_to_str_SOURCES_DIST = fuzz_dname_to_str.c main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__objects_2 = fuzz_dname_to_str-main.$(OBJEXT)
+am_fuzz_dname_to_str_OBJECTS = \
+ fuzz_dname_to_str-fuzz_dname_to_str.$(OBJEXT) $(am__objects_2)
+fuzz_dname_to_str_OBJECTS = $(am_fuzz_dname_to_str_OBJECTS)
+fuzz_dname_to_str_DEPENDENCIES = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+fuzz_dname_to_str_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(fuzz_dname_to_str_LDFLAGS) $(LDFLAGS) \
+ -o $@
+am__fuzz_packet_SOURCES_DIST = fuzz_packet.c main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__objects_3 = \
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@ fuzz_packet-main.$(OBJEXT)
+am_fuzz_packet_OBJECTS = fuzz_packet-fuzz_packet.$(OBJEXT) \
+ $(am__objects_3)
+fuzz_packet_OBJECTS = $(am_fuzz_packet_OBJECTS)
+fuzz_packet_DEPENDENCIES = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+fuzz_packet_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(fuzz_packet_LDFLAGS) $(LDFLAGS) -o $@
+am__fuzz_zscanner_SOURCES_DIST = fuzz_zscanner.c main.c
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@am__objects_4 = \
+@FUZZER_FALSE@@OSS_FUZZ_FALSE@ fuzz_zscanner-main.$(OBJEXT)
+am_fuzz_zscanner_OBJECTS = fuzz_zscanner-fuzz_zscanner.$(OBJEXT) \
+ $(am__objects_4)
+fuzz_zscanner_OBJECTS = $(am_fuzz_zscanner_OBJECTS)
+fuzz_zscanner_DEPENDENCIES = $(top_builddir)/src/libzscanner.la \
+ $(am__DEPENDENCIES_1)
+fuzz_zscanner_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(fuzz_zscanner_LDFLAGS) $(LDFLAGS) -o $@
+am__knotd_stdio_SOURCES_DIST = knotd_wrap/afl-loop.h \
+ knotd_wrap/server.c knotd_wrap/tcp-handler.c \
+ knotd_wrap/udp-handler.c
+am__dirstamp = $(am__leading_dot)dirstamp
+@HAVE_DAEMON_TRUE@am_knotd_stdio_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knotd_wrap/knotd_stdio-server.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@ knotd_wrap/knotd_stdio-tcp-handler.$(OBJEXT) \
+@HAVE_DAEMON_TRUE@ knotd_wrap/knotd_stdio-udp-handler.$(OBJEXT)
+@HAVE_DAEMON_TRUE@nodist_knotd_stdio_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knotd_wrap/knotd_stdio-main.$(OBJEXT)
+knotd_stdio_OBJECTS = $(am_knotd_stdio_OBJECTS) \
+ $(nodist_knotd_stdio_OBJECTS)
+@HAVE_DAEMON_TRUE@knotd_stdio_DEPENDENCIES = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknot.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libdnssec.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libcontrib.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libzscanner.la \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1)
+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@ -I$(top_builddir)/src
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = \
+ ./$(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po \
+ ./$(DEPDIR)/fuzz_dname_from_str-main.Po \
+ ./$(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po \
+ ./$(DEPDIR)/fuzz_dname_to_str-main.Po \
+ ./$(DEPDIR)/fuzz_packet-fuzz_packet.Po \
+ ./$(DEPDIR)/fuzz_packet-main.Po \
+ ./$(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po \
+ ./$(DEPDIR)/fuzz_zscanner-main.Po \
+ knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po \
+ knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po \
+ knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po \
+ knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.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 = $(fuzz_dname_from_str_SOURCES) $(fuzz_dname_to_str_SOURCES) \
+ $(fuzz_packet_SOURCES) $(fuzz_zscanner_SOURCES) \
+ $(knotd_stdio_SOURCES) $(nodist_knotd_stdio_SOURCES)
+DIST_SOURCES = $(am__fuzz_dname_from_str_SOURCES_DIST) \
+ $(am__fuzz_dname_to_str_SOURCES_DIST) \
+ $(am__fuzz_packet_SOURCES_DIST) \
+ $(am__fuzz_zscanner_SOURCES_DIST) \
+ $(am__knotd_stdio_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# 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
+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`
+AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
+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)/depcomp \
+ $(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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src \
+ -DCONFIG_DIR='"${config_dir}"' \
+ -DSTORAGE_DIR='"${storage_dir}"' \
+ -DRUN_DIR='"${run_dir}"' \
+ -DSRCDIR=\"$(abs_srcdir)\"
+
+FUZZERS = \
+ fuzz_packet \
+ fuzz_zscanner \
+ fuzz_dname_to_str \
+ fuzz_dname_from_str
+
+@HAVE_DAEMON_TRUE@nodist_knotd_stdio_SOURCES = knotd_wrap/main.c
+@HAVE_DAEMON_TRUE@knotd_stdio_SOURCES = \
+@HAVE_DAEMON_TRUE@ knotd_wrap/afl-loop.h \
+@HAVE_DAEMON_TRUE@ knotd_wrap/server.c \
+@HAVE_DAEMON_TRUE@ knotd_wrap/tcp-handler.c \
+@HAVE_DAEMON_TRUE@ knotd_wrap/udp-handler.c
+
+@HAVE_DAEMON_TRUE@knotd_stdio_CPPFLAGS = \
+@HAVE_DAEMON_TRUE@ $(AM_CPPFLAGS) \
+@HAVE_DAEMON_TRUE@ $(libkqueue_CFLAGS) \
+@HAVE_DAEMON_TRUE@ $(liburcu_CFLAGS) \
+@HAVE_DAEMON_TRUE@ $(lmdb_CFLAGS) \
+@HAVE_DAEMON_TRUE@ $(systemd_CFLAGS)
+
+@HAVE_DAEMON_TRUE@knotd_stdio_LDADD = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknot.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libdnssec.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libcontrib.la \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libzscanner.la \
+@HAVE_DAEMON_TRUE@ $(cap_ng_LIBS) \
+@HAVE_DAEMON_TRUE@ $(gnutls_LIBS) \
+@HAVE_DAEMON_TRUE@ $(liburcu_LIBS) \
+@HAVE_DAEMON_TRUE@ $(lmdb_LIBS) \
+@HAVE_DAEMON_TRUE@ $(systemd_LIBS)
+
+@HAVE_DAEMON_TRUE@BUILT_SOURCES = knotd_wrap/main.c
+@HAVE_DAEMON_TRUE@CLEANFILES = knotd_wrap/main.c
+
+# Simple function wrappers (AFL, libFuzzer, tests)
+fuzz_packet_SOURCES = fuzz_packet.c $(am__append_10)
+fuzz_packet_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_6)
+fuzz_packet_LDADD = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(lmdb_LIBS) $(am__append_2)
+fuzz_zscanner_SOURCES = fuzz_zscanner.c $(am__append_11)
+fuzz_zscanner_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_7)
+fuzz_zscanner_LDADD = $(top_builddir)/src/libzscanner.la \
+ $(am__append_3)
+fuzz_dname_to_str_SOURCES = fuzz_dname_to_str.c $(am__append_12)
+fuzz_dname_to_str_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_8)
+fuzz_dname_to_str_LDADD = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(lmdb_LIBS) $(am__append_4)
+fuzz_dname_from_str_SOURCES = fuzz_dname_from_str.c $(am__append_13)
+fuzz_dname_from_str_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_9)
+fuzz_dname_from_str_LDADD = $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la $(lmdb_LIBS) $(am__append_5)
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@fuzz_packet_LDFLAGS = ${fuzzer_LDFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@fuzz_zscanner_LDFLAGS = ${fuzzer_LDFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@fuzz_dname_to_str_LDFLAGS = ${fuzzer_LDFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@fuzz_dname_from_str_LDFLAGS = ${fuzzer_LDFLAGS}
+@FUZZER_TRUE@@OSS_FUZZ_FALSE@EXTRA_DIST = check/check_fuzzers.test
+all: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(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 tests-fuzz/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests-fuzz/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):
+
+clean-checkPROGRAMS:
+ @list='$(check_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
+
+fuzz_dname_from_str$(EXEEXT): $(fuzz_dname_from_str_OBJECTS) $(fuzz_dname_from_str_DEPENDENCIES) $(EXTRA_fuzz_dname_from_str_DEPENDENCIES)
+ @rm -f fuzz_dname_from_str$(EXEEXT)
+ $(AM_V_CCLD)$(fuzz_dname_from_str_LINK) $(fuzz_dname_from_str_OBJECTS) $(fuzz_dname_from_str_LDADD) $(LIBS)
+
+fuzz_dname_to_str$(EXEEXT): $(fuzz_dname_to_str_OBJECTS) $(fuzz_dname_to_str_DEPENDENCIES) $(EXTRA_fuzz_dname_to_str_DEPENDENCIES)
+ @rm -f fuzz_dname_to_str$(EXEEXT)
+ $(AM_V_CCLD)$(fuzz_dname_to_str_LINK) $(fuzz_dname_to_str_OBJECTS) $(fuzz_dname_to_str_LDADD) $(LIBS)
+
+fuzz_packet$(EXEEXT): $(fuzz_packet_OBJECTS) $(fuzz_packet_DEPENDENCIES) $(EXTRA_fuzz_packet_DEPENDENCIES)
+ @rm -f fuzz_packet$(EXEEXT)
+ $(AM_V_CCLD)$(fuzz_packet_LINK) $(fuzz_packet_OBJECTS) $(fuzz_packet_LDADD) $(LIBS)
+
+fuzz_zscanner$(EXEEXT): $(fuzz_zscanner_OBJECTS) $(fuzz_zscanner_DEPENDENCIES) $(EXTRA_fuzz_zscanner_DEPENDENCIES)
+ @rm -f fuzz_zscanner$(EXEEXT)
+ $(AM_V_CCLD)$(fuzz_zscanner_LINK) $(fuzz_zscanner_OBJECTS) $(fuzz_zscanner_LDADD) $(LIBS)
+knotd_wrap/$(am__dirstamp):
+ @$(MKDIR_P) knotd_wrap
+ @: > knotd_wrap/$(am__dirstamp)
+knotd_wrap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knotd_wrap/$(DEPDIR)
+ @: > knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+knotd_wrap/knotd_stdio-server.$(OBJEXT): knotd_wrap/$(am__dirstamp) \
+ knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+knotd_wrap/knotd_stdio-tcp-handler.$(OBJEXT): \
+ knotd_wrap/$(am__dirstamp) \
+ knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+knotd_wrap/knotd_stdio-udp-handler.$(OBJEXT): \
+ knotd_wrap/$(am__dirstamp) \
+ knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+knotd_wrap/knotd_stdio-main.$(OBJEXT): knotd_wrap/$(am__dirstamp) \
+ knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+
+knotd_stdio$(EXEEXT): $(knotd_stdio_OBJECTS) $(knotd_stdio_DEPENDENCIES) $(EXTRA_knotd_stdio_DEPENDENCIES)
+ @rm -f knotd_stdio$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knotd_stdio_OBJECTS) $(knotd_stdio_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f knotd_wrap/*.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_dname_from_str-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_dname_to_str-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_packet-fuzz_packet.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_packet-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzz_zscanner-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.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 $@ $<
+
+fuzz_dname_from_str-fuzz_dname_from_str.o: fuzz_dname_from_str.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_from_str-fuzz_dname_from_str.o -MD -MP -MF $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Tpo -c -o fuzz_dname_from_str-fuzz_dname_from_str.o `test -f 'fuzz_dname_from_str.c' || echo '$(srcdir)/'`fuzz_dname_from_str.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Tpo $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_dname_from_str.c' object='fuzz_dname_from_str-fuzz_dname_from_str.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_from_str-fuzz_dname_from_str.o `test -f 'fuzz_dname_from_str.c' || echo '$(srcdir)/'`fuzz_dname_from_str.c
+
+fuzz_dname_from_str-fuzz_dname_from_str.obj: fuzz_dname_from_str.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_from_str-fuzz_dname_from_str.obj -MD -MP -MF $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Tpo -c -o fuzz_dname_from_str-fuzz_dname_from_str.obj `if test -f 'fuzz_dname_from_str.c'; then $(CYGPATH_W) 'fuzz_dname_from_str.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_dname_from_str.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Tpo $(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_dname_from_str.c' object='fuzz_dname_from_str-fuzz_dname_from_str.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_from_str-fuzz_dname_from_str.obj `if test -f 'fuzz_dname_from_str.c'; then $(CYGPATH_W) 'fuzz_dname_from_str.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_dname_from_str.c'; fi`
+
+fuzz_dname_from_str-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_from_str-main.o -MD -MP -MF $(DEPDIR)/fuzz_dname_from_str-main.Tpo -c -o fuzz_dname_from_str-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_from_str-main.Tpo $(DEPDIR)/fuzz_dname_from_str-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_dname_from_str-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_from_str-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+fuzz_dname_from_str-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_from_str-main.obj -MD -MP -MF $(DEPDIR)/fuzz_dname_from_str-main.Tpo -c -o fuzz_dname_from_str-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_from_str-main.Tpo $(DEPDIR)/fuzz_dname_from_str-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_dname_from_str-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_from_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_from_str-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+fuzz_dname_to_str-fuzz_dname_to_str.o: fuzz_dname_to_str.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_to_str-fuzz_dname_to_str.o -MD -MP -MF $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Tpo -c -o fuzz_dname_to_str-fuzz_dname_to_str.o `test -f 'fuzz_dname_to_str.c' || echo '$(srcdir)/'`fuzz_dname_to_str.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Tpo $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_dname_to_str.c' object='fuzz_dname_to_str-fuzz_dname_to_str.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_to_str-fuzz_dname_to_str.o `test -f 'fuzz_dname_to_str.c' || echo '$(srcdir)/'`fuzz_dname_to_str.c
+
+fuzz_dname_to_str-fuzz_dname_to_str.obj: fuzz_dname_to_str.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_to_str-fuzz_dname_to_str.obj -MD -MP -MF $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Tpo -c -o fuzz_dname_to_str-fuzz_dname_to_str.obj `if test -f 'fuzz_dname_to_str.c'; then $(CYGPATH_W) 'fuzz_dname_to_str.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_dname_to_str.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Tpo $(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_dname_to_str.c' object='fuzz_dname_to_str-fuzz_dname_to_str.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_to_str-fuzz_dname_to_str.obj `if test -f 'fuzz_dname_to_str.c'; then $(CYGPATH_W) 'fuzz_dname_to_str.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_dname_to_str.c'; fi`
+
+fuzz_dname_to_str-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_to_str-main.o -MD -MP -MF $(DEPDIR)/fuzz_dname_to_str-main.Tpo -c -o fuzz_dname_to_str-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_to_str-main.Tpo $(DEPDIR)/fuzz_dname_to_str-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_dname_to_str-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_to_str-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+fuzz_dname_to_str-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_dname_to_str-main.obj -MD -MP -MF $(DEPDIR)/fuzz_dname_to_str-main.Tpo -c -o fuzz_dname_to_str-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_dname_to_str-main.Tpo $(DEPDIR)/fuzz_dname_to_str-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_dname_to_str-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_dname_to_str_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_dname_to_str-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+fuzz_packet-fuzz_packet.o: fuzz_packet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_packet-fuzz_packet.o -MD -MP -MF $(DEPDIR)/fuzz_packet-fuzz_packet.Tpo -c -o fuzz_packet-fuzz_packet.o `test -f 'fuzz_packet.c' || echo '$(srcdir)/'`fuzz_packet.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_packet-fuzz_packet.Tpo $(DEPDIR)/fuzz_packet-fuzz_packet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_packet.c' object='fuzz_packet-fuzz_packet.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_packet-fuzz_packet.o `test -f 'fuzz_packet.c' || echo '$(srcdir)/'`fuzz_packet.c
+
+fuzz_packet-fuzz_packet.obj: fuzz_packet.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_packet-fuzz_packet.obj -MD -MP -MF $(DEPDIR)/fuzz_packet-fuzz_packet.Tpo -c -o fuzz_packet-fuzz_packet.obj `if test -f 'fuzz_packet.c'; then $(CYGPATH_W) 'fuzz_packet.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_packet.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_packet-fuzz_packet.Tpo $(DEPDIR)/fuzz_packet-fuzz_packet.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_packet.c' object='fuzz_packet-fuzz_packet.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_packet-fuzz_packet.obj `if test -f 'fuzz_packet.c'; then $(CYGPATH_W) 'fuzz_packet.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_packet.c'; fi`
+
+fuzz_packet-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_packet-main.o -MD -MP -MF $(DEPDIR)/fuzz_packet-main.Tpo -c -o fuzz_packet-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_packet-main.Tpo $(DEPDIR)/fuzz_packet-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_packet-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_packet-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+fuzz_packet-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_packet-main.obj -MD -MP -MF $(DEPDIR)/fuzz_packet-main.Tpo -c -o fuzz_packet-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_packet-main.Tpo $(DEPDIR)/fuzz_packet-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_packet-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_packet_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_packet-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+fuzz_zscanner-fuzz_zscanner.o: fuzz_zscanner.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_zscanner-fuzz_zscanner.o -MD -MP -MF $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Tpo -c -o fuzz_zscanner-fuzz_zscanner.o `test -f 'fuzz_zscanner.c' || echo '$(srcdir)/'`fuzz_zscanner.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Tpo $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_zscanner.c' object='fuzz_zscanner-fuzz_zscanner.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_zscanner-fuzz_zscanner.o `test -f 'fuzz_zscanner.c' || echo '$(srcdir)/'`fuzz_zscanner.c
+
+fuzz_zscanner-fuzz_zscanner.obj: fuzz_zscanner.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_zscanner-fuzz_zscanner.obj -MD -MP -MF $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Tpo -c -o fuzz_zscanner-fuzz_zscanner.obj `if test -f 'fuzz_zscanner.c'; then $(CYGPATH_W) 'fuzz_zscanner.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_zscanner.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Tpo $(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fuzz_zscanner.c' object='fuzz_zscanner-fuzz_zscanner.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_zscanner-fuzz_zscanner.obj `if test -f 'fuzz_zscanner.c'; then $(CYGPATH_W) 'fuzz_zscanner.c'; else $(CYGPATH_W) '$(srcdir)/fuzz_zscanner.c'; fi`
+
+fuzz_zscanner-main.o: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_zscanner-main.o -MD -MP -MF $(DEPDIR)/fuzz_zscanner-main.Tpo -c -o fuzz_zscanner-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_zscanner-main.Tpo $(DEPDIR)/fuzz_zscanner-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_zscanner-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_zscanner-main.o `test -f 'main.c' || echo '$(srcdir)/'`main.c
+
+fuzz_zscanner-main.obj: main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT fuzz_zscanner-main.obj -MD -MP -MF $(DEPDIR)/fuzz_zscanner-main.Tpo -c -o fuzz_zscanner-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/fuzz_zscanner-main.Tpo $(DEPDIR)/fuzz_zscanner-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='main.c' object='fuzz_zscanner-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(fuzz_zscanner_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o fuzz_zscanner-main.obj `if test -f 'main.c'; then $(CYGPATH_W) 'main.c'; else $(CYGPATH_W) '$(srcdir)/main.c'; fi`
+
+knotd_wrap/knotd_stdio-server.o: knotd_wrap/server.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-server.o -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-server.Tpo -c -o knotd_wrap/knotd_stdio-server.o `test -f 'knotd_wrap/server.c' || echo '$(srcdir)/'`knotd_wrap/server.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-server.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/server.c' object='knotd_wrap/knotd_stdio-server.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-server.o `test -f 'knotd_wrap/server.c' || echo '$(srcdir)/'`knotd_wrap/server.c
+
+knotd_wrap/knotd_stdio-server.obj: knotd_wrap/server.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-server.obj -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-server.Tpo -c -o knotd_wrap/knotd_stdio-server.obj `if test -f 'knotd_wrap/server.c'; then $(CYGPATH_W) 'knotd_wrap/server.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/server.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-server.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/server.c' object='knotd_wrap/knotd_stdio-server.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-server.obj `if test -f 'knotd_wrap/server.c'; then $(CYGPATH_W) 'knotd_wrap/server.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/server.c'; fi`
+
+knotd_wrap/knotd_stdio-tcp-handler.o: knotd_wrap/tcp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-tcp-handler.o -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Tpo -c -o knotd_wrap/knotd_stdio-tcp-handler.o `test -f 'knotd_wrap/tcp-handler.c' || echo '$(srcdir)/'`knotd_wrap/tcp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/tcp-handler.c' object='knotd_wrap/knotd_stdio-tcp-handler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-tcp-handler.o `test -f 'knotd_wrap/tcp-handler.c' || echo '$(srcdir)/'`knotd_wrap/tcp-handler.c
+
+knotd_wrap/knotd_stdio-tcp-handler.obj: knotd_wrap/tcp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-tcp-handler.obj -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Tpo -c -o knotd_wrap/knotd_stdio-tcp-handler.obj `if test -f 'knotd_wrap/tcp-handler.c'; then $(CYGPATH_W) 'knotd_wrap/tcp-handler.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/tcp-handler.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/tcp-handler.c' object='knotd_wrap/knotd_stdio-tcp-handler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-tcp-handler.obj `if test -f 'knotd_wrap/tcp-handler.c'; then $(CYGPATH_W) 'knotd_wrap/tcp-handler.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/tcp-handler.c'; fi`
+
+knotd_wrap/knotd_stdio-udp-handler.o: knotd_wrap/udp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-udp-handler.o -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Tpo -c -o knotd_wrap/knotd_stdio-udp-handler.o `test -f 'knotd_wrap/udp-handler.c' || echo '$(srcdir)/'`knotd_wrap/udp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/udp-handler.c' object='knotd_wrap/knotd_stdio-udp-handler.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-udp-handler.o `test -f 'knotd_wrap/udp-handler.c' || echo '$(srcdir)/'`knotd_wrap/udp-handler.c
+
+knotd_wrap/knotd_stdio-udp-handler.obj: knotd_wrap/udp-handler.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-udp-handler.obj -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Tpo -c -o knotd_wrap/knotd_stdio-udp-handler.obj `if test -f 'knotd_wrap/udp-handler.c'; then $(CYGPATH_W) 'knotd_wrap/udp-handler.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/udp-handler.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/udp-handler.c' object='knotd_wrap/knotd_stdio-udp-handler.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-udp-handler.obj `if test -f 'knotd_wrap/udp-handler.c'; then $(CYGPATH_W) 'knotd_wrap/udp-handler.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/udp-handler.c'; fi`
+
+knotd_wrap/knotd_stdio-main.o: knotd_wrap/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-main.o -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-main.Tpo -c -o knotd_wrap/knotd_stdio-main.o `test -f 'knotd_wrap/main.c' || echo '$(srcdir)/'`knotd_wrap/main.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-main.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/main.c' object='knotd_wrap/knotd_stdio-main.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-main.o `test -f 'knotd_wrap/main.c' || echo '$(srcdir)/'`knotd_wrap/main.c
+
+knotd_wrap/knotd_stdio-main.obj: knotd_wrap/main.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knotd_wrap/knotd_stdio-main.obj -MD -MP -MF knotd_wrap/$(DEPDIR)/knotd_stdio-main.Tpo -c -o knotd_wrap/knotd_stdio-main.obj `if test -f 'knotd_wrap/main.c'; then $(CYGPATH_W) 'knotd_wrap/main.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/main.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knotd_wrap/$(DEPDIR)/knotd_stdio-main.Tpo knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knotd_wrap/main.c' object='knotd_wrap/knotd_stdio-main.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_stdio_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knotd_wrap/knotd_stdio-main.obj `if test -f 'knotd_wrap/main.c'; then $(CYGPATH_W) 'knotd_wrap/main.c'; else $(CYGPATH_W) '$(srcdir)/knotd_wrap/main.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(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-am
+
+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-am
+
+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
+
+# 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"$(AM_TESTSUITE_SUMMARY_HEADER)"$${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: $(check_PROGRAMS)
+ @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 $(check_PROGRAMS)
+ @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 $$?
+fuzz_packet.log: fuzz_packet$(EXEEXT)
+ @p='fuzz_packet$(EXEEXT)'; \
+ b='fuzz_packet'; \
+ $(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)
+fuzz_zscanner.log: fuzz_zscanner$(EXEEXT)
+ @p='fuzz_zscanner$(EXEEXT)'; \
+ b='fuzz_zscanner'; \
+ $(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)
+fuzz_dname_to_str.log: fuzz_dname_to_str$(EXEEXT)
+ @p='fuzz_dname_to_str$(EXEEXT)'; \
+ b='fuzz_dname_to_str'; \
+ $(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)
+fuzz_dname_from_str.log: fuzz_dname_from_str$(EXEEXT)
+ @p='fuzz_dname_from_str$(EXEEXT)'; \
+ b='fuzz_dname_from_str'; \
+ $(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_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) check-am
+all-am: Makefile
+installdirs:
+install: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) install-am
+install-exec: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) 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)
+ -rm -f knotd_wrap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knotd_wrap/$(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)
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_from_str-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_to_str-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_packet-fuzz_packet.Po
+ -rm -f ./$(DEPDIR)/fuzz_packet-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po
+ -rm -f ./$(DEPDIR)/fuzz_zscanner-main.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+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 ./$(DEPDIR)/fuzz_dname_from_str-fuzz_dname_from_str.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_from_str-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_to_str-fuzz_dname_to_str.Po
+ -rm -f ./$(DEPDIR)/fuzz_dname_to_str-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_packet-fuzz_packet.Po
+ -rm -f ./$(DEPDIR)/fuzz_packet-main.Po
+ -rm -f ./$(DEPDIR)/fuzz_zscanner-fuzz_zscanner.Po
+ -rm -f ./$(DEPDIR)/fuzz_zscanner-main.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-main.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-server.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-tcp-handler.Po
+ -rm -f knotd_wrap/$(DEPDIR)/knotd_stdio-udp-handler.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: all check check-am install install-am install-exec \
+ install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \
+ check-am clean clean-checkPROGRAMS clean-generic clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am 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-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ recheck tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+@HAVE_DAEMON_TRUE@knotd_wrap/main.c: $(top_builddir)/src/utils/knotd/main.c
+@HAVE_DAEMON_TRUE@ @echo '#include "knotd_wrap/afl-loop.h"' > $@
+@HAVE_DAEMON_TRUE@ @$(SED) -e 's/for (;;)/while (__AFL_LOOP(10000))/' $(top_srcdir)/src/utils/knotd/main.c >> $@
+
+check-compile: $(check_PROGRAMS)
+
+# 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/tests-fuzz/fuzz_dname_from_str.c b/tests-fuzz/fuzz_dname_from_str.c
new file mode 100644
index 0000000..518dbe5
--- /dev/null
+++ b/tests-fuzz/fuzz_dname_from_str.c
@@ -0,0 +1,39 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "libknot/dname.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ // Prepare 0-terminated dname string.
+ char *txt = malloc(size + 1);
+ if (txt == NULL) {
+ return 0;
+ }
+ memcpy(txt, data, size);
+ txt[size] = '\0';
+
+ // Transform the input.
+ knot_dname_storage_t dname;
+ (void)knot_dname_from_str(dname, txt, sizeof(dname));
+
+ free(txt);
+
+ return 0;
+}
diff --git a/tests-fuzz/fuzz_dname_to_str.c b/tests-fuzz/fuzz_dname_to_str.c
new file mode 100644
index 0000000..3d091a2
--- /dev/null
+++ b/tests-fuzz/fuzz_dname_to_str.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "libknot/dname.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ // Skip invalid dnames.
+ if (knot_dname_wire_check(data, data + size, NULL) <= 0) {
+ return 0;
+ }
+
+ // Transform the input.
+ knot_dname_txt_storage_t txt;
+ (void)knot_dname_to_str(txt, (const knot_dname_t *)data, sizeof(txt));
+
+ return 0;
+}
diff --git a/tests-fuzz/fuzz_packet.c b/tests-fuzz/fuzz_packet.c
new file mode 100644
index 0000000..dff564e
--- /dev/null
+++ b/tests-fuzz/fuzz_packet.c
@@ -0,0 +1,33 @@
+/* Copyright (C) 2022 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 <stdint.h>
+
+#include "libknot/libknot.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ uint8_t copy[1 + size];
+ memcpy(copy, data, size);
+
+ knot_pkt_t *pkt = knot_pkt_new(copy, size, NULL);
+ if (pkt != NULL) {
+ knot_pkt_parse(pkt, 0);
+ knot_pkt_free(pkt);
+ }
+
+ return 0;
+}
diff --git a/tests-fuzz/fuzz_zscanner.c b/tests-fuzz/fuzz_zscanner.c
new file mode 100644
index 0000000..8ebcc53
--- /dev/null
+++ b/tests-fuzz/fuzz_zscanner.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2018 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 <stdint.h>
+
+#include "libzscanner/scanner.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ zs_scanner_t s;
+ if (zs_init(&s, ".", 1, 0) == 0 &&
+ zs_set_input_string(&s, (const char *)data, size) == 0) {
+ zs_parse_all(&s);
+ }
+ zs_deinit(&s);
+
+ return 0;
+}
diff --git a/tests-fuzz/knotd_wrap/afl-loop.h b/tests-fuzz/knotd_wrap/afl-loop.h
new file mode 100644
index 0000000..87ae9c3
--- /dev/null
+++ b/tests-fuzz/knotd_wrap/afl-loop.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#ifndef __AFL_LOOP
+#define __AFL_LOOP(x) (0)
+#endif
diff --git a/tests-fuzz/knotd_wrap/server.c b/tests-fuzz/knotd_wrap/server.c
new file mode 100644
index 0000000..fc37fbc
--- /dev/null
+++ b/tests-fuzz/knotd_wrap/server.c
@@ -0,0 +1,29 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#define server_reconfigure orig_server_reconfigure
+#include "knot/server/server.c"
+#undef server_reconfigure
+
+extern void udp_master_init_stdio(server_t *server);
+
+int server_reconfigure(conf_t *conf, server_t *server)
+{
+ orig_server_reconfigure(conf, server);
+ udp_master_init_stdio(server);
+
+ return KNOT_EOK;
+}
diff --git a/tests-fuzz/knotd_wrap/tcp-handler.c b/tests-fuzz/knotd_wrap/tcp-handler.c
new file mode 100644
index 0000000..59b13fb
--- /dev/null
+++ b/tests-fuzz/knotd_wrap/tcp-handler.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#define tcp_master orig_tcp_master
+#include "knot/server/tcp-handler.c"
+#undef tcp_master
+
+int tcp_master(dthread_t *thread)
+{
+ log_info("AFL, empty TCP handler");
+ return 0;
+}
diff --git a/tests-fuzz/knotd_wrap/udp-handler.c b/tests-fuzz/knotd_wrap/udp-handler.c
new file mode 100644
index 0000000..cdd8727
--- /dev/null
+++ b/tests-fuzz/knotd_wrap/udp-handler.c
@@ -0,0 +1,116 @@
+/* Copyright (C) 2022 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 <signal.h>
+
+#include "knot/server/udp-handler.c"
+#include "knot/common/log.h"
+
+typedef struct {
+ struct iovec iov[NBUFS];
+ uint8_t buf[NBUFS][KNOT_WIRE_MAX_PKTSIZE];
+ struct sockaddr_storage addr;
+ bool afl_persistent;
+} udp_stdin_t;
+
+static inline void next(udp_stdin_t *rq)
+{
+ if (rq->afl_persistent) {
+ raise(SIGSTOP);
+ } else {
+ exit(0);
+ }
+}
+
+static void *udp_stdin_init(_unused_ udp_context_t *ctx, _unused_ void *xdp_sock)
+{
+ udp_stdin_t *rq = calloc(1, sizeof(udp_stdin_t));
+ if (rq == NULL) {
+ return NULL;
+ }
+
+ for (unsigned i = 0; i < NBUFS; ++i) {
+ rq->iov[i].iov_base = rq->buf[i];
+ rq->iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE;
+ }
+
+ struct sockaddr_in *a = (struct sockaddr_in *)&rq->addr;
+ a->sin_family = AF_INET;
+ a->sin_addr.s_addr = IN_LOOPBACKNET;
+ a->sin_port = 42;
+
+ rq->afl_persistent = getenv("AFL_PERSISTENT") != NULL;
+
+ return rq;
+}
+
+static void udp_stdin_deinit(void *d)
+{
+ free(d);
+}
+
+static int udp_stdin_recv(_unused_ int fd, void *d)
+{
+ udp_stdin_t *rq = (udp_stdin_t *)d;
+ rq->iov[RX].iov_len = fread(rq->iov[RX].iov_base, 1,
+ KNOT_WIRE_MAX_PKTSIZE, stdin);
+ if (rq->iov[RX].iov_len == 0) {
+ next(rq);
+ }
+
+ return rq->iov[RX].iov_len;
+}
+
+static void udp_stdin_handle(udp_context_t *ctx, void *d)
+{
+ udp_stdin_t *rq = (udp_stdin_t *)d;
+ udp_handle(ctx, STDIN_FILENO, &rq->addr, &rq->iov[RX], &rq->iov[TX], false);
+}
+
+static void udp_stdin_send(void *d)
+{
+ udp_stdin_t *rq = (udp_stdin_t *)d;
+ next(rq);
+}
+
+static udp_api_t stdin_api = {
+ udp_stdin_init,
+ udp_stdin_deinit,
+ udp_stdin_recv,
+ udp_stdin_handle,
+ udp_stdin_send
+};
+
+void udp_master_init_stdio(server_t *server) {
+
+ log_info("AFL, UDP handler listening on stdin");
+
+ // Register dummy interface to server.
+ iface_t *ifc = calloc(1, sizeof(*ifc));
+ assert(ifc);
+ ifc->fd_udp = calloc(1, sizeof(*ifc->fd_udp));
+ assert(ifc->fd_udp);
+ ifc->fd_udp[0] = STDIN_FILENO;
+ ifc->fd_udp_count = 1;
+
+ server->n_ifaces = 1;
+ server->ifaces = ifc;
+
+ udp_recvfrom_api = stdin_api;
+#ifdef ENABLE_RECVMMSG
+ udp_recvmmsg_api = stdin_api;
+#endif
+}
diff --git a/tests-fuzz/main.c b/tests-fuzz/main.c
new file mode 100644
index 0000000..e8eb428
--- /dev/null
+++ b/tests-fuzz/main.c
@@ -0,0 +1,140 @@
+/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ * Copyright (C) 2017 Tim Ruehsen
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+#ifndef __AFL_LOOP
+
+#include <dirent.h>
+
+static void test_all_from(const char *dirname)
+{
+ DIR *dirp = opendir(dirname);
+ if (dirp == NULL) {
+ return;
+ }
+
+ struct dirent *dp;
+ while ((dp = readdir(dirp))) {
+ if (*dp->d_name == '.') {
+ continue;
+ }
+
+ char fname[strlen(dirname) + strlen(dp->d_name) + 2];
+ int ret = snprintf(fname, sizeof(fname), "%s/%s", dirname, dp->d_name);
+ if (ret < 0 || ret >= sizeof(fname)) {
+ fprintf(stderr, "Invalid path %s/%s\n", dirname, dp->d_name);
+ }
+
+ int fd;
+ if ((fd = open(fname, O_RDONLY)) == -1) {
+ fprintf(stderr, "Failed to open %s (%d)\n", fname, errno);
+ continue;
+ }
+
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ fprintf(stderr, "Failed to stat %d (%d)\n", fd, errno);
+ close(fd);
+ continue;
+ }
+
+ uint8_t *data = malloc(st.st_size);
+ if (data == NULL) {
+ fprintf(stderr, "Failed to stat %d (%d)\n", fd, ENOMEM);
+ close(fd);
+ continue;
+ }
+
+ ssize_t n;
+ if ((n = read(fd, data, st.st_size)) == st.st_size) {
+ printf("testing %llu bytes from '%s'\n", (unsigned long long) st.st_size, fname);
+ fflush(stdout);
+ LLVMFuzzerTestOneInput(data, st.st_size);
+ fflush(stderr);
+ } else {
+ fprintf(stderr, "Failed to read %llu bytes from %s (%d), got %zd\n",
+ (unsigned long long) st.st_size, fname, errno, n);
+ }
+
+ free(data);
+ close(fd);
+ }
+ closedir(dirp);
+}
+
+int main(int argc, char **argv)
+{
+ const char *target = strrchr(argv[0], '/');
+ target = target ? target + 1 : argv[0];
+
+ char corporadir[sizeof(SRCDIR) + 1 + strlen(target) + 8];
+
+ if (strncmp(target, "lt-", 3) == 0) {
+ target += 3;
+ }
+
+ int ret = snprintf(corporadir, sizeof(corporadir), SRCDIR "/%s.in", target);
+ if (ret < 0 || ret >= sizeof(corporadir)) {
+ fprintf(stderr, "Invalid path %s/%s\n", SRCDIR "/%s.in", target);
+ }
+
+ test_all_from(corporadir);
+
+ ret = snprintf(corporadir, sizeof(corporadir), SRCDIR "/%s.repro", target);
+ if (ret < 0 || ret >= sizeof(corporadir)) {
+ fprintf(stderr, "Invalid path %s/%s\n", SRCDIR "/%s.repro", target);
+ }
+
+ test_all_from(corporadir);
+
+ return 0;
+}
+
+#else
+
+int main(int argc, char **argv)
+{
+ unsigned char buf[64 * 1024];
+
+ while (__AFL_LOOP(10000)) {
+ int ret = fread(buf, 1, sizeof(buf), stdin);
+ if (ret < 0) {
+ return 0;
+ }
+
+ LLVMFuzzerTestOneInput(buf, ret);
+ }
+
+ return 0;
+}
+
+#endif /* __AFL_LOOP */
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 0000000..6a3eb0f
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,257 @@
+AM_CPPFLAGS = \
+ -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/libdnssec \
+ -I$(top_srcdir)/src/libdnssec/shared \
+ $(gnutls_CFLAGS) \
+ $(libkqueue_CFLAGS) \
+ $(lmdb_CFLAGS)
+
+LDADD = \
+ libtap.la
+
+if HAVE_DAEMON
+LDADD += \
+ $(top_builddir)/src/libknotd.la \
+ $(liburcu_LIBS) \
+ $(systemd_LIBS)
+endif HAVE_DAEMON
+
+LDADD += \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la \
+ $(gnutls_LIBS) \
+ $(lmdb_LIBS)
+
+EXTRA_DIST = \
+ tap/libtap.sh \
+ libdnssec/sample_keys.h \
+ knot/semantic_check_data \
+ knot/test_semantic_check.in \
+ libzscanner/data \
+ libzscanner/test_zscanner.in \
+ libzscanner/TESTS
+
+check_LTLIBRARIES = libtap.la
+
+libtap_la_SOURCES = \
+ tap/basic.c \
+ tap/basic.h \
+ tap/files.c \
+ tap/files.h \
+ tap/float.c \
+ tap/float.h \
+ tap/macros.h
+
+EXTRA_PROGRAMS = tap/runtests
+
+check_PROGRAMS = \
+ contrib/test_base32hex \
+ contrib/test_base64 \
+ contrib/test_base64url \
+ contrib/test_heap \
+ contrib/test_inet_ntop \
+ contrib/test_net \
+ contrib/test_net_shortwrite \
+ contrib/test_qp-trie \
+ contrib/test_qp-cow \
+ contrib/test_siphash \
+ contrib/test_sockaddr \
+ contrib/test_spinlock \
+ contrib/test_string \
+ contrib/test_strtonum \
+ contrib/test_time \
+ contrib/test_toeplitz \
+ contrib/test_wire_ctx
+
+check_PROGRAMS += \
+ libdnssec/test_binary \
+ libdnssec/test_crypto \
+ libdnssec/test_key \
+ libdnssec/test_key_algorithm \
+ libdnssec/test_key_ds \
+ libdnssec/test_keyid \
+ libdnssec/test_keystore_pkcs11 \
+ libdnssec/test_keystore_pkcs8 \
+ libdnssec/test_keytag \
+ libdnssec/test_nsec_bitmap \
+ libdnssec/test_nsec_hash \
+ libdnssec/test_random \
+ libdnssec/test_sign \
+ libdnssec/test_sign_der \
+ libdnssec/test_shared_bignum \
+ libdnssec/test_shared_dname \
+ libdnssec/test_tsig
+
+if HAVE_DAEMON
+check_PROGRAMS += \
+ knot/test_acl \
+ knot/test_changeset \
+ knot/test_conf \
+ knot/test_conf_tools \
+ knot/test_confdb \
+ knot/test_confio \
+ knot/test_digest \
+ knot/test_dthreads \
+ knot/test_fdset \
+ knot/test_journal \
+ knot/test_kasp_db \
+ knot/test_node \
+ knot/test_process_query \
+ knot/test_query_module \
+ knot/test_requestor \
+ knot/test_server \
+ knot/test_unreachable \
+ knot/test_worker_pool \
+ knot/test_worker_queue \
+ knot/test_zone-tree \
+ knot/test_zone-update \
+ knot/test_zone_events \
+ knot/test_zone_serial \
+ knot/test_zone_timers \
+ knot/test_zonedb
+
+knot_test_acl_SOURCES = \
+ knot/test_acl.c \
+ knot/test_conf.h
+
+knot_test_conf_SOURCES = \
+ knot/test_conf.c \
+ knot/test_conf.h
+
+knot_test_confdb_SOURCES = \
+ knot/test_confdb.c \
+ knot/test_conf.h
+
+knot_test_confio_SOURCES = \
+ knot/test_confio.c \
+ knot/test_conf.h
+
+knot_test_process_query_SOURCES = \
+ knot/test_process_query.c \
+ knot/test_server.h \
+ knot/test_conf.h
+endif HAVE_DAEMON
+
+check_PROGRAMS += \
+ libknot/test_control \
+ libknot/test_cookies \
+ libknot/test_db \
+ libknot/test_descriptor \
+ libknot/test_dname \
+ libknot/test_dynarray \
+ libknot/test_edns \
+ libknot/test_edns_ecs \
+ libknot/test_endian \
+ libknot/test_lookup \
+ libknot/test_pkt \
+ libknot/test_probe \
+ libknot/test_rdata \
+ libknot/test_rdataset \
+ libknot/test_rrset \
+ libknot/test_rrset-wire \
+ libknot/test_tsig \
+ libknot/test_yparser \
+ libknot/test_ypschema \
+ libknot/test_yptrafo \
+ libknot/test_wire
+
+if ENABLE_XDP
+AM_CPPFLAGS += $(libbpf_CFLAGS)
+check_PROGRAMS += \
+ libknot/test_xdp_tcp
+endif ENABLE_XDP
+
+if HAVE_LIBUTILS
+check_PROGRAMS += \
+ utils/test_cert \
+ utils/test_lookup
+endif HAVE_LIBUTILS
+
+if HAVE_DAEMON
+if STATIC_MODULE_onlinesign
+check_PROGRAMS += \
+ modules/test_onlinesign
+else
+if SHARED_MODULE_onlinesign
+check_PROGRAMS += \
+ modules/test_onlinesign
+endif
+endif
+
+if STATIC_MODULE_rrl
+check_PROGRAMS += \
+ modules/test_rrl
+else
+if SHARED_MODULE_rrl
+check_PROGRAMS += \
+ modules/test_rrl
+endif
+endif
+endif HAVE_DAEMON
+
+libdnssec_test_keystore_pkcs11_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DLIBDIR='"$(libdir)"'
+
+if HAVE_LIBUTILS
+utils_test_lookup_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(libedit_CFLAGS)
+
+utils_test_lookup_LDADD = \
+ $(top_builddir)/src/libknotus.la \
+ $(libedit_LIBS) \
+ $(LDADD)
+
+utils_test_cert_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(libedit_CFLAGS)
+
+utils_test_cert_LDADD = \
+ $(top_builddir)/src/libknotus.la \
+ $(libedit_LIBS) \
+ $(LDADD)
+endif HAVE_LIBUTILS
+
+EXTRA_PROGRAMS += libzscanner/zscanner-tool
+
+libzscanner_zscanner_tool_SOURCES = \
+ libzscanner/zscanner-tool.c \
+ libzscanner/processing.h \
+ libzscanner/processing.c
+
+check_SCRIPTS = \
+ libzscanner/test_zscanner
+
+edit = $(SED) \
+ -e 's|@top_srcdir[@]|$(abs_top_srcdir)|g' \
+ -e 's|@top_builddir[@]|$(abs_top_builddir)|g'
+
+if HAVE_LIBUTILS
+check_SCRIPTS += \
+ knot/test_semantic_check
+
+knot/test_semantic_check:
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+endif HAVE_LIBUTILS
+
+libzscanner/test_zscanner: libzscanner/zscanner-tool
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+
+CLEANFILES = $(check_SCRIPTS) $(EXTRA_PROGRAMS) runtests.log
+
+check-compile: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+
+AM_V_RUNTESTS = $(am__v_RUNTESTS_@AM_V@)
+am__v_RUNTESTS_ = $(am__v_RUNTESTS_@AM_DEFAULT_V@)
+am__v_RUNTESTS_0 =
+am__v_RUNTESTS_1 = RET=$$?; if [ "$$RET" != "0" ]; then cat "$(builddir)/runtests.log"; exit $$RET; fi
+check-local: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+ @$(top_builddir)/tests/tap/runtests -s $(srcdir) -b $(builddir) \
+ -L $(builddir)/runtests.log $(check_PROGRAMS) $(check_SCRIPTS); \
+ $(AM_V_RUNTESTS)
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644
index 0000000..2708b29
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,2813 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 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@
+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@
+@HAVE_DAEMON_TRUE@am__append_1 = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la \
+@HAVE_DAEMON_TRUE@ $(liburcu_LIBS) \
+@HAVE_DAEMON_TRUE@ $(systemd_LIBS)
+
+EXTRA_PROGRAMS = tap/runtests$(EXEEXT) \
+ libzscanner/zscanner-tool$(EXEEXT)
+check_PROGRAMS = contrib/test_base32hex$(EXEEXT) \
+ contrib/test_base64$(EXEEXT) contrib/test_base64url$(EXEEXT) \
+ contrib/test_heap$(EXEEXT) contrib/test_inet_ntop$(EXEEXT) \
+ contrib/test_net$(EXEEXT) contrib/test_net_shortwrite$(EXEEXT) \
+ contrib/test_qp-trie$(EXEEXT) contrib/test_qp-cow$(EXEEXT) \
+ contrib/test_siphash$(EXEEXT) contrib/test_sockaddr$(EXEEXT) \
+ contrib/test_spinlock$(EXEEXT) contrib/test_string$(EXEEXT) \
+ contrib/test_strtonum$(EXEEXT) contrib/test_time$(EXEEXT) \
+ contrib/test_toeplitz$(EXEEXT) contrib/test_wire_ctx$(EXEEXT) \
+ libdnssec/test_binary$(EXEEXT) libdnssec/test_crypto$(EXEEXT) \
+ libdnssec/test_key$(EXEEXT) \
+ libdnssec/test_key_algorithm$(EXEEXT) \
+ libdnssec/test_key_ds$(EXEEXT) libdnssec/test_keyid$(EXEEXT) \
+ libdnssec/test_keystore_pkcs11$(EXEEXT) \
+ libdnssec/test_keystore_pkcs8$(EXEEXT) \
+ libdnssec/test_keytag$(EXEEXT) \
+ libdnssec/test_nsec_bitmap$(EXEEXT) \
+ libdnssec/test_nsec_hash$(EXEEXT) \
+ libdnssec/test_random$(EXEEXT) libdnssec/test_sign$(EXEEXT) \
+ libdnssec/test_sign_der$(EXEEXT) \
+ libdnssec/test_shared_bignum$(EXEEXT) \
+ libdnssec/test_shared_dname$(EXEEXT) \
+ libdnssec/test_tsig$(EXEEXT) $(am__EXEEXT_1) \
+ libknot/test_control$(EXEEXT) libknot/test_cookies$(EXEEXT) \
+ libknot/test_db$(EXEEXT) libknot/test_descriptor$(EXEEXT) \
+ libknot/test_dname$(EXEEXT) libknot/test_dynarray$(EXEEXT) \
+ libknot/test_edns$(EXEEXT) libknot/test_edns_ecs$(EXEEXT) \
+ libknot/test_endian$(EXEEXT) libknot/test_lookup$(EXEEXT) \
+ libknot/test_pkt$(EXEEXT) libknot/test_probe$(EXEEXT) \
+ libknot/test_rdata$(EXEEXT) libknot/test_rdataset$(EXEEXT) \
+ libknot/test_rrset$(EXEEXT) libknot/test_rrset-wire$(EXEEXT) \
+ libknot/test_tsig$(EXEEXT) libknot/test_yparser$(EXEEXT) \
+ libknot/test_ypschema$(EXEEXT) libknot/test_yptrafo$(EXEEXT) \
+ libknot/test_wire$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_3) \
+ $(am__EXEEXT_4) $(am__EXEEXT_5) $(am__EXEEXT_6) \
+ $(am__EXEEXT_7)
+@HAVE_DAEMON_TRUE@am__append_2 = \
+@HAVE_DAEMON_TRUE@ knot/test_acl \
+@HAVE_DAEMON_TRUE@ knot/test_changeset \
+@HAVE_DAEMON_TRUE@ knot/test_conf \
+@HAVE_DAEMON_TRUE@ knot/test_conf_tools \
+@HAVE_DAEMON_TRUE@ knot/test_confdb \
+@HAVE_DAEMON_TRUE@ knot/test_confio \
+@HAVE_DAEMON_TRUE@ knot/test_digest \
+@HAVE_DAEMON_TRUE@ knot/test_dthreads \
+@HAVE_DAEMON_TRUE@ knot/test_fdset \
+@HAVE_DAEMON_TRUE@ knot/test_journal \
+@HAVE_DAEMON_TRUE@ knot/test_kasp_db \
+@HAVE_DAEMON_TRUE@ knot/test_node \
+@HAVE_DAEMON_TRUE@ knot/test_process_query \
+@HAVE_DAEMON_TRUE@ knot/test_query_module \
+@HAVE_DAEMON_TRUE@ knot/test_requestor \
+@HAVE_DAEMON_TRUE@ knot/test_server \
+@HAVE_DAEMON_TRUE@ knot/test_unreachable \
+@HAVE_DAEMON_TRUE@ knot/test_worker_pool \
+@HAVE_DAEMON_TRUE@ knot/test_worker_queue \
+@HAVE_DAEMON_TRUE@ knot/test_zone-tree \
+@HAVE_DAEMON_TRUE@ knot/test_zone-update \
+@HAVE_DAEMON_TRUE@ knot/test_zone_events \
+@HAVE_DAEMON_TRUE@ knot/test_zone_serial \
+@HAVE_DAEMON_TRUE@ knot/test_zone_timers \
+@HAVE_DAEMON_TRUE@ knot/test_zonedb
+
+@ENABLE_XDP_TRUE@am__append_3 = $(libbpf_CFLAGS)
+@ENABLE_XDP_TRUE@am__append_4 = \
+@ENABLE_XDP_TRUE@ libknot/test_xdp_tcp
+
+@HAVE_LIBUTILS_TRUE@am__append_5 = \
+@HAVE_LIBUTILS_TRUE@ utils/test_cert \
+@HAVE_LIBUTILS_TRUE@ utils/test_lookup
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__append_6 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__append_7 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@ modules/test_onlinesign
+
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__append_8 = \
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@ modules/test_rrl
+
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__append_9 = \
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@ modules/test_rrl
+
+@HAVE_LIBUTILS_TRUE@am__append_10 = \
+@HAVE_LIBUTILS_TRUE@ knot/test_semantic_check
+
+subdir = tests
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_check_compile_flag.m4 \
+ $(top_srcdir)/m4/ax_check_link_flag.m4 \
+ $(top_srcdir)/m4/code-coverage.m4 \
+ $(top_srcdir)/m4/knot-lib-version.m4 \
+ $(top_srcdir)/m4/knot-module.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)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \
+ $(top_srcdir)/m4/knot-version.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 =
+@HAVE_DAEMON_TRUE@am__EXEEXT_1 = knot/test_acl$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_changeset$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_conf$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_conf_tools$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_confdb$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_confio$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_digest$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_dthreads$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_fdset$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_journal$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_kasp_db$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_node$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_process_query$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_query_module$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_requestor$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_server$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_unreachable$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_worker_pool$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_worker_queue$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone-tree$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone-update$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_events$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_serial$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zone_timers$(EXEEXT) \
+@HAVE_DAEMON_TRUE@ knot/test_zonedb$(EXEEXT)
+@ENABLE_XDP_TRUE@am__EXEEXT_2 = libknot/test_xdp_tcp$(EXEEXT)
+@HAVE_LIBUTILS_TRUE@am__EXEEXT_3 = utils/test_cert$(EXEEXT) \
+@HAVE_LIBUTILS_TRUE@ utils/test_lookup$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_onlinesign_TRUE@am__EXEEXT_4 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_onlinesign_TRUE@@STATIC_MODULE_onlinesign_FALSE@am__EXEEXT_5 = modules/test_onlinesign$(EXEEXT)
+@HAVE_DAEMON_TRUE@@STATIC_MODULE_rrl_TRUE@am__EXEEXT_6 = modules/test_rrl$(EXEEXT)
+@HAVE_DAEMON_TRUE@@SHARED_MODULE_rrl_TRUE@@STATIC_MODULE_rrl_FALSE@am__EXEEXT_7 = modules/test_rrl$(EXEEXT)
+libtap_la_LIBADD =
+am__dirstamp = $(am__leading_dot)dirstamp
+am_libtap_la_OBJECTS = tap/basic.lo tap/files.lo tap/float.lo
+libtap_la_OBJECTS = $(am_libtap_la_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 =
+contrib_test_base32hex_SOURCES = contrib/test_base32hex.c
+contrib_test_base32hex_OBJECTS = contrib/test_base32hex.$(OBJEXT)
+contrib_test_base32hex_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+@HAVE_DAEMON_TRUE@am__DEPENDENCIES_2 = \
+@HAVE_DAEMON_TRUE@ $(top_builddir)/src/libknotd.la \
+@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+contrib_test_base32hex_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_base64_SOURCES = contrib/test_base64.c
+contrib_test_base64_OBJECTS = contrib/test_base64.$(OBJEXT)
+contrib_test_base64_LDADD = $(LDADD)
+contrib_test_base64_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_base64url_SOURCES = contrib/test_base64url.c
+contrib_test_base64url_OBJECTS = contrib/test_base64url.$(OBJEXT)
+contrib_test_base64url_LDADD = $(LDADD)
+contrib_test_base64url_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_heap_SOURCES = contrib/test_heap.c
+contrib_test_heap_OBJECTS = contrib/test_heap.$(OBJEXT)
+contrib_test_heap_LDADD = $(LDADD)
+contrib_test_heap_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_inet_ntop_SOURCES = contrib/test_inet_ntop.c
+contrib_test_inet_ntop_OBJECTS = contrib/test_inet_ntop.$(OBJEXT)
+contrib_test_inet_ntop_LDADD = $(LDADD)
+contrib_test_inet_ntop_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_net_SOURCES = contrib/test_net.c
+contrib_test_net_OBJECTS = contrib/test_net.$(OBJEXT)
+contrib_test_net_LDADD = $(LDADD)
+contrib_test_net_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_net_shortwrite_SOURCES = contrib/test_net_shortwrite.c
+contrib_test_net_shortwrite_OBJECTS = \
+ contrib/test_net_shortwrite.$(OBJEXT)
+contrib_test_net_shortwrite_LDADD = $(LDADD)
+contrib_test_net_shortwrite_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_qp_cow_SOURCES = contrib/test_qp-cow.c
+contrib_test_qp_cow_OBJECTS = contrib/test_qp-cow.$(OBJEXT)
+contrib_test_qp_cow_LDADD = $(LDADD)
+contrib_test_qp_cow_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_qp_trie_SOURCES = contrib/test_qp-trie.c
+contrib_test_qp_trie_OBJECTS = contrib/test_qp-trie.$(OBJEXT)
+contrib_test_qp_trie_LDADD = $(LDADD)
+contrib_test_qp_trie_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_siphash_SOURCES = contrib/test_siphash.c
+contrib_test_siphash_OBJECTS = contrib/test_siphash.$(OBJEXT)
+contrib_test_siphash_LDADD = $(LDADD)
+contrib_test_siphash_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_sockaddr_SOURCES = contrib/test_sockaddr.c
+contrib_test_sockaddr_OBJECTS = contrib/test_sockaddr.$(OBJEXT)
+contrib_test_sockaddr_LDADD = $(LDADD)
+contrib_test_sockaddr_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_spinlock_SOURCES = contrib/test_spinlock.c
+contrib_test_spinlock_OBJECTS = contrib/test_spinlock.$(OBJEXT)
+contrib_test_spinlock_LDADD = $(LDADD)
+contrib_test_spinlock_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_string_SOURCES = contrib/test_string.c
+contrib_test_string_OBJECTS = contrib/test_string.$(OBJEXT)
+contrib_test_string_LDADD = $(LDADD)
+contrib_test_string_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_strtonum_SOURCES = contrib/test_strtonum.c
+contrib_test_strtonum_OBJECTS = contrib/test_strtonum.$(OBJEXT)
+contrib_test_strtonum_LDADD = $(LDADD)
+contrib_test_strtonum_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_time_SOURCES = contrib/test_time.c
+contrib_test_time_OBJECTS = contrib/test_time.$(OBJEXT)
+contrib_test_time_LDADD = $(LDADD)
+contrib_test_time_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_toeplitz_SOURCES = contrib/test_toeplitz.c
+contrib_test_toeplitz_OBJECTS = contrib/test_toeplitz.$(OBJEXT)
+contrib_test_toeplitz_LDADD = $(LDADD)
+contrib_test_toeplitz_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+contrib_test_wire_ctx_SOURCES = contrib/test_wire_ctx.c
+contrib_test_wire_ctx_OBJECTS = contrib/test_wire_ctx.$(OBJEXT)
+contrib_test_wire_ctx_LDADD = $(LDADD)
+contrib_test_wire_ctx_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__knot_test_acl_SOURCES_DIST = knot/test_acl.c knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_acl_OBJECTS = knot/test_acl.$(OBJEXT)
+knot_test_acl_OBJECTS = $(am_knot_test_acl_OBJECTS)
+knot_test_acl_LDADD = $(LDADD)
+knot_test_acl_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_changeset_SOURCES = knot/test_changeset.c
+knot_test_changeset_OBJECTS = knot/test_changeset.$(OBJEXT)
+knot_test_changeset_LDADD = $(LDADD)
+knot_test_changeset_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__knot_test_conf_SOURCES_DIST = knot/test_conf.c knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_conf_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_conf.$(OBJEXT)
+knot_test_conf_OBJECTS = $(am_knot_test_conf_OBJECTS)
+knot_test_conf_LDADD = $(LDADD)
+knot_test_conf_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_conf_tools_SOURCES = knot/test_conf_tools.c
+knot_test_conf_tools_OBJECTS = knot/test_conf_tools.$(OBJEXT)
+knot_test_conf_tools_LDADD = $(LDADD)
+knot_test_conf_tools_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__knot_test_confdb_SOURCES_DIST = knot/test_confdb.c \
+ knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_confdb_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_confdb.$(OBJEXT)
+knot_test_confdb_OBJECTS = $(am_knot_test_confdb_OBJECTS)
+knot_test_confdb_LDADD = $(LDADD)
+knot_test_confdb_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__knot_test_confio_SOURCES_DIST = knot/test_confio.c \
+ knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_confio_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_confio.$(OBJEXT)
+knot_test_confio_OBJECTS = $(am_knot_test_confio_OBJECTS)
+knot_test_confio_LDADD = $(LDADD)
+knot_test_confio_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_digest_SOURCES = knot/test_digest.c
+knot_test_digest_OBJECTS = knot/test_digest.$(OBJEXT)
+knot_test_digest_LDADD = $(LDADD)
+knot_test_digest_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_dthreads_SOURCES = knot/test_dthreads.c
+knot_test_dthreads_OBJECTS = knot/test_dthreads.$(OBJEXT)
+knot_test_dthreads_LDADD = $(LDADD)
+knot_test_dthreads_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_fdset_SOURCES = knot/test_fdset.c
+knot_test_fdset_OBJECTS = knot/test_fdset.$(OBJEXT)
+knot_test_fdset_LDADD = $(LDADD)
+knot_test_fdset_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_journal_SOURCES = knot/test_journal.c
+knot_test_journal_OBJECTS = knot/test_journal.$(OBJEXT)
+knot_test_journal_LDADD = $(LDADD)
+knot_test_journal_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_kasp_db_SOURCES = knot/test_kasp_db.c
+knot_test_kasp_db_OBJECTS = knot/test_kasp_db.$(OBJEXT)
+knot_test_kasp_db_LDADD = $(LDADD)
+knot_test_kasp_db_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_node_SOURCES = knot/test_node.c
+knot_test_node_OBJECTS = knot/test_node.$(OBJEXT)
+knot_test_node_LDADD = $(LDADD)
+knot_test_node_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am__knot_test_process_query_SOURCES_DIST = knot/test_process_query.c \
+ knot/test_server.h knot/test_conf.h
+@HAVE_DAEMON_TRUE@am_knot_test_process_query_OBJECTS = \
+@HAVE_DAEMON_TRUE@ knot/test_process_query.$(OBJEXT)
+knot_test_process_query_OBJECTS = \
+ $(am_knot_test_process_query_OBJECTS)
+knot_test_process_query_LDADD = $(LDADD)
+knot_test_process_query_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_query_module_SOURCES = knot/test_query_module.c
+knot_test_query_module_OBJECTS = knot/test_query_module.$(OBJEXT)
+knot_test_query_module_LDADD = $(LDADD)
+knot_test_query_module_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_requestor_SOURCES = knot/test_requestor.c
+knot_test_requestor_OBJECTS = knot/test_requestor.$(OBJEXT)
+knot_test_requestor_LDADD = $(LDADD)
+knot_test_requestor_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_server_SOURCES = knot/test_server.c
+knot_test_server_OBJECTS = knot/test_server.$(OBJEXT)
+knot_test_server_LDADD = $(LDADD)
+knot_test_server_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_unreachable_SOURCES = knot/test_unreachable.c
+knot_test_unreachable_OBJECTS = knot/test_unreachable.$(OBJEXT)
+knot_test_unreachable_LDADD = $(LDADD)
+knot_test_unreachable_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_worker_pool_SOURCES = knot/test_worker_pool.c
+knot_test_worker_pool_OBJECTS = knot/test_worker_pool.$(OBJEXT)
+knot_test_worker_pool_LDADD = $(LDADD)
+knot_test_worker_pool_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_worker_queue_SOURCES = knot/test_worker_queue.c
+knot_test_worker_queue_OBJECTS = knot/test_worker_queue.$(OBJEXT)
+knot_test_worker_queue_LDADD = $(LDADD)
+knot_test_worker_queue_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zone_tree_SOURCES = knot/test_zone-tree.c
+knot_test_zone_tree_OBJECTS = knot/test_zone-tree.$(OBJEXT)
+knot_test_zone_tree_LDADD = $(LDADD)
+knot_test_zone_tree_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zone_update_SOURCES = knot/test_zone-update.c
+knot_test_zone_update_OBJECTS = knot/test_zone-update.$(OBJEXT)
+knot_test_zone_update_LDADD = $(LDADD)
+knot_test_zone_update_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zone_events_SOURCES = knot/test_zone_events.c
+knot_test_zone_events_OBJECTS = knot/test_zone_events.$(OBJEXT)
+knot_test_zone_events_LDADD = $(LDADD)
+knot_test_zone_events_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zone_serial_SOURCES = knot/test_zone_serial.c
+knot_test_zone_serial_OBJECTS = knot/test_zone_serial.$(OBJEXT)
+knot_test_zone_serial_LDADD = $(LDADD)
+knot_test_zone_serial_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zone_timers_SOURCES = knot/test_zone_timers.c
+knot_test_zone_timers_OBJECTS = knot/test_zone_timers.$(OBJEXT)
+knot_test_zone_timers_LDADD = $(LDADD)
+knot_test_zone_timers_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+knot_test_zonedb_SOURCES = knot/test_zonedb.c
+knot_test_zonedb_OBJECTS = knot/test_zonedb.$(OBJEXT)
+knot_test_zonedb_LDADD = $(LDADD)
+knot_test_zonedb_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_binary_SOURCES = libdnssec/test_binary.c
+libdnssec_test_binary_OBJECTS = libdnssec/test_binary.$(OBJEXT)
+libdnssec_test_binary_LDADD = $(LDADD)
+libdnssec_test_binary_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_crypto_SOURCES = libdnssec/test_crypto.c
+libdnssec_test_crypto_OBJECTS = libdnssec/test_crypto.$(OBJEXT)
+libdnssec_test_crypto_LDADD = $(LDADD)
+libdnssec_test_crypto_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_key_SOURCES = libdnssec/test_key.c
+libdnssec_test_key_OBJECTS = libdnssec/test_key.$(OBJEXT)
+libdnssec_test_key_LDADD = $(LDADD)
+libdnssec_test_key_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_key_algorithm_SOURCES = libdnssec/test_key_algorithm.c
+libdnssec_test_key_algorithm_OBJECTS = \
+ libdnssec/test_key_algorithm.$(OBJEXT)
+libdnssec_test_key_algorithm_LDADD = $(LDADD)
+libdnssec_test_key_algorithm_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_key_ds_SOURCES = libdnssec/test_key_ds.c
+libdnssec_test_key_ds_OBJECTS = libdnssec/test_key_ds.$(OBJEXT)
+libdnssec_test_key_ds_LDADD = $(LDADD)
+libdnssec_test_key_ds_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_keyid_SOURCES = libdnssec/test_keyid.c
+libdnssec_test_keyid_OBJECTS = libdnssec/test_keyid.$(OBJEXT)
+libdnssec_test_keyid_LDADD = $(LDADD)
+libdnssec_test_keyid_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_keystore_pkcs11_SOURCES = \
+ libdnssec/test_keystore_pkcs11.c
+libdnssec_test_keystore_pkcs11_OBJECTS = \
+ libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.$(OBJEXT)
+libdnssec_test_keystore_pkcs11_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs11_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_keystore_pkcs8_SOURCES = \
+ libdnssec/test_keystore_pkcs8.c
+libdnssec_test_keystore_pkcs8_OBJECTS = \
+ libdnssec/test_keystore_pkcs8.$(OBJEXT)
+libdnssec_test_keystore_pkcs8_LDADD = $(LDADD)
+libdnssec_test_keystore_pkcs8_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_keytag_SOURCES = libdnssec/test_keytag.c
+libdnssec_test_keytag_OBJECTS = libdnssec/test_keytag.$(OBJEXT)
+libdnssec_test_keytag_LDADD = $(LDADD)
+libdnssec_test_keytag_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_nsec_bitmap_SOURCES = libdnssec/test_nsec_bitmap.c
+libdnssec_test_nsec_bitmap_OBJECTS = \
+ libdnssec/test_nsec_bitmap.$(OBJEXT)
+libdnssec_test_nsec_bitmap_LDADD = $(LDADD)
+libdnssec_test_nsec_bitmap_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_nsec_hash_SOURCES = libdnssec/test_nsec_hash.c
+libdnssec_test_nsec_hash_OBJECTS = libdnssec/test_nsec_hash.$(OBJEXT)
+libdnssec_test_nsec_hash_LDADD = $(LDADD)
+libdnssec_test_nsec_hash_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_random_SOURCES = libdnssec/test_random.c
+libdnssec_test_random_OBJECTS = libdnssec/test_random.$(OBJEXT)
+libdnssec_test_random_LDADD = $(LDADD)
+libdnssec_test_random_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_shared_bignum_SOURCES = libdnssec/test_shared_bignum.c
+libdnssec_test_shared_bignum_OBJECTS = \
+ libdnssec/test_shared_bignum.$(OBJEXT)
+libdnssec_test_shared_bignum_LDADD = $(LDADD)
+libdnssec_test_shared_bignum_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_shared_dname_SOURCES = libdnssec/test_shared_dname.c
+libdnssec_test_shared_dname_OBJECTS = \
+ libdnssec/test_shared_dname.$(OBJEXT)
+libdnssec_test_shared_dname_LDADD = $(LDADD)
+libdnssec_test_shared_dname_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_sign_SOURCES = libdnssec/test_sign.c
+libdnssec_test_sign_OBJECTS = libdnssec/test_sign.$(OBJEXT)
+libdnssec_test_sign_LDADD = $(LDADD)
+libdnssec_test_sign_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_sign_der_SOURCES = libdnssec/test_sign_der.c
+libdnssec_test_sign_der_OBJECTS = libdnssec/test_sign_der.$(OBJEXT)
+libdnssec_test_sign_der_LDADD = $(LDADD)
+libdnssec_test_sign_der_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libdnssec_test_tsig_SOURCES = libdnssec/test_tsig.c
+libdnssec_test_tsig_OBJECTS = libdnssec/test_tsig.$(OBJEXT)
+libdnssec_test_tsig_LDADD = $(LDADD)
+libdnssec_test_tsig_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_control_SOURCES = libknot/test_control.c
+libknot_test_control_OBJECTS = libknot/test_control.$(OBJEXT)
+libknot_test_control_LDADD = $(LDADD)
+libknot_test_control_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_cookies_SOURCES = libknot/test_cookies.c
+libknot_test_cookies_OBJECTS = libknot/test_cookies.$(OBJEXT)
+libknot_test_cookies_LDADD = $(LDADD)
+libknot_test_cookies_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_db_SOURCES = libknot/test_db.c
+libknot_test_db_OBJECTS = libknot/test_db.$(OBJEXT)
+libknot_test_db_LDADD = $(LDADD)
+libknot_test_db_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_descriptor_SOURCES = libknot/test_descriptor.c
+libknot_test_descriptor_OBJECTS = libknot/test_descriptor.$(OBJEXT)
+libknot_test_descriptor_LDADD = $(LDADD)
+libknot_test_descriptor_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_dname_SOURCES = libknot/test_dname.c
+libknot_test_dname_OBJECTS = libknot/test_dname.$(OBJEXT)
+libknot_test_dname_LDADD = $(LDADD)
+libknot_test_dname_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_dynarray_SOURCES = libknot/test_dynarray.c
+libknot_test_dynarray_OBJECTS = libknot/test_dynarray.$(OBJEXT)
+libknot_test_dynarray_LDADD = $(LDADD)
+libknot_test_dynarray_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_edns_SOURCES = libknot/test_edns.c
+libknot_test_edns_OBJECTS = libknot/test_edns.$(OBJEXT)
+libknot_test_edns_LDADD = $(LDADD)
+libknot_test_edns_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_edns_ecs_SOURCES = libknot/test_edns_ecs.c
+libknot_test_edns_ecs_OBJECTS = libknot/test_edns_ecs.$(OBJEXT)
+libknot_test_edns_ecs_LDADD = $(LDADD)
+libknot_test_edns_ecs_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_endian_SOURCES = libknot/test_endian.c
+libknot_test_endian_OBJECTS = libknot/test_endian.$(OBJEXT)
+libknot_test_endian_LDADD = $(LDADD)
+libknot_test_endian_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_lookup_SOURCES = libknot/test_lookup.c
+libknot_test_lookup_OBJECTS = libknot/test_lookup.$(OBJEXT)
+libknot_test_lookup_LDADD = $(LDADD)
+libknot_test_lookup_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_pkt_SOURCES = libknot/test_pkt.c
+libknot_test_pkt_OBJECTS = libknot/test_pkt.$(OBJEXT)
+libknot_test_pkt_LDADD = $(LDADD)
+libknot_test_pkt_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_probe_SOURCES = libknot/test_probe.c
+libknot_test_probe_OBJECTS = libknot/test_probe.$(OBJEXT)
+libknot_test_probe_LDADD = $(LDADD)
+libknot_test_probe_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_rdata_SOURCES = libknot/test_rdata.c
+libknot_test_rdata_OBJECTS = libknot/test_rdata.$(OBJEXT)
+libknot_test_rdata_LDADD = $(LDADD)
+libknot_test_rdata_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_rdataset_SOURCES = libknot/test_rdataset.c
+libknot_test_rdataset_OBJECTS = libknot/test_rdataset.$(OBJEXT)
+libknot_test_rdataset_LDADD = $(LDADD)
+libknot_test_rdataset_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_rrset_SOURCES = libknot/test_rrset.c
+libknot_test_rrset_OBJECTS = libknot/test_rrset.$(OBJEXT)
+libknot_test_rrset_LDADD = $(LDADD)
+libknot_test_rrset_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_rrset_wire_SOURCES = libknot/test_rrset-wire.c
+libknot_test_rrset_wire_OBJECTS = libknot/test_rrset-wire.$(OBJEXT)
+libknot_test_rrset_wire_LDADD = $(LDADD)
+libknot_test_rrset_wire_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_tsig_SOURCES = libknot/test_tsig.c
+libknot_test_tsig_OBJECTS = libknot/test_tsig.$(OBJEXT)
+libknot_test_tsig_LDADD = $(LDADD)
+libknot_test_tsig_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_wire_SOURCES = libknot/test_wire.c
+libknot_test_wire_OBJECTS = libknot/test_wire.$(OBJEXT)
+libknot_test_wire_LDADD = $(LDADD)
+libknot_test_wire_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_xdp_tcp_SOURCES = libknot/test_xdp_tcp.c
+libknot_test_xdp_tcp_OBJECTS = libknot/test_xdp_tcp.$(OBJEXT)
+libknot_test_xdp_tcp_LDADD = $(LDADD)
+libknot_test_xdp_tcp_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_yparser_SOURCES = libknot/test_yparser.c
+libknot_test_yparser_OBJECTS = libknot/test_yparser.$(OBJEXT)
+libknot_test_yparser_LDADD = $(LDADD)
+libknot_test_yparser_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_ypschema_SOURCES = libknot/test_ypschema.c
+libknot_test_ypschema_OBJECTS = libknot/test_ypschema.$(OBJEXT)
+libknot_test_ypschema_LDADD = $(LDADD)
+libknot_test_ypschema_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+libknot_test_yptrafo_SOURCES = libknot/test_yptrafo.c
+libknot_test_yptrafo_OBJECTS = libknot/test_yptrafo.$(OBJEXT)
+libknot_test_yptrafo_LDADD = $(LDADD)
+libknot_test_yptrafo_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_libzscanner_zscanner_tool_OBJECTS = \
+ libzscanner/zscanner-tool.$(OBJEXT) \
+ libzscanner/processing.$(OBJEXT)
+libzscanner_zscanner_tool_OBJECTS = \
+ $(am_libzscanner_zscanner_tool_OBJECTS)
+libzscanner_zscanner_tool_LDADD = $(LDADD)
+libzscanner_zscanner_tool_DEPENDENCIES = libtap.la \
+ $(am__DEPENDENCIES_2) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+modules_test_onlinesign_SOURCES = modules/test_onlinesign.c
+modules_test_onlinesign_OBJECTS = modules/test_onlinesign.$(OBJEXT)
+modules_test_onlinesign_LDADD = $(LDADD)
+modules_test_onlinesign_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+modules_test_rrl_SOURCES = modules/test_rrl.c
+modules_test_rrl_OBJECTS = modules/test_rrl.$(OBJEXT)
+modules_test_rrl_LDADD = $(LDADD)
+modules_test_rrl_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+tap_runtests_SOURCES = tap/runtests.c
+tap_runtests_OBJECTS = tap/runtests.$(OBJEXT)
+tap_runtests_LDADD = $(LDADD)
+tap_runtests_DEPENDENCIES = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+utils_test_cert_SOURCES = utils/test_cert.c
+utils_test_cert_OBJECTS = utils/test_cert-test_cert.$(OBJEXT)
+am__DEPENDENCIES_3 = libtap.la $(am__DEPENDENCIES_2) \
+ $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+@HAVE_LIBUTILS_TRUE@utils_test_cert_DEPENDENCIES = \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_3)
+utils_test_lookup_SOURCES = utils/test_lookup.c
+utils_test_lookup_OBJECTS = \
+ utils/utils_test_lookup-test_lookup.$(OBJEXT)
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_DEPENDENCIES = \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \
+@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_3)
+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@ -I$(top_builddir)/src
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = contrib/$(DEPDIR)/test_base32hex.Po \
+ contrib/$(DEPDIR)/test_base64.Po \
+ contrib/$(DEPDIR)/test_base64url.Po \
+ contrib/$(DEPDIR)/test_heap.Po \
+ contrib/$(DEPDIR)/test_inet_ntop.Po \
+ contrib/$(DEPDIR)/test_net.Po \
+ contrib/$(DEPDIR)/test_net_shortwrite.Po \
+ contrib/$(DEPDIR)/test_qp-cow.Po \
+ contrib/$(DEPDIR)/test_qp-trie.Po \
+ contrib/$(DEPDIR)/test_siphash.Po \
+ contrib/$(DEPDIR)/test_sockaddr.Po \
+ contrib/$(DEPDIR)/test_spinlock.Po \
+ contrib/$(DEPDIR)/test_string.Po \
+ contrib/$(DEPDIR)/test_strtonum.Po \
+ contrib/$(DEPDIR)/test_time.Po \
+ contrib/$(DEPDIR)/test_toeplitz.Po \
+ contrib/$(DEPDIR)/test_wire_ctx.Po knot/$(DEPDIR)/test_acl.Po \
+ knot/$(DEPDIR)/test_changeset.Po knot/$(DEPDIR)/test_conf.Po \
+ knot/$(DEPDIR)/test_conf_tools.Po \
+ knot/$(DEPDIR)/test_confdb.Po knot/$(DEPDIR)/test_confio.Po \
+ knot/$(DEPDIR)/test_digest.Po knot/$(DEPDIR)/test_dthreads.Po \
+ knot/$(DEPDIR)/test_fdset.Po knot/$(DEPDIR)/test_journal.Po \
+ knot/$(DEPDIR)/test_kasp_db.Po knot/$(DEPDIR)/test_node.Po \
+ knot/$(DEPDIR)/test_process_query.Po \
+ knot/$(DEPDIR)/test_query_module.Po \
+ knot/$(DEPDIR)/test_requestor.Po knot/$(DEPDIR)/test_server.Po \
+ knot/$(DEPDIR)/test_unreachable.Po \
+ knot/$(DEPDIR)/test_worker_pool.Po \
+ knot/$(DEPDIR)/test_worker_queue.Po \
+ knot/$(DEPDIR)/test_zone-tree.Po \
+ knot/$(DEPDIR)/test_zone-update.Po \
+ knot/$(DEPDIR)/test_zone_events.Po \
+ knot/$(DEPDIR)/test_zone_serial.Po \
+ knot/$(DEPDIR)/test_zone_timers.Po \
+ knot/$(DEPDIR)/test_zonedb.Po \
+ libdnssec/$(DEPDIR)/test_binary.Po \
+ libdnssec/$(DEPDIR)/test_crypto.Po \
+ libdnssec/$(DEPDIR)/test_key.Po \
+ libdnssec/$(DEPDIR)/test_key_algorithm.Po \
+ libdnssec/$(DEPDIR)/test_key_ds.Po \
+ libdnssec/$(DEPDIR)/test_keyid.Po \
+ libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po \
+ libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po \
+ libdnssec/$(DEPDIR)/test_keytag.Po \
+ libdnssec/$(DEPDIR)/test_nsec_bitmap.Po \
+ libdnssec/$(DEPDIR)/test_nsec_hash.Po \
+ libdnssec/$(DEPDIR)/test_random.Po \
+ libdnssec/$(DEPDIR)/test_shared_bignum.Po \
+ libdnssec/$(DEPDIR)/test_shared_dname.Po \
+ libdnssec/$(DEPDIR)/test_sign.Po \
+ libdnssec/$(DEPDIR)/test_sign_der.Po \
+ libdnssec/$(DEPDIR)/test_tsig.Po \
+ libknot/$(DEPDIR)/test_control.Po \
+ libknot/$(DEPDIR)/test_cookies.Po libknot/$(DEPDIR)/test_db.Po \
+ libknot/$(DEPDIR)/test_descriptor.Po \
+ libknot/$(DEPDIR)/test_dname.Po \
+ libknot/$(DEPDIR)/test_dynarray.Po \
+ libknot/$(DEPDIR)/test_edns.Po \
+ libknot/$(DEPDIR)/test_edns_ecs.Po \
+ libknot/$(DEPDIR)/test_endian.Po \
+ libknot/$(DEPDIR)/test_lookup.Po libknot/$(DEPDIR)/test_pkt.Po \
+ libknot/$(DEPDIR)/test_probe.Po \
+ libknot/$(DEPDIR)/test_rdata.Po \
+ libknot/$(DEPDIR)/test_rdataset.Po \
+ libknot/$(DEPDIR)/test_rrset-wire.Po \
+ libknot/$(DEPDIR)/test_rrset.Po libknot/$(DEPDIR)/test_tsig.Po \
+ libknot/$(DEPDIR)/test_wire.Po \
+ libknot/$(DEPDIR)/test_xdp_tcp.Po \
+ libknot/$(DEPDIR)/test_yparser.Po \
+ libknot/$(DEPDIR)/test_ypschema.Po \
+ libknot/$(DEPDIR)/test_yptrafo.Po \
+ libzscanner/$(DEPDIR)/processing.Po \
+ libzscanner/$(DEPDIR)/zscanner-tool.Po \
+ modules/$(DEPDIR)/test_onlinesign.Po \
+ modules/$(DEPDIR)/test_rrl.Po tap/$(DEPDIR)/basic.Plo \
+ tap/$(DEPDIR)/files.Plo tap/$(DEPDIR)/float.Plo \
+ tap/$(DEPDIR)/runtests.Po \
+ utils/$(DEPDIR)/test_cert-test_cert.Po \
+ utils/$(DEPDIR)/utils_test_lookup-test_lookup.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 = $(libtap_la_SOURCES) contrib/test_base32hex.c \
+ contrib/test_base64.c contrib/test_base64url.c \
+ contrib/test_heap.c contrib/test_inet_ntop.c \
+ contrib/test_net.c contrib/test_net_shortwrite.c \
+ contrib/test_qp-cow.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_spinlock.c contrib/test_string.c \
+ contrib/test_strtonum.c contrib/test_time.c \
+ contrib/test_toeplitz.c contrib/test_wire_ctx.c \
+ $(knot_test_acl_SOURCES) knot/test_changeset.c \
+ $(knot_test_conf_SOURCES) knot/test_conf_tools.c \
+ $(knot_test_confdb_SOURCES) $(knot_test_confio_SOURCES) \
+ knot/test_digest.c knot/test_dthreads.c knot/test_fdset.c \
+ knot/test_journal.c knot/test_kasp_db.c knot/test_node.c \
+ $(knot_test_process_query_SOURCES) knot/test_query_module.c \
+ knot/test_requestor.c knot/test_server.c \
+ knot/test_unreachable.c knot/test_worker_pool.c \
+ knot/test_worker_queue.c knot/test_zone-tree.c \
+ knot/test_zone-update.c knot/test_zone_events.c \
+ knot/test_zone_serial.c knot/test_zone_timers.c \
+ knot/test_zonedb.c libdnssec/test_binary.c \
+ libdnssec/test_crypto.c libdnssec/test_key.c \
+ libdnssec/test_key_algorithm.c libdnssec/test_key_ds.c \
+ libdnssec/test_keyid.c libdnssec/test_keystore_pkcs11.c \
+ libdnssec/test_keystore_pkcs8.c libdnssec/test_keytag.c \
+ libdnssec/test_nsec_bitmap.c libdnssec/test_nsec_hash.c \
+ libdnssec/test_random.c libdnssec/test_shared_bignum.c \
+ libdnssec/test_shared_dname.c libdnssec/test_sign.c \
+ libdnssec/test_sign_der.c libdnssec/test_tsig.c \
+ libknot/test_control.c libknot/test_cookies.c \
+ libknot/test_db.c libknot/test_descriptor.c \
+ libknot/test_dname.c libknot/test_dynarray.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_probe.c libknot/test_rdata.c \
+ libknot/test_rdataset.c libknot/test_rrset.c \
+ libknot/test_rrset-wire.c libknot/test_tsig.c \
+ libknot/test_wire.c libknot/test_xdp_tcp.c \
+ libknot/test_yparser.c libknot/test_ypschema.c \
+ libknot/test_yptrafo.c $(libzscanner_zscanner_tool_SOURCES) \
+ modules/test_onlinesign.c modules/test_rrl.c tap/runtests.c \
+ utils/test_cert.c utils/test_lookup.c
+DIST_SOURCES = $(libtap_la_SOURCES) contrib/test_base32hex.c \
+ contrib/test_base64.c contrib/test_base64url.c \
+ contrib/test_heap.c contrib/test_inet_ntop.c \
+ contrib/test_net.c contrib/test_net_shortwrite.c \
+ contrib/test_qp-cow.c contrib/test_qp-trie.c \
+ contrib/test_siphash.c contrib/test_sockaddr.c \
+ contrib/test_spinlock.c contrib/test_string.c \
+ contrib/test_strtonum.c contrib/test_time.c \
+ contrib/test_toeplitz.c contrib/test_wire_ctx.c \
+ $(am__knot_test_acl_SOURCES_DIST) knot/test_changeset.c \
+ $(am__knot_test_conf_SOURCES_DIST) knot/test_conf_tools.c \
+ $(am__knot_test_confdb_SOURCES_DIST) \
+ $(am__knot_test_confio_SOURCES_DIST) knot/test_digest.c \
+ knot/test_dthreads.c knot/test_fdset.c knot/test_journal.c \
+ knot/test_kasp_db.c knot/test_node.c \
+ $(am__knot_test_process_query_SOURCES_DIST) \
+ knot/test_query_module.c knot/test_requestor.c \
+ knot/test_server.c knot/test_unreachable.c \
+ knot/test_worker_pool.c knot/test_worker_queue.c \
+ knot/test_zone-tree.c knot/test_zone-update.c \
+ knot/test_zone_events.c knot/test_zone_serial.c \
+ knot/test_zone_timers.c knot/test_zonedb.c \
+ libdnssec/test_binary.c libdnssec/test_crypto.c \
+ libdnssec/test_key.c libdnssec/test_key_algorithm.c \
+ libdnssec/test_key_ds.c libdnssec/test_keyid.c \
+ libdnssec/test_keystore_pkcs11.c \
+ libdnssec/test_keystore_pkcs8.c libdnssec/test_keytag.c \
+ libdnssec/test_nsec_bitmap.c libdnssec/test_nsec_hash.c \
+ libdnssec/test_random.c libdnssec/test_shared_bignum.c \
+ libdnssec/test_shared_dname.c libdnssec/test_sign.c \
+ libdnssec/test_sign_der.c libdnssec/test_tsig.c \
+ libknot/test_control.c libknot/test_cookies.c \
+ libknot/test_db.c libknot/test_descriptor.c \
+ libknot/test_dname.c libknot/test_dynarray.c \
+ libknot/test_edns.c libknot/test_edns_ecs.c \
+ libknot/test_endian.c libknot/test_lookup.c libknot/test_pkt.c \
+ libknot/test_probe.c libknot/test_rdata.c \
+ libknot/test_rdataset.c libknot/test_rrset.c \
+ libknot/test_rrset-wire.c libknot/test_tsig.c \
+ libknot/test_wire.c libknot/test_xdp_tcp.c \
+ libknot/test_yparser.c libknot/test_ypschema.c \
+ libknot/test_yptrafo.c $(libzscanner_zscanner_tool_SOURCES) \
+ modules/test_onlinesign.c modules/test_rrl.c tap/runtests.c \
+ utils/test_cert.c utils/test_lookup.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# 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
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+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@
+CFLAG_VISIBILITY = @CFLAG_VISIBILITY@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DNSTAP_CFLAGS = @DNSTAP_CFLAGS@
+DNSTAP_LIBS = @DNSTAP_LIBS@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+HAVE_VISIBILITY = @HAVE_VISIBILITY@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@
+KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@
+KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_NO_UNDEFINED = @LT_NO_UNDEFINED@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+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@
+PDFLATEX = @PDFLATEX@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PROTOC_C = @PROTOC_C@
+RANLIB = @RANLIB@
+RELEASE_DATE = @RELEASE_DATE@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+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@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cap_ng_CFLAGS = @cap_ng_CFLAGS@
+cap_ng_LIBS = @cap_ng_LIBS@
+conf_mapsize = @conf_mapsize@
+config_dir = @config_dir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dlopen_LIBS = @dlopen_LIBS@
+docdir = @docdir@
+dvidir = @dvidir@
+embedded_libbpf_CFLAGS = @embedded_libbpf_CFLAGS@
+embedded_libbpf_LIBS = @embedded_libbpf_LIBS@
+embedded_libngtcp2_CFLAGS = @embedded_libngtcp2_CFLAGS@
+embedded_libngtcp2_LIBS = @embedded_libngtcp2_LIBS@
+exec_prefix = @exec_prefix@
+fuzzer_CFLAGS = @fuzzer_CFLAGS@
+fuzzer_LDFLAGS = @fuzzer_LDFLAGS@
+gnutls_CFLAGS = @gnutls_CFLAGS@
+gnutls_LIBS = @gnutls_LIBS@
+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@
+libbpf_CFLAGS = @libbpf_CFLAGS@
+libbpf_LIBS = @libbpf_LIBS@
+libdir = @libdir@
+libdnssec_SONAME = @libdnssec_SONAME@
+libdnssec_SOVERSION = @libdnssec_SOVERSION@
+libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@
+libedit_CFLAGS = @libedit_CFLAGS@
+libedit_LIBS = @libedit_LIBS@
+libelf_CFLAGS = @libelf_CFLAGS@
+libelf_LIBS = @libelf_LIBS@
+libexecdir = @libexecdir@
+libfstrm_CFLAGS = @libfstrm_CFLAGS@
+libfstrm_LIBS = @libfstrm_LIBS@
+libidn2_CFLAGS = @libidn2_CFLAGS@
+libidn2_LIBS = @libidn2_LIBS@
+libidn_CFLAGS = @libidn_CFLAGS@
+libidn_LIBS = @libidn_LIBS@
+libknot_SONAME = @libknot_SONAME@
+libknot_SOVERSION = @libknot_SOVERSION@
+libknot_VERSION_INFO = @libknot_VERSION_INFO@
+libkqueue_CFLAGS = @libkqueue_CFLAGS@
+libkqueue_LIBS = @libkqueue_LIBS@
+libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@
+libmaxminddb_LIBS = @libmaxminddb_LIBS@
+libmnl_CFLAGS = @libmnl_CFLAGS@
+libmnl_LIBS = @libmnl_LIBS@
+libnghttp2_CFLAGS = @libnghttp2_CFLAGS@
+libnghttp2_LIBS = @libnghttp2_LIBS@
+libngtcp2_CFLAGS = @libngtcp2_CFLAGS@
+libngtcp2_LIBS = @libngtcp2_LIBS@
+libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@
+libprotobuf_c_LIBS = @libprotobuf_c_LIBS@
+liburcu_CFLAGS = @liburcu_CFLAGS@
+liburcu_LIBS = @liburcu_LIBS@
+liburcu_PKGCONFIG = @liburcu_PKGCONFIG@
+libxdp_CFLAGS = @libxdp_CFLAGS@
+libxdp_LIBS = @libxdp_LIBS@
+libzscanner_SONAME = @libzscanner_SONAME@
+libzscanner_SOVERSION = @libzscanner_SOVERSION@
+libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@
+lmdb_CFLAGS = @lmdb_CFLAGS@
+lmdb_LIBS = @lmdb_LIBS@
+localedir = @localedir@
+localstatedir = @localstatedir@
+malloc_LIBS = @malloc_LIBS@
+mandir = @mandir@
+math_LIBS = @math_LIBS@
+mkdir_p = @mkdir_p@
+module_dir = @module_dir@
+module_instdir = @module_instdir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgconfigdir = @pkgconfigdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pthread_LIBS = @pthread_LIBS@
+run_dir = @run_dir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+storage_dir = @storage_dir@
+sysconfdir = @sysconfdir@
+systemd_CFLAGS = @systemd_CFLAGS@
+systemd_LIBS = @systemd_LIBS@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+AM_CPPFLAGS = -include $(top_builddir)/src/config.h \
+ -I$(top_srcdir)/src -I$(top_srcdir)/src/libdnssec \
+ -I$(top_srcdir)/src/libdnssec/shared $(gnutls_CFLAGS) \
+ $(libkqueue_CFLAGS) $(lmdb_CFLAGS) $(am__append_3)
+LDADD = libtap.la $(am__append_1) $(top_builddir)/src/libknot.la \
+ $(top_builddir)/src/libdnssec.la \
+ $(top_builddir)/src/libcontrib.la \
+ $(top_builddir)/src/libzscanner.la $(gnutls_LIBS) $(lmdb_LIBS)
+EXTRA_DIST = \
+ tap/libtap.sh \
+ libdnssec/sample_keys.h \
+ knot/semantic_check_data \
+ knot/test_semantic_check.in \
+ libzscanner/data \
+ libzscanner/test_zscanner.in \
+ libzscanner/TESTS
+
+check_LTLIBRARIES = libtap.la
+libtap_la_SOURCES = \
+ tap/basic.c \
+ tap/basic.h \
+ tap/files.c \
+ tap/files.h \
+ tap/float.c \
+ tap/float.h \
+ tap/macros.h
+
+@HAVE_DAEMON_TRUE@knot_test_acl_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_acl.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_conf_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_conf.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_confdb_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_confdb.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_confio_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_confio.c \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+@HAVE_DAEMON_TRUE@knot_test_process_query_SOURCES = \
+@HAVE_DAEMON_TRUE@ knot/test_process_query.c \
+@HAVE_DAEMON_TRUE@ knot/test_server.h \
+@HAVE_DAEMON_TRUE@ knot/test_conf.h
+
+libdnssec_test_keystore_pkcs11_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -DLIBDIR='"$(libdir)"'
+
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_CPPFLAGS = \
+@HAVE_LIBUTILS_TRUE@ $(AM_CPPFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libedit_CFLAGS)
+
+@HAVE_LIBUTILS_TRUE@utils_test_lookup_LDADD = \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(LDADD)
+
+@HAVE_LIBUTILS_TRUE@utils_test_cert_CPPFLAGS = \
+@HAVE_LIBUTILS_TRUE@ $(AM_CPPFLAGS) \
+@HAVE_LIBUTILS_TRUE@ $(libedit_CFLAGS)
+
+@HAVE_LIBUTILS_TRUE@utils_test_cert_LDADD = \
+@HAVE_LIBUTILS_TRUE@ $(top_builddir)/src/libknotus.la \
+@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS) \
+@HAVE_LIBUTILS_TRUE@ $(LDADD)
+
+libzscanner_zscanner_tool_SOURCES = \
+ libzscanner/zscanner-tool.c \
+ libzscanner/processing.h \
+ libzscanner/processing.c
+
+check_SCRIPTS = libzscanner/test_zscanner $(am__append_10)
+edit = $(SED) \
+ -e 's|@top_srcdir[@]|$(abs_top_srcdir)|g' \
+ -e 's|@top_builddir[@]|$(abs_top_builddir)|g'
+
+CLEANFILES = $(check_SCRIPTS) $(EXTRA_PROGRAMS) runtests.log
+AM_V_RUNTESTS = $(am__v_RUNTESTS_@AM_V@)
+am__v_RUNTESTS_ = $(am__v_RUNTESTS_@AM_DEFAULT_V@)
+am__v_RUNTESTS_0 =
+am__v_RUNTESTS_1 = RET=$$?; if [ "$$RET" != "0" ]; then cat "$(builddir)/runtests.log"; exit $$RET; fi
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .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 tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign tests/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):
+
+clean-checkPROGRAMS:
+ @list='$(check_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
+
+clean-checkLTLIBRARIES:
+ -test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES)
+ @list='$(check_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+tap/$(am__dirstamp):
+ @$(MKDIR_P) tap
+ @: > tap/$(am__dirstamp)
+tap/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) tap/$(DEPDIR)
+ @: > tap/$(DEPDIR)/$(am__dirstamp)
+tap/basic.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+tap/files.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+tap/float.lo: tap/$(am__dirstamp) tap/$(DEPDIR)/$(am__dirstamp)
+
+libtap.la: $(libtap_la_OBJECTS) $(libtap_la_DEPENDENCIES) $(EXTRA_libtap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libtap_la_OBJECTS) $(libtap_la_LIBADD) $(LIBS)
+contrib/$(am__dirstamp):
+ @$(MKDIR_P) contrib
+ @: > contrib/$(am__dirstamp)
+contrib/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) contrib/$(DEPDIR)
+ @: > contrib/$(DEPDIR)/$(am__dirstamp)
+contrib/test_base32hex.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base32hex$(EXEEXT): $(contrib_test_base32hex_OBJECTS) $(contrib_test_base32hex_DEPENDENCIES) $(EXTRA_contrib_test_base32hex_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base32hex$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base32hex_OBJECTS) $(contrib_test_base32hex_LDADD) $(LIBS)
+contrib/test_base64.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base64$(EXEEXT): $(contrib_test_base64_OBJECTS) $(contrib_test_base64_DEPENDENCIES) $(EXTRA_contrib_test_base64_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base64$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base64_OBJECTS) $(contrib_test_base64_LDADD) $(LIBS)
+contrib/test_base64url.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_base64url$(EXEEXT): $(contrib_test_base64url_OBJECTS) $(contrib_test_base64url_DEPENDENCIES) $(EXTRA_contrib_test_base64url_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_base64url$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_base64url_OBJECTS) $(contrib_test_base64url_LDADD) $(LIBS)
+contrib/test_heap.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_heap$(EXEEXT): $(contrib_test_heap_OBJECTS) $(contrib_test_heap_DEPENDENCIES) $(EXTRA_contrib_test_heap_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_heap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_heap_OBJECTS) $(contrib_test_heap_LDADD) $(LIBS)
+contrib/test_inet_ntop.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_inet_ntop$(EXEEXT): $(contrib_test_inet_ntop_OBJECTS) $(contrib_test_inet_ntop_DEPENDENCIES) $(EXTRA_contrib_test_inet_ntop_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_inet_ntop$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_inet_ntop_OBJECTS) $(contrib_test_inet_ntop_LDADD) $(LIBS)
+contrib/test_net.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_net$(EXEEXT): $(contrib_test_net_OBJECTS) $(contrib_test_net_DEPENDENCIES) $(EXTRA_contrib_test_net_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_net$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_net_OBJECTS) $(contrib_test_net_LDADD) $(LIBS)
+contrib/test_net_shortwrite.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_net_shortwrite$(EXEEXT): $(contrib_test_net_shortwrite_OBJECTS) $(contrib_test_net_shortwrite_DEPENDENCIES) $(EXTRA_contrib_test_net_shortwrite_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_net_shortwrite$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_net_shortwrite_OBJECTS) $(contrib_test_net_shortwrite_LDADD) $(LIBS)
+contrib/test_qp-cow.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_qp-cow$(EXEEXT): $(contrib_test_qp_cow_OBJECTS) $(contrib_test_qp_cow_DEPENDENCIES) $(EXTRA_contrib_test_qp_cow_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_qp-cow$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_qp_cow_OBJECTS) $(contrib_test_qp_cow_LDADD) $(LIBS)
+contrib/test_qp-trie.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_qp-trie$(EXEEXT): $(contrib_test_qp_trie_OBJECTS) $(contrib_test_qp_trie_DEPENDENCIES) $(EXTRA_contrib_test_qp_trie_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_qp-trie$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_qp_trie_OBJECTS) $(contrib_test_qp_trie_LDADD) $(LIBS)
+contrib/test_siphash.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_siphash$(EXEEXT): $(contrib_test_siphash_OBJECTS) $(contrib_test_siphash_DEPENDENCIES) $(EXTRA_contrib_test_siphash_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_siphash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_siphash_OBJECTS) $(contrib_test_siphash_LDADD) $(LIBS)
+contrib/test_sockaddr.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_sockaddr$(EXEEXT): $(contrib_test_sockaddr_OBJECTS) $(contrib_test_sockaddr_DEPENDENCIES) $(EXTRA_contrib_test_sockaddr_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_sockaddr$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_sockaddr_OBJECTS) $(contrib_test_sockaddr_LDADD) $(LIBS)
+contrib/test_spinlock.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_spinlock$(EXEEXT): $(contrib_test_spinlock_OBJECTS) $(contrib_test_spinlock_DEPENDENCIES) $(EXTRA_contrib_test_spinlock_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_spinlock$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_spinlock_OBJECTS) $(contrib_test_spinlock_LDADD) $(LIBS)
+contrib/test_string.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_string$(EXEEXT): $(contrib_test_string_OBJECTS) $(contrib_test_string_DEPENDENCIES) $(EXTRA_contrib_test_string_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_string$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_string_OBJECTS) $(contrib_test_string_LDADD) $(LIBS)
+contrib/test_strtonum.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_strtonum$(EXEEXT): $(contrib_test_strtonum_OBJECTS) $(contrib_test_strtonum_DEPENDENCIES) $(EXTRA_contrib_test_strtonum_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_strtonum$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_strtonum_OBJECTS) $(contrib_test_strtonum_LDADD) $(LIBS)
+contrib/test_time.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_time$(EXEEXT): $(contrib_test_time_OBJECTS) $(contrib_test_time_DEPENDENCIES) $(EXTRA_contrib_test_time_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_time$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_time_OBJECTS) $(contrib_test_time_LDADD) $(LIBS)
+contrib/test_toeplitz.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_toeplitz$(EXEEXT): $(contrib_test_toeplitz_OBJECTS) $(contrib_test_toeplitz_DEPENDENCIES) $(EXTRA_contrib_test_toeplitz_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_toeplitz$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_toeplitz_OBJECTS) $(contrib_test_toeplitz_LDADD) $(LIBS)
+contrib/test_wire_ctx.$(OBJEXT): contrib/$(am__dirstamp) \
+ contrib/$(DEPDIR)/$(am__dirstamp)
+
+contrib/test_wire_ctx$(EXEEXT): $(contrib_test_wire_ctx_OBJECTS) $(contrib_test_wire_ctx_DEPENDENCIES) $(EXTRA_contrib_test_wire_ctx_DEPENDENCIES) contrib/$(am__dirstamp)
+ @rm -f contrib/test_wire_ctx$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(contrib_test_wire_ctx_OBJECTS) $(contrib_test_wire_ctx_LDADD) $(LIBS)
+knot/$(am__dirstamp):
+ @$(MKDIR_P) knot
+ @: > knot/$(am__dirstamp)
+knot/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) knot/$(DEPDIR)
+ @: > knot/$(DEPDIR)/$(am__dirstamp)
+knot/test_acl.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_acl$(EXEEXT): $(knot_test_acl_OBJECTS) $(knot_test_acl_DEPENDENCIES) $(EXTRA_knot_test_acl_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_acl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_acl_OBJECTS) $(knot_test_acl_LDADD) $(LIBS)
+knot/test_changeset.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_changeset$(EXEEXT): $(knot_test_changeset_OBJECTS) $(knot_test_changeset_DEPENDENCIES) $(EXTRA_knot_test_changeset_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_changeset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_changeset_OBJECTS) $(knot_test_changeset_LDADD) $(LIBS)
+knot/test_conf.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_conf$(EXEEXT): $(knot_test_conf_OBJECTS) $(knot_test_conf_DEPENDENCIES) $(EXTRA_knot_test_conf_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_conf$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_conf_OBJECTS) $(knot_test_conf_LDADD) $(LIBS)
+knot/test_conf_tools.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_conf_tools$(EXEEXT): $(knot_test_conf_tools_OBJECTS) $(knot_test_conf_tools_DEPENDENCIES) $(EXTRA_knot_test_conf_tools_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_conf_tools$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_conf_tools_OBJECTS) $(knot_test_conf_tools_LDADD) $(LIBS)
+knot/test_confdb.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_confdb$(EXEEXT): $(knot_test_confdb_OBJECTS) $(knot_test_confdb_DEPENDENCIES) $(EXTRA_knot_test_confdb_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_confdb$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_confdb_OBJECTS) $(knot_test_confdb_LDADD) $(LIBS)
+knot/test_confio.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_confio$(EXEEXT): $(knot_test_confio_OBJECTS) $(knot_test_confio_DEPENDENCIES) $(EXTRA_knot_test_confio_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_confio$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_confio_OBJECTS) $(knot_test_confio_LDADD) $(LIBS)
+knot/test_digest.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_digest$(EXEEXT): $(knot_test_digest_OBJECTS) $(knot_test_digest_DEPENDENCIES) $(EXTRA_knot_test_digest_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_digest$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_digest_OBJECTS) $(knot_test_digest_LDADD) $(LIBS)
+knot/test_dthreads.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_dthreads$(EXEEXT): $(knot_test_dthreads_OBJECTS) $(knot_test_dthreads_DEPENDENCIES) $(EXTRA_knot_test_dthreads_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_dthreads$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_dthreads_OBJECTS) $(knot_test_dthreads_LDADD) $(LIBS)
+knot/test_fdset.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_fdset$(EXEEXT): $(knot_test_fdset_OBJECTS) $(knot_test_fdset_DEPENDENCIES) $(EXTRA_knot_test_fdset_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_fdset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_fdset_OBJECTS) $(knot_test_fdset_LDADD) $(LIBS)
+knot/test_journal.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_journal$(EXEEXT): $(knot_test_journal_OBJECTS) $(knot_test_journal_DEPENDENCIES) $(EXTRA_knot_test_journal_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_journal$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_journal_OBJECTS) $(knot_test_journal_LDADD) $(LIBS)
+knot/test_kasp_db.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_kasp_db$(EXEEXT): $(knot_test_kasp_db_OBJECTS) $(knot_test_kasp_db_DEPENDENCIES) $(EXTRA_knot_test_kasp_db_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_kasp_db$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_kasp_db_OBJECTS) $(knot_test_kasp_db_LDADD) $(LIBS)
+knot/test_node.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_node$(EXEEXT): $(knot_test_node_OBJECTS) $(knot_test_node_DEPENDENCIES) $(EXTRA_knot_test_node_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_node$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_node_OBJECTS) $(knot_test_node_LDADD) $(LIBS)
+knot/test_process_query.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_process_query$(EXEEXT): $(knot_test_process_query_OBJECTS) $(knot_test_process_query_DEPENDENCIES) $(EXTRA_knot_test_process_query_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_process_query$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_process_query_OBJECTS) $(knot_test_process_query_LDADD) $(LIBS)
+knot/test_query_module.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_query_module$(EXEEXT): $(knot_test_query_module_OBJECTS) $(knot_test_query_module_DEPENDENCIES) $(EXTRA_knot_test_query_module_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_query_module$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_query_module_OBJECTS) $(knot_test_query_module_LDADD) $(LIBS)
+knot/test_requestor.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_requestor$(EXEEXT): $(knot_test_requestor_OBJECTS) $(knot_test_requestor_DEPENDENCIES) $(EXTRA_knot_test_requestor_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_requestor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_requestor_OBJECTS) $(knot_test_requestor_LDADD) $(LIBS)
+knot/test_server.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_server$(EXEEXT): $(knot_test_server_OBJECTS) $(knot_test_server_DEPENDENCIES) $(EXTRA_knot_test_server_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_server$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_server_OBJECTS) $(knot_test_server_LDADD) $(LIBS)
+knot/test_unreachable.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_unreachable$(EXEEXT): $(knot_test_unreachable_OBJECTS) $(knot_test_unreachable_DEPENDENCIES) $(EXTRA_knot_test_unreachable_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_unreachable$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_unreachable_OBJECTS) $(knot_test_unreachable_LDADD) $(LIBS)
+knot/test_worker_pool.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_worker_pool$(EXEEXT): $(knot_test_worker_pool_OBJECTS) $(knot_test_worker_pool_DEPENDENCIES) $(EXTRA_knot_test_worker_pool_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_worker_pool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_worker_pool_OBJECTS) $(knot_test_worker_pool_LDADD) $(LIBS)
+knot/test_worker_queue.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_worker_queue$(EXEEXT): $(knot_test_worker_queue_OBJECTS) $(knot_test_worker_queue_DEPENDENCIES) $(EXTRA_knot_test_worker_queue_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_worker_queue$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_worker_queue_OBJECTS) $(knot_test_worker_queue_LDADD) $(LIBS)
+knot/test_zone-tree.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone-tree$(EXEEXT): $(knot_test_zone_tree_OBJECTS) $(knot_test_zone_tree_DEPENDENCIES) $(EXTRA_knot_test_zone_tree_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone-tree$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_tree_OBJECTS) $(knot_test_zone_tree_LDADD) $(LIBS)
+knot/test_zone-update.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone-update$(EXEEXT): $(knot_test_zone_update_OBJECTS) $(knot_test_zone_update_DEPENDENCIES) $(EXTRA_knot_test_zone_update_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone-update$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_update_OBJECTS) $(knot_test_zone_update_LDADD) $(LIBS)
+knot/test_zone_events.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_events$(EXEEXT): $(knot_test_zone_events_OBJECTS) $(knot_test_zone_events_DEPENDENCIES) $(EXTRA_knot_test_zone_events_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_events$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_events_OBJECTS) $(knot_test_zone_events_LDADD) $(LIBS)
+knot/test_zone_serial.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_serial$(EXEEXT): $(knot_test_zone_serial_OBJECTS) $(knot_test_zone_serial_DEPENDENCIES) $(EXTRA_knot_test_zone_serial_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_serial$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_serial_OBJECTS) $(knot_test_zone_serial_LDADD) $(LIBS)
+knot/test_zone_timers.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zone_timers$(EXEEXT): $(knot_test_zone_timers_OBJECTS) $(knot_test_zone_timers_DEPENDENCIES) $(EXTRA_knot_test_zone_timers_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zone_timers$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zone_timers_OBJECTS) $(knot_test_zone_timers_LDADD) $(LIBS)
+knot/test_zonedb.$(OBJEXT): knot/$(am__dirstamp) \
+ knot/$(DEPDIR)/$(am__dirstamp)
+
+knot/test_zonedb$(EXEEXT): $(knot_test_zonedb_OBJECTS) $(knot_test_zonedb_DEPENDENCIES) $(EXTRA_knot_test_zonedb_DEPENDENCIES) knot/$(am__dirstamp)
+ @rm -f knot/test_zonedb$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(knot_test_zonedb_OBJECTS) $(knot_test_zonedb_LDADD) $(LIBS)
+libdnssec/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec
+ @: > libdnssec/$(am__dirstamp)
+libdnssec/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libdnssec/$(DEPDIR)
+ @: > libdnssec/$(DEPDIR)/$(am__dirstamp)
+libdnssec/test_binary.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_binary$(EXEEXT): $(libdnssec_test_binary_OBJECTS) $(libdnssec_test_binary_DEPENDENCIES) $(EXTRA_libdnssec_test_binary_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_binary$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_binary_OBJECTS) $(libdnssec_test_binary_LDADD) $(LIBS)
+libdnssec/test_crypto.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_crypto$(EXEEXT): $(libdnssec_test_crypto_OBJECTS) $(libdnssec_test_crypto_DEPENDENCIES) $(EXTRA_libdnssec_test_crypto_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_crypto$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_crypto_OBJECTS) $(libdnssec_test_crypto_LDADD) $(LIBS)
+libdnssec/test_key.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key$(EXEEXT): $(libdnssec_test_key_OBJECTS) $(libdnssec_test_key_DEPENDENCIES) $(EXTRA_libdnssec_test_key_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_OBJECTS) $(libdnssec_test_key_LDADD) $(LIBS)
+libdnssec/test_key_algorithm.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key_algorithm$(EXEEXT): $(libdnssec_test_key_algorithm_OBJECTS) $(libdnssec_test_key_algorithm_DEPENDENCIES) $(EXTRA_libdnssec_test_key_algorithm_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key_algorithm$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_algorithm_OBJECTS) $(libdnssec_test_key_algorithm_LDADD) $(LIBS)
+libdnssec/test_key_ds.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_key_ds$(EXEEXT): $(libdnssec_test_key_ds_OBJECTS) $(libdnssec_test_key_ds_DEPENDENCIES) $(EXTRA_libdnssec_test_key_ds_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_key_ds$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_key_ds_OBJECTS) $(libdnssec_test_key_ds_LDADD) $(LIBS)
+libdnssec/test_keyid.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keyid$(EXEEXT): $(libdnssec_test_keyid_OBJECTS) $(libdnssec_test_keyid_DEPENDENCIES) $(EXTRA_libdnssec_test_keyid_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keyid$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keyid_OBJECTS) $(libdnssec_test_keyid_LDADD) $(LIBS)
+libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.$(OBJEXT): \
+ libdnssec/$(am__dirstamp) libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keystore_pkcs11$(EXEEXT): $(libdnssec_test_keystore_pkcs11_OBJECTS) $(libdnssec_test_keystore_pkcs11_DEPENDENCIES) $(EXTRA_libdnssec_test_keystore_pkcs11_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keystore_pkcs11$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keystore_pkcs11_OBJECTS) $(libdnssec_test_keystore_pkcs11_LDADD) $(LIBS)
+libdnssec/test_keystore_pkcs8.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keystore_pkcs8$(EXEEXT): $(libdnssec_test_keystore_pkcs8_OBJECTS) $(libdnssec_test_keystore_pkcs8_DEPENDENCIES) $(EXTRA_libdnssec_test_keystore_pkcs8_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keystore_pkcs8$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keystore_pkcs8_OBJECTS) $(libdnssec_test_keystore_pkcs8_LDADD) $(LIBS)
+libdnssec/test_keytag.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_keytag$(EXEEXT): $(libdnssec_test_keytag_OBJECTS) $(libdnssec_test_keytag_DEPENDENCIES) $(EXTRA_libdnssec_test_keytag_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_keytag$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_keytag_OBJECTS) $(libdnssec_test_keytag_LDADD) $(LIBS)
+libdnssec/test_nsec_bitmap.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_nsec_bitmap$(EXEEXT): $(libdnssec_test_nsec_bitmap_OBJECTS) $(libdnssec_test_nsec_bitmap_DEPENDENCIES) $(EXTRA_libdnssec_test_nsec_bitmap_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_nsec_bitmap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_nsec_bitmap_OBJECTS) $(libdnssec_test_nsec_bitmap_LDADD) $(LIBS)
+libdnssec/test_nsec_hash.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_nsec_hash$(EXEEXT): $(libdnssec_test_nsec_hash_OBJECTS) $(libdnssec_test_nsec_hash_DEPENDENCIES) $(EXTRA_libdnssec_test_nsec_hash_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_nsec_hash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_nsec_hash_OBJECTS) $(libdnssec_test_nsec_hash_LDADD) $(LIBS)
+libdnssec/test_random.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_random$(EXEEXT): $(libdnssec_test_random_OBJECTS) $(libdnssec_test_random_DEPENDENCIES) $(EXTRA_libdnssec_test_random_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_random$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_random_OBJECTS) $(libdnssec_test_random_LDADD) $(LIBS)
+libdnssec/test_shared_bignum.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_shared_bignum$(EXEEXT): $(libdnssec_test_shared_bignum_OBJECTS) $(libdnssec_test_shared_bignum_DEPENDENCIES) $(EXTRA_libdnssec_test_shared_bignum_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_shared_bignum$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_shared_bignum_OBJECTS) $(libdnssec_test_shared_bignum_LDADD) $(LIBS)
+libdnssec/test_shared_dname.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_shared_dname$(EXEEXT): $(libdnssec_test_shared_dname_OBJECTS) $(libdnssec_test_shared_dname_DEPENDENCIES) $(EXTRA_libdnssec_test_shared_dname_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_shared_dname$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_shared_dname_OBJECTS) $(libdnssec_test_shared_dname_LDADD) $(LIBS)
+libdnssec/test_sign.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_sign$(EXEEXT): $(libdnssec_test_sign_OBJECTS) $(libdnssec_test_sign_DEPENDENCIES) $(EXTRA_libdnssec_test_sign_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_sign$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_sign_OBJECTS) $(libdnssec_test_sign_LDADD) $(LIBS)
+libdnssec/test_sign_der.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_sign_der$(EXEEXT): $(libdnssec_test_sign_der_OBJECTS) $(libdnssec_test_sign_der_DEPENDENCIES) $(EXTRA_libdnssec_test_sign_der_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_sign_der$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_sign_der_OBJECTS) $(libdnssec_test_sign_der_LDADD) $(LIBS)
+libdnssec/test_tsig.$(OBJEXT): libdnssec/$(am__dirstamp) \
+ libdnssec/$(DEPDIR)/$(am__dirstamp)
+
+libdnssec/test_tsig$(EXEEXT): $(libdnssec_test_tsig_OBJECTS) $(libdnssec_test_tsig_DEPENDENCIES) $(EXTRA_libdnssec_test_tsig_DEPENDENCIES) libdnssec/$(am__dirstamp)
+ @rm -f libdnssec/test_tsig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libdnssec_test_tsig_OBJECTS) $(libdnssec_test_tsig_LDADD) $(LIBS)
+libknot/$(am__dirstamp):
+ @$(MKDIR_P) libknot
+ @: > libknot/$(am__dirstamp)
+libknot/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libknot/$(DEPDIR)
+ @: > libknot/$(DEPDIR)/$(am__dirstamp)
+libknot/test_control.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_control$(EXEEXT): $(libknot_test_control_OBJECTS) $(libknot_test_control_DEPENDENCIES) $(EXTRA_libknot_test_control_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_control$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_control_OBJECTS) $(libknot_test_control_LDADD) $(LIBS)
+libknot/test_cookies.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_cookies$(EXEEXT): $(libknot_test_cookies_OBJECTS) $(libknot_test_cookies_DEPENDENCIES) $(EXTRA_libknot_test_cookies_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_cookies$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_cookies_OBJECTS) $(libknot_test_cookies_LDADD) $(LIBS)
+libknot/test_db.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_db$(EXEEXT): $(libknot_test_db_OBJECTS) $(libknot_test_db_DEPENDENCIES) $(EXTRA_libknot_test_db_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_db$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_db_OBJECTS) $(libknot_test_db_LDADD) $(LIBS)
+libknot/test_descriptor.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_descriptor$(EXEEXT): $(libknot_test_descriptor_OBJECTS) $(libknot_test_descriptor_DEPENDENCIES) $(EXTRA_libknot_test_descriptor_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_descriptor$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_descriptor_OBJECTS) $(libknot_test_descriptor_LDADD) $(LIBS)
+libknot/test_dname.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_dname$(EXEEXT): $(libknot_test_dname_OBJECTS) $(libknot_test_dname_DEPENDENCIES) $(EXTRA_libknot_test_dname_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_dname$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_dname_OBJECTS) $(libknot_test_dname_LDADD) $(LIBS)
+libknot/test_dynarray.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_dynarray$(EXEEXT): $(libknot_test_dynarray_OBJECTS) $(libknot_test_dynarray_DEPENDENCIES) $(EXTRA_libknot_test_dynarray_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_dynarray$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_dynarray_OBJECTS) $(libknot_test_dynarray_LDADD) $(LIBS)
+libknot/test_edns.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_edns$(EXEEXT): $(libknot_test_edns_OBJECTS) $(libknot_test_edns_DEPENDENCIES) $(EXTRA_libknot_test_edns_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_edns$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_edns_OBJECTS) $(libknot_test_edns_LDADD) $(LIBS)
+libknot/test_edns_ecs.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_edns_ecs$(EXEEXT): $(libknot_test_edns_ecs_OBJECTS) $(libknot_test_edns_ecs_DEPENDENCIES) $(EXTRA_libknot_test_edns_ecs_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_edns_ecs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_edns_ecs_OBJECTS) $(libknot_test_edns_ecs_LDADD) $(LIBS)
+libknot/test_endian.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_endian$(EXEEXT): $(libknot_test_endian_OBJECTS) $(libknot_test_endian_DEPENDENCIES) $(EXTRA_libknot_test_endian_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_endian$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_endian_OBJECTS) $(libknot_test_endian_LDADD) $(LIBS)
+libknot/test_lookup.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_lookup$(EXEEXT): $(libknot_test_lookup_OBJECTS) $(libknot_test_lookup_DEPENDENCIES) $(EXTRA_libknot_test_lookup_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_lookup$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_lookup_OBJECTS) $(libknot_test_lookup_LDADD) $(LIBS)
+libknot/test_pkt.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_pkt$(EXEEXT): $(libknot_test_pkt_OBJECTS) $(libknot_test_pkt_DEPENDENCIES) $(EXTRA_libknot_test_pkt_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_pkt$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_pkt_OBJECTS) $(libknot_test_pkt_LDADD) $(LIBS)
+libknot/test_probe.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_probe$(EXEEXT): $(libknot_test_probe_OBJECTS) $(libknot_test_probe_DEPENDENCIES) $(EXTRA_libknot_test_probe_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_probe$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_probe_OBJECTS) $(libknot_test_probe_LDADD) $(LIBS)
+libknot/test_rdata.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rdata$(EXEEXT): $(libknot_test_rdata_OBJECTS) $(libknot_test_rdata_DEPENDENCIES) $(EXTRA_libknot_test_rdata_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rdata$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rdata_OBJECTS) $(libknot_test_rdata_LDADD) $(LIBS)
+libknot/test_rdataset.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rdataset$(EXEEXT): $(libknot_test_rdataset_OBJECTS) $(libknot_test_rdataset_DEPENDENCIES) $(EXTRA_libknot_test_rdataset_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rdataset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rdataset_OBJECTS) $(libknot_test_rdataset_LDADD) $(LIBS)
+libknot/test_rrset.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rrset$(EXEEXT): $(libknot_test_rrset_OBJECTS) $(libknot_test_rrset_DEPENDENCIES) $(EXTRA_libknot_test_rrset_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rrset$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rrset_OBJECTS) $(libknot_test_rrset_LDADD) $(LIBS)
+libknot/test_rrset-wire.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_rrset-wire$(EXEEXT): $(libknot_test_rrset_wire_OBJECTS) $(libknot_test_rrset_wire_DEPENDENCIES) $(EXTRA_libknot_test_rrset_wire_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_rrset-wire$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_rrset_wire_OBJECTS) $(libknot_test_rrset_wire_LDADD) $(LIBS)
+libknot/test_tsig.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_tsig$(EXEEXT): $(libknot_test_tsig_OBJECTS) $(libknot_test_tsig_DEPENDENCIES) $(EXTRA_libknot_test_tsig_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_tsig$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_tsig_OBJECTS) $(libknot_test_tsig_LDADD) $(LIBS)
+libknot/test_wire.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_wire$(EXEEXT): $(libknot_test_wire_OBJECTS) $(libknot_test_wire_DEPENDENCIES) $(EXTRA_libknot_test_wire_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_wire$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_wire_OBJECTS) $(libknot_test_wire_LDADD) $(LIBS)
+libknot/test_xdp_tcp.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_xdp_tcp$(EXEEXT): $(libknot_test_xdp_tcp_OBJECTS) $(libknot_test_xdp_tcp_DEPENDENCIES) $(EXTRA_libknot_test_xdp_tcp_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_xdp_tcp$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_xdp_tcp_OBJECTS) $(libknot_test_xdp_tcp_LDADD) $(LIBS)
+libknot/test_yparser.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_yparser$(EXEEXT): $(libknot_test_yparser_OBJECTS) $(libknot_test_yparser_DEPENDENCIES) $(EXTRA_libknot_test_yparser_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_yparser$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_yparser_OBJECTS) $(libknot_test_yparser_LDADD) $(LIBS)
+libknot/test_ypschema.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_ypschema$(EXEEXT): $(libknot_test_ypschema_OBJECTS) $(libknot_test_ypschema_DEPENDENCIES) $(EXTRA_libknot_test_ypschema_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_ypschema$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_ypschema_OBJECTS) $(libknot_test_ypschema_LDADD) $(LIBS)
+libknot/test_yptrafo.$(OBJEXT): libknot/$(am__dirstamp) \
+ libknot/$(DEPDIR)/$(am__dirstamp)
+
+libknot/test_yptrafo$(EXEEXT): $(libknot_test_yptrafo_OBJECTS) $(libknot_test_yptrafo_DEPENDENCIES) $(EXTRA_libknot_test_yptrafo_DEPENDENCIES) libknot/$(am__dirstamp)
+ @rm -f libknot/test_yptrafo$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libknot_test_yptrafo_OBJECTS) $(libknot_test_yptrafo_LDADD) $(LIBS)
+libzscanner/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner
+ @: > libzscanner/$(am__dirstamp)
+libzscanner/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libzscanner/$(DEPDIR)
+ @: > libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/zscanner-tool.$(OBJEXT): libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+libzscanner/processing.$(OBJEXT): libzscanner/$(am__dirstamp) \
+ libzscanner/$(DEPDIR)/$(am__dirstamp)
+
+libzscanner/zscanner-tool$(EXEEXT): $(libzscanner_zscanner_tool_OBJECTS) $(libzscanner_zscanner_tool_DEPENDENCIES) $(EXTRA_libzscanner_zscanner_tool_DEPENDENCIES) libzscanner/$(am__dirstamp)
+ @rm -f libzscanner/zscanner-tool$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(libzscanner_zscanner_tool_OBJECTS) $(libzscanner_zscanner_tool_LDADD) $(LIBS)
+modules/$(am__dirstamp):
+ @$(MKDIR_P) modules
+ @: > modules/$(am__dirstamp)
+modules/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) modules/$(DEPDIR)
+ @: > modules/$(DEPDIR)/$(am__dirstamp)
+modules/test_onlinesign.$(OBJEXT): modules/$(am__dirstamp) \
+ modules/$(DEPDIR)/$(am__dirstamp)
+
+modules/test_onlinesign$(EXEEXT): $(modules_test_onlinesign_OBJECTS) $(modules_test_onlinesign_DEPENDENCIES) $(EXTRA_modules_test_onlinesign_DEPENDENCIES) modules/$(am__dirstamp)
+ @rm -f modules/test_onlinesign$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(modules_test_onlinesign_OBJECTS) $(modules_test_onlinesign_LDADD) $(LIBS)
+modules/test_rrl.$(OBJEXT): modules/$(am__dirstamp) \
+ modules/$(DEPDIR)/$(am__dirstamp)
+
+modules/test_rrl$(EXEEXT): $(modules_test_rrl_OBJECTS) $(modules_test_rrl_DEPENDENCIES) $(EXTRA_modules_test_rrl_DEPENDENCIES) modules/$(am__dirstamp)
+ @rm -f modules/test_rrl$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(modules_test_rrl_OBJECTS) $(modules_test_rrl_LDADD) $(LIBS)
+tap/runtests.$(OBJEXT): tap/$(am__dirstamp) \
+ tap/$(DEPDIR)/$(am__dirstamp)
+
+tap/runtests$(EXEEXT): $(tap_runtests_OBJECTS) $(tap_runtests_DEPENDENCIES) $(EXTRA_tap_runtests_DEPENDENCIES) tap/$(am__dirstamp)
+ @rm -f tap/runtests$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(tap_runtests_OBJECTS) $(tap_runtests_LDADD) $(LIBS)
+utils/$(am__dirstamp):
+ @$(MKDIR_P) utils
+ @: > utils/$(am__dirstamp)
+utils/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) utils/$(DEPDIR)
+ @: > utils/$(DEPDIR)/$(am__dirstamp)
+utils/test_cert-test_cert.$(OBJEXT): utils/$(am__dirstamp) \
+ utils/$(DEPDIR)/$(am__dirstamp)
+
+utils/test_cert$(EXEEXT): $(utils_test_cert_OBJECTS) $(utils_test_cert_DEPENDENCIES) $(EXTRA_utils_test_cert_DEPENDENCIES) utils/$(am__dirstamp)
+ @rm -f utils/test_cert$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(utils_test_cert_OBJECTS) $(utils_test_cert_LDADD) $(LIBS)
+utils/utils_test_lookup-test_lookup.$(OBJEXT): utils/$(am__dirstamp) \
+ utils/$(DEPDIR)/$(am__dirstamp)
+
+utils/test_lookup$(EXEEXT): $(utils_test_lookup_OBJECTS) $(utils_test_lookup_DEPENDENCIES) $(EXTRA_utils_test_lookup_DEPENDENCIES) utils/$(am__dirstamp)
+ @rm -f utils/test_lookup$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(utils_test_lookup_OBJECTS) $(utils_test_lookup_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+ -rm -f contrib/*.$(OBJEXT)
+ -rm -f knot/*.$(OBJEXT)
+ -rm -f libdnssec/*.$(OBJEXT)
+ -rm -f libknot/*.$(OBJEXT)
+ -rm -f libzscanner/*.$(OBJEXT)
+ -rm -f modules/*.$(OBJEXT)
+ -rm -f tap/*.$(OBJEXT)
+ -rm -f tap/*.lo
+ -rm -f utils/*.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base32hex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base64.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_base64url.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_heap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_inet_ntop.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_net_shortwrite.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_qp-cow.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_qp-trie.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_siphash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_sockaddr.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_spinlock.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_string.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_strtonum.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_time.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_toeplitz.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/test_wire_ctx.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_acl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_changeset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_conf_tools.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confdb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_confio.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_digest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_dthreads.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_fdset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_journal.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_kasp_db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_node.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_process_query.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_query_module.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_requestor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_server.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_unreachable.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_pool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_worker_queue.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-tree.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone-update.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_events.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_serial.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zone_timers.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@knot/$(DEPDIR)/test_zonedb.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_binary.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_crypto.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_algorithm.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_key_ds.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keyid.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_keytag.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_bitmap.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_nsec_hash.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_random.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_bignum.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_shared_dname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_sign_der.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/test_tsig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_control.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_cookies.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_db.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_descriptor.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_dname.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_dynarray.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_edns_ecs.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_endian.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_lookup.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_pkt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_probe.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdata.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rdataset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset-wire.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_rrset.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_tsig.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_wire.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_xdp_tcp.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yparser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_ypschema.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/test_yptrafo.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/processing.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/zscanner-tool.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_onlinesign.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/test_rrl.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/basic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/files.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/float.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@tap/$(DEPDIR)/runtests.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/test_cert-test_cert.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@utils/$(DEPDIR)/utils_test_lookup-test_lookup.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 $@ $<
+
+libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.o: libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.o -MD -MP -MF libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.o `test -f 'libdnssec/test_keystore_pkcs11.c' || echo '$(srcdir)/'`libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.o `test -f 'libdnssec/test_keystore_pkcs11.c' || echo '$(srcdir)/'`libdnssec/test_keystore_pkcs11.c
+
+libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.obj: libdnssec/test_keystore_pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.obj -MD -MP -MF libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo -c -o libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.obj `if test -f 'libdnssec/test_keystore_pkcs11.c'; then $(CYGPATH_W) 'libdnssec/test_keystore_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/libdnssec/test_keystore_pkcs11.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Tpo libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/test_keystore_pkcs11.c' object='libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_test_keystore_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/test_keystore_pkcs11-test_keystore_pkcs11.obj `if test -f 'libdnssec/test_keystore_pkcs11.c'; then $(CYGPATH_W) 'libdnssec/test_keystore_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/libdnssec/test_keystore_pkcs11.c'; fi`
+
+utils/test_cert-test_cert.o: utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/test_cert-test_cert.o -MD -MP -MF utils/$(DEPDIR)/test_cert-test_cert.Tpo -c -o utils/test_cert-test_cert.o `test -f 'utils/test_cert.c' || echo '$(srcdir)/'`utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/test_cert-test_cert.Tpo utils/$(DEPDIR)/test_cert-test_cert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_cert.c' object='utils/test_cert-test_cert.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/test_cert-test_cert.o `test -f 'utils/test_cert.c' || echo '$(srcdir)/'`utils/test_cert.c
+
+utils/test_cert-test_cert.obj: utils/test_cert.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/test_cert-test_cert.obj -MD -MP -MF utils/$(DEPDIR)/test_cert-test_cert.Tpo -c -o utils/test_cert-test_cert.obj `if test -f 'utils/test_cert.c'; then $(CYGPATH_W) 'utils/test_cert.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_cert.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/test_cert-test_cert.Tpo utils/$(DEPDIR)/test_cert-test_cert.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_cert.c' object='utils/test_cert-test_cert.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_cert_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/test_cert-test_cert.obj `if test -f 'utils/test_cert.c'; then $(CYGPATH_W) 'utils/test_cert.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_cert.c'; fi`
+
+utils/utils_test_lookup-test_lookup.o: utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_lookup-test_lookup.o -MD -MP -MF utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo -c -o utils/utils_test_lookup-test_lookup.o `test -f 'utils/test_lookup.c' || echo '$(srcdir)/'`utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_lookup.c' object='utils/utils_test_lookup-test_lookup.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_lookup-test_lookup.o `test -f 'utils/test_lookup.c' || echo '$(srcdir)/'`utils/test_lookup.c
+
+utils/utils_test_lookup-test_lookup.obj: utils/test_lookup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/utils_test_lookup-test_lookup.obj -MD -MP -MF utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo -c -o utils/utils_test_lookup-test_lookup.obj `if test -f 'utils/test_lookup.c'; then $(CYGPATH_W) 'utils/test_lookup.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_lookup.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/$(DEPDIR)/utils_test_lookup-test_lookup.Tpo utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/test_lookup.c' object='utils/utils_test_lookup-test_lookup.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(utils_test_lookup_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/utils_test_lookup-test_lookup.obj `if test -f 'utils/test_lookup.c'; then $(CYGPATH_W) 'utils/test_lookup.c'; else $(CYGPATH_W) '$(srcdir)/utils/test_lookup.c'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+ -rm -rf contrib/.libs contrib/_libs
+ -rm -rf knot/.libs knot/_libs
+ -rm -rf libdnssec/.libs libdnssec/_libs
+ -rm -rf libknot/.libs libknot/_libs
+ -rm -rf libzscanner/.libs libzscanner/_libs
+ -rm -rf modules/.libs modules/_libs
+ -rm -rf tap/.libs tap/_libs
+ -rm -rf utils/.libs utils/_libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(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-am
+
+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-am
+
+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
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(check_LTLIBRARIES) \
+ $(check_SCRIPTS)
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+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:
+
+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 contrib/$(DEPDIR)/$(am__dirstamp)
+ -rm -f contrib/$(am__dirstamp)
+ -rm -f knot/$(DEPDIR)/$(am__dirstamp)
+ -rm -f knot/$(am__dirstamp)
+ -rm -f libdnssec/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libdnssec/$(am__dirstamp)
+ -rm -f libknot/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libknot/$(am__dirstamp)
+ -rm -f libzscanner/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libzscanner/$(am__dirstamp)
+ -rm -f modules/$(DEPDIR)/$(am__dirstamp)
+ -rm -f modules/$(am__dirstamp)
+ -rm -f tap/$(DEPDIR)/$(am__dirstamp)
+ -rm -f tap/$(am__dirstamp)
+ -rm -f utils/$(DEPDIR)/$(am__dirstamp)
+ -rm -f utils/$(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."
+clean: clean-am
+
+clean-am: clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f contrib/$(DEPDIR)/test_base32hex.Po
+ -rm -f contrib/$(DEPDIR)/test_base64.Po
+ -rm -f contrib/$(DEPDIR)/test_base64url.Po
+ -rm -f contrib/$(DEPDIR)/test_heap.Po
+ -rm -f contrib/$(DEPDIR)/test_inet_ntop.Po
+ -rm -f contrib/$(DEPDIR)/test_net.Po
+ -rm -f contrib/$(DEPDIR)/test_net_shortwrite.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-cow.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-trie.Po
+ -rm -f contrib/$(DEPDIR)/test_siphash.Po
+ -rm -f contrib/$(DEPDIR)/test_sockaddr.Po
+ -rm -f contrib/$(DEPDIR)/test_spinlock.Po
+ -rm -f contrib/$(DEPDIR)/test_string.Po
+ -rm -f contrib/$(DEPDIR)/test_strtonum.Po
+ -rm -f contrib/$(DEPDIR)/test_time.Po
+ -rm -f contrib/$(DEPDIR)/test_toeplitz.Po
+ -rm -f contrib/$(DEPDIR)/test_wire_ctx.Po
+ -rm -f knot/$(DEPDIR)/test_acl.Po
+ -rm -f knot/$(DEPDIR)/test_changeset.Po
+ -rm -f knot/$(DEPDIR)/test_conf.Po
+ -rm -f knot/$(DEPDIR)/test_conf_tools.Po
+ -rm -f knot/$(DEPDIR)/test_confdb.Po
+ -rm -f knot/$(DEPDIR)/test_confio.Po
+ -rm -f knot/$(DEPDIR)/test_digest.Po
+ -rm -f knot/$(DEPDIR)/test_dthreads.Po
+ -rm -f knot/$(DEPDIR)/test_fdset.Po
+ -rm -f knot/$(DEPDIR)/test_journal.Po
+ -rm -f knot/$(DEPDIR)/test_kasp_db.Po
+ -rm -f knot/$(DEPDIR)/test_node.Po
+ -rm -f knot/$(DEPDIR)/test_process_query.Po
+ -rm -f knot/$(DEPDIR)/test_query_module.Po
+ -rm -f knot/$(DEPDIR)/test_requestor.Po
+ -rm -f knot/$(DEPDIR)/test_server.Po
+ -rm -f knot/$(DEPDIR)/test_unreachable.Po
+ -rm -f knot/$(DEPDIR)/test_worker_pool.Po
+ -rm -f knot/$(DEPDIR)/test_worker_queue.Po
+ -rm -f knot/$(DEPDIR)/test_zone-tree.Po
+ -rm -f knot/$(DEPDIR)/test_zone-update.Po
+ -rm -f knot/$(DEPDIR)/test_zone_events.Po
+ -rm -f knot/$(DEPDIR)/test_zone_serial.Po
+ -rm -f knot/$(DEPDIR)/test_zone_timers.Po
+ -rm -f knot/$(DEPDIR)/test_zonedb.Po
+ -rm -f libdnssec/$(DEPDIR)/test_binary.Po
+ -rm -f libdnssec/$(DEPDIR)/test_crypto.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_algorithm.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_ds.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keyid.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keytag.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_bitmap.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_hash.Po
+ -rm -f libdnssec/$(DEPDIR)/test_random.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_bignum.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_dname.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign_der.Po
+ -rm -f libdnssec/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_control.Po
+ -rm -f libknot/$(DEPDIR)/test_cookies.Po
+ -rm -f libknot/$(DEPDIR)/test_db.Po
+ -rm -f libknot/$(DEPDIR)/test_descriptor.Po
+ -rm -f libknot/$(DEPDIR)/test_dname.Po
+ -rm -f libknot/$(DEPDIR)/test_dynarray.Po
+ -rm -f libknot/$(DEPDIR)/test_edns.Po
+ -rm -f libknot/$(DEPDIR)/test_edns_ecs.Po
+ -rm -f libknot/$(DEPDIR)/test_endian.Po
+ -rm -f libknot/$(DEPDIR)/test_lookup.Po
+ -rm -f libknot/$(DEPDIR)/test_pkt.Po
+ -rm -f libknot/$(DEPDIR)/test_probe.Po
+ -rm -f libknot/$(DEPDIR)/test_rdata.Po
+ -rm -f libknot/$(DEPDIR)/test_rdataset.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset-wire.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset.Po
+ -rm -f libknot/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_wire.Po
+ -rm -f libknot/$(DEPDIR)/test_xdp_tcp.Po
+ -rm -f libknot/$(DEPDIR)/test_yparser.Po
+ -rm -f libknot/$(DEPDIR)/test_ypschema.Po
+ -rm -f libknot/$(DEPDIR)/test_yptrafo.Po
+ -rm -f libzscanner/$(DEPDIR)/processing.Po
+ -rm -f libzscanner/$(DEPDIR)/zscanner-tool.Po
+ -rm -f modules/$(DEPDIR)/test_onlinesign.Po
+ -rm -f modules/$(DEPDIR)/test_rrl.Po
+ -rm -f tap/$(DEPDIR)/basic.Plo
+ -rm -f tap/$(DEPDIR)/files.Plo
+ -rm -f tap/$(DEPDIR)/float.Plo
+ -rm -f tap/$(DEPDIR)/runtests.Po
+ -rm -f utils/$(DEPDIR)/test_cert-test_cert.Po
+ -rm -f utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+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 contrib/$(DEPDIR)/test_base32hex.Po
+ -rm -f contrib/$(DEPDIR)/test_base64.Po
+ -rm -f contrib/$(DEPDIR)/test_base64url.Po
+ -rm -f contrib/$(DEPDIR)/test_heap.Po
+ -rm -f contrib/$(DEPDIR)/test_inet_ntop.Po
+ -rm -f contrib/$(DEPDIR)/test_net.Po
+ -rm -f contrib/$(DEPDIR)/test_net_shortwrite.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-cow.Po
+ -rm -f contrib/$(DEPDIR)/test_qp-trie.Po
+ -rm -f contrib/$(DEPDIR)/test_siphash.Po
+ -rm -f contrib/$(DEPDIR)/test_sockaddr.Po
+ -rm -f contrib/$(DEPDIR)/test_spinlock.Po
+ -rm -f contrib/$(DEPDIR)/test_string.Po
+ -rm -f contrib/$(DEPDIR)/test_strtonum.Po
+ -rm -f contrib/$(DEPDIR)/test_time.Po
+ -rm -f contrib/$(DEPDIR)/test_toeplitz.Po
+ -rm -f contrib/$(DEPDIR)/test_wire_ctx.Po
+ -rm -f knot/$(DEPDIR)/test_acl.Po
+ -rm -f knot/$(DEPDIR)/test_changeset.Po
+ -rm -f knot/$(DEPDIR)/test_conf.Po
+ -rm -f knot/$(DEPDIR)/test_conf_tools.Po
+ -rm -f knot/$(DEPDIR)/test_confdb.Po
+ -rm -f knot/$(DEPDIR)/test_confio.Po
+ -rm -f knot/$(DEPDIR)/test_digest.Po
+ -rm -f knot/$(DEPDIR)/test_dthreads.Po
+ -rm -f knot/$(DEPDIR)/test_fdset.Po
+ -rm -f knot/$(DEPDIR)/test_journal.Po
+ -rm -f knot/$(DEPDIR)/test_kasp_db.Po
+ -rm -f knot/$(DEPDIR)/test_node.Po
+ -rm -f knot/$(DEPDIR)/test_process_query.Po
+ -rm -f knot/$(DEPDIR)/test_query_module.Po
+ -rm -f knot/$(DEPDIR)/test_requestor.Po
+ -rm -f knot/$(DEPDIR)/test_server.Po
+ -rm -f knot/$(DEPDIR)/test_unreachable.Po
+ -rm -f knot/$(DEPDIR)/test_worker_pool.Po
+ -rm -f knot/$(DEPDIR)/test_worker_queue.Po
+ -rm -f knot/$(DEPDIR)/test_zone-tree.Po
+ -rm -f knot/$(DEPDIR)/test_zone-update.Po
+ -rm -f knot/$(DEPDIR)/test_zone_events.Po
+ -rm -f knot/$(DEPDIR)/test_zone_serial.Po
+ -rm -f knot/$(DEPDIR)/test_zone_timers.Po
+ -rm -f knot/$(DEPDIR)/test_zonedb.Po
+ -rm -f libdnssec/$(DEPDIR)/test_binary.Po
+ -rm -f libdnssec/$(DEPDIR)/test_crypto.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_algorithm.Po
+ -rm -f libdnssec/$(DEPDIR)/test_key_ds.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keyid.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs11-test_keystore_pkcs11.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keystore_pkcs8.Po
+ -rm -f libdnssec/$(DEPDIR)/test_keytag.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_bitmap.Po
+ -rm -f libdnssec/$(DEPDIR)/test_nsec_hash.Po
+ -rm -f libdnssec/$(DEPDIR)/test_random.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_bignum.Po
+ -rm -f libdnssec/$(DEPDIR)/test_shared_dname.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign.Po
+ -rm -f libdnssec/$(DEPDIR)/test_sign_der.Po
+ -rm -f libdnssec/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_control.Po
+ -rm -f libknot/$(DEPDIR)/test_cookies.Po
+ -rm -f libknot/$(DEPDIR)/test_db.Po
+ -rm -f libknot/$(DEPDIR)/test_descriptor.Po
+ -rm -f libknot/$(DEPDIR)/test_dname.Po
+ -rm -f libknot/$(DEPDIR)/test_dynarray.Po
+ -rm -f libknot/$(DEPDIR)/test_edns.Po
+ -rm -f libknot/$(DEPDIR)/test_edns_ecs.Po
+ -rm -f libknot/$(DEPDIR)/test_endian.Po
+ -rm -f libknot/$(DEPDIR)/test_lookup.Po
+ -rm -f libknot/$(DEPDIR)/test_pkt.Po
+ -rm -f libknot/$(DEPDIR)/test_probe.Po
+ -rm -f libknot/$(DEPDIR)/test_rdata.Po
+ -rm -f libknot/$(DEPDIR)/test_rdataset.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset-wire.Po
+ -rm -f libknot/$(DEPDIR)/test_rrset.Po
+ -rm -f libknot/$(DEPDIR)/test_tsig.Po
+ -rm -f libknot/$(DEPDIR)/test_wire.Po
+ -rm -f libknot/$(DEPDIR)/test_xdp_tcp.Po
+ -rm -f libknot/$(DEPDIR)/test_yparser.Po
+ -rm -f libknot/$(DEPDIR)/test_ypschema.Po
+ -rm -f libknot/$(DEPDIR)/test_yptrafo.Po
+ -rm -f libzscanner/$(DEPDIR)/processing.Po
+ -rm -f libzscanner/$(DEPDIR)/zscanner-tool.Po
+ -rm -f modules/$(DEPDIR)/test_onlinesign.Po
+ -rm -f modules/$(DEPDIR)/test_rrl.Po
+ -rm -f tap/$(DEPDIR)/basic.Plo
+ -rm -f tap/$(DEPDIR)/files.Plo
+ -rm -f tap/$(DEPDIR)/float.Plo
+ -rm -f tap/$(DEPDIR)/runtests.Po
+ -rm -f utils/$(DEPDIR)/test_cert-test_cert.Po
+ -rm -f utils/$(DEPDIR)/utils_test_lookup-test_lookup.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \
+ check-local clean clean-checkLTLIBRARIES clean-checkPROGRAMS \
+ clean-generic clean-libtool cscopelist-am ctags ctags-am \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am 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-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+@HAVE_LIBUTILS_TRUE@knot/test_semantic_check:
+@HAVE_LIBUTILS_TRUE@ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+@HAVE_LIBUTILS_TRUE@ @chmod +x $(top_builddir)/tests/$@
+
+libzscanner/test_zscanner: libzscanner/zscanner-tool
+ @$(edit) < $(top_srcdir)/tests/$@.in > $(top_builddir)/tests/$@
+ @chmod +x $(top_builddir)/tests/$@
+
+check-compile: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+check-local: $(check_LTLIBRARIES) $(EXTRA_PROGRAMS) $(check_PROGRAMS) $(check_SCRIPTS)
+ @$(top_builddir)/tests/tap/runtests -s $(srcdir) -b $(builddir) \
+ -L $(builddir)/runtests.log $(check_PROGRAMS) $(check_SCRIPTS); \
+ $(AM_V_RUNTESTS)
+
+# 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/tests/contrib/test_base32hex.c b/tests/contrib/test_base32hex.c
new file mode 100644
index 0000000..541667c
--- /dev/null
+++ b/tests/contrib/test_base32hex.c
@@ -0,0 +1,267 @@
+/* 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base32hex.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define BUF_LEN 256
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 8) * 5)
+
+int main(int argc, char *argv[])
+{
+ plan(67);
+
+ int32_t ret;
+ uint8_t in[BUF_LEN], ref[BUF_LEN], out[BUF_LEN], out2[BUF_LEN], *out3;
+ uint32_t in_len, ref_len;
+
+ // 0. test invalid input
+ ret = knot_base32hex_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode: NULL input buffer");
+ ret = knot_base32hex_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode: NULL output buffer");
+ ret = knot_base32hex_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: input buffer too large");
+ ret = knot_base32hex_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode: output buffer too small");
+
+ ret = knot_base32hex_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode_alloc: NULL input buffer");
+ ret = knot_base32hex_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_encode_alloc: input buffer too large");
+ ret = knot_base32hex_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_encode_alloc: NULL output buffer");
+
+ ret = knot_base32hex_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode: NULL input buffer");
+ ret = knot_base32hex_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode: NULL output buffer");
+ ret = knot_base32hex_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: input buffer too large");
+ ret = knot_base32hex_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode: output buffer too small");
+
+ ret = knot_base32hex_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode_alloc: NULL input buffer");
+ ret = knot_base32hex_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base32hex_decode_aloc: input buffer too large");
+ ret = knot_base32hex_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base32hex_decode_alloc: NULL output buffer");
+
+ // 1. test vector -> ENC -> DEC
+ strlcpy((char *)in, "", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "1. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "1. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "1. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "1. test vector - DEC output content");
+ }
+
+ // 2. test vector -> ENC -> DEC
+ strlcpy((char *)in, "f", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "co======", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "2. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "2. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "2. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "2. test vector - DEC output content");
+ }
+
+ // 3. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpng====", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "3. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "3. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "3. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "3. test vector - DEC output content");
+ }
+
+ // 4. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmu===", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "4. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "4. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "4. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "4. test vector - DEC output content");
+ }
+
+ // 5. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foob", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuog=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "5. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "5. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "5. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "5. test vector - DEC output content");
+ }
+
+ // 6. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fooba", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuoj1", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "6. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "6. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "6. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "6. test vector - DEC output content");
+ }
+
+ // 7. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foobar", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "cpnmuoj1e8======", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base32hex_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "7. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "7. test vector - ENC output content");
+ }
+ ret = knot_base32hex_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "7. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "7. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAA==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 2");
+ ret = knot_base32hex_decode((uint8_t *)"AAA=====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 5");
+ ret = knot_base32hex_decode((uint8_t *)"A=======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 7");
+ ret = knot_base32hex_decode((uint8_t *)"========", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding length 8");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA=A=", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 2");
+ ret = knot_base32hex_decode((uint8_t *)"AA=A====", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 5");
+ ret = knot_base32hex_decode((uint8_t *)"=A======", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad padding character on position 7");
+ ret = knot_base32hex_decode((uint8_t *)"CO======CO======", 16, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Two octects with padding");
+
+ // Bad data length
+ ret = knot_base32hex_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 1");
+ ret = knot_base32hex_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 2");
+ ret = knot_base32hex_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 3");
+ ret = knot_base32hex_decode((uint8_t *)"AAAA", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 4");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 5");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAA", 6, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 6");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA", 7, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 7");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAAAA", 9, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ESIZE, "Bad data length 9");
+
+ // Bad data character
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA$", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAAA ", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character space");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAAA$A", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 7");
+ ret = knot_base32hex_decode((uint8_t *)"AAAAA$AA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 6");
+ ret = knot_base32hex_decode((uint8_t *)"AAAA$AAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 5");
+ ret = knot_base32hex_decode((uint8_t *)"AAA$AAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 4");
+ ret = knot_base32hex_decode((uint8_t *)"AA$AAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 3");
+ ret = knot_base32hex_decode((uint8_t *)"A$AAAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 2");
+ ret = knot_base32hex_decode((uint8_t *)"$AAAAAAA", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE32HEX_ECHAR, "Bad data character dollar on position 1");
+
+ return 0;
+}
diff --git a/tests/contrib/test_base64.c b/tests/contrib/test_base64.c
new file mode 100644
index 0000000..82eee7d
--- /dev/null
+++ b/tests/contrib/test_base64.c
@@ -0,0 +1,237 @@
+/* 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base64.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define BUF_LEN 256
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3)
+
+int main(int argc, char *argv[])
+{
+ plan(52);
+
+ int32_t ret;
+ uint8_t in[BUF_LEN], ref[BUF_LEN], out[BUF_LEN], out2[BUF_LEN], *out3;
+ uint32_t in_len, ref_len;
+
+ // 0. test invalid input
+ ret = knot_base64_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode: NULL input buffer");
+ ret = knot_base64_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode: NULL output buffer");
+ ret = knot_base64_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode: input buffer too large");
+ ret = knot_base64_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode: output buffer too small");
+
+ ret = knot_base64_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode_alloc: NULL input buffer");
+ ret = knot_base64_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64_encode_alloc: input buffer too large");
+ ret = knot_base64_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64_encode_alloc: NULL output buffer");
+
+ ret = knot_base64_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode: NULL input buffer");
+ ret = knot_base64_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode: NULL output buffer");
+ ret = knot_base64_decode(in, UINT32_MAX, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode: input buffer too large");
+ ret = knot_base64_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode: output buffer too small");
+
+ ret = knot_base64_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode_alloc: NULL input buffer");
+ ret = knot_base64_decode_alloc(in, UINT32_MAX, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64_decode_aloc: input buffer too large");
+ ret = knot_base64_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64_decode_alloc: NULL output buffer");
+
+ // 1. test vector -> ENC -> DEC
+ strlcpy((char *)in, "", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "1. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "1. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "1. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "1. test vector - DEC output content");
+ }
+
+ // 2. test vector -> ENC -> DEC
+ strlcpy((char *)in, "f", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zg==", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "2. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "2. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "2. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "2. test vector - DEC output content");
+ }
+
+ // 3. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm8=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "3. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "3. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "3. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "3. test vector - DEC output content");
+ }
+
+ // 4. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9v", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "4. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "4. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "4. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "4. test vector - DEC output content");
+ }
+
+ // 5. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foob", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYg==", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "5. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "5. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "5. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "5. test vector - DEC output content");
+ }
+
+ // 6. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fooba", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmE=", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "6. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "6. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "6. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "6. test vector - DEC output content");
+ }
+
+ // 7. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foobar", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmFy", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "7. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "7. test vector - ENC output content");
+ }
+ ret = knot_base64_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "7. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "7. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = knot_base64_decode((uint8_t *)"A===", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 3");
+ ret = knot_base64_decode((uint8_t *)"====", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 4");
+ ret = knot_base64_decode((uint8_t *)"AA=A", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding character on position 2");
+ ret = knot_base64_decode((uint8_t *)"Zg==Zg==", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Two quartets with padding");
+
+ // Bad data length
+ ret = knot_base64_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 1");
+ ret = knot_base64_decode((uint8_t *)"AA", 2, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 2");
+ ret = knot_base64_decode((uint8_t *)"AAA", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 3");
+ ret = knot_base64_decode((uint8_t *)"AAAAA", 5, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ESIZE, "Bad data length 5");
+
+ // Bad data character
+ ret = knot_base64_decode((uint8_t *)"AAA$", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character dollar");
+ ret = knot_base64_decode((uint8_t *)"AAA ", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character space");
+
+ return 0;
+}
diff --git a/tests/contrib/test_base64url.c b/tests/contrib/test_base64url.c
new file mode 100644
index 0000000..710aa29
--- /dev/null
+++ b/tests/contrib/test_base64url.c
@@ -0,0 +1,252 @@
+/* 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "contrib/base64url.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define BUF_LEN 256
+#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3)
+
+int main(int argc, char *argv[])
+{
+ plan(50);
+
+ int32_t ret;
+ uint8_t in[BUF_LEN], ref[BUF_LEN], out[BUF_LEN], out2[BUF_LEN], *out3;
+ uint32_t in_len, ref_len;
+
+ // 0. test invalid input
+ ret = knot_base64url_encode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode: NULL input buffer");
+ ret = knot_base64url_encode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode: NULL output buffer");
+ ret = knot_base64url_encode(in, MAX_BIN_DATA_LEN + 1, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: input buffer too large");
+ ret = knot_base64url_encode(in, BUF_LEN, out, BUF_LEN);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode: output buffer too small");
+
+ ret = knot_base64url_encode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode_alloc: NULL input buffer");
+ ret = knot_base64url_encode_alloc(in, MAX_BIN_DATA_LEN + 1, &out3);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_encode_alloc: input buffer too large");
+ ret = knot_base64url_encode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_encode_alloc: NULL output buffer");
+
+ ret = knot_base64url_decode(NULL, 0, out, BUF_LEN);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode: NULL input buffer");
+ ret = knot_base64url_decode(in, BUF_LEN, NULL, 0);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode: NULL output buffer");
+ ret = knot_base64url_decode(in, BUF_LEN, out, 0);
+ is_int(KNOT_ERANGE, ret, "knot_base64ulr_decode: output buffer too small");
+
+ ret = knot_base64url_decode_alloc(NULL, 0, &out3);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode_alloc: NULL input buffer");
+ ret = knot_base64url_decode_alloc(in, BUF_LEN, NULL);
+ is_int(KNOT_EINVAL, ret, "knot_base64ulr_decode_alloc: NULL output buffer");
+
+ // 1. test vector -> ENC -> DEC
+ strlcpy((char *)in, "", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "1. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "1. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "1. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "1. test vector - DEC output content");
+ }
+
+ // 2. test vector -> ENC -> DEC
+ strlcpy((char *)in, "f", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zg", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "2. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "2. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "2. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "2. test vector - DEC output content");
+ }
+
+ // 3. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm8", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "3. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "3. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "3. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "3. test vector - DEC output content");
+ }
+
+ // 4. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foo", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9v", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "4. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "4. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "4. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "4. test vector - DEC output content");
+ }
+
+ // 5. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foob", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYg", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "5. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "5. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "5. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "5. test vector - DEC output content");
+ }
+
+ // 6. test vector -> ENC -> DEC
+ strlcpy((char *)in, "fooba", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmE", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "6. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "6. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "6. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "6. test vector - DEC output content");
+ }
+
+ // 7. test vector -> ENC -> DEC
+ strlcpy((char *)in, "foobar", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "Zm9vYmFy", BUF_LEN);
+ ref_len = strlen((char *)ref);
+ ret = knot_base64url_encode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "7. test vector - ENC output length");
+ if (ret < 0) {
+ skip("Encode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "7. test vector - ENC output content");
+ }
+ ret = knot_base64url_decode(out, ret, out2, BUF_LEN);
+ ok(ret == in_len, "7. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out2, in, ret) == 0, "7. test vector - DEC output content");
+ }
+
+ // 8. ENC (percent-encoded padding) -> DEC
+ strlcpy((char *)in, "Zm9vYmE%3D", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "fooba", BUF_LEN);
+ ref_len = strlen((char *)ref);
+
+ ret = knot_base64url_decode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "8. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "8. test vector - DEC output content");
+ }
+
+ strlcpy((char *)in, "Zm9vYmFyCg%3d%3d", BUF_LEN);
+ in_len = strlen((char *)in);
+ strlcpy((char *)ref, "foobar\n", BUF_LEN);
+ ref_len = strlen((char *)ref);
+
+ ret = knot_base64url_decode(in, in_len, out, BUF_LEN);
+ ok(ret == ref_len, "9. test vector - DEC output length");
+ if (ret < 0) {
+ skip("Decode err");
+ } else {
+ ok(memcmp(out, ref, ret) == 0, "9. test vector - DEC output content");
+ }
+
+ // Bad paddings
+ ret = knot_base64url_decode((uint8_t *)"A", 1, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 3");
+ ret = knot_base64url_decode((uint8_t *)"%3D", 3, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding length 4");
+
+ // Paddings not at the end
+ ret = knot_base64url_decode((uint8_t *)"AB%3DCDEFG", 10, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding 1");
+ ret = knot_base64url_decode((uint8_t *)"AB\0CDEFG", 8, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad padding 2");
+
+ // Bad data character
+ ret = knot_base64url_decode((uint8_t *)"AAA$", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character dollar");
+ ret = knot_base64url_decode((uint8_t *)"AAA ", 4, out, BUF_LEN);
+ ok(ret == KNOT_BASE64_ECHAR, "Bad data character space");
+
+ return 0;
+}
diff --git a/tests/contrib/test_heap.c b/tests/contrib/test_heap.c
new file mode 100644
index 0000000..7dc5975
--- /dev/null
+++ b/tests/contrib/test_heap.c
@@ -0,0 +1,166 @@
+/* Copyright (C) 2016 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 <tap/basic.h>
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "contrib/ucw/heap.h"
+
+static void seed_random(void)
+{
+ unsigned short int seed[3] = { 0 };
+
+ FILE *f = fopen("/dev/urandom", "r");
+ if (f) {
+ if (fread(seed, sizeof(seed), 1, f) != 1) {
+ diag("failed to seed random source");
+ }
+ fclose(f);
+ }
+
+ diag("seed %hu %hu %hu", seed[0], seed[1], seed[2]);
+ seed48(seed);
+}
+
+struct value {
+ heap_val_t _heap;
+ int data;
+};
+
+static int value_cmp(void *_a, void *_b)
+{
+ const struct value *a = _a;
+ const struct value *b = _b;
+ return (a->data - b->data);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ seed_random();
+
+ static const int VALUE_COUNT = 1000;
+ static const int VALUE_RANGE = 950;
+ static const int VALUE_REPLACE = 300;
+ static const int VALUE_DELETE = 100;
+
+ struct heap heap;
+ heap_init(&heap, value_cmp, 0);
+
+ ok(EMPTY_HEAP(&heap), "heap is empty");
+
+ // fill the heap with random values (with duplicates)
+
+ struct value *values = calloc(VALUE_COUNT, sizeof(struct value));
+ assert(values);
+ assert(VALUE_RANGE < VALUE_COUNT);
+
+ bool valid = true;
+ for (int i = 0; i < VALUE_COUNT; i++) {
+ values[i].data = lrand48() % VALUE_RANGE;
+ if (heap_insert(&heap, &values[i]._heap) == 0) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_insert");
+ ok(!EMPTY_HEAP(&heap), "heap is non-empty");
+
+ // exercise heap_insert
+
+ valid = true;
+ for (int i = 0; i < VALUE_COUNT; i++) {
+ int pos = heap_find(&heap, &values[i]._heap);
+ if (*HELEMENT(&heap, pos) != &values[i]._heap) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_find");
+
+ // exercise heap_replace
+
+ assert(VALUE_REPLACE <= VALUE_COUNT);
+ struct value *replaced = calloc(VALUE_REPLACE, sizeof(struct value));
+ assert(replaced);
+
+ valid = true;
+ for (int i = 0; i < VALUE_REPLACE; i++) {
+ replaced[i].data = lrand48() % VALUE_RANGE;
+ int pos = heap_find(&heap, &values[i]._heap);
+ if (pos < 1) {
+ valid = false;
+ continue;
+ }
+
+ heap_replace(&heap, pos, &replaced[i]._heap);
+ int newpos = heap_find(&heap, &replaced[i]._heap);
+ if (newpos < 1) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_replace");
+
+ // exercise heap_delete
+
+ assert(VALUE_REPLACE + VALUE_DELETE < VALUE_COUNT);
+
+ valid = true;
+ for (int i = 0; i < VALUE_DELETE; i++) {
+ heap_val_t *value = &values[i + VALUE_REPLACE]._heap;
+ int pos = heap_find(&heap, value);
+ if (pos < 1) {
+ valid = false;
+ continue;
+
+ }
+ heap_delete(&heap, pos);
+ pos = heap_find(&heap, value);
+ if (pos != 0) {
+ valid = false;
+ }
+ }
+ ok(valid, "heap_delete");
+
+ // exercise item retrieval
+
+ assert(VALUE_COUNT > VALUE_DELETE);
+
+ valid = true;
+ int current = -1;
+ for (int i = 0; i < VALUE_COUNT - VALUE_DELETE; i++) {
+ struct value *val = (struct value *)*HHEAD(&heap);
+ heap_delmin(&heap);
+ if (current <= val->data) {
+ current = val->data;
+ } else {
+ valid = false;
+ }
+ }
+
+ ok(valid, "heap ordering");
+ ok(EMPTY_HEAP(&heap), "heap_delmin");
+
+ free(replaced);
+ free(values);
+ heap_deinit(&heap);
+
+ return 0;
+}
diff --git a/tests/contrib/test_inet_ntop.c b/tests/contrib/test_inet_ntop.c
new file mode 100644
index 0000000..33e7b7e
--- /dev/null
+++ b/tests/contrib/test_inet_ntop.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2023 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 <tap/basic.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <string.h>
+
+#include "contrib/musl/inet_ntop.h"
+
+uint8_t bin[sizeof(struct in6_addr)];
+const socklen_t len = INET6_ADDRSTRLEN;
+char buf[INET6_ADDRSTRLEN];
+const char *txt;
+
+#define CHECK4(addr) \
+ ok(inet_pton(AF_INET, addr, bin) == 1, "inet_pton(%s)", addr); \
+ ok((txt = knot_inet_ntop(AF_INET, bin, buf, len)) != NULL, "knot_inet_ntop(%s)", addr); \
+ ok(strcmp(txt, addr) == 0, "match %s", addr);
+
+#define CHECK6(addr, ref) \
+ ok(inet_pton(AF_INET6, addr, bin) == 1, "inet_pton(%s)", addr); \
+ ok((txt = knot_inet_ntop(AF_INET6, bin, buf, len)) != NULL, "knot_inet_ntop(%s)", addr); \
+ ok(strcmp(txt, ref) == 0, "match %s %s", txt, ref);
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("IPv4 addresses");
+ CHECK4("0.0.0.0");
+ CHECK4("1.2.3.4");
+ CHECK4("11.12.13.14");
+ CHECK4("255.255.255.255");
+
+ diag("IPv6 addresses");
+ CHECK6("::0", "::");
+ CHECK6("::00", "::");
+ CHECK6("::000", "::");
+ CHECK6("::0000", "::");
+
+ CHECK6("::1", "::1");
+ CHECK6("::01", "::1");
+ CHECK6("::001", "::1");
+ CHECK6("::0001", "::1");
+
+ CHECK6("::10", "::10");
+ CHECK6("::100", "::100");
+ CHECK6("::1000", "::1000");
+
+ CHECK6("::1:0", "::1:0");
+ CHECK6("::1:0:0", "::1:0:0");
+ CHECK6("::1:0:0:0", "::1:0:0:0");
+ CHECK6("::1:0:0:0:0", "0:0:0:1::");
+ CHECK6("::1:0:0:0:0:0", "0:0:1::");
+ CHECK6("::1:0:0:0:0:0:0", "0:1::");
+ CHECK6("1:0:0:0:0:0:0:0", "1::");
+
+ // IPv4-Compatible IPv6 Addresses (not supported).
+ CHECK6("::0:1:1", "::1:1");
+ CHECK6("::0:1.2.3.4", "::102:304");
+
+ // IPv4-Mapped IPv6 Addresses.
+ CHECK6("::ffff:1:1", "::ffff:0.1.0.1");
+ CHECK6("::ffff:1.2.3.4", "::ffff:1.2.3.4");
+
+ CHECK6("1::1", "1::1");
+ CHECK6("1000::1", "1000::1");
+ CHECK6("1:20:300:4000:0005:006:07:8", "1:20:300:4000:5:6:7:8");
+
+ return 0;
+}
diff --git a/tests/contrib/test_net.c b/tests/contrib/test_net.c
new file mode 100644
index 0000000..c0061cd
--- /dev/null
+++ b/tests/contrib/test_net.c
@@ -0,0 +1,718 @@
+/* Copyright (C) 2023 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 <tap/basic.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libknot/errcode.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+const int TIMEOUT = 5000;
+const int TIMEOUT_SHORT = 500;
+
+/*!
+ * \brief Get loopback socket address with unset port.
+ */
+static struct sockaddr_storage addr_local(void)
+{
+ struct sockaddr_storage addr = { 0 };
+
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
+ addr4->sin_family = AF_INET;
+ addr4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ return addr;
+}
+
+/*!
+ * \brief Get address of a socket.
+ */
+static struct sockaddr_storage addr_from_socket(int sock)
+{
+ struct sockaddr_storage addr = { 0 };
+ socklen_t len = sizeof(addr);
+ int ret = getsockname(sock, (struct sockaddr *)&addr, &len);
+ is_int(0, ret, "check getsockname return");
+
+ return addr;
+}
+
+static const char *socktype_name(int type)
+{
+ switch (type) {
+ case SOCK_STREAM: return "TCP";
+ case SOCK_DGRAM: return "UDP";
+ default: return "unknown";
+ }
+}
+
+static bool socktype_is_stream(int type)
+{
+ return type == SOCK_STREAM;
+}
+
+/* -- mock server ---------------------------------------------------------- */
+
+#define LISTEN_BACKLOG 5
+
+struct server_ctx;
+typedef struct server_ctx server_ctx_t;
+
+typedef void (*server_cb)(int sock, void *data);
+
+/*!
+ * \brief Server context.
+ */
+struct server_ctx {
+ int sock;
+ int type;
+ bool terminate;
+ server_cb handler;
+ void *handler_data;
+
+ pthread_t thr;
+ pthread_mutex_t mx;
+};
+
+static int poll_read(int sock)
+{
+ struct pollfd pfd = { .fd = sock, .events = POLLIN };
+ return poll(&pfd, 1, TIMEOUT);
+}
+
+static void server_handle(server_ctx_t *ctx)
+{
+ int remote = ctx->sock;
+
+ assert(ctx->type == SOCK_STREAM || ctx->type == SOCK_DGRAM);
+
+ if (socktype_is_stream(ctx->type)) {
+ remote = accept(ctx->sock, 0, 0);
+ if (remote < 0) {
+ return;
+ }
+ }
+
+ pthread_mutex_lock(&ctx->mx);
+ server_cb handler = ctx->handler;
+ pthread_mutex_unlock(&ctx->mx);
+ handler(remote, ctx->handler_data);
+
+ if (socktype_is_stream(ctx->type)) {
+ close(remote);
+ }
+}
+
+/*!
+ * \brief Simple server.
+ *
+ * Terminated when a one-byte message is delivered.
+ */
+static void *server_main(void *_ctx)
+{
+ server_ctx_t *ctx = _ctx;
+
+ for (;;) {
+ pthread_mutex_lock(&ctx->mx);
+ bool terminate = ctx->terminate;
+ pthread_mutex_unlock(&ctx->mx);
+ if (terminate) {
+ break;
+ }
+
+ int r = poll_read(ctx->sock);
+ if (r == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ break;
+ }
+ } else if (r == 0) {
+ continue;
+ }
+
+ assert(r == 1);
+ server_handle(ctx);
+ }
+
+ return NULL;
+}
+
+static bool server_start(server_ctx_t *ctx, int sock, int type,
+ server_cb handler, void *handler_data)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->sock = sock;
+ ctx->type = type;
+ ctx->handler = handler;
+ ctx->handler_data = handler_data;
+
+ ctx->terminate = false;
+
+ pthread_mutex_init(&ctx->mx, NULL);
+ return (pthread_create(&ctx->thr, NULL, server_main, ctx) == 0);
+}
+
+static void server_stop(server_ctx_t *ctx)
+{
+ pthread_mutex_lock(&ctx->mx);
+ ctx->terminate = true;
+ pthread_mutex_unlock(&ctx->mx);
+
+ pthread_kill(ctx->thr, SIGUSR1);
+ pthread_join(ctx->thr, NULL);
+}
+
+/* -- tests ---------------------------------------------------------------- */
+
+static void handler_echo(int sock, void *_server)
+{
+ server_ctx_t *server = _server;
+ uint8_t buffer[16] = { 0 };
+
+ struct sockaddr_storage remote = { 0 };
+ struct sockaddr_storage *addr = NULL;
+ if (!socktype_is_stream(server->type)) {
+ addr = &remote;
+ }
+
+ int in = net_base_recv(sock, buffer, sizeof(buffer), addr, TIMEOUT);
+ if (in <= 0) {
+ return;
+ }
+
+ net_base_send(sock, buffer, in, addr, TIMEOUT);
+}
+
+static void test_connected_one(const struct sockaddr_storage *server_addr,
+ const struct sockaddr_storage *source_addr,
+ int type, const char *name, const char *addr_name)
+{
+ int r;
+
+ int client = net_connected_socket(type, server_addr, source_addr, false);
+ ok(client >= 0, "%s, %s: client, create connected socket", name, addr_name);
+
+ const uint8_t out[] = "test message";
+ const size_t out_len = sizeof(out);
+ if (socktype_is_stream(type)) {
+ r = net_stream_send(client, out, out_len, TIMEOUT);
+ } else {
+ r = net_dgram_send(client, out, out_len, NULL);
+ }
+ is_int(out_len, r, "%s, %s: client, send message", name, addr_name);
+
+ r = net_is_connected(client);
+ ok(r, "%s, %s: client, is connected", name, addr_name);
+
+ uint8_t in[128] = { 0 };
+ if (socktype_is_stream(type)) {
+ r = net_stream_recv(client, in, sizeof(in), TIMEOUT);
+ } else {
+ r = net_dgram_recv(client, in, sizeof(in), TIMEOUT);
+ }
+ is_int(out_len, r, "%s, %s: client, receive message length", name, addr_name);
+ ok(memcmp(out, in, out_len) == 0, "%s, %s: client, receive message", name, addr_name);
+
+ close(client);
+}
+
+static void test_connected(int type)
+{
+ const char *name = socktype_name(type);
+ const struct sockaddr_storage empty_addr = { 0 };
+ const struct sockaddr_storage local_addr = addr_local();
+
+ int r;
+
+ // setup server
+
+ int server = net_bound_socket(type, &local_addr, 0, 0);
+ ok(server >= 0, "%s: server, create bound socket", name);
+
+ if (socktype_is_stream(type)) {
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "%s: server, start listening", name);
+ }
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, type, handler_echo, &server_ctx);
+ ok(r, "%s: server, start", name);
+
+ const struct sockaddr_storage server_addr = addr_from_socket(server);
+
+ // connected socket, send and receive
+
+ test_connected_one(&server_addr, NULL, type, name, "NULL source");
+ test_connected_one(&server_addr, &empty_addr, type, name, "zero source");
+ test_connected_one(&server_addr, &local_addr, type, name, "valid source");
+
+ // cleanup
+
+ server_stop(&server_ctx);
+ close(server);
+}
+
+static void handler_noop(int sock, void *data)
+{
+}
+
+static void test_unconnected(void)
+{
+ int r = 0;
+ int sock = -1;
+ const struct sockaddr_storage local = addr_local();
+
+ uint8_t buffer[] = { 'k', 'n', 'o', 't' };
+ ssize_t buffer_len = sizeof(buffer);
+
+ // server
+
+ int server = net_bound_socket(SOCK_DGRAM, &local, 0, 0);
+ ok(server >= 0, "UDP, create server socket");
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, SOCK_DGRAM, handler_noop, NULL);
+ ok(r, "UDP, start server");
+
+ // UDP
+
+ sock = net_unbound_socket(SOCK_DGRAM, &local);
+ ok(sock >= 0, "UDP, create unbound socket");
+
+ ok(!net_is_connected(sock), "UDP, is not connected");
+
+ r = net_dgram_send(sock, buffer, buffer_len, NULL);
+ is_int(KNOT_ECONN, r, "UDP, send failure on unconnected socket");
+
+ r = net_dgram_recv(sock, buffer, buffer_len, TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "UDP, receive timeout on unconnected socket");
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ r = net_dgram_send(sock, buffer, buffer_len, &server_addr);
+ is_int(buffer_len, r, "UDP, send on defined address");
+
+ close(sock);
+
+ // TCP
+
+ sock = net_unbound_socket(SOCK_STREAM, &local);
+ ok(sock >= 0, "TCP, create unbound socket");
+
+ ok(!net_is_connected(sock), "TCP, is not connected");
+
+#ifdef __linux__
+ const int expected = KNOT_ECONN;
+ const char *expected_msg = "failure";
+ const int expected_timeout = TIMEOUT;
+#else
+ const int expected = KNOT_ETIMEOUT;
+ const char *expected_msg = "timeout";
+ const int expected_timeout = TIMEOUT_SHORT;
+#endif
+
+ r = net_stream_send(sock, buffer, buffer_len, expected_timeout);
+ is_int(expected, r, "TCP, send %s on unconnected socket", expected_msg);
+
+ r = net_stream_recv(sock, buffer, sizeof(buffer), expected_timeout);
+ is_int(expected, r, "TCP, receive %s on unconnected socket", expected_msg);
+
+ close(sock);
+
+ // server termination
+
+ server_stop(&server_ctx);
+ close(server);
+}
+
+static void test_refused(void)
+{
+ int r = -1;
+
+ struct sockaddr_storage addr = { 0 };
+ uint8_t buffer[1] = { 0 };
+ int server, client;
+
+ // listening, not accepting
+
+ addr = addr_local();
+ server = net_bound_socket(SOCK_STREAM, &addr, 0, 0);
+ ok(server >= 0, "server, create server");
+ addr = addr_from_socket(server);
+
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "server, start listening");
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "client, connect");
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ is_int(1, r, "client, successful write");
+
+ r = net_stream_recv(client, buffer, sizeof(buffer), TIMEOUT_SHORT);
+ is_int(KNOT_ETIMEOUT, r, "client, timeout on read");
+
+ close(client);
+
+ // listening, closed immediately
+
+ client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "client, connect");
+
+ r = close(server);
+ is_int(0, r, "server, close socket");
+ usleep(50000);
+
+ r = net_stream_send(client, (uint8_t *)"", 1, TIMEOUT);
+ is_int(KNOT_ECONN, r, "client, refused on write");
+
+ close(client);
+}
+
+struct dns_handler_ctx {
+ const uint8_t *expected;
+ int len;
+ bool raw;
+ bool success;
+};
+
+static bool _sync(int remote, int send)
+{
+ uint8_t buf[1] = { 0 };
+ int r;
+ if (send) {
+ r = net_stream_send(remote, buf, sizeof(buf), TIMEOUT);
+ } else {
+ r = net_stream_recv(remote, buf, sizeof(buf), TIMEOUT);
+
+ }
+ return r == sizeof(buf);
+}
+
+static bool sync_signal(int remote)
+{
+ return _sync(remote, true);
+}
+
+static bool sync_wait(int remote)
+{
+ return _sync(remote, false);
+}
+
+static void handler_dns(int sock, void *_ctx)
+{
+ struct dns_handler_ctx *ctx = _ctx;
+
+ uint8_t in[16] = { 0 };
+ int in_len = 0;
+
+ sync_signal(sock);
+
+ if (ctx->raw) {
+ in_len = net_stream_recv(sock, in, sizeof(in), TIMEOUT);
+ } else {
+ in_len = net_dns_tcp_recv(sock, in, sizeof(in), TIMEOUT);
+ }
+
+ ctx->success = in_len == ctx->len &&
+ (ctx->len < 0 || memcmp(in, ctx->expected, in_len) == 0);
+}
+
+static void dns_send_hello(int sock)
+{
+ net_dns_tcp_send(sock, (uint8_t *)"wimbgunts", 9, TIMEOUT, NULL);
+}
+
+static void dns_send_fragmented(int sock)
+{
+ struct fragment { const uint8_t *data; size_t len; };
+
+ const struct fragment fragments[] = {
+ { (uint8_t *)"\x00", 1 },
+ { (uint8_t *)"\x08""qu", 3 },
+ { (uint8_t *)"oopisk", 6 },
+ { NULL }
+ };
+
+ for (const struct fragment *f = fragments; f->len > 0; f++) {
+ net_stream_send(sock, f->data, f->len, TIMEOUT);
+ }
+}
+
+static void dns_send_incomplete(int sock)
+{
+ net_stream_send(sock, (uint8_t *)"\x00\x08""korm", 6, TIMEOUT);
+}
+
+static void dns_send_trailing(int sock)
+{
+ net_stream_send(sock, (uint8_t *)"\x00\x05""bloitxx", 9, TIMEOUT);
+}
+
+static void test_dns_tcp(void)
+{
+ struct testcase {
+ const char *name;
+ const uint8_t *expected;
+ size_t expected_len;
+ bool expected_raw;
+ void (*send_callback)(int sock);
+ };
+
+ const struct testcase testcases[] = {
+ { "single DNS", (uint8_t *)"wimbgunts", 9, false, dns_send_hello },
+ { "single RAW", (uint8_t *)"\x00\x09""wimbgunts", 11, true, dns_send_hello },
+ { "fragmented", (uint8_t *)"quoopisk", 8, false, dns_send_fragmented },
+ { "incomplete", NULL, KNOT_ECONN, false, dns_send_incomplete },
+ { "trailing garbage", (uint8_t *)"bloit", 5, false, dns_send_trailing },
+ { NULL }
+ };
+
+ for (const struct testcase *t = testcases; t->name != NULL; t++) {
+ struct dns_handler_ctx handler_ctx = {
+ .expected = t->expected,
+ .len = t->expected_len,
+ .raw = t->expected_raw,
+ .success = false
+ };
+
+ struct sockaddr_storage addr = addr_local();
+ int server = net_bound_socket(SOCK_STREAM, &addr, 0, 0);
+ ok(server >= 0, "%s, server, create socket", t->name);
+
+ int r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "%s, server, start listening", t->name);
+
+ server_ctx_t server_ctx = { 0 };
+ r = server_start(&server_ctx, server, SOCK_STREAM, handler_dns, &handler_ctx);
+ ok(r, "%s, server, start handler", t->name);
+
+ addr = addr_from_socket(server);
+ int client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "%s, client, create connected socket", t->name);
+
+ r = sync_wait(client);
+ ok(r, "%s, client, wait for stream read", t->name);
+ t->send_callback(client);
+
+ close(client);
+ server_stop(&server_ctx);
+ close(server);
+
+ ok(handler_ctx.success, "%s, expected result", t->name);
+ }
+}
+
+static bool socket_is_blocking(int sock)
+{
+ return fcntl(sock, F_GETFL, O_NONBLOCK) == 0;
+}
+
+static void test_nonblocking_mode(int type)
+{
+ const char *name = socktype_name(type);
+ const struct sockaddr_storage addr = addr_local();
+
+ int client = net_unbound_socket(type, &addr);
+ ok(client >= 0, "%s: unbound, create", name);
+ ok(!socket_is_blocking(client), "%s: unbound, nonblocking mode", name);
+ close(client);
+
+ int server = net_bound_socket(type, &addr, 0, 0);
+ ok(server >= 0, "%s: bound, create", name);
+ ok(!socket_is_blocking(server), "%s: bound, nonblocking mode", name);
+
+ if (socktype_is_stream(type)) {
+ int r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "%s: bound, start listening", name);
+ }
+
+ struct sockaddr_storage server_addr = addr_from_socket(server);
+ client = net_connected_socket(type, &server_addr, NULL, false);
+ ok(client >= 0, "%s: connected, create", name);
+ ok(!socket_is_blocking(client), "%s: connected, nonblocking mode", name);
+
+ close(client);
+ close(server);
+}
+
+static void test_nonblocking_accept(void)
+{
+ int r;
+
+ // create server
+
+ struct sockaddr_storage addr_server = addr_local();
+
+ int server = net_bound_socket(SOCK_STREAM, &addr_server, 0, 0);
+ ok(server >= 0, "server, create socket");
+
+ r = listen(server, LISTEN_BACKLOG);
+ is_int(0, r, "server, start listening");
+
+ addr_server = addr_from_socket(server);
+
+ // create client
+
+ int client = net_connected_socket(SOCK_STREAM, &addr_server, NULL, false);
+ ok(client >= 0, "client, create connected socket");
+
+ struct sockaddr_storage addr_client = addr_from_socket(client);
+
+ // accept connection
+
+ r = poll_read(server);
+ is_int(1, r, "server, pending connection");
+
+ struct sockaddr_storage addr_accepted = { 0 };
+ int accepted = net_accept(server, &addr_accepted);
+ ok(accepted >= 0, "server, accept connection");
+
+ ok(!socket_is_blocking(accepted), "accepted, nonblocking mode");
+
+ ok(sockaddr_cmp(&addr_client, &addr_accepted, false) == 0,
+ "accepted, correct address");
+
+ close(client);
+
+ // client reconnect
+
+ close(client);
+ client = net_connected_socket(SOCK_STREAM, &addr_server, NULL, false);
+ ok(client >= 0, "client, reconnect");
+
+ r = poll_read(server);
+ is_int(1, r, "server, pending connection");
+
+ accepted = net_accept(server, NULL);
+ ok(accepted >= 0, "server, accept connection (no remote address)");
+
+ ok(!socket_is_blocking(accepted), "accepted, nonblocking mode");
+
+ // cleanup
+
+ close(client);
+ close(server);
+}
+
+static void test_socket_types(void)
+{
+ struct sockaddr_storage addr = addr_local();
+
+ struct testcase {
+ const char *name;
+ int type;
+ bool is_stream;
+ };
+
+ const struct testcase testcases[] = {
+ { "UDP", SOCK_DGRAM, false },
+ { "TCP", SOCK_STREAM, true },
+ { NULL }
+ };
+
+ for (const struct testcase *t = testcases; t->name != NULL; t++) {
+ int sock = net_unbound_socket(t->type, &addr);
+ ok(sock >= 0, "%s, create socket", t->name);
+
+ is_int(t->type, net_socktype(sock), "%s, socket type", t->name);
+
+ ok(net_is_stream(sock) == t->is_stream, "%s, is stream", t->name);
+
+ close(sock);
+ }
+
+ is_int(AF_UNSPEC, net_socktype(-1), "invalid, socket type");
+ ok(!net_is_stream(-1), "invalid, is stream");
+}
+
+static void test_bind_multiple(void)
+{
+ const struct sockaddr_storage addr = addr_local();
+
+ // bind first socket
+
+ int sock_one = net_bound_socket(SOCK_DGRAM, &addr, NET_BIND_MULTIPLE, 0);
+ if (sock_one == KNOT_ENOTSUP) {
+ skip("not supported on this system");
+ return;
+ }
+ ok(sock_one >= 0, "bind first socket");
+
+ // bind second socket to the same address
+
+ const struct sockaddr_storage addr_one = addr_from_socket(sock_one);
+ int sock_two = net_bound_socket(SOCK_DGRAM, &addr_one, NET_BIND_MULTIPLE, 0);
+ ok(sock_two >= 0, "bind second socket");
+
+ // compare sockets
+
+ ok(sock_one != sock_two, "descriptors are different");
+
+ const struct sockaddr_storage addr_two = addr_from_socket(sock_two);
+ ok(sockaddr_cmp(&addr_one, &addr_two, false) == 0,
+ "addresses are the same");
+
+ close(sock_one);
+ close(sock_two);
+}
+
+static void signal_noop(int sig)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ signal(SIGUSR1, signal_noop);
+
+ diag("nonblocking mode");
+ test_nonblocking_mode(SOCK_DGRAM);
+ test_nonblocking_mode(SOCK_STREAM);
+ test_nonblocking_accept();
+
+ diag("socket types");
+ test_socket_types();
+
+ diag("connected sockets");
+ test_connected(SOCK_DGRAM);
+ test_connected(SOCK_STREAM);
+
+ diag("unconnected sockets");
+ test_unconnected();
+
+ diag("refused connections");
+ test_refused();
+
+ diag("DNS messages over TCP");
+ test_dns_tcp();
+
+ diag("flag NET_BIND_MULTIPLE");
+ test_bind_multiple();
+
+ return 0;
+}
diff --git a/tests/contrib/test_net_shortwrite.c b/tests/contrib/test_net_shortwrite.c
new file mode 100644
index 0000000..f3d4c6e
--- /dev/null
+++ b/tests/contrib/test_net_shortwrite.c
@@ -0,0 +1,151 @@
+/* Copyright (C) 2023 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 <tap/basic.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+#include <poll.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "libknot/errcode.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+
+const int TIMEOUT = 2000;
+
+static struct sockaddr_storage localhost(void)
+{
+ struct sockaddr_storage addr = { 0 };
+
+ struct addrinfo *res = NULL;
+ if (getaddrinfo(NULL, "0", NULL, &res) == 0) {
+ memcpy(&addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ return addr;
+}
+
+struct data {
+ int server_fd;
+ uint8_t *buffer;
+ size_t size;
+ int result;
+};
+
+static void *thr_receive(void *data)
+{
+ struct data *d = data;
+
+ struct pollfd pfd = { .fd = d->server_fd, .events = POLLIN };
+ int r = poll(&pfd, 1, TIMEOUT);
+ if (r != 1) {
+ d->result = KNOT_ETIMEOUT;
+ return NULL;
+ }
+
+ int client = accept(d->server_fd, NULL, NULL);
+ if (client < 0) {
+ d->result = KNOT_ECONN;
+ return NULL;
+ }
+
+ d->result = net_dns_tcp_recv(client, d->buffer, d->size, TIMEOUT);
+
+ close(client);
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ int r;
+
+ // create TCP server
+
+ struct sockaddr_storage addr = localhost();
+ int server = net_bound_socket(SOCK_STREAM, &addr, 0, 0);
+ ok(server >= 0, "server: bind socket");
+
+ r = listen(server, 1);
+ ok(r == 0, "server: start listening");
+
+ struct sockaddr *sa = (struct sockaddr *)&addr;
+ socklen_t salen = sockaddr_len(&addr);
+ r = getsockname(server, sa, &salen);
+ ok(r == 0, "server: get bound address");
+
+ // create TCP client
+
+ int client = net_connected_socket(SOCK_STREAM, &addr, NULL, false);
+ ok(client >= 0, "client: connect to server");
+
+ int optval = 8192;
+ socklen_t optlen = sizeof(optval);
+ r = setsockopt(client, SOL_SOCKET, SO_SNDBUF, &optval, optlen);
+ ok(r == 0, "client: configure small send buffer");
+
+ // accept TCP connection on the background
+
+ uint8_t recvbuf[UINT16_MAX] = { 0 };
+ struct data recv_data = {
+ .server_fd = server,
+ .buffer = recvbuf,
+ .size = sizeof(recvbuf)
+ };
+
+ pthread_t thr;
+ r = pthread_create(&thr, NULL, thr_receive, &recv_data);
+ ok(r == 0, "server: start receiver thread");
+
+ // send message (should handle partial-write correctly)
+
+ uint8_t sndbuf[UINT16_MAX];
+ for (size_t i = 0; i < sizeof(sndbuf); i++) {
+ sndbuf[i] = i;
+ }
+ r = net_dns_tcp_send(client, sndbuf, sizeof(sndbuf), TIMEOUT, NULL);
+ ok(r == sizeof(sndbuf), "client: net_dns_tcp_send() with short-write");
+
+ // receive message
+
+ r = pthread_join(thr, NULL);
+ ok(r == 0, "server: wait for receiver thread to terminate");
+
+ ok(recv_data.result == sizeof(recvbuf) &&
+ memcmp(sndbuf, recvbuf, sizeof(recvbuf)) == 0,
+ "server: net_dns_tcp_recv() complete and valid data");
+
+ // clean up
+
+ if (server >= 0) {
+ close(server);
+ }
+
+ if (client >= 0) {
+ close(client);
+ }
+
+ return 0;
+}
diff --git a/tests/contrib/test_qp-cow.c b/tests/contrib/test_qp-cow.c
new file mode 100644
index 0000000..4cd8c6c
--- /dev/null
+++ b/tests/contrib/test_qp-cow.c
@@ -0,0 +1,282 @@
+/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+ Copyright (C) 2018 Tony Finch <dot@dotat.at>
+
+ 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <err.h>
+#include <unistd.h>
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/string.h"
+#include "libknot/attribute.h"
+#include "libknot/errcode.h"
+#include "tap/basic.h"
+
+/* Constants. */
+#define MAX_KEYLEN 64
+#define MAX_LEAVES 12345
+#define MAX_MUTATIONS 123
+#define MAX_TRANSACTIONS 1234
+
+enum cowstate {
+ cow_absent, // not in trie
+ cow_unmarked,
+ cow_shared,
+ cow_old, // deleted from new trie
+ cow_new, // added to new trie
+ deadbeef,
+};
+
+struct cowleaf {
+ char *key;
+ size_t len;
+ int cowstate;
+};
+
+static inline size_t
+prng(size_t max) {
+ /* good enough these days */
+ return (size_t)rand() % max;
+}
+
+static struct cowleaf *
+grow_leaves(size_t maxlen, size_t leaves)
+{
+ struct cowleaf *leaf = bcalloc(leaves, sizeof(*leaf));
+
+ trie_t *trie = trie_create(NULL);
+ if (!trie) sysbail("trie_create");
+
+ for (size_t i = 0; i < leaves; i++) {
+ trie_val_t *valp;
+ char *str = NULL;
+ size_t len = 0;
+ do {
+ free(str);
+ len = prng(maxlen);
+ str = bmalloc(len + 1);
+ for (size_t j = 0; j < len; j++)
+ str[j] = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ [prng(62)];
+ str[len] = '\0';
+ valp = trie_get_ins(trie, (uint8_t *)str, (uint32_t)len);
+ if (!valp) bail("trie_get_ins");
+ } while (*valp != NULL);
+ *valp = &leaf[i];
+ leaf[i].key = str;
+ leaf[i].len = len;
+ leaf[i].cowstate = cow_absent;
+ }
+ trie_free(trie);
+
+ return (leaf);
+}
+
+static void
+dead_leaves(struct cowleaf *leaf, size_t leaves)
+{
+ for (size_t i = 0; i < leaves; i++)
+ free(leaf[i].key);
+ free(leaf);
+}
+
+static void
+mark_cb(trie_val_t val, const uint8_t *key, size_t len, void *d)
+{
+ struct cowleaf *leaf = val;
+ assert(leaf->cowstate == cow_unmarked &&
+ "leaf should go from unmarked to shared exactly once");
+ leaf->cowstate = cow_shared;
+ (void)key;
+ (void)len;
+ (void)d;
+}
+
+static void
+commit_rollback(trie_val_t val, const uint8_t *key, size_t len, void *d)
+{
+ struct cowleaf *leaf = val;
+ int *commit = d;
+ if (*commit)
+ assert((leaf->cowstate == cow_shared ||
+ leaf->cowstate == cow_old) &&
+ "committing deletes from old trie");
+ else
+ assert((leaf->cowstate == cow_shared ||
+ leaf->cowstate == cow_new) &&
+ "roll back deletes from new trie");
+ if (leaf->cowstate != cow_shared)
+ leaf->cowstate = deadbeef;
+ (void)key;
+ (void)len;
+}
+
+static void
+del_cow(trie_cow_t *x, struct cowleaf *leaf)
+{
+ _unused_ trie_val_t val;
+ assert(KNOT_EOK == trie_del_cow(x,
+ (uint8_t *)leaf->key,
+ (uint32_t)leaf->len,
+ &val));
+ assert(val == leaf);
+}
+
+static void
+usage(void) {
+ fprintf(stderr,
+ "usage: test_qp-cow [-k N] [-l N] [-t N]\n"
+ " -k N maximum key length (default %d)\n"
+ " -l N number of leaves (default %d)\n"
+ " -m N mutations per transaction (default %d)\n"
+ " -t N number of transactions (default %d)\n",
+ MAX_KEYLEN,
+ MAX_LEAVES,
+ MAX_MUTATIONS,
+ MAX_TRANSACTIONS);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ size_t keylen = MAX_KEYLEN;
+ size_t leaves = MAX_LEAVES;
+ int mutations = MAX_MUTATIONS;
+ int transactions = MAX_TRANSACTIONS;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "k:l:m:t:h")) != -1)
+ switch (opt) {
+ case('k'):
+ keylen = (unsigned)atoi(optarg);
+ continue;
+ case('l'):
+ leaves = (unsigned)atoi(optarg);
+ continue;
+ case('m'):
+ mutations = atoi(optarg);
+ continue;
+ case('t'):
+ transactions = atoi(optarg);
+ continue;
+ default:
+ usage();
+ }
+
+ if (argc != optind)
+ usage();
+
+ plan(transactions);
+
+ struct cowleaf *leaf = grow_leaves(keylen, leaves);
+ trie_t *t = trie_create(NULL);
+
+ for (int round = 0; round < transactions; round++) {
+ trie_cow_t *x = trie_cow(t, mark_cb, NULL);
+ if (!x) sysbail("trie_cow");
+
+ int hits = prng(mutations);
+ for (int hit = 0; hit < hits; hit++) {
+ size_t i = prng(leaves);
+ switch (leaf[i].cowstate) {
+ case(cow_absent): {
+ trie_val_t *val =
+ trie_get_cow(x,
+ (uint8_t *)leaf[i].key,
+ (uint32_t)leaf[i].len);
+ if (!val) sysbail("trie_get_cow");
+ assert(*val == NULL && "new leaf");
+ *val = &leaf[i];
+ leaf[i].cowstate = cow_new;
+ } break;
+ case(cow_unmarked): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_shared &&
+ "state changed unmarked -> shared");
+ leaf[i].cowstate = cow_old;
+ } break;
+ case(cow_shared): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_shared &&
+ "state remained shared");
+ leaf[i].cowstate = cow_old;
+ } break;
+ case(cow_new): {
+ del_cow(x, &leaf[i]);
+ assert(leaf[i].cowstate == cow_new &&
+ "state remained new");
+ leaf[i].cowstate = cow_absent;
+ } break;
+ case(cow_old): {
+ // don't want to mess with old tree
+ } break;
+ case(deadbeef): {
+ assert(!"deadbeef should not be possible");
+ } break;
+ default:
+ assert(!"bug - unhandled state");
+ }
+ }
+
+ int commit = !prng(2);
+ if (commit)
+ t = trie_cow_commit(x, commit_rollback, &commit);
+ else
+ t = trie_cow_rollback(x, commit_rollback, &commit);
+
+ trie_it_t *it = trie_it_begin(t);
+ while (!trie_it_finished(it)) {
+ trie_val_t *val = trie_it_val(it);
+ assert(val != NULL);
+ struct cowleaf *l = *val;
+ if (commit)
+ assert((l->cowstate == cow_unmarked ||
+ l->cowstate == cow_shared ||
+ l->cowstate == cow_new) &&
+ "committing expected state");
+ else
+ assert((l->cowstate == cow_unmarked ||
+ l->cowstate == cow_shared ||
+ l->cowstate == cow_old) &&
+ "roll back expected state");
+ l->cowstate = cow_unmarked;
+ trie_it_next(it);
+ }
+ trie_it_free(it);
+
+ for (size_t i = 0; i < leaves; i++) {
+ assert((leaf[i].cowstate == cow_unmarked ||
+ leaf[i].cowstate == cow_absent ||
+ leaf[i].cowstate == deadbeef) &&
+ "cleanup leaves either unmarked or dead");
+ if (leaf[i].cowstate == deadbeef)
+ leaf[i].cowstate = cow_absent;
+ }
+ ok(1, "transaction done");
+ }
+
+ trie_free(t);
+ dead_leaves(leaf, leaves);
+
+ return 0;
+}
diff --git a/tests/contrib/test_qp-trie.c b/tests/contrib/test_qp-trie.c
new file mode 100644
index 0000000..a50661f
--- /dev/null
+++ b/tests/contrib/test_qp-trie.c
@@ -0,0 +1,284 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tap/basic.h>
+
+#include "contrib/qp-trie/trie.h"
+#include "contrib/macros.h"
+#include "contrib/string.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+
+/* UCW array sorting defines. */
+#define ASORT_PREFIX(X) str_key_##X
+#define ASORT_KEY_TYPE char*
+#define ASORT_LT(x, y) (strcmp((x), (y)) < 0)
+#include "contrib/ucw/array-sort.h"
+
+/* Constants. */
+#define KEY_MAXLEN 64
+
+/* Generate random key. */
+static const char *alphabet = "abcdefghijklmn0123456789";
+static char *str_key_rand(size_t len)
+{
+ char *s = malloc(len);
+ memset(s, 0, len);
+ for (unsigned i = 0; i < len - 1; ++i) {
+ s[i] = alphabet[rand() % strlen(alphabet)];
+ }
+ return s;
+}
+
+/* Check lesser or equal result. */
+static bool str_key_get_leq(trie_t *trie, char **keys, size_t i, size_t size)
+{
+ static char key_buf[KEY_MAXLEN];
+
+ int ret = 0;
+ trie_val_t *val = NULL;
+ const char *key = keys[i];
+ size_t key_len = strlen(key) + 1;
+ memcpy(key_buf, key, key_len);
+
+ /* Count equal first keys. */
+ size_t first_key_count = 1;
+ for (size_t k = 1; k < size; ++k) {
+ if (strcmp(keys[0], keys[k]) == 0) {
+ first_key_count += 1;
+ } else {
+ break;
+ }
+ }
+
+ /* Before current key. */
+ key_buf[key_len - 2] -= 1;
+ if (i < first_key_count) {
+ ret = trie_get_leq(trie, (uint8_t *)key_buf, key_len, &val);
+ if (ret != KNOT_ENOENT) {
+ diag("%s: leq for key BEFORE %zu/'%s' ret = %d", __func__, i, keys[i], ret);
+ return false; /* No key before first. */
+ }
+ } else {
+ ret = trie_get_leq(trie, (uint8_t *)key_buf, key_len, &val);
+ if (ret < KNOT_EOK || strcmp(*val, key_buf) > 0) {
+ diag("%s: '%s' is not before the key %zu/'%s'", __func__, (char*)*val, i, keys[i]);
+ return false; /* Found key must be LEQ than searched. */
+ }
+ }
+
+ /* Current key. */
+ key_buf[key_len - 2] += 1;
+ ret = trie_get_leq(trie, (uint8_t *)key_buf, key_len, &val);
+ if (! (ret == KNOT_EOK && val && strcmp(*val, key_buf) == 0)) {
+ diag("%s: leq for key %zu/'%s' ret = %d", __func__, i, keys[i], ret);
+ return false; /* Must find equal match. */
+ }
+
+ /* After the current key. */
+ key_buf[key_len - 2] += 1;
+ ret = trie_get_leq(trie, (uint8_t *)key_buf, key_len, &val);
+ if (! (ret >= KNOT_EOK && strcmp(*val, key_buf) <= 0)) {
+ diag("%s: leq for key AFTER %zu/'%s' ret = %d %s", __func__, i, keys[i], ret, (char*)*val);
+ return false; /* Every key must have its LEQ match. */
+ }
+
+ return true;
+
+}
+
+static void test_wildcards(void)
+{
+ /* Test zone. */
+ const char *names[] = {
+ "*",
+ "example.cz",
+ "*.example.cz",
+ "+.example.cz",
+
+ "*.exampld.cz",
+ "www.exampld.cz",
+ };
+ /* Query-answer pairs for wildcard search. */
+ const char *qa_pairs[][2] = {
+ { ".", NULL },
+ { "*", "*" },
+ { "bar", "*" },
+ { "foo.test.", "*" },
+ { "example.cz", "example.cz" },
+ { "*.example.cz", "*.example.cz" },
+ { "a.example.cz", "*.example.cz" },
+ { "ab.cd.example.cz", "*.example.cz" },
+ { "a+.example.cz", "*.example.cz" },
+ { "+.example.cz", "+.example.cz" },
+ { "exampld.cz", NULL },
+ { ":.exampld.cz", "*.exampld.cz" },
+ { "ww.exampld.cz", "*.exampld.cz" },
+ };
+
+ trie_t *trie = trie_create(NULL);
+ if (!trie) ok(false, "trie: create");
+
+ /* Insert the whole zone. */
+ for (int i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
+ knot_dname_storage_t dname_st, lf_st;
+ const knot_dname_t
+ *dname = knot_dname_from_str(dname_st, names[i], sizeof(dname_st)),
+ *lf = knot_dname_lf(dname, lf_st);
+ if (!dname || !lf) {
+ ok(false, "trie: converting '%s'", names[i]);
+ return;
+ }
+
+ trie_val_t *val = trie_get_ins(trie, lf + 1 , lf[0]);
+ if (!val || *val != NULL) {
+ ok(false, "trie: inserting '%s' (as dname_lf)", names[i]);
+ return;
+ }
+ *val = (void *)names[i];
+ }
+
+ /* Perform each test query. */
+ for (int i = 0; i < sizeof(qa_pairs) / sizeof(qa_pairs[0]); ++i) {
+ knot_dname_storage_t q_dname_st, q_lf_st;
+ const knot_dname_t *q_dname =
+ knot_dname_from_str(q_dname_st, qa_pairs[i][0], sizeof(q_dname_st));
+ const knot_dname_t *q_lf = knot_dname_lf(q_dname, q_lf_st);
+ if (!q_dname || !q_lf) {
+ ok(false, "trie: converting '%s'", qa_pairs[i][0]);
+ return;
+ }
+
+ const char **ans = (const char **)trie_get_try_wildcard(trie, q_lf + 1, q_lf[0]);
+ bool is_ok = !!ans == !!qa_pairs[i][1] && (!ans || !strcmp(*ans, qa_pairs[i][1]));
+ if (!is_ok) {
+ ok(false, "trie: wildcard test for '%s' -> '%s'",
+ qa_pairs[i][0], ans ? *ans : "<null>");
+ return;
+ }
+ }
+
+ trie_free(trie);
+ ok(true, "trie: wildcard searches");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Random keys. */
+ srand(time(NULL));
+ unsigned key_count = 100000;
+ char **keys = malloc(sizeof(char*) * key_count);
+ /* key must have at least one char and a nul terminator
+ so that the before/after checks have a char to modify */
+ for (unsigned i = 0; i < key_count; ++i) {
+ keys[i] = str_key_rand(rand() % (KEY_MAXLEN - 2) + 2);
+ }
+
+ /* Sort random keys. */
+ str_key_sort(keys, key_count);
+
+ /* Create trie */
+ trie_val_t *val = NULL;
+ trie_t *trie = trie_create(NULL);
+ ok(trie != NULL, "trie: create");
+
+ /* Insert keys */
+ bool passed = true;
+ size_t inserted = 0;
+ for (unsigned i = 0; i < key_count; ++i) {
+ val = trie_get_ins(trie, (uint8_t *)keys[i], strlen(keys[i]) + 1);
+ if (!val) {
+ passed = false;
+ break;
+ }
+ if (*val == NULL) {
+ *val = keys[i];
+ ++inserted;
+ }
+ }
+ ok(passed, "trie: insert");
+
+ /* Check total insertions against trie weight. */
+ is_int(trie_weight(trie), inserted, "trie: trie weight matches insertions");
+
+ /* Lookup all keys */
+ passed = true;
+ for (unsigned i = 0; i < key_count; ++i) {
+ val = trie_get_try(trie, (uint8_t *)keys[i], strlen(keys[i]) + 1);
+ if (val && (*val == keys[i] || strcmp(*val, keys[i]) == 0)) {
+ continue;
+ } else {
+ diag("trie: mismatch on element '%u'", i);
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "trie: lookup all keys");
+
+ /* Lesser or equal lookup. */
+ passed = true;
+ for (unsigned i = 0; i < key_count; ++i) {
+ if (!str_key_get_leq(trie, keys, i, key_count)) {
+ passed = false;
+ for (int off = -10; off < 10; ++off) {
+ int k = (int)i + off;
+ if (k < 0 || k >= key_count) {
+ continue;
+ }
+ diag("[%u/%d]: %s%s", i, off, off == 0?">":"",keys[k]);
+ }
+ break;
+ }
+ }
+ ok(passed, "trie: find lesser or equal for all keys");
+
+ /* Sorted iteration. */
+ char key_buf[KEY_MAXLEN] = {'\0'};
+ size_t iterated = 0;
+ trie_it_t *it = trie_it_begin(trie);
+ while (!trie_it_finished(it)) {
+ size_t cur_key_len = 0;
+ const char *cur_key = (const char *)trie_it_key(it, &cur_key_len);
+ if (iterated > 0) { /* Only if previous exists. */
+ if (strcmp(key_buf, cur_key) > 0) {
+ diag("'%s' <= '%s' FAIL\n", key_buf, cur_key);
+ break;
+ }
+ }
+ ++iterated;
+ memcpy(key_buf, cur_key, cur_key_len);
+ trie_it_next(it);
+ }
+ is_int(inserted, iterated, "trie: sorted iteration");
+ trie_it_free(it);
+
+ /* Cleanup */
+ for (unsigned i = 0; i < key_count; ++i) {
+ free(keys[i]);
+ }
+ free(keys);
+ trie_free(trie);
+
+ /* Test trie_get_try_wildcard(). */
+ test_wildcards();
+
+ return 0;
+}
diff --git a/tests/contrib/test_siphash.c b/tests/contrib/test_siphash.c
new file mode 100644
index 0000000..1ca019b
--- /dev/null
+++ b/tests/contrib/test_siphash.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2018 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 <string.h>
+#include <tap/basic.h>
+
+// Prevent possible linking with a system SiphHash (OpenBSD).
+#include "contrib/openbsd/siphash.c"
+
+// https://github.com/veorq/SipHash
+const uint8_t vectors24[64][8] = {
+ { 0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72 },
+ { 0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74 },
+ { 0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d },
+ { 0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85 },
+ { 0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf },
+ { 0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18 },
+ { 0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb },
+ { 0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab },
+ { 0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93 },
+ { 0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e },
+ { 0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a },
+ { 0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4 },
+ { 0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75 },
+ { 0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14 },
+ { 0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7 },
+ { 0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1 },
+ { 0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f },
+ { 0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69 },
+ { 0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b },
+ { 0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb },
+ { 0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe },
+ { 0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0 },
+ { 0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93 },
+ { 0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8 },
+ { 0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8 },
+ { 0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc },
+ { 0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17 },
+ { 0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f },
+ { 0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde },
+ { 0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6 },
+ { 0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad },
+ { 0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32 },
+ { 0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71 },
+ { 0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7 },
+ { 0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12 },
+ { 0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15 },
+ { 0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31 },
+ { 0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02 },
+ { 0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca },
+ { 0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a },
+ { 0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e },
+ { 0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad },
+ { 0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18 },
+ { 0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4 },
+ { 0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9 },
+ { 0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9 },
+ { 0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb },
+ { 0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0 },
+ { 0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6 },
+ { 0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7 },
+ { 0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee },
+ { 0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1 },
+ { 0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a },
+ { 0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81 },
+ { 0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f },
+ { 0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24 },
+ { 0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7 },
+ { 0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea },
+ { 0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60 },
+ { 0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66 },
+ { 0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c },
+ { 0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f },
+ { 0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5 },
+ { 0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95 },
+};
+
+static void block_test(SIPHASH_KEY *key, uint8_t *data, int data_len, int block_len)
+{
+ int count = data_len / block_len;
+ int rest = data_len % block_len;
+
+ SIPHASH_CTX ctx;
+ SipHash24_Init(&ctx, key);
+
+ for (int i = 0; i < count; i++) {
+ SipHash24_Update(&ctx, data + i * block_len, block_len);
+ }
+ SipHash24_Update(&ctx, data + count * block_len, rest);
+
+ uint64_t hash = SipHash24_End(&ctx);
+ ok(memcmp(&hash, vectors24[data_len], sizeof(uint64_t)) == 0,
+ "siphash24: %i-byte block updates", block_len);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ SIPHASH_KEY key;
+ memcpy(&key.k0, "\x00\x01\x02\x03\x04\x05\x06\x07", sizeof(uint64_t));
+ memcpy(&key.k1, "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", sizeof(uint64_t));
+
+ uint8_t data[64];
+ for (int i = 0; i < sizeof(data); i++) {
+ data[i] = i;
+ }
+
+ for (int data_len = 0; data_len < sizeof(data); data_len++) {
+ diag("data length %i", data_len);
+
+ uint64_t hash = SipHash24(&key, data, data_len);
+ ok(memcmp(&hash, vectors24[data_len], sizeof(uint64_t)) == 0,
+ "siphash24: 1-block update");
+
+ for (int block_len = 1; block_len <= 8; block_len++) {
+ block_test(&key, data, data_len, block_len);
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/contrib/test_sockaddr.c b/tests/contrib/test_sockaddr.c
new file mode 100644
index 0000000..87d2d77
--- /dev/null
+++ b/tests/contrib/test_sockaddr.c
@@ -0,0 +1,229 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+
+static void test_sockaddr_is_any(void)
+{
+ struct sockaddr_storage invalid = { 0 };
+ ok(!sockaddr_is_any(&invalid), "sockaddr_is_any: invalid");
+
+ struct sockaddr_storage path = { 0 };
+ path.ss_family = AF_UNIX;
+ ok(!sockaddr_is_any(&path), "sockaddr_is_any: unix");
+
+ struct sockaddr_storage ipv4_local = { 0 };
+ sockaddr_set(&ipv4_local, AF_INET, "127.0.0.1", 0);
+ ok(!sockaddr_is_any(&ipv4_local), "sockaddr_is_any: IPv4 local");
+
+ struct sockaddr_storage ipv4_any = { 0 };
+ sockaddr_set(&ipv4_any, AF_INET, "0.0.0.0", 0);
+ ok(sockaddr_is_any(&ipv4_any), "sockaddr_is_any: IPv4 any");
+
+ struct sockaddr_storage ipv6_local = { 0 };
+ sockaddr_set(&ipv6_local, AF_INET6, "::1", 0);
+ ok(!sockaddr_is_any(&ipv6_local), "sockaddr_is_any: IPv6 local");
+
+ struct sockaddr_storage ipv6_any = { 0 };
+ sockaddr_set(&ipv6_any, AF_INET6, "::", 0);
+ ok(sockaddr_is_any(&ipv6_any), "sockaddr_is_any: IPv6 any");
+}
+
+static void check_sockaddr_set(struct sockaddr_storage *ss, int family,
+ const char *straddr, int port)
+{
+ int ret = sockaddr_set(ss, family, straddr, port);
+ is_int(KNOT_EOK, ret, "set address '%s'", straddr);
+}
+
+static void test_net_match(void)
+{
+ int ret;
+ struct sockaddr_storage t = { 0 };
+
+ // 127 dec ~ 01111111 bin
+ // 170 dec ~ 10101010 bin
+ struct sockaddr_storage ref4 = { 0 };
+ check_sockaddr_set(&ref4, AF_INET, "127.170.170.127", 0);
+
+ // 7F hex ~ 01111111 bin
+ // AA hex ~ 10101010 bin
+ struct sockaddr_storage ref6 = { 0 };
+ check_sockaddr_set(&ref6, AF_INET6, "7FAA::AA7F", 0);
+
+ ret = sockaddr_net_match(&ref4, &ref6, 32);
+ ok(ret == false, "match: family mismatch");
+
+ ret = sockaddr_net_match(NULL, &ref4, 32);
+ ok(ret == false, "match: NULL first parameter");
+ ret = sockaddr_net_match(&ref4, NULL, 32);
+ ok(ret == false, "match: NULL second parameter");
+
+ ret = sockaddr_net_match(&ref4, &ref4, -1);
+ ok(ret == true, "match: ipv4 - identity, auto full prefix");
+ ret = sockaddr_net_match(&ref4, &ref4, 31);
+ ok(ret == true, "match: ipv4 - identity, subnet");
+ ret = sockaddr_net_match(&ref4, &ref4, 32);
+ ok(ret == true, "match: ipv4 - identity, full prefix");
+ ret = sockaddr_net_match(&ref4, &ref4, 33);
+ ok(ret == true, "match: ipv4 - identity, prefix overflow");
+
+ ret = sockaddr_net_match(&ref6, &ref6, -1);
+ ok(ret == true, "match: ipv6 - identity, auto full prefix");
+ ret = sockaddr_net_match(&ref6, &ref6, 127);
+ ok(ret == true, "match: ipv6 - identity, subnet");
+ ret = sockaddr_net_match(&ref6, &ref6, 128);
+ ok(ret == true, "match: ipv6 - identity, full prefix");
+ ret = sockaddr_net_match(&ref6, &ref6, 129);
+ ok(ret == true, "match: ipv6 - identity, prefix overflow");
+
+ // 124 dec ~ 01111100 bin
+ check_sockaddr_set(&t, AF_INET, "124.0.0.0", 0);
+ ret = sockaddr_net_match(&t, &ref4, 5);
+ ok(ret == true, "match: ipv4 - first byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref4, 6);
+ ok(ret == true, "match: ipv4 - first byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref4, 7);
+ ok(ret == false, "match: ipv4 - first byte, not match");
+
+ check_sockaddr_set(&t, AF_INET, "127.170.170.124", 0);
+ ret = sockaddr_net_match(&t, &ref4, 29);
+ ok(ret == true, "match: ipv4 - last byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref4, 30);
+ ok(ret == true, "match: ipv4 - last byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref4, 31);
+ ok(ret == false, "match: ipv4 - last byte, not match");
+
+ // 7C hex ~ 01111100 bin
+ check_sockaddr_set(&t, AF_INET6, "7CAA::", 0);
+ ret = sockaddr_net_match(&t, &ref6, 5);
+ ok(ret == true, "match: ipv6 - first byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref6, 6);
+ ok(ret == true, "match: ipv6 - first byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref6, 7);
+ ok(ret == false, "match: ipv6 - first byte, not match");
+
+ check_sockaddr_set(&t, AF_INET6, "7FAA::AA7C", 0);
+ ret = sockaddr_net_match(&t, &ref6, 125);
+ ok(ret == true, "match: ipv6 - last byte, shorter prefix");
+ ret = sockaddr_net_match(&t, &ref6, 126);
+ ok(ret == true, "match: ipv6 - last byte, precise prefix");
+ ret = sockaddr_net_match(&t, &ref6, 127);
+ ok(ret == false, "match: ipv6 - last byte, not match");
+}
+
+static void test_range_match(void)
+{
+ bool ret;
+ struct sockaddr_storage t = { 0 };
+ struct sockaddr_storage min = { 0 };
+ struct sockaddr_storage max = { 0 };
+
+ // IPv4 tests.
+
+ check_sockaddr_set(&min, AF_INET, "0.0.0.0", 0);
+ check_sockaddr_set(&max, AF_INET, "255.255.255.255", 0);
+
+ check_sockaddr_set(&t, AF_INET, "0.0.0.0", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 max range - minimum");
+ check_sockaddr_set(&t, AF_INET, "255.255.255.255", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 max range - maximum");
+
+ check_sockaddr_set(&min, AF_INET, "1.13.113.213", 0);
+ check_sockaddr_set(&max, AF_INET, "2.24.124.224", 0);
+
+ check_sockaddr_set(&t, AF_INET, "1.12.113.213", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv4 middle range - negative far min");
+ check_sockaddr_set(&t, AF_INET, "1.13.113.212", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv4 middle range - negative close min");
+ check_sockaddr_set(&t, AF_INET, "1.13.113.213", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - minimum");
+ check_sockaddr_set(&t, AF_INET, "1.13.213.213", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - middle");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.224", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv4 middle range - max");
+ check_sockaddr_set(&t, AF_INET, "2.24.124.225", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv4 middle range - negative close max");
+ check_sockaddr_set(&t, AF_INET, "2.25.124.225", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv4 middle range - negative far max");
+
+ // IPv6 tests.
+
+ check_sockaddr_set(&min, AF_INET6, "::0", 0);
+ check_sockaddr_set(&max, AF_INET6,
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", 0);
+
+ check_sockaddr_set(&t, AF_INET6, "::0", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 max range - minimum");
+ check_sockaddr_set(&t, AF_INET6,
+ "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 max range - maximum");
+
+ check_sockaddr_set(&min, AF_INET6, "1:13::ABCD:200B", 0);
+ check_sockaddr_set(&max, AF_INET6, "2:A24::124:224", 0);
+
+ check_sockaddr_set(&t, AF_INET6, "1:12::BCD:2000", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv6 middle range - negative far min");
+ check_sockaddr_set(&t, AF_INET6, "1:13::ABCD:200A", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv6 middle range - negative close min");
+ check_sockaddr_set(&t, AF_INET6, "1:13::ABCD:200B", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 middle range - minimum");
+ check_sockaddr_set(&t, AF_INET6, "1:13:0:12:34:0:ABCD:200B", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 middle range - middle");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:224", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == true, "match: ipv6 middle range - max");
+ check_sockaddr_set(&t, AF_INET6, "2:A24::124:225", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv6 middle range - negative close max");
+ check_sockaddr_set(&t, AF_INET6, "2:FA24::4:24", 0);
+ ret = sockaddr_range_match(&t, &min, &max);
+ ok(ret == false, "match: ipv6 middle range - negative far max");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("sockaddr_is_any");
+ test_sockaddr_is_any();
+
+ diag("sockaddr_net_match");
+ test_net_match();
+
+ diag("sockaddr_range_match");
+ test_range_match();
+
+ return 0;
+}
diff --git a/tests/contrib/test_spinlock.c b/tests/contrib/test_spinlock.c
new file mode 100644
index 0000000..0d5122b
--- /dev/null
+++ b/tests/contrib/test_spinlock.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2021 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 <signal.h>
+#include <tap/basic.h>
+
+#include "knot/server/dthreads.h"
+#include "contrib/spinlock.h"
+
+#define THREADS 8
+#define CYCLES 100000
+
+static volatile int counter = 0;
+static volatile int tens_counter = 0;
+static knot_spin_t spinlock;
+
+static int thread(struct dthread *thread)
+{
+ volatile int i, j, k;
+
+ for (i = 0; i < CYCLES; i++) {
+ knot_spin_lock(&spinlock);
+ j = counter;
+ k = tens_counter;
+ if (++j % 10 == 0) {
+ k++;
+ }
+ tens_counter = k;
+ counter = j;
+ knot_spin_unlock(&spinlock);
+ }
+
+ return 0;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Register service and signal handler
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ knot_spin_init(&spinlock);
+
+ dt_unit_t *unit = dt_create(THREADS, thread, NULL, NULL);
+ dt_start(unit);
+ dt_join(unit);
+ dt_delete(&unit);
+
+ knot_spin_destroy(&spinlock);
+
+ is_int(THREADS * CYCLES, counter, "spinlock: protected counter one");
+ is_int(THREADS * CYCLES / 10, tens_counter, "spinlock: protected counter two");
+
+ return 0;
+}
diff --git a/tests/contrib/test_string.c b/tests/contrib/test_string.c
new file mode 100644
index 0000000..681dd61
--- /dev/null
+++ b/tests/contrib/test_string.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2015 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 <tap/basic.h>
+
+#include <stdlib.h>
+
+#include "contrib/string.h"
+
+static void test_strstrip(void)
+{
+ char *c = NULL;
+
+ c = strstrip("hello");
+ is_string("hello", c, "strstrip: no whitespace");
+ free(c);
+
+ c = strstrip("world \n");
+ is_string("world", c, "strstrip: trailing whitespace");
+ free(c);
+
+ c = strstrip(" \n banana");
+ is_string("banana", c, "strstrip: leading whitespace");
+ free(c);
+
+ c = strstrip(" \t hello world \n");
+ is_string("hello world", c, "strstrip: leading and trailing");
+ free(c);
+
+ c = strstrip("");
+ is_string("", c, "strstrip: empty string");
+ free(c);
+
+ c = strstrip(" ");
+ is_string("", c, "strstrip: just whitespaces");
+ free(c);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_strstrip();
+
+ return 0;
+}
diff --git a/tests/contrib/test_strtonum.c b/tests/contrib/test_strtonum.c
new file mode 100644
index 0000000..e883575
--- /dev/null
+++ b/tests/contrib/test_strtonum.c
@@ -0,0 +1,156 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <tap/basic.h>
+
+#include "contrib/strtonum.h"
+#include "libdnssec/error.h"
+#include "libknot/attribute.h"
+
+static void test_u8(const char *in, uint8_t expected, int errcode)
+{
+ uint8_t out = 0x11;
+ assert(expected != out);
+
+ ok(str_to_u8(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u8 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_u16(const char *in, uint16_t expected, int errcode)
+{
+ uint16_t out = 0x0101;
+ assert(expected != out);
+
+ ok(str_to_u16(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u16 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_u32(const char *in, uint32_t expected, int errcode)
+{
+ uint32_t out = 0x010101;
+ assert(expected != out);
+
+ ok(str_to_u32(in, &out) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_u32 %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_int(const char *in, int expected, int errcode)
+{
+ int out = 12345;
+ assert(expected != out);
+
+ ok(str_to_int(in, &out, INT_MIN, INT_MAX) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_int %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+static void test_size(const char *in, size_t expected, size_t min, size_t max,
+ int errcode)
+{
+ size_t out = 12345;
+ assert(expected != out);
+
+ ok(str_to_size(in, &out, min, max) == errcode &&
+ (errcode != KNOT_EOK || out == expected),
+ "str_to_int %s on \"%s\"",
+ (errcode == KNOT_EOK ? "succeeds" : "fails"), in);
+}
+
+#define asprintf(args, ...) do { \
+ _unused_ int r = (asprintf)(args, ##__VA_ARGS__); assert(r >= 0); \
+} while (0);
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_u8("-1", 0, KNOT_EINVAL);
+ test_u8("256", 0, KNOT_ERANGE);
+ test_u8("0x1", 0, KNOT_EINVAL);
+ test_u8(" 1", 0, KNOT_EINVAL);
+ test_u8("1 ", 0, KNOT_EINVAL);
+ test_u8("0", 0, KNOT_EOK);
+ test_u8("42", 42, KNOT_EOK);
+ test_u8("+84", 84, KNOT_EOK);
+ test_u8("255", UINT8_MAX, KNOT_EOK);
+
+ test_u16("-1", 0, KNOT_EINVAL);
+ test_u16("65536", 0, KNOT_ERANGE);
+ test_u16("0x1", 0, KNOT_EINVAL);
+ test_u16(" 1", 0, KNOT_EINVAL);
+ test_u16("1 ", 0, KNOT_EINVAL);
+ test_u16("0", 0, KNOT_EOK);
+ test_u16("65280", 65280, KNOT_EOK);
+ test_u16("+256", 256, KNOT_EOK);
+ test_u16("65535", UINT16_MAX, KNOT_EOK);
+
+ test_u32("-1", 0, KNOT_EINVAL);
+ test_u32("4294967296", 0, KNOT_ERANGE);
+ test_u32("0x1", 0, KNOT_EINVAL);
+ test_u32(" 1", 0, KNOT_EINVAL);
+ test_u32("1 ", 0, KNOT_EINVAL);
+ test_u32("0", 0, KNOT_EOK);
+ test_u32("65280", 65280, KNOT_EOK);
+ test_u32("+256", 256, KNOT_EOK);
+ test_u32("4294967295", UINT32_MAX, KNOT_EOK);
+
+ test_size("-1", 0, 0, 1, KNOT_EINVAL);
+ test_size("4294967296", 0, 0, 1, KNOT_ERANGE);
+ test_size("0", 0, 1, 2, KNOT_ERANGE);
+ test_size("0x1", 0, 0, 1, KNOT_EINVAL);
+ test_size(" 1", 0, 0, 1, KNOT_EINVAL);
+ test_size("1 ", 0, 0, 1, KNOT_EINVAL);
+ test_size("0", 0, 0, 1, KNOT_EOK);
+ test_size("65280", 65280, 0, 65280, KNOT_EOK);
+ test_size("+256", 256, 0, 65280, KNOT_EOK);
+
+ char *int_under = NULL;
+ asprintf(&int_under, "%lld", (long long)INT_MIN - 1);
+ char *int_min = NULL;
+ asprintf(&int_min, "%lld", (long long)INT_MIN);
+ char *int_max = NULL;
+ asprintf(&int_max, "%lld", (long long)INT_MAX);
+ char *int_over = NULL;
+ asprintf(&int_over, "%lld", (long long)INT_MAX + 1);
+
+ test_int(int_under, 0, KNOT_ERANGE);
+ test_int(int_over, 0, KNOT_ERANGE);
+ test_int("0x1", 0, KNOT_EINVAL);
+ test_int(" 1", 0, KNOT_EINVAL);
+ test_int("1 ", 0, KNOT_EINVAL);
+ test_int(int_min, INT_MIN, KNOT_EOK);
+ test_int("0", 0, KNOT_EOK);
+ test_int("268435459", 268435459, KNOT_EOK);
+ test_int("+1073741827", 1073741827, KNOT_EOK);
+ test_int(int_max, INT_MAX, KNOT_EOK);
+
+ free(int_under);
+ free(int_min);
+ free(int_max);
+ free(int_over);
+
+ return 0;
+}
diff --git a/tests/contrib/test_time.c b/tests/contrib/test_time.c
new file mode 100644
index 0000000..84518f2
--- /dev/null
+++ b/tests/contrib/test_time.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2017 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 <tap/basic.h>
+
+#include <string.h>
+
+#include "contrib/time.h"
+
+static void test_now(void)
+{
+ struct timespec t = time_now();
+ ok(t.tv_sec != 0, "time_now() returns something");
+}
+
+static void test_diff(void)
+{
+ struct timespec t1 = { 10, 1000 };
+ struct timespec t2 = { 50, 1500 };
+ struct timespec t3 = { 70, 500 };
+
+ struct timespec res;
+
+ res = time_diff(&t1, &t2);
+ ok(res.tv_sec == 40 && res.tv_nsec == 500, "time_diff()");
+
+ res = time_diff(&t2, &t3);
+ ok(res.tv_sec == 19 && res.tv_nsec == 999999000, "time_diff() ns overflow");
+
+ res = time_diff(&t3, &t1);
+ ok(res.tv_sec == -60 && res.tv_nsec == 500, "time_diff() negative");
+
+ res = time_diff(&t2, &t1);
+ ok(res.tv_sec == -41 && res.tv_nsec == 999999500, "time_diff() negative");
+}
+
+static void test_diff_ms(void)
+{
+ struct timespec t1 = { 10, 1000 };
+ struct timespec t2 = { 50, 500 };
+
+ float ms = 0.0;
+
+ ms = time_diff_ms(&t1, &t2);
+ ok(39990.0 < ms && ms < 40010.0, "time_diff_ms()");
+
+ ms = time_diff_ms(&t2, &t1);
+ ok(-40010.0 < ms && ms < -39990.0, "time_diff_ms() negative");
+}
+
+static void test_knot_time(void)
+{
+ knot_time_t a = knot_time();
+ knot_time_t inf = 0;
+ knot_time_t c;
+ knot_timediff_t d;
+ int ret;
+
+ ok(a != 0, "knot time not zero");
+
+ ret = knot_time_cmp(a, a);
+ ok(ret == 0, "compare same times");
+
+ ret = knot_time_cmp(a - 1, a + 1);
+ ok(ret == -1, "compare smaller time");
+
+ ret = knot_time_cmp(a + 10, a - 10);
+ ok(ret == 1, "compare bigger time");
+
+ ret = knot_time_cmp(inf, inf);
+ ok(ret == 0, "compare two infinities");
+
+ ret = knot_time_cmp(a, inf);
+ ok(ret == -1, "compare time and infinity");
+
+ ret = knot_time_cmp(inf, a);
+ ok(ret == 1, "compare infinity and time");
+
+ c = knot_time_min(a, a);
+ ok(c == a, "take same time");
+
+ c = knot_time_min(a, a + 1);
+ ok(c == a, "take first smaller");
+
+ c = knot_time_min(a + 1, a);
+ ok(c == a, "take second smaller");
+
+ c = knot_time_min(inf, inf);
+ ok(c == inf, "take same infinity");
+
+ c = knot_time_min(a, inf);
+ ok(c == a, "take first finite");
+
+ c = knot_time_min(inf, a);
+ ok(c == a, "take second finite");
+
+ d = knot_time_diff(a + 1, a);
+ ok(d == 1, "positive diff");
+
+ d = knot_time_diff(a, a + 1);
+ ok(d == -1, "negative diff");
+
+ d = knot_time_diff(inf, inf);
+ ok(d == KNOT_TIMEDIFF_MAX, "positive double infinity diff");
+
+ d = knot_time_diff(inf, a);
+ ok(d == KNOT_TIMEDIFF_MAX, "positive infinity diff");
+
+ d = knot_time_diff(a, inf);
+ ok(d == KNOT_TIMEDIFF_MIN, "negative infinity diff");
+}
+
+static void test_time_parse_expect(int ret, knot_time_t res,
+ knot_time_t expected, const char *msg)
+{
+ ok(ret == 0, "time_parse %s ok", msg);
+ ok(res == expected, "time_parse %s result", msg);
+}
+
+static void test_time_parse(void)
+{
+ knot_time_t res;
+ int ret;
+
+ ret = knot_time_parse("", "", &res);
+ test_time_parse_expect(ret, res, 0, "nihilist");
+
+ ret = knot_time_parse("#", "12345", &res);
+ test_time_parse_expect(ret, res, 12345, "unix");
+
+ ret = knot_time_parse("+-#U", "-1h", &res);
+ test_time_parse_expect(ret, res, knot_time() - 3600, "hour");
+
+ ret = knot_time_parse("+-#u'nths'|+-#u'nutes'", "+1minutes", &res);
+ test_time_parse_expect(ret, res, knot_time() + 60, "minute");
+}
+
+static void test_time_print_expect(int ret, const char *res, int res_len,
+ const char *expected, const char *msg)
+{
+ ok(ret == 0, "time_print %s ok", msg);
+ ok(strncmp(res, expected, res_len) == 0, "time_print %s result", msg);
+}
+
+static void test_time_print(void)
+{
+ char buff[100];
+ int bufl = sizeof(buff);
+ int ret;
+ knot_time_t t = 44000, t2, big;
+
+ ret = knot_time_print(TIME_PRINT_UNIX, t, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "44000", "unix");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_RELSEC, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-10000", "relsec");
+
+ ret = knot_time_print(TIME_PRINT_ISO8601, t, buff, bufl);
+ buff[11] = '0', buff[12] = '0'; // zeroing 'hours' field to avoid locality issues
+ test_time_print_expect(ret, buff, bufl, "1970-01-01T00:13:20Z", "iso");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_HUMAN_MIXED, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-2h46m40s", "negative human mixed");
+ big = knot_time_add(knot_time(), 2 * 365 * 24 * 3600 + 1);
+ ret = knot_time_print(TIME_PRINT_HUMAN_MIXED, big, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "+2Y1s", "big human mixed");
+
+ t2 = knot_time_add(knot_time(), -10000);
+ ret = knot_time_print(TIME_PRINT_HUMAN_LOWER, t2, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "-2h46mi40s", "negative human lower");
+ big = knot_time_add(knot_time(), 2 * 365 * 24 * 3600 + 1);
+ ret = knot_time_print(TIME_PRINT_HUMAN_LOWER, big, buff, bufl);
+ test_time_print_expect(ret, buff, bufl, "+2y1s", "big human lower");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_now();
+ test_diff();
+ test_diff_ms();
+ test_knot_time();
+ test_time_parse();
+ test_time_print();
+
+ return 0;
+}
diff --git a/tests/contrib/test_toeplitz.c b/tests/contrib/test_toeplitz.c
new file mode 100644
index 0000000..244137c
--- /dev/null
+++ b/tests/contrib/test_toeplitz.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2022 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 <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "contrib/toeplitz.h"
+#include "contrib/wire_ctx.h"
+
+// Test vectors come from Intel Ethernet Controller X710/XXV710/XL710 Series Datasheet
+const uint8_t key[] = {
+ 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
+ 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
+ 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
+ 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
+ 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+void toeplitz_check(int family, const char *src_ip, const char *dst_ip,
+ uint16_t src_port, uint16_t dst_port, uint32_t expected)
+{
+ uint8_t data[2 * sizeof(struct in6_addr) + 2 * sizeof(uint16_t)];
+
+ wire_ctx_t ctx = wire_ctx_init(data, sizeof(data));
+
+ struct in_addr src_addr4, dst_addr4;
+ struct in6_addr src_addr6, dst_addr6;
+
+ if (family == AF_INET &&
+ inet_pton(AF_INET, src_ip, &src_addr4) == 1 &&
+ inet_pton(AF_INET, dst_ip, &dst_addr4) == 1) {
+ wire_ctx_write(&ctx, (uint8_t *)&(src_addr4.s_addr), sizeof(struct in_addr));
+ wire_ctx_write(&ctx, (uint8_t *)&(dst_addr4.s_addr), sizeof(struct in_addr));
+ } else if (family == AF_INET6 &&
+ inet_pton(AF_INET6, src_ip, &src_addr6) == 1 &&
+ inet_pton(AF_INET6, dst_ip, &dst_addr6) == 1) {
+ wire_ctx_write(&ctx, (uint8_t *)&(src_addr6.s6_addr), sizeof(struct in6_addr));
+ wire_ctx_write(&ctx, (uint8_t *)&(dst_addr6.s6_addr), sizeof(struct in6_addr));
+ } else {
+ assert(0);
+ }
+
+ wire_ctx_write_u16(&ctx, src_port);
+ wire_ctx_write_u16(&ctx, dst_port);
+
+ if (ctx.error != KNOT_EOK) {
+ assert(0);
+ }
+
+ uint32_t value = toeplitz_hash(key, sizeof(key), data, wire_ctx_offset(&ctx));
+ is_int(expected, value, "toeplitz_hash: %u", expected);
+
+ toeplitz_ctx_t toepl;
+ for (int i = 0; i <= wire_ctx_offset(&ctx); i++) {
+ toeplitz_init(&toepl, i, key, sizeof(key), data, wire_ctx_offset(&ctx));
+ value = toeplitz_finish(&toepl);
+ is_int(expected, value, "toeplitz_init to %i: %u", i, expected);
+ }
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ toeplitz_check(AF_INET, "66.9.149.187", "161.142.100.80", 2794, 1766, 0x51ccc178);
+ toeplitz_check(AF_INET, "199.92.111.2", "65.69.140.83", 14230, 4739, 0xc626b0ea);
+ toeplitz_check(AF_INET, "24.19.198.95", "12.22.207.184", 12898, 38024, 0x5c2b394a);
+ toeplitz_check(AF_INET, "38.27.205.30", "209.142.163.6", 48228, 2217, 0xafc7327f);
+ toeplitz_check(AF_INET, "153.39.163.191", "202.188.127.2", 44251, 1303, 0x10e828a2);
+
+ toeplitz_check(AF_INET6, "3ffe:2501:200:1fff::7", "3ffe:2501:200:3::1", 2794, 1766, 0x40207d3d);
+ toeplitz_check(AF_INET6, "3ffe:501:8::260:97ff:fe40:efab", "ff02::1", 14230, 4739, 0xdde51bbf);
+ toeplitz_check(AF_INET6, "3ffe:1900:4545:3:200:f8ff:fe21:67cf", "fe80::200:f8ff:fe21:67cf", 44251, 38024, 0x02d1feef);
+
+ return 0;
+}
diff --git a/tests/contrib/test_wire_ctx.c b/tests/contrib/test_wire_ctx.c
new file mode 100644
index 0000000..81386c9
--- /dev/null
+++ b/tests/contrib/test_wire_ctx.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2015 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 <tap/basic.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "libknot/errcode.h"
+#include "contrib/wire_ctx.h"
+
+#define OK(wire) { \
+ is_int(KNOT_EOK, (wire)->error, "check for no error"); \
+}
+
+#define NOK(wire, code) { \
+ is_int(code, (wire)->error, "check for error"); \
+}
+
+void ok_offset(wire_ctx_t *wire, size_t max, size_t i)
+{
+ wire_ctx_set_offset(wire, i);
+ OK(wire);
+ is_int(max - i, wire_ctx_available(wire), "get available %zu", max - i);
+ OK(wire);
+ is_int(i, wire_ctx_offset(wire), "get start position %zu", i);
+ OK(wire);
+}
+
+void nok_offset(wire_ctx_t *wire, size_t max)
+{
+ wire_ctx_set_offset(wire, max);
+ OK(wire);
+ wire_ctx_set_offset(wire, max + 1);
+ NOK(wire, KNOT_ERANGE);
+ is_int(0, wire_ctx_available(wire), "get available %i", 0);
+ NOK(wire, KNOT_ERANGE);
+ is_int(max, wire_ctx_offset(wire), "get last start position %zu", max);
+ NOK(wire, KNOT_ERANGE);
+}
+
+void offset_test(void)
+{
+ diag("offset operation");
+
+ const size_t LEN = 3;
+ uint8_t data[LEN];
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ // First free byte.
+ ok_offset(&wire, LEN, 0);
+ // Last free byte.
+ ok_offset(&wire, LEN, 2);
+ // First non-free byte.
+ ok_offset(&wire, LEN, 3);
+ // Invalid offset.
+ nok_offset(&wire, LEN);
+}
+
+void skip_test(void)
+{
+ diag("skip operation");
+
+ uint8_t data[3];
+
+ // Forward skips.
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_skip(&wire, 2);
+ OK(&wire);
+ is_int(2, wire_ctx_offset(&wire), "skip by offset %i", 2);
+
+ wire_ctx_skip(&wire, 1);
+ OK(&wire);
+ is_int(3, wire_ctx_offset(&wire), "skip by offset %i", 1);
+
+ // Out-of-bounds skip.
+ wire_ctx_skip(&wire, 1);
+ NOK(&wire, KNOT_ERANGE);
+ is_int(3, wire_ctx_offset(&wire), "out-of-bounds skip by %i", 1);
+
+ // Backward skips.
+
+ wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_set_offset(&wire, 3);
+ OK(&wire);
+
+ wire_ctx_skip(&wire, -2);
+ OK(&wire);
+ is_int(1, wire_ctx_offset(&wire), "skip by offset %i", -2);
+
+ wire_ctx_skip(&wire, -1);
+ OK(&wire);
+ is_int(0, wire_ctx_offset(&wire), "skip by offset %i", -1);
+
+ // Out-of-bounds skip.
+ wire_ctx_skip(&wire, -1);
+ NOK(&wire, KNOT_ERANGE);
+ is_int(0, wire_ctx_offset(&wire), "out-of-bounds skip by %i", -1);
+}
+
+void clear_test(void)
+{
+ diag("clear operation");
+
+ uint8_t data[] = { 1, 2, 3 };
+
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data));
+
+ wire_ctx_clear(&wire, 10);
+ NOK(&wire, KNOT_ESPACE);
+ is_int(1, data[0], "no attempt to clear");
+
+ wire = wire_ctx_init(data, sizeof(data));
+ wire_ctx_clear(&wire, 3);
+ OK(&wire);
+ is_int(0, wire_ctx_available(&wire), "no space available");
+ for (int i = 0; i < sizeof(data); i++) {
+ is_int(0, data[i], "wire position %i is zero", i);
+ }
+}
+
+#define check_rw(size, value, ...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t data[sizeof(expect)] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ \
+ wire_ctx_write_u ## size(&wire, value); \
+ OK(&wire); \
+ ok(memcmp(data, expect, sizeof(expect)) == 0, "write %i value", size); \
+ is_int(size/8, wire_ctx_offset(&wire), "write %i offset", size); \
+ \
+ wire_ctx_set_offset(&wire, 0); \
+ OK(&wire); \
+ \
+ uint64_t num = wire_ctx_read_u ## size(&wire); \
+ OK(&wire); \
+ is_int(value, num, "read %i value", size); \
+ is_int(size/8, wire_ctx_offset(&wire), "read %i offset", size); \
+}
+
+#define check_general_rw(...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t data[sizeof(expect)] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ \
+ wire_ctx_write(&wire, expect, sizeof(expect)); \
+ OK(&wire); \
+ ok(memcmp(data, expect, sizeof(expect)) == 0, "write value"); \
+ is_int(sizeof(expect), wire_ctx_offset(&wire), "write offset"); \
+ \
+ wire_ctx_set_offset(&wire, 0); \
+ OK(&wire); \
+ \
+ uint8_t d[sizeof(expect)] = { 0 }; \
+ wire_ctx_read(&wire, d, sizeof(expect)); \
+ OK(&wire); \
+ ok(memcmp(d, expect, sizeof(expect)) == 0, "read value"); \
+ is_int(sizeof(expect), wire_ctx_offset(&wire), "read offset"); \
+}
+
+void read_write_test(void)
+{
+ diag("read and write operation");
+
+ check_rw( 8, 0x11, 0x11);
+ check_rw(16, 0x1122, 0x11, 0x22);
+ check_rw(32, 0x11223344, 0x11, 0x22, 0x33, 0x44);
+ check_rw(48, 0x112233445566, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
+ check_rw(64, 0x1122334455667788, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88);
+
+ check_general_rw(0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x10);
+}
+
+#define check_rw_over(size) { \
+ uint8_t data[1] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_set_offset(&wire, 1); \
+ OK(&wire); \
+ \
+ wire_ctx_write_u ## size(&wire, 0); \
+ NOK(&wire, KNOT_ESPACE); \
+ is_int(1, wire_ctx_offset(&wire), "err write %i offset", size); \
+ \
+ wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_set_offset(&wire, 1); \
+ OK(&wire); \
+ \
+ uint64_t num = wire_ctx_read_u ## size(&wire); \
+ NOK(&wire, KNOT_EFEWDATA); \
+ is_int(0, num, "err read %i value", size); \
+ is_int(1, wire_ctx_offset(&wire), "err read %i offset", size); \
+}
+
+#define check_general_rw_over(void) { \
+ uint8_t data[1] = { 0 }; \
+ uint8_t d[2] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_write(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_ESPACE); \
+ is_int(0, wire_ctx_offset(&wire), "err write offset"); \
+ \
+ wire = wire_ctx_init(data, sizeof(data)); \
+ wire_ctx_read(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_EFEWDATA); \
+ is_int(0, wire_ctx_offset(&wire), "err read offset"); \
+}
+
+void read_write_overflow_test(void)
+{
+ diag("overflow read and write operation");
+
+ check_rw_over(8);
+ check_rw_over(16);
+ check_rw_over(32);
+ check_rw_over(48);
+ check_rw_over(64);
+
+ check_general_rw_over();
+}
+
+#define check_ro(size) { \
+ uint8_t data[8] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init_const(data, sizeof(data)); \
+ \
+ wire_ctx_write_u ## size(&wire, 0); \
+ NOK(&wire, KNOT_EACCES); \
+ is_int(0, wire_ctx_offset(&wire), "err write %i offset", size); \
+}
+
+#define check_general_ro(void) { \
+ uint8_t data[8] = { 0 }; \
+ uint8_t d[2] = { 0 }; \
+ \
+ wire_ctx_t wire = wire_ctx_init_const(data, sizeof(data)); \
+ \
+ wire_ctx_write(&wire, d, sizeof(d)); \
+ NOK(&wire, KNOT_EACCES); \
+ is_int(0, wire_ctx_offset(&wire), "err write offset"); \
+}
+
+void write_readonly_test(void)
+{
+ diag("readonly write operation");
+
+ check_ro(8);
+ check_ro(16);
+ check_ro(32);
+ check_ro(48);
+ check_ro(64);
+
+ check_general_ro();
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ offset_test();
+ skip_test();
+ clear_test();
+ read_write_test();
+ read_write_overflow_test();
+ write_readonly_test();
+
+ return 0;
+}
diff --git a/tests/knot/semantic_check_data/cdnskey.cds b/tests/knot/semantic_check_data/cdnskey.cds
new file mode 100644
index 0000000..6ce5610
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.cds
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ dEDk41MHSAAoc2eboWOXxGQHYFj1gXuD/gfX
+ Qz6HEq44narP0IHuOWt4ni9HUhYDBuanPp7S
+ j/8nYnZc6gdpMg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ 1HFpOHudUJp7hvrsTmdX6qt+X0I4K9RYo/Uy
+ gpWbJBNhNsPVENVrw8AabhnPaETJGbreS/4T
+ slgbxM1Ks/erzA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ EA9rtC9Ub4LPDwS6Q8wE4g9nGddbVrg9ivHN
+ oHQzUjTFlxtn8gFPaJkUfHwqwg3PsSVGagyx
+ Bjsool21k/TG7A== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144147 25752 example.com.
+ YLQPkC55O9bpQI/Hg/Ih91UkieeM3wtQvJMT
+ ro3QJ2eDImSyeoIbWsF+ghtoQ+6IUulXLu3k
+ PtDViOe2tfaL/Q== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ 1J1lDp/FQFgAGv7EFeDTAru7rUIcUCc7bkYj
+ 8OlczfdQjo9IfS5MFg6MqIrE/KPC18CDX1Ki
+ DzaCFaMGDlavjQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 25752
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ hRcbHnvrTqCb215+XsIn96tvHacV5d15lcnS
+ h91pg8Htes3H0vOoG98C5oWXoj7RM4V/tDoH
+ /0ahiLyRzRnvBA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 20197
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ JLKC5uLW1+JPkOyVcc8D6B6lCC/0FOlak/Qd
+ Na6Nb33hi9io1HMFI1eYiG7u7lxWmXsKnBo9
+ ONROz+WYGds++Q== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ pgi1+O/TWU6WCmLLYEibCYj+RzbcOuodnF1i
+ wlBQxDZLTcGYG+1KEC0spZTN1nQncEfdeEKc
+ jnYQUa0izPQRnA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ MaFyQcB908WIXS+RiLeLXiKdjOo/R6tl9AM/
+ 6xokhcvRqQzuyQeoH4snUvcht0m5ghz09Km7
+ MPN0uzJcXIGg0Q== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 20197 example.com.
+ Vdo7aYGIByxiC85dyqLKrrNAYYDFBnKXm8uE
+ rYSXBMWiQoFHwzvlavyqhUWlEABfvYD0pUrX
+ PZ27Hz8rPFCSLQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ 9Llt7e4nm8uMLqliT2NZJINmAmLmKDYqjloj
+ Q3/wNI4K+J0RUmWpg3f6xODVkKjjuVnwpxkK
+ eWV9zqY4jUTAGg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144147 25752 example.com.
+ lZSHyLdXGFvoL9fhk26y70ifFwui2A5bpdir
+ Su7VhfsnNdLgNuCceRXbYwxQaUyODCl7dcJ9
+ UkRzq2eDs0evKQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144147 25752 example.com.
+ dDE1XApt4lZ9u20Z/vXwhJxE27AZJQzKwLkk
+ jpwEDVJo6/SdV2smB7s7+qmGnSKhIehVpUFX
+ wv3/3YaFxSTifQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.both b/tests/knot/semantic_check_data/cdnskey.delete.both
new file mode 100644
index 0000000..b3b840b
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.both
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ uHjgn9WEMdw/d//q2ZhGF1GAQItK9UPyByET
+ VDuZgER/JBHuFd1/MMEkkFmCRneXuVudSnki
+ aXiza0GLV0ujfw== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ 39YAhtx1qe9sbJ/6N1fS7F4QLS9iqagdbQN4
+ w6VRyMRrseRY16G2n3Th9yw1+R9aXOazb6iP
+ BL6azQJiUCZJ5g== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ EXv3vV7Njpz59INdubRpDsGANROKfEhqBzQ8
+ zSL1vujpUOdaZWqmS3uoKusxHCghJacCFeUA
+ KQNrWNuZHT2S8g== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144234 36859 example.com.
+ LgXpsIgBZBO03iU6D2nqsbmal6AK51ev21Cj
+ PQFfFBLQ+ARqyE3k7mlTK4A+/UfIpWgpkKnz
+ St4SbtL3r6GK+g== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ l/Uak3BSxeoEO8n42GtZkS1aTdEV590rAuwS
+ Jvt8Gzyj1S5Aqx5Tytm+nb93ZtO3eSL2OpJg
+ p7tdmPjtHKxYpg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 36859
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ jNkK9sXUo8jTJ2snaD+3Mao2q0m5UjyZ7ykD
+ 6yQqTJ2xgldvTCyuu/YlSCoR9gli8pOGz+KT
+ 3YA9HjG46ob8ug==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 65430
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ id6EVGBrg2vZm6vIIGNhSukuI2Uv6/MzZiJk
+ C1N9k5P3zAP6Es9aLp9m4cR8qGIdUu3DZ3AU
+ ngKndEZvk5YUUg== )
+ 3600 CDS 0 0 0 (
+ 00 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ mDmiCviPRxQ1BiinR2+/lQ/KabHgIu/LSKZ2
+ yZFsgiF8YF4IT8mJc/qiKVtaCWLK4Sszxk/F
+ P8kMTmTKORT40Q== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ O1KH8u+VPLnd5TwGPRbv7VpMss+Mjwr+nIOE
+ UxSS7unksPUldU0e9qXby0fydlN5LTf/L0sD
+ daMwGOA2fuD/dA== )
+ 3600 CDNSKEY 0 3 0 (
+ AA==
+ ) ; ZSK; alg = 0 ; key id = 768
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ Hj8WJNT51BdqA6szAI7sn8gZftHY6/1/Y7qQ
+ DRsunh1J1cNRuqHtLBnRKpVdteZ4znNKnavb
+ uoC6kzSzbRiJzQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144234 65430 example.com.
+ 7YGVqSgaiHXwY+GdMkUJXZyqkGvkfA8LliB6
+ 6Nn4AvuETs4lX080MNq3dWmjI/tHSg5ptQz7
+ Hukvd6cYWNgtBQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144234 36859 example.com.
+ SVatJA8FhwAotw625XttyhgD8Rcp4ukcidii
+ By06YX9e5rCgOHOvjsHwA57kBBzcZg0ZXAbF
+ SOhDdUQibKaRSg== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144234 36859 example.com.
+ D+r82Tvm8eGuYrJKVCUMw1Gz+tevXwE2IGoG
+ 7pXErKbDv13p/eFAPsRdUKtdmsOq4mHSxQuZ
+ GVGAULfJjcs3pQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey
new file mode 100644
index 0000000..366edaf
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cdnskey
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ wXvCukXPMbON0oD2nKINzyauQRgeYE/kIYKZ
+ pYaMwV5Z6yZ9SKSSy7oRBn7t1+rOmGI69NSx
+ 3WHXaRiLjcH1Sg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ XNdl4tiEhUPOpEgwGO2njssc8QMB8IeP5QDM
+ 9/LZJUPZ0hZ76F7fX9C3X3edgysEoDFR1HAE
+ JdTxkJ5Oqv7Xig== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ Or2a9ZLl2FnBmNM1KbUcgAjgLKRS6O9H4XmK
+ VAGM3QxutaTZuF1sjsz+kNh6yrT38eLm5B8M
+ PLCxUmkTSUmgeA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144623 39533 example.com.
+ 5SBXb1HpSfhPinO3hadK7E0lhRHwyUAsjZpy
+ /7jTO7/uUNXD6asY9V6kvOJmRgMpSeXFJKFw
+ +Vsyx0jifistyg== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ TQSEqjdF8egQ1YjZPdVXrX+pngPHTdCgwJFR
+ AefWVHOLsMADS3/LL5G+pZTSldB3j3Xo4Na/
+ 1tsuCgNmV+58xA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39533
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ VARBBNSEYzAbBYxgdQi/epYgWFaGnL49509p
+ CeZWg4LO4jhjVT7uyhsSQny2wyahP2Y37YeO
+ d+sY503BNpqzMQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 59324
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ YRhAwruTjWmu6drb4+iJ/QOwQg8dnGur8LH7
+ bsn1ZCHQYNDHiIai8JqikqzkhEYKIK8HIqT8
+ F2RY/LqFxKebjg== )
+ 3600 CDS 0 0 0 (
+ 00 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ cHTGBug23nTe/aS09JaakuG4wa9EEbWxL3gu
+ LQpCK8HV/JMsNSGqh1FsUlX92y4tSIvJn+Lx
+ vvdN+Qzh+zASHg== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ GU9Q/CipUscofDL6uhT2ZmhQoyApLX9zbyfN
+ dG5XW6sXYaB94hVSiT2DSyt19fyQwYoKK2Br
+ fJwy4pI890kKoQ== )
+ 3600 CDNSKEY 0 3 0 (
+ BA==
+ ) ; ZSK; alg = 0 ; key id = 1792
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ CXeUfFxa7aT2tivKLovVQ2CA0HYZxxlUrbm1
+ voABTNkU7lb5W9Z7GQ/VDugd8QeKNK8YWOaQ
+ Tdl79jkL1rQKXw== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144623 59324 example.com.
+ sd+fzJmLLIoFIcbKCJ+rHE+tOs0PwHjjY9ml
+ Dsbel1k5sANI4xR8iMv6YAEhcpvb0S+8Nd7h
+ 7BT45SkKVtyFsQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144623 39533 example.com.
+ VGa9LkgVATBLHOwMBNc6g74iXCCSXnWWNs8O
+ ndoXk4ZMMRRkmaxSWXH2pBdJLZPL5f26aEVl
+ 4toVcsE722LoFA== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144623 39533 example.com.
+ i+94RvIQBBEOza7Y963huNEWYrqt/VT/eE1E
+ Gqx5kngvZgZ7wO8tcOsaE7ctb69SvgZwRR9c
+ RBgb2N6ezo9OxA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds
new file mode 100644
index 0000000..9d63eb9
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.delete.invalid.cds
@@ -0,0 +1,113 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ 1CRyeUic9BIwBWcjk95VQJktQng6f3dLQm64
+ JwGGqivUM3Hgp7URguNIx0BsCvfo67NIpk7N
+ mMIFwMkMGOHmgg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ pB4+Z3ltuzY+/NkAeCb9LOS7Zlh7QLfHKimR
+ JPtvdOuIhd8vB0NZLzcYX0lIkrqyP3LadbrS
+ u8r9BMIlu4cKpg== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ x8XhP7r3/glI7AenoSLVmfqhZXQfj6YllgxA
+ jkVxExiM9OJZOPdyeDTuRyUD1PFiBOEsP7Wu
+ vNgWA9eyQFOslA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144646 56106 example.com.
+ TCn7V7sHR2TNY5ywyEpbYZMegZwTX+I/TPeO
+ 76D3WORu9pN0kJWjGPAebwTvL/a7p8xS8B9U
+ X9ivUVFORG+mJA== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ cOjtacSzGkoh6bO4clqYPM2y+g5ezQUtCNdx
+ iRqickHCvQnL9OM/h7V8txqEsSulG5ZCeW+O
+ LDhDQDUchpNv7A==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 56106
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ pB2mCNXFJ8e+UaMeMmy1LSCv6TJ92Fs3kFxY
+ I8NyZPyGvfePpMlzWZr7Bw7wS6G6Jhayhj94
+ MMJ4lM/5+ZzVJw==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 45911
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ uOAPEzDkPNI9Uo2N+iiRkIb2p1Y0VhgqwUom
+ +Dssd6X0CEdQEmD8YQ43Cuq9ZNwk8Bm+lgm3
+ X+ImdIKeE4MvNQ== )
+ 3600 CDS 0 0 0 (
+ 01 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ IN5tLpm7OKjIL4VpucR1ero1Gv5UEyVqjzB9
+ rRJefwUtlZFKNaTbU0oQD33vQXEjUiIMr66b
+ zIC3Ju/YtYFDLg== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ f8VJa9GRwSWNmg0AR4nA3OD4X8im7BriZjME
+ 2ypYUOJkdIafolyb0LDz7XWTaVsFHQWO0z+J
+ 14g0CgCroTm3pQ== )
+ 3600 CDNSKEY 0 3 0 (
+ AA==
+ ) ; ZSK; alg = 0 ; key id = 768
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 45911 example.com.
+ 89oeIQuH82i2RYIj/fnX/71s8kspDHcI8lIa
+ R02OZZ9bF37bi6LbGkypdXpmxN9/rEjk4ThF
+ IHRX2USEPtl+wQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ Hgf4SgtoV0IHsF6feSP8YqeibPTtwZelLpLs
+ hux/D94MFKtYa6OseyzT3qIDdixav+mlI2ud
+ 0JyflYZ6MCBlxg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144646 56106 example.com.
+ XdhVQ3Na3LsvdtT2HwdsM3ItiD3UH0HO6TZD
+ W6/jy8r0NA6fTN4b4oVr6wSqHAQIQVYUbWER
+ 7pav2Ek03LDa0Q== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144646 56106 example.com.
+ dVTxTNAfZy5sa0SW8eme+KMx3hByBnPIrRlF
+ zGDsGN1Xzw3OBhsTmuOwhbnZSnnvdBrhBOJw
+ 8eU/6zpcZypyFQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.invalid b/tests/knot/semantic_check_data/cdnskey.invalid
new file mode 100644
index 0000000..6937db5
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ fIUb0+hjrELDVphcGgDZemNVpq1TBgyTt184
+ 9YnzaAhADynsscEd5iZRjuA5r7mlI/M9fFtU
+ l6wpEmqAs7sG5w== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ 86HnJEU3jP+bL9JmnY+2TGwna7DGtUVvgdhu
+ slzGQWN3EHb51vx1fHQGGfQlJ4P4ch5US3TE
+ 1rd/OKNUBE+p7w== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ 33SrrSRr8KwasK7qfxYAPxP//dj8Y9i95oza
+ 2Fwvt23QxfZS3TBLqMyMA6G/nmXyavUxsye8
+ C+mks7QsS7HJCA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144725 7800 example.com.
+ WRb17ehBEEjIVl//Zw8vtDmbnTY6eLWe2KQ2
+ +E+pCMEK0QE1qXwcethJ9PkM+gKFmN9RscXH
+ DjrmWIAfgndjsA== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ VxRHPS89GaMJvJ1xL8/HulwW75tDXUZ6nYlI
+ 8VCFOMB7vU+SoZhaaoZu4YcCZqzjzfZLl8Lt
+ SEaXZPQbnpkhyA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 7800
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ MWndPmlRdffYHO8Z2quMkXq80Nm3PNmWpTix
+ xJLJ71Oph+ta4XaTuiza6AQgVkCSzrfwoTuJ
+ UKHL13s4/IrRGg==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 46605
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ GRVgc202uXoxu8f36V/Tc4r9BzCKK07SCmS6
+ MCJ+mXO7PCv4RIzN9Dp8t6sVuDb5smLe6cV6
+ 5lgyPYJwr1TVJA== )
+ 3600 CDS 53851 8 2 (
+ 668159D684EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ r+OpHWsZ0enCPKtUIZFXSb/8YbLdfYb3Ihpt
+ n/5kAWbOkkkVzAJX2/sCrVExMCVcP/nFSIIf
+ hACGKBjTvuLFLA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ buNL2/GqYvtwcXMPSiOeaEB5L6r5InyVxzaJ
+ 1PaaJigmJHbdNKGFl8ijDiH7WBdQECb8M3oU
+ zeuWGebSLuy0AQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ wYB3zuX5/bt3Pg2nz9F0j6MK1bkY19QvDcRb
+ pk/0rHXLbSjTepbIwy8O0KbJndHy+a70fN5p
+ 3dBGN5J56KymFg== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144725 46605 example.com.
+ pXWJCUC0kKqWpjZetDhGJLNPpXGqc8sJZ9wY
+ HKs4Sd734p+Gr45vnJ94pGYjjtZi9bwPo2nF
+ DmFP5K3NLACG+Q== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144725 7800 example.com.
+ Khv6ptUd4l4SgJI/H+L6Ls/gQHnmmQJcg0fB
+ xv7zECmQfQFguVIJ1bmoz4jP26ejsNH1pG+o
+ Wz9U7I5oWsDzYg== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144725 7800 example.com.
+ z8omQAty9S0cNyFATnM8DZ+RbMly/7staAmc
+ RF+PmOp/E7FtdKOZe5+ega/+aQV9VpePYXMA
+ UwmIeeYYU2pAJQ== )
diff --git a/tests/knot/semantic_check_data/cdnskey.invalid.param b/tests/knot/semantic_check_data/cdnskey.invalid.param
new file mode 100644
index 0000000..2814ddd
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.invalid.param
@@ -0,0 +1,123 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ tBomI7xR670RBUw9IjNL2A5eMVKtYqDUdhiq
+ XJI3CFdb4j6plfdUF75SfaiCP70aLX8Atzxm
+ 2RAzpR6M2Q3gbQ== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ 8mHEeq/7fnXpM/CaOFsIqTKyTrixQVZr8V+P
+ Lwn641YbbKniEP+KacrJ7Ul2jt2jCT2cnxC0
+ b9XicHENmd0phA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ f8ZdC3vD/oIltQLyL4zBmwo9rRyijN183BGw
+ L6iZ6DnH4BASlUyrGa0IceRH4yD5pP+gnhCc
+ lBzWFgvtEIyPPQ== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144756 33730 example.com.
+ UK+oQx75Gdn82LKBzht8KxrtwPE5JCBhEMcR
+ hRhHTeMqRUjbbeEOSWRdjg/36329yNYrxC60
+ l7bBcqolo9dDmw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ kr0M2egbhUXhH0i6fYiSl+zRH1pU7XhamCdO
+ nPhMEgFa3CsGp61kCuZFulpY0ODh8WrAPZcO
+ qC0tCj5Bz7nWZQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 33730
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 7fs6TMYYlkkxI1PCunVT9dxcxWVGXu1N7xVv
+ 2EUyVYMXSn/Z04URNTaxXcoWuDafy99G8rcT
+ oPycl2oOhc+s0w==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 60664
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ WpjHrB8ZfAhOSjq79gAaPEiQgSxvEatTi8nC
+ AYYpGs4dc1n54iYZ4IjCfMW/etlkZsMzXbVE
+ s6t+Dj/gJ3JKZg== )
+ 3600 CDS 53851 4 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ IAgBYDhTIYQvmF2vUy72TWoRlPJQGyGErJuT
+ 0xxZDStaSfoAVM3Hr6VEqIq7R3B+Xel/urDM
+ WYUbIAinEnvpOw== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ IpzPg5fx+O1HUqjN0lR1Bbo6Zx/Lq1wrrJvv
+ Y518ooGelg8Q2wH7NgScsyhLY342+MHk0fKX
+ RcxRzfaFohiEZg== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ WMqSVG8Tcq7e5E2y8oHThr6Ip7ASu/35m10m
+ TzsEANrlFf0e1Z6XG5ca/6//NSolXoTu6jBx
+ 2kvnsX2bA222PA== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144756 60664 example.com.
+ LLGAWxuAhlKM/3i9+FFGngy6Zqo6NsxdXScR
+ wgVe3Ilw+3vU/Nih70uRE/xUjZpfFBOlMEk6
+ EBSf/DJr6awY/A== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144756 33730 example.com.
+ mpcGxsR9c/K6wuaJCeFds1kg0af6Xj8K24o6
+ FHzqn60w7HXXNnDjxS0jPTHpaVUkWhuKUcCR
+ 9EcvMW7uwVfULQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144756 33730 example.com.
+ gLwhcu1t0qloiWb5/XHuv0PAQZ+ChmDdMuMS
+ qS3hi0VPk9cscMjd7ZH7shJBH+9KKMI6YbMz
+ VGU4MSCj5/kT0A== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nocdnskey b/tests/knot/semantic_check_data/cdnskey.nocdnskey
new file mode 100644
index 0000000..a7bac63
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocdnskey
@@ -0,0 +1,101 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ 0JDLQ/bZj4SSmqvLPAzt1v/UUb8mfJQnuLC9
+ B1CL4oRD45Hw00KgmbE7xgJVflYZJxfx7KIw
+ ydsB0/1/dMJzbA== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ Mnk/oSM7sdAhGYbWUMLpYFR1ahcvULo/8z42
+ giRwzAX8HiqvxxkqRCFbvzYeRkZLLw0fYTeR
+ Mqit0zQuWuc0ow== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ qPQblbJyzHdmhqYhYx4wfUHWe3SYGUA65hZR
+ UFYcx99Vhs1CXUobjCk9NBedRbBHR04kQ5Bo
+ /72fhuCPJFIC1Q== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144854 39620 example.com.
+ H5So5m0YdxOBU3k0+pi6KOgPNF2V4hU+GLxa
+ c0JdGnALP4Wz6lWCdMRPXIaMjImb3TK9vFti
+ 89lB/2MMDe4dTw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ 6R8b9KzH06NQ/4AUqrmp8rFmY0AmHpbW/vhj
+ xLul6ON720xvdeKBzi0nLSeTdUO8/gK8s8jh
+ RmJ8Fw279eXXZQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39620
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ hfwsa6JnfqjMRma2PlO+gt8qqLytVIygLZHB
+ 5APAuz2cheZCMD8A2kyt5NziCCj6szmCK4oZ
+ fColPGaDgYtpmA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 6821
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144854 6821 example.com.
+ UbEQQoX5j1FVOqpkQBqckaG4WnCd7+4dBJax
+ 5sgjHQnfSSwKGfJx0zxd3ZbPCEKj+Ymrhpsm
+ nqfPzVRZhUPKuQ== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144854 6821 example.com.
+ Sc/K9xI1C9rzujnllO5o7sKoJiEKFUEfPxt8
+ gsxs3sb9Q1s0/uSocrPc2OcaLgEzuFGS5FzA
+ fg7HcgZN63I5TA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ ykGu61Yjp24MJjp0wIYV20LSQ9ovRHT0zqp2
+ CSvlROIVpbUGlNjAAKJdWwYJAqNUD571gJ7E
+ TkhrLEIX02ySqw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144854 39620 example.com.
+ ye5pM/p8OWbdRNhLfbfWsY6lG8lr0Ae80LKv
+ rVOCMhAowrtKmDL6hUByovCV7MjCIYwGM26C
+ Vl9CRmrWwJEULw== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144854 39620 example.com.
+ JHP3TuxCuZ+N0lWtRI7Xl0qIcHSrn/X+WDUr
+ 0cVBfQTsFrAZs14bJhvw0zMGgONAgnFsXlxg
+ QmAqIPmpRvKtnA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nocds b/tests/knot/semantic_check_data/cdnskey.nocds
new file mode 100644
index 0000000..ecb3188
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nocds
@@ -0,0 +1,110 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ GDfM/H4m+FRVp3M/KsOv//eMFaL1LnyrIi8O
+ pUSht1KyYDRoVqSL72XTy1aAJJ49Sd0uq+4U
+ acekI3Xi9OpvXg== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ ICtOUMZr415dJb22HWsrjbYfW7q6hh6gxD2i
+ EikMQAkPncdBHHd7dCrjy1/4CPhixn/BnDfV
+ ZwF87k2Sa7EV8w== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ IokJy9LCiCaOPsluuBKYnwkesiPwsU/KZdA9
+ jK25UmdfD1uU8AA63OOciTZQSv9NI+Q4nzl3
+ LyqkRWFKToMz9A== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144849 42608 example.com.
+ kuhtgHhoeIwJ8IG08x+Tp5M7kQ+LzWoH/hTs
+ V17ZSyPD06YvMEmv9vdB+ATLd+j3uNYnMd4n
+ HW7Jh/ocOWg6+Q== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ p9BANIrBFV9hX2qwbzydeiubQkm9qstpzvUe
+ OFMDOEyyQxI+8s2nfHI76KmRliHuM7fOM9B5
+ e8wNmEeVd9JJmQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 42608
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 5sv4MetMS4KWSgyzvn658Prs0A8tLaWFhRJD
+ E9IznhGY2ogp8Z/uSIqh8QWzf1kQvfDUQiav
+ kOx4CNa3dSx/ZA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 8616
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 8616 example.com.
+ DeZBLj99QbyGhalCZ4UOmBJO/RLNgrPsAdaW
+ swYSg18lvE7jmLn9vxkUVZu0G6z43tulSb+a
+ lQT8m+U+PlusNA== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 8616 example.com.
+ IlysaALuak04Zbh0+104PHAuQgnYDBTLpvz+
+ BgirzX9Vp+pg4yZVelAXsaDbcj2ZrXrwBjpo
+ +DHj53HmZygj4g== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ dJhB1Xmd3G1ueRVnFU+M4yc379LH0UrpBcNS
+ xHzjVd+vWtpNGPq03Wi3sczA9UUkXE0F5n22
+ 6ZNR5XAswf+SYw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144849 42608 example.com.
+ 3DTwpPojzX4r9ZWeKo+zmJw+2L/uqrtoAZEv
+ ncPJG0AGB9QVzjLFiRg0BV4GiDZCl2Hh4onl
+ OShOi5Nt0GXp5Q== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144849 42608 example.com.
+ 1m2PpD3S6/5x3Kkes+1JgbHtsm0xlnKrNCmF
+ xeBvCl55D98zSvs0DjfRjFowAg22nWJkvsWo
+ 3N1vnfFZpzmPPA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.nodnskey b/tests/knot/semantic_check_data/cdnskey.nodnskey
new file mode 100644
index 0000000..461e05a
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.nodnskey
@@ -0,0 +1,111 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ Tng1e4Zs8LvGZJqp75aBSX9Ci9bsncY+w8+K
+ rfYdoVe/Smq0I+Hgtygcq0Twc7llW0rwtZ8R
+ jQpbXbp+XNDi3g== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ OcKfgxtnriGsC/9wV9yI71wIVzR+71j3sZ3+
+ ZGVqAo2bWR8QRULa5g5lQpIxlayN7w6xi6vV
+ IVWY3vauy59pPQ== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ CtZFcGvbco6ZreotcmfSYl8SlRdN/JiSuoOG
+ KtdauRz9+a+xkT2k1Wy6dADfLpwHwXL8yElg
+ /LdNXKEWK96HcQ== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144347 19649 example.com.
+ 6GVUlXemDUb6W9IID4qK+PPDSizeURGJEJlN
+ Hoof218/H/k8/BLNphFIGpdhCC2jHnAx2Nxd
+ Af65dTLtt7OBjQ== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ LiJCYpav6haPA3M3GhTZ/L6wtSqS7e9mwKsU
+ TdBkZ71RS8qmXsITLz5bFHMSy7K8mCuQIdTT
+ J3cGkbguNBqgJg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 19649
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ j18Cd+0frtc1WPeWn8bwdxYd9iTe7XsqTwnO
+ W46ZpPJPGBq/n31+7/N9TRAtXulE2r+rJDRF
+ mMooK5qrWOtqvw==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 24385
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ 8O0L6xxTnGMccrMSjaG2/MtljkSOls/BIwoX
+ eUmB9nJvDQNd8jg9XtNYUGG79dmysetBrNQl
+ TohQ1BEVGTJwig== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ CLjvJJOAZVToWUQQX06ySDkKo4QO4YcN2vhl
+ JZZ2a1hA2ranrzpeE8cslGKme5lxHKr8Y1ev
+ ffWfrz8KoQVW+A== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ TaPgzUzL+fPwEUNyusjCb6OZOF3DtlMNh3eY
+ ZTvogl2eRq84NA+mfzPmh0NXqVDbsVHGHq1B
+ mJoxuMtIt4G5Rg== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ 5sY/Q/1tP9qPMAHyQVMtbFQ0gO24rofCLg/D
+ /BaXTvjp5bnWhGuv1wFbSCyEreYr072Va08t
+ JdntIC8Prt/1MQ== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144347 24385 example.com.
+ UlLhm8Nb6g0jUIs1ldjW4OedzzLXDjCllRSm
+ +6WQuBK1uA7vboyqYVvLxxyFZCxgz6xV02iK
+ eawtsKsOnlfGCg== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144347 19649 example.com.
+ ZRDwnV+YyfKPI58ASagzoCo+qWTscYZZa6j+
+ wr4axJ7jtIO6Firy4R1GlO6NXmN5vcjHAj90
+ NZ26ezRgCMCFQQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144347 19649 example.com.
+ c5ILb+AR9BIinFp6mCogN+jwR8067Fm9LT9Y
+ AWaR3pqUC4d+Qdo4pkODLkmhAaSQLJCyPyYB
+ TQ7OFkQCC49MtA== )
diff --git a/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey b/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey
new file mode 100644
index 0000000..70241ab
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.orphan.cdnskey
@@ -0,0 +1,135 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ pdj652v0OfPO/McP8sNpxoE+adY+Qim5je8m
+ TQPcudU3gm7I2L+YqU/ujX1NUOyhUAhzRng7
+ m6nfrudJebq15g== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 7/X57I7FmbSlgxxeaE3Xgoot7KxN6nxtDb0E
+ mEEZNwdLCpjgaftaXXXM3NaZ1W2sdoECCrlz
+ R4/75kqrmNpYPw== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 0tcHIXXPEKy1tpc+Of6s2hTdQ5dGh1IoIoxY
+ se9paUUfhoF2oH5Pb8HP3rNyWLiTqXh4/lxV
+ vFLi4rR5zojxLA== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144945 39996 example.com.
+ kbImDj5vgk5VG9MI+4HJ4FtwnJ4ykSbk8vNY
+ e49ibkZChGsTtIzLwdcNAmOk7w/em67FkGBi
+ oxqCj6b3G0C45w== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ R4pG7HF8CbXgbo4N6UqdSnE8CaClNUw6v/di
+ aScNknRS0eLPOKmpANe0tyiwBV1bRQyjpmxq
+ fgZ9Oxac7plIJw==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 39996
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ cJdrUmmcxe9JKHwHHAkJ8mO1J63Cm6Qoln56
+ CUya+eWuF1A3u9L3wumvY2rAXvzBpplLXeUN
+ GIN0GgLHejH6QQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 56026
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ srEjUMAQ4Z/yc22bas+P0ly30IVbZaIIlli9
+ H7avBz013fn90vDRDLiLuHAMvW++xdDJypcg
+ Sr+9I9+nv6jzRA== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ inhdpEZ+2W4EM1HSiVZdJa4xT5S319D0x3b5
+ eJpskw/EV/Rx1X87FCr8FP18iBOszsWJjQQq
+ Z66eAxIhpBcb7A== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ TA7UxWd+j6bOXKPxo3XuKlIy87/HvIPGoELS
+ WQyrON5IURgGw/2YWD0M5xw852jl27USezzo
+ pai940D3+VGeOQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ CSk6oHNIsj3XQgXpPtFOhf4dTv/Wu/vnJfJs
+ Lpc3IoApBMxrpSIzfM/c72JtjSVzjJcdo6kL
+ n71WM21CsMcQ4A== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144945 56026 example.com.
+ hsml4IaJtzvMdvaMTR3MzeCT5fMHJ46rCY0y
+ 8DTAvK7/Z6LHbF4G7yRh9ozwcyZbB006cMdc
+ 4XUFDtEPK62DGw== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144945 39996 example.com.
+ 1cNj6TJEHFxLXFYVt3RU3wC8Wz/F5bfjy8/W
+ jEJdrnVzo1ihmJWoY48e9MlvsGXnGe4+GUrl
+ HSS+2bsGOS7DyA== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144945 39996 example.com.
+ 5mtXYcvidkSnG12dZof3xSEaH2eOsV2fuBvb
+ 8Eb6XEuPfD9v5g2mweyZYrBtowEsTA9IOsly
+ 6AWT5PfZbNAe+Q== )
diff --git a/tests/knot/semantic_check_data/cdnskey.orphan.cds b/tests/knot/semantic_check_data/cdnskey.orphan.cds
new file mode 100644
index 0000000..54732de
--- /dev/null
+++ b/tests/knot/semantic_check_data/cdnskey.orphan.cds
@@ -0,0 +1,138 @@
+example.com. 3600 IN SOA dns2.example.com. hostmaster.example.com. (
+ 2010135808 ; serial
+ 10800 ; refresh (3 hours)
+ 3600 ; retry (1 hour)
+ 1209600 ; expire (2 weeks)
+ 7200 ; minimum (2 hours)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ ThTlvNtautK64IeJRxNCr5acLrRu8jXkTR3N
+ y5TlXrei2DIagbPja++4vLjhUJAcKTGndD+x
+ wgMrDpCY6pMAYQ== )
+ 3600 NS dns2.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ 3OJiG3v9Nq9OHkyysT3A6PNPRVn9sYTQkHNS
+ 6JL5BzLCQ+uYKJBCu0ZPxDlYpbYnO0HKQ7Ta
+ iZYCjm7vzqtvwA== )
+ 3600 MX 10 mail.example.com.
+ 3600 RRSIG MX 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ 9vi3n2cVyr+ghB0ql4Wc8vhpLfAuclopapXw
+ BQV328nEwftj0okcPz4Z7Iye9by4X6NDd13x
+ vzWXDKjZCSxLJg== )
+ 7200 NSEC dns2.example.com. NS SOA MX RRSIG NSEC DNSKEY CDS CDNSKEY
+ 7200 RRSIG NSEC 13 2 7200 (
+ 20601231235959 20201012144942 8996 example.com.
+ HP8iIlUO+EKFRgoHUrQWLcaX8oSGEb/tldEP
+ GcJKM+rGMeJvxXOJnjSskUm7AyRK1TKK4RqE
+ xaOHTgIz1uUkzw== )
+ 3600 DNSKEY 256 3 8 (
+ AwEAAdKraxDdGTL4HDOkXTDI1Md1UdHuYhVw
+ YkB+u2umVjTJ1H9Qb2oBryqwXI+gklnuCqrH
+ 1znkDvzGEAeHRQUCbtKbjmqErTAcRRHW3D+6
+ jsOGXzbyGCfbyzRBwsbNCLWr3ONpPi5JOWEe
+ CUJfyc/mRXcmh5uYl1JvzAM1zprtljZt
+ ) ; ZSK; alg = RSASHA256 ; key id = 48849
+ 3600 DNSKEY 256 3 13 (
+ bkP3kBcYNsUB6jpKA764AJeNBzGJjNIRPxDl
+ 2wK1O7I/bvZDILscWSMUsSRmxZuPWGLjevpp
+ Tve1UMe+dP9VIA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 8996
+ 3600 DNSKEY 257 3 8 (
+ AwEAAaulfU2biYVBiUsGwAyCXbA+gm0yWgH2
+ Z71S16R2YNERlb0he9Od28DcFd0HbaKdFnw/
+ CtX7Z2UWs6/IRu8QmHGn6SKDsLzZ5StdPsJD
+ KilfvSlEcQeqrRAncug1SnA5BogNQSD0/02Y
+ w5KDGn7ALCSYlNgOgy7l+D/urlkuxgsPWvqY
+ XnlxaIcKt96fndwmkfZ5eF+WAqxguaNcvm14
+ 6NA53wRrWx8BQbcHk1R+WcQGqFcVOlifCs9z
+ V+87QJy2H660QKqOVDgt8PF8QmRRJqzOKpu2
+ 9T+Vd1dM3zjBJ7deLaNH2E5p7Bbp1eeOCeOt
+ WpCG6XfaRmZIF3ZWVM6Ways=
+ ) ; KSK; alg = RSASHA256 ; key id = 56474
+ 3600 DNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 DNSKEY 257 3 8 (
+ AwEAAetE6qfN/GbtMmvM0PXUTyskauES2FKf
+ jqLVz7EQlfS8iAFWLi1eHjHXDkueZ1OYRzQ4
+ IBy6MIsce4XVXLQoS8njtfaU7c5NZvktH5la
+ 7JuH32KYr3PdWL5KDsUdED3GSxfNV+DbcYU8
+ 0AZxTxy6Bm6EP+DztL1dpYrmqr8JRl+qlSbm
+ LIrPemZFUEQzhiepcYMWviDUz+ixSVzjEzpM
+ CLsrNxA30Ziiq9GKA8KKlFHdAmxuNcH0TzRn
+ dpo6bu5nKyJHiREIazHVuPBEzUmHtcWETCDs
+ 9UVsbji2Z2ozqLz9cqnfYV/kOD+OZBAqvZ0n
+ /4lgdSiBtvByLCXoWEYIGRs=
+ ) ; KSK; alg = RSASHA256 ; key id = 19420
+ 3600 DNSKEY 257 3 13 (
+ 1OgEqruDg7pI2dTIRMdP9ihhdl3wFngZW9bP
+ E4jMg4ByKKoKM/C1QN4Q+BQiQDkcprwE9vLf
+ D/cLgFNspjcBgQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 63865
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ 9d2q8pWH1AftoDmPq3DNblta3oPV+6ROZmVR
+ BvjHj7xJjI27aY514C0qNkQVhioe2mhQjikO
+ gyxvkWwBV/owPg== )
+ 3600 CDS 53851 8 2 (
+ 6F8129D687EC387C948E6F4B0AC9AA01481C
+ CEBF7570AFEC582897E7725122D6 )
+ 3600 CDS 56474 8 2 (
+ 260E7ADB07D1ECC40DEE79EFF6527CF7119C
+ 0AFC1CFA5DAC1ADFE342568CF32D )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ E7iVsJZjRyGbjMUADsi9Chz74+t1W75zTPmm
+ MYVD77dkRHiEpN41MJB6Z7Fn1lNOE6f8q2B5
+ iL/3UXULB1vpwA== )
+ 3600 RRSIG CDS 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ fsMqYcBDcTBtaDEqDTYrHHivnuQKb629drhm
+ 77RFfBxFJAxlq176PzaddA++zHfWsBgIlJzy
+ VHFy3S3huuyfaQ== )
+ 3600 CDNSKEY 257 3 8 (
+ AwEAAcQ1EqTPebcJyUnpxO3Xjx6ehRtsiZYT
+ oARoJsJG12XR6Ci9yy4SCCsejtaWIFO4XVfM
+ 2BHzFWqmABtQHtN7AazXAFMLsrSE4DYbgk5W
+ mnQv5Jloi6jhhmmXwr8EOi3HR2jdG0gVq/Ta
+ x7ztNNZsflJrs3rZs2TVO00BkyyOkmO35jCN
+ bGPUwm5cW1vse137BMa7jAcMyNLPIiQubj1/
+ mJcIyzF2duvfpjBTgEmSvNcXqLfYFjK8lG4N
+ odQG8AcK0MvWqN4mxW/hK0U9nMSjhCnfzPg5
+ tjyqdheWRyhkLGjM/mR7gBhtqoSPMr+2KMJQ
+ EHYAd/AP8YgaovS8N1fJyh0=
+ ) ; KSK; alg = RSASHA256 ; key id = 53851
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ hhpJcQ4cMcq9fLNtZrTEVAMGB2bjMwcDvv4C
+ Sss9wWDBNxIVOsi4x3j/08PZTqbfmYePWtK8
+ k2R5GOOK1lpVlw== )
+ 3600 RRSIG CDNSKEY 13 2 3600 (
+ 20601231235959 20201012144942 63865 example.com.
+ xU82j/dJf8oBd1Ti2lHH0YoxBvgCQo2MOdwJ
+ yOc6fDrT/c39rCMT//VoDmmKj3SavQ92ABBt
+ 18JqxCXK7+tnYQ== )
+dns2.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201012144942 8996 example.com.
+ D3O6XOYrOT1tlCieJJvw7zys0ClqXcCvs5+D
+ qSEpKcE6RNNeJG2d3SJg95fbO+eTkw30MROF
+ ajnNh5xJ+8xsMQ== )
+ 7200 NSEC example.com. A RRSIG NSEC
+ 7200 RRSIG NSEC 13 3 7200 (
+ 20601231235959 20201012144942 8996 example.com.
+ sGBFze6wRGj8n0B8izUNHO2ufA72sR55U3OQ
+ RLYTx2XqBRvdmapMKK6QDu/6lmwqgYMbjiBJ
+ XqDLv/1RP4DisQ== )
diff --git a/tests/knot/semantic_check_data/cname_extra_01.zone b/tests/knot/semantic_check_data/cname_extra_01.zone
new file mode 100644
index 0000000..ae3f27a
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_extra_01.zone
@@ -0,0 +1,18 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111218 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ MX 10 mail
+
+dns1 A 192.0.2.1
+
+; error CNAME, node contains other records
+email CNAME mail
+ A 192.0.2.2
diff --git a/tests/knot/semantic_check_data/cname_extra_02.signed b/tests/knot/semantic_check_data/cname_extra_02.signed
new file mode 100644
index 0000000..724a8da
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_extra_02.signed
@@ -0,0 +1,76 @@
+email.example.com. 3600 IN CNAME mail.example.com.
+ 3600 RRSIG CNAME 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ IxkF8oqOEzhlZDSRBIi4448EGvQwxm0QDFE3
+ JExA4Byx2QaJvXo8LoCeyQxS/f9E6bXpXQk2
+ 4dgQxUrRZqnKEA== )
+ 86400 NSEC example.com. CNAME RRSIG NSEC A
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ iKA+5qsYA7A7JN7Df99aJnToYESjqordQgVj
+ yMS/1RVBYEGE4y3ggehzAxvc8bsNYnUwGeGt
+ vse5dMVKCcIaPA== )
+; CNAME extra record A
+email.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ DummySignatureDEADBEEFToYESjqordQgVj
+ yMS/1RVBYEGE4y3ggehzAxvc8bsNYnUwGeGt
+ vse5dMVKCcIaPA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ MT+QgXcsDzkrFgncNwFyH8lwXiOTpj1rnPgs
+ OUIOfIhyJyzT1hpozAHt+IWOPHUkKjBN1C5y
+ SwyTnlqwJtG0yw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ Mr0Gu7PUu9PsUBflhd8tMhcQ9+ve+z561/ml
+ kP6PL0MHgLg7V8KVmL2tc7+JAhSOVSpJ4BGQ
+ c9HKD15lFDFEgw== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ oEMpoEhi86OM/SdyobPEh90zF3c3FhOgv68j
+ paD5BLUsAntf3qU+KoIMb9iVglp+VTGrg0Ol
+ XdJ2D/xSMA+XHA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ WOtx+LBKbS2MOahlpDJMqgeH1TI5dZoQitmA
+ SOkDRlJgfPsiKeiaGMrnWN9xnPZOVr9MsInE
+ sKYjh6EZM1nuBQ== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224073150 31323 example.com.
+ nL4eJLv62C56wexu10DMPHqXCXSE/3vRe4es
+ 4e0e1CkY9bdj+LgLfgs7CH7UDNXFX2CxKxHd
+ mL4sp5AtaA8fnQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224073150 29600 example.com.
+ nn2dG+ORbcNQWHT87ijfOddx0SKCSE+8hAxt
+ SiQQpxAzPw13CZmnbYas8uvFFtth6U689V3h
+ rMzzZcxQEA1z8w== )
+ 86400 NSEC email.example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224073150 29600 example.com.
+ BFz1Z7dbBNgHXDOaufuCoIzGHbwyLUrA+Wad
+ QBPD9xCYkXHoHfvVOhtEeMR19Rz+fi6ottJI
+ 4AWItiobBC/DAQ== )
diff --git a/tests/knot/semantic_check_data/cname_multiple.zone b/tests/knot/semantic_check_data/cname_multiple.zone
new file mode 100644
index 0000000..971c34f
--- /dev/null
+++ b/tests/knot/semantic_check_data/cname_multiple.zone
@@ -0,0 +1,15 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+
+email CNAME mail
+email CNAME mail2
diff --git a/tests/knot/semantic_check_data/delegation.signed b/tests/knot/semantic_check_data/delegation.signed
new file mode 100644
index 0000000..2007216
--- /dev/null
+++ b/tests/knot/semantic_check_data/delegation.signed
@@ -0,0 +1,43 @@
+; Delegation NS and glue signed despite mustn't.
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 NS dns1.example.com.
+ 0 NSEC3PARAM 1 0 10 -
+
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406103150 20210205090150 25674 example.com. 4tMK6g2B0ITXf2haSSuH45nO53GlpZQ97ofC5Pd/S38oeNzWmhfxIBaGtb597qxRA2NC7rYtGsscLrCa0sthMA==
+example.com. 3600 RRSIG NS 13 2 3600 20400406102301 20210205085301 61806 example.com. TrCJZgu1hVoUK532mmhQpZcEcPdw4FezPCymtUuQH9XjZNBn3DP/OhM8NvAbtailiOIX/djosTC2cNDlqSoVCQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406102301 20210205085301 61806 example.com. h/+XG/WWQsoAuzOM2wiulY8TOslYTj4MyP7Rjj3VXx8frlheIN84yH7NL6Xgt3ibQJpJl7rujkDuoTBH+snnCw==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406102301 20210205085301 61806 example.com. TYk9hqD6hWA8YH/G3VeggrUHb7CwX3ut5GGiAOcl9o8I0gdMIOu8E1uUukexvJsZAt1Fbcjc7ZIbsUmvgs2MVg==
+deleg.example.com. 3600 RRSIG NS 13 3 3600 20400406102301 20210205085301 61806 example.com. /Xg/3viyTMyd88hcByGifSMHGo3up83exBQQt4FC6qexZffRyNiLrHOfnoz/2LqFMg/oDVCsvqaEomiMM6FlZw==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400406102301 20210205085301 61806 example.com. zc6VOVGfgoB9C8/0WPHOVrdikBzK6xh25UtrdIYuSzcPWbFlWSsV3+xS1q20MBDb2dj635jcyBWRep+287rDLA==
+deleg.example.com. 3600 RRSIG A 13 3 3600 20400406103429 20210205090429 61806 example.com. 8hcIsHOARI1XXMcPXwtlmQC071+FBH+I0a6CufDbE7nPa38brBKomqTjiYF26K1KZ4IQASw5vvF0lFg3eEOZog==
+
+deleg.example.com. 3600 NS deleg.example.com.
+deleg.example.com. 3600 A 192.0.2.1
+dns1.example.com. 3600 A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008173641 61552 example.com.
+ URGzLYXySdeOtXWW5ph64pNedd7/cq0WYcbd
+ nArHBIN2S08knOfV/OHOMDaR7WufUbIF8bPQ
+ FxDkURlAhZbH9A== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 3600 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 3600 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS RRSIG )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 3600 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20g1gol477ro51rk9a9nfd54tfqal7iq.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406102301 20210205085301 61806 example.com. LUBULY9667EsrOHecNjp2QkW9JJW1fOSyTmleWul7vGFwuNC1mKVUQu3H3V5ndtwzU1YD69oa6eI2DOERmiJXg==
+mjv836rjqej5ubghvksq7n44rso3q938.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406105733 20210205092733 61806 example.com. zYuSttG565eDv3FPeKfZs4FNuJHD204/8nv8cNx+9iqbxMdh5s1XJx4nolWyiOJcBq+G8CmtiuJK6plUs7x67w==
+utqvuhu2blk3dhmrr5t1hd9vteohqt0a.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406102301 20210205085301 61806 example.com. aMivM0YOs4Il/WRWqf3SRzh21nZXau7VIJOpX2NK46qxBCW41N/+J7rXaeAT15ayWNjCHP1YoDwyuC/lVZtCqg==
diff --git a/tests/knot/semantic_check_data/different_signer_name.signed b/tests/knot/semantic_check_data/different_signer_name.signed
new file mode 100644
index 0000000..ff92f7b
--- /dev/null
+++ b/tests/knot/semantic_check_data/different_signer_name.signed
@@ -0,0 +1,52 @@
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ UH4IJhLwxWI9g2vycAuGAHm5XzsW5LKr6xeI
+ aoaiMeb1pepw9vAWEUO1Byimg7FfhvYpt7+J
+ IhYCvpBb6u3ucA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ ou6B0AgSUxs7//b+c+Gm3XjC83TpgGvRwj9d
+ F48TEZCMRpdvtVNc1hDnNKa8oXA16TafbkqN
+ Z0ekrEo2LlN+hw== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008164859 49259 example.com.
+ uCzqU8DU8ZMt3t/h0jwZjdVgSj33HhwtGwhE
+ ZglZ0gUVDVLndP5Q+psqlz2jBmiXIN16s/+b
+ di0crJ9LULq0NA== )
+ 3600 DNSKEY 256 3 13 (
+ qWpA6ejmc17FHZTN/YoYX4WdNN32LC2IlBmm
+ n2Yoi16OQ1e2ztEusvQaSwzEMbN2pIzfTIlF
+ YQQ1gzLQAhWIpg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 49259
+ 3600 DNSKEY 257 3 13 (
+ rHQi5BOkLnSsZh1v9saRZ38MkzYLL0oGbAK2
+ Dp86tH3lpDqPoR7LM98gyBLZgp81m0YHAYnf
+ 2yK617XStIPw+A==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 3753
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008164859 3753 example.com.
+ 81C/yn0gxkwOMUWNZPszGow4UyDuDn1V4WQJ
+ NXJfNiTvT6edQ0rQakhJPGgVyH4LIwWJV8Uk
+ fOubCv7BBgu0wg== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008164859 49259 example.com.
+ x6z2ftS2deCBR9HJeIazQNrDdzw0lEE04UYp
+ npUe2zkIx6aH7MvvgZIjcFTwPOVsI00u7gaU
+ AzuxODSma50TXQ== )
+ 86400 NSEC example.com. A RRSIG NSEC
+; different signer name in RRSIG
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008164859 49259 different.com.
+ K/URrUmli54Noy0E3REXBo/g0LZ/8gneyVfa
+ FrGXLB0kvQydPyceL+BFIoJP6d/Gs/0qkUjT
+ vMQfvF0x3bFS3w== )
diff --git a/tests/knot/semantic_check_data/dname_apex_nsec3.signed b/tests/knot/semantic_check_data/dname_apex_nsec3.signed
new file mode 100644
index 0000000..b083ce9
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_apex_nsec3.signed
@@ -0,0 +1,25 @@
+; Zone without any semantic error
+
+;; Zone dump (Knot DNS 2.6.0)
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNAME bar.example.com.
+example.com. 0 CDNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 CDS 25674 13 2 2EC05563A3537BD32EA3EB92C44794C644F249EE440785CF28207B903E35322D
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 13 2 3600 20400406111136 20210205094136 61806 example.com. WIlxYlV/hn9mfojITrVbIV+Giy9b5pAKofkw62Yli+jIspQ3dC/WWLrM5Y4HcQwTfNp7yuhIS0jPzkuy0xuAxg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406111136 20210205094136 61806 example.com. z71ipK0zBRKKokzXdoZdtkxGC75MJbwmICNjSfd+IX/hneIGvFE7mTose1Zbb0WGgKRdUMEoii7hLZLrx7waqg==
+example.com. 3600 RRSIG DNAME 13 2 3600 20400406111136 20210205094136 61806 example.com. 5tIYeBwbwpVF0X5ZLoSpHeB8IYLU5/2fFYXqvctZYqTO24T0EBfu+++j66VSERAI38xf2Z0KkYcwx1XeIeivBQ==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406111136 20210205094136 25674 example.com. X3n5YVkjpSpK+IOCkhv/wFmF5WIPHUR2LXkNME84i5S4efvQiRRq/jgqos2f7OgfSi/9Q2Q2x6BiMQ1vx/R+Pw==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406111136 20210205094136 61806 example.com. gogp8pZycFopDodl4IOfpaKCbLqXw2v+5DcV2YwmHr/pMwrc28bClQxw4HVGcYQ13HpC9kKmzmcrn3dEumTb3A==
+example.com. 0 RRSIG CDS 13 2 0 20400406111136 20210205094136 25674 example.com. zRQEFycg2sNVVB4TOZO8QcMwRwSA7tHJqkc1l9V+WtEdJY8UvYpYPPgAn9FjWMzzhvRMlws89TBSsQzqCemHiQ==
+example.com. 0 RRSIG CDNSKEY 13 2 0 20400406111136 20210205094136 25674 example.com. hLOpPxmKXU//dmQoE5OdCqzWkkJsuBHa8QITWB/A3Tc2CXQTaqFKqTspZvTLOAYKNaSVu6BOLWM7Fi2Bq3I0mQ==
+;; DNSSEC NSEC3 chain
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS SOA DNAME RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+;; DNSSEC NSEC3 signatures
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406111136 20210205094136 61806 example.com. C3JeKvcKdQO3zTJqg5Z114jTd36tgF7PIL2kCs7X6VnCaVe7E5NtwUuLMLFIw/gUqaLDbE7vQwHMK3Psl536aA==
+;; Written 17 records
+;; Time 2017-10-06 15:58:57 CEST
diff --git a/tests/knot/semantic_check_data/dname_children.zone b/tests/knot/semantic_check_data/dname_children.zone
new file mode 100644
index 0000000..5758833
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_children.zone
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+ AAAA 2001:DB8::1
+
+foo DNAME bar
+bar.foo A 192.0.0.1
diff --git a/tests/knot/semantic_check_data/dname_extra_ns.zone b/tests/knot/semantic_check_data/dname_extra_ns.zone
new file mode 100644
index 0000000..e188742
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_extra_ns.zone
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+ AAAA 2001:DB8::1
+
+foo DNAME bar
+foo NS dns1
diff --git a/tests/knot/semantic_check_data/dname_multiple.zone b/tests/knot/semantic_check_data/dname_multiple.zone
new file mode 100644
index 0000000..2a6c0a2
--- /dev/null
+++ b/tests/knot/semantic_check_data/dname_multiple.zone
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+ NS dns1
+
+dns1 A 192.0.2.1
+ AAAA 2001:DB8::1
+
+foo DNAME bar1
+foo DNAME bar2
diff --git a/tests/knot/semantic_check_data/dnskey_param_error.signed b/tests/knot/semantic_check_data/dnskey_param_error.signed
new file mode 100644
index 0000000..1a2e936
--- /dev/null
+++ b/tests/knot/semantic_check_data/dnskey_param_error.signed
@@ -0,0 +1,70 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 1 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 5 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 1 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/duplicate.signature b/tests/knot/semantic_check_data/duplicate.signature
new file mode 100644
index 0000000..77bf21f
--- /dev/null
+++ b/tests/knot/semantic_check_data/duplicate.signature
@@ -0,0 +1,19 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010112269 10800 3600 1209600 7200
+example.com. 3600 NS dns1.example.com.
+example.com. 3600 DNSKEY 256 3 7 AwEAAd0e6EjJ0PgChDpbjB9QtvJ0ZqwKC/j7wlEOOB9owqefH/taZ37w6QR8Ysvvv2058AflDcCP3qlaOXp+ogq7AhayA+K4kc/UQyTPCe2jXKlX9IB0KAsr8nO9UEXzjYuyBw80Ry86Xxmj7OGYRu8eRm3ruOjVJy8hCrEQ7680an303Iu3Ixnmo8lPTPPMg4dbFXZ/RW6Sanrr/Sy6fre87XY58yywqX9lZOh5eqBeZG9WvU/HycrDkx5AcwD5etVk98tVTnofShY34ePZWDmRHEtvBpMzNObdomgM5we+DawC6P+Z8PFeGz+OgN7WVzkjm+MmYAk1aIeLQyNIE8SyMts=
+example.com. 3600 DNSKEY 257 3 7 AwEAAea81n0wL09ZMejh806rJ0Km3MYC+ySPWnOmV70nEmONbnduRPpXWjYSqFmH5kldfNdCH403kI/YCMYDYBAPFPbhxuZuVBaJJqOQVsI4rpwj+XiANfGFAq9pZ0oA8iH7gSoNUCf6+g2hcP0ajYoqCjUZ7ZZQNytV/x6foW5t5PbyPNeAU1AEKxk2VSg1TMfkccZqTIx1ofS0N102Z4tOBn26judPqLc0tXMCJc7wgekqG04IGe7UWfk9xWtwo2SbtX9diErF8DJ93C17OWkb04n1xCm3i8/XZadA/HrBjfX/NvlHF8qnUQzzxN7UGrvBD4hE12R9ICj4YNFZViOTdvs=
+dns1.example.com. 3600 A 192.0.2.1
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 7 2 3600 20170403124401 20170403124311 40703 example.com. ltKNDw2O/sIwQUsv3UCKqOtZYvWNJ0mHo2xDxpzZXfAiMbgR4k7jBIkpSEpcBiBlH7EvWom7CYVigPu8Y+j/Jq4uv+wmVF47OVY3YZvuzfprWj+iOQwPlfDJfUPx+U+73SSsZ21B5/+auB5cada730B4gQKmldleVGg5aov4H2+BpEyrsSs2o79qiXNBzLPqrZEmT0nfUAvQC8xhFV/71I8Q5qtfa3vO6DLSOBmBUtAlGKqfpWoZ2w+QDdA6rtOe0haizTZUtghL2ut47bdTR253brhUccL6nnLc5//jTUBToIhmG/p698xLnU9BYnuHIi74xsb3hVr5b46W5gAGKw==
+example.com. 3600 RRSIG SOA 7 2 3600 20170403124401 20170403124311 40703 example.com. E9a+I8HDh6ycTVkFgOWkzbH7PWds7ewp1M5lUci13ZzMVWsJeFW7t1tLnnOtvz2H8pq5/BevPB8iZBA7rHH7GxoQ5P8xrxAO6HvuRZT8O4kYAWRZ0QHhMIvY8f6VqTyoOmzgIGt0nJ3BL/XJgxIiFrsiLyih6+dkckEu62F22+FFvlv49ufKkCo+EUQPCzo7ZYODc8xKWo97SmaADzjfz7Hq9UPHraUgLhNkfBDbI9YPCGKaJaAiqCBy/6ih3SyHxVPLcIz95okeo5AJVszFIS+8pNPZssJBpWsLKYyAGzs2dsliRwS9z+a3wkHXJIfbLX+r3kGhcG4lQMYDz9SrFA==
+example.com. 7200 RRSIG NSEC 7 2 7200 20170403124401 20170403124311 40703 example.com. poh0BT+nUD3sM05axVGC+k7jj1r3YVNcx4bn/0cviNxzCqLY9RGgImPWsmkTgbJpmCox9SHzpTqL8acIQDNZaciNH9WeYKvn7wkap3z6jtCuQRezM3nUx7E37fzbnNC8MUoWkV37y3FSmtiza9l1isrE5dGkNMOsBcPvIp5wrbQ+dH4cMdcgQuW+NDjee6czIeeYtyarBWhq30S2lxroh8VXlrFDTcbiIY4UoGzJDfevvsonNFQXc+p7qq2fU1fyU1e3Ugty9I23g6fLhLcrmflVbYpcgE8/02K4asu5D7x/dOq21OU/jJfeudk66l6CVw7c3Qh/N63jRn8SsCj0Sg==
+example.com. 3600 RRSIG DNSKEY 7 2 3600 20170403124401 20170403124311 5154 example.com. RmAPllqg+CEX+vj5KKmXGYsF8vqbqLoXSYqSOSWbvgWRazKaQU98fpJWdrmqylkR6Xa1fnbvliP4N/0MGremNejsNPvMsJ4GvpyM75Mb4BEf5mwwikW6xov9V/n1AN9grWofj/r5evsZYcxIR7naM9oxV6qJvy8fFIjthG805MO18bYk1/Och2x9TgUf6DTqKNBHQjk1AfrhVvpuLjdNnNT16Ak3izrCLOm2tuNTaflkYkD0n06ZIAsz1krJWztpncA2csnKQmdybSL95wZnFeb6nkmq+P5vk3PuTENIMURYMCNfzBHogLfbVG5HpDhaHkcM2zSe1qATbp9xRZujLw==
+dns1.example.com. 3600 RRSIG A 7 3 3600 20170403124401 20170403124311 40703 example.com. iYfVT+HPDqMyH9f8aLrzNK6sOCoo38tlRJ5tjiko0DOpsWp20LLgVQLvKsTs3SfdC0gYzMVQCgzfDMbAgrEvmm4ZEQT/NSUhcO2t08f6pABn6GSdoswFupi0LGdQmgj/MbOET02OTALh9I6g3Ir1+bF+C10GS/8CYqffO/52IEJylc6AzDCwAjfkI/55hsuv2H8Wp5cqEG5yAlL4fK+U2zQWEuAGOtGbEuzeKcEDV6iiAuFge7ClW+CbB3gQxEhDdx5TQNNAcpzHmum5yfsfcFkIezZqIzEvOQWg1nJVcLvYnuBqyMWv/uGbG4CxTDy49U9JB/6QfilMk38VVcitZA==
+dns1.example.com. 7200 RRSIG NSEC 7 3 7200 20170403124401 20170403124311 40703 example.com. gtRE8TafAp50tzk3rAub93X69pp4J7uPzXPM0UAAp97oVMqcqvuZh80fICLmKl7xShvBx+AYfV+2CoeMW66CXVHTP8CyIjLyi32EGgL75Y2xs55/lEOaMl8hREgxniopCWGX/5vjmY0SBdGWVQVyeQeb0DbTXFWQNw/1LUPueoM1zqGcHFKFt5Y1GidboUEDsNeCmG3ZzGV9/v9sVUezzDK53uaHm8Ojz6E4N7kg6qXDF32ZAxs0dDjh46bsaTNvMLCEXqO2imHx9Omc2wYyCt/roMoeYiulXQ7yHYt0yQuCYwqxxMqJ4z9jvLNdLxH3YZYV0CVUrNgNC/5vtUILsQ==
+dns1.example.com. 3600 RRSIG A 7 3 3600 20170216152943 20170216152853 45258 example.com. j7H3N22L+tqfwuSd4GhIwMyjrFSY3+kypIcOvg0Ipbj4pAHsJOJTiW454Ueq54G/0ntoHxgmGLv3d/EV9prMPPQz8eqtRcYFip2NuEF9bJsIG3SMy+0HolPK+8D7B0MOGFA2TExKNknS7sJy/Jn/yQrf7BHubC61zWnqB+vN7MNlJASXEvy3008oi4FScSsrAVIrZK+z7utY4exkCVfELC7flGenoyPDFR12y8WpN/Tk6q1H37x+EKaQgFj361Bm6f/InPKW8Npn/SNCIJ2DvSWAnj6+2n1mse0sC+rKhRIDMDopu7JzTjpVs9U/p9BY5dtH/3YvST4Vz3syqd1unA==
+;; DNSSEC NSEC chain
+example.com. 7200 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 7200 NSEC example.com. A RRSIG NSEC
+;; Written 13 records
+;; Time 2017-04-03 14:43:12 CEST
diff --git a/tests/knot/semantic_check_data/glue_apex_both.missing b/tests/knot/semantic_check_data/glue_apex_both.missing
new file mode 100644
index 0000000..74e37f6
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_apex_both.missing
@@ -0,0 +1,14 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ NS dns2
+
+; missing glue for dns1 and dns2
diff --git a/tests/knot/semantic_check_data/glue_apex_one.missing b/tests/knot/semantic_check_data/glue_apex_one.missing
new file mode 100644
index 0000000..47ee797
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_apex_one.missing
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+ NS dns2
+
+dns1 A 192.0.2.1
+
+; missing glue for dns2
diff --git a/tests/knot/semantic_check_data/glue_besides.missing b/tests/knot/semantic_check_data/glue_besides.missing
new file mode 100644
index 0000000..38ad890
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_besides.missing
@@ -0,0 +1,17 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+
+dns1 A 192.0.2.1
+
+deleg NS dns2
+
+; missing glue for dns2
diff --git a/tests/knot/semantic_check_data/glue_deleg.missing b/tests/knot/semantic_check_data/glue_deleg.missing
new file mode 100644
index 0000000..291b450
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_deleg.missing
@@ -0,0 +1,17 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+
+dns1 A 192.0.2.1
+
+deleg NS ns1.deleg
+
+; missing glue for ns1.deleg
diff --git a/tests/knot/semantic_check_data/glue_in_apex.missing b/tests/knot/semantic_check_data/glue_in_apex.missing
new file mode 100644
index 0000000..a02f6bf
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_in_apex.missing
@@ -0,0 +1,13 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS @
+
+; missing glue for @
diff --git a/tests/knot/semantic_check_data/glue_in_deleg.valid b/tests/knot/semantic_check_data/glue_in_deleg.valid
new file mode 100644
index 0000000..42adf6b
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_in_deleg.valid
@@ -0,0 +1,16 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS ns2.d
+
+d NS ns1.d
+ns1.d A 1.2.3.4
+
+; glue below another delegation is not mandatory
diff --git a/tests/knot/semantic_check_data/glue_no_foreign.valid b/tests/knot/semantic_check_data/glue_no_foreign.valid
new file mode 100644
index 0000000..4cdcbe0
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_no_foreign.valid
@@ -0,0 +1,13 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS foreign.
+
+; glue for foreign. is not mandatory
diff --git a/tests/knot/semantic_check_data/glue_wildcard.valid b/tests/knot/semantic_check_data/glue_wildcard.valid
new file mode 100644
index 0000000..9e36b5e
--- /dev/null
+++ b/tests/knot/semantic_check_data/glue_wildcard.valid
@@ -0,0 +1,22 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111217 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+ NS dns1
+
+dns1 A 1.2.3.4
+
+abc NS a.ns.abc
+deleg1 NS a.ns.abc
+deleg2 NS a.ns.ns.ns.ns.xyz
+
+; wildcard glue
+
+*.ns.abc AAAA ::1
+*.ns.xyz AAAA ::2
diff --git a/tests/knot/semantic_check_data/invalid_ds.signed b/tests/knot/semantic_check_data/invalid_ds.signed
new file mode 100644
index 0000000..2435014
--- /dev/null
+++ b/tests/knot/semantic_check_data/invalid_ds.signed
@@ -0,0 +1,106 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 5 3 ( 2BB183AF5F22588179A53B0A
+ 98631FAD1A292118 )
+ 3600 IN DS 60485 5 7 ( 2BB183AF5F22588179A53B0A
+ 98631FAD1A292118 )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 1 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ A RRSIG )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+
+
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
+
+
diff --git a/tests/knot/semantic_check_data/missing.signed b/tests/knot/semantic_check_data/missing.signed
new file mode 100644
index 0000000..75c7d22
--- /dev/null
+++ b/tests/knot/semantic_check_data/missing.signed
@@ -0,0 +1,20 @@
+;; Zone dump (Knot DNS 2.5.0-dev)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 1081539379 3600 300 3600000 3600
+example.com. 3600 NS dns1.example.com.
+example.com. 3600 DNSKEY 256 3 7 AwEAAaBgc4O+4UWd7mzSyelnPb/le/x0q/E90B/xnlf56kgEFMvEGz++o6CMRXr5JfgyxDDsahxTwFoWu30KJry4MjgcwlETM63DFpIYtyDBsi8TlQFEp5NDrlYUWlPGiPfywZBVkHGFMcFct+5/ZalTzYIP39tDytZcPZ/IgQRQZA9qeHYIw51YX9IlNMalHFCtJyrpzCdo22FY/vwBwSbdCa+vzkH1Uu8JkIqyAvAGkuwVisgpMpzWhvJNi5WSAnQfwOcsYCftINAHdRXtuqyG+uU/RDcZ2psx+woi+mYzEPeYV9MEqWpDyIz7jS7e1hK/1o05+qY8Eu2gt4enRj9BQr0=
+example.com. 3600 DNSKEY 256 3 7 AwEAAfcfJSUnim+cR3YEc4VfdJ5W65GNlK0LQaAh6vAejH7uol8VYmXdlyz+wlhad+DyRM5Jl+XJVFMMyFUqWx+Q63DPRtl+TlN/2pWU2gsNHUoovFhFpdX1cQMVoxr2QLgsm1ASTeqvZV8Dn0xAlNRihNv877sTySjveaH0JpuVCMpe5DB1zVbAzLgDqFKAvwJCumdycp7RzMi9PqS1XtsEInKi+X/zZteTJbDO7l+tFt9/NgFxiaLgNo8Gz2oVBTQvAbjCDEi2mPA/YJrpOGZWNkB2L9HFSfzZih8BbgUI3Fh4lhS8XCVrfVV7K9YR2F5NBVi7h0Mk15hzNsSS7tRK1FM=
+example.com. 3600 DNSKEY 257 3 7 AwEAAcjdwuJkjM8G5rk967z1cJqF88BqpvN2GN/6Tj1XA5AbIx+33qy5JI6K43ehlT/neLizOCk/JyXaw8gcjQaDKcIy0vKysXvI6yK4PNgHTdzQunBqGTfvPDlXKCle550R/DJF2OZH/T7jgX2GhQlem6UB3A23n24YP50IzAmXK9RYdE/dMFXU5jEz+CjcHNkB8ZCb2VrKE9RDjY88vr6lyM2kPbvBtx4UaUSEzwlDMRc3Wf+dBWKm6mKWAPsHZM/cux+S2mca/cxEA1ngCgBBbm7824WjTXgDs14QWuwruMTqLPDujUYND5kbsiuhQsfEFGVq2UyhGEZG/NoIEEg7qLc=
+dns1.example.com. 3600 AAAA 2001:db8::3
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 7 2 3600 20840201000000 20160302125715 7242 example.com. B/6k7YAQGkiz6IkssLZblExgMZBE+Flkhv/leVgvM4RLPPpQ2znouYyrSbVCcU5irA7PFLbee5Mn1aWj2S57L8yGJjHBuamQSIO0GcvGcmXi1CrdaYXSofo3PtnKpM8/mG3+8RCUL5YhoxhTK4Y5gJrYGPkRPKsBTw2Qd2TUJFebtYgCuGN8Q3UwbeYPw89rNbqC3a7zsGwJoZKgDnm3NwCWcv6NRTcQA/H5v6T0/QvYbZpBMrjl3EiAWOccdUlQnALngGSzbJ2GnmK933VXYhuAoSKEN6thauOBSLkdCh9afkUzo/t7xhTJszo0F1uuavs8PYf3HjmdnMwdPMkUuQ==
+example.com. 3600 RRSIG SOA 7 2 3600 20840201000000 20160302125715 7242 example.com. dHmPqRl9snHFavwkkAFZqHDmvUrI3+e+dmEexqgW9txr30fbrkeGAp6ApdZlqJiDTJ/2q0UoyQxSYe/BzgV4gEBgTTgfmC7m9eHVLTD70KMlNuvwC4jkh1vWT1Zn6IFUsQtJ+54XfRTe/2VHyeK7saqsA/ARRZGOzk6To8CWxNCApUdLZMQO9UTX7uVcXKkPvfMvlhx1fmn4OE8ntwbY1oPosQb987N8V8x9Rb2hINr4DCkXNDydDZAh4vsZO0DHPlmyfkyNguQDmdgnDz1CVbJzguy8tqeMGT7CrwU8AmX3JADQTHoxjnWEidLLUa/gNDRFcRc5YMcdZyImHqdNZQ==
+example.com. 3600 RRSIG NSEC 7 2 3600 20840201000000 20160302125715 7242 example.com. bjw2G/BwF2xTP/QSkKqdr7byUS+nqMvfppuhZmH0VcysAKN2oqsV51bn7gWej6dnx0svtX7nCOlwdDFSCMJld5BGZFnAfhS+XVc/wTeZGMi1BkeJxlT3UbGLhf2DuLLyL969HPltL527vSysjBEmi7OsTlH+wXD6SW35ZClajNSRLjxrRpVHTGpA5uyyysHRYNXAKS3+SSc1N/Fovjgzi68exWD0BKTGia7Nf9Fn+bqvhbYh+pMHA7djPFJIsER3OCBx7H1KMxl6rap7Q3rC0I289xnnsOqRh/GnzSVgvobKWozOOs9XXNg+w9ioSos+kbzTxxSEvLKqNBgCbLjUHg==
+example.com. 3600 RRSIG DNSKEY 7 2 3600 20840201000000 20160302125715 37855 example.com. EHXazcQ27b5Cjqd1T8TAui2PrUqEq7cBxk45OA8BfBDmOuH2vXFVL+juCM2gCvQ+0oZmcJmpkjMCxUqQekXgxRy21PlEzJfR3VHRDSYCSogR9cCLw9T9OiFXugZAtYcLVXVHddKu0+t5yeQqdStgLBiz9EmeuPFYd/h/BxKI8FGx/TjHNzd06SKgxlZAT/vCGhEswgSIpxJm4Ju561vT2/Hh+NmD4jIKVf0OUSkfRVRbxpzMs0HaZx6s0T2mcL8so/rEXjSIORkw56Q7x3EmYQDxNJjoNo4nHKT0/pciCey+vHj9pxxaiave8wLBG96JpgJgSV7BG8TTbE2q62wX1Q==
+dns1.example.com. 3600 RRSIG AAAA 7 3 3600 20840201000000 20160302125715 7242 example.com. h0oZ7ghuhANB27zD+M1m0NyVUHND7g2qI1IfEKDjMzZ34wyqM0xWLm/Izln86ol4naDvJU3a7hsJS/95DdvW/s711Oi2nKhX/Hkjvnzu8WVcf5DvEKYQe/fyZ676hnwviKqFzwmfTAKgIuSvt2uZzJkpcyL8ZE6O/GdPrcR6rTuuDI30F4zXIWPmuMNLR2qJv59DwM0tZScLdmRGKGnZNpdxDvtbCsZrXBUPrOE5XpAw+fe8+oL3UEeKQZq8qFhVvegl4TAuk1a8CS+zG4E1ABQKp86J1h0G4l/ajmWqq2T59lHsBAOuX0IbKHEHIJzwRd9EV7LM59EtJVacx8ZCkw==
+dns1.example.com. 3600 RRSIG NSEC 7 3 3600 20840201000000 20160302125715 7242 example.com. Pq6F6akEhyIqch7vwWJ5C53FW1UW/Y8vseFqB6tzql5bnIYjEwikgiWR85uvSUNGvsjHbadBYiVh2i68k80ws/2LecQCvguSH+rMkqqY9go+pBh3pdiNlJaJZp3zJQ6+E35xOA+p0G5t84Et3satJl3OcpVthrdBKuotpDg4P+nOpfLHkI3FO5vehxs71HmESQli5JllhPNMH6WZfWsP74D4DgRjUpIK9hGznCeuZxJT5+S5wL4fzqb+P20W30bsQqMbo9GNdPy5AdbwZoEKJVoN3HC/sv03ScQzWUxjamHCQOeZFys25fFlh7+JU1xYSb3V/fPhUUuf7OsBVvn7+g==
+mail.example.com. 7200 RRSIG NSEC 7 3 7200 20840201000000 20160302125715 19578 example.com. gjNXoKVddN+Z3MmHXxs0v4Gv/3zaTAg0mBSLkSp8Ion6qKj/aR2y50QhNfZGVEZSmyerDiaVpfPMN+q9mwx+6xmv4/G97DkadXBYt5IXGR1fXhMCF+RLJb5ePYjQKSk2TfRMJAlk1Mowfvlp8rXFrT576y2F+IXKbpiJOdRt/13Wo5IUbw6LLFDOeZ3fUiZtBmoWTTjBnrGWYdb+ePcSXID+qM5TmRXqIOFceJvt/RhGZ5LYAchgM2sZDf4Asacxg6Z6vS2cA1opTLMAIu+cuEmq61cSJxWHblfXIpPMXFG+4i+nkCxEFxWyxt9edlAeHS/l2AiHQl5QeuzwEjFI2g==
+;; DNSSEC NSEC chain
+example.com. 3600 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 3600 NSEC example.com. AAAA RRSIG NSEC
+;; Written 14 records
+;; Time 2017-03-31 15:38:20 CEST
diff --git a/tests/knot/semantic_check_data/no_error_delegation_bitmap.signed b/tests/knot/semantic_check_data/no_error_delegation_bitmap.signed
new file mode 100644
index 0000000..abbf088
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_delegation_bitmap.signed
@@ -0,0 +1,61 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ /R/djqaZWRo1zCmz7B58/93D8ZxJoZAAKEbH
+ xuCsAJ5dm2ubvtgvqmhNXMqdVBvpb2OPdBX8
+ VF1j9RsjuE7ORQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ AmyqpqMfMEztA9S1Urv6yEtKd5yc6kkSedRU
+ uLp7velyCkipFzWgpzRVDqn+wp2ZaHig0Fod
+ kryw3j4yHOLlHA== )
+ 86400 NSEC deleg.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ 9tR3kL4pVEYsHzt888pbP0TtS/npeApAEUfZ
+ L5rXQE0WqBLQGtyEPYxujFuaruvxH0SgLl6r
+ n6MKCEB07DjhTA== )
+ 3600 DNSKEY 256 3 13 (
+ C7v6eelCoXgBoUjHe/gKdsnWNw04GH7PpYMo
+ 2hF5jaeq1zkLSXkF2xS/04MgBTFFYuDU+LGt
+ 8kMKNc8o2wH2jQ==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 32411
+ 3600 DNSKEY 257 3 13 (
+ m6KdGBizfDaUhcW+nIHuRdufZFcSYlZ5Xoky
+ +GcH23OxZtPzPwKwpg5rTx+RCRPlVpmwyiW3
+ aC69n0Q8mr8NpA==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 60051
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008173446 60051 example.com.
+ SD4149dui/vuky4G6wiJQLUw5b8XpG+Cy/cf
+ +9CSbuKWHRcC1K0wVEw6xyEah6eD/7Sh0eFA
+ EECgej5etJbL3A== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 86400 NSEC dns1.example.com. NS RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ HFA1XBdjaUvb8lbyhXVDYxTUn8Nr2HNC5ktc
+ kPBW2AGMQiVUtyR3vPxUIiusxsQn+uyRL2QC
+ NBG3ANo5exT8ug== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008173446 32411 example.com.
+ 4NLhmO0Sa3yk1ZikWSRYEX0FpHK0NkTGk++h
+ RHJO3E9M6Og1am/PiPf67DAe/2n4ANItC/SH
+ u/1WSvYQV7OZqg== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008173446 32411 example.com.
+ pQgr7WzGpL8gbbAcbeYEIYBLq8lCAuE9NaUf
+ itYKBFh7Cbg4YrLOoeAV6v6V4tfZPpmNpd2U
+ 9VUrY9es4QfX6Q== )
diff --git a/tests/knot/semantic_check_data/no_error_nsec3_optout.signed b/tests/knot/semantic_check_data/no_error_nsec3_optout.signed
new file mode 100644
index 0000000..a03f4ea
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_error_nsec3_optout.signed
@@ -0,0 +1,29 @@
+; Zone without any semantic error
+
+;; Zone dump (Knot DNS 3.1.dev.1612270066.d215637a6)
+example.com. 3600 SOA dns1.example.com. hostmaster.example.com. 2010111222 21600 3600 604800 86400
+example.com. 3600 NS dns1.example.com.
+example.com. 0 CDNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 CDS 25674 13 2 2EC05563A3537BD32EA3EB92C44794C644F249EE440785CF28207B903E35322D
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg.example.com. 3600 NS deleg.example.com.
+deleg.example.com. 3600 A 192.0.2.1
+dns1.example.com. 3600 A 192.0.2.1
+;; DNSSEC signatures
+example.com. 3600 RRSIG NS 13 2 3600 20400406110811 20210205093811 61806 example.com. VD3IclxLUSi1tgv4+FJ+9e3EWiRny6de1y4jUFn1Ama8+Cl2vZO2Jc34Q9MKY/S9m4id7Xe8MtkkrKThQcaaXw==
+example.com. 3600 RRSIG SOA 13 2 3600 20400406110811 20210205093811 61806 example.com. BniH53lEM1hYGcorTmqF7At3+neZkifPT1sM15nGlQUQ6RfkPxh7Uy8Pj3PxLL5v7WDTyFGbLVThEFWZUh/h6w==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400406110811 20210205093811 25674 example.com. 3FSDEJ9f54++FX/EHWXXnbHW8iJPaDG4kc7qf772y62dtqTfAvb22lq2yKzCOaRFFwpPKEdcS4OEkhx0IbC27w==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400406110811 20210205093811 61806 example.com. BTT+7Gj8V2pATxogxJ8xEO5eiVHoVIDxdK60zDS3MWNcbUc/n9vJR8NrCECel9egQUWrejawikO4DkyQxLZpkw==
+example.com. 0 RRSIG CDS 13 2 0 20400406110811 20210205093811 25674 example.com. h43kZiM1EFETWQEtMM8Xls/RFDsAkLIpLf+DUnJ+zzxv37xpGvtf/s//3ew9qEhouBnGh/1FWtNr8vjhzh0tsg==
+example.com. 0 RRSIG CDNSKEY 13 2 0 20400406110811 20210205093811 25674 example.com. 4mf7C/zyWoFRllUEaLHpdxJdlbEQXIRKNH6JOH3sTKSQMGj1SMmkWm9qlO9tVaUm1ggB6r8TPWgrAUBG+4A9gQ==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400406110811 20210205093811 61806 example.com. lMj63MgZYiCl6Fdf0Q5C4/K99AAXTqCI9HSBQcrc7qiZDjRpZXzBUO8yv7+5JSMIo/A3tJtQL/12VFPGZ9NQ5w==
+;; DNSSEC NSEC3 chain
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F rvcd9h11kcnenarqcmtmrhusdmb24rm4 NS SOA RRSIG DNSKEY NSEC3PARAM CDS CDNSKEY
+rvcd9h11kcnenarqcmtmrhusdmb24rm4.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc A RRSIG
+;; DNSSEC NSEC3 signatures
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406110811 20210205093811 61806 example.com. 7AdxaQLQ16ORwtf3t9lNQrzOP1BKu0TOIiKfx8/7o0JKoVtDYjqTC+ilWSD/Mbfb6PI6ND3NQKsIbnApOa2SUA==
+rvcd9h11kcnenarqcmtmrhusdmb24rm4.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406110811 20210205093811 61806 example.com. bOUzqzuIhV/SPyXiFOgsJbnS77dijFWcLDY/0X3r9aNiAo3/vSE4OTT0f6CkcBQDka+LjIoRaE7NIaTMl24fdg==
+;; Written 21 records
+;; Time 2021-02-05 12:08:11 CET
diff --git a/tests/knot/semantic_check_data/no_rrsig.signed b/tests/knot/semantic_check_data/no_rrsig.signed
new file mode 100644
index 0000000..6a3161b
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_rrsig.signed
@@ -0,0 +1,48 @@
+dns1.example.com. 3600 IN A 192.0.2.1
+ 86400 NSEC example.com. A NSEC
+; missing RRSIGs
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ ieEKhIV69ywg+YFSqdz0t17eE+PLl1eR4kpv
+ Mq6Q6TfjC7V5/PcFW6KRoP50RFp4m4cD0E7T
+ GpmpnPF++QV1Vw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ kYbAbCGzyWPBEfc0TH1calUiKsZi12MH3TNV
+ 7vtjOvIYEqeNmuJkrw899a7nOPNoahB6h7o/
+ DXuRlFqYYCC16Q== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224081310 29600 example.com.
+ PchT9RWRkLCMxWAQ3ut6LZlh4MYT4CkAPThQ
+ cnIn0ORi/fVgGzlifQ88xfEdEr1ZoXk9PlhT
+ 5b+wocBOl2HhGg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081310 29600 example.com.
+ JLcSyR8KgSicUou0c7Zs7Ol1DYiaQ8Lfyort
+ 8a+5OP3em3r3NH1nJkiVfs8+xdvUcGlGkbib
+ RKlfRWiIcOEalQ== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081310 31323 example.com.
+ EQMX5DPXhwa+blMRkzl+swUW3BtzpGJ5tGEU
+ hkH7bJfM51gIAO5qnUO/mMPnEA8b4dc20nnZ
+ 8j8lETDjqBLgDQ== )
diff --git a/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed b/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed
new file mode 100644
index 0000000..2c36b9b
--- /dev/null
+++ b/tests/knot/semantic_check_data/no_rrsig_with_delegation.signed
@@ -0,0 +1,61 @@
+ns.deleg.example.com. 3600 IN A 192.168.0.2
+deleg.example.com. 3600 IN NS ns.deleg.example.com.
+ 86400 NSEC dns1.example.com. NS NSEC
+; missing RRSIG for NSEC record
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ HhnlCtlIaZFVklpzVUnzm6AzFd65CSc4WCJL
+ f2o7Gkevu+HTnkiPN6gqtERC/BKJz1EKd2fC
+ KDyLxXw6KeTRAw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ rcHuZd9wTYykzis+9Z8uyqD8V9h22szf2bmE
+ GYNyJBlHZO0sOmys31xnvDfQ9sdk9hf1TUfB
+ 9ACGIF5lDHBEog== )
+ 86400 NSEC deleg.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224081610 29600 example.com.
+ YGSe1OINjOY3I8BY1EoxcOJsDZ/DjGCT5nqY
+ J6BBjTcbT5S1W61SN50xc2sGB4Q8F2KTotAe
+ arzn4DGDt9mOMw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ rdmqHOUXqhwrJusNt/7FTV+AtO/v6Md3LXzj
+ /QzR/pCADNC6ZA+FvqaOycnUxoryKk7PY3pM
+ 5ispCMuEx/1OGA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224081610 31323 example.com.
+ jELyXsaJx+G4heZJ96dyE12hSyTNFazwWDkq
+ 1Mkja9/bTTdYAd+t8fhf/c35bUiTVJWMivJe
+ +YcCwqGf2U+2zw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224081610 29600 example.com.
+ ln2xuvghOWBDOfyk19Wwtv3oc8+1go3WQuMf
+ vel5x/uHVx6voNA25cpFIQ6nPlCo8pmd5R3w
+ paMxgoQtBkzBcA== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224081610 29600 example.com.
+ EEKcIegUeyn/1FIgxHV+gSX3b/ygQPAcjD8g
+ aCt1yiO0B1xmVm09RJNxzCLaTKxQENhxIoUZ
+ 2l7250pBQnrlAQ== )
diff --git a/tests/knot/semantic_check_data/ns_apex.missing b/tests/knot/semantic_check_data/ns_apex.missing
new file mode 100644
index 0000000..fd7b7be
--- /dev/null
+++ b/tests/knot/semantic_check_data/ns_apex.missing
@@ -0,0 +1,11 @@
+$ORIGIN example.com.
+$TTL 3600
+
+@ IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111214 ; serial
+ 6h ; refresh
+ 1h ; retry
+ 1w ; expire
+ 1d ) ; minimum
+
+; missing NS in apex
diff --git a/tests/knot/semantic_check_data/nsec3_chain_01.signed b/tests/knot/semantic_check_data/nsec3_chain_01.signed
new file mode 100644
index 0000000..cd90b9f
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_01.signed
@@ -0,0 +1,80 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ ; wrong next record
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+; RRSIGs for NSEC3s
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ PjEM7Bxxb7w67366fKCLkR9BVFAL0RI8RJCZ
+ 5aqoMVuy+ui7MLKxKT2LfeTHgBw1Cww1bbJw
+ Ip2zu0/ZGPfKzA== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ DUMMYDUMMYDUMMYgc7Jx/FgAlruRjwsS/YJa
+ sZRspDGZhSqK2daV5K0lmK+XL8BoOtp7aXtq
+ VER5XcWLOebCdw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229140652 29600 example.com.
+ XsJa0IUE2ddTohJmiuNVd/Po1ZOK0PDCuU7/
+ CS0/wiZ5ZlxPdVUAYXuC7HhGH+ZPsqwZ4oUU
+ ToDbFqfdzmC1XQ== )
+
+; other zone data without error
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ u5QMvOSkZBUM5tLiEAq3A+x4Ha17ZsNUYqeI
+ SuYA1+NbaBDxAtT6scB9aeA4lOTQ0TZvpGFE
+ YF/XxGtqvwdZ8g== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ ELZOh4iS9DpAafa8NTaI/eNL3Qwy+lsmgrzF
+ 7jaoR5yOURl/RZSJY+m9Peaq4ALcROdGJ0O4
+ miSpdTIZsBSGZg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ sjxCP/grgOR+4vmXw7HU/hGSx5dS5QxM00IA
+ gZNJ6Lqf+4OSL3TEa1/qqRSFTl5uv3rqh5W4
+ 8p2JoT1ZkcJj6w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229140652 31323 example.com.
+ jrH48r1iPFRfbyIZWcARQrejVgrE9v8qqt4R
+ uPHjz5t7PYmZYH544SI9HtaWGkIJ9jzlxr5l
+ ikCWo1we50y9Lg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229140652 29600 example.com.
+ yIXzhQw8c/c/in+doXX5JmqoGiqoYD2Hhw6d
+ /aGXc5QLQqxyATXln02vkwt1d7DK/ha1vkfx
+ bvGdduXDQ7YZ+g== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229140652 29600 example.com.
+ aMRzV/1+m9wQHWezSiwkmDEbnS85wB9dA5x/
+ u2P7NPsgwMnRdfpVIMfaVhSJH88i5OlLTvL1
+ sSK+RADpuoqnLA== )
diff --git a/tests/knot/semantic_check_data/nsec3_chain_02.signed b/tests/knot/semantic_check_data/nsec3_chain_02.signed
new file mode 100644
index 0000000..ca70cfa
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_02.signed
@@ -0,0 +1,94 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ ; wrong next
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938 ; wrong next
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ oErbN3Xw+0zAqkz5KC5nOsINblBc4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ ep1TVqEISn2ZOiBtizK2eyuuhsYyD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySginature6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
diff --git a/tests/knot/semantic_check_data/nsec3_chain_03.signed b/tests/knot/semantic_check_data/nsec3_chain_03.signed
new file mode 100644
index 0000000..80112f8
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_chain_03.signed
@@ -0,0 +1,94 @@
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A ; wrong next
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT ; wrong next
+ NS )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignatureD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
diff --git a/tests/knot/semantic_check_data/nsec3_ds.signed b/tests/knot/semantic_check_data/nsec3_ds.signed
new file mode 100644
index 0000000..ad5da7c
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_ds.signed
@@ -0,0 +1,57 @@
+
+
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+ 3600 IN DS 60485 5 2 ( 4EFB4310DB01A42E7882E102
+ 7A73CC28E2E0FE938F2D5888
+ A0DA0005B99E7FF8 )
+deleg.example.com. 3600 IN RRSIG DS 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 3600 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 3600 IN NSEC3 1 1 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ A RRSIG )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 3600 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 3600 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+20g1gol477ro51rk9a9nfd54tfqal7iq.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. k2hYD9qbLM8cRuN1fcLar/GsSufK/5oQYxRnE9bUDiKvC1WhCDF3pee6MSqybb3LoNkQUeOgGV4jdzvslzDlhQ==
+6dfjitu5vml86qnku9fo2ljddqqtqpvt.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. fGQRezo0T9Hd1tGJqhCXPyLONKSxOPmX1Kl7MjD1OVDLg9l5Ei9DmhrpCFxahXMBGIA4yy1J7mSK3PqelPMyFw==
+ui312kqop1ng8iqeiefnpsla94kb5q92.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. 6AmfjUgzO4Ew9FmW6koVhQ+98Vd7xI7kpFXwj8wb4ObmuM8uFu6tpvcT/jDcUduFuUb//DS5fS9fXraLNL8JUQ==
+utqvuhu2blk3dhmrr5t1hd9vteohqt0a.example.com. 3600 RRSIG NSEC3 13 3 3600 20400406101515 20210205084515 61806 example.com. WMvFzIf9Ekvr0UVRzbZpxUAjT2Sf2KgsXek6iG786Iw6nZ/rzPVNlfNLhWvdqQmi5LEDp03UExshDlPz3JgOkQ==
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 NS dns1.example.com.
+ 0 NSEC3PARAM 1 0 10 -
+dns1.example.com. 3600 IN A 192.0.2.1
+www.example.com. 3600 IN A 192.0.2.1
+
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400405162736 20210204145736 25674 example.com. 5nIsRsT30KNhPS/i8rNhT/C3uPli0jb+7fLYL+eHKggTHk5UK69Z5EHA/ISKnbEOMIQA3QJ98XNreLJk+sTZ4w==
+
+example.com. 3600 RRSIG NS 13 2 3600 20400405162736 20210204145736 61806 example.com. nzKbYNX9cbrf3zNMSRK4ftG/p4DLn/uB3BM29txIj0nyKxL1cmK0wsTltGmwLzJTegBy/LV1VtMudDLWEFU3sQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400405162736 20210204145736 61806 example.com. HvtxfzCSLHjFSHuAyO+KKymy/vxOGLS8T1DuhfAoUtweHv1zVeYfbFOfCdcfs15PKO31ldqwWRvFWAhM+3hnrA==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400405162736 20210204145736 61806 example.com. 8uDKYTVd+XuYFyzf/aNm6kMjZhbI8r+22v1AuuYYqgP5aH6/ZFXusczSGkPdcauVIgKLV1I7dBBQkQm2LNIqAA==
+deleg.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. beD3O8cnCQ+8HWZpn35gFrR2tLkb9tGpe143BfUA0aOkAr2PdK9CUBs47uSyWAATYoa11gtxxdFUzW6coa7l/w==
+deleg.example.com. 3600 RRSIG NS 13 3 3600 20400405162736 20210204145736 61806 example.com. HJCAXBevueFA2BOP6eOnsbP1X+2VUQRGXRcYI2SDqqq4U2DQHWQMOfI+pKVpkfdc8D6qDYnFZSg6II/dDJQ0AQ==
+deleg.example.com. 3600 RRSIG DS 13 3 3600 20400405162736 20210204145736 61806 example.com. DhZsh6wiPACEUz7GY4WpvcIrMOF+sU27kJAGKaCcpxv9jQBY7Jpf/otRf+yn+Bmm32RZUr5swSXMXAvDtCj6qA==
+dns1.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. z/pEp4EcGkmy+niefZRLgRo1LraBlJABdgpSo94cYEqJM3GBMHsPZeAKmqnMAYA5Nz0hQtTplqS3rsJHJJdQ7w==
+www.example.com. 3600 RRSIG A 13 3 3600 20400405162736 20210204145736 61806 example.com. FpOwodJYlk3NxEEjGvY75r8Ptef13P4Um9N74NxV1QWQlqtBhg+1bndvaY376uBFVDFGsEiDFEgIoFL0Ao+PeA==
+
+
diff --git a/tests/knot/semantic_check_data/nsec3_missing.signed b/tests/knot/semantic_check_data/nsec3_missing.signed
new file mode 100644
index 0000000..4974956
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_missing.signed
@@ -0,0 +1,120 @@
+
+; extra record without corresponding NSEC3
+extra.example.com. 3600 IN A 1.2.3.4
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature12345678
+ 123456789123456789123456789123456789
+ lqhxunAbh08dsQ== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ 6DFJITU5VML86QNKU9FO2LJDDQQTQPVT
+ A RRSIG )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UI312KQOP1NG8IQEIEFNPSLA94KB5Q92
+ NS )
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG)
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN A 1.2.3.4
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG A 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+
+UI312KQOP1NG8IQEIEFNPSLA94KB5Q92.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignature4ey0Qcln
+ uquQZT+z2HIdCE9HeslAkTlu/Xt78vF4+3db
+ t2Vno21DkteA+w== )
+6DFJITU5VML86QNKU9FO2LJDDQQTQPVT.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ KElp8dLKBKFzgEFV8r5aP9pCyYUD+Z8rLBA9
+ KkCDm1y82x5T/Cu5UXuZJwhvDGDzwPqoY5Dr
+ Qbiek52n6umbEw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DummySignatureDummySignatureD37X9Bw2
+ 9JOkecZnmzCwBqfMCBvRYmNRpMd512+ZnW/I
+ 1vIViE7CGwkHyA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160302125715 29600 example.com.
+ DPwNyH7r/4wIBfTGxikNv4pY7omY6IqpQS6Q
+ jtTNuStA+5gk98dvcgRjluxqo/+ZlZz4V53f
+ 1y506ytGbX/q4Q== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ bGk1vLxVuJpcEy7n0gPvQVzfanbvINLJLcbD
+ eeie4sXZZAOwu6oQZy6kd8tvKtV4mL0OJzpH
+ XCO6BdZkmk/aQA== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ roe4aBp4G3TqQ4x5eRxbVIjApIh17gXDjfOY
+ zvRFLOkrwqKz3eX9WrRiCk3bYNn8s1fuenaQ
+ OSV1D5SL7utX5w== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ c1yhXb8wRGYndpVqG61+lHAAbZg+JcVYGPX3
+ Fw0jYigN4G+P0+VUCqPLkC4yfJylzuefyGfk
+ TUmriM3ihfXxIg== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160302125715 31323 example.com.
+ mZiLLTzbdaj7EJ8uj3TwvcvAfaMxYjyavlGT
+ qpa+cElfvBDm7R6MF4MaEQ9aZ2ylMt1lppjq
+ YyYRaaQC6yhm4g== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160302125715 29600 example.com.
+ K3PkVYZZV8QvZFtDsz9+ZfiM9wDkFu/eO2S5
+ tAtCXd1fktcW44TLWL0qADfFEEcMotvzLqv1
+ YJrD7TvrFDot8Q== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ vlzRtVFa44pRtdD8XZcgDa6021uA9A3TnNEw
+ 5jRnor4aoftUuVQNAanQMCgrWk63d14XZ2d0
+ lqhxunAbh08dsQ== )
+
+www.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160302125715 29600 example.com.
+ NWFuYaSEg3z3K4l/fHu/X9dK+rDZ177BbCNN
+ ZeFTPCAdOnX0nw1CQys629k7Vzdv1pHaanmy
+ 0Ru0tX9R65NlKw== )
+
+
diff --git a/tests/knot/semantic_check_data/nsec3_optout.signed b/tests/knot/semantic_check_data/nsec3_optout.signed
new file mode 100644
index 0000000..c9caa5d
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout.signed
@@ -0,0 +1,81 @@
+
+; insecure delegation, not covered by NSEC3 or opt-out
+zzz.example.com. 3600 IN NS zzz.example.com.
+ 3600 A 192.0.2.1
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+MJV836RJQEJ5UBGHVKSQ7N44RSO3Q938.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ NS )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ jRNMrWLfS4yzRHQOBxs6/GKWIzx6AZV5lyCm
+ 7bYTV9wS3owDJSQhJ7lft0WbBmUMtV3tP9Xr
+ Yc+yW48p2Vr+QQ== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/nsec3_optout_ent.all b/tests/knot/semantic_check_data/nsec3_optout_ent.all
new file mode 100644
index 0000000..5ebd917
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.all
@@ -0,0 +1,15 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173442 20210209160442 61806 example.com. laxHzto10anAyWXb/IqVEoBsybVmb/aCMb4SdxEC3YiJFj1IX9rxChVnuXrQ5zgr1f6YaRyc/DDTP8NFvwyTWg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173442 20210209160442 61806 example.com. /eNl2bkB/SJ6qBX+Jpm5KTXIs5Xi978JWRN2jtbEh5Z9udy7liS73oMkBLlJ33amKc7Gwfqi2+SgdHHud4j0Ug==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173442 20210209160442 25674 example.com. TpePckJM7GcsE72vbfSf49LzEM1chUFIiKBN0VyCHdB3YFpRH5d8Qx+XWh8Vs9AuLoKMWTQ0UD4kZK8yF70N4A==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173442 20210209160442 61806 example.com. RfPCpoA94H+dm7fqxhZ+GIf4fQwzN19yJVbhmEOtx6if9U/H6mJalvoy4d5UD/L2bferTBbie4I/TzAIXgVETQ==
+
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS SOA RRSIG DNSKEY NSEC3PARAM
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410181548 20210209164548 61806 example.com. EBPlHXYdARm1T0TaYadx0ETwC6w0g5J1yPR6LB3ur9IItcEWRONhqDrNwUbYGbW5c4nWep/hnJYdmMFq1bTfiw==
diff --git a/tests/knot/semantic_check_data/nsec3_optout_ent.invalid b/tests/knot/semantic_check_data/nsec3_optout_ent.invalid
new file mode 100644
index 0000000..114d5d7
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.invalid
@@ -0,0 +1,18 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173442 20210209160442 61806 example.com. laxHzto10anAyWXb/IqVEoBsybVmb/aCMb4SdxEC3YiJFj1IX9rxChVnuXrQ5zgr1f6YaRyc/DDTP8NFvwyTWg==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173442 20210209160442 61806 example.com. /eNl2bkB/SJ6qBX+Jpm5KTXIs5Xi978JWRN2jtbEh5Z9udy7liS73oMkBLlJ33amKc7Gwfqi2+SgdHHud4j0Ug==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173442 20210209160442 25674 example.com. TpePckJM7GcsE72vbfSf49LzEM1chUFIiKBN0VyCHdB3YFpRH5d8Qx+XWh8Vs9AuLoKMWTQ0UD4kZK8yF70N4A==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173442 20210209160442 61806 example.com. RfPCpoA94H+dm7fqxhZ+GIf4fQwzN19yJVbhmEOtx6if9U/H6mJalvoy4d5UD/L2bferTBbie4I/TzAIXgVETQ==
+
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F gtr2v0c3d7eqh7ob8rbad7ta90tq8lci NS SOA RRSIG DNSKEY NSEC3PARAM
+
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173442 20210209160442 61806 example.com. gb3uKByt54iwCsd284xzOVnnpN97r7ARz6UacMdm2Xs4M8t6Ao9bRG7jvbNpFCALfaU/xDQF7K3v31iKBeVwjw==
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173442 20210209160442 61806 example.com. kpuFRuzOhsG5zy0Sdql0AB44IDUtf9ccTwJXdULoIqUNKeRqvgWJ7ekEhBKvntVHlBQZPescgPMvvq7PLcA2Dw==
diff --git a/tests/knot/semantic_check_data/nsec3_optout_ent.valid b/tests/knot/semantic_check_data/nsec3_optout_ent.valid
new file mode 100644
index 0000000..c9fe657
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_optout_ent.valid
@@ -0,0 +1,20 @@
+example.com. 3600 SOA dns1.com. hostmaster.com. 2010111217 21600 3600 604800 86400
+example.com. 3600 NS dns1.com.
+example.com. 3600 DNSKEY 256 3 13 tCoteOM+A4o/A9uxgLyDg3HOg2DClU+3d+1XPQRtTfuaEFOGIpyH6qiFUv2b4DYuvmMyTkL99nxvyhA8yo0Cgg==
+example.com. 3600 DNSKEY 257 3 13 Yk8KOmyVzOij3x+Zs+eT4J2Up9+ipwXEKOhL9fTYY/DU10yIQt+zYm02UFZJX2oVTdHBCajpBFsZLH2X4ho1yw==
+example.com. 0 NSEC3PARAM 1 0 10 151E9F1094FE188F
+deleg1.ent.example.com. 3600 NS glue.outofzone.net.
+deleg2.ent.example.com. 3600 NS glue.outofzone.net.
+
+example.com. 3600 RRSIG NS 13 2 3600 20400410173236 20210209160236 61806 example.com. C4ierSNpy03xjH5rQEfb01wCj4SVIzX9b15FVEMIbn3lmDo5jXO6stOrW8Z7OjoVuCaRi1Qj997TeCYqOxNXSQ==
+example.com. 3600 RRSIG SOA 13 2 3600 20400410173236 20210209160236 61806 example.com. NNyQzYOcPbfEsqv61I78MuMguN/KIFi/wSJc940pj7rv+riA3J+XVzpaHSSh//q8CmrvpBAk2g8KsQG/6kOXmg==
+example.com. 3600 RRSIG DNSKEY 13 2 3600 20400410173236 20210209160236 25674 example.com. ZY3nxZJeOfSOEhs02mfhQgt6N1EgZubtPp3HuV69gStFSu4aCLi8a2aseQGilOFW64dOAYNm3LL/WqhPi7MZ1Q==
+example.com. 0 RRSIG NSEC3PARAM 13 2 0 20400410173236 20210209160236 61806 example.com. JITs/EH8nLaFRidlkT6+mcTwEpjgp2TMjb9fU5TBIlKn94og8YtOWFbNmzdEYBKlGLlkg8LwY2ortrSoRHS6Hw==
+
+ej69a9a2k2j0ntktmdvihrv5ao8fl1jt.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F gtr2v0c3d7eqh7ob8rbad7ta90tq8lci
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ple28jlp3q5anh045ssk9f3u7ltd4qlc NS
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 NSEC3 1 1 10 151E9F1094FE188F ej69a9a2k2j0ntktmdvihrv5ao8fl1jt NS SOA RRSIG DNSKEY NSEC3PARAM
+
+ej69a9a2k2j0ntktmdvihrv5ao8fl1jt.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. yatL/lbFSUyN4UyRtMXymxsiqhOXHp+N+pTI/zNOc0NXCdaaLceh+tZHlc+E4napRfP53XXEhuGavjShTIJ/+g==
+gtr2v0c3d7eqh7ob8rbad7ta90tq8lci.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. 20XNZrfJ4l/JIDjCbsba3mUOrNyOxJ2VuCju/yLc0XbdzqMcKJR87g3u967GEnoYY5f5+rJt/IHsuJWHcLApCQ==
+ple28jlp3q5anh045ssk9f3u7ltd4qlc.example.com. 3600 RRSIG NSEC3 13 3 3600 20400410173236 20210209160236 61806 example.com. eLRo9y8Rxf157qcciWM/LSUbtjYks2zLO5xQ9Ff5bidHc9m2XEqjWxqdPZz5gurEf+uPnM8mnix36X4YH4ZXwg==
diff --git a/tests/knot/semantic_check_data/nsec3_param_invalid.signed b/tests/knot/semantic_check_data/nsec3_param_invalid.signed
new file mode 100644
index 0000000..c7d8d6d
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_param_invalid.signed
@@ -0,0 +1,70 @@
+; Zone without any semantic error
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ W9EprjaR4loSnNW96h4rLsquPDw3LHYvD05k
+ djkQofHSkMNZAJ7Q+eA3Fs2ik5fnJFM7wi5C
+ MtFsV2TfqMJFmg== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ I9Je1S7XhZIW9C0fWE8NwFLC2rhHklddNYBO
+ dxVKL/lxENU4jPPBwZBGrcYn2WVHgkIzjG0n
+ EOHONAgRFPi3Xw== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ vO2UQiTN/CNUZOmSEg8kJlR/UqiAZHc4qMwj
+ 9u31sbPmOMuni+ZGuVCFFoEMtZerIkkQowkB
+ sXJFkvCP5oF2rA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160229083110 31323 example.com.
+ Z+aaLu4rmzekfhlj6A0ClREloRi8MloRHf/3
+ Dlw/RYY1hrOCfcZKEY6AXeVdUwESEsSkSOco
+ CbhyGHH10dKAAg== )
+ 0 NSEC3PARAM 1 4 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160229083110 29600 example.com.
+ d69kc52VdALI8fbdbflsVsltc1m7bI6QsJ5U
+ IDE9fy5VqcufZecZMKuozPDuF2vBA8ADFIRU
+ OfYgKs6YNIOLWg== )
+deleg.example.com. 3600 IN NS deleg.example.com.
+ 3600 A 192.0.2.1
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 1 15 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ D24JCtCcNzwsY1FXVliAjxMm+x95N2eUTXn0
+ M8NK5glSk1yLtnAUKzHxpRExAJLGUiaG4yPu
+ 2yGZuqwNvJztzw== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 4 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+ 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160229083110 29600 example.com.
+ F7y+xW/C7iICgmZeYrF4e7Yx4kWZAZPAMzlu
+ PtWVuf37ySg1VfEWcQcDP04vF2rXVUqSMEcj
+ bqUVN5W8Hoazxw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160229083110 29600 example.com.
+ MoYrL/lToC4AHo6KCZRiBRmCMWHUAx2Xt32A
+ P4lDpwA+wiBWkCZSfVTh60AosS/BIGtBb2BK
+ mszMx8CLBvkjRg== )
diff --git a/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed
new file mode 100644
index 0000000..a3024d8
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_01.signed
@@ -0,0 +1,70 @@
+; example.com -- missing DNSKEY in type bitmap
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG NSEC3PARAM )
+; dns1.example.com
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ li23VC44fumpMHhKwWug2J1C2fwCMiwgofYO
+ DKydNYsJyYTlyi8ezLJ2KoBlCtOc4Fp0NbqS
+ aN8CKWh7fQVnkQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ Y8olY2OClZgC+QHnOhY52LONVOcctOnl8jNY
+ /c7sCHZO4TdPPDHDhpbVntQD+Vc4fUTx+cXY
+ GrF5sLbhddBJXg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ fx2rZzhyYrp1b4tNH1SmM852VbGEeZdKrD+f
+ ZoInny1m8sovb1J9ORtVbGkOYOnInDMLWMCX
+ fghHC2MafuFV+Q== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 31323 example.com.
+ TcNU6AlrYhJLrNlkfOPJzO6A77j6C39IPoP4
+ OfmY2ClA5Vx2JO0vQ4bIHR7GIW8fiMe6M6tt
+ ZwQImhVWdG414A== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160225083237 29600 example.com.
+ iY0WB0dN1hQXoctaMwvvXzn7paQt5xUyucT3
+ xwo6HAI8Y+OJlecUfOpkkQ9lqIfsqPTXmgbY
+ RieoZGrWR6ZvaQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ QptUkTNS7umNQ5V6Z9DyGl6z+rG7G3TFmHG8
+ p9HGaKifSxjwSFW0nZ9/s86XHQ8ql5+bQmPa
+ xw39ntBmQLVxfg== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ CPx6000z5m1zUUpVhki1u9U7P/WMr7PUJAk3
+ G0w3v+/Lw56mDzYzNuTpPzS0noe0LKuecqRu
+ m99KpLyLOx+9QA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ m2z+hx+8hTA7Phu6QzGJrq+o4MiURpda3fYm
+ 0wTDmXtfPKsHmojGr3kBlvUMg16s2gpvNyCL
+ MSlnJ+7KCkI+Mw== )
diff --git a/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed
new file mode 100644
index 0000000..e3e4940
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec3_wrong_bitmap_02.signed
@@ -0,0 +1,70 @@
+; example.com
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 IN NSEC3 1 0 10 - (
+ 20G1GOL477RO51RK9A9NFD54TFQAL7IQ
+ NS SOA RRSIG DNSKEY NSEC3PARAM )
+; dns1.example.com -- extra type in bitmap - NSEC
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 IN NSEC3 1 0 10 - (
+ UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A
+ A RRSIG NSEC)
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ li23VC44fumpMHhKwWug2J1C2fwCMiwgofYO
+ DKydNYsJyYTlyi8ezLJ2KoBlCtOc4Fp0NbqS
+ aN8CKWh7fQVnkQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ Y8olY2OClZgC+QHnOhY52LONVOcctOnl8jNY
+ /c7sCHZO4TdPPDHDhpbVntQD+Vc4fUTx+cXY
+ GrF5sLbhddBJXg== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ fx2rZzhyYrp1b4tNH1SmM852VbGEeZdKrD+f
+ ZoInny1m8sovb1J9ORtVbGkOYOnInDMLWMCX
+ fghHC2MafuFV+Q== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160225083237 31323 example.com.
+ TcNU6AlrYhJLrNlkfOPJzO6A77j6C39IPoP4
+ OfmY2ClA5Vx2JO0vQ4bIHR7GIW8fiMe6M6tt
+ ZwQImhVWdG414A== )
+ 0 NSEC3PARAM 1 0 10 -
+ 0 RRSIG NSEC3PARAM 7 2 0 (
+ 20840201000000 20160225083237 29600 example.com.
+ iY0WB0dN1hQXoctaMwvvXzn7paQt5xUyucT3
+ xwo6HAI8Y+OJlecUfOpkkQ9lqIfsqPTXmgbY
+ RieoZGrWR6ZvaQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160225083237 29600 example.com.
+ QptUkTNS7umNQ5V6Z9DyGl6z+rG7G3TFmHG8
+ p9HGaKifSxjwSFW0nZ9/s86XHQ8ql5+bQmPa
+ xw39ntBmQLVxfg== )
+
+20G1GOL477RO51RK9A9NFD54TFQAL7IQ.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ CPx6000z5m1zUUpVhki1u9U7P/WMr7PUJAk3
+ G0w3v+/Lw56mDzYzNuTpPzS0noe0LKuecqRu
+ m99KpLyLOx+9QA== )
+UTQVUHU2BLK3DHMRR5T1HD9VTEOHQT0A.example.com. 86400 RRSIG NSEC3 7 3 86400 (
+ 20840201000000 20160225083237 29600 example.com.
+ m2z+hx+8hTA7Phu6QzGJrq+o4MiURpda3fYm
+ 0wTDmXtfPKsHmojGr3kBlvUMg16s2gpvNyCL
+ MSlnJ+7KCkI+Mw== )
diff --git a/tests/knot/semantic_check_data/nsec_broken_chain_01.signed b/tests/knot/semantic_check_data/nsec_broken_chain_01.signed
new file mode 100644
index 0000000..cb41dce
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_broken_chain_01.signed
@@ -0,0 +1,72 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_broken_chain_02.signed b/tests/knot/semantic_check_data/nsec_broken_chain_02.signed
new file mode 100644
index 0000000..5c5f004
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_broken_chain_02.signed
@@ -0,0 +1,65 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ Qwf3qgLbSvE4PmUVU8rpIASe0v1T1K0ie3Lw
+ g+6o3tpBS8vWcmHMUiKns/6rAvoum7vHQRmO
+ dH7X3Pp1/X3xCw== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ 92/D4j7CUCKkykxMzdjfJoaNrMwO93OQtZlB
+ APsfcEyYl+W0sSnow/2RgYvKfX+kdcmp5VXD
+ vQxTGC0VqdMwCQ== )
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ shdsucYBfD8/zV1h1QgUBiC7VgYdFxFEcF1k
+ FQfY+UHkfD/AyOkiFPQxysimgzqJn2/z5Q+v
+ GT1CzzzemgzoXw== )
+ 3600 DNSKEY 256 3 13 (
+ /4RnFpCmaYIIrL/zP1T6LvfhXdpun0ZyYDKL
+ ho0zuUD+RMDe31IQCzr9AuSn1BAIQWIunxFs
+ EaTSlvpiUd+CAg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 31772
+ 3600 DNSKEY 257 3 13 (
+ /KEwa6qUWHdkpEMGX55UaIvl7do5l2IADCDq
+ iNnawoCLu7Tm4MU6ylzYS1htz1mTd8Zcuzl0
+ gkRe4FXwOmOzvQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 14119
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008171141 14119 example.com.
+ FPftK2atu4GMOspSR24p5iIvmq2VKgPJMUTu
+ 5RiwflEf8UgD7s2WFe7A6/JLurwEhqa/313T
+ eEURk7m313h4jQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ +Xvcx4bZ536B8DtNwzurqmPPoDVdtS5nlRhQ
+ pMZ+OLsHECDnFaI50dSw4F1/c3DERz1ktM0+
+ QCC96MZ7QdAYQw== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ 64MPHHZG8wrbAtk+LY/5ISicI1vU7V19q9lF
+ wOm0mcpvoBERyDwadgZpmHsvin1sRt/LZYDr
+ iBKxcnaviHfULg== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008171141 31772 example.com.
+ +/+14BFYf5Iq9IIeX1Oz5XqxsaPaw3T6PTPH
+ neJz6N9QhnI6aKkGZFYBuqY0Zhmcr52zbhPi
+ 1yZAUTP7OvouhA== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008171141 31772 example.com.
+ fPqL9hFml35JLmfX3MA32hMnMhh9UA1Mc2OZ
+ nY+0j4wTtVR0PVMWHOv9UaULzTCM+5mlpFXm
+ nRUMj8sMTGzFzw== )
diff --git a/tests/knot/semantic_check_data/nsec_missing.signed b/tests/knot/semantic_check_data/nsec_missing.signed
new file mode 100644
index 0000000..e901607
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_missing.signed
@@ -0,0 +1,67 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC example.com. A RRSIG NSEC
+; missing NSEC for www.example.com.
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
+
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
diff --git a/tests/knot/semantic_check_data/nsec_multiple.signed b/tests/knot/semantic_check_data/nsec_multiple.signed
new file mode 100644
index 0000000..0cd6aec
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_multiple.signed
@@ -0,0 +1,66 @@
+; not coherent NSEC chain
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC
+www.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ dEcgYVtA8cRE8ErOZGO/aaMat99+KuJdKoDc
+ 0+8fauQ3dcTUHVg2I+v4hdizjlmAJzGXJN+7
+ 6ssZgcvXCnWOsQ== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ 2OEk6Lpt+1c58vnCEHBrV7//7gyoo1bGJSHo
+ k+oWaF9Uh07XVkVWznq6mmCErqukUPLnW1Bn
+ rysjk4i5Yflqkg== )
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ icB72dzHg9d9klcTL/mW53mGIX6KzF0GLWUt
+ DKLCcu2Ailyp3kdM64dyJxRYTr7F7KfxyHi4
+ 3KJtphYNEA6ZWA== )
+ 3600 DNSKEY 256 3 13 (
+ H1roLYze5AZ+ouWMduBJtoJ8N5BPFdF3n6Pv
+ +Nfw5bNHUtCzgvMhmtX2gcRlmZ70Ycv1C/U+
+ mCvLWVdfJm08lA==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 19445
+ 3600 DNSKEY 257 3 13 (
+ MSWkrHjEr7zi143oQdRthBBzl70MXeILunB7
+ 8j55a5a9+Q39YKaIiRM4zyCV6WTXpm9H6eOS
+ RRgdQqGNL1gsKQ==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 23836
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008170543 23836 example.com.
+ ejlk2L0CVBWuAxr1g+qivdvyIXqzp3+9U0tu
+ a2geLUtaVx8ErYnIvUug15S54g75+lZoZ1uK
+ l2WFWuy751kIsw== )
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ 8k4wk4+kCs1kO3+8sL6zZdpkHw0U58oua/Ur
+ C8CHo6TjlLx/jRrLdQKcFy5H7gBMcJY76SDs
+ mT91HuWH+BpwNA== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ 3XbwYx32/Y8sLtQ+dW1lg+s1eaOSZlmkdJeO
+ IsLOAF6U9kq/2zrUTYCtFBMfqs5yYDEISK6X
+ W5UfBBdFRdYzgw== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008170543 19445 example.com.
+ DDTolVJ5Mxfm8srRVi/SRu0+5y3OBTQCVFuQ
+ ywdv4IahQoE11pjXRCBUXvroTeDgoHrmD7PD
+ b1aIBxHLiC/2pg== )
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008170543 19445 example.com.
+ DDhuGYMEij4vbJZlscX3os8qj/wgq55w63jc
+ 8mPr/LquDr6o6lrEYdcnZl4Rz22snnF2+po1
+ 3SEjRSJ0ROmTbw== )
diff --git a/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed b/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed
new file mode 100644
index 0000000..058a0a3
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_wrong_bitmap_01.signed
@@ -0,0 +1,73 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; extra AAAA type in NSEC bitmap
+www.example.com. 86400 NSEC example.com. A RRSIG NSEC AAAA
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
diff --git a/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed b/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed
new file mode 100644
index 0000000..dafdc92
--- /dev/null
+++ b/tests/knot/semantic_check_data/nsec_wrong_bitmap_02.signed
@@ -0,0 +1,73 @@
+example.com. 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+dns1.example.com. 86400 NSEC www.example.com. A RRSIG NSEC
+
+; missing A type in NSEC bitmap
+www.example.com. 86400 NSEC example.com. RRSIG NSEC
+www.example.com. 3600 IN A 192.0.2.2
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ FLR8e2k6u7dhQA1xZ3YMxkvuktoydXC+ZNwl
+ xzW9hLpF3oKoqqY/V+kw7m2OMgnOEu2jWN4Q
+ EETdmMeQzkiuNw== )
+
+; signatures for NSECs
+example.com. 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FHLUUQTvnVboNzGoQVLpwQAcB+fUEF5xQqMQ
+ oKhE86sdvlQUiEfUpv2PJ9y3YfXHeYxJUtvm
+ cY14UkYqsdP3fA== )
+dns1.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ GF3mqBf6Ny481XSbEor1uTzQZtT2DSA/3jU2
+ ZcLXXhlmHG3nI/PB49lG+17O83rDrbhcYc8G
+ cHEbLIGNr/6+Mw== )
+www.example.com. 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160224082919 29600 example.com.
+ FDPJTLixRBZtMFLqk5wfYTSLnLMZiLtN7uTA
+ COEqyphK33oW+7XJzfG6ADvwGewY4hTCPQkk
+ cEg+DBI7qZ88NA== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ xJIoENJ4d24FIVd9ZSGpQlcWN4zuriU90r/H
+ +ufcM2qtWcOGR1M1LVNIAWEVJEcD2dBGA2w1
+ B7Cx+BILQRev8w== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ vBffD+/kBuxUHfeXKYBVYxeMIbuW5f8BstRM
+ XJnC1GTGfdNvb8NknHuv5fEytBmnnpH6f9pC
+ iWLeZzFR1+aJBA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ LMyY8+vWsFB7CziWt8rnR5jfg4Loe/xzy4TQ
+ /ITEDbz5pkoadG+0mqTHQ0F5XCe6ZJPamcyr
+ kcMw0GqUzOVb9w== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160224082919 31323 example.com.
+ tpHcGRuIkul47hHXVpNAOL48c5YYMsaIJkFE
+ rlQi9wU4TCiukdJkLuPk7ykk9XrxbiCB/FwD
+ o63Vcqyy3gZfvA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160224082919 29600 example.com.
+ HlfZThngg+1xglDUh8kjDtzVn5D5a9T3emMt
+ Uxfryu9va7bj+xoK4gLADGau69GCZxJNSvwK
+ TAGEqGRYFSY9Ew== )
diff --git a/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed b/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed
new file mode 100644
index 0000000..28b118c
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_rdata_ttl.signed
@@ -0,0 +1,52 @@
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008172615 16105 example.com.
+ UkbSKt1soIfnM7ZkNAfOcS4D3eHBzMQOef1d
+ bFK+ne+MtJsKEGM9brUD23v0f0CdvteVkeNS
+ 2oRrfrb3avZ08A== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008172615 16105 example.com.
+ Mu/BsXIC10V5uRFUGR42/ntmT5eYt4192AQe
+ a5zdWnLo7A3GYHlPcOcZRMdqvsa3SAPOK2Br
+ UmFkHsWTawhWJQ== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008172615 16105 example.com.
+ IexJzu8x2GxGzGrWlceYZmUbry2D+E67py6B
+ /7j2K5IPjNQVGKbItfvqjQTUm+eVrdcwFbyK
+ iiEuVeU7qG5hIw== )
+ 3600 DNSKEY 256 3 13 (
+ tGxruia7b3JYm32MDdFLYX1M1e44DQJmXpVM
+ EWDjcNulSNY5sWR/zgDzhqiQSKEKCFolwhB/
+ MFVIF71WNjE65Q==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 16105
+ 3600 DNSKEY 257 3 13 (
+ 24gAMJg6uXIBEdWkrAXmwP6znng79lTelLDg
+ WxeHbXriSxVPLSTYxrp7SO1FUi2N03v1RXcn
+ 5jONJdQYlxLtSg==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 17031
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008172615 17031 example.com.
+ Tm4MkXCDkavltvRYnEp/enJzzjyjX3EgI8yY
+ OF2VuJY8uQHD0/uzZF3JTmXj7pkGShAUpFKI
+ Uzn5e3jrGqtMGA== )
+dns1.example.com. 3600 IN A 192.0.2.1
+; wrong RRSIG original-ttl
+ 3600 RRSIG A 13 3 600 (
+ 20601231235959 20201008172615 16105 example.com.
+ 7J01Zyly+ky0F94kfaDtERQDVyxhHexzqETa
+ qgsemJkH0pP9FKsEY/dTkeZUwCY4EFZeps7C
+ AOKyGTKdqR5N7Q== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008172615 16105 example.com.
+ 0evb+3+rXrrx0f8Za//w6q2acUZPvYbW+Ezj
+ BoJFvwBYHrhyiiVHlfUzmr/jJh9cTEdxPnL3
+ ow6ZUsfF0HJ4hg== )
diff --git a/tests/knot/semantic_check_data/rrsig_signed.signed b/tests/knot/semantic_check_data/rrsig_signed.signed
new file mode 100644
index 0000000..2798026
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_signed.signed
@@ -0,0 +1,62 @@
+dns1.example.com. 86400 RRSIG RRSIG 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ DummySignatureDEADBEEF8ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
+
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ kINKkWiBvb9Dpb0vghlLhXyObSzsYYNsOqe9
+ pWJN4lI4F2O3T6biPTQPsq3mYMR+6x9gPr6v
+ ysEPHlGtLdTLag== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ LkagMndC+wJGlQycPDvNmCZ0/QuBB7Zo4UVZ
+ He5jzQrE3Hnq8tn+/QfJ/yn62qCZ87DETwTT
+ rGaLqOTYRb1isg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 7 2 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ YDJ1tQvNlv8Y7cGioq8nkbaETx7wmyJKqa0B
+ 8hDLClYA4nf9UtyVXqZCISa2PlgRdBc5GEEh
+ U5BuLr4wYXqEFA== )
+ 3600 DNSKEY 256 3 7 (
+ AwEAAcvvW/oJAjcRdntRC8J52baXoNFVWOFz
+ oVFe3Vgl8aBBiGh3gnbuNt7xKmy9z2qc2/35
+ MFwieWYfDdgUnPxyKMM=
+ ) ; ZSK; alg = NSEC3RSASHA1; key id = 29600
+ 3600 DNSKEY 257 3 7 (
+ AwEAAeXCF7sHLcFiaCwCFH4xh2CJcCp55i04
+ exG41EtzILS2waabEM5byhRkoylbv91q6HY+
+ JH9YXitS21LMD0Hqp1s=
+ ) ; KSK; alg = NSEC3RSASHA1; key id = 31323
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ FPOm8y3e09jh0fv0ZaOecWbdIXDAoERVKdjz
+ qsg1Etop1n6nDhO/lW3pwOUe02Zq2vretu2W
+ DozlDr5E6ZoqPA== )
+ 3600 RRSIG DNSKEY 7 2 3600 (
+ 20840201000000 20160201000000 31323 example.com.
+ cZTevjvA8UO9Tqet/pbsN0Peep6aN8heyxMK
+ XP/Twsj4u0DeClKeIN7pd7Gi7Aac/UV2dev/
+ x/90SM22VQVpeQ== )
+dns1.example.com. 3600 IN A 192.0.2.1
+ 3600 RRSIG A 7 3 3600 (
+ 20840201000000 20160201000000 29600 example.com.
+ f24sVhH1P/0mEMYTMbFLrWmJtl6kqZF6yzaS
+ TcyK6JhVM4sDT//YnjizJGsTVGSCelz3FxMj
+ LdiUm9AD05uY6A== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 7 3 86400 (
+ 20840201000000 20160201000000 29600 example.com.
+ FgQ4VD1yDeA+uvJ+o8e1F28ijooV1IMfEtki
+ kLbaIvFcgZbPvTnXXHyesHO2OPiRsc7zF576
+ Z6prBT8CkMM7bw== )
diff --git a/tests/knot/semantic_check_data/rrsig_ttl.signed b/tests/knot/semantic_check_data/rrsig_ttl.signed
new file mode 100644
index 0000000..1aeef78
--- /dev/null
+++ b/tests/knot/semantic_check_data/rrsig_ttl.signed
@@ -0,0 +1,52 @@
+example.com. 3600 IN SOA dns1.example.com. hostmaster.example.com. (
+ 2010111220 ; serial
+ 21600 ; refresh (6 hours)
+ 3600 ; retry (1 hour)
+ 604800 ; expire (1 week)
+ 86400 ; minimum (1 day)
+ )
+ 3600 RRSIG SOA 13 2 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ NaUbzn4tb3bsVI4O2YgrefFtZPJSYlLKbVKB
+ HyIqwfQjwdkbIKZ5tqH/IGJagvj8oxeStwF/
+ vEoG9c/o/MNs4g== )
+ 3600 NS dns1.example.com.
+ 3600 RRSIG NS 13 2 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ YZqxQKpj3kxfRHxoQda1z9JD9nmX8uNJTBGV
+ qdMMU3cPOVamTzOqymseQYjBPaaeoxL1kyqk
+ K2w/ixOUCFp8qg== )
+ 86400 NSEC dns1.example.com. NS SOA RRSIG NSEC DNSKEY
+ 86400 RRSIG NSEC 13 2 86400 (
+ 20601231235959 20201008165912 34876 example.com.
+ 88QLNDpFWd2FIag2vcKGvY1HQFVeOaRIiMU5
+ 2VZfLFOPBmuTniTcnPvCt76i5ObPVsWdwJhM
+ /7NVMxoRPfMC1w== )
+ 3600 DNSKEY 256 3 13 (
+ 9+7buhxES5wZQZ54+O1qQGuRcKz3P3URZwws
+ 30CacknPsdcWAy7RN1yYmUjP80geUrxJVQt3
+ boo1BwFW4Rnnsg==
+ ) ; ZSK; alg = ECDSAP256SHA256 ; key id = 34876
+ 3600 DNSKEY 257 3 13 (
+ eYNrBYFUn5JIhTlS3N0i2aFj1YE8127h3tlb
+ VJP9JAfMMxQT+Mg6lwDpUa0oQkNFbEoHhqrD
+ 0pcMvp4VeMgJ7g==
+ ) ; KSK; alg = ECDSAP256SHA256 ; key id = 36952
+ 3600 RRSIG DNSKEY 13 2 3600 (
+ 20601231235959 20201008165912 36952 example.com.
+ AVF7u7FzDx2ORApl74nP2hcJd4Szs1o1LXH5
+ OWe6JULh80kITEb9zogpCryQu41bYSZYuxMk
+ yeblfo1OEI2DZg== )
+dns1.example.com. 3600 IN A 192.0.2.1
+; TTL of RRSIG differs from original-ttl
+ 600 RRSIG A 13 3 3600 (
+ 20601231235959 20201008165912 34876 example.com.
+ +PPg6tDZVS2mbxWXOtVEYTQtjK+CkwRk/WFZ
+ dWgX3rzHPQ9AIexC9vKbXdont3s0xdHpcV/8
+ +Sf+N2h44ZTwMQ== )
+ 86400 NSEC example.com. A RRSIG NSEC
+ 86400 RRSIG NSEC 13 3 86400 (
+ 20601231235959 20201008165912 34876 example.com.
+ OCjWQ/5e4SUIWgR84IJLlghKyuowctiZ+b0q
+ eXB0o2qpcWoX6wfxzMlYxGtpgyq3OWKF+R8H
+ UBVCdT+qBt5VOA== )
diff --git a/tests/knot/test_acl.c b/tests/knot/test_acl.c
new file mode 100644
index 0000000..6a66404
--- /dev/null
+++ b/tests/knot/test_acl.c
@@ -0,0 +1,314 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "test_conf.h"
+#include "libknot/libknot.h"
+#include "knot/updates/acl.h"
+#include "contrib/sockaddr.h"
+
+#define ZONE "example.zone"
+#define ZONE2 "example2.zone"
+#define KEY1 "key1_md5"
+#define KEY2 "key2_md5"
+#define KEY3 "key3_sha256"
+
+static void check_sockaddr_set(struct sockaddr_storage *ss, int family,
+ const char *straddr, int port)
+{
+ int ret = sockaddr_set(ss, family, straddr, port);
+ ok(ret == 0, "set address '%s'", straddr);
+}
+
+void check_update(conf_t *conf, knot_rrset_t *authority, knot_tsig_key_t *key,
+ knot_dname_t *zone_name, bool allowed, const char *desc)
+{
+ struct sockaddr_storage addr;
+ check_sockaddr_set(&addr, AF_INET, "1.2.3.4", 0);
+
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ assert(query);
+ knot_pkt_begin(query, KNOT_AUTHORITY);
+ knot_pkt_put(query, 0, authority, 0);
+
+ knot_pkt_t *parsed = knot_pkt_new(query->wire, query->size, NULL);
+ ok(knot_pkt_parse(parsed, 0) == KNOT_EOK, "Parse update packet");
+
+ conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+
+ bool ret = acl_allowed(conf, &acl, ACL_ACTION_UPDATE, &addr, key,
+ zone_name, parsed);
+ ok(ret == allowed, "%s", desc);
+
+ knot_pkt_free(parsed);
+ knot_pkt_free(query);
+}
+
+static void test_acl_allowed(void)
+{
+ int ret;
+ conf_val_t acl;
+ struct sockaddr_storage addr = { 0 };
+
+ knot_dname_t *zone_name = knot_dname_from_str_alloc(ZONE);
+ ok(zone_name != NULL, "create zone dname");
+ knot_dname_t *zone2_name = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2_name != NULL, "create zone2 dname");
+ knot_dname_t *key1_name = knot_dname_from_str_alloc(KEY1);
+ ok(key1_name != NULL, "create "KEY1);
+ knot_dname_t *key2_name = knot_dname_from_str_alloc(KEY2);
+ ok(key2_name != NULL, "create "KEY2);
+ knot_dname_t *key3_name = knot_dname_from_str_alloc(KEY3);
+ ok(key3_name != NULL, "create "KEY3);
+
+ knot_tsig_key_t key0 = { 0 };
+ knot_tsig_key_t key1 = { DNSSEC_TSIG_HMAC_MD5, key1_name };
+ knot_tsig_key_t key2 = { DNSSEC_TSIG_HMAC_MD5, key2_name };
+ knot_tsig_key_t key3 = { DNSSEC_TSIG_HMAC_SHA256, key3_name };
+
+ const char *conf_str =
+ "key:\n"
+ " - id: "KEY1"\n"
+ " algorithm: hmac-md5\n"
+ " secret: Zm9v\n"
+ " - id: "KEY2"\n"
+ " algorithm: hmac-md5\n"
+ " secret: Zm9v\n"
+ " - id: "KEY3"\n"
+ " algorithm: hmac-sha256\n"
+ " secret: Zm8=\n"
+ "\n"
+ "remote:\n"
+ " - id: remote_v6_ko\n"
+ " address: [ 2009::1 ]\n"
+ " key: key1_md5\n"
+ " - id: remote_v6_ok\n"
+ " address: [ 127.0.0.1, 2001::1 ]\n"
+ " key: key1_md5\n"
+ "\n"
+ "acl:\n"
+ " - id: acl_key_addr\n"
+ " remote: [ remote_v6_ko, remote_v6_ok ]\n"
+ " action: [ transfer ]\n"
+ " - id: acl_deny\n"
+ " address: [ 240.0.0.2 ]\n"
+ " action: [ notify ]\n"
+ " deny: on\n"
+ " - id: acl_no_action_deny\n"
+ " address: [ 240.0.0.3 ]\n"
+ " deny: on\n"
+ " - id: acl_multi_addr\n"
+ " address: [ 192.168.1.1, 240.0.0.0/24 ]\n"
+ " action: [ notify, update ]\n"
+ " - id: acl_multi_key\n"
+ " key: [ key2_md5, key3_sha256 ]\n"
+ " action: [ notify, update ]\n"
+ " - id: acl_range_addr\n"
+ " address: [ 100.0.0.0-100.0.0.5, ::0-::5 ]\n"
+ " action: [ transfer ]\n"
+ " - id: acl_deny_no_action_no_key\n"
+ " address: [ 240.0.0.4 ]\n"
+ " deny: on\n"
+ " - id: acl_notify_key\n"
+ " address: [ 240.0.0.0/24 ]\n"
+ " key: "KEY1"\n"
+ " action: [ notify ]\n"
+ " - id: acl_update_key\n"
+ " key: "KEY1"\n"
+ " update-owner: key\n"
+ " update-type: [ AAAA, A ]\n"
+ " action: [ update ]\n"
+ " - id: acl_update_name\n"
+ " key: "KEY2"\n"
+ " update-owner: name\n"
+ " update-owner-name: [ a, b."KEY2". ]\n"
+ " update-owner-match: equal\n"
+ " action: [ update ]\n"
+ "\n"
+ "zone:\n"
+ " - domain: "ZONE"\n"
+ " acl: [ acl_key_addr, acl_deny, acl_no_action_deny ]\n"
+ " acl: [ acl_multi_addr, acl_multi_key ]\n"
+ " acl: [ acl_range_addr ]\n"
+ " - domain: "ZONE2"\n"
+ " acl: [ acl_deny_no_action_no_key, acl_notify_key ]\n"
+ " - domain: "KEY1"\n"
+ " acl: acl_update_key\n"
+ " - domain: "KEY2"\n"
+ " acl: acl_update_name";
+
+ ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_QUERY, &addr, &key1, zone_name, NULL);
+ ok(ret == true, "Address, key, empty action");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key1, zone_name, NULL);
+ ok(ret == true, "Address, key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key1, zone_name, NULL);
+ ok(ret == false, "Address not match, key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0, zone_name, NULL);
+ ok(ret == false, "Address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key2, zone_name, NULL);
+ ok(ret == false, "Address match, key not match, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "2001::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone_name, NULL);
+ ok(ret == false, "Address, key match, action not match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key0, zone_name, NULL);
+ ok(ret == true, "Second address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone_name, NULL);
+ ok(ret == false, "Second address match, extra key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key0, zone_name, NULL);
+ ok(ret == false, "Denied address match, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.2", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key0, zone_name, NULL);
+ ok(ret == true, "Denied address match, no key, action not match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.3", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key0, zone_name, NULL);
+ ok(ret == false, "Denied address match, no key, no action");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "1.1.1.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_UPDATE, &addr, &key3, zone_name, NULL);
+ ok(ret == true, "Arbitrary address, second key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET, "100.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0, zone_name, NULL);
+ ok(ret == true, "IPv4 address from range, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone_name);
+ ok(acl.code == KNOT_EOK, "Get zone ACL");
+ check_sockaddr_set(&addr, AF_INET6, "::1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_TRANSFER, &addr, &key0, zone_name, NULL);
+ ok(ret == true, "IPv6 address from range, no key, action match");
+
+ acl = conf_zone_get(conf(), C_ACL, zone2_name);
+ ok(acl.code == KNOT_EOK, "Get zone2 ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.4", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone2_name, NULL);
+ ok(ret == false, "Address, key, action, denied");
+
+ acl = conf_zone_get(conf(), C_ACL, zone2_name);
+ ok(acl.code == KNOT_EOK, "Get zone2 ACL");
+ check_sockaddr_set(&addr, AF_INET, "240.0.0.1", 0);
+ ret = acl_allowed(conf(), &acl, ACL_ACTION_NOTIFY, &addr, &key1, zone2_name, NULL);
+ ok(ret == true, "Address, key, action, match");
+
+ knot_rrset_t A;
+ knot_rrset_init(&A, key1_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&A, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &A, &key1, key1_name, true, "Update, tsig, type");
+
+ check_update(conf(), &A, &key2, key2_name, false, "Update, tsig, bad name");
+ knot_rdataset_clear(&A.rrs, NULL);
+
+ knot_rrset_t MX;
+ knot_rrset_init(&MX, key1_name, KNOT_RRTYPE_MX, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&MX, (uint8_t *)"\x00\x00\x00", 3, NULL);
+ check_update(conf(), &MX, &key1, key1_name, false, "Update, tsig, bad type");
+ knot_rdataset_clear(&MX.rrs, NULL);
+
+ knot_rrset_t aA;
+ knot_dname_t *a_key2_name = knot_dname_from_str_alloc("a."KEY2".");
+ ok(a_key2_name != NULL, "create a."KEY2".");
+ knot_rrset_init(&aA, a_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&aA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &aA, &key2, key2_name, true, "Update, tsig, relative name");
+ knot_dname_free(a_key2_name, NULL);
+ knot_rdataset_clear(&aA.rrs, NULL);
+
+ knot_rrset_t bA;
+ knot_dname_t *b_key2_name = knot_dname_from_str_alloc("b."KEY2".");
+ ok(b_key2_name != NULL, "create b."KEY2".");
+ knot_rrset_init(&bA, b_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&bA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &bA, &key2, key2_name, true, "Update, tsig, absolute name");
+ knot_dname_free(b_key2_name, NULL);
+ knot_rdataset_clear(&bA.rrs, NULL);
+
+ knot_rrset_t aaA;
+ knot_dname_t *aa_key2_name = knot_dname_from_str_alloc("a.a."KEY2);
+ ok(aa_key2_name != NULL, "create a.a."KEY2);
+ knot_rrset_init(&aaA, aa_key2_name, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&aaA, (uint8_t *)"\x00\x00\x00\x00", 4, NULL);
+ check_update(conf(), &aaA, &key2, key2_name, false, "Update, tsig, bad name");
+ knot_dname_free(aa_key2_name, NULL);
+ knot_rdataset_clear(&aaA.rrs, NULL);
+
+ conf_free(conf());
+ knot_dname_free(zone_name, NULL);
+ knot_dname_free(zone2_name, NULL);
+ knot_dname_free(key1_name, NULL);
+ knot_dname_free(key2_name, NULL);
+ knot_dname_free(key3_name, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("acl_allowed");
+ test_acl_allowed();
+
+ return 0;
+}
diff --git a/tests/knot/test_changeset.c b/tests/knot/test_changeset.c
new file mode 100644
index 0000000..6775b76
--- /dev/null
+++ b/tests/knot/test_changeset.c
@@ -0,0 +1,166 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <assert.h>
+#include <tap/basic.h>
+
+#include "libknot/errcode.h"
+#include "libknot/error.h"
+#include "knot/updates/changesets.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test with NULL changeset
+ ok(changeset_size(NULL) == 0, "changeset: NULL size");
+ ok(changeset_empty(NULL), "changeset: NULL empty");
+
+ // Test creation.
+ knot_dname_t *d = knot_dname_from_str_alloc("test.");
+ assert(d);
+ changeset_t *ch = changeset_new(d);
+ knot_dname_free(d, NULL);
+ ok(ch != NULL, "changeset: new");
+ if (!ch) {
+ return 1;
+ }
+ ok(changeset_empty(ch), "changeset: empty");
+ ch->soa_to = (knot_rrset_t *)0xdeadbeef;
+ ok(!changeset_empty(ch), "changeset: empty SOA");
+ ch->soa_to = NULL;
+ ok(changeset_size(ch) == 0, "changeset: empty size");
+
+ // Test additions.
+ d = knot_dname_from_str_alloc("non.terminals.test.");
+ assert(d);
+ knot_rrset_t *apex_txt_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600, NULL);
+ assert(apex_txt_rr);
+ uint8_t data[8] = "\7teststr";
+ knot_rrset_add_rdata(apex_txt_rr, data, sizeof(data), NULL);
+
+ int ret = changeset_add_addition(ch, apex_txt_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: add RRSet");
+ ok(changeset_size(ch) == 1, "changeset: size add");
+ ret = changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: rem RRSet");
+ ok(changeset_size(ch) == 0, "changeset: size remove");
+ ok(changeset_empty(ch), "changeset: empty");
+ changeset_add_addition(ch, apex_txt_rr, CHANGESET_CHECK);
+
+ // Add another RR to node.
+ knot_rrset_t *apex_spf_rr = knot_rrset_new(d, KNOT_RRTYPE_SPF, KNOT_CLASS_IN, 3600, NULL);
+ assert(apex_spf_rr);
+ knot_rrset_add_rdata(apex_spf_rr, data, sizeof(data), NULL);
+ ret = changeset_add_addition(ch, apex_spf_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: add multiple");
+
+ // Add another node.
+ knot_dname_free(d, NULL);
+ d = knot_dname_from_str_alloc("here.come.more.non.terminals.test");
+ assert(d);
+ knot_rrset_t *other_rr = knot_rrset_new(d, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600, NULL);
+ assert(other_rr);
+ knot_rrset_add_rdata(other_rr, data, sizeof(data), NULL);
+ ret = changeset_add_addition(ch, other_rr, CHANGESET_CHECK);
+ is_int(KNOT_EOK, ret, "changeset: remove multiple");
+
+ // Test add traversal.
+ changeset_iter_t it;
+ ret = changeset_iter_add(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: create iter add");
+ // Order: non.terminals.test. TXT, SPF, here.come.more.non.terminals.test. TXT.
+ knot_rrset_t iter = changeset_iter_next(&it);
+ bool trav_ok = knot_rrset_equal(&iter, apex_txt_rr, true);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, apex_spf_rr, true);
+ iter = changeset_iter_next(&it);
+ trav_ok = trav_ok && knot_rrset_equal(&iter, other_rr, true);
+
+ ok(trav_ok, "changeset: add traversal");
+
+ iter = changeset_iter_next(&it);
+ changeset_iter_clear(&it);
+ ok(knot_rrset_empty(&iter), "changeset: traversal: skip non-terminals");
+
+ changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+ changeset_add_removal(ch, apex_txt_rr, CHANGESET_CHECK);
+
+ // Test remove traversal.
+ ret = changeset_iter_rem(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: create iter rem");
+ iter = changeset_iter_next(&it);
+ ok(knot_rrset_equal(&iter, apex_txt_rr, true),
+ "changeset: rem traversal");
+ changeset_iter_clear(&it);
+
+ // Test all traversal - just count.
+ ret = changeset_iter_all(&it, ch);
+ is_int(KNOT_EOK, ret, "changeset: create iter all");
+ size_t size = 0;
+ iter = changeset_iter_next(&it);
+ while (!knot_rrset_empty(&iter)) {
+ ++size;
+ iter = changeset_iter_next(&it);
+ }
+ changeset_iter_clear(&it);
+ ok(size == 3, "changeset: iter all");
+
+ // Create new changeset.
+ knot_dname_free(d, NULL);
+ d = knot_dname_from_str_alloc("test.");
+ assert(d);
+ changeset_t *ch2 = changeset_new(d);
+ knot_dname_free(d, NULL);
+ assert(ch2);
+ // Add something to add section.
+ knot_dname_free(apex_txt_rr->owner, NULL);
+ apex_txt_rr->owner = knot_dname_from_str_alloc("something.test.");
+ assert(apex_txt_rr->owner);
+ ret = changeset_add_addition(ch2, apex_txt_rr, CHANGESET_CHECK);
+ assert(ret == KNOT_EOK);
+
+ // Add something to remove section.
+ knot_dname_free(apex_txt_rr->owner, NULL);
+ apex_txt_rr->owner =
+ knot_dname_from_str_alloc("and.now.for.something.completely.different.test.");
+ assert(apex_txt_rr->owner);
+ ret = changeset_add_removal(ch2, apex_txt_rr, CHANGESET_CHECK);
+ assert(ret == KNOT_EOK);
+
+ // Test merge.
+ ret = changeset_merge(ch, ch2, 0);
+ ok(ret == KNOT_EOK && changeset_size(ch) == 5, "changeset: merge");
+
+ // Test cleanup.
+ changeset_clear(ch);
+ ok(changeset_empty(ch), "changeset: clear");
+ free(ch);
+
+ list_t chgs;
+ init_list(&chgs);
+ add_head(&chgs, &ch2->n);
+ changesets_clear(&chgs);
+ ok(changeset_empty(ch2), "changeset: clear list");
+ free(ch2);
+
+ knot_rrset_free(apex_txt_rr, NULL);
+ knot_rrset_free(apex_spf_rr, NULL);
+ knot_rrset_free(other_rr, NULL);
+
+ return 0;
+}
diff --git a/tests/knot/test_conf.c b/tests/knot/test_conf.c
new file mode 100644
index 0000000..cc625ed
--- /dev/null
+++ b/tests/knot/test_conf.c
@@ -0,0 +1,274 @@
+/* Copyright (C) 2022 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/>.
+ */
+
+#define CONFIG_DIR "/tmp"
+
+#include <tap/basic.h>
+
+#include "knot/conf/conf.c"
+#include "test_conf.h"
+
+#define ZONE_ARPA "0/25.2.0.192.in-addr.arpa."
+#define ZONE_ROOT "."
+#define ZONE_1LABEL "x."
+#define ZONE_3LABEL "abc.ab.a."
+#define ZONE_UNKNOWN "unknown."
+
+static void check_name(const char *zone, const char *name, const char *ref)
+{
+ knot_dname_t *z = knot_dname_from_str_alloc(zone);
+
+ char *file = get_filename(conf(), NULL, z, name);
+ ok(file != NULL, "Get zonefile path for %s", zone);
+ if (file != NULL) {
+ ok(strcmp(file, ref) == 0, "Zonefile path compare %s", name);
+ free(file);
+ }
+
+ knot_dname_free(z, NULL);
+}
+
+static void check_name_err(const char *zone, const char *name)
+{
+ knot_dname_t *z = knot_dname_from_str_alloc(zone);
+
+ char *filename = get_filename(conf(), NULL, z, name);
+ ok(filename == NULL, "Invalid name %s", name);
+ free(filename);
+
+ knot_dname_free(z, NULL);
+}
+
+static void test_get_filename(void)
+{
+ int ret = test_conf("", NULL);
+ is_int(KNOT_EOK, ret, "Prepare empty configuration");
+
+ // Name formatter.
+ char *zone = "abc";
+ check_name(zone, "/%s", "/abc");
+
+ zone = ".";
+ check_name(zone, "/%s", "/");
+
+ // Char formatter.
+ zone = "abc.def.g";
+ check_name(zone, "/%c[0]", "/a");
+ check_name(zone, "/%c[3]", "/.");
+ check_name(zone, "/%c[8]", "/g");
+ check_name(zone, "/%c[9]", "/.");
+ check_name(zone, "/%c[10]", "/");
+ check_name(zone, "/%c[255]", "/");
+ check_name(zone, "/%c[0-1]", "/ab");
+ check_name(zone, "/%c[1-1]", "/b");
+ check_name(zone, "/%c[1-3]", "/bc.");
+ check_name(zone, "/%c[1-4]", "/bc.d");
+ check_name(zone, "/%c[254-255]", "/");
+ check_name_err(zone, "/%c");
+ check_name_err(zone, "/%cx");
+ check_name_err(zone, "/%c[a]");
+ check_name_err(zone, "/%c[:]");
+ check_name_err(zone, "/%c[/]");
+ check_name_err(zone, "/%c[-1]");
+ check_name_err(zone, "/%c[256]");
+ check_name_err(zone, "/%c[");
+ check_name_err(zone, "/%c[1");
+ check_name_err(zone, "/%c[1-");
+ check_name_err(zone, "/%c[1-2");
+ check_name_err(zone, "/%c[1-b]");
+ check_name_err(zone, "/%c[8-0]");
+
+ zone = "abcd";
+ check_name(zone, "/%c[2-9]", "/cd.");
+ check_name(zone, "/%c[3]", "/d");
+ check_name(zone, "/%c[4]", "/.");
+
+ zone = ".";
+ check_name(zone, "/%c[0]", "/.");
+ check_name(zone, "/%c[1]", "/");
+
+ // Label formatter.
+ zone = "abc.def.gh";
+ check_name(zone, "/%l[0]", "/gh");
+ check_name(zone, "/%l[1]", "/def");
+ check_name(zone, "/%l[2]", "/abc");
+ check_name(zone, "/%l[3]", "/");
+ check_name(zone, "/%l[255]", "/");
+ check_name(zone, "/%l[0]-%l[1]-%l[2]", "/gh-def-abc");
+ check_name_err(zone, "/%l[0-1]");
+ check_name_err(zone, "/%l[-1]");
+ check_name_err(zone, "/%l[256]");
+
+ zone = ".";
+ check_name(zone, "/%l[0]", "/");
+ check_name(zone, "/%l[1]", "/");
+
+ test_conf_free();
+}
+
+static void test_conf_zonefile(void)
+{
+ int ret;
+ char *file;
+
+ knot_dname_t *zone_arpa = knot_dname_from_str_alloc(ZONE_ARPA);
+ ok(zone_arpa != NULL, "create dname "ZONE_ARPA);
+ knot_dname_t *zone_root = knot_dname_from_str_alloc(ZONE_ROOT);
+ ok(zone_root != NULL, "create dname "ZONE_ROOT);
+ knot_dname_t *zone_1label = knot_dname_from_str_alloc(ZONE_1LABEL);
+ ok(zone_1label != NULL, "create dname "ZONE_1LABEL);
+ knot_dname_t *zone_3label = knot_dname_from_str_alloc(ZONE_3LABEL);
+ ok(zone_3label != NULL, "create dname "ZONE_3LABEL);
+ knot_dname_t *zone_unknown = knot_dname_from_str_alloc(ZONE_UNKNOWN);
+ ok(zone_unknown != NULL, "create dname "ZONE_UNKNOWN);
+
+ const char *conf_str =
+ "template:\n"
+ " - id: default\n"
+ " storage: /tmp\n"
+ "\n"
+ "zone:\n"
+ " - domain: "ZONE_ARPA"\n"
+ " file: dir/a%%b/%s.suffix/%a\n"
+ " - domain: "ZONE_ROOT"\n"
+ " file: /%s\n"
+ " - domain: "ZONE_1LABEL"\n"
+ " file: /%s\n"
+ " - domain: "ZONE_3LABEL"\n";
+
+ ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ // Relative path with formatters.
+ file = conf_zonefile(conf(), zone_arpa);
+ ok(file != NULL, "Get zonefile path for "ZONE_ARPA);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/dir/a%b/0_25.2.0.192.in-addr.arpa.suffix/") == 0,
+ "Zonefile path compare for "ZONE_ARPA);
+ free(file);
+ }
+
+ // Absolute path without formatters - root zone.
+ file = conf_zonefile(conf(), zone_root);
+ ok(file != NULL, "Get zonefile path for "ZONE_ROOT);
+ if (file != NULL) {
+ ok(strcmp(file, "/") == 0,
+ "Zonefile path compare for "ZONE_ROOT);
+ free(file);
+ }
+
+ // Absolute path without formatters - non-root zone.
+ file = conf_zonefile(conf(), zone_1label);
+ ok(file != NULL, "Get zonefile path for "ZONE_1LABEL);
+ if (file != NULL) {
+ ok(strcmp(file, "/x") == 0,
+ "Zonefile path compare for "ZONE_1LABEL);
+ free(file);
+ }
+
+ // Default zonefile path.
+ file = conf_zonefile(conf(), zone_3label);
+ ok(file != NULL, "Get zonefile path for "ZONE_3LABEL);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/abc.ab.a.zone") == 0,
+ "Zonefile path compare for "ZONE_3LABEL);
+ free(file);
+ }
+
+ // Unknown zone zonefile path.
+ file = conf_zonefile(conf(), zone_unknown);
+ ok(file != NULL, "Get zonefile path for "ZONE_UNKNOWN);
+ if (file != NULL) {
+ ok(strcmp(file, "/tmp/unknown.zone") == 0,
+ "Zonefile path compare for "ZONE_UNKNOWN);
+ free(file);
+ }
+
+ test_conf_free();
+ knot_dname_free(zone_arpa, NULL);
+ knot_dname_free(zone_root, NULL);
+ knot_dname_free(zone_1label, NULL);
+ knot_dname_free(zone_3label, NULL);
+ knot_dname_free(zone_unknown, NULL);
+}
+
+static void test_mix_ref(void)
+{
+ const char *conf_string =
+ "remote:\n"
+ " - id: r1\n"
+ " address: ::1\n"
+ " - id: r2\n"
+ " address: ::2\n"
+ " - id: r3\n"
+ " address: ::3\n"
+ " - id: r4\n"
+ " address: ::4\n"
+ " - id: r5\n"
+ " address: ::5\n"
+ "remotes:\n"
+ " - id: rs2\n"
+ " remote: [r2]\n"
+ " - id: rs45\n"
+ " remote: [r4, r5]\n"
+ "\n"
+ "submission:\n"
+ " - id: t1\n"
+ " parent: [r1, rs2, r3, rs45]\n"
+ " - id: t2\n"
+ " parent: [rs45, r2, r1]\n";
+
+ int ret = test_conf(conf_string, NULL);
+ is_int(KNOT_EOK, ret, "Prepare configuration");
+
+ size_t cnt1 = 0;
+ conf_val_t test1 = conf_rawid_get(conf(), C_SBM, C_PARENT, (const uint8_t *)"t1", 3);
+ conf_mix_iter_t iter1;
+ conf_mix_iter_init(conf(), &test1, &iter1);
+ while (iter1.id->code == KNOT_EOK) {
+ cnt1++;
+ conf_mix_iter_next(&iter1);
+ }
+ is_int(5, cnt1, "number of mixed references 1");
+
+ size_t cnt2 = 0;
+ conf_val_t test2 = conf_rawid_get(conf(), C_SBM, C_PARENT, (const uint8_t *)"t2", 3);
+ conf_mix_iter_t iter2;
+ conf_mix_iter_init(conf(), &test2, &iter2);
+ while (iter2.id->code == KNOT_EOK) {
+ cnt2++;
+ conf_mix_iter_next(&iter2);
+ }
+ is_int(4, cnt2, "number of mixed references 2");
+
+ test_conf_free();
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("get_filename");
+ test_get_filename();
+
+ diag("conf_zonefile");
+ test_conf_zonefile();
+
+ diag("mixed references");
+ test_mix_ref();
+
+ return 0;
+}
diff --git a/tests/knot/test_conf.h b/tests/knot/test_conf.h
new file mode 100644
index 0000000..1297ec0
--- /dev/null
+++ b/tests/knot/test_conf.h
@@ -0,0 +1,50 @@
+/* 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/>.
+ */
+
+#pragma once
+
+#include "knot/conf/conf.h"
+#include "libknot/errcode.h"
+
+/* Prepare server configuration. */
+static inline int test_conf(const char *conf_str, const yp_item_t *schema)
+{
+ // Use default schema if not specified.
+ if (schema == NULL) {
+ schema = conf_schema;
+ }
+
+ conf_t *new_conf = NULL;
+ int ret = conf_new(&new_conf, schema, NULL, 2 * 1024 * 1024, CONF_FNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ ret = conf_import(new_conf, conf_str, false, false);
+ if (ret != KNOT_EOK) {
+ conf_free(new_conf);
+ return ret;
+ }
+
+ conf_update(new_conf, CONF_UPD_FNONE);
+
+ return KNOT_EOK;
+}
+
+static inline void test_conf_free(void)
+{
+ conf_update(NULL, CONF_UPD_FNONE);
+}
diff --git a/tests/knot/test_conf_tools.c b/tests/knot/test_conf_tools.c
new file mode 100644
index 0000000..dc83a06
--- /dev/null
+++ b/tests/knot/test_conf_tools.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "knot/conf/tools.h"
+#include "libknot/libknot.h"
+
+static void mod_id_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ mod_id_to_bin,
+ mod_id_to_txt } };
+
+ diag("module id \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(b, val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void mod_id_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TDATA, YP_VDATA = { 0, NULL,
+ mod_id_to_bin,
+ mod_id_to_txt } };
+
+ diag("module id \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Module id tests. */
+ mod_id_test("module/id", "\x06moduleid");
+ mod_id_test("module", "\x06module");
+ mod_id_bad_test("module/", KNOT_EINVAL);
+ mod_id_bad_test("/", KNOT_EINVAL);
+ mod_id_bad_test("/id", KNOT_EINVAL);
+
+ return 0;
+}
diff --git a/tests/knot/test_confdb.c b/tests/knot/test_confdb.c
new file mode 100644
index 0000000..e149f8d
--- /dev/null
+++ b/tests/knot/test_confdb.c
@@ -0,0 +1,489 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+
+#include "test_conf.h"
+#include "knot/conf/confdb.c"
+
+static void check_db_content(conf_t *conf, knot_db_txn_t *txn, int count)
+{
+ ok(db_check_version(conf, txn) == KNOT_EOK, "Version check");
+ if (count >= 0) {
+ ok(conf->api->count(txn) == 1 + count, "Check DB entries count");
+ }
+}
+
+static void check_code(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ uint8_t section_code,
+ const yp_name_t *name,
+ db_action_t action,
+ int ret,
+ uint8_t ref_code)
+{
+ uint8_t code = 0;
+ ok(db_code(conf, txn, section_code, name, action, &code) == ret,
+ "Compare DB code return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t k[64] = { section_code, 0 };
+ memcpy(k + 2, name + 1, name[0]);
+ knot_db_val_t key = { .data = k, .len = 2 + name[0] };
+ knot_db_val_t val;
+
+ ret = conf->api->find(txn, &key, &val, 0);
+ switch (action) {
+ case DB_GET:
+ case DB_SET:
+ ok(code == ref_code, "Compare DB code");
+ is_int(KNOT_EOK, ret, "Find DB code");
+ ok(val.len == 1, "Compare DB code length");
+ ok(((uint8_t *)val.data)[0] == code, "Compare DB code value");
+ break;
+ case DB_DEL:
+ is_int(KNOT_ENOENT, ret, "Find item code");
+ break;
+ }
+}
+
+static void test_db_code(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Add codes.
+ check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 1);
+ check_code(conf, txn, 0, C_LOG, DB_SET, KNOT_EOK, KEY1_FIRST + 1);
+ check_db_content(conf, txn, 2);
+ check_code(conf, txn, 2, C_IDENT, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 3);
+ check_code(conf, txn, 0, C_ZONE, DB_SET, KNOT_EOK, KEY1_FIRST + 2);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_VERSION, DB_SET, KNOT_EOK, KEY1_FIRST + 1);
+ check_db_content(conf, txn, 5);
+
+ // Add existing code (no change).
+ check_code(conf, txn, 0, C_SERVER, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+
+ // Get codes.
+ check_code(conf, txn, 0, C_SERVER, DB_GET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+ check_code(conf, txn, 0, C_RMT, DB_GET, KNOT_ENOENT, 0);
+ check_db_content(conf, txn, 5);
+
+ // Delete not existing code.
+ check_code(conf, txn, 0, C_COMMENT, DB_DEL, KNOT_ENOENT, 0);
+ check_db_content(conf, txn, 5);
+
+ // Delete codes.
+ check_code(conf, txn, 0, C_SERVER, DB_DEL, KNOT_EOK, 0);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_IDENT, DB_DEL, KNOT_EOK, 0);
+ check_db_content(conf, txn, 3);
+
+ // Reuse deleted codes.
+ check_code(conf, txn, 0, C_ACL, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 4);
+ check_code(conf, txn, 2, C_NSID, DB_SET, KNOT_EOK, KEY1_FIRST);
+ check_db_content(conf, txn, 5);
+}
+
+static void check_set(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *data,
+ size_t data_len,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ ok(conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len) == ret,
+ "Check set return");
+
+ if (ret != KNOT_EOK || (key1 == NULL && id == NULL)) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ section_code = 0, item_code = 0; // prevents Wuninitialized
+ ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code) == KNOT_EOK,
+ "Get DB section code");
+ if (key1 != NULL) {
+ ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK,
+ "Get DB item code");
+ } else {
+ item_code = KEY1_ID;
+ }
+
+ uint8_t k[64] = { section_code, item_code };
+ if (id != NULL) {
+ memcpy(k + 2, id, id_len);
+ }
+ knot_db_val_t key = { .data = k, .len = 2 + id_len };
+ knot_db_val_t val;
+
+ ok(conf->api->find(txn, &key, &val, 0) == KNOT_EOK, "Get inserted data");
+ ok(val.len == exp_data_len, "Compare data length");
+ ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data");
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_set(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Set section without item - noop.
+ check_set(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EOK, NULL, 0, NULL, 0);
+
+ // Set singlevalued item.
+ check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\0", 1);
+ check_set(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4);
+
+ // Set multivalued item.
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3);
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7);
+ check_set(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // Set group id.
+ check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, (uint8_t *)"", 0);
+
+ // Set singlevalued item with id.
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\0", 1);
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, (uint8_t *)"a b\0", 4);
+
+ // Set multivalued item with id.
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\0", 1, (uint8_t *)"\x00\x01""\0", 3);
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0", 7);
+ check_set(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"b\0", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // ERR set invalid section.
+ check_set(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR set invalid item.
+ check_set(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR redefine section id.
+ check_set(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_CONF_EREDEFINE,
+ NULL, 0, NULL, 0);
+
+ // ERR set singlevalued item with non-existing id.
+ check_set(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0, NULL, 0);
+}
+
+static void check_get(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ conf_val_t val;
+ ok(conf_db_get(conf, txn, key0, key1, id, id_len, &val) == ret,
+ "Check get return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ ok(val.blob_len == exp_data_len, "Compare data length");
+ ok(val.blob != NULL && memcmp(val.blob, exp_data, exp_data_len) == 0,
+ "Compare data");
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_get(conf_t *conf, knot_db_txn_t *txn)
+{
+ // Get singlevalued item.
+ check_get(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4);
+
+ // Get multivalued item.
+ check_get(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // Get group id.
+ check_get(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"", 0);
+
+ // Get singlevalued item with id.
+ check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"a b\0", 4);
+
+ // Get multivalued item with id.
+ check_get(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ (uint8_t *)"\x00\x01""\0""\x00\x02""a\0""\x00\x02""b\0", 11);
+
+ // ERR get section without item.
+ check_get(conf, txn, C_INCL, NULL, NULL, 0, KNOT_EINVAL, NULL, 0);
+
+ // ERR get invalid section.
+ check_get(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0);
+
+ // ERR get invalid item.
+ check_get(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0);
+
+ // ERR get singlevalued item with non-existing id.
+ check_get(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0);
+}
+
+static void check_unset(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret,
+ const uint8_t *data,
+ size_t data_len,
+ const uint8_t *exp_data,
+ size_t exp_data_len)
+{
+ ok(conf_db_unset(conf, txn, key0, key1, id, id_len, data, data_len, false) == ret,
+ "Check unset return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ ok(db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code) == KNOT_EOK,
+ "Get DB section code");
+ if (key1 != NULL) {
+ ok(db_code(conf, txn, section_code, key1, DB_GET, &item_code) == KNOT_EOK,
+ "Get DB item code");
+ } else {
+ item_code = KEY1_ID;
+ }
+
+ uint8_t k[64] = { section_code, item_code };
+ if (id != NULL) {
+ memcpy(k + 2, id, id_len);
+ }
+ knot_db_val_t key = { .data = k, .len = 2 + id_len };
+ knot_db_val_t val;
+
+ ret = conf->api->find(txn, &key, &val, 0);
+ if (exp_data != NULL) {
+ is_int(KNOT_EOK, ret, "Get deleted data");
+ ok(val.len == exp_data_len, "Compare data length");
+ ok(memcmp(val.data, exp_data, exp_data_len) == 0, "Compare data");
+ } else {
+ is_int(KNOT_ENOENT, ret, "Get deleted data");
+ }
+
+ check_db_content(conf, txn, -1);
+}
+
+static void check_unset_key(
+ conf_t *conf,
+ knot_db_txn_t *txn,
+ const yp_name_t *key0,
+ const yp_name_t *key1,
+ const uint8_t *id,
+ size_t id_len,
+ int ret)
+{
+ ok(conf_db_unset(conf, txn, key0, key1, id, id_len, NULL, 0, true) == ret,
+ "Check unset return");
+
+ if (ret != KNOT_EOK) {
+ return;
+ }
+
+ uint8_t section_code, item_code;
+ ret = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &section_code);
+ if (key1 == NULL && id_len == 0) {
+ is_int(KNOT_ENOENT, ret, "Get DB section code");
+ } else {
+ is_int(KNOT_EOK, ret, "Get DB section code");
+ ret = db_code(conf, txn, section_code, key1, DB_GET, &item_code);
+ is_int(KNOT_ENOENT, ret, "Get DB item code");
+ }
+
+ check_db_content(conf, txn, -1);
+}
+
+static void test_conf_db_unset(conf_t *conf, knot_db_txn_t *txn)
+{
+ // ERR unset section without item.
+ check_unset(conf, txn, C_INCL, NULL, NULL, 0, KNOT_ENOENT,
+ NULL, 0, NULL, 0);
+
+ // ERR unset invalid section.
+ check_unset(conf, txn, C_MASTER, NULL, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR unset invalid item.
+ check_unset(conf, txn, C_SERVER, C_DOMAIN, NULL, 0, KNOT_YP_EINVAL_ITEM,
+ NULL, 0, NULL, 0);
+
+ // ERR unset singlevalued item with non-existing id.
+ check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"idx", 3, KNOT_YP_EINVAL_ID,
+ NULL, 0, NULL, 0);
+
+ // ERR unset singlevalued item invalid value.
+ check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_ENOENT,
+ (uint8_t *)"x\0", 2, NULL, 0);
+
+ // Unset singlevalued item data.
+ check_unset(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a b\0", 4, NULL, 0);
+ // Unset item.
+ check_unset_key(conf, txn, C_SERVER, C_RUNDIR, NULL, 0, KNOT_EOK);
+
+ // Unset multivalued item.
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"a", 2, (uint8_t *)"\x00\x01""\0""\x00\x02""b\0", 7);
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"", 1, (uint8_t *)"\x00\x02""b\0", 4);
+ check_unset(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK,
+ (uint8_t *)"b", 2, NULL, 0);
+ // Unset item.
+ check_unset_key(conf, txn, C_SERVER, C_LISTEN, NULL, 0, KNOT_EOK);
+ // Unset section.
+ check_unset_key(conf, txn, C_SERVER, NULL, NULL, 0, KNOT_EOK);
+
+ // Unset singlevalued item with id - all data at one step.
+ check_unset(conf, txn, C_ZONE, C_FILE, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, NULL, 0);
+
+ // Unset multivalued item with id - all data at one step (non-null data!).
+ check_unset(conf, txn, C_ZONE, C_MASTER, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL + 1, 0, NULL, 0);
+
+ // Unset group id.
+ check_unset(conf, txn, C_ZONE, NULL, (uint8_t *)"id", 2, KNOT_EOK,
+ NULL, 0, NULL, 0);
+}
+
+static void test_conf_db_iter(conf_t *conf, knot_db_txn_t *txn)
+{
+ const size_t total = 4;
+ char names[][10] = { "alfa", "beta", "delta", "epsilon" };
+
+ // Prepare identifiers to iterate through.
+ for (size_t i = 0; i < total; i++) {
+ check_set(conf, txn, C_RMT, NULL, (uint8_t *)names[i],
+ strlen(names[i]), KNOT_EOK, NULL, 0, (uint8_t *)"", 0);
+ }
+
+ // Create section iterator.
+ conf_iter_t iter;
+ int ret = conf_db_iter_begin(conf, txn, C_RMT, &iter);
+ is_int(KNOT_EOK, ret, "Create iterator");
+
+ // Iterate through the section.
+ size_t count = 0;
+ while (ret == KNOT_EOK) {
+ const uint8_t *id;
+ size_t id_len;
+ id = NULL, id_len = 0; // prevents Wuninitialized
+ ret = conf_db_iter_id(conf, &iter, &id, &id_len);
+ is_int(KNOT_EOK, ret, "Get iteration id");
+ ok(id_len == strlen(names[count]), "Compare iteration id length");
+ ok(memcmp(id, names[count], id_len) == 0, "Compare iteration id");
+
+ ok(conf_db_iter_del(conf, &iter) == KNOT_EOK, "Delete iteration key");
+
+ count++;
+ ret = conf_db_iter_next(conf, &iter);
+ }
+ is_int(KNOT_EOF, ret, "Finished iteration");
+ ok(count == total, "Check iteration count");
+
+ // Check empty section.
+ ret = conf_db_iter_begin(conf, txn, C_RMT, &iter);
+ is_int(KNOT_ENOENT, ret, "Create iterator");
+
+ // ERR non-iterable section.
+ ok(conf_db_iter_begin(conf, txn, C_SERVER, &iter) == KNOT_ENOTSUP, "Create iterator");
+
+ // ERR empty section.
+ ok(conf_db_iter_begin(conf, txn, C_ZONE, &iter) == KNOT_ENOENT, "Create iterator");
+
+ // ERR section with no code.
+ ok(conf_db_iter_begin(conf, txn, C_LOG, &iter) == KNOT_ENOENT, "Create iterator");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ok(test_conf("", NULL) == KNOT_EOK, "Prepare configuration");
+ check_db_content(conf(), &conf()->read_txn, 0);
+
+ knot_db_txn_t txn;
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("db_code");
+ test_db_code(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("conf_db_set");
+ test_conf_db_set(conf(), &txn);
+
+ diag("conf_db_get");
+ test_conf_db_get(conf(), &txn);
+
+ diag("conf_db_unset");
+ test_conf_db_unset(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ ok(conf()->api->txn_begin(conf()->db, &txn, 0) == KNOT_EOK, "Begin transaction");
+
+ diag("conf_db_iter");
+ test_conf_db_iter(conf(), &txn);
+
+ conf()->api->txn_abort(&txn);
+
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_confio.c b/tests/knot/test_confio.c
new file mode 100644
index 0000000..f5d3c94
--- /dev/null
+++ b/tests/knot/test_confio.c
@@ -0,0 +1,1100 @@
+/* Copyright (C) 2022 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 <tap/basic.h>
+
+#include "test_conf.h"
+#include "knot/conf/confio.h"
+#include "knot/conf/tools.h"
+#include "libknot/yparser/yptrafo.h"
+#include "contrib/string.h"
+#include "contrib/openbsd/strlcat.h"
+
+#define SKIP_OPENBSD skip("Nested transactions are not supported on OpenBSD");
+#define OUT_LEN 1024
+#define ZONE1 "zone1"
+#define ZONE2 "zone2"
+#define ZONE3 "zone3"
+
+char *format_key(conf_io_t *io)
+{
+ knot_dname_txt_storage_t id;
+ size_t id_len = sizeof(id);
+
+ // Get the textual item id.
+ if (io->id_len > 0 && !io->id_as_data) {
+ if (yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len, id,
+ &id_len, YP_SNOQUOTE) != KNOT_EOK) {
+ return NULL;
+ }
+ }
+
+ // Get the item prefix.
+ const char *prefix = "";
+ switch (io->type) {
+ case NEW: prefix = "+"; break;
+ case OLD: prefix = "-"; break;
+ default: break;
+ }
+
+ // Format the item key.
+ return sprintf_alloc(
+ "%s%.*s%s%.*s%s%s%.*s",
+ prefix, (int)io->key0->name[0], io->key0->name + 1,
+ (io->id_len > 0 && !io->id_as_data ? "[" : ""),
+ (io->id_len > 0 && !io->id_as_data ? (int)id_len : 0), id,
+ (io->id_len > 0 && !io->id_as_data ? "]" : ""),
+ (io->key1 != NULL ? "." : ""),
+ (io->key1 != NULL ? (int)io->key1->name[0] : 0),
+ (io->key1 != NULL ? io->key1->name + 1 : ""));
+}
+
+static int append_data(const yp_item_t *item, const uint8_t *bin, size_t bin_len,
+ char *out, size_t out_len)
+{
+ char buf[YP_MAX_TXT_DATA_LEN + 1] = "\0";
+ size_t buf_len = sizeof(buf);
+
+ int ret = yp_item_to_txt(item, bin, bin_len, buf, &buf_len, YP_SNONE);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ if (strlcat(out, buf, out_len) >= out_len) {
+ return KNOT_ESPACE;
+ }
+
+ return KNOT_EOK;
+}
+
+char *format_data(conf_io_t *io)
+{
+ char out[YP_MAX_TXT_DATA_LEN + 1] = "\0";
+
+ // Return the item identifier as the item data.
+ if (io->id_as_data) {
+ if (append_data(io->key0->var.g.id, io->id, io->id_len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+
+ return strdup(out);
+ }
+
+ // Check for no data.
+ if (io->data.val == NULL && io->data.bin == NULL) {
+ return NULL;
+ }
+
+ const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0;
+
+ // Format explicit binary data value.
+ if (io->data.bin != NULL) {
+ if (append_data(item, io->data.bin, io->data.bin_len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+ // Format multivalued item data.
+ } else if (item->flags & YP_FMULTI) {
+ size_t values = conf_val_count(io->data.val);
+ for (size_t i = 0; i < values; i++) {
+ // Skip other values if known index (counted from 1).
+ if (io->data.index > 0 &&
+ io->data.index != i + 1) {
+ conf_val_next(io->data.val);
+ continue;
+ }
+
+ if (i > 0) {
+ if (strlcat(out, " ", sizeof(out)) >= sizeof(out)) {
+ return NULL;
+ }
+ }
+
+ conf_val(io->data.val);
+ if (append_data(item, io->data.val->data, io->data.val->len,
+ out, sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+
+ conf_val_next(io->data.val);
+ }
+ // Format singlevalued item data.
+ } else {
+ conf_val(io->data.val);
+ if (append_data(item, io->data.val->data, io->data.val->len, out,
+ sizeof(out)) != KNOT_EOK) {
+ return NULL;
+ }
+ }
+
+ return strdup(out);
+}
+
+static int format_item(conf_io_t *io)
+{
+ char *out = (char *)io->misc;
+
+ // Get the item key and data strings.
+ char *key = format_key(io);
+ char *data = format_data(io);
+
+ // Format the item.
+ char *item = sprintf_alloc(
+ "%s%s%s%s",
+ (*out != '\0' ? "\n" : ""),
+ (key != NULL ? key : ""),
+ (data != NULL ? " = " : ""),
+ (data != NULL ? data : ""));
+ free(key);
+ free(data);
+ if (item == NULL) {
+ return KNOT_ENOMEM;
+ }
+
+ // Append the item.
+ if (strlcat(out, item, OUT_LEN) >= OUT_LEN) {
+ free(item);
+ return KNOT_ESPACE;
+ }
+
+ free(item);
+
+ return KNOT_EOK;
+}
+
+static void test_conf_io_begin(void)
+{
+ ok(conf_io_begin(true) == KNOT_TXN_ENOTEXISTS, "begin child txn with no parent");
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
+
+ ok(conf_io_begin(false) == KNOT_TXN_EEXISTS, "begin another parent txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[0]), "check txn depth");
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[i]), "check txn depth");
+ }
+ ok(conf_io_begin(true) == KNOT_TXN_EEXISTS, "begin another child txn");
+ ok(conf()->io.txn == &(conf()->io.txn_stack[CONF_MAX_TXN_DEPTH - 1]),
+ "check txn depth");
+
+ conf_io_abort(false);
+ ok(conf()->io.txn == NULL, "check txn depth");
+#endif
+}
+
+static void test_conf_io_abort(void)
+{
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ // Test child persistence after subchild abort.
+ {
+ char idx[2] = { '0' };
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
+ char idx[2] = { '0' + i };
+ conf_io_abort(true);
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(*data == (idx[0] - 1), "compare txn data '%s'", data);
+ }
+
+ conf_io_abort(false);
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+ // Test child abort with committed subchild.
+ ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn");
+ ok(conf_io_begin(true) == KNOT_EOK, "begin subchild txn");
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_EOK, "set single value");
+ ok(conf_io_commit(true) == KNOT_EOK, "commit subchild txn");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(strcmp(data, "text") == 0, "compare subchild txn data '%s'", data);
+ conf_io_abort(true);
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ conf_io_abort(false);
+
+ // Test unchanged read_txn.
+ val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+#endif
+}
+
+static void test_conf_io_commit(void)
+{
+ ok(conf_io_commit(false) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
+ ok(conf_io_commit(true) == KNOT_TXN_ENOTEXISTS, "commit no txt txn");
+
+#if defined(__OpenBSD__)
+ SKIP_OPENBSD
+#else
+ // Test subchild persistence after commit.
+ {
+ char idx[2] = { '0' };
+ ok(conf_io_begin(false) == KNOT_EOK, "begin parent txn");
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = 1; i < CONF_MAX_TXN_DEPTH; i++) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_begin(true) == KNOT_EOK, "begin child txn %s", idx);
+ ok(conf_io_set("server", "version", NULL, idx) ==
+ KNOT_EOK, "set single value '%s'", idx);
+ }
+
+ for (int i = CONF_MAX_TXN_DEPTH - 1; i > 0; i--) {
+ char idx[2] = { '0' + i };
+ ok(conf_io_commit(true) == KNOT_EOK, "commit child txn %s", idx);
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ const char *data = conf_str(&val);
+ ok(*data == ('0' + CONF_MAX_TXN_DEPTH - 1), "compare txn data '%s'", data);
+ }
+
+ ok(conf_io_commit(false) == KNOT_EOK, "commit parent txn");
+ ok(conf()->io.txn == NULL, "check txn depth");
+
+ // Test child persistence after parent commit.
+ ok(conf_io_begin(false) == KNOT_EOK, "begin new parent txn");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ char idx[2] = { '0' + CONF_MAX_TXN_DEPTH - 1 };
+ const char *data = conf_str(&val);
+ ok(strcmp(data, idx) == 0, "compare final data '%s'", data);
+ conf_io_abort(false);
+
+ // Test unchanged read_txn.
+ val = conf_get_txn(conf(), &conf()->read_txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+#endif
+}
+
+static void test_conf_io_check(void)
+{
+ conf_io_t io = { NULL };
+
+ // ERR no txn.
+ ok(conf_io_check(&io) ==
+ KNOT_TXN_ENOTEXISTS, "check without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // Section check.
+ ok(conf_io_set("remote", "id", NULL, "remote1") ==
+ KNOT_EOK, "set remote id");
+ ok(conf_io_check(&io) ==
+ KNOT_EINVAL, "check missing remote address");
+ ok(io.error.code == KNOT_EINVAL, "compare error code");
+
+ ok(conf_io_set("remote", "address", "remote1", "1.1.1.1") ==
+ KNOT_EOK, "set remote address");
+ ok(conf_io_check(&io) ==
+ KNOT_EOK, "check remote address");
+ ok(io.error.code == KNOT_EOK, "compare error code");
+
+ // Item check.
+ ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
+ KNOT_EOK, "set zone domain "ZONE1);
+ ok(conf_io_set("zone", "master", ZONE1, "remote1") ==
+ KNOT_EOK, "set zone master");
+
+ ok(conf_io_check(&io) ==
+ KNOT_EOK, "check all");
+
+ ok(conf_io_unset("remote", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset remotes");
+
+ ok(conf_io_check(&io) ==
+ KNOT_ENOENT, "check missing master remote");
+ ok(io.error.code == KNOT_ENOENT, "compare error code");
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_set(void)
+{
+ // ERR no txn.
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_TXN_ENOTEXISTS, "set without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_set(NULL, NULL, NULL, NULL) ==
+ KNOT_EINVAL, "set NULL key0");
+ ok(conf_io_set("", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set empty key0");
+ ok(conf_io_set("unknown", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set unknown key0");
+ ok(conf_io_set("server", "unknown", NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "set unknown key1");
+ ok(conf_io_set("include", NULL, NULL, NULL) ==
+ KNOT_YP_ENODATA, "set non-group without data");
+ ok(conf_io_set("server", "background-workers", NULL, "x") ==
+ KNOT_EINVAL, "set invalid data");
+
+ // ERR callback
+ ok(conf_io_set("include", NULL, NULL, "invalid") ==
+ KNOT_EFILE, "set invalid callback value");
+
+ // Single group, no item, no value.
+ ok(conf_io_set("server", NULL, NULL, NULL) ==
+ KNOT_ENOTSUP, "set group no value");
+ // Single group, no item, value.
+ ok(conf_io_set("server", NULL, NULL, "text") ==
+ KNOT_YP_ENOTSUP_DATA, "set group value");
+ // Single group, item, no value.
+ ok(conf_io_set("server", "version", NULL, NULL) ==
+ KNOT_YP_ENODATA, "set group item no value");
+
+ // Single group, single value.
+ ok(conf_io_set("server", "version", NULL, "text") ==
+ KNOT_EOK, "set single value");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "text") == 0, "check entry value");
+
+ // Single group, multi value.
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "set multivalue 1");
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.2") ==
+ KNOT_EOK, "set multivalue 2");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(conf_val_count(&val) == 2, "check entry value count");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+ knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2 != NULL, "create dname "ZONE2);
+ knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
+ ok(zone3 != NULL, "create dname "ZONE3);
+
+ // Multi group no id.
+ ok(conf_io_set("zone", "domain", NULL, NULL) ==
+ KNOT_YP_ENOID, "set zone empty domain");
+
+ // Multi group ids.
+ ok(conf_io_set("zone", "domain", NULL, ZONE1) ==
+ KNOT_EOK, "set zone domain "ZONE1);
+ ok(conf_io_set("zone", NULL, ZONE2, NULL) ==
+ KNOT_EOK, "set zone domain "ZONE2);
+
+ // Multi group, single value.
+ ok(conf_io_set("zone", "file", ZONE1, "name") ==
+ KNOT_EOK, "set zone file");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "name") == 0, "check entry value");
+
+ // Multi group, single value, bad id.
+ ok(conf_io_set("zone", "file", ZONE3, "name") ==
+ KNOT_YP_EINVAL_ID, "set zone file");
+
+ // Multi group, single value, all ids.
+ ok(conf_io_set("zone", "comment", NULL, "abc") ==
+ KNOT_EOK, "set zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "abc") == 0, "check entry value");
+
+ // Prepare different comment.
+ ok(conf_io_set("zone", "domain", NULL, ZONE3) ==
+ KNOT_EOK, "set zone domain "ZONE3);
+ ok(conf_io_set("zone", "comment", ZONE3, "xyz") ==
+ KNOT_EOK, "set zone comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(strcmp(conf_str(&val), "xyz") == 0, "check entry value");
+
+ knot_dname_free(zone1, NULL);
+ knot_dname_free(zone2, NULL);
+ knot_dname_free(zone3, NULL);
+
+ ok(conf_io_commit(false) == KNOT_EOK, "commit txn");
+
+ // Update read-only transaction.
+ ok(conf_refresh_txn(conf()) == KNOT_EOK, "update read-only txn");
+}
+
+static void test_conf_io_unset(void)
+{
+ // ERR no txn.
+ ok(conf_io_unset("server", "version", NULL, "text") ==
+ KNOT_TXN_ENOTEXISTS, "unset without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_unset("", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key0");
+ ok(conf_io_unset("unknown", NULL, NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key0");
+ ok(conf_io_unset("server", "unknown", NULL, NULL) ==
+ KNOT_YP_EINVAL_ITEM, "unset unknown key1");
+ ok(conf_io_unset("include", NULL, NULL, "file") ==
+ KNOT_ENOTSUP, "unset non-group item");
+ ok(conf_io_unset("server", "background-workers", NULL, "x") ==
+ KNOT_EINVAL, "unset invalid data");
+
+ // Single group, single value.
+ ok(conf_io_unset("server", "version", NULL, "") ==
+ KNOT_ENOENT, "unset zero length text value");
+ conf_val_t val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "version", NULL, "bad text") ==
+ KNOT_ENOENT, "unset bad value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "version", NULL, "text") ==
+ KNOT_EOK, "unset explicit value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ ok(conf_io_unset("server", "version", NULL, NULL) ==
+ KNOT_EOK, "unset value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Single group, multi value.
+ ok(conf_io_unset("server", "listen", NULL, "9.9.9.9") ==
+ KNOT_ENOENT, "unset bad value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "unset explicit value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_EOK, "check entry");
+ ok(conf_val_count(&val) == 1, "check entry value count");
+
+ ok(conf_io_unset("server", "listen", NULL, NULL) ==
+ KNOT_EOK, "unset value");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Whole section items.
+ ok(conf_io_unset("server", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset section");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+ knot_dname_t *zone2 = knot_dname_from_str_alloc(ZONE2);
+ ok(zone2 != NULL, "create dname "ZONE2);
+ knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
+ ok(zone3 != NULL, "create dname "ZONE3);
+
+ // Multi group, single value.
+ ok(conf_io_unset("zone", "file", ZONE1, "name") ==
+ KNOT_EOK, "unset zone file");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Multi group, single bad value, all ids.
+ ok(conf_io_unset("zone", "comment", NULL, "other") ==
+ KNOT_EOK, "unset zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_EOK, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, single value (not all match), all ids.
+ ok(conf_io_unset("zone", "comment", NULL, "abc") ==
+ KNOT_EOK, "unset some zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, single value (all match), all ids.
+ ok(conf_io_unset("zone", "comment", NULL, NULL) ==
+ KNOT_EOK, "unset all zones comment");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone3);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, all items, specific id.
+ ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
+ KNOT_EOK, "unset zone items");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_EOK, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // Multi group, all items, all ids.
+ ok(conf_io_unset("zone", NULL, NULL, NULL) ==
+ KNOT_EOK, "unset zone items");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ // Restart transaction.
+ conf_io_abort(false);
+ ok(conf_io_begin(false) == KNOT_EOK, "restart txn");
+
+ // All groups.
+ ok(conf_io_unset(NULL, NULL, NULL, NULL) ==
+ KNOT_EOK, "unset all");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_VERSION);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_get_txn(conf(), conf()->io.txn, C_SERVER, C_LISTEN);
+ ok(val.code == KNOT_ENOENT, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_FILE, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone1);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+ val = conf_zone_get_txn(conf(), conf()->io.txn, C_COMMENT, zone2);
+ ok(val.code == KNOT_YP_EINVAL_ID, "check entry");
+
+ knot_dname_free(zone1, NULL);
+ knot_dname_free(zone2, NULL);
+ knot_dname_free(zone3, NULL);
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_get(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR no txn.
+ ok(conf_io_get("server", "version", NULL, false, &io) ==
+ KNOT_TXN_ENOTEXISTS, "get without active txn");
+
+ // Get current, no active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, true, &io) ==
+ KNOT_EOK, "get current without active txn");
+ ref = "server.version = \"text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_get("", NULL, NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get empty key0");
+ ok(conf_io_get("unknown", NULL, NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get unknown key0");
+ ok(conf_io_get("server", "unknown", NULL, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "get unknown key1");
+ ok(conf_io_get("include", NULL, NULL, true, &io) ==
+ KNOT_ENOTSUP, "get non-group item");
+
+ // Update item in the active txn.
+ ok(conf_io_set("server", "version", NULL, "new text") ==
+ KNOT_EOK, "set single value");
+
+ // Get new, active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, false, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"new text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Get current, active txn.
+ *out = '\0';
+ ok(conf_io_get("server", "version", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi value.
+ *out = '\0';
+ ok(conf_io_get("server", "listen", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Single group.
+ *out = '\0';
+ ok(conf_io_get("server", NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"\n"
+ "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Prepare dnames.
+ knot_dname_t *zone1 = knot_dname_from_str_alloc(ZONE1);
+ ok(zone1 != NULL, "create dname "ZONE1);
+
+ // Multi group, all values, all ids.
+ *out = '\0';
+ ok(conf_io_get("zone", NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone[zone2.].comment = \"abc\"\n"
+ "zone.domain = \"zone3.\"\n"
+ "zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group ids.
+ *out = '\0';
+ ok(conf_io_get("zone", "domain", NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone.domain = \"zone3.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group, all values, single id.
+ *out = '\0';
+ ok(conf_io_get("zone", NULL, ZONE1, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Multi group, single value, single id.
+ *out = '\0';
+ ok(conf_io_get("zone", "file", ZONE1, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "zone[zone1.].file = \"name\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // All groups.
+ *out = '\0';
+ ok(conf_io_get(NULL, NULL, NULL, true, &io) ==
+ KNOT_EOK, "get with active txn");
+ ref = "server.version = \"text\"\n"
+ "server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "zone.domain = \"zone1.\"\n"
+ "zone[zone1.].file = \"name\"\n"
+ "zone[zone1.].comment = \"abc\"\n"
+ "zone.domain = \"zone2.\"\n"
+ "zone[zone2.].comment = \"abc\"\n"
+ "zone.domain = \"zone3.\"\n"
+ "zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ knot_dname_free(zone1, NULL);
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_diff(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR no txn.
+ ok(conf_io_diff("server", "version", NULL, &io) ==
+ KNOT_TXN_ENOTEXISTS, "diff without active txn");
+
+ ok(conf_io_begin(false) == KNOT_EOK, "begin txn");
+
+ // ERR.
+ ok(conf_io_diff("", NULL, NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff empty key0");
+ ok(conf_io_diff("unknown", NULL, NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff unknown key0");
+ ok(conf_io_diff("server", "unknown", NULL, &io) ==
+ KNOT_YP_EINVAL_ITEM, "diff unknown key1");
+ ok(conf_io_diff("include", NULL, NULL, &io) ==
+ KNOT_ENOTSUP, "diff non-group item");
+
+ *out = '\0';
+ ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff no change");
+ ref = "";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Update singlevalued item.
+ ok(conf_io_set("server", "version", NULL, "new text") ==
+ KNOT_EOK, "set single value");
+
+ *out = '\0';
+ ok(conf_io_diff("server", "version", NULL, &io) == KNOT_EOK, "diff single item");
+ ref = "-server.version = \"text\"\n"
+ "+server.version = \"new text\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Update multivalued item.
+ ok(conf_io_unset("server", "listen", NULL, "1.1.1.1") ==
+ KNOT_EOK, "unset multivalue");
+ ok(conf_io_set("server", "listen", NULL, "1.1.1.3") ==
+ KNOT_EOK, "set multivalue");
+
+ *out = '\0';
+ ok(conf_io_diff("server", "listen", NULL, &io) == KNOT_EOK, "diff multi item");
+ ref = "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "+server.listen = \"1.1.1.2\" \"1.1.1.3\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Unset single item.
+ ok(conf_io_unset("zone", "comment", ZONE3, NULL) ==
+ KNOT_EOK, "unset multivalue");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", NULL, ZONE3, &io) == KNOT_EOK, "diff section");
+ ref = "-zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Unset id.
+ ok(conf_io_unset("zone", NULL, ZONE1, NULL) ==
+ KNOT_EOK, "unset id");
+ ok(conf_io_unset("zone", NULL, ZONE2, NULL) ==
+ KNOT_EOK, "unset id");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", NULL, ZONE2, &io) == KNOT_EOK, "diff id section");
+ ref = "-zone.domain = \"zone2.\"\n"
+ "-zone[zone2.].comment = \"abc\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ *out = '\0';
+ ok(conf_io_diff("zone", "domain", NULL, &io) == KNOT_EOK, "diff id");
+ ref = "-zone.domain = \"zone1.\"\n"
+ "-zone.domain = \"zone2.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ *out = '\0';
+ ok(conf_io_diff(NULL, NULL, NULL, &io) == KNOT_EOK, "diff whole change");
+ ref = "-server.version = \"text\"\n"
+ "+server.version = \"new text\"\n"
+ "-server.listen = \"1.1.1.1\" \"1.1.1.2\"\n"
+ "+server.listen = \"1.1.1.2\" \"1.1.1.3\"\n"
+ "-zone.domain = \"zone1.\"\n"
+ "-zone[zone1.].file = \"name\"\n"
+ "-zone[zone1.].comment = \"abc\"\n"
+ "-zone.domain = \"zone2.\"\n"
+ "-zone[zone2.].comment = \"abc\"\n"
+ "-zone[zone3.].comment = \"xyz\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ conf_io_abort(false);
+}
+
+static void test_conf_io_list(void)
+{
+ const char *ref;
+ char out[OUT_LEN];
+
+ conf_io_t io = {
+ .fcn = format_item,
+ .misc = out
+ };
+
+ // ERR.
+ ok(conf_io_list("", NULL, NULL, false, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "list empty key0");
+ ok(conf_io_list("unknown", NULL, NULL, false, true, &io) ==
+ KNOT_YP_EINVAL_ITEM, "list unknown key0");
+ ok(conf_io_list("include", NULL, NULL, false, true, &io) ==
+ KNOT_EOK, "list non-group item");
+ ok(conf_io_list("template", NULL, NULL, false, false, &io) ==
+ KNOT_TXN_ENOTEXISTS, "no active txn");
+
+ // Desc schema.
+ *out = '\0';
+ ok(conf_io_list(NULL, NULL, NULL, true, true, &io) ==
+ KNOT_EOK, "list schema");
+ ref = "server\n"
+ "xdp\n"
+ "control\n"
+ "remote\n"
+ "template\n"
+ "zone\n"
+ "include";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // Desc group.
+ *out = '\0';
+ ok(conf_io_list("server", NULL, NULL, true, true, &io) ==
+ KNOT_EOK, "list group");
+ ref = "server.version\n"
+ "server.listen\n"
+ "server.tcp-idle-timeout\n"
+ "server.tcp-io-timeout\n"
+ "server.tcp-remote-io-timeout\n"
+ "server.tcp-max-clients\n"
+ "server.tcp-reuseport\n"
+ "server.tcp-fastopen\n"
+ "server.quic-max-clients\n"
+ "server.quic-idle-close-timeout\n"
+ "server.quic-outbuf-max-size\n"
+ "server.socket-affinity\n"
+ "server.udp-workers\n"
+ "server.tcp-workers\n"
+ "server.background-workers\n"
+ "server.udp-max-payload\n"
+ "server.udp-max-payload-ipv4\n"
+ "server.udp-max-payload-ipv6\n"
+ "server.edns-client-subnet\n"
+ "server.answer-rotation\n"
+ "server.automatic-acl\n"
+ "server.dbus-event";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List item options.
+ *out = '\0';
+ ok(conf_io_list("zone", "zonefile-load", NULL, true, true, &io) ==
+ KNOT_EOK, "list item options");
+ ref = "zone.zonefile-load = \"opt1\"\n"
+ "zone.zonefile-load = \"opt2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List zone identifiers 1.
+ *out = '\0';
+ ok(conf_io_list("zone", NULL, NULL, false, true, &io) ==
+ KNOT_EOK, "list zone identifiers 1");
+ ref = "zone[zone1.]\n"
+ "zone[zone2.]\n"
+ "zone[zone3.]";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List zone identifiers 2.
+ *out = '\0';
+ ok(conf_io_list("zone", "domain", NULL, false, true, &io) ==
+ KNOT_EOK, "list zone identifiers 2");
+ ref = "zone = \"zone1.\"\n"
+ "zone = \"zone2.\"\n"
+ "zone = \"zone3.\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List item values.
+ *out = '\0';
+ ok(conf_io_list("server", "listen", NULL, false, true, &io) ==
+ KNOT_EOK, "list item values");
+ ref = "server.listen = \"1.1.1.1\" \"1.1.1.2\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+
+ // List multi-group item values.
+ *out = '\0';
+ ok(conf_io_list("zone", "file", "zone1", false, true, &io) ==
+ KNOT_EOK, "list multi-group item values");
+ ref = "zone[zone1.].file = \"name\"";
+ ok(strcmp(ref, out) == 0, "compare result");
+}
+
+static const yp_item_t desc_server[] = {
+ { C_VERSION, YP_TSTR, YP_VNONE },
+ { C_LISTEN, YP_TADDR, YP_VNONE, YP_FMULTI },
+ // Required config cache items - assert fix.
+ { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_IO_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_RMT_IO_TIMEOUT, YP_TINT, YP_VNONE },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_TCP_REUSEPORT, YP_TBOOL, YP_VNONE },
+ { C_TCP_FASTOPEN, YP_TBOOL, YP_VNONE },
+ { C_QUIC_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_QUIC_IDLE_CLOSE, YP_TINT, YP_VNONE },
+ { C_QUIC_OUTBUF_MAX_SIZE, YP_TINT, YP_VNONE },
+ { C_SOCKET_AFFINITY, YP_TBOOL, YP_VNONE },
+ { C_UDP_WORKERS, YP_TINT, YP_VNONE },
+ { C_TCP_WORKERS, YP_TINT, YP_VNONE },
+ { C_BG_WORKERS, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD_IPV4, YP_TINT, YP_VNONE },
+ { C_UDP_MAX_PAYLOAD_IPV6, YP_TINT, YP_VNONE },
+ { C_ECS, YP_TBOOL, YP_VNONE },
+ { C_ANS_ROTATION, YP_TBOOL, YP_VNONE },
+ { C_AUTO_ACL, YP_TBOOL, YP_VNONE },
+ { C_DBUS_EVENT, YP_TOPT, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_xdp[] = {
+ { C_UDP, YP_TBOOL, YP_VNONE },
+ { C_TCP, YP_TBOOL, YP_VNONE },
+ { C_QUIC, YP_TBOOL, YP_VNONE },
+ { C_TCP_MAX_CLIENTS, YP_TINT, YP_VNONE },
+ { C_TCP_INBUF_MAX_SIZE, YP_TINT, YP_VNONE },
+ { C_TCP_OUTBUF_MAX_SIZE,YP_TINT, YP_VNONE },
+ { C_TCP_IDLE_CLOSE, YP_TINT, YP_VNONE },
+ { C_TCP_IDLE_RESET, YP_TINT, YP_VNONE },
+ { C_TCP_RESEND, YP_TINT, YP_VNONE },
+ { C_ROUTE_CHECK, YP_TBOOL, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_control[] = {
+ { C_TIMEOUT, YP_TINT, YP_VNONE },
+ { NULL }
+};
+
+static const yp_item_t desc_remote[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_ADDR, YP_TADDR, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+static const knot_lookup_t opts[] = {
+ { 1, "opt1" },
+ { 2, "opt2" },
+ { 0, NULL }
+};
+
+#define ZONE_ITEMS \
+ { C_FILE, YP_TSTR, YP_VNONE }, \
+ { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \
+ { C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE }, \
+ { C_DNSSEC_VALIDATION, YP_TBOOL, YP_VNONE }, \
+ { C_CATALOG_ROLE, YP_TOPT, YP_VOPT = { opts, 0 } }, \
+ { C_CATALOG_TPL, YP_TREF, YP_VREF = { C_RMT } }, \
+ { C_COMMENT, YP_TSTR, YP_VNONE },
+
+static const yp_item_t desc_template[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ ZONE_ITEMS
+ { NULL }
+};
+
+static const yp_item_t desc_zone[] = {
+ { C_DOMAIN, YP_TDNAME, YP_VNONE },
+ ZONE_ITEMS
+ { NULL }
+};
+
+const yp_item_t test_schema[] = {
+ { C_SRV, YP_TGRP, YP_VGRP = { desc_server } },
+ { C_XDP, YP_TGRP, YP_VGRP = { desc_xdp } },
+ { C_CTL, YP_TGRP, YP_VGRP = { desc_control } },
+ { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } },
+ { C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } },
+ { C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI, { check_zone } },
+ { C_INCL, YP_TSTR, YP_VNONE, YP_FNONE, { include_file } },
+ { NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ok(test_conf("", test_schema) == KNOT_EOK, "Prepare configuration");
+
+ diag("conf_io_begin");
+ test_conf_io_begin();
+
+ diag("conf_io_abort");
+ test_conf_io_abort();
+
+ diag("conf_io_commit");
+ test_conf_io_commit();
+
+ diag("conf_io_check");
+ test_conf_io_check();
+
+ diag("conf_io_set");
+ test_conf_io_set();
+
+ diag("conf_io_unset");
+ test_conf_io_unset();
+
+ diag("conf_io_get");
+ test_conf_io_get();
+
+ diag("conf_io_diff");
+ test_conf_io_diff();
+
+ diag("conf_io_list");
+ test_conf_io_list();
+
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_digest.c b/tests/knot/test_digest.c
new file mode 100644
index 0000000..a694e49
--- /dev/null
+++ b/tests/knot/test_digest.c
@@ -0,0 +1,474 @@
+/* Copyright (C) 2022 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 "knot/zone/digest.h"
+
+#include <string.h>
+#include <tap/basic.h>
+
+#include "knot/zone/zonefile.h"
+#include "libzscanner/scanner.h"
+
+// copy-pasted from knot/zone/zonefile.c
+static void process_data(zs_scanner_t *scanner)
+{
+ zcreator_t *zc = scanner->process.data;
+ if (zc->ret != KNOT_EOK) {
+ scanner->state = ZS_STATE_STOP;
+ return;
+ }
+
+ knot_dname_t *owner = knot_dname_copy(scanner->r_owner, NULL);
+ if (owner == NULL) {
+ zc->ret = KNOT_ENOMEM;
+ return;
+ }
+
+ knot_rrset_t rr;
+ knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class, scanner->r_ttl);
+
+ int ret = knot_rrset_add_rdata(&rr, scanner->r_data, scanner->r_data_length, NULL);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ ret = knot_rrset_rr_to_canonical(&rr);
+ if (ret != KNOT_EOK) {
+ knot_rrset_clear(&rr, NULL);
+ zc->ret = ret;
+ return;
+ }
+
+ zc->ret = zcreator_step(zc, &rr);
+ knot_rrset_clear(&rr, NULL);
+}
+
+static void process_error(zs_scanner_t *s)
+{
+ (void)s;
+ assert(0);
+}
+
+static zone_contents_t *str2contents(const char *zone_str)
+{
+ knot_dname_txt_storage_t origin_str;
+ sscanf(zone_str, "%s", origin_str); // NOTE assuming that first token in zone_str is origin name!
+
+ knot_dname_t *origin = knot_dname_from_str_alloc(origin_str);
+ assert(origin != NULL);
+
+ zone_contents_t *cont = zone_contents_new(origin, false);
+ assert(cont != NULL);
+ knot_dname_free(origin, NULL);
+
+ zcreator_t zc = { cont, true, KNOT_EOK };
+
+ zs_scanner_t sc;
+ ok(zs_init(&sc, origin_str, KNOT_CLASS_IN, 3600) == 0 &&
+ zs_set_input_string(&sc, zone_str, strlen(zone_str)) == 0 &&
+ zs_set_processing(&sc, process_data, process_error, &zc) == 0 &&
+ zs_parse_all(&sc) == 0, "zscanner initialization");
+ zs_deinit(&sc);
+
+ return cont;
+}
+
+static int check_contents(const char *zone_str)
+{
+ zone_contents_t *cont = str2contents(zone_str);
+ int ret = zone_contents_digest_verify(cont);
+ zone_contents_deep_free(cont);
+ return ret;
+}
+
+const char *simple_zone = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *complex_zone = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ a3b69bad980a3504 \n\
+ e1cffcb0fd6397f9 \n\
+ 3848071c93151f55 \n\
+ 2ae2f6b1711d4bd2 \n\
+ d8b39808226d7b9d \n\
+ b71e34b72077f8fe ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+NS2 3600 IN AAAA 2001:db8::63 \n\
+occluded.sub 7200 IN TXT \"I'm occluded but must be digested\" \n\
+sub 7200 IN NS ns1 \n\
+duplicate 300 IN TXT \"I must be digested just once\" \n\
+duplicate 300 IN TXT \"I must be digested just once\" \n\
+foo.test. 555 IN TXT \"out-of-zone data must be excluded\" \n\
+UPPERCASE 3600 IN TXT \"canonicalize uppercase owner names\" \n\
+* 777 IN PTR dont-forget-about-wildcards \n\
+mail 3600 IN MX 20 MAIL1 \n\
+mail 3600 IN MX 10 Mail2.Example. \n\
+sortme 3600 IN AAAA 2001:db8::5:61 \n\
+sortme 3600 IN AAAA 2001:db8::3:62 \n\
+sortme 3600 IN AAAA 2001:db8::4:63 \n\
+sortme 3600 IN AAAA 2001:db8::1:65 \n\
+sortme 3600 IN AAAA 2001:db8::2:64 \n\
+non-apex 900 IN ZONEMD 2018031900 1 1 ( \n\
+ 616c6c6f77656420 \n\
+ 6275742069676e6f \n\
+ 7265642e20616c6c \n\
+ 6f77656420627574 \n\
+ 2069676e6f726564 \n\
+ 2e20616c6c6f7765 )";
+
+const char *multiple_digests = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+example. 86400 IN NS ns1.example. \n\
+example. 86400 IN NS ns2.example. \n\
+example. 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ 62e6cf51b02e54b9 \n\
+ b5f967d547ce4313 \n\
+ 6792901f9f88e637 \n\
+ 493daaf401c92c27 \n\
+ 9dd10f0edb1c56f8 \n\
+ 080211f8480ee306 ) \n\
+example. 86400 IN ZONEMD 2018031900 1 2 ( \n\
+ 08cfa1115c7b948c \n\
+ 4163a901270395ea \n\
+ 226a930cd2cbcf2f \n\
+ a9a5e6eb85f37c8a \n\
+ 4e114d884e66f176 \n\
+ eab121cb02db7d65 \n\
+ 2e0cc4827e7a3204 \n\
+ f166b47e5613fd27 ) \n\
+example. 86400 IN ZONEMD 2018031900 1 240 ( \n\
+ e2d523f654b9422a \n\
+ 96c5a8f44607bbee ) \n\
+example. 86400 IN ZONEMD 2018031900 241 1 ( \n\
+ e1846540e33a9e41 \n\
+ 89792d18d5d131f6 \n\
+ 05fc283e ) \n\
+ns1.example. 3600 IN A 203.0.113.63 \n\
+ns2.example. 86400 IN TXT \"This example has multiple digests\" \n\
+NS2.EXAMPLE. 3600 IN AAAA 2001:db8::63";
+
+const char *signed_zone = "\
+uri.arpa. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2018100702 10800 3600 1209600 3600 \n\
+uri.arpa. 3600 IN RRSIG SOA 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. GzQw+QzwLDJr13REPGVmpEChjD1D2XlX0ie1DnWHpgaEw1E/dhs3lCN3 +BmHd4Kx3tffTRgiyq65HxR6feQ5v7VmAifjyXUYB1DZur1eP5q0Ms2y gCB3byoeMgCNsFS1oKZ2LdzNBRpy3oace8xQn1SpmHGfyrsgg+WbHKCT 1dY= \n\
+uri.arpa. 86400 IN NS a.iana-servers.net. \n\
+uri.arpa. 86400 IN NS b.iana-servers.net. \n\
+uri.arpa. 86400 IN NS c.iana-servers.net. \n\
+uri.arpa. 86400 IN NS ns2.lacnic.net. \n\
+uri.arpa. 86400 IN NS sec3.apnic.net. \n\
+uri.arpa. 86400 IN RRSIG NS 8 2 86400 20210217232440 20210120232440 37444 uri.arpa. M+Iei2lcewWGaMtkPlrhM9FpUAHXFkCHTVpeyrjxjEONeNgKtHZor5e4 V4qJBOzNqo8go/qJpWlFBm+T5Hn3asaBZVstFIYky38/C8UeRLPKq1hT THARYUlFrexr5fMtSUAVOgOQPSBfH3xBq/BgSccTdRb9clD+HE7djpqr LS4= \n\
+uri.arpa. 600 IN MX 10 pechora.icann.org. \n\
+uri.arpa. 600 IN RRSIG MX 8 2 600 20210217232440 20210120232440 37444 uri.arpa. kQAJQivmv6A5hqYBK8h6Z13ESY69gmosXwKI6WE09I8RFetfrxr24ecd nYd0lpnDtgNNSoHkYRSOoB+C4+zuJsoyAAzGo9uoWMWj97/2xeGhf3PT C9meQ9Ohi6hul9By7OR76XYmGhdWX8PBi60RUmZ1guslFBfQ8izwPqzu phs= \n\
+uri.arpa. 3600 IN NSEC ftp.uri.arpa. NS SOA MX RRSIG NSEC DNSKEY ZONEMD \n\
+uri.arpa. 3600 IN RRSIG NSEC 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. dU/rXLM/naWd1+1PiWiYVaNJyCkiuyZJSccr91pJI673T8r3685B4ODM YFafZRboVgwnl3ZrXddY6xOhZL3n9V9nxXZwjLJ2HJUojFoKcXTlpnUy YUYvVQ2kj4GHAo6fcGCEp5QFJ2KbCpeJoS+PhKGRRx28icCiNT4/uXQv O2E= \n\
+uri.arpa. 3600 IN DNSKEY 256 3 8 AwEAAbMxuFuLeVDuOwIMzYOTD/bTREjLflo7wOi6ieIJhqltEzgjNzmW Jf9kGwwDmzxU7kbthMEhBNBZNn84zmcyRSCMzuStWveL7xmqqUlE3swL 8kLOvdZvc75XnmpHrk3ndTyEb6eZM7slh2C63Oh6K8VR5VkiZAkEGg0u ZIT3NjsF \n\
+uri.arpa. 3600 IN DNSKEY 257 3 8 AwEAAdkTaWkZtZuRh7/OobBUFxM+ytTst+bCu0r9w+rEwXD7GbDs0pIM hMenrZzoAvmv1fQxw2MGs6Ri6yPKfNULcFOSt9l8i6BVBLI+SKTY6XXe DUQpSEmSaxohHeRPMQFzpysfjxINp/L2rGtZ7yPmxY/XRiFPSO0myqwG Ja9r06Zw9CHM5UDHKWV/E+zxPFq/I7CfPbrrzbUotBX7Z6Vh3Sarllbe 8cGUB2UFNaTRgwB0TwDBPRD5ER3w2Dzbry9NhbElTr7vVfhaGWeOGuqA UXwlXEg6CrNkmJXJ2F1Rzr9WHUzhp7uWxhAbmJREGfi2dEyPAbUAyCjB qhFaqglknvc= \n\
+uri.arpa. 3600 IN DNSKEY 257 3 8 AwEAAenQaBoFmDmvRT+/H5oNbm0Tr5FmNRNDEun0Jpj/ELkzeUrTWhNp QmZeIMC8I0kZ185tEvOnRvn8OvV39B17QIdrvvKGIh2HlgeDRCLolhao jfn2QM0DStjF/WWHpxJOmE6CIuvhqYEU37yoJscGAPpPVPzNvnL1HhYT aao1VRYWQ/maMrJ+bfHg+YX1N6M/8MnRjIKBif1FWjbCKvsn6dnuGGL9 oCWYUFJ3DwofXuhgPyZMkzPc88YkJj5EMvbMH4wtelbCwC+ivx732l0w /rXJn0ciQSOgoeVvDio8dIJmWQITWQAuP+q/ZHFEFHPlrP3gvQh5mcVS 48eLX71Bq7c= \n\
+uri.arpa. 3600 IN RRSIG DNSKEY 8 2 3600 20210217232440 20210120232440 12670 uri.arpa. DBE2gkKAoxJCfz47KKxzoImN/0AKArhIVHE7TyTwy0DdRPo44V5R+vL6 thUxlQ1CJi2Rw0jwAXymx5Y3Q873pOEllH+4bJoIT4dmoBmPXfYWW7Cl vw9UPKHRP0igKHmCVwIeBYDTU3gfLcMTbR4nEWPDN0GxlL1Mf7ITaC2I oabo79Ip3M/MR8I3Vx/xZ4ZKKPHtLn3xUuJluPNanqJrED2gTslL2xWZ 1tqjsAjJv7JnJo2HJ8XVRB5zBto0IaJ2oBlqcjdcQ/0VlyoM8uOy1pDw HQ2BJl7322gNMHBP9HSiUPIOaIDNUCwW8eUcW6DIUk+s9u3GN1uTqwWz sYB/rA== \n\
+uri.arpa. 3600 IN RRSIG DNSKEY 8 2 3600 20210217232440 20210120232440 30577 uri.arpa. Kx6HwP4UlkGc1UZ7SERXtQjPajOF4iUvkwDj7MEG1xbQFB1KoJiEb/ei W0qmSWdIhMDv8myhgauejRLyJxwxz8HDRV4xOeHWnRGfWBk4XGYwkejV zOHzoIArVdUVRbr2JKigcTOoyFN+uu52cNB7hRYu7dH5y1hlc6UbOnzR pMtGxcgVyKQ+/ARbIqGG3pegdEOvV49wTPWEiyY65P2urqhvnRg5ok/j zwAdMx4XGshiib7Ojq0sRVl2ZIzj4rFgY/qsSO8SEXEhMo2VuSkoJNio fVzYoqpxEeGnANkIT7Tx2xJL1BWyJxyc7E8Wr2QSgCcc+rYL6IkHDtJG Hy7TaQ== \n\
+uri.arpa. 3600 IN ZONEMD 2018100702 1 1 0DBC3C4DBFD75777C12CA19C337854B1577799901307C482E9D91D5D 15CD934D16319D98E30C4201CF25A1D5A0254960 \n\
+uri.arpa. 3600 IN RRSIG ZONEMD 8 2 3600 20210217232440 20210120232440 37444 uri.arpa. QDo4XZcL3HMyn8aAHyCUsu/Tqj4Gkth8xY1EqByOb8XOTwVtA4ZNQORE 1siqNqjtJUbeJPtJSbLNqCL7rCq0CzNNnBscv6IIf4gnqJZjlGtHO30o hXtKvEc4z7SU3IASsi6bB3nLmEAyERdYSeU6UBfx8vatQDIRhkgEnnWU Th4= \n\
+ftp.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^ftp://([^:/?#]*).*$!\\\\1!i\" . \n\
+ftp.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. EygekDgl+Lyyq4NMSEpPyOrOywYf9Y3FAB4v1DT44J3R5QGidaH8l7ZF jHoYFI8sY64iYOCV4sBnX/dh6C1L5NgpY+8l5065Xu3vvjyzbtuJ2k6Y YwJrrCbvl5DDn53zAhhO2hL9uLgyLraZGi9i7TFGd0sm3zNyUF/EVL0C cxU= \n\
+ftp.uri.arpa. 3600 IN NSEC http.uri.arpa. NAPTR RRSIG NSEC \n\
+ftp.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. pbP4KxevPXCu/bDqcvXiuBppXyFEmtHyiy0eAN5gS7mi6mp9Z9bWFjx/ LdH9+6oFGYa5vGmJ5itu/4EDMe8iQeZbI8yrpM4TquB7RR/MGfBnTd8S +sjyQtlRYG7yqEu77Vd78Fme22BKPJ+MVqjS0JHMUE/YUGomPkAjLJJw wGw= \n\
+http.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^http://([^:/?#]*).*$!\\\\1!i\" . \n\
+http.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. eTqbWvt1GvTeXozuvm4ebaAfkXFQKrtdu0cEiExto80sHIiCbO0WL8UD a/J3cDivtQca7LgUbOb6c17NESsrsVkc6zNPx5RK2tG7ZQYmhYmtqtfg 1oU5BRdHZ5TyqIXcHlw9Blo2pir1Y9IQgshhD7UOGkbkEmvB1Lrd0aHh AAg= \n\
+http.uri.arpa. 3600 IN NSEC mailto.uri.arpa. NAPTR RRSIG NSEC \n\
+http.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. R9rlNzw1CVz2N08q6DhULzcsuUm0UKcPaGAWEU40tr81jEDHsFHNM+kh CdOI8nDstzA42aee4rwCEgijxJpRCcY9hrO1Ysrrr2fdqNz60JikMdar vU5O0p0VXeaaJDfJQT44+o+YXaBwI7Qod3FTMx7aRib8i7istvPm1Rr7 ixA= \n\
+mailto.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"!^mailto:(.*)@(.*)$!\\\\2!i\" . \n\
+mailto.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. Ch2zTG2F1plEvQPyIH4Yd80XXLjXOPvMbiqDjpJBcnCJsV8QF7kr0wTL nUT3dB+asQudOjPyzaHGwFlMzmrrAsszN4XAMJ6htDtFJdsgTMP/NkHh YRSmVv6rLeAhd+mVfObY12M//b/GGVTjeUI/gJaLW0fLVZxr1Fp5U5CR jyw= \n\
+mailto.uri.arpa. 3600 IN NSEC urn.uri.arpa. NAPTR RRSIG NSEC \n\
+mailto.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. fQUbSIE6E7JDi2rosah4SpCOTrKufeszFyj5YEavbQuYlQ5cNFvtm8Ku E2xXMRgRI4RGvM2leVqcoDw5hS3m2pOJLxH8l2WE72YjYvWhvnwc5Rof e/8yB/vaSK9WCnqN8y2q6Vmy73AGP0fuiwmuBra7LlkOiqmyx3amSFiz wms= \n\
+urn.uri.arpa. 604800 IN NAPTR 0 0 \"\" \"\" \"/urn:([^:]+)/\\\\1/i\" . \n\
+urn.uri.arpa. 604800 IN RRSIG NAPTR 8 3 604800 20210217232440 20210120232440 37444 uri.arpa. CVt2Tgz0e5ZmaSXqRfNys/8OtVCk9nfP0zhezhN8Bo6MDt6yyKZ2kEEW JPjkN7PCYHjO8fGjnUn0AHZI2qBNv7PKHcpR42VY03q927q85a65weOO 1YE0vPYMzACpua9TOtfNnynM2Ws0uN9URxUyvYkXBdqOC81N3sx1dVEL cwc= \n\
+urn.uri.arpa. 3600 IN NSEC uri.arpa. NAPTR RRSIG NSEC \n\
+urn.uri.arpa. 3600 IN RRSIG NSEC 8 3 3600 20210217232440 20210120232440 37444 uri.arpa. JuKkMiC3/j9iM3V8/izcouXWAVGnSZjkOgEgFPhutMqoylQNRcSkbEZQ zFK8B/PIVdzZF0Y5xkO6zaKQjOzz6OkSaNPIo1a7Vyyl3wDY/uLCRRAH RJfpknuY7O+AUNXvVVIEYJqZggd4kl/Rjh1GTzPYZTRrVi5eQidI1LqC Oeg=";
+
+const char *nsec3_zone = "\
+arpa. 86400 IN SOA a.root-servers.net. nstld.verisign-grs.com. 2021051902 1800 900 604800 86400 \n\
+arpa. 518400 IN NS a.root-servers.net. \n\
+arpa. 518400 IN NS b.root-servers.net. \n\
+arpa. 518400 IN NS c.root-servers.net. \n\
+arpa. 518400 IN NS d.root-servers.net. \n\
+arpa. 518400 IN NS e.root-servers.net. \n\
+arpa. 518400 IN NS f.root-servers.net. \n\
+arpa. 518400 IN NS g.root-servers.net. \n\
+arpa. 518400 IN NS h.root-servers.net. \n\
+arpa. 518400 IN NS i.root-servers.net. \n\
+arpa. 518400 IN NS k.root-servers.net. \n\
+arpa. 518400 IN NS l.root-servers.net. \n\
+arpa. 518400 IN NS m.root-servers.net. \n\
+arpa. 518400 IN RRSIG NS 8 1 518400 20210616170429 20210519170429 29094 arpa. gyq/RdMYEGuTElq9QCbqmZSEUAF3aeBc+MGOMVK0hgmYKfVr8DDrh9UZJy4Ht+24+FHXGgAh8OkW4UbnmiIHQnsSflbQiyHljNYZGX3/H2fUs2FFWAjjAww2iPKuuPUkHgjZZQk0683FQuI9Ium0VK7dXGAvNKFh4Ay4LMjkQ6Y= \n\
+arpa. 86400 IN RRSIG SOA 8 1 86400 20210616170429 20210519170429 29094 arpa. BnSptCxxljkkYItDfsphqUzCz4fALNhOqWrLtYx5aDRWAydcG0N7owhGTqy56VBop+lTzYKmlHfO5/bb/fRCYAXkDhsmVEqS00cDYTqpygTJbVB8Xd+ia1tBeF8cqsbngRhigF4y0cts+bkn7Wrvw21j7nhs01KROimudGH08hs= \n\
+arpa. 86400 IN RRSIG DNSKEY 8 1 86400 20210616170429 20210519170429 18949 arpa. be6sPsu3+7kzDMkAHDsUM0FSoUhULtajWemX95PIVS4wpiEpVMsvF71YLIGRTzw+GfFI2NgsL/idFbUW2Fo7bZIBhbj8JXyZwvsoxt+cLfSfZtVGllKO1XQn5u7/PGU6U8YRSyzRA+ocpdjKqyohkMmOqiqkM7mOSvchDkcZDiw= \n\
+arpa. 3600 IN RRSIG NSEC3PARAM 8 1 3600 20210616170429 20210519170429 29094 arpa. CHmmYN1DJGWraPdMPurcXadDO7ODWoz6gv0B7ln0Gwz5L4Mwb5SEtGAinO5R0T2M4OxQEkN0xhy73VERrZb5FvsxyEGJu0M5S6icvyKkJ1Zq+US5b3FX6MI/bIKu2pI5x7/ubpzWKZJ9itNWBRONBiuBsGT9c3Tb2IreuQWziH0= \n\
+arpa. 86400 IN RRSIG TYPE63 8 1 86400 20210616170429 20210519170429 29094 arpa. Aqb9IQoNaga9euw67potZbiQYeyEAqd/zVYhDFxfLNfC4Qf6v7aPxW8Tyl+foNob91/KX5JGcS5tD4pq+G+IV+heLRH57s+moF3C0lsid8oZLqCbctmR/hr0YUQc5+dGQ/iy2erEPZq1W4eLsWX+YlUsQfajb5y4ggp7OMTmRuY= \n\
+arpa. 86400 IN DNSKEY 256 3 8 AwEAAdMaRW2okM0GrfInisiH9HWsqokdnmeXnJjKUwVQ8dy5sxm0DyCtzNapj54SF4ofgJxYufQCzYoe3Y3WsB6dKW15pTvu6ggqwuTTxvAnkMSHAlMGBE0sybRBIM38WswPcjAXmpITj7Zvgm8qh80dcusK5vwqJhb2CDWHRezUwiIB ;{id = 29094 (zsk), size = 1024b} \n\
+arpa. 86400 IN DNSKEY 257 3 8 AwEAAdQP1t2ookuQYFNUNGDmLHcoA6LFSImvULaUgChKiIO6Vv5yDyHB0Ng6ZkfHM0586cLcbXNBLj/9u5A4vqzOFj8phzW4WLZREZBLYMcuHhvQdqzuDJ0J5mxmLLis5eNaCwukVm6Zpf/otzCJsx9LyrhQBTyx6FF+h7dbSCvjh7tD ;{id = 18949 (ksk), size = 1024b} \n\
+arpa. 3600 IN NSEC3PARAM 1 0 1 - \n\
+arpa. 86400 IN TYPE63 \\# 54 7876cdfe01019a84145013e13e3de2328868888c65aa46b7381213990f83d496c642d2324029cc852e09bffa38afd8e9197977776591 \n\
+0js82oec35lbbc4hl35476cm5icacksf.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. PRVkH4+Nm17QlFgwFLnoqwaiIwWZ4pvscanHdMb6HOKkSxwtDoWAGhZubvYGt/Je735nQkGQPPXW2tkMkJa3D7e6RkX/8AoxcqqXOimC6BlG6LuSL4rSousDlbrulyh87qgIHXkUtrHyYUNAMZMKOjMHo7t5IxwjBO0SGADoglk= \n\
+0js82oec35lbbc4hl35476cm5icacksf.arpa. 86400 IN NSEC3 1 0 1 - 2UB8EN7BK0T6DENIGO3I729IVQVME3VE NS \n\
+2ub8en7bk0t6denigo3i729ivqvme3ve.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. JsSiqDiPs0juQxKEcCKTFvKXzUdvIvCILEzcN79+qAxaiQuulHUxTSMDvrsxm83m9juvoOUYtBlPyZdI9erAfiEkpF71ZIl8iP7AKGgqTeV1C4SHnf2KsFi69qimdLbWeIfFGYEq+54Vj5vF1SrRounvj63avhI/Zf0tTWz11+4= \n\
+2ub8en7bk0t6denigo3i729ivqvme3ve.arpa. 86400 IN NSEC3 1 0 1 - 3MKQ4F9MV3H6JSJNUJ6G31KRJLHKN9KJ NS DS RRSIG \n\
+3mkq4f9mv3h6jsjnuj6g31krjlhkn9kj.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. NAt7ul6uWzK19LyTcxbtfIt0SppVHyVjj4S/j0zxqcOH7gkJwf36+uIsb0lP7QzdYoB7dDeMFKnZfOCjBu+OkXTnOmfdwS5XA5OTM3dpi6g8plVRkcBDoWqz+UtQljD66A2XyuVl5vBmhP3OWe8TnlnA3jrHYO5zneEM/MdsoEE= \n\
+3mkq4f9mv3h6jsjnuj6g31krjlhkn9kj.arpa. 86400 IN NSEC3 1 0 1 - BA4462JFP3IQK2KT4COIMT6532KSV55K NS DS RRSIG \n\
+as112.arpa. 172800 IN NS a.iana-servers.net. \n\
+as112.arpa. 172800 IN NS b.iana-servers.net. \n\
+as112.arpa. 172800 IN NS c.iana-servers.net. \n\
+as112.arpa. 86400 IN DS 20236 8 1 1307e5595598b25fe2eb07bcef767c9d96c3ecdc \n\
+as112.arpa. 86400 IN DS 20236 8 2 72c9e5d15accc54a32c8c76fe5944bcbf3aabc2b13dc417609763e57bd89d515 \n\
+as112.arpa. 86400 IN DS 49400 8 1 0236339d6c1fb0fdf6069a9babe455b443fe2f95 \n\
+as112.arpa. 86400 IN DS 49400 8 2 f8e230e43e20e14200e46beb6e0a67ced274790c8c8c169df7fec5fb7dfa321f \n\
+as112.arpa. 86400 IN DS 53690 8 1 85d712965f3aa6556f40e11ba29c638565444acf \n\
+as112.arpa. 86400 IN DS 53690 8 2 354c6ef7b8b46a4c87ce6a21f3a9043898e68427ad64d029097ce2a38933b82e \n\
+as112.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. Hs6t8f1s8NCPO1yzQIqCWWpGADwHqTVLCRVJIxMkpiWpDPP8zXxQRFp2BHNQ8jAcsp5w5OwIfIR27+5N7O73/y5qjcjDe6Yyzeh7L/nut0fuOuqne47a6VkuXJHmdilGeNFitAFZ+1iP9KnFVxb3NxNLByemx8mO30jYDw14O4Y= \n\
+ba4462jfp3iqk2kt4coimt6532ksv55k.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. MrpAQuo8eH4CAA2jjsLHGiMJ8DexXMDI7LHzQbX7k5L4oUTtBNoTnKFdxqKdxZoEXvO39GB5s0nD0qgR8g5xFAFfj+pcF2y4GC+LqXqV5N6gXKa23zEEN5mfxSuwnQ/JXw95ct2IuQkuU80MIU0ZdE/FVhSyHnlJYMGE3uB2DyY= \n\
+ba4462jfp3iqk2kt4coimt6532ksv55k.arpa. 86400 IN NSEC3 1 0 1 - C26TIAI64HA5JPB4P8KII6P9JHH3TJFH NS DS RRSIG \n\
+c26tiai64ha5jpb4p8kii6p9jhh3tjfh.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. FSQuCmqKEUtYHqhkXDC8uikAIi5ZpMtS14jeaeWEn6Mip3uP1pFNuSQHgFhX9L20hdbeuOG3ribTqs3d4kz9VQ51g4KqD3uhHMVuQZyzpBJWq4Xwynt9cetvSK0f/kaf/wtAARo9HLkciJTBYiYUmYZVdmknIto4TqDNy2kkMrA= \n\
+c26tiai64ha5jpb4p8kii6p9jhh3tjfh.arpa. 86400 IN NSEC3 1 0 1 - DKAS8UE0E261D6338P2GMF52ALH64LA6 NS DS RRSIG \n\
+dkas8ue0e261d6338p2gmf52alh64la6.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. pPD9lqm6kAoLwagCrQwBWBq4McfrHywg4RkQ20ZjuVcnmopggO6UkjlmYUnBn53Si5eqRY9CwtSEvYjKztXcyXnkwbD1xWExAsYucRYVbUPZmOllulYezphTHi1Qp7fRrhEjb/TCYcBUXvLJfU+S9OeVqefruYnIw3VevMPp518= \n\
+dkas8ue0e261d6338p2gmf52alh64la6.arpa. 86400 IN NSEC3 1 0 1 - EARMJ48JEL1C2RDHIGD36N68U3V8Q1KV NS DS RRSIG \n\
+e164.arpa. 172800 IN NS ns3.lacnic.net. \n\
+e164.arpa. 172800 IN NS ns3.afrinic.net. \n\
+e164.arpa. 172800 IN NS ns4.apnic.net. \n\
+e164.arpa. 172800 IN NS pri.authdns.ripe.net. \n\
+e164.arpa. 172800 IN NS rirns.arin.net. \n\
+e164.arpa. 86400 IN DS 46334 8 2 550664875d1121c6edd01f9602577640fed5ad19a749ae1e3fd68476af454578 \n\
+e164.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. A07roaG8r7ns0YydNMhaURb741akipIL8UCgRRMAs3BzzneUtXW3EmS50C7vxb5ikH84a39FerXHOetifGTKETjVMtuQmdPw1F8ClHMkWfdRyR5a+lWwosV3fgnSItoekfbggUZop1dZxzie93pv4RM89Jf/SMlOW/3bYJ1p7Hk= \n\
+earmj48jel1c2rdhigd36n68u3v8q1kv.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. yNYXtZ4dGDdJW3VNoLRtktV93mZmQsQv3Tvy6+iBTGx+W7T0ipSCZq+l5yvblfqGKXXnWWzYf/xKktaLmXnAzvdsacWaKGtudvvtSwLkhlxNWlL018Eoe2md0tsSLd5tSiTbufahrd4p1lv09ne//sGoSw/amfvY5hsRvmnhNhA= \n\
+earmj48jel1c2rdhigd36n68u3v8q1kv.arpa. 86400 IN NSEC3 1 0 1 - H2D0RTQ108UOOUB5UDNN9D2PGQBVABC9 NS DS RRSIG \n\
+h2d0rtq108uooub5udnn9d2pgqbvabc9.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. isNpvWJ3TpDmEl66a9J9Q2GdlNqh9HculGjNFVIbiSfTb5aNgCITkgrKSoxjfZ8go3pDSeqwo5fhaBlbZQ4xGNGlc/T5U2qh2hJPGZpBwHYkR9a1YzMhzMx33oRXfMzsuC+6sasS8BLRHPmS4X89jPeA+lItEJPd1rQlHb1wt1I= \n\
+h2d0rtq108uooub5udnn9d2pgqbvabc9.arpa. 86400 IN NSEC3 1 0 1 - KSH70CK6POGI86ENT4ONT3I9UJ71QE8K NS SOA RRSIG DNSKEY NSEC3PARAM TYPE63 \n\
+home.arpa. 172800 IN NS blackhole-1.iana.org. \n\
+home.arpa. 172800 IN NS blackhole-2.iana.org. \n\
+in-addr.arpa. 172800 IN NS a.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS b.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS c.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS d.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS e.in-addr-servers.arpa. \n\
+in-addr.arpa. 172800 IN NS f.in-addr-servers.arpa. \n\
+in-addr.arpa. 86400 IN DS 47054 8 2 5cafccec201d1933b4c9f6a9c8f51e51f3b39979058ac21b8df1b1f281cbc6f2 \n\
+in-addr.arpa. 86400 IN DS 53696 8 2 13e5501c56b20394da921b51412d48b7089c5eb6957a7c58553c4d4d424f04df \n\
+in-addr.arpa. 86400 IN DS 63982 8 2 aaf4fb5d213ef25ae44679032ebe3514c487d7abd99d7f5fec3383d030733c73 \n\
+in-addr.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. lr32Q5rTcwVyBASuYq2Mc1t8XPCSSXJDNtK+MzisWifCZ0b0m/GARo34QKR2y3afqeFdqVXWrYrBVjAF2Rg21izsWqpMNyfLloesNNl63A9uQi4dFT3Zfz3OdQOGhWcy51ydn8KVtieIubRTBQAgExgZsDzyRC4PXjzh4Jj872g= \n\
+in-addr-servers.arpa. 172800 IN NS a.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS b.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS c.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS d.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS e.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 172800 IN NS f.in-addr-servers.arpa. \n\
+in-addr-servers.arpa. 86400 IN DS 1987 8 2 dacfdeb02a489a514c6408d0d54e0904fe6e09a6e111abc9eacb27f6552805e1 \n\
+in-addr-servers.arpa. 86400 IN DS 45104 8 2 50136f7a8d3ffe4f9887ad234ff8ce945cabd331feb12569b2f61f99ce40fdbf \n\
+in-addr-servers.arpa. 86400 IN DS 62996 8 2 836537710efc1e5570e3aeff7c0c80d3957a16ddf8005034bc9082898968dc81 \n\
+in-addr-servers.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. j+2AVMMc1xfd/ua7lHpNQUr95kUTcr8SIQJk6prTkYnPdDvMNZPIhhdVNw7WzFjIvGLF3iumbYY46I3KN3P1eZUKtn0OFvTZ/UG/tlbWaj473XNxWnbwp8sPuT46nuLH6P14gNEhbPGGrh2VE+hFPkM/4ZdfwlCbDC5vEsQNYko= \n\
+a.in-addr-servers.arpa. 172800 IN A 199.180.182.53 \n\
+a.in-addr-servers.arpa. 172800 IN AAAA 2620:37:e000::53 \n\
+b.in-addr-servers.arpa. 172800 IN A 199.253.183.183 \n\
+b.in-addr-servers.arpa. 172800 IN AAAA 2001:500:87::87 \n\
+c.in-addr-servers.arpa. 172800 IN A 196.216.169.10 \n\
+c.in-addr-servers.arpa. 172800 IN AAAA 2001:43f8:110::10 \n\
+d.in-addr-servers.arpa. 172800 IN A 200.10.60.53 \n\
+d.in-addr-servers.arpa. 172800 IN AAAA 2001:13c7:7010::53 \n\
+e.in-addr-servers.arpa. 172800 IN A 203.119.86.101 \n\
+e.in-addr-servers.arpa. 172800 IN AAAA 2001:dd8:6::101 \n\
+f.in-addr-servers.arpa. 172800 IN A 193.0.9.1 \n\
+f.in-addr-servers.arpa. 172800 IN AAAA 2001:67c:e0::1 \n\
+ip6.arpa. 172800 IN NS a.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS b.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS c.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS d.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS e.ip6-servers.arpa. \n\
+ip6.arpa. 172800 IN NS f.ip6-servers.arpa. \n\
+ip6.arpa. 86400 IN DS 13880 8 2 068554efcb5861f42af93ef8e79c442a86c16fc5652e6b6d2419ed527f344d17 \n\
+ip6.arpa. 86400 IN DS 45094 8 2 e6b54e0a20ce1edbfcb6879c02f5782059cecb043a31d804a04afa51af01d5fb \n\
+ip6.arpa. 86400 IN DS 64060 8 2 8a11501086330132be2c23f22dedf0634ad5ff668b4aa1988e172c6a2a4e5f7b \n\
+ip6.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. aNklM0l2ixPusry6KMt0PYGuKgLXqAJArq3KSZgG0QgMjGC0ChVwAO2+vq4wwR8QuqA6vAWHKKpw79l8MYV9I7+a50WPFyEOugl1s+konVjzkgMboPaOZbg52g47mPdQ7Q0N9MPLA8/FJx13cHauimQjZ+1FOiiWhveqgR2Jg8o= \n\
+ip6-servers.arpa. 172800 IN NS a.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS b.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS c.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS d.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS e.ip6-servers.arpa. \n\
+ip6-servers.arpa. 172800 IN NS f.ip6-servers.arpa. \n\
+ip6-servers.arpa. 86400 IN DS 16169 8 2 27fb5354c3c011c2851ee25ba32929b645d63262779ac101a6f28cd631991269 \n\
+ip6-servers.arpa. 86400 IN DS 19720 8 2 f154d00f5759c274de9cad621910cc0b87d720d35b7de4b0b566e135196c38e2 \n\
+ip6-servers.arpa. 86400 IN DS 54832 8 2 ff0d5f44a086a7a31b99c81cfd1135524b5896878e6de78f12b3f609bf7279dc \n\
+ip6-servers.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. fYShlxJWViKV2SbFCqyxUa64AKAedJ2udqcw/VtKNxg2T6i5IQzFc2aPB7V/+MtE64vHWwbrThgOvNC4Xmc7jVqKNsSc1X4Q8ZSQy+/CgmS5pBkI4XpLBb6kTUJMGorgAOI1ek1OMpl25mGmeJ6lE8e5PTNUisz/7ybIx5pBTz0= \n\
+a.ip6-servers.arpa. 172800 IN A 199.180.182.53 \n\
+a.ip6-servers.arpa. 172800 IN AAAA 2620:37:e000::53 \n\
+b.ip6-servers.arpa. 172800 IN A 199.253.182.182 \n\
+b.ip6-servers.arpa. 172800 IN AAAA 2001:500:86::86 \n\
+c.ip6-servers.arpa. 172800 IN A 196.216.169.11 \n\
+c.ip6-servers.arpa. 172800 IN AAAA 2001:43f8:110::11 \n\
+d.ip6-servers.arpa. 172800 IN A 200.7.86.53 \n\
+d.ip6-servers.arpa. 172800 IN AAAA 2001:13c7:7012::53 \n\
+e.ip6-servers.arpa. 172800 IN A 203.119.86.101 \n\
+e.ip6-servers.arpa. 172800 IN AAAA 2001:dd8:6::101 \n\
+f.ip6-servers.arpa. 172800 IN A 193.0.9.2 \n\
+f.ip6-servers.arpa. 172800 IN AAAA 2001:67c:e0::2 \n\
+ipv4only.arpa. 172800 IN NS a.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS b.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS c.iana-servers.net. \n\
+ipv4only.arpa. 172800 IN NS ns.icann.org. \n\
+iris.arpa. 172800 IN NS a.iana-servers.net. \n\
+iris.arpa. 172800 IN NS b.iana-servers.net. \n\
+iris.arpa. 172800 IN NS c.iana-servers.net. \n\
+iris.arpa. 172800 IN NS ns3.lacnic.net. \n\
+iris.arpa. 172800 IN NS ns4.apnic.net. \n\
+iris.arpa. 86400 IN DS 38534 8 2 163416c9dcaf8d1babfec16552ed109029607907ab80b195e1dab40f1792a59c \n\
+iris.arpa. 86400 IN DS 39464 8 2 1e09a2d6374800d54cfd0e52293906ccf7db7e923dcab7015e4bb697d76d9846 \n\
+iris.arpa. 86400 IN DS 44285 8 2 05cbf77375a8bf5702cf8e261ff947be8c8ab7a0b9485a0241edcfe2f155c7f3 \n\
+iris.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. oikOvs9AfaPv1Po/E76SZ7VBoYjqHqzZEzrA0N4gWXlemmsUKyXh9fiXqtusFIZD7QUBJMvOYkIpWnAOliWnk/oj4lmmwnYMqqLWDMWVoXiUAUtmwQHm89cAjyWc9nRuDVBweKtqH5GQKtEWxu4nkKPIbuUVNHBgxtKZP7Jbzic= \n\
+ksh70ck6pogi86ent4ont3i9uj71qe8k.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. YPnC0imYz+x2dNwUQwvp2CB1Ini1dEcn9Vur9T4KwzAMqVr+PPkheMRiIQcAbmkSLG1D1p/qVzaFEC7ixlaxuEFlvGwM+c5OvukbWek1QtdCDJpgtse3HBajoRTgBDGRwvj+DFej9ppygZpe+vlgSDmiC2fgPMhcG4Z6jMmVAec= \n\
+ksh70ck6pogi86ent4ont3i9uj71qe8k.arpa. 86400 IN NSEC3 1 0 1 - MKQDDR5C3MPRP6DRU5TO19BB27TDVCVT NS DS RRSIG \n\
+mkqddr5c3mprp6dru5to19bb27tdvcvt.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. sUTu2ijBQlhCmn/fNl8O+UofW4ERQ0tgmK0LY8ggHCnvY26k4RCrGieZ6YXl8lCereSyx1DEPuScBA7YRCUEw/FtrW8rCKMo+wQhb4Uon2UUZRl/mrjNNsYxtYwjIN7u/BzfDhBHq2/8vVCybAS8GhqqJhOYpEcDgsITuDKVFOE= \n\
+mkqddr5c3mprp6dru5to19bb27tdvcvt.arpa. 86400 IN NSEC3 1 0 1 - SRGGVLP1DI07IJT2IA31AGJRPFCNC616 NS DS RRSIG \n\
+srggvlp1di07ijt2ia31agjrpfcnc616.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. ep49bJfQ1c1dNMIlFO+EgeG4iW7pHyJvKbK6MJBBj/LJwVfhzwTa8ellqgHp3AH63j8tNPutowc1shlQwE7G/f3KfiVBUwPtAZHtqNYBFdNm0WdxoqRueJmyVR0h+vUfY+r1F4IYzwfjn+ldfj5lhKqQ+gX2HFR3M/FI6H97nHQ= \n\
+srggvlp1di07ijt2ia31agjrpfcnc616.arpa. 86400 IN NSEC3 1 0 1 - SSTSS4TF3ICJ43RCMUQTSJORRDDSRSRL NS \n\
+sstss4tf3icj43rcmuqtsjorrddsrsrl.arpa. 86400 IN RRSIG NSEC3 8 2 86400 20210616170429 20210519170429 29094 arpa. 0GVjQFd8YAYSXMh526fZ5Rx4WDHIf84MTzIsAYuLwM00H6uagrFxQv8mrGExWPummQ+Q+nHDuCBC5lEXjTF4/1qAu7MI627/mKtpcQevTvF3iE2ocf1/vfAFWVCzyLQ3AuFbGGuYQ6nlZzbOu2oRtma6/m4WpDhNszOhuONNlbY= \n\
+sstss4tf3icj43rcmuqtsjorrddsrsrl.arpa. 86400 IN NSEC3 1 0 1 - 0JS82OEC35LBBC4HL35476CM5ICACKSF NS DS RRSIG \n\
+uri.arpa. 172800 IN NS a.iana-servers.net. \n\
+uri.arpa. 172800 IN NS b.iana-servers.net. \n\
+uri.arpa. 172800 IN NS c.iana-servers.net. \n\
+uri.arpa. 172800 IN NS ns3.lacnic.net. \n\
+uri.arpa. 172800 IN NS ns4.apnic.net. \n\
+uri.arpa. 86400 IN DS 15796 8 2 7f8fa18fdd9a826eb08a4d4e9ce94dbba7a5b7b2b3ce1d74afd150242e9f572f \n\
+uri.arpa. 86400 IN DS 28547 8 2 deaefd0c163175350152da7b127dc7c4f9ec8bdf04ccc02829455df86c5ca035 \n\
+uri.arpa. 86400 IN DS 57851 8 2 8feda13f642ed9be2e4aaa3d50099dd422ca6081b6bf8188f804343b58d39cb7 \n\
+uri.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. jwQhmqBE2EWCE2yi14CqgjMfYWq4/W//IuL/EHSRZPJjyP7R7cnUgh/7rDO4JUcYebviO4s9hidjfpnLQWxpR2Jy2SH6aeNERLo76O28UW2Y28eused7aWMDWAnWW4HxURsQSBy2cyQbNwPCLGVLeQZaeZbKRBJUbWJ4MT4UpDE= \n\
+urn.arpa. 172800 IN NS a.iana-servers.net. \n\
+urn.arpa. 172800 IN NS b.iana-servers.net. \n\
+urn.arpa. 172800 IN NS c.iana-servers.net. \n\
+urn.arpa. 172800 IN NS ns3.lacnic.net. \n\
+urn.arpa. 172800 IN NS ns4.apnic.net. \n\
+urn.arpa. 86400 IN DS 28996 8 2 8e66d01a1e5864bcdb8e1f85579aec7c8c536c9d6fc7032ee708e869fd27f3d3 \n\
+urn.arpa. 86400 IN DS 34555 8 2 bd743967def1caf0812fe9eff2371d3adf29e27251db272145a5d523c92f7101 \n\
+urn.arpa. 86400 IN DS 45052 8 2 7685b675f93ada412cfe534820c8dcc55654b1711f677ba83a8564c12943f695 \n\
+urn.arpa. 86400 IN RRSIG DS 8 2 86400 20210616170429 20210519170429 29094 arpa. BHHa1YLYUOABgiloeQQRIMXRKxXNIwRken6E6ETFAWw3Js1ocu6H/X3bcPvBTjID/B+GRGgIyCnDnZ9iWeU41Tw1GnMNT9EM35DmnUgfzUU79shVzRtiYDV6JHF9Kidc90IxNrQOGAcUy0J9jhMa4KYEjfQab8sJSo0M+uJkNMw=";
+
+const char *no_zonemd = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *wrong_soa = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031901 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *duplicate_schemalg = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ 86400 IN ZONEMD 2018031901 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044c ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+const char *wrong_hash = "\
+example. 86400 IN SOA ns1 admin 2018031900 ( \n\
+ 1800 900 604800 86400 ) \n\
+ 86400 IN NS ns1 \n\
+ 86400 IN NS ns2 \n\
+ 86400 IN ZONEMD 2018031900 1 1 ( \n\
+ c68090d90a7aed71 \n\
+ 6bc459f9340e3d7c \n\
+ 1370d4d24b7e2fc3 \n\
+ a1ddc0b9a87153b9 \n\
+ a9713b3c9ae5cc27 \n\
+ 777f98b8e730044d ) \n\
+ns1 3600 IN A 203.0.113.63 \n\
+ns2 3600 IN AAAA 2001:db8::63";
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ int ret = check_contents(simple_zone);
+ is_int(KNOT_EOK, ret, "simple zone");
+
+ ret = check_contents(complex_zone);
+ is_int(KNOT_EOK, ret, "complex zone");
+
+ ret = check_contents(multiple_digests);
+ is_int(KNOT_EOK, ret, "multiple digests");
+
+ ret = check_contents(signed_zone);
+ is_int(KNOT_EOK, ret, "signed zone");
+
+ ret = check_contents(nsec3_zone);
+ is_int(KNOT_EOK, ret, "nsec3 zone");
+
+ ret = check_contents(no_zonemd);
+ is_int(KNOT_ENOENT, ret, "no zonemd");
+
+ ret = check_contents(wrong_soa);
+ is_int(KNOT_ENOTSUP, ret, "wrong SOA serial");
+ // TODO tests for different scheme / algorithm ?
+
+ ret = check_contents(duplicate_schemalg);
+ is_int(KNOT_ESEMCHECK, ret, "duplicate scheme+algorithm pair");
+
+ ret = check_contents(wrong_hash);
+ is_int(KNOT_EMALF, ret, "wrong hash");
+
+ return 0;
+}
diff --git a/tests/knot/test_dthreads.c b/tests/knot/test_dthreads.c
new file mode 100644
index 0000000..3bdfa3a
--- /dev/null
+++ b/tests/knot/test_dthreads.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 2021 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 <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <tap/basic.h>
+
+#include "knot/server/dthreads.h"
+
+/* Unit runnable data. */
+static pthread_mutex_t _runnable_mx;
+static volatile int _runnable_i = 0;
+static const int _runnable_cycles = 10000;
+
+/*! \brief Unit runnable. */
+int runnable(struct dthread *thread)
+{
+ for (int i = 0; i < _runnable_cycles; ++i) {
+
+ // Increase counter
+ pthread_mutex_lock(&_runnable_mx);
+ ++_runnable_i;
+ pthread_mutex_unlock(&_runnable_mx);
+
+ // Cancellation point
+ if (dt_is_cancelled(thread)) {
+ break;
+ }
+
+ // Yield
+ sched_yield();
+ }
+
+ return 0;
+}
+
+/* Destructor data. */
+static volatile int _destructor_data = 0;
+static pthread_mutex_t _destructor_mx;
+
+/*! \brief Thread destructor. */
+int destruct(struct dthread *thread)
+{
+ pthread_mutex_lock(&_destructor_mx);
+ _destructor_data += 1;
+ pthread_mutex_unlock(&_destructor_mx);
+
+ return 0;
+}
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ int cpus = dt_online_cpus();
+ ok(cpus > 0, "dthread: online cpus is positive value");
+
+ // Register service and signal handler
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ /* Initialize */
+ srand(time(NULL));
+ pthread_mutex_init(&_runnable_mx, NULL);
+ pthread_mutex_init(&_destructor_mx, NULL);
+
+ /* Test 1: Create unit */
+ int size = 2;
+ dt_unit_t *unit = dt_create(size, &runnable, NULL, NULL);
+ ok(unit != NULL, "dthreads: create unit (size %d)", size);
+ if (unit == NULL) {
+ skip_block(7, "No dthreads unit");
+ goto skip_all;
+ }
+
+ /* Test 2: Start tasks. */
+ _runnable_i = 0;
+ ok(dt_start(unit) == 0, "dthreads: start single task");
+
+ /* Test 3: Wait for tasks. */
+ ok(dt_join(unit) == 0, "dthreads: join threads");
+
+ /* Test 4: Compare counter. */
+ int expected = _runnable_cycles * 2;
+ is_int(expected, _runnable_i, "dthreads: result ok");
+
+ /* Test 5: Deinitialize */
+ dt_delete(&unit);
+ ok(unit == NULL, "dthreads: delete unit");
+
+ /* Test 6: Wrong values. */
+ unit = dt_create(-1, NULL, NULL, NULL);
+ ok(unit == NULL, "dthreads: create with negative count");
+
+ /* Test 7: NULL operations crashing. */
+ int ret = 0;
+ ret += dt_activate(0);
+ ret += dt_cancel(0);
+ ret += dt_compact(0);
+ dt_delete(0);
+ ret += dt_is_cancelled(0);
+ ret += dt_join(0);
+ ret += dt_signalize(0, SIGALRM);
+ ret += dt_start(0);
+ ret += dt_stop(0);
+ ret += dt_unit_lock(0);
+ ret += dt_unit_unlock(0);
+ is_int(-198, ret, "dthreads: correct values when passed NULL context");
+
+ /* Test 8: Thread destructor. */
+ _destructor_data = 0;
+ unit = dt_create(2, 0, destruct, 0);
+ dt_start(unit);
+ dt_stop(unit);
+ dt_join(unit);
+ is_int(2, _destructor_data, "dthreads: destructor with dt_create_coherent()");
+ dt_delete(&unit);
+
+skip_all:
+
+ pthread_mutex_destroy(&_runnable_mx);
+ pthread_mutex_destroy(&_destructor_mx);
+ return 0;
+}
diff --git a/tests/knot/test_fdset.c b/tests/knot/test_fdset.c
new file mode 100644
index 0000000..d0282f0
--- /dev/null
+++ b/tests/knot/test_fdset.c
@@ -0,0 +1,150 @@
+/* Copyright (C) 2022 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 <pthread.h>
+#include <tap/basic.h>
+#include <unistd.h>
+
+#include "knot/common/fdset.h"
+#include "libknot/attribute.h"
+#include "contrib/time.h"
+
+#define PATTERN1 "0x45"
+#define PATTERN2 "0xED"
+
+#define TIMEOUT0 2000
+#define TIMEOUT1 10
+#define TIMEOUT2 400
+#define JITTER (TIMEOUT2 - TIMEOUT1 - 10)
+
+void *thr_action1(void *arg)
+{
+ usleep(1000 * TIMEOUT1);
+ _unused_ int ret = write(*((int *)arg), &PATTERN1, 1);
+ return NULL;
+}
+
+void *thr_action2(void *arg)
+{
+ usleep(1000 * TIMEOUT2);
+ _unused_ int ret = write(*((int *)arg), &PATTERN2, 1);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ fdset_t fdset;
+ int ret = fdset_init(&fdset, 32);
+ ok(ret == KNOT_EOK, "fdset_init");
+
+ int fds0[2], fds1[2], fds2[2];
+
+ ret = pipe(fds0);
+ ok(ret >= 0, "create pipe 0");
+ ret = fdset_add(&fdset, fds0[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 0 to fdset");
+
+ ret = pipe(fds1);
+ ok(ret >= 0, "create pipe 1");
+ ret = fdset_add(&fdset, fds1[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 1 to fdset");
+
+ ret = pipe(fds2);
+ ok(ret >= 0, "create pipe 2");
+ ret = fdset_add(&fdset, fds2[0], FDSET_POLLIN, NULL);
+ ok(ret >= 0, "add pipe 2 to fdset");
+
+ ok(fdset_get_length(&fdset) == 3, "fdset size full");
+
+ struct timespec time0 = time_now();
+
+ pthread_t t1, t2;
+ ret = pthread_create(&t1, 0, thr_action1, &fds1[1]);
+ ok(ret == 0, "create thread 1");
+ ret = pthread_create(&t2, 0, thr_action2, &fds2[1]);
+ ok(ret == 0, "create thread 2");
+
+ fdset_it_t it;
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time1 = time_now();
+ double diff1 = time_diff_ms(&time0, &time1);
+ ok(ret == 1, "fdset_poll return 1");
+ ok(diff1 >= TIMEOUT1 && diff1 < TIMEOUT1 + JITTER, "fdset_poll timeout 1 (%f)", diff1);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds1[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN1[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 2, "fdset size 2");
+ close(fds1[1]);
+
+ int fd2_dup = dup(fds2[0]);
+ ok(fd2_dup >= 0, "duplicate fd");
+
+ ret = fdset_poll(&fdset, &it, 0, TIMEOUT0);
+ struct timespec time2 = time_now();
+ double diff2 = time_diff_ms(&time0, &time2);
+ ok(ret == 1, "fdset_poll return 2");
+ ok(diff2 >= TIMEOUT2 && diff2 < TIMEOUT2 + JITTER, "fdset_poll timeout 2 (%f)", diff2);
+ for(; !fdset_it_is_done(&it); fdset_it_next(&it)) {
+ ok(!fdset_it_is_error(&it), "fdset no error");
+ ok(fdset_it_is_pollin(&it), "fdset can read");
+
+ int fd = fdset_it_get_fd(&it);
+ ok(fd == fds2[0], "fdset_it fd check");
+
+ char buf = 0x00;
+ ret = read(fd, &buf, sizeof(buf));
+ ok(ret == 1 && buf == PATTERN2[0], "fdset_it value check");
+
+ fdset_it_remove(&it);
+ }
+ fdset_it_commit(&it);
+ ok(fdset_get_length(&fdset) == 1, "fdset size 1");
+
+ pthread_join(t1, 0);
+ pthread_join(t2, 0);
+
+ ret = fdset_remove(&fdset, 0);
+ ok(ret == KNOT_EOK, "fdset remove");
+ close(fds0[1]);
+ ok(fdset_get_length(&fdset) == 0, "fdset size 0");
+
+ ret = write(fds2[1], &PATTERN2, 1);
+ ok(ret == 1, "write to removed fd");
+ ret = fdset_poll(&fdset, &it, 0, 100);
+ ok(ret == 0, "fdset_poll return 3");
+
+
+ close(fds2[1]);
+ if (fd2_dup >= 0) {
+ close(fd2_dup);
+ }
+ fdset_clear(&fdset);
+
+ return 0;
+}
diff --git a/tests/knot/test_journal.c b/tests/knot/test_journal.c
new file mode 100644
index 0000000..748ab02
--- /dev/null
+++ b/tests/knot/test_journal.c
@@ -0,0 +1,880 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "knot/journal/journal_read.h"
+#include "knot/journal/journal_write.h"
+
+#include "libknot/attribute.h"
+#include "libknot/libknot.h"
+#include "knot/zone/zone.h"
+#include "knot/zone/zone-diff.h"
+#include "test_conf.h"
+
+#define RAND_RR_LABEL 16
+#define RAND_RR_PAYLOAD 64
+#define MIN_SOA_SIZE 22
+
+char *test_dir_name;
+
+knot_lmdb_db_t jdb;
+zone_journal_t jj;
+
+unsigned env_flag;
+
+static unsigned lmdb_page_size(knot_lmdb_db_t *db)
+{
+ knot_lmdb_txn_t txn = { 0 };
+ knot_lmdb_begin(db, &txn, false);
+ MDB_stat st = { 0 };
+ mdb_stat(txn.txn, txn.db->dbi, &st);
+ knot_lmdb_abort(&txn);
+ return st.ms_psize;
+}
+
+static void set_conf(int zonefile_sync, size_t journal_usage, const knot_dname_t *apex)
+{
+ (void)apex;
+ char conf_str[512];
+ snprintf(conf_str, sizeof(conf_str),
+ "template:\n"
+ " - id: default\n"
+ " zonefile-sync: %d\n"
+ " journal-max-usage: %zu\n"
+ " journal-max-depth: 1000\n",
+ zonefile_sync, journal_usage);
+ _unused_ int ret = test_conf(conf_str, NULL);
+ assert(ret == KNOT_EOK);
+ jj.conf = conf();
+}
+
+static void unset_conf(void)
+{
+ conf_update(NULL, CONF_UPD_FNONE);
+}
+
+/*! \brief Generate random string with given length. */
+static int randstr(char* dst, size_t len)
+{
+ for (int i = 0; i < len - 1; ++i) {
+ dst[i] = '0' + (int) (('Z'-'0') * (rand() / (RAND_MAX + 1.0)));
+ }
+ dst[len - 1] = '\0';
+
+ return 0;
+}
+
+/*! \brief Init RRSet with type SOA and given serial. */
+static void init_soa(knot_rrset_t *rr, const uint32_t serial, const knot_dname_t *apex)
+{
+ knot_rrset_init(rr, knot_dname_copy(apex, NULL), KNOT_RRTYPE_SOA, KNOT_CLASS_IN, 3600);
+
+ uint8_t soa_data[MIN_SOA_SIZE] = { 0 };
+ _unused_ int ret = knot_rrset_add_rdata(rr, soa_data, sizeof(soa_data), NULL);
+ knot_soa_serial_set(rr->rrs.rdata, serial);
+ assert(ret == KNOT_EOK);
+}
+
+/*! \brief Init RRSet with type TXT, random owner and random payload. */
+static void init_random_rr(knot_rrset_t *rr , const knot_dname_t *apex)
+{
+ /* Create random label. */
+ char owner[RAND_RR_LABEL + knot_dname_size(apex)];
+ owner[0] = RAND_RR_LABEL - 1;
+ randstr(owner + 1, RAND_RR_LABEL);
+
+ /* Append zone apex. */
+ memcpy(owner + RAND_RR_LABEL, apex, knot_dname_size(apex));
+ knot_rrset_init(rr, knot_dname_copy((knot_dname_t *)owner, NULL),
+ KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600);
+
+ /* Create random RDATA. */
+ uint8_t txt[RAND_RR_PAYLOAD + 1];
+ txt[0] = RAND_RR_PAYLOAD - 1;
+ randstr((char *)(txt + 1), RAND_RR_PAYLOAD);
+
+ _unused_ int ret = knot_rrset_add_rdata(rr, txt, RAND_RR_PAYLOAD, NULL);
+ assert(ret == KNOT_EOK);
+}
+
+/*! \brief Init changeset with random changes. */
+static void init_random_changeset(changeset_t *ch, const uint32_t from, const uint32_t to,
+ const size_t size, const knot_dname_t *apex, bool is_bootstrap)
+{
+ // Add SOAs
+ knot_rrset_t soa;
+
+ if (is_bootstrap) {
+ ch->soa_from = NULL;
+ zone_contents_deep_free(ch->remove);
+ ch->remove = NULL;
+ } else {
+ init_soa(&soa, from, apex);
+ ch->soa_from = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_from);
+ knot_rrset_clear(&soa, NULL);
+ }
+
+ init_soa(&soa, to, apex);
+ ch->soa_to = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_to);
+ knot_rrset_clear(&soa, NULL);
+
+ // Add RRs to add section
+ for (size_t i = 0; i < size / 2; ++i) {
+ knot_rrset_t rr;
+ init_random_rr(&rr, apex);
+ _unused_ int ret = changeset_add_addition(ch, &rr, 0);
+ assert(ret == KNOT_EOK);
+ knot_rrset_clear(&rr, NULL);
+ }
+
+ // Add RRs to remove section
+ for (size_t i = 0; i < size / 2 && !is_bootstrap; ++i) {
+ knot_rrset_t rr;
+ init_random_rr(&rr, apex);
+ _unused_ int ret = changeset_add_removal(ch, &rr, 0);
+ assert(ret == KNOT_EOK);
+ knot_rrset_clear(&rr, NULL);
+ }
+}
+
+static void changeset_set_soa_serials(changeset_t *ch, uint32_t from, uint32_t to,
+ const knot_dname_t *apex)
+{
+ knot_rrset_t soa;
+
+ init_soa(&soa, from, apex);
+ knot_rrset_free(ch->soa_from, NULL);
+ ch->soa_from = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_from);
+ knot_rrset_clear(&soa, NULL);
+
+ init_soa(&soa, to, apex);
+ knot_rrset_free(ch->soa_to, NULL);
+ ch->soa_to = knot_rrset_copy(&soa, NULL);
+ assert(ch->soa_to);
+ knot_rrset_clear(&soa, NULL);
+}
+
+/*! \brief Compare two changesets for equality. */
+static bool changesets_eq(const changeset_t *ch1, changeset_t *ch2)
+{
+ bool bootstrap = (ch1->remove == NULL);
+
+ if (!bootstrap && changeset_size(ch1) != changeset_size(ch2)) {
+ return false;
+ }
+
+ if ((bootstrap && ch2->remove != NULL) ||
+ (!bootstrap && ch2->remove == NULL)) {
+ return false;
+ }
+
+ changeset_iter_t it1;
+ changeset_iter_t it2;
+ if (bootstrap) {
+ changeset_iter_add(&it1, ch1);
+ changeset_iter_add(&it2, ch2);
+ } else {
+ changeset_iter_all(&it1, ch1);
+ changeset_iter_all(&it2, ch2);
+ }
+
+ knot_rrset_t rr1 = changeset_iter_next(&it1);
+ knot_rrset_t rr2 = changeset_iter_next(&it2);
+ bool ret = true;
+ while (!knot_rrset_empty(&rr1)) {
+ if (!knot_rrset_equal(&rr1, &rr2, true)) {
+ ret = false;
+ break;
+ }
+ rr1 = changeset_iter_next(&it1);
+ rr2 = changeset_iter_next(&it2);
+ }
+
+ changeset_iter_clear(&it1);
+ changeset_iter_clear(&it2);
+
+ return ret;
+}
+
+static bool changesets_list_eq(list_t *l1, list_t *l2)
+{
+ node_t *n = NULL;
+ node_t *k = HEAD(*l2);
+ WALK_LIST(n, *l1) {
+ if (k == NULL) {
+ return false;
+ }
+
+ changeset_t *ch1 = (changeset_t *) n;
+ changeset_t *ch2 = (changeset_t *) k;
+ if (!changesets_eq(ch1, ch2)) {
+ return false;
+ }
+
+ k = k->next;
+ }
+
+ if (k->next != NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+/*! \brief Test a list of changesets for continuity. */
+static bool test_continuity(list_t *l)
+{
+ node_t *n = NULL;
+ uint32_t key1, key2;
+ WALK_LIST(n, *l) {
+ if (n == TAIL(*l)) {
+ break;
+ }
+ changeset_t *ch1 = (changeset_t *) n;
+ changeset_t *ch2 = (changeset_t *) n->next;
+ key1 = knot_soa_serial(ch1->soa_to->rrs.rdata);
+ key2 = knot_soa_serial(ch2->soa_from->rrs.rdata);
+ if (key1 != key2) {
+ return KNOT_EINVAL;
+ }
+ }
+
+ return KNOT_EOK;
+}
+
+static void test_journal_db(void)
+{
+ env_flag = journal_env_flags(JOURNAL_MODE_ASYNC, false);
+ knot_lmdb_init(&jdb, test_dir_name, 2048 * 1024, env_flag, NULL);
+
+ int ret = knot_lmdb_open(&jdb);
+ is_int(KNOT_EOK, ret, "journal: open db (%s)", knot_strerror(ret));
+
+ ret = knot_lmdb_reconfigure(&jdb, test_dir_name, 4096 * 1024, env_flag);
+ is_int(KNOT_EOK, ret, "journal: re-open with bigger mapsize (%s)", knot_strerror(ret));
+
+ ret = knot_lmdb_reconfigure(&jdb, test_dir_name, 1024 * 1024, env_flag);
+ is_int(KNOT_EOK, ret, "journal: re-open with smaller mapsize (%s)", knot_strerror(ret));
+
+ knot_lmdb_deinit(&jdb);
+}
+
+static int load_j_list(zone_journal_t *zj, bool zij, uint32_t serial,
+ journal_read_t **read, list_t *list)
+{
+ changeset_t *ch;
+ init_list(list);
+ int ret = journal_read_begin(*zj, zij, serial, read);
+ if (ret == KNOT_EOK) {
+ while ((ch = calloc(1, sizeof(*ch))) != NULL &&
+ journal_read_changeset(*read, ch)) {
+ add_tail(list, &ch->n);
+ }
+ free(ch);
+ ret = journal_read_get_error(*read, KNOT_EOK);
+ }
+ return ret;
+}
+
+/*! \brief Test behavior with real changesets. */
+static void test_store_load(const knot_dname_t *apex)
+{
+ set_conf(1000, 512 * 1024, apex);
+
+ knot_lmdb_init(&jdb, test_dir_name, 1536 * 1024, env_flag, NULL);
+ assert(knot_lmdb_open(&jdb) == KNOT_EOK);
+
+ jj.db = &jdb;
+ jj.zone = apex;
+ list_t l, k;
+
+ changeset_t *m_ch = changeset_new(apex), r_ch, e_ch;
+ init_random_changeset(m_ch, 0, 1, 128, apex, false);
+ int ret = journal_insert(jj, m_ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: store changeset (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal: check after store changeset (%s)", knot_strerror(ret));
+ journal_read_t *read = NULL;
+
+ ret = load_j_list(&jj, false, changeset_from(m_ch), &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read single changeset (%s)", knot_strerror(ret));
+ ok(1 == list_size(&l), "journal: read exactly one changeset");
+ ok(changesets_eq(m_ch, HEAD(l)), "journal: changeset equal after read");
+ changesets_free(&l);
+
+ journal_read_end(read);
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: first simple flush (%s)", knot_strerror(ret));
+
+ init_list(&k);
+ uint32_t serial = 1;
+ for (; ret == KNOT_EOK && serial < 40000; ++serial) {
+ changeset_t *m_ch2 = changeset_new(apex);
+ init_random_changeset(m_ch2, serial, serial + 1, 128, apex, false);
+ ret = journal_insert(jj, m_ch2, NULL, NULL);
+ if (ret != KNOT_EOK) {
+ changeset_free(m_ch2);
+ break;
+ }
+ add_tail(&k, &m_ch2->n);
+ }
+ is_int(KNOT_EBUSY, ret, "journal: overfill with changesets (%d inserted) (%d should= %d)",
+ serial, ret, KNOT_EBUSY);
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after read");
+ ok(test_continuity(&l) == KNOT_EOK, "journal: changesets are in order");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list 2nd (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after 2nd read");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: flush after overfill (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+
+ ret = load_j_list(&jj, false, 1, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load list (%s)", knot_strerror(ret));
+ ok(changesets_list_eq(&l, &k), "journal: changeset lists equal after flush");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changesets_free(&k);
+
+ changeset_t ch;
+ ret = changeset_init(&ch, apex);
+ ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret);
+ init_random_changeset(&ch, serial, serial + 1, 555, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: store after flush (%d)", ret);
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, serial, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load after store after flush after overfill (%s)", knot_strerror(ret));
+ is_int(1, list_size(&l), "journal: single changeset in list");
+ ok(changesets_eq(&ch, HEAD(l)), "journal: changeset not malformed after overfill");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_clear(&ch);
+
+ changeset_init(&ch, apex);
+ init_random_changeset(&ch, 2, 3, 100, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert discontinuous changeset (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, 2, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read after discontinuity (%s)", knot_strerror(ret));
+ is_int(1, list_size(&l), "journal: discontinuity caused journal to drop");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ // Test for serial number collision handling. We insert changesets
+ // with valid serial sequence that overflows and then collides with itself.
+ // The sequence is 0 -> 1 -> 2 -> 2147483647 -> 4294967294 -> 1 which should
+ // remove changesets 0->1 and 1->2. *
+ uint32_t serials[6] = { 0, 1, 2, 2147483647, 4294967294, 1 };
+ for (int i = 0; i < 5; i++) {
+ changeset_clear(&ch);
+ changeset_init(&ch, apex);
+ init_random_changeset(&ch, serials[i], serials[i + 1], 100, apex, false);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(i == 4 ? KNOT_EBUSY : KNOT_EOK, ret, "journal: inserting cycle (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ }
+ ret = journal_set_flushed(jj);
+ is_int(KNOT_EOK, ret, "journal: flush in cycle (%s)", knot_strerror(ret));
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: inserted cycle (%s)", knot_strerror(ret));
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal check (%s)", knot_strerror(ret));
+ ret = journal_read_begin(jj, false, 0, &read);
+ is_int(KNOT_ENOENT, ret, "journal: cycle removed first changeset (%d should= %d)", ret, KNOT_ENOENT);
+ ret = journal_read_begin(jj, false, 1, &read);
+ is_int(KNOT_ENOENT, ret, "journal: cycle removed second changeset (%d should= %d)", ret, KNOT_ENOENT);
+ ret = load_j_list(&jj, false, 4294967294, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read after cycle (%s)", knot_strerror(ret));
+ ok(3 >= list_size(&l), "journal: cycle caused journal to partly drop");
+ ok(changesets_eq(&ch, HEAD(l)), "journal: changeset not malformed after cycle");
+ changesets_free(&l);
+ journal_read_end(read);
+ changeset_clear(&ch);
+ changeset_free(m_ch);
+
+ changeset_init(&e_ch, apex);
+ init_random_changeset(&e_ch, 0, 1, 200, apex, true);
+ zone_node_t *n = NULL;
+ zone_contents_add_rr(e_ch.add, e_ch.soa_to, &n);
+ ret = journal_insert_zone(jj, e_ch.add);
+ zone_contents_remove_rr(e_ch.add, e_ch.soa_to, &n);
+ is_int(KNOT_EOK, ret, "journal: insert zone-in-journal (%s)", knot_strerror(ret));
+ changeset_init(&r_ch, apex);
+ init_random_changeset(&r_ch, 1, 2, 200, apex, false);
+ ret = journal_insert(jj, &r_ch, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert after zone-in-journal (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: read two changesets from zone-in-journal");
+ ok(changesets_eq(&e_ch, HEAD(l)), "journal: zone-in-journal not malformed");
+ ok(changesets_eq(&r_ch, TAIL(l)), "journal: after zone-in-journal not malformed");
+ changesets_free(&l);
+ journal_read_end(read);
+ changeset_clear(&e_ch);
+ changeset_clear(&r_ch);
+
+ ret = journal_scrape_with_md(jj, true);
+ is_int(KNOT_EOK, ret, "journal: scrape with md (%s)", knot_strerror(ret));
+
+ unset_conf();
+}
+
+static void test_size_control(const knot_dname_t *zone1, const knot_dname_t *zone2)
+{
+ set_conf(-1, 100 * 1024, zone1);
+
+ zone_journal_t jj2 = { &jdb, zone2, conf() };
+ changeset_t *small_ch2 = changeset_new(zone2);
+ init_random_changeset(small_ch2, 1, 2, 100, zone2, false);
+ int ret = journal_insert(jj2, small_ch2, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok");
+
+ changeset_t *big_zij = changeset_new(zone1);
+ init_random_changeset(big_zij, 0, 1, 1200, zone1, true);
+ zone_node_t *n = NULL;
+ zone_contents_add_rr(big_zij->add, big_zij->soa_to, &n);
+ ret = journal_insert_zone(jj, big_zij->add);
+ is_int(KNOT_EOK, ret, "journal: store big zone-in-journal");
+
+ changeset_t *big_ch2 = changeset_new(zone2);
+ init_random_changeset(big_ch2, 2, 3, 750, zone2, false);
+ ret = journal_insert(jj2, big_ch2, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: second zone is not affected by storing big zij of other zone");
+
+ journal_read_t *read = NULL;
+ list_t l;
+ init_list(&l);
+ changeset_t *medium_ch1 = changeset_new(zone1);
+ init_random_changeset(medium_ch1, 1, 2, 300, zone1, false);
+ ret = journal_insert(jj, medium_ch1, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing medium changeset must be ok");
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: read two changesets from journal");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_t *small_ch1 = changeset_new(zone1);
+ init_random_changeset(small_ch1, 2, 3, 100, zone1, false);
+ ret = journal_insert(jj, small_ch1, NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: storing small changeset must be ok");
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: load zone-in-journal (%s)", knot_strerror(ret));
+ is_int(2, list_size(&l), "journal: previous chs merged into zone-in-journal due to size limits");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ changeset_t *medium_ch1b = changeset_new(zone1);
+ init_random_changeset(medium_ch1b, 3, 4, 300, zone1, false);
+ ret = journal_insert(jj, medium_ch1b, NULL, NULL);
+ is_int(KNOT_ESPACE, ret, "journal: not able to free space for changeset by merging");
+
+ changeset_t *too_big_zij = changeset_new(zone1);
+ init_random_changeset(too_big_zij, 0, 1, 2200, zone1, true);
+ zone_contents_add_rr(too_big_zij->add, too_big_zij->soa_to, &n);
+ ret = journal_insert_zone(jj, too_big_zij->add);
+ is_int(KNOT_ESPACE, ret, "journal: store too big zone-in-journal");
+
+ changeset_free(big_ch2);
+ changeset_free(big_zij);
+ changeset_free(too_big_zij);
+ changeset_free(small_ch2);
+ changeset_free(small_ch1);
+ changeset_free(medium_ch1);
+ changeset_free(medium_ch1b);
+
+ unset_conf();
+}
+
+const uint8_t *rdA = (const uint8_t *) "\x01\x02\x03\x04";
+const uint8_t *rdB = (const uint8_t *) "\x01\x02\x03\x05";
+const uint8_t *rdC = (const uint8_t *) "\x01\x02\x03\x06";
+
+// frees owner
+static knot_rrset_t * tm_rrset(knot_dname_t * owner, const uint8_t * rdata)
+{
+ knot_rrset_t * rrs = knot_rrset_new(owner, KNOT_RRTYPE_A, KNOT_CLASS_IN, 3600, NULL);
+ knot_rrset_add_rdata(rrs, rdata, 4, NULL);
+ free(owner);
+ return rrs;
+}
+
+static knot_dname_t *tm_owner(const char *prefix, const knot_dname_t *apex)
+{
+ size_t prefix_len = strlen(prefix);
+ size_t apex_len = knot_dname_size(apex);
+
+ knot_dname_t *out = malloc(prefix_len + apex_len + 2);
+ out[0] = prefix_len;
+ memcpy(out + 1, prefix, prefix_len);
+ memcpy(out + 1 + prefix_len, apex, apex_len);
+ return out;
+}
+
+static knot_dname_t *tm_owner_int(int x, const knot_dname_t *apex)
+{
+ char buf[12] = { 0 };
+ (void)snprintf(buf, sizeof(buf), "i%d", x);
+ return tm_owner(buf, apex);
+}
+
+static knot_rrset_t * tm_rrs(const knot_dname_t * apex, int x)
+{
+ static knot_rrset_t * rrsA = NULL;
+ static knot_rrset_t * rrsB = NULL;
+ static knot_rrset_t * rrsC = NULL;
+
+ if (apex == NULL) {
+ knot_rrset_free(rrsA, NULL);
+ knot_rrset_free(rrsB, NULL);
+ knot_rrset_free(rrsC, NULL);
+ rrsA = rrsB = rrsC = NULL;
+ return NULL;
+ }
+
+ if (rrsA == NULL) rrsA = tm_rrset(tm_owner("aaaaaaaaaaaaaaaaa", apex), rdA);
+ if (rrsB == NULL) rrsB = tm_rrset(tm_owner("bbbbbbbbbbbbbbbbb", apex), rdB);
+ if (rrsC == NULL) rrsC = tm_rrset(tm_owner("ccccccccccccccccc", apex), rdC);
+ switch ((x % 3 + 3) % 3) {
+ case 0: return rrsA;
+ case 1: return rrsB;
+ case 2: return rrsC;
+ }
+ assert(0); return NULL;
+}
+
+#define TM_RRS_INT_MAX 1000
+
+static knot_rrset_t *tm_rrs_int(const knot_dname_t *apex, int x)
+{
+ assert(x < TM_RRS_INT_MAX);
+ static knot_rrset_t *stat_rrs[TM_RRS_INT_MAX] = { 0 };
+
+ if (apex == NULL) {
+ for (int i = 0; i < TM_RRS_INT_MAX; i++) {
+ knot_rrset_free(stat_rrs[i], NULL);
+ stat_rrs[i] = NULL;
+ }
+ return NULL;
+ }
+
+ if (stat_rrs[x] == NULL) {
+ stat_rrs[x] = tm_rrset(tm_owner_int(x, apex), rdA);
+ }
+ return stat_rrs[x];
+}
+
+int tm_rrcnt(const changeset_t * ch, int flg)
+{
+ changeset_iter_t it;
+ int i = 0;
+ if (flg >= 0) changeset_iter_add(&it, ch);
+ else changeset_iter_rem(&it, ch);
+
+ knot_rrset_t rri;
+ while (rri = changeset_iter_next(&it), !knot_rrset_empty(&rri)) i++;
+
+ changeset_iter_clear(&it);
+ return i;
+}
+
+static changeset_t * tm_chs(const knot_dname_t * apex, int x)
+{
+ static changeset_t * chsI = NULL, * chsX = NULL, * chsY = NULL;
+ static uint32_t serial = 0;
+ if (x < 0) {
+ serial = 0;
+ return NULL;
+ }
+
+ if (apex == NULL) {
+ changeset_free(chsI);
+ changeset_free(chsX);
+ changeset_free(chsY);
+ chsI = chsX = chsY = NULL;
+ return NULL;
+ }
+
+ if (chsI == NULL) {
+ chsI = changeset_new(apex);
+ assert(chsI != NULL);
+ changeset_add_addition(chsI, tm_rrs(apex, 0), 0);
+ changeset_add_addition(chsI, tm_rrs(apex, 1), 0);
+ }
+ if (chsX == NULL) {
+ chsX = changeset_new(apex);
+ assert(chsX != NULL);
+ changeset_add_removal(chsX, tm_rrs(apex, 1), 0);
+ changeset_add_addition(chsX, tm_rrs(apex, 2), 0);
+ }
+ if (chsY == NULL) {
+ chsY = changeset_new(apex);
+ assert(chsY != NULL);
+ changeset_add_removal(chsY, tm_rrs(apex, 2), 0);
+ changeset_add_addition(chsY, tm_rrs(apex, 1), 0);
+ }
+ assert(x >= 0);
+ changeset_t * ret;
+ if (x == 0) ret = chsI;
+ else if (x % 2 == 1) ret = chsX;
+ else ret = chsY;
+
+ changeset_set_soa_serials(ret, serial, serial + 1, apex);
+ serial++;
+
+ return ret;
+}
+
+static void tm2_add_all(zone_contents_t *toadd)
+{
+ assert(toadd != NULL);
+ for (int i = 1; i < TM_RRS_INT_MAX; i++) {
+ zone_node_t *unused = NULL;
+ _unused_ int ret = zone_contents_add_rr(toadd, tm_rrs_int(toadd->apex->owner, i), &unused);
+ assert(ret == KNOT_EOK);
+ }
+}
+
+static zone_contents_t *tm2_zone(const knot_dname_t *apex)
+{
+ zone_contents_t *z = zone_contents_new(apex, false);
+ if (z != NULL) {
+ knot_rrset_t soa;
+ zone_node_t *unused = NULL;
+ init_soa(&soa, 1, apex);
+ _unused_ int ret = zone_contents_add_rr(z, &soa, &unused);
+ knot_rrset_clear(&soa, NULL);
+ assert(ret == KNOT_EOK);
+ tm2_add_all(z);
+ }
+ return z;
+}
+
+static changeset_t *tm2_chs_unzone(const knot_dname_t *apex)
+{
+ changeset_t *ch = changeset_new(apex);
+ if (ch != NULL) {
+ changeset_set_soa_serials(ch, 1, 2, apex);
+ tm2_add_all(ch->remove);
+ _unused_ int ret = changeset_add_addition(ch, tm_rrs_int(apex, 0), 0);
+ assert(ret == KNOT_EOK);
+ }
+ return ch;
+}
+
+static int merged_present(void)
+{
+ bool exists, has_merged;
+ return journal_info(jj, &exists, NULL, NULL, NULL, &has_merged, NULL, NULL, NULL) == KNOT_EOK && exists && has_merged;
+}
+
+static void test_merge(const knot_dname_t *apex)
+{
+ int i, ret;
+ list_t l;
+
+ // allow merge
+ set_conf(-1, 400 * 1024, apex);
+ ok(!journal_allow_flush(jj), "journal: merge allowed");
+
+ ret = journal_scrape_with_md(jj, false);
+ is_int(KNOT_EOK, ret, "journal: journal_drop_changesets must be ok");
+
+ // insert stuff and check the merge
+ for (i = 0; !merged_present() && i < 40000; i++) {
+ ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL);
+ assert(ret == KNOT_EOK);
+ }
+ ret = journal_sem_check(jj);
+ is_int(KNOT_EOK, ret, "journal: sem check (%s)", knot_strerror(ret));
+ journal_read_t *read = NULL;
+ ret = load_j_list(&jj, false, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets must be ok");
+ assert(ret == KNOT_EOK);
+ ok(list_size(&l) == 2, "journal: read the merged and one following");
+ changeset_t * mch = (changeset_t *)HEAD(l);
+ ok(list_size(&l) >= 1 && tm_rrcnt(mch, 1) == 2, "journal: merged additions # = 2");
+ ok(list_size(&l) >= 1 && tm_rrcnt(mch, -1) == 0, "journal: merged removals # = 0");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ // insert one more and check the #s of results
+ ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL);
+ is_int(KNOT_EOK, ret, "journal: insert one more (%s)", knot_strerror(ret));
+ ret = load_j_list(&jj, false, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets2 must be ok");
+ ok(list_size(&l) == 3, "journal: read merged together with new changeset");
+ changesets_free(&l);
+ journal_read_end(read);
+ ret = load_j_list(&jj, false, i - 3, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: journal_load_changesets3 must be ok");
+ ok(list_size(&l) == 4, "journal: read short history of merged/unmerged changesets");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ // insert large zone-in-journal taking more than one chunk
+ zone_contents_t *bigz = tm2_zone(apex);
+ ret = journal_insert_zone(jj, bigz);
+ zone_contents_deep_free(bigz);
+ is_int(KNOT_EOK, ret, "journal: insert large zone-in-journal");
+
+ // insert changeset that will cancel it mostly out
+ changeset_t *bigz_cancelout = tm2_chs_unzone(apex);
+ ret = journal_insert(jj, bigz_cancelout, NULL, NULL);
+ changeset_free(bigz_cancelout);
+ is_int(KNOT_EOK, ret, "journal: insert cancel-out changeset");
+
+ // now fill up with dumy changesets to enforce merge
+ tm_chs(apex, -1);
+ while (changeset_to(tm_chs(apex, 0)) != 2) { }
+ for (i = 0; i < 1600; i++) {
+ ret = journal_insert(jj, tm_chs(apex, i), NULL, NULL);
+ assert(ret == KNOT_EOK);
+ }
+
+ // finally: the test case. Reading the journal now must be no EMALF and
+ // the zone-in-journal must be little
+ ret = load_j_list(&jj, true, 0, &read, &l);
+ is_int(KNOT_EOK, ret, "journal: read chunks-shrinked zone-in-journal");
+ is_int(4, trie_weight(((changeset_t *)HEAD(l))->add->nodes->trie), "journal: small merged zone-in-journal");
+ changesets_free(&l);
+ journal_read_end(read);
+
+ ret = journal_scrape_with_md(jj, false);
+ assert(ret == KNOT_EOK);
+
+ // disallow merge
+ unset_conf();
+ set_conf(1000, 512 * 1024, apex);
+ ok(journal_allow_flush(jj), "journal: merge disallowed");
+
+ tm_rrs(NULL, 0);
+ tm_chs(NULL, 0);
+ tm_rrs_int(NULL, 0);
+ unset_conf();
+}
+
+static void test_stress_base(const knot_dname_t *apex,
+ size_t update_size, size_t file_size)
+{
+ uint32_t serial = 0;
+
+ int ret = knot_lmdb_reconfigure(&jdb, test_dir_name, file_size, journal_env_flags(JOURNAL_MODE_ASYNC, false));
+ is_int(KNOT_EOK, ret, "journal: reconfigure to mapsize %zu (%s)", file_size, knot_strerror(ret));
+
+ set_conf(1000, file_size / 2, apex);
+
+ changeset_t ch;
+ ret = changeset_init(&ch, apex);
+ ok(ret == KNOT_EOK, "journal: changeset init (%d)", ret);
+ init_random_changeset(&ch, serial, serial + 1, update_size, apex, false);
+
+ for (int i = 1; i <= 6; ++i) {
+ serial = 0;
+ while (true) {
+ changeset_set_soa_serials(&ch, serial, serial + 1, apex);
+ ret = journal_insert(jj, &ch, NULL, NULL);
+ if (ret == KNOT_EOK) {
+ serial++;
+ } else {
+ break;
+ }
+ }
+
+ ret = journal_set_flushed(jj);
+ if (ret == KNOT_EOK) {
+ ret = journal_sem_check(jj);
+ }
+ ok(serial > 0 && ret == KNOT_EOK, "journal: pass #%d fillup run (%d inserts) (%s)", i, serial, knot_strerror(ret));
+ }
+
+ changeset_clear(&ch);
+
+ unset_conf();
+}
+
+/*! \brief Test behavior when writing to the journal and flushing it. */
+static void test_stress(const knot_dname_t *apex)
+{
+ diag("stress test: small data");
+ test_stress_base(apex, 40, (1024 + 512) * 1024);
+
+ diag("stress test: medium data");
+ test_stress_base(apex, 400, 3 * 1024 * 1024);
+
+ diag("stress test: large data");
+ test_stress_base(apex, 4000, 10 * 1024 * 1024);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ const knot_dname_t *apex = (const uint8_t *)"\4test";
+ const knot_dname_t *apex2 = (const uint8_t *)"\4ufoo";
+
+ test_dir_name = test_mkdtemp();
+
+ test_journal_db();
+
+ test_store_load(apex);
+
+ if (lmdb_page_size(&jdb) == 4096) {
+ test_size_control(apex, apex2);
+ } // else it is not manually optimized enough to test correctly
+
+ test_merge(apex);
+
+ test_stress(apex);
+
+ knot_lmdb_deinit(&jdb);
+
+ test_rm_rf(test_dir_name);
+ free(test_dir_name);
+
+ return 0;
+}
diff --git a/tests/knot/test_kasp_db.c b/tests/knot/test_kasp_db.c
new file mode 100644
index 0000000..e4f9766
--- /dev/null
+++ b/tests/knot/test_kasp_db.c
@@ -0,0 +1,233 @@
+/* Copyright (C) 2022 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "libknot/libknot.h"
+#include "test_conf.h"
+#include "knot/dnssec/kasp/kasp_db.c"
+
+#define CHARS500_1 "kTZgFfrHPP2EOSK24zRjY9GlgCUEkZNBF5UwqsTWisCxGQT4ieinitjXWT1c" \
+ "pj+OR8UX/httSugee+MFsm5yOU/4/211BpLKwwOIAt4Yf8K7Bc+oXTdk15cH" \
+ "TRZtshM1AtfjRsX9rsLDsnaFCyMXzty9AQoRxSphjxnUUC6fszfrSsRx7htl" \
+ "/Xn1PAuwp9Bfn+FxAws98LYVuwiDqUgn4BR5lELdGd16zNOZnN7v023pmPDM" \
+ "nGyvIATuqTCPbFeXTfw7aIDyx2DF+y95/kSnPtY3c1b0Yf+oCv4t3Hx2jjWT" \
+ "9zuC6H+d+PL6HWqilJBs7ysn2FVpnE/Yo44VrQ8orw8QFZr1kR6z7AOVAcMk" \
+ "ac+44swsc8orGCyJx6OlUfN5oU3YahUfLqg9ewl13+P2chmeI6wUyttKsq/4" \
+ "Ud0YQAozBabiAKr1O/Eg7sZR6bV1YkCydQyYgmR/+VOu9D8Ld6uO4DcvhiE/" \
+ "2AmTkLsKLxtpMnQqsTnx"
+#define CHARS500_2 "pzqkMLvpxUYYg0KuMCcBsk9aMm4b5Ny+vJ5UnTq8DVC0jHJJyyGcljKqfpi3" \
+ "MkjfrWY0rbzXFZbZZ6i8bmhhRBcSxE+tK/3AU1LR7ZJsTuITuoJo5LKRH7Uu" \
+ "MU7RBAzFuk3o+Pcyk+9UdbiRn9p4QqPTvb+2xfYn1pJvGHofJcQsSsPEe9Hw" \
+ "ycVW+kdImvWiidn0/e1G6B2xibovnPKDUBFmTbdZIBKHb/eUUoUCNA9CWt5N" \
+ "g8MutK2ixlBJlOvA6CA1V/VW56EJpLqvMxLaoRks5VY5Ls7zWAy97GEFH0Pl" \
+ "uO/Rba1du5tsC0MAC08hljlmu9uoPhsvHdBYHUnQ7jDuYnu9GN3DN0Z6oVbV" \
+ "N01JQZYhKQK/Bl61oM5JubLydtAypryDoG3IH75LhoVC8iGxDoDkxt3zoi/Q" \
+ "PVfPZZsm5j5UOs3wrQL0KWylm2IDK42mrHK8F/XebnOYLNLQaan2a90C+fhH" \
+ "a6hvu0RorkZzGNAZkq/D"
+
+const knot_dname_t *zone1 = (const knot_dname_t *)"\x05""zonea";
+const knot_dname_t *zone2 = (const knot_dname_t *)"\x05""zoneb";
+
+knot_lmdb_db_t _db, *db = &_db;
+
+const key_params_t params1 = { .id = "key id 1", .keytag = 1, .timing = { 1, 11, 111, 1111, 11111 },
+ .public_key = { 520, (uint8_t *)"pk1 plus 500 chars: " CHARS500_1 } };
+const key_params_t params2 = { .id = "key id 2", .keytag = 2, .timing = { 2, 22, 222, 2222, 22222 },
+ .public_key = { 520, (uint8_t *)"pk2 plus 500 chars: " CHARS500_2 } };
+
+bool params_eq(const key_params_t *a, const key_params_t *b)
+{
+ return ((a->keytag == b->keytag) && (a->public_key.size == b->public_key.size) &&
+ (a->timing.retire == b->timing.retire) && (strcmp(a->id, b->id) == 0) &&
+ (memcmp(a->public_key.data, b->public_key.data, b->public_key.size) == 0));
+}
+
+static void init_key_records(key_records_t *r)
+{
+ knot_rrset_init(&r->dnskey, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, 3600);
+ knot_rrset_init(&r->cdnskey, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->cds, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_CDS, KNOT_CLASS_IN, 0);
+ knot_rrset_init(&r->rrsig, knot_dname_copy(zone1, NULL),
+ KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN, 3600);
+ knot_rrset_add_rdata(&r->rrsig, (uint8_t *)CHARS500_1, 500, NULL);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ char *test_dir_name = test_mkdtemp();
+ bool ignore = false;
+
+ list_t l;
+ key_params_t *params;
+#define free_params free(params->id); free(params->public_key.data); params->id = NULL; params->public_key.data = NULL;
+
+ knot_lmdb_init(db, test_dir_name, 500*1024*1024, 0, "keys_db");
+ int ret = knot_lmdb_open(db);
+ is_int(KNOT_EOK, ret, "kasp_db: open eok");
+ ok(db->env != NULL, "kasp_db: lmdb env notnull");
+
+ ret = kasp_db_add_key(db, zone1, &params1);
+ is_int(KNOT_EOK, ret, "kasp_db: add key 1 eok");
+
+ ret = kasp_db_list_keys(db, zone1, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 1 eok");
+ is_int(1, list_size(&l), "kasp_db: list keys reports one key 1");
+ params = ((ptrnode_t *)HEAD(l))->d;
+ ok(params_eq(params, &params1), "kasp_db: key params equal 1");
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: list keys 1 enoent");
+ is_int(0, list_size(&l), "kasp_db: list keys reports no keys 1");
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_share_key(db, zone1, zone2, params1.id);
+ is_int(KNOT_EOK, ret, "kasp_db: share key eok");
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 3 eok");
+ is_int(1, list_size(&l), "kasp_db: list keys reports one key 2");
+ params = ((ptrnode_t *)HEAD(l))->d;
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_add_key(db, zone2, &params2);
+ is_int(KNOT_EOK, ret, "kasp_db: add key 2 eok");
+
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_EOK, ret, "kasp_db: list keys 4 eok");
+ is_int(2, list_size(&l), "kasp_db: list keys reports two keys 1");
+ params = ((ptrnode_t *)TAIL(l))->d;
+ ok(params_eq(params, &params2), "kasp_db: key params equal 2");
+ free_params
+ params = ((ptrnode_t *)HEAD(l))->d;
+ free_params
+ ptrlist_deep_free(&l, NULL);
+
+ ret = kasp_db_delete_key(db, zone1, params1.id, &ignore);
+ is_int(KNOT_EOK, ret, "kasp_db: delete key 1 eok");
+
+ ret = kasp_db_list_keys(db, zone1, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: list keys 2 enoent");
+ is_int(list_size(&l), 0, "kasp_db: list keys reports no keys 2");
+ ptrlist_deep_free(&l, NULL);
+
+ dnssec_binary_t salt1 = { 500, (uint8_t *)CHARS500_1 }, salt2 = { 0 };
+ knot_time_t time = 0;
+ ret = kasp_db_store_nsec3salt(db, zone1, &salt1, 1234);
+ is_int(KNOT_EOK, ret, "kasp_db: store nsec3salt");
+ ret = kasp_db_load_nsec3salt(db, zone1, &salt2, &time);
+ is_int(KNOT_EOK, ret, "kasp_db: load nsec3salt");
+ is_int(1234, time, "kasp_db: salt_created preserved");
+ is_int(0, dnssec_binary_cmp(&salt1, &salt2), "kasp_db: salt preserved");
+ dnssec_binary_free(&salt2);
+ salt1.size = 0;
+ ret = kasp_db_store_nsec3salt(db, zone2, &salt1, 0);
+ is_int(KNOT_EOK, ret, "kasp_db: store empty nsec3salt");
+ ret = kasp_db_load_nsec3salt(db, zone2, &salt2, &time);
+ is_int(KNOT_EOK, ret, "kasp_db: load empty nsec3salt");
+ is_int(0, time, "kasp_db: empty salt_created preserved");
+ is_int(0, salt2.size, "kasp_db: empty salt preserved");
+ dnssec_binary_free(&salt2);
+
+ ret = kasp_db_delete_all(db, zone2);
+ is_int(KNOT_EOK, ret, "kasp_db: delete all");
+ ret = kasp_db_list_keys(db, zone2, &l);
+ is_int(KNOT_ENOENT, ret, "kasp_db: delete all deleted keys");
+ ret = kasp_db_load_nsec3salt(db, zone2, &salt2, &time);
+ is_int(KNOT_ENOENT, ret, "kasp_db: delete all removed nsec3salt");
+ dnssec_binary_free(&salt2);
+
+ ret = kasp_db_store_serial(db, zone2, KASPDB_SERIAL_MASTER, 1);
+ is_int(KNOT_EOK, ret, "kasp_db: store master_serial");
+ ret = kasp_db_store_serial(db, zone2, KASPDB_SERIAL_LASTSIGNED, 2);
+ is_int(KNOT_EOK, ret, "kasp_db: store lastsigned_serial");
+ uint32_t serial = 0;
+ ret = kasp_db_load_serial(db, zone2, KASPDB_SERIAL_MASTER, &serial);
+ is_int(KNOT_EOK, ret, "kasp_db: load master_serial");
+ is_int(1, serial, "kasp_db: master_serial preserved");
+ ret = kasp_db_load_serial(db, zone2, KASPDB_SERIAL_LASTSIGNED, &serial);
+ is_int(KNOT_EOK, ret, "kasp_db: load lastsigned_serial");
+ is_int(2, serial, "kasp_db: lastsigned_serial preserved");
+
+ ret = kasp_db_add_key(db, zone1, &params1);
+ ok(ret == KNOT_EOK, "kasp_db: add key1");
+ ret = kasp_db_add_key(db, zone2, &params2);
+ ok(ret == KNOT_EOK, "kasp_db: add key2");
+
+ ret = kasp_db_set_policy_last(db, "policy1", NULL, zone1, params1.id);
+ is_int(KNOT_EOK, ret, "kasp_db: set policylast");
+ knot_dname_t *zoneX;
+ char *keyidX;
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: get policylast");
+ ok(knot_dname_cmp(zoneX, zone1) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params1.id) == 0, "kasp_db: policy last preserved");
+ free(zoneX);
+ free(keyidX);
+ ret = kasp_db_set_policy_last(db, "policy1", params1.id, zone2, params2.id);
+ is_int(KNOT_EOK, ret, "kasp_db: reset policylast");
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: reget policylast");
+ ok(knot_dname_cmp(zoneX, zone2) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params2.id) == 0, "kasp_db: policy last represerved");
+ free(zoneX);
+ free(keyidX);
+ ret = kasp_db_set_policy_last(db, "policy1", params1.id, zone1, params1.id);
+ is_int(KNOT_ESEMCHECK, ret, "kasp_db: refused policylast with wrong keyid");
+ ret = kasp_db_get_policy_last(db, "policy1", &zoneX, &keyidX);
+ is_int(KNOT_EOK, ret, "kasp_db: reget policylast2");
+ ok(knot_dname_cmp(zoneX, zone2) == 0 && keyidX != NULL &&
+ strcmp(keyidX, params2.id) == 0, "kasp_db: policy last represerved2");
+ free(zoneX);
+ free(keyidX);
+
+ knot_time_t two = 2;
+ key_records_t kr;
+ init_key_records(&kr);
+ ret = kasp_db_store_offline_records(db, 1, &kr);
+ is_int(KNOT_EOK, ret, "kasp_db: store key records");
+ //key_records_clear_rdatasets(&kr);
+ ret = kasp_db_load_offline_records(db, zone1, &two, &time, &kr);
+ is_int(KNOT_EOK, ret, "kasp_db: load key records");
+ ok(kr.cds.type == KNOT_RRTYPE_CDS && kr.rrsig.rrs.count == 1, "kasp_db: key records ok");
+ is_int(0, time, "kasp_db: no next key records");
+ key_records_clear(&kr);
+ ret = kasp_db_delete_offline_records(db, zone1, 1, 3);
+ is_int(KNOT_EOK, ret, "kasp_db: delete key records");
+ ret = kasp_db_load_offline_records(db, zone1, &two, &time, &kr);
+ is_int(KNOT_ENOENT, ret, "kasp_db: no more key records");
+
+ knot_lmdb_deinit(db);
+
+ test_rm_rf(test_dir_name);
+ free(test_dir_name);
+
+ return 0;
+}
diff --git a/tests/knot/test_node.c b/tests/knot/test_node.c
new file mode 100644
index 0000000..e8c6cdb
--- /dev/null
+++ b/tests/knot/test_node.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <tap/basic.h>
+
+#include "knot/zone/node.h"
+#include "libknot/libknot.h"
+
+static knot_rrset_t *create_dummy_rrset(const knot_dname_t *owner, uint16_t type)
+{
+ knot_rrset_t *r = knot_rrset_new(owner, type, KNOT_CLASS_IN, 3600, NULL);
+ assert(r);
+ uint8_t wire[16] = { 0 };
+ memcpy(wire, "testtest", strlen("testtest"));
+ int ret = knot_rrset_add_rdata(r, wire, strlen("testtest"), NULL);
+ assert(ret == KNOT_EOK);
+ (void)ret;
+ return r;
+}
+
+static knot_rrset_t *create_dummy_rrsig(const knot_dname_t *owner, uint16_t type)
+{
+ knot_rrset_t *r = knot_rrset_new(owner, KNOT_RRTYPE_RRSIG, KNOT_CLASS_IN,
+ 3600, NULL);
+ assert(r);
+ uint8_t wire[sizeof(uint16_t)];
+ knot_wire_write_u16(wire, type);
+ int ret = knot_rrset_add_rdata(r, wire, sizeof(uint16_t), NULL);
+ assert(ret == KNOT_EOK);
+ (void)ret;
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test.");
+ // Test new
+ zone_node_t *node = node_new(dummy_owner, false, false, NULL);
+ ok(node != NULL, "Node: new");
+ assert(node);
+ ok(knot_dname_is_equal(node->owner, dummy_owner), "Node: new - set fields");
+
+ // Test RRSet addition
+ knot_rrset_t *dummy_rrset = create_dummy_rrset(dummy_owner, KNOT_RRTYPE_TXT);
+ int ret = node_add_rrset(node, dummy_rrset, NULL);
+ ok(ret == KNOT_EOK && node->rrset_count == 1 &&
+ knot_rdataset_eq(&dummy_rrset->rrs, &node->rrs[0].rrs), "Node: add RRSet.");
+
+ // Test RRSet getters
+ knot_rrset_t *n_rrset = node_create_rrset(node, KNOT_RRTYPE_TXT);
+ ok(n_rrset && knot_rrset_equal(n_rrset, dummy_rrset, true),
+ "Node: create existing RRSet.");
+
+ knot_rrset_free(n_rrset, NULL);
+
+ n_rrset = node_create_rrset(node, KNOT_RRTYPE_SOA);
+ ok(n_rrset == NULL, "Node: create non-existing RRSet.");
+
+ knot_rrset_t stack_rrset = node_rrset(node, KNOT_RRTYPE_TXT);
+ ok(knot_rrset_equal(&stack_rrset, dummy_rrset, true), "Node: get existing RRSet.");
+ stack_rrset = node_rrset(node, KNOT_RRTYPE_SOA);
+ ok(knot_rrset_empty(&stack_rrset), "Node: get non-existent RRSet.");
+
+ knot_rdataset_t *n_rdataset = node_rdataset(node, KNOT_RRTYPE_TXT);
+ ok(n_rdataset && knot_rdataset_eq(n_rdataset, &dummy_rrset->rrs),
+ "Node: get existing rdataset.");
+ n_rdataset = node_rdataset(node, KNOT_RRTYPE_SOA);
+ ok(n_rdataset == NULL, "Node: get non-existing rdataset.");
+
+ stack_rrset = node_rrset_at(node, 0);
+ ok(knot_rrset_equal(&stack_rrset, dummy_rrset, true),
+ "Node: get existing position.");
+ stack_rrset = node_rrset_at(node, 1);
+ ok(knot_rrset_empty(&stack_rrset), "Node: get non-existent position.");
+
+ // Test TTL mismatch
+ dummy_rrset->ttl = 1800;
+ ret = node_add_rrset(node, dummy_rrset, NULL);
+ ok(ret == KNOT_ETTL && node->rrset_count == 1,
+ "Node: add RRSet, TTL mismatch.");
+
+ knot_rrset_free(dummy_rrset, NULL);
+
+ // Test bool functions
+ ok(node_rrtype_exists(node, KNOT_RRTYPE_TXT), "Node: type exists.");
+ ok(!node_rrtype_exists(node, KNOT_RRTYPE_AAAA), "Node: type does not exist.");
+ ok(!node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is not signed.");
+
+ dummy_rrset = create_dummy_rrsig(dummy_owner, KNOT_RRTYPE_TXT);
+ ret = node_add_rrset(node, dummy_rrset, NULL);
+ assert(ret == KNOT_EOK);
+
+ ok(node_rrtype_is_signed(node, KNOT_RRTYPE_TXT), "Node: type is signed.");
+
+ knot_rrset_free(dummy_rrset, NULL);
+
+ // Test remove RRset
+ node_remove_rdataset(node, KNOT_RRTYPE_AAAA);
+ ok(node->rrset_count == 2, "Node: remove non-existent rdataset.");
+ node_remove_rdataset(node, KNOT_RRTYPE_TXT);
+ ok(node->rrset_count == 1, "Node: remove existing rdataset.");
+
+ // "Test" freeing
+ node_free_rrsets(node, NULL);
+ ok(node->rrset_count == 0, "Node: free RRSets.");
+
+ node_free(node, NULL);
+
+ knot_dname_free(dummy_owner, NULL);
+
+ return 0;
+}
diff --git a/tests/knot/test_process_query.c b/tests/knot/test_process_query.c
new file mode 100644
index 0000000..83f62d8
--- /dev/null
+++ b/tests/knot/test_process_query.c
@@ -0,0 +1,201 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libknot/descriptor.h"
+#include "libknot/packet/wire.h"
+#include "knot/nameserver/process_query.h"
+#include "test_server.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/mempool.h"
+
+/* Basic response check (4 TAP tests). */
+static void answer_sanity_check(const uint8_t *query,
+ const uint8_t *answer, uint16_t answer_len,
+ uint8_t expected_rcode, const char *name)
+{
+ ok(answer_len >= KNOT_WIRE_HEADER_SIZE, "ns: len(%s answer) >= DNS header", name);
+ if (answer_len >= KNOT_WIRE_HEADER_SIZE) {
+ ok(knot_wire_get_qr(answer), "ns: %s answer has QR=1", name);
+ is_int(expected_rcode, knot_wire_get_rcode(answer), "ns: %s answer RCODE=%d", name, expected_rcode);
+ is_int(knot_wire_get_id(query), knot_wire_get_id(answer), "ns: %s MSGID match", name);
+ } else {
+ skip_block(3, "ns: can't check DNS header");
+ }
+}
+
+/* Resolve query and check answer for sanity (2 TAP tests). */
+static void exec_query(knot_layer_t *layer, const char *name,
+ knot_pkt_t *query,
+ uint8_t expected_rcode)
+{
+ knot_pkt_t *answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ assert(answer);
+
+ /* Input packet. */
+ knot_pkt_parse(query, 0);
+ knot_layer_consume(layer, query);
+
+ ok(layer->state == KNOT_STATE_PRODUCE ||
+ layer->state == KNOT_STATE_FAIL, "ns: process %s query", name);
+
+ /* Create answer. */
+ knot_layer_produce(layer, answer);
+ if (layer->state == KNOT_STATE_FAIL) {
+ /* Allow 1 generic error response. */
+ knot_layer_produce(layer, answer);
+ }
+
+ ok(layer->state == KNOT_STATE_DONE, "ns: answer %s query", name);
+
+ /* Check answer. */
+ answer_sanity_check(query->wire, answer->wire, answer->size, expected_rcode, name);
+
+ knot_pkt_free(answer);
+}
+
+/* \internal Helpers */
+#define WIRE_COPY(dst, dst_len, src, src_len) \
+ memcpy(dst, src, src_len); \
+ dst_len = src_len;
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Create processing context. */
+ knot_layer_t proc;
+ memset(&proc, 0, sizeof(knot_layer_t));
+ knot_layer_init(&proc, &mm, process_query_layer());
+
+ /* Create temporary storage directory. */
+ char *temp_dir = test_mkdtemp();
+ ok(temp_dir != NULL, "make temporary directory");
+
+ /* Create fake server environment. */
+ server_t server;
+ int ret = create_fake_server(&server, proc.mm, temp_dir);
+ is_int(KNOT_EOK, ret, "ns: fake server initialization");
+ if (ret != KNOT_EOK) {
+ goto fatal;
+ }
+
+ zone_t *zone = knot_zonedb_find(server.zone_db, ROOT_DNAME);
+
+ /* Prepare. */
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, proc.mm);
+
+ /* Create query processing parameter. */
+ struct sockaddr_storage ss;
+ memset(&ss, 0, sizeof(struct sockaddr_storage));
+ sockaddr_set(&ss, AF_INET, "127.0.0.1", 53);
+ knotd_qdata_params_t params = {
+ .proto = KNOTD_QUERY_PROTO_TCP,
+ .remote = &ss,
+ .server = &server
+ };
+
+ /* Query processor (CH zone) */
+ knot_layer_begin(&proc, &params);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, IDSERVER_DNAME, KNOT_CLASS_CH, KNOT_RRTYPE_TXT);
+ exec_query(&proc, "CH TXT", query, KNOT_RCODE_NOERROR);
+
+ /* Query processor (valid input). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ exec_query(&proc, "IN/root", query, KNOT_RCODE_NOERROR);
+
+ /* Query processor (-1 bytes, not enough data). */
+ knot_layer_reset(&proc);
+ query->size -= 1;
+ exec_query(&proc, "IN/few-data", query, KNOT_RCODE_FORMERR);
+ query->size += 1;
+
+ /* Query processor (+1 bytes trailing). */
+ knot_layer_reset(&proc);
+ query->wire[query->size] = '\1'; /* Initialize the "garbage" value. */
+ query->size += 1;
+ exec_query(&proc, "IN/trail-garbage", query, KNOT_RCODE_FORMERR);
+ query->size -= 1;
+
+ /* Forge NOTIFY query from SOA query. */
+ knot_layer_reset(&proc);
+ knot_wire_set_opcode(query->wire, KNOT_OPCODE_NOTIFY);
+ exec_query(&proc, "IN/notify", query, KNOT_RCODE_NOTAUTH);
+
+ /* Forge AXFR query. */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_AXFR);
+ exec_query(&proc, "IN/axfr", query, KNOT_RCODE_NOTAUTH);
+
+ /* Forge IXFR query (well formed). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_IXFR);
+ /* Append SOA RR. */
+ knot_rrset_t soa_rr = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA);
+ knot_pkt_begin(query, KNOT_AUTHORITY);
+ knot_pkt_put(query, KNOT_COMPR_HINT_NONE, &soa_rr, 0);
+ exec_query(&proc, "IN/ixfr", query, KNOT_RCODE_NOTAUTH);
+
+ /* \note Tests below are not possible without proper zone and zone data. */
+ /* #189 Process UPDATE query. */
+ /* #189 Process AXFR client. */
+ /* #189 Process IXFR client. */
+
+ /* Query processor (smaller than DNS header, ignore). */
+ knot_layer_reset(&proc);
+ knot_pkt_clear(query);
+ knot_pkt_put_question(query, ROOT_DNAME, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ size_t orig_query_size = query->size;
+ query->size = KNOT_WIRE_HEADER_SIZE - 1;
+ knot_layer_consume(&proc, query);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
+ query->size = orig_query_size;
+
+ /* Query processor (response, ignore). */
+ knot_layer_reset(&proc);
+ knot_wire_set_qr(query->wire);
+ knot_layer_consume(&proc, query);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: IN/less-than-header query ignored");
+
+ /* Finish. */
+ knot_layer_finish(&proc);
+ ok(proc.state == KNOT_STATE_NOOP, "ns: processing end" );
+
+fatal:
+ /* Cleanup. */
+ mp_delete((struct mempool *)mm.ctx);
+ server_deinit(&server);
+ conf_free(conf());
+ test_rm_rf(temp_dir);
+ free(temp_dir);
+
+ return 0;
+}
+
+#undef WIRE_COPY
diff --git a/tests/knot/test_query_module.c b/tests/knot/test_query_module.c
new file mode 100644
index 0000000..4ab14d2
--- /dev/null
+++ b/tests/knot/test_query_module.c
@@ -0,0 +1,87 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libknot/libknot.h"
+#include "knot/nameserver/query_module.h"
+#include "libknot/packet/pkt.h"
+
+/* Universal processing stage. */
+unsigned state_visit(unsigned state, knot_pkt_t *pkt, knotd_qdata_t *qdata,
+ knotd_mod_t *mod)
+{
+ /* Visit current state */
+ bool *state_map = (bool *)mod;
+ state_map[state] = true;
+
+ return state + 1;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create a map of expected steps. */
+ bool state_map[KNOTD_STAGES] = { false };
+
+ /* Prepare query plan. */
+ struct query_plan *plan = query_plan_create();
+ ok(plan != NULL, "query_plan: create");
+ if (plan == NULL) {
+ goto fatal;
+ }
+
+ /* Register all stage visits. */
+ int ret = KNOT_EOK;
+ for (unsigned stage = KNOTD_STAGE_BEGIN; stage < KNOTD_STAGES; ++stage) {
+ ret = query_plan_step(plan, stage, state_visit, state_map);
+ if (ret != KNOT_EOK) {
+ break;
+ }
+ }
+ is_int(KNOT_EOK, ret, "query_plan: planned all steps");
+
+ /* Execute the plan. */
+ int state = 0, next_state = 0;
+ for (unsigned stage = KNOTD_STAGE_BEGIN; stage < KNOTD_STAGES; ++stage) {
+ struct query_step *step = NULL;
+ WALK_LIST(step, plan->stage[stage]) {
+ next_state = step->process(state, NULL, NULL, step->ctx);
+ if (next_state != state + 1) {
+ break;
+ }
+ state = next_state;
+ }
+ }
+ ok(state == KNOTD_STAGES, "query_plan: executed all steps");
+
+ /* Verify if all steps executed their callback. */
+ for (state = 0; state < KNOTD_STAGES; ++state) {
+ if (state_map[state] == false) {
+ break;
+ }
+ }
+ ok(state == KNOTD_STAGES, "query_plan: executed all callbacks");
+
+fatal:
+ /* Free the query plan. */
+ query_plan_free(plan);
+
+ return 0;
+}
diff --git a/tests/knot/test_requestor.c b/tests/knot/test_requestor.c
new file mode 100644
index 0000000..e0bd360
--- /dev/null
+++ b/tests/knot/test_requestor.c
@@ -0,0 +1,188 @@
+/* Copyright (C) 2023 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 <assert.h>
+#include <tap/basic.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+#include "knot/query/layer.h"
+#include "knot/query/requestor.h"
+#include "contrib/mempattern.h"
+#include "contrib/net.h"
+#include "contrib/sockaddr.h"
+#include "contrib/ucw/mempool.h"
+
+bool TFO = false;
+
+/* @note Purpose of this test is not to verify process_answer functionality,
+ * but simply if the requesting/receiving works, so mirror is okay. */
+static int reset(knot_layer_t *ctx) { return KNOT_STATE_PRODUCE; }
+static int begin(knot_layer_t *ctx, void *module_param) { return reset(ctx); }
+static int finish(knot_layer_t *ctx) { return reset(ctx); }
+static int in(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_DONE; }
+static int out(knot_layer_t *ctx, knot_pkt_t *pkt) { return KNOT_STATE_CONSUME; }
+
+static const int TIMEOUT = 2000;
+
+/*! \brief Dummy answer processing module. */
+const knot_layer_api_t dummy_module = {
+ &begin, &reset, &finish, &in, &out
+};
+
+static void set_blocking_mode(int sock)
+{
+ int flags = fcntl(sock, F_GETFL);
+ flags &= ~O_NONBLOCK;
+ fcntl(sock, F_SETFL, flags);
+}
+
+static void *responder_thread(void *arg)
+{
+ int fd = *(int *)arg;
+
+ set_blocking_mode(fd);
+ uint8_t buf[KNOT_WIRE_MAX_PKTSIZE] = { 0 };
+ while (true) {
+ int client = accept(fd, NULL, NULL);
+ if (client < 0) {
+ break;
+ }
+ int len = net_dns_tcp_recv(client, buf, sizeof(buf), -1);
+ if (len < KNOT_WIRE_HEADER_SIZE) {
+ close(client);
+ break;
+ }
+ knot_wire_set_qr(buf);
+ net_dns_tcp_send(client, buf, len, -1, NULL);
+ close(client);
+ }
+
+ return NULL;
+}
+
+/* Test implementations. */
+
+static knot_request_t *make_query(knot_requestor_t *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, requestor->mm);
+ assert(pkt);
+ static const knot_dname_t *root = (uint8_t *)"";
+ knot_pkt_put_question(pkt, root, KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+
+ knot_request_flag_t flags = TFO ? KNOT_REQUEST_TFO: KNOT_REQUEST_NONE;
+
+ return knot_request_make(requestor->mm, dst, src, pkt, NULL, flags);
+}
+
+static void test_disconnected(knot_requestor_t *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ knot_request_t *req = make_query(requestor, dst, src);
+ int ret = knot_requestor_exec(requestor, req, TIMEOUT);
+ /* ECONNREFUSED on FreeBSD, ETIMEOUT on NetBSD/OpenBSD/macOS. */
+ ret = (ret == KNOT_ECONNREFUSED || ret == KNOT_ETIMEOUT) ? KNOT_ECONN : ret;
+ is_int(KNOT_ECONN, ret, "requestor: disconnected/exec");
+ knot_request_free(req, requestor->mm);
+
+}
+
+static void test_connected(knot_requestor_t *requestor,
+ const struct sockaddr_storage *dst,
+ const struct sockaddr_storage *src)
+{
+ /* Enqueue packet. */
+ knot_request_t *req = make_query(requestor, dst, src);
+ int ret = knot_requestor_exec(requestor, req, TIMEOUT);
+ is_int(KNOT_EOK, ret, "requestor: connected/exec");
+ knot_request_free(req, requestor->mm);
+}
+
+int main(int argc, char *argv[])
+{
+#if defined(__linux__)
+ FILE *fd = fopen("/proc/sys/net/ipv4/tcp_fastopen", "r");
+ if (fd != NULL) {
+ int val = fgetc(fd);
+ fclose(fd);
+ // 0 - disabled, 1 - server TFO (client fallbacks),
+ // 2 - client TFO, 3 - both
+ if (val == '1' || val == '3') {
+ TFO = true;
+ }
+ }
+#endif
+ plan_lazy();
+
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Initialize requestor. */
+ knot_requestor_t requestor;
+ knot_requestor_init(&requestor, &dummy_module, NULL, &mm);
+
+ /* Define endpoints. */
+ struct sockaddr_storage client = { 0 };
+ sockaddr_set(&client, AF_INET, "127.0.0.1", 0);
+ struct sockaddr_storage server = { 0 };
+ sockaddr_set(&server, AF_INET, "127.0.0.1", 0);
+
+ /* Bind to random port. */
+ int responder_fd = net_bound_socket(SOCK_STREAM, &server, 0, 0);
+ assert(responder_fd >= 0);
+ socklen_t addr_len = sockaddr_len(&server);
+ int ret = getsockname(responder_fd, (struct sockaddr *)&server, &addr_len);
+ ok(ret == 0, "check getsockname return");
+
+ /* Test requestor in disconnected environment. */
+ test_disconnected(&requestor, &server, &client);
+
+ /* Start responder. */
+ ret = listen(responder_fd, 10);
+ ok(ret == 0, "check listen return");
+
+ if (TFO) {
+ ret = net_bound_tfo(responder_fd, 10);
+ ok(ret == KNOT_EOK, "check bound TFO return");
+ }
+
+ pthread_t thread;
+ pthread_create(&thread, 0, responder_thread, &responder_fd);
+
+ /* Test requestor in connected environment. */
+ test_connected(&requestor, &server, &client);
+
+ /* Terminate responder. */
+ int conn = net_connected_socket(SOCK_STREAM, &server, NULL, false);
+ assert(conn > 0);
+ conn = net_dns_tcp_send(conn, (uint8_t *)"", 1, TIMEOUT, NULL);
+ assert(conn > 0);
+ pthread_join(thread, NULL);
+ close(responder_fd);
+
+ /* Cleanup. */
+ mp_delete((struct mempool *)mm.ctx);
+
+ return 0;
+}
diff --git a/tests/knot/test_semantic_check.in b/tests/knot/test_semantic_check.in
new file mode 100644
index 0000000..dc29f0a
--- /dev/null
+++ b/tests/knot/test_semantic_check.in
@@ -0,0 +1,169 @@
+#!/bin/sh
+
+KZONECHECK="@top_builddir@/src/kzonecheck"
+DATA="@top_srcdir@/tests/knot/semantic_check_data"
+
+. "@top_srcdir@/tests/tap/libtap.sh"
+
+TMPDIR=$(test_tmpdir)
+LOG="$TMPDIR/log"
+
+# Params: zonefile fatal_error expected_erros_count semcheck_err_msg
+expect_error()
+{
+ if [ ! -r "$DATA/$1" ]; then
+ skip_block 4 "missing zone file for test"
+ return
+ fi
+
+ "$KZONECHECK" -o example.com "$DATA/$1" > "$LOG"
+ ok "$1 - check program return" test $? -eq 1
+
+ fatal=$(grep -E "^Serious semantic error detected" $LOG | wc -l)
+ ok "$1 - check fatal" test $fatal -eq $2
+
+ errors=$(grep -E "^\[.+\] $4" $LOG | wc -l)
+ ok "$1 - check errors" test $errors -eq $3
+ if [ $errors != $3 ]; then
+ diag "expected errors $3 but found $errors"
+ fi
+}
+
+#param zonefile
+test_correct()
+{
+ $KZONECHECK -o example.com "$DATA/$1" > /dev/null
+ ok "$1 - correct zone, without error" test $? -eq 0
+}
+
+#param zonefile
+test_correct_no_dnssec()
+{
+ $KZONECHECK -o example.com -d off "$DATA/$1" > /dev/null
+ ok "$1 - correct zone, without error" test $? -eq 0
+}
+
+if [ ! -x $KZONECHECK ]; then
+ skip_all "kzonecheck is missing or is not executable"
+fi
+
+# error messages exported from knot/src/zone/semantic-check.c
+CDNSKEY_NONE="missing CDNSKEY"
+CDNSKEY_NO_CDS="CDNSKEY without corresponding CDS"
+CDNSKEY_DELETE="invalid CDNSKEY/CDS for DNSSEC delete algorithm"
+CDS_NONE="missing CDS"
+CDS_NOT_MATCH="CDS not match CDNSKEY"
+CNAME_EXTRA_RECORDS="another record exists beside CNAME"
+CNAME_MULTIPLE="multiple CNAME records"
+DNAME_CHILDREN="child record exists under DNAME"
+DNAME_MULTIPLE="multiple DNAME records"
+DNAME_EXTRA_NS="NS record exists beside DNAME"
+DNSKEY_INVALID="invalid DNSKEY"
+DS_ALG="invalid algorithm in DS"
+NSEC3PARAM_FLAGS="invalid flags in NSEC3PARAM"
+NSEC_NONE="missing NSEC\(3\) record"
+NSEC_RDATA_BITMAP="wrong NSEC\(3\) bitmap"
+NSEC_RDATA_CHAIN="inconsistent NSEC\(3\) chain"
+NSEC3_INSECURE_DELEGATION_OPT="wrong NSEC3 opt-out"
+NS_APEX="missing NS at the zone apex"
+NS_GLUE="missing glue record"
+RRSIG_UNVERIFIABLE="no valid signature for a record"
+
+plan_lazy
+
+expect_error "cname_extra_01.zone" 1 1 "$CNAME_EXTRA_RECORDS"
+expect_error "cname_extra_02.signed" 1 1 "$CNAME_EXTRA_RECORDS"
+expect_error "cname_multiple.zone" 1 1 "$CNAME_MULTIPLE"
+expect_error "dname_children.zone" 1 1 "$DNAME_CHILDREN"
+expect_error "dname_multiple.zone" 1 1 "$DNAME_MULTIPLE"
+expect_error "dname_extra_ns.zone" 1 1 "$DNAME_EXTRA_NS"
+
+expect_error "ns_apex.missing" 0 1 "$NS_APEX"
+expect_error "glue_apex_both.missing" 0 2 "$NS_GLUE"
+expect_error "glue_apex_one.missing" 0 1 "$NS_GLUE"
+expect_error "glue_besides.missing" 0 1 "$NS_GLUE"
+expect_error "glue_deleg.missing" 0 1 "$NS_GLUE"
+expect_error "glue_in_apex.missing" 0 1 "$NS_GLUE"
+expect_error "different_signer_name.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "no_rrsig_with_delegation.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "nsec_broken_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec_broken_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec_missing.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec_multiple.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_missing.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec3_optout_ent.invalid" 0 1 "$NSEC_NONE"
+expect_error "nsec3_wrong_bitmap_01.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_wrong_bitmap_02.signed" 0 1 "$NSEC_RDATA_BITMAP"
+expect_error "nsec3_ds.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec3_optout.signed" 0 1 "$NSEC3_INSECURE_DELEGATION_OPT"
+expect_error "nsec3_chain_01.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_02.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_chain_03.signed" 0 1 "$NSEC_RDATA_CHAIN"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC_NONE"
+expect_error "nsec3_param_invalid.signed" 0 1 "$NSEC3PARAM_FLAGS"
+expect_error "rrsig_signed.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "rrsig_rdata_ttl.signed" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "duplicate.signature" 0 1 "$RRSIG_UNVERIFIABLE"
+expect_error "missing.signed" 0 1 "$NSEC_NONE"
+expect_error "dnskey_param_error.signed" 0 1 "$DNSKEY_INVALID"
+expect_error "invalid_ds.signed" 0 2 "$DS_ALG \(keytag 60485\)"
+expect_error "cdnskey.invalid" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.invalid.param" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.nocds" 0 1 "$CDS_NONE"
+expect_error "cdnskey.nocdnskey" 0 1 "$CDNSKEY_NONE"
+expect_error "cdnskey.nodnskey" 0 1 "$CDNSKEY_NOT_MATCH"
+expect_error "cdnskey.orphan.cds" 0 1 "$CDS_NOT_MATCH"
+expect_error "cdnskey.orphan.cdnskey" 0 1 "$CDNSKEY_NO_CDS"
+expect_error "cdnskey.delete.invalid.cds" 0 1 "$CDNSKEY_DELETE"
+expect_error "cdnskey.delete.invalid.cdnskey" 0 1 "$CDNSKEY_DELETE"
+expect_error "delegation.signed" 0 1 "$NSEC_RDATA_BITMAP"
+
+test_correct "rrsig_ttl.signed"
+test_correct "no_error_delegation_bitmap.signed"
+test_correct "no_error_nsec3_optout.signed"
+test_correct "glue_wildcard.valid"
+test_correct "glue_no_foreign.valid"
+test_correct "glue_in_deleg.valid"
+test_correct "cdnskey.cds"
+test_correct "cdnskey.delete.both"
+test_correct "dname_apex_nsec3.signed"
+test_correct "nsec3_optout_ent.valid"
+test_correct "nsec3_optout_ent.all"
+
+test_correct_no_dnssec "no_rrsig.signed"
+test_correct_no_dnssec "no_rrsig_with_delegation.signed"
+test_correct_no_dnssec "nsec_broken_chain_01.signed"
+test_correct_no_dnssec "nsec_broken_chain_02.signed"
+test_correct_no_dnssec "nsec_missing.signed"
+test_correct_no_dnssec "nsec_multiple.signed"
+test_correct_no_dnssec "nsec_wrong_bitmap_01.signed"
+test_correct_no_dnssec "nsec_wrong_bitmap_02.signed"
+test_correct_no_dnssec "nsec3_missing.signed"
+test_correct_no_dnssec "nsec3_wrong_bitmap_01.signed"
+test_correct_no_dnssec "nsec3_wrong_bitmap_02.signed"
+test_correct_no_dnssec "nsec3_ds.signed"
+test_correct_no_dnssec "nsec3_optout.signed"
+test_correct_no_dnssec "nsec3_chain_01.signed"
+test_correct_no_dnssec "nsec3_chain_02.signed"
+test_correct_no_dnssec "nsec3_chain_03.signed"
+test_correct_no_dnssec "nsec3_param_invalid.signed"
+test_correct_no_dnssec "rrsig_signed.signed"
+test_correct_no_dnssec "rrsig_rdata_ttl.signed"
+test_correct_no_dnssec "duplicate.signature"
+test_correct_no_dnssec "missing.signed"
+test_correct_no_dnssec "dnskey_param_error.signed"
+test_correct_no_dnssec "cdnskey.invalid"
+test_correct_no_dnssec "cdnskey.invalid.param"
+test_correct_no_dnssec "cdnskey.nocds"
+test_correct_no_dnssec "cdnskey.nocdnskey"
+test_correct_no_dnssec "cdnskey.nodnskey"
+test_correct_no_dnssec "cdnskey.orphan.cds"
+test_correct_no_dnssec "cdnskey.orphan.cdnskey"
+test_correct_no_dnssec "cdnskey.delete.invalid.cds"
+test_correct_no_dnssec "cdnskey.delete.invalid.cdnskey"
+test_correct_no_dnssec "delegation.signed"
+
+rm $LOG
diff --git a/tests/knot/test_server.c b/tests/knot/test_server.c
new file mode 100644
index 0000000..bde3d10
--- /dev/null
+++ b/tests/knot/test_server.c
@@ -0,0 +1,72 @@
+/* Copyright (C) 2021 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 <signal.h>
+#include <tap/basic.h>
+#include "knot/server/server.h"
+#include "test_conf.h"
+
+// Signal handler
+static void interrupt_handle(int s)
+{
+}
+
+/*! API: run tests. */
+int main(int argc, char *argv[])
+{
+ plan(2);
+
+ server_t server;
+ int ret = 0;
+
+ /* Some random configuration just to apply the default conf schema */
+ ret = test_conf("", NULL);
+ assert(ret == KNOT_EOK);
+
+ /* Register service and signal handler */
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ /* Test server for correct initialization */
+ ret = server_init(&server, 1);
+ is_int(KNOT_EOK, ret, "server: initialized");
+ if (ret != KNOT_EOK) {
+ return 1;
+ }
+
+ /* Test server startup */
+ ret = server_start(&server, false);
+ is_int(KNOT_EOK, ret, "server: started ok");
+ if (ret != KNOT_EOK) {
+ return 1;
+ }
+
+ server_stop(&server);
+
+ /* Wait for server to finish. */
+ server_wait(&server);
+
+ /* Destroy the server structure. */
+ server_deinit(&server);
+
+ /* Remove the configuration. */
+ conf_free(conf());
+
+ return 0;
+}
diff --git a/tests/knot/test_server.h b/tests/knot/test_server.h
new file mode 100644
index 0000000..d68561d
--- /dev/null
+++ b/tests/knot/test_server.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include "test_conf.h"
+#include "knot/server/server.h"
+#include "knot/zone/adjust.h"
+#include "contrib/mempattern.h"
+
+/* Some domain names. */
+#define ROOT_DNAME ((const uint8_t *)"")
+#define EXAMPLE_DNAME ((const uint8_t *)"\x7""example")
+#define IDSERVER_DNAME ((const uint8_t *)"\2""id""\6""server")
+
+/* Create fake root zone. */
+static inline void create_root_zone(server_t *server, knot_mm_t *mm)
+{
+ /* SOA RDATA. */
+ #define SOA_RDLEN 30
+ static const uint8_t SOA_RDATA[SOA_RDLEN] = {
+ 0x02, 'n', 's', 0x00, /* ns. */
+ 0x04, 'm', 'a', 'i', 'l', 0x00,/* mail. */
+ 0x77, 0xdf, 0x1e, 0x63, /* serial */
+ 0x00, 0x01, 0x51, 0x80, /* refresh */
+ 0x00, 0x00, 0x1c, 0x20, /* retry */
+ 0x00, 0x0a, 0x8c, 0x00, /* expire */
+ 0x00, 0x00, 0x0e, 0x10 /* min ttl */
+ };
+
+ /* Insert root zone. */
+ zone_t *root = zone_new(ROOT_DNAME);
+ root->server = server;
+ root->contents = zone_contents_new(root->name, true);
+
+ knot_rrset_t *soa = knot_rrset_new(root->name, KNOT_RRTYPE_SOA, KNOT_CLASS_IN,
+ 7200, mm);
+ knot_rrset_add_rdata(soa, SOA_RDATA, SOA_RDLEN, mm);
+ node_add_rrset(root->contents->apex, soa, NULL);
+ knot_rrset_free(soa, mm);
+
+ /* Bake the zone. */
+ (void)zone_adjust_full(root->contents, 1);
+
+ /* Switch zone db. */
+ knot_zonedb_free(&server->zone_db);
+ server->zone_db = knot_zonedb_new();
+ knot_zonedb_insert(server->zone_db, root);
+}
+
+/* Create fake server. */
+static inline int create_fake_server(server_t *server, knot_mm_t *mm, const char *db_storage)
+{
+ int ret;
+
+ /* Create test configuration. */
+ /* String `db_storage' obtained from test_mkdtemp() may be up to 4096 bytes. */
+ char conf_str[4096 + 512];
+ (void)snprintf(conf_str, sizeof(conf_str),
+ "server:\n"
+ " identity: bogus.ns\n"
+ " version: 0.11\n"
+ " nsid: \n"
+ "database:\n"
+ " storage: %s\n"
+ "zone:\n"
+ " - domain: .\n"
+ " zonefile-sync: -1\n",
+ db_storage);
+
+ /* Load test configuration. */
+ ret = test_conf(conf_str, NULL);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Create name server. */
+ ret = server_init(server, 1);
+ if (ret != KNOT_EOK) {
+ return ret;
+ }
+
+ /* Insert root zone. */
+ create_root_zone(server, mm);
+
+ return KNOT_EOK;
+}
diff --git a/tests/knot/test_unreachable.c b/tests/knot/test_unreachable.c
new file mode 100644
index 0000000..826544d
--- /dev/null
+++ b/tests/knot/test_unreachable.c
@@ -0,0 +1,59 @@
+/* Copyright (C) 2022 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 <tap/basic.h>
+
+#include "knot/common/unreachable.h"
+
+#include "contrib/sockaddr.h"
+
+#define UR_TEST_ADDRS 32
+struct sockaddr_storage ur_test_addrs[UR_TEST_ADDRS] = { { 0 } };
+struct sockaddr_storage ur_test_via[2] = { { 0 } };
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ global_unreachables = knot_unreachables_init(10);
+ ok(global_unreachables != NULL, "unreachables: init");
+
+ // ur_test_via[0] left empty - AF_UNSPEC
+ sockaddr_set(&ur_test_via[1], AF_INET6, "::1", 0);
+
+ for (int i = 0; i < UR_TEST_ADDRS; i++) {
+ struct sockaddr_storage *s = &ur_test_addrs[i];
+ sockaddr_set(s, AF_INET6, "::2", i + 1);
+ struct sockaddr_storage *via = &ur_test_via[i % 2];
+ struct sockaddr_storage *not_via = &ur_test_via[1 - i % 2];
+
+ ok(!knot_unreachable_is(global_unreachables, s, via), "unreachables: pre[%d]", i);
+ knot_unreachable_add(global_unreachables, s, via);
+ ok(knot_unreachable_is(global_unreachables, s, via), "unreachables: post[%d]", i);
+ ok(!knot_unreachable_is(global_unreachables, s, not_via), "unreachables: via[%d]", i);
+
+ usleep(1000);
+ if (i >= 10) {
+ ok(!knot_unreachable_is(global_unreachables, &ur_test_addrs[i - 10], via),
+ "unreachables: expired[%d]", i - 10);
+ }
+ }
+
+ knot_unreachables_deinit(&global_unreachables);
+ ok(global_unreachables == NULL, "unreachables: deinit");
+
+ return 0;
+}
diff --git a/tests/knot/test_worker_pool.c b/tests/knot/test_worker_pool.c
new file mode 100644
index 0000000..59dee36
--- /dev/null
+++ b/tests/knot/test_worker_pool.c
@@ -0,0 +1,152 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <time.h>
+
+#include "knot/worker/pool.h"
+#include "knot/worker/queue.h"
+
+#define THREADS 4
+#define TASKS_BATCH 40
+
+/*!
+ * Task execution log.
+ */
+typedef struct task_log {
+ pthread_mutex_t mx;
+ unsigned executed;
+} task_log_t;
+
+/*!
+ * Get number of executed tasks and clear.
+ */
+static unsigned executed_reset(task_log_t *log)
+{
+ pthread_mutex_lock(&log->mx);
+ unsigned result = log->executed;
+ log->executed = 0;
+ pthread_mutex_unlock(&log->mx);
+
+ return result;
+}
+
+/*!
+ * Simple task, just increases the counter in the log.
+ */
+static void task_counting(worker_task_t *task)
+{
+ task_log_t *log = task->ctx;
+
+ pthread_mutex_lock(&log->mx);
+ log->executed += 1;
+ pthread_mutex_unlock(&log->mx);
+}
+
+static void interrupt_handle(int s)
+{
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ struct sigaction sa;
+ sa.sa_handler = interrupt_handle;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL); // Interrupt
+
+ // create pool
+
+ worker_pool_t *pool = worker_pool_create(THREADS);
+ ok(pool != NULL, "create worker pool");
+ if (!pool) {
+ return 1;
+ }
+
+ task_log_t log = {
+ .mx = PTHREAD_MUTEX_INITIALIZER,
+ };
+
+ // schedule jobs while pool is stopped
+
+ worker_task_t task = { .run = task_counting, .ctx = &log };
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ sched_yield();
+ ok(executed_reset(&log) == 0, "executed count before start");
+
+ // start and wait for finish
+
+ worker_pool_start(pool);
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after start");
+
+ // add additional jobs while pool is running
+
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after add");
+
+ // temporary suspension
+
+ worker_pool_suspend(pool);
+
+ for (int i = 0; i < TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+
+ sched_yield();
+ ok(executed_reset(&log) == 0, "executed count after suspend");
+
+ worker_pool_resume(pool);
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) == TASKS_BATCH, "executed count after resume");
+
+ // try clean
+
+ pthread_mutex_lock(&log.mx);
+ for (int i = 0; i < THREADS + TASKS_BATCH; i++) {
+ worker_pool_assign(pool, &task);
+ }
+ sched_yield();
+ worker_pool_clear(pool);
+ pthread_mutex_unlock(&log.mx);
+
+ worker_pool_wait(pool);
+ ok(executed_reset(&log) <= THREADS, "executed count after clear");
+
+ // cleanup
+
+ worker_pool_stop(pool);
+ worker_pool_join(pool);
+ worker_pool_destroy(pool);
+
+ pthread_mutex_destroy(&log.mx);
+
+ return 0;
+}
diff --git a/tests/knot/test_worker_queue.c b/tests/knot/test_worker_queue.c
new file mode 100644
index 0000000..2d20afd
--- /dev/null
+++ b/tests/knot/test_worker_queue.c
@@ -0,0 +1,57 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+
+#include "knot/worker/queue.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ worker_task_t task_one = { 0 };
+ worker_task_t task_two = { 0 };
+ worker_task_t task_three = { 0 };
+
+ // init
+
+ worker_queue_t queue;
+ worker_queue_init(&queue);
+ ok(1, "queue init");
+
+ // enqueue
+
+ worker_queue_enqueue(&queue, &task_one);
+ ok(1, "enqueue first");
+ worker_queue_enqueue(&queue, &task_two);
+ ok(1, "enqueue second");
+
+ // dequeue
+
+ ok(worker_queue_dequeue(&queue) == &task_one, "dequeue first");
+ ok(worker_queue_dequeue(&queue) == &task_two, "dequeue second");
+ ok(worker_queue_dequeue(&queue) == NULL, "dequeue from empty");
+
+ // deinit
+
+ worker_queue_enqueue(&queue, &task_three);
+ ok(1, "enqueue third");
+
+ worker_queue_deinit(&queue);
+ ok(1, "queue deinit");
+
+ return 0;
+}
diff --git a/tests/knot/test_zone-tree.c b/tests/knot/test_zone-tree.c
new file mode 100644
index 0000000..59207ae
--- /dev/null
+++ b/tests/knot/test_zone-tree.c
@@ -0,0 +1,135 @@
+/* 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 <stdlib.h>
+#include <tap/basic.h>
+
+#include "libknot/errcode.h"
+#include "knot/zone/zone-tree.h"
+
+#define NCOUNT 4
+static knot_dname_t* NAME[NCOUNT];
+static zone_node_t NODEE[NCOUNT];
+static knot_dname_t* ORDER[NCOUNT];
+static void ztree_init_data(void)
+{
+ NAME[0] = knot_dname_from_str_alloc(".");
+ NAME[1] = knot_dname_from_str_alloc("master.ac.");
+ NAME[2] = knot_dname_from_str_alloc("ac.");
+ NAME[3] = knot_dname_from_str_alloc("ns.");
+
+ knot_dname_t *order[NCOUNT] = {
+ NAME[0], NAME[2], NAME[1], NAME[3]
+ };
+ memcpy(ORDER, order, NCOUNT * sizeof(knot_dname_t*));
+
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ memset(NODEE + i, 0, sizeof(zone_node_t));
+ NODEE[i].owner = NAME[i];
+ NODEE[i].prev = NODEE + ((NCOUNT + i - 1) % NCOUNT);
+ NODEE[i].rrset_count = 1; /* required for ordered search */
+ }
+}
+
+static void ztree_free_data(void)
+{
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ knot_dname_free(NAME[i], NULL);
+ }
+}
+
+static int ztree_iter_data(zone_node_t *node, void *data)
+{
+ unsigned *i = (unsigned *)data;
+ knot_dname_t *owner = node->owner;
+ int result = KNOT_EOK;
+ if (owner != ORDER[*i]) {
+ result = KNOT_ERROR;
+ char *exp_s = knot_dname_to_str_alloc(ORDER[*i]);
+ char *owner_s = knot_dname_to_str_alloc(owner);
+ diag("ztree: at index: %u expected '%s' got '%s'\n", *i, exp_s, owner_s);
+ free(exp_s);
+ free(owner_s);
+ }
+ ++(*i);
+ return result;
+}
+
+static int ztree_node_counter(zone_node_t *node, void *data)
+{
+ (void)node;
+ int *counter = data;
+ (*counter)++;
+ return KNOT_EOK;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ ztree_init_data();
+
+ /* 1. create test */
+ zone_tree_t* t = zone_tree_create(false);
+ ok(t != NULL, "ztree: created");
+
+ /* 2. insert test */
+ unsigned passed = 1;
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ zone_node_t *node = NODEE + i;
+ if (zone_tree_insert(t, &node) != KNOT_EOK) {
+ passed = 0;
+ break;
+ }
+ }
+ ok(passed, "ztree: insertion");
+
+ /* 3. check data test */
+ passed = 1;
+ for (unsigned i = 0; i < NCOUNT; ++i) {
+ zone_node_t *node = zone_tree_get(t, NAME[i]);
+ if (node == NULL || node != NODEE + i) {
+ passed = 0;
+ break;
+ }
+ }
+ ok(passed, "ztree: lookup");
+
+ /* 4. ordered lookup */
+ zone_node_t *node = NULL;
+ zone_node_t *prev = NULL;
+ knot_dname_t *tmp_dn = knot_dname_from_str_alloc("z.ac.");
+ zone_tree_get_less_or_equal(t, tmp_dn, &node, &prev);
+ knot_dname_free(tmp_dn, NULL);
+ ok(prev == NODEE + 1, "ztree: ordered lookup");
+
+ /* 5. ordered traversal */
+ unsigned i = 0;
+ int ret = zone_tree_apply(t, ztree_iter_data, &i);
+ ok (ret == KNOT_EOK, "ztree: ordered traversal");
+
+ /* 6. subtree apply */
+ int counter = 0;
+ ret = zone_tree_sub_apply(t, (const knot_dname_t *)"\x02""ac", false, ztree_node_counter, &counter);
+ ok(ret == KNOT_EOK && counter == 2, "ztree: subtree iteration");
+ counter = 0;
+ ret = zone_tree_sub_apply(t, (const knot_dname_t *)"\x02""ac", true, ztree_node_counter, &counter);
+ ok(ret == KNOT_EOK && counter == 1, "ztree: subtree iteration excluding root");
+
+ zone_tree_free(&t);
+ ztree_free_data();
+ return 0;
+}
diff --git a/tests/knot/test_zone-update.c b/tests/knot/test_zone-update.c
new file mode 100644
index 0000000..1346aaf
--- /dev/null
+++ b/tests/knot/test_zone-update.c
@@ -0,0 +1,337 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <pthread.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+#include <unistd.h>
+
+#include "test_conf.h"
+#include "contrib/getline.h"
+#include "knot/server/server.h"
+#include "knot/updates/zone-update.h"
+#include "knot/zone/adjust.h"
+#include "knot/zone/node.h"
+#include "libzscanner/scanner.h"
+
+static const char *zone_str1 = "test. 600 IN SOA ns.test. m.test. 1 900 300 4800 900 \n";
+static const char *zone_str2 = "test. 600 IN TXT \"test\"\n";
+static const char *add_str = "test. 600 IN TXT \"test2\"\n";
+static const char *del_str = "test. 600 IN TXT \"test\"\n";
+static const char *node_str1 = "node.test. 601 IN TXT \"abc\"\n";
+static const char *node_str2 = "node.test. 601 IN TXT \"def\"\n";
+
+knot_rrset_t rrset;
+
+/*!< \brief Returns true if node contains given RR in its RRSets. */
+static bool node_contains_rr(const zone_node_t *node, const knot_rrset_t *data)
+{
+ const knot_rdataset_t *zone_rrs = node_rdataset(node, data->type);
+ if (zone_rrs != NULL) {
+ knot_rdata_t *rr = data->rrs.rdata;
+ for (size_t i = 0; i < data->rrs.count; ++i) {
+ if (!knot_rdataset_member(zone_rrs, rr)) {
+ return false;
+ }
+ rr = knot_rdataset_next(rr);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+static void process_rr(zs_scanner_t *scanner)
+{
+ knot_rrset_init(&rrset, scanner->r_owner, scanner->r_type, scanner->r_class,
+ scanner->r_ttl);
+
+ int ret = knot_rrset_add_rdata(&rrset, scanner->r_data,
+ scanner->r_data_length, NULL);
+ (void)ret;
+ assert(ret == KNOT_EOK);
+}
+
+static int rr_data_cmp(struct rr_data *a, struct rr_data *b)
+{
+ if (a->type != b->type) {
+ return 1;
+ }
+ if (a->ttl != b->ttl) {
+ return 1;
+ }
+ if (a->rrs.count != b->rrs.count) {
+ return 1;
+ }
+ if (a->rrs.rdata != b->rrs.rdata) {
+ return 1;
+ }
+ if (a->additional != b->additional) {
+ return 1;
+ }
+ return 0;
+}
+
+static int test_node_unified(zone_node_t *n1, _unused_ void *v)
+{
+ zone_node_t *n2 = binode_node(n1, false);
+ if (n2 == n1) {
+ n2 = binode_node(n1, true);
+ }
+ ok(n1->owner == n2->owner, "binode %s has equal %s owner", n1->owner, n2->owner);
+ ok(n1->rrset_count == n2->rrset_count, "binode %s has equal rrset_count", n1->owner);
+ for (uint16_t i = 0; i < n1->rrset_count; i++) {
+ ok(rr_data_cmp(&n1->rrs[i], &n2->rrs[i]) == 0, "binode %s has equal rrs", n1->owner);
+ }
+ if (n1->flags & NODE_FLAGS_BINODE) {
+ ok((n1->flags ^ n2->flags) == NODE_FLAGS_SECOND, "binode %s has correct flags", n1->owner);
+ }
+ ok(n1->children == n2->children, "binode %s has equal children count", n1->owner);
+ return KNOT_EOK;
+}
+
+static void test_zone_unified(zone_t *z)
+{
+ knot_sem_wait(&z->cow_lock);
+ zone_tree_apply(z->contents->nodes, test_node_unified, NULL);
+ knot_sem_post(&z->cow_lock);
+}
+
+void test_full(zone_t *zone, zs_scanner_t *sc)
+{
+ zone_update_t update;
+ /* Init update */
+ int ret = zone_update_init(&update, zone, UPDATE_FULL);
+ is_int(KNOT_EOK, ret, "zone update: init full");
+
+ if (zs_set_input_string(sc, zone_str1, strlen(zone_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* First addition */
+ ret = zone_update_add(&update, &rrset);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ is_int(KNOT_EOK, ret, "full zone update: first addition");
+
+ if (zs_set_input_string(sc, zone_str2, strlen(zone_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* Second addition */
+ ret = zone_update_add(&update, &rrset);
+ zone_node_t *node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ bool rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: second addition");
+
+ /* Removal */
+ ret = zone_update_remove(&update, &rrset);
+ node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && !rrset_present, "full zone update: removal");
+
+ /* Last addition */
+ ret = zone_update_add(&update, &rrset);
+ node = (zone_node_t *) zone_update_get_node(&update, rrset.owner);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: last addition");
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ /* Prepare node removal */
+ if (zs_set_input_string(sc, node_str1, strlen(node_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ if (zs_set_input_string(sc, node_str2, strlen(node_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ knot_dname_t *rem_node_name = knot_dname_from_str_alloc("node.test");
+ node = (zone_node_t *) zone_update_get_node(&update, rem_node_name);
+ assert(node && node_rdataset(node, KNOT_RRTYPE_TXT)->count == 2);
+ /* Node removal */
+ ret = zone_update_remove_node(&update, rem_node_name);
+ node = (zone_node_t *) zone_update_get_node(&update, rem_node_name);
+ ok(ret == KNOT_EOK && !node, "full zone update: node removal");
+ knot_dname_free(rem_node_name, NULL);
+
+ /* Re-add a node for later incremental functionality test */
+ if (zs_set_input_string(sc, node_str1, strlen(node_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ /* Commit */
+ ret = zone_update_commit(conf(), &update);
+ node = zone_contents_find_node_for_rr(zone->contents, &rrset);
+ rrset_present = node_contains_rr(node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "full zone update: commit (max TTL: %u)", zone->contents->max_ttl);
+
+ test_zone_unified(zone);
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+}
+
+void test_incremental(zone_t *zone, zs_scanner_t *sc)
+{
+ int ret = KNOT_EOK;
+
+ /* Init update */
+ zone_update_t update;
+ zone_update_init(&update, zone, UPDATE_INCREMENTAL);
+ ok(update.zone == zone && changeset_empty(&update.change),
+ "incremental zone update: init");
+
+ if (zs_set_input_string(sc, add_str, strlen(add_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+
+ /* Addition */
+ ret = zone_update_add(&update, &rrset);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+ is_int(KNOT_EOK, ret, "incremental zone update: addition");
+
+ const zone_node_t *synth_node = update.new_cont->apex;
+ ok(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 2,
+ "incremental zone update: add change");
+
+ if (zs_set_input_string(sc, del_str, strlen(del_str)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ /* Removal */
+ ret = zone_update_remove(&update, &rrset);
+ is_int(KNOT_EOK, ret, "incremental zone update: removal");
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ ok(node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 1,
+ "incremental zone update: del change");
+
+ /* Prepare node removal */
+ if (zs_set_input_string(sc, node_str2, strlen(node_str2)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ knot_dname_t *rem_node_name = knot_dname_from_str_alloc("node.test");
+ synth_node = zone_update_get_node(&update, rem_node_name);
+ assert(synth_node && node_rdataset(synth_node, KNOT_RRTYPE_TXT)->count == 2);
+ /* Node Removal */
+ ret = zone_update_remove_node(&update, rem_node_name);
+ synth_node = zone_update_get_node(&update, rem_node_name);
+ ok(ret == KNOT_EOK && !synth_node,
+ "incremental zone update: node removal");
+ knot_dname_free(rem_node_name, NULL);
+
+ /* Re-add a node for later incremental functionality test */
+ if (zs_set_input_string(sc, node_str1, strlen(node_str1)) != 0 ||
+ zs_parse_all(sc) != 0) {
+ assert(0);
+ }
+ ret = zone_update_add(&update, &rrset);
+ assert(ret == KNOT_EOK);
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ /* Commit */
+ ret = zone_update_commit(conf(), &update);
+ const zone_node_t *iter_node = zone_contents_find_node_for_rr(zone->contents, &rrset);
+ bool rrset_present = node_contains_rr(iter_node, &rrset);
+ ok(ret == KNOT_EOK && rrset_present, "incremental zone update: commit");
+
+ test_zone_unified(zone);
+
+ knot_rdataset_clear(&rrset.rrs, NULL);
+
+ size_t zone_size1 = zone->contents->size;
+ uint32_t zone_max_ttl1 = zone->contents->max_ttl;
+ ret = zone_adjust_full(zone->contents, 2);
+ ok(ret == KNOT_EOK, "zone adjust full shall work");
+ size_t zone_size2 = zone->contents->size;
+ uint32_t zone_max_ttl2 = zone->contents->max_ttl;
+ ok(zone_size1 == zone_size2, "zone size measured the same incremental vs full (%zu, %zu)", zone_size1, zone_size2);
+ ok(zone_max_ttl1 == zone_max_ttl2, "zone max TTL measured the same incremental vs full (%u, %u)", zone_max_ttl1, zone_max_ttl2);
+ // TODO test more things after re-adjust, search for non-unified bi-nodes
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ char *temp_dir = test_mkdtemp();
+ ok(temp_dir != NULL, "make temporary directory");
+
+ char conf_str[512];
+ snprintf(conf_str, sizeof(conf_str),
+ "zone:\n"
+ " - domain: test.\n"
+ "database:\n"
+ " journal-db-max-size: 100M\n"
+ " storage: %s\n",
+ temp_dir);
+
+ /* Load test configuration. */
+ int ret = test_conf(conf_str, NULL);
+ is_int(KNOT_EOK, ret, "load configuration");
+
+ server_t server;
+ ret = server_init(&server, 1);
+ is_int(KNOT_EOK, ret, "server init");
+
+ /* Set up empty zone */
+ knot_dname_t *apex = knot_dname_from_str_alloc("test");
+ assert(apex);
+ zone_t *zone = zone_new(apex);
+ zone->server = &server;
+
+ /* Setup zscanner */
+ zs_scanner_t sc;
+ if (zs_init(&sc, "test.", KNOT_CLASS_IN, 3600) != 0 ||
+ zs_set_processing(&sc, process_rr, NULL, NULL) != 0) {
+ assert(0);
+ }
+
+ /* Test FULL update, commit it and use the result to test the INCREMENTAL update */
+ test_full(zone, &sc);
+ test_incremental(zone, &sc);
+
+ zs_deinit(&sc);
+ zone_free(&zone);
+ server_deinit(&server);
+ knot_dname_free(apex, NULL);
+ conf_free(conf());
+ test_rm_rf(temp_dir);
+ free(temp_dir);
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_events.c b/tests/knot/test_zone_events.c
new file mode 100644
index 0000000..18df288
--- /dev/null
+++ b/tests/knot/test_zone_events.c
@@ -0,0 +1,99 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "knot/common/evsched.h"
+#include "knot/worker/pool.h"
+#include "knot/events/events.h"
+#include "knot/zone/zone.h"
+
+static void test_scheduling(zone_t *zone)
+{
+ const time_t now = time(NULL);
+ const unsigned offset = 1000;
+
+ time_t timestamp = 0;
+ zone_event_type_t event = 0;
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp < 0 && event == ZONE_EVENT_INVALID, "nothing planned");
+
+ // scheduling
+
+ zone_events_schedule_at(zone, ZONE_EVENT_EXPIRE, now + offset);
+ zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, now + (offset / 2));
+
+ for (int i = 0; i < ZONE_EVENT_COUNT; i++) {
+ time_t t = zone_events_get_time(zone, i);
+ bool scheduled = i == ZONE_EVENT_EXPIRE || i == ZONE_EVENT_FLUSH;
+ const char *name = zone_events_get_name(i);
+
+ ok((t > 0) == scheduled && name, "event %s (%s)", name,
+ scheduled ? "scheduled" : "not scheduled");
+ }
+
+ // queuing
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp >= now + (offset / 2) && event == ZONE_EVENT_FLUSH, "flush is next");
+
+ zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, 0);
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp >= now + offset && event == ZONE_EVENT_EXPIRE, "expire is next");
+
+ zone_events_schedule_at(zone, ZONE_EVENT_EXPIRE, 0);
+
+ timestamp = zone_events_get_next(zone, &event);
+ ok(timestamp < 0 && event == ZONE_EVENT_INVALID, "nothing planned");
+
+ // zone_events_enqueue
+
+ // zone_events_freeze
+ // zone_events_start
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ int r;
+
+ evsched_t sched = { 0 };
+ worker_pool_t *pool = NULL;
+ zone_t zone = { 0 };
+
+ r = evsched_init(&sched, NULL);
+ ok(r == KNOT_EOK, "create scheduler");
+
+ pool = worker_pool_create(1);
+ ok(pool != NULL, "create worker pool");
+
+ r = zone_events_init(&zone);
+ ok(r == KNOT_EOK, "zone events init");
+
+ r = zone_events_setup(&zone, pool, &sched);
+ ok(r == KNOT_EOK, "zone events setup");
+
+ test_scheduling(&zone);
+
+ zone_events_deinit(&zone);
+ worker_pool_destroy(pool);
+ evsched_deinit(&sched);
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_serial.c b/tests/knot/test_zone_serial.c
new file mode 100644
index 0000000..2d50720
--- /dev/null
+++ b/tests/knot/test_zone_serial.c
@@ -0,0 +1,159 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "knot/zone/serial.h"
+#include "knot/conf/schema.h"
+#include "contrib/strtonum.h"
+
+enum serials {
+ S_LOWEST = 0, // lowest value
+ S_2LOWEST = 1, // second lowest value
+ S_BELOW_MIDDLE = 0x7fffffff, // one below middle
+ S_ABOVE_MIDDLE = 0x80000000, // one above middle
+ S_2HIGHEST = 0xffffffff - 1, // second highest value
+ S_HIGHEST = 0xffffffff // highest value
+};
+
+static uint32_t random_serial(void)
+{
+ uint32_t s = rand() & 0xff;
+ s |= (rand() & 0xff) << 8;
+ s |= (rand() & 0xff) << 16;
+ s |= (rand() & 0xff) << 24;
+
+ return s;
+}
+
+static void check_unixtime(uint32_t current, uint32_t increment, uint32_t expected, const char *msg)
+{
+ uint32_t next = serial_next(current, SERIAL_POLICY_UNIXTIME, increment);
+ ok(next == expected, "unixtime: %s", msg);
+}
+
+/* Test will wrongly fail if the next second starts while running;
+ * this is unlikely, so not taking any action */
+static void test_unixtime(void)
+{
+ time_t serial0 = time(NULL);
+
+ check_unixtime(1000000000, 1, serial0, "from old second or policy");
+ check_unixtime(serial0, 0, serial0, "reuse current second");
+ check_unixtime(serial0, 1, serial0 + 1, "this second's first increment");
+ check_unixtime(serial0 + 1, 1, serial0 + 2, "this second's second increment");
+ check_unixtime(3000000000, 1, 3000000001, "from future second");
+ check_unixtime(3000000000, 0, 3000000000, "at future second");
+}
+
+static void check_dateserial(uint32_t current, uint32_t increment, uint32_t expected, const char *msg)
+{
+ uint32_t next = serial_next(current, SERIAL_POLICY_DATESERIAL, increment);
+ ok(next == expected, "dateserial: %s", msg);
+}
+
+/* Test will wrongly fail if the next day starts while running;
+ * this is EXTREMELY unlikely, so definitely not taking any action */
+static void test_dateserial(void)
+{
+ time_t now = time(NULL);
+
+ struct tm *gm_ret = gmtime(&now);
+
+ char str[32];
+ int ret1 = strftime(str, sizeof(str), "%Y%m%d00", gm_ret);
+
+ uint32_t serial0 = 0;
+ int ret2 = str_to_u32(str, &serial0);
+
+ ok(gm_ret != NULL && ret1 > 0 && ret2 == KNOT_EOK,
+ "dateserial: prepare current value");
+
+ check_dateserial(2000010100, 1, serial0, "from old date or policy");
+ check_dateserial(serial0, 1, serial0 + 1, "today's first increment");
+ check_dateserial(serial0 + 98, 1, serial0 + 99, "today's last increment");
+ check_dateserial(serial0 + 99, 1, serial0 + 100, "wrap from today");
+ check_dateserial(2100010100, 1, 2100010101, "from future date");
+ check_dateserial(2100010100, 0, 2100010100, "at future date");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Serial compare test. */
+ ok(serial_compare(S_LOWEST, S_BELOW_MIDDLE) == SERIAL_LOWER,
+ "serial compare: lowest < below middle");
+ ok(serial_compare(S_BELOW_MIDDLE, S_LOWEST) == SERIAL_GREATER,
+ "serial compare: below middle > lowest");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ ok(serial_compare(S_LOWEST, S_ABOVE_MIDDLE) == SERIAL_INCOMPARABLE,
+ "serial compare: lowest < above_middle");
+ ok(serial_compare(S_ABOVE_MIDDLE, S_LOWEST) == SERIAL_INCOMPARABLE,
+ "serial compare: above_middle < lowest");
+
+ ok(serial_compare(S_LOWEST, S_HIGHEST) == SERIAL_GREATER,
+ "serial compare: lowest > highest");
+ ok(serial_compare(S_HIGHEST, S_LOWEST) == SERIAL_LOWER,
+ "serial compare: highest < lowest");
+
+ ok(serial_compare(S_2LOWEST, S_ABOVE_MIDDLE) == SERIAL_LOWER,
+ "serial compare: 2nd lowest < above middle");
+ ok(serial_compare(S_ABOVE_MIDDLE, S_2LOWEST) == SERIAL_GREATER,
+ "serial compare: above middle > 2nd lowest");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ ok(serial_compare(S_BELOW_MIDDLE, S_HIGHEST) == SERIAL_INCOMPARABLE,
+ "serial compare: below middle < highest");
+ ok(serial_compare(S_HIGHEST, S_BELOW_MIDDLE) == SERIAL_INCOMPARABLE,
+ "serial compare: highest < below middle");
+
+ ok(serial_compare(S_BELOW_MIDDLE, S_2HIGHEST) == SERIAL_LOWER,
+ "serial compare: below middle < 2nd highest");
+ ok(serial_compare(S_2HIGHEST, S_BELOW_MIDDLE) == SERIAL_GREATER,
+ "serial compare: 2nd highest > below middle");
+
+ ok(serial_compare(S_ABOVE_MIDDLE, S_HIGHEST) == SERIAL_LOWER,
+ "serial compare: above middle < highest");
+ ok(serial_compare(S_HIGHEST, S_ABOVE_MIDDLE) == SERIAL_GREATER,
+ "serial compare: highest > above middle");
+
+ ok(serial_compare(S_LOWEST, S_LOWEST) == SERIAL_EQUAL,
+ "serial compare: lowest == lowest");
+ ok(serial_compare(S_HIGHEST, S_HIGHEST) == SERIAL_EQUAL,
+ "serial compare: highest == highest");
+
+ ok(serial_compare(S_LOWEST - 1, S_HIGHEST) == SERIAL_EQUAL,
+ "serial compare: lowest - 1 == highest");
+ ok(serial_compare(S_LOWEST, S_HIGHEST + 1) == SERIAL_EQUAL,
+ "serial compare: lowest== highest + 1");
+
+ /* Corner-case: these serials' distance is exactly 2^31. */
+ uint32_t s1 = random_serial();
+ uint32_t s2 = s1 + S_ABOVE_MIDDLE; // exactly the 'opposite' number
+ ok(serial_compare(s1, s2) == SERIAL_INCOMPARABLE,
+ "serial compare: random opposites (s1 < s2)");
+ ok(serial_compare(s2, s1) == SERIAL_INCOMPARABLE,
+ "serial compare: random opposites (s2 < s1)");
+
+ test_dateserial();
+ test_unixtime();
+
+ return 0;
+}
diff --git a/tests/knot/test_zone_timers.c b/tests/knot/test_zone_timers.c
new file mode 100644
index 0000000..272733c
--- /dev/null
+++ b/tests/knot/test_zone_timers.c
@@ -0,0 +1,110 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "knot/zone/timers.h"
+#include "libknot/db/db_lmdb.h"
+#include "libknot/dname.h"
+#include "libknot/error.h"
+
+static const zone_timers_t MOCK_TIMERS = {
+ .next_refresh = 1474559960,
+ .last_notified_serial = 0,
+ .last_flush = 1,
+ .next_ds_check = 1474559961,
+ .next_ds_push = 1474559962,
+ .catalog_member = 1474559963,
+ .next_expire = 1639727731,
+};
+
+static bool timers_eq(const zone_timers_t *a, const zone_timers_t *b)
+{
+ return a->next_refresh == b->next_refresh &&
+ a->last_notified_serial == b->last_notified_serial &&
+ a->last_flush == b->last_flush &&
+ a->next_ds_check == b->next_ds_check &&
+ a->next_ds_push == b->next_ds_push &&
+ a->catalog_member == b->catalog_member &&
+ a->next_expire == b->next_expire;
+}
+
+static bool keep_all(const knot_dname_t *zone, void *data)
+{
+ return true;
+}
+
+static bool remove_all(const knot_dname_t *zone, void *data)
+{
+ return false;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+ assert(knot_db_lmdb_api());
+
+ char *dbid = test_mkdtemp();
+ if (!dbid) {
+ return EXIT_FAILURE;
+ }
+
+ const knot_dname_t *zone = (uint8_t *)"\x7""example""\x3""com";
+ struct zone_timers timers = MOCK_TIMERS;
+
+ // Create database
+ knot_lmdb_db_t _db = { 0 }, *db = &_db;
+ knot_lmdb_init(db, dbid, 1024 * 1024, 0, NULL);
+ int ret = knot_lmdb_open(db);
+ ok(ret == KNOT_EOK && db != NULL, "open timers");
+
+ // Lookup nonexistent
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_ENOENT, ret, "zone_timer_read() nonexistent");
+
+ // Write timers
+ ret = zone_timers_write(db, zone, &timers);
+ is_int(KNOT_EOK, ret, "zone_timers_write()");
+
+ // Read timers
+ memset(&timers, 0, sizeof(timers));
+ ret = zone_timers_read(db, zone, &timers);
+ ok(ret == KNOT_EOK, "zone_timers_read()");
+ ok(timers_eq(&timers, &MOCK_TIMERS), "inconsistent timers");
+
+ // Sweep none
+ ret = zone_timers_sweep(db, keep_all, NULL);
+ is_int(KNOT_EOK, ret, "zone_timers_sweep() none");
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_EOK, ret, "zone_timers_read()");
+
+ // Sweep all
+ ret = zone_timers_sweep(db, remove_all, NULL);
+ is_int(KNOT_EOK, ret, "zone_timers_sweep() all");
+ ret = zone_timers_read(db, zone, &timers);
+ is_int(KNOT_ENOENT, ret, "zone_timers_read() nonexistent");
+
+ // Clean up.
+ knot_lmdb_deinit(db);
+ test_rm_rf(dbid);
+ free(dbid);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/knot/test_zonedb.c b/tests/knot/test_zonedb.c
new file mode 100644
index 0000000..3ef7632
--- /dev/null
+++ b/tests/knot/test_zonedb.c
@@ -0,0 +1,115 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "knot/zone/zone.h"
+#include "knot/zone/zonedb.h"
+#include "contrib/openbsd/strlcat.h"
+#include "contrib/openbsd/strlcpy.h"
+
+#define ZONE_COUNT 10
+static const char *zone_list[ZONE_COUNT] = {
+ ".",
+ "com",
+ "net",
+ "c.com",
+ "a.com",
+ "a.net",
+ "b.net",
+ "c.a.com",
+ "b.b.b.com",
+ "b.b.b.b.net",
+};
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create database. */
+ knot_dname_txt_storage_t buf;
+ const char *prefix = "zzz.";
+ size_t nr_passed = 0;
+ knot_dname_t *dname = NULL;
+ zone_t *zones[ZONE_COUNT] = {0};
+ knot_zonedb_t *db = knot_zonedb_new();
+ ok(db != NULL, "zonedb: new");
+
+ /* Populate. */
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ knot_dname_t *zone_name = knot_dname_from_str_alloc(zone_list[i]);
+ zones[i] = zone_new(zone_name);
+ knot_dname_free(zone_name, NULL);
+
+ if (zones[i] == NULL) {
+ goto cleanup;
+ }
+ if (knot_zonedb_insert(db, zones[i]) == KNOT_EOK) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_add_zone(%s) failed", zone_list[i]);
+ }
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: add zones");
+
+ /* Lookup of exact names. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ dname = knot_dname_from_str_alloc(zone_list[i]);
+ if (knot_zonedb_find(db, dname) == zones[i]) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_find(%s) failed", zone_list[i]);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: find exact zones");
+
+ /* Lookup of sub-names. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ strlcpy(buf, prefix, sizeof(buf));
+ if (strcmp(zone_list[i], ".") != 0) {
+ strlcat(buf, zone_list[i], sizeof(buf));
+ }
+ dname = knot_dname_from_str_alloc(buf);
+ if (knot_zonedb_find_suffix(db, dname) == zones[i]) {
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_find_suffix(%s) failed", buf);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: find zones for subnames");
+
+ /* Remove all zones. */
+ nr_passed = 0;
+ for (unsigned i = 0; i < ZONE_COUNT; ++i) {
+ dname = knot_dname_from_str_alloc(zone_list[i]);
+ if (knot_zonedb_del(db, dname) == KNOT_EOK) {
+ zone_free(&zones[i]);
+ ++nr_passed;
+ } else {
+ diag("knot_zonedb_remove_zone(%s) failed", zone_list[i]);
+ }
+ knot_dname_free(dname, NULL);
+ }
+ ok(nr_passed == ZONE_COUNT, "zonedb: removed all zones");
+
+cleanup:
+ knot_zonedb_deep_free(&db, false);
+ return 0;
+}
diff --git a/tests/libdnssec/sample_keys.h b/tests/libdnssec/sample_keys.h
new file mode 100644
index 0000000..cd9f18f
--- /dev/null
+++ b/tests/libdnssec/sample_keys.h
@@ -0,0 +1,498 @@
+/* Copyright (C) 2021 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/>.
+ */
+
+#pragma once
+
+#include <binary.h>
+
+typedef struct key_parameters {
+ // DNSSEC fields
+ uint8_t *name;
+ uint16_t flags;
+ uint8_t protocol;
+ uint8_t algorithm;
+ dnssec_binary_t public_key;
+
+ // DNSSEC wire format
+ dnssec_binary_t rdata;
+
+ // Hashes
+ const char *key_id;
+ uint16_t keytag;
+ dnssec_binary_t ds_sha1;
+ dnssec_binary_t ds_sha256;
+ dnssec_binary_t ds_sha384;
+
+ // Key information
+ unsigned bit_size;
+
+ // Private key in PEM
+ dnssec_binary_t pem;
+} key_parameters_t;
+
+/*
+
+RSA-SHA-256
+
+rsa. IN DNSKEY 256 3 8 AwEAAaqwL+O6GcCPkRZjoObfIJHcHPwQQY9mnAg6kROea2gsyRJOAwBNQPCfXoPtmrU0BiZ0aGBVTVPAvZh+HJRu9NEfTNDPK2HSyHdSucjY1qs6WAub6oWHJuLBxMesftpnUwoLnVZyN+pOblUZUMsvxP3PlS+mA+E6NyQX0F/fcfGL
+rsa. IN DS 37335 8 1 2ABEFAAB07A900F8CB5B266FC930EEBEF51766F6
+rsa. IN DS 37335 8 2 30226484F230814C08C6DD9E2DF6E7A3DB860C2552A418CBF70D0FEE94DFA15F
+rsa. IN DS 37335 8 4 978E0F7766096E131E3E90C50B63DBD825E7428E864BC5A3D32F3135A3786F0CDC6A070B6B8D760190F0F572B03CA4C0
+
+Modulus: qrAv47oZwI+RFmOg5t8gkdwc/BBBj2acCDqRE55raCzJEk4DAE1A8J9eg+2atTQGJnRoYFVNU8C9mH4clG700R9M0M8rYdLId1K5yNjWqzpYC5vqhYcm4sHEx6x+2mdTCgudVnI36k5uVRlQyy/E/c+VL6YD4To3JBfQX99x8Ys=
+PublicExponent: AQAB
+PrivateExponent: NGDSoVBHfMbRoAw8oPxRk1D3eAZJCAdV1FSclmej0BkGLt7PnvUV+4D8UQHF2ts3E+/e48jpbM0VoUj53jbaWx1ULVmQ1cpJY0XLsRUmaQdOwEnSgXjtQy2htlth8RinB+LnVG8eUS9jWnEEikfvCLH0ptkOa/u6GKFUMj+Q95k=
+Prime1: 4ZZj/YD5xvjxEuE0uR0KedsZeGT6iHqwtmJuLNuhFaeXIw5vXXZmg88U/lIo2t0DESYTbfXglw0eu62MwWb+5w==
+Prime2: wbMU0wM6MYaDs4FfEeuTXT14P3cXZOFGikJPWiIUGoMGvDgYzxdiFoHzGdLkapsPizTqBKMtYQ9CYQa8g1cXvQ==
+Exponent1: ywKuZVqGbdtmB9mHuvc5kEPuffxRwjS3hsq538CfDH/PcYryCagdxYy8lcqWXa/7rJkZbyGQxh7Wg4tBWmM4DQ==
+Exponent2: L8MYv29sSgoBL6IW7zRHghZGMGANRLLH0g/HwVHl4yOr5X1voKEDbslcSGHYMPFLQ+goTDxwVB6PH52pnjk7gQ==
+Coefficient: USHiV/UQkTz3BlxZc1IAiUQv9/Ba8wtHWSVp+YqPhxt1sfdiyUMXtlA4f6WAKAGMraoRw4wIcYr+N6Wx+wwXZw==
+
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKqwL+O6GcCPkRZj
+oObfIJHcHPwQQY9mnAg6kROea2gsyRJOAwBNQPCfXoPtmrU0BiZ0aGBVTVPAvZh+
+HJRu9NEfTNDPK2HSyHdSucjY1qs6WAub6oWHJuLBxMesftpnUwoLnVZyN+pOblUZ
+UMsvxP3PlS+mA+E6NyQX0F/fcfGLAgMBAAECgYA0YNKhUEd8xtGgDDyg/FGTUPd4
+BkkIB1XUVJyWZ6PQGQYu3s+e9RX7gPxRAcXa2zcT797jyOlszRWhSPneNtpbHVQt
+WZDVykljRcuxFSZpB07ASdKBeO1DLaG2W2HxGKcH4udUbx5RL2NacQSKR+8IsfSm
+2Q5r+7oYoVQyP5D3mQJBAOGWY/2A+cb48RLhNLkdCnnbGXhk+oh6sLZibizboRWn
+lyMOb112ZoPPFP5SKNrdAxEmE2314JcNHrutjMFm/ucCQQDBsxTTAzoxhoOzgV8R
+65NdPXg/dxdk4UaKQk9aIhQagwa8OBjPF2IWgfMZ0uRqmw+LNOoEoy1hD0JhBryD
+Vxe9AkEAywKuZVqGbdtmB9mHuvc5kEPuffxRwjS3hsq538CfDH/PcYryCagdxYy8
+lcqWXa/7rJkZbyGQxh7Wg4tBWmM4DQJAL8MYv29sSgoBL6IW7zRHghZGMGANRLLH
+0g/HwVHl4yOr5X1voKEDbslcSGHYMPFLQ+goTDxwVB6PH52pnjk7gQJAUSHiV/UQ
+kTz3BlxZc1IAiUQv9/Ba8wtHWSVp+YqPhxt1sfdiyUMXtlA4f6WAKAGMraoRw4wI
+cYr+N6Wx+wwXZw==
+-----END PRIVATE KEY-----
+
+*/
+
+static const key_parameters_t SAMPLE_RSA_KEY = {
+ .name = (uint8_t *)"\x03""rsa",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 8,
+ .public_key = { .size = 132, .data = (uint8_t []) {
+ 0x03, 0x01, 0x00, 0x01, 0xaa, 0xb0, 0x2f, 0xe3, 0xba, 0x19,
+ 0xc0, 0x8f, 0x91, 0x16, 0x63, 0xa0, 0xe6, 0xdf, 0x20, 0x91,
+ 0xdc, 0x1c, 0xfc, 0x10, 0x41, 0x8f, 0x66, 0x9c, 0x08, 0x3a,
+ 0x91, 0x13, 0x9e, 0x6b, 0x68, 0x2c, 0xc9, 0x12, 0x4e, 0x03,
+ 0x00, 0x4d, 0x40, 0xf0, 0x9f, 0x5e, 0x83, 0xed, 0x9a, 0xb5,
+ 0x34, 0x06, 0x26, 0x74, 0x68, 0x60, 0x55, 0x4d, 0x53, 0xc0,
+ 0xbd, 0x98, 0x7e, 0x1c, 0x94, 0x6e, 0xf4, 0xd1, 0x1f, 0x4c,
+ 0xd0, 0xcf, 0x2b, 0x61, 0xd2, 0xc8, 0x77, 0x52, 0xb9, 0xc8,
+ 0xd8, 0xd6, 0xab, 0x3a, 0x58, 0x0b, 0x9b, 0xea, 0x85, 0x87,
+ 0x26, 0xe2, 0xc1, 0xc4, 0xc7, 0xac, 0x7e, 0xda, 0x67, 0x53,
+ 0x0a, 0x0b, 0x9d, 0x56, 0x72, 0x37, 0xea, 0x4e, 0x6e, 0x55,
+ 0x19, 0x50, 0xcb, 0x2f, 0xc4, 0xfd, 0xcf, 0x95, 0x2f, 0xa6,
+ 0x03, 0xe1, 0x3a, 0x37, 0x24, 0x17, 0xd0, 0x5f, 0xdf, 0x71,
+ 0xf1, 0x8b,
+ }},
+ .rdata = { .size = 136, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x08,
+ 0x03, 0x01, 0x00, 0x01, 0xaa, 0xb0, 0x2f, 0xe3, 0xba, 0x19,
+ 0xc0, 0x8f, 0x91, 0x16, 0x63, 0xa0, 0xe6, 0xdf, 0x20, 0x91,
+ 0xdc, 0x1c, 0xfc, 0x10, 0x41, 0x8f, 0x66, 0x9c, 0x08, 0x3a,
+ 0x91, 0x13, 0x9e, 0x6b, 0x68, 0x2c, 0xc9, 0x12, 0x4e, 0x03,
+ 0x00, 0x4d, 0x40, 0xf0, 0x9f, 0x5e, 0x83, 0xed, 0x9a, 0xb5,
+ 0x34, 0x06, 0x26, 0x74, 0x68, 0x60, 0x55, 0x4d, 0x53, 0xc0,
+ 0xbd, 0x98, 0x7e, 0x1c, 0x94, 0x6e, 0xf4, 0xd1, 0x1f, 0x4c,
+ 0xd0, 0xcf, 0x2b, 0x61, 0xd2, 0xc8, 0x77, 0x52, 0xb9, 0xc8,
+ 0xd8, 0xd6, 0xab, 0x3a, 0x58, 0x0b, 0x9b, 0xea, 0x85, 0x87,
+ 0x26, 0xe2, 0xc1, 0xc4, 0xc7, 0xac, 0x7e, 0xda, 0x67, 0x53,
+ 0x0a, 0x0b, 0x9d, 0x56, 0x72, 0x37, 0xea, 0x4e, 0x6e, 0x55,
+ 0x19, 0x50, 0xcb, 0x2f, 0xc4, 0xfd, 0xcf, 0x95, 0x2f, 0xa6,
+ 0x03, 0xe1, 0x3a, 0x37, 0x24, 0x17, 0xd0, 0x5f, 0xdf, 0x71,
+ 0xf1, 0x8b,
+ }},
+ .key_id = "76f0d6c093d8328bc7f0e25bd8cde5575bad9b44",
+ .keytag = 37335,
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x01,
+ 0x2a, 0xbe, 0xfa, 0xab, 0x07, 0xa9, 0x00, 0xf8, 0xcb, 0x5b,
+ 0x26, 0x6f, 0xc9, 0x30, 0xee, 0xbe, 0xf5, 0x17, 0x66, 0xf6,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x02,
+ 0x30, 0x22, 0x64, 0x84, 0xf2, 0x30, 0x81, 0x4c, 0x08, 0xc6,
+ 0xdd, 0x9e, 0x2d, 0xf6, 0xe7, 0xa3, 0xdb, 0x86, 0x0c, 0x25,
+ 0x52, 0xa4, 0x18, 0xcb, 0xf7, 0x0d, 0x0f, 0xee, 0x94, 0xdf,
+ 0xa1, 0x5f,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x91, 0xd7, 0x08, 0x04,
+ 0x97, 0x8e, 0x0f, 0x77, 0x66, 0x09, 0x6e, 0x13, 0x1e, 0x3e,
+ 0x90, 0xc5, 0x0b, 0x63, 0xdb, 0xd8, 0x25, 0xe7, 0x42, 0x8e,
+ 0x86, 0x4b, 0xc5, 0xa3, 0xd3, 0x2f, 0x31, 0x35, 0xa3, 0x78,
+ 0x6f, 0x0c, 0xdc, 0x6a, 0x07, 0x0b, 0x6b, 0x8d, 0x76, 0x01,
+ 0x90, 0xf0, 0xf5, 0x72, 0xb0, 0x3c, 0xa4, 0xc0,
+ }},
+ .bit_size = 1024,
+ .pem = { .size = 916, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e,
+ 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49,
+ 0x49, 0x43, 0x64, 0x67, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4e,
+ 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77,
+ 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43,
+ 0x41, 0x6d, 0x41, 0x77, 0x67, 0x67, 0x4a, 0x63, 0x41, 0x67,
+ 0x45, 0x41, 0x41, 0x6f, 0x47, 0x42, 0x41, 0x4b, 0x71, 0x77,
+ 0x4c, 0x2b, 0x4f, 0x36, 0x47, 0x63, 0x43, 0x50, 0x6b, 0x52,
+ 0x5a, 0x6a, 0x0a, 0x6f, 0x4f, 0x62, 0x66, 0x49, 0x4a, 0x48,
+ 0x63, 0x48, 0x50, 0x77, 0x51, 0x51, 0x59, 0x39, 0x6d, 0x6e,
+ 0x41, 0x67, 0x36, 0x6b, 0x52, 0x4f, 0x65, 0x61, 0x32, 0x67,
+ 0x73, 0x79, 0x52, 0x4a, 0x4f, 0x41, 0x77, 0x42, 0x4e, 0x51,
+ 0x50, 0x43, 0x66, 0x58, 0x6f, 0x50, 0x74, 0x6d, 0x72, 0x55,
+ 0x30, 0x42, 0x69, 0x5a, 0x30, 0x61, 0x47, 0x42, 0x56, 0x54,
+ 0x56, 0x50, 0x41, 0x76, 0x5a, 0x68, 0x2b, 0x0a, 0x48, 0x4a,
+ 0x52, 0x75, 0x39, 0x4e, 0x45, 0x66, 0x54, 0x4e, 0x44, 0x50,
+ 0x4b, 0x32, 0x48, 0x53, 0x79, 0x48, 0x64, 0x53, 0x75, 0x63,
+ 0x6a, 0x59, 0x31, 0x71, 0x73, 0x36, 0x57, 0x41, 0x75, 0x62,
+ 0x36, 0x6f, 0x57, 0x48, 0x4a, 0x75, 0x4c, 0x42, 0x78, 0x4d,
+ 0x65, 0x73, 0x66, 0x74, 0x70, 0x6e, 0x55, 0x77, 0x6f, 0x4c,
+ 0x6e, 0x56, 0x5a, 0x79, 0x4e, 0x2b, 0x70, 0x4f, 0x62, 0x6c,
+ 0x55, 0x5a, 0x0a, 0x55, 0x4d, 0x73, 0x76, 0x78, 0x50, 0x33,
+ 0x50, 0x6c, 0x53, 0x2b, 0x6d, 0x41, 0x2b, 0x45, 0x36, 0x4e,
+ 0x79, 0x51, 0x58, 0x30, 0x46, 0x2f, 0x66, 0x63, 0x66, 0x47,
+ 0x4c, 0x41, 0x67, 0x4d, 0x42, 0x41, 0x41, 0x45, 0x43, 0x67,
+ 0x59, 0x41, 0x30, 0x59, 0x4e, 0x4b, 0x68, 0x55, 0x45, 0x64,
+ 0x38, 0x78, 0x74, 0x47, 0x67, 0x44, 0x44, 0x79, 0x67, 0x2f,
+ 0x46, 0x47, 0x54, 0x55, 0x50, 0x64, 0x34, 0x0a, 0x42, 0x6b,
+ 0x6b, 0x49, 0x42, 0x31, 0x58, 0x55, 0x56, 0x4a, 0x79, 0x57,
+ 0x5a, 0x36, 0x50, 0x51, 0x47, 0x51, 0x59, 0x75, 0x33, 0x73,
+ 0x2b, 0x65, 0x39, 0x52, 0x58, 0x37, 0x67, 0x50, 0x78, 0x52,
+ 0x41, 0x63, 0x58, 0x61, 0x32, 0x7a, 0x63, 0x54, 0x37, 0x39,
+ 0x37, 0x6a, 0x79, 0x4f, 0x6c, 0x73, 0x7a, 0x52, 0x57, 0x68,
+ 0x53, 0x50, 0x6e, 0x65, 0x4e, 0x74, 0x70, 0x62, 0x48, 0x56,
+ 0x51, 0x74, 0x0a, 0x57, 0x5a, 0x44, 0x56, 0x79, 0x6b, 0x6c,
+ 0x6a, 0x52, 0x63, 0x75, 0x78, 0x46, 0x53, 0x5a, 0x70, 0x42,
+ 0x30, 0x37, 0x41, 0x53, 0x64, 0x4b, 0x42, 0x65, 0x4f, 0x31,
+ 0x44, 0x4c, 0x61, 0x47, 0x32, 0x57, 0x32, 0x48, 0x78, 0x47,
+ 0x4b, 0x63, 0x48, 0x34, 0x75, 0x64, 0x55, 0x62, 0x78, 0x35,
+ 0x52, 0x4c, 0x32, 0x4e, 0x61, 0x63, 0x51, 0x53, 0x4b, 0x52,
+ 0x2b, 0x38, 0x49, 0x73, 0x66, 0x53, 0x6d, 0x0a, 0x32, 0x51,
+ 0x35, 0x72, 0x2b, 0x37, 0x6f, 0x59, 0x6f, 0x56, 0x51, 0x79,
+ 0x50, 0x35, 0x44, 0x33, 0x6d, 0x51, 0x4a, 0x42, 0x41, 0x4f,
+ 0x47, 0x57, 0x59, 0x2f, 0x32, 0x41, 0x2b, 0x63, 0x62, 0x34,
+ 0x38, 0x52, 0x4c, 0x68, 0x4e, 0x4c, 0x6b, 0x64, 0x43, 0x6e,
+ 0x6e, 0x62, 0x47, 0x58, 0x68, 0x6b, 0x2b, 0x6f, 0x68, 0x36,
+ 0x73, 0x4c, 0x5a, 0x69, 0x62, 0x69, 0x7a, 0x62, 0x6f, 0x52,
+ 0x57, 0x6e, 0x0a, 0x6c, 0x79, 0x4d, 0x4f, 0x62, 0x31, 0x31,
+ 0x32, 0x5a, 0x6f, 0x50, 0x50, 0x46, 0x50, 0x35, 0x53, 0x4b,
+ 0x4e, 0x72, 0x64, 0x41, 0x78, 0x45, 0x6d, 0x45, 0x32, 0x33,
+ 0x31, 0x34, 0x4a, 0x63, 0x4e, 0x48, 0x72, 0x75, 0x74, 0x6a,
+ 0x4d, 0x46, 0x6d, 0x2f, 0x75, 0x63, 0x43, 0x51, 0x51, 0x44,
+ 0x42, 0x73, 0x78, 0x54, 0x54, 0x41, 0x7a, 0x6f, 0x78, 0x68,
+ 0x6f, 0x4f, 0x7a, 0x67, 0x56, 0x38, 0x52, 0x0a, 0x36, 0x35,
+ 0x4e, 0x64, 0x50, 0x58, 0x67, 0x2f, 0x64, 0x78, 0x64, 0x6b,
+ 0x34, 0x55, 0x61, 0x4b, 0x51, 0x6b, 0x39, 0x61, 0x49, 0x68,
+ 0x51, 0x61, 0x67, 0x77, 0x61, 0x38, 0x4f, 0x42, 0x6a, 0x50,
+ 0x46, 0x32, 0x49, 0x57, 0x67, 0x66, 0x4d, 0x5a, 0x30, 0x75,
+ 0x52, 0x71, 0x6d, 0x77, 0x2b, 0x4c, 0x4e, 0x4f, 0x6f, 0x45,
+ 0x6f, 0x79, 0x31, 0x68, 0x44, 0x30, 0x4a, 0x68, 0x42, 0x72,
+ 0x79, 0x44, 0x0a, 0x56, 0x78, 0x65, 0x39, 0x41, 0x6b, 0x45,
+ 0x41, 0x79, 0x77, 0x4b, 0x75, 0x5a, 0x56, 0x71, 0x47, 0x62,
+ 0x64, 0x74, 0x6d, 0x42, 0x39, 0x6d, 0x48, 0x75, 0x76, 0x63,
+ 0x35, 0x6b, 0x45, 0x50, 0x75, 0x66, 0x66, 0x78, 0x52, 0x77,
+ 0x6a, 0x53, 0x33, 0x68, 0x73, 0x71, 0x35, 0x33, 0x38, 0x43,
+ 0x66, 0x44, 0x48, 0x2f, 0x50, 0x63, 0x59, 0x72, 0x79, 0x43,
+ 0x61, 0x67, 0x64, 0x78, 0x59, 0x79, 0x38, 0x0a, 0x6c, 0x63,
+ 0x71, 0x57, 0x58, 0x61, 0x2f, 0x37, 0x72, 0x4a, 0x6b, 0x5a,
+ 0x62, 0x79, 0x47, 0x51, 0x78, 0x68, 0x37, 0x57, 0x67, 0x34,
+ 0x74, 0x42, 0x57, 0x6d, 0x4d, 0x34, 0x44, 0x51, 0x4a, 0x41,
+ 0x4c, 0x38, 0x4d, 0x59, 0x76, 0x32, 0x39, 0x73, 0x53, 0x67,
+ 0x6f, 0x42, 0x4c, 0x36, 0x49, 0x57, 0x37, 0x7a, 0x52, 0x48,
+ 0x67, 0x68, 0x5a, 0x47, 0x4d, 0x47, 0x41, 0x4e, 0x52, 0x4c,
+ 0x4c, 0x48, 0x0a, 0x30, 0x67, 0x2f, 0x48, 0x77, 0x56, 0x48,
+ 0x6c, 0x34, 0x79, 0x4f, 0x72, 0x35, 0x58, 0x31, 0x76, 0x6f,
+ 0x4b, 0x45, 0x44, 0x62, 0x73, 0x6c, 0x63, 0x53, 0x47, 0x48,
+ 0x59, 0x4d, 0x50, 0x46, 0x4c, 0x51, 0x2b, 0x67, 0x6f, 0x54,
+ 0x44, 0x78, 0x77, 0x56, 0x42, 0x36, 0x50, 0x48, 0x35, 0x32,
+ 0x70, 0x6e, 0x6a, 0x6b, 0x37, 0x67, 0x51, 0x4a, 0x41, 0x55,
+ 0x53, 0x48, 0x69, 0x56, 0x2f, 0x55, 0x51, 0x0a, 0x6b, 0x54,
+ 0x7a, 0x33, 0x42, 0x6c, 0x78, 0x5a, 0x63, 0x31, 0x49, 0x41,
+ 0x69, 0x55, 0x51, 0x76, 0x39, 0x2f, 0x42, 0x61, 0x38, 0x77,
+ 0x74, 0x48, 0x57, 0x53, 0x56, 0x70, 0x2b, 0x59, 0x71, 0x50,
+ 0x68, 0x78, 0x74, 0x31, 0x73, 0x66, 0x64, 0x69, 0x79, 0x55,
+ 0x4d, 0x58, 0x74, 0x6c, 0x41, 0x34, 0x66, 0x36, 0x57, 0x41,
+ 0x4b, 0x41, 0x47, 0x4d, 0x72, 0x61, 0x6f, 0x52, 0x77, 0x34,
+ 0x77, 0x49, 0x0a, 0x63, 0x59, 0x72, 0x2b, 0x4e, 0x36, 0x57,
+ 0x78, 0x2b, 0x77, 0x77, 0x58, 0x5a, 0x77, 0x3d, 0x3d, 0x0a,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59,
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a,
+ }},
+};
+
+/*
+
+ECDSA-P256-SHA256
+
+ecdsa. IN DNSKEY 256 3 13 8uD7C4THTM/w7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7g bvUP6SsIga5ZQ4CSAxNmYA/gZsuXzA==
+ecdsa. IN DS 5345 13 1 954103ac7c43810ce9f414e80f30ab1cbe49b236
+ecdsa. IN DS 5345 13 2 bac2107036e735b50f85006ce409a19a3438cab272e70769ebda032239a3d0ca
+ecdsa. IN DS 5345 13 4 a0ac6790483872be72a258314200a88ab75cdd70f66a18a09f0f414c074df0989fdb1df0e67d82d4312cda67b93a76c1
+
+PrivateKey: iyLIPdk3DOIxVmmSYlmTstbtUPiVlEyDX46psyCwNVQ=
+
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhAIsiyD3ZNwziMVZp
+kmJZk7LW7VD4lZRMg1+OqbMgsDVUoAoGCCqGSM49AwEHoUQDQgAE8uD7C4THTM/w
+7uhryRSToeE/jKT78/p853RX0L5EwrZrSLBubLPiBw7gbvUP6SsIga5ZQ4CSAxNm
+YA/gZsuXzA==
+-----END PRIVATE KEY-----
+
+*/
+
+static const key_parameters_t SAMPLE_ECDSA_KEY = {
+ .name = (uint8_t *)"\x05""ecdsa",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 13,
+ .public_key = { .size = 64, .data = (uint8_t []) {
+ 0xf2, 0xe0, 0xfb, 0x0b, 0x84, 0xc7, 0x4c, 0xcf, 0xf0, 0xee,
+ 0xe8, 0x6b, 0xc9, 0x14, 0x93, 0xa1, 0xe1, 0x3f, 0x8c, 0xa4,
+ 0xfb, 0xf3, 0xfa, 0x7c, 0xe7, 0x74, 0x57, 0xd0, 0xbe, 0x44,
+ 0xc2, 0xb6, 0x6b, 0x48, 0xb0, 0x6e, 0x6c, 0xb3, 0xe2, 0x07,
+ 0x0e, 0xe0, 0x6e, 0xf5, 0x0f, 0xe9, 0x2b, 0x08, 0x81, 0xae,
+ 0x59, 0x43, 0x80, 0x92, 0x03, 0x13, 0x66, 0x60, 0x0f, 0xe0,
+ 0x66, 0xcb, 0x97, 0xcc,
+ }},
+ .rdata = { .size = 68, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0d,
+ 0xf2, 0xe0, 0xfb, 0x0b, 0x84, 0xc7, 0x4c, 0xcf, 0xf0, 0xee,
+ 0xe8, 0x6b, 0xc9, 0x14, 0x93, 0xa1, 0xe1, 0x3f, 0x8c, 0xa4,
+ 0xfb, 0xf3, 0xfa, 0x7c, 0xe7, 0x74, 0x57, 0xd0, 0xbe, 0x44,
+ 0xc2, 0xb6, 0x6b, 0x48, 0xb0, 0x6e, 0x6c, 0xb3, 0xe2, 0x07,
+ 0x0e, 0xe0, 0x6e, 0xf5, 0x0f, 0xe9, 0x2b, 0x08, 0x81, 0xae,
+ 0x59, 0x43, 0x80, 0x92, 0x03, 0x13, 0x66, 0x60, 0x0f, 0xe0,
+ 0x66, 0xcb, 0x97, 0xcc,
+ }},
+ .keytag = 5345,
+ .key_id = "47fd10011e76cc6741af586041eae5519465fc8d",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x01,
+ 0x95, 0x41, 0x03, 0xac, 0x7c, 0x43, 0x81, 0x0c, 0xe9, 0xf4,
+ 0x14, 0xe8, 0x0f, 0x30, 0xab, 0x1c, 0xbe, 0x49, 0xb2, 0x36,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x02,
+ 0xba, 0xc2, 0x10, 0x70, 0x36, 0xe7, 0x35, 0xb5, 0x0f, 0x85,
+ 0x00, 0x6c, 0xe4, 0x09, 0xa1, 0x9a, 0x34, 0x38, 0xca, 0xb2,
+ 0x72, 0xe7, 0x07, 0x69, 0xeb, 0xda, 0x03, 0x22, 0x39, 0xa3,
+ 0xd0, 0xca,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x14, 0xe1, 0x0d, 0x04,
+ 0xa0, 0xac, 0x67, 0x90, 0x48, 0x38, 0x72, 0xbe, 0x72, 0xa2,
+ 0x58, 0x31, 0x42, 0x00, 0xa8, 0x8a, 0xb7, 0x5c, 0xdd, 0x70,
+ 0xf6, 0x6a, 0x18, 0xa0, 0x9f, 0x0f, 0x41, 0x4c, 0x07, 0x4d,
+ 0xf0, 0x98, 0x9f, 0xdb, 0x1d, 0xf0, 0xe6, 0x7d, 0x82, 0xd4,
+ 0x31, 0x2c, 0xda, 0x67, 0xb9, 0x3a, 0x76, 0xc1,
+ }},
+ .bit_size = 256,
+ .pem = { .size = 262, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e,
+ 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b,
+ 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x49,
+ 0x47, 0x55, 0x41, 0x67, 0x45, 0x41, 0x4d, 0x42, 0x4d, 0x47,
+ 0x42, 0x79, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39, 0x41, 0x67,
+ 0x45, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53, 0x4d, 0x34, 0x39,
+ 0x41, 0x77, 0x45, 0x48, 0x42, 0x48, 0x6f, 0x77, 0x65, 0x41,
+ 0x49, 0x42, 0x41, 0x51, 0x51, 0x68, 0x41, 0x49, 0x73, 0x69,
+ 0x79, 0x44, 0x33, 0x5a, 0x4e, 0x77, 0x7a, 0x69, 0x4d, 0x56,
+ 0x5a, 0x70, 0x0a, 0x6b, 0x6d, 0x4a, 0x5a, 0x6b, 0x37, 0x4c,
+ 0x57, 0x37, 0x56, 0x44, 0x34, 0x6c, 0x5a, 0x52, 0x4d, 0x67,
+ 0x31, 0x2b, 0x4f, 0x71, 0x62, 0x4d, 0x67, 0x73, 0x44, 0x56,
+ 0x55, 0x6f, 0x41, 0x6f, 0x47, 0x43, 0x43, 0x71, 0x47, 0x53,
+ 0x4d, 0x34, 0x39, 0x41, 0x77, 0x45, 0x48, 0x6f, 0x55, 0x51,
+ 0x44, 0x51, 0x67, 0x41, 0x45, 0x38, 0x75, 0x44, 0x37, 0x43,
+ 0x34, 0x54, 0x48, 0x54, 0x4d, 0x2f, 0x77, 0x0a, 0x37, 0x75,
+ 0x68, 0x72, 0x79, 0x52, 0x53, 0x54, 0x6f, 0x65, 0x45, 0x2f,
+ 0x6a, 0x4b, 0x54, 0x37, 0x38, 0x2f, 0x70, 0x38, 0x35, 0x33,
+ 0x52, 0x58, 0x30, 0x4c, 0x35, 0x45, 0x77, 0x72, 0x5a, 0x72,
+ 0x53, 0x4c, 0x42, 0x75, 0x62, 0x4c, 0x50, 0x69, 0x42, 0x77,
+ 0x37, 0x67, 0x62, 0x76, 0x55, 0x50, 0x36, 0x53, 0x73, 0x49,
+ 0x67, 0x61, 0x35, 0x5a, 0x51, 0x34, 0x43, 0x53, 0x41, 0x78,
+ 0x4e, 0x6d, 0x0a, 0x59, 0x41, 0x2f, 0x67, 0x5a, 0x73, 0x75,
+ 0x58, 0x7a, 0x41, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
+ 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x0a,
+ }},
+};
+
+/*
+ * Private-key-format: v1.2
+ * Algorithm: 15 (ED25519)
+ * PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI=
+ *
+ * example.com. 3600 IN DNSKEY 256 3 15 (
+ * l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= )
+ *
+ * example.com. 3600 IN DS 3612 15 2 (
+ * 3aa5ab37efce57f737fc1627013fee07bdf241bd10f3b1964ab55c78e79
+ * a304b )
+ *
+ * example.com. 3600 IN MX 10 mail.example.com.
+ *
+ * example.com. 3600 IN RRSIG MX 15 2 3600 (
+ * 1440021600 1438207200 3613 example.com. (
+ * oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jPGrHpjQeRAvTdszaPD+QLs3f
+ * x8A4M3e23mRZ9VrbpMngwcrqNAg== )
+ */
+
+static const key_parameters_t SAMPLE_ED25519_KEY = {
+ .name = (uint8_t *)"\x07""ed25519",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 15,
+ .public_key = { .size = 32, .data = (uint8_t []) {
+ 0x97, 0x4d, 0x96, 0xa2, 0x2d, 0x22, 0x4b, 0xc0, 0x1a, 0xdb, 0x91, 0x50,
+ 0x91, 0x47, 0x7d, 0x44, 0xcc, 0xd9, 0x1c, 0x9a, 0x41, 0xa1, 0x14, 0x30,
+ 0x01, 0x01, 0x17, 0xd5, 0x2c, 0x59, 0x24, 0x0e,
+ }},
+ .rdata = { .size = 36, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0f,
+ 0x97, 0x4d, 0x96, 0xa2, 0x2d, 0x22, 0x4b, 0xc0, 0x1a, 0xdb, 0x91, 0x50,
+ 0x91, 0x47, 0x7d, 0x44, 0xcc, 0xd9, 0x1c, 0x9a, 0x41, 0xa1, 0x14, 0x30,
+ 0x01, 0x01, 0x17, 0xd5, 0x2c, 0x59, 0x24, 0x0e,
+ }},
+ .keytag = 3612,
+ .key_id = "bea75b99fb22ee1a68106ad6399e4acc43eb9929",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x01,
+ 0x50, 0x12, 0x49, 0x72, 0x1e, 0x1f, 0x09, 0xa7, 0x9d, 0x30, 0xd5, 0xc6,
+ 0xc4, 0xdc, 0xa1, 0xdc, 0x1d, 0xa4, 0xed, 0x5d,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x02,
+ 0x1b, 0x1c, 0x87, 0x66, 0xb2, 0xa9, 0x65, 0x66, 0xff, 0x19, 0x6f, 0x77,
+ 0xc0, 0xc4, 0x19, 0x4a, 0xf8, 0x6a, 0xaa, 0x10, 0x9c, 0x53, 0x46, 0xff,
+ 0x60, 0x23, 0x1a, 0x27, 0xd2, 0xb0, 0x7a, 0xc0,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x0e, 0x1c, 0x0f, 0x04,
+ 0xd1, 0x18, 0x31, 0x15, 0x3a, 0xf4, 0x98, 0x5e, 0xfb, 0xd0, 0xae, 0x79,
+ 0x2c, 0x96, 0x7e, 0xb4, 0xaf, 0xf3, 0xc3, 0x54, 0x88, 0xdb, 0x95, 0xf7,
+ 0xe2, 0xf8, 0x5d, 0xce, 0xc7, 0x4a, 0xe8, 0xf5, 0x9f, 0x9a, 0x72, 0x64,
+ 0x17, 0x98, 0xc9, 0x1c, 0x67, 0xc6, 0x75, 0xdb, 0x1d, 0x71, 0x0c, 0x18,
+ }},
+ .bit_size = 256,
+ .pem = { .size = 119, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x43, 0x34, 0x43, 0x41, 0x51, 0x41, 0x77,
+ 0x42, 0x51, 0x59, 0x44, 0x4b, 0x32, 0x56, 0x77, 0x42, 0x43, 0x49, 0x45,
+ 0x49, 0x44, 0x67, 0x79, 0x4d, 0x6a, 0x59, 0x77, 0x4d, 0x7a, 0x67, 0x30,
+ 0x4e, 0x6a, 0x49, 0x34, 0x4d, 0x44, 0x67, 0x77, 0x4d, 0x54, 0x49, 0x79,
+ 0x4e, 0x6a, 0x51, 0x31, 0x4d, 0x54, 0x6b, 0x77, 0x4d, 0x6a, 0x41, 0x30,
+ 0x4d, 0x54, 0x51, 0x79, 0x4d, 0x6a, 0x59, 0x79, 0x0a, 0x2d, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54,
+ 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a
+ }},
+};
+
+/*
+ * Private-key-format: v1.2
+ * Algorithm: 16 (ED448)
+ * PrivateKey: MEcCAQAwBQYDK2VxBDsEOVsk9cLaP+mD8n97xRET5bCqCUDZpPCIkCGrXLJG
+ * JgXCGlz4mOxH4qva7fIhEXm/62vtdbd55WRXIA==
+ *
+ * example.com. 3600 IN DNSKEY 256 3 16 (
+ * 3y0sCUZKI+DSyZQTTbgHOFppOzYz01iZQQVUWw9gCb1bLWNqqC/5qq2PL9T
+ * c6YaK2vHthBtaY0iA )
+ *
+ * example.com. 3600 IN DS 28205 16 2 (
+ * 7a27236ccb5193f696cdb4c5fd58af3500634bd836e6defacaac5dd4d76
+ * 6dcbe )
+ *
+ * example.com. 3600 IN MX 10 mail.example.com.
+ *
+ * example.com. 3600 IN RRSIG MX 16 2 3600 20211108134038 (
+ * 20211105115718 28205 example.com.
+ * uzvY2twOxxSas25N1HNQPRmhVgIaOHrEm6TccvwU
+ * WeJkecp2Nd0om5+Em1/91cfJWc/ZCFSYBcIAUFVB
+ * bMKoK0SXbmY5dM6rl0wQRk+Sl8VLyNttLMn5tpFk
+ * c74nWRGfXebz/HALT5WTdOidgIR8HCcA )
+ */
+
+static const key_parameters_t SAMPLE_ED448_KEY = {
+ .name = (uint8_t *)"\x07""example""\x03""com",
+ .flags = 256,
+ .protocol = 3,
+ .algorithm = 16,
+ .public_key = { .size = 57, .data = (uint8_t []) {
+ 0xdf, 0x2d, 0x2c, 0x09, 0x46, 0x4a, 0x23, 0xe0, 0xd2, 0xc9, 0x94, 0x13,
+ 0x4d, 0xb8, 0x07, 0x38, 0x5a, 0x69, 0x3b, 0x36, 0x33, 0xd3, 0x58, 0x99,
+ 0x41, 0x05, 0x54, 0x5b, 0x0f, 0x60, 0x09, 0xbd, 0x5b, 0x2d, 0x63, 0x6a,
+ 0xa8, 0x2f, 0xf9, 0xaa, 0xad, 0x8f, 0x2f, 0xd4, 0xdc, 0xe9, 0x86, 0x8a,
+ 0xda, 0xf1, 0xed, 0x84, 0x1b, 0x5a, 0x63, 0x48, 0x80,
+ }},
+ .rdata = { .size = 61, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x10,
+ 0xdf, 0x2d, 0x2c, 0x09, 0x46, 0x4a, 0x23, 0xe0, 0xd2, 0xc9, 0x94, 0x13,
+ 0x4d, 0xb8, 0x07, 0x38, 0x5a, 0x69, 0x3b, 0x36, 0x33, 0xd3, 0x58, 0x99,
+ 0x41, 0x05, 0x54, 0x5b, 0x0f, 0x60, 0x09, 0xbd, 0x5b, 0x2d, 0x63, 0x6a,
+ 0xa8, 0x2f, 0xf9, 0xaa, 0xad, 0x8f, 0x2f, 0xd4, 0xdc, 0xe9, 0x86, 0x8a,
+ 0xda, 0xf1, 0xed, 0x84, 0x1b, 0x5a, 0x63, 0x48, 0x80,
+ }},
+ .keytag = 28205,
+ .key_id = "501a69b2d8ad46c721ffabaa9eaf8e7fa49c1454",
+ .ds_sha1 = { .size = 24, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x01,
+ 0x69, 0xac, 0x45, 0x1c, 0xfa, 0xbb, 0xbb, 0x16, 0x5e, 0xf3, 0x82, 0x08,
+ 0x1f, 0xd3, 0x7e, 0x7a, 0xb4, 0xd9, 0x13, 0xbf,
+ }},
+ .ds_sha256 = { .size = 36, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x02,
+ 0x7a, 0x27, 0x23, 0x6c, 0xcb, 0x51, 0x93, 0xf6, 0x96, 0xcd, 0xb4, 0xc5,
+ 0xfd, 0x58, 0xaf, 0x35, 0x00, 0x63, 0x4b, 0xd8, 0x36, 0xe6, 0xde, 0xfa,
+ 0xca, 0xac, 0x5d, 0xd4, 0xd7, 0x66, 0xdc, 0xbe,
+ }},
+ .ds_sha384 = { .size = 52, .data = (uint8_t []) {
+ 0x6e, 0x2d, 0x10, 0x04,
+ 0x6e, 0x06, 0x6f, 0xfb, 0xd5, 0xa2, 0x6c, 0xf3, 0x04, 0xa1, 0x2b, 0x76,
+ 0xf1, 0x83, 0xdb, 0xd3, 0x8b, 0x5e, 0x7c, 0xcb, 0x19, 0x79, 0xff, 0x3f,
+ 0x46, 0xee, 0xf2, 0x61, 0xf7, 0xa0, 0x48, 0x96, 0xbd, 0xe6, 0x6a, 0xe8,
+ 0xd0, 0x30, 0x54, 0xc8, 0x3a, 0xa1, 0x2d, 0xb3, 0x77, 0x37, 0xf1, 0xbb,
+ }},
+ .bit_size = 456,
+ .pem = { .size = 156, .data = (uint8_t []) {
+ 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50,
+ 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x0a, 0x4d, 0x45, 0x63, 0x43, 0x41, 0x51, 0x41, 0x77,
+ 0x42, 0x51, 0x59, 0x44, 0x4b, 0x32, 0x56, 0x78, 0x42, 0x44, 0x73, 0x45,
+ 0x4f, 0x56, 0x73, 0x6b, 0x39, 0x63, 0x4c, 0x61, 0x50, 0x2b, 0x6d, 0x44,
+ 0x38, 0x6e, 0x39, 0x37, 0x78, 0x52, 0x45, 0x54, 0x35, 0x62, 0x43, 0x71,
+ 0x43, 0x55, 0x44, 0x5a, 0x70, 0x50, 0x43, 0x49, 0x6b, 0x43, 0x47, 0x72,
+ 0x58, 0x4c, 0x4a, 0x47, 0x4a, 0x67, 0x58, 0x43, 0x0a, 0x47, 0x6c, 0x7a,
+ 0x34, 0x6d, 0x4f, 0x78, 0x48, 0x34, 0x71, 0x76, 0x61, 0x37, 0x66, 0x49,
+ 0x68, 0x45, 0x58, 0x6d, 0x2f, 0x36, 0x32, 0x76, 0x74, 0x64, 0x62, 0x64,
+ 0x35, 0x35, 0x57, 0x52, 0x58, 0x49, 0x41, 0x3d, 0x3d, 0x0a, 0x2d, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
+ 0x54, 0x45, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a
+ }},
+ /* raw private key outside of PEM:
+ *
+ * 0x5b, 0x24, 0xf5, 0xc2, 0xda, 0x3f, 0xe9, 0x83, 0xf2, 0x7f, 0x7b, 0xc5,
+ * 0x11, 0x13, 0xe5, 0xb0, 0xaa, 0x09, 0x40, 0xd9, 0xa4, 0xf0, 0x88, 0x90,
+ * 0x21, 0xab, 0x5c, 0xb2, 0x46, 0x26, 0x05, 0xc2, 0x1a, 0x5c, 0xf8, 0x98,
+ * 0xec, 0x47, 0xe2, 0xab, 0xda, 0xed, 0xf2, 0x21, 0x11, 0x79, 0xbf, 0xeb,
+ * 0x6b, 0xed, 0x75, 0xb7, 0x79, 0xe5, 0x64, 0x57, 0x20,
+ *
+ */
+};
diff --git a/tests/libdnssec/test_binary.c b/tests/libdnssec/test_binary.c
new file mode 100644
index 0000000..4c78556
--- /dev/null
+++ b/tests/libdnssec/test_binary.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2018 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 <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "binary.h"
+#include "error.h"
+
+typedef struct test_string {
+ const char *encoded;
+ size_t encoded_size;
+ const char *decoded;
+ size_t decoded_size;
+} test_string_t;
+
+static void test_base64(void)
+{
+ test_string_t data[] = {
+ { "", 0, "", 0 },
+ { "YQ==", 4, "a", 1 },
+ { "YWI=", 4, "ab", 2 },
+ { "YWJj", 4, "abc", 3 },
+ { "YWJjZA==", 8, "abcd", 4 },
+ { "YWJjZGU=", 8, "abcde", 5 },
+ { "YWJjZGVm", 8, "abcdef", 6 },
+ { NULL }
+ };
+
+ for (int i = 0; data[i].encoded != NULL; i++) {
+ test_string_t *ts = &data[i];
+
+ const dnssec_binary_t base64 = {
+ .size = ts->encoded_size,
+ .data = (uint8_t *) ts->encoded
+ };
+
+ dnssec_binary_t binary = { 0 };
+
+ int r = dnssec_binary_from_base64(&base64, &binary);
+ ok(r == DNSSEC_EOK &&
+ binary.size == ts->decoded_size &&
+ (binary.size == 0 || memcmp(binary.data, ts->decoded, binary.size) == 0),
+ "dnssec_binary_from_base64() for '%s'", ts->encoded);
+
+ dnssec_binary_t encoded = { 0 };
+ r = dnssec_binary_to_base64(&binary, &encoded);
+ ok(r == DNSSEC_EOK &&
+ encoded.size == ts->encoded_size &&
+ memcmp(encoded.data, ts->encoded, encoded.size) == 0,
+ "dnssec_binary_to_base64() for '%s'", ts->encoded);
+
+ dnssec_binary_free(&binary);
+ dnssec_binary_free(&encoded);
+ }
+}
+
+static void test_dup(void)
+{
+ dnssec_binary_t src = { .size = 5, .data = (uint8_t *) "ahoj" };
+ dnssec_binary_t dst = { 0 };
+
+ int r = dnssec_binary_dup(&src, &dst);
+ ok(r == DNSSEC_EOK &&
+ src.size == dst.size && memcmp(src.data, dst.data, src.size) == 0,
+ "dnssec_binary_dup()");
+
+ dnssec_binary_free(&dst);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_base64();
+ test_dup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_crypto.c b/tests/libdnssec/test_crypto.c
new file mode 100644
index 0000000..eb2601d
--- /dev/null
+++ b/tests/libdnssec/test_crypto.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+
+#include "crypto.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ // not much we can test
+
+ dnssec_crypto_init();
+ ok(1, "dnssec_crypto_init() didn't crash");
+
+ dnssec_crypto_reinit();
+ ok(1, "dnssec_crypto_reinit() didn't crash");
+
+ dnssec_crypto_cleanup();
+ ok(1, "dnssec_crypto_cleanup() didn't crash");
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key.c b/tests/libdnssec/test_key.c
new file mode 100644
index 0000000..cd0aaee
--- /dev/null
+++ b/tests/libdnssec/test_key.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+#include <string.h>
+
+#include "binary.h"
+#include "crypto.h"
+#include "error.h"
+#include "key.h"
+
+#include "sample_keys.h"
+
+#define check_attr_scalar(key, type, name, def_val, set_val) { \
+ type value = dnssec_key_get_##name(key); \
+ ok(value == def_val, #name " default"); \
+ r = dnssec_key_set_##name(key, set_val); \
+ ok(r == DNSSEC_EOK, #name " set"); \
+ value = dnssec_key_get_##name(key); \
+ ok(value == set_val, #name " get"); \
+}
+
+static void check_key_tag(dnssec_key_t *key, const key_parameters_t *params)
+{
+ uint16_t keytag = dnssec_key_get_keytag(key);
+ ok(keytag == params->keytag, "get keytag");
+}
+
+static void check_key_size(dnssec_key_t *key, const key_parameters_t *params)
+{
+ ok(dnssec_key_get_size(key) == params->bit_size,
+ "key size %u bits", params->bit_size);
+}
+
+static void check_usage(dnssec_key_t *key, bool ok_verify, bool ok_sign)
+{
+ ok(dnssec_key_can_verify(key) == ok_verify,
+ "%s verify", ok_verify ? "can" : "cannot");
+ ok(dnssec_key_can_sign(key) == ok_sign,
+ "%s sign", ok_sign ? "can" : "cannot");
+}
+
+static void test_public_key(const key_parameters_t *params)
+{
+ dnssec_key_t *key = NULL;
+ int r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+
+ // create from parameters
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_INVALID_KEY_ALGORITHM,
+ "set public key (fails, no algorithm set)");
+
+ check_attr_scalar(key, uint16_t, flags, 256, params->flags);
+ check_attr_scalar(key, uint8_t, protocol, 3, params->protocol);
+ check_attr_scalar(key, uint8_t, algorithm, 0, params->algorithm);
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_EOK, "set public key (succeeds)");
+
+ r = dnssec_key_set_pubkey(key, &params->public_key);
+ ok(r == DNSSEC_KEY_ALREADY_PRESENT,
+ "set public key (fails, already present)");
+
+ dnssec_binary_t rdata = { 0 };
+ r = dnssec_key_get_rdata(key, &rdata);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&rdata, &params->rdata) == 0,
+ "get RDATA");
+
+ check_key_tag(key, params);
+
+ // create from RDATA
+
+ dnssec_key_clear(key);
+ r = dnssec_key_set_rdata(key, &params->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ check_key_tag(key, params);
+ check_key_size(key, params);
+ check_usage(key, true, false);
+
+ // create copy
+
+ dnssec_key_t *copy = dnssec_key_dup(key);
+ ok(copy != NULL, "duplicate key");
+
+ check_key_tag(copy, params);
+ check_key_size(copy, params);
+ check_usage(copy, true, false);
+
+ dnssec_key_free(copy);
+ dnssec_key_free(key);
+}
+
+static void test_private_key(const key_parameters_t *params)
+{
+ dnssec_key_t *key = NULL;
+ int r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+
+ // import to public
+
+ r = dnssec_key_set_rdata(key, &params->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ r = dnssec_key_load_pkcs8(key, &params->pem);
+ ok(r == DNSSEC_EOK, "load private key (1)");
+
+ ok(dnssec_key_can_verify(key), "can verify");
+ ok(dnssec_key_can_sign(key), "can sign");
+
+ // purely from parameters
+
+ dnssec_key_clear(key);
+
+ dnssec_key_set_algorithm(key, params->algorithm);
+ dnssec_key_set_flags(key, params->flags);
+ r = dnssec_key_load_pkcs8(key, &params->pem);
+ ok(r == DNSSEC_EOK, "load private key (2)");
+
+ dnssec_binary_t rdata = { 0 };
+ r = dnssec_key_get_rdata(key, &rdata);
+ ok(r == DNSSEC_EOK && dnssec_binary_cmp(&rdata, &params->rdata) == 0,
+ "get RDATA");
+
+ check_key_tag(key, params);
+ check_key_size(key, params);
+ check_usage(key, true, true);
+
+ // create copy
+
+ dnssec_key_t *copy = dnssec_key_dup(key);
+ ok(copy != NULL, "duplicate key");
+
+ check_key_tag(copy, params);
+ check_key_size(copy, params);
+ check_usage(copy, true, false);
+
+ dnssec_key_free(copy);
+ dnssec_key_free(key);
+}
+
+static void test_naming(void)
+{
+ dnssec_key_t *key = NULL;
+ dnssec_key_new(&key);
+
+ const uint8_t *input = (uint8_t *)"\x07""eXample""\x03""COM";
+ const uint8_t *expected = (uint8_t *)"\x07""example""\x03""com";
+ size_t expected_size = 13;
+
+ ok(dnssec_key_get_dname(key) == NULL, "implicit key name");
+
+ dnssec_key_set_dname(key, input);
+ const uint8_t *output = dnssec_key_get_dname(key);
+
+ ok(strlen((char *)output) + 1 == 13 &&
+ memcmp(output, expected, expected_size) == 0,
+ "set key name");
+
+ dnssec_key_set_dname(key, NULL);
+ ok(dnssec_key_get_dname(key) == NULL, "clear key name");
+
+ dnssec_key_free(key);
+}
+
+typedef struct keyinfo {
+ const char *name;
+ const key_parameters_t *parameters;
+} keyinfo_t;
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ static const keyinfo_t keys[] = {
+ { "RSA", &SAMPLE_RSA_KEY },
+ { "ECDSA", &SAMPLE_ECDSA_KEY },
+#ifdef HAVE_ED25519
+ { "ED25519", &SAMPLE_ED25519_KEY },
+#endif
+#ifdef HAVE_ED448
+ { "ED448", &SAMPLE_ED448_KEY },
+#endif
+ { NULL }
+ };
+
+ for (const keyinfo_t *k = keys; k->name != NULL; k += 1) {
+ diag("%s key", k->name);
+ test_public_key(k->parameters);
+ test_private_key(k->parameters);
+ }
+
+ test_naming();
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key_algorithm.c b/tests/libdnssec/test_key_algorithm.c
new file mode 100644
index 0000000..6c62106
--- /dev/null
+++ b/tests/libdnssec/test_key_algorithm.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+
+#include "error.h"
+#include "key.h"
+
+static void ok_range(dnssec_key_algorithm_t algo,
+ unsigned exp_min, unsigned exp_max,
+ const char *name)
+{
+ unsigned min = 0, max = 0;
+ int r = dnssec_algorithm_key_size_range(algo, &min, &max);
+ ok(r == DNSSEC_EOK && min == exp_min && max == exp_max,
+ "dnssec_algorithm_key_size_range() for %s", name);
+}
+
+static void null_range(void)
+{
+ dnssec_key_algorithm_t algo = DNSSEC_KEY_ALGORITHM_RSA_SHA256;
+ unsigned val = 0;
+ int r;
+
+ r = dnssec_algorithm_key_size_range(algo, NULL, NULL);
+ ok(r == DNSSEC_EINVAL, "dnssec_algorithm_key_size_range() all null");
+ r = dnssec_algorithm_key_size_range(algo, &val, NULL);
+ ok(r == DNSSEC_EOK && val == 1024, "dnssec_algorithm_key_size_range() min only");
+ r = dnssec_algorithm_key_size_range(algo, NULL, &val);
+ ok(r == DNSSEC_EOK && val == 4096, "dnssec_algorithm_key_size_range() max only");
+}
+
+static void check_borders(void)
+{
+ dnssec_key_algorithm_t rsa = DNSSEC_KEY_ALGORITHM_RSA_SHA1;
+
+ ok(dnssec_algorithm_key_size_check(rsa, 1023) == false, "rsa 1023");
+ ok(dnssec_algorithm_key_size_check(rsa, 1024) == true, "rsa 1024");
+ ok(dnssec_algorithm_key_size_check(rsa, 1025) == true, "rsa 1025");
+ ok(dnssec_algorithm_key_size_check(rsa, 4095) == true, "rsa 4095");
+ ok(dnssec_algorithm_key_size_check(rsa, 4096) == true, "rsa 4096");
+ ok(dnssec_algorithm_key_size_check(rsa, 4097) == false, "rsa 4097");
+}
+
+static void check_defaults(void)
+{
+ is_int(2048, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3), "rsa default");
+ is_int(256, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256), "ecc default");
+#ifdef HAVE_ED25519
+ is_int(256, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ED25519), "ed25519 default");
+#endif
+#ifdef HAVE_ED448
+ is_int(456, dnssec_algorithm_key_size_default(DNSSEC_KEY_ALGORITHM_ED448), "ed448 default");
+#endif
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ // ranges
+ ok_range(DNSSEC_KEY_ALGORITHM_RSA_SHA512, 1024, 4096, "RSA/SHA256");
+ ok_range(DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, 384, 384, "ECDSA/SHA384");
+#ifdef HAVE_ED25519
+ ok_range(DNSSEC_KEY_ALGORITHM_ED25519, 256, 256, "ED25519");
+#endif
+#ifdef HAVE_ED448
+ ok_range(DNSSEC_KEY_ALGORITHM_ED448, 456, 456, "ED448");
+#endif
+ null_range();
+
+ check_borders();
+
+ check_defaults();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_key_ds.c b/tests/libdnssec/test_key_ds.c
new file mode 100644
index 0000000..fbc6327
--- /dev/null
+++ b/tests/libdnssec/test_key_ds.c
@@ -0,0 +1,122 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+
+#include <stddef.h>
+#include <string.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/error.h"
+#include "libdnssec/key.h"
+#include "sample_keys.h"
+
+static void test_key(const char *name, const struct key_parameters *params)
+{
+ dnssec_key_t *key = NULL;
+
+ dnssec_key_new(&key);
+ dnssec_key_set_dname(key, params->name);
+ dnssec_key_set_rdata(key, &params->rdata);
+
+ struct ds_type {
+ const char *name;
+ dnssec_key_digest_t digest;
+ size_t params_offset;
+ };
+
+ static const struct ds_type DS_TYPES[] = {
+ { "SHA-1", DNSSEC_KEY_DIGEST_SHA1, offsetof(typeof(*params), ds_sha1) },
+ { "SHA-256", DNSSEC_KEY_DIGEST_SHA256, offsetof(typeof(*params), ds_sha256) },
+ { "SHA-384", DNSSEC_KEY_DIGEST_SHA384, offsetof(typeof(*params), ds_sha384) },
+ { NULL }
+ };
+
+ for (const struct ds_type *dt = DS_TYPES; dt->name != NULL; dt++) {
+ dnssec_binary_t ds = { 0 };
+ int r = dnssec_key_create_ds(key, dt->digest, &ds);
+
+ const dnssec_binary_t *expect = (void *)params + dt->params_offset;
+
+ ok(r == DNSSEC_EOK &&
+ ds.size == expect->size &&
+ memcmp(ds.data, expect->data, ds.size) == 0,
+ "dnssec_key_create_ds() for %s/%s", name, dt->name);
+
+ dnssec_binary_free(&ds);
+ }
+
+ dnssec_key_free(key);
+}
+
+static void test_errors(const struct key_parameters *params)
+{
+ dnssec_key_t *key = NULL;
+ dnssec_binary_t ds = { 0 };
+
+ int r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_EINVAL, r, "dnssec_key_create_ds() no key");
+ dnssec_binary_free(&ds);
+
+ dnssec_key_new(&key);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_INVALID_KEY_NAME, r, "dnssec_key_create_ds() no key name");
+
+ dnssec_key_set_dname(key, params->name);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_INVALID_PUBLIC_KEY, r, "dnssec_key_create_ds() no public key");
+
+ dnssec_key_set_rdata(key, &params->rdata);
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, NULL);
+ is_int(DNSSEC_EINVAL, r, "dnssec_key_create_ds() no RDATA buffer");
+
+ r = dnssec_key_create_ds(key, 3, &ds);
+ is_int(DNSSEC_INVALID_DS_ALGORITHM, r, "dnssec_key_create_ds() unsupported algorithm");
+
+ r = dnssec_key_create_ds(key, DNSSEC_KEY_DIGEST_SHA1, &ds);
+ is_int(DNSSEC_EOK, r, "dnssec_key_create_ds() valid parameters");
+
+ dnssec_binary_free(&ds);
+ dnssec_key_free(key);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ test_key("RSA", &SAMPLE_RSA_KEY);
+ test_key("ECDSA", &SAMPLE_ECDSA_KEY);
+#ifdef HAVE_ED25519
+ test_key("ED25519", &SAMPLE_ED25519_KEY);
+#endif
+#ifdef HAVE_ED448
+ test_key("ED448", &SAMPLE_ED448_KEY);
+#endif
+
+ test_errors(&SAMPLE_ECDSA_KEY);
+#ifdef HAVE_ED25519
+ test_errors(&SAMPLE_ED25519_KEY);
+#endif
+#ifdef HAVE_ED448
+ test_errors(&SAMPLE_ED448_KEY);
+#endif
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keyid.c b/tests/libdnssec/test_keyid.c
new file mode 100644
index 0000000..43a1303
--- /dev/null
+++ b/tests/libdnssec/test_keyid.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "keyid.h"
+
+static void test_keyid_is_valid_run(const char *param, bool should_ok)
+{
+ ok(dnssec_keyid_is_valid(param) == should_ok,
+ "dnssec_keyid_is_valid(\"%s\")", param);
+}
+
+static void test_keyid_is_valid(void)
+{
+ test_keyid_is_valid_run(NULL, false);
+ test_keyid_is_valid_run("a1b1", true);
+ test_keyid_is_valid_run("3e90c5cb1fad5f8512da2028fda3808e749d3bf", false);
+ test_keyid_is_valid_run("9aa6dAAC706fb6fe4aceb327452a7b5FEA457544", true);
+ test_keyid_is_valid_run("eac45c184b7f476472c16d5b0c4f0c52389848001", false);
+ test_keyid_is_valid_run("9aa6daac706fb6fe4aceb32g452a7b5fea457544", false);
+}
+
+static void test_keyid_normalize(void)
+{
+ char id[] = "3711927404f64CE7df88253d763e442CE39f9B5c";
+ const char *id_norm = "3711927404f64ce7df88253d763e442ce39f9b5c";
+
+ dnssec_keyid_normalize(id);
+ ok(strcmp(id, id_norm) == 0, "dnssec_keyid_normalize()");
+}
+
+static void test_keyid_copy(void)
+{
+ const char *id = "21669f1eca6418f9aBBBf0007e6f73463d467424";
+ const char *expected = "21669f1eca6418f9abbbf0007e6f73463d467424";
+
+ char *copy = dnssec_keyid_copy(id);
+ ok(copy && strcmp(copy, expected) == 0, "dnssec_keyid_copy()");
+
+ free(copy);
+}
+
+static void test_keyid_equal(void)
+{
+ const char *id = "dd63237d4a07867de715499690c9ad12990519f0";
+ const char *id_case = "dd63237d4a07867de715499690C9AD12990519F0";
+ const char *id_diff = "dd63237d4a07867de715499690c9ad12990519f1";
+
+ ok(dnssec_keyid_equal(id, NULL) == false, "dnssec_keyid_equal(id, NULL)");
+ ok(dnssec_keyid_equal(id, id) == true, "dnssec_keyid_equal(id, id)");
+ ok(dnssec_keyid_equal(id, id_case) == true, "dnssec_keyid_equal(id, ID)");
+ ok(dnssec_keyid_equal(id, id_diff) == false, "dnssec_keyid_equal(ida, idb)");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_keyid_is_valid();
+ test_keyid_normalize();
+ test_keyid_copy();
+ test_keyid_equal();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keystore_pkcs11.c b/tests/libdnssec/test_keystore_pkcs11.c
new file mode 100644
index 0000000..9828fce
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs11.c
@@ -0,0 +1,421 @@
+/* Copyright (C) 2022 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 <tap/basic.h>
+#include <tap/files.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/error.h"
+#include "libdnssec/keystore.h"
+#include "libdnssec/sign.h"
+
+#include "sample_keys.h"
+
+#define ENV_SOFTHSM_DSO "KNOT_SOFTHSM2_DSO"
+#define ENV_SOFTHSM_UTIL "KNOT_SOFTHSM2_UTIL"
+
+#define SOFTHSM_DSO "libsofthsm2.so"
+#define SOFTHSM_CONF "softhsm2.conf"
+#define SOFTHSM_CONF_ENV "SOFTHSM2_CONF"
+#define SOFTHSM_UTIL "softhsm2-util"
+
+#define TOKEN_LABEL "libdnssec-test"
+#define TOKEN_PIN "1234"
+#define TOKEN_SOPIN "123456"
+
+#define EXIT_EXEC_FAILED 127
+
+#ifndef LIBDIR
+# include <bits/wordsize.h>
+# if __WORDSIZE == 32
+# define LIBDIR "/usr/lib32"
+# elif __WORDSIZE == 64
+# define LIBDIR "/usr/lib64"
+# endif
+#endif
+
+#define MSG_SOFTWARE "soft -"
+#define MSG_PKCS11 "p11 -"
+
+/*!
+ * Get SoftHSM DSO path.
+ */
+static char *libsofthsm_dso(void)
+{
+ // prefer environment variable
+
+ const char *env = getenv(ENV_SOFTHSM_DSO);
+ if (env) {
+ return (env[0] != '\0' ? strdup(env) : NULL);
+ }
+
+ // autodetection
+
+ static const char *paths[] = {
+ LIBDIR "/pkcs11/" SOFTHSM_DSO,
+ LIBDIR "/softhsm/" SOFTHSM_DSO,
+ LIBDIR "/" SOFTHSM_DSO,
+ NULL
+ };
+
+ for (const char **path_ptr = paths; *path_ptr; path_ptr += 1) {
+ const char *path = *path_ptr;
+ if (access(path, R_OK|X_OK) == 0) {
+ return strdup(path);
+ }
+ }
+
+ return NULL;
+}
+
+/*!
+ * Get SoftHSM utility path.
+ */
+static char *libsofthsm_util(void)
+{
+ // prefer environment variable
+
+ const char *env = getenv(ENV_SOFTHSM_UTIL);
+ if (env && env[0] != '\0') {
+ return strdup(env);
+ }
+
+ // fallback, will rely on PATH
+
+ return strdup(SOFTHSM_UTIL);
+}
+
+/*!
+ * Path to temporary token data.
+ */
+static char *token_path = NULL;
+
+/*!
+ * Cleanup token test data.
+ */
+static void token_cleanup(void)
+{
+ test_rm_rf(token_path);
+ free(token_path);
+}
+
+/*!
+ * Initialize token using the support tool.
+ */
+static bool token_init_exec(const char *util)
+{
+ pid_t child = fork();
+ if (child == -1) {
+ return false;
+ }
+
+ // child
+
+ if (child == 0) {
+ fclose(stdin);
+ fclose(stdout);
+ fclose(stderr);
+
+ const char *basename = strrchr(util, '/');
+ if (basename) {
+ basename += 1;
+ } else {
+ basename = util;
+ }
+
+ execlp(util, basename,
+ "--init-token", "--slot=0", "--label=" TOKEN_LABEL,
+ "--pin=" TOKEN_PIN, "--so-pin=" TOKEN_SOPIN,
+ NULL);
+
+ exit(EXIT_EXEC_FAILED);
+ }
+
+ // parent
+
+ int status = 0;
+ if (waitpid(child, &status, 0) == -1) {
+ return false;
+ }
+
+ int exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+ if (exit_code != 0) {
+ diag("%s exit status %d", util, exit_code);
+ if (exit_code == EXIT_EXEC_FAILED) {
+ diag("set %s environment variable to adjust the path",
+ ENV_SOFTHSM_UTIL);
+ }
+ }
+
+ return exit_code == 0;
+}
+
+/*!
+ * Initialize environment and token for testing.
+ */
+static bool token_init(void)
+{
+ token_path = test_mkdtemp();
+ if (!token_path) {
+ return false;
+ }
+
+ // generate configuration file for unit test
+
+ char config[4096] = { 0 };
+ int r = snprintf(config, sizeof(config), "%s/%s", token_path, SOFTHSM_CONF);
+ if (r <= 0 || r >= sizeof(config)) {
+ return false;
+ }
+
+ FILE *file = fopen(config, "w");
+ if (!file) {
+ return false;
+ }
+
+ fprintf(file, "directories.tokendir = %s\n", token_path);
+ fprintf(file, "objectstore.backend = file\n");
+ fprintf(file, "log.level = INFO\n");
+ fprintf(file, "slots.removable = false\n");
+
+ fclose(file);
+
+ // update environment to use the config
+
+ if (setenv(SOFTHSM_CONF_ENV, config, 1) != 0) {
+ return false;
+ }
+
+ // initialize token
+
+ char *util = libsofthsm_util();
+ if (!util) {
+ return false;
+ }
+
+ bool inited = token_init_exec(util);
+ free(util);
+
+ return inited;
+}
+
+static void create_dnskeys(dnssec_keystore_t *keystore,
+ dnssec_key_algorithm_t algorithm, const char *id,
+ dnssec_key_t **p11_key_ptr, dnssec_key_t **soft_key_ptr)
+{
+ int r;
+
+ // construct PKCS #11 privkey-pubkey key pair
+
+ dnssec_key_t *p11_key = NULL;
+ r = dnssec_key_new(&p11_key);
+ ok(r == DNSSEC_EOK && p11_key != NULL, MSG_PKCS11 " dnssec_key_new()");
+
+ r = dnssec_key_set_algorithm(p11_key, algorithm);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_key_set_algorithm()");
+
+ r = dnssec_keystore_get_private(keystore, id, p11_key);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_keystore_get_private()");
+
+ // construct software public key
+
+ dnssec_key_t *soft_key = NULL;
+ r = dnssec_key_new(&soft_key);
+ ok(r == DNSSEC_EOK && soft_key != NULL, MSG_SOFTWARE " dnssec_key_new()");
+
+ dnssec_binary_t rdata = { 0 };
+ dnssec_key_get_rdata(p11_key, &rdata);
+ r = dnssec_key_set_rdata(soft_key, &rdata);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_key_set_rdata()");
+
+ *p11_key_ptr = p11_key;
+ *soft_key_ptr = soft_key;
+}
+
+static void test_sign(dnssec_key_t *p11_key, dnssec_key_t *soft_key)
+{
+ int r;
+
+ static const dnssec_binary_t input = {
+ .data = (uint8_t *)"So Long, and Thanks for All the Fish.",
+ .size = 37
+ };
+
+ dnssec_binary_t sign = { 0 };
+
+ // usage constraints
+
+ ok(dnssec_key_can_sign(p11_key), MSG_PKCS11 " dnssec_key_can_sign()");
+ ok(dnssec_key_can_verify(p11_key), MSG_PKCS11 " dnssec_key_can_verify()");
+
+ ok(!dnssec_key_can_sign(soft_key), MSG_SOFTWARE " dnssec_key_can_sign()");
+ ok(dnssec_key_can_verify(soft_key), MSG_SOFTWARE " dnssec_key_can_verify()");
+
+ // PKCS #11 key signature
+
+ dnssec_sign_ctx_t *ctx = NULL;
+ r = dnssec_sign_new(&ctx, p11_key);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_init() ");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_add()");
+
+ r = dnssec_sign_write(ctx, DNSSEC_SIGN_NORMAL, &sign);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_write()");
+
+ // PKCS #11 key verification
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_init()");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_add()");
+
+ r = dnssec_sign_verify(ctx, false, &sign);
+ ok(r == DNSSEC_EOK, MSG_PKCS11 " dnssec_sign_verify()");
+
+ // software verification
+
+ dnssec_sign_free(ctx);
+ ctx = NULL;
+
+ r = dnssec_sign_new(&ctx, soft_key);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_init()");
+
+ r = dnssec_sign_add(ctx, &input);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_add()");
+
+ r = dnssec_sign_verify(ctx, false, &sign);
+ ok(r == DNSSEC_EOK, MSG_SOFTWARE " dnssec_sign_verify()");
+
+ dnssec_binary_free(&sign);
+ dnssec_sign_free(ctx);
+}
+
+static void test_key_use(dnssec_keystore_t *store,
+ dnssec_key_algorithm_t algorithm,
+ const char *keyid)
+{
+ dnssec_key_t *p11_key = NULL;
+ dnssec_key_t *soft_key = NULL;
+
+ create_dnskeys(store, algorithm, keyid, &p11_key, &soft_key);
+ test_sign(p11_key, soft_key);
+
+ dnssec_key_free(p11_key);
+ dnssec_key_free(soft_key);
+}
+
+static void test_algorithm(dnssec_keystore_t *store,
+ const key_parameters_t *params)
+{
+ char *id_generate = NULL;
+ char *id_import = NULL;
+
+ int r;
+
+ diag("algorithm %d, generated key", params->algorithm);
+
+ r = dnssec_keystore_generate(store, params->algorithm, params->bit_size,
+ NULL, &id_generate);
+ ok(r == DNSSEC_EOK && id_generate != NULL, "dnssec_keystore_generate()");
+ test_key_use(store, params->algorithm, id_generate);
+
+ diag("algorithm %d, imported key", params->algorithm);
+
+ r = dnssec_keystore_import(store, &params->pem, &id_import);
+ ok(r == DNSSEC_EOK && id_import != NULL, "dnssec_keystore_import()");
+ test_key_use(store, params->algorithm, id_import);
+
+ free(id_generate);
+ free(id_import);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ // PKCS #11 initialization
+
+ dnssec_keystore_t *store = NULL;
+ int r = dnssec_keystore_init_pkcs11(&store);
+ if (r == DNSSEC_NOT_IMPLEMENTED_ERROR) {
+ skip_all("not supported");
+ goto done;
+ }
+ ok(r == DNSSEC_EOK && store, "dnssec_keystore_init_pkcs11()");
+
+ char *dso_name = libsofthsm_dso();
+ if (!dso_name) {
+ skip_all("%s not found, set %s environment variable",
+ SOFTHSM_DSO, ENV_SOFTHSM_DSO);
+ goto done;
+ }
+ ok(dso_name != NULL, "find token DSO");
+
+ bool success = token_init();
+ if (!success) {
+ skip_all("failed to configure and initialize the token");
+ goto done;
+ }
+ ok(success, "initialize the token");
+
+ char config[4096] = { 0 };
+ r = snprintf(config, sizeof(config), "pkcs11:token=%s;pin-value=%s %s",
+ TOKEN_LABEL, TOKEN_PIN, dso_name);
+ free(dso_name);
+ ok(r > 0 && r < sizeof(config), "build configuration");
+
+ // key store access
+
+ r = dnssec_keystore_init(store, config);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init()");
+
+ r = dnssec_keystore_open(store, config);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_open()");
+
+ // key manipulation
+
+ static const int KEYS_COUNT = 2;
+ static const key_parameters_t *KEYS[] = {
+ &SAMPLE_RSA_KEY,
+ &SAMPLE_ECDSA_KEY,
+ };
+ assert(KEYS_COUNT == sizeof(KEYS) / sizeof(*KEYS));
+
+ for (int i = 0; i < KEYS_COUNT; i++) {
+ test_algorithm(store, KEYS[i]);
+ }
+
+ r = dnssec_keystore_close(store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_close()");
+done:
+ dnssec_keystore_deinit(store);
+ dnssec_crypto_cleanup();
+ token_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keystore_pkcs8.c b/tests/libdnssec/test_keystore_pkcs8.c
new file mode 100644
index 0000000..26acbd4
--- /dev/null
+++ b/tests/libdnssec/test_keystore_pkcs8.c
@@ -0,0 +1,98 @@
+/* Copyright (C) 2022 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 <tap/basic.h>
+#include <tap/files.h>
+
+#include "error.h"
+#include "key.h"
+#include "keystore.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ char *dir = test_tmpdir();
+ if (!dir) {
+ return 1;
+ }
+
+ // create context
+
+ dnssec_keystore_t *store = NULL;
+ int r = dnssec_keystore_init_pkcs8(&store);
+ ok(r == DNSSEC_EOK, "dnssec_keystore_init_pkcs8()");
+
+ r = dnssec_keystore_init(store, dir);
+ ok(r == DNSSEC_EOK, "init");
+
+ r = dnssec_keystore_open(store, dir);
+ ok(r == DNSSEC_EOK, "open");
+
+ // writing new content
+
+ char *id_A = NULL;
+ r = dnssec_keystore_generate(store, DNSSEC_KEY_ALGORITHM_RSA_SHA256,
+ 1024, NULL, &id_A);
+ ok(r == DNSSEC_EOK, "generate A");
+
+ char *id_B = NULL;
+ r = dnssec_keystore_generate(store, DNSSEC_KEY_ALGORITHM_RSA_SHA256,
+ 1024, NULL, &id_B);
+ ok(r == DNSSEC_EOK, "generate B");
+
+ // reading existing content
+
+ dnssec_key_t *key = NULL;
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_A, key);
+ ok(r == DNSSEC_EOK, "read A");
+ dnssec_key_free(key);
+
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_B, key);
+ ok(r == DNSSEC_EOK, "read B");
+ dnssec_key_free(key);
+
+ // content removal
+
+ r = dnssec_keystore_remove(store, id_A);
+ ok(r == DNSSEC_EOK, "remove A");
+
+ dnssec_key_new(&key);
+ dnssec_key_set_algorithm(key, DNSSEC_KEY_ALGORITHM_RSA_SHA256);
+ r = dnssec_keystore_get_private(store, id_A, key);
+ ok(r == DNSSEC_ENOENT, "read removed");
+ dnssec_key_free(key);
+
+ // cleanup
+
+ free(id_A);
+ free(id_B);
+
+ r = dnssec_keystore_close(store);
+ ok(r == DNSSEC_EOK, "close");
+
+ r = dnssec_keystore_deinit(store);
+ ok(r == DNSSEC_EOK, "deinit");
+
+ test_rm_rf(dir);
+ free(dir);
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_keytag.c b/tests/libdnssec/test_keytag.c
new file mode 100644
index 0000000..ac5f57a
--- /dev/null
+++ b/tests/libdnssec/test_keytag.c
@@ -0,0 +1,61 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+#include <stdint.h>
+
+#include "binary.h"
+#include "error.h"
+#include "keytag.h"
+
+int main(void)
+{
+ plan_lazy();
+
+ const dnssec_binary_t rsa_md5_rdata = { .size = 72, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x01,
+ 0x03, 0x01, 0x00, 0x01, 0xa6, 0x83, 0x41, 0x42, 0x58, 0x1b,
+ 0xc2, 0xa7, 0xc7, 0xd5, 0xef, 0xb5, 0x6c, 0xec, 0x34, 0xc5,
+ 0xc9, 0x5e, 0x84, 0xa8, 0x35, 0x4a, 0xe0, 0xc2, 0x70, 0xf5,
+ 0x40, 0xe9, 0x92, 0x06, 0x70, 0x45, 0x88, 0xd3, 0x86, 0xcf,
+ 0xf5, 0xff, 0x83, 0x08, 0x06, 0x98, 0xe7, 0x8a, 0xa9, 0x2c,
+ 0xe7, 0xe1, 0x4d, 0xa6, 0x46, 0xef, 0x3a, 0x96, 0x93, 0x8c,
+ 0xc1, 0x02, 0x00, 0x6f, 0x31, 0x9f, 0xa2, 0x1d
+ }};
+
+ uint16_t tag = 0;
+ ok(dnssec_keytag(&rsa_md5_rdata, &tag) == DNSSEC_EOK && tag == 40866,
+ "keytag for RSA/MD5");
+
+ const dnssec_binary_t ecdsa_rdata = { .size = 100, .data = (uint8_t []) {
+ 0x01, 0x00, 0x03, 0x0e,
+ 0xbe, 0x8f, 0x42, 0x92, 0x34, 0xa0, 0x06, 0x5f, 0x18, 0xa1,
+ 0x15, 0x01, 0x84, 0x50, 0x33, 0x1f, 0x44, 0xa2, 0xbb, 0x61,
+ 0x2a, 0xc8, 0x86, 0x9c, 0xf3, 0x4b, 0x2e, 0xf9, 0x63, 0xd1,
+ 0x81, 0x72, 0x56, 0x96, 0xc7, 0x67, 0x34, 0xa1, 0x55, 0xc2,
+ 0xf3, 0x1d, 0x03, 0xbe, 0x1b, 0x39, 0xeb, 0xa7, 0xb8, 0x2c,
+ 0x72, 0x2e, 0x58, 0x75, 0x56, 0x42, 0x0b, 0x6f, 0x21, 0xa2,
+ 0x33, 0xf4, 0x21, 0x00, 0xb7, 0x0f, 0x5a, 0xf7, 0x1a, 0xf0,
+ 0xe9, 0x94, 0xfa, 0x43, 0xb0, 0x4a, 0x48, 0xb8, 0x64, 0x89,
+ 0x7b, 0xc9, 0xe0, 0xf7, 0x97, 0x52, 0xf4, 0x85, 0x0f, 0xb4,
+ 0xf4, 0xfc, 0xe2, 0x10, 0xd4, 0x26
+ }};
+
+ ok(dnssec_keytag(&ecdsa_rdata, &tag) == DNSSEC_EOK && tag == 61768,
+ "keytag for ECDSA/SHA384");
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_nsec_bitmap.c b/tests/libdnssec/test_nsec_bitmap.c
new file mode 100644
index 0000000..f013131
--- /dev/null
+++ b/tests/libdnssec/test_nsec_bitmap.c
@@ -0,0 +1,102 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "nsec.h"
+#include "libknot/descriptor.h"
+
+#define TEST_BITMAP_SIZE 18
+
+int main(void)
+{
+ plan_lazy();
+
+ // Which rrtypes will be contained in the bitmap.
+ int test_contains_count = 8;
+ enum knot_rr_type test_contains[] = {
+ KNOT_RRTYPE_A,
+ KNOT_RRTYPE_NS,
+ KNOT_RRTYPE_SOA,
+ KNOT_RRTYPE_RRSIG,
+ KNOT_RRTYPE_NSEC,
+ KNOT_RRTYPE_DNSKEY,
+ KNOT_RRTYPE_SPF,
+ KNOT_RRTYPE_CAA
+ };
+
+ // Which rrtypes will not be contained in the bitmap.
+ int test_not_contains_count = 4;
+ enum knot_rr_type test_not_contains[] = {
+ KNOT_RRTYPE_AAAA,
+ KNOT_RRTYPE_MX,
+ KNOT_RRTYPE_AXFR,
+ KNOT_RRTYPE_CNAME
+ };
+
+ // Allocate new bitmap.
+ dnssec_nsec_bitmap_t *bitmap = dnssec_nsec_bitmap_new();
+ ok(bitmap != NULL, "allocate bitmap");
+ if (!bitmap) {
+ return 1;
+ }
+
+ // Add the desired RR types to bitmap.
+ for (int i = 0; i < test_contains_count; i++) {
+ dnssec_nsec_bitmap_add(bitmap, test_contains[i]);
+ }
+
+ size_t size = dnssec_nsec_bitmap_size(bitmap);
+ ok(size == TEST_BITMAP_SIZE, "valid bitmap size");
+ if (size != TEST_BITMAP_SIZE) {
+ dnssec_nsec_bitmap_free(bitmap);
+ return 1;
+ }
+
+ const uint8_t expected[TEST_BITMAP_SIZE] = {
+ 0x00, 0x0D, 0x62, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x01, 0x01, 0x40
+ };
+ uint8_t encoded[TEST_BITMAP_SIZE] = { 0 };
+ dnssec_nsec_bitmap_write(bitmap, encoded);
+
+ ok(memcmp(encoded, expected, TEST_BITMAP_SIZE) == 0, "valid bitmap");
+
+ // Test contained types.
+ char rrtype_str[50];
+ for (int i = 0; i < test_contains_count; i++) {
+ bool contains = dnssec_nsec_bitmap_contains(encoded, size, test_contains[i]);
+ (void)knot_rrtype_to_string(test_contains[i], rrtype_str, 50);
+ ok(contains, "bitmap contains %s", rrtype_str);
+ }
+
+ // Test not contained types.
+ for (int i = 0; i < test_not_contains_count; i++) {
+ bool contains = dnssec_nsec_bitmap_contains(encoded, size, test_not_contains[i]);
+ (void)knot_rrtype_to_string(test_not_contains[i], rrtype_str, 50);
+ ok(!contains, "bitmap does not contain %s", rrtype_str);
+ }
+
+ dnssec_nsec_bitmap_clear(bitmap);
+ ok(dnssec_nsec_bitmap_size(bitmap) == 0, "bitmap clear");
+
+ dnssec_nsec_bitmap_free(bitmap);
+ return 0;
+}
diff --git a/tests/libdnssec/test_nsec_hash.c b/tests/libdnssec/test_nsec_hash.c
new file mode 100644
index 0000000..595a314
--- /dev/null
+++ b/tests/libdnssec/test_nsec_hash.c
@@ -0,0 +1,114 @@
+/* Copyright (C) 2018 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 <string.h>
+#include <tap/basic.h>
+
+#include "crypto.h"
+#include "error.h"
+#include "nsec.h"
+
+static const dnssec_binary_t RDATA = { .size = 9, .data = (uint8_t []) {
+ 0x01, // algorithm
+ 0x00, // flags
+ 0x00, 0x0a, // iterations
+ 0x04, // salt length
+ 'a', 'b', 'c', 'd' // salt
+}};
+
+static void test_length(void)
+{
+ ok(dnssec_nsec3_hash_length(DNSSEC_NSEC3_ALGORITHM_SHA1) == 20,
+ "dnssec_nsec3_hash_length() for SHA1");
+}
+
+static void test_parsing(void)
+{
+
+ dnssec_nsec3_params_t params = { 0 };
+ int result = dnssec_nsec3_params_from_rdata(&params, &RDATA);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_params_from_rdata()");
+
+ ok(params.algorithm == 1, "algorithm");
+ ok(params.flags == 0, "flags");
+ ok(params.iterations == 10, "iterations");
+ ok(params.salt.size == 4, "salt length");
+ ok(params.salt.data != NULL && memcmp(params.salt.data, "abcd", 4) == 0,
+ "salt content");
+
+ dnssec_nsec3_params_free(&params);
+ ok(params.salt.data == NULL, "dnssec_nsec3_params_free()");
+}
+
+static void test_hashing(void)
+{
+ const dnssec_binary_t dname = {
+ .size = 13,
+ .data = (uint8_t *) "\x08""knot-dns""\x02""cz"
+ };
+
+ const dnssec_nsec3_params_t params = {
+ .algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1,
+ .flags = 0,
+ .iterations = 7,
+ .salt = { .size = 14, .data = (uint8_t *) "happywithnsec3" }
+ };
+
+ const dnssec_binary_t expected = { .size = 20, .data = (uint8_t []) {
+ 0x72, 0x40, 0x55, 0x83, 0x92, 0x93, 0x95, 0x28, 0xee, 0xa2,
+ 0xcc, 0xe1, 0x13, 0xbe, 0xcd, 0x41, 0xee, 0x8a, 0x71, 0xfd
+ }};
+
+ dnssec_binary_t hash = { 0 };
+
+ int result = dnssec_nsec3_hash(&dname, &params, &hash);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_hash()");
+
+ ok(hash.size == expected.size && hash.data != NULL &&
+ memcmp(hash.data, expected.data, expected.size) == 0,
+ "valid hash");
+
+ dnssec_binary_free(&hash);
+}
+
+static void test_clear(void)
+{
+ const dnssec_nsec3_params_t empty = { 0 };
+ dnssec_nsec3_params_t params = { 0 };
+
+ int result = dnssec_nsec3_params_from_rdata(&params, &RDATA);
+ ok(result == DNSSEC_EOK, "dnssec_nsec3_params_from_rdata()");
+
+ ok(memcmp(&params, &empty, sizeof(dnssec_nsec3_params_t)) != 0,
+ "non-empty after dnssec_nsec3_params_from_rdata()");
+
+ dnssec_nsec3_params_free(&params);
+
+ ok(memcmp(&params, &empty, sizeof(dnssec_nsec3_params_t)) == 0,
+ "cleared after dnssec_nsec3_params_free()");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_length();
+ test_parsing();
+ test_hashing();
+ test_clear();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_random.c b/tests/libdnssec/test_random.c
new file mode 100644
index 0000000..622c967
--- /dev/null
+++ b/tests/libdnssec/test_random.c
@@ -0,0 +1,82 @@
+/* Copyright (C) 2018 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 <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "crypto.h"
+#include "error.h"
+#include "random.h"
+
+int check_buffer(void)
+{
+ const size_t buffer_size = 128;
+ uint8_t buffer_prev[buffer_size];
+ memset(buffer_prev, 0, buffer_size);
+ uint8_t buffer[buffer_size];
+ memset(buffer, 0, buffer_size);
+
+ for (int i = 0; i < 10; i++) {
+ int result = dnssec_random_buffer(buffer, buffer_size);
+ if (result != DNSSEC_EOK) {
+ return 1;
+ }
+
+ if (memcmp(buffer, buffer_prev, buffer_size) == 0) {
+ return 1;
+ }
+
+ memmove(buffer_prev, buffer, buffer_size);
+ }
+
+ return 0;
+}
+
+int check_random_type(void)
+{
+ uint16_t numbers[1000] = { 0 };
+ int conflicts = 0;
+
+ for (int i = 0; i < 1000; i++) {
+ numbers[i] = dnssec_random_uint16_t();
+ // check all previous
+ for (int j = 0; j < i; j++) {
+ if (numbers[i] == numbers[j]) {
+ conflicts += 1;
+ }
+ }
+ }
+
+ // allow 5 % of conflicts
+ return conflicts <= 50 ? 0 : 1;
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ // quite stupid, just check if it does something
+
+ ok(check_buffer() == 0, "dnssec_random_buffer()");
+ ok(check_random_type() == 0, "dnssec_random_uint16_t()");
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_shared_bignum.c b/tests/libdnssec/test_shared_bignum.c
new file mode 100644
index 0000000..6571d90
--- /dev/null
+++ b/tests/libdnssec/test_shared_bignum.c
@@ -0,0 +1,128 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <string.h>
+
+#include "bignum.c"
+#include "binary.h"
+
+#define bin_init(array) { .data = array, .size = sizeof(array) }
+
+#define test_size(value, usize, ssize, msg) \
+ dnssec_binary_t __bin = bin_init(value); \
+ is_int(usize, bignum_size_u(&__bin), "bignum_size_u, " msg); \
+ is_int(ssize, bignum_size_s(&__bin), "bignum_size_s, " msg)
+
+#define test_write(num, expect, msg) \
+ uint8_t __buffer[sizeof(expect)]; \
+ memset(__buffer, 0xaa, sizeof(__buffer)); \
+ wire_ctx_t __ctx = wire_ctx_init(__buffer, sizeof(expect)); \
+ dnssec_binary_t __num = bin_init(num); \
+ dnssec_binary_t __exp = bin_init(expect); \
+ bignum_write(&__ctx, sizeof(expect), &__num); \
+ dnssec_binary_t __dst = bin_init(__buffer); \
+ ok(dnssec_binary_cmp(&__dst, &__exp) == 0, "bignum_write, " msg)
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ {
+ uint8_t num[] = { };
+ test_size(num, 1, 1, "empty string");
+ }
+
+ {
+ uint8_t num[] = { 0x00 };
+ test_size(num, 1, 1, "zero");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00 };
+ test_size(num, 1, 1, "long zero");
+ }
+
+ {
+ uint8_t num[] = { 0x01, 0x02, 0x03 };
+ test_size(num, 3, 3, "no MSB");
+ }
+
+ {
+ uint8_t num[] = { 0x7f, 0xff, 0x00, 0x00, 0x00 };
+ test_size(num, 5, 5, "no MSB but all other bits");
+ }
+
+ {
+ uint8_t num[] = { 0x84, 0x42 };
+ test_size(num, 2, 3, "MSB");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x84, 0x42 };
+ test_size(num, 2, 3, "MSB and leading zero");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00, 0x00, 0xfc, 0xe1, 0xda };
+ test_size(num, 3, 4, "MSB, many leading zeroes");
+ }
+
+ {
+ uint8_t num[] = { 0x00, 0x00, 0x00, 0x01 };
+ test_size(num, 1, 1, "no MSB, many leading zeroes");
+ }
+
+ // test writing
+
+ {
+ uint8_t num[] = { };
+ uint8_t exp[] = { 0x00 };
+ test_write(num, exp, "empty string");
+ }
+
+ {
+ uint8_t num[] = { 0x00 };
+ uint8_t exp[] = { 0x00 };
+ test_write(num, exp, "zero");
+ }
+
+ {
+ uint8_t num[] = { 0x11, 0x22, 0x33 };
+ uint8_t exp[] = { 0x00, 0x00, 0x00, 0x11, 0x22, 0x33 };
+ test_write(num, exp, "no MSB, right-aligned");
+ }
+
+ {
+ uint8_t num[] = { 0xff, 0xee, 0xdd };
+ uint8_t exp[] = { 0x00, 0x00, 0x00, 0xff, 0xee, 0xdd };
+ test_write(num, exp, "MSB, right-aligned");
+ }
+
+ {
+ uint8_t num[] = { 0x11, 0x22, 0x33 };
+ uint8_t exp[] = { 0x11, 0x22, 0x33 };
+ test_write(num, exp, "no MSB, fitting exactly");
+ }
+
+ {
+ uint8_t num[] = { 0xff, 0xee, 0xdd };
+ uint8_t exp[] = { 0xff, 0xee, 0xdd };
+ test_write(num, exp, "MSB, fitting exactly");
+ }
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_shared_dname.c b/tests/libdnssec/test_shared_dname.c
new file mode 100644
index 0000000..c3d8fd2
--- /dev/null
+++ b/tests/libdnssec/test_shared_dname.c
@@ -0,0 +1,79 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdbool.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "dname.c"
+
+static void ok_length(const char *dname, size_t length, const char *info)
+{
+ ok(dname_length((uint8_t *)dname) == length,
+ "dname_length() for %s", info);
+}
+
+static void test_length(void)
+{
+ ok_length(NULL, 0, "NULL");
+ ok_length("", 1, ".");
+ ok_length("\x2""cz", 4, "cz.");
+ ok_length("\x7""example""\x3""com", 13, "example.com.");
+}
+
+static bool dname_binary_equal(const uint8_t *one, const uint8_t *two)
+{
+ return one && two && strcmp((char *)one, (char *)two) == 0;
+}
+
+static void test_copy(void)
+{
+ const uint8_t *dname = (uint8_t *)"\x3""www""\x8""KNOT-DNS""\x2""cz";
+ uint8_t *copy = dname_copy(dname);
+ ok(dname_binary_equal(dname, copy), "dname_copy()");
+ free(copy);
+}
+
+static void test_equal(void)
+{
+ #define eq(a, b) dname_equal((uint8_t *)a, (uint8_t *)b)
+
+ ok(eq("\x4""kiwi""\x4""limo", "\x4""kiwi""\x4""limo") == true,
+ "dname_equal() same");
+ ok(eq("\x6""orange", "\x6""ORANGE") == true,
+ "dname_equal() case single label");
+ ok(eq("\x6""Banana""\03""Tea", "\x6""bANAna""\x3""tea") == true,
+ "dname_equal() case two labels");
+ ok(eq("\x4""Coco""\x4""MILK", "\x3""cow""\x4""milk") == false,
+ "dname_equal() different first");
+ ok(eq("\x4""LIME""\x5""syrup", "\x4""LIme""\x4""beer") == false,
+ "dname_equal() different last");
+ ok(eq("\x5""apple", "\x5""apple""\x5""shake") == false,
+ "dname_equal() a prefix of b");
+ ok(eq("\x5""apple""\x5""juice", "\x5""apple") == false,
+ "dname_equal() b prefix of a");
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_length();
+ test_copy();
+ test_equal();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_sign.c b/tests/libdnssec/test_sign.c
new file mode 100644
index 0000000..8f57a41
--- /dev/null
+++ b/tests/libdnssec/test_sign.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2021 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 <string.h>
+#include <tap/basic.h>
+
+#include "sample_keys.h"
+
+#include "binary.h"
+#include "crypto.h"
+#include "error.h"
+#include "key.h"
+#include "sign.h"
+
+static const dnssec_binary_t input_data = {
+ .size = 25,
+ .data = (uint8_t *)"Very good, young padawan."
+};
+
+static const dnssec_binary_t signed_rsa = { .size = 128, .data = (uint8_t []) {
+ 0x21, 0xba, 0xff, 0x0c, 0x15, 0x10, 0x73, 0x25, 0xa7, 0x8e,
+ 0xf4, 0x71, 0x4b, 0xd3, 0x97, 0x6d, 0x95, 0x52, 0xc2, 0x0b,
+ 0x43, 0xb3, 0x7d, 0x82, 0xe4, 0x3e, 0x2a, 0xc3, 0xb7, 0x17,
+ 0x5b, 0x05, 0xe9, 0x1e, 0x13, 0xac, 0x27, 0x6f, 0x20, 0x93,
+ 0x1a, 0xeb, 0xe2, 0x2c, 0x72, 0x70, 0x14, 0xe6, 0x49, 0xa7,
+ 0x62, 0xdd, 0x4c, 0x72, 0x1e, 0x1d, 0xd8, 0xf9, 0xba, 0xbc,
+ 0x96, 0x0e, 0xc3, 0xd4, 0xc1, 0x8f, 0x95, 0xdb, 0x01, 0x18,
+ 0x24, 0x43, 0xbd, 0x2b, 0x52, 0x9b, 0x10, 0x1f, 0xba, 0x0a,
+ 0x76, 0xbe, 0x0e, 0xaa, 0x91, 0x27, 0x7b, 0x9f, 0xed, 0x5a,
+ 0xad, 0x96, 0x1a, 0x02, 0x97, 0x42, 0x91, 0x30, 0x03, 0x2b,
+ 0x5c, 0xb8, 0xcc, 0x6b, 0xcf, 0x39, 0x62, 0x5e, 0x47, 0xae,
+ 0x6d, 0x5b, 0x43, 0xd2, 0xc2, 0xd8, 0x22, 0x5d, 0xf5, 0x5e,
+ 0x0a, 0x97, 0x65, 0x41, 0xc7, 0xaa, 0x28, 0x67,
+}};
+
+static const dnssec_binary_t signed_ecdsa = { .size = 64, .data = (uint8_t []) {
+ 0xa2, 0x95, 0x76, 0xb5, 0xf5, 0x7e, 0xbd, 0xdd, 0xf5, 0x62,
+ 0xa2, 0xc3, 0xa4, 0x8d, 0xd4, 0x53, 0x5c, 0xba, 0x29, 0x71,
+ 0x8c, 0xcc, 0x28, 0x7b, 0x58, 0xf3, 0x1e, 0x4e, 0x58, 0xe2,
+ 0x36, 0x7e,
+ 0xa0, 0x1a, 0xb6, 0xe6, 0x29, 0x71, 0x1b, 0xd3, 0x8c, 0x88,
+ 0xc3, 0xee, 0x12, 0x0e, 0x69, 0x70, 0x55, 0x99, 0xec, 0xd5,
+ 0xf6, 0x4f, 0x4b, 0xe2, 0x41, 0xd9, 0x10, 0x7e, 0x67, 0xe5,
+ 0xad, 0x2f,
+}};
+
+#ifdef HAVE_ED25519
+static const dnssec_binary_t signed_ed25519 = { .size = 64, .data = (uint8_t []) {
+ 0x0a, 0x9e, 0x51, 0x5f, 0x16, 0x89, 0x49, 0x27,
+ 0x0e, 0x98, 0x34, 0xd3, 0x48, 0xef, 0x5a, 0x6e,
+ 0x85, 0x2f, 0x7c, 0xd6, 0xd7, 0xc8, 0xd0, 0xf4,
+ 0x2c, 0x68, 0x8c, 0x1f, 0xf7, 0xdf, 0xeb, 0x7c,
+ 0x25, 0xd6, 0x1a, 0x76, 0x3e, 0xaf, 0x28, 0x1f,
+ 0x1d, 0x08, 0x10, 0x20, 0x1c, 0x01, 0x77, 0x1b,
+ 0x5a, 0x48, 0xd6, 0xe5, 0x1c, 0xf9, 0xe3, 0xe0,
+ 0x70, 0x34, 0x5e, 0x02, 0x49, 0xfb, 0x9e, 0x05,
+ }};
+#endif
+
+#ifdef HAVE_ED448
+static const dnssec_binary_t signed_ed448 = { .size = 114, .data = (uint8_t []) {
+ 0x8d, 0x79, 0x27, 0xbd, 0xe2, 0xc4, 0x23, 0xd8, 0x26, 0xc1, 0xd4, 0xab,
+ 0x6a, 0x0d, 0xdf, 0xe5, 0x5c, 0xf1, 0x8d, 0x3f, 0x1b, 0x13, 0x81, 0x94,
+ 0xb2, 0x2d, 0xf0, 0x94, 0x58, 0x38, 0x6c, 0xf7, 0xe8, 0xc0, 0x92, 0xab,
+ 0x33, 0x1f, 0x1c, 0xe8, 0x18, 0x3f, 0xab, 0x24, 0x41, 0x10, 0xf7, 0x04,
+ 0xf5, 0x81, 0x68, 0x0d, 0x0c, 0x38, 0x8d, 0xd6, 0x80, 0xb4, 0x6b, 0xe8,
+ 0x65, 0xc1, 0xce, 0x73, 0xc7, 0x54, 0x20, 0x32, 0x21, 0x7c, 0x63, 0x5e,
+ 0x55, 0xe0, 0xdf, 0x2b, 0xdd, 0xd7, 0xd1, 0x82, 0xe0, 0x41, 0x75, 0xd4,
+ 0xe9, 0xb9, 0x76, 0xb8, 0xa6, 0xa9, 0x0a, 0x4f, 0x18, 0xe1, 0x62, 0x27,
+ 0x74, 0x99, 0x01, 0x98, 0x5f, 0xdb, 0xea, 0xdf, 0xab, 0x59, 0x6c, 0x79,
+ 0xe2, 0xc2, 0x2a, 0x91, 0x29, 0x00
+ }};
+#endif
+
+static dnssec_binary_t binary_set_string(char *str)
+{
+ dnssec_binary_t result = { .data = (uint8_t *)str, .size = strlen(str) };
+ return result;
+}
+
+static void check_key(const key_parameters_t *key_data, const dnssec_binary_t *data,
+ const dnssec_binary_t *signature, bool signature_match)
+{
+ int r;
+
+ // initialize key from public parameters
+
+ dnssec_key_t *key = NULL;
+ r = dnssec_key_new(&key);
+ ok(r == DNSSEC_EOK && key != NULL, "create key");
+ r = dnssec_key_set_rdata(key, &key_data->rdata);
+ ok(r == DNSSEC_EOK, "set RDATA");
+
+ // check validation on static signature
+
+ dnssec_sign_ctx_t *ctx = NULL;
+ r = dnssec_sign_new(&ctx, key);
+ ok(r == DNSSEC_EOK, "create signing context");
+ r = dnssec_sign_add(ctx, data);
+ ok(r == DNSSEC_EOK, "add data to be signed");
+ r = dnssec_sign_verify(ctx, false, signature);
+ ok(r == DNSSEC_EOK, "signature verified");
+
+ // create new signature and self-validate
+
+ r = dnssec_key_load_pkcs8(key, &key_data->pem);
+ ok(r == DNSSEC_EOK, "load private key");
+
+ if (signature_match) {
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+ r = dnssec_sign_add(ctx, data);
+ ok(r == DNSSEC_EOK, "add data to be signed");
+ dnssec_binary_t new_signature = { 0 };
+ r = dnssec_sign_write(ctx, DNSSEC_SIGN_NORMAL, &new_signature);
+ ok(r == DNSSEC_EOK, "write the signature");
+ ok(dnssec_binary_cmp(signature, &new_signature) == 0,
+ "signature exact match");
+ r = dnssec_sign_verify(ctx, false, &new_signature);
+ ok(r == DNSSEC_EOK, "reverify the new signature");
+ dnssec_binary_free(&new_signature);
+ }
+
+ // context reinitialization
+
+ dnssec_binary_t tmp = { 0 };
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("bind");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (1)");
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("knot");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (2)");
+
+ tmp = binary_set_string(" is the best");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (3)");
+
+ dnssec_binary_t new_signature = { 0 };
+ r = dnssec_sign_write(ctx, DNSSEC_SIGN_NORMAL, &new_signature);
+ ok(r == DNSSEC_EOK, "write signature");
+
+ r = dnssec_sign_init(ctx);
+ ok(r == DNSSEC_EOK, "reinitialize context");
+
+ tmp = binary_set_string("knot is the best");
+ r = dnssec_sign_add(ctx, &tmp);
+ ok(r == DNSSEC_EOK, "add data (4)");
+
+ r = dnssec_sign_verify(ctx, false, &new_signature);
+ ok(r == DNSSEC_EOK, "verify signature");
+
+ dnssec_binary_free(&new_signature);
+
+ // cleanup
+
+ dnssec_sign_free(ctx);
+ dnssec_key_free(key);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ diag("RSA signing");
+ check_key(&SAMPLE_RSA_KEY, &input_data, &signed_rsa, true);
+ diag("ECDSA signing");
+ check_key(&SAMPLE_ECDSA_KEY, &input_data, &signed_ecdsa, false);
+#ifdef HAVE_ED25519
+ diag("ED25519 signing");
+ check_key(&SAMPLE_ED25519_KEY, &input_data, &signed_ed25519, true);
+#endif
+#ifdef HAVE_ED448
+ diag("ED448 signing");
+ check_key(&SAMPLE_ED448_KEY, &input_data, &signed_ed448, true);
+#endif
+
+ dnssec_crypto_cleanup();
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_sign_der.c b/tests/libdnssec/test_sign_der.c
new file mode 100644
index 0000000..18745a9
--- /dev/null
+++ b/tests/libdnssec/test_sign_der.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "bignum.c"
+#include "binary.h"
+#include "error.h"
+#include "sign/der.c"
+
+static int binary_eq(const dnssec_binary_t *a, const dnssec_binary_t *b)
+{
+ return a && b &&
+ a->size == b->size &&
+ memcmp(a->data, b->data, a->size) == 0;
+}
+
+#define DECODE_OK(der, r, s, message) \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __out_s = { 0 }; \
+ dnssec_binary_t __out_r = { 0 }; \
+ int _result = dss_sig_value_decode(&__der, &__out_r, &__out_s); \
+ ok(_result == DNSSEC_EOK && \
+ binary_eq(&__r, &__out_r) && \
+ binary_eq(&__s, &__out_s), \
+ "decode ok, " message)
+
+#define DECODE_FAIL(der, message) \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __out_r = { 0 }; \
+ dnssec_binary_t __out_s = { 0 }; \
+ int _result = dss_sig_value_decode(&__der, &__out_r, &__out_s); \
+ ok(_result != DNSSEC_EOK, \
+ "decode fail, " message)
+
+#define ENCODE_OK(r, s, der, message) \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __der = { .data = der, .size = sizeof(der) }; \
+ dnssec_binary_t __out_der = { 0 }; \
+ int _result = dss_sig_value_encode(&__r, &__s, &__out_der); \
+ ok(_result == DNSSEC_EOK && \
+ binary_eq(&__der, &__out_der), \
+ "encode ok, " message); \
+ dnssec_binary_free(&__out_der)
+
+#define ENCODE_FAIL(r, s, message) \
+ dnssec_binary_t __r = { .data = r, .size = sizeof(r) }; \
+ dnssec_binary_t __s = { .data = s, .size = sizeof(s) }; \
+ dnssec_binary_t __out_der = { 0 }; \
+ int _result = dss_sig_value_encode(&__r, &__s, &__out_der); \
+ ok(_result != DNSSEC_EOK, \
+ "encode fail, " message); \
+ dnssec_binary_free(&__out_der)
+
+#define ONE_64_TIMES \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, \
+ 0x01,0x01,0x01,0x01
+
+#define ONE_128_TIMES \
+ ONE_64_TIMES, ONE_64_TIMES
+
+int main(void)
+{
+ plan_lazy();
+
+ {
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0x1a,0x2b, 0x02,0x02,0x3c,0x4d };
+ uint8_t r[] = { 0x1a, 0x2b };
+ uint8_t s[] = { 0x3c, 0x4d };
+ DECODE_OK(der, r, s, "simple without MSB");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0xff,0xff, 0x02,0x02,0x80,0x00 };
+ uint8_t r[] = { 0xff, 0xff };
+ uint8_t s[] = { 0x80, 0x00 };
+ DECODE_OK(der, r, s, "simple with MSB");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x09, 0x02,0x04,0x00,0x00,0x00,0xfa, 0x02,0x01,0x07 };
+ uint8_t r[] = { 0xfa };
+ uint8_t s[] = { 0x07 };
+ DECODE_OK(der, r, s, "leading zeros");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x00, 0x02,0x02,0x00,0x00 };
+ uint8_t r[] = { 0x00 };
+ uint8_t s[] = { 0x00 };
+ DECODE_OK(der, r, s, "zero values" );
+ }
+
+ {
+ uint8_t der[] = { };
+ DECODE_FAIL(der, "empty input");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x04, 0x02,0x01,0x01 };
+ DECODE_FAIL(der, "partial sequence");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x01, 0x02,0x02,0x01 };
+ DECODE_FAIL(der, "partial integer");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x00 };
+ DECODE_FAIL(der, "zero-length sequence");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x05, 0x02,0x01,0xff, 0x02,0x00 };
+ DECODE_FAIL(der, "zero-length integer");
+ }
+
+ {
+ uint8_t der[] = { 0x30,0x84, 0x02,0x40,ONE_64_TIMES, 0x02,0x40,ONE_64_TIMES };
+ DECODE_FAIL(der, "unsupported size");
+ }
+
+ {
+ uint8_t r[] = { 0x01, };
+ uint8_t s[] = { 0x02,0x03 };
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x01, 0x02,0x02,0x02,0x03 };
+ ENCODE_OK(r, s, der, "simple");
+ }
+
+ {
+ uint8_t r[] = { 0x00,0x01, };
+ uint8_t s[] = { 0x00,0x00,0x02,0x03 };
+ uint8_t der[] = { 0x30,0x07, 0x02,0x01,0x01, 0x02,0x02,0x02,0x03 };
+ ENCODE_OK(r, s, der, "unnecessary leading zeros");
+ }
+
+ {
+ uint8_t r[] = { 0x00,0x8f };
+ uint8_t s[] = { 0x00,0x00,0xff };
+ uint8_t der[] = { 0x30,0x08, 0x02,0x02,0x00,0x8f, 0x02,0x02,0x00,0xff };
+ ENCODE_OK(r, s, der, "required zero not removed");
+ }
+
+ {
+ uint8_t r[] = { 0x8c };
+ uint8_t s[] = { 0xff,0xee };
+ uint8_t der[] = { 0x30,0x09, 0x02,0x02,0x00,0x8c, 0x02,0x03,0x00,0xff,0xee };
+ ENCODE_OK(r, s, der, "implicitly added zero");
+ }
+
+ {
+ uint8_t r[] = { 0x00 };
+ uint8_t s[] = { 0x00,0x00 };
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x00, 0x02,0x01,0x00 };
+ ENCODE_OK(r, s, der, "zero");
+ }
+
+ {
+ uint8_t r[] = { 0x01 };
+ uint8_t s[] = { };
+ uint8_t der[] = { 0x30,0x06, 0x02,0x01,0x01, 0x02,0x01,0x00 };
+ ENCODE_OK(r, s, der, "zero-length input");
+ }
+
+ {
+ uint8_t r[] = { ONE_128_TIMES };
+ uint8_t s[] = { 0x01 };
+ ENCODE_FAIL(r, s, "input too long");
+ }
+
+ {
+ uint8_t r[] = { ONE_64_TIMES };
+ uint8_t s[] = { ONE_64_TIMES };
+ ENCODE_FAIL(r, s, "result too long");
+ }
+
+ return 0;
+}
diff --git a/tests/libdnssec/test_tsig.c b/tests/libdnssec/test_tsig.c
new file mode 100644
index 0000000..bddb17f
--- /dev/null
+++ b/tests/libdnssec/test_tsig.c
@@ -0,0 +1,145 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+#include <string.h>
+
+#include "binary.h"
+#include "dname.c"
+#include "tsig.h"
+
+static const dnssec_binary_t payload = {
+ .size = 40,
+ .data = (uint8_t []){
+ 0xfd, 0x07, 0xca, 0x30, 0xf9, 0xff, 0x38, 0xb1, 0x32, 0x54,
+ 0xd1, 0x16, 0x24, 0xaa, 0x81, 0x2c, 0x97, 0xa0, 0x7a, 0xac,
+ 0x68, 0x7a, 0x3a, 0x60, 0xde, 0xc9, 0xf7, 0x7a, 0x5a, 0x58,
+ 0xff, 0xc9, 0x0c, 0xef, 0x31, 0xc7, 0x45, 0x2c, 0xee, 0x9d,
+ }
+};
+
+static const dnssec_binary_t key = {
+ .size = 16,
+ .data = (uint8_t []){
+ 0xa8, 0x05, 0x9c, 0x5c, 0x20, 0xc5, 0x00, 0x22, 0x6f, 0xad,
+ 0xf2, 0x55, 0xdf, 0x89, 0x8a, 0x68
+ }
+};
+
+typedef struct hmac {
+ int algorithm;
+ const char *name;
+ const dnssec_binary_t hmac;
+} hmac_t;
+
+static const hmac_t HMACS[] = {
+ { DNSSEC_TSIG_HMAC_MD5, "md5", { .size = 16, .data = (uint8_t []) {
+ 0x12, 0x38, 0x17, 0x4f, 0xa9, 0xc7, 0x5b, 0xcf, 0xd7, 0x08,
+ 0x19, 0x97, 0xf9, 0x3d, 0x5e, 0xe7
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA1, "sha1", { .size = 20, .data = (uint8_t []) {
+ 0xb8, 0x18, 0x2a, 0x5d, 0xf8, 0x2e, 0xa0, 0xb7, 0xcc, 0xcc,
+ 0xed, 0xc1, 0xaa, 0x34, 0xeb, 0x92, 0x48, 0xf9, 0x65, 0x7b
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA224, "sha224", { .size = 28, .data = (uint8_t []) {
+ 0xb7, 0x43, 0xcd, 0x0d, 0x9d, 0x51, 0x8c, 0x61, 0xc6, 0x43,
+ 0x98, 0x73, 0x5c, 0x16, 0x01, 0x1b, 0xfc, 0x82, 0xe9, 0x99,
+ 0xc2, 0x21, 0xde, 0x16, 0xb1, 0x94, 0x2d, 0xd5
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA256, "sha256", { .size = 32, .data = (uint8_t []) {
+ 0x16, 0x5e, 0xf6, 0xed, 0x9b, 0x1a, 0xe5, 0x67, 0x58, 0x7b,
+ 0xf1, 0x35, 0x9e, 0x59, 0xbd, 0x50, 0x6d, 0x72, 0xf8, 0x87,
+ 0x0e, 0x22, 0xda, 0x65, 0x00, 0xd6, 0x76, 0x91, 0xde, 0x5f,
+ 0xec, 0xd8
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA384, "sha384", { .size = 48, .data = (uint8_t []) {
+ 0x8a, 0xcf, 0xf3, 0xb7, 0x1c, 0xbe, 0x5c, 0x3e, 0x05, 0x74,
+ 0x97, 0x46, 0x04, 0x79, 0x3a, 0xe7, 0x8a, 0x5b, 0x7b, 0x12,
+ 0xca, 0xcd, 0xf2, 0xe2, 0xdf, 0xa9, 0x17, 0xfc, 0x8e, 0x61,
+ 0xc5, 0x86, 0x3e, 0xdc, 0xad, 0x84, 0x9e, 0x13, 0x0d, 0xa0,
+ 0x04, 0xb6, 0x6f, 0x7c, 0x85, 0x1b, 0x5c, 0xdf
+ }}},
+ { DNSSEC_TSIG_HMAC_SHA512, "sha512", { .size = 64, .data = (uint8_t []) {
+ 0xc3, 0x41, 0xd0, 0x96, 0x50, 0xd7, 0xf7, 0xfd, 0x59, 0x73,
+ 0xde, 0xd6, 0xc7, 0x4c, 0xda, 0xf1, 0x5d, 0xe1, 0x59, 0x34,
+ 0x79, 0xdc, 0x93, 0x23, 0xcb, 0xf2, 0x1f, 0x25, 0x4e, 0x35,
+ 0xb0, 0xd0, 0x9f, 0xfc, 0x22, 0xf1, 0xea, 0xbf, 0x9c, 0x18,
+ 0xd8, 0xcc, 0xcd, 0xb6, 0xb1, 0x4a, 0x06, 0x09, 0xc4, 0x3f,
+ 0x28, 0x93, 0x71, 0xd6, 0xca, 0xce, 0xf3, 0xa6, 0x08, 0x38,
+ 0xe3, 0x99, 0xc1, 0xb2
+ }}},
+ { 0 }
+};
+
+static void test_lookup_dname(const uint8_t *dname, int algorithm)
+{
+ dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(dname);
+ const char *name = dnssec_tsig_algorithm_to_name(algorithm);
+ if (name == NULL) name = "invalid";
+ ok(alg == algorithm, "dnssec_tsig_algorithm_from_dname(%s)", name);
+
+ const uint8_t *reverse = dnssec_tsig_algorithm_to_dname(algorithm);
+ ok((algorithm == DNSSEC_TSIG_UNKNOWN && reverse == NULL) ||
+ (algorithm != DNSSEC_TSIG_UNKNOWN && dname_equal(reverse, dname)),
+ "dnssec_tsig_algorithm_to_dname(%d)", algorithm);
+}
+
+static void test_lookup_name(const char *name, int algorithm)
+{
+ ok(dnssec_tsig_algorithm_from_name(name) == algorithm,
+ "dnssec_tsig_algorithm_from_name(%s)", name);
+
+ const char *reverse = dnssec_tsig_algorithm_to_name(algorithm);
+ ok((algorithm == DNSSEC_TSIG_UNKNOWN && reverse == NULL) ||
+ (algorithm != DNSSEC_TSIG_UNKNOWN && strcasecmp(reverse, name) == 0),
+ "dnssec_tsig_algorithm_to_name(%d)", algorithm);
+}
+
+static void test_tsig_hmac(const hmac_t *params)
+{
+ dnssec_tsig_ctx_t *ctx = NULL;
+ dnssec_tsig_new(&ctx, params->algorithm, &key);
+ dnssec_tsig_add(ctx, &payload);
+
+ size_t size = dnssec_tsig_size(ctx);
+ uint8_t hmac[size];
+ dnssec_tsig_write(ctx, hmac);
+ dnssec_tsig_free(ctx);
+
+ ok(size == params->hmac.size && memcmp(hmac, params->hmac.data, size) == 0,
+ "dnssec_tsig_write(%s)", params->name);
+}
+
+int main(void)
+{
+ plan_lazy();
+
+ test_lookup_dname((uint8_t *)"\x08""HMAC-MD5""\x07""SIG-ALG""\x03""REG""\x03""INT",
+ DNSSEC_TSIG_HMAC_MD5);
+ test_lookup_dname((uint8_t *)"\x0B""hmac-sha224", DNSSEC_TSIG_HMAC_SHA224);
+ test_lookup_dname((uint8_t *)"\x06""foobar", DNSSEC_TSIG_UNKNOWN);
+
+ test_lookup_name("hmac-md5", DNSSEC_TSIG_HMAC_MD5);
+ test_lookup_name("hmac-sha512", DNSSEC_TSIG_HMAC_SHA512);
+ test_lookup_name("barfoo", DNSSEC_TSIG_UNKNOWN);
+ test_lookup_name("hmac-foo", DNSSEC_TSIG_UNKNOWN);
+
+ for (const hmac_t *h = HMACS; h->algorithm != 0; h++) {
+ test_tsig_hmac(h);
+ }
+
+ return 0;
+}
diff --git a/tests/libknot/test_control.c b/tests/libknot/test_control.c
new file mode 100644
index 0000000..3846f31
--- /dev/null
+++ b/tests/libknot/test_control.c
@@ -0,0 +1,221 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#define CTL_BUFF_SIZE 18
+#include "libknot/control/control.c"
+
+#define fake_ok(condition, msg, ...) \
+ if (!(condition)) { \
+ if (msg != NULL) { \
+ printf("error: " msg "\n", ##__VA_ARGS__); \
+ } \
+ exit(-1); \
+ }
+
+static void ctl_client(const char *socket, size_t argc, knot_ctl_data_t *argv)
+{
+ knot_ctl_t *ctl = knot_ctl_alloc();
+ fake_ok(ctl != NULL, "Allocate control");
+
+ int ret;
+ for (int i = 0; i < 20; i++) {
+ ret = knot_ctl_connect(ctl, socket);
+ if (ret == KNOT_EOK) {
+ break;
+ }
+ usleep(100000);
+ }
+ fake_ok(ret == KNOT_EOK, "Connect to socket");
+
+ diag("BEGIN: Client -> Server");
+
+ if (argc > 0) {
+ for (size_t i = 0; i < argc; i++) {
+ if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
+ fake_ok(ret == KNOT_EOK, "Client send data block end type");
+ } else {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
+ fake_ok(ret == KNOT_EOK, "Client send data %zu", i);
+ }
+ }
+ }
+
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ fake_ok(ret == KNOT_EOK, "Client send final data");
+
+ diag("END: Client -> Server");
+ diag("BEGIN: Client <- Server");
+
+ size_t count = 0;
+ knot_ctl_data_t data;
+ knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
+ while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
+ if (type == KNOT_CTL_TYPE_END) {
+ break;
+ }
+ if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
+ fake_ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
+ } else {
+ fake_ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
+ for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
+ fake_ok((data[i] == NULL && argv[count][i] == NULL) ||
+ (data[i] != NULL && argv[count][i] != NULL),
+ "Client compare input item occupation %zu", i);
+ if (data[i] == NULL) {
+ continue;
+ }
+
+ fake_ok(strcmp(data[i], argv[count][i]) == 0,
+ "Client compare input item '%s", argv[count][i]);
+ }
+ }
+ count++;
+ }
+ fake_ok(ret == KNOT_EOK, "Receive OK check");
+ fake_ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
+ fake_ok(count == argc, "Client compare input count '%zu'", argc);
+
+ diag("END: Client <- Server");
+
+ knot_ctl_close(ctl);
+ knot_ctl_free(ctl);
+}
+
+static void ctl_server(const char *socket, size_t argc, knot_ctl_data_t *argv)
+{
+ knot_ctl_t *ctl = knot_ctl_alloc();
+ ok(ctl != NULL, "Allocate control");
+
+ int ret = knot_ctl_bind(ctl, socket);
+ is_int(KNOT_EOK, ret, "Bind control socket");
+
+ ret = knot_ctl_accept(ctl);
+ is_int(KNOT_EOK, ret, "Accept a connection");
+
+ diag("BEGIN: Server <- Client");
+
+ size_t count = 0;
+ knot_ctl_data_t data;
+ knot_ctl_type_t type = KNOT_CTL_TYPE_DATA;
+ while ((ret = knot_ctl_receive(ctl, &type, &data)) == KNOT_EOK) {
+ if (type == KNOT_CTL_TYPE_END) {
+ break;
+ }
+ if (argv[count][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[count][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ok(type == KNOT_CTL_TYPE_BLOCK, "Receive block end type");
+ } else {
+ ok(type == KNOT_CTL_TYPE_DATA, "Check data type");
+ for (size_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) {
+ ok((data[i] == NULL && argv[count][i] == NULL) ||
+ (data[i] != NULL && argv[count][i] != NULL),
+ "Server compare input item occupation %zu", i);
+ if (data[i] == NULL) {
+ continue;
+ }
+
+ ok(strcmp(data[i], argv[count][i]) == 0,
+ "Server compare input item '%s", argv[count][i]);
+ }
+ }
+ count++;
+ }
+ is_int(KNOT_EOK, ret, "Receive OK check");
+ ok(type == KNOT_CTL_TYPE_END, "Receive EOF type");
+ ok(count == argc, "Server compare input count '%zu'", argc);
+
+ diag("END: Server <- Client");
+ diag("BEGIN: Server -> Client");
+
+ if (argc > 0) {
+ for (size_t i = 0; i < argc; i++) {
+ if (argv[i][KNOT_CTL_IDX_CMD] != NULL &&
+ argv[i][KNOT_CTL_IDX_CMD][0] == '\0') {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL);
+ is_int(KNOT_EOK, ret, "Client send data block end type");
+ } else {
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &argv[i]);
+ is_int(KNOT_EOK, ret, "Server send data %zu", i);
+ }
+ }
+ }
+
+ ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL);
+ is_int(KNOT_EOK, ret, "Server send final data");
+
+ diag("END: Server -> Client");
+
+ knot_ctl_close(ctl);
+ knot_ctl_unbind(ctl);
+ knot_ctl_free(ctl);
+}
+
+static void test_client_server_client(void)
+{
+ char *socket = test_mktemp();
+ ok(socket != NULL, "Make a temporary socket file '%s'", socket);
+
+ size_t data_len = 5;
+ knot_ctl_data_t data[] = {
+ { "command", "error", "section", "item", "identifier",
+ "zone", "owner", "ttl", "type", "data" },
+ { [KNOT_CTL_IDX_DATA] = "\x01\x02" },
+ { [KNOT_CTL_IDX_CMD] = "\0" }, // This means block end in this test!
+ { NULL },
+ { [KNOT_CTL_IDX_ERROR] = "Ultra long message" }
+ };
+
+ // Fork a client process.
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ ok(child_pid >= 0, "Process fork");
+ return;
+ }
+ if (child_pid == 0) {
+ ctl_client(socket, data_len, data);
+ free(socket);
+ return;
+ } else {
+ ctl_server(socket, data_len, data);
+ }
+
+ int status = 0;
+ wait(&status);
+ ok(WIFEXITED(status), "Wait for client");
+
+ test_rm_rf(socket);
+ free(socket);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Client -> Server -> Client");
+ test_client_server_client();
+
+ return 0;
+}
diff --git a/tests/libknot/test_cookies.c b/tests/libknot/test_cookies.c
new file mode 100644
index 0000000..82ba502
--- /dev/null
+++ b/tests/libknot/test_cookies.c
@@ -0,0 +1,174 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+#include <time.h>
+
+#include "libknot/cookies.h"
+#include "libknot/endian.h"
+#include "libknot/errcode.h"
+#include "contrib/sockaddr.h"
+
+static knot_edns_cookie_t client_generate(
+ struct sockaddr_storage *s_addr, const uint8_t *c_secret,
+ const char *msg, int code, const char *ref)
+{
+ knot_edns_cookie_params_t params = {
+ .server_addr = s_addr,
+ };
+ memcpy(params.secret, c_secret, sizeof(params.secret));
+
+ knot_edns_cookie_t cc;
+ int ret = knot_edns_cookie_client_generate(&cc, &params);
+ is_int(code, ret, "client_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ ok(cc.len == KNOT_EDNS_COOKIE_CLNT_SIZE && memcmp(cc.data, ref, cc.len) == 0,
+ "client_generate value: %s", msg);
+ }
+ return cc;
+}
+
+static knot_edns_cookie_t server_generate(
+ struct sockaddr_storage *c_addr, const uint8_t *s_secret, uint32_t timestamp,
+ const knot_edns_cookie_t *cc, const char *msg, int code, const char *ref)
+{
+ knot_edns_cookie_params_t params = {
+ .version = KNOT_EDNS_COOKIE_VERSION,
+ .timestamp = timestamp,
+ .client_addr = c_addr,
+ };
+ memcpy(params.secret, s_secret, sizeof(params.secret));
+
+ knot_edns_cookie_t sc;
+ int ret = knot_edns_cookie_server_generate(&sc, cc, &params);
+ is_int(code, ret, "server_generate ret: %s", msg);
+ if (ret == KNOT_EOK) {
+ ok(sc.len == 16 && memcmp(sc.data, ref, sc.len) == 0,
+ "server_generate value: %s", msg);
+ }
+ return sc;
+}
+
+static void client_check(
+ struct sockaddr_storage *s_addr, const uint8_t *secret,
+ knot_edns_cookie_t *cc, const char *msg, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .server_addr = s_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ int ret = knot_edns_cookie_client_check(cc, &params);
+ is_int(code, ret, "client_check ret: %s", msg);
+}
+
+static void server_check(
+ struct sockaddr_storage *c_addr, const uint8_t *secret,
+ knot_edns_cookie_t *sc, knot_edns_cookie_t *cc, uint32_t timestamp,
+ const char *msg, int code)
+{
+ knot_edns_cookie_params_t params = {
+ .version = KNOT_EDNS_COOKIE_VERSION,
+ .timestamp = timestamp,
+ .lifetime_before = 3600,
+ .lifetime_after = 300,
+ .client_addr = c_addr,
+ };
+ memcpy(params.secret, secret, sizeof(params.secret));
+
+ int ret = knot_edns_cookie_server_check(sc, cc, &params);
+ is_int(code, ret, "server_check ret: %s", msg);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_edns_cookie_t cc;
+ knot_edns_cookie_t sc;
+
+ const uint8_t c_secret1[] = "\x3F\x66\x51\xC9\x81\xC1\xD7\x3E\x58\x79\x25\xD2\xF9\x98\x5F\x08";
+ const uint8_t c_secret2[] = "\x4C\x31\x15\x17\xFA\xB6\xBF\xE2\xE1\x49\xAB\x74\xEC\x1B\xC9\xA0";
+ const uint8_t s_secret1[] = "\xE5\xE9\x73\xE5\xA6\xB2\xA4\x3F\x48\xE7\xDC\x84\x9E\x37\xBF\xCF";
+
+ struct sockaddr_storage c4_sa1 = { 0 };
+ struct sockaddr_storage c4_sa2 = { 0 };
+ struct sockaddr_storage s4_sa = { 0 };
+ sockaddr_set(&c4_sa1, AF_INET, "198.51.100.100", 0);
+ sockaddr_set(&c4_sa2, AF_INET, "203.0.113.203", 0);
+ sockaddr_set(&s4_sa, AF_INET, "192.0.2.53", 0);
+
+ struct sockaddr_storage c6_sa = { 0 };
+ struct sockaddr_storage s6_sa = { 0 };
+ sockaddr_set(&c6_sa, AF_INET6, "2001:db8:220:1:59de:d0f4:8769:82b8", 0);
+ sockaddr_set(&s6_sa, AF_INET6, "2001:db8:8f::53", 0);
+
+ const uint8_t c_secret6[] = "\x3B\x49\x5B\xA6\xA5\xB7\xFD\x87\x73\x5B\xD5\x8F\x1E\xF7\x26\x1D";
+ const uint8_t s_secret6[] = "\xDD\x3B\xDF\x93\x44\xB6\x78\xB1\x85\xA6\xF5\xCB\x60\xFC\xA7\x15";
+ const uint8_t s_secret7[] = "\x44\x55\x36\xBC\xD2\x51\x32\x98\x07\x5A\x5D\x37\x96\x63\xC9\x62";
+
+ // Learning a new Server Cookie
+
+ cc = client_generate(&s4_sa, c_secret1, "IPv4", KNOT_EOK,
+ "\x24\x64\xC4\xAB\xCF\x10\xC9\x57");
+ client_check(&s4_sa, c_secret1, &cc, "IPv4", KNOT_EOK);
+ sc = server_generate(&c4_sa1, s_secret1, 1559731985, &cc, "IPv4", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\x9F\x11\x1F\x81\x30\xC3\xEE\xE2\x94\x80");
+ server_check(&c4_sa1, s_secret1, &sc, &cc, 1559731985, "IPv4", KNOT_EOK);
+
+ // The same client learning a renewed (fresh) Server Cookie
+
+ server_generate(&c4_sa1, s_secret1, 1559734385, &cc, "IPv4", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xA8\x71\xD4\xA5\x64\xA1\x44\x2A\xCA\x77");
+
+ // Another client learning a renewed Server Cookie
+
+ cc = client_generate(&s4_sa, c_secret2, "IPv4", KNOT_EOK,
+ "\xFC\x93\xFC\x62\x80\x7D\xDB\x86");
+ char *sc_reserved = "\x01\xAB\xCD\xEF\x5C\xF7\x8F\x71\xA3\x14\x22\x7B\x66\x79\xEB\xF5";
+ memcpy(sc.data, sc_reserved, strlen(sc_reserved));
+ server_check(&c4_sa2, s_secret1, &sc, &cc, 1559727985, "IPv4", KNOT_EOK);
+
+ // Version check
+
+ sc.data[0] = 10;
+ server_check(&c4_sa2, s_secret1, &sc, &cc, 1559727985, "version", KNOT_ENOTSUP);
+
+ // IPv6 query with rolled over secret
+
+ cc = client_generate(&s6_sa, c_secret6, "IPv6", KNOT_EOK,
+ "\x22\x68\x1A\xB9\x7D\x52\xC2\x98");
+ client_check(&s6_sa, c_secret6, &cc, "IPv6", KNOT_EOK);
+ sc = server_generate(&c6_sa, s_secret6, 1559741817, &cc, "IPv6", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xC5\x79\x26\x55\x6B\xD0\x93\x4C\x72\xF8");
+ server_check(&c6_sa, s_secret6, &sc, &cc, 1559741961, "IPv6", KNOT_EOK);
+ sc = server_generate(&c6_sa, s_secret7, 1559741961, &cc, "IPv6", KNOT_EOK,
+ "\x01\x00\x00\x00\x5C\xF7\xC6\x09\xA6\xBB\x79\xD1\x66\x25\x50\x7A");
+
+ // Past lifetime check
+
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 + 3600, "last old", KNOT_EOK);
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 + 3601, "too old", KNOT_ERANGE);
+
+ // Future lifetime check
+
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 - 300, "last new", KNOT_EOK);
+ server_check(&c6_sa, s_secret7, &sc, &cc, 1559741961 - 301, "too new", KNOT_ERANGE);
+
+ return 0;
+}
diff --git a/tests/libknot/test_db.c b/tests/libknot/test_db.c
new file mode 100644
index 0000000..e409ad8
--- /dev/null
+++ b/tests/libknot/test_db.c
@@ -0,0 +1,287 @@
+/* Copyright (C) 2011 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <tap/basic.h>
+#include <tap/files.h>
+
+#include "contrib/string.h"
+#include "libknot/libknot.h"
+#include "contrib/mempattern.h"
+#include "contrib/openbsd/strlcpy.h"
+#include "contrib/ucw/mempool.h"
+
+/* UCW array sorting defines. */
+#define ASORT_PREFIX(X) str_key_##X
+#define ASORT_KEY_TYPE char*
+#define ASORT_LT(x, y) (strcmp((x), (y)) < 0)
+#include "contrib/ucw/array-sort.h"
+
+/* Constants. */
+#define KEY_MAXLEN 64
+#define KEY_SET(key, str) key.data = (str); key.len = strlen(str) + 1
+
+/*! \brief Generate random key. */
+static const char *alphabet = "abcdefghijklmn0123456789";
+static char *str_key_rand(size_t len, knot_mm_t *pool)
+{
+ char *s = mm_alloc(pool, len);
+ memset(s, 0, len);
+ for (unsigned i = 0; i < len - 1; ++i) {
+ s[i] = alphabet[rand() % strlen(alphabet)];
+ }
+ return s;
+}
+
+static void knot_db_test_set(unsigned nkeys, char **keys, void *opts,
+ const knot_db_api_t *api, knot_mm_t *pool)
+{
+ if (api == NULL) {
+ skip("API not compiled in");
+ return;
+ }
+
+ /* Create database */
+ knot_db_t *db = NULL;
+ int ret = api->init(&db, pool, opts);
+ ok(ret == KNOT_EOK && db != NULL, "%s: create", api->name);
+
+ /* Start WR transaction. */
+ knot_db_txn_t txn;
+ ret = api->txn_begin(db, &txn, 0);
+ is_int(KNOT_EOK, ret, "%s: txn_begin(WR)", api->name);
+
+ /* Insert keys */
+ knot_db_val_t key, val;
+ bool passed = true;
+ for (unsigned i = 0; i < nkeys; ++i) {
+ KEY_SET(key, keys[i]);
+ val.len = sizeof(void*);
+ val.data = &key.data;
+
+ ret = api->insert(&txn, &key, &val, 0);
+ if (ret != KNOT_EOK && ret != KNOT_EEXIST) {
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "%s: insert", api->name);
+
+ /* Commit WR transaction. */
+ ret = api->txn_commit(&txn);
+ is_int(KNOT_EOK, ret, "%s: txn_commit(WR)", api->name);
+
+ /* Start RD transaction. */
+ ret = api->txn_begin(db, &txn, KNOT_DB_RDONLY);
+ is_int(KNOT_EOK, ret, "%s: txn_begin(RD)", api->name);
+
+ /* Lookup all keys */
+ passed = true;
+ for (unsigned i = 0; i < nkeys; ++i) {
+ KEY_SET(key, keys[i]);
+
+ ret = api->find(&txn, &key, &val, 0);
+ if (ret != KNOT_EOK) {
+ passed = false;
+ break;
+ }
+
+ const char **stored_key = val.data;
+ if (strcmp(*stored_key, keys[i]) != 0) {
+ diag("%s: mismatch on element '%u'", api->name, i);
+ passed = false;
+ break;
+ }
+ }
+ ok(passed, "%s: lookup all keys", api->name);
+
+ /* Fetch dataset size. */
+ int db_size = api->count(&txn);
+ ok(db_size > 0 && db_size <= nkeys, "%s: count %d", api->name, db_size);
+
+ /* Unsorted iteration */
+ int iterated = 0;
+ knot_db_iter_t *it = api->iter_begin(&txn, 0);
+ while (it != NULL) {
+ ++iterated;
+ it = api->iter_next(it);
+ }
+ api->iter_finish(it);
+ is_int(db_size, iterated, "%s: unsorted iteration", api->name);
+
+ /* Sorted iteration. */
+ char first_key[KEY_MAXLEN] = { '\0' };
+ char second_key[KEY_MAXLEN] = { '\0' };
+ char last_key[KEY_MAXLEN] = { '\0' };
+ char key_buf[KEY_MAXLEN] = {'\0'};
+ iterated = 0;
+ memset(&key, 0, sizeof(key));
+ it = api->iter_begin(&txn, KNOT_DB_SORTED);
+ while (it != NULL) {
+ api->iter_key(it, &key);
+ if (iterated > 0) { /* Only if previous exists. */
+ if (strcmp(key_buf, key.data) > 0) {
+ diag("%s: iter_sort '%s' <= '%s' FAIL\n",
+ api->name, key_buf, (const char *)key.data);
+ break;
+ }
+ if (iterated == 1) {
+ memcpy(second_key, key.data, key.len);
+ }
+ } else {
+ memcpy(first_key, key.data, key.len);
+ }
+ ++iterated;
+ memcpy(key_buf, key.data, key.len);
+ it = api->iter_next(it);
+ }
+ strlcpy(last_key, key_buf, sizeof(last_key));
+ is_int(db_size, iterated, "%s: sorted iteration", api->name);
+ api->iter_finish(it);
+
+ /* Interactive iteration. */
+ it = api->iter_begin(&txn, KNOT_DB_NOOP);
+ if (it != NULL) { /* If supported. */
+ ret = 0;
+ /* Check if first and last keys are reachable */
+ it = api->iter_seek(it, NULL, KNOT_DB_FIRST);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(FIRST)", api->name);
+ /* Check left/right iteration. */
+ it = api->iter_seek(it, &key, KNOT_DB_NEXT);
+ ret += api->iter_key(it, &key);
+ is_string(second_key, key.data, "%s: iter_set(NEXT)", api->name);
+ it = api->iter_seek(it, &key, KNOT_DB_PREV);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(PREV)", api->name);
+ it = api->iter_seek(it, &key, KNOT_DB_LAST);
+ ret += api->iter_key(it, &key);
+ is_string(last_key, key.data, "%s: iter_set(LAST)", api->name);
+ /* Check if prev(last_key + 1) is the last_key */
+ strlcpy(key_buf, last_key, sizeof(key_buf));
+ key_buf[0] += 1;
+ KEY_SET(key, key_buf);
+ it = api->iter_seek(it, &key, KNOT_DB_LEQ);
+ ret += api->iter_key(it, &key);
+ is_string(last_key, key.data, "%s: iter_set(LEQ)", api->name);
+ /* Check if next(first_key - 1) is the first_key */
+ strlcpy(key_buf, first_key, sizeof(key_buf));
+ key_buf[0] -= 1;
+ KEY_SET(key, key_buf);
+ it = api->iter_seek(it, &key, KNOT_DB_GEQ);
+ ret += api->iter_key(it, &key);
+ is_string(first_key, key.data, "%s: iter_set(GEQ)", api->name);
+ api->iter_finish(it);
+ is_int(ret, 0, "%s: iter_* error codes", api->name);
+ }
+ api->txn_abort(&txn);
+
+ /* Deleting during iteration. */
+ const uint8_t DEL_MAX_CNT = 3;
+ api->txn_begin(db, &txn, 0);
+ api->clear(&txn);
+ for (uint8_t i = 0; i < DEL_MAX_CNT; ++i) {
+ key.data = &i;
+ key.len = sizeof(i);
+ val.data = NULL;
+ val.len = 0;
+
+ ret = api->insert(&txn, &key, &val, 0);
+ is_int(KNOT_EOK, ret, "%s: add key '%u'", api->name, i);
+ }
+ it = api->iter_begin(&txn, KNOT_DB_NOOP);
+ if (it != NULL) { /* If supported. */
+ is_int(DEL_MAX_CNT, api->count(&txn), "%s: key count before", api->name);
+ it = api->iter_seek(it, NULL, KNOT_DB_FIRST);
+ uint8_t pos = 0;
+ while (it != NULL) {
+ ret = api->iter_key(it, &key);
+ is_int(KNOT_EOK, ret, "%s: iter key before del", api->name);
+ is_int(pos, ((uint8_t *)(key.data))[0], "%s: iter compare key '%u'",
+ api->name, pos);
+
+ ret = knot_db_lmdb_iter_del(it);
+ is_int(KNOT_EOK, ret, "%s: iter del", api->name);
+
+ it = api->iter_next(it);
+
+ ret = api->iter_key(it, &key);
+ if (++pos < DEL_MAX_CNT) {
+ is_int(KNOT_EOK, ret, "%s: iter key after del", api->name);
+ is_int(pos, ((uint8_t *)key.data)[0], "%s: iter compare key '%u",
+ api->name, pos);
+ } else {
+ is_int(KNOT_EINVAL, ret, "%s: iter key after del", api->name);
+ }
+ }
+ api->iter_finish(it);
+ is_int(0, api->count(&txn), "%s: key count after", api->name);
+ }
+ api->txn_abort(&txn);
+
+ /* Clear database and recheck. */
+ ret = api->txn_begin(db, &txn, 0);
+ ret += api->clear(&txn);
+ ret += api->txn_commit(&txn);
+ is_int(0, ret, "%s: clear()", api->name);
+
+ /* Check if the database is empty. */
+ api->txn_begin(db, &txn, KNOT_DB_RDONLY);
+ db_size = api->count(&txn);
+ is_int(0, db_size, "%s: count after clear = %d", api->name, db_size);
+ api->txn_abort(&txn);
+
+ api->deinit(db);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_mm_t pool;
+ mm_ctx_mempool(&pool, MM_DEFAULT_BLKSIZE);
+
+ char *dbid = test_mkdtemp();
+ ok(dbid != NULL, "make temporary directory");
+
+ /* Random keys. */
+ unsigned nkeys = 10000;
+ char **keys = mm_alloc(&pool, sizeof(char*) * nkeys);
+ for (unsigned i = 0; i < nkeys; ++i) {
+ keys[i] = str_key_rand(KEY_MAXLEN, &pool);
+ }
+
+ /* Sort random keys. */
+ str_key_sort(keys, nkeys);
+
+ /* Execute test set for all backends. */
+ struct knot_db_lmdb_opts lmdb_opts = KNOT_DB_LMDB_OPTS_INITIALIZER;
+ lmdb_opts.path = dbid;
+ struct knot_db_trie_opts trie_opts = KNOT_DB_TRIE_OPTS_INITIALIZER;
+ knot_db_test_set(nkeys, keys, &lmdb_opts, knot_db_lmdb_api(), &pool);
+ knot_db_test_set(nkeys, keys, &trie_opts, knot_db_trie_api(), &pool);
+
+ /* Cleanup. */
+ mp_delete(pool.ctx);
+ test_rm_rf(dbid);
+ free(dbid);
+
+ return 0;
+}
diff --git a/tests/libknot/test_descriptor.c b/tests/libknot/test_descriptor.c
new file mode 100644
index 0000000..8c25e2c
--- /dev/null
+++ b/tests/libknot/test_descriptor.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/descriptor.h"
+
+#define BUF_LEN 256
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ const knot_rdata_descriptor_t *descr;
+ char name[BUF_LEN] = "";
+ int ret;
+ uint16_t num;
+
+ // Get descriptor, type num to string:
+ // 1. TYPE0
+ descr = knot_get_rdata_descriptor(0);
+ ok(descr->type_name == 0, "get TYPE0 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE0 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE0 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(0, name, BUF_LEN);
+ ok(ret != -1, "get TYPE0 ret");
+ ok(strcmp(name, "TYPE0") == 0, "get TYPE0 name");
+
+ // 2. A
+ descr = knot_get_rdata_descriptor(1);
+ ok(strcmp(descr->type_name, "A") == 0, "get A descriptor name");
+ ok(descr->block_types[0] == 4,
+ "get A descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get A descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(1, name, BUF_LEN);
+ ok(ret != -1, "get A ret");
+ ok(strcmp(name, "A") == 0, "get A name");
+
+ // 3. CNAME
+ descr = knot_get_rdata_descriptor(5);
+ ok(strcmp(descr->type_name, "CNAME") == 0, "get CNAME descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_COMPRESSIBLE_DNAME,
+ "get CNAME descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get CNAME descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(5, name, BUF_LEN);
+ ok(ret != -1, "get CNAME ret");
+ ok(strcmp(name, "CNAME") == 0, "get CNAME name");
+
+ // 4. TYPE38 (A6)
+ descr = knot_get_rdata_descriptor(38);
+ ok(descr->type_name == 0, "get TYPE38 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE38 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE38 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(38, name, BUF_LEN);
+ ok(ret != -1, "get TYPE38 ret");
+ ok(strcmp(name, "TYPE38") == 0, "get TYPE38 name");
+
+ // 5. ANY
+ descr = knot_get_rdata_descriptor(255);
+ ok(strcmp(descr->type_name, "ANY") == 0, "get ANY descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get ANY descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get ANY descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(255, name, BUF_LEN);
+ ok(ret != -1, "get ANY ret");
+ ok(strcmp(name, "ANY") == 0, "get ANY name");
+
+ // 6. TYPE65535
+ descr = knot_get_rdata_descriptor(65535);
+ ok(descr->type_name == 0, "get TYPE65535 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE65535 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE65535 descriptor 2. item type");
+
+ ret = knot_rrtype_to_string(65535, name, BUF_LEN);
+ ok(ret != -1, "get TYPE65535 ret");
+ ok(strcmp(name, "TYPE65535") == 0, "get TYPE65535 name");
+
+ // Class num to string:
+ // 7. CLASS0
+ ret = knot_rrclass_to_string(0, name, BUF_LEN);
+ ok(ret != -1, "get CLASS0 ret");
+ ok(strcmp(name, "CLASS0") == 0, "get CLASS0 name");
+
+ // 8. IN
+ ret = knot_rrclass_to_string(1, name, BUF_LEN);
+ ok(ret != -1, "get IN ret");
+ ok(strcmp(name, "IN") == 0, "get IN name");
+
+ // 9. ANY
+ ret = knot_rrclass_to_string(255, name, BUF_LEN);
+ ok(ret != -1, "get ANY ret");
+ ok(strcmp(name, "ANY") == 0, "get ANY name");
+
+ // 10. CLASS65535
+ ret = knot_rrclass_to_string(65535, name, BUF_LEN);
+ ok(ret != -1, "get CLASS65535 ret");
+ ok(strcmp(name, "CLASS65535") == 0, "get CLASS65535 name");
+
+ // String to type num:
+ // 11. A
+ ret = knot_rrtype_from_string("A", &num);
+ ok(ret != -1, "get A num ret");
+ ok(num == 1, "get A num");
+
+ // 12. a
+ ret = knot_rrtype_from_string("a", &num);
+ ok(ret != -1, "get a num ret");
+ ok(num == 1, "get a num");
+
+ // 13. AaAa
+ ret = knot_rrtype_from_string("AaAa", &num);
+ ok(ret != -1, "get AaAa num ret");
+ ok(num == 28, "get AaAa num");
+
+ // 14. ""
+ ret = knot_rrtype_from_string("", &num);
+ ok(ret == -1, "get "" num ret");
+
+ // 15. DUMMY
+ ret = knot_rrtype_from_string("DUMMY", &num);
+ ok(ret == -1, "get DUMMY num ret");
+
+ // 16. TypE33
+ ret = knot_rrtype_from_string("TypE33", &num);
+ ok(ret != -1, "get TypE33 num ret");
+ ok(num == 33, "get TypE33 num");
+
+ // 17. TYPE
+ ret = knot_rrtype_from_string("TYPE", &num);
+ ok(ret == -1, "get TYPE num ret");
+
+ // 18. TYPE0
+ ret = knot_rrtype_from_string("TYPE0", &num);
+ ok(ret != -1, "get TYPE0 num ret");
+ ok(num == 0, "get TYPE0 num");
+
+ // 19. TYPE65535
+ ret = knot_rrtype_from_string("TYPE65535", &num);
+ ok(ret != -1, "get TYPE65535 num ret");
+ ok(num == 65535, "get TYPE65535 num");
+
+ // 20. TYPE65536
+ ret = knot_rrtype_from_string("TYPE65536", &num);
+ ok(ret == -1, "get TYPE65536 num ret");
+
+ // String to class num:
+ // 21. In
+ ret = knot_rrclass_from_string("In", &num);
+ ok(ret != -1, "get In num ret");
+ ok(num == 1, "get In num");
+
+ // 22. ANY
+ ret = knot_rrclass_from_string("ANY", &num);
+ ok(ret != -1, "get ANY num ret");
+ ok(num == 255, "get ANY num");
+
+ // 23. ""
+ ret = knot_rrclass_from_string("", &num);
+ ok(ret == -1, "get "" num ret");
+
+ // 24. DUMMY
+ ret = knot_rrclass_from_string("DUMMY", &num);
+ ok(ret == -1, "get DUMMY num ret");
+
+ // 25. CLass33
+ ret = knot_rrclass_from_string("CLass33", &num);
+ ok(ret != -1, "get CLass33 num ret");
+ ok(num == 33, "get CLass33 num");
+
+ // 26. CLASS
+ ret = knot_rrclass_from_string("CLASS", &num);
+ ok(ret == -1, "get CLASS num ret");
+
+ // 27. CLASS0
+ ret = knot_rrclass_from_string("CLASS0", &num);
+ ok(ret != -1, "get CLASS0 num ret");
+ ok(num == 0, "get CLASS0 num");
+
+ // 28. CLASS65535
+ ret = knot_rrclass_from_string("CLASS65535", &num);
+ ok(ret != -1, "get CLASS65535 num ret");
+ ok(num == 65535, "get CLASS65535 num");
+
+ // 29. CLASS65536
+ ret = knot_rrclass_from_string("CLASS65536", &num);
+ ok(ret == -1, "get CLASS65536 num ret");
+
+ // Get obsolete descriptor:
+ // 30. TYPE0
+ descr = knot_get_obsolete_rdata_descriptor(0);
+ ok(descr->type_name == 0, "get TYPE0 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE0 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE0 descriptor 2. item type");
+
+ // 31. MD
+ descr = knot_get_obsolete_rdata_descriptor(3);
+ ok(strcmp(descr->type_name, "MD") == 0, "get MD descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ "get A descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get A descriptor 2. item type");
+
+ // 32. NXT
+ descr = knot_get_obsolete_rdata_descriptor(30);
+ ok(strcmp(descr->type_name, "NXT") == 0, "get NXT descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME,
+ "get CNAME descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_REMAINDER,
+ "get CNAME descriptor 2. item type");
+ ok(descr->block_types[2] == KNOT_RDATA_WF_END,
+ "get CNAME descriptor 3. item type");
+
+ // 33. TYPE38 (A6)
+ descr = knot_get_obsolete_rdata_descriptor(38);
+ ok(descr->type_name == 0, "get TYPE38 descriptor name");
+ ok(descr->block_types[0] == KNOT_RDATA_WF_REMAINDER,
+ "get TYPE38 descriptor 1. item type");
+ ok(descr->block_types[1] == KNOT_RDATA_WF_END,
+ "get TYPE38 descriptor 2. item type");
+
+ // knot_rrtype_to_string invalid output buffer size
+ ret = knot_rrtype_to_string(1, NULL, 0);
+ ok(ret == -1, "knot_rrtype_to_string: invalid output buffer size");
+
+ // knot_rrclass_to_string invalid output buffer size
+ ret = knot_rrclass_to_string(1, NULL, 0);
+ ok(ret == -1, "knot_rrclass_to_string: invalid output buffer size");
+
+ // knot_rrtype_is_metatype
+ ok(knot_rrtype_is_metatype(0) == 0,
+ "rrtype is not metatype");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_SIG) != 0,
+ "rrtype is SIG");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_OPT) != 0,
+ "rrtype is OPT");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_TKEY) != 0,
+ "rrtype is TKEY");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_TSIG) != 0,
+ "rrtype is TSIG");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_IXFR) != 0,
+ "rrtype is IXFR");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_AXFR) != 0,
+ "rrtype is AXFR");
+ ok(knot_rrtype_is_metatype(KNOT_RRTYPE_ANY) != 0,
+ "rrtype is ANY");
+
+ // knot_rrtype_is_dnssec
+ ok(knot_rrtype_is_dnssec(0) == 0,
+ "rrtype is not DNSSEC");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_DNSKEY) != 0,
+ "rrtype is DNSKEY");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_RRSIG) != 0,
+ "rrtype is RRSIG");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC) != 0,
+ "rrtype is NSEC");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC3) != 0,
+ "rrtype is NSEC3");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_NSEC3PARAM) != 0,
+ "rrtype is NSEC3PARAM");
+ ok(knot_rrtype_is_dnssec(KNOT_RRTYPE_CDNSKEY) != 0,
+ "rrtype is CDNSKEY");
+
+ // knot_rrtype_additional_needed
+ ok(knot_rrtype_additional_needed(0) == 0,
+ "rrtype is not additional needed");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_NS) != 0,
+ "rrtype is NS");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_MX) != 0,
+ "rrtype is MX");
+ ok(knot_rrtype_additional_needed(KNOT_RRTYPE_SRV) != 0,
+ "rrtype is SRV");
+
+ // knot_rrtype_should_be_lowercased
+ ok(knot_rrtype_should_be_lowercased(0) == 0,
+ "rrtype should not be lowercased");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NS) != 0,
+ "rrtype is NS");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MD) != 0,
+ "rrtype is MD");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MF) != 0,
+ "rrtype is MF");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_CNAME) != 0,
+ "rrtype is CNAME");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SOA) != 0,
+ "rrtype is SOA");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MB) != 0,
+ "rrtype is MB");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MG) != 0,
+ "rrtype is MG");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MR) != 0,
+ "rrtype is MR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_PTR) != 0,
+ "rrtype is PTR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MINFO) != 0,
+ "rrtype is MINFO");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_MX) != 0,
+ "rrtype is MX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RP) != 0,
+ "rrtype is RP");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_AFSDB) != 0,
+ "rrtype is AFSDB");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RT) != 0,
+ "rrtype is RT");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SIG) != 0,
+ "rrtype is SIG");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_PX) != 0,
+ "rrtype is PX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NXT) != 0,
+ "rrtype is NXT");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_NAPTR) != 0,
+ "rrtype is NAPTR");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_KX) != 0,
+ "rrtype is KX");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_SRV) != 0,
+ "rrtype is SRV");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_DNAME) != 0,
+ "rrtype is DNAME");
+ ok(knot_rrtype_should_be_lowercased(KNOT_RRTYPE_RRSIG) != 0,
+ "rrtype is RRSIG");
+
+ ret = knot_opt_code_to_string(0, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "CODE0") == 0, "opt to str, code 0");
+ ret = knot_opt_code_to_string(10, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "COOKIE") == 0, "opt to str, code 10");
+ ret = knot_opt_code_to_string(65535, name, BUF_LEN);
+ ok(ret != -1 && strcmp(name, "CODE65535") == 0, "opt to str, code 65535");
+
+ return 0;
+}
diff --git a/tests/libknot/test_dname.c b/tests/libknot/test_dname.c
new file mode 100644
index 0000000..2480135
--- /dev/null
+++ b/tests/libknot/test_dname.c
@@ -0,0 +1,618 @@
+/* Copyright (C) 2021 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/dname.h"
+
+/* Test dname_parse_from_wire */
+static int test_fw(size_t l, const char *w) {
+ const uint8_t *np = NULL;
+ if (w != NULL) {
+ np = (const uint8_t *)w + l;
+ }
+ return knot_dname_wire_check((const uint8_t *)w, np, NULL) > 0;
+}
+
+/* Test dname to/from string operations */
+static void test_str(const char *in_str, const char *in_bin, size_t bin_len)
+{
+ knot_dname_storage_t d1;
+ knot_dname_txt_storage_t s1;
+ knot_dname_t *d2 = NULL, *aux_d = NULL;
+ char *s2 = NULL, *aux_s = NULL;
+ int ret = 0;
+
+ /* dname_from_str */
+ aux_d = knot_dname_from_str(d1, in_str, sizeof(d1));
+ ok(aux_d != NULL, "dname_from_str: %s", in_str);
+ if (aux_d == NULL) {
+ skip_block(10, "dname_from_str: %s", in_str);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d1, d1 + sizeof(d1), NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", in_str);
+
+ /* dname compare */
+ ok(memcmp(d1, in_bin, bin_len) == 0, "dname compare: %s", in_str);
+
+ /* dname_to_str */
+ aux_s = knot_dname_to_str(s1, d1, sizeof(s1));
+ ok(aux_s != NULL, "dname_to_str: %s", in_str);
+ if (aux_s == NULL) {
+ skip_block(7, "dname_to_str: %s", in_str);
+ return;
+ }
+
+ /* dname_from_str_alloc */
+ d2 = knot_dname_from_str_alloc(s1);
+ ok(d2 != NULL, "dname_from_str_alloc: %s", s1);
+ if (d2 == NULL) {
+ skip_block(6, "dname_from_str_alloc: %s", s1);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d2, d2 + bin_len, NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", s1);
+
+ /* dname compare */
+ ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s1);
+
+ /* dname_to_str_alloc */
+ s2 = knot_dname_to_str_alloc(d2);
+ knot_dname_free(d2, NULL);
+ ok(s2 != NULL, "dname_to_str_alloc: %s", s1);
+ if (s2 == NULL) {
+ skip_block(3, "dname_to_str_alloc: %s", s1);
+ return;
+ }
+
+ /* As the string representation is ambiguous, the following steps
+ * are just for comparison in wire form.
+ */
+ d2 = knot_dname_from_str_alloc(s2);
+ ok(d2 != NULL, "dname_from_str_alloc: %s", s2);
+ if (aux_d == NULL) {
+ skip_block(2, "dname_from_str_alloc: %s", s2);
+ free(s2);
+ return;
+ }
+
+ /* dname_wire_check */
+ ret = knot_dname_wire_check(d2, d2 + bin_len, NULL);
+ ok(ret == bin_len, "dname_wire_check: %s", s2);
+
+ /* dname compare */
+ ok(d2 && memcmp(d2, in_bin, bin_len) == 0, "dname compare: %s", s2);
+
+ knot_dname_free(d2, NULL);
+ free(s2);
+}
+
+static void test_dname_lf(void)
+{
+ knot_dname_storage_t storage;
+
+ /* Maximal DNAME length */
+ const knot_dname_t *in = (uint8_t *)
+ "\x3f""iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
+ "\x3f""hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
+ "\x3f""ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg"
+ "\x1f""fffffffffffffffffffffffffffffff"
+ "\x0f""eeeeeeeeeeeeeee"
+ "\x07""ddddddd"
+ "\x03""ccc"
+ "\x01""b"
+ "\x00";
+ const uint8_t *ref = (uint8_t *)
+ "\xFE"
+ "b""\x00"
+ "ccc""\00"
+ "ddddddd""\x00"
+ "eeeeeeeeeeeeeee""\x00"
+ "fffffffffffffffffffffffffffffff""\x00"
+ "ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg""\x00"
+ "hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh""\x00"
+ "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii""\x00";
+ assert(strlen((const char *)in) == KNOT_DNAME_MAXLEN - 1);
+ uint8_t *out = knot_dname_lf(in, storage);
+ ok(out != NULL && memcmp(ref, out, KNOT_DNAME_MAXLEN) == 0,
+ "knot_dname_lf: max-length DNAME converted");
+
+ /* Zero label DNAME*/
+ in = (uint8_t *) "\x00";
+ out = knot_dname_lf(in, storage);
+ ok(out != NULL && out[0] == '\x00', "knot_dname_lf: zero-label DNAME converted");
+}
+
+static void test_dname_storage(void)
+{
+ const knot_dname_t *dname = (uint8_t *)"\x04""test";
+ size_t dname_len = knot_dname_size(dname);
+
+ knot_dname_storage_t storage;
+ size_t store_len = knot_dname_store(storage, dname);
+ size_t storage_len = knot_dname_size(storage);
+
+ ok(store_len == dname_len && storage_len == dname_len &&
+ memcmp(storage, dname, dname_len) == 0,
+ "knot_dname_storage: valid name");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_dname_t *d = NULL, *d2 = NULL;
+ const char *w = NULL, *t = NULL;
+ char *s = NULL;
+
+ /* DNAME WIRE CHECKS */
+
+ /* NULL wire */
+ ok(!test_fw(0, NULL), "parsing NULL dname");
+
+ /* empty label */
+ ok(test_fw(1, ""), "parsing empty dname");
+
+ /* incomplete dname */
+ ok(!test_fw(5, "\x08" "dddd"), "parsing incomplete wire");
+
+ /* non-fqdn */
+ ok(!test_fw(3, "\x02" "ab"), "parsing non-fqdn name");
+
+ /* label length == 63 */
+ w = "\x3f" "123456789012345678901234567890123456789012345678901234567890123";
+ ok(test_fw(1 + 63 + 1, w), "parsing label length == 63");
+
+ /* label length > 63 */
+ w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234";
+ ok(!test_fw(1 + 64 + 1, w), "parsing label length > 63");
+
+ /* label count == 127 (also maximal dname length) */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ ok(test_fw(127 * 2 + 1, w), "parsing label count == 127");
+
+ /* label count > 127 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ ok(!test_fw(128 * 2 + 1, w), "parsing label count > 127");
+
+ /* dname length > 255 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64";
+ ok(!test_fw(126 * 2 + 3 + 1, w), "parsing dname len > 255");
+
+ /* DNAME STRING CHECKS */
+
+ /* root dname */
+ test_str(".", "\x00", 1);
+
+ /* 1-char dname */
+ test_str("a.", "\x01""a", 2 + 1);
+
+ /* 1-char dname - non-fqdn */
+ test_str("a", "\x01""a", 2 + 1);
+
+ /* wildcard and asterisks */
+ test_str("*.*a.a*a.**.",
+ "\x01" "*" "\x02" "*a" "\x03" "a*a" "\x02" "**",
+ 2 + 3 + 4 + 3 + 1);
+
+ /* special label */
+ test_str("\\000\\0320\\ \\\\\\\"\\.\\@\\*.",
+ "\x09" "\x00\x20\x30\x20\x5c\x22.@*",
+ 10 + 1);
+
+ /* unescaped special characters */
+ test_str("_a.b-c./d.",
+ "\x02" "_a" "\x03" "b-c" "\x02" "/d",
+ 3 + 4 + 3 + 1);
+
+ /* all possible characters */
+ test_str("\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019",
+ "\x14" "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13",
+ 22);
+ test_str("\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029\\030\\031\\032\\033\\034\\035\\036\\037\\038\\039",
+ "\x14" "\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27",
+ 22);
+ test_str("\\040\\041\\042\\043\\044\\045\\046\\047\\048\\049\\050\\051\\052\\053\\054\\055\\056\\057\\058\\059",
+ "\x14" "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b",
+ 22);
+ test_str("\\060\\061\\062\\063\\064\\065\\066\\067\\068\\069\\070\\071\\072\\073\\074\\075\\076\\077\\078\\079",
+ "\x14" "\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+ 22);
+ test_str("\\080\\081\\082\\083\\084\\085\\086\\087\\088\\089\\090\\091\\092\\093\\094\\095\\096\\097\\098\\099",
+ "\x14" "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63",
+ 22);
+ test_str("\\100\\101\\102\\103\\104\\105\\106\\107\\108\\109\\110\\111\\112\\113\\114\\115\\116\\117\\118\\119",
+ "\x14" "\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77",
+ 22);
+ test_str("\\120\\121\\122\\123\\124\\125\\126\\127\\128\\129\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139",
+ "\x14" "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b",
+ 22);
+ test_str("\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149\\150\\151\\152\\153\\154\\155\\156\\157\\158\\159",
+ "\x14" "\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+ 22);
+ test_str("\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179",
+ "\x14" "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3",
+ 22);
+ test_str("\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199",
+ "\x14" "\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+ 22);
+ test_str("\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219",
+ "\x14" "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb",
+ 22);
+ test_str("\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239",
+ "\x14" "\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef",
+ 22);
+ test_str("\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249\\250\\251\\252\\253\\254\\255",
+ "\x10" "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+ 18);
+
+ /* maximal dname label length */
+ test_str("12345678901234567890123456789012345678901234567890123456789012\\063",
+ "\x3f" "12345678901234567890123456789012345678901234567890123456789012?",
+ 65);
+
+ /* maximal dname length */
+ test_str("1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "\\#234567890123456789012345678901234567890123456789012\\063",
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x31" "1234567890123456789012345678901234567890123456789"
+ "\x35" "#234567890123456789012345678901234567890123456789012?",
+ 255);
+
+ /* NULL output, positive maxlen */
+ w = "\x02" "aa";
+ s = knot_dname_to_str(NULL, (const uint8_t *)w, 1);
+ ok(s != NULL, "dname_to_str: null dname");
+ if (s != NULL) {
+ ok(memcmp(s, "aa.", 4) == 0, "dname_to_str: null dname compare");
+ free(s);
+ } else {
+ skip("dname_to_str: null dname");
+ }
+
+ /* non-NULL output, zero maxlen */
+ char s_small[2];
+ s = knot_dname_to_str(s_small, (const uint8_t *)w, 0);
+ ok(s == NULL, "dname_to_str: non-NULL output, zero maxlen");
+
+ /* small buffer */
+ s = knot_dname_to_str(s_small, (const uint8_t *)w, 1);
+ ok(s == NULL, "dname_to_str: small buffer");
+
+ /* NULL dname */
+ s = knot_dname_to_str_alloc(NULL);
+ ok(s == NULL, "dname_to_str: null dname");
+
+ /* empty dname is considered as a root dname */
+ w = "";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: empty dname");
+ if (s != NULL) {
+ ok(memcmp(s, ".", 1) == 0, "dname_to_str: empty dname is root dname");
+ free(s);
+ } else {
+ skip("dname_to_str: empty dname");
+ }
+
+ /* incomplete dname */
+ /* ASAN: global-buffer-overflow
+ w = "\x08" "dddd";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: incomplete dname");
+ free(s);
+ */
+
+ /* non-fqdn */
+ w = "\x02" "ab";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: non-fqdn");
+ free(s);
+
+ /* label length > 63 */
+ w = "\x40" "1234567890123456789012345678901234567890123456789012345678901234";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: label length > 63");
+ free(s);
+
+ /* label count > 127 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: label count > 127");
+ free(s);
+
+ /* dname length > 255 */
+ w = "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64"
+ "\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x01\x64\x02\x64\x64";
+ s = knot_dname_to_str_alloc((const uint8_t *)w);
+ ok(s != NULL, "dname_to_str: dname length > 255");
+ free(s);
+
+ /* output overflow sanity check */
+ uint8_t in[4] = "\x02""\x00\x00""\x00";
+ for (uint16_t i = 0; i < UINT16_MAX; i++) {
+ memcpy(in + 1, &i, sizeof(i));
+ for (int j = 3; j < 8; j++) {
+ char tmp[j];
+ char *out_static = knot_dname_to_str(tmp, in, sizeof(tmp));
+ char *out_dynamic = knot_dname_to_str_alloc(in);
+ if (out_dynamic == NULL) {
+ ok(out_dynamic != NULL, "dname_to_str_alloc: invalid input");
+ } else if (strlen(out_dynamic) < sizeof(tmp) - 1 &&
+ out_static == NULL) {
+ ok(out_static != NULL, "dname_to_str: invalid input");
+ }
+ free(out_dynamic);
+ }
+ }
+
+ /* NULL output, positive maxlen */
+ s = "aa.";
+ d = knot_dname_from_str(NULL, s, 1);
+ ok(s != NULL, "dname_from_str: null name");
+ if (s != NULL) {
+ ok(memcmp(d, "\x02" "aa", 4) == 0, "dname_from_str: null name compare");
+ free(d);
+ } else {
+ skip("dname_from_str: null name");
+ }
+
+ /* non-NULL output, zero maxlen */
+ uint8_t d_small[2];
+ d = knot_dname_from_str(d_small, s, 0);
+ ok(d == NULL, "dname_from_str: non-NULL output, zero maxlen");
+
+ /* small buffer */
+ d = knot_dname_from_str(d_small, s, 1);
+ ok(d == NULL, "dname_from_str: small buffer");
+
+ /* NULL string */
+ d = knot_dname_from_str_alloc(NULL);
+ ok(d == NULL, "dname_from_str: null string");
+
+ /* empty string */
+ t = "";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: empty string");
+
+ /* empty label */
+ t = "..";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: empty label");
+
+ /* leading dot */
+ t = ".a";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: leading dot");
+
+ /* incomplete decimal notation I */
+ t = "\\1";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: incomplete decimal I");
+
+ /* incomplete decimal notation II */
+ t = "\\12";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: incomplete decimal II");
+
+ /* invalid decimal notation I */
+ t = "\\256";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid decimal I");
+
+ /* invalid decimal notation II */
+ t = "\\2x6";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid decimal II");
+
+ /* invalid escape notation */
+ t = "\\2";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: invalid escape");
+
+ /* label length > 63 I */
+ t = "1234567890123456789012345678901234567890123456789012345678901234";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 I");
+
+ /* label length > 63 II */
+ t = "123456789012345678901234567890123456789012345678901234567890123\\?";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 II");
+
+ /* label length > 63 III */
+ t = "123456789012345678901234567890123456789012345678901234567890123\\063";
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: label length > 63 III");
+
+ /* dname length > 255 */
+ t = "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "1234567890123456789012345678901234567890123456789."
+ "123456789012345678901234567890123456789012345678901234.",
+ d = knot_dname_from_str_alloc(t);
+ ok(d == NULL, "dname_from_str: dname length > 255");
+
+ /* DNAME SUBDOMAIN CHECKS */
+
+ /* equal name is subdomain */
+ t = "ab.cd.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ t = "ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) == 0, "dname_subdomain: equal name");
+ knot_dname_free(d, NULL);
+
+ /* true subdomain */
+ t = "0.ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) == 1, "dname_subdomain: true subdomain");
+ knot_dname_free(d, NULL);
+
+ /* not subdomain */
+ t = "cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d, d2) < 0, "dname_subdomain: not subdomain");
+ knot_dname_free(d, NULL);
+
+ /* root subdomain */
+ t = ".";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_in_bailiwick(d2, d) == 3, "dname_subdomain: root subdomain");
+ knot_dname_free(d, NULL);
+ knot_dname_free(d2, NULL);
+
+ /* DNAME EQUALITY CHECKS */
+
+ t = "ab.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_equal(d, d), "dname_is_equal: equal names");
+
+ t = "ab.cd.fe";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: same label count");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) < len(d2)");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.ef.gh";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: len(d1) > len(d2)");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.efe";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label longer");
+ knot_dname_free(d2, NULL);
+
+ t = "ab.cd.e";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_equal(d, d2), "dname_is_equal: last label shorter");
+ knot_dname_free(d2, NULL);
+
+ knot_dname_free(d, NULL);
+
+ /* DNAME EQUALITY CHECKS IGNORING CASE */
+
+ t = "aB.cd.ef";
+ d = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_case_equal(d, d), "dname_is_case_equal: equal case");
+
+ t = "aB.cD.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different case");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.dc.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different name");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.cd";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different length");
+ knot_dname_free(d2, NULL);
+
+ t = "aB.cdx.ef";
+ d2 = knot_dname_from_str_alloc(t);
+ ok(!knot_dname_is_case_equal(d, d2), "dname_is_case_equal: different label");
+ knot_dname_free(d2, NULL);
+
+ knot_dname_free(d, NULL);
+
+ /* OTHER CHECKS */
+
+ test_dname_lf();
+
+ test_dname_storage();
+
+ return 0;
+}
diff --git a/tests/libknot/test_dynarray.c b/tests/libknot/test_dynarray.c
new file mode 100644
index 0000000..ac1e0f1
--- /dev/null
+++ b/tests/libknot/test_dynarray.c
@@ -0,0 +1,137 @@
+/* Copyright (C) 2022 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 <string.h>
+#include <tap/basic.h>
+
+#include "libknot/dynarray.h"
+
+#define test_capacity 5
+// minimum 3
+
+typedef struct {
+ int x;
+ int x2;
+} quadrate_t;
+
+knot_dynarray_declare(q, quadrate_t, DYNARRAY_VISIBILITY_STATIC, test_capacity);
+knot_dynarray_define(q, quadrate_t, DYNARRAY_VISIBILITY_STATIC);
+
+static q_dynarray_t q_fill(size_t howmany)
+{
+ quadrate_t q = { 0 };
+ q_dynarray_t qd = { 0 };
+
+ for (size_t i = 0; i < howmany; i++) {
+ q.x2 = q.x * q.x;
+ q_dynarray_add(&qd, &q);
+ q.x++;
+ }
+ return qd;
+}
+
+static void check_arr(q_dynarray_t *q, size_t count, size_t index, const char *msg)
+{
+ quadrate_t *arr = q->arr(q);
+ ok(arr[index].x == index && arr[index].x2 == index * index,
+ "%s check: index %zu", msg, index);
+
+ size_t i = 0;
+ knot_dynarray_foreach(q, quadrate_t, p, *q) {
+ ok(p->x == i && p->x2 == i * i, "%s foreach: index %zu", msg, i);
+ i++;
+ }
+
+ ok(i == count, "%s foreach: whole array", msg);
+}
+
+static size_t q_set_dups(q_dynarray_t *q, double dup_percentage, const quadrate_t *dupval)
+{
+ size_t dup_cnt = 0;
+ int threshold = (int)(dup_percentage / 100 * ((double)RAND_MAX + 1.0));
+
+ knot_dynarray_foreach(q, quadrate_t, item, *q) {
+ if (rand() < threshold && q_dynarray_memb_cmp(item, dupval) != 0) {
+ *item = *dupval;
+ dup_cnt++;
+ }
+ }
+
+ return dup_cnt;
+}
+
+static void check_dups(q_dynarray_t *q, const quadrate_t *dupval,
+ const size_t expected, const char *msg)
+{
+ size_t cnt = 0;
+ knot_dynarray_foreach(q, quadrate_t, item, *q) {
+ if (q_dynarray_memb_cmp(item, dupval) == 0) {
+ cnt++;
+ }
+ }
+ ok(cnt == expected, "duplicate items: %s", msg);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // first fill
+ q_dynarray_t q = q_fill(test_capacity - 1);
+ check_arr(&q, test_capacity - 1, test_capacity - 3, "initial");
+ q_dynarray_free(&q);
+
+ // second fill
+ q = q_fill(test_capacity + 3);
+ check_arr(&q, test_capacity + 3, test_capacity + 1, "second");
+ q_dynarray_free(&q);
+
+ // third fill
+ q = q_fill(test_capacity * 5);
+ check_arr(&q, test_capacity * 5, test_capacity * 4, "third");
+ q_dynarray_free(&q);
+
+ // duplicate items removal test
+ q = q_fill(test_capacity * 10);
+ quadrate_t dup_item = { .x = 0, .x2 = 0 }; // matches the first item
+ size_t dups = q_set_dups(&q, 50, &dup_item);
+ ok(q.size == test_capacity * 10, "duplicate items: created");
+ check_dups(&q, &dup_item, dups + 1, "created all");
+
+ q_dynarray_remove(&q, &dup_item);
+ ok(q.size == test_capacity * 10 - dups - 1, "duplicate items: removed");
+ check_dups(&q, &dup_item, 0, "removed all");
+
+ q_dynarray_free(&q);
+
+ // binary search removal test
+ q = q_fill(test_capacity * 10);
+ for (int i = 0; i < test_capacity * 10; i++) {
+ quadrate_t qu = { i, i * i };
+ if ((qu.x % 2) == 0) {
+ q_dynarray_remove(&q, &qu);
+ }
+ }
+ q_dynarray_sort(&q);
+ for (int i = 0; i < test_capacity * 10; i++) {
+ quadrate_t qu = { i, i * i };
+ int present = (q_dynarray_bsearch(&q, &qu) != NULL ? 1 : 0);
+ ok(present == (i % 2), "presence in sorted array %d", i);
+ }
+ q_dynarray_free(&q);
+
+ return 0;
+}
diff --git a/tests/libknot/test_edns.c b/tests/libknot/test_edns.c
new file mode 100644
index 0000000..d2b4fd8
--- /dev/null
+++ b/tests/libknot/test_edns.c
@@ -0,0 +1,502 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+
+#include <assert.h>
+#include "libknot/libknot.h"
+#include "libknot/rrtype/opt.h"
+#include "libknot/descriptor.h"
+#include "libknot/wire.h"
+#include "contrib/sockaddr.h"
+
+static const uint16_t E_MAX_PLD = 10000;
+static const uint16_t E_MAX_PLD2 = 20000;
+static const uint8_t E_VERSION = 1;
+static const uint8_t E_VERSION2 = 2;
+static const uint8_t E_RCODE = 0;
+static const uint8_t E_RCODE2 = 200;
+
+static const char *E_NSID_STR = "FooBar";
+static const char *E_NSID_STR2 = "BarFoo";
+static const uint16_t E_NSID_LEN = 6;
+
+#define E_NSID_SIZE (4 + E_NSID_LEN)
+
+static const uint16_t E_OPT3_CODE = 15;
+static const char *E_OPT3_FAKE_DATA = "Not used";
+static const char *E_OPT3_DATA = NULL;
+static const uint16_t E_OPT3_LEN = 0;
+static const uint16_t E_OPT3_FAKE_LEN = 8;
+
+#define E_OPT3_SIZE (4 + E_OPT3_LEN)
+
+static const uint16_t E_OPT4_CODE = 30;
+static const char *E_OPT4_DATA = NULL;
+static const uint16_t E_OPT4_LEN = 0;
+
+#define E_OPT4_SIZE (4 + E_OPT4_LEN)
+
+enum offsets {
+ /*! \brief Offset of Extended RCODE in wire order of TTL. */
+ OFFSET_ERCODE = 0,
+ /*! \brief Offset of Version in wire order of TTL. */
+ OFFSET_VER = 1,
+ /*! \brief Offset of Flags in wire order of TTL. */
+ OFFSET_FLAGS = 2,
+ /*! \brief Offset of OPTION code in one OPTION in RDATA. */
+ OFFSET_OPT_CODE = 0,
+ /*! \brief Offset of OPTION size in one OPTION in RDATA. */
+ OFFSET_OPT_SIZE = 2,
+ /*! \brief Offset of OPTION data in one OPTION in RDATA. */
+ OFFSET_OPT_DATA = 4
+};
+
+static const uint16_t DO_FLAG = (uint16_t)1 << 15;
+
+static void check_ttl(knot_rrset_t *rrset, uint8_t ext_rcode, uint8_t ver,
+ uint16_t flags, char *msg)
+{
+ if (rrset == NULL) {
+ return;
+ }
+
+ /* TTL should be stored in machine byte order.
+ We need network byte order to compare its parts. */
+ uint8_t ttl_wire[4] = { 0, 0, 0, 0 };
+ knot_wire_write_u32(ttl_wire, rrset->ttl);
+
+ /* Convert Flags from EDNS parameters to wire format for comparison. */
+ uint8_t flags_wire[2] = { 0, 0 };
+ knot_wire_write_u16(flags_wire, flags);
+
+ /* TTL = Ext RCODE + Version + Flags */
+ bool check = (ttl_wire[OFFSET_ERCODE] == ext_rcode);
+ ok(check, "%s: extended RCODE", msg);
+
+ check = (ttl_wire[OFFSET_VER] == ver);
+ ok(check, "%s: version", msg);
+
+ check = (memcmp(flags_wire, ttl_wire + OFFSET_FLAGS, 2) == 0);
+ ok(check, "%s: flags", msg);
+}
+
+static void check_option(knot_rdata_t *rdata, uint16_t opt_code,
+ uint16_t opt_len, uint8_t *opt_data, char *msg)
+{
+ assert(rdata != NULL);
+
+ uint8_t *data = rdata->data;
+ uint16_t data_len = rdata->len;
+
+ /* Check RDLENGTH according to given data length. */
+ bool check = (data_len >= 4 + opt_len);
+ ok(check, "%s: RDLENGTH (%u)", msg, data_len);
+
+ /* Find the desired option. */
+ bool found = false;
+ int pos = 0;
+ while (pos <= data_len - 4) {
+ uint16_t code = knot_wire_read_u16(data + pos + OFFSET_OPT_CODE);
+ if (code == opt_code) {
+ found = true;
+ break;
+ }
+ uint16_t len = knot_wire_read_u16(data + pos + OFFSET_OPT_SIZE);
+ pos += 4 + len;
+ }
+
+ /* Check that the option is present. */
+ ok(found, "%s: find OPTION %u in OPT RR", msg, opt_code);
+
+ /* Check that the first OPTION's size si the size of the option data. */
+ uint16_t opt_size = knot_wire_read_u16(data + pos + OFFSET_OPT_SIZE);
+ check = (opt_size == opt_len);
+ ok(check, "%s: OPTION data size", msg);
+
+ /* Check the actual NSID data. */
+ check = (opt_data == 0 || memcmp(data + pos + OFFSET_OPT_DATA, opt_data, opt_len) == 0);
+ ok(check, "%s: OPTION data", msg);
+}
+
+static void check_header(knot_rrset_t *opt_rr, uint16_t payload, uint8_t ver,
+ uint16_t flags, uint8_t ext_rcode, char *msg)
+{
+ assert(opt_rr != NULL);
+ bool check;
+
+ /* Check values in OPT RR by hand. */
+ /* CLASS == Max UDP payload */
+ check = (opt_rr->rclass == payload);
+ ok(check, "%s: max payload", msg);
+
+ /* The OPT RR should have exactly one RDATA. */
+ check = (opt_rr->rrs.count == 1);
+ ok(check, "%s: RR count == 1", msg);
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ check = (rdata != NULL);
+ ok(check, "%s: RDATA exists", msg);
+
+ check_ttl(opt_rr, ext_rcode, ver, flags, msg);
+}
+
+static void test_getters(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+
+ /* These values should be set from the setters test:
+ * Max UDP payload: E_MAX_PLD2
+ * Version: E_VERSION2
+ * RCODE: E_RCODE2
+ * Flags: E_FLAGS | KNOT_EDNS_FLAG_DO
+ * OPTIONs: 1) KNOT_EDNS_OPTION_NSID, E_NSID_LEN, E_NSID_STR
+ * 2) E_OPT3_CODE, 0, 0
+ * 3) E_OPT4_CODE, 0, 0
+ * 4) KNOT_EDNS_OPTION_NSID, E_NSID_LEN, E_NSID_STR2
+ */
+
+ /* Payload */
+ bool check = (knot_edns_get_payload(opt_rr) == E_MAX_PLD2);
+ ok(check, "OPT RR getters: payload");
+
+ /* Extended RCODE */
+ check = (knot_edns_get_ext_rcode(opt_rr) == E_RCODE2);
+ ok(check, "OPT RR getters: extended RCODE");
+
+ /* Extended RCODE */
+ check = (knot_edns_get_version(opt_rr) == E_VERSION2);
+ ok(check, "OPT RR getters: version");
+
+ /* DO bit */
+ check = knot_edns_do(opt_rr);
+ ok(check, "OPT RR getters: DO bit check");
+
+ /* Wire size */
+ size_t total_size = KNOT_EDNS_MIN_SIZE
+ + 2 * E_NSID_SIZE + E_OPT3_SIZE + E_OPT4_SIZE;
+ size_t actual_size = knot_edns_wire_size(opt_rr);
+ check = actual_size == total_size;
+ ok(check, "OPT RR getters: wire size (expected: %zu, actual: %zu)",
+ total_size, actual_size);
+
+ /* NSID */
+ uint8_t *nsid1 = knot_edns_get_option(opt_rr, KNOT_EDNS_OPTION_NSID, NULL);
+ check = nsid1 != NULL;
+ ok(check, "OPT RR getters: NSID check");
+ check = memcmp(knot_edns_opt_get_data(nsid1), E_NSID_STR, knot_edns_opt_get_length(nsid1)) == 0;
+ ok(check, "OPT RR getters: NSID value check");
+
+ /* Another NSID */
+ uint8_t *nsid2 = knot_edns_get_option(opt_rr, KNOT_EDNS_OPTION_NSID, nsid1);
+ check = nsid2 != NULL;
+ ok(check, "OPT RR getters: another NSID check");
+ check = memcmp(knot_edns_opt_get_data(nsid2), E_NSID_STR2, knot_edns_opt_get_length(nsid2)) == 0;
+ ok(check, "OPT RR getters: another NSID value check");
+
+ /* Other OPTIONs */
+ check = knot_edns_get_option(opt_rr, E_OPT3_CODE, NULL) != NULL;
+ ok(check, "OPT RR getters: empty option 1");
+
+ check = knot_edns_get_option(opt_rr, E_OPT4_CODE, NULL) != NULL;
+ ok(check, "OPT RR getters: empty option 2");
+
+ uint16_t code = knot_edns_opt_get_code((const uint8_t *)"\x00\x0a" "\x00\x00");
+ ok(code == KNOT_EDNS_OPTION_COOKIE, "OPT RR getters: EDNS OPT code");
+}
+
+static void test_setters(knot_rrset_t *opt_rr)
+{
+ assert(opt_rr != NULL);
+
+ /* Header-related setters. */
+ knot_edns_set_payload(opt_rr, E_MAX_PLD2);
+ knot_edns_set_ext_rcode(opt_rr, E_RCODE2);
+ knot_edns_set_version(opt_rr, E_VERSION2);
+ knot_edns_set_do(opt_rr);
+
+ check_header(opt_rr, E_MAX_PLD2, E_VERSION2, DO_FLAG, E_RCODE2,
+ "OPT RR setters");
+
+ /* OPTION(RDATA)-related setters. */
+
+ /* Proper option NSID. */
+ int ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
+ E_NSID_LEN, (uint8_t *)E_NSID_STR, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add option with data (ret = %s)",
+ knot_strerror(ret));
+
+ /* Wrong argument: no OPT RR. */
+ ret = knot_edns_add_option(NULL, E_OPT3_CODE, E_OPT3_FAKE_LEN,
+ (uint8_t *)E_OPT3_FAKE_DATA, NULL);
+ is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (rr == NULL) "
+ "(ret = %s)", knot_strerror(ret));
+
+ /* Wrong argument: option length != 0 && data == NULL. */
+ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_FAKE_LEN, NULL,
+ NULL);
+ is_int(KNOT_EINVAL, ret, "OPT RR setters: add option (data == NULL, "
+ "len != 0) (ret = %s)", knot_strerror(ret));
+
+ /* Empty OPTION (length 0, data != NULL). */
+ ret = knot_edns_add_option(opt_rr, E_OPT3_CODE, E_OPT3_LEN,
+ (uint8_t *)E_OPT3_FAKE_DATA, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 1 (ret = %s)",
+ knot_strerror(ret));
+
+ /* Empty OPTION (length 0, data == NULL). */
+ ret = knot_edns_add_option(opt_rr, E_OPT4_CODE, E_OPT4_LEN,
+ (uint8_t *)E_OPT4_DATA, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add empty option 2 (ret = %s)",
+ knot_strerror(ret));
+
+ /* Another option NSID. */
+ ret = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_NSID,
+ E_NSID_LEN, (uint8_t *)E_NSID_STR2, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR setters: add option with data (ret = %s)",
+ knot_strerror(ret));
+
+ knot_rdata_t *rdata = opt_rr->rrs.rdata;
+ ok(rdata != NULL, "OPT RR setters: non-empty RDATA");
+
+ /* Check proper option NSID */
+ check_option(rdata, KNOT_EDNS_OPTION_NSID, E_NSID_LEN,
+ (uint8_t *)E_NSID_STR, "OPT RR setters (proper option)");
+
+ /* Check empty option 1 */
+ check_option(rdata, E_OPT3_CODE, E_OPT3_LEN,
+ (uint8_t *)E_OPT3_DATA, "OPT RR setters (empty option 1)");
+
+ /* Check empty option 2 */
+ check_option(rdata, E_OPT4_CODE, E_OPT4_LEN,
+ (uint8_t *)E_OPT4_DATA, "OPT RR setters (empty option 2)");
+}
+
+static void test_alignment(void)
+{
+ int ret;
+
+ ret = knot_edns_alignment_size(1, 1, 1);
+ ok(ret == -1, "no alignment");
+
+ ret = knot_edns_alignment_size(1, 1, 2);
+ ok(ret == -1, "no alignment");
+
+ ret = knot_edns_alignment_size(1, 1, 3);
+ ok(ret == (6 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+
+ ret = knot_edns_alignment_size(1, 1, 4);
+ ok(ret == (8 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+
+ ret = knot_edns_alignment_size(1, 1, 512);
+ ok(ret == (512 - (1 + 1 + KNOT_EDNS_OPTION_HDRLEN)), "%i-Byte alignment", ret);
+}
+
+static void test_keepalive(void)
+{
+ typedef struct {
+ char *msg;
+ uint16_t opt_len;
+ char *opt;
+ uint16_t val;
+ } test_t;
+
+ // OK tests.
+
+ static const test_t TESTS[] = {
+ { "ok 0", 0, "", 0 },
+ { "ok 1", 2, "\x00\x01", 1 },
+ { "ok 258", 2, "\x01\x02", 258 },
+ { "ok 65535", 2, "\xFF\xFF", 65535 },
+ { NULL }
+ };
+
+ for (const test_t *t = TESTS; t->msg != NULL; t++) {
+ uint16_t len = knot_edns_keepalive_size(t->val);
+ ok(len == t->opt_len, "%s: %s, size", __func__, t->msg);
+
+ uint8_t wire[8] = { 0 };
+ int ret = knot_edns_keepalive_write(wire, sizeof(wire), t->val);
+ is_int(KNOT_EOK, ret, "%s: %s, write, return", __func__, t->msg);
+ ok(memcmp(wire, t->opt, t->opt_len) == 0, "%s: %s, write, value",
+ __func__, t->msg);
+
+ uint16_t timeout = 0;
+ ret = knot_edns_keepalive_parse(&timeout, (uint8_t *)t->opt, t->opt_len);
+ is_int(KNOT_EOK, ret, "%s: %s, parse, return", __func__, t->msg);
+ ok(timeout == t->val, "%s: %s, parse, value", __func__, t->msg);
+ }
+
+ // Error tests.
+
+ uint8_t wire[8] = { 0 };
+ ok(knot_edns_keepalive_write(NULL, 0, 0) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_keepalive_write(wire, 1, 1) == KNOT_ESPACE,
+ "%s: write, no room", __func__);
+
+ uint16_t timeout = 0;
+ ok(knot_edns_keepalive_parse(NULL, (const uint8_t *)"", 0) == KNOT_EINVAL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_keepalive_parse(&timeout, NULL, 0) == KNOT_EINVAL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_keepalive_parse(&timeout, (const uint8_t *)"\x01", 1) == KNOT_EMALF,
+ "%s: parse, malformed", __func__);
+}
+
+static void test_chain(void)
+{
+ typedef struct {
+ char *msg;
+ uint16_t opt_len;
+ knot_dname_t *dname;
+ } test_t;
+
+ // OK tests.
+
+ static const test_t TESTS[] = {
+ { ".", 1, (knot_dname_t *)"" },
+ { "a.", 3, (knot_dname_t *)"\x01" "a" },
+ { NULL }
+ };
+
+ for (const test_t *t = TESTS; t->msg != NULL; t++) {
+ uint16_t len = knot_edns_chain_size(t->dname);
+ ok(len == t->opt_len, "%s: dname %s, size", __func__, t->msg);
+
+ uint8_t wire[8] = { 0 };
+ int ret = knot_edns_chain_write(wire, sizeof(wire), t->dname);
+ is_int(KNOT_EOK, ret, "%s: dname %s, write, return", __func__, t->msg);
+ ok(memcmp(wire, t->dname, t->opt_len) == 0, "%s: dname %s, write, value",
+ __func__, t->msg);
+
+ knot_dname_t *dname = NULL;
+ ret = knot_edns_chain_parse(&dname, (uint8_t *)t->dname, t->opt_len, NULL);
+ is_int(KNOT_EOK, ret, "%s: dname %s, parse, return", __func__, t->msg);
+ ok(knot_dname_is_equal(dname, t->dname), "%s: dname %s, parse, value",
+ __func__, t->msg);
+ knot_dname_free(dname, NULL);
+ }
+
+ // Error tests.
+
+ ok(knot_edns_chain_size(NULL) == 0, "%s: size, NULL", __func__);
+
+ uint8_t wire[8] = { 0 };
+ ok(knot_edns_chain_write(NULL, 0, wire) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_chain_write(wire, 0, NULL) == KNOT_EINVAL,
+ "%s: write, NULL", __func__);
+ ok(knot_edns_chain_write(wire, 0, (const knot_dname_t *)"") == KNOT_ESPACE,
+ "%s: write, no room", __func__);
+
+ knot_dname_t *dname = NULL;
+ ok(knot_edns_chain_parse(NULL, wire, 0, NULL) == KNOT_EINVAL && dname == NULL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_chain_parse(&dname, NULL, 0, NULL) == KNOT_EINVAL && dname == NULL,
+ "%s: parse, NULL", __func__);
+ ok(knot_edns_chain_parse(&dname, (const uint8_t *)"\x01", 1, NULL) == KNOT_EMALF &&
+ dname == NULL, "%s: parse, malformed", __func__);
+}
+
+static void check_cookie_parse(const char *opt, knot_edns_cookie_t *cc,
+ knot_edns_cookie_t *sc, int code, const char *msg)
+{
+ const uint8_t *data = NULL;
+ uint16_t data_len = 0;
+ if (opt != NULL) {
+ data = knot_edns_opt_get_data((uint8_t *)opt);
+ data_len = knot_edns_opt_get_length((uint8_t *)opt);
+ }
+
+ int ret = knot_edns_cookie_parse(cc, sc, data, data_len);
+ is_int(code, ret, "cookie parse ret: %s", msg);
+}
+
+static void ok_cookie_check(const char *opt, knot_edns_cookie_t *cc,
+ knot_edns_cookie_t *sc, uint16_t cc_len, uint16_t sc_len,
+ const char *msg)
+{
+ check_cookie_parse(opt, cc, sc, KNOT_EOK, msg);
+
+ is_int(cc->len, cc_len, "cookie parse cc len: %s", msg);
+ is_int(sc->len, sc_len, "cookie parse cc len: %s", msg);
+
+ uint16_t size = knot_edns_cookie_size(cc, sc);
+ is_int(size, cc_len + sc_len, "cookie len: %s", msg);
+
+ uint8_t buf[64];
+ int ret = knot_edns_cookie_write(buf, sizeof(buf), cc, sc);
+ is_int(KNOT_EOK, ret, "cookie write ret: %s", msg);
+}
+
+static void test_cookie(void)
+{
+ const char *good[] = {
+ "\x00\x0a" "\x00\x08" "\x00\x01\x02\x03\x04\x05\x06\x07", /* Only client cookie. */
+ "\x00\x0a" "\x00\x10" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", /* 8 octets long server cookie. */
+ "\x00\x0a" "\x00\x28" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" /* 32 octets long server cookie. */
+ };
+
+ const char *bad[] = {
+ "\x00\x0a" "\x00\x00", /* Zero length cookie. */
+ "\x00\x0a" "\x00\x01" "\x00", /* Short client cookie. */
+ "\x00\x0a" "\x00\x07" "\x00\x01\x02\x03\x04\x05\x06", /* Short client cookie. */
+ "\x00\x0a" "\x00\x09" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08", /* Short server cookie. */
+ "\x00\x0a" "\x00\x0f" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e", /* Short server cookie. */
+ "\x00\x0a" "\x00\x29" "\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28", /* Long server cookie. */
+ };
+
+ knot_edns_cookie_t cc, sc;
+
+ ok_cookie_check(good[0], &cc, &sc, 8, 0, "good cookie 0");
+ ok_cookie_check(good[1], &cc, &sc, 8, 8, "good cookie 1");
+ ok_cookie_check(good[2], &cc, &sc, 8, 32, "good cookie 2");
+
+ check_cookie_parse(NULL, &cc, &sc, KNOT_EINVAL, "no data");
+ check_cookie_parse(good[0], NULL, &sc, KNOT_EINVAL, "no client cookie");
+ check_cookie_parse(good[1], &cc, NULL, KNOT_EINVAL, "no server cookie");
+
+ check_cookie_parse(bad[0], &cc, &sc, KNOT_EMALF, "bad cookie 0");
+ check_cookie_parse(bad[1], &cc, &sc, KNOT_EMALF, "bad cookie 1");
+ check_cookie_parse(bad[2], &cc, &sc, KNOT_EMALF, "bad cookie 2");
+ check_cookie_parse(bad[3], &cc, &sc, KNOT_EMALF, "bad cookie 3");
+ check_cookie_parse(bad[4], &cc, &sc, KNOT_EMALF, "bad cookie 4");
+ check_cookie_parse(bad[5], &cc, &sc, KNOT_EMALF, "bad cookie 5");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_rrset_t opt_rr;
+ int ret = knot_edns_init(&opt_rr, E_MAX_PLD, E_RCODE, E_VERSION, NULL);
+ is_int(KNOT_EOK, ret, "OPT RR: init");
+
+ /* Check initialized values (no NSID yet). */
+ check_header(&opt_rr, E_MAX_PLD, E_VERSION, 0, E_RCODE, "OPT RR: check header");
+
+ test_setters(&opt_rr);
+ test_getters(&opt_rr);
+ test_alignment();
+ test_keepalive();
+ test_chain();
+ test_cookie();
+
+ knot_rrset_clear(&opt_rr, NULL);
+
+ return 0;
+}
diff --git a/tests/libknot/test_edns_ecs.c b/tests/libknot/test_edns_ecs.c
new file mode 100644
index 0000000..9397d84
--- /dev/null
+++ b/tests/libknot/test_edns_ecs.c
@@ -0,0 +1,271 @@
+/* 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 <assert.h>
+#include <tap/basic.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/errcode.h"
+#include "libknot/rrtype/opt.h"
+
+#define GARBAGE_BYTE 0xdb
+
+static void test_size(void)
+{
+ struct test {
+ const char *msg;
+ size_t expected;
+ knot_edns_client_subnet_t ecs;
+ };
+
+ static struct test const TESTS[] = {
+ // invalid
+ { "zero family", 0, { 0 } },
+ { "zero family & source", 0, { 0, 1 } },
+ { "unknown family", 0, { 42, 0 } },
+ { "unknown family & source", 0, { 42, 1 } },
+ // IPv4 bit ops
+ { "IPv4, zero source", 4, { 1 } },
+ { "IPv4, 7 bits in last byte", 7, { 1, 23 } },
+ { "IPv4, 8 bits in last byte", 7, { 1, 24 } },
+ { "IPv4, 1 bit in last byte", 8, { 1, 25 } },
+ // IPv6 bit ops
+ { "IPv6, zero source", 4, { 2 } },
+ { "IPv6, 7 bits in last byte", 19, { 2, 113 } },
+ { "IPv6, 8 bits in last byte", 19, { 2, 120 } },
+ { "IPv6, 1 bit in last byte", 20, { 2, 121 } },
+ // sources
+ { "IPv4, source < max", 8, { 1, 31 } },
+ { "IPv4, source = max", 8, { 1, 32 } },
+ { "IPv4, source > max", 0, { 1, 33 } },
+ // scopes
+ { "IPv6, scope < source", 12, { 2, 64, 48 } },
+ { "IPv6, scope = source", 20, { 2, 128, 128 } },
+ { "IPv6, scope > max", 0, { 2, 128, 129 } },
+ { NULL }
+ };
+
+ is_int(0, knot_edns_client_subnet_size(NULL), "%s: null", __func__);
+
+ for (struct test const *t = TESTS; t->msg != NULL; t++) {
+ int r = knot_edns_client_subnet_size(&t->ecs);
+ is_int(t->expected, r, "%s: %s", __func__, t->msg);
+ }
+}
+
+struct test_io {
+ const char *msg;
+ int expected;
+ size_t option_len;
+ const char *option;
+ knot_edns_client_subnet_t ecs;
+};
+
+static void test_write(void)
+{
+ static struct test_io const TESTS[] = {
+ // invalid
+ { "unset family", KNOT_EINVAL, 0, NULL, { 0 } },
+ { "invalid family", KNOT_EINVAL, 0, NULL, { 3 } },
+ { "small buffer", KNOT_ESPACE, 4, NULL, { 1, 1 } },
+ // IPv4 prefix
+ { "IPv4, zero source", KNOT_EOK, 4, "\x00\x01\x00\x00", { 1 } },
+ { "IPv4, 7 bits in LSB", KNOT_EOK, 6, "\x00\x01\x0f\x00\xff\xfe", { 1, 15, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, 8 bits in LSB", KNOT_EOK, 6, "\x00\x01\x10\x00\xff\xff", { 1, 16, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, 7, "\x00\x01\x11\x00\xff\xff\x80", { 1, 17, 0, "\xff\xff\xff\xff" } },
+ { "IPv4, source = max", KNOT_EOK, 8, "\x00\x01\x20\x00\xaa\xbb\xcc\xdd", { 1, 32, 0, "\xaa\xbb\xcc\xdd" } },
+ { "IPv6, source > max", KNOT_EINVAL, 0, NULL, { 2, 129 } },
+ // IPv6 scope
+ { "IPv6, scope < source", KNOT_EOK, 6, "\x00\x02\x10\x0e\xff\xff", { 2, 16, 14, "\xff\xff\xff\xff" } },
+ { "IPv6, scope = source", KNOT_EOK, 6, "\x00\x02\x08\x08\xff", { 2, 8, 8, "\xff\xff\xff\xff" } },
+ { "IPv6, scope > max", KNOT_EINVAL, 0, NULL, { 2, 128, 129 } },
+ // other
+ { "larger buffer", KNOT_EOK, 7, "\x00\x01\x10\x0e\xff\xff\x00", { 1, 16, 14, "\xff\xff\xff\xff" } },
+ { NULL }
+ };
+
+ for (struct test_io const *t = TESTS; t->msg != NULL; t++) {
+ uint8_t option[64];
+ assert(sizeof(option) >= t->option_len);
+ memset(option, GARBAGE_BYTE, sizeof(option));
+
+ int r = knot_edns_client_subnet_write(option, t->option_len, &t->ecs);
+ ok(r == t->expected &&
+ (t->expected != KNOT_EOK || memcmp(option, t->option, t->option_len) == 0),
+ "%s: %s", __func__, t->msg);
+ }
+}
+
+static void test_parse(void)
+{
+ static struct test_io const TESTS[] = {
+ // invalid
+ { "null", KNOT_EINVAL, 0, NULL },
+ { "empty buffer", KNOT_EMALF, 0, "" },
+ { "incomplete header", KNOT_EMALF, 3, "\x00\x01\x00" },
+ { "incomplete source", KNOT_EMALF, 5, "\x00\x0a\x00\x00\xff\xff" },
+ { "zero family", KNOT_EMALF, 4, "\x00\x00\x00\x00" },
+ { "unknown family", KNOT_EMALF, 4, "\x00\x03\x00\x00" },
+ // IPv4 prefix
+ { "IPv4, zero source", KNOT_EOK, 4, "\x00\x01\x00\x00", { 1 } },
+ { "IPv4, 7 bits in LSB", KNOT_EOK, 6, "\x00\x01\x0f\x00\xff\xfe", { 1, 15, 0, "\xff\xfe" } },
+ { "IPv4, 9 bits in LSB", KNOT_EOK, 6, "\x00\x01\x10\x00\xff\xff", { 1, 16, 0, "\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, 7, "\x00\x01\x11\x00\xff\xff\x80", { 1, 17, 0, "\xff\xff\x80" } },
+ { "IPv4, source = max", KNOT_EOK, 8, "\x00\x01\x20\x00\xaa\xbb\xcc\xdd", { 1, 32, 0, "\xaa\xbb\xcc\xdd" } },
+ { "IPv4, dirty source", KNOT_EOK, 8, "\x00\x01\x0b\x00\xff\xff\xff\xff", { 1, 11, 0, "\xff\xe0" } },
+ { "IPv4, source > max", KNOT_EMALF, 9, "\x00\x01\x21\x00\xaa\xbb\xcc\xdd\xee" },
+ // IPv6 scope
+ { "IPv6 scope < source", KNOT_EOK, 5, "\x00\x02\x07\x05\xff", { 2, 7, 5, "\xfe" } },
+ { "IPv6 scope = source", KNOT_EOK, 5, "\x00\x02\x06\x06\xff", { 2, 6, 6, "\xfc" } },
+ { "IPv6 scope > max", KNOT_EMALF, 5, "\x00\x02\x06\x81\xff" },
+ // extra buffer size
+ { "extra space", KNOT_EOK, 6, "\x00\x01\x00\x00\xff\x00", { 1 } },
+ { "extra space", KNOT_EOK, 6, "\x00\x01\x01\x00\xff\x00", { 1, 1, 0, "\x80" } },
+ { NULL }
+ };
+
+ for (struct test_io const *t = TESTS; t->msg != NULL; t++) {
+ knot_edns_client_subnet_t ecs = { 0 };
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+
+ int r = knot_edns_client_subnet_parse(&ecs, (uint8_t *)t->option, t->option_len);
+ ok(r == t->expected &&
+ (t->expected != KNOT_EOK || memcmp(&ecs, &t->ecs, sizeof(ecs)) == 0),
+ "%s: %s", __func__, t->msg);
+ }
+}
+
+static struct sockaddr_storage addr_init(const char *addr)
+{
+ struct sockaddr_storage sa = { 0 };
+
+ struct addrinfo hints = { .ai_flags = AI_NUMERICHOST };
+ struct addrinfo *info = NULL;
+ int r = getaddrinfo(addr, NULL, &hints, &info);
+ (void)r;
+ assert(r == 0);
+ memcpy(&sa, info->ai_addr, info->ai_addrlen);
+ freeaddrinfo(info);
+
+ return sa;
+}
+
+static void test_set_address(void)
+{
+ int r;
+ knot_edns_client_subnet_t ecs = { 0 };
+ struct sockaddr_storage ss = { 0 };
+
+ r = knot_edns_client_subnet_set_addr(NULL, &ss);
+ is_int(KNOT_EINVAL, r, "%s: missing ECS", __func__);
+
+ r = knot_edns_client_subnet_set_addr(&ecs, NULL);
+ is_int(KNOT_EINVAL, r, "%s: missing address", __func__);
+
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+ ss = addr_init("198.51.100.42");
+ assert(ss.ss_family == AF_INET);
+ const uint8_t raw4[4] = { 198, 51, 100, 42 };
+
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss);
+ ok(r == KNOT_EOK &&
+ ecs.family == 1 && ecs.source_len == 32 && ecs.scope_len == 0 &&
+ memcmp(ecs.address, raw4, sizeof(raw4)) == 0,
+ "%s: IPv4", __func__);
+
+ memset(&ecs, GARBAGE_BYTE, sizeof(ecs));
+ ss = addr_init("2001:db8::dead:beef");
+ assert(ss.ss_family == AF_INET6);
+ const uint8_t raw6[16] = "\x20\x01\x0d\xb8\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\xde\xad\xbe\xef";
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss);
+ ok(r == KNOT_EOK &&
+ ecs.family == 2 && ecs.source_len == 128 && ecs.scope_len == 0 &&
+ memcmp(ecs.address, raw6, sizeof(raw6)) == 0,
+ "%s: IPv6", __func__);
+
+ const struct sockaddr_storage ss_unix = { .ss_family = AF_UNIX };
+ r = knot_edns_client_subnet_set_addr(&ecs, &ss_unix);
+ is_int(KNOT_ENOTSUP, r, "%s: UNIX not supported", __func__);
+}
+
+static bool sockaddr_eq(const struct sockaddr_storage *a, const struct sockaddr_storage *b)
+{
+ return sockaddr_cmp(a, b, true) == 0;
+}
+
+static void test_get_address(void)
+{
+ struct test {
+ const char *msg;
+ int expected;
+ const char *addr_str;
+ knot_edns_client_subnet_t ecs;
+ };
+
+ static struct test const TESTS[] = {
+ // invalid
+ { "unset family", KNOT_ENOTSUP, NULL, { 0 } },
+ { "unknown family", KNOT_ENOTSUP, NULL, { 3 } },
+ // zero source
+ { "IPv4, any", KNOT_EOK, "0.0.0.0", { 1 } },
+ { "IPv6, any", KNOT_EOK, "::0" , { 2 } },
+ // IPv4
+ { "IPv4, 7 bits in LSB", KNOT_EOK, "198.50.0.0", { 1, 15, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, 8 bits in LSB", KNOT_EOK, "198.51.0.0", { 1, 16, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, 1 bit in LSB", KNOT_EOK, "198.51.128.0", { 1, 17, 0, "\xc6\x33\xff\xff" } },
+ { "IPv4, source = max", KNOT_EOK, "198.51.128.1", { 1, 32, 0, "\xc6\x33\x80\x01" } },
+ // IPv6
+ { "IPv6, 7 bits in LSB", KNOT_EOK, "2001:db8:200::", { 2, 39, 0, "\x20\x01\x0d\xb8\x03\xff" } },
+ { "IPv6, 8 bits in LSB", KNOT_EOK, "2001:db8:100::", { 2, 40, 0, "\x20\x01\x0d\xb8\x01\xff" } },
+ { "IPv6, 1 bit in LSB", KNOT_EOK, "2001:db8:180::", { 2, 41, 0, "\x20\x01\x0d\xb8\x01\xff" } },
+ { "IPv6, source = max", KNOT_EOK, "2001:db8::1", { 2, 128, 0, "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" } },
+ { NULL }
+ };
+
+ for (struct test const *t = TESTS; t->msg != NULL; t++) {
+ struct sockaddr_storage result = { 0 };
+ int r = knot_edns_client_subnet_get_addr(&result, &t->ecs);
+ bool valid = false;
+
+ if (t->expected == KNOT_EOK) {
+ struct sockaddr_storage addr = addr_init(t->addr_str);
+ assert(addr.ss_family != AF_UNSPEC);
+ valid = (r == t->expected && sockaddr_eq(&result, &addr));
+ } else {
+ valid = (r == t->expected);
+ }
+
+ ok(valid, "%s: %s", __func__, t->msg);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_size();
+ test_write();
+ test_parse();
+ test_set_address();
+ test_get_address();
+
+ return 0;
+}
diff --git a/tests/libknot/test_endian.c b/tests/libknot/test_endian.c
new file mode 100644
index 0000000..30f16f7
--- /dev/null
+++ b/tests/libknot/test_endian.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2018 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/endian.h"
+
+int main(int argc, char *argv[])
+{
+ plan(12);
+
+ typedef union {
+ uint16_t value;
+ uint8_t array[2];
+ } trafo16_t;
+
+ const uint16_t host16 = 0x0102;
+ const trafo16_t be16 = { .array = { 0x01, 0x02 } };
+ const trafo16_t le16 = { .array = { 0x02, 0x01 } };
+ ok(htobe16(host16) == be16.value, "htobe16");
+ ok(htole16(host16) == le16.value, "htole16");
+ ok(be16toh(be16.value) == host16, "be16toh");
+ ok(le16toh(le16.value) == host16, "le16toh");
+
+ typedef union {
+ uint32_t value;
+ uint8_t array[4];
+ } trafo32_t;
+
+ const uint32_t host32 = 0x01020304;
+ const trafo32_t be32 = { .array = { 0x01, 0x02, 0x03, 0x04 } };
+ const trafo32_t le32 = { .array = { 0x04, 0x03, 0x02, 0x01 } };
+ ok(htobe32(host32) == be32.value, "htobe32");
+ ok(htole32(host32) == le32.value, "htole32");
+ ok(be32toh(be32.value) == host32, "be32toh");
+ ok(le32toh(le32.value) == host32, "le32toh");
+
+ typedef union {
+ uint64_t value;
+ uint8_t array[8];
+ } trafo64_t;
+
+ const uint64_t host64 = 0x0102030405060708;
+ const trafo64_t be64 = { .array = { 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08 } };
+ const trafo64_t le64 = { .array = { 0x08, 0x07, 0x06, 0x05,
+ 0x04, 0x03, 0x02, 0x01 } };
+ ok(htobe64(host64) == be64.value, "htobe64");
+ ok(htole64(host64) == le64.value, "htole64");
+ ok(be64toh(be64.value) == host64, "be64toh");
+ ok(le64toh(le64.value) == host64, "le64toh");
+
+ return 0;
+}
diff --git a/tests/libknot/test_lookup.c b/tests/libknot/test_lookup.c
new file mode 100644
index 0000000..3f7b514
--- /dev/null
+++ b/tests/libknot/test_lookup.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2014 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 <tap/basic.h>
+
+#include "libknot/lookup.h"
+
+const knot_lookup_t test_table[] = {
+ { 0, "test item 0" },
+ { 10, "" },
+ { 2, "test item 2" },
+ { -1, "test item -1" },
+ { 0, NULL }
+};
+
+int main(int argc, char *argv[])
+{
+ plan(9);
+
+ /* Lookup by ID. */
+ const knot_lookup_t *found = knot_lookup_by_id(test_table, 3);
+ ok(found == NULL, "lookup table: find by id - non-existent ID");
+
+ found = knot_lookup_by_id(test_table, 2);
+ ok(found && found->id == 2 && strcmp(found->name, "test item 2") == 0,
+ "lookup table: find by id - ID 2 (unordered IDs)");
+
+ found = knot_lookup_by_id(NULL, 2);
+ ok(found == NULL, "lookup table: find by id - table == NULL");
+
+ /* Lookup by name. */
+ found = knot_lookup_by_name(test_table, "test item 2");
+ ok(found && found->id == 2 && strcmp(found->name, "test item 2") == 0,
+ "lookup table: find by name - existent");
+
+ found = knot_lookup_by_name(test_table, "");
+ ok(found && found->id == 10 && strcmp(found->name, "") == 0,
+ "lookup table: find by name - empty string");
+
+ found = knot_lookup_by_name(test_table, NULL);
+ ok(found == NULL, "lookup table: find by name - NULL name");
+
+ found = knot_lookup_by_name(NULL, "test item 2");
+ ok(found == NULL, "lookup table: find by name - NULL table");
+
+ found = knot_lookup_by_name(NULL, NULL);
+ ok(found == NULL, "lookup table: find by name - NULL table & NULL name");
+
+ found = knot_lookup_by_name(test_table, "non existent name");
+ ok(found == NULL, "lookup table: find by name - non-existent name");
+
+ return 0;
+}
diff --git a/tests/libknot/test_pkt.c b/tests/libknot/test_pkt.c
new file mode 100644
index 0000000..fe31d3c
--- /dev/null
+++ b/tests/libknot/test_pkt.c
@@ -0,0 +1,199 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "libknot/libknot.h"
+#include "libknot/packet/pkt.c"
+#include "contrib/ucw/mempool.h"
+
+#define TTL 7200
+#define NAMECOUNT 3
+#define DATACOUNT 3
+const char *g_names[NAMECOUNT] = {
+ "example.com",
+ "ns1.example.com",
+ "ns2.example.com"
+};
+
+const char *g_rdata[DATACOUNT] = {
+ "\x04" "\xc2\x0c\x00\x01", /* 4B, 194.0.12.1" */
+ "\x11" "\x03""ns1""\x07""example""\x03""com""\x00", /* domain name */
+ "\x11" "\x03""ns2""\x07""example""\x03""com""\x00", /* domain name */
+};
+
+#define RDVAL(i) ((const uint8_t*)(g_rdata[(i)] + 1))
+#define RDLEN(i) ((uint16_t)(g_rdata[(i)][0]))
+
+/* @note Packet equivalence test, 5 checks. */
+static void packet_match(knot_pkt_t *in, knot_pkt_t *out)
+{
+ assert(in);
+ assert(out);
+
+ /* Check counts */
+ is_int(knot_wire_get_qdcount(out->wire),
+ knot_wire_get_qdcount(in->wire), "pkt: QD match");
+ is_int(knot_wire_get_ancount(out->wire),
+ knot_wire_get_ancount(in->wire), "pkt: AN match");
+ is_int(knot_wire_get_nscount(out->wire),
+ knot_wire_get_nscount(in->wire), "pkt: NS match");
+ is_int(knot_wire_get_arcount(out->wire),
+ knot_wire_get_arcount(in->wire), "pkt: AR match");
+
+ /* Check RRs */
+ int rr_matched = 0;
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ if (knot_rrset_equal(&out->rr[i], &in->rr[i], true) > 0) {
+ ++rr_matched;
+ }
+ }
+ is_int(NAMECOUNT, rr_matched, "pkt: RR content match");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Create memory pool context. */
+ int ret = 0;
+ knot_mm_t mm;
+ mm_ctx_mempool(&mm, MM_DEFAULT_BLKSIZE);
+
+ /* Create names and data. */
+ knot_dname_t* dnames[NAMECOUNT] = {0};
+ knot_rrset_t* rrsets[NAMECOUNT] = {0};
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ dnames[i] = knot_dname_from_str_alloc(g_names[i]);
+ }
+
+ uint8_t *edns_str = (uint8_t *)"ab";
+ /* Create OPT RR. */
+ knot_rrset_t opt_rr = { 0 };
+ ret = knot_edns_init(&opt_rr, 1024, 0, 0, &mm);
+ is_int(KNOT_EOK, ret, "initialize OPT RR");
+
+ /* Add NSID */
+ ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID,
+ strlen((char *)edns_str), edns_str, &mm);
+ is_int(KNOT_EOK, ret, "initialize NSID in OPT RR");
+
+ /*
+ * Packet writer tests.
+ */
+
+ /* Create packet. */
+ knot_pkt_t *out = knot_pkt_new(NULL, MM_DEFAULT_BLKSIZE, &mm);
+ ok(out != NULL, "pkt: new");
+ assert(out);
+
+ /* Mark as response (not part of the test). */
+ knot_wire_set_qr(out->wire);
+
+ /* Secure packet. */
+ const char *tsig_secret = "abcd";
+ knot_tsig_key_t tsig_key;
+ tsig_key.algorithm = DNSSEC_TSIG_HMAC_MD5;
+ tsig_key.name = dnames[0];
+ tsig_key.secret.data = (uint8_t *)strdup(tsig_secret);
+ tsig_key.secret.size = strlen(tsig_secret);
+ ret = knot_pkt_reserve(out, knot_tsig_wire_size(&tsig_key));
+ is_int(KNOT_EOK, ret, "pkt: set TSIG key");
+
+ /* Write question. */
+ ret = knot_pkt_put_question(out, dnames[0], KNOT_CLASS_IN, KNOT_RRTYPE_A);
+ is_int(KNOT_EOK, ret, "pkt: put question");
+
+ /* Add OPT to packet (empty NSID). */
+ ret = knot_pkt_reserve(out, knot_edns_wire_size(&opt_rr));
+ is_int(KNOT_EOK, ret, "pkt: reserve OPT RR");
+
+ /* Begin ANSWER section. */
+ ret = knot_pkt_begin(out, KNOT_ANSWER);
+ is_int(KNOT_EOK, ret, "pkt: begin ANSWER");
+
+ /* Write ANSWER section. */
+ rrsets[0] = knot_rrset_new(dnames[0], KNOT_RRTYPE_A, KNOT_CLASS_IN, TTL, NULL);
+ knot_dname_free(dnames[0], NULL);
+ knot_rrset_add_rdata(rrsets[0], RDVAL(0), RDLEN(0), NULL);
+ ret = knot_pkt_put(out, KNOT_COMPR_HINT_QNAME, rrsets[0], 0);
+ is_int(KNOT_EOK, ret, "pkt: write ANSWER");
+
+ /* Begin AUTHORITY. */
+ ret = knot_pkt_begin(out, KNOT_AUTHORITY);
+ is_int(KNOT_EOK, ret, "pkt: begin AUTHORITY");
+
+ /* Write rest to AUTHORITY. */
+ ret = KNOT_EOK;
+ for (unsigned i = 1; i < NAMECOUNT; ++i) {
+ rrsets[i] = knot_rrset_new(dnames[i], KNOT_RRTYPE_NS, KNOT_CLASS_IN, TTL, NULL);
+ knot_dname_free(dnames[i], NULL);
+ knot_rrset_add_rdata(rrsets[i], RDVAL(i), RDLEN(i), NULL);
+ ret |= knot_pkt_put(out, KNOT_COMPR_HINT_NONE, rrsets[i], 0);
+ }
+ is_int(KNOT_EOK, ret, "pkt: write AUTHORITY(%u)", NAMECOUNT - 1);
+
+ /* Begin ADDITIONALS */
+ ret = knot_pkt_begin(out, KNOT_ADDITIONAL);
+ is_int(KNOT_EOK, ret, "pkt: begin ADDITIONALS");
+
+ /* Encode OPT RR. */
+ ret = knot_pkt_put(out, KNOT_COMPR_HINT_NONE, &opt_rr, 0);
+ is_int(KNOT_EOK, ret, "pkt: write OPT RR");
+
+ /*
+ * Packet reader tests.
+ */
+
+ /* Create new packet from query packet. */
+ knot_pkt_t *in = knot_pkt_new(out->wire, out->size, &out->mm);
+ ok(in != NULL, "pkt: create packet for parsing");
+
+ /* Read packet header. */
+ ret = knot_pkt_parse_question(in);
+ is_int(KNOT_EOK, ret, "pkt: read header");
+
+ /* Read packet payload. */
+ ret = parse_payload(in, 0);
+ is_int(KNOT_EOK, ret, "pkt: read payload");
+
+ /* Compare parsed packet to written packet. */
+ packet_match(in, out);
+
+ /*
+ * Copied packet tests.
+ */
+ knot_pkt_t *copy = knot_pkt_new(NULL, in->max_size, &in->mm);
+ ret = knot_pkt_copy(copy, in);
+ is_int(KNOT_EOK, ret, "pkt: create packet copy");
+
+ /* Compare copied packet to original. */
+ packet_match(in, copy);
+
+ /* Free packets. */
+ knot_pkt_free(copy);
+ knot_pkt_free(out);
+ knot_pkt_free(in);
+
+ /* Free extra data. */
+ for (unsigned i = 0; i < NAMECOUNT; ++i) {
+ knot_rrset_free(rrsets[i], NULL);
+ }
+ free(tsig_key.secret.data);
+ mp_delete((struct mempool *)mm.ctx);
+
+ return 0;
+}
diff --git a/tests/libknot/test_probe.c b/tests/libknot/test_probe.c
new file mode 100644
index 0000000..37437f4
--- /dev/null
+++ b/tests/libknot/test_probe.c
@@ -0,0 +1,96 @@
+/* Copyright (C) 2021 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 <tap/basic.h>
+#include <tap/files.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "contrib/sockaddr.h"
+#include "libknot/packet/pkt.c"
+#include "libknot/probe/probe.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ knot_probe_t *probe_out = knot_probe_alloc();
+ ok(probe_out != NULL, "probe: initialize output probe");
+ knot_probe_t *probe_in = knot_probe_alloc();
+ ok(probe_in != NULL, "probe: initialize input probe");
+
+ int fd = knot_probe_fd(probe_out);
+ ok(fd < 0, "probe: unavailable fd");
+
+ char *workdir = test_mkdtemp();
+ ok(workdir != NULL, "probe: create temporary workdir");
+
+ int ret = knot_probe_set_producer(probe_out, workdir, 1);
+ ok(ret == KNOT_ECONN, "probe: connect producer");
+
+ ret = knot_probe_set_consumer(probe_in, workdir, 1);
+ ok(ret == KNOT_EOK, "probe: connect consumer");
+ fd = knot_probe_fd(probe_in);
+ ok(fd >= 0, "probe: get input probe fd");
+
+ ret = knot_probe_set_producer(probe_out, workdir, 1);
+ ok(ret == KNOT_EOK, "probe: reconnect producer");
+ fd = knot_probe_fd(probe_out);
+ ok(fd >= 0, "probe: get output probe fd");
+
+ struct sockaddr_storage addr;
+ ret = sockaddr_set(&addr, AF_INET, "192.168.0.1", 53);
+ ok(ret == KNOT_EOK, "probe: set address");
+
+ knot_pkt_t *query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ ok(query != NULL, "probe: create query");
+ knot_pkt_t *reply = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL);
+ ok(reply != NULL, "probe: create reply");
+
+ ret = knot_pkt_put_question(query, (const uint8_t *)"\x04test\x00",
+ KNOT_CLASS_IN, KNOT_RRTYPE_SOA);
+ ok(ret == KNOT_EOK, "probe: put query");
+
+ knot_probe_data_t data_out;
+ ret = knot_probe_data_set(&data_out, KNOT_PROBE_PROTO_UDP,
+ &addr, &addr, query, reply, KNOT_RCODE_NXDOMAIN);
+ ok(ret == KNOT_EOK, "probe: connect producer");
+
+ knot_pkt_free(query);
+ knot_pkt_free(reply);
+
+ ret = knot_probe_produce(probe_out, &data_out, 1);
+ ok(ret == KNOT_EOK, "probe: produce datagram");
+
+ knot_probe_data_t data_in;
+ ret = knot_probe_consume(probe_in, &data_in, 1, 20);
+ ok(ret == 1, "probe: consume datagram");
+
+ ret = memcmp(&data_in, &data_out, offsetof(knot_probe_data_t, query.qname));
+ ok(ret == 0, "probe: data comparison");
+
+ ret = knot_dname_cmp(data_in.query.qname, data_out.query.qname);
+ ok(ret == 0, "probe: qname comparison");
+
+ knot_probe_free(probe_in);
+ knot_probe_free(probe_out);
+
+ test_rm_rf(workdir);
+ free(workdir);
+
+ return 0;
+}
diff --git a/tests/libknot/test_rdata.c b/tests/libknot/test_rdata.c
new file mode 100644
index 0000000..754cd7f
--- /dev/null
+++ b/tests/libknot/test_rdata.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 2017 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 <stdbool.h>
+#include <tap/basic.h>
+
+#include "libknot/rdata.h"
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test array size
+ ok(knot_rdata_size(1) == 2 + 1 + 1, "rdata: array size odd.");
+ ok(knot_rdata_size(2) == 2 + 2, "rdata: array size even.");
+
+ // Test init
+ const size_t data_size = 16;
+ uint8_t buf1[knot_rdata_size(data_size)];
+ knot_rdata_t *rdata = (knot_rdata_t *)buf1;
+ uint8_t payload[] = "abcdefghijklmnop";
+ knot_rdata_init(rdata, data_size, payload);
+ const bool set_ok = rdata->len == data_size &&
+ memcmp(rdata->data, payload, data_size) == 0;
+ ok(set_ok, "rdata: init.");
+
+ // Test compare
+ rdata->len = data_size;
+ ok(knot_rdata_cmp(rdata, rdata) == 0, "rdata: cmp eq.");
+
+ knot_rdata_t *lower = rdata;
+ uint8_t buf2[knot_rdata_size(data_size)];
+ knot_rdata_t *greater = (knot_rdata_t *)buf2;
+ knot_rdata_init(greater, data_size, (uint8_t *)"qrstuvwxyz123456");
+ ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower.");
+ ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater.");
+
+ // Payloads will be the same.
+ memcpy(greater->data, lower->data, data_size);
+ assert(knot_rdata_cmp(lower, greater) == 0);
+
+ lower->len = data_size - 1;
+ ok(knot_rdata_cmp(lower, greater) < 0, "rdata: cmp lower size.");
+ ok(knot_rdata_cmp(greater, lower) > 0, "rdata: cmp greater size.");
+
+ return 0;
+}
diff --git a/tests/libknot/test_rdataset.c b/tests/libknot/test_rdataset.c
new file mode 100644
index 0000000..d364be3
--- /dev/null
+++ b/tests/libknot/test_rdataset.c
@@ -0,0 +1,227 @@
+/* Copyright (C) 2022 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 <assert.h>
+#include <tap/basic.h>
+#include <string.h>
+
+#include "libknot/rdataset.c"
+#include "libknot/libknot.h"
+
+// Inits rdataset with given rdata.
+#define RDATASET_INIT_WITH(set, rdata) \
+ knot_rdataset_clear(&set, NULL); \
+ ret = knot_rdataset_add(&set, rdata, NULL); \
+ assert(ret == KNOT_EOK);
+
+static size_t rdataset_size(const knot_rdataset_t *rrs)
+{
+ if (rrs == NULL || rrs->count == 0) {
+ return 0;
+ }
+
+ const knot_rdata_t *last = rr_seek(rrs, rrs->count - 1);
+ return (uint8_t *)last + knot_rdata_size(last->len) - (uint8_t *)rrs->rdata;
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test init
+ knot_rdataset_t rdataset;
+ knot_rdataset_init(&rdataset);
+ ok(rdataset.rdata == NULL && rdataset.count == 0 && rdataset.size == 0, "rdataset: init.");
+
+ // Test rdata addition
+ uint8_t buf_gt[knot_rdata_size(4)];
+ knot_rdata_t *rdata_gt = (knot_rdata_t *)buf_gt;
+ knot_rdata_init(rdata_gt, 4, (uint8_t *)"wxyz");
+
+ int ret = knot_rdataset_add(NULL, NULL, NULL);
+ is_int(KNOT_EINVAL, ret, "rdataset: add NULL.");
+ ret = knot_rdataset_add(&rdataset, rdata_gt, NULL);
+ bool add_ok = ret == KNOT_EOK && rdataset.count == 1 &&
+ knot_rdata_cmp(rdata_gt, rdataset.rdata) == 0;
+ ok(add_ok, "rdataset: add.");
+
+ uint8_t buf_lo[knot_rdata_size(4)];
+ knot_rdata_t *rdata_lo = (knot_rdata_t *)buf_lo;
+ knot_rdata_init(rdata_lo, 4, (uint8_t *)"abcd");
+ ret = knot_rdataset_add(&rdataset, rdata_lo, NULL);
+ add_ok = ret == KNOT_EOK && rdataset.count == 2 &&
+ knot_rdata_cmp(rdata_lo, rdataset.rdata) == 0;
+ ok(add_ok, "rdataset: add lower.");
+
+ // Test getters
+ ok(knot_rdata_cmp(knot_rdataset_at(&rdataset, 0), rdata_lo) == 0 &&
+ knot_rdata_cmp(knot_rdataset_at(&rdataset, 1), rdata_gt) == 0,
+ "rdataset: at.");
+
+ ok(rdataset_size(&rdataset) == knot_rdata_size(4) * 2,
+ "rdataset: size.");
+ ok(rdataset.size == rdataset_size(&rdataset), "rdataset: size precomputed (%u %zu).",
+ rdataset.size, rdataset_size(&rdataset));
+
+ // Test copy
+ ok(knot_rdataset_copy(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: copy NULL.");
+ knot_rdataset_t copy;
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ const bool copy_ok = ret == KNOT_EOK && copy.count == rdataset.count &&
+ rdataset_size(&copy) == rdataset_size(&rdataset) &&
+ rdataset.rdata != NULL && copy.rdata != NULL &&
+ memcmp(rdataset.rdata, copy.rdata,
+ rdataset_size(&rdataset)) == 0;
+ ok(copy_ok, "rdataset: copy");
+ ok(copy.size == rdataset_size(&copy), "copy: size precomputed.");
+
+ // Test eq
+ ok(knot_rdataset_eq(&rdataset, &copy), "rdataset: equal");
+
+ // Test clear
+ knot_rdataset_clear(&copy, NULL);
+ ok(copy.count == 0 && copy.rdata == NULL, "rdataset: clear.");
+
+ // Test not equal (different count)
+ ok(!knot_rdataset_eq(&rdataset, &copy), "rdataset: not equal - count");
+
+ // Test member
+ uint8_t buf_not[knot_rdata_size(1)];
+ knot_rdata_t *not_a_member = (knot_rdata_t *)buf_not;
+ knot_rdata_init(not_a_member, 1, (uint8_t *)"?");
+ ok(knot_rdataset_member(&rdataset, rdata_gt), "rdataset: is member.");
+ ok(!knot_rdataset_member(&rdataset, not_a_member), "rdataset: is not member.");
+
+ // Test merge
+ ok(knot_rdataset_merge(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: merge NULL.");
+ knot_rdataset_t empty;
+ knot_rdataset_init(&empty);
+ ret = knot_rdataset_merge(&empty, &rdataset, NULL);
+ bool merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&empty, &rdataset);
+ ok(merge_ok, "rdataset: merge empty.");
+ knot_rdata_t *data_before = rdataset.rdata;
+ ret = knot_rdataset_merge(&rdataset, &rdataset, NULL);
+ merge_ok = ret == KNOT_EOK && rdataset.count == 2 &&
+ data_before == rdataset.rdata;
+ ok(merge_ok, "rdataset: merge self.");
+
+ knot_rdataset_clear(&empty, NULL);
+
+ // Init structs for merge sort testing
+ knot_rdataset_t rdataset_lo; // "Lower" rdataset
+ knot_rdataset_init(&rdataset_lo);
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ knot_rdataset_t rdataset_gt; // "Greater" rdataset
+ knot_rdataset_init(&rdataset_gt);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+
+ // Test not equal - different data
+ ok(!knot_rdataset_eq(&rdataset_gt, &rdataset_lo), "rdataset: data not equal.");
+
+ // Test that merge keeps the sorted order
+ ret = knot_rdataset_merge(&rdataset_lo, &rdataset_gt, NULL);
+ merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_lo, &rdataset);
+ ok(merge_ok, "rdataset: merge into lower.");
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_merge(&rdataset_gt, &rdataset_lo, NULL);
+ merge_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset_gt, &rdataset);
+ ok(merge_ok, "rdataset: merge into greater.");
+
+ // Test intersect
+ ok(knot_rdataset_intersect(NULL, NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: intersect NULL.");
+
+ knot_rdataset_t intersection;
+ ret = knot_rdataset_intersect(&rdataset, &rdataset, &intersection, NULL);
+ bool intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset, &intersection);
+ ok(intersect_ok, "rdataset: intersect self.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_intersect(&rdataset_lo, &rdataset_gt, &intersection, NULL);
+ intersect_ok = ret == KNOT_EOK && intersection.count == 0;
+ ok(intersect_ok, "rdataset: intersect no common.");
+
+ ret = knot_rdataset_intersect(&rdataset, &rdataset_lo, &intersection, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&intersection, &rdataset_lo);
+ ok(intersect_ok, "rdataset: intersect normal.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ // Test intersect2
+ ok(knot_rdataset_intersect2(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: intersect2 NULL.");
+
+ ret = knot_rdataset_intersect2(&rdataset, &rdataset, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&rdataset, &rdataset);
+ ok(intersect_ok, "rdataset: intersect2 self.");
+ knot_rdataset_clear(&intersection, NULL);
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ ret = knot_rdataset_intersect2(&rdataset_lo, &rdataset_gt, NULL);
+ intersect_ok = ret == KNOT_EOK && rdataset_lo.count == 0;
+ ok(intersect_ok, "rdataset: intersect2 no common.");
+
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ret = knot_rdataset_intersect2(&copy, &rdataset_lo, NULL);
+ intersect_ok = ret == KNOT_EOK && knot_rdataset_eq(&copy, &rdataset_lo);
+ ok(intersect_ok, "rdataset: intersect2 normal.");
+ knot_rdataset_clear(&copy, NULL);
+
+ // Test subtract
+ ok(knot_rdataset_subtract(NULL, NULL, NULL) == KNOT_EINVAL,
+ "rdataset: subtract NULL.");
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ok(knot_rdataset_subtract(&copy, &copy, NULL) == KNOT_EOK &&
+ copy.count == 0, "rdataset: subtract self.");
+
+ ret = knot_rdataset_copy(&copy, &rdataset, NULL);
+ assert(ret == KNOT_EOK);
+ ret = knot_rdataset_subtract(&copy, &rdataset, NULL);
+ bool subtract_ok = ret == KNOT_EOK && copy.count == 0;
+ ok(subtract_ok, "rdataset: subtract identical.");
+
+ RDATASET_INIT_WITH(rdataset_lo, rdata_lo);
+ RDATASET_INIT_WITH(rdataset_gt, rdata_gt);
+ data_before = rdataset_lo.rdata;
+ ret = knot_rdataset_subtract(&rdataset_lo, &rdataset_gt, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset_lo.count == 1 &&
+ rdataset_lo.rdata == data_before;
+ ok(subtract_ok, "rdataset: subtract no common.");
+
+ ret = knot_rdataset_subtract(&rdataset, &rdataset_gt, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset.count == 1;
+ ok(subtract_ok, "rdataset: subtract the second.");
+
+ ret = knot_rdataset_subtract(&rdataset, &rdataset_lo, NULL);
+ subtract_ok = ret == KNOT_EOK && rdataset.count == 0 &&
+ rdataset.rdata == NULL;
+ ok(subtract_ok, "rdataset: subtract last.");
+
+ knot_rdataset_clear(&copy, NULL);
+ knot_rdataset_clear(&rdataset, NULL);
+ knot_rdataset_clear(&rdataset_lo, NULL);
+ knot_rdataset_clear(&rdataset_gt, NULL);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/libknot/test_rrset-wire.c b/tests/libknot/test_rrset-wire.c
new file mode 100644
index 0000000..35dc1bc
--- /dev/null
+++ b/tests/libknot/test_rrset-wire.c
@@ -0,0 +1,264 @@
+/* Copyright (C) 2018 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 <assert.h>
+#include <tap/basic.h>
+
+#include "libknot/packet/rrset-wire.h"
+#include "libknot/descriptor.h"
+#include "libknot/errcode.h"
+
+// Wire initializers
+
+#define MESSAGE_HEADER(AN, AUTH, ADD) 0xd4, 0xec, 0x81, 0xa0, 0x00, 0x01, \
+ 0x00, AN, 0x00, AUTH, 0x00, ADD
+
+#define QUERY(qname, type) qname, 0x00, type, 0x00, 0x01
+
+#define RR_HEADER(owner, type, rdlength0, rdlength1) owner, 0x00, type, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, rdlength0, rdlength1
+
+#define QNAME_POINTER 0xc0, 0x0c
+
+// Initializers' sizes
+
+#define QUERY_SIZE 12 + 4
+#define RR_HEADER_SIZE 10
+
+// Sample domain names
+
+#define QNAME 0x03, 0x6e, 0x69, 0x63, 0x02, 0x63, 0x7a, 0x00
+#define QNAME_SIZE 8
+#define QNAME_LONG \
+0x3f,'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+'m', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', \
+'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', \
+'m', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'w', 'y', \
+'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,\
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
+'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', \
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \
+'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', \
+'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 0x3f,'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
+'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', \
+'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', \
+'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'i', 'k', 0x3d,'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
+'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', \
+'p', 'q', 'r', 's', 't', 'u', 'v', 'x', 'x', 'y', 'z', 'a', 'b', \
+'c', 'd', 'e', 'f', 'g', 'h', 'i', 0x00
+#define QNAME_LONG_SIZE 255
+#define POINTER_SIZE 2
+
+struct wire_data {
+ uint8_t wire[65535];
+ size_t size;
+ size_t pos;
+ int code;
+ const char *msg;
+};
+
+#define FROM_CASE_COUNT 17
+
+static const struct wire_data FROM_CASES[FROM_CASE_COUNT] = {
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A)},
+ .size = QUERY_SIZE + QNAME_SIZE,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "No header" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A), 0x00, 0x00, 0x01},
+ .size = QUERY_SIZE + QNAME_SIZE + 3,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Partial header" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04) },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "No RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Partial RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x04), 0x01, 0x02, 0x03, 0x04 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 4,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "OK RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_A),
+ RR_HEADER(QNAME, KNOT_RRTYPE_A, 0x00, 0x05), 0x01, 0x02, 0x03, 0x04, 0x05 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_SIZE * 2 + 5,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Trailing RDATA" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SOA),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SOA, 0x00, 0x18), QNAME_POINTER, QNAME_POINTER,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ .size = QUERY_SIZE + RR_HEADER_SIZE + QNAME_LONG_SIZE + 6 + 20,
+ .pos = QUERY_SIZE + QNAME_LONG_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Max DNAME" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_SIG),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xdb),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME },
+ .size = 65535,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Max RDLENGTH" },
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME_LONG, KNOT_RRTYPE_SIG),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_SIG, 0xff, 0xff),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, QNAME_POINTER },
+ .size = 65535 + QNAME_LONG_SIZE + QUERY_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_LONG_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Max RDLENGTH + compression"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NSEC),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NSEC, 0x00, 0x03),
+ QNAME_POINTER, 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "DNAME wrong compression"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x01),
+ 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 1,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR missing header"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, QNAME_POINTER},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 9,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR bad offset"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x09),
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 7,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "NAPTR no DNAME"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_NAPTR),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_NAPTR, 0x00, 0x0c),
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x01, 0xff, 0x01, 0xff, QNAME_POINTER},
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 10 + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "NAPTR valid"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_APL),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_APL, 0x00, 0x00) },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Valid 0 RDATA"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_TXT),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_TXT, 0x00, 0x00) },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EMALF,
+ .msg = "Invalid 0 RDATA"},
+{ .wire = { MESSAGE_HEADER(1, 0, 0), QUERY(QNAME, KNOT_RRTYPE_PX),
+ RR_HEADER(QNAME_POINTER, KNOT_RRTYPE_PX, 0x00, 0x06),
+ 0x00, 0x00, QNAME_POINTER, QNAME_POINTER },
+ .size = QUERY_SIZE + QNAME_SIZE + RR_HEADER_SIZE + 2 + 6,
+ .pos = QUERY_SIZE + QNAME_SIZE,
+ .code = KNOT_EOK,
+ .msg = "Obsolete RR type"},
+};
+
+#define TEST_CASE_FROM(rrset, i) size_t _pos##i = FROM_CASES[i].pos; \
+ ok(knot_rrset_rr_from_wire(FROM_CASES[i].wire, &_pos##i, FROM_CASES[i].size, \
+ rrset, NULL, true) == FROM_CASES[i].code, "rrset wire: %s", FROM_CASES[i].msg)
+
+static void test_inputs(void)
+{
+ for (size_t i = 0; i < FROM_CASE_COUNT; ++i) {
+ knot_rrset_t rrset;
+ knot_rrset_init_empty(&rrset);
+ TEST_CASE_FROM(&rrset, i);
+ knot_rrset_clear(&rrset, NULL);
+ }
+}
+
+static void check_canon(uint8_t *wire, size_t size, size_t pos, bool canon,
+ knot_dname_t *qname, knot_dname_t *dname)
+{
+ knot_rrset_t rrset;
+ knot_rrset_init_empty(&rrset);
+
+ int ret = knot_rrset_rr_from_wire(wire, &pos, size, &rrset, NULL, canon);
+ is_int(KNOT_EOK, ret, "OK %s canonization", canon ? "with" : "without");
+ ok(memcmp(rrset.owner, qname, knot_dname_size(qname)) == 0, "compare owner");
+
+ uint8_t *rdata = rrset.rrs.rdata->data;
+ ok(memcmp(rdata, dname, knot_dname_size(dname)) == 0, "compare rdata dname");
+
+ knot_rrset_clear(&rrset, NULL);
+}
+
+static void test_canonization(void)
+{
+ #define UPP_QNAME_SIZE 5
+ #define UPP_QNAME 0x01, 0x41, 0x01, 0x5a, 0x00 // A.Z.
+ #define LOW_QNAME 0x01, 0x61, 0x01, 0x7a, 0x00 // a.z.
+
+ #define UPP_DNAME_SIZE 3
+ #define UPP_DNAME 0x01, 0x58, 0x00 // X.
+ #define LOW_DNAME 0x01, 0x78, 0x00 // x.
+
+ uint8_t wire[] = {
+ MESSAGE_HEADER(1, 0, 0), QUERY(UPP_QNAME, KNOT_RRTYPE_NS),
+ RR_HEADER(UPP_QNAME, KNOT_RRTYPE_NS, 0x00, UPP_DNAME_SIZE), UPP_DNAME
+ };
+ size_t size = QUERY_SIZE + RR_HEADER_SIZE + UPP_QNAME_SIZE * 2 + UPP_DNAME_SIZE;
+ size_t pos = QUERY_SIZE + UPP_QNAME_SIZE;
+
+ knot_dname_t upp_qname[] = { UPP_QNAME };
+ knot_dname_t upp_dname[] = { UPP_DNAME };
+ check_canon(wire, size, pos, false, upp_qname, upp_dname);
+
+ knot_dname_t low_qname[] = { LOW_QNAME };
+ knot_dname_t low_dname[] = { LOW_DNAME };
+ check_canon(wire, size, pos, true, low_qname, low_dname);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Test NULL parameters");
+ int ret = knot_rrset_rr_from_wire(NULL, NULL, 0, NULL, NULL, true);
+ is_int(KNOT_EINVAL, ret, "rr wire: Invalid params");
+
+ diag("Test various inputs");
+ test_inputs();
+
+ diag("Test canonization");
+ test_canonization();
+
+ return 0;
+}
diff --git a/tests/libknot/test_rrset.c b/tests/libknot/test_rrset.c
new file mode 100644
index 0000000..cc67e0f
--- /dev/null
+++ b/tests/libknot/test_rrset.c
@@ -0,0 +1,121 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <tap/basic.h>
+
+#include "libknot/rrset.h"
+#include "libknot/descriptor.h"
+
+static bool check_rrset(const knot_rrset_t *rrset, const knot_dname_t *owner,
+ uint16_t type, uint16_t rclass, uint32_t ttl)
+{
+ if (!rrset) {
+ return false;
+ }
+
+ const bool dname_cmp = owner == NULL ? rrset->owner == NULL :
+ knot_dname_is_equal(rrset->owner, owner);
+ return rrset->type == type && rrset->rclass == rclass && dname_cmp &&
+ rrset->ttl == ttl && rrset->rrs.count == 0; // We do not test rdataset here
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // Test new
+ knot_dname_t *dummy_owner = knot_dname_from_str_alloc("test.");
+ assert(dummy_owner);
+
+ knot_rrset_t *rrset = knot_rrset_new(dummy_owner, KNOT_RRTYPE_TXT,
+ KNOT_CLASS_IN, 3600, NULL);
+ ok(rrset != NULL, "rrset: create.");
+ assert(rrset);
+
+ ok(check_rrset(rrset, dummy_owner, KNOT_RRTYPE_TXT, KNOT_CLASS_IN, 3600),
+ "rrset: set fields during create.");
+
+ // Test init
+ knot_dname_free(dummy_owner, NULL);
+ dummy_owner = knot_dname_from_str_alloc("test2.");
+ assert(dummy_owner);
+
+ knot_dname_free(rrset->owner, NULL);
+ knot_rrset_init(rrset, dummy_owner, KNOT_RRTYPE_A, KNOT_CLASS_CH, 7200);
+ ok(check_rrset(rrset, dummy_owner, KNOT_RRTYPE_A, KNOT_CLASS_CH, 7200),
+ "rrset: init.");
+
+ // Test copy
+ knot_rrset_t *copy = knot_rrset_copy(rrset, NULL);
+ ok(copy != NULL, "rrset: copy.");
+ ok(check_rrset(copy, rrset->owner, rrset->type, rrset->rclass, 7200),
+ "rrset: set fields during copy.");
+ ok(knot_rrset_copy(NULL, NULL) == NULL, "rrset: copy NULL.");
+ assert(copy);
+
+ // Test equal - same TTL
+ ok(knot_rrset_equal(rrset, copy, true), "rrset: cmp same TTL");
+
+ // Test equal - different TTL
+ copy->ttl++;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different TTL");
+
+ // Test equal - ignore TTL
+ ok(knot_rrset_equal(rrset, copy, false), "rrset: cmp ignore TTL");
+
+ copy->ttl = rrset->ttl;
+
+ // Test equal - different type
+ copy->type++;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different type");
+
+ copy->type = rrset->type;
+
+ // Test equal - owners
+ knot_dname_free(rrset->owner, NULL);
+ rrset->owner = NULL;
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp NULL owner");
+
+ knot_dname_free(copy->owner, NULL);
+ copy->owner = NULL;
+ ok(knot_rrset_equal(rrset, copy, true), "rrset: cmp NULL owners");
+
+ // Test equal - different rdata
+ knot_rrset_add_rdata(copy, (const uint8_t *)"abc", 3, NULL);
+ ok(!knot_rrset_equal(rrset, copy, true), "rrset: cmp different rdata");
+
+ // Test clear
+ knot_rrset_clear(rrset, NULL);
+ ok(rrset->owner == NULL, "rrset: clear.");
+
+ // Test empty
+ ok(knot_rrset_empty(rrset), "rrset: empty.");
+ ok(knot_rrset_empty(NULL), "rrset: empty NULL.");
+ copy->rrs.count = 1;
+ ok(!knot_rrset_empty(copy), "rrset: not empty.");
+
+ // Test init empty
+ knot_rrset_init_empty(rrset);
+ ok(check_rrset(rrset, NULL, 0, KNOT_CLASS_IN, 0), "rrset: init empty.");
+
+ // "Test" freeing
+ knot_rrset_free(rrset, NULL);
+ knot_rrset_free(copy, NULL);
+
+ return 0;
+}
diff --git a/tests/libknot/test_tsig.c b/tests/libknot/test_tsig.c
new file mode 100644
index 0000000..a34e7ca
--- /dev/null
+++ b/tests/libknot/test_tsig.c
@@ -0,0 +1,204 @@
+/* Copyright (C) 2015 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 <tap/basic.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libknot/errcode.h"
+#include "libknot/tsig.h"
+
+static bool key_is_eq(const knot_tsig_key_t *a, const knot_tsig_key_t *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ return a->algorithm == b->algorithm &&
+ knot_dname_is_equal(a->name, b->name) &&
+ dnssec_binary_cmp(&a->secret, &b->secret) == 0;
+}
+
+#define test_function(function, msg, expected, ...) \
+ knot_tsig_key_t key = { 0 }; \
+ int r = function(&key, __VA_ARGS__); \
+ ok((r != KNOT_EOK && expected == NULL) || \
+ (r == KNOT_EOK && key_is_eq(&key, expected)), \
+ "%s: %s", #function, msg); \
+ knot_tsig_key_deinit(&key);
+
+static void test_init(const char *msg, const knot_tsig_key_t *expected,
+ const char *algo, const char *name, const char *secret)
+{
+ test_function(knot_tsig_key_init, msg, expected, algo, name, secret);
+}
+
+static void test_init_str(const char *msg, const knot_tsig_key_t *expected,
+ const char *params)
+{
+ test_function(knot_tsig_key_init_str, msg, expected, params);
+}
+
+static void test_init_file(const char *msg, const knot_tsig_key_t *expected,
+ const char *filename)
+{
+ test_function(knot_tsig_key_init_file, msg, expected, filename);
+}
+
+static void test_init_file_content(const char *msg,
+ const knot_tsig_key_t *expected,
+ const char *content)
+{
+ char filename[] = "testkey.XXXXXX";
+
+ int fd = mkstemp(filename);
+ if (fd == -1) {
+ bail("failed to create temporary file");
+ return;
+ }
+
+ ok(write(fd, content, strlen(content)) != -1, "file write");
+ close(fd);
+
+ test_init_file(msg, expected, filename);
+
+ unlink(filename);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // initialization from parameters
+
+ test_init("missing name", NULL, "hmac-md5", NULL, "Wg==");
+ test_init("missing secret", NULL, "hmac-md5", "name", NULL);
+ test_init("invalid HMAC", NULL, "hmac-sha11", "name", "Wg==");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA256,
+ .name = (uint8_t *)"\x3""key""\x4""name",
+ .secret.size = 1,
+ .secret.data = (uint8_t *)"\x5a"
+ };
+ test_init("default algorithm", &key, NULL, "key.name", "Wg==");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""knot""\x3""dns",
+ .secret.size = 6,
+ .secret.data = (uint8_t *)"secret"
+ };
+ test_init("sha1", &key, "hmac-sha1", "knot.dns.", "c2VjcmV0");
+ }
+
+ // initialization from string
+
+ test_init_str("missing value", NULL, NULL);
+ test_init_str("malformed", NULL, "this is malformed");
+ test_init_str("invalid HMAC", NULL, "hmac-sha51299:key:Wg==");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA256,
+ .name = (uint8_t *)"\x4""tsig""\x3""key",
+ .secret.size = 9,
+ .secret.data = (uint8_t *)"bananakey"
+ };
+ test_init_str("default algorithm", &key, "tsig.key:YmFuYW5ha2V5");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA384,
+ .name = (uint8_t *)"\x6""strong""\x3""key",
+ .secret.size = 8,
+ .secret.data = (uint8_t *)"applekey"
+ };
+ test_init_str("sha384", &key, "hmac-sha384:strong.KEY:YXBwbGVrZXk=");
+ }
+
+ // initialization from a file
+
+ test_init_file("no filename", NULL, NULL);
+ test_init_file("not-existing", NULL, "/this-really-should-not-exist");
+ test_init_file_content("malformed content", NULL, "malformed\n");
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA512,
+ .name = (uint8_t *)"\x6""django""\x3""one",
+ .secret.size = 40,
+ .secret.data = (uint8_t *)"Who's that stumbling around in the dark?"
+ };
+ test_init_file_content("sha512", &key,
+ "hmac-sha512:django.one:V2hvJ3MgdGhhdCB"
+ "zdHVtYmxpbmcgYXJvdW5kIGluIHRoZSBkYXJrP"
+ "w==\n\n\n");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA512,
+ .name = (uint8_t *)"\x6""django""\x3""two",
+ .secret.size = 22,
+ .secret.data = (uint8_t *)"Prepare to get winged!"
+ };
+ test_init_file_content("sha512 without newline", &key,
+ "hmac-sha512:django.two:UHJlcGFyZSB0byB"
+ "nZXQgd2luZ2VkIQ==");
+ }
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""test",
+ .secret.size = 1,
+ .secret.data = (uint8_t *)"\x5a"
+ };
+ test_init_file_content("leading and trailing white spaces", &key,
+ "\thmac-sha1:test:Wg== \n");
+ }
+
+ // tsig key duplication
+
+ {
+ static const knot_tsig_key_t key = {
+ .algorithm = DNSSEC_TSIG_HMAC_SHA1,
+ .name = (uint8_t *)"\x4""copy""\x2""me",
+ .secret.size = 6,
+ .secret.data = (uint8_t *)"orange"
+ };
+
+ knot_tsig_key_t copy = { 0 };
+ int r;
+
+ r = knot_tsig_key_copy(NULL, &key);
+ ok(r != KNOT_EOK, "knot_tsig_key_copy: no destination");
+ r = knot_tsig_key_copy(&copy, NULL);
+ ok(r != KNOT_EOK, "knot_tsig_key_copy: no source");
+ r = knot_tsig_key_copy(&copy, &key);
+ ok(r == KNOT_EOK && key_is_eq(&copy, &key) &&
+ copy.secret.data != key.secret.data && copy.name != key.name,
+ "knot_tsig_key_copy: simple copy");
+
+ knot_tsig_key_deinit(&copy);
+ }
+
+ return 0;
+}
diff --git a/tests/libknot/test_wire.c b/tests/libknot/test_wire.c
new file mode 100644
index 0000000..3e2468c
--- /dev/null
+++ b/tests/libknot/test_wire.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+
+#include "libknot/wire.h"
+
+#define write_test(size, value, ...) { \
+ const uint8_t expect[] = { __VA_ARGS__ }; \
+ uint8_t wdata[sizeof(expect)] = { 0x00 }; \
+ knot_wire_write_u ## size(wdata, value); \
+ ok(memcmp(wdata, expect, sizeof(expect)) == 0, "%d-bit write", size); \
+}
+
+int main(int argc, char *argv[])
+{
+ plan(8);
+
+ const uint8_t rdata[] = { 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
+
+ is_hex( 0x8899, knot_wire_read_u16(rdata), "16-bit read");
+ is_hex( 0x8899aabb, knot_wire_read_u32(rdata), "32-bit read");
+ is_hex( 0x8899aabbccdd, knot_wire_read_u48(rdata), "48-bit read");
+ is_hex(0x8899aabbccddeeff, knot_wire_read_u64(rdata), "64-bit read");
+
+ write_test(16, 0x1122, 0x11, 0x22);
+ write_test(32, 0x66778899, 0x66, 0x77, 0x88, 0x99);
+ write_test(48, 0xbbccdd778899, 0xbb, 0xcc, 0xdd, 0x77, 0x88, 0x99);
+ write_test(64, 0xbbccddee66778899, 0xbb, 0xcc, 0xdd, 0xee,
+ 0x66, 0x77, 0x88, 0x99);
+
+ return 0;
+}
diff --git a/tests/libknot/test_xdp_tcp.c b/tests/libknot/test_xdp_tcp.c
new file mode 100644
index 0000000..64e3bdc
--- /dev/null
+++ b/tests/libknot/test_xdp_tcp.c
@@ -0,0 +1,633 @@
+/* Copyright (C) 2022 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 <unistd.h>
+
+#include "tap/basic.h"
+#include "libknot/error.h"
+#include "libknot/xdp/msg_init.h"
+#include "libknot/xdp/tcp.c"
+#include "libknot/xdp/tcp_iobuf.c"
+#include "libknot/xdp/bpf-user.h"
+
+#define INFTY INT32_MAX
+
+knot_tcp_table_t *test_table = NULL;
+knot_tcp_table_t *test_syn_table = NULL;
+#define TEST_TABLE_SIZE 100
+
+size_t sent_acks = 0;
+size_t sent_rsts = 0;
+size_t sent_syns = 0;
+size_t sent_fins = 0;
+uint32_t sent_seqno = 0;
+uint32_t sent_ackno = 0;
+size_t sent2_data = 0;
+size_t send2_mss = 0;
+
+knot_xdp_socket_t *test_sock = NULL;
+
+struct sockaddr_in test_addr = { AF_INET, 0, { 127 + (1 << 24) }, { 0 } };
+
+knot_tcp_conn_t *test_conn = NULL;
+
+/*!
+ * \brief Length of timeout-watching list.
+ */
+static size_t tcp_table_timeout_length(knot_tcp_table_t *table)
+{
+ return list_size(tcp_table_timeout(table));
+}
+
+/*!
+ * \brief Clean up old TCP connection w/o sending RST or FIN.
+ *
+ * \param tcp_table TCP connection table to clean up.
+ * \param timeout Remove connections older than this (usecs).
+ * \param at_least Remove at least this number of connections.
+ */
+static void tcp_cleanup(knot_tcp_table_t *tcp_table, uint32_t timeout,
+ uint32_t at_least)
+{
+ uint32_t now = get_timestamp(), i = 0;
+ knot_tcp_conn_t *conn, *next;
+ WALK_LIST_DELSAFE(conn, next, *tcp_table_timeout(tcp_table)) {
+ if (i++ < at_least || now - conn->last_active >= timeout) {
+ tcp_table_remove(tcp_table_re_lookup(conn, tcp_table), tcp_table);
+ del_conn(conn);
+ }
+ }
+}
+
+/*!
+ * \brief Find connection related to incoming message.
+ */
+static knot_tcp_conn_t *tcp_table_find(knot_tcp_table_t *table, knot_xdp_msg_t *msg_recv)
+{
+ uint64_t unused = 0;
+ return *tcp_table_lookup(&msg_recv->ip_from, &msg_recv->ip_to, &unused, table);
+}
+
+static int mock_send(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ ok(n_msgs <= 20, "send: not too many at once");
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+
+ ok(msg->flags & KNOT_XDP_MSG_TCP, "send: is TCP message");
+ ok(msg->payload.iov_len == 0, "send: is empty payload");
+
+ if (msg->flags & KNOT_XDP_MSG_RST) {
+ ok(!(msg->flags & KNOT_XDP_MSG_ACK), "send: no RST+ACK");
+ sent_rsts++;
+ } else if (msg->flags & KNOT_XDP_MSG_SYN) {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is SYN+ACK");
+ sent_syns++;
+ } else if (msg->flags & KNOT_XDP_MSG_FIN) {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: FIN has always ACK");
+ sent_fins++;
+ } else {
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send: is ACK");
+ sent_acks++;
+ }
+
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static int mock_send_nocheck(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+ if (msg->flags & KNOT_XDP_MSG_RST) {
+ sent_rsts++;
+ } else if (msg->flags & KNOT_XDP_MSG_SYN) {
+ sent_syns++;
+ } else if (msg->flags & KNOT_XDP_MSG_FIN) {
+ sent_fins++;
+ } else {
+ sent_acks++;
+ }
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static int mock_send2(_unused_ knot_xdp_socket_t *sock, const knot_xdp_msg_t msgs[],
+ uint32_t n_msgs, _unused_ uint32_t *sent)
+{
+ ok(n_msgs <= 20, "send2: not too many at once");
+ for (uint32_t i = 0; i < n_msgs; i++) {
+ const knot_xdp_msg_t *msg = msgs + i;
+ ok(msg->flags & KNOT_XDP_MSG_TCP, "send2: is TCP message");
+ ok(msg->flags & KNOT_XDP_MSG_ACK, "send2: has ACK");
+ ok(msg->payload.iov_len <= send2_mss, "send2: fulfilled MSS");
+ sent2_data += msg->payload.iov_len;
+
+ sent_seqno = msg->seqno;
+ sent_ackno = msg->ackno;
+ }
+ return KNOT_EOK;
+}
+
+static void clean_table(void)
+{
+ (void)tcp_cleanup(test_table, 0, INFTY);
+}
+
+static void clean_sent(void)
+{
+ sent_acks = 0;
+ sent_rsts = 0;
+ sent_syns = 0;
+ sent_fins = 0;
+}
+
+static void check_sent(size_t expect_acks, size_t expect_rsts, size_t expect_syns, size_t expect_fins)
+{
+ is_int(expect_acks, sent_acks, "sent ACKs");
+ is_int(expect_rsts, sent_rsts, "sent RSTs");
+ is_int(expect_syns, sent_syns, "sent SYNs");
+ is_int(expect_fins, sent_fins, "sent FINs");
+ clean_sent();
+}
+
+static void prepare_msg(knot_xdp_msg_t *msg, int flags, uint16_t sport, uint16_t dport)
+{
+ msg_init(msg, flags | KNOT_XDP_MSG_TCP);
+ memcpy(&msg->ip_from, &test_addr, sizeof(test_addr));
+ memcpy(&msg->ip_to, &test_addr, sizeof(test_addr));
+ msg->ip_from.sin6_port = htobe16(sport);
+ msg->ip_to.sin6_port = htobe16(dport);
+}
+
+static void prepare_seqack(knot_xdp_msg_t *msg, int seq_shift, int ack_shift)
+{
+ msg->seqno = sent_ackno + seq_shift;
+ msg->ackno = sent_seqno + ack_shift;
+}
+
+static void prepare_data(knot_xdp_msg_t *msg, const char *bytes, size_t n)
+{
+ msg->payload.iov_len = n;
+ msg->payload.iov_base = (void *)bytes;
+}
+
+static void fix_seqack(knot_xdp_msg_t *msg)
+{
+ knot_tcp_conn_t *conn = tcp_table_find(test_table, msg);
+ if (conn == NULL) {
+ conn = tcp_table_find(test_syn_table, msg);
+ }
+ assert(conn != NULL);
+ msg->seqno = conn->seqno;
+ msg->ackno = conn->ackno;
+}
+
+static void fix_seqacks(knot_xdp_msg_t *msgs, size_t count)
+{
+ for (size_t i = 0; i < count; i++) {
+ fix_seqack(&msgs[i]);
+ }
+}
+
+void test_syn(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "SYN: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN: send OK");
+ is_int(msg.seqno + 1, sent_ackno, "SYN: ackno");
+ check_sent(0, 0, 1, 0);
+ is_int(XDP_TCP_SYN, rl.action, "SYN: relay action");
+ is_int(XDP_TCP_NOOP, rl.answer, "SYN: relay answer");
+ is_int(0, rl.inbufs_count, "SYN: no payload");
+ is_int(0, test_table->usage, "SYN: no connection in normal table");
+ is_int(1, test_syn_table->usage, "SYN: one connection in SYN table");
+ knot_tcp_conn_t *conn = tcp_table_find(test_syn_table, &msg);
+ ok(conn != NULL, "SYN: connection present");
+ assert(conn);
+ ok(conn == rl.conn, "SYN: relay points to connection");
+ is_int(XDP_TCP_ESTABLISHING, conn->state, "SYN: connection state");
+ ok(memcmp(&conn->ip_rem, &msg.ip_from, sizeof(msg.ip_from)) == 0, "SYN: conn IP from");
+ ok(memcmp(&conn->ip_loc, &msg.ip_to, sizeof(msg.ip_to)) == 0, "SYN: conn IP to");
+
+ knot_tcp_cleanup(test_syn_table, &rl, 1);
+ test_conn = conn;
+}
+
+void test_establish(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
+ prepare_seqack(&msg, 0, 1);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "establish: relay OK");
+ is_int(0, test_syn_table->usage, "SYN: no connection in SYN table");
+ is_int(1, test_table->usage, "SYN: one connection in normal table");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "establish: send OK");
+ check_sent(0, 0, 0, 0);
+ is_int(0, rl.auto_answer, "establish: no auto answer");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
+ clean_table();
+}
+
+void test_syn_ack(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN | KNOT_XDP_MSG_ACK, 1000, 2000);
+ int ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "SYN+ACK: relay OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "SYN+ACK: send OK");
+ is_int(msg.seqno + 1, sent_ackno, "SYN+ACK: ackno");
+ check_sent(1, 0, 0, 0);
+ is_int(XDP_TCP_ESTABLISH, rl.action, "SYN+ACK: relay action");
+ ok(rl.conn != NULL, "SYN+ACK: connection present");
+
+ test_conn = rl.conn;
+ knot_tcp_cleanup(test_table, &rl, 1);
+}
+
+void test_data_fragments(void)
+{
+ const size_t CONNS = 4;
+ knot_xdp_msg_t msgs[CONNS];
+ knot_tcp_relay_t rls[CONNS];
+ memset(rls, 0, CONNS * sizeof(*rls));
+
+ // first msg contains one whole payload and one fragment
+ prepare_msg(&msgs[0], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[0], 0, 0);
+ prepare_data(&msgs[0], "\x00\x03""xyz""\x00\x04""ab", 9);
+
+ // second msg contains just fragment not completing anything
+ prepare_msg(&msgs[1], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[1], 9, 0);
+ prepare_data(&msgs[1], "c", 1);
+
+ // third msg finishes fragment, contains one whole, and starts new fragment by just half of length info
+ prepare_msg(&msgs[2], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[2], 10, 0);
+ prepare_data(&msgs[2], "d""\x00\x01""i""\x00", 5);
+
+ // fourth msg completes fragment and starts never-finishing one
+ prepare_msg(&msgs[3], KNOT_XDP_MSG_ACK, 1000, 2000);
+ prepare_seqack(&msgs[3], 15, 0);
+ prepare_data(&msgs[3], "\x02""AB""\xff\xff""abcdefghijklmnopqrstuvwxyz...", 34);
+
+ assert(test_table);
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "fragments: relay OK");
+ assert(test_sock);
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "fragments: send OK");
+ is_int(msgs[3].ackno, sent_seqno, "fragments: seqno");
+ is_int(msgs[3].seqno + msgs[3].payload.iov_len, sent_ackno, "fragments: ackno");
+ check_sent(4, 0, 0, 0);
+
+ is_int(KNOT_XDP_MSG_ACK, rls[0].auto_answer, "fragments[0]: auto answer");
+ ok(rls[0].conn != NULL, "fragments0: connection present");
+ ok(rls[0].conn == test_conn, "fragments0: same connection");
+ is_int(1, rls[0].inbufs_count, "fragments0: inbufs count");
+ is_int(3, rls[0].inbufs[0].iov_len, "fragments0: data length");
+ is_int(0, memcmp("xyz", rls[0].inbufs[0].iov_base, rls[0].inbufs[0].iov_len), "fragments0: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[1].auto_answer, "fragments[1]: auto answer");
+ is_int(XDP_TCP_NOOP, rls[1].action, "fragments[1]: action"); // NOTE: NOOP
+ ok(rls[0].conn != NULL, "fragments1: connection present");
+ ok(rls[0].conn == test_conn, "fragments1: same connection");
+ is_int(0, rls[1].inbufs_count, "fragments1: inbufs count");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[2].auto_answer, "fragments[2]: auto answer");
+ ok(rls[0].conn != NULL, "fragments2: connection present");
+ ok(rls[0].conn == test_conn, "fragments2: same connection");
+ is_int(2, rls[2].inbufs_count, "fragments2: inbufs count");
+ is_int(4, rls[2].inbufs[0].iov_len, "fragments2-0: data length");
+ is_int(0, memcmp("abcd", rls[2].inbufs[0].iov_base, rls[2].inbufs[0].iov_len), "fragments2-0: data");
+ is_int(1, rls[2].inbufs[1].iov_len, "fragments2-1: data length");
+ is_int(0, memcmp("i", rls[2].inbufs[1].iov_base, rls[2].inbufs[1].iov_len), "fragments2-1: data");
+
+ is_int(KNOT_XDP_MSG_ACK, rls[3].auto_answer, "fragments[3]: auto answer");
+ ok(rls[0].conn != NULL, "fragments3: connection present");
+ ok(rls[0].conn == test_conn, "fragments3: same connection");
+ is_int(1, rls[3].inbufs_count, "fragments3: inbufs count");
+ is_int(2, rls[3].inbufs[0].iov_len, "fragments3: data length");
+ is_int(0, memcmp("AB", rls[3].inbufs[0].iov_base, rls[3].inbufs[0].iov_len), "fragments3: data");
+
+ knot_tcp_cleanup(test_table, rls, 4);
+}
+
+void test_close(void)
+{
+ size_t conns_pre = test_table->usage;
+
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+ prepare_msg(&msg, KNOT_XDP_MSG_FIN | KNOT_XDP_MSG_ACK,
+ be16toh(test_conn->ip_rem.sin6_port),
+ be16toh(test_conn->ip_loc.sin6_port));
+ prepare_seqack(&msg, 0, 0);
+
+ // test wrong ackno synack, shall reply with RST with same
+ knot_xdp_msg_t wrong = msg;
+ wrong.seqno += INT32_MAX;
+ wrong.ackno += INT32_MAX;
+ int ret = knot_tcp_recv(&rl, &wrong, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 0 OK");
+ is_int(KNOT_XDP_MSG_RST, rl.auto_answer, "close: reset wrong ackno");
+ is_int(rl.auto_seqno, wrong.ackno, "close: reset seqno");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send 0 OK");
+ check_sent(0, 1, 0, 0);
+ is_int(sent_seqno, wrong.ackno, "close: reset seqno sent");
+
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 1 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send OK");
+ check_sent(0, 0, 0, 1);
+ is_int(XDP_TCP_CLOSE, rl.action, "close: relay action");
+ assert(rl.conn);
+ ok(rl.conn == test_conn, "close: same connection");
+ is_int(XDP_TCP_CLOSING2, rl.conn->state, "close: conn state");
+
+ msg.flags &= ~KNOT_XDP_MSG_FIN;
+ prepare_seqack(&msg, 0, 0);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "close: relay 2 OK");
+ ret = knot_tcp_send(test_sock, &rl, 1, 1);
+ is_int(KNOT_EOK, ret, "close: send 2 OK");
+ check_sent(0, 0, 0, 0);
+ is_int(conns_pre - 1, test_table->usage, "close: connection removed");
+ is_int(conns_pre - 1, tcp_table_timeout_length(test_table), "close: timeout list size");
+ knot_tcp_cleanup(test_table, &rl, 1);
+}
+
+void test_many(void)
+{
+ size_t CONNS = test_table->size * test_table->size;
+ size_t i_survive = CONNS / 2;
+ uint32_t timeout_time = 1000000;
+
+ knot_xdp_msg_t *msgs = malloc(CONNS * sizeof(*msgs));
+ assert(msgs != NULL);
+ for (size_t i = 0; i < CONNS; i++) {
+ prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2, 1);
+ }
+ knot_tcp_relay_t *rls = malloc(CONNS * sizeof(*rls));
+
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, NULL, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "many: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many: relay send OK");
+ check_sent(0, 0, CONNS, 0);
+ is_int(CONNS, test_table->usage, "many: table usage");
+
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ usleep(timeout_time);
+ knot_xdp_msg_t *survive = &msgs[i_survive];
+ knot_tcp_relay_t surv_rl = { 0 };
+ survive->flags = (KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK);
+ knot_tcp_conn_t *surv_conn = tcp_table_find(test_table, survive);
+ fix_seqack(survive);
+ prepare_data(survive, "\x00\x00", 2);
+ assert(test_table);
+ ret = knot_tcp_recv(&surv_rl, survive, 1, test_table, NULL, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "many/survivor: OK");
+ clean_sent();
+
+ knot_sweep_stats_t stats = { 0 };
+ ret = knot_tcp_sweep(test_table, timeout_time, INFTY, INFTY, INFTY, INFTY,
+ INFTY, rls, CONNS, &stats);
+ is_int(KNOT_EOK, ret, "many/timeout1: OK");
+ is_int(CONNS - 1, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "many/timeout1: close count");
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_LIMIT_CONN], "may/timeout1: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout1: send OK");
+ check_sent(0, 0, 0, CONNS - 1);
+
+ knot_sweep_stats_reset(&stats);
+ ret = knot_tcp_sweep(test_table, INFTY, timeout_time, INFTY, INFTY, INFTY,
+ INFTY, rls, CONNS, &stats);
+ is_int(KNOT_EOK, ret, "many/timeout2: OK");
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "many/timeout2: close count");
+ is_int(CONNS - 1, stats.counters[KNOT_SWEEP_CTR_LIMIT_CONN], "may/timeout2: reset count");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "many/timeout2: send OK");
+ check_sent(0, CONNS - 1, 0, 0);
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ is_int(1, test_table->usage, "many/timeout: one survivor");
+ is_int(1, tcp_table_timeout_length(test_table), "many/timeout: one survivor in timeout list");
+ ok(surv_conn != NULL, "many/timeout: survivor connection present");
+ ok(surv_conn == surv_rl.conn, "many/timeout: same connection");
+ knot_tcp_cleanup(test_table, &surv_rl, 1);
+
+ free(msgs);
+ free(rls);
+}
+
+void test_ibufs_size(void)
+{
+ int CONNS = 4;
+ knot_xdp_msg_t msgs[CONNS];
+ knot_tcp_relay_t rls[CONNS];
+
+ // just open connections
+ for (int i = 0; i < CONNS; i++) {
+ prepare_msg(&msgs[i], KNOT_XDP_MSG_SYN, i + 2000, 1);
+ }
+ int ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "ibufs: open OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "ibufs: first send OK");
+ check_sent(0, 0, CONNS, 0);
+ for (int i = 0; i < CONNS; i++) {
+ msgs[i].flags = KNOT_XDP_MSG_TCP | KNOT_XDP_MSG_ACK;
+ }
+ fix_seqacks(msgs, CONNS);
+ (void)knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+
+ is_int(0, test_table->inbufs_total, "inbufs: initial total zero");
+
+ // first connection will start a fragment buf then finish it
+ fix_seqack(&msgs[0]);
+ prepare_data(&msgs[0], "\x00\x0a""lorem", 7);
+ ret = knot_tcp_recv(&rls[0], &msgs[0], 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "ibufs: must be OK");
+ ret = knot_tcp_send(test_sock, &rls[0], 1, 1);
+ is_int(KNOT_EOK, ret, "ibufs: must send OK");
+ check_sent(1, 0, 0, 0);
+ is_int(7, test_table->inbufs_total, "inbufs: first inbuf");
+ knot_tcp_cleanup(test_table, &rls[0], 1);
+
+ // other connection will just store fragments
+ fix_seqacks(msgs, CONNS);
+ prepare_data(&msgs[0], "ipsum", 5);
+ prepare_data(&msgs[1], "\x00\xff""12345", 7);
+ prepare_data(&msgs[2], "\xff\xff""abcde", 7);
+ prepare_data(&msgs[3], "\xff\xff""abcde", 7);
+ ret = knot_tcp_recv(rls, msgs, CONNS, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "inbufs: relay OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: send OK");
+ check_sent(CONNS, 0, 0, 0);
+ is_int(21, test_table->inbufs_total, "inbufs: after change");
+ is_int(0, rls[1].action, "inbufs: one relay");
+ is_int(10, rls[0].inbufs[0].iov_len, "inbufs: data length");
+ knot_tcp_cleanup(test_table, rls, CONNS);
+
+ // now free some
+ knot_sweep_stats_t stats = { 0 };
+ ret = knot_tcp_sweep(test_table, INFTY, INFTY, INFTY, INFTY,
+ test_table->inbufs_total - 8, INFTY, rls,
+ CONNS, &stats);
+ is_int(KNOT_EOK, ret, "inbufs: timeout OK");
+ ret = knot_tcp_send(test_sock, rls, CONNS, CONNS);
+ is_int(KNOT_EOK, ret, "inbufs: timeout send OK");
+ check_sent(0, 2, 0, 0);
+ is_int(0, stats.counters[KNOT_SWEEP_CTR_TIMEOUT], "inbufs: close count");
+ is_int(2, stats.counters[KNOT_SWEEP_CTR_LIMIT_IBUF], "inbufs: reset count");
+ knot_tcp_cleanup(test_table, rls, CONNS);
+ is_int(7, test_table->inbufs_total, "inbufs: final state");
+ ok(NULL != tcp_table_find(test_table, &msgs[0]), "inbufs: first conn survived");
+ ok(NULL == tcp_table_find(test_table, &msgs[1]), "inbufs: second conn not survived");
+ ok(NULL == tcp_table_find(test_table, &msgs[2]), "inbufs: third conn not survived");
+ ok(NULL != tcp_table_find(test_table, &msgs[3]), "inbufs: fourth conn survived");
+
+ clean_table();
+}
+
+void test_obufs(void)
+{
+ knot_xdp_msg_t msg;
+ knot_tcp_relay_t rl = { 0 };
+
+ prepare_msg(&msg, KNOT_XDP_MSG_SYN, 1, 2);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE); // SYN
+ (void)knot_tcp_send(test_sock, &rl, 1, 1); // SYN+ACK
+ prepare_msg(&msg, KNOT_XDP_MSG_ACK, 1, 2);
+ prepare_seqack(&msg, 0, 1);
+ (void)knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE); // ACK
+
+ size_t TEST_MSS = 1111;
+ size_t DATA_LEN = 65535; // with 2-byte len prefix, this is > 64k == window_size
+ uint8_t *data = calloc(DATA_LEN, 1);
+ assert(rl.conn);
+ rl.conn->mss = TEST_MSS;
+ rl.conn->window_size = 65536;
+ send2_mss = TEST_MSS;
+
+ int ret = knot_tcp_reply_data(&rl, test_table, false, data, DATA_LEN), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: fill with data");
+ for (knot_tcp_outbuf_t *ob = rl.conn->outbufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(ob->len > 0, "init last ob[%d]: non-trivial", i);
+ ok(ob->len <= TEST_MSS, "init last ob[%d]: fulfills MSS", i);
+ } else {
+ is_int(TEST_MSS, ob->len, "init ob[%d]: exactly MSS", i);
+ }
+ ok(!ob->sent, "init ob[%d]: not sent", i);
+ }
+ ret = knot_tcp_send(test_sock, &rl, 1, 20), i = 0;
+ is_int(KNOT_EOK, ret, "obufs: send OK");
+ is_int((DATA_LEN + 2) / TEST_MSS * TEST_MSS, sent2_data, "obufs: sent all but one MSS");
+ for (knot_tcp_outbuf_t *ob = rl.conn->outbufs; ob != NULL; ob = ob->next, i++) {
+ if (ob->next == NULL) {
+ ok(!ob->sent, "last ob[%d]: not sent", i);
+ } else {
+ ok(ob->sent, "ob[%d]: sent", i);
+ if (ob->next->next != NULL) {
+ is_int(ob->seqno + ob->len, ob->next->seqno, "init ob[%d+1]: seqno", i);
+ }
+ }
+ }
+ knot_tcp_cleanup(test_table, &rl, 1);
+
+ prepare_seqack(&msg, 0, TEST_MSS);
+ ret = knot_tcp_recv(&rl, &msg, 1, test_table, test_syn_table, XDP_TCP_IGNORE_NONE);
+ is_int(KNOT_EOK, ret, "obufs: ACKed data");
+ assert(rl.conn);
+ rl.conn->window_size = 65536;
+ knot_tcp_outbuf_t *surv_ob = rl.conn->outbufs;
+ ok(surv_ob != NULL, "obufs: unACKed survived");
+ assert(surv_ob);
+ ok(surv_ob->next == NULL, "obufs: just one survived");
+ ok(!surv_ob->sent, "obufs: survivor not sent");
+ ret = knot_tcp_send(test_sock, &rl, 1, 20);
+ is_int(KNOT_EOK, ret, "obufs: send rest OK");
+ is_int(DATA_LEN + 2, sent2_data, "obufs: sent all");
+ ok(surv_ob->sent, "obufs: survivor sent");
+ is_int(sent_seqno, surv_ob->seqno, "obufs: survivor seqno");
+
+ knot_tcp_cleanup(test_table, &rl, 1);
+ clean_table();
+ free(data);
+}
+
+static void init_mock(knot_xdp_socket_t **socket, void *send_mock)
+{
+ *socket = calloc(1, sizeof(**socket));
+ if (*socket != NULL) {
+ (*socket)->send_mock = send_mock;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_table = knot_tcp_table_new(TEST_TABLE_SIZE, NULL);
+ assert(test_table != NULL);
+ test_syn_table = knot_tcp_table_new(TEST_TABLE_SIZE, test_table);
+
+ init_mock(&test_sock, mock_send);
+
+ test_syn();
+ test_establish();
+
+ test_syn_ack();
+ test_data_fragments();
+ test_close();
+
+ test_ibufs_size();
+
+ knot_xdp_deinit(test_sock);
+ init_mock(&test_sock, mock_send_nocheck);
+ test_many();
+
+ knot_xdp_deinit(test_sock);
+ init_mock(&test_sock, mock_send2);
+ test_obufs();
+
+ knot_xdp_deinit(test_sock);
+ knot_tcp_table_free(test_table);
+ knot_tcp_table_free(test_syn_table);
+
+ return 0;
+}
diff --git a/tests/libknot/test_yparser.c b/tests/libknot/test_yparser.c
new file mode 100644
index 0000000..8655096
--- /dev/null
+++ b/tests/libknot/test_yparser.c
@@ -0,0 +1,346 @@
+/* Copyright (C) 2021 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yparser.h"
+#include "libknot/libknot.h"
+
+const char *syntax_ok =
+ "#comment\n"
+ " # comment\n"
+ "a:\n"
+ "a :\n"
+ "a : #comment\n"
+ "\n"
+ "b: \"b\"\n"
+ "b: b #comment\n"
+ "b : b\n"
+ "b: [ b] # comment\n"
+ "b: [b ]\n"
+ "b: [ b ]\n"
+ "\n"
+ " f: \"f\"\n"
+ " f: f #comment\n"
+ " f : f\n"
+ " f: [ f] # comment\n"
+ " f: [f ]\n"
+ " f: [ f ]\n"
+ " f: [ \"f\" ]\n"
+ "\n"
+ "c: [a,b]\n"
+ "c: [a, b]\n"
+ "c: [a ,b]\n"
+ "c: [a , b]\n"
+ "c: [ a , b ]\n"
+ "c: [ \"a\" , \"b\" ]\n"
+ "\n"
+ "- d: d\n"
+ "- d : d # comment\n"
+ "\n"
+ "e: \"a#b' c[d,]\"\n"
+ "\n"
+ "zone:\n"
+ "#comment\n"
+ " # comment\n"
+ " - domain: example. # comment\n"
+ " master: bind\n"
+ " - domain: example.\n"
+ " master: bind\n"
+ "zone2:\n"
+ " - a: b # different indentation";
+
+const char *syntax_error1 =
+ "f:\n"
+ " - a: b\n"
+ " - b: c\n";
+
+const char *syntax_error2 =
+ "f:\n"
+ " - a: b\n"
+ " c: d\n";
+
+const char *syntax_error3 =
+ "f:\n"
+ " a: b\n"
+ " c: d\n";
+
+const char *tab_error1 =
+ "a:\n"
+ "b:\t\n";
+
+const char *tab_error2 =
+ "a:\n"
+ "b: c\t\n";
+
+const char *tab_error3 =
+ "a:\n"
+ "\t\n";
+
+const char *dname_ok =
+ ".:\n"
+ "dom-ain:\n"
+ "\\070-\\071.\\072.:\n"
+ "*.wildchar.com:\n"
+ "_ldap._tcp.example.com:\n";
+
+const char *quotes_ok =
+ "g: \"\"\n"
+ "g: a\\ b\n"
+ "g: \"\\# 1 00\"\n"
+ "g: \"\\\"\\\"\"\n"
+ "g: \" a \\\" b \\\" \\\"c\\\" \"\n"
+ "g: \"\\@ \\[ \\# \\, \\]\"\n";
+
+const char *utf8_ok =
+ "key: příšera\n";
+
+static void test_syntax_ok(yp_parser_t *yp)
+{
+ // OK input.
+ int ret = yp_set_input_string(yp, syntax_ok, strlen(syntax_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 3;
+ for (int i = 0; i < 3; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'a' &&
+ yp->data_len == 0 && yp->event == YP_EKEY0 &&
+ yp->line_count == line + i, "compare %i. key0", i);
+ }
+
+ line += 4;
+ for (int i = 0; i < 6; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'b' &&
+ yp->data_len == 1 && yp->data[0] == 'b' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with value", i);
+ }
+
+ line += 7;
+ for (int i = 0; i < 7; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key1 with value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'f' &&
+ yp->data_len == 1 && yp->data[0] == 'f' &&
+ yp->event == YP_EKEY1 && yp->line_count == line + i,
+ "compare %i. key1 with value", i);
+ }
+
+ line += 8;
+ for (int i = 0; i < 6; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with first value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'c' &&
+ yp->data_len == 1 && yp->data[0] == 'a' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with first value", i);
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key0 with second value", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'c' &&
+ yp->data_len == 1 && yp->data[0] == 'b' &&
+ yp->event == YP_EKEY0 && yp->line_count == line + i,
+ "compare %i. key0 with second value", i);
+ }
+
+ line += 7;
+ for (int i = 0; i < 2; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. id", i);
+ ok(yp->key_len == 1 && yp->key[0] == 'd' &&
+ yp->data_len == 1 && yp->data[0] == 'd' &&
+ yp->event == YP_EID && yp->line_count == line + i,
+ "compare %i. id", i);
+ }
+
+ line += 3;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0 with quoted value");
+ ok(yp->key_len == 1 && yp->key[0] == 'e' && yp->data_len == 10 &&
+ memcmp(yp->data, "a#b' c[d,]", yp->data_len) == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 with quoted value");
+
+ line += 2;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ok(yp->key_len == 4 && strcmp(yp->key, "zone") == 0 &&
+ yp->data_len == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 value");
+
+ line += 3;
+ for (int i = 0; i < 2; i++) {
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. id", i);
+ ok(yp->key_len == 6 && strcmp(yp->key, "domain") == 0 &&
+ yp->data_len == 8 && strcmp(yp->data, "example.") == 0 &&
+ yp->event == YP_EID && yp->line_count == line + 2 * i,
+ "compare id");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse %i. key1", i);
+ ok(yp->key_len == 6 && strcmp(yp->key, "master") == 0 &&
+ yp->data_len == 4 && strcmp(yp->data, "bind") == 0 &&
+ yp->event == YP_EKEY1 && yp->line_count == line + 2 * i + 1,
+ "compare key1");
+ }
+
+ line += 4;
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ok(yp->key_len == 5 && strcmp(yp->key, "zone2") == 0 &&
+ yp->data_len == 0 &&
+ yp->event == YP_EKEY0 && yp->line_count == line,
+ "compare key0 value");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key1");
+ ok(yp->key_len == 1 && strcmp(yp->key, "a") == 0 &&
+ yp->data_len == 1 && strcmp(yp->data, "b") == 0 &&
+ yp->event == YP_EID && yp->line_count == line + 1,
+ "compare key1 value");
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOF, ret, "parse EOF");
+}
+
+static void test_syntax_error(yp_parser_t *yp, const char *input)
+{
+ static int count = 1;
+
+ int ret = yp_set_input_string(yp, input, strlen(input));
+ is_int(KNOT_EOK, ret, "set syntax error input string %i", count++);
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key1");
+ ret = yp_parse(yp);
+ is_int(KNOT_YP_EINVAL_INDENT, ret, "parse key1 - invalid indentation");
+ is_int(yp->line_count, 3, "invalid indentation line");
+}
+
+static void test_tab_error(yp_parser_t *yp, const char *input)
+{
+ static int count = 1;
+
+ int ret = yp_set_input_string(yp, input, strlen(input));
+ is_int(KNOT_EOK, ret, "set tab error input string %i", count++);
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key0");
+ ret = yp_parse(yp);
+ is_int(KNOT_YP_ECHAR_TAB, ret, "invalid tabulator");
+ is_int(yp->line_count, 2, "invalid tabulator line");
+}
+
+static void test_dname(yp_parser_t *yp)
+{
+#define CHECK_DNAME(str) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse dname " str); \
+ ok(yp->key_len == strlen(str) && strcmp(yp->key, str) == 0 && yp->data_len == 0 && \
+ yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
+
+ // Dname key value.
+ int ret = yp_set_input_string(yp, dname_ok, strlen(dname_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 1;
+ CHECK_DNAME(".");
+ CHECK_DNAME("dom-ain");
+ CHECK_DNAME("\\070-\\071.\\072.");
+ CHECK_DNAME("*.wildchar.com");
+ CHECK_DNAME("_ldap._tcp.example.com");
+}
+
+static void test_quotes(yp_parser_t *yp)
+{
+#define CHECK_QUOTE(str) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse quoted " str); \
+ ok(yp->key_len == 1 && yp->key[0] == 'g' && \
+ yp->data_len == strlen(str) && strcmp(yp->data, str) == 0 && \
+ yp->event == YP_EKEY0 && yp->line_count == line++, "compare " str);
+
+ int ret = yp_set_input_string(yp, quotes_ok, strlen(quotes_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ size_t line = 1;
+ CHECK_QUOTE("");
+ CHECK_QUOTE("a\\ b");
+ CHECK_QUOTE("\\# 1 00");
+ CHECK_QUOTE("\"\"");
+ CHECK_QUOTE(" a \" b \" \"c\" ");
+ CHECK_QUOTE("\\@ \\[ \\# \\, \\]");
+}
+
+// Check that wrong wildcard dname is NOT parsed as valid dname.
+static void test_wildcard(yp_parser_t *yp)
+{
+#define CHECK_NOT_WILDCARD(str) \
+ ret = yp_set_input_string(yp, str, strlen(str)); \
+ is_int(KNOT_EOK, ret, "set input string");\
+ ret = yp_parse(yp); \
+ is_int(KNOT_EPARSEFAIL, ret, str " is not wildcard"); \
+ ok(yp->key_len != strlen(str) || strcmp(yp->key, str) != 0 || \
+ yp->event != YP_EKEY0, "compare " str);
+
+ int ret;
+ CHECK_NOT_WILDCARD("a.*.example.com.");
+ CHECK_NOT_WILDCARD("**.example.com.");
+ CHECK_NOT_WILDCARD("*example.com.");
+}
+
+static void test_utf8(yp_parser_t *yp)
+{
+ int ret = yp_set_input_string(yp, utf8_ok, strlen(utf8_ok));
+ is_int(KNOT_EOK, ret, "set input string");
+
+ ret = yp_parse(yp);
+ is_int(KNOT_EOK, ret, "parse key with a value in UTF-8");
+ ok(yp->key_len == 3 && strcmp(yp->key, "key") == 0 &&
+ yp->data_len == 10 && strcmp(yp->data, "p""\xC5\x99\xC3\xAD\xC5\xA1""era") == 0,
+ "compare UTF-8 value");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ yp_parser_t yp;
+ yp_init(&yp);
+
+ test_syntax_ok(&yp);
+ test_syntax_error(&yp, syntax_error1);
+ test_syntax_error(&yp, syntax_error2);
+ test_syntax_error(&yp, syntax_error3);
+ test_tab_error(&yp, tab_error1);
+ test_tab_error(&yp, tab_error2);
+ test_tab_error(&yp, tab_error3);
+ test_dname(&yp);
+ test_quotes(&yp);
+ test_wildcard(&yp);
+ test_utf8(&yp);
+
+ yp_deinit(&yp);
+
+ return 0;
+}
diff --git a/tests/libknot/test_ypschema.c b/tests/libknot/test_ypschema.c
new file mode 100644
index 0000000..728a847
--- /dev/null
+++ b/tests/libknot/test_ypschema.c
@@ -0,0 +1,417 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/ypschema.h"
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/libknot.h"
+
+#define C_ID "\x02""id"
+#define C_INT "\x07""integer"
+#define C_BOOL "\x04""bool"
+#define C_OPT "\x06""option"
+#define C_STR "\x06""string"
+#define C_ADDR "\x07""address"
+#define C_DNAME "\x05""dname"
+#define C_HEX "\x03""hex"
+#define C_BASE64 "\x06""base64"
+#define C_DATA "\x04""data"
+#define C_REF "\x09""reference"
+#define C_GRP "\x05""group"
+#define C_MULTIGRP "\x0B""multi-group"
+
+static const yp_item_t group[] = {
+ { C_INT, YP_TINT, YP_VINT = { 0, 100, YP_NIL } },
+ { C_STR, YP_TSTR, YP_VNONE, YP_FMULTI },
+ { NULL }
+};
+
+static const yp_item_t multi_group[] = {
+ { C_ID, YP_TSTR, YP_VNONE },
+ { C_HEX, YP_THEX, YP_VNONE },
+ { C_BASE64, YP_TB64, YP_VNONE },
+ { NULL }
+};
+
+static const knot_lookup_t opts[] = {
+ { 1, "one" },
+ { 10, "ten" },
+ { 0, NULL }
+ };
+
+static const yp_item_t static_schema[] = {
+ { C_OPT, YP_TOPT, YP_VOPT = { opts } },
+ { C_BOOL, YP_TBOOL, YP_VNONE },
+ { C_DNAME, YP_TDNAME, YP_VNONE },
+ { C_GRP, YP_TGRP, YP_VGRP = { group } },
+ { C_MULTIGRP, YP_TGRP, YP_VGRP = { multi_group }, YP_FMULTI },
+ { C_REF, YP_TREF, YP_VREF = { C_MULTIGRP } },
+ { C_DATA, YP_TDATA, YP_VNONE },
+ { NULL }
+};
+
+static void schema_find_test(void)
+{
+ yp_item_t *schema = NULL;
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+
+ const yp_item_t *i = yp_schema_find(C_OPT, NULL, schema);
+ ok(i != NULL, "schema find");
+ if (i == NULL) {
+ goto error_schema;
+ }
+ ok(strcmp(&i->name[1], &C_OPT[1]) == 0, "name check");
+
+ i = yp_schema_find(C_STR, C_GRP, schema);
+ ok(i != NULL, "schema find with parent");
+ if (i == NULL) {
+ goto error_schema;
+ }
+ ok(strcmp(&i->name[1], &C_STR[1]) == 0, "name check");
+
+ i = yp_schema_find(C_ADDR, NULL, schema);
+ ok(i == NULL, "schema not find");
+
+ i = yp_schema_find(C_ADDR, C_GRP, schema);
+ ok(i == NULL, "schema not find with parent");
+
+error_schema:
+ yp_schema_free(schema);
+}
+
+static void schema_merge_test(void)
+{
+ static const yp_item_t items1[] = {
+ { "\x01""1", YP_TSTR, YP_VNONE },
+ { "\x01""2", YP_TSTR, YP_VNONE },
+ { NULL }
+ };
+
+ static const yp_item_t items2[] = {
+ { "\x01""3", YP_TSTR, YP_VNONE },
+ { "\x01""4", YP_TSTR, YP_VNONE },
+ { NULL }
+ };
+
+ yp_item_t *schema = NULL;
+ yp_item_t *tmp = NULL;
+
+ int ret = yp_schema_copy(&tmp, items1);
+ is_int(KNOT_EOK, ret, "schema copy");
+
+ ret = yp_schema_merge(&schema, items1, items2);
+ is_int(KNOT_EOK, ret, "schema merge");
+
+ yp_schema_free(tmp);
+
+ for (uint8_t i = 0; i < 4; i++) {
+ yp_name_t name[3] = { '\x01', '1' + i };
+ const yp_item_t *item = yp_schema_find(name, NULL, schema);
+ ok(item != NULL, "schema find");
+ }
+
+ yp_schema_free(schema);
+}
+
+#define SET_INPUT_STR(str) \
+ ret = yp_set_input_string(yp, str, strlen(str)); \
+ is_int(KNOT_EOK, ret, "set input string");
+
+#define PARSER_CHECK(depth) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse"); \
+ ret = yp_schema_check_parser(ctx, yp); \
+ is_int(KNOT_EOK, ret, "check parser"); \
+ node = &ctx->nodes[ctx->current]; \
+ parent = node->parent; \
+ ok(ctx->current == depth, "depth check");
+
+#define PARSER_RET_CHECK(code) \
+ ret = yp_parse(yp); \
+ is_int(KNOT_EOK, ret, "parse"); \
+ ret = yp_schema_check_parser(ctx, yp); \
+ ok(ret == code, "return check parser");
+
+static void parser_test(void)
+{
+ yp_parser_t yparser;
+ yp_parser_t *yp = &yparser;
+ yp_item_t *schema = NULL;
+ yp_check_ctx_t *ctx = NULL;
+
+ yp_init(yp);
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+ if (ret != KNOT_EOK) {
+ goto error_parser;
+ }
+
+ ctx = yp_schema_check_init(&schema);
+ ok(ctx != NULL, "create check ctx");
+ if (ctx == NULL) {
+ goto error_parser;
+ }
+
+ yp_node_t *node;
+ yp_node_t *parent;
+ const yp_item_t *id;
+
+ diag("parser key0 test");
+ SET_INPUT_STR("option: one");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "option") == 0, "name check");
+ ok(node->item->type == YP_TOPT, "type check");
+ ok(yp_opt(node->data) == 1, "value check");
+
+ diag("parser group test");
+ SET_INPUT_STR("group:\n integer: 20\n string: [short, \"long string\"]");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
+ ok(node->item->type == YP_TINT, "type check");
+ ok(yp_int(node->data) == 20, "value check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "short") == 0, "value check");
+ PARSER_CHECK(1);
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
+
+ diag("parser multi-group test");
+ SET_INPUT_STR("multi-group:\n - id: foo\n base64: Zm9vYmFy\nreference: foo");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ PARSER_CHECK(0);
+ ok(node->id_len > 0, "id check");
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ id = node->item->var.g.id;
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
+ PARSER_CHECK(1);
+ id = parent->item->var.g.id;
+ ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
+ ok(parent->item->type == YP_TGRP, "type check");
+ ok(parent->data_len == 0, "value length check");
+ ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
+ ok(node->item->type == YP_TB64, "type check");
+ ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
+ "value check");
+ ok(node->id_len == 0, "id length check");
+ PARSER_CHECK(0);
+ ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
+ ok(node->item->type == YP_TREF, "type check");
+ ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
+
+ diag("parser check return");
+ SET_INPUT_STR("unknown:");
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n unknown:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n - unknown: data");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("group:\n - hex: data");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_EINVAL_ITEM);
+
+ SET_INPUT_STR("dname:");
+ PARSER_RET_CHECK(KNOT_EINVAL);
+
+ SET_INPUT_STR("group: data");
+ PARSER_RET_CHECK(KNOT_YP_ENOTSUP_DATA);
+
+ SET_INPUT_STR("group:\n integer:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_EINVAL);
+
+ SET_INPUT_STR("multi-group:\n id:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_ENODATA);
+
+ SET_INPUT_STR("multi-group:\n hex:");
+ PARSER_RET_CHECK(KNOT_EOK);
+ PARSER_RET_CHECK(KNOT_YP_ENOID);
+
+error_parser:
+ yp_schema_check_deinit(ctx);
+ yp_schema_free(schema);
+ yp_deinit(yp);
+}
+
+#define STR_CHECK(depth, key0, key1, id, data) \
+ ret = yp_schema_check_str(ctx, key0, key1, id, data); \
+ is_int(KNOT_EOK, ret, "check str"); \
+ ok(ctx->current == depth, "depth check"); \
+ node = &ctx->nodes[ctx->current]; \
+ parent = node->parent;
+
+#define STR_RET_CHECK(code, key0, key1, id, data) \
+ ret = yp_schema_check_str(ctx, key0, key1, id, data); \
+ ok(ret == code, "return check str");
+
+static void str_test(void)
+{
+ yp_item_t *schema;
+ yp_check_ctx_t *ctx = NULL;
+
+ int ret = yp_schema_copy(&schema, static_schema);
+ is_int(KNOT_EOK, ret, "schema copy");
+ if (ret != KNOT_EOK) {
+ goto error_str;
+ }
+
+ ctx = yp_schema_check_init(&schema);
+ ok(ctx != NULL, "create check ctx");
+ if (ctx == NULL) {
+ goto error_str;
+ }
+
+ yp_node_t *node;
+ yp_node_t *parent;
+ const yp_item_t *id;
+
+ diag("str key0 test");
+ STR_CHECK(0, "option", NULL, NULL, "one");
+ ok(strcmp(node->item->name + 1, "option") == 0, "name check");
+ ok(node->item->type == YP_TOPT, "type check");
+ ok(yp_opt(node->data) == 1, "value check");
+
+ diag("str group test");
+ STR_CHECK(0, "group", NULL, NULL, NULL);
+ ok(strcmp(node->item->name + 1, "group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ STR_CHECK(1, "group", "integer", NULL, "20");
+ ok(strcmp(node->item->name + 1, "integer") == 0, "name check");
+ ok(node->item->type == YP_TINT, "type check");
+ ok(yp_int(node->data) == 20, "value check");
+ STR_CHECK(1, "group", "string", NULL, "short");
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "short") == 0, "value check");
+ STR_CHECK(1, "group", "string", NULL, "long string");
+ ok(strcmp(node->item->name + 1, "string") == 0, "name check");
+ ok(node->item->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->data), "long string") == 0, "value check");
+
+ diag("str multi-group test");
+ STR_CHECK(0, "multi-group", NULL, NULL, NULL);
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ STR_CHECK(0, "multi-group", NULL, "foo", NULL);
+ ok(node->id_len > 0, "id check");
+ ok(strcmp(node->item->name + 1, "multi-group") == 0, "name check");
+ ok(node->item->type == YP_TGRP, "type check");
+ ok(node->data_len == 0, "value length check");
+ id = node->item->var.g.id;
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(yp_str(node->id), "foo") == 0, "value check");
+ STR_CHECK(1, "multi-group", "base64", "foo", "Zm9vYmFy");
+ id = parent->item->var.g.id;
+ ok(strcmp(parent->item->name + 1, "multi-group") == 0, "name check");
+ ok(parent->item->type == YP_TGRP, "type check");
+ ok(parent->data_len == 0, "value length check");
+ ok(strcmp(yp_str(parent->id), "foo") == 0, "value check");
+ ok(strcmp(id->name + 1, "id") == 0, "name check");
+ ok(id->type == YP_TSTR, "type check");
+ ok(strcmp(node->item->name + 1, "base64") == 0, "name check");
+ ok(node->item->type == YP_TB64, "type check");
+ ok(memcmp(yp_bin(node->data), "foobar", yp_bin_len(node->data)) == 0,
+ "value check");
+ ok(node->id_len == 0, "id length check");
+ STR_CHECK(0, "reference", NULL, NULL, "foo");
+ ok(strcmp(node->item->name + 1, "reference") == 0, "name check");
+ ok(node->item->type == YP_TREF, "type check");
+ ok(strcmp(yp_str(node->data), "foo") == 0, "value check");
+
+ diag("str check return");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "", "", "", "");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, NULL, NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "unknown", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, NULL, "unknown", NULL, NULL);
+ STR_RET_CHECK(KNOT_EINVAL, "dname", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "dname", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "dname", NULL, NULL, ".");
+ STR_RET_CHECK(KNOT_EINVAL, "dname", NULL, NULL, "..");
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "dname", NULL, "id", NULL);
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "dname", "unknown", NULL, NULL);
+
+ STR_RET_CHECK(KNOT_EOK, "group", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "group", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "group", "", "", "data");
+ STR_RET_CHECK(KNOT_YP_EINVAL_ITEM, "group", "unknown", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "group", "string", NULL, "");
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "group", "", "id", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_ID, "group", "string", "id", NULL);
+
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "", "", "");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", NULL, NULL, NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL, NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", NULL, "idval", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", NULL, "idval", "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", "idval", NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", "idval", "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", NULL, NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "hex", NULL, "data");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", "", NULL);
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", NULL, "idval");
+ STR_RET_CHECK(KNOT_EOK, "multi-group", "id", "idval", NULL);
+ STR_RET_CHECK(KNOT_YP_ENOTSUP_DATA, "multi-group", "id", "idval", "data");
+
+error_str:
+ yp_schema_check_deinit(ctx);
+ yp_schema_free(schema);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ schema_find_test();
+ schema_merge_test();
+ parser_test();
+ str_test();
+
+ return 0;
+}
diff --git a/tests/libknot/test_yptrafo.c b/tests/libknot/test_yptrafo.c
new file mode 100644
index 0000000..cca9635
--- /dev/null
+++ b/tests/libknot/test_yptrafo.c
@@ -0,0 +1,404 @@
+/* Copyright (C) 2015 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 <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <tap/basic.h>
+
+#include "libknot/yparser/yptrafo.h"
+#include "libknot/libknot.h"
+
+static void int_test(const char *txt, int64_t num, yp_style_t s,
+ int64_t min, int64_t max)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
+
+ diag("integer \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_int(b) == num, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, s | YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void int_bad_test(const char *txt, int code, yp_style_t s,
+ int64_t min, int64_t max)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TINT, YP_VINT = { min, max, YP_NIL, s } };
+
+ diag("integer \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void bool_test(const char *txt, bool val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
+
+ diag("boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_bool(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void bool_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TBOOL, YP_VNONE };
+
+ diag("boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void opt_test(const char *txt, unsigned val, const knot_lookup_t *opts)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
+
+ diag("option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(b_len == 1, "compare length");
+ ok(yp_opt(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void opt_bad_test(const char *txt, int code, const knot_lookup_t *opts)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TOPT, YP_VOPT = { opts } };
+
+ diag("option \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void str_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TSTR, YP_VNONE };
+
+ diag("string \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(b_len == strlen(txt) + 1, "compare length");
+ ok(memcmp(yp_str(b), val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_test(const char *txt, bool port)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
+
+ diag("address \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ bool no_port;
+ yp_addr(b, &no_port);
+ ok(no_port == port, "compare port presence");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TADDR, YP_VNONE };
+
+ diag("address \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void addr_range_test(const char *txt)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TNET, YP_VNONE };
+
+ diag("address range \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void addr_range_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_TNET, YP_VNONE };
+
+ diag("address range \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void dname_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TDNAME, YP_VNONE };
+
+ diag("dname \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_dname(b), val, b_len) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void hex_test(const char *txt, const char *val, const char *txt_out)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_THEX, YP_VNONE };
+
+ if (txt_out == NULL) {
+ txt_out = txt;
+ }
+
+ diag("hex \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt_out) == t_len, "txt length");
+ ok(memcmp(txt_out, t, t_len) == 0, "compare");
+}
+
+static void hex_bad_test(const char *txt, int code)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ yp_item_t i = { NULL, YP_THEX, YP_VNONE };
+
+ diag("hex \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ ok(ret == code, "invalid txt to bin");
+}
+
+static void base64_test(const char *txt, const char *val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t i = { NULL, YP_TB64, YP_VNONE };
+
+ diag("base64 \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(memcmp(yp_bin(b), val, yp_bin_len(b)) == 0, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+static void ref_test(const char *txt, bool val)
+{
+ int ret;
+ uint8_t b[64];
+ size_t b_len = sizeof(b);
+ char t[64];
+ size_t t_len = sizeof(t);
+ yp_item_t id = { NULL, YP_TBOOL, YP_VNONE };
+ yp_item_t ref = { NULL, YP_TGRP, YP_VNONE };
+ yp_item_t i = { NULL, YP_TREF, YP_VNONE };
+ ref.var.g.id = &id;
+ i.var.r.ref = &ref;
+
+ diag("reference to boolean \"%s\":", txt);
+ ret = yp_item_to_bin(&i, txt, strlen(txt), b, &b_len);
+ is_int(KNOT_EOK, ret, "txt to bin");
+ ok(yp_bool(b) == val, "compare");
+ ret = yp_item_to_txt(&i, b, b_len, t, &t_len, YP_SNOQUOTE);
+ is_int(KNOT_EOK, ret, "bin to txt");
+ ok(strlen(t) == t_len, "txt ret length");
+ ok(strlen(txt) == t_len, "txt length");
+ ok(memcmp(txt, t, t_len) == 0, "compare");
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ /* Integer tests. */
+ int64_t min = -20000000000, max = 20000000000;
+ int_test("5", 5, YP_SNONE, min, max);
+ int_test("0", 0, YP_SNONE, min, max);
+ int_test("-5", -5, YP_SNONE, min, max);
+ int_test("20000000000", max, YP_SNONE, min, max);
+ int_test("-20000000000", min, YP_SNONE, min, max);
+ int_test("11B", 11LL * 1, YP_SSIZE, min, max);
+ int_test("11K", 11LL * 1024, YP_SSIZE, min, max);
+ int_test("11M", 11LL * 1024 * 1024, YP_SSIZE, min, max);
+ int_test("11G", 11LL * 1024 * 1024 * 1024, YP_SSIZE, min, max);
+ int_test("11s", 11LL * 1, YP_STIME, min, max);
+ int_test("11m", 11LL * 60, YP_STIME, min, max);
+ int_test("11h", 11LL * 3600, YP_STIME, min, max);
+ int_test("11d", 11LL * 24 * 3600, YP_STIME, min, max);
+ int_test("1025B", 1025LL, YP_SSIZE, min, max);
+ int_test("61s", 61LL, YP_STIME, min, max);
+ int_bad_test("20000000001", KNOT_ERANGE, YP_SNONE, min, max);
+ int_bad_test("-20000000001", KNOT_ERANGE, YP_SNONE, min, max);
+ int_bad_test("1x", KNOT_EINVAL, YP_SNONE, min, max);
+ int_bad_test("1sx", KNOT_EINVAL, YP_STIME, min, max);
+
+ /* Boolean tests. */
+ bool_test("on", true);
+ bool_test("off", false);
+ bool_bad_test("onx", KNOT_EINVAL);
+ bool_bad_test("enable", KNOT_EINVAL);
+
+ /* Option tests. */
+ static const knot_lookup_t opts[] = {
+ { 1, "one" },
+ { 10, "ten" },
+ { 255, "max" },
+ { 0, NULL }
+ };
+ opt_test("one", 1, opts);
+ opt_test("ten", 10, opts);
+ opt_test("max", 255, opts);
+ opt_bad_test("onex", KNOT_EINVAL, opts);
+ opt_bad_test("word", KNOT_EINVAL, opts);
+
+ /* String tests. */
+ str_test("Test string!", "Test string!");
+
+ /* Address tests. */
+ addr_test("192.168.123.1", true);
+ addr_test("192.168.123.1@12345", false);
+ addr_test("2001:db8::1", true);
+ addr_test("::1@12345", false);
+ addr_test("/tmp/test.sock", true);
+ addr_test("eth1@53", true);
+ addr_bad_test("192.168.123.x", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@1x", KNOT_EINVAL);
+ addr_bad_test("192.168.123.1@65536", KNOT_ERANGE);
+
+ /* Address range tests. */
+ addr_range_test("1.1.1.1");
+ addr_range_test("1.1.1.1/0");
+ addr_range_test("1.1.1.1/32");
+ addr_range_test("1.1.1.1-1.2.3.4");
+ addr_range_test("::1");
+ addr_range_test("::1/0");
+ addr_range_test("::1/32");
+ addr_range_test("1::-5::");
+ addr_range_bad_test("unix", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1/", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1/33", KNOT_ERANGE);
+ addr_range_bad_test("1.1.1.1-", KNOT_EINVAL);
+ addr_range_bad_test("1.1.1.1-::1", KNOT_EINVAL);
+
+ /* Dname tests. */
+ dname_test("example.com.", "\x07""example""\x03""com""\x00");
+
+ /* Hex tests. */
+ hex_test("", "", NULL);
+ hex_test("0x", "", "");
+ hex_test("Hello World!", "Hello World!", NULL);
+ hex_test("0x0155FF", "\x01\x55\xFF", NULL);
+ hex_bad_test("0xA", KNOT_EINVAL);
+
+ /* Base64 tests. */
+ base64_test("Zm9vYmFy", "foobar");
+
+ /* Ref tests. */
+ ref_test("on", true);
+
+ return 0;
+}
diff --git a/tests/libzscanner/TESTS b/tests/libzscanner/TESTS
new file mode 100644
index 0000000..227cdb4
--- /dev/null
+++ b/tests/libzscanner/TESTS
@@ -0,0 +1,86 @@
+00-0_general
+00-1_general
+00-2_general
+00-3_general
+00-4_general
+01_owner
+02_class
+03_rrttl
+04-0_ORIGIN
+04-1_ORIGIN
+04-2_ORIGIN
+04-3_ORIGIN
+04-4_ORIGIN
+04-5_ORIGIN
+04-6_ORIGIN
+04-7_ORIGIN
+04-8_ORIGIN
+04-9_ORIGIN
+05-0_TTL
+05-1_TTL
+05-2_TTL
+05-3_TTL
+05-4_TTL
+06-0_INCLUDE
+06-1_INCLUDE
+06-2_INCLUDE
+06-3_INCLUDE
+06-4_INCLUDE
+06-5_INCLUDE
+06-6_INCLUDE
+06-7_INCLUDE
+06-8_INCLUDE
+07-0-rdata
+07-1-rdata
+07-2-rdata
+07-3-rdata
+07-4-rdata
+10_A
+11_AAAA
+12_TXT
+13_SPF
+14_NS
+15_CNAME
+16_PTR
+17_DNAME
+18_MX
+19_AFSDB
+20_RT
+21_KX
+22_HINFO
+23_MINFO
+24_RP
+25_SOA
+26_SRV
+27_NAPTR
+28_TYPE
+29_CERT
+30_KEY
+31_DNSKEY
+32_APL
+33_DS
+34_SSHFP
+35_IPSECKEY
+36_RRSIG
+37_NSEC
+38_DHCID
+39_NSEC3
+40_NSEC3PARAM
+41_TLSA
+42_LOC
+43_EUI48
+44_EUI64
+45_NID
+46_L32
+47_L64
+48_LP
+49_CDS
+50_CDNSKEY
+51_URI
+52_CAA
+53_SMIMEA
+54_OPENPGPKEY
+55_CSYNC
+56_ZONEMD
+57_SVCB
+58_HTTPS
diff --git a/tests/libzscanner/data/00-0_general.in b/tests/libzscanner/data/00-0_general.in
new file mode 100644
index 0000000..21e9d03
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.in
@@ -0,0 +1,27 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+t01 IN 10 NS @ ; All items are mentioned
+ IN 10 NS @ ; Same as above without OWNER
+t02 20 IN NS @ ; Switched CLASS and TTL
+ 20 IN NS @ ; Same as above without OWNER
+t03 30 NS @ ; Missing CLASS
+ 30 NS @ ; Same as above without OWNER
+t04 IN NS @ ; Missing TTL
+ IN NS @ ; Same as above without OWNER
+t05 NS @ ; Missing CLASS and TTL
+ NS @ ; Same as above without OWNER
+
+@ ( ) NS ( ; Multiline 1/5
+ ) () ( ; Multiline 2/5
+
+ @ ; Multiline 4/5
+ ) (
+) ; Multiline 6/6
+
+; KO
+@ A (
+ 1.1.1
+ ) ; Error in multiline
+@ NS ((@)) ; Nested parentheses - ERROR = STOP PROCESSING!
diff --git a/tests/libzscanner/data/00-0_general.out b/tests/libzscanner/data/00-0_general.out
new file mode 100644
index 0000000..522b3a6
--- /dev/null
+++ b/tests/libzscanner/data/00-0_general.out
@@ -0,0 +1,70 @@
+OWNER=0374303100
+CLASS=0001
+RRTTL=0000000A
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303100
+CLASS=0001
+RRTTL=0000000A
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303200
+CLASS=0001
+RRTTL=00000014
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303200
+CLASS=0001
+RRTTL=00000014
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303300
+CLASS=0001
+RRTTL=0000001E
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303300
+CLASS=0001
+RRTTL=0000001E
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0374303500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_IPV4
+------
+ERROR=ZS_LEFT_PARENTHESIS
+------
diff --git a/tests/libzscanner/data/00-1_general.in b/tests/libzscanner/data/00-1_general.in
new file mode 100644
index 0000000..2423909
--- /dev/null
+++ b/tests/libzscanner/data/00-1_general.in
@@ -0,0 +1,7 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+@ TXT ( "text" ; Unclosed multiline block
+
+;comment
diff --git a/tests/libzscanner/data/00-1_general.out b/tests/libzscanner/data/00-1_general.out
new file mode 100644
index 0000000..85b0653
--- /dev/null
+++ b/tests/libzscanner/data/00-1_general.out
@@ -0,0 +1,2 @@
+ERROR=ZS_UNCLOSED_MULTILINE
+------
diff --git a/tests/libzscanner/data/00-2_general.in b/tests/libzscanner/data/00-2_general.in
new file mode 100644
index 0000000..460eeb5
--- /dev/null
+++ b/tests/libzscanner/data/00-2_general.in
@@ -0,0 +1 @@
+$ORIGIN .
diff --git a/tests/libzscanner/data/00-2_general.out b/tests/libzscanner/data/00-2_general.out
new file mode 100644
index 0000000..79776a7
--- /dev/null
+++ b/tests/libzscanner/data/00-2_general.out
@@ -0,0 +1,2 @@
+ERROR=ZS_DOS_NEWLINE
+------
diff --git a/tests/libzscanner/data/00-3_general.in b/tests/libzscanner/data/00-3_general.in
new file mode 100644
index 0000000..6ce107c
--- /dev/null
+++ b/tests/libzscanner/data/00-3_general.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+$TTL 1
+
+. NS @ ; No newline \ No newline at end of file
diff --git a/tests/libzscanner/data/00-3_general.out b/tests/libzscanner/data/00-3_general.out
new file mode 100644
index 0000000..bc06672
--- /dev/null
+++ b/tests/libzscanner/data/00-3_general.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
diff --git a/tests/libzscanner/data/00-4_general.in b/tests/libzscanner/data/00-4_general.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/00-4_general.in
diff --git a/tests/libzscanner/data/00-4_general.out b/tests/libzscanner/data/00-4_general.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/00-4_general.out
diff --git a/tests/libzscanner/data/01_owner.in b/tests/libzscanner/data/01_owner.in
new file mode 100644
index 0000000..0108be3
--- /dev/null
+++ b/tests/libzscanner/data/01_owner.in
@@ -0,0 +1,37 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+. NS @ ; The simplest owner
+tld. NS @ ; FQD tld owner
+tld NS @ ; Relative form
+ NS @ ; The previous owner
+*. NS @ ; FQD with asterisk
+* NS @ ; Alone asterisk
+*.* NS @ ; More asterisks
+*a.a*a.** NS @ ; Also possible
+@ NS @ ; Use origin
+0123456789 NS @ ; Digits
+0/25.2.0.192.in-addr.arpa. NS @ ; CIDR notation
+_a_.-b-c-./d/. NS @ ; Allowed characters '_' '-' '/' anywhere
+ABCDEFGHIJKLMNOPQRSTUVWXYZ NS @ ; All upper-case letters
+abcdefghijklmnopqrstuvwxyz NS @ ; All lower-case letters
+\000\0320\ \\\"\.\@\*.tld. NS @ ; Label with special chars
+b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. NS @ ; IPv6 reverse
+12345678901234567890123456789012345678901234567890123456789012\051.tld. NS @ ; Label of maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. NS @ ; Domain name of maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901 NS @ ; Domain name of maximal length (after appending origin)
+
+; KO
+& NS @ ; Bad (unslashed) character
+ NS @ ; Bad previous
+.a NS @ ; Leading dot
+@@ NS @ ; Double @@
+.. NS @ ; Missing label between dots
+\1 NS @ ; Slash notation requires 3 digits
+\12 NS @ ; Slash notation requires 3 digits
+12345678901234567890123456789012345678901234567890123456789012\0514.tld. NS @ ; Label exceeded maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. NS @ ; Domain name exceeded maximal length
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012 NS @ ; Domain name exceeded maximal length (after appending origin)
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.1 NS @ ; Domain name exceeded maximal length (maximal dname length check is after each valid label)
+123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901.123456789012345678901234567890123456789012345678901234567890123 NS @ ; Domain name exceeded maximal length (maximal dname length check is after each valid label)
diff --git a/tests/libzscanner/data/01_owner.out b/tests/libzscanner/data/01_owner.out
new file mode 100644
index 0000000..26a8454
--- /dev/null
+++ b/tests/libzscanner/data/01_owner.out
@@ -0,0 +1,138 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=012A012A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=022A6103612A61022A2A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0A3031323334353637383900
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=04302F3235013201300331393207696E2D61646472046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=035F615F052D622D632D032F642F00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=1A4142434445464748494A4B4C4D4E4F505152535455565758595A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=1A6162636465666768696A6B6C6D6E6F707172737475767778797A00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=09002030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_OWNER
+------
+WARNG=ZS_BAD_PREVIOUS_OWNER
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_LABEL_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
diff --git a/tests/libzscanner/data/02_class.in b/tests/libzscanner/data/02_class.in
new file mode 100644
index 0000000..c4347e7
--- /dev/null
+++ b/tests/libzscanner/data/02_class.in
@@ -0,0 +1,10 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ IN NS @ ; The only accepted class IN
+@ in NS @ ; Class in lower-case
+
+; KO
+@ CH NS @ ; Unsupported class
+@ CLASS1 NS @ ; Unsupported notation
diff --git a/tests/libzscanner/data/02_class.out b/tests/libzscanner/data/02_class.out
new file mode 100644
index 0000000..69daae3
--- /dev/null
+++ b/tests/libzscanner/data/02_class.out
@@ -0,0 +1,16 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
diff --git a/tests/libzscanner/data/03_rrttl.in b/tests/libzscanner/data/03_rrttl.in
new file mode 100644
index 0000000..7c57d73
--- /dev/null
+++ b/tests/libzscanner/data/03_rrttl.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ 0 NS @ ; Minimal ttl
+@ 3600 NS @ ; Inner value
+@ 4294967295 NS @ ; Maximal ttl
+@ 1S NS @ ; 1 second (upper-case)
+@ 1s NS @ ; 1 second (lower-case)
+@ 1M NS @ ; 1 minute (upper-case)
+@ 1m NS @ ; 1 minute (lower-case)
+@ 1H NS @ ; 1 hour (upper-case)
+@ 1h NS @ ; 1 hour (lower-case)
+@ 1D NS @ ; 1 day (upper-case)
+@ 1d NS @ ; 1 day (lower-case)
+@ 1W NS @ ; 1 week (upper-case)
+@ 1w NS @ ; 1 week (lower-case)
+@ 1w1d1h1m1s NS @ ; More time units
+@ 1s1m1m NS @ ; Same time units, non decreasing order
+
+; KO
+@ -1 NS @ ; Negative ttl
+@ 4294967296 NS @ ; 32bit overflow
+@ 100000000W NS @ ; 32bit overflow
+@ 4294967295s1w NS @ ; 32bit overflow
+@ 1x NS @ ; Unknown time unit
diff --git a/tests/libzscanner/data/03_rrttl.out b/tests/libzscanner/data/03_rrttl.out
new file mode 100644
index 0000000..f5cc907
--- /dev/null
+++ b/tests/libzscanner/data/03_rrttl.out
@@ -0,0 +1,100 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=FFFFFFFF
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=000A9A4D
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000079
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_BAD_TIME_UNIT
+------
diff --git a/tests/libzscanner/data/04-0_ORIGIN.in b/tests/libzscanner/data/04-0_ORIGIN.in
new file mode 100644
index 0000000..4d65ec4
--- /dev/null
+++ b/tests/libzscanner/data/04-0_ORIGIN.in
@@ -0,0 +1,29 @@
+$TTL 1
+
+; OK
+$ORIGIN . ; Root domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN tld. ; 1. level domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN second.tld. ; 2. level domain
+@ NS @ ; Use origin
+a. NS @ ; Absolute dname
+a NS @ ; Relative dname
+$ORIGIN \0320\ \\\"\.\@\*.tld. ; Label with special chars
+@ NS @ ; Use origin
+$ORIGIN b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ; IPv6 reverse
+@ NS @ ; Use origin
+$ORIGIN 12345678901234567890123456789012345678901234567890123456789012\051.tld. ; Label of maximal length
+@ NS @ ; Use origin
+$ORIGIN 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. ; Domain name of maximal length
+@ NS @ ; Use origin
+$origin . ; Lower-case
+@ NS @ ; Use origin
+
+; KO
+$ORIGIN ; Empty input
+. NS . ; Is OK, but shouldn't be processed due to previous error stop!
diff --git a/tests/libzscanner/data/04-0_ORIGIN.out b/tests/libzscanner/data/04-0_ORIGIN.out
new file mode 100644
index 0000000..92dc459
--- /dev/null
+++ b/tests/libzscanner/data/04-0_ORIGIN.out
@@ -0,0 +1,86 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016103746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=067365636F6E6403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=0161067365636F6E6403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=067365636F6E6403746C6400
+------
+OWNER=082030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+------
+OWNER=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+------
+OWNER=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-1_ORIGIN.in b/tests/libzscanner/data/04-1_ORIGIN.in
new file mode 100644
index 0000000..871c064
--- /dev/null
+++ b/tests/libzscanner/data/04-1_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN tld ; Not FQD
diff --git a/tests/libzscanner/data/04-1_ORIGIN.out b/tests/libzscanner/data/04-1_ORIGIN.out
new file mode 100644
index 0000000..467de10
--- /dev/null
+++ b/tests/libzscanner/data/04-1_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-2_ORIGIN.in b/tests/libzscanner/data/04-2_ORIGIN.in
new file mode 100644
index 0000000..8dd2e20
--- /dev/null
+++ b/tests/libzscanner/data/04-2_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN % ; Bad (unslashed) character
diff --git a/tests/libzscanner/data/04-2_ORIGIN.out b/tests/libzscanner/data/04-2_ORIGIN.out
new file mode 100644
index 0000000..467de10
--- /dev/null
+++ b/tests/libzscanner/data/04-2_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_ORIGIN
+------
diff --git a/tests/libzscanner/data/04-3_ORIGIN.in b/tests/libzscanner/data/04-3_ORIGIN.in
new file mode 100644
index 0000000..26d1872
--- /dev/null
+++ b/tests/libzscanner/data/04-3_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN .. ; Missing label between dots
diff --git a/tests/libzscanner/data/04-3_ORIGIN.out b/tests/libzscanner/data/04-3_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-3_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-4_ORIGIN.in b/tests/libzscanner/data/04-4_ORIGIN.in
new file mode 100644
index 0000000..0be4721
--- /dev/null
+++ b/tests/libzscanner/data/04-4_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN \1 ; Slash notation requires 3 digits
diff --git a/tests/libzscanner/data/04-4_ORIGIN.out b/tests/libzscanner/data/04-4_ORIGIN.out
new file mode 100644
index 0000000..03a0738
--- /dev/null
+++ b/tests/libzscanner/data/04-4_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/04-5_ORIGIN.in b/tests/libzscanner/data/04-5_ORIGIN.in
new file mode 100644
index 0000000..170d465
--- /dev/null
+++ b/tests/libzscanner/data/04-5_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN \12 ; Slash notation requires 3 digits
diff --git a/tests/libzscanner/data/04-5_ORIGIN.out b/tests/libzscanner/data/04-5_ORIGIN.out
new file mode 100644
index 0000000..03a0738
--- /dev/null
+++ b/tests/libzscanner/data/04-5_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/04-6_ORIGIN.in b/tests/libzscanner/data/04-6_ORIGIN.in
new file mode 100644
index 0000000..db9652d
--- /dev/null
+++ b/tests/libzscanner/data/04-6_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN .tld ; Leading dot
diff --git a/tests/libzscanner/data/04-6_ORIGIN.out b/tests/libzscanner/data/04-6_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-6_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-7_ORIGIN.in b/tests/libzscanner/data/04-7_ORIGIN.in
new file mode 100644
index 0000000..556646c
--- /dev/null
+++ b/tests/libzscanner/data/04-7_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN tld. x ; Unexpected item
diff --git a/tests/libzscanner/data/04-7_ORIGIN.out b/tests/libzscanner/data/04-7_ORIGIN.out
new file mode 100644
index 0000000..b7d67e5
--- /dev/null
+++ b/tests/libzscanner/data/04-7_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/04-8_ORIGIN.in b/tests/libzscanner/data/04-8_ORIGIN.in
new file mode 100644
index 0000000..445cbec
--- /dev/null
+++ b/tests/libzscanner/data/04-8_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN 12345678901234567890123456789012345678901234567890123456789012\0514.tld. ; Label exceeded maximal length
diff --git a/tests/libzscanner/data/04-8_ORIGIN.out b/tests/libzscanner/data/04-8_ORIGIN.out
new file mode 100644
index 0000000..788a495
--- /dev/null
+++ b/tests/libzscanner/data/04-8_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_LABEL_OVERFLOW
+------
diff --git a/tests/libzscanner/data/04-9_ORIGIN.in b/tests/libzscanner/data/04-9_ORIGIN.in
new file mode 100644
index 0000000..e0a4b95
--- /dev/null
+++ b/tests/libzscanner/data/04-9_ORIGIN.in
@@ -0,0 +1,4 @@
+$TTL 1
+
+; KO
+$ORIGIN 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. ; Domain name exceeded maximal length
diff --git a/tests/libzscanner/data/04-9_ORIGIN.out b/tests/libzscanner/data/04-9_ORIGIN.out
new file mode 100644
index 0000000..eef0ab5
--- /dev/null
+++ b/tests/libzscanner/data/04-9_ORIGIN.out
@@ -0,0 +1,2 @@
+ERROR=ZS_DNAME_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-0_TTL.in b/tests/libzscanner/data/05-0_TTL.in
new file mode 100644
index 0000000..baa8f38
--- /dev/null
+++ b/tests/libzscanner/data/05-0_TTL.in
@@ -0,0 +1,36 @@
+$ORIGIN .
+
+; OK
+$TTL 0 ; Minimal ttl
+@ NS @ ; Use ttl
+$TTL 3600 ; Inner value
+@ NS @ ; Use ttl
+$TTL 4294967295 ; Maximal ttl
+@ NS @ ; Use ttl
+$TTL 1S ; 1 second (upper-case)
+@ NS @ ; Use ttl
+$TTL 1s ; 1 second (lower-case)
+@ NS @ ; Use ttl
+$TTL 1M ; 1 minute (upper-case)
+@ NS @ ; Use ttl
+$TTL 1m ; 1 minute (lower-case)
+@ NS @ ; Use ttl
+$TTL 1H ; 1 hour (upper-case)
+@ NS @ ; Use ttl
+$TTL 1h ; 1 hour (lower-case)
+@ NS @ ; Use ttl
+$TTL 1D ; 1 day (upper-case)
+@ NS @ ; Use ttl
+$TTL 1d ; 1 day (lower-case)
+@ NS @ ; Use ttl
+$TTL 1W ; 1 week (upper-case)
+@ NS @ ; Use ttl
+$TTL 1w ; 1 week (lower-case)
+@ NS @ ; Use ttl
+$TTL 1w1d1h1m1s ; More time units
+@ NS @ ; Use ttl
+$TTL 1s1m1m ; Same time units, non decreasing order
+@ NS @ ; Use ttl
+
+; KO
+$TTL -1 ; Negative ttl
diff --git a/tests/libzscanner/data/05-0_TTL.out b/tests/libzscanner/data/05-0_TTL.out
new file mode 100644
index 0000000..93c7651
--- /dev/null
+++ b/tests/libzscanner/data/05-0_TTL.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=FFFFFFFF
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=0000003C
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00015180
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00093A80
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=000A9A4D
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000079
+RTYPE=0002
+RDATA=00
+------
+ERROR=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/05-1_TTL.in b/tests/libzscanner/data/05-1_TTL.in
new file mode 100644
index 0000000..519fe79
--- /dev/null
+++ b/tests/libzscanner/data/05-1_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 4294967296 ; 32bit overflow
diff --git a/tests/libzscanner/data/05-1_TTL.out b/tests/libzscanner/data/05-1_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-1_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-2_TTL.in b/tests/libzscanner/data/05-2_TTL.in
new file mode 100644
index 0000000..ed112da
--- /dev/null
+++ b/tests/libzscanner/data/05-2_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 100000000W ; 32bit overflow
diff --git a/tests/libzscanner/data/05-2_TTL.out b/tests/libzscanner/data/05-2_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-2_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-3_TTL.in b/tests/libzscanner/data/05-3_TTL.in
new file mode 100644
index 0000000..d96c9ba
--- /dev/null
+++ b/tests/libzscanner/data/05-3_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 4294967295s1w ; 32bit overflow
diff --git a/tests/libzscanner/data/05-3_TTL.out b/tests/libzscanner/data/05-3_TTL.out
new file mode 100644
index 0000000..1849267
--- /dev/null
+++ b/tests/libzscanner/data/05-3_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_NUMBER32_OVERFLOW
+------
diff --git a/tests/libzscanner/data/05-4_TTL.in b/tests/libzscanner/data/05-4_TTL.in
new file mode 100644
index 0000000..efaf4d0
--- /dev/null
+++ b/tests/libzscanner/data/05-4_TTL.in
@@ -0,0 +1,4 @@
+$ORIGIN .
+
+; KO
+$TTL 1x ; Unknown time unit
diff --git a/tests/libzscanner/data/05-4_TTL.out b/tests/libzscanner/data/05-4_TTL.out
new file mode 100644
index 0000000..d4bef98
--- /dev/null
+++ b/tests/libzscanner/data/05-4_TTL.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_TIME_UNIT
+------
diff --git a/tests/libzscanner/data/06-0_INCLUDE.in b/tests/libzscanner/data/06-0_INCLUDE.in
new file mode 100644
index 0000000..962e6c9
--- /dev/null
+++ b/tests/libzscanner/data/06-0_INCLUDE.in
@@ -0,0 +1,29 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+0. NS @
+
+$INCLUDE ./includes/include1 ; Relative path without origin
+1. NS @
+
+$INCLUDE "./includes/include2" . ; Quoted filename and the simplest origin
+2. NS @
+
+$INCLUDE ./includes/include\050 tld. ; Simple origin
+3. NS @
+
+$INCLUDE \./includes/include2 _a_.-b-c-./d/. ; Slashed character in file name, allowed characters in origin
+4. NS @
+
+$INCLUDE ./includes/include2 \0320\ \\\"\.\@\*.tld. ; Origin with special chars
+5. NS @
+
+$INCLUDE @TMPDIR@/includes/include2 ; Absolute path without origin
+6. NS @
+
+$INCLUDE @TMPDIR@/includes/include2 tld. ; Absolute path with origin
+7. NS @
+
+; KO (DISABLED - different results)
+;$INCLUDE ; Empty parameters
diff --git a/tests/libzscanner/data/06-0_INCLUDE.out b/tests/libzscanner/data/06-0_INCLUDE.out
new file mode 100644
index 0000000..2536f72
--- /dev/null
+++ b/tests/libzscanner/data/06-0_INCLUDE.out
@@ -0,0 +1,138 @@
+OWNER=013000
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016105746C64316100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=016105746C64316200
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=05746C64316200
+------
+OWNER=013100
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013200
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016203746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013300
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162035F615F052D622D632D032F642F00
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=035F615F052D622D632D032F642F00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=0162082030205C222E402A03746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013500
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013600
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=016203746C6400
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
+OWNER=013700
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
diff --git a/tests/libzscanner/data/06-1_INCLUDE.in b/tests/libzscanner/data/06-1_INCLUDE.in
new file mode 100644
index 0000000..194fb52
--- /dev/null
+++ b/tests/libzscanner/data/06-1_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include1 a ; Origin is not FQDN
diff --git a/tests/libzscanner/data/06-1_INCLUDE.out b/tests/libzscanner/data/06-1_INCLUDE.out
new file mode 100644
index 0000000..6634921
--- /dev/null
+++ b/tests/libzscanner/data/06-1_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_INCLUDE_ORIGIN
+------
diff --git a/tests/libzscanner/data/06-2_INCLUDE.in b/tests/libzscanner/data/06-2_INCLUDE.in
new file mode 100644
index 0000000..21bcf05
--- /dev/null
+++ b/tests/libzscanner/data/06-2_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include1 % ; Bad origin
diff --git a/tests/libzscanner/data/06-2_INCLUDE.out b/tests/libzscanner/data/06-2_INCLUDE.out
new file mode 100644
index 0000000..6634921
--- /dev/null
+++ b/tests/libzscanner/data/06-2_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_BAD_INCLUDE_ORIGIN
+------
diff --git a/tests/libzscanner/data/06-3_INCLUDE.in b/tests/libzscanner/data/06-3_INCLUDE.in
new file mode 100644
index 0000000..f7079b4
--- /dev/null
+++ b/tests/libzscanner/data/06-3_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE @TMPDIR@/ ; Given file is a directory
diff --git a/tests/libzscanner/data/06-3_INCLUDE.out b/tests/libzscanner/data/06-3_INCLUDE.out
new file mode 100644
index 0000000..6823154
--- /dev/null
+++ b/tests/libzscanner/data/06-3_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_FILE_INVALID
+------
diff --git a/tests/libzscanner/data/06-4_INCLUDE.in b/tests/libzscanner/data/06-4_INCLUDE.in
new file mode 100644
index 0000000..3cdc79d
--- /dev/null
+++ b/tests/libzscanner/data/06-4_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE @TMPDIR@/file-doesnt-exist ; File doesn't exist
diff --git a/tests/libzscanner/data/06-4_INCLUDE.out b/tests/libzscanner/data/06-4_INCLUDE.out
new file mode 100644
index 0000000..e09e5d1
--- /dev/null
+++ b/tests/libzscanner/data/06-4_INCLUDE.out
@@ -0,0 +1,2 @@
+ERROR=ZS_FILE_OPEN
+------
diff --git a/tests/libzscanner/data/06-5_INCLUDE.in b/tests/libzscanner/data/06-5_INCLUDE.in
new file mode 100644
index 0000000..1d65812
--- /dev/null
+++ b/tests/libzscanner/data/06-5_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include3 ; Blank include file
diff --git a/tests/libzscanner/data/06-5_INCLUDE.out b/tests/libzscanner/data/06-5_INCLUDE.out
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/06-5_INCLUDE.out
diff --git a/tests/libzscanner/data/06-6_INCLUDE.in b/tests/libzscanner/data/06-6_INCLUDE.in
new file mode 100644
index 0000000..cbfda64
--- /dev/null
+++ b/tests/libzscanner/data/06-6_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include4 ; Include file contains warning
diff --git a/tests/libzscanner/data/06-6_INCLUDE.out b/tests/libzscanner/data/06-6_INCLUDE.out
new file mode 100644
index 0000000..a9c0259
--- /dev/null
+++ b/tests/libzscanner/data/06-6_INCLUDE.out
@@ -0,0 +1,4 @@
+WARNG=ZS_BAD_RDATA
+------
+ERROR=ZS_UNPROCESSED_INCLUDE
+------
diff --git a/tests/libzscanner/data/06-7_INCLUDE.in b/tests/libzscanner/data/06-7_INCLUDE.in
new file mode 100644
index 0000000..24deef7
--- /dev/null
+++ b/tests/libzscanner/data/06-7_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include5 ; Include file contains error
diff --git a/tests/libzscanner/data/06-7_INCLUDE.out b/tests/libzscanner/data/06-7_INCLUDE.out
new file mode 100644
index 0000000..cbbb0c1
--- /dev/null
+++ b/tests/libzscanner/data/06-7_INCLUDE.out
@@ -0,0 +1,4 @@
+ERROR=ZS_BAD_NUMBER
+------
+ERROR=ZS_UNPROCESSED_INCLUDE
+------
diff --git a/tests/libzscanner/data/06-8_INCLUDE.in b/tests/libzscanner/data/06-8_INCLUDE.in
new file mode 100644
index 0000000..c856668
--- /dev/null
+++ b/tests/libzscanner/data/06-8_INCLUDE.in
@@ -0,0 +1,5 @@
+$ORIGIN .
+$TTL 1
+
+; KO
+$INCLUDE ./includes/include6 ; Include file contains include
diff --git a/tests/libzscanner/data/06-8_INCLUDE.out b/tests/libzscanner/data/06-8_INCLUDE.out
new file mode 100644
index 0000000..4581388
--- /dev/null
+++ b/tests/libzscanner/data/06-8_INCLUDE.out
@@ -0,0 +1,12 @@
+OWNER=016200
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=00
+------
+OWNER=016205746C64316100
+CLASS=0001
+RRTTL=00000E10
+RTYPE=0002
+RDATA=05746C64316100
+------
diff --git a/tests/libzscanner/data/07-0-rdata.in b/tests/libzscanner/data/07-0-rdata.in
new file mode 100644
index 0000000..a79403f
--- /dev/null
+++ b/tests/libzscanner/data/07-0-rdata.in
@@ -0,0 +1 @@
+@ A \ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-0-rdata.out b/tests/libzscanner/data/07-0-rdata.out
new file mode 100644
index 0000000..ed303f6
--- /dev/null
+++ b/tests/libzscanner/data/07-0-rdata.out
@@ -0,0 +1,2 @@
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
diff --git a/tests/libzscanner/data/07-1-rdata.in b/tests/libzscanner/data/07-1-rdata.in
new file mode 100644
index 0000000..7a5a11a
--- /dev/null
+++ b/tests/libzscanner/data/07-1-rdata.in
@@ -0,0 +1 @@
+@ TXT \ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-1-rdata.out b/tests/libzscanner/data/07-1-rdata.out
new file mode 100644
index 0000000..1702677
--- /dev/null
+++ b/tests/libzscanner/data/07-1-rdata.out
@@ -0,0 +1,2 @@
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/07-2-rdata.in b/tests/libzscanner/data/07-2-rdata.in
new file mode 100644
index 0000000..7a4c8fe
--- /dev/null
+++ b/tests/libzscanner/data/07-2-rdata.in
@@ -0,0 +1 @@
+@ TXT \092 \ No newline at end of file
diff --git a/tests/libzscanner/data/07-2-rdata.out b/tests/libzscanner/data/07-2-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-2-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/07-3-rdata.in b/tests/libzscanner/data/07-3-rdata.in
new file mode 100644
index 0000000..338b212
--- /dev/null
+++ b/tests/libzscanner/data/07-3-rdata.in
@@ -0,0 +1 @@
+@ TXT \\ \ No newline at end of file
diff --git a/tests/libzscanner/data/07-3-rdata.out b/tests/libzscanner/data/07-3-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-3-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/07-4-rdata.in b/tests/libzscanner/data/07-4-rdata.in
new file mode 100644
index 0000000..74a45dd
--- /dev/null
+++ b/tests/libzscanner/data/07-4-rdata.in
@@ -0,0 +1 @@
+@ TXT \# 2 015C \ No newline at end of file
diff --git a/tests/libzscanner/data/07-4-rdata.out b/tests/libzscanner/data/07-4-rdata.out
new file mode 100644
index 0000000..3f52437
--- /dev/null
+++ b/tests/libzscanner/data/07-4-rdata.out
@@ -0,0 +1,6 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000000
+RTYPE=0010
+RDATA=015C
+------
diff --git a/tests/libzscanner/data/10_A.in b/tests/libzscanner/data/10_A.in
new file mode 100644
index 0000000..6c7c90a
--- /dev/null
+++ b/tests/libzscanner/data/10_A.in
@@ -0,0 +1,19 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ A 0.0.0.0 ; Minimal ipv4 address
+@ A 255.255.255.255 ; Maximal ipv4 address
+@ A \# 4 00000000 ; Hexadecimal rdata
+@ TYPE1 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE1 0.0.0.0 ; TYPE
+@ a 0.0.0.0 ; Type in lower-case
+
+; KO
+@ A
+@ A ; Empty rdata
+@ A \# 0 ; Hex empty rdata
+@ A 0.0.0.256 ; 8bit overflow
+@ A 0.0.0 ; Short address
+@ A 0.0.0.A ; Bad character
+@ A 0.0.0.0 1.1.1.1 ; Unexpected item
diff --git a/tests/libzscanner/data/10_A.out b/tests/libzscanner/data/10_A.out
new file mode 100644
index 0000000..dda68ab
--- /dev/null
+++ b/tests/libzscanner/data/10_A.out
@@ -0,0 +1,50 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/11_AAAA.in b/tests/libzscanner/data/11_AAAA.in
new file mode 100644
index 0000000..48c4b37
--- /dev/null
+++ b/tests/libzscanner/data/11_AAAA.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ AAAA 0:0:0:0:0:0:0:0 ; Minimal ipv6 address
+@ AAAA FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF ; Maximal ipv6 address
+@ AAAA \# 16 00000000 00000000 00000000 00000000 ; Hexadecimal rdata
+@ TYPE28 \# 16 00000000 00000000 00000000 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE28 0:0:0:0:0:0:0:0 ; TYPE
+@ AAAA 0::1.2.3.4 ; ipv6 address based on ipv4 address
+@ AAAA :: ; Double colon
+@ aaAA :: ; Type in lower-case
+
+; KO
+@ AAAA
+@ AAAA ; Empty rdata
+@ AAAA \# 0 ; Hex empty rdata
+@ AAAA 0::FFFFF ; 16bit overflow
+@ AAAA 0:0:0:0:0:0:0 ; Short address
+@ AAAA 0:0:0:0:0:0:0:X ; Bad character
+@ AAAA :: :: ; Unexpected item
diff --git a/tests/libzscanner/data/11_AAAA.out b/tests/libzscanner/data/11_AAAA.out
new file mode 100644
index 0000000..0cb174a
--- /dev/null
+++ b/tests/libzscanner/data/11_AAAA.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001C
+RDATA=00000000000000000000000000000000
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/12_TXT.in b/tests/libzscanner/data/12_TXT.in
new file mode 100644
index 0000000..cdd0eaa
--- /dev/null
+++ b/tests/libzscanner/data/12_TXT.in
@@ -0,0 +1,37 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TXT "" ; Blank string
+@ TXT a ; One char string
+@ TXT \ ; One space char
+@ TXT "\ " ; One space char in quotes
+@ TXT \021 ; One unprintable char
+@ TXT "\\ \"" ; Special chars
+@ TXT "" "test1" "\255" test2 ; Array of text strings
+@ TXT "" "" "" ; Array of blank strings
+@ TXT first \# "\#" ; Array with special string
+@ TXT \0320\ \\\"\;\.\@\*.tld. ; Special domain as a string
+@ TXT " !\"#$%&'()*+,-./0123456789:;<=>?@" ; First part of all printables
+@ TXT "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`" ; Second part of all printables
+@ TXT "abcdefghijklmnopqrstuvwxyz{|}~" ; Third part of all printables
+@ TXT \# 1 00 ; Hexadecimal rdata
+@ TYPE16 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE16 "" ; TYPE
+@ TXT ( ; Special multi-line string
+"first
+second"
+third ; Second string
+)
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" ; Text string of maximal length (255 chars)
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" ; Minimum overflowed text string which should be divided into two strings.
+@ TXT "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053\054" ; Minimum overflowed text string which should be divided into two strings (decimal version).
+@ txt "" ; Type in lower-case
+
+; KO
+@ TXT
+@ TXT ; Empty rdata
+@ TXT \# 0 ; Hex empty rdata
+@ TXT \01 ; Missing digit in decimal notation
+@ TXT \256 ; 8bit overflow in decimal notation
+@ TXT """ ; '"' char without forward slash
diff --git a/tests/libzscanner/data/12_TXT.out b/tests/libzscanner/data/12_TXT.out
new file mode 100644
index 0000000..283b1e0
--- /dev/null
+++ b/tests/libzscanner/data/12_TXT.out
@@ -0,0 +1,138 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0115
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=035C2022
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0005746573743101FF057465737432
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=05666972737401230123
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0E2030205C223B2E402A2E746C642E
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=21202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=204142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=1E6162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=0C66697273740A7365636F6E64057468697264
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E3132333435
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E31323334350136
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E31323334350136
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0010
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_TEXT
+------
diff --git a/tests/libzscanner/data/13_SPF.in b/tests/libzscanner/data/13_SPF.in
new file mode 100644
index 0000000..0ad6c84
--- /dev/null
+++ b/tests/libzscanner/data/13_SPF.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The SPF is the same as the TXT, so there are the differences and basics only.
+
+; OK
+@ SPF "" "test1" "\255" test2 ; Array of text strings
+@ SPF \# 1 00 ; Hexadecimal rdata
+@ TYPE99 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE99 "" ; TYPE
+@ spf "" ; Type in lower-case
+
+; KO
+@ SPF
diff --git a/tests/libzscanner/data/13_SPF.out b/tests/libzscanner/data/13_SPF.out
new file mode 100644
index 0000000..be7fb35
--- /dev/null
+++ b/tests/libzscanner/data/13_SPF.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=0005746573743101FF057465737432
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0063
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/14_NS.in b/tests/libzscanner/data/14_NS.in
new file mode 100644
index 0000000..b56a579
--- /dev/null
+++ b/tests/libzscanner/data/14_NS.in
@@ -0,0 +1,38 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NS . ; The simplest owner
+@ NS tld. ; FQD tld owner
+@ NS tld ; Relative form
+@ NS *. ; FQD with asterisk
+@ NS * ; Alone asterisk
+@ NS *.* ; More asterisks
+@ NS *a.a*a.** ; Also possible
+@ NS @ ; Use origin
+@ NS 0123456789 ; Digits
+@ NS _a_.-b-c-./d/. ; Allowed characters '_' '-' '/' anywhere
+@ NS ABCDEFGHIJKLMNOPQRSTUVWXYZ ; All upper-case letters
+@ NS abcdefghijklmnopqrstuvwxyz ; All lower-case letters
+@ NS \0320\ \\\"\.\@\*.tld. ; Label with special chars
+@ NS b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. ; IPv6 reverse
+@ NS 123456789012345678901234567890123456789012345678901234567890123.tld. ; Label of maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901. ; Domain name of maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.1234567890123456789012345678901234567890123456789012345678901 ; Domain name of maximal length (after appending origin)
+@ TYPE2 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE2 @ ; TYPE
+@ ns @ ; Type in lower-case
+
+; KO
+@ NS
+@ NS ; Empty rdata
+@ NS \# 0 ; Hex empty rdata
+@ NS & ; Bad (unslashed) character
+@ NS @@ ; Double @@
+@ NS .. ; Missing label between dots
+@ NS \1 ; Slash notation requires 3 digits
+@ NS \12 ; Slash notation requires 3 digits
+@ NS 1234567890123456789012345678901234567890123456789012345678901234.tld. ; Label exceeded maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012. ; Domain name exceeded maximal length
+@ NS 123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012 ; Domain name exceeded maximal length (after appending origin)
+@ NS . x ; Unexpected item
diff --git a/tests/libzscanner/data/14_NS.out b/tests/libzscanner/data/14_NS.out
new file mode 100644
index 0000000..08d739a
--- /dev/null
+++ b/tests/libzscanner/data/14_NS.out
@@ -0,0 +1,144 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=012A012A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=022A6103612A61022A2A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0A3031323334353637383900
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=035F615F052D622D632D032F642F00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=1A4142434445464748494A4B4C4D4E4F505152535455565758595A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=1A6162636465666768696A6B6C6D6E6F707172737475767778797A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=0162016101390138013701360135013001300130013001300130013001300130013001300130013001300130013001300138016201640130013101300130013203697036046172706100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323303746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=3F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333F3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333D3132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0002
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_LABEL_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_DNAME_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/15_CNAME.in b/tests/libzscanner/data/15_CNAME.in
new file mode 100644
index 0000000..806b8f9
--- /dev/null
+++ b/tests/libzscanner/data/15_CNAME.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The CNAME is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ CNAME test.example.com ; Relative dname
+@ CNAME \# 1 00 ; Hexadecimal rdata
+@ TYPE5 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE5 @ ; TYPE
+@ cname @ ; Type in lower-case
+
+; KO
+@ CNAME
diff --git a/tests/libzscanner/data/15_CNAME.out b/tests/libzscanner/data/15_CNAME.out
new file mode 100644
index 0000000..3c3a55d
--- /dev/null
+++ b/tests/libzscanner/data/15_CNAME.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0005
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/16_PTR.in b/tests/libzscanner/data/16_PTR.in
new file mode 100644
index 0000000..3694e7d
--- /dev/null
+++ b/tests/libzscanner/data/16_PTR.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The PTR is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ PTR test.example.com ; Relative dname
+@ PTR \# 1 00 ; Hexadecimal rdata
+@ TYPE12 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE12 @ ; TYPE
+@ ptr @ ; Type in lower-case
+
+; KO
+@ PTR
diff --git a/tests/libzscanner/data/16_PTR.out b/tests/libzscanner/data/16_PTR.out
new file mode 100644
index 0000000..55d94b9
--- /dev/null
+++ b/tests/libzscanner/data/16_PTR.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000C
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/17_DNAME.in b/tests/libzscanner/data/17_DNAME.in
new file mode 100644
index 0000000..5e9ba71
--- /dev/null
+++ b/tests/libzscanner/data/17_DNAME.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The DNAME is the same as the NS, so there are the differences and basics only.
+
+; OK
+@ DNAME test.example.com ; Relative dname
+@ DNAME \# 1 00 ; Hexadecimal rdata
+@ TYPE39 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE39 @ ; TYPE
+@ dname @ ; Type in lower-case
+
+; KO
+@ DNAME
diff --git a/tests/libzscanner/data/17_DNAME.out b/tests/libzscanner/data/17_DNAME.out
new file mode 100644
index 0000000..35f11d8
--- /dev/null
+++ b/tests/libzscanner/data/17_DNAME.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=0474657374076578616D706C6503636F6D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0027
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/18_MX.in b/tests/libzscanner/data/18_MX.in
new file mode 100644
index 0000000..61e6afb
--- /dev/null
+++ b/tests/libzscanner/data/18_MX.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; For more tests on dname see NS test (same processing)
+
+; OK
+@ MX 0 @ ; Minimal priority
+@ MX 65535 @ ; Maximal priority
+@ MX 1 mail ; Relative dname
+@ MX 1 mail.tld. ; Absolute dname
+@ MX \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE15 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE15 1 @ ; TYPE
+@ mx 1 @ ; Type in lower-case
+
+; KO
+@ MX
+@ MX ; Empty rdata
+@ MX \# 0 ; Hex empty rdata
+@ MX -1 @ ; Negative number
+@ MX 65536 @ ; 16bit overflow
+@ MX 1 $ ; Bad dname
+@ MX 0 @ x ; Unexpected item
diff --git a/tests/libzscanner/data/18_MX.out b/tests/libzscanner/data/18_MX.out
new file mode 100644
index 0000000..0a50038
--- /dev/null
+++ b/tests/libzscanner/data/18_MX.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=0001046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000F
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/19_AFSDB.in b/tests/libzscanner/data/19_AFSDB.in
new file mode 100644
index 0000000..be569e4
--- /dev/null
+++ b/tests/libzscanner/data/19_AFSDB.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The AFSDB is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ AFSDB 1 mail ; Relative dname
+@ AFSDB \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE18 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE18 1 @ ; TYPE
+@ afsdb 1 @ ; Type in lower-case
+
+; KO
+@ AFSDB
diff --git a/tests/libzscanner/data/19_AFSDB.out b/tests/libzscanner/data/19_AFSDB.out
new file mode 100644
index 0000000..b5c77eb
--- /dev/null
+++ b/tests/libzscanner/data/19_AFSDB.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0012
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/20_RT.in b/tests/libzscanner/data/20_RT.in
new file mode 100644
index 0000000..3bc774d
--- /dev/null
+++ b/tests/libzscanner/data/20_RT.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The RT is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ RT 1 mail ; Relative dname
+@ RT \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE21 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE21 1 @ ; TYPE
+@ rt 1 @ ; Type in lower-case
+
+; KO
+@ RT
diff --git a/tests/libzscanner/data/20_RT.out b/tests/libzscanner/data/20_RT.out
new file mode 100644
index 0000000..8be89a3
--- /dev/null
+++ b/tests/libzscanner/data/20_RT.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0015
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/21_KX.in b/tests/libzscanner/data/21_KX.in
new file mode 100644
index 0000000..529480d
--- /dev/null
+++ b/tests/libzscanner/data/21_KX.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The KX is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ KX 1 mail ; Relative dname
+@ KX \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE36 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE36 1 @ ; TYPE
+@ kx 1 @ ; Type in lower-case
+
+; KO
+@ KX
diff --git a/tests/libzscanner/data/21_KX.out b/tests/libzscanner/data/21_KX.out
new file mode 100644
index 0000000..1bc91e6
--- /dev/null
+++ b/tests/libzscanner/data/21_KX.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0024
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/22_HINFO.in b/tests/libzscanner/data/22_HINFO.in
new file mode 100644
index 0000000..4cd5ab8
--- /dev/null
+++ b/tests/libzscanner/data/22_HINFO.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ HINFO "" "" ; Blank string
+@ HINFO a b ; One char string
+@ HINFO \ \ ; One space char
+@ HINFO "\ " "\ " ; One space char in quotes
+@ HINFO \021 \022 ; One unprintable char
+@ HINFO "\\ \"" "\\ \"" ; Special chars
+@ HINFO first \# ; Array with special string
+@ HINFO \# 2 00 00 ; Hexadecimal rdata
+@ TYPE13 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE13 "" "" ; TYPE
+@ HINFO "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" "" ; Text string of maximal length (255 chars)
+@ hinfo "" "" ; Type in lower-case
+
+; KO
+@ HINFO
+@ HINFO ; Empty rdata
+@ HINFO \# 0 ; Hex empty rdata
+@ HINFO \01 "" ; Missing digit in decimal notation
+@ HINFO \256 "" ; 8bit overflow in decimal notation
+@ HINFO """ "" ; '"' char without forward slash
+@ HINFO "" "" "" ; Unexpected item
+@ HINFO "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" "" ; Maximal length overflow
diff --git a/tests/libzscanner/data/22_HINFO.out b/tests/libzscanner/data/22_HINFO.out
new file mode 100644
index 0000000..fa66729
--- /dev/null
+++ b/tests/libzscanner/data/22_HINFO.out
@@ -0,0 +1,88 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01610162
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01200120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01200120
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=01150116
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=035C2022035C2022
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0566697273740123
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E313233343500
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000D
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_TEXT
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_ITEM_OVERFLOW
+------
diff --git a/tests/libzscanner/data/23_MINFO.in b/tests/libzscanner/data/23_MINFO.in
new file mode 100644
index 0000000..7b34c60
--- /dev/null
+++ b/tests/libzscanner/data/23_MINFO.in
@@ -0,0 +1,18 @@
+$ORIGIN .
+$TTL 1
+
+; For more tests on dname see NS test (same processing)
+
+; OK
+@ MINFO . . ; The simplest dnames
+@ MINFO @ @ ; Use origin
+@ MINFO mail mail.tld. ; Relative and absolute dnames
+@ MINFO \# 2 00 00 ; Hexadecimal rdata
+@ TYPE14 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE14 @ @ ; TYPE
+@ minfo @ @ ; Type in lower-case
+
+; KO
+@ MINFO
+@ MINFO ; Empty rdata
+@ MINFO \# 0 ; Hex empty rdata
diff --git a/tests/libzscanner/data/23_MINFO.out b/tests/libzscanner/data/23_MINFO.out
new file mode 100644
index 0000000..8154c0c
--- /dev/null
+++ b/tests/libzscanner/data/23_MINFO.out
@@ -0,0 +1,48 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=046D61696C00046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=000E
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/24_RP.in b/tests/libzscanner/data/24_RP.in
new file mode 100644
index 0000000..a70d792
--- /dev/null
+++ b/tests/libzscanner/data/24_RP.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The RP is the same as the MINFO, so there are the differences and basics only.
+
+; OK
+@ RP mail mail.tld. ; Relative and absolute dnames
+@ RP \# 2 00 00 ; Hexadecimal rdata
+@ TYPE17 \# 2 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE17 @ @ ; TYPE
+@ rp @ @ ; Type in lower-case
+
+; KO
+@ RP
diff --git a/tests/libzscanner/data/24_RP.out b/tests/libzscanner/data/24_RP.out
new file mode 100644
index 0000000..d13b1ba
--- /dev/null
+++ b/tests/libzscanner/data/24_RP.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=046D61696C00046D61696C03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0011
+RDATA=0000
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/25_SOA.in b/tests/libzscanner/data/25_SOA.in
new file mode 100644
index 0000000..bf093d4
--- /dev/null
+++ b/tests/libzscanner/data/25_SOA.in
@@ -0,0 +1,31 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SOA @ @ 0 0 0 0 0 ; The simplest variant
+@ SOA tld. tld 0 0 0 0 0 ; Absolute and relative dnames.
+@ SOA @ @ 4294967295 0 0 0 0 ; Maximal serial
+@ SOA @ @ 0 4294967295 4294967295 4294967295 4294967295 ; Maximal times
+@ SOA @ @ 0 1d 1h 1m 1s ; Time units
+@ TYPE6 \# 22 00 00 0000000000000000000000000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE6 @ @ 0 0 0 0 0 ; TYPE
+@ SOA ns.tld. first\.second.tld. ( ; Multiline record
+ 2007120710
+ 1w2d3h4m5s
+ 2h
+ 3m
+ 4s
+)
+@ soa @ @ 0 0 0 0 0 ; Type in lower-case
+
+; KO
+@ SOA
+@ SOA ; Empty rdata
+@ SOA \# 0 ; Hex empty rdata
+@ SOA @ @ 1h 0 0 0 0 ; Bad number
+@ SOA @ @ 4294967296 0 0 0 0 ; Serial overflow
+@ SOA @ @ 0 4294967296 0 0 0 ; Refresh overflow
+@ SOA @ @ 0 0 4294967296 0 0 ; Retry overflow
+@ SOA @ @ 0 0 0 4294967296 0 ; Expire overflow
+@ SOA @ @ 0 0 0 0 4294967296 ; Minimum overflow
+@ SOA @ @ 0 0 0 0 0 x ; Unexpected item
diff --git a/tests/libzscanner/data/25_SOA.out b/tests/libzscanner/data/25_SOA.out
new file mode 100644
index 0000000..f658442
--- /dev/null
+++ b/tests/libzscanner/data/25_SOA.out
@@ -0,0 +1,74 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=03746C640003746C64000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=0000FFFFFFFF00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=0000000000000001518000000E100000003C00000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=026E7303746C64000C66697273742E7365636F6E6403746C640077A23B46000C08A500001C20000000B400000004
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0006
+RDATA=00000000000000000000000000000000000000000000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/26_SRV.in b/tests/libzscanner/data/26_SRV.in
new file mode 100644
index 0000000..001e1e6
--- /dev/null
+++ b/tests/libzscanner/data/26_SRV.in
@@ -0,0 +1,25 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SRV 0 0 0 @ ; The simplest variant
+_ldap._tcp.test.tld. SRV 0 0 0 @ ; Underscores in owner
+@ SRV 65535 65535 65535 @ ; Maximal numbers
+@ SRV 0 0 0 \0320\ \\\"\.\@\*.tld. ; Dname with specials
+@ TYPE33 \# 7 000000000000 00 ; TYPE + Hexadecimal rdata
+@ TYPE33 0 0 0 @ ; TYPE
+@ srv 0 0 0 @ ; Type in lower-case
+
+; KO
+@ SRV
+@ SRV ; Empty rdata
+@ SRV \# 0 ; Hex empty rdata
+@ SRV 1h 0 0 @ ; Bad priority
+@ SRV 0 1h 0 @ ; Bad weight
+@ SRV 0 0 1h @ ; Bad port
+@ SRV 0 0 0 % ; Bad target
+@ SRV 65536 0 0 @ ; Priority overflow
+@ SRV 0 65536 0 @ ; Weight overflow
+@ SRV 0 0 65536 @ ; Port overflow
+@ SRV 0 0 0 @ x ; Unexpected item
+@ SRV 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/26_SRV.out b/tests/libzscanner/data/26_SRV.out
new file mode 100644
index 0000000..eadb804
--- /dev/null
+++ b/tests/libzscanner/data/26_SRV.out
@@ -0,0 +1,66 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=055F6C646170045F746370047465737403746C6400
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=FFFFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=000000000000082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0021
+RDATA=00000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/27_NAPTR.in b/tests/libzscanner/data/27_NAPTR.in
new file mode 100644
index 0000000..44fd346
--- /dev/null
+++ b/tests/libzscanner/data/27_NAPTR.in
@@ -0,0 +1,20 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NAPTR 0 0 "" "" "" @ ; The simplest variant
+@ NAPTR 65535 65535 "" "" "" @ ; Maximal numbers
+@ NAPTR 65535 65535 "" "" "!^urn:cid:.+@([^\.]+\.)(.*)$!\\2!i" @ ; Regexp example
+@ NAPTR 0 0 "" "" "" \0320\ \\\"\.\@\*.tld. ; Dname with specials
+@ TYPE35 \# 8 00000000000000 00 ; TYPE + Hexadecimal rdata
+@ TYPE35 0 0 "" "" "" @ ; TYPE
+@ naptr 0 0 "" "" "" @ ; Type in lower-case
+
+; KO
+@ NAPTR
+@ NAPTR ; Empty rdata
+@ NAPTR \# 0 ; Hex empty rdata
+@ NAPTR 65536 0 "" "" "" @ ; Order overflow
+@ NAPTR 0 65536 "" "" "" @ ; Preference overflow
+@ NAPTR 0 0 "" "" "" @ x ; Unexpected item
+@ NAPTR 0 0 "" "" "" ; Missing item
diff --git a/tests/libzscanner/data/27_NAPTR.out b/tests/libzscanner/data/27_NAPTR.out
new file mode 100644
index 0000000..cc34e26
--- /dev/null
+++ b/tests/libzscanner/data/27_NAPTR.out
@@ -0,0 +1,56 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=FFFFFFFF00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=FFFFFFFF00001F215E75726E3A6369643A2E2B40285B5E2E5D2B2E29282E2A2924215C32216900
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=00000000000000082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0023
+RDATA=0000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/28_TYPE.in b/tests/libzscanner/data/28_TYPE.in
new file mode 100644
index 0000000..f8189e8
--- /dev/null
+++ b/tests/libzscanner/data/28_TYPE.in
@@ -0,0 +1,27 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TYPE0 \# 0 ; Minimal type number
+@ TYPE65535 \# 0 ; Maximal type number
+@ TYPE55555 \# 0 ; Without hex rdata
+@ TYPE55555 \# 1 00 ; Without hex rdata
+@ TYPE1 \# 4 00000000 ; Known type
+@ TYPE1 0.0.0.0 ; Known type in text format
+@ TYPE55555 ( ; Multiline begin
+ \#
+ 5
+ 0102 03
+ 04 05
+ ) ; Multiline end
+@ type55555 \# 0 ; Type in lower-case
+
+; KO
+@ TYPE55555
+@ TYPE55555 ; Without text rdata
+@ TYPE65536 ; Type number overflow
+@ TYPE65535x ; Bad type
+@ TYPE55555 \# ; Missing hex length
+@ TYPE55555 \# 1 0000 ; Too long rdata
+@ TYPE55555 \# 2 00 ; Bad rdata length
+@ TYPE55555 \# 1 00 x ; Unexpected data
diff --git a/tests/libzscanner/data/28_TYPE.out b/tests/libzscanner/data/28_TYPE.out
new file mode 100644
index 0000000..0aa409a
--- /dev/null
+++ b/tests/libzscanner/data/28_TYPE.out
@@ -0,0 +1,64 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0000
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=FFFF
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0001
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=0102030405
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=D903
+RDATA=
+------
+WARNG=ZS_CANNOT_TEXT_DATA
+------
+WARNG=ZS_CANNOT_TEXT_DATA
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_RDATA_LENGTH
+------
+WARNG=ZS_BAD_RDATA_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/29_CERT.in b/tests/libzscanner/data/29_CERT.in
new file mode 100644
index 0000000..ee70ce2
--- /dev/null
+++ b/tests/libzscanner/data/29_CERT.in
@@ -0,0 +1,58 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CERT 0 0 0 AA== ; The simplest variant
+@ CERT 65535 65535 255 AA== ; Maximal numbers
+@ CERT PKIX 0 0 AA== ; Certificate type mnemo
+@ CERT SPKI 0 0 AA== ; Certificate type mnemo
+@ CERT PGP 0 0 AA== ; Certificate type mnemo
+@ CERT IPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT ISPKI 0 0 AA== ; Certificate type mnemo
+@ CERT IPGP 0 0 AA== ; Certificate type mnemo
+@ CERT ACPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT IACPKIX 0 0 AA== ; Certificate type mnemo
+@ CERT URI 0 0 AA== ; Certificate type mnemo
+@ CERT OID 0 0 AA== ; Certificate type mnemo
+@ CERT 0 0 RSAMD5 AA== ; Algorithm mnemo
+@ CERT 0 0 DH AA== ; Algorithm mnemo
+@ CERT 0 0 DSA AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 DSA-NSEC3-SHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA1-NSEC3-SHA1 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA256 AA== ; Algorithm mnemo
+@ CERT 0 0 RSASHA512 AA== ; Algorithm mnemo
+@ CERT 0 0 ECC-GOST AA== ; Algorithm mnemo
+@ CERT 0 0 ECDSAP256SHA256 AA== ; Algorithm mnemo
+@ CERT 0 0 ECDSAP384SHA384 AA== ; Algorithm mnemo
+@ CERT 0 0 ED25519 AA== ; Algorithm mnemo
+@ CERT 0 0 ED448 AA== ; Algorithm mnemo
+@ CERT 0 0 INDIRECT AA== ; Algorithm mnemo
+@ CERT 0 0 PRIVATEDNS AA== ; Algorithm mnemo
+@ CERT 0 0 PRIVATEOID AA== ; Algorithm mnemo
+@ CERT 0 0 0 Zm8= ; One char padding
+@ CERT 0 0 0 Zm9v ; Without padding
+@ CERT 0 0 0 Zm9vYg== ; Two base64 blocks
+@ CERT 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE37 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE37 0 0 0 AA== ; TYPE
+@ cert 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ CERT
+@ CERT ; Empty rdata
+@ CERT \# 0 ; Hex empty rdata
+@ CERT 65536 0 0 AA== ; Type overflow
+@ CERT X 0 0 AA== ; Bad type mnemonic
+@ CERT 0 65536 0 AA== ; Key tag overflow
+@ CERT 0 0 256 AA== ; Algorithm overflow
+@ CERT 0 0 0 A ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 AB ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ CERT 0 0 0 A=== ; Bad padding
+@ CERT 0 0 0 = ; Bad padding
+@ CERT 0 0 0 == ; Bad padding
+@ CERT 0 0 0 === ; Bad padding
+@ CERT 0 0 0 ==== ; Bad padding
+@ CERT 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/29_CERT.out b/tests/libzscanner/data/29_CERT.out
new file mode 100644
index 0000000..da2c897
--- /dev/null
+++ b/tests/libzscanner/data/29_CERT.out
@@ -0,0 +1,244 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=FFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000100000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000200000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000300000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000400000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000500000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000600000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000700000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000800000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00FD00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00FE00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000200
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000300
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000500
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000600
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000700
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000800
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000A00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000D00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000E00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000F00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000001000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FC00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FD00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=00000000FE00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=0000000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0025
+RDATA=000000000000
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_CERT_TYPE
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/30_KEY.in b/tests/libzscanner/data/30_KEY.in
new file mode 100644
index 0000000..263fa03
--- /dev/null
+++ b/tests/libzscanner/data/30_KEY.in
@@ -0,0 +1,31 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ KEY 0 0 0 AA== ; The simplest variant
+@ KEY 65535 255 255 AA== ; Maximal numbers
+@ KEY 0 0 0 Zm8= ; One char padding
+@ KEY 0 0 0 Zm9v ; Without padding
+@ KEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ KEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE25 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE25 0 0 0 AA== ; TYPE
+@ key 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ KEY
+@ KEY ; Empty rdata
+@ KEY \# 0 ; Hex empty rdata
+@ KEY 65536 0 0 AA== ; Type overflow
+@ KEY 0 256 0 AA== ; Key tag overflow
+@ KEY 0 0 256 AA== ; Algorithm overflow
+@ KEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ KEY 0 0 0 A=== ; Bad padding
+@ KEY 0 0 0 = ; Bad padding
+@ KEY 0 0 0 == ; Bad padding
+@ KEY 0 0 0 === ; Bad padding
+@ KEY 0 0 0 ==== ; Bad padding
+@ KEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/30_KEY.out b/tests/libzscanner/data/30_KEY.out
new file mode 100644
index 0000000..1ec998b
--- /dev/null
+++ b/tests/libzscanner/data/30_KEY.out
@@ -0,0 +1,86 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0019
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/31_DNSKEY.in b/tests/libzscanner/data/31_DNSKEY.in
new file mode 100644
index 0000000..871de97
--- /dev/null
+++ b/tests/libzscanner/data/31_DNSKEY.in
@@ -0,0 +1,32 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DNSKEY 0 0 0 AA== ; The simplest variant
+@ DNSKEY 65535 255 255 AA== ; Maximal numbers
+@ DNSKEY 0 0 RSAMD5 AA== ; Algorithm mnemonic
+@ DNSKEY 0 0 0 Zm8= ; One char padding
+@ DNSKEY 0 0 0 Zm9v ; Without padding
+@ DNSKEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ DNSKEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE48 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE48 0 0 0 AA== ; TYPE
+@ dnskey 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ DNSKEY
+@ DNSKEY ; Empty rdata
+@ DNSKEY \# 0 ; Hex empty rdata
+@ DNSKEY 65536 0 0 AA== ; Type overflow
+@ DNSKEY 0 256 0 AA== ; Key tag overflow
+@ DNSKEY 0 0 256 AA== ; Algorithm overflow
+@ DNSKEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ DNSKEY 0 0 0 A=== ; Bad padding
+@ DNSKEY 0 0 0 = ; Bad padding
+@ DNSKEY 0 0 0 == ; Bad padding
+@ DNSKEY 0 0 0 === ; Bad padding
+@ DNSKEY 0 0 0 ==== ; Bad padding
+@ DNSKEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/31_DNSKEY.out b/tests/libzscanner/data/31_DNSKEY.out
new file mode 100644
index 0000000..fa034c5
--- /dev/null
+++ b/tests/libzscanner/data/31_DNSKEY.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0030
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/32_APL.in b/tests/libzscanner/data/32_APL.in
new file mode 100644
index 0000000..ea9a747
--- /dev/null
+++ b/tests/libzscanner/data/32_APL.in
@@ -0,0 +1,30 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ APL ; The simplest variant - blank list
+@ APL 1:0.0.0.0/0 ; Minimal ipv4 prefix length
+@ APL 1:255.255.255.255/32 ; Maximal ipv4 prefix length
+@ APL 1:255.255.255.255/30 ; Prefix length isn't multiple of 8
+@ APL 2:::/0 ; Minimal ipv6 address
+@ APL 2:0::0/0 ; Minimal ipv6 prefix length
+@ APL 2:0::0/128 ; Maximal ipv6 prefix length
+@ APL 2:FFFF:FFFF:FFFF::/2 ; Trailing zeroes test
+@ APL !1:0.0.0.0/0 ; Negation flag
+@ APL 1:0.0.0.0/0 1:255.255.255.255/32 ; More APLs
+@ TYPE42 \# 4 00010000 ; TYPE + Hexadecimal rdata
+@ TYPE42 1:0.0.0.0/0 ; TYPE
+@ APL \# 0 ; Zero length rdata
+@ apl 1:0.0.0.0/0 ; Type in lower-case
+
+; KO
+@ APL 0:0.0.0.0/32 ; Bad address family
+@ APL x:0.0.0.0/32 ; Bad address family
+@ APL !x:0.0.0.0/32 ; Bad address family
+@ APL 2:0.0.0.0/32 ; Address family mismatch
+@ APL 1:0::0/32 ; Address family mismatch
+@ APL 1:0.0.0.0/33 ; Prefix length is too long
+@ APL 2:0::0/129 ; Prefix length is too long
+@ APL 2::/0 ; Bad ipv6 address
+@ APL 2:0::0/x ; Bad prefix length
+@ APL 1:0.0.0.0/ ; Missing prefix length
diff --git a/tests/libzscanner/data/32_APL.out b/tests/libzscanner/data/32_APL.out
new file mode 100644
index 0000000..50ec101
--- /dev/null
+++ b/tests/libzscanner/data/32_APL.out
@@ -0,0 +1,104 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00012004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00011E04FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00028000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00020206FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010080
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=0001000000012004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002A
+RDATA=00010000
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_IPV6
+------
+WARNG=ZS_BAD_APL
+------
+WARNG=ZS_BAD_APL
+------
diff --git a/tests/libzscanner/data/33_DS.in b/tests/libzscanner/data/33_DS.in
new file mode 100644
index 0000000..a5d805c
--- /dev/null
+++ b/tests/libzscanner/data/33_DS.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DS 0 0 0 00 ; The simplest variant
+@ DS 65535 255 255 00 ; Maximal numbers
+@ DS 0 RSAMD5 0 00 ; Algorithm mnemonic
+@ DS 0 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE43 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE43 0 0 0 00 ; TYPE
+@ ds 0 0 0 00 ; Type in lower-case
+
+; KO
+@ DS
+@ DS ; Empty rdata
+@ DS \# 0 ; Hex empty rdata
+@ DS 65536 0 0 00 ; Key tag overflow
+@ DS 0 256 0 00 ; Algorithm overflow
+@ DS 0 0 256 00 ; Digest type overflow
+@ DS 0 0 0 0 ; Continuous block length must be multiple of 2
+@ DS 0 0 0 00 0 ; Continuous block length must be multiple of 2
+@ DS 0 0 0 XX ; Bad hex character
+@ DS 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/33_DS.out b/tests/libzscanner/data/33_DS.out
new file mode 100644
index 0000000..2257119
--- /dev/null
+++ b/tests/libzscanner/data/33_DS.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002B
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/34_SSHFP.in b/tests/libzscanner/data/34_SSHFP.in
new file mode 100644
index 0000000..89c4de3
--- /dev/null
+++ b/tests/libzscanner/data/34_SSHFP.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SSHFP 0 0 00 ; The simplest variant
+@ SSHFP 255 255 00 ; Maximal numbers
+@ SSHFP 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE44 \# 3 000000 ; TYPE + Hexadecimal rdata
+@ TYPE44 0 0 00 ; TYPE
+@ sshfp 0 0 00 ; Type in lower-case
+
+; KO
+@ SSHFP
+@ SSHFP ; Empty rdata
+@ SSHFP \# 0 ; Hex empty rdata
+@ SSHFP 256 0 00 ; Algorithm overflow
+@ SSHFP 0 256 00 ; Fp type overflow
+@ SSHFP 0 0 0 ; Continuous block length must be multiple of 2
+@ SSHFP 0 0 00 0 ; Continuous block length must be multiple of 2
+@ SSHFP 0 0 XX ; Bad hex character
+@ SSHFP 0 0 ; Missing item
diff --git a/tests/libzscanner/data/34_SSHFP.out b/tests/libzscanner/data/34_SSHFP.out
new file mode 100644
index 0000000..da8ce6d
--- /dev/null
+++ b/tests/libzscanner/data/34_SSHFP.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002C
+RDATA=000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/35_IPSECKEY.in b/tests/libzscanner/data/35_IPSECKEY.in
new file mode 100644
index 0000000..dc56bd7
--- /dev/null
+++ b/tests/libzscanner/data/35_IPSECKEY.in
@@ -0,0 +1,29 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ IPSECKEY 0 0 0 . ; The simplest variant - no gw, no key
+@ IPSECKEY 255 3 255 . AA== ; Maximal numbers
+@ IPSECKEY 0 1 0 0.0.0.0 ; IPv4 address
+@ IPSECKEY 0 2 0 :: ; IPv6 address
+@ IPSECKEY 0 3 0 \0320\ \\\"\.\@\*.tld. ; Special chars in domain name
+@ IPSECKEY 0 0 1 . Zm8= ; One char padding
+@ IPSECKEY 0 0 1 . Zm9v ; Without padding
+@ IPSECKEY 0 0 1 . Zm9vYg== ; Two base64 blocks
+@ IPSECKEY 0 0 1 . Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE45 \# 3 000000 ; TYPE + Hexadecimal rdata
+@ TYPE45 0 0 1 . AA== ; TYPE
+@ ipseckey 0 0 1 . AA== ; Type in lower-case
+
+; KO
+@ IPSECKEY
+@ IPSECKEY ; Empty rdata
+@ IPSECKEY \# 0 ; Hex empty rdata
+@ IPSECKEY 256 0 0 . ; Precedence overflow
+@ IPSECKEY 0 4 0 . ; Unknown gateway
+@ IPSECKEY 0 0 256 . AA== ; Algorithm overflow
+@ IPSECKEY 0 0 0 . AA== ; If alg is 0 then key shouldn't be given
+@ IPSECKEY 0 0 0 a% ; Bad domain name char
+@ IPSECKEY 0 0 1 . A ; Continuous block length must be multiple of 4
+@ IPSECKEY 0 0 1 . = ; Bad padding
+@ IPSECKEY 0 0 ; Missing item
diff --git a/tests/libzscanner/data/35_IPSECKEY.out b/tests/libzscanner/data/35_IPSECKEY.out
new file mode 100644
index 0000000..718ce22
--- /dev/null
+++ b/tests/libzscanner/data/35_IPSECKEY.out
@@ -0,0 +1,94 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=FF03FF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00010000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00020000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000300082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000001666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002D
+RDATA=00000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_GATEWAY
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_GATEWAY_KEY
+------
+WARNG=ZS_BAD_GATEWAY
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_GATEWAY_KEY
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/36_RRSIG.in b/tests/libzscanner/data/36_RRSIG.in
new file mode 100644
index 0000000..cb873fc
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.in
@@ -0,0 +1,46 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ RRSIG TYPE0 0 0 0 0 0 0 . AA== ; The simplest variant
+@ RRSIG A 2 3 4 5 6 7 \008. CQ== ; Human visual test - block numbering
+@ RRSIG TYPE65535 255 255 4294967295 4294967295 4294967295 65535 . AA== ; Maximal numbers
+@ RRSIG TYPE0 RSAMD5 0 0 0 0 0 . AA== ; Algorithm mnemonic
+@ RRSIG A 0 0 0 19700101000000 0 0 . AA== ; Minimal date format
+@ RRSIG A 0 0 0 0 21051231235959 0 . AA== ; Maximal date format (zscanner original limit)
+@ RRSIG A 0 0 0 0 22251231235959 0 . AA== ; Maximal date format (zscanner limit)
+@ RRSIG TYPE0 0 0 0 0 0 0 \0320\ \\\"\.\@\*.tld. AA== ; Special chars in domain name
+@ RRSIG A 0 0 0 0 0 0 . Zm8= ; One char padding
+@ RRSIG A 0 0 0 0 0 0 . Zm9v ; Without padding
+@ RRSIG A 0 0 0 0 0 0 . Zm9vYg== ; Two base64 blocks
+@ RRSIG A 0 0 0 0 0 0 . Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE46 \# 20 000100000000000000000000000000000000 00 00 ; TYPE + Hexadecimal rdata
+@ TYPE46 A 0 0 0 0 0 0 . AA== ; TYPE
+@ rrsig A 0 0 0 0 0 0 . AA== ; Type in lower-case
+
+; KO
+@ RRSIG
+@ RRSIG ; Empty rdata
+@ RRSIG \# 0 ; Hex empty rdata
+@ RRSIG X 0 0 0 0 0 0 . AA== ; Unknown type
+@ RRSIG TYPE65536 0 0 0 0 0 0 . AA== ; Type overflow
+@ RRSIG A 256 0 0 0 0 0 . AA== ; Algorithm overflow
+@ RRSIG A 0 256 0 0 0 0 . AA== ; Labels overflow
+@ RRSIG A 0 0 4294967296 0 0 0 . AA== ; TTL overflow
+@ RRSIG A 0 0 0 9294967296 0 0 . AA== ; Sig. exp. overflow
+@ RRSIG A 0 0 0 0 4294967296 0 . AA== ; Sig. inc. overflow
+@ RRSIG A 0 0 0 0 0 65536 . AA== ; Key tag overflow
+@ RRSIG A 0 0 0 0 22260101000000 0 . AA== ; Date overflow
+@ RRSIG A 0 0 0 0 2226010100000x 0 . AA== ; Bad timestamp char
+@ RRSIG A 0 0 0 0 222601010000000 0 . AA== ; Bad timestamp length
+@ RRSIG A 0 0 0 0 0 0 a% AA== ; Bad domain char
+@ RRSIG A 0 0 0 0 0 0 . A ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . AB ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . ABC ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . AA == ; Continuous block length must be multiple of 4
+@ RRSIG A 0 0 0 0 0 0 . A=== ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . = ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . == ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . === ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . ==== ; Bad padding
+@ RRSIG A 0 0 0 0 0 0 . ; Missing item
diff --git a/tests/libzscanner/data/36_RRSIG.out b/tests/libzscanner/data/36_RRSIG.out
new file mode 100644
index 0000000..dff1471
--- /dev/null
+++ b/tests/libzscanner/data/36_RRSIG.out
@@ -0,0 +1,140 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0000000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010203000000040000000500000006000701080009
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0000010000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=000100000000000000000000FFCEDD7F00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=000100000000000000000000E1853CFF00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=000000000000000000000000000000000000082030205C222E402A03746C640000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=00010000000000000000000000000000000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002E
+RDATA=0001000000000000000000000000000000000000
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_UNSUPPORTED_TYPE
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_DATE
+------
+WARNG=ZS_BAD_TIMESTAMP_CHAR
+------
+WARNG=ZS_BAD_TIMESTAMP_LENGTH
+------
+WARNG=ZS_BAD_DNAME_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/37_NSEC.in b/tests/libzscanner/data/37_NSEC.in
new file mode 100644
index 0000000..1efa4ea
--- /dev/null
+++ b/tests/libzscanner/data/37_NSEC.in
@@ -0,0 +1,20 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC . ; The simplest variant - without bitmap
+@ NSEC \0320\ \\\"\.\@\*.tld. ; Special chars in domain name
+@ NSEC . TYPE0 ; Minimal type number
+@ NSEC . TYPE65535 ; Maximal type number
+@ NSEC . TYPE0 A NS ; First bitmap window
+@ NSEC . TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE47 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE47 . ; TYPE
+@ nsec . ; Type in lower-case
+
+; KO
+@ NSEC
+@ NSEC ; Empty rdata
+@ NSEC \# 0 ; Hex empty rdata
+@ NSEC . TYPE65536 ; Type number overflow
+@ NSEC . X ; Unknown type
diff --git a/tests/libzscanner/data/37_NSEC.out b/tests/libzscanner/data/37_NSEC.out
new file mode 100644
index 0000000..7a62def
--- /dev/null
+++ b/tests/libzscanner/data/37_NSEC.out
@@ -0,0 +1,64 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=082030205C222E402A03746C6400
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=002F
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/38_DHCID.in b/tests/libzscanner/data/38_DHCID.in
new file mode 100644
index 0000000..4c0642a
--- /dev/null
+++ b/tests/libzscanner/data/38_DHCID.in
@@ -0,0 +1,26 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ DHCID AA== ; The simplest variant
+@ DHCID Zm8= ; One char padding
+@ DHCID Zm9v ; Without padding
+@ DHCID Zm9vYg== ; Two base64 blocks
+@ DHCID Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE49 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE49 AA== ; TYPE
+@ dhcid AA== ; Type in lower-case
+
+; KO
+@ DHCID
+@ DHCID ; Empty rdata
+@ DHCID \# 0 ; Hex empty rdata
+@ DHCID A ; Continuous block length must be multiple of 4
+@ DHCID AB ; Continuous block length must be multiple of 4
+@ DHCID ABC ; Continuous block length must be multiple of 4
+@ DHCID AA == ; Continuous block length must be multiple of 4
+@ DHCID A=== ; Bad padding
+@ DHCID = ; Bad padding
+@ DHCID == ; Bad padding
+@ DHCID === ; Bad padding
+@ DHCID ==== ; Bad padding
diff --git a/tests/libzscanner/data/38_DHCID.out b/tests/libzscanner/data/38_DHCID.out
new file mode 100644
index 0000000..8dc8ec9
--- /dev/null
+++ b/tests/libzscanner/data/38_DHCID.out
@@ -0,0 +1,72 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0031
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/39_NSEC3.in b/tests/libzscanner/data/39_NSEC3.in
new file mode 100644
index 0000000..cadbfa9
--- /dev/null
+++ b/tests/libzscanner/data/39_NSEC3.in
@@ -0,0 +1,46 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC3 0 0 0 - 00====== ; The simplest variant - without bitmap
+@ NSEC3 255 255 65535 - 00====== ; Maximal numbers
+@ NSEC3 0 0 0 00FF 00====== ; Hex string
+@ NSEC3 0 0 0 - 00====== ; Eight char padding
+@ NSEC3 0 0 0 - CPNG==== ; Four char padding
+@ NSEC3 0 0 0 - CPNMU=== ; Three char padding
+@ NSEC3 0 0 0 - CPNMUOG= ; One char padding
+@ NSEC3 0 0 0 - CPNMUOJ1 ; Without padding
+@ NSEC3 0 0 0 - CPNMUOJ1E8====== ; Two base32hex blocks
+@ NSEC3 0 0 0 - 00====== TYPE0 ; Minimal type number
+@ NSEC3 0 0 0 - 00====== TYPE65535 ; Maximal type number
+@ NSEC3 0 0 0 - 00====== TYPE0 A NS ; First bitmap window
+@ NSEC3 0 0 0 - 00====== TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE50 \# 7 00000000000100 ; TYPE + Hexadecimal rdata
+@ TYPE50 0 0 0 - 00====== ; TYPE
+@ nsec3 0 0 0 - 00====== ; Type in lower-case
+
+; KO
+@ NSEC3
+@ NSEC3 ; Empty rdata
+@ NSEC3 \# 0 ; Hex empty rdata
+@ NSEC3 256 0 0 - 00====== ; Algorithm overflow
+@ NSEC3 0 256 0 - 00====== ; Flags overflow
+@ NSEC3 0 0 65536 - 00====== ; Iterations overflow
+@ NSEC3 0 0 0 0 00====== ; Hex block must be multiple of 2
+@ NSEC3 0 0 0 0X 00====== ; Bad hex char
+@ NSEC3 0 0 0 00 FF 00====== ; Hex string with blank space inside
+@ NSEC3 0 0 0 - 1 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 12 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 1234 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 12345 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123456 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 1234567 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - 123456 78 ; Continuous block length must be multiple of 8
+@ NSEC3 0 0 0 - ======== ; Bad padding
+@ NSEC3 0 0 0 - 1======= ; Bad padding
+@ NSEC3 0 0 0 - 123===== ; Bad padding
+@ NSEC3 0 0 0 - 123456== ; Bad padding
+@ NSEC3 0 0 0 - CPNMUOJ1 E8====== ; Two base32hex blocks with blank space between them
+@ NSEC3 0 0 0 - 00====== TYPE65536 ; Type number overflow
+@ NSEC3 0 0 0 - 00====== X ; Unknown type
diff --git a/tests/libzscanner/data/39_NSEC3.out b/tests/libzscanner/data/39_NSEC3.out
new file mode 100644
index 0000000..1aaec4f
--- /dev/null
+++ b/tests/libzscanner/data/39_NSEC3.out
@@ -0,0 +1,144 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=FFFFFFFF000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000200FF0100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000002666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000003666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000004666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000005666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000006666F6F626172
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=000000000001000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0032
+RDATA=00000000000100
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BASE32HEX_CHAR
+------
+WARNG=ZS_BAD_BITMAP
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/40_NSEC3PARAM.in b/tests/libzscanner/data/40_NSEC3PARAM.in
new file mode 100644
index 0000000..694e3f7
--- /dev/null
+++ b/tests/libzscanner/data/40_NSEC3PARAM.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ NSEC3PARAM 0 0 0 - ; The simplest variant
+@ NSEC3PARAM 255 255 65535 - ; Maximal numbers
+@ NSEC3PARAM 0 0 0 0102FF ; Hex string
+@ TYPE51 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE51 0 0 0 - ; TYPE
+@ nsec3param 0 0 0 - ; Type in lower-case
+
+; KO
+@ NSEC3PARAM
+@ NSEC3PARAM ; Empty rdata
+@ NSEC3PARAM \# 0 ; Hex empty rdata
+@ NSEC3PARAM 256 0 0 00 ; Algorithm overflow
+@ NSEC3PARAM 0 256 0 00 ; Flags overflow
+@ NSEC3PARAM 0 0 65536 00 ; Iterations overflow
+@ NSEC3PARAM 0 0 0 0 ; Hex block length must be multiple of 2
+@ NSEC3PARAM 0 0 0 0x ; Bad hex char
+@ NSEC3PARAM 0 0 0 00 00 ; Hex block must not contain blank spaces
+@ NSEC3PARAM 0 0 0 00 x ; Unexpected item
+@ NSEC3PARAM 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/40_NSEC3PARAM.out b/tests/libzscanner/data/40_NSEC3PARAM.out
new file mode 100644
index 0000000..83b9bb1
--- /dev/null
+++ b/tests/libzscanner/data/40_NSEC3PARAM.out
@@ -0,0 +1,58 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=00000000030102FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0033
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_REST
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/41_TLSA.in b/tests/libzscanner/data/41_TLSA.in
new file mode 100644
index 0000000..927498e
--- /dev/null
+++ b/tests/libzscanner/data/41_TLSA.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ TLSA 0 0 0 00 ; The simplest variant
+@ TLSA 255 255 255 00 ; Maximal numbers
+@ TLSA 0 0 0 0102 00 FF ; Hex string with blank spaces inside
+@ TYPE52 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE52 0 0 0 00 ; TYPE
+@ tlsa 0 0 0 00 ; Type in lower-case
+
+; KO
+@ TLSA
+@ TLSA ; Empty rdata
+@ TLSA \# 0 ; Hex empty rdata
+@ TLSA 256 0 0 00 ; Algorithm overflow
+@ TLSA 0 256 0 00 ; Flags overflow
+@ TLSA 0 0 256 00 ; Iterations overflow
+@ TLSA 0 0 0 0 ; Hex block length must be multiple of 2
+@ TLSA 0 0 0 0x ; Bad hex char
+@ TLSA 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/41_TLSA.out b/tests/libzscanner/data/41_TLSA.out
new file mode 100644
index 0000000..6f5b6a1
--- /dev/null
+++ b/tests/libzscanner/data/41_TLSA.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=FFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=000000010200FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0034
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/42_LOC.in b/tests/libzscanner/data/42_LOC.in
new file mode 100644
index 0000000..9eaae1d
--- /dev/null
+++ b/tests/libzscanner/data/42_LOC.in
@@ -0,0 +1,64 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ LOC 1 N 1 E 0 ; The simplest case
+@ LOC 0 1 N 1 E 0 ; Combination of parameters
+@ LOC 0 0 1 N 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 0 1 E 0 ; Combination of parameters
+@ LOC 1 N 0 0 1 E 0m ; Combination of parameters
+@ LOC 1 N 1 E 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 1m ; Combination of parameters
+@ LOC 1 N 1 E 0 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 0 1m ; Combination of parameters
+@ LOC 1 N 1 E 0 0 0 1 ; Combination of parameters
+@ LOC 1 N 1 E 0 0 0 1m ; Combination of parameters
+@ LOC 0 0 0 N 0 0 0 E -100000.00 0 0 0 ; Minimal values
+@ LOC 90 59 59.999 S 180 59 59.999 W 42849672.95m 90000000.00m 90000000.00m 90000000.00m ; Maximal values
+@ LOC 0 S 0 0 0.001 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 0.01 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 0.1 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 1.0 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 0 10 W 0 ; Float dd.ddd test
+@ LOC 0 S 0 W 0 0.01 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 0.10 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1.0 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 100 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 100000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 1000000 ; Number to [mantisa,exponent] test
+@ LOC 0 S 0 W 0 10000000 ; Number to [mantisa,exponent] test
+@ LOC \# 16 00 00 00 00 00000000 00000000 00000000 ; Hexadecimal rdata
+@ TYPE29 \# 16 00 00 00 00 00000000 00000000 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE29 0 N 0 E 0 ; TYPE
+@ loc 0 N 0 E 0 ; Type in lower-case
+
+; KO
+@ LOC
+@ LOC ; Empty rdata
+@ LOC \# 0 ; Hex empty rdata
+@ LOC 91 0 0 N 0 0 0 E 0 0 0 0 ; Degree overflow
+@ LOC 0 60 0 N 0 0 0 E 0 0 0 0 ; Minute overflow
+@ LOC 0 0 60 0 N 0 0 0 E 0 0 0 0 ; Second overflow
+@ LOC 0 0 0 N 181 0 0 E 0 0 0 0 ; Degree overflow
+@ LOC 0 0 0 N 0 60 0 E 0 0 0 0 ; Minute overflow
+@ LOC 0 0 0 N 0 0 60 E 0 0 0 0 ; Second overflow
+@ LOC 0 0 0 N 0 0 0 E 42849672.96 0 0 0 ; Altitude overflow
+@ LOC 0 0 0 N 0 0 0 E 42849673 0 0 0 ; Altitude overflow
+@ LOC 0 0 0 N 0 0 0 E -100000.01 0 0 0 ; Altitude underflow
+@ LOC 0 0 0 N 0 0 0 E -100001 0 0 0 ; Altitude underflow
+@ LOC 0 0 0 N 0 0 0 E 0 90000000.01 0 0 ; Size overflow
+@ LOC 0 0 0 N 0 0 0 E 0 90000001 0 0 ; Size overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 90000000.01 0 ; HP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 90000001 0 ; HP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 90000000.01 ; VP overflow
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 90000001 ; VP overflow
+@ LOC 1 1 E 0 ; Missing N or S
+@ LOC 1 x 1 E 0 ; Bad letter
+@ LOC 1 N 1 0 ; Missing E or W
+@ LOC 1 N 1 x 0 ; Bad letter
+@ LOC 1 N 1 E ; Missing altitude
+@ LOC 0 0 0 N 0 0 0 E 0 0 0 0 x ; Unexpected item
diff --git a/tests/libzscanner/data/42_LOC.out b/tests/libzscanner/data/42_LOC.out
new file mode 100644
index 0000000..730cc3b
--- /dev/null
+++ b/tests/libzscanner/data/42_LOC.out
@@ -0,0 +1,248 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138000EA608036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800003E88036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808000EA6000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE80800003E800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE80800003E800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=001216138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000012138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000012138036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000000128036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=000000128036EE808036EE8000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000800000008000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=009999996C79388159295F81FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFFFF00989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFFF600989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFF9C00989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFFC1800989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000007FFFD8F000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00101613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00111613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00131613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00141613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00151613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00161613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00171613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00181613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00191613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00000000000000000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=001D
+RDATA=00121613800000008000000000989680
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
+WARNG=ZS_BAD_LOC_DATA
+------
diff --git a/tests/libzscanner/data/43_EUI48.in b/tests/libzscanner/data/43_EUI48.in
new file mode 100644
index 0000000..a2abbec
--- /dev/null
+++ b/tests/libzscanner/data/43_EUI48.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ EUI48 00-00-00-00-00-00 ; The simplest case
+@ EUI48 FF-FF-FF-FF-FF-FF ; The maximal case
+@ EUI48 aa-bb-cc-dd-ee-ff ; Lower-case
+@ EUI48 \# 6 000000000000 ; Hexadecimal rdata
+@ TYPE108 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE108 00-00-00-00-00-00 ; TYPE
+@ eui48 00-00-00-00-00-00 ; Type in lower-case
+
+; KO
+@ EUI48
+@ EUI48 ; Empty rdata
+@ EUI48 \# 0 ; Hex empty rdata
+@ EUI48 00-00-00-00-00 ; Too few hex pairs
+@ EUI48 00-00-00-00-00-00-00 ; Too many hex pairs
+@ EUI48 00-00-00-00-00-0 ; Missing char in a hex pair
+@ EUI48 00:00-00-00-00-00 ; Bad separator
+@ EUI48 00-00-00-x0-00-00 ; Bad character
+@ EUI48 00-00-00-00-00-00 x ; Unexpected item
diff --git a/tests/libzscanner/data/43_EUI48.out b/tests/libzscanner/data/43_EUI48.out
new file mode 100644
index 0000000..a54a314
--- /dev/null
+++ b/tests/libzscanner/data/43_EUI48.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=AABBCCDDEEFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006C
+RDATA=000000000000
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_DASH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/44_EUI64.in b/tests/libzscanner/data/44_EUI64.in
new file mode 100644
index 0000000..10f6346
--- /dev/null
+++ b/tests/libzscanner/data/44_EUI64.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ EUI64 00-00-00-00-00-00-00-00 ; The simplest case
+@ EUI64 FF-FF-FF-FF-FF-FF-FF-FF ; The maximal case
+@ EUI64 aa-bb-cc-dd-ee-ff-01-02 ; Lower-case
+@ EUI64 \# 8 0000000000000000 ; Hexadecimal rdata
+@ TYPE109 \# 8 0000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE109 00-00-00-00-00-00-00-00 ; TYPE
+@ eui64 00-00-00-00-00-00-00-00 ; Type in lower-case
+
+; KO
+@ EUI64
+@ EUI64 ; Empty rdata
+@ EUI64 \# 0 ; Hex empty rdata
+@ EUI64 00-00-00-00-00-00-00 ; Too few hex pairs
+@ EUI64 00-00-00-00-00-00-00-00-00 ; Too many hex pairs
+@ EUI64 00-00-00-00-00-00-00-0 ; Missing char in a hex pair
+@ EUI64 00:00-00-00-00-00-00-00 ; Bad separator
+@ EUI64 00-00-00-x0-00-00-00-00 ; Bad character
+@ EUI64 00-00-00-00-00-00-00-00 x ; Unexpected item
diff --git a/tests/libzscanner/data/44_EUI64.out b/tests/libzscanner/data/44_EUI64.out
new file mode 100644
index 0000000..4cce5f5
--- /dev/null
+++ b/tests/libzscanner/data/44_EUI64.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=FFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=AABBCCDDEEFF0102
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006D
+RDATA=0000000000000000
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_EUI_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_DASH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/45_NID.in b/tests/libzscanner/data/45_NID.in
new file mode 100644
index 0000000..a85f9c3
--- /dev/null
+++ b/tests/libzscanner/data/45_NID.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The NID is the same as the L64, so there are the differences and basics only.
+
+; OK
+@ NID 0 0000:0000:0000:0000 ; The simplest case
+@ NID \# 10 00000000000000000000 ; Hexadecimal rdata
+@ TYPE104 \# 10 00000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE104 0 0000:0000:0000:0000 ; TYPE
+@ nid 0 0000:0000:0000:0000 ; Type in lower-case
+
+; KO
+@ NID
diff --git a/tests/libzscanner/data/45_NID.out b/tests/libzscanner/data/45_NID.out
new file mode 100644
index 0000000..a2bfb4c
--- /dev/null
+++ b/tests/libzscanner/data/45_NID.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0068
+RDATA=00000000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/46_L32.in b/tests/libzscanner/data/46_L32.in
new file mode 100644
index 0000000..866e953
--- /dev/null
+++ b/tests/libzscanner/data/46_L32.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ L32 0 0.0.0.0 ; The simplest case
+@ L32 65535 255.255.255.255 ; The maximal case
+@ L32 \# 6 000000000000 ; Hexadecimal rdata
+@ TYPE105 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE105 0 0.0.0.0 ; TYPE
+@ l32 0 0.0.0.0 ; Type in lower-case
+
+; KO
+@ L32
+@ L32 ; Empty rdata
+@ L32 \# 0 ; Hex empty rdata
+@ L32 65536 0.0.0.0 ; Too big preference
+@ L32 0 0.0.0.256 ; 8-bit overflow
+@ L32 0 0.0.0 ; Short address
+@ L32 0 0.0.0.0.0 ; Long address
+@ L32 0 0.0.0.x ; Bad character
+@ L32 0 0.0.0.0 x ; Unexpected item
diff --git a/tests/libzscanner/data/46_L32.out b/tests/libzscanner/data/46_L32.out
new file mode 100644
index 0000000..e9c77b5
--- /dev/null
+++ b/tests/libzscanner/data/46_L32.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0069
+RDATA=000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/47_L64.in b/tests/libzscanner/data/47_L64.in
new file mode 100644
index 0000000..b4aabf3
--- /dev/null
+++ b/tests/libzscanner/data/47_L64.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ L64 0 0000:0000:0000:0000 ; The simplest case
+@ L64 65535 FFFF:FFFF:FFFF:FFFF ; The maximal case
+@ L64 0 abcd:ef00:0000:0000 ; Lower-case
+@ L64 \# 10 00000000000000000000 ; Hexadecimal rdata
+@ TYPE106 \# 10 00000000000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE106 0 0000:0000:0000:0000 ; TYPE
+@ l64 0 0000:0000:0000:0000 ; Type in lower-case
+
+; KO
+@ L64
+@ L64 ; Empty rdata
+@ L64 \# 0 ; Hex empty rdata
+@ L64 65536 ; Too big preference
+@ L64 0 0000:0000:0000 ; Missing label
+@ L64 0 0000:0000:0000:0000:0000 ; Too many labels
+@ L64 0 0000:0000:0000:000 ; Missing hex character
+@ L64 0 0000:0000:0000-0000 ; Bad separator
+@ L64 0 0000:0000:0000:x000 ; Bad hex character
+@ L64 0 0000:0000:0000:0000 x ; Unexpected item
diff --git a/tests/libzscanner/data/47_L64.out b/tests/libzscanner/data/47_L64.out
new file mode 100644
index 0000000..106ee58
--- /dev/null
+++ b/tests/libzscanner/data/47_L64.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=FFFFFFFFFFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=0000ABCDEF0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006A
+RDATA=00000000000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_L64_LENGTH
+------
+WARNG=ZS_BAD_L64_LENGTH
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_CHAR_COLON
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/48_LP.in b/tests/libzscanner/data/48_LP.in
new file mode 100644
index 0000000..bc537d0
--- /dev/null
+++ b/tests/libzscanner/data/48_LP.in
@@ -0,0 +1,14 @@
+$ORIGIN .
+$TTL 1
+
+; The LP is the same as the MX, so there are the differences and basics only.
+
+; OK
+@ LP 1 mail ; Relative dname
+@ LP \# 3 0001 00 ; Hexadecimal rdata
+@ TYPE107 \# 3 0001 00 ; TYPE + Hexadecimal rdata
+@ TYPE107 1 @ ; TYPE
+@ lp 1 @ ; Type in lower-case
+
+; KO
+@ LP
diff --git a/tests/libzscanner/data/48_LP.out b/tests/libzscanner/data/48_LP.out
new file mode 100644
index 0000000..4961c7e
--- /dev/null
+++ b/tests/libzscanner/data/48_LP.out
@@ -0,0 +1,32 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=0001046D61696C00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=006B
+RDATA=000100
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/49_CDS.in b/tests/libzscanner/data/49_CDS.in
new file mode 100644
index 0000000..7b6c347
--- /dev/null
+++ b/tests/libzscanner/data/49_CDS.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CDS 0 0 0 00 ; The simplest variant
+@ CDS 65535 255 255 00 ; Maximal numbers
+@ CDS 0 RSAMD5 0 00 ; Algorithm mnemonic
+@ CDS 0 0 0 01 02 0304 ; Hex block with blank spaces between them
+@ TYPE59 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE59 0 0 0 00 ; TYPE
+@ cds 0 0 0 00 ; Type in lower-case
+
+; KO
+@ CDS
+@ CDS ; Empty rdata
+@ CDS \# 0 ; Hex empty rdata
+@ CDS 65536 0 0 00 ; Key tag overflow
+@ CDS 0 256 0 00 ; Algorithm overflow
+@ CDS 0 0 256 00 ; Digest type overflow
+@ CDS 0 0 0 0 ; Continuous block length must be multiple of 2
+@ CDS 0 0 0 00 0 ; Continuous block length must be multiple of 2
+@ CDS 0 0 0 XX ; Bad hex character
+@ CDS 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/49_CDS.out b/tests/libzscanner/data/49_CDS.out
new file mode 100644
index 0000000..de40722
--- /dev/null
+++ b/tests/libzscanner/data/49_CDS.out
@@ -0,0 +1,62 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000010000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000001020304
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003B
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/50_CDNSKEY.in b/tests/libzscanner/data/50_CDNSKEY.in
new file mode 100644
index 0000000..dc91462
--- /dev/null
+++ b/tests/libzscanner/data/50_CDNSKEY.in
@@ -0,0 +1,32 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CDNSKEY 0 0 0 AA== ; The simplest variant
+@ CDNSKEY 65535 255 255 AA== ; Maximal numbers
+@ CDNSKEY 0 0 RSAMD5 AA== ; Algorithm mnemonic
+@ CDNSKEY 0 0 0 Zm8= ; One char padding
+@ CDNSKEY 0 0 0 Zm9v ; Without padding
+@ CDNSKEY 0 0 0 Zm9vYg== ; Two base64 blocks
+@ CDNSKEY 0 0 0 Zm9v YmE= ; Two base64 blocks with blank space between them
+@ TYPE60 \# 5 0000000000 ; TYPE + Hexadecimal rdata
+@ TYPE60 0 0 0 AA== ; TYPE
+@ cdnskey 0 0 0 AA== ; Type in lower-case
+
+; KO
+@ CDNSKEY
+@ CDNSKEY ; Empty rdata
+@ CDNSKEY \# 0 ; Hex empty rdata
+@ CDNSKEY 65536 0 0 AA== ; Type overflow
+@ CDNSKEY 0 256 0 AA== ; Key tag overflow
+@ CDNSKEY 0 0 256 AA== ; Algorithm overflow
+@ CDNSKEY 0 0 0 A ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 AB ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 ABC ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 AA == ; Continuous block length must be multiple of 4
+@ CDNSKEY 0 0 0 A=== ; Bad padding
+@ CDNSKEY 0 0 0 = ; Bad padding
+@ CDNSKEY 0 0 0 == ; Bad padding
+@ CDNSKEY 0 0 0 === ; Bad padding
+@ CDNSKEY 0 0 0 ==== ; Bad padding
+@ CDNSKEY 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/50_CDNSKEY.out b/tests/libzscanner/data/50_CDNSKEY.out
new file mode 100644
index 0000000..3100313
--- /dev/null
+++ b/tests/libzscanner/data/50_CDNSKEY.out
@@ -0,0 +1,92 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=FFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=00000000666F6F6261
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003C
+RDATA=0000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_BASE64_CHAR
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/51_URI.in b/tests/libzscanner/data/51_URI.in
new file mode 100644
index 0000000..8d18f42
--- /dev/null
+++ b/tests/libzscanner/data/51_URI.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ URI 0 0 a ; The simplest variant
+@ URI 65535 65535 ftp://a ; Maximal priority and weight
+@ URI 0 0 "ftp://a" ; Quoted target
+@ URI 0 0 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ; Target longer than 255
+@ TYPE256 \# 5 0000000061 ; TYPE + Hexadecimal rdata
+@ TYPE256 0 0 a ; TYPE
+@ uri 0 0 a ; Type in lower-case
+
+; OK extensions
+@ URI 0 0 "" ; Empty target
+
+; KO
+@ URI
+@ URI ; Empty rdata
+@ URI \# 0 ; Hex empty rdata
+@ URI 65536 0 a ; Priority overflow
+@ URI 0 65536 a ; Weight overflow
+@ URI 0 0 a a ; Unexpected item
diff --git a/tests/libzscanner/data/51_URI.out b/tests/libzscanner/data/51_URI.out
new file mode 100644
index 0000000..9dcf933
--- /dev/null
+++ b/tests/libzscanner/data/51_URI.out
@@ -0,0 +1,60 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=FFFFFFFF6674703A2F2F61
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=000000006674703A2F2F61
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=00000000303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=0000000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0100
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/52_CAA.in b/tests/libzscanner/data/52_CAA.in
new file mode 100644
index 0000000..7043ce5
--- /dev/null
+++ b/tests/libzscanner/data/52_CAA.in
@@ -0,0 +1,23 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CAA 0 a a ; The simplest variant
+@ CAA 255 a a ; Maximal flags
+@ CAA 0 a "a ; b" ; Quoted value
+@ CAA 0 a 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 ; Value longer than 255
+@ TYPE257 \# 4 00016161 ; TYPE + Hexadecimal rdata
+@ TYPE257 0 a a ; TYPE
+@ caa 0 a a ; Type in lower-case
+
+; OK fallbacks and extensions
+@ CAA 0 "" a ; Empty tag
+@ CAA 0 "a" a ; Quoted tag
+@ CAA 0 abcdefghijklmnopqrstuvwxyz0123456789 a ; All allowed characters, longer than 15
+
+; KO
+@ CAA
+@ CAA ; Empty rdata
+@ CAA \# 0 ; Hex empty rdata
+@ CAA 256 a a ; Flags overflow
+@ CAA 0 a a a ; Unexpected item
diff --git a/tests/libzscanner/data/52_CAA.out b/tests/libzscanner/data/52_CAA.out
new file mode 100644
index 0000000..e5eefdf
--- /dev/null
+++ b/tests/libzscanner/data/52_CAA.out
@@ -0,0 +1,70 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=FF016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161203B2062
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=000161303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=000061
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00016161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0101
+RDATA=00246162636465666768696A6B6C6D6E6F707172737475767778797A3031323334353637383961
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_REST
+------
diff --git a/tests/libzscanner/data/53_SMIMEA.in b/tests/libzscanner/data/53_SMIMEA.in
new file mode 100644
index 0000000..aec390f
--- /dev/null
+++ b/tests/libzscanner/data/53_SMIMEA.in
@@ -0,0 +1,13 @@
+$ORIGIN .
+$TTL 1
+
+; SMIMEA is the same as TLSA, so there are differences and basics only.
+
+; OK
+@ SMIMEA 0 0 0 00 ; The simplest variant
+@ TYPE53 \# 4 00000000 ; TYPE + Hexadecimal rdata
+@ TYPE53 0 0 0 00 ; TYPE
+@ smimea 0 0 0 00 ; Type in lower-case
+
+; KO
+@ SMIMEA
diff --git a/tests/libzscanner/data/53_SMIMEA.out b/tests/libzscanner/data/53_SMIMEA.out
new file mode 100644
index 0000000..c270a7d
--- /dev/null
+++ b/tests/libzscanner/data/53_SMIMEA.out
@@ -0,0 +1,26 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0035
+RDATA=00000000
+------
+WARNG=ZS_BAD_NUMBER
+------
diff --git a/tests/libzscanner/data/54_OPENPGPKEY.in b/tests/libzscanner/data/54_OPENPGPKEY.in
new file mode 100644
index 0000000..a788a28
--- /dev/null
+++ b/tests/libzscanner/data/54_OPENPGPKEY.in
@@ -0,0 +1,13 @@
+$ORIGIN .
+$TTL 1
+
+; OPENPGPKEY is the same as DHCID, so there are differences and basics only.
+
+; OK
+@ OPENPGPKEY AA== ; The simplest variant
+@ TYPE61 \# 1 00 ; TYPE + Hexadecimal rdata
+@ TYPE61 AA== ; TYPE
+@ openpgpkey AA== ; Type in lower-case
+
+; KO
+@ OPENPGPKEY
diff --git a/tests/libzscanner/data/54_OPENPGPKEY.out b/tests/libzscanner/data/54_OPENPGPKEY.out
new file mode 100644
index 0000000..8741e4e
--- /dev/null
+++ b/tests/libzscanner/data/54_OPENPGPKEY.out
@@ -0,0 +1,26 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003D
+RDATA=00
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/55_CSYNC.in b/tests/libzscanner/data/55_CSYNC.in
new file mode 100644
index 0000000..97dcfa2
--- /dev/null
+++ b/tests/libzscanner/data/55_CSYNC.in
@@ -0,0 +1,22 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ CSYNC 0 0 ; The simplest variant - without bitmap
+@ CSYNC 4294967295 65535 ; Maximal numbers
+@ CSYNC 0 0 TYPE0 ; Minimal type number
+@ CSYNC 0 0 TYPE65535 ; Maximal type number
+@ CSYNC 0 0 TYPE0 A NS ; First bitmap window
+@ CSYNC 0 0 TYPE0 TYPE256 TYPE512 TYPE32768 ; First, second, third and 128. bitmap window
+@ TYPE62 \# 6 000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE62 0 0 ; TYPE
+@ csync 0 0 ; Type in lower-case
+
+; KO
+@ CSYNC
+@ CSYNC ; Empty rdata
+@ CSYNC \# 0 ; Hex empty rdata
+@ CSYNC 4294967296 0 ; Serial overflow
+@ CSYNC 0 65536 ; Flags overflow
+@ CSYNC 0 0 TYPE65536 ; Type number overflow
+@ CSYNC 0 0 X ; Unknown type
diff --git a/tests/libzscanner/data/55_CSYNC.out b/tests/libzscanner/data/55_CSYNC.out
new file mode 100644
index 0000000..d762c2b
--- /dev/null
+++ b/tests/libzscanner/data/55_CSYNC.out
@@ -0,0 +1,68 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=FFFFFFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000000180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000FF200000000000000000000000000000000000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=0000000000000001E0
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000000180010180020180800180
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003E
+RDATA=000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_BITMAP
+------
diff --git a/tests/libzscanner/data/56_ZONEMD.in b/tests/libzscanner/data/56_ZONEMD.in
new file mode 100644
index 0000000..2342350
--- /dev/null
+++ b/tests/libzscanner/data/56_ZONEMD.in
@@ -0,0 +1,21 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ ZONEMD 0 0 0 00 ; The simplest variant
+@ ZONEMD 4294967295 255 255 00 ; Maximal numbers
+@ ZONEMD 0 0 0 0102 00 FF ; Hex string with blank spaces inside
+@ TYPE63 \# 7 00000000000000 ; TYPE + Hexadecimal rdata
+@ TYPE63 0 0 0 00 ; TYPE
+@ zonemd 0 0 0 00 ; Type in lower-case
+
+; KO
+@ ZONEMD
+@ ZONEMD ; Empty rdata
+@ ZONEMD \# 0 ; Hex empty rdata
+@ ZONEMD 4294967296 0 0 00 ; Serial overflow
+@ ZONEMD 0 256 0 00 ; Type overflow
+@ ZONEMD 0 0 256 00 ; Reserved overflow
+@ ZONEMD 0 0 0 0 ; Hex block length must be multiple of 2
+@ ZONEMD 0 0 0 0x ; Bad hex char
+@ ZONEMD 0 0 0 ; Missing item
diff --git a/tests/libzscanner/data/56_ZONEMD.out b/tests/libzscanner/data/56_ZONEMD.out
new file mode 100644
index 0000000..0f56c24
--- /dev/null
+++ b/tests/libzscanner/data/56_ZONEMD.out
@@ -0,0 +1,54 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=FFFFFFFFFFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=000000000000010200FF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=003F
+RDATA=00000000000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER32_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_NUMBER8_OVERFLOW
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
diff --git a/tests/libzscanner/data/57_SVCB.in b/tests/libzscanner/data/57_SVCB.in
new file mode 100644
index 0000000..c4ce534
--- /dev/null
+++ b/tests/libzscanner/data/57_SVCB.in
@@ -0,0 +1,102 @@
+$ORIGIN .
+$TTL 1
+
+; OK
+@ SVCB 0 .
+@ SVCB 0 @ ; Comment
+@ SVCB 65535 . mandatory=alpn alpn=h2
+@ SVCB 1 . mandatory="alpn" alpn=h2
+@ SVCB 1 . alpn=h2
+@ SVCB 1 . alpn="abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\053" ; Text string of maximal length (255 chars)
+@ SVCB 1 . alpn="part1,part2,part3\\,part4\\\\"
+@ SVCB 1 . alpn=part1\,\p\a\r\t2\044part3\092,part4\092\\
+@ SVCB 1 . no-default-alpn
+@ SVCB 1 . port=0
+@ SVCB 1 . port="65535"
+@ SVCB 1 . ipv4hint=0.0.0.0
+@ SVCB 1 . ipv4hint="255.255.255.255"
+@ SVCB 1 . ech=AA==
+@ SVCB 1 . ech="Zm9vYg=="
+@ SVCB 1 . ipv6hint=::1
+@ SVCB 1 . ipv6hint="::1"
+@ SVCB 1 . key7
+@ SVCB 1 . key65535
+@ SVCB 1 . key65535=a
+@ SVCB 1 . key65535="a"
+@ SVCB 1 . key65535="a" key1000=b port=4 key7 mandatory=key7,port
+@ TYPE64 \# 3 000000
+@ TYPE64 0 .
+@ svcb 0 .
+
+; RFC OK examples
+example.com. HTTPS 0 foo.example.com.
+example.com. SVCB 1 .
+example.com. SVCB 16 foo.example.com. port=53
+example.com. SVCB 1 foo.example.com. key667=hello
+example.com. SVCB 1 foo.example.com. key667="hello\210qoo"
+example.com. SVCB 1 foo.example.com. (
+ ipv6hint="2001:db8::1,2001:db8::53:1"
+ )
+example.com. SVCB 1 example.com. ipv6hint="::ffff:198.51.100.100"
+example.com. SVCB 16 foo.example.org. (
+ alpn=h2,h3-19 mandatory=ipv4hint,alpn
+ ipv4hint=192.0.2.1
+ )
+example.com. SVCB 16 foo.example.org. alpn="f\\\\oo\\,bar,h2"
+example.com. SVCB 16 foo.example.org. alpn=f\\\092oo\092,bar,h2
+
+; KO
+@ SVCB
+@ SVCB ; Empty rdata
+@ SVCB \# 0 ; Hex empty rdata
+@ SVCB 65536 . ; Priority overflow
+@ SVCB 0 ; Missing item
+@ SVCB 1 . bogus ; Unknown parameter
+@ SVCB 1 . PORT=0 ; Capital letter in parameter name
+@ SVCB 1 . mandatory
+@ SVCB 1 . mandatory=
+@ SVCB 1 . mandatory=a,
+@ SVCB 1 . mandatory=a,,b
+@ SVCB 1 . mandatory=mandatory
+@ SVCB 1 . mandatory=bogus
+@ SVCB 1 . alpn
+@ SVCB 1 . alpn=
+@ SVCB 1 . alpn=a,
+@ SVCB 1 . alpn=a,,b
+@ SVCB 1 . alpn="abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNabcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMN1234\0536" ; Text string overflow (256 chars)
+@ SVCB 1 . no-default-alpn=
+@ SVCB 1 . no-default-alpn=h1
+@ SVCB 1 . no-default-alpn="h1"
+@ SVCB 1 . port
+@ SVCB 1 . port=
+@ SVCB 1 . port=65536
+@ SVCB 1 . port=1,2
+@ SVCB 1 . ipv4hint
+@ SVCB 1 . ipv4hint=
+@ SVCB 1 . ipv4hint=1.2.3
+@ SVCB 1 . ipv4hint=1.2.3.4,
+@ SVCB 1 . ipv4hint=1.2.3.4,,2.3.4.5
+@ SVCB 1 . ech
+@ SVCB 1 . ech=
+@ SVCB 1 . ech=AA==,AA==
+@ SVCB 1 . ech=W
+@ SVCB 1 . ipv6hint
+@ SVCB 1 . ipv6hint=
+@ SVCB 1 . ipv6hint=::1,,::2
+@ SVCB 1 . ipv6hint=::W
+
+; RFC KO examples
+example.com. SVCB 1 foo.example.com. (
+ key123=abc key123=def
+ )
+example.com. SVCB 1 foo.example.com. mandatory
+example.com. SVCB 1 foo.example.com. alpn
+example.com. SVCB 1 foo.example.com. port
+example.com. SVCB 1 foo.example.com. ipv4hint
+example.com. SVCB 1 foo.example.com. ipv6hint
+example.com. SVCB 1 foo.example.com. no-default-alpn=abc
+example.com. SVCB 1 foo.example.com. mandatory=key123
+example.com. SVCB 1 foo.example.com. mandatory=mandatory
+example.com. SVCB 1 foo.example.com. (
+ mandatory=key123,key123 key123=abc
+ )
diff --git a/tests/libzscanner/data/57_SVCB.out b/tests/libzscanner/data/57_SVCB.out
new file mode 100644
index 0000000..a799eb7
--- /dev/null
+++ b/tests/libzscanner/data/57_SVCB.out
@@ -0,0 +1,306 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=FFFF0000000002000100010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000000002000100010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000010003026832
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000010100FF6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E6162636465666768696A6B6C6D6E6F707172737475767778797A313233343536373839304142434445464748494A4B4C4D4E3132333435
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000100190570617274310570617274320C70617274332C70617274345C
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000100190570617274310570617274320C70617274332C70617274345C
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100000300020000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000030002FFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000004000400000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000040004FFFFFFFF
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000005000100
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000050004666F6F62
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000006001000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001000006001000000000000000000000000000000001
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000070000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF0000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=00010000000004000300070003000200040007000003E8000162FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000000
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000003666F6F076578616D706C6503636F6D00
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000100
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C6503636F6D00000300020035
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D00029B000568656C6C6F
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D00029B000968656C6C6FD2716F6F
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=000103666F6F076578616D706C6503636F6D000006002020010DB800000000000000000000000120010DB8000000000000000000530001
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=0001076578616D706C6503636F6D000006001000000000000000000000FFFFC6336464
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000000000400010004000100090268320568332D313900040004C0000201
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000001000C08665C6F6F2C626172026832
+------
+OWNER=076578616D706C6503636F6D00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0040
+RDATA=001003666F6F076578616D706C65036F7267000001000C08665C6F6F2C626172026832
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_EMPTY_LIST_ITEM
+------
+WARNG=ZS_EMPTY_LIST_ITEM
+------
+WARNG=ZS_ITEM_OVERFLOW
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_IPV4
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_BAD_ADDRESS_CHAR
+------
+WARNG=ZS_DUPLICATE_SVCB_KEY
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_BAD_SVCB_PARAM
+------
+WARNG=ZS_MISSING_SVCB_MANDATORY
+------
+WARNG=ZS_BAD_SVCB_MANDATORY
+------
+WARNG=ZS_DUPLICATE_SVCB_MANDATORY
+------
diff --git a/tests/libzscanner/data/58_HTTPS.in b/tests/libzscanner/data/58_HTTPS.in
new file mode 100644
index 0000000..674f865
--- /dev/null
+++ b/tests/libzscanner/data/58_HTTPS.in
@@ -0,0 +1,19 @@
+$ORIGIN .
+$TTL 1
+
+; HTTPS is the same as SVCB, so there are the differences and basics only.
+
+; OK
+@ HTTPS 0 .
+@ HTTPS 65535 @ ; Comment
+@ HTTPS 1 . key65535="a" key1000=b port=4 key7 mandatory=key7,port
+@ TYPE65 \# 3 000000
+@ TYPE65 0 .
+@ https 0 .
+
+; KO
+@ HTTPS
+@ HTTPS ; Empty rdata
+@ HTTPS \# 0 ; Hex empty rdata
+@ HTTPS 65536 . ; Priority overflow
+@ HTTPS 0 ; Missing item
diff --git a/tests/libzscanner/data/58_HTTPS.out b/tests/libzscanner/data/58_HTTPS.out
new file mode 100644
index 0000000..8ec9a35
--- /dev/null
+++ b/tests/libzscanner/data/58_HTTPS.out
@@ -0,0 +1,46 @@
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=FFFF00
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=00010000000004000300070003000200040007000003E8000162FFFF000161
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+OWNER=00
+CLASS=0001
+RRTTL=00000001
+RTYPE=0041
+RDATA=000000
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_NUMBER
+------
+WARNG=ZS_BAD_HEX_CHAR
+------
+WARNG=ZS_NUMBER16_OVERFLOW
+------
+WARNG=ZS_BAD_RDATA
+------
diff --git a/tests/libzscanner/data/includes/include1 b/tests/libzscanner/data/includes/include1
new file mode 100644
index 0000000..9de1859
--- /dev/null
+++ b/tests/libzscanner/data/includes/include1
@@ -0,0 +1,9 @@
+$TTL 1
+
+a NS @
+
+$ORIGIN tld1a.
+a NS @
+
+$ORIGIN tld1b.
+a NS @
diff --git a/tests/libzscanner/data/includes/include2 b/tests/libzscanner/data/includes/include2
new file mode 100644
index 0000000..1e14e96
--- /dev/null
+++ b/tests/libzscanner/data/includes/include2
@@ -0,0 +1,6 @@
+$TTL 1H
+
+b NS @
+
+$ORIGIN tld1a.
+b NS @
diff --git a/tests/libzscanner/data/includes/include3 b/tests/libzscanner/data/includes/include3
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/libzscanner/data/includes/include3
diff --git a/tests/libzscanner/data/includes/include4 b/tests/libzscanner/data/includes/include4
new file mode 100644
index 0000000..7e8d5e2
--- /dev/null
+++ b/tests/libzscanner/data/includes/include4
@@ -0,0 +1 @@
+a NS ; Missing data
diff --git a/tests/libzscanner/data/includes/include5 b/tests/libzscanner/data/includes/include5
new file mode 100644
index 0000000..ac98e01
--- /dev/null
+++ b/tests/libzscanner/data/includes/include5
@@ -0,0 +1 @@
+$TTL x ; Bad number
diff --git a/tests/libzscanner/data/includes/include6 b/tests/libzscanner/data/includes/include6
new file mode 100644
index 0000000..b5e8cb8
--- /dev/null
+++ b/tests/libzscanner/data/includes/include6
@@ -0,0 +1 @@
+$INCLUDE include2 ; Include in include
diff --git a/tests/libzscanner/processing.c b/tests/libzscanner/processing.c
new file mode 100644
index 0000000..22066dd
--- /dev/null
+++ b/tests/libzscanner/processing.c
@@ -0,0 +1,184 @@
+/* Copyright (C) 2021 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "libzscanner/scanner.h"
+#include "libzscanner/functions.c"
+#include "libzscanner/processing.h"
+#include "libknot/descriptor.c"
+
+const char *separator = "------\n";
+
+static void print_wire_dname(const uint8_t *dname, uint32_t dname_length)
+{
+ uint32_t label_length = 0, i = 0;
+
+ for (i = 0; i < dname_length; i++) {
+ if (label_length == 0) {
+ label_length = dname[i];
+ printf("(%u)", label_length);
+ continue;
+ }
+ printf("%c", (char)dname[i]);
+ label_length--;
+ }
+}
+
+void debug_process_error(zs_scanner_t *s)
+{
+ if (s->error.fatal) {
+ printf("LINE(%03"PRIu64") ERROR(%s) FILE(%s) NEAR(%s)\n",
+ s->line_counter,
+ zs_strerror(s->error.code),
+ s->file.name,
+ s->buffer);
+ } else {
+ printf("LINE(%03"PRIu64") WARNING(%s) FILE(%s) NEAR(%s)\n",
+ s->line_counter,
+ zs_strerror(s->error.code),
+ s->file.name,
+ s->buffer);
+ }
+ fflush(stdout);
+}
+
+void debug_process_record(zs_scanner_t *s)
+{
+ uint32_t i;
+
+ char rclass[32];
+ char rtype[32];
+
+ if (knot_rrclass_to_string(s->r_class, rclass, sizeof(rclass)) > 0 &&
+ knot_rrtype_to_string(s->r_type, rtype, sizeof(rtype)) > 0) {
+ printf("LINE(%03"PRIu64") %s %6u %*s ",
+ s->line_counter, rclass, s->r_ttl, 5, rtype);
+ } else {
+ printf("LINE(%03"PRIu64") %u %6u %*u ",
+ s->line_counter, s->r_class, s->r_ttl, 5, s->r_type);
+ }
+
+ print_wire_dname(s->r_owner, s->r_owner_length);
+
+ printf(" \\# %u ", s->r_data_length);
+
+ int block = *((int *)(s->process.data));
+ for (i = 0; i < s->r_data_length; i++) {
+ if (block > 0 && i > 0 && (i % block) == 0) {
+ printf(" ");
+ }
+ printf("%02X", (s->r_data)[i]);
+ }
+ printf("\n");
+ fflush(stdout);
+}
+
+void debug_process_comment(zs_scanner_t *s)
+{
+ printf("LINE(%03"PRIu64") COMMENT(%.*s)\n", s->line_counter,
+ (int)s->buffer_length, s->buffer);
+ fflush(stdout);
+}
+
+void test_process_error(zs_scanner_t *s)
+{
+ if (s->error.fatal) {
+ printf("ERROR=%s\n%s", zs_errorname(s->error.code), separator);
+ } else {
+ printf("WARNG=%s\n%s", zs_errorname(s->error.code), separator);
+ }
+ fflush(stdout);
+}
+
+void test_process_record(zs_scanner_t *s)
+{
+ uint32_t i;
+
+ printf("OWNER=");
+ for (i = 0; i < s->r_owner_length; i++) {
+ printf("%02X", s->r_owner[i]);
+ }
+ printf("\n");
+ printf("CLASS=%04X\n", s->r_class);
+ printf("RRTTL=%08X\n", s->r_ttl);
+ printf("RTYPE=%04X\n", s->r_type);
+ printf("RDATA=");
+ for (i = 0; i < s->r_data_length; i++) {
+ printf("%02X", (s->r_data)[i]);
+ }
+ printf("\n%s", separator);
+ fflush(stdout);
+}
+
+int test_date_to_timestamp(void)
+{
+ time_t ref_timestamp, max_timestamp;
+ uint32_t test_timestamp;
+ uint8_t buffer[16];
+ uint64_t val1, val2; // For time_t type unification.
+ struct tm tm;
+
+ // Set UTC for strftime.
+ putenv("TZ=UTC");
+ tzset();
+
+ // Get maximal allowed timestamp.
+ strptime("22251231235959", "%Y%m%d%H%M%S", &tm);
+ max_timestamp = mktime(&tm);
+
+ // Testing loop over whole input interval.
+ for (ref_timestamp = 0;
+ ref_timestamp < max_timestamp;
+ ref_timestamp += 1) {
+ struct tm result;
+ // Get reference (correct) timestamp.
+ strftime((char*)buffer, sizeof(buffer), "%Y%m%d%H%M%S",
+ gmtime_r(&ref_timestamp, &result));
+
+ // Get testing timestamp.
+ test_timestamp = 0U; // prevents Wuninitialized
+ date_to_timestamp(buffer, &test_timestamp);
+
+ // Some continuous logging.
+ if (ref_timestamp % 10000000 == 0) {
+ val1 = ref_timestamp;
+ printf("%s = %"PRIu32"\n", buffer, (uint32_t)val1);
+ }
+
+ // Comparing results.
+ if ((uint32_t)ref_timestamp != test_timestamp) {
+ val1 = ref_timestamp;
+
+ if (ref_timestamp > test_timestamp) {
+ val2 = ref_timestamp - test_timestamp;
+ printf("%s = %"PRIu64", in - out = %"PRIu64"\n",
+ buffer, val1, val2);
+ } else {
+ val2 = test_timestamp - ref_timestamp;
+ printf("%s = %"PRIu64", out - in = %"PRIu64"\n",
+ buffer, val1, val2);
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/tests/libzscanner/processing.h b/tests/libzscanner/processing.h
new file mode 100644
index 0000000..47cf03b
--- /dev/null
+++ b/tests/libzscanner/processing.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2018 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/>.
+ */
+
+#pragma once
+
+#include "libzscanner/scanner.h"
+
+void debug_process_error(zs_scanner_t *scanner);
+
+void debug_process_record(zs_scanner_t *scanner);
+
+void debug_process_comment(zs_scanner_t *scanner);
+
+void test_process_error(zs_scanner_t *scanner);
+
+void test_process_record(zs_scanner_t *scanner);
+
+int test_date_to_timestamp(void);
diff --git a/tests/libzscanner/test_zscanner.in b/tests/libzscanner/test_zscanner.in
new file mode 100644
index 0000000..10d2b5c
--- /dev/null
+++ b/tests/libzscanner/test_zscanner.in
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+SOURCE=@top_srcdir@/tests/libzscanner
+BUILD=@top_builddir@/tests/libzscanner
+
+. @top_srcdir@/tests/tap/libtap.sh
+
+cd "$BUILD"
+
+TMPDIR=$(test_tmpdir)
+TESTS_DIR="$SOURCE"/data
+ZSCANNER_TOOL="$BUILD"/zscanner-tool
+
+plan 86
+
+mkdir -p "$TMPDIR"/includes/
+for a in 1 2 3 4 5 6; do
+ cat "$TESTS_DIR"/includes/include"$a" > "$TMPDIR"/includes/include"$a";
+done
+
+for case in $(cat "$SOURCE"/TESTS); do
+ casein=$(test_file_path data/"$case".in)
+ caseout=$(test_file_path data/"$case".out)
+ filein="$TMPDIR"/"$case".in
+ fileout="$TMPDIR"/"$case".out
+
+ sed -e "s|@TMPDIR@|$TMPDIR|;" < "$casein" > "$filein"
+
+ "$ZSCANNER_TOOL" -m 2 . "$filein" > "$fileout"
+
+ if cmp -s "$fileout" "$caseout"; then
+ ok "$case: output matches" true
+ rm "$filein"
+ rm "$fileout"
+ else
+ ok "$case: output differs" false
+ diff -urNap "$caseout" "$fileout" | while read line; do diag "$line"; done
+ fi
+done
+
+rm -rf "$TMPDIR"/includes/
diff --git a/tests/libzscanner/zscanner-tool.c b/tests/libzscanner/zscanner-tool.c
new file mode 100644
index 0000000..d5f514d
--- /dev/null
+++ b/tests/libzscanner/zscanner-tool.c
@@ -0,0 +1,257 @@
+/* Copyright (C) 2021 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 <getopt.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libzscanner/processing.h"
+#include "libzscanner/scanner.h"
+
+#define DEFAULT_MODE 1
+#define DEFAULT_CLASS 1
+#define DEFAULT_TTL 0
+
+static void *timestamp_worker(void *data)
+{
+ int *ret = (int *)data;
+ *ret = test_date_to_timestamp();
+ return NULL;
+}
+
+static void help(void)
+{
+ printf("\nZone scanner testing tool.\n"
+ "Usage: zscanner-tool [parameters] origin zonefile\n"
+ "\n"
+ "Parameters:\n"
+ " -m [0,1,2] Processing mode.\n"
+ " 0 Empty output.\n"
+ " 1 Debug output (DEFAULT).\n"
+ " 2 Test output.\n"
+ " -b <num> Divide hex string to blocks of length <num>.\n"
+ " -s State parsing mode.\n"
+ " -t Launch unit tests.\n"
+ " -h Print this help.\n");
+}
+
+static int time_test(void)
+{
+ pthread_t t1, t2, t3;
+ int ret1, ret2, ret3;
+
+ pthread_create(&t1, NULL, timestamp_worker, &ret1);
+ pthread_create(&t2, NULL, timestamp_worker, &ret2);
+ pthread_create(&t3, NULL, timestamp_worker, &ret3);
+
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+ pthread_join(t3, NULL);
+
+ if (ret1 != 0 || ret2 != 0 || ret3 != 0) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int include(zs_scanner_t *s);
+
+static int state_parsing(zs_scanner_t *s)
+{
+ while (zs_parse_record(s) == 0) {
+ switch (s->state) {
+ case ZS_STATE_DATA:
+ if (s->process.record != NULL) {
+ s->process.record(s);
+ }
+ break;
+ case ZS_STATE_ERROR:
+ if (s->process.error != NULL) {
+ s->process.error(s);
+ }
+ if (s->error.fatal) {
+ return -1;
+ }
+ break;
+ case ZS_STATE_INCLUDE:
+ if (include(s) != 0) {
+ return -1;
+ }
+ break;
+ default:
+ return (s->error.counter == 0) ? 0 : -1;
+ }
+ }
+
+ return -1;
+}
+
+static int include(zs_scanner_t *s)
+{
+ zs_scanner_t *ss;
+ int ret = 0;
+
+ if ((ss = malloc(sizeof(zs_scanner_t))) == NULL ||
+ zs_init(ss, (char *)s->buffer, s->default_class, s->default_ttl) != 0 ||
+ zs_set_input_file(ss, (char *)(s->include_filename)) != 0 ||
+ zs_set_processing(ss, s->process.record, s->process.error, s->process.data) != 0 ||
+ state_parsing(ss) != 0) {
+ if (ss == NULL) {
+ s->error.code = ZS_ENOMEM;
+ } else if (ss->error.counter > 0) {
+ s->error.counter += ss->error.counter;
+ s->error.code = ZS_UNPROCESSED_INCLUDE;
+ } else {
+ s->error.code = ss->error.code;
+ }
+
+ if (s->process.error != NULL) {
+ s->buffer[0] = '\0'; // Clear unrelated content.
+ s->buffer_length = 0;
+ s->error.counter++;
+ s->error.fatal = true;
+ s->process.error(s);
+ }
+
+ ret = -1;
+ }
+
+ zs_deinit(ss);
+ free(ss);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int mode = DEFAULT_MODE, block = 0, state = 0, test = 0;
+
+ // Command line long options.
+ struct option opts[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "block", required_argument, NULL, 'b' },
+ { "state", no_argument, NULL, 's' },
+ { "test", no_argument, NULL, 't' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL }
+ };
+
+ // Parsed command line arguments.
+ int opt = 0, li = 0;
+ while ((opt = getopt_long(argc, argv, "m:b:sth", opts, &li)) != -1) {
+ switch (opt) {
+ case 'm':
+ mode = atoi(optarg);
+ break;
+ case 'b':
+ block = atoi(optarg);
+ break;
+ case 's':
+ state = 1;
+ break;
+ case 't':
+ test = 1;
+ break;
+ case 'h':
+ help();
+ return EXIT_SUCCESS;
+ default:
+ help();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (test == 1) {
+ return time_test();
+ }
+
+ // Check if there are 2 remaining non-options.
+ if (argc - optind != 2) {
+ help();
+ return EXIT_FAILURE;
+ }
+
+ const char *origin = argv[optind];
+ const char *zone_file = argv[optind + 1];
+
+ // Create a zone scanner.
+ zs_scanner_t *s = malloc(sizeof(zs_scanner_t));
+ if (s == NULL) {
+ printf("Scanner create error!\n");
+ return EXIT_FAILURE;
+ }
+ if (zs_init(s, origin, DEFAULT_CLASS, DEFAULT_TTL) != 0) {
+ printf("Scanner init error!\n");
+ free(s);
+ return EXIT_FAILURE;
+ }
+ if (zs_set_input_file(s, zone_file) != 0) {
+ printf("Scanner file error!\n");
+ zs_deinit(s);
+ free(s);
+ return EXIT_FAILURE;
+ }
+
+ // Set the processing mode.
+ int ret;
+ switch (mode) {
+ case 0:
+ ret = 0;
+ break;
+ case 1:
+ ret = zs_set_processing(s, debug_process_record, debug_process_error, &block);
+ ret += zs_set_processing_comment(s, debug_process_comment);
+ break;
+ case 2:
+ ret = zs_set_processing(s, test_process_record, test_process_error, NULL);
+ break;
+ default:
+ printf("Bad mode number!\n");
+ help();
+ return EXIT_FAILURE;
+ }
+ if (ret != 0) {
+ printf("Processing setup error!\n");
+ return EXIT_FAILURE;
+ }
+
+ // Parse the file.
+ ret = state ? state_parsing(s) : zs_parse_all(s);
+ if (ret == 0) {
+ if (mode == DEFAULT_MODE) {
+ printf("Zone file has been processed successfully\n");
+ }
+
+ zs_deinit(s);
+ free(s);
+ return EXIT_SUCCESS;
+ } else {
+ if (s->error.counter > 0 && mode == DEFAULT_MODE) {
+ printf("Zone processing has stopped with "
+ "%"PRIu64" warnings/errors!\n",
+ s->error.counter);
+ } else if (mode == DEFAULT_MODE) {
+ printf("%s\n", zs_strerror(s->error.code));
+ }
+
+ zs_deinit(s);
+ free(s);
+ return EXIT_FAILURE;
+ }
+}
diff --git a/tests/modules/test_onlinesign.c b/tests/modules/test_onlinesign.c
new file mode 100644
index 0000000..fbddb6e
--- /dev/null
+++ b/tests/modules/test_onlinesign.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2018 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 <tap/basic.h>
+#include <assert.h>
+
+#include "knot/modules/onlinesign/nsec_next.h"
+#include "libknot/consts.h"
+#include "libknot/dname.h"
+#include "libknot/errcode.h"
+
+/*!
+ * \brief Assert that a domain name in a static buffer is valid.
+ */
+#define _assert_dname(name) \
+ assert(knot_dname_wire_check(name, name + KNOT_DNAME_MAXLEN, NULL) > 0)
+
+static void _test_nsec_next(const char *msg,
+ const knot_dname_t *input,
+ const knot_dname_t *apex,
+ const knot_dname_t *expected)
+{
+ knot_dname_t *next = online_nsec_next(input, apex);
+ ok(next != NULL && knot_dname_is_equal(next, expected),
+ "nsec_next, %s", msg);
+ knot_dname_free(next, NULL);
+}
+
+/*!
+ * \brief Check \a online_nsec_next.
+ *
+ * Intentionally implemented as a macro. The input domain names are copied
+ * into static buffers and validated.
+ */
+#define test_nsec_next(msg, _input, _apex, _expected) \
+{ \
+ uint8_t input[KNOT_DNAME_MAXLEN] = _input; \
+ uint8_t apex[KNOT_DNAME_MAXLEN] = _apex; \
+ uint8_t expected[KNOT_DNAME_MAXLEN] = _expected; \
+ \
+ _assert_dname(input); \
+ _assert_dname(apex); \
+ _assert_dname(expected); \
+ \
+ _test_nsec_next(msg, input, apex, expected); \
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ // adding a single zero-byte label
+
+ test_nsec_next(
+ "zero-byte label, apex",
+ "\x7""example""\x3""com",
+ "\x7""example""\x3""com",
+ "\x01\x00""\x07""example""\x03""com"
+ );
+
+ test_nsec_next(
+ "zero-byte label, subdomain",
+ "\x02""nx""\x7""example""\x3""com",
+ "\x7""example""\x3""com",
+ "\x01\x00""\x02""nx""\x07""example""\x03""com"
+ );
+
+ test_nsec_next(
+ "zero-byte label, binary",
+ "\x02\xff\xff""\x7""example""\x3""com",
+ "\x07""example""\x3""com",
+ "\x01\x00""\x02\xff\xff""\x7""example""\x3""com"
+ );
+
+ // zero byte label won't fit, increment
+ #define APEX \
+ "\x05""bacon""\x05""salad"
+
+ #define LONG_SUFFIX \
+ "\x2e""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
+ "\x2e""iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" \
+ "\x2e""mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" \
+ "\x2e""qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq" \
+ "\x2c""zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" \
+ APEX
+ assert(sizeof(LONG_SUFFIX) == 245 + 1);
+
+ test_nsec_next(
+ "increment first label (simple)",
+ "\x08""icecream" LONG_SUFFIX,
+ APEX,
+ "\x08""icecrean" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (binary)",
+ "\x08""walrus\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x08""walrut\x00\x00" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (in place)",
+ "\x07""lobster" LONG_SUFFIX,
+ APEX,
+ "\x07""lobstes" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "increment first label (extend)",
+ "\x07""\xff\xff\xff\xff\xff\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" LONG_SUFFIX
+ );
+
+ // name too long
+
+ test_nsec_next(
+ "name to long, strip label and increase next (simple)",
+ "\x03""\xff\xff\xff""\x04""newt" LONG_SUFFIX,
+ APEX,
+ "\x04""newu" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "name to long, strip label and increase next (binary)",
+ "\x03""\xff\xff\xff""\x04""cc\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x04""cd\x00\x00" LONG_SUFFIX
+ );
+
+ test_nsec_next(
+ "name to long, strip label and increase next (extend)",
+ "\x04""\xff\xff\xff\xff""\x03""\xff\xff\xff" LONG_SUFFIX,
+ APEX,
+ "\x04""\xff\xff\xff\x00" LONG_SUFFIX
+ );
+
+ // label too long
+
+ #define MAX_LABEL "\x3f" /* 63 */ \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" \
+ "\xff\xff\xff"
+ assert(sizeof(MAX_LABEL) == 64 + 1);
+
+ #define PAD_LABEL "\x28" /* 40 */ \
+ "iiiiiiiiiioooooooooottttttttttssssssssss"
+ assert(sizeof(PAD_LABEL) == 41 + 1);
+
+ test_nsec_next(
+ "label too long, strip and increase next (simple)",
+ MAX_LABEL "\x08""mandrill" MAX_LABEL MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""mandrilm" MAX_LABEL MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, strip and increase next (extend)",
+ MAX_LABEL "\x07""\xff\xff\xff\xff\xff\xff\xff" MAX_LABEL MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""\xff\xff\xff\xff\xff\xff\xff\x00" MAX_LABEL MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, strip multiple",
+ MAX_LABEL MAX_LABEL "\x08""flamingo" MAX_LABEL PAD_LABEL APEX,
+ APEX,
+ "\x08""flamingp" MAX_LABEL PAD_LABEL APEX
+ );
+
+ test_nsec_next(
+ "label too long, wrap around to apex",
+ "\x31" /* 49 */
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ "\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+ MAX_LABEL MAX_LABEL MAX_LABEL APEX,
+ APEX,
+ APEX
+ );
+
+ return 0;
+}
diff --git a/tests/modules/test_rrl.c b/tests/modules/test_rrl.c
new file mode 100644
index 0000000..6a5210f
--- /dev/null
+++ b/tests/modules/test_rrl.c
@@ -0,0 +1,178 @@
+/* Copyright (C) 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <tap/basic.h>
+
+#include "libdnssec/crypto.h"
+#include "libdnssec/random.h"
+#include "libknot/libknot.h"
+#include "contrib/sockaddr.h"
+#include "knot/modules/rrl/functions.c"
+#include "stdio.h"
+
+/* Enable time-dependent tests. */
+//#define ENABLE_TIMED_TESTS
+#define RRL_SIZE 196613
+#define RRL_THREADS 8
+#define RRL_INSERTS (RRL_SIZE/(5*RRL_THREADS)) /* lf = 1/5 */
+
+/* Disabled as default as it depends on random input.
+ * Table may be consistent even if some collision occur (and they may occur).
+ * Note: Disabled due to reported problems when running on VMs due to time
+ * flow inconsistencies. Should work alright on a host machine.
+ */
+#ifdef ENABLE_TIMED_TESTS
+struct bucketmap {
+ unsigned i;
+ uint64_t x;
+};
+
+/*! \brief Unit runnable. */
+struct runnable_data {
+ int passed;
+ rrl_table_t *rrl;
+ struct sockaddr_storage *addr;
+ rrl_req_t *rq;
+ knot_dname_t *zone;
+};
+
+static void* rrl_runnable(void *arg)
+{
+ struct runnable_data *d = (struct runnable_data *)arg;
+ struct sockaddr_storage addr;
+ memcpy(&addr, d->addr, sizeof(struct sockaddr_storage));
+ int lock = -1;
+ uint32_t now = time(NULL);
+ struct bucketmap *m = malloc(RRL_INSERTS * sizeof(struct bucketmap));
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ m[i].i = dnssec_random_uint32_t();
+ ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ m[i].x = b->netblk;
+ }
+ for (unsigned i = 0; i < RRL_INSERTS; ++i) {
+ ((struct sockaddr_in *) &addr)->sin_addr.s_addr = m[i].i;
+ rrl_item_t *b = rrl_hash(d->rrl, &addr, d->rq, d->zone, now, &lock);
+ rrl_unlock(d->rrl, lock);
+ if (b->netblk != m[i].x) {
+ d->passed = 0;
+ }
+ }
+ free(m);
+ return NULL;
+}
+
+static void rrl_hopscotch(struct runnable_data* rd)
+{
+ rd->passed = 1;
+ pthread_t thr[RRL_THREADS];
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_create(thr + i, NULL, &rrl_runnable, rd);
+ }
+ for (unsigned i = 0; i < RRL_THREADS; ++i) {
+ pthread_join(thr[i], NULL);
+ }
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ dnssec_crypto_init();
+
+ /* Prepare query. */
+ knot_pkt_t *query = knot_pkt_new(NULL, 512, NULL);
+ if (query == NULL) {
+ return KNOT_ERROR; /* Fatal */
+ }
+
+ knot_dname_t *qname = knot_dname_from_str_alloc("beef.");
+ int ret = knot_pkt_put_question(query, qname, KNOT_CLASS_IN, KNOT_RRTYPE_A);
+ knot_dname_free(qname, NULL);
+ if (ret != KNOT_EOK) {
+ knot_pkt_free(query);
+ return KNOT_ERROR; /* Fatal */
+ }
+
+ /* Prepare response */
+ uint8_t rbuf[65535];
+ size_t rlen = sizeof(rbuf);
+ memcpy(rbuf, query->wire, query->size);
+ knot_wire_flags_set_qr(rbuf);
+
+ rrl_req_t rq;
+ rq.wire = rbuf;
+ rq.len = rlen;
+ rq.query = query;
+ rq.flags = 0;
+
+ /* 1. create rrl table */
+ const uint32_t rate = 10;
+ rrl_table_t *rrl = rrl_create(RRL_SIZE, rate);
+ ok(rrl != NULL, "rrl: create");
+
+ /* 2. N unlimited requests. */
+ knot_dname_t *zone = knot_dname_from_str_alloc("rrl.");
+
+ struct sockaddr_storage addr;
+ struct sockaddr_storage addr6;
+ sockaddr_set(&addr, AF_INET, "1.2.3.4", 0);
+ sockaddr_set(&addr6, AF_INET6, "1122:3344:5566:7788::aabb", 0);
+ ret = 0;
+ for (unsigned i = 0; i < rate * RRL_CAPACITY; ++i) {
+ if (rrl_query(rrl, &addr, &rq, zone, NULL) != KNOT_EOK ||
+ rrl_query(rrl, &addr6, &rq, zone, NULL) != KNOT_EOK) {
+ ret = KNOT_ELIMIT;
+ break;
+ }
+ }
+ is_int(0, ret, "rrl: unlimited IPv4/v6 requests");
+
+ /* 3. Endian-independent hash input buffer. */
+ uint8_t buf[RRL_CLSBLK_MAXLEN];
+ // CLS_LARGE + remote + dname wire.
+ uint8_t expectedv4[] = "\x10\x01\x02\x03\x00\x00\x00\x00\x00\x04""beef";
+ rrl_classify(buf, sizeof(buf), &addr, &rq, qname);
+ is_int(0, memcmp(buf, expectedv4, sizeof(expectedv4)), "rrl: IPv4 hash input buffer");
+ uint8_t expectedv6[] = "\x10\x11\x22\x33\x44\x55\x66\x77\x00\x04""beef";
+ rrl_classify(buf, sizeof(buf), &addr6, &rq, qname);
+ is_int(0, memcmp(buf, expectedv6, sizeof(expectedv6)), "rrl: IPv6 hash input buffer");
+
+#ifdef ENABLE_TIMED_TESTS
+ /* 5. limited request */
+ ret = rrl_query(rrl, &addr, &rq, zone, NULL);
+ is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv4 request");
+
+ /* 6. limited IPv6 request */
+ ret = rrl_query(rrl, &addr6, &rq, zone, NULL);
+ is_int(KNOT_ELIMIT, ret, "rrl: throttled IPv6 request");
+
+ /* 8. hopscotch test */
+ struct runnable_data rd = {
+ 1, rrl, &addr, &rq, zone
+ };
+ rrl_hopscotch(&rd);
+ ok(rd.passed, "rrl: hashtable is ~ consistent");
+#endif
+
+ knot_dname_free(zone, NULL);
+ knot_pkt_free(query);
+ rrl_destroy(rrl);
+ dnssec_crypto_cleanup();
+ return 0;
+}
diff --git a/tests/tap/basic.c b/tests/tap/basic.c
new file mode 100644
index 0000000..23b595c
--- /dev/null
+++ b/tests/tap/basic.c
@@ -0,0 +1,605 @@
+/*
+ * Some utility routines for writing tests.
+ *
+ * Here are a variety of utility routines for writing tests compatible with
+ * the TAP protocol. All routines of the form ok() or is*() take a test
+ * number and some number of appropriate arguments, check to be sure the
+ * results match the expected output using the arguments, and print out
+ * something appropriate for that test number. Other utility routines help in
+ * constructing more complex tests, skipping tests, reporting errors, setting
+ * up the TAP output format, or finding things in the test environment.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+# include <direct.h>
+#else
+# include <sys/stat.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "basic.h"
+
+/* Windows provides mkdir and rmdir under different names. */
+#ifdef _WIN32
+# define mkdir(p, m) _mkdir(p)
+# define rmdir(p) _rmdir(p)
+#endif
+
+/*
+ * The test count. Always contains the number that will be used for the next
+ * test status.
+ */
+unsigned long testnum = 1;
+
+/*
+ * Status information stored so that we can give a test summary at the end of
+ * the test case. We store the planned final test and the count of failures.
+ * We can get the highest test count from testnum.
+ *
+ * We also store the PID of the process that called plan() and only summarize
+ * results when that process exits, so as to not misreport results in forked
+ * processes.
+ *
+ * If _lazy is true, we're doing lazy planning and will print out the plan
+ * based on the last test number at the end of testing.
+ */
+static unsigned long _planned = 0;
+static unsigned long _failed = 0;
+static pid_t _process = 0;
+static int _lazy = 0;
+
+/*
+ * Our exit handler. Called on completion of the test to report a summary of
+ * results provided we're still in the original process. This also handles
+ * printing out the plan if we used plan_lazy(), although that's suppressed if
+ * we never ran a test (due to an early bail, for example).
+ */
+static void
+finish(void)
+{
+ unsigned long highest = testnum - 1;
+
+ if (_planned == 0 && !_lazy)
+ return;
+ fflush(stderr);
+ if (_process != 0 && getpid() == _process) {
+ if (_lazy && highest > 0) {
+ printf("1..%lu\n", highest);
+ _planned = highest;
+ }
+ if (_planned > highest)
+ printf("# Looks like you planned %lu test%s but only ran %lu\n",
+ _planned, (_planned > 1 ? "s" : ""), highest);
+ else if (_planned < highest)
+ printf("# Looks like you planned %lu test%s but ran %lu extra\n",
+ _planned, (_planned > 1 ? "s" : ""), highest - _planned);
+ else if (_failed > 0)
+ printf("# Looks like you failed %lu test%s of %lu\n", _failed,
+ (_failed > 1 ? "s" : ""), _planned);
+ else if (_planned > 1)
+ printf("# All %lu tests successful or skipped\n", _planned);
+ else
+ printf("# %lu test successful or skipped\n", _planned);
+ }
+}
+
+/*
+ * Initialize things. Turns on line buffering on stdout and then prints out
+ * the number of tests in the test suite.
+ */
+void
+plan(unsigned long count)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
+ strerror(errno));
+ fflush(stderr);
+ printf("1..%lu\n", count);
+ testnum = 1;
+ _planned = count;
+ _process = getpid();
+ atexit(finish);
+}
+
+/*
+ * Initialize things for lazy planning, where we'll automatically print out a
+ * plan at the end of the program. Turns on line buffering on stdout as well.
+ */
+void
+plan_lazy(void)
+{
+ if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0)
+ fprintf(stderr, "# cannot set stdout to line buffered: %s\n",
+ strerror(errno));
+ testnum = 1;
+ _process = getpid();
+ _lazy = 1;
+ atexit(finish);
+}
+
+/*
+ * Skip the entire test suite and exits. Should be called instead of plan(),
+ * not after it, since it prints out a special plan line.
+ */
+void
+skip_all(const char *format, ...)
+{
+ fflush(stderr);
+ printf("1..0 # skip");
+ if (format != NULL) {
+ va_list args;
+
+ putchar(' ');
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+ exit(0);
+}
+
+/*
+ * Print the test description.
+ */
+static void
+print_desc(const char *format, va_list args)
+{
+ printf(" - ");
+ vprintf(format, args);
+}
+
+/*
+ * Takes a boolean success value and assumes the test passes if that value
+ * is true and fails if that value is false.
+ */
+void
+ok(int success, const char *format, ...)
+{
+ fflush(stderr);
+ printf("%sok %lu", success ? "" : "not ", testnum++);
+ if (!success)
+ _failed++;
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Same as ok(), but takes the format arguments as a va_list.
+ */
+void
+okv(int success, const char *format, va_list args)
+{
+ fflush(stderr);
+ printf("%sok %lu", success ? "" : "not ", testnum++);
+ if (!success)
+ _failed++;
+ if (format != NULL)
+ print_desc(format, args);
+ putchar('\n');
+}
+
+/*
+ * Skip a test.
+ */
+void
+skip(const char *reason, ...)
+{
+ fflush(stderr);
+ printf("ok %lu # skip", testnum++);
+ if (reason != NULL) {
+ va_list args;
+
+ va_start(args, reason);
+ putchar(' ');
+ vprintf(reason, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Report the same status on the next count tests.
+ */
+void
+ok_block(unsigned long count, int status, const char *format, ...)
+{
+ unsigned long i;
+
+ fflush(stderr);
+ for (i = 0; i < count; i++) {
+ printf("%sok %lu", status ? "" : "not ", testnum++);
+ if (!status)
+ _failed++;
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+ }
+}
+
+/*
+ * Skip the next count tests.
+ */
+void
+skip_block(unsigned long count, const char *reason, ...)
+{
+ unsigned long i;
+
+ fflush(stderr);
+ for (i = 0; i < count; i++) {
+ printf("ok %lu # skip", testnum++);
+ if (reason != NULL) {
+ va_list args;
+
+ va_start(args, reason);
+ putchar(' ');
+ vprintf(reason, args);
+ va_end(args);
+ }
+ putchar('\n');
+ }
+}
+
+/*
+ * Takes an expected integer and a seen integer and assumes the test passes
+ * if those two numbers match.
+ */
+void
+is_int(long long wanted, long long seen, const char *format, ...)
+{
+ fflush(stderr);
+ if (wanted == seen)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %lld\n# seen: %lld\n", wanted, seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Takes a string and what the string should be, and assumes the test passes
+ * if those strings match (using strcmp).
+ */
+void
+is_string(const char *wanted, const char *seen, const char *format, ...)
+{
+ if (wanted == NULL)
+ wanted = "(null)";
+ if (seen == NULL)
+ seen = "(null)";
+ fflush(stderr);
+ if (strcmp(wanted, seen) == 0)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %s\n# seen: %s\n", wanted, seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Takes an expected unsigned long and a seen unsigned long and assumes the
+ * test passes if the two numbers match. Otherwise, reports them in hex.
+ */
+void
+is_hex(unsigned long long wanted, unsigned long long seen,
+ const char *format, ...)
+{
+ fflush(stderr);
+ if (wanted == seen)
+ printf("ok %lu", testnum++);
+ else {
+ printf("# wanted: %llx\n# seen: %llx\n",
+ (unsigned long long) wanted,
+ (unsigned long long) seen);
+ printf("not ok %lu", testnum++);
+ _failed++;
+ }
+ if (format != NULL) {
+ va_list args;
+
+ va_start(args, format);
+ print_desc(format, args);
+ va_end(args);
+ }
+ putchar('\n');
+}
+
+/*
+ * Bail out with an error.
+ */
+void
+bail(const char *format, ...)
+{
+ va_list args;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("Bail out! ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+ exit(255);
+}
+
+/*
+ * Bail out with an error, appending strerror(errno).
+ */
+void
+sysbail(const char *format, ...)
+{
+ va_list args;
+ int oerrno = errno;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("Bail out! ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf(": %s\n", strerror(oerrno));
+ exit(255);
+}
+
+/*
+ * Report a diagnostic to stderr.
+ */
+void
+diag(const char *format, ...)
+{
+ va_list args;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf("\n");
+}
+
+/*
+ * Report a diagnostic to stderr, appending strerror(errno).
+ */
+void
+sysdiag(const char *format, ...)
+{
+ va_list args;
+ int oerrno = errno;
+
+ fflush(stderr);
+ fflush(stdout);
+ printf("# ");
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+ printf(": %s\n", strerror(oerrno));
+}
+
+/*
+ * Allocate cleared memory, reporting a fatal error with bail on failure.
+ */
+void *
+bcalloc(size_t n, size_t size)
+{
+ void *p;
+
+ p = calloc(n, size);
+ if (p == NULL)
+ sysbail("failed to calloc %lu", (unsigned long)(n * size));
+ return p;
+}
+
+/*
+ * Allocate memory, reporting a fatal error with bail on failure.
+ */
+void *
+bmalloc(size_t size)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL)
+ sysbail("failed to malloc %lu", (unsigned long) size);
+ return p;
+}
+
+/*
+ * Reallocate memory, reporting a fatal error with bail on failure.
+ */
+void *
+brealloc(void *p, size_t size)
+{
+ p = realloc(p, size);
+ if (p == NULL)
+ sysbail("failed to realloc %lu bytes", (unsigned long) size);
+ return p;
+}
+
+/*
+ * Copy a string, reporting a fatal error with bail on failure.
+ */
+char *
+bstrdup(const char *s)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ if (p == NULL)
+ sysbail("failed to strdup %lu bytes", (unsigned long) len);
+ memcpy(p, s, len);
+ return p;
+}
+
+/*
+ * Copy up to n characters of a string, reporting a fatal error with bail on
+ * failure. Don't use the system strndup function, since it may not exist and
+ * the TAP library doesn't assume any portability support.
+ */
+char *
+bstrndup(const char *s, size_t n)
+{
+ const char *p;
+ char *copy;
+ size_t length;
+
+ /* Don't assume that the source string is nul-terminated. */
+ for (p = s; (size_t) (p - s) < n && *p != '\0'; p++)
+ ;
+ length = p - s;
+ copy = malloc(length + 1);
+ if (p == NULL)
+ sysbail("failed to strndup %lu bytes", (unsigned long) length);
+ memcpy(copy, s, length);
+ copy[length] = '\0';
+ return copy;
+}
+
+/*
+ * Locate a test file. Given the partial path to a file, look under BUILD and
+ * then SOURCE for the file and return the full path to the file. Returns
+ * NULL if the file doesn't exist. A non-NULL return should be freed with
+ * test_file_path_free().
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_file_path(const char *file)
+{
+ char *base;
+ char *path = NULL;
+ size_t length;
+ const char *envs[] = { "BUILD", "SOURCE", NULL };
+ int i;
+
+ for (i = 0; envs[i] != NULL; i++) {
+ base = getenv(envs[i]);
+ if (base == NULL)
+ continue;
+ length = strlen(base) + 1 + strlen(file) + 1;
+ path = bmalloc(length);
+ snprintf(path, length, "%s/%s", base, file);
+ if (access(path, R_OK) == 0)
+ break;
+ free(path);
+ path = NULL;
+ }
+ return path;
+}
+
+/*
+ * Free a path returned from test_file_path(). This function exists primarily
+ * for Windows, where memory must be freed from the same library domain that
+ * it was allocated from.
+ */
+void
+test_file_path_free(char *path)
+{
+ if (path != NULL)
+ free(path);
+}
+
+/*
+ * Create a temporary directory, tmp, under BUILD if set and the current
+ * directory if it does not. Returns the path to the temporary directory in
+ * newly allocated memory, and calls bail on any failure. The return value
+ * should be freed with test_tmpdir_free.
+ *
+ * This function uses sprintf because it attempts to be independent of all
+ * other portability layers. The use immediately after a memory allocation
+ * should be safe without using snprintf or strlcpy/strlcat.
+ */
+char *
+test_tmpdir(void)
+{
+ const char *build;
+ char *path = NULL;
+ size_t length;
+
+ build = getenv("BUILD");
+ if (build == NULL)
+ build = ".";
+ length = strlen(build) + strlen("/tmp") + 1;
+ path = bmalloc(length);
+ snprintf(path, length, "%s/tmp", build);
+ if (access(path, X_OK) < 0)
+ if (mkdir(path, 0777) < 0)
+ sysbail("error creating temporary directory %s", path);
+ return path;
+}
+
+/*
+ * Free a path returned from test_tmpdir() and attempt to remove the
+ * directory. If we can't delete the directory, don't worry; something else
+ * that hasn't yet cleaned up may still be using it.
+ */
+void
+test_tmpdir_free(char *path)
+{
+ rmdir(path);
+ if (path != NULL)
+ free(path);
+}
diff --git a/tests/tap/basic.h b/tests/tap/basic.h
new file mode 100644
index 0000000..2e1b7e3
--- /dev/null
+++ b/tests/tap/basic.h
@@ -0,0 +1,132 @@
+/*
+ * Basic utility routines for the TAP protocol.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+ * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "macros.h"
+#include <stdarg.h> /* va_list */
+#include <sys/types.h> /* size_t */
+
+/*
+ * Used for iterating through arrays. ARRAY_SIZE returns the number of
+ * elements in the array (useful for a < upper bound in a for loop) and
+ * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it
+ * legal to refer to such a pointer as long as it's never dereferenced).
+ */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)])
+
+BEGIN_DECLS
+
+/*
+ * The test count. Always contains the number that will be used for the next
+ * test status.
+ */
+extern unsigned long testnum;
+
+/* Print out the number of tests and set standard output to line buffered. */
+void plan(unsigned long count);
+
+/*
+ * Prepare for lazy planning, in which the plan will be printed automatically
+ * at the end of the test program.
+ */
+void plan_lazy(void);
+
+/* Skip the entire test suite. Call instead of plan. */
+void skip_all(const char *format, ...)
+ __attribute__((__noreturn__, __format__(printf, 1, 2)));
+
+/*
+ * Basic reporting functions. The okv() function is the same as ok() but
+ * takes the test description as a va_list to make it easier to reuse the
+ * reporting infrastructure when writing new tests.
+ */
+void ok(int success, const char *format, ...)
+ __attribute__((__format__(printf, 2, 3)));
+void okv(int success, const char *format, va_list args);
+void skip(const char *reason, ...)
+ __attribute__((__format__(printf, 1, 2)));
+
+/* Report the same status on, or skip, the next count tests. */
+void ok_block(unsigned long count, int success, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void skip_block(unsigned long count, const char *reason, ...)
+ __attribute__((__format__(printf, 2, 3)));
+
+/* Check an expected value against a seen value. */
+void is_int(long long wanted, long long seen, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void is_string(const char *wanted, const char *seen, const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+void is_hex(unsigned long long wanted, unsigned long long seen,
+ const char *format, ...)
+ __attribute__((__format__(printf, 3, 4)));
+
+/* Bail out with an error. sysbail appends strerror(errno). */
+void bail(const char *format, ...)
+ __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
+void sysbail(const char *format, ...)
+ __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2)));
+
+/* Report a diagnostic to stderr prefixed with #. */
+void diag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+void sysdiag(const char *format, ...)
+ __attribute__((__nonnull__, __format__(printf, 1, 2)));
+
+/* Allocate memory, reporting a fatal error with bail on failure. */
+void *bcalloc(size_t, size_t)
+ __attribute__((__alloc_size__(1, 2), __malloc__));
+void *bmalloc(size_t)
+ __attribute__((__alloc_size__(1), __malloc__));
+void *brealloc(void *, size_t)
+ __attribute__((__alloc_size__(2), __malloc__));
+char *bstrdup(const char *)
+ __attribute__((__malloc__, __nonnull__));
+char *bstrndup(const char *, size_t)
+ __attribute__((__malloc__, __nonnull__));
+
+/*
+ * Find a test file under BUILD or SOURCE, returning the full path. The
+ * returned path should be freed with test_file_path_free().
+ */
+char *test_file_path(const char *file)
+ __attribute__((__malloc__, __nonnull__));
+void test_file_path_free(char *path);
+
+/*
+ * Create a temporary directory relative to BUILD and return the path. The
+ * returned path should be freed with test_tmpdir_free.
+ */
+char *test_tmpdir(void)
+ __attribute__((__malloc__));
+void test_tmpdir_free(char *path);
+
+END_DECLS
diff --git a/tests/tap/files.c b/tests/tap/files.c
new file mode 100644
index 0000000..f979d07
--- /dev/null
+++ b/tests/tap/files.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 2016 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 "files.h"
+
+#include "../../src/contrib/string.c"
+#include "../../src/contrib/files.c"
+
+#include <stdlib.h>
+
+static char *make_temp(bool is_directory)
+{
+ char *tmpdir = getenv("TMPDIR");
+ if (!tmpdir) {
+ tmpdir = "/tmp";
+ }
+
+ char tmp[4096] = { 0 };
+ int r = snprintf(tmp, sizeof(tmp), "%s/knot_unit.XXXXXX", tmpdir);
+ if (r <= 0 || r >= sizeof(tmp)) {
+ return NULL;
+ }
+
+ if (is_directory) {
+ char *ret = mkdtemp(tmp);
+ if (ret == NULL) {
+ return NULL;
+ }
+ } else {
+ int ret = mkstemp(tmp);
+ if (ret == -1) {
+ return NULL;
+ }
+ close(ret);
+ }
+
+ return strdup(tmp);
+}
+
+char *test_mktemp(void)
+{
+ return make_temp(false);
+}
+
+char *test_mkdtemp(void)
+{
+ return make_temp(true);
+}
+
+bool test_rm_rf(const char *path)
+{
+ return remove_path(path);
+}
diff --git a/tests/tap/files.h b/tests/tap/files.h
new file mode 100644
index 0000000..04c9956
--- /dev/null
+++ b/tests/tap/files.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2016 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/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+/*!
+ * \brief Create a temporary file.
+ *
+ * If TMPDIR environment variable is set, the file is created within
+ * that directory. If the variable is not set, the file is created
+ * within /tmp.
+ */
+char *test_mktemp(void);
+
+/*!
+ * \brief Create a temporary directory.
+ *
+ * If TMPDIR environment variable is set, the directory is created within
+ * that directory. If the variable is not set, the directory is created
+ * within /tmp.
+ */
+char *test_mkdtemp(void);
+
+/*!
+ * \brief Delete file or directory (recursive).
+ *
+ * \return true on success, false when one or more files failed to be removed.
+ */
+bool test_rm_rf(const char *path);
diff --git a/tests/tap/float.c b/tests/tap/float.c
new file mode 100644
index 0000000..5c0c643
--- /dev/null
+++ b/tests/tap/float.c
@@ -0,0 +1,67 @@
+/*
+ * Utility routines for writing floating point tests.
+ *
+ * Currently provides only one function, which checks whether a double is
+ * equal to an expected value within a given epsilon. This is broken into a
+ * separate source file from the rest of the basic C TAP library because it
+ * may require linking with -lm on some platforms, and the package may not
+ * otherwise care about floating point.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/* Required for isnan() and isinf(). */
+#if defined(__STRICT_ANSI__) || defined(PEDANTIC)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+# endif
+#endif
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "basic.h"
+#include "float.h"
+
+/*
+ * Takes an expected double and a seen double and assumes the test passes if
+ * those two numbers are within delta of each other.
+ */
+void
+is_double(double wanted, double seen, double epsilon, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ fflush(stderr);
+ if ((isnan(wanted) && isnan(seen))
+ || (isinf(wanted) && isinf(seen) && wanted == seen)
+ || fabs(wanted - seen) <= epsilon)
+ okv(1, format, args);
+ else {
+ printf("# wanted: %g\n# seen: %g\n", wanted, seen);
+ okv(0, format, args);
+ }
+}
diff --git a/tests/tap/float.h b/tests/tap/float.h
new file mode 100644
index 0000000..5c58b5b
--- /dev/null
+++ b/tests/tap/float.h
@@ -0,0 +1,39 @@
+/*
+ * Floating point check function for the TAP protocol.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2010, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include "macros.h"
+
+BEGIN_DECLS
+
+/* Check an expected value against a seen value within epsilon. */
+void is_double(double wanted, double seen, double epsilon,
+ const char *format, ...)
+ __attribute__((__format__(printf, 4, 5)));
+
+END_DECLS
diff --git a/tests/tap/libtap.sh b/tests/tap/libtap.sh
new file mode 100644
index 0000000..0ffab2d
--- /dev/null
+++ b/tests/tap/libtap.sh
@@ -0,0 +1,246 @@
+# Shell function library for test cases.
+#
+# Note that while many of the functions in this library could benefit from
+# using "local" to avoid possibly hammering global variables, Solaris /bin/sh
+# doesn't support local and this library aspires to be portable to Solaris
+# Bourne shell. Instead, all private variables are prefixed with "tap_".
+#
+# This file provides a TAP-compatible shell function library useful for
+# writing test cases. It is part of C TAP Harness, which can be found at
+# <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+#
+# Written by Russ Allbery <rra@stanford.edu>
+# Copyright 2009, 2010, 2011, 2012 Russ Allbery <rra@stanford.edu>
+# Copyright 2006, 2007, 2008, 2013
+# The Board of Trustees of the Leland Stanford Junior University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+# IN THE SOFTWARE.
+
+# Print out the number of test cases we expect to run.
+plan () {
+ count=1
+ planned="$1"
+ failed=0
+ echo "1..$1"
+ trap finish 0
+}
+
+# Prepare for lazy planning.
+plan_lazy () {
+ count=1
+ planned=0
+ failed=0
+ trap finish 0
+}
+
+# Report the test status on exit.
+finish () {
+ tap_highest=`expr "$count" - 1`
+ if [ "$planned" = 0 ] ; then
+ echo "1..$tap_highest"
+ planned="$tap_highest"
+ fi
+ tap_looks='# Looks like you'
+ if [ "$planned" -gt 0 ] ; then
+ if [ "$planned" -gt "$tap_highest" ] ; then
+ if [ "$planned" -gt 1 ] ; then
+ echo "$tap_looks planned $planned tests but only ran" \
+ "$tap_highest"
+ else
+ echo "$tap_looks planned $planned test but only ran" \
+ "$tap_highest"
+ fi
+ elif [ "$planned" -lt "$tap_highest" ] ; then
+ tap_extra=`expr "$tap_highest" - "$planned"`
+ if [ "$planned" -gt 1 ] ; then
+ echo "$tap_looks planned $planned tests but ran" \
+ "$tap_extra extra"
+ else
+ echo "$tap_looks planned $planned test but ran" \
+ "$tap_extra extra"
+ fi
+ elif [ "$failed" -gt 0 ] ; then
+ if [ "$failed" -gt 1 ] ; then
+ echo "$tap_looks failed $failed tests of $planned"
+ else
+ echo "$tap_looks failed $failed test of $planned"
+ fi
+ elif [ "$planned" -gt 1 ] ; then
+ echo "# All $planned tests successful or skipped"
+ else
+ echo "# $planned test successful or skipped"
+ fi
+ fi
+}
+
+# Skip the entire test suite. Should be run instead of plan.
+skip_all () {
+ tap_desc="$1"
+ if [ -n "$tap_desc" ] ; then
+ echo "1..0 # skip $tap_desc"
+ else
+ echo "1..0 # skip"
+ fi
+ exit 0
+}
+
+# ok takes a test description and a command to run and prints success if that
+# command is successful, false otherwise. The count starts at 1 and is
+# updated each time ok is printed.
+ok () {
+ tap_desc="$1"
+ if [ -n "$tap_desc" ] ; then
+ tap_desc=" - $tap_desc"
+ fi
+ shift
+ if "$@" ; then
+ echo ok "$count$tap_desc"
+ else
+ echo not ok "$count$tap_desc"
+ failed=`expr $failed + 1`
+ fi
+ count=`expr $count + 1`
+}
+
+# Skip the next test. Takes the reason why the test is skipped.
+skip () {
+ echo "ok $count # skip $*"
+ count=`expr $count + 1`
+}
+
+# Report the same status on a whole set of tests. Takes the count of tests,
+# the description, and then the command to run to determine the status.
+ok_block () {
+ tap_i=$count
+ tap_end=`expr $count + $1`
+ shift
+ while [ "$tap_i" -lt "$tap_end" ] ; do
+ ok "$@"
+ tap_i=`expr $tap_i + 1`
+ done
+}
+
+# Skip a whole set of tests. Takes the count and then the reason for skipping
+# the test.
+skip_block () {
+ tap_i=$count
+ tap_end=`expr $count + $1`
+ shift
+ while [ "$tap_i" -lt "$tap_end" ] ; do
+ skip "$@"
+ tap_i=`expr $tap_i + 1`
+ done
+}
+
+# Portable variant of printf '%s\n' "$*". In the majority of cases, this
+# function is slower than printf, because the latter is often implemented
+# as a builtin command. The value of the variable IFS is ignored.
+#
+# This macro must not be called via backticks inside double quotes, since this
+# will result in bizarre escaping behavior and lots of extra backslashes on
+# Solaris.
+puts () {
+ cat << EOH
+$@
+EOH
+}
+
+# Run a program expected to succeed, and print ok if it does and produces the
+# correct output. Takes the description, expected exit status, the expected
+# output, the command to run, and then any arguments for that command.
+# Standard output and standard error are combined when analyzing the output of
+# the command.
+#
+# If the command may contain system-specific error messages in its output,
+# add strip_colon_error before the command to post-process its output.
+ok_program () {
+ tap_desc="$1"
+ shift
+ tap_w_status="$1"
+ shift
+ tap_w_output="$1"
+ shift
+ tap_output=`"$@" 2>&1`
+ tap_status=$?
+ if [ $tap_status = $tap_w_status ] \
+ && [ x"$tap_output" = x"$tap_w_output" ] ; then
+ ok "$tap_desc" true
+ else
+ echo "# saw: ($tap_status) $tap_output"
+ echo "# not: ($tap_w_status) $tap_w_output"
+ ok "$tap_desc" false
+ fi
+}
+
+# Strip a colon and everything after it off the output of a command, as long
+# as that colon comes after at least one whitespace character. (This is done
+# to avoid stripping the name of the program from the start of an error
+# message.) This is used to remove system-specific error messages (coming
+# from strerror, for example).
+strip_colon_error() {
+ tap_output=`"$@" 2>&1`
+ tap_status=$?
+ tap_output=`puts "$tap_output" | sed 's/^\([^ ]* [^:]*\):.*/\1/'`
+ puts "$tap_output"
+ return $tap_status
+}
+
+# Bail out with an error message.
+bail () {
+ echo 'Bail out!' "$@"
+ exit 255
+}
+
+# Output a diagnostic on standard error, preceded by the required # mark.
+diag () {
+ echo '#' "$@"
+}
+
+# Search for the given file first in $BUILD and then in $SOURCE and echo the
+# path where the file was found, or the empty string if the file wasn't
+# found.
+#
+# This macro uses puts, so don't run it using backticks inside double quotes
+# or bizarre quoting behavior will happen with Solaris sh.
+test_file_path () {
+ if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then
+ puts "$BUILD/$1"
+ elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then
+ puts "$SOURCE/$1"
+ else
+ echo ''
+ fi
+}
+
+# Create $BUILD/tmp for use by tests for storing temporary files and return
+# the path (via standard output).
+#
+# This macro uses puts, so don't run it using backticks inside double quotes
+# or bizarre quoting behavior will happen with Solaris sh.
+test_tmpdir () {
+ if [ -z "$BUILD" ] ; then
+ tap_tmpdir="./tmp"
+ else
+ tap_tmpdir="$BUILD"/tmp
+ fi
+ if [ ! -d "$tap_tmpdir" ] ; then
+ mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir"
+ fi
+ puts "$tap_tmpdir"
+}
diff --git a/tests/tap/macros.h b/tests/tap/macros.h
new file mode 100644
index 0000000..31081a4
--- /dev/null
+++ b/tests/tap/macros.h
@@ -0,0 +1,85 @@
+/*
+ * Helpful macros for TAP header files.
+ *
+ * This is not, strictly speaking, related to TAP, but any TAP add-on is
+ * probably going to need these macros, so define them in one place so that
+ * everyone can pull them in.
+ *
+ * This file is part of C TAP Harness. The current version plus supporting
+ * documentation is at <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2008, 2012 Russ Allbery <rra@stanford.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+/*
+ * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
+ * could you use the __format__ form of the attributes, which is what we use
+ * (to avoid confusion with other macros), and only with gcc 2.96 can you use
+ * the attribute __malloc__. 2.96 is very old, so don't bother trying to get
+ * the other attributes to work with GCC versions between 2.7 and 2.96.
+ */
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+# define __attribute__(spec) /* empty */
+# endif
+#endif
+
+/*
+ * We use __alloc_size__, but it was only available in fairly recent versions
+ * of GCC. Suppress warnings about the unknown attribute if GCC is too old.
+ * We know that we're GCC at this point, so we can use the GCC variadic macro
+ * extension, which will still work with versions of GCC too old to have C99
+ * variadic macro support.
+ */
+#if !defined(__attribute__) && !defined(__alloc_size__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
+#endif
+
+/*
+ * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
+ * settings that GCC does. For them, suppress warnings about unknown
+ * attributes on declarations. This unfortunately will affect the entire
+ * compilation context, but there's no push and pop available.
+ */
+#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
+# pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+/* Used for unused parameters to silence gcc warnings. */
+/* #define UNUSED __attribute__((__unused__)) */
+
+/*
+ * BEGIN_DECLS is used at the beginning of declarations so that C++
+ * compilers don't mangle their names. END_DECLS is used at the end.
+ */
+#undef BEGIN_DECLS
+#undef END_DECLS
+#ifdef __cplusplus
+# define BEGIN_DECLS extern "C" {
+# define END_DECLS }
+#else
+# define BEGIN_DECLS /* empty */
+# define END_DECLS /* empty */
+#endif
diff --git a/tests/tap/runtests.c b/tests/tap/runtests.c
new file mode 100644
index 0000000..31ac943
--- /dev/null
+++ b/tests/tap/runtests.c
@@ -0,0 +1,1404 @@
+/*
+ * Run a set of tests, reporting results.
+ *
+ * Usage:
+ *
+ * runtests [-b <build-dir>] [-s <source-dir>] <test-list>
+ * runtests -o [-b <build-dir>] [-s <source-dir>] <test>
+ *
+ * In the first case, expects a list of executables located in the given file,
+ * one line per executable. For each one, runs it as part of a test suite,
+ * reporting results. Test output should start with a line containing the
+ * number of tests (numbered from 1 to this number), optionally preceded by
+ * "1..", although that line may be given anywhere in the output. Each
+ * additional line should be in the following format:
+ *
+ * ok <number>
+ * not ok <number>
+ * ok <number> # skip
+ * not ok <number> # todo
+ *
+ * where <number> is the number of the test. An optional comment is permitted
+ * after the number if preceded by whitespace. ok indicates success, not ok
+ * indicates failure. "# skip" and "# todo" are a special cases of a comment,
+ * and must start with exactly that formatting. They indicate the test was
+ * skipped for some reason (maybe because it doesn't apply to this platform)
+ * or is testing something known to currently fail. The text following either
+ * "# skip" or "# todo" and whitespace is the reason.
+ *
+ * As a special case, the first line of the output may be in the form:
+ *
+ * 1..0 # skip some reason
+ *
+ * which indicates that this entire test case should be skipped and gives a
+ * reason.
+ *
+ * Any other lines are ignored, although for compliance with the TAP protocol
+ * all lines other than the ones in the above format should be sent to
+ * standard error rather than standard output and start with #.
+ *
+ * This is a subset of TAP as documented in Test::Harness::TAP or
+ * TAP::Parser::Grammar, which comes with Perl.
+ *
+ * If the -o option is given, instead run a single test and display all of its
+ * output. This is intended for use with failing tests so that the person
+ * running the test suite can get more details about what failed.
+ *
+ * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP
+ * Harness will export those values in the environment so that tests can find
+ * the source and build directory and will look for tests under both
+ * directories. These paths can also be set with the -b and -s command-line
+ * options, which will override anything set at build time.
+ *
+ * Any bug reports, bug fixes, and improvements are very much welcome and
+ * should be sent to the e-mail address below. This program is part of C TAP
+ * Harness <http://www.eyrie.org/~eagle/software/c-tap-harness/>.
+ *
+ * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011
+ * Russ Allbery <rra@stanford.edu>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+*/
+
+/* Required for fdopen(), getopt(), and putenv(). */
+#if defined(__STRICT_ANSI__) || defined(PEDANTIC)
+# ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 500
+# endif
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+/* sys/time.h must be included before sys/resource.h on some platforms. */
+#include <sys/resource.h>
+
+/* AIX doesn't have WCOREDUMP. */
+#ifndef WCOREDUMP
+# define WCOREDUMP(status) ((unsigned)(status) & 0x80)
+#endif
+
+/*
+ * Used for iterating through arrays. Returns the number of elements in the
+ * array (useful for a < upper bound in a for loop).
+ */
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+/*
+ * The source and build versions of the tests directory. This is used to set
+ * the SOURCE and BUILD environment variables and find test programs, if set.
+ * Normally, this should be set as part of the build process to the test
+ * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively.
+ */
+#ifndef SOURCE
+# define SOURCE NULL
+#endif
+#ifndef BUILD
+# define BUILD NULL
+#endif
+
+/* Test status codes. */
+enum test_status {
+ TEST_FAIL,
+ TEST_PASS,
+ TEST_SKIP,
+ TEST_INVALID
+};
+
+/* Indicates the state of our plan. */
+enum plan_status {
+ PLAN_INIT, /* Nothing seen yet. */
+ PLAN_FIRST, /* Plan seen before any tests. */
+ PLAN_PENDING, /* Test seen and no plan yet. */
+ PLAN_FINAL /* Plan seen after some tests. */
+};
+
+/* Error exit statuses for test processes. */
+#define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */
+#define CHILDERR_EXEC 101 /* Couldn't exec child process. */
+#define CHILDERR_STDERR 102 /* Couldn't open stderr file. */
+
+/* Structure to hold data for a set of tests. */
+struct testset {
+ char *file; /* The file name of the test. */
+ char *path; /* The path to the test program. */
+ enum plan_status plan; /* The status of our plan. */
+ unsigned long count; /* Expected count of tests. */
+ unsigned long current; /* The last seen test number. */
+ unsigned int length; /* The length of the last status message. */
+ unsigned long passed; /* Count of passing tests. */
+ unsigned long failed; /* Count of failing lists. */
+ unsigned long skipped; /* Count of skipped tests (passed). */
+ unsigned long allocated; /* The size of the results table. */
+ enum test_status *results; /* Table of results by test number. */
+ unsigned int aborted; /* Whether the set was aborted. */
+ unsigned int killed; /* Whether the set was killed. */
+ int reported; /* Whether the results were reported. */
+ int status; /* The exit status of the test. */
+ unsigned int all_skipped; /* Whether all tests were skipped. */
+ char *reason; /* Why all tests were skipped. */
+};
+
+/* Structure to hold a linked list of test sets. */
+struct testlist {
+ struct testset *ts;
+ struct testlist *next;
+};
+
+/*
+ * Usage message. Should be used as a printf format with four arguments: the
+ * path to runtests, given three times, and the usage_description. This is
+ * split into variables to satisfy the pedantic ISO C90 limit on strings.
+ */
+static const char usage_message[] = "\
+Usage: %s [-b <build-dir>] [-s <source-dir>] <test> ...\n\
+ %s [-b <build-dir>] [-s <source-dir>] -l <test-list> -L <log-file>\n\
+ %s -o [-b <build-dir>] [-s <source-dir>] <test>\n\
+\n%s";
+static const char usage_extra[] = "\
+Options:\n\
+ -b <build-dir> Set the build directory to <build-dir>\n\
+ -l <list> Take the list of tests to run from <test-list>\n\
+ -o Run a single test rather than a list of tests\n\
+ -s <source-dir> Set the source directory to <source-dir>\n\
+ -L <log-file> Set log file for a list of tests\n\
+\n\
+runtests normally runs each test listed on the command line. With the -l\n\
+option, it instead runs every test listed in a file. With the -o option,\n\
+it instead runs a single test and shows its complete output.\n";
+
+/*
+ * Header used for test output. %s is replaced by the file name of the list
+ * of tests.
+ */
+static const char banner[] = "\n\
+Running all tests listed in %s. If any tests fail, run the failing\n\
+test program with runtests -o to see more details.\n\n";
+
+/* Header for reports of failed tests. */
+static const char header[] = "\n\
+Failed Set Fail/Total (%) Skip Stat Failing Tests\n\
+-------------------------- -------------- ---- ---- ------------------------";
+
+/* Include the file name and line number in malloc failures. */
+#define xcalloc(n, size) x_calloc((n), (size), __FILE__, __LINE__)
+#define xmalloc(size) x_malloc((size), __FILE__, __LINE__)
+#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__)
+#define xstrdup(p) x_strdup((p), __FILE__, __LINE__)
+
+/*
+ * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7
+ * could you use the __format__ form of the attributes, which is what we use
+ * (to avoid confusion with other macros).
+ */
+#ifndef __attribute__
+# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
+# define __attribute__(spec) /* empty */
+# endif
+#endif
+
+/*
+ * We use __alloc_size__, but it was only available in fairly recent versions
+ * of GCC. Suppress warnings about the unknown attribute if GCC is too old.
+ * We know that we're GCC at this point, so we can use the GCC variadic macro
+ * extension, which will still work with versions of GCC too old to have C99
+ * variadic macro support.
+ */
+#if !defined(__attribute__) && !defined(__alloc_size__)
+# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
+# define __alloc_size__(spec, args...) /* empty */
+# endif
+#endif
+
+/*
+ * LLVM and Clang pretend to be GCC but don't support all of the __attribute__
+ * settings that GCC does. For them, suppress warnings about unknown
+ * attributes on declarations. This unfortunately will affect the entire
+ * compilation context, but there's no push and pop available.
+ */
+#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__))
+# pragma GCC diagnostic ignored "-Wattributes"
+#endif
+
+/* Declare internal functions that benefit from compiler attributes. */
+static void sysdie(const char *, ...)
+ __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2)));
+static void *x_calloc(size_t, size_t, const char *, int)
+ __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__));
+static void *x_malloc(size_t, const char *, int)
+ __attribute__((__alloc_size__(1), __malloc__, __nonnull__));
+static void *x_realloc(void *, size_t, const char *, int)
+ __attribute__((__alloc_size__(2), __malloc__, __nonnull__(3)));
+static char *x_strdup(const char *, const char *, int)
+ __attribute__((__malloc__, __nonnull__));
+
+/*
+ * Report a fatal error, including the results of strerror, and exit.
+ */
+static void
+sysdie(const char *format, ...)
+{
+ int oerrno;
+ va_list args;
+
+ oerrno = errno;
+ fflush(stdout);
+ fprintf(stderr, "runtests: ");
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, ": %s\n", strerror(oerrno));
+ exit(1);
+}
+
+/*
+ * Allocate zeroed memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_calloc(size_t n, size_t size, const char *file, int line)
+{
+ void *p;
+
+ n = (n > 0) ? n : 1;
+ size = (size > 0) ? size : 1;
+ p = calloc(n, size);
+ if (p == NULL)
+ sysdie("failed to calloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Allocate memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_malloc(size_t size, const char *file, int line)
+{
+ void *p;
+
+ p = malloc(size);
+ if (p == NULL)
+ sysdie("failed to malloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Reallocate memory, reporting a fatal error and exiting on failure.
+ */
+static void *
+x_realloc(void *p, size_t size, const char *file, int line)
+{
+ p = realloc(p, size);
+ if (p == NULL)
+ sysdie("failed to realloc %lu bytes at %s line %d",
+ (unsigned long) size, file, line);
+ return p;
+}
+
+/*
+ * Copy a string, reporting a fatal error and exiting on failure.
+ */
+static char *
+x_strdup(const char *s, const char *file, int line)
+{
+ char *p;
+ size_t len;
+
+ len = strlen(s) + 1;
+ p = malloc(len);
+ if (p == NULL)
+ sysdie("failed to strdup %lu bytes at %s line %d",
+ (unsigned long) len, file, line);
+ memcpy(p, s, len);
+ return p;
+}
+
+/*
+ * Given a struct timeval, return the number of seconds it represents as a
+ * double. Use difftime() to convert a time_t to a double.
+ */
+static double
+tv_seconds(const struct timeval *tv)
+{
+ return difftime(tv->tv_sec, 0) + tv->tv_usec * 1e-6;
+}
+
+/*
+ * Given two struct timevals, return the difference in seconds.
+ */
+static double
+tv_diff(const struct timeval *tv1, const struct timeval *tv0)
+{
+ return tv_seconds(tv1) - tv_seconds(tv0);
+}
+
+/*
+ * Given two struct timevals, return the sum in seconds as a double.
+ */
+static double
+tv_sum(const struct timeval *tv1, const struct timeval *tv2)
+{
+ return tv_seconds(tv1) + tv_seconds(tv2);
+}
+
+/*
+ * Given a pointer to a string, skip any leading whitespace and return a
+ * pointer to the first non-whitespace character.
+ */
+static const char *
+skip_whitespace(const char *p)
+{
+ while (isspace((unsigned char)(*p)))
+ p++;
+ return p;
+}
+
+/*
+ * Start a program, connecting its stdout to a pipe on our end and its stderr
+ * to /dev/null, and storing the file descriptor to read from in the two
+ * argument. Returns the PID of the new process. Errors are fatal.
+ */
+static pid_t
+test_start(const char *path, int *fd)
+{
+ int fds[2], errfd;
+ pid_t child;
+
+ if (pipe(fds) == -1) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("can't create pipe");
+ }
+ child = fork();
+ if (child == (pid_t) -1) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("can't fork");
+ } else if (child == 0) {
+ /* In child. Set up our stdout and stderr. */
+ errfd = open("/dev/null", O_WRONLY);
+ if (errfd < 0)
+ _exit(CHILDERR_STDERR);
+ if (dup2(errfd, 2) == -1)
+ _exit(CHILDERR_DUP);
+ close(fds[0]);
+ if (dup2(fds[1], 1) == -1)
+ _exit(CHILDERR_DUP);
+
+ /* Now, exec our process. */
+ if (execl(path, path, (char *) 0) == -1)
+ _exit(CHILDERR_EXEC);
+ } else {
+ /* In parent. Close the extra file descriptor. */
+ close(fds[1]);
+ }
+ *fd = fds[0];
+ return child;
+}
+
+/*
+ * Back up over the output saying what test we were executing.
+ */
+static void
+test_backspace(struct testset *ts)
+{
+ unsigned int i;
+
+ if (!isatty(STDOUT_FILENO))
+ return;
+ for (i = 0; i < ts->length; i++)
+ putchar('\b');
+ for (i = 0; i < ts->length; i++)
+ putchar(' ');
+ for (i = 0; i < ts->length; i++)
+ putchar('\b');
+ ts->length = 0;
+}
+
+/*
+ * Read the plan line of test output, which should contain the range of test
+ * numbers. We may initialize the testset structure here if we haven't yet
+ * seen a test. Return true if initialization succeeded and the test should
+ * continue, false otherwise.
+ */
+static int
+test_plan(const char *line, struct testset *ts)
+{
+ unsigned long i;
+ long n;
+
+ /*
+ * Accept a plan without the leading 1.. for compatibility with older
+ * versions of runtests. This will only be allowed if we've not yet seen
+ * a test result.
+ */
+ line = skip_whitespace(line);
+ if (strncmp(line, "1..", 3) == 0)
+ line += 3;
+
+ /*
+ * Get the count, check it for validity, and initialize the struct. If we
+ * have something of the form "1..0 # skip foo", the whole file was
+ * skipped; record that. If we do skip the whole file, zero out all of
+ * our statistics, since they're no longer relevant. strtol is called
+ * with a second argument to advance the line pointer past the count to
+ * make it simpler to detect the # skip case.
+ */
+ n = strtol(line, (char **) &line, 10);
+ if (n == 0) {
+ line = skip_whitespace(line);
+ if (*line == '#') {
+ line = skip_whitespace(line + 1);
+ if (strncasecmp(line, "skip", 4) == 0) {
+ line = skip_whitespace(line + 4);
+ if (*line != '\0') {
+ ts->reason = xstrdup(line);
+ ts->reason[strlen(ts->reason) - 1] = '\0';
+ }
+ ts->all_skipped = 1;
+ ts->aborted = 1;
+ ts->count = 0;
+ ts->passed = 0;
+ ts->skipped = 0;
+ ts->failed = 0;
+ ts->killed = 0;
+ return 0;
+ }
+ }
+ }
+ if (n <= 0) {
+ puts("ABORTED (invalid test count)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ if (ts->plan == PLAN_INIT && ts->allocated == 0) {
+ ts->count = n;
+ ts->allocated = n;
+ ts->plan = PLAN_FIRST;
+ ts->results = xmalloc(ts->count * sizeof(enum test_status));
+ for (i = 0; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ } else if (ts->plan == PLAN_PENDING) {
+ if ((unsigned long) n < ts->count) {
+ test_backspace(ts);
+ printf("ABORTED (invalid test number %lu)\n", ts->count);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return 0;
+ }
+ ts->count = n;
+ if ((unsigned long) n > ts->allocated) {
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < ts->count; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ ts->plan = PLAN_FINAL;
+ }
+ return 1;
+}
+
+/*
+ * Given a single line of output from a test, parse it and return the success
+ * status of that test. Anything printed to stdout not matching the form
+ * /^(not )?ok \d+/ is ignored. Sets ts->current to the test number that just
+ * reported status.
+ */
+static void
+test_checkline(const char *line, struct testset *ts)
+{
+ enum test_status status = TEST_PASS;
+ const char *bail;
+ char *end;
+ long number;
+ unsigned long i, current;
+ int outlen;
+
+ /* Before anything, check for a test abort. */
+ bail = strstr(line, "Bail out!");
+ if (bail != NULL) {
+ bail = skip_whitespace(bail + strlen("Bail out!"));
+ if (*bail != '\0') {
+ size_t length;
+
+ length = strlen(bail);
+ if (bail[length - 1] == '\n')
+ length--;
+ test_backspace(ts);
+ printf("ABORTED (%.*s)\n", (int) length, bail);
+ ts->reported = 1;
+ }
+ ts->aborted = 1;
+ return;
+ }
+
+ /*
+ * If the given line isn't newline-terminated, it was too big for an
+ * fgets(), which means ignore it.
+ */
+ if (line[strlen(line) - 1] != '\n')
+ return;
+
+ /* If the line begins with a hash mark, ignore it. */
+ if (line[0] == '#')
+ return;
+
+ /* If we haven't yet seen a plan, look for one. */
+ if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) {
+ if (!test_plan(line, ts))
+ return;
+ } else if (strncmp(line, "1..", 3) == 0) {
+ if (ts->plan == PLAN_PENDING) {
+ if (!test_plan(line, ts))
+ return;
+ } else {
+ test_backspace(ts);
+ puts("ABORTED (multiple plans)");
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+ }
+
+ /* Parse the line, ignoring something we can't parse. */
+ if (strncmp(line, "not ", 4) == 0) {
+ status = TEST_FAIL;
+ line += 4;
+ }
+ if (strncmp(line, "ok", 2) != 0)
+ return;
+ line = skip_whitespace(line + 2);
+ errno = 0;
+ number = strtol(line, &end, 10);
+ if (errno != 0 || end == line)
+ number = ts->current + 1;
+ current = number;
+ if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) {
+ test_backspace(ts);
+ printf("ABORTED (invalid test number %lu)\n", current);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+
+ /* We have a valid test result. Tweak the results array if needed. */
+ if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) {
+ ts->plan = PLAN_PENDING;
+ if (current > ts->count)
+ ts->count = current;
+ if (current > ts->allocated) {
+ unsigned long n;
+
+ n = (ts->allocated == 0) ? 32 : ts->allocated * 2;
+ if (n < current)
+ n = current;
+ ts->results = xrealloc(ts->results, n * sizeof(enum test_status));
+ for (i = ts->allocated; i < n; i++)
+ ts->results[i] = TEST_INVALID;
+ ts->allocated = n;
+ }
+ }
+
+ /*
+ * Handle directives. We should probably do something more interesting
+ * with unexpected passes of todo tests.
+ */
+ while (isdigit((unsigned char)(*line)))
+ line++;
+ line = skip_whitespace(line);
+ if (*line == '#') {
+ line = skip_whitespace(line + 1);
+ if (strncasecmp(line, "skip", 4) == 0)
+ status = TEST_SKIP;
+ if (strncasecmp(line, "todo", 4) == 0)
+ status = (status == TEST_FAIL) ? TEST_SKIP : TEST_FAIL;
+ }
+
+ /* Make sure that the test number is in range and not a duplicate. */
+ if (ts->results[current - 1] != TEST_INVALID) {
+ test_backspace(ts);
+ printf("ABORTED (duplicate test number %lu)\n", current);
+ ts->aborted = 1;
+ ts->reported = 1;
+ return;
+ }
+
+ /* Good results. Increment our various counters. */
+ switch (status) {
+ case TEST_PASS: ts->passed++; break;
+ case TEST_FAIL: ts->failed++; break;
+ case TEST_SKIP: ts->skipped++; break;
+ case TEST_INVALID: break;
+ }
+ ts->current = current;
+ ts->results[current - 1] = status;
+ if (isatty(STDOUT_FILENO)) {
+ test_backspace(ts);
+ if (ts->plan == PLAN_PENDING)
+ outlen = printf("%lu/?", current);
+ else
+ outlen = printf("%lu/%lu", current, ts->count);
+ ts->length = (outlen >= 0) ? outlen : 0;
+ fflush(stdout);
+ }
+}
+
+/*
+ * Print out a range of test numbers, returning the number of characters it
+ * took up. Takes the first number, the last number, the number of characters
+ * already printed on the line, and the limit of number of characters the line
+ * can hold. Add a comma and a space before the range if chars indicates that
+ * something has already been printed on the line, and print ... instead if
+ * chars plus the space needed would go over the limit (use a limit of 0 to
+ * disable this).
+ */
+static unsigned int
+test_print_range(unsigned long first, unsigned long last, unsigned int chars,
+ unsigned int limit)
+{
+ unsigned int needed = 0;
+ unsigned long n;
+
+ for (n = first; n > 0; n /= 10)
+ needed++;
+ if (last > first) {
+ for (n = last; n > 0; n /= 10)
+ needed++;
+ needed++;
+ }
+ if (chars > 0)
+ needed += 2;
+ if (limit > 0 && chars + needed > limit) {
+ needed = 0;
+ if (chars <= limit) {
+ if (chars > 0) {
+ printf(", ");
+ needed += 2;
+ }
+ printf("...");
+ needed += 3;
+ }
+ } else {
+ if (chars > 0)
+ printf(", ");
+ if (last > first)
+ printf("%lu-", first);
+ printf("%lu", last);
+ }
+ return needed;
+}
+
+/*
+ * Summarize a single test set. The second argument is 0 if the set exited
+ * cleanly, a positive integer representing the exit status if it exited
+ * with a non-zero status, and a negative integer representing the signal
+ * that terminated it if it was killed by a signal.
+ */
+static void
+test_summarize(struct testset *ts, int status)
+{
+ unsigned long i;
+ unsigned long missing = 0;
+ unsigned long failed = 0;
+ unsigned long first = 0;
+ unsigned long last = 0;
+
+ if (ts->aborted) {
+ fputs("ABORTED", stdout);
+ if (ts->count > 0)
+ printf(" (passed %lu/%lu)", ts->passed, ts->count - ts->skipped);
+ } else {
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_INVALID) {
+ if (missing == 0)
+ fputs("MISSED ", stdout);
+ if (first && i == last)
+ last = i + 1;
+ else {
+ if (first)
+ test_print_range(first, last, missing - 1, 0);
+ missing++;
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first)
+ test_print_range(first, last, missing - 1, 0);
+ first = 0;
+ last = 0;
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_FAIL) {
+ if (missing && !failed)
+ fputs("; ", stdout);
+ if (failed == 0)
+ fputs("FAILED ", stdout);
+ if (first && i == last)
+ last = i + 1;
+ else {
+ if (first)
+ test_print_range(first, last, failed - 1, 0);
+ failed++;
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first)
+ test_print_range(first, last, failed - 1, 0);
+ if (!missing && !failed) {
+ fputs(!status ? "ok" : "dubious", stdout);
+ if (ts->skipped > 0) {
+ if (ts->skipped == 1)
+ printf(" (skipped %lu test)", ts->skipped);
+ else
+ printf(" (skipped %lu tests)", ts->skipped);
+ }
+ }
+ }
+ if (status > 0)
+ printf(" (exit status %d)", status);
+ else if (status < 0)
+ printf(" (killed by signal %d%s)", -status,
+ WCOREDUMP(ts->status) ? ", core dumped" : "");
+ putchar('\n');
+}
+
+/*
+ * Given a test set, analyze the results, classify the exit status, handle a
+ * few special error messages, and then pass it along to test_summarize() for
+ * the regular output. Returns true if the test set ran successfully and all
+ * tests passed or were skipped, false otherwise.
+ */
+static int
+test_analyze(struct testset *ts)
+{
+ if (ts->reported)
+ return 0;
+ if (ts->all_skipped) {
+ if (ts->reason == NULL)
+ puts("skipped");
+ else
+ printf("skipped (%s)\n", ts->reason);
+ return 1;
+ } else if (WIFEXITED(ts->status) && WEXITSTATUS(ts->status) != 0) {
+ switch (WEXITSTATUS(ts->status)) {
+ case CHILDERR_DUP:
+ if (!ts->reported)
+ puts("ABORTED (can't dup file descriptors)");
+ break;
+ case CHILDERR_EXEC:
+ if (!ts->reported)
+ puts("ABORTED (execution failed -- not found?)");
+ break;
+ case CHILDERR_STDERR:
+ if (!ts->reported)
+ puts("ABORTED (can't open /dev/null)");
+ break;
+ default:
+ test_summarize(ts, WEXITSTATUS(ts->status));
+ break;
+ }
+ return 0;
+ } else if (WIFSIGNALED(ts->status)) {
+ test_summarize(ts, -WTERMSIG(ts->status));
+ ts->killed = 1;
+ return 0;
+ } else if (ts->plan != PLAN_FIRST && ts->plan != PLAN_FINAL) {
+ puts("ABORTED (no valid test plan)");
+ ts->aborted = 1;
+ return 0;
+ } else {
+ test_summarize(ts, 0);
+ return (ts->failed == 0);
+ }
+}
+
+static void
+cond_fputs(const char *buffer, FILE *stream)
+{
+ if (!stream) {
+ return;
+ }
+
+ fputs(buffer, stream);
+}
+
+/*
+ * Runs a single test set, accumulating and then reporting the results.
+ * Returns true if the test set was successfully run and all tests passed,
+ * false otherwise.
+ */
+static int
+test_run(struct testset *ts, FILE *logfile)
+{
+ pid_t testpid, child;
+ int outfd, status;
+ unsigned long i;
+ FILE *output;
+ char buffer[BUFSIZ];
+
+ /* Run the test program. */
+ testpid = test_start(ts->path, &outfd);
+ output = fdopen(outfd, "r");
+ if (!output) {
+ puts("ABORTED");
+ fflush(stdout);
+ sysdie("fdopen failed");
+ }
+
+ /* Pass each line of output to test_checkline(). */
+ while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) {
+ cond_fputs(buffer, logfile);
+ test_checkline(buffer, ts);
+ }
+ if (ferror(output) || ts->plan == PLAN_INIT)
+ ts->aborted = 1;
+ test_backspace(ts);
+
+ /*
+ * Consume the rest of the test output, close the output descriptor,
+ * retrieve the exit status, and pass that information to test_analyze()
+ * for eventual output.
+ */
+ while (fgets(buffer, sizeof(buffer), output))
+ ;
+ fclose(output);
+ child = waitpid(testpid, &ts->status, 0);
+ if (child == (pid_t) -1) {
+ if (!ts->reported) {
+ puts("ABORTED");
+ fflush(stdout);
+ }
+ sysdie("waitpid for %u failed", (unsigned int) testpid);
+ }
+ if (ts->all_skipped)
+ ts->aborted = 0;
+ if (WEXITSTATUS(ts->status) > 0)
+ ts->failed++;
+ status = test_analyze(ts);
+
+ /* Convert missing tests to failed tests. */
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_INVALID) {
+ ts->failed++;
+ ts->results[i] = TEST_FAIL;
+ status = 0;
+ }
+ }
+ return status;
+}
+
+/* Summarize a list of test failures. */
+static void
+test_fail_summary(const struct testlist *fails)
+{
+ struct testset *ts;
+ unsigned int chars;
+ unsigned long i, first, last, total;
+
+ puts(header);
+
+ /* Failed Set Fail/Total (%) Skip Stat Failing (25)
+ -------------------------- -------------- ---- ---- -------------- */
+ for (; fails; fails = fails->next) {
+ ts = fails->ts;
+ total = ts->count - ts->skipped;
+ printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed,
+ total, total ? (ts->failed * 100.0) / total : 0,
+ ts->skipped);
+ if (WIFEXITED(ts->status)) {
+ printf("%4d ", WEXITSTATUS(ts->status));
+ } else if (ts->killed) {
+ printf("KILL ");
+ } else {
+ printf(" -- ");
+ }
+ if (ts->aborted) {
+ puts("aborted");
+ continue;
+ }
+ chars = 0;
+ first = 0;
+ last = 0;
+ for (i = 0; i < ts->count; i++) {
+ if (ts->results[i] == TEST_FAIL) {
+ if (first != 0 && i == last)
+ last = i + 1;
+ else {
+ if (first != 0)
+ chars += test_print_range(first, last, chars, 19);
+ first = i + 1;
+ last = i + 1;
+ }
+ }
+ }
+ if (first != 0)
+ test_print_range(first, last, chars, 19);
+ putchar('\n');
+ }
+}
+
+/*
+ * Check whether a given file path is a valid test. Currently, this checks
+ * whether it is executable and is a regular file. Returns true or false.
+ */
+static int
+is_valid_test(const char *path)
+{
+ struct stat st;
+
+ if (access(path, X_OK) < 0)
+ return 0;
+ if (stat(path, &st) < 0)
+ return 0;
+ if (!S_ISREG(st.st_mode))
+ return 0;
+ return 1;
+}
+
+/*
+ * Given the name of a test, a pointer to the testset struct, and the source
+ * and build directories, find the test. We try first relative to the current
+ * directory, then in the build directory (if not NULL), then in the source
+ * directory. In each of those directories, we first try a "-t" extension and
+ * then a ".t" extension. When we find an executable program, we return the
+ * path to that program. If none of those paths are executable, just fill in
+ * the name of the test as is.
+ *
+ * The caller is responsible for freeing the path member of the testset
+ * struct.
+ */
+static char *
+find_test(const char *name, const char *source, const char *build)
+{
+ char *path;
+ const char *bases[3], *suffix, *base;
+ unsigned int i, j;
+ const char *suffixes[3] = { "-t", ".t", "" };
+
+ /* Possible base directories. */
+ bases[0] = ".";
+ bases[1] = build;
+ bases[2] = source;
+
+ /* Try each suffix with each base. */
+ for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
+ suffix = suffixes[i];
+ for (j = 0; j < ARRAY_SIZE(bases); j++) {
+ unsigned int path_len;
+
+ base = bases[j];
+ if (base == NULL)
+ continue;
+ path_len = strlen(base) + strlen(name) + strlen(suffix) + 2;
+ path = xmalloc(path_len);
+ snprintf(path, path_len, "%s/%s%s", base, name, suffix);
+ if (is_valid_test(path))
+ return path;
+ free(path);
+ path = NULL;
+ }
+ }
+ if (path == NULL)
+ path = xstrdup(name);
+ return path;
+}
+
+/*
+ * Read a list of tests from a file, returning the list of tests as a struct
+ * testlist. Reports an error to standard error and exits if the list of
+ * tests cannot be read.
+ */
+static struct testlist *
+read_test_list(const char *filename)
+{
+ FILE *file;
+ unsigned int line;
+ size_t length;
+ char buffer[BUFSIZ];
+ struct testlist *listhead, *current;
+
+ /* Create the initial container list that will hold our results. */
+ listhead = xmalloc(sizeof(struct testlist));
+ listhead->ts = NULL;
+ listhead->next = NULL;
+ current = NULL;
+
+ /*
+ * Open our file of tests to run and read it line by line, creating a new
+ * struct testlist and struct testset for each line.
+ */
+ file = fopen(filename, "r");
+ if (file == NULL)
+ sysdie("can't open %s", filename);
+ line = 0;
+ while (fgets(buffer, sizeof(buffer), file)) {
+ line++;
+ length = strlen(buffer) - 1;
+ if (buffer[length] != '\n') {
+ fprintf(stderr, "%s:%u: line too long\n", filename, line);
+ exit(1);
+ }
+ buffer[length] = '\0';
+ if (current == NULL)
+ current = listhead;
+ else {
+ current->next = xmalloc(sizeof(struct testlist));
+ current = current->next;
+ current->next = NULL;
+ }
+ current->ts = xcalloc(1, sizeof(struct testset));
+ current->ts->plan = PLAN_INIT;
+ current->ts->file = xstrdup(buffer);
+ current->ts->reason = NULL;
+ }
+ fclose(file);
+
+ /* Return the results. */
+ return listhead;
+}
+
+/*
+ * Build a list of tests from command line arguments. Takes the argv and argc
+ * representing the command line arguments and returns a newly allocated test
+ * list. The caller is responsible for freeing.
+ */
+static struct testlist *
+build_test_list(char *argv[], int argc)
+{
+ int i;
+ struct testlist *listhead, *current;
+
+ /* Create the initial container list that will hold our results. */
+ listhead = xmalloc(sizeof(struct testlist));
+ listhead->ts = NULL;
+ listhead->next = NULL;
+ current = NULL;
+
+ /* Walk the list of arguments and create test sets for them. */
+ for (i = 0; i < argc; i++) {
+ if (current == NULL)
+ current = listhead;
+ else {
+ current->next = xmalloc(sizeof(struct testlist));
+ current = current->next;
+ current->next = NULL;
+ }
+ current->ts = xcalloc(1, sizeof(struct testset));
+ current->ts->plan = PLAN_INIT;
+ current->ts->file = xstrdup(argv[i]);
+ current->ts->reason = NULL;
+ }
+
+ /* Return the results. */
+ return listhead;
+}
+
+/* Free a struct testset. */
+static void
+free_testset(struct testset *ts)
+{
+ free(ts->file);
+ free(ts->path);
+ free(ts->results);
+ if (ts->reason != NULL)
+ free(ts->reason);
+ free(ts);
+}
+
+/*
+ * Run a batch of tests. Takes two additional parameters: the root of the
+ * source directory and the root of the build directory. Test programs will
+ * be first searched for in the current directory, then the build directory,
+ * then the source directory. Returns true iff all tests passed, and always
+ * frees the test list that's passed in.
+ */
+static int
+test_batch(struct testlist *tests, const char *source, const char *build,
+ const char *logfile_name)
+{
+ size_t length;
+ unsigned int i;
+ unsigned int longest = 0;
+ unsigned int count = 0;
+ struct testset *ts;
+ struct timeval start, end;
+ struct rusage stats;
+ struct testlist *failhead = NULL;
+ struct testlist *failtail = NULL;
+ struct testlist *current, *next;
+ int succeeded;
+ FILE *logfile = NULL;
+ unsigned long total = 0;
+ unsigned long passed = 0;
+ unsigned long skipped = 0;
+ unsigned long failed = 0;
+ unsigned long aborted = 0;
+ unsigned long killed = 0;
+
+ /* Walk the list of tests to find the longest name. */
+ for (current = tests; current != NULL; current = current->next) {
+ length = strlen(current->ts->file);
+ if (length > longest)
+ longest = length;
+ }
+
+ /*
+ * Add two to longest and round up to the nearest tab stop. This is how
+ * wide the column for printing the current test name will be.
+ */
+ longest += 2;
+ if (longest % 8)
+ longest += 8 - (longest % 8);
+
+ /* Start the wall clock timer. */
+ gettimeofday(&start, NULL);
+
+ /* Open the log (soft error). */
+ if (logfile_name != NULL) {
+ logfile = fopen(logfile_name, "w+");
+ if (!logfile) {
+ fprintf(stderr, "Could not open the log file.\n");
+ }
+ }
+
+ /* Now, plow through our tests again, running each one. */
+ for (current = tests; current != NULL; current = current->next) {
+ ts = current->ts;
+
+ /* Print out the name of the test file. */
+ fputs(ts->file, stdout);
+ for (i = strlen(ts->file); i < longest; i++)
+ putchar('.');
+ if (isatty(STDOUT_FILENO))
+ fflush(stdout);
+
+ /* Run the test. */
+ ts->path = find_test(ts->file, source, build);
+ succeeded = test_run(ts, logfile);
+ fflush(stdout);
+
+ /* Record cumulative statistics. */
+ aborted += ts->aborted;
+ total += ts->count + ts->all_skipped;
+ passed += ts->passed;
+ skipped += ts->skipped + ts->all_skipped;
+ failed += ts->failed;
+ killed += ts->killed;
+ count++;
+
+ /* If the test fails, we shuffle it over to the fail list. */
+ if (!succeeded) {
+ if (failhead == NULL) {
+ failhead = xmalloc(sizeof(struct testset));
+ failtail = failhead;
+ } else {
+ failtail->next = xmalloc(sizeof(struct testset));
+ failtail = failtail->next;
+ }
+ failtail->ts = ts;
+ failtail->next = NULL;
+ }
+ }
+ total -= skipped;
+
+ /* Close the log. */
+ if (logfile) {
+ fclose(logfile);
+ }
+
+ /* Stop the timer and get our child resource statistics. */
+ gettimeofday(&end, NULL);
+ getrusage(RUSAGE_CHILDREN, &stats);
+
+ /* Summarize the failures and free the failure list. */
+ if (failhead != NULL) {
+ test_fail_summary(failhead);
+ while (failhead != NULL) {
+ next = failhead->next;
+ free(failhead);
+ failhead = next;
+ }
+ }
+
+ /* Free the memory used by the test lists. */
+ while (tests != NULL) {
+ next = tests->next;
+ free_testset(tests->ts);
+ free(tests);
+ tests = next;
+ }
+
+ /* Print out the final test summary. */
+ putchar('\n');
+ if (killed != 0) {
+ printf("Killed %lu test set%s, passed %lu/%lu tests",
+ killed, killed == 1 ? "" : "s", passed, total);
+ } else if (aborted != 0) {
+ if (aborted == 1)
+ printf("Aborted %lu test set", aborted);
+ else
+ printf("Aborted %lu test sets", aborted);
+ printf(", passed %lu/%lu tests", passed, total);
+ }
+ else if (failed == 0)
+ fputs("All tests successful", stdout);
+ else
+ printf("Failed %lu/%lu tests, %.2f%% okay", failed, total,
+ (total - failed) * 100.0 / total);
+ if (skipped != 0) {
+ if (skipped == 1)
+ printf(", %lu test skipped", skipped);
+ else
+ printf(", %lu tests skipped", skipped);
+ }
+ puts(".");
+ printf("Files=%u, Tests=%lu", count, total);
+ printf(", %.2f seconds", tv_diff(&end, &start));
+ printf(" (%.2f usr + %.2f sys = %.2f CPU)\n",
+ tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime),
+ tv_sum(&stats.ru_utime, &stats.ru_stime));
+ return (failed == 0 && aborted == 0 && killed == 0);
+}
+
+/*
+ * Run a single test case. This involves just running the test program after
+ * having done the environment setup and finding the test program.
+ */
+static void
+test_single(const char *program, const char *source, const char *build)
+{
+ char *path;
+
+ path = find_test(program, source, build);
+ if (execl(path, path, (char *) 0) == -1)
+ sysdie("cannot exec %s", path);
+}
+
+/*
+ * Main routine. Set the SOURCE and BUILD environment variables and then,
+ * given a file listing tests, run each test listed.
+ */
+int
+main(int argc, char *argv[])
+{
+ int option;
+ int status = 0;
+ int single = 0;
+ char *source_env = NULL;
+ char *build_env = NULL;
+ const char *shortlist;
+ const char *list = NULL;
+ const char *source = SOURCE;
+ const char *build = BUILD;
+ const char *logfile = NULL;
+ struct testlist *tests;
+
+ while ((option = getopt(argc, argv, "b:hl:os:L:")) != EOF) {
+ switch (option) {
+ case 'b':
+ build = optarg;
+ break;
+ case 'h':
+ printf(usage_message, argv[0], argv[0], argv[0], usage_extra);
+ exit(0);
+ break;
+ case 'l':
+ list = optarg;
+ break;
+ case 'o':
+ single = 1;
+ break;
+ case 's':
+ source = optarg;
+ break;
+ case 'L':
+ logfile = optarg;
+ break;
+ default:
+ exit(1);
+ }
+ }
+ argv += optind;
+ argc -= optind;
+ if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) {
+ fprintf(stderr, usage_message, argv[0], argv[0], argv[0], usage_extra);
+ exit(1);
+ }
+
+ /* Set SOURCE and BUILD environment variables. */
+ if (source != NULL) {
+ unsigned int len = strlen("SOURCE=") + strlen(source) + 1;
+ source_env = xmalloc(len);
+ snprintf(source_env, len, "SOURCE=%s", source);
+ if (putenv(source_env) != 0)
+ sysdie("cannot set SOURCE in the environment");
+ }
+ if (build != NULL) {
+ unsigned int len = strlen("BUILD=") + strlen(build) + 1;
+ build_env = xmalloc(len);
+ snprintf(build_env, len, "BUILD=%s", build);
+ if (putenv(build_env) != 0)
+ sysdie("cannot set BUILD in the environment");
+ }
+
+ /* Run the tests as instructed. */
+ if (single)
+ test_single(argv[0], source, build);
+ else if (list != NULL) {
+ shortlist = strrchr(list, '/');
+ if (shortlist == NULL)
+ shortlist = list;
+ else
+ shortlist++;
+ printf(banner, shortlist);
+ tests = read_test_list(list);
+ status = test_batch(tests, source, build, logfile) ? 0 : 1;
+ } else {
+ tests = build_test_list(argv, argc);
+ status = test_batch(tests, source, build, logfile) ? 0 : 1;
+ }
+
+ /* For valgrind cleanliness, free all our memory. */
+ if (source_env != NULL) {
+ putenv((char *) "SOURCE=");
+ free(source_env);
+ }
+ if (build_env != NULL) {
+ putenv((char *) "BUILD=");
+ free(build_env);
+ }
+ exit(status);
+}
diff --git a/tests/utils/test_cert.c b/tests/utils/test_cert.c
new file mode 100644
index 0000000..364fc08
--- /dev/null
+++ b/tests/utils/test_cert.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 2016 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 "utils/common/cert.h"
+#include "libknot/error.h"
+
+#include <tap/basic.h>
+#include <string.h>
+
+static const uint8_t CERT_DER[] = {
+ 0x30, 0x82, 0x06, 0xad, 0x30, 0x82, 0x05, 0x95, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x07, 0x06, 0xdb, 0x97, 0x5f, 0xf1, 0xa8, 0x9b, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05,
+ 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d,
+ 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55,
+ 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44,
+ 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69,
+ 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13,
+ 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c,
+ 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72,
+ 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61,
+ 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41,
+ 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x36, 0x30, 0x38,
+ 0x31, 0x30, 0x33, 0x36, 0x5a, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x30,
+ 0x36, 0x30, 0x38, 0x31, 0x33, 0x31, 0x33, 0x5a, 0x30, 0x81, 0xa2, 0x31,
+ 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x5a,
+ 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x12, 0x48,
+ 0x6c, 0x61, 0x76, 0x6e, 0x69, 0x20, 0x6d, 0x65, 0x73, 0x74, 0x6f, 0x20,
+ 0x50, 0x72, 0x61, 0x68, 0x61, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55,
+ 0x04, 0x07, 0x13, 0x11, 0x50, 0x72, 0x61, 0x68, 0x61, 0x20, 0x2d, 0x20,
+ 0x56, 0x69, 0x6e, 0x6f, 0x68, 0x72, 0x61, 0x64, 0x79, 0x31, 0x19, 0x30,
+ 0x17, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x10, 0x43, 0x5a, 0x2e, 0x4e,
+ 0x49, 0x43, 0x2c, 0x20, 0x7a, 0x2e, 0x73, 0x2e, 0x70, 0x2e, 0x6f, 0x2e,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0f, 0x77,
+ 0x77, 0x77, 0x2e, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e,
+ 0x63, 0x7a, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x16, 0x68, 0x6f, 0x73, 0x74, 0x6d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x40, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64,
+ 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0x9d, 0xa9, 0x87, 0x3b, 0x2e, 0xfa, 0xfd, 0xf6, 0x0b, 0x63,
+ 0xa8, 0x23, 0xc6, 0x66, 0x3d, 0x02, 0x9e, 0xf0, 0xa0, 0x83, 0x44, 0xbd,
+ 0x1a, 0xea, 0xee, 0xdd, 0xb3, 0x8d, 0xe6, 0xbd, 0xe1, 0xdc, 0xff, 0xf6,
+ 0xa9, 0x10, 0xdd, 0x0e, 0x3e, 0x6b, 0xb2, 0x8d, 0xa7, 0x52, 0x2b, 0xd4,
+ 0xff, 0xc6, 0x7a, 0x65, 0x23, 0x34, 0x02, 0x09, 0xc0, 0x17, 0xcc, 0x5d,
+ 0x47, 0x29, 0x9a, 0xac, 0x40, 0xdc, 0x8a, 0x3a, 0x65, 0xa3, 0xf5, 0x9f,
+ 0x1b, 0xaa, 0xaf, 0xdf, 0xab, 0xa7, 0xd3, 0x14, 0x86, 0xcc, 0xb8, 0x28,
+ 0x9a, 0x65, 0x33, 0xda, 0x22, 0xae, 0x62, 0x1a, 0x6b, 0xb7, 0x67, 0xdc,
+ 0xf0, 0x8c, 0xa3, 0xc1, 0x84, 0x1e, 0xf2, 0xcc, 0xf3, 0xe5, 0xfe, 0xf4,
+ 0xd8, 0x90, 0x50, 0xbc, 0x9d, 0x77, 0xc9, 0x4d, 0xb9, 0x8c, 0xfe, 0xff,
+ 0x33, 0x02, 0x7c, 0x4f, 0xb1, 0x3d, 0x66, 0x30, 0x97, 0xa3, 0xe0, 0x54,
+ 0xc1, 0x3f, 0x4a, 0xd8, 0x3a, 0xa7, 0xcb, 0xe8, 0x16, 0x37, 0x72, 0xb3,
+ 0x36, 0x90, 0x75, 0x1a, 0x2f, 0x95, 0x55, 0xb5, 0x10, 0x18, 0x29, 0xb0,
+ 0xee, 0x32, 0x8b, 0x3e, 0x02, 0x38, 0x5f, 0x53, 0xd6, 0x73, 0x41, 0x4c,
+ 0x1e, 0xae, 0xcf, 0x4f, 0x50, 0xa9, 0x71, 0xbc, 0x79, 0xa5, 0x9e, 0xd6,
+ 0x13, 0x07, 0xa3, 0xaa, 0x89, 0x0d, 0x31, 0x8c, 0x3a, 0x80, 0xe1, 0x53,
+ 0x22, 0x29, 0x43, 0xb9, 0xdf, 0xf6, 0xfb, 0x6d, 0xad, 0xbd, 0xf8, 0xc3,
+ 0xee, 0xbe, 0x59, 0xd0, 0x06, 0x45, 0x35, 0x95, 0xce, 0xcb, 0x61, 0xd9,
+ 0x74, 0x9e, 0x90, 0x96, 0xb8, 0xd6, 0xb2, 0xc9, 0x29, 0xfd, 0x45, 0xaf,
+ 0xea, 0xbe, 0x7b, 0x96, 0x76, 0x59, 0xe8, 0x04, 0x05, 0x5c, 0x8e, 0xc1,
+ 0xb3, 0x7c, 0xeb, 0xd8, 0xc8, 0x3d, 0x84, 0x13, 0x50, 0x07, 0x6f, 0xff,
+ 0x27, 0x69, 0xcb, 0x33, 0x62, 0x87, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3,
+ 0x82, 0x02, 0xfa, 0x30, 0x82, 0x02, 0xf6, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d,
+ 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, 0x30, 0x1d, 0x06, 0x03, 0x55,
+ 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x02, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x01, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+ 0x14, 0x86, 0x4e, 0x14, 0x78, 0x0e, 0x5d, 0x44, 0x21, 0xe0, 0x80, 0x23,
+ 0x6b, 0x1f, 0x9b, 0xf2, 0xd6, 0x09, 0xc5, 0x50, 0xa6, 0x30, 0x1f, 0x06,
+ 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x11, 0xdb,
+ 0x23, 0x45, 0xfd, 0x54, 0xcc, 0x6a, 0x71, 0x6f, 0x84, 0x8a, 0x03, 0xd7,
+ 0xbe, 0xf7, 0x01, 0x2f, 0x26, 0x86, 0x30, 0x36, 0x06, 0x03, 0x55, 0x1d,
+ 0x11, 0x04, 0x2f, 0x30, 0x2d, 0x82, 0x0f, 0x77, 0x77, 0x77, 0x2e, 0x6b,
+ 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x82, 0x0b,
+ 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e, 0x63, 0x7a, 0x82,
+ 0x0d, 0x2a, 0x2e, 0x6b, 0x6e, 0x6f, 0x74, 0x2d, 0x64, 0x6e, 0x73, 0x2e,
+ 0x63, 0x7a, 0x30, 0x82, 0x01, 0x56, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04,
+ 0x82, 0x01, 0x4d, 0x30, 0x82, 0x01, 0x49, 0x30, 0x08, 0x06, 0x06, 0x67,
+ 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x82, 0x01, 0x3b, 0x06, 0x0b, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0x81, 0xb5, 0x37, 0x01, 0x02, 0x03, 0x30, 0x82,
+ 0x01, 0x2a, 0x30, 0x2e, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x02, 0x01, 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77,
+ 0x77, 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x70,
+ 0x64, 0x66, 0x30, 0x81, 0xf7, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x02, 0x02, 0x30, 0x81, 0xea, 0x30, 0x27, 0x16, 0x20, 0x53, 0x74,
+ 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74,
+ 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x03, 0x02, 0x01, 0x01, 0x1a,
+ 0x81, 0xbe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, 0x65, 0x72, 0x74, 0x69,
+ 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x69,
+ 0x73, 0x73, 0x75, 0x65, 0x64, 0x20, 0x61, 0x63, 0x63, 0x6f, 0x72, 0x64,
+ 0x69, 0x6e, 0x67, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x43,
+ 0x6c, 0x61, 0x73, 0x73, 0x20, 0x32, 0x20, 0x56, 0x61, 0x6c, 0x69, 0x64,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43,
+ 0x41, 0x20, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2c, 0x20, 0x72, 0x65,
+ 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20,
+ 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x69, 0x6e, 0x74, 0x65,
+ 0x6e, 0x64, 0x65, 0x64, 0x20, 0x70, 0x75, 0x72, 0x70, 0x6f, 0x73, 0x65,
+ 0x20, 0x69, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e,
+ 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65,
+ 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x70, 0x61, 0x72, 0x74, 0x79, 0x20,
+ 0x6f, 0x62, 0x6c, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x30, 0x35, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x2e, 0x30, 0x2c, 0x30,
+ 0x2a, 0xa0, 0x28, 0xa0, 0x26, 0x86, 0x24, 0x68, 0x74, 0x74, 0x70, 0x3a,
+ 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73,
+ 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x72, 0x74, 0x32, 0x2d,
+ 0x63, 0x72, 0x6c, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0x8e, 0x06, 0x08,
+ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x81, 0x81, 0x30,
+ 0x7f, 0x30, 0x39, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30,
+ 0x01, 0x86, 0x2d, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63,
+ 0x73, 0x70, 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x75, 0x62, 0x2f, 0x63, 0x6c, 0x61, 0x73,
+ 0x73, 0x32, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x63, 0x61,
+ 0x30, 0x42, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02,
+ 0x86, 0x36, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x61, 0x69, 0x61,
+ 0x2e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x73, 0x2f, 0x73, 0x75, 0x62, 0x2e,
+ 0x63, 0x6c, 0x61, 0x73, 0x73, 0x32, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x2e, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x23, 0x06, 0x03,
+ 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74,
+ 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x73, 0x74, 0x61, 0x72,
+ 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0xae, 0xea, 0xec, 0xe0, 0x6e, 0xe1, 0x5e,
+ 0xe3, 0x06, 0xe6, 0x09, 0xff, 0xf2, 0xea, 0xeb, 0xbd, 0xc2, 0xf9, 0xa2,
+ 0x79, 0xbb, 0xd1, 0xc3, 0x9c, 0x9f, 0xbd, 0x74, 0x0c, 0x9c, 0xeb, 0x73,
+ 0xf1, 0x5c, 0x57, 0x98, 0x8c, 0xaf, 0xaa, 0xfb, 0xcf, 0xfb, 0x55, 0x31,
+ 0x54, 0x71, 0x07, 0xdd, 0x7c, 0x83, 0x70, 0xcb, 0x12, 0xbf, 0x05, 0xd8,
+ 0x62, 0xf1, 0xe0, 0x9d, 0x1c, 0x35, 0xb2, 0x42, 0xb1, 0x37, 0xe8, 0x73,
+ 0x4c, 0xe5, 0xda, 0xd9, 0xcb, 0xe6, 0x5a, 0x50, 0x31, 0x14, 0xce, 0x50,
+ 0xc0, 0xfb, 0x68, 0xb9, 0xe6, 0x48, 0x24, 0xdd, 0x4f, 0xbe, 0x34, 0x28,
+ 0xba, 0x21, 0x53, 0x86, 0x86, 0x91, 0x6f, 0xb0, 0x8e, 0x34, 0x20, 0x4d,
+ 0xdf, 0xef, 0xf3, 0x6f, 0xb0, 0x78, 0x89, 0x4b, 0x80, 0x36, 0xe9, 0x75,
+ 0x3a, 0xd6, 0x18, 0xc6, 0x84, 0xe3, 0x0c, 0xa9, 0x24, 0xcb, 0x49, 0xaf,
+ 0x72, 0x09, 0x3a, 0xb5, 0xdd, 0x59, 0xb9, 0xe0, 0xb6, 0x7e, 0xc2, 0x3c,
+ 0xd0, 0xea, 0xeb, 0x39, 0x06, 0x3f, 0xc6, 0xe9, 0x1f, 0x37, 0x25, 0x3c,
+ 0xdf, 0x0d, 0x95, 0xcc, 0x9f, 0xa3, 0x68, 0x15, 0x3b, 0x80, 0x9b, 0x17,
+ 0x1a, 0x54, 0x65, 0x61, 0xb0, 0xcf, 0xb5, 0x76, 0x7c, 0xc2, 0x7e, 0x54,
+ 0x4d, 0x03, 0xe6, 0x90, 0xa0, 0x59, 0xa9, 0x1c, 0x6d, 0x4b, 0x34, 0x03,
+ 0xc3, 0xbb, 0xcd, 0x77, 0x60, 0x0e, 0xb1, 0x4e, 0x22, 0x81, 0xe4, 0x17,
+ 0xf4, 0xd2, 0x58, 0x2c, 0x72, 0x4e, 0xde, 0xd0, 0x24, 0x25, 0xfb, 0xd8,
+ 0x3f, 0xc8, 0x0f, 0x3b, 0x0d, 0xec, 0x75, 0x81, 0x37, 0x08, 0xd0, 0x0a,
+ 0x29, 0x28, 0x9f, 0x7f, 0x35, 0x83, 0x70, 0x18, 0x6c, 0x4b, 0x24, 0x8e,
+ 0xc0, 0xe5, 0xc1, 0xbb, 0x5b, 0x24, 0xb4, 0x5c, 0x8e, 0xbc, 0x79, 0xc0,
+ 0xad, 0x47, 0x17, 0xdd, 0x7a, 0xf2, 0x26, 0x6e, 0xe4
+};
+
+static gnutls_x509_crt_t get_cert(void)
+{
+ gnutls_x509_crt_t cert = NULL;
+ if (gnutls_x509_crt_init(&cert) != 0) {
+ return NULL;
+ }
+
+ const gnutls_datum_t der = {
+ .data = (uint8_t *)CERT_DER,
+ .size = sizeof(CERT_DER)
+ };
+ if (gnutls_x509_crt_import(cert, &der, GNUTLS_X509_FMT_DER) != 0) {
+ gnutls_x509_crt_deinit(cert);
+ return NULL;
+ }
+
+ return cert;
+}
+
+void test_get_pin(void)
+{
+ gnutls_x509_crt_t cert = get_cert();
+ ok(cert != NULL, "create testing certificate");
+
+ static const uint8_t expected[] = {
+ 0x35, 0xa2, 0x1a, 0x6b, 0x95, 0x34, 0x53, 0xed, 0xf7, 0xf7,
+ 0x08, 0x76, 0x08, 0x17, 0x9c, 0x4a, 0x16, 0x6e, 0xcf, 0xd5,
+ 0xff, 0x46, 0x71, 0x1d, 0xa8, 0x08, 0xb7, 0xef, 0x75, 0xaf,
+ 0xfd, 0xa0
+ };
+
+ uint8_t pin[CERT_PIN_LEN] = { 0 };
+
+ int r = cert_get_pin(cert, NULL, 0);
+ ok(r == KNOT_EINVAL, "cert_get_pin(), no target buffer");
+
+ r = cert_get_pin(cert, pin, sizeof(pin) - 1);
+ ok(r == KNOT_EINVAL, "cert_get_pin(), invalid buffer size");
+
+ r = cert_get_pin(cert, pin, sizeof(pin));
+ ok(r == KNOT_EOK && sizeof(pin) == sizeof(expected) &&
+ memcmp(pin, expected, sizeof(pin)) == 0,
+ "cert_get_pin(), valid input");
+
+ gnutls_x509_crt_deinit(cert);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ test_get_pin();
+
+ return 0;
+}
diff --git a/tests/utils/test_lookup.c b/tests/utils/test_lookup.c
new file mode 100644
index 0000000..ddb0f9d
--- /dev/null
+++ b/tests/utils/test_lookup.c
@@ -0,0 +1,136 @@
+/* Copyright (C) 2016 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 <stdlib.h>
+#include <string.h>
+#include <tap/basic.h>
+
+#include "libknot/error.h"
+#include "utils/common/lookup.h"
+
+static void check_search_ok(lookup_t *l, const char *in, const char *out)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_EOK, ret, "Check found");
+ ok(strcmp(out, l->found.key) == 0, "Compare key");
+ ok(strcmp(out, l->found.data) == 0, "Compare data");
+ ok(l->iter.first_key == NULL, "Compare no first key");
+ ok(l->iter.count == 1, "Compare 1 count");
+}
+
+static void check_search_multi(lookup_t *l, const char *in, const char *out,
+ const char *first, size_t count)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_EFEWDATA, ret, "Check found multi");
+ ok(strcmp(out, l->found.key) == 0, "Compare key");
+ ok(l->found.data == NULL, "Compare no data");
+ ok(strcmp(first, l->iter.first_key) == 0, "Compare first key");
+ ok(l->iter.count == count, "Compare count");
+}
+
+static void check_search_none(lookup_t *l, const char *in)
+{
+ diag("Search for '%s'", in);
+ int ret = lookup_search(l, in, strlen(in));
+ is_int(KNOT_ENOENT, ret, "Check not found");
+ ok(l->found.key == NULL, "Check no key");
+ ok(l->found.data == NULL, "Check no data");
+}
+
+static void init(lookup_t *l, const char **table)
+{
+ int ret = lookup_init(l);
+ is_int(KNOT_EOK, ret, "Init");
+
+ while (*table != NULL) {
+ ret = lookup_insert(l, *table, (void *)*table);
+ is_int(KNOT_EOK, ret, "Insert '%s'", *table);
+ table++;
+ }
+}
+
+static void test_search_basic(void)
+{
+ const char* table[] = {
+ "aa",
+ "bb",
+ NULL
+ };
+
+ lookup_t l;
+ init(&l, table);
+
+ check_search_ok(&l, "a", "aa");
+ check_search_ok(&l, "aa", "aa");
+ check_search_ok(&l, "b", "bb");
+ check_search_ok(&l, "bb", "bb");
+
+ check_search_none(&l, "0");
+ check_search_none(&l, "000");
+ check_search_none(&l, "00000000000000000000000000000000000000000000");
+ check_search_none(&l, "a0");
+ check_search_none(&l, "ab");
+ check_search_none(&l, "aaa");
+ check_search_none(&l, "bbb");
+ check_search_none(&l, "cc");
+ check_search_none(&l, "ccc");
+ check_search_none(&l, "cccccccccccccccccccccccccccccccccccccccccccc");
+
+ check_search_multi(&l, "", "", "aa", 2);
+
+ lookup_deinit(&l);
+}
+
+static void test_search_iter(void)
+{
+ const char* table[] = {
+ "0",
+ "ab",
+ "abc",
+ "abcd",
+ "abc-1",
+ "abc-99",
+ "z",
+ NULL
+ };
+
+ lookup_t l;
+ init(&l, table);
+
+ check_search_multi(&l, "", "", "0", 7);
+ check_search_multi(&l, "a", "ab", "ab", 5);
+ check_search_multi(&l, "ab", "ab", "ab", 5);
+ check_search_multi(&l, "abc", "abc", "abc", 4);
+ check_search_multi(&l, "abc-", "abc-", "abc-1", 2);
+
+ lookup_deinit(&l);
+}
+
+int main(int argc, char *argv[])
+{
+ plan_lazy();
+
+ diag("Search tests basic");
+ test_search_basic();
+
+ diag("Search tests multi-result");
+ test_search_iter();
+
+ return 0;
+}